حظر متجر

لا يزال العديد من المستخدمين يديرون بيانات الاعتماد الخاصة بهم عند إعداد جهاز جديد يعمل بنظام التشغيل Android. ويمكن أن تصبح هذه العملية اليدوية صعبة وغالبًا ما تؤدي إلى تجربة سيئة للمستخدم. تهدف Block Store API، وهي مكتبة تستند إلى خدمات Google Play، إلى حلّ هذه المشكلة من خلال توفير طريقة للتطبيقات لحفظ بيانات اعتماد المستخدمين بدون التعقيد أو المخاطر الأمنية المرتبطة بحفظ كلمات مرور المستخدمين.

تسمح Block Store API لتطبيقك بتخزين البيانات التي يمكنه استردادها لاحقًا لإعادة مصادقة المستخدمين على جهاز جديد. يساعد ذلك في توفير تجربة أكثر سلاسة للمستخدم، لأنّه لن يحتاج إلى رؤية شاشة تسجيل الدخول عند إطلاق تطبيقك لأول مرة على الجهاز الجديد.

تشمل مزايا استخدام Block Store ما يلي:

  • حلّ مشفَّر لوحدة تخزين بيانات الاعتماد للمطوّرين تخضع بيانات الاعتماد للتشفير التام بين الأطراف متى أمكن ذلك.
  • حفظ الرموز المميّزة بدلاً من أسماء المستخدمين وكلمات المرور
  • إزالة المشاكل من عمليات تسجيل الدخول
  • توفير عناء إدارة كلمات المرور المعقّدة على المستخدمين
  • تتحقّق Google من هوية المستخدم.

قبل البدء

لإعداد تطبيقك، أكمل الخطوات الواردة في الأقسام التالية.

إعداد تطبيقك

في ملف build.gradle على مستوى المشروع، أدرِج مستودع Maven من Google في كلٍّ من قسمَي buildscript و allprojects

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

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

أضِف تبعيات خدمات Google Play لـ Block Store API إلى ملف Gradle الخاص بالوحدة، والذي يكون عادةً app/build.gradle:

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

آلية العمل

تسمح Block Store للمطوّرين بحفظ ما يصل إلى 16 صفيفًا من البايتات واستعادتها. يتيح لك ذلك حفظ المعلومات المهمة المتعلقة بجلسة المستخدم الحالية ويوفر لك المرونة لحفظ هذه المعلومات بالطريقة التي تريدها. يمكن أن تخضع هذه البيانات للتشفير التام بين الأطراف، وتم إنشاء البنية الأساسية التي تستخدم Block Store استنادًا إلى البنية الأساسية للاحتفاظ بنسخة احتياطية واستعادة البيانات.

سيشمل هذا الدليل حالة استخدام حفظ الرمز المميّز للمستخدم في Block Store. توضّح الخطوات التالية كيفية عمل تطبيق يستخدم Block Store:

  1. أثناء عملية المصادقة في تطبيقك أو في أي وقت بعد ذلك، يمكنك تخزين الرمز المميّز لمصادقة المستخدم في Block Store لاسترداده لاحقًا.
  2. سيتم تخزين الرمز المميّز محليًا ويمكن أيضًا الاحتفاظ بنسخة احتياطية منه في السحابة الإلكترونية، مع تشفيره تمامًا بين الأطراف متى أمكن ذلك.
  3. يتم نقل البيانات عندما يبدأ المستخدم عملية استعادة البيانات على جهاز جديد.
  4. إذا استعاد المستخدم تطبيقك أثناء عملية الاستعادة، يمكن لتطبيقك بعد ذلك استرداد الرمز المميّز المحفوظ من Block Store على الجهاز الجديد.

حفظ الرمز المميّز

عندما يسجّل المستخدم الدخول إلى تطبيقك، يمكنك حفظ الرمز المميّز للمصادقة الذي تنشئه لهذا المستخدم في Block Store. يمكنك تخزين هذا الرمز المميّز باستخدام قيمة فريدة لمفتاح الزوج، والتي يبلغ الحد الأقصى لها 4 كيلوبايت لكل إدخال. لتخزين الرمز المميّز، استخدِم setBytes() و setKey() في مثيل من StoreBytesData.Builder لتخزين بيانات اعتماد المستخدم على الجهاز المصدر. بعد حفظ الرمز المميّز باستخدام Block Store، يتم تشفيره وتخزينه محليًا على الجهاز.

