rakuishi.com

Android 14(API レベル 34)にアプリを移行する

Android 14 対応は、主にセキュリティ関連の変更が必要となります。この記事では、対応した移行項目とフォアグラウンドサービスのリジェクトに対する修正内容を共有します。

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

フォアグラウンドサービスを利用している場合、foregroundServiceType の定義が必須となりました。既に定義されていた location フォアグラウンドサービスタイプなどに加え、いくつかのタイプが追加されています。

  • camera
  • connectedDevice
  • dataSync
  • health
  • location
  • mediaPlayback
  • mediaProjection
  • microphone
  • phoneCall
  • remoteMessaging
  • shortService
  • specialUse
  • systemExempted

定義するには AndroidManifest.xml 内の uses-permission, service 両方に書く必要があります。

<uses-permission
    android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />

<service
    android:name=".DataSyncService"
    android:exported="false"
    android:foregroundServiceType="dataSync" />

また、Android 14 対応(targetSdk = 34)ビルドを提出した際に、フォアグラウンドサービスタイプごとに、その利用箇所の動画の提出が必要になります。これは uses-permission を見て判断しています。

自分は Android のスクリーンレコード機能で撮影し、Google Drive にアップロードして公開リンクを作成し、提出を行いました。

この審査は厳密に行われており、仕事のアプリではいくつかの理由からリジェクトされました。その修正内容を共有します。

  • dataSync フォアグラウンドサービスの利用は不適切
    • 公式ドキュメント内に Data upload or download の用途は dataSync が適切と書いていたのですが、そもそもフォアグラウンドサービスで行う必要がないと指摘されました。意義申し立ては通らなかったため、通常のサービスに修正しました
  • フォアグラウンドサービスが利用されていることをユーザーに周知する
    • 位置情報の権限がある場合は、location のフォアグラウンドサービスを立ち上げていたのですが、ユーザーに周知させる必要があると指摘されました。location のフォアグラウンドサービスの初回起動時にダイアログを表示して、ユーザーが「フォアグラウンド」のサービスであることを学習できるように修正しました
    • また、アプリ内のダイアログに加え、ストアの説明文にもフォアグラウンドの位置情報を取得することを説明する旨を追加しました

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

ブロードキャストレシーバーを利用している場合 registerReceiver(receiver, filter) を呼ぶと SecurityException が発生するようになりました。registerReceiver(receiver, filter, RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED) に置き換える必要があります。

ただし、システムブロードキャストを利用している場所は対応する必要がありません。仕事のプロジェクトでは、次のように、対応する必要があるレシーバーと、必要がないレシーバーがありました。

  • 対応する必要あり(ブロードキャスト)
    • com.google.android.gms.auth.api.phone.SMS_RETRIEVED
  • 対応する必要なし(システムブロードキャスト)
    • android.bluetooth.adapter.action.STATE_CHANGED

また下方互換のために次のような関数を用意しました。

/**
 * Android 14: 実行時に登録されるブロードキャストレシーバでは、エクスポート動作を指定する必要がある
 * https://developer.android.com/about/versions/14/behavior-changes-14?hl=ja#runtime-receivers-exported
 */
@SuppressLint("UnspecifiedRegisterReceiverFlag")
fun ContextWrapper.registerExportedReceiver(
        receiver: BroadcastReceiver?,
        filter: IntentFilter,
): Intent? {
    return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
        registerReceiver(receiver, filter, RECEIVER_EXPORTED)
    } else {
        registerReceiver(receiver, filter)
    }
}

参考