Allow apps to collect which appops were noted

If private user data is send to an app the data provider should note an
app-op. This change adds a new API AppOpsManager#setNotedAppOpsCollector
that allows an app to get notified every time such an private data access
happens.

This will allow apps to monitor their own private data usage. Esp. with
big, old apps, distributed teams or 3rd party libraries it might not always
be clear what subsystems access private data.

There are three different situations how private data can be accessed and
an app op is noted:

1. Private data access inside a two-way binder call.
E.g. LocationManager#getLastKnownLocation. When we start a two way
binder transaction, we remember the calling uid via
AppOpsManager#collectNotedAppOps. Then when the data providing code
calls AppOpsManager#noteOp->AppOpsManager#markAppOpNoted the noted
app-op is remembered in
AppOpsManager#sAppOpsNotedInThisBinderTransaction. Then when returning
from the binder call, we add the list of noted app-ops to the
reply-parcel via AppOpsManager#prefixParcelWithAppOpsIfNeeded. On the
calling side we check if there were any app-ops noted in
AppOpsManager#readAndLogNotedAppops and then call the collector while
still in the binder code. This allows the collector e.g. collect a stack
trace which can be used to figure out what app code caused the
private data access.

2. Very complex apps might do permissions checks internal to themself.
I.e. an app notes an op for itself. We detect this case in
AppOpsManager#markAppOpNoted and immediately call the collector similar
to case (1).

