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

Commit 720bdde5 authored by Nino Jagar's avatar Nino Jagar Committed by Android (Google) Code Review
Browse files

Merge "Integrate admin controls for content protection" into main

parents d003fb1b 65eb00f0
Loading
Loading
Loading
Loading
+54 −1
Original line number Diff line number Diff line
@@ -15,15 +15,23 @@
 */
package com.android.settings.security;

import static android.view.contentprotection.flags.Flags.manageDevicePolicyEnabled;

import static com.android.internal.R.string.config_defaultContentProtectionService;

import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.DeviceConfig;
import android.view.contentcapture.ContentCaptureManager;

import androidx.annotation.Nullable;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.android.settings.Utils;

/** Util class for content protection preference. */
public class ContentProtectionPreferenceUtils {
@@ -60,4 +68,49 @@ public class ContentProtectionPreferenceUtils {
                ContentCaptureManager.DEVICE_CONFIG_PROPERTY_ENABLE_CONTENT_PROTECTION_RECEIVER,
                ContentCaptureManager.DEFAULT_ENABLE_CONTENT_PROTECTION_RECEIVER);
    }

    /** Returns the managed profile or null if none exists. */
    @Nullable
    public static UserHandle getManagedProfile(@NonNull Context context) {
        UserManager userManager = context.getSystemService(UserManager.class);
        if (userManager == null) {
            return null;
        }
        return Utils.getManagedProfile(userManager);
    }

    /** Returns the current content protection policy. */
    @DevicePolicyManager.ContentProtectionPolicy
    public static int getContentProtectionPolicy(
            @NonNull Context context, @Nullable UserHandle managedProfile) {
        if (!manageDevicePolicyEnabled()) {
            return DevicePolicyManager.CONTENT_PROTECTION_DISABLED;
        }
        Context policyContext = createContentProtectionPolicyContext(context, managedProfile);
        return getContentProtectionPolicyWithGivenContext(policyContext);
    }

    @NonNull
    private static Context createContentProtectionPolicyContext(
            @NonNull Context context, @Nullable UserHandle managedProfile) {
        if (managedProfile == null) {
            return context;
        }
        try {
            return context.createPackageContextAsUser(
                    context.getPackageName(), /* flags= */ 0, managedProfile);
        } catch (PackageManager.NameNotFoundException ex) {
            throw new IllegalStateException(ex);
        }
    }

    @DevicePolicyManager.ContentProtectionPolicy
    private static int getContentProtectionPolicyWithGivenContext(@NonNull Context context) {
        DevicePolicyManager devicePolicyManager =
                context.getSystemService(DevicePolicyManager.class);
        if (devicePolicyManager == null) {
            return DevicePolicyManager.CONTENT_PROTECTION_DISABLED;
        }
        return devicePolicyManager.getContentProtectionPolicy(/* admin= */ null);
    }
}
+58 −10
Original line number Diff line number Diff line
@@ -15,12 +15,17 @@
 */
package com.android.settings.security;

import static android.view.contentprotection.flags.Flags.manageDevicePolicyEnabled;

import android.app.admin.DevicePolicyManager;
import android.content.ContentResolver;
import android.content.Context;
import android.os.UserHandle;
import android.provider.Settings;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.preference.Preference;
@@ -40,12 +45,22 @@ public class ContentProtectionTogglePreferenceController extends TogglePreferenc
    static final String KEY_CONTENT_PROTECTION_PREFERENCE = "content_protection_user_consent";

    @Nullable private SettingsMainSwitchPreference mSwitchBar;

    @Nullable private RestrictedLockUtils.EnforcedAdmin mEnforcedAdmin;
    private final ContentResolver mContentResolver;

    @NonNull private final ContentResolver mContentResolver;

    @DevicePolicyManager.ContentProtectionPolicy
    private int mContentProtectionPolicy = DevicePolicyManager.CONTENT_PROTECTION_DISABLED;

    public ContentProtectionTogglePreferenceController(Context context, String preferenceKey) {
        super(context, preferenceKey);
        mContentResolver = context.getContentResolver();

        if (manageDevicePolicyEnabled()) {
            mEnforcedAdmin = getEnforcedAdmin();
            mContentProtectionPolicy = getContentProtectionPolicy(getManagedProfile());
        }
    }

    @Override
