Blok Mağazası

Birçok kullanıcı, yeni bir Android cihaz kurarken kendi kimlik bilgilerini yönetmeye devam ediyor. Bu manuel süreç zorlu olabilir ve genellikle kötü bir kullanıcı deneyimine yol açar. Google Play Hizmetleri tarafından desteklenen bir kitaplık olan Block Store API, uygulamaların kullanıcı şifrelerini kaydetmeyle ilişkili karmaşıklık veya güvenlik riski olmadan kullanıcı kimlik bilgilerini kaydetmesine olanak tanıyarak bu sorunu çözmeyi amaçlar.

Block Store API, uygulamanızın daha sonra yeni bir cihazda kullanıcıların kimliğini yeniden doğrulamak için alabileceği verileri depolamasına olanak tanır. Bu sayede, uygulamanızı yeni cihazda ilk kez başlattığında oturum açma ekranını görmesi gerekmeyeceğinden kullanıcıya daha sorunsuz bir deneyim sunulur.

Block Store'u kullanmanın avantajları şunlardır:

  • Geliştiriciler için şifrelenmiş kimlik bilgileri depolama çözümü. Kimlik bilgileri mümkün olduğunda uçtan uca şifrelenir.
  • Kullanıcı adları ve şifreler yerine jetonları kaydedin.
  • Oturum açma akışlarındaki sorunları ortadan kaldırın.
  • Kullanıcıları karmaşık şifreleri yönetme zahmetinden kurtarın.
  • Google, kullanıcının kimliğini doğrular.

Başlamadan önce

Uygulamanızı hazırlamak için aşağıdaki bölümlerde yer alan adımları tamamlayın.

Uygulamanızı yapılandırma

Proje düzeyindeki build.gradle dosyanızda, Google'ın Maven deposunu hem buildscript hem de allprojects bölümünüze ekleyin:

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

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

Block Store API için Google Play Hizmetleri bağımlılığını modülünüzün Gradle derleme dosyasına (genellikle app/build.gradle) ekleyin:

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

İşleyiş şekli

Block Store, geliştiricilerin 16 baytlık dizileri kaydetmesine ve geri yüklemesine olanak tanır. Bu sayede, mevcut kullanıcı oturumuyla ilgili önemli bilgileri kaydedebilir ve bu bilgileri dilediğiniz şekilde kaydetme esnekliğinden yararlanabilirsiniz. Bu veriler uçtan uca şifrelenebilir ve Block Store'u destekleyen altyapı, Yedekleme ve Geri Yükleme altyapısı üzerine kuruludur.

Bu kılavuzda, kullanıcının jetonunu Block Store'a kaydetme kullanım alanı ele alınmaktadır. Aşağıdaki adımlarda, Block Store'u kullanan bir uygulamanın nasıl çalışacağı açıklanmaktadır:

  1. Uygulamanızın kimlik doğrulama akışı sırasında veya daha sonra istediğiniz zaman kullanıcının kimlik doğrulama jetonunu Block Store'da saklayarak daha sonra alabilirsiniz.
  2. Jeton yerel olarak depolanır ve mümkün olduğunda uçtan uca şifrelenerek buluta da yedeklenebilir.
  3. Kullanıcı yeni bir cihazda geri yükleme akışı başlattığında veriler aktarılır.
  4. Kullanıcı, geri yükleme akışı sırasında uygulamanızı geri yüklerse uygulamanız, kaydedilen jetonu yeni cihazdaki Block Store'dan alabilir.

Jetonu kaydetme

Bir kullanıcı uygulamanızda oturum açtığında, o kullanıcı için oluşturduğunuz kimlik doğrulama jetonunu Block Store'a kaydedebilirsiniz. Bu jetonu, giriş başına maksimum 4 KB olan benzersiz bir anahtar çifti kullanarak depolayabilirsiniz. Jetonu depolamak için bir StoreBytesData.Builder örneğinde setBytes() ve setKey() yöntemlerini çağırarak kullanıcının kimlik bilgilerini kaynak cihaza kaydedin. Jetonu Block Store'a kaydettikten sonra jeton şifrelenir ve cihazda yerel olarak depolanır.

