Как и предыдущие выпуски, Android 14 включает изменения в поведении, которые могут повлиять на ваше приложение. Следующие изменения поведения применяются исключительно к приложениям, ориентированным на Android 14 (уровень API 34) или более поздних версий. Если ваше приложение ориентировано на Android 14 или более поздней версии, вам следует изменить его, чтобы обеспечить правильную поддержку такого поведения, если это применимо.
Обязательно ознакомьтесь со списком изменений поведения, которые влияют на все приложения, работающие на Android 14, независимо от targetSdkVersion
приложения.
Основная функциональность
Укажите типы приоритетных служб.
Если ваше приложение предназначено для Android 14 (уровень API 34) или более поздней версии, оно должно указать хотя бы один тип службы переднего плана для каждой службы переднего плана в вашем приложении. Вам следует выбрать тип службы приоритетного плана, который соответствует варианту использования вашего приложения. Система ожидает, что службы приоритетного плана, имеющие определенный тип, удовлетворят конкретному сценарию использования.
Если вариант использования в вашем приложении не связан ни с одним из этих типов, настоятельно рекомендуется перенести вашу логику на использование WorkManager или заданий передачи данных, инициируемых пользователем .
Применение разрешения BLUETOOTH_CONNECT в BluetoothAdapter
Android 14 применяет разрешение BLUETOOTH_CONNECT
при вызове метода BluetoothAdapter
getProfileConnectionState()
для приложений, предназначенных для Android 14 (уровень API 34) или выше.
Для этого метода уже требовалось разрешение BLUETOOTH_CONNECT
, но оно не было применено. Убедитесь, что ваше приложение объявляет BLUETOOTH_CONNECT
в файле AndroidManifest.xml
вашего приложения, как показано в следующем фрагменте, и убедитесь, что пользователь предоставил разрешение перед вызовом 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 усиливает обратный вызов и поведение сети
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
.
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
для запуска страницы настроек, на которой пользователи могут предоставить разрешение.
Безопасность
Ограничения неявных и ожидающих намерений
For apps targeting Android 14 (API level 34) or higher, Android restricts apps from sending implicit intents to internal app components in the following ways:
- Implicit intents are only delivered to exported components. Apps must either use an explicit intent to deliver to unexported components, or mark the component as exported.
- If an app creates a mutable pending intent with an intent that doesn't specify a component or package, the system throws an exception.
These changes prevent malicious apps from intercepting implicit intents that are intended for use by an app's internal components.
For example, here is an intent filter that could be declared in your app's manifest file:
<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>
If your app tried to launch this activity using an implicit intent, an
ActivityNotFoundException
exception would be thrown:
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"));
To launch the non-exported activity, your app should use an explicit intent instead:
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);
Получатели широковещательных сообщений, зарегистрированные во время выполнения, должны указать поведение экспорта.
Приложения и службы, ориентированные на Android 14 (уровень API 34) или выше и использующие приемники, зарегистрированные в контексте, должны указать флаг, указывающий, следует ли экспортировать приемник во все другие приложения на устройстве: либо RECEIVER_EXPORTED
, либо RECEIVER_NOT_EXPORTED
соответственно. . Это требование помогает защитить приложения от уязвимостей безопасности за счет использования функций этих приемников, представленных в Android 13 .
Исключение для приемников, которые принимают только системные трансляции.
Если ваше приложение регистрирует получателя только для системных широковещательных рассылок с помощью методов Context#registerReceiver
, таких как Context#registerReceiver()
, то ему не следует указывать флаг при регистрации получателя.
Более безопасная динамическая загрузка кода.
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.
Дополнительные ограничения на запуск активности в фоновом режиме
Для приложений, ориентированных на Android 14 (уровень API 34) или выше, система дополнительно ограничивает, когда приложениям разрешено запускать действия в фоновом режиме:
- Когда приложение отправляет
PendingIntent
с помощьюPendingIntent#send()
или аналогичных методов, приложение должно согласиться, если оно хочет предоставить свои собственные привилегии запуска фоновой активности для запуска ожидающего намерения. Чтобы принять участие, приложение должно передать пакетActivityOptions
с помощьюsetPendingIntentBackgroundActivityStartMode(MODE_BACKGROUND_ACTIVITY_START_ALLOWED)
. - Когда видимое приложение привязывает службу другого приложения, находящегося в фоновом режиме, с помощью
bindService()
, видимое приложение теперь должно согласиться, если оно хочет предоставить свои собственные привилегии запуска фоновой активности связанному сервису. Чтобы принять участие, приложение должно включить флагBIND_ALLOW_ACTIVITY_STARTS
при вызове методаbindService()
.
Эти изменения расширяют существующий набор ограничений для защиты пользователей, не позволяя вредоносным приложениям злоупотреблять API и запускать разрушительные действия в фоновом режиме.
Обход пути почтового индекса
Для приложений, предназначенных для Android 14 (уровень API 34) или выше, Android предотвращает уязвимость обхода пути Zip следующим образом: ZipFile(String)
и ZipInputStream.getNextEntry()
выдают исключение ZipException
, если имена записей zip-файла содержат «..» или start. с "/".
Приложения могут отказаться от этой проверки, вызвав 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
Intent
that is returned fromMediaProjectionManager#createScreenCaptureIntent
, and passes it multiple times toMediaProjectionManager#getMediaProjection
. - Your app invokes
MediaProjection#createVirtualDisplay
multiple times on the sameMediaProjection
instance.
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#resize
with the new width and height. - Provide a new
Surface
with 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.