Sklep blokowy

Wielu użytkowników nadal samodzielnie zarządza danymi logowania podczas konfigurowania nowego urządzenia z Androidem. Ten ręczny proces bywa trudny i często pogarsza wrażenia użytkowników. Block Store API to biblioteka wykorzystująca Usługi Google Play, która ma na celu rozwiązanie tego problemu, zapewniając aplikacjom możliwość zapisywania danych logowania użytkowników bez konieczności zwiększania złożoności i bezpieczeństwa związanych z zapisywaniem haseł użytkowników.

Interfejs Block Store API umożliwia aplikacji przechowywanie danych, które mogą później pobrać, aby ponownie uwierzytelnić użytkowników na nowym urządzeniu. Dzięki temu użytkownicy będą mogli wygodniej korzystać z aplikacji, ponieważ nie muszą widzieć ekranu logowania przy pierwszym uruchomieniu na nowym urządzeniu.

Zalety korzystania z funkcji Block Store:

  • Rozwiązanie dla deweloperów do przechowywania zaszyfrowanych danych logowania. Dane logowania są w miarę możliwości w pełni szyfrowane.
  • Zapisywanie tokenów zamiast nazw użytkowników i haseł.
  • Wyeliminuj problemy związane z procesami logowania.
  • Ułatw użytkownikom zarządzanie złożonymi hasłami.
  • Google weryfikuje tożsamość użytkownika.

Zanim zaczniesz

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

Konfiguracja aplikacji

W sekcjach buildscript i allprojects w pliku build.gradle na poziomie projektu umieść repozytorium Google Maven:

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.2.0'
}

Jak to działa

Magazyn blokowy umożliwia programistom zapisywanie i przywracanie do 16-bajtowych tablic. Dzięki temu możesz zapisać ważne informacje o bieżącej sesji użytkownika i możesz je zapisać w dowolny sposób. Dane te mogą być w pełni szyfrowane, a infrastruktura obsługująca magazyn blokowy jest zbudowana na bazie infrastruktury tworzenia i przywracania kopii zapasowych.

Z tego przewodnika dowiesz się, jak zapisać token użytkownika w Block Store. Aby zobaczyć, jak działa aplikacja korzystająca z funkcji Block Store:

  1. Podczas procesu uwierzytelniania w aplikacji lub w dowolnym momencie po jej zakończeniu możesz zapisać token uwierzytelniania użytkownika w Block Store, aby móc go później pobrać.
  2. Token będzie przechowywany lokalnie, a jego kopia zapasowa może być też tworzona w chmurze, w pełni szyfrowana, gdy tylko jest to możliwe.
  3. Dane są przenoszone, gdy użytkownik rozpocznie proces przywracania na nowym urządzeniu.
  4. Jeśli użytkownik przywróci Twoją aplikację podczas procesu przywracania, będzie ona mogła pobrać zapisany token z Block Store na nowym urządzeniu.

Zapisuję token

Gdy użytkownik loguje się w Twojej aplikacji, możesz zapisać wygenerowany dla niego token uwierzytelniania w programie Block Store. Możesz przechowywać ten token przy użyciu unikalnej wartości pary kluczy o maksymalnym rozmiarze 4 KB na wpis. Aby zapisać token, wywołaj setBytes() i setKey() w instancji StoreBytesData.Builder w celu zapisania danych użytkownika na urządzeniu źródłowym. Gdy zapiszesz token w magazynie bloków, zostanie on zaszyfrowany i zapisany lokalnie na urządzeniu.

Z przykładu poniżej dowiesz się, 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 tokena domyślnego

Dane zapisywane za pomocą StoreBytes bez klucza używają domyślnego klucza BlockstoreClient.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)
    }

Pobieram token

Później, gdy użytkownik przechodzi przez proces przywracania na nowym urządzeniu, Usługi Google Play najpierw go weryfikuje, a potem pobiera dane z funkcji Block Store. Użytkownik zgodził się już na przywrócenie danych aplikacji w ramach procesu przywracania, więc nie są wymagane dodatkowe zgody. Gdy użytkownik otworzy aplikację, możesz poprosić o token z Block Store, wywołując metodę retrieveBytes(). Pobrany token może następnie posłużyć do utrzymywania zalogowania użytkownika na nowym urządzeniu.

Z przykładu poniżej dowiesz się, jak pobrać wiele tokenów na podstawie konkretnych 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 blockstoreDataMap = result.getBlockstoreDataMap();
          for (Map.Entry 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)
  }

Pobieram wszystkie tokeny.

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 blockstoreDataMap = result.getBlockstoreDataMap();
          for (Map.Entry 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)

Usuwam tokeny

Usunięcie tokenów z BlockStore może być wymagane z tych powodów:

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

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

Poniżej znajdziesz przykład usuwania niektórych kluczy.

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 spowoduje usunięcie wszystkich tokenów zapisanych 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()
client.deleteBytes(deleteAllRequest)
  .addOnSuccessListener { result: Boolean ->
    Log.d(TAG,
          "Any data found and deleted? $result")
  }

Pełne szyfrowanie

