Thay đổi về hành vi: Ứng dụng nhắm đến Android 14 trở lên

Giống như các bản phát hành trước, Android 14 có các thay đổi về hành vi có thể ảnh hưởng đến ứng dụng của bạn. Những thay đổi về hành vi sau đây chỉ áp dụng cho ứng dụng nhắm đến Android 14 (API cấp 34) trở lên. Nếu ứng dụng của bạn nhắm đến Android 14 trở lên, bạn nên sửa đổi ứng dụng để hỗ trợ những hành vi này cho phù hợp (nếu có).

Ngoài ra, hãy nhớ tham khảo danh sách thay đổi về hành vi ảnh hưởng đến tất cả ứng dụng chạy trên Android 14 bất kể targetSdkVersion của ứng dụng.

Chức năng cốt lõi

Bắt buộc phải có loại dịch vụ trên nền trước

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.

Thực thi quyền BLUETOOTH_CONNECT trong BluetoothAdapter

Android 14 thực thi quyền BLUETOOTH_CONNECT khi gọi phương thức BluetoothAdapter getProfileConnectionState() đối với ứng dụng nhắm đến Android 14 (API cấp 34) trở lên.

Phương thức này đã yêu cầu quyền BLUETOOTH_CONNECT nhưng chưa được thực thi. Hãy đảm bảo ứng dụng của bạn khai báo BLUETOOTH_CONNECT trong tệp AndroidManifest.xml của ứng dụng như minh hoạ trong đoạn mã sau và kiểm tra để đảm bảo rằng người dùng đã cấp quyền trước khi gọi getProfileConnectionState.

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

Nội dung cập nhật 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 củng cố lệnh gọi lại và hành vi mạng

Kể từ khi được giới thiệu, JobScheduler dự kiến ứng dụng của bạn sẽ trở về từ onStartJob hoặc onStopJob trong vòng vài giây. Trước Android 14, nếu một công việc chạy quá lâu, công việc đó sẽ tự dừng và không thành công. Nếu ứng dụng của bạn nhắm đến Android 14 (API cấp 34) trở lên và vượt quá thời gian đã cấp trên luồng chính, thì ứng dụng sẽ kích hoạt lỗi ANR với thông báo lỗi "No response to onStartJob" (Không phản hồi onStartJob) hoặc "No response to onStopJob" (Không phản hồi cho onStopJob). Hãy cân nhắc di chuyển sang WorkManager, một công cụ hỗ trợ xử lý không đồng bộ hoặc di chuyển mọi công việc nặng nhọc vào luồng trong nền.

JobScheduler cũng đưa ra yêu cầu khai báo quyền ACCESS_NETWORK_STATE nếu sử dụng quy tắc ràng buộc setRequiredNetworkType hoặc setRequiredNetwork. Nếu ứng dụng của bạn không khai báo quyền ACCESS_NETWORK_STATE khi lên lịch công việc và đang nhắm đến Android 14 trở lên, thì điều này sẽ dẫn đến việc SecurityException.

API khởi chạy thẻ thông tin

Đối với các ứng dụng nhắm đến 14 trở lên, TileService#startActivityAndCollapse(Intent) không còn được dùng nữa và hiện sẽ gửi ra một ngoại lệ khi được gọi. Nếu ứng dụng của bạn chạy các hoạt động từ thẻ thông tin, hãy sử dụng TileService#startActivityAndCollapse(PendingIntent).

Quyền riêng tư

Có quyền truy cập một phần vào ảnh và video

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.

Trải nghiệm người dùng

Thông báo bảo mật về ý định toàn màn hình

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.

Bảo mật

Các quy tắc hạn chế đối với ý định ngầm ẩn và ý định đang chờ xử lý

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 exception would be thrown:

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

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

Broadcast receiver đã đăng ký trong thời gian chạy phải chỉ định hành vi xuất

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.

Tải mã động an toàn hơn

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.

Hạn chế bổ sung khi bắt đầu hoạt động ở chế độ nền

