Взаимодействие с Центром безопасности

Перенаправление в Центр безопасности

Любое приложение может открыть Центр безопасности с помощью действия android.content.Intent.ACTION_SAFETY_CENTER (строковое значение android.intent.action.SAFETY_CENTER ).

Чтобы открыть Центр безопасности, выполните вызов из экземпляра Activity :

Intent openSafetyCenterIntent = new Intent(Intent.ACTION_SAFETY_CENTER);

startActivity(openSafetyCenterIntent);

Перенаправление на конкретную проблему

Также можно перенаправить на конкретную предупреждающую карточку Центра безопасности, используя дополнительные функции для определенных намерений. Эти дополнительные возможности не предназначены для использования третьими лицами, поэтому они являются частью SafetyCenterManager , который является частью @SystemApi . Только системные приложения могут получить доступ к этим дополнительным функциям.

Дополнительные функции Intent, которые перенаправляют определенную предупреждающую карту:

  • EXTRA_SAFETY_SOURCE_ID
    • Строковое значение: android.safetycenter.extra.SAFETY_SOURCE_ID
    • Тип строки: указывает идентификатор источника безопасности связанной карты предупреждения.
    • Требуется для работы перенаправления на задачу
  • EXTRA_SAFETY_SOURCE_ISSUE_ID
    • Строковое значение: android.safetycenter.extra.SAFETY_SOURCE_ISSUE_ID
    • Тип строки: указывает идентификатор карты предупреждения.
    • Требуется для работы перенаправления на задачу
  • EXTRA_SAFETY_SOURCE_USER_HANDLE
    • Строковое значение: android.safetycenter.extra.SAFETY_SOURCE_USER_HANDLE
    • Тип UserHandle : указывает UserHandle для связанной карточки с предупреждением.
    • Необязательно (по умолчанию — текущий пользователь)

Приведенный ниже фрагмент кода можно использовать из экземпляра Activity , чтобы открыть экран Центра безопасности для конкретной проблемы:

UserHandle theUserHandleThisIssueCameFrom = …;

Intent openSafetyCenterIntent = new Intent(Intent.ACTION_SAFETY_CENTER)
.putExtra(SafetyCenterManager.EXTRA_SAFETY_SOURCE_ID, "TheSafetySourceIdThisIssueCameFrom")
.putExtra(SafetyCenterManager.EXTRA_SAFETY_SOURCE_ISSUE_ID, "TheSafetySourceIssueIdToRedirectTo")
.putExtra(SafetyCenterManager.EXTRA_SAFETY_SOURCE_USER_HANDLE, theUserHandleThisIssueCameFrom);

startActivity(openSafetyCenterIntent);

Перенаправление на определенную подстраницу (начиная с Android 14)

В Android 14 или более поздней версии страница Центра безопасности разделена на несколько подстраниц, которые представляют разные SafetySourcesGroup (в Android 13 это отображается как свертываемые записи).

Можно перенаправить на определенную подстраницу, используя это дополнительное намерение:

  • EXTRA_SAFETY_SOURCES_GROUP_ID
    • Строковое значение: android.safetycenter.extra.SAFETY_SOURCES_GROUP_ID
    • Тип строки: указывает идентификатор SafetySourcesGroup
    • Требуется для работы перенаправления на подстраницу

Приведенный ниже фрагмент кода можно использовать из экземпляра Activity , чтобы открыть экран Центра безопасности на определенной подстранице:

Intent openSafetyCenterIntent = new Intent(Intent.ACTION_SAFETY_CENTER)
.putExtra(SafetyCenterManager.EXTRA_SAFETY_SOURCES_GROUP_ID, "TheSafetySourcesGroupId");

startActivity(openSafetyCenterIntent);

Используйте исходные API Центра безопасности.

Исходные API-интерфейсы Центра безопасности доступны с помощью SafetyCenterManager (который является @SystemApi ). Код для поверхности API доступен в разделе «Поиск кода» . Код реализации API доступен в разделе «Поиск кода» .

Разрешения

Исходные API Центра безопасности доступны только системным приложениям из разрешенного списка, использующим разрешения, перечисленные ниже. Дополнительную информацию см. в разделе Белый список привилегированных разрешений .

  • READ_SAFETY_CENTER_STATUS
    • signature|privileged
    • Используется для API SafetyCenterManager#isSafetyCenterEnabled() (не требуется для источников Центра безопасности, им требуется только разрешение SEND_SAFETY_CENTER_UPDATE ).
    • Используется системными приложениями, которые проверяют, включен ли Центр безопасности.
    • Разрешено только системным приложениям из разрешенного списка.
  • SEND_SAFETY_CENTER_UPDATE
    • internal|privileged
    • Используется для включенного API и API источников безопасности.
    • Используется только безопасными источниками
    • Разрешено только системным приложениям из разрешенного списка.

