Merge "Add additional toggleable active unlock settings" into tm-dev

This commit is contained in:
Beverly Tai
2022-05-07 01:28:17 +00:00
committed by Android (Google) Code Review
6 changed files with 396 additions and 65 deletions

View File

@@ -9694,6 +9694,40 @@ public final class Settings {
public static final String ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL =
"active_unlock_on_biometric_fail";
/**
* If active unlock triggers on biometric failures, include the following error codes
* as a biometric failure. See {@link android.hardware.biometrics.BiometricFaceConstants}.
* Error codes should be separated by a pipe. For example: "1|4|5". If active unlock
* should never trigger on any face errors, this should be set to an empty string.
* A null value will use the system default value (TIMEOUT).
* @hide
*/
public static final String ACTIVE_UNLOCK_ON_FACE_ERRORS =
"active_unlock_on_face_errors";
/**
* If active unlock triggers on biometric failures, include the following acquired info
* as a "biometric failure". See {@link android.hardware.biometrics.BiometricFaceConstants}.
* Acquired codes should be separated by a pipe. For example: "1|4|5". If active unlock
* should never on trigger on any acquired info messages, this should be
* set to an empty string. A null value will use the system default value (none).
* @hide
*/
public static final String ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO =
"active_unlock_on_face_acquire_info";
/**
* If active unlock triggers on biometric failures, then also request active unlock on
* unlock intent when each setting (BiometricType) is the only biometric type enrolled.
* Biometric types should be separated by a pipe. For example: "0|3" or "0". If this
* setting should be disabled, then this should be set to an empty string. A null value
* will use the system default value (0 / None).
* 0 = None, 1 = Any face, 2 = Any fingerprint, 3 = Under display fingerprint
* @hide
*/
public static final String ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED =
"active_unlock_on_unlock_intent_when_biometric_enrolled";
/**
* Whether the assist gesture should be enabled.
*

View File

@@ -120,6 +120,9 @@ public class SecureSettings {
Settings.Secure.ACTIVE_UNLOCK_ON_WAKE,
Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT,
Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS,
Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO,
Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
Settings.Secure.VR_DISPLAY_MODE,
Settings.Secure.NOTIFICATION_BADGING,
Settings.Secure.NOTIFICATION_DISMISS_RTL,

View File

@@ -18,6 +18,7 @@ package android.provider.settings.validators;
import static android.provider.settings.validators.SettingsValidators.ACCESSIBILITY_SHORTCUT_TARGET_LIST_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.ANY_INTEGER_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.ANY_STRING_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.BOOLEAN_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.COLON_SEPARATED_COMPONENT_LIST_VALIDATOR;
import static android.provider.settings.validators.SettingsValidators.COLON_SEPARATED_PACKAGE_LIST_VALIDATOR;
@@ -176,6 +177,10 @@ public class SecureSettingsValidators {
VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_WAKE, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS, ANY_STRING_VALIDATOR);
VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO, ANY_STRING_VALIDATOR);
VALIDATORS.put(Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
ANY_STRING_VALIDATOR);
VALIDATORS.put(Secure.ASSIST_GESTURE_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ASSIST_GESTURE_SILENCE_ALERTS_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.ASSIST_GESTURE_WAKE_ENABLED, BOOLEAN_VALIDATOR);

View File

@@ -16,14 +16,20 @@
package com.android.keyguard
import android.annotation.IntDef
import android.content.ContentResolver
import android.database.ContentObserver
import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_TIMEOUT
import android.net.Uri
import android.os.Handler
import android.os.UserHandle
import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL
import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO
import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS
import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT
import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED
import android.provider.Settings.Secure.ACTIVE_UNLOCK_ON_WAKE
import android.util.Log
import com.android.keyguard.KeyguardUpdateMonitor.getCurrentUser
import com.android.systemui.Dumpable
import com.android.systemui.dagger.SysUISingleton
@@ -44,6 +50,20 @@ class ActiveUnlockConfig @Inject constructor(
dumpManager: DumpManager
) : Dumpable {
companion object {
const val TAG = "ActiveUnlockConfig"
const val BIOMETRIC_TYPE_NONE = 0
const val BIOMETRIC_TYPE_ANY_FACE = 1
const val BIOMETRIC_TYPE_ANY_FINGERPRINT = 2
const val BIOMETRIC_TYPE_UNDER_DISPLAY_FINGERPRINT = 3
}
@Retention(AnnotationRetention.SOURCE)
@IntDef(BIOMETRIC_TYPE_NONE, BIOMETRIC_TYPE_ANY_FACE, BIOMETRIC_TYPE_ANY_FINGERPRINT,
BIOMETRIC_TYPE_UNDER_DISPLAY_FINGERPRINT)
annotation class BiometricType
/**
* Indicates the origin for an active unlock request.
*/
@@ -51,35 +71,50 @@ class ActiveUnlockConfig @Inject constructor(
WAKE, UNLOCK_INTENT, BIOMETRIC_FAIL, ASSISTANT
}
var keyguardUpdateMonitor: KeyguardUpdateMonitor? = null
private var requestActiveUnlockOnWakeup = false
private var requestActiveUnlockOnUnlockIntent = false
private var requestActiveUnlockOnBioFail = false
private var faceErrorsToTriggerBiometricFailOn = mutableSetOf(FACE_ERROR_TIMEOUT)
private var faceAcquireInfoToTriggerBiometricFailOn = mutableSetOf<Int>()
private var onUnlockIntentWhenBiometricEnrolled = mutableSetOf<Int>(BIOMETRIC_TYPE_NONE)
private val settingsObserver = object : ContentObserver(handler) {
private val wakeUri: Uri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_WAKE)
private val unlockIntentUri: Uri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT)
private val bioFailUri: Uri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL)
private val wakeUri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_WAKE)
private val unlockIntentUri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT)
private val bioFailUri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL)
private val faceErrorsUri = secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ERRORS)
private val faceAcquireInfoUri =
secureSettings.getUriFor(ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO)
private val unlockIntentWhenBiometricEnrolledUri =
secureSettings.getUriFor(ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED)
fun register() {
contentResolver.registerContentObserver(
wakeUri,
false,
this,
UserHandle.USER_ALL)
contentResolver.registerContentObserver(
unlockIntentUri,
false,
this,
UserHandle.USER_ALL)
contentResolver.registerContentObserver(
bioFailUri,
false,
this,
UserHandle.USER_ALL)
registerUri(
listOf(
wakeUri,
unlockIntentUri,
bioFailUri,
faceErrorsUri,
faceAcquireInfoUri,
unlockIntentWhenBiometricEnrolledUri
)
)
onChange(true, ArrayList(), 0, getCurrentUser())
}
private fun registerUri(uris: Collection<Uri>) {
for (uri in uris) {
contentResolver.registerContentObserver(
uri,
false,
this,
UserHandle.USER_ALL)
}
}
override fun onChange(
selfChange: Boolean,
uris: Collection<Uri>,
@@ -104,6 +139,55 @@ class ActiveUnlockConfig @Inject constructor(
requestActiveUnlockOnBioFail = secureSettings.getIntForUser(
ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL, 0, getCurrentUser()) == 1
}
if (selfChange || uris.contains(faceErrorsUri)) {
processStringArray(
secureSettings.getStringForUser(ACTIVE_UNLOCK_ON_FACE_ERRORS,
getCurrentUser()),
faceErrorsToTriggerBiometricFailOn,
setOf(FACE_ERROR_TIMEOUT))
}
if (selfChange || uris.contains(faceAcquireInfoUri)) {
processStringArray(
secureSettings.getStringForUser(ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO,
getCurrentUser()),
faceAcquireInfoToTriggerBiometricFailOn,
setOf<Int>())
}
if (selfChange || uris.contains(unlockIntentWhenBiometricEnrolledUri)) {
processStringArray(
secureSettings.getStringForUser(
ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
getCurrentUser()),
onUnlockIntentWhenBiometricEnrolled,
setOf(BIOMETRIC_TYPE_NONE))
}
}
/**
* Convert a pipe-separated set of integers into a set of ints.
* @param stringSetting expected input are integers delineated by a pipe. For example,
* it may look something like this: "1|5|3".
* @param out updates the "out" Set will the integers between the pipes.
* @param default If stringSetting is null, "out" will be populated with values in "default"
*/
private fun processStringArray(
stringSetting: String?,
out: MutableSet<Int>,
default: Set<Int>
) {
out.clear()
stringSetting?.let {
for (code: String in stringSetting.split("|")) {
try {
out.add(code.toInt())
} catch (e: NumberFormatException) {
Log.e(TAG, "Passed an invalid setting=$code")
}
}
} ?: out.addAll(default)
}
}
@@ -112,6 +196,30 @@ class ActiveUnlockConfig @Inject constructor(
dumpManager.registerDumpable(this)
}
/**
* If any active unlock triggers are enabled.
*/
fun isActiveUnlockEnabled(): Boolean {
return requestActiveUnlockOnWakeup || requestActiveUnlockOnUnlockIntent ||
requestActiveUnlockOnBioFail
}
/**
* Whether the face error code from {@link BiometricFaceConstants} should trigger
* active unlock on biometric failure.
*/
fun shouldRequestActiveUnlockOnFaceError(errorCode: Int): Boolean {
return faceErrorsToTriggerBiometricFailOn.contains(errorCode)
}
/**
* Whether the face acquireInfo from {@link BiometricFaceConstants} should trigger
* active unlock on biometric failure.
*/
fun shouldRequestActiveUnlockOnFaceAcquireInfo(acquiredInfo: Int): Boolean {
return faceAcquireInfoToTriggerBiometricFailOn.contains(acquiredInfo)
}
/**
* Whether to trigger active unlock based on where the request is coming from and
* the current settings.
@@ -121,7 +229,8 @@ class ActiveUnlockConfig @Inject constructor(
ACTIVE_UNLOCK_REQUEST_ORIGIN.WAKE -> requestActiveUnlockOnWakeup
ACTIVE_UNLOCK_REQUEST_ORIGIN.UNLOCK_INTENT ->
requestActiveUnlockOnUnlockIntent || requestActiveUnlockOnWakeup
requestActiveUnlockOnUnlockIntent || requestActiveUnlockOnWakeup ||
(shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment())
ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL ->
requestActiveUnlockOnBioFail || requestActiveUnlockOnUnlockIntent ||
@@ -131,17 +240,55 @@ class ActiveUnlockConfig @Inject constructor(
}
}
/**
* If any active unlock triggers are enabled.
*/
fun isActiveUnlockEnabled(): Boolean {
return requestActiveUnlockOnWakeup || requestActiveUnlockOnUnlockIntent ||
requestActiveUnlockOnBioFail
private fun shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment(): Boolean {
if (!requestActiveUnlockOnBioFail) {
return false
}
keyguardUpdateMonitor?.let {
val anyFaceEnrolled = it.isFaceEnrolled
val anyFingerprintEnrolled =
it.getCachedIsUnlockWithFingerprintPossible(getCurrentUser())
val udfpsEnrolled = it.isUdfpsEnrolled
if (!anyFaceEnrolled && !anyFingerprintEnrolled) {
return onUnlockIntentWhenBiometricEnrolled.contains(BIOMETRIC_TYPE_NONE)
}
if (!anyFaceEnrolled && anyFingerprintEnrolled) {
return onUnlockIntentWhenBiometricEnrolled.contains(
BIOMETRIC_TYPE_ANY_FINGERPRINT) ||
(udfpsEnrolled && onUnlockIntentWhenBiometricEnrolled.contains(
BIOMETRIC_TYPE_UNDER_DISPLAY_FINGERPRINT))
}
if (!anyFingerprintEnrolled && anyFaceEnrolled) {
return onUnlockIntentWhenBiometricEnrolled.contains(BIOMETRIC_TYPE_ANY_FACE)
}
}
return false
}
override fun dump(pw: PrintWriter, args: Array<out String>) {
pw.println("Settings:")
pw.println(" requestActiveUnlockOnWakeup=$requestActiveUnlockOnWakeup")
pw.println(" requestActiveUnlockOnUnlockIntent=$requestActiveUnlockOnUnlockIntent")
pw.println(" requestActiveUnlockOnBioFail=$requestActiveUnlockOnBioFail")
pw.println(" requestActiveUnlockOnUnlockIntentWhenBiometricEnrolled=" +
"$onUnlockIntentWhenBiometricEnrolled")
pw.println(" requestActiveUnlockOnFaceError=$faceErrorsToTriggerBiometricFailOn")
pw.println(" requestActiveUnlockOnFaceAcquireInfo=" +
"$faceAcquireInfoToTriggerBiometricFailOn")
pw.println("Current state:")
keyguardUpdateMonitor?.let {
pw.println(" shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment=" +
"${shouldRequestActiveUnlockOnUnlockIntentFromBiometricEnrollment()}")
pw.println(" faceEnrolled=${it.isFaceEnrolled}")
pw.println(" fpEnrolled=${
it.getCachedIsUnlockWithFingerprintPossible(getCurrentUser())}")
pw.println(" udfpsEnrolled=${it.isUdfpsEnrolled}")
} ?: pw.println(" keyguardUpdateMonitor is uninitialized")
}
}

