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:
committed by
Kholoud Mohamed
parent
fafb66fb86
commit
d004f41188
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user