Как некоторые приложения получают доступ к содержимому подпапок /Android/ на Android 11 без рута?

Фон

Существуют различные ограничения на объем хранилища в Android 10 и 11, которое также включает новое разрешение (MANAGE_EXTERNAL_STORAGE) для доступа все файлы (но это не позволяет получить доступ ко всем файлам), в то время как предыдущее разрешение на хранение было уменьшено, чтобы предоставить доступ только к медиафайлам:

  1. Приложения могут свободно обращаться к подпапке мультимедиа.
  2. Приложения никогда не смогут получить доступ к подпапке данных и особенно к содержимому.
  3. Для папки obb, если приложению разрешено устанавливать приложения, оно может получить доступ к ней (чтобы скопировать туда файлы). Иначе нельзя.
  4. Используя USB или root, вы все равно можете получить к ним доступ, а как конечный пользователь вы можете получить к ним доступ через встроенное приложение файлового менеджера Files.

Проблема

Я заметил приложение, которое каким-то образом преодолевает это ограничение (здесь) называется X-plore: как только вы входите в папку Android/data, вам будет предложено предоставить к ней доступ (непосредственно с помощью SAF , каким-то образом), и когда вы предоставите его, вы сможете получить доступ ко всему во всех папках папки Android.

Это означает, что все еще может быть способ добраться до него, но проблема в том, что по какой-то причине я не смог сделать образец, который делает то же самое.

Что я нашел и попробовал

Похоже, что это приложение нацелено на API 29 (Android 10), оно еще не использует новое разрешение и имеет флаг requestLegacyExternalStorage. Я не знаю, сработает ли тот же трюк, который они используют, при нацеливании на API 30, но я могу сказать, что в моем случае, работающем на Pixel 4 с Android 11, он работает нормально.

Поэтому я попытался сделать то же самое:

  1. Я сделал образец POC, ориентированный на Android API 29, с предоставленными разрешениями на хранение (всех видов), включая устаревший флаг.

  2. Я попытался запросить доступ непосредственно к папке Android (на основе здесь), который, к сожалению, не сработал, так как по какой-то причине (продолжал переходить в папку DCIM, не знаю почему):

val androidFolderDocumentFile = DocumentFile.fromFile(File(primaryVolume.directory!!, "Android"))
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
        .setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) 
        .putExtra(DocumentsContract.EXTRA_INITIAL_URI, androidFolderDocumentFile.uri)
startActivityForResult(intent, 1)

Пробовал разные комбинации флагов.

  1. При запуске приложения, когда я сам добираюсь до папки Android вручную, так как это не сработало, и я предоставил доступ к этой папке, как и в другом приложении.

  2. При получении результата я пытаюсь получить файлы и папки по пути, но не могу их получить:

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    Log.d("AppLog", "resultCode:$resultCode")
    val uri = data?.data ?: return
    if (!DocumentFile.isDocumentUri(this, uri))
        return
    grantUriPermission(packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
    contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
    val fullPathFromTreeUri = FileUtilEx.getFullPathFromTreeUri(this, uri) // code for this here: https://stackoverflow.com/q/56657639/878126
    val documentFile = DocumentFile.fromTreeUri(this, uri)
    val listFiles: Array<DocumentFile> = documentFile!!.listFiles() // this returns just an array of a single folder ("media")
    val androidFolder = File(fullPathFromTreeUri)
    androidFolder.listFiles()?.forEach {
        Log.d("AppLog", "${it.absoluteFile} children:${it.listFiles()?.joinToString()}") //this does find the folders, but can't reach their contents
    }
    Log.d("AppLog", "granted uri:$uri $fullPathFromTreeUri")
}

Таким образом, используя DocumentFile.fromTreeUri, я все еще мог получить только медиа-папку, которая бесполезна, и с помощью класса File я мог видеть только папки с данными и obb, но все еще не мог получить доступ к их содержимому...

Так что это совсем не сработало.

Позже я обнаружил другое приложение, использующее этот прием, под названием MiXplorer. В этом приложении ему не удалось напрямую запросить папку Android (возможно, он даже не пытался), но он предоставляет вам полный доступ к нему и его подпапкам, как только вы это разрешите. И он нацелен на API 30, поэтому это означает, что он не работает только потому, что вы ориентируетесь на API 29.

