การตัดสินด้านความสมบูรณ์

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

รูปแบบการตัดสินความสมบูรณ์ที่แสดงผล

โดยเพย์โหลดจะเป็น JSON ที่เป็นข้อความธรรมดาและมีสัญญาณความสมบูรณ์ควบคู่ไปกับข้อมูลที่นักพัฒนาแอประบุ

โครงสร้างโดยทั่วไปของเพย์โหลดมีดังนี้

{
  requestDetails: { ... }
  appIntegrity: { ... }
  deviceIntegrity: { ... }
  accountDetails: { ... }
  environmentDetails: { ... }
}

ก่อนอื่น คุณต้องตรวจสอบว่าค่าในช่อง requestDetails ตรงกับค่าของคำขอต้นฉบับก่อนตรวจสอบผลการตรวจสอบความสมบูรณ์แต่ละรายการ ส่วนต่อไปนี้จะอธิบายแต่ละช่องอย่างละเอียด

ช่องขอรายละเอียด

ช่อง requestDetails มีข้อมูลเกี่ยวกับคำขอ รวมถึงข้อมูลที่ได้รับจากนักพัฒนาแอปใน requestHash สำหรับคำขอมาตรฐาน และ nonce สำหรับคำขอแบบคลาสสิก

สําหรับคําขอ API มาตรฐาน

requestDetails: {
  // Application package name this attestation was requested for.
  // Note that this field might be spoofed in the middle of the request.
  requestPackageName: "com.package.name"
  // Request hash provided by the developer.
  requestHash: "aGVsbG8gd29scmQgdGhlcmU"
  // The timestamp in milliseconds when the integrity token
  // was requested.
  timestampMillis: "1675655009345"
}

ค่าเหล่านี้ควรตรงกับค่าของคำขอเดิม ดังนั้น ให้ยืนยันส่วน requestDetails ของเพย์โหลด JSON โดยตรวจสอบว่า requestPackageName และ requestHash ตรงกับที่ส่งในคำขอเดิม ดังที่แสดงในข้อมูลโค้ดต่อไปนี้

Kotlin

val requestDetails = JSONObject(payload).getJSONObject("requestDetails")
val requestPackageName = requestDetails.getString("requestPackageName")
val requestHash = requestDetails.getString("requestHash")
val timestampMillis = requestDetails.getLong("timestampMillis")
val currentTimestampMillis = ...

// Ensure the token is from your app.
if (!requestPackageName.equals(expectedPackageName)
        // Ensure the token is for this specific request
    || !requestHash.equals(expectedRequestHash)
        // Ensure the freshness of the token.
    || currentTimestampMillis - timestampMillis > ALLOWED_WINDOW_MILLIS) {
        // The token is invalid! See below for further checks.
        ...
}

Java

RequestDetails requestDetails =
    decodeIntegrityTokenResponse
    .getTokenPayloadExternal()
    .getRequestDetails();
String requestPackageName = requestDetails.getRequestPackageName();
String requestHash = requestDetails.getRequestHash();
long timestampMillis = requestDetails.getTimestampMillis();
long currentTimestampMillis = ...;

// Ensure the token is from your app.
if (!requestPackageName.equals(expectedPackageName)
        // Ensure the token is for this specific request.
    || !requestHash.equals(expectedRequestHash)
        // Ensure the freshness of the token.
    || currentTimestampMillis - timestampMillis > ALLOWED_WINDOW_MILLIS) {
        // The token is invalid! See below for further checks.
        ...
}

สำหรับคำขอ API แบบคลาสสิก

requestDetails: {
  // Application package name this attestation was requested for.
  // Note that this field might be spoofed in the middle of the
  // request.
  requestPackageName: "com.package.name"
  // base64-encoded URL-safe no-wrap nonce provided by the developer.
  nonce: "aGVsbG8gd29scmQgdGhlcmU"
  // The timestamp in milliseconds when the request was made
  // (computed on the server).
  timestampMillis: "1617893780"
}

ค่าเหล่านี้ควรตรงกับค่าของคำขอเดิม ดังนั้น ให้ยืนยันส่วน requestDetails ของเพย์โหลด JSON โดยตรวจสอบว่า requestPackageName และ nonce ตรงกับที่ส่งในคำขอเดิม ดังที่แสดงในข้อมูลโค้ดต่อไปนี้

Kotlin

val requestDetails = JSONObject(payload).getJSONObject("requestDetails")
val requestPackageName = requestDetails.getString("requestPackageName")
val nonce = requestDetails.getString("nonce")
val timestampMillis = requestDetails.getLong("timestampMillis")
val currentTimestampMillis = ...