View File

@@ -56,7 +56,6 @@ import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.hardware.SensorPrivacyManager;
import android.hardware.biometrics.BiometricFaceConstants;
import android.hardware.biometrics.BiometricFingerprintConstants;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricSourceType;
@@ -1615,7 +1614,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mKeyguardBypassController.setUserHasDeviceEntryIntent(false);
}
if (errMsgId == BiometricFaceConstants.FACE_ERROR_TIMEOUT) {
if (mActiveUnlockConfig.shouldRequestActiveUnlockOnFaceError(errMsgId)) {
requestActiveUnlock(
ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL,
"faceError-" + errMsgId);
@@ -1625,6 +1624,13 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
@Override
public void onAuthenticationAcquired(int acquireInfo) {
handleFaceAcquired(acquireInfo);
if (mActiveUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo(
acquireInfo)) {
requestActiveUnlock(
ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL,
"faceAcquireInfo-" + acquireInfo);
}
}
};
@@ -1639,6 +1645,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private boolean mFingerprintLockedOut;
private boolean mFingerprintLockedOutPermanent;
private boolean mFaceLockedOutPermanent;
private HashMap<Integer, Boolean> mIsUnlockWithFingerprintPossible = new HashMap<>();
private TelephonyManager mTelephonyManager;
/**
@@ -1889,6 +1896,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
dumpManager.registerDumpable(getClass().getName(), this);
mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class);
mActiveUnlockConfig = activeUnlockConfiguration;
mActiveUnlockConfig.setKeyguardUpdateMonitor(this);
mHandler = new Handler(mainLooper) {
@Override
@@ -2329,7 +2337,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
if (shouldTriggerActiveUnlock()) {
if (DEBUG) {
if (DEBUG_ACTIVE_UNLOCK) {
Log.d("ActiveUnlock", "initiate active unlock triggerReason=" + reason);
}
mTrustManager.reportUserMayRequestUnlock(KeyguardUpdateMonitor.getCurrentUser());
@@ -2359,7 +2367,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
if (allowRequest && shouldTriggerActiveUnlock()) {
if (DEBUG) {
if (DEBUG_ACTIVE_UNLOCK) {
Log.d("ActiveUnlock", "reportUserRequestedUnlock"
+ " origin=" + requestOrigin.name()
+ " reason=" + reason
@@ -2777,8 +2785,17 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
private boolean isUnlockWithFingerprintPossible(int userId) {
return mFpm != null && mFpm.isHardwareDetected() && !isFingerprintDisabled(userId)
&& mFpm.hasEnrolledTemplates(userId);
mIsUnlockWithFingerprintPossible.put(userId, mFpm != null && mFpm.isHardwareDetected()
&& !isFingerprintDisabled(userId) && mFpm.hasEnrolledTemplates(userId));
return mIsUnlockWithFingerprintPossible.get(userId);
}
/**
* Cached value for whether fingerprint is enrolled and possible to use for authentication.
* Note: checking fingerprint enrollment directly with the AuthController requires an IPC.
*/
public boolean getCachedIsUnlockWithFingerprintPossible(int userId) {
return mIsUnlockWithFingerprintPossible.get(userId);
}
private boolean isUnlockWithFacePossible(int userId) {

View File

@@ -18,6 +18,7 @@ package com.android.keyguard
import android.content.ContentResolver
import android.database.ContentObserver
import android.hardware.biometrics.BiometricFaceConstants
import android.net.Uri
import android.os.Handler
import android.os.UserHandle
@@ -44,18 +45,20 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
private val fakeWakeUri = Uri.Builder().appendPath("wake").build()
private val fakeUnlockIntentUri = Uri.Builder().appendPath("unlock-intent").build()
private val fakeBioFailUri = Uri.Builder().appendPath("bio-fail").build()
private val fakeFaceErrorsUri = Uri.Builder().appendPath("face-errors").build()
private val fakeFaceAcquiredUri = Uri.Builder().appendPath("face-acquired").build()
private val fakeUnlockIntentBioEnroll = Uri.Builder().appendPath("unlock-intent-bio").build()
@Mock
private lateinit var secureSettings: SecureSettings
@Mock
private lateinit var contentResolver: ContentResolver
@Mock
private lateinit var handler: Handler
@Mock
private lateinit var dumpManager: DumpManager
@Mock
private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Captor
private lateinit var settingsObserverCaptor: ArgumentCaptor<ContentObserver>
@@ -72,6 +75,13 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
.thenReturn(fakeUnlockIntentUri)
`when`(secureSettings.getUriFor(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL))
.thenReturn(fakeBioFailUri)
`when`(secureSettings.getUriFor(Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS))
.thenReturn(fakeFaceErrorsUri)
`when`(secureSettings.getUriFor(Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO))
.thenReturn(fakeFaceAcquiredUri)
`when`(secureSettings.getUriFor(
Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED))
.thenReturn(fakeUnlockIntentBioEnroll)
activeUnlockConfig = ActiveUnlockConfig(
handler,
@@ -99,12 +109,7 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
// WHEN unlock on wake is allowed
`when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_WAKE,
0, 0)).thenReturn(1)
settingsObserverCaptor.value.onChange(
false,
listOf(fakeWakeUri),
0,
0
)
updateSetting(fakeWakeUri)
// THEN active unlock triggers allowed on: wake, unlock-intent, and biometric failure
assertTrue(
@@ -134,12 +139,7 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
// WHEN unlock on biometric failed is allowed
`when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT,
0, 0)).thenReturn(1)
settingsObserverCaptor.value.onChange(
false,
listOf(fakeUnlockIntentUri),
0,
0
)
updateSetting(fakeUnlockIntentUri)
// THEN active unlock triggers allowed on: biometric failure ONLY
assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -154,19 +154,19 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
fun testOnBioFailSettingChanged() {
verifyRegisterSettingObserver()
// GIVEN no active unlock settings enabled
// GIVEN no active unlock settings enabled and triggering unlock intent on biometric
// enrollment setting is disabled (empty string is disabled, null would use the default)
`when`(secureSettings.getStringForUser(
Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
0)).thenReturn("")
updateSetting(fakeUnlockIntentBioEnroll)
assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL))
// WHEN unlock on biometric failed is allowed
`when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
0, 0)).thenReturn(1)
settingsObserverCaptor.value.onChange(
false,
listOf(fakeBioFailUri),
0,
0
)
updateSetting(fakeBioFailUri)
// THEN active unlock triggers allowed on: biometric failure ONLY
assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
@@ -177,21 +177,146 @@ class ActiveUnlockConfigTest : SysuiTestCase() {
ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.BIOMETRIC_FAIL))
}
@Test
fun testFaceErrorSettingsChanged() {
verifyRegisterSettingObserver()
// GIVEN unlock on biometric fail
`when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
0, 0)).thenReturn(1)
updateSetting(fakeBioFailUri)
// WHEN face error timeout (3), allow trigger active unlock
`when`(secureSettings.getStringForUser(Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ERRORS,
0)).thenReturn("3")
updateSetting(fakeFaceAcquiredUri)
// THEN active unlock triggers allowed on error TIMEOUT
assertTrue(activeUnlockConfig.shouldRequestActiveUnlockOnFaceError(
BiometricFaceConstants.FACE_ERROR_TIMEOUT))
assertFalse(activeUnlockConfig.shouldRequestActiveUnlockOnFaceError(
BiometricFaceConstants.FACE_ERROR_CANCELED))
}
@Test
fun testFaceAcquiredSettingsChanged() {
verifyRegisterSettingObserver()
// GIVEN unlock on biometric fail
`when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
0, 0)).thenReturn(1)
updateSetting(fakeBioFailUri)
// WHEN face acquiredMsg DARK_GLASSESand MOUTH_COVERING are allowed to trigger
`when`(secureSettings.getStringForUser(Settings.Secure.ACTIVE_UNLOCK_ON_FACE_ACQUIRE_INFO,
0)).thenReturn(
"${BiometricFaceConstants.FACE_ACQUIRED_MOUTH_COVERING_DETECTED}" +
"|${BiometricFaceConstants.FACE_ACQUIRED_DARK_GLASSES_DETECTED}")
updateSetting(fakeFaceAcquiredUri)
// THEN active unlock triggers allowed on acquired messages DARK_GLASSES & MOUTH_COVERING
assertTrue(activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo(
BiometricFaceConstants.FACE_ACQUIRED_MOUTH_COVERING_DETECTED))
assertTrue(activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo(
BiometricFaceConstants.FACE_ACQUIRED_DARK_GLASSES_DETECTED))
assertFalse(activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo(
BiometricFaceConstants.FACE_ACQUIRED_GOOD))
assertFalse(activeUnlockConfig.shouldRequestActiveUnlockOnFaceAcquireInfo(
BiometricFaceConstants.FACE_ACQUIRED_NOT_DETECTED))
}
@Test
fun testTriggerOnUnlockIntentWhenBiometricEnrolledNone() {
verifyRegisterSettingObserver()
// GIVEN unlock on biometric fail
`when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
0, 0)).thenReturn(1)
updateSetting(fakeBioFailUri)
// GIVEN fingerprint and face are NOT enrolled
activeUnlockConfig.keyguardUpdateMonitor = keyguardUpdateMonitor
`when`(keyguardUpdateMonitor.isFaceEnrolled()).thenReturn(false)
`when`(keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0)).thenReturn(false)
// WHEN unlock intent is allowed when NO biometrics are enrolled (0)
`when`(secureSettings.getStringForUser(
Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
0)).thenReturn("${ActiveUnlockConfig.BIOMETRIC_TYPE_NONE}")
updateSetting(fakeUnlockIntentBioEnroll)
// THEN active unlock triggers allowed on unlock intent
assertTrue(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.UNLOCK_INTENT))
}
@Test
fun testTriggerOnUnlockIntentWhenBiometricEnrolledFingerprintOrFaceOnly() {
verifyRegisterSettingObserver()
// GIVEN unlock on biometric fail
`when`(secureSettings.getIntForUser(Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
0, 0)).thenReturn(1)
updateSetting(fakeBioFailUri)
// GIVEN fingerprint and face are both enrolled
activeUnlockConfig.keyguardUpdateMonitor = keyguardUpdateMonitor
`when`(keyguardUpdateMonitor.isFaceEnrolled()).thenReturn(true)
`when`(keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0)).thenReturn(true)
// WHEN unlock intent is allowed when ONLY fingerprint is enrolled or NO biometircs
// are enrolled
`when`(secureSettings.getStringForUser(
Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT_WHEN_BIOMETRIC_ENROLLED,
0)).thenReturn(
"${ActiveUnlockConfig.BIOMETRIC_TYPE_ANY_FACE}" +
"|${ActiveUnlockConfig.BIOMETRIC_TYPE_ANY_FINGERPRINT}")
updateSetting(fakeUnlockIntentBioEnroll)
// THEN active unlock triggers NOT allowed on unlock intent
assertFalse(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.UNLOCK_INTENT))
// WHEN fingerprint ONLY enrolled
`when`(keyguardUpdateMonitor.isFaceEnrolled()).thenReturn(false)
`when`(keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0)).thenReturn(true)
// THEN active unlock triggers allowed on unlock intent
assertTrue(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.UNLOCK_INTENT))
// WHEN face ONLY enrolled
`when`(keyguardUpdateMonitor.isFaceEnrolled()).thenReturn(true)
`when`(keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(0)).thenReturn(false)
// THEN active unlock triggers allowed on unlock intent
assertTrue(activeUnlockConfig.shouldAllowActiveUnlockFromOrigin(
ActiveUnlockConfig.ACTIVE_UNLOCK_REQUEST_ORIGIN.UNLOCK_INTENT))
}
private fun updateSetting(uri: Uri) {
settingsObserverCaptor.value.onChange(
false,
listOf(uri),
0,
0 /* flags */
)
}
private fun verifyRegisterSettingObserver() {
verify(contentResolver).registerContentObserver(
eq(fakeWakeUri),
eq(false),
capture(settingsObserverCaptor),
eq(UserHandle.USER_ALL))
verifyRegisterSettingObserver(fakeWakeUri)
verifyRegisterSettingObserver(fakeUnlockIntentUri)
verifyRegisterSettingObserver(fakeBioFailUri)
verifyRegisterSettingObserver(fakeFaceErrorsUri)
verifyRegisterSettingObserver(fakeFaceAcquiredUri)
verifyRegisterSettingObserver(fakeUnlockIntentBioEnroll)
}
private fun verifyRegisterSettingObserver(uri: Uri) {
verify(contentResolver).registerContentObserver(
eq(fakeUnlockIntentUri),
eq(false),
capture(settingsObserverCaptor),
eq(UserHandle.USER_ALL))
verify(contentResolver).registerContentObserver(
eq(fakeBioFailUri),
eq(uri),
eq(false),
capture(settingsObserverCaptor),
eq(UserHandle.USER_ALL))