diff options
author | Alex Kershaw <alexkershaw@google.com> | 2020-12-05 10:03:00 +0000 |
---|---|---|
committer | kholoud mohamed <kholoudm@google.com> | 2020-12-17 13:27:34 +0000 |
commit | f764d9e98f9fadb1c76b89e768683bb02595363c (patch) | |
tree | 460df70ae6ceeb92d9e4804cf0a5f21f59905685 | |
parent | 6403bb4cc2f9dd141a08e34f74d81ab34da85210 (diff) |
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)
6 files changed, 218 insertions, 98 deletions
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java index 8f5dbc45bf1b..62ac84b2b1e6 100644 --- a/core/java/android/app/admin/DevicePolicyManagerInternal.java +++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java @@ -222,4 +222,16 @@ public abstract class DevicePolicyManagerInternal { * 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 16a749fa360a..255aeac06cae 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 class CrossProfileAppsInternal { */ 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 1b12dc79d98f..4e194e2e495a 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.Manifest; 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.AppOpsManagerInternal.CheckOpsDelegate; 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 @@ public class AppOpsService extends IAppOpsService.Stub { 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 @@ public class AppOpsService extends IAppOpsService.Stub { 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 @@ public class AppOpsService extends IAppOpsService.Stub { } } - 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 617f6879e65e..3e9e45e59b95 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; 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 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { 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 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { 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 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { 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 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { /** * 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 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { 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 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { // 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 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { } 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 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { * 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 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { @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 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { if (uid == -1) { continue; } - if (isPermissionGranted(Manifest.permission.INTERACT_ACROSS_PROFILES, uid)) { + if (isPermissionGranted(INTERACT_ACROSS_PROFILES, uid)) { return true; } } @@ -642,7 +651,7 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { 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 class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { 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 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { } 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 class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { 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 ce3cdeab232a..b8a20ed4fe5b 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.ActivityThread; 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 class DevicePolicyManagerService extends BaseIDevicePolicyManager { 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 4b25890e5fdb..adf892aa12ab 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.annotation.UserIdInt; 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 @@ public class CrossProfileAppsServiceImplRoboTest { 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 @@ public class CrossProfileAppsServiceImplRoboTest { 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 @@ public class CrossProfileAppsServiceImplRoboTest { @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 @@ public class CrossProfileAppsServiceImplRoboTest { 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 @@ public class CrossProfileAppsServiceImplRoboTest { 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 @@ public class CrossProfileAppsServiceImplRoboTest { } @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 @@ public class CrossProfileAppsServiceImplRoboTest { @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 @@ public class CrossProfileAppsServiceImplRoboTest { 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() { |