和先前版本一樣,Android 14 也包含可能會影響應用程式的行為變更。以下行為變更僅適用於指定 Android 14 (API 級別 34) 以上版本的應用程式。如果您的應用程式指定 Android 14 以上版本,建議您視情況修改應用程式,以支援這些行為。
此外,無論應用程式的 targetSdkVersion 為何,請務必查看對所有 Android 14 應用程式有影響的行為變更清單。
核心功能
必須提供前景服務類型
如果您的應用程式以 Android 14 (API 級別 34) 以上版本為目標,則必須為應用程式中的每個前景服務指定至少一個前景服務類型。您應選擇代表應用程式用途的前景服務類型。系統會預期具有特定類型的前景服務,以滿足特定用途。
如果應用程式的用途與上述任一類型無關,強烈建議您遷移邏輯,以便使用 WorkManager 或使用者啟動的資料移轉作業。
在 BluetoothAdapter 中強制執行 BLUETOOTH_CONNECT 權限
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 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
IllegalArgumentExceptionis thrown by thejava.util.regex.Matcherclass, so make sure to test your app for areas that use regular expressions. To enable or disable this change while testing, toggle theDISALLOW_INVALID_GROUP_REFERENCEflag 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 anIllegalArgumentExceptionduring deserialization. To enable or disable this change while testing, toggle theENABLE_STRICT_VALIDATIONflag using the compatibility framework tools. - ProGuard issues: In some cases, the addition of the
java.lang.ClassValueclass 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 whetherClass.forName("java.lang.ClassValue")returns a class or not. If your app was developed against an older version of the runtime without thejava.lang.ClassValueclass available, then these optimizations might remove thecomputeValuemethod from classes derived fromjava.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 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 傳送全螢幕意圖。您可以在 AndroidManifest 中宣告 USE_FULL_SCREEN_INTENT 權限,以便在安裝應用程式時自動授予這項權限。
全螢幕意圖通知是專為需要使用者立即處理的高度優先通知設計,例如:來電或使用者設定的鬧鐘。針對指定 Android 14 (API 級別 34) 以上版本為目標版本的應用程式,只有提供通話和鬧鐘功能的應用程式,才可以使用這項權限。凡不符合此設定檔的任何應用程式,Google Play 商店將撤銷預設的 USE_FULL_SCREEN_INTENT 權限。這些政策異動的期限為 2024 年 5 月 31 日。
在使用者更新至 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);
已註冊執行階段的廣播接收器必須指定匯出行為
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 檔案) 盡快處於唯讀狀態 會在檔案開啟和寫入任何內容前執行:
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);
處理已存在的動態載入檔案
為避免現有的動態載入檔案擲回例外狀況,建議您先刪除並重新建立檔案,然後再嘗試在應用程式中動態載入這些檔案。重新建立檔案時,請按照上述指示,在寫入時將檔案標記為唯讀。此外,也可以 將現有檔案重新標示為唯讀 建議您先驗證檔案的完整性 (例如,使用 檢查檔案簽名是否符合信任的值),協助您保護應用程式 不受惡意動作的影響
從背景啟動活動的額外限制
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.
Zip Path Traversal
針對指定 Android 14 (API 級別 34) 以上版本的應用程式,Android 會透過下列方式避免 Zip Path Traversal 安全漏洞:如果 ZIP 檔案的項目名稱包含「..」或以「/」開頭,則 ZipFile(String) 和 ZipInputStream.getNextEntry() 會擲回 ZipException。
應用程式可透過呼叫 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 includes updated lists of restricted non-SDK interfaces based on collaboration with Android developers and the latest internal testing. Whenever possible, we make sure that public alternatives are available before we restrict non-SDK interfaces.
If your app does not target Android 14, some of these changes might not immediately affect you. However, while you can currently use some non-SDK interfaces (depending on your app's target API level), using any non-SDK method or field always carries a high risk of breaking your app.
If you are unsure if your app uses non-SDK interfaces, you can test your app to find out. If your app relies on non-SDK interfaces, you should begin planning a migration to SDK alternatives. Nevertheless, we understand that some apps have valid use cases for using non-SDK interfaces. If you cannot find an alternative to using a non-SDK interface for a feature in your app, you should request a new public API.
如要進一步瞭解此 Android 版本中的變更,請參閱「Android 14 的非 SDK 介面限制更新內容」。如要進一步瞭解非 SDK 介面的一般資訊,請參閱「非 SDK 介面的限制」。