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

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

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

コア機能

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

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

アプリのユースケースがこれらのタイプのいずれにも関連していない場合は、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 将继续更新 Android 的核心库,以与最新 OpenJDK LTS 版本中的功能保持一致,包括适合应用和平台开发者的库更新和 Java 17 语言支持。

以下变更可能会影响应用兼容性:

  • 对正则表达式的更改:现在,为了更严格地遵循 OpenJDK 的语义,不允许无效的组引用。您可能会看到 java.util.regex.Matcher 类抛出 IllegalArgumentException 的新情况,因此请务必测试应用中使用正则表达式的情形。如需在测试期间启用或停用此变更,请使用兼容性框架工具切换 DISALLOW_INVALID_GROUP_REFERENCE 标志。
  • UUID 处理:现在,验证输入参数时,java.util.UUID.fromString() 方法会执行更严格的检查,因此您可能会在反序列化期间看到 IllegalArgumentException。如需在测试期间启用或停用此变更,请使用兼容性框架工具切换 ENABLE_STRICT_VALIDATION 标志。
  • ProGuard 问题:有时,在您尝试使用 ProGuard 缩减、混淆和优化应用时,添加 java.lang.ClassValue 类会导致问题。问题源自 Kotlin 库,该库会根据 Class.forName("java.lang.ClassValue") 是否会返回类更改运行时行为。如果您的应用是根据没有 java.lang.ClassValue 类的旧版运行时开发的,则这些优化可能会将 computeValue 方法从派生自 java.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 のリリース API

对于以 Android 14 及更高版本为目标平台的应用, TileService#startActivityAndCollapse(Intent) 已弃用,现在会抛出 调用时抛出异常。如果您的应用从功能块启动 activity,请使用 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 を使用して、ユーザーが権限を付与できる設定ページを起動できます。

セキュリティ

暗黙的インテントとペンディング インテントの制限

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>

アプリが暗黙的インテントを使用してこのアクティビティを起動しようとすると、ActivityNotFoundException 例外がスローされます。

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

エクスポートされていないアクティビティをアプリが起動するには、代わりに明示的インテントを使用する必要があります。

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.

バックグラウンドからのアクティビティの起動に関する追加の制限

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() を呼び出すことで、この検証をオプトアウトできます。

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.

非 SDK の制限の更新

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

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

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

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