Я заметил (мне кто-то писал), что при некоторых изменениях в коде я мог запросить доступ к каждой из подпапок отдельно (имеется в виду запрос данных и новый запрос обб), но это не то, что я вижу здесь, что приложения делают.

Это означает, что для доступа к папке Android я использую этот Uri в качестве параметра для Intent.EXTRA_INITIAL_URI :

val androidUri=Uri.Builder().scheme("content").authority("com.android.externalstorage.documents")
                    .appendEncodedPath("tree").appendPath("primary:").appendPath("document").appendPath("primary:Android").build()

Однако получив к нему доступ, вы не сможете получить из него список файлов, ни через File, ни через SAF.

Но, как я уже писал, странно то, что если вы попробуете что-то подобное, вместо этого получить доступ к Android/data, вы сможете получить его содержимое:

val androidDataUri=Uri.Builder().scheme("content").authority("com.android.externalstorage.documents")
                 .appendEncodedPath("tree").appendPath("primary:").appendPath("document").appendPath("primary:Android/data").build()

Вопросы

  1. Как я могу запросить Intent непосредственно в папку Android, которая фактически позволит мне получить к ней доступ и позволит мне получить подпапки и их содержимое?
  2. Есть ли другая альтернатива этому? Может быть, используя adb и/или root, я мог бы предоставить SAF доступ к этой конкретной папке?

