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, roubo 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 principal desse framework restringe o início de atividades em segundo plano.

Restrições de inicialização de atividades em segundo plano

Uma inicialização 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. Esta é uma inicialização de atividade em segundo plano (BAL, na sigla em inglês). Embora existam casos de uso legítimos, como quando um app de despertador é iniciado, as BALs sem restrições prejudicam a experiência do usuário e criam vulnerabilidades de segurança.

Por que eles são restritos?

Desde o Android 10 (nível 29 da API), 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 ao reduzir abusos comuns, incluindo:

  • Sequestro 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, sequestrando a sessão.
  • Phishing e falsificação de identidade: um app em segundo plano inicia uma atividade que se passa por outro app (por exemplo, uma tela de login falsa para um app legítimo) para roubar credenciais de usuário. Isso geralmente é feito por um ataque "Activity Sandwich", 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 obscurecida 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 de forma ilegítima as métricas de usuários ativos por dia.

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

Se o app precisar realizar uma tarefa de longa duração em segundo plano que o usuário precisa conhecer, como tocar música ou monitorar um treino, use 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, como botões de reproduzir/pausar 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 só passando para opções mais intrusivas quando necessário, você cria uma experiência melhor e mais previsível para os usuários.

Quando o início de atividades em segundo plano é permitido (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 por uma PendingIntent enviada pelo sistema (por exemplo, um toque em uma 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 é vinculado por um serviço que recebeu permissão para iniciar atividades em segundo plano.
  • O início é feito pelo app de início do dispositivo, como quando um usuário toca no ícone de um app ou interage com um widget.
  • O lançamento é de uma parte principal do sistema operacional que precisa ser executada em todos os momentos, como o serviço de telefonia que inicia a tela de chamada recebida.

Novas proteções e ativações obrigatórias

Para aumentar ainda mais a segurança, o Android introduziu regras mais rígidas que exigem ativações explícitas para apps que usam PendingIntent e IntentSender para iniciar atividades. Uma inicialização só é permitida se o app que criou a PendingIntent ou o app que a envia ativar a concessão de privilégios de inicialização em segundo plano.

Na maioria dos casos, o app que envia o PendingIntent precisa ativar a opção, 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 versões mais recentes, ele não concede mais privilégios de BAL por padrão ao enviar um PendingIntent. Se você não ativar explicitamente, 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 ativar a concessão de 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 restrito e seguro. Ele concede permissão somente se o app de envio estiver visível na tela no momento em que o PendingIntent é enviado. Isso garante mais fortemente que o início da atividade é resultado direto da interação de um usuário com seu app.

Tabela de intents pendentes
Figura 1: fluxo de decisão para inicializações 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 de conteúdo precisam ativar a PendingIntent

Quando o app é direcionado ao Android 15 (nível 35 da API) ou versões mais recentes, um app que cria um PendingIntent não concede mais privilégios de início em segundo plano por padrão. Para permitir que o remetente use os privilégios de BAL do seu app, você precisa ativar essa opção 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())

Início com 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 no 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 ou mais recente), o uso de IntentSender.sendIntent() exige uma ativação no 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 da BAL

Tabela de intents pendentes
Figura 2: processo de inicialização segura de uma atividade usando um PendingIntent

Este diagrama ilustra o processo de iniciar uma atividade com segurança usando uma PendingIntent. Um lançamento bem-sucedido depende de uma cadeia de privilégios válida em que pelo menos um dos apps participantes concede 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 a versão 35 ou mais recente, o criador precisará delegar explicitamente os privilégios da 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. Lançamento e contribuição (App B: The Sender)
    1. Mais tarde, o app remetente inicia o lançamento chamando PendingIntent.send().
    2. Se o SDK de destino for a versão 34 ou mais recente, o remetente precisará contribuir explicitamente com os próprios privilégios usando setPendingIntentBackgroundActivityStartMode() se 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 inicialização e realiza uma verificação de segurança.
    2. Ele avalia duas condições:
    3. O criador de conteúdo delegou os privilégios, E o app Creator atualmente atende a uma das exceções gerais da BAL?
    4. O remetente contribuiu com os privilégios dele? E o app do remetente atualmente atende a uma das exceções gerais da 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 de sucesso.
    2. BLOCKED: se nenhuma condição for atendida, o sistema vai bloquear o lançamento. 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 mensagem "Background activity launch blocked!" no Logcat, que os desenvolvedores precisam verificar para depuração.

Prevenção contra invasão de tarefas

Para evitar ataques de sequestro na tarefa (como o "Activity Sandwich"), 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 pertencente ao mesmo aplicativo (ou seja, com o mesmo UID) da atividade atual no topo da 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 para o primeiro plano.

Ativação de proteções na tarefa para desenvolvedores

Esse comportamento pode ser ativado a partir do SDK de destino 37. É necessário ativar explicitamente. Ele foi projetado para evitar o sequestro de tarefas (ou Activity Sandwiching), em que um app malicioso pode iniciar uma atividade na tarefa do seu app para se passar por ele e roubar dados do usuário.

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ê tiver ativado esse recurso para seu app, mas precisar permitir que uma atividade específica e confiável seja iniciada por outros apps, crie uma exceção direcionada. Para eximir uma única atividade dessa proteção, chame setAllowCrossUidActivitySwitchFromBelow(true) no método onCreate() dessa atividade. Isso permite que uma 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 principais mensagens de registro

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. Este é o remetente.
  • callingPackage: o app que criou a PendingIntent. Este é o criador.

Modo restrito

A partir do Android 16, o desenvolvedor de apps pode ativar o modo estrito para receber notificações quando o início de uma atividade é bloqueado (ou corre o risco de ser bloqueado quando o SDK de destino do app é aumentado).

Exemplo de código para ativar 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());
     )
 }