Zmiany zachowania: aplikacje kierowane na Androida 14 lub nowszego

Podobnie jak w przypadku poprzednich wersji, Android 14 zawiera zmiany zachowania, które mogą mieć wpływ na Twoją aplikację. Poniższe zmiany zachowania mają zastosowanie wyłącznie do aplikacji kierowanych na Androida 14 (API na poziomie 34) lub nowszego. Jeśli Twoja aplikacja jest kierowana na Androida 14 lub nowszego, zmodyfikuj aplikację, aby w stosownych przypadkach prawidłowo obsługiwała te funkcje.

Zapoznaj się też z listą zmian w zachowaniu, które wpływają na wszystkie aplikacje działające na Androidzie 14, niezależnie od ich targetSdkVersion.

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 aplikacji BluetoothAdapter

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

Ta metoda wymagała już uprawnienia BLUETOOTH_CONNECT, ale nie była ona egzekwowana. Upewnij się, że w pliku AndroidManifest.xml aplikacji deklarujesz BLUETOOTH_CONNECT, jak pokazano w tym fragmencie kodu, i sprawdź, czy użytkownik przyznał uprawnienia, zanim wywołasz funkcję getProfileConnectionState.

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

Aktualizacje OpenJDK 17

Android 14 continues the work of refreshing Android's core libraries to align with the features in the latest OpenJDK LTS releases, including both library updates and Java 17 language support for app and platform developers.

A few of these changes can affect app compatibility:

  • Changes to regular expressions: Invalid group references are now disallowed to more closely follow the semantics of OpenJDK. You might see new cases where an IllegalArgumentException is thrown by the java.util.regex.Matcher class, so make sure to test your app for areas that use regular expressions. To enable or disable this change while testing, toggle the DISALLOW_INVALID_GROUP_REFERENCE flag using the compatibility framework tools.
  • UUID handling: The java.util.UUID.fromString() method now does more strict checks when validating the input argument, so you might see an IllegalArgumentException during deserialization. To enable or disable this change while testing, toggle the ENABLE_STRICT_VALIDATION flag using the compatibility framework tools.
  • ProGuard issues: In some cases, the addition of the java.lang.ClassValue class causes an issue if you try to shrink, obfuscate, and optimize your app using ProGuard. The problem originates with a Kotlin library that changes runtime behaviour based on whether Class.forName("java.lang.ClassValue") returns a class or not. If your app was developed against an older version of the runtime without the java.lang.ClassValue class available, then these optimizations might remove the computeValue method from classes derived from java.lang.ClassValue.

JobScheduler wzmacnia wywołania zwrotne i działanie sieci

Since its introduction, JobScheduler expects your app to return from onStartJob or onStopJob within a few seconds. Prior to Android 14, if a job runs too long, the job is stopped and fails silently. If your app targets Android 14 (API level 34) or higher and exceeds the granted time on the main thread, the app triggers an ANR with the error message "No response to onStartJob" or "No response to onStopJob".

This ANR may be a result of 2 scenarios: 1. There is work blocking the main thread, preventing the callbacks onStartJob or onStopJob from executing and completing within the expected time limit. 2. The developer is running blocking work within the JobScheduler callback onStartJob or onStopJob, preventing the callback from completing within the expected time limit.

To address #1, you will need to further debug what is blocking the main thread when the ANR occurs, you can do this using ApplicationExitInfo#getTraceInputStream() to get the tombstone trace when the ANR occurs. If you're able to manually reproduce the ANR, you can record a system trace and inspect the trace using either Android Studio or Perfetto to better understand what is running on the main thread when the ANR occurs. Note that this can happen when using JobScheduler API directly or using the androidx library WorkManager.

To address #2, consider migrating to WorkManager, which provides support for wrapping any processing in onStartJob or onStopJob in an asynchronous thread.

JobScheduler also introduces a requirement to declare the ACCESS_NETWORK_STATE permission if using setRequiredNetworkType or setRequiredNetwork constraint. If your app does not declare the ACCESS_NETWORK_STATE permission when scheduling the job and is targeting Android 14 or higher, it will result in a SecurityException.

Interfejs API uruchamiania kafelków

W przypadku aplikacji kierowanych do użytkowników, którzy ukończyli 14 lat, Narzędzie TileService#startActivityAndCollapse(Intent) zostało wycofane i obecnie zgłasza tylko w przypadku wywołania. Jeśli aplikacja uruchamia czynności z kart, użyj zamiast tego TileService#startActivityAndCollapse(PendingIntent).

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.

Interfejs użytkownika

Bezpieczne powiadomienia dotyczące intencji pełnoekranowej

With Android 11 (API level 30), it was possible for any app to use Notification.Builder.setFullScreenIntent to send full-screen intents while the phone is locked. You could auto-grant this on app install by declaring USE_FULL_SCREEN_INTENT permission in the AndroidManifest.

Full-screen intent notifications are designed for extremely high-priority notifications demanding the user's immediate attention, such as an incoming phone call or alarm clock settings configured by the user. For apps targeting Android 14 (API level 34) or higher, apps that are allowed to use this permission are limited to those that provide calling and alarms only. The Google Play Store revokes default USE_FULL_SCREEN_INTENT permissions for any apps that don't fit this profile. The deadline for these policy changes is May 31, 2024.

This permission remains enabled for apps installed on the phone before the user updates to Android 14. Users can turn this permission on and off.

You can use the new API NotificationManager.canUseFullScreenIntent to check if your app has the permission; if not, your app can use the new intent ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT to launch the settings page where users can grant the permission.

