חסימת החנות

משתמשים רבים עדיין מנהלים את פרטי הכניסה שלהם כשהם מגדירים מכשיר Android חדש. התהליך הידני הזה יכול להיות מאתגר ובמקרים רבים פוגע בחוויית המשתמש. המטרה של Block Store API, ספרייה שמופעלת על ידי שירותי Google Play, היא לספק לאפליקציות דרך לשמור את פרטי הכניסה של משתמשים בלי המורכבות או סיכון האבטחה הכרוכים בשמירת סיסמאות של משתמשים.

ה-API של Block Store מאפשר לאפליקציה לאחסן נתונים שהיא יכולה לאחזר מאוחר יותר, כדי לאמת מחדש את המשתמשים במכשיר חדש. כך אפשר לספק למשתמשים חוויה חלקה יותר, כי הם לא צריכים לראות מסך כניסה כשמפעילים את האפליקציה בפעם הראשונה במכשיר החדש.

בין היתרונות של שימוש ב-Block Store:

  • פתרון לאחסון פרטי כניסה מוצפנים למפתחים. פרטי הכניסה מוצפנים מקצה לקצה כשהדבר אפשרי.
  • שומרים אסימונים במקום שמות משתמש וסיסמאות.
  • הפחיתו את הקשיים בתהליך הכניסה.
  • לחסוך למשתמשים את הצורך לנהל סיסמאות מורכבות.
  • Google מאמתת את זהות המשתמש.

לפני שמתחילים

כדי להכין את האפליקציה, יש לבצע את השלבים המפורטים בקטעים הבאים.

הגדרת האפליקציה

בקובץ build.gradle ברמת הפרויקט, צריך לכלול את מאגר Maven של Google בקטע buildscript וגם בקטע allprojects:

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

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

מוסיפים את התלות של Google Play Services של Block Store API לקובץ ה-build של המודול, בדר"כ app/build.gradle:

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

איך זה עובד

חסימת החנות מאפשרת למפתחים לשמור ולשחזר מערכים של עד 16 בייטים. כך תהיה לך אפשרות לשמור מידע חשוב לגבי הסשן הנוכחי של המשתמש, וגם את הגמישות לשמור את המידע הזה כרצונך. הנתונים האלה יכולים להצפין מקצה לקצה והתשתית שתומכת ב-block Store מבוססת על התשתית של הגיבוי והשחזור.

מדריך זה יעסוק בתרחיש לדוגמה של שמירת אסימון של משתמש כדי לחסום את החנות. בשלבים הבאים מתואר איך תפעל אפליקציה שמשתמשת ב-block Store:

  1. במהלך תהליך האימות של האפליקציה או בכל שלב לאחר מכן, תוכלו לשמור את אסימון האימות של המשתמש ב-block Store כדי שתוכלו לאחזר אותו מאוחר יותר.
  2. האסימון יאוחסן באופן מקומי ואפשר גם לגבות אותו בענן בצורה מוצפנת מקצה לקצה, אם אפשר.
  3. הנתונים מועברים כשהמשתמש מתחיל תהליך שחזור במכשיר חדש.
  4. אם המשתמש ישחזר את האפליקציה במהלך השחזור, האפליקציה תוכל לאחזר את האסימון שנשמר מ-Block Store במכשיר החדש.

שמירת האסימון

כשמשתמש נכנס לאפליקציה שלכם, אתם יכולים לשמור ב-block Store את אסימון האימות שאתם יוצרים לאותו משתמש. אפשר לאחסן את האסימון הזה באמצעות ערך ייחודי של זוג מפתחות, שכולל עד 4kb לכל רשומה. כדי לאחסן את האסימון, צריך להפעיל את 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 Services מאמתת את המשתמש ואחר כך מאחזרת את נתוני ה-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 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)
  }

מאחזר את כל האסימונים.

הדוגמה הבאה מראה איך לאחזר את כל האסימונים שנשמרו ב-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)
  }

הדוגמה הבאה מראה איך לאחזר את מפתח ברירת המחדל.

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()
client.deleteBytes(deleteAllRequest)
  .addOnSuccessListener { result: Boolean ->
    Log.d(TAG,
          "Any data found and deleted? $result")
  }

הצפנה מקצה לקצה

כדי שההצפנה מקצה לקצה תהיה זמינה, מערכת ההפעלה במכשיר צריכה להיות Android 9 ואילך, והמשתמש צריך להגדיר נעילת מסך (קוד אימות, קו ביטול נעילה או סיסמה) למכשיר. כדי לבדוק אם ההצפנה זמינה במכשיר, אפשר להתקשר למספר isEndToEndEncryptionAvailable().

הדוגמה הבאה ממחישה איך לוודא שההצפנה תהיה זמינה במהלך הגיבוי בענן:

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

