Изменения поведения: приложения для Android 14 или более поздней версии. Изменения поведения: приложения для Android 14 или более поздней версии. Изменения поведения: приложения для Android 14 или более поздней версии. Изменения поведения: приложения для Android 14 или более поздней версии.

Как и предыдущие выпуски, Android 14 включает изменения в поведении, которые могут повлиять на ваше приложение. Следующие изменения поведения применяются исключительно к приложениям, ориентированным на Android 14 (уровень API 34) или более поздних версий. Если ваше приложение ориентировано на Android 14 или более поздней версии, вам следует изменить его так, чтобы оно правильно поддерживало такое поведение, если это применимо.

Обязательно ознакомьтесь также со списком изменений поведения, которые влияют на все приложения, работающие на Android 14, независимо от targetSdkVersion приложения.

Основная функциональность

Укажите типы приоритетных служб.

Если ваше приложение предназначено для Android 14 (уровень API 34) или более поздней версии, оно должно указать хотя бы один тип службы переднего плана для каждой службы переднего плана в вашем приложении. Вам следует выбрать тип службы приоритетного плана, который соответствует варианту использования вашего приложения. Система ожидает, что службы приоритетного плана, имеющие определенный тип, удовлетворят конкретному сценарию использования.

Если вариант использования в вашем приложении не связан ни с одним из этих типов, настоятельно рекомендуется перенести вашу логику на использование WorkManager или заданий передачи данных, инициируемых пользователем .

Применение разрешения 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 усиливает обратный вызов и поведение сети

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 запуска плиток

Для приложений, предназначенных для версии 14 и выше, TileService#startActivityAndCollapse(Intent) устарел и теперь выдает исключение при вызове. Если ваше приложение запускает действия из плиток, используйте вместо этого TileService#startActivityAndCollapse(PendingIntent) .

Конфиденциальность

Частичный доступ к фото и видео

В Android 14 представлен доступ к выбранным фотографиям, который позволяет пользователям предоставлять приложениям доступ к определенным изображениям и видео в их библиотеке, а не предоставлять доступ ко всем медиафайлам определенного типа.

Это изменение доступно только в том случае, если ваше приложение предназначено для Android 14 (уровень API 34) или более поздней версии. Если вы еще не используете средство выбора фотографий, мы рекомендуем внедрить его в свое приложение , чтобы обеспечить единообразный выбор изображений и видео, а также повысить конфиденциальность пользователей без необходимости запрашивать какие-либо разрешения на хранение.

Если вы поддерживаете свой собственный инструмент выбора галереи, используя разрешения на хранение, и вам необходимо сохранять полный контроль над своей реализацией, адаптируйте свою реализацию для использования нового разрешения READ_MEDIA_VISUAL_USER_SELECTED . Если ваше приложение не использует новое разрешение, система запускает его в режиме совместимости .

Пользовательский опыт

Безопасные полноэкранные уведомления о намерениях

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.

Безопасность

Ограничения неявных и ожидающих намерений

Для приложений, предназначенных для 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);

Получатели широковещательных сообщений, зарегистрированные во время выполнения, должны указать поведение экспорта.

Приложения и службы, ориентированные на 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.

Дополнительные ограничения на запуск активности в фоновом режиме

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.

Обход почтового пути

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

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.

Обновлены ограничения, не связанные с SDK.

Android 14 включает обновленные списки ограниченных интерфейсов, не входящих в SDK, основанные на сотрудничестве с разработчиками Android и последних результатах внутреннего тестирования. По возможности мы обеспечиваем доступность общедоступных альтернатив, прежде чем ограничивать интерфейсы, не относящиеся к SDK.

Если ваше приложение не предназначено для Android 14, некоторые из этих изменений могут не затронуть вас сразу. Однако, хотя в настоящее время вы можете использовать некоторые интерфейсы, не входящие в SDK ( в зависимости от целевого уровня API вашего приложения ), использование любого метода или поля, не входящего в SDK, всегда сопряжено с высоким риском поломки вашего приложения.

Если вы не уверены, использует ли ваше приложение интерфейсы, отличные от SDK, вы можете протестировать свое приложение, чтобы выяснить это. Если ваше приложение использует интерфейсы, отличные от SDK, вам следует начать планировать переход на альтернативы SDK. Тем не менее мы понимаем, что в некоторых приложениях есть допустимые варианты использования интерфейсов, отличных от SDK. Если вы не можете найти альтернативу использованию интерфейса, отличного от SDK, для функции вашего приложения, вам следует запросить новый общедоступный API .

Дополнительные сведения об изменениях в этой версии Android см . в разделе Обновления ограничений интерфейса, не связанных с SDK, в Android 14 . Дополнительные сведения об интерфейсах, отличных от SDK, см. в разделе Ограничения на интерфейсы, не относящиеся к SDK .