Оценки целостности

На этой странице рассказывается, как интерпретировать и использовать результаты проверки целостности. Как при стандартных, так и при классических запросах к API оценка целостности возвращается в одном и том же формате и примерно с одинаковым содержимым. Она указывает, можно ли доверять устройствам, приложениям и аккаунтам. Сервер приложения может использовать полученную полезную нагрузку в расшифрованной, проверенной оценке, чтобы определить, как лучше всего поступить с конкретным действием или запросом в вашем приложении.

Формат возвращаемой оценки целостности

Полезная нагрузка представляет собой обычный текст в формате JSON и содержит сигналы целостности, а также информацию, предоставленную разработчиком.

Общая структура полезной нагрузки выглядит следующим образом:

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

Порядок полей в полезной нагрузке JSON не гарантируется. Перед проверкой каждой оценки целостности необходимо убедиться, что данные в поле 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.
        ...
}

Поле "Сведения об аккаунте"

Поле accountDetails содержит единственное значение, appLicensingVerdict, которое представляет статус лицензирования приложения в Google Play для аккаунта пользователя, вошедшего в систему на устройстве. Если у аккаунта пользователя есть лицензия Google Play на приложение, это означает, что он скачал или купил его в Google Play.

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

appLicensingVerdict может иметь одно из следующих значений:

LICENSED
У пользователя есть право на использование приложения. Другими словами, пользователь установил или обновил ваше приложение из Google Play на своем устройстве.
UNLICENSED
У пользователя нет права на использование приложения. Это происходит, например, если ваше приложение установлено из неизвестного источника. Чтобы устранить это нарушение, пользователю может быть показано диалоговое окно 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!
}

Поле "Целостность приложения"

В поле 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, в котором указана одна или несколько меток, обозначающих, насколько эффективно обеспечивается целостность приложений на устройстве. Если устройство не соответствует критериям ни одной из меток, поле deviceIntegrity будет пустымdeviceRecognitionVerdict.

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

По умолчанию в deviceRecognitionVerdict могут содержаться следующие элементы:

MEETS_DEVICE_INTEGRITY
Приложение запущено на оригинальном сертифицированном устройстве Android. На устройствах с Android 13 и более поздних версий есть аппаратная поддержка подтверждения того, что загрузчик операционной системы заблокирован и загруженная ОС Android является сертифицированным образом производителя устройства.
Пустое значение
Приложение запущено на устройстве, где зафиксированы признаки взлома (например, перехват управления API) или нарушения системы безопасности (например, настройка root-доступа), или приложение запущено не на физическом устройстве (например, на эмуляторе, который не прошел проверку целостности 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 для ПК, в элементе deviceRecognitionVerdict может быть указана следующая метка:

MEETS_VIRTUAL_INTEGRITY
Приложение запущено в эмуляторе Android с сервисами Google Play. Эмулятор прошел проверку целостности системы и соответствует основным требованиям Android к совместимости.

Необязательная информация об устройстве и запоминание устройства

Вы можете включить получение дополнительных меток устройств в рамках многоуровневой стратегии защиты. Если вы согласитесь получать дополнительные ярлыки в оценке целостности, элемент deviceRecognitionVerdict может содержать следующие дополнительные ярлыки:

MEETS_BASIC_INTEGRITY
Устройство, на котором запускается приложение, проходит основные проверки целостности системы. Загрузчик операционной системы устройства может быть заблокирован или разблокирован, а состояние при запуске может быть проверенным или непроверенным. Устройство может быть не сертифицировано. В этом случае Google не может гарантировать безопасность, конфиденциальность и совместимость приложений. В Android 13 и более поздних версиях для оценки MEETS_BASIC_INTEGRITY требуется только, чтобы корень доверия для подтверждения был предоставлен Google.
MEETS_STRONG_INTEGRITY
Приложение запущено на оригинальном сертифицированном устройстве Android с последним обновлением системы безопасности.
  • В Android 13 и более поздних версиях для вердикта MEETS_STRONG_INTEGRITY требуется MEETS_DEVICE_INTEGRITY и обновления системы безопасности за последний год для всех разделов устройства, включая исправление для раздела ОС Android и исправление для раздела поставщика.
  • В Android 12 и более ранних версиях для оценки MEETS_STRONG_INTEGRITY требуется только подтверждение целостности загрузки из аппаратного хранилища. Не требуется, чтобы на устройстве было установлено последнее обновление системы безопасности. Поэтому при использовании MEETS_STRONG_INTEGRITY рекомендуется также учитывать версию Android SDK в поле deviceAttributes.

Если устройство соответствует критериям нескольких меток, в вердикте защиты целостности данных на устройстве будет несколько меток.

Атрибуты устройств

Вы также можете включить атрибуты устройств, которые показывают версию Android SDK ОС Android, установленной на устройстве. Версия Android SDK позволяет различать устройства с Android 13 и более поздними версиями и устройства с более ранними версиями Android SDK в рамках многоуровневой стратегии применения правил. В будущем в него могут быть добавлены другие атрибуты устройств.

Значение версии SDK – это номер версии Android SDK, указанный в файле Build.VERSION_CODES. Версия SDK не проверяется, если не соблюдено обязательное требование. В этом случае поле sdkVersion не задано, поэтому поле deviceAttributes пустое. Это может произойти по следующим причинам:

  • Устройство не считается достаточно надежным.
  • На устройстве возникли технические проблемы.

Если вы включите получение объекта 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.
}

