DO NOT MERGE Correctly reset cross-profile app-op

Delegate the resetting of the INTERACT_ACROSS_PROFILES app-op to
DevicePolicyManager, which knows whether it should be pre-granted and
knows to apply it equally across all users in the profile group.

Further unit tests for DevicePolicyManagerInternal will be added in
b/175440570 when we have the better infra for that.

The CrossProfileAppsServiceImpl changes look more complex than they are.
They consist of the following:
- Inclusive language changes to 'allowlist'
- Static imports of permissions to improve readability
- Previously, the setInteractAcrossProfilesAppOp method would set the
app-op for every user within the profile group of the 'calling user'.
However, given that we are now exposing this as a server-side internal
API where we need to pass in a user ID (from AppOpsService), we don't
necessarily have the guarantee that the 'calling user' is in the same
profile group. So we split it up: the client-side API and AIDL API still
set the app-op for the calling profile group, whereas the internal API
sets the app-op for every user within the profile group of the provided
user. The changes simply abstract away references to the 'calling user
ID'.

Fixes: 166561076
Bug: 175440570
Test: atest services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java --verbose -c
Test: manual
Change-Id: I2181fe66022aaf6c3e6d784c0569d2f41ab66537
(cherry picked from commit d004f41188ba39afd2c75e4e7dede6b755a9d752)
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index 8f5dbc4..62ac84b 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -222,4 +222,16 @@
      * Returns the profile owner component for the given user, or {@code null} if there is not one.
      */
     public abstract ComponentName getProfileOwnerAsUser(int userHandle);
+
+    /**
+     * Returns whether this class supports being deferred the responsibility for resetting the given
+     * op.
+     */
+    public abstract boolean supportsResetOp(int op);
+
+    /**
+     * Resets the given op across the profile group of the given user for the given package. Assumes
+     * {@link #supportsResetOp(int)} is true.
+     */
+    public abstract void resetOp(int op, String packageName, @UserIdInt int userId);
 }
diff --git a/core/java/android/content/pm/CrossProfileAppsInternal.java b/core/java/android/content/pm/CrossProfileAppsInternal.java
index 16a749f..255aeac 100644
--- a/core/java/android/content/pm/CrossProfileAppsInternal.java
+++ b/core/java/android/content/pm/CrossProfileAppsInternal.java
@@ -17,6 +17,7 @@
 package android.content.pm;
 
 import android.annotation.UserIdInt;
+import android.app.AppOpsManager.Mode;
 import android.os.UserHandle;
 
 import java.util.List;
@@ -62,4 +63,14 @@
      */
     public abstract List<UserHandle> getTargetUserProfiles(
             String packageName, @UserIdInt int userId);
+
+    /**
+     * Sets the app-op for {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES} that is
+     * configurable by users in Settings. This configures it for the profile group of the given
+     * user.
+     *
+     * @see CrossProfileApps#setInteractAcrossProfilesAppOp(String, int)
+     */
+    public abstract void setInteractAcrossProfilesAppOp(
+            String packageName, @Mode int newMode, @UserIdInt int userId);
 }
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 1b12dc7..4e194e2 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -75,6 +75,7 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.app.AppGlobals;
@@ -89,6 +90,7 @@
 import android.app.AsyncNotedAppOp;
 import android.app.RuntimeAppOpAccessMessage;
 import android.app.SyncNotedAppOp;
+import android.app.admin.DevicePolicyManagerInternal;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -270,6 +272,8 @@
 
     private final AppOpsManagerInternalImpl mAppOpsManagerInternal
             = new AppOpsManagerInternalImpl();
