Blok Mağazası

Birçok kullanıcı, yeni bir Android cihaz kurarken kendi kimlik bilgilerini yönetmeye devam eder. Bu manuel işlem zor 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ı şifrelerinin kaydedilmesiyle ilişkili karmaşıklık veya güvenlik riski olmadan kullanıcı kimlik bilgilerini kaydetmesine olanak tanıyarak bu sorunu çözmek için çalışır.

Block Store API, uygulamanızın daha sonra yeni bir cihazda kullanıcıların kimliklerini yeniden doğrulamak için alabileceği verileri depolamasına olanak tanır. Bu, uygulamanızı yeni cihazda ilk kez başlatırken kullanıcıların oturum açma ekranı görmeleri gerekmediğinden kullanıcılara daha sorunsuz bir deneyim sunulmasına yardımcı olur.

Blok Mağaza'yı kullanmanın avantajları şunlardır:

  • Geliştiriciler için şifrelenmiş kimlik bilgisi 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 zorlukları 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ıza, hem buildscript hem de allprojects bölümlerinize Google'ın Maven deposunu 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 ekleyin. Bu dosya genellikle app/build.gradle olan:

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

İşleyiş şekli

Blok Mağazası, geliştiricilerin 16 bayta kadar diziyi kaydetmesine ve geri yüklemesine olanak tanır. Bu, geçerli kullanıcı oturumuyla ilgili önemli bilgileri kaydetmenize olanak tanır ve bu bilgileri istediğiniz gibi kaydetme esnekliği sunar. Bu veriler uçtan uca şifrelenebilir. Blok Mağazası'nı destekleyen altyapı, Yedekleme ve Geri Yükleme altyapısının üzerine kurulur.

Bu kılavuzda, bir kullanıcının jetonunun Blok Mağaza'ya kaydedilmesiyle ilgili kullanım alanı açıklanmaktadır. Aşağıdaki adımlarda, Block Store'u kullanan bir uygulamanın işleyiş şekli açıklanmaktadır:

  1. Uygulamanızın kimlik doğrulama akışı sırasında veya sonrasında istediğiniz zaman, kullanıcının kimlik doğrulama jetonunu daha sonra almak üzere Blok Mağaza'da depolayabilirsiniz.
  2. Jeton yerel olarak depolanır ve mümkün olduğunda uçtan uca şifrelenmiş şekilde buluta 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, kayıtlı jetonu yeni cihazdaki Blok Mağazası'ndan alabilir.

Jetonu kaydetme

Bir kullanıcı uygulamanızda oturum açtığında, bu kullanıcı için oluşturduğunuz kimlik doğrulama jetonunu Blok Mağazası'na kaydedebilirsiniz. Bu jetonu, giriş başına maksimum 4 KB olan benzersiz bir anahtar çifti değeri kullanarak depolayabilirsiniz. Jetonu depolamak için StoreBytesData.Builder örneğinde setBytes() ve setKey() çağrılarını yaparak kullanıcının kimlik bilgilerini kaynak cihazda depolayın. Jeton, Block Store ile kaydedildikten sonra ş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

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

Jeton alınıyor

Daha sonra yeni bir cihazda geri yükleme akışından geçen bir kullanıcı, Google Play Hizmetleri'nde ilk olarak kullanıcıyı doğrular, ardından Block Store verilerinizi alır. Kullanıcı, geri yükleme akışı kapsamında uygulama verilerinizi geri yüklemeyi zaten kabul etmiş. Dolayısıyla ek izin gerekmez. Kullanıcı uygulamanızı açtığında, retrieveBytes() numaralı telefonu arayarak Blok Store'dan jetonunuzu isteyebilirsiniz. Alınan jeton daha sonra kullanıcının yeni cihazda oturumunu açık tutmak 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 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)
  }

Tüm jetonlar alınıyor.

Aşağıda, BlockStore'a kaydedilen tüm jetonların nasıl alınacağına dair bir örnek verilmiştir.

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

Aşağıda, varsayılan anahtarın nasıl alınacağıyla ilgili bir örnek verilmiştir.

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 siliniyor

Jetonların BlockStore'dan silinmesi aşağıdaki nedenlerden dolayı gerekebilir:

  • Kullanıcı, kullanıcı oturumunu kapatma işleminden geçiyor.
  • Jeton iptal edilmiş veya geçersiz.

Jetonları almaya benzer şekilde, silme gerektiren bir anahtar dizisi ayarlayarak hangi jetonların silinmesi gerektiğini belirtebilirsiniz.

Aşağıda, belirli anahtarları silmeyle ilgili bir örnek verilmiştir.

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)

Tüm Jetonları Sil

Aşağıdaki örnek, şu anda BlockStore'a kayıtlı olan tüm jetonları siler:

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

