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