androidunity-game-engineandroid-external-storageandroid-fileproviderandroid-unity-plugin

Why FileProvider fails when using READ_EXTERNAL_STORAGE permission as well?


In my Android Unity plugin, I want to provide access to the screenshot that unity made, so provide access to Application.persistentDataPath, which is the files folder of the app per documentation...

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <files-path name="files" path="."/>
</paths>

I want also to read / write external storage. But as soon as I add...

<!-- For access `DICM/Camera` -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

...to the manifest, Unity adds the screenshots to the external storage, so FileProvider cannot provide files from there.

java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/Android/data/<PACKAGE_NAME>/files/<FILE_NAME>

How can I still provide files from app files directory wherever it resides?


Solution

  • I usually see that message coming from getUriForFile(), when you pass in a File that does not match your configuration.

    In your provider metadata, you are only supporting getFilesDir() (and locations underneath it) as places that you are willing to serve from. However, the File object that you passed into getUriForFile() appears to be built from getExternalFilesDir(), which is not the same thing.

    Unfortunately, FileProvider does not provide direct access to getExternalFilesDir(). <external-path> comes close, and the documentation claims that it points to getExternalFilesDir(). That is a documentation bug; <external-path> really points to the root of external storage (i.e., Environment.getExternalStorageDirectory()).

    Your options are:

    1. Stick to FileProvider just for your internal storage files, and use file:// Uri values for the files on external storage

    2. Work out an <external-path> value to add to your provider metadata that points to your particular portion of external storage, based on your application ID

    3. Use my StreamProvider, which adds <external-files-path> as an option

    4. Roll your own ContentProvider to handle this, rather than using either FileProvider or StreamProvider