Audit access to data

You can gain insights into how your app and its dependencies access private data from users by performing data access auditing. This process, available on devices that run Android 11 (API level 30) and higher, lets you better identify potentially unexpected data access. Your app can register an instance of AppOpsManager.OnOpNotedCallback, which can perform actions each time one of the following events occurs:

  • Your app's code accesses private data. To help you determine which logical part of your app invoked the event, you can audit data access by attribution tag.
  • Code in a dependent library or SDK accesses private data.

Data access auditing is invoked on the thread where the data request takes place. This means that if a third-party SDK or library in your app calls an API that accesses private data, data access auditing lets your OnOpNotedCallback examine information about the call. Usually, this callback object can tell whether the call came from your app or the SDK by looking at the app's current status, such as the current thread's stack trace.

Log access of data

To perform data access auditing using an instance of AppOpsManager.OnOpNotedCallback, implement the callback logic in the component where you intend to audit data access, such as within an activity's onCreate() method or an application's onCreate() method.

The following code snippet defines an AppOpsManager.OnOpNotedCallback for auditing data access within a single activity:

Kotlin

overridefunonCreate(savedInstanceState:Bundle?){
valappOpsCallback=object:AppOpsManager.OnOpNotedCallback(){
privatefunlogPrivateDataAccess(opCode:String,trace:String){
Log.i(MY_APP_TAG,"Private data accessed. "+
"Operation: $opCode\nStack Trace:\n$trace")
}
overridefunonNoted(syncNotedAppOp:SyncNotedAppOp){
logPrivateDataAccess(
syncNotedAppOp.op,Throwable().stackTrace.toString())
}
overridefunonSelfNoted(syncNotedAppOp:SyncNotedAppOp){
logPrivateDataAccess(
syncNotedAppOp.op,Throwable().stackTrace.toString())
}
overridefunonAsyncNoted(asyncNotedAppOp:AsyncNotedAppOp){
logPrivateDataAccess(asyncNotedAppOp.op,asyncNotedAppOp.message)
}
}
valappOpsManager=
getSystemService(AppOpsManager::class.java)asAppOpsManager
appOpsManager.setOnOpNotedCallback(mainExecutor,appOpsCallback)
}

Java

@Override
publicvoidonCreate(@NullableBundlesavedInstanceState,
@NullablePersistableBundlepersistentState){
AppOpsManager.OnOpNotedCallbackappOpsCallback=
newAppOpsManager.OnOpNotedCallback(){
privatevoidlogPrivateDataAccess(StringopCode,Stringtrace){
Log.i(MY_APP_TAG,"Private data accessed. "+
"Operation: $opCode\nStack Trace:\n$trace");
}
@Override
publicvoidonNoted(@NonNullSyncNotedAppOpsyncNotedAppOp){
logPrivateDataAccess(syncNotedAppOp.getOp(),
Arrays.toString(newThrowable().getStackTrace()));
}
@Override
publicvoidonSelfNoted(@NonNullSyncNotedAppOpsyncNotedAppOp){
logPrivateDataAccess(syncNotedAppOp.getOp(),
Arrays.toString(newThrowable().getStackTrace()));
}
@Override
publicvoidonAsyncNoted(@NonNullAsyncNotedAppOpasyncNotedAppOp){
logPrivateDataAccess(asyncNotedAppOp.getOp(),
asyncNotedAppOp.getMessage());
}
};
AppOpsManagerappOpsManager=getSystemService(AppOpsManager.class);
if(appOpsManager!=null){
appOpsManager.setOnOpNotedCallback(getMainExecutor(),appOpsCallback);
}
}

The onAsyncNoted() and onSelfNoted() methods are called in specific situations:

  • onAsyncNoted() is called if the data access doesn't happen during your app's API call. The most common example is when your app registers a listener and the data access happens each time the listener's callback is invoked.

    The AsyncNotedOp argument that's passed into onAsyncNoted() contains a method called getMessage(). This method provides more information about the data access. In the case of the location callbacks, the message contains the system-identity-hash of the listener.

  • onSelfNoted() is called in the very rare case when an app passes its own UID into noteOp().

Audit data access by attribution tag

Your app might have several primary use cases, such as letting users capture photos and share these photos with their contacts. If you develop a multi-purpose app, you can apply an attribution tag to each part of your app when you audit its data access. The attributionTag context is returned back in the objects passed to the calls to onNoted(). This helps you more easily trace data access back to logical parts of your code.

To define attribution tags in your app, complete the steps in the following sections.

Declare attribution tags in manifest

If your app targets Android 12 (API level 31) or higher, you must declare attribution tags in your app's manifest file, using the format shown in the following code snippet. If you attempt to use an attribution tag that you don't declare in your app's manifest file, the system creates a null tag for you and logs a message in Logcat.

