#android #android-contentprovider #android-fileprovider
#Android #android-contentprovider #android-fileprovider
Вопрос:
Я пытаюсь предоставить файл с помощью file provider, а затем прочитать этот файл в другом приложении, которое я разработал.
Вот код для приложения, которое предоставляет файл и запускает намерение:
val intent = Intent(ACTION_INSTALL)
intent.setPackage(servicePackage)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
intent.putExtra(EXTRA_URI,
"content://com.mma.apkprovider/apks//storage/emulated/0/main_app-release.apk")
// Launch the installer service
context.startForegroundService(intent)
Наряду со следующим AndroidManifest.xml:
<permission
android:name="com.mma.permissions.APK_PROVIDER"
android:protectionLevel="signature" />
<application>
<provider
android:name=".ApkProvider"
android:authorities="com.mma.mainapp.ApkProvider"
android:exported="false"
android:grantUriPermissions="true"
android:permission="com.mma.permissions.APK_PROVIDER" >
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/apk_paths" />
</provider>
</application>
Вот код для приложения, которое запускается из намерения прочитать файл:
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
log("onStartCommand called...")
log("action: ${intent?.action}")
log("flags: $flags")
log("action: $startId")
val action = intent?.action
val uri = intent?.getStringExtra(EXTRA_URI) ?: return START_NOT_STICKY
if (action == ACTION_INSTALL) {
log("Install APK $uri...")
executor.submit {
performInstallation(Uri.parse(uri))
}
}
return START_NOT_STICKY
}
private fun performInstallation(uri: Uri) {
try {
val packageInstaller = packageManager.packageInstaller
val sessionId = packageInstaller.createSession(
PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL
)
)
val session = packageInstaller.openSession(sessionId)
val uniqueName = UUID.randomUUID().toString()
//
val file = contentResolver.openFileDescriptor(uri, "r")
val size = file?.statSize ?: throw IOException("Couldn't read file from provider!")
file.close()
val outputStream = session.openWrite(uniqueName, 0, size)
val inputStream = contentResolver.openInputStream(uri)
?: throw IOException("Couldn't read file from provider!")
} catch (e: Exception) {
val intent = Intent(ACTION_RESULT).also { it.putExtra(EXTRA_RESULT, e.message) }
sendBroadcast(intent)
loge("Error installing $uri", e)
}
//...
}
Вот трассировка стека, которую я получаю из приложения установщика:
D Installer: onStartCommand called...
10-19 16:27:57.310 3814 3814 D Installer: action: com.mma.INSTALL
10-19 16:27:57.310 3814 3814 D Installer: flags: 0
10-19 16:27:57.310 3814 3814 D Installer: action: 3
10-19 16:27:57.324 3814 3814 D Installer: Install APK content://com.mma.main_app.ApkProvider/apks//storage/emulated/0/main_app-release.apk...
10-19 16:27:57.332 2607 2607 W Binder:2607_8: type=1400 audit(0.0:45866): avc: denied { dac_read_search } for capability=2 scontext=u:r:installd:s0 tcontext=u:r:installd:s0 tclass=capability permissive=0
10-19 16:27:57.576 3814 7880 E ActivityThread: Failed to find provider info for com.mma.main_app.ApkProvider
10-19 16:27:57.336 2607 2607 I chatty : uid=0(root) Binder:2607_2 identical 3 lines
10-19 16:27:57.336 2607 2607 W Binder:2607_8: type=1400 audit(0.0:45870): avc: denied { dac_read_search } for capability=2 scontext=u:r:installd:s0 tcontext=u:r:installd:s0 tclass=capability permissive=0
10-19 16:27:57.588 3814 7880 E Installer: Error installing content://com.mma.main_app.ApkProvider/apks//storage/emulated/0/main_app-release.apk
10-19 16:27:57.588 3814 7880 E Installer: java.io.FileNotFoundException: No content provider: content://com.mma.main_app.ApkProvider/apks//storage/emulated/0/main_app-release.apk
10-19 16:27:57.588 3814 7880 E Installer: at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:1445)
10-19 16:27:57.588 3814 7880 E Installer: at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:1296)
10-19 16:27:57.588 3814 7880 E Installer: at android.content.ContentResolver.openFileDescriptor(ContentResolver.java:1149)
10-19 16:27:57.588 3814 7880 E Installer: at android.content.ContentResolver.openFileDescriptor(ContentResolver.java:1103)
10-19 16:27:57.588 3814 7880 E Installer: at com.mma.installer.InstallerService.performInstallation(InstallerService.kt:74)
10-19 16:27:57.588 3814 7880 E Installer: at com.mma.installer.InstallerService.access$performInstallation(InstallerService.kt:18)
10-19 16:27:57.588 3814 7880 E Installer: at com.mma.installer.InstallerService$onStartCommand$1.run(InstallerService.kt:46)
10-19 16:27:57.588 3814 7880 E Installer: at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:458)
10-19 16:27:57.588 3814 7880 E Installer: at java.util.concurrent.FutureTask.run(FutureTask.java:266)
10-19 16:27:57.588 3814 7880 E Installer: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
10-19 16:27:57.588 3814 7880 E Installer: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
10-19 16:27:57.588 3814 7880 E Installer: at java.lang.Thread.run(Thread.java:764)
Поэтому у меня возникли проблемы с поиском поставщика контента из приложения установщика.
Я застрял на несколько дней на этом. Я новичок в использовании поставщиков / преобразователей контента, для меня это выглядит довольно сложно.
Редактировать
ApkProvider — это подкласс FileProvider:
import androidx.core.content.FileProvider
class ApkProvider : FileProvider() {}
Комментарии:
1.
content://com.mma.apkprovider/apks//storage/emulated/0/main_app-release.apk
Вы не используете FileProvider для создания uri. Более того, тот, который вы сделали, совершенно невозможен.2.
android:name=".ApkProvider"
Вы не используете FileProvider.3.
Intent(action)
Какое действие?4. В дополнение ко всем проблемам, указанным blackapps, ваши полномочия не совпадают. Ваше, к сожалению, жестко
Uri
запрограммированное использованиеcom.mma.apkprovider
, но вашеandroid:authorities
естьcom.mma.mainapp.ApkProvider
.5. Нет смысла использовать класс ApkProvider, поскольку он ничего не расширяет или не добавляет. Лучше использовать FileProvider напрямую. Кроме того, вы все еще создаете этот невозможный uri вместо использования FileProvider.getUriForFile().
Ответ №1:
В итоге я не использовал API FileProvider / ContentResolver / Uri, которые довольно подвержены ошибкам и не предоставляют четких предупреждений / сообщений об ошибках.
Файл, к которому нужно предоставить общий доступ, можно просто сохранить в /data/local/tmp («${Environment.getDataDirectory()}/local/tmp»)И извлекается из другого действия без определенных разрешений.
Запущенное приложение может просто прочитать файл из абсолютного пути:
val file = File(apkPath)
val inputStream = FileInputStream(file)