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

Commit db73c865 authored by Irem Uguz's avatar Irem Uguz
Browse files

Introduce getEnforcingAdminsForKeyguardFeatures

Add RestrictedLockUtilsInternal::getEnforcingAdminsForKeyguardFeatures
which returns the EnforcingAdmin information that disables the given
keyguard flags. Add a special case handling to DevicePolicyManager to
cover the use-case of keyguard features policy properly.
Add EnforcingAdmin option to SupervisionRestrictionsHelper to create
supervision admin.
These changes will be used in biometrics settings in ag/35288530.

Flag: android.app.admin.flags.policy_transparency_refactor_enabled
Bug: 414733570
Test: atest SupervisionRestrictionsHelperTest
Test: atest FrameworksServicesTests_devicepolicy:com.android.server.devicepolicy.DevicePolicyManagerTest#getEnforcingAdminsForPolicy_keyguardDisabledFeatures_returnsManagedProfileAdminForParent

Change-Id: I42dec66f851d70b9ed101641ad67854eec8f3e02
parent cbd1c5f0
Loading
Loading
Loading
Loading
+49 −0
Original line number Diff line number Diff line
@@ -26,9 +26,11 @@ import static com.android.settingslib.Utils.getColorAttrDefaultColor;
import android.annotation.UserIdInt;
import android.app.AppGlobals;
import android.app.AppOpsManager;
import android.app.admin.DevicePolicyIdentifiers;
import android.app.admin.DevicePolicyManager;
import android.app.admin.EnforcingAdmin;
import android.app.admin.PackagePolicy;
import android.app.admin.PolicyEnforcementInfo;
import android.app.admin.SystemAuthority;
import android.app.ecm.EnhancedConfirmationManager;
import android.app.role.RoleManager;
@@ -272,6 +274,53 @@ public class RestrictedLockUtilsInternal extends RestrictedLockUtils {
        return checkForLockSetting(context, userId, check);
    }

    /**
     * Checks whether keyguard features are disabled by policy and returns the admin information.
     *
     * @param context          {@link Context} for the calling user.
     * @param keyguardFeatures Any one of keyguard features that can be disabled by {@link
     *                         android.app.admin.DevicePolicyManager#setKeyguardDisabledFeatures}.
     * @param userId           User to check keyguard features for.
     * @return PolicyEnforcementInfo Object containing the enforcing admin information. null if
     * keyguard features are not disabled.
     * @throws IllegalStateException if
     * {@link android.app.admin.flags.Flags#setKeyguardDisabledFeaturesCoexistence} is not enabled.
     * When the flag is disabled, please use {@link #checkIfKeyguardFeaturesDisabled} instead.
     */
    @RequiresApi(Build.VERSION_CODES.BAKLAVA)
    public static PolicyEnforcementInfo getEnforcingAdminsForKeyguardFeatures(Context context,
            int keyguardFeatures, @UserIdInt int userId) {
        // TODO(b/359186276): Remove this check once the flag is ready for clean-up.
        if (!android.app.admin.flags.Flags.setKeyguardDisabledFeaturesCoexistence()) {
            throw new IllegalStateException(
                    "setKeyguardDisabledFeaturesCoexistence is not enabled. Use "
                            + "checkIfKeyguardFeaturesDisabled instead.");
        }

        DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
        if (dpm == null) {
            Log.w(LOG_TAG, "DevicePolicyManager is null");
            return null;
        }

        if (!isKeyguardFeaturesDisabled(dpm, keyguardFeatures, userId)) {
            return null;
        }

        // We don't need to check separately for managed profile or parent profile policies as
        // the policy application is handled by DPM in {@link android.app.admin
        // .DevicePolicyManager#setKeyguardDisabledFeatures} already.
        return dpm.getEnforcingAdminsForPolicy(
                DevicePolicyIdentifiers.KEYGUARD_DISABLED_FEATURES_POLICY, userId);
    }

    private static boolean isKeyguardFeaturesDisabled(DevicePolicyManager dpm, int keyguardFeatures,
            int userId) {
        // Set admin as null to check for all admins who has set policy.
        int disabledFeatures = dpm.getKeyguardDisabledFeatures(/*admin=*/null, userId);
        return (disabledFeatures & keyguardFeatures) != KEYGUARD_DISABLE_FEATURES_NONE;
    }

    /**
     * @return the UserHandle for a userId. Return null for USER_NULL
     */
