Integrate Session Coordinator

Test: atest AuthResultCoordinatorTest AuthSessionCoordinatorTest MultiBiometricLockoutStateTest
Test: Verified auth with strong & convenient biometric.
Test: Verified auth with strong & weak biometric.
Bug: 244355277
Change-Id: I38ea11852fa3052a13d955f1de597d8428878821
This commit is contained in:
Joshua McCloskey
2022-10-03 20:44:23 +00:00
parent 51d34ccac2
commit 5945a4b818
21 changed files with 701 additions and 248 deletions

View File

@@ -212,6 +212,26 @@ public class BiometricManager {
* @see android.security.keystore.KeyGenParameterSpec.Builder
*/
int DEVICE_CREDENTIAL = 1 << 15;
}
/**
* @hide
* returns a string representation of an authenticator type.
*/
@NonNull public static String authenticatorToStr(@Authenticators.Types int authenticatorType) {
switch(authenticatorType) {
case Authenticators.BIOMETRIC_STRONG:
return "BIOMETRIC_STRONG";
case Authenticators.BIOMETRIC_WEAK:
return "BIOMETRIC_WEAK";
case Authenticators.BIOMETRIC_CONVENIENCE:
return "BIOMETRIC_CONVENIENCE";
case Authenticators.DEVICE_CREDENTIAL:
return "DEVICE_CREDENTIAL";
default:
return "Unknown authenticator type: " + authenticatorType;
}
}
/**

View File

@@ -21,11 +21,15 @@ import android.annotation.Nullable;
import android.content.Context;
import android.hardware.biometrics.common.OperationContext;
import com.android.server.biometrics.sensors.AuthSessionCoordinator;
import java.util.function.Consumer;
/**
* Cache for system state not directly related to biometric operations that is used for
* logging or optimizations.
*
* This class is also used to inject dependencies such as {@link AuthSessionCoordinator}
*/
public interface BiometricContext {
/** Gets the context source from the system context. */
@@ -59,4 +63,7 @@ public interface BiometricContext {
/** Unsubscribe from context changes. */
void unsubscribe(@NonNull OperationContext context);
/** Obtains an AuthSessionCoordinator. */
AuthSessionCoordinator getAuthSessionCoordinator();
}

View File

@@ -35,6 +35,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.InstanceId;
import com.android.internal.statusbar.ISessionListener;
import com.android.internal.statusbar.IStatusBarService;
import com.android.server.biometrics.sensors.AuthSessionCoordinator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -59,7 +60,8 @@ final class BiometricContextProvider implements BiometricContext {
sInstance = new BiometricContextProvider(
new AmbientDisplayConfiguration(context),
IStatusBarService.Stub.asInterface(ServiceManager.getServiceOrThrow(
Context.STATUS_BAR_SERVICE)), null /* handler */);
Context.STATUS_BAR_SERVICE)), null /* handler */,
new AuthSessionCoordinator());
} catch (ServiceNotFoundException e) {
throw new IllegalStateException("Failed to find required service", e);
}
@@ -76,13 +78,16 @@ final class BiometricContextProvider implements BiometricContext {
private final Map<Integer, InstanceId> mSession = new ConcurrentHashMap<>();
private final AmbientDisplayConfiguration mAmbientDisplayConfiguration;
private final AuthSessionCoordinator mAuthSessionCoordinator;
private boolean mIsAod = false;
private boolean mIsAwake = false;
@VisibleForTesting
BiometricContextProvider(@NonNull AmbientDisplayConfiguration ambientDisplayConfiguration,
@NonNull IStatusBarService service, @Nullable Handler handler) {
@NonNull IStatusBarService service, @Nullable Handler handler,
AuthSessionCoordinator authSessionCoordinator) {
mAmbientDisplayConfiguration = ambientDisplayConfiguration;
mAuthSessionCoordinator = authSessionCoordinator;
try {
service.setBiometicContextListener(new IBiometricContextListener.Stub() {
@Override
@@ -190,6 +195,11 @@ final class BiometricContextProvider implements BiometricContext {
mSubscribers.remove(context);
}
@Override
public AuthSessionCoordinator getAuthSessionCoordinator() {
return mAuthSessionCoordinator;
}
private void notifySubscribers() {
mSubscribers.forEach((context, consumer) -> {
context.isAod = isAod();

View File

@@ -17,9 +17,11 @@
package com.android.server.biometrics.sensors;
import android.hardware.biometrics.BiometricManager.Authenticators;
import android.util.ArrayMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Collections;
import java.util.Map;
import java.util.function.IntFunction;
/**
* A class that takes in a series of authentication attempts (successes, failures, lockouts)
@@ -30,64 +32,64 @@ import java.util.List;
*/
class AuthResultCoordinator {
/**
* Indicates no change has occurred with this authenticator.
*/
static final int AUTHENTICATOR_DEFAULT = 0;
/**
* Indicated this authenticator has received a lockout.
*/
static final int AUTHENTICATOR_LOCKED = 1 << 0;
/**
* Indicates this authenticator has received a successful unlock.
*/
static final int AUTHENTICATOR_UNLOCKED = 1 << 1;
private static final String TAG = "AuthResultCoordinator";
private final List<AuthResult> mOperations;
private final Map<Integer, Integer> mAuthenticatorState;
AuthResultCoordinator() {
mOperations = new ArrayList<>();
mAuthenticatorState = new ArrayMap<>();
mAuthenticatorState.put(Authenticators.BIOMETRIC_STRONG, AUTHENTICATOR_DEFAULT);
mAuthenticatorState.put(Authenticators.BIOMETRIC_WEAK, AUTHENTICATOR_DEFAULT);
mAuthenticatorState.put(Authenticators.BIOMETRIC_CONVENIENCE, AUTHENTICATOR_DEFAULT);
}
private void updateState(@Authenticators.Types int strength, IntFunction<Integer> mapper) {
switch (strength) {
case Authenticators.BIOMETRIC_STRONG:
mAuthenticatorState.put(Authenticators.BIOMETRIC_STRONG,
mapper.apply(mAuthenticatorState.get(Authenticators.BIOMETRIC_STRONG)));
// fall through
case Authenticators.BIOMETRIC_WEAK:
mAuthenticatorState.put(Authenticators.BIOMETRIC_WEAK,
mapper.apply(mAuthenticatorState.get(Authenticators.BIOMETRIC_WEAK)));
// fall through
case Authenticators.BIOMETRIC_CONVENIENCE:
mAuthenticatorState.put(Authenticators.BIOMETRIC_CONVENIENCE,
mapper.apply(
mAuthenticatorState.get(Authenticators.BIOMETRIC_CONVENIENCE)));
}
}
/**
* Adds auth success for a given strength to the current operation list.
*/
void authenticatedFor(@Authenticators.Types int strength) {
mOperations.add(new AuthResult(AuthResult.AUTHENTICATED, strength));
}
/**
* Adds auth ended for a given strength to the current operation list.
*/
void authEndedFor(@Authenticators.Types int strength) {
mOperations.add(new AuthResult(AuthResult.FAILED, strength));
updateState(strength, (old) -> AUTHENTICATOR_UNLOCKED | old);
}
/**
* Adds a lock out of a given strength to the current operation list.
*/
void lockedOutFor(@Authenticators.Types int strength) {
mOperations.add(new AuthResult(AuthResult.LOCKED_OUT, strength));
updateState(strength, (old) -> AUTHENTICATOR_LOCKED | old);
}
/**
* Obtains an auth result & strength from a current set of biometric operations.
* Returns the current authenticator state. Each authenticator will have
* the associated operations that were performed on them(DEFAULT, LOCKED, UNLOCKED).
*/
AuthResult getResult() {
AuthResult result = new AuthResult(AuthResult.FAILED, Authenticators.BIOMETRIC_CONVENIENCE);
return mOperations.stream().filter(
(element) -> element.getStatus() != AuthResult.FAILED).reduce(result,
((curr, next) -> {
int strengthCompare = curr.getBiometricStrength() - next.getBiometricStrength();
if (strengthCompare < 0) {
return curr;
} else if (strengthCompare == 0) {
// Equal level of strength, favor authentication.
if (curr.getStatus() == AuthResult.AUTHENTICATED) {
return curr;
} else {
// Either next is Authenticated, or it is not, either way return this
// one.
return next;
}
} else {
// curr is a weaker biometric
return next;
}
}));
}
void resetState() {
mOperations.clear();
final Map<Integer, Integer> getResult() {
return Collections.unmodifiableMap(mAuthenticatorState);
}
}

View File

@@ -16,10 +16,22 @@
package com.android.server.biometrics.sensors;
import static com.android.server.biometrics.sensors.AuthResultCoordinator.AUTHENTICATOR_LOCKED;
import static com.android.server.biometrics.sensors.AuthResultCoordinator.AUTHENTICATOR_UNLOCKED;
import android.hardware.biometrics.BiometricManager.Authenticators;
import android.os.SystemClock;
import android.util.Pair;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import java.time.Clock;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
@@ -28,20 +40,31 @@ import java.util.Set;
* This class is not thread-safe. In general, all calls to this class should be made on the same
* handler to ensure no collisions.
*/
class AuthSessionCoordinator implements AuthSessionListener {
public class AuthSessionCoordinator implements AuthSessionListener {
private static final String TAG = "AuthSessionCoordinator";
private final Set<Integer> mAuthOperations;
private final MultiBiometricLockoutState mMultiBiometricLockoutState;
private final List<Pair<Integer, Long>> mTimedLockouts;
private final RingBuffer mRingBuffer;
private final Clock mClock;
private int mUserId;
private boolean mIsAuthenticating;
private AuthResultCoordinator mAuthResultCoordinator;
private MultiBiometricLockoutState mMultiBiometricLockoutState;
AuthSessionCoordinator() {
public AuthSessionCoordinator() {
this(SystemClock.currentNetworkTimeClock());
}
@VisibleForTesting
AuthSessionCoordinator(Clock clock) {
mAuthOperations = new HashSet<>();
mAuthResultCoordinator = new AuthResultCoordinator();
mMultiBiometricLockoutState = new MultiBiometricLockoutState();
mMultiBiometricLockoutState = new MultiBiometricLockoutState(clock);
mRingBuffer = new RingBuffer(100);
mTimedLockouts = new ArrayList<>();
mClock = clock;
}
/**
@@ -51,7 +74,9 @@ class AuthSessionCoordinator implements AuthSessionListener {
mAuthOperations.clear();
mUserId = userId;
mIsAuthenticating = true;
mAuthResultCoordinator.resetState();
mAuthOperations.clear();
mAuthResultCoordinator = new AuthResultCoordinator();
mRingBuffer.addApiCall("internal : onAuthSessionStarted(" + userId + ")");
}
/**
@@ -64,14 +89,27 @@ class AuthSessionCoordinator implements AuthSessionListener {
void endAuthSession() {
if (mIsAuthenticating) {
mAuthOperations.clear();
AuthResult res =
mAuthResultCoordinator.getResult();
if (res.getStatus() == AuthResult.AUTHENTICATED) {
mMultiBiometricLockoutState.onUserUnlocked(mUserId, res.getBiometricStrength());
} else if (res.getStatus() == AuthResult.LOCKED_OUT) {
mMultiBiometricLockoutState.onUserLocked(mUserId, res.getBiometricStrength());
final long currentTime = mClock.millis();
for (Pair<Integer, Long> timedLockouts : mTimedLockouts) {
mMultiBiometricLockoutState.increaseLockoutTime(mUserId, timedLockouts.first,
timedLockouts.second + currentTime);
}
mAuthResultCoordinator.resetState();
// User unlocks can also unlock timed lockout Authenticator.Types
final Map<Integer, Integer> result = mAuthResultCoordinator.getResult();
for (int authenticator : Arrays.asList(Authenticators.BIOMETRIC_CONVENIENCE,
Authenticators.BIOMETRIC_WEAK, Authenticators.BIOMETRIC_STRONG)) {
final Integer value = result.get(authenticator);
if ((value & AUTHENTICATOR_UNLOCKED) == AUTHENTICATOR_UNLOCKED) {
mMultiBiometricLockoutState.setAuthenticatorTo(mUserId, authenticator,
true /* canAuthenticate */);
mMultiBiometricLockoutState.clearLockoutTime(mUserId, authenticator);
} else if ((value & AUTHENTICATOR_LOCKED) == AUTHENTICATOR_LOCKED) {
mMultiBiometricLockoutState.setAuthenticatorTo(mUserId, authenticator,
false /* canAuthenticate */);
}
}
mRingBuffer.addApiCall("internal : onAuthSessionEnded(" + mUserId + ")");
mIsAuthenticating = false;
}
}
@@ -79,12 +117,15 @@ class AuthSessionCoordinator implements AuthSessionListener {
/**
* @return true if a user can authenticate with a given strength.
*/
boolean getCanAuthFor(int userId, @Authenticators.Types int strength) {
public boolean getCanAuthFor(int userId, @Authenticators.Types int strength) {
return mMultiBiometricLockoutState.canUserAuthenticate(userId, strength);
}
@Override
public void authStartedFor(int userId, int sensorId) {
public void authStartedFor(int userId, int sensorId, long requestId) {
mRingBuffer.addApiCall(
"authStartedFor(userId=" + userId + ", sensorId=" + sensorId + ", requestId="
+ requestId + ")");
if (!mIsAuthenticating) {
onAuthSessionStarted(userId);
}
@@ -105,34 +146,58 @@ class AuthSessionCoordinator implements AuthSessionListener {
@Override
public void authenticatedFor(int userId, @Authenticators.Types int biometricStrength,
int sensorId) {
int sensorId, long requestId) {
final String authStr =
"authenticatedFor(userId=" + userId + ", strength=" + biometricStrength
+ " , sensorId=" + sensorId + ", requestId= " + requestId + ")";
mRingBuffer.addApiCall(authStr);
mAuthResultCoordinator.authenticatedFor(biometricStrength);
attemptToFinish(userId, sensorId,
"authenticatedFor(userId=" + userId + ", biometricStrength=" + biometricStrength
+ ", sensorId=" + sensorId + "");
attemptToFinish(userId, sensorId, authStr);
}
@Override
public void lockedOutFor(int userId, @Authenticators.Types int biometricStrength,
int sensorId) {
mAuthResultCoordinator.lockedOutFor(biometricStrength);
attemptToFinish(userId, sensorId,
int sensorId, long requestId) {
final String lockedOutStr =
"lockOutFor(userId=" + userId + ", biometricStrength=" + biometricStrength
+ ", sensorId=" + sensorId + "");
+ ", sensorId=" + sensorId + ", requestId=" + requestId + ")";
mRingBuffer.addApiCall(lockedOutStr);
mAuthResultCoordinator.lockedOutFor(biometricStrength);
attemptToFinish(userId, sensorId, lockedOutStr);
}
@Override
public void lockOutTimed(int userId, @Authenticators.Types int biometricStrength, int sensorId,
long time, long requestId) {
final String lockedOutStr =
"lockOutTimedFor(userId=" + userId + ", biometricStrength=" + biometricStrength
+ ", sensorId=" + sensorId + "time=" + time + ", requestId=" + requestId
+ ")";
mRingBuffer.addApiCall(lockedOutStr);
mTimedLockouts.add(new Pair<>(biometricStrength, time));
attemptToFinish(userId, sensorId, lockedOutStr);
}
@Override
public void authEndedFor(int userId, @Authenticators.Types int biometricStrength,
int sensorId) {
mAuthResultCoordinator.authEndedFor(biometricStrength);
attemptToFinish(userId, sensorId,
int sensorId, long requestId) {
final String authEndedStr =
"authEndedFor(userId=" + userId + " ,biometricStrength=" + biometricStrength
+ ", sensorId=" + sensorId);
+ ", sensorId=" + sensorId + ", requestId=" + requestId + ")";
mRingBuffer.addApiCall(authEndedStr);
attemptToFinish(userId, sensorId, authEndedStr);
}
@Override
public void resetLockoutFor(int userId, @Authenticators.Types int biometricStrength) {
mMultiBiometricLockoutState.onUserUnlocked(userId, biometricStrength);
public void resetLockoutFor(int userId, @Authenticators.Types int biometricStrength,
long requestId) {
final String resetLockStr =
"resetLockoutFor(userId=" + userId + " ,biometricStrength=" + biometricStrength
+ ", requestId=" + requestId + ")";
mRingBuffer.addApiCall(resetLockStr);
mMultiBiometricLockoutState.setAuthenticatorTo(userId, biometricStrength,
true /*canAuthenticate */);
mMultiBiometricLockoutState.clearLockoutTime(userId, biometricStrength);
}
private void attemptToFinish(int userId, int sensorId, String description) {
@@ -154,4 +219,49 @@ class AuthSessionCoordinator implements AuthSessionListener {
}
}
/**
* Returns a string representation of the past N API calls as well as the
* permanent and timed lockout states for each user's authenticators.
*/
@Override
public String toString() {
return mRingBuffer + "\n" + mMultiBiometricLockoutState;
}
private static class RingBuffer {
private final String[] mApiCalls;
private final int mSize;
private int mCurr;
private int mApiCallNumber;
RingBuffer(int size) {
if (size <= 0) {
Slog.wtf(TAG, "Cannot initialize ring buffer of size: " + size);
}
mApiCalls = new String[size];
mCurr = 0;
mSize = size;
mApiCallNumber = 0;
}
void addApiCall(String str) {
mApiCalls[mCurr] = str;
mCurr++;
mCurr %= mSize;
mApiCallNumber++;
}
@Override
public String toString() {
String buffer = "";
int apiCall = mApiCallNumber > mSize ? mApiCallNumber - mSize : 0;
for (int i = 0; i < mSize; i++) {
final int location = (mCurr + i) % mSize;
if (mApiCalls[location] != null) {
buffer += String.format("#%-5d %s\n", apiCall++, mApiCalls[location]);
}
}
return buffer;
}
}
}

View File

@@ -25,25 +25,34 @@ interface AuthSessionListener {
/**
* Indicates an auth operation has started for a given user and sensor.
*/
void authStartedFor(int userId, int sensorId);
void authStartedFor(int userId, int sensorId, long requestId);
/**
* Indicates a successful authentication occurred for a sensor of a given strength.
*/
void authenticatedFor(int userId, @Authenticators.Types int biometricStrength, int sensorId);
void authenticatedFor(int userId, @Authenticators.Types int biometricStrength, int sensorId,
long requestId);
/**
* Indicates authentication ended for a sensor of a given strength.
*/
void authEndedFor(int userId, @Authenticators.Types int biometricStrength, int sensorId);
void authEndedFor(int userId, @Authenticators.Types int biometricStrength, int sensorId,
long requestId);
/**
* Indicates a lockout occurred for a sensor of a given strength.
*/
void lockedOutFor(int userId, @Authenticators.Types int biometricStrength, int sensorId);
void lockedOutFor(int userId, @Authenticators.Types int biometricStrength, int sensorId,
long requestId);
/**
* Indicates a timed lockout occurred for a sensor of a given strength.
*/
void lockOutTimed(int userId, @Authenticators.Types int biometricStrength, int sensorId,
long duration, long requestId);
/**
* Indicates that a reset lockout has happened for a given strength.
*/
void resetLockoutFor(int uerId, @Authenticators.Types int biometricStrength);
void resetLockoutFor(int uerId, @Authenticators.Types int biometricStrength, long requestId);
}

View File

@@ -21,82 +21,93 @@ import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMET
import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_STRONG;
import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_WEAK;
import android.util.ArrayMap;
import android.hardware.biometrics.BiometricManager;
import android.os.SystemClock;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import java.util.Arrays;
import java.util.Collections;
import java.time.Clock;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* This class is used as a system to store the state of each
* {@link Authenticators.Types} status for every user.
*
* Note that initially all biomertics are unlocked, meaning users can authenticate
* with each strength.
*/
class MultiBiometricLockoutState {
private static final String TAG = "MultiBiometricLockoutState";
private static final Map<Integer, List<Integer>> PRECEDENCE;
private final Map<Integer, Map<Integer, AuthenticatorState>> mCanUserAuthenticate;
private final Clock mClock;
static {
Map<Integer, List<Integer>> precedence = new ArrayMap<>();
precedence.put(Authenticators.BIOMETRIC_STRONG,
Arrays.asList(BIOMETRIC_STRONG, BIOMETRIC_WEAK, BIOMETRIC_CONVENIENCE));
precedence.put(BIOMETRIC_WEAK, Arrays.asList(BIOMETRIC_WEAK, BIOMETRIC_CONVENIENCE));
precedence.put(BIOMETRIC_CONVENIENCE, Arrays.asList(BIOMETRIC_CONVENIENCE));
PRECEDENCE = Collections.unmodifiableMap(precedence);
}
private final Map<Integer, Map<Integer, Boolean>> mCanUserAuthenticate;
@VisibleForTesting
MultiBiometricLockoutState() {
mCanUserAuthenticate = new HashMap<>();
this(SystemClock.currentNetworkTimeClock());
}
private static Map<Integer, Boolean> createLockedOutMap() {
Map<Integer, Boolean> lockOutMap = new HashMap<>();
lockOutMap.put(BIOMETRIC_STRONG, false);
lockOutMap.put(BIOMETRIC_WEAK, false);
lockOutMap.put(BIOMETRIC_CONVENIENCE, false);
MultiBiometricLockoutState(Clock clock) {
mCanUserAuthenticate = new HashMap<>();
mClock = clock;
}
private Map<Integer, AuthenticatorState> createUnlockedMap() {
Map<Integer, AuthenticatorState> lockOutMap = new HashMap<>();
lockOutMap.put(BIOMETRIC_STRONG,
new AuthenticatorState(BIOMETRIC_STRONG, false, 0, mClock));
lockOutMap.put(BIOMETRIC_WEAK, new AuthenticatorState(BIOMETRIC_WEAK, false, 0, mClock));
lockOutMap.put(BIOMETRIC_CONVENIENCE,
new AuthenticatorState(BIOMETRIC_CONVENIENCE, false, 0, mClock));
return lockOutMap;
}
private Map<Integer, Boolean> getAuthMapForUser(int userId) {
private Map<Integer, AuthenticatorState> getAuthMapForUser(int userId) {
if (!mCanUserAuthenticate.containsKey(userId)) {
mCanUserAuthenticate.put(userId, createLockedOutMap());
mCanUserAuthenticate.put(userId, createUnlockedMap());
}
return mCanUserAuthenticate.get(userId);
}
/**
* Indicates a {@link Authenticators} has been locked for userId.
*
* @param userId The user.
* @param strength The strength of biometric that is requested to be locked.
*/
void onUserLocked(int userId, @Authenticators.Types int strength) {
Slog.d(TAG, "onUserLocked(userId=" + userId + ", strength=" + strength + ")");
Map<Integer, Boolean> canUserAuthState = getAuthMapForUser(userId);
for (int strengthToLockout : PRECEDENCE.get(strength)) {
canUserAuthState.put(strengthToLockout, false);
void setAuthenticatorTo(int userId, @Authenticators.Types int strength, boolean canAuth) {
final Map<Integer, AuthenticatorState> authMap = getAuthMapForUser(userId);
switch (strength) {
case Authenticators.BIOMETRIC_STRONG:
authMap.get(BIOMETRIC_STRONG).mPermanentlyLockedOut = !canAuth;
// fall through
case Authenticators.BIOMETRIC_WEAK:
authMap.get(BIOMETRIC_WEAK).mPermanentlyLockedOut = !canAuth;
// fall through
case Authenticators.BIOMETRIC_CONVENIENCE:
authMap.get(BIOMETRIC_CONVENIENCE).mPermanentlyLockedOut = !canAuth;
}
}
/**
* Indicates that a user has unlocked a {@link Authenticators}
*
* @param userId The user.
* @param strength The strength of biometric that is unlocked.
*/
void onUserUnlocked(int userId, @Authenticators.Types int strength) {
Slog.d(TAG, "onUserUnlocked(userId=" + userId + ", strength=" + strength + ")");
Map<Integer, Boolean> canUserAuthState = getAuthMapForUser(userId);
for (int strengthToLockout : PRECEDENCE.get(strength)) {
canUserAuthState.put(strengthToLockout, true);
void increaseLockoutTime(int userId, @Authenticators.Types int strength, long duration) {
final Map<Integer, AuthenticatorState> authMap = getAuthMapForUser(userId);
switch (strength) {
case Authenticators.BIOMETRIC_STRONG:
authMap.get(BIOMETRIC_STRONG).increaseLockoutTo(duration);
// fall through
case Authenticators.BIOMETRIC_WEAK:
authMap.get(BIOMETRIC_WEAK).increaseLockoutTo(duration);
// fall through
case Authenticators.BIOMETRIC_CONVENIENCE:
authMap.get(BIOMETRIC_CONVENIENCE).increaseLockoutTo(duration);
}
}
void clearLockoutTime(int userId, @Authenticators.Types int strength) {
final Map<Integer, AuthenticatorState> authMap = getAuthMapForUser(userId);
switch (strength) {
case Authenticators.BIOMETRIC_STRONG:
authMap.get(BIOMETRIC_STRONG).setTimedLockout(0);
// fall through
case Authenticators.BIOMETRIC_WEAK:
authMap.get(BIOMETRIC_WEAK).setTimedLockout(0);
// fall through
case Authenticators.BIOMETRIC_CONVENIENCE:
authMap.get(BIOMETRIC_CONVENIENCE).setTimedLockout(0);
}
}
@@ -109,9 +120,64 @@ class MultiBiometricLockoutState {
* @return If a user can authenticate with a given biometric of this strength.
*/
boolean canUserAuthenticate(int userId, @Authenticators.Types int strength) {
final boolean canAuthenticate = getAuthMapForUser(userId).get(strength);
final boolean canAuthenticate = getAuthMapForUser(userId).get(strength).canAuthenticate();
Slog.d(TAG, "canUserAuthenticate(userId=" + userId + ", strength=" + strength + ") ="
+ canAuthenticate);
return canAuthenticate;
}
@Override
public String toString() {
String dumpState = "Permanent Lockouts\n";
final long time = mClock.millis();
for (Map.Entry<Integer, Map<Integer, AuthenticatorState>> userState :
mCanUserAuthenticate.entrySet()) {
final int userId = userState.getKey();
final Map<Integer, AuthenticatorState> map = userState.getValue();
String prettyStr = map.entrySet().stream().map(
(Map.Entry<Integer, AuthenticatorState> entry) -> entry.getValue().toString(
time)).collect(Collectors.joining(", "));
dumpState += "UserId=" + userId + ", {" + prettyStr + "}\n";
}
return dumpState;
}
private static class AuthenticatorState {
private Integer mAuthenticatorType;
private boolean mPermanentlyLockedOut;
private long mTimedLockout;
private Clock mClock;
AuthenticatorState(Integer authenticatorId, boolean permanentlyLockedOut,
long timedLockout, Clock clock) {
mAuthenticatorType = authenticatorId;
mPermanentlyLockedOut = permanentlyLockedOut;
mTimedLockout = timedLockout;
mClock = clock;
}
boolean canAuthenticate() {
return !mPermanentlyLockedOut && mClock.millis() - mTimedLockout >= 0;
}
/**
* Either increases the lockout to duration, or leaves it as it, whichever is longer.
*/
void increaseLockoutTo(long duration) {
mTimedLockout = Math.max(mTimedLockout, duration);
}
void setTimedLockout(long duration) {
mTimedLockout = duration;
}
String toString(long currentTime) {
final String duration =
mTimedLockout - currentTime > 0 ? (mTimedLockout - currentTime) + "ms" : "none";
final String permanentLockout = mPermanentlyLockedOut ? "true" : "false";
return String.format("(%s, permanentLockout=%s, timedLockoutRemaining=%s)",
BiometricManager.authenticatorToStr(mAuthenticatorType), permanentLockout,
duration);
}
}
}

View File

@@ -25,6 +25,7 @@ import android.hardware.SensorPrivacyManager;
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricFaceConstants;
import android.hardware.biometrics.BiometricManager.Authenticators;
import android.hardware.biometrics.common.ICancellationSignal;
import android.hardware.biometrics.face.IFace;
import android.hardware.face.FaceAuthenticationFrame;
@@ -38,6 +39,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.server.biometrics.Utils;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.AuthSessionCoordinator;
import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.BiometricNotificationUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
@@ -58,18 +60,25 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession>
implements LockoutConsumer {
private static final String TAG = "FaceAuthenticationClient";
@NonNull private final UsageStats mUsageStats;
@NonNull private final LockoutCache mLockoutCache;
@Nullable private final NotificationManager mNotificationManager;
@Nullable private ICancellationSignal mCancellationSignal;
@Nullable private SensorPrivacyManager mSensorPrivacyManager;
@NonNull
private final UsageStats mUsageStats;
@NonNull
private final LockoutCache mLockoutCache;
@NonNull
private final AuthSessionCoordinator mAuthSessionCoordinator;
@Nullable
private final NotificationManager mNotificationManager;
private final int[] mBiometricPromptIgnoreList;
private final int[] mBiometricPromptIgnoreListVendor;
private final int[] mKeyguardIgnoreList;
private final int[] mKeyguardIgnoreListVendor;
@FaceManager.FaceAcquired private int mLastAcquire = FaceManager.FACE_ACQUIRED_UNKNOWN;
private final int mBiometricStrength;
@Nullable
private ICancellationSignal mCancellationSignal;
@Nullable
private SensorPrivacyManager mSensorPrivacyManager;
@FaceManager.FaceAcquired
private int mLastAcquire = FaceManager.FACE_ACQUIRED_UNKNOWN;
FaceAuthenticationClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon,
@@ -79,11 +88,12 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession>
@NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
boolean isStrongBiometric, @NonNull UsageStats usageStats,
@NonNull LockoutCache lockoutCache, boolean allowBackgroundAuthentication,
boolean isKeyguardBypassEnabled) {
boolean isKeyguardBypassEnabled, @Authenticators.Types int biometricStrength) {
this(context, lazyDaemon, token, requestId, listener, targetUserId, operationId,
restricted, owner, cookie, requireConfirmation, sensorId, logger, biometricContext,
isStrongBiometric, usageStats, lockoutCache, allowBackgroundAuthentication,
isKeyguardBypassEnabled, context.getSystemService(SensorPrivacyManager.class));
isKeyguardBypassEnabled, context.getSystemService(SensorPrivacyManager.class),
biometricStrength);
}
@VisibleForTesting
@@ -95,7 +105,8 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession>
@NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
boolean isStrongBiometric, @NonNull UsageStats usageStats,
@NonNull LockoutCache lockoutCache, boolean allowBackgroundAuthentication,
boolean isKeyguardBypassEnabled, SensorPrivacyManager sensorPrivacyManager) {
boolean isKeyguardBypassEnabled, SensorPrivacyManager sensorPrivacyManager,
@Authenticators.Types int biometricStrength) {
super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted,
owner, cookie, requireConfirmation, sensorId, logger, biometricContext,
isStrongBiometric, null /* taskStackListener */, lockoutCache,
@@ -107,6 +118,7 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession>
mLockoutCache = lockoutCache;
mNotificationManager = context.getSystemService(NotificationManager.class);
mSensorPrivacyManager = sensorPrivacyManager;
mAuthSessionCoordinator = biometricContext.getAuthSessionCoordinator();
final Resources resources = getContext().getResources();
mBiometricPromptIgnoreList = resources.getIntArray(
@@ -117,12 +129,14 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession>
R.array.config_face_acquire_keyguard_ignorelist);
mKeyguardIgnoreListVendor = resources.getIntArray(
R.array.config_face_acquire_vendor_keyguard_ignorelist);
mBiometricStrength = biometricStrength;
}
@Override
public void start(@NonNull ClientMonitorCallback callback) {
super.start(callback);
mState = STATE_STARTED;
mAuthSessionCoordinator.authStartedFor(getTargetUserId(), getSensorId(), getRequestId());
}
@NonNull
@@ -138,7 +152,7 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession>
if (mSensorPrivacyManager != null
&& mSensorPrivacyManager
.isSensorPrivacyEnabled(SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE,
SensorPrivacyManager.Sensors.CAMERA)) {
SensorPrivacyManager.Sensors.CAMERA)) {
onError(BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
0 /* vendorCode */);
mCallback.onClientFinished(this, false /* success */);
@@ -207,6 +221,9 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession>
0 /* error */,
0 /* vendorError */,
getTargetUserId()));
mAuthSessionCoordinator
.authenticatedFor(getTargetUserId(), mBiometricStrength, getSensorId(),
getRequestId());
}
@Override
@@ -222,7 +239,8 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession>
if (error == BiometricConstants.BIOMETRIC_ERROR_RE_ENROLL) {
BiometricNotificationUtils.showReEnrollmentNotification(getContext());
}
mAuthSessionCoordinator.authEndedFor(getTargetUserId(), mBiometricStrength, getSensorId(),
getRequestId());
super.onError(error, vendorCode);
}
@@ -283,6 +301,8 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession>
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
}
mAuthSessionCoordinator.lockOutTimed(getTargetUserId(), mBiometricStrength, getSensorId(),
durationMillis, getRequestId());
}
@Override
@@ -298,5 +318,7 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession>
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception", e);
}
mAuthSessionCoordinator.lockedOutFor(getTargetUserId(), mBiometricStrength, getSensorId(),
getRequestId());
}
}

View File

@@ -414,6 +414,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
boolean allowBackgroundAuthentication, boolean isKeyguardBypassEnabled) {
mHandler.post(() -> {
final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId);
final int biometricStrength = Utils.getCurrentStrength(sensorId);
final FaceAuthenticationClient client = new FaceAuthenticationClient(
mContext, mSensors.get(sensorId).getLazySession(), token, requestId, callback,
userId, operationId, restricted, opPackageName, cookie,
@@ -421,7 +422,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient),
mBiometricContext, isStrongBiometric,
mUsageStats, mSensors.get(sensorId).getLockoutCache(),
allowBackgroundAuthentication, isKeyguardBypassEnabled);
allowBackgroundAuthentication, isKeyguardBypassEnabled, biometricStrength);
scheduleForSensor(sensorId, client);
});
}
@@ -490,7 +491,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN),
mBiometricContext, hardwareAuthToken,
mSensors.get(sensorId).getLockoutCache(), mLockoutResetDispatcher);
mSensors.get(sensorId).getLockoutCache(), mLockoutResetDispatcher,
Utils.getCurrentStrength(sensorId));
scheduleForSensor(sensorId, client);
});
@@ -623,6 +625,9 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
}
pw.println(dump);
pw.println("HAL deaths since last reboot: " + performanceTracker.getHALDeathCount());
pw.println("---AuthSessionCoordinator logs begin---");
pw.println(mBiometricContext.getAuthSessionCoordinator());
pw.println("---AuthSessionCoordinator logs end ---");
mSensors.get(sensorId).getScheduler().dump(pw);
mUsageStats.print(pw);

