บล็อกร้านค้า

ผู้ใช้หลายคนยังคงจัดการข้อมูลรับรองของตนเองขณะตั้งค่าอุปกรณ์ 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.2.0'
}

ลักษณะการทำงาน

Block Store ช่วยให้นักพัฒนาซอฟต์แวร์บันทึกและคืนค่าอาร์เรย์สูงสุด 16 ไบต์ วิธีนี้จะช่วยให้คุณบันทึกข้อมูลสําคัญเกี่ยวกับเซสชันผู้ใช้ปัจจุบัน และมีความยืดหยุ่นในการบันทึกข้อมูลนี้ตามที่คุณต้องการ ข้อมูลนี้สามารถเข้ารหัสจากต้นทางถึงปลายทางได้ และโครงสร้างพื้นฐานที่สนับสนุน Block Store สร้างขึ้นบนโครงสร้างพื้นฐานของการสํารองและคืนค่าข้อมูล

คู่มือนี้จะครอบคลุมกรณีการใช้งานเกี่ยวกับการบันทึกโทเค็นของผู้ใช้ลงใน Block Store ขั้นตอนต่อไปนี้สรุปวิธีการทํางานของแอปที่ใช้ Block Store

  1. ระหว่างขั้นตอนการตรวจสอบสิทธิ์แอป หรือในภายหลัง คุณสามารถจัดเก็บโทเค็นการตรวจสอบสิทธิ์ของผู้ใช้ไปยัง Block Store เพื่อดึงข้อมูลในภายหลังได้
  2. ระบบจะจัดเก็บโทเค็นไว้ในเครื่องและสามารถสํารองข้อมูลไปยังระบบคลาวด์และเข้ารหัสแบบจุดต่อจุดได้ถ้าเป็นไปได้
  3. ระบบจะโอนข้อมูลเมื่อผู้ใช้เริ่มกระบวนการคืนค่าในอุปกรณ์ใหม่
  4. หากผู้ใช้คืนค่าแอปของคุณระหว่างขั้นตอนการกู้คืน แอปจะดึงข้อมูลโทเค็นที่บันทึกไว้จากบล็อกสโตร์บนอุปกรณ์ใหม่ได้

กําลังบันทึกโทเค็น

เมื่อผู้ใช้ลงชื่อเข้าใช้แอป คุณสามารถบันทึกโทเค็นการตรวจสอบสิทธิ์ที่คุณสร้างขึ้นสําหรับผู้ใช้รายนั้นไปยัง Block Store คุณจัดเก็บโทเค็นนี้โดยใช้ค่าคู่คีย์ที่ไม่ซ้ํากันซึ่งมีขนาดได้สูงสุด 4KB ต่อรายการ หากต้องการจัดเก็บโทเค็น ให้เรียกใช้ setBytes() และ 'setKey(/android/reference/com/google/android/gms/auth/blockstore/StoreBytesData.Builder.html#setKey(java.lang.String)' ในอินสแตนซ์ของ StoreBytesData.Builder เพื่อจัดเก็บข้อมูลเข้าสู่ระบบของผู้ใช้ไปยังอุปกรณ์ต้นทาง หลังจากบันทึกโทเค็นด้วย บล็อกสโตร์แล้ว โทเค็นจะถูกเข้ารหัสและจัดเก็บไว้ในเครื่อง

ตัวอย่างต่อไปนี้แสดงวิธีบันทึกโทเค็นการตรวจสอบสิทธิ์ลงในอุปกรณ์ในระบบ

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 จะยืนยันผู้ใช้ก่อน แล้วจึงเรียกข้อมูลการบล็อกใน 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 ขึ้นไปและผู้ใช้ต้องตั้งค่าการล็อกหน้าจอ (PIN, รูปแบบ หรือรหัสผ่าน) ของอุปกรณ์เพื่อให้การเข้ารหัสแบบจุดต่อจุดพร้อมใช้งาน คุณยืนยันได้ว่าจะมีการเข้ารหัสในอุปกรณ์หรือไม่โดยโทรไปที่ isEndToEndEncryptionAvailable()

ตัวอย่างต่อไปนี้แสดงวิธีตรวจสอบว่าการเข้ารหัสจะพร้อมใช้งานระหว่างการสํารองข้อมูลในระบบคลาวด์หรือไม่

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 > การสํารองข้อมูล) ข้อมูลบล็อกสโตร์จะถูกถอนการติดตั้ง/ติดตั้งแอปอีกครั้ง

คุณสามารถทําตามขั้นตอนต่อไปนี้เพื่อทดสอบ

  1. ผสานรวม BlockStore API ลงในแอปทดสอบ
  2. ใช้แอปทดสอบเพื่อเรียก BlockStore API เพื่อเก็บข้อมูล
  3. ถอนการติดตั้งแอปทดสอบแล้วติดตั้งแอปในอุปกรณ์เดิมอีกครั้ง
  4. ใช้แอปทดสอบเพื่อเรียก BlockStore API เพื่อเรียกข้อมูล
  5. ตรวจสอบว่าจํานวนไบต์ที่ดึงมาตรงกับที่เก็บไว้ในก่อนถอนการติดตั้ง

