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:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */);
|
||||
}
|
||||
|
||||
|
||||
@@ -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(() -> {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 */);
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user