Segurança de atividade

O Android protege os usuários contra apps maliciosos e oferece uma experiência de interface confiável. O framework de segurança de atividades abrange regras e restrições de plataforma. Essas regras e restrições evitam interrupções indesejadas na interface, invasão de tarefas e outras ameaças à segurança. Essas ameaças se relacionam a quando e como os componentes do app aparecem na tela. Um componente essencial desse framework restringe o início de atividades em segundo plano.

Restrições de início de atividades em segundo plano

Um início de atividade em segundo plano (BAL, na sigla em inglês) ocorre quando um app que não está em primeiro plano, sem atividades visíveis ou um PendingIntent recebido de um app diferente tenta iniciar uma nova atividade. Esse é um início de atividade em segundo plano (BAL). Embora existam casos de uso legítimos, como quando um app de despertador é iniciado, os BALs irrestritos levam a uma experiência ruim do usuário e criam vulnerabilidades de segurança.

Por que eles são restritos?

Desde o Android 10 (nível da API 29), a plataforma impõe restrições quanto ao momento em que os apps podem iniciar atividades em segundo plano. Essas proteções ajudam a evitar comportamentos maliciosos de apps e melhoram a experiência do usuário, atenuando abusos comuns, incluindo:

  • Invasão de interface e anúncios pop-up: um app em segundo plano inicia inesperadamente uma atividade (geralmente um anúncio) sobre o app com que o usuário está interagindo, invadindo a sessão.
  • Phishing e falsificação de identidade: um app em segundo plano inicia uma atividade que falsifica a identidade de outro app (por exemplo, uma tela de login falsa para um app legítimo) para roubar as credenciais do usuário. Isso geralmente é feito por meio de um ataque de "sanduíche de atividades", em que uma atividade maliciosa é inserida na pilha de tarefas de um app legítimo.
  • Tapjacking: um app em segundo plano mostra uma atividade transparente ou oculta sobre outro app para interceptar os toques do usuário e enganá-lo para que ele realize ações não intencionais.
  • Despertar de apps: um componente em segundo plano de um app desperta os componentes em primeiro plano de outro app para aumentar ilegitimamente as métricas de usuários ativos diários.

Serviços em primeiro plano (para tarefas em andamento)

Se o app precisar realizar uma tarefa de longa duração em segundo plano de que o usuário precisa estar ciente, como reproduzir música ou monitorar um treino, você deve usar um serviço em primeiro plano. Um serviço em primeiro plano precisa mostrar uma notificação persistente que não pode ser dispensada pelo usuário. Essa notificação pode fornecer controles interativos (por exemplo, botões de reprodução/pausa para um app de música). Isso mantém o usuário informado e no controle, mas não o interrompe com uma atividade em tela cheia.

Ao seguir essa hierarquia, começando com notificações padrão e escalonando apenas para opções mais intrusivas quando necessário, você cria uma experiência melhor e mais previsível para os usuários.

Quando os inícios de atividades em segundo plano são permitidos (exceções)

Um app pode iniciar uma atividade em segundo plano se uma das seguintes condições for atendida:

  • O app tem uma janela visível, como uma atividade em primeiro plano.
  • O app é o editor de método de entrada (IME, na sigla em inglês) atual.
  • A atividade é iniciada em um PendingIntent enviado pelo sistema (por exemplo, em um toque de notificação).
  • O app tem a permissão SYSTEM_ALERT_WINDOW concedida pelo usuário.
  • O app recebeu a permissão START_ACTIVITIES_FROM_BACKGROUND.
  • O app está vinculado a um serviço que recebeu permissão para iniciar atividades em segundo plano.
  • O início é iniciado pelo app de inicialização do dispositivo, como quando um usuário toca no ícone do app ou interage com um widget.
  • O início é de uma parte principal do sistema operacional que precisa ser executada o tempo todo, como o serviço de telefonia que inicia a tela de chamada recebida.

Novas proteções e ativações necessárias

Para melhorar ainda mais a segurança, o Android introduziu regras mais rigorosas que exigem ativações explícitas para apps que usam PendingIntent e IntentSender para iniciar atividades. Um início só é permitido se o app que criou o PendingIntent ou o app que o envia ativar o recurso para conceder os privilégios de início em segundo plano.

Na maioria dos casos, o app que envia o PendingIntent precisa ativar o recurso, já que geralmente é o app com que o usuário está interagindo diretamente (por exemplo, tocando em um botão).

Os remetentes precisam ativar o PendingIntent

Quando o app é direcionado ao Android 14 (nível 34 da API) ou mais recente, ele não concede mais os privilégios de BAL por padrão ao enviar um PendingIntent. Se você não ativar explicitamente o recurso, o início da atividade poderá ser bloqueado, a menos que o criador do PendingIntent já tenha concedido os próprios privilégios.

