Loading services/core/java/com/android/server/pm/CrossProfileIntentFilter.java +62 −1 Original line number Original line Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.pm; package com.android.server.pm; import android.annotation.IntDef; import android.content.Intent; import android.content.Intent; import android.content.IntentFilter; import android.content.IntentFilter; import android.os.UserHandle; import android.os.UserHandle; Loading @@ -30,6 +31,8 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** /** * The {@link PackageManagerService} maintains some {@link CrossProfileIntentFilter}s for each user. * The {@link PackageManagerService} maintains some {@link CrossProfileIntentFilter}s for each user. Loading @@ -41,13 +44,52 @@ class CrossProfileIntentFilter extends WatchedIntentFilter { private static final String ATTR_FLAGS = "flags"; private static final String ATTR_FLAGS = "flags"; private static final String ATTR_OWNER_PACKAGE = "ownerPackage"; private static final String ATTR_OWNER_PACKAGE = "ownerPackage"; private static final String ATTR_FILTER = "filter"; private static final String ATTR_FILTER = "filter"; private static final String ATTR_ACCESS_CONTROL = "accessControl"; private static final String TAG = "CrossProfileIntentFilter"; private static final String TAG = "CrossProfileIntentFilter"; /** * AccessControlLevel provides level of access for user to create/modify * {@link CrossProfileIntentFilter} in {@link com.android.server.pm.Settings}. * Each AccessControlLevel have value assigned, the higher the value * implies higher restriction for creation/modification. * AccessControlLevel allows us to protect against malicious changes in user's * {@link CrossProfileIntentFilter}s, which might add/remove {@link CrossProfileIntentFilter} * leading to unprecedented results. */ @IntDef(prefix = {"ACCESS_LEVEL_"}, value = { ACCESS_LEVEL_ALL, ACCESS_LEVEL_SYSTEM, ACCESS_LEVEL_SYSTEM_ADD_ONLY, }) @Retention(RetentionPolicy.SOURCE) public @interface AccessControlLevel { } /** * ACCESS_LEVEL_ALL signifies that irrespective of user we would allow * access(addition/modification/removal) for CrossProfileIntentFilter. * This is the default access control level. */ public static final int ACCESS_LEVEL_ALL = 0; /** * ACCESS_LEVEL_SYSTEM signifies that only system/root user would be able to * access(addition/modification/removal) CrossProfileIntentFilter. */ public static final int ACCESS_LEVEL_SYSTEM = 10; /** * ACCESS_LEVEL_SYSTEM_ADD_ONLY signifies that only system/root user would be able to add * CrossProfileIntentFilter but not modify/remove. Once added, it cannot be modified or removed. */ public static final int ACCESS_LEVEL_SYSTEM_ADD_ONLY = 20; // If the intent matches the IntentFilter, then it can be forwarded to this userId. // If the intent matches the IntentFilter, then it can be forwarded to this userId. final int mTargetUserId; final int mTargetUserId; final String mOwnerPackage; // packageName of the app. final String mOwnerPackage; // packageName of the app. final int mFlags; final int mFlags; final int mAccessControlLevel; // The cache for snapshots, so they are not rebuilt if the base object has not // The cache for snapshots, so they are not rebuilt if the base object has not // changed. // changed. Loading @@ -65,10 +107,16 @@ class CrossProfileIntentFilter extends WatchedIntentFilter { CrossProfileIntentFilter(IntentFilter filter, String ownerPackage, int targetUserId, CrossProfileIntentFilter(IntentFilter filter, String ownerPackage, int targetUserId, int flags) { int flags) { this(filter, ownerPackage, targetUserId, flags, ACCESS_LEVEL_ALL); } CrossProfileIntentFilter(IntentFilter filter, String ownerPackage, int targetUserId, int flags, @AccessControlLevel int accessControlLevel) { super(filter); super(filter); mTargetUserId = targetUserId; mTargetUserId = targetUserId; mOwnerPackage = ownerPackage; mOwnerPackage = ownerPackage; mFlags = flags; mFlags = flags; mAccessControlLevel = accessControlLevel; mSnapshot = makeCache(); mSnapshot = makeCache(); } } Loading @@ -77,12 +125,18 @@ class CrossProfileIntentFilter extends WatchedIntentFilter { this(filter.mFilter, ownerPackage, targetUserId, flags); this(filter.mFilter, ownerPackage, targetUserId, flags); } } CrossProfileIntentFilter(WatchedIntentFilter filter, String ownerPackage, int targetUserId, int flags, @AccessControlLevel int accessControlLevel) { this(filter.mFilter, ownerPackage, targetUserId, flags, accessControlLevel); } // Copy constructor used only to create a snapshot. // Copy constructor used only to create a snapshot. private CrossProfileIntentFilter(CrossProfileIntentFilter f) { private CrossProfileIntentFilter(CrossProfileIntentFilter f) { super(f); super(f); mTargetUserId = f.mTargetUserId; mTargetUserId = f.mTargetUserId; mOwnerPackage = f.mOwnerPackage; mOwnerPackage = f.mOwnerPackage; mFlags = f.mFlags; mFlags = f.mFlags; mAccessControlLevel = f.mAccessControlLevel; mSnapshot = new SnapshotCache.Sealed(); mSnapshot = new SnapshotCache.Sealed(); } } Loading @@ -98,9 +152,14 @@ class CrossProfileIntentFilter extends WatchedIntentFilter { return mOwnerPackage; return mOwnerPackage; } } public @AccessControlLevel int getAccessControlLevel() { return mAccessControlLevel; } CrossProfileIntentFilter(TypedXmlPullParser parser) throws XmlPullParserException, IOException { CrossProfileIntentFilter(TypedXmlPullParser parser) throws XmlPullParserException, IOException { mTargetUserId = parser.getAttributeInt(null, ATTR_TARGET_USER_ID, UserHandle.USER_NULL); mTargetUserId = parser.getAttributeInt(null, ATTR_TARGET_USER_ID, UserHandle.USER_NULL); mOwnerPackage = getStringFromXml(parser, ATTR_OWNER_PACKAGE, ""); mOwnerPackage = getStringFromXml(parser, ATTR_OWNER_PACKAGE, ""); mAccessControlLevel = parser.getAttributeInt(null, ATTR_ACCESS_CONTROL, ACCESS_LEVEL_ALL); mFlags = parser.getAttributeInt(null, ATTR_FLAGS, 0); mFlags = parser.getAttributeInt(null, ATTR_FLAGS, 0); mSnapshot = makeCache(); mSnapshot = makeCache(); Loading Loading @@ -151,6 +210,7 @@ class CrossProfileIntentFilter extends WatchedIntentFilter { serializer.attributeInt(null, ATTR_TARGET_USER_ID, mTargetUserId); serializer.attributeInt(null, ATTR_TARGET_USER_ID, mTargetUserId); serializer.attributeInt(null, ATTR_FLAGS, mFlags); serializer.attributeInt(null, ATTR_FLAGS, mFlags); serializer.attribute(null, ATTR_OWNER_PACKAGE, mOwnerPackage); serializer.attribute(null, ATTR_OWNER_PACKAGE, mOwnerPackage); serializer.attributeInt(null, ATTR_ACCESS_CONTROL, mAccessControlLevel); serializer.startTag(null, ATTR_FILTER); serializer.startTag(null, ATTR_FILTER); mFilter.writeToXml(serializer); mFilter.writeToXml(serializer); serializer.endTag(null, ATTR_FILTER); serializer.endTag(null, ATTR_FILTER); Loading @@ -165,7 +225,8 @@ class CrossProfileIntentFilter extends WatchedIntentFilter { boolean equalsIgnoreFilter(CrossProfileIntentFilter other) { boolean equalsIgnoreFilter(CrossProfileIntentFilter other) { return mTargetUserId == other.mTargetUserId return mTargetUserId == other.mTargetUserId && mOwnerPackage.equals(other.mOwnerPackage) && mOwnerPackage.equals(other.mOwnerPackage) && mFlags == other.mFlags; && mFlags == other.mFlags && mAccessControlLevel == other.mAccessControlLevel; } } public CrossProfileIntentFilter snapshot() { public CrossProfileIntentFilter snapshot() { Loading services/core/java/com/android/server/pm/PackageManagerService.java +13 −2 Original line number Original line Diff line number Diff line Loading @@ -3350,6 +3350,12 @@ public class PackageManagerService implements PackageSender, TestUtilityService android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null); android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null); int callingUid = Binder.getCallingUid(); int callingUid = Binder.getCallingUid(); enforceOwnerRights(snapshot, ownerPackage, callingUid); enforceOwnerRights(snapshot, ownerPackage, callingUid); // Verifying that current calling uid should be able to add {@link CrossProfileIntentFilter} // for source and target user mUserManager.enforceCrossProfileIntentFilterAccess(sourceUserId, targetUserId, callingUid, /* addCrossProfileIntentFilter */ true); PackageManagerServiceUtils.enforceShellRestriction(mInjector.getUserManagerInternal(), PackageManagerServiceUtils.enforceShellRestriction(mInjector.getUserManagerInternal(), UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, sourceUserId); UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, sourceUserId); if (intentFilter.countActions() == 0) { if (intentFilter.countActions() == 0) { Loading @@ -3358,7 +3364,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService } } synchronized (mLock) { synchronized (mLock) { CrossProfileIntentFilter newFilter = new CrossProfileIntentFilter(intentFilter, CrossProfileIntentFilter newFilter = new CrossProfileIntentFilter(intentFilter, ownerPackage, targetUserId, flags); ownerPackage, targetUserId, flags, mUserManager .getCrossProfileIntentFilterAccessControl(sourceUserId, targetUserId)); CrossProfileIntentResolver resolver = CrossProfileIntentResolver resolver = mSettings.editCrossProfileIntentResolverLPw(sourceUserId); mSettings.editCrossProfileIntentResolverLPw(sourceUserId); ArrayList<CrossProfileIntentFilter> existing = resolver.findFilters(intentFilter); ArrayList<CrossProfileIntentFilter> existing = resolver.findFilters(intentFilter); Loading Loading @@ -4572,7 +4579,11 @@ public class PackageManagerService implements PackageSender, TestUtilityService ArraySet<CrossProfileIntentFilter> set = ArraySet<CrossProfileIntentFilter> set = new ArraySet<>(resolver.filterSet()); new ArraySet<>(resolver.filterSet()); for (CrossProfileIntentFilter filter : set) { for (CrossProfileIntentFilter filter : set) { if (filter.getOwnerPackage().equals(ownerPackage)) { //Only remove if calling user is allowed based on access control of // {@link CrossProfileIntentFilter} if (filter.getOwnerPackage().equals(ownerPackage) && mUserManager.isCrossProfileIntentFilterAccessible(sourceUserId, filter.mTargetUserId, /* addCrossProfileIntentFilter */ false)) { resolver.removeFilter(filter); resolver.removeFilter(filter); } } } } Loading services/core/java/com/android/server/pm/UserManagerService.java +97 −0 Original line number Original line Diff line number Diff line Loading @@ -1866,6 +1866,103 @@ public class UserManagerService extends IUserManager.Stub { return mLocalService.exists(userId); return mLocalService.exists(userId); } } /** * Returns user's {@link CrossProfileIntentFilter.AccessControlLevel}, which is derived from * {@link UserTypeDetails}. If user does not have defined their access control level, * returns default {@link CrossProfileIntentFilter#ACCESS_LEVEL_ALL} */ private @CrossProfileIntentFilter.AccessControlLevel int getCrossProfileIntentFilterAccessControl(@UserIdInt int userId) { final UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(userId); return userTypeDetails != null ? userTypeDetails.getCrossProfileIntentFilterAccessControl() : CrossProfileIntentFilter.ACCESS_LEVEL_ALL; } /** * Verifies if calling user is allowed to access {@link CrossProfileIntentFilter} between given * source and target user. * @param sourceUserId userId for which CrossProfileIntentFilter would be configured * @param targetUserId target user where we can resolve given intent filter * @param callingUid user accessing api * @param addCrossProfileIntentFilter if the operation is addition or not. * @throws SecurityException is calling user is not allowed to access. */ public void enforceCrossProfileIntentFilterAccess( int sourceUserId, int targetUserId, int callingUid, boolean addCrossProfileIntentFilter) { if (!isCrossProfileIntentFilterAccessible(sourceUserId, targetUserId, addCrossProfileIntentFilter)) { throw new SecurityException("CrossProfileIntentFilter cannot be accessed by user " + callingUid); } } /** * Checks if {@link CrossProfileIntentFilter} can be accessed by calling user for given source * and target user. There are following rules of access * 1. For {@link CrossProfileIntentFilter#ACCESS_LEVEL_ALL}, * irrespective of user we would allow access(addition/modification/removal) * 2. For {@link CrossProfileIntentFilter#ACCESS_LEVEL_SYSTEM}, * only system/root user would be able to access(addition/modification/removal) * 3. For {@link CrossProfileIntentFilter#ACCESS_LEVEL_SYSTEM_ADD_ONLY}, * only system/root user would be able to add but not modify/remove. Once added, it cannot be * modified or removed * @param sourceUserId userId for which CrossProfileIntentFilter would be configured * @param targetUserId target user where we can resolve given intent filter * @param addCrossProfileIntentFilter if the operation is addition or not. * @return true if {@link CrossProfileIntentFilter} can be accessed by calling user */ public boolean isCrossProfileIntentFilterAccessible(int sourceUserId, int targetUserId, boolean addCrossProfileIntentFilter) { int effectiveAccessControl = getCrossProfileIntentFilterAccessControl(sourceUserId, targetUserId); /* For {@link CrossProfileIntentFilter#ACCESS_LEVEL_SYSTEM}, if accessing user is not system or root disallowing access to {@link CrossProfileIntentFilter} */ if (CrossProfileIntentFilter.ACCESS_LEVEL_SYSTEM == effectiveAccessControl && !PackageManagerServiceUtils.isSystemOrRoot()) { return false; } /* For {@link CrossProfileIntentFilter#ACCESS_LEVEL_SYSTEM_ADD_ONLY}, allowing only system user to add {@link CrossProfileIntentFilter}. All users(including system) are disallowed to modify/remove. */ if (CrossProfileIntentFilter.ACCESS_LEVEL_SYSTEM_ADD_ONLY == effectiveAccessControl && (!addCrossProfileIntentFilter || !PackageManagerServiceUtils.isSystemOrRoot())) { return false; } return true; } /** * Returns {@link CrossProfileIntentFilter.AccessControlLevel} * that should be assigned to {@link CrossProfileIntentFilter} * computed from source user's and target user's * {@link CrossProfileIntentFilter.AccessControlLevel}. * The Access Level is configured per {@link CrossProfileIntentFilter} and its property of edge * between source and target user e.g. for all {@link CrossProfileIntentFilter}s configured * between Primary user and Clone profile should have access level of * {@link CrossProfileIntentFilter#ACCESS_LEVEL_SYSTEM} which is driven by highest * access value from source or target. The higher value means higher restrictions. * @param sourceUserId userId of source user for whom CrossProfileIntentFilter will be stored * @param targetUserId userId of target user for whom Cross Profile access would be allowed * @return least privileged {@link CrossProfileIntentFilter.AccessControlLevel} from source or * target user. */ public @CrossProfileIntentFilter.AccessControlLevel int getCrossProfileIntentFilterAccessControl(int sourceUserId, int targetUserId) { int sourceAccessControlLevel, targetAccessControlLevel, effectiveAccessControl; sourceAccessControlLevel = getCrossProfileIntentFilterAccessControl(sourceUserId); targetAccessControlLevel = getCrossProfileIntentFilterAccessControl(targetUserId); effectiveAccessControl = Math.max(sourceAccessControlLevel, targetAccessControlLevel); return effectiveAccessControl; } @Override @Override public void setUserName(@UserIdInt int userId, String name) { public void setUserName(@UserIdInt int userId, String name) { checkManageUsersPermission("rename users"); checkManageUsersPermission("rename users"); Loading services/core/java/com/android/server/pm/UserTypeDetails.java +35 −2 Original line number Original line Diff line number Diff line Loading @@ -163,6 +163,14 @@ public final class UserTypeDetails { */ */ private final boolean mIsCredentialSharableWithParent; private final boolean mIsCredentialSharableWithParent; /** * Denotes the default access control for {@link CrossProfileIntentFilter} of user profile. * * <p> Default value is {@link CrossProfileIntentFilter#ACCESS_LEVEL_ALL} */ private final @CrossProfileIntentFilter.AccessControlLevel int mCrossProfileIntentFilterAccessControl; private UserTypeDetails(@NonNull String name, boolean enabled, int maxAllowed, private UserTypeDetails(@NonNull String name, boolean enabled, int maxAllowed, @UserInfoFlag int baseType, @UserInfoFlag int defaultUserInfoPropertyFlags, int label, @UserInfoFlag int baseType, @UserInfoFlag int defaultUserInfoPropertyFlags, int label, int maxAllowedPerParent, int maxAllowedPerParent, Loading @@ -174,7 +182,8 @@ public final class UserTypeDetails { @Nullable Bundle defaultSecureSettings, @Nullable Bundle defaultSecureSettings, @Nullable List<DefaultCrossProfileIntentFilter> defaultCrossProfileIntentFilters, @Nullable List<DefaultCrossProfileIntentFilter> defaultCrossProfileIntentFilters, boolean isMediaSharedWithParent, boolean isMediaSharedWithParent, boolean isCredentialSharableWithParent) { boolean isCredentialSharableWithParent, @CrossProfileIntentFilter.AccessControlLevel int accessControlLevel) { this.mName = name; this.mName = name; this.mEnabled = enabled; this.mEnabled = enabled; this.mMaxAllowed = maxAllowed; this.mMaxAllowed = maxAllowed; Loading @@ -195,6 +204,7 @@ public final class UserTypeDetails { this.mDarkThemeBadgeColors = darkThemeBadgeColors; this.mDarkThemeBadgeColors = darkThemeBadgeColors; this.mIsMediaSharedWithParent = isMediaSharedWithParent; this.mIsMediaSharedWithParent = isMediaSharedWithParent; this.mIsCredentialSharableWithParent = isCredentialSharableWithParent; this.mIsCredentialSharableWithParent = isCredentialSharableWithParent; this.mCrossProfileIntentFilterAccessControl = accessControlLevel; } } /** /** Loading Loading @@ -327,6 +337,16 @@ public final class UserTypeDetails { return mIsCredentialSharableWithParent; return mIsCredentialSharableWithParent; } } /** * Returning user's {@link CrossProfileIntentFilter.AccessControlLevel}. If not explicitly * configured, default value is {@link CrossProfileIntentFilter#ACCESS_LEVEL_ALL} * @return user's {@link CrossProfileIntentFilter.AccessControlLevel} */ public @CrossProfileIntentFilter.AccessControlLevel int getCrossProfileIntentFilterAccessControl() { return mCrossProfileIntentFilterAccessControl; } /** Returns a {@link Bundle} representing the default user restrictions. */ /** Returns a {@link Bundle} representing the default user restrictions. */ @NonNull Bundle getDefaultRestrictions() { @NonNull Bundle getDefaultRestrictions() { return BundleUtils.clone(mDefaultRestrictions); return BundleUtils.clone(mDefaultRestrictions); Loading Loading @@ -420,6 +440,8 @@ public final class UserTypeDetails { private @DrawableRes int mBadgeNoBackground = Resources.ID_NULL; private @DrawableRes int mBadgeNoBackground = Resources.ID_NULL; private boolean mIsMediaSharedWithParent = false; private boolean mIsMediaSharedWithParent = false; private boolean mIsCredentialSharableWithParent = false; private boolean mIsCredentialSharableWithParent = false; private @CrossProfileIntentFilter.AccessControlLevel int mCrossProfileIntentFilterAccessControl = CrossProfileIntentFilter.ACCESS_LEVEL_ALL; public Builder setName(String name) { public Builder setName(String name) { mName = name; mName = name; Loading Loading @@ -519,6 +541,16 @@ public final class UserTypeDetails { return this; return this; } } /** * Sets {@link CrossProfileIntentFilter.AccessControlLevel} for the user. * @param accessControlLevel default access control for user */ public Builder setCrossProfileIntentFilterAccessControl( @CrossProfileIntentFilter.AccessControlLevel int accessControlLevel) { mCrossProfileIntentFilterAccessControl = accessControlLevel; return this; } /** /** * Sets shared media property for the user. * Sets shared media property for the user. * @param isCredentialSharableWithParent the value to be set, true or false * @param isCredentialSharableWithParent the value to be set, true or false Loading Loading @@ -571,7 +603,8 @@ public final class UserTypeDetails { mDefaultSecureSettings, mDefaultSecureSettings, mDefaultCrossProfileIntentFilters, mDefaultCrossProfileIntentFilters, mIsMediaSharedWithParent, mIsMediaSharedWithParent, mIsCredentialSharableWithParent); mIsCredentialSharableWithParent, mCrossProfileIntentFilterAccessControl); } } private boolean hasBadge() { private boolean hasBadge() { Loading services/core/java/com/android/server/pm/UserTypeFactory.java +2 −0 Original line number Original line Diff line number Diff line Loading @@ -122,6 +122,8 @@ public final class UserTypeFactory { .setLabel(0) .setLabel(0) .setDefaultRestrictions(null) .setDefaultRestrictions(null) .setIsMediaSharedWithParent(true) .setIsMediaSharedWithParent(true) .setCrossProfileIntentFilterAccessControl( CrossProfileIntentFilter.ACCESS_LEVEL_SYSTEM) .setIsCredentialSharableWithParent(true); .setIsCredentialSharableWithParent(true); } } Loading Loading
services/core/java/com/android/server/pm/CrossProfileIntentFilter.java +62 −1 Original line number Original line Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.pm; package com.android.server.pm; import android.annotation.IntDef; import android.content.Intent; import android.content.Intent; import android.content.IntentFilter; import android.content.IntentFilter; import android.os.UserHandle; import android.os.UserHandle; Loading @@ -30,6 +31,8 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** /** * The {@link PackageManagerService} maintains some {@link CrossProfileIntentFilter}s for each user. * The {@link PackageManagerService} maintains some {@link CrossProfileIntentFilter}s for each user. Loading @@ -41,13 +44,52 @@ class CrossProfileIntentFilter extends WatchedIntentFilter { private static final String ATTR_FLAGS = "flags"; private static final String ATTR_FLAGS = "flags"; private static final String ATTR_OWNER_PACKAGE = "ownerPackage"; private static final String ATTR_OWNER_PACKAGE = "ownerPackage"; private static final String ATTR_FILTER = "filter"; private static final String ATTR_FILTER = "filter"; private static final String ATTR_ACCESS_CONTROL = "accessControl"; private static final String TAG = "CrossProfileIntentFilter"; private static final String TAG = "CrossProfileIntentFilter"; /** * AccessControlLevel provides level of access for user to create/modify * {@link CrossProfileIntentFilter} in {@link com.android.server.pm.Settings}. * Each AccessControlLevel have value assigned, the higher the value * implies higher restriction for creation/modification. * AccessControlLevel allows us to protect against malicious changes in user's * {@link CrossProfileIntentFilter}s, which might add/remove {@link CrossProfileIntentFilter} * leading to unprecedented results. */ @IntDef(prefix = {"ACCESS_LEVEL_"}, value = { ACCESS_LEVEL_ALL, ACCESS_LEVEL_SYSTEM, ACCESS_LEVEL_SYSTEM_ADD_ONLY, }) @Retention(RetentionPolicy.SOURCE) public @interface AccessControlLevel { } /** * ACCESS_LEVEL_ALL signifies that irrespective of user we would allow * access(addition/modification/removal) for CrossProfileIntentFilter. * This is the default access control level. */ public static final int ACCESS_LEVEL_ALL = 0; /** * ACCESS_LEVEL_SYSTEM signifies that only system/root user would be able to * access(addition/modification/removal) CrossProfileIntentFilter. */ public static final int ACCESS_LEVEL_SYSTEM = 10; /** * ACCESS_LEVEL_SYSTEM_ADD_ONLY signifies that only system/root user would be able to add * CrossProfileIntentFilter but not modify/remove. Once added, it cannot be modified or removed. */ public static final int ACCESS_LEVEL_SYSTEM_ADD_ONLY = 20; // If the intent matches the IntentFilter, then it can be forwarded to this userId. // If the intent matches the IntentFilter, then it can be forwarded to this userId. final int mTargetUserId; final int mTargetUserId; final String mOwnerPackage; // packageName of the app. final String mOwnerPackage; // packageName of the app. final int mFlags; final int mFlags; final int mAccessControlLevel; // The cache for snapshots, so they are not rebuilt if the base object has not // The cache for snapshots, so they are not rebuilt if the base object has not // changed. // changed. Loading @@ -65,10 +107,16 @@ class CrossProfileIntentFilter extends WatchedIntentFilter { CrossProfileIntentFilter(IntentFilter filter, String ownerPackage, int targetUserId, CrossProfileIntentFilter(IntentFilter filter, String ownerPackage, int targetUserId, int flags) { int flags) { this(filter, ownerPackage, targetUserId, flags, ACCESS_LEVEL_ALL); } CrossProfileIntentFilter(IntentFilter filter, String ownerPackage, int targetUserId, int flags, @AccessControlLevel int accessControlLevel) { super(filter); super(filter); mTargetUserId = targetUserId; mTargetUserId = targetUserId; mOwnerPackage = ownerPackage; mOwnerPackage = ownerPackage; mFlags = flags; mFlags = flags; mAccessControlLevel = accessControlLevel; mSnapshot = makeCache(); mSnapshot = makeCache(); } } Loading @@ -77,12 +125,18 @@ class CrossProfileIntentFilter extends WatchedIntentFilter { this(filter.mFilter, ownerPackage, targetUserId, flags); this(filter.mFilter, ownerPackage, targetUserId, flags); } } CrossProfileIntentFilter(WatchedIntentFilter filter, String ownerPackage, int targetUserId, int flags, @AccessControlLevel int accessControlLevel) { this(filter.mFilter, ownerPackage, targetUserId, flags, accessControlLevel); } // Copy constructor used only to create a snapshot. // Copy constructor used only to create a snapshot. private CrossProfileIntentFilter(CrossProfileIntentFilter f) { private CrossProfileIntentFilter(CrossProfileIntentFilter f) { super(f); super(f); mTargetUserId = f.mTargetUserId; mTargetUserId = f.mTargetUserId; mOwnerPackage = f.mOwnerPackage; mOwnerPackage = f.mOwnerPackage; mFlags = f.mFlags; mFlags = f.mFlags; mAccessControlLevel = f.mAccessControlLevel; mSnapshot = new SnapshotCache.Sealed(); mSnapshot = new SnapshotCache.Sealed(); } } Loading @@ -98,9 +152,14 @@ class CrossProfileIntentFilter extends WatchedIntentFilter { return mOwnerPackage; return mOwnerPackage; } } public @AccessControlLevel int getAccessControlLevel() { return mAccessControlLevel; } CrossProfileIntentFilter(TypedXmlPullParser parser) throws XmlPullParserException, IOException { CrossProfileIntentFilter(TypedXmlPullParser parser) throws XmlPullParserException, IOException { mTargetUserId = parser.getAttributeInt(null, ATTR_TARGET_USER_ID, UserHandle.USER_NULL); mTargetUserId = parser.getAttributeInt(null, ATTR_TARGET_USER_ID, UserHandle.USER_NULL); mOwnerPackage = getStringFromXml(parser, ATTR_OWNER_PACKAGE, ""); mOwnerPackage = getStringFromXml(parser, ATTR_OWNER_PACKAGE, ""); mAccessControlLevel = parser.getAttributeInt(null, ATTR_ACCESS_CONTROL, ACCESS_LEVEL_ALL); mFlags = parser.getAttributeInt(null, ATTR_FLAGS, 0); mFlags = parser.getAttributeInt(null, ATTR_FLAGS, 0); mSnapshot = makeCache(); mSnapshot = makeCache(); Loading Loading @@ -151,6 +210,7 @@ class CrossProfileIntentFilter extends WatchedIntentFilter { serializer.attributeInt(null, ATTR_TARGET_USER_ID, mTargetUserId); serializer.attributeInt(null, ATTR_TARGET_USER_ID, mTargetUserId); serializer.attributeInt(null, ATTR_FLAGS, mFlags); serializer.attributeInt(null, ATTR_FLAGS, mFlags); serializer.attribute(null, ATTR_OWNER_PACKAGE, mOwnerPackage); serializer.attribute(null, ATTR_OWNER_PACKAGE, mOwnerPackage); serializer.attributeInt(null, ATTR_ACCESS_CONTROL, mAccessControlLevel); serializer.startTag(null, ATTR_FILTER); serializer.startTag(null, ATTR_FILTER); mFilter.writeToXml(serializer); mFilter.writeToXml(serializer); serializer.endTag(null, ATTR_FILTER); serializer.endTag(null, ATTR_FILTER); Loading @@ -165,7 +225,8 @@ class CrossProfileIntentFilter extends WatchedIntentFilter { boolean equalsIgnoreFilter(CrossProfileIntentFilter other) { boolean equalsIgnoreFilter(CrossProfileIntentFilter other) { return mTargetUserId == other.mTargetUserId return mTargetUserId == other.mTargetUserId && mOwnerPackage.equals(other.mOwnerPackage) && mOwnerPackage.equals(other.mOwnerPackage) && mFlags == other.mFlags; && mFlags == other.mFlags && mAccessControlLevel == other.mAccessControlLevel; } } public CrossProfileIntentFilter snapshot() { public CrossProfileIntentFilter snapshot() { Loading
services/core/java/com/android/server/pm/PackageManagerService.java +13 −2 Original line number Original line Diff line number Diff line Loading @@ -3350,6 +3350,12 @@ public class PackageManagerService implements PackageSender, TestUtilityService android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null); android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null); int callingUid = Binder.getCallingUid(); int callingUid = Binder.getCallingUid(); enforceOwnerRights(snapshot, ownerPackage, callingUid); enforceOwnerRights(snapshot, ownerPackage, callingUid); // Verifying that current calling uid should be able to add {@link CrossProfileIntentFilter} // for source and target user mUserManager.enforceCrossProfileIntentFilterAccess(sourceUserId, targetUserId, callingUid, /* addCrossProfileIntentFilter */ true); PackageManagerServiceUtils.enforceShellRestriction(mInjector.getUserManagerInternal(), PackageManagerServiceUtils.enforceShellRestriction(mInjector.getUserManagerInternal(), UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, sourceUserId); UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, sourceUserId); if (intentFilter.countActions() == 0) { if (intentFilter.countActions() == 0) { Loading @@ -3358,7 +3364,8 @@ public class PackageManagerService implements PackageSender, TestUtilityService } } synchronized (mLock) { synchronized (mLock) { CrossProfileIntentFilter newFilter = new CrossProfileIntentFilter(intentFilter, CrossProfileIntentFilter newFilter = new CrossProfileIntentFilter(intentFilter, ownerPackage, targetUserId, flags); ownerPackage, targetUserId, flags, mUserManager .getCrossProfileIntentFilterAccessControl(sourceUserId, targetUserId)); CrossProfileIntentResolver resolver = CrossProfileIntentResolver resolver = mSettings.editCrossProfileIntentResolverLPw(sourceUserId); mSettings.editCrossProfileIntentResolverLPw(sourceUserId); ArrayList<CrossProfileIntentFilter> existing = resolver.findFilters(intentFilter); ArrayList<CrossProfileIntentFilter> existing = resolver.findFilters(intentFilter); Loading Loading @@ -4572,7 +4579,11 @@ public class PackageManagerService implements PackageSender, TestUtilityService ArraySet<CrossProfileIntentFilter> set = ArraySet<CrossProfileIntentFilter> set = new ArraySet<>(resolver.filterSet()); new ArraySet<>(resolver.filterSet()); for (CrossProfileIntentFilter filter : set) { for (CrossProfileIntentFilter filter : set) { if (filter.getOwnerPackage().equals(ownerPackage)) { //Only remove if calling user is allowed based on access control of // {@link CrossProfileIntentFilter} if (filter.getOwnerPackage().equals(ownerPackage) && mUserManager.isCrossProfileIntentFilterAccessible(sourceUserId, filter.mTargetUserId, /* addCrossProfileIntentFilter */ false)) { resolver.removeFilter(filter); resolver.removeFilter(filter); } } } } Loading
services/core/java/com/android/server/pm/UserManagerService.java +97 −0 Original line number Original line Diff line number Diff line Loading @@ -1866,6 +1866,103 @@ public class UserManagerService extends IUserManager.Stub { return mLocalService.exists(userId); return mLocalService.exists(userId); } } /** * Returns user's {@link CrossProfileIntentFilter.AccessControlLevel}, which is derived from * {@link UserTypeDetails}. If user does not have defined their access control level, * returns default {@link CrossProfileIntentFilter#ACCESS_LEVEL_ALL} */ private @CrossProfileIntentFilter.AccessControlLevel int getCrossProfileIntentFilterAccessControl(@UserIdInt int userId) { final UserTypeDetails userTypeDetails = getUserTypeDetailsNoChecks(userId); return userTypeDetails != null ? userTypeDetails.getCrossProfileIntentFilterAccessControl() : CrossProfileIntentFilter.ACCESS_LEVEL_ALL; } /** * Verifies if calling user is allowed to access {@link CrossProfileIntentFilter} between given * source and target user. * @param sourceUserId userId for which CrossProfileIntentFilter would be configured * @param targetUserId target user where we can resolve given intent filter * @param callingUid user accessing api * @param addCrossProfileIntentFilter if the operation is addition or not. * @throws SecurityException is calling user is not allowed to access. */ public void enforceCrossProfileIntentFilterAccess( int sourceUserId, int targetUserId, int callingUid, boolean addCrossProfileIntentFilter) { if (!isCrossProfileIntentFilterAccessible(sourceUserId, targetUserId, addCrossProfileIntentFilter)) { throw new SecurityException("CrossProfileIntentFilter cannot be accessed by user " + callingUid); } } /** * Checks if {@link CrossProfileIntentFilter} can be accessed by calling user for given source * and target user. There are following rules of access * 1. For {@link CrossProfileIntentFilter#ACCESS_LEVEL_ALL}, * irrespective of user we would allow access(addition/modification/removal) * 2. For {@link CrossProfileIntentFilter#ACCESS_LEVEL_SYSTEM}, * only system/root user would be able to access(addition/modification/removal) * 3. For {@link CrossProfileIntentFilter#ACCESS_LEVEL_SYSTEM_ADD_ONLY}, * only system/root user would be able to add but not modify/remove. Once added, it cannot be * modified or removed * @param sourceUserId userId for which CrossProfileIntentFilter would be configured * @param targetUserId target user where we can resolve given intent filter * @param addCrossProfileIntentFilter if the operation is addition or not. * @return true if {@link CrossProfileIntentFilter} can be accessed by calling user */ public boolean isCrossProfileIntentFilterAccessible(int sourceUserId, int targetUserId, boolean addCrossProfileIntentFilter) { int effectiveAccessControl = getCrossProfileIntentFilterAccessControl(sourceUserId, targetUserId); /* For {@link CrossProfileIntentFilter#ACCESS_LEVEL_SYSTEM}, if accessing user is not system or root disallowing access to {@link CrossProfileIntentFilter} */ if (CrossProfileIntentFilter.ACCESS_LEVEL_SYSTEM == effectiveAccessControl && !PackageManagerServiceUtils.isSystemOrRoot()) { return false; } /* For {@link CrossProfileIntentFilter#ACCESS_LEVEL_SYSTEM_ADD_ONLY}, allowing only system user to add {@link CrossProfileIntentFilter}. All users(including system) are disallowed to modify/remove. */ if (CrossProfileIntentFilter.ACCESS_LEVEL_SYSTEM_ADD_ONLY == effectiveAccessControl && (!addCrossProfileIntentFilter || !PackageManagerServiceUtils.isSystemOrRoot())) { return false; } return true; } /** * Returns {@link CrossProfileIntentFilter.AccessControlLevel} * that should be assigned to {@link CrossProfileIntentFilter} * computed from source user's and target user's * {@link CrossProfileIntentFilter.AccessControlLevel}. * The Access Level is configured per {@link CrossProfileIntentFilter} and its property of edge * between source and target user e.g. for all {@link CrossProfileIntentFilter}s configured * between Primary user and Clone profile should have access level of * {@link CrossProfileIntentFilter#ACCESS_LEVEL_SYSTEM} which is driven by highest * access value from source or target. The higher value means higher restrictions. * @param sourceUserId userId of source user for whom CrossProfileIntentFilter will be stored * @param targetUserId userId of target user for whom Cross Profile access would be allowed * @return least privileged {@link CrossProfileIntentFilter.AccessControlLevel} from source or * target user. */ public @CrossProfileIntentFilter.AccessControlLevel int getCrossProfileIntentFilterAccessControl(int sourceUserId, int targetUserId) { int sourceAccessControlLevel, targetAccessControlLevel, effectiveAccessControl; sourceAccessControlLevel = getCrossProfileIntentFilterAccessControl(sourceUserId); targetAccessControlLevel = getCrossProfileIntentFilterAccessControl(targetUserId); effectiveAccessControl = Math.max(sourceAccessControlLevel, targetAccessControlLevel); return effectiveAccessControl; } @Override @Override public void setUserName(@UserIdInt int userId, String name) { public void setUserName(@UserIdInt int userId, String name) { checkManageUsersPermission("rename users"); checkManageUsersPermission("rename users"); Loading
services/core/java/com/android/server/pm/UserTypeDetails.java +35 −2 Original line number Original line Diff line number Diff line Loading @@ -163,6 +163,14 @@ public final class UserTypeDetails { */ */ private final boolean mIsCredentialSharableWithParent; private final boolean mIsCredentialSharableWithParent; /** * Denotes the default access control for {@link CrossProfileIntentFilter} of user profile. * * <p> Default value is {@link CrossProfileIntentFilter#ACCESS_LEVEL_ALL} */ private final @CrossProfileIntentFilter.AccessControlLevel int mCrossProfileIntentFilterAccessControl; private UserTypeDetails(@NonNull String name, boolean enabled, int maxAllowed, private UserTypeDetails(@NonNull String name, boolean enabled, int maxAllowed, @UserInfoFlag int baseType, @UserInfoFlag int defaultUserInfoPropertyFlags, int label, @UserInfoFlag int baseType, @UserInfoFlag int defaultUserInfoPropertyFlags, int label, int maxAllowedPerParent, int maxAllowedPerParent, Loading @@ -174,7 +182,8 @@ public final class UserTypeDetails { @Nullable Bundle defaultSecureSettings, @Nullable Bundle defaultSecureSettings, @Nullable List<DefaultCrossProfileIntentFilter> defaultCrossProfileIntentFilters, @Nullable List<DefaultCrossProfileIntentFilter> defaultCrossProfileIntentFilters, boolean isMediaSharedWithParent, boolean isMediaSharedWithParent, boolean isCredentialSharableWithParent) { boolean isCredentialSharableWithParent, @CrossProfileIntentFilter.AccessControlLevel int accessControlLevel) { this.mName = name; this.mName = name; this.mEnabled = enabled; this.mEnabled = enabled; this.mMaxAllowed = maxAllowed; this.mMaxAllowed = maxAllowed; Loading @@ -195,6 +204,7 @@ public final class UserTypeDetails { this.mDarkThemeBadgeColors = darkThemeBadgeColors; this.mDarkThemeBadgeColors = darkThemeBadgeColors; this.mIsMediaSharedWithParent = isMediaSharedWithParent; this.mIsMediaSharedWithParent = isMediaSharedWithParent; this.mIsCredentialSharableWithParent = isCredentialSharableWithParent; this.mIsCredentialSharableWithParent = isCredentialSharableWithParent; this.mCrossProfileIntentFilterAccessControl = accessControlLevel; } } /** /** Loading Loading @@ -327,6 +337,16 @@ public final class UserTypeDetails { return mIsCredentialSharableWithParent; return mIsCredentialSharableWithParent; } } /** * Returning user's {@link CrossProfileIntentFilter.AccessControlLevel}. If not explicitly * configured, default value is {@link CrossProfileIntentFilter#ACCESS_LEVEL_ALL} * @return user's {@link CrossProfileIntentFilter.AccessControlLevel} */ public @CrossProfileIntentFilter.AccessControlLevel int getCrossProfileIntentFilterAccessControl() { return mCrossProfileIntentFilterAccessControl; } /** Returns a {@link Bundle} representing the default user restrictions. */ /** Returns a {@link Bundle} representing the default user restrictions. */ @NonNull Bundle getDefaultRestrictions() { @NonNull Bundle getDefaultRestrictions() { return BundleUtils.clone(mDefaultRestrictions); return BundleUtils.clone(mDefaultRestrictions); Loading Loading @@ -420,6 +440,8 @@ public final class UserTypeDetails { private @DrawableRes int mBadgeNoBackground = Resources.ID_NULL; private @DrawableRes int mBadgeNoBackground = Resources.ID_NULL; private boolean mIsMediaSharedWithParent = false; private boolean mIsMediaSharedWithParent = false; private boolean mIsCredentialSharableWithParent = false; private boolean mIsCredentialSharableWithParent = false; private @CrossProfileIntentFilter.AccessControlLevel int mCrossProfileIntentFilterAccessControl = CrossProfileIntentFilter.ACCESS_LEVEL_ALL; public Builder setName(String name) { public Builder setName(String name) { mName = name; mName = name; Loading Loading @@ -519,6 +541,16 @@ public final class UserTypeDetails { return this; return this; } } /** * Sets {@link CrossProfileIntentFilter.AccessControlLevel} for the user. * @param accessControlLevel default access control for user */ public Builder setCrossProfileIntentFilterAccessControl( @CrossProfileIntentFilter.AccessControlLevel int accessControlLevel) { mCrossProfileIntentFilterAccessControl = accessControlLevel; return this; } /** /** * Sets shared media property for the user. * Sets shared media property for the user. * @param isCredentialSharableWithParent the value to be set, true or false * @param isCredentialSharableWithParent the value to be set, true or false Loading Loading @@ -571,7 +603,8 @@ public final class UserTypeDetails { mDefaultSecureSettings, mDefaultSecureSettings, mDefaultCrossProfileIntentFilters, mDefaultCrossProfileIntentFilters, mIsMediaSharedWithParent, mIsMediaSharedWithParent, mIsCredentialSharableWithParent); mIsCredentialSharableWithParent, mCrossProfileIntentFilterAccessControl); } } private boolean hasBadge() { private boolean hasBadge() { Loading
services/core/java/com/android/server/pm/UserTypeFactory.java +2 −0 Original line number Original line Diff line number Diff line Loading @@ -122,6 +122,8 @@ public final class UserTypeFactory { .setLabel(0) .setLabel(0) .setDefaultRestrictions(null) .setDefaultRestrictions(null) .setIsMediaSharedWithParent(true) .setIsMediaSharedWithParent(true) .setCrossProfileIntentFilterAccessControl( CrossProfileIntentFilter.ACCESS_LEVEL_SYSTEM) .setIsCredentialSharableWithParent(true); .setIsCredentialSharableWithParent(true); } } Loading