Как мы можем получить доступ к файлу расширения в Android Q? - PullRequest
4 голосов
/ 23 июня 2019

Я пишу приложение, которому нужен файл расширения, и я хочу убедиться, что он будет совместим с Android Q. Кажется, что документация , предоставленная , не учитывает изменения в Android Q. В Android Q , getExternalStorageDirectory() не сможет использоваться, так как мы можем получить доступ к файлу расширения?

1 Ответ

3 голосов
/ 29 июня 2019

Из документации , связанной с вопросом, мы знаем, что имя файла расширения имеет вид:

.. [Главная | патч] <расширение-версия> <имя пакета> .obb

и метод getObbDir () возвращает конкретное расположение для файлов расширения в следующей форме:

<разделяемой хранения> / / / <имя пакета> Android ФЖДА /

Итак, вопрос в том, как нам получить доступ к таким файлам?

Чтобы ответить на этот вопрос, я взял каталог, содержащий пять файлов APK, и создал файл OBB с именем "main.314159.com.example.opaquebinaryblob.obb", используя JOBB . Мое намерение состоит в том, чтобы смонтировать и прочитать этот файл OBB, чтобы отобразить имена файлов APK и количество записей в каждом APK (считанные в виде Zip-файлов) в небольшом демонстрационном приложении.

Демонстрационное приложение также попытается создать / прочитать тестовые файлы в различных каталогах в каталоге внешнего хранилища.

Следующее было выполнено на эмуляторе Pixel XL с последней доступной версией «Q» (Android 10.0 (API Google)). Приложение имеет следующие характеристики:

  • targetSdkVersion 29
  • minSdkVersion 18
  • Нет явных разрешений указанный в манифесте

Я заглянул вперед, чтобы узнать, какой каталог getObbDir () возвращает для этого небольшого приложения, и обнаружил, что это

/ хранение / эмулировать / 0 / Android / ФЖДА / com.example.opaquebinaryblob

поэтому я загрузил свой файл OBB в

/ хранение / эмулировать / 0 / Android / ФЖДА / com.example.opaquebinaryblob / main.314159.com.example.opaquebinaryblob.obb

с использованием Android Studio. Вот где файл завелся.

enter image description here

Итак, мы можем смонтировать и прочитать этот файл OBB? Можем ли мы создавать / читать файлы в других каталогах в пути к внешним файлам? Вот что приложение сообщает об API 29:

enter image description here

Единственные доступные файлы находятся в / storage / emulated / 0 / Android / obb / com.example.opaquebinaryblob . Другие файлы в иерархии не могут быть ни созданы, ни прочитаны. (Интересно, однако, существование этих файлов может быть определено.)

Для предыдущего экрана приложение открывает файл OBB и читает его напрямую, не монтируя.

Когда мы пытаемся смонтировать файл OBB и вывести его содержимое, вот что сообщается:

enter image description here

Что мы и ожидаем. Короче говоря, похоже, что Android Q ограничивает доступ к каталогу внешних файлов, в то же время разрешая целевой доступ на основе имени пакета приложения.

MainActivity.kt

class MainActivity : AppCompatActivity() {
    private lateinit var myObbFile: File
    private lateinit var mStorageManager: StorageManager

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        obbDumpText.movementMethod = ScrollingMovementMethod()

        val sb = StringBuilder()

        val extStorageDir = Environment.getExternalStorageDirectory()
        sb.appendln("getExternalStorageDirectory() reported at $extStorageDir").appendln()
        myObbFile = File(obbDir, BLOB_FILE_NAME)

        val obbDir = obbDir
        sb.appendln("obbDir reported at $obbDir").appendln()
        myObbFile = File(obbDir, BLOB_FILE_NAME)

