Interactúa con el Centro de seguridad

Redireccionar al Centro de seguridad

Cualquier app puede abrir el Centro de seguridad con la acción android.content.Intent.ACTION_SAFETY_CENTER (valor de cadena android.intent.action.SAFETY_CENTER).

Para abrir Safety Center, realiza una llamada desde una instancia de Activity:

Intent openSafetyCenterIntent = new Intent(Intent.ACTION_SAFETY_CENTER);

startActivity(openSafetyCenterIntent);

Redirecciona a un problema específico

También es posible redireccionar a una tarjeta de advertencia específica del Centro de seguridad con extras de intent específicos. Estos extras no están destinados a ser utilizados por terceros, por lo que son parte de SafetyCenterManager, que es parte de @SystemApi. Solo las apps del sistema pueden acceder a estos elementos adicionales.

Elementos adicionales de intent que redireccionan una tarjeta de advertencia específica:

  • EXTRA_SAFETY_SOURCE_ID
    • Valor de cadena: android.safetycenter.extra.SAFETY_SOURCE_ID
    • Tipo de cadena: Especifica el ID de la fuente de seguridad de la tarjeta de advertencia asociada.
    • Obligatorio para que funcione el redireccionamiento al problema
  • EXTRA_SAFETY_SOURCE_ISSUE_ID
    • Valor de cadena: android.safetycenter.extra.SAFETY_SOURCE_ISSUE_ID
    • Tipo de cadena: Especifica el ID de la tarjeta de advertencia.
    • Obligatorio para que funcione el redireccionamiento al problema
  • EXTRA_SAFETY_SOURCE_USER_HANDLE
    • Valor de cadena: android.safetycenter.extra.SAFETY_SOURCE_USER_HANDLE
    • Tipo de UserHandle: Especifica UserHandle para la tarjeta de advertencia asociada.
    • Opcional (la configuración predeterminada es el usuario actual)

El siguiente fragmento de código se puede usar desde una instancia de Activity para abrir la pantalla del Centro de seguridad y detectar un problema específico:

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);

Redirecciona a una subpágina específica (a partir de Android 14)

En Android 14 o versiones posteriores, la página del Centro de seguridad se divide en varias subpáginas que representan los diferentes SafetySourcesGroup (en Android 13, esto se muestra como entradas que se pueden contraer).

Es posible redireccionar a una subpágina específica con este intent adicional:

  • EXTRA_SAFETY_SOURCES_GROUP_ID
    • Valor de cadena: android.safetycenter.extra.SAFETY_SOURCES_GROUP_ID
    • Tipo de cadena: Especifica el ID de SafetySourcesGroup.
    • Obligatorio para que funcione el redireccionamiento a la subpágina

El siguiente fragmento de código se puede usar desde una instancia de Activity para abrir la pantalla del Centro de seguridad en una subpágina específica:

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

startActivity(openSafetyCenterIntent);

Cómo usar las APIs de origen del Centro de seguridad

Las APIs de origen del Centro de seguridad están disponibles usando SafetyCenterManager (que es un @SystemApi). El código para la plataforma de la API está disponible en Code Search. El código de implementación de las APIs está disponible en Code Search.

Permisos

Solo las apps del sistema incluidas en la lista de entidades permitidas pueden acceder a las APIs de origen de Safety Center con los permisos que se indican a continuación. Para obtener información adicional, consulta Lista de permisos privilegiados.

  • READ_SAFETY_CENTER_STATUS
    • signature|privileged
    • Se usa para la API de SafetyCenterManager#isSafetyCenterEnabled() (no es necesaria para las fuentes del Centro de seguridad, solo necesita el permiso SEND_SAFETY_CENTER_UPDATE)
    • Lo usan las apps del sistema que verifican si el Centro de seguridad está habilitado
    • Solo se otorga a las apps del sistema incluidas en la lista de entidades permitidas
  • SEND_SAFETY_CENTER_UPDATE
    • internal|privileged
    • Se usa para la API habilitada y la API de Safety Sources.
    • Solo lo usan las fuentes de seguridad
    • Solo se otorga a las apps del sistema incluidas en la lista de entidades permitidas

