From fd5b145e6f934fba723d86e251955919413dcf44 Mon Sep 17 00:00:00 2001 From: Karishma Vakil Date: Sat, 12 Nov 2022 00:58:08 +0000 Subject: [PATCH] [Permissions Hub Refactor] Expose AppOpsManager#OnOpNotedListener and AppOpsManager#start/stopWatchingNoted as System API * Expose AppOpsManager#OnOpNotedListener and AppOpsManager#start/stopWatchingNoted as System API as we would like Permissions Hub UI to listen for app op notes to update its UI (which shows apps that have recently accessed permissions) * Current these methods take integer op as parameter which is not allowed for API (String op names need to be used instead), so we need to separate into different interfaces for internal/external use. Refactor existing usages of OnOpNotedListener to use OnOpNotedListenerInternal instead. Bug: 258257361 Test: atest CtsAppOpTestCases:AppOpsTest Change-Id: I3d87e6e33ec6fdafed4b3b2f6d029a8fb47bf355 --- core/api/system-current.txt | 6 ++ core/java/android/app/AppOpsManager.java | 81 ++++++++++++++++--- .../systemui/appops/AppOpsControllerImpl.java | 2 +- .../com/android/server/am/ActiveServices.java | 4 +- .../sensorprivacy/SensorPrivacyService.java | 2 +- .../server/appop/AppOpsNotedWatcherTest.java | 6 +- 6 files changed, 85 insertions(+), 16 deletions(-) diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 137af1a2ac34a..f050cc6935bb6 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -548,6 +548,8 @@ package android.app { method @Nullable public static String opToPermission(@NonNull String); method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setMode(@NonNull String, int, @Nullable String, int); method @RequiresPermission("android.permission.MANAGE_APP_OPS_MODES") public void setUidMode(@NonNull String, int, int); + method @RequiresPermission(value="android.permission.WATCH_APPOPS", conditional=true) public void startWatchingNoted(@NonNull String[], @NonNull android.app.AppOpsManager.OnOpNotedListener); + method public void stopWatchingNoted(@NonNull android.app.AppOpsManager.OnOpNotedListener); field public static final int HISTORY_FLAGS_ALL = 3; // 0x3 field public static final int HISTORY_FLAG_AGGREGATE = 1; // 0x1 field public static final int HISTORY_FLAG_DISCRETE = 2; // 0x2 @@ -735,6 +737,10 @@ package android.app { field @NonNull public static final android.os.Parcelable.Creator CREATOR; } + public static interface AppOpsManager.OnOpNotedListener { + method public void onOpNoted(@NonNull String, int, @NonNull String, @Nullable String, int, int); + } + public static final class AppOpsManager.OpEntry implements android.os.Parcelable { method public int describeContents(); method @NonNull public java.util.Map getAttributedOpEntries(); diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index d5879fb523ce1..563f6d4d95444 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -6844,22 +6844,52 @@ public class AppOpsManager { } /** - * Callback for notification of an op being noted. + * Callback for notification of an app-op being noted. * * @hide */ + @SystemApi public interface OnOpNotedListener { /** - * Called when an op was noted. - * @param code The op code. + * Called when an app-op is noted. + * + * @param op The operation that was noted. * @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 note. */ - void onOpNoted(int code, int uid, String packageName, String attributionTag, - @OpFlags int flags, @Mode int result); + void onOpNoted(@NonNull String op, int uid, @NonNull String packageName, + @Nullable String attributionTag, @OpFlags int flags, @Mode int result); + } + + /** + * Callback for notification of an app-op being noted to be used within platform code. + * + * This allows being notified using raw op codes instead of string op names. + * + * @hide + */ + public interface OnOpNotedInternalListener extends OnOpNotedListener { + /** + * Called when an app-op is noted. + * + * @param code The code of the operation that was noted. + * @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 note. + */ + void onOpNoted(int code, int uid, @NonNull String packageName, + @Nullable String attributionTag, @OpFlags int flags, @Mode int result); + + @Override + default void onOpNoted(@NonNull String op, int uid, @NonNull String packageName, + @Nullable String attributionTag, @OpFlags int flags, @Mode int result) { + onOpNoted(strOpToOp(op), uid, packageName, attributionTag, flags, result); + } } /** @@ -7654,13 +7684,42 @@ public class AppOpsManager { * @param ops The ops to watch. * @param callback Where to report changes. * - * @see #startWatchingActive(int[], OnOpActiveChangedListener) - * @see #startWatchingStarted(int[], OnOpStartedListener) * @see #stopWatchingNoted(OnOpNotedListener) * @see #noteOp(String, int, String, String, String) * * @hide */ + @SystemApi + @RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true) + public void startWatchingNoted(@NonNull String[] ops, @NonNull OnOpNotedListener callback) { + final int[] intOps = new int[ops.length]; + for (int i = 0; i < ops.length; i++) { + intOps[i] = strOpToOp(ops[i]); + } + startWatchingNoted(intOps, callback); + } + + /** + * 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 + * method allows registering a listener to be notified when an app op is noted. If + * an op is being noted 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. + * + *

