Sklep blokowy

Wielu użytkowników nadal zarządza własnymi danymi logowania podczas konfigurowania nowego urządzenia z Androidem. Ten ręczny proces może być trudny i często prowadzi do pogorszenia wygody użytkowników. Interfejs Block Store API, czyli biblioteka oparta na usługach Google Play, ma rozwiązać ten problem, umożliwiając aplikacjom zapisywanie danych logowania użytkowników bez złożoności i zagrożeń związanych z zapisywaniem haseł.

Interfejs Block Store API umożliwia aplikacji przechowywanie danych, które może później pobrać, aby ponownie uwierzytelnić użytkowników na nowym urządzeniu. Dzięki temu użytkownik nie musi widzieć ekranu logowania, gdy po raz pierwszy uruchamia aplikację na nowym urządzeniu, co zapewnia mu większy komfort.

Korzystanie z Block Store ma te zalety:

  • Rozwiązanie do przechowywania zaszyfrowanych danych logowania dla deweloperów. Dane logowania są w miarę możliwości w pełni szyfrowane.
  • zapisywać tokeny zamiast nazw użytkowników i haseł;
  • Eliminowanie problemów w procesie logowania.
  • Ułatwianie użytkownikom zarządzania złożonymi hasłami.
  • Google weryfikuje tożsamość użytkownika.

Zanim zaczniesz

Aby przygotować aplikację, wykonaj czynności opisane w poniższych sekcjach.

Konfigurowanie aplikacji

W sekcjach buildscriptallprojects pliku build.gradle na poziomie projektu dodaj repozytorium Maven firmy Google:

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

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

Dodaj zależność Usług Google Play do interfejsu Block Store API do pliku build.gradle modułu. Jest to zwykle app/build.gradle:

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

Jak to działa

Usługa Block Store umożliwia deweloperom zapisywanie i przywracanie maksymalnie 16 tablic bajtów. Dzięki temu możesz zapisywać ważne informacje dotyczące bieżącej sesji użytkownika i mieć możliwość zapisywania tych informacji w dowolny sposób. Dane te mogą być w pełni szyfrowane, a infrastruktura obsługująca Block Store jest zbudowana na infrastrukturze tworzenia i przywracania kopii zapasowych.

W tym przewodniku omówimy przypadek użycia polegający na zapisywaniu tokena użytkownika w Block Store. Poniższe kroki pokazują, jak działa aplikacja korzystająca z Block Store:

  1. Podczas procesu uwierzytelniania aplikacji lub w dowolnym momencie później możesz zapisać token uwierzytelniania użytkownika w Block Store, aby móc go później pobrać.
  2. Token będzie przechowywany lokalnie i może być też zapisywany w chmurze jako kopia zapasowa. W miarę możliwości będzie on w pełni szyfrowany.
  3. Dane są przesyłane, gdy użytkownik rozpocznie proces przywracania na nowym urządzeniu.
  4. Jeśli użytkownik przywróci aplikację podczas procesu przywracania, będzie ona mogła pobrać zapisany token z Block Store na nowym urządzeniu.

Zapisywanie tokena

Gdy użytkownik zaloguje się w Twojej aplikacji, możesz zapisać w Block Store token uwierzytelniania, który wygenerujesz dla tego użytkownika. Możesz przechowywać ten token za pomocą unikalnej pary klucz-wartość, która może mieć maksymalnie 4 KB na wpis. Aby zapisać token, wywołaj funkcje setBytes()setKey() w instancji StoreBytesData.Builder, aby zapisać dane logowania użytkownika na urządzeniu źródłowym. Po zapisaniu tokena w Block Store jest on szyfrowany i przechowywany lokalnie na urządzeniu.

Poniższy przykład pokazuje, jak zapisać token uwierzytelniania na urządzeniu lokalnym:

