Loading services/core/java/com/android/server/biometrics/AuthenticationClient.java +2 −5 Original line number Diff line number Diff line Loading @@ -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 Loading services/core/java/com/android/server/biometrics/BiometricService.java +61 −3 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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. Loading Loading @@ -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() { Loading @@ -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; Loading Loading @@ -370,6 +389,11 @@ public class BiometricService extends SystemService { break; } case MSG_CLIENT_DIED: { handleClientDied(); break; } default: Slog.e(TAG, "Unknown message: " + msg); break; Loading Loading @@ -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 Loading Loading @@ -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); Loading Loading @@ -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. Loading Loading @@ -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; } Loading services/core/java/com/android/server/biometrics/ClientMonitor.java +7 −1 Original line number Diff line number Diff line Loading @@ -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 { Loading services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java +79 −6 Original line number Diff line number Diff line Loading @@ -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)) Loading Loading @@ -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 { Loading Loading @@ -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); Loading @@ -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 */, Loading Loading @@ -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)); Loading @@ -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, Loading @@ -1244,7 +1314,7 @@ public class BiometricServiceTest { } } resetReceiver(); resetReceivers(); authenticators = Authenticators.BIOMETRIC_STRONG; assertEquals(BiometricManager.BIOMETRIC_SUCCESS, invokeCanAuthenticate(mBiometricService, authenticators)); Loading Loading @@ -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() { Loading Loading
services/core/java/com/android/server/biometrics/AuthenticationClient.java +2 −5 Original line number Diff line number Diff line Loading @@ -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 Loading
services/core/java/com/android/server/biometrics/BiometricService.java +61 −3 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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. Loading Loading @@ -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() { Loading @@ -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; Loading Loading @@ -370,6 +389,11 @@ public class BiometricService extends SystemService { break; } case MSG_CLIENT_DIED: { handleClientDied(); break; } default: Slog.e(TAG, "Unknown message: " + msg); break; Loading Loading @@ -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 Loading Loading @@ -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); Loading Loading @@ -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. Loading Loading @@ -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; } Loading
services/core/java/com/android/server/biometrics/ClientMonitor.java +7 −1 Original line number Diff line number Diff line Loading @@ -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 { Loading
services/tests/servicestests/src/com/android/server/biometrics/BiometricServiceTest.java +79 −6 Original line number Diff line number Diff line Loading @@ -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)) Loading Loading @@ -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 { Loading Loading @@ -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); Loading @@ -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 */, Loading Loading @@ -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)); Loading @@ -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, Loading @@ -1244,7 +1314,7 @@ public class BiometricServiceTest { } } resetReceiver(); resetReceivers(); authenticators = Authenticators.BIOMETRIC_STRONG; assertEquals(BiometricManager.BIOMETRIC_SUCCESS, invokeCanAuthenticate(mBiometricService, authenticators)); Loading Loading @@ -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() { Loading