Merge "linkToDeath on BiometricPrompt#authenticate" into rvc-dev
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user