If you don't hold the {@link android.Manifest.permission#WATCH_APPOPS} permission + * you can watch changes only for your UID. + * + * This allows observing noted ops by their raw op codes instead of string op names. + * + * @param ops The ops to watch. + * @param callback Where to report changes. + * + * @see #startWatchingActive(int[], OnOpActiveChangedListener) + * @see #startWatchingStarted(int[], OnOpStartedListener) + * @see #startWatchingNoted(String[], OnOpNotedListener) + * + * @hide + */ @RequiresPermission(value=Manifest.permission.WATCH_APPOPS, conditional=true) public void startWatchingNoted(@NonNull int[] ops, @NonNull OnOpNotedListener callback) { IAppOpsNotedCallback cb; @@ -7673,7 +7732,10 @@ public class AppOpsManager { @Override public void opNoted(int op, int uid, String packageName, String attributionTag, int flags, int mode) { - callback.onOpNoted(op, uid, packageName, attributionTag, flags, mode); + if (sAppOpInfos[op].name != null) { + callback.onOpNoted(sAppOpInfos[op].name, uid, packageName, attributionTag, + flags, mode); + } } }; mNotedWatchers.put(callback, cb); @@ -7689,11 +7751,12 @@ public class AppOpsManager { * Stop watching for noted app ops. An app op may be immediate or long running. * Unregistering a non-registered callback has no effect. * - * @see #startWatchingNoted(int[], OnOpNotedListener) + * @see #startWatchingNoted(String[], OnOpNotedListener) * @see #noteOp(String, int, String, String, String) * * @hide */ + @SystemApi public void stopWatchingNoted(@NonNull OnOpNotedListener callback) { synchronized (mNotedWatchers) { final IAppOpsNotedCallback cb = mNotedWatchers.remove(callback); diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java index 6785a43600f24..9708d9a02edc8 100644 --- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java @@ -64,7 +64,7 @@ import javax.inject.Inject; @SysUISingleton public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsController, AppOpsManager.OnOpActiveChangedListener, - AppOpsManager.OnOpNotedListener, IndividualSensorPrivacyController.Callback, + AppOpsManager.OnOpNotedInternalListener, IndividualSensorPrivacyController.Callback, Dumpable { // This is the minimum time that we will keep AppOps that are noted on record. If multiple diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 35b3db8a63325..3c88e5c07daaa 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -2787,8 +2787,8 @@ public final class ActiveServices { } } - private final AppOpsManager.OnOpNotedListener mOpNotedCallback = - new AppOpsManager.OnOpNotedListener() { + private final AppOpsManager.OnOpNotedInternalListener mOpNotedCallback = + new AppOpsManager.OnOpNotedInternalListener() { @Override public void onOpNoted(int op, int uid, String pkgName, String attributionTag, int flags, int result) { diff --git a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java index ab35dc86b9617..6d391779ea18d 100644 --- a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java +++ b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java @@ -223,7 +223,7 @@ public final class SensorPrivacyService extends SystemService { } class SensorPrivacyServiceImpl extends ISensorPrivacyManager.Stub implements - AppOpsManager.OnOpNotedListener, AppOpsManager.OnOpStartedListener, + AppOpsManager.OnOpNotedInternalListener, AppOpsManager.OnOpStartedListener, IBinder.DeathRecipient, UserManagerInternal.UserRestrictionsListener { private final SensorPrivacyHandler mHandler; diff --git a/services/tests/servicestests/src/com/android/server/appop/AppOpsNotedWatcherTest.java b/services/tests/servicestests/src/com/android/server/appop/AppOpsNotedWatcherTest.java index 663017890b0c6..47fdcb6333527 100644 --- a/services/tests/servicestests/src/com/android/server/appop/AppOpsNotedWatcherTest.java +++ b/services/tests/servicestests/src/com/android/server/appop/AppOpsNotedWatcherTest.java @@ -64,12 +64,12 @@ public class AppOpsNotedWatcherTest { // Verify that we got called for the ops being noted final InOrder inOrder = inOrder(listener); inOrder.verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS) - .times(1)).onOpNoted(eq(AppOpsManager.OP_FINE_LOCATION), + .times(1)).onOpNoted(eq(AppOpsManager.OPSTR_FINE_LOCATION), eq(Process.myUid()), eq(getContext().getPackageName()), eq(getContext().getAttributionTag()), eq(AppOpsManager.OP_FLAG_SELF), eq(AppOpsManager.MODE_ALLOWED)); inOrder.verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS) - .times(1)).onOpNoted(eq(AppOpsManager.OP_CAMERA), + .times(1)).onOpNoted(eq(AppOpsManager.OPSTR_CAMERA), eq(Process.myUid()), eq(getContext().getPackageName()), eq(getContext().getAttributionTag()), eq(AppOpsManager.OP_FLAG_SELF), eq(AppOpsManager.MODE_ALLOWED)); @@ -94,7 +94,7 @@ public class AppOpsNotedWatcherTest { // Verify it's watched again verify(listener, timeout(NOTIFICATION_TIMEOUT_MILLIS) - .times(2)).onOpNoted(eq(AppOpsManager.OP_FINE_LOCATION), + .times(2)).onOpNoted(eq(AppOpsManager.OPSTR_FINE_LOCATION), eq(Process.myUid()), eq(getContext().getPackageName()), eq(getContext().getAttributionTag()), eq(AppOpsManager.OP_FLAG_SELF), eq(AppOpsManager.MODE_ALLOWED));