Android chroni użytkowników przed złośliwymi aplikacjami i zapewnia im bezpieczne korzystanie z interfejsu. System zabezpieczeń związanych z aktywnością obejmuje reguły i ograniczenia platformy. Te reguły i ograniczenia zapobiegają niechcianym przerwom w interfejsie, przejmowaniu zadań i innym zagrożeniom bezpieczeństwa. Te zagrożenia dotyczą tego, kiedy i jak komponenty aplikacji pojawiają się na ekranie. Kluczowym elementem tego systemu jest ograniczenie rozpoczynania aktywności w tle.
Ograniczenia dotyczące uruchamiania aktywności w tle
Uruchomienie aktywności w tle (BAL) ma miejsce, gdy aplikacja, która nie działa na pierwszym planie i nie ma widocznych aktywności, lub intencja PendingIntent otrzymana z innej aplikacji próbuje rozpocząć nową aktywność. Jest to uruchomienie aktywności w tle (BAL).
Chociaż istnieją uzasadnione przypadki użycia, np. gdy uruchamia się aplikacja budzika, nieograniczone uruchamianie aktywności w tle prowadzi do pogorszenia wrażeń użytkownika i stwarza luki w zabezpieczeniach.
Dlaczego są one ograniczone?
Od Androida 10 (API na poziomie 29) platforma wprowadziła ograniczenia dotyczące tego, kiedy aplikacje mogą rozpoczynać aktywności w tle. Te zabezpieczenia pomagają zapobiegać złośliwemu zachowaniu aplikacji i poprawiają wrażenia użytkownika, ograniczając powszechne nadużycia, takie jak:
- Przejęcie interfejsu i reklamy wyskakujące: aplikacja działająca w tle nieoczekiwanie uruchamia aktywność (często reklamę) na wierzchu aplikacji, z której korzysta użytkownik, przejmując jego sesję.
- Phishing i podszywanie się: aplikacja działająca w tle uruchamia aktywność, która podszywa się pod inną aplikację (np. fałszywy ekran logowania do legalnej aplikacji), aby ukraść dane uwierzytelniające użytkownika. Często odbywa się to za pomocą ataku „Activity Sandwich”, w którym złośliwa aktywność jest wstawiana do stosu zadań legalnej aplikacji.
- Tapjacking: aplikacja działająca w tle wyświetla przezroczystą lub zasłoniętą aktywność nad inną aplikacją, aby przechwytywać dotknięcia użytkownika i nakłaniać go do wykonywania niezamierzonych działań.
- Wybudzanie aplikacji: komponent działający w tle w jednej aplikacji wybudza komponenty działające na pierwszym planie w innej aplikacji, aby nielegalnie zwiększyć liczbę dziennych aktywnych użytkowników.
Usługi działające na pierwszym planie (w przypadku zadań ciągłych)
Jeśli Twoja aplikacja musi wykonywać długotrwałe zadanie w tle, o którym użytkownik musi wiedzieć, np. odtwarzać muzykę lub śledzić trening, powinna używać usługi działającej na pierwszym planie. Usługa działająca na pierwszym planie musi wyświetlać trwałe powiadomienie, którego użytkownik nie może zamknąć. To powiadomienie może zawierać interaktywne elementy sterujące (np. przyciski odtwarzania/pauzy w aplikacji muzycznej). Dzięki temu użytkownik jest informowany i ma kontrolę, ale nie jest przerywany przez aktywność na pełnym ekranie.
Postępując zgodnie z tą hierarchią – zaczynając od standardowych powiadomień i przechodząc do bardziej natrętnych opcji tylko w razie potrzeby – zapewnisz użytkownikom lepsze i bardziej przewidywalne wrażenia.
Kiedy zezwala się na uruchamianie aktywności w tle (wyjątki)
Aplikacja może rozpocząć aktywność w tle, jeśli spełniony jest jeden z tych warunków:
- Aplikacja ma widoczne okno, np. aktywność na pierwszym planie.
- Aplikacja jest bieżącym edytorem metody wprowadzania (IME).
- Aktywność jest uruchamiana z intencji
PendingIntentwysłanej przez system (np. po kliknięciu powiadomienia). - Aplikacja ma uprawnienie
SYSTEM_ALERT_WINDOWprzyznane przez użytkownika. - Aplikacja ma uprawnienie
START_ACTIVITIES_FROM_BACKGROUND. - Aplikacja jest powiązana z usługą, która ma uprawnienia do uruchamiania aktywności w tle.
- Uruchomienie jest inicjowane przez aplikację uruchamiającą na urządzeniu, np. gdy użytkownik kliknie ikonę aplikacji lub wejdzie w interakcję z widżetem.
- Uruchomienie następuje z podstawowej części systemu operacyjnego, która musi działać przez cały czas, np. usługa telefoniczna uruchamia ekran połączenia przychodzącego.
Nowe wzmacnianie zabezpieczeń i wymagane akceptacje
Aby zwiększyć bezpieczeństwo, Android wprowadził bardziej rygorystyczne reguły wymagające wyraźnej zgody na używanie przez aplikacje intencji PendingIntent i IntentSender do uruchamiania aktywności. Uruchomienie jest dozwolone tylko wtedy, gdy aplikacja, która utworzyła intencję PendingIntent, lub aplikacja, która ją wysyła, wyrazi zgodę na przyznanie uprawnień do uruchamiania w tle.
W większości przypadków zgodę powinna wyrazić aplikacja wysyłająca intencję PendingIntent, ponieważ zwykle jest to aplikacja, z którą użytkownik bezpośrednio wchodzi w interakcję (np. klikając przycisk).
Nadawcy muszą wyrazić zgodę na intencję PendingIntent
Gdy Twoja aplikacja jest kierowana na Androida 14 (API na poziomie 34) lub nowszego, domyślnie nie przyznaje już uprawnień do uruchamiania aktywności w tle podczas wysyłania intencji PendingIntent. Jeśli nie
wyrazisz wyraźnie zgody, uruchomienie aktywności może zostać zablokowane, chyba że twórca
intencji PendingIntent przyznał już własne uprawnienia.
Aby zapewnić pomyślne uruchomienie, nadawca powinien wyrazić zgodę na przyznanie uprawnień, wywołując metodę ActivityOptions.setPendingIntentBackgroundActivityStartMode() i zalecany tryb to ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOW_IF_VISIBLE (dodany w pakiecie SDK 36).
Jest to bardziej rygorystyczny i bezpieczniejszy tryb. Przyznaje uprawnienia tylko wtedy, gdy aplikacja wysyłająca jest widoczna na ekranie w momencie wysyłania intencji PendingIntent. Dzięki temu masz większą pewność, że uruchomienie aktywności jest bezpośrednim wynikiem interakcji użytkownika z Twoją aplikacją.
Aby przyznać uprawnienia, użyj metody ActivityOptions.setPendingIntentBackgroundActivityStartMode().
// 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)
}
Twórcy muszą wyrazić zgodę na intencję PendingIntent
Gdy Twoja aplikacja jest kierowana na Androida 15 (API na poziomie 35) lub nowszego, aplikacja, która tworzy intencję PendingIntent, domyślnie nie przyznaje już uprawnień do uruchamiania w tle. Aby zezwolić nadawcy na korzystanie z uprawnień do uruchamiania aktywności w tle Twojej aplikacji, musisz wyraźnie wyrazić zgodę.
Aby przyznać uprawnienia, użyj metody ActivityOptions.setPendingIntentCreatorBackgroundActivityStartMode().
// 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())
Uruchamianie za pomocą intencji IntentSender
Te same ograniczenia dotyczące uruchamiania aktywności w tle obowiązują również w przypadku uruchamiania aktywności za pomocą intencji
IntentSender. Ponieważ intencja IntentSender jest uzyskiwana za pomocą
PendingIntent.getIntentSender, obowiązują w jej przypadku podobne wymagania dotyczące zgody.
- Od Androida 14 (API na poziomie 34) używanie metody Context.startIntentSender()
wymaga zgody po stronie nadawcy. Musisz też podać pakiet
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())
- Od Androida 17 (API na poziomie 37) używanie metody IntentSender.sendIntent() wymaga zgody po stronie nadawcy.
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())
- Używanie klasy ActivityResultLauncher<IntentSenderRequest>: ten interfejs AndroidX API wewnętrznie używa metody Context.startIntentSender(), dlatego obowiązują w jego przypadku ograniczenia dotyczące uruchamiania aktywności w tle.
Schemat sekwencji: ograniczenia dotyczące uruchamiania aktywności w tle
Ten schemat ilustruje proces bezpiecznego uruchamiania aktywności za pomocą intencji PendingIntent. Pomyślne uruchomienie zależy od prawidłowego łańcucha uprawnień , w którym co najmniej jedna z uczestniczących aplikacji zarówno przyznaje swoje uprawnienia, jak i ma możliwość uruchamiania aktywności w tle.
- Tworzenie i delegowanie (aplikacja A – twórca)
- Aplikacja twórcy tworzy intencję
PendingIntent. - Jeśli aplikacja jest kierowana na pakiet SDK 35 lub nowszy, twórca musi wyraźnie delegować swoje uprawnienia do uruchamiania aktywności w tle za pomocą metody setPendingIntentCreatorBackgroundActivityStartMode() , jeśli chce, aby jego uprawnienia były używane. Domyślnie nie są delegowane żadne uprawnienia.
- Intencja
PendingIntentjest następnie dostarczana do innej aplikacji (aplikacji B).
- Aplikacja twórcy tworzy intencję
- Uruchamianie i przekazywanie uprawnień (aplikacja B – nadawca)
- W późniejszym czasie aplikacja nadawcy inicjuje uruchomienie, wywołując metodę
PendingIntent.send(). - Jeśli aplikacja jest kierowana na pakiet SDK 34 lub nowszy, nadawca musi wyraźnie przekazać swoje uprawnienia za pomocą metody setPendingIntentBackgroundActivityStartMode(), jeśli chce, aby jego uprawnienia były używane. Domyślnie nie są delegowane żadne uprawnienia.
- W późniejszym czasie aplikacja nadawcy inicjuje uruchomienie, wywołując metodę
- Weryfikacja zabezpieczeń systemu Android
- System Android przechwytuje żądanie uruchomienia i przeprowadza kontrolę bezpieczeństwa.
- Sprawdza 2 warunki:
- Czy twórca delegował swoje uprawnienia ORAZ czy aplikacja twórcy spełnia obecnie jeden z ogólnych wyjątków dotyczących uruchamiania aktywności w tle?
- Czy nadawca przekazał swoje uprawnienia ORAZ czy aplikacja nadawcy spełnia obecnie jeden z ogólnych wyjątków dotyczących uruchamiania aktywności w tle?
- Wynik
- DOZWOLONE: jeśli co najmniej jeden z 2 warunków w kroku 3 jest spełniony, łańcuch uprawnień jest zweryfikowany. System Android uruchamia docelową aktywność, a nadawca otrzymuje wynik powodzenia.
- ZABLOKOWANE: jeśli żaden z warunków nie jest spełniony, system blokuje uruchomienie. Aplikacja nadawcy nie otrzymuje bezpośredniej wartości zwracanej ani wyjątku wskazującego na niepowodzenie. Zamiast tego system Android wewnętrznie rejestruje w Logcat komunikat "Background activity launch blocked!", który deweloperzy muszą sprawdzić w celu debugowania.
Zapobieganie przejmowaniu zadań
Aby zapobiegać atakom polegającym na przejmowaniu zadań (np. „Activity Sandwich”), Android 15 wprowadza nowe reguły dla aplikacji kierowanych na API na poziomie 37 lub nowszym.
- Reguła 1: w ramach jednego zadania aktywność może być uruchamiana tylko przez inną aktywność, która należy do tej samej aplikacji (czyli ma ten sam UID) co bieżąca aktywność na wierzchu zadania.
- Reguła 2: tylko aktywność w zadaniu na pierwszym planie, która ma ten sam UID co aktywność na wierzchu, może utworzyć nowe zadanie lub przenieść inne, istniejące zadanie na pierwszy plan.
Zgoda dewelopera na zabezpieczenia w ramach zadania
To zachowanie można włączyć od pakietu SDK 37. Musisz wyraźnie wyrazić zgodę na jego włączenie. Ma ono zapobiegać przejmowaniu zadań (lub Activity Sandwiching), w którym złośliwa aplikacja może uruchomić aktywność w zadaniu Twojej aplikacji, aby się pod nią podszyć i ukraść dane użytkownika.
Włączanie zabezpieczeń
Aby wyrazić zgodę i włączyć ASM w swojej aplikacji, ustaw w pliku AndroidManifest.xml atrybut android:allowCrossUidActivitySwitchFromBelow na wartość false. Jest to ustawienie na poziomie aplikacji, które domyślnie chroni wszystkie aktywności w Twojej aplikacji.
Tworzenie wyjątków dla określonych aktywności
Jeśli włączysz tę opcję w swojej aplikacji, ale musisz zezwolić innym aplikacjom na uruchamianie określonej, zaufanej aktywności, możesz utworzyć wyjątek. Aby wyłączyć tę ochronę dla pojedynczej aktywności, wywołaj metodę setAllowCrossUidActivitySwitchFromBelow(true) w metodzie onCreate() tej aktywności. Dzięki temu ta jedna aktywność będzie mogła być uruchamiana, a reszta aplikacji pozostanie chroniona.
Rozwiązywanie problemów
Filtruj Logcat, aby znaleźć odpowiednie wiadomości za pomocą wyrażenia regularnego. Często używany jest tag ActivityTaskManager, a filtrowanie według niego może pomóc w wyodrębnieniu logów.ActivityTaskManager
Interpretacja kluczowych komunikatów w logach
Blocked Launch (Error) (Zablokowane uruchomienie (błąd)): ten komunikat wskazuje, że uruchomienie aktywności zostało zablokowane.
- Znaczenie: uruchomienie aktywności zostało odrzucone, ponieważ w przypadku nadawcy (kierowanego na pakiet SDK 34 lub nowszy) lub twórcy (kierowanego na pakiet SDK 35 lub nowszy) brakowało niezbędnej zgody na intencję PendingIntent.
- Działanie: musisz zaktualizować kod, aby uwzględnić prawidłową zgodę na ActivityOptions.
Podczas analizowania logów sprawdź te pola:
- realCallingPackage: aplikacja, która wysłała intencję PendingIntent. Jest to nadawca.
- callingPackage: aplikacja, która utworzyła intencję PendingIntent. Jest to twórca.
Tryb ścisły
Od Androida 16 deweloper aplikacji może włączyć tryb ścisły, aby otrzymywać powiadomienia, gdy uruchomienie aktywności zostanie zablokowane (lub gdy istnieje ryzyko zablokowania po podniesieniu docelowego pakietu SDK aplikacji).
Przykładowy kod, który można włączyć na wczesnym etapie w metodzie Application.onCreate() aplikacji, aktywności lub innego komponentu aplikacji:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
StrictMode.setVmPolicy(
StrictMode.VmPolicy.Builder()
.detectBlockedBackgroundActivityLaunch()
.penaltyLog()
.build());
)
}