1 /*
2  * Copyright (C) 2023 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.hardware.face;
18 
19 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
20 
21 import android.annotation.Nullable;
22 import android.content.Context;
23 import android.hardware.biometrics.face.IFace;
24 import android.hardware.biometrics.face.SensorProps;
25 import android.os.Binder;
26 import android.os.Parcel;
27 import android.os.Parcelable;
28 import android.os.RemoteException;
29 import android.os.ServiceManager;
30 import android.util.Log;
31 import android.util.Slog;
32 
33 import androidx.annotation.NonNull;
34 
35 import java.util.ArrayList;
36 import java.util.HashMap;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.Optional;
40 
41 /**
42  * Provides the sensor props for face sensor, if available.
43  * @hide
44  */
45 public class FaceSensorConfigurations implements Parcelable {
46     private static final String TAG = "FaceSensorConfigurations";
47 
48     private final boolean mResetLockoutRequiresChallenge;
49     private final Map<String, SensorProps[]> mSensorPropsMap;
50 
51     public static final Creator<FaceSensorConfigurations> CREATOR =
52             new Creator<FaceSensorConfigurations>() {
53                 @Override
54                 public FaceSensorConfigurations createFromParcel(Parcel in) {
55                     return new FaceSensorConfigurations(in);
56                 }
57 
58                 @Override
59                 public FaceSensorConfigurations[] newArray(int size) {
60                     return new FaceSensorConfigurations[size];
61                 }
62             };
63 
FaceSensorConfigurations(boolean resetLockoutRequiresChallenge)64     public FaceSensorConfigurations(boolean resetLockoutRequiresChallenge) {
65         mResetLockoutRequiresChallenge = resetLockoutRequiresChallenge;
66         mSensorPropsMap = new HashMap<>();
67     }
68 
FaceSensorConfigurations(Parcel in)69     protected FaceSensorConfigurations(Parcel in) {
70         mResetLockoutRequiresChallenge = in.readByte() != 0;
71         mSensorPropsMap = in.readHashMap(null, String.class, SensorProps[].class);
72     }
73 
74     /**
75      * Process AIDL instances to extract sensor props and add it to the sensor map.
76      * @param aidlInstances available face AIDL instances
77      */
addAidlConfigs(@onNull String[] aidlInstances)78     public void addAidlConfigs(@NonNull String[] aidlInstances) {
79         for (String aidlInstance : aidlInstances) {
80             mSensorPropsMap.put(aidlInstance, null);
81         }
82     }
83 
84     /**
85      * Parse through HIDL configuration and add it to the sensor map.
86      */
addHidlConfigs(@onNull String[] hidlConfigStrings, @NonNull Context context)87     public void addHidlConfigs(@NonNull String[] hidlConfigStrings,
88             @NonNull Context context) {
89         final List<HidlFaceSensorConfig> hidlFaceSensorConfigs = new ArrayList<>();
90         for (String hidlConfig: hidlConfigStrings) {
91             final HidlFaceSensorConfig hidlFaceSensorConfig = new HidlFaceSensorConfig();
92             try {
93                 hidlFaceSensorConfig.parse(hidlConfig, context);
94             } catch (Exception e) {
95                 Log.e(TAG, "HIDL sensor configuration format is incorrect.");
96                 continue;
97             }
98             if (hidlFaceSensorConfig.getModality() == TYPE_FACE) {
99                 hidlFaceSensorConfigs.add(hidlFaceSensorConfig);
100             }
101         }
102         final String hidlHalInstanceName = "defaultHIDL";
103         mSensorPropsMap.put(hidlHalInstanceName, hidlFaceSensorConfigs.toArray(
104                 new SensorProps[hidlFaceSensorConfigs.size()]));
105     }
106 
107     /**
108      * Returns true if any face sensors have been added.
109      */
hasSensorConfigurations()110     public boolean hasSensorConfigurations() {
111         return mSensorPropsMap.size() > 0;
112     }
113 
114     /**
115      * Returns true if there is only a single face sensor configuration available.
116      */
isSingleSensorConfigurationPresent()117     public boolean isSingleSensorConfigurationPresent() {
118         return mSensorPropsMap.size() == 1;
119     }
120 
121     /**
122      * Checks if {@param instance} exists.
123      */
124     @Nullable
doesInstanceExist(String instance)125     public boolean doesInstanceExist(String instance) {
126         return mSensorPropsMap.containsKey(instance);
127     }
128 
129     /**
130      * Return the first HAL instance, which does not correspond to the given {@param instance}.
131      * If another instance is not available, then null is returned.
132      */
133     @Nullable
getSensorNameNotForInstance(String instance)134     public String getSensorNameNotForInstance(String instance) {
135         Optional<String> notAVirtualInstance = mSensorPropsMap.keySet().stream().filter(
136                 (instanceName) -> !instanceName.equals(instance)).findFirst();
137         return notAVirtualInstance.orElse(null);
138     }
139 
140     /**
141      * Returns the first instance that has been added to the map.
142      */
143     @Nullable
getSensorInstance()144     public String getSensorInstance() {
145         Optional<String> optionalInstance = mSensorPropsMap.keySet().stream().findFirst();
146         return optionalInstance.orElse(null);
147     }
148 
getResetLockoutRequiresChallenge()149     public boolean getResetLockoutRequiresChallenge() {
150         return mResetLockoutRequiresChallenge;
151     }
152 
153     @Override
describeContents()154     public int describeContents() {
155         return 0;
156     }
157 
158     @Override
writeToParcel(@onNull Parcel dest, int flags)159     public void writeToParcel(@NonNull Parcel dest, int flags) {
160         dest.writeByte((byte) (mResetLockoutRequiresChallenge ? 1 : 0));
161         dest.writeMap(mSensorPropsMap);
162     }
163 
164     /**
165      * Returns face sensor props for the HAL {@param instance}.
166      */
167     @Nullable
getSensorPropForInstance(String instance)168     public SensorProps[] getSensorPropForInstance(String instance) {
169         SensorProps[] props = mSensorPropsMap.get(instance);
170 
171         //Props should not be null for HIDL configs
172         if (props != null) {
173             return props;
174         }
175 
176         final String fqName = IFace.DESCRIPTOR + "/" + instance;
177         IFace face = IFace.Stub.asInterface(Binder.allowBlocking(
178                 ServiceManager.waitForDeclaredService(fqName)));
179         try {
180             if (face != null) {
181                 props = face.getSensorProps();
182             } else {
183                 Slog.e(TAG, "Unable to get declared service: " + fqName);
184             }
185         } catch (RemoteException e) {
186             Log.d(TAG, "Unable to get sensor properties!");
187         }
188         return props;
189     }
190 }
191