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