Merge "Add additional toggleable active unlock settings" into tm-dev
This commit is contained in:
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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))
|
||||
|
||||
Reference in New Issue
Block a user