Blok Mağazası

Birçok kullanıcı yeni bir Android cihaz kurarken kendi kimlik bilgilerini yönetmeye devam eder. Bu manuel işlem zorlayıcı olabilir ve genellikle kötü bir kullanıcı deneyimine neden olur. Google Play hizmetleri tarafından desteklenen bir kitaplık olan Block Store API'nin, uygulamalara kullanıcı şifrelerinin kaydedilmesiyle ilişkili karmaşıklık veya güvenlik riski olmadan kullanıcı kimlik bilgilerini kaydetmeleri için bir yol sunarak bu sorunu gidermeyi amaçlıyoruz.

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

Block Store'u kullanmanın avantajları arasında şunlar yer alır:

  • Geliştiriciler için şifrelenmiş kimlik bilgisi depolama çözümü. Mümkün olduğunda kimlik bilgileri 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 verilen adımları tamamlayın.

Uygulamanızı yapılandırma

Proje düzeyindeki build.gradle dosyanızdaki Google Maven deposunu hem buildscript hem de allprojects bölümlerine ekleyin:

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

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

Block Store API'si 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 olur:

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

İşleyiş şekli

Blok Deposu, geliştiricilerin 16 bayta kadar dizileri kaydetmelerine ve geri yüklemelerine olanak tanır. Böylece geçerli kullanıcı oturumuyla ilgili önemli bilgileri kaydedebilir ve bu bilgileri istediğiniz gibi kaydedebilirsiniz. Bu veriler uçtan uca şifrelenebilir ve Block Store'u destekleyen altyapı, Yedekleme ve Geri Yükleme altyapısını temel alır.

Bu kılavuzda, bir kullanıcının jetonunun Google Play Store'a kaydedilmesiyle ilgili kullanım alanı ele alınmaktadır. Aşağıdaki adımlarda, Block Store'u kullanan bir uygulamanın nasıl çalışacağı özetlenmiştir:

  1. Uygulamanızın kimlik doğrulama akışı sırasında veya daha sonra herhangi bir zamanda, kullanıcının kimlik doğrulama jetonunu daha sonra almak üzere Blok Mağazası'nda saklayabilirsiniz.
  2. Jeton yerel olarak depolanır ve mümkün olduğunda uçtan uca şifrelenerek 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, yeni cihazdaki Block Store'dan kaydedilen jetonu alabilir.

Jetonu kaydetme

Bir kullanıcı uygulamanızda oturum açtığında, söz konusu kullanıcı için oluşturduğunuz kimlik doğrulama jetonunu Play Store'u Engelle'ye kaydedebilirsiniz. Bu jetonu, giriş başına maksimum 4 KB'lık benzersiz bir anahtar çifti değeri kullanarak depolayabilirsiniz. Jetonu depolamak ve kullanıcının kimlik bilgilerini kaynak cihazda depolamak için StoreBytesData.Builder örneğinde setBytes() ve setKey() çağrılarını yapın. Jetonu Block Store ile kaydetmenizin ardından 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

Anahtar olmadan StoreBytes kullanılarak kaydedilen veriler, varsayılan BlockstoreClient.DEFAULT_BYTES_DATA_KEY anahtarını kullanır.

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, bir kullanıcı yeni bir cihazda geri yükleme akışından geçtiğinde Google Play Hizmetleri önce kullanıcıyı doğrular, ardından Engelleme Deposu verilerinizi alır. Kullanıcı, geri yükleme akışının bir parçası olarak uygulama verilerinizi geri yüklemeyi zaten kabul etmiştir. Bu nedenle, ek izin gerekmez. Kullanıcı uygulamanızı açtığında retrieveBytes() numaralı telefonu arayarak Block Store'dan jetonunuzu isteyebilirsiniz. Alınan jeton daha sonra, kullanıcının yeni cihazda oturumunun açık kalması 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ı almayla 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ı silme

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

  • Kullanıcı, oturumu kapatma işlemini tamamlıyor.
  • Jeton iptal edilmiş veya geçersiz.

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

