Zmiany w działaniu: aplikacje kierowane na Androida 14 lub nowszego

Podobnie jak w poprzednich wersjach, Android 14 wprowadza zmiany w działaniu, które mogą wpłynąć na Twoją aplikację. Podane niżej zmiany w działaniu dotyczą tylko aplikacji kierowanych na Androida 14 (poziom API 34) lub nowszego. Jeśli Twoja aplikacja jest kierowana na Androida 14 lub nowszego, musisz ją zmodyfikować, aby prawidłowo obsługiwała te zachowania (w stosownych przypadkach).

Zapoznaj się też z listą zmian w działaniu, które wpływają na wszystkie aplikacje na Androida 14, niezależnie od targetSdkVersion aplikacji.

Główna funkcja

Typy usług działających na pierwszym planie są wymagane

Jeśli Twoja aplikacja jest kierowana na Androida 14 (poziom interfejsu API 34) lub nowszego, musi określić co najmniej 1 typ takiej usługi dla każdej usługi na pierwszym planie w aplikacji. Wybierz typ tej usługi, który odzwierciedla jej zastosowanie. System oczekuje, że usługi na pierwszym planie, które mają określony typ, spełnią określony przypadek użycia.

Jeśli przypadek użycia w Twojej aplikacji nie jest powiązany z żadnym z tych typów, zdecydowanie zalecamy przeniesienie logiki za pomocą WorkManagera lub zadań przenoszenia danych inicjowanych przez użytkownika.

Egzekwowanie uprawnień BLUETOOTH_CONNECT w BluetoothAdapter

W przypadku aplikacji kierowanych na Androida 14 (poziom interfejsu API 34) lub nowszy Android 14 wymusza uprawnienie BLUETOOTH_CONNECT podczas wywoływania metody BluetoothAdapter getProfileConnectionState().

Ta metoda wymagała już uprawnienia BLUETOOTH_CONNECT, ale nie została wymuszona. Upewnij się, że Twoja aplikacja zadeklaruje w jej pliku AndroidManifest.xml właściwość BLUETOOTH_CONNECT, jak pokazujemy w tym fragmencie, i sprawdź, czy użytkownik przyznał te uprawnienia, zanim wywołasz metodę getProfileConnectionState.

<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

Aktualizacje OpenJDK 17

Android 14 kontynuuje prace nad odświeżaniem podstawowych bibliotek Androida w celu dostosowania do funkcji z najnowszych wersji OpenJDK LTS, w tym aktualizacji bibliotek oraz obsługi języka Java 17 dla deweloperów aplikacji i platform.

Kilka z tych zmian może mieć wpływ na zgodność aplikacji:

  • Zmiany w wyrażeniach regularnych: nie można teraz stosować nieprawidłowych odwołań do grup, aby lepiej zachować zgodność z semantyką OpenJDK. Mogą się pojawić nowe przypadki, w których obiekt IllegalArgumentException jest wywoływany przez klasę java.util.regex.Matcher, dlatego pamiętaj, aby przetestować aplikację pod kątem obszarów, które używają wyrażeń regularnych. Aby włączyć lub wyłączyć tę zmianę podczas testowania, przełącz flagę DISALLOW_INVALID_GROUP_REFERENCE za pomocą narzędzi platformy zgodności.
  • Obsługa identyfikatorów UUID: metoda java.util.UUID.fromString() przeprowadza teraz bardziej rygorystyczne testy podczas weryfikacji argumentu wejściowego, więc podczas deserializacji możesz zobaczyć IllegalArgumentException. Aby włączyć lub wyłączyć tę zmianę podczas testowania, przełącz flagę ENABLE_STRICT_VALIDATION za pomocą narzędzi platformy zgodności.
  • Problemy z ProGuard: w niektórych przypadkach dodanie klasy java.lang.ClassValue powoduje problem, gdy próbujesz zmniejszyć, zaciemnić lub zoptymalizować aplikację za pomocą ProGuard. Problem wynika z biblioteki Kotlin, która zmienia działanie w czasie działania w zależności od tego, czy Class.forName("java.lang.ClassValue") zwraca klasę. Jeśli Twoja aplikacja została opracowana przy użyciu starszej wersji środowiska wykonawczego, w którym nie ma dostępnej klasy java.lang.ClassValue, optymalizacje mogą spowodować usunięcie metody computeValue z klas wyodrębnionych ze źródła java.lang.ClassValue.

JobScheduler wzmacnia wywołania zwrotne i działanie sieci

