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

Commit f3a6dcf1 authored by Eric Biggers's avatar Eric Biggers Committed by Gerrit Code Review
Browse files

Merge "Fix UnlockedDeviceRequired with biometric unlock with unified challenge" into main

parents e9a8efa9 bc8a1787
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -1442,6 +1442,13 @@ public class TrustManagerService extends SystemService {
        if (biometricManager == null) {
            return new long[0];
        }
        if (android.security.Flags.fixUnlockedDeviceRequiredKeysV2()
                && mLockPatternUtils.isProfileWithUnifiedChallenge(userId)) {
            // Profiles with unified challenge have their own set of biometrics, but the device
            // unlock happens via the parent user.  In this case Keystore needs to be given the list
            // of biometric SIDs from the parent user, not the profile.
            userId = resolveProfileParent(userId);
        }
        return biometricManager.getAuthenticatorIds(userId);
    }

+95 −0
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ import static com.google.common.truth.Truth.assertThat;
import android.Manifest;
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.IActivityManager;
import android.app.admin.DevicePolicyManager;
import android.app.trust.ITrustListener;
import android.app.trust.ITrustManager;
@@ -45,7 +46,9 @@ import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
@@ -53,7 +56,12 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
import android.security.Authorization;
import android.security.authorization.IKeystoreAuthorization;
import android.service.trust.TrustAgentService;
import android.testing.TestableContext;
import android.view.IWindowManager;
@@ -83,23 +91,34 @@ public class TrustManagerServiceTest {

    @Rule
    public final ExtendedMockitoRule mExtendedMockitoRule = new ExtendedMockitoRule.Builder(this)
            .spyStatic(ActivityManager.class)
            .spyStatic(Authorization.class)
            .mockStatic(ServiceManager.class)
            .mockStatic(WindowManagerGlobal.class)
            .build();

    @Rule
    public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();

    @Rule
    public final MockContext mMockContext = new MockContext(
            ApplicationProvider.getApplicationContext());

    private static final String URI_SCHEME_PACKAGE = "package";
    private static final int TEST_USER_ID = 50;
    private static final int PARENT_USER_ID = 60;
    private static final int PROFILE_USER_ID = 70;
    private static final long[] PARENT_BIOMETRIC_SIDS = new long[] { 600L, 601L };
    private static final long[] PROFILE_BIOMETRIC_SIDS = new long[] { 700L, 701L };

    private final ArrayList<ResolveInfo> mTrustAgentResolveInfoList = new ArrayList<>();
    private final ArrayList<ComponentName> mKnownTrustAgents = new ArrayList<>();
    private final ArrayList<ComponentName> mEnabledTrustAgents = new ArrayList<>();

    private @Mock ActivityManager mActivityManager;
    private @Mock BiometricManager mBiometricManager;
    private @Mock DevicePolicyManager mDevicePolicyManager;
    private @Mock IKeystoreAuthorization mKeystoreAuthorization;
    private @Mock LockPatternUtils mLockPatternUtils;
    private @Mock PackageManager mPackageManager;
    private @Mock UserManager mUserManager;
@@ -113,6 +132,9 @@ public class TrustManagerServiceTest {
    @Before
    public void setUp() throws Exception {
        when(mActivityManager.isUserRunning(TEST_USER_ID)).thenReturn(true);
        doReturn(mock(IActivityManager.class)).when(() -> ActivityManager.getService());

        doReturn(mKeystoreAuthorization).when(() -> Authorization.getService());

        when(mLockPatternUtils.getDevicePolicyManager()).thenReturn(mDevicePolicyManager);
        when(mLockPatternUtils.isSecure(TEST_USER_ID)).thenReturn(true);
@@ -146,6 +168,7 @@ public class TrustManagerServiceTest {
        when(mWindowManager.isKeyguardLocked()).thenReturn(true);

        mMockContext.addMockSystemService(ActivityManager.class, mActivityManager);
        mMockContext.addMockSystemService(BiometricManager.class, mBiometricManager);
        mMockContext.setMockPackageManager(mPackageManager);
        mMockContext.addMockSystemService(UserManager.class, mUserManager);
        doReturn(mWindowManager).when(() -> WindowManagerGlobal.getWindowManagerService());
@@ -322,6 +345,73 @@ public class TrustManagerServiceTest {
        verify(trustListener).onEnabledTrustAgentsChanged(TEST_USER_ID);
    }

    // Tests that when the device is locked for a managed profile with a *unified* challenge, the
    // device locked notification that is sent to Keystore contains the biometric SIDs of the parent
    // user, not the profile.  This matches the authentication that is needed to unlock the device
    // for the profile again.
    @Test
    @RequiresFlagsEnabled(android.security.Flags.FLAG_FIX_UNLOCKED_DEVICE_REQUIRED_KEYS_V2)
    public void testLockDeviceForManagedProfileWithUnifiedChallenge_usesParentBiometricSids()
            throws Exception {
        setupMocksForProfile(/* unifiedChallenge= */ true);

        when(mWindowManager.isKeyguardLocked()).thenReturn(false);
        mTrustManager.reportKeyguardShowingChanged();
        verify(mKeystoreAuthorization).onDeviceUnlocked(PARENT_USER_ID, null);
        verify(mKeystoreAuthorization).onDeviceUnlocked(PROFILE_USER_ID, null);

        when(mWindowManager.isKeyguardLocked()).thenReturn(true);
        mTrustManager.reportKeyguardShowingChanged();
        verify(mKeystoreAuthorization)
                .onDeviceLocked(eq(PARENT_USER_ID), eq(PARENT_BIOMETRIC_SIDS));
        verify(mKeystoreAuthorization)
                .onDeviceLocked(eq(PROFILE_USER_ID), eq(PARENT_BIOMETRIC_SIDS));
    }

    // Tests that when the device is locked for a managed profile with a *separate* challenge, the
    // device locked notification that is sent to Keystore contains the biometric SIDs of the
    // profile itself.  This matches the authentication that is needed to unlock the device for the
    // profile again.
    @Test
    public void testLockDeviceForManagedProfileWithSeparateChallenge_usesProfileBiometricSids()
            throws Exception {
        setupMocksForProfile(/* unifiedChallenge= */ false);

        mTrustManager.setDeviceLockedForUser(PROFILE_USER_ID, false);
        verify(mKeystoreAuthorization).onDeviceUnlocked(PROFILE_USER_ID, null);

        mTrustManager.setDeviceLockedForUser(PROFILE_USER_ID, true);
        verify(mKeystoreAuthorization)
                .onDeviceLocked(eq(PROFILE_USER_ID), eq(PROFILE_BIOMETRIC_SIDS));
    }

    private void setupMocksForProfile(boolean unifiedChallenge) {
        UserInfo parent = new UserInfo(PARENT_USER_ID, "parent", UserInfo.FLAG_FULL);
        UserInfo profile = new UserInfo(PROFILE_USER_ID, "profile", UserInfo.FLAG_MANAGED_PROFILE);
        when(mUserManager.getAliveUsers()).thenReturn(List.of(parent, profile));
        when(mUserManager.getUserInfo(PARENT_USER_ID)).thenReturn(parent);
        when(mUserManager.getUserInfo(PROFILE_USER_ID)).thenReturn(profile);
        when(mUserManager.getProfileParent(PROFILE_USER_ID)).thenReturn(parent);
        when(mUserManager.getEnabledProfileIds(PARENT_USER_ID))
                .thenReturn(new int[] { PROFILE_USER_ID });

        when(mLockPatternUtils.isSecure(anyInt())).thenReturn(true);
        when(mLockPatternUtils.isProfileWithUnifiedChallenge(PROFILE_USER_ID))
                .thenReturn(unifiedChallenge);
        when(mLockPatternUtils.isManagedProfileWithUnifiedChallenge(PROFILE_USER_ID))
                .thenReturn(unifiedChallenge);
        when(mLockPatternUtils.isSeparateProfileChallengeEnabled(PROFILE_USER_ID))
                .thenReturn(!unifiedChallenge);

        when(mBiometricManager.getAuthenticatorIds(PARENT_USER_ID))
                .thenReturn(PARENT_BIOMETRIC_SIDS);
        when(mBiometricManager.getAuthenticatorIds(PROFILE_USER_ID))
                .thenReturn(PROFILE_BIOMETRIC_SIDS);

        bootService();
        mService.onUserSwitching(null, new SystemService.TargetUser(parent));
    }

    private void addTrustAgent(ComponentName agentComponentName, boolean isSystemApp) {
        ApplicationInfo applicationInfo = new ApplicationInfo();
        if (isSystemApp) {
@@ -378,6 +468,11 @@ public class TrustManagerServiceTest {
                    scheduler);
        }

        @Override
        public void sendBroadcastAsUser(Intent intent, UserHandle user,
                @Nullable String receiverPermission, @Nullable Bundle options) {
        }

        void sendPackageChangedBroadcast(ComponentName changedComponent) {
            Intent intent = new Intent(
                    Intent.ACTION_PACKAGE_CHANGED,