@@ -56,14 +71,30 @@ public class ContentProtectionTogglePreferenceController extends TogglePreferenc
    @Override
    public boolean isChecked() {
        if (mEnforcedAdmin != null) {
            if (!manageDevicePolicyEnabled()) {
                // If fully managed device, it should always unchecked
                return false;
            }

            if (mContentProtectionPolicy == DevicePolicyManager.CONTENT_PROTECTION_DISABLED) {
                return false;
            }
            if (mContentProtectionPolicy == DevicePolicyManager.CONTENT_PROTECTION_ENABLED) {
                return true;
            }
        }
        return Settings.Global.getInt(mContentResolver, KEY_CONTENT_PROTECTION_PREFERENCE, 0) >= 0;
    }

    @Override
    public boolean setChecked(boolean isChecked) {
        if (manageDevicePolicyEnabled()) {
            if (mEnforcedAdmin != null
                    && mContentProtectionPolicy
                            != DevicePolicyManager.CONTENT_PROTECTION_NOT_CONTROLLED_BY_POLICY) {
                return false;
            }
        }
        Settings.Global.putInt(
                mContentResolver, KEY_CONTENT_PROTECTION_PREFERENCE, isChecked ? 1 : -1);
        return true;
@@ -80,16 +111,20 @@ public class ContentProtectionTogglePreferenceController extends TogglePreferenc
        }
    }

    /**
     * Temporary workaround for SettingsMainSwitchPreference.setDisabledByAdmin without user
     * restriction.
     */
    // Workaround for SettingsMainSwitchPreference.setDisabledByAdmin without user restriction.
    @Override
    public void updateState(Preference preference) {
        super.updateState(preference);

        if (!manageDevicePolicyEnabled()) {
            // Assign the value to mEnforcedAdmin since it's needed in isChecked()
            mEnforcedAdmin = getEnforcedAdmin();
        if (mSwitchBar != null && mEnforcedAdmin != null) {
            mContentProtectionPolicy = DevicePolicyManager.CONTENT_PROTECTION_DISABLED;
        }
        if (mSwitchBar != null
                && mEnforcedAdmin != null
                && mContentProtectionPolicy
                        != DevicePolicyManager.CONTENT_PROTECTION_NOT_CONTROLLED_BY_POLICY) {
            mSwitchBar.setDisabledByAdmin(mEnforcedAdmin);
        }
    }
@@ -107,7 +142,20 @@ public class ContentProtectionTogglePreferenceController extends TogglePreferenc
    }

    @VisibleForTesting
    @Nullable
    protected UserHandle getManagedProfile() {
        return ContentProtectionPreferenceUtils.getManagedProfile(mContext);
    }

    @VisibleForTesting
    @Nullable
    protected RestrictedLockUtils.EnforcedAdmin getEnforcedAdmin() {
        return RestrictedLockUtilsInternal.getDeviceOwner(mContext);
    }

    @VisibleForTesting
    @DevicePolicyManager.ContentProtectionPolicy
    protected int getContentProtectionPolicy(@Nullable UserHandle userHandle) {
        return ContentProtectionPreferenceUtils.getContentProtectionPolicy(mContext, userHandle);
    }
}
+46 −13
Original line number Diff line number Diff line
@@ -15,9 +15,11 @@
 */
package com.android.settings.security;

import static android.view.contentprotection.flags.Flags.manageDevicePolicyEnabled;

import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.os.UserHandle;
import android.os.UserManager;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -25,7 +27,6 @@ import androidx.annotation.VisibleForTesting;
import androidx.preference.PreferenceScreen;

import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.core.TogglePreferenceController;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedSwitchPreference;
@@ -33,25 +34,49 @@ import com.android.settingslib.RestrictedSwitchPreference;
/** Preference controller for content protection work profile switch bar. */
public class ContentProtectionWorkSwitchController extends TogglePreferenceController {

    @Nullable private UserHandle mManagedProfile;

    @DevicePolicyManager.ContentProtectionPolicy
    private int mContentProtectionPolicy = DevicePolicyManager.CONTENT_PROTECTION_DISABLED;

    public ContentProtectionWorkSwitchController(
            @NonNull Context context, @NonNull String preferenceKey) {
        super(context, preferenceKey);

        if (manageDevicePolicyEnabled()) {
            mManagedProfile = getManagedProfile();
            if (mManagedProfile != null) {
                mContentProtectionPolicy = getContentProtectionPolicy(mManagedProfile);
            }
        }
    }

    @Override
    public int getAvailabilityStatus() {
        if (!manageDevicePolicyEnabled()) {
            return getManagedProfile() != null ? AVAILABLE : CONDITIONALLY_UNAVAILABLE;
        }
        if (mManagedProfile == null) {
            return CONDITIONALLY_UNAVAILABLE;
        }
        if (mContentProtectionPolicy
                == DevicePolicyManager.CONTENT_PROTECTION_NOT_CONTROLLED_BY_POLICY) {
            return CONDITIONALLY_UNAVAILABLE;
        }
        return AVAILABLE;
    }

    // The switch is always set to unchecked until Android V by design
    @Override
    public boolean isChecked() {
        if (!manageDevicePolicyEnabled()) {
            return false;
        }
        return mContentProtectionPolicy == DevicePolicyManager.CONTENT_PROTECTION_ENABLED;
    }

