これまでのリリースと同様、Android 14 には、アプリに影響する可能性がある動作変更が含まれています。下記の動作変更は、Android 14(API レベル 34)以上をターゲットとするアプリにのみ適用されます。アプリが Android 14 以上をターゲットとする場合は、必要に応じてアプリを変更し、下記の動作に適切に対応できるようにしてください。
アプリの targetSdkVersion
に関係なく、Android 14 で実行されるすべてのアプリに影響する動作変更のリストも必ずご確認ください。
コア機能
フォアグラウンド サービス タイプは必須
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.
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
IllegalArgumentException
is thrown by thejava.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 theDISALLOW_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 anIllegalArgumentException
during deserialization. To enable or disable this change while testing, toggle theENABLE_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 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.ClassValue
class available, then these optimizations might remove thecomputeValue
method from classes derived fromjava.lang.ClassValue
.
JobScheduler がコールバックとネットワークの動作を強化
JobScheduler の導入以来、アプリは onStartJob
または onStopJob
から数秒以内に戻ることが想定されています。Android 14 より前のバージョンでは、
ジョブの実行時間が長すぎると、ジョブは停止し、通知なく失敗します。
アプリが Android 14(API レベル 34)以降をターゲットとし、メインスレッドで許可された時間が超過すると、アプリは「onStartJob
への応答なし」または「onStopJob
への応答なし」というエラー メッセージとともに ANR をトリガーします。
この ANR は、次の 2 つのシナリオの結果である可能性があります。
1. メインスレッドをブロックする処理があり、コールバック onStartJob
または onStopJob
が想定された時間制限内に実行されず、完了しません。2. デベロッパーが JobScheduler コールバック onStartJob
または onStopJob
内でブロッキング処理を実行しているため、コールバックが想定された制限時間内に完了していません。
1 の問題に対処するには、メインスレッドをブロックしているものをさらにデバッグする必要があります。
アラートのタイムスタンプを
ApplicationExitInfo#getTraceInputStream()
: tombstone を取得します
トレースをトリガーできますANR を手動で再現できる場合は
システム トレースを記録し、次のいずれかの方法でトレースを検査できます。
Android Studio または Perfetto を使用して、何が
メインスレッドで待機します。
これは、JobScheduler API を直接使用する場合や、androidx ライブラリの WorkManager を使用する場合に発生する可能性があります。
2 に対処するには、WorkManager への移行を検討してください。
onStartJob
または onStopJob
での処理をラップするためのサポート
呼び出されることがあります
JobScheduler
には、サービス アカウント キーの
ACCESS_NETWORK_STATE
権限(setRequiredNetworkType
または
setRequiredNetwork
制約。アプリがジョブのスケジュール設定時に ACCESS_NETWORK_STATE
権限を宣言しておらず、Android 14 以降をターゲットとしている場合、SecurityException
が発生します。
Tiles launch API
Android 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.
ユーザー エクスペリエンス
全画面インテントの通知を保護する
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.
セキュリティ
暗黙的インテントとペンディング インテントの制限
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.
バックグラウンドからのアクティビティの起動に関する追加の制限
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
PendingIntent
usingPendingIntent#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 anActivityOptions
bundle 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_STARTS
flag 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 パス トラバーサル
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 キャプチャ セッションごとにユーザーの同意が必要
Android 14(API レベル 34)以降をターゲットとするアプリでは、次のいずれかのシナリオで MediaProjection#createVirtualDisplay
によって SecurityException
がスローされます。
- アプリは、
MediaProjectionManager#createScreenCaptureIntent
から返されたIntent
をキャッシュに保存し、MediaProjectionManager#getMediaProjection
に複数回渡します。 - アプリが同じ
MediaProjection
インスタンスでMediaProjection#createVirtualDisplay
を複数回呼び出している。
アプリは、各キャプチャ セッションの前にユーザーに同意を求める必要があります。1 回の回収セッションは MediaProjection#createVirtualDisplay
の 1 回の呼び出しであり、各 MediaProjection
インスタンスは 1 回だけ使用する必要があります。
構成の変更に対処する
アプリで MediaProjection#createVirtualDisplay
を呼び出して、構成の変更(画面の向きや画面サイズの変更など)を処理する必要がある場合は、次の手順に沿って既存の MediaProjection
インスタンスの VirtualDisplay
を更新します。
- 新しい幅と高さで
VirtualDisplay#resize
を呼び出します。 - 新しい幅と高さを持つ新しい
Surface
をVirtualDisplay#setSurface
に指定します。
コールバックを登録する
アプリは、キャプチャ セッションを続行するための同意をユーザーが許可しなかった場合のケースを処理するコールバックを登録する必要があります。これを行うには、Callback#onStop
を実装し、関連するリソース(VirtualDisplay
や Surface
など)をアプリで解放します。
アプリがこのコールバックを登録していない場合、アプリが呼び出すと MediaProjection#createVirtualDisplay
は IllegalStateException
をスローします。
非 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.
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.