// Ensure the token is from your app.
if (!requestPackageName.equals(expectedPackageName)
        // Ensure the token is for this specific request. See 'Generate a nonce'
        // section of the doc on how to store/compute the expected nonce.
    || !nonce.equals(expectedNonce)
        // Ensure the freshness of the token.
    || currentTimestampMillis - timestampMillis > ALLOWED_WINDOW_MILLIS) {
        // The token is invalid! See below for further checks.
        ...
}

Java

JSONObject requestDetails =
    new JSONObject(payload).getJSONObject("requestDetails");
String requestPackageName = requestDetails.getString("requestPackageName");
String nonce = requestDetails.getString("nonce");
long timestampMillis = requestDetails.getLong("timestampMillis");
long currentTimestampMillis = ...;

// Ensure the token is from your app.
if (!requestPackageName.equals(expectedPackageName)
        // Ensure the token is for this specific request. See 'Generate a nonce'
        // section of the doc on how to store/compute the expected nonce.
    || !nonce.equals(expectedNonce)
        // Ensure the freshness of the token.
    || currentTimestampMillis - timestampMillis > ALLOWED_WINDOW_MILLIS) {
        // The token is invalid! See below for further checks.
        ...
}

ช่องความสมบูรณ์ของแอปพลิเคชัน

ฟิลด์ appIntegrity มีข้อมูลเกี่ยวกับแพ็กเกจ

appIntegrity: {
  // PLAY_RECOGNIZED, UNRECOGNIZED_VERSION, or UNEVALUATED.
  appRecognitionVerdict: "PLAY_RECOGNIZED"
  // The package name of the app.
  // This field is populated iff appRecognitionVerdict != UNEVALUATED.
  packageName: "com.package.name"
  // The sha256 digest of app certificates (base64-encoded URL-safe).
  // This field is populated iff appRecognitionVerdict != UNEVALUATED.
  certificateSha256Digest: ["6a6a1474b5cbbb2b1aa57e0bc3"]
  // The version of the app.
  // This field is populated iff appRecognitionVerdict != UNEVALUATED.
  versionCode: "42"
}

appRecognitionVerdict อาจมีค่าดังต่อไปนี้

PLAY_RECOGNIZED
แอปและใบรับรองตรงกับเวอร์ชันที่เผยแพร่โดย Google Play
UNRECOGNIZED_VERSION
ใบรับรองหรือชื่อแพ็กเกจไม่ตรงกับบันทึกของ Google Play
UNEVALUATED
ระบบไม่ได้ประเมินความสมบูรณ์ของแอปพลิเคชัน ไม่เป็นไปตามข้อกําหนดที่จําเป็น เช่น อุปกรณ์ไม่น่าเชื่อถือพอ

ตรวจสอบว่าแอปที่คุณสร้างเป็นแอปที่สร้างโทเค็นดังกล่าว โดยการยืนยันว่าความสมบูรณ์ของแอปพลิเคชันเป็นไปตามที่คาดไว้ ดังที่แสดงในตัวอย่างโค้ดต่อไปนี้

Kotlin

val appIntegrity = JSONObject(payload).getJSONObject("appIntegrity")
val appRecognitionVerdict = appIntegrity.getString("appRecognitionVerdict")

if (appRecognitionVerdict == "PLAY_RECOGNIZED") {
    // Looks good!
}

Java

JSONObject appIntegrity =
    new JSONObject(payload).getJSONObject("appIntegrity");
String appRecognitionVerdict =
    appIntegrity.getString("appRecognitionVerdict");

if (appRecognitionVerdict.equals("PLAY_RECOGNIZED")) {
    // Looks good!
}

นอกจากนี้ คุณยังตรวจสอบชื่อแพ็กเกจแอป เวอร์ชันแอป และใบรับรองแอปด้วยตนเองได้

ช่องความสมบูรณ์ของอุปกรณ์

ช่อง deviceIntegrity อาจมีค่าเดียว deviceRecognitionVerdict ที่มีป้ายกำกับอย่างน้อย 1 รายการซึ่งแสดงถึงประสิทธิภาพของอุปกรณ์ในการบังคับใช้ความสมบูรณ์ของแอป หากอุปกรณ์ไม่ตรงตามเกณฑ์ของป้ายกำกับใดๆ ฟิลด์ deviceIntegrity จะว่างเปล่า

deviceIntegrity: {
  // "MEETS_DEVICE_INTEGRITY" is one of several possible values.
  deviceRecognitionVerdict: ["MEETS_DEVICE_INTEGRITY"]
}

