3

My application uses FileProvider to get a content URI for a file created in a subdirectory on the external storage and grant permissions to the selected camera application so it can save a photo to that file. It works like a charm on every device and emulator except one which I don't own - Huawei Honor 7 (I have a crash log from that device). I'm trying to understand the problem and find a way to fix it.

AndroidManifest.xml

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="<package>.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_provider_paths" />
</provider>

file_provider_paths.xml

<paths>
    <external-files-path name="external_storage" path="" />
</paths>

When I create an empty file to share with the camera application, I obtain the base external directory path using Context.getExternalFilesDir(null). It usually returns a path like this: /storage/emulated/0/Android/data/<package>/files

which is the same as these:

  • Context.getExternalFilesDirs(null)[0]
  • ContextCompat.getExternalFilesDirs(context, null)[0]

And I put the file a bit deeper, e.g. /storage/emulated/0/Android/data/<package>/files/247/images/ad4b7cd7-8917-43d5-8f6c-69555d76c0e0

Then I call FileProvider.getUriForFile(context, "<package>.fileprovider", file)

As I said, it works properly on everything I have, both emulators and devices, with APIs 19 to 25.


Now, there is that crash:

Caused by java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/0123-4567/Android/data/<package>/files/42/images/51e4b571-67e4-4e7d-abb0-0ae921d339f4
       at android.support.v4.content.FileProvider$SimplePathStrategy.getUriForFile(FileProvider.java:711)
       at android.support.v4.content.FileProvider.getUriForFile(FileProvider.java:400)

I have found out that the current FileProvider implementation matches the external-files-path tag with this piece of code:

} else if (TAG_EXTERNAL_FILES.equals(tag)) {
    File[] externalFilesDirs = ContextCompat.getExternalFilesDirs(context, null);
    if (externalFilesDirs.length > 0) {
        target = externalFilesDirs[0];
    }
}

So my idea is that maybe Context.getExternalFilesDir(null) gives a different storage directory than ContextCompat.getExternalFilesDirs(context, null) at 0 index. If so, I would create a file on the storage which is not supported by the FileProvider.

I have also found some issue reported to Google regarding this limitation.

Do you think that's the real root cause or do you have any other ideas?

Andrzej Zabost
  • 1,387
  • 11
  • 26
  • `I create an empty file to share with the camera application, `. Do not create the file yourself. You only need to create a filename/filepath. The camera app will create the file. – greenapps Mar 15 '17 at 21:49
  • 1
    `root that contains /storage/0123-4567/Android/data//files/42/images/...`. That is not a path from external memory but of a micro SD card. Fileprovider cannot serve files from an SD card. – greenapps Mar 15 '17 at 21:52
  • @greenapps Regarding file creation I was following [this guide](https://developer.android.com/training/camera/photobasics.html#TaskPath) - thanks for the tip. Yeah, I understand it is the SD card path, but it was obtained using `Context.getExternalFilesDir(null)` which I thought is the proper way of getting the most primary and supported external storage. Any suggestions on how to avoid this problem? – Andrzej Zabost Mar 16 '17 at 08:26
  • Have a look at getExternalFilesDirs(). You get two paths? Which ones? – greenapps Mar 16 '17 at 09:13
  • @greenapps As I said, I do not own the device which has crashed, so I can't check it. My own devices can have two external storage dirs. The first one: `/storage/emulated/0/Android/data//files` (it's the same as `Context.getExternalFilesDir(null)`) and optionally the second one: `/storage//Android/data//files`. I understand that the first one is emulated, while the second one is the SD card. The weird thing here is that the problematic device has returned the second one-like on `Context.getExternalFilesDir(null)`, which was not supported by the `FileProvider`. – Andrzej Zabost Mar 16 '17 at 09:31
  • You are telling nothing new. You should add code to inspect both [0] and [1] as i think they will be reversed on the Huawei. You can check if the path starts with /storage/emulated. Probably the user of the Huawei can choose the default storage space for pictures and so and the user choosed sd card. Just an idea. – greenapps Mar 16 '17 at 09:42
  • @greenapps Do you think hardcoding the `"storage/emulated"` string is a good idea? Maybe I should try to `FileProvider.getUriForFile(...)` in a loop, catching the exceptions, to determine the supported storage. – Andrzej Zabost Mar 16 '17 at 09:45
  • To check if the items of getExternalFilesDirs are reversed you could use /storage/emulated. Why not? It has no influence on the behaviour of your code on all those other devices. By the way: that path starts with a /. – greenapps Mar 16 '17 at 09:51
  • Yes indeed you could make such a loop. Good idea. Or just if 0 fails try 1 too. – greenapps Mar 16 '17 at 09:54

0 Answers0