Loading api/current.txt +2 −0 Original line number Diff line number Diff line Loading @@ -11356,6 +11356,8 @@ package android.content.pm { } public class CrossProfileApps { method public boolean canInteractAcrossProfiles(); method public boolean canRequestInteractAcrossProfiles(); method @NonNull public android.graphics.drawable.Drawable getProfileSwitchingIconDrawable(@NonNull android.os.UserHandle); method @NonNull public CharSequence getProfileSwitchingLabel(@NonNull android.os.UserHandle); method @NonNull public java.util.List<android.os.UserHandle> getTargetUserProfiles(); core/java/android/app/admin/DevicePolicyManagerInternal.java +22 −0 Original line number Diff line number Diff line Loading @@ -17,9 +17,12 @@ package android.app.admin; import android.annotation.UserIdInt; import android.content.ComponentName; import android.content.Intent; import android.os.UserHandle; import java.util.List; import java.util.Set; /** * Device policy manager local system service interface. Loading Loading @@ -165,4 +168,23 @@ public abstract class DevicePolicyManagerInternal { * Do not call it directly. Use {@link DevicePolicyCache#getInstance()} instead. */ protected abstract DeviceStateCache getDeviceStateCache(); /** * Returns the combined set of the following: * <ul> * <li>The package names that the admin has previously set as allowed to request user consent * for cross-profile communication, via {@link * DevicePolicyManager#setCrossProfilePackages(ComponentName, Set)}.</li> * <li>The default package names that are allowed to request user consent for cross-profile * communication without being explicitly enabled by the admin , via {@link * DevicePolicyManager#setDefaultCrossProfilePackages(ComponentName, UserHandle, Set)}.</li> * </ul> * * @return the combined set of whitelisted package names set via * {@link DevicePolicyManager#setCrossProfilePackages(ComponentName, Set)} and * {@link DevicePolicyManager#setDefaultCrossProfilePackages(ComponentName, UserHandle, Set)} * * @hide */ public abstract List<String> getAllCrossProfilePackages(); } core/java/android/content/pm/CrossProfileApps.java +57 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ import com.android.internal.R; import com.android.internal.util.UserIcons; import java.util.List; import java.util.Set; /** * Class for handling cross profile operations. Apps can use this class to interact with its Loading Loading @@ -169,6 +170,62 @@ public class CrossProfileApps { } } /** * Returns whether the calling package can request to interact across profiles. * * <p>The package's current ability to interact across profiles can be checked with * {@link #canInteractAcrossProfiles()}. * * <p>Specifically, returns whether the following are all true: * <ul> * <li>{@link #getTargetUserProfiles()} returns a non-empty list for the calling user.</li> * <li>The calling app has requested</li> * {@code android.Manifest.permission.INTERACT_ACROSS_PROFILES} in its manifest. * <li>The calling package has either been whitelisted by default by the OEM or has been * explicitly whitelisted by the admin via * {@link android.app.admin.DevicePolicyManager#setCrossProfilePackages(ComponentName, Set)}. * </li> * </ul> * * @return true if the calling package can request to interact across profiles. */ public boolean canRequestInteractAcrossProfiles() { try { return mService.canRequestInteractAcrossProfiles(mContext.getPackageName()); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } /** * Returns whether the calling package can interact across profiles. * * <p>The package's current ability to request to interact across profiles can be checked with * {@link #canRequestInteractAcrossProfiles()}. * * <p>Specifically, returns whether the following are all true: * <ul> * <li>{@link #getTargetUserProfiles()} returns a non-empty list for the calling user.</li> * <li>The user has previously consented to cross-profile communication for the calling * package.</li> * <li>The calling package has either been whitelisted by default by the OEM or has been * explicitly whitelisted by the admin via * {@link android.app.admin.DevicePolicyManager#setCrossProfilePackages(ComponentName, Set)}. * </li> * </ul> * * @return true if the calling package can interact across profiles. * @throws SecurityException if {@code mContext.getPackageName()} does not belong to the * calling UID. */ public boolean canInteractAcrossProfiles() { try { return mService.canInteractAcrossProfiles(mContext.getPackageName()); } 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 @@ -30,4 +30,6 @@ interface ICrossProfileApps { void startActivityAsUser(in IApplicationThread caller, in String callingPackage, in ComponentName component, int userId, boolean launchMainActivity); List<UserHandle> getTargetUserProfiles(in String callingPackage); boolean canInteractAcrossProfiles(in String callingPackage); boolean canRequestInteractAcrossProfiles(in String callingPackage); } No newline at end of file services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java +86 −1 Original line number Diff line number Diff line Loading @@ -15,35 +15,45 @@ */ package com.android.server.pm; import static android.app.AppOpsManager.OP_INTERACT_ACROSS_PROFILES; 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; import android.app.ActivityOptions; import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.IApplicationThread; import android.app.admin.DevicePolicyEventLogger; import android.app.admin.DevicePolicyManagerInternal; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ICrossProfileApps; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.ResolveInfo; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; import android.os.UserManager; import android.stats.devicepolicy.DevicePolicyEnums; import android.text.TextUtils; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import com.android.internal.app.IAppOpsService; import com.android.internal.util.ArrayUtils; import com.android.server.LocalServices; import com.android.server.appop.AppOpsService; import com.android.server.wm.ActivityTaskManagerInternal; import java.util.ArrayList; Loading @@ -55,6 +65,9 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { private Context mContext; private Injector mInjector; private AppOpsService mAppOpsService; private final DevicePolicyManagerInternal mDpmi; private final IPackageManager mIpm; public CrossProfileAppsServiceImpl(Context context) { this(context, new InjectorImpl(context)); Loading @@ -64,6 +77,8 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { CrossProfileAppsServiceImpl(Context context, Injector injector) { mContext = context; mInjector = injector; mIpm = AppGlobals.getPackageManager(); mDpmi = LocalServices.getService(DevicePolicyManagerInternal.class); } @Override Loading Loading @@ -153,6 +168,63 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { userId); } @Override public boolean canRequestInteractAcrossProfiles(String callingPackage) { Objects.requireNonNull(callingPackage); verifyCallingPackage(callingPackage); final List<UserHandle> targetUserProfiles = getTargetUserProfilesUnchecked( callingPackage, mInjector.getCallingUserId()); if (targetUserProfiles.isEmpty()) { return false; } if (!hasRequestedAppOpPermission( AppOpsManager.opToPermission(OP_INTERACT_ACROSS_PROFILES), callingPackage)) { return false; } return isCrossProfilePackageWhitelisted(callingPackage); } private boolean hasRequestedAppOpPermission(String permission, String packageName) { try { String[] packages = mIpm.getAppOpPermissionPackages(permission); return ArrayUtils.contains(packages, packageName); } catch (RemoteException exc) { Slog.e(TAG, "PackageManager dead. Cannot get permission info"); return false; } } @Override public boolean canInteractAcrossProfiles(String callingPackage) { Objects.requireNonNull(callingPackage); verifyCallingPackage(callingPackage); final List<UserHandle> targetUserProfiles = getTargetUserProfilesUnchecked( callingPackage, mInjector.getCallingUserId()); if (targetUserProfiles.isEmpty()) { return false; } final int callingUid = mInjector.getCallingUid(); return isPermissionGranted(Manifest.permission.INTERACT_ACROSS_USERS_FULL, callingUid) || isPermissionGranted(Manifest.permission.INTERACT_ACROSS_USERS, callingUid) || isPermissionGranted(Manifest.permission.INTERACT_ACROSS_PROFILES, callingUid) || AppOpsManager.MODE_ALLOWED == getAppOpsService().noteOperation( OP_INTERACT_ACROSS_PROFILES, callingUid, callingPackage, /* featureId= */ null, /*shouldCollectAsyncNotedOp= */false, /*message= */null); } private boolean isCrossProfilePackageWhitelisted(String packageName) { final long ident = mInjector.clearCallingIdentity(); try { return mDpmi.getAllCrossProfilePackages().contains(packageName); } finally { mInjector.restoreCallingIdentity(ident); } } private List<UserHandle> getTargetUserProfilesUnchecked( String callingPackage, @UserIdInt int callingUserId) { final long ident = mInjector.clearCallingIdentity(); Loading Loading @@ -239,6 +311,19 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { mInjector.getAppOpsManager().checkPackage(mInjector.getCallingUid(), callingPackage); } private static boolean isPermissionGranted(String permission, int uid) { return PackageManager.PERMISSION_GRANTED == ActivityManager.checkComponentPermission( permission, uid, /* owningUid= */-1, /* exported= */ true); } private AppOpsService getAppOpsService() { if (mAppOpsService == null) { IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE); mAppOpsService = (AppOpsService) IAppOpsService.Stub.asInterface(b); } return mAppOpsService; } private static class InjectorImpl implements Injector { private Context mContext; Loading Loading
api/current.txt +2 −0 Original line number Diff line number Diff line Loading @@ -11356,6 +11356,8 @@ package android.content.pm { } public class CrossProfileApps { method public boolean canInteractAcrossProfiles(); method public boolean canRequestInteractAcrossProfiles(); method @NonNull public android.graphics.drawable.Drawable getProfileSwitchingIconDrawable(@NonNull android.os.UserHandle); method @NonNull public CharSequence getProfileSwitchingLabel(@NonNull android.os.UserHandle); method @NonNull public java.util.List<android.os.UserHandle> getTargetUserProfiles();
core/java/android/app/admin/DevicePolicyManagerInternal.java +22 −0 Original line number Diff line number Diff line Loading @@ -17,9 +17,12 @@ package android.app.admin; import android.annotation.UserIdInt; import android.content.ComponentName; import android.content.Intent; import android.os.UserHandle; import java.util.List; import java.util.Set; /** * Device policy manager local system service interface. Loading Loading @@ -165,4 +168,23 @@ public abstract class DevicePolicyManagerInternal { * Do not call it directly. Use {@link DevicePolicyCache#getInstance()} instead. */ protected abstract DeviceStateCache getDeviceStateCache(); /** * Returns the combined set of the following: * <ul> * <li>The package names that the admin has previously set as allowed to request user consent * for cross-profile communication, via {@link * DevicePolicyManager#setCrossProfilePackages(ComponentName, Set)}.</li> * <li>The default package names that are allowed to request user consent for cross-profile * communication without being explicitly enabled by the admin , via {@link * DevicePolicyManager#setDefaultCrossProfilePackages(ComponentName, UserHandle, Set)}.</li> * </ul> * * @return the combined set of whitelisted package names set via * {@link DevicePolicyManager#setCrossProfilePackages(ComponentName, Set)} and * {@link DevicePolicyManager#setDefaultCrossProfilePackages(ComponentName, UserHandle, Set)} * * @hide */ public abstract List<String> getAllCrossProfilePackages(); }
core/java/android/content/pm/CrossProfileApps.java +57 −0 Original line number Diff line number Diff line Loading @@ -30,6 +30,7 @@ import com.android.internal.R; import com.android.internal.util.UserIcons; import java.util.List; import java.util.Set; /** * Class for handling cross profile operations. Apps can use this class to interact with its Loading Loading @@ -169,6 +170,62 @@ public class CrossProfileApps { } } /** * Returns whether the calling package can request to interact across profiles. * * <p>The package's current ability to interact across profiles can be checked with * {@link #canInteractAcrossProfiles()}. * * <p>Specifically, returns whether the following are all true: * <ul> * <li>{@link #getTargetUserProfiles()} returns a non-empty list for the calling user.</li> * <li>The calling app has requested</li> * {@code android.Manifest.permission.INTERACT_ACROSS_PROFILES} in its manifest. * <li>The calling package has either been whitelisted by default by the OEM or has been * explicitly whitelisted by the admin via * {@link android.app.admin.DevicePolicyManager#setCrossProfilePackages(ComponentName, Set)}. * </li> * </ul> * * @return true if the calling package can request to interact across profiles. */ public boolean canRequestInteractAcrossProfiles() { try { return mService.canRequestInteractAcrossProfiles(mContext.getPackageName()); } catch (RemoteException ex) { throw ex.rethrowFromSystemServer(); } } /** * Returns whether the calling package can interact across profiles. * * <p>The package's current ability to request to interact across profiles can be checked with * {@link #canRequestInteractAcrossProfiles()}. * * <p>Specifically, returns whether the following are all true: * <ul> * <li>{@link #getTargetUserProfiles()} returns a non-empty list for the calling user.</li> * <li>The user has previously consented to cross-profile communication for the calling * package.</li> * <li>The calling package has either been whitelisted by default by the OEM or has been * explicitly whitelisted by the admin via * {@link android.app.admin.DevicePolicyManager#setCrossProfilePackages(ComponentName, Set)}. * </li> * </ul> * * @return true if the calling package can interact across profiles. * @throws SecurityException if {@code mContext.getPackageName()} does not belong to the * calling UID. */ public boolean canInteractAcrossProfiles() { try { return mService.canInteractAcrossProfiles(mContext.getPackageName()); } 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 @@ -30,4 +30,6 @@ interface ICrossProfileApps { void startActivityAsUser(in IApplicationThread caller, in String callingPackage, in ComponentName component, int userId, boolean launchMainActivity); List<UserHandle> getTargetUserProfiles(in String callingPackage); boolean canInteractAcrossProfiles(in String callingPackage); boolean canRequestInteractAcrossProfiles(in String callingPackage); } No newline at end of file
services/core/java/com/android/server/pm/CrossProfileAppsServiceImpl.java +86 −1 Original line number Diff line number Diff line Loading @@ -15,35 +15,45 @@ */ package com.android.server.pm; import static android.app.AppOpsManager.OP_INTERACT_ACROSS_PROFILES; 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; import android.app.ActivityOptions; import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.IApplicationThread; import android.app.admin.DevicePolicyEventLogger; import android.app.admin.DevicePolicyManagerInternal; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ICrossProfileApps; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.ResolveInfo; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; import android.os.UserManager; import android.stats.devicepolicy.DevicePolicyEnums; import android.text.TextUtils; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import com.android.internal.app.IAppOpsService; import com.android.internal.util.ArrayUtils; import com.android.server.LocalServices; import com.android.server.appop.AppOpsService; import com.android.server.wm.ActivityTaskManagerInternal; import java.util.ArrayList; Loading @@ -55,6 +65,9 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { private Context mContext; private Injector mInjector; private AppOpsService mAppOpsService; private final DevicePolicyManagerInternal mDpmi; private final IPackageManager mIpm; public CrossProfileAppsServiceImpl(Context context) { this(context, new InjectorImpl(context)); Loading @@ -64,6 +77,8 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { CrossProfileAppsServiceImpl(Context context, Injector injector) { mContext = context; mInjector = injector; mIpm = AppGlobals.getPackageManager(); mDpmi = LocalServices.getService(DevicePolicyManagerInternal.class); } @Override Loading Loading @@ -153,6 +168,63 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { userId); } @Override public boolean canRequestInteractAcrossProfiles(String callingPackage) { Objects.requireNonNull(callingPackage); verifyCallingPackage(callingPackage); final List<UserHandle> targetUserProfiles = getTargetUserProfilesUnchecked( callingPackage, mInjector.getCallingUserId()); if (targetUserProfiles.isEmpty()) { return false; } if (!hasRequestedAppOpPermission( AppOpsManager.opToPermission(OP_INTERACT_ACROSS_PROFILES), callingPackage)) { return false; } return isCrossProfilePackageWhitelisted(callingPackage); } private boolean hasRequestedAppOpPermission(String permission, String packageName) { try { String[] packages = mIpm.getAppOpPermissionPackages(permission); return ArrayUtils.contains(packages, packageName); } catch (RemoteException exc) { Slog.e(TAG, "PackageManager dead. Cannot get permission info"); return false; } } @Override public boolean canInteractAcrossProfiles(String callingPackage) { Objects.requireNonNull(callingPackage); verifyCallingPackage(callingPackage); final List<UserHandle> targetUserProfiles = getTargetUserProfilesUnchecked( callingPackage, mInjector.getCallingUserId()); if (targetUserProfiles.isEmpty()) { return false; } final int callingUid = mInjector.getCallingUid(); return isPermissionGranted(Manifest.permission.INTERACT_ACROSS_USERS_FULL, callingUid) || isPermissionGranted(Manifest.permission.INTERACT_ACROSS_USERS, callingUid) || isPermissionGranted(Manifest.permission.INTERACT_ACROSS_PROFILES, callingUid) || AppOpsManager.MODE_ALLOWED == getAppOpsService().noteOperation( OP_INTERACT_ACROSS_PROFILES, callingUid, callingPackage, /* featureId= */ null, /*shouldCollectAsyncNotedOp= */false, /*message= */null); } private boolean isCrossProfilePackageWhitelisted(String packageName) { final long ident = mInjector.clearCallingIdentity(); try { return mDpmi.getAllCrossProfilePackages().contains(packageName); } finally { mInjector.restoreCallingIdentity(ident); } } private List<UserHandle> getTargetUserProfilesUnchecked( String callingPackage, @UserIdInt int callingUserId) { final long ident = mInjector.clearCallingIdentity(); Loading Loading @@ -239,6 +311,19 @@ public class CrossProfileAppsServiceImpl extends ICrossProfileApps.Stub { mInjector.getAppOpsManager().checkPackage(mInjector.getCallingUid(), callingPackage); } private static boolean isPermissionGranted(String permission, int uid) { return PackageManager.PERMISSION_GRANTED == ActivityManager.checkComponentPermission( permission, uid, /* owningUid= */-1, /* exported= */ true); } private AppOpsService getAppOpsService() { if (mAppOpsService == null) { IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE); mAppOpsService = (AppOpsService) IAppOpsService.Stub.asInterface(b); } return mAppOpsService; } private static class InjectorImpl implements Injector { private Context mContext; Loading