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

Commit 3b2d32e9 authored by Kevin Chyn's avatar Kevin Chyn Committed by Automerger Merge Worker
Browse files

Merge "linkToDeath on BiometricPrompt#authenticate" into rvc-dev am: e6fe6013

Change-Id: I70840c874cc2aa139fcc2164e4bc600873c9a1e8
parents 1bd4b0a7 e6fe6013
Loading
Loading
Loading
Loading
+2 −5
Original line number Diff line number Diff line
@@ -84,11 +84,8 @@ public abstract class AuthenticationClient extends ClientMonitor {

    @Override
    public void binderDied() {
        super.binderDied();
        // When the binder dies, we should stop the client. This probably belongs in
        // ClientMonitor's binderDied(), but testing all the cases would be tricky.
        // AuthenticationClient is the most user-visible case.
        stop(false /* initiatedByClient */);
        final boolean clearListener = !isBiometricPrompt();
        binderDiedInternal(clearListener);
    }

    @Override
+61 −3
Original line number Diff line number Diff line
@@ -113,6 +113,7 @@ public class BiometricService extends SystemService {
    private static final int MSG_ON_AUTHENTICATION_TIMED_OUT = 11;
    private static final int MSG_ON_DEVICE_CREDENTIAL_PRESSED = 12;
    private static final int MSG_ON_SYSTEM_EVENT = 13;
    private static final int MSG_CLIENT_DIED = 14;

    /**
     * Authentication either just called and we have not transitioned to the CALLED state, or
@@ -151,8 +152,13 @@ public class BiometricService extends SystemService {
     * Device credential in AuthController is showing
     */
    static final int STATE_SHOWING_DEVICE_CREDENTIAL = 8;
    /**
     * The client binder died, and sensors were authenticating at the time. Cancel has been
     * requested and we're waiting for the HAL(s) to send ERROR_CANCELED.
     */
    static final int STATE_CLIENT_DIED_CANCELLING = 9;

    final class AuthSession {
    final class AuthSession implements IBinder.DeathRecipient {
        // Map of Authenticator/Cookie pairs. We expect to receive the cookies back from
        // <Biometric>Services before we can start authenticating. Pairs that have been returned
        // are moved to mModalitiesMatched.
@@ -211,7 +217,14 @@ public class BiometricService extends SystemService {
            mCallingUserId = callingUserId;
            mModality = modality;
            mRequireConfirmation = requireConfirmation;

            Slog.d(TAG, "New AuthSession, mSysUiSessionId: " + mSysUiSessionId);

            try {
                mClientReceiver.asBinder().linkToDeath(this, 0 /* flags */);
            } catch (RemoteException e) {
                Slog.w(TAG, "Unable to link to death");
            }
        }

        boolean isCrypto() {
@@ -231,6 +244,12 @@ public class BiometricService extends SystemService {
        boolean isAllowDeviceCredential() {
            return Utils.isCredentialRequested(mBundle);
        }

        @Override
        public void binderDied() {
            Slog.e(TAG, "Binder died, sysUiSessionId: " + mSysUiSessionId);
            mHandler.obtainMessage(MSG_CLIENT_DIED).sendToTarget();
        }
    }

    private final Injector mInjector;
@@ -370,6 +389,11 @@ public class BiometricService extends SystemService {
                    break;
                }

                case MSG_CLIENT_DIED: {
                    handleClientDied();
                    break;
                }

                default:
                    Slog.e(TAG, "Unknown message: " + msg);
                    break;
@@ -1391,6 +1415,7 @@ public class BiometricService extends SystemService {
    }

    private void handleOnError(int cookie, int modality, int error, int vendorCode) {

        Slog.d(TAG, "handleOnError: " + error + " cookie: " + cookie);
        // Errors can either be from the current auth session or the pending auth session.
        // The pending auth session may receive errors such as ERROR_LOCKOUT before
@@ -1431,6 +1456,9 @@ public class BiometricService extends SystemService {
                } else if (mCurrentAuthSession.mState == STATE_SHOWING_DEVICE_CREDENTIAL) {
                    Slog.d(TAG, "Biometric canceled, ignoring from state: "
                            + mCurrentAuthSession.mState);
                } else if (mCurrentAuthSession.mState == STATE_CLIENT_DIED_CANCELLING) {
                    mStatusBarService.hideAuthenticationDialog();
                    mCurrentAuthSession = null;
                } else {
                    Slog.e(TAG, "Impossible session error state: "
                            + mCurrentAuthSession.mState);
@@ -1622,6 +1650,36 @@ public class BiometricService extends SystemService {
        }
    }

    private void handleClientDied() {
        if (mCurrentAuthSession == null) {
            Slog.e(TAG, "Auth session null");
            return;
        }

        Slog.e(TAG, "SysUiSessionId: " + mCurrentAuthSession.mSysUiSessionId
                + " State: " + mCurrentAuthSession.mState);

        try {
            // Check if any sensors are authenticating. If so, need to cancel them. When
            // ERROR_CANCELED is received from the HAL, we hide the dialog and cleanup the session.
            if (mCurrentAuthSession.mState == STATE_AUTH_STARTED) {
                mCurrentAuthSession.mState = STATE_CLIENT_DIED_CANCELLING;
                cancelInternal(mCurrentAuthSession.mToken,
                        mCurrentAuthSession.mOpPackageName,
                        mCurrentAuthSession.mCallingUid,
                        mCurrentAuthSession.mCallingPid,
                        mCurrentAuthSession.mCallingUserId,
                        false /* fromClient */);
            } else {
                // If the sensors are not authenticating, set the auth session to null.
                mStatusBarService.hideAuthenticationDialog();
                mCurrentAuthSession = null;
            }
        } catch (RemoteException e) {
            Slog.e(TAG, "Remote exception: " + e);
        }
    }

    /**
     * Invoked when each service has notified that its client is ready to be started. When
     * all biometrics are ready, this invokes the SystemUI dialog through StatusBar.
@@ -1822,11 +1880,11 @@ public class BiometricService extends SystemService {

    void cancelInternal(IBinder token, String opPackageName, int callingUid, int callingPid,
            int callingUserId, boolean fromClient) {

        if (mCurrentAuthSession == null) {
            Slog.w(TAG, "Skipping cancelInternal");
            return;
        } else if (mCurrentAuthSession.mState != STATE_AUTH_STARTED) {
        } else if (mCurrentAuthSession.mState != STATE_AUTH_STARTED
                && mCurrentAuthSession.mState != STATE_CLIENT_DIED_CANCELLING) {
            Slog.w(TAG, "Skipping cancelInternal, state: " + mCurrentAuthSession.mState);
            return;
        }
+7 −1
Original line number Diff line number Diff line
@@ -233,12 +233,18 @@ public abstract class ClientMonitor extends LoggableMonitor implements IBinder.D

    @Override
    public void binderDied() {
        binderDiedInternal(true /* clearListener */);
    }

    void binderDiedInternal(boolean clearListener) {
        // If the current client dies we should cancel the current operation.
        Slog.e(getLogTag(), "Binder died, cancelling client");
        stop(false /* initiatedByClient */);
        mToken = null;
        if (clearListener) {
            mListener = null;
        }
    }

    @Override
    protected void finalize() throws Throwable {
+79 −6
Original line number Diff line number Diff line
@@ -115,6 +115,8 @@ public class BiometricServiceTest {
    public void setUp() {
        MockitoAnnotations.initMocks(this);

        resetReceivers();

        when(mContext.getContentResolver()).thenReturn(mContentResolver);
        when(mContext.getResources()).thenReturn(mResources);
        when(mContext.getSystemService(Context.DEVICE_POLICY_SERVICE))
@@ -146,6 +148,74 @@ public class BiometricServiceTest {
        when(mInjector.getConfiguration(any())).thenReturn(config);
    }

    @Test
    public void testClientBinderDied_whenPaused() throws Exception {
        setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG);

        invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
                true /* requireConfirmation */, null /* authenticators */);
        waitForIdle();
        verify(mReceiver1.asBinder()).linkToDeath(eq(mBiometricService.mCurrentAuthSession),
                anyInt());

        mBiometricService.mInternalReceiver.onError(
                getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
                BiometricAuthenticator.TYPE_FACE,
                BiometricConstants.BIOMETRIC_ERROR_TIMEOUT,
                0 /* vendorCode */);
        waitForIdle();

        assertEquals(BiometricService.STATE_AUTH_PAUSED,
                mBiometricService.mCurrentAuthSession.mState);

        mBiometricService.mCurrentAuthSession.binderDied();
        waitForIdle();

        assertNull(mBiometricService.mCurrentAuthSession);
        verify(mBiometricService.mStatusBarService).hideAuthenticationDialog();
        verify(mReceiver1, never()).onError(anyInt(), anyInt(), anyInt());
    }

    @Test
    public void testClientBinderDied_whenAuthenticating() throws Exception {
        setupAuthForOnly(BiometricAuthenticator.TYPE_FACE, Authenticators.BIOMETRIC_STRONG);

        invokeAuthenticateAndStart(mBiometricService.mImpl, mReceiver1,
                true /* requireConfirmation */, null /* authenticators */);
        waitForIdle();
        verify(mReceiver1.asBinder()).linkToDeath(eq(mBiometricService.mCurrentAuthSession),
                anyInt());

        assertEquals(BiometricService.STATE_AUTH_STARTED,
                mBiometricService.mCurrentAuthSession.mState);
        mBiometricService.mCurrentAuthSession.binderDied();
        waitForIdle();

        assertNotNull(mBiometricService.mCurrentAuthSession);
        verify(mBiometricService.mStatusBarService, never()).hideAuthenticationDialog();
        assertEquals(BiometricService.STATE_CLIENT_DIED_CANCELLING,
                mBiometricService.mCurrentAuthSession.mState);

        verify(mBiometricService.mAuthenticators.get(0).impl).cancelAuthenticationFromService(
                any(),
                any(),
                anyInt(),
                anyInt(),
                anyInt(),
                eq(false) /* fromClient */);

        // Simulate ERROR_CANCELED received from HAL
        mBiometricService.mInternalReceiver.onError(
                getCookieForCurrentSession(mBiometricService.mCurrentAuthSession),
                BiometricAuthenticator.TYPE_FACE,
                BiometricConstants.BIOMETRIC_ERROR_CANCELED,
                0 /* vendorCode */);
        waitForIdle();
        verify(mBiometricService.mStatusBarService).hideAuthenticationDialog();
        verify(mReceiver1, never()).onError(anyInt(), anyInt(), anyInt());
        assertNull(mBiometricService.mCurrentAuthSession);
    }

    @Test
    public void testAuthenticate_credentialAllowedButNotSetup_returnsNoDeviceCredential()
            throws Exception {
@@ -311,7 +381,7 @@ public class BiometricServiceTest {
                eq(0 /* vendorCode */));

        // Enrolled, not disabled in settings, user requires confirmation in settings
        resetReceiver();
        resetReceivers();
        when(mBiometricService.mSettingObserver.getFaceEnabledForApps(anyInt())).thenReturn(true);
        when(mBiometricService.mSettingObserver.getFaceAlwaysRequireConfirmation(anyInt()))
                .thenReturn(true);
@@ -332,7 +402,7 @@ public class BiometricServiceTest {
                anyInt() /* callingUserId */);

        // Enrolled, not disabled in settings, user doesn't require confirmation in settings
        resetReceiver();
        resetReceivers();
        when(mBiometricService.mSettingObserver.getFaceAlwaysRequireConfirmation(anyInt()))
                .thenReturn(false);
        invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
@@ -1198,7 +1268,7 @@ public class BiometricServiceTest {
                eq(0) /* vendorCode */);

        // Request for weak auth works
        resetReceiver();
        resetReceivers();
        authenticators = Authenticators.BIOMETRIC_WEAK;
        assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
                invokeCanAuthenticate(mBiometricService, authenticators));
@@ -1217,7 +1287,7 @@ public class BiometricServiceTest {
                anyInt() /* sysUiSessionId */);

        // Requesting strong and credential, when credential is setup
        resetReceiver();
        resetReceivers();
        authenticators = Authenticators.BIOMETRIC_STRONG | Authenticators.DEVICE_CREDENTIAL;
        when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(true);
        assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
@@ -1244,7 +1314,7 @@ public class BiometricServiceTest {
            }
        }

        resetReceiver();
        resetReceivers();
        authenticators = Authenticators.BIOMETRIC_STRONG;
        assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
                invokeCanAuthenticate(mBiometricService, authenticators));
@@ -1449,9 +1519,12 @@ public class BiometricServiceTest {
        }
    }

    private void resetReceiver() {
    private void resetReceivers() {
        mReceiver1 = mock(IBiometricServiceReceiver.class);
        mReceiver2 = mock(IBiometricServiceReceiver.class);

        when(mReceiver1.asBinder()).thenReturn(mock(Binder.class));
        when(mReceiver2.asBinder()).thenReturn(mock(Binder.class));
    }

    private void resetStatusBar() {