Эти разрешения являются привилегированными, и вы можете получить их, только добавив их в соответствующий файл, например, файл com.android.settings.xml для приложения «Настройки» и в файл AndroidManifest.xml приложения. Дополнительную информацию о модели разрешений см. в разделе protectionLevel .

Получить SafetyCenterManager

SafetyCenterManager — это класс @SystemApi , доступный из системных приложений, начиная с Android 13. Этот вызов демонстрирует, как получить SafetyCenterManager:

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
  // Must be on T or above to interact with Safety Center.
  return;
}
SafetyCenterManager safetyCenterManager = context.getSystemService(SafetyCenterManager.class);
if (safetyCenterManager == null) {
  // Should not be null on T.
  return;
}

Проверьте, включен ли Центр безопасности

Этот вызов проверяет, включен ли Центр безопасности. Для вызова требуется разрешение READ_SAFETY_CENTER_STATUS или SEND_SAFETY_CENTER_UPDATE :

boolean isSafetyCenterEnabled = safetyCenterManager.isSafetyCenterEnabled();
if (isSafetyCenterEnabled) {
  // …
} else {
  // …
}

Предоставить данные

Исходные данные Центра безопасности с заданным String sourceId передаются в Центр безопасности с помощью объекта SafetySourceData , который представляет запись пользовательского интерфейса и список проблем (карточки с предупреждениями). Запись пользовательского интерфейса и карточки с предупреждениями могут иметь разные уровни серьезности, указанные в классе SafetySourceData :

  • SEVERITY_LEVEL_UNSPECIFIED
    • Серьезность не указана
    • Цвет: серый или прозрачный (в зависимости от группы SafetySourcesGroup записи)
    • Используется для динамических данных, которые представляют собой статическую запись в пользовательском интерфейсе или для отображения неопределенной записи.
    • Не следует использовать для предупреждающих карточек.
  • SEVERITY_LEVEL_INFORMATION
    • Основная информация или незначительное предложение
    • Цвет: Зеленый
  • SEVERITY_LEVEL_RECOMMENDATION
    • Рекомендация пользователю принять меры по этой проблеме, поскольку это может подвергнуть его риску.
    • Цвет: Желтый
  • SEVERITY_LEVEL_CRITICAL_WARNING
    • Критическое предупреждение о том, что пользователь должен принять меры по этой проблеме, поскольку она представляет риск.
    • Цвет: Красный

SafetySourceData

Объект SafetySourceData состоит из записи пользовательского интерфейса, предупреждающих карточек и инвариантов.

  • Необязательный экземпляр SafetySourceStatus (запись пользовательского интерфейса)
  • Список экземпляров SafetySourceIssue (предупреждающие карточки)
  • Дополнительные возможности Bundle (начиная с 14)
  • Инварианты:
    • Список SafetySourceIssue должен состоять из задач с уникальными идентификаторами.
    • Экземпляр SafetySourceIssue не должен иметь большей важности, чем SafetySourceStatus , если он существует (если только SafetySourceStatus не имеет SEVERITY_LEVEL_UNSPECIFIED , в этом случае допускаются проблемы SEVERITY_LEVEL_INFORMATION ).
    • Должны быть соблюдены дополнительные требования, налагаемые конфигурацией API, например, если источник предназначен только для проблем, он не должен предоставлять экземпляр SafetySourceStatus .

SafetySourceStatus

  • Требуемый заголовок CharSequence
  • Обязательная сводка CharSequence
  • Требуемый уровень серьезности
  • Необязательный экземпляр PendingIntent для перенаправления пользователя на нужную страницу (по умолчанию используется intentAction из конфигурации, если таковая имеется)
  • Необязательное IconAction (отображается в виде бокового значка в записи), состоящее из:
    • Требуемый тип значка, который должен быть одним из следующих типов:
      • ICON_TYPE_GEAR : отображается в виде шестеренки рядом с записью пользовательского интерфейса.
      • ICON_TYPE_INFO : отображается в виде информационного значка рядом с записью пользовательского интерфейса.
    • Требуется PendingIntent для перенаправления пользователя на другую страницу.
  • Необязательное enabled значение, которое позволяет пометить запись пользовательского интерфейса как отключенную, чтобы она не была доступна для щелчка (по умолчанию — true ).
  • Инварианты:
    • Экземпляры PendingIntent должны открывать экземпляр Activity .
    • Если запись отключена, она должна иметь обозначение SEVERITY_LEVEL_UNSPECIFIED .
    • Дополнительные требования, налагаемые конфигурацией API.

