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
This commit is contained in:
Alex Kershaw
2020-12-05 10:03:00 +00:00
committed by Kholoud Mohamed
parent fafb66fb86
commit d004f41188
6 changed files with 217 additions and 98 deletions

View File

@@ -237,4 +237,16 @@ public abstract class DevicePolicyManagerInternal {
* Returns whether the given package is a device owner or a profile owner in the calling user.
*/
public abstract boolean isDeviceOrProfileOwnerInCallingUser(String packageName);
/**
* 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);
}

View File

@@ -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);
}

View File

@@ -93,6 +93,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;
@@ -273,6 +274,8 @@ public class AppOpsService extends IAppOpsService.Stub {
private final AppOpsManagerInternalImpl mAppOpsManagerInternal
= new AppOpsManagerInternalImpl();
@Nullable private final DevicePolicyManagerInternal dpmi =
LocalServices.getService(DevicePolicyManagerInternal.class);
private final IPlatformCompat mPlatformCompat = IPlatformCompat.Stub.asInterface(
ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
@@ -2690,6 +2693,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;
@@ -2739,16 +2746,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);

View File

@@ -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);
}
}
}

View File

@@ -124,6 +124,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;
@@ -11780,6 +11781,28 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return profileOwnerInCallingUser != null
&& packageName.equals(profileOwnerInCallingUser.getPackageName());
}
@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) {

View File

@@ -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.getPackageUid(
CROSS_PROFILE_APP_PACKAGE_NAME, /* flags= */ 0, PERSONAL_PROFILE_USER_ID))
.thenReturn(PERSONAL_PROFILE_UID);
when(mPackageManagerInternal.getPackageUid(
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
@@ -421,6 +437,27 @@ public class CrossProfileAppsServiceImplRoboTest {
return permissionInfo;
}
@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();
@@ -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() {