يوضّح المثال التالي كيفية حفظ الرمز المميّز للمصادقة على الجهاز المحلي:

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

استخدام الرمز المميّز التلقائي

تستخدم البيانات التي يتم حفظها باستخدام StoreBytes بدون مفتاح المفتاح التلقائي 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)
    }

استرداد الرمز المميّز

في وقت لاحق، عندما يكمل المستخدم عملية الاستعادة على جهاز جديد، تتحقّق خدمات Google Play أولاً من المستخدم، ثم تسترد بيانات Block Store. لقد وافق المستخدم مسبقًا على استعادة بيانات تطبيقك كجزء من عملية الاستعادة، لذا لا يلزم الحصول على أي موافقات إضافية. عندما يفتح المستخدم تطبيقك، يمكنك طلب الرمز المميّز من Block Store باستخدام retrieveBytes(). يمكن بعد ذلك استخدام الرمز المميّز الذي تم استرداده لإبقاء المستخدم مسجّلاً الدخول على الجهاز الجديد.

يوضّح المثال التالي كيفية استرداد رموز مميّزة متعددة استنادًا إلى مفاتيح معيّنة.

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

استرداد جميع الرموز المميّزة

في ما يلي مثال على كيفية استرداد جميع الرموز المميّزة المحفوظة في 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)
  }

في ما يلي مثال على كيفية استرداد المفتاح التلقائي.

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)

حذف الرموز المميّزة

قد يكون حذف الرموز المميّزة من BlockStore مطلوبًا للأسباب التالية:

  • إكمال المستخدم عملية تسجيل الخروج
  • تم إبطال الرمز المميّز أو أنّه غير صالح.

على غرار استرداد الرموز المميّزة، يمكنك تحديد الرموز المميّزة التي يجب حذفها من خلال ضبط مجموعة من المفاتيح التي تتطلب الحذف.

يوضّح المثال التالي كيفية حذف مفاتيح معيّنة:

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)

حذف جميع الرموز المميّزة

يوضّح المثال التالي كيفية حذف جميع الرموز المميّزة المحفوظة حاليًا في 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")
        }

تفعيل النسخ الاحتياطي عبر السحابة الإلكترونية

لتفعيل النسخ الاحتياطي عبر السحابة الإلكترونية، أضِف الطريقة setShouldBackupToCloud() إلى عنصر StoreBytesData. سيحتفظ Block Store بشكل دوري بنسخة احتياطية في السحابة الإلكترونية من البايتات المخزَّنة عندما يتم ضبط setShouldBackupToCloud() على "صحيح".

يوضّح المثال التالي كيفية تفعيل النسخ الاحتياطي عبر السحابة الإلكترونية فقط عندما يكون النسخ الاحتياطي عبر السحابة الإلكترونية مشفّرًا تمامًا بين الأطراف:

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

كيفية الاختبار

استخدِم الطرق التالية أثناء التطوير لاختبار عمليات الاستعادة.

إلغاء تثبيت التطبيق وإعادة تثبيته على الجهاز نفسه

إذا فعّل المستخدم خدمات "الاحتفاظ بنسخة احتياطية" (يمكن التحقّق من ذلك في الإعدادات > Google > الاحتفاظ بنسخة احتياطية)، فستظل بيانات Block Store محفوظة بعد إلغاء تثبيت التطبيق وإعادة تثبيته.

يمكنك اتّباع الخطوات التالية للاختبار:

  1. ادمِج Block Store API في تطبيقك التجريبي.
  2. استخدِم التطبيق التجريبي لاستدعاء Block Store API لتخزين بياناتك.
  3. ألغِ تثبيت تطبيقك التجريبي ثم أعِد تثبيت تطبيقك على الجهاز نفسه.
  4. استخدِم التطبيق التجريبي لاستدعاء Block Store API لاسترداد بياناتك.
  5. تأكَّد من أنّ البايتات التي تم استردادها هي نفسها التي تم تخزينها قبل إلغاء التثبيت.

من جهاز إلى جهاز

في معظم الحالات، سيتطلب ذلك إعادة الضبط على الإعدادات الأصلية لجهاز الاختبار. يمكنك بعد ذلك إكمال عملية الاستعادة اللاسلكية على Android أو عملية الاستعادة باستخدام كابل Google (للأجهزة المتوافقة).