Estos permisos tienen privilegios y solo puedes adquirirlos si los agregas al archivo relevante, por ejemplo, al archivo com.android.settings.xml de la app de Configuración y al archivo AndroidManifest.xml de la app. Consulta protectionLevel para obtener más información sobre el modelo de permisos.

Obtén SafetyCenterManager

SafetyCenterManager es una clase @SystemApi a la que se puede acceder desde las apps del sistema a partir de Android 13. Esta llamada muestra cómo obtener 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;
}

Verifica si el Centro de seguridad está habilitado

Esta llamada verifica si el Centro de seguridad está habilitado. La llamada requiere el permiso READ_SAFETY_CENTER_STATUS o SEND_SAFETY_CENTER_UPDATE:

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

Proporciona datos

Los datos de origen de Safety Center con el String sourceId determinado se proporcionan a Safety Center con el objeto SafetySourceData, que representa una entrada de la IU y una lista de problemas (tarjetas de advertencia). La entrada de la IU y las tarjetas de advertencia pueden tener diferentes niveles de gravedad especificados en la clase SafetySourceData:

  • SEVERITY_LEVEL_UNSPECIFIED
    • No se especificó la gravedad
    • Color: Gris o transparente (según el SafetySourcesGroup de la entrada)
    • Se usa para datos dinámicos que se presentan como una entrada estática en la IU o para mostrar una entrada no especificada.
    • No se debe usar para tarjetas de advertencia
  • SEVERITY_LEVEL_INFORMATION
    • Información básica o sugerencia menor
    • Color: Verde
  • SEVERITY_LEVEL_RECOMMENDATION
    • Recomendación de que el usuario debe tomar medidas con respecto a este problema, ya que podría ponerlo en riesgo
    • Color: Amarillo
  • SEVERITY_LEVEL_CRITICAL_WARNING
    • Advertencia importante que indica que el usuario debe tomar medidas con respecto a este problema, ya que representa un riesgo
    • Color: Rojo

SafetySourceData

El objeto SafetySourceData consta de una entrada de IU, invariantes y tarjetas de advertencia.

  • Instancia de SafetySourceStatus opcional (entrada de la IU)
  • Lista de instancias de SafetySourceIssue (tarjetas de advertencia)
  • Elementos adicionales opcionales de Bundle (a partir de la versión 14)
  • Invariantes:
    • La lista SafetySourceIssue debe incluir problemas con identificadores únicos.
    • La instancia de SafetySourceIssue no debe ser más importante que SafetySourceStatus si hay una (a menos que SafetySourceStatus sea SEVERITY_LEVEL_UNSPECIFIED, en cuyo caso se permiten problemas de SEVERITY_LEVEL_INFORMATION).
    • Se deben cumplir los requisitos adicionales impuestos por la configuración de la API, por ejemplo, si la fuente solo es un problema, no debe proporcionar una instancia de SafetySourceStatus.

SafetySourceStatus

  • Título CharSequence obligatorio
  • Resumen obligatorio de CharSequence
  • Nivel de gravedad obligatorio
  • Instancia opcional PendingIntent para redireccionar al usuario a la página correcta (la configuración predeterminada usa intentAction de la configuración, si corresponde)
  • IconAction opcional (se muestra como un ícono lateral en la entrada) que consta de lo siguiente:
    • Es el tipo de ícono obligatorio, que debe ser uno de los siguientes:
      • ICON_TYPE_GEAR: Se muestra como un engranaje junto a la entrada de la IU.
      • ICON_TYPE_INFO: Se muestra como un ícono de información junto a la entrada de la IU.
    • Se requiere PendingIntent para redireccionar al usuario a otra página.
  • Es un valor booleano enabled opcional que permite marcar la entrada de la IU como inhabilitada, de modo que no se pueda hacer clic en ella (el valor predeterminado es true).
  • Invarianzas:
    • Las instancias de PendingIntent deben abrir una instancia de Activity.
    • Si la entrada está inhabilitada, se debe designar como SEVERITY_LEVEL_UNSPECIFIED.
    • Son requisitos adicionales que impone la configuración de la API.

