Interagir com a Central de segurança

Redirecionar para a Central de segurança

Qualquer app pode abrir a Central de segurança usando a ação android.content.Intent.ACTION_SAFETY_CENTER (valor de string android.intent.action.SAFETY_CENTER).

Para abrir a Central de segurança, faça uma chamada em uma instância Activity:

Intent openSafetyCenterIntent = new Intent(Intent.ACTION_SAFETY_CENTER);

startActivity(openSafetyCenterIntent);

Redirecionar para um problema específico

Também é possível redirecionar para um card de alerta específico da Central de segurança usando extras de intents específicas. Esses extras não são destinados a terceiros, portanto, fazem parte de SafetyCenterManager, que faz parte de @SystemApi. Somente apps do sistema podem acessar esses extras.

Extras de intent que redirecionam um card de aviso específico:

  • EXTRA_SAFETY_SOURCE_ID
    • Valor da string: android.safetycenter.extra.SAFETY_SOURCE_ID
    • String type: especifica o ID da origem de segurança do card de aviso associado
    • Obrigatório para que o redirecionamento funcione
  • EXTRA_SAFETY_SOURCE_ISSUE_ID
    • Valor da string: android.safetycenter.extra.SAFETY_SOURCE_ISSUE_ID
    • String type: especifica o ID do card de aviso
    • Obrigatório para que o redirecionamento funcione
  • EXTRA_SAFETY_SOURCE_USER_HANDLE
    • Valor da string: android.safetycenter.extra.SAFETY_SOURCE_USER_HANDLE
    • Tipo UserHandle: especifica UserHandle para o card de aviso associado
    • Opcional (o padrão é o usuário atual)

O snippet de código abaixo pode ser usado em uma instância Activity para abrir a tela da Central de segurança para um 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);

Redirecionar para uma subpágina específica (a partir do Android 14)

No Android 14 ou versões mais recentes, a página da Central de segurança é dividida em várias subpáginas que representam os diferentes SafetySourcesGroup. No Android 13, isso é mostrado como entradas rebatíveis.

É possível redirecionar para uma subpágina específica usando este extra de intent:

  • EXTRA_SAFETY_SOURCES_GROUP_ID
    • Valor da string: android.safetycenter.extra.SAFETY_SOURCES_GROUP_ID
    • String type: especifica o ID do SafetySourcesGroup
    • Obrigatório para que o redirecionamento para a subpágina funcione

O snippet de código abaixo pode ser usado em uma instância de Activity para abrir a tela da Central de segurança em uma subpágina específica:

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

startActivity(openSafetyCenterIntent);

Usar as APIs de origem do Safety Center

As APIs de origem do Safety Center estão disponíveis usando SafetyCenterManager (que é um @SystemApi). O código para a superfície da API está disponível em Pesquisa de código. O código de implementação das APIs está disponível no Code Search.

Permissões

As APIs de origem do Safety Center só podem ser acessadas por apps do sistema na lista de permissões usando as permissões listadas abaixo. Para mais informações, consulte Lista de permissões privilegiadas.

  • READ_SAFETY_CENTER_STATUS
    • signature|privileged
    • Usado para a API SafetyCenterManager#isSafetyCenterEnabled() (não necessário para fontes do Safety Center, elas só precisam da permissão SEND_SAFETY_CENTER_UPDATE)
    • Usado por apps do sistema que verificam se o Safety Center está ativado
    • Concedida apenas a apps do sistema na lista de permissões
  • SEND_SAFETY_CENTER_UPDATE
    • internal|privileged
    • Usado para a API ativada e a API Safety Sources
    • Usado apenas por fontes de segurança
    • Concedido apenas a apps do sistema que estão na lista de permissões

Essas permissões são privilegiadas e só podem ser adquiridas adicionando-as ao arquivo relevante, por exemplo, o com.android.settings.xml do app Configurações e ao arquivo AndroidManifest.xml do app. Consulte protectionLevel para mais informações sobre o modelo de permissão.

