Как и в предыдущих версиях, в Android 14 внесены изменения в поведение, которые могут повлиять на ваше приложение. Следующие изменения в поведении применяются исключительно к приложениям, предназначенным для Android 14 (уровень API 34) и выше. Если ваше приложение предназначено для Android 14 или выше, вам следует изменить его для корректной поддержки этих изменений, где это применимо.
Обязательно ознакомьтесь со списком изменений поведения, которые влияют на все приложения, работающие на Android 14, независимо от targetSdkVersion приложения.
Основная функциональность
Требуются типы служб переднего плана
If your app targets Android 14 (API level 34) or higher, it must specify at least one foreground service type for each foreground service within your app. You should choose a foreground service type that represents your app's use case. The system expects foreground services that have a particular type to satisfy a particular use case.
If a use case in your app isn't associated with any of these types, it's strongly recommended that you migrate your logic to use WorkManager or user-initiated data transfer jobs.
Обеспечение разрешения BLUETOOTH_CONNECT в BluetoothAdapter
Android 14 enforces the BLUETOOTH_CONNECT permission when calling the
BluetoothAdapter getProfileConnectionState() method for apps targeting
Android 14 (API level 34) or higher.
This method already required the BLUETOOTH_CONNECT permission, but it was not
enforced. Make sure your app declares BLUETOOTH_CONNECT in your app's
AndroidManifest.xml file as shown in the following snippet and check that
a user has granted the permission before calling
getProfileConnectionState.
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
Обновления OpenJDK 17
В Android 14 продолжается работа по обновлению основных библиотек Android, чтобы они соответствовали функциям последних выпусков OpenJDK LTS, включая обновления библиотек и поддержку языка Java 17 для разработчиков приложений и платформ.
Некоторые из этих изменений могут повлиять на совместимость приложений:
- Изменения в регулярных выражениях : недопустимые ссылки на группы теперь запрещены, чтобы более точно соответствовать семантике OpenJDK. Вы можете столкнуться с новыми случаями, когда класс
java.util.regex.Matcherгенерирует исключениеIllegalArgumentException, поэтому обязательно проверяйте свое приложение на наличие областей, в которых используются регулярные выражения. Чтобы включить или отключить это изменение во время тестирования, переключите флагDISALLOW_INVALID_GROUP_REFERENCEс помощью инструментов платформы совместимости . - Обработка UUID : метод
java.util.UUID.fromString()теперь выполняет более строгие проверки при проверке входного аргумента, поэтому во время десериализации вы можете увидетьIllegalArgumentException. Чтобы включить или отключить это изменение во время тестирования, переключите флагENABLE_STRICT_VALIDATIONс помощью инструментов платформы совместимости . - Проблемы ProGuard . В некоторых случаях добавление класса
java.lang.ClassValueвызывает проблемы, если вы пытаетесь сжать, запутать и оптимизировать свое приложение с помощью ProGuard. Проблема возникает из-за библиотеки Kotlin, которая меняет поведение во время выполнения в зависимости от того, возвращает лиClass.forName("java.lang.ClassValue")класс или нет. Если ваше приложение было разработано для более старой версии среды выполнения без доступного классаjava.lang.ClassValue, то эти оптимизации могут удалить методcomputeValueиз классов, производных отjava.lang.ClassValue.
JobScheduler усиливает обратный вызов и сетевое поведение
С момента своего появления JobScheduler ожидает, что ваше приложение вернется из onStartJob или onStopJob в течение нескольких секунд. До версии Android 14, если задание выполнялось слишком долго, оно останавливалось и завершалось автоматически. Если ваше приложение предназначено для Android 14 (уровень API 34) или выше и превышает разрешенное время в основном потоке, приложение запускает ANR с сообщением об ошибке «Нет ответа на onStartJob » или «Нет ответа на onStopJob ».
Этот ANR может быть результатом двух сценариев: 1. Выполняется работа, блокирующая основной поток, что препятствует выполнению и завершению обратных вызовов onStartJob или onStopJob в течение ожидаемого срока. 2. Разработчик запускает блокировку в обратном вызове JobScheduler onStartJob или onStopJob , не позволяя обратному вызову завершиться в течение ожидаемого срока.
Для решения № 1 вам потребуется дополнительная отладка того, что блокирует основной поток при возникновении ошибки ANR. Это можно сделать с помощью ApplicationExitInfo#getTraceInputStream() чтобы получить трассировку захоронения при возникновении ошибки ANR. Если вы можете вручную воспроизвести ANR, вы можете записать системную трассировку и проверить ее с помощью Android Studio или Perfetto, чтобы лучше понять, что происходит в основном потоке при возникновении ошибки ANR. Обратите внимание, что это может произойти при непосредственном использовании API JobScheduler или при использовании библиотеки WorkManager для Android.
Для решения № 2 рассмотрите возможность перехода на WorkManager , который обеспечивает поддержку переноса любой обработки в onStartJob или onStopJob в асинхронный поток.
JobScheduler также вводит требование объявлять разрешение ACCESS_NETWORK_STATE при использовании ограничения setRequiredNetworkType или setRequiredNetwork . Если ваше приложение не декларирует разрешение ACCESS_NETWORK_STATE при планировании задания и предназначено для Android 14 или более поздней версии, это приведет к возникновению SecurityException .
С момента своего появления JobScheduler ожидает, что ваше приложение вернется из onStartJob или onStopJob в течение нескольких секунд. До версии Android 14, если задание выполнялось слишком долго, оно останавливалось и завершалось автоматически. Если ваше приложение предназначено для Android 14 (уровень API 34) или выше и превышает разрешенное время в основном потоке, приложение запускает ANR с сообщением об ошибке «Нет ответа на onStartJob » или «Нет ответа на onStopJob ».
Этот ANR может быть результатом двух сценариев: 1. Выполняется работа, блокирующая основной поток, что препятствует выполнению и завершению обратных вызовов onStartJob или onStopJob в течение ожидаемого срока. 2. Разработчик запускает блокировку в обратном вызове JobScheduler onStartJob или onStopJob , не позволяя обратному вызову завершиться в течение ожидаемого срока.
Для решения № 1 вам потребуется дополнительная отладка того, что блокирует основной поток при возникновении ошибки ANR. Это можно сделать с помощью ApplicationExitInfo#getTraceInputStream() чтобы получить трассировку захоронения при возникновении ошибки ANR. Если вы можете вручную воспроизвести ANR, вы можете записать системную трассировку и проверить ее с помощью Android Studio или Perfetto, чтобы лучше понять, что происходит в основном потоке при возникновении ошибки ANR. Обратите внимание, что это может произойти при непосредственном использовании API JobScheduler или при использовании библиотеки WorkManager для Android.
Для решения № 2 рассмотрите возможность перехода на WorkManager , который обеспечивает поддержку переноса любой обработки в onStartJob или onStopJob в асинхронный поток.
JobScheduler также вводит требование объявлять разрешение ACCESS_NETWORK_STATE при использовании ограничения setRequiredNetworkType или setRequiredNetwork . Если ваше приложение не декларирует разрешение ACCESS_NETWORK_STATE при планировании задания и предназначено для Android 14 или более поздней версии, это приведет к возникновению SecurityException .
С момента своего появления JobScheduler ожидает, что ваше приложение вернется из onStartJob или onStopJob в течение нескольких секунд. До версии Android 14, если задание выполнялось слишком долго, оно останавливалось и завершалось автоматически. Если ваше приложение предназначено для Android 14 (уровень API 34) или выше и превышает разрешенное время в основном потоке, приложение запускает ANR с сообщением об ошибке «Нет ответа на onStartJob » или «Нет ответа на onStopJob ».
Этот ANR может быть результатом двух сценариев: 1. Выполняется работа, блокирующая основной поток, что препятствует выполнению и завершению обратных вызовов onStartJob или onStopJob в течение ожидаемого срока. 2. Разработчик запускает блокировку в обратном вызове JobScheduler onStartJob или onStopJob , не позволяя обратному вызову завершиться в течение ожидаемого срока.
Для решения № 1 вам потребуется дополнительная отладка того, что блокирует основной поток при возникновении ошибки ANR. Это можно сделать с помощью ApplicationExitInfo#getTraceInputStream() чтобы получить трассировку захоронения при возникновении ошибки ANR. Если вы можете вручную воспроизвести ANR, вы можете записать системную трассировку и проверить ее с помощью Android Studio или Perfetto, чтобы лучше понять, что происходит в основном потоке при возникновении ошибки ANR. Обратите внимание, что это может произойти при непосредственном использовании API JobScheduler или при использовании библиотеки WorkManager для Android.
Для решения № 2 рассмотрите возможность перехода на WorkManager , который обеспечивает поддержку переноса любой обработки в onStartJob или onStopJob в асинхронный поток.
JobScheduler также вводит требование объявлять разрешение ACCESS_NETWORK_STATE при использовании ограничения setRequiredNetworkType или setRequiredNetwork . Если ваше приложение не декларирует разрешение ACCESS_NETWORK_STATE при планировании задания и предназначено для Android 14 или более поздней версии, это приведет к возникновению SecurityException .
С момента своего появления JobScheduler ожидает, что ваше приложение вернется из onStartJob или onStopJob в течение нескольких секунд. До версии Android 14, если задание выполнялось слишком долго, оно останавливалось и завершалось автоматически. Если ваше приложение предназначено для Android 14 (уровень API 34) или выше и превышает разрешенное время в основном потоке, приложение запускает ANR с сообщением об ошибке «Нет ответа на onStartJob » или «Нет ответа на onStopJob ».
Этот ANR может быть результатом двух сценариев: 1. Выполняется работа, блокирующая основной поток, что препятствует выполнению и завершению обратных вызовов onStartJob или onStopJob в течение ожидаемого срока. 2. Разработчик запускает блокировку в обратном вызове JobScheduler onStartJob или onStopJob , не позволяя обратному вызову завершиться в течение ожидаемого срока.
Для решения № 1 вам потребуется дополнительная отладка того, что блокирует основной поток при возникновении ошибки ANR. Это можно сделать с помощью ApplicationExitInfo#getTraceInputStream() чтобы получить трассировку захоронения при возникновении ошибки ANR. Если вы можете воспроизвести ANR вручную, вы можете записать трассировку системы и проверить ее с помощью Android Studio или Perfetto, чтобы лучше понять, что происходит в основном потоке при возникновении ошибки ANR. Обратите внимание, что это может произойти при непосредственном использовании API JobScheduler или при использовании библиотеки WorkManager для Android.
Для решения № 2 рассмотрите возможность перехода на WorkManager , который обеспечивает поддержку переноса любой обработки в onStartJob или onStopJob в асинхронный поток.
JobScheduler также вводит требование объявлять разрешение ACCESS_NETWORK_STATE при использовании ограничения setRequiredNetworkType или setRequiredNetwork . Если ваше приложение не декларирует разрешение ACCESS_NETWORK_STATE при планировании задания и предназначено для Android 14 или более поздней версии, это приведет к возникновению SecurityException .
API запуска плиток
For apps targeting 14 and higher,
TileService#startActivityAndCollapse(Intent) is deprecated and now throws
an exception when called. If your app launches activities from tiles, use
TileService#startActivityAndCollapse(PendingIntent) instead.
Конфиденциальность
Частичный доступ к фотографиям и видео
Android 14 introduces Selected Photos Access, which allows users to grant apps access to specific images and videos in their library, rather than granting access to all media of a given type.
This change is only enabled if your app targets Android 14 (API level 34) or higher. If you don't use the photo picker yet, we recommend implementing it in your app to provide a consistent experience for selecting images and videos that also enhances user privacy without having to request any storage permissions.
If you maintain your own gallery picker using storage permissions and need to
maintain full control over your implementation, adapt your implementation
to use the new READ_MEDIA_VISUAL_USER_SELECTED permission. If your app
doesn't use the new permission, the system runs your app in a compatibility
mode.
Пользовательский опыт
Безопасные полноэкранные уведомления о намерениях
В Android 11 (уровень API 30) любое приложение могло использовать Notification.Builder.setFullScreenIntent для отправки полноэкранных намерений, когда телефон заблокирован. Вы можете автоматически предоставить это разрешение при установке приложения, объявив разрешение USE_FULL_SCREEN_INTENT в AndroidManifest.
Полноэкранные уведомления о намерениях предназначены для уведомлений с чрезвычайно высоким приоритетом, требующих немедленного внимания пользователя, таких как входящий телефонный звонок или настройки будильника, настроенные пользователем. Для приложений, предназначенных для Android 14 (уровень API 34) или выше, приложения, которым разрешено использовать это разрешение, ограничены теми, которые обеспечивают только вызовы и сигналы тревоги. Магазин Google Play отзывает разрешения USE_FULL_SCREEN_INTENT по умолчанию для всех приложений, которые не соответствуют этому профилю. Крайний срок внесения этих изменений в политику — 31 мая 2024 года .
Это разрешение остается включенным для приложений, установленных на телефоне, до обновления пользователя до Android 14. Пользователи могут включать и отключать это разрешение.
Вы можете использовать новый API NotificationManager.canUseFullScreenIntent чтобы проверить, имеет ли ваше приложение разрешение; в противном случае ваше приложение может использовать новое намерение ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT для запуска страницы настроек, на которой пользователи могут предоставить разрешение.
Безопасность
Ограничения на неявные и ожидаемые намерения
Для приложений, предназначенных для Android 14 (уровень API 34) или выше, Android запрещает приложениям отправлять неявные намерения внутренним компонентам приложения следующими способами:
- Неявные намерения доставляются только экспортированным компонентам. Приложения должны либо использовать явное намерение доставить неэкспортированные компоненты, либо пометить компонент как экспортированный.
- Если приложение создает изменяемое ожидающее намерение с намерением, в котором не указан компонент или пакет, система выдает исключение.
Эти изменения не позволяют вредоносным приложениям перехватывать неявные намерения, предназначенные для использования внутренними компонентами приложения.
Например, вот фильтр намерений , который можно объявить в файле манифеста вашего приложения:
<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>
Если ваше приложение попытается запустить это действие, используя неявное намерение, будет выдано исключение ActivityNotFoundException :
Котлин
// Throws an ActivityNotFoundException exception when targeting Android 14. context.startActivity(Intent("com.example.action.APP_ACTION"))
Ява
// Throws an ActivityNotFoundException exception when targeting Android 14. context.startActivity(new Intent("com.example.action.APP_ACTION"));
Чтобы запустить неэкспортируемое действие, ваше приложение должно вместо этого использовать явное намерение:
Котлин
// This makes the intent explicit. val explicitIntent = Intent("com.example.action.APP_ACTION") explicitIntent.apply { package = context.packageName } context.startActivity(explicitIntent)
Ява
// This makes the intent explicit. Intent explicitIntent = new Intent("com.example.action.APP_ACTION") explicitIntent.setPackage(context.getPackageName()); context.startActivity(explicitIntent);
Приемники широковещательных сообщений, зарегистрированные во время выполнения, должны указывать поведение экспорта.
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.
Более безопасная динамическая загрузка кода
Если ваше приложение предназначено для Android 14 (уровень API 34) или выше и использует динамическую загрузку кода (DCL), все динамически загружаемые файлы должны быть помечены как доступные только для чтения. В противном случае система выдает исключение. Мы рекомендуем приложениям избегать динамической загрузки кода , когда это возможно, поскольку это значительно увеличивает риск того, что приложение может быть скомпрометировано путем внедрения кода или подделки кода.
Если вам необходимо динамически загружать код, используйте следующий подход, чтобы установить динамически загружаемый файл (например, файл DEX, JAR или APK) только для чтения сразу после открытия файла и до записи какого-либо содержимого:
Котлин
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)
Ява
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);
Обработка динамически загружаемых файлов, которые уже существуют.
Чтобы предотвратить создание исключений для существующих динамически загружаемых файлов, мы рекомендуем удалить и воссоздать файлы, прежде чем пытаться снова динамически загрузить их в свое приложение. При воссоздании файлов следуйте приведенным выше инструкциям, чтобы пометить файлы только для чтения во время записи. Альтернативно вы можете пометить существующие файлы как доступные только для чтения, но в этом случае мы настоятельно рекомендуем сначала проверить целостность файлов (например, проверив подпись файла на соответствие доверенному значению), чтобы защитить ваше приложение от вредоносных действий.
Дополнительные ограничения на запуск действий в фоновом режиме
For apps targeting Android 14 (API level 34) or higher, the system further restricts when apps are allowed to start activities from the background:
- When an app sends a
PendingIntentusingPendingIntent#send()or similar methods, the app must opt in if it wants to grant its own background activity launch privileges to start the pending intent. To opt in, the app should pass anActivityOptionsbundle withsetPendingIntentBackgroundActivityStartMode(MODE_BACKGROUND_ACTIVITY_START_ALLOWED). - When a visible app binds a service of another app that's in the background
using the
bindService()method, the visible app must now opt in if it wants to grant its own background activity launch privileges to the bound service. To opt in, the app should include theBIND_ALLOW_ACTIVITY_STARTSflag when calling thebindService()method.
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.
Обход почтового индекса
For apps targeting Android 14 (API level 34) or higher, Android prevents the Zip
Path Traversal Vulnerability in the following way:
ZipFile(String) and
ZipInputStream.getNextEntry() throws a
ZipException if zip file entry names contain ".." or start
with "/".
Apps can opt-out from this validation by calling
dalvik.system.ZipPathValidator.clearCallback().
Для каждого сеанса захвата MediaProjection требуется согласие пользователя.
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 caches the
Intentthat is returned fromMediaProjectionManager#createScreenCaptureIntent, and passes it multiple times toMediaProjectionManager#getMediaProjection. - Your app invokes
MediaProjection#createVirtualDisplaymultiple times on the sameMediaProjectioninstance.
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:
- Invoke
VirtualDisplay#resizewith the new width and height. - Provide a new
Surfacewith the new width and height toVirtualDisplay#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.
Обновлены ограничения, не связанные с SDK
В Android 14 включены обновлённые списки ограниченных интерфейсов, не входящих в SDK, основанные на результатах сотрудничества с разработчиками Android и последних внутренних тестов. По возможности мы проверяем наличие общедоступных альтернатив, прежде чем ограничивать интерфейсы, не входящие в SDK.
Если ваше приложение не предназначено для Android 14, некоторые из этих изменений могут не сразу вас затронуть. Однако, хотя в настоящее время вы можете использовать некоторые интерфейсы, не относящиеся к SDK ( в зависимости от целевого уровня API вашего приложения ), использование любого метода или поля, не относящегося к SDK, всегда сопряжено с высоким риском выхода приложения из строя.
Если вы не уверены, использует ли ваше приложение интерфейсы, не относящиеся к SDK, вы можете протестировать его , чтобы выяснить это. Если ваше приложение использует интерфейсы, не относящиеся к SDK, вам следует начать планировать миграцию на альтернативы SDK. Тем не менее, мы понимаем, что в некоторых приложениях есть обоснованные примеры использования интерфейсов, не относящихся к SDK. Если вы не можете найти альтернативу использованию интерфейса, не относящегося к SDK, для какой-либо функции вашего приложения, вам следует запросить новый публичный API .
To learn more about the changes in this release of Android, see Updates to non-SDK interface restrictions in Android 14. To learn more about non-SDK interfaces generally, see Restrictions on non-SDK interfaces.