Đối với ứng dụng nhắm đến Android 14 (API cấp 34) trở lên, hệ thống sẽ hạn chế hơn nữa thời điểm các ứng dụng được phép bắt đầu hoạt động ở chế độ nền:

  • Khi gửi một PendingIntent bằng PendingIntent#send() hoặc các phương thức tương tự, ứng dụng phải chọn sử dụng nếu muốn cấp đặc quyền khởi chạy hoạt động của riêng mình ở chế độ nền để bắt đầu ý định đang chờ xử lý. Để chọn sử dụng, ứng dụng phải truyền một gói ActivityOptionssetPendingIntentBackgroundActivityStartMode(MODE_BACKGROUND_ACTIVITY_START_ALLOWED).
  • Khi một ứng dụng đang hiển thị ràng buộc dịch vụ của một ứng dụng khác ở chế độ nền bằng phương thức bindService(), thì ứng dụng đang hiển thị đó phải chọn sử dụng nếu muốn cấp đặc quyền khởi chạy hoạt động của riêng mình ở chế độ nền với dịch vụ ràng buộc. Để chọn sử dụng, ứng dụng phải gắn cờ BIND_ALLOW_ACTIVITY_STARTS khi gọi phương thức bindService().

Những thay đổi này mở rộng nhóm quy tắc hạn chế hiện có để bảo vệ người dùng bằng cách ngăn các ứng dụng độc hại lợi dụng API để bắt đầu các hoạt động gây phiền toái ở chế độ nền.

Truyền tải qua đường dẫn Zip

Đối với các ứng dụng nhắm đến Android 14 (API cấp 34) trở lên, Android sẽ ngăn chặn lỗ hổng Truyền tải qua đường dẫn Zip theo cách sau: ZipFile(String)ZipInputStream.getNextEntry() gửi ZipException nếu tên tệp zip chứa ".." hoặc bắt đầu bằng "/".

Ứng dụng có thể chọn không sử dụng tính năng xác thực này bằng cách gọi 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.

Các quy tắc hạn chế mới cập nhật đối với yếu tố ngoài SDK

Android 14 cung cấp danh sách mới cập nhật về các giao diện không phải SDK bị hạn chế dựa trên khả năng cộng tác với nhà phát triển Android và kiểm thử nội bộ mới nhất. Bất cứ khi nào có thể, chúng tôi phải đảm bảo việc cung cấp các phương án thay thế công khai trước khi hạn chế giao diện không phải SDK.

Nếu ứng dụng của bạn không nhắm đến Android 14, thì một số thay đổi này có thể sẽ không ảnh hưởng ngay. Tuy nhiên, mặc dù hiện tại bạn có thể sử dụng một số giao diện không phải SDK (tuỳ thuộc vào cấp độ API mục tiêu của ứng dụng), nhưng việc sử dụng phương thức hoặc trường không phải SDK luôn có nguy cơ cao làm hỏng ứng dụng.

Nếu không chắc ứng dụng của mình có sử dụng giao diện không phải SDK hay không, bạn có thể kiểm tra ứng dụng để tìm hiểu. Nếu ứng dụng của bạn dựa vào giao diện không phải SDK, thì bạn nên bắt đầu lập kế hoạch di chuyển sang SDK làm giải pháp thay thế. Tuy nhiên, chúng tôi hiểu rằng vẫn có một số trường hợp sử dụng hợp lệ cho việc ứng dụng sử dụng giao diện không phải SDK. Nếu không tìm được giải pháp thay thế cho việc sử dụng giao diện không phải SDK cho một tính năng trong ứng dụng, thì bạn nên yêu cầu một API công khai mới.

Để tìm hiểu thêm về những thay đổi trong bản phát hành Android này, hãy xem bài viết Thông tin cập nhật đối với những hạn chế về giao diện không phải SDK trong Android 14. Để tìm hiểu tổng quan thêm về giao diện không phải SDK, hãy xem Hạn chế đối với giao diện không phải SDK.