SafetySourceIssue

  • Обязательный уникальный String идентификатор
  • Требуемый заголовок CharSequence
  • Необязательный подзаголовок CharSequence
  • Обязательная сводка CharSequence
  • Требуемый уровень серьезности
  • Необязательная категория проблемы, которая должна быть одной из:
    • ISSUE_CATEGORY_DEVICE : проблема затрагивает устройство пользователя.
    • ISSUE_CATEGORY_ACCOUNT : проблема затрагивает учетные записи пользователя.
    • ISSUE_CATEGORY_GENERAL : проблема влияет на общую безопасность пользователя. Это значение по умолчанию.
    • ISSUE_CATEGORY_DATA (начиная с Android 14): проблема затрагивает данные пользователя.
    • ISSUE_CATEGORY_PASSWORDS (начиная с Android 14): проблема затрагивает пароли пользователя.
    • ISSUE_CATEGORY_PERSONAL_SAFETY (начиная с Android 14): проблема влияет на личную безопасность пользователя.
  • Список элементов Action , которые пользователь может предпринять для решения этой проблемы, причем каждый экземпляр Action состоит из:
    • Обязательный уникальный String идентификатор
    • Требуемая метка CharSequence
    • Требуется PendingIntent , чтобы перенаправить пользователя на другую страницу или обработать действие непосредственно с экрана Центра безопасности.
    • Необязательное логическое значение, позволяющее указать, можно ли решить эту проблему непосредственно с экрана Центра безопасности (по умолчанию — false ).
    • Дополнительное сообщение об успехе CharSequence , которое будет отображаться пользователю, когда проблема будет успешно решена непосредственно с экрана Центра безопасности.
  • Необязательный PendingIntent , который вызывается, когда пользователь отклоняет проблему (по умолчанию ничего не вызывается)
  • Обязательный String идентификатор типа проблемы; он похож на идентификатор проблемы, но не обязательно должен быть уникальным и используется для регистрации.
  • Необязательная String для идентификатора дедупликации. Это позволяет публиковать один и тот же SafetySourceIssue из разных источников и показывать его в пользовательском интерфейсе только один раз, при условии, что у них одна и та же deduplicationGroup (начиная с Android 14). Если не указано, проблема никогда не дедуплицируется.
  • Необязательный CharSequence для заголовка атрибуции. Это текст, показывающий, откуда возникла карточка с предупреждением (начиная с Android 14). Если не указано, используется заголовок SafetySourcesGroup
  • Необязательное решение проблемы (начиная с Android 14), которое должно быть одним из:
    • ISSUE_ACTIONABILITY_MANUAL : пользователю необходимо решить эту проблему вручную. Это значение по умолчанию.
    • ISSUE_ACTIONABILITY_TIP : Эта проблема является всего лишь подсказкой и может не требовать участия пользователя.
    • ISSUE_ACTIONABILITY_AUTOMATIC : эта проблема уже решена и может не требовать вмешательства пользователя.
  • Необязательное поведение уведомления (начиная с Android 14), которое должно быть одним из:
    • NOTIFICATION_BEHAVIOR_UNSPECIFIED : Центр безопасности решит, требуется ли уведомление для карточки с предупреждением. Это значение по умолчанию.
    • NOTIFICATION_BEHAVIOR_NEVER : уведомление не публикуется.
    • NOTIFICATION_BEHAVIOR_DELAYED : уведомление публикуется через некоторое время после первого сообщения о проблеме.
    • NOTIFICATION_BEHAVIOR_IMMEDIATELY : уведомление публикуется, как только сообщается о проблеме.
  • Дополнительное Notification : отображение специального уведомления с карточкой предупреждения (начиная с Android 14). Если не указано, Notification создается на основе карточки с предупреждением. Состоит из:
    • Требуемый заголовок CharSequence
    • Обязательная сводка CharSequence
    • Список элементов Action , которые пользователь может предпринять для этого уведомления
  • Инварианты:
    • Список экземпляров Action должен состоять из действий с уникальными идентификаторами.
    • Список экземпляров Action должен содержать один или два элемента Action . Если возможность действия не равна ISSUE_ACTIONABILITY_MANUAL , допускается нулевое Action .
    • OnDismiss PendingIntent не должен открывать экземпляр Activity
    • Дополнительные требования, налагаемые конфигурацией API

Данные передаются в Центр безопасности при определенных событиях, поэтому необходимо указать, что заставило источник предоставить SafetySourceData экземпляр SafetyEvent .