Acessar o SafetyCenterManager

SafetyCenterManager é uma classe @SystemApi que pode ser acessada por apps do sistema a partir do Android 13. Esta chamada demonstra como receber o 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;
}

Verificar se o Safety Center está ativado

Essa chamada verifica se o Safety Center está ativado. A chamada requer a permissão READ_SAFETY_CENTER_STATUS ou SEND_SAFETY_CENTER_UPDATE:

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

Fornecer dados

Os dados de origem do Safety Center com o String sourceId fornecido são fornecidos ao Safety Center com o objeto SafetySourceData, que representa uma entrada da interface e uma lista de problemas (cards de aviso). A entrada da interface e os cartões de aviso podem ter níveis de gravidade diferentes especificados na classe SafetySourceData:

  • SEVERITY_LEVEL_UNSPECIFIED
    • Nenhuma gravidade especificada
    • Cor: cinza ou transparente (dependendo do SafetySourcesGroup da entrada)
    • Usado para dados dinâmicos que se apresentam como uma entrada estática na interface ou para mostrar uma entrada não especificada
    • Não pode ser usado para cards de aviso
  • SEVERITY_LEVEL_INFORMATION
    • Informações básicas ou sugestões menores
    • Cor: verde
  • SEVERITY_LEVEL_RECOMMENDATION
    • Recomendação de que o usuário tome medidas em relação a esse problema, já que ele pode colocar o usuário em risco
    • Cor: amarelo
  • SEVERITY_LEVEL_CRITICAL_WARNING
    • Aviso crítico de que o usuário precisa tomar medidas em relação a esse problema, já que ele representa um risco
    • Cor: vermelho

SafetySourceData

O objeto SafetySourceData é composto por uma entrada de interface, cartões de aviso e invariantes.

  • Instância SafetySourceStatus opcional (entrada da interface)
  • Lista de SafetySourceIssue instâncias (cards de aviso)
  • Extras do Bundle opcionais (a partir do 14)
  • Invariantes:
    • A lista SafetySourceIssue precisa ser composta por problemas com identificadores exclusivos.
    • A instância SafetySourceIssue não pode ser maior do que SafetySourceStatus se houver uma (a menos que SafetySourceStatus seja SEVERITY_LEVEL_UNSPECIFIED. Nesse caso, os problemas com SEVERITY_LEVEL_INFORMATION são permitidos).
    • Os requisitos adicionais impostos pela configuração da API precisam ser atendidos. Por exemplo, se a origem for somente de problemas, ela não poderá fornecer uma instância de SafetySourceStatus.

SafetySourceStatus

  • Título CharSequence obrigatório
  • Resumo de CharSequence obrigatório
  • Nível de gravidade necessário
  • Instância opcional PendingIntent para redirecionar o usuário para a página certa (o padrão usa intentAction da configuração, se houver)
  • IconAction opcional (exibido como um ícone lateral na entrada) composto por:
    • Tipo de ícone obrigatório, que precisa ser um dos seguintes:
      • ICON_TYPE_GEAR: mostrado como uma engrenagem ao lado da entrada da interface
      • ICON_TYPE_INFO: mostrado como um ícone de informação ao lado da entrada da interface
    • Necessário PendingIntent para redirecionar o usuário a outra página
  • Valor booleano opcional enabled que permite marcar a entrada da interface como desativada, para que ela não seja clicável (o padrão é true).
  • Invariantes:
    • As instâncias de PendingIntent precisam abrir uma instância de Activity.
    • Se a entrada estiver desativada, ela precisa ser designada como SEVERITY_LEVEL_UNSPECIFIED.
    • Requisitos adicionais impostos pela configuração da API.

