Block Store

Many users still manage their own credentials when setting up a new Android device. This manual process can become challenging and often results in a poor user experience. The Block Store API, a library powered by Google Play services, looks to solve this by providing a way for apps to save user credentials without the complexity or security risk associated with saving user passwords.

The Block Store API allows your app to store user credentials that it can later retrieve to re-authenticate users on a new device. This helps provide a more seamless experience for the user, as they don't need to see a sign-in screen when launching your app for the first time on the new device.

The benefits to using Block Store include the following:

  • Encrypted credential storage solution for developers. Credentials are end-to-end encrypted when possible.
  • Save tokens instead of usernames and passwords.
  • Eliminate friction from sign-in flows.
  • Save users from the burden of managing complex passwords.
  • Google verifies the user's identity.

Before you begin

To prepare your app, complete the steps in the following sections.

Configure your app

In your project-level build.gradle file, include Google's Maven repository in both your buildscript and allprojects sections:

buildscript {
  repositories {
    google()
    mavenCentral()
  }
}

allprojects {
  repositories {
    google()
    mavenCentral()
  }
}

Add the Google Play services dependency for the Block Store API to your module's Gradle build file, which is commonly app/build.gradle:

dependencies {
  implementation 'com.google.android.gms:play-services-auth-blockstore:16.1.0'
}

How it works

Block store is a token-based sign-in mechanism that is end-to-end encrypted and built on top of the backup and restore infrastructure. The following steps outline how an app utilizing Block Store would work:

  1. During your app’s authentication flow, or anytime thereafter, you can store the user’s authentication token to Block Store for later retrieval.
  2. The token will be stored locally and can also be backed up to the cloud, end-to-end encrypted when possible.
  3. Data is transferred when the user initiates a restore flow on a new device.
  4. If the user restores your app during the restore flow, your app can then retrieve the saved token from Block Store on the new device.

Saving the token

When a user signs into your app, you can save the authentication token that you generate for that user to Block Store. This is done by calling setBytes() on an instance of StoreBytesData.Builder to store the user's credentials to the source device. After you save the token with Block Store, the token is encrypted and stored locally on the device.

The following sample shows how to save the authentication token to the local device:

val client = Blockstore.getClient(this)
val data = StoreBytesData.Builder()
        .setBytes(/* BYTE_ARRAY */)
        .build()
client.storeBytes(data)
        .addOnSuccessListener{ result ->
            Log.d(TAG, "Stored: ${result} bytes")
        }
        .addOnFailureListener { e ->
            Log.e(TAG, “Failed to store bytes”, e)
        }

Retrieving the token

Later on, when a user goes through the restore flow on a new device, Google Play services first verifies the user, then retrieves your Block Store data. The user has already agreed to restore your app data as a part of the restore flow, so no additional consents are required. When the user opens your app, you can request your token from Block Store by calling retrieveBytes(). The retrieved token can then be used to keep the user signed in on the new device.

The following sample shows how to retrieve the encrypted token that was previously stored with Block Store:

val client = Blockstore.getClient(this)
client.retrieveBytes()
            .addOnSuccessListener { result ->
                Log.d(TAG, "Retrieved: ${String(result)}")
            }
            .addOnFailureListener { e ->
                Log.e(TAG, "Failed to retrieve bytes", e)
            }
}

End-to-end encryption

In order for end-to-end encryption to be made available, the device must be running Android 9 or higher, and the user must have set a screen lock (PIN, pattern, or password) for their device. You can verify if encryption will be available on the device by calling isEndToEndEncryptionAvailable().

The following sample shows how to verify if encryption will be available during cloud backup:

client.isEndToEndEncryptionAvailable()
        .addOnSuccessListener { result ->
          Log.d(TAG, "Will Block Store cloud backup be end-to-end encrypted? $result")
        }

Enable cloud backup

To enable cloud backup, add the setShouldBackupToCloud() method to your StoreBytesData object. Block Store will periodically backup to cloud the bytes stored when setShouldBackupToCloud() is set as true.

The following sample shows how to enable cloud backup only when cloud backup is end-to-end encrypted:

val client = Blockstore.getClient(this)
val storeBytesDataBuilder = StoreBytesData.Builder()
        .setBytes(/* BYTE_ARRAY */)

client.isEndToEndEncryptionAvailable()
        .addOnSuccessListener { isE2EEAvailable ->
          if (isE2EEAvailable) {
            storeBytesDataBuilder.setShouldBackupToCloud(true)
            Log.d(TAG, "E2EE is available, enable backing up bytes to the cloud.")

            client.storeBytes(storeBytesDataBuilder.build())
                .addOnSuccessListener { result ->
                  Log.d(TAG, "stored: ${result.getBytesStored()}")
                }.addOnFailureListener { e ->
                  Log.e(TAG, “Failed to store bytes”, e)
                }
          } else {
            Log.d(TAG, "E2EE is not available, only store bytes for D2D restore.")
          }
        }

How to test

Use the following methods during development in order to test the restore flows.

Same device uninstall/reinstall

If the user enables Backup services (it can be checked at Settings > Google > Backup), Block Store data is persisted across the app uninstall/reinstall.

You can follow these steps to test:

  1. Integrate the BlockStore API to your test app.
  2. Use the test app to invoke the BlockStore API to store your data.
  3. Uninstall your test app and then reinstall your app on the same device.
  4. Use the test app to invoke the BlockStore API to retrieve your data.
  5. Verify that the bytes retrieved are the same as what were stored before uninstallation.

Device-to-device

In most cases, this will require a factory reset of the target device. You can then enter the Android wireless restore flow or Google cable restore (for supported devices).

Cloud restore

  1. Integrate the Blockstore API to your test app. The test app needs to be submitted to the Play Store.
  2. On the source device, use the test app to invoke the Blockstore API to store your data, with shouldBackUpToCloud set to true.
  3. For O and above devices, you can manually trigger a Block Store cloud backup: go to Settings > Google > Backup, click the “Backup Now” button.
    1. To verify that Block Store cloud backup succeeded, you can:
      1. After the backup finishes, search for log lines with tag “CloudSyncBpTkSvc”.
      2. You should see lines like this: “......, CloudSyncBpTkSvc: sync result: SUCCESS, ..., uploaded size: XXX bytes ...”
    2. After a Block Store cloud backup, there’s a 5-minute “cool down” period. Within that 5 minutes, clicking the “Backup Now” button won’t trigger another Block Store cloud backup.
  4. Factory reset the target device and go through a cloud restore flow. Select to restore your test app during the restore flow. For more information about cloud restore flows, see Supported cloud restore flows.
  5. On the target device, use the test app to invoke the Blockstore API to retrieve your data.
  6. Verify that the bytes retrieved are the same as what were stored in the source device.

Device Requirements

End to End Encryption

  • End to End encryption is supported on devices running Android 9 (API 29) and above.
  • The device must have a screen lock set with a PIN, pattern, or password for end to end encryption to be enabled and correctly encrypt the user’s data.

Device to Device Restore Flow

Device to device restore will require you to have a source device and a target device. These will be the two devices which are transferring data.

Source devices must be running Android 6 (API 23) and above to backup.

Target devices running Android 9 (API 29) and above to have the ability to restore.

More information on the device to device restore flow can be found here.

Cloud Backup and Restore Flow

Cloud backup and restore will require a source device and a target device.

Source devices must be running Android 6 (API 23) and above to backup.

Target devices are supported based on their vendors. Pixel devices can use this feature from Android 9 (API 29) and all other devices must be running Android 12 (API 31) or above.