SafetyEvent

  • Обязательный тип, который должен быть одним из:
    • SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED : состояние источника изменилось.
    • SAFETY_EVENT_TYPE_REFRESH_REQUESTED : ответ на сигнал обновления/повторного сканирования из Центра безопасности; используйте это вместо SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED , чтобы Центр безопасности мог отслеживать запрос на обновление/повторное сканирование.
    • SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED : мы разрешили SafetySourceIssue.Action непосредственно с экрана Центра безопасности; используйте это вместо SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED , чтобы Центр безопасности мог отслеживать разрешаемое SafetySourceIssue.Action .
    • SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED : мы попытались разрешить SafetySourceIssue.Action непосредственно с экрана Центра безопасности, но не смогли этого сделать; используйте это вместо SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED , чтобы Центр безопасности мог отслеживать сбой SafetySourceIssue.Action .
    • SAFETY_EVENT_TYPE_DEVICE_LOCALE_CHANGED : язык устройства изменился, поэтому мы обновляем текст предоставленных данных; для этого разрешено использовать SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED .
    • SAFETY_EVENT_TYPE_DEVICE_REBOOTED : мы предоставляем эти данные как часть начальной загрузки, поскольку данные Центра безопасности не сохраняются при перезагрузках; для этого разрешено использовать SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED .
  • Необязательный String идентификатор для идентификатора обновления широковещательной рассылки.
  • Необязательный String идентификатор для разрешаемого экземпляра SafetySourceIssue .
  • Необязательный String идентификатор для разрешаемого экземпляра SafetySourceIssue.Action .
  • Инварианты:
    • Идентификатор обновления широковещательного сообщения должен быть предоставлен, если тип SAFETY_EVENT_TYPE_REFRESH_REQUESTED
    • Идентификаторы проблемы и действия должны быть указаны, если тип SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED или SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED

Ниже приведен пример того, как источник может предоставлять данные в Центр безопасности (в данном случае он предоставляет запись с одной карточкой предупреждения):

PendingIntent redirectToMyScreen =
    PendingIntent.getActivity(
        context, requestCode, redirectToMyScreenIntent, PendingIntent.FLAG_IMMUTABLE);
SafetySourceData safetySourceData =
    new SafetySourceData.Builder()
        .setStatus(
            new SafetySourceStatus.Builder(
                    "title", "summary", SafetySourceData.SEVERITY_LEVEL_RECOMMENDATION)
                .setPendingIntent(redirectToMyScreen)
                .build())
        .addIssue(
            new SafetySourceIssue.Builder(
                    "MyIssueId",
                    "title",
                    "summary",
                    SafetySourceData.SEVERITY_LEVEL_RECOMMENDATION,
                    "MyIssueTypeId")
                .setSubtitle("subtitle")
                .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_DEVICE)
                .addAction(
                    new SafetySourceIssue.Action.Builder(
                            "MyIssueActionId", "label", redirectToMyScreen)
                        .build())
                .build())
        .build();
SafetyEvent safetyEvent = new SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build();
safetyCenterManager.setSafetySourceData("MySourceId", safetySourceData, safetyEvent);

Получить последние предоставленные данные

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

Используйте этот код, чтобы получить последние данные, предоставленные Центру безопасности:

SafetySourceData lastDataProvided = safetyCenterManager.getSafetySourceData("MySourceId");

Сообщить об ошибке

Если вы не можете собрать данные SafetySourceData , вы можете сообщить об ошибке в Центр безопасности, который изменит запись на серый цвет, очистит кэшированные данные и выдаст сообщение, подобное Не удалось проверить настройку . Вы также можете сообщить об ошибке, если экземпляр SafetySourceIssue.Action не удается разрешить; в этом случае кэшированные данные не очищаются и запись пользовательского интерфейса не изменяется; но пользователю отображается сообщение, сообщающее ему, что что-то пошло не так.

Вы можете указать ошибку, используя SafetySourceErrorDetails , который состоит из:

  • SafetySourceErrorDetails : обязательный экземпляр SafetyEvent :
// An error has occurred in the background, need to clear the Safety Center data to avoid showing data that may not be valid anymore
SafetyEvent safetyEvent = new SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build();
SafetySourceErrorDetails safetySourceErrorDetails = new SafetySourceErrorDetails(safetyEvent);
safetyCenterManager.reportSafetySourceError("MySourceId", safetySourceErrorDetails);

Ответ на запрос обновления или повторного сканирования

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

Это делается путем приема широковещательной рассылки со следующим действием:

  • ACTION_REFRESH_SAFETY_SOURCES
    • Строковое значение: android.safetycenter.action.REFRESH_SAFETY_SOURCES
    • Срабатывает, когда Центр безопасности отправляет запрос на обновление данных источника безопасности для данного приложения.
    • Защищенное намерение, которое может быть отправлено только системой.
    • Отправляется всем источникам безопасности в файле конфигурации как явное намерение и требует разрешения SEND_SAFETY_CENTER_UPDATE

