Android FileProvider content:// uri нет разрешения на чтение в целевом приложении

Я пытаюсь поделиться файлом (типа PDF, но это не важно), присутствующим в моем каталоге «external_files» App1 (/storage/emulated/0/Android/data/com.package.app1/files), в мое приложение App2. Я тестирую это в эмуляторе Android 10 (из Android Studio, последняя версия), оба приложения используют Scoped Storage, а поддержка устаревшего внешнего хранилища не включена.

Кажется, все определяется «книгой» в манифесте App1:

    <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="com.package.app1.fileprovider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/my_paths" />
    </provider>

тогда my_paths.xml имеет

<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-files-path name="external_files" path="." />
</paths>

Затем в App1:

Intent intent = new Intent(Intent.ACTION_SEND);
intent.setPackage("com.package.app2");
File file = new File(context.getExternalFilesDir(null), "MyFile.pdf");
Uri uri = FileProvider.getUriForFile(context, "com.package.app1.fileprovider", file);
context.grantUriPermission("com.package.app2", uri,
                        FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndTypeAndNormalize(uri, "application/pdf");
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
activity.startActivityForResult(intent, MY_REQUEST_CODE);

App2 имеет в своем манифесте:

<activity android:name=".MainActivity" android:configChanges="orientation|screenSize|keyboardHidden"
    android:label="@string/app_name" android:theme="@style/AppTheme.NoActionBar">
    <intent-filter>
        <action android:name="android.intent.action.SEND" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="application/pdf" />
    </intent-filter>
</activity>

В App2: активность onCreate():

   Uri uri = getIntent().getData(); // The content:// uri looks correct
   if (uri != null) {
        DocumentFile docFile = DocumentFile.fromSingleUri(this, uri);
        Log.d("myTag", uri.toString + ": EXISTS: ", docFile.exists(), ", can read: ", docFile.canRead());
   }

Вывод всегда, независимо от того, что я делаю:

content://com.package.app1.fileprovider/external_files/MyFile.pdf: EXISTS: true, can read: false

Что я делаю не так, почему я не могу получить "can read: true"?


person gregko    schedule 20.11.2019    source источник
comment
Является ли canRead() надежным? Лучше попробуй прочитать. Откройте входной поток и прочитайте.   -  person blackapps    schedule 20.11.2019
comment
context.grantUriPermission(com.package.app2... попробуйте без этой строки.   -  person blackapps    schedule 20.11.2019
comment
Вместо этого используйте setDataAndType().   -  person blackapps    schedule 20.11.2019
comment
@blackapps, спасибо - попытался открыть входной поток, не удалось. Когда я тестирую docFile.isFile() и docFile.isDirectory(), оба возвращают false. Однако член mUri docFile выглядит правильно: content://com.package.app1.fileprovider/external_files/MyFile.pdf   -  person gregko    schedule 20.11.2019
comment
@blackapps, спасибо за попытку помочь, вместо этого также пробовал использовать setDataAndType(), без разницы...   -  person gregko    schedule 20.11.2019
comment
У вашего приложения есть разрешение READ_EXTERNAL_STORAGE? Во время выполнения тоже?   -  person blackapps    schedule 20.11.2019
comment
@blackapps - вы правы, canRead() ненадежен. Прежде чем получить входной поток, в ошибочной попытке предотвратить попытку открытия потока в каталоге я тестировал docFile.isFile(), и он вернул false. Теперь я тестирую !docFile().isDirectory() и получаю читаемый входной поток. СПАСИБО!!!   -  person gregko    schedule 20.11.2019


Ответы (2)


Предложение @blackapps в самом первом комментарии было правильным: docFile.canRead() для DocumentFile ненадежен, когда docFile.isFile() возвращает false (docFile.isDirectory() также возвращает false, это было обычное содержимое файла из Файловый провайдер). Несмотря на то, что docFile.canRead() возвращает false, я могу открыть объект InputStream для объекта docFile и могу его нормально прочитать. Спасибо, @blackapps!

person gregko    schedule 20.11.2019
comment
Это явный сбой в Android или, по крайней мере, вводящая в заблуждение документация. Я постараюсь отправить отчет об ошибке разработчикам или авторам документации. - person Andreas K. aus M.; 27.10.2020

На самом деле canRead() для DocumentFile надежен. Но он проверяет разные вещи, чем File.canRead()

Согласно DocumentsContractApi19, это то, что canRead() делает для DocumentFile:

 public static boolean canRead(Context context, Uri self) {
        // Ignore if grant doesn't allow read
        if (context.checkCallingOrSelfUriPermission(self, Intent.FLAG_GRANT_READ_URI_PERMISSION)
                != PackageManager.PERMISSION_GRANTED) {
            return false;
        }

        // Ignore documents without MIME
        if (TextUtils.isEmpty(getRawType(context, self))) {
            return false;
        }

        return true;
    }

Моя рекомендация: не используйте canRead(), как для файла. вместо этого попробуйте открыть uri для чтения с помощью try/catch

person Simon    schedule 23.07.2021