【问题标题】:Providing icon to system chooser via ChooserTargetService, FileProvider and grantUriPermission通过 ChooserTargetService、FileProvider 和 grantUriPermission 向系统选择器提供图标
【发布时间】:2017-10-09 06:58:14
【问题描述】:

我在本地应用程序中存储了一些与特定上下文(如联系人)相关的图像。我通过ChooserTargetService 使用直接共享(API 23+)来显示这些以供选择,我希望ChooserTarget 实例让Icon 填充这些图像。

所以我想我可以为此使用android.support.v4.content.FileProvider(在ChooserTargetService::onGetChooserTargets 内):

val file = File(File(filesDir, "images"), imageFileName)
val contentUri = FileProvider.getUriForFile(this, "com.company.fileprovider", file)
val icon = Icon.createWithContentUri(contentUri)

并在清单中:

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="com.mycompany.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">

    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_provider_paths"/>
</provider>

但问题是我遇到了异常

05-10 16:06:09.100 32444-32444/android:ui W/Icon: Unable to load image from URI: content://com.mycompany.fileprovider/images/icon_dice.png
java.lang.SecurityException: Permission Denial: reading android.support.v4.content.FileProvider uri content://com.mycompany.fileprovider/images/icon_dice.png from pid=32444, uid=1000 requires the provider be exported, or grantUriPermission()
    at android.os.Parcel.readException(Parcel.java:1684)
    at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:183)
    at android.database.DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(DatabaseUtils.java:146)
    at android.content.ContentProviderProxy.openTypedAssetFile(ContentProviderNative.java:692)
    at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:1147)
    at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:984)
    at android.content.ContentResolver.openInputStream(ContentResolver.java:704)
    at android.graphics.drawable.Icon.loadDrawableInner(Icon.java:335)
    at android.graphics.drawable.Icon.loadDrawable(Icon.java:272)
    at com.android.internal.app.ChooserActivity$ChooserTargetInfo.<init>(ChooserActivity.java:645)
    at com.android.internal.app.ChooserActivity$ChooserListAdapter.addServiceResults(ChooserActivity.java:1003)
    at com.android.internal.app.ChooserActivity$1.handleMessage(ChooserActivity.java:126)
    at android.os.Handler.dispatchMessage(Handler.java:102)
    at android.os.Looper.loop(Looper.java:154)
    at android.app.ActivityThread.main(ActivityThread.java:6119)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)

不可能遵循“导出”的建议,因为FileProvider 不幸的是硬编码不允许它(来自FileProvider.java android 支持库源代码):

// Sanity check our security
if (info.exported) {
    throw new SecurityException("Provider must not be exported");
}
if (!info.grantUriPermissions) {
    throw new SecurityException("Provider must grant uri permissions");
}

所以我试着打电话

grantUriPermission("<something goes here>", contentUri, Intent.FLAG_GRANT_READ_URI_PERMISSION)

但不清楚应该将什么作为包名的第一个参数。从异常详细信息中可以推断出代码在com.android.internal.app.ChooserActivity 中并被系统调用。

编辑:

使用Icon.createWithFilePath 是不可能的,因为你不能从不同的进程访问文件:

W/Icon: Unable to load image from path: /data/user/0/com.mycompany.app/files/images/image.png
java.io.FileNotFoundException: /data/user/0/com.mycompany.app/files/images/image.png (Permission denied)

如果您尝试将文件设置为已弃用的Context.MODE_WORLD_READABLE,您会得到:

java.lang.SecurityException: MODE_WORLD_READABLE no longer supported

在 Andorid 7 上。

【问题讨论】:

  • 恕我直言,最好使用不同的ContentProvider,它对您的文件具有只读支持并且可以导出。您提出的 hack 让我印象深刻,因为它可能适用于某些设备,但不适用于其他设备,基于 Android 更改、设备制造商更改、自定义 ROM 更改等。最终,我计划将此功能添加到my StreamProvider。恕我直言,ChooserTargetService 应该为您处理权限问题。
  • @CommonsWare 谢谢。我可能会使用您的StreamProvider。我最初尝试的是android:exported="true" + android:readPermission="android.permission.BIND_CHOOSER_TARGET_SERVICE"(与我的ChooserTargetService 实现的客户端所需的权限相同),如果不是FileProvider 中的硬编码ifs,它应该是简单有效且安全的。这个hack真的可以破解吗?应用程序选择器/直接共享处理程序是系统的一部分,不能像例如默认启动器应用程序。
  • 我不能排除有人将使用Icon 的系统部分的应用程序ID 更改为ChooserTargetService 的可能性。欢迎您尝试com.android.systemui 的软件包,因为它有效for notification sounds。或者,如果文件在外部存储上,请使用 Uri.fromFile()StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder().build()); 撤消 FileUriExposedException 检查。
  • 相关,但不完全相同:stackoverflow.com/questions/37860874/…

标签: android android-support-library android-contentprovider android-intent-chooser


【解决方案1】:

为什么不在创建ChooserTarget 之前使用文件中的数据创建图标的图像。这就是 Google 的 Messenger 应用所做的。

File file = new File(new File(filesDir, "images"), imageFileName);
Bitmap b = BitmapFactory.decodeStream(new FileInputStream(file));
Icon icon = Icon.createWithBitmap(b);
return new ChooserTarget(title, icon, score, cn, extras);

如果您愿意,您甚至可以对位图进行一些压缩。

但是,我确实必须警告您需要注意放置在活页夹上的这些对象的数量和大小......这些是 Parcelable 对象,从 7.0 开始,活页夹可以抛出 @987654323 @如果您将太多或太大的位图放入这些ChooserTargets 或通过它发送的任何 Parcelable。

【讨论】:

  • 这行不通。我在最后的问题中添加了为什么它不会的信息。
  • 更新答案。
  • 拉取 APK 并将其反编译为源代码 :-)
【解决方案2】:

MODE_WORLD_READABLE 是一个安全漏洞。
因此,谷歌首先弃用了它,然后将其完全删除。
MODE_WORLD_READABLE 在 Android M 之前的版本中已被弃用。在 Android N 中,它不再受支持并引发 SecurityException。

解决方案 : 请尝试不同的模式。我使用了 Context.MODE_PRIVATE 并且成功了。

【讨论】:

  • OP 的问题是关于跨进程文件共享...Context.MODE_PRIVATE 不起作用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-05
  • 1970-01-01
  • 2014-05-28
相关资源
最近更新 更多