如何检查可用于 Intent 的 ContentProvider(或阻止特定的)

Posted

技术标签:

【中文标题】如何检查可用于 Intent 的 ContentProvider(或阻止特定的)【英文标题】:How to inspect ContentProvider available for Intent (or block specific one) 【发布时间】:2018-01-27 20:04:43 【问题描述】:

我使用 Intent 机制让用户通过标准方式选择图像

    val intent = Intent(Intent.ACTION_GET_CONTENT)
    intent.type = "image/*"
    intent.addCategory(Intent.CATEGORY_OPENABLE)
    ctx.startActivityForResult(intent, RequestCodes.SelectPhoto)

然后我将 Uri 传递给另一个活动以裁剪照片。我需要 Uri 之前进行一些预检查。

android 模拟器上,Photos 等默认提供程序(显然)授予我整个应用程序打开 Uri 的权限,而不仅仅是请求活动。然而,在亚洲有一个“奇怪”的提供者 com.miui.gallery.provider.GalleryOpenProvider 没有——在cropper 中发生了一个邪恶的 SecurityException。

所以我尝试使用 ACTION_OPEN_DOCUMENT,根据规范,它会授予我的整个应用程序权限,直到设备重新启动,但不幸的是,在模拟器中不支持云中的 Google 照片。

所以我正在寻找一种方法来确定com.miui.gallery.provider.GalleryOpenProvider 是否会出现在 GET_CONTENT 的列表中,如果是的话,要么阻止它,要么回退到使用 ACTION_OPEN_DOCUMENT。我想避免在将 Uri 提供给裁剪器之前复制流,裁剪活动将其视为只读。

这是启动裁剪 (kotlin) 的完整功能。 CropActivity 是对旧的开源图库应用 com.android.gallery3d 的修改。

private fun startCrop(ctx: Activity, uri: Uri) 
    val intent = Intent(ctx, CropActivity::class.java)
    intent.data = uri
    val file = this.createImageFile(ctx, "photofinal")
    if (file == null) 
        this.showStorageUnavailable(ctx)
        return
    
    val outputUri = Uri.fromFile(file)
    intent.putExtra(MediaStore.EXTRA_OUTPUT, outputUri)
    intent.putExtra(CropExtras.KEY_MIN_CROP_SIDE, Config.minimumImageDimension)
    intent.putExtra(CropExtras.KEY_MOST_OBLONG_ASPECT, Config.maxPhotoAspectRatio)
    intent.putExtra(CropExtras.KEY_EXIF_ORIENTATION, exifOrientation)
    ctx.startActivityForResult(intent, RequestCodes.CropPhoto)

【问题讨论】:

还显示您开始第二个活动的完整意图。收割机。 通过 ACTION_GET_CONTENT 获得的 Uries 始终是只读的。 缺少FLAG_GRANT_READ_URI_PERMISSION来转移获得的权限。 【参考方案1】:

然后我将 Uri 传递给另一个活动以裁剪照片

Intent 的“数据”方面传递Uri,并添加FLAG_GRANT_READ_URI_PERMISSION 以将读取访问权转移到其他组件。见this sample app:

  @Override
  public void onActivityResult(int requestCode, int resultCode,
                               Intent resultData) 
    if (resultCode==Activity.RESULT_OK) 
      getActivity()
        .startService(new Intent(getActivity(), DurablizerService.class)
          .setData(resultData.getData())
          .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION));
    
  

在这里,我碰巧将Uri 传递给服务,但同样的原则也适用于活动。

有关Uri 访问生命周期的更多信息,另请参阅this blog post。

或者,不要使用单独的活动,而是做其他事情(例如,多个片段)。

在 Android 模拟器上,Photos 等默认提供程序(显然)允许我的整个应用程序打开 Uri,而不仅仅是请求活动。

如果Uri 具有file 方案或来自导出的无权限ContentProvider,则会发生这种情况。

所以我尝试使用 ACTION_OPEN_DOCUMENT,根据规范,它会授予我的整个应用权限,直到设备重新启动

它遵循与您从ACTION_GET_CONTENT 获得的Uri 值相同的一般规则。

所以我正在寻找一种方法来确定 com.miui.gallery.provider.GalleryOpenProvider 是否会出现在 GET_CONTENT 的列表中

严格来说这是不可能的。任何应用程序都可以从该提供商返回Uri。在实践中,该提供程序只能由其托管应用程序使用。如果您找到了该提供程序的应用程序包名称,并且您在 PackageManager 上使用了 queryIntentActivities() 和您的 ACTION_GET_CONTENT Intent,您可以确定来自该应用程序的活动是否在 ACTION_GET_CONTENT 实现列表中。

但是,如果您使用 FLAG_GRANT_READ_URI_PERMISSION,正如我之前提到的,那应该没有必要。

如果是这样,要么阻止它

除了滚动您自己的“选择器”式 UI 之外,这在严格意义上是不可能的。

【讨论】:

但我认为,根据文档,FLAG_GRANT_READ_URI_PERMISSION 仅用于表示您已获得来自 ContentProvider 的许可。开发者网站上是否提到了这种用法(转发授予的权限)? @user3175580:FLAG_GRANT_READ_URI_PERMISSION 的主要用途是将Uri 标识的内容的权限授予Intent 的接收者组件。内容不是您“拥有”的事实并不重要。如果您拥有内容的权利,您可以使用FLAG_GRANT_READ_URI_PERMISSION 和/或FLAG_GRANT_WRITE_URI_PERMISSION 转发这些内容。 @user3175580:“开发者网站上是否提到过这种用法...?” ——嗯,这就是the JavaDocs 所拥有的。例如,它也是used by FileProvider(在这种情况下,用于授予其他应用程序的权限)。但是,文档还有很多不足之处。 :-(

以上是关于如何检查可用于 Intent 的 ContentProvider(或阻止特定的)的主要内容,如果未能解决你的问题,请参考以下文章

Intent详解

Android Intent基本使用

Intent系列讲解---Intent简介以及相关属性

Android Intent用法总结

Android笔记02:Intent机制详解

Intent和IntentFilter详解