Merge "Describe how data access auditing works internally"
This commit is contained in:
committed by
Android (Google) Code Review
commit
52ec389661
@@ -231,7 +231,107 @@ system is tracking for it](https://developer.android.com/preview/privacy/data-ac
|
||||
As each runtime permission has an associated app-op this API is particularly useful for an app
|
||||
that want to find unexpected private data accesses.
|
||||
|
||||
### Tracking the last accesses via an API
|
||||
#### Implementation
|
||||
|
||||
The goal is to trigger a callback to `AppOpsManager.OnOpNotedCallback` any time a data provider
|
||||
declares that data was sent to the app (i.e. calls `AppOpsManager.noteOp`). There are four cases
|
||||
|
||||
##### Synchronous data accesses
|
||||
|
||||
This is the case where the client calls an API and the data is sent back as the return value of this
|
||||
API call. E.g. `LocationManager.getLastKnownLocation` returns the last known location as the return
|
||||
value of the method call.
|
||||
|
||||
In this case
|
||||
1. The client calls into a Android API in the Android framework, e.g. `LocationManager`
|
||||
2. The framework code calls via a `Binder` call into the data provider, e.g. the
|
||||
`LocationManagerService` residing in the system server.
|
||||
3. Somewhere in the data provider the data provider calls `AppOpsManager.noteOp` and thereby
|
||||
declares that data was accessed. This data access is recorded in
|
||||
`AppOpsManager.sAppOpsNotedInThisBinderTransaction`
|
||||
4. When the binder call returns the RPC code (`Binder`/`Parcel`) calls
|
||||
`AppOpsManager.readAndLogNotedAppops` which checks is the binder return value contained any
|
||||
prefix indicating that data was accessed. If so the RPC code calls `onNoted` on the the currently
|
||||
registered `OnOpNotedCallback`.
|
||||
5. The rest of the implementation is up to the client, but one to use the callbacks is for the
|
||||
client to take a stack trace in the `onNoted` implementation. This stack trace allows to pin point
|
||||
where in the app's code the data access came from.
|
||||
|
||||

|
||||
|
||||
In above graphics you can see that
|
||||
1. an app (`com.app.A`, red) is calling into the android framework
|
||||
(blue).
|
||||
1. The call triggers a RPC call into the data provider (green).
|
||||
1. The data provider calls `AppOpsManager.noteOp` (first star)
|
||||
1. On the return from the RPC call the framework code (second star) realizes that there was a data
|
||||
access and calls `OnOpNotedCallback.onNoted`.
|
||||
1. If at this time the code in onNoted would take a stack trace it would get what is in the gray
|
||||
box, i.e.
|
||||
```
|
||||
com.app.A.a
|
||||
- com.app.A.b
|
||||
- com.app.A.c
|
||||
- com.app.A.d
|
||||
- android...Manager
|
||||
- several android internal RPC methods
|
||||
- com.app.B.onNoted (extends OnOpNotedCallback.onNoted)
|
||||
```
|
||||
|
||||
As `onNoted` also reports the attributionTag and the noted op the app can now build a mapping
|
||||
indicating what code accesses what private data.
|
||||
|
||||
##### Self data accesses
|
||||
|
||||
This is similar to the [synchronous data access](#synchronous-data-accesses) case only that the data
|
||||
provider and client are in the same process. In this case Android's RPC code is no involved and
|
||||
`AppOpsManager.noteOp` directly triggers `OnOpNotedCallback.onSelfNoted`. This should be a uncommon
|
||||
case as it is uncommon for an app to provide data, esp. to itself.
|
||||
|
||||
If an app takes above suggestion and collects stack traces for synchronous accesses self-accesses
|
||||
can be treated in the same way.
|
||||
|
||||
##### Async data accesses
|
||||
|
||||
There are cases where the data access is not directly triggered via an API. E.g.
|
||||
`LocationManager.requestLocationUpdates(listener)` registers a callback. Once the location subsystem
|
||||
determines a location it calls the registered listener with the data. There can be quite significant
|
||||
time between registering the listener and getting the data. In some cases (e.g. Geo-fencing) it
|
||||
might take days and the app registering for the data and the app receiving the data might not even
|
||||
be the same process or even version.
|
||||
|
||||
Hence above suggestion with taking the stack trace to determine what triggered the data access does
|
||||
not work. In this case it is recommended for data providers to come up with a way to help the app
|
||||
developer understand why a data access is triggered. E.g. in the case of
|
||||
`LocationManager.requestLocationUpdates(listener)` the data provider is setting the `message` field
|
||||
in`AppOpsManager.noteOp` to the system-identity hash code of the registered listener. There are
|
||||
convenience methods for that, e.g. `AppOpsManager.toReceiverId`. This `message` field is then
|
||||
delivered to the app inside the `AsyncNotedAppOp` parameter to `OnOpNotedCallback.onAsyncNoted`.
|
||||
|
||||
While this case is not as elegant as the synchronous case, a properly set `message` can often be
|
||||
enough for the app to figure out where the data access comes from. Async data accesses are less
|
||||
common than synchronous data accesses but they come in more variations. E.g. registered listeners,
|
||||
pending-intents, manifest broadcast receivers, activity starts, etc... Hence there is no one perfect
|
||||
message format. This is why the message field is a free text string.
|
||||
|
||||
It is very highly recommended for data providers to set appropriate `message` parameters for their
|
||||
`AppOpsManager.noteOp` calls for all times where there is async data access. If no `message`
|
||||
parameter is set, the system defaults to a stack trace of the data provider code which is often slow
|
||||
and not useful.
|
||||
|
||||
Async data accesses also carry the attribution tag, but this can sometimes not be enough. Again, a
|
||||
properly set `message` parameter is the best choice.
|
||||
|
||||
##### Data providers implemented in native code
|
||||
|
||||
Some data providers (e.g. camera a microphone) are implemented using native code. As of now this is
|
||||
not properly hooked up to the Java logic. To make sure to always collect all data accesses all
|
||||
`AppOpsManager::noteOp` calls from native code trigger an [async data access](#async-data-accesses),
|
||||
no matter if the code is in a synchronous RPC or not.
|
||||
|
||||
This is not ideal and should be improved.
|
||||
|
||||
### Getting last data accesses via an API
|
||||
|
||||
To get the last accesses for an op or package an app can use `AppOpsManager.getPackagesForOps`.
|
||||
|
||||
|
||||
BIN
core/java/android/app/sync-data-access.png
Normal file
BIN
core/java/android/app/sync-data-access.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 74 KiB |
Reference in New Issue
Block a user