以前のリリースと同様に、Android 14 には、アプリに影響する可能性がある動作変更が含まれています。以下の動作変更は、Android 14(API レベル 34)以降をターゲットとするアプリにのみ適用されます。Android 14 以降をターゲットとするアプリの場合は、必要に応じてアプリを修正し、下記の動作に適切に対応する必要があります。
アプリの targetSdkVersion
に関係なく、Android 14 で実行されるすべてのアプリに影響する動作変更のリストも必ずご確認ください。
コア機能
フォアグラウンド サービス タイプは必須
Android 14(API レベル 34)以降をターゲットとするアプリの場合は、アプリ内のフォアグラウンド サービスごとにフォアグラウンド サービス タイプを 1 つ以上指定する必要があります。フォアグラウンド サービスのタイプには、アプリのユースケースを表すものを選択する必要があります。システムは、特定のタイプのフォアグラウンド サービスが特定のユースケースを満たすことを想定しています。
アプリのユースケースがこれらのタイプのいずれにも関連していない場合は、WorkManager またはユーザーが開始するデータ転送ジョブを使用するようにロジックを移行することを強くおすすめします。
BluetoothAdapter での BLUETOOTH_CONNECT 権限の適用
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 では、最新の OpenJDK LTS リリースの機能に合わせて Android のコアライブラリを更新する取り組みが引き続き行われています。これには、アプリ デベロッパーとプラットフォーム デベロッパー向けのライブラリの更新と 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 を使用するアプリの圧縮、難読化、最適化をしようとすると問題が発生することがあります。この問題は、Class.forName("java.lang.ClassValue")
がクラスを返すかどうかに基づいてランタイムの動作を変更する Kotlin ライブラリに起因します。java.lang.ClassValue
クラスを利用できない古いバージョンのランタイムを対象にアプリが開発されている場合、これらの最適化により、java.lang.ClassValue
から派生したクラスからcomputeValue
メソッドが削除されることがあります。
JobScheduler がコールバックとネットワークの動作を強化する
導入後、JobScheduler は、アプリが数秒以内に onStartJob
または onStopJob
から戻ることを想定しています。Android 14 より前では、実行時間が長すぎるとジョブは停止し、通知なく失敗します。Android 14(API レベル 34)以降をターゲットとするアプリがメインスレッドで許可された時間を超えた場合、アプリは「onStartJob
への応答がありません」または「onStopJob
への応答がありません」というエラー メッセージを表示して ANR をトリガーします。非同期処理をサポートするか、負荷の高い作業をバックグラウンド スレッドに移行する WorkManager への移行を検討してください。
JobScheduler
では、setRequiredNetworkType
または setRequiredNetwork
の制約を使用する場合に、ACCESS_NETWORK_STATE
権限を宣言する要件も導入されます。ジョブのスケジュール時にアプリが ACCESS_NETWORK_STATE
権限を宣言せず、Android 14 以降をターゲットとしている場合、SecurityException
が発生します。
Tiles API のリリース
バージョン 14 以降をターゲットとするアプリの場合、TileService#startActivityAndCollapse(Intent)
が非推奨になり、呼び出されると例外がスローされるようになりました。アプリがタイルからアクティビティを起動する場合は、代わりに TileService#startActivityAndCollapse(PendingIntent)
を使用します。
プライバシー
写真と動画への部分的なアクセス
Android 14 では、Selected Photos Access が導入され、特定のタイプのすべてのメディアへのアクセス権を付与するのではなく、ライブラリ内の特定の画像および動画へのアクセス権をアプリに付与できるようになりました。
この変更は、アプリが Android 14(API レベル 34)以降をターゲットとする場合にのみ有効になります。写真選択ツールをまだ使用していない場合は、アプリに実装して、ストレージの権限をリクエストしなくても、画像や動画を選択する際に一貫性のあるエクスペリエンスを提供することをおすすめします。これにより、ユーザーのプライバシーも強化されます。
ストレージの権限を使用して独自のギャラリー選択ツールを管理しており、実装を完全に制御する必要がある場合は、新しい READ_MEDIA_VISUAL_USER_SELECTED
権限を使用するように実装を調整します。アプリが新しい権限を使用しない場合、システムはアプリを互換モードで実行します。
ユーザー エクスペリエンス
安全な全画面インテント通知
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
を使用して、ユーザーが権限を付与できる設定ページを起動できます。
セキュリティ
暗黙的インテントとペンディング インテントの制限
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>
アプリが暗黙的インテントを使用してこのアクティビティを起動しようとすると、例外がスローされます。
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"));
エクスポートされていないアクティビティをアプリが起動するには、代わりに明示的インテントを使用する必要があります。
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
メソッドを介してシステム ブロードキャストのレシーバのみを登録する場合は、レシーバの登録時にフラグを指定しないでください。
動的コードの読み込みの安全性を改善
アプリが 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);
既存の動的読み込みファイルを処理する
既存の動的読み込みファイルに対して例外がスローされないようにするには、ファイルを削除して再作成してから、アプリでファイルを動的に読み込み直すことをおすすめします。ファイルを再作成するときは、上記のガイダンスに沿って書き込み時にファイルを読み取り専用としてマークしてください。既存のファイルに読み取り専用として再度ラベルを付けることもできますが、その場合は、信頼できる値に照らしてファイルの署名を確認するなど、最初にファイルの整合性を確認することを強くおすすめします。これにより、悪意のあるアクションからアプリを保護できます。
バックグラウンドからのアクティビティの起動に関する追加の制限
Android 14(API レベル 34)以降をターゲットとするアプリでは、アプリがバックグラウンドからアクティビティを開始できるタイミングがさらに制限されます。
- アプリが
PendingIntent#send()
などのメソッドを使用してPendingIntent
を送信する場合、ペンディング インテントを開始するために独自のバックグラウンド アクティビティの起動権限を付与する場合は、オプトインする必要があります。オプトインするには、アプリでsetPendingIntentBackgroundActivityStartMode(MODE_BACKGROUND_ACTIVITY_START_ALLOWED)
を含むActivityOptions
バンドルを渡す必要があります。 - 可視アプリが、
bindService()
メソッドを使用してバックグラウンドにある別のアプリのサービスをバインドする際、バインドされたサービスに独自のバックグラウンド アクティビティの起動権限を付与する場合、可視アプリはオプトインが必要になります。オプトインするには、アプリでbindService()
メソッドを呼び出すときにBIND_ALLOW_ACTIVITY_STARTS
フラグを含める必要があります。
この変更により、既存の制限セットが拡張され、悪意のあるアプリが API を悪用してバックグラウンドから破壊的なアクティビティを開始することを防止し、ユーザーを保護します。
zip パス トラバーサル
Android 14(API レベル 34)以降をターゲットとするアプリの場合、Android は次の方法で ZIP パス トラバーサルの脆弱性を回避します。ZIP ファイルのエントリ名に「..」が含まれるか「/」で始まる場合、ZipFile(String)
と ZipInputStream.getNextEntry()
は ZipException
をスローします。
アプリは dalvik.system.ZipPathValidator.clearCallback()
を呼び出すことで、この検証をオプトアウトできます。
MediaProjection のキャプチャ セッションごとにユーザーの同意が必要です
Android 14(API レベル 34)以降をターゲットとするアプリでは、次のいずれかのシナリオにおいて SecurityException
が MediaProjection#createVirtualDisplay
によってスローされます。
- アプリは、
MediaProjectionManager#createScreenCaptureIntent
から返されたIntent
をキャッシュに保存し、MediaProjectionManager#getMediaProjection
に複数回渡します。 - アプリが同じ
MediaProjection
インスタンスでMediaProjection#createVirtualDisplay
を複数回呼び出している。
アプリは、各キャプチャ セッションの前にユーザーに同意を求める必要があります。単一キャプチャ セッションは、MediaProjection#createVirtualDisplay
に対する 1 回の呼び出しです。各 MediaProjection
インスタンスは 1 回だけ使用する必要があります。
構成の変更に対処する
アプリで構成の変更(画面の向きや画面サイズの変更など)を処理するために MediaProjection#createVirtualDisplay
を呼び出す必要がある場合は、次の手順で既存の MediaProjection
インスタンスの VirtualDisplay
を更新できます。
- 新しい幅と高さで
VirtualDisplay#resize
を呼び出します。 - 新しい幅と高さが設定された新しい
Surface
をVirtualDisplay#setSurface
に指定します。
コールバックを登録する
アプリはコールバックを登録して、ユーザーがキャプチャ セッションの続行に同意しない場合に対処する必要があります。そのためには、Callback#onStop
を実装し、アプリで関連リソース(VirtualDisplay
や Surface
など)を解放します。
アプリがこのコールバックを登録しない場合、MediaProjection#createVirtualDisplay
は、アプリがこのコールバックを呼び出したときに IllegalStateException
をスローします。
非 SDK の制限の更新
Android 14 では、Android デベロッパーの協力と直近の内部テストに基づいて、制限を受ける非 SDK インターフェースのリストが更新されています。Google は、非 SDK インターフェースを制限する前に、可能な限り、その代わりとなる公開インターフェースを利用可能にしています。
Android 14 をターゲットとしないアプリでは、この変更の一部はすぐには影響しない可能性があります。ただし、現時点で(アプリのターゲット API レベルに応じて)一部の非 SDK インターフェースを利用できていても、非 SDK のメソッドまたはフィールドをそのまま使用し続けると、将来的にアプリが機能しなくなるリスクが高くなります。
アプリが非 SDK インターフェースを使用しているかどうか不明な場合は、アプリをテストして確認できます。アプリが非 SDK インターフェースに依存している場合は、SDK の代替インターフェースへの移行を計画してください。ただし Google も、一部のアプリには非 SDK インターフェースを使用する正当なユースケースがあると承知しています。アプリの機能に使用している非 SDK インターフェースの代わりが見つからない場合は、新しい公開 API をリクエストしてください。
Android の今回のリリースの変更について詳しくは、非 SDK インターフェースの制限に関する Android 14 での変更点をご覧ください。非 SDK インターフェース全般について詳しくは、非 SDK インターフェースの制限をご覧ください。