Implementations of biometric contraints for weak and convenience tiers
(1) 24 hours fallback
(2) 4 hours idle timeout
Bug: 141025588
Test: atest AuthServiceTest
Test: atest KeyguardUpdateMonitorTest
Test: atest BiometricsUnlockControllerTest
Test: atest KeyguardIndicationControllerTest
Test: make -j
Change-Id: I1078ce39a2ae1e4c250b6468e477b703e3016e2c
diff --git a/core/java/android/app/trust/IStrongAuthTracker.aidl b/core/java/android/app/trust/IStrongAuthTracker.aidl
index 36c71bf..6d54396 100644
--- a/core/java/android/app/trust/IStrongAuthTracker.aidl
+++ b/core/java/android/app/trust/IStrongAuthTracker.aidl
@@ -23,4 +23,5 @@
*/
oneway interface IStrongAuthTracker {
void onStrongAuthRequiredChanged(int strongAuthRequired, int userId);
+ void onIsNonStrongBiometricAllowedChanged(boolean allowed, int userId);
}
\ No newline at end of file
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 3a0660d..9b9889e 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -97,8 +97,10 @@
}
@Override // binder call
- public void onAuthenticationSucceeded(long deviceId, Face face, int userId) {
- mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, userId, 0, face).sendToTarget();
+ public void onAuthenticationSucceeded(long deviceId, Face face, int userId,
+ boolean isStrongBiometric) {
+ mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, userId, isStrongBiometric ? 1 : 0,
+ face).sendToTarget();
}
@Override // binder call
@@ -814,6 +816,7 @@
private Face mFace;
private CryptoObject mCryptoObject;
private int mUserId;
+ private boolean mIsStrongBiometric;
/**
* Authentication result
@@ -822,10 +825,12 @@
* @param face the recognized face data, if allowed.
* @hide
*/
- public AuthenticationResult(CryptoObject crypto, Face face, int userId) {
+ public AuthenticationResult(CryptoObject crypto, Face face, int userId,
+ boolean isStrongBiometric) {
mCryptoObject = crypto;
mFace = face;
mUserId = userId;
+ mIsStrongBiometric = isStrongBiometric;
}
/**
@@ -857,6 +862,16 @@
public int getUserId() {
return mUserId;
}
+
+ /**
+ * Check whether the strength of the face modality associated with this operation is strong
+ * (i.e. not weak or convenience).
+ *
+ * @hide
+ */
+ public boolean isStrongBiometric() {
+ return mIsStrongBiometric;
+ }
}
/**
@@ -1056,7 +1071,8 @@
msg.arg2 /* vendorCode */);
break;
case MSG_AUTHENTICATION_SUCCEEDED:
- sendAuthenticatedSucceeded((Face) msg.obj, msg.arg1 /* userId */);
+ sendAuthenticatedSucceeded((Face) msg.obj, msg.arg1 /* userId */,
+ msg.arg2 == 1 /* isStrongBiometric */);
break;
case MSG_AUTHENTICATION_FAILED:
sendAuthenticatedFailed();
@@ -1133,10 +1149,10 @@
}
}
- private void sendAuthenticatedSucceeded(Face face, int userId) {
+ private void sendAuthenticatedSucceeded(Face face, int userId, boolean isStrongBiometric) {
if (mAuthenticationCallback != null) {
final AuthenticationResult result =
- new AuthenticationResult(mCryptoObject, face, userId);
+ new AuthenticationResult(mCryptoObject, face, userId, isStrongBiometric);
mAuthenticationCallback.onAuthenticationSucceeded(result);
}
}
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index 8ba2473..6318274 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -110,4 +110,7 @@
void getFeature(int userId, int feature, IFaceServiceReceiver receiver, String opPackageName);
void userActivity();
+
+ // Initialize the OEM configured biometric strength
+ void initConfiguredStrength(int strength);
}
diff --git a/core/java/android/hardware/face/IFaceServiceReceiver.aidl b/core/java/android/hardware/face/IFaceServiceReceiver.aidl
index 10f9c43..7582308 100644
--- a/core/java/android/hardware/face/IFaceServiceReceiver.aidl
+++ b/core/java/android/hardware/face/IFaceServiceReceiver.aidl
@@ -24,7 +24,8 @@
oneway interface IFaceServiceReceiver {
void onEnrollResult(long deviceId, int faceId, int remaining);
void onAcquired(long deviceId, int acquiredInfo, int vendorCode);
- void onAuthenticationSucceeded(long deviceId, in Face face, int userId);
+ void onAuthenticationSucceeded(long deviceId, in Face face, int userId,
+ boolean isStrongBiometric);
void onAuthenticationFailed(long deviceId);
void onError(long deviceId, int error, int vendorCode);
void onRemoved(long deviceId, int faceId, int remaining);
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index f301a5c..dc8ea5e 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -176,6 +176,7 @@
private Fingerprint mFingerprint;
private CryptoObject mCryptoObject;
private int mUserId;
+ private boolean mIsStrongBiometric;
/**
* Authentication result
@@ -184,10 +185,12 @@
* @param fingerprint the recognized fingerprint data, if allowed.
* @hide
*/
- public AuthenticationResult(CryptoObject crypto, Fingerprint fingerprint, int userId) {
+ public AuthenticationResult(CryptoObject crypto, Fingerprint fingerprint, int userId,
+ boolean isStrongBiometric) {
mCryptoObject = crypto;
mFingerprint = fingerprint;
mUserId = userId;
+ mIsStrongBiometric = isStrongBiometric;
}
/**
@@ -211,6 +214,15 @@
* @hide
*/
public int getUserId() { return mUserId; }
+
+ /**
+ * Check whether the strength of the fingerprint modality associated with this operation is
+ * strong (i.e. not weak or convenience).
+ * @hide
+ */
+ public boolean isStrongBiometric() {
+ return mIsStrongBiometric;
+ }
};
/**
@@ -833,7 +845,8 @@
msg.arg2 /* vendorCode */);
break;
case MSG_AUTHENTICATION_SUCCEEDED:
- sendAuthenticatedSucceeded((Fingerprint) msg.obj, msg.arg1 /* userId */);
+ sendAuthenticatedSucceeded((Fingerprint) msg.obj, msg.arg1 /* userId */,
+ msg.arg2 == 1 /* isStrongBiometric */);
break;
case MSG_AUTHENTICATION_FAILED:
sendAuthenticatedFailed();
@@ -890,10 +903,10 @@
}
}
- private void sendAuthenticatedSucceeded(Fingerprint fp, int userId) {
+ private void sendAuthenticatedSucceeded(Fingerprint fp, int userId, boolean isStrongBiometric) {
if (mAuthenticationCallback != null) {
final AuthenticationResult result =
- new AuthenticationResult(mCryptoObject, fp, userId);
+ new AuthenticationResult(mCryptoObject, fp, userId, isStrongBiometric);
mAuthenticationCallback.onAuthenticationSucceeded(result);
}
}
@@ -1078,8 +1091,10 @@
}
@Override // binder call
- public void onAuthenticationSucceeded(long deviceId, Fingerprint fp, int userId) {
- mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, userId, 0, fp).sendToTarget();
+ public void onAuthenticationSucceeded(long deviceId, Fingerprint fp, int userId,
+ boolean isStrongBiometric) {
+ mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, userId, isStrongBiometric ? 1 : 0,
+ fp).sendToTarget();
}
@Override // binder call
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index f2ffd08d..0285448 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -113,4 +113,7 @@
// Removes a callback set by addClientActiveCallback
void removeClientActiveCallback(IFingerprintClientActiveCallback callback);
+
+ // Initialize the OEM configured biometric strength
+ void initConfiguredStrength(int strength);
}
diff --git a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
index cf1c94e..4412cee 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
@@ -24,7 +24,8 @@
oneway interface IFingerprintServiceReceiver {
void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining);
void onAcquired(long deviceId, int acquiredInfo, int vendorCode);
- void onAuthenticationSucceeded(long deviceId, in Fingerprint fp, int userId);
+ void onAuthenticationSucceeded(long deviceId, in Fingerprint fp, int userId,
+ boolean isStrongBiometric);
void onAuthenticationFailed(long deviceId);
void onError(long deviceId, int error, int vendorCode);
void onRemoved(long deviceId, int fingerId, int groupId, int remaining);
diff --git a/core/java/android/hardware/iris/IIrisService.aidl b/core/java/android/hardware/iris/IIrisService.aidl
index 8cf3c13..5ef0a0c 100644
--- a/core/java/android/hardware/iris/IIrisService.aidl
+++ b/core/java/android/hardware/iris/IIrisService.aidl
@@ -21,4 +21,6 @@
* @hide
*/
interface IIrisService {
-}
\ No newline at end of file
+ // Initialize the OEM configured biometric strength
+ void initConfiguredStrength(int strength);
+}
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index e24e982..e35fda1 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -57,6 +57,8 @@
void registerStrongAuthTracker(in IStrongAuthTracker tracker);
void unregisterStrongAuthTracker(in IStrongAuthTracker tracker);
void requireStrongAuth(int strongAuthReason, int userId);
+ void reportSuccessfulBiometricUnlock(boolean isStrongBiometric, int userId);
+ void scheduleNonStrongBiometricIdleTimeout(int userId);
void systemReady();
void userPresent(int userId);
int getStrongAuthForUser(int userId);
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 864429c..d9b2902 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -49,6 +49,7 @@
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
+import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.SparseLongArray;
@@ -1382,6 +1383,22 @@
}
}
+ public void reportSuccessfulBiometricUnlock(boolean isStrongBiometric, int userId) {
+ try {
+ getLockSettings().reportSuccessfulBiometricUnlock(isStrongBiometric, userId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not report successful biometric unlock", e);
+ }
+ }
+
+ public void scheduleNonStrongBiometricIdleTimeout(int userId) {
+ try {
+ getLockSettings().scheduleNonStrongBiometricIdleTimeout(userId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Could not schedule non-strong biometric idle timeout", e);
+ }
+ }
+
/**
* @see StrongAuthTracker#getStrongAuthForUser
*/
@@ -1557,7 +1574,8 @@
SOME_AUTH_REQUIRED_AFTER_USER_REQUEST,
STRONG_AUTH_REQUIRED_AFTER_LOCKOUT,
STRONG_AUTH_REQUIRED_AFTER_TIMEOUT,
- STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN})
+ STRONG_AUTH_REQUIRED_AFTER_USER_LOCKDOWN,
+ STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT})
@Retention(RetentionPolicy.SOURCE)
public @interface StrongAuthFlags {}
@@ -1604,6 +1622,12 @@
public static final int STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE = 0x40;
/**
+ * Strong authentication is required because it hasn't been used for a time after a
+ * non-strong biometric (i.e. weak or convenience biometric) is used to unlock device.
+ */
+ public static final int STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT = 0x80;
+
+ /**
* Strong auth flags that do not prevent biometric methods from being accepted as auth.
* If any other flags are set, biometric authentication is disabled.
*/
@@ -1614,6 +1638,10 @@
private final H mHandler;
private final int mDefaultStrongAuthFlags;
+ private final SparseBooleanArray mIsNonStrongBiometricAllowedForUser =
+ new SparseBooleanArray();
+ private final boolean mDefaultIsNonStrongBiometricAllowed = true;
+
public StrongAuthTracker(Context context) {
this(context, Looper.myLooper());
}
@@ -1657,8 +1685,21 @@
* @return true if unlocking with a biometric method alone is allowed for {@code userId}
* by the current strong authentication requirements.
*/
- public boolean isBiometricAllowedForUser(int userId) {
- return (getStrongAuthForUser(userId) & ~ALLOWING_BIOMETRIC) == 0;
+ public boolean isBiometricAllowedForUser(boolean isStrongBiometric, int userId) {
+ boolean allowed = ((getStrongAuthForUser(userId) & ~ALLOWING_BIOMETRIC) == 0);
+ if (!isStrongBiometric) {
+ allowed &= isNonStrongBiometricAllowedAfterIdleTimeout(userId);
+ }
+ return allowed;
+ }
+
+ /**
+ * @return true if unlocking with a non-strong (i.e. weak or convenience) biometric method
+ * alone is allowed for {@code userId}, otherwise returns false.
+ */
+ public boolean isNonStrongBiometricAllowedAfterIdleTimeout(int userId) {
+ return mIsNonStrongBiometricAllowedForUser.get(userId,
+ mDefaultIsNonStrongBiometricAllowed);
}
/**
@@ -1667,6 +1708,12 @@
public void onStrongAuthRequiredChanged(int userId) {
}
+ /**
+ * Called when whether non-strong biometric is allowed for {@code userId} changed.
+ */
+ public void onIsNonStrongBiometricAllowedChanged(int userId) {
+ }
+
protected void handleStrongAuthRequiredChanged(@StrongAuthFlags int strongAuthFlags,
int userId) {
int oldValue = getStrongAuthForUser(userId);
@@ -1680,6 +1727,18 @@
}
}
+ protected void handleIsNonStrongBiometricAllowedChanged(boolean allowed,
+ int userId) {
+ boolean oldValue = isNonStrongBiometricAllowedAfterIdleTimeout(userId);
+ if (allowed != oldValue) {
+ if (allowed == mDefaultIsNonStrongBiometricAllowed) {
+ mIsNonStrongBiometricAllowedForUser.delete(userId);
+ } else {
+ mIsNonStrongBiometricAllowedForUser.put(userId, allowed);
+ }
+ onIsNonStrongBiometricAllowedChanged(userId);
+ }
+ }
protected final IStrongAuthTracker.Stub mStub = new IStrongAuthTracker.Stub() {
@Override
@@ -1688,10 +1747,17 @@
mHandler.obtainMessage(H.MSG_ON_STRONG_AUTH_REQUIRED_CHANGED,
strongAuthFlags, userId).sendToTarget();
}
+
+ @Override
+ public void onIsNonStrongBiometricAllowedChanged(boolean allowed, int userId) {
+ mHandler.obtainMessage(H.MSG_ON_IS_NON_STRONG_BIOMETRIC_ALLOWED_CHANGED,
+ allowed ? 1 : 0, userId).sendToTarget();
+ }
};
private class H extends Handler {
static final int MSG_ON_STRONG_AUTH_REQUIRED_CHANGED = 1;
+ static final int MSG_ON_IS_NON_STRONG_BIOMETRIC_ALLOWED_CHANGED = 2;
public H(Looper looper) {
super(looper);
@@ -1703,6 +1769,10 @@
case MSG_ON_STRONG_AUTH_REQUIRED_CHANGED:
handleStrongAuthRequiredChanged(msg.arg1, msg.arg2);
break;
+ case MSG_ON_IS_NON_STRONG_BIOMETRIC_ALLOWED_CHANGED:
+ handleIsNonStrongBiometricAllowedChanged(msg.arg1 == 1 /* allowed */,
+ msg.arg2);
+ break;
}
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
index 09d4d5f..20b1e0d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityView.java
@@ -57,6 +57,12 @@
int PROMPT_REASON_PREPARE_FOR_UPDATE = 6;
/**
+ * Primary auth is required because the user uses weak/convenience biometrics and hasn't used
+ * primary auth since a while
+ */
+ int PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT = 7;
+
+ /**
* Interface back to keyguard to tell it when security
* @param callback
*/
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 9ba3860..52ea300 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -80,6 +80,7 @@
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import android.telephony.TelephonyManager;
import android.util.Log;
+import android.util.SparseArray;
import android.util.SparseBooleanArray;
import com.android.internal.annotations.VisibleForTesting;
@@ -91,6 +92,7 @@
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
@@ -108,6 +110,7 @@
import java.util.Map;
import java.util.Map.Entry;
import java.util.TimeZone;
+import java.util.concurrent.Executor;
import java.util.function.Consumer;
import javax.inject.Inject;
@@ -264,6 +267,7 @@
// If the user long pressed the lock icon, disabling face auth for the current session.
private boolean mLockIconPressed;
private int mActiveMobileDataSubscription = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ private final Executor mBackgroundExecutor;
/**
* Short delay before restarting biometric authentication after a successful try
@@ -320,12 +324,22 @@
}
};
+ private class BiometricAuthenticated {
+ private final boolean mAuthenticated;
+ private final boolean mIsStrongBiometric;
+
+ BiometricAuthenticated(boolean authenticated, boolean isStrongBiometric) {
+ this.mAuthenticated = authenticated;
+ this.mIsStrongBiometric = isStrongBiometric;
+ }
+ }
+
private SparseBooleanArray mUserIsUnlocked = new SparseBooleanArray();
private SparseBooleanArray mUserHasTrust = new SparseBooleanArray();
private SparseBooleanArray mUserTrustIsManaged = new SparseBooleanArray();
private SparseBooleanArray mUserTrustIsUsuallyManaged = new SparseBooleanArray();
- private SparseBooleanArray mUserFingerprintAuthenticated = new SparseBooleanArray();
- private SparseBooleanArray mUserFaceAuthenticated = new SparseBooleanArray();
+ private SparseArray<BiometricAuthenticated> mUserFingerprintAuthenticated = new SparseArray<>();
+ private SparseArray<BiometricAuthenticated> mUserFaceAuthenticated = new SparseArray<>();
private SparseBooleanArray mUserFaceUnlockRunning = new SparseBooleanArray();
private Map<Integer, Intent> mSecondaryLockscreenRequirement = new HashMap<Integer, Intent>();
@@ -523,10 +537,11 @@
}
@VisibleForTesting
- protected void onFingerprintAuthenticated(int userId) {
+ protected void onFingerprintAuthenticated(int userId, boolean isStrongBiometric) {
Assert.isMainThread();
Trace.beginSection("KeyGuardUpdateMonitor#onFingerPrintAuthenticated");
- mUserFingerprintAuthenticated.put(userId, true);
+ mUserFingerprintAuthenticated.put(userId,
+ new BiometricAuthenticated(true, isStrongBiometric));
// Update/refresh trust state only if user can skip bouncer
if (getUserCanSkipBouncer(userId)) {
mTrustManager.unlockedByBiometricForUser(userId, BiometricSourceType.FINGERPRINT);
@@ -536,7 +551,8 @@
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
- cb.onBiometricAuthenticated(userId, BiometricSourceType.FINGERPRINT);
+ cb.onBiometricAuthenticated(userId, BiometricSourceType.FINGERPRINT,
+ isStrongBiometric);
}
}
@@ -546,9 +562,21 @@
// Only authenticate fingerprint once when assistant is visible
mAssistantVisible = false;
+ // Report unlock with strong or non-strong biometric
+ reportSuccessfulBiometricUnlock(isStrongBiometric, userId);
+
Trace.endSection();
}
+ private void reportSuccessfulBiometricUnlock(boolean isStrongBiometric, int userId) {
+ mBackgroundExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ mLockPatternUtils.reportSuccessfulBiometricUnlock(isStrongBiometric, userId);
+ }
+ });
+ }
+
private void handleFingerprintAuthFailed() {
Assert.isMainThread();
for (int i = 0; i < mCallbacks.size(); i++) {
@@ -574,7 +602,7 @@
}
}
- private void handleFingerprintAuthenticated(int authUserId) {
+ private void handleFingerprintAuthenticated(int authUserId, boolean isStrongBiometric) {
Trace.beginSection("KeyGuardUpdateMonitor#handlerFingerPrintAuthenticated");
try {
final int userId;
@@ -592,7 +620,7 @@
Log.d(TAG, "Fingerprint disabled by DPM for userId: " + userId);
return;
}
- onFingerprintAuthenticated(userId);
+ onFingerprintAuthenticated(userId, isStrongBiometric);
} finally {
setFingerprintRunningState(BIOMETRIC_STATE_STOPPED);
}
@@ -683,10 +711,11 @@
}
@VisibleForTesting
- protected void onFaceAuthenticated(int userId) {
+ protected void onFaceAuthenticated(int userId, boolean isStrongBiometric) {
Trace.beginSection("KeyGuardUpdateMonitor#onFaceAuthenticated");
Assert.isMainThread();
- mUserFaceAuthenticated.put(userId, true);
+ mUserFaceAuthenticated.put(userId,
+ new BiometricAuthenticated(true, isStrongBiometric));
// Update/refresh trust state only if user can skip bouncer
if (getUserCanSkipBouncer(userId)) {
mTrustManager.unlockedByBiometricForUser(userId, BiometricSourceType.FACE);
@@ -697,7 +726,8 @@
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
cb.onBiometricAuthenticated(userId,
- BiometricSourceType.FACE);
+ BiometricSourceType.FACE,
+ isStrongBiometric);
}
}
@@ -707,6 +737,9 @@
// Only authenticate face once when assistant is visible
mAssistantVisible = false;
+ // Report unlock with strong or non-strong biometric
+ reportSuccessfulBiometricUnlock(isStrongBiometric, userId);
+
Trace.endSection();
}
@@ -737,7 +770,7 @@
}
}
- private void handleFaceAuthenticated(int authUserId) {
+ private void handleFaceAuthenticated(int authUserId, boolean isStrongBiometric) {
Trace.beginSection("KeyGuardUpdateMonitor#handlerFaceAuthenticated");
try {
if (mGoingToSleep) {
@@ -760,7 +793,7 @@
return;
}
if (DEBUG_FACE) Log.d(TAG, "Face auth succeeded for user " + userId);
- onFaceAuthenticated(userId);
+ onFaceAuthenticated(userId, isStrongBiometric);
} finally {
setFaceRunningState(BIOMETRIC_STATE_STOPPED);
}
@@ -914,9 +947,13 @@
* Returns whether the user is unlocked with biometrics.
*/
public boolean getUserUnlockedWithBiometric(int userId) {
- boolean fingerprintOrFace = mUserFingerprintAuthenticated.get(userId)
- || mUserFaceAuthenticated.get(userId);
- return fingerprintOrFace && isUnlockingWithBiometricAllowed();
+ BiometricAuthenticated fingerprint = mUserFingerprintAuthenticated.get(userId);
+ BiometricAuthenticated face = mUserFaceAuthenticated.get(userId);
+ boolean fingerprintAllowed = fingerprint != null && fingerprint.mAuthenticated
+ && isUnlockingWithBiometricAllowed(fingerprint.mIsStrongBiometric);
+ boolean faceAllowed = face != null && face.mAuthenticated
+ && isUnlockingWithBiometricAllowed(face.mIsStrongBiometric);
+ return fingerprintAllowed || faceAllowed;
}
public boolean getUserTrustIsManaged(int userId) {
@@ -970,8 +1007,8 @@
return mUserTrustIsUsuallyManaged.get(userId);
}
- public boolean isUnlockingWithBiometricAllowed() {
- return mStrongAuthTracker.isUnlockingWithBiometricAllowed();
+ public boolean isUnlockingWithBiometricAllowed(boolean isStrongBiometric) {
+ return mStrongAuthTracker.isUnlockingWithBiometricAllowed(isStrongBiometric);
}
public boolean isUserInLockdown(int userId) {
@@ -1169,7 +1206,7 @@
@Override
public void onAuthenticationSucceeded(AuthenticationResult result) {
Trace.beginSection("KeyguardUpdateMonitor#onAuthenticationSucceeded");
- handleFingerprintAuthenticated(result.getUserId());
+ handleFingerprintAuthenticated(result.getUserId(), result.isStrongBiometric());
Trace.endSection();
}
@@ -1201,7 +1238,7 @@
@Override
public void onAuthenticationSucceeded(FaceManager.AuthenticationResult result) {
Trace.beginSection("KeyguardUpdateMonitor#onAuthenticationSucceeded");
- handleFaceAuthenticated(result.getUserId());
+ handleFaceAuthenticated(result.getUserId(), result.isStrongBiometric());
Trace.endSection();
}
@@ -1305,9 +1342,9 @@
mStrongAuthRequiredChangedCallback = strongAuthRequiredChangedCallback;
}
- public boolean isUnlockingWithBiometricAllowed() {
+ public boolean isUnlockingWithBiometricAllowed(boolean isStrongBiometric) {
int userId = getCurrentUser();
- return isBiometricAllowedForUser(userId);
+ return isBiometricAllowedForUser(isStrongBiometric, userId);
}
public boolean hasUserAuthenticatedSinceBoot() {
@@ -1438,12 +1475,14 @@
Context context,
@Main Looper mainLooper,
BroadcastDispatcher broadcastDispatcher,
- DumpController dumpController) {
+ DumpController dumpController,
+ @Background Executor backgroundExecutor) {
mContext = context;
mSubscriptionManager = SubscriptionManager.from(context);
mDeviceProvisioned = isDeviceProvisionedInSettingsDb();
mStrongAuthTracker = new StrongAuthTracker(context, this::notifyStrongAuthStateChanged);
dumpController.registerDumpable(this);
+ mBackgroundExecutor = backgroundExecutor;
mHandler = new Handler(mainLooper) {
@Override
@@ -1753,14 +1792,16 @@
}
private boolean shouldListenForFingerprintAssistant() {
+ BiometricAuthenticated fingerprint = mUserFingerprintAuthenticated.get(getCurrentUser());
return mAssistantVisible && mKeyguardOccluded
- && !mUserFingerprintAuthenticated.get(getCurrentUser(), false)
+ && !(fingerprint != null && fingerprint.mAuthenticated)
&& !mUserHasTrust.get(getCurrentUser(), false);
}
private boolean shouldListenForFaceAssistant() {
+ BiometricAuthenticated face = mUserFaceAuthenticated.get(getCurrentUser());
return mAssistantVisible && mKeyguardOccluded
- && !mUserFaceAuthenticated.get(getCurrentUser(), false)
+ && !(face != null && face.mAuthenticated)
&& !mUserHasTrust.get(getCurrentUser(), false);
}
@@ -1817,7 +1858,7 @@
public void onLockIconPressed() {
mLockIconPressed = true;
final int userId = getCurrentUser();
- mUserFaceAuthenticated.put(userId, false);
+ mUserFaceAuthenticated.put(userId, null);
updateFaceListeningState();
mStrongAuthTracker.onStrongAuthRequiredChanged(userId);
}
@@ -2691,9 +2732,11 @@
if (mFpm != null && mFpm.isHardwareDetected()) {
final int userId = ActivityManager.getCurrentUser();
final int strongAuthFlags = mStrongAuthTracker.getStrongAuthForUser(userId);
+ BiometricAuthenticated fingerprint = mUserFingerprintAuthenticated.get(userId);
pw.println(" Fingerprint state (user=" + userId + ")");
- pw.println(" allowed=" + isUnlockingWithBiometricAllowed());
- pw.println(" auth'd=" + mUserFingerprintAuthenticated.get(userId));
+ pw.println(" allowed=" + fingerprint != null
+ && isUnlockingWithBiometricAllowed(fingerprint.mIsStrongBiometric));
+ pw.println(" auth'd=" + fingerprint != null && fingerprint.mAuthenticated);
pw.println(" authSinceBoot="
+ getStrongAuthTracker().hasUserAuthenticatedSinceBoot());
pw.println(" disabled(DPM)=" + isFingerprintDisabled(userId));
@@ -2706,9 +2749,11 @@
if (mFaceManager != null && mFaceManager.isHardwareDetected()) {
final int userId = ActivityManager.getCurrentUser();
final int strongAuthFlags = mStrongAuthTracker.getStrongAuthForUser(userId);
+ BiometricAuthenticated face = mUserFaceAuthenticated.get(userId);
pw.println(" Face authentication state (user=" + userId + ")");
- pw.println(" allowed=" + isUnlockingWithBiometricAllowed());
- pw.println(" auth'd=" + mUserFaceAuthenticated.get(userId));
+ pw.println(" allowed=" + face != null
+ && isUnlockingWithBiometricAllowed(face.mIsStrongBiometric));
+ pw.println(" auth'd=" + face != null && face.mAuthenticated);
pw.println(" authSinceBoot="
+ getStrongAuthTracker().hasUserAuthenticatedSinceBoot());
pw.println(" disabled(DPM)=" + isFaceDisabled(userId));
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index 49f72a9..12e0ecd 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -239,7 +239,8 @@
* @param userId the user id for which the biometric sample was authenticated
* @param biometricSourceType
*/
- public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) { }
+ public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType,
+ boolean isStrongBiometric) { }
/**
* Called when biometric authentication provides help string (e.g. "Try again")
diff --git a/packages/SystemUI/src/com/android/systemui/LatencyTester.java b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
index 1a47dac..dc0cb03 100644
--- a/packages/SystemUI/src/com/android/systemui/LatencyTester.java
+++ b/packages/SystemUI/src/com/android/systemui/LatencyTester.java
@@ -96,6 +96,7 @@
private void fakeWakeAndUnlock() {
mBiometricUnlockController.onBiometricAcquired(BiometricSourceType.FINGERPRINT);
mBiometricUnlockController.onBiometricAuthenticated(
- KeyguardUpdateMonitor.getCurrentUser(), BiometricSourceType.FINGERPRINT);
+ KeyguardUpdateMonitor.getCurrentUser(), BiometricSourceType.FINGERPRINT,
+ true /* isStrongBiometric */);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
index 6a64c83..f719cc6 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingManagerImpl.java
@@ -130,7 +130,8 @@
new KeyguardUpdateMonitorCallback() {
@Override
public void onBiometricAuthenticated(int userId,
- BiometricSourceType biometricSourceType) {
+ BiometricSourceType biometricSourceType,
+ boolean isStrongBiometric) {
if (userId == KeyguardUpdateMonitor.getCurrentUser()
&& biometricSourceType == BiometricSourceType.FACE) {
mJustUnlockedWithFace = true;
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
index 2f3e336..a084ae6 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/brightline/BrightLineFalsingManager.java
@@ -78,7 +78,8 @@
new KeyguardUpdateMonitorCallback() {
@Override
public void onBiometricAuthenticated(int userId,
- BiometricSourceType biometricSourceType) {
+ BiometricSourceType biometricSourceType,
+ boolean isStrongBiometric) {
if (userId == KeyguardUpdateMonitor.getCurrentUser()
&& biometricSourceType == BiometricSourceType.FACE) {
mJustUnlockedWithFace = true;
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index b1db7df..9577121 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -22,6 +22,7 @@
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_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_NON_STRONG_BIOMETRICS_TIMEOUT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE;
import static com.android.systemui.DejankUtils.whitelistIpcs;
@@ -537,7 +538,8 @@
}
@Override
- public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) {
+ public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType,
+ boolean isStrongBiometric) {
if (mLockPatternUtils.isSecure(userId)) {
mLockPatternUtils.getDevicePolicyManager().reportSuccessfulBiometricAttempt(
userId);
@@ -673,6 +675,9 @@
return KeyguardSecurityView.PROMPT_REASON_AFTER_LOCKOUT;
} else if (any && (strongAuth & STRONG_AUTH_REQUIRED_FOR_UNATTENDED_UPDATE) != 0) {
return KeyguardSecurityView.PROMPT_REASON_PREPARE_FOR_UPDATE;
+ } else if (any && (strongAuth
+ & STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT) != 0) {
+ return KeyguardSecurityView.PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT;
}
return KeyguardSecurityView.PROMPT_REASON_NONE;
}
@@ -1839,6 +1844,13 @@
mShowKeyguardWakeLock.release();
}
mKeyguardDisplayManager.show();
+
+ // schedule 4hr idle timeout after which non-strong biometrics (i.e. weak or convenience
+ // biometric) can't be used to unlock device until unlocking with strong biometric or
+ // primary auth (i.e. PIN/pattern/password)
+ mLockPatternUtils.scheduleNonStrongBiometricIdleTimeout(
+ KeyguardUpdateMonitor.getCurrentUser());
+
Trace.endSection();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 7d3d406..4f8e6cf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -645,7 +645,13 @@
@Override
public void onBiometricHelp(int msgId, String helpString,
BiometricSourceType biometricSourceType) {
- if (!mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed()) {
+ // 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 */)) {
return;
}
boolean showSwipeToUnlock =
@@ -705,13 +711,21 @@
private boolean shouldSuppressFingerprintError(int msgId,
KeyguardUpdateMonitor updateMonitor) {
- return ((!updateMonitor.isUnlockingWithBiometricAllowed()
+ // 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 != FingerprintManager.FINGERPRINT_ERROR_LOCKOUT_PERMANENT)
|| msgId == FingerprintManager.FINGERPRINT_ERROR_CANCELED);
}
private boolean shouldSuppressFaceError(int msgId, KeyguardUpdateMonitor updateMonitor) {
- return ((!updateMonitor.isUnlockingWithBiometricAllowed()
+ // 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)
|| msgId == FaceManager.FACE_ERROR_CANCELED);
}
@@ -745,8 +759,9 @@
}
@Override
- public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) {
- super.onBiometricAuthenticated(userId, biometricSourceType);
+ public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType,
+ boolean isStrongBiometric) {
+ super.onBiometricAuthenticated(userId, biometricSourceType, isStrongBiometric);
mHandler.sendEmptyMessage(MSG_HIDE_TRANSIENT);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 691e1c4..1dde5c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -150,14 +150,26 @@
private KeyguardViewMediator mKeyguardViewMediator;
private ScrimController mScrimController;
private StatusBar mStatusBar;
- private int mPendingAuthenticatedUserId = -1;
- private BiometricSourceType mPendingAuthenticatedBioSourceType = null;
+ private PendingAuthenticated mPendingAuthenticated = null;
private boolean mPendingShowBouncer;
private boolean mHasScreenTurnedOnSinceAuthenticating;
private boolean mFadedAwayAfterWakeAndUnlock;
private final MetricsLogger mMetricsLogger;
+ private static final class PendingAuthenticated {
+ public final int userId;
+ public final BiometricSourceType biometricSourceType;
+ public final boolean isStrongBiometric;
+
+ PendingAuthenticated(int userId, BiometricSourceType biometricSourceType,
+ boolean isStrongBiometric) {
+ this.userId = userId;
+ this.biometricSourceType = biometricSourceType;
+ this.isStrongBiometric = isStrongBiometric;
+ }
+ }
+
@Inject
public BiometricUnlockController(Context context, DozeScrimController dozeScrimController,
KeyguardViewMediator keyguardViewMediator, ScrimController scrimController,
@@ -251,28 +263,30 @@
}
@Override
- public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) {
+ public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType,
+ boolean isStrongBiometric) {
Trace.beginSection("BiometricUnlockController#onBiometricAuthenticated");
if (mUpdateMonitor.isGoingToSleep()) {
- mPendingAuthenticatedUserId = userId;
- mPendingAuthenticatedBioSourceType = biometricSourceType;
+ mPendingAuthenticated = new PendingAuthenticated(userId, biometricSourceType,
+ isStrongBiometric);
Trace.endSection();
return;
}
mMetricsLogger.write(new LogMaker(MetricsEvent.BIOMETRIC_AUTH)
.setType(MetricsEvent.TYPE_SUCCESS).setSubtype(toSubtype(biometricSourceType)));
boolean unlockAllowed = mKeyguardBypassController.onBiometricAuthenticated(
- biometricSourceType);
+ biometricSourceType, isStrongBiometric);
if (unlockAllowed) {
mKeyguardViewMediator.userActivity();
- startWakeAndUnlock(biometricSourceType);
+ startWakeAndUnlock(biometricSourceType, isStrongBiometric);
} else {
Log.d(TAG, "onBiometricAuthenticated aborted by bypass controller");
}
}
- public void startWakeAndUnlock(BiometricSourceType biometricSourceType) {
- startWakeAndUnlock(calculateMode(biometricSourceType));
+ public void startWakeAndUnlock(BiometricSourceType biometricSourceType,
+ boolean isStrongBiometric) {
+ startWakeAndUnlock(calculateMode(biometricSourceType, isStrongBiometric));
}
public void startWakeAndUnlock(@WakeAndUnlockMode int mode) {
@@ -373,45 +387,46 @@
public void onStartedGoingToSleep(int why) {
resetMode();
mFadedAwayAfterWakeAndUnlock = false;
- mPendingAuthenticatedUserId = -1;
- mPendingAuthenticatedBioSourceType = null;
+ mPendingAuthenticated = null;
}
@Override
public void onFinishedGoingToSleep(int why) {
Trace.beginSection("BiometricUnlockController#onFinishedGoingToSleep");
- BiometricSourceType pendingType = mPendingAuthenticatedBioSourceType;
- int pendingUserId = mPendingAuthenticatedUserId;
- if (pendingUserId != -1 && pendingType != null) {
+ if (mPendingAuthenticated != null) {
// Post this to make sure it's executed after the device is fully locked.
- mHandler.post(() -> onBiometricAuthenticated(pendingUserId, pendingType));
+ mHandler.post(() -> onBiometricAuthenticated(mPendingAuthenticated.userId,
+ mPendingAuthenticated.biometricSourceType,
+ mPendingAuthenticated.isStrongBiometric));
+ mPendingAuthenticated = null;
}
- mPendingAuthenticatedUserId = -1;
- mPendingAuthenticatedBioSourceType = null;
Trace.endSection();
}
public boolean hasPendingAuthentication() {
- return mPendingAuthenticatedUserId != -1
- && mUpdateMonitor.isUnlockingWithBiometricAllowed()
- && mPendingAuthenticatedUserId == KeyguardUpdateMonitor.getCurrentUser();
+ return mPendingAuthenticated != null
+ && mUpdateMonitor
+ .isUnlockingWithBiometricAllowed(mPendingAuthenticated.isStrongBiometric)
+ && mPendingAuthenticated.userId == KeyguardUpdateMonitor.getCurrentUser();
}
public int getMode() {
return mMode;
}
- private @WakeAndUnlockMode int calculateMode(BiometricSourceType biometricSourceType) {
+ private @WakeAndUnlockMode int calculateMode(BiometricSourceType biometricSourceType,
+ boolean isStrongBiometric) {
if (biometricSourceType == BiometricSourceType.FACE
|| biometricSourceType == BiometricSourceType.IRIS) {
- return calculateModeForPassiveAuth();
+ return calculateModeForPassiveAuth(isStrongBiometric);
} else {
- return calculateModeForFingerprint();
+ return calculateModeForFingerprint(isStrongBiometric);
}
}
- private @WakeAndUnlockMode int calculateModeForFingerprint() {
- boolean unlockingAllowed = mUpdateMonitor.isUnlockingWithBiometricAllowed();
+ private @WakeAndUnlockMode int calculateModeForFingerprint(boolean isStrongBiometric) {
+ boolean unlockingAllowed =
+ mUpdateMonitor.isUnlockingWithBiometricAllowed(isStrongBiometric);
boolean deviceDreaming = mUpdateMonitor.isDreaming();
if (!mUpdateMonitor.isDeviceInteractive()) {
@@ -440,8 +455,9 @@
return MODE_NONE;
}
- private @WakeAndUnlockMode int calculateModeForPassiveAuth() {
- boolean unlockingAllowed = mUpdateMonitor.isUnlockingWithBiometricAllowed();
+ private @WakeAndUnlockMode int calculateModeForPassiveAuth(boolean isStrongBiometric) {
+ boolean unlockingAllowed =
+ mUpdateMonitor.isUnlockingWithBiometricAllowed(isStrongBiometric);
boolean deviceDreaming = mUpdateMonitor.isDreaming();
boolean bypass = mKeyguardBypassController.getBypassEnabled();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
index b4d0d47..03918e0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBypassController.kt
@@ -38,11 +38,20 @@
private val mKeyguardStateController: KeyguardStateController
private val statusBarStateController: StatusBarStateController
private var hasFaceFeature: Boolean
+ private var pendingUnlock: PendingUnlock? = null
/**
+ * Pending unlock info:
+ *
* The pending unlock type which is set if the bypass was blocked when it happened.
+ *
+ * Whether the pending unlock type is strong biometric or non-strong biometric
+ * (i.e. weak or convenience).
*/
- private var pendingUnlockType: BiometricSourceType? = null
+ private data class PendingUnlock(
+ val pendingUnlockType: BiometricSourceType,
+ val isStrongBiometric: Boolean
+ )
lateinit var unlockController: BiometricUnlockController
var isPulseExpanding = false
@@ -86,7 +95,7 @@
statusBarStateController.addCallback(object : StatusBarStateController.StateListener {
override fun onStateChanged(newState: Int) {
if (newState != StatusBarState.KEYGUARD) {
- pendingUnlockType = null
+ pendingUnlock = null
}
}
})
@@ -101,7 +110,7 @@
lockscreenUserManager.addUserChangedListener(
object : NotificationLockscreenUserManager.UserChangedListener {
override fun onUserChanged(userId: Int) {
- pendingUnlockType = null
+ pendingUnlock = null
}
})
}
@@ -111,11 +120,14 @@
*
* @return false if we can not wake and unlock right now
*/
- fun onBiometricAuthenticated(biometricSourceType: BiometricSourceType): Boolean {
+ fun onBiometricAuthenticated(
+ biometricSourceType: BiometricSourceType,
+ isStrongBiometric: Boolean
+ ): Boolean {
if (bypassEnabled) {
val can = canBypass()
if (!can && (isPulseExpanding || qSExpanded)) {
- pendingUnlockType = biometricSourceType
+ pendingUnlock = PendingUnlock(biometricSourceType, isStrongBiometric)
}
return can
}
@@ -123,10 +135,12 @@
}
fun maybePerformPendingUnlock() {
- if (pendingUnlockType != null) {
- if (onBiometricAuthenticated(pendingUnlockType!!)) {
- unlockController.startWakeAndUnlock(pendingUnlockType)
- pendingUnlockType = null
+ if (pendingUnlock != null) {
+ if (onBiometricAuthenticated(pendingUnlock!!.pendingUnlockType,
+ pendingUnlock!!.isStrongBiometric)) {
+ unlockController.startWakeAndUnlock(pendingUnlock!!.pendingUnlockType,
+ pendingUnlock!!.isStrongBiometric)
+ pendingUnlock = null
}
}
}
@@ -162,12 +176,17 @@
}
fun onStartedGoingToSleep() {
- pendingUnlockType = null
+ pendingUnlock = null
}
override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
pw.println("KeyguardBypassController:")
- pw.println(" pendingUnlockType: $pendingUnlockType")
+ if (pendingUnlock != null) {
+ pw.println(" mPendingUnlock.pendingUnlockType: ${pendingUnlock!!.pendingUnlockType}")
+ pw.println(" mPendingUnlock.isStrongBiometric: ${pendingUnlock!!.isStrongBiometric}")
+ } else {
+ pw.println(" mPendingUnlock: $pendingUnlock")
+ }
pw.println(" bypassEnabled: $bypassEnabled")
pw.println(" canBypass: ${canBypass()}")
pw.println(" bouncerShowing: $bouncerShowing")
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
index 60589843..61cef68 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockIcon.java
@@ -387,7 +387,12 @@
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
boolean fingerprintRunning = mKeyguardUpdateMonitor.isFingerprintDetectionRunning();
- boolean unlockingAllowed = mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed();
+ // 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
+ boolean unlockingAllowed = mKeyguardUpdateMonitor
+ .isUnlockingWithBiometricAllowed(true /* isStrongBiometric */);
if (fingerprintRunning && unlockingAllowed) {
AccessibilityNodeInfo.AccessibilityAction unlock
= new AccessibilityNodeInfo.AccessibilityAction(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index fb7976f..c61d7bb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -202,8 +202,10 @@
@Override
public void onBiometricAuthenticated(int userId,
- BiometricSourceType biometricSourceType) {
- if (mFirstBypassAttempt && mUpdateMonitor.isUnlockingWithBiometricAllowed()) {
+ BiometricSourceType biometricSourceType,
+ boolean isStrongBiometric) {
+ if (mFirstBypassAttempt
+ && mUpdateMonitor.isUnlockingWithBiometricAllowed(isStrongBiometric)) {
mDelayShowingKeyguardStatusBar = true;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
index 0ab08a8..a7f60d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardStateControllerImpl.java
@@ -293,13 +293,12 @@
}
@Override
- public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType) {
+ public void onBiometricAuthenticated(int userId, BiometricSourceType biometricSourceType,
+ boolean isStrongBiometric) {
Trace.beginSection("KeyguardUpdateMonitorCallback#onBiometricAuthenticated");
- if (!mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed()) {
- Trace.endSection();
- return;
+ if (mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(isStrongBiometric)) {
+ update(false /* updateAlways */);
}
- update(false /* updateAlways */);
Trace.endSection();
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index befe3e1..6a093963 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -22,6 +22,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
@@ -75,6 +76,7 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
@SmallTest
@@ -117,6 +119,8 @@
private SubscriptionManager mSubscriptionManager;
@Mock
private BroadcastDispatcher mBroadcastDispatcher;
+ @Mock
+ private Executor mBackgroundExecutor;
private TestableLooper mTestableLooper;
private TestableKeyguardUpdateMonitor mKeyguardUpdateMonitor;
@@ -137,7 +141,9 @@
when(mFaceManager.hasEnrolledTemplates(anyInt())).thenReturn(true);
when(mUserManager.isUserUnlocked(anyInt())).thenReturn(true);
when(mUserManager.isPrimaryUser()).thenReturn(true);
- when(mStrongAuthTracker.isUnlockingWithBiometricAllowed()).thenReturn(true);
+ when(mStrongAuthTracker
+ .isUnlockingWithBiometricAllowed(anyBoolean() /* isStrongBiometric */))
+ .thenReturn(true);
context.addMockSystemService(TrustManager.class, mTrustManager);
context.addMockSystemService(FingerprintManager.class, mFingerprintManager);
context.addMockSystemService(BiometricManager.class, mBiometricManager);
@@ -450,7 +456,10 @@
@Test
public void testOnFaceAuthenticated_skipsFaceWhenAuthenticated() {
- mKeyguardUpdateMonitor.onFaceAuthenticated(KeyguardUpdateMonitor.getCurrentUser());
+ // test whether face will be skipped if authenticated, so the value of isStrongBiometric
+ // doesn't matter here
+ mKeyguardUpdateMonitor.onFaceAuthenticated(KeyguardUpdateMonitor.getCurrentUser(),
+ true /* isStrongBiometric */);
mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(true);
mTestableLooper.processAllMessages();
@@ -460,18 +469,36 @@
@Test
public void testGetUserCanSkipBouncer_whenFace() {
int user = KeyguardUpdateMonitor.getCurrentUser();
- mKeyguardUpdateMonitor.onFaceAuthenticated(user);
+ mKeyguardUpdateMonitor.onFaceAuthenticated(user, true /* isStrongBiometric */);
assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isTrue();
}
@Test
+ public void testGetUserCanSkipBouncer_whenFace_nonStrongAndDisallowed() {
+ when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(false /* isStrongBiometric */))
+ .thenReturn(false);
+ int user = KeyguardUpdateMonitor.getCurrentUser();
+ mKeyguardUpdateMonitor.onFaceAuthenticated(user, false /* isStrongBiometric */);
+ assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isFalse();
+ }
+
+ @Test
public void testGetUserCanSkipBouncer_whenFingerprint() {
int user = KeyguardUpdateMonitor.getCurrentUser();
- mKeyguardUpdateMonitor.onFingerprintAuthenticated(user);
+ mKeyguardUpdateMonitor.onFingerprintAuthenticated(user, true /* isStrongBiometric */);
assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isTrue();
}
@Test
+ public void testGetUserCanSkipBouncer_whenFingerprint_nonStrongAndDisallowed() {
+ when(mStrongAuthTracker.isUnlockingWithBiometricAllowed(false /* isStrongBiometric */))
+ .thenReturn(false);
+ int user = KeyguardUpdateMonitor.getCurrentUser();
+ mKeyguardUpdateMonitor.onFingerprintAuthenticated(user, false /* isStrongBiometric */);
+ assertThat(mKeyguardUpdateMonitor.getUserCanSkipBouncer(user)).isFalse();
+ }
+
+ @Test
public void testGetUserCanSkipBouncer_whenTrust() {
int user = KeyguardUpdateMonitor.getCurrentUser();
mKeyguardUpdateMonitor.onTrustChanged(true /* enabled */, user, 0 /* flags */);
@@ -585,7 +612,7 @@
protected TestableKeyguardUpdateMonitor(Context context) {
super(context,
TestableLooper.get(KeyguardUpdateMonitorTest.this).getLooper(),
- mBroadcastDispatcher, mDumpController);
+ mBroadcastDispatcher, mDumpController, mBackgroundExecutor);
mStrongAuthTracker = KeyguardUpdateMonitorTest.this.mStrongAuthTracker;
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
index 1d4b4be..581d795 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/KeyguardIndicationControllerTest.java
@@ -21,6 +21,7 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
@@ -124,7 +125,7 @@
mContext.addMockSystemService(Context.TRUST_SERVICE, mock(TrustManager.class));
mContext.addMockSystemService(Context.FINGERPRINT_SERVICE, mock(FingerprintManager.class));
- when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true);
+ when(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
when(mKeyguardUpdateMonitor.isScreenOn()).thenReturn(true);
when(mKeyguardUpdateMonitor.isUserUnlocked(anyInt())).thenReturn(true);
when(mIndicationArea.findViewById(R.id.keyguard_indication_text)).thenReturn(mTextView);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index 769b774..813923d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -97,7 +97,8 @@
when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(true);
when(mUpdateMonitor.isDeviceInteractive()).thenReturn(true);
when(mKeyguardStateController.isFaceAuthEnabled()).thenReturn(true);
- when(mKeyguardBypassController.onBiometricAuthenticated(any())).thenReturn(true);
+ when(mKeyguardBypassController.onBiometricAuthenticated(any(), anyBoolean()))
+ .thenReturn(true);
when(mKeyguardBypassController.canPlaySubtleWindowAnimations()).thenReturn(true);
mContext.addMockSystemService(PowerManager.class, mPowerManager);
mDependency.injectTestDependency(NotificationMediaManager.class, mMediaManager);
@@ -112,11 +113,28 @@
@Test
public void onBiometricAuthenticated_whenFingerprintAndBiometricsDisallowed_showBouncer() {
+ when(mUpdateMonitor.isUnlockingWithBiometricAllowed(true /* isStrongBiometric */))
+ .thenReturn(false);
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
- BiometricSourceType.FINGERPRINT);
+ BiometricSourceType.FINGERPRINT, true /* isStrongBiometric */);
verify(mStatusBarKeyguardViewManager).showBouncer(eq(false));
verify(mShadeController).animateCollapsePanels(anyInt(), anyBoolean(), anyBoolean(),
anyFloat());
+ assertThat(mBiometricUnlockController.getMode())
+ .isEqualTo(BiometricUnlockController.MODE_SHOW_BOUNCER);
+ }
+
+ @Test
+ public void onBiometricAuthenticated_whenFingerprint_nonStrongBioDisallowed_showBouncer() {
+ when(mUpdateMonitor.isUnlockingWithBiometricAllowed(false /* isStrongBiometric */))
+ .thenReturn(false);
+ mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
+ BiometricSourceType.FINGERPRINT, false /* isStrongBiometric */);
+ verify(mStatusBarKeyguardViewManager).showBouncer(eq(false));
+ verify(mShadeController).animateCollapsePanels(anyInt(), anyBoolean(), anyBoolean(),
+ anyFloat());
+ assertThat(mBiometricUnlockController.getMode())
+ .isEqualTo(BiometricUnlockController.MODE_SHOW_BOUNCER);
}
@Test
@@ -124,44 +142,60 @@
reset(mUpdateMonitor);
reset(mStatusBarKeyguardViewManager);
when(mStatusBarKeyguardViewManager.isShowing()).thenReturn(true);
- when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true);
+ when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
when(mDozeScrimController.isPulsing()).thenReturn(true);
+ // the value of isStrongBiometric doesn't matter here since we only care about the returned
+ // value of isUnlockingWithBiometricAllowed()
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
- BiometricSourceType.FINGERPRINT);
+ BiometricSourceType.FINGERPRINT, true /* isStrongBiometric */);
verify(mKeyguardViewMediator).onWakeAndUnlocking();
+ assertThat(mBiometricUnlockController.getMode())
+ .isEqualTo(BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING);
}
@Test
public void onBiometricAuthenticated_whenFingerprint_dismissKeyguard() {
- when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true);
+ when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
+ // the value of isStrongBiometric doesn't matter here since we only care about the returned
+ // value of isUnlockingWithBiometricAllowed()
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
- BiometricSourceType.FINGERPRINT);
+ BiometricSourceType.FINGERPRINT, true /* isStrongBiometric */);
verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean());
verify(mShadeController).animateCollapsePanels(anyInt(), anyBoolean(), anyBoolean(),
anyFloat());
+ assertThat(mBiometricUnlockController.getMode())
+ .isEqualTo(BiometricUnlockController.MODE_UNLOCK_COLLAPSING);
}
@Test
public void onBiometricAuthenticated_whenFingerprintOnBouncer_dismissBouncer() {
- when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true);
+ when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
when(mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing()).thenReturn(true);
+ // the value of isStrongBiometric doesn't matter here since we only care about the returned
+ // value of isUnlockingWithBiometricAllowed()
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
- BiometricSourceType.FINGERPRINT);
+ BiometricSourceType.FINGERPRINT, true /* isStrongBiometric */);
verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(eq(false));
+ assertThat(mBiometricUnlockController.getMode())
+ .isEqualTo(BiometricUnlockController.MODE_DISMISS_BOUNCER);
}
@Test
public void onBiometricAuthenticated_whenFace_dontDismissKeyguard() {
- when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true);
+ when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
+ // the value of isStrongBiometric doesn't matter here since we only care about the returned
+ // value of isUnlockingWithBiometricAllowed()
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
- BiometricSourceType.FACE);
+ BiometricSourceType.FACE, true /* isStrongBiometric */);
verify(mShadeController, never()).animateCollapsePanels(anyInt(), anyBoolean(),
anyBoolean(), anyFloat());
verify(mStatusBarKeyguardViewManager, never()).notifyKeyguardAuthenticated(anyBoolean());
+ assertThat(mBiometricUnlockController.getMode())
+ .isEqualTo(BiometricUnlockController.MODE_NONE);
}
@Test
@@ -169,13 +203,17 @@
when(mKeyguardBypassController.getBypassEnabled()).thenReturn(true);
mBiometricUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
- when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true);
+ when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
+ // the value of isStrongBiometric doesn't matter here since we only care about the returned
+ // value of isUnlockingWithBiometricAllowed()
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
- BiometricSourceType.FACE);
+ BiometricSourceType.FACE, true /* isStrongBiometric */);
verify(mShadeController, never()).animateCollapsePanels(anyInt(), anyBoolean(),
anyBoolean(), anyFloat());
verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(eq(false));
+ assertThat(mBiometricUnlockController.getMode())
+ .isEqualTo(BiometricUnlockController.MODE_UNLOCK_FADING);
}
@Test
@@ -184,9 +222,11 @@
when(mKeyguardBypassController.getBypassEnabled()).thenReturn(true);
mBiometricUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
- when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(false);
+ when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
+ // the value of isStrongBiometric doesn't matter here since we only care about the returned
+ // value of isUnlockingWithBiometricAllowed()
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
- BiometricSourceType.FACE);
+ BiometricSourceType.FACE, true /* isStrongBiometric */);
// Wake up before showing the bouncer
verify(mStatusBarKeyguardViewManager, never()).showBouncer(eq(false));
@@ -202,9 +242,11 @@
reset(mUpdateMonitor);
mBiometricUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
- when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(false);
+ when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(false);
+ // the value of isStrongBiometric doesn't matter here since we only care about the returned
+ // value of isUnlockingWithBiometricAllowed()
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
- BiometricSourceType.FACE);
+ BiometricSourceType.FACE, true /* isStrongBiometric */);
verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean());
verify(mShadeController, never()).animateCollapsePanels(anyInt(), anyBoolean(),
@@ -215,23 +257,30 @@
@Test
public void onBiometricAuthenticated_whenFaceOnBouncer_dismissBouncer() {
- when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true);
+ when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
when(mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing()).thenReturn(true);
+ // the value of isStrongBiometric doesn't matter here since we only care about the returned
+ // value of isUnlockingWithBiometricAllowed()
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
- BiometricSourceType.FACE);
+ BiometricSourceType.FACE, true /* isStrongBiometric */);
verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(eq(false));
+ assertThat(mBiometricUnlockController.getMode())
+ .isEqualTo(BiometricUnlockController.MODE_DISMISS_BOUNCER);
}
@Test
public void onBiometricAuthenticated_whenBypassOnBouncer_dismissBouncer() {
reset(mKeyguardBypassController);
- when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true);
+ when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
when(mKeyguardBypassController.getBypassEnabled()).thenReturn(true);
- when(mKeyguardBypassController.onBiometricAuthenticated(any())).thenReturn(true);
+ when(mKeyguardBypassController.onBiometricAuthenticated(any(), anyBoolean()))
+ .thenReturn(true);
when(mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing()).thenReturn(true);
+ // the value of isStrongBiometric doesn't matter here since we only care about the returned
+ // value of isUnlockingWithBiometricAllowed()
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
- BiometricSourceType.FACE);
+ BiometricSourceType.FACE, true /* isStrongBiometric */);
verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(eq(false));
assertThat(mBiometricUnlockController.getMode())
@@ -240,11 +289,13 @@
@Test
public void onBiometricAuthenticated_whenBypassOnBouncer_respectsCanPlaySubtleAnim() {
- when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true);
+ when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
when(mKeyguardBypassController.getBypassEnabled()).thenReturn(true);
when(mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing()).thenReturn(true);
+ // the value of isStrongBiometric doesn't matter here since we only care about the returned
+ // value of isUnlockingWithBiometricAllowed()
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
- BiometricSourceType.FACE);
+ BiometricSourceType.FACE, true /* isStrongBiometric */);
verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(eq(false));
assertThat(mBiometricUnlockController.getMode())
@@ -255,13 +306,17 @@
public void onBiometricAuthenticated_whenFaceAndPulsing_dontDismissKeyguard() {
reset(mUpdateMonitor);
reset(mStatusBarKeyguardViewManager);
- when(mUpdateMonitor.isUnlockingWithBiometricAllowed()).thenReturn(true);
+ when(mUpdateMonitor.isUnlockingWithBiometricAllowed(anyBoolean())).thenReturn(true);
when(mDozeScrimController.isPulsing()).thenReturn(true);
+ // the value of isStrongBiometric doesn't matter here since we only care about the returned
+ // value of isUnlockingWithBiometricAllowed()
mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT,
- BiometricSourceType.FACE);
+ BiometricSourceType.FACE, true /* isStrongBiometric */);
verify(mShadeController, never()).animateCollapsePanels(anyInt(), anyBoolean(),
anyBoolean(), anyFloat());
+ assertThat(mBiometricUnlockController.getMode())
+ .isEqualTo(BiometricUnlockController.MODE_ONLY_WAKE);
}
@Test
@@ -270,8 +325,10 @@
mBiometricUnlockController.onFinishedGoingToSleep(-1);
verify(mHandler, never()).post(any());
+ // the value of isStrongBiometric doesn't matter here since we only care about the returned
+ // value of isUnlockingWithBiometricAllowed()
mBiometricUnlockController.onBiometricAuthenticated(1 /* userId */,
- BiometricSourceType.FACE);
+ BiometricSourceType.FACE, true /* isStrongBiometric */);
mBiometricUnlockController.onFinishedGoingToSleep(-1);
verify(mHandler).post(any());
}
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index 204f072..8ed221d 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -311,6 +311,7 @@
}
authenticator = new FingerprintAuthenticator(fingerprintService);
+ fingerprintService.initConfiguredStrength(config.mStrength);
break;
case TYPE_FACE:
@@ -322,6 +323,7 @@
}
authenticator = new FaceAuthenticator(faceService);
+ faceService.initConfiguredStrength(config.mStrength);
break;
case TYPE_IRIS:
@@ -333,6 +335,7 @@
}
authenticator = new IrisAuthenticator(irisService);
+ irisService.initConfiguredStrength(config.mStrength);
break;
default:
diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
index 0e70994..74c70df 100644
--- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
+++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java
@@ -31,6 +31,7 @@
import android.content.pm.UserInfo;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.IBiometricNativeHandle;
import android.hardware.biometrics.IBiometricService;
@@ -106,6 +107,7 @@
private PerformanceStats mPerformanceStats;
protected int mCurrentUserId = UserHandle.USER_NULL;
protected long mHalDeviceId;
+ private int mOEMStrength; // Tracks the OEM configured biometric modality strength
// Tracks if the current authentication makes use of CryptoObjects.
protected boolean mIsCrypto;
// Normal authentications are tracked by mPerformanceMap.
@@ -681,6 +683,20 @@
statsModality(), BiometricsProtoEnums.ISSUE_HAL_DEATH);
}
+ protected void initConfiguredStrengthInternal(int strength) {
+ if (DEBUG) {
+ Slog.d(getTag(), "initConfiguredStrengthInternal(" + strength + ")");
+ }
+ mOEMStrength = strength;
+ }
+
+ protected boolean isStrongBiometric() {
+ // TODO(b/141025588): need to calculate actual strength when downgrading tiers
+ final int biometricBits = mOEMStrength
+ & BiometricManager.Authenticators.BIOMETRIC_MIN_STRENGTH;
+ return biometricBits == BiometricManager.Authenticators.BIOMETRIC_STRONG;
+ }
+
protected ClientMonitor getCurrentClient() {
return mCurrentClient;
}
diff --git a/services/core/java/com/android/server/biometrics/face/FaceService.java b/services/core/java/com/android/server/biometrics/face/FaceService.java
index 31c3d4d..a87a455 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -740,6 +740,12 @@
}
return 0;
}
+
+ @Override // Binder call
+ public void initConfiguredStrength(int strength) {
+ checkPermission(USE_BIOMETRIC_INTERNAL);
+ initConfiguredStrengthInternal(strength);
+ }
}
/**
@@ -809,7 +815,7 @@
if (mFaceServiceReceiver != null) {
if (biometric == null || biometric instanceof Face) {
mFaceServiceReceiver.onAuthenticationSucceeded(deviceId, (Face) biometric,
- userId);
+ userId, isStrongBiometric());
} else {
Slog.e(TAG, "onAuthenticationSucceeded received non-face biometric");
}
diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
index 0a61988..83aa9cf 100644
--- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
@@ -21,6 +21,7 @@
import static android.Manifest.permission.MANAGE_FINGERPRINT;
import static android.Manifest.permission.RESET_FINGERPRINT_LOCKOUT;
import static android.Manifest.permission.USE_BIOMETRIC;
+import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.Manifest.permission.USE_FINGERPRINT;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
@@ -462,6 +463,12 @@
checkPermission(MANAGE_FINGERPRINT);
mClientActiveCallbacks.remove(callback);
}
+
+ @Override // Binder call
+ public void initConfiguredStrength(int strength) {
+ checkPermission(USE_BIOMETRIC_INTERNAL);
+ initConfiguredStrengthInternal(strength);
+ }
}
/**
@@ -526,8 +533,8 @@
throws RemoteException {
if (mFingerprintServiceReceiver != null) {
if (biometric == null || biometric instanceof Fingerprint) {
- mFingerprintServiceReceiver
- .onAuthenticationSucceeded(deviceId, (Fingerprint) biometric, userId);
+ mFingerprintServiceReceiver.onAuthenticationSucceeded(deviceId,
+ (Fingerprint) biometric, userId, isStrongBiometric());
} else {
Slog.e(TAG, "onAuthenticationSucceeded received non-fingerprint biometric");
}
diff --git a/services/core/java/com/android/server/biometrics/iris/IrisService.java b/services/core/java/com/android/server/biometrics/iris/IrisService.java
index 2817315..903ae6b 100644
--- a/services/core/java/com/android/server/biometrics/iris/IrisService.java
+++ b/services/core/java/com/android/server/biometrics/iris/IrisService.java
@@ -16,9 +16,12 @@
package com.android.server.biometrics.iris;
+import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
+
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricsProtoEnums;
+import android.hardware.iris.IIrisService;
import com.android.server.biometrics.AuthenticationClient;
import com.android.server.biometrics.BiometricServiceBase;
@@ -42,6 +45,17 @@
private static final String TAG = "IrisService";
/**
+ * Receives the incoming binder calls from IrisManager.
+ */
+ private final class IrisServiceWrapper extends IIrisService.Stub {
+ @Override // Binder call
+ public void initConfiguredStrength(int strength) {
+ checkPermission(USE_BIOMETRIC_INTERNAL);
+ initConfiguredStrengthInternal(strength);
+ }
+ }
+
+ /**
* Initializes the system service.
* <p>
* Subclasses must define a single argument constructor that accepts the context
@@ -57,6 +71,7 @@
@Override
public void onStart() {
super.onStart();
+ publishBinderService(Context.IRIS_SERVICE, new IrisServiceWrapper());
}
@Override
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index 1f4048f..15dfab9 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -17,6 +17,7 @@
package com.android.server.locksettings;
import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE;
+import static android.Manifest.permission.MANAGE_BIOMETRIC;
import static android.Manifest.permission.READ_CONTACTS;
import static android.content.Context.KEYGUARD_SERVICE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
@@ -178,6 +179,7 @@
public class LockSettingsService extends ILockSettings.Stub {
private static final String TAG = "LockSettingsService";
private static final String PERMISSION = ACCESS_KEYGUARD_SECURE_STORAGE;
+ private static final String BIOMETRIC_PERMISSION = MANAGE_BIOMETRIC;
private static final boolean DEBUG = false;
private static final int PROFILE_KEY_IV_SIZE = 12;
@@ -1050,6 +1052,10 @@
}
}
+ private final void checkBiometricPermission() {
+ mContext.enforceCallingOrSelfPermission(BIOMETRIC_PERMISSION, "LockSettingsBiometric");
+ }
+
@Override
public boolean hasSecureLockScreen() {
return mHasSecureLockScreen;
@@ -2304,6 +2310,18 @@
}
@Override
+ public void reportSuccessfulBiometricUnlock(boolean isStrongBiometric, int userId) {
+ checkBiometricPermission();
+ mStrongAuth.reportSuccessfulBiometricUnlock(isStrongBiometric, userId);
+ }
+
+ @Override
+ public void scheduleNonStrongBiometricIdleTimeout(int userId) {
+ checkBiometricPermission();
+ mStrongAuth.scheduleNonStrongBiometricIdleTimeout(userId);
+ }
+
+ @Override
public void userPresent(int userId) {
checkWritePermission(userId);
mStrongAuth.reportUnlock(userId);
@@ -3191,6 +3209,12 @@
mStorage.dump(pw);
pw.println();
pw.decreaseIndent();
+
+ pw.println("StrongAuth:");
+ pw.increaseIndent();
+ mStrongAuth.dump(pw);
+ pw.println();
+ pw.decreaseIndent();
}
/**
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java b/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java
index 91cf53e..fbee6f4 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStrongAuth.java
@@ -17,6 +17,7 @@
package com.android.server.locksettings;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT;
import android.app.AlarmManager;
@@ -32,8 +33,10 @@
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.Slog;
+import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker;
/**
@@ -42,6 +45,7 @@
public class LockSettingsStrongAuth {
private static final String TAG = "LockSettings";
+ private static final boolean DEBUG = false;
private static final int MSG_REQUIRE_STRONG_AUTH = 1;
private static final int MSG_REGISTER_TRACKER = 2;
@@ -49,15 +53,40 @@
private static final int MSG_REMOVE_USER = 4;
private static final int MSG_SCHEDULE_STRONG_AUTH_TIMEOUT = 5;
private static final int MSG_NO_LONGER_REQUIRE_STRONG_AUTH = 6;
+ private static final int MSG_SCHEDULE_NON_STRONG_BIOMETRIC_TIMEOUT = 7;
+ private static final int MSG_STRONG_BIOMETRIC_UNLOCK = 8;
+ private static final int MSG_SCHEDULE_NON_STRONG_BIOMETRIC_IDLE_TIMEOUT = 9;
private static final String STRONG_AUTH_TIMEOUT_ALARM_TAG =
"LockSettingsStrongAuth.timeoutForUser";
+ private static final String NON_STRONG_BIOMETRIC_TIMEOUT_ALARM_TAG =
+ "LockSettingsPrimaryAuth.nonStrongBiometricTimeoutForUser";
+ private static final String NON_STRONG_BIOMETRIC_IDLE_TIMEOUT_ALARM_TAG =
+ "LockSettingsPrimaryAuth.nonStrongBiometricIdleTimeoutForUser";
+
+ /**
+ * Default and maximum timeout in milliseconds after which unlocking with weak auth times out,
+ * i.e. the user has to use a strong authentication method like password, PIN or pattern.
+ */
+ public static final long DEFAULT_NON_STRONG_BIOMETRIC_TIMEOUT_MS = 24 * 60 * 60 * 1000; // 24h
+ public static final long DEFAULT_NON_STRONG_BIOMETRIC_IDLE_TIMEOUT_MS =
+ 4 * 60 * 60 * 1000; // 4h
private final RemoteCallbackList<IStrongAuthTracker> mTrackers = new RemoteCallbackList<>();
private final SparseIntArray mStrongAuthForUser = new SparseIntArray();
+ private final SparseBooleanArray mIsNonStrongBiometricAllowedForUser = new SparseBooleanArray();
private final ArrayMap<Integer, StrongAuthTimeoutAlarmListener>
mStrongAuthTimeoutAlarmListenerForUser = new ArrayMap<>();
+ // Track non-strong biometric timeout
+ private final ArrayMap<Integer, NonStrongBiometricTimeoutAlarmListener>
+ mNonStrongBiometricTimeoutAlarmListener = new ArrayMap<>();
+ // Track non-strong biometric idle timeout
+ private final ArrayMap<Integer, NonStrongBiometricIdleTimeoutAlarmListener>
+ mNonStrongBiometricIdleTimeoutAlarmListener = new ArrayMap<>();
+
private final int mDefaultStrongAuthFlags;
+ private final boolean mDefaultIsNonStrongBiometricAllowed = true;
+
private final Context mContext;
private AlarmManager mAlarmManager;
@@ -80,6 +109,17 @@
Slog.e(TAG, "Exception while adding StrongAuthTracker.", e);
}
}
+
+ for (int i = 0; i < mIsNonStrongBiometricAllowedForUser.size(); i++) {
+ int key = mIsNonStrongBiometricAllowedForUser.keyAt(i);
+ boolean value = mIsNonStrongBiometricAllowedForUser.valueAt(i);
+ try {
+ tracker.onIsNonStrongBiometricAllowedChanged(value, key);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Exception while adding StrongAuthTracker: "
+ + "IsNonStrongBiometricAllowedChanged.", e);
+ }
+ }
}
private void handleRemoveStrongAuthTracker(IStrongAuthTracker tracker) {
@@ -134,6 +174,13 @@
mStrongAuthForUser.removeAt(index);
notifyStrongAuthTrackers(mDefaultStrongAuthFlags, userId);
}
+
+ index = mIsNonStrongBiometricAllowedForUser.indexOfKey(userId);
+ if (index >= 0) {
+ mIsNonStrongBiometricAllowedForUser.removeAt(index);
+ notifyStrongAuthTrackersForIsNonStrongBiometricAllowed(
+ mDefaultIsNonStrongBiometricAllowed, userId);
+ }
}
private void handleScheduleStrongAuthTimeout(int userId) {
@@ -151,6 +198,125 @@
// schedule a new alarm listener for the user
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, when, STRONG_AUTH_TIMEOUT_ALARM_TAG,
alarm, mHandler);
+
+ // cancel current non-strong biometric alarm listener for the user (if there was one)
+ cancelNonStrongBiometricAlarmListener(userId);
+ // cancel current non-strong biometric idle alarm listener for the user (if there was one)
+ cancelNonStrongBiometricIdleAlarmListener(userId);
+ // re-allow unlock with non-strong biometrics
+ setIsNonStrongBiometricAllowed(true, userId);
+ }
+
+ private void handleScheduleNonStrongBiometricTimeout(int userId) {
+ if (DEBUG) Slog.d(TAG, "handleScheduleNonStrongBiometricTimeout for userId=" + userId);
+ long when = SystemClock.elapsedRealtime() + DEFAULT_NON_STRONG_BIOMETRIC_TIMEOUT_MS;
+ NonStrongBiometricTimeoutAlarmListener alarm = mNonStrongBiometricTimeoutAlarmListener
+ .get(userId);
+ if (alarm != null) {
+ // Unlock with non-strong biometric will not affect the existing non-strong biometric
+ // timeout alarm
+ if (DEBUG) {
+ Slog.d(TAG, "There is an existing alarm for non-strong biometric"
+ + " fallback timeout, so do not re-schedule");
+ }
+ } else {
+ if (DEBUG) {
+ Slog.d(TAG, "Schedule a new alarm for non-strong biometric fallback timeout");
+ }
+ alarm = new NonStrongBiometricTimeoutAlarmListener(userId);
+ mNonStrongBiometricTimeoutAlarmListener.put(userId, alarm);
+ // schedule a new alarm listener for the user
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, when,
+ NON_STRONG_BIOMETRIC_TIMEOUT_ALARM_TAG, alarm, mHandler);
+ }
+
+ // cancel current non-strong biometric idle alarm listener for the user (if there was one)
+ cancelNonStrongBiometricIdleAlarmListener(userId);
+ }
+
+ private void handleStrongBiometricUnlock(int userId) {
+ if (DEBUG) Slog.d(TAG, "handleStrongBiometricUnlock for userId=" + userId);
+ // cancel current non-strong biometric alarm listener for the user (if there was one)
+ cancelNonStrongBiometricAlarmListener(userId);
+ // cancel current non-strong biometric idle alarm listener for the user (if there was one)
+ cancelNonStrongBiometricIdleAlarmListener(userId);
+ // re-allow unlock with non-strong biometrics
+ setIsNonStrongBiometricAllowed(true, userId);
+ }
+
+ private void cancelNonStrongBiometricAlarmListener(int userId) {
+ if (DEBUG) Slog.d(TAG, "cancelNonStrongBiometricAlarmListener for userId=" + userId);
+ NonStrongBiometricTimeoutAlarmListener alarm = mNonStrongBiometricTimeoutAlarmListener
+ .get(userId);
+ if (alarm != null) {
+ if (DEBUG) Slog.d(TAG, "Cancel alarm for non-strong biometric fallback timeout");
+ mAlarmManager.cancel(alarm);
+ // need to remove the alarm when cancelled by primary auth or strong biometric
+ mNonStrongBiometricTimeoutAlarmListener.remove(userId);
+ }
+ }
+
+ private void cancelNonStrongBiometricIdleAlarmListener(int userId) {
+ if (DEBUG) Slog.d(TAG, "cancelNonStrongBiometricIdleAlarmListener for userId=" + userId);
+ // cancel idle alarm listener by any unlocks (i.e. primary auth, strong biometric,
+ // non-strong biometric)
+ NonStrongBiometricIdleTimeoutAlarmListener alarm =
+ mNonStrongBiometricIdleTimeoutAlarmListener.get(userId);
+ if (alarm != null) {
+ if (DEBUG) Slog.d(TAG, "Cancel alarm for non-strong biometric idle timeout");
+ mAlarmManager.cancel(alarm);
+ }
+ }
+
+ private void setIsNonStrongBiometricAllowed(boolean allowed, int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, "setIsNonStrongBiometricAllowed for allowed=" + allowed
+ + ", userId=" + userId);
+ }
+ if (userId == UserHandle.USER_ALL) {
+ for (int i = 0; i < mIsNonStrongBiometricAllowedForUser.size(); i++) {
+ int key = mIsNonStrongBiometricAllowedForUser.keyAt(i);
+ setIsNonStrongBiometricAllowedOneUser(allowed, key);
+ }
+ } else {
+ setIsNonStrongBiometricAllowedOneUser(allowed, userId);
+ }
+ }
+
+ private void setIsNonStrongBiometricAllowedOneUser(boolean allowed, int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, "setIsNonStrongBiometricAllowedOneUser for allowed=" + allowed
+ + ", userId=" + userId);
+ }
+ boolean oldValue = mIsNonStrongBiometricAllowedForUser.get(userId,
+ mDefaultIsNonStrongBiometricAllowed);
+ if (allowed != oldValue) {
+ if (DEBUG) {
+ Slog.d(TAG, "mIsNonStrongBiometricAllowedForUser value changed:"
+ + " oldValue=" + oldValue + ", allowed=" + allowed);
+ }
+ mIsNonStrongBiometricAllowedForUser.put(userId, allowed);
+ notifyStrongAuthTrackersForIsNonStrongBiometricAllowed(allowed, userId);
+ }
+ }
+
+ private void handleScheduleNonStrongBiometricIdleTimeout(int userId) {
+ if (DEBUG) Slog.d(TAG, "handleScheduleNonStrongBiometricIdleTimeout for userId=" + userId);
+ long when = SystemClock.elapsedRealtime() + DEFAULT_NON_STRONG_BIOMETRIC_IDLE_TIMEOUT_MS;
+ // cancel current alarm listener for the user (if there was one)
+ NonStrongBiometricIdleTimeoutAlarmListener alarm =
+ mNonStrongBiometricIdleTimeoutAlarmListener.get(userId);
+ if (alarm != null) {
+ if (DEBUG) Slog.d(TAG, "Cancel existing alarm for non-strong biometric idle timeout");
+ mAlarmManager.cancel(alarm);
+ } else {
+ alarm = new NonStrongBiometricIdleTimeoutAlarmListener(userId);
+ mNonStrongBiometricIdleTimeoutAlarmListener.put(userId, alarm);
+ }
+ // schedule a new alarm listener for the user
+ if (DEBUG) Slog.d(TAG, "Schedule a new alarm for non-strong biometric idle timeout");
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, when,
+ NON_STRONG_BIOMETRIC_IDLE_TIMEOUT_ALARM_TAG, alarm, mHandler);
}
private void notifyStrongAuthTrackers(int strongAuthReason, int userId) {
@@ -170,6 +336,29 @@
}
}
+ private void notifyStrongAuthTrackersForIsNonStrongBiometricAllowed(boolean allowed,
+ int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, "notifyStrongAuthTrackersForIsNonStrongBiometricAllowed"
+ + " for allowed=" + allowed + ", userId=" + userId);
+ }
+ int i = mTrackers.beginBroadcast();
+ try {
+ while (i > 0) {
+ i--;
+ try {
+ mTrackers.getBroadcastItem(i).onIsNonStrongBiometricAllowedChanged(
+ allowed, userId);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Exception while notifying StrongAuthTracker: "
+ + "IsNonStrongBiometricAllowedChanged.", e);
+ }
+ }
+ } finally {
+ mTrackers.finishBroadcast();
+ }
+ }
+
public void registerStrongAuthTracker(IStrongAuthTracker tracker) {
mHandler.obtainMessage(MSG_REGISTER_TRACKER, tracker).sendToTarget();
}
@@ -207,11 +396,45 @@
requireStrongAuth(STRONG_AUTH_NOT_REQUIRED, userId);
}
+ /**
+ * Report successful unlocking with primary auth
+ */
public void reportSuccessfulStrongAuthUnlock(int userId) {
final int argNotUsed = 0;
mHandler.obtainMessage(MSG_SCHEDULE_STRONG_AUTH_TIMEOUT, userId, argNotUsed).sendToTarget();
}
+ /**
+ * Report successful unlocking with biometric
+ */
+ public void reportSuccessfulBiometricUnlock(boolean isStrongBiometric, int userId) {
+ if (DEBUG) {
+ Slog.d(TAG, "reportSuccessfulBiometricUnlock for isStrongBiometric="
+ + isStrongBiometric + ", userId=" + userId);
+ }
+ final int argNotUsed = 0;
+ if (isStrongBiometric) { // unlock with strong biometric
+ mHandler.obtainMessage(MSG_STRONG_BIOMETRIC_UNLOCK, userId, argNotUsed)
+ .sendToTarget();
+ } else { // unlock with non-strong biometric (i.e. weak or convenience)
+ mHandler.obtainMessage(MSG_SCHEDULE_NON_STRONG_BIOMETRIC_TIMEOUT, userId, argNotUsed)
+ .sendToTarget();
+ }
+ }
+
+ /**
+ * Schedule idle timeout for non-strong biometric (i.e. weak or convenience)
+ */
+ public void scheduleNonStrongBiometricIdleTimeout(int userId) {
+ if (DEBUG) Slog.d(TAG, "scheduleNonStrongBiometricIdleTimeout for userId=" + userId);
+ final int argNotUsed = 0;
+ mHandler.obtainMessage(MSG_SCHEDULE_NON_STRONG_BIOMETRIC_IDLE_TIMEOUT, userId, argNotUsed)
+ .sendToTarget();
+ }
+
+ /**
+ * Alarm of fallback timeout for primary auth
+ */
private class StrongAuthTimeoutAlarmListener implements OnAlarmListener {
private final int mUserId;
@@ -226,6 +449,41 @@
}
}
+ /**
+ * Alarm of fallback timeout for non-strong biometric (i.e. weak or convenience)
+ */
+ private class NonStrongBiometricTimeoutAlarmListener implements OnAlarmListener {
+
+ private final int mUserId;
+
+ NonStrongBiometricTimeoutAlarmListener(int userId) {
+ mUserId = userId;
+ }
+
+ @Override
+ public void onAlarm() {
+ requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_NON_STRONG_BIOMETRICS_TIMEOUT, mUserId);
+ }
+ }
+
+ /**
+ * Alarm of idle timeout for non-strong biometric (i.e. weak or convenience biometric)
+ */
+ private class NonStrongBiometricIdleTimeoutAlarmListener implements OnAlarmListener {
+
+ private final int mUserId;
+
+ NonStrongBiometricIdleTimeoutAlarmListener(int userId) {
+ mUserId = userId;
+ }
+
+ @Override
+ public void onAlarm() {
+ // disallow unlock with non-strong biometrics
+ setIsNonStrongBiometricAllowed(false, mUserId);
+ }
+ }
+
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
@@ -248,8 +506,38 @@
case MSG_NO_LONGER_REQUIRE_STRONG_AUTH:
handleNoLongerRequireStrongAuth(msg.arg1, msg.arg2);
break;
+ case MSG_SCHEDULE_NON_STRONG_BIOMETRIC_TIMEOUT:
+ handleScheduleNonStrongBiometricTimeout(msg.arg1);
+ break;
+ case MSG_STRONG_BIOMETRIC_UNLOCK:
+ handleStrongBiometricUnlock(msg.arg1);
+ break;
+ case MSG_SCHEDULE_NON_STRONG_BIOMETRIC_IDLE_TIMEOUT:
+ handleScheduleNonStrongBiometricIdleTimeout(msg.arg1);
+ break;
}
}
};
+ public void dump(IndentingPrintWriter pw) {
+ pw.println("PrimaryAuthFlags state:");
+ pw.increaseIndent();
+ for (int i = 0; i < mStrongAuthForUser.size(); i++) {
+ final int key = mStrongAuthForUser.keyAt(i);
+ final int value = mStrongAuthForUser.valueAt(i);
+ pw.println("userId=" + key + ", primaryAuthFlags=" + Integer.toHexString(value));
+ }
+ pw.println();
+ pw.decreaseIndent();
+
+ pw.println("NonStrongBiometricAllowed state:");
+ pw.increaseIndent();
+ for (int i = 0; i < mIsNonStrongBiometricAllowedForUser.size(); i++) {
+ final int key = mIsNonStrongBiometricAllowedForUser.keyAt(i);
+ final boolean value = mIsNonStrongBiometricAllowedForUser.valueAt(i);
+ pw.println("userId=" + key + ", allowed=" + value);
+ }
+ pw.println();
+ pw.decreaseIndent();
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
index 1c8b00f..30bb38a 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
@@ -111,6 +111,29 @@
any());
}
+ @Test
+ public void testRegisterAuthenticator_callsInitConfiguredStrength() throws Exception {
+
+ final String[] config = {
+ "0:2:15", // ID0:Fingerprint:Strong
+ "1:4:255", // ID1:Iris:Weak
+ "2:8:4095", // ID2:Face:Convenience
+ };
+
+ when(mInjector.getConfiguration(any())).thenReturn(config);
+
+ mAuthService = new AuthService(mContext, mInjector);
+ mAuthService.onStart();
+
+ final int fingerprintStrength = 15;
+ final int irisStrength = 255;
+ final int faceStrength = 4095;
+
+ verify(mFingerprintService).initConfiguredStrength(eq(fingerprintStrength));
+ verify(mIrisService).initConfiguredStrength(eq(irisStrength));
+ verify(mFaceService).initConfiguredStrength(eq(faceStrength));
+ }
+
// TODO(b/141025588): Check that an exception is thrown when the userId != callingUserId
@Test