1 /*
2  * Copyright (C) 2020 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 android.server.biometrics;
18 
19 import androidx.annotation.NonNull;
20 
21 import com.android.server.biometrics.nano.BiometricSchedulerProto;
22 import com.android.server.biometrics.nano.BiometricsProto;
23 import com.android.server.biometrics.nano.SensorServiceStateProto;
24 import com.android.server.biometrics.nano.SensorStateProto;
25 import com.android.server.biometrics.nano.UserStateProto;
26 
27 import com.google.common.primitives.Ints;
28 
29 import java.util.HashMap;
30 import java.util.List;
31 import java.util.Map;
32 
33 /**
34  * The overall state for a list of sensors. This could be either:
35  *
36  * 1) A list of sensors from a single instance of a <Biometric>Service such as
37  * {@link com.android.server.biometrics.sensors.fingerprint.FingerprintService} or
38  * {@link com.android.server.biometrics.sensors.face.FaceService}, or
39  *
40  * 2) A list of sensors from multiple instances of <Biometric>Services.
41  *
42  * Note that a single service may provide multiple sensors.
43  */
44 public class SensorStates {
45 
46     @NonNull public final Map<Integer, SensorState> sensorStates;
47 
48     public static class SchedulerState {
49         private final int mCurrentOperation;
50         private final int mTotalOperations;
51         @NonNull private final List<Integer> mRecentOperations;
52 
parseFrom(@onNull BiometricSchedulerProto proto)53         public static SchedulerState parseFrom(@NonNull BiometricSchedulerProto proto) {
54             return new SchedulerState(proto.currentOperation, proto.totalOperations,
55                     Ints.asList(proto.recentOperations));
56         }
57 
SchedulerState(int currentOperation, int totalOperations, @NonNull List<Integer> recentOperations)58         public SchedulerState(int currentOperation, int totalOperations,
59                 @NonNull List<Integer> recentOperations) {
60             mCurrentOperation = currentOperation;
61             mTotalOperations = totalOperations;
62             mRecentOperations = recentOperations;
63         }
64 
65         @NonNull
getRecentOperations()66         public List<Integer> getRecentOperations() {
67             return mRecentOperations;
68         }
69     }
70 
71     public static class SensorState {
72         private final SchedulerState mSchedulerState;
73         private final int mModality;
74         private final int[] mModalityFlags;
75         private final int mCurrentStrength;
76         @NonNull private final Map<Integer, UserState> mUserStates;
77         private final boolean mResetLockoutRequiresHardwareAuthToken;
78         private final boolean mResetLockoutRequiresChallenge;
79 
SensorState(@onNull SchedulerState schedulerState, int modality, int[] modalityFlags, int currentStrength, @NonNull Map<Integer, UserState> userStates, boolean resetLockoutRequiresHardwareAuthToken, boolean resetLockoutRequiresChallenge)80         public SensorState(@NonNull SchedulerState schedulerState,
81                 int modality, int[] modalityFlags,
82                 int currentStrength, @NonNull Map<Integer, UserState> userStates,
83                 boolean resetLockoutRequiresHardwareAuthToken,
84                 boolean resetLockoutRequiresChallenge) {
85             mSchedulerState = schedulerState;
86             mModality = modality;
87             mModalityFlags = modalityFlags;
88             mCurrentStrength = currentStrength;
89             mUserStates = userStates;
90             mResetLockoutRequiresHardwareAuthToken = resetLockoutRequiresHardwareAuthToken;
91             mResetLockoutRequiresChallenge = resetLockoutRequiresChallenge;
92         }
93 
getSchedulerState()94         public SchedulerState getSchedulerState() {
95             return mSchedulerState;
96         }
97 
isBusy()98         public boolean isBusy() {
99             return mSchedulerState.mCurrentOperation != BiometricsProto.CM_NONE;
100         }
101 
getModality()102         public int getModality() {
103             return mModality;
104         }
105 
hasModalityFlag(int flag)106         public boolean hasModalityFlag(int flag) {
107             for (int f : mModalityFlags) {
108                 if (f == flag) {
109                     return true;
110                 }
111             }
112 
113             return false;
114         }
115 
getCurrentStrength()116         public int getCurrentStrength() {
117             return mCurrentStrength;
118         }
119 
getUserStates()120         @NonNull public Map<Integer, UserState> getUserStates() {
121             return mUserStates;
122         }
123 
isResetLockoutRequiresHardwareAuthToken()124         public boolean isResetLockoutRequiresHardwareAuthToken() {
125             return mResetLockoutRequiresHardwareAuthToken;
126         }
127 
isResetLockoutRequiresChallenge()128         public boolean isResetLockoutRequiresChallenge() {
129             return mResetLockoutRequiresChallenge;
130         }
131     }
132 
133     public static class UserState {
134         public final int numEnrolled;
135 
UserState(int numEnrolled)136         public UserState(int numEnrolled) {
137             this.numEnrolled = numEnrolled;
138         }
139     }
140 
141     @NonNull
parseFrom(@onNull SensorServiceStateProto proto)142     public static SensorStates parseFrom(@NonNull SensorServiceStateProto proto) {
143         final Map<Integer, SensorState> sensorStates = new HashMap<>();
144 
145         for (SensorStateProto sensorStateProto : proto.sensorStates) {
146             final Map<Integer, UserState> userStates = new HashMap<>();
147             for (UserStateProto userStateProto : sensorStateProto.userStates) {
148                 userStates.put(userStateProto.userId, new UserState(userStateProto.numEnrolled));
149             }
150 
151             final SchedulerState schedulerState =
152                     SchedulerState.parseFrom(sensorStateProto.scheduler);
153             final SensorState sensorState = new SensorState(schedulerState,
154                     sensorStateProto.modality, sensorStateProto.modalityFlags,
155                     sensorStateProto.currentStrength, userStates,
156                     sensorStateProto.resetLockoutRequiresHardwareAuthToken,
157                     sensorStateProto.resetLockoutRequiresChallenge);
158             sensorStates.put(sensorStateProto.sensorId, sensorState);
159         }
160 
161         return new SensorStates(sensorStates);
162     }
163 
164     /**
165      * Combines multiple {@link SensorStates} into a single instance.
166      */
167     @NonNull
merge(@onNull List<SensorStates> sensorServiceStates)168     public static SensorStates merge(@NonNull List<SensorStates> sensorServiceStates) {
169         final Map<Integer, SensorState> sensorStates = new HashMap<>();
170 
171         for (SensorStates sensorServiceState : sensorServiceStates) {
172             for (Integer sensorId : sensorServiceState.sensorStates.keySet()) {
173                 if (sensorStates.containsKey(sensorId)) {
174                     throw new IllegalStateException("Duplicate sensorId found: " + sensorId);
175                 }
176                 sensorStates.put(sensorId, sensorServiceState.sensorStates.get(sensorId));
177             }
178         }
179 
180         return new SensorStates(sensorStates);
181     }
182 
areAllSensorsIdle()183     public boolean areAllSensorsIdle() {
184         for (SensorState state : sensorStates.values()) {
185             if (state.isBusy()) {
186                 return false;
187             }
188         }
189 
190         return true;
191     }
192 
containsModality(int modality)193     public boolean containsModality(int modality) {
194         for (SensorState state : sensorStates.values()) {
195             if (state.getModality() == modality) {
196                 return true;
197             }
198         }
199 
200         return false;
201     }
202 
containsModalityFlag(int flag)203     public boolean containsModalityFlag(int flag) {
204         for (SensorState state : sensorStates.values()) {
205             if (state.hasModalityFlag(flag)) {
206                 return true;
207             }
208         }
209 
210         return false;
211     }
212 
SensorStates(@onNull Map<Integer, SensorState> sensorStates)213     private SensorStates(@NonNull Map<Integer, SensorState> sensorStates) {
214         this.sensorStates = sensorStates;
215     }
216 
217     @Override
toString()218     public String toString() {
219         final StringBuilder sb = new StringBuilder();
220 
221         for (Integer sensorId : sensorStates.keySet()) {
222             sb.append("{SensorId: ").append(sensorId);
223             sb.append(", Operation: ").append(sensorStates.get(sensorId)
224                     .getSchedulerState().mCurrentOperation);
225 
226             final Map<Integer, UserState> userStates = sensorStates.get(sensorId).getUserStates();
227             for (Integer userId : userStates.keySet()) {
228                 sb.append(", UserId: ").append(userId);
229                 sb.append(", NumEnrolled: ").append(userStates.get(userId).numEnrolled);
230             }
231             sb.append("} ");
232         }
233         return sb.toString();
234     }
235 }
236