View File

@@ -18,6 +18,7 @@ package com.android.server.biometrics.sensors.face.aidl;
import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.BiometricManager.Authenticators;
import android.hardware.biometrics.face.IFace;
import android.hardware.keymaster.HardwareAuthToken;
import android.os.RemoteException;
@@ -48,17 +49,20 @@ public class FaceResetLockoutClient extends HalClientMonitor<AidlSession> implem
private final HardwareAuthToken mHardwareAuthToken;
private final LockoutCache mLockoutCache;
private final LockoutResetDispatcher mLockoutResetDispatcher;
private final int mBiometricStrength;
FaceResetLockoutClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon, int userId, String owner, int sensorId,
@NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext,
@NonNull byte[] hardwareAuthToken, @NonNull LockoutCache lockoutTracker,
@NonNull LockoutResetDispatcher lockoutResetDispatcher) {
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@Authenticators.Types int biometricStrength) {
super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
0 /* cookie */, sensorId, logger, biometricContext);
mHardwareAuthToken = HardwareAuthTokenUtils.toHardwareAuthToken(hardwareAuthToken);
mLockoutCache = lockoutTracker;
mLockoutResetDispatcher = lockoutResetDispatcher;
mBiometricStrength = biometricStrength;
}
@Override
@@ -85,6 +89,8 @@ public class FaceResetLockoutClient extends HalClientMonitor<AidlSession> implem
void onLockoutCleared() {
resetLocalLockoutStateToNone(getSensorId(), getTargetUserId(), mLockoutCache,
mLockoutResetDispatcher);
getBiometricContext().getAuthSessionCoordinator()
.resetLockoutFor(getTargetUserId(), mBiometricStrength, getRequestId());
mCallback.onClientFinished(this, true /* success */);
}