Недавние действия на устройстве

Вы также можете включить функцию "Недавние действия на устройстве", которая показывает, сколько раз за последний час ваше приложение запрашивало токен целостности на определенном устройстве. Эта функция позволяет защитить приложение при необычно высокой активности устройств, которая может быть признаком действий злоумышленника. Вы можете решить, насколько доверять каждому уровню недавних действий на устройстве, исходя из того, сколько раз в час ваше приложение, установленное на обычном устройстве, должно запрашивать токен целостности.

Если вы включите получение recentDeviceActivity, поле deviceIntegrity будет содержать два значения:

"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 13 и более поздних версий).
  • Технические проблемы с устройством.

Сохранение данных об устройстве (бета)

Вы также можете включить запоминание устройства, которое позволяет хранить некоторые пользовательские данные для каждого устройства и надежно получать их, когда ваше приложение будет снова установлено на том же устройстве. После запроса токена целостности вы выполняете отдельный межсерверный вызов, чтобы изменить значения запоминания устройства для определенного устройства.

Если вы включите функцию deviceRecall, в поле deviceIntegrity будет содержаться информация о запоминании устройства, которую вы задали для определенного устройства:

"deviceIntegrity": {
  "deviceRecognitionVerdict": ["MEETS_DEVICE_INTEGRITY"],
  "deviceRecall": {
    "values": {
      "bitFirst": true,
      "bitSecond": false,
      "bitThird": true
    },
    "writeDates": {
      // Write time in YYYYMM format in UTC.
      "yyyymmFirst": 202401,
      // Note that yyyymmSecond is not set because bitSecond is false.
      "yyyymmThird": 202310
    }
  }
}

Объект deviceRecall разделен на два поля:

  • values – восстановить значения битов, заданные ранее для этого устройства.
  • writeDates: укажите даты записи битов по всемирному координированному времени с точностью до года и месяца. Дата записи бита отзыва будет обновляться каждый раз, когда бит устанавливается в значение true, и удаляться, когда бит устанавливается в значение false.

Если информация о запоминании устройства недоступна, значение device recall будет пустым:

"deviceIntegrity": {
  "deviceRecognitionVerdict": ["MEETS_DEVICE_INTEGRITY"],
  "deviceRecall": {
    "values": {},
    "writeDates": {}
  }
}

Поле "Сведения о среде"

Вы также можете включить дополнительные сигналы о среде. Сигнал о риске доступа к приложению сообщает о запущенных сторонних приложениях, которые могут быть использованы для записи экрана, показа оверлеев или управления устройством. Оценка Play Защиты указывает, включена ли на устройстве Google Play Защита и обнаружено ли известное вредоносное ПО.

Если вы включили оценку риска доступа к приложению или оценку Google Play Защиты в Google Play Console, в ответе API будет поле environmentDetails. Поле environmentDetails может содержать два значения: appAccessRiskVerdict и playProtectVerdict.