В рамках этой трансляции предоставляются следующие дополнительные материалы:

  • EXTRA_REFRESH_SAFETY_SOURCE_IDS
    • Строковое значение: android.safetycenter.extra.REFRESH_SAFETY_SOURCE_IDS
    • Тип строкового массива ( String[] ) представляет идентификаторы источников, которые необходимо обновить для данного приложения.
  • EXTRA_REFRESH_SAFETY_SOURCES_REQUEST_TYPE

    • Строковое значение: android.safetycenter.extra.REFRESH_SAFETY_SOURCES_REQUEST_TYPE
    • Целочисленный тип, представляет тип запроса @IntDef
    • Должно быть одно из:
      • EXTRA_REFRESH_REQUEST_TYPE_GET_DATA : запрашивает источник предоставить данные относительно быстро, обычно когда пользователь открывает страницу.
      • EXTRA_REFRESH_REQUEST_TYPE_FETCH_FRESH_DATA : запрашивает источник предоставить как можно более свежие данные, обычно когда пользователь нажимает кнопку повторного сканирования.
  • EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID

    • Строковое значение: android.safetycenter.extra.REFRESH_SAFETY_SOURCES_BROADCAST_ID
    • Строковый тип представляет собой уникальный идентификатор запрошенного обновления.

Чтобы получить сигнал из Центра безопасности, реализуйте экземпляр BroadcastReceiver . Трансляция отправляется со специальными BroadcastOptions , которые позволяют получателю запустить приоритетную службу.

BroadcastReceiver отвечает на запрос обновления:

public final class SafetySourceReceiver extends BroadcastReceiver {
  // All the safety sources owned by this application.
  private static final String[] ALL_SAFETY_SOURCES = new String[] {"MySourceId1", "…"};
  @Override
  public void onReceive(Context context, Intent intent) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
      // Must be on T or above to interact with Safety Center.
      return;
    }
    String action = intent.getAction();
    if (!SafetyCenterManager.ACTION_REFRESH_SAFETY_SOURCES.equals(action)) {
      return;
    }
    String refreshBroadcastId =
        intent.getStringExtra(SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID);
    if (refreshBroadcastId == null) {
      // Should always be provided.
      return;
    }
    String[] sourceIds =
        intent.getStringArrayExtra(SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCE_IDS);
    if (sourceIds == null) {
      sourceIds = ALL_SAFETY_SOURCES;
    }
    int requestType =
        intent.getIntExtra(
            SafetyCenterManager.EXTRA_REFRESH_SAFETY_SOURCES_REQUEST_TYPE,
            SafetyCenterManager.EXTRA_REFRESH_REQUEST_TYPE_GET_DATA);
    SafetyCenterManager safetyCenterManager = context.getSystemService(SafetyCenterManager.class);
    if (safetyCenterManager == null) {
      // Should not be null on T.
      return;
    }
    if (!safetyCenterManager.isSafetyCenterEnabled()) {
      // Preferably, no Safety Source code should be run if Safety Center is disabled.
      return;
    }
    SafetyEvent refreshSafetyEvent =
        new SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_REFRESH_REQUESTED)
            .setRefreshBroadcastId(refreshBroadcastId)
            .build();
    for (String sourceId : sourceIds) {
      SafetySourceData safetySourceData = getSafetySourceDataFor(sourceId, requestType);
      // Set the data (or report an error with reportSafetySourceError, if something went wrong).
      safetyCenterManager.setSafetySourceData(sourceId, safetySourceData, refreshSafetyEvent);
    }
  }
  private SafetySourceData getSafetySourceDataFor(String sourceId, int requestType) {
    switch (requestType) {
      case SafetyCenterManager.EXTRA_REFRESH_REQUEST_TYPE_GET_DATA:
        return getRefreshSafetySourceDataFor(sourceId);
      case SafetyCenterManager.EXTRA_REFRESH_REQUEST_TYPE_FETCH_FRESH_DATA:
        return getRescanSafetySourceDataFor(sourceId);
      default:
    }
    return getRefreshSafetySourceDataFor(sourceId);
  }
  // Data to provide when the user opens the page or on specific events.
  private SafetySourceData getRefreshSafetySourceDataFor(String sourceId) {
    // Get data for the source, if it's a fast operation it could potentially be executed in the
    // receiver directly.
    // Otherwise, it must start some kind of foreground service or expedited job.
    return null;
  }
  // Data to provide when the user pressed the rescan button.
  private SafetySourceData getRescanSafetySourceDataFor(String sourceId) {
    // Could be implemented the same way as getRefreshSafetySourceDataFor, depending on the source's
    // need.
    // Otherwise, could potentially perform a longer task.
    // In which case, it must start some kind of foreground service or expedited job.
    return null;
  }
}

Тот же экземпляр BroadcastReceiver в приведенном выше примере объявлен в AndroidManifest.xml :

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="…">
    <application>
    <!-- … -->
        <receiver android:name=".SafetySourceReceiver"
            android:exported="false">
            <intent-filter>
                <action android:name="android.safetycenter.action.REFRESH_SAFETY_SOURCES"/>
            </intent-filter>
        </receiver>
    <!-- … -->
    </application>