View File

@@ -26,6 +26,7 @@ import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricFingerprintConstants;
import android.hardware.biometrics.BiometricFingerprintConstants.FingerprintAcquired;
import android.hardware.biometrics.BiometricManager.Authenticators;
import android.hardware.biometrics.common.ICancellationSignal;
import android.hardware.biometrics.common.OperationContext;
import android.hardware.biometrics.fingerprint.PointerContext;
@@ -45,6 +46,7 @@ import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.log.CallbackWithProbe;
import com.android.server.biometrics.log.Probe;
import com.android.server.biometrics.sensors.AuthSessionCoordinator;
import com.android.server.biometrics.sensors.AuthenticationClient;
import com.android.server.biometrics.sensors.BiometricNotificationUtils;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
@@ -81,7 +83,9 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession>
private final Handler mHandler;
private final int mSkipWaitForPowerAcquireMessage;
private final int mSkipWaitForPowerVendorAcquireMessage;
private final int mBiometricStrength;
private final long mFingerUpIgnoresPower = 500;
private final AuthSessionCoordinator mAuthSessionCoordinator;
@Nullable
private ICancellationSignal mCancellationSignal;
private boolean mIsPointerDown;
@@ -112,7 +116,8 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession>
@Nullable ISidefpsController sidefpsController,
boolean allowBackgroundAuthentication,
@NonNull FingerprintSensorPropertiesInternal sensorProps,
@NonNull Handler handler) {
@NonNull Handler handler,
@Authenticators.Types int biometricStrength) {
super(
context,
lazyDaemon,
@@ -154,6 +159,8 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession>
mSkipWaitForPowerVendorAcquireMessage =
context.getResources().getInteger(
R.integer.config_sidefpsSkipWaitForPowerVendorAcquireMessage);
mBiometricStrength = biometricStrength;
mAuthSessionCoordinator = biometricContext.getAuthSessionCoordinator();
if (mSensorProps.isAnySidefpsType()) {
if (Build.isDebuggable()) {
@@ -180,6 +187,8 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession>
} else {
mState = STATE_STARTED;
}
mAuthSessionCoordinator.authStartedFor(getTargetUserId(), getSensorId(),
getRequestId());
}
@NonNull
@@ -193,6 +202,8 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession>
protected void handleLifecycleAfterAuth(boolean authenticated) {
if (authenticated) {
mCallback.onClientFinished(this, true /* success */);
mAuthSessionCoordinator.authenticatedFor(
getTargetUserId(), mBiometricStrength, getSensorId(), getRequestId());
}
}
@@ -294,6 +305,8 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession>
}
mSensorOverlays.hide(getSensorId());
mAuthSessionCoordinator.authEndedFor(getTargetUserId(), mBiometricStrength, getSensorId(),
getRequestId());
}
@Override
@@ -447,6 +460,8 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession>
mSensorOverlays.hide(getSensorId());
mCallback.onClientFinished(this, false /* success */);
mAuthSessionCoordinator.lockOutTimed(getTargetUserId(), mBiometricStrength, getSensorId(),
durationMillis, getRequestId());
}
@Override
@@ -470,6 +485,8 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession>
mSensorOverlays.hide(getSensorId());
mCallback.onClientFinished(this, false /* success */);
mAuthSessionCoordinator.lockedOutFor(getTargetUserId(), mBiometricStrength, getSensorId(),
getRequestId());
}
@Override
@@ -482,6 +499,8 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession>
mHandler.removeMessages(MESSAGE_AUTH_SUCCESS);
// Do not call onError() as that will send an additional callback to coex.
onErrorInternal(BiometricConstants.BIOMETRIC_ERROR_POWER_PRESSED, 0, true);
mAuthSessionCoordinator.authEndedFor(getTargetUserId(),
mBiometricStrength, getSensorId(), getRequestId());
}
mHandler.removeMessages(MESSAGE_IGNORE_AUTH);
mHandler.postDelayed(() -> {

View File

@@ -328,7 +328,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
createLogger(BiometricsProtoEnums.ACTION_UNKNOWN,
BiometricsProtoEnums.CLIENT_UNKNOWN),
mBiometricContext, hardwareAuthToken,
mSensors.get(sensorId).getLockoutCache(), mLockoutResetDispatcher);
mSensors.get(sensorId).getLockoutCache(), mLockoutResetDispatcher,
Utils.getCurrentStrength(sensorId));
scheduleForSensor(sensorId, client);
});
}
@@ -447,7 +448,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
mBiometricContext, isStrongBiometric,
mTaskStackListener, mSensors.get(sensorId).getLockoutCache(),
mUdfpsOverlayController, mSidefpsController, allowBackgroundAuthentication,
mSensors.get(sensorId).getSensorProperties(), mHandler);
mSensors.get(sensorId).getSensorProperties(), mHandler,
Utils.getCurrentStrength(sensorId));
scheduleForSensor(sensorId, client, mBiometricStateCallback);
});
}
@@ -702,6 +704,9 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
}
pw.println(dump);
pw.println("HAL deaths since last reboot: " + performanceTracker.getHALDeathCount());
pw.println("---AuthSessionCoordinator logs begin---");
pw.println(mBiometricContext.getAuthSessionCoordinator());
pw.println("---AuthSessionCoordinator logs end ---");
mSensors.get(sensorId).getScheduler().dump(pw);
}