Od momentu wprowadzenia JobScheduler oczekuje, że Twoja aplikacja powinna wrócić ze strony onStartJob lub onStopJob w ciągu kilku sekund. W wersjach sprzed Androida 14, jeśli zadanie jest uruchomione zbyt długo, zatrzymuje się i nie ukazuje dyskretnie. Jeśli Twoja aplikacja jest kierowana na Androida 14 (poziom interfejsu API 34) lub nowszego i przekroczy limit czasu w wątku głównym, aplikacja wywoła błąd ANR z komunikatem „Brak odpowiedzi na onStartJob” lub „Brak odpowiedzi na onStopJob”. Rozważ przejście na usługę WorkManager, która zapewnia obsługę asynchronicznego przetwarzania lub przeniesienie wszystkich ciężkich zadań do wątku w tle.

JobScheduler wymaga też zadeklarowania uprawnienia ACCESS_NETWORK_STATE w przypadku korzystania z ograniczenia setRequiredNetworkType lub setRequiredNetwork. Jeśli podczas planowania zadania Twoja aplikacja nie zadeklaruje uprawnienia ACCESS_NETWORK_STATE i jest kierowana na Androida w wersji 14 lub nowszej, spowoduje to wyświetlenie właściwości SecurityException.

prywatność

Częściowy dostęp do zdjęć i filmów

Android 14 wprowadza dostęp do wybranych zdjęć, który pozwala przyznawać aplikacjom dostęp do określonych obrazów i filmów w bibliotece, zamiast przyznawać dostęp do wszystkich multimediów danego typu.

Ta zmiana jest włączona tylko wtedy, gdy Twoja aplikacja jest kierowana na Androida 14 (poziom API 34) lub nowszego. Jeśli nie używasz jeszcze selektora zdjęć, zalecamy zaimplementowanie go w swojej aplikacji. Pozwoli to w spójny sposób wybierać obrazy i filmy, a jednocześnie zwiększy prywatność użytkowników bez konieczności wysyłania próśb o uprawnienia do przechowywania danych.

Jeśli samodzielnie zarządzasz selektorem galerii z użyciem uprawnień do przechowywania danych i chcesz zachować pełną kontrolę nad implementacją, dostosuj implementację, aby wykorzystywała nowe uprawnienie READ_MEDIA_VISUAL_USER_SELECTED. Jeśli aplikacja nie korzysta z nowych uprawnień, system uruchomi ją w trybie zgodności.

Z perspektywy użytkownika

Bezpieczne powiadomienia dotyczące intencji pełnoekranowej

Na Androidzie 11 (poziom interfejsu API 30) każda aplikacja mogła używać Notification.Builder.setFullScreenIntent do wysyłania intencji pełnoekranowych, gdy telefon jest zablokowany. Możesz automatycznie przyznać tę wartość podczas instalacji aplikacji, zadeklarując uprawnienie USE_FULL_SCREEN_INTENT w pliku AndroidManifest.

Powiadomienia intencji pełnoekranowej zaprojektowano z myślą o powiadomieniach o bardzo wysokim priorytecie, które wymagają natychmiastowej uwagi użytkownika, np. o przychodzących połączeniach telefonicznych lub ustawieniach budzika. W przypadku aplikacji kierowanych na Androida 14 (poziom interfejsu API 34) lub nowszego aplikacje, które mogą korzystać z tego uprawnienia, są ograniczone do tych, które oferują tylko wywoływanie i alarmy. Sklep Google Play cofa domyślne uprawnienia USE_FULL_SCREEN_INTENT wszystkim aplikacjom, które nie pasują do tego profilu. Ostateczny termin wprowadzenia tych zmian zasad to 31 maja 2024 r.

To uprawnienie pozostaje włączone w przypadku aplikacji zainstalowanych na telefonie, zanim użytkownik przejdzie na Androida 14. Użytkownicy mogą włączać i wyłączać to uprawnienie.

Możesz użyć nowego interfejsu API NotificationManager.canUseFullScreenIntent, aby sprawdzić, czy aplikacja ma odpowiednie uprawnienia. Jeśli nie, aplikacja może użyć nowej intencji ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT, aby otworzyć stronę ustawień, na której użytkownicy mogą przyznawać te uprawnienia.

Zabezpieczenia

Ograniczenia dotyczące intencji niejawnych i oczekujących

W przypadku aplikacji kierowanych na Androida 14 (poziom interfejsu API 34) lub nowszego Android ogranicza wysyłanie przez aplikacje niejawnych intencji do wewnętrznych komponentów aplikacji w taki sposób:

  • Intencje ogólne są dostarczane tylko do komponentów wyeksportowanych. Aplikacje muszą używać wyraźnej intencji przesłania do niewyeksportowanych komponentów lub oznaczyć komponent jako wyeksportowany.
  • Jeśli aplikacja tworzy zmienną intencję oczekującą z intencją, która nie określa komponentu ani pakietu, system zgłasza wyjątek.

Te zmiany uniemożliwiają złośliwym aplikacjom przechwytywanie niejawnych intencji, które mają być wykorzystane przez wewnętrzne komponenty aplikacji.