Aby można było udostępnić pełne szyfrowanie, na urządzeniu musi być zainstalowany Android 9 lub nowszy, a użytkownik musi ustawić na urządzeniu blokadę ekranu (kod PIN, wzór lub hasło). Aby sprawdzić, czy na urządzeniu będzie dostępne szyfrowanie, wywołaj isEndToEndEncryptionAvailable().

Z przykładu poniżej dowiesz się, jak sprawdzić, czy podczas tworzenia kopii zapasowej w chmurze będzie dostępne szyfrowanie:

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

Włącz kopię zapasową w chmurze

Aby włączyć tworzenie kopii zapasowych w chmurze, dodaj metodę setShouldBackupToCloud() do obiektu StoreBytesData. Jeśli zasada setShouldBackupToCloud() ma wartość true, będzie okresowo tworzyć w chmurze kopie zapasowe przechowywanych bajtów.

Z przykładu poniżej dowiesz się, jak włączyć kopię zapasową w chmurze tylko wtedy, gdy kopia zapasowa w chmurze jest 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 to sprawdzić

Podczas programowania korzystaj z poniższych metod, aby przetestować procesy przywracania.

Odinstalowanie/ponowna instalacja na tym samym urządzeniu

Jeśli użytkownik włączy usługi kopii zapasowych (możesz je zaznaczyć w sekcji Ustawienia > Google > Kopia zapasowa), dane dotyczące zablokowanych Sklepu są zachowane podczas odinstalowywania/ponownego instalowania aplikacji.

Aby to zrobić, wykonaj te czynności:

  1. Zintegruj interfejs BlockStore API z aplikacją testową.
  2. Użyj aplikacji testowej, aby wywołać interfejs BlockStore API w celu przechowywania danych.
  3. Odinstaluj aplikację testową, a potem zainstaluj ją ponownie na tym samym urządzeniu.
  4. Użyj aplikacji testowej, aby wywołać interfejs BlockStore API w celu pobrania danych.
  5. Sprawdź, czy pobrane bajty są takie same jak te zapisane przed odinstalowaniem.

Połączenia między urządzeniami

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

Przywracanie w chmurze

  1. Zintegruj interfejs Blockstore 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 Blockstore API w celu przechowywania danych. Parametr powinienBackUpToCloud ustawiony na wartość „true” (prawda).
  3. W przypadku urządzeń w wersji O lub nowszej możesz ręcznie aktywować tworzenie kopii zapasowej w chmurze przez Block Store: otwórz Ustawienia > Google > Kopia zapasowa i kliknij przycisk „Utwórz kopię zapasową teraz”.
    1. Aby sprawdzić, czy udało się utworzyć kopię zapasową w chmurze Block Store, możesz wykonać te czynności:
      1. Po zakończeniu tworzenia kopii zapasowej wyszukaj wiersze logu z tagiem „CloudSyncBpTkSvc”.
      2. Powinny się wyświetlić wiersze podobne do tych: „......, CloudSyncBpTkSvc: syncresult: success, ..., uploaded size: XXX bytes ...”
    2. Po utworzeniu kopii zapasowej w chmurze Block Store obowiązuje 5-minutowy okres oczekiwania. W ciągu tych 5 minut kliknięcie przycisku „Utwórz kopię zapasową teraz” nie spowoduje uruchomienia kolejnej kopii zapasowej w chmurze Block Store.
  4. Przywróć urządzenie docelowe do ustawień fabrycznych i przeprowadź proces przywracania do chmury. Wybierz tę opcję, aby w trakcie procesu przywracania przywrócić aplikację testową. Więcej informacji o procesach przywracania chmury znajdziesz w artykule Obsługiwane przepływy przywracania w chmurze.
  5. Na urządzeniu docelowym użyj aplikacji testowej, aby wywołać interfejs Blockstore API w celu pobrania danych.
  6. Sprawdź, czy pobrane bajty są takie same jak te zapisane na urządzeniu źródłowym.

Wymagania dotyczące urządzenia

Pełne szyfrowanie

  • Pełne szyfrowanie jest obsługiwane na urządzeniach z Androidem 9 (API 29) lub nowszym.
  • Aby można było 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 między urządzeniami

Aby przywrócić dane z urządzenia, musisz mieć urządzenie źródłowe i docelowe. Będą to urządzenia służące do przenoszenia danych.

Na urządzeniach źródłowych musi być zainstalowany Android 6 (API 23) lub nowszy, aby można było utworzyć kopię zapasową.

Określ kierowanie urządzeń z Androidem 9 (API 29) lub nowszym, aby mieć możliwość przywracania.

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

Proces tworzenia i przywracania kopii zapasowych w Cloud

Tworzenie i przywracanie kopii zapasowych w chmurze wymaga urządzenia źródłowego i urządzenia docelowego.

Na urządzeniach źródłowych musi być zainstalowany Android 6 (API 23) lub nowszy, aby można było utworzyć kopię zapasową.

Urządzenia docelowe są obsługiwane w zależności od dostawców. Urządzenia Pixel mogą korzystać z tej funkcji w wersji od Androida 9 (API 29), a pozostałe urządzenia muszą mieć zainstalowany Android 12 (API 31) lub nowszy.