Use start/finish app ops in window manager

Add infrastructure to app ops to specify how to treat mode_default
(for now only for startOp) allowing the caller to decide of this
mode should be treated as success - this is useful if the caller
already performed the default permission checks which determined
that the caller would perform the operation if the mode is default.
This way there is a record in the app ops history that this op
was performed. This is now used by the window manager service
which starts/finishes ops when an alert window is shown/hidden.
The window manager allows adding the window if the mode is default
but the caller has the fallback permission. In this case the
alert window would be shown and we want that noted in the op
history.

Now the window manager properly starts/finishes alert window op
when an alert window is shown/hidden. This is required to allow
SystemUI to badge notifications from apps showing alert windows
or add a dedicated notification if the app has no notifications.

Test: cts-tradefed run cts-dev -m CtsWindowManagerDeviceTestCases

      Added android.server.wm.AppOpAlertWindowAppOpsTest

      cts-tradefed run cts-dev -m CtsPermissionTestCases
          -t android.permission.cts.AppOpsTest

bug:64085448

Change-Id: I9041b1ac287bc5f9ed11d39bb203beba80f3f0f6
diff --git a/api/test-current.txt b/api/test-current.txt
index d5b4311..ca9127c8 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -47,7 +47,10 @@
 
   public class AppOpsManager {
     method public static java.lang.String[] getOpStrs();
+    method public boolean isOperationActive(int, int, java.lang.String);
     method public void setMode(int, int, java.lang.String, int);
+    method public void startWatchingActive(int[], android.app.AppOpsManager.OnOpActiveChangedListener);
+    method public void stopWatchingActive(android.app.AppOpsManager.OnOpActiveChangedListener);
     field public static final java.lang.String OPSTR_ACCEPT_HANDOVER = "android:accept_handover";
     field public static final java.lang.String OPSTR_ACCESS_NOTIFICATIONS = "android:access_notifications";
     field public static final java.lang.String OPSTR_ACTIVATE_VPN = "android:activate_vpn";
@@ -89,6 +92,11 @@
     field public static final java.lang.String OPSTR_WRITE_ICC_SMS = "android:write_icc_sms";
     field public static final java.lang.String OPSTR_WRITE_SMS = "android:write_sms";
     field public static final java.lang.String OPSTR_WRITE_WALLPAPER = "android:write_wallpaper";
+    field public static final int OP_SYSTEM_ALERT_WINDOW = 24; // 0x18
+  }
+
+  public static abstract interface AppOpsManager.OnOpActiveChangedListener {
+    method public abstract void onOpActiveChanged(int, int, java.lang.String, boolean);
   }
 
   public final class NotificationChannelGroup implements android.os.Parcelable {
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index c5b3a4a..fa4a350 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -31,7 +31,6 @@
 import android.os.Parcelable;
 import android.os.Process;
 import android.os.RemoteException;
-import android.os.UserHandle;
 import android.os.UserManager;
 import android.util.ArrayMap;
 
@@ -168,6 +167,7 @@
     /** @hide */
     public static final int OP_WRITE_SETTINGS = 23;
     /** @hide Required to draw on top of other apps. */
+    @TestApi
     public static final int OP_SYSTEM_ALERT_WINDOW = 24;
     /** @hide */
     public static final int OP_ACCESS_NOTIFICATIONS = 25;
@@ -1540,6 +1540,7 @@
      *
      * @hide
      */
+    @TestApi
     public interface OnOpActiveChangedListener {
         /**
          * Called when the active state of an app op changes.
@@ -1731,7 +1732,7 @@
      * 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.
+     * you can watch changes only for your UID.
      *
      * @param op The operation to monitor, one of OP_*.
      * @param packageName The name of the application to monitor.
@@ -1788,6 +1789,9 @@
      * watched ops for a registered callback you need to unregister and register it
      * again.
      *
+     * <p> If you don't hold the {@link android.Manifest.permission#WATCH_APPOPS} permission
+     * you can watch changes only for your UID.
+     *
      * @param ops The ops to watch.
      * @param callback Where to report changes.
      *
@@ -1798,7 +1802,9 @@
      *
      * @hide
      */
-    @RequiresPermission(Manifest.permission.WATCH_APPOPS)
+    @TestApi
+    // TODO: Uncomment below annotation once b/73559440 is fixed
+    // @RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true)
     public void startWatchingActive(@NonNull int[] ops,
             @NonNull OnOpActiveChangedListener callback) {
         Preconditions.checkNotNull(ops, "ops cannot be null");
@@ -1836,6 +1842,7 @@
      *
      * @hide
      */
+    @TestApi
     public void stopWatchingActive(@NonNull OnOpActiveChangedListener callback) {
         synchronized (mActiveWatchers) {
             final IAppOpsActiveCallback cb = mActiveWatchers.get(callback);
@@ -2087,15 +2094,11 @@
      * @hide
      */
     public int noteOp(int op, int uid, String packageName) {
-        try {
-            int mode = mService.noteOperation(op, uid, packageName);
-            if (mode == MODE_ERRORED) {
-                throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
-            }
-            return mode;
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+        final int mode = noteOpNoThrow(op, uid, packageName);
+        if (mode == MODE_ERRORED) {
+            throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
         }
+        return mode;
     }
 
     /**
@@ -2174,6 +2177,11 @@
         }
     }
 
+    /** @hide */
+    public int startOp(int op) {
+        return startOp(op, Process.myUid(), mContext.getOpPackageName());
+    }
+
     /**
      * Report that an application has started executing a long-running operation.  Note that you
      * must pass in both the uid and name of the application to be checked; this function will
@@ -2182,6 +2190,7 @@
      * the current time and the operation will be marked as "running".  In this case you must
      * later call {@link #finishOp(int, int, String)} to report when the application is no
      * longer performing the operation.
+     *
      * @param op The operation to start.  One of the OP_* constants.
      * @param uid The user id of the application attempting to perform the operation.
      * @param packageName The name of the application attempting to perform the operation.
@@ -2192,15 +2201,34 @@
      * @hide
      */
     public int startOp(int op, int uid, String packageName) {
-        try {
-            int mode = mService.startOperation(getToken(mService), op, uid, packageName);
-            if (mode == MODE_ERRORED) {
-                throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
-            }
-            return mode;
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
+        return startOp(op, uid, packageName, false);
+    }
+
+    /**
+     * Report that an application has started executing a long-running operation. Similar
+     * to {@link #startOp(String, int, String) except that if the mode is {@link #MODE_DEFAULT}
+     * the operation should succeed since the caller has performed its standard permission
+     * checks which passed and would perform the protected operation for this mode.
+     *
+     * @param op The operation to start.  One of the OP_* constants.
+     * @param uid The user id of the application attempting to perform the operation.
+     * @param packageName The name of the application attempting to perform the operation.
+     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
+     * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
+     * causing the app to crash).
+     * @param startIfModeDefault Whether to start if mode is {@link #MODE_DEFAULT}.
+     *
+     * @throws SecurityException If the app has been configured to crash on this op or
+     * the package is not in the passed in UID.
+     *
+     * @hide
+     */
+    public int startOp(int op, int uid, String packageName, boolean startIfModeDefault) {
+        final int mode = startOpNoThrow(op, uid, packageName, startIfModeDefault);
+        if (mode == MODE_ERRORED) {
+            throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
         }
+        return mode;
     }
 
     /**
@@ -2209,18 +2237,32 @@
      * @hide
      */
     public int startOpNoThrow(int op, int uid, String packageName) {
+        return startOpNoThrow(op, uid, packageName, false);
+    }
+
+    /**
+     * Like {@link #startOp(int, int, String, boolean)} but instead of throwing a
+     * {@link SecurityException} it returns {@link #MODE_ERRORED}.
+     *
+     * @param op The operation to start.  One of the OP_* constants.
+     * @param uid The user id of the application attempting to perform the operation.
+     * @param packageName The name of the application attempting to perform the operation.
+     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
+     * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
+     * causing the app to crash).
+     * @param startIfModeDefault Whether to start if mode is {@link #MODE_DEFAULT}.
+     *
+     * @hide
+     */
+    public int startOpNoThrow(int op, int uid, String packageName, boolean startIfModeDefault) {
         try {
-            return mService.startOperation(getToken(mService), op, uid, packageName);
+            return mService.startOperation(getToken(mService), op, uid, packageName,
+                    startIfModeDefault);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
-    /** @hide */
-    public int startOp(int op) {
-        return startOp(op, Process.myUid(), mContext.getOpPackageName());
-    }
-
     /**
      * Report that an application is no longer performing an operation that had previously
      * been started with {@link #startOp(int, int, String)}.  There is no validation of input
@@ -2241,8 +2283,21 @@
         finishOp(op, Process.myUid(), mContext.getOpPackageName());
     }
 
-    /** @hide */
-    @RequiresPermission(Manifest.permission.WATCH_APPOPS)
+    /**
+     * Checks whether the given op for a UID and package is active.
+     *
+     * <p> If you don't hold the {@link android.Manifest.permission#WATCH_APPOPS} permission
+     * you can query only for your UID.
+     *
+     * @see #startWatchingActive(int[], OnOpActiveChangedListener)
+     * @see #stopWatchingMode(OnOpChangedListener)
+     * @see #finishOp(int)
+     * @see #startOp(int)
+     *
+     * @hide */
+    @TestApi
+    // TODO: Uncomment below annotation once b/73559440 is fixed
+    // @RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true)
     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/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index fabda4a..2505ea5 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -26,7 +26,8 @@
     // be kept in sync with frameworks/native/libs/binder/include/binder/IAppOpsService.h
     int checkOperation(int code, int uid, String packageName);
     int noteOperation(int code, int uid, String packageName);
-    int startOperation(IBinder token, int code, int uid, String packageName);
+    int startOperation(IBinder token, int code, int uid, String packageName,
+            boolean startIfModeDefault);
     void finishOperation(IBinder token, int code, int uid, String packageName);
     void startWatchingMode(int op, String packageName, IAppOpsCallback callback);
     void stopWatchingMode(IAppOpsCallback callback);
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index ca67a34..692b12f 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -211,9 +211,11 @@
 
     public final class ActiveCallback implements DeathRecipient {
         final IAppOpsActiveCallback mCallback;
+        final int mUid;
 
-        public ActiveCallback(IAppOpsActiveCallback callback) {
+        public ActiveCallback(IAppOpsActiveCallback callback, int uid) {
             mCallback = callback;
+            mUid = uid;
             try {
                 mCallback.asBinder().linkToDeath(this, 0);
             } catch (RemoteException e) {
@@ -233,21 +235,19 @@
     final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<IBinder, ClientState>();
 
     public final class ClientState extends Binder implements DeathRecipient {
+        final ArrayList<Op> mStartedOps = new ArrayList<>();
         final IBinder mAppToken;
         final int mPid;
-        final ArrayList<Op> mStartedOps;
 
         public ClientState(IBinder appToken) {
             mAppToken = appToken;
             mPid = Binder.getCallingPid();
-            if (appToken instanceof Binder) {
-                // For local clients, there is no reason to track them.
-                mStartedOps = null;
-            } else {
-                mStartedOps = new ArrayList<Op>();
+            // Watch only for remote processes dying
+            if (!(appToken instanceof Binder)) {
                 try {
                     mAppToken.linkToDeath(this, 0);
                 } catch (RemoteException e) {
+                    /* do nothing */
                 }
             }
         }
@@ -256,7 +256,7 @@
         public String toString() {
             return "ClientState{" +
                     "mAppToken=" + mAppToken +
-                    ", " + (mStartedOps != null ? ("pid=" + mPid) : "local") +
+                    ", " + "pid=" + mPid +
                     '}';
         }
 
@@ -1195,8 +1195,11 @@
 
     @Override
     public void startWatchingActive(int[] ops, IAppOpsActiveCallback callback) {
-        mContext.enforceCallingOrSelfPermission(Manifest.permission.WATCH_APPOPS,
-                "startWatchingActive");
+        int watchedUid = -1;
+        if (mContext.checkCallingOrSelfPermission(Manifest.permission.WATCH_APPOPS)
+                != PackageManager.PERMISSION_GRANTED) {
+            watchedUid = Binder.getCallingUid();
+        }
         if (ops != null) {
             Preconditions.checkArrayElementsInRange(ops, 0,
                     AppOpsManager._NUM_OP - 1, "Invalid op code in: " + Arrays.toString(ops));
@@ -1210,7 +1213,7 @@
                 callbacks = new SparseArray<>();
                 mActiveWatchers.put(callback.asBinder(), callbacks);
             }
-            final ActiveCallback activeCallback = new ActiveCallback(callback);
+            final ActiveCallback activeCallback = new ActiveCallback(callback, watchedUid);
             for (int op : ops) {
                 callbacks.put(op, activeCallback);
             }
@@ -1239,7 +1242,8 @@
     }
 
     @Override
-    public int startOperation(IBinder token, int code, int uid, String packageName) {
+    public int startOperation(IBinder token, int code, int uid, String packageName,
+            boolean startIfModeDefault) {
         verifyIncomingUid(uid);
         verifyIncomingOp(code);
         String resolvedPackageName = resolvePackageName(uid, packageName);
@@ -1265,7 +1269,8 @@
             // 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 (uidMode != AppOpsManager.MODE_ALLOWED
+                        && (!startIfModeDefault || uidMode != AppOpsManager.MODE_DEFAULT)) {
                     if (DEBUG) Slog.d(TAG, "noteOperation: reject #" + op.mode + " for code "
                             + switchCode + " (" + code + ") uid " + uid + " package "
                             + resolvedPackageName);
@@ -1274,7 +1279,8 @@
                 }
             } else {
                 final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
-                if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
+                if (switchOp.mode != AppOpsManager.MODE_ALLOWED
+                        && (!startIfModeDefault || switchOp.mode != AppOpsManager.MODE_DEFAULT)) {
                     if (DEBUG) Slog.d(TAG, "startOperation: reject #" + op.mode + " for code "
                             + switchCode + " (" + code + ") uid " + uid + " package "
                             + resolvedPackageName);
@@ -1316,11 +1322,9 @@
             if (op == null) {
                 return;
             }
-            if (client.mStartedOps != null) {
-                if (!client.mStartedOps.remove(op)) {
-                    throw new IllegalStateException("Operation not started: uid" + op.uid
-                            + " pkg=" + op.packageName + " op=" + op.op);
-                }
+            if (!client.mStartedOps.remove(op)) {
+                throw new IllegalStateException("Operation not started: uid" + op.uid
+                        + " pkg=" + op.packageName + " op=" + op.op);
             }
             finishOperationLocked(op);
             if (op.nesting <= 0) {
@@ -1337,6 +1341,9 @@
             final SparseArray<ActiveCallback> callbacks = mActiveWatchers.valueAt(i);
             ActiveCallback callback = callbacks.get(code);
             if (callback != null) {
+                if (callback.mUid >= 0 && callback.mUid != uid) {
+                    continue;
+                }
                 if (dispatchedCallbacks == null) {
                     dispatchedCallbacks = new ArraySet<>();
                 }
@@ -2420,7 +2427,7 @@
                     pw.print("    "); pw.print(mClients.keyAt(i)); pw.println(":");
                     ClientState cs = mClients.valueAt(i);
                     pw.print("      "); pw.println(cs);
-                    if (cs.mStartedOps != null && cs.mStartedOps.size() > 0) {
+                    if (cs.mStartedOps.size() > 0) {
                         pw.println("      Started ops:");
                         for (int j=0; j<cs.mStartedOps.size(); j++) {
                             Op op = cs.mStartedOps.get(j);
@@ -2651,8 +2658,12 @@
 
     @Override
     public boolean isOperationActive(int code, int uid, String packageName) {
-        mContext.enforceCallingOrSelfPermission(Manifest.permission.WATCH_APPOPS,
-                "isOperationActive");
+        if (Binder.getCallingUid() != uid) {
+            if (mContext.checkCallingOrSelfPermission(Manifest.permission.WATCH_APPOPS)
+                    != PackageManager.PERMISSION_GRANTED) {
+                return false;
+            }
+        }
         verifyIncomingOp(code);
         final String resolvedPackageName = resolvePackageName(uid, packageName);
         if (resolvedPackageName == null) {
@@ -2661,8 +2672,6 @@
         synchronized (AppOpsService.this) {
             for (int i = mClients.size() - 1; i >= 0; i--) {
                 final ClientState client = mClients.valueAt(i);
-                if (client.mStartedOps == null) continue;
-
                 for (int j = client.mStartedOps.size() - 1; j >= 0; j--) {
                     final Op op = client.mStartedOps.get(j);
                     if (op.op == code && op.uid == uid) return true;
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 14c99b2..752c44a 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -98,7 +98,7 @@
 
     private final Context mContext;
     private final PowerManager.WakeLock mWakeLock;
-    private final IAppOpsService mAppOpsService;
+    private final AppOpsManager mAppOps;
     private final IBatteryStats mBatteryStatsService;
     private PowerManagerInternal mPowerManagerInternal;
     private InputManager mIm;
@@ -265,8 +265,7 @@
         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
         mWakeLock.setReferenceCounted(true);
 
-        mAppOpsService =
-            IAppOpsService.Stub.asInterface(ServiceManager.getService(Context.APP_OPS_SERVICE));
+        mAppOps = mContext.getSystemService(AppOpsManager.class);
         mBatteryStatsService = IBatteryStats.Stub.asInterface(ServiceManager.getService(
                 BatteryStats.SERVICE_NAME));
 
@@ -721,17 +720,10 @@
     }
 
     private int getAppOpMode(Vibration vib) {
-        int mode;
-        try {
-            mode = mAppOpsService.checkAudioOperation(AppOpsManager.OP_VIBRATE,
-                    vib.usageHint, vib.uid, vib.opPkg);
-            if (mode == AppOpsManager.MODE_ALLOWED) {
-                mode = mAppOpsService.startOperation(AppOpsManager.getToken(mAppOpsService),
-                    AppOpsManager.OP_VIBRATE, vib.uid, vib.opPkg);
-            }
-        } catch (RemoteException e) {
-            Slog.e(TAG, "Failed to get appop mode for vibration!", e);
-            mode = AppOpsManager.MODE_IGNORED;
+        int mode = mAppOps.checkAudioOpNoThrow(AppOpsManager.OP_VIBRATE,
+                vib.usageHint, vib.uid, vib.opPkg);
+        if (mode == AppOpsManager.MODE_ALLOWED) {
+            mode = mAppOps.startOpNoThrow(AppOpsManager.OP_VIBRATE, vib.uid, vib.opPkg);
         }
         return mode;
     }
@@ -741,11 +733,8 @@
         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "reportFinishVibrationLocked");
         try {
             if (mCurrentVibration != null) {
-                try {
-                    mAppOpsService.finishOperation(AppOpsManager.getToken(mAppOpsService),
-                            AppOpsManager.OP_VIBRATE, mCurrentVibration.uid,
-                            mCurrentVibration.opPkg);
-                } catch (RemoteException e) { }
+                mAppOps.finishOp(AppOpsManager.OP_VIBRATE, mCurrentVibration.uid,
+                        mCurrentVibration.opPkg);
                 unlinkVibration(mCurrentVibration);
                 mCurrentVibration = null;
             }
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 5267f54..729ac0c 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -459,7 +459,7 @@
     private final PendingIntent mWakeupIntent;
     private final PendingIntent mTimeoutIntent;
 
-    private final IAppOpsService mAppOpsService;
+    private final AppOpsManager mAppOps;
     private final IBatteryStats mBatteryStats;
 
     // Current list of underlying location clients.
@@ -782,8 +782,7 @@
         mConnMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
 
         // App ops service to keep track of who is accessing the GPS
-        mAppOpsService = IAppOpsService.Stub.asInterface(ServiceManager.getService(
-                Context.APP_OPS_SERVICE));
+        mAppOps = mContext.getSystemService(AppOpsManager.class);
 
         // Battery statistics service to be notified when GPS turns on or off
         mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService(
@@ -1490,26 +1489,16 @@
             if (newChains != null) {
                 for (int i = 0; i < newChains.size(); ++i) {
                     final WorkChain newChain = newChains.get(i);
-                    try {
-                        mAppOpsService.startOperation(AppOpsManager.getToken(mAppOpsService),
-                                AppOpsManager.OP_GPS, newChain.getAttributionUid(),
-                                newChain.getAttributionTag());
-                    } catch (RemoteException e) {
-                        Log.w(TAG, "RemoteException", e);
-                    }
+                    mAppOps.startOpNoThrow(AppOpsManager.OP_GPS, newChain.getAttributionUid(),
+                            newChain.getAttributionTag());
                 }
             }
 
             if (goneChains != null) {
                 for (int i = 0; i < goneChains.size(); i++) {
                     final WorkChain goneChain = goneChains.get(i);
-                    try {
-                        mAppOpsService.finishOperation(AppOpsManager.getToken(mAppOpsService),
-                                AppOpsManager.OP_GPS, goneChain.getAttributionUid(),
-                                goneChain.getAttributionTag());
-                    } catch (RemoteException e) {
-                        Log.w(TAG, "RemoteException", e);
-                    }
+                    mAppOps.finishOp(AppOpsManager.OP_GPS, goneChain.getAttributionUid(),
+                            goneChain.getAttributionTag());
                 }
             }
 
@@ -1525,24 +1514,15 @@
             // Update sources that were not previously tracked.
             if (newWork != null) {
                 for (int i = 0; i < newWork.size(); i++) {
-                    try {
-                        mAppOpsService.startOperation(AppOpsManager.getToken(mAppOpsService),
-                                AppOpsManager.OP_GPS, newWork.get(i), newWork.getName(i));
-                    } catch (RemoteException e) {
-                        Log.w(TAG, "RemoteException", e);
-                    }
+                    mAppOps.startOpNoThrow(AppOpsManager.OP_GPS,
+                            newWork.get(i), newWork.getName(i));
                 }
             }
 
             // Update sources that are no longer tracked.
             if (goneWork != null) {
                 for (int i = 0; i < goneWork.size(); i++) {
-                    try {
-                        mAppOpsService.finishOperation(AppOpsManager.getToken(mAppOpsService),
-                                AppOpsManager.OP_GPS, goneWork.get(i), goneWork.getName(i));
-                    } catch (RemoteException e) {
-                        Log.w(TAG, "RemoteException", e);
-                    }
+                    mAppOps.finishOp(AppOpsManager.OP_GPS, goneWork.get(i), goneWork.getName(i));
                 }
             }
         }
diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java
index 3072f21..b729b6a 100644
--- a/services/core/java/com/android/server/power/Notifier.java
+++ b/services/core/java/com/android/server/power/Notifier.java
@@ -91,7 +91,7 @@
 
     private final Context mContext;
     private final IBatteryStats mBatteryStats;
-    private final IAppOpsService mAppOps;
+    private final AppOpsManager mAppOps;
     private final SuspendBlocker mSuspendBlocker;
     private final WindowManagerPolicy mPolicy;
     private final ActivityManagerInternal mActivityManagerInternal;
@@ -134,11 +134,10 @@
     private boolean mUserActivityPending;
 
     public Notifier(Looper looper, Context context, IBatteryStats batteryStats,
-            IAppOpsService appOps, SuspendBlocker suspendBlocker,
-            WindowManagerPolicy policy) {
+            SuspendBlocker suspendBlocker, WindowManagerPolicy policy) {
         mContext = context;
         mBatteryStats = batteryStats;
-        mAppOps = appOps;
+        mAppOps = mContext.getSystemService(AppOpsManager.class);
         mSuspendBlocker = suspendBlocker;
         mPolicy = policy;
         mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
@@ -194,8 +193,7 @@
                     mBatteryStats.noteStartWakelock(ownerUid, ownerPid, tag, historyTag,
                             monitorType, unimportantForLogging);
                     // XXX need to deal with disabled operations.
-                    mAppOps.startOperation(AppOpsManager.getToken(mAppOps),
-                            AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName);
+                    mAppOps.startOpNoThrow(AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName);
                 }
             } catch (RemoteException ex) {
                 // Ignore
@@ -295,8 +293,7 @@
                 } else {
                     mBatteryStats.noteStopWakelock(ownerUid, ownerPid, tag,
                             historyTag, monitorType);
-                    mAppOps.finishOperation(AppOpsManager.getToken(mAppOps),
-                            AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName);
+                    mAppOps.finishOp(AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName);
                 }
             } catch (RemoteException ex) {
                 // Ignore
@@ -539,12 +536,11 @@
         try {
             mBatteryStats.noteWakeUp(reason, reasonUid);
             if (opPackageName != null) {
-                mAppOps.noteOperation(AppOpsManager.OP_TURN_SCREEN_ON, opUid, opPackageName);
+                mAppOps.noteOpNoThrow(AppOpsManager.OP_TURN_SCREEN_ON, opUid, opPackageName);
             }
         } catch (RemoteException ex) {
             // Ignore
         }
-
     }
 
     /**
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index d67acc4..f77b0ee 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -759,8 +759,7 @@
             // with the animations and other critical functions of the power manager.
             mBatteryStats = BatteryStatsService.getService();
             mNotifier = new Notifier(Looper.getMainLooper(), mContext, mBatteryStats,
-                    mAppOps, createSuspendBlockerLocked("PowerManagerService.Broadcasts"),
-                    mPolicy);
+                    createSuspendBlockerLocked("PowerManagerService.Broadcasts"), mPolicy);
 
             mWirelessChargerDetector = new WirelessChargerDetector(sensorManager,
                     createSuspendBlockerLocked("PowerManagerService.WirelessChargerDetector"),
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 6356a35..36d331d 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -29,9 +29,7 @@
 import android.os.ParcelFileDescriptor;
 import android.os.PowerManager;
 import android.os.RemoteException;
-import android.os.SystemClock;
 import android.os.UserHandle;
-import android.provider.Settings;
 import android.util.ArraySet;
 import android.util.EventLog;
 import android.util.Slog;
@@ -50,9 +48,6 @@
 import java.util.List;
 import java.util.function.Consumer;
 
-import static android.app.AppOpsManager.MODE_ALLOWED;
-import static android.app.AppOpsManager.MODE_DEFAULT;
-import static android.app.AppOpsManager.OP_NONE;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
 import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
@@ -68,8 +63,6 @@
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_KEEP_SCREEN_ON;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_POWER;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE;
 import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS;
@@ -427,12 +420,7 @@
 
     void updateAppOpsState() {
         forAllWindows((w) -> {
-            if (w.mAppOp == OP_NONE) {
-                return;
-            }
-            final int mode = mService.mAppOps.noteOpNoThrow(w.mAppOp, w.getOwningUid(),
-                    w.getOwningPackage());
-            w.setAppOpVisibilityLw(mode == MODE_ALLOWED || mode == MODE_DEFAULT);
+            w.updateAppOpsState();
         }, false /* traverseTopToBottom */);
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index be1aa46..5633032 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1384,14 +1384,8 @@
 
             win.attach();
             mWindowMap.put(client.asBinder(), win);
-            if (win.mAppOp != AppOpsManager.OP_NONE) {
-                int startOpResult = mAppOps.startOpNoThrow(win.mAppOp, win.getOwningUid(),
-                        win.getOwningPackage());
-                if ((startOpResult != AppOpsManager.MODE_ALLOWED) &&
-                        (startOpResult != AppOpsManager.MODE_DEFAULT)) {
-                    win.setAppOpVisibilityLw(false);
-                }
-            }
+
+            win.initAppOpsState();
 
             final boolean hideSystemAlertWindows = !mHidingNonSystemOverlayWindows.isEmpty();
             win.setForceHideNonSystemOverlayWindowIfNeeded(hideSystemAlertWindows);
@@ -1656,9 +1650,8 @@
     void postWindowRemoveCleanupLocked(WindowState win) {
         if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "postWindowRemoveCleanupLocked: " + win);
         mWindowMap.remove(win.mClient.asBinder());
-        if (win.mAppOp != AppOpsManager.OP_NONE) {
-            mAppOps.finishOp(win.mAppOp, win.getOwningUid(), win.getOwningPackage());
-        }
+
+        win.resetAppOpsState();
 
         if (mCurrentFocus == null) {
             mWinRemovedSinceNullFocus.add(win);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index b706096..6266473 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -17,6 +17,9 @@
 package com.android.server.wm;
 
 import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_DEFAULT;
+import static android.app.AppOpsManager.OP_NONE;
 import static android.os.PowerManager.DRAW_WAKE_LOCK;
 import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.Display.DEFAULT_DISPLAY;
@@ -2571,7 +2574,7 @@
         }
     }
 
-    public void setAppOpVisibilityLw(boolean state) {
+    private void setAppOpVisibilityLw(boolean state) {
         if (mAppOpVisibility != state) {
             mAppOpVisibility = state;
             if (state) {
@@ -2588,6 +2591,49 @@
         }
     }
 
+    void initAppOpsState() {
+        if (mAppOp == OP_NONE || !mAppOpVisibility) {
+            return;
+        }
+        // If the app op was MODE_DEFAULT we would have checked the permission
+        // and add the window only if the permission was granted. Therefore, if
+        // the mode is MODE_DEFAULT we want the op to succeed as the window is
+        // shown.
+        final int mode = mService.mAppOps.startOpNoThrow(mAppOp,
+                getOwningUid(), getOwningPackage(), true);
+        if (mode != MODE_ALLOWED && mode != MODE_DEFAULT) {
+            setAppOpVisibilityLw(false);
+        }
+    }
+
+    void resetAppOpsState() {
+        if (mAppOp != OP_NONE && mAppOpVisibility) {
+            mService.mAppOps.finishOp(mAppOp, getOwningUid(), getOwningPackage());
+        }
+    }
+
+    void updateAppOpsState() {
+        if (mAppOp == OP_NONE) {
+            return;
+        }
+        final int uid = getOwningUid();
+        final String packageName = getOwningPackage();
+        if (mAppOpVisibility) {
+            // There is a race between the check and the finish calls but this is fine
+            // as this would mean we will get another change callback and will reconcile.
+            int mode = mService.mAppOps.checkOpNoThrow(mAppOp, uid, packageName);
+            if (mode != MODE_ALLOWED && mode != MODE_DEFAULT) {
+                mService.mAppOps.finishOp(mAppOp, uid, packageName);
+                setAppOpVisibilityLw(false);
+            }
+        } else {
+            final int mode = mService.mAppOps.startOpNoThrow(mAppOp, uid, packageName);
+            if (mode == MODE_ALLOWED || mode == MODE_DEFAULT) {
+                setAppOpVisibilityLw(true);
+            }
+        }
+    }
+
     public void hidePermanentlyLw() {
         if (!mPermanentlyHidden) {
             mPermanentlyHidden = true;