+    @Nullable private final DevicePolicyManagerInternal dpmi =
+            LocalServices.getService(DevicePolicyManagerInternal.class);
 
     /**
      * Registered callbacks, called from {@link #collectAsyncNotedOp}.
@@ -2675,6 +2679,10 @@
                     Ops pkgOps = ent.getValue();
                     for (int j=pkgOps.size()-1; j>=0; j--) {
                         Op curOp = pkgOps.valueAt(j);
+                        if (shouldDeferResetOpToDpm(curOp.op)) {
+                            deferResetOpToDpm(curOp.op, reqPackageName, reqUserId);
+                            continue;
+                        }
                         if (AppOpsManager.opAllowsReset(curOp.op)
                                 && curOp.mode != AppOpsManager.opToDefaultMode(curOp.op)) {
                             int previousMode = curOp.mode;
@@ -2724,16 +2732,27 @@
             }
         }
 
-        if (allChanges != null) {
-            int numChanges = allChanges.size();
-            for (int i = 0; i < numChanges; i++) {
-                ChangeRec change = allChanges.get(i);
-                notifyOpChangedSync(change.op, change.uid, change.pkg,
-                        AppOpsManager.opToDefaultMode(change.op), change.previous_mode);
-            }
+        int numChanges = allChanges.size();
+        for (int i = 0; i < numChanges; i++) {
+            ChangeRec change = allChanges.get(i);
+            notifyOpChangedSync(change.op, change.uid, change.pkg,
+                    AppOpsManager.opToDefaultMode(change.op), change.previous_mode);
         }
     }
 
+    private boolean shouldDeferResetOpToDpm(int op) {
+        // TODO(b/174582385): avoid special-casing app-op resets by migrating app-op permission
+        //  pre-grants to a role-based mechanism or another general-purpose mechanism.
+        return dpmi != null && dpmi.supportsResetOp(op);
+    }
+
+    /** Assumes {@link #shouldDeferResetOpToDpm(int)} is true. */
+    private void deferResetOpToDpm(int op, String packageName, @UserIdInt int userId) {
+        // TODO(b/174582385): avoid special-casing app-op resets by migrating app-op permission
+        //  pre-grants to a role-based mechanism or another general-purpose mechanism.
+        dpmi.resetOp(op, packageName, userId);
+    }
+
     private void evalAllForegroundOpsLocked() {
         for (int uidi = mUidStates.size() - 1; uidi >= 0; uidi--) {
             final UidState uidState = mUidStates.valueAt(uidi);
diff --git a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
index 617f687..3e9e45e 100644
--- a/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
+++ b/services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java
@@ -14,14 +14,17 @@
  * limitations under the License.
  */
 package com.android.server.pm;
-
+import static android.Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES;
+import static android.Manifest.permission.INTERACT_ACROSS_PROFILES;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.Manifest.permission.MANAGE_APP_OPS_MODES;
 import static android.app.AppOpsManager.OP_INTERACT_ACROSS_PROFILES;
 import static android.content.Intent.FLAG_RECEIVER_REGISTERED_ONLY;
 import static android.content.pm.CrossProfileApps.ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED;
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
 
-import android.Manifest;
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
@@ -31,7 +34,6 @@
 import android.app.AppOpsManager.Mode;
 import android.app.IApplicationThread;
 import android.app.admin.DevicePolicyEventLogger;
-import android.app.admin.DevicePolicyManager;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.content.ComponentName;
 import android.content.Context;
@@ -154,15 +156,15 @@
             if (callerUserId != userId) {
                 final int permissionFlag =  PermissionChecker.checkPermissionForPreflight(
                         mContext,
-                        android.Manifest.permission.INTERACT_ACROSS_PROFILES,
+                        INTERACT_ACROSS_PROFILES,
                         callingPid,
                         callingUid,
                         callingPackage);
                 if (permissionFlag != PermissionChecker.PERMISSION_GRANTED
                         || !isSameProfileGroup(callerUserId, userId)) {
                     throw new SecurityException("Attempt to launch activity without required "
-                            + android.Manifest.permission.INTERACT_ACROSS_PROFILES + " permission"
-                            + " or target user is not in the same profile group.");
+                            + INTERACT_ACROSS_PROFILES
+                            + " permission or target user is not in the same profile group.");
                 }
             }
             launchIntent.setComponent(component);
@@ -217,8 +219,8 @@
         if (callerUserId != userId) {
             if (!hasCallerGotInteractAcrossProfilesPermission(callingPackage)) {
                 throw new SecurityException("Attempt to launch activity without required "
-                        + android.Manifest.permission.INTERACT_ACROSS_PROFILES + " permission"
-                        + " or target user is not in the same profile group.");
+                        + INTERACT_ACROSS_PROFILES
+                        + " permission or target user is not in the same profile group.");
             }
         }
 
@@ -294,13 +296,13 @@
                 callingPackage, mInjector.getCallingUid(), mInjector.getCallingPid());
     }
 