Oto na przykład filtr intencji, który można zadeklarować w pliku manifestu aplikacji:

<activity
    android:name=".AppActivity"
    android:exported="false">
    <intent-filter>
        <action android:name="com.example.action.APP_ACTION" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

Jeśli aplikacja próbuje uruchomić to działanie w intencji pośredniej, zostanie zgłoszony wyjątek:

Kotlin

// Throws an exception when targeting Android 14.
context.startActivity(Intent("com.example.action.APP_ACTION"))

Java

// Throws an exception when targeting Android 14.
context.startActivity(new Intent("com.example.action.APP_ACTION"));

Aby uruchomić niewyeksportowaną aktywność, aplikacja powinna użyć wyraźnej intencji:

Kotlin

// This makes the intent explicit.
val explicitIntent =
        Intent("com.example.action.APP_ACTION")
explicitIntent.apply {
    package = context.packageName
}
context.startActivity(explicitIntent)

Java

// This makes the intent explicit.
Intent explicitIntent =
        new Intent("com.example.action.APP_ACTION")
explicitIntent.setPackage(context.getPackageName());
context.startActivity(explicitIntent);

Odbiorniki transmisji zarejestrowane w środowisku wykonawczym muszą określić zachowanie eksportu

Aplikacje i usługi, które są kierowane na Androida 14 (poziom interfejsu API 34) lub nowsze i używają odbiorników zarejestrowanych na podstawie kontekstu, muszą określić flagę wskazującą, czy odbiorca powinien zostać wyeksportowany do wszystkich innych aplikacji na urządzeniu (odpowiednio RECEIVER_EXPORTED lub RECEIVER_NOT_EXPORTED). To wymaganie pomaga chronić aplikacje przed lukami w zabezpieczeniach przez wykorzystanie funkcji dla odbiorców wprowadzonych w Androidzie 13.

Wyjątek dla odbiorców, którzy odbierają tylko komunikaty systemowe

Jeśli Twoja aplikacja rejestruje odbiornik tylko w przypadku komunikatów systemowych za pomocą metod Context#registerReceiver, takich jak Context#registerReceiver(), nie powinna określać flagi podczas rejestrowania odbiorcy.

Bezpieczniejsze wczytywanie kodu dynamicznego

Jeśli Twoja aplikacja jest kierowana na Androida 14 (poziom interfejsu API 34) lub nowszego i korzysta z dynamicznego wczytywania kodu (DCL), wszystkie pliki ładowane dynamicznie muszą być oznaczone jako tylko do odczytu. W przeciwnym razie system zgłosi wyjątek. Zalecamy, aby w miarę możliwości unikać dynamicznego wczytywania kodu w aplikacjach, ponieważ znacznie zwiększa to ryzyko przejęcia aplikacji przez wstrzyknięcie kodu lub ingerencję w kod.

Jeśli musisz dynamicznie ładować kod, zastosuj poniższe podejście, aby ustawić ładowany dynamicznie plik (np. DEX, JAR lub APK) jako dostępny tylko do odczytu zaraz po jego otwarciu, a przed napisaniem jakiejkolwiek treści:

Kotlin

val jar = File("DYNAMICALLY_LOADED_FILE.jar")
val os = FileOutputStream(jar)
os.use {
    // Set the file to read-only first to prevent race conditions
    jar.setReadOnly()
    // Then write the actual file content
}
val cl = PathClassLoader(jar, parentClassLoader)

Java

File jar = new File("DYNAMICALLY_LOADED_FILE.jar");
try (FileOutputStream os = new FileOutputStream(jar)) {
    // Set the file to read-only first to prevent race conditions
    jar.setReadOnly();
    // Then write the actual file content
} catch (IOException e) { ... }
PathClassLoader cl = new PathClassLoader(jar, parentClassLoader);

Obsługa plików wczytywanych dynamicznie, które już istnieją

Aby zapobiec zgłaszaniu wyjątków w przypadku istniejących plików wczytywanych dynamicznie, zalecamy usunięcie i ponowne utworzenie plików przed ich ponownym ich dynamicznym wczytaniem w aplikacji. Podczas odtwarzania tych plików postępuj zgodnie z poprzednimi wskazówkami dotyczącymi oznaczania plików jako tylko do odczytu w czasie zapisu. Możesz też oznaczyć istniejące pliki etykietą „tylko do odczytu”, ale w tym przypadku zdecydowanie zalecamy wcześniejsze sprawdzenie integralności plików (np. przez sprawdzenie podpisu pliku pod kątem wartości zaufanej), aby lepiej chronić aplikację przed szkodliwymi działaniami.

Dodatkowe ograniczenia dotyczące rozpoczynania działań w tle