Оценка риска доступа к приложению

Если он включен, в поле environmentDetails данных о полезной нагрузке Play Integrity API будет указана новая оценка риска доступа к приложению.

{
  "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 с одним или несколькими ответами. Эти ответы делятся на две группы в зависимости от источника установки обнаруженных приложений:

  • Приложения Google 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.
  • У аккаунта пользователя нет лицензии Play.
  • Был использован стандартный запрос с параметром verdictOptOut.
  • Стандартный запрос был отправлен с помощью версии библиотеки Play Integrity API, которая ещё не поддерживает оценку риска доступа к приложению для стандартных запросов.

Риск доступа к приложению автоматически исключает проверенные сервисы специальных возможностей, которые прошли расширенную проверку Google Play (установлены из любого магазина приложений на устройстве). Если указано "Исключено", это означает, что проверенные сервисы специальных возможностей, запущенные на устройстве, не будут возвращать ответ о захвате экрана, управлении или оверлеях в вердикте о риске доступа к приложению. Чтобы запросить расширенную проверку приложения для доступа к специальным возможностям в Google Play, опубликуйте его в Google Play, убедившись, что в манифесте приложения для флага isAccessibilityTool задано значение true, или запросите проверку.

Примеры оценок риска доступа к приложению

В таблице ниже приведены примеры вердиктов о риске доступа к приложению и их значения (в таблице перечислены не все возможные результаты):

Пример ответа с оценкой риска доступа к приложению Пояснения
appsDetected:
["KNOWN_INSTALLED"]
Установлены только приложения, которые распознаются Google Play или были предварительно загружены в системный раздел производителем устройства.
Нет запущенных приложений, которые могли бы привести к вердиктам "Захват", "Управление" или "Оверлей".
appsDetected:
["KNOWN_INSTALLED",
"UNKNOWN_INSTALLED",
"UNKNOWN_CAPTURING"]
Приложения, установленные через Google Play или предустановленные в системном разделе производителем устройства.
Другие запущенные приложения с включенными разрешениями, которые можно использовать для просмотра экрана или записи других входных и выходных данных.
appsDetected:
["KNOWN_INSTALLED",
"KNOWN_CAPTURING",
"UNKNOWN_INSTALLED",
"UNKNOWN_CONTROLLING"]
Запущены приложения Google 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 Защиты

Если эта функция включена, в поле environmentDetails данных о полезной нагрузке Play Integrity API будет указана оценка Play Защиты.

"environmentDetails": {
  "playProtectVerdict": "NO_ISSUES"
}

playProtectVerdict может иметь одно из следующих значений:

NO_ISSUES
Сервис "Google Play Защита" включен и не обнаружил проблем с приложением на устройстве.
NO_DATA
Google Play Защита включена, но проверка ещё не проводилась. Возможно, настройки устройства или приложения Google Play недавно были сброшены.
POSSIBLE_RISK
Play Защита отключена.
MEDIUM_RISK
Сервис "Google Play Защита" включен и обнаружил, что на устройстве установлены потенциально опасные приложения.
HIGH_RISK
Google Play Защита включена, и с ее помощью на устройстве были обнаружены опасные приложения.
UNEVALUATED

Наличие Google Play Защиты и вредоносного ПО не проверялось.

Это может происходить в том числе по следующим причинам:

  • Устройство не считается достаточно надежным.
  • У аккаунта пользователя нет лицензии Play.

Рекомендации по использованию оценки Play Защиты

Внутренний сервер будет реагировать на риск согласно заданным допускам. Вот несколько советов и возможных действий пользователей:

NO_ISSUES
Play Защита включена и не обнаружила проблем, поэтому никаких действий не требуется.
POSSIBLE_RISK и NO_DATA
Если вы получили такой вердикт, попросите пользователя проверить, включена ли Play Защита и выполнена ли проверка. NO_DATA должен появляться только в редких случаях.
MEDIUM_RISK и HIGH_RISK
В зависимости от того, насколько вы готовы к риску, вы можете попросить пользователя запустить Google Play Защиту и выполнить действия, рекомендованные в предупреждениях. Если пользователь не сможет выполнить эти требования, вы можете заблокировать для него действие на сервере.