-    private boolean isCrossProfilePackageWhitelisted(String packageName) {
+    private boolean isCrossProfilePackageAllowlisted(String packageName) {
         return mInjector.withCleanCallingIdentity(() ->
                 mInjector.getDevicePolicyManagerInternal()
                         .getAllCrossProfilePackages().contains(packageName));
     }
 
-    private boolean isCrossProfilePackageWhitelistedByDefault(String packageName) {
+    private boolean isCrossProfilePackageAllowlistedByDefault(String packageName) {
         return mInjector.withCleanCallingIdentity(() ->
                 mInjector.getDevicePolicyManagerInternal()
                         .getDefaultCrossProfilePackages().contains(packageName));
@@ -388,32 +390,36 @@
     /**
      * See {@link android.content.pm.CrossProfileApps#setInteractAcrossProfilesAppOp(String, int)}.
      *
-     * <p>Logs metrics. Use {@link #setInteractAcrossProfilesAppOpUnchecked(String, int, boolean)}
-     * to avoid permission checks or to specify not to log metrics.
+     * <p>Use {@link #setInteractAcrossProfilesAppOpUnchecked(String, int, int)} to avoid permission
+     * checks.
      */
     @Override
     public void setInteractAcrossProfilesAppOp(String packageName, @Mode int newMode) {
+        setInteractAcrossProfilesAppOp(packageName, newMode, mInjector.getCallingUserId());
+    }
+
+    private void setInteractAcrossProfilesAppOp(
+            String packageName, @Mode int newMode, @UserIdInt int userId) {
         final int callingUid = mInjector.getCallingUid();
-        if (!isPermissionGranted(Manifest.permission.INTERACT_ACROSS_USERS_FULL, callingUid)
-                && !isPermissionGranted(Manifest.permission.INTERACT_ACROSS_USERS, callingUid)) {
+        if (!isPermissionGranted(INTERACT_ACROSS_USERS_FULL, callingUid)
+                && !isPermissionGranted(INTERACT_ACROSS_USERS, callingUid)) {
             throw new SecurityException(
                     "INTERACT_ACROSS_USERS or INTERACT_ACROSS_USERS_FULL is required to set the"
                             + " app-op for interacting across profiles.");
         }
-        if (!isPermissionGranted(Manifest.permission.MANAGE_APP_OPS_MODES, callingUid)
-                && !isPermissionGranted(
-                        Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES, callingUid)) {
+        if (!isPermissionGranted(MANAGE_APP_OPS_MODES, callingUid)
+                && !isPermissionGranted(CONFIGURE_INTERACT_ACROSS_PROFILES, callingUid)) {
             throw new SecurityException(
                     "MANAGE_APP_OPS_MODES or CONFIGURE_INTERACT_ACROSS_PROFILES is required to set"
                             + " the app-op for interacting across profiles.");
         }
-        setInteractAcrossProfilesAppOpUnchecked(packageName, newMode, /* logMetrics= */ true);
+        setInteractAcrossProfilesAppOpUnchecked(packageName, newMode, userId);
     }
 
     private void setInteractAcrossProfilesAppOpUnchecked(
-            String packageName, @Mode int newMode, boolean logMetrics) {
+            String packageName, @Mode int newMode, @UserIdInt int userId) {
         if (newMode == AppOpsManager.MODE_ALLOWED
-                && !canConfigureInteractAcrossProfiles(packageName)) {
+                && !canConfigureInteractAcrossProfiles(packageName, userId)) {
             // The user should not be prompted for apps that cannot request to interact across
             // profiles. However, we return early here if required to avoid race conditions.
             Slog.e(TAG, "Tried to turn on the appop for interacting across profiles for invalid"
@@ -421,56 +427,57 @@
             return;
         }
         final int[] profileIds =
-                mInjector.getUserManager()
-                        .getProfileIds(mInjector.getCallingUserId(), /* enabledOnly= */ false);
+                mInjector.getUserManager().getProfileIds(userId, /* enabledOnly= */ false);
         for (int profileId : profileIds) {
             if (!isPackageInstalled(packageName, profileId)) {
                 continue;
             }
-            setInteractAcrossProfilesAppOpForUser(packageName, newMode, profileId, logMetrics);
+            // Only log once per profile group by checking against the user ID.
+            setInteractAcrossProfilesAppOpForProfile(
+                    packageName, newMode, profileId, /* logMetrics= */ profileId == userId);
         }
     }
 
+    /**
+     * Returns whether the given package name is installed in the given user ID. The calling UID is
+     * used as the filter calling UID, as described at {@link PackageManagerInternal#getPackageInfo(
+     * String, int, int, int)}.
+     */
     private boolean isPackageInstalled(String packageName, @UserIdInt int userId) {
-        final int callingUid = mInjector.getCallingUid();
         return mInjector.withCleanCallingIdentity(() -> {
+            final int flags = MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE;
             final PackageInfo info =
                     mInjector.getPackageManagerInternal()
-                            .getPackageInfo(
-                                    packageName,
-                                    MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
-                                    callingUid,
-                                    userId);
+                            .getPackageInfo(packageName, flags, mInjector.getCallingUid(), userId);
             return info != null;
         });
     }
 
-    private void setInteractAcrossProfilesAppOpForUser(
-            String packageName, @Mode int newMode, @UserIdInt int userId, boolean logMetrics) {
+    private void setInteractAcrossProfilesAppOpForProfile(
+            String packageName, @Mode int newMode, @UserIdInt int profileId, boolean logMetrics) {
         try {
-            setInteractAcrossProfilesAppOpForUserOrThrow(packageName, newMode, userId, logMetrics);
+            setInteractAcrossProfilesAppOpForProfileOrThrow(
+                    packageName, newMode, profileId, logMetrics);
         } catch (PackageManager.NameNotFoundException e) {
-            Slog.e(TAG, "Missing package " + packageName + " on user ID " + userId, e);
+            Slog.e(TAG, "Missing package " + packageName + " on profile user ID " + profileId, e);
         }
     }
 
-    private void setInteractAcrossProfilesAppOpForUserOrThrow(
-            String packageName, @Mode int newMode, @UserIdInt int userId, boolean logMetrics)
+    private void setInteractAcrossProfilesAppOpForProfileOrThrow(
+            String packageName, @Mode int newMode, @UserIdInt int profileId, boolean logMetrics)
             throws PackageManager.NameNotFoundException {
         final int uid = mInjector.getPackageManager()
-                .getPackageUidAsUser(packageName, /* flags= */ 0, userId);
+                .getPackageUidAsUser(packageName, /* flags= */ 0, profileId);
         if (currentModeEquals(newMode, packageName, uid)) {
             Slog.i(TAG, "Attempt to set mode to existing value of " + newMode + " for "
-                    + packageName + " on user ID " + userId);
+                    + packageName + " on profile user ID " + profileId);
             return;
         }
 
         final boolean hadPermission = hasInteractAcrossProfilesPermission(
                 packageName, uid, PermissionChecker.PID_UNKNOWN);
 
-        final int callingUid = mInjector.getCallingUid();
-        if (isPermissionGranted(
-                Manifest.permission.CONFIGURE_INTERACT_ACROSS_PROFILES, callingUid)) {
+        if (isPermissionGranted(CONFIGURE_INTERACT_ACROSS_PROFILES, mInjector.getCallingUid())) {
             // Clear calling identity since the CONFIGURE_INTERACT_ACROSS_PROFILES permission allows
             // this particular app-op to be modified without the broader app-op permissions.
             mInjector.withCleanCallingIdentity(() ->
@@ -483,16 +490,15 @@
         // Kill the UID before sending the broadcast to ensure that apps can be informed when
         // their app-op has been revoked.
         maybeKillUid(packageName, uid, hadPermission);
-        sendCanInteractAcrossProfilesChangedBroadcast(packageName, uid, UserHandle.of(userId));
-        maybeLogSetInteractAcrossProfilesAppOp(packageName, newMode, userId, logMetrics, uid);
+        sendCanInteractAcrossProfilesChangedBroadcast(packageName, uid, UserHandle.of(profileId));
+        maybeLogSetInteractAcrossProfilesAppOp(packageName, newMode, logMetrics, uid);
     }
 
     /**
      * Kills the process represented by the given UID if it has lost the permission to
      * interact across profiles.
      */
-    private void maybeKillUid(
-            String packageName, int uid, boolean hadPermission) {
+    private void maybeKillUid(String packageName, int uid, boolean hadPermission) {
         if (!hadPermission) {
             return;
         }
@@ -503,18 +509,10 @@
     }
 
     private void maybeLogSetInteractAcrossProfilesAppOp(
-            String packageName,
-            @Mode int newMode,
-            @UserIdInt int userId,
-            boolean logMetrics,
-            int uid) {
+            String packageName, @Mode int newMode, boolean logMetrics, int uid) {
         if (!logMetrics) {
             return;
         }
-        if (userId != mInjector.getCallingUserId()) {
-            // Only log once per profile group by checking for the calling user ID.
-            return;
-        }
         DevicePolicyEventLogger
                 .createEvent(DevicePolicyEnums.SET_INTERACT_ACROSS_PROFILES_APP_OP)
                 .setStrings(packageName)
@@ -529,8 +527,7 @@
      * any necessary permission checks.
      */
     private boolean currentModeEquals(@Mode int otherMode, String packageName, int uid) {
-        final String op =
-                AppOpsManager.permissionToOp(Manifest.permission.INTERACT_ACROSS_PROFILES);
+        final String op = AppOpsManager.permissionToOp(INTERACT_ACROSS_PROFILES);
         return mInjector.withCleanCallingIdentity(() -> otherMode
                 == mInjector.getAppOpsManager().unsafeCheckOpNoThrow(op, uid, packageName));
     }
@@ -562,37 +559,49 @@
 
     @Override
     public boolean canConfigureInteractAcrossProfiles(String packageName) {
-        if (!canUserAttemptToConfigureInteractAcrossProfiles(packageName)) {
+        return canConfigureInteractAcrossProfiles(packageName, mInjector.getCallingUserId());
+    }
+
+    private boolean canConfigureInteractAcrossProfiles(String packageName, @UserIdInt int userId) {
+        if (!canUserAttemptToConfigureInteractAcrossProfiles(packageName, userId)) {
             return false;
         }
-        if (!hasOtherProfileWithPackageInstalled(packageName, mInjector.getCallingUserId())) {
+        if (!hasOtherProfileWithPackageInstalled(packageName, userId)) {
             return false;
         }
         if (!hasRequestedAppOpPermission(
                 AppOpsManager.opToPermission(OP_INTERACT_ACROSS_PROFILES), packageName)) {
             return false;
         }
-        return isCrossProfilePackageWhitelisted(packageName);
+        return isCrossProfilePackageAllowlisted(packageName);
     }
 
     @Override
     public boolean canUserAttemptToConfigureInteractAcrossProfiles(String packageName) {
-        final int[] profileIds = mInjector.getUserManager().getProfileIds(
-                mInjector.getCallingUserId(), /* enabledOnly= */ false);
+        return canUserAttemptToConfigureInteractAcrossProfiles(
+                packageName, mInjector.getCallingUserId());
+    }
+
+    private boolean canUserAttemptToConfigureInteractAcrossProfiles(
+            String packageName, @UserIdInt int userId) {
+        final int[] profileIds =
+                mInjector.getUserManager().getProfileIds(userId, /* enabledOnly= */ false);
         if (profileIds.length < 2) {
             return false;
         }
         if (isProfileOwner(packageName, profileIds)) {
             return false;
         }
-        return hasRequestedAppOpPermission(
-                AppOpsManager.opToPermission(OP_INTERACT_ACROSS_PROFILES), packageName)
-                && !isPlatformSignedAppWithNonUserConfigurablePermission(packageName, profileIds);
+        if (!hasRequestedAppOpPermission(
+                AppOpsManager.opToPermission(OP_INTERACT_ACROSS_PROFILES), packageName)) {
+            return false;
+        }
+        return !isPlatformSignedAppWithNonUserConfigurablePermission(packageName, profileIds);
     }
 
     private boolean isPlatformSignedAppWithNonUserConfigurablePermission(
             String packageName, int[] profileIds) {
-        return !isCrossProfilePackageWhitelistedByDefault(packageName)
+        return !isCrossProfilePackageAllowlistedByDefault(packageName)
                 && isPlatformSignedAppWithAutomaticProfilesPermission(packageName, profileIds);
     }
 
@@ -610,7 +619,7 @@
             if (uid == -1) {
                 continue;
             }
-            if (isPermissionGranted(Manifest.permission.INTERACT_ACROSS_PROFILES, uid)) {
+            if (isPermissionGranted(INTERACT_ACROSS_PROFILES, uid)) {
                 return true;
             }
         }
@@ -642,7 +651,7 @@
             return;
         }
         final String op =
-                AppOpsManager.permissionToOp(Manifest.permission.INTERACT_ACROSS_PROFILES);
+                AppOpsManager.permissionToOp(INTERACT_ACROSS_PROFILES);
         setInteractAcrossProfilesAppOp(packageName, AppOpsManager.opToDefaultMode(op));
     }
 
@@ -650,7 +659,7 @@
     public void clearInteractAcrossProfilesAppOps() {
         final int defaultMode =
                 AppOpsManager.opToDefaultMode(
-                        AppOpsManager.permissionToOp(Manifest.permission.INTERACT_ACROSS_PROFILES));
+                        AppOpsManager.permissionToOp(INTERACT_ACROSS_PROFILES));
         findAllPackageNames()
                 .forEach(packageName -> setInteractAcrossProfilesAppOp(packageName, defaultMode));
     }
@@ -695,17 +704,13 @@
     }
 
     private boolean hasInteractAcrossProfilesPermission(String packageName, int uid, int pid) {
-        if (isPermissionGranted(Manifest.permission.INTERACT_ACROSS_USERS_FULL, uid)
-                || isPermissionGranted(Manifest.permission.INTERACT_ACROSS_USERS, uid)) {
+        if (isPermissionGranted(INTERACT_ACROSS_USERS_FULL, uid)
+                || isPermissionGranted(INTERACT_ACROSS_USERS, uid)) {
             return true;
         }
         return PermissionChecker.PERMISSION_GRANTED
                 == PermissionChecker.checkPermissionForPreflight(
-                        mContext,
-                        Manifest.permission.INTERACT_ACROSS_PROFILES,
-                        pid,
-                        uid,
-                        packageName);
+                        mContext, INTERACT_ACROSS_PROFILES, pid, uid, packageName);
     }
 
     private boolean isProfileOwner(String packageName, int[] userIds) {
@@ -898,5 +903,12 @@
         public List<UserHandle> getTargetUserProfiles(String packageName, int userId) {
             return getTargetUserProfilesUnchecked(packageName, userId);
         }
+
+        @Override
+        public void setInteractAcrossProfilesAppOp(
+                String packageName, int newMode, @UserIdInt int userId) {
+            CrossProfileAppsServiceImpl.this.setInteractAcrossProfilesAppOpUnchecked(
+                    packageName, newMode, userId);
+        }
     }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index ce3cdea..b8a20ed 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -125,6 +125,7 @@
 import android.app.AlarmManager;
 import android.app.AppGlobals;
 import android.app.AppOpsManager;
+import android.app.AppOpsManager.Mode;
 import android.app.BroadcastOptions;
 import android.app.IActivityManager;
 import android.app.IActivityTaskManager;
@@ -12816,6 +12817,28 @@
         public ComponentName getProfileOwnerAsUser(int userHandle) {
             return DevicePolicyManagerService.this.getProfileOwnerAsUser(userHandle);
         }
+
+        @Override
+        public boolean supportsResetOp(int op) {
+            return op == AppOpsManager.OP_INTERACT_ACROSS_PROFILES
+                    && LocalServices.getService(CrossProfileAppsInternal.class) != null;
+        }
+
+        @Override
+        public void resetOp(int op, String packageName, @UserIdInt int userId) {
+            if (op != AppOpsManager.OP_INTERACT_ACROSS_PROFILES) {
+                throw new IllegalArgumentException("Unsupported op for DPM reset: " + op);
+            }
+            LocalServices.getService(CrossProfileAppsInternal.class)
+                    .setInteractAcrossProfilesAppOp(
+                            packageName, findInteractAcrossProfilesResetMode(packageName), userId);
+        }
+
+        private @Mode int findInteractAcrossProfilesResetMode(String packageName) {
+            return getDefaultCrossProfilePackages().contains(packageName)
+                    ? AppOpsManager.MODE_ALLOWED
+                    : AppOpsManager.opToDefaultMode(AppOpsManager.OP_INTERACT_ACROSS_PROFILES);
+        }
     }
 
     private Intent createShowAdminSupportIntent(ComponentName admin, int userId) {
diff --git a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
index 4b25890..adf892a 100644
--- a/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
+++ b/services/robotests/src/com/android/server/pm/CrossProfileAppsServiceImplRoboTest.java
@@ -37,7 +37,6 @@
 import android.app.ActivityManagerInternal;
 import android.app.AppOpsManager;
 import android.app.AppOpsManager.Mode;
-import android.app.admin.DevicePolicyManager;
 import android.app.admin.DevicePolicyManagerInternal;
 import android.content.ComponentName;
 import android.content.ContextWrapper;
@@ -94,12 +93,15 @@
     private static final int CALLING_PID = 1000;
     private static final String CROSS_PROFILE_APP_PACKAGE_NAME =
             "com.android.server.pm.crossprofileappsserviceimplrobotest.crossprofileapp";
-    private static final int PERSONAL_PROFILE_USER_ID = 0;
+    @UserIdInt private static final int PERSONAL_PROFILE_USER_ID = 0;
     private static final int PERSONAL_PROFILE_UID = 2222;
-    private static final int WORK_PROFILE_USER_ID = 10;
+    @UserIdInt private static final int WORK_PROFILE_USER_ID = 10;
     private static final int WORK_PROFILE_UID = 3333;
     private static final int OTHER_PROFILE_WITHOUT_CROSS_PROFILE_APP_USER_ID = 20;
-    private static final int OUTSIDE_PROFILE_GROUP_USER_ID = 30;
+    @UserIdInt private static final int OTHER_PROFILE_GROUP_USER_ID = 30;
+    private static final int OTHER_PROFILE_GROUP_UID = 4444;
+    @UserIdInt private static final int OTHER_PROFILE_GROUP_2_USER_ID = 31;
+    private static final int OTHER_PROFILE_GROUP_2_UID = 5555;
 
     private final ContextWrapper mContext = ApplicationProvider.getApplicationContext();
     private final UserManager mUserManager = mContext.getSystemService(UserManager.class);
@@ -138,6 +140,10 @@
         mockCrossProfileAppInstalledOnProfile(
                 packageInfo, PERSONAL_PROFILE_USER_ID, PERSONAL_PROFILE_UID);
         mockCrossProfileAppInstalledOnProfile(packageInfo, WORK_PROFILE_USER_ID, WORK_PROFILE_UID);
+        mockCrossProfileAppInstalledOnProfile(
+                packageInfo, OTHER_PROFILE_GROUP_USER_ID, OTHER_PROFILE_GROUP_UID);
+        mockCrossProfileAppInstalledOnProfile(
+                packageInfo, OTHER_PROFILE_GROUP_2_USER_ID, OTHER_PROFILE_GROUP_2_UID);
     }
 
     private void mockCrossProfileAppInstalledOnProfile(
@@ -200,16 +206,22 @@
 
     @Before
     public void setUpCrossProfileAppUidsAndPackageNames() {
+        setUpCrossProfileAppUidAndPackageName(
+                PERSONAL_PROFILE_UID, PERSONAL_PROFILE_USER_ID);
+        setUpCrossProfileAppUidAndPackageName(
+                WORK_PROFILE_UID, WORK_PROFILE_USER_ID);
+        setUpCrossProfileAppUidAndPackageName(
+                OTHER_PROFILE_GROUP_UID, OTHER_PROFILE_GROUP_USER_ID);
+        setUpCrossProfileAppUidAndPackageName(
+                OTHER_PROFILE_GROUP_2_UID, OTHER_PROFILE_GROUP_2_USER_ID);
+    }
+
+    private void setUpCrossProfileAppUidAndPackageName(int uid, @UserIdInt int userId) {
         ShadowApplicationPackageManager.setPackageUidAsUser(
-                CROSS_PROFILE_APP_PACKAGE_NAME, PERSONAL_PROFILE_UID, PERSONAL_PROFILE_USER_ID);
-        ShadowApplicationPackageManager.setPackageUidAsUser(
-                CROSS_PROFILE_APP_PACKAGE_NAME, WORK_PROFILE_UID, WORK_PROFILE_USER_ID);
-        when(mPackageManagerInternal.getPackageUidInternal(
-                CROSS_PROFILE_APP_PACKAGE_NAME, /* flags= */ 0, PERSONAL_PROFILE_USER_ID))
-                .thenReturn(PERSONAL_PROFILE_UID);
-        when(mPackageManagerInternal.getPackageUidInternal(
-                CROSS_PROFILE_APP_PACKAGE_NAME, /* flags= */ 0, WORK_PROFILE_USER_ID))
-                .thenReturn(WORK_PROFILE_UID);
+                CROSS_PROFILE_APP_PACKAGE_NAME, uid, userId);
+        when(mPackageManagerInternal
+                .getPackageUid(CROSS_PROFILE_APP_PACKAGE_NAME, /* flags= */ 0, userId))
+                .thenReturn(uid);
     }
 
     @Before
@@ -229,7 +241,9 @@
                 PERSONAL_PROFILE_USER_ID,
                 WORK_PROFILE_USER_ID,
                 OTHER_PROFILE_WITHOUT_CROSS_PROFILE_APP_USER_ID);
-        shadowUserManager.addProfileIds(OUTSIDE_PROFILE_GROUP_USER_ID);
+        shadowUserManager.addProfileIds(
+                OTHER_PROFILE_GROUP_USER_ID,
+                OTHER_PROFILE_GROUP_2_USER_ID);
     }
 
     @Before
@@ -239,6 +253,8 @@
         final int defaultMode = AppOpsManager.opToDefaultMode(OP_INTERACT_ACROSS_PROFILES);
         explicitlySetInteractAcrossProfilesAppOp(PERSONAL_PROFILE_UID, defaultMode);
         explicitlySetInteractAcrossProfilesAppOp(WORK_PROFILE_UID, defaultMode);
+        explicitlySetInteractAcrossProfilesAppOp(OTHER_PROFILE_GROUP_UID, defaultMode);
+        explicitlySetInteractAcrossProfilesAppOp(OTHER_PROFILE_GROUP_2_UID, defaultMode);
     }
 
     @Test
@@ -422,6 +438,27 @@
     }
 
     @Test
+    public void setInteractAcrossProfilesAppOp_userToSetInDifferentProfileGroupToCaller_setsAppOp() {
+        mCrossProfileAppsServiceImpl.getLocalService().setInteractAcrossProfilesAppOp(
+                CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED, OTHER_PROFILE_GROUP_USER_ID);
+        assertThat(getCrossProfileAppOp(OTHER_PROFILE_GROUP_UID)).isEqualTo(MODE_ALLOWED);
+    }
+
+    @Test
+    public void setInteractAcrossProfilesAppOp_userToSetInDifferentProfileGroupToCaller_setsAppOpOnOtherProfile() {
+        mCrossProfileAppsServiceImpl.getLocalService().setInteractAcrossProfilesAppOp(
+                CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED, OTHER_PROFILE_GROUP_USER_ID);
+        assertThat(getCrossProfileAppOp(OTHER_PROFILE_GROUP_2_UID)).isEqualTo(MODE_ALLOWED);
+    }
+
+    @Test
+    public void setInteractAcrossProfilesAppOp_userToSetInDifferentProfileGroupToCaller_doesNotSetCallerAppOp() {
+        mCrossProfileAppsServiceImpl.getLocalService().setInteractAcrossProfilesAppOp(
+                CROSS_PROFILE_APP_PACKAGE_NAME, MODE_ALLOWED, OTHER_PROFILE_GROUP_USER_ID);
+        assertThat(getCrossProfileAppOp()).isEqualTo(MODE_DEFAULT);
+    }
+
+    @Test
     public void canConfigureInteractAcrossProfiles_packageNotInstalledInProfile_returnsFalse() {
         mockUninstallCrossProfileAppFromWorkProfile();
         assertThat(mCrossProfileAppsServiceImpl
@@ -530,7 +567,7 @@
 
     @Test
     public void canUserAttemptToConfigureInteractAcrossProfiles_profileOwnerOutsideProfileGroup_returnsTrue() {
-        when(mDevicePolicyManagerInternal.getProfileOwnerAsUser(OUTSIDE_PROFILE_GROUP_USER_ID))
+        when(mDevicePolicyManagerInternal.getProfileOwnerAsUser(OTHER_PROFILE_GROUP_USER_ID))
                 .thenReturn(buildCrossProfileComponentName());
         assertThat(mCrossProfileAppsServiceImpl
                 .canUserAttemptToConfigureInteractAcrossProfiles(CROSS_PROFILE_APP_PACKAGE_NAME))
@@ -601,8 +638,14 @@
     private void mockCrossProfileAndroidPackage(AndroidPackage androidPackage) {
         when(mPackageManagerInternal.getPackage(CROSS_PROFILE_APP_PACKAGE_NAME))
                 .thenReturn(androidPackage);
-        when(mPackageManagerInternal.getPackage(PERSONAL_PROFILE_UID)).thenReturn(androidPackage);
-        when(mPackageManagerInternal.getPackage(WORK_PROFILE_UID)).thenReturn(androidPackage);
+        when(mPackageManagerInternal.getPackage(PERSONAL_PROFILE_UID))
+                .thenReturn(androidPackage);
+        when(mPackageManagerInternal.getPackage(WORK_PROFILE_UID))
+                .thenReturn(androidPackage);
+        when(mPackageManagerInternal.getPackage(OTHER_PROFILE_GROUP_UID))
+                .thenReturn(androidPackage);
+        when(mPackageManagerInternal.getPackage(OTHER_PROFILE_GROUP_2_UID))
+                .thenReturn(androidPackage);
     }
 
     private void mockCrossProfileAppNotWhitelisted() {