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

Commit 61852ac6 authored by Chandru S's avatar Chandru S Committed by Android (Google) Code Review
Browse files

Merge "Add more unit tests for the current face auth logic in KUM" into tm-qpr-dev

parents 9ae4e862 db1d91a6
Loading
Loading
Loading
Loading
+329 −10
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.keyguard;

import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT;
import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE;
import static android.telephony.SubscriptionManager.NAME_SOURCE_CARRIER_ID;

@@ -31,7 +32,6 @@ import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
@@ -41,6 +41,7 @@ import static org.mockito.Mockito.when;

import android.app.Activity;
import android.app.ActivityManager;
import android.app.IActivityManager;
import android.app.admin.DevicePolicyManager;
import android.app.trust.IStrongAuthTracker;
import android.app.trust.TrustManager;
@@ -52,6 +53,7 @@ import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricSourceType;
@@ -68,6 +70,7 @@ import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.IRemoteCallback;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import android.telephony.ServiceState;
@@ -182,6 +185,18 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
    private ActiveUnlockConfig mActiveUnlockConfig;
    @Mock
    private KeyguardUpdateMonitorLogger mKeyguardUpdateMonitorLogger;
    @Mock
    private IActivityManager mActivityService;

    private final int mCurrentUserId = 100;
    private final UserInfo mCurrentUserInfo = new UserInfo(mCurrentUserId, "Test user", 0);

    @Captor
    private ArgumentCaptor<IBiometricEnabledOnKeyguardCallback>
            mBiometricEnabledCallbackArgCaptor;
    @Captor
    private ArgumentCaptor<FaceManager.AuthenticationCallback> mAuthenticationCallbackCaptor;

    // Direct executor
    private Executor mBackgroundExecutor = Runnable::run;
    private Executor mMainExecutor = Runnable::run;
