Merge "Update KeyguardUpdateMonitor to use biometricStrength" into udc-dev

This commit is contained in:
Beverly Tai
2023-04-07 20:01:30 +00:00
committed by Android (Google) Code Review
4 changed files with 228 additions and 143 deletions

View File

@@ -96,6 +96,7 @@ import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricSourceType;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.biometrics.SensorProperties;
import android.hardware.biometrics.SensorPropertiesInternal;
import android.hardware.face.FaceAuthenticateOptions;
import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorPropertiesInternal;
@@ -1278,6 +1279,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
if (msgId == FaceManager.FACE_ERROR_LOCKOUT_PERMANENT) {
lockedOutStateChanged = !mFaceLockedOutPermanent;
mFaceLockedOutPermanent = true;
if (isFaceClass3()) {
updateFingerprintListeningState(BIOMETRIC_ACTION_STOP);
}
}
if (isHwUnavailable && cameraPrivacyEnabled) {
@@ -1487,8 +1491,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
// STRONG_AUTH_REQUIRED_AFTER_LOCKOUT which is the same as mFingerprintLockedOutPermanent;
// however the strong auth tracker does not include the temporary lockout
// mFingerprintLockedOut.
// Class 3 biometric lockout will lockout ALL biometrics
return mStrongAuthTracker.isUnlockingWithBiometricAllowed(isStrongBiometric)
&& !mFingerprintLockedOut;
&& (!isFingerprintClass3() || !isFingerprintLockedOut())
&& (!isFaceClass3() || !mFaceLockedOutPermanent);
}
/**
@@ -1506,9 +1512,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
@NonNull BiometricSourceType biometricSourceType) {
switch (biometricSourceType) {
case FINGERPRINT:
return isUnlockingWithBiometricAllowed(true);
return isUnlockingWithBiometricAllowed(isFingerprintClass3());
case FACE:
return isUnlockingWithBiometricAllowed(false);
return isUnlockingWithBiometricAllowed(isFaceClass3());
default:
return false;
}
@@ -2473,7 +2479,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
private void updateFaceEnrolled(int userId) {
Boolean isFaceEnrolled = mFaceManager != null && !mFaceSensorProperties.isEmpty()
final Boolean isFaceEnrolled = isFaceSupported()
&& mBiometricEnabledForUser.get(userId)
&& mAuthController.isFaceAuthEnrolled(userId);
if (mIsFaceEnrolled != isFaceEnrolled) {
@@ -2482,10 +2488,14 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mIsFaceEnrolled = isFaceEnrolled;
}
public boolean isFaceSupported() {
private boolean isFaceSupported() {
return mFaceManager != null && !mFaceSensorProperties.isEmpty();
}
private boolean isFingerprintSupported() {
return mFpm != null && !mFingerprintSensorProperties.isEmpty();
}
/**
* @return true if there's at least one udfps enrolled for the current user.
*/
@@ -2792,10 +2802,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
|| !mLockPatternUtils.isSecure(user);
// Don't trigger active unlock if fp is locked out
final boolean fpLockedOut = mFingerprintLockedOut || mFingerprintLockedOutPermanent;
final boolean fpLockedOut = isFingerprintLockedOut();
// Don't trigger active unlock if primary auth is required
final boolean primaryAuthRequired = !isUnlockingWithBiometricAllowed(true);
final boolean primaryAuthRequired = !isUnlockingWithTrustAgentAllowed();
final boolean shouldTriggerActiveUnlock =
(mAuthInterruptActive || triggerActiveUnlockForAssistant || awakeKeyguard)
@@ -2949,7 +2959,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
// allow face detection to happen even if stronger auth is required. When face is detected,
// we show the bouncer. However, if the user manually locked down the device themselves,
// never attempt to detect face.
final boolean supportsDetect = !mFaceSensorProperties.isEmpty()
final boolean supportsDetect = isFaceSupported()
&& mFaceSensorProperties.get(0).supportsFaceDetection
&& canBypass && !mPrimaryBouncerIsOrWillBeShowing
&& !isUserInLockdown(user);
@@ -3104,7 +3114,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
: WAKE_REASON_UNKNOWN
).toFaceAuthenticateOptions();
// This would need to be updated for multi-sensor devices
final boolean supportsFaceDetection = !mFaceSensorProperties.isEmpty()
final boolean supportsFaceDetection = isFaceSupported()
&& mFaceSensorProperties.get(0).supportsFaceDetection;
if (!isUnlockingWithBiometricAllowed(FACE)) {
final boolean udfpsFingerprintAuthRunning = isUdfpsSupported()
@@ -3166,21 +3176,15 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
* @return {@code true} if possible.
*/
public boolean isUnlockingWithNonStrongBiometricsPossible(int userId) {
// This assumes that there is at most one face and at most one fingerprint sensor
return (mFaceManager != null && !mFaceSensorProperties.isEmpty()
&& (mFaceSensorProperties.get(0).sensorStrength != SensorProperties.STRENGTH_STRONG)
&& isUnlockWithFacePossible(userId))
|| (mFpm != null && !mFingerprintSensorProperties.isEmpty()
&& (mFingerprintSensorProperties.get(0).sensorStrength
!= SensorProperties.STRENGTH_STRONG) && isUnlockWithFingerprintPossible(userId));
return (!isFaceClass3() && isUnlockWithFacePossible(userId))
|| (isFingerprintClass3() && isUnlockWithFingerprintPossible(userId));
}
@SuppressLint("MissingPermission")
@VisibleForTesting
boolean isUnlockWithFingerprintPossible(int userId) {
// TODO (b/242022358), make this rely on onEnrollmentChanged event and update it only once.
boolean newFpEnrolled = mFpm != null
&& !mFingerprintSensorProperties.isEmpty()
boolean newFpEnrolled = isFingerprintSupported()
&& !isFingerprintDisabled(userId) && mFpm.hasEnrolledTemplates(userId);
Boolean oldFpEnrolled = mIsUnlockWithFingerprintPossible.getOrDefault(userId, false);
if (oldFpEnrolled != newFpEnrolled) {
@@ -3330,12 +3334,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
// Immediately stop previous biometric listening states.
// Resetting lockout states updates the biometric listening states.
if (mFaceManager != null && !mFaceSensorProperties.isEmpty()) {
if (isFaceSupported()) {
stopListeningForFace(FACE_AUTH_UPDATED_USER_SWITCHING);
handleFaceLockoutReset(mFaceManager.getLockoutModeForUser(
mFaceSensorProperties.get(0).sensorId, userId));
}
if (mFpm != null && !mFingerprintSensorProperties.isEmpty()) {
if (isFingerprintSupported()) {
stopListeningForFingerprint();
handleFingerprintLockoutReset(mFpm.getLockoutModeForUser(
mFingerprintSensorProperties.get(0).sensorId, userId));
@@ -4071,6 +4075,22 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
return BIOMETRIC_LOCKOUT_RESET_DELAY_MS;
}
@VisibleForTesting
protected boolean isFingerprintClass3() {
// This assumes that there is at most one fingerprint sensor property
return isFingerprintSupported() && isClass3Biometric(mFingerprintSensorProperties.get(0));
}
@VisibleForTesting
protected boolean isFaceClass3() {
// This assumes that there is at most one face sensor property
return isFaceSupported() && isClass3Biometric(mFaceSensorProperties.get(0));
}
private boolean isClass3Biometric(SensorPropertiesInternal sensorProperties) {
return sensorProperties.sensorStrength == SensorProperties.STRENGTH_STRONG;
}
/**
* Unregister all listeners.
*/
@@ -4122,11 +4142,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
for (int subId : mServiceStates.keySet()) {
pw.println(" " + subId + "=" + mServiceStates.get(subId));
}
if (mFpm != null && !mFingerprintSensorProperties.isEmpty()) {
if (isFingerprintSupported()) {
final int userId = mUserTracker.getUserId();
final int strongAuthFlags = mStrongAuthTracker.getStrongAuthForUser(userId);
BiometricAuthenticated fingerprint = mUserFingerprintAuthenticated.get(userId);
pw.println(" Fingerprint state (user=" + userId + ")");
pw.println(" isFingerprintClass3=" + isFingerprintClass3());
pw.println(" areAllFpAuthenticatorsRegistered="
+ mAuthController.areAllFingerprintAuthenticatorsRegistered());
pw.println(" allowed="
@@ -4184,11 +4205,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mFingerprintListenBuffer.toList()
).printTableData(pw);
}
if (mFaceManager != null && !mFaceSensorProperties.isEmpty()) {
if (isFaceSupported()) {
final int userId = mUserTracker.getUserId();
final int strongAuthFlags = mStrongAuthTracker.getStrongAuthForUser(userId);
BiometricAuthenticated face = mUserFaceAuthenticated.get(userId);
pw.println(" Face authentication state (user=" + userId + ")");
pw.println(" isFaceClass3=" + isFaceClass3());
pw.println(" allowed="
+ (face != null && isUnlockingWithBiometricAllowed(face.mIsStrongBiometric)));
pw.println(" auth'd="

View File

@@ -1127,13 +1127,7 @@ public class KeyguardIndicationController {
final boolean faceAuthUnavailable = biometricSourceType == FACE
&& msgId == BIOMETRIC_HELP_FACE_NOT_AVAILABLE;
// TODO(b/141025588): refactor to reduce repetition of code/comments
// Only checking if unlocking with Biometric is allowed (no matter strong or non-strong
// as long as primary auth, i.e. PIN/pattern/password, is not required), so it's ok to
// pass true for isStrongBiometric to isUnlockingWithBiometricAllowed() to bypass the
// check of whether non-strong biometric is allowed
if (!mKeyguardUpdateMonitor
.isUnlockingWithBiometricAllowed(true /* isStrongBiometric */)
if (isPrimaryAuthRequired()
&& !faceAuthUnavailable) {
return;
}
@@ -1234,7 +1228,7 @@ public class KeyguardIndicationController {
private void onFaceAuthError(int msgId, String errString) {
CharSequence deferredFaceMessage = mFaceAcquiredMessageDeferral.getDeferredMessage();
mFaceAcquiredMessageDeferral.reset();
if (shouldSuppressFaceError(msgId, mKeyguardUpdateMonitor)) {
if (shouldSuppressFaceError(msgId)) {
mKeyguardLogger.logBiometricMessage("suppressingFaceError", msgId, errString);
return;
}
@@ -1248,7 +1242,7 @@ public class KeyguardIndicationController {
}
private void onFingerprintAuthError(int msgId, String errString) {
if (shouldSuppressFingerprintError(msgId, mKeyguardUpdateMonitor)) {
if (shouldSuppressFingerprintError(msgId)) {
mKeyguardLogger.logBiometricMessage("suppressingFingerprintError",
msgId,
errString);
@@ -1257,31 +1251,19 @@ public class KeyguardIndicationController {
}
}
private boolean shouldSuppressFingerprintError(int msgId,
KeyguardUpdateMonitor updateMonitor) {
// Only checking if unlocking with Biometric is allowed (no matter strong or non-strong
// as long as primary auth, i.e. PIN/pattern/password, is not required), so it's ok to
// pass true for isStrongBiometric to isUnlockingWithBiometricAllowed() to bypass the
// check of whether non-strong biometric is allowed
return ((!updateMonitor.isUnlockingWithBiometricAllowed(true /* isStrongBiometric */)
&& !isLockoutError(msgId))
private boolean shouldSuppressFingerprintError(int msgId) {
return ((isPrimaryAuthRequired() && !isLockoutError(msgId))
|| msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED
|| msgId == FingerprintManager.FINGERPRINT_ERROR_USER_CANCELED
|| msgId == FingerprintManager.BIOMETRIC_ERROR_POWER_PRESSED);
}
private boolean shouldSuppressFaceError(int msgId, KeyguardUpdateMonitor updateMonitor) {
// Only checking if unlocking with Biometric is allowed (no matter strong or non-strong
// as long as primary auth, i.e. PIN/pattern/password, is not required), so it's ok to
// pass true for isStrongBiometric to isUnlockingWithBiometricAllowed() to bypass the
// check of whether non-strong biometric is allowed
return ((!updateMonitor.isUnlockingWithBiometricAllowed(true /* isStrongBiometric */)
&& msgId != FaceManager.FACE_ERROR_LOCKOUT_PERMANENT)
private boolean shouldSuppressFaceError(int msgId) {
return ((isPrimaryAuthRequired() && msgId != FaceManager.FACE_ERROR_LOCKOUT_PERMANENT)
|| msgId == FaceManager.FACE_ERROR_CANCELED
|| msgId == FaceManager.FACE_ERROR_UNABLE_TO_PROCESS);
}
@Override
public void onTrustChanged(int userId) {
if (!isCurrentUser(userId)) return;
@@ -1355,6 +1337,16 @@ public class KeyguardIndicationController {
}
}
private boolean isPrimaryAuthRequired() {
// Only checking if unlocking with Biometric is allowed (no matter strong or non-strong
// as long as primary auth, i.e. PIN/pattern/password, is required), so it's ok to
// pass true for isStrongBiometric to isUnlockingWithBiometricAllowed() to bypass the
// check of whether non-strong biometric is allowed since strong biometrics can still be
// used.
return !mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
true /* isStrongBiometric */);
}
protected boolean isPluggedInAndCharging() {
return mPowerPluggedIn;
}

View File

@@ -38,14 +38,14 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import dagger.Lazy;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Objects;
import javax.inject.Inject;
import dagger.Lazy;
/**
*/
@SysUISingleton

View File

@@ -20,9 +20,12 @@ import static android.app.StatusBarManager.SESSION_KEYGUARD;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT;
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT_PERMANENT;
import static android.hardware.biometrics.SensorProperties.STRENGTH_CONVENIENCE;
import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG;
import static android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_PRIMARY_BOUNCER_SHOWN;
import static android.hardware.face.FaceAuthenticateOptions.AUTHENTICATE_REASON_STARTED_WAKING_UP;
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_POWER_BUTTON;
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_OPTICAL;
import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE;
import static android.telephony.SubscriptionManager.NAME_SOURCE_CARRIER_ID;
@@ -41,6 +44,8 @@ import static com.android.systemui.statusbar.policy.DevicePostureController.DEVI
import static com.google.common.truth.Truth.assertThat;
import static junit.framework.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -79,7 +84,6 @@ import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricSourceType;
import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
import android.hardware.biometrics.SensorProperties;
import android.hardware.face.FaceAuthenticateOptions;
import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorProperties;
@@ -283,33 +287,13 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Before
public void setup() throws RemoteException {
MockitoAnnotations.initMocks(this);
mFaceSensorProperties =
List.of(createFaceSensorProperties(/* supportsFaceDetection = */ false));
when(mFaceManager.isHardwareDetected()).thenReturn(true);
when(mAuthController.isFaceAuthEnrolled(anyInt())).thenReturn(true);
when(mFaceManager.getSensorPropertiesInternal()).thenReturn(mFaceSensorProperties);
when(mSessionTracker.getSessionId(SESSION_KEYGUARD)).thenReturn(mKeyguardInstanceId);
mFingerprintSensorProperties = List.of(
new FingerprintSensorPropertiesInternal(1 /* sensorId */,
FingerprintSensorProperties.STRENGTH_STRONG,
1 /* maxEnrollmentsPerUser */,
List.of(new ComponentInfoInternal("fingerprintSensor" /* componentId */,
"vendor/model/revision" /* hardwareVersion */,
"1.01" /* firmwareVersion */,
"00000001" /* serialNumber */, "" /* softwareVersion */)),
FingerprintSensorProperties.TYPE_UDFPS_OPTICAL,
false /* resetLockoutRequiresHAT */));
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(
mFingerprintSensorProperties);
when(mUserManager.isUserUnlocked(anyInt())).thenReturn(true);
when(mUserManager.isPrimaryUser()).thenReturn(true);
when(mStrongAuthTracker.getStub()).thenReturn(mock(IStrongAuthTracker.Stub.class));
when(mStrongAuthTracker
.isUnlockingWithBiometricAllowed(anyBoolean() /* isStrongBiometric */))
.isUnlockingWithBiometricAllowed(anyBoolean() /* isClass3Biometric */))
.thenReturn(true);
when(mTelephonyManager.getServiceStateForSubscriber(anyInt()))
.thenReturn(new ServiceState());
@@ -346,20 +330,9 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
anyInt());
mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mContext);
ArgumentCaptor<IFaceAuthenticatorsRegisteredCallback> faceCaptor =
ArgumentCaptor.forClass(IFaceAuthenticatorsRegisteredCallback.class);
verify(mFaceManager).addAuthenticatorsRegisteredCallback(faceCaptor.capture());
mFaceAuthenticatorsRegisteredCallback = faceCaptor.getValue();
mFaceAuthenticatorsRegisteredCallback.onAllAuthenticatorsRegistered(mFaceSensorProperties);
ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback> fingerprintCaptor =
ArgumentCaptor.forClass(IFingerprintAuthenticatorsRegisteredCallback.class);
verify(mFingerprintManager).addAuthenticatorsRegisteredCallback(
fingerprintCaptor.capture());
mFingerprintAuthenticatorsRegisteredCallback = fingerprintCaptor.getValue();
mFingerprintAuthenticatorsRegisteredCallback
.onAllAuthenticatorsRegistered(mFingerprintSensorProperties);
captureAuthenticatorsRegisteredCallbacks();
setupFaceAuth(/* isClass3 */ false);
setupFingerprintAuth(/* isClass3 */ true);
verify(mBiometricManager)
.registerEnabledOnKeyguardCallback(mBiometricEnabledCallbackArgCaptor.capture());
@@ -381,8 +354,64 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
when(mAuthController.areAllFingerprintAuthenticatorsRegistered()).thenReturn(true);
}
private void captureAuthenticatorsRegisteredCallbacks() throws RemoteException {
ArgumentCaptor<IFaceAuthenticatorsRegisteredCallback> faceCaptor =
ArgumentCaptor.forClass(IFaceAuthenticatorsRegisteredCallback.class);
verify(mFaceManager).addAuthenticatorsRegisteredCallback(faceCaptor.capture());
mFaceAuthenticatorsRegisteredCallback = faceCaptor.getValue();
mFaceAuthenticatorsRegisteredCallback.onAllAuthenticatorsRegistered(mFaceSensorProperties);
ArgumentCaptor<IFingerprintAuthenticatorsRegisteredCallback> fingerprintCaptor =
ArgumentCaptor.forClass(IFingerprintAuthenticatorsRegisteredCallback.class);
verify(mFingerprintManager).addAuthenticatorsRegisteredCallback(
fingerprintCaptor.capture());
mFingerprintAuthenticatorsRegisteredCallback = fingerprintCaptor.getValue();
mFingerprintAuthenticatorsRegisteredCallback
.onAllAuthenticatorsRegistered(mFingerprintSensorProperties);
}
private void setupFaceAuth(boolean isClass3) throws RemoteException {
when(mFaceManager.isHardwareDetected()).thenReturn(true);
when(mAuthController.isFaceAuthEnrolled(anyInt())).thenReturn(true);
mFaceSensorProperties =
List.of(createFaceSensorProperties(/* supportsFaceDetection = */ false, isClass3));
when(mFaceManager.getSensorPropertiesInternal()).thenReturn(mFaceSensorProperties);
mFaceAuthenticatorsRegisteredCallback.onAllAuthenticatorsRegistered(mFaceSensorProperties);
assertEquals(isClass3, mKeyguardUpdateMonitor.isFaceClass3());
}
private void setupFingerprintAuth(boolean isClass3) throws RemoteException {
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
mFingerprintSensorProperties = List.of(
createFingerprintSensorPropertiesInternal(TYPE_UDFPS_OPTICAL, isClass3));
when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(
mFingerprintSensorProperties);
mFingerprintAuthenticatorsRegisteredCallback.onAllAuthenticatorsRegistered(
mFingerprintSensorProperties);
assertEquals(isClass3, mKeyguardUpdateMonitor.isFingerprintClass3());
}
private FingerprintSensorPropertiesInternal createFingerprintSensorPropertiesInternal(
@FingerprintSensorProperties.SensorType int sensorType,
boolean isClass3) {
final List<ComponentInfoInternal> componentInfo =
List.of(new ComponentInfoInternal("fingerprintSensor" /* componentId */,
"vendor/model/revision" /* hardwareVersion */,
"1.01" /* firmwareVersion */,
"00000001" /* serialNumber */, "" /* softwareVersion */));
return new FingerprintSensorPropertiesInternal(
FINGERPRINT_SENSOR_ID,
isClass3 ? STRENGTH_STRONG : STRENGTH_CONVENIENCE,
1 /* maxEnrollmentsPerUser */,
componentInfo,
sensorType,
true /* resetLockoutRequiresHardwareAuthToken */);
}
@NonNull
private FaceSensorPropertiesInternal createFaceSensorProperties(boolean supportsFaceDetection) {
private FaceSensorPropertiesInternal createFaceSensorProperties(
boolean supportsFaceDetection, boolean isClass3) {
final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
componentInfo.add(new ComponentInfoInternal("faceSensor" /* componentId */,
"vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */,
@@ -391,10 +420,9 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
"" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */,
"vendor/version/revision" /* softwareVersion */));
return new FaceSensorPropertiesInternal(
0 /* id */,
FaceSensorProperties.STRENGTH_STRONG,
FACE_SENSOR_ID /* id */,
isClass3 ? STRENGTH_STRONG : STRENGTH_CONVENIENCE,
1 /* maxTemplatesAllowed */,
componentInfo,
FaceSensorProperties.TYPE_UNKNOWN,
@@ -686,7 +714,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testUnlockingWithFaceAllowed_strongAuthTrackerUnlockingWithBiometricAllowed() {
// GIVEN unlocking with biometric is allowed
strongAuthNotRequired();
primaryAuthNotRequiredByStrongAuthTracker();
// THEN unlocking with face and fp is allowed
Assert.assertTrue(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
@@ -706,18 +734,69 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
}
@Test
public void testUnlockingWithFaceAllowed_fingerprintLockout() {
// GIVEN unlocking with biometric is allowed
strongAuthNotRequired();
public void class3FingerprintLockOut_lockOutClass1Face() throws RemoteException {
setupFaceAuth(/* isClass3 */ false);
setupFingerprintAuth(/* isClass3 */ true);
// WHEN fingerprint is locked out
fingerprintErrorTemporaryLockedOut();
// GIVEN primary auth is not required by StrongAuthTracker
primaryAuthNotRequiredByStrongAuthTracker();
// WHEN fingerprint (class 3) is lock out
fingerprintErrorTemporaryLockOut();
// THEN unlocking with face is not allowed
Assert.assertFalse(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
BiometricSourceType.FACE));
}
@Test
public void class3FingerprintLockOut_lockOutClass3Face() throws RemoteException {
setupFaceAuth(/* isClass3 */ true);
setupFingerprintAuth(/* isClass3 */ true);
// GIVEN primary auth is not required by StrongAuthTracker
primaryAuthNotRequiredByStrongAuthTracker();
// WHEN fingerprint (class 3) is lock out
fingerprintErrorTemporaryLockOut();
// THEN unlocking with face is not allowed
Assert.assertFalse(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
BiometricSourceType.FACE));
}
@Test
public void class3FaceLockOut_lockOutClass3Fingerprint() throws RemoteException {
setupFaceAuth(/* isClass3 */ true);
setupFingerprintAuth(/* isClass3 */ true);
// GIVEN primary auth is not required by StrongAuthTracker
primaryAuthNotRequiredByStrongAuthTracker();
// WHEN face (class 3) is lock out
faceAuthLockOut();
// THEN unlocking with fingerprint is not allowed
Assert.assertFalse(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
BiometricSourceType.FINGERPRINT));
}
@Test
public void class1FaceLockOut_doesNotLockOutClass3Fingerprint() throws RemoteException {
setupFaceAuth(/* isClass3 */ false);
setupFingerprintAuth(/* isClass3 */ true);
// GIVEN primary auth is not required by StrongAuthTracker
primaryAuthNotRequiredByStrongAuthTracker();
// WHEN face (class 1) is lock out
faceAuthLockOut();
// THEN unlocking with fingerprint is still allowed
Assert.assertTrue(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
BiometricSourceType.FINGERPRINT));
}
@Test
public void testUnlockingWithFpAllowed_strongAuthTrackerUnlockingWithBiometricNotAllowed() {
// GIVEN unlocking with biometric is not allowed
@@ -731,10 +810,10 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testUnlockingWithFpAllowed_fingerprintLockout() {
// GIVEN unlocking with biometric is allowed
strongAuthNotRequired();
primaryAuthNotRequiredByStrongAuthTracker();
// WHEN fingerprint is locked out
fingerprintErrorTemporaryLockedOut();
// WHEN fingerprint is lock out
fingerprintErrorTemporaryLockOut();
// THEN unlocking with fingerprint is not allowed
Assert.assertFalse(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
@@ -757,8 +836,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mKeyguardUpdateMonitor.onTrustChanged(true, true, getCurrentUser(), 0, null);
Assert.assertTrue(mKeyguardUpdateMonitor.getUserHasTrust(getCurrentUser()));
// WHEN fingerprint is locked out
fingerprintErrorTemporaryLockedOut();
// WHEN fingerprint is lock out
fingerprintErrorTemporaryLockOut();
// THEN user is NOT considered as "having trust" and bouncer cannot be skipped
Assert.assertFalse(mKeyguardUpdateMonitor.getUserHasTrust(getCurrentUser()));
@@ -826,7 +905,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
// GIVEN udfps is supported and strong auth required for weak biometrics (face) only
givenUdfpsSupported();
strongAuthRequiredForWeakBiometricOnly(); // this allows fingerprint to run but not face
primaryAuthRequiredForWeakBiometricOnly(); // allows class3 fp to run but not class1 face
// WHEN the device wakes up
mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
@@ -863,7 +942,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
public void noFaceDetect_whenStrongAuthRequiredAndBypass_faceDetectionUnsupported() {
// GIVEN bypass is enabled, face detection is NOT supported and strong auth is required
lockscreenBypassIsAllowed();
strongAuthRequiredEncrypted();
primaryAuthRequiredEncrypted();
keyguardIsVisible();
// WHEN the device wakes up
@@ -1011,10 +1090,10 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testOnFaceAuthenticated_skipsFaceWhenAuthenticated() {
// test whether face will be skipped if authenticated, so the value of isStrongBiometric
// test whether face will be skipped if authenticated, so the value of isClass3Biometric
// doesn't matter here
mKeyguardUpdateMonitor.onFaceAuthenticated(KeyguardUpdateMonitor.getCurrentUser(),
true /* isStrongBiometric */);
true /* isClass3Biometric */);
setKeyguardBouncerVisibility(true);
mTestableLooper.processAllMessages();
@@ -1027,7 +1106,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mTestableLooper.processAllMessages();
keyguardIsVisible();
faceAuthLockedOut();
faceAuthLockOut();
verify(mLockPatternUtils, never()).requireStrongAuth(anyInt(), anyInt());
}
@@ -1050,7 +1129,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mTestableLooper.processAllMessages();
keyguardIsVisible();
faceAuthLockedOut();
faceAuthLockOut();
mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback
.onAuthenticationError(FINGERPRINT_ERROR_LOCKOUT_PERMANENT, "");
@@ -1060,32 +1139,32 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testGetUserCanSkipBouncer_whenFace() {
int user = KeyguardUpdateMonitor.getCurrentUser();
mKeyguardUpdateMonitor.onFaceAuthenticated(user, true /* isStrongBiometric */);
mKeyguardUpdateMonitor.onFaceAuthenticated(user, true /* isClass3Biometric */);
assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isTrue();
}
@Test
public void testGetUserCanSkipBouncer_whenFace_nonStrongAndDisallowed() {
when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(false /* isStrongBiometric */))
when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(false /* isClass3Biometric */))
.thenReturn(false);
int user = KeyguardUpdateMonitor.getCurrentUser();
mKeyguardUpdateMonitor.onFaceAuthenticated(user, false /* isStrongBiometric */);
mKeyguardUpdateMonitor.onFaceAuthenticated(user, false /* isClass3Biometric */);
assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isFalse();
}
@Test
public void testGetUserCanSkipBouncer_whenFingerprint() {
int user = KeyguardUpdateMonitor.getCurrentUser();
mKeyguardUpdateMonitor.onFingerprintAuthenticated(user, true /* isStrongBiometric */);
mKeyguardUpdateMonitor.onFingerprintAuthenticated(user, true /* isClass3Biometric */);
assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isTrue();
}
@Test
public void testGetUserCanSkipBouncer_whenFingerprint_nonStrongAndDisallowed() {
when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(false /* isStrongBiometric */))
when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(false /* isClass3Biometric */))
.thenReturn(false);
int user = KeyguardUpdateMonitor.getCurrentUser();
mKeyguardUpdateMonitor.onFingerprintAuthenticated(user, false /* isStrongBiometric */);
mKeyguardUpdateMonitor.onFingerprintAuthenticated(user, false /* isClass3Biometric */);
assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isFalse();
}
@@ -1126,9 +1205,9 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@BiometricConstants.LockoutMode int fingerprintLockoutMode,
@BiometricConstants.LockoutMode int faceLockoutMode) {
final int newUser = 12;
final boolean faceLocked =
final boolean faceLockOut =
faceLockoutMode != BiometricConstants.BIOMETRIC_LOCKOUT_NONE;
final boolean fpLocked =
final boolean fpLockOut =
fingerprintLockoutMode != BiometricConstants.BIOMETRIC_LOCKOUT_NONE;
mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
@@ -1161,8 +1240,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
eq(false), eq(BiometricSourceType.FINGERPRINT));
// THEN locked out states are updated
assertThat(mKeyguardUpdateMonitor.isFingerprintLockedOut()).isEqualTo(fpLocked);
assertThat(mKeyguardUpdateMonitor.isFaceLockedOut()).isEqualTo(faceLocked);
assertThat(mKeyguardUpdateMonitor.isFingerprintLockedOut()).isEqualTo(fpLockOut);
assertThat(mKeyguardUpdateMonitor.isFaceLockedOut()).isEqualTo(faceLockOut);
// Fingerprint should be cancelled on lockout if going to lockout state, else
// restarted if it's not
@@ -1443,7 +1522,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
throws RemoteException {
// GIVEN SFPS supported and enrolled
final ArrayList<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
props.add(newFingerprintSensorPropertiesInternal(TYPE_POWER_BUTTON));
props.add(createFingerprintSensorPropertiesInternal(TYPE_POWER_BUTTON,
/* isClass3 */ true));
when(mAuthController.getSfpsProps()).thenReturn(props);
when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true);
@@ -1466,17 +1546,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(false)).isTrue();
}
private FingerprintSensorPropertiesInternal newFingerprintSensorPropertiesInternal(
@FingerprintSensorProperties.SensorType int sensorType) {
return new FingerprintSensorPropertiesInternal(
0 /* sensorId */,
SensorProperties.STRENGTH_STRONG,
1 /* maxEnrollmentsPerUser */,
new ArrayList<ComponentInfoInternal>(),
sensorType,
true /* resetLockoutRequiresHardwareAuthToken */);
}
@Test
public void testShouldNotListenForUdfps_whenTrustEnabled() {
// GIVEN a "we should listen for udfps" state
@@ -1613,7 +1682,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
keyguardNotGoingAway();
occludingAppRequestsFaceAuth();
currentUserIsPrimary();
strongAuthNotRequired();
primaryAuthNotRequiredByStrongAuthTracker();
biometricsEnabledForCurrentUser();
currentUserDoesNotHaveTrust();
biometricsNotDisabledThroughDevicePolicyManager();
@@ -1622,7 +1691,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
// Fingerprint is locked out.
fingerprintErrorTemporaryLockedOut();
fingerprintErrorTemporaryLockOut();
assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
}
@@ -1634,7 +1703,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
bouncerFullyVisibleAndNotGoingToSleep();
keyguardNotGoingAway();
currentUserIsPrimary();
strongAuthNotRequired();
primaryAuthNotRequiredByStrongAuthTracker();
biometricsEnabledForCurrentUser();
currentUserDoesNotHaveTrust();
biometricsNotDisabledThroughDevicePolicyManager();
@@ -1657,7 +1726,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
bouncerFullyVisibleAndNotGoingToSleep();
keyguardNotGoingAway();
currentUserIsPrimary();
strongAuthNotRequired();
primaryAuthNotRequiredByStrongAuthTracker();
biometricsEnabledForCurrentUser();
currentUserDoesNotHaveTrust();
biometricsNotDisabledThroughDevicePolicyManager();
@@ -1684,7 +1753,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
// Face auth should run when the following is true.
keyguardNotGoingAway();
bouncerFullyVisibleAndNotGoingToSleep();
strongAuthNotRequired();
primaryAuthNotRequiredByStrongAuthTracker();
biometricsEnabledForCurrentUser();
currentUserDoesNotHaveTrust();
biometricsNotDisabledThroughDevicePolicyManager();
@@ -1940,7 +2009,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
// Face is locked out.
faceAuthLockedOut();
faceAuthLockOut();
mTestableLooper.processAllMessages();
// This is needed beccause we want to show face locked out error message whenever face auth
@@ -2578,7 +2647,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
verify(mFingerprintManager).addLockoutResetCallback(fpLockoutResetCallbackCaptor.capture());
// GIVEN device is locked out
fingerprintErrorTemporaryLockedOut();
fingerprintErrorTemporaryLockOut();
// GIVEN FP detection is running
givenDetectFingerprintWithClearingFingerprintManagerInvocations();
@@ -2705,8 +2774,10 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
}
private void supportsFaceDetection() throws RemoteException {
final boolean isClass3 = !mFaceSensorProperties.isEmpty()
&& mFaceSensorProperties.get(0).sensorStrength == STRENGTH_STRONG;
mFaceSensorProperties =
List.of(createFaceSensorProperties(/* supportsFaceDetection = */ true));
List.of(createFaceSensorProperties(/* supportsFaceDetection = */ true, isClass3));
mFaceAuthenticatorsRegisteredCallback.onAllAuthenticatorsRegistered(mFaceSensorProperties);
}
@@ -2734,7 +2805,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
}
}
private void faceAuthLockedOut() {
private void faceAuthLockOut() {
mKeyguardUpdateMonitor.mFaceAuthenticationCallback
.onAuthenticationError(FaceManager.FACE_ERROR_LOCKOUT_PERMANENT, "");
}
@@ -2767,7 +2838,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mKeyguardUpdateMonitor.setSwitchingUser(true);
}
private void fingerprintErrorTemporaryLockedOut() {
private void fingerprintErrorTemporaryLockOut() {
mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback
.onAuthenticationError(FINGERPRINT_ERROR_LOCKOUT, "Fingerprint locked out");
}
@@ -2821,18 +2892,18 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
);
}
private void strongAuthRequiredEncrypted() {
private void primaryAuthRequiredEncrypted() {
when(mStrongAuthTracker.getStrongAuthForUser(KeyguardUpdateMonitor.getCurrentUser()))
.thenReturn(STRONG_AUTH_REQUIRED_AFTER_BOOT);
when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
}
private void strongAuthRequiredForWeakBiometricOnly() {
private void primaryAuthRequiredForWeakBiometricOnly() {
when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(eq(true))).thenReturn(true);
when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(eq(false))).thenReturn(false);
}
private void strongAuthNotRequired() {
private void primaryAuthNotRequiredByStrongAuthTracker() {
when(mStrongAuthTracker.getStrongAuthForUser(KeyguardUpdateMonitor.getCurrentUser()))
.thenReturn(0);
when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
@@ -2917,10 +2988,10 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
}
private void givenDetectFace() throws RemoteException {
// GIVEN bypass is enabled, face detection is supported and strong auth is required
// GIVEN bypass is enabled, face detection is supported and primary auth is required
lockscreenBypassIsAllowed();
supportsFaceDetection();
strongAuthRequiredEncrypted();
primaryAuthRequiredEncrypted();
keyguardIsVisible();
// fingerprint is NOT running, UDFPS is NOT supported