SafetySourceIssue

  • Identificador String exclusivo obrigatório
  • Título CharSequence obrigatório
  • Subtítulo CharSequence opcional
  • Resumo de CharSequence obrigatório
  • Nível de gravidade necessário
  • A categoria do problema é opcional, que precisa ser uma das seguintes:
    • ISSUE_CATEGORY_DEVICE: o problema afeta o dispositivo do usuário.
    • ISSUE_CATEGORY_ACCOUNT: o problema afeta as contas do usuário.
    • ISSUE_CATEGORY_GENERAL: o problema afeta a segurança geral do usuário. Esse é o padrão.
    • ISSUE_CATEGORY_DATA (a partir do Android 14): o problema afeta os dados do usuário.
    • ISSUE_CATEGORY_PASSWORDS (do Android 14 em diante): o problema afeta as senhas do usuário.
    • ISSUE_CATEGORY_PERSONAL_SAFETY (a partir do Android 14): o problema afeta a segurança pessoal do usuário.
  • Lista de elementos Action que o usuário pode usar para esse problema. Cada instância de Action é composta por:
    • Identificador String exclusivo obrigatório
    • Rótulo CharSequence obrigatório
    • É necessário PendingIntent para redirecionar o usuário para outra página ou processar a ação diretamente da tela da Central de segurança.
    • Booleano opcional para especificar se esse problema pode ser resolvido diretamente na tela do Centro de segurança (o padrão é false).
    • Mensagem de sucesso CharSequence opcional, que será exibida ao usuário quando o problema for resolvido diretamente na tela do Centro de segurança.
  • PendingIntent opcional chamado quando o usuário dispensa o problema (o padrão é que nada é chamado)
  • Identificador de tipo de problema String obrigatório. É semelhante ao identificador de problema, mas não precisa ser exclusivo e é usado para registro.
  • String opcional para o id de eliminação de duplicação. Isso permite postar o mesmo SafetySourceIssue de diferentes fontes e mostrar apenas uma vez na interface, desde que tenham o mesmo deduplicationGroup (a partir do Android 14). Se não for especificado, o problema nunca será eliminado
  • CharSequence opcional para o título de atribuição. É um texto que mostra a origem do card de aviso (a partir do Android 14). Se não for especificado, será usado o título do SafetySourcesGroup
  • Ação de problema opcional (a partir do Android 14), que precisa ser uma destas:
    • ISSUE_ACTIONABILITY_MANUAL: o usuário precisa resolver esse problema manualmente. Esse é o padrão.
    • ISSUE_ACTIONABILITY_TIP: esse problema é apenas uma dica e pode não exigir nenhuma entrada do usuário.
    • ISSUE_ACTIONABILITY_AUTOMATIC: o problema já foi resolvido e talvez não exija nenhuma entrada do usuário.
  • Comportamento de notificação opcional (a partir do Android 14), que precisa ser um destes:
    • NOTIFICATION_BEHAVIOR_UNSPECIFIED: a Central de segurança decide se uma notificação é necessária para o card de aviso. Esse é o padrão.
    • NOTIFICATION_BEHAVIOR_NEVER: nenhuma notificação foi postada.
    • NOTIFICATION_BEHAVIOR_DELAYED: uma notificação é postada algum tempo depois que o problema é relatado pela primeira vez.
    • NOTIFICATION_BEHAVIOR_IMMEDIATELY: uma notificação será postada assim que o problema for relatado.
  • Notification opcional para mostrar uma notificação personalizada com o card de aviso (a partir do Android 14). Se não for especificado, o Notification será derivado do cartão de aviso. Composto por:
    • Título CharSequence obrigatório
    • Resumo de CharSequence obrigatório
    • Lista de elementos Action que o usuário pode usar para essa notificação
  • Invariantes:
    • A lista de instâncias Action precisa ser composta por ações com identificadores exclusivos.
    • A lista de instâncias de Action precisa conter um ou dois elementos Action. Se a capacidade de ação não for ISSUE_ACTIONABILITY_MANUAL, é permitido ter Action zero.
    • O PendingIntent OnDismiss não pode abrir uma instância Activity.
    • Requisitos adicionais impostos pela configuração da API