SafetySourceIssue

  • Es el identificador String único obligatorio.
  • Título CharSequence obligatorio
  • Subtítulo CharSequence opcional
  • Resumen obligatorio de CharSequence
  • Nivel de gravedad obligatorio
  • Categoría del problema opcional, que debe ser una de las siguientes:
    • ISSUE_CATEGORY_DEVICE: El problema afecta al dispositivo del usuario.
    • ISSUE_CATEGORY_ACCOUNT: El problema afecta a las cuentas del usuario.
    • ISSUE_CATEGORY_GENERAL: El problema afecta la seguridad general del usuario. Es el valor predeterminado.
    • ISSUE_CATEGORY_DATA (A partir de Android 14): El problema afecta los datos del usuario.
    • ISSUE_CATEGORY_PASSWORDS (a partir de Android 14): El problema afecta las contraseñas del usuario.
    • ISSUE_CATEGORY_PERSONAL_SAFETY (A partir de Android 14): El problema afecta la seguridad personal del usuario.
  • Lista de elementos Action que el usuario puede tomar para este problema. Cada instancia de Action está compuesta por lo siguiente:
    • Es el identificador String único obligatorio.
    • Etiqueta CharSequence obligatoria
    • Es obligatorio usar PendingIntent para redireccionar al usuario a otra página o procesar la acción directamente desde la pantalla del Centro de seguridad.
    • Booleano opcional para especificar si este problema se puede resolver directamente desde la pantalla de SafetyCenter (el valor predeterminado es false)
    • Mensaje de éxito opcional CharSequence que se mostrará al usuario cuando el problema se resuelva correctamente directamente desde la pantalla del Centro de seguridad
  • PendingIntent opcional al que se llama cuando el usuario descarta el problema (la opción predeterminada es que no se llame a nada)
  • Es un identificador obligatorio del tipo de problema String; es similar al identificador del problema, pero no tiene que ser único y se usa para el registro
  • String opcional para el ID de anulación de duplicación. Esto permite publicar el mismo SafetySourceIssue desde diferentes fuentes y mostrarlo solo una vez en la IU, siempre que tengan el mismo deduplicationGroup (a partir de Android 14). Si no se especifica, el problema nunca se anula la duplicación.
  • CharSequence opcional para el título de atribución. Es un texto que muestra dónde se originó la tarjeta de advertencia (a partir de Android 14). Si no se especifica, se usa el título de la SafetySourcesGroup.
  • Acción opcional del problema (a partir de Android 14), que debe ser una de las siguientes:
    • ISSUE_ACTIONABILITY_MANUAL: El usuario debe resolver este problema manualmente. Es el valor predeterminado.
    • ISSUE_ACTIONABILITY_TIP: Este problema es solo una sugerencia y es posible que no requiera ninguna entrada del usuario.
    • ISSUE_ACTIONABILITY_AUTOMATIC: Este problema ya se solucionó y es posible que no requiera ninguna entrada del usuario.
  • Comportamiento de notificación opcional (a partir de Android 14), que debe ser uno de los siguientes:
    • NOTIFICATION_BEHAVIOR_UNSPECIFIED: El Centro de seguridad decidirá si se necesita una notificación para la tarjeta de advertencia. Es el valor predeterminado.
    • NOTIFICATION_BEHAVIOR_NEVER: No se publica ninguna notificación.
    • NOTIFICATION_BEHAVIOR_DELAYED: Se publica una notificación poco tiempo después de que se informa el problema por primera vez.
    • NOTIFICATION_BEHAVIOR_IMMEDIATELY: Se publica una notificación en cuanto se informa el problema.
  • Notification opcional para mostrar una notificación personalizada con la tarjeta de advertencia (a partir de Android 14) Si no se especifica, Notification se deriva de la tarjeta de advertencia. Se compone de lo siguiente:
    • Título CharSequence obligatorio
    • Resumen obligatorio de CharSequence
    • Lista de elementos Action que el usuario puede tomar para esta notificación
  • Invariantes:
    • La lista de instancias de Action debe estar compuesta por acciones con identificadores únicos.
    • La lista de instancias de Action debe contener uno o dos elementos Action. Si la capacidad de acción no es ISSUE_ACTIONABILITY_MANUAL, no se permite no tener Action.
    • El PendingIntent de OnDismiss no debe abrir una instancia de Activity.
    • Requisitos adicionales impuestos por la configuración de la API