Java

  BlockstoreClient client = Blockstore.getClient(this);
  byte[] bytes1 = new byte[] { 1, 2, 3, 4 };  // Store one data block.
  String key1 = "com.example.app.key1";
  StoreBytesData storeRequest1 = StoreBytesData.Builder()
          .setBytes(bytes1)
          // Call this method to set the key value pair the data should be associated with.
          .setKeys(Arrays.asList(key1))
          .build();
  client.storeBytes(storeRequest1)
    .addOnSuccessListener(result -> Log.d(TAG, "stored " + result + " bytes"))
    .addOnFailureListener(e -> Log.e(TAG, "Failed to store bytes", e));

Kotlin

  val client = Blockstore.getClient(this)

  val bytes1 = byteArrayOf(1, 2, 3, 4) // Store one data block.
  val key1 = "com.example.app.key1"
  val storeRequest1 = StoreBytesData.Builder()
    .setBytes(bytes1) // Call this method to set the key value with which the data should be associated with.
    .setKeys(Arrays.asList(key1))
    .build()
  client.storeBytes(storeRequest1)
    .addOnSuccessListener { result: Int ->
      Log.d(TAG,
            "Stored $result bytes")
    }
    .addOnFailureListener { e ->
      Log.e(TAG, "Failed to store bytes", e)
    }

Użyj domyślnego tokena

Dane zapisane za pomocą funkcji StoreBytes bez klucza używają klucza domyślnegoBlockstoreClient.DEFAULT_BYTES_DATA_KEY.

Java

  BlockstoreClient client = Blockstore.getClient(this);
  // The default key BlockstoreClient.DEFAULT_BYTES_DATA_KEY.
  byte[] bytes = new byte[] { 9, 10 };
  StoreBytesData storeRequest = StoreBytesData.Builder()
          .setBytes(bytes)
          .build();
  client.storeBytes(storeRequest)
    .addOnSuccessListener(result -> Log.d(TAG, "stored " + result + " bytes"))
    .addOnFailureListener(e -> Log.e(TAG, "Failed to store bytes", e));

Kotlin

  val client = Blockstore.getClient(this);
  // the default key BlockstoreClient.DEFAULT_BYTES_DATA_KEY.
  val bytes = byteArrayOf(1, 2, 3, 4)
  val storeRequest = StoreBytesData.Builder()
    .setBytes(bytes)
    .build();
  client.storeBytes(storeRequest)
    .addOnSuccessListener { result: Int ->
      Log.d(TAG,
            "stored $result bytes")
    }
    .addOnFailureListener { e ->
      Log.e(TAG, "Failed to store bytes", e)
    }

Pobieranie tokena

Gdy użytkownik przejdzie proces przywracania na nowym urządzeniu, usługi Google Play najpierw zweryfikują użytkownika, a potem pobiorą dane z Block Store. Użytkownik wyraził już zgodę na przywrócenie danych aplikacji w ramach procesu przywracania, więc nie są wymagane żadne dodatkowe zgody. Gdy użytkownik otworzy Twoją aplikację, możesz poprosić o token w Block Store, wywołując funkcję retrieveBytes(). Pobrany token można następnie wykorzystać, aby użytkownik pozostał zalogowany na nowym urządzeniu.

Poniższy przykład pokazuje, jak pobrać wiele tokenów na podstawie określonych kluczy.

Java

BlockstoreClient client = Blockstore.getClient(this);

// Retrieve data associated with certain keys.
String key1 = "com.example.app.key1";
String key2 = "com.example.app.key2";
String key3 = BlockstoreClient.DEFAULT_BYTES_DATA_KEY; // Used to retrieve data stored without a key

List requestedKeys = Arrays.asList(key1, key2, key3); // Add keys to array
RetrieveBytesRequest retrieveRequest = new RetrieveBytesRequest.Builder()
    .setKeys(requestedKeys)
    .build();

