Merge "linkToDeath on BiometricPrompt#authenticate" into rvc-dev am: e6fe6013cb
Change-Id: I70840c874cc2aa139fcc2164e4bc600873c9a1e8
This commit is contained in:
@@ -84,11 +84,8 @@ public abstract class AuthenticationClient extends ClientMonitor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void binderDied() {
|
public void binderDied() {
|
||||||
super.binderDied();
|
final boolean clearListener = !isBiometricPrompt();
|
||||||
// When the binder dies, we should stop the client. This probably belongs in
|
binderDiedInternal(clearListener);
|
||||||
// ClientMonitor's binderDied(), but testing all the cases would be tricky.
|
|
||||||
// AuthenticationClient is the most user-visible case.
|
|
||||||
stop(false /* initiatedByClient */);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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_AUTHENTICATION_TIMED_OUT = 11;
|
||||||
private static final int MSG_ON_DEVICE_CREDENTIAL_PRESSED = 12;
|
private static final int MSG_ON_DEVICE_CREDENTIAL_PRESSED = 12;
|
||||||
private static final int MSG_ON_SYSTEM_EVENT = 13;
|
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
|
* 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
|
* Device credential in AuthController is showing
|
||||||
*/
|
*/
|
||||||
static final int STATE_SHOWING_DEVICE_CREDENTIAL = 8;
|
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
|
// 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
|
// <Biometric>Services before we can start authenticating. Pairs that have been returned
|
||||||
// are moved to mModalitiesMatched.
|
// are moved to mModalitiesMatched.
|
||||||
@@ -211,7 +217,14 @@ public class BiometricService extends SystemService {
|
|||||||
mCallingUserId = callingUserId;
|
mCallingUserId = callingUserId;
|
||||||
mModality = modality;
|
mModality = modality;
|
||||||
mRequireConfirmation = requireConfirmation;
|
mRequireConfirmation = requireConfirmation;
|
||||||
|
|
||||||
Slog.d(TAG, "New AuthSession, mSysUiSessionId: " + mSysUiSessionId);
|
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() {
|
boolean isCrypto() {
|
||||||
@@ -231,6 +244,12 @@ public class BiometricService extends SystemService {
|
|||||||
boolean isAllowDeviceCredential() {
|
boolean isAllowDeviceCredential() {
|
||||||
return Utils.isCredentialRequested(mBundle);
|
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;
|
private final Injector mInjector;
|
||||||
@@ -370,6 +389,11 @@ public class BiometricService extends SystemService {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case MSG_CLIENT_DIED: {
|
||||||
|
handleClientDied();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Slog.e(TAG, "Unknown message: " + msg);
|
Slog.e(TAG, "Unknown message: " + msg);
|
||||||
break;
|
break;
|
||||||
@@ -1391,6 +1415,7 @@ public class BiometricService extends SystemService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void handleOnError(int cookie, int modality, int error, int vendorCode) {
|
private void handleOnError(int cookie, int modality, int error, int vendorCode) {
|
||||||
|
|
||||||
Slog.d(TAG, "handleOnError: " + error + " cookie: " + cookie);
|
Slog.d(TAG, "handleOnError: " + error + " cookie: " + cookie);
|
||||||
// Errors can either be from the current auth session or the pending auth session.
|
// 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
|
// 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) {
|
} else if (mCurrentAuthSession.mState == STATE_SHOWING_DEVICE_CREDENTIAL) {
|
||||||
Slog.d(TAG, "Biometric canceled, ignoring from state: "
|
Slog.d(TAG, "Biometric canceled, ignoring from state: "
|
||||||
+ mCurrentAuthSession.mState);
|
+ mCurrentAuthSession.mState);
|
||||||
|
} else if (mCurrentAuthSession.mState == STATE_CLIENT_DIED_CANCELLING) {
|
||||||
|
mStatusBarService.hideAuthenticationDialog();
|
||||||
|
mCurrentAuthSession = null;
|
||||||
} else {
|
} else {
|
||||||
Slog.e(TAG, "Impossible session error state: "
|
Slog.e(TAG, "Impossible session error state: "
|
||||||
+ mCurrentAuthSession.mState);
|
+ 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
|
* 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.
|
* 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,
|
void cancelInternal(IBinder token, String opPackageName, int callingUid, int callingPid,
|
||||||
int callingUserId, boolean fromClient) {
|
int callingUserId, boolean fromClient) {
|
||||||
|
|
||||||
if (mCurrentAuthSession == null) {
|
if (mCurrentAuthSession == null) {
|
||||||
Slog.w(TAG, "Skipping cancelInternal");
|
Slog.w(TAG, "Skipping cancelInternal");
|
||||||
return;
|
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);
|
Slog.w(TAG, "Skipping cancelInternal, state: " + mCurrentAuthSession.mState);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -233,12 +233,18 @@ public abstract class ClientMonitor extends LoggableMonitor implements IBinder.D
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void binderDied() {
|
public void binderDied() {
|
||||||
|
binderDiedInternal(true /* clearListener */);
|
||||||
|
}
|
||||||
|
|
||||||
|
void binderDiedInternal(boolean clearListener) {
|
||||||
// If the current client dies we should cancel the current operation.
|
// If the current client dies we should cancel the current operation.
|
||||||
Slog.e(getLogTag(), "Binder died, cancelling client");
|
Slog.e(getLogTag(), "Binder died, cancelling client");
|
||||||
stop(false /* initiatedByClient */);
|
stop(false /* initiatedByClient */);
|
||||||
mToken = null;
|
mToken = null;
|
||||||
|
if (clearListener) {
|
||||||
mListener = null;
|
mListener = null;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void finalize() throws Throwable {
|
protected void finalize() throws Throwable {
|
||||||
|
|||||||
@@ -115,6 +115,8 @@ public class BiometricServiceTest {
|
|||||||
public void setUp() {
|
public void setUp() {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
|
|
||||||
|
resetReceivers();
|
||||||
|
|
||||||
when(mContext.getContentResolver()).thenReturn(mContentResolver);
|
when(mContext.getContentResolver()).thenReturn(mContentResolver);
|
||||||
when(mContext.getResources()).thenReturn(mResources);
|
when(mContext.getResources()).thenReturn(mResources);
|
||||||
when(mContext.getSystemService(Context.DEVICE_POLICY_SERVICE))
|
when(mContext.getSystemService(Context.DEVICE_POLICY_SERVICE))
|
||||||
@@ -146,6 +148,74 @@ public class BiometricServiceTest {
|
|||||||
when(mInjector.getConfiguration(any())).thenReturn(config);
|
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
|
@Test
|
||||||
public void testAuthenticate_credentialAllowedButNotSetup_returnsNoDeviceCredential()
|
public void testAuthenticate_credentialAllowedButNotSetup_returnsNoDeviceCredential()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
@@ -311,7 +381,7 @@ public class BiometricServiceTest {
|
|||||||
eq(0 /* vendorCode */));
|
eq(0 /* vendorCode */));
|
||||||
|
|
||||||
// Enrolled, not disabled in settings, user requires confirmation in settings
|
// Enrolled, not disabled in settings, user requires confirmation in settings
|
||||||
resetReceiver();
|
resetReceivers();
|
||||||
when(mBiometricService.mSettingObserver.getFaceEnabledForApps(anyInt())).thenReturn(true);
|
when(mBiometricService.mSettingObserver.getFaceEnabledForApps(anyInt())).thenReturn(true);
|
||||||
when(mBiometricService.mSettingObserver.getFaceAlwaysRequireConfirmation(anyInt()))
|
when(mBiometricService.mSettingObserver.getFaceAlwaysRequireConfirmation(anyInt()))
|
||||||
.thenReturn(true);
|
.thenReturn(true);
|
||||||
@@ -332,7 +402,7 @@ public class BiometricServiceTest {
|
|||||||
anyInt() /* callingUserId */);
|
anyInt() /* callingUserId */);
|
||||||
|
|
||||||
// Enrolled, not disabled in settings, user doesn't require confirmation in settings
|
// Enrolled, not disabled in settings, user doesn't require confirmation in settings
|
||||||
resetReceiver();
|
resetReceivers();
|
||||||
when(mBiometricService.mSettingObserver.getFaceAlwaysRequireConfirmation(anyInt()))
|
when(mBiometricService.mSettingObserver.getFaceAlwaysRequireConfirmation(anyInt()))
|
||||||
.thenReturn(false);
|
.thenReturn(false);
|
||||||
invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
|
invokeAuthenticate(mBiometricService.mImpl, mReceiver1, false /* requireConfirmation */,
|
||||||
@@ -1198,7 +1268,7 @@ public class BiometricServiceTest {
|
|||||||
eq(0) /* vendorCode */);
|
eq(0) /* vendorCode */);
|
||||||
|
|
||||||
// Request for weak auth works
|
// Request for weak auth works
|
||||||
resetReceiver();
|
resetReceivers();
|
||||||
authenticators = Authenticators.BIOMETRIC_WEAK;
|
authenticators = Authenticators.BIOMETRIC_WEAK;
|
||||||
assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
|
assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
|
||||||
invokeCanAuthenticate(mBiometricService, authenticators));
|
invokeCanAuthenticate(mBiometricService, authenticators));
|
||||||
@@ -1217,7 +1287,7 @@ public class BiometricServiceTest {
|
|||||||
anyInt() /* sysUiSessionId */);
|
anyInt() /* sysUiSessionId */);
|
||||||
|
|
||||||
// Requesting strong and credential, when credential is setup
|
// Requesting strong and credential, when credential is setup
|
||||||
resetReceiver();
|
resetReceivers();
|
||||||
authenticators = Authenticators.BIOMETRIC_STRONG | Authenticators.DEVICE_CREDENTIAL;
|
authenticators = Authenticators.BIOMETRIC_STRONG | Authenticators.DEVICE_CREDENTIAL;
|
||||||
when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(true);
|
when(mTrustManager.isDeviceSecure(anyInt())).thenReturn(true);
|
||||||
assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
|
assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
|
||||||
@@ -1244,7 +1314,7 @@ public class BiometricServiceTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resetReceiver();
|
resetReceivers();
|
||||||
authenticators = Authenticators.BIOMETRIC_STRONG;
|
authenticators = Authenticators.BIOMETRIC_STRONG;
|
||||||
assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
|
assertEquals(BiometricManager.BIOMETRIC_SUCCESS,
|
||||||
invokeCanAuthenticate(mBiometricService, authenticators));
|
invokeCanAuthenticate(mBiometricService, authenticators));
|
||||||
@@ -1449,9 +1519,12 @@ public class BiometricServiceTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resetReceiver() {
|
private void resetReceivers() {
|
||||||
mReceiver1 = mock(IBiometricServiceReceiver.class);
|
mReceiver1 = mock(IBiometricServiceReceiver.class);
|
||||||
mReceiver2 = mock(IBiometricServiceReceiver.class);
|
mReceiver2 = mock(IBiometricServiceReceiver.class);
|
||||||
|
|
||||||
|
when(mReceiver1.asBinder()).thenReturn(mock(Binder.class));
|
||||||
|
when(mReceiver2.asBinder()).thenReturn(mock(Binder.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resetStatusBar() {
|
private void resetStatusBar() {
|
||||||
|
|||||||
Reference in New Issue
Block a user