Para garantir que um lançamento seja bem-sucedido, o remetente precisa aceitar para conceder os privilégios chamando ActivityOptions.setPendingIntentBackgroundActivityStartMode() e o modo recomendado é ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE (adicionado no SDK 36).

Esse é um modo mais rigoroso e seguro. Ele concede permissão apenas se o app de envio estiver visível na tela no momento em que o PendingIntent for enviado. Isso garante que o início da atividade seja um resultado direto da interação do usuário com o app.

Tabela de intents pendentes
Figura 1: fluxo de decisão para inícios de atividades em segundo plano.

Use ActivityOptions.setPendingIntentBackgroundActivityStartMode() para conceder privilégios.

// Sender Side
ActivityOptions options = ActivityOptions.makeBasic()
    .setPendingIntentBackgroundActivityStartMode(
        ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE);

try {
    myPendingIntent.send(options.toBundle());
} catch (PendingIntent.CanceledException e) {
    Log.e(TAG, "The PendingIntent was canceled", e);
}
// Sender Side
val options = ActivityOptions.makeBasic().apply {
    pendingIntentBackgroundActivityStartMode = ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE
}

try {
    myPendingIntent.send(options.toBundle())
} catch (e: PendingIntent.CanceledException) {
    Log.e(TAG, "The PendingIntent was canceled", e)
}

Os criadores precisam ativar o PendingIntent

Quando o app é direcionado ao Android 15 (nível 35 da API) ou mais recente, um app que cria um PendingIntent não concede mais os privilégios de início em segundo plano por padrão. Para permitir que o remetente use os privilégios de BAL do app, você precisa ativar o recurso explicitamente.

Use ActivityOptions.setPendingIntentCreatorBackgroundActivityStartMode() para conceder permissões.

// Creator Side
Intent intent = new Intent(context, MyActivity.class);
ActivityOptions options = ActivityOptions.makeBasic().setPendingIntentCreatorBackgroundActivityStartMode(ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);

PendingIntent pendingIntent = PendingIntent.getActivity(context, REQUEST_CODE, intent, PendingIntent.FLAG_IMMUTABLE, options.toBundle());
// Creator Side
val intent = Intent(context, MyActivity::class.java)
val options = ActivityOptions.makeBasic().apply {
    pendingIntentCreatorBackgroundActivityStartMode = ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
}

val pendingIntent = PendingIntent.getActivity(context, REQUEST_CODE, intent,
        PendingIntent.FLAG_IMMUTABLE, options.toBundle())

Como iniciar com o IntentSender

As mesmas restrições de BAL também se aplicam ao iniciar atividades usando um IntentSender. Como um IntentSender é obtido via PendingIntent.getIntentSender, ele está sujeito a requisitos de ativação semelhantes.

  • A partir do Android 14 (API 34), o uso de Context.startIntentSender() exige uma ativação do lado do remetente. Você também precisa fornecer o pacote ActivityOptions aqui.
ActivityOptions options = ActivityOptions.makeBasic()
        .setPendingIntentBackgroundActivityStartMode(
            ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);

context.startIntentSender(myIntentSender, fillInIntent, flagsMask,
        flagsValues, extraFlags, options.toBundle());
val options = ActivityOptions.makeBasic().apply {
    pendingIntentBackgroundActivityStartMode = ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
}

context.startIntentSender(myIntentSender, fillInIntent, flagsMask,
        flagsValues, extraFlags, options.toBundle())
  • A partir do Android 17 (API 37 e mais recentes), o uso de IntentSender.sendIntent() exige uma ativação do lado do remetente.
ActivityOptions options = ActivityOptions.makeBasic()
        .setPendingIntentBackgroundActivityStartMode(
            ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED);

myIntentSender.sendIntent(context, code, intent, onFinished, handler,
        requiredPermission, options.toBundle());
val options = ActivityOptions.makeBasic().apply {
    pendingIntentBackgroundActivityStartMode = ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
}

myIntentSender.sendIntent(context, code, intent, onFinished, handler,
        requiredPermission, options.toBundle())

Diagrama de sequência: restrições de BAL

Tabela de intents pendentes
Figura 2: O processo de início seguro de uma atividade usando um PendingIntent

