Secure sharing of large datasets using data blobs

In some situations, such as those that involve machine learning or media playback, your app might want to use the same large dataset as another app.

To help reduce data redundancy, both over the network and on disk, Android 11 (API level 'R') caches shared datasets that support use cases such as machine learning and media playback. When your app needs access to a shared large dataset, it can first look for these cached datasets, called shared data blobs, before determining whether to download a new copy.

The system maintains the shared data blobs and controls which apps can access them. When your app contributes data blobs, you can indicate which other apps should have access by calling one of the following methods:

  • To grant access to a specific set of apps on a device, pass the package names of these apps into allowPackageAccess().
  • To allow only apps whose certificates are signed using the same key as the one used for your app—such as an app suite that you manage—call allowSameSignatureAccess().
  • To grant access to all apps on a device, call allowPublicAccess().

Access shared data blobs

The system represents each shared data blob using a BlobHandle object. Each instance of BlobHandle contains a cryptographically-secure hash and some identifying details for the dataset.

To access shared data blobs, download identifying details from the server. Using these details, check whether the dataset is already available on the system.

The next step depends on whether data is available.

Dataset available

If the dataset is already available on the device, then access it from the system, as shown in the following code snippet:

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 pfd = ParcelFileDescriptor.AutoCloseInputStream(
            blobStoreManager.openBlob(blobHandle))
    useDataset(pfd)
}

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 (ParcelFileDescriptor.AutoCloseInputStream pfd =
            new ParcelFileDescriptor.AutoCloseInputStream(
            blobStoreManager.openBlob(blobHandle))) {
        useDataset(pfd);
    }
}

Dataset unavailable

If the dataset isn't available, then download it from the server and contribute it to the system, as shown in the following code snippet:

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 pfd = ParcelFileDescriptor.AutoCloseOutputStream(
                session.openWrite(0, 1024 * 1024 * 200))
        writeDataset(pfd)

        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 (ParcelFileDescriptor.AutoCloseOutputStream pfd =
            new ParcelFileDescriptor.AutoCloseOutputStream(
            session.openWrite(0, 1024 * 1024 * 200)))
        writeDataset(pfd);
        session.allowSameSignatureAccess();
        session.allowPackageAccess(your-app-package,
                    app-certificate);
        session.allowPackageAccess(some-other-app-package,
                    app-certificate);
        session.commit(getMainExecutor(), callback);
    }
}