AndroidDev
AndroidDev

Reputation: 1625

java.lang.IllegalArgumentException Volume external_primary not found in Android 10 devices

Crash : java.lang.IllegalArgumentException Volume external_primary not found

When querying for tracks from media store, I am getting this crash in some Android 10 devices (Most of them are from Xiaomi Mi A2 Lite, Motorola, HMD Global Nokia).

Cursor cursor = getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null, null);

Should I be passing a different URI for Android 10 devices ?. (However it is working fine in most of the devices )

Stack trace :

Caused by java.lang.IllegalArgumentException: Volume external_primary not found
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:170)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:140)
at android.content.ContentProviderProxy.query(ContentProviderProxy.java:423)
at android.content.ContentResolver.query(ContentResolver.java:946)
at android.content.ContentResolver.query(ContentResolver.java:881)
at android.content.ContentResolver.query(ContentResolver.java:837)
at com.example.musicplayer.CursorFactory.getAllSongsCursor(CursorFactory.java:164)

Edit : Based on this issue reported, Suspect it could be an issue with sdcard in those devices with Android 10 OS.

Upvotes: 12

Views: 13399

Answers (3)

Pratik
Pratik

Reputation: 30855

You need to ask read permission for Android Q and above to access files outside of your application scope.

As per documentation mention as below and here is the documention link

If scoped storage is enabled, the collection shows only the photos, videos, and audio files that your app has created. Most developers won't need to use MediaStore.Files to view media files from other apps, but if you have a specific requirement to do so, you can declare the READ_EXTERNAL_STORAGE permission. It's recommended, however, that you use the MediaStore APIs to open files that your app hasn't created.

Here is the common code to ask permission for all version,

Declare the request launcher object

private val requestReadResult = registerForActivityResult(ActivityResultContracts.RequestPermission()) { result ->
    if (result) {
        queryData()
    } else {
        AlertDialog.Builder(this)
                .setMessage(R.string.app_permission_required)
                .setPositiveButton(R.string.exit) { _, _ -> finish() }.setCancelable(false).create().show()
    }
}

Check permission granted or not before query

if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED) {
        requestReadResult.launch(Manifest.permission.READ_EXTERNAL_STORAGE)
        return
}
queryData()

Now check for the Android SDK version and as per that initialize your Uri

val uri= if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
    MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
else 
    MediaStore.Images.Media.EXTERNAL_CONTENT_URI

// Here you will getting all the Images from device.
val mediaCursor = contentResolver.query(uri, null, null, null, null)

As per your media requirement change MediaStore.yourmediatype and use it.

Thanks

Upvotes: 1

Harvey
Harvey

Reputation: 1375

Google added a thing called Scoped Storage which changes the File operation a bit. so you should use VOLUME_EXTERNAL/VOLUME_EXTERNAL_PRIMARY instead of EXTERNAL_CONTENT_URI

val uri =
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            MediaStore.Video.Media.getContentUri(MediaStore.VOLUME_EXTERNAL)
        } else {
            MediaStore.Video.Media.EXTERNAL_CONTENT_URI
        }

Going with VOLUME_EXTERNAL/VOLUME_EXTERNAL_PRIMARY is the right direction but there is a really rare case that the user stores their audio file on SD card but the phone doesn't recognize it which leads to the crash. This is a device error and there is nothing you can do about it.

From the doc of https://developer.android.com/reference/android/provider/MediaStore#VOLUME_EXTERNAL_PRIMARY

you could use getExternalVolumeNames to check is there any external storage available before making the query.

https://developer.android.com/reference/android/provider/MediaStore#getExternalVolumeNames(android.content.Context)

Upvotes: 5

Siraj Sumra
Siraj Sumra

Reputation: 954

Post Android 10, API 28, there are some changes when accessing the media content from other sources.

Google mentioned it in this link https://developer.android.com/training/data-storage/shared/media#storage-volume

You can get more information on how to resolve this issue with more information given by google with the below code:

// Add a specific media item.
ContentResolver resolver = getApplicationContext()
        .getContentResolver();

// Find all audio files on the primary external storage device.
// On API <= 28, use VOLUME_EXTERNAL instead.
Uri audioCollection = MediaStore.Audio.Media.getContentUri(
        MediaStore.VOLUME_EXTERNAL_PRIMARY);

// Publish a new song.
ContentValues newSongDetails = new ContentValues();
newSongDetails.put(MediaStore.Audio.Media.DISPLAY_NAME,
        "My Song.mp3");

// Keeps a handle to the new song's URI in case we need to modify it
// later.
Uri myFavoriteSongUri = resolver
        .insert(audioCollection, newSongDetails);

Hope this works for you!

Upvotes: 6

Related Questions