Este diagrama ilustra o processo de início seguro de uma atividade usando um PendingIntent. Um início bem-sucedido depende de uma cadeia de privilégios válida em que pelo menos um dos apps participantes concede os privilégios e tem a capacidade de iniciar uma atividade em segundo plano.

  1. Criação e delegação (app A: o criador)
    1. O app criador cria o PendingIntent.
    2. Se o SDK de destino for 35 ou mais recente, o criador precisará delegar explicitamente os privilégios de BAL usando setPendingIntentCreatorBackgroundActivityStartMode() se quiser que os privilégios sejam usados. Por padrão, nenhum privilégio é delegado.
    3. O PendingIntent é entregue a outro app (app B).
  2. Início e contribuição (app B: o remetente)
    1. Mais tarde, o app remetente inicia o início chamando PendingIntent.send().
    2. Se o SDK de destino for 34 ou mais recente, o remetente precisará contribuir explicitamente com os próprios privilégios usando setPendingIntentBackgroundActivityStartMode() se ele quiser que os privilégios sejam usados. Por padrão, nenhum privilégio é delegado.
  3. Validação de segurança do sistema Android
    1. O sistema Android intercepta a solicitação de início e realiza uma verificação de segurança.
    2. Ele avalia duas condições:
    3. O criador delegou os privilégios E o app criador atende a uma das exceções gerais de BAL?
    4. O remetente contribuiu com os privilégios E o app remetente atende a uma das exceções gerais de BAL?
  4. Resultado
    1. PERMITIDO: se pelo menos uma das duas condições na etapa 3 for atendida, a cadeia de privilégios será validada. O sistema Android inicia a atividade de destino, e o remetente recebe um resultado bem-sucedido.
    2. BLOQUEADO: se nenhuma condição for atendida, o sistema bloqueará o início. O app remetente não recebe um valor de retorno direto ou uma exceção indicando a falha. Em vez disso, o sistema Android registra internamente uma "Background activity launch blocked!" mensagem no Logcat, que os desenvolvedores precisam verificar para depuração.

Prevenção de invasão de tarefas

Para evitar ataques de invasão de tarefas (como o "sanduíche de atividades"), o Android 15 introduz novas regras para apps direcionados ao nível 37 da API ou mais recente.

  • Regra 1: em uma única tarefa, uma atividade só pode ser iniciada por outra atividade que pertença ao mesmo aplicativo (ou seja, tenha o mesmo UID) que a atividade mais alta atual na tarefa.
  • Regra 2: somente uma atividade em uma tarefa em primeiro plano que corresponda ao UID da atividade mais alta pode criar uma nova tarefa ou trazer uma tarefa diferente e existente para o primeiro plano.

Ativação do desenvolvedor para proteções na tarefa

Esse comportamento pode ser ativado a partir do SDK de destino 37. Você precisa ativar o recurso explicitamente. Ele foi projetado para evitar invasão de tarefas (ou sanduíche de atividades), em que um app malicioso pode iniciar uma atividade na tarefa do seu app para representá-lo e roubar dados do usuário.

Como ativar proteções

Para ativar o ASM no seu aplicativo, defina o atributo android:allowCrossUidActivitySwitchFromBelow como "false" no arquivo AndroidManifest.xml. Essa é uma configuração no nível do aplicativo que protege todas as atividades no app por padrão.

Como criar exceções para atividades específicas

Se você ativou o recurso para o app, mas precisa permitir que uma atividade específica e confiável seja iniciada por outros apps, crie uma exceção direcionada. Para isentar uma única atividade dessa proteção, chame setAllowCrossUidActivitySwitchFromBelow(true) no método onCreate() dessa atividade. Isso permite que essa atividade seja iniciada enquanto o restante do app permanece protegido.

Solução de problemas

Filtre o Logcat para encontrar mensagens relevantes usando uma expressão regular. A tag ActivityTaskManager é usada com frequência, e a filtragem por ActivityTaskManager pode ajudar a isolar os registros.

Como entender as mensagens de registro principais

Início bloqueado (erro): essa mensagem indica que o início de uma atividade foi bloqueado.

Ao analisar os registros, verifique estes campos:

  • realCallingPackage: o app que enviou o PendingIntent. Esse é o remetente.
  • callingPackage: o app que criou o PendingIntent. Esse é o criador.

Modo estrito

A partir do Android 16, o desenvolvedor de apps pode ativar o modo estrito para receber uma notificação quando um início de atividade for bloqueado (ou estiver em risco de ser bloqueado quando o SDK de destino do app for aumentado).

Exemplo de código para ativar o modo estrito no início do método Application.onCreate() do aplicativo, da atividade ou de outro componente do aplicativo:

override fun onCreate(savedInstanceState: Bundle?) {
     super.onCreate(savedInstanceState)
     StrictMode.setVmPolicy(
         StrictMode.VmPolicy.Builder()
         .detectBlockedBackgroundActivityLaunch()
         .penaltyLog()
         .build());
     )
 }