View File

@@ -18,6 +18,7 @@ package com.android.server.biometrics.sensors.fingerprint.aidl;
import android.annotation.NonNull;
import android.content.Context;
import android.hardware.biometrics.BiometricManager.Authenticators;
import android.hardware.biometrics.fingerprint.IFingerprint;
import android.hardware.keymaster.HardwareAuthToken;
import android.os.RemoteException;
@@ -48,17 +49,20 @@ class FingerprintResetLockoutClient extends HalClientMonitor<AidlSession> implem
private final HardwareAuthToken mHardwareAuthToken;
private final LockoutCache mLockoutCache;
private final LockoutResetDispatcher mLockoutResetDispatcher;
private final int mBiometricStrength;
FingerprintResetLockoutClient(@NonNull Context context,
@NonNull Supplier<AidlSession> lazyDaemon, int userId, String owner, int sensorId,
@NonNull BiometricLogger biometricLogger, @NonNull BiometricContext biometricContext,
@NonNull byte[] hardwareAuthToken, @NonNull LockoutCache lockoutTracker,
@NonNull LockoutResetDispatcher lockoutResetDispatcher) {
@NonNull LockoutResetDispatcher lockoutResetDispatcher,
@Authenticators.Types int biometricStrength) {
super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner,
0 /* cookie */, sensorId, biometricLogger, biometricContext);
mHardwareAuthToken = HardwareAuthTokenUtils.toHardwareAuthToken(hardwareAuthToken);
mLockoutCache = lockoutTracker;
mLockoutResetDispatcher = lockoutResetDispatcher;
mBiometricStrength = biometricStrength;
}
@Override
@@ -89,6 +93,8 @@ class FingerprintResetLockoutClient extends HalClientMonitor<AidlSession> implem
void onLockoutCleared() {
resetLocalLockoutStateToNone(getSensorId(), getTargetUserId(), mLockoutCache,
mLockoutResetDispatcher);
getBiometricContext().getAuthSessionCoordinator()
.resetLockoutFor(getTargetUserId(), mBiometricStrength, getRequestId());
mCallback.onClientFinished(this, true /* success */);
}