        val directoryPathList = listOf(
            "$extStorageDir",
            "$extStorageDir/Pictures",
            "$extStorageDir/Android/obb/com.example.anotherpackage",
            "$extStorageDir/Android/obb/$packageName"
        )
        var e: Exception?
        for (directoryPath in directoryPathList) {
            val fileToCheck = File(directoryPath, TEST_FILE_NAME)
            e = checkFileReadability(fileToCheck)
            if (e == null) {
                sb.appendln("$fileToCheck is accessible.").appendln()
            } else {
                sb.appendln(e.message)
                try {
                    sb.appendln("Trying to create $fileToCheck")
                    fileToCheck.createNewFile()
                    sb.appendln("Created $fileToCheck")
                    e = checkFileReadability(fileToCheck)
                    if (e == null) {
                        sb.appendln("$fileToCheck is accessible").appendln()
                    } else {
                        sb.appendln("e").appendln()
                    }
                } catch (e: Exception) {
                    sb.appendln("Could not create $fileToCheck").appendln(e).appendln()
                }
            }
        }

        if (!myObbFile.exists()) {
            sb.appendln("OBB file doesn't exist: $myObbFile").appendln()
            obbDumpText.text = sb.toString()
            return
        }

        e = checkFileReadability(myObbFile)
        if (e != null) {
            // Need to request READ_EXTERNAL_STORAGE permission before reading OBB file
            sb.appendln("Need READ_EXTERNAL_STORAGE permission.").appendln()
            obbDumpText.text = sb.toString()
            return
        }

        sb.appendln("OBB is accessible at")
            .appendln(myObbFile).appendln()

        mStorageManager = getSystemService(Context.STORAGE_SERVICE) as StorageManager
        obbDumpText.text = sb.toString()
    }

    private fun dumpMountedObb(obbMountPath: String) {
        val obbFile = File(obbMountPath)

        val sb = StringBuilder().appendln("Dumping OBB...").appendln()
        sb.appendln("OBB file path is $myObbFile").appendln()
        sb.appendln("OBB mounted at $obbMountPath").appendln()
        val listFiles = obbFile.listFiles()
        if (listFiles == null || listFiles.isEmpty()) {
            Log.d(TAG, "No files in obb!")
            return
        }
        sb.appendln("Contents of OBB").appendln()
        for (listFile in listFiles) {
            val zipFile = ZipFile(listFile)
            sb.appendln("${listFile.name} has ${zipFile.entries().toList().size} entries.")
                .appendln()
        }
        obbDumpText.text = sb.toString()
    }

    private fun checkFileReadability(file: File): Exception? {
        if (!file.exists()) {
            return IOException("$file does not exist")
        }

        var inputStream: FileInputStream? = null
        try {
            inputStream = FileInputStream(file).also { input ->
                input.read()
            }
        } catch (e: IOException) {
            return e
        } finally {
            inputStream?.close()
        }
        return null
    }

    fun onClick(view: View) {
        mStorageManager.mountObb(
            myObbFile.absolutePath,
            null,
            object : OnObbStateChangeListener() {
                override fun onObbStateChange(path: String, state: Int) {
                    super.onObbStateChange(path, state)
                    val mountPath = mStorageManager.getMountedObbPath(myObbFile.absolutePath)
                    dumpMountedObb(mountPath)
                }
            }
        )
    }

    companion object {
        const val BLOB_FILE_NAME = "main.314159.com.example.opaquebinaryblob.obb"
        const val TEST_FILE_NAME = "TestFile.txt"
        const val TAG = "MainActivity"
    }
}

activity_main.xml

<androidx.constraintlayout.widget.ConstraintLayout 
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="16dp"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/obbDumpText"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:scrollbars="vertical"
        android:text="Click the button to view content of the OBB."
        android:textColor="@android:color/black"
        app:layout_constraintBottom_toTopOf="@+id/dumpMountObb"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_chainStyle="spread_inside" />

    <Button
        android:id="@+id/dumpMountObb"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="onClick"
        android:text="Dump\nMounted OBB"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/obbDumpText"
        app:layout_constraintVertical_bias="0.79" />
</androidx.constraintlayout.widget.ConstraintLayout>

Для продолжения, как указано здесь :

Начиная с Android 4.4 (уровень API 19), приложения могут читать файлы расширения OBB без разрешения внешнего хранилища. Однако для некоторых реализаций Android 6.0 (уровень API 23) и более поздних версий все еще требуется разрешение, поэтому вам необходимо объявить разрешение READ_EXTERNAL_STORAGE в манифесте приложения и запросить разрешение во время выполнения ...

Это относится к Android Q? Это не понятно. Демо показывает, что это не для эмулятора. Я надеюсь, что это будет единообразно для всех устройств.

...