Merge "APIs to watch active op changes"
diff --git a/Android.bp b/Android.bp
index e65ba0f..ea1ed91 100644
--- a/Android.bp
+++ b/Android.bp
@@ -356,6 +356,7 @@
         "core/java/android/speech/IRecognitionService.aidl",
         "core/java/android/speech/tts/ITextToSpeechCallback.aidl",
         "core/java/android/speech/tts/ITextToSpeechService.aidl",
+        "core/java/com/android/internal/app/IAppOpsActiveCallback.aidl",
         "core/java/com/android/internal/app/IAppOpsCallback.aidl",
         "core/java/com/android/internal/app/IAppOpsService.aidl",
         "core/java/com/android/internal/app/IBatteryStats.aidl",
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 1026550..c5b3a4a 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -17,6 +17,7 @@
 package android.app;
 
 import android.Manifest;
+import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
@@ -34,8 +35,10 @@
 import android.os.UserManager;
 import android.util.ArrayMap;
 
+import com.android.internal.app.IAppOpsActiveCallback;
 import com.android.internal.app.IAppOpsCallback;
 import com.android.internal.app.IAppOpsService;
+import com.android.internal.util.Preconditions;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -74,8 +77,9 @@
 
     final Context mContext;
     final IAppOpsService mService;
-    final ArrayMap<OnOpChangedListener, IAppOpsCallback> mModeWatchers
-            = new ArrayMap<OnOpChangedListener, IAppOpsCallback>();
+    final ArrayMap<OnOpChangedListener, IAppOpsCallback> mModeWatchers = new ArrayMap<>();
+    final ArrayMap<OnOpActiveChangedListener, IAppOpsActiveCallback> mActiveWatchers =
+            new ArrayMap<>();
 
     static IBinder sToken;
 
@@ -1532,6 +1536,23 @@
     }
 
     /**
+     * Callback for notification of changes to operation active state.
+     *
+     * @hide
+     */
+    public interface OnOpActiveChangedListener {
+        /**
+         * Called when the active state of an app op changes.
+         *
+         * @param code The op code.
+         * @param uid The UID performing the operation.
+         * @param packageName The package performing the operation.
+         * @param active Whether the operation became active or inactive.
+         */
+        void onOpActiveChanged(int code, int uid, String packageName, boolean active);
+    }
+
+    /**
      * Callback for notification of changes to operation state.
      * This allows you to see the raw op codes instead of strings.
      * @hide
@@ -1695,6 +1716,8 @@
 
     /**
      * Monitor for changes to the operating mode for the given op in the given app package.
+     * You can watch op changes only for your UID.
+     *
      * @param op The operation to monitor, one of OPSTR_*.
      * @param packageName The name of the application to monitor.
      * @param callback Where to report changes.
@@ -1706,11 +1729,17 @@
 
     /**
      * Monitor for changes to the operating mode for the given op in the given app package.
+     *
+     * <p> If you don't hold the {@link android.Manifest.permission#WATCH_APPOPS} permission
+     * to watch changes only for your UID.
+     *
      * @param op The operation to monitor, one of OP_*.
      * @param packageName The name of the application to monitor.
      * @param callback Where to report changes.
      * @hide
      */
+    // TODO: Uncomment below annotation once b/73559440 is fixed
+    // @RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true)
     public void startWatchingMode(int op, String packageName, final OnOpChangedListener callback) {
         synchronized (mModeWatchers) {
             IAppOpsCallback cb = mModeWatchers.get(callback);
@@ -1752,6 +1781,74 @@
         }
     }
 
+    /**
+     * Start watching for changes to the active state of app ops. An app op may be
+     * long running and it has a clear start and stop delimiters. If an op is being
+     * started or stopped by any package you will get a callback. To change the
+     * watched ops for a registered callback you need to unregister and register it
+     * again.
+     *
+     * @param ops The ops to watch.
+     * @param callback Where to report changes.
+     *
+     * @see #isOperationActive(int, int, String)
+     * @see #stopWatchingActive(OnOpActiveChangedListener)
+     * @see #startOp(int, int, String)
+     * @see #finishOp(int, int, String)
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.WATCH_APPOPS)
+    public void startWatchingActive(@NonNull int[] ops,
+            @NonNull OnOpActiveChangedListener callback) {
+        Preconditions.checkNotNull(ops, "ops cannot be null");
+        Preconditions.checkNotNull(callback, "callback cannot be null");
+        IAppOpsActiveCallback cb;
+        synchronized (mActiveWatchers) {
+            cb = mActiveWatchers.get(callback);
+            if (cb != null) {
+                return;
+            }
+            cb = new IAppOpsActiveCallback.Stub() {
+                @Override
+                public void opActiveChanged(int op, int uid, String packageName, boolean active) {
+                    callback.onOpActiveChanged(op, uid, packageName, active);
+                }
+            };
+            mActiveWatchers.put(callback, cb);
+        }
+        try {
+            mService.startWatchingActive(ops, cb);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Stop watching for changes to the active state of an app op. An app op may be
+     * long running and it has a clear start and stop delimiters. Unregistering a
+     * non-registered callback has no effect.
+     *
+     * @see #isOperationActive#(int, int, String)
+     * @see #startWatchingActive(int[], OnOpActiveChangedListener)
+     * @see #startOp(int, int, String)
+     * @see #finishOp(int, int, String)
+     *
+     * @hide
+     */
+    public void stopWatchingActive(@NonNull OnOpActiveChangedListener callback) {
+        synchronized (mActiveWatchers) {
+            final IAppOpsActiveCallback cb = mActiveWatchers.get(callback);
+            if (cb != null) {
+                try {
+                    mService.stopWatchingActive(cb);
+                } catch (RemoteException e) {
+                    throw e.rethrowFromSystemServer();
+                }
+            }
+        }
+    }
+
     private String buildSecurityExceptionMsg(int op, int uid, String packageName) {
         return packageName + " from uid " + uid + " not allowed to perform " + sOpNames[op];
     }
@@ -2145,6 +2242,7 @@
     }
 
     /** @hide */