โดยค่าเริ่มต้น deviceRecognitionVerdict อาจมีสิ่งต่อไปนี้

MEETS_DEVICE_INTEGRITY
แอปกำลังทำงานบนอุปกรณ์ที่ขับเคลื่อนโดย Android ที่มีบริการ Google Play อุปกรณ์ผ่านการตรวจสอบความสมบูรณ์ของระบบและเป็นไปตามข้อกําหนดความเข้ากันได้ของ Android
ว่างเปล่า (ค่าว่าง)
แอปกำลังทำงานในอุปกรณ์ที่มีสัญญาณการโจมตี (เช่น การฮุก API) หรือการบุกรุกระบบ (เช่น การรูท) หรือแอปไม่ทำงานในอุปกรณ์จริง (เช่น โปรแกรมจำลองที่ไม่ผ่านการตรวจสอบความน่าเชื่อถือของ Google Play)

ตรวจสอบว่า deviceRecognitionVerdict เป็นไปตามที่คาดไว้ ดังที่แสดงในตัวอย่างโค้ดต่อไปนี้ เพื่อให้มั่นใจว่าโทเค็นมาจากอุปกรณ์ที่เชื่อถือได้

Kotlin

val deviceIntegrity =
    JSONObject(payload).getJSONObject("deviceIntegrity")
val deviceRecognitionVerdict =
    if (deviceIntegrity.has("deviceRecognitionVerdict")) {
        deviceIntegrity.getJSONArray("deviceRecognitionVerdict").toString()
    } else {
        ""
    }

if (deviceRecognitionVerdict.contains("MEETS_DEVICE_INTEGRITY")) {
    // Looks good!
}

Java

JSONObject deviceIntegrity =
    new JSONObject(payload).getJSONObject("deviceIntegrity");
String deviceRecognitionVerdict =
    deviceIntegrity.has("deviceRecognitionVerdict")
    ? deviceIntegrity.getJSONArray("deviceRecognitionVerdict").toString()
    : "";

if (deviceRecognitionVerdict.contains("MEETS_DEVICE_INTEGRITY")) {
    // Looks good!
}

หากอุปกรณ์ทดสอบไม่เป็นไปตามความสมบูรณ์ของอุปกรณ์ ให้ตรวจสอบว่าได้ติดตั้ง ROM ของโรงงานแล้ว (เช่น โดยการรีเซ็ตอุปกรณ์) และบูตโหลดเดอร์ถูกล็อก นอกจากนี้ คุณยังสร้างการทดสอบ Play Integrity API ใน Play Console ได้ด้วย

ป้ายกำกับอุปกรณ์แบบมีเงื่อนไข

หากแอปของคุณเผยแพร่ใน Google Play Games สำหรับ PC deviceRecognitionVerdict อาจมีป้ายกำกับต่อไปนี้ด้วย

MEETS_VIRTUAL_INTEGRITY
แอปกำลังทำงานบนโปรแกรมจำลองที่ขับเคลื่อนโดย Android ที่มีบริการ Google Play โปรแกรมจำลองผ่านการตรวจสอบความสมบูรณ์ของระบบและเป็นไปตามข้อกำหนดหลักด้านความเข้ากันได้ของ Android

ข้อมูลอุปกรณ์ที่ไม่บังคับ

หากคุณเลือกใช้ป้ายกำกับเพิ่มเติมในการตัดสินความสมบูรณ์ deviceRecognitionVerdict จะมีป้ายกำกับเพิ่มเติมต่อไปนี้

MEETS_BASIC_INTEGRITY
แอปกำลังทำงานบนอุปกรณ์ที่ผ่านการตรวจสอบความสมบูรณ์ของระบบพื้นฐาน และสำหรับอุปกรณ์ Android 13 ขึ้นไป จะต้องมีเอกสารรับรองคีย์แพลตฟอร์ม Android อุปกรณ์อาจไม่เป็นไปตามข้อกําหนดความเข้ากันได้ของ Android และอาจไม่ได้รับอนุมัติให้เรียกใช้บริการ Google Play เช่น อุปกรณ์อาจใช้ Android เวอร์ชันที่ไม่รู้จัก อาจมี Bootloader ที่ปลดล็อกแล้ว บูตที่ไม่ได้รับการยืนยัน หรืออาจไม่ได้รับการรับรองจากผู้ผลิต
MEETS_STRONG_INTEGRITY
แอปกำลังทำงานบนอุปกรณ์ที่ใช้ระบบปฏิบัติการ Android ที่มีบริการ Google Play และมีการรับประกันความสมบูรณ์ของระบบที่เข้มงวด เช่น หลักฐานความสมบูรณ์ของการบูตที่สนับสนุนด้วยฮาร์ดแวร์ และสำหรับอุปกรณ์ Android 13 ขึ้นไป จะต้องมีอัปเดตความปลอดภัยในช่วงปีที่ผ่านมา อุปกรณ์ผ่านการตรวจสอบความสมบูรณ์ของระบบและเป็นไปตามข้อกําหนดความเข้ากันได้ของ Android