Aşağıda, belirli anahtarları silmeyle ilgili ö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'da 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ünü ç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ılıp kullanılamadığını isEndToEndEncryptionAvailable() numaralı telefonu arayarak doğrulayabilirsiniz.

Aşağıdaki örnekte, bulutta yedekleme sırasında şifrelemenin kullanılıp kullanılamayacağı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 yedeklemesini etkinleştirmek için StoreBytesData nesnesine setShouldBackupToCloud() yöntemini ekleyin. Block Store, setShouldBackupToCloud() doğru olarak ayarlandığında depolanan baytları düzenli aralıklarla buluta yedekler.

Aşağıdaki örnekte, yalnızca bulut yedekleme uçtan uca şifrelendiğinde bulut yedeklemenin 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ı cihazda kaldırma/yeniden yükleme

Kullanıcı, Yedekleme hizmetlerini etkinleştirirse (Ayarlar > Google > Yedekleme bölümünden kontrol edilebilir) Engellenen Mağaza verileri, uygulamanın kaldırılması/yeniden yüklenmesi boyunca korunur.

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 için test uygulamasını kullanın.
  3. Test uygulamanızı kaldırıp aynı cihazda yeniden yükleyin.
  4. Verilerinizi almak amacıyla 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 bu işlem, hedef cihazın fabrika ayarlarına sıfırlanmasını gerektirir. Ardından Android kablosuz geri yükleme akışı veya Google kablo geri yükleme (desteklenen cihazlar için) bölümüne girebilirsiniz.

Buluta 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, verilerinizi depolamak amacıyla Blockstore API'yi çağırmak için test uygulamasını kullanın. shouldBackUpToCloud değeri "true" olarak ayarlanmış olmalıdır.
  3. O ve üstü cihazlarda, bir Block Store bulut yedeklemesini manuel olarak tetikleyebilirsiniz: Ayarlar > Google > Yedekleme'ye gidin ve "Şimdi Yedekle" düğmesini tıklayın.
    1. Block Store 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. Şöyle satırlar görürsünüz: “......, CloudSyncBpTkSvc: sync result: SUCCESS, ..., uploaded size: XXX bytes ...” (S.
    2. Block Store bulut yedeklemesinden sonra 5 dakikalık bir "bekleme" süresi vardır. Bu 5 dakika içinde, "Şimdi Yedekle" düğmesi tıklandığında başka bir Block Store bulut yedeklemesi tetiklenmez.
  4. Hedef cihazı fabrika ayarlarına sıfırlayın ve bulut geri yükleme akışını uygulayın. Geri yükleme akışı sırasında test uygulamanızı geri yüklemek için bu seçeneği belirleyin. Buluta geri yükleme akışları hakkında daha fazla bilgi için Desteklenen bulut geri yükleme akışları bölümüne bakın.
  5. Verilerinizi almak amacıyla Blockstore API'yi çağırmak için hedef cihazda 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ının verilerinin doğru bir şekilde şifrelenmesi için cihazda PIN, desen veya şifreyle ayarlanmış bir ekran kilidi olmalıdır.

Cihazdan Cihaza Geri Yükleme Akışı

Cihazdan cihaza geri yükleme işlemi için bir kaynak cihaz ve hedef cihaz sahibi olmanız gerekir. Bunlar veri aktaran iki cihaz olacaktır.

Kaynak cihazlar, yedekleme için Android 6 (API 23) ve sonraki sürümleri çalıştırıyor olmalıdır.

Android 9 (API 29) ve sonraki sürümleri çalıştıran cihazları geri yükleyebilmek için hedefleyin.

Cihazdan cihaza geri yükleme akışıyla ilgili daha fazla bilgiyi burada bulabilirsiniz.

Bulut Yedekleme ve Geri Yükleme Akışı

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

Kaynak cihazlar, yedekleme için Android 6 (API 23) ve sonraki sürümleri çalıştırıyor olmalıdır.

Hedef cihazlar, satıcılarına göre desteklenir. Pixel cihazlar bu özelliği Android 9 (API 29) üzerinden kullanabilir ve diğer tüm cihazlar Android 12 (API 31) veya sonraki bir sürümü çalıştırmalıdır.