الاستعادة من السحابة الإلكترونية

  1. ادمِج Block Store API في تطبيقك التجريبي. يجب إرسال التطبيق التجريبي إلى "متجر Play".
  2. على الجهاز المصدر، استخدِم التطبيق التجريبي لاستدعاء Block Store API لتخزين بياناتك، مع ضبط shouldBackUpToCloud على true.
  3. بالنسبة إلى الأجهزة التي تعمل بنظام التشغيل Android O والإصدارات الأحدث، يمكنك بدء عملية الاحتفاظ بنسخة احتياطية في السحابة الإلكترونية من Block Store يدويًا: انتقِل إلى الإعدادات > Google > الاحتفاظ بنسخة احتياطية، وانقر على الزر "الاحتفاظ بنسخة احتياطية الآن".
    1. للتأكّد من نجاح عملية الاحتفاظ بنسخة احتياطية في السحابة الإلكترونية من Block Store، يمكنك:
      1. بعد اكتمال عملية الاحتفاظ بنسخة احتياطية، ابحث عن أسطر السجلّ التي تحمل العلامة "CloudSyncBpTkSvc".
      2. يجب أن تظهر لك أسطر مثل: "......, CloudSyncBpTkSvc: sync result: SUCCESS, ..., uploaded size: XXX bytes ..."
    2. بعد الاحتفاظ بنسخة احتياطية في السحابة الإلكترونية من Block Store، هناك فترة "توقف مؤقت" مدتها 5 دقائق. خلال هذه الدقائق الخمس، لن يؤدي النقر على الزر "الاحتفاظ بنسخة احتياطية الآن" إلى بدء عملية أخرى للاحتفاظ بنسخة احتياطية في السحابة الإلكترونية من Block Store.
  4. أعِد ضبط الجهاز المستهدَف على الإعدادات الأصلية وأكمِل عملية الاستعادة من السحابة الإلكترونية. اختَر استعادة تطبيقك التجريبي أثناء عملية الاستعادة. لمزيد من المعلومات عن عمليات الاستعادة من السحابة الإلكترونية، يُرجى الاطّلاع على عمليات الاستعادة من السحابة الإلكترونية المتوافقة.
  5. على جهاز الاختبار، استخدِم التطبيق التجريبي لاستدعاء Block Store API لاسترداد بياناتك.
  6. تأكَّد من أنّ البايتات التي تم استردادها هي نفسها التي تم تخزينها على الجهاز المصدر.

متطلبات الجهاز

التشفير التام بين الأطراف

  • يتوافق التشفير التام بين الأطراف مع الأجهزة التي تعمل بنظام التشغيل Android 9 (المستوى 29 من واجهة برمجة التطبيقات) والإصدارات الأحدث.
  • يجب ضبط قفل شاشة الجهاز باستخدام رقم تعريف شخصي أو نقش أو كلمة مرور لتفعيل التشفير التام بين الأطراف وتشفير بيانات المستخدم بشكل صحيح.

عملية الاستعادة من جهاز إلى جهاز

تتطلب عملية الاستعادة من جهاز إلى جهاز توفّر جهاز مصدر وجهاز مستهدَف. سيكون هذان الجهازان هما الجهازان اللذان ينقلان البيانات.

يجب أن تعمل أجهزة المصدر بنظام التشغيل Android 6 (المستوى 23 من واجهة برمجة التطبيقات) والإصدارات الأحدث للاحتفاظ بنسخة احتياطية.

يجب أن تعمل أجهزة المستهدَف بنظام التشغيل Android 9 (المستوى 29 من واجهة برمجة التطبيقات) والإصدارات الأحدث لتتمكّن من الاستعادة.

يمكنك الاطّلاع على مزيد من المعلومات عن عملية الاستعادة من جهاز إلى جهاز هنا.

عملية الاحتفاظ بنسخة احتياطية والاستعادة من السحابة الإلكترونية

تتطلب عملية الاحتفاظ بنسخة احتياطية والاستعادة من السحابة الإلكترونية توفّر جهاز مصدر وجهاز اختبار.

يجب أن تعمل أجهزة المصدر بنظام التشغيل Android 6 (المستوى 23 من واجهة برمجة التطبيقات) والإصدارات الأحدث للاحتفاظ بنسخة احتياطية.

تتوافق أجهزة المستهدَف استنادًا إلى مورّديها. يمكن لأجهزة Pixel استخدام هذه الميزة من Android 9 (المستوى 29 من واجهة برمجة التطبيقات)، ويجب أن تعمل جميع الأجهزة الأخرى بنظام التشغيل Android 12 (المستوى 31 من واجهة برمجة التطبيقات) أو إصدار أحدث.