อุปกรณ์เครื่องเดียวจะแสดงป้ายกำกับอุปกรณ์หลายรายการในผลการตัดสินความสมบูรณ์ของอุปกรณ์ หากตรงตามเกณฑ์ของแต่ละป้ายกำกับ

กิจกรรมล่าสุดในอุปกรณ์

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

หากคุณเลือกใช้เพื่อรับ recentDeviceActivity ฟิลด์ deviceIntegrity จะมีค่า 2 ค่าดังนี้

deviceIntegrity: {
  deviceRecognitionVerdict: ["MEETS_DEVICE_INTEGRITY"]
  recentDeviceActivity: {
    // "LEVEL_2" is one of several possible values.
    deviceActivityLevel: "LEVEL_2"
  }
}

คําจํากัดความของ deviceActivityLevel จะแตกต่างกันไปตามโหมดและอาจมีค่าใดค่าหนึ่งต่อไปนี้

ระดับกิจกรรมล่าสุดในอุปกรณ์ คำขอโทเค็นความสมบูรณ์ของ API มาตรฐานในอุปกรณ์นี้ในชั่วโมงที่ผ่านมาต่อแอป คำขอโทเค็นความสมบูรณ์ของ API แบบคลาสสิกในอุปกรณ์นี้ในชั่วโมงที่ผ่านมาต่อแอป
LEVEL_1 (ต่ำสุด) ไม่เกิน 10 5 หรือน้อยกว่า
LEVEL_2 ระหว่าง 11 ถึง 25 ระหว่าง 6 ถึง 10
LEVEL_3 ระหว่าง 26 ถึง 50 ระหว่าง 11 ถึง 15
LEVEL_4 (สูงสุด) มากกว่า 50 มากกว่า 15
UNEVALUATED ระบบไม่ได้ประเมินกิจกรรมล่าสุดในอุปกรณ์ ซึ่งอาจเกิดขึ้นเนื่องจากเหตุผลต่อไปนี้
  • อุปกรณ์ไม่น่าเชื่อถือพอ
  • Google Play ไม่รู้จักเวอร์ชันของแอปที่ติดตั้งในอุปกรณ์
  • ปัญหาทางเทคนิคในอุปกรณ์

แอตทริบิวต์อุปกรณ์

นอกจากนี้ คุณยังเลือกใช้แอตทริบิวต์อุปกรณ์ได้ด้วย ซึ่งจะระบุเวอร์ชัน Android SDK ของระบบปฏิบัติการ Android ที่ทำงานในอุปกรณ์ ในอนาคต เราอาจขยายแอตทริบิวต์นี้ด้วยแอตทริบิวต์อุปกรณ์อื่นๆ

ค่าเวอร์ชัน SDK คือหมายเลขเวอร์ชัน Android SDK ที่กําหนดไว้ใน Build.VERSION_CODES ระบบจะไม่ประเมินเวอร์ชัน SDK หากไม่เป็นไปตามข้อกําหนดที่จําเป็น ในกรณีนี้ ฟิลด์ sdkVersion จะไม่มีการตั้งค่า ดังนั้นฟิลด์ deviceAttributes จะว่างเปล่า ปัญหานี้อาจเกิดขึ้นเนื่องจากสาเหตุต่อไปนี้

  • อุปกรณ์ไม่น่าเชื่อถือพอ
  • Google Play ไม่รู้จักเวอร์ชันของแอปที่ติดตั้งในอุปกรณ์
  • อุปกรณ์มีปัญหาทางเทคนิค

หากคุณเลือกใช้ deviceAttributes ฟิลด์ deviceIntegrity จะมีช่องเพิ่มเติมต่อไปนี้

deviceIntegrity: {
  deviceRecognitionVerdict: ["MEETS_DEVICE_INTEGRITY"]
  deviceAttributes: {
    // 33 is one possible value, which represents Android 13 (Tiramisu).
    sdkVersion: 33
  }
}

ในกรณีที่ไม่ได้ประเมินเวอร์ชัน SDK ระบบจะตั้งค่าช่อง deviceAttributes ดังนี้

