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

Commit 6a152f70 authored by Rubin Xu's avatar Rubin Xu
Browse files

Enable device management role holder to set app restrictions

Give DMRH the ability to set app restrictions on any target applications
in the calling user, as well as the parent user of a COPE profile. App
restrictions will be automatically propagated to the private space if they
are set on the parent user of a COPE profile.

App restrictions set by the DMRH are handled by policy engine as coexisable.
Existing app restrictions set by DPC are kept untouched so they are
mostly indepenent from the ones set by the DMRH.

Bug: 304969881
Test: ApplicationRestrictionsTest

Change-Id: Iffb8a0b4a7bf14350f52676d5d7d9d75a1c96393
parent c6d3da58
Loading
Loading
Loading
Loading
+29 −6
Original line number Diff line number Diff line
@@ -10285,6 +10285,16 @@ public class DevicePolicyManager {
     * get the list of app restrictions set by each admin via
     * {@link android.content.RestrictionsManager#getApplicationRestrictionsPerAdmin}.
     *
     * <p>Starting from Android Version {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM},
     * the device policy management role holder can also set app restrictions on any applications
     * in the calling user, as well as the parent user of an organization-owned managed profile via
     * the {@link DevicePolicyManager} instance returned by
     * {@link #getParentProfileInstance(ComponentName)}. App restrictions set by the device policy
     * management role holder are not returned by
     * {@link UserManager#getApplicationRestrictions(String)}. The target application should use
     * {@link android.content.RestrictionsManager#getApplicationRestrictionsPerAdmin} to retrieve
     * them, alongside any app restrictions the profile or device owner might have set.
     *
     * <p>NOTE: The method performs disk I/O and shouldn't be called on the main thread
     *
     * @param admin Which {@link DeviceAdminReceiver} this request is associated with, or
@@ -10300,11 +10310,14 @@ public class DevicePolicyManager {
    @WorkerThread
    public void setApplicationRestrictions(@Nullable ComponentName admin, String packageName,
            Bundle settings) {
        if (!Flags.dmrhCanSetAppRestriction()) {
            throwIfParentInstance("setApplicationRestrictions");
        }
        if (mService != null) {
            try {
                mService.setApplicationRestrictions(admin, mContext.getPackageName(), packageName,
                        settings);
                        settings, mParentInstance);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
@@ -11705,11 +11718,14 @@ public class DevicePolicyManager {
    @WorkerThread
    public @NonNull Bundle getApplicationRestrictions(
            @Nullable ComponentName admin, String packageName) {
        if (!Flags.dmrhCanSetAppRestriction()) {
            throwIfParentInstance("getApplicationRestrictions");
        }
        if (mService != null) {
            try {
                return mService.getApplicationRestrictions(admin, mContext.getPackageName(),
                        packageName);
                        packageName, mParentInstance);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
@@ -13989,9 +14005,16 @@ public class DevicePolicyManager {
    public @NonNull DevicePolicyManager getParentProfileInstance(@NonNull ComponentName admin) {
        throwIfParentInstance("getParentProfileInstance");
        try {
            if (Flags.dmrhCanSetAppRestriction()) {
                UserManager um = mContext.getSystemService(UserManager.class);
                if (!um.isManagedProfile()) {
                    throw new SecurityException("The current user does not have a parent profile.");
                }
            } else {
                if (!mService.isManagedProfile(admin)) {
                    throw new SecurityException("The current user does not have a parent profile.");
                }
            }
            return new DevicePolicyManager(mContext, mService, true);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
+2 −2
Original line number Diff line number Diff line
@@ -244,8 +244,8 @@ interface IDevicePolicyManager {
    void setDefaultSmsApplication(in ComponentName admin, String callerPackageName, String packageName, boolean parent);
    void setDefaultDialerApplication(String packageName);

    void setApplicationRestrictions(in ComponentName who, in String callerPackage, in String packageName, in Bundle settings);
    Bundle getApplicationRestrictions(in ComponentName who, in String callerPackage, in String packageName);
    void setApplicationRestrictions(in ComponentName who, in String callerPackage, in String packageName, in Bundle settings, in boolean parent);
    Bundle getApplicationRestrictions(in ComponentName who, in String callerPackage, in String packageName, in boolean parent);
    boolean setApplicationRestrictionsManagingPackage(in ComponentName admin, in String packageName);
    String getApplicationRestrictionsManagingPackage(in ComponentName admin);
    boolean isCallerApplicationRestrictionsManagingPackage(in String callerPackage);
+7 −0
Original line number Diff line number Diff line
@@ -190,6 +190,13 @@ flag {
    }
}

flag {
  name: "dmrh_can_set_app_restriction"
  namespace: "enterprise"
  description: "Allow DMRH to set application restrictions (both on the profile and the parent)"
  bug: "328758346"
}

flag {
  name: "allow_screen_brightness_control_on_cope"
  namespace: "enterprise"
+2 −2
Original line number Diff line number Diff line
@@ -92,7 +92,7 @@ final class BundlePolicySerializer extends PolicySerializer<Bundle> {
                while (count > 0 && (type = parser.next()) != XmlPullParser.END_DOCUMENT) {
                    if (type == XmlPullParser.START_TAG
                            && parser.getName().equals(TAG_VALUE)) {
                        values.add(parser.nextText().trim());
                        values.add(parser.nextText());
                        count--;
                    }
                }
@@ -111,7 +111,7 @@ final class BundlePolicySerializer extends PolicySerializer<Bundle> {
                restrictions.putParcelableArray(key,
                        bundleList.toArray(new Bundle[bundleList.size()]));
            } else {
                String value = parser.nextText().trim();
                String value = parser.nextText();
                if (ATTR_TYPE_BOOLEAN.equals(valType)) {
                    restrictions.putBoolean(key, Boolean.parseBoolean(value));
                } else if (ATTR_TYPE_INTEGER.equals(valType)) {
+108 −15
Original line number Diff line number Diff line
@@ -11502,10 +11502,17 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
    @Override
    public void setApplicationRestrictions(ComponentName who, String callerPackage,
            String packageName, Bundle restrictions) {
            String packageName, Bundle restrictions, boolean parent) {
        final CallerIdentity caller = getCallerIdentity(who, callerPackage);
        checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_APPLICATION_RESTRICTIONS);
        // This check is eventually made in UMS, checking here to fail early.
        String validationResult =
                FrameworkParsingPackageUtils.validateName(packageName, false, false);
        if (validationResult != null) {
            throw new IllegalArgumentException("Invalid package name: " + validationResult);
        }
        if (isUnicornFlagEnabled()) {
            EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
                    who,
@@ -11513,12 +11520,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
                    caller.getPackageName(),
                    caller.getUserId()
            );
            // This check is eventually made in UMS, checking here to fail early.
            String validationResult =
                    FrameworkParsingPackageUtils.validateName(packageName, false, false);
            if (validationResult != null) {
                throw new IllegalArgumentException("Invalid package name: " + validationResult);
            }
            if (restrictions == null || restrictions.isEmpty()) {
                mDevicePolicyEngine.removeLocalPolicy(
@@ -11534,6 +11535,57 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
            }
            setBackwardsCompatibleAppRestrictions(
                    caller, packageName, restrictions, caller.getUserHandle());
        } else if (Flags.dmrhCanSetAppRestriction()) {
            final boolean isRoleHolder;
            if (who != null) {
                // DO or PO
                Preconditions.checkCallAuthorization(
                        (isProfileOwner(caller) || isDefaultDeviceOwner(caller)));
                Preconditions.checkCallAuthorization(!parent,
                        "DO or PO cannot call this on parent");
                // Caller has opted to be treated as DPC (by passing a non-null who), so don't
                // consider it as the DMRH, even if the caller is both the DPC and the DMRH.
                isRoleHolder = false;
            } else {
                // Delegates, or the DMRH. Only DMRH can call this on COPE parent
                isRoleHolder = isCallerDevicePolicyManagementRoleHolder(caller);
                if (parent) {
                    Preconditions.checkCallAuthorization(isRoleHolder);
                    Preconditions.checkState(isOrganizationOwnedDeviceWithManagedProfile(),
                            "Role Holder can only operate parent app restriction on COPE devices");
                } else {
                    Preconditions.checkCallAuthorization(isRoleHolder
                            || isCallerDelegate(caller, DELEGATION_APP_RESTRICTIONS));
                }
            }
            // DMRH caller uses policy engine, others still use legacy code path
            if (isRoleHolder) {
                EnforcingAdmin enforcingAdmin = getEnforcingAdminForCaller(/* who */ null,
                        caller.getPackageName());
                int affectedUserId = parent
                        ? getProfileParentId(caller.getUserId()) : caller.getUserId();
                if (restrictions == null || restrictions.isEmpty()) {
                    mDevicePolicyEngine.removeLocalPolicy(
                            PolicyDefinition.APPLICATION_RESTRICTIONS(packageName),
                            enforcingAdmin,
                            affectedUserId);
                } else {
                    mDevicePolicyEngine.setLocalPolicy(
                            PolicyDefinition.APPLICATION_RESTRICTIONS(packageName),
                            enforcingAdmin,
                            new BundlePolicyValue(restrictions),
                            affectedUserId);
                }
                Intent changeIntent = new Intent(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED);
                changeIntent.setPackage(packageName);
                changeIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
                mContext.sendBroadcastAsUser(changeIntent, UserHandle.of(affectedUserId));
            } else {
                mInjector.binderWithCleanCallingIdentity(() -> {
                    mUserManager.setApplicationRestrictions(packageName, restrictions,
                            caller.getUserHandle());
                });
            }
        } else {
            Preconditions.checkCallAuthorization((caller.hasAdminComponent()
                    && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
@@ -12865,7 +12917,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
    @Override
    public Bundle getApplicationRestrictions(ComponentName who, String callerPackage,
            String packageName) {
            String packageName, boolean parent) {
        final CallerIdentity caller = getCallerIdentity(who, callerPackage);
        if (isUnicornFlagEnabled()) {
@@ -12884,6 +12936,50 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
                return Bundle.EMPTY;
            }
            return policies.get(enforcingAdmin).getValue();
        } else if (Flags.dmrhCanSetAppRestriction()) {
            final boolean isRoleHolder;
            if (who != null) {
                // Caller is DO or PO. They cannot call this on parent
                Preconditions.checkCallAuthorization(!parent
                        && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)));
                // Caller has opted to be treated as DPC (by passing a non-null who), so don't
                // consider it as the DMRH, even if the caller is both the DPC and the DMRH.
                isRoleHolder = false;
            } else {
                // Caller is delegates or the DMRH. Only DMRH can call this on parent
                isRoleHolder = isCallerDevicePolicyManagementRoleHolder(caller);
                if (parent) {
                    Preconditions.checkCallAuthorization(isRoleHolder);
                    Preconditions.checkState(isOrganizationOwnedDeviceWithManagedProfile(),
                            "Role Holder can only operate parent app restriction on COPE devices");
                } else {
                    Preconditions.checkCallAuthorization(isRoleHolder
                            || isCallerDelegate(caller, DELEGATION_APP_RESTRICTIONS));
                }
            }
            if (isRoleHolder) {
                EnforcingAdmin enforcingAdmin = getEnforcingAdminForCaller(/* who */ null,
                        caller.getPackageName());
                int affectedUserId = parent
                        ? getProfileParentId(caller.getUserId()) : caller.getUserId();
                LinkedHashMap<EnforcingAdmin, PolicyValue<Bundle>> policies =
                        mDevicePolicyEngine.getLocalPoliciesSetByAdmins(
                                PolicyDefinition.APPLICATION_RESTRICTIONS(packageName),
                                affectedUserId);
                if (!policies.containsKey(enforcingAdmin)) {
                    return Bundle.EMPTY;
                }
                return policies.get(enforcingAdmin).getValue();
            } else {
                return mInjector.binderWithCleanCallingIdentity(() -> {
                    Bundle bundle = mUserManager.getApplicationRestrictions(packageName,
                            caller.getUserHandle());
                    // if no restrictions were saved, mUserManager.getApplicationRestrictions
                    // returns null, but DPM method should return an empty Bundle as per JavaDoc
                    return bundle != null ? bundle : Bundle.EMPTY;
                });
            }
        } else {
            Preconditions.checkCallAuthorization((caller.hasAdminComponent()
                    && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
@@ -15804,19 +15900,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
            for (EnforcingAdmin admin : policies.keySet()) {
                restrictions.add(policies.get(admin).getValue());
            }
            if (!restrictions.isEmpty()) {
                return restrictions;
            }
            return mInjector.binderWithCleanCallingIdentity(() -> {
                // Could be a device that has a DPC that hasn't migrated yet, so just return any
                // Could be a device that has a DPC that hasn't migrated yet, so also return any
                // restrictions saved in userManager.
                Bundle bundle = mUserManager.getApplicationRestrictions(
                        packageName, UserHandle.of(userId));
                if (bundle == null || bundle.isEmpty()) {
                    return new ArrayList<>();
                if (bundle != null && !bundle.isEmpty()) {
                    restrictions.add(bundle);
                }
                return List.of(bundle);
                return restrictions;
            });
        }