<manifest...>
<!--Thevalueof"android:tag"mustbealiteralstring,andthe
valueof"android:label"mustbearesource.Thevalueof
"android:label"isuser-readable.-->
<attributionandroid:tag="sharePhotos"
android:label="@string/share_photos_attribution_label"/>
...
</manifest>

Create attribution tags

In the onCreate() method of the activity where you access data, such as the activity where you request location or access the user's list of contacts, call createAttributionContext(), passing in the attribution tag that you wish to associate with a part of your app.

The following code snippet demonstrates how to create an attribution tag for a photo-location-sharing part of an app:

Kotlin

classSharePhotoLocationActivity:AppCompatActivity(){
lateinitvarattributionContext:Context
overridefunonCreate(savedInstanceState:Bundle?){
attributionContext=createAttributionContext("sharePhotos")
}
fungetLocation(){
vallocationManager=attributionContext.getSystemService(
LocationManager::class.java)asLocationManager
// Use "locationManager" to access device location information.
}
}

Java

publicclass SharePhotoLocationActivityextendsAppCompatActivity{
privateContextattributionContext;
@Override
publicvoidonCreate(@NullableBundlesavedInstanceState,
@NullablePersistableBundlepersistentState){
attributionContext=createAttributionContext("sharePhotos");
}
publicvoidgetLocation(){
LocationManagerlocationManager=
attributionContext.getSystemService(LocationManager.class);
if(locationManager!=null){
// Use "locationManager" to access device location information.
}
}
}

Include attribution tags in access logs

Update your AppOpsManager.OnOpNotedCallback callback so that your app's logs include the names of the attribution tags that you defined.

The following code snippet shows updated logic that logs attribution tags:

Kotlin

valappOpsCallback=object:AppOpsManager.OnOpNotedCallback(){
privatefunlogPrivateDataAccess(
opCode:String,attributionTag:String,trace:String){
Log.i(MY_APP_TAG,"Private data accessed. "+
"Operation: $opCode\n "+
"Attribution Tag:$attributionTag\nStack Trace:\n$trace")
}
overridefunonNoted(syncNotedAppOp:SyncNotedAppOp){
logPrivateDataAccess(syncNotedAppOp.op,
syncNotedAppOp.attributionTag,
Throwable().stackTrace.toString())
}
overridefunonSelfNoted(syncNotedAppOp:SyncNotedAppOp){
logPrivateDataAccess(syncNotedAppOp.op,
syncNotedAppOp.attributionTag,
Throwable().stackTrace.toString())
}
overridefunonAsyncNoted(asyncNotedAppOp:AsyncNotedAppOp){
logPrivateDataAccess(asyncNotedAppOp.op,
asyncNotedAppOp.attributionTag,
asyncNotedAppOp.message)
}
}

Java

@Override
publicvoidonCreate(@NullableBundlesavedInstanceState,
@NullablePersistableBundlepersistentState){
AppOpsManager.OnOpNotedCallbackappOpsCallback=
newAppOpsManager.OnOpNotedCallback(){
privatevoidlogPrivateDataAccess(StringopCode,
StringattributionTag,Stringtrace){
Log.i("MY_APP_TAG","Private data accessed. "+
"Operation: $opCode\n "+
"Attribution Tag:$attributionTag\nStack Trace:\n$trace");
}
@Override
publicvoidonNoted(@NonNullSyncNotedAppOpsyncNotedAppOp){
logPrivateDataAccess(syncNotedAppOp.getOp(),
syncNotedAppOp.getAttributionTag(),
Arrays.toString(newThrowable().getStackTrace()));
}
@Override
publicvoidonSelfNoted(@NonNullSyncNotedAppOpsyncNotedAppOp){
logPrivateDataAccess(syncNotedAppOp.getOp(),
syncNotedAppOp.getAttributionTag(),
Arrays.toString(newThrowable().getStackTrace()));
}
@Override
publicvoidonAsyncNoted(@NonNullAsyncNotedAppOpasyncNotedAppOp){
logPrivateDataAccess(asyncNotedAppOp.getOp(),
asyncNotedAppOp.getAttributionTag(),
asyncNotedAppOp.getMessage());
}
};
AppOpsManagerappOpsManager=getSystemService(AppOpsManager.class);
if(appOpsManager!=null){
appOpsManager.setOnOpNotedCallback(getMainExecutor(),appOpsCallback);
}
}

Content and code samples on this page are subject to the licenses described in the Content License. Java and OpenJDK are trademarks or registered trademarks of Oracle and/or its affiliates.

Last updated 2026年08月14日 UTC.