Os dados são fornecidos ao Safety Center em determinados eventos. Por isso, é necessário especificar o que fez com que a origem fornecesse SafetySourceData com uma instância SafetyEvent.

SafetyEvent

  • O tipo obrigatório, que precisa ser um dos seguintes:
    • SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED: o estado da origem mudou.
    • SAFETY_EVENT_TYPE_REFRESH_REQUESTED: responde a um sinal de atualização/nova verificação da Central de segurança. Use essa opção em vez de SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED para que a Central de segurança possa rastrear a solicitação de atualização/nova verificação.
    • SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED: resolvemos SafetySourceIssue.Action diretamente na tela da Central de segurança. Use isso em vez de SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED para que a Central de segurança possa acompanhar a resolução de SafetySourceIssue.Action.
    • SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED: tentamos resolver SafetySourceIssue.Action diretamente na tela da Central de segurança, mas não foi possível. Use essa opção em vez de SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED para que a Central de segurança possa monitorar a falha de SafetySourceIssue.Action.
    • SAFETY_EVENT_TYPE_DEVICE_LOCALE_CHANGED: o idioma do dispositivo mudou, então estamos atualizando o texto dos dados fornecidos. É permitido usar SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED para isso.
    • SAFETY_EVENT_TYPE_DEVICE_REBOOTED: fornecemos esses dados como parte de uma inicialização inicial, porque os dados do Safety Center não são mantidos em inicializações. É permitido usar SAFETY_EVENT_TYPE_SOURCE_STATE_CHANGED para isso.
  • Identificador String opcional para o ID de transmissão de atualização.
  • Identificador String opcional para a instância SafetySourceIssue sendo resolvida.
  • Identificador String opcional para a instância SafetySourceIssue.Action que está sendo resolvida.
  • Invariantes:
    • O ID de transmissão de atualização precisa ser fornecido se o tipo for SAFETY_EVENT_TYPE_REFRESH_REQUESTED.
    • Os IDs de problema e ação precisam ser fornecidos se o tipo for SAFETY_EVENT_TYPE_RESOLVING_ACTION_SUCCEEDED ou SAFETY_EVENT_TYPE_RESOLVING_ACTION_FAILED.

Confira abaixo um exemplo de como uma fonte pode fornecer dados à Central de segurança. Nesse caso, ela fornece uma entrada com um único card de aviso:

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

Receber os últimos dados fornecidos

É possível receber os últimos dados fornecidos ao Safety Center para uma origem que pertence ao app. Você pode usar isso para mostrar algo na sua própria interface, verificar se os dados precisam ser atualizados antes de realizar uma operação de alto custo ou fornecer a mesma instância SafetySourceData ao Safety Center com algumas mudanças ou com uma nova instância SafetyEvent. Isso também é útil para testes.

Use este código para receber os últimos dados enviados ao Safety Center:

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

Informar um erro

Se você não conseguir coletar dados de SafetySourceData, informe o erro ao Centro de segurança, que vai mudar a entrada para cinza, limpar os dados em cache e mostrar uma mensagem como Não foi possível verificar a configuração. Também é possível informar um erro se uma instância de SafetySourceIssue.Action não for resolvida. Nesse caso, os dados em cache não são limpos e a entrada da interface não é alterada, mas uma mensagem é exibida para o usuário informando que algo deu errado.

É possível fornecer o erro usando SafetySourceErrorDetails, que é composto por:

  • SafetySourceErrorDetails: instância SafetyEvent obrigatória:
// 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);

Responder a uma solicitação de atualização ou nova verificação

Você pode receber um sinal da Central de segurança para informar novos dados. Responder a uma solicitação de atualização ou nova verificação garante que o usuário veja o status atual ao abrir o Centro de segurança e ao tocar no botão de verificação.