deviceIntegrity: {
  deviceRecognitionVerdict: ["MEETS_DEVICE_INTEGRITY"]
  deviceAttributes: {}  // sdkVersion field is not set.
}

ช่องรายละเอียดบัญชี

ช่อง accountDetails มีค่าเดียวคือ appLicensingVerdict ซึ่งแสดงสถานะการอนุญาตให้ใช้สิทธิ Google Play ของแอปสำหรับบัญชีผู้ใช้ที่ลงชื่อเข้าใช้ในอุปกรณ์ หากบัญชีผู้ใช้มีใบอนุญาต Play สำหรับแอป แสดงว่าผู้ใช้ดาวน์โหลดหรือซื้อแอปจาก Google Play

accountDetails: {
  // This field can be LICENSED, UNLICENSED, or UNEVALUATED.
  appLicensingVerdict: "LICENSED"
}

appLicensingVerdict อาจมีค่าใดค่าหนึ่งต่อไปนี้

LICENSED
ผู้ใช้มีสิทธิ์ในการใช้แอป กล่าวคือ ผู้ใช้ได้ติดตั้งหรืออัปเดตแอปของคุณจาก Google Play ในอุปกรณ์
UNLICENSED
ผู้ใช้ไม่มีสิทธิ์ในการใช้แอป กรณีเช่นนี้เกิดขึ้นเมื่อผู้ใช้โหลดแอปของคุณจากแหล่งที่ไม่รู้จัก หรือไม่ได้ดาวน์โหลดแอปจาก Google Play เป็นต้น คุณสามารถแสดงกล่องโต้ตอบ GET_LICENSED แก่ผู้ใช้เพื่อแก้ไขปัญหานี้ได้
UNEVALUATED

ระบบไม่ได้ประเมินรายละเอียดการอนุญาตให้ใช้สิทธิ เนื่องจากไม่เป็นไปตามข้อกําหนดที่จําเป็น

ปัญหานี้อาจเกิดขึ้นได้จากหลายสาเหตุ เช่น

  • อุปกรณ์ไม่น่าเชื่อถือพอ
  • Google Play ไม่รู้จักเวอร์ชันของแอปที่ติดตั้งในอุปกรณ์
  • ผู้ใช้ไม่ได้ลงชื่อเข้าใช้ Google Play

หากต้องการตรวจสอบว่าผู้ใช้มีสิทธิ์ของแอปสำหรับแอปของคุณ ให้ยืนยันว่า appLicensingVerdict เป็นไปตามที่คาดไว้ ดังที่แสดงในข้อมูลโค้ดต่อไปนี้

Kotlin

val accountDetails = JSONObject(payload).getJSONObject("accountDetails")
val appLicensingVerdict = accountDetails.getString("appLicensingVerdict")

if (appLicensingVerdict == "LICENSED") {
    // Looks good!
}

Java

JSONObject accountDetails =
    new JSONObject(payload).getJSONObject("accountDetails");
String appLicensingVerdict = accountDetails.getString("appLicensingVerdict");

if (appLicensingVerdict.equals("LICENSED")) {
    // Looks good!
}

ฟิลด์รายละเอียดสภาพแวดล้อม

นอกจากนี้ คุณยังเลือกใช้สัญญาณเพิ่มเติมเกี่ยวกับสภาพแวดล้อมได้ด้วย ความเสี่ยงในการเข้าถึงแอปจะบอกให้แอปทราบว่ามีแอปอื่นๆ ทำงานอยู่ซึ่งอาจใช้เพื่อจับภาพหน้าจอ แสดงซ้อนทับแอป หรือควบคุมอุปกรณ์หรือไม่ ผลการตัดสินของ Play Protect จะบอกคุณว่า Google Play Protect เปิดใช้ในอุปกรณ์หรือไม่ และพบมัลแวร์ที่รู้จักหรือไม่

หากคุณเลือกรับผลการตัดสินความเสี่ยงการเข้าถึงแอปหรือผลการตัดสินของ Play Protect ใน Google Play Console การตอบกลับจาก API จะมีช่องenvironmentDetails ฟิลด์ environmentDetails อาจมีค่า 2 ค่า ได้แก่ appAccessRiskVerdict และ playProtectVerdict

คำตัดสินความเสี่ยงในการเข้าถึงแอป

เมื่อเปิดใช้แล้ว ช่อง environmentDetails ใน Play Integrity API payload จะมีผลการตัดสินความเสี่ยงในการเข้าถึงแอปใหม่