View File

@@ -74,7 +74,7 @@ public class BiometricContextProviderTest {
public void setup() throws RemoteException {
when(mAmbientDisplayConfiguration.alwaysOnEnabled(anyInt())).thenReturn(true);
mProvider = new BiometricContextProvider(mAmbientDisplayConfiguration, mStatusBarService,
null /* handler */);
null /* handler */, null /* authSessionCoordinator */);
ArgumentCaptor<IBiometricContextListener> captor =
ArgumentCaptor.forClass(IBiometricContextListener.class);
verify(mStatusBarService).setBiometicContextListener(captor.capture());

View File

@@ -16,6 +16,10 @@
package com.android.server.biometrics.sensors;
import static com.android.server.biometrics.sensors.AuthResultCoordinator.AUTHENTICATOR_DEFAULT;
import static com.android.server.biometrics.sensors.AuthResultCoordinator.AUTHENTICATOR_LOCKED;
import static com.android.server.biometrics.sensors.AuthResultCoordinator.AUTHENTICATOR_UNLOCKED;
import static com.google.common.truth.Truth.assertThat;
import android.hardware.biometrics.BiometricManager;
@@ -23,6 +27,8 @@ import android.hardware.biometrics.BiometricManager;
import org.junit.Before;
import org.junit.Test;
import java.util.Map;
public class AuthResultCoordinatorTest {
private AuthResultCoordinator mAuthResultCoordinator;
@@ -33,62 +39,107 @@ public class AuthResultCoordinatorTest {
@Test
public void testDefaultMessage() {
checkResult(mAuthResultCoordinator.getResult(),
AuthResult.FAILED,
BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE);
final Map<Integer, Integer> authMap = mAuthResultCoordinator.getResult();
assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_STRONG)).isEqualTo(
AUTHENTICATOR_DEFAULT);
assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_WEAK)).isEqualTo(
AUTHENTICATOR_DEFAULT);
assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE)).isEqualTo(
AUTHENTICATOR_DEFAULT);
}
@Test
public void testSingleMessageCoordinator() {
mAuthResultCoordinator.authenticatedFor(
BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE);
checkResult(mAuthResultCoordinator.getResult(),
AuthResult.AUTHENTICATED,
BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE);
final Map<Integer, Integer> authMap = mAuthResultCoordinator.getResult();
assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_STRONG)).isEqualTo(
AUTHENTICATOR_DEFAULT);
assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_WEAK)).isEqualTo(
AUTHENTICATOR_DEFAULT);
assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE)).isEqualTo(
AUTHENTICATOR_UNLOCKED);
}
@Test
public void testLockout() {
mAuthResultCoordinator.lockedOutFor(
BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE);
checkResult(mAuthResultCoordinator.getResult(),
AuthResult.LOCKED_OUT,
BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE);
final Map<Integer, Integer> authMap = mAuthResultCoordinator.getResult();
assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_STRONG)).isEqualTo(
AUTHENTICATOR_DEFAULT);
assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_WEAK)).isEqualTo(
AUTHENTICATOR_DEFAULT);
assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE)).isEqualTo(
AUTHENTICATOR_LOCKED);
}
@Test
public void testHigherStrengthPrecedence() {
public void testConvenientLockout() {
mAuthResultCoordinator.authenticatedFor(
BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE);
mAuthResultCoordinator.authenticatedFor(
BiometricManager.Authenticators.BIOMETRIC_WEAK);
checkResult(mAuthResultCoordinator.getResult(),
AuthResult.AUTHENTICATED,
BiometricManager.Authenticators.BIOMETRIC_WEAK);
mAuthResultCoordinator.authenticatedFor(
BiometricManager.Authenticators.BIOMETRIC_STRONG);
checkResult(mAuthResultCoordinator.getResult(),
AuthResult.AUTHENTICATED,
BiometricManager.Authenticators.BIOMETRIC_STRONG);
Map<Integer, Integer> authMap = mAuthResultCoordinator.getResult();
assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_STRONG)).isEqualTo(
AUTHENTICATOR_DEFAULT);
assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_WEAK)).isEqualTo(
AUTHENTICATOR_DEFAULT);
assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE)).isEqualTo(
AUTHENTICATOR_UNLOCKED);
}
@Test
public void testAuthPrecedence() {
public void testWeakLockout() {
mAuthResultCoordinator.authenticatedFor(
BiometricManager.Authenticators.BIOMETRIC_WEAK);
Map<Integer, Integer> authMap = mAuthResultCoordinator.getResult();
assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_STRONG)).isEqualTo(
AUTHENTICATOR_DEFAULT);
assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_WEAK)).isEqualTo(
AUTHENTICATOR_UNLOCKED);
assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE)).isEqualTo(
AUTHENTICATOR_UNLOCKED);
}
@Test
public void testStrongLockout() {
mAuthResultCoordinator.authenticatedFor(
BiometricManager.Authenticators.BIOMETRIC_STRONG);
final Map<Integer, Integer> authMap = mAuthResultCoordinator.getResult();
assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_STRONG)).isEqualTo(
AUTHENTICATOR_UNLOCKED);
assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_WEAK)).isEqualTo(
AUTHENTICATOR_UNLOCKED);
assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE)).isEqualTo(
AUTHENTICATOR_UNLOCKED);
}
@Test
public void testAuthAndLockout() {
mAuthResultCoordinator.authenticatedFor(
BiometricManager.Authenticators.BIOMETRIC_WEAK);
mAuthResultCoordinator.lockedOutFor(
BiometricManager.Authenticators.BIOMETRIC_WEAK);
checkResult(mAuthResultCoordinator.getResult(),
AuthResult.AUTHENTICATED,
BiometricManager.Authenticators.BIOMETRIC_WEAK);
}
final Map<Integer, Integer> authMap = mAuthResultCoordinator.getResult();
assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_STRONG)).isEqualTo(
AUTHENTICATOR_DEFAULT);
assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_WEAK)).isEqualTo(
AUTHENTICATOR_UNLOCKED | AUTHENTICATOR_LOCKED);
assertThat(authMap.get(BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE)).isEqualTo(
AUTHENTICATOR_UNLOCKED | AUTHENTICATOR_LOCKED);
void checkResult(AuthResult res, int status,
@BiometricManager.Authenticators.Types int strength) {
assertThat(res.getStatus()).isEqualTo(status);
assertThat(res.getBiometricStrength()).isEqualTo(strength);
}
}