person android developer    schedule 30.01.2021    source источник
comment
.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION... Это не имеет смысла. Вы не можете ничего грандиозного. Вместо этого вы должны быть рады, что вам что-то предоставлено в onActivityResult. Лучше удалите этот код.   -  person blackapps    schedule 30.01.2021
comment
I could grant SAF access to this specific folder ? Неправильно. Вы ничего не можете сделать. Вместо этого радуйтесь, что у вас есть доступ к saf grands.   -  person blackapps    schedule 30.01.2021
comment
Цитата: приложение достигает этого, ориентируясь на Android 10, что позволяет ему временно отказаться от хранилища с заданной областью.   -  person blackapps    schedule 30.01.2021
comment
Флаг также упоминается в документах, поэтому его не следует удалять: developer.android.com/training/data-storage/shared/ . О том, что я мог запросить, прочитайте предложение полностью, так как я писал о том, что это делается через adb/root. Что касается временного отказа от статьи, я не думаю, что это правильно, поскольку речь идет о разрешении на хранение, а не о папке Android. Кроме того, мой POC также нацелен на API 29, поэтому он до сих пор не объясняет, как это делается.   -  person android developer    schedule 30.01.2021
comment
Я вижу и цитирую // Provide read access to files and sub-directories in the user-selected // directory. intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);. Ну, это ошибка в этом документе. Убери его и увидишь. И подумайте об этом: как тогда можно было обойтись без флага WRITE?   -  person blackapps    schedule 30.01.2021
comment
Все еще не работает. Я пробовал без, вместо этого я пробовал FLAG_GRANT_WRITE_URI_PERMISSION, я пробовал их оба. Все эти варианты дают мне тот же результат. Также почему-то у меня иногда не показывалась кнопка использования этой папки, и приходилось переходить в другую папку и обратно. Похоже на странные проблемы в пользовательском интерфейсе выбора папок. Можешь пожалуйста тоже попробовать?   -  person android developer    schedule 30.01.2021
comment
Я видел такие вещи раньше. Но я не ставлю эти флаги на это намерение с самого начала. Как говорится: нет смысла. При получении постоянных разрешений uri просто посмотрите, какие из них вам предлагаются. Вы увидите, что их размещение не имеет никакого эффекта. Этот док ошибается.   -  person blackapps    schedule 30.01.2021
comment
Хорошо, но как я сейчас написал, даже без них это не помогло. Не могли бы вы проверить это?   -  person android developer    schedule 30.01.2021
comment
Что касается таргетинга на Android 10, кажется, что доступ к папке по-прежнему возможен даже при таргетинге на Android 11. Это потому, что я заметил, что MixPlorer преуспел в этом.   -  person android developer    schedule 30.01.2021
comment
Я понятия не имею, что я должен проверить. И действительно, вы должны нацелиться на 10 и добавить этот дополнительный флаг.   -  person blackapps    schedule 30.01.2021
comment
Как я уже писал, вы все еще можете ориентироваться на API 30 (Android 11), и он все равно будет работать. Я заметил это в другом приложении. Поэтому целевой API не имеет значения (по крайней мере, на данный момент).   -  person android developer    schedule 30.01.2021
comment
??? Я понятия не имею, что я должен проверить.   -  person blackapps    schedule 30.01.2021
comment
Я представил код. Вы говорите, что знаете, как это сделать, и что я сделал это неправильно. Пожалуйста, попробуйте мой код или покажите свой, как добраться до папок. Вопрос по-прежнему заключается в том, как получить доступ к папкам (и было бы очень неплохо знать, как сделать так, чтобы запрос был непосредственно в папку).   -  person android developer    schedule 30.01.2021
comment
??? Что я сказал, что я знаю, как это сделать? Я никогда не говорил таких вещей.   -  person blackapps    schedule 30.01.2021
comment
Вы написали 3 вещи в начале, говоря, что я не прав (и я показал, что это не так, кстати), так что это означает, что вы думаете, что знаете, что нужно делать. Пожалуйста, объясните, что нужно сделать.   -  person android developer    schedule 30.01.2021
comment
Вы видели этот отчет об ошибке? В нем есть ссылки на обсуждение Amaze File Manager в репозитории GitHub, которые могут вас заинтересовать. .   -  person Cheticamp    schedule 08.02.2021
comment
@Cheticamp Спасибо. Приложение, которое вы представили, похоже, не делает этого. Но отчет об ошибке показывает, что я нашел. Так ты думаешь, это какая-то лазейка? Каким-то образом приложение Total Commander также может добраться туда (хотя это немного неудобно).   -  person android developer    schedule 08.02.2021
comment
Я думаю, что намерение ограничить доступ к этим папкам Android довольно ясно, поэтому для меня это выглядит как лазейка. Будет интересно увидеть реакцию на сообщение об ошибке. Я надеюсь, как и многие, многие другие, что у пользователя есть какой-то законный способ разрешить доступ.   -  person Cheticamp    schedule 08.02.2021
comment
Я собираюсь отозвать последний комментарий. Не думаю, что это лазейка. Взгляните на этот комментарий. SD Maid и X-Plore запрашивают разрешение на ../Android/... одинаково, поэтому я думаю, что это стандартный метод. Тем не менее, будет интересно увидеть ответ на отчет об ошибке, который я разместил в своем последнем комментарии.   -  person Cheticamp    schedule 09.02.2021
comment
@Cheticamp Я вижу. Почему он упомянул root/data/media/0/Android/data? Что это?   -  person android developer    schedule 09.02.2021
comment
Я думаю, что он говорит, что эти два пути ведут в одно и то же место. Не знаю конкретно о root/data/media/0/Android/data.   -  person Cheticamp    schedule 09.02.2021
comment
@Cheticamp Возможно, вы знаете, как реализовать использование этой лазейки (независимо от того, является ли она таковой или нет)? Можете ли вы попробовать? Я понятия не имею, почему и как некоторые приложения для управления файлами, похоже, уже каким-то образом автоматически это сделали.   -  person android developer    schedule 09.02.2021
comment
developer.android.com/about/versions/11/privacy/storage   -  person Abdelrahman Farag    schedule 18.02.2021
comment
@AbdelrahmanFarag Пожалуйста, прочитайте вопрос. Некоторым приложениям удалось попасть туда.   -  person android developer    schedule 18.02.2021
comment
How can I request an Intent directly to "Android" folder? Полная награда?   -  person blackapps    schedule 25.02.2021
comment
looking for an answer from a reputable source. Что ты имеешь в виду?   -  person blackapps    schedule 25.02.2021
comment
Кажется, вы не прочитали мой первый комментарий сегодня.   -  person blackapps    schedule 25.02.2021
comment
@blackapps Я не понимаю.   -  person android developer    schedule 26.02.2021
comment
Вас не интересует решение только по первому вопросу. Ладно, хорошо. Нет награды за это.   -  person blackapps    schedule 26.02.2021
comment
@blackapps Я на самом деле каким-то образом получил его (проверьте обновленный вопрос), но, как я уже писал, этого недостаточно, потому что мне нужен реальный доступ к нему, как это делают эти приложения. Я обновил вопрос, чтобы объяснить его лучше. Теперь эта часть находится в вопросе 1, потому что это одно и то же.   -  person android developer    schedule 26.02.2021


Ответы (2)


Вот как это работает в X-plore:

