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

Commit 36df85a2 authored by Alex Johnston's avatar Alex Johnston Committed by Android (Google) Code Review
Browse files

Merge "Disable IME on the personal profile"

parents 9fcdc982 e066d89b
Loading
Loading
Loading
Loading
+39 −7
Original line number Diff line number Diff line
@@ -8675,6 +8675,19 @@ public class DevicePolicyManager {
     * Called by a profile or device owner to set the permitted input methods services for this
     * user. By default, the user can use any input method.
     * <p>
     * This method can be called on the {@link DevicePolicyManager} instance,
     * returned by {@link #getParentProfileInstance(ComponentName)}, where the caller must be
     * a profile owner of an organization-owned device.
     * <p>
     * If called on the parent instance:
     * <ul>
     *    <li>The permitted input methods will be applied on the personal profile</li>
     *    <li>Can only permit all input methods (calling this method with a {@code null} package
     *    list) or only permit system input methods (calling this method with an empty package
     *    list). This is to prevent the caller from learning which packages are installed on
     *    the personal side</li>
     * </ul>
     * <p>
     * When zero or more packages have been added, input method that are not in the list and not
     * part of the system can not be enabled by the user. This method will fail if it is called for
     * a admin that is not for the foreground user or a profile of the foreground user. Any
@@ -8689,14 +8702,18 @@ public class DevicePolicyManager {
     * @param packageNames List of input method package names.
     * @return {@code true} if the operation succeeded, or {@code false} if the list didn't
     *        contain every enabled non-system input method service.
     * @throws SecurityException if {@code admin} is not a device or profile owner.
     * @throws SecurityException if {@code admin} is not a device, profile owner or if called on
     *                           the parent profile and the {@code admin} is not a profile owner
     *                           of an organization-owned managed profile.
     * @throws IllegalArgumentException if called on the parent profile, the {@code admin} is a
     *                           profile owner of an organization-owned managed profile and the
     *                           list of permitted input method package names is not null or empty.
     */
    public boolean setPermittedInputMethods(
            @NonNull ComponentName admin, List<String> packageNames) {
        throwIfParentInstance("setPermittedInputMethods");
        if (mService != null) {
            try {
                return mService.setPermittedInputMethods(admin, packageNames);
                return mService.setPermittedInputMethods(admin, packageNames, mParentInstance);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
@@ -8708,18 +8725,25 @@ public class DevicePolicyManager {
    /**
     * Returns the list of permitted input methods set by this device or profile owner.
     * <p>
     * This method can be called on the {@link DevicePolicyManager} instance,
     * returned by {@link #getParentProfileInstance(ComponentName)}, where the caller must be
     * a profile owner of an organization-owned managed profile. If called on the parent instance,
     * then the returned list of permitted input methods are those which are applied on the
     * personal profile.
     * <p>
     * An empty list means no input methods except system input methods are allowed. Null means all
     * input methods are allowed.
     *
     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
     * @return List of input method package names.
     * @throws SecurityException if {@code admin} is not a device or profile owner.
     * @throws SecurityException if {@code admin} is not a device, profile owner or if called on
     *                           the parent profile and the {@code admin} is not a profile owner
     *                           of an organization-owned managed profile.
     */
    public @Nullable List<String> getPermittedInputMethods(@NonNull ComponentName admin) {
        throwIfParentInstance("getPermittedInputMethods");
        if (mService != null) {
            try {
                return mService.getPermittedInputMethods(admin);
                return mService.getPermittedInputMethods(admin, mParentInstance);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
@@ -8729,6 +8753,11 @@ public class DevicePolicyManager {
    /**
     * Called by the system to check if a specific input method is disabled by admin.
     * <p>
     * This method can be called on the {@link DevicePolicyManager} instance,
     * returned by {@link #getParentProfileInstance(ComponentName)}. If called on the parent
     * instance, this method will check whether the given input method is permitted on
     * the personal profile.
     *
     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
     * @param packageName Input method package name that needs to be checked.
@@ -8741,7 +8770,8 @@ public class DevicePolicyManager {
            @NonNull String packageName, int userHandle) {
        if (mService != null) {
            try {
                return mService.isInputMethodPermittedByAdmin(admin, packageName, userHandle);
                return mService.isInputMethodPermittedByAdmin(admin, packageName, userHandle,
                        mParentInstance);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
@@ -10828,6 +10858,8 @@ public class DevicePolicyManager {
     * <li>{@link #setCameraDisabled}</li>
     * <li>{@link #getCameraDisabled}</li>
     * <li>{@link #setAccountManagementDisabled(ComponentName, String, boolean)}</li>
     * <li>{@link #setPermittedInputMethods}</li>
     * <li>{@link #getPermittedInputMethods}</li>
     * </ul>
     *
     * <p>The following methods can be called by the profile owner of a managed profile
+3 −3
Original line number Diff line number Diff line
@@ -232,10 +232,10 @@ interface IDevicePolicyManager {
    List getPermittedAccessibilityServicesForUser(int userId);
    boolean isAccessibilityServicePermittedByAdmin(in ComponentName admin, String packageName, int userId);

    boolean setPermittedInputMethods(in ComponentName admin,in List packageList);
    List getPermittedInputMethods(in ComponentName admin);
    boolean setPermittedInputMethods(in ComponentName admin,in List packageList, boolean parent);
    List getPermittedInputMethods(in ComponentName admin, boolean parent);
    List getPermittedInputMethodsForCurrentUser();
    boolean isInputMethodPermittedByAdmin(in ComponentName admin, String packageName, int userId);
    boolean isInputMethodPermittedByAdmin(in ComponentName admin, String packageName, int userId, boolean parent);

    boolean setPermittedCrossProfileNotificationListeners(in ComponentName admin, in List<String> packageList);
    List<String> getPermittedCrossProfileNotificationListeners(in ComponentName admin);
+17 −8
Original line number Diff line number Diff line
@@ -267,19 +267,28 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils {
            permitted = dpm.isInputMethodPermittedByAdmin(admin.component,
                    packageName, userId);
        }

        boolean permittedByParentAdmin = true;
        EnforcedAdmin profileAdmin = null;
        int managedProfileId = getManagedProfileId(context, userId);
        EnforcedAdmin profileAdmin = getProfileOrDeviceOwner(context,
                getUserHandleOf(managedProfileId));
        boolean permittedByProfileAdmin = true;
        if (profileAdmin != null) {
            permittedByProfileAdmin = dpm.isInputMethodPermittedByAdmin(profileAdmin.component,
                    packageName, managedProfileId);
        if (managedProfileId != UserHandle.USER_NULL) {
            profileAdmin = getProfileOrDeviceOwner(context, getUserHandleOf(managedProfileId));
            // If the device is an organization-owned device with a managed profile, the
            // managedProfileId will be used instead of the affected userId. This is because
            // isInputMethodPermittedByAdmin is called on the parent DPM instance, which will
            // return results affecting the personal profile.
            if (profileAdmin != null && dpm.isOrganizationOwnedDeviceWithManagedProfile()) {
                DevicePolicyManager parentDpm = sProxy.getParentProfileInstance(dpm,
                        UserManager.get(context).getUserInfo(managedProfileId));
                permittedByParentAdmin = parentDpm.isInputMethodPermittedByAdmin(
                        profileAdmin.component, packageName, managedProfileId);
            }
        if (!permitted && !permittedByProfileAdmin) {
        }
        if (!permitted && !permittedByParentAdmin) {
            return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
        } else if (!permitted) {
            return admin;
        } else if (!permittedByProfileAdmin) {
        } else if (!permittedByParentAdmin) {
            return profileAdmin;
        }
        return null;
+48 −19
Original line number Diff line number Diff line
@@ -378,6 +378,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
    private static final String CREDENTIAL_MANAGEMENT_APP = "credentialManagementApp";
    private static final String NOT_CREDENTIAL_MANAGEMENT_APP = "notCredentialManagementApp";
    private static final String NULL_STRING_ARRAY = "nullStringArray";
    // Comprehensive list of delegations.
    private static final String DELEGATIONS[] = {
        DELEGATION_CERT_INSTALL,
@@ -9577,59 +9579,86 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
    }
    @Override
    public boolean setPermittedInputMethods(ComponentName who, List packageList) {
    public boolean setPermittedInputMethods(ComponentName who, List packageList,
            boolean calledOnParentInstance) {
        if (!mHasFeature) {
            return false;
        }
        Objects.requireNonNull(who, "ComponentName is null");
        final CallerIdentity caller = getCallerIdentity(who);
        final int userId = getProfileParentUserIfRequested(
                caller.getUserId(), calledOnParentInstance);
        if (calledOnParentInstance) {
            Preconditions.checkCallAuthorization(isProfileOwnerOfOrganizationOwnedDevice(caller));
            Preconditions.checkArgument(packageList == null || packageList.isEmpty(),
                    "Permitted input methods must allow all input methods or only "
                            + "system input methods when called on the parent instance of an "
                            + "organization-owned device");
        } else {
            Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
        }
        if (packageList != null) {
            List<InputMethodInfo> enabledImes = InputMethodManagerInternal.get()
                    .getEnabledInputMethodListAsUser(caller.getUserId());
            List<InputMethodInfo> enabledImes = mInjector.binderWithCleanCallingIdentity(() ->
                    InputMethodManagerInternal.get().getEnabledInputMethodListAsUser(userId));
            if (enabledImes != null) {
                List<String> enabledPackages = new ArrayList<String>();
                for (InputMethodInfo ime : enabledImes) {
                    enabledPackages.add(ime.getPackageName());
                }
                if (!checkPackagesInPermittedListOrSystem(enabledPackages, packageList,
                        caller.getUserId())) {
                    Slog.e(LOG_TAG, "Cannot set permitted input methods, "
                            + "because it contains already enabled input method.");
                        userId)) {
                    Slog.e(LOG_TAG, "Cannot set permitted input methods, because the list of "
                            + "permitted input methods excludes an already-enabled input method.");
                    return false;
                }
            }
        }
        synchronized (getLockObject()) {
            ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
            final ActiveAdmin admin = getParentOfAdminIfRequired(
                    getProfileOwnerOrDeviceOwnerLocked(caller), calledOnParentInstance);
            admin.permittedInputMethods = packageList;
            saveSettingsLocked(caller.getUserId());
        }
        final String[] packageArray =
                packageList != null ? ((List<String>) packageList).toArray(new String[0]) : null;
        DevicePolicyEventLogger
                .createEvent(DevicePolicyEnums.SET_PERMITTED_INPUT_METHODS)
                .setAdmin(who)
                .setStrings(packageArray)
                .setStrings(getStringArrayForLogging(packageList, calledOnParentInstance))
                .write();
        return true;
    }
    private String[] getStringArrayForLogging(List list, boolean calledOnParentInstance) {
        List<String> stringList = new ArrayList<String>();
        stringList.add(calledOnParentInstance ? CALLED_FROM_PARENT : NOT_CALLED_FROM_PARENT);
        if (list == null) {
            stringList.add(NULL_STRING_ARRAY);
        } else {
            stringList.addAll((List<String>) list);
        }
        return stringList.toArray(new String[0]);
    }
    @Override
    public List getPermittedInputMethods(ComponentName who) {
    public List getPermittedInputMethods(ComponentName who, boolean calledOnParentInstance) {
        if (!mHasFeature) {
            return null;
        }
        Objects.requireNonNull(who, "ComponentName is null");
        final CallerIdentity caller = getCallerIdentity(who);
        if (calledOnParentInstance) {
            Preconditions.checkCallAuthorization(isProfileOwnerOfOrganizationOwnedDevice(caller));
        } else {
            Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
        }
        synchronized (getLockObject()) {
            ActiveAdmin admin = getProfileOwnerOrDeviceOwnerLocked(caller);
            final ActiveAdmin admin = getParentOfAdminIfRequired(
                    getProfileOwnerOrDeviceOwnerLocked(caller), calledOnParentInstance);
            return admin.permittedInputMethods;
        }
    }
@@ -9642,9 +9671,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
        synchronized (getLockObject()) {
            List<String> result = null;
            // Only device or profile owners can have permitted lists set.
            DevicePolicyData policy = getUserDataUnchecked(caller.getUserId());
            for (int i = 0; i < policy.mAdminList.size(); i++) {
                ActiveAdmin admin = policy.mAdminList.get(i);
            List<ActiveAdmin> admins = getActiveAdminsForAffectedUserLocked(caller.getUserId());
            for (ActiveAdmin admin: admins) {
                List<String> fromAdmin = admin.permittedInputMethods;
                if (fromAdmin != null) {
                    if (result == null) {
@@ -9675,7 +9703,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
    @Override
    public boolean isInputMethodPermittedByAdmin(ComponentName who, String packageName,
            int userHandle) {
            int userHandle, boolean calledOnParentInstance) {
        if (!mHasFeature) {
            return true;
        }
@@ -9684,7 +9712,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
        enforceSystemCaller("query if an input method is disabled by admin");
        synchronized (getLockObject()) {
            ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle);
            ActiveAdmin admin = getParentOfAdminIfRequired(
                    getActiveAdminUncheckedLocked(who, userHandle), calledOnParentInstance);
            if (admin == null) {
                return false;
            }
+26 −0
Original line number Diff line number Diff line
@@ -2333,6 +2333,32 @@ public class DevicePolicyManagerTest extends DpmTestBase {
        assertThat(actualAccounts).containsExactlyElementsIn(expectedAccounts);
    }

    @Test
    public void testSetPermittedInputMethodsWithPOOfOrganizationOwnedDevice()
            throws Exception {
        String packageName = "com.google.pkg.one";
        setupProfileOwner();
        configureProfileOwnerOfOrgOwnedDevice(admin1, CALLER_USER_HANDLE);

        // Allow all input methods
        parentDpm.setPermittedInputMethods(admin1, null);

        assertThat(parentDpm.getPermittedInputMethods(admin1)).isNull();

        // Allow only system input methods
        parentDpm.setPermittedInputMethods(admin1, new ArrayList<>());

        assertThat(parentDpm.getPermittedInputMethods(admin1)).isEmpty();

        // Don't allow specific third party input methods
        final List<String> inputMethods = Collections.singletonList(packageName);

        assertExpectException(IllegalArgumentException.class, /* messageRegex= */ "Permitted "
                        + "input methods must allow all input methods or only system input methods "
                        + "when called on the parent instance of an organization-owned device",
                () -> parentDpm.setPermittedInputMethods(admin1, inputMethods));
    }

    @Test
    public void testSetKeyguardDisabledFeaturesWithDO() throws Exception {
        mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;