View File

@@ -22,12 +22,18 @@ import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMET
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
import android.platform.test.annotations.Presubmit;
import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.time.Clock;
@Presubmit
@SmallTest
@@ -36,20 +42,51 @@ public class AuthSessionCoordinatorTest {
private static final int SECONDARY_USER = 10;
private AuthSessionCoordinator mCoordinator;
@Mock
private Clock mClock;
@Before
public void setUp() throws Exception {
mCoordinator = new AuthSessionCoordinator();
MockitoAnnotations.initMocks(this);
when(mClock.millis()).thenReturn(0L);
mCoordinator = new AuthSessionCoordinator(mClock);
}
@Test
public void testUserUnlocked() {
mCoordinator.authStartedFor(PRIMARY_USER, 1 /* sensorId */, 0 /* requestId */);
mCoordinator.lockedOutFor(PRIMARY_USER, BIOMETRIC_STRONG, 1 /* sensorId */,
0 /* requestId */);
assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isFalse();
assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_WEAK)).isFalse();
assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_STRONG)).isFalse();
mCoordinator.authStartedFor(PRIMARY_USER, 1);
mCoordinator.authenticatedFor(PRIMARY_USER, BIOMETRIC_WEAK, 1);
mCoordinator.authStartedFor(PRIMARY_USER, 1 /* sensorId */, 0 /* requestId */);
mCoordinator.authenticatedFor(PRIMARY_USER, BIOMETRIC_WEAK, 1 /* sensorId */,
0 /* requestId */);
assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isTrue();
assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue();
assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_STRONG)).isFalse();
}
@Test
public void testUserLocked() {
mCoordinator.authStartedFor(PRIMARY_USER, 1 /* sensorId */, 0 /* requestId */);
mCoordinator.authStartedFor(PRIMARY_USER, 2 /* sensorId */, 0 /* requestId */);
mCoordinator.lockedOutFor(PRIMARY_USER, BIOMETRIC_STRONG, 1 /* sensorId */,
0 /* requestId */);
mCoordinator.authenticatedFor(PRIMARY_USER, BIOMETRIC_WEAK, 2 /* sensorId */,
0 /* requestId */);
assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isFalse();
assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_WEAK)).isFalse();
assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_STRONG)).isFalse();
mCoordinator.authStartedFor(PRIMARY_USER, 1 /* sensorId */, 0 /* requestId */);
mCoordinator.authenticatedFor(PRIMARY_USER, BIOMETRIC_WEAK, 1 /* sensorId */,
0 /* requestId */);
assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isTrue();
assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue();
@@ -58,15 +95,16 @@ public class AuthSessionCoordinatorTest {
@Test
public void testUserCanAuthDuringLockoutOfSameSession() {
mCoordinator.resetLockoutFor(PRIMARY_USER, BIOMETRIC_STRONG);
mCoordinator.resetLockoutFor(PRIMARY_USER, BIOMETRIC_STRONG, 0 /* requestId */);
assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isTrue();
assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue();
assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_STRONG)).isTrue();
mCoordinator.authStartedFor(PRIMARY_USER, 1);
mCoordinator.authStartedFor(PRIMARY_USER, 2);
mCoordinator.lockedOutFor(PRIMARY_USER, BIOMETRIC_WEAK, 2);
mCoordinator.authStartedFor(PRIMARY_USER, 1 /* sensorId */, 0 /* requestId */);
mCoordinator.authStartedFor(PRIMARY_USER, 2 /* sensorId */, 0 /* requestId */);
mCoordinator.lockedOutFor(PRIMARY_USER, BIOMETRIC_WEAK, 2 /* sensorId */,
0 /* requestId */);
assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isTrue();
assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue();
@@ -75,7 +113,15 @@ public class AuthSessionCoordinatorTest {
@Test
public void testMultiUserAuth() {
mCoordinator.resetLockoutFor(PRIMARY_USER, BIOMETRIC_STRONG);
mCoordinator.authStartedFor(PRIMARY_USER, 1 /* sensorId */, 0 /* requestId */);
mCoordinator.lockedOutFor(PRIMARY_USER, BIOMETRIC_STRONG, 1 /* sensorId */,
0 /* requestId */);
mCoordinator.authStartedFor(SECONDARY_USER, 1 /* sensorId */, 0 /* requestId */);
mCoordinator.lockedOutFor(SECONDARY_USER, BIOMETRIC_STRONG, 1 /* sensorId */,
0 /* requestId */);
mCoordinator.resetLockoutFor(PRIMARY_USER, BIOMETRIC_STRONG, 0 /* requestId */);
assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isTrue();
assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue();
@@ -85,9 +131,10 @@ public class AuthSessionCoordinatorTest {
assertThat(mCoordinator.getCanAuthFor(SECONDARY_USER, BIOMETRIC_WEAK)).isFalse();
assertThat(mCoordinator.getCanAuthFor(SECONDARY_USER, BIOMETRIC_STRONG)).isFalse();
mCoordinator.authStartedFor(PRIMARY_USER, 1);
mCoordinator.authStartedFor(PRIMARY_USER, 2);
mCoordinator.lockedOutFor(PRIMARY_USER, BIOMETRIC_WEAK, 2);
mCoordinator.authStartedFor(PRIMARY_USER, 1 /* sensorId */, 0 /* requestId */);
mCoordinator.authStartedFor(PRIMARY_USER, 2 /* sensorId */, 0 /* requestId */);
mCoordinator.lockedOutFor(PRIMARY_USER, BIOMETRIC_WEAK, 2 /* sensorId */,
0 /* requestId */);
assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isTrue();
assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue();

View File

@@ -22,6 +22,8 @@ import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMET
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.when;
import android.platform.test.annotations.Presubmit;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -31,6 +33,10 @@ import androidx.test.filters.SmallTest;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.time.Clock;
@SmallTest
@Presubmit
@@ -38,111 +44,155 @@ import org.junit.runner.RunWith;
@TestableLooper.RunWithLooper(setAsMainLooper = true)
public class MultiBiometricLockoutStateTest {
private static final int PRIMARY_USER = 0;
private MultiBiometricLockoutState mCoordinator;
private MultiBiometricLockoutState mLockoutState;
@Mock
private Clock mClock;
private static void unlockAllBiometrics(MultiBiometricLockoutState lockoutState, int userId) {
lockoutState.setAuthenticatorTo(userId, BIOMETRIC_STRONG, true /* canAuthenticate */);
assertThat(lockoutState.canUserAuthenticate(userId, BIOMETRIC_STRONG)).isTrue();
assertThat(lockoutState.canUserAuthenticate(userId, BIOMETRIC_WEAK)).isTrue();
assertThat(lockoutState.canUserAuthenticate(userId, BIOMETRIC_CONVENIENCE)).isTrue();
}
private static void lockoutAllBiometrics(MultiBiometricLockoutState lockoutState, int userId) {
lockoutState.setAuthenticatorTo(userId, BIOMETRIC_STRONG, false /* canAuthenticate */);
assertThat(lockoutState.canUserAuthenticate(userId, BIOMETRIC_STRONG)).isFalse();
assertThat(lockoutState.canUserAuthenticate(userId, BIOMETRIC_WEAK)).isFalse();
assertThat(lockoutState.canUserAuthenticate(userId, BIOMETRIC_CONVENIENCE)).isFalse();
}
private void unlockAllBiometrics() {
unlockAllBiometrics(mCoordinator, PRIMARY_USER);
unlockAllBiometrics(mLockoutState, PRIMARY_USER);
}
private void lockoutAllBiometrics() {
lockoutAllBiometrics(mCoordinator, PRIMARY_USER);
}
private static void unlockAllBiometrics(MultiBiometricLockoutState coordinator, int userId) {
coordinator.onUserUnlocked(userId, BIOMETRIC_STRONG);
assertThat(coordinator.canUserAuthenticate(userId, BIOMETRIC_STRONG)).isTrue();
assertThat(coordinator.canUserAuthenticate(userId, BIOMETRIC_WEAK)).isTrue();
assertThat(coordinator.canUserAuthenticate(userId, BIOMETRIC_CONVENIENCE)).isTrue();
}
private static void lockoutAllBiometrics(MultiBiometricLockoutState coordinator, int userId) {
coordinator.onUserLocked(userId, BIOMETRIC_STRONG);
assertThat(coordinator.canUserAuthenticate(userId, BIOMETRIC_STRONG)).isFalse();
assertThat(coordinator.canUserAuthenticate(userId, BIOMETRIC_WEAK)).isFalse();
assertThat(coordinator.canUserAuthenticate(userId, BIOMETRIC_CONVENIENCE)).isFalse();
lockoutAllBiometrics(mLockoutState, PRIMARY_USER);
}
@Before
public void setUp() throws Exception {
mCoordinator = new MultiBiometricLockoutState();
MockitoAnnotations.initMocks(this);
when(mClock.millis()).thenReturn(0L);
mLockoutState = new MultiBiometricLockoutState(mClock);
}
@Test
public void testInitialStateLockedOut() {
assertThat(mCoordinator.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_STRONG)).isFalse();
assertThat(mCoordinator.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_WEAK)).isFalse();
assertThat(mCoordinator.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isFalse();
assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_STRONG)).isTrue();
assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue();
assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isTrue();
}
@Test
public void testConvenienceLockout() {
unlockAllBiometrics();
mCoordinator.onUserLocked(PRIMARY_USER, BIOMETRIC_CONVENIENCE);
assertThat(mCoordinator.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_STRONG)).isTrue();
assertThat(mCoordinator.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue();
assertThat(mCoordinator.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isFalse();
mLockoutState.setAuthenticatorTo(PRIMARY_USER, BIOMETRIC_CONVENIENCE,
false /* canAuthenticate */);
assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_STRONG)).isTrue();
assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue();
assertThat(
mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isFalse();
}
@Test
public void testWeakLockout() {
unlockAllBiometrics();
mCoordinator.onUserLocked(PRIMARY_USER, BIOMETRIC_WEAK);
assertThat(mCoordinator.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_STRONG)).isTrue();
assertThat(mCoordinator.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_WEAK)).isFalse();
assertThat(mCoordinator.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isFalse();
mLockoutState.setAuthenticatorTo(PRIMARY_USER, BIOMETRIC_WEAK, false /* canAuthenticate */);
assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_STRONG)).isTrue();
assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_WEAK)).isFalse();
assertThat(
mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isFalse();
}
@Test
public void testStrongLockout() {
unlockAllBiometrics();
mCoordinator.onUserLocked(PRIMARY_USER, BIOMETRIC_STRONG);
assertThat(mCoordinator.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_STRONG)).isFalse();
assertThat(mCoordinator.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_WEAK)).isFalse();
assertThat(mCoordinator.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isFalse();
lockoutAllBiometrics();
mLockoutState.setAuthenticatorTo(PRIMARY_USER, BIOMETRIC_STRONG,
false /* canAuthenticate */);
assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_STRONG)).isFalse();
assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_WEAK)).isFalse();
assertThat(
mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isFalse();
}
@Test
public void testConvenienceUnlock() {
lockoutAllBiometrics();
mCoordinator.onUserUnlocked(PRIMARY_USER, BIOMETRIC_CONVENIENCE);
assertThat(mCoordinator.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_STRONG)).isFalse();
assertThat(mCoordinator.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_WEAK)).isFalse();
assertThat(mCoordinator.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isTrue();
mLockoutState.setAuthenticatorTo(PRIMARY_USER, BIOMETRIC_CONVENIENCE,
true /* canAuthenticate */);
assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_STRONG)).isFalse();
assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_WEAK)).isFalse();
assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isTrue();
}
@Test
public void testWeakUnlock() {
lockoutAllBiometrics();
mCoordinator.onUserUnlocked(PRIMARY_USER, BIOMETRIC_WEAK);
assertThat(mCoordinator.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_STRONG)).isFalse();
assertThat(mCoordinator.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue();
assertThat(mCoordinator.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isTrue();
mLockoutState.setAuthenticatorTo(PRIMARY_USER, BIOMETRIC_WEAK, true /* canAuthenticate */);
assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_STRONG)).isFalse();
assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue();
assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isTrue();
}
@Test
public void testStrongUnlock() {
lockoutAllBiometrics();
mCoordinator.onUserUnlocked(PRIMARY_USER, BIOMETRIC_STRONG);
assertThat(mCoordinator.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_STRONG)).isTrue();
assertThat(mCoordinator.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue();
assertThat(mCoordinator.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isTrue();
mLockoutState.setAuthenticatorTo(PRIMARY_USER, BIOMETRIC_STRONG,
true /* canAuthenticate */);
assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_STRONG)).isTrue();
assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue();
assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isTrue();
}
@Test
public void multiUser_userOneDoesNotAffectUserTwo() {
final int userOne = 1;
final int userTwo = 2;
MultiBiometricLockoutState coordinator = new MultiBiometricLockoutState();
lockoutAllBiometrics(coordinator, userOne);
lockoutAllBiometrics(coordinator, userTwo);
MultiBiometricLockoutState lockoutState = new MultiBiometricLockoutState(mClock);
lockoutAllBiometrics(lockoutState, userOne);
lockoutAllBiometrics(lockoutState, userTwo);
coordinator.onUserUnlocked(userOne, BIOMETRIC_WEAK);
assertThat(coordinator.canUserAuthenticate(userOne, BIOMETRIC_STRONG)).isFalse();
assertThat(coordinator.canUserAuthenticate(userOne, BIOMETRIC_WEAK)).isTrue();
assertThat(coordinator.canUserAuthenticate(userOne, BIOMETRIC_CONVENIENCE)).isTrue();
lockoutState.setAuthenticatorTo(userOne, BIOMETRIC_WEAK, true /* canAuthenticate */);
assertThat(lockoutState.canUserAuthenticate(userOne, BIOMETRIC_STRONG)).isFalse();
assertThat(lockoutState.canUserAuthenticate(userOne, BIOMETRIC_WEAK)).isTrue();
assertThat(lockoutState.canUserAuthenticate(userOne, BIOMETRIC_CONVENIENCE)).isTrue();
assertThat(coordinator.canUserAuthenticate(userTwo, BIOMETRIC_STRONG)).isFalse();
assertThat(coordinator.canUserAuthenticate(userTwo, BIOMETRIC_WEAK)).isFalse();
assertThat(coordinator.canUserAuthenticate(userTwo, BIOMETRIC_CONVENIENCE)).isFalse();
assertThat(lockoutState.canUserAuthenticate(userTwo, BIOMETRIC_STRONG)).isFalse();
assertThat(lockoutState.canUserAuthenticate(userTwo, BIOMETRIC_WEAK)).isFalse();
assertThat(lockoutState.canUserAuthenticate(userTwo, BIOMETRIC_CONVENIENCE)).isFalse();
}
@Test
public void testTimedLockout() {
assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_STRONG)).isTrue();
assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue();
assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isTrue();
mLockoutState.increaseLockoutTime(PRIMARY_USER, BIOMETRIC_STRONG,
System.currentTimeMillis() + 1);
assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_STRONG)).isFalse();
assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_WEAK)).isFalse();
assertThat(
mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isFalse();
}
@Test
public void testTimedLockoutAfterDuration() {
assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_STRONG)).isTrue();
assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue();
assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isTrue();
when(mClock.millis()).thenReturn(0L);
mLockoutState.increaseLockoutTime(PRIMARY_USER, BIOMETRIC_STRONG, 1);
assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_STRONG)).isFalse();
assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_WEAK)).isFalse();
assertThat(
mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isFalse();
when(mClock.millis()).thenReturn(2L);
assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_STRONG)).isTrue();
assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue();
assertThat(mLockoutState.canUserAuthenticate(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isTrue();
}
}

