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