使用数据 blob 安全共享大型数据集

在某些情况下,例如涉及机器学习或媒体播放时,您的应用可能需要与其他应用共用一个大型数据集。

为帮助减少网络中和磁盘上的数据冗余,Android 11(API 级别 30)会缓存共享数据集,为机器学习和媒体播放等用例提供支持。当您的应用需要访问大型共享数据集时,可以先查找是否有这类缓存的数据集(称为共享数据 blob),然后再决定是否下载新副本。应用可以通过 BlobStoreManager(Android 11 中的新系统服务管理器)中的 API 使用此共享数据集功能。

系统维护着共享数据 blob 并控制着哪些应用可以访问它们。当您的应用提供数据 blob 时,您可以通过调用以下方法之一指明应该对其具有访问权限的其他应用:

  • 如需向设备上的一组特定应用授予访问权限,请将这些应用的软件包名称传递到 allowPackageAccess() 中。
  • 要仅允许证书签名所用密钥与您的应用所用密钥相同的应用(例如您管理的应用套件),请调用 allowSameSignatureAccess()
  • 如需向设备上的所有应用授予访问权限,请调用 allowPublicAccess()

访问共享数据 blob

系统使用 BlobHandle 对象表示每个共享数据 blob。每个 BlobHandle 实例都包含相应数据集的加密安全哈希值和一些识别性详细信息。

如需访问共享数据 blob,请从服务器下载识别性详细信息。使用这些详细信息检查系统上是否已存在相应数据集。

下一步取决于是否已存在相应数据。

已存在数据集

如果设备上已存在相应数据集,请从系统访问该数据集,如以下代码段所示:

Kotlin

val blobStoreManager =
        getSystemService(Context.BLOB_STORE_SERVICE) as BlobStoreManager
// The label "Sample photos" is visible to the user.
val blobHandle = BlobHandle.createWithSha256(sha256DigestBytes,
        "Sample photos",
        System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1),
        "photoTrainingDataset")
try {
    val input = ParcelFileDescriptor.AutoCloseInputStream(
            blobStoreManager.openBlob(blobHandle))
    useDataset(input)
}

Java

BlobStoreManager blobStoreManager =
        ((BlobStoreManager) getSystemService(Context.BLOB_STORE_SERVICE));
if (blobStoreManager != null) {
    // The label "Sample photos" is visible to the user.
    BlobHandle blobHandle = BlobHandle.createWithSha256(
            sha256DigestBytes,
            "Sample photos",
            System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1),
            "photoTrainingDataset");
    try (InputStream input = new ParcelFileDescriptor.AutoCloseInputStream(
            blobStoreManager.openBlob(blobHandle))) {
        useDataset(input);
    }
}

不存在数据集

如果不存在相应数据集,请从服务器下载该数据集并将其提供给系统,如以下代码段所示:

Kotlin

val sessionId = blobStoreManager.createSession(blobHandle)
try {
    val session = blobStoreManager.openSession(sessionId)
    try {
        // For this example, write 200 MiB at the beginning of the file.
        val output = ParcelFileDescriptor.AutoCloseOutputStream(
                session.openWrite(0, 1024 * 1024 * 200))
        writeDataset(output)

        session.apply {
            allowSameSignatureAccess()
            allowPackageAccess(your-app-package,
                    app-certificate)
            allowPackageAccess(some-other-app-package,
                    app-certificate)
            commit(mainExecutor, callback)
        }
    }
}

Java

long sessionId = blobStoreManager.createSession(blobHandle);
try (BlobStoreManager.Session session =
        blobStoreManager.openSession(sessionId)) {
    // For this example, write 200 MiB at the beginning of the file.
    try (OutputStream output = new ParcelFileDescriptor.AutoCloseOutputStream(
            session.openWrite(0, 1024 * 1024 * 200)))
        writeDataset(output);
        session.allowSameSignatureAccess();
        session.allowPackageAccess(your-app-package,
                    app-certificate);
        session.allowPackageAccess(some-other-app-package,
                    app-certificate);
        session.commit(getMainExecutor(), callback);
    }
}