+    @RequiresPermission(Manifest.permission.WATCH_APPOPS)
     public boolean isOperationActive(int code, int uid, String packageName) {
         try {
             return mService.isOperationActive(code, uid, packageName);
diff --git a/core/java/com/android/internal/app/IAppOpsActiveCallback.aidl b/core/java/com/android/internal/app/IAppOpsActiveCallback.aidl
new file mode 100644
index 0000000..510af77
--- /dev/null
+++ b/core/java/com/android/internal/app/IAppOpsActiveCallback.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2018 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;
+
+// Iterface to observe op active changes
+oneway interface IAppOpsActiveCallback {
+    void opActiveChanged(int op, int uid, String packageName, boolean active);
+}
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index 2b975fe..fabda4a 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -19,6 +19,7 @@
 import android.app.AppOpsManager;
 import android.os.Bundle;
 import com.android.internal.app.IAppOpsCallback;
+import com.android.internal.app.IAppOpsActiveCallback;
 
 interface IAppOpsService {
     // These first methods are also called by native code, so must
@@ -49,5 +50,7 @@
     void setUserRestriction(int code, boolean restricted, IBinder token, int userHandle, in String[] exceptionPackages);
     void removeUser(int userHandle);
 
+    void startWatchingActive(in int[] ops, IAppOpsActiveCallback callback);
+    void stopWatchingActive(IAppOpsActiveCallback callback);
     boolean isOperationActive(int code, int uid, String packageName);
 }
diff --git a/core/java/com/android/internal/util/function/HexConsumer.java b/core/java/com/android/internal/util/function/HexConsumer.java
new file mode 100644
index 0000000..ef6aee2
--- /dev/null
+++ b/core/java/com/android/internal/util/function/HexConsumer.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 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.util.function;
+
+import java.util.function.Consumer;
+
+/**
+ * A 6-argument {@link Consumer}
+ *
+ * @hide
+ */
+public interface HexConsumer<A, B, C, D, E, F> {
+    void accept(A a, B b, C c, D d, E e, F f);
+}
diff --git a/core/java/com/android/internal/util/function/HexFunction.java b/core/java/com/android/internal/util/function/HexFunction.java
new file mode 100644
index 0000000..6268daf
--- /dev/null
+++ b/core/java/com/android/internal/util/function/HexFunction.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 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.util.function;
+
+import java.util.function.Function;
+
+/**
+ * A 6-argument {@link Function}
+ *
+ * @hide
+ */
+public interface HexFunction<A, B, C, D, E, F, R> {
+    R apply(A a, B b, C c, D d, E e, F f);
+}
diff --git a/core/java/com/android/internal/util/function/HexPredicate.java b/core/java/com/android/internal/util/function/HexPredicate.java
new file mode 100644
index 0000000..c6ebf6a
--- /dev/null
+++ b/core/java/com/android/internal/util/function/HexPredicate.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 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.util.function;
+
+import java.util.function.Predicate;
+
+/**
+ * A 6-argument {@link Predicate}
+ *
+ * @hide
+ */
+public interface HexPredicate<A, B, C, D, E, F> {
+    boolean test(A a, B b, C c, D d, E e, F f);
+}
diff --git a/core/java/com/android/internal/util/function/QuintConsumer.java b/core/java/com/android/internal/util/function/QuintConsumer.java
new file mode 100644
index 0000000..ebbc5ad
--- /dev/null
+++ b/core/java/com/android/internal/util/function/QuintConsumer.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 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.util.function;
+
+import java.util.function.Consumer;
+
+/**
+ * A 5-argument {@link Consumer}
+ *
+ * @hide
+ */
+public interface QuintConsumer<A, B, C, D, E> {
+    void accept(A a, B b, C c, D d, E e);
+}
diff --git a/core/java/com/android/internal/util/function/QuintFunction.java b/core/java/com/android/internal/util/function/QuintFunction.java
new file mode 100644
index 0000000..1b58f1f
--- /dev/null
+++ b/core/java/com/android/internal/util/function/QuintFunction.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 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.util.function;
+
+import java.util.function.Function;
+
+/**
+ * A 5-argument {@link Function}
+ *
+ * @hide
+ */
+public interface QuintFunction<A, B, C, D, E, R> {
+    R apply(A a, B b, C c, D d, E e);
+}
diff --git a/core/java/com/android/internal/util/function/QuintPredicate.java b/core/java/com/android/internal/util/function/QuintPredicate.java
new file mode 100644
index 0000000..5e1f11d
--- /dev/null
+++ b/core/java/com/android/internal/util/function/QuintPredicate.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 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.util.function;
+
+import java.util.function.Predicate;
+
+/**
+ * A 5-argument {@link Predicate}
+ *
+ * @hide
+ */
+public interface QuintPredicate<A, B, C, D, E> {
+    boolean test(A a, B b, C c, D d, E e);
+}
diff --git a/core/java/com/android/internal/util/function/pooled/OmniFunction.java b/core/java/com/android/internal/util/function/pooled/OmniFunction.java
index c0f506e..9378869 100755
--- a/core/java/com/android/internal/util/function/pooled/OmniFunction.java
+++ b/core/java/com/android/internal/util/function/pooled/OmniFunction.java
@@ -18,8 +18,12 @@
 
 import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
 import com.android.internal.util.FunctionalUtils.ThrowingSupplier;
+import com.android.internal.util.function.HexConsumer;
+import com.android.internal.util.function.HexFunction;
 import com.android.internal.util.function.QuadConsumer;
 import com.android.internal.util.function.QuadFunction;
+import com.android.internal.util.function.QuintConsumer;
+import com.android.internal.util.function.QuintFunction;
 import com.android.internal.util.function.TriConsumer;
 import com.android.internal.util.function.TriFunction;
 
@@ -33,58 +37,59 @@
  *
  * @hide
  */
-abstract class OmniFunction<A, B, C, D, R> implements
+abstract class OmniFunction<A, B, C, D, E, F, R> implements
         PooledFunction<A, R>, BiFunction<A, B, R>, TriFunction<A, B, C, R>,
-        QuadFunction<A, B, C, D, R>,
-        PooledConsumer<A>, BiConsumer<A, B>, TriConsumer<A, B, C>, QuadConsumer<A, B, C, D>,
-        PooledPredicate<A>, BiPredicate<A, B>,
-        PooledSupplier<R>, PooledRunnable,
-        ThrowingRunnable, ThrowingSupplier<R>,
+        QuadFunction<A, B, C, D, R>, QuintFunction<A, B, C, D, E, R>,
+        HexFunction<A, B, C, D, E, F, R>, PooledConsumer<A>, BiConsumer<A, B>,
+        TriConsumer<A, B, C>, QuadConsumer<A, B, C, D>, QuintConsumer<A, B, C, D, E>,
+        HexConsumer<A, B, C, D, E, F>, PooledPredicate<A>, BiPredicate<A, B>,
+        PooledSupplier<R>, PooledRunnable, ThrowingRunnable, ThrowingSupplier<R>,
         PooledSupplier.OfInt, PooledSupplier.OfLong, PooledSupplier.OfDouble {
 
-    abstract R invoke(A a, B b, C c, D d);
+    abstract R invoke(A a, B b, C c, D d, E e, F f);
 
     @Override
     public R apply(A o, B o2) {
-        return invoke(o, o2, null, null);
+        return invoke(o, o2, null, null, null, null);
     }
 
     @Override
     public R apply(A o) {
-        return invoke(o, null, null, null);
+        return invoke(o, null, null, null, null, null);
     }
 
-    abstract public <V> OmniFunction<A, B, C, D, V> andThen(Function<? super R, ? extends V> after);
-    abstract public OmniFunction<A, B, C, D, R> negate();
+    abstract public <V> OmniFunction<A, B, C, D, E, F, V> andThen(
+            Function<? super R, ? extends V> after);
+    abstract public OmniFunction<A, B, C, D, E, F, R> negate();
 
     @Override
     public void accept(A o, B o2) {
-        invoke(o, o2, null, null);
+        invoke(o, o2, null, null, null, null);
     }
 
     @Override
     public void accept(A o) {
-        invoke(o, null, null, null);
+        invoke(o, null, null, null, null, null);
     }
 
     @Override
     public void run() {
-        invoke(null, null, null, null);
+        invoke(null, null, null, null, null, null);
     }
 
     @Override
     public R get() {
-        return invoke(null, null, null, null);
+        return invoke(null, null, null, null, null, null);
     }
 
     @Override
     public boolean test(A o, B o2) {
-        return (Boolean) invoke(o, o2, null, null);
+        return (Boolean) invoke(o, o2, null, null, null, null);
     }
 
     @Override
     public boolean test(A o) {
-        return (Boolean) invoke(o, null, null, null);
+        return (Boolean) invoke(o, null, null, null, null, null);
     }
 
     @Override
@@ -99,22 +104,42 @@
 
     @Override
     public R apply(A a, B b, C c) {
-        return invoke(a, b, c, null);
+        return invoke(a, b, c, null, null, null);
     }
 
     @Override
     public void accept(A a, B b, C c) {
-        invoke(a, b, c, null);
+        invoke(a, b, c, null, null, null);
     }
 
     @Override
     public R apply(A a, B b, C c, D d) {
-        return invoke(a, b, c, d);
+        return invoke(a, b, c, d, null, null);
+    }
+
+    @Override
+    public R apply(A a, B b, C c, D d, E e) {
+        return invoke(a, b, c, d, e, null);
+    }
+
+    @Override
+    public R apply(A a, B b, C c, D d, E e, F f) {
+        return invoke(a, b, c, d, e, f);
     }
 
     @Override
     public void accept(A a, B b, C c, D d) {
-        invoke(a, b, c, d);
+        invoke(a, b, c, d, null, null);
+    }
+
+    @Override
+    public void accept(A a, B b, C c, D d, E e) {
+        invoke(a, b, c, d, e, null);
+    }
+
+    @Override
+    public void accept(A a, B b, C c, D d, E e, F f) {
+        invoke(a, b, c, d, e, f);
     }
 
     @Override
@@ -128,5 +153,5 @@
     }
 
     @Override
-    abstract public OmniFunction<A, B, C, D, R> recycleOnUse();
+    abstract public OmniFunction<A, B, C, D, E, F, R> recycleOnUse();
 }
diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambda.java b/core/java/com/android/internal/util/function/pooled/PooledLambda.java
index 87c25e9..15698cc 100755
--- a/core/java/com/android/internal/util/function/pooled/PooledLambda.java
+++ b/core/java/com/android/internal/util/function/pooled/PooledLambda.java
@@ -21,8 +21,12 @@
 
 import android.os.Message;
 
+import com.android.internal.util.function.HexConsumer;
+import com.android.internal.util.function.HexFunction;
 import com.android.internal.util.function.QuadConsumer;
 import com.android.internal.util.function.QuadFunction;
+import com.android.internal.util.function.QuintConsumer;
+import com.android.internal.util.function.QuintFunction;
 import com.android.internal.util.function.TriConsumer;
 import com.android.internal.util.function.TriFunction;
 import com.android.internal.util.function.pooled.PooledLambdaImpl.LambdaType.ReturnType;
@@ -170,7 +174,7 @@
             Consumer<? super A> function,
             A arg1) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 1, 0, ReturnType.VOID, arg1, null, null, null);