Когда на Build.VERSION.SDK_INT>=30 [Внутреннее хранилище]/Android/данные недоступны, java File.canRead() или File.canWrite() возвращает false, поэтому нам нужно переключиться на альтернативную файловую систему для файлов внутри этой папки (и, возможно, также obb).

Вы уже знаете, как работает структура доступа к хранилищу, поэтому я просто подробно расскажу, что именно нужно сделать.

Вы звоните ContentResolver.getPersistedUriPermissions(), чтобы узнать, есть ли у вас уже сохраненное разрешение для этой папки. Изначально у вас его нет, поэтому вы спрашиваете разрешения у пользователя:

Чтобы запросить доступ, используйте startActivityForResult с Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).putExtra(DocumentsContract.EXTRA_INITIAL_URI, DocumentsContract.buildDocumentUri("com.android.externalstorage.documents", "primary:Android")) Здесь вы устанавливаете с помощью EXTRA_INITIAL_URI, что средство выбора должно запускаться непосредственно в папке Android в основном хранилище, потому что нам нужен доступ к папке Android. Когда ваше приложение будет нацелено на API30, сборщик не позволит выбрать корень хранилища, а также, получив разрешение на папку Android, вы можете работать с папками data и obb внутри, с одним запросом разрешения.

Когда пользователь подтвердит 2 клика, в onActivityResult вы получите Uri в данных, которые должны быть content://com.android.externalstorage.documents/tree/primary%3AAndroid. Сделайте необходимые проверки, чтобы убедиться, что пользователь подтвердил правильную папку. Затем вызовите contentResolver.takePersistableUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION), чтобы сохранить разрешение, и все готово.

