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

Commit 7202272e authored by Rubin Xu's avatar Rubin Xu Committed by Android (Google) Code Review
Browse files

Merge "Enable device management role holder to set app restrictions" into main

parents ae5c99eb 6a152f70
Loading
Loading
Loading
Loading
+29 −6
Original line number Diff line number Diff line
@@ -10284,6 +10284,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
@@ -10299,11 +10309,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();
            }
@@ -11704,11 +11717,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();
            }
@@ -13986,9 +14002,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
@@ -203,6 +203,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
@@ -11509,10 +11509,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,
@@ -11520,12 +11527,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(
@@ -11541,6 +11542,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)))
@@ -12872,7 +12924,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()) {
@@ -12891,6 +12943,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)))
@@ -15811,19 +15907,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;
            });
        }