+37 −3
Original line number Diff line number Diff line
@@ -17,6 +17,9 @@
package com.android.settingslib.supervision

import android.app.admin.DeviceAdminReceiver
import android.app.admin.EnforcingAdmin
import android.app.admin.RoleAuthority
import android.app.role.RoleManager
import android.app.supervision.SupervisionAppService
import android.app.supervision.SupervisionManager
import android.content.ComponentName
@@ -40,6 +43,37 @@ object SupervisionRestrictionsHelper {
        restriction: String,
        user: UserHandle,
    ): EnforcedAdmin? {
        if (!supervisionEnabled(context, user.identifier)) {
            return null
        }
        return EnforcedAdmin(getSupervisionComponent(context, user.identifier), restriction, user)
    }

    /**
     * Creates an instance of [EnforcingAdmin] that uses the correct supervision component or
     * returns null if supervision is not enabled.
     */
    @JvmStatic
    fun createEnforcingAdmin(context: Context, user: UserHandle): EnforcingAdmin? {
        if (!supervisionEnabled(context, user.identifier)) {
            return null
        }
        val componentName = getSupervisionComponent(context, user.identifier)
        val packageName = componentName?.packageName ?: ""
        return EnforcingAdmin(
            packageName,
            RoleAuthority(setOf(RoleManager.ROLE_SYSTEM_SUPERVISION)),
            user,
            componentName,
        )
    }

    private fun supervisionEnabled(context: Context, userId: Int): Boolean {
        val supervisionManager = context.getSystemService(SupervisionManager::class.java)
        return supervisionManager?.isSupervisionEnabledForUser(userId) == true
    }

    private fun getSupervisionComponent(context: Context, userId: Int): ComponentName? {
        val supervisionManager = context.getSystemService(SupervisionManager::class.java)
        val supervisionAppPackage = supervisionManager?.activeSupervisionAppPackage ?: return null
        var supervisionComponent: ComponentName? = null
@@ -49,7 +83,7 @@ object SupervisionRestrictionsHelper {
            context.packageManager.queryIntentServicesAsUser(
                Intent(SupervisionAppService.ACTION_SUPERVISION_APP_SERVICE),
                PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS,
                user.identifier,
                userId,
            )
        resolveSupervisionApps
            .mapNotNull { it.serviceInfo?.componentName }
@@ -63,7 +97,7 @@ object SupervisionRestrictionsHelper {
                context.packageManager.queryBroadcastReceiversAsUser(
                    Intent(DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED),
                    PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS,
                    user.identifier,
                    userId,
                )
            resolveDeviceAdmins
                .mapNotNull { it.activityInfo?.componentName }
@@ -74,6 +108,6 @@ object SupervisionRestrictionsHelper {
        if (supervisionComponent == null) {
            Log.d(SupervisionLog.TAG, "Could not find the supervision component.")
        }
        return EnforcedAdmin(supervisionComponent, restriction, user)
        return supervisionComponent
    }
}
+89 −13
Original line number Diff line number Diff line
@@ -17,6 +17,9 @@
package com.android.settingslib.supervision

import android.app.admin.DeviceAdminReceiver
import android.app.admin.EnforcingAdmin
import android.app.admin.RoleAuthority
import android.app.role.RoleManager
import android.app.supervision.SupervisionAppService
import android.app.supervision.SupervisionManager
import android.content.Context
@@ -51,11 +54,14 @@ import org.mockito.junit.MockitoRule
 */
@RunWith(AndroidJUnit4::class)
class SupervisionRestrictionsHelperTest {
    @get:Rule val mocks: MockitoRule = MockitoJUnit.rule()
    @get:Rule
    val mocks: MockitoRule = MockitoJUnit.rule()

    @Mock private lateinit var mockPackageManager: PackageManager
    @Mock
    private lateinit var mockPackageManager: PackageManager

    @Mock private lateinit var mockSupervisionManager: SupervisionManager
    @Mock
    private lateinit var mockSupervisionManager: SupervisionManager

    private lateinit var context: Context

@@ -75,6 +81,7 @@ class SupervisionRestrictionsHelperTest {

    @Test
    fun createEnforcedAdmin_nullSupervisionPackage() {
        `when`(mockSupervisionManager.isSupervisionEnabledForUser(USER_ID)).thenReturn(false)
        `when`(mockSupervisionManager.activeSupervisionAppPackage).thenReturn(null)

        val enforcedAdmin =
@@ -94,6 +101,7 @@ class SupervisionRestrictionsHelperTest {
                    }
            }

        `when`(mockSupervisionManager.isSupervisionEnabledForUser(USER_ID)).thenReturn(true)
        `when`(mockSupervisionManager.activeSupervisionAppPackage)
            .thenReturn(SUPERVISION_APP_PACKAGE)
        `when`(
@@ -125,6 +133,7 @@ class SupervisionRestrictionsHelperTest {
                    }
            }

        `when`(mockSupervisionManager.isSupervisionEnabledForUser(USER_ID)).thenReturn(true)
        `when`(mockSupervisionManager.activeSupervisionAppPackage)
            .thenReturn(SUPERVISION_APP_PACKAGE)
        `when`(mockPackageManager.queryIntentServicesAsUser(any<Intent>(), anyInt(), eq(USER_ID)))
@@ -149,6 +158,7 @@ class SupervisionRestrictionsHelperTest {

    @Test
    fun createEnforcedAdmin_noSupervisionComponent() {
        `when`(mockSupervisionManager.isSupervisionEnabledForUser(USER_ID)).thenReturn(true)
        `when`(mockSupervisionManager.activeSupervisionAppPackage)
            .thenReturn(SUPERVISION_APP_PACKAGE)
        `when`(mockPackageManager.queryIntentServicesAsUser(any<Intent>(), anyInt(), anyInt()))
@@ -165,6 +175,72 @@ class SupervisionRestrictionsHelperTest {
        assertThat(enforcedAdmin.user).isEqualTo(USER_HANDLE)
    }

    @Test
    fun createEnforcingAdmin_returnsNullIfSupervisionDisabled() {
        `when`(mockSupervisionManager.isSupervisionEnabledForUser(USER_ID)).thenReturn(false)

        val enforcingAdmin =
            SupervisionRestrictionsHelper.createEnforcingAdmin(context, USER_HANDLE)

        assertThat(enforcingAdmin).isNull()
    }

    @Test
    fun createEnforcingAdmin_returnsAdminIfSupervisionEnabledWithService() {
        val resolveInfo =
            ResolveInfo().apply {
                serviceInfo =
                    ServiceInfo().apply {
                        packageName = SUPERVISION_APP_PACKAGE
                        name = "service.class"
                    }
            }
        `when`(mockSupervisionManager.isSupervisionEnabledForUser(USER_ID)).thenReturn(true)
        `when`(mockSupervisionManager.activeSupervisionAppPackage).thenReturn(
            SUPERVISION_APP_PACKAGE
        )
        `when`(
            mockPackageManager.queryIntentServicesAsUser(
                argThat(hasAction(SupervisionAppService.ACTION_SUPERVISION_APP_SERVICE)),
                anyInt(),
                eq(USER_ID)
            )
        )
            .thenReturn(listOf(resolveInfo))

        val enforcingAdmin: EnforcingAdmin? =
            SupervisionRestrictionsHelper.createEnforcingAdmin(context, USER_HANDLE)

        assertThat(enforcingAdmin).isNotNull()
        assertThat(enforcingAdmin!!.packageName).isEqualTo(SUPERVISION_APP_PACKAGE)
        assertThat(enforcingAdmin.authority)
            .isEqualTo(RoleAuthority(setOf(RoleManager.ROLE_SYSTEM_SUPERVISION)))
        assertThat(enforcingAdmin.userHandle).isEqualTo(USER_HANDLE)
        assertThat(enforcingAdmin.componentName).isEqualTo(resolveInfo.serviceInfo.componentName)
    }

    @Test
    fun createEnforcingAdmin_returnsAdminIfSupervisionEnabledWithNoComponent() {
        `when`(mockSupervisionManager.isSupervisionEnabledForUser(USER_ID)).thenReturn(true)
        `when`(mockSupervisionManager.activeSupervisionAppPackage).thenReturn(
            SUPERVISION_APP_PACKAGE
        )
        `when`(mockPackageManager.queryIntentServicesAsUser(any<Intent>(), anyInt(), anyInt()))
            .thenReturn(emptyList<ResolveInfo>())
        `when`(mockPackageManager.queryBroadcastReceiversAsUser(any<Intent>(), anyInt(), anyInt()))
            .thenReturn(emptyList<ResolveInfo>())

        val enforcingAdmin: EnforcingAdmin? =
            SupervisionRestrictionsHelper.createEnforcingAdmin(context, USER_HANDLE)

        assertThat(enforcingAdmin).isNotNull()
        assertThat(enforcingAdmin!!.packageName).isEmpty()
        assertThat(enforcingAdmin.authority)
            .isEqualTo(RoleAuthority(setOf(RoleManager.ROLE_SYSTEM_SUPERVISION)))
        assertThat(enforcingAdmin.userHandle).isEqualTo(USER_HANDLE)
        assertThat(enforcingAdmin.componentName).isNull()
    }

    private fun hasAction(action: String) =
        object : ArgumentMatcher<Intent> {
            override fun matches(intent: Intent?) = intent?.action == action
+49 −5
Original line number Diff line number Diff line
@@ -255,6 +255,7 @@ import static com.android.server.devicepolicy.DevicePolicyStatsLog.DEVICE_POLICY
import static com.android.server.devicepolicy.DevicePolicyStatsLog.DEVICE_POLICY_STATE__PASSWORD_COMPLEXITY__COMPLEXITY_NONE;
import static com.android.server.devicepolicy.DevicePolicyStatsLog.DEVICE_POLICY_STATE__PASSWORD_COMPLEXITY__COMPLEXITY_UNSPECIFIED;
import static com.android.server.devicepolicy.PolicyDefinition.CROSS_PROFILE_WIDGET_PROVIDER;
import static com.android.server.devicepolicy.PolicyDefinition.KEYGUARD_DISABLED_FEATURES;
import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_DEVICE_OWNER;
import static com.android.server.devicepolicy.TransferOwnershipMetadataManager.ADMIN_TYPE_PROFILE_OWNER;
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
@@ -4864,8 +4865,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
    private <V> V getResolvedPolicyForUserAndItsManagedProfiles(
            PolicyDefinition<V> policyDefinition, int userHandle,
            Predicate<UserInfo> shouldIncludeProfile) {
        List<Integer> users = new ArrayList<>();
        List<Integer> users = getUserAndItsManagedProfiles(userHandle, shouldIncludeProfile);
        return mDevicePolicyEngine.getResolvedPolicyAcrossUsers(policyDefinition, users);
    }
    private List<Integer> getUserAndItsManagedProfiles(int userHandle,
            Predicate<UserInfo> shouldIncludeProfile) {
        List<Integer> users = new ArrayList<>();
        mInjector.binderWithCleanCallingIdentity(() -> {
            for (UserInfo userInfo : mUserManager.getProfiles(userHandle)) {
                if (userInfo.id == userHandle) {
@@ -4875,7 +4881,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
                }
            }
        });
        return mDevicePolicyEngine.getResolvedPolicyAcrossUsers(policyDefinition, users);
        return users;
    }
    private boolean isSeparateProfileChallengeEnabled(int userHandle) {
@@ -16875,9 +16881,18 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
                                + "instead.");
            }
            Set<EnforcingAdmin> admins =
            Set<EnforcingAdmin> admins;
            // Lockscreen policies have special case where a policy that's set on managed profile
            // shows effect on its parent. This case needs to be included in enforcing admins
            // list to ensure coverage on policy transparency.
            if (isLockScreenPolicy(policyDefinition)) {
                admins = getLockScreenPolicyEnforcingAdmins(policyDefinition, userId);
            } else {
                admins = new HashSet<>(
                        mDevicePolicyEngine.getEnforcingAdminsForResolvedPolicy(policyDefinition,
                            userId);
                                userId));
            }
            // The user restrictions that are set by the admins are already included in the
            // EnforcingAdmin set that DevicePolicyEngine returns in the previous line.
@@ -16942,6 +16957,35 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
        return null;
    }
    /**
     * Lock screen policies can have a different handling as managed profile's policies will take
     * effect on the parent user if the managed profile has a unified challenge with parent. This
     * can only happen for lockscreen policies (currently only {@link KEYGUARD_DISABLED_FEATURES})
     * where this is allowed.
     */
    private <V> boolean isLockScreenPolicy(@NonNull PolicyDefinition<V> policyDefinition) {
        // TODO(414733570): Add password policies to this list as they apply on lockscreen as well.
        return policyDefinition.equals(KEYGUARD_DISABLED_FEATURES);
    }
    /**
     * For lock screen policies, managed profile's policies will take effect on the parent user if
     * the managed profile has a unified challenge with parent. This method returns the set of
     * enforcing admins from managed profile in that case. If there's no such policy from
     * managed profiles or the parent profile, it'll return empty set.
     */
    @NonNull
    private <V> Set<EnforcingAdmin> getLockScreenPolicyEnforcingAdmins(
            PolicyDefinition<V> definition, int userId) {
        Set<EnforcingAdmin> admins = new HashSet<>();
        for (int profileUserId : getUserAndItsManagedProfiles(userId,
                (user) -> mLockPatternUtils.isProfileWithUnifiedChallenge(user.id))) {
            admins.addAll(mDevicePolicyEngine.getEnforcingAdminsForResolvedPolicy(definition,
                    profileUserId));
        }
        return admins;
    }
    private boolean isUserRestrictionPolicyEnforcedBySystem(
            PolicyDefinition<?> policyDefinition, int userId) {
+29 −0
Original line number Diff line number Diff line
@@ -8987,6 +8987,35 @@ public class DevicePolicyManagerTest extends DpmTestBase {
        assertThat(enforcingAdmins.getFirst().getPackageName()).isEqualTo(admin1.getPackageName());
    }

    @Test
    @RequiresFlagsEnabled(Flags.FLAG_SET_KEYGUARD_DISABLED_FEATURES_COEXISTENCE)
    public void getEnforcingAdminsForPolicy_keyguardDisabledFeatures_returnsManagedProfileAdminForParent()
            throws Exception {
        // Set-up managed profile with parent as system user.
        mContext.callerPermissions.add(permission.BIND_DEVICE_ADMIN);
        final int managedProfileUserId = 78;
        final int managedProfileAdminUid = UserHandle.getUid(managedProfileUserId,
                DpmMockContext.SYSTEM_UID);
        mContext.binder.callingUid = DpmMockContext.SYSTEM_UID;
        mContext.packageName = admin1.getPackageName();
        // Add a managed profile belonging to the system user.
        addManagedProfile(admin1, managedProfileAdminUid, admin1);
        // Set policy on the managed profile.
        mContext.binder.callingUid = managedProfileAdminUid;
        dpm.setKeyguardDisabledFeatures(admin1, DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT);
        when(getServices().lockPatternUtils.isProfileWithUnifiedChallenge(
                managedProfileUserId)).thenReturn(true);
        mContext.callerPermissions.add(permission.QUERY_ADMIN_POLICY);

        // Get enforcing admins on parent.
        List<EnforcingAdmin> enforcingAdmins = dpm.getEnforcingAdminsForPolicy(
                DevicePolicyIdentifiers.KEYGUARD_DISABLED_FEATURES_POLICY,
                UserHandle.USER_SYSTEM).getAllAdmins();

        assertThat(enforcingAdmins.size()).isEqualTo(1);
        assertThat(enforcingAdmins.getFirst().getComponentName()).isEqualTo(admin1);
    }

    private void setupVpnAuthorization(String userVpnPackage, int userVpnUid) {
        final AppOpsManager.PackageOps vpnOp = new AppOpsManager.PackageOps(userVpnPackage,
                userVpnUid, List.of(new AppOpsManager.OpEntry(