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() {