android-intent-security について
Android コンポーネントと Intent の保護ルールや実装パターンを示すセキュリティスキルです。信頼できない Intent の転送、PendingIntent、ContentProvider、プロセス間呼び出しを扱います。
主な機能
- 01Intent のルーティングと転送の安全な処理
- 02IntentSanitizer による入力検証
- 03PendingIntent の可変性設定
- 04ContentProvider の権限保護
- 05エクスポートされたサービスの呼び出し元署名の検証
活用シーン
- 01エクスポートされたコンポーネントと権限設定の確認
- 02信頼できない Intent のリダイレクト防止
- 03通知やアラーム向けの安全な PendingIntent 作成
- 04アプリ間サービス呼び出しとデータアクセスの制限
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ android/ skills --skill android-intent-securityインストール後、Claude Code で/android-intent-securityを入力して読み込まれたか確認します。
詳細
このスキルでは、Android コンポーネント(Activity、Service、Broadcast Receiver、Content Provider)を保護し、Intent を安全に処理して、権限昇格や不正アクセスを防ぐためのガイドラインとパターンを紹介します。
用語集
- Intent: 別のアプリコンポーネントにアクションを要求するために使用する、非同期メッセージングオブジェクトです。
- エクスポートされたコンポーネント: デバイス上の他のアプリから起動できるコンポーネント(
android:exported="true")です。 - スティッキー Intent: 送信後もシステムキャッシュに残り、すべてのアプリがその内容を取得できるブロードキャスト Intent です。
- 署名権限: 保護レベルが
signatureに設定された権限です。同じ開発者キーで署名されたアプリにのみ付与されます。 - onNewIntent: アクティビティが
FLAG_ACTIVITY_SINGLE_TOPを指定して起動され、すでに履歴スタックの最上位で実行中の場合に呼び出される、アクティビティのライフサイクルコールバックです。 - PendingIntent: 他のアプリケーション(システムサービスなど)に付与されるトークンです。作成元の権限を使って、事前に定義された Intent を実行できます。
- 可変 PendingIntent: 内部の Intent パラメーターを受信側のアプリケーションが変更できる PendingIntent です。
- ContentProvider: データをカプセル化し、標準の query/insert インターフェイスを通じて他のアプリケーションに提供するコンポーネントです。
- IntentSanitizer: 許可されていないコンポーネント、アクション、またはエクストラを除外して、安全にサニタイズした受信 Intent のコピーを作成するために使用する、AndroidX Core のユーティリティクラスです。
- Intent リダイレクト(転送): アプリケーションが信頼できないソースから Intent を受け取り、それを使って非公開の非エクスポートコンポーネントを起動する脆弱性です。
前提条件
- エージェントは、
onCreate、onNewIntent、およびsingleTop起動モードの機能とセキュリティ上の影響を説明できなければなりません。 - エージェントは、
AndroidManifest.xmlで<activity>、<service>、<receiver>、および<provider>タグを宣言し、それらのandroid:exported属性とandroid:permission属性を定義できなければなりません。 - エージェントは、
PackageManagerを使った署名検証チェックを実装できなければなりません。
制限事項
- このスキルでは、Android プラットフォームにおけるローカルなコンポーネント間およびアプリ間の通信セキュリティに焦点を当てます。
- このスキルでは、ネットワークセキュリティ、ウェブ連携、ホストからサーバーへのセキュリティは扱いません。
セットアップと依存関係
- Android SDK: 標準的なハードウェアバックドキーヤー操作とコンポーネントの検証には、API レベル 23(Android 6.0)以上が必要です。
- AndroidX Core Library:
IntentSanitizerを利用するには、androidx.core:core:1.9.0以上が必須です。 - 標準 API へのアクセス: 実行時のコンポーネント検証には、標準の Android
PackageManagerAPI が必要です。
Intent のセキュリティロジックと判断基準
1. Intent ルーティングの比較
各種 Intent 配信方法のセキュリティ機能を評価します。
| Intent 配信方法 | 範囲 | 推奨される用途 |
|---|---|---|
| 明示的 Intent(内部) | アプリ内 | 内部アクティビティ/サービスの起動 |
| 暗黙的 Intent | システム全体 | システムカメラ、ダイヤラー、共有機能の起動 |
| ローカルブロードキャスト(LocalBroadcastManager)(非推奨) | アプリ内 | 内部の非同期イベントのルーティング。非推奨:代わりに Kotlin Flows/SharedFlow、LiveData などのアプリ内オブザーバーやリアクティブパターンを使用してください。 |
| システムブロードキャスト | システム全体 | システムイベント(NFC、Bluetooth)の受信 |
2. PendingIntent の可変性フラグの選択肢
PendingIntent の可変性フラグがもたらすセキュリティ上の影響を評価します。
| フラグ名 | 可変性 | 推奨される用途 |
|---|---|---|
PendingIntent.FLAG_IMMUTABLE | 不変 | アラームや通知など、ほぼすべての PendingIntent でのデフォルト |
PendingIntent.FLAG_MUTABLE | 可変 | インライン通知への返信、スライスアクション(明示的なターゲット Intent が必要) |
3. Intent の処理とリダイレクトのロジック
IF(コンポーネントが extra としてネストされた Intent を受け取る){
IF(AndroidX Core 1.9.0+ 以降が利用可能){
コンポーネント、アクション、データ、extra を明示的に許可リストに登録する IntentSanitizer を MUST 構築します。
起動前に sanitizeByThrowing() または sanitizeByFiltering() を MUST 呼び出します。
} ELSE {
URI 権限付与フラグ(FLAG_GRANT_READ_URI_PERMISSION、FLAG_GRANT_WRITE_URI_PERMISSION、FLAG_GRANT_PERSISTABLE_URI_PERMISSION、FLAG_GRANT_PREFIX_URI_PERMISSION)を MUST 拒否または削除します。
resolveActivity() を使用してターゲットコンポーネントを解決し、その packageName と className の両方が明示的に許可リストに登録されたコンポーネントと一致することを MUST 確認します。
}
検証せずにネストされた Intent を直接起動することは NEVER しないでください。
} ELSE IF(コンポーネントがブロードキャストを処理する){
システムイベントについては、システムの Protected Broadcast メカニズムに MUST 依存します(これにより送信元がシステムフレームワークであることが保証されます)。
カスタムレシーバーは署名レベルの権限で MUST 保護するか、動的レシーバーには RECEIVER_NOT_EXPORTED を使用して送信元を制限します。
IF(ContextCompat.registerReceiver で RECEIVER_NOT_EXPORTED を使用し、かつ minSdk < 33 の場合){
<permission android:name="${applicationId}.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION" android:protectionLevel="signature" /> と <uses-permission android:name="${applicationId}.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION" /> が AndroidManifest.xml に宣言されていることを MUST 確認します。
}
}
4. PendingIntent のセキュリティロジック
IF(PendingIntent を別のアプリケーションに渡すために作成する){
デフォルトで PendingIntent.FLAG_IMMUTABLE を MUST 使用します。
IF(PendingIntent を変更可能にする必要がある){
ベースとなる Intent に明示的なターゲットコンポーネントまたはパッケージ名を MUST 設定します。
暗黙的で変更可能な PendingIntent を作成することは NEVER しないでください。
}
}
5. ContentProvider のセキュリティロジック
IF(ContentProvider をアプリ内部でのみ使用する){
android:exported="false" を MUST 設定します。
} ELSE {
android:readPermission と android:writePermission で MUST 保護します。
一時的な URL アクセスが厳密に必要な場合を除き、android:grantUriPermissions="false" を MUST 設定します。
}
6. サービス呼び出し元の検証ロジック
IF(エクスポートされたサービスが、信頼できる関連アプリ/パートナーアプリと通信する){
同一プロセス内でのみ機能するローカル Binder サブクラスではなく、AIDL Stub や Messenger などのプロセス間インターフェースを MUST 公開します。
すべての IPC リクエストで呼び出し元を MUST 検証します。AIDL Stub メソッド(または onTransact())内で Binder.getCallingUid() を使用するか、Messenger の Handler で Message.sendingUid を使用し、呼び出し元のパッケージ署名フィンガープリントが信頼できる証明書ハッシュと一致することを確認します。
}
コードと設定のパターン
1. 安全な Intent リダイレクト(手動検証)
最新のサニタイズライブラリを利用できない場合は、起動前にネストされた Intent のターゲットを検証します。
- 想定される入力:
EXTRA_NESTED_INTENTという名前のネストされた Intent extra を含む、受信したIntent。
- 想定される出力:
- 安全な場合はターゲットコンポーネントを起動します。検証に失敗した場合は
SecurityExceptionをスローします。
- 安全な場合はターゲットコンポーネントを起動します。検証に失敗した場合は
fun safeIntentRedirectionManual() {
val nestedIntent = IntentCompat.getParcelableExtra(intent, "EXTRA_NESTED_INTENT", Intent::class.java)
if (nestedIntent != null) {
// 1. Check for URI permission grants to prevent URI permission bypass
val hasUriPermissionGrants = (
nestedIntent.flags and (
Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION or
Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION or
Intent.FLAG_GRANT_PREFIX_URI_PERMISSION
)
) != 0
if (hasUriPermissionGrants) {
throw SecurityException("Nested intent contains forbidden URI permission grant flags!")
}
// 2. Clear any selector intent that could override resolution
nestedIntent.selector = null
// 3. Resolve the target activity and verify both packageName and className
// match the expected allowlisted destination
val target = nestedIntent.resolveActivity(packageManager)
if (target != null &&
target.packageName == packageName &&
target.className == TargetActivity::class.java.name
) {
// 4. Explicitly pin the resolved component to prevent intent interception
nestedIntent.component = target
startActivity(nestedIntent)
} else {
throw SecurityException("Invalid or disallowed redirect target: $target")
}
}
}
2. IntentSanitizer を使用した安全な Intent リダイレクト
AndroidX IntentSanitizer(AndroidX Core
1.9.0+)を使用して、動的な Intent をフィルタリングまたは拒否します。
- 想定される入力:
- 信頼できない受信
Intent。
- 信頼できない受信
- 想定される出力:
Intent:許可リストに登録されたコンポーネント、カテゴリ、アクションのみを含むサニタイズ済みコピー。sanitizeByThrowing()を使用した場合、違反時にはSecurityExceptionをスローします。
fun safeIntentRedirectionSanitizer() {
// Allowlist: anything NOT listed is rejected (only component is required; other fields are optional).
// No allowFlags(): every flag, including FLAG_GRANT_*_URI_PERMISSION, is rejected.
val sanitizer = IntentSanitizer.Builder()
.allowComponent(ComponentName(this, TargetActivity::class.java))
.allowAction(Intent.ACTION_VIEW)
.allowData { it.scheme == ContentResolver.SCHEME_CONTENT && it.authority == "$packageName.provider" }
.allowType("text/plain")
.allowExtra("user_display_name", String::class.java) { it.length <= 100 } // type and content
.build()
val safeIntent = try {
val untrusted = IntentCompat.getParcelableExtra(intent, "EXTRA_NESTED_INTENT", Intent::class.java)
?: return
sanitizer.sanitizeByThrowing(untrusted)
} catch (_: SecurityException) {
Log.w("SECURITY_ALERT", "Blocked non-allowlisted nested intent")
return
} catch (_: BadParcelableException) { // e.g. an extra naming an unknown Parcelable/Serializable
Log.w("SECURITY_ALERT", "Malformed nested intent")
return
}
// The sanitizer doesn't enforce presence; check what the target actually requires.
if (safeIntent.getStringExtra("user_display_name") == null) {
Log.w("SECURITY_ALERT", "Nested intent missing user_display_name")
return
}
startActivity(safeIntent)
}
3. カスタム署名権限による保護
マニフェストでカスタム署名レベル権限を宣言し、関連アプリ間の通信を保護します。
- 想定される入力: マニフェスト設定。
- 想定される出力: 同じ開発者証明書で署名されたアプリのみが起動できるアクティビティ。
<permission
android:name="com.example.snippets.permission.INTERNAL_COMMUNICATION"
android:protectionLevel="signature" />
<activity
android:name=".intents.InternalSharingActivity"
android:exported="true"
android:permission="com.example.snippets.permission.INTERNAL_COMMUNICATION">
<intent-filter>
<action android:name="com.example.snippets.ACTION_SHARE" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
### 4. Safe onNewIntent ライフサイクルの検証(ウォームブート保護)
動的インテントを再利用するアクティビティ(バックグラウンドからの起動経路など)が、onNewIntent 内でも同じ厳格なセキュリティフィルターを適用するようにします。
- 想定される入力:
newIntent(Intent):新たに渡されたインテント。
- 想定される出力:
- 新しいインテントがセキュリティ検証に合格した場合にのみ、処理ロジックを実行します。
override fun onNewIntent(newIntent: Intent) {
super.onNewIntent(newIntent)
// Set the intent to ensure intent returns the new one
intent = newIntent
// Validate the intent payload
if (validateIntent(newIntent)) {
processIntentPayload(newIntent)
} else {
Log.w("SECURITY_ALERT", "Received invalid or insecure intent during warm boot")
}
}
private fun validateIntent(intent: Intent): Boolean {
return intent.hasExtra("VALID_PAYLOAD_MARKER")
}
5. 安全な PendingIntent の作成
変更可能であることが明示的に必要な場合を除き、不変性を適用します。
- 想定される入力(不変): インテントのターゲット。
- 想定される出力(不変): 受信側が変更できない
PendingIntent。 - 想定される入力(変更可能): 明示的なコンポーネントが設定されたインテント。
- 想定される出力(変更可能): 特定の受信コンポーネントに固定され、乗っ取りを防ぐ変更可能な
PendingIntent。
fun createPendingIntents(context: Context) {
// 1. Secure Immutable PendingIntent (Default)
val intent = Intent(context, TargetActivity::class.java)
val pendingIntent = PendingIntent.getActivity(
context,
0,
intent,
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
// 2. Secure Mutable PendingIntent (e.g., Notification Direct Reply)
val mutableIntent = Intent().apply {
// MUST set explicit target component to prevent redirection hijacking
component = ComponentName(context, ReplyReceiver::class.java)
}
val mutablePendingIntent = PendingIntent.getBroadcast(
context,
0,
mutableIntent,
PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
}
6. ContentProvider の安全な設定とクエリ
SQL インジェクションを防ぐためにクエリをパラメーター化し、ContentProvider を安全に公開します。
- 想定される入力:
uri(Uri):クエリ URI。projection(String[]):取得する列。selection(String):クエリ条件。selectionArgs(String[]):選択条件のプレースホルダー(?)に対応する値。
- 想定される出力:
Cursor:プロジェクションマップに厳密に制限された、フィルタリング済みのクエリ結果。
<provider
android:name=".intents.SecureDataProvider"
android:authorities="com.example.snippets.provider"
android:exported="true"
android:readPermission="com.example.snippets.permission.READ_DATA"
android:writePermission="com.example.snippets.permission.WRITE_DATA"
android:grantUriPermissions="false" />
override fun query(
uri: Uri,
projection: Array<String>?,
selection: String?,
selectionArgs: Array<String>?,
sortOrder: String?
): Cursor? {
val queryBuilder = SQLiteQueryBuilder()
queryBuilder.tables = tableName
// Strict projection map to prevent querying unauthorized columns
queryBuilder.projectionMap = mapOf(
"_id" to "_id",
"display_name" to "display_name"
)
// Enable strict validation (always available since minSdk is 36)
queryBuilder.setStrict(true)
queryBuilder.setStrictColumns(true)
queryBuilder.setStrictGrammar(true)
// MUST parameterize selection criteria; NEVER append selection strings directly
val db = dbHelper.readableDatabase
return queryBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder)
}
7. サービス呼び出し元の署名検証
エクスポートされたバインドサービスへの受信プロセス間 IPC 呼び出しで、呼び出し元アプリケーションの署名証明書を検証します。
- 想定される入力:
- 受信したプロセス間 Binder トランザクション(AIDL の
Stubメソッド呼び出しやonTransact()など)。
- 受信したプロセス間 Binder トランザクション(AIDL の
- 想定される出力:
- 呼び出し元 UID の署名証明書が信頼できるパートナーのハッシュと一致する場合は、要求されたサービス操作を実行します。それ以外の場合は
SecurityExceptionをスローします。
- 呼び出し元 UID の署名証明書が信頼できるパートナーのハッシュと一致する場合は、要求されたサービス操作を実行します。それ以外の場合は
class SecureBoundService : Service() {
companion object {
// Expected SHA-256 hash of the trusted app's signing certificate (Base64 encoded)
private const val TRUSTED_PARTNER_SHA256 = "A1B2C3D4E5F6G7H8I9J0K1L2M3N4O5P6Q7R8S9T0U1V="
}
override fun onBind(intent: Intent): IBinder {
// Return the binder. Do NOT perform signature verification in onBind() because
// the binder connection is cached by Android, which can bypass checks on subsequent binds.
return LocalBinder()
}
private fun enforceTrustedCaller() {
val callingUid = Binder.getCallingUid()
// Allow calls from the same application
if (callingUid == Process.myUid()) {
return
}
val pm = packageManager
val packages = pm.getPackagesForUid(callingUid)
if (packages.isNullOrEmpty() || !verifySignature(pm, packages[0])) {
throw SecurityException("Access Denied: Caller signature is untrusted.")
}
}
private fun verifySignature(pm: PackageManager, packageName: String): Boolean {
try {
val trustedSha256Raw = Base64.decode(TRUSTED_PARTNER_SHA256, Base64.DEFAULT)
// API 28+ handles rotated certificates and avoids manual hashing.
// Since minSdk is 36, this is always available.
return pm.hasSigningCertificate(packageName, trustedSha256Raw, PackageManager.CERT_INPUT_SHA256)
} catch (e: Exception) {
Log.e("SECURITY_ERROR", "Verification failed for package: $packageName", e)
}
return false
}
inner class LocalBinder : Binder() {
fun doSecureWork() {
// Verify caller identity on every transaction method call
enforceTrustedCaller()
// Safe to proceed with sensitive operations
}
}
}
エラー処理
コンポーネントのバインド、データベースクエリ、インテントのリダイレクトに関する失敗を安全に処理し、内部構造が公開されないようにします。
fun safeErrorHandling(callingPackage: String?) {
try {
val payload = intent.getStringExtra("DATA_EXTRA") ?: throw IllegalArgumentException("Payload parameter missing.")
// Create a specific target intent using the validated payload
val targetIntent = Intent(this, TargetActivity::class.java).apply {
putExtra("SECURE_PAYLOAD", payload)
}
startActivity(targetIntent)
} catch (e: SecurityException) {
// MUST log security violations for audit, but NEVER expose exception details to the user.
Log.e("SECURITY_ERROR", "Unauthorized component transition blocked. Calling Package: ${callingPackage ?: "Unknown"}", e)
// MUST provide generic user feedback.
showFeedbackToUser("Process request failed: Access Denied.")
} catch (e: IllegalArgumentException) {
Log.w("INTEGRITY_WARNING", "Missing intent parameter", e)
}
}
// クライアント側で ContentProvider クエリを安全に処理する例:
try {
val cursor = contentResolver.query(providerUri, projection, selection, selectionArgs, null)
} catch (e: SQLiteException) {
Log.e("PROVIDER_ERROR", "ContentProvider database query failed", e)
// 安全な処理:生のクエリ構文の詳細が UI に漏えいしないようにする
}
報告ガイドライン
このスキルを実行してコードベースにセキュリティ強化の更新を適用する場合、エージェントは開発者向けに、構造化された「ベストプラクティスとセキュリティ整合性の更新」レポートを必ず作成する必要があります。レポートはセッションの成果物フォルダーに書き込むか、最終応答に表示し、次の内容を含める必要があります。
- セキュリティ整合性の領域: 適用した改善のカテゴリ(例:安全なインテントのリダイレクト、安全な PendingIntent の設定、ContentProvider のデータ保護)。
- 影響と優先度: 更新によって対処した潜在的な安全上のリスク(例:コンポーネントの乗っ取り防止、プライベートデータの分離)。
- 変更範囲: 変更したすべてのクラス、XML ファイル、依存関係の一覧。
- 実装の概要: 解決策の具体的な詳細(例:「厳格なコンポーネント許可リストを設定した
IntentSanitizerAPI を使用するよう、ネストされたインテントの解析を更新」)。 - コード差分: 変更内容を正確に示す標準的な unified diff。
ベストプラクティスとセキュリティ整合性の更新テンプレート
開発者に変更を報告する際は、次の Markdown テンプレートを使用してください。
### ベストプラクティスとセキュリティ整合性の更新:[セキュリティ整合性の領域]
* **改善内容:** [強化に関する更新の概要と、推奨される理由]
* **優先度:** [高/中/低]
* **整合性確保のための対応:** [更新内容の概要。例:FLAG_IMMUTABLE に変更]
#### 変更したファイル
* `[ファイル 1 の相対パス]`
* `[ファイル 2 の相対パス]`
#### 実装の差分
```diff
// ここに Unified Diff を挿入
テストと検証
- [コンポーネントが正しく動作することを検証する手順 1。例:コンポーネントの単体テストを実行する]
- [回帰がないことを検証する手順 2] ```
アンチパターン
- 絶対に、信頼できない送信元から受け取ったネストされた
Intentを、その解決先コンポーネント(packageNameとclassName)が明示的な許可リストと一致することを確認し、URI 権限付与フラグを拒否または除去することなく起動しないでください。 - 絶対に、スティッキーブロードキャスト(
sendStickyBroadcast)を使用しないでください。 - 絶対に、エクスポートされたコンポーネントがバックグラウンドスレッドで実行される、または内部チェックを行うという理由だけで、安全だと決めつけないでください。
- 絶対に、SSO 認証や決済処理などの機密性の高い機能を、署名レベルの権限制限なしでコンポーネントに公開しないでください。
- 絶対に、
onNewIntentで受信したインテントに、onCreateと同じセキュリティ制御を適用せずに処理しないでください。 - 絶対に、ベースとなる
Intentに明示的なターゲットコンポーネントを設定せずに、変更可能なPendingIntentを作成しないでください。 - 絶対に、
ContentProviderのクエリ内で選択条件を構築するために、動的な文字列連結を使用しないでください。 - 絶対に、ブロードキャストの送信元を特定するために
BroadcastReceiver.onReceive内でBinder.getCallingUidを使用しないでください。これは送信元ではなく、レシーバー自身の UID を返します。
ベストプラクティス
- 外部との通信が不要なすべてのコンポーネントに、
android:exported="false"を明示的に設定する必要があります。 - ファミリーアプリ間で通信する場合、エクスポートされたすべてのコンポーネントを、
android:protectionLevel="signature"を使用するカスタム権限で保護する必要があります。 - クラッシュを防ぐため、受信したすべてのインテントの追加情報を検証し、パラメーターが不足している場合も適切に処理する必要があります。
- システムイベント(例:起動完了、パッケージの変更)には、システムの保護されたブロードキャストの仕組みを利用する必要があります。システムによって、信頼できないアプリがこれらのアクションを偽装できないように保護されています。
- カスタムブロードキャストは署名レベルの権限で保護するか、動的レシーバーに
RECEIVER_NOT_EXPORTEDを使用して送信元を制限する必要があります(また、minSdk < 33の場合、ContextCompat.registerReceiverを使用する際は、AndroidManifest.xmlに<permission android:name="${applicationId}.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION" android:protectionLevel="signature" />と<uses-permission android:name="${applicationId}.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION" />が含まれていることを確認する必要があります)。 - アクティブな参照を最新の状態に保つため、ペイロードを処理する前に
onNewIntent()内でsetIntent(newIntent)を呼び出す必要があります。 PendingIntentインスタンスを作成する際は、デフォルトでPendingIntent.FLAG_IMMUTABLEを使用する必要があります。- エクスポートされた
ContentProvidersはreadPermissionとwritePermissionで保護する必要があります。 ContentProviderの query/update メソッドでは、パラメーター化された選択条件の構造を強制する必要があります。- クロスプロセスインターフェース(AIDL
StubやMessengerなど)を使用するエクスポート済みのバインドサービスでは、受信した IPC トランザクションに対して、実行時に呼び出し元アプリの署名証明書を検証する必要があります。 - プロジェクトに AndroidX Core 1.9.0 以降がインポートされている場合、受信した動的インテントをリダイレクトする前にサニタイズするため、
androidx.core.content.IntentSanitizerを使用する必要があります。