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.hardware.biometrics.face.virtualhal.IVirtualHal; 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 import android.util.Slog; 33 34 import androidx.annotation.NonNull; 35 36 import java.util.ArrayList; 37 import java.util.HashMap; 38 import java.util.List; 39 import java.util.Map; 40 import java.util.Optional; 41 42 /** 43 * Provides the sensor props for face sensor, if available. 44 * @hide 45 */ 46 public class FaceSensorConfigurations implements Parcelable { 47 private static final String TAG = "FaceSensorConfigurations"; 48 49 private final boolean mResetLockoutRequiresChallenge; 50 private final Map<String, SensorProps[]> mSensorPropsMap; 51 52 public static final Creator<FaceSensorConfigurations> CREATOR = 53 new Creator<FaceSensorConfigurations>() { 54 @Override 55 public FaceSensorConfigurations createFromParcel(Parcel in) { 56 return new FaceSensorConfigurations(in); 57 } 58 59 @Override 60 public FaceSensorConfigurations[] newArray(int size) { 61 return new FaceSensorConfigurations[size]; 62 } 63 }; 64 FaceSensorConfigurations(boolean resetLockoutRequiresChallenge)65 public FaceSensorConfigurations(boolean resetLockoutRequiresChallenge) { 66 mResetLockoutRequiresChallenge = resetLockoutRequiresChallenge; 67 mSensorPropsMap = new HashMap<>(); 68 } 69 FaceSensorConfigurations(Parcel in)70 protected FaceSensorConfigurations(Parcel in) { 71 mResetLockoutRequiresChallenge = in.readByte() != 0; 72 mSensorPropsMap = in.readHashMap(null, String.class, SensorProps[].class); 73 } 74 75 /** 76 * Process AIDL instances to extract sensor props and add it to the sensor map. 77 * @param aidlInstances available face AIDL instances 78 */ addAidlConfigs(@onNull String[] aidlInstances)79 public void addAidlConfigs(@NonNull String[] aidlInstances) { 80 for (String aidlInstance : aidlInstances) { 81 mSensorPropsMap.put(aidlInstance, null); 82 } 83 } 84 85 /** 86 * Parse through HIDL configuration and add it to the sensor map. 87 */ addHidlConfigs(@onNull String[] hidlConfigStrings, @NonNull Context context)88 public void addHidlConfigs(@NonNull String[] hidlConfigStrings, 89 @NonNull Context context) { 90 final List<HidlFaceSensorConfig> hidlFaceSensorConfigs = new ArrayList<>(); 91 for (String hidlConfig: hidlConfigStrings) { 92 final HidlFaceSensorConfig hidlFaceSensorConfig = new HidlFaceSensorConfig(); 93 try { 94 hidlFaceSensorConfig.parse(hidlConfig, context); 95 } catch (Exception e) { 96 Log.e(TAG, "HIDL sensor configuration format is incorrect."); 97 continue; 98 } 99 if (hidlFaceSensorConfig.getModality() == TYPE_FACE) { 100 hidlFaceSensorConfigs.add(hidlFaceSensorConfig); 101 } 102 } 103 final String hidlHalInstanceName = "defaultHIDL"; 104 mSensorPropsMap.put(hidlHalInstanceName, hidlFaceSensorConfigs.toArray( 105 new SensorProps[hidlFaceSensorConfigs.size()])); 106 } 107 108 /** 109 * Returns true if any face 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 face 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 getResetLockoutRequiresChallenge()150 public boolean getResetLockoutRequiresChallenge() { 151 return mResetLockoutRequiresChallenge; 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) (mResetLockoutRequiresChallenge ? 1 : 0)); 162 dest.writeMap(mSensorPropsMap); 163 } 164 /** 165 * Remap fqName of VHAL because the `virtual` instance is registered 166 * with IVirtulalHal now (IFace previously) 167 * @param fqName fqName to be translated 168 * @return real fqName 169 */ remapFqName(String fqName)170 public static String remapFqName(String fqName) { 171 if (!fqName.contains(IFace.DESCRIPTOR + "/virtual")) { 172 return fqName; //no remap needed for real hardware HAL 173 } else { 174 //new Vhal instance name 175 return fqName.replace("IFace", "virtualhal.IVirtualHal"); 176 } 177 } 178 /** 179 * @param fqName aidl interface instance name 180 * @return aidl interface 181 */ getIFace(String fqName)182 public static IFace getIFace(String fqName) { 183 if (fqName.contains("virtual")) { 184 String fqNameMapped = remapFqName(fqName); 185 Slog.i(TAG, "getIFace fqName is mapped: " + fqName + "->" + fqNameMapped); 186 try { 187 IVirtualHal vhal = IVirtualHal.Stub.asInterface( 188 Binder.allowBlocking(ServiceManager.waitForService(fqNameMapped))); 189 return vhal.getFaceHal(); 190 } catch (RemoteException e) { 191 Slog.e(TAG, "Remote exception in vhal.getFaceHal() call" + fqNameMapped); 192 } 193 } 194 195 return IFace.Stub.asInterface( 196 Binder.allowBlocking(ServiceManager.waitForDeclaredService(fqName))); 197 } 198 199 200 /** 201 * Returns face sensor props for the HAL {@param instance}. 202 */ 203 @Nullable getSensorPropForInstance(String instance)204 public SensorProps[] getSensorPropForInstance(String instance) { 205 SensorProps[] props = mSensorPropsMap.get(instance); 206 207 //Props should not be null for HIDL configs 208 if (props != null) { 209 return props; 210 } 211 212 try { 213 final String fqName = IFace.DESCRIPTOR + "/" + instance; 214 final IFace fp = getIFace(fqName); 215 if (fp != null) { 216 props = fp.getSensorProps(); 217 } else { 218 Log.d(TAG, "IFace null for instance " + instance); 219 } 220 } catch (RemoteException e) { 221 Log.d(TAG, "Unable to get sensor properties!"); 222 } 223 return props; 224 } 225 } 226