</manifest>

В идеале источник Центра безопасности реализован таким образом, что он вызывает SafetyCenterManager при изменении своих данных. По соображениям работоспособности системы мы рекомендуем реагировать только на сигнал повторного сканирования (когда пользователь нажимает кнопку сканирования), а не когда пользователь открывает Центр безопасности. Если эта функция требуется, refreshOnPageOpenAllowed="true" в файле конфигурации должно быть установлено, чтобы источник мог получать широковещательную рассылку, доставляемую в этих случаях.

Ответить на запросы Центра безопасности при включении или отключении

Вы можете отреагировать на включение или отключение Центра безопасности с помощью этого действия с намерением:

  • ACTION_SAFETY_CENTER_ENABLED_CHANGED
    • Строковое значение: android.safetycenter.action.SAFETY_CENTER_ENABLED_CHANGED
    • Срабатывает, когда Центр безопасности включен или отключен во время работы устройства.
    • Не вызывается при загрузке (для этого используйте ACTION_BOOT_COMPLETED )
    • Защищенное намерение, которое может быть отправлено только системой.
    • Отправляется во все источники безопасности в файле конфигурации в качестве явного намерения, требует разрешения SEND_SAFETY_CENTER_UPDATE
    • Отправляется как неявное намерение, требующее разрешения READ_SAFETY_CENTER_STATUS

Это намеренное действие полезно для включения или отключения функций, связанных с Центром безопасности на устройстве.

Реализуйте разрешающие действия

Разрешающее действие — это экземпляр SafetySourceIssue.Action , который пользователь может выполнить непосредственно с экрана Центра безопасности. Пользователь нажимает кнопку действия, и запускается экземпляр PendingIntent в SafetySourceIssue.Action , отправленный источником безопасности, который решает проблему в фоновом режиме и уведомляет Центр безопасности, когда это будет сделано.

Для реализации разрешающих действий источник Центра безопасности может использовать службу, если ожидается, что операция займет некоторое время ( PendingIntent.getService ) или широковещательный приемник ( PendingIntent.getBroadcast ).

Используйте этот код, чтобы отправить решение проблемы в Центр безопасности:

Intent resolveIssueBroadcastIntent =
    new Intent("my.package.name.MY_RESOLVING_ACTION").setClass(ResolveActionReceiver.class);
PendingIntent resolveIssue =
    PendingIntent.getBroadcast(
        context, requestCode, resolveIssueBroadcastIntent, PendingIntent.FLAG_IMMUTABLE);
SafetySourceData safetySourceData =
    new SafetySourceData.Builder()
        .setStatus(
            new SafetySourceStatus.Builder(
                    "title", "summary", SafetySourceData.SEVERITY_LEVEL_RECOMMENDATION)
                .setPendingIntent(redirectToMyScreen)
                .build())
        .addIssue(
            new SafetySourceIssue.Builder(
                    "MyIssueId",
                    "title",
                    "summary",
                    SafetySourceData.SEVERITY_LEVEL_RECOMMENDATION,
                    "MyIssueTypeId")
                .setIssueCategory(SafetySourceIssue.ISSUE_CATEGORY_DEVICE)
                .addAction(
                    new SafetySourceIssue.Action.Builder(
                            "MyIssueActionId", "label", resolveIssue)
                        .setWillResolve(true)
                        .build())
                .build())
        .build();
SafetyEvent safetyEvent = new SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED).build();
safetyCenterManager.setSafetySourceData("MySourceId", safetySourceData, safetyEvent);

BroadcastReceiver разрешает действие:

public final class ResolveActionReceiver extends BroadcastReceiver {
  private static final String MY_RESOLVING_ACTION = "my.package.name.MY_RESOLVING_ACTION";
  @Override
  public void onReceive(Context context, Intent intent) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
      // Must be on T or above to interact with Safety Center.
      return;
    }
    String action = intent.getAction();
    if (!MY_RESOLVING_ACTION.equals(action)) {
      return;
    }
    SafetyCenterManager safetyCenterManager = context.getSystemService(SafetyCenterManager.class);
    if (safetyCenterManager == null) {
      // Should not be null on T.
      return;
    }
    if (!safetyCenterManager.isSafetyCenterEnabled()) {
      // Preferably, no Safety Source code should be run if Safety Center is disabled.
      return;
    }
    resolveTheIssue();
    SafetyEvent resolveActionSafetyEvent =
        new SafetyEvent.Builder(SafetyEvent.SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED)
            .setSafetySourceIssueId("MyIssueId")
            .setSafetySourceIssueActionId("MyIssueActionId")
            .build();
    SafetySourceData dataWithoutTheIssue = …;
    // Set the data (or report an error with reportSafetySourceError and
    // SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED, if something went wrong).
    safetyCenterManager.setSafetySourceData("MySourceId", dataWithoutTheIssue, resolveActionSafetyEvent);
  }

  private void resolveTheIssue() {
    // Resolves the issue for the user. Given this a BroadcastReceiver, this should be a fast action.
    // Otherwise, a foreground service and PendingIntent.getService should be used instead (or a job
    // could be scheduled here, too).
  }
}

