Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f7b47253 authored by Svet Ganov's avatar Svet Ganov
Browse files

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
parent be06b5fc
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -47,7 +47,10 @@ package android.app {

  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 @@ package android.app {
    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 {
+82 −27
Original line number Diff line number Diff line
@@ -31,7 +31,6 @@ import android.os.Parcel;
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 @@ public class AppOpsManager {
    /** @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 @@ public class AppOpsManager {
     *
     * @hide
     */
    @TestApi
    public interface OnOpActiveChangedListener {
        /**
         * Called when the active state of an app op changes.
@@ -1731,7 +1732,7 @@ public class AppOpsManager {
     * 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 @@ public class AppOpsManager {
     * 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 @@ public class AppOpsManager {
     *
     * @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 @@ public class AppOpsManager {
     *
     * @hide
     */
    @TestApi
    public void stopWatchingActive(@NonNull OnOpActiveChangedListener callback) {
        synchronized (mActiveWatchers) {
            final IAppOpsActiveCallback cb = mActiveWatchers.get(callback);
@@ -2087,15 +2094,11 @@ public class AppOpsManager {
     * @hide
     */
    public int noteOp(int op, int uid, String packageName) {
        try {
            int mode = mService.noteOperation(op, uid, packageName);
        final int mode = noteOpNoThrow(op, uid, packageName);
        if (mode == MODE_ERRORED) {
            throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
        }
        return mode;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
@@ -2174,6 +2177,11 @@ public class AppOpsManager {
        }
    }

    /** @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 @@ public class AppOpsManager {
     * 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 @@ public class AppOpsManager {
     * @hide
     */
    public int startOp(int op, int uid, String packageName) {
        try {
            int mode = mService.startOperation(getToken(mService), op, uid, packageName);
        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;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    /**
@@ -2209,18 +2237,32 @@ public class AppOpsManager {
     * @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 @@ public class AppOpsManager {
        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);
+2 −1
Original line number Diff line number Diff line
@@ -26,7 +26,8 @@ interface IAppOpsService {
    // 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);
+33 −24
Original line number Diff line number Diff line
@@ -211,9 +211,11 @@ public class AppOpsService extends IAppOpsService.Stub {

    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 @@ public class AppOpsService extends IAppOpsService.Stub {
    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 class AppOpsService extends IAppOpsService.Stub {
        public String toString() {
            return "ClientState{" +
                    "mAppToken=" + mAppToken +
                    ", " + (mStartedOps != null ? ("pid=" + mPid) : "local") +
                    ", " + "pid=" + mPid +
                    '}';
        }

@@ -1195,8 +1195,11 @@ public class AppOpsService extends IAppOpsService.Stub {

    @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 @@ public class AppOpsService extends IAppOpsService.Stub {
                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 @@ public class AppOpsService extends IAppOpsService.Stub {
    }

    @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 @@ public class AppOpsService extends IAppOpsService.Stub {
            // 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 @@ public class AppOpsService extends IAppOpsService.Stub {
                }
            } 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,12 +1322,10 @@ public class AppOpsService extends IAppOpsService.Stub {
            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);
            }
            }
            finishOperationLocked(op);
            if (op.nesting <= 0) {
                scheduleOpActiveChangedIfNeededLocked(code, uid, packageName, false);
@@ -1337,6 +1341,9 @@ public class AppOpsService extends IAppOpsService.Stub {
            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 @@ public class AppOpsService extends IAppOpsService.Stub {
                    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 @@ public class AppOpsService extends IAppOpsService.Stub {

    @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 @@ public class AppOpsService extends IAppOpsService.Stub {
        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;
+8 −19
Original line number Diff line number Diff line
@@ -98,7 +98,7 @@ public class VibratorService extends IVibratorService.Stub

    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 @@ public class VibratorService extends IVibratorService.Stub
        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 @@ public class VibratorService extends IVibratorService.Stub
    }

    private int getAppOpMode(Vibration vib) {
        int mode;
        try {
            mode = mAppOpsService.checkAudioOperation(AppOpsManager.OP_VIBRATE,
        int mode = mAppOps.checkAudioOpNoThrow(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;
            mode = mAppOps.startOpNoThrow(AppOpsManager.OP_VIBRATE, vib.uid, vib.opPkg);
        }
        return mode;
    }
@@ -741,11 +733,8 @@ public class VibratorService extends IVibratorService.Stub
        Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "reportFinishVibrationLocked");
        try {
            if (mCurrentVibration != null) {
                try {
                    mAppOpsService.finishOperation(AppOpsManager.getToken(mAppOpsService),
                            AppOpsManager.OP_VIBRATE, mCurrentVibration.uid,
                mAppOps.finishOp(AppOpsManager.OP_VIBRATE, mCurrentVibration.uid,
                        mCurrentVibration.opPkg);
                } catch (RemoteException e) { }
                unlinkVibration(mCurrentVibration);
                mCurrentVibration = null;
            }
Loading