Loading core/java/android/app/AppOpsManager.java +57 −3 Original line number Diff line number Diff line Loading @@ -7205,6 +7205,34 @@ public class AppOpsManager { * @hide */ public interface OnOpStartedListener { /** * Represents a start operation that was unsuccessful * @hide */ public int START_TYPE_FAILED = 0; /** * Represents a successful start operation * @hide */ public int START_TYPE_STARTED = 1; /** * Represents an operation where a restricted operation became unrestricted, and resumed. * @hide */ public int START_TYPE_RESUMED = 2; /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(flag = true, prefix = { "TYPE_" }, value = { START_TYPE_FAILED, START_TYPE_STARTED, START_TYPE_RESUMED }) public @interface StartedType {} /** * Called when an op was started. * Loading @@ -7213,11 +7241,35 @@ public class AppOpsManager { * @param uid The UID performing the operation. * @param packageName The package performing the operation. * @param attributionTag The attribution tag performing the operation. * @param flags The flags of this op * @param flags The flags of this op. * @param result The result of the start. */ void onOpStarted(int op, int uid, String packageName, String attributionTag, @OpFlags int flags, @Mode int result); /** * Called when an op was started. * * Note: This is only for op starts. It is not called when an op is noted or stopped. * By default, unless this method is overridden, no code will be executed for resume * events. * @param op The op code. * @param uid The UID performing the operation. * @param packageName The package performing the operation. * @param attributionTag The attribution tag performing the operation. * @param flags The flags of this op. * @param result The result of the start. * @param startType The start type of this start event. Either failed, resumed, or started. * @param attributionFlags The location of this started op in an attribution chain. * @param attributionChainId The ID of the attribution chain of this op, if it is in one. */ default void onOpStarted(int op, int uid, String packageName, String attributionTag, @OpFlags int flags, @Mode int result, @StartedType int startType, @AttributionFlags int attributionFlags, int attributionChainId) { if (startType != START_TYPE_RESUMED) { onOpStarted(op, uid, packageName, attributionTag, flags, result); } } } AppOpsManager(Context context, IAppOpsService service) { Loading Loading @@ -7858,8 +7910,10 @@ public class AppOpsManager { cb = new IAppOpsStartedCallback.Stub() { @Override public void opStarted(int op, int uid, String packageName, String attributionTag, int flags, int mode) { callback.onOpStarted(op, uid, packageName, attributionTag, flags, mode); int flags, int mode, int startType, int attributionFlags, int attributionChainId) { callback.onOpStarted(op, uid, packageName, attributionTag, flags, mode, startType, attributionFlags, attributionChainId); } }; mStartedWatchers.put(callback, cb); Loading core/java/android/permission/PermissionUsageHelper.java +72 −12 Original line number Diff line number Diff line Loading @@ -31,7 +31,9 @@ import static android.app.AppOpsManager.OPSTR_FINE_LOCATION; import static android.app.AppOpsManager.OPSTR_PHONE_CALL_CAMERA; import static android.app.AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE; import static android.app.AppOpsManager.OPSTR_RECORD_AUDIO; import static android.app.AppOpsManager.OP_CAMERA; import static android.app.AppOpsManager.OP_FLAGS_ALL_TRUSTED; import static android.app.AppOpsManager.OP_RECORD_AUDIO; import static android.media.AudioSystem.MODE_IN_COMMUNICATION; import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS; Loading Loading @@ -63,7 +65,8 @@ import java.util.Objects; * * @hide */ public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedListener { public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedListener, AppOpsManager.OnOpStartedListener { /** Whether to show the mic and camera icons. */ private static final String PROPERTY_CAMERA_MIC_ICONS_ENABLED = "camera_mic_icons_enabled"; Loading Loading @@ -160,9 +163,10 @@ public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedLis mUserContexts = new ArrayMap<>(); mUserContexts.put(Process.myUserHandle(), mContext); // TODO ntmyren: make this listen for flag enable/disable changes String[] ops = { OPSTR_CAMERA, OPSTR_RECORD_AUDIO }; mContext.getSystemService(AppOpsManager.class).startWatchingActive(ops, context.getMainExecutor(), this); String[] opStrs = { OPSTR_CAMERA, OPSTR_RECORD_AUDIO }; mAppOpsManager.startWatchingActive(opStrs, context.getMainExecutor(), this); int[] ops = { OP_CAMERA, OP_RECORD_AUDIO }; mAppOpsManager.startWatchingStarted(ops, this); } private Context getUserContext(UserHandle user) { Loading @@ -182,25 +186,65 @@ public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedLis public void onOpActiveChanged(@NonNull String op, int uid, @NonNull String packageName, @Nullable String attributionTag, boolean active, @AttributionFlags int attributionFlags, int attributionChainId) { if (attributionChainId == ATTRIBUTION_CHAIN_ID_NONE || attributionFlags == ATTRIBUTION_FLAGS_NONE || (attributionFlags & ATTRIBUTION_FLAG_TRUSTED) == 0) { // If this is not a chain, or it is untrusted, return if (active) { // Started callback handles these return; } if (!active) { // if any link in the chain is finished, remove the chain. // if any link in the chain is finished, remove the chain. Then, find any other chains that // contain this op/package/uid/tag combination, and remove them, as well. // TODO ntmyren: be smarter about this mAttributionChains.remove(attributionChainId); int numChains = mAttributionChains.size(); ArrayList<Integer> toRemove = new ArrayList<>(); for (int i = 0; i < numChains; i++) { int chainId = mAttributionChains.keyAt(i); ArrayList<AccessChainLink> chain = mAttributionChains.valueAt(i); int chainSize = chain.size(); for (int j = 0; j < chainSize; j++) { AccessChainLink link = chain.get(j); if (link.packageAndOpEquals(op, packageName, attributionTag, uid)) { toRemove.add(chainId); break; } } } mAttributionChains.removeAll(toRemove); } @Override public void onOpStarted(int op, int uid, String packageName, String attributionTag, @AppOpsManager.OpFlags int flags, @AppOpsManager.Mode int result) { // not part of an attribution chain. Do nothing } @Override public void onOpStarted(int op, int uid, String packageName, String attributionTag, @AppOpsManager.OpFlags int flags, @AppOpsManager.Mode int result, @StartedType int startedType, @AttributionFlags int attributionFlags, int attributionChainId) { if (startedType == START_TYPE_FAILED || attributionChainId == ATTRIBUTION_CHAIN_ID_NONE || attributionFlags == ATTRIBUTION_FLAGS_NONE || (attributionFlags & ATTRIBUTION_FLAG_TRUSTED) == 0) { // If this is not a successful start, or it is not a chain, or it is untrusted, return return; } addLinkToChainIfNotPresent(AppOpsManager.opToPublicName(op), packageName, uid, attributionTag, attributionFlags, attributionChainId); } private void addLinkToChainIfNotPresent(String op, String packageName, int uid, String attributionTag, int attributionFlags, int attributionChainId) { ArrayList<AccessChainLink> currentChain = mAttributionChains.computeIfAbsent( attributionChainId, k -> new ArrayList<>()); AccessChainLink link = new AccessChainLink(op, packageName, attributionTag, uid, attributionFlags); if (currentChain.contains(link)) { return; } int currSize = currentChain.size(); if (currSize == 0 || link.isEnd() || !currentChain.get(currSize - 1).isEnd()) { // if the list is empty, this link is the end, or the last link in the current chain Loading Loading @@ -613,5 +657,21 @@ public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedLis public boolean isStart() { return (flags & ATTRIBUTION_FLAG_RECEIVER) != 0; } @Override public boolean equals(Object obj) { if (!(obj instanceof AccessChainLink)) { return false; } AccessChainLink other = (AccessChainLink) obj; return other.flags == flags && packageAndOpEquals(other.usage.op, other.usage.packageName, other.usage.attributionTag, other.usage.uid); } public boolean packageAndOpEquals(String op, String packageName, String attributionTag, int uid) { return Objects.equals(op, usage.op) && Objects.equals(packageName, usage.packageName) && Objects.equals(attributionTag, usage.attributionTag) && uid == usage.uid; } } } core/java/com/android/internal/app/IAppOpsStartedCallback.aidl +2 −1 Original line number Diff line number Diff line Loading @@ -18,5 +18,6 @@ package com.android.internal.app; // Iterface to observe op starts oneway interface IAppOpsStartedCallback { void opStarted(int op, int uid, String packageName, String attributionTag, int flags, int mode); void opStarted(int op, int uid, String packageName, String attributionTag, int flags, int mode, int startedType, int attributionFlags, int attributionChainId); } services/core/java/com/android/server/appop/AppOpsService.java +23 −8 Original line number Diff line number Diff line Loading @@ -45,6 +45,9 @@ import static android.app.AppOpsManager.OP_NONE; import static android.app.AppOpsManager.OP_PLAY_AUDIO; import static android.app.AppOpsManager.OP_RECORD_AUDIO; import static android.app.AppOpsManager.OP_RECORD_AUDIO_HOTWORD; import static android.app.AppOpsManager.OnOpStartedListener.START_TYPE_FAILED; import static android.app.AppOpsManager.OnOpStartedListener.START_TYPE_RESUMED; import static android.app.AppOpsManager.OnOpStartedListener.START_TYPE_STARTED; import static android.app.AppOpsManager.OpEventProxyInfo; import static android.app.AppOpsManager.RestrictionBypass; import static android.app.AppOpsManager.SAMPLING_STRATEGY_BOOT_TIME_SAMPLING; Loading Loading @@ -1238,6 +1241,11 @@ public class AppOpsService extends IAppOpsService.Stub { scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid, parent.packageName, tag, true, event.getAttributionFlags(), event.getAttributionChainId()); } // Note: this always sends MODE_ALLOWED, even if the mode is FOREGROUND // TODO ntmyren: figure out how to get the real mode. scheduleOpStartedIfNeededLocked(parent.op, parent.uid, parent.packageName, tag, event.getFlags(), MODE_ALLOWED, START_TYPE_RESUMED, event.getAttributionFlags(), event.getAttributionChainId()); } mPausedInProgressEvents = null; } Loading Loading @@ -3945,13 +3953,15 @@ public class AppOpsService extends IAppOpsService.Stub { } boolean isRestricted = false; int startType = START_TYPE_FAILED; synchronized (this) { final Ops ops = getOpsLocked(uid, packageName, attributionTag, pvr.isAttributionTagValid, pvr.bypass, /* edit */ true); if (ops == null) { if (!dryRun) { scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag, flags, AppOpsManager.MODE_IGNORED); flags, AppOpsManager.MODE_IGNORED, startType, attributionFlags, attributionChainId); } if (DEBUG) Slog.d(TAG, "startOperation: no op for code " + code + " uid " + uid + " package " + packageName + " flags: " Loading @@ -3977,7 +3987,7 @@ public class AppOpsService extends IAppOpsService.Stub { if (!dryRun) { attributedOp.rejected(uidState.state, flags); scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag, flags, uidMode); flags, uidMode, startType, attributionFlags, attributionChainId); } return new SyncNotedAppOp(uidMode, code, attributionTag, packageName); } Loading @@ -3993,7 +4003,7 @@ public class AppOpsService extends IAppOpsService.Stub { if (!dryRun) { attributedOp.rejected(uidState.state, flags); scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag, flags, mode); flags, mode, startType, attributionFlags, attributionChainId); } return new SyncNotedAppOp(mode, code, attributionTag, packageName); } Loading @@ -4011,12 +4021,14 @@ public class AppOpsService extends IAppOpsService.Stub { attributedOp.started(clientId, proxyUid, proxyPackageName, proxyAttributionTag, uidState.state, flags, attributionFlags, attributionChainId); startType = START_TYPE_STARTED; } } catch (RemoteException e) { throw new RuntimeException(e); } scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag, flags, isRestricted ? MODE_IGNORED : MODE_ALLOWED); isRestricted ? MODE_IGNORED : MODE_ALLOWED, startType, attributionFlags, attributionChainId); } } Loading Loading @@ -4187,7 +4199,9 @@ public class AppOpsService extends IAppOpsService.Stub { } private void scheduleOpStartedIfNeededLocked(int code, int uid, String pkgName, String attributionTag, @OpFlags int flags, @Mode int result) { String attributionTag, @OpFlags int flags, @Mode int result, @AppOpsManager.OnOpStartedListener.StartedType int startedType, @AttributionFlags int attributionFlags, int attributionChainId) { ArraySet<StartedCallback> dispatchedCallbacks = null; final int callbackListCount = mStartedWatchers.size(); for (int i = 0; i < callbackListCount; i++) { Loading @@ -4213,12 +4227,13 @@ public class AppOpsService extends IAppOpsService.Stub { mHandler.sendMessage(PooledLambda.obtainMessage( AppOpsService::notifyOpStarted, this, dispatchedCallbacks, code, uid, pkgName, attributionTag, flags, result)); result, startedType, attributionFlags, attributionChainId)); } private void notifyOpStarted(ArraySet<StartedCallback> callbacks, int code, int uid, String packageName, String attributionTag, @OpFlags int flags, @Mode int result) { @Mode int result, @AppOpsManager.OnOpStartedListener.StartedType int startedType, @AttributionFlags int attributionFlags, int attributionChainId) { final long identity = Binder.clearCallingIdentity(); try { final int callbackCount = callbacks.size(); Loading @@ -4226,7 +4241,7 @@ public class AppOpsService extends IAppOpsService.Stub { final StartedCallback callback = callbacks.valueAt(i); try { callback.mCallback.opStarted(code, uid, packageName, attributionTag, flags, result); result, startedType, attributionFlags, attributionChainId); } catch (RemoteException e) { /* do nothing */ } Loading services/tests/servicestests/src/com/android/server/appop/AppOpsStartedWatcherTest.java +9 −3 Original line number Diff line number Diff line Loading @@ -64,12 +64,16 @@ public class AppOpsStartedWatcherTest { .times(1)).onOpStarted(eq(AppOpsManager.OP_FINE_LOCATION), eq(Process.myUid()), eq(getContext().getPackageName()), eq(getContext().getAttributionTag()), eq(AppOpsManager.OP_FLAG_SELF), eq(AppOpsManager.MODE_ALLOWED)); eq(AppOpsManager.MODE_ALLOWED), eq(OnOpStartedListener.START_TYPE_STARTED), eq(AppOpsManager.ATTRIBUTION_FLAGS_NONE), eq(AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE)); inOrder.verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS) .times(1)).onOpStarted(eq(AppOpsManager.OP_CAMERA), eq(Process.myUid()), eq(getContext().getPackageName()), eq(getContext().getAttributionTag()), eq(AppOpsManager.OP_FLAG_SELF), eq(AppOpsManager.MODE_ALLOWED)); eq(AppOpsManager.MODE_ALLOWED), eq(OnOpStartedListener.START_TYPE_STARTED), eq(AppOpsManager.ATTRIBUTION_FLAGS_NONE), eq(AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE)); // Stop watching appOpsManager.stopWatchingStarted(listener); Loading @@ -94,7 +98,9 @@ public class AppOpsStartedWatcherTest { .times(2)).onOpStarted(eq(AppOpsManager.OP_CAMERA), eq(Process.myUid()), eq(getContext().getPackageName()), eq(getContext().getAttributionTag()), eq(AppOpsManager.OP_FLAG_SELF), eq(AppOpsManager.MODE_ALLOWED)); eq(AppOpsManager.MODE_ALLOWED), eq(OnOpStartedListener.START_TYPE_STARTED), eq(AppOpsManager.ATTRIBUTION_FLAGS_NONE), eq(AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE)); verifyNoMoreInteractions(listener); // Finish up Loading Loading
core/java/android/app/AppOpsManager.java +57 −3 Original line number Diff line number Diff line Loading @@ -7205,6 +7205,34 @@ public class AppOpsManager { * @hide */ public interface OnOpStartedListener { /** * Represents a start operation that was unsuccessful * @hide */ public int START_TYPE_FAILED = 0; /** * Represents a successful start operation * @hide */ public int START_TYPE_STARTED = 1; /** * Represents an operation where a restricted operation became unrestricted, and resumed. * @hide */ public int START_TYPE_RESUMED = 2; /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(flag = true, prefix = { "TYPE_" }, value = { START_TYPE_FAILED, START_TYPE_STARTED, START_TYPE_RESUMED }) public @interface StartedType {} /** * Called when an op was started. * Loading @@ -7213,11 +7241,35 @@ public class AppOpsManager { * @param uid The UID performing the operation. * @param packageName The package performing the operation. * @param attributionTag The attribution tag performing the operation. * @param flags The flags of this op * @param flags The flags of this op. * @param result The result of the start. */ void onOpStarted(int op, int uid, String packageName, String attributionTag, @OpFlags int flags, @Mode int result); /** * Called when an op was started. * * Note: This is only for op starts. It is not called when an op is noted or stopped. * By default, unless this method is overridden, no code will be executed for resume * events. * @param op The op code. * @param uid The UID performing the operation. * @param packageName The package performing the operation. * @param attributionTag The attribution tag performing the operation. * @param flags The flags of this op. * @param result The result of the start. * @param startType The start type of this start event. Either failed, resumed, or started. * @param attributionFlags The location of this started op in an attribution chain. * @param attributionChainId The ID of the attribution chain of this op, if it is in one. */ default void onOpStarted(int op, int uid, String packageName, String attributionTag, @OpFlags int flags, @Mode int result, @StartedType int startType, @AttributionFlags int attributionFlags, int attributionChainId) { if (startType != START_TYPE_RESUMED) { onOpStarted(op, uid, packageName, attributionTag, flags, result); } } } AppOpsManager(Context context, IAppOpsService service) { Loading Loading @@ -7858,8 +7910,10 @@ public class AppOpsManager { cb = new IAppOpsStartedCallback.Stub() { @Override public void opStarted(int op, int uid, String packageName, String attributionTag, int flags, int mode) { callback.onOpStarted(op, uid, packageName, attributionTag, flags, mode); int flags, int mode, int startType, int attributionFlags, int attributionChainId) { callback.onOpStarted(op, uid, packageName, attributionTag, flags, mode, startType, attributionFlags, attributionChainId); } }; mStartedWatchers.put(callback, cb); Loading
core/java/android/permission/PermissionUsageHelper.java +72 −12 Original line number Diff line number Diff line Loading @@ -31,7 +31,9 @@ import static android.app.AppOpsManager.OPSTR_FINE_LOCATION; import static android.app.AppOpsManager.OPSTR_PHONE_CALL_CAMERA; import static android.app.AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE; import static android.app.AppOpsManager.OPSTR_RECORD_AUDIO; import static android.app.AppOpsManager.OP_CAMERA; import static android.app.AppOpsManager.OP_FLAGS_ALL_TRUSTED; import static android.app.AppOpsManager.OP_RECORD_AUDIO; import static android.media.AudioSystem.MODE_IN_COMMUNICATION; import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS; Loading Loading @@ -63,7 +65,8 @@ import java.util.Objects; * * @hide */ public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedListener { public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedListener, AppOpsManager.OnOpStartedListener { /** Whether to show the mic and camera icons. */ private static final String PROPERTY_CAMERA_MIC_ICONS_ENABLED = "camera_mic_icons_enabled"; Loading Loading @@ -160,9 +163,10 @@ public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedLis mUserContexts = new ArrayMap<>(); mUserContexts.put(Process.myUserHandle(), mContext); // TODO ntmyren: make this listen for flag enable/disable changes String[] ops = { OPSTR_CAMERA, OPSTR_RECORD_AUDIO }; mContext.getSystemService(AppOpsManager.class).startWatchingActive(ops, context.getMainExecutor(), this); String[] opStrs = { OPSTR_CAMERA, OPSTR_RECORD_AUDIO }; mAppOpsManager.startWatchingActive(opStrs, context.getMainExecutor(), this); int[] ops = { OP_CAMERA, OP_RECORD_AUDIO }; mAppOpsManager.startWatchingStarted(ops, this); } private Context getUserContext(UserHandle user) { Loading @@ -182,25 +186,65 @@ public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedLis public void onOpActiveChanged(@NonNull String op, int uid, @NonNull String packageName, @Nullable String attributionTag, boolean active, @AttributionFlags int attributionFlags, int attributionChainId) { if (attributionChainId == ATTRIBUTION_CHAIN_ID_NONE || attributionFlags == ATTRIBUTION_FLAGS_NONE || (attributionFlags & ATTRIBUTION_FLAG_TRUSTED) == 0) { // If this is not a chain, or it is untrusted, return if (active) { // Started callback handles these return; } if (!active) { // if any link in the chain is finished, remove the chain. // if any link in the chain is finished, remove the chain. Then, find any other chains that // contain this op/package/uid/tag combination, and remove them, as well. // TODO ntmyren: be smarter about this mAttributionChains.remove(attributionChainId); int numChains = mAttributionChains.size(); ArrayList<Integer> toRemove = new ArrayList<>(); for (int i = 0; i < numChains; i++) { int chainId = mAttributionChains.keyAt(i); ArrayList<AccessChainLink> chain = mAttributionChains.valueAt(i); int chainSize = chain.size(); for (int j = 0; j < chainSize; j++) { AccessChainLink link = chain.get(j); if (link.packageAndOpEquals(op, packageName, attributionTag, uid)) { toRemove.add(chainId); break; } } } mAttributionChains.removeAll(toRemove); } @Override public void onOpStarted(int op, int uid, String packageName, String attributionTag, @AppOpsManager.OpFlags int flags, @AppOpsManager.Mode int result) { // not part of an attribution chain. Do nothing } @Override public void onOpStarted(int op, int uid, String packageName, String attributionTag, @AppOpsManager.OpFlags int flags, @AppOpsManager.Mode int result, @StartedType int startedType, @AttributionFlags int attributionFlags, int attributionChainId) { if (startedType == START_TYPE_FAILED || attributionChainId == ATTRIBUTION_CHAIN_ID_NONE || attributionFlags == ATTRIBUTION_FLAGS_NONE || (attributionFlags & ATTRIBUTION_FLAG_TRUSTED) == 0) { // If this is not a successful start, or it is not a chain, or it is untrusted, return return; } addLinkToChainIfNotPresent(AppOpsManager.opToPublicName(op), packageName, uid, attributionTag, attributionFlags, attributionChainId); } private void addLinkToChainIfNotPresent(String op, String packageName, int uid, String attributionTag, int attributionFlags, int attributionChainId) { ArrayList<AccessChainLink> currentChain = mAttributionChains.computeIfAbsent( attributionChainId, k -> new ArrayList<>()); AccessChainLink link = new AccessChainLink(op, packageName, attributionTag, uid, attributionFlags); if (currentChain.contains(link)) { return; } int currSize = currentChain.size(); if (currSize == 0 || link.isEnd() || !currentChain.get(currSize - 1).isEnd()) { // if the list is empty, this link is the end, or the last link in the current chain Loading Loading @@ -613,5 +657,21 @@ public class PermissionUsageHelper implements AppOpsManager.OnOpActiveChangedLis public boolean isStart() { return (flags & ATTRIBUTION_FLAG_RECEIVER) != 0; } @Override public boolean equals(Object obj) { if (!(obj instanceof AccessChainLink)) { return false; } AccessChainLink other = (AccessChainLink) obj; return other.flags == flags && packageAndOpEquals(other.usage.op, other.usage.packageName, other.usage.attributionTag, other.usage.uid); } public boolean packageAndOpEquals(String op, String packageName, String attributionTag, int uid) { return Objects.equals(op, usage.op) && Objects.equals(packageName, usage.packageName) && Objects.equals(attributionTag, usage.attributionTag) && uid == usage.uid; } } }
core/java/com/android/internal/app/IAppOpsStartedCallback.aidl +2 −1 Original line number Diff line number Diff line Loading @@ -18,5 +18,6 @@ package com.android.internal.app; // Iterface to observe op starts oneway interface IAppOpsStartedCallback { void opStarted(int op, int uid, String packageName, String attributionTag, int flags, int mode); void opStarted(int op, int uid, String packageName, String attributionTag, int flags, int mode, int startedType, int attributionFlags, int attributionChainId); }
services/core/java/com/android/server/appop/AppOpsService.java +23 −8 Original line number Diff line number Diff line Loading @@ -45,6 +45,9 @@ import static android.app.AppOpsManager.OP_NONE; import static android.app.AppOpsManager.OP_PLAY_AUDIO; import static android.app.AppOpsManager.OP_RECORD_AUDIO; import static android.app.AppOpsManager.OP_RECORD_AUDIO_HOTWORD; import static android.app.AppOpsManager.OnOpStartedListener.START_TYPE_FAILED; import static android.app.AppOpsManager.OnOpStartedListener.START_TYPE_RESUMED; import static android.app.AppOpsManager.OnOpStartedListener.START_TYPE_STARTED; import static android.app.AppOpsManager.OpEventProxyInfo; import static android.app.AppOpsManager.RestrictionBypass; import static android.app.AppOpsManager.SAMPLING_STRATEGY_BOOT_TIME_SAMPLING; Loading Loading @@ -1238,6 +1241,11 @@ public class AppOpsService extends IAppOpsService.Stub { scheduleOpActiveChangedIfNeededLocked(parent.op, parent.uid, parent.packageName, tag, true, event.getAttributionFlags(), event.getAttributionChainId()); } // Note: this always sends MODE_ALLOWED, even if the mode is FOREGROUND // TODO ntmyren: figure out how to get the real mode. scheduleOpStartedIfNeededLocked(parent.op, parent.uid, parent.packageName, tag, event.getFlags(), MODE_ALLOWED, START_TYPE_RESUMED, event.getAttributionFlags(), event.getAttributionChainId()); } mPausedInProgressEvents = null; } Loading Loading @@ -3945,13 +3953,15 @@ public class AppOpsService extends IAppOpsService.Stub { } boolean isRestricted = false; int startType = START_TYPE_FAILED; synchronized (this) { final Ops ops = getOpsLocked(uid, packageName, attributionTag, pvr.isAttributionTagValid, pvr.bypass, /* edit */ true); if (ops == null) { if (!dryRun) { scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag, flags, AppOpsManager.MODE_IGNORED); flags, AppOpsManager.MODE_IGNORED, startType, attributionFlags, attributionChainId); } if (DEBUG) Slog.d(TAG, "startOperation: no op for code " + code + " uid " + uid + " package " + packageName + " flags: " Loading @@ -3977,7 +3987,7 @@ public class AppOpsService extends IAppOpsService.Stub { if (!dryRun) { attributedOp.rejected(uidState.state, flags); scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag, flags, uidMode); flags, uidMode, startType, attributionFlags, attributionChainId); } return new SyncNotedAppOp(uidMode, code, attributionTag, packageName); } Loading @@ -3993,7 +4003,7 @@ public class AppOpsService extends IAppOpsService.Stub { if (!dryRun) { attributedOp.rejected(uidState.state, flags); scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag, flags, mode); flags, mode, startType, attributionFlags, attributionChainId); } return new SyncNotedAppOp(mode, code, attributionTag, packageName); } Loading @@ -4011,12 +4021,14 @@ public class AppOpsService extends IAppOpsService.Stub { attributedOp.started(clientId, proxyUid, proxyPackageName, proxyAttributionTag, uidState.state, flags, attributionFlags, attributionChainId); startType = START_TYPE_STARTED; } } catch (RemoteException e) { throw new RuntimeException(e); } scheduleOpStartedIfNeededLocked(code, uid, packageName, attributionTag, flags, isRestricted ? MODE_IGNORED : MODE_ALLOWED); isRestricted ? MODE_IGNORED : MODE_ALLOWED, startType, attributionFlags, attributionChainId); } } Loading Loading @@ -4187,7 +4199,9 @@ public class AppOpsService extends IAppOpsService.Stub { } private void scheduleOpStartedIfNeededLocked(int code, int uid, String pkgName, String attributionTag, @OpFlags int flags, @Mode int result) { String attributionTag, @OpFlags int flags, @Mode int result, @AppOpsManager.OnOpStartedListener.StartedType int startedType, @AttributionFlags int attributionFlags, int attributionChainId) { ArraySet<StartedCallback> dispatchedCallbacks = null; final int callbackListCount = mStartedWatchers.size(); for (int i = 0; i < callbackListCount; i++) { Loading @@ -4213,12 +4227,13 @@ public class AppOpsService extends IAppOpsService.Stub { mHandler.sendMessage(PooledLambda.obtainMessage( AppOpsService::notifyOpStarted, this, dispatchedCallbacks, code, uid, pkgName, attributionTag, flags, result)); result, startedType, attributionFlags, attributionChainId)); } private void notifyOpStarted(ArraySet<StartedCallback> callbacks, int code, int uid, String packageName, String attributionTag, @OpFlags int flags, @Mode int result) { @Mode int result, @AppOpsManager.OnOpStartedListener.StartedType int startedType, @AttributionFlags int attributionFlags, int attributionChainId) { final long identity = Binder.clearCallingIdentity(); try { final int callbackCount = callbacks.size(); Loading @@ -4226,7 +4241,7 @@ public class AppOpsService extends IAppOpsService.Stub { final StartedCallback callback = callbacks.valueAt(i); try { callback.mCallback.opStarted(code, uid, packageName, attributionTag, flags, result); result, startedType, attributionFlags, attributionChainId); } catch (RemoteException e) { /* do nothing */ } Loading
services/tests/servicestests/src/com/android/server/appop/AppOpsStartedWatcherTest.java +9 −3 Original line number Diff line number Diff line Loading @@ -64,12 +64,16 @@ public class AppOpsStartedWatcherTest { .times(1)).onOpStarted(eq(AppOpsManager.OP_FINE_LOCATION), eq(Process.myUid()), eq(getContext().getPackageName()), eq(getContext().getAttributionTag()), eq(AppOpsManager.OP_FLAG_SELF), eq(AppOpsManager.MODE_ALLOWED)); eq(AppOpsManager.MODE_ALLOWED), eq(OnOpStartedListener.START_TYPE_STARTED), eq(AppOpsManager.ATTRIBUTION_FLAGS_NONE), eq(AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE)); inOrder.verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS) .times(1)).onOpStarted(eq(AppOpsManager.OP_CAMERA), eq(Process.myUid()), eq(getContext().getPackageName()), eq(getContext().getAttributionTag()), eq(AppOpsManager.OP_FLAG_SELF), eq(AppOpsManager.MODE_ALLOWED)); eq(AppOpsManager.MODE_ALLOWED), eq(OnOpStartedListener.START_TYPE_STARTED), eq(AppOpsManager.ATTRIBUTION_FLAGS_NONE), eq(AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE)); // Stop watching appOpsManager.stopWatchingStarted(listener); Loading @@ -94,7 +98,9 @@ public class AppOpsStartedWatcherTest { .times(2)).onOpStarted(eq(AppOpsManager.OP_CAMERA), eq(Process.myUid()), eq(getContext().getPackageName()), eq(getContext().getAttributionTag()), eq(AppOpsManager.OP_FLAG_SELF), eq(AppOpsManager.MODE_ALLOWED)); eq(AppOpsManager.MODE_ALLOWED), eq(OnOpStartedListener.START_TYPE_STARTED), eq(AppOpsManager.ATTRIBUTION_FLAGS_NONE), eq(AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE)); verifyNoMoreInteractions(listener); // Finish up Loading