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

Commit ad99d757 authored by Salud Lemus's avatar Salud Lemus
Browse files

Use device owner type to restrict a financed device owner

The restriction is to limit the APIs a financed device owner can
call.

Bug: 215754643
Test: atest com.android.server.devicepolicy.DevicePolicyManagerTest

Change-Id: Ic38b50d7388b277ad74d2c22182bc1e711a1aea4
parent 1e4e6962
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -461,6 +461,7 @@ package android.app.admin {
    method @RequiresPermission(android.Manifest.permission.FORCE_DEVICE_POLICY_MANAGER_LOGS) public long forceSecurityLogs();
    method public void forceUpdateUserSetupComplete(int);
    method @NonNull public java.util.Set<java.lang.String> getDefaultCrossProfilePackages();
    method public int getDeviceOwnerType(@NonNull android.content.ComponentName);
    method @NonNull public java.util.Set<java.lang.String> getDisallowedSystemApps(@NonNull android.content.ComponentName, int, @NonNull String);
    method public long getLastBugReportRequestTime();
    method public long getLastNetworkLogRetrievalTime();
@@ -476,6 +477,7 @@ package android.app.admin {
    method @RequiresPermission(allOf={android.Manifest.permission.MANAGE_DEVICE_ADMINS, android.Manifest.permission.INTERACT_ACROSS_USERS_FULL}) public void setActiveAdmin(@NonNull android.content.ComponentName, boolean, int);
    method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public boolean setDeviceOwner(@NonNull android.content.ComponentName, @Nullable String, int);
    method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public boolean setDeviceOwnerOnly(@NonNull android.content.ComponentName, @Nullable String, int);
    method public void setDeviceOwnerType(@NonNull android.content.ComponentName, int);
    method @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public void setNextOperationSafety(int, int);
    field public static final String ACTION_DATA_SHARING_RESTRICTION_APPLIED = "android.app.action.DATA_SHARING_RESTRICTION_APPLIED";
    field public static final String ACTION_DEVICE_POLICY_CONSTANTS_CHANGED = "android.app.action.DEVICE_POLICY_CONSTANTS_CHANGED";
+48 −0
Original line number Diff line number Diff line
@@ -454,6 +454,52 @@ public class DevicePolicyManager {
     * <li>{@link #EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE}, optional</li>
     * </ul>
     *
     * <p>Once the device admin app is set as the device owner, the following APIs are available for
     * managing polices on the device:
     * <ul>
     * <li>{@link #isDeviceManaged()}</li>
     * <li>{@link #isUninstallBlocked(ComponentName, String)}</li>
     * <li>{@link #setUninstallBlocked(ComponentName, String, boolean)}</li>
     * <li>{@link #setUserControlDisabledPackages(ComponentName, List)}</li>
     * <li>{@link #getUserControlDisabledPackages(ComponentName)}</li>
     * <li>{@link #setOrganizationName(ComponentName, CharSequence)}</li>
     * <li>{@link #setShortSupportMessage(ComponentName, CharSequence)}</li>
     * <li>{@link #isBackupServiceEnabled(ComponentName)}</li>
     * <li>{@link #setBackupServiceEnabled(ComponentName, boolean)}</li>
     * <li>{@link #isLockTaskPermitted(String)}</li>
     * <li>{@link #setLockTaskFeatures(ComponentName, int)}, where the following lock task features
     * can be set (otherwise a {@link SecurityException} will be thrown):</li>
     * <ul>
     *     <li>{@link #LOCK_TASK_FEATURE_SYSTEM_INFO}</li>
     *     <li>{@link #LOCK_TASK_FEATURE_KEYGUARD}</li>
     *     <li>{@link #LOCK_TASK_FEATURE_HOME}</li>
     *     <li>{@link #LOCK_TASK_FEATURE_GLOBAL_ACTIONS}</li>
     *     <li>{@link #LOCK_TASK_FEATURE_NOTIFICATIONS}</li>
     * </ul>
     * <li>{@link #setLockTaskPackages(ComponentName, String[])}</li>
     * <li>{@link #addPersistentPreferredActivity(ComponentName, IntentFilter, ComponentName)}</li>
     * <li>{@link #clearPackagePersistentPreferredActivities(ComponentName, String)} </li>
     * <li>{@link #wipeData(int)}</li>
     * <li>{@link #isDeviceOwnerApp(String)}</li>
     * <li>{@link #clearDeviceOwnerApp(String)}</li>
     * <li>{@link #setPermissionGrantState(ComponentName, String, String, int)}, where
     * {@link permission#READ_PHONE_STATE} is the <b>only</b> permission that can be
     * {@link #PERMISSION_GRANT_STATE_GRANTED}, {@link #PERMISSION_GRANT_STATE_DENIED}, or
     * {@link #PERMISSION_GRANT_STATE_DEFAULT} and can <b>only</b> be applied to the device admin
     * app (otherwise a {@link SecurityException} will be thrown)</li>
     * <li>{@link #addUserRestriction(ComponentName, String)}, where the following user restrictions
     * are permitted (otherwise a {@link SecurityException} will be thrown):</li>
     * <ul>
     *     <li>{@link UserManager#DISALLOW_ADD_USER}</li>
     *     <li>{@link UserManager#DISALLOW_DEBUGGING_FEATURES}</li>
     *     <li>{@link UserManager#DISALLOW_INSTALL_UNKNOWN_SOURCES}</li>
     *     <li>{@link UserManager#DISALLOW_SAFE_BOOT}</li>
     *     <li>{@link UserManager#DISALLOW_CONFIG_DATE_TIME}</li>
     *     <li>{@link UserManager#DISALLOW_OUTGOING_CALLS}</li>
     * </ul>
     * <li>{@link #clearUserRestriction(ComponentName, String)}</li>
     * </ul>
     *
     * @hide
     */
    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
@@ -14577,6 +14623,7 @@ public class DevicePolicyManager {
     *
     * @hide
     */
    @TestApi
    public void setDeviceOwnerType(@NonNull ComponentName admin,
            @DeviceOwnerType int deviceOwnerType) {
        throwIfParentInstance("setDeviceOwnerType");
@@ -14600,6 +14647,7 @@ public class DevicePolicyManager {
     *
     * @hide
     */
    @TestApi
    @DeviceOwnerType
    public int getDeviceOwnerType(@NonNull ComponentName admin) {
        throwIfParentInstance("getDeviceOwnerType");
+22 −0
Original line number Diff line number Diff line
@@ -284,6 +284,19 @@ public class UserRestrictionsUtils {
            UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY
    );

    /**
     * User restrictions available to a device owner whose type is
     * {@link android.app.admin.DevicePolicyManager#DEVICE_OWNER_TYPE_FINANCED}.
     */
    private static final Set<String> FINANCED_DEVICE_OWNER_RESTRICTIONS = Sets.newArraySet(
            UserManager.DISALLOW_ADD_USER,
            UserManager.DISALLOW_DEBUGGING_FEATURES,
            UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
            UserManager.DISALLOW_SAFE_BOOT,
            UserManager.DISALLOW_CONFIG_DATE_TIME,
            UserManager.DISALLOW_OUTGOING_CALLS
    );

    /**
     * Returns whether the given restriction name is valid (and logs it if it isn't).
     */
@@ -457,6 +470,15 @@ public class UserRestrictionsUtils {
        return DEFAULT_ENABLED_FOR_MANAGED_PROFILES;
    }

    /**
     * @return {@code true} only if the restriction is allowed for financed devices and can be set
     * by a device owner. Otherwise, {@code false} would be returned.
     */
    public static boolean canFinancedDeviceOwnerChange(String restriction) {
        return FINANCED_DEVICE_OWNER_RESTRICTIONS.contains(restriction)
                && canDeviceOwnerChange(restriction);
    }

    /**
     * Whether given user restriction should be enforced globally.
     */
+337 −192

File changed.

Preview size limit exceeded, changes collapsed.

+302 −0
Original line number Diff line number Diff line
@@ -28,6 +28,14 @@ import static android.app.admin.DevicePolicyManager.ID_TYPE_BASE_INFO;
import static android.app.admin.DevicePolicyManager.ID_TYPE_IMEI;
import static android.app.admin.DevicePolicyManager.ID_TYPE_MEID;
import static android.app.admin.DevicePolicyManager.ID_TYPE_SERIAL;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_GLOBAL_ACTIONS;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_HOME;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_KEYGUARD;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NONE;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_NOTIFICATIONS;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_OVERVIEW;
import static android.app.admin.DevicePolicyManager.LOCK_TASK_FEATURE_SYSTEM_INFO;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_LOW;
import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_MEDIUM;
@@ -101,6 +109,7 @@ import android.app.admin.WifiSsidPolicy;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -138,6 +147,9 @@ import com.android.internal.widget.LockscreenCredential;
import com.android.server.LocalServices;
import com.android.server.SystemService;
import com.android.server.devicepolicy.DevicePolicyManagerService.RestrictionsListener;
import com.android.server.pm.RestrictionsSet;
import com.android.server.pm.UserManagerInternal;
import com.android.server.pm.UserRestrictionsUtils;

import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
@@ -7763,6 +7775,296 @@ public class DevicePolicyManagerTest extends DpmTestBase {
        assertThrows(IllegalStateException.class, () -> dpm.getDeviceOwnerType(admin2));
    }

    @Test
    public void testSetUserRestriction_financeDo_invalidRestrictions_restrictionNotSet()
            throws Exception {
        setDeviceOwner();
        dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);

        for (String restriction : UserRestrictionsUtils.USER_RESTRICTIONS) {
            if (!UserRestrictionsUtils.canFinancedDeviceOwnerChange(restriction)) {
                assertNoDeviceOwnerRestrictions();
                assertExpectException(SecurityException.class, /* messageRegex= */ null,
                        () -> dpm.addUserRestriction(admin1, restriction));

                verify(getServices().userManagerInternal, never())
                        .setDevicePolicyUserRestrictions(anyInt(), any(), any(), anyBoolean());
            }
        }
    }

    @Test
    public void testSetUserRestriction_financeDo_validRestrictions_setsRestriction()
            throws Exception {
        setDeviceOwner();
        dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);

        for (String restriction : UserRestrictionsUtils.USER_RESTRICTIONS) {
            if (UserRestrictionsUtils.canFinancedDeviceOwnerChange(restriction)) {
                assertNoDeviceOwnerRestrictions();
                dpm.addUserRestriction(admin1, restriction);

                Bundle globalRestrictions =
                        dpms.getDeviceOwnerAdminLocked().getGlobalUserRestrictions(
                                UserManagerInternal.OWNER_TYPE_DEVICE_OWNER);
                RestrictionsSet localRestrictions = new RestrictionsSet();
                localRestrictions.updateRestrictions(
                        UserHandle.USER_SYSTEM,
                        dpms.getDeviceOwnerAdminLocked().getLocalUserRestrictions(
                                UserManagerInternal.OWNER_TYPE_DEVICE_OWNER));
                verify(getServices().userManagerInternal)
                        .setDevicePolicyUserRestrictions(eq(UserHandle.USER_SYSTEM),
                                MockUtils.checkUserRestrictions(globalRestrictions),
                                MockUtils.checkUserRestrictions(
                                        UserHandle.USER_SYSTEM, localRestrictions),
                                eq(true));
                reset(getServices().userManagerInternal);

                dpm.clearUserRestriction(admin1, restriction);
                reset(getServices().userManagerInternal);
            }
        }
    }

    @Test
    public void testSetLockTaskFeatures_financeDo_validLockTaskFeatures_lockTaskFeaturesSet()
            throws Exception {
        int validLockTaskFeatures = LOCK_TASK_FEATURE_SYSTEM_INFO | LOCK_TASK_FEATURE_KEYGUARD
                | LOCK_TASK_FEATURE_HOME | LOCK_TASK_FEATURE_GLOBAL_ACTIONS
                | LOCK_TASK_FEATURE_NOTIFICATIONS;
        setDeviceOwner();
        dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);

        dpm.setLockTaskFeatures(admin1, validLockTaskFeatures);

        verify(getServices().iactivityTaskManager)
                .updateLockTaskFeatures(eq(UserHandle.USER_SYSTEM), eq(validLockTaskFeatures));
    }

    @Test
    public void testSetLockTaskFeatures_financeDo_invalidLockTaskFeatures_throwsException()
            throws Exception {
        int invalidLockTaskFeatures = LOCK_TASK_FEATURE_NONE | LOCK_TASK_FEATURE_OVERVIEW
                | LOCK_TASK_FEATURE_HOME | LOCK_TASK_FEATURE_BLOCK_ACTIVITY_START_IN_TASK;
        setDeviceOwner();
        dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
        // Called during setup.
        verify(getServices().iactivityTaskManager).updateLockTaskFeatures(anyInt(), anyInt());

        assertExpectException(SecurityException.class, /* messageRegex= */ null,
                () -> dpm.setLockTaskFeatures(admin1, invalidLockTaskFeatures));

        verifyNoMoreInteractions(getServices().iactivityTaskManager);
    }

    @Test
    public void testIsUninstallBlocked_financeDo_success() throws Exception {
        String packageName = "com.android.foo.package";
        setDeviceOwner();
        dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
        when(getServices().ipackageManager.getBlockUninstallForUser(
                eq(packageName), eq(UserHandle.USER_SYSTEM)))
                .thenReturn(true);

        assertThat(dpm.isUninstallBlocked(admin1, packageName)).isTrue();
    }

    @Test
    public void testSetUninstallBlocked_financeDo_success() throws Exception {
        String packageName = "com.android.foo.package";
        setDeviceOwner();
        dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);

        dpm.setUninstallBlocked(admin1, packageName, false);

        verify(getServices().ipackageManager)
                .setBlockUninstallForUser(eq(packageName), eq(false),
                        eq(UserHandle.USER_SYSTEM));
    }

    @Test
    public void testSetUserControlDisabledPackages_financeDo_success() throws Exception {
        List<String> packages = new ArrayList<>();
        packages.add("com.android.foo.package");
        setDeviceOwner();
        dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);

        dpm.setUserControlDisabledPackages(admin1, packages);

        verify(getServices().packageManagerInternal)
                .setDeviceOwnerProtectedPackages(eq(admin1.getPackageName()), eq(packages));
    }

    @Test
    public void testGetUserControlDisabledPackages_financeDo_success() throws Exception {
        setDeviceOwner();
        dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);

        assertThat(dpm.getUserControlDisabledPackages(admin1)).isEmpty();
    }

    @Test
    public void testSetOrganizationName_financeDo_success() throws Exception {
        String organizationName = "Test Organization";
        setDeviceOwner();
        dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);

        dpm.setOrganizationName(admin1, organizationName);

        assertThat(dpm.getDeviceOwnerOrganizationName()).isEqualTo(organizationName);
    }

    @Test
    public void testSetShortSupportMessage_financeDo_success() throws Exception {
        String supportMessage = "Test short support message";
        setDeviceOwner();
        dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);

        dpm.setShortSupportMessage(admin1, supportMessage);

        assertThat(dpm.getShortSupportMessage(admin1)).isEqualTo(supportMessage);
    }

    @Test
    public void testIsBackupServiceEnabled_financeDo_success() throws Exception {
        setDeviceOwner();
        dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
        when(getServices().ibackupManager.isBackupServiceActive(eq(UserHandle.USER_SYSTEM)))
                .thenReturn(true);

        assertThat(dpm.isBackupServiceEnabled(admin1)).isTrue();
    }

    @Test
    public void testSetBackupServiceEnabled_financeDo_success() throws Exception {
        setDeviceOwner();
        dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);

        dpm.setBackupServiceEnabled(admin1, true);

        verify(getServices().ibackupManager)
                .setBackupServiceActive(eq(UserHandle.USER_SYSTEM), eq(true));
    }

    @Test
    public void testIsLockTaskPermitted_financeDo_success() throws Exception {
        String packageName = "com.android.foo.package";
        mockPolicyExemptApps(packageName);
        mockVendorPolicyExemptApps();
        setDeviceOwner();
        dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);

        assertThat(dpm.isLockTaskPermitted(packageName)).isTrue();
    }

    @Test
    public void testSetLockTaskPackages_financeDo_success() throws Exception {
        String[] packages = {"com.android.foo.package"};
        mockEmptyPolicyExemptApps();
        setDeviceOwner();
        dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);

        dpm.setLockTaskPackages(admin1, packages);

        verify(getServices().iactivityManager)
                .updateLockTaskPackages(eq(UserHandle.USER_SYSTEM), eq(packages));
    }

    @Test
    public void testAddPersistentPreferredActivity_financeDo_success() throws Exception {
        IntentFilter filter = new IntentFilter();
        ComponentName target = new ComponentName(admin2.getPackageName(), "test.class");
        setDeviceOwner();
        dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);

        dpm.addPersistentPreferredActivity(admin1, filter, target);

        verify(getServices().ipackageManager)
                .addPersistentPreferredActivity(eq(filter), eq(target), eq(UserHandle.USER_SYSTEM));
        verify(getServices().ipackageManager)
                .flushPackageRestrictionsAsUser(eq(UserHandle.USER_SYSTEM));
    }

    @Test
    public void testClearPackagePersistentPreferredActvities_financeDo_success() throws Exception {
        String packageName = admin2.getPackageName();
        setDeviceOwner();
        dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);

        dpm.clearPackagePersistentPreferredActivities(admin1, packageName);

        verify(getServices().ipackageManager)
                .clearPackagePersistentPreferredActivities(
                        eq(packageName), eq(UserHandle.USER_SYSTEM));
        verify(getServices().ipackageManager)
                .flushPackageRestrictionsAsUser(eq(UserHandle.USER_SYSTEM));
    }

    @Test
    public void testWipeData_financeDo_success() throws Exception {
        setDeviceOwner();
        dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
        when(getServices().userManager.getUserRestrictionSource(
                UserManager.DISALLOW_FACTORY_RESET,
                UserHandle.SYSTEM))
                .thenReturn(UserManager.RESTRICTION_SOURCE_DEVICE_OWNER);
        when(mMockContext.getResources()
                .getString(R.string.work_profile_deleted_description_dpm_wipe))
                .thenReturn("Test string");

        dpm.wipeData(0);

        verifyRebootWipeUserData(/* wipeEuicc= */ false);
    }

    @Test
    public void testIsDeviceOwnerApp_financeDo_success() throws Exception {
        setDeviceOwner();
        dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);

        assertThat(dpm.isDeviceOwnerApp(admin1.getPackageName())).isTrue();
    }

    @Test
    public void testClearDeviceOwnerApp_financeDo_success() throws Exception {
        setDeviceOwner();
        dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);

        dpm.clearDeviceOwnerApp(admin1.getPackageName());

        assertThat(dpm.getDeviceOwnerComponentOnAnyUser()).isNull();
        assertThat(dpm.isAdminActiveAsUser(admin1, UserHandle.USER_SYSTEM)).isFalse();
        verify(mMockContext.spiedContext, times(2))
                .sendBroadcastAsUser(
                        MockUtils.checkIntentAction(
                                DevicePolicyManager.ACTION_DEVICE_OWNER_CHANGED),
                        eq(UserHandle.SYSTEM));
    }

    @Test
    public void testSetPermissionGrantState_financeDo_notReadPhoneStatePermission_throwsException()
            throws Exception {
        setDeviceOwner();
        dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);

        assertExpectException(SecurityException.class, /* messageRegex= */ null,
                () -> dpm.setPermissionGrantState(admin1, admin1.getPackageName(),
                        permission.READ_CALENDAR,
                        DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED));
    }

    @Test
    public void testSetPermissionGrantState_financeDo_grantPermissionToNonDeviceOwnerPackage_throwsException()
            throws Exception {
        setDeviceOwner();
        dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);

        assertExpectException(SecurityException.class, /* messageRegex= */ null,
                () -> dpm.setPermissionGrantState(admin1, "com.android.foo.package",
                        permission.READ_PHONE_STATE,
                        DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED));
    }

    @Test
    public void testSetUsbDataSignalingEnabled_noDeviceOwnerOrPoOfOrgOwnedDevice() {
        assertThrows(SecurityException.class,
Loading