Se proporcionan datos sobre ciertos eventos al Centro de seguridad, por lo que es necesario especificar qué hizo que la fuente le proporcionara a SafetySourceData una instancia de SafetyEvent.

SafetyEvent

  • Tipo obligatorio, que debe ser uno de los siguientes:
    • SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED: El estado de la fuente cambió.
    • SAFETY_EVENT_TYPE_REFRESH_REQUESTED: Respuesta a una señal de actualización o búsqueda del Centro de seguridad. Usa esta opción en lugar de SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED para que el Centro de seguridad pueda hacer un seguimiento de la solicitud de actualización o nueva búsqueda.
    • SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED: Solucionamos SafetySourceIssue.Action directamente desde la pantalla de Safety Center. Usa esto en lugar de SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED para que Safety Center pueda hacer un seguimiento de la resolución de SafetySourceIssue.Action.
    • SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED: Intentamos resolver SafetySourceIssue.Action directamente desde la pantalla del Centro de seguridad, pero no pudimos hacerlo. Usa esto en lugar de SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED para que el Centro de seguridad pueda hacer un seguimiento de que SafetySourceIssue.Action falló.
    • SAFETY_EVENT_TYPE_DEVICE_LOCALE_CHANGED: Cambió el idioma del dispositivo, por lo que actualizaremos el texto de los datos proporcionados. Para ello, se permite usar SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED.
    • SAFETY_EVENT_TYPE_DEVICE_REBOOTED: Proporcionamos estos datos como parte de un inicio inicial, ya que los datos del Centro de seguridad no persisten durante los reinicios. Se permite usar SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED para esto.
  • Es el identificador String opcional para el ID de transmisión actualizado.
  • Es el identificador String opcional para la instancia SafetySourceIssue que se resuelve.
  • Es el identificador String opcional para la instancia SafetySourceIssue.Action que se resuelve.
  • Invarianzas:
    • Se debe proporcionar el ID de transmisión de actualización si el tipo es SAFETY_EVENT_TYPE_REFRESH_REQUESTED.
    • Si el tipo es SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED o SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED, se deben proporcionar los IDs de problema y de acción.

A continuación, se muestra un ejemplo de cómo una fuente puede proporcionar datos a Safety Center (en este caso, proporciona una entrada con una sola tarjeta de advertencia):

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);

Cómo obtener los últimos datos proporcionados

Puedes obtener los últimos datos proporcionados al Centro de seguridad de una fuente que pertenece a tu app. Puedes usar esto para mostrar algo en tu propia IU, verificar si los datos deben actualizarse antes de realizar una operación costosa o proporcionar la misma instancia de SafetySourceData al Centro de seguridad con algunos cambios o con una nueva instancia de SafetyEvent. También es útil para pruebas.

Usa este código para obtener los últimos datos proporcionados al Centro de seguridad:

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

Informar un error

Si no puedes recopilar datos de SafetySourceData, informa el error al Centro de seguridad, que cambia la entrada a gris, borra los datos almacenados en caché y proporciona un mensaje similar a No se pudo verificar la configuración. También puedes informar un error si una instancia de SafetySourceIssue.Action no se resuelve, en cuyo caso no se borran los datos almacenados en caché ni se cambia la entrada de la IU, pero se muestra un mensaje al usuario para informarle que se produjo un error.

Puedes proporcionar el error mediante SafetySourceErrorDetails, que se compone de lo siguiente:

  • SafetySourceErrorDetails: Instancia de SafetyEvent obligatoria:
// 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);

Cómo responder a una solicitud de actualización o análisis

Puedes recibir una señal del Centro de seguridad para proporcionar datos nuevos. Responder a una solicitud de actualización o nuevo análisis garantiza que el usuario vea el estado actual cuando abra el Centro de seguridad y cuando presione el botón de análisis.

