Merge "linkToDeath on BiometricPrompt#authenticate" into rvc-dev

This commit is contained in:
Kevin Chyn
2020-05-20 05:49:16 +00:00
committed by Android (Google) Code Review
4 changed files with 149 additions and 15 deletions

View File

@@ -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

View File

@@ -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;
}

View File

@@ -233,11 +233,17 @@ 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;
mListener = null;
if (clearListener) {
mListener = null;
}
}
@Override

View File

@@ -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() {