Search code examples
androidandroid-sdcardandroid-external-storageandroid-storage

Android 26 returns only one path Context.getExternalFilesDirs()


I was testing Pixel with 21, 23, 29, 30 API emulators and they would return two paths (first is external built-in sdcard and second is external removable sdcard) when using getExternalFilesDirs(null) method:

  • /storage/emulated/0/Android/data/com.*.*/files
  • /storage/13F6-3510/Android/data/com.*.*/files

But then I tried to use Pixel with 26 API and I only got one path:

  • /storage/emulated/0/Android/data/com.*.*/files

Then I found another method to get all storages paths which returns something new to me, usually it would start with /storage/ but now it starts with /mnt/media_rw/:

  • /storage/emulated/0
  • /mnt/media_rw/1D13-0D17

Method to get it using StorageManager:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    val storageManager = context.getSystemService(Context.STORAGE_SERVICE) as StorageManager
    val storageVolumes = storageManager.storageVolumes
    if (!storageVolumes.isNullOrEmpty()) {
        for (storageVolume in storageVolumes) {
            val volumePath = getVolumePath(storageVolume) ?: continue
            result.add(File(volumePath))
        }
    }
}


@RequiresApi(Build.VERSION_CODES.N)
private fun getVolumePath(storageVolume: StorageVolume): String? {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
        return storageVolume.directory?.absolutePath
    try {
        val storageVolumeClazz = StorageVolume::class.java
        val getPath = storageVolumeClazz.getMethod("getPath")
        return getPath.invoke(storageVolume) as String
    } catch (e: Exception) {
        e.printStackTrace()
    }
    return null
}

You can see that this method even uses reflection for API < R (30)

So I got this path /mnt/media_rw/1D13-0D17 for Pixel 26 API. I don't understand why there is difference from other APIs.

But this path is not accessible yet, I can't read it. Then I tried to use some popular Files Browser/Explorer apps from Google Play and I found out that they request some permission to get access to such external removable storage:

enter image description here

enter image description here

I've never seen something like this before. How do they do it and why it even exists?

I have real devices with 21, 23, 29, 30 API and none of them need to use such method to access external removable storage, I can just use getExternalFilesDirs(null). The same works for Pixel emulators with the same API

So where does /mnt/media_rw/* come from and how to request permission to access it from my app and why do I even need to grant this permission if for other APIs I don't need to do it?

I can't find any information about it at official Android documentation


Solution

  • So I got this path /mnt/media_rw/1D13-0D17 for Pixel 26 API. I don't understand why there is difference from other APIs. But this path is not accessible yet, I can't read it. Then I tried to use some popular Files Browser/Explorer apps from Google Play and I found out that they request some permission to get access to such external removable storage:

    I found such paths unreadable and unlistable too.

    But why other apps can? I think they cannot too using the File class. They will use Storage Access Framework.

    For instance SDK 27 is Android O.

    For Android N, O and P your app can create an access intent with:

    volume.createAccessIntent()
    

    Please try.