    // The switch is disabled until Android V by design
    @Override
    public boolean setChecked(boolean isChecked) {
        // Controlled by the admin API
        return false;
    }

@@ -59,12 +84,15 @@ public class ContentProtectionWorkSwitchController extends TogglePreferenceContr
    public void displayPreference(PreferenceScreen screen) {
        super.displayPreference(screen);

        RestrictedSwitchPreference switchPreference = screen.findPreference(getPreferenceKey());
        UserHandle managedProfile = getManagedProfile();
        UserHandle managedProfile =
                manageDevicePolicyEnabled() ? mManagedProfile : getManagedProfile();
        if (managedProfile != null) {
            RestrictedSwitchPreference switchPreference = screen.findPreference(getPreferenceKey());
            if (switchPreference != null) {
                switchPreference.setDisabledByAdmin(getEnforcedAdmin(managedProfile));
            }
        }
    }

    @Override
    public int getSliceHighlightMenuRes() {
@@ -74,13 +102,18 @@ public class ContentProtectionWorkSwitchController extends TogglePreferenceContr
    @VisibleForTesting
    @Nullable
    protected UserHandle getManagedProfile() {
        return Utils.getManagedProfile(mContext.getSystemService(UserManager.class));
        return ContentProtectionPreferenceUtils.getManagedProfile(mContext);
    }

    @VisibleForTesting
    @Nullable
    protected RestrictedLockUtils.EnforcedAdmin getEnforcedAdmin(
            @NonNull UserHandle managedProfile) {
        return RestrictedLockUtils.getProfileOrDeviceOwner(mContext, managedProfile);
    protected RestrictedLockUtils.EnforcedAdmin getEnforcedAdmin(@NonNull UserHandle userHandle) {
        return RestrictedLockUtils.getProfileOrDeviceOwner(mContext, userHandle);
    }

    @VisibleForTesting
    @DevicePolicyManager.ContentProtectionPolicy
    protected int getContentProtectionPolicy(@Nullable UserHandle userHandle) {
        return ContentProtectionPreferenceUtils.getContentProtectionPolicy(mContext, userHandle);
    }
}
+146 −12
Original line number Diff line number Diff line
@@ -16,47 +16,73 @@

package com.android.settings.security;

import static android.view.contentprotection.flags.Flags.FLAG_MANAGE_DEVICE_POLICY_ENABLED;

import static com.android.internal.R.string.config_defaultContentProtectionService;

import static com.google.common.truth.Truth.assertThat;

import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.flag.junit.SetFlagsRule;
import android.provider.DeviceConfig;
import android.view.contentcapture.ContentCaptureManager;

import com.android.settings.testutils.shadow.ShadowDeviceConfig;

import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;

import java.util.List;

@RunWith(RobolectricTestRunner.class)
@Config(
        shadows = {
            ShadowDeviceConfig.class,
        })
@Config(shadows = {ShadowDeviceConfig.class})
public class ContentProtectionPreferenceUtilsTest {

    private static final String PACKAGE_NAME = "com.test.package";

    private static final ComponentName COMPONENT_NAME =
            new ComponentName(PACKAGE_NAME, "TestClass");

    private String mConfigDefaultContentProtectionService = COMPONENT_NAME.flattenToString();
    private static final UserHandle USER_HANDLE = UserHandle.of(111);

    private static final int PROCESS_USER_ID = 222;

    private final String mConfigDefaultContentProtectionService = COMPONENT_NAME.flattenToString();

    @Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();

    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();

    @Mock private Context mMockContext;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
    }
    @Mock private Context mMockUserContext;

    @Mock private UserManager mMockUserManager;

    @Mock private DevicePolicyManager mMockDevicePolicyManager;

    @Mock private UserInfo mMockUserInfo;

    @After
    public void tearDown() {
@@ -134,7 +160,6 @@ public class ContentProtectionPreferenceUtilsTest {
        assertThat(ContentProtectionPreferenceUtils.isAvailable(mMockContext)).isFalse();
    }


    @Test
    public void isAvailable_bothDisabled_false() {
        DeviceConfig.setProperty(
@@ -145,4 +170,113 @@ public class ContentProtectionPreferenceUtilsTest {

        assertThat(ContentProtectionPreferenceUtils.isAvailable(mMockContext)).isFalse();
    }

    @Test
    public void getManagedProfile_noProfiles() {
        when(mMockContext.getSystemService(UserManager.class)).thenReturn(mMockUserManager);
        when(mMockUserManager.getUserProfiles()).thenReturn(List.of());

        UserHandle actual = ContentProtectionPreferenceUtils.getManagedProfile(mMockContext);

        assertThat(actual).isNull();
    }

    @Test
    public void getManagedProfile_notManaged() {
        when(mMockContext.getSystemService(UserManager.class)).thenReturn(mMockUserManager);
        when(mMockUserManager.getUserProfiles()).thenReturn(List.of(USER_HANDLE));
        when(mMockUserManager.getProcessUserId()).thenReturn(PROCESS_USER_ID);
        when(mMockUserManager.getUserInfo(USER_HANDLE.getIdentifier())).thenReturn(mMockUserInfo);

        UserHandle actual = ContentProtectionPreferenceUtils.getManagedProfile(mMockContext);

        assertThat(actual).isNull();
        verify(mMockUserInfo).isManagedProfile();
    }

    @Test
    public void getManagedProfile_managed() {
        when(mMockContext.getSystemService(UserManager.class)).thenReturn(mMockUserManager);
        when(mMockUserManager.getUserProfiles()).thenReturn(List.of(USER_HANDLE));
        when(mMockUserManager.getProcessUserId()).thenReturn(PROCESS_USER_ID);
        when(mMockUserManager.getUserInfo(USER_HANDLE.getIdentifier())).thenReturn(mMockUserInfo);
        when(mMockUserInfo.isManagedProfile()).thenReturn(true);

        UserHandle actual = ContentProtectionPreferenceUtils.getManagedProfile(mMockContext);

        assertThat(actual).isEqualTo(USER_HANDLE);
    }

    @Test
    public void getContentProtectionPolicy_flagDisabled_managedProfileNull() {
        mSetFlagsRule.disableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);

        int actual =
                ContentProtectionPreferenceUtils.getContentProtectionPolicy(
                        mMockContext, /* managedProfile= */ null);

        assertThat(actual).isEqualTo(DevicePolicyManager.CONTENT_PROTECTION_DISABLED);
    }

    @Test
    public void getContentProtectionPolicy_flagDisabled_managedProfileNotNull() {
        mSetFlagsRule.disableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);

        int actual =
                ContentProtectionPreferenceUtils.getContentProtectionPolicy(
                        mMockContext, USER_HANDLE);

        assertThat(actual).isEqualTo(DevicePolicyManager.CONTENT_PROTECTION_DISABLED);
    }

    @Test
    public void getContentProtectionPolicy_flagEnabled_managedProfileNull() throws Exception {
        mSetFlagsRule.enableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);
        when(mMockContext.getSystemService(DevicePolicyManager.class))
                .thenReturn(mMockDevicePolicyManager);
        when(mMockDevicePolicyManager.getContentProtectionPolicy(/* admin= */ null))
                .thenReturn(DevicePolicyManager.CONTENT_PROTECTION_ENABLED);

        int actual =
                ContentProtectionPreferenceUtils.getContentProtectionPolicy(
                        mMockContext, /* managedProfile= */ null);

        assertThat(actual).isEqualTo(DevicePolicyManager.CONTENT_PROTECTION_ENABLED);
        verify(mMockContext, never()).createPackageContextAsUser(anyString(), anyInt(), any());
    }

    @Test
    public void getContentProtectionPolicy_flagEnabled_managedProfileNotNull() throws Exception {
        mSetFlagsRule.enableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);
        when(mMockContext.getPackageName()).thenReturn(PACKAGE_NAME);
        when(mMockContext.createPackageContextAsUser(PACKAGE_NAME, /* flags= */ 0, USER_HANDLE))
                .thenReturn(mMockUserContext);
        when(mMockUserContext.getSystemService(DevicePolicyManager.class))
                .thenReturn(mMockDevicePolicyManager);
        when(mMockDevicePolicyManager.getContentProtectionPolicy(/* admin= */ null))
                .thenReturn(DevicePolicyManager.CONTENT_PROTECTION_ENABLED);

        int actual =
                ContentProtectionPreferenceUtils.getContentProtectionPolicy(
                        mMockContext, USER_HANDLE);

        assertThat(actual).isEqualTo(DevicePolicyManager.CONTENT_PROTECTION_ENABLED);
    }

    @Test
    public void getContentProtectionPolicy_flagEnabled_managedProfileNotNull_nameNotFound()
            throws Exception {
        mSetFlagsRule.enableFlags(FLAG_MANAGE_DEVICE_POLICY_ENABLED);
        when(mMockContext.getPackageName()).thenReturn(PACKAGE_NAME);
        when(mMockContext.createPackageContextAsUser(PACKAGE_NAME, /* flags= */ 0, USER_HANDLE))
                .thenThrow(new PackageManager.NameNotFoundException());

        assertThrows(
                IllegalStateException.class,
                () ->
                        ContentProtectionPreferenceUtils.getContentProtectionPolicy(
                                mMockContext, USER_HANDLE));

        verify(mMockContext, never()).getSystemService(DevicePolicyManager.class);
    }
}
+234 −63

File changed.

Preview size limit exceeded, changes collapsed.

Loading