Uçtan uca şifreleme

Uçtan uca şifrelemenin kullanılabilmesi için cihazın Android 9 veya sonraki bir sürümü çalıştırıyor olması ve kullanıcının cihazında bir ekran kilidi (PIN, desen veya şifre) ayarlamış olması gerekir. Cihazda şifrelemenin kullanılabilir olup olmadığını isEndToEndEncryptionAvailable() numaralı telefonu arayarak doğrulayabilirsiniz.

Aşağıdaki örnekte, bulut yedeklemesi sırasında şifrelemenin kullanılabilir olup olmadığını nasıl doğrulayacağınız gösterilmektedir:

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

Bulut yedeklemeyi etkinleştir

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

Aşağıdaki örnekte, bulut yedeklemenin yalnızca bulut yedekleme 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.")
          }
        }

Nasıl test edilir?

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

Aynı cihazı kaldırma/yeniden yükleme

Kullanıcı, Yedekleme hizmetlerini etkinleştirirse (Ayarlar > Google > Yedekleme'den kontrol edilebilir) Blok Mağazası verileri, uygulamanın yüklemesini kaldırma/yeniden yükleme işlemi boyunca kalıcı olur.

Test etmek için şu adımları uygulayabilirsiniz:

  1. BlockStore API'yi test uygulamanıza entegre edin.
  2. Verilerinizi depolamak için BlockStore API'yi çağırmak amacıyla test uygulamasını kullanın.
  3. Test uygulamanızı kaldırıp aynı cihaza yeniden yükleyin.
  4. Verilerinizi almak üzere BlockStore API'yi çağırmak için test uygulamasını kullanın.
  5. Alınan baytların, yüklemeyi kaldırma işleminden önce depolananlarla 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'ye (desteklenen cihazlarda) girebilirsiniz.

Bulutta geri yükleme

  1. Blockstore API'yi test uygulamanıza entegre edin. Test uygulamasının Play Store'a gönderilmesi gerekir.
  2. Kaynak cihazda test uygulamasını kullanarak verilerinizi depolamak için Blockstore API'yi çağırın ve shouldBackUpToCloud'u true (doğru) olarak ayarlayın.
  3. O ve üstü cihazlarda, Block Store bulut yedeklemesini manuel olarak tetikleyebilirsiniz: Ayarlar > Google > Yedekleme'ye gidin ve "Şimdi Yedekle" düğmesini tıklayın.
    1. Blok Mağazası bulut yedeklemesinin başarılı olduğunu doğrulamak için şunları yapabilirsiniz:
      1. Yedekleme tamamlandıktan sonra "CloudSyncBpTkSvc" etiketine sahip günlük satırlarını arayın.
      2. Şuna benzer satırlar göreceksiniz: "......, CloudSyncBpTkSvc: senkronizasyon sonucu: BAŞARILI, ..., yüklenen boyut: XXX bayt ..."
    2. Blok Mağazası bulut yedekleme işleminden sonra 5 dakikalık bir "soğuma" süresi olur. Bu 5 dakika içinde "Şimdi Yedekle" düğmesi tıklandığında başka bir Blok Mağazası bulut yedeklemesi tetiklenmez.
  4. Hedef cihazı fabrika ayarlarına sıfırlayın ve bulut geri yükleme akışından geçin. Geri yükleme akışı sırasında test uygulamanızı geri yüklemek için bu seçeneği belirleyin. Bulut geri yükleme akışları hakkında daha fazla bilgi edinmek için Desteklenen bulut geri yükleme akışları başlıklı makaleyi inceleyin.
  5. Hedef cihazda, verilerinizi almak amacıyla Blockstore API'yi çağırmak için test uygulamasını kullanın.
  6. Alınan baytların, kaynak cihazda depolananlarla aynı olduğunu doğrulayın.

Cihaz Gereksinimleri

Uçtan Uca Şifreleme

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

Cihazdan Cihaza Geri Yükleme Akışı

Cihazlardan cihaza geri yükleme için bir kaynak ve hedef cihaz kullanmanız gerekir. Bunlar, veri aktaran iki cihaz olacaktır.

Yedekleme için kaynak cihazlarda Android 6 (API 23) veya sonraki sürümlerin yüklü olması gerekir.

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

Cihazdan cihaza geri yükleme akışı hakkında daha fazla bilgiyi burada bulabilirsiniz.

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ümlerin yüklü olması gerekir.

Hedef cihazlar, tedarikçilerine göre desteklenir. Pixel cihazlar, Android 9'dan (API 29) bu özelliği kullanabilir. Diğer tüm cihazların Android 12 (API 31) veya sonraki sürümleri çalıştırması gerekir.