3. Sometimes private data is accessed outside of a two-way binder call.
E.g. if an app registers a LocationListener an app-op is noted each time
a new location is send to the app. In this case it is not clear to the
framework which app-action triggered this app-op-note. Hence the data
provider has to describe in a AsyncNotedAppOp object when an why the
access happened. These objects are then send to the system server via
IAppOpsService#noteAsyncOp and then the collector in the app. There are
rare cases where a private data access happens before the app is running
(e.g. when a geo-fence is triggered). In this case we cache a small
amount of AsyncNotedAppOps (in AppOpsService#mUnforwardedAsyncNotedOps)
and deliver them when the app is ready for these events (in
AppOpsManager#setNotedAppOpsCollector).

Test: atest CtsAppOpsTestCases (includes new tests covering this
                                functionality)
Bug: 136505050
Change-Id: I96ded4a8d8d9bcb37a4555d9b1281cb57945ffa9
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 34045c9..d1be17c 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -28,13 +28,19 @@
 import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.app.usage.UsageStatsManager;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ParceledListSlice;
+import android.database.DatabaseUtils;
 import android.media.AudioAttributes.AttributeUsage;
 import android.os.Binder;
+import android.os.Build;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.HandlerThread;
 import android.os.IBinder;
+import android.os.Looper;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.Process;
@@ -49,9 +55,12 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.Immutable;
 import com.android.internal.app.IAppOpsActiveCallback;
+import com.android.internal.app.IAppOpsAsyncNotedCallback;
 import com.android.internal.app.IAppOpsCallback;
 import com.android.internal.app.IAppOpsNotedCallback;
 import com.android.internal.app.IAppOpsService;
+import com.android.internal.os.RuntimeInit;
+import com.android.internal.os.ZygoteInit;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.Preconditions;
 
@@ -59,10 +68,12 @@
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
+import java.lang.reflect.Method;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.BitSet;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -118,6 +129,12 @@
     private final ArrayMap<OnOpNotedListener, IAppOpsNotedCallback> mNotedWatchers =
             new ArrayMap<>();
 
+    private static final Object sLock = new Object();
+
+    /** Current {@link AppOpsCollector}. Change via {@link #setNotedAppOpsCollector} */
+    @GuardedBy("sLock")
+    private static @Nullable AppOpsCollector sNotedAppOpsCollector;
+
     static IBinder sToken;
 
     /** @hide */
@@ -1126,6 +1143,22 @@
     /** @hide Query all packages on device */
     public static final String OPSTR_QUERY_ALL_PACKAGES = "android:query_all_packages";
 
+
+    /** {@link #sAppOpsToNote} not initialized yet for this op */
+    private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0;
+    /** Should not collect noting of this app-op in {@link #sAppOpsToNote} */
+    private static final byte SHOULD_NOT_COLLECT_NOTE_OP = 1;
+    /** Should collect noting of this app-op in {@link #sAppOpsToNote} */
+    private static final byte SHOULD_COLLECT_NOTE_OP = 2;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = { "SHOULD_" }, value = {
+            SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED,
+            SHOULD_NOT_COLLECT_NOTE_OP,
+            SHOULD_COLLECT_NOTE_OP
+    })
+    private @interface ShouldCollectNoteOp {}
+
     // Warning: If an permission is added here it also has to be added to
     // com.android.packageinstaller.permission.utils.EventLogger
     private static final int[] RUNTIME_AND_APPOP_PERMISSIONS_OPS = {
@@ -1988,6 +2021,27 @@
      */
     private static HashMap<String, Integer> sPermToOp = new HashMap<>();
 
+    /**
+     * Set to the uid of the caller if this thread is currently executing a two-way binder
+     * transaction. Not set if this thread is currently not executing a two way binder transaction.
+     *
+     * @see #startNotedAppOpsCollection
+     * @see #markAppOpNoted
+     */
+    private static final ThreadLocal<Integer> sBinderThreadCallingUid = new ThreadLocal<>();
+
+    /**
+     * If a thread is currently executing a two-way binder transaction, this stores the op-codes of
+     * the app-ops that were noted during this transaction.
+     *
+     * @see #markAppOpNoted
+     */
+    private static final ThreadLocal<long[]> sAppOpsNotedInThisBinderTransaction =
+            new ThreadLocal<>();
+
+    /** Whether noting for an appop should be collected */
+    private static final @ShouldCollectNoteOp byte[] sAppOpsToNote = new byte[_NUM_OP];
+
     static {
         if (sOpToSwitch.length != _NUM_OP) {
             throw new IllegalStateException("sOpToSwitch length " + sOpToSwitch.length
@@ -2031,6 +2085,12 @@
                 sPermToOp.put(sOpPerms[op], op);
             }
         }
+
+        if ((_NUM_OP + Long.SIZE - 1) / Long.SIZE != 2) {
+            // The code currently assumes that the length of sAppOpsNotedInThisBinderTransaction is
+            // two longs
+            throw new IllegalStateException("notedAppOps collection code assumes < 128 appops");
+        }
     }
 
     /** @hide */
@@ -4350,8 +4410,8 @@
      * The mode of the ops returned are set for the package but may not reflect their effective
      * state due to UID policy or because it's controlled by a different master op.
      *
-     * Use {@link #unsafeCheckOp(String, int, String)}} or {@link #noteOp(String, int, String)}
-     * if the effective mode is needed.
+     * Use {@link #unsafeCheckOp(String, int, String)}} or
+     * {@link #noteOp(String, int, String, String)} if the effective mode is needed.
      *
      * @param ops The set of operations you are interested in, or null if you want all of them.
      * @hide
@@ -4374,8 +4434,8 @@
      * The mode of the ops returned are set for the package but may not reflect their effective
      * state due to UID policy or because it's controlled by a different master op.
      *
-     * Use {@link #unsafeCheckOp(String, int, String)}} or {@link #noteOp(String, int, String)}
-     * if the effective mode is needed.
+     * Use {@link #unsafeCheckOp(String, int, String)}} or
+     * {@link #noteOp(String, int, String, String)} if the effective mode is needed.
      *
      * @param ops The set of operations you are interested in, or null if you want all of them.
      * @hide
@@ -4396,8 +4456,8 @@
      * The mode of the ops returned are set for the package but may not reflect their effective
      * state due to UID policy or because it's controlled by a different master op.
      *
-     * Use {@link #unsafeCheckOp(String, int, String)}} or {@link #noteOp(String, int, String)}
-     * if the effective mode is needed.
+     * Use {@link #unsafeCheckOp(String, int, String)}} or
+     * {@link #noteOp(String, int, String, String)} if the effective mode is needed.
      *
      * @param uid The uid of the application of interest.
      * @param packageName The name of the application of interest.
@@ -4429,8 +4489,8 @@
      * The mode of the ops returned are set for the package but may not reflect their effective
      * state due to UID policy or because it's controlled by a different master op.
      *
-     * Use {@link #unsafeCheckOp(String, int, String)}} or {@link #noteOp(String, int, String)}
-     * if the effective mode is needed.
+     * Use {@link #unsafeCheckOp(String, int, String)}} or
+     * {@link #noteOp(String, int, String, String)} if the effective mode is needed.
      *
      * @param uid The uid of the application of interest.
      * @param packageName The name of the application of interest.
@@ -4820,7 +4880,7 @@
      *
      * @see #isOperationActive
      * @see #stopWatchingActive
-     * @see #startOp(int, int, String)
+     * @see #startOp(int, int, String, boolean, String)
      * @see #finishOp(int, int, String)
      */
     // TODO: Uncomment below annotation once b/73559440 is fixed
@@ -4871,7 +4931,7 @@
      *
      * @see #isOperationActive
      * @see #startWatchingActive
-     * @see #startOp(int, int, String)
+     * @see #startOp(int, int, String, boolean, String)
      * @see #finishOp(int, int, String)
      */
     public void stopWatchingActive(@NonNull OnOpActiveChangedListener callback) {
@@ -4902,7 +4962,7 @@
      *
      * @see #startWatchingActive(int[], OnOpActiveChangedListener)
      * @see #stopWatchingNoted(OnOpNotedListener)
-     * @see #noteOp(String, int, String)
+     * @see #noteOp(String, int, String, String)
      *
      * @hide
      */
@@ -4934,7 +4994,7 @@
      * Unregistering a non-registered callback has no effect.
      *
      * @see #startWatchingNoted(int[], OnOpNotedListener)
-     * @see #noteOp(String, int, String)
+     * @see #noteOp(String, int, String, String)
      *
      * @hide
      */
@@ -4969,15 +5029,16 @@
 
     /**
      * Do a quick check for whether an application might be able to perform an operation.
-     * This is <em>not</em> a security check; you must use {@link #noteOp(String, int, String)}
-     * or {@link #startOp(String, int, String)} for your actual security checks, which also
-     * ensure that the given uid and package name are consistent.  This function can just be
-     * used for a quick check to see if an operation has been disabled for the application,
+     * This is <em>not</em> a security check; you must use {@link #noteOp(String, int, String,
+     * String)} or {@link #startOp(String, int, String, String)} for your actual security checks,
+     * which also ensure that the given uid and package name are consistent. This function can just
+     * be used for a quick check to see if an operation has been disabled for the application,
      * as an early reject of some work.  This does not modify the time stamp or other data
      * about the operation.
      *
      * <p>Important things this will not do (which you need to ultimate use
-     * {@link #noteOp(String, int, String)} or {@link #startOp(String, int, String)} to cover):</p>
+     * {@link #noteOp(String, int, String, String)} or
+     * {@link #startOp(String, int, String, String)} to cover):</p>
      * <ul>
      *     <li>Verifying the uid and package are consistent, so callers can't spoof
      *     their identity.</li>
@@ -5048,126 +5109,305 @@
     }
 
     /**
+     * @deprecated Use {@link #noteOp(String, int, String, String)} instead
+     */
+    @Deprecated
+    public int noteOp(@NonNull String op, int uid, @NonNull String packageName) {
+        return noteOp(op, uid, packageName, null);
+    }
+
+    /**
+     * @deprecated Use {@link #noteOp(String, int, String, String)} instead
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
+            + "#noteOp(java.lang.String, int, java.lang.String, java.lang.String)} instead")
+    @Deprecated
+    public int noteOp(int op) {
+        return noteOp(op, Process.myUid(), mContext.getOpPackageName(), null);
+    }
+
+    /**
+     * @deprecated Use {@link #noteOp(String, int, String, String)} instead
+     *
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
+            + "#noteOp(java.lang.String, int, java.lang.String, java.lang.String)} instead")
+    public int noteOp(int op, int uid, @Nullable String packageName) {
+        return noteOp(op, uid, packageName, null);
+    }
+
+    /**
      * Make note of an application performing an operation.  Note that you must pass
      * in both the uid and name of the application to be checked; this function will verify
      * that these two match, and if not, return {@link #MODE_IGNORED}.  If this call
      * succeeds, the last execution time of the operation for this app will be updated to
      * the current time.
+     *
      * @param op The operation to note.  One of the OPSTR_* constants.
      * @param uid The user id of the application attempting to perform the operation.
      * @param packageName The name of the application attempting to perform the operation.
-     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
-     * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
-     * causing the app to crash).
-     * @throws SecurityException If the app has been configured to crash on this op.
-     */
-    public int noteOp(@NonNull String op, int uid, @NonNull String packageName) {
-        return noteOp(strOpToOp(op), uid, packageName);
-    }
-
-    /**
-     * Like {@link #noteOp} but instead of throwing a {@link SecurityException} it
-     * returns {@link #MODE_ERRORED}.
-     */
-    public int noteOpNoThrow(@NonNull String op, int uid, @NonNull String packageName) {
-        return noteOpNoThrow(strOpToOp(op), uid, packageName);
-    }
-
-    /**
-     * Make note of an application performing an operation on behalf of another
-     * application when handling an IPC. Note that you must pass the package name
-     * of the application that is being proxied while its UID will be inferred from
-     * the IPC state; this function will verify that the calling uid and proxied
-     * package name match, and if not, return {@link #MODE_IGNORED}. If this call
-     * succeeds, the last execution time of the operation for the proxied app and
-     * your app will be updated to the current time.
-     * @param op The operation to note.  One of the OPSTR_* constants.
-     * @param proxiedPackageName The name of the application calling into the proxy application.
-     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
-     * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
-     * causing the app to crash).
-     * @throws SecurityException If the app has been configured to crash on this op.
-     */
-    public int noteProxyOp(@NonNull String op, @NonNull String proxiedPackageName) {
-        return noteProxyOp(strOpToOp(op), proxiedPackageName);
-    }
-
-    /**
-     * Like {@link #noteProxyOp(String, String)} but instead
-     * of throwing a {@link SecurityException} it returns {@link #MODE_ERRORED}.
+     * @param message A message describing the reason the op was noted
      *
-     * <p>This API requires the package with the {@code proxiedPackageName} to belongs to
-     * {@link Binder#getCallingUid()}.
+     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
+     * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
+     * causing the app to crash).
+     *
+     * @throws SecurityException If the app has been configured to crash on this op.
      */
-    public int noteProxyOpNoThrow(@NonNull String op, @NonNull String proxiedPackageName) {
-        return noteProxyOpNoThrow(strOpToOp(op), proxiedPackageName);
+    public int noteOp(@NonNull String op, int uid, @Nullable String packageName,
+            @Nullable String message) {
+        return noteOp(strOpToOp(op), uid, packageName, message);
     }
 
     /**
-     * Like {@link #noteProxyOpNoThrow(String, String)} but allows to specify the proxied uid.
+     * Make note of an application performing an operation.  Note that you must pass
+     * in both the uid and name of the application to be checked; this function will verify
+     * that these two match, and if not, return {@link #MODE_IGNORED}.  If this call
+     * succeeds, the last execution time of the operation for this app will be updated to
+     * the current time.
+     *
+     * @param op The operation to note.  One of the OP_* constants.
+     * @param uid The user id of the application attempting to perform the operation.
+     * @param packageName The name of the application attempting to perform the operation.
+     * @param message A message describing the reason the op was noted
+     *
+     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
+     * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
+     * causing the app to crash).
+     *
+     * @throws SecurityException If the app has been configured to crash on this op.
+     *
+     * @hide
+     */
+    public int noteOp(int op, int uid, @Nullable String packageName, @Nullable String message) {
+        final int mode = noteOpNoThrow(op, uid, packageName, message);
+        if (mode == MODE_ERRORED) {
+            throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
+        }
+        return mode;
+    }
+
+    /**
+     * @deprecated Use {@link #noteOpNoThrow(String, int, String, String)} instead
+     */
+    @Deprecated
+    public int noteOpNoThrow(@NonNull String op, int uid, @NonNull String packageName) {
+        return noteOpNoThrow(op, uid, packageName, null);
+    }
+
+    /**
+     * @deprecated Use {@link #noteOpNoThrow(int, int, String, String)} instead
+     *
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
+            + "#noteOpNoThrow(java.lang.String, int, java.lang.String, java.lang.String)} instead")
+    public int noteOpNoThrow(int op, int uid, String packageName) {
+        return noteOpNoThrow(op, uid, packageName, null);
+    }
+
+    /**
+     * Like {@link #noteOp(String, int, String, String)} but instead of throwing a
+     * {@link SecurityException} it returns {@link #MODE_ERRORED}.
+     *
+     * @param op The operation to note.  One of the OPSTR_* constants.
+     * @param uid The user id of the application attempting to perform the operation.
+     * @param packageName The name of the application attempting to perform the operation.
+     * @param message A message describing the reason the op was noted
+     *
+     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
+     * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
+     * causing the app to crash).
+     */
+    public int noteOpNoThrow(@NonNull String op, int uid, @NonNull String packageName,
+            @Nullable String message) {
+        return noteOpNoThrow(strOpToOp(op), uid, packageName, message);
+    }
+
+    /**
+     * Like {@link #noteOp(String, int, String, String)} but instead of throwing a
+     * {@link SecurityException} it returns {@link #MODE_ERRORED}.
+     *
+     * @param op The operation to note.  One of the OP_* constants.
+     * @param uid The user id of the application attempting to perform the operation.
+     * @param packageName The name of the application attempting to perform the operation.
+     * @param message A message describing the reason the op was noted
+     *
+     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
+     * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
+     * causing the app to crash).
+     *
+     * @hide
+     */
+    public int noteOpNoThrow(int op, int uid, @Nullable String packageName,
+            @Nullable String message) {
+        try {
+            int mode = mService.noteOperation(op, uid, packageName);
+            if (mode == MODE_ALLOWED) {
+                markAppOpNoted(uid, packageName, op, message);
+            }
+
+            return mode;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * @deprecated Use {@link #noteProxyOp(String, String, int, String)} instead
+     */
+    @Deprecated
+    public int noteProxyOp(@NonNull String op, @NonNull String proxiedPackageName) {
+        return noteProxyOp(op, proxiedPackageName, Binder.getCallingUid(), null);
+    }
+
+    /**
+     * @deprecated Use {@link #noteProxyOp(String, String, int, String)} instead
+     *
+     * @hide
+     */
+    @Deprecated
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "Use {@link "
+            + "#noteProxyOp(java.lang.String, java.lang.String, int, java.lang.String)} instead")
+    public int noteProxyOp(int op, @Nullable String proxiedPackageName) {
+        return noteProxyOp(op, proxiedPackageName, Binder.getCallingUid(), null);
+    }
+
+    /**
+     * Make note of an application performing an operation on behalf of another application when
+     * handling an IPC. This function will verify that the calling uid and proxied package name
+     * match, and if not, return {@link #MODE_IGNORED}. If this call succeeds, the last execution
+     * time of the operation for the proxied app and your app will be updated to the current time.
+     *
+     * @param op The operation to note. One of the OP_* constants.
+     * @param proxiedPackageName The name of the application calling into the proxy application.
+     * @param proxiedUid The uid of the proxied application
+     * @param message A message describing the reason the op was noted
+     *
+     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or {@link #MODE_IGNORED}
+     * if it is not allowed and should be silently ignored (without causing the app to crash).
+     *
+     * @throws SecurityException If the proxy or proxied app has been configured to crash on this
+     * op.
+     *
+     * @hide
+     */
+    public int noteProxyOp(int op, @Nullable String proxiedPackageName, int proxiedUid,
+            @Nullable String message) {
+        int mode = noteProxyOpNoThrow(op, proxiedPackageName, proxiedUid, message);
+        if (mode == MODE_ERRORED) {
+            throw new SecurityException("Proxy package " + mContext.getOpPackageName()
+                    + " from uid " + Process.myUid() + " or calling package " + proxiedPackageName
+                    + " from uid " + proxiedUid + " not allowed to perform " + sOpNames[op]);
+        }
+        return mode;
+    }
+
+    /**
+     * Make note of an application performing an operation on behalf of another application when
+     * handling an IPC. This function will verify that the calling uid and proxied package name
+     * match, and if not, return {@link #MODE_IGNORED}. If this call succeeds, the last execution
+     * time of the operation for the proxied app and your app will be updated to the current time.
+     *
+     * @param op The operation to note. One of the OPSTR_* constants.
+     * @param proxiedPackageName The name of the application calling into the proxy application.
+     * @param proxiedUid The uid of the proxied application
+     * @param message A message describing the reason the op was noted
+     *
+     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or {@link #MODE_IGNORED}
+     * if it is not allowed and should be silently ignored (without causing the app to crash).
+     *
+     * @throws SecurityException If the proxy or proxied app has been configured to crash on this
+     * op.
+     */
+    public int noteProxyOp(@NonNull String op, @Nullable String proxiedPackageName, int proxiedUid,
+            @Nullable String message) {
+        return noteProxyOp(strOpToOp(op), proxiedPackageName, proxiedUid, message);
+    }
+
+    /**
+     * @deprecated Use {@link #noteProxyOpNoThrow(String, String, int, String)} instead
+     */
+    @Deprecated
+    public int noteProxyOpNoThrow(@NonNull String op, @NonNull String proxiedPackageName) {
+        return noteProxyOpNoThrow(op, proxiedPackageName, Binder.getCallingUid(), null);
+    }
+
+    /**
+     * @deprecated Use {@link #noteProxyOpNoThrow(String, String, int, String)} instead
+     */
+    @Deprecated
+    public int noteProxyOpNoThrow(@NonNull String op, @Nullable String proxiedPackageName,
+            int proxiedUid) {
+        return noteProxyOpNoThrow(op, proxiedPackageName, proxiedUid, null);
+    }
+
+    /**
+     * Like {@link #noteProxyOp(String, String, int, String)} but instead
+     * of throwing a {@link SecurityException} it returns {@link #MODE_ERRORED}.
      *
      * <p>This API requires package with the {@code proxiedPackageName} to belong to
      * {@code proxiedUid}.
      *
      * @param op The op to note
+     * @param proxiedPackageName The package to note the op for
+     * @param proxiedUid The uid the package belongs to
+     * @param message A message describing the reason the op was noted
+     */
+    public int noteProxyOpNoThrow(@NonNull String op, @Nullable String proxiedPackageName,
+            int proxiedUid, @Nullable String message) {
+        return noteProxyOpNoThrow(strOpToOp(op), proxiedPackageName, proxiedUid, message);
+    }
+
+    /**
+     * Like {@link #noteProxyOp(int, String, int, String)} but instead
+     * of throwing a {@link SecurityException} it returns {@link #MODE_ERRORED}.
+     *
+     * @param op The op to note
      * @param proxiedPackageName The package to note the op for or {@code null} if the op should be
      *                           noted for the "android" package
      * @param proxiedUid The uid the package belongs to
+     * @param message A message describing the reason the op was noted
+     *
+     * @hide
      */
-    public int noteProxyOpNoThrow(@NonNull String op, @Nullable String proxiedPackageName,
-            int proxiedUid) {
-        return noteProxyOpNoThrow(strOpToOp(op), proxiedPackageName, proxiedUid);
-    }
+    public int noteProxyOpNoThrow(int op, @Nullable String proxiedPackageName, int proxiedUid,
+            @Nullable String message) {
+        int myUid = Process.myUid();
 
-    /**
-     * Report that an application has started executing a long-running operation.  Note that you
-     * must pass in both the uid and name of the application to be checked; this function will
-     * verify that these two match, and if not, return {@link #MODE_IGNORED}.  If this call
-     * succeeds, the last execution time of the operation for this app will be updated to
-     * the current time and the operation will be marked as "running".  In this case you must
-     * later call {@link #finishOp(String, int, String)} to report when the application is no
-     * longer performing the operation.
-     * @param op The operation to start.  One of the OPSTR_* constants.
-     * @param uid The user id of the application attempting to perform the operation.
-     * @param packageName The name of the application attempting to perform the operation.
-     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
-     * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
-     * causing the app to crash).
-     * @throws SecurityException If the app has been configured to crash on this op.
-     */
-    public int startOp(@NonNull String op, int uid, @NonNull String packageName) {
-        return startOp(strOpToOp(op), uid, packageName);
-    }
+        try {
+            int mode = mService.noteProxyOperation(op, myUid, mContext.getOpPackageName(),
+                    proxiedUid, proxiedPackageName);
+            if (mode == MODE_ALLOWED
+                    // Only collect app-ops when the proxy is trusted
+                    && mContext.checkPermission(Manifest.permission.UPDATE_APP_OPS_STATS, -1, myUid)
+                    == PackageManager.PERMISSION_GRANTED) {
+                markAppOpNoted(proxiedUid, proxiedPackageName, op, message);
+            }
 
-    /**
-     * Like {@link #startOp} but instead of throwing a {@link SecurityException} it
-     * returns {@link #MODE_ERRORED}.
-     */
-    public int startOpNoThrow(@NonNull String op, int uid, @NonNull String packageName) {
-        return startOpNoThrow(strOpToOp(op), uid, packageName);
-    }
-
-    /**
-     * Report that an application is no longer performing an operation that had previously
-     * been started with {@link #startOp(String, int, String)}.  There is no validation of input
-     * or result; the parameters supplied here must be the exact same ones previously passed
-     * in when starting the operation.
-     */
-    public void finishOp(@NonNull String op, int uid, @NonNull String packageName) {
-        finishOp(strOpToOp(op), uid, packageName);
+            return mode;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
     }
 
     /**
      * Do a quick check for whether an application might be able to perform an operation.
-     * This is <em>not</em> a security check; you must use {@link #noteOp(int, int, String)}
-     * or {@link #startOp(int, int, String)} for your actual security checks, which also
-     * ensure that the given uid and package name are consistent.  This function can just be
-     * used for a quick check to see if an operation has been disabled for the application,
-     * as an early reject of some work.  This does not modify the time stamp or other data
-     * about the operation.
+     * This is <em>not</em> a security check; you must use {@link #noteOp(String, int, String,
+     * String)} or {@link #startOp(int, int, String, boolean, String)} for your actual security
+     * checks, which also ensure that the given uid and package name are consistent. This function
+     * can just be used for a quick check to see if an operation has been disabled for the
+     * application, as an early reject of some work.  This does not modify the time stamp or other
+     * data about the operation.
      *
      * <p>Important things this will not do (which you need to ultimate use
-     * {@link #noteOp(int, int, String)} or {@link #startOp(int, int, String)} to cover):</p>
+     * {@link #noteOp(String, int, String, String)} or
+     * {@link #startOp(int, int, String, boolean, String)} to cover):</p>
      * <ul>
      *     <li>Verifying the uid and package are consistent, so callers can't spoof
      *     their identity.</li>
@@ -5259,107 +5499,6 @@
         }
     }
 
-    /**
-     * Make note of an application performing an operation.  Note that you must pass
-     * in both the uid and name of the application to be checked; this function will verify
-     * that these two match, and if not, return {@link #MODE_IGNORED}.  If this call
-     * succeeds, the last execution time of the operation for this app will be updated to
-     * the current time.
-     * @param op The operation to note.  One of the OP_* constants.
-     * @param uid The user id of the application attempting to perform the operation.
-     * @param packageName The name of the application attempting to perform the operation.
-     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
-     * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
-     * causing the app to crash).
-     * @throws SecurityException If the app has been configured to crash on this op.
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public int noteOp(int op, int uid, String packageName) {
-        final int mode = noteOpNoThrow(op, uid, packageName);
-        if (mode == MODE_ERRORED) {
-            throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
-        }
-        return mode;
-    }
-
-    /**
-     * Make note of an application performing an operation on behalf of another
-     * application when handling an IPC. Note that you must pass the package name
-     * of the application that is being proxied while its UID will be inferred from
-     * the IPC state; this function will verify that the calling uid and proxied
-     * package name match, and if not, return {@link #MODE_IGNORED}. If this call
-     * succeeds, the last execution time of the operation for the proxied app and
-     * your app will be updated to the current time.
-     * @param op The operation to note. One of the OPSTR_* constants.
-     * @param proxiedPackageName The name of the application calling into the proxy application.
-     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
-     * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
-     * causing the app to crash).
-     * @throws SecurityException If the proxy or proxied app has been configured to
-     * crash on this op.
-     *
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public int noteProxyOp(int op, String proxiedPackageName) {
-        int mode = noteProxyOpNoThrow(op, proxiedPackageName);
-        if (mode == MODE_ERRORED) {
-            throw new SecurityException("Proxy package " + mContext.getOpPackageName()
-                    + " from uid " + Process.myUid() + " or calling package "
-                    + proxiedPackageName + " from uid " + Binder.getCallingUid()
-                    + " not allowed to perform " + sOpNames[op]);
-        }
-        return mode;
-    }
-
-    /**
-     * Like {@link #noteProxyOp(int, String)} but instead
-     * of throwing a {@link SecurityException} it returns {@link #MODE_ERRORED}.
-     * @hide
-     */
-    public int noteProxyOpNoThrow(int op, String proxiedPackageName, int proxiedUid) {
-        try {
-            return mService.noteProxyOperation(op, Process.myUid(), mContext.getOpPackageName(),
-                    proxiedUid, proxiedPackageName);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
-     * Like {@link #noteProxyOp(int, String)} but instead
-     * of throwing a {@link SecurityException} it returns {@link #MODE_ERRORED}.
-     *
-     * <p>This API requires the package with {@code proxiedPackageName} to belongs to
-     * {@link Binder#getCallingUid()}.
-     *
-     * @hide
-     */
-    public int noteProxyOpNoThrow(int op, String proxiedPackageName) {
-        return noteProxyOpNoThrow(op, proxiedPackageName, Binder.getCallingUid());
-    }
-
-    /**
-     * Like {@link #noteOp} but instead of throwing a {@link SecurityException} it
-     * returns {@link #MODE_ERRORED}.
-     * @hide
-     */
-    @UnsupportedAppUsage
-    public int noteOpNoThrow(int op, int uid, String packageName) {
-        try {
-            return mService.noteOperation(op, uid, packageName);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /** @hide */
-    @UnsupportedAppUsage
-    public int noteOp(int op) {
-        return noteOp(op, Process.myUid(), mContext.getOpPackageName());
-    }
-
     /** @hide */
     @UnsupportedAppUsage
     public static IBinder getToken(IAppOpsService service) {
@@ -5376,54 +5515,86 @@
         }
     }
 
-    /** @hide */
-    public int startOp(int op) {
-        return startOp(op, Process.myUid(), mContext.getOpPackageName());
+
+    /**
+     * @deprecated use {@link #startOp(String, int, String, String)} instead
+     */
+    @Deprecated
+    public int startOp(@NonNull String op, int uid, @NonNull String packageName) {
+        return startOp(op, uid, packageName, null);
     }
 
     /**
-     * Report that an application has started executing a long-running operation.  Note that you
-     * must pass in both the uid and name of the application to be checked; this function will
-     * verify that these two match, and if not, return {@link #MODE_IGNORED}.  If this call
-     * succeeds, the last execution time of the operation for this app will be updated to
-     * the current time and the operation will be marked as "running".  In this case you must
-     * later call {@link #finishOp(int, int, String)} to report when the application is no
-     * longer performing the operation.
+     * @deprecated Use {@link #startOp(int, int, String, boolean, String)} instead
      *
-     * @param op The operation to start.  One of the OP_* constants.
-     * @param uid The user id of the application attempting to perform the operation.
-     * @param packageName The name of the application attempting to perform the operation.
-     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
-     * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
-     * causing the app to crash).
-     * @throws SecurityException If the app has been configured to crash on this op.
      * @hide
      */
-    public int startOp(int op, int uid, String packageName) {
-        return startOp(op, uid, packageName, false);
+    @Deprecated
+    public int startOp(int op) {
+        return startOp(op, Process.myUid(), mContext.getOpPackageName(), false, null);
     }
 
     /**
-     * Report that an application has started executing a long-running operation. Similar
-     * to {@link #startOp(String, int, String) except that if the mode is {@link #MODE_DEFAULT}
-     * the operation should succeed since the caller has performed its standard permission
-     * checks which passed and would perform the protected operation for this mode.
+     * @deprecated Use {@link #startOp(int, int, String, boolean, String)} instead
+     *
+     * @hide
+     */
+    @Deprecated
+    public int startOp(int op, int uid, String packageName) {
+        return startOp(op, uid, packageName, false, null);
+    }
+
+    /**
+     * @deprecated Use {@link #startOp(int, int, String, boolean, String)} instead
+     *
+     * @hide
+     */
+    @Deprecated
+    public int startOp(int op, int uid, String packageName, boolean startIfModeDefault) {
+        return startOp(op, uid, packageName, startIfModeDefault, null);
+    }
+
+    /**
+     * Report that an application has started executing a long-running operation.
+     *
+     * @param op The operation to start.  One of the OPSTR_* constants.
+     * @param uid The user id of the application attempting to perform the operation.
+     * @param packageName The name of the application attempting to perform the operation.
+     * @param message Description why op was started
+     *
+     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
+     * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
+     * causing the app to crash).
+     *
+     * @throws SecurityException If the app has been configured to crash on this op or
+     * the package is not in the passed in UID.
+     */
+    public int startOp(@NonNull String op, int uid, @Nullable String packageName,
+            @Nullable String message) {
+        return startOp(strOpToOp(op), uid, packageName, false, message);
+    }
+
+    /**
+     * Report that an application has started executing a long-running operation.
      *
      * @param op The operation to start.  One of the OP_* constants.
      * @param uid The user id of the application attempting to perform the operation.
      * @param packageName The name of the application attempting to perform the operation.
+     * @param startIfModeDefault Whether to start if mode is {@link #MODE_DEFAULT}.
+     * @param message Description why op was started
+     *
      * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
      * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
      * causing the app to crash).
-     * @param startIfModeDefault Whether to start if mode is {@link #MODE_DEFAULT}.
      *
      * @throws SecurityException If the app has been configured to crash on this op or
      * the package is not in the passed in UID.
      *
      * @hide
      */
-    public int startOp(int op, int uid, String packageName, boolean startIfModeDefault) {
-        final int mode = startOpNoThrow(op, uid, packageName, startIfModeDefault);
+    public int startOp(int op, int uid, @Nullable String packageName, boolean startIfModeDefault,
+            @Nullable String message) {
+        final int mode = startOpNoThrow(op, uid, packageName, startIfModeDefault, message);
         if (mode == MODE_ERRORED) {
             throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
         }
@@ -5431,45 +5602,111 @@
     }
 
     /**
-     * Like {@link #startOp} but instead of throwing a {@link SecurityException} it
-     * returns {@link #MODE_ERRORED}.
-     * @hide
+     * @deprecated use {@link #startOpNoThrow(String, int, String, String)} instead
      */
-    public int startOpNoThrow(int op, int uid, String packageName) {
-        return startOpNoThrow(op, uid, packageName, false);
+    @Deprecated
+    public int startOpNoThrow(@NonNull String op, int uid, @NonNull String packageName) {
+        return startOpNoThrow(op, uid, packageName, null);
     }
 
     /**
-     * Like {@link #startOp(int, int, String, boolean)} but instead of throwing a
+     * @deprecated Use {@link #startOpNoThrow(int, int, String, boolean, String} instead
+     *
+     * @hide
+     */
+    @Deprecated
+    public int startOpNoThrow(int op, int uid, String packageName) {
+        return startOpNoThrow(op, uid, packageName, false, null);
+    }
+
+    /**
+     * @deprecated Use {@link #startOpNoThrow(int, int, String, boolean, String} instead
+     *
+     * @hide
+     */
+    @Deprecated
+    public int startOpNoThrow(int op, int uid, String packageName, boolean startIfModeDefault) {
+        return startOpNoThrow(op, uid, packageName, startIfModeDefault, null);
+    }
+
+    /**
+     * Like {@link #startOp(String, int, String, String)} but instead of throwing a
      * {@link SecurityException} it returns {@link #MODE_ERRORED}.
      *
      * @param op The operation to start.  One of the OP_* constants.
      * @param uid The user id of the application attempting to perform the operation.
      * @param packageName The name of the application attempting to perform the operation.
+     * @param message Description why op was started
+     *
      * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
      * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
      * causing the app to crash).
+     */
+    public int startOpNoThrow(@NonNull String op, int uid, @NonNull String packageName,
+            @Nullable String message) {
+        return startOpNoThrow(strOpToOp(op), uid, packageName, false, message);
+    }
+
+    /**
+     * Like {@link #startOp(int, int, String, boolean, String)} but instead of throwing a
+     * {@link SecurityException} it returns {@link #MODE_ERRORED}.
+     *
+     * @param op The operation to start.  One of the OP_* constants.
+     * @param uid The user id of the application attempting to perform the operation.
+     * @param packageName The name of the application attempting to perform the operation.
      * @param startIfModeDefault Whether to start if mode is {@link #MODE_DEFAULT}.
+     * @param message Description why op was started
+     *
+     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
+     * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
+     * causing the app to crash).
      *
      * @hide
      */
-    public int startOpNoThrow(int op, int uid, String packageName, boolean startIfModeDefault) {
+    public int startOpNoThrow(int op, int uid, @NonNull String packageName,
+            boolean startIfModeDefault, @Nullable String message) {
         try {
-            return mService.startOperation(getToken(mService), op, uid, packageName,
+            int mode = mService.startOperation(getToken(mService), op, uid, packageName,
                     startIfModeDefault);
+            if (mode == MODE_ALLOWED) {
+                markAppOpNoted(uid, packageName, op, message);
+            }
+
+            return mode;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
-     * Report that an application is no longer performing an operation that had previously
-     * been started with {@link #startOp(int, int, String)}.  There is no validation of input
-     * or result; the parameters supplied here must be the exact same ones previously passed
-     * in when starting the operation.
+     * @deprecated Use {@link #finishOp(String, int, String)} instead
+     *
      * @hide
      */
-    public void finishOp(int op, int uid, String packageName) {
+    @Deprecated
+    public void finishOp(int op) {
+        finishOp(op, Process.myUid(), mContext.getOpPackageName());
+    }
+
+    /**
+     * Report that an application is no longer performing an operation that had previously
+     * been started with {@link #startOp(String, int, String, String)}.  There is no validation of
+     * input or result; the parameters supplied here must be the exact same ones previously passed
+     * in when starting the operation.
+     */
+    public void finishOp(@NonNull String op, int uid, @NonNull String packageName) {
+        finishOp(strOpToOp(op), uid, packageName);
+    }
+
+    /**
+     * Report that an application is no longer performing an operation that had previously
+     * been started with {@link #startOp(int, int, String, boolean, String)}. There is no
+     * validation of input or result; the parameters supplied here must be the exact same ones
+     * previously passed in when starting the operation.
+     *
+     * @hide
+     */
+    public void finishOp(int op, int uid, @NonNull String packageName) {
         try {
             mService.finishOperation(getToken(mService), op, uid, packageName);
         } catch (RemoteException e) {
@@ -5477,25 +5714,399 @@
         }
     }
 
-    /** @hide */
-    public void finishOp(int op) {
-        finishOp(op, Process.myUid(), mContext.getOpPackageName());
-    }
-
     /**
      * Checks whether the given op for a package is active.
      * <p>
      * If you don't hold the {@code android.Manifest.permission#WATCH_APPOPS}
      * permission you can query only for your UID.
      *
-     * @see #finishOp(int)
-     * @see #startOp(int)
+     * @see #finishOp(String, int, String)
+     * @see #startOp(String, int, String, String)
      */
     public boolean isOpActive(@NonNull String op, int uid, @NonNull String packageName) {
         return isOperationActive(strOpToOp(op), uid, packageName);
     }
 
     /**
+     * Start collection of noted appops on this thread.
+     *
+     * <p>Called at the beginning of a two way binder transaction.
+     *
+     * @see #finishNotedAppOpsCollection()
+     *
+     * @hide
+     */
+    public static void startNotedAppOpsCollection(int callingUid) {
+        sBinderThreadCallingUid.set(callingUid);
+    }
+
+    /**
+     * State of a temporarily paused noted app-ops collection.
+     *
+     * @see #pauseNotedAppOpsCollection()
+     *
+     * @hide
+     */
+    public static class PausedNotedAppOpsCollection {
+        final int mUid;
+        final @Nullable long[] mCollectedNotedAppOps;
+
+        PausedNotedAppOpsCollection(int uid, @Nullable long[] collectedNotedAppOps) {
+            mUid = uid;
+            mCollectedNotedAppOps = collectedNotedAppOps;
+        }
+    }
+
+    /**
+     * Temporarily suspend collection of noted app-ops when binder-thread calls into the other
+     * process. During such a call there might be call-backs coming back on the same thread which
+     * should not be accounted to the current collection.
+     *
+     * @return a state needed to resume the collection
+     *
+     * @hide
+     */
+    public static @Nullable PausedNotedAppOpsCollection pauseNotedAppOpsCollection() {
+        Integer previousUid = sBinderThreadCallingUid.get();
+        if (previousUid != null) {
+            long[] previousCollectedNotedAppOps = sAppOpsNotedInThisBinderTransaction.get();
+
+            sBinderThreadCallingUid.remove();
+            sAppOpsNotedInThisBinderTransaction.remove();
+
+            return new PausedNotedAppOpsCollection(previousUid, previousCollectedNotedAppOps);
+        }
+
+        return null;
+    }
+
+    /**
+     * Resume a collection paused via {@link #pauseNotedAppOpsCollection}.
+     *
+     * @param prevCollection The state of the previous collection
+     *
+     * @hide
+     */
+    public static void resumeNotedAppOpsCollection(
+            @Nullable PausedNotedAppOpsCollection prevCollection) {
+        if (prevCollection != null) {
+            sBinderThreadCallingUid.set(prevCollection.mUid);
+
+            if (prevCollection.mCollectedNotedAppOps != null) {
+                sAppOpsNotedInThisBinderTransaction.set(prevCollection.mCollectedNotedAppOps);
+            }
+        }
+    }
+
+    /**
+     * Finish collection of noted appops on this thread.
+     *
+     * <p>Called at the end of a two way binder transaction.
+     *
+     * @see #startNotedAppOpsCollection(int)
+     *
+     * @hide
+     */
+    public static void finishNotedAppOpsCollection() {
+        sBinderThreadCallingUid.remove();
+        sAppOpsNotedInThisBinderTransaction.remove();
+    }
+
+    /**
+     * Mark an app-op as noted
+     */
+    private void markAppOpNoted(int uid, @NonNull String packageName, int code,
+            @Nullable String message) {
+        // check it the appops needs to be collected and cache result
+        if (sAppOpsToNote[code] == SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED) {
+            boolean shouldCollectNotes;
+            try {
+                shouldCollectNotes = mService.shouldCollectNotes(code);
+            } catch (RemoteException e) {
+                return;
+            }
+
+            if (shouldCollectNotes) {
+                sAppOpsToNote[code] = SHOULD_COLLECT_NOTE_OP;
+            } else {
+                sAppOpsToNote[code] = SHOULD_NOT_COLLECT_NOTE_OP;
+            }
+        }
+
+        if (sAppOpsToNote[code] != SHOULD_COLLECT_NOTE_OP) {
+            return;
+        }
+
+        Integer binderUid = sBinderThreadCallingUid.get();
+
+        synchronized (sLock) {
+            if (sNotedAppOpsCollector != null && uid == Process.myUid() && packageName.equals(
+                    ActivityThread.currentOpPackageName())) {
+                // This app is noting an app-op for itself. Deliver immediately.
+                sNotedAppOpsCollector.onSelfNoted(new SyncNotedAppOp(code));
+            } else if (binderUid != null && binderUid == uid) {
+                // We are inside of a two-way binder call. Delivered to caller via
+                // {@link #prefixParcelWithAppOpsIfNeeded}
+                long[] appOpsNotedInThisBinderTransaction;
+
+                appOpsNotedInThisBinderTransaction = sAppOpsNotedInThisBinderTransaction.get();
+                if (appOpsNotedInThisBinderTransaction == null) {
+                    appOpsNotedInThisBinderTransaction = new long[2];
+                    sAppOpsNotedInThisBinderTransaction.set(appOpsNotedInThisBinderTransaction);
+                }
+
+                if (code < 64) {
+                    appOpsNotedInThisBinderTransaction[0] |= 1L << code;
+                } else {
+                    appOpsNotedInThisBinderTransaction[1] |= 1L << (code - 64);
+                }
+            } else {
+                // We cannot deliver the note synchronous. Hence send it to the system server to
+                // notify the noted process.
+                if (message == null) {
+                    // Default message is a stack trace
+                    message = getFormattedStackTrace();
+                }
+
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mService.noteAsyncOp(mContext.getOpPackageName(), uid, packageName, code,
+                            message);
+                } catch (RemoteException e) {
+                    e.rethrowFromSystemServer();
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
+            }
+        }
+    }
+
+    /**
+     * Append app-ops noted in the current two-way binder transaction to parcel.
+     *
+     * <p>This is called on the callee side of a two way binder transaction just before the
+     * transaction returns.
+     *
+     * @param p the parcel to append the noted app-ops to
+     *
+     * @hide
+     */
+    public static void prefixParcelWithAppOpsIfNeeded(@NonNull Parcel p) {
+        long[] notedAppOps = sAppOpsNotedInThisBinderTransaction.get();
+        if (notedAppOps == null || (notedAppOps[0] == 0 && notedAppOps[1] == 0)) {
+            return;
+        }
+
+        p.writeInt(Parcel.EX_HAS_NOTED_APPOPS_REPLY_HEADER);
+        p.writeLong(notedAppOps[0]);
+        p.writeLong(notedAppOps[1]);
+    }
+
+    /**
+     * Read app-ops noted during a two-way binder transaction from parcel.
+     *
+     * <p>This is called on the calling side of a two way binder transaction just after the
+     * transaction returns.
+     *
+     * <p>Note: Make sure to keep frameworks/native/libs/binder/Status.cpp::readAndLogNotedAppops
+     * in sync.
+     *
+     * @param p The parcel to read from
+     *
+     * @hide
+     */
+    public static void readAndLogNotedAppops(@NonNull Parcel p) {
+        long[] rawNotedAppOps = new long[2];
+        rawNotedAppOps[0] = p.readLong();
+        rawNotedAppOps[1] = p.readLong();
+
+        if (rawNotedAppOps[0] != 0 || rawNotedAppOps[1] != 0) {
+            BitSet notedAppOps = BitSet.valueOf(rawNotedAppOps);
+
+            synchronized (sLock) {
+                for (int code = notedAppOps.nextSetBit(0); code != -1;
+                        code = notedAppOps.nextSetBit(code + 1)) {
+                    if (sNotedAppOpsCollector != null) {
+                        sNotedAppOpsCollector.onNoted(new SyncNotedAppOp(code));
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Register a new {@link AppOpsCollector}.
+     *
+     * <p>There can only ever be one collector per process. If there currently is a collector
+     * registered, it will be unregistered.
+     *
+     * <p><b>Only appops related to dangerous permissions are collected.</b>
+     *
+     * @param collector The collector to set or {@code null} to unregister.
+     */
+    public void setNotedAppOpsCollector(@Nullable AppOpsCollector collector) {
+        synchronized (sLock) {
+            if (sNotedAppOpsCollector != null) {
+                try {
+                    mService.stopWatchingAsyncNoted(mContext.getPackageName(),
+                            sNotedAppOpsCollector.mAsyncCb);
+                } catch (RemoteException e) {
+                    e.rethrowFromSystemServer();
+                }
+            }
+
+            sNotedAppOpsCollector = collector;
+
+            if (sNotedAppOpsCollector != null) {
+                List<AsyncNotedAppOp> missedAsyncOps = null;
+                try {
+                    mService.startWatchingAsyncNoted(mContext.getPackageName(),
+                            sNotedAppOpsCollector.mAsyncCb);
+                    missedAsyncOps = mService.extractAsyncOps(mContext.getPackageName());
+                } catch (RemoteException e) {
+                    e.rethrowFromSystemServer();
+                }
+
+                if (missedAsyncOps != null) {
+                    int numMissedAsyncOps = missedAsyncOps.size();
+                    for (int i = 0; i < numMissedAsyncOps; i++) {
+                        final AsyncNotedAppOp asyncNotedAppOp = missedAsyncOps.get(i);
+                        if (sNotedAppOpsCollector != null) {
+                            sNotedAppOpsCollector.getAsyncNotedExecutor().execute(
+                                    () -> sNotedAppOpsCollector.onAsyncNoted(
+                                            asyncNotedAppOp));
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * @return {@code true} iff the process currently is currently collecting noted appops.
+     *
+     * @see #setNotedAppOpsCollector(AppOpsCollector)
+     *
+     * @hide
+     */
+    public static boolean isCollectingNotedAppOps() {
+        synchronized (sLock) {
+            return sNotedAppOpsCollector != null;
+        }
+    }
+
+    /**
+     * Callback an app can choose to {@link #setNotedAppOpsCollector register} to monitor it's noted
+     * appops.
+     *
+     * <p><b>Only appops related to dangerous permissions are collected.</b>
+     */
+    public abstract static class AppOpsCollector {
+        /** Callback registered with the system. This will receive the async notes ops */
+        private final IAppOpsAsyncNotedCallback mAsyncCb = new IAppOpsAsyncNotedCallback.Stub() {
+            @Override
+            public void opNoted(AsyncNotedAppOp op) {
+                Preconditions.checkNotNull(op);
+
+                getAsyncNotedExecutor().execute(() -> onAsyncNoted(op));
+            }
+        };
+
+        /**
+         * @return The executor for the system to use when calling {@link #onAsyncNoted}.
+         */
+        public @NonNull Executor getAsyncNotedExecutor() {
+            return new HandlerExecutor(Handler.getMain());
+        }
+
+        /**
+         * Called when an app-op was noted for this package inside of a two-way binder-call.
+         *
+         * <p>Called on the calling thread just after executing the binder-call. This allows
+         * the app to e.g. collect stack traces to figure out where the access came from.
+         *
+         * @param op The op noted
+         */
+        public abstract void onNoted(@NonNull SyncNotedAppOp op);
+
+        /**
+         * Called when this app noted an app-op for its own package.
+         *
+         * <p>Called on the thread the noted the op. This allows the app to e.g. collect stack
+         * traces to figure out where the access came from.
+         *
+         * @param op The op noted
+         */
+        public abstract void onSelfNoted(@NonNull SyncNotedAppOp op);
+
+        /**
+         * Called when an app-op was noted for this package which cannot be delivered via the other
+         * two mechanisms.
+         *
+         * <p>Called as soon as possible after the app-op was noted, but the delivery delay is not
+         * guaranteed. Due to how async calls work in Android this might even be delivered slightly
+         * before the private data is delivered to the app.
+         *
+         * <p>If the app is not running or no {@link AppOpsCollector} is registered a small amount
+         * of noted app-ops are buffered and then delivered as soon as a collector is registered.
+         *
+         * @param asyncOp The op noted
+         */
+        public abstract void onAsyncNoted(@NonNull AsyncNotedAppOp asyncOp);
+    }
+
+    /**
+     * Generate a stack trace used for noted app-ops logging.
+     *
+     * <p>This strips away the first few and last few stack trace elements as they are not
+     * interesting to apps.
+     */
+    private static String getFormattedStackTrace() {
+        StackTraceElement[] trace = new Exception().getStackTrace();
+
+        int firstInteresting = 0;
+        for (int i = 0; i < trace.length; i++) {
+            if (trace[i].getClassName().startsWith(AppOpsManager.class.getName())
+                    || trace[i].getClassName().startsWith(Parcel.class.getName())
+                    || trace[i].getClassName().contains("$Stub$Proxy")
+                    || trace[i].getClassName().startsWith(DatabaseUtils.class.getName())
+                    || trace[i].getClassName().startsWith("android.content.ContentProviderProxy")
+                    || trace[i].getClassName().startsWith(ContentResolver.class.getName())) {
+                firstInteresting = i;
+            } else {
+                break;
+            }
+        }
+
+        int lastInteresting = trace.length - 1;
+        for (int i = trace.length - 1; i >= 0; i--) {
+            if (trace[i].getClassName().startsWith(HandlerThread.class.getName())
+                    || trace[i].getClassName().startsWith(Handler.class.getName())
+                    || trace[i].getClassName().startsWith(Looper.class.getName())
+                    || trace[i].getClassName().startsWith(Binder.class.getName())
+                    || trace[i].getClassName().startsWith(RuntimeInit.class.getName())
+                    || trace[i].getClassName().startsWith(ZygoteInit.class.getName())
+                    || trace[i].getClassName().startsWith(ActivityThread.class.getName())
+                    || trace[i].getClassName().startsWith(Method.class.getName())
+                    || trace[i].getClassName().startsWith("com.android.server.SystemServer")) {
+                lastInteresting = i;
+            } else {
+                break;
+            }
+        }
+
+        StringBuilder sb = new StringBuilder();
+        for (int i = firstInteresting; i <= lastInteresting; i++) {
+            sb.append(trace[i]);
+            if (i != lastInteresting) {
+                sb.append('\n');
+            }
+        }
+
+        return sb.toString();
+    }
+
+    /**
      * Checks whether the given op for a UID and package is active.
      *
      * <p> If you don't hold the {@link android.Manifest.permission#WATCH_APPOPS} permission
@@ -5504,7 +6115,7 @@
      * @see #startWatchingActive(int[], OnOpActiveChangedListener)
      * @see #stopWatchingMode(OnOpChangedListener)
      * @see #finishOp(int)
-     * @see #startOp(int)
+     * @see #startOp(int, int, String, boolean, String)
      *
      * @hide */
     @TestApi
diff --git a/core/java/android/app/AsyncNotedAppOp.aidl b/core/java/android/app/AsyncNotedAppOp.aidl
new file mode 100644
index 0000000..ebfefd0
--- /dev/null
+++ b/core/java/android/app/AsyncNotedAppOp.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+parcelable AsyncNotedAppOp;
diff --git a/core/java/android/app/AsyncNotedAppOp.java b/core/java/android/app/AsyncNotedAppOp.java
new file mode 100644
index 0000000..64f886a
--- /dev/null
+++ b/core/java/android/app/AsyncNotedAppOp.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcelable;
+
+import com.android.internal.annotations.Immutable;
+import com.android.internal.util.DataClass;
+
+/**
+ * When an {@link AppOpsManager#noteOp(String, int, String, String) app-op is noted} and the
+ * app the app-op is noted for has a {@link AppOpsManager.AppOpsCollector} registered the note-event
+ * needs to be delivered to the collector. Usually this is done via an {@link SyncNotedAppOp}, but
+ * in some cases this is not possible. In this case an {@link AsyncNotedAppOp} is send to the system
+ * server and then forwarded to the {@link AppOpsManager.AppOpsCollector} in the app.
+ */
+@Immutable
+@DataClass(genEqualsHashCode = true,
+        genAidl = true,
+        genHiddenConstructor = true)
+// - We don't expose the opCode, but rather the public name of the op, hence use a non-standard
+//   getter
+@DataClass.Suppress({"getOpCode"})
+public final class AsyncNotedAppOp implements Parcelable {
+    /** Op that was noted */
+    private final @IntRange(from = 0, to = AppOpsManager._NUM_OP - 1) int mOpCode;
+
+    /** Uid that noted the op */
+    private final @IntRange(from = 0) int mNotingUid;
+
+    /**
+     * Package that noted the op. {@code null} if the package name that noted the op could be not
+     * be determined (e.g. when the op is noted from native code).
+     */
+    private final @Nullable String mNotingPackageName;
+
+    /** Message associated with the noteOp. This message is set by the app noting the op */
+    private final @NonNull String mMessage;
+
+    /** Milliseconds since epoch when the op was noted */
+    private final @IntRange(from = 0) long mTime;
+
+    /**
+     * @return Op that was noted.
+     */
+    public @NonNull String getOp() {
+        return AppOpsManager.opToPublicName(mOpCode);
+    }
+
+
+
+    // Code below generated by codegen v1.0.0.
+    //
+    // DO NOT MODIFY!
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/app/AsyncNotedAppOp.java
+    //
+    // CHECKSTYLE:OFF Generated code
+
+    /**
+     * Creates a new AsyncNotedAppOp.
+     *
+     * @param opCode
+     *   Op that was noted
+     * @param notingUid
+     *   Uid that noted the op
+     * @param notingPackageName
+     *   Package that noted the op
+     * @param message
+     *   Message associated with the noteOp. This message is set by the app noting the op
+     * @param time
+     *   Milliseconds since epoch when the op was noted
+     * @hide
+     */
+    @DataClass.Generated.Member
+    public AsyncNotedAppOp(
+            @IntRange(from = 0, to = AppOpsManager._NUM_OP - 1) int opCode,
+            @IntRange(from = 0) int notingUid,
+            @Nullable String notingPackageName,
+            @NonNull String message,
+            @IntRange(from = 0) long time) {
+        this.mOpCode = opCode;
+        com.android.internal.util.AnnotationValidations.validate(
+                IntRange.class, null, mOpCode,
+                "from", 0,
+                "to", AppOpsManager._NUM_OP - 1);
+        this.mNotingUid = notingUid;
+        com.android.internal.util.AnnotationValidations.validate(
+                IntRange.class, null, mNotingUid,
+                "from", 0);
+        this.mNotingPackageName = notingPackageName;
+        this.mMessage = message;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mMessage);
+        this.mTime = time;
+        com.android.internal.util.AnnotationValidations.validate(
+                IntRange.class, null, mTime,
+                "from", 0);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * Uid that noted the op
+     */
+    @DataClass.Generated.Member
+    public @IntRange(from = 0) int getNotingUid() {
+        return mNotingUid;
+    }
+
+    /**
+     * Package that noted the op
+     */
+    @DataClass.Generated.Member
+    public @Nullable String getNotingPackageName() {
+        return mNotingPackageName;
+    }
+
+    /**
+     * Message associated with the noteOp. This message is set by the app noting the op
+     */
+    @DataClass.Generated.Member
+    public @NonNull String getMessage() {
+        return mMessage;
+    }
+
+    /**
+     * Milliseconds since epoch when the op was noted
+     */
+    @DataClass.Generated.Member
+    public @IntRange(from = 0) long getTime() {
+        return mTime;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(AsyncNotedAppOp other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        AsyncNotedAppOp that = (AsyncNotedAppOp) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && mOpCode == that.mOpCode
+                && mNotingUid == that.mNotingUid
+                && java.util.Objects.equals(mNotingPackageName, that.mNotingPackageName)
+                && java.util.Objects.equals(mMessage, that.mMessage)
+                && mTime == that.mTime;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + mOpCode;
+        _hash = 31 * _hash + mNotingUid;
+        _hash = 31 * _hash + java.util.Objects.hashCode(mNotingPackageName);
+        _hash = 31 * _hash + java.util.Objects.hashCode(mMessage);
+        _hash = 31 * _hash + Long.hashCode(mTime);
+        return _hash;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(android.os.Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        byte flg = 0;
+        if (mNotingPackageName != null) flg |= 0x4;
+        dest.writeByte(flg);
+        dest.writeInt(mOpCode);
+        dest.writeInt(mNotingUid);
+        if (mNotingPackageName != null) dest.writeString(mNotingPackageName);
+        dest.writeString(mMessage);
+        dest.writeLong(mTime);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<AsyncNotedAppOp> CREATOR
+            = new Parcelable.Creator<AsyncNotedAppOp>() {
+        @Override
+        public AsyncNotedAppOp[] newArray(int size) {
+            return new AsyncNotedAppOp[size];
+        }
+
+        @Override
+        @SuppressWarnings({"unchecked", "RedundantCast"})
+        public AsyncNotedAppOp createFromParcel(android.os.Parcel in) {
+            // You can override field unparcelling by defining methods like:
+            // static FieldType unparcelFieldName(Parcel in) { ... }
+
+            byte flg = in.readByte();
+            int opCode = in.readInt();
+            int notingUid = in.readInt();
+            String notingPackageName = (flg & 0x4) == 0 ? null : in.readString();
+            String message = in.readString();
+            long time = in.readLong();
+            return new AsyncNotedAppOp(
+                    opCode,
+                    notingUid,
+                    notingPackageName,
+                    message,
+                    time);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1566503083973L,
+            codegenVersion = "1.0.0",
+            sourceFile = "frameworks/base/core/java/android/app/AsyncNotedAppOp.java",
+            inputSignatures = "private final @android.annotation.IntRange(from=0L, to=90L) int mOpCode\nprivate final @android.annotation.IntRange(from=0L) int mNotingUid\nprivate final @android.annotation.Nullable java.lang.String mNotingPackageName\nprivate final @android.annotation.NonNull java.lang.String mMessage\nprivate final @android.annotation.IntRange(from=0L) long mTime\npublic @android.annotation.NonNull java.lang.String getOp()\nclass AsyncNotedAppOp extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genAidl=true, genHiddenConstructor=true)")
+    @Deprecated
+    private void __metadata() {}
+
+}
diff --git a/core/java/android/app/SyncNotedAppOp.java b/core/java/android/app/SyncNotedAppOp.java
new file mode 100644
index 0000000..f7b83d4
--- /dev/null
+++ b/core/java/android/app/SyncNotedAppOp.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+
+import com.android.internal.annotations.Immutable;
+
+/**
+ * Description of an app-op that was noted for the current process.
+ *
+ * <p>This is either delivered after a
+ * {@link AppOpsManager.AppOpsCollector#onNoted(SyncNotedAppOp) two way binder call} or
+ * when the app
+ * {@link AppOpsManager.AppOpsCollector#onSelfNoted(SyncNotedAppOp) notes an app-op for
+ * itself}.
+ */
+@Immutable
+public final class SyncNotedAppOp {
+    private final int mOpCode;
+
+    /**
+     * @return The op that was noted.
+     */
+    public @NonNull String getOp() {
+        return AppOpsManager.opToPublicName(mOpCode);
+    }
+
+    /**
+     * Create a new sync op description
+     *
+     * @param opCode The op that was noted
+     *
+     * @hide
+     */
+    public SyncNotedAppOp(@IntRange(from = 0, to = AppOpsManager._NUM_OP - 1) int opCode) {
+        mOpCode = opCode;
+    }
+
+    @Override
+    public boolean equals(Object other) {
+        if (!(other instanceof SyncNotedAppOp)) {
+            return false;
+        }
+
+        return mOpCode == ((SyncNotedAppOp) other).mOpCode;
+    }
+
+    @Override
+    public int hashCode() {
+        return mOpCode;
+    }
+}
diff --git a/core/java/android/content/PermissionChecker.java b/core/java/android/content/PermissionChecker.java
index 6fe6e99..e24512a 100644
--- a/core/java/android/content/PermissionChecker.java
+++ b/core/java/android/content/PermissionChecker.java
@@ -16,6 +16,8 @@
 
 package android.content;
 
+import static android.app.AppOpsManager.strOpToOp;
+
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -75,6 +77,16 @@
     }
 
     /**
+     * @deprecated Use {@link #checkPermission(Context, String, int, int, String, String)} instead
+     */
+    @Deprecated
+    @PermissionResult
+    public static int checkPermission(@NonNull Context context, @NonNull String permission,
+            int pid, int uid, @Nullable String packageName) {
+        return checkPermission(context, permission, pid, uid, packageName, null);
+    }
+
+    /**
      * Checks whether a given package in a UID and PID has a given permission
      * and whether the app op that corresponds to this permission is allowed.
      *
@@ -84,12 +96,14 @@
      * @param uid The uid for which to check.
      * @param packageName The package name for which to check. If null the
      *     the first package for the calling UID will be used.
+     * @param message A message describing the reason the permission was checked
+     *
      * @return The permission check result which is either {@link #PERMISSION_GRANTED}
      *     or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
      */
     @PermissionResult
     public static int checkPermission(@NonNull Context context, @NonNull String permission,
-            int pid, int uid, @Nullable String packageName) {
+            int pid, int uid, @Nullable String packageName, @Nullable String message) {
         if (context.checkPermission(permission, pid, uid) == PackageManager.PERMISSION_DENIED) {
             return PERMISSION_DENIED;
         }
@@ -108,7 +122,8 @@
             packageName = packageNames[0];
         }
 
-        if (appOpsManager.noteProxyOpNoThrow(op, packageName, uid) != AppOpsManager.MODE_ALLOWED) {
+        if (appOpsManager.noteProxyOpNoThrow(strOpToOp(op), packageName, uid, message)
+                != AppOpsManager.MODE_ALLOWED) {
             return PERMISSION_DENIED_APP_OP;
         }
 
@@ -131,7 +146,17 @@
     public static int checkSelfPermission(@NonNull Context context,
             @NonNull String permission) {
         return checkPermission(context, permission, Process.myPid(),
-                Process.myUid(), context.getPackageName());
+                Process.myUid(), context.getPackageName(), null /* self access */);
+    }
+
+    /**
+     * @deprecated Use {@link #checkCallingPermission(Context, String, String, String)} instead
+     */
+    @Deprecated
+    @PermissionResult
+    public static int checkCallingPermission(@NonNull Context context,
+            @NonNull String permission, @Nullable String packageName) {
+        return checkCallingPermission(context, permission, packageName, null);
     }
 
     /**
@@ -142,17 +167,29 @@
      * @param permission The permission to check.
      * @param packageName The package name making the IPC. If null the
      *     the first package for the calling UID will be used.
+     * @param message A message describing the reason the permission was checked
+     *
      * @return The permission check result which is either {@link #PERMISSION_GRANTED}
      *     or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
      */
     @PermissionResult
     public static int checkCallingPermission(@NonNull Context context,
-            @NonNull String permission, @Nullable String packageName) {
+            @NonNull String permission, @Nullable String packageName, @Nullable String message) {
         if (Binder.getCallingPid() == Process.myPid()) {
             return PERMISSION_DENIED;
         }
         return checkPermission(context, permission, Binder.getCallingPid(),
-                Binder.getCallingUid(), packageName);
+                Binder.getCallingUid(), packageName, message);
+    }
+
+    /**
+     * @deprecated Use {@link #checkCallingOrSelfPermission(Context, String, String)} instead
+     */
+    @Deprecated
+    @PermissionResult
+    public static int checkCallingOrSelfPermission(@NonNull Context context,
+            @NonNull String permission) {
+        return checkCallingOrSelfPermission(context, permission, null);
     }
 
     /**
@@ -161,15 +198,17 @@
      *
      * @param context Context for accessing resources.
      * @param permission The permission to check.
+     * @param message A message describing the reason the permission was checked
+     *
      * @return The permission check result which is either {@link #PERMISSION_GRANTED}
      *     or {@link #PERMISSION_DENIED} or {@link #PERMISSION_DENIED_APP_OP}.
      */
     @PermissionResult
     public static int checkCallingOrSelfPermission(@NonNull Context context,
-            @NonNull String permission) {
+            @NonNull String permission, @Nullable String message) {
         String packageName = (Binder.getCallingPid() == Process.myPid())
                 ? context.getPackageName() : null;
         return checkPermission(context, permission, Binder.getCallingPid(),
-                Binder.getCallingUid(), packageName);
+                Binder.getCallingUid(), packageName, message);
     }
 }
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index 5533721..2c9333b 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.UnsupportedAppUsage;
+import android.app.AppOpsManager;
 import android.util.ExceptionUtils;
 import android.util.Log;
 import android.util.Slog;
@@ -1029,7 +1030,17 @@
                 Trace.traceBegin(Trace.TRACE_TAG_ALWAYS, getClass().getName() + ":"
                         + (transactionName != null ? transactionName : code));
             }
-            res = onTransact(code, data, reply, flags);
+
+            if ((flags & FLAG_COLLECT_NOTED_APP_OPS) != 0) {
+                AppOpsManager.startNotedAppOpsCollection(callingUid);
+                try {
+                    res = onTransact(code, data, reply, flags);
+                } finally {
+                    AppOpsManager.finishNotedAppOpsCollection();
+                }
+            } else {
+                res = onTransact(code, data, reply, flags);
+            }
         } catch (RemoteException|RuntimeException e) {
             if (observer != null) {
                 observer.callThrewException(callSession, e);
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index c74cef8..b3b4f78 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -18,6 +18,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.AppOpsManager;
 import android.util.Log;
 import android.util.SparseIntArray;
 
@@ -506,9 +507,18 @@
             }
         }
 
+        final AppOpsManager.PausedNotedAppOpsCollection prevCollection =
+                AppOpsManager.pauseNotedAppOpsCollection();
+
+        if ((flags & FLAG_ONEWAY) == 0 && AppOpsManager.isCollectingNotedAppOps()) {
+            flags |= FLAG_COLLECT_NOTED_APP_OPS;
+        }
+
         try {
             return transactNative(code, data, reply, flags);
         } finally {
+            AppOpsManager.resumeNotedAppOpsCollection(prevCollection);
+
             if (transactListener != null) {
                 transactListener.onTransactEnded(session);
             }
diff --git a/core/java/android/os/IBinder.java b/core/java/android/os/IBinder.java
index 83f88ad..12bce8a 100644
--- a/core/java/android/os/IBinder.java
+++ b/core/java/android/os/IBinder.java
@@ -170,6 +170,11 @@
     int FLAG_ONEWAY             = 0x00000001;
 
     /**
+     * @hide
+     */
+    int FLAG_COLLECT_NOTED_APP_OPS = 0x00000002;
+
+    /**
      * Limit that should be placed on IPC sizes to keep them safely under the
      * transaction buffer limit.
      * @hide
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index e1b5542..50487e9 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
+import android.app.AppOpsManager;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -267,7 +268,9 @@
     private static final int EX_UNSUPPORTED_OPERATION = -7;
     private static final int EX_SERVICE_SPECIFIC = -8;
     private static final int EX_PARCELABLE = -9;
-    private static final int EX_HAS_REPLY_HEADER = -128;  // special; see below
+    /** @hide */
+    public static final int EX_HAS_NOTED_APPOPS_REPLY_HEADER = -127; // special; see below
+    private static final int EX_HAS_STRICTMODE_REPLY_HEADER = -128;  // special; see below
     // EX_TRANSACTION_FAILED is used exclusively in native code.
     // see libbinder's binder/Status.h
     private static final int EX_TRANSACTION_FAILED = -129;
@@ -1866,6 +1869,8 @@
      * @see #readException
      */
     public final void writeException(@NonNull Exception e) {
+        AppOpsManager.prefixParcelWithAppOpsIfNeeded(this);
+
         int code = 0;
         if (e instanceof Parcelable
                 && (e.getClass().getClassLoader() == Parcelable.class.getClassLoader())) {
@@ -1944,6 +1949,8 @@
      * @see #readException
      */
     public final void writeNoException() {
+        AppOpsManager.prefixParcelWithAppOpsIfNeeded(this);
+
         // Despite the name of this function ("write no exception"),
         // it should instead be thought of as "write the RPC response
         // header", but because this function name is written out by
@@ -1951,14 +1958,14 @@
         //
         // The response header, in the non-exception case (see also
         // writeException above, also called by the AIDL compiler), is
-        // either a 0 (the default case), or EX_HAS_REPLY_HEADER if
+        // either a 0 (the default case), or EX_HAS_STRICTMODE_REPLY_HEADER if
         // StrictMode has gathered up violations that have occurred
         // during a Binder call, in which case we write out the number
         // of violations and their details, serialized, before the
         // actual RPC respons data.  The receiving end of this is
         // readException(), below.
         if (StrictMode.hasGatheredViolations()) {
-            writeInt(EX_HAS_REPLY_HEADER);
+            writeInt(EX_HAS_STRICTMODE_REPLY_HEADER);
             final int sizePosition = dataPosition();
             writeInt(0);  // total size of fat header, to be filled in later
             StrictMode.writeGatheredViolationsToParcel(this);
@@ -2005,7 +2012,13 @@
     @TestApi
     public final int readExceptionCode() {
         int code = readInt();
-        if (code == EX_HAS_REPLY_HEADER) {
+        if (code == EX_HAS_NOTED_APPOPS_REPLY_HEADER) {
+            AppOpsManager.readAndLogNotedAppops(this);
+            // Read next header or real exception if there is no more header
+            code = readInt();
+        }
+
+        if (code == EX_HAS_STRICTMODE_REPLY_HEADER) {
             int headerSize = readInt();
             if (headerSize == 0) {
                 Log.e(TAG, "Unexpected zero-sized Parcel reply header.");
diff --git a/core/java/com/android/internal/app/IAppOpsAsyncNotedCallback.aidl b/core/java/com/android/internal/app/IAppOpsAsyncNotedCallback.aidl
new file mode 100644
index 0000000..163e4f7
--- /dev/null
+++ b/core/java/com/android/internal/app/IAppOpsAsyncNotedCallback.aidl
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import android.app.AsyncNotedAppOp;
+
+// Interface to observe async noted appops
+oneway interface IAppOpsAsyncNotedCallback {
+    void opNoted(in AsyncNotedAppOp op);
+}
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index 72dbbf3..fcf09a9 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -17,12 +17,13 @@
 package com.android.internal.app;
 
 import android.app.AppOpsManager;
-import android.app.AppOpsManager;
+import android.app.AsyncNotedAppOp;
 import android.content.pm.ParceledListSlice;
 import android.os.Bundle;
 import android.os.RemoteCallback;
 import com.android.internal.app.IAppOpsCallback;
 import com.android.internal.app.IAppOpsActiveCallback;
+import com.android.internal.app.IAppOpsAsyncNotedCallback;
 import com.android.internal.app.IAppOpsNotedCallback;
 
 interface IAppOpsService {
@@ -40,6 +41,9 @@
     IBinder getToken(IBinder clientToken);
     int permissionToOpCode(String permission);
     int checkAudioOperation(int code, int usage, int uid, String packageName);
+    void noteAsyncOp(String callingPackageName, int uid, String packageName, int opCode,
+            String message);
+    boolean shouldCollectNotes(int opCode);
     // End of methods also called by native code.
     // Any new method exposed to native must be added after the last one, do not reorder
 
@@ -82,6 +86,10 @@
     void startWatchingNoted(in int[] ops, IAppOpsNotedCallback callback);
     void stopWatchingNoted(IAppOpsNotedCallback callback);
 
+    void startWatchingAsyncNoted(String packageName, IAppOpsAsyncNotedCallback callback);
+    void stopWatchingAsyncNoted(String packageName, IAppOpsAsyncNotedCallback callback);
+    List<AsyncNotedAppOp> extractAsyncOps(String packageName);
+
     int checkOperationRaw(int code, int uid, String packageName);
 
     void reloadNonHistoricalState();
diff --git a/core/java/com/android/internal/util/AnnotationValidations.java b/core/java/com/android/internal/util/AnnotationValidations.java
index c8afdd4..2d3b450 100644
--- a/core/java/com/android/internal/util/AnnotationValidations.java
+++ b/core/java/com/android/internal/util/AnnotationValidations.java
@@ -61,16 +61,52 @@
     }
 
     public static void validate(Class<IntRange> annotation, IntRange ignored, int value,
-            String paramName1, int param1, String paramName2, int param2) {
+            String paramName1, long param1, String paramName2, long param2) {
         validate(annotation, ignored, value, paramName1, param1);
         validate(annotation, ignored, value, paramName2, param2);
     }
 
     public static void validate(Class<IntRange> annotation, IntRange ignored, int value,
-            String paramName, int param) {
+            String paramName, long param) {
         switch (paramName) {
-            case "from": if (value < param) invalid(annotation, value, paramName, param); break;
-            case "to": if (value > param) invalid(annotation, value, paramName, param); break;
+            case "from":
+                if (value < param) {
+                    invalid(annotation, value, paramName, param);
+                }
+                break;
+            case "to":
+                if (value > param) {
+                    invalid(annotation, value, paramName, param);
+                }
+                break;
+        }
+    }
+
+    /**
+     * Validate a long value with two parameters.
+     */
+    public static void validate(Class<IntRange> annotation, IntRange ignored, long value,
+            String paramName1, long param1, String paramName2, long param2) {
+        validate(annotation, ignored, value, paramName1, param1);
+        validate(annotation, ignored, value, paramName2, param2);
+    }
+
+    /**
+     * Validate a long value with one parameter.
+     */
+    public static void validate(Class<IntRange> annotation, IntRange ignored, long value,
+            String paramName, long param) {
+        switch (paramName) {
+            case "from":
+                if (value < param) {
+                    invalid(annotation, value, paramName, param);
+                }
+                break;
+            case "to":
+                if (value > param) {
+                    invalid(annotation, value, paramName, param);
+                }
+                break;
         }
     }