Para ello, se recibe una transmisión con la siguiente acción:

  • ACTION_REFRESH_SAFETY_SOURCES
    • Valor de cadena: android.safetycenter.action.REFRESH_SAFETY_SOURCES
    • Se activa cuando el Centro de seguridad envía una solicitud para actualizar los datos de la fuente de seguridad de una app determinada.
    • Intent protegido que solo puede enviar el sistema
    • Se envía a todas las fuentes de seguridad en el archivo de configuración como un intent explícito y requiere el permiso SEND_SAFETY_CENTER_UPDATE.

Como parte de esta transmisión, se proporcionan los siguientes extras:

  • EXTRA_REFRESH_SAFETY_SOURCE_IDS
    • Valor de cadena: android.safetycenter.extra.REFRESH_SAFETY_SOURCE_IDS
    • El tipo de array de cadenas (String[]) representa los IDs de origen que se deben actualizar para la app determinada.
  • EXTRA_REFRESH_SAFETY_SOURCES_REQUEST_TYPE

    • Valor de cadena: android.safetycenter.extra.REFRESH_SAFETY_SOURCES_REQUEST_TYPE
    • Tipo de número entero, representa un tipo de solicitud @IntDef
    • El modo debe ser uno de los siguientes:
      • EXTRA_REFRESH_REQUEST_TYPE_GET_DATA: Solicita a la fuente que proporcione datos con relativa rapidez, por lo general, cuando el usuario abre la página.
      • EXTRA_REFRESH_REQUEST_TYPE_FETCH_FRESH_DATA: Solicita a la fuente que proporcione datos lo más actualizados posible, por lo general, cuando el usuario presiona el botón de volver a analizar.
  • EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID

    • Valor de cadena: android.safetycenter.extra.REFRESH_SAFETY_SOURCES_BROADCAST_ID
    • Es un tipo de cadena que representa un identificador único para la actualización solicitada.

Para obtener un indicador del Centro de seguridad, implementa una instancia de BroadcastReceiver. La transmisión se envía con un BroadcastOptions especial que permite que el receptor inicie un servicio en primer plano.

BroadcastReceiver responde a una solicitud de actualización:

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;
  }
}

La misma instancia de BroadcastReceiver del ejemplo anterior se declara en 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>

Lo ideal es que una fuente del Centro de seguridad se implemente de tal manera que llame a SafetyCenterManager cuando cambien sus datos. Por motivos de estado del sistema, te recomendamos que respondas solo a la señal de nuevo análisis (cuando el usuario presiona el botón de análisis) y no cuando abre el Centro de seguridad. Si se requiere esta funcionalidad, se debe configurar el campo refreshOnPageOpenAllowed="true" en el archivo de configuración para que la fuente reciba la transmisión que se entrega en estos casos.

Responde al Centro de seguridad cuando esté habilitado o inhabilitado

Puedes responder cuando el Centro de seguridad está habilitado o inhabilitado con esta acción de intent:

  • ACTION_SAFETY_CENTER_ENABLED_CHANGED
    • Valor de cadena: android.safetycenter.action.SAFETY_CENTER_ENABLED_CHANGED
    • Se activa cuando el Centro de seguridad está habilitado o inhabilitado mientras el dispositivo está en ejecución.
    • No se llama durante el inicio (usa ACTION_BOOT_COMPLETED para eso).
    • Intent protegido que solo puede enviar el sistema
    • Se envía a todas las fuentes de seguridad en el archivo de configuración como un intent explícito y requiere el permiso SEND_SAFETY_CENTER_UPDATE.
    • Se envía como un intent implícito que requiere el permiso READ_SAFETY_CENTER_STATUS.

Esta acción de intent es útil para habilitar o inhabilitar funciones relacionadas con el Centro de seguridad en el dispositivo.

Implementa acciones de resolución

Una acción de resolución es una instancia de SafetySourceIssue.Action que un usuario puede resolver directamente desde la pantalla de Safety Center. El usuario presiona un botón de acción, y se activa la instancia PendingIntent en SafetySourceIssue.Action enviada por la fuente de seguridad, lo que resuelve el problema en segundo plano y notifica al Centro de seguridad cuando se completa.