+                function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null);
     }
 
     /**
@@ -186,7 +190,7 @@
             Predicate<? super A> function,
             A arg1) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 1, 0, ReturnType.BOOLEAN, arg1, null, null, null);
+                function, 1, 0, ReturnType.BOOLEAN, arg1, null, null, null, null, null);
     }
 
     /**
@@ -202,7 +206,7 @@
             Function<? super A, ? extends R> function,
             A arg1) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 1, 0, ReturnType.OBJECT, arg1, null, null, null);
+                function, 1, 0, ReturnType.OBJECT, arg1, null, null, null, null, null);
     }
 
     /**
@@ -232,7 +236,7 @@
             A arg1) {
         synchronized (Message.sPoolSync) {
             PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
-                    function, 1, 0, ReturnType.VOID, arg1, null, null, null);
+                    function, 1, 0, ReturnType.VOID, arg1, null, null, null, null, null);
             return Message.obtain().setCallback(callback.recycleOnUse());
         }
     }
@@ -251,7 +255,7 @@
             BiConsumer<? super A, ? super B> function,
             A arg1, B arg2) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 2, 0, ReturnType.VOID, arg1, arg2, null, null);
+                function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null);
     }
 
     /**
@@ -268,7 +272,7 @@
             BiPredicate<? super A, ? super B> function,
             A arg1, B arg2) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 2, 0, ReturnType.BOOLEAN, arg1, arg2, null, null);
+                function, 2, 0, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null);
     }
 
     /**
@@ -285,7 +289,7 @@
             BiFunction<? super A, ? super B, ? extends R> function,
             A arg1, B arg2) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 2, 0, ReturnType.OBJECT, arg1, arg2, null, null);
+                function, 2, 0, ReturnType.OBJECT, arg1, arg2, null, null, null, null);
     }
 
     /**
@@ -302,7 +306,7 @@
             BiConsumer<? super A, ? super B> function,
             ArgumentPlaceholder<A> arg1, B arg2) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 2, 1, ReturnType.VOID, arg1, arg2, null, null);
+                function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null);
     }
 
     /**
@@ -319,7 +323,7 @@
             BiPredicate<? super A, ? super B> function,
             ArgumentPlaceholder<A> arg1, B arg2) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null);
+                function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null);
     }
 
     /**
@@ -336,7 +340,7 @@
             BiFunction<? super A, ? super B, ? extends R> function,
             ArgumentPlaceholder<A> arg1, B arg2) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null);
+                function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null);
     }
 
     /**
@@ -353,7 +357,7 @@
             BiConsumer<? super A, ? super B> function,
             A arg1, ArgumentPlaceholder<B> arg2) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 2, 1, ReturnType.VOID, arg1, arg2, null, null);
+                function, 2, 1, ReturnType.VOID, arg1, arg2, null, null, null, null);
     }
 
     /**
@@ -370,7 +374,7 @@
             BiPredicate<? super A, ? super B> function,
             A arg1, ArgumentPlaceholder<B> arg2) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null);
+                function, 2, 1, ReturnType.BOOLEAN, arg1, arg2, null, null, null, null);
     }
 
     /**
@@ -387,7 +391,7 @@
             BiFunction<? super A, ? super B, ? extends R> function,
             A arg1, ArgumentPlaceholder<B> arg2) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null);
+                function, 2, 1, ReturnType.OBJECT, arg1, arg2, null, null, null, null);
     }
 
     /**
@@ -418,7 +422,7 @@
             A arg1, B arg2) {
         synchronized (Message.sPoolSync) {
             PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
-                    function, 2, 0, ReturnType.VOID, arg1, arg2, null, null);
+                    function, 2, 0, ReturnType.VOID, arg1, arg2, null, null, null, null);
             return Message.obtain().setCallback(callback.recycleOnUse());
         }
     }
@@ -438,7 +442,7 @@
             TriConsumer<? super A, ? super B, ? super C> function,
             A arg1, B arg2, C arg3) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null);
+                function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null);
     }
 
     /**
@@ -456,7 +460,7 @@
             TriFunction<? super A, ? super B, ? super C, ? extends R> function,
             A arg1, B arg2, C arg3) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 3, 0, ReturnType.OBJECT, arg1, arg2, arg3, null);
+                function, 3, 0, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null);
     }
 
     /**
@@ -474,7 +478,7 @@
             TriConsumer<? super A, ? super B, ? super C> function,
             ArgumentPlaceholder<A> arg1, B arg2, C arg3) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null);
+                function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null);
     }
 
     /**
@@ -492,7 +496,7 @@
             TriFunction<? super A, ? super B, ? super C, ? extends R> function,
             ArgumentPlaceholder<A> arg1, B arg2, C arg3) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null);
+                function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null);
     }
 
     /**
@@ -510,7 +514,7 @@
             TriConsumer<? super A, ? super B, ? super C> function,
             A arg1, ArgumentPlaceholder<B> arg2, C arg3) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null);
+                function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null);
     }
 
     /**
@@ -528,7 +532,7 @@
             TriFunction<? super A, ? super B, ? super C, ? extends R> function,
             A arg1, ArgumentPlaceholder<B> arg2, C arg3) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null);
+                function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null);
     }
 
     /**
@@ -546,7 +550,7 @@
             TriConsumer<? super A, ? super B, ? super C> function,
             A arg1, B arg2, ArgumentPlaceholder<C> arg3) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null);
+                function, 3, 1, ReturnType.VOID, arg1, arg2, arg3, null, null, null);
     }
 
     /**
@@ -564,7 +568,7 @@
             TriFunction<? super A, ? super B, ? super C, ? extends R> function,
             A arg1, B arg2, ArgumentPlaceholder<C> arg3) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null);
+                function, 3, 1, ReturnType.OBJECT, arg1, arg2, arg3, null, null, null);
     }
 
     /**
@@ -596,7 +600,7 @@
             A arg1, B arg2, C arg3) {
         synchronized (Message.sPoolSync) {
             PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
-                    function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null);
+                    function, 3, 0, ReturnType.VOID, arg1, arg2, arg3, null, null, null);
             return Message.obtain().setCallback(callback.recycleOnUse());
         }
     }
@@ -617,7 +621,7 @@
             QuadConsumer<? super A, ? super B, ? super C, ? super D> function,
             A arg1, B arg2, C arg3, D arg4) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4);
+                function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null);
     }
 
     /**
@@ -636,7 +640,7 @@
             QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function,
             A arg1, B arg2, C arg3, D arg4) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 4, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4);
+                function, 4, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null);
     }
 
     /**
@@ -655,7 +659,7 @@
             QuadConsumer<? super A, ? super B, ? super C, ? super D> function,
             ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4);
+                function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null);
     }
 
     /**
@@ -674,7 +678,7 @@
             QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function,
             ArgumentPlaceholder<A> arg1, B arg2, C arg3, D arg4) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4);
+                function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null);
     }
 
     /**
@@ -693,7 +697,7 @@
             QuadConsumer<? super A, ? super B, ? super C, ? super D> function,
             A arg1, ArgumentPlaceholder<B> arg2, C arg3, D arg4) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4);
+                function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null);
     }
 
     /**
@@ -712,7 +716,7 @@
             QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function,
             A arg1, ArgumentPlaceholder<B> arg2, C arg3, D arg4) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4);
+                function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null);
     }
 
     /**
@@ -731,7 +735,7 @@
             QuadConsumer<? super A, ? super B, ? super C, ? super D> function,
             A arg1, B arg2, ArgumentPlaceholder<C> arg3, D arg4) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4);
+                function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null);
     }
 
     /**
@@ -750,7 +754,7 @@
             QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function,
             A arg1, B arg2, ArgumentPlaceholder<C> arg3, D arg4) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4);
+                function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null);
     }
 
     /**
@@ -769,7 +773,7 @@
             QuadConsumer<? super A, ? super B, ? super C, ? super D> function,
             A arg1, B arg2, C arg3, ArgumentPlaceholder<D> arg4) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4);
+                function, 4, 1, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null);
     }
 
     /**
@@ -788,7 +792,7 @@
             QuadFunction<? super A, ? super B, ? super C, ? super D, ? extends R> function,
             A arg1, B arg2, C arg3, ArgumentPlaceholder<D> arg4) {
         return acquire(PooledLambdaImpl.sPool,
-                function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4);
+                function, 4, 1, ReturnType.OBJECT, arg1, arg2, arg3, arg4, null, null);
     }
 
     /**
@@ -821,7 +825,164 @@
             A arg1, B arg2, C arg3, D arg4) {
         synchronized (Message.sPoolSync) {
             PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
-                    function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4);
+                    function, 4, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, null, null);
+            return Message.obtain().setCallback(callback.recycleOnUse());
+        }
+    }
+
+    /**
+     * {@link PooledRunnable} factory
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 parameter supplied to {@code function} on call
+     * @param arg2 parameter supplied to {@code function} on call
+     * @param arg3 parameter supplied to {@code function} on call
+     * @param arg4 parameter supplied to {@code function} on call
+     * @param arg5 parameter supplied to {@code function} on call
+     * @return a {@link PooledRunnable}, equivalent to lambda:
+     *         {@code () -> function(arg1, arg2, arg3, arg4, arg5) }
+     */
+    static <A, B, C, D, E> PooledRunnable obtainRunnable(
+            QuintConsumer<? super A, ? super B, ? super C, ? super D, ? super E> function,
+            A arg1, B arg2, C arg3, D arg4, E arg5) {
+        return acquire(PooledLambdaImpl.sPool,
+                function, 5, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, null);
+    }
+
+    /**
+     * {@link PooledSupplier} factory
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 parameter supplied to {@code function} on call
+     * @param arg2 parameter supplied to {@code function} on call
+     * @param arg3 parameter supplied to {@code function} on call
+     * @param arg4 parameter supplied to {@code function} on call
+     * @param arg5 parameter supplied to {@code function} on call
+     * @return a {@link PooledSupplier}, equivalent to lambda:
+     *         {@code () -> function(arg1, arg2, arg3, arg4, arg5) }
+     */
+    static <A, B, C, D, E, R> PooledSupplier<R> obtainSupplier(
+            QuintFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? extends R>
+                    function, A arg1, B arg2, C arg3, D arg4, E arg5) {
+        return acquire(PooledLambdaImpl.sPool,
+                function, 5, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, null);
+    }
+
+    /**
+     * Factory of {@link Message}s that contain an
+     * ({@link PooledLambda#recycleOnUse auto-recycling}) {@link PooledRunnable} as its
+     * {@link Message#getCallback internal callback}.
+     *
+     * The callback is equivalent to one obtainable via
+     * {@link #obtainRunnable(QuintConsumer, Object, Object, Object, Object, Object)}
+     *
+     * Note that using this method with {@link android.os.Handler#handleMessage}
+     * is more efficient than the alternative of {@link android.os.Handler#post}
+     * with a {@link PooledRunnable} due to the lack of 2 separate synchronization points
+     * when obtaining {@link Message} and {@link PooledRunnable} from pools separately
+     *
+     * You may optionally set a {@link Message#what} for the message if you want to be
+     * able to cancel it via {@link android.os.Handler#removeMessages}, but otherwise
+     * there's no need to do so
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 parameter supplied to {@code function} on call
+     * @param arg2 parameter supplied to {@code function} on call
+     * @param arg3 parameter supplied to {@code function} on call
+     * @param arg4 parameter supplied to {@code function} on call
+     * @param arg5 parameter supplied to {@code function} on call
+     * @return a {@link Message} invoking {@code function(arg1, arg2, arg3, arg4, arg5) } when
+     *         handled
+     */
+    static <A, B, C, D, E> Message obtainMessage(
+            QuintConsumer<? super A, ? super B, ? super C, ? super D, ? super E> function,
+            A arg1, B arg2, C arg3, D arg4, E arg5) {
+        synchronized (Message.sPoolSync) {
+            PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
+                    function, 5, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, null);
+            return Message.obtain().setCallback(callback.recycleOnUse());
+        }
+    }
+
+    /**
+     * {@link PooledRunnable} factory
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 parameter supplied to {@code function} on call
+     * @param arg2 parameter supplied to {@code function} on call
+     * @param arg3 parameter supplied to {@code function} on call
+     * @param arg4 parameter supplied to {@code function} on call
+     * @param arg5 parameter supplied to {@code function} on call
+     * @param arg6 parameter supplied to {@code function} on call
+     * @return a {@link PooledRunnable}, equivalent to lambda:
+     *         {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6) }
+     */
+    static <A, B, C, D, E, F> PooledRunnable obtainRunnable(
+            HexConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F> function,
+            A arg1, B arg2, C arg3, D arg4, E arg5, F arg6) {
+        return acquire(PooledLambdaImpl.sPool,
+                function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6);
+    }
+
+    /**
+     * {@link PooledSupplier} factory
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 parameter supplied to {@code function} on call
+     * @param arg2 parameter supplied to {@code function} on call
+     * @param arg3 parameter supplied to {@code function} on call
+     * @param arg4 parameter supplied to {@code function} on call
+     * @param arg5 parameter supplied to {@code function} on call
+     * @param arg6 parameter supplied to {@code function} on call
+     * @return a {@link PooledSupplier}, equivalent to lambda:
+     *         {@code () -> function(arg1, arg2, arg3, arg4, arg5, arg6) }
+     */
+    static <A, B, C, D, E, F, R> PooledSupplier<R> obtainSupplier(
+            HexFunction<? super A, ? super B, ? super C, ? super D, ? super E, ? super F,
+                    ? extends R> function, A arg1, B arg2, C arg3, D arg4, E arg5, F arg6) {
+        return acquire(PooledLambdaImpl.sPool,
+                function, 6, 0, ReturnType.OBJECT, arg1, arg2, arg3, arg4, arg5, arg6);
+    }
+
+    /**
+     * Factory of {@link Message}s that contain an
+     * ({@link PooledLambda#recycleOnUse auto-recycling}) {@link PooledRunnable} as its
+     * {@link Message#getCallback internal callback}.
+     *
+     * The callback is equivalent to one obtainable via
+     * {@link #obtainRunnable(QuintConsumer, Object, Object, Object, Object, Object)}
+     *
+     * Note that using this method with {@link android.os.Handler#handleMessage}
+     * is more efficient than the alternative of {@link android.os.Handler#post}
+     * with a {@link PooledRunnable} due to the lack of 2 separate synchronization points
+     * when obtaining {@link Message} and {@link PooledRunnable} from pools separately
+     *
+     * You may optionally set a {@link Message#what} for the message if you want to be
+     * able to cancel it via {@link android.os.Handler#removeMessages}, but otherwise
+     * there's no need to do so
+     *
+     * @param function non-capturing lambda(typically an unbounded method reference)
+     *                 to be invoked on call
+     * @param arg1 parameter supplied to {@code function} on call
+     * @param arg2 parameter supplied to {@code function} on call
+     * @param arg3 parameter supplied to {@code function} on call
+     * @param arg4 parameter supplied to {@code function} on call
+     * @param arg5 parameter supplied to {@code function} on call
+     * @param arg6 parameter supplied to {@code function} on call
+     * @return a {@link Message} invoking {@code function(arg1, arg2, arg3, arg4, arg5, arg6) }
+     *         when handled
+     */
+    static <A, B, C, D, E, F> Message obtainMessage(
+            HexConsumer<? super A, ? super B, ? super C, ? super D, ? super E, ? super F> function,
+            A arg1, B arg2, C arg3, D arg4, E arg5, F arg6) {
+        synchronized (Message.sPoolSync) {
+            PooledRunnable callback = acquire(PooledLambdaImpl.sMessageCallbacksPool,
+                    function, 6, 0, ReturnType.VOID, arg1, arg2, arg3, arg4, arg5, arg6);
             return Message.obtain().setCallback(callback.recycleOnUse());
         }
     }