client.retrieveBytes(retrieveRequest)
    .addOnSuccessListener(
        result -> {
          Map<String, BlockstoreData> blockstoreDataMap = result.getBlockstoreDataMap();
          for (Map.Entry<String, BlockstoreData> entry : blockstoreDataMap.entrySet()) {
            Log.d(TAG, String.format(
                "Retrieved bytes %s associated with key %s.",
                new String(entry.getValue().getBytes()), entry.getKey()));
          }
        })
    .addOnFailureListener(e -> Log.e(TAG, "Failed to store bytes", e));

Kotlin

val client = Blockstore.getClient(this)

// Retrieve data associated with certain keys.
val key1 = "com.example.app.key1"
val key2 = "com.example.app.key2"
val key3 = BlockstoreClient.DEFAULT_BYTES_DATA_KEY // Used to retrieve data stored without a key

val requestedKeys = Arrays.asList(key1, key2, key3) // Add keys to array

val retrieveRequest = RetrieveBytesRequest.Builder()
  .setKeys(requestedKeys)
  .build()

client.retrieveBytes(retrieveRequest)
  .addOnSuccessListener { result: RetrieveBytesResponse ->
    val blockstoreDataMap =
      result.blockstoreDataMap
    for ((key, value) in blockstoreDataMap) {
      Log.d(ContentValues.TAG, String.format(
        "Retrieved bytes %s associated with key %s.",
        String(value.bytes), key))
    }
  }
  .addOnFailureListener { e: Exception? ->
    Log.e(ContentValues.TAG,
          "Failed to store bytes",
          e)
  }

Pobieranie wszystkich tokenów.

Poniżej znajdziesz przykład pobierania wszystkich tokenów zapisanych w BlockStore.

Java

BlockstoreClient client = Blockstore.getClient(this)

// Retrieve all data.
RetrieveBytesRequest retrieveRequest = new RetrieveBytesRequest.Builder()
    .setRetrieveAll(true)
    .build();

client.retrieveBytes(retrieveRequest)
    .addOnSuccessListener(
        result -> {
          Map<String, BlockstoreData> blockstoreDataMap = result.getBlockstoreDataMap();
          for (Map.Entry<String, BlockstoreData> entry : blockstoreDataMap.entrySet()) {
            Log.d(TAG, String.format(
                "Retrieved bytes %s associated with key %s.",
                new String(entry.getValue().getBytes()), entry.getKey()));
          }
        })
    .addOnFailureListener(e -> Log.e(TAG, "Failed to store bytes", e));

Kotlin

val client = Blockstore.getClient(this)

val retrieveRequest = RetrieveBytesRequest.Builder()
  .setRetrieveAll(true)
  .build()

client.retrieveBytes(retrieveRequest)
  .addOnSuccessListener { result: RetrieveBytesResponse ->
    val blockstoreDataMap =
      result.blockstoreDataMap
    for ((key, value) in blockstoreDataMap) {
      Log.d(ContentValues.TAG, String.format(
        "Retrieved bytes %s associated with key %s.",
        String(value.bytes), key))
    }
  }
  .addOnFailureListener { e: Exception? ->
    Log.e(ContentValues.TAG,
          "Failed to store bytes",
          e)
  }
.

Poniżej znajdziesz przykład pobierania klucza domyślnego.

Java

BlockStoreClient client = Blockstore.getClient(this);
RetrieveBytesRequest retrieveRequest = new RetrieveBytesRequest.Builder()
    .setKeys(Arrays.asList(BlockstoreClient.DEFAULT_BYTES_DATA_KEY))
    .build();
client.retrieveBytes(retrieveRequest);

Kotlin

val client = Blockstore.getClient(this)

val retrieveRequest = RetrieveBytesRequest.Builder()
  .setKeys(Arrays.asList(BlockstoreClient.DEFAULT_BYTES_DATA_KEY))
  .build()
client.retrieveBytes(retrieveRequest)

Usuwanie tokenów