Para implementar acciones de resolución, la fuente del Centro de seguridad puede usar un servicio si se espera que la operación tome un tiempo (PendingIntent.getService) o un receptor de emisión (PendingIntent.getBroadcast).

Usa este código para enviar un problema resuelto a Safety Center:

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 resuelve la acción:

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).
  }
}

La misma instancia de BroadcastReceiver en el ejemplo anterior se declara en 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>

Cómo responder a los rechazos de problemas

Puedes especificar una instancia PendingIntent que se pueda activar cuando se descarta una instancia SafetySourceIssue. Safety Center controla los siguientes rechazos de problemas:

  • Si una fuente genera un problema, el usuario puede descartarlo en la pantalla del Centro de seguridad presionando el botón para descartar (un botón X en la tarjeta de advertencia).
  • Cuando un usuario descarta un problema, si este persiste, no se mostrará nuevamente en la IU.
  • Los rechazos persistentes en un disco permanecen durante los reinicios del dispositivo.
  • Si la fuente de Safety Center deja de mostrar un problema y, luego, vuelve a mostrarlo más adelante, el problema reaparecerá. El objetivo es permitir situaciones en las que un usuario ve una advertencia, la descarta y, luego, toma medidas que deberían solucionar el problema, pero que el usuario vuelve a hacer algo que causa un problema similar. En este punto, debería volver a aparecer la tarjeta de advertencia.
  • Las tarjetas de advertencia amarillas y rojas vuelven a aparecer cada 180 días, a menos que el usuario las haya descartado varias veces.

La fuente no debería necesitar comportamientos adicionales, a menos que se cumplan las siguientes condiciones:

  • La fuente intenta implementar este comportamiento de manera diferente, por ejemplo, nunca vuelve a mostrar el problema.
  • La fuente intenta usar esto como una devolución de llamada, por ejemplo, para registrar la información.

Proporciona datos para múltiples usuarios/perfiles

La API de SafetyCenterManager se puede usar con usuarios y perfiles. Para obtener más información, consulta Cómo compilar apps con reconocimiento de multiusuario. El objeto Context que proporciona SafetyCenterManager está asociado con una instancia de UserHandle, por lo que la instancia de SafetyCenterManager que se muestra interactúa con el Centro de seguridad de esa instancia de UserHandle. De forma predeterminada, Context está asociado con el usuario en ejecución, pero es posible crear una instancia para otro usuario si la app tiene los permisos INTERACT_ACROSS_USERS y INTERACT_ACROSS_USERS_FULL. En este ejemplo, se muestra cómo realizar una llamada entre usuarios o perfiles:

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

Cada usuario del dispositivo puede tener varios perfiles administrados. Safety Center proporciona datos diferentes para cada usuario, pero combina los datos de todos los perfiles administrados asociados con un usuario determinado.

Cuando se establece profile="all_profiles" para la fuente en el archivo de configuración, ocurre lo siguiente:

  • Hay una entrada de IU para el usuario (perfil superior) y todos sus perfiles administrados asociados (que usan instancias de titleForWork).
  • El indicador de actualización o nuevo análisis se envía para el perfil superior y todos los perfiles administrados asociados. El receptor asociado se inicia para cada perfil y puede proporcionar los datos asociados directamente a SafetyCenterManager sin tener que realizar una llamada entre perfiles, a menos que el receptor o la app sean singleUser.

  • Se espera que la fuente proporcione datos para el usuario y todos sus perfiles administrados. Los datos de cada entrada de la IU pueden ser diferentes según el perfil.

Prueba

Puedes acceder a ShadowSafetyCenterManager y usarlo en una prueba de 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);
}

Puedes escribir más pruebas de extremo a extremo (E2E), pero eso está fuera del alcance de esta guía. Para obtener más información sobre cómo escribir estas pruebas E2E, consulta Pruebas CTS (CtsSafetyCenterTestCases)

APIs internas y de prueba

Las APIs internas y de prueba son para uso interno, por lo que no se describen en detalle en esta guía. Sin embargo, es posible que ampliemos algunas APIs internas en el futuro para permitir que los OEM compilen su propia IU a partir de ella, y actualizaremos esta guía para proporcionar orientación sobre cómo usarlas.

