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

Commit 65d94931 authored by Eran Messeri's avatar Eran Messeri
Browse files

Allow control over account management of parent profile

Let the owner of a managed profile on an organization-owned device set
accounts for which management is disabled in the primary profile, via
the parent profile's DevicePolicyManager instance.

Test: atest CtsDevicePolicyManagerTestCases:com.android.cts.devicepolicy.OrgOwnedProfileOwnerTest#testCanRestrictAccountManagementOnParentProfile
Test: atest FrameworksServicesTests:DevicePolicyManagerTest
Bug: 148438071
Change-Id: I45eaf5e8e403e0c23dad2df106fefd1a1f3c6f4b
parent 90991d41
Loading
Loading
Loading
Loading
+28 −6
Original line number Diff line number Diff line
@@ -8463,6 +8463,11 @@ public class DevicePolicyManager {
     * From {@link android.os.Build.VERSION_CODES#N} the profile or device owner can still use
     * {@link android.accounts.AccountManager} APIs to add or remove accounts when account
     * management for a specific type is disabled.
     * <p>
     * This method may be called on the {@code DevicePolicyManager} instance returned from
     * {@link #getParentProfileInstance(ComponentName)} by the profile owner on an
     * organization-owned device, to restrict accounts that may not be managed on the primary
     * profile.
     *
     * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
     * @param accountType For which account management is disabled or enabled.
@@ -8472,10 +8477,10 @@ public class DevicePolicyManager {
     */
    public void setAccountManagementDisabled(@NonNull ComponentName admin, String accountType,
            boolean disabled) {
        throwIfParentInstance("setAccountManagementDisabled");
        if (mService != null) {
            try {
                mService.setAccountManagementDisabled(admin, accountType, disabled);
                mService.setAccountManagementDisabled(admin, accountType, disabled,
                        mParentInstance);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
@@ -8483,28 +8488,43 @@ public class DevicePolicyManager {
    }
    /**
     * Gets the array of accounts for which account management is disabled by the profile owner.
     * Gets the array of accounts for which account management is disabled by the profile owner
     * or device owner.
     *
     * <p> Account management can be disabled/enabled by calling
     * {@link #setAccountManagementDisabled}.
     * <p>
     * This method may be called on the {@code DevicePolicyManager} instance returned from
     * {@link #getParentProfileInstance(ComponentName)}. Note that only a profile owner on
     * an organization-deviced can affect account types on the parent profile instance.
     *
     * @return a list of account types for which account management has been disabled.
     *
     * @see #setAccountManagementDisabled
     */
    public @Nullable String[] getAccountTypesWithManagementDisabled() {
        throwIfParentInstance("getAccountTypesWithManagementDisabled");
        return getAccountTypesWithManagementDisabledAsUser(myUserId());
        return getAccountTypesWithManagementDisabledAsUser(myUserId(), mParentInstance);
    }
    /**
     * @see #getAccountTypesWithManagementDisabled()
     * Note that calling this method on the parent profile instance will return the same
     * value as calling it on the main {@code DevicePolicyManager} instance.
     * @hide
     */
    public @Nullable String[] getAccountTypesWithManagementDisabledAsUser(int userId) {
        return getAccountTypesWithManagementDisabledAsUser(userId, false);
    }
    /**
     * @see #getAccountTypesWithManagementDisabled()
     * @hide
     */
    public @Nullable String[] getAccountTypesWithManagementDisabledAsUser(
            int userId, boolean parentInstance) {
        if (mService != null) {
            try {
                return mService.getAccountTypesWithManagementDisabledAsUser(userId);
                return mService.getAccountTypesWithManagementDisabledAsUser(userId, parentInstance);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
@@ -9842,6 +9862,7 @@ public class DevicePolicyManager {
     * <li>{@link #setTrustAgentConfiguration}</li>
     * <li>{@link #getRequiredStrongAuthTimeout}</li>
     * <li>{@link #setRequiredStrongAuthTimeout}</li>
     * <li>{@link #getAccountTypesWithManagementDisabled}</li>
     * </ul>
     * <p>
     * The following methods are supported for the parent instance but can only be called by the
@@ -9850,6 +9871,7 @@ public class DevicePolicyManager {
     * <li>{@link #getPasswordComplexity}</li>
     * <li>{@link #setCameraDisabled}</li>
     * <li>{@link #getCameraDisabled}</li>
     * <li>{@link #setAccountManagementDisabled(ComponentName, String, boolean)}</li>
     * </ul>
     *
     * <p>The following methods can be called by the profile owner of a managed profile
+2 −2
Original line number Diff line number Diff line
@@ -248,9 +248,9 @@ interface IDevicePolicyManager {
    int enableSystemAppWithIntent(in ComponentName admin, in String callerPackage, in Intent intent);
    boolean installExistingPackage(in ComponentName admin, in String callerPackage, in String packageName);

    void setAccountManagementDisabled(in ComponentName who, in String accountType, in boolean disabled);
    void setAccountManagementDisabled(in ComponentName who, in String accountType, in boolean disabled, in boolean parent);
    String[] getAccountTypesWithManagementDisabled();
    String[] getAccountTypesWithManagementDisabledAsUser(int userId);
    String[] getAccountTypesWithManagementDisabledAsUser(int userId, in boolean parent);

    void setSecondaryLockscreenEnabled(in ComponentName who, boolean enabled);
    boolean isSecondaryLockscreenEnabled(int userId);
+29 −10
Original line number Diff line number Diff line
@@ -11049,14 +11049,21 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
    @Override
    public void setAccountManagementDisabled(ComponentName who, String accountType,
            boolean disabled) {
            boolean disabled, boolean parent) {
        if (!mHasFeature) {
            return;
        }
        Objects.requireNonNull(who, "ComponentName is null");
        synchronized (getLockObject()) {
            /*
             * When called on the parent DPM instance (parent == true), affects active admin
             * selection in two ways:
             * * The ActiveAdmin must be of an org-owned profile owner.
             * * The parent ActiveAdmin instance should be used for managing the restriction.
             */
            ActiveAdmin ap = getActiveAdminForCallerLocked(who,
                    DeviceAdminInfo.USES_POLICY_PROFILE_OWNER);
                    parent ? DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER
                            : DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, parent);
            if (disabled) {
                ap.accountTypesWithManagementDisabled.add(accountType);
            } else {
@@ -11068,23 +11075,35 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
    @Override
    public String[] getAccountTypesWithManagementDisabled() {
        return getAccountTypesWithManagementDisabledAsUser(UserHandle.getCallingUserId());
        return getAccountTypesWithManagementDisabledAsUser(UserHandle.getCallingUserId(), false);
    }
    @Override
    public String[] getAccountTypesWithManagementDisabledAsUser(int userId) {
    public String[] getAccountTypesWithManagementDisabledAsUser(int userId, boolean parent) {
        enforceFullCrossUsersPermission(userId);
        if (!mHasFeature) {
            return null;
        }
        synchronized (getLockObject()) {
            DevicePolicyData policy = getUserData(userId);
            final int N = policy.mAdminList.size();
            ArraySet<String> resultSet = new ArraySet<>();
            for (int i = 0; i < N; i++) {
                ActiveAdmin admin = policy.mAdminList.get(i);
            final ArraySet<String> resultSet = new ArraySet<>();
            if (!parent) {
                final DevicePolicyData policy = getUserData(userId);
                for (ActiveAdmin admin : policy.mAdminList) {
                    resultSet.addAll(admin.accountTypesWithManagementDisabled);
                }
            }
            // Check if there's a profile owner of an org-owned device and the method is called for
            // the parent user of this profile owner.
            final ActiveAdmin orgOwnedAdmin =
                    getProfileOwnerOfOrganizationOwnedDeviceLocked(userId);
            final boolean shouldGetParentAccounts = orgOwnedAdmin != null && (parent
                    || UserHandle.getUserId(orgOwnedAdmin.getUid()) != userId);
            if (shouldGetParentAccounts) {
                resultSet.addAll(
                        orgOwnedAdmin.getParentActiveAdmin().accountTypesWithManagementDisabled);
            }
            return resultSet.toArray(new String[resultSet.size()]);
        }
    }
+49 −1
Original line number Diff line number Diff line
@@ -96,7 +96,6 @@ import android.test.MoreAsserts;
import android.util.ArraySet;
import android.util.Pair;

import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;

import com.android.internal.R;
@@ -6022,6 +6021,55 @@ public class DevicePolicyManagerTest extends DpmTestBase {
                .thenReturn(packages);
    }

    public void testSetAccountTypesWithManagementDisabledOnManagedProfile() throws Exception {
        setupProfileOwner();

        final String accountType = "com.example.account.type";
        int originalUid = mContext.binder.callingUid;
        dpm.setAccountManagementDisabled(admin1, accountType, true);
        assertThat(dpm.getAccountTypesWithManagementDisabled()).asList().containsExactly(
                accountType);
        mContext.binder.callingUid = DpmMockContext.ANOTHER_UID;
        assertThat(dpm.getAccountTypesWithManagementDisabled()).isEmpty();
        mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
        assertThat(dpm.getAccountTypesWithManagementDisabled()).isEmpty();

        mContext.binder.callingUid = originalUid;
        dpm.setAccountManagementDisabled(admin1, accountType, false);
        assertThat(dpm.getAccountTypesWithManagementDisabled()).isEmpty();
    }

    public void testSetAccountTypesWithManagementDisabledOnOrgOwnedManagedProfile()
            throws Exception {
        final int managedProfileUserId = 15;
        final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId, 19436);

        addManagedProfile(admin1, managedProfileAdminUid, admin1);
        mContext.binder.callingUid = managedProfileAdminUid;

        configureProfileOwnerOfOrgOwnedDevice(admin1, managedProfileUserId);

        int originalUid = mContext.binder.callingUid;
        final String accountType = "com.example.account.type";
        dpm.getParentProfileInstance(admin1).setAccountManagementDisabled(admin1, accountType,
                true);
        assertThat(dpm.getAccountTypesWithManagementDisabled()).isEmpty();
        mContext.binder.callingUid = DpmMockContext.ANOTHER_UID;
        assertThat(dpm.getAccountTypesWithManagementDisabled()).asList().containsExactly(
                accountType);
        mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
        assertThat(dpm.getAccountTypesWithManagementDisabled()).asList().containsExactly(
                accountType);

        mContext.binder.callingUid = originalUid;
        dpm.getParentProfileInstance(admin1).setAccountManagementDisabled(admin1, accountType,
                false);
        mContext.binder.callingUid = DpmMockContext.ANOTHER_UID;
        assertThat(dpm.getAccountTypesWithManagementDisabled()).isEmpty();
        mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
        assertThat(dpm.getAccountTypesWithManagementDisabled()).isEmpty();
    }

    // admin1 is the outgoing DPC, adminAnotherPakcage is the incoming one.
    private void assertDeviceOwnershipRevertedWithFakeTransferMetadata() throws Exception {
        writeFakeTransferMetadataFile(UserHandle.USER_SYSTEM,