חסימת החנות

משתמשים רבים עדיין מנהלים את פרטי הכניסה שלהם בעצמם כשהם מגדירים מכשיר 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 של Gradle של המודול, שעולה בדרך כלל app/build.gradle:

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

איך זה עובד

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

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

אפשר לבצע את השלבים הבאים כדי לבדוק:

  1. שילוב ה-blockStore API באפליקציית הבדיקה.
  2. משתמשים באפליקציית הבדיקה כדי להפעיל את ממשק ה-API של BlockStore לאחסון הנתונים.
  3. מסירים את אפליקציית הבדיקה ומתקינים מחדש את האפליקציה באותו המכשיר.
  4. משתמשים באפליקציית הבדיקה כדי להפעיל את ממשק ה-API של BlockStore לאחזור הנתונים.
  5. מוודאים שהבייטים שאוחזרו זהים למה שאוחסנו לפני ההסרה.

בין מכשירים

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

שחזור בענן

  1. שלב את ממשק ה-API של בלוקstore באפליקציית הבדיקה. צריך לשלוח את אפליקציית הבדיקה לחנות Play.
  2. במכשיר המקור, השתמשו באפליקציית הבדיקה כדי להפעיל את ה-API של בלוקstore לשמירת הנתונים, כשערך ההגדרה shouldBackUpToCloud הוא TRUE.
  3. במכשירים עם מערכת O ואילך, אפשר להפעיל באופן ידני גיבוי בענן ב-block Store: עוברים להגדרות > Google > גיבוי, ולוחצים על הלחצן 'גיבוי עכשיו'.
    1. כדי לוודא שהגיבוי בענן של Block Store הסתיים, אתם יכולים:
      1. כשהגיבוי מסתיים, מחפשים את שורות היומן עם התג CloudSyncBpTkSvc.
      2. אמורות להופיע שורות כמו: “......, CloudSyncBpTkSvc: sync result: הצלחה, ..., גודל שהועלה: XXX בייטים ...”
    2. אחרי הגיבוי בענן של Block Store, יש פרק זמן של 'קירור' של 5 דקות. תוך 5 דקות, לחיצה על הלחצן 'Backup Now' לא תפעיל גיבוי נוסף בענן של Block Store.
  4. יש לאפס את מכשיר היעד להגדרות המקוריות ולבצע תהליך שחזור בענן. בוחרים אם לשחזר את אפליקציית הבדיקה במהלך השחזור. למידע נוסף על תהליכי השחזור בענן, ראו תהליכים נתמכים לשחזור בענן.
  5. במכשיר היעד, משתמשים באפליקציית הבדיקה כדי להפעיל את ה-API של Blockstore לאחזור הנתונים.
  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) ומעלה.