Loading core/java/android/app/AppOps.md +101 −1 Original line number Diff line number Diff line Loading @@ -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`. Loading core/java/android/app/sync-data-access.png 0 → 100644 +73.7 KiB Loading image diff... Loading
core/java/android/app/AppOps.md +101 −1 Original line number Diff line number Diff line Loading @@ -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`. Loading