Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2074b25a authored by bkchoi's avatar bkchoi
Browse files

Implement System Authority EnforcingAdmin.

Added new hidden API for system services to set/clear user resrictions
on a specified target user. This will be used for Automotive passenger
users (visible background users) in multi-user multi-display
configurations.

Bug: 324899199
Flag: EXEMPT hidden API
Test: atest FrameworksServicesTests:com.android.server.devicepolicy.DevicePolicyManagerTest
Test: manual test using MD emulator.
      visually check in Car Settings.
      $ adb shell dumpsys user
      $ adb shell dumpsys device_policy

Change-Id: I8993417786cfe4b608b49150cda73b3cb77908ea
parent 8788db00
Loading
Loading
Loading
Loading
+57 −1
Original line number Diff line number Diff line
@@ -11963,6 +11963,34 @@ public class DevicePolicyManager {
        }
    }
    /**
     * Adds a user restriction on {@code targetUser}, specified by the {@code key}.
     *
     * <p>Called by a system service only, meaning that the caller's UID must be equal to
     * {@link Process#SYSTEM_UID}.
     *
     * @param systemEntity  The service entity that adds the restriction. A user restriction set by
     *                       a service entity can only be cleared by the same entity. This can be
     *                       just the calling package name, or any string of the caller's choice
     *                       can be used.
     * @param key  The key of the restriction.
     * @param targetUser  The user to add the restriction on.
     * @throws SecurityException if the caller is not a system service
     *
     * @hide
     */
    public void addUserRestriction(@NonNull String systemEntity,
            @NonNull @UserManager.UserRestrictionKey String key, @UserIdInt int targetUser) {
        if (mService != null) {
            try {
                mService.setUserRestrictionForUser(
                        systemEntity, key, /* enable= */ true, targetUser);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
    }
    /**
     * Called by a profile owner, device owner or a holder of any permission that is associated with
     *  a user restriction to set a user restriction specified by the provided {@code key} globally
@@ -11971,7 +11999,7 @@ public class DevicePolicyManager {
     * <p>For a given user, a restriction will be set if it was applied globally or locally by any
     * admin.
     *
     * <p> The calling device admin must be a profile owner, device owner or or a holder of any
     * <p> The calling device admin must be a profile owner, device owner or a holder of any
     * permission that is associated with a user restriction; if it is not, a security
     * exception will be thrown.
     *
@@ -12071,6 +12099,34 @@ public class DevicePolicyManager {
        }
    }
    /**
     * Clears a user restriction from {@code targetUser}, specified by the {@code key}.
     *
     * <p>Called by a system service only, meaning that the caller's UID must be equal to
     * {@link Process#SYSTEM_UID}.
     *
     * @param systemEntity  The system entity that clears the restriction. A user restriction
     *                         set by a system entity can only be cleared by the same entity. This
     *                         can be just the calling package name, or any string of the caller's
     *                         choice can be used.
     * @param key  The key of the restriction.
     * @param targetUser  The user to clear the restriction from.
     * @throws SecurityException if the caller is not a system service
     *
     * @hide
     */
    public void clearUserRestriction(@NonNull String systemEntity,
            @NonNull @UserManager.UserRestrictionKey String key, @UserIdInt int targetUser) {
        if (mService != null) {
            try {
                mService.setUserRestrictionForUser(
                        systemEntity, key, /* enable= */ false, targetUser);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
    }
    /**
     * Called by an admin to get user restrictions set by themselves with
     * {@link #addUserRestriction(ComponentName, String)}.
+1 −0
Original line number Diff line number Diff line
@@ -255,6 +255,7 @@ interface IDevicePolicyManager {
    ComponentName getRestrictionsProvider(int userHandle);

    void setUserRestriction(in ComponentName who, in String callerPackage, in String key, boolean enable, boolean parent);
    void setUserRestrictionForUser(in String systemEntity, in String key, boolean enable, int targetUser);
    void setUserRestrictionGlobally(in String callerPackage, in String key);
    Bundle getUserRestrictions(in ComponentName who, in String callerPackage, boolean parent);
    Bundle getUserRestrictionsGlobally(in String callerPackage);
+28 −5
Original line number Diff line number Diff line
@@ -13618,7 +13618,28 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
            setBackwardCompatibleUserRestriction(
                    caller, admin, key, enabledFromThisOwner, parent);
        }
        logUserRestrictionCall(key, enabledFromThisOwner, parent, caller);
        logUserRestrictionCall(key, enabledFromThisOwner, parent, caller, affectedUserId);
    }
    @Override
    public void setUserRestrictionForUser(
            @NonNull String systemEntity, String key, boolean enabled, @UserIdInt int targetUser) {
        Objects.requireNonNull(systemEntity);
        CallerIdentity caller = getCallerIdentity();
        if (caller.getUid() != Process.SYSTEM_UID) {
            throw new SecurityException("Only system services can call setUserRestrictionForUser"
                    + " on a target user: " + targetUser);
        }
        if (VERBOSE_LOG) {
            Slogf.v(LOG_TAG, "Creating SystemEnforcingAdmin %s for calling package %s",
                    systemEntity, caller.getPackageName());
        }
        EnforcingAdmin admin = EnforcingAdmin.createSystemEnforcingAdmin(systemEntity);
        setLocalUserRestrictionInternal(admin, key, enabled, targetUser);
        logUserRestrictionCall(key, enabled, /* parent= */ false, caller, targetUser);
    }
    private void checkAdminCanSetRestriction(CallerIdentity caller, boolean parent, String key) {
@@ -13739,7 +13760,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
        setGlobalUserRestrictionInternal(admin, key, /* enabled= */ true);
        logUserRestrictionCall(key, /* enabled= */ true, /* parent= */ false, caller);
        logUserRestrictionCall(key, /* enabled= */ true, /* parent= */ false, caller,
                UserHandle.USER_ALL);
    }
    private void setLocalUserRestrictionInternal(
            EnforcingAdmin admin, String key, boolean enabled, int userId) {
@@ -13775,7 +13797,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
    }
    private void logUserRestrictionCall(
            String key, boolean enabled, boolean parent, CallerIdentity caller) {
            String key, boolean enabled, boolean parent, CallerIdentity caller, int targetUserId) {
        final int eventId = enabled
                ? DevicePolicyEnums.ADD_USER_RESTRICTION
                : DevicePolicyEnums.REMOVE_USER_RESTRICTION;
@@ -13791,8 +13813,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
            SecurityLog.writeEvent(eventTag, caller.getPackageName(), caller.getUserId(), key);
        }
        Slogf.i(LOG_TAG, "Changing user restriction %s to: %b caller: %s",
                key, enabled, caller.toString());
        Slogf.i(LOG_TAG, "Changing user restriction %s on %s to: %b caller: %s",
                key, (targetUserId == UserHandle.USER_ALL ? "all users" : ("user " + targetUserId)),
                enabled, caller.toString());
    }
    @Override
+70 −3
Original line number Diff line number Diff line
@@ -21,7 +21,6 @@ import android.annotation.Nullable;
import android.app.admin.Authority;
import android.app.admin.DeviceAdminAuthority;
import android.app.admin.DpcAuthority;
import android.app.admin.PackagePermissionPolicyKey;
import android.app.admin.RoleAuthority;
import android.app.admin.UnknownAuthority;
import android.content.ComponentName;
@@ -57,23 +56,29 @@ final class EnforcingAdmin {
    static final String TAG = "EnforcingAdmin";

    static final String ROLE_AUTHORITY_PREFIX = "role:";
    static final String SYSTEM_AUTHORITY_PREFIX = "system:";
    static final String DPC_AUTHORITY = "enterprise";
    static final String DEVICE_ADMIN_AUTHORITY = "device_admin";
    static final String DEFAULT_AUTHORITY = "default";

    private static final String ATTR_PACKAGE_NAME = "package-name";
    private static final String ATTR_SYSTEM_ENTITY = "system-entity";
    private static final String ATTR_CLASS_NAME = "class-name";
    private static final String ATTR_AUTHORITIES = "authorities";
    private static final String ATTR_AUTHORITIES_SEPARATOR = ";";
    private static final String ATTR_USER_ID = "user-id";
    private static final String ATTR_IS_ROLE = "is-role";
    private static final String ATTR_IS_SYSTEM = "is-system";

    private final String mPackageName;
    // Name of the system entity. Only used when mIsSystemAuthority is true.
    private final String mSystemEntity;
    // This is needed for DPCs and active admins
    private final ComponentName mComponentName;
    private Set<String> mAuthorities;
    private final int mUserId;
    private final boolean mIsRoleAuthority;
    private final boolean mIsSystemAuthority;
    private final ActiveAdmin mActiveAdmin;

    static EnforcingAdmin createEnforcingAdmin(@NonNull String packageName, int userId,
@@ -106,6 +111,11 @@ final class EnforcingAdmin {
                userId, activeAdmin);
    }

    static EnforcingAdmin createSystemEnforcingAdmin(@NonNull String systemEntity) {
        Objects.requireNonNull(systemEntity);
        return new EnforcingAdmin(systemEntity);
    }

    static EnforcingAdmin createEnforcingAdmin(android.app.admin.EnforcingAdmin admin) {
        Objects.requireNonNull(admin);
        Authority authority = admin.getAuthority();
@@ -127,6 +137,7 @@ final class EnforcingAdmin {
                    /* activeAdmin = */ null,
                    /* isRoleAuthority = */ true);
        }
        // TODO(b/324899199): Consider supporting android.app.admin.SystemAuthority.
        return new EnforcingAdmin(admin.getPackageName(), admin.getComponentName(),
                Set.of(), admin.getUserHandle().getIdentifier(),
                /* activeAdmin = */ null);
@@ -159,9 +170,11 @@ final class EnforcingAdmin {
        Objects.requireNonNull(packageName);
        Objects.requireNonNull(authorities);

        // Role authorities should not be using this constructor
        // Role/System authorities should not be using this constructor
        mIsRoleAuthority = false;
        mIsSystemAuthority = false;
        mPackageName = packageName;
        mSystemEntity = null;
        mComponentName = componentName;
        mAuthorities = new HashSet<>(authorities);
        mUserId = userId;
@@ -173,7 +186,9 @@ final class EnforcingAdmin {

        // Only role authorities use this constructor.
        mIsRoleAuthority = true;
        mIsSystemAuthority = false;
        mPackageName = packageName;
        mSystemEntity = null;
        mUserId = userId;
        mComponentName = null;
        // authorities will be loaded when needed
@@ -181,6 +196,21 @@ final class EnforcingAdmin {
        mActiveAdmin = activeAdmin;
    }

    /** Constructor for System authorities. */
    private EnforcingAdmin(@NonNull String systemEntity) {
        Objects.requireNonNull(systemEntity);

        // Only system authorities use this constructor.
        mIsSystemAuthority = true;
        mIsRoleAuthority = false;
        mPackageName = null;
        mSystemEntity = systemEntity;
        mUserId = UserHandle.USER_SYSTEM;
        mComponentName = null;
        mAuthorities = getSystemAuthority(systemEntity);
        mActiveAdmin = null;
    }

    private EnforcingAdmin(
            String packageName, @Nullable ComponentName componentName, Set<String> authorities,
            int userId, @Nullable ActiveAdmin activeAdmin, boolean isRoleAuthority) {
@@ -188,7 +218,9 @@ final class EnforcingAdmin {
        Objects.requireNonNull(authorities);

        mIsRoleAuthority = isRoleAuthority;
        mIsSystemAuthority = false;
        mPackageName = packageName;
        mSystemEntity = null;
        mComponentName = componentName;
        mAuthorities = new HashSet<>(authorities);
        mUserId = userId;
@@ -204,6 +236,18 @@ final class EnforcingAdmin {
        return authorities.isEmpty() ? Set.of(DEFAULT_AUTHORITY) : authorities;
    }

    /**
     * Returns a set of authorities for system authority.
     *
     * <p>Note that a system authority enforcing admin has only one authority that has the package
     * name of the calling system service. Therefore, the returned set always contains one element.
     */
    private static Set<String> getSystemAuthority(String systemEntity) {
        Set<String> authorities = new HashSet<>();
        authorities.add(SYSTEM_AUTHORITY_PREFIX + systemEntity);
        return authorities;
    }

    // TODO(b/259042794): move this logic to RoleManagerLocal
    private static Set<String> getRoles(String packageName, int userId) {
        RoleManagerLocal roleManagerLocal = LocalManagerRegistry.getManager(
@@ -264,6 +308,7 @@ final class EnforcingAdmin {
        } else if (mAuthorities.contains(DEVICE_ADMIN_AUTHORITY)) {
            authority = DeviceAdminAuthority.DEVICE_ADMIN_AUTHORITY;
        } else {
            // For now, System Authority returns UNKNOWN_AUTHORITY.
            authority = UnknownAuthority.UNKNOWN_AUTHORITY;
        }
        return new android.app.admin.EnforcingAdmin(
@@ -291,8 +336,10 @@ final class EnforcingAdmin {
        if (o == null || getClass() != o.getClass()) return false;
        EnforcingAdmin other = (EnforcingAdmin) o;
        return Objects.equals(mPackageName, other.mPackageName)
                && Objects.equals(mSystemEntity, other.mSystemEntity)
                && Objects.equals(mComponentName, other.mComponentName)
                && Objects.equals(mIsRoleAuthority, other.mIsRoleAuthority)
                && (mIsSystemAuthority == other.mIsSystemAuthority)
                && hasMatchingAuthorities(this, other);
    }

@@ -307,6 +354,8 @@ final class EnforcingAdmin {
    public int hashCode() {
        if (mIsRoleAuthority) {
            return Objects.hash(mPackageName, mUserId);
        } else if (mIsSystemAuthority) {
            return Objects.hash(mSystemEntity);
        } else {
            return Objects.hash(
                    mComponentName == null ? mPackageName : mComponentName,
@@ -318,8 +367,12 @@ final class EnforcingAdmin {
    void saveToXml(TypedXmlSerializer serializer) throws IOException {
        serializer.attribute(/* namespace= */ null, ATTR_PACKAGE_NAME, mPackageName);
        serializer.attributeBoolean(/* namespace= */ null, ATTR_IS_ROLE, mIsRoleAuthority);
        serializer.attributeBoolean(/* namespace= */ null, ATTR_IS_SYSTEM, mIsSystemAuthority);
        serializer.attributeInt(/* namespace= */ null, ATTR_USER_ID, mUserId);
        if (!mIsRoleAuthority) {
        if (mIsSystemAuthority) {
            serializer.attribute(/* namespace= */ null, ATTR_SYSTEM_ENTITY, mSystemEntity);
        }
        if (!mIsRoleAuthority && !mIsSystemAuthority) {
            if (mComponentName != null) {
                serializer.attribute(
                        /* namespace= */ null, ATTR_CLASS_NAME, mComponentName.getClassName());
@@ -336,7 +389,10 @@ final class EnforcingAdmin {
    static EnforcingAdmin readFromXml(TypedXmlPullParser parser)
            throws XmlPullParserException {
        String packageName = parser.getAttributeValue(/* namespace= */ null, ATTR_PACKAGE_NAME);
        String systemEntity = parser.getAttributeValue(/* namespace= */ null, ATTR_SYSTEM_ENTITY);
        boolean isRoleAuthority = parser.getAttributeBoolean(/* namespace= */ null, ATTR_IS_ROLE);
        boolean isSystemAuthority = parser.getAttributeBoolean(
                /* namespace= */ null, ATTR_IS_SYSTEM, /* defaultValue= */ false);
        String authoritiesStr = parser.getAttributeValue(/* namespace= */ null, ATTR_AUTHORITIES);
        int userId = parser.getAttributeInt(/* namespace= */ null, ATTR_USER_ID);

@@ -348,6 +404,13 @@ final class EnforcingAdmin {
            }
            // TODO(b/281697976): load active admin
            return new EnforcingAdmin(packageName, userId, null);
        } else if (isSystemAuthority) {
            if (systemEntity == null) {
                Slogf.wtf(TAG, "Error parsing EnforcingAdmin with SystemAuthority, "
                        + "systemEntity is null.");
                return null;
            }
            return new EnforcingAdmin(systemEntity);
        } else {
            if (packageName == null || authoritiesStr == null) {
                Slogf.wtf(TAG, "Error parsing EnforcingAdmin, packageName is "
@@ -381,6 +444,10 @@ final class EnforcingAdmin {
        sb.append(mUserId);
        sb.append(", mIsRoleAuthority= ");
        sb.append(mIsRoleAuthority);
        sb.append(", mIsSystemAuthority= ");
        sb.append(mIsSystemAuthority);
        sb.append(", mSystemEntity = ");
        sb.append(mSystemEntity);
        sb.append(" }");
        return sb.toString();
    }