Isso é feito recebendo uma transmissão com a seguinte ação:

  • ACTION_REFRESH_SAFETY_SOURCES
    • Valor da string: android.safetycenter.action.REFRESH_SAFETY_SOURCES
    • Acionado quando a Central de segurança está enviando uma solicitação para atualizar os dados da fonte de segurança de um determinado app
    • Intenção protegida que só pode ser enviada pelo sistema
    • Enviada a todas as fontes de segurança no arquivo de configuração como uma intent explícita e requer a permissão SEND_SAFETY_CENTER_UPDATE

Os seguintes extras são fornecidos como parte desta transmissão:

  • EXTRA_REFRESH_SAFETY_SOURCE_IDS
    • Valor da string: android.safetycenter.extra.REFRESH_SAFETY_SOURCE_IDS
    • O tipo de matriz de string (String[]) representa os IDs de origem a serem atualizados para o app especificado.
  • EXTRA_REFRESH_SAFETY_SOURCES_REQUEST_TYPE

    • Valor da string: android.safetycenter.extra.REFRESH_SAFETY_SOURCES_REQUEST_TYPE
    • Tipo de número inteiro, representa um tipo de solicitação @IntDef
    • Precisa ser um dos seguintes:
      • EXTRA_REFRESH_REQUEST_TYPE_GET_DATA: solicita que a fonte forneça dados de maneira relativamente rápida, normalmente quando o usuário abre a página
      • EXTRA_REFRESH_REQUEST_TYPE_FETCH_FRESH_DATA: solicita que a origem forneça dados o mais recentes possível, normalmente quando o usuário pressiona o botão de nova verificação.
  • EXTRA_REFRESH_SAFETY_SOURCES_BROADCAST_ID

    • Valor da string: android.safetycenter.extra.REFRESH_SAFETY_SOURCES_BROADCAST_ID
    • Tipo de string, representa um identificador exclusivo para a atualização solicitada

Para receber um sinal da Central de Segurança, implemente uma instância de BroadcastReceiver. A transmissão é enviada com BroadcastOptions especial, que permite que o receptor inicie um serviço em primeiro plano.

BroadcastReceiver responde a uma solicitação de atualização:

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

A mesma instância de BroadcastReceiver no exemplo acima é declarada em 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>

O ideal é que uma fonte do Safety Center seja implementada de modo que chame SafetyCenterManager quando os dados mudarem. Por motivos de integridade do sistema, recomendamos responder apenas ao sinal de nova verificação (quando o usuário toca no botão de verificação) e não quando o usuário abre o Centro de segurança. Se essa funcionalidade for necessária, o campo refreshOnPageOpenAllowed="true" no arquivo de configuração precisa ser definido para que a origem receba a transmissão entregue nesses casos.

Responder ao Safety Center quando ele estiver ativado ou desativado

Você pode responder quando o Safety Center for ativado ou desativado usando esta ação de intent:

  • ACTION_SAFETY_CENTER_ENABLED_CHANGED
    • Valor da string: android.safetycenter.action.SAFETY_CENTER_ENABLED_CHANGED
    • Acionado quando o Safety Center é ativado ou desativado enquanto o dispositivo está em execução.
    • Não é chamado na inicialização (use ACTION_BOOT_COMPLETED para isso)
    • Intent protegida que só pode ser enviada pelo sistema
    • Enviada para todas as fontes de segurança no arquivo de configuração como uma intent explícita, requer a permissão SEND_SAFETY_CENTER_UPDATE
    • Enviada como uma intent implícita que requer a permissão READ_SAFETY_CENTER_STATUS

Essa ação da intent é útil para ativar ou desativar recursos relacionados à Central de segurança do dispositivo.

Implementar ações de resolução