diff --git a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
index 03e013c..565ae11 100755
--- a/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
+++ b/core/java/com/android/internal/util/function/pooled/PooledLambdaImpl.java
@@ -24,9 +24,15 @@
 
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.BitUtils;
+import com.android.internal.util.function.HexConsumer;
+import com.android.internal.util.function.HexFunction;
+import com.android.internal.util.function.HexPredicate;
 import com.android.internal.util.function.QuadConsumer;
 import com.android.internal.util.function.QuadFunction;
 import com.android.internal.util.function.QuadPredicate;
+import com.android.internal.util.function.QuintConsumer;
+import com.android.internal.util.function.QuintFunction;
+import com.android.internal.util.function.QuintPredicate;
 import com.android.internal.util.function.TriConsumer;
 import com.android.internal.util.function.TriFunction;
 import com.android.internal.util.function.TriPredicate;
@@ -44,12 +50,13 @@
  * @see PooledLambda
  * @hide
  */
-final class PooledLambdaImpl<R> extends OmniFunction<Object, Object, Object, Object, R> {
+final class PooledLambdaImpl<R> extends OmniFunction<Object,
+        Object, Object, Object, Object, Object, R> {
 
     private static final boolean DEBUG = false;
     private static final String LOG_TAG = "PooledLambdaImpl";
 
-    private static final int MAX_ARGS = 4;
+    private static final int MAX_ARGS = 5;
 
     private static final int MAX_POOL_SIZE = 50;
 
@@ -151,16 +158,17 @@
     }
 
     @Override
-    R invoke(Object a1, Object a2, Object a3, Object a4) {
+    R invoke(Object a1, Object a2, Object a3, Object a4, Object a5, Object a6) {
         checkNotRecycled();
         if (DEBUG) {
             Log.i(LOG_TAG, this + ".invoke("
                     + commaSeparateFirstN(
-                            new Object[] { a1, a2, a3, a4 },
+                            new Object[] { a1, a2, a3, a4, a5, a6 },
                             LambdaType.decodeArgCount(getFlags(MASK_EXPOSED_AS)))
                     + ")");
         }
-        boolean ignored = fillInArg(a1) && fillInArg(a2) && fillInArg(a3) && fillInArg(a4);
+        final boolean notUsed = fillInArg(a1) && fillInArg(a2) && fillInArg(a3)
+                && fillInArg(a4) && fillInArg(a5) && fillInArg(a6);
         int argCount = LambdaType.decodeArgCount(getFlags(MASK_FUNC_TYPE));
         if (argCount != LambdaType.MASK_ARG_COUNT) {
             for (int i = 0; i < argCount; i++) {
@@ -289,6 +297,42 @@
                     }
                 }
             } break;
+
+            case 5: {
+                switch (returnType) {
+                    case LambdaType.ReturnType.VOID: {
+                        ((QuintConsumer) mFunc).accept(popArg(0), popArg(1),
+                                popArg(2), popArg(3), popArg(4));
+                        return null;
+                    }
+                    case LambdaType.ReturnType.BOOLEAN: {
+                        return (R) (Object) ((QuintPredicate) mFunc).test(
+                                popArg(0), popArg(1), popArg(2), popArg(3), popArg(4));
+                    }
+                    case LambdaType.ReturnType.OBJECT: {
+                        return (R) ((QuintFunction) mFunc).apply(
+                                popArg(0), popArg(1), popArg(2), popArg(3),  popArg(4));
+                    }
+                }
+            } break;
+
+            case 6: {
+                switch (returnType) {
+                    case LambdaType.ReturnType.VOID: {
+                        ((HexConsumer) mFunc).accept(popArg(0), popArg(1),
+                                popArg(2), popArg(3), popArg(4), popArg(5));
+                        return null;
+                    }
+                    case LambdaType.ReturnType.BOOLEAN: {
+                        return (R) (Object) ((HexPredicate) mFunc).test(popArg(0),
+                                popArg(1), popArg(2), popArg(3), popArg(4), popArg(5));
+                    }
+                    case LambdaType.ReturnType.OBJECT: {
+                        return (R) ((HexFunction) mFunc).apply(popArg(0), popArg(1),
+                                popArg(2), popArg(3), popArg(4), popArg(5));
+                    }
+                }
+            }
         }
         throw new IllegalStateException("Unknown function type: " + LambdaType.toString(funcType));
     }
@@ -350,14 +394,14 @@
     /**
      * Internal non-typesafe factory method for {@link PooledLambdaImpl}
      */
