blob: 6605d49ece9bb4bba3c4d2ae5ebfc5947b347920 [file] [log] [blame]
/*
* 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.hardware.biometrics.BiometricManager;
import android.util.Slog;
import java.time.Clock;
import java.util.HashMap;
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 final Map<Integer, Map<Integer, AuthenticatorState>> mCanUserAuthenticate;
private final Clock mClock;
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, AuthenticatorState> getAuthMapForUser(int userId) {
if (!mCanUserAuthenticate.containsKey(userId)) {
mCanUserAuthenticate.put(userId, createUnlockedMap());
}
return mCanUserAuthenticate.get(userId);
}
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;
}
}
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);
}
}
/**
* 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).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);
}
}
}