如何在 Android 11 上使用 ContentResolver 从另一个应用导出的 ContentProvider 中读取数据?

How do I use ContentResolver to read data from another app's exported ContentProvider on Android 11?

我有一个应用程序在 Android 清单文件

中包含导出的 ContentProvider
        <provider
            android:authorities="my.content.Provider"
            android:name="my.content.Provider"
            android:exported="true"
            tools:ignore="ExportedContentProvider"/>

此提供程序的实现如下所示:

package my.content

// imports excluded for brevity

class Provider : ContentProvider() {
    override fun query(
        uri: Uri, p: Array<out String>?, s: String?, sa: Array<out String>?, so: String?
    ): Cursor {
        return MatrixCursor(arrayOf("_id", "data")).apply {
            newRow()
                .add("_id", "1")
                .add("data", "data from the provider")
        }
    }

    override fun getType(uri: Uri) = "vnd.android.cursor.dir/vnd.my.content.Providers.data"
    override fun insert(uri: Uri, values: ContentValues?): Uri? = null
    override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?) = 0
    override fun update(uri: Uri, v: ContentValues?, s: String?, sa: Array<out String>?) = 0
    override fun onCreate() = true
}

我可以使用 adb 查询此提供程序:

$ adb shell content query --uri content://my.content.Provider
Row: 0 _id=1, data=data from the provider

但是,当 运行 在 Android 11 上时,我无法从另一个应用程序查询它。我正在尝试遵循 Android 开发人员文档 content providers,但没有成功。我尝试查询内容提供者的其他应用程序的主要 activity 代码:

val cursor = contentResolver.query(
    Uri.parse("content://my.content.Provider/data"),
    null,
    null,
    null,
    null
)
cursor?.apply {
    val dataIndex = getColumnIndex("data")
    while (moveToNext()) {
        println("Data retrieved: ${getString(dataIndex)}")
    }
    close()
}

当我 运行 时,我得到的只是一条打印在 logcat 中的错误消息:E/ActivityThread: Failed to find provider info for my.content.Provider

由于它与 adb 一起使用但与我的内容解析器应用程序不兼容,我认为内容解析器应用程序有问题,但我无法弄清楚是什么。以防万一,内容解析器和内容提供者应用程序都具有最低 sdk 级别 24 和 target/compile sdk 版本 30。

虽然大多数关于 package visibility rules 围绕活动的讨论,它也需要绑定到服务,显然,与内容提供者交互。

为了扩展 CommonsWare 接受的答案,观察到的问题是由于 Android 11 中引入的新隐私保护功能,如 Package visibility in Android 11 博客 post 中所述。

当在 Android 11 或更高版本上定位并 运行 时,<queries> element needs to be added to the manifest of the app that want to interact with other apps in various ways, including using content providers. The package visibility documentation 表示 Google Play 可能会分析清单文件并使用 <queries> 元素和 QUERY_ALL_PACKAGES 权限,以检测可能以不符合政策的方式访问个人和敏感用户数据的应用程序。

Also, filtered package visibility helps app stores like Google Play assess the privacy and security that your app provides for users. For example, Google Play considers the list of installed apps to be personal and sensitive user data.

因此,如果在 Android 11 上的另一个应用程序中使用内容提供程序,想要访问内容提供程序数据的内容解析器应用程序需要使用 <queries> 元素更新清单文件.它应该包括一个 <provider> 子项,指定它打算查询的内容提供者的权限:

    <queries>
        <provider android:authorities="my.content.Provider" />
    </queries>