Permisos

  • MANAGE_SAFETY_CENTER
    • internal|installer|role
    • Se usa para las APIs internas de Safety Center
    • Solo se otorga a PermissionController y shell.

App de Configuración

Redirección del Centro de seguridad

De forma predeterminada, se puede acceder al Centro de seguridad a través de la app de Configuración con una entrada nueva de Seguridad y privacidad. Si usas una app de Configuración diferente o la modificaste, es posible que debas personalizar la forma en que se accede al Centro de seguridad.

Cuando Safety Center está habilitado, sucede lo siguiente:

  • La entrada heredada de Privacidad es un código oculto.
  • La entrada heredada de Seguridad es un código oculto.
  • Se agregó una nueva entrada Seguridad y privacidad código
  • Nueva entrada de Seguridad y privacidad redirecciona al código del Centro de seguridad
  • Las acciones de intent android.settings.PRIVACY_SETTINGS y android.settings.SECURITY_SETTINGS se redireccionan al Centro de seguridad abierto (código: seguridad, privacidad).

Páginas de seguridad y privacidad avanzadas

La app de Configuración contiene parámetros de configuración adicionales en los títulos Más parámetros de configuración de seguridad y Más parámetros de configuración de privacidad, disponibles en el Centro de seguridad:

Fuentes de seguridad

El Centro de seguridad se integra en un conjunto específico de fuentes de seguridad que proporciona la app de Configuración:

  • Una fuente de seguridad de la pantalla de bloqueo verifica que esta se haya configurado con una contraseña (o algún otro tipo de seguridad) para garantizar que la información privada del usuario esté protegida del acceso externo.
  • Una fuente de seguridad biométrica (oculta de forma predeterminada) se integra con un sensor facial o de huella dactilar.

Se puede acceder al código fuente de estas fuentes de Safety Center a través de la búsqueda de código de Android. Si no se modifica la app de Configuración (no se realizan cambios en el nombre del paquete, el código fuente ni el código fuente relacionado con una pantalla de bloqueo y datos biométricos), esta integración debería funcionar de inmediato. De lo contrario, es posible que se requieran algunas modificaciones, como cambiar el archivo de configuración para cambiar el nombre del paquete de la app de Configuración y las fuentes que se integran con el Centro de seguridad, además de la integración. Para obtener más información, consulta Cómo actualizar el archivo de configuración y la configuración de integración.

Información acerca de PendingIntent

Si dependes de la integración existente del Centro de seguridad de la app de Configuración en Android 14 o versiones posteriores, se corrigió el error que se describe a continuación. No es necesario leer esta sección en este caso.

Cuando sepas con certeza que el error no existe, establece un valor de configuración de recursos booleanos XML en la app de Configuración config_isSafetyCenterLockScreenPendingIntentFixed en true para desactivar la solución alternativa en el Centro de seguridad.

Solución alternativa de PendingIntent

Este error se debe a que la configuración usa los extras de instancia de Intent para determinar qué fragmento abrir. Como Intent#equals no tiene en cuenta los extras de la instancia de Intent, la instancia de PendingIntent para el ícono de menú de ajustes y la entrada se consideran iguales y navegan a la misma IU (aunque están destinadas a navegar a una IU diferente). Este problema se corrigió en una versión de QPR diferenciando las instancias de PendingIntent por código de solicitud. Como alternativa, esto se podría diferenciar con Intent#setId.

Fuentes de seguridad internas

Algunas fuentes del Centro de seguridad son internas y se implementan en la app del sistema de PermissionController, dentro del módulo PermissionController. Estas fuentes se comportan como fuentes normales de Safety Center y no reciben ningún tratamiento especial. El código de estas fuentes está disponible a través de la búsqueda de código de Android.

Estos son principalmente indicadores de privacidad, por ejemplo:

  • Accesibilidad
  • Revocar automáticamente las apps en desuso
  • Acceso a la ubicación
  • Agente de escucha de notificaciones
  • Información sobre la política de trabajo