{
  requestDetails: { ... }
  appIntegrity: { ... }
  deviceIntegrity: { ... }
  accountDetails: { ... }
  environmentDetails: {
      appAccessRiskVerdict: {
          // This field contains one or more responses, for example the following.
          appsDetected: ["KNOWN_INSTALLED", "UNKNOWN_INSTALLED", "UNKNOWN_CAPTURING"]
      }
 }
}

หากมีการประเมินความเสี่ยงการเข้าถึงแอป appAccessRiskVerdict จะมีช่อง appsDetected ที่มีคำตอบอย่างน้อย 1 รายการ การตอบกลับเหล่านี้จะจัดอยู่ในกลุ่มใดกลุ่มหนึ่งต่อไปนี้ โดยขึ้นอยู่กับแหล่งที่มาของการติดตั้งแอปที่ตรวจพบ

  • แอป Play หรือแอประบบ: แอปที่ Google Play ติดตั้งหรือผู้ผลิตอุปกรณ์โหลดไว้ล่วงหน้าในพาร์ติชันระบบของอุปกรณ์ (ระบุด้วย FLAG_SYSTEM) การตอบกลับสําหรับแอปดังกล่าวจะมี KNOWN_ นำหน้า

  • แอปอื่นๆ: แอปที่ Google Play ไม่ได้ติดตั้ง โดยไม่รวมแอปที่ผู้ผลิตอุปกรณ์โหลดไว้ล่วงหน้าในพาร์ทิชันระบบ การตอบกลับสำหรับแอปดังกล่าวจะมี UNKNOWN_ นำหน้า

ระบบจะแสดงการตอบกลับต่อไปนี้

KNOWN_INSTALLED, UNKNOWN_INSTALLED
มีการติดตั้งแอปที่ตรงกับแหล่งที่มาของการติดตั้งที่เกี่ยวข้อง
KNOWN_CAPTURING, UNKNOWN_CAPTURING
มีแอปที่ทำงานอยู่ซึ่งเปิดใช้สิทธิ์ที่อาจใช้เพื่อดูหน้าจอขณะที่แอปของคุณทำงานอยู่ ซึ่งไม่รวมบริการการช่วยเหลือพิเศษที่ Google Play ทราบว่าทำงานอยู่ในอุปกรณ์
KNOWN_CONTROLLING, UNKNOWN_CONTROLLING
มีแอปที่ทำงานอยู่ซึ่งเปิดใช้สิทธิ์ที่อาจใช้เพื่อควบคุมอุปกรณ์และควบคุมอินพุตไปยังแอปของคุณได้โดยตรง และอาจใช้เพื่อบันทึกอินพุตและเอาต์พุตของแอป ทั้งนี้ไม่รวมบริการการช่วยเหลือพิเศษที่ผ่านการยืนยันซึ่ง Google Play ทราบว่าทำงานอยู่ในอุปกรณ์
KNOWN_OVERLAYS, UNKNOWN_OVERLAYS
มีแอปที่ทำงานอยู่ซึ่งเปิดใช้สิทธิ์ที่อาจใช้เพื่อแสดงการวางซ้อนบนแอปของคุณ โดยไม่รวมบริการการช่วยเหลือพิเศษที่ได้รับการยืนยันซึ่ง Google Play ทราบว่าทำงานอยู่ในอุปกรณ์
ว่างเปล่า (ค่าว่าง)

ระบบจะไม่ประเมินความเสี่ยงในการเข้าถึงแอปหากไม่เป็นไปตามข้อกําหนดที่จําเป็น ในกรณีนี้ ช่อง appAccessRiskVerdict จะว่างเปล่า ปัญหานี้อาจเกิดขึ้นได้จากหลายสาเหตุ เช่น

  • อุปกรณ์ไม่น่าเชื่อถือพอ
  • รูปแบบของอุปกรณ์ไม่ใช่โทรศัพท์ แท็บเล็ต หรือแบบพับได้
  • อุปกรณ์ไม่ได้ใช้ Android 6 (API ระดับ 23) ขึ้นไป
  • Google Play ไม่รู้จักเวอร์ชันของแอปที่ติดตั้งในอุปกรณ์
  • Google Play Store ในอุปกรณ์เป็นเวอร์ชันล้าสมัย
  • เกมเท่านั้น: บัญชีผู้ใช้ไม่มีใบอนุญาต Play สำหรับเกม
  • ใช้คําขอมาตรฐานกับพารามิเตอร์ verdictOptOut
  • มีการใช้คําขอมาตรฐานกับไลบรารี Play Integrity API เวอร์ชันที่ยังไม่รองรับความเสี่ยงในการเข้าถึงแอปสําหรับคําขอมาตรฐาน