Uma ação de resolução é uma instância de SafetySourceIssue.Action que um usuário pode resolver diretamente na tela da Central de segurança. O usuário toca em um botão de ação e a instância PendingIntent em SafetySourceIssue.Action enviada pela fonte de segurança é acionada, o que resolve o problema em segundo plano e notifica a Central de segurança quando ele é concluído.

Para implementar ações de resolução, a origem da Central de segurança pode usar um serviço se a operação for esperada para levar algum tempo (PendingIntent.getService) ou um broadcast receiver (PendingIntent.getBroadcast).

Use este código para enviar um problema resolvido à Central de segurança:

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 resolve a ação:

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

A mesma instância de BroadcastReceiver no exemplo acima é declarada em 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>

Responder a dispensas de problemas

É possível especificar uma instância de PendingIntent que pode ser acionada quando uma instância de SafetySourceIssue é dispensada. A Central de segurança lida com estas dispensas de problemas:

  • Se uma fonte envia um problema, o usuário pode tocar no botão "Dispensar" (um botão X no card de aviso) para dispensá-lo na tela da Central de segurança.
  • Quando um usuário dispensa um problema, se ele persistir, ele não vai aparecer na interface novamente.
  • As dispensas persistentes em um disco permanecem durante as reinicializações do dispositivo.
  • Se a origem da Central de segurança parar de fornecer um problema e disponibilizá-lo novamente mais tarde, ele aparecerá novamente. Isso permite situações em que um usuário recebe um aviso, o dispensa e depois toma uma ação que deveria aliviar o problema, mas o usuário faz algo novamente que causa um problema semelhante. Nesse momento, o card de aviso vai reaparecer.
  • Os cards de aviso amarelos e vermelhos reaparecem a cada 180 dias, a menos que o usuário os tenha dispensado várias vezes.

Não é necessário que a fonte tenha outros comportamentos, a menos que:

  • A origem tenta implementar esse comportamento de maneira diferente, por exemplo, nunca retornar o problema.
  • A origem tenta usar isso como um callback, por exemplo, para registrar as informações.

Fornecer dados para vários usuários/perfis

A API SafetyCenterManager pode ser usada por usuários e perfis. Para mais informações, consulte Criar apps compatíveis com vários usuários . O objeto Context que fornece SafetyCenterManager é associado a uma instância UserHandle. Portanto, a instância SafetyCenterManager retornada interage com o Centro de segurança para essa instância UserHandle. Por padrão, Context está associado ao usuário em execução, mas é possível criar uma instância para outro usuário se o app tiver as permissões INTERACT_ACROSS_USERS e INTERACT_ACROSS_USERS_FULL. Este exemplo mostra como fazer uma chamada entre usuários/perfis:

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 usuário no dispositivo pode ter vários perfis gerenciados. O Safety Center oferece dados diferentes para cada usuário, mas mescla os dados de todos os perfis gerenciados associados a um determinado usuário.

Quando profile="all_profiles" é definido para a origem no arquivo de configuração, o seguinte ocorre:

  • Há uma UIentry para o usuário (pai do perfil) e todos os perfis gerenciados associados (que usam instâncias titleForWork).
  • O sinal de atualização ou nova verificação é enviado para o perfil pai e todos os perfis gerenciados associados. O receiver associado é iniciado para cada perfil e pode fornecer os dados associados diretamente para SafetyCenterManager sem precisar fazer uma chamada entre perfis, a menos que o receiver ou o app seja singleUser.

  • A fonte precisa fornecer dados para o usuário e todos os perfis gerenciados. Os dados de cada entrada da interface podem ser diferentes, dependendo do perfil.

Teste

Você pode acessar ShadowSafetyCenterManager e usá-lo em um teste 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);
}

Você pode escrever mais testes de ponta a ponta (E2E), mas isso está fora do escopo deste guia. Para mais informações sobre como escrever esses testes E2E, consulte Testes do CTS (CtsSafetyCenterTestCases).

Testar e APIs internas

