Android 14 開発中のバージョン差異にどう対応するか?Android 14迅速適応ガイド!
- システムはもはや正確なアラームの権限をデフォルトで付与しなくなり、アプリケーションが自ら申請する必要があります。
- Android 14 は target SDK >= 33 のアプリに対して権限
SCHEDULE_EXACT_ALARM
をデフォルトで同意しなくなりました
(デフォルトでは拒否
に設定されています) - これは以前の Android 12(以前のノート)で追加された権限です
当時は AndroidManifest.xml に宣言するだけで済みましたが 今は
権限をリクエスト
する必要があります権限をリクエスト
する手順:AlarmManager.canScheduleExactAlarms()
を使用して権限があるかどうかを確認しますonResume()
内でACTION_REQUEST_SCHEDULE_EXACT_ALARM
を含むインテントを呼び出します- 例:
override fun onResume() { … if (alarmManager.canScheduleExactAlarms()) { // Set exact alarms. alarmManager.setExact(...) } else { // Permission not yet approved. Display user notice and revert to a fallback // approach. alarmManager.setWindow(...) } }
-
公式も、正確なアラームを使用する必要がない場合は削除することを推奨しています:こちらをクリック
- アプリが
キャッシュ状態(cached state)
に入ると、コンテキストで登録されたブロードキャストはキューに追加されます。- この時に受信したブロードキャストはキューに入り、次にアプリが
前景
に戻るかキャッシュ状態
を離れると、順次アプリに返されます。 - キャッシュ状態(cached state):簡単に言うと、バックグラウンドにあるアプリで、現在前景にないプロセスです。そのため、システムの他の部分がメモリを必要とする場合、システムは必要に応じてこれらのプロセスを自由に終了できます。
- コンテキストで登録されたものだけが対象で、
静的に登録されたものは対象外
です。例:AndroidManifest.xml にブロードキャストを追加する場合。
- この時に受信したブロードキャストはキューに入り、次にアプリが
- Android 14 は target SDK >= 33 のアプリに対して権限
- アプリは自分自身のバックグラウンドプロセスのみを終了でき、他のアプリには影響を与えられません。
- target SDK 34 以降、
killBackgroundProcesses
を使用して他のアプリを閉じることはできなくなります - もしアプリ内でこの方法を使用して他のアプリのバックグラウンドプロセスを閉じている場合、将来的には無効になる可能性があります
- target SDK 34 以降、
- 最初に MTU をリクエストする GATT クライアントの MTU 設定は
517
バイトに設定され、その ACL 接続に対するすべての後続の MTU リクエストは無視
されます。- 簡単に言うと、アプリ内の GATT クライアントが作成され接続された後(BluetoothGatt#connect)、
最初に APIBluetoothGatt#requestMtu(int)
を使用して MTU を設定すると、システムは 517 バイトに設定します - 関連知識:
MTU(Maximum Transmission Unit)
:単一のパケットで送信できる最大データ量
Bluetooth Core Specification 5.2
:この変更はこの規格を厳格に遵守するために行われました。規格を見る - 将来的に GATT 接続を実装して
Bluetooth デバイス
に接続し、MTU を設定する場合は:このチュートリアル を参照してください もし製品が実際に Bluetooth デバイスとデータを転送する場合
、
MTU の制限により多くの状況に適応する必要があるかもしれません。
例:GATT で MTU を設定するが、ターゲットの Bluetooth デバイスがサポートしていない場合、対応策が必要になるかもしれません
または、Android 14 の調整により Bluetooth デバイスのファームウェアも調整が必要になるなど…
- 簡単に言うと、アプリ内の GATT クライアントが作成され接続された後(BluetoothGatt#connect)、
- 新しい権限により、ユーザーはどの
写真
やビデオ
がアクセスされるかを選択できます。- Android 14では、新しい
写真選択
権限READ_MEDIA_VISUAL_USER_SELECTED
が追加されました。 -
READ_MEDIA_VISUAL_USER_SELECTED
を使用すると、ユーザーはアクセスされる写真
やビデオ
を選択
できます。
または、すべてのアクセスを許可することもできます。以下の図のように、ユーザーには2つのオプションが表示されます。
Android 13
では、すでに一度権限が細分化されており、
READ_MEDIA_VIDEO
、READ_MEDIA_IMAGES
を使用してすべて
の画像やビデオにアクセスできます:以前のAndroid 13のメモ<!-- Devices running Android 12L (API level 32) or lower --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" /> <!-- Devices running Android 13 (API level 33) or higher --> <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" /> <!-- To handle the reselection within the app on Android 14 (API level 34) --> <uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" />
-
上記の方法はニーズに応じて調整できますが、 Android 14で
READ_MEDIA_VISUAL_USER_SELECTED
を使用しない場合は、互換モードが適用されます。 - 互換モード:毎回、どの写真やビデオがアプリで使用されるかをユーザーに選択させる。
-
公式の説明では、これは
ユーザープライバシー
を向上させる方法の一つです。 - 実際の例:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { requestPermissions.launch(arrayOf(READ_MEDIA_IMAGES, READ_MEDIA_VIDEO, READ_MEDIA_VISUAL_USER_SELECTED)) } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { requestPermissions.launch(arrayOf(READ_MEDIA_IMAGES, READ_MEDIA_VIDEO)) } else { requestPermissions.launch(arrayOf(READ_EXTERNAL_STORAGE)) }
または公式の権限取得例を参照:こちら
- 新しい動作:
閉じられない
通知がユーザーによって閉じられる
ようになりました。公式ドキュメント- Android 14では、この動作が変更され、ユーザーがこの種の通知を閉じることができるようになりました。
- 簡単に言うと、現在
Notification.Builder#setOngoing(true)
、NotificationCompat.Builder#setOngoing(true)
を使用してNotification.FLAG_ONGOING_EVENTを設定し、ユーザーが前景通知を閉じるのを防ぐことが無効
になります。 - 実際の効果はこのようになります(左:Android 14、右:Android 11):
- ただし、以下の2つの状況ではまだ禁止されていません:
- デバイスがロックされている場合
- ユーザーが
すべての通知をクリア
を選択した場合
- また、以下のタイプには現在影響しません:
- CallStyle通知:簡単に言うと
電話関連
の通知 例:val builder = NotificationCompat.Builder(context, CHANNEL_ID) .setSmallIcon(R.drawable.notification_icon) .setContentTitle("Incoming call") .setContentText("Caller Name") .setPriority(NotificationCompat.PRIORITY_HIGH) .setCategory(NotificationCompat.CATEGORY_CALL) .setFullScreenIntent(fullScreenPendingIntent, true) .setOngoing(true) .addAction(R.drawable.ic_accept, "Accept", acceptPendingIntent) .addAction(R.drawable.ic_decline, "Decline", declinePendingIntent) .setStyle(new NotificationCompat.DecoratedCustomViewStyle())
- 企業デバイスポリシーコントローラー (DPC) とサポートソフトウェアパッケージ
- CallStyle通知:簡単に言うと
- Android 14では、新しい
- 非線形フォントの拡大が200%まで可能に。
- Androidシステムが
テキストフォント
を最大200%まで拡大できるように調整されました。
適応されていない場合、フォントが拡大されるとレイアウトが予期しない形になる可能性があります。 - 以下の方法で適応できます:
- a. 常に
sp
を使用してテキストフォントサイズを設定する - b. コードで設定することも可能です
TypedValue.applyDimension()
を使用:spをピクセルに変換TypedValue.deriveDimension()
を使用:ピクセルをspに変換 - c. (オプション)
lineHeight
にsp単位を使用:dpまたはpx単位を使用する場合
この場合、テキストが拡大されないか、文字が狭く見える可能性があります。
textSizeとlineHeightの両方にspを設定することで、
システムが設定に応じて行の高さとフォントサイズを調整します。 (主に自社製品で行の高さを設定する必要があるかどうか
) - d. 公式ドキュメント
- a. 常に
- テスト方法:
設定
>アクセシビリティ
>表示サイズとテキスト
フォントサイズオプションで、プラス(+)アイコンをクリックし、最大倍率に調整します。 コンパイル
にはほとんど影響はありませんが、追加の適応が必要な場合があります。 古い方法ではフォント設定にdpを使用している場合や、dpをピクセルに変換する
コードが見られます。 そのため、見つけた場合は公式の推奨方法に変更する習慣をつけると良いでしょう。
- Androidシステムが
targetSdkVersion
API レベルの最低インストール要件が23
に引き上げられました。-
主に
Android 14
以降では targetSdkVersion>= 23
のアプリのみインストール可能です。 -
ただし、開発時に単純にテストする場合は以下のadbコマンドを使用できます:
adb install --bypass-low-target-sdk-block ファイル名.apk
-
公式の主な目的は、
悪意のあるソフトウェア
が古いバージョンを利用して新しいバージョンのセキュリティ制約を回避するのを防ぐことです。
例えば:targetSDK 22を利用して、Android 6.0 Marshmallow (API 23)
の権限リクエスト制限を回避することなど。
-
- Google Play上で表示されるデータセキュリティ情報が調整されました:提供された情報に基づいて表示されますが、
アプリ自体にはコンパイル上の影響はありません。主に公開後のページ上で表示される情報です。
この点については、各自のアプリや製品が受け入れるかどうか、または調整が必要かどうかを確認してください。参考URL:こちら - システム通知:現在、アプリが位置情報を第三者のライブラリに共有している場合、ユーザーに通知されます。30日以内にどのアプリが位置情報を第三者に共有したかが通知されます。
- メディアストレージのクエリで特定のメディアファイルを保存しているアプリケーションの OWNER_PACKAGE_NAME フィールドを使用できます。
Android 14 バージョン以降、以下の条件のいずれかを満たさない限り、システムはこの値を隠します:- メディアファイルを保存するアプリケーションが他のアプリケーションから常に閲覧可能なパッケージ名を持っている。
- メディアストレージをクエリするアプリケーションが QUERY_ALL_PACKAGES 権限を要求している。
- Android 14(API レベル 34)またはそれ以上をターゲットとするアプリに対して
Android 14 は BluetoothAdapter#getProfileConnectionState() メソッドを呼び出す際にBLUETOOTH_CONNECT
権限を強制します。
- 使用する場合は
AndroidManifest.xml
にその権限を追加する必要があります。 - 使用前に権限が付与されているか確認してください。
- 使用する場合は
-
JobScheduler が
setRequiredNetworkType
またはsetRequiredNetwork
を使用する場合、
現在はACCESS_NETWORK_STATE
権限を宣言する必要があります。
そうしないと、Android 14 以降では SecurityException が発生します。 インプリシット
インテントと保留中のインテントの制限:Android 14 ではインプリシットインテントの使用に制限が追加されました。- インプリシットインテントは現在、エクスポートされたコンポーネントでのみ使用できます。
exported = true
を設定するか、エクスプリシットインテント
を使用してください。
例:エクスプリシットインテント
を使用するか、exported = true
を設定する
<activity android:name=".MyActivity" android:exported="true"> <intent-filter> <action android:name="com.example.action.APP_ACTION" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity>
// This makes the intent explicit. val explicitIntent = Intent("com.example.action.APP_ACTION") explicitIntent.apply { package = context.packageName } context.startActivity(explicitIntent)
mutable
保留中のインテントを使用する際にパッケージ名を指定しない場合、例外が発生する可能性があります。
例外の例として、FLAG_MUTABLE
の保留中のインテントを使用する場合:
Intent intent = new Intent(Intent.ACTION_VIEW); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_MUTABLE);
例外を避けるためにパッケージ名を追加する:
Intent intent = new Intent(Intent.ACTION_VIEW); intent.setPackage("com.example.myapp"); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_MUTABLE);
- インプリシットインテントは現在、エクスポートされたコンポーネントでのみ使用できます。
- 現在、コンテキストに登録されたBroadcast receiverにフラグを追加する必要があります。
RECEIVER_EXPORTED
またはRECEIVER_NOT_EXPORTED
を使用して、アプリケーションをセキュリティ脆弱性から保護します。
val receiverFlags = if (listenToBroadcastsFromOtherApps) {
ContextCompat.RECEIVER_EXPORTED
} else {
ContextCompat.RECEIVER_NOT_EXPORTED
}
ContextCompat.registerReceiver(context, br, filter, receiverFlags)
- 現在使用動的コードを読み込むファイルは
read only
に設定する必要があります
そうしないと例外が発生します
これは公式がその安全性を考慮しているためです- 既存の読み込みファイルに対する公式の提案
- 動的にコードを読み込む必要がある場合は、以下の方法を使用してください
動的ファイル(例えば DEX、JAR または APK ファイル)を開いて内容を書き込む前にすぐにそれをread onlyに設定します:
val jar = File("DYNAMICALLY_LOADED_FILE.jar") val os = FileOutputStream(jar) os.use { // レースコンディションを防ぐために最初にファイルを読み取り専用に設定 jar.setReadOnly() // その後、実際のファイル内容を書き込む } val cl = PathClassLoader(jar, parentClassLoader)
- zipトラバーサルの脆弱性を防ぐため
現在、ZipInputStream.getNextEntry()を使用する際、パスに..
、/
が含まれているとZipException
がスローされます
- この検証ステップをスキップしたい場合は、
dalvik.system.ZipPathValidator.clearCallback()
を直接呼び出すことができます。
- この検証ステップをスキップしたい場合は、
USE_FULL_SCREEN_INTENT
:Android 11以上では全画面通知を表示するために使用されます
しかし、Android 14では、通話
およびアラーム
タイプのアプリでのみ使用できます
2024/05/31以降、Google Playはこの権限を使用する他のアプリを撤回します。
- 現在、API
NotificationManager#canUseFullScreenIntent()
を使用して全画面通知を使用できるかどうかを確認できます。 - 権限がない場合は、
ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT
権限を要求できます。
- 現在、API
- 現在、foregroundServiceを使用する場合は必ず
android:foregroundServiceType
属性を宣言する必要があります- この属性はAndroid 10で導入されましたが、現在はAndroid 14で必ず宣言する必要があります。そうしないとエラーが発生します。
- 公式は開発者が宣言するための
13
種類のタイプを提供しています。詳細はドキュメントを参照してください。 - 公式の提案として、
上記のタイプに関連しない場合
は、ロジックをWorkManager
またはユーザーが起動するデータ転送操作
に移行することができます。 -
上記のタイプを宣言する場合、各タイプに必要な権限は異なります。例えば、私たちのプロジェクトでよく使用される
mediaProjection
の場合、以下の手順を完了する必要があります:
a. AndroidManifest.xmlでandroid:foregroundServiceType mediaProjection
を宣言する
b. 元のForegroundService権限に加えて、FOREGROUND_SERVICE_MEDIA_PROJECTION
権限も宣言する必要があります
c. startForegroundを実行する前に、createScreenCaptureIntent()
メソッドを使用してユーザーの権限を確認し、foreground serviceを起動できるようにする必要があります。
d. startForegroundを呼び出す際に、FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION
を追加する必要がありますstartForeground( AIRSOS_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION)
- 他のタイプも同様の概念ですが、宣言する権限や追加するServiceInfoはタイプによって異なるため、上記のドキュメントを参照してください。
-
上記以外にも、現在
AirDroid Remote Support
を使用してこの問題を調整するためのいくつかの方向性があります a. この問題を回避するためにdataSync
タイプを使用する。
dataSync
は実行時
に追加の権限を取得する必要がないため、元のフローにあまり影響を与えません。
->リスク
:すべてをdataSync
に宣言すると、短期的には変更量が少なく、フローにあまり影響を与えませんが、公式ドキュメントのタイプとは関連しません。
(公式は各タイプの下で、異なるタイプに適した職務を説明しています)
dataSync
は公式が将来的に廃止
する予定であることが記載されています。以下の図を参照してください
b.実行時
に追加の権限を取得する必要がある場合、元のフローでstartForegroundを実行する前にユーザーから権限を取得する
例えば、mediaProjection
はstartForegroundの前にcreateScreenCaptureIntent()を呼び出してmediaProjectionの権限を取得する必要があります。
ここでデモを行いました
build sdk versionを更新してください
以下の権限を追加してください
必要に応じてforeground service typeを追加してください
->
実際に遭遇する可能性のある問題の共有
:以前に書いたService
コードには、
すでにforegroundServiceType
を追加していました(以前は必須ではありませんでした)。
このservice
には、クラス内のアクションを操作するメソッドがあります。
例えば、startForegroundService
したがって、公式ドキュメントによると、android14以上では、
createScreenCaptureIntent()
を呼び出して権限を取得する必要があります。
上記のサンプルを追加することでクラッシュを回避できますが、
元々の予期されたフローとは異なるため、
ロジックを分割し、全体のコードをテスト・修正するための時間が必要です。
毎回フォアグラウンドサービスのために上記の権限を取得する必要があるため、
以前に作成した製品やソリューションでフォアグラウンドサービスを使用している場合、調整が必要です。- 備考:【実行時】、ここでは
startForeground
を実行する時を指します。
実際のテストでは、ドキュメントに従わず
に実行前
に対応する権限を取得しないと、例外
が発生しクラッシュ
します。 - クラッシュの例は以下の通りです。
c. その後、いくつかの状況でクロステストを行いました。 -
Manifest
で|
を使用して複数のforegroundServiceTypeを宣言
そして、ソースコード内で異なるバージョンの判定
に基づいて異なるタイプ
のforegroundServiceType
を指定
-
ソースコード内で
foregroundServiceType
を追加しない場合、
FOREGROUND_SERVICE_MICROPHONE
権限がないと表示されてクラッシュ
します
(複数のタイプを宣言した場合)
そのため、他のServiceをテストしました。
Manifestに単純にdataSync
を追加した場合、
ソースコード内でforegroundServiceType
を入力しなくてもクラッシュしません。
しかし、microphone
を混用し、空のforegroundServiceType
を使用するとクラッシュ
します。
(左側はクラッシュ
し、右側は正常に動作します。クリックして拡大できます)
- 備考:【実行時】、ここでは