ความเสี่ยงในการเข้าถึงแอปจะยกเว้นบริการการช่วยเหลือพิเศษที่ได้รับการยืนยันแล้วโดยอัตโนมัติ ซึ่งผ่านการตรวจสอบการช่วยเหลือพิเศษที่ปรับปรุงแล้วของ Google Play (ติดตั้งโดย App Store ใดก็ได้ในอุปกรณ์) "ยกเว้น" หมายความว่าบริการการช่วยเหลือพิเศษที่ยืนยันแล้วซึ่งทำงานอยู่ในอุปกรณ์จะไม่แสดงการตอบกลับการจับภาพ การควบคุม หรือการวางซ้อนในผลการตัดสินความเสี่ยงการเข้าถึงแอป หากต้องการขอรับการตรวจสอบการช่วยเหลือพิเศษที่เพิ่มประสิทธิภาพของ Google Play สำหรับแอปการช่วยเหลือพิเศษ ให้เผยแพร่แอปใน Google Play โดยตรวจสอบว่าแอปตั้งค่า Flag isAccessibilityTool เป็น "จริง" ในไฟล์ Manifest ของแอป หรือขอรับการตรวจสอบ

ตารางต่อไปนี้แสดงตัวอย่างคำตัดสินและความหมาย (ตารางนี้ไม่ได้แสดงผลลัพธ์ที่เป็นไปได้ทั้งหมด)

ตัวอย่างคำตัดสินความเสี่ยงในการเข้าถึงแอป การตีความ
appsDetected:
["KNOWN_INSTALLED"]
มีเพียงแอปที่ Google Play รู้จักหรือผู้ผลิตอุปกรณ์โหลดไว้ล่วงหน้าในพาร์ติชันระบบเท่านั้น
ไม่มีแอปใดทำงานอยู่ซึ่งจะส่งผลให้มีการตัดสินการจับภาพ การควบคุม หรือการวางซ้อน
appsDetected:
["KNOWN_INSTALLED",
"UNKNOWN_INSTALLED",
"UNKNOWN_CAPTURING"]
มีแอปที่ Google Play ติดตั้งไว้หรือผู้ผลิตอุปกรณ์โหลดไว้ล่วงหน้าในพาร์ติชันระบบ
มีแอปอื่นๆ ที่ทำงานอยู่และเปิดใช้สิทธิ์ที่อาจใช้เพื่อดูหน้าจอหรือจับภาพอินพุตและเอาต์พุตอื่นๆ
appsDetected:
["KNOWN_INSTALLED",
"KNOWN_CAPTURING",
"UNKNOWN_INSTALLED",
"UNKNOWN_CONTROLLING"]
มี Play หรือระบบที่ทำงานอยู่ซึ่งเปิดใช้สิทธิ์ที่อาจใช้เพื่อดูหน้าจอหรือจับภาพอินพุตและเอาต์พุตอื่นๆ
นอกจากนี้ยังมีแอปอื่นๆ ที่ทำงานอยู่ซึ่งเปิดใช้สิทธิ์ที่อาจใช้เพื่อควบคุมอุปกรณ์และควบคุมอินพุตไปยังแอปของคุณได้โดยตรง
appAccessRiskVerdict: {} ระบบไม่ได้ประเมินความเสี่ยงการเข้าถึงแอปเนื่องจากไม่เป็นไปตามข้อกําหนดที่จําเป็น เช่น อุปกรณ์ไม่น่าเชื่อถือพอ

คุณเลือกชุดค่าผสมของคำตัดสินที่ยอมรับได้เพื่อดำเนินการต่อและคำตัดสินที่ต้องการดำเนินการได้ ทั้งนี้ขึ้นอยู่กับระดับความเสี่ยง ตัวอย่างโค้ดต่อไปนี้แสดงการยืนยันว่าไม่มีแอปใดทำงานอยู่ซึ่งอาจจับภาพหน้าจอหรือควบคุมแอปของคุณ

Kotlin

val environmentDetails =
    JSONObject(payload).getJSONObject("environmentDetails")
val appAccessRiskVerdict =
    environmentDetails.getJSONObject("appAccessRiskVerdict")

if (appAccessRiskVerdict.has("appsDetected")) {
    val appsDetected = appAccessRiskVerdict.getJSONArray("appsDetected").toString()
    if (!appsDetected.contains("CAPTURING") && !appsDetected.contains("CONTROLLING")) {
        // Looks good!
    }
}

Java

JSONObject environmentDetails =
    new JSONObject(payload).getJSONObject("environmentDetails");
