Exploring Android 11: Data Access Auditing

When building applications, user privacy is shifting more and more to the core of development values throughout the ecosystem. Aligned with this, Android 11 brings in a collection of changes to help improve the approaches to the privacy of user data in our apps.

Amongst these changes are the introduction of the new Data Access Auditing API, used to help developers become more aware of user data they are accessing – both from their own code and that of third party dependencies within the project. To add this functionality, the existing AppOpsManager class (API 19) has been extended to provide an OnOpNotedCallback (API 30) which can be registered against the class. This callback will then be triggered whenever private data has been accessed, regardless of the source of that call.

But why has this been added? As our projects grow, it can become difficult to keep track or even be aware of what private data is being accessed (and by who). We might be working on a project which has pieces of code we are not familiar with or aware of (such as legacy code) that is accessing private data, or even more ‘recent’ code that may be written in a part of the application that we are not too involved in. As well as these, third party SDKs may be accessing private user data that we are not aware of – be it SDKs that we are not aware of being used in the app, or even ones that we have added ourselves in more recent times.

To help add transparency around all of this private data access, the Data Access Auditing API offers a way for us to not only keep track of what private user data we are accessing throughout our application, but also educate ourselves in parts that we may not be aware of. With the OnOpNotedCallback from the AppOpsManager class, a callback event will be triggered whenever private data is accessed. Within this callback we can then handle the trigger however we desire – this could be in the form of local logging or even remote logging to help track and manage the data that we are accessing.

To enable this data logging, we need to begin by defining a reference to an OnOpNotedCallback:

val opsCallback = object : AppOpsManager.OnOpNotedCallback() {

    override fun onNoted(syncNotedAppOp: SyncNotedAppOp) {
        myLogger.logAppOpNoted(syncNotedAppOp.op)
    }

    override fun onSelfNoted(syncNotedAppOp: SyncNotedAppOp) {
        myLogger.logAppOpNoted(syncNotedAppOp.op)
    }

    override fun onAsyncNoted(asyncNotedAppOp: AsyncNotedAppOp) {
        myLogger.logAppOpNoted(asyncNotedAppOp.op)
    }
}

In this callback we can see several functions that have been implemented:

  • onNoted – called when an operation has been noted from a synchronous API call. This will be triggered on the thread that call the operation prior to the API returning the data.
  • onSelfNoted – similar to the onNoted callback, except this is called when an operation has been noted within the apps own process
  • onAsyncNoted – called when an operation has been noted that could not be provided to either onNoted or onSelfNoted. Whilst this attempted to be called after when the operation was noted, the delivery is not guaranteed and may not be directly after.

Now that we have the OnOpNotedCallback implemented, we can go ahead and register it using the AppOpsManager class:

val opsManager = getSystemService(AppOpsManager::class.java) as AppOpsManager
opsManager.setOnOpNotedCallback(mainExecutor, opsCallback)

With this in place, our callback will now receive events whenever private data is access within our app. Whilst this will help us to identify where this data is being accessed within our app, it’s likely that we’ll be receiving a lot of different events for different parts of our application. When it comes to grouping this data to help identify what features of our app are triggering these events, this could become both a difficult and manual process, or require us to implement some form of filtering within our logging service. To aid with this we can utilise what are known at Attribution Tags – these tags can be applied to where we access private data to then be provided to the OnOpNotedCallback functions.

To achieve this, the createAttributionContext() function (API 30) can be used to retrieve a Context object that is tagged using the identifier that we provide to it. This context can then be used when retrieving / accessing the services that utilise private user data – when the OnOpNotedCallback receives events for this data access, the assigned identifier will also be provided.

private lateinit var attributionContext: Context

    override fun onCreate(savedInstanceState: Bundle?) {
        attributionContext = createAttributionContext("searchNearbyShops")
    }

    fun getLocation() {
        val locationManager = attributionContext.getSystemService(
                LocationManager::class.java) as LocationManager
    }

Because this identifier is then provided to our OnOpNotedCallback callback we can then use this when logging the private data access. This means that now we know exactly where this data was accessed we can perform better analysis with simplified grouping in place.

val appOpsCallback = object : AppOpsManager.OnOpNotedCallback() {

    override fun onNoted(syncNotedAppOp: SyncNotedAppOp) {
        myLogger.logAppOpNoted(
            syncNotedAppOp.op,
            syncNotedAppOp.attributionTag
        )
    }

    override fun onSelfNoted(syncNotedAppOp: SyncNotedAppOp) {
        myLogger.logAppOpNoted(
            syncNotedAppOp.op,
            syncNotedAppOp.attributionTag
        )
    }

    override fun onAsyncNoted(asyncNotedAppOp: AsyncNotedAppOp) {
        myLogger.logAppOpNoted(
            syncNotedAppOp.op,
            syncNotedAppOp.attributionTag
        )
    }
}


In this post we’ve dived into the Data Access Auditing API and how it can be used in our Android 11 applications to both discover and monitor the access of private user data. With this in place we can ensure that our applications are only accessing the data that is required, when it is required. If Data Access Auditing is something you’ll be utilising in your applications, or you have any questions on the above then please feel free to reach out!