Adding lockout coordinator
Adding the building blocks for the lockout coordinator. This class will be used to manage & coordinate lockouts/resetLockouts across users/biometrics. Test: atest AuthResultCoordinatorTest AuthSessionCoordinatorTest MultiBiometricLockoutStateTest Bug: 244355277 Change-Id: I1ffbd97eb06687f83f85f0c7b57a1bef65e0b141
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.biometrics.sensors;
|
||||
|
||||
import android.hardware.biometrics.BiometricManager;
|
||||
|
||||
class AuthResult {
|
||||
static final int FAILED = 0;
|
||||
static final int LOCKED_OUT = 1;
|
||||
static final int AUTHENTICATED = 2;
|
||||
private final int mStatus;
|
||||
private final int mBiometricStrength;
|
||||
|
||||
AuthResult(int status, @BiometricManager.Authenticators.Types int strength) {
|
||||
mStatus = status;
|
||||
mBiometricStrength = strength;
|
||||
}
|
||||
|
||||
int getStatus() {
|
||||
return mStatus;
|
||||
}
|
||||
|
||||
int getBiometricStrength() {
|
||||
return mBiometricStrength;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.biometrics.sensors;
|
||||
|
||||
import android.hardware.biometrics.BiometricManager.Authenticators;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A class that takes in a series of authentication attempts (successes, failures, lockouts)
|
||||
* across different biometric strengths (convenience, weak, strong) and returns a single AuthResult.
|
||||
*
|
||||
* The AuthResult will be the strongest biometric operation that occurred amongst all reported
|
||||
* operations, and if multiple such operations exist, it will favor a successful authentication.
|
||||
*/
|
||||
class AuthResultCoordinator {
|
||||
|
||||
private static final String TAG = "AuthResultCoordinator";
|
||||
private final List<AuthResult> mOperations;
|
||||
|
||||
AuthResultCoordinator() {
|
||||
mOperations = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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));
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains an auth result & strength from a current set of biometric operations.
|
||||
*/
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.biometrics.sensors;
|
||||
|
||||
import android.hardware.biometrics.BiometricManager.Authenticators;
|
||||
import android.util.Slog;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Coordinates lockout counter enforcement for all types of biometric strengths across all users.
|
||||
*
|
||||
* 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 {
|
||||
private static final String TAG = "AuthSessionCoordinator";
|
||||
|
||||
private final Set<Integer> mAuthOperations;
|
||||
|
||||
private int mUserId;
|
||||
private boolean mIsAuthenticating;
|
||||
private AuthResultCoordinator mAuthResultCoordinator;
|
||||
private MultiBiometricLockoutState mMultiBiometricLockoutState;
|
||||
|
||||
AuthSessionCoordinator() {
|
||||
mAuthOperations = new HashSet<>();
|
||||
mAuthResultCoordinator = new AuthResultCoordinator();
|
||||
mMultiBiometricLockoutState = new MultiBiometricLockoutState();
|
||||
}
|
||||
|
||||
/**
|
||||
* A Call indicating that an auth session has started
|
||||
*/
|
||||
void onAuthSessionStarted(int userId) {
|
||||
mAuthOperations.clear();
|
||||
mUserId = userId;
|
||||
mIsAuthenticating = true;
|
||||
mAuthResultCoordinator.resetState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends the current auth session and updates the lockout state.
|
||||
*
|
||||
* This can happen two ways.
|
||||
* 1. Manually calling this API
|
||||
* 2. If authStartedFor() was called, and all authentication attempts finish.
|
||||
*/
|
||||
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());
|
||||
}
|
||||
mAuthResultCoordinator.resetState();
|
||||
mIsAuthenticating = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if a user can authenticate with a given strength.
|
||||
*/
|
||||
boolean getCanAuthFor(int userId, @Authenticators.Types int strength) {
|
||||
return mMultiBiometricLockoutState.canUserAuthenticate(userId, strength);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void authStartedFor(int userId, int sensorId) {
|
||||
if (!mIsAuthenticating) {
|
||||
onAuthSessionStarted(userId);
|
||||
}
|
||||
|
||||
if (mAuthOperations.contains(sensorId)) {
|
||||
Slog.e(TAG, "Error, authStartedFor(" + sensorId + ") without being finished");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mUserId != userId) {
|
||||
Slog.e(TAG, "Error authStartedFor(" + userId + ") Incorrect userId, expected" + mUserId
|
||||
+ ", ignoring...");
|
||||
return;
|
||||
}
|
||||
|
||||
mAuthOperations.add(sensorId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void authenticatedFor(int userId, @Authenticators.Types int biometricStrength,
|
||||
int sensorId) {
|
||||
mAuthResultCoordinator.authenticatedFor(biometricStrength);
|
||||
attemptToFinish(userId, sensorId,
|
||||
"authenticatedFor(userId=" + userId + ", biometricStrength=" + biometricStrength
|
||||
+ ", sensorId=" + sensorId + "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lockedOutFor(int userId, @Authenticators.Types int biometricStrength,
|
||||
int sensorId) {
|
||||
mAuthResultCoordinator.lockedOutFor(biometricStrength);
|
||||
attemptToFinish(userId, sensorId,
|
||||
"lockOutFor(userId=" + userId + ", biometricStrength=" + biometricStrength
|
||||
+ ", sensorId=" + sensorId + "");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void authEndedFor(int userId, @Authenticators.Types int biometricStrength,
|
||||
int sensorId) {
|
||||
mAuthResultCoordinator.authEndedFor(biometricStrength);
|
||||
attemptToFinish(userId, sensorId,
|
||||
"authEndedFor(userId=" + userId + " ,biometricStrength=" + biometricStrength
|
||||
+ ", sensorId=" + sensorId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetLockoutFor(int userId, @Authenticators.Types int biometricStrength) {
|
||||
mMultiBiometricLockoutState.onUserUnlocked(userId, biometricStrength);
|
||||
}
|
||||
|
||||
private void attemptToFinish(int userId, int sensorId, String description) {
|
||||
boolean didFail = false;
|
||||
if (!mAuthOperations.contains(sensorId)) {
|
||||
Slog.e(TAG, "Error unable to find auth operation : " + description);
|
||||
didFail = true;
|
||||
}
|
||||
if (userId != mUserId) {
|
||||
Slog.e(TAG, "Error mismatched userId, expected=" + mUserId + " for " + description);
|
||||
didFail = true;
|
||||
}
|
||||
if (didFail) {
|
||||
return;
|
||||
}
|
||||
mAuthOperations.remove(sensorId);
|
||||
if (mIsAuthenticating && mAuthOperations.isEmpty()) {
|
||||
endAuthSession();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.biometrics.sensors;
|
||||
|
||||
import android.hardware.biometrics.BiometricManager.Authenticators;
|
||||
|
||||
/**
|
||||
* An interface that listens to authentication events.
|
||||
*/
|
||||
interface AuthSessionListener {
|
||||
/**
|
||||
* Indicates an auth operation has started for a given user and sensor.
|
||||
*/
|
||||
void authStartedFor(int userId, int sensorId);
|
||||
|
||||
/**
|
||||
* Indicates a successful authentication occurred for a sensor of a given strength.
|
||||
*/
|
||||
void authenticatedFor(int userId, @Authenticators.Types int biometricStrength, int sensorId);
|
||||
|
||||
/**
|
||||
* Indicates authentication ended for a sensor of a given strength.
|
||||
*/
|
||||
void authEndedFor(int userId, @Authenticators.Types int biometricStrength, int sensorId);
|
||||
|
||||
/**
|
||||
* Indicates a lockout occurred for a sensor of a given strength.
|
||||
*/
|
||||
void lockedOutFor(int userId, @Authenticators.Types int biometricStrength, int sensorId);
|
||||
|
||||
/**
|
||||
* Indicates that a reset lockout has happened for a given strength.
|
||||
*/
|
||||
void resetLockoutFor(int uerId, @Authenticators.Types int biometricStrength);
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.biometrics.sensors;
|
||||
|
||||
import static android.hardware.biometrics.BiometricManager.Authenticators;
|
||||
import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE;
|
||||
import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_STRONG;
|
||||
import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_WEAK;
|
||||
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This class is used as a system to store the state of each
|
||||
* {@link Authenticators.Types} status for every user.
|
||||
*/
|
||||
class MultiBiometricLockoutState {
|
||||
|
||||
private static final String TAG = "MultiBiometricLockoutState";
|
||||
private static final Map<Integer, List<Integer>> PRECEDENCE;
|
||||
|
||||
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<>();
|
||||
}
|
||||
|
||||
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);
|
||||
return lockOutMap;
|
||||
}
|
||||
|
||||
private Map<Integer, Boolean> getAuthMapForUser(int userId) {
|
||||
if (!mCanUserAuthenticate.containsKey(userId)) {
|
||||
mCanUserAuthenticate.put(userId, createLockedOutMap());
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if a user can perform an authentication operation with a given
|
||||
* {@link Authenticators.Types}
|
||||
*
|
||||
* @param userId The user.
|
||||
* @param strength The strength of biometric that is requested to authenticate.
|
||||
* @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);
|
||||
Slog.d(TAG, "canUserAuthenticate(userId=" + userId + ", strength=" + strength + ") ="
|
||||
+ canAuthenticate);
|
||||
return canAuthenticate;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.biometrics.sensors;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.hardware.biometrics.BiometricManager;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class AuthResultCoordinatorTest {
|
||||
private AuthResultCoordinator mAuthResultCoordinator;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
mAuthResultCoordinator = new AuthResultCoordinator();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultMessage() {
|
||||
checkResult(mAuthResultCoordinator.getResult(),
|
||||
AuthResult.FAILED,
|
||||
BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSingleMessageCoordinator() {
|
||||
mAuthResultCoordinator.authenticatedFor(
|
||||
BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE);
|
||||
checkResult(mAuthResultCoordinator.getResult(),
|
||||
AuthResult.AUTHENTICATED,
|
||||
BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLockout() {
|
||||
mAuthResultCoordinator.lockedOutFor(
|
||||
BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE);
|
||||
checkResult(mAuthResultCoordinator.getResult(),
|
||||
AuthResult.LOCKED_OUT,
|
||||
BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHigherStrengthPrecedence() {
|
||||
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);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAuthPrecedence() {
|
||||
mAuthResultCoordinator.authenticatedFor(
|
||||
BiometricManager.Authenticators.BIOMETRIC_WEAK);
|
||||
mAuthResultCoordinator.lockedOutFor(
|
||||
BiometricManager.Authenticators.BIOMETRIC_WEAK);
|
||||
checkResult(mAuthResultCoordinator.getResult(),
|
||||
AuthResult.AUTHENTICATED,
|
||||
BiometricManager.Authenticators.BIOMETRIC_WEAK);
|
||||
|
||||
}
|
||||
|
||||
void checkResult(AuthResult res, int status,
|
||||
@BiometricManager.Authenticators.Types int strength) {
|
||||
assertThat(res.getStatus()).isEqualTo(status);
|
||||
assertThat(res.getBiometricStrength()).isEqualTo(strength);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.biometrics.sensors;
|
||||
|
||||
import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE;
|
||||
import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_STRONG;
|
||||
import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_WEAK;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
@Presubmit
|
||||
@SmallTest
|
||||
public class AuthSessionCoordinatorTest {
|
||||
private static final int PRIMARY_USER = 0;
|
||||
private static final int SECONDARY_USER = 10;
|
||||
|
||||
private AuthSessionCoordinator mCoordinator;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
mCoordinator = new AuthSessionCoordinator();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUserUnlocked() {
|
||||
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);
|
||||
|
||||
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 testUserCanAuthDuringLockoutOfSameSession() {
|
||||
mCoordinator.resetLockoutFor(PRIMARY_USER, BIOMETRIC_STRONG);
|
||||
|
||||
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);
|
||||
|
||||
assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isTrue();
|
||||
assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue();
|
||||
assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiUserAuth() {
|
||||
mCoordinator.resetLockoutFor(PRIMARY_USER, BIOMETRIC_STRONG);
|
||||
|
||||
assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isTrue();
|
||||
assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue();
|
||||
assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_STRONG)).isTrue();
|
||||
|
||||
assertThat(mCoordinator.getCanAuthFor(SECONDARY_USER, BIOMETRIC_CONVENIENCE)).isFalse();
|
||||
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);
|
||||
|
||||
assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_CONVENIENCE)).isTrue();
|
||||
assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue();
|
||||
assertThat(mCoordinator.getCanAuthFor(PRIMARY_USER, BIOMETRIC_WEAK)).isTrue();
|
||||
|
||||
assertThat(mCoordinator.getCanAuthFor(SECONDARY_USER, BIOMETRIC_CONVENIENCE)).isFalse();
|
||||
assertThat(mCoordinator.getCanAuthFor(SECONDARY_USER, BIOMETRIC_WEAK)).isFalse();
|
||||
assertThat(mCoordinator.getCanAuthFor(SECONDARY_USER, BIOMETRIC_STRONG)).isFalse();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.biometrics.sensors;
|
||||
|
||||
import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE;
|
||||
import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_STRONG;
|
||||
import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_WEAK;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@SmallTest
|
||||
@Presubmit
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@TestableLooper.RunWithLooper(setAsMainLooper = true)
|
||||
public class MultiBiometricLockoutStateTest {
|
||||
private static final int PRIMARY_USER = 0;
|
||||
private MultiBiometricLockoutState mCoordinator;
|
||||
|
||||
private void unlockAllBiometrics() {
|
||||
unlockAllBiometrics(mCoordinator, 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();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
mCoordinator = new MultiBiometricLockoutState();
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void multiUser_userOneDoesNotAffectUserTwo() {
|
||||
final int userOne = 1;
|
||||
final int userTwo = 2;
|
||||
MultiBiometricLockoutState coordinator = new MultiBiometricLockoutState();
|
||||
lockoutAllBiometrics(coordinator, userOne);
|
||||
lockoutAllBiometrics(coordinator, 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();
|
||||
|
||||
assertThat(coordinator.canUserAuthenticate(userTwo, BIOMETRIC_STRONG)).isFalse();
|
||||
assertThat(coordinator.canUserAuthenticate(userTwo, BIOMETRIC_WEAK)).isFalse();
|
||||
assertThat(coordinator.canUserAuthenticate(userTwo, BIOMETRIC_CONVENIENCE)).isFalse();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user