データアクセスの監査を実行すると、アプリとその依存関係がユーザーのプライベート データにどのようにアクセスするかを知ることができます。このプロセスは、Android 11(API レベル 30)以降を搭載しているデバイスで使用でき、予期しないデータアクセスの可能性の特定に役立ちます。アプリでは AppOpsManager.OnOpNotedCallback
のインスタンスを登録して、次のいずれかのイベントが発生するたびにアクションを実行できます。
- アプリのコードがプライベート データにアクセスした。アプリ内のどのロジック部分がイベントを呼び出したのかを特定するために、アトリビューション タグを使ってデータアクセスを監査できます。
- 依存ライブラリまたは SDK のコードがプライベート データにアクセスした。
データアクセスの監査は、データがリクエストされたスレッドで呼び出されます。つまり、アプリ内のサードパーティ SDK やライブラリがプライベート データにアクセスする API を呼び出すと、データアクセスの監査では OnOpNotedCallback
を使って呼び出しに関する情報を調べられるようになります。通常、このコールバック オブジェクトは、アプリからの呼び出しか、SDK からの呼び出しかを、現在のスレッドのスタック トレースなど、アプリの現在のステータスを調べて判別できます。
データアクセスをログに記録する
AppOpsManager.OnOpNotedCallback
のインスタンスを使ってデータアクセスの監査を行うには、アクティビティの onCreate()
メソッド内など、データアクセスを監査するコンポーネントにコールバック ロジックを実装します。
次のコード スニペットでは、1 つのアクティビティ内でデータアクセスを監査するために AppOpsManager.OnOpNotedCallback
を定義しています。
Kotlin
override fun onCreate(savedInstanceState: Bundle?) { val appOpsCallback = object : AppOpsManager.OnOpNotedCallback() { private fun logPrivateDataAccess(opCode: String, trace: String) { Log.i(MY_APP_TAG, "Private data accessed. " + "Operation: $opCode\nStack Trace:\n$trace") } override fun onNoted(syncNotedAppOp: SyncNotedAppOp) { logPrivateDataAccess( syncNotedAppOp.op, Throwable().stackTrace.toString()) } override fun onSelfNoted(syncNotedAppOp: SyncNotedAppOp) { logPrivateDataAccess( syncNotedAppOp.op, Throwable().stackTrace.toString()) } override fun onAsyncNoted(asyncNotedAppOp: AsyncNotedAppOp) { logPrivateDataAccess(asyncNotedAppOp.op, asyncNotedAppOp.message) } } val appOpsManager = getSystemService(AppOpsManager::class.java) as AppOpsManager appOpsManager.setOnOpNotedCallback(mainExecutor, appOpsCallback) }
Java
@Override public void onCreate(@Nullable Bundle savedInstanceState, @Nullable PersistableBundle persistentState) { AppOpsManager.OnOpNotedCallback appOpsCallback = new AppOpsManager.OnOpNotedCallback() { private void logPrivateDataAccess(String opCode, String trace) { Log.i(MY_APP_TAG, "Private data accessed. " + "Operation: $opCode\nStack Trace:\n$trace"); } @Override public void onNoted(@NonNull SyncNotedAppOp syncNotedAppOp) { logPrivateDataAccess(syncNotedAppOp.getOp(), Arrays.toString(new Throwable().getStackTrace())); } @Override public void onSelfNoted(@NonNull SyncNotedAppOp syncNotedAppOp) { logPrivateDataAccess(syncNotedAppOp.getOp(), Arrays.toString(new Throwable().getStackTrace())); } @Override public void onAsyncNoted(@NonNull AsyncNotedAppOp asyncNotedAppOp) { logPrivateDataAccess(asyncNotedAppOp.getOp(), asyncNotedAppOp.getMessage()); } }; AppOpsManager appOpsManager = getSystemService(AppOpsManager.class); if (appOpsManager != null) { appOpsManager.setOnOpNotedCallback(getMainExecutor(), appOpsCallback); } }
onAsyncNoted()
メソッドと onSelfNoted()
メソッドは、次のような特定の状況で呼び出されます。
アプリによる API 呼び出し中にはデータアクセスが行われない場合は、
onAsyncNoted()
が呼び出されます。最も一般的な例は、アプリがリスナーを登録し、リスナーのコールバックが呼び出されるたびにデータアクセスが行われる場合です。onAsyncNoted()
に渡されるAsyncNotedOp
引数にはgetMessage()
というメソッドが含まれます。このメソッドは、データアクセスに関する詳細情報を提供します。位置情報に関するコールバックの場合、メッセージにはリスナーの system-identity-hash が含まれます。onSelfNoted()
が呼び出されるのはごくまれな場合で、アプリが自身の UID をnoteOp()
に渡したときです。
アトリビューション タグを使ってデータアクセスを監査する
ユーザーが写真を撮影して連絡先と共有できるようにするなど、アプリの主要なユースケースを複数想定することがあります。このような多目的アプリを開発する場合、データアクセスを監査するにあたって、アプリの各部分に「アトリビューション タグ」を適用することができます。onNoted()
の呼び出しに渡されるオブジェクト内に attributionTag
コンテキストが返されます。これにより、コードの論理部分までさかのぼってデータアクセスを追跡することが簡単になります。
アプリでアトリビューション タグを定義するには、次のセクションに示す手順を完了します。
アトリビューション タグを作成する
位置情報のリクエストや、ユーザーの連絡先リストへのアクセスのように、データにアクセスするアクティビティの onCreate()
メソッド内で、createAttributionContext()
を呼び出して、アプリの構成部分に関連付けるアトリビューション タグを渡します。
次のコード スニペットでは、アプリの「写真の位置情報を共有する」部分にアトリビューション タグを作成する方法を示します。
Kotlin
class SharePhotoLocationActivity : AppCompatActivity() { lateinit var attributionContext: Context override fun onCreate(savedInstanceState: Bundle?) { attributionContext = createAttributionContext("sharePhotos") } fun getLocation() { val locationManager = attributionContext.getSystemService( LocationManager::class.java) as LocationManager // Use "locationManager" to access device location information. } }
Java
public class SharePhotoLocationActivity extends AppCompatActivity { private Context attributionContext; @Override public void onCreate(@Nullable Bundle savedInstanceState, @Nullable PersistableBundle persistentState) { attributionContext = createAttributionContext("sharePhotos"); } public void getLocation() { LocationManager locationManager = attributionContext.getSystemService(LocationManager.class); if (locationManager != null) { // Use "locationManager" to access device location information. } } }
アクセスログにアトリビューション タグを含める
AppOpsManager.OnOpNotedCallback
コールバックを更新して、定義したアトリビューション タグの名前がアプリのログに含まれるようにします。
次のコード スニペットでは、アトリビューション タグをログに記録するように更新したロジックを示します。
Kotlin
val appOpsCallback = object : AppOpsManager.OnOpNotedCallback() { private fun logPrivateDataAccess( opCode: String, attributionTag: String, trace: String) { Log.i(MY_APP_TAG, "Private data accessed. " + "Operation: $opCode\n " + "Attribution Tag:$attributionTag\nStack Trace:\n$trace") } override fun onNoted(syncNotedAppOp: SyncNotedAppOp) { logPrivateDataAccess(syncNotedAppOp.op, syncNotedAppOp.attributionTag, Throwable().stackTrace.toString()) } override fun onSelfNoted(syncNotedAppOp: SyncNotedAppOp) { logPrivateDataAccess(syncNotedAppOp.op, syncNotedAppOp.attributionTag, Throwable().stackTrace.toString()) } override fun onAsyncNoted(asyncNotedAppOp: AsyncNotedAppOp) { logPrivateDataAccess(asyncNotedAppOp.op, asyncNotedAppOp.attributionTag, asyncNotedAppOp.message) } }
Java
@Override public void onCreate(@Nullable Bundle savedInstanceState, @Nullable PersistableBundle persistentState) { AppOpsManager.OnOpNotedCallback appOpsCallback = new AppOpsManager.OnOpNotedCallback() { private void logPrivateDataAccess(String opCode, String attributionTag, String trace) { Log.i("MY_APP_TAG", "Private data accessed. " + "Operation: $opCode\n " + "Attribution Tag:$attributionTag\nStack Trace:\n$trace"); } @Override public void onNoted(@NonNull SyncNotedAppOp syncNotedAppOp) { logPrivateDataAccess(syncNotedAppOp.getOp(), syncNotedAppOp.getAttributionTag(), Arrays.toString(new Throwable().getStackTrace())); } @Override public void onSelfNoted(@NonNull SyncNotedAppOp syncNotedAppOp) { logPrivateDataAccess(syncNotedAppOp.getOp(), syncNotedAppOp.getAttributionTag(), Arrays.toString(new Throwable().getStackTrace())); } @Override public void onAsyncNoted(@NonNull AsyncNotedAppOp asyncNotedAppOp) { logPrivateDataAccess(asyncNotedAppOp.getOp(), asyncNotedAppOp.getAttributionTag(), asyncNotedAppOp.getMessage()); } }; AppOpsManager appOpsManager = getSystemService(AppOpsManager.class); if (appOpsManager != null) { appOpsManager.setNotedAppOpsCollector(appOpsCollector); } }