W przypadku aplikacji kierowanych na Androida 14 (poziom interfejsu API 34) lub nowszego system jeszcze bardziej ogranicza możliwość uruchamiania działań w tle:

  • Gdy aplikacja wysyła obiekt PendingIntent za pomocą PendingIntent#send() lub podobnej metody, musi wyrazić zgodę na przyznanie przez nią uprawnień do uruchamiania intencji w tle, aby uruchomić oczekującą intencję. Aby można było włączyć tę opcję, aplikacja musi przekazywać pakiet ActivityOptions z parametrem setPendingIntentBackgroundActivityStartMode(MODE_BACKGROUND_ACTIVITY_START_ALLOWED).
  • Gdy widoczna aplikacja wiąże za pomocą metody bindService() usługę innej aplikacji działającej w tle, ta aplikacja musi teraz wyrazić na nią zgodę, jeśli chce przyznać tej usłudze uprawnienia do uruchamiania własnej aktywności w tle. Aby ją włączyć, podczas wywoływania metody bindService() aplikacja powinna zawierać flagę BIND_ALLOW_ACTIVITY_STARTS.

Te zmiany rozszerzają obecny zestaw ograniczeń, aby chronić użytkowników przez uniemożliwienie szkodliwym aplikacjom nadużywania interfejsów API w celu rozpoczynania uciążliwych działań w tle.

Przemierzanie ścieżki ZIP

W przypadku aplikacji kierowanych na Androida 14 (poziom interfejsu API 34) lub nowszego Android zapobiega lukom w porcie ścieżki ZIP w taki sposób: ZipFile(String) i ZipInputStream.getNextEntry() zwracają wynik ZipException, jeśli nazwy wpisów w pliku ZIP zawierają „..” lub zaczynają się od „/”.

Aplikacje mogą zrezygnować z tej weryfikacji, wywołując metodę dalvik.system.ZipPathValidator.clearCallback().

对于以 Android 14(API 级别 34)或更高版本为目标平台的应用,在以下任一情况下,MediaProjection#createVirtualDisplay 会抛出 SecurityException

您的应用必须在每次捕获会话之前征求用户同意。单次拍摄会话是指对 MediaProjection#createVirtualDisplay 的单次调用,且每个 MediaProjection 实例只能使用一次。

处理配置变更

如果您的应用需要调用 MediaProjection#createVirtualDisplay 来处理配置更改(例如屏幕方向或屏幕尺寸更改),您可以按照以下步骤更新现有 MediaProjection 实例的 VirtualDisplay

  1. 使用新的宽度和高度调用 VirtualDisplay#resize
  2. VirtualDisplay#setSurface 提供具有新宽度和高度的新 Surface

注册回调

您的应用应注册一个回调,以处理用户不同意继续拍摄会话的情况。为此,请实现 Callback#onStop 并让您的应用发布所有相关资源(例如 VirtualDisplaySurface)。

如果您的应用未注册此回调,MediaProjection#createVirtualDisplay 会在应用调用此回调时抛出 IllegalStateException

Zaktualizowano ograniczenia dotyczące aplikacji innych niż SDK

Android 14 zawiera zaktualizowane listy podlegających ograniczeniom interfejsów spoza pakietu SDK opracowane na podstawie współpracy z deweloperami aplikacji na Androida i najnowszych testów wewnętrznych. Zanim ograniczymy dostęp do interfejsów spoza SDK, dbamy o to, aby w miarę możliwości dostępne były publiczne alternatywy.

Jeśli Twoja aplikacja nie jest kierowana na Androida 14, niektóre z tych zmian mogą Cię nie odczuć od razu. Mimo że obecnie możesz używać niektórych interfejsów spoza pakietu SDK (w zależności od docelowego poziomu interfejsu API aplikacji), używanie dowolnej metody lub pola spoza pakietu SDK niesie ze sobą wysokie ryzyko uszkodzenia aplikacji.

Jeśli nie masz pewności, czy Twoja aplikacja korzysta z interfejsów innych niż SDK, możesz przetestować ją, aby to sprawdzić. Jeśli Twoja aplikacja korzysta z interfejsów innych niż SDK, zacznij planować migrację do alternatywnych pakietów SDK. Zdajemy sobie jednak sprawę, że niektóre aplikacje mają odpowiednie przypadki użycia w zakresie interfejsów spoza SDK. Jeśli nie możesz znaleźć alternatywy dla interfejsu innego niż SDK dla funkcji w aplikacji, poproś o nowy publiczny interfejs API.

Więcej informacji o zmianach w tej wersji Androida znajdziesz w artykule Aktualizacje ograniczeń interfejsu spoza pakietu SDK w Androidzie 14. Więcej informacji o interfejsach spoza SDK znajdziesz w artykule Ograniczenia dotyczące interfejsów spoza SDK.