Тот же экземпляр BroadcastReceiver в приведенном выше примере объявлен в AndroidManifest.xml :

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="…">
    <application>
    <!-- … -->
        <receiver android:name=".ResolveActionReceiver"
            android:exported="false">
            <intent-filter>
                <action android:name="my.package.name.MY_RESOLVING_ACTION"/>
            </intent-filter>
        </receiver>
    <!-- … -->
    </application>
</manifest>

Реагировать на увольнения

Вы можете указать экземпляр PendingIntent , который будет запускаться при закрытии экземпляра SafetySourceIssue . Центр безопасности занимается увольнениями по следующим вопросам:

  • Если источник сообщает о проблеме, пользователь может закрыть ее на экране Центра безопасности, нажав кнопку «Отклонить» (кнопка X на карточке с предупреждением).
  • Когда пользователь отклоняет проблему, если проблема сохраняется, она больше не будет отображаться в пользовательском интерфейсе.
  • Постоянные увольнения на диске сохраняются во время перезагрузок устройства.
  • Если источник Центра безопасности перестанет сообщать о проблеме, а затем снова предоставит ее позже, проблема появится снова. Это сделано для того, чтобы разрешить ситуации, когда пользователь видит предупреждение, отклоняет его, затем предпринимает действия, которые должны решить проблему, но затем пользователь снова делает что-то, что вызывает аналогичную проблему. В этот момент предупреждение должно появиться снова.
  • Желтые и красные карточки с предупреждениями появляются каждые 180 дней, если пользователь не закрыл их несколько раз.

Источнику не требуется дополнительное поведение, если:

  • Источник пытается реализовать это поведение по-другому, например, никогда не поднимать проблему повторно.
  • Источник пытается использовать это как обратный вызов, например, для регистрации информации.

Предоставьте данные для нескольких пользователей/профилей

API SafetyCenterManager можно использовать для разных пользователей и профилей. Дополнительные сведения см. в разделе Создание приложений с поддержкой многопользовательской работы . Объект Context , предоставляющий SafetyCenterManager связан с экземпляром UserHandle , поэтому возвращаемый экземпляр SafetyCenterManager взаимодействует с центром безопасности для этого экземпляра UserHandle . По умолчанию Context связан с работающим пользователем, но можно создать экземпляр для другого пользователя, если приложение имеет разрешения INTERACT_ACROSS_USERS и INTERACT_ACROSS_USERS_FULL . В этом примере показано выполнение вызова между пользователями/профилями:

Context userContext = context.createContextAsUser(userHandle, 0);
SafetyCenterManager userSafetyCenterManager = userContext.getSystemService(SafetyCenterManager.class);
if (userSafetyCenterManager == null) {
  // Should not be null on T.
  return;
}
// Calls to userSafetyCenterManager will provide data for the given userHandle

Каждый пользователь на устройстве может иметь несколько управляемых профилей. Центр безопасности предоставляет разные данные для каждого пользователя, но объединяет данные всех управляемых профилей, связанных с данным пользователем.

Когда в файле конфигурации для источника задано profile="all_profiles" , происходит следующее:

  • Существует запись пользовательского интерфейса для пользователя (родительского профиля) и всех связанных с ним управляемых профилей (которые используют экземпляры titleForWork ).
  • Сигнал обновления или повторного сканирования отправляется для родительского профиля и всех связанных управляемых профилей. Связанный получатель запускается для каждого профиля и может предоставлять связанные данные непосредственно в SafetyCenterManager без необходимости выполнения межпрофильного вызова, если только получатель или приложение не является singleUser .

  • Ожидается, что источник предоставит данные о пользователе и всех его управляемых профилях. Данные для каждой записи пользовательского интерфейса могут отличаться в зависимости от профиля.

Тестирование

вы можете получить доступ к ShadowSafetyCenterManager и использовать его в тесте Robolectric.

private static final String MY_SOURCE_ID = "MySourceId";

private final MyClass myClass = …;
private final SafetyCenterManager safetyCenterManager = getApplicationContext().getSystemService(SafetyCenterManager.class);

@Test
public void whenRefreshingData_providesDataToSafetyCenterForMySourceId() {
    shadowOf(safetyCenterManager).setSafetyCenterEnabled(true);
    setupDataForMyClass(…);

    myClass.refreshData();

    SafetySourceData expectedSafetySourceData = …;
    assertThat(safetyCenterManager.getSafetySourceData(MY_SOURCE_ID)).isEqualTo(expectedSafetySourceData);
    SafetyEvent expectedSafetyEvent = …;
    assertThat(shadowOf(safetyCenterManager).getLastSafetyEvent(MY_SOURCE_ID)).isEqualTo(expectedSafetyEvent);
}

