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

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

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

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

Intent openSafetyCenterIntent = new Intent(Intent.ACTION_SAFETY_CENTER);

startActivity(openSafetyCenterIntent);

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

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

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

  • 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 доступен в Code Search . Код реализации API доступен в Code Search .

Разрешения

API-интерфейсы источника Safety Center доступны только разрешенным системным приложениям, использующим разрешения, перечисленные ниже. Для получения дополнительной информации см. Privileged Permission Allowlisting .

  • READ_SAFETY_CENTER_STATUS
    • signature|privileged
    • Используется для API SafetyCenterManager#isSafetyCenterEnabled() (не требуется для источников Safety Center, им нужно только разрешение 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 {
  // …
}

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

Исходные данные Safety Center с заданным String sourceId предоставляются Safety Center с объектом 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 . Если actionability не 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);

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

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

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

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

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

Если вы не можете собрать данные SafetySourceData , вы можете сообщить об ошибке в Safety Center, который изменит запись на серый, очистит кэшированные данные и выдаст сообщение, похожее на Couldn't check setting . Вы также можете сообщить об ошибке, если экземпляр 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
    • Тип строки, представляет собой уникальный идентификатор запрошенного обновления.

Чтобы получить сигнал от Safety Center, реализуйте экземпляр 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 взаимодействует с Safety Center для этого экземпляра 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 представляет собой скрытый код
  • Добавлен новый код безопасности и конфиденциальности
  • Новый раздел «Безопасность и конфиденциальность» перенаправляет на код Центра безопасности
  • Действия намерения android.settings.PRIVACY_SETTINGS и android.settings.SECURITY_SETTINGS перенаправляются на открытие Центра безопасности (код: безопасность , конфиденциальность )

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

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

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

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

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

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

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

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

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

О PendingIntent

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

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

Обходной путь PendingIntent

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

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

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

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

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