Bezpieczeństwo

Ograniczenia dotyczące intencji niejawnych i oczekujących

W przypadku aplikacji kierowanych na Androida 14 (poziom interfejsu API 34) lub nowszego Android ogranicza możliwość wysyłania intencji niejawnych do wewnętrznych komponentów aplikacji w ten sposób:

  • Implicit intents są dostarczane tylko do komponentów eksportowanych. Aplikacje muszą używać jawnej intencji, by dostarczyć komponenty do niewyeksportowanych komponentów, lub oznaczyć komponent jako wyeksportowany.
  • Jeśli aplikacja tworzy zmienną oczekującą intencję z intencją, która nie określa komponentu ani pakietu, system zgłasza wyjątek.

Te zmiany zapobiegają przechwytywaniu niejawnych intencji, które mają być używane przez wewnętrzne komponenty aplikacji.

Oto przykładowy 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 spróbuje uruchomić tę aktywność za pomocą domyślnego zamiaru, zostanie zgłoszony wyjątek ActivityNotFoundException:

Kotlin

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

Java

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

Aby uruchomić aktywność, która nie została wyeksportowana, aplikacja powinna użyć wyraźnego zamiaru:

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);

Rejestrowane w czasie wykonywania odbiorniki transmisji muszą określać zachowanie eksportu

Apps and services that target Android 14 (API level 34) or higher and use context-registered receivers are required to specify a flag to indicate whether or not the receiver should be exported to all other apps on the device: either RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED, respectively. This requirement helps protect apps from security vulnerabilities by leveraging the features for these receivers introduced in Android 13.

Exception for receivers that receive only system broadcasts

If your app is registering a receiver only for system broadcasts through Context#registerReceiver methods, such as Context#registerReceiver(), then it shouldn't specify a flag when registering the receiver.

Bezpieczniejsze wczytywanie kodu dynamicznego

If your app targets Android 14 (API level 34) or higher and uses Dynamic Code Loading (DCL), all dynamically-loaded files must be marked as read-only. Otherwise, the system throws an exception. We recommend that apps avoid dynamically loading code whenever possible, as doing so greatly increases the risk that an app can be compromised by code injection or code tampering.

If you must dynamically load code, use the following approach to set the dynamically-loaded file (such as a DEX, JAR, or APK file) as read-only as soon as the file is opened and before any content is written:

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);

Handle dynamically-loaded files that already exist

To prevent exceptions from being thrown for existing dynamically-loaded files, we recommend deleting and recreating the files before you try to dynamically load them again in your app. As you recreate the files, follow the preceding guidance for marking the files read-only at write time. Alternatively, you can re-label the existing files as read-only, but in this case, we strongly recommend that you verify the integrity of the files first (for example, by checking the file's signature against a trusted value), to help protect your app from malicious actions.

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

For apps targeting Android 14 (API level 34) or higher, the system further restricts when apps are allowed to start activities from the background:

These changes expand the existing set of restrictions to protect users by preventing malicious apps from abusing APIs to start disruptive activities from the background.

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().

For apps targeting Android 14 (API level 34) or higher, a SecurityException is thrown by MediaProjection#createVirtualDisplay in either of the following scenarios:

Your app must ask the user to give consent before each capture session. A single capture session is a single invocation on MediaProjection#createVirtualDisplay, and each MediaProjection instance must be used only once.

Handle configuration changes

If your app needs to invoke MediaProjection#createVirtualDisplay to handle configuration changes (such as the screen orientation or screen size changing), you can follow these steps to update the VirtualDisplay for the existing MediaProjection instance:

  1. Invoke VirtualDisplay#resize with the new width and height.
  2. Provide a new Surface with the new width and height to VirtualDisplay#setSurface.

Register a callback

Your app should register a callback to handle cases where the user doesn't grant consent to continue a capture session. To do this, implement Callback#onStop and have your app release any related resources (such as the VirtualDisplay and Surface).

If your app doesn't register this callback, MediaProjection#createVirtualDisplay throws an IllegalStateException when your app invokes it.

Zaktualizowane ograniczenia inne niż związane z pakietem SDK

Android 14 zawiera zaktualizowane listy interfejsów innych niż SDK, które są objęte ograniczeniami. Lista została opracowana na podstawie współpracy z deweloperami Androida i najnowszych testów wewnętrznych. Zawsze, gdy to możliwe, sprawdzamy, czy dostępne są publiczne alternatywy, zanim zaczniemy ograniczać interfejsy inne niż SDK.

Jeśli Twoja aplikacja nie jest kierowana na Androida 14, niektóre z tych zmian mogą nie mieć na Ciebie natychmiastowego wpływu. Mimo że obecnie można korzystać z niektórych interfejsów innych niż SDK (w zależności od docelowego poziomu interfejsu API aplikacji), użycie dowolnej metody lub pola spoza pakietu SDK zawsze wiąże się z dużym ryzykiem uszkodzenia aplikacji.

Jeśli nie masz pewności, czy Twoja aplikacja korzysta z interfejsów innych niż SDK, możesz to przetestować. Jeśli Twoja aplikacja wymaga interfejsów innych niż SDK, zacznij planować migrację na alternatywne wersje pakietów SDK. Zdajemy sobie jednak sprawę, że niektóre aplikacje mogą prawidłowo korzystać z interfejsów innych niż SDK. Jeśli nie możesz znaleźć w swojej aplikacji interfejsu innego niż interfejs SDK, musisz poprosić 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.