Вы можете написать больше сквозных (E2E) тестов, но это выходит за рамки данного руководства. Дополнительные сведения о написании этих тестов E2E см. в разделе Тесты CTS (CtsSafetyCenterTestCases).

Тестовые и внутренние API

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

Разрешения

  • MANAGE_SAFETY_CENTER
    • internal|installer|role
    • Используется для внутренних API-интерфейсов Центра безопасности.
    • Предоставляется только PermissionController и оболочке

Приложение настроек

Перенаправление Центра безопасности

По умолчанию доступ к Центру безопасности осуществляется через приложение «Настройки» с новой записью «Безопасность и конфиденциальность» . Если вы используете другое приложение «Настройки» или изменили приложение «Настройки», вам может потребоваться настроить способ доступа к Центру безопасности.

Когда Центр безопасности включен:

  • Устаревшая запись конфиденциальности представляет собой скрытый код
  • Запись Legacy Security представляет собой скрытый код
  • Добавлен код новой записи «Безопасность и конфиденциальность» .
  • Новая запись «Безопасность и конфиденциальность» перенаправляет на код Центра безопасности.
  • Действия намерения android.settings.PRIVACY_SETTINGS и android.settings.SECURITY_SETTINGS перенаправляются на открытие Центра безопасности (код: безопасность , конфиденциальность )

Страницы расширенной безопасности и конфиденциальности

Приложение «Настройки» содержит дополнительные настройки в разделах «Дополнительные настройки безопасности» и «Дополнительные настройки конфиденциальности» , доступные в Центре безопасности:

  • Расширенный код безопасности

  • Расширенный код конфиденциальности

  • Начиная с Android 14, страница расширенных настроек безопасности и конфиденциальности объединена в одну страницу «Большая безопасность и конфиденциальность» с действием намерения "com.android.settings.MORE_SECURITY_PRIVACY_SETTINGS"

Источники безопасности

Центр безопасности интегрируется с определенным набором источников безопасности, предоставляемых приложением «Настройки»:

  • Источник безопасности экрана блокировки проверяет, настроен ли экран блокировки с паролем (или другой защитой), чтобы гарантировать, что личная информация пользователя хранится в безопасности от внешнего доступа.
  • Источник биометрической безопасности (скрытый по умолчанию) появляется для интеграции с датчиком отпечатка пальца или лица.

Исходный код этих источников Центра безопасности доступен через поиск по коду Android . Если приложение «Настройки» не изменено (не внесены изменения в имя пакета, исходный код или исходный код, который касается экрана блокировки и биометрии), то эта интеграция должна работать «из коробки». В противном случае могут потребоваться некоторые изменения, например изменение файла конфигурации для изменения имени пакета приложения «Настройки» и источников, которые интегрируются с Центром безопасности, а также самой интеграции. Дополнительную информацию см. в разделе Обновление файла конфигурации и параметров интеграции .

О ожидающем намерении

Если вы полагаетесь на существующую интеграцию Центра безопасности приложения «Настройки» в Android 14 или более поздней версии, описанная ниже ошибка была исправлена. В этом случае чтение данного раздела не обязательно.

Если вы уверены, что ошибки не существует, установите для логического значения конфигурации ресурса XML в приложении «Настройки» config_isSafetyCenterLockScreenPendingIntentFixed значение true чтобы отключить обходной путь в Центре безопасности.

Обходное решение PendingIntent

Эта ошибка вызвана тем, что настройки используют дополнительные возможности экземпляра Intent , чтобы определить, какой фрагмент открывать. Поскольку Intent#equals не учитывает дополнительные элементы экземпляра Intent , экземпляр PendingIntent для значка меню шестеренки и записи считаются равными и переходят к одному и тому же пользовательскому интерфейсу (даже если они предназначены для перехода к другому пользовательскому интерфейсу). Эта проблема исправлена ​​в выпуске QPR путем дифференциации экземпляров PendingIntent по коду запроса. В качестве альтернативы это можно отличить, используя Intent#setId .

Внутренние источники безопасности

Некоторые источники Центра безопасности являются внутренними и реализованы в системном приложении PermissionController внутри модуля PermissionController. Эти источники ведут себя как обычные источники Центра безопасности и не подвергаются специальной обработке. Код для этих источников доступен через поиск по коду Android .

В основном это сигналы конфиденциальности, например:

  • Доступность
  • Автоматическое удаление неиспользуемых приложений
  • Доступ к местоположению
  • Прослушиватель уведомлений
  • Информация о политике работы