View File

@@ -44,6 +44,7 @@ import androidx.test.platform.app.InstrumentationRegistry;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.AuthSessionCoordinator;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.LockoutCache;
@@ -95,6 +96,8 @@ public class FaceAuthenticationClientTest {
private ActivityTaskManager mActivityTaskManager;
@Mock
private ICancellationSignal mCancellationSignal;
@Mock
private AuthSessionCoordinator mAuthSessionCoordinator;
@Captor
private ArgumentCaptor<OperationContext> mOperationContextCaptor;
@@ -105,6 +108,7 @@ public class FaceAuthenticationClientTest {
public void setup() {
when(mBiometricContext.updateContext(any(), anyBoolean())).thenAnswer(
i -> i.getArgument(0));
when(mBiometricContext.getAuthSessionCoordinator()).thenReturn(mAuthSessionCoordinator);
}
@Test
@@ -158,7 +162,8 @@ public class FaceAuthenticationClientTest {
false /* requireConfirmation */, 9 /* sensorId */,
mBiometricLogger, mBiometricContext, true /* isStrongBiometric */,
mUsageStats, mLockoutCache, false /* allowBackgroundAuthentication */,
false /* isKeyguardBypassEnabled */, null /* sensorPrivacyManager */) {
false /* isKeyguardBypassEnabled */, null /* sensorPrivacyManager */,
0 /* biometricStrength */) {
@Override
protected ActivityTaskManager getActivityTaskManager() {
return mActivityTaskManager;

View File

@@ -34,6 +34,7 @@ import androidx.test.filters.SmallTest;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.AuthSessionCoordinator;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
@@ -71,6 +72,8 @@ public class SensorTest {
private BiometricLogger mBiometricLogger;
@Mock
private BiometricContext mBiometricContext;
@Mock
private AuthSessionCoordinator mAuthSessionCoordinator;
private final TestLooper mLooper = new TestLooper();
private final LockoutCache mLockoutCache = new LockoutCache();
@@ -84,6 +87,8 @@ public class SensorTest {
when(mContext.getSystemService(Context.BIOMETRIC_SERVICE)).thenReturn(mBiometricService);
when(mBiometricContext.getAuthSessionCoordinator()).thenReturn(mAuthSessionCoordinator);
mScheduler = new UserAwareBiometricScheduler(TAG,
new Handler(mLooper.getLooper()),
BiometricScheduler.SENSOR_TYPE_FACE,
@@ -107,7 +112,7 @@ public class SensorTest {
mScheduler.scheduleClientMonitor(new FaceResetLockoutClient(mContext,
() -> new AidlSession(1, mSession, USER_ID, mHalCallback),
USER_ID, TAG, SENSOR_ID, mBiometricLogger, mBiometricContext,
HAT, mLockoutCache, mLockoutResetDispatcher));
HAT, mLockoutCache, mLockoutResetDispatcher, 0 /* biometricStrength */));
mLooper.dispatchAll();
verifyNotLocked();

View File

@@ -58,6 +58,7 @@ import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.log.CallbackWithProbe;
import com.android.server.biometrics.log.Probe;
import com.android.server.biometrics.sensors.AuthSessionCoordinator;
import com.android.server.biometrics.sensors.ClientMonitorCallback;
import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter;
import com.android.server.biometrics.sensors.LockoutCache;
@@ -127,6 +128,8 @@ public class FingerprintAuthenticationClientTest {
private ICancellationSignal mCancellationSignal;
@Mock
private Probe mLuxProbe;
@Mock
private AuthSessionCoordinator mAuthSessionCoordinator;
@Captor
private ArgumentCaptor<OperationContext> mOperationContextCaptor;
@Captor
@@ -138,6 +141,7 @@ public class FingerprintAuthenticationClientTest {
@Before
public void setup() {
mContext.addMockSystemService(BiometricManager.class, mBiometricManager);
when(mBiometricContext.getAuthSessionCoordinator()).thenReturn(mAuthSessionCoordinator);
when(mBiometricLogger.getAmbientLightProbe(anyBoolean())).thenAnswer(i ->
new CallbackWithProbe<>(mLuxProbe, i.getArgument(0)));
when(mBiometricContext.updateContext(any(), anyBoolean())).thenAnswer(
@@ -496,7 +500,7 @@ public class FingerprintAuthenticationClientTest {
null /* taskStackListener */, mLockoutCache,
mUdfpsOverlayController, mSideFpsController, allowBackgroundAuthentication,
mSensorProps,
new Handler(mLooper.getLooper())) {
new Handler(mLooper.getLooper()), 0 /* biometricStrength */) {
@Override
protected ActivityTaskManager getActivityTaskManager() {
return mActivityTaskManager;

View File

@@ -34,6 +34,7 @@ import androidx.test.filters.SmallTest;
import com.android.server.biometrics.log.BiometricContext;
import com.android.server.biometrics.log.BiometricLogger;
import com.android.server.biometrics.sensors.AuthSessionCoordinator;
import com.android.server.biometrics.sensors.BiometricScheduler;
import com.android.server.biometrics.sensors.LockoutCache;
import com.android.server.biometrics.sensors.LockoutResetDispatcher;
@@ -71,6 +72,8 @@ public class SensorTest {
private BiometricLogger mLogger;
@Mock
private BiometricContext mBiometricContext;
@Mock
private AuthSessionCoordinator mAuthSessionCoordinator;
private final TestLooper mLooper = new TestLooper();
private final LockoutCache mLockoutCache = new LockoutCache();
@@ -83,6 +86,7 @@ public class SensorTest {
MockitoAnnotations.initMocks(this);
when(mContext.getSystemService(Context.BIOMETRIC_SERVICE)).thenReturn(mBiometricService);
when(mBiometricContext.getAuthSessionCoordinator()).thenReturn(mAuthSessionCoordinator);
mScheduler = new UserAwareBiometricScheduler(TAG,
new Handler(mLooper.getLooper()),
@@ -107,7 +111,7 @@ public class SensorTest {
mScheduler.scheduleClientMonitor(new FingerprintResetLockoutClient(mContext,
() -> new AidlSession(1, mSession, USER_ID, mHalCallback),
USER_ID, TAG, SENSOR_ID, mLogger, mBiometricContext, HAT, mLockoutCache,
mLockoutResetDispatcher));
mLockoutResetDispatcher, 0 /* biometricStrength */));
mLooper.dispatchAll();
verifyNotLocked();