As APIs internas e de teste são para uso interno e, por isso, não são descritas em detalhes neste guia. No entanto, podemos ampliar algumas APIs internas no futuro para permitir que OEMs criem a própria interface. Vamos atualizar este guia com orientações sobre como usá-las.

Permissões

  • MANAGE_SAFETY_CENTER
    • internal|installer|role
    • Usado para as APIs internas do Safety Center
    • Concedido apenas ao PermissionController e ao shell

App Config.

Redirecionamento da Central de segurança

Por padrão, a Central de segurança é acessada pelo app Configurações com uma nova entrada Segurança e privacidade. Se você usa outro app Configurações ou se modificou ele, pode ser necessário personalizar a forma como a Central de segurança é acessada.

Quando a Central de segurança está ativada:

  • A entrada legada de Privacidade está oculta no código.
  • A entrada legada de segurança é um código oculto.
  • Uma nova entrada de Segurança e privacidade foi adicionada código
  • A nova entrada de Segurança e privacidade redireciona para o código da Central de segurança
  • As ações de intent android.settings.PRIVACY_SETTINGS e android.settings.SECURITY_SETTINGS são redirecionadas para abrir a Central de segurança (código: security, privacy).

Páginas avançadas de segurança e privacidade

O app Configurações contém outras configurações nos títulos Mais configurações de segurança e Mais configurações de privacidade, disponíveis na Central de segurança:

Fontes de segurança

A Central de segurança se integra a um conjunto específico de fontes de segurança fornecidas pelo app Configurações:

  • Uma fonte de segurança da tela de bloqueio verifica se uma tela de bloqueio está configurada com um código de acesso (ou outra segurança) para garantir que as informações particulares do usuário fiquem protegidas contra acesso externo.
  • Uma fonte de segurança de biometria (oculta por padrão) é mostrada para integração com um sensor facial ou de impressão digital.

O código-fonte dessas fontes da Central de segurança pode ser acessado pela Pesquisa de código do Android. Se o app Configurações não for modificado (se não houver mudanças no nome do pacote, no código-fonte ou no código-fonte que lida com uma tela de bloqueio e biometria), essa integração vai funcionar sem precisar de ajustes. Caso contrário, algumas modificações podem ser necessárias, como alterar o arquivo de configuração para mudar o nome do pacote do app Configurações e as fontes que se integram ao Safety Center, bem como a integração. Para mais informações, consulte Atualizar o arquivo de configuração e as configurações de integração.

Sobre a PendingIntent

Se você usa a integração do app Configurações com o Safety Center no Android 14 ou versões mais recentes, o bug descrito abaixo foi corrigido. A leitura desta seção não é necessária neste caso.

Quando tiver certeza de que o bug não existe, defina um valor de configuração de recurso booleano XML no app Configurações config_isSafetyCenterLockScreenPendingIntentFixed como true para desativar a solução alternativa na Central de segurança.

Solução alternativa para PendingIntent

Esse bug é causado pelas configurações que usam extras de instância Intent para determinar qual fragmento abrir. Como Intent#equals não considera os extras da instância Intent, a instância PendingIntent do ícone do menu de engrenagem e a entrada são consideradas iguais e navegar para a mesma interface, mesmo que elas sejam destinadas a navegar para uma interface diferente. Esse problema foi corrigido em uma versão do QPR, diferenciando as instâncias de PendingIntent pelo código de solicitação. Como alternativa, isso pode ser diferenciado usando Intent#setId.

Fontes internas de segurança

Algumas fontes da Central de segurança são internas e são implementadas no app do sistema PermissionController dentro do módulo PermissionController. Essas fontes se comportam como as fontes normais do Safety Center e não recebem tratamento especial. O código dessas origens está disponível na pesquisa de código do Android.

Esses são principalmente indicadores de privacidade, por exemplo:

  • Acessibilidade
  • Revogar automaticamente permissões de apps não usados
  • Acesso ao local
  • Ouvinte de notificações
  • Informações da política de trabalho