-    static <E extends PooledLambda> E acquire(Pool pool, Object f,
+    static <E extends PooledLambda> E acquire(Pool pool, Object func,
             int fNumArgs, int numPlaceholders, int fReturnType,
-            Object a, Object b, Object c, Object d) {
+            Object a, Object b, Object c, Object d, Object e, Object f) {
         PooledLambdaImpl r = acquire(pool);
         if (DEBUG) {
             Log.i(LOG_TAG,
                     "acquire(this = @" + hashCodeHex(r)
-                            + ", f = " + f
+                            + ", func = " + func
                             + ", fNumArgs = " + fNumArgs
                             + ", numPlaceholders = " + numPlaceholders
                             + ", fReturnType = " + LambdaType.ReturnType.toString(fReturnType)
@@ -365,9 +409,11 @@
                             + ", b = " + b
                             + ", c = " + c
                             + ", d = " + d
+                            + ", e = " + e
+                            + ", f = " + f
                             + ")");
         }
-        r.mFunc = f;
+        r.mFunc = func;
         r.setFlags(MASK_FUNC_TYPE, LambdaType.encode(fNumArgs, fReturnType));
         r.setFlags(MASK_EXPOSED_AS, LambdaType.encode(numPlaceholders, fReturnType));
         if (ArrayUtils.size(r.mArgs) < fNumArgs) r.mArgs = new Object[fNumArgs];
@@ -375,6 +421,8 @@
         setIfInBounds(r.mArgs, 1, b);
         setIfInBounds(r.mArgs, 2, c);
         setIfInBounds(r.mArgs, 3, d);
+        setIfInBounds(r.mArgs, 4, e);
+        setIfInBounds(r.mArgs, 5, f);
         return (E) r;
     }
 
@@ -400,12 +448,12 @@
     }
 
     @Override
-    public OmniFunction<Object, Object, Object, Object, R> negate() {
+    public OmniFunction<Object, Object, Object, Object, Object, Object, R> negate() {
         throw new UnsupportedOperationException();
     }
 
     @Override
-    public <V> OmniFunction<Object, Object, Object, Object, V> andThen(
+    public <V> OmniFunction<Object, Object, Object, Object, Object, Object, V> andThen(
             Function<? super R, ? extends V> after) {
         throw new UnsupportedOperationException();
     }
@@ -426,7 +474,7 @@
     }
 
     @Override
-    public OmniFunction<Object, Object, Object, Object, R> recycleOnUse() {
+    public OmniFunction<Object, Object, Object, Object, Object, Object, R> recycleOnUse() {
         if (DEBUG) Log.i(LOG_TAG, this + ".recycleOnUse()");
         mFlags |= FLAG_RECYCLE_ON_USE;
         return this;
@@ -507,6 +555,8 @@
                 case 2: return "Bi";
                 case 3: return "Tri";
                 case 4: return "Quad";
+                case 5: return "Quint";
+                case 6: return "Hex";
                 default: throw new IllegalArgumentException("" + argCount);
             }
         }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 3a527b5..caeca592 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3839,6 +3839,11 @@
     <permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS"
         android:protectionLevel="signature|privileged" />
 
+    <!-- Allows an application to watch changes and/or active state of app ops.
+         @hide <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.WATCH_APPOPS"
+        android:protectionLevel="signature" />
+
     <application android:process="system"
                  android:persistent="true"
                  android:hasCode="false"
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 3021555..ed29028 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -162,6 +162,7 @@
     <assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="cameraserver" />
     <assign-permission name="android.permission.GET_PROCESS_STATE_AND_OOM_SCORE" uid="cameraserver" />
     <assign-permission name="android.permission.PACKAGE_USAGE_STATS" uid="cameraserver" />
+    <assign-permission name="android.permission.WATCH_APPOPS" uid="cameraserver" />
 
     <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="graphics" />
 
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 5c8d745..d919d6a 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -143,6 +143,7 @@
         android:name="android.permission.MANAGE_BLUETOOTH_WHEN_PERMISSION_REVIEW_REQUIRED" />
     <uses-permission
         android:name="android.permission.MANAGE_WIFI_WHEN_PERMISSION_REVIEW_REQUIRED" />
+    <uses-permission android:name="android.permission.WATCH_APPOPS" />
 
     <application android:label="@string/app_label"
                  android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 35c4d59..3d49e5c 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -202,6 +202,9 @@
     <!-- to change themes - light or dark -->
     <uses-permission android:name="android.permission.CHANGE_OVERLAY_PACKAGES" />
 
+    <!-- Listen app op changes -->
+    <uses-permission android:name="android.permission.WATCH_APPOPS" />
+
     <application
         android:name=".SystemUIApplication"
         android:persistent="true"
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index 894106a..ca67a34 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -39,14 +39,12 @@
 import android.os.ServiceManager;
 import android.os.ShellCallback;
 import android.os.ShellCommand;
-import android.os.SystemClock;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.os.storage.StorageManagerInternal;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.AtomicFile;
-import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
@@ -54,6 +52,7 @@
 import android.util.Xml;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.IAppOpsActiveCallback;
 import com.android.internal.app.IAppOpsCallback;
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.os.Zygote;
@@ -63,6 +62,7 @@
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.XmlUtils;
 
+import com.android.internal.util.function.pooled.PooledLambda;
 import libcore.util.EmptyArray;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -180,16 +180,19 @@
         }
     }
 
-    final SparseArray<ArraySet<Callback>> mOpModeWatchers = new SparseArray<>();
-    final ArrayMap<String, ArraySet<Callback>> mPackageModeWatchers = new ArrayMap<>();
-    final ArrayMap<IBinder, Callback> mModeWatchers = new ArrayMap<>();
+    final SparseArray<ArraySet<ModeCallback>> mOpModeWatchers = new SparseArray<>();
+    final ArrayMap<String, ArraySet<ModeCallback>> mPackageModeWatchers = new ArrayMap<>();
+    final ArrayMap<IBinder, ModeCallback> mModeWatchers = new ArrayMap<>();
+    final ArrayMap<IBinder, SparseArray<ActiveCallback>> mActiveWatchers = new ArrayMap<>();
     final SparseArray<SparseArray<Restriction>> mAudioRestrictions = new SparseArray<>();
 
