動作の変更点: Android 14 以上をターゲットとするアプリ

これまでのリリースと同様、Android 14 には、アプリに影響する可能性がある動作変更が含まれています。下記の動作変更は、Android 14(API レベル 34)以降をターゲットとするアプリにのみ適用されます。アプリが Android 14 以上をターゲットとする場合は、必要に応じてアプリを変更し、下記の動作に適切に対応できるようにしてください。

アプリの targetSdkVersion に関係なく、Android 14 で実行されるすべてのアプリに影響する動作変更のリストも必ずご確認ください。

コア機能

フォアグラウンド サービス タイプは必須

Android 14(API レベル 34)以降をターゲットとするアプリの場合、アプリ内のフォアグラウンド サービスごとに少なくとも 1 つのフォアグラウンド サービス タイプを指定する必要があります(アプリのユースケースを表すフォアグラウンド サービス タイプを選択します)。システムは、特定のタイプのフォアグラウンド サービスが、特定のユースケースを満たすことを想定しています。

アプリのユースケースがこれらのタイプのいずれにも関連していない場合は、WorkManager またはユーザーが開始するデータ転送ジョブを使用するようにロジックを移行することを強くおすすめします。

BluetoothAdapter での BLUETOOTH_CONNECT 権限の適用

Android 14 では、Android 14(API レベル 34)以降をターゲットとするアプリで BluetoothAdapter getProfileConnectionState() メソッドを呼び出すときに、BLUETOOTH_CONNECT 権限が適用されます。

このメソッドにはすでに BLUETOOTH_CONNECT 権限が必要でしたが、適用されていませんでした。次のスニペットに示すように、アプリの AndroidManifest.xml ファイルで BLUETOOTH_CONNECT を宣言し、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 をトリガーします。

この 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 では、選択した写真へのアクセスが導入されています。これにより、ユーザーは特定の種類のすべてのメディアへのアクセス権をアプリに付与するのではなく、ライブラリ内の特定の画像や動画へのアクセス権をアプリに付与できるようになりました。

この変更は、アプリが Android 14(API レベル 34)以降をターゲットとしている場合にのみ有効になります。まだ写真選択ツールを使用していない場合は、アプリに実装することをおすすめします。これにより、画像と動画を選択する際の一貫したエクスペリエンスを提供できるほか、ストレージの権限をリクエストしなくてもユーザーのプライバシーを保護できます。

ストレージ権限を使用して独自のギャラリー選択ツールを維持し、実装を完全に制御する必要がある場合は、新しい READ_MEDIA_VISUAL_USER_SELECTED 権限を使用するように実装を調整してください。アプリが新しい権限を使用していない場合、システムはアプリを互換モードで実行します。

ユーザー エクスペリエンス

全画面インテントの通知を保護する

在 Android 11(API 级别 30)中,任何应用都可以在手机处于锁定状态时使用 Notification.Builder.setFullScreenIntent 发送全屏 intent。您可以通过在 AndroidManifest 中声明 USE_FULL_SCREEN_INTENT 权限,在应用安装时自动授予此权限。

全屏 intent 通知适用于需要用户立即注意的极高优先级通知,例如用户来电或用户配置的闹钟设置。对于以 Android 14(API 级别 34)或更高版本为目标平台的应用,获准使用此权限的应用仅限于提供通话和闹钟的应用。对于不适合此情况的任何应用,Google Play 商店会撤消其默认的 USE_FULL_SCREEN_INTENT 权限。这些政策变更的截止日期为 2024 年 5 月 31 日

在用户更新到 Android 14 之前,在手机上安装的应用仍拥有此权限。用户可以开启和关闭此权限。

您可以使用新 API NotificationManager.canUseFullScreenIntent 检查应用是否具有该权限;如果没有,应用可以使用新 intent 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);

実行時に登録されるブロードキャスト レシーバでは、エクスポート動作を指定する必要がある

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)或更高版本为目标平台的应用,系统会进一步限制允许应用在后台启动 activity 的时间:

这些更改扩大了一组现有限制条件的范围,目的是防止恶意应用滥用 API 以在后台启动干扰性活动,从而保护用户。

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

Android 14(API レベル 34)以降をターゲットとするアプリでは、次のいずれかのシナリオで MediaProjection#createVirtualDisplay によって SecurityException がスローされます。

アプリは、各キャプチャ セッションの前にユーザーに同意を求める必要があります。1 回の回収セッションは MediaProjection#createVirtualDisplay の 1 回の呼び出しであり、各 MediaProjection インスタンスは 1 回だけ使用する必要があります。

構成の変更に対処する

アプリで MediaProjection#createVirtualDisplay を呼び出して、構成の変更(画面の向きや画面サイズの変更など)を処理する必要がある場合は、次の手順に沿って既存の MediaProjection インスタンスの VirtualDisplay を更新します。

  1. 新しい幅と高さで VirtualDisplay#resize を呼び出します。
  2. 新しい幅と高さを持つ新しい SurfaceVirtualDisplay#setSurface に指定します。

コールバックを登録する

アプリは、キャプチャ セッションを続行するための同意をユーザーが許可しなかった場合のケースを処理するコールバックを登録する必要があります。これを行うには、Callback#onStop を実装し、関連するリソース(VirtualDisplaySurface など)をアプリで解放します。

アプリがこのコールバックを登録していない場合、アプリが呼び出すと MediaProjection#createVirtualDisplayIllegalStateException をスローします。

非 SDK の制限の更新

Android 14 包含更新后的受限非 SDK 接口列表(基于与 Android 开发者之间的协作以及最新的内部测试)。在限制使用非 SDK 接口之前,我们会尽可能确保有可用的公开替代方案。

如果您的应用并非以 Android 14 为目标平台,其中一些变更可能不会立即对您产生影响。然而,虽然您目前仍可以使用一些非 SDK 接口(具体取决于应用的目标 API 级别),但只要您使用任何非 SDK 方法或字段,终归存在导致应用出问题的显著风险。

如果您不确定自己的应用是否使用了非 SDK 接口,则可以测试您的应用来进行确认。如果您的应用依赖于非 SDK 接口,您应该开始计划迁移到 SDK 替代方案。然而,我们知道某些应用具有使用非 SDK 接口的有效用例。如果您无法为应用中的某项功能找到使用非 SDK 接口的替代方案,应请求新的公共 API

Android の今回のリリースの変更について詳しくは、非 SDK インターフェースの制限に関する Android 14 での変更点をご覧ください。非 SDK インターフェース全般について詳しくは、非 SDK インターフェースの制限をご覧ください。