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

Commit 2eee2632 authored by Adam Bookatz's avatar Adam Bookatz Committed by Automerger Merge Worker
Browse files

Merge "Listener to watch op starts" into rvc-dev am: 316bc6ff am: 2d3310ec

Change-Id: I3c21676964f0317a2aaa25465ba211f68b5bbe2b
parents 3d4415ae 2d3310ec
Loading
Loading
Loading
Loading
+93 −1
Original line number Diff line number Diff line
@@ -66,6 +66,7 @@ import com.android.internal.app.IAppOpsAsyncNotedCallback;
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsNotedCallback;
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IAppOpsStartedCallback;
import com.android.internal.app.MessageSamplingConfig;
import com.android.internal.os.RuntimeInit;
import com.android.internal.os.ZygoteInit;
@@ -201,6 +202,10 @@ public class AppOpsManager {
    private final ArrayMap<OnOpActiveChangedListener, IAppOpsActiveCallback> mActiveWatchers =
            new ArrayMap<>();

    @GuardedBy("mStartedWatchers")
    private final ArrayMap<OnOpStartedListener, IAppOpsStartedCallback> mStartedWatchers =
            new ArrayMap<>();

    @GuardedBy("mNotedWatchers")
    private final ArrayMap<OnOpNotedListener, IAppOpsNotedCallback> mNotedWatchers =
            new ArrayMap<>();
@@ -6367,6 +6372,25 @@ public class AppOpsManager {
        default void onOpActiveChanged(int op, int uid, String packageName, boolean active) { }
    }

    /**
     * Callback for notification of an op being started.
     *
     * @hide
     */
    public interface OnOpStartedListener {
        /**
         * Called when an op was started.
         *
         * Note: This is only for op starts. It is not called when an op is noted or stopped.
         *
         * @param op The op code.
         * @param uid The UID performing the operation.
         * @param packageName The package performing the operation.
         * @param result The result of the start.
         */
        void onOpStarted(int op, int uid, String packageName, int result);
    }

    AppOpsManager(Context context, IAppOpsService service) {
        mContext = context;
        mService = service;
@@ -6921,6 +6945,73 @@ public class AppOpsManager {
        }
    }

    /**
     * Start watching for started app-ops.
     * An app-op may be long running and it has a clear start delimiter.
     * If an op start is attempted 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.
     *
     * <p> If you don't hold the {@code android.Manifest.permission#WATCH_APPOPS} permission
     * you can watch changes only for your UID.
     *
     * @param ops The operations to watch.
     * @param callback Where to report changes.
     *
     * @see #stopWatchingStarted(OnOpStartedListener)
     * @see #startWatchingActive(int[], OnOpActiveChangedListener)
     * @see #startWatchingNoted(int[], OnOpNotedListener)
     * @see #startOp(int, int, String, boolean, String, String)
     * @see #finishOp(int, int, String, String)
     *
     * @hide
     */
     @RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true)
     public void startWatchingStarted(@NonNull int[] ops, @NonNull OnOpStartedListener callback) {
         IAppOpsStartedCallback cb;
         synchronized (mStartedWatchers) {
             if (mStartedWatchers.containsKey(callback)) {
                 return;
             }
             cb = new IAppOpsStartedCallback.Stub() {
                 @Override
                 public void opStarted(int op, int uid, String packageName, int mode) {
                     callback.onOpStarted(op, uid, packageName, mode);
                 }
             };
             mStartedWatchers.put(callback, cb);
         }
         try {
             mService.startWatchingStarted(ops, cb);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
    }

    /**
     * Stop watching for started app-ops.
     * An app-op may be long running and it has a clear start delimiter.
     * Henceforth, if an op start is attempted by any package, you will not get a callback.
     * Unregistering a non-registered callback has no effect.
     *
     * @see #startWatchingStarted(int[], OnOpStartedListener)
     * @see #startOp(int, int, String, boolean, String, String)
     *
     * @hide
     */
    public void stopWatchingStarted(@NonNull OnOpStartedListener callback) {
        synchronized (mStartedWatchers) {
            final IAppOpsStartedCallback cb = mStartedWatchers.remove(callback);
            if (cb != null) {
                try {
                    mService.stopWatchingStarted(cb);
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
        }
    }

    /**
     * Start watching for noted app ops. An app op may be immediate or long running.
     * Immediate ops are noted while long running ones are started and stopped. This
@@ -6935,6 +7026,7 @@ public class AppOpsManager {
     * @param callback Where to report changes.
     *
     * @see #startWatchingActive(int[], OnOpActiveChangedListener)
     * @see #startWatchingStarted(int[], OnOpStartedListener)
     * @see #stopWatchingNoted(OnOpNotedListener)
     * @see #noteOp(String, int, String, String, String)
     *
@@ -6974,7 +7066,7 @@ public class AppOpsManager {
     */
    public void stopWatchingNoted(@NonNull OnOpNotedListener callback) {
        synchronized (mNotedWatchers) {
            final IAppOpsNotedCallback cb = mNotedWatchers.get(callback);
            final IAppOpsNotedCallback cb = mNotedWatchers.remove(callback);
            if (cb != null) {
                try {
                    mService.stopWatchingNoted(cb);
+4 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsActiveCallback;
import com.android.internal.app.IAppOpsAsyncNotedCallback;
import com.android.internal.app.IAppOpsNotedCallback;
import com.android.internal.app.IAppOpsStartedCallback;
import com.android.internal.app.MessageSamplingConfig;

interface IAppOpsService {
@@ -91,6 +92,9 @@ interface IAppOpsService {
    void stopWatchingActive(IAppOpsActiveCallback callback);
    boolean isOperationActive(int code, int uid, String packageName);

    void startWatchingStarted(in int[] ops, IAppOpsStartedCallback callback);
    void stopWatchingStarted(IAppOpsStartedCallback callback);

    void startWatchingModeWithFlags(int op, String packageName, int flags, IAppOpsCallback callback);

    void startWatchingNoted(in int[] ops, IAppOpsNotedCallback callback);
+22 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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 starts
oneway interface IAppOpsStartedCallback {
    void opStarted(int op, int uid, String packageName, int mode);
}
+13 −11
Original line number Diff line number Diff line
@@ -1634,22 +1634,24 @@ public final class ActiveServices {
                new AppOpsManager.OnOpNotedListener() {
                    @Override
                    public void onOpNoted(int op, int uid, String pkgName, int result) {
                        if (uid == mProcessRecord.uid && isNotTop()) {
                            incrementOpCount(op, result == AppOpsManager.MODE_ALLOWED);
                        }
                        incrementOpCountIfNeeded(op, uid, result);
                    }
        };

        private final AppOpsManager.OnOpActiveChangedInternalListener mOpActiveCallback =
                new AppOpsManager.OnOpActiveChangedInternalListener() {
        private final AppOpsManager.OnOpStartedListener mOpStartedCallback =
                new AppOpsManager.OnOpStartedListener() {
                    @Override
                    public void onOpActiveChanged(int op, int uid, String pkgName, boolean active) {
                        if (uid == mProcessRecord.uid && active && isNotTop()) {
                            incrementOpCount(op, true);
                        }
                    public void onOpStarted(int op, int uid, String pkgName, int result) {
                        incrementOpCountIfNeeded(op, uid, result);
                    }
        };

        private void incrementOpCountIfNeeded(int op, int uid, @AppOpsManager.Mode int result) {
            if (uid == mProcessRecord.uid && isNotTop()) {
                incrementOpCount(op, result == AppOpsManager.MODE_ALLOWED);
            }
        }

        private boolean isNotTop() {
            return mProcessRecord.getCurProcState() != ActivityManager.PROCESS_STATE_TOP;
        }
@@ -1674,7 +1676,7 @@ public final class ActiveServices {
            mNumFgs++;
            if (mNumFgs == 1) {
                mAppOpsManager.startWatchingNoted(LOGGED_AP_OPS, mOpNotedCallback);
                mAppOpsManager.startWatchingActive(LOGGED_AP_OPS, mOpActiveCallback);
                mAppOpsManager.startWatchingStarted(LOGGED_AP_OPS, mOpStartedCallback);
            }
        }

@@ -1684,7 +1686,7 @@ public final class ActiveServices {
                mDestroyed = true;
                logFinalValues();
                mAppOpsManager.stopWatchingNoted(mOpNotedCallback);
                mAppOpsManager.stopWatchingActive(mOpActiveCallback);
                mAppOpsManager.stopWatchingStarted(mOpStartedCallback);
            }
        }

+199 −6
Original line number Diff line number Diff line
@@ -149,6 +149,7 @@ import com.android.internal.app.IAppOpsAsyncNotedCallback;
import com.android.internal.app.IAppOpsCallback;
import com.android.internal.app.IAppOpsNotedCallback;
import com.android.internal.app.IAppOpsService;
import com.android.internal.app.IAppOpsStartedCallback;
import com.android.internal.app.MessageSamplingConfig;
import com.android.internal.os.Zygote;
import com.android.internal.util.ArrayUtils;
@@ -1292,6 +1293,7 @@ public class AppOpsService extends IAppOpsService.Stub {
    final ArrayMap<String, ArraySet<ModeCallback>> mPackageModeWatchers = new ArrayMap<>();
    final ArrayMap<IBinder, ModeCallback> mModeWatchers = new ArrayMap<>();
    final ArrayMap<IBinder, SparseArray<ActiveCallback>> mActiveWatchers = new ArrayMap<>();
    final ArrayMap<IBinder, SparseArray<StartedCallback>> mStartedWatchers = new ArrayMap<>();
    final ArrayMap<IBinder, SparseArray<NotedCallback>> mNotedWatchers = new ArrayMap<>();
    final AudioRestrictionManager mAudioRestrictionManager = new AudioRestrictionManager();

@@ -1407,6 +1409,50 @@ public class AppOpsService extends IAppOpsService.Stub {
        }
    }

    final class StartedCallback implements DeathRecipient {
        final IAppOpsStartedCallback mCallback;
        final int mWatchingUid;
        final int mCallingUid;
        final int mCallingPid;

        StartedCallback(IAppOpsStartedCallback callback, int watchingUid, int callingUid,
                int callingPid) {
            mCallback = callback;
            mWatchingUid = watchingUid;
            mCallingUid = callingUid;
            mCallingPid = callingPid;
            try {
                mCallback.asBinder().linkToDeath(this, 0);
            } catch (RemoteException e) {
                /*ignored*/
            }
        }

        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder(128);
            sb.append("StartedCallback{");
            sb.append(Integer.toHexString(System.identityHashCode(this)));
            sb.append(" watchinguid=");
            UserHandle.formatUid(sb, mWatchingUid);
            sb.append(" from uid=");
            UserHandle.formatUid(sb, mCallingUid);
            sb.append(" pid=");
            sb.append(mCallingPid);
            sb.append('}');
            return sb.toString();
        }

        void destroy() {
            mCallback.asBinder().unlinkToDeath(this, 0);
        }

        @Override
        public void binderDied() {
            stopWatchingStarted(mCallback);
        }
    }

    final class NotedCallback implements DeathRecipient {
        final IAppOpsNotedCallback mCallback;
        final int mWatchingUid;
@@ -3031,13 +3077,12 @@ public class AppOpsService extends IAppOpsService.Stub {
                return AppOpsManager.MODE_ERRORED;
            }
            final Op op = getOpLocked(ops, code, uid, true);
            final AttributedOp attributedOp = op.getOrCreateAttribution(op, attributionTag);
            if (isOpRestrictedLocked(uid, code, packageName, bypass)) {
                scheduleOpNotedIfNeededLocked(code, uid, packageName,
                        AppOpsManager.MODE_IGNORED);
                return AppOpsManager.MODE_IGNORED;
            }
            final UidState uidState = ops.uidState;
            final AttributedOp attributedOp = op.getOrCreateAttribution(op, attributionTag);
            if (attributedOp.isRunning()) {
                Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName + " code "
                        + code + " startTime of in progress event="
@@ -3045,6 +3090,7 @@ public class AppOpsService extends IAppOpsService.Stub {
            }

            final int switchCode = AppOpsManager.opToSwitch(code);
            final UidState uidState = ops.uidState;
            // 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) {
@@ -3076,10 +3122,9 @@ public class AppOpsService extends IAppOpsService.Stub {
                                + packageName + (attributionTag == null ? ""
                                : "." + attributionTag));
            }
            scheduleOpNotedIfNeededLocked(code, uid, packageName, AppOpsManager.MODE_ALLOWED);
            attributedOp.accessed(proxyUid, proxyPackageName, proxyAttributionTag, uidState.state,
                    flags);
            scheduleOpNotedIfNeededLocked(code, uid, packageName,
                    AppOpsManager.MODE_ALLOWED);

            if (shouldCollectAsyncNotedOp) {
                collectAsyncNotedOp(uid, packageName, code, attributionTag, message);
@@ -3092,7 +3137,7 @@ public class AppOpsService extends IAppOpsService.Stub {
    // TODO moltmann: Allow watching for attribution ops
    @Override
    public void startWatchingActive(int[] ops, IAppOpsActiveCallback callback) {
        int watchedUid = -1;
        int watchedUid = Process.INVALID_UID;
        final int callingUid = Binder.getCallingUid();
        final int callingPid = Binder.getCallingPid();
        if (mContext.checkCallingOrSelfPermission(Manifest.permission.WATCH_APPOPS)
@@ -3138,6 +3183,54 @@ public class AppOpsService extends IAppOpsService.Stub {
        }
    }

    @Override
    public void startWatchingStarted(int[] ops, @NonNull IAppOpsStartedCallback callback) {
        int watchedUid = Process.INVALID_UID;
        final int callingUid = Binder.getCallingUid();
        final int callingPid = Binder.getCallingPid();
        if (mContext.checkCallingOrSelfPermission(Manifest.permission.WATCH_APPOPS)
                != PackageManager.PERMISSION_GRANTED) {
            watchedUid = callingUid;
        }

        Preconditions.checkArgument(!ArrayUtils.isEmpty(ops), "Ops cannot be null or empty");
        Preconditions.checkArrayElementsInRange(ops, 0, AppOpsManager._NUM_OP - 1,
                "Invalid op code in: " + Arrays.toString(ops));
        Objects.requireNonNull(callback, "Callback cannot be null");

        synchronized (this) {
            SparseArray<StartedCallback> callbacks = mStartedWatchers.get(callback.asBinder());
            if (callbacks == null) {
                callbacks = new SparseArray<>();
                mStartedWatchers.put(callback.asBinder(), callbacks);
            }

            final StartedCallback startedCallback = new StartedCallback(callback, watchedUid,
                    callingUid, callingPid);
            for (int op : ops) {
                callbacks.put(op, startedCallback);
            }
        }
    }

    @Override
    public void stopWatchingStarted(IAppOpsStartedCallback callback) {
        Objects.requireNonNull(callback, "Callback cannot be null");

        synchronized (this) {
            final SparseArray<StartedCallback> startedCallbacks =
                    mStartedWatchers.remove(callback.asBinder());
            if (startedCallbacks == null) {
                return;
            }

            final int callbackCount = startedCallbacks.size();
            for (int i = 0; i < callbackCount; i++) {
                startedCallbacks.valueAt(i).destroy();
            }
        }
    }

    @Override
    public void startWatchingNoted(@NonNull int[] ops, @NonNull IAppOpsNotedCallback callback) {
        int watchedUid = Process.INVALID_UID;
@@ -3340,12 +3433,14 @@ public class AppOpsService extends IAppOpsService.Stub {
            final Ops ops = getOpsLocked(uid, resolvedPackageName, attributionTag, bypass,
                    true /* edit */);
            if (ops == null) {
                scheduleOpStartedIfNeededLocked(code, uid, packageName, AppOpsManager.MODE_IGNORED);
                if (DEBUG) Slog.d(TAG, "startOperation: no op for code " + code + " uid " + uid
                        + " package " + resolvedPackageName);
                return AppOpsManager.MODE_ERRORED;
            }
            final Op op = getOpLocked(ops, code, uid, true);
            if (isOpRestrictedLocked(uid, code, resolvedPackageName, bypass)) {
                scheduleOpStartedIfNeededLocked(code, uid, packageName, AppOpsManager.MODE_IGNORED);
                return AppOpsManager.MODE_IGNORED;
            }
            final AttributedOp attributedOp = op.getOrCreateAttribution(op, attributionTag);
@@ -3353,7 +3448,6 @@ public class AppOpsService extends IAppOpsService.Stub {
            final UidState uidState = ops.uidState;
            // 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.
            final int opCode = op.op;
            if (uidState.opModes != null && uidState.opModes.indexOfKey(switchCode) >= 0) {
                final int uidMode = uidState.evalMode(code, uidState.opModes.get(switchCode));
                if (uidMode != AppOpsManager.MODE_ALLOWED
@@ -3362,6 +3456,7 @@ public class AppOpsService extends IAppOpsService.Stub {
                            + switchCode + " (" + code + ") uid " + uid + " package "
                            + resolvedPackageName);
                    attributedOp.rejected(uidState.state, AppOpsManager.OP_FLAG_SELF);
                    scheduleOpStartedIfNeededLocked(code, uid, packageName, uidMode);
                    return uidMode;
                }
            } else {
@@ -3374,11 +3469,13 @@ public class AppOpsService extends IAppOpsService.Stub {
                            + switchCode + " (" + code + ") uid " + uid + " package "
                            + resolvedPackageName);
                    attributedOp.rejected(uidState.state, AppOpsManager.OP_FLAG_SELF);
                    scheduleOpStartedIfNeededLocked(code, uid, packageName, mode);
                    return mode;
                }
            }
            if (DEBUG) Slog.d(TAG, "startOperation: allowing code " + code + " uid " + uid
                    + " package " + resolvedPackageName);
            scheduleOpStartedIfNeededLocked(code, uid, packageName, AppOpsManager.MODE_ALLOWED);
            try {
                attributedOp.started(clientId, uidState.state);
            } catch (RemoteException e) {
@@ -3480,6 +3577,52 @@ public class AppOpsService extends IAppOpsService.Stub {
        }
    }

    private void scheduleOpStartedIfNeededLocked(int code, int uid, String pkgName, int result) {
        ArraySet<StartedCallback> dispatchedCallbacks = null;
        final int callbackListCount = mStartedWatchers.size();
        for (int i = 0; i < callbackListCount; i++) {
            final SparseArray<StartedCallback> callbacks = mStartedWatchers.valueAt(i);

            StartedCallback callback = callbacks.get(code);
            if (callback != null) {
                if (callback.mWatchingUid >= 0 && callback.mWatchingUid != uid) {
                    continue;
                }

                if (dispatchedCallbacks == null) {
                    dispatchedCallbacks = new ArraySet<>();
                }
                dispatchedCallbacks.add(callback);
            }
        }

        if (dispatchedCallbacks == null) {
            return;
        }

        mHandler.sendMessage(PooledLambda.obtainMessage(
                AppOpsService::notifyOpStarted,
                this, dispatchedCallbacks, code, uid, pkgName, result));
    }

    private void notifyOpStarted(ArraySet<StartedCallback> callbacks,
            int code, int uid, String packageName, int result) {
        final long identity = Binder.clearCallingIdentity();
        try {
            final int callbackCount = callbacks.size();
            for (int i = 0; i < callbackCount; i++) {
                final StartedCallback callback = callbacks.valueAt(i);
                try {
                    callback.mCallback.opStarted(code, uid, packageName, result);
                } catch (RemoteException e) {
                    /* do nothing */
                }
            }
        } finally {
            Binder.restoreCallingIdentity(identity);
        }
    }

    private void scheduleOpNotedIfNeededLocked(int code, int uid, String packageName,
            int result) {
        ArraySet<NotedCallback> dispatchedCallbacks = null;
@@ -5185,6 +5328,56 @@ public class AppOpsService extends IAppOpsService.Stub {
                    pw.println(cb);
                }
            }
            if (mStartedWatchers.size() > 0 && dumpMode < 0) {
                needSep = true;
                boolean printedHeader = false;

                final int watchersSize = mStartedWatchers.size();
                for (int watcherNum = 0; watcherNum < watchersSize; watcherNum++) {
                    final SparseArray<StartedCallback> startedWatchers =
                            mStartedWatchers.valueAt(watcherNum);
                    if (startedWatchers.size() <= 0) {
                        continue;
                    }

                    final StartedCallback cb = startedWatchers.valueAt(0);
                    if (dumpOp >= 0 && startedWatchers.indexOfKey(dumpOp) < 0) {
                        continue;
                    }

                    if (dumpPackage != null
                            && dumpUid != UserHandle.getAppId(cb.mWatchingUid)) {
                        continue;
                    }

                    if (!printedHeader) {
                        pw.println("  All op started watchers:");
                        printedHeader = true;
                    }

                    pw.print("    ");
                    pw.print(Integer.toHexString(System.identityHashCode(
                            mStartedWatchers.keyAt(watcherNum))));
                    pw.println(" ->");

                    pw.print("        [");
                    final int opCount = startedWatchers.size();
                    for (int opNum = 0; opNum < opCount; opNum++) {
                        if (opNum > 0) {
                            pw.print(' ');
                        }

                        pw.print(AppOpsManager.opToName(startedWatchers.keyAt(opNum)));
                        if (opNum < opCount - 1) {
                            pw.print(',');
                        }
                    }
                    pw.println("]");

                    pw.print("        ");
                    pw.println(cb);
                }
            }
            if (mNotedWatchers.size() > 0 && dumpMode < 0) {
                needSep = true;
                boolean printedHeader = false;
Loading