-    public final class Callback implements DeathRecipient {
+    public final class ModeCallback implements DeathRecipient {
         final IAppOpsCallback mCallback;
+        final int mUid;
 
-        public Callback(IAppOpsCallback callback) {
+        public ModeCallback(IAppOpsCallback callback, int uid) {
             mCallback = callback;
+            mUid = uid;
             try {
                 mCallback.asBinder().linkToDeath(this, 0);
             } catch (RemoteException e) {
@@ -206,6 +209,27 @@
         }
     }
 
+    public final class ActiveCallback implements DeathRecipient {
+        final IAppOpsActiveCallback mCallback;
+
+        public ActiveCallback(IAppOpsActiveCallback callback) {
+            mCallback = callback;
+            try {
+                mCallback.asBinder().linkToDeath(this, 0);
+            } catch (RemoteException e) {
+            }
+        }
+
+        public void destroy() {
+            mCallback.asBinder().unlinkToDeath(this, 0);
+        }
+
+        @Override
+        public void binderDied() {
+            stopWatchingActive(mCallback);
+        }
+    }
+
     final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<IBinder, ClientState>();
 
     public final class ClientState extends Binder implements DeathRecipient {
@@ -360,21 +384,30 @@
                 return;
             }
 
-            boolean changed = false;
+            Ops ops = null;
 
             // Remove any package state if such.
-            if (uidState.pkgOps != null && uidState.pkgOps.remove(packageName) != null) {
-                changed = true;
+            if (uidState.pkgOps != null) {
+                ops = uidState.pkgOps.remove(packageName);
             }
 
             // If we just nuked the last package state check if the UID is valid.
-            if (changed && uidState.pkgOps.isEmpty()
+            if (ops != null && uidState.pkgOps.isEmpty()
                     && getPackagesForUid(uid).length <= 0) {
                 mUidStates.remove(uid);
             }
 
-            if (changed) {
+            if (ops != null) {
                 scheduleFastWriteLocked();
+
+                final int opCount = ops.size();
+                for (int i = 0; i < opCount; i++) {
+                    final Op op = ops.valueAt(i);
+                    if (op.duration == -1) {
+                        scheduleOpActiveChangedIfNeededLocked(
+                                op.op, op.uid, op.packageName, false);
+                    }
+                }
             }
         }
     }
@@ -598,14 +631,14 @@
         }
 
         String[] uidPackageNames = getPackagesForUid(uid);
-        ArrayMap<Callback, ArraySet<String>> callbackSpecs = null;
+        ArrayMap<ModeCallback, ArraySet<String>> callbackSpecs = null;
 
         synchronized (this) {
-            ArraySet<Callback> callbacks = mOpModeWatchers.get(code);
+            ArraySet<ModeCallback> callbacks = mOpModeWatchers.get(code);
             if (callbacks != null) {
                 final int callbackCount = callbacks.size();
                 for (int i = 0; i < callbackCount; i++) {
-                    Callback callback = callbacks.valueAt(i);
+                    ModeCallback callback = callbacks.valueAt(i);
                     ArraySet<String> changedPackages = new ArraySet<>();
                     Collections.addAll(changedPackages, uidPackageNames);
                     callbackSpecs = new ArrayMap<>();
@@ -621,7 +654,7 @@
                     }
                     final int callbackCount = callbacks.size();
                     for (int i = 0; i < callbackCount; i++) {
-                        Callback callback = callbacks.valueAt(i);
+                        ModeCallback callback = callbacks.valueAt(i);
                         ArraySet<String> changedPackages = callbackSpecs.get(callback);
                         if (changedPackages == null) {
                             changedPackages = new ArraySet<>();
@@ -637,30 +670,23 @@
             return;
         }
 
-        // There are components watching for mode changes such as window manager
-        // and location manager which are in our process. The callbacks in these
-        // components may require permissions our remote caller does not have.
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            for (int i = 0; i < callbackSpecs.size(); i++) {
-                Callback callback = callbackSpecs.keyAt(i);
-                ArraySet<String> reportedPackageNames = callbackSpecs.valueAt(i);
-                try {
-                    if (reportedPackageNames == null) {
-                        callback.mCallback.opChanged(code, uid, null);
-                    } else {
-                        final int reportedPackageCount = reportedPackageNames.size();
-                        for (int j = 0; j < reportedPackageCount; j++) {
-                            String reportedPackageName = reportedPackageNames.valueAt(j);
-                            callback.mCallback.opChanged(code, uid, reportedPackageName);
-                        }
-                    }
-                } catch (RemoteException e) {
-                    Log.w(TAG, "Error dispatching op op change", e);
+        for (int i = 0; i < callbackSpecs.size(); i++) {
+            final ModeCallback callback = callbackSpecs.keyAt(i);
+            final ArraySet<String> reportedPackageNames = callbackSpecs.valueAt(i);
+            if (reportedPackageNames == null) {
+                mHandler.sendMessage(PooledLambda.obtainMessage(
+                        AppOpsService::notifyOpChanged,
+                        this, callback, code, uid, (String) null));
+
+            } else {
+                final int reportedPackageCount = reportedPackageNames.size();
+                for (int j = 0; j < reportedPackageCount; j++) {
+                    final String reportedPackageName = reportedPackageNames.valueAt(j);
+                    mHandler.sendMessage(PooledLambda.obtainMessage(
+                            AppOpsService::notifyOpChanged,
+                            this, callback, code, uid, reportedPackageName));
                 }
             }
-        } finally {
-            Binder.restoreCallingIdentity(identity);
         }
     }
 
@@ -671,7 +697,7 @@
                     Binder.getCallingPid(), Binder.getCallingUid(), null);
         }
         verifyIncomingOp(code);
-        ArrayList<Callback> repCbs = null;
+        ArraySet<ModeCallback> repCbs = null;
         code = AppOpsManager.opToSwitch(code);
         synchronized (this) {
             UidState uidState = getUidStateLocked(uid, false);
@@ -679,17 +705,17 @@
             if (op != null) {
                 if (op.mode != mode) {
                     op.mode = mode;
-                    ArraySet<Callback> cbs = mOpModeWatchers.get(code);
+                    ArraySet<ModeCallback> cbs = mOpModeWatchers.get(code);
                     if (cbs != null) {
                         if (repCbs == null) {
-                            repCbs = new ArrayList<>();
+                            repCbs = new ArraySet<>();
                         }
                         repCbs.addAll(cbs);
                     }
                     cbs = mPackageModeWatchers.get(packageName);
                     if (cbs != null) {
                         if (repCbs == null) {
-                            repCbs = new ArrayList<>();
+                            repCbs = new ArraySet<>();
                         }
                         repCbs.addAll(cbs);
                     }
@@ -703,26 +729,41 @@
             }
         }
         if (repCbs != null) {
-            // There are components watching for mode changes such as window manager
-            // and location manager which are in our process. The callbacks in these
-            // components may require permissions our remote caller does not have.
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                for (int i = 0; i < repCbs.size(); i++) {
-                    try {
-                        repCbs.get(i).mCallback.opChanged(code, uid, packageName);
-                    } catch (RemoteException e) {
-                    }
-                }
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
+            mHandler.sendMessage(PooledLambda.obtainMessage(
+                    AppOpsService::notifyOpChanged,
+                    this, repCbs, code, uid, packageName));
         }
     }
 
-    private static HashMap<Callback, ArrayList<ChangeRec>> addCallbacks(
-            HashMap<Callback, ArrayList<ChangeRec>> callbacks,
-            int op, int uid, String packageName, ArraySet<Callback> cbs) {
+    private void notifyOpChanged(ArraySet<ModeCallback> callbacks, int code,
+            int uid, String packageName) {
+        for (int i = 0; i < callbacks.size(); i++) {
+            final ModeCallback callback = callbacks.valueAt(i);
+            notifyOpChanged(callback, code, uid, packageName);
+        }
+    }
+
+    private void notifyOpChanged(ModeCallback callback, int code,
+            int uid, String packageName) {
+        if (callback.mUid >= 0 && callback.mUid != uid) {
+            return;
+        }
+        // There are components watching for mode changes such as window manager
+        // and location manager which are in our process. The callbacks in these
+        // components may require permissions our remote caller does not have.
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            callback.mCallback.opChanged(code, uid, packageName);
+        } catch (RemoteException e) {
+            /* ignore */
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    private static HashMap<ModeCallback, ArrayList<ChangeRec>> addCallbacks(
+            HashMap<ModeCallback, ArrayList<ChangeRec>> callbacks,
+            int op, int uid, String packageName, ArraySet<ModeCallback> cbs) {
         if (cbs == null) {
             return callbacks;
         }
@@ -732,7 +773,7 @@
         boolean duplicate = false;
         final int N = cbs.size();
         for (int i=0; i<N; i++) {
-            Callback cb = cbs.valueAt(i);
+            ModeCallback cb = cbs.valueAt(i);
             ArrayList<ChangeRec> reports = callbacks.get(cb);
             if (reports == null) {
                 reports = new ArrayList<>();
@@ -785,7 +826,7 @@
             }
         }
 
-        HashMap<Callback, ArrayList<ChangeRec>> callbacks = null;
+        HashMap<ModeCallback, ArrayList<ChangeRec>> callbacks = null;
         synchronized (this) {
             boolean changed = false;
             for (int i = mUidStates.size() - 1; i >= 0; i--) {
@@ -860,15 +901,14 @@
             }
         }
         if (callbacks != null) {
-            for (Map.Entry<Callback, ArrayList<ChangeRec>> ent : callbacks.entrySet()) {
-                Callback cb = ent.getKey();
+            for (Map.Entry<ModeCallback, ArrayList<ChangeRec>> ent : callbacks.entrySet()) {
+                ModeCallback cb = ent.getKey();
                 ArrayList<ChangeRec> reports = ent.getValue();
                 for (int i=0; i<reports.size(); i++) {
                     ChangeRec rep = reports.get(i);
-                    try {
-                        cb.mCallback.opChanged(rep.op, rep.uid, rep.pkg);
-                    } catch (RemoteException e) {
-                    }
+                    mHandler.sendMessage(PooledLambda.obtainMessage(
+                            AppOpsService::notifyOpChanged,
+                            this, cb, rep.op, rep.uid, rep.pkg));
                 }
             }
         }
@@ -876,18 +916,25 @@
 
     @Override
     public void startWatchingMode(int op, String packageName, IAppOpsCallback callback) {
+        int watchedUid = -1;
+        if (mContext.checkCallingOrSelfPermission(Manifest.permission.WATCH_APPOPS)
+                != PackageManager.PERMISSION_GRANTED) {
+            watchedUid = Binder.getCallingUid();
+        }
+        Preconditions.checkArgumentInRange(op, AppOpsManager.OP_NONE,
+                AppOpsManager._NUM_OP - 1, "Invalid op code: " + op);
         if (callback == null) {
             return;
         }
         synchronized (this) {
             op = (op != AppOpsManager.OP_NONE) ? AppOpsManager.opToSwitch(op) : op;
-            Callback cb = mModeWatchers.get(callback.asBinder());
+            ModeCallback cb = mModeWatchers.get(callback.asBinder());
             if (cb == null) {
-                cb = new Callback(callback);
+                cb = new ModeCallback(callback, watchedUid);
                 mModeWatchers.put(callback.asBinder(), cb);
             }
             if (op != AppOpsManager.OP_NONE) {
-                ArraySet<Callback> cbs = mOpModeWatchers.get(op);
+                ArraySet<ModeCallback> cbs = mOpModeWatchers.get(op);
                 if (cbs == null) {
                     cbs = new ArraySet<>();
                     mOpModeWatchers.put(op, cbs);
@@ -895,7 +942,7 @@
                 cbs.add(cb);
             }
             if (packageName != null) {
-                ArraySet<Callback> cbs = mPackageModeWatchers.get(packageName);
+                ArraySet<ModeCallback> cbs = mPackageModeWatchers.get(packageName);
                 if (cbs == null) {
                     cbs = new ArraySet<>();
                     mPackageModeWatchers.put(packageName, cbs);
@@ -911,18 +958,18 @@
             return;
         }
         synchronized (this) {
-            Callback cb = mModeWatchers.remove(callback.asBinder());
+            ModeCallback cb = mModeWatchers.remove(callback.asBinder());
             if (cb != null) {
                 cb.unlinkToDeath();
                 for (int i=mOpModeWatchers.size()-1; i>=0; i--) {
-                    ArraySet<Callback> cbs = mOpModeWatchers.valueAt(i);
+                    ArraySet<ModeCallback> cbs = mOpModeWatchers.valueAt(i);
                     cbs.remove(cb);
                     if (cbs.size() <= 0) {
                         mOpModeWatchers.removeAt(i);
                     }
                 }
                 for (int i=mPackageModeWatchers.size()-1; i>=0; i--) {
-                    ArraySet<Callback> cbs = mPackageModeWatchers.valueAt(i);
+                    ArraySet<ModeCallback> cbs = mPackageModeWatchers.valueAt(i);
                     cbs.remove(cb);
                     if (cbs.size() <= 0) {
                         mPackageModeWatchers.removeAt(i);
@@ -981,7 +1028,7 @@
         }
 
         if (suspended) {
-            Log.i(TAG, "Audio disabled for suspended package=" + packageName + " for uid=" + uid);
+            Slog.i(TAG, "Audio disabled for suspended package=" + packageName + " for uid=" + uid);
             return AppOpsManager.MODE_IGNORED;
         }
 
@@ -1042,7 +1089,9 @@
                 usageRestrictions.put(usage, r);
             }
         }
-        notifyWatchersOfChange(code);
+
+        mHandler.sendMessage(PooledLambda.obtainMessage(
+                AppOpsService::notifyWatchersOfChange, this, code));
     }
 
     @Override
@@ -1098,7 +1147,7 @@
             Ops ops = getOpsRawLocked(uid, packageName, true /* edit */,
                     false /* uidMismatchExpected */);
             if (ops == null) {
-                if (DEBUG) Log.d(TAG, "noteOperation: no op for code " + code + " uid " + uid
+                if (DEBUG) Slog.d(TAG, "noteOperation: no op for code " + code + " uid " + uid
                         + " package " + packageName);
                 return AppOpsManager.MODE_ERRORED;
             }
@@ -1118,7 +1167,7 @@
             if (uidState.opModes != null && uidState.opModes.indexOfKey(switchCode) >= 0) {
                 final int uidMode = uidState.opModes.get(switchCode);
                 if (uidMode != AppOpsManager.MODE_ALLOWED) {
-                    if (DEBUG) Log.d(TAG, "noteOperation: reject #" + op.mode + " for code "
+                    if (DEBUG) Slog.d(TAG, "noteOperation: reject #" + op.mode + " for code "
                             + switchCode + " (" + code + ") uid " + uid + " package "
                             + packageName);
                     op.rejectTime = System.currentTimeMillis();
@@ -1127,14 +1176,14 @@
             } else {
                 final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
                 if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
-                    if (DEBUG) Log.d(TAG, "noteOperation: reject #" + op.mode + " for code "
+                    if (DEBUG) Slog.d(TAG, "noteOperation: reject #" + op.mode + " for code "
                             + switchCode + " (" + code + ") uid " + uid + " package "
                             + packageName);
                     op.rejectTime = System.currentTimeMillis();
                     return switchOp.mode;
                 }
             }
-            if (DEBUG) Log.d(TAG, "noteOperation: allowing code " + code + " uid " + uid
+            if (DEBUG) Slog.d(TAG, "noteOperation: allowing code " + code + " uid " + uid
                     + " package " + packageName);
             op.time = System.currentTimeMillis();
             op.rejectTime = 0;
@@ -1145,6 +1194,51 @@
     }
 
     @Override
+    public void startWatchingActive(int[] ops, IAppOpsActiveCallback callback) {
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.WATCH_APPOPS,
+                "startWatchingActive");
+        if (ops != null) {
+            Preconditions.checkArrayElementsInRange(ops, 0,
+                    AppOpsManager._NUM_OP - 1, "Invalid op code in: " + Arrays.toString(ops));
+        }
+        if (callback == null) {
+            return;
+        }
+        synchronized (this) {
+            SparseArray<ActiveCallback> callbacks = mActiveWatchers.get(callback.asBinder());
+            if (callbacks == null) {
+                callbacks = new SparseArray<>();
+                mActiveWatchers.put(callback.asBinder(), callbacks);
+            }
+            final ActiveCallback activeCallback = new ActiveCallback(callback);
+            for (int op : ops) {
+                callbacks.put(op, activeCallback);
+            }
+        }
+    }
+
+    @Override
+    public void stopWatchingActive(IAppOpsActiveCallback callback) {
+        if (callback == null) {
+            return;
+        }
+        synchronized (this) {
+            final SparseArray<ActiveCallback> activeCallbacks =
+                    mActiveWatchers.remove(callback.asBinder());
+            if (activeCallbacks == null) {
+                return;
+            }
+            final int callbackCount = activeCallbacks.size();
+            for (int i = 0; i < callbackCount; i++) {
+                // Apps ops are mapped to a singleton
+                if (i == 0) {
+                    activeCallbacks.valueAt(i).destroy();
+                }
+            }
+        }
+    }
+
+    @Override
     public int startOperation(IBinder token, int code, int uid, String packageName) {
         verifyIncomingUid(uid);
         verifyIncomingOp(code);
@@ -1157,7 +1251,7 @@
             Ops ops = getOpsRawLocked(uid, resolvedPackageName, true /* edit */,
                     false /* uidMismatchExpected */);
             if (ops == null) {
-                if (DEBUG) Log.d(TAG, "startOperation: no op for code " + code + " uid " + uid
+                if (DEBUG) Slog.d(TAG, "startOperation: no op for code " + code + " uid " + uid
                         + " package " + resolvedPackageName);
                 return AppOpsManager.MODE_ERRORED;
             }
@@ -1167,37 +1261,42 @@
             }
             final int switchCode = AppOpsManager.opToSwitch(code);
             UidState uidState = ops.uidState;
-            if (uidState.opModes != null) {
+            // If there is a non-default per UID policy (we set UID op mode only if
+            // non-default) it takes over, otherwise use the per package policy.
+            if (uidState.opModes != null && uidState.opModes.indexOfKey(switchCode) >= 0) {
                 final int uidMode = uidState.opModes.get(switchCode);
                 if (uidMode != AppOpsManager.MODE_ALLOWED) {
-                    if (DEBUG) Log.d(TAG, "noteOperation: reject #" + op.mode + " for code "
+                    if (DEBUG) Slog.d(TAG, "noteOperation: reject #" + op.mode + " for code "
                             + switchCode + " (" + code + ") uid " + uid + " package "
                             + resolvedPackageName);
                     op.rejectTime = System.currentTimeMillis();
                     return uidMode;
                 }
+            } else {
+                final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
+                if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
+                    if (DEBUG) Slog.d(TAG, "startOperation: reject #" + op.mode + " for code "
+                            + switchCode + " (" + code + ") uid " + uid + " package "
+                            + resolvedPackageName);
+                    op.rejectTime = System.currentTimeMillis();
+                    return switchOp.mode;
+                }
             }
-            final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
-            if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
-                if (DEBUG) Log.d(TAG, "startOperation: reject #" + op.mode + " for code "
-                        + switchCode + " (" + code + ") uid " + uid + " package "
-                        + resolvedPackageName);
-                op.rejectTime = System.currentTimeMillis();
-                return switchOp.mode;
-            }
-            if (DEBUG) Log.d(TAG, "startOperation: allowing code " + code + " uid " + uid
+            if (DEBUG) Slog.d(TAG, "startOperation: allowing code " + code + " uid " + uid
                     + " package " + resolvedPackageName);
             if (op.nesting == 0) {
                 op.time = System.currentTimeMillis();
                 op.rejectTime = 0;
                 op.duration = -1;
+                scheduleOpActiveChangedIfNeededLocked(code, uid, packageName, true);
             }
             op.nesting++;
             if (client.mStartedOps != null) {
                 client.mStartedOps.add(op);
             }
-            return AppOpsManager.MODE_ALLOWED;
         }
+
+        return AppOpsManager.MODE_ALLOWED;
     }
 
     @Override
@@ -1224,6 +1323,52 @@
                 }
             }
             finishOperationLocked(op);
+            if (op.nesting <= 0) {
+                scheduleOpActiveChangedIfNeededLocked(code, uid, packageName, false);
+            }
+        }
+    }
+
+    private void scheduleOpActiveChangedIfNeededLocked(int code, int uid, String packageName,
+            boolean active) {
+        ArraySet<ActiveCallback> dispatchedCallbacks = null;
+        final int callbackListCount = mActiveWatchers.size();
+        for (int i = 0; i < callbackListCount; i++) {
+            final SparseArray<ActiveCallback> callbacks = mActiveWatchers.valueAt(i);
+            ActiveCallback callback = callbacks.get(code);
+            if (callback != null) {
+                if (dispatchedCallbacks == null) {
+                    dispatchedCallbacks = new ArraySet<>();
+                }
+                dispatchedCallbacks.add(callback);
+            }
+        }
+        if (dispatchedCallbacks == null) {
+            return;
+        }
+        mHandler.sendMessage(PooledLambda.obtainMessage(
+                AppOpsService::notifyOpActiveChanged,
+                this, dispatchedCallbacks, code, uid, packageName, active));
+    }
+
+    private void notifyOpActiveChanged(ArraySet<ActiveCallback> callbacks,
+            int code, int uid, String packageName, boolean active) {
+        // There are components watching for mode changes such as window manager
+        // and location manager which are in our process. The callbacks in these
+        // components may require permissions our remote caller does not have.
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            final int callbackCount = callbacks.size();
+            for (int i = 0; i < callbackCount; i++) {
+                final ActiveCallback callback = callbacks.valueAt(i);
+                try {
+                    callback.mCallback.opActiveChanged(code, uid, packageName, active);
+                } catch (RemoteException e) {
+                    /* do nothing */
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(identity);
         }
     }
 
@@ -2220,7 +2365,7 @@
                 for (int i=0; i<mOpModeWatchers.size(); i++) {
                     pw.print("    Op "); pw.print(AppOpsManager.opToName(mOpModeWatchers.keyAt(i)));
                     pw.println(":");
-                    ArraySet<Callback> callbacks = mOpModeWatchers.valueAt(i);
+                    ArraySet<ModeCallback> callbacks = mOpModeWatchers.valueAt(i);
                     for (int j=0; j<callbacks.size(); j++) {
                         pw.print("      #"); pw.print(j); pw.print(": ");
                         pw.println(callbacks.valueAt(j));
@@ -2233,7 +2378,7 @@
                 for (int i=0; i<mPackageModeWatchers.size(); i++) {
                     pw.print("    Pkg "); pw.print(mPackageModeWatchers.keyAt(i));
                     pw.println(":");
-                    ArraySet<Callback> callbacks = mPackageModeWatchers.valueAt(i);
+                    ArraySet<ModeCallback> callbacks = mPackageModeWatchers.valueAt(i);
                     for (int j=0; j<callbacks.size(); j++) {
                         pw.print("      #"); pw.print(j); pw.print(": ");
                         pw.println(callbacks.valueAt(j));
@@ -2242,12 +2387,32 @@
             }
             if (mModeWatchers.size() > 0) {
                 needSep = true;
-                pw.println("  All mode watchers:");
+                pw.println("  All op mode watchers:");
                 for (int i=0; i<mModeWatchers.size(); i++) {
                     pw.print("    "); pw.print(mModeWatchers.keyAt(i));
                     pw.print(" -> "); pw.println(mModeWatchers.valueAt(i));
                 }
             }
+            if (mActiveWatchers.size() > 0) {
+                needSep = true;
+                pw.println("  All op active watchers:");
+                for (int i = 0; i < mActiveWatchers.size(); i++) {
+                    final SparseArray<ActiveCallback> activeWatchers = mActiveWatchers.valueAt(i);
+                    if (activeWatchers.size() <= 0) {
+                        continue;
+                    }
+                    pw.print("    "); pw.print(mActiveWatchers.keyAt(i));
+                    pw.print(" -> [");
+                    final int opCount = activeWatchers.size();
+                    for (i = 0; i < opCount; i++) {
+                        pw.print(AppOpsManager.opToName(activeWatchers.keyAt(i)));
+                        if (i < opCount - 1) {
+                            pw.print(',');
+                        }
+                    }
+                    pw.print("]" ); pw.println(activeWatchers.valueAt(0));
+                }
+            }
             if (mClients.size() > 0) {
                 needSep = true;
                 pw.println("  Clients:");
@@ -2434,8 +2599,6 @@
 
     private void setUserRestrictionNoCheck(int code, boolean restricted, IBinder token,
             int userHandle, String[] exceptionPackages) {
-        boolean notifyChange = false;
-
         synchronized (AppOpsService.this) {
             ClientRestrictionState restrictionState = mOpUserRestrictions.get(token);
 
@@ -2449,7 +2612,8 @@
             }
 
             if (restrictionState.setRestriction(code, restricted, exceptionPackages, userHandle)) {
-                notifyChange = true;
+                mHandler.sendMessage(PooledLambda.obtainMessage(
+                        AppOpsService::notifyWatchersOfChange, this, code));
             }
 
             if (restrictionState.isDefault()) {
@@ -2457,39 +2621,19 @@
                 restrictionState.destroy();
             }
         }
-
-        if (notifyChange) {
-            notifyWatchersOfChange(code);
-        }
     }
 
     private void notifyWatchersOfChange(int code) {
-        final ArraySet<Callback> clonedCallbacks;
+        final ArraySet<ModeCallback> clonedCallbacks;
         synchronized (this) {
-            ArraySet<Callback> callbacks = mOpModeWatchers.get(code);
+            ArraySet<ModeCallback> callbacks = mOpModeWatchers.get(code);
             if (callbacks == null) {
                 return;
             }
             clonedCallbacks = new ArraySet<>(callbacks);
         }
 
-        // There are components watching for mode changes such as window manager
-        // and location manager which are in our process. The callbacks in these
-        // components may require permissions our remote caller does not have.
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            final int callbackCount = clonedCallbacks.size();
-            for (int i = 0; i < callbackCount; i++) {
-                Callback callback = clonedCallbacks.valueAt(i);
-                try {
-                    callback.mCallback.opChanged(code, -1, null);
-                } catch (RemoteException e) {
-                    Log.w(TAG, "Error dispatching op op change", e);
-                }
-            }
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
+        notifyOpChanged(clonedCallbacks,  code, -1, null);
     }
 
     @Override
@@ -2507,13 +2651,14 @@
 
     @Override
     public boolean isOperationActive(int code, int uid, String packageName) {
-        verifyIncomingUid(uid);
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.WATCH_APPOPS,
+                "isOperationActive");
         verifyIncomingOp(code);
-        String resolvedPackageName = resolvePackageName(uid, packageName);
+        final String resolvedPackageName = resolvePackageName(uid, packageName);
         if (resolvedPackageName == null) {
             return false;
         }
-        synchronized (this) {
+        synchronized (AppOpsService.this) {
             for (int i = mClients.size() - 1; i >= 0; i--) {
                 final ClientState client = mClients.valueAt(i);
                 if (client.mStartedOps == null) continue;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 2af284c..183862b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2711,6 +2711,19 @@
             throw new RuntimeException(
                     "Unable to find android system package", e);
         }
+
+        // Start watching app ops after we and the package manager are up and running.
+        mAppOpsService.startWatchingMode(AppOpsManager.OP_RUN_IN_BACKGROUND, null,
+                new IAppOpsCallback.Stub() {
+                    @Override public void opChanged(int op, int uid, String packageName) {
+                        if (op == AppOpsManager.OP_RUN_IN_BACKGROUND && packageName != null) {
+                            if (mAppOpsService.checkOperation(op, uid, packageName)
+                                    != AppOpsManager.MODE_ALLOWED) {
+                                runInBackgroundDisabled(uid);
+                            }
+                        }
+                    }
+                });
     }
 
     public void setWindowManager(WindowManagerService wm) {
@@ -2987,17 +3000,6 @@
         mProcessStats = new ProcessStatsService(this, new File(systemDir, "procstats"));
 
         mAppOpsService = mInjector.getAppOpsService(new File(systemDir, "appops.xml"), mHandler);
-        mAppOpsService.startWatchingMode(AppOpsManager.OP_RUN_IN_BACKGROUND, null,
-                new IAppOpsCallback.Stub() {
-                    @Override public void opChanged(int op, int uid, String packageName) {
-                        if (op == AppOpsManager.OP_RUN_IN_BACKGROUND && packageName != null) {
-                            if (mAppOpsService.checkOperation(op, uid, packageName)
-                                    != AppOpsManager.MODE_ALLOWED) {
-                                runInBackgroundDisabled(uid);
-                            }
-                        }
-                    }
-                });
 
         mGrantFile = new AtomicFile(new File(systemDir, "urigrants.xml"), "uri-grants");
 
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 5d8aca1..568d7a7 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -60,6 +60,7 @@
     <uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
     <uses-permission android:name="android.permission.READ_FRAME_BUFFER" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.WATCH_APPOPS" />
 
     <!-- Uses API introduced in O (26) -->
     <uses-sdk android:minSdkVersion="1"
diff --git a/services/tests/servicestests/src/com/android/server/appops/AppOpsActiveWatcherTest.java b/services/tests/servicestests/src/com/android/server/appops/AppOpsActiveWatcherTest.java
new file mode 100644
index 0000000..ea0fe45
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/appops/AppOpsActiveWatcherTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2018 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.server.appops;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import android.app.AppOpsManager;
+import android.app.AppOpsManager.OnOpActiveChangedListener;
+import android.content.Context;
+import android.os.Process;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests app ops version upgrades
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AppOpsActiveWatcherTest {
+
+    private static final long NOTIFICATION_TIMEOUT_MILLIS = 5000;
+
+    @Test
+    public void testWatchActiveOps() {
+        // Create a mock listener
+        final OnOpActiveChangedListener listener = mock(OnOpActiveChangedListener.class);
+
+        // Start watching active ops
+        final AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class);
+        appOpsManager.startWatchingActive(new int[] {AppOpsManager.OP_CAMERA,
+                AppOpsManager.OP_RECORD_AUDIO}, listener);
+
+        // Start the op
+        appOpsManager.startOp(AppOpsManager.OP_CAMERA);
+
+        // Verify that we got called for the op being active
+        verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS)
+                .times(1)).onOpActiveChanged(eq(AppOpsManager.OP_CAMERA),
+                eq(Process.myUid()), eq(getContext().getPackageName()), eq(true));
+
+        // This should be the only callback we got
+        verifyNoMoreInteractions(listener);
+
+        // Start with a clean slate
+        reset(listener);
+
+        // Verify that the op is active
+        assertThat(appOpsManager.isOperationActive(AppOpsManager.OP_CAMERA,
+                Process.myUid(), getContext().getPackageName())).isTrue();
+
+        // Finish the op
+        appOpsManager.finishOp(AppOpsManager.OP_CAMERA);
+
+        // Verify that we got called for the op being active
+        verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS)
+                .times(1)).onOpActiveChanged(eq(AppOpsManager.OP_CAMERA),
+                eq(Process.myUid()), eq(getContext().getPackageName()), eq(false));
+
+        // Verify that the op is not active
+        assertThat(appOpsManager.isOperationActive(AppOpsManager.OP_CAMERA,
+                Process.myUid(), getContext().getPackageName())).isFalse();
+
+        // This should be the only callback we got
+        verifyNoMoreInteractions(listener);
+
+        // Start with a clean slate
+        reset(listener);
+
+        // Stop watching active ops
+        appOpsManager.stopWatchingActive(listener);
+
+        // Start the op
+        appOpsManager.startOp(AppOpsManager.OP_CAMERA);
+
+        // We should not be getting any callbacks
+        verifyNoMoreInteractions(listener);
+
+        // Finish the op
+        appOpsManager.finishOp(AppOpsManager.OP_CAMERA);
+
+        // We should not be getting any callbacks
+        verifyNoMoreInteractions(listener);
+    }
+
+    private static Context getContext() {
+        return InstrumentationRegistry.getContext();
+    }
+}
\ No newline at end of file