Android protegge gli utenti dalle app dannose e offre un'esperienza UI affidabile. Il framework Activity Security comprende regole e limitazioni della piattaforma. Queste regole e limitazioni impediscono interruzioni indesiderate dell'UI, il dirottamento delle attività e altre minacce alla sicurezza. Queste minacce riguardano quando e come i componenti dell'app vengono visualizzati sullo schermo. Un componente chiave di questo framework limita l'avvio delle attività in background.
Limitazioni all'avvio delle attività in background
Un avvio di attività in background (BAL) si verifica quando un'app non in primo piano, senza attività visibili o un PendingIntent ricevuto da un'altra app tenta di avviare una nuova attività. Questo è un avvio di attività in background (BAL).
Sebbene esistano casi d'uso legittimi, ad esempio quando viene avviata un'app sveglia, gli avvii di attività in background senza limitazioni comportano una scarsa esperienza utente e creano vulnerabilità alla sicurezza.
Perché sono limitati?
A partire da Android 10 (livello API 29), la piattaforma ha imposto limitazioni su quando le app possono avviare attività in background. Queste protezioni contribuiscono a prevenire comportamenti dannosi delle app e a migliorare l'esperienza utente mitigando gli abusi comuni, tra cui:
- Dirottamento dell'UI e annunci popup: un'app in background avvia in modo imprevisto un'attività (spesso un annuncio) sopra l'app con cui l'utente sta interagendo, dirottando la sua sessione.
- Phishing e furto d'identità: un'app in background avvia un'attività che imita un'altra app (ad esempio, una schermata di accesso falsa per un'app legittima) per rubare le credenziali dell'utente. Questo risultato viene spesso ottenuto tramite un attacco "Activity Sandwich", in cui un'attività dannosa viene inserita nello stack delle attività di un'app legittima.
- Tapjacking: un'app in background mostra un'attività trasparente o oscurata sopra un'altra app per intercettare i tocchi dell'utente e indurlo a intraprendere azioni non intenzionali.
- Riattivazione dell'app: un componente in background di un'app riattiva i componenti in primo piano di un'altra app per aumentare in modo illegittimo le metriche degli utenti attivi giornalieri.
Servizi in primo piano (per attività in corso)
Se la tua app deve eseguire un'attività a lunga esecuzione in background di cui l'utente deve essere a conoscenza, ad esempio riprodurre musica o monitorare un allenamento, devi utilizzare un servizio in primo piano. Un servizio in primo piano deve mostrare una notifica persistente che non può essere ignorata dall'utente. Questa notifica può fornire controlli interattivi (ad esempio, pulsanti di riproduzione/pausa per un'app musicale). In questo modo, l'utente è informato e ha il controllo, ma non viene interrotto da un'attività a schermo intero.
Seguendo questa gerarchia, iniziando con le notifiche standard e passando a opzioni più intrusive solo quando necessario, crei un'esperienza migliore e più prevedibile per i tuoi utenti.
Quando sono consentiti gli avvii di attività in background (eccezioni)
Un'app può avviare un'attività in background se è soddisfatta una delle seguenti condizioni:
- L'app ha una finestra visibile, ad esempio un'attività in primo piano.
- L'app è l'Input Method Editor (IME) corrente.
- L'attività viene avviata da un
PendingIntentinviato dal sistema (ad esempio, da un tocco di una notifica). - L'app ha l'autorizzazione
SYSTEM_ALERT_WINDOWconcessa dall'utente. - All'app è stata concessa l'autorizzazione
START_ACTIVITIES_FROM_BACKGROUND. - L'app è vincolata da un servizio a cui è stata concessa l'autorizzazione per avviare attività in background.
- L'avvio viene avviato dall'app di avvio del dispositivo, ad esempio quando un utente tocca l'icona di un'app o interagisce con un widget.
- L'avvio avviene da una parte fondamentale del sistema operativo che deve essere sempre in esecuzione, ad esempio il servizio di telefonia che avvia la schermata della chiamata in arrivo.
Nuovo rafforzamento e attivazioni obbligatorie
Per migliorare ulteriormente la sicurezza, Android ha introdotto regole più rigide che richiedono l'attivazione esplicita per le app che utilizzano PendingIntent e IntentSender per avviare le attività. Un avvio è consentito solo se l'app che ha creato PendingIntent o l'app che lo invia attiva la concessione dei privilegi di avvio in background.
Nella maggior parte dei casi, l'app che invia PendingIntent deve essere quella che attiva l'opzione, in quanto in genere è l'app con cui l'utente interagisce direttamente (ad esempio, toccando un pulsante).
I mittenti devono attivare PendingIntent
Quando la tua app ha come target Android 14 (livello API 34) o versioni successive, non concede più i privilegi di avvio di attività in background per impostazione predefinita quando invia un PendingIntent. Se non
attivi esplicitamente l'opzione, l'avvio dell'attività potrebbe essere bloccato, a meno che il creatore
di PendingIntent non abbia già concesso i propri privilegi.
Per garantire l'avvio, il mittente deve attivare la concessione dei privilegi chiamando ActivityOptions.setPendingIntentBackgroundActivityStartMode() e la modalità consigliata è ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE (aggiunta nell'SDK 36).
Questa è una modalità più rigorosa e sicura. Concede l'autorizzazione solo se l'app di invio è visibile sullo schermo nel momento in cui viene inviato PendingIntent. In questo modo, l'avvio dell'attività è più sicuramente il risultato diretto dell'interazione di un utente con la tua app.
Utilizza ActivityOptions.setPendingIntentBackgroundActivityStartMode() per concedere i privilegi.
// 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)
}
I creatori devono attivare PendingIntent
Quando la tua app ha come target Android 15 (livello API 35) o versioni successive, un'app che crea un PendingIntent non concede più i privilegi di avvio in background per impostazione predefinita. Per consentire al mittente di utilizzare i privilegi di avvio di attività in background della tua app, devi attivare esplicitamente l'opzione.
Utilizza ActivityOptions.setPendingIntentCreatorBackgroundActivityStartMode() per concedere i privilegi.
// 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())
Avvio con IntentSender
Le stesse limitazioni all'avvio di attività in background si applicano anche quando si avviano attività utilizzando un
IntentSender. Poiché un IntentSender viene ottenuto tramite
PendingIntent.getIntentSender, è soggetto a requisiti di attivazione
simili.
- A partire da Android 14 (API 34), l'utilizzo di Context.startIntentSender()
richiede l'attivazione lato mittente. Devi fornire anche il bundle
ActivityOptions.
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 partire da Android 17 (API 37 e versioni successive), l'utilizzo di IntentSender.sendIntent() richiede l'attivazione lato mittente.
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())
- Utilizzo di ActivityResultLauncher
: questa API AndroidX utilizza internamente Context.startIntentSender() ed è quindi interessata dalle limitazioni all'avvio di attività in background.
Diagramma di sequenza: limitazioni all'avvio di attività in background
Questo diagramma illustra la procedura per avviare in sicurezza un'attività utilizzando un PendingIntent. Un avvio riuscito dipende da una catena di privilegi valida in cui almeno una delle app partecipanti concede i propri privilegi e ha la possibilità di avviare un'attività in background.
- Creazione e delega (app A - il creatore)
- L'app del creatore crea
PendingIntent. - Se ha come target l'SDK 35 o versioni successive, il creatore deve delegare esplicitamente i propri privilegi di avvio di attività in background utilizzando setPendingIntentCreatorBackgroundActivityStartMode() se vuole che i suoi privilegi vengano utilizzati. Per impostazione predefinita, non vengono delegati privilegi.
PendingIntentviene quindi consegnato a un'altra app (app B).
- L'app del creatore crea
- Avvio e contributo (app B - il mittente)
- In un secondo momento, l'app del mittente avvia l'avvio chiamando
PendingIntent.send(). - Se ha come target l'SDK 34 o versioni successive, il mittente deve contribuire esplicitamente con i propri privilegi utilizzando setPendingIntentBackgroundActivityStartMode() se vuole che i suoi privilegi vengano utilizzati. Per impostazione predefinita, non vengono delegati privilegi.
- In un secondo momento, l'app del mittente avvia l'avvio chiamando
- Convalida della sicurezza del sistema Android
- Il sistema Android intercetta la richiesta di avvio ed esegue un controllo di sicurezza.
- Valuta due condizioni:
- Il creatore ha delegato i propri privilegi e l'app del creatore soddisfa attualmente una delle eccezioni generali all'avvio di attività in background?
- Il mittente ha contribuito con i propri privilegi e l'app del mittente soddisfa attualmente una delle eccezioni generali all'avvio di attività in background?
- Risultato
- CONSENTITO: se è soddisfatta almeno una delle due condizioni del passaggio 3, la catena di privilegi viene convalidata. Il sistema Android avvia l'attività di destinazione e il mittente riceve un risultato positivo.
- BLOCCATO: se nessuna delle due condizioni è soddisfatta, il sistema blocca l'avvio. L'app del mittente non riceve un valore di ritorno diretto o un'eccezione che indica l'errore. Il sistema Android registra internamente un messaggio "Avvio di attività in background bloccato" in Logcat, che gli sviluppatori devono controllare per il debug.
Prevenzione del dirottamento delle attività
Per impedire gli attacchi di dirottamento delle attività (come "Activity Sandwich"), Android 15 introduce nuove regole per le app che hanno come target il livello API 37 o superiore.
- Regola 1: all'interno di una singola attività, un'attività può essere avviata solo da un'altra attività che appartiene alla stessa applicazione (ovvero ha lo stesso UID) dell' attività più in alto corrente nell'attività.
- Regola 2: solo un'attività all'interno di un'attività in primo piano che corrisponde all'UID di quella più in alto può creare una nuova attività o portare in primo piano un'attività esistente diversa.
Attivazione da parte dello sviluppatore per le protezioni in-task
Questo comportamento può essere attivato a partire dall'SDK di destinazione 37, ma devi attivarlo esplicitamente. È progettato per impedire il dirottamento in-task (o Activity Sandwiching), in cui un'app dannosa potrebbe avviare un'attività nell'attività della tua app per impersonarla e rubare i dati dell'utente.
Attivazione delle protezioni
Per attivare ASM per la tua applicazione, imposta l'attributo android:allowCrossUidActivitySwitchFromBelow su false nel file AndroidManifest.xml. Si tratta di un'impostazione a livello di applicazione che protegge per impostazione predefinita tutte le attività della tua app.
Creazione di eccezioni per attività specifiche
Se l'hai attivato per la tua app, ma devi consentire l'avvio di un'attività specifica e attendibile da parte di altre app, puoi creare un'eccezione mirata. Per esentare una singola attività da questa protezione, chiama setAllowCrossUidActivitySwitchFromBelow(true) all'interno del metodo onCreate() di questa attività. In questo modo, l'attività può essere avviata mentre il resto dell'app rimane protetto.
Risoluzione dei problemi
Filtra Logcat per trovare i messaggi pertinenti utilizzando un'espressione regolare. Il tag ActivityTaskManager viene spesso utilizzato e il filtro per ActivityTaskManager può aiutarti a isolare i log.
Informazioni sui messaggi di log chiave
Avvio bloccato (errore): questo messaggio indica che l'avvio di un'attività è stato bloccato.
- Significato: l'avvio di un'attività è stato negato perché mancava l'attivazione PendingIntent necessaria dal mittente (con SDK di destinazione 34 o versioni successive) o dal creatore (con SDK di destinazione 35 o versioni successive).
- Azione: devi aggiornare il codice per includere l'attivazione ActivityOptions corretta.
Quando analizzi i log, controlla questi campi:
- realCallingPackage: l'app che ha inviato PendingIntent. Questo è il mittente.
- callingPackage: l'app che ha creato PendingIntent. Questo è il creatore.
Modalità StrictMode
A partire da Android 16, lo sviluppatore dell'app può attivare la modalità StrictMode per ricevere una notifica quando l'avvio di un'attività viene bloccato (o rischia di essere bloccato quando viene aumentato l'SDK di destinazione dell'app).
Esempio di codice per l'attivazione dal metodo Application.onCreate() dell'applicazione, dell'attività o di un altro componente dell'applicazione:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
StrictMode.setVmPolicy(
StrictMode.VmPolicy.Builder()
.detectBlockedBackgroundActivityLaunch()
.penaltyLog()
.build());
)
}