Loading services/core/java/com/android/server/biometrics/sensors/AuthResult.java 0 → 100644 +40 −0 Original line number Diff line number Diff line /* * 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; } } services/core/java/com/android/server/biometrics/sensors/AuthResultCoordinator.java 0 → 100644 +93 −0 Original line number Diff line number Diff line /* * 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(); } } services/core/java/com/android/server/biometrics/sensors/AuthSessionCoordinator.java 0 → 100644 +157 −0 Original line number Diff line number Diff line /* * 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(); } } } services/core/java/com/android/server/biometrics/sensors/AuthSessionListener.java 0 → 100644 +49 −0 Original line number Diff line number Diff line /* * 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); } services/core/java/com/android/server/biometrics/sensors/MultiBiometricLockoutState.java 0 → 100644 +117 −0 Original line number Diff line number Diff line /* * 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; } } Loading
services/core/java/com/android/server/biometrics/sensors/AuthResult.java 0 → 100644 +40 −0 Original line number Diff line number Diff line /* * 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; } }
services/core/java/com/android/server/biometrics/sensors/AuthResultCoordinator.java 0 → 100644 +93 −0 Original line number Diff line number Diff line /* * 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(); } }
services/core/java/com/android/server/biometrics/sensors/AuthSessionCoordinator.java 0 → 100644 +157 −0 Original line number Diff line number Diff line /* * 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(); } } }
services/core/java/com/android/server/biometrics/sensors/AuthSessionListener.java 0 → 100644 +49 −0 Original line number Diff line number Diff line /* * 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); }
services/core/java/com/android/server/biometrics/sensors/MultiBiometricLockoutState.java 0 → 100644 +117 −0 Original line number Diff line number Diff line /* * 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; } }