Usuwanie tokenów z BlockStore może być konieczne z tych powodów:

  • Użytkownik przechodzi proces wylogowywania.
  • Token został unieważniony lub jest nieprawidłowy.

Podobnie jak w przypadku pobierania tokenów możesz określić, które tokeny mają zostać usunięte, ustawiając tablicę kluczy, które wymagają usunięcia.

Poniższy przykład pokazuje, jak usunąć niektóre klucze:

Java

BlockstoreClient client = Blockstore.getClient(this);

// Delete data associated with certain keys.
String key1 = "com.example.app.key1";
String key2 = "com.example.app.key2";
String key3 = BlockstoreClient.DEFAULT_BYTES_DATA_KEY; // Used to delete data stored without key

List requestedKeys = Arrays.asList(key1, key2, key3) // Add keys to array
DeleteBytesRequest deleteRequest = new DeleteBytesRequest.Builder()
      .setKeys(requestedKeys)
      .build();
client.deleteBytes(deleteRequest)

Kotlin

val client = Blockstore.getClient(this)

// Retrieve data associated with certain keys.
val key1 = "com.example.app.key1"
val key2 = "com.example.app.key2"
val key3 = BlockstoreClient.DEFAULT_BYTES_DATA_KEY // Used to retrieve data stored without a key

val requestedKeys = Arrays.asList(key1, key2, key3) // Add keys to array

val retrieveRequest = DeleteBytesRequest.Builder()
      .setKeys(requestedKeys)
      .build()

client.deleteBytes(retrieveRequest)

Usuń wszystkie tokeny

Ten przykład pokazuje, jak usunąć wszystkie tokeny zapisane obecnie w BlockStore:

Java

// Delete all data.
DeleteBytesRequest deleteAllRequest = new DeleteBytesRequest.Builder()
      .setDeleteAll(true)
      .build();
client.deleteBytes(deleteAllRequest)
.addOnSuccessListener(result -> Log.d(TAG, "Any data found and deleted? " + result));

Kotlin

  val deleteAllRequest = DeleteBytesRequest.Builder()
  .setDeleteAll(true)
  .build()
retrieve bytes, the key BlockstoreClient.DEFAULT_BYTES_DATA_KEY can be used
in the RetrieveBytesRequest instance in order to get your saved data

The following example shows how to retrieve the default key.

Java

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")
        }

Włącz tworzenie kopii zapasowej w chmurze

Aby włączyć tworzenie kopii zapasowej w chmurze, dodaj metodę setShouldBackupToCloud() do obiektu StoreBytesData. Usługa Block Store będzie okresowo tworzyć w chmurze kopie zapasowe bajtów przechowywanych, gdy wartość parametru setShouldBackupToCloud() jest ustawiona na true.

Poniższy przykład pokazuje, jak włączyć kopię zapasową w chmurze tylko wtedy, gdy jest ona w pełni szyfrowana:

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.")
          }
        }

Jak przeprowadzić test

Podczas tworzenia aplikacji użyj tych metod, aby przetestować procesy przywracania.

Odinstalowanie i ponowne zainstalowanie na tym samym urządzeniu

Jeśli użytkownik włączy usługi tworzenia kopii zapasowej (można to sprawdzić w sekcji Ustawienia > Google > Kopia zapasowa), dane Block Store będą zachowywane po odinstalowaniu i ponownym zainstalowaniu aplikacji.

Aby to sprawdzić, wykonaj te czynności:

  1. Zintegruj interfejs Block Store API z aplikacją testową.
  2. Użyj aplikacji testowej, aby wywołać interfejs Block Store API i zapisać dane.
  3. Odinstaluj aplikację testową, a następnie ponownie zainstaluj aplikację na tym samym urządzeniu.
  4. Użyj aplikacji testowej, aby wywołać interfejs Block Store API i pobrać dane.
  5. Sprawdź, czy pobrane bajty są takie same jak te, które były przechowywane przed odinstalowaniem.