Aşağıdaki örnekte, kimlik doğrulama jetonunun yerel cihaza nasıl kaydedileceği gösterilmektedir:

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

Varsayılan jetonu kullan

StoreBytes kullanılarak anahtar olmadan kaydedilen veriler varsayılan anahtarı kullanır 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)
  }

Jetonu alma

Daha sonra, kullanıcı yeni bir cihazda geri yükleme akışını uyguladığında Google Play Hizmetleri önce kullanıcının kimliğini doğrular, ardından Block Store verilerinizi alır. Kullanıcı, geri yükleme akışının bir parçası olarak uygulama verilerinizi geri yüklemeyi zaten kabul ettiğinden ek izin gerekmez. Kullanıcı uygulamanızı açtığında retrieveBytes() yöntemini çağırarak Block Store'dan jetonunuzu isteyebilirsiniz. Alınan jeton, kullanıcının yeni cihazda oturumunun açık kalmasını sağlamak için kullanılabilir.

Aşağıdaki örnekte, belirli anahtarlara göre birden fazla jetonun nasıl alınacağı gösterilmektedir.

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

Tüm jetonlar alınıyor.

Aşağıdaki örnekte, BlockStore'a kaydedilen tüm jetonların nasıl alınacağı gösterilmektedir.

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

Aşağıdaki örnekte, varsayılan anahtarın nasıl alınacağı gösterilmektedir.

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)

Jetonları silme

Aşağıdaki nedenlerle BlockStore'dan jetonların silinmesi gerekebilir:

  • Kullanıcı, oturumu kapatma kullanıcı işlemleri akışını tamamlar.
  • Jeton iptal edildi veya geçersiz.

Jetonları almaya benzer şekilde, silinmesi gereken anahtarlardan oluşan bir dizi ayarlayarak hangi jetonların silinmesi gerektiğini belirtebilirsiniz.

Aşağıdaki örnekte belirli anahtarların nasıl silineceği gösterilmektedir:

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<String> 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)

Tüm Jetonları Sil

Aşağıdaki örnekte, Block Store'a kaydedilen tüm jetonların nasıl silineceği gösterilmektedir:

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

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

Uçtan uca şifreleme

Uçtan uca şifrelemenin kullanılabilmesi için cihazda Android 9 veya sonraki bir sürümün yüklü olması ve kullanıcının cihazı için bir ekran kilidi (PIN, desen veya şifre) ayarlamış olması gerekir. isEndToEndEncryptionAvailable() yöntemini çağırarak cihazda şifrelemenin kullanılıp kullanılamayacağını doğrulayabilirsiniz.

Aşağıdaki örnekte, bulut yedekleme sırasında şifrelemenin kullanılabilir olup olmayacağının nasıl doğrulanacağı gösterilmektedir:

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

Bulut yedeklemesini etkinleştirme

Bulut yedeklemeyi etkinleştirmek için StoreBytesData nesnenize setShouldBackupToCloud() yöntemini ekleyin. Block Store, setShouldBackupToCloud() değeri doğru olarak ayarlandığında depolanan baytları düzenli olarak buluta yedekler.

Aşağıdaki örnekte, bulut yedeklemenin yalnızca uçtan uca şifrelendiğinde nasıl etkinleştirileceği gösterilmektedir:

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

Test etme

Geri yükleme akışlarını test etmek için geliştirme sırasında aşağıdaki yöntemleri kullanın.

Aynı cihazda kaldırma ve yeniden yükleme

Kullanıcı, yedekleme hizmetlerini etkinleştirirse (Ayarlar > Google > Yedekleme bölümünden kontrol edilebilir) Block Store verileri, uygulama kaldırılıp yeniden yüklendiğinde de korunur.

Test etmek için aşağıdaki adımları uygulayabilirsiniz:

  1. Block Store API'yi test uygulamanıza entegre edin.
  2. Verilerinizi depolamak için Block Store API'yi çağırmak üzere test uygulamasını kullanın.
  3. Test uygulamanızı kaldırın ve ardından uygulamanızı aynı cihaza yeniden yükleyin.
  4. Verilerinizi almak için Block Store API'yi çağırmak üzere test uygulamasını kullanın.
  5. Alınan baytların, kaldırma işleminden önce depolanan baytlarla aynı olduğunu doğrulayın.