อุปกรณ์ต่ออุปกรณ์

ในกรณีส่วนใหญ่ จะต้องมีการรีเซ็ตอุปกรณ์ปลายทางเป็นค่าเริ่มต้น คุณสามารถป้อน ขั้นตอนการคืนค่า Android แบบไร้สาย หรือคืนค่าสาย Google (สําหรับอุปกรณ์ที่สนับสนุน)

การกู้คืนระบบคลาวด์

  1. ผสานรวม Blockstore API ลงในแอปทดสอบ แอปทดสอบจําเป็นต้องส่งไปยัง Play Store
  2. ในอุปกรณ์ต้นทาง ให้ใช้แอปทดสอบเพื่อเรียก API ของ Blockstore เพื่อเก็บข้อมูล โดยตั้งค่า ShoBackUpCloud เป็น "จริง"
  3. สําหรับอุปกรณ์ O และอุปกรณ์ข้างต้น คุณสามารถทริกเกอร์การสํารองข้อมูลในระบบคลาวด์ของ Store Store ได้ด้วยตนเอง โดยไปที่การตั้งค่า > Google > การสํารองข้อมูล คลิกปุ่ม “สํารองข้อมูลเลย”
    1. หากต้องการยืนยันว่าการสํารองข้อมูลระบบคลาวด์ของ Store Store ดําเนินการสําเร็จแล้ว ให้ทําดังนี้
      1. หลังจากสํารองข้อมูลเรียบร้อยแล้ว ให้ค้นหาบรรทัดของบันทึกที่มีแท็ก “CloudSyncBpTkSvc”
      2. คุณควรเห็นบรรทัดลักษณะต่อไปนี้ “......, CloudSyncBpTkSvc: sync result: SUCCESS, ..., uploaded size: XXX bytes ...”
    2. หลังจากสํารองข้อมูลในระบบคลาวด์ของ Store Store แล้ว ระบบจะ “พัก” เป็นเวลา 5 นาที ภายในเวลา 5 นาที การคลิกปุ่ม “สํารองข้อมูลทันที” จะไม่ทําให้เกิด การสํารองข้อมูลในระบบคลาวด์อีกใน Store Store
  4. รีเซ็ตอุปกรณ์เป้าหมายเป็นค่าเริ่มต้นและทําตามขั้นตอนการกู้คืนระบบคลาวด์ เลือกเพื่อคืนค่าแอปทดสอบระหว่างขั้นตอนการกู้คืน โปรดดูข้อมูลเพิ่มเติมเกี่ยวกับขั้นตอนการกู้คืนในคลาวด์ที่ขั้นตอนการกู้คืนในระบบคลาวด์ที่รองรับ
  5. ในอุปกรณ์เป้าหมาย ให้ใช้แอปทดสอบเพื่อเรียก Blockstore API เพื่อเรียกข้อมูล
  6. ยืนยันว่าไบต์ที่ดึงมานั้นเหมือนกับที่เก็บไว้ในอุปกรณ์ต้นทาง

ข้อกําหนดของอุปกรณ์

การเข้ารหัสจากต้นทางถึงปลายทาง

  • การเข้ารหัสแบบจุดต่อจุดได้รับการสนับสนุนในอุปกรณ์ที่ใช้ Android 9 (API 29) ขึ้นไป
  • อุปกรณ์ต้องล็อกหน้าจอด้วย PIN, รูปแบบ หรือรหัสผ่านเพื่อให้มีการเข้ารหัสจากต้นทางถึงปลายทางและเข้ารหัสข้อมูลของผู้ใช้ได้อย่างถูกต้อง

ขั้นตอนการกู้คืนระหว่างอุปกรณ์

ในการคืนค่าอุปกรณ์ คุณจะต้องมีอุปกรณ์ต้นทางและอุปกรณ์เป้าหมาย ซึ่งก็คืออุปกรณ์ 2 เครื่องที่กําลังโอนข้อมูล

อุปกรณ์ต้นทางต้องใช้ Android 6 (API 23) ขึ้นไปเพื่อสํารองข้อมูล

กําหนดเป้าหมายอุปกรณ์ที่ใช้ Android 9 (API 29) ขึ้นไปเพื่อให้กู้คืนได้

คุณสามารถดูข้อมูลเพิ่มเติมเกี่ยวกับขั้นตอนการคืนค่าอุปกรณ์ได้ที่นี่

ขั้นตอนการสํารองและกู้คืนข้อมูลในระบบคลาวด์

การสํารองและกู้คืนข้อมูลในระบบคลาวด์จะต้องใช้อุปกรณ์ต้นทางและอุปกรณ์เป้าหมาย

อุปกรณ์ต้นทางต้องใช้ Android 6 (API 23) ขึ้นไปเพื่อสํารองข้อมูล

ระบบรองรับอุปกรณ์เป้าหมายตามผู้ให้บริการ อุปกรณ์ Pixel ใช้ฟีเจอร์นี้ได้จาก Android 9 (API 29) และอุปกรณ์อื่นๆ ทั้งหมดต้องใช้ Android 12 (API 31) ขึ้นไป