On strongAuthChanges, update fp & face states

Previously, we relied on other state to update which
would trigger updateBiometricListeningState. Instead
of relying on other triggers, directly update
the biometric listening state if strong auth
or non-strong auth allowed states change.

Don't run face auth or detect if device is locked down by the
user. However, if bypass is enabled and the other strongauth
requirements are triggered (ie: idle timeout, timeout, encrypted,
dpm lockdown, after boot), then run detectFace if its supported.

Fixes: 259908246
Fixes: 258513069
Fixes: 260682144
Test: atest KeyguardUpdateMonitorTest
Change-Id: If03a1525832dfb8366c36757ebc84c0dead51b9f
Merged-In: If03a1525832dfb8366c36757ebc84c0dead51b9f
This commit is contained in:
Beverly
2022-11-30 16:24:10 +00:00
committed by Beverly Tai
parent ae41b142e8
commit 8c42220b10
5 changed files with 274 additions and 142 deletions

View File

@@ -48,11 +48,13 @@ import com.android.keyguard.InternalFaceAuthReasons.KEYGUARD_INIT
import com.android.keyguard.InternalFaceAuthReasons.KEYGUARD_OCCLUSION_CHANGED
import com.android.keyguard.InternalFaceAuthReasons.KEYGUARD_RESET
import com.android.keyguard.InternalFaceAuthReasons.KEYGUARD_VISIBILITY_CHANGED
import com.android.keyguard.InternalFaceAuthReasons.NON_STRONG_BIOMETRIC_ALLOWED_CHANGED
import com.android.keyguard.InternalFaceAuthReasons.OCCLUDING_APP_REQUESTED
import com.android.keyguard.InternalFaceAuthReasons.PRIMARY_BOUNCER_SHOWN
import com.android.keyguard.InternalFaceAuthReasons.PRIMARY_BOUNCER_SHOWN_OR_WILL_BE_SHOWN
import com.android.keyguard.InternalFaceAuthReasons.RETRY_AFTER_HW_UNAVAILABLE
import com.android.keyguard.InternalFaceAuthReasons.STARTED_WAKING_UP
import com.android.keyguard.InternalFaceAuthReasons.STRONG_AUTH_ALLOWED_CHANGED
import com.android.keyguard.InternalFaceAuthReasons.TRUST_DISABLED
import com.android.keyguard.InternalFaceAuthReasons.TRUST_ENABLED
import com.android.keyguard.InternalFaceAuthReasons.USER_SWITCHING
@@ -121,6 +123,9 @@ private object InternalFaceAuthReasons {
const val FACE_AUTHENTICATED = "Face auth started/stopped because face is authenticated"
const val BIOMETRIC_ENABLED =
"Face auth started/stopped because biometric is enabled on keyguard"
const val STRONG_AUTH_ALLOWED_CHANGED = "Face auth stopped because strong auth allowed changed"
const val NON_STRONG_BIOMETRIC_ALLOWED_CHANGED =
"Face auth stopped because non strong biometric allowed changed"
}
/**
@@ -204,7 +209,11 @@ constructor(private val id: Int, val reason: String, var extraInfo: Int = 0) :
@UiEvent(doc = FACE_AUTHENTICATED)
FACE_AUTH_UPDATED_ON_FACE_AUTHENTICATED(1187, FACE_AUTHENTICATED),
@UiEvent(doc = BIOMETRIC_ENABLED)
FACE_AUTH_UPDATED_BIOMETRIC_ENABLED_ON_KEYGUARD(1188, BIOMETRIC_ENABLED);
FACE_AUTH_UPDATED_BIOMETRIC_ENABLED_ON_KEYGUARD(1188, BIOMETRIC_ENABLED),
@UiEvent(doc = STRONG_AUTH_ALLOWED_CHANGED)
FACE_AUTH_UPDATED_STRONG_AUTH_CHANGED(1255, STRONG_AUTH_ALLOWED_CHANGED),
@UiEvent(doc = NON_STRONG_BIOMETRIC_ALLOWED_CHANGED)
FACE_AUTH_NON_STRONG_BIOMETRIC_ALLOWED_CHANGED(1256, NON_STRONG_BIOMETRIC_ALLOWED_CHANGED);
override fun getId(): Int = this.id

View File

@@ -53,17 +53,17 @@ data class KeyguardFaceListenModel(
val biometricSettingEnabledForUser: Boolean,
val bouncerFullyShown: Boolean,
val faceAndFpNotAuthenticated: Boolean,
val faceAuthAllowed: Boolean,
val faceDisabled: Boolean,
val faceLockedOut: Boolean,
val fpLockedOut: Boolean,
val goingToSleep: Boolean,
val keyguardAwake: Boolean,
val keyguardGoingAway: Boolean,
val listeningForFaceAssistant: Boolean,
val occludingAppRequestingFaceAuth: Boolean,
val primaryUser: Boolean,
val scanningAllowedByStrongAuth: Boolean,
val secureCameraLaunched: Boolean,
val supportsDetect: Boolean,
val switchingUser: Boolean,
val udfpsBouncerShowing: Boolean,
val udfpsFingerDown: Boolean,
@@ -79,9 +79,8 @@ data class KeyguardActiveUnlockModel(
// keep sorted
val awakeKeyguard: Boolean,
val authInterruptActive: Boolean,
val encryptedOrTimedOut: Boolean,
val fpLockout: Boolean,
val lockDown: Boolean,
val fpLockedOut: Boolean,
val primaryAuthRequired: Boolean,
val switchingUser: Boolean,
val triggerActiveUnlockForAssistant: Boolean,
val userCanDismissLockScreen: Boolean

View File

@@ -34,9 +34,9 @@ import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
import static com.android.keyguard.FaceAuthReasonKt.apiRequestReasonToUiEvent;
import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_NON_STRONG_BIOMETRIC_ALLOWED_CHANGED;
import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_STOPPED_DREAM_STARTED;
import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_STOPPED_FACE_CANCEL_NOT_RECEIVED;
import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_STOPPED_FINISHED_GOING_TO_SLEEP;
@@ -65,6 +65,7 @@ import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_ON_FACE_AUT
import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_ON_KEYGUARD_INIT;
import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_PRIMARY_BOUNCER_SHOWN;
import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_STARTED_WAKING_UP;
import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_STRONG_AUTH_CHANGED;
import static com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_UPDATED_USER_SWITCHING;
import static com.android.systemui.DejankUtils.whitelistIpcs;
@@ -171,7 +172,6 @@ import java.util.Map.Entry;
import java.util.Set;
import java.util.TimeZone;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import javax.inject.Inject;
@@ -1417,6 +1417,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
}
/**
* Whether the user locked down the device. This doesn't include device policy manager lockdown.
*/
public boolean isUserInLockdown(int userId) {
return containsFlag(mStrongAuthTracker.getStrongAuthForUser(userId),
STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
@@ -1448,7 +1451,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
return mStrongAuthTracker;
}
private void notifyStrongAuthStateChanged(int userId) {
@VisibleForTesting
void notifyStrongAuthAllowedChanged(int userId) {
Assert.isMainThread();
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -1456,6 +1460,15 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
cb.onStrongAuthStateChanged(userId);
}
}
if (userId == getCurrentUser()) {
FACE_AUTH_UPDATED_STRONG_AUTH_CHANGED.setExtraInfo(
mStrongAuthTracker.getStrongAuthForUser(getCurrentUser()));
// Strong auth is only reset when primary auth is used to enter the device,
// so we only check whether to stop biometric listening states here
updateBiometricListeningState(
BIOMETRIC_ACTION_STOP, FACE_AUTH_UPDATED_STRONG_AUTH_CHANGED);
}
}
private void notifyLockedOutStateChanged(BiometricSourceType type) {
@@ -1467,8 +1480,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
}
}
private void notifyNonStrongBiometricStateChanged(int userId) {
@VisibleForTesting
void notifyNonStrongBiometricAllowedChanged(int userId) {
Assert.isMainThread();
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -1476,6 +1489,16 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
cb.onNonStrongBiometricAllowedChanged(userId);
}
}
if (userId == getCurrentUser()) {
FACE_AUTH_NON_STRONG_BIOMETRIC_ALLOWED_CHANGED.setExtraInfo(
mStrongAuthTracker.isNonStrongBiometricAllowedAfterIdleTimeout(
getCurrentUser()) ? -1 : 1);
// This is only reset when primary auth is used to enter the device, so we only check
// whether to stop biometric listening states here
updateBiometricListeningState(BIOMETRIC_ACTION_STOP,
FACE_AUTH_NON_STRONG_BIOMETRIC_ALLOWED_CHANGED);
}
}
private void dispatchErrorMessage(CharSequence message) {
@@ -1821,16 +1844,12 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
}
public static class StrongAuthTracker extends LockPatternUtils.StrongAuthTracker {
private final Consumer<Integer> mStrongAuthRequiredChangedCallback;
private final Consumer<Integer> mNonStrongBiometricAllowedChanged;
public StrongAuthTracker(Context context,
Consumer<Integer> strongAuthRequiredChangedCallback,
Consumer<Integer> nonStrongBiometricAllowedChanged) {
/**
* Updates callbacks when strong auth requirements change.
*/
public class StrongAuthTracker extends LockPatternUtils.StrongAuthTracker {
public StrongAuthTracker(Context context) {
super(context);
mStrongAuthRequiredChangedCallback = strongAuthRequiredChangedCallback;
mNonStrongBiometricAllowedChanged = nonStrongBiometricAllowedChanged;
}
public boolean isUnlockingWithBiometricAllowed(boolean isStrongBiometric) {
@@ -1846,7 +1865,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
@Override
public void onStrongAuthRequiredChanged(int userId) {
mStrongAuthRequiredChangedCallback.accept(userId);
notifyStrongAuthAllowedChanged(userId);
}
// TODO(b/247091681): Renaming the inappropriate onIsNonStrongBiometricAllowedChanged
@@ -1854,7 +1873,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
// Strong-Auth
@Override
public void onIsNonStrongBiometricAllowedChanged(int userId) {
mNonStrongBiometricAllowedChanged.accept(userId);
notifyNonStrongBiometricAllowedChanged(userId);
}
}
@@ -2015,8 +2034,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mUserTracker = userTracker;
mTelephonyListenerManager = telephonyListenerManager;
mDeviceProvisioned = isDeviceProvisionedInSettingsDb();
mStrongAuthTracker = new StrongAuthTracker(context, this::notifyStrongAuthStateChanged,
this::notifyNonStrongBiometricStateChanged);
mStrongAuthTracker = new StrongAuthTracker(context);
mBackgroundExecutor = backgroundExecutor;
mBroadcastDispatcher = broadcastDispatcher;
mInteractionJankMonitor = interactionJankMonitor;
@@ -2590,24 +2608,17 @@ 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 = mFingerprintLockedOut || mFingerprintLockedOutPermanent;
// Don't trigger active unlock if primary auth is required
final int strongAuth = mStrongAuthTracker.getStrongAuthForUser(user);
final boolean isLockDown =
containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW)
|| containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
final boolean isEncryptedOrTimedOut =
containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_BOOT)
|| containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_TIMEOUT);
final boolean primaryAuthRequired = !isUnlockingWithBiometricAllowed(true);
final boolean shouldTriggerActiveUnlock =
(mAuthInterruptActive || triggerActiveUnlockForAssistant || awakeKeyguard)
&& !mSwitchingUser
&& !userCanDismissLockScreen
&& !fpLockedout
&& !isLockDown
&& !isEncryptedOrTimedOut
&& !fpLockedOut
&& !primaryAuthRequired
&& !mKeyguardGoingAway
&& !mSecureCameraLaunched;
@@ -2619,9 +2630,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
shouldTriggerActiveUnlock,
awakeKeyguard,
mAuthInterruptActive,
isEncryptedOrTimedOut,
fpLockedout,
isLockDown,
fpLockedOut,
primaryAuthRequired,
mSwitchingUser,
triggerActiveUnlockForAssistant,
userCanDismissLockScreen));
@@ -2673,7 +2683,8 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
&& !fingerprintDisabledForUser
&& (!mKeyguardGoingAway || !mDeviceInteractive)
&& mIsPrimaryUser
&& biometricEnabledForUser;
&& biometricEnabledForUser
&& !isUserInLockdown(user);
final boolean strongerAuthRequired = !isUnlockingWithFingerprintAllowed();
final boolean isSideFps = isSfpsSupported() && isSfpsEnrolled();
final boolean shouldListenBouncerState =
@@ -2735,14 +2746,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
final boolean awakeKeyguard = isKeyguardVisible() && mDeviceInteractive
&& !statusBarShadeLocked;
final int user = getCurrentUser();
final int strongAuth = mStrongAuthTracker.getStrongAuthForUser(user);
final boolean isLockDown =
containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW)
|| containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
final boolean isEncryptedOrTimedOut =
containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_BOOT)
|| containsFlag(strongAuth, STRONG_AUTH_REQUIRED_AFTER_TIMEOUT);
final boolean fpLockedOut = isFingerprintLockedOut();
final boolean faceAuthAllowed = isUnlockingWithBiometricAllowed(FACE);
final boolean canBypass = mKeyguardBypassController != null
&& mKeyguardBypassController.canBypass();
// There's no reason to ask the HAL for authentication when the user can dismiss the
@@ -2750,20 +2754,15 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
// the lock screen even when TrustAgents are keeping the device unlocked.
final boolean userNotTrustedOrDetectionIsNeeded = !getUserHasTrust(user) || canBypass;
// Scan even when encrypted or timeout to show a preemptive bouncer when bypassing.
// Lock-down mode shouldn't scan, since it is more explicit.
boolean strongAuthAllowsScanning = (!isEncryptedOrTimedOut || canBypass
&& !mPrimaryBouncerFullyShown);
// If the device supports face detection (without authentication) and bypass is enabled,
// allow face scanning to happen if the device is in lockdown mode.
// Otherwise, prevent scanning.
final boolean supportsDetectOnly = !mFaceSensorProperties.isEmpty()
&& canBypass
&& mFaceSensorProperties.get(0).supportsFaceDetection;
if (isLockDown && !supportsDetectOnly) {
strongAuthAllowsScanning = false;
}
// If the device supports face detection (without authentication), if bypass is enabled,
// 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()
&& mFaceSensorProperties.get(0).supportsFaceDetection
&& canBypass && !mPrimaryBouncerIsOrWillBeShowing
&& !isUserInLockdown(user);
final boolean faceAuthAllowedOrDetectionIsNeeded = faceAuthAllowed || supportsDetect;
// If the face or fp has recently been authenticated do not attempt to authenticate again.
final boolean faceAndFpNotAuthenticated = !getUserUnlockedWithBiometric(user);
@@ -2784,14 +2783,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
|| mUdfpsBouncerShowing)
&& !mSwitchingUser && !faceDisabledForUser && userNotTrustedOrDetectionIsNeeded
&& !mKeyguardGoingAway && biometricEnabledForUser
&& strongAuthAllowsScanning && mIsPrimaryUser
&& faceAuthAllowedOrDetectionIsNeeded && mIsPrimaryUser
&& (!mSecureCameraLaunched || mOccludingAppRequestingFace)
&& faceAndFpNotAuthenticated
&& !mGoingToSleep
// We only care about fp locked out state and not face because we still trigger
// face auth even when face is locked out to show the user a message that face
// unlock was supposed to run but didn't
&& !fpLockedOut;
&& !mGoingToSleep;
// Aggregate relevant fields for debug logging.
maybeLogListenerModelData(
@@ -2801,19 +2796,19 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
shouldListen,
mAuthInterruptActive,
biometricEnabledForUser,
mPrimaryBouncerFullyShown,
mPrimaryBouncerFullyShown,
faceAndFpNotAuthenticated,
faceAuthAllowed,
faceDisabledForUser,
isFaceLockedOut(),
fpLockedOut,
mGoingToSleep,
awakeKeyguard,
mKeyguardGoingAway,
shouldListenForFaceAssistant,
mOccludingAppRequestingFace,
mIsPrimaryUser,
strongAuthAllowsScanning,
mSecureCameraLaunched,
supportsDetect,
mSwitchingUser,
mUdfpsBouncerShowing,
isUdfpsFingerDown,
@@ -2917,9 +2912,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
// This would need to be updated for multi-sensor devices
final boolean supportsFaceDetection = !mFaceSensorProperties.isEmpty()
&& mFaceSensorProperties.get(0).supportsFaceDetection;
if (isEncryptedOrLockdown(userId) && supportsFaceDetection) {
if (!isUnlockingWithBiometricAllowed(FACE) && supportsFaceDetection) {
mLogger.v("startListeningForFace - detect");
mFaceManager.detectFace(mFaceCancelSignal, mFaceDetectionCallback, userId);
} else {
mLogger.v("startListeningForFace - authenticate");
final boolean isBypassEnabled = mKeyguardBypassController != null
&& mKeyguardBypassController.isBypassEnabled();
mFaceManager.authenticate(null /* crypto */, mFaceCancelSignal,

View File

@@ -87,17 +87,17 @@ private fun faceModel(user: Int) = KeyguardFaceListenModel(
biometricSettingEnabledForUser = false,
bouncerFullyShown = false,
faceAndFpNotAuthenticated = false,
faceAuthAllowed = true,
faceDisabled = false,
faceLockedOut = false,
fpLockedOut = false,
goingToSleep = false,
keyguardAwake = false,
keyguardGoingAway = false,
listeningForFaceAssistant = false,
occludingAppRequestingFaceAuth = false,
primaryUser = false,
scanningAllowedByStrongAuth = false,
secureCameraLaunched = false,
supportsDetect = true,
switchingUser = false,
udfpsBouncerShowing = false,
udfpsFingerDown = false,

View File

@@ -26,6 +26,7 @@ import static android.telephony.SubscriptionManager.NAME_SOURCE_CARRIER_ID;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN;
import static com.android.keyguard.FaceAuthApiRequestReason.NOTIFICATION_PANEL_CLICKED;
import static com.android.keyguard.KeyguardUpdateMonitor.BIOMETRIC_STATE_CANCELLING_RESTARTING;
import static com.android.keyguard.KeyguardUpdateMonitor.DEFAULT_CANCEL_SIGNAL_TIMEOUT;
@@ -100,6 +101,8 @@ import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import androidx.annotation.NonNull;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.InstanceId;
@@ -267,21 +270,9 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
// IBiometricsFace@1.0 does not support detection, only authentication.
when(mFaceSensorProperties.isEmpty()).thenReturn(false);
when(mFaceSensorProperties.get(anyInt())).thenReturn(
createFaceSensorProperties(/* supportsFaceDetection = */ false));
final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
componentInfo.add(new ComponentInfoInternal("faceSensor" /* componentId */,
"vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */,
"00000001" /* serialNumber */, "" /* softwareVersion */));
componentInfo.add(new ComponentInfoInternal("matchingAlgorithm" /* componentId */,
"" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */,
"vendor/version/revision" /* softwareVersion */));
when(mFaceSensorProperties.get(anyInt())).thenReturn(new FaceSensorPropertiesInternal(
0 /* id */,
FaceSensorProperties.STRENGTH_STRONG, 1 /* maxTemplatesAllowed */,
componentInfo, FaceSensorProperties.TYPE_UNKNOWN,
false /* supportsFaceDetection */, true /* supportsSelfIllumination */,
false /* resetLockoutRequiresChallenge */));
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
when(mFingerprintManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(List.of(
@@ -354,6 +345,28 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
when(mAuthController.areAllFingerprintAuthenticatorsRegistered()).thenReturn(true);
}
@NonNull
private FaceSensorPropertiesInternal createFaceSensorProperties(boolean supportsFaceDetection) {
final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
componentInfo.add(new ComponentInfoInternal("faceSensor" /* componentId */,
"vendor/model/revision" /* hardwareVersion */, "1.01" /* firmwareVersion */,
"00000001" /* serialNumber */, "" /* softwareVersion */));
componentInfo.add(new ComponentInfoInternal("matchingAlgorithm" /* componentId */,
"" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */,
"vendor/version/revision" /* softwareVersion */));
return new FaceSensorPropertiesInternal(
0 /* id */,
FaceSensorProperties.STRENGTH_STRONG,
1 /* maxTemplatesAllowed */,
componentInfo,
FaceSensorProperties.TYPE_UNKNOWN,
supportsFaceDetection /* supportsFaceDetection */,
true /* supportsSelfIllumination */,
false /* resetLockoutRequiresChallenge */);
}
@After
public void tearDown() {
if (mMockitoSession != null) {
@@ -611,7 +624,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testUnlockingWithFaceAllowed_strongAuthTrackerUnlockingWithBiometricAllowed() {
// GIVEN unlocking with biometric is allowed
when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
strongAuthNotRequired();
// THEN unlocking with face and fp is allowed
Assert.assertTrue(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
@@ -633,7 +646,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testUnlockingWithFaceAllowed_fingerprintLockout() {
// GIVEN unlocking with biometric is allowed
when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
strongAuthNotRequired();
// WHEN fingerprint is locked out
fingerprintErrorLockedOut();
@@ -656,7 +669,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testUnlockingWithFpAllowed_fingerprintLockout() {
// GIVEN unlocking with biometric is allowed
when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
strongAuthNotRequired();
// WHEN fingerprint is locked out
fingerprintErrorLockedOut();
@@ -710,10 +723,9 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
}
@Test
public void skipsAuthentication_whenEncryptedKeyguard() {
when(mStrongAuthTracker.getStrongAuthForUser(anyInt())).thenReturn(
STRONG_AUTH_REQUIRED_AFTER_BOOT);
mKeyguardUpdateMonitor.setKeyguardBypassController(mKeyguardBypassController);
public void skipsAuthentication_whenStrongAuthRequired_nonBypass() {
lockscreenBypassIsNotAllowed();
when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
mTestableLooper.processAllMessages();
@@ -723,15 +735,48 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
}
@Test
public void requiresAuthentication_whenEncryptedKeyguard_andBypass() {
testStrongAuthExceptOnBouncer(
STRONG_AUTH_REQUIRED_AFTER_BOOT);
public void faceDetect_whenStrongAuthRequiredAndBypass() {
// GIVEN bypass is enabled, face detection is supported and strong auth is required
lockscreenBypassIsAllowed();
supportsFaceDetection();
strongAuthRequiredEncrypted();
keyguardIsVisible();
// WHEN the device wakes up
mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
mTestableLooper.processAllMessages();
// FACE detect is triggered, not authenticate
verify(mFaceManager).detectFace(any(), any(), anyInt());
verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt(),
anyBoolean());
// WHEN bouncer becomes visible
setKeyguardBouncerVisibility(true);
clearInvocations(mFaceManager);
// THEN face scanning is not run
mKeyguardUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.UDFPS_POINTER_DOWN);
verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt(),
anyBoolean());
verify(mFaceManager, never()).detectFace(any(), any(), anyInt());
}
@Test
public void requiresAuthentication_whenTimeoutKeyguard_andBypass() {
testStrongAuthExceptOnBouncer(
KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT);
public void noFaceDetect_whenStrongAuthRequiredAndBypass_faceDetectionUnsupported() {
// GIVEN bypass is enabled, face detection is NOT supported and strong auth is required
lockscreenBypassIsAllowed();
strongAuthRequiredEncrypted();
keyguardIsVisible();
// WHEN the device wakes up
mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
mTestableLooper.processAllMessages();
// FACE detect and authenticate are NOT triggered
verify(mFaceManager, never()).detectFace(any(), any(), anyInt());
verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt(),
anyBoolean());
}
@Test
@@ -764,24 +809,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
assertThat(didFaceAuthRun).isFalse();
}
private void testStrongAuthExceptOnBouncer(int strongAuth) {
when(mKeyguardBypassController.canBypass()).thenReturn(true);
mKeyguardUpdateMonitor.setKeyguardBypassController(mKeyguardBypassController);
when(mStrongAuthTracker.getStrongAuthForUser(anyInt())).thenReturn(strongAuth);
mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
mTestableLooper.processAllMessages();
keyguardIsVisible();
verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean());
// Stop scanning when bouncer becomes visible
setKeyguardBouncerVisibility(true);
clearInvocations(mFaceManager);
mKeyguardUpdateMonitor.requestFaceAuth(FaceAuthApiRequestReason.UDFPS_POINTER_DOWN);
verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt(),
anyBoolean());
}
@Test
public void testTriesToAuthenticate_whenAssistant() {
mKeyguardUpdateMonitor.setKeyguardShowing(true, true);
@@ -792,10 +819,9 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Test
public void testTriesToAuthenticate_whenTrustOnAgentKeyguard_ifBypass() {
mKeyguardUpdateMonitor.setKeyguardBypassController(mKeyguardBypassController);
mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
mTestableLooper.processAllMessages();
when(mKeyguardBypassController.canBypass()).thenReturn(true);
lockscreenBypassIsAllowed();
mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */,
KeyguardUpdateMonitor.getCurrentUser(), 0 /* flags */,
new ArrayList<>());
@@ -815,26 +841,17 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
}
@Test
public void testIgnoresAuth_whenLockdown() {
mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
mTestableLooper.processAllMessages();
when(mStrongAuthTracker.getStrongAuthForUser(anyInt())).thenReturn(
KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
public void testNoFaceAuth_whenLockDown() {
when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
userDeviceLockDown();
mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
keyguardIsVisible();
mTestableLooper.processAllMessages();
verify(mFaceManager, never()).authenticate(any(), any(), any(), any(), anyInt(),
anyBoolean());
}
@Test
public void testTriesToAuthenticate_whenLockout() {
mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
mTestableLooper.processAllMessages();
when(mStrongAuthTracker.getStrongAuthForUser(anyInt())).thenReturn(
KeyguardUpdateMonitor.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT);
keyguardIsVisible();
verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean());
verify(mFaceManager, never()).detectFace(any(), any(), anyInt());
}
@Test
@@ -977,8 +994,6 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean());
verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt(), anyInt(),
anyInt());
// resetFaceManager();
// resetFingerprintManager();
when(mFingerprintManager.getLockoutModeForUser(eq(FINGERPRINT_SENSOR_ID), eq(newUser)))
.thenReturn(fingerprintLockoutMode);
@@ -1322,7 +1337,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mStatusBarStateListener.onStateChanged(StatusBarState.SHADE_LOCKED);
setKeyguardBouncerVisibility(false /* isVisible */);
mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
when(mKeyguardBypassController.canBypass()).thenReturn(true);
lockscreenBypassIsAllowed();
keyguardIsVisible();
// WHEN status bar state reports a change to the keyguard that would normally indicate to
@@ -1506,11 +1521,9 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
userNotCurrentlySwitching();
// This disables face auth
when(mStrongAuthTracker.getStrongAuthForUser(KeyguardUpdateMonitor.getCurrentUser()))
.thenReturn(STRONG_AUTH_REQUIRED_AFTER_BOOT);
when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
mTestableLooper.processAllMessages();
assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
}
@@ -1974,6 +1987,109 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
);
}
@Test
public void testStrongAuthChange_lockDown_stopsFpAndFaceListeningState() {
// GIVEN device is listening for face and fingerprint
mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
mTestableLooper.processAllMessages();
keyguardIsVisible();
verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean());
verify(mFingerprintManager).authenticate(any(), any(), any(), any(), anyInt(), anyInt(),
anyInt());
final CancellationSignal faceCancel = spy(mKeyguardUpdateMonitor.mFaceCancelSignal);
final CancellationSignal fpCancel = spy(mKeyguardUpdateMonitor.mFingerprintCancelSignal);
mKeyguardUpdateMonitor.mFaceCancelSignal = faceCancel;
mKeyguardUpdateMonitor.mFingerprintCancelSignal = fpCancel;
KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class);
mKeyguardUpdateMonitor.registerCallback(callback);
// WHEN strong auth changes and device is in user lockdown
when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
userDeviceLockDown();
mKeyguardUpdateMonitor.notifyStrongAuthAllowedChanged(getCurrentUser());
mTestableLooper.processAllMessages();
// THEN face and fingerprint listening are cancelled
verify(faceCancel).cancel();
verify(callback).onBiometricRunningStateChanged(
eq(false), eq(BiometricSourceType.FACE));
verify(fpCancel).cancel();
verify(callback).onBiometricRunningStateChanged(
eq(false), eq(BiometricSourceType.FINGERPRINT));
}
@Test
public void testNonStrongBiometricAllowedChanged_stopsFaceListeningState() {
// GIVEN device is listening for face and fingerprint
mKeyguardUpdateMonitor.dispatchStartedWakingUp(PowerManager.WAKE_REASON_POWER_BUTTON);
mTestableLooper.processAllMessages();
keyguardIsVisible();
verify(mFaceManager).authenticate(any(), any(), any(), any(), anyInt(), anyBoolean());
final CancellationSignal faceCancel = spy(mKeyguardUpdateMonitor.mFaceCancelSignal);
mKeyguardUpdateMonitor.mFaceCancelSignal = faceCancel;
KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class);
mKeyguardUpdateMonitor.registerCallback(callback);
// WHEN non-strong biometric allowed changes
when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
mKeyguardUpdateMonitor.notifyNonStrongBiometricAllowedChanged(getCurrentUser());
mTestableLooper.processAllMessages();
// THEN face and fingerprint listening are cancelled
verify(faceCancel).cancel();
verify(callback).onBiometricRunningStateChanged(
eq(false), eq(BiometricSourceType.FACE));
}
@Test
public void testShouldListenForFace_withLockedDown_returnsFalse()
throws RemoteException {
keyguardNotGoingAway();
bouncerFullyVisibleAndNotGoingToSleep();
currentUserIsPrimary();
currentUserDoesNotHaveTrust();
biometricsNotDisabledThroughDevicePolicyManager();
biometricsEnabledForCurrentUser();
userNotCurrentlySwitching();
supportsFaceDetection();
mTestableLooper.processAllMessages();
assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
userDeviceLockDown();
assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
}
private void userDeviceLockDown() {
when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
when(mStrongAuthTracker.getStrongAuthForUser(mCurrentUserId))
.thenReturn(STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN);
}
private void supportsFaceDetection() {
when(mFaceSensorProperties.get(anyInt()))
.thenReturn(createFaceSensorProperties(
/* supportsFaceDetection = */ true));
}
private void lockscreenBypassIsAllowed() {
mockCanBypassLockscreen(true);
}
private void mockCanBypassLockscreen(boolean canBypass) {
mKeyguardUpdateMonitor.setKeyguardBypassController(mKeyguardBypassController);
when(mKeyguardBypassController.canBypass()).thenReturn(canBypass);
}
private void lockscreenBypassIsNotAllowed() {
mockCanBypassLockscreen(false);
}
private void cleanupKeyguardUpdateMonitor() {
if (mKeyguardUpdateMonitor != null) {
mKeyguardUpdateMonitor.removeCallback(mTestCallback);
@@ -2067,9 +2183,16 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
);
}
private void strongAuthRequiredEncrypted() {
when(mStrongAuthTracker.getStrongAuthForUser(KeyguardUpdateMonitor.getCurrentUser()))
.thenReturn(STRONG_AUTH_REQUIRED_AFTER_BOOT);
when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
}
private void strongAuthNotRequired() {
when(mStrongAuthTracker.getStrongAuthForUser(KeyguardUpdateMonitor.getCurrentUser()))
.thenReturn(0);
when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
}
private void currentUserDoesNotHaveTrust() {
@@ -2110,6 +2233,10 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
setKeyguardBouncerVisibility(true);
}
private void bouncerNotVisible() {
setKeyguardBouncerVisibility(false);
}
private void setKeyguardBouncerVisibility(boolean isVisible) {
mKeyguardUpdateMonitor.sendPrimaryBouncerChanged(isVisible, isVisible);
mTestableLooper.processAllMessages();