Итак, мы вернулись к ContentResolver.getPersistedUriPermissions(), который содержит список предоставленных разрешений (их может быть больше), тот, который вы предоставили выше, выглядит так: UriPermission {uri=content://com.android.externalstorage.documents/tree/primary%3AAndroid, modeFlags=3} (тот же Uri, что и в onActivityResult). Повторите список из getPersistedUriPermissions, чтобы найти интересующий uri, если он работает с ним, в противном случае попросите пользователя предоставить.

Теперь вы хотите работать с ContentResolver и DocumentsContract, используя этот uri дерева и ваш относительный путь к файлам внутри папки Android. Вот пример списка папок data: data/ — это путь относительно предоставленного дерева uri. Создайте окончательный uri, используя либо DocumentsContract.buildChildDocumentsUriUsingTree() (для списка файлов), либо DocumentsContract.buildDocumentUriUsingTree() (для работы с отдельными файлами), например: DocumentsContract.buildChildDocumentsUriUsingTree(treeUri, DocumentsContract.getTreeDocumentId(treeUri), DocumentsContract.getTreeDocumentId(treeUri)+"/data/"), вы получите uri=content://com.android.externalstorage.documents/tree/primary%3AAndroid/document/primary%3AAndroid%2Fdata%2F/children, подходящий для списка файлов в папке данных. Теперь вызовите ContentResolver.query(uri, ...) и обработайте данные в Cursor, чтобы получить список папок.

Подобным образом вы работаете с другими функциями SAF для чтения/записи/переименования/перемещения/удаления/создания, которые вы, вероятно, уже знаете, используя ContentResolver или методы DocumentsContract.

Некоторые детали:

  • это не нужно android.permission.MANAGE_EXTERNAL_STORAGE
  • он работает на целевом API 29 или 30
  • он работает только на основной памяти, а не на внешних SD-картах
  • для всех файлов внутри папки данных вам нужно использовать SAF (файл Java не будет работать), просто используйте иерархию файлов относительно папки Android
  • в будущем Google может исправить эту дыру в своих намерениях безопасности, и это может не работать после некоторого обновления безопасности

РЕДАКТИРОВАТЬ: пример кода, основанный на примере Github Cheticamp. В примере показано содержимое (и количество файлов) каждой из подпапок папки Android:

class MainActivity : AppCompatActivity() {
    private val handleIntentActivityResult =
        registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
            if (it.resultCode != Activity.RESULT_OK)
                return@registerForActivityResult
            val directoryUri = it.data?.data ?: return@registerForActivityResult
            contentResolver.takePersistableUriPermission(
                directoryUri, Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
            )
            if (checkIfGotAccess())
                onGotAccess()
            else
                Log.d("AppLog", "you didn't grant permission to the correct folder")
        }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        setSupportActionBar(findViewById(R.id.toolbar))
        val openDirectoryButton = findViewById<FloatingActionButton>(R.id.fab_open_directory)
        openDirectoryButton.setOnClickListener {
            openDirectory()
        }
    }

    private fun checkIfGotAccess(): Boolean {
        return contentResolver.persistedUriPermissions.indexOfFirst { uriPermission ->
            uriPermission.uri.equals(androidTreeUri) && uriPermission.isReadPermission && uriPermission.isWritePermission
        } >= 0
    }

    private fun onGotAccess() {
        Log.d("AppLog", "got access to Android folder. showing content of each folder:")
        @Suppress("DEPRECATION")
        File(Environment.getExternalStorageDirectory(), "Android").listFiles()?.forEach { androidSubFolder ->
            val docId = "$ANDROID_DOCID/${androidSubFolder.name}"
            val childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(androidTreeUri, docId)
            val contentResolver = this.contentResolver
            Log.d("AppLog", "content of:${androidSubFolder.absolutePath} :")
            contentResolver.query(childrenUri, null, null, null)
                ?.use { cursor ->
                    val filesCount = cursor.count
                    Log.d("AppLog", "filesCount:$filesCount")
                    val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
                    val mimeIndex = cursor.getColumnIndex("mime_type")
                    while (cursor.moveToNext()) {
                        val displayName = cursor.getString(nameIndex)
                        val mimeType = cursor.getString(mimeIndex)
                        Log.d("AppLog", "  $displayName isFolder?${mimeType == DocumentsContract.Document.MIME_TYPE_DIR}")
                    }
                }
        }
    }

    private fun openDirectory() {
        if (checkIfGotAccess())
            onGotAccess()
        else {
            val primaryStorageVolume = (getSystemService(STORAGE_SERVICE) as StorageManager).primaryStorageVolume
            val intent =
                primaryStorageVolume.createOpenDocumentTreeIntent().putExtra(EXTRA_INITIAL_URI, androidUri)
            handleIntentActivityResult.launch(intent)
        }
    }

    companion object {
        private const val ANDROID_DOCID = "primary:Android"
        private const val EXTERNAL_STORAGE_PROVIDER_AUTHORITY = "com.android.externalstorage.documents"
        private val androidUri = DocumentsContract.buildDocumentUri(
            EXTERNAL_STORAGE_PROVIDER_AUTHORITY, ANDROID_DOCID
        )
        private val androidTreeUri = DocumentsContract.buildTreeDocumentUri(
            EXTERNAL_STORAGE_PROVIDER_AUTHORITY, ANDROID_DOCID
        )
    }
}
person Pointer Null    schedule 03.03.2021
comment
Это выглядит многообещающе. Не могли бы вы сделать здесь пример приложения, которое содержит разрешение SAF и печатает, например, количество файлов в папке Android и для каждой из ее подпапок? - person android developer; 04.03.2021
comment
Вы получили много советов по сложной теме. - person loop; 04.03.2021
comment
@androiddeveloper Это определенно тот ответ, который вы ищете. Вот суть с функцией, которая будет регистрировать записи в ../Android/data после доступа разрешено для ../Android. (Код мог бы быть короче, но он демонстрирует концепцию.) - person Cheticamp; 04.03.2021
comment
Извините, нет времени делать демо. Вы должны быть в состоянии сделать это в собственном приложении с этой информацией. - person Pointer Null; 04.03.2021
comment
@PointerNull Я не понимаю, когда ты получил Uri. Кажется, вы не используете DocumentFile.fromTreeUri(this, treeUri), а затем используете listFiles(). Я попробовал то, что вы написали, а не то, что я знаю, и использование contentResolver.query в новом созданном вами Uri не привело к реальному содержимому папки Android. Он показал мне только медиа и еще одну папку, которую я туда добавил, но никаких данных и кэша. - person android developer; 04.03.2021
comment
Данные @androiddeveloper и obb не будут перечислены как дочерние элементы каталога Android. Запросите их вручную, и вы увидите их содержимое. - person artman; 04.03.2021
comment
@artman На самом деле по какой-то причине использование File API на пути заставило меня перечислить эти папки. Но оттуда я не понимаю, как получить детей данных и obb. Инструкция мне не понятна :( - person android developer; 04.03.2021
comment
@androiddeveloper Вот небольшое приложение, которое демонстрирует, как перемещаться по каталогу данных. Как вы знаете, File().listFiles() предоставит вам содержимое верхнего уровня ../Android/, поэтому вы сможете использовать логику в приложении для просмотра obb, а также всего, что вы там найдете. - person Cheticamp; 04.03.2021
comment
@androiddeveloper предоставит SAF доступ к папке Android и запросит ContentResolver для content://com.android.externalstorage.documents/tree/primary%3AAndroid/document/primary%3AAndroid%2Fdata/children. Это прояснит ситуацию - person artman; 04.03.2021
comment
@Cheticamp Большое спасибо! Обновлен текущий ответ, чтобы иметь упрощенный образец, основанный на том, что вы написали, и на том, что я нашел. Кстати, ваш образец завис для меня из-за слишком большого количества папок, и даже когда он собирался показать контент, из-за темной темы он показывал темный текст на темном фоне ... - person android developer; 05.03.2021
comment
@androiddeveloper я посмотрю. Вы понимаете, что это просто уязвимость в Android 11, и она, скорее всего, будет устранена в следующем выпуске. - person Cheticamp; 05.03.2021
comment
@Cheticamp Надеюсь, Google проигнорирует это или исправит навсегда. - person android developer; 05.03.2021

Что ж, я попробовал этот код, и он работает на Android API 29, Samsung Galaxy 20FE:

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void triggerStorageAccessFramework() {
    Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
    startActivityForResult(intent, REQUEST_CODE_STORAGE_ACCESS);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == REQUEST_CODE_STORAGE_ACCESS) {
        Uri treeUri = null;
        // Get Uri from Storage Access Framework.
        treeUri = data.getData();

        // Persist URI in shared preference so that you can use it later.
        // Use your own framework here instead of PreferenceUtil.
        MySharedPreferences.getInstance(null).setFileURI(treeUri);

        // Persist access permissions.
        final int takeFlags = data.getFlags()
                & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
        getContentResolver().takePersistableUriPermission(treeUri, takeFlags);

        createDir(DIR_PATH);


        finish();
    }
}