הפעלת גיבוי בענן

כדי להפעיל גיבוי בענן, צריך להוסיף את השיטה setShouldBackupToCloud() לאובייקט StoreBytesData. כש-setShouldBackupToCloud() מוגדרת כ-True, יתבצעו גיבויים תקופתיים של הבייטים ששמורים ב-block Store בענן.

הדוגמה הבאה ממחישה איך מפעילים גיבוי בענן רק אם הגיבוי בענן מוצפן מקצה לקצה:

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. משלבים את BlockStore API באפליקציית הבדיקה.
  2. משתמשים באפליקציית הבדיקה כדי להפעיל את BlockStore API לשמירת הנתונים.
  3. מסירים את אפליקציית הבדיקה ומתקינים מחדש את האפליקציה באותו מכשיר.
  4. משתמשים באפליקציית הבדיקה כדי להפעיל את BlockStore API לאחזור הנתונים.
  5. חשוב לוודא שהבייטים שאוחזרו זהים לנתונים שאוחסנו לפני ההסרה.

ממכשיר למכשיר

ברוב המקרים, יהיה צורך באיפוס של מכשיר היעד להגדרות המקוריות. לאחר מכן תוכלו להיכנס לתהליך של שחזור אלחוטי של Android או אל שחזור באמצעות כבל Google (למכשירים נתמכים).

שחזור בענן

  1. משלבים את Blockstore API באפליקציית הבדיקה. צריך לשלוח את אפליקציית הבדיקה לחנות Play.
  2. במכשיר המקור, משתמשים באפליקציית הבדיקה כדי להפעיל את Blockstore API כדי לאחסן את הנתונים, כש- shouldBackUpToCloud מוגדר כ-true.
  3. במכשירים O ואילך, אפשר להפעיל ידנית את הגיבוי בענן ב-Block Store: נכנסים להגדרות > Google > גיבוי, ולוחצים על הלחצן 'גיבוי עכשיו'.
    1. כדי לוודא שהגיבוי בענן של Block Store בוצע בהצלחה, אתם יכולים:
      1. כשהגיבוי מסתיים, מחפשים שורות ביומן עם התג CloudSyncBpTkSvc.
      2. אמורות להופיע שורות כמו: “......, CloudSyncBpTkSvc: sync result: SUCCESS, ..., upload size: XXX bytes ...”
    2. אחרי הגיבוי בענן של Block Store, יש פרק זמן של 5 דקות 'שלווה'. ב-5 הדקות האלה, לחיצה על הלחצן 'Backup Now' לא תגרום להפעלה של גיבוי נוסף בענן של Block Store.
  4. מאפסים את מכשיר היעד להגדרות המקוריות ומבצעים תהליך שחזור בענן. בוחרים אם לשחזר את אפליקציית הבדיקה במהלך תהליך השחזור. מידע נוסף על תהליכי השחזור בענן זמין במאמר תהליכי שחזור בענן נתמכים.
  5. במכשיר היעד, משתמשים באפליקציית הבדיקה כדי להפעיל את Blockstore API לאחזור הנתונים.
  6. חשוב לוודא שהבייטים שאוחזרו זהים לנתונים שאוחסנו במכשיר המקור.

דרישות למכשיר

הצפנה מקצה לקצה

  • ההצפנה מקצה לקצה נתמכת במכשירים עם Android מגרסה 9 (API 29) ואילך.
  • כדי שההצפנה מקצה לקצה תופעל, נתוני המשתמש יהיו מוצפנים וצריך להגדיר במכשיר נעילת מסך עם קוד אימות, קו ביטול נעילה או סיסמה.

תהליך שחזור ממכשיר למכשיר

כדי לשחזר נתונים ממכשיר למכשיר, נדרשים מכשיר מקור ומכשיר יעד. אלו יהיו שני המכשירים שמעבירים נתונים.

כדי לגבות מכשירי מקור, מערכת ההפעלה שלהם צריכה להיות Android 6 (API 23) ואילך.

לטרגט מכשירים עם Android 9 (API 29) ומעלה כדי שתהיה להם אפשרות לבצע שחזור.

כאן ניתן למצוא מידע נוסף על תהליך השחזור מהמכשיר למכשיר.

תהליך גיבוי ושחזור בענן

כדי לבצע גיבוי ושחזור בענן, נדרשים מכשיר מקור ומכשיר יעד.

כדי לגבות מכשירי מקור, מערכת ההפעלה שלהם צריכה להיות Android 6 (API 23) ואילך.

מכשירי טירגוט נתמכים על סמך הספקים שלהם. מכשירי Pixel יכולים להשתמש בתכונה הזו מ-Android 9 (API 29) ובכל שאר המכשירים חייבים לפעול עם Android 12 (API 31) ואילך.