JSONObject appAccessRiskVerdict =
    environmentDetails.getJSONObject("appAccessRiskVerdict");

if (appAccessRiskVerdict.has("appsDetected")) {
    String appsDetected = appAccessRiskVerdict.getJSONArray("appsDetected").toString()
    if (!appsDetected.contains("CAPTURING") && !appsDetected.contains("CONTROLLING")) {
        // Looks good!
    }
}
แก้ไขคำตัดสินความเสี่ยงในการเข้าถึงแอป

คุณเลือกได้ว่าต้องการดำเนินการกับคำตัดสินความเสี่ยงในการเข้าถึงแอปใดก่อนอนุญาตให้ผู้ใช้ส่งคำขอหรือดำเนินการให้เสร็จสมบูรณ์ ทั้งนี้ขึ้นอยู่กับระดับความเสี่ยง คุณสามารถแสดงพรอมต์ของ Google Play ให้แก่ผู้ใช้ได้หลังจากตรวจสอบคำตัดสินความเสี่ยงในการเข้าถึงแอป คุณสามารถแสดง CLOSE_UNKNOWN_ACCESS_RISK เพื่อขอให้ผู้ใช้ปิดแอปที่ไม่รู้จักซึ่งทําให้เกิดคำตัดสินความเสี่ยงในการเข้าถึงแอป หรือจะแสดง CLOSE_ALL_ACCESS_RISK เพื่อขอให้ผู้ใช้ปิดแอปทั้งหมด (ทั้งแอปที่รู้จักและไม่รู้จัก) ซึ่งทําให้เกิดคำตัดสินความเสี่ยงในการเข้าถึงแอปก็ได้

ผลการตรวจสอบของ Play Protect

เมื่อเปิดใช้แล้ว ช่อง environmentDetails ในPlay Integrity API พายโหลดจะมีผลการตัดสินของ Play Protect ดังนี้

environmentDetails: {
  playProtectVerdict: "NO_ISSUES"
}

playProtectVerdict อาจมีค่าใดค่าหนึ่งต่อไปนี้

NO_ISSUES
Play Protect เปิดอยู่และไม่พบปัญหาเกี่ยวกับแอปในอุปกรณ์
NO_DATA
Play Protect เปิดอยู่แต่ยังไม่มีการสแกน อุปกรณ์หรือแอป Play Store อาจเพิ่งรีเซ็ต
POSSIBLE_RISK
Play Protect ปิดอยู่
MEDIUM_RISK
Play Protect เปิดอยู่และพบแอปที่อาจเป็นอันตรายซึ่งติดตั้งในอุปกรณ์
HIGH_RISK
Play Protect เปิดอยู่และพบแอปที่เป็นอันตรายซึ่งติดตั้งอยู่ในอุปกรณ์
UNEVALUATED

ระบบไม่ได้ประเมินคำตัดสินของ Play Protect

ปัญหานี้อาจเกิดขึ้นได้จากหลายสาเหตุ เช่น

  • อุปกรณ์ไม่น่าเชื่อถือพอ
  • เกมเท่านั้น: บัญชีผู้ใช้ไม่มีใบอนุญาต Play สำหรับเกม

คำแนะนำในการใช้ผลการตรวจสอบของ Play Protect

เซิร์ฟเวอร์แบ็กเอนด์ของแอปสามารถเลือกได้ว่าต้องดำเนินการอย่างไรโดยอิงตามคำตัดสินตามระดับความเสี่ยงที่ยอมรับ คำแนะนำและการดำเนินการที่ผู้ใช้อาจทำมีดังนี้

NO_ISSUES
Play Protect เปิดอยู่และไม่พบปัญหาใดๆ ผู้ใช้จึงไม่ต้องดำเนินการใดๆ
POSSIBLE_RISK และ NO_DATA
เมื่อได้รับผลการตรวจสอบเหล่านี้แล้ว ให้ขอให้ผู้ใช้ตรวจสอบว่า Play Protect เปิดอยู่และได้ทำการสแกนแล้ว NO_DATA ควรปรากฏเฉพาะในบางกรณีเท่านั้น
MEDIUM_RISK และ HIGH_RISK
คุณสามารถขอให้ผู้ใช้เปิด Play Protect และดำเนินการกับคำเตือนของ Play Protect ทั้งนี้ขึ้นอยู่กับระดับความเสี่ยงที่ยอมรับ หากผู้ใช้ไม่สามารถปฏิบัติตามข้อกําหนดเหล่านี้ คุณสามารถบล็อกผู้ใช้ไม่ให้ดําเนินการกับเซิร์ฟเวอร์ได้