1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.biometrics.sensors; 18 19 import static android.hardware.biometrics.BiometricManager.Authenticators; 20 import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE; 21 import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_STRONG; 22 import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_WEAK; 23 24 import android.hardware.biometrics.BiometricManager; 25 import android.util.Slog; 26 27 import java.time.Clock; 28 import java.util.HashMap; 29 import java.util.Map; 30 import java.util.stream.Collectors; 31 32 /** 33 * This class is used as a system to store the state of each 34 * {@link Authenticators.Types} status for every user. 35 * 36 * Note that initially all biomertics are unlocked, meaning users can authenticate 37 * with each strength. 38 */ 39 class MultiBiometricLockoutState { 40 41 private static final String TAG = "MultiBiometricLockoutState"; 42 private final Map<Integer, Map<Integer, AuthenticatorState>> mCanUserAuthenticate; 43 private final Clock mClock; 44 MultiBiometricLockoutState(Clock clock)45 MultiBiometricLockoutState(Clock clock) { 46 mCanUserAuthenticate = new HashMap<>(); 47 mClock = clock; 48 } 49 createUnlockedMap()50 private Map<Integer, AuthenticatorState> createUnlockedMap() { 51 Map<Integer, AuthenticatorState> lockOutMap = new HashMap<>(); 52 lockOutMap.put(BIOMETRIC_STRONG, 53 new AuthenticatorState(BIOMETRIC_STRONG, false, false)); 54 lockOutMap.put(BIOMETRIC_WEAK, 55 new AuthenticatorState(BIOMETRIC_WEAK, false, false)); 56 lockOutMap.put(BIOMETRIC_CONVENIENCE, 57 new AuthenticatorState(BIOMETRIC_CONVENIENCE, false, false)); 58 return lockOutMap; 59 } 60 getAuthMapForUser(int userId)61 private Map<Integer, AuthenticatorState> getAuthMapForUser(int userId) { 62 if (!mCanUserAuthenticate.containsKey(userId)) { 63 mCanUserAuthenticate.put(userId, createUnlockedMap()); 64 } 65 return mCanUserAuthenticate.get(userId); 66 } 67 setPermanentLockOut(int userId, @Authenticators.Types int strength)68 void setPermanentLockOut(int userId, @Authenticators.Types int strength) { 69 final Map<Integer, AuthenticatorState> authMap = getAuthMapForUser(userId); 70 switch (strength) { 71 case Authenticators.BIOMETRIC_STRONG: 72 authMap.get(BIOMETRIC_STRONG).mPermanentlyLockedOut = true; 73 // fall through 74 case Authenticators.BIOMETRIC_WEAK: 75 authMap.get(BIOMETRIC_WEAK).mPermanentlyLockedOut = true; 76 // fall through 77 case Authenticators.BIOMETRIC_CONVENIENCE: 78 authMap.get(BIOMETRIC_CONVENIENCE).mPermanentlyLockedOut = true; 79 return; 80 default: 81 Slog.e(TAG, "increaseLockoutTime called for invalid strength : " + strength); 82 } 83 } 84 clearPermanentLockOut(int userId, @Authenticators.Types int strength)85 void clearPermanentLockOut(int userId, @Authenticators.Types int strength) { 86 final Map<Integer, AuthenticatorState> authMap = getAuthMapForUser(userId); 87 switch (strength) { 88 case Authenticators.BIOMETRIC_STRONG: 89 authMap.get(BIOMETRIC_STRONG).mPermanentlyLockedOut = false; 90 // fall through 91 case Authenticators.BIOMETRIC_WEAK: 92 authMap.get(BIOMETRIC_WEAK).mPermanentlyLockedOut = false; 93 // fall through 94 case Authenticators.BIOMETRIC_CONVENIENCE: 95 authMap.get(BIOMETRIC_CONVENIENCE).mPermanentlyLockedOut = false; 96 return; 97 default: 98 Slog.e(TAG, "increaseLockoutTime called for invalid strength : " + strength); 99 } 100 } 101 setTimedLockout(int userId, @Authenticators.Types int strength)102 void setTimedLockout(int userId, @Authenticators.Types int strength) { 103 final Map<Integer, AuthenticatorState> authMap = getAuthMapForUser(userId); 104 switch (strength) { 105 case Authenticators.BIOMETRIC_STRONG: 106 authMap.get(BIOMETRIC_STRONG).mTimedLockout = true; 107 // fall through 108 case Authenticators.BIOMETRIC_WEAK: 109 authMap.get(BIOMETRIC_WEAK).mTimedLockout = true; 110 // fall through 111 case Authenticators.BIOMETRIC_CONVENIENCE: 112 authMap.get(BIOMETRIC_CONVENIENCE).mTimedLockout = true; 113 return; 114 default: 115 Slog.e(TAG, "increaseLockoutTime called for invalid strength : " + strength); 116 } 117 } 118 clearTimedLockout(int userId, @Authenticators.Types int strength)119 void clearTimedLockout(int userId, @Authenticators.Types int strength) { 120 final Map<Integer, AuthenticatorState> authMap = getAuthMapForUser(userId); 121 switch (strength) { 122 case Authenticators.BIOMETRIC_STRONG: 123 authMap.get(BIOMETRIC_STRONG).mTimedLockout = false; 124 // fall through 125 case Authenticators.BIOMETRIC_WEAK: 126 authMap.get(BIOMETRIC_WEAK).mTimedLockout = false; 127 // fall through 128 case Authenticators.BIOMETRIC_CONVENIENCE: 129 authMap.get(BIOMETRIC_CONVENIENCE).mTimedLockout = false; 130 return; 131 default: 132 Slog.e(TAG, "increaseLockoutTime called for invalid strength : " + strength); 133 } 134 } 135 136 /** 137 * Retrieves the lockout state for a user of a specified strength. 138 * 139 * @param userId The user. 140 * @param strength The strength of biometric that is requested to authenticate. 141 */ 142 @LockoutTracker.LockoutMode getLockoutState(int userId, @Authenticators.Types int strength)143 int getLockoutState(int userId, @Authenticators.Types int strength) { 144 final Map<Integer, AuthenticatorState> authMap = getAuthMapForUser(userId); 145 if (!authMap.containsKey(strength)) { 146 Slog.e(TAG, "Error, getLockoutState for unknown strength: " + strength 147 + " returning LOCKOUT_NONE"); 148 return LockoutTracker.LOCKOUT_NONE; 149 } 150 final AuthenticatorState state = authMap.get(strength); 151 if (state.mPermanentlyLockedOut) { 152 return LockoutTracker.LOCKOUT_PERMANENT; 153 } else if (state.mTimedLockout) { 154 return LockoutTracker.LOCKOUT_TIMED; 155 } else { 156 return LockoutTracker.LOCKOUT_NONE; 157 } 158 } 159 160 @Override toString()161 public String toString() { 162 String dumpState = "Permanent Lockouts\n"; 163 final long time = mClock.millis(); 164 for (Map.Entry<Integer, Map<Integer, AuthenticatorState>> userState : 165 mCanUserAuthenticate.entrySet()) { 166 final int userId = userState.getKey(); 167 final Map<Integer, AuthenticatorState> map = userState.getValue(); 168 String prettyStr = map.entrySet().stream().map( 169 (Map.Entry<Integer, AuthenticatorState> entry) -> entry.getValue().toString( 170 time)).collect(Collectors.joining(", ")); 171 dumpState += "UserId=" + userId + ", {" + prettyStr + "}\n"; 172 } 173 return dumpState; 174 } 175 176 private static class AuthenticatorState { 177 private Integer mAuthenticatorType; 178 private boolean mPermanentlyLockedOut; 179 private boolean mTimedLockout; 180 AuthenticatorState(Integer authenticatorId, boolean permanentlyLockedOut, boolean timedLockout)181 AuthenticatorState(Integer authenticatorId, boolean permanentlyLockedOut, 182 boolean timedLockout) { 183 mAuthenticatorType = authenticatorId; 184 mPermanentlyLockedOut = permanentlyLockedOut; 185 mTimedLockout = timedLockout; 186 } 187 toString(long currentTime)188 String toString(long currentTime) { 189 final String timedLockout = mTimedLockout ? "true" : "false"; 190 final String permanentLockout = mPermanentlyLockedOut ? "true" : "false"; 191 return String.format("(%s, permanentLockout=%s, timedLockout=%s)", 192 BiometricManager.authenticatorToStr(mAuthenticatorType), permanentLockout, 193 timedLockout); 194 } 195 } 196 } 197