@@ -190,18 +205,16 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
    private TestableContext mSpiedContext;
    private MockitoSession mMockitoSession;
    private StatusBarStateController.StateListener mStatusBarStateListener;
    private IBiometricEnabledOnKeyguardCallback mBiometricEnabledOnKeyguardCallback;

    @Before
    public void setup() {
    public void setup() throws RemoteException {
        MockitoAnnotations.initMocks(this);
        mSpiedContext = spy(mContext);
        when(mPackageManager.hasSystemFeature(anyString())).thenReturn(true);
        when(mSpiedContext.getPackageManager()).thenReturn(mPackageManager);
        doAnswer(invocation -> {
            IBiometricEnabledOnKeyguardCallback callback = invocation.getArgument(0);
            callback.onChanged(true /* enabled */, KeyguardUpdateMonitor.getCurrentUser());
            return null;
        }).when(mBiometricManager).registerEnabledOnKeyguardCallback(any());
        when(mActivityService.getCurrentUser()).thenReturn(mCurrentUserInfo);
        when(mActivityService.getCurrentUserId()).thenReturn(mCurrentUserId);
        when(mFaceManager.isHardwareDetected()).thenReturn(true);
        when(mFaceManager.hasEnrolledTemplates()).thenReturn(true);
        when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
@@ -262,13 +275,20 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
                .startMocking();
        ExtendedMockito.doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
                .when(SubscriptionManager::getDefaultSubscriptionId);
        KeyguardUpdateMonitor.setCurrentUser(mCurrentUserId);
        ExtendedMockito.doReturn(KeyguardUpdateMonitor.getCurrentUser())
                .when(ActivityManager::getCurrentUser);
        ExtendedMockito.doReturn(mActivityService).when(ActivityManager::getService);

        mTestableLooper = TestableLooper.get(this);
        allowTestableLooperAsMainThread();
        mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mSpiedContext);

        verify(mBiometricManager)
                .registerEnabledOnKeyguardCallback(mBiometricEnabledCallbackArgCaptor.capture());
        mBiometricEnabledOnKeyguardCallback = mBiometricEnabledCallbackArgCaptor.getValue();
        biometricsEnabledForCurrentUser();

        verify(mStatusBarStateController).addCallback(mStatusBarStateListenerCaptor.capture());
        mStatusBarStateListener = mStatusBarStateListenerCaptor.getValue();
        mKeyguardUpdateMonitor.registerCallback(mTestCallback);
@@ -718,7 +738,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
        verify(mLockPatternUtils).requireStrongAuth(anyInt(), anyInt());
    }


    @Test
    public void testFaceAndFingerprintLockout() {
        mKeyguardUpdateMonitor.dispatchStartedWakingUp();
@@ -924,6 +943,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {

    @Test
    public void testSecondaryLockscreenRequirement() {
        KeyguardUpdateMonitor.setCurrentUser(UserHandle.myUserId());
        int user = KeyguardUpdateMonitor.getCurrentUser();
        String packageName = "fake.test.package";
        String cls = "FakeService";
@@ -1097,8 +1117,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
    @Test
    public void testShouldNotUpdateBiometricListeningStateOnStatusBarStateChange() {
        // GIVEN state for face auth should run aside from StatusBarState
        when(mDevicePolicyManager.getKeyguardDisabledFeatures(null,
                KeyguardUpdateMonitor.getCurrentUser())).thenReturn(0);
        biometricsNotDisabledThroughDevicePolicyManager();
        mStatusBarStateListener.onStateChanged(StatusBarState.SHADE_LOCKED);
        setKeyguardBouncerVisibility(false /* isVisible */);
        mKeyguardUpdateMonitor.dispatchStartedWakingUp();
@@ -1153,6 +1172,306 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
        verify(mTestCallback).showTrustGrantedMessage("Unlocked by wearable");
    }

    @Test
    public void testShouldListenForFace_whenFaceManagerNotAvailable_returnsFalse() {
        mFaceManager = null;
        mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mSpiedContext);

        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
    }

    @Test
    public void testShouldListenForFace_whenFpIsLockedOut_returnsFalse() throws RemoteException {
        // Face auth should run when the following is true.
        keyguardNotGoingAway();
        bouncerFullyVisibleAndNotGoingToSleep();
        currentUserIsPrimary();
        strongAuthNotRequired();
        biometricsEnabledForCurrentUser();
        currentUserDoesNotHaveTrust();
        biometricsNotDisabledThroughDevicePolicyManager();
        userNotCurrentlySwitching();
        mTestableLooper.processAllMessages();

        // Fingerprint is locked out.
        fingerprintErrorLockedOut();

        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
    }

    @Test
    public void testShouldListenForFace_whenFaceIsAlreadyAuthenticated_returnsFalse()
            throws RemoteException {
        // Face auth should run when the following is true.
        bouncerFullyVisibleAndNotGoingToSleep();
        keyguardNotGoingAway();
        currentUserIsPrimary();
        strongAuthNotRequired();
        biometricsEnabledForCurrentUser();
        currentUserDoesNotHaveTrust();
        biometricsNotDisabledThroughDevicePolicyManager();
        userNotCurrentlySwitching();

        mTestableLooper.processAllMessages();

        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();

        triggerSuccessfulFaceAuth();
        mTestableLooper.processAllMessages();

        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
    }

    @Test
    public void testShouldListenForFace_whenUserIsNotPrimary_returnsFalse() throws RemoteException {
        // This disables face auth
        when(mUserManager.isPrimaryUser()).thenReturn(false);
        mKeyguardUpdateMonitor =
                new TestableKeyguardUpdateMonitor(mSpiedContext);

        // Face auth should run when the following is true.
        keyguardNotGoingAway();
        bouncerFullyVisibleAndNotGoingToSleep();
        strongAuthNotRequired();
        biometricsEnabledForCurrentUser();
        currentUserDoesNotHaveTrust();
        biometricsNotDisabledThroughDevicePolicyManager();
        userNotCurrentlySwitching();
        mTestableLooper.processAllMessages();


        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
    }

    @Test
    public void testShouldListenForFace_whenStrongAuthDoesNotAllowScanning_returnsFalse()
            throws RemoteException {
        // Face auth should run when the following is true.
        keyguardNotGoingAway();
        bouncerFullyVisibleAndNotGoingToSleep();
        currentUserIsPrimary();
        biometricsEnabledForCurrentUser();
        currentUserDoesNotHaveTrust();
        biometricsNotDisabledThroughDevicePolicyManager();
        userNotCurrentlySwitching();

        // This disables face auth
        when(mStrongAuthTracker.getStrongAuthForUser(KeyguardUpdateMonitor.getCurrentUser()))
                .thenReturn(STRONG_AUTH_REQUIRED_AFTER_BOOT);
        mTestableLooper.processAllMessages();


        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
    }

    @Test
    public void testShouldListenForFace_whenBiometricsDisabledForUser_returnsFalse()
            throws RemoteException {
        // Face auth should run when the following is true.
        keyguardNotGoingAway();
        bouncerFullyVisibleAndNotGoingToSleep();
        currentUserIsPrimary();
        currentUserDoesNotHaveTrust();
        biometricsNotDisabledThroughDevicePolicyManager();
        biometricsEnabledForCurrentUser();
        userNotCurrentlySwitching();
        mTestableLooper.processAllMessages();

        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();

        // This disables face auth
        biometricsDisabledForCurrentUser();
        mTestableLooper.processAllMessages();

        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
    }

    @Test
    public void testShouldListenForFace_whenUserCurrentlySwitching_returnsFalse()
            throws RemoteException {
        // Face auth should run when the following is true.
        keyguardNotGoingAway();
        bouncerFullyVisibleAndNotGoingToSleep();
        currentUserIsPrimary();
        currentUserDoesNotHaveTrust();
        biometricsNotDisabledThroughDevicePolicyManager();
        biometricsEnabledForCurrentUser();
        userNotCurrentlySwitching();
        mTestableLooper.processAllMessages();

        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();

        userCurrentlySwitching();
        mTestableLooper.processAllMessages();

        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
    }

    @Test
    public void testShouldListenForFace_whenSecureCameraLaunched_returnsFalse()
            throws RemoteException {
        // Face auth should run when the following is true.
        keyguardNotGoingAway();
        bouncerFullyVisibleAndNotGoingToSleep();
        currentUserIsPrimary();
        currentUserDoesNotHaveTrust();
        biometricsNotDisabledThroughDevicePolicyManager();
        biometricsEnabledForCurrentUser();
        userNotCurrentlySwitching();
        mTestableLooper.processAllMessages();

        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();

        secureCameraLaunched();
        mTestableLooper.processAllMessages();

        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
    }

    @Test
    public void testShouldListenForFace_whenOccludingAppRequestsFaceAuth_returnsTrue()
            throws RemoteException {
        // Face auth should run when the following is true.
        keyguardNotGoingAway();
        bouncerFullyVisibleAndNotGoingToSleep();
        currentUserIsPrimary();
        currentUserDoesNotHaveTrust();
        biometricsNotDisabledThroughDevicePolicyManager();
        biometricsEnabledForCurrentUser();
        userNotCurrentlySwitching();
        mTestableLooper.processAllMessages();

        secureCameraLaunched();

        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();

        occludingAppRequestsFaceAuth();
        mTestableLooper.processAllMessages();

        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
    }

    @Test
    public void testShouldListenForFace_whenBouncerShowingAndDeviceIsAwake_returnsTrue()
            throws RemoteException {
        // Face auth should run when the following is true.
        keyguardNotGoingAway();
        currentUserIsPrimary();
        currentUserDoesNotHaveTrust();
        biometricsNotDisabledThroughDevicePolicyManager();
        biometricsEnabledForCurrentUser();
        userNotCurrentlySwitching();
        mTestableLooper.processAllMessages();

        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();

        bouncerFullyVisibleAndNotGoingToSleep();
        mTestableLooper.processAllMessages();

        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
    }

    @Test
    public void testShouldListenForFace_whenAuthInterruptIsActive_returnsTrue()
            throws RemoteException {
        // Face auth should run when the following is true.
        keyguardNotGoingAway();
        currentUserIsPrimary();
        currentUserDoesNotHaveTrust();
        biometricsNotDisabledThroughDevicePolicyManager();
        biometricsEnabledForCurrentUser();
        userNotCurrentlySwitching();
        mTestableLooper.processAllMessages();

        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();

        triggerAuthInterrupt();
        mTestableLooper.processAllMessages();

        assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
    }

    private void triggerAuthInterrupt() {
        mKeyguardUpdateMonitor.onAuthInterruptDetected(true);
    }

    private void occludingAppRequestsFaceAuth() {
        mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(true);
    }

    private void secureCameraLaunched() {
        mKeyguardUpdateMonitor.onCameraLaunched();
    }

    private void userCurrentlySwitching() {
        mKeyguardUpdateMonitor.setSwitchingUser(true);
    }

    private void fingerprintErrorLockedOut() {
        mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback
                .onAuthenticationError(FINGERPRINT_ERROR_LOCKOUT, "Fingerprint locked out");
    }

    private void triggerSuccessfulFaceAuth() {
        mKeyguardUpdateMonitor.requestFaceAuth(true);
        verify(mFaceManager).authenticate(any(),
                any(),
                mAuthenticationCallbackCaptor.capture(),
                any(),
                anyInt(),
                anyBoolean());
        mAuthenticationCallbackCaptor.getValue()
                .onAuthenticationSucceeded(
                        new FaceManager.AuthenticationResult(null, null, mCurrentUserId, false));
    }

    private void currentUserIsPrimary() {
        when(mUserManager.isPrimaryUser()).thenReturn(true);
    }

    private void biometricsNotDisabledThroughDevicePolicyManager() {
        when(mDevicePolicyManager.getKeyguardDisabledFeatures(null,
                KeyguardUpdateMonitor.getCurrentUser())).thenReturn(0);
    }

    private void biometricsEnabledForCurrentUser() throws RemoteException {
        mBiometricEnabledOnKeyguardCallback.onChanged(true, KeyguardUpdateMonitor.getCurrentUser());
    }

    private void biometricsDisabledForCurrentUser() throws RemoteException {
        mBiometricEnabledOnKeyguardCallback.onChanged(
                false,
                KeyguardUpdateMonitor.getCurrentUser()
        );
    }

    private void strongAuthNotRequired() {
        when(mStrongAuthTracker.getStrongAuthForUser(KeyguardUpdateMonitor.getCurrentUser()))
                .thenReturn(0);
    }

    private void currentUserDoesNotHaveTrust() {
        mKeyguardUpdateMonitor.onTrustChanged(
                false,
                KeyguardUpdateMonitor.getCurrentUser(),
                -1,
                new ArrayList<>()
        );
    }

    private void userNotCurrentlySwitching() {
        mKeyguardUpdateMonitor.setSwitchingUser(false);
    }

    private void keyguardNotGoingAway() {
        mKeyguardUpdateMonitor.setKeyguardGoingAway(false);
    }

    private void bouncerFullyVisibleAndNotGoingToSleep() {
        mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(true, true);
        mKeyguardUpdateMonitor.dispatchFinishedGoingToSleep(/* value doesn't matter */1);
    }

    private void setKeyguardBouncerVisibility(boolean isVisible) {
        mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(isVisible, isVisible);
        mTestableLooper.processAllMessages();