Loading core/java/android/app/admin/DevicePolicyManager.java +4 −0 Original line number Diff line number Diff line Loading @@ -11399,6 +11399,10 @@ public class DevicePolicyManager { * * <p>Previous calls are overridden by each subsequent call to this method. * * <p>When previously-set cross-profile packages are missing from {@code packageNames}, the * app-op for {@code INTERACT_ACROSS_PROFILES} will be reset for those packages. This will not * occur for packages that are whitelisted by the OEM. * * @param admin the {@link DeviceAdminReceiver} this request is associated with * @param packageNames the new cross-profile package names */ Loading core/java/android/content/pm/CrossProfileApps.java +66 −7 Original line number Diff line number Diff line Loading @@ -34,8 +34,10 @@ import android.provider.Settings; import com.android.internal.R; import com.android.internal.util.UserIcons; import java.util.Collection; import java.util.List; import java.util.Set; import java.util.stream.Collectors; /** * Class for handling cross profile operations. Apps can use this class to interact with its Loading Loading @@ -298,19 +300,24 @@ public class CrossProfileApps { * configurable by users in Settings. This configures it for the profile group of the calling * package. * * <p>Before calling, check {@link #canRequestInteractAcrossProfiles()} and do not call if it is * {@code false}. If presenting a user interface, do not allow the user to configure the app-op * in that case. * <p>Before calling, check {@link #canConfigureInteractAcrossProfiles(String)} and do not call * if it is {@code false}. If presenting a user interface, do not allow the user to configure * the app-op in that case. * * <p>The underlying app-op {@link android.app.AppOpsManager#OP_INTERACT_ACROSS_PROFILES} should * never be set directly. This method ensures that the app-op is kept in sync for the app across * each user in the profile group and that those apps are sent a broadcast when their ability to * interact across profiles changes. * * <p>This method should be used whenever an app's ability to interact across profiles changes, * as defined by the return value of {@link #canInteractAcrossProfiles()}. This includes user * consent changes in Settings or during provisioning, plus changes to the admin or OEM consent * whitelists that make the current app-op value invalid. * <p>This method should be used directly whenever a user's action results in a change in an * app's ability to interact across profiles, as defined by the return value of {@link * #canInteractAcrossProfiles()}. This includes user consent changes in Settings or during * provisioning. * * <p>If other changes could have affected the app's ability to interact across profiles, as * defined by the return value of {@link #canInteractAcrossProfiles()}, such as changes to the * admin or OEM consent whitelists, then {@link * #resetInteractAcrossProfilesAppOpsIfInvalid(List)} should be used. * * @hide */ Loading @@ -325,6 +332,58 @@ public class CrossProfileApps { } } /** * Returns whether the given package can have its ability to interact across profiles configured * by the user. This means that every other condition to interact across profiles has been set. * * <p>This differs from {@link #canRequestInteractAcrossProfiles()} since it will not return * {@code false} simply when the target profile is disabled. * * @hide */ public boolean canConfigureInteractAcrossProfiles(@NonNull String packageName) { try { return mService.canConfigureInteractAcrossProfiles(packageName); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } /** * For each of the packages defined in {@code previousCrossProfilePackages} but not included in * {@code newCrossProfilePackages}, resets the app-op for {@link android.Manifest.permission * #INTERACT_ACROSS_PROFILES} back to its default value if it can no longer be configured by * users in Settings, as defined by {@link #canConfigureInteractAcrossProfiles(String)}. * * <p>This method should be used whenever an app's ability to interact across profiles could * have changed as a result of non-user actions, such as changes to admin or OEM consent * whitelists. * * @hide */ @RequiresPermission( allOf={android.Manifest.permission.MANAGE_APP_OPS_MODES, android.Manifest.permission.INTERACT_ACROSS_USERS}) public void resetInteractAcrossProfilesAppOps( @NonNull Collection<String> previousCrossProfilePackages, @NonNull Set<String> newCrossProfilePackages) { if (previousCrossProfilePackages.isEmpty()) { return; } final List<String> unsetCrossProfilePackages = previousCrossProfilePackages.stream() .filter(packageName -> !newCrossProfilePackages.contains(packageName)) .collect(Collectors.toList()); if (unsetCrossProfilePackages.isEmpty()) { return; } try { mService.resetInteractAcrossProfilesAppOps(unsetCrossProfilePackages); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } private void verifyCanAccessUser(UserHandle userHandle) { if (!getTargetUserProfiles().contains(userHandle)) { throw new SecurityException("Not allowed to access " + userHandle); Loading core/java/android/content/pm/ICrossProfileApps.aidl +2 −0 Original line number Diff line number Diff line Loading @@ -35,4 +35,6 @@ interface ICrossProfileApps { boolean canInteractAcrossProfiles(in String callingPackage); boolean canRequestInteractAcrossProfiles(in String callingPackage); void setInteractAcrossProfilesAppOp(in String packageName, int newMode); boolean canConfigureInteractAcrossProfiles(in String packageName); void resetInteractAcrossProfilesAppOps(in List<String> packageNames); } No newline at end of file services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java +67 −17 Original line number Diff line number Diff line Loading @@ -286,21 +286,21 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { } private List<UserHandle> getTargetUserProfilesUnchecked( String callingPackage, @UserIdInt int callingUserId) { String packageName, @UserIdInt int userId) { final long ident = mInjector.clearCallingIdentity(); try { final int[] enabledProfileIds = mInjector.getUserManager().getEnabledProfileIds(callingUserId); mInjector.getUserManager().getEnabledProfileIds(userId); List<UserHandle> targetProfiles = new ArrayList<>(); for (final int userId : enabledProfileIds) { if (userId == callingUserId) { for (final int profileId : enabledProfileIds) { if (profileId == userId) { continue; } if (!isPackageEnabled(callingPackage, userId)) { if (!isPackageEnabled(packageName, profileId)) { continue; } targetProfiles.add(UserHandle.of(userId)); targetProfiles.add(UserHandle.of(profileId)); } return targetProfiles; } finally { Loading Loading @@ -385,14 +385,9 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { "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)) { throw new SecurityException( "MANAGE_APP_OPS_MODES is required to set the app-op for interacting across" + " profiles."); } final int callingUserId = mInjector.getCallingUserId(); if (newMode == AppOpsManager.MODE_ALLOWED && !canRequestInteractAcrossProfilesUnchecked(packageName, callingUserId)) { && !canConfigureInteractAcrossProfiles(packageName)) { // 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" Loading Loading @@ -462,18 +457,73 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { private void sendCanInteractAcrossProfilesChangedBroadcast( String packageName, int uid, UserHandle userHandle) { final Intent intent = new Intent(ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED) .setPackage(packageName); if (!appDeclaresCrossProfileAttribute(uid)) { final Intent intent = new Intent(ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED).setPackage(packageName); if (appDeclaresCrossProfileAttribute(uid)) { intent.addFlags( Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND | Intent.FLAG_RECEIVER_FOREGROUND); } else { intent.addFlags(FLAG_RECEIVER_REGISTERED_ONLY); } for (ResolveInfo receiver : findBroadcastReceiversForUser(intent, userHandle)) { intent.setComponent(receiver.getComponentInfo().getComponentName()); mInjector.sendBroadcastAsUser(intent, userHandle); } } private List<ResolveInfo> findBroadcastReceiversForUser(Intent intent, UserHandle userHandle) { return mInjector.getPackageManager() .queryBroadcastReceiversAsUser(intent, /* flags= */ 0, userHandle); } private boolean appDeclaresCrossProfileAttribute(int uid) { return mInjector.getPackageManagerInternal().getPackage(uid).isCrossProfile(); } @Override public boolean canConfigureInteractAcrossProfiles(String packageName) { if (!hasOtherProfileWithPackageInstalled(packageName, mInjector.getCallingUserId())) { return false; } if (!hasRequestedAppOpPermission( AppOpsManager.opToPermission(OP_INTERACT_ACROSS_PROFILES), packageName)) { return false; } return isCrossProfilePackageWhitelisted(packageName); } private boolean hasOtherProfileWithPackageInstalled(String packageName, @UserIdInt int userId) { final long ident = mInjector.clearCallingIdentity(); try { final int[] profileIds = mInjector.getUserManager().getProfileIds(userId, /* enabledOnly= */ false); for (int profileId : profileIds) { if (profileId != userId && isPackageInstalled(packageName, profileId)) { return true; } } } finally { mInjector.restoreCallingIdentity(ident); } return false; } @Override public void resetInteractAcrossProfilesAppOps(List<String> packageNames) { packageNames.forEach(this::resetInteractAcrossProfilesAppOp); } private void resetInteractAcrossProfilesAppOp(String packageName) { if (canConfigureInteractAcrossProfiles(packageName)) { Slog.w(TAG, "Not resetting app-op for package " + packageName + " since it is still configurable by users."); return; } final String op = AppOpsManager.permissionToOp(Manifest.permission.INTERACT_ACROSS_PROFILES); setInteractAcrossProfilesAppOp(packageName, AppOpsManager.opToDefaultMode(op)); } private boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId) { final long ident = mInjector.clearCallingIdentity(); try { Loading services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +9 −0 Original line number Diff line number Diff line Loading @@ -156,6 +156,7 @@ import android.content.IntentFilter; import android.content.PermissionChecker; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.CrossProfileApps; import android.content.pm.IPackageDataObserver; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; Loading Loading @@ -302,6 +303,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; Loading @@ -310,6 +312,7 @@ import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Function; import java.util.stream.Collectors; /** * Implementation of the device policy APIs. Loading Loading @@ -14887,12 +14890,18 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } Objects.requireNonNull(who, "ComponentName is null"); Objects.requireNonNull(packageNames, "Package names is null"); final List<String> previousCrossProfilePackages; synchronized (getLockObject()) { final ActiveAdmin admin = getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); previousCrossProfilePackages = admin.mCrossProfilePackages; admin.mCrossProfilePackages = packageNames; saveSettingsLocked(mInjector.userHandleGetCallingUserId()); } final CrossProfileApps crossProfileApps = mContext.getSystemService(CrossProfileApps.class); mInjector.binderWithCleanCallingIdentity( () -> crossProfileApps.resetInteractAcrossProfilesAppOps( previousCrossProfilePackages, new HashSet<>(packageNames))); } @Override Loading
core/java/android/app/admin/DevicePolicyManager.java +4 −0 Original line number Diff line number Diff line Loading @@ -11399,6 +11399,10 @@ public class DevicePolicyManager { * * <p>Previous calls are overridden by each subsequent call to this method. * * <p>When previously-set cross-profile packages are missing from {@code packageNames}, the * app-op for {@code INTERACT_ACROSS_PROFILES} will be reset for those packages. This will not * occur for packages that are whitelisted by the OEM. * * @param admin the {@link DeviceAdminReceiver} this request is associated with * @param packageNames the new cross-profile package names */ Loading
core/java/android/content/pm/CrossProfileApps.java +66 −7 Original line number Diff line number Diff line Loading @@ -34,8 +34,10 @@ import android.provider.Settings; import com.android.internal.R; import com.android.internal.util.UserIcons; import java.util.Collection; import java.util.List; import java.util.Set; import java.util.stream.Collectors; /** * Class for handling cross profile operations. Apps can use this class to interact with its Loading Loading @@ -298,19 +300,24 @@ public class CrossProfileApps { * configurable by users in Settings. This configures it for the profile group of the calling * package. * * <p>Before calling, check {@link #canRequestInteractAcrossProfiles()} and do not call if it is * {@code false}. If presenting a user interface, do not allow the user to configure the app-op * in that case. * <p>Before calling, check {@link #canConfigureInteractAcrossProfiles(String)} and do not call * if it is {@code false}. If presenting a user interface, do not allow the user to configure * the app-op in that case. * * <p>The underlying app-op {@link android.app.AppOpsManager#OP_INTERACT_ACROSS_PROFILES} should * never be set directly. This method ensures that the app-op is kept in sync for the app across * each user in the profile group and that those apps are sent a broadcast when their ability to * interact across profiles changes. * * <p>This method should be used whenever an app's ability to interact across profiles changes, * as defined by the return value of {@link #canInteractAcrossProfiles()}. This includes user * consent changes in Settings or during provisioning, plus changes to the admin or OEM consent * whitelists that make the current app-op value invalid. * <p>This method should be used directly whenever a user's action results in a change in an * app's ability to interact across profiles, as defined by the return value of {@link * #canInteractAcrossProfiles()}. This includes user consent changes in Settings or during * provisioning. * * <p>If other changes could have affected the app's ability to interact across profiles, as * defined by the return value of {@link #canInteractAcrossProfiles()}, such as changes to the * admin or OEM consent whitelists, then {@link * #resetInteractAcrossProfilesAppOpsIfInvalid(List)} should be used. * * @hide */ Loading @@ -325,6 +332,58 @@ public class CrossProfileApps { } } /** * Returns whether the given package can have its ability to interact across profiles configured * by the user. This means that every other condition to interact across profiles has been set. * * <p>This differs from {@link #canRequestInteractAcrossProfiles()} since it will not return * {@code false} simply when the target profile is disabled. * * @hide */ public boolean canConfigureInteractAcrossProfiles(@NonNull String packageName) { try { return mService.canConfigureInteractAcrossProfiles(packageName); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } /** * For each of the packages defined in {@code previousCrossProfilePackages} but not included in * {@code newCrossProfilePackages}, resets the app-op for {@link android.Manifest.permission * #INTERACT_ACROSS_PROFILES} back to its default value if it can no longer be configured by * users in Settings, as defined by {@link #canConfigureInteractAcrossProfiles(String)}. * * <p>This method should be used whenever an app's ability to interact across profiles could * have changed as a result of non-user actions, such as changes to admin or OEM consent * whitelists. * * @hide */ @RequiresPermission( allOf={android.Manifest.permission.MANAGE_APP_OPS_MODES, android.Manifest.permission.INTERACT_ACROSS_USERS}) public void resetInteractAcrossProfilesAppOps( @NonNull Collection<String> previousCrossProfilePackages, @NonNull Set<String> newCrossProfilePackages) { if (previousCrossProfilePackages.isEmpty()) { return; } final List<String> unsetCrossProfilePackages = previousCrossProfilePackages.stream() .filter(packageName -> !newCrossProfilePackages.contains(packageName)) .collect(Collectors.toList()); if (unsetCrossProfilePackages.isEmpty()) { return; } try { mService.resetInteractAcrossProfilesAppOps(unsetCrossProfilePackages); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } private void verifyCanAccessUser(UserHandle userHandle) { if (!getTargetUserProfiles().contains(userHandle)) { throw new SecurityException("Not allowed to access " + userHandle); Loading
core/java/android/content/pm/ICrossProfileApps.aidl +2 −0 Original line number Diff line number Diff line Loading @@ -35,4 +35,6 @@ interface ICrossProfileApps { boolean canInteractAcrossProfiles(in String callingPackage); boolean canRequestInteractAcrossProfiles(in String callingPackage); void setInteractAcrossProfilesAppOp(in String packageName, int newMode); boolean canConfigureInteractAcrossProfiles(in String packageName); void resetInteractAcrossProfilesAppOps(in List<String> packageNames); } No newline at end of file
services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java +67 −17 Original line number Diff line number Diff line Loading @@ -286,21 +286,21 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { } private List<UserHandle> getTargetUserProfilesUnchecked( String callingPackage, @UserIdInt int callingUserId) { String packageName, @UserIdInt int userId) { final long ident = mInjector.clearCallingIdentity(); try { final int[] enabledProfileIds = mInjector.getUserManager().getEnabledProfileIds(callingUserId); mInjector.getUserManager().getEnabledProfileIds(userId); List<UserHandle> targetProfiles = new ArrayList<>(); for (final int userId : enabledProfileIds) { if (userId == callingUserId) { for (final int profileId : enabledProfileIds) { if (profileId == userId) { continue; } if (!isPackageEnabled(callingPackage, userId)) { if (!isPackageEnabled(packageName, profileId)) { continue; } targetProfiles.add(UserHandle.of(userId)); targetProfiles.add(UserHandle.of(profileId)); } return targetProfiles; } finally { Loading Loading @@ -385,14 +385,9 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { "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)) { throw new SecurityException( "MANAGE_APP_OPS_MODES is required to set the app-op for interacting across" + " profiles."); } final int callingUserId = mInjector.getCallingUserId(); if (newMode == AppOpsManager.MODE_ALLOWED && !canRequestInteractAcrossProfilesUnchecked(packageName, callingUserId)) { && !canConfigureInteractAcrossProfiles(packageName)) { // 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" Loading Loading @@ -462,18 +457,73 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { private void sendCanInteractAcrossProfilesChangedBroadcast( String packageName, int uid, UserHandle userHandle) { final Intent intent = new Intent(ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED) .setPackage(packageName); if (!appDeclaresCrossProfileAttribute(uid)) { final Intent intent = new Intent(ACTION_CAN_INTERACT_ACROSS_PROFILES_CHANGED).setPackage(packageName); if (appDeclaresCrossProfileAttribute(uid)) { intent.addFlags( Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND | Intent.FLAG_RECEIVER_FOREGROUND); } else { intent.addFlags(FLAG_RECEIVER_REGISTERED_ONLY); } for (ResolveInfo receiver : findBroadcastReceiversForUser(intent, userHandle)) { intent.setComponent(receiver.getComponentInfo().getComponentName()); mInjector.sendBroadcastAsUser(intent, userHandle); } } private List<ResolveInfo> findBroadcastReceiversForUser(Intent intent, UserHandle userHandle) { return mInjector.getPackageManager() .queryBroadcastReceiversAsUser(intent, /* flags= */ 0, userHandle); } private boolean appDeclaresCrossProfileAttribute(int uid) { return mInjector.getPackageManagerInternal().getPackage(uid).isCrossProfile(); } @Override public boolean canConfigureInteractAcrossProfiles(String packageName) { if (!hasOtherProfileWithPackageInstalled(packageName, mInjector.getCallingUserId())) { return false; } if (!hasRequestedAppOpPermission( AppOpsManager.opToPermission(OP_INTERACT_ACROSS_PROFILES), packageName)) { return false; } return isCrossProfilePackageWhitelisted(packageName); } private boolean hasOtherProfileWithPackageInstalled(String packageName, @UserIdInt int userId) { final long ident = mInjector.clearCallingIdentity(); try { final int[] profileIds = mInjector.getUserManager().getProfileIds(userId, /* enabledOnly= */ false); for (int profileId : profileIds) { if (profileId != userId && isPackageInstalled(packageName, profileId)) { return true; } } } finally { mInjector.restoreCallingIdentity(ident); } return false; } @Override public void resetInteractAcrossProfilesAppOps(List<String> packageNames) { packageNames.forEach(this::resetInteractAcrossProfilesAppOp); } private void resetInteractAcrossProfilesAppOp(String packageName) { if (canConfigureInteractAcrossProfiles(packageName)) { Slog.w(TAG, "Not resetting app-op for package " + packageName + " since it is still configurable by users."); return; } final String op = AppOpsManager.permissionToOp(Manifest.permission.INTERACT_ACROSS_PROFILES); setInteractAcrossProfilesAppOp(packageName, AppOpsManager.opToDefaultMode(op)); } private boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId) { final long ident = mInjector.clearCallingIdentity(); try { Loading
services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +9 −0 Original line number Diff line number Diff line Loading @@ -156,6 +156,7 @@ import android.content.IntentFilter; import android.content.PermissionChecker; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.CrossProfileApps; import android.content.pm.IPackageDataObserver; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; Loading Loading @@ -302,6 +303,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; Loading @@ -310,6 +312,7 @@ import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Function; import java.util.stream.Collectors; /** * Implementation of the device policy APIs. Loading Loading @@ -14887,12 +14890,18 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } Objects.requireNonNull(who, "ComponentName is null"); Objects.requireNonNull(packageNames, "Package names is null"); final List<String> previousCrossProfilePackages; synchronized (getLockObject()) { final ActiveAdmin admin = getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER); previousCrossProfilePackages = admin.mCrossProfilePackages; admin.mCrossProfilePackages = packageNames; saveSettingsLocked(mInjector.userHandleGetCallingUserId()); } final CrossProfileApps crossProfileApps = mContext.getSystemService(CrossProfileApps.class); mInjector.binderWithCleanCallingIdentity( () -> crossProfileApps.resetInteractAcrossProfilesAppOps( previousCrossProfilePackages, new HashSet<>(packageNames))); } @Override