private void createDir(String path) {
    Uri treeUri = MySharedPreferences.getInstance(null).getFileURI();

    if (treeUri == null) {
        return;
    }

    // start with root of SD card and then parse through document tree.
    DocumentFile document = DocumentFile.fromTreeUri(getApplicationContext(), treeUri);
    document.createDirectory(path);
}

Я вызываю это с помощью кнопки onClick:

Button btnLinkSd = findViewById(R.id.btnLinkSD);
    btnLinkSd.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            triggerStorageAccessFramework();
        }
    });

В пользовательском интерфейсе я нажимаю «Показать внутреннее хранилище», перехожу в каталог Android и нажимаю «Разрешить». После этого при отладке, если я попытаюсь перечислить все файлы под андроидом, я получаю список всех каталогов в Data. Если это то, что вы ищете. введите здесь описание изображения введите здесь описание изображения введите здесь описание изображения

И, наконец, результаты отладки: введите здесь описание изображения

person Dan Baruch    schedule 03.03.2021
comment
Проблема (также написанная в заголовке) связана с Android API 30 (Android 11). В Android 10 ограничения на доступ к папке и подпапкам Android не существует. Пожалуйста, попробуйте это - person android developer; 04.03.2021
comment
Боюсь, у меня нет Android 11, но я считаю, что он все равно должен работать, так как это не связано с устаревшим флагом или чем-то еще. - person Dan Baruch; 04.03.2021
comment
Есть эмулятор, который существует довольно давно... - person android developer; 04.03.2021
comment
Да, но они укоренены, я думал, что они не будут действительны. Нет проблем, проверю на Android 11. - person Dan Baruch; 04.03.2021
comment
Что ж, эмулятор у меня здесь не работает, и, кажется, у вас уже есть лучший ответ, поэтому я не буду проверять его на Android 11. - person Dan Baruch; 04.03.2021
comment
Речь идет об Android 11. Только тогда добавили ограничение доступа к папке Android и просмотра ее содержимого. Не могли бы вы предоставить пример проекта, который я могу попробовать? Вы можете загрузить его сюда, если хотите: uploadfiles.io - person android developer; 04.03.2021
comment
Неважно. Сделал пробу на основе того, что мне написали. - person android developer; 05.03.2021