Cihazdan cihaza

Çoğu durumda, bunun için hedef cihazın fabrika ayarlarına sıfırlanması gerekir. Ardından Android kablosuz geri yükleme akışına veya Google kablolu geri yükleme (desteklenen cihazlar için) akışına girebilirsiniz.

Buluttan geri yükleme

  1. Block Store API'yi test uygulamanıza entegre edin. Test uygulamasının Play Store'a gönderilmesi gerekir.
  2. Kaynak cihazda, test uygulamasını kullanarak Block Store API'yi çağırın ve shouldBackUpToCloud, true olarak ayarlanmış şekilde verilerinizi depolayın.
  3. Android 8.0 (API düzeyi 26) veya sonraki sürümleri çalıştıran cihazlarda Block Store bulut yedeklemesini manuel olarak tetikleyebilirsiniz: Ayarlar > Google > Yedekle'ye gidip "Şimdi Yedekle" düğmesini tıklayın.
    1. Block Store bulut yedeklemesinin başarılı olduğunu doğrulamak için:
      1. Yedekleme tamamlandıktan sonra [CloudSyncBackupTaskService] etiketini içeren günlük satırlarını arayın.
      2. Şuna benzer satırlar görürsünüz: ..., [CloudSyncBackupTaskService] sync result: SUCCESS, ..., uploaded size: [size] bytes ...
    2. Block Store bulut yedeklemesinden sonra 5 dakikalık bir "bekleme" süresi vardır. Bu 5 dakika içinde "Şimdi Yedekle" düğmesini tıklamak başka bir Block Store bulut yedeklemesini tetiklemez.
  4. Hedef cihazı fabrika ayarlarına sıfırlayın ve buluttan geri yükleme akışını uygulayın. Geri yükleme akışı sırasında test uygulamanızı geri yüklemek için seçin. Buluttan geri yükleme akışları hakkında daha fazla bilgi için Desteklenen buluttan geri yükleme akışları bölümüne bakın.
  5. Hedef cihazda, verilerinizi almak için Block Store API'yi çağırmak üzere test uygulamasını kullanın.
  6. Alınan baytların, kaynak cihazda depolanan baytlarla aynı olduğunu doğrulayın.

Cihaz Gereksinimleri

Block Store işlemlerine katılan cihazlar için aşağıdaki şartları inceleyin.

Uçtan uca şifreleme

  • Uçtan uca şifreleme, Android 9 (API 29) veya sonraki sürümleri çalıştıran cihazlarda desteklenir.
  • Uçtan uca şifrelemenin etkinleştirilmesi ve kullanıcının verilerini doğru şekilde şifrelemesi için cihazda PIN, desen veya şifre ile ayarlanmış bir ekran kilidi olmalıdır.

Cihazdan Cihaza Geri Yükleme Akışı

Cihazdan cihaza geri yükleme için bir kaynak cihaz ve bir hedef cihaz gerekir. Bunlar, veri aktarımı yapan iki cihazdır.

Yedekleme için kaynak cihazlarda Android 6 (API 23) veya sonraki sürümler yüklü olmalıdır.

Geri yükleme özelliğini kullanabilmek için Android 9 (API 29) veya sonraki sürümleri çalıştıran cihazları hedefleyin.

Cihazdan cihaza geri yükleme akışı hakkında daha fazla bilgi için Desteklenen cihazdan cihaza geri yükleme akışları başlıklı makaleyi inceleyin.

Bulut Yedekleme ve Geri Yükleme Akışı

Bulutta yedekleme ve geri yükleme için bir kaynak cihaz ve bir hedef cihaz gerekir.

Yedekleme için kaynak cihazlarda Android 6 (API 23) veya sonraki sürümler yüklü olmalıdır.

Hedef cihazlar, tedarikçilerine göre desteklenir. Pixel cihazlar bu özelliği Android 9 (API 29) sürümünden itibaren kullanabilir. Diğer tüm cihazlarda Android 12 (API 31) veya sonraki bir sürüm yüklü olmalıdır.