Urządzenie-urządzenie

W większości przypadków będzie to wymagać przywrócenia urządzenia docelowego do ustawień fabrycznych. Następnie możesz przejść do bezprzewodowego przywracania danych na Androidzie lub przywracania danych za pomocą kabla Google (w przypadku obsługiwanych urządzeń).

Przywracanie z chmury

  1. Zintegruj interfejs Block Store API z aplikacją testową. Aplikację testową musisz przesłać do Sklepu Play.
  2. Na urządzeniu źródłowym użyj aplikacji testowej, aby wywołać interfejs Block Store API i zapisać dane, ustawiając wartość shouldBackUpToCloud na true.
  3. Na urządzeniach z Androidem O i nowszym możesz ręcznie uruchomić tworzenie kopii zapasowej w chmurze Block Store: otwórz Ustawienia > Google > Kopia zapasowa i kliknij przycisk „Utwórz kopię zapasową teraz”.
    1. Aby sprawdzić, czy tworzenie kopii zapasowej w chmurze w Block Store się powiodło, możesz:
      1. Po zakończeniu tworzenia kopii zapasowej wyszukaj wiersze logów z tagiem „CloudSyncBpTkSvc”.
      2. Powinny się pojawić wiersze podobne do tego: „......, CloudSyncBpTkSvc: sync result: SUCCESS, ..., uploaded size: XXX bytes ...”
    2. Po utworzeniu kopii zapasowej w chmurze Block Store następuje 5-minutowy okres „ochłonięcia”. W ciągu tych 5 minut kliknięcie przycisku „Utwórz kopię zapasową teraz” nie spowoduje utworzenia kolejnej kopii zapasowej w chmurze Block Store.
  4. Przywróć urządzenie docelowe do ustawień fabrycznych i wykonaj proces przywracania z chmury. Wybierz aby przywrócić aplikację testową podczas procesu przywracania. Więcej informacji o procesach przywracania z chmury znajdziesz w artykule Obsługiwane procesy przywracania z chmury.
  5. Na urządzeniu docelowym użyj aplikacji testowej, aby wywołać interfejs Block Store API i pobrać dane.
  6. Sprawdź, czy pobrane bajty są takie same jak te, które zostały zapisane na urządzeniu źródłowym.

Wymagania dotyczące urządzeń

Pełne szyfrowanie

  • Pełne szyfrowanie jest obsługiwane na urządzeniach z Androidem 9 (API 29) i nowszym.
  • Aby włączyć pełne szyfrowanie i prawidłowo zaszyfrować dane użytkownika, urządzenie musi mieć ustawioną blokadę ekranu z kodem PIN, wzorem lub hasłem.

Proces przywracania danych z urządzenia na urządzenie

Przywracanie danych z urządzenia na urządzenie wymaga urządzenia źródłowego i docelowego. Będą to 2 urządzenia, między którymi będą przesyłane dane.

Urządzenia źródłowe muszą mieć Androida 6 (API 23) lub nowszego, aby można było utworzyć kopię zapasową.

Urządzenia docelowe z Androidem 9 (API 29) lub nowszym, które mają możliwość przywracania.

Więcej informacji o procesie przywracania danych z urządzenia na urządzenie znajdziesz tutaj.

Przepływ tworzenia i przywracania kopii zapasowej w chmurze

Kopia zapasowa w chmurze i przywracanie danych wymagają urządzenia źródłowego i urządzenia docelowego.

Urządzenia źródłowe muszą mieć Androida 6 (API 23) lub nowszego, aby można było utworzyć kopię zapasową.

Urządzenia docelowe są obsługiwane na podstawie ich dostawców. Na urządzeniach Pixel ta funkcja jest dostępna od Androida 9 (API 29), a na wszystkich innych urządzeniach musi być zainstalowany Android 12 (API 31) lub nowszy.