Loading core/java/android/app/AppOpsManager.java +93 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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<>(); Loading Loading @@ -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; Loading Loading @@ -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 Loading @@ -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) * Loading Loading @@ -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); Loading core/java/com/android/internal/app/IAppOpsService.aidl +4 −0 Original line number Diff line number Diff line Loading @@ -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 { Loading Loading @@ -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); Loading core/java/com/android/internal/app/IAppOpsStartedCallback.aidl 0 → 100644 +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); } services/core/java/com/android/server/am/ActiveServices.java +13 −11 Original line number Diff line number Diff line Loading @@ -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; } Loading @@ -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); } } Loading @@ -1684,7 +1686,7 @@ public final class ActiveServices { mDestroyed = true; logFinalValues(); mAppOpsManager.stopWatchingNoted(mOpNotedCallback); mAppOpsManager.stopWatchingActive(mOpActiveCallback); mAppOpsManager.stopWatchingStarted(mOpStartedCallback); } } Loading services/core/java/com/android/server/appop/AppOpsService.java +199 −6 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(); Loading Loading @@ -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; Loading Loading @@ -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=" Loading @@ -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) { Loading Loading @@ -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); Loading @@ -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) Loading Loading @@ -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; Loading Loading @@ -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); Loading @@ -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 Loading @@ -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 { Loading @@ -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) { Loading Loading @@ -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; Loading Loading @@ -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 Loading
core/java/android/app/AppOpsManager.java +93 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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<>(); Loading Loading @@ -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; Loading Loading @@ -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 Loading @@ -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) * Loading Loading @@ -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); Loading
core/java/com/android/internal/app/IAppOpsService.aidl +4 −0 Original line number Diff line number Diff line Loading @@ -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 { Loading Loading @@ -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); Loading
core/java/com/android/internal/app/IAppOpsStartedCallback.aidl 0 → 100644 +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); }
services/core/java/com/android/server/am/ActiveServices.java +13 −11 Original line number Diff line number Diff line Loading @@ -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; } Loading @@ -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); } } Loading @@ -1684,7 +1686,7 @@ public final class ActiveServices { mDestroyed = true; logFinalValues(); mAppOpsManager.stopWatchingNoted(mOpNotedCallback); mAppOpsManager.stopWatchingActive(mOpActiveCallback); mAppOpsManager.stopWatchingStarted(mOpStartedCallback); } } Loading
services/core/java/com/android/server/appop/AppOpsService.java +199 −6 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(); Loading Loading @@ -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; Loading Loading @@ -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=" Loading @@ -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) { Loading Loading @@ -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); Loading @@ -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) Loading Loading @@ -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; Loading Loading @@ -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); Loading @@ -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 Loading @@ -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 { Loading @@ -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) { Loading Loading @@ -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; Loading Loading @@ -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