diff options
52 files changed, 3807 insertions, 482 deletions
diff --git a/Android.bp b/Android.bp index 91f03e0036a3..bb9304819e80 100644 --- a/Android.bp +++ b/Android.bp @@ -97,6 +97,7 @@ filegroup { // AIDL sources from external directories ":android.hardware.biometrics.common-V4-java-source", ":android.hardware.biometrics.fingerprint-V3-java-source", + ":android.hardware.biometrics.face-V4-java-source", ":android.hardware.gnss-V2-java-source", ":android.hardware.graphics.common-V3-java-source", ":android.hardware.keymaster-V4-java-source", diff --git a/core/java/android/hardware/face/FaceSensorConfigurations.aidl b/core/java/android/hardware/face/FaceSensorConfigurations.aidl new file mode 100644 index 000000000000..26367b3c3dbf --- /dev/null +++ b/core/java/android/hardware/face/FaceSensorConfigurations.aidl @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hardware.face; + +parcelable FaceSensorConfigurations;
\ No newline at end of file diff --git a/core/java/android/hardware/face/FaceSensorConfigurations.java b/core/java/android/hardware/face/FaceSensorConfigurations.java new file mode 100644 index 000000000000..6ef692f81069 --- /dev/null +++ b/core/java/android/hardware/face/FaceSensorConfigurations.java @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.face; + +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE; + +import android.annotation.Nullable; +import android.content.Context; +import android.hardware.biometrics.face.IFace; +import android.hardware.biometrics.face.SensorProps; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.RemoteException; +import android.util.Log; +import android.util.Pair; +import android.util.Slog; + +import androidx.annotation.NonNull; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; + +/** + * Provides the sensor props for face sensor, if available. + * @hide + */ +public class FaceSensorConfigurations implements Parcelable { + private static final String TAG = "FaceSensorConfigurations"; + + private final boolean mResetLockoutRequiresChallenge; + private final Map<String, SensorProps[]> mSensorPropsMap; + + public static final Creator<FaceSensorConfigurations> CREATOR = + new Creator<FaceSensorConfigurations>() { + @Override + public FaceSensorConfigurations createFromParcel(Parcel in) { + return new FaceSensorConfigurations(in); + } + + @Override + public FaceSensorConfigurations[] newArray(int size) { + return new FaceSensorConfigurations[size]; + } + }; + + public FaceSensorConfigurations(boolean resetLockoutRequiresChallenge) { + mResetLockoutRequiresChallenge = resetLockoutRequiresChallenge; + mSensorPropsMap = new HashMap<>(); + } + + protected FaceSensorConfigurations(Parcel in) { + mResetLockoutRequiresChallenge = in.readByte() != 0; + mSensorPropsMap = in.readHashMap(null, String.class, SensorProps[].class); + } + + /** + * Process AIDL instances to extract sensor props and add it to the sensor map. + * @param aidlInstances available face AIDL instances + * @param getIFace function that provides the daemon for the specific instance + */ + public void addAidlConfigs(@NonNull String[] aidlInstances, + @NonNull Function<String, IFace> getIFace) { + for (String aidlInstance : aidlInstances) { + final String fqName = IFace.DESCRIPTOR + "/" + aidlInstance; + IFace face = getIFace.apply(fqName); + try { + if (face != null) { + mSensorPropsMap.put(aidlInstance, face.getSensorProps()); + } else { + Slog.e(TAG, "Unable to get declared service: " + fqName); + } + } catch (RemoteException e) { + Log.d(TAG, "Unable to get sensor properties!"); + } + } + } + + /** + * Parse through HIDL configuration and add it to the sensor map. + */ + public void addHidlConfigs(@NonNull String[] hidlConfigStrings, + @NonNull Context context) { + final List<HidlFaceSensorConfig> hidlFaceSensorConfigs = new ArrayList<>(); + for (String hidlConfig: hidlConfigStrings) { + final HidlFaceSensorConfig hidlFaceSensorConfig = new HidlFaceSensorConfig(); + try { + hidlFaceSensorConfig.parse(hidlConfig, context); + } catch (Exception e) { + Log.e(TAG, "HIDL sensor configuration format is incorrect."); + continue; + } + if (hidlFaceSensorConfig.getModality() == TYPE_FACE) { + hidlFaceSensorConfigs.add(hidlFaceSensorConfig); + } + } + final String hidlHalInstanceName = "defaultHIDL"; + mSensorPropsMap.put(hidlHalInstanceName, hidlFaceSensorConfigs.toArray( + new SensorProps[hidlFaceSensorConfigs.size()])); + } + + /** + * Returns true if any face sensors have been added. + */ + public boolean hasSensorConfigurations() { + return mSensorPropsMap.size() > 0; + } + + /** + * Returns true if there is only a single face sensor configuration available. + */ + public boolean isSingleSensorConfigurationPresent() { + return mSensorPropsMap.size() == 1; + } + + /** + * Return sensor props for the given instance. If instance is not available, + * then null is returned. + */ + @Nullable + public Pair<String, SensorProps[]> getSensorPairForInstance(String instance) { + if (mSensorPropsMap.containsKey(instance)) { + return new Pair<>(instance, mSensorPropsMap.get(instance)); + } + + return null; + } + + /** + * Return the first pair of instance and sensor props, which does not correspond to the given + * If instance is not available, then null is returned. + */ + @Nullable + public Pair<String, SensorProps[]> getSensorPairNotForInstance(String instance) { + Optional<String> notAVirtualInstance = mSensorPropsMap.keySet().stream().filter( + (instanceName) -> !instanceName.equals(instance)).findFirst(); + return notAVirtualInstance.map(this::getSensorPairForInstance).orElseGet( + this::getSensorPair); + } + + /** + * Returns the first pair of instance and sensor props that has been added to the map. + */ + @Nullable + public Pair<String, SensorProps[]> getSensorPair() { + Optional<String> optionalInstance = mSensorPropsMap.keySet().stream().findFirst(); + return optionalInstance.map(this::getSensorPairForInstance).orElse(null); + + } + + public boolean getResetLockoutRequiresChallenge() { + return mResetLockoutRequiresChallenge; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeByte((byte) (mResetLockoutRequiresChallenge ? 1 : 0)); + dest.writeMap(mSensorPropsMap); + } +} diff --git a/core/java/android/hardware/face/HidlFaceSensorConfig.java b/core/java/android/hardware/face/HidlFaceSensorConfig.java new file mode 100644 index 000000000000..cab146d0ff54 --- /dev/null +++ b/core/java/android/hardware/face/HidlFaceSensorConfig.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.face; + +import android.annotation.NonNull; +import android.content.Context; +import android.hardware.biometrics.BiometricAuthenticator; +import android.hardware.biometrics.BiometricManager; +import android.hardware.biometrics.common.CommonProps; +import android.hardware.biometrics.common.SensorStrength; +import android.hardware.biometrics.face.SensorProps; + +import com.android.internal.R; + +/** + * Parse HIDL face sensor config and map it to SensorProps.aidl to match AIDL. + * See core/res/res/values/config.xml config_biometric_sensors + * @hide + */ +public final class HidlFaceSensorConfig extends SensorProps { + private int mSensorId; + private int mModality; + private int mStrength; + + /** + * Parse through the config string and map it to SensorProps.aidl. + * @throws IllegalArgumentException when config string has unexpected format + */ + public void parse(@NonNull String config, @NonNull Context context) + throws IllegalArgumentException { + final String[] elems = config.split(":"); + if (elems.length < 3) { + throw new IllegalArgumentException(); + } + mSensorId = Integer.parseInt(elems[0]); + mModality = Integer.parseInt(elems[1]); + mStrength = Integer.parseInt(elems[2]); + mapHidlToAidlFaceSensorConfigurations(context); + } + + @BiometricAuthenticator.Modality + public int getModality() { + return mModality; + } + + private void mapHidlToAidlFaceSensorConfigurations(@NonNull Context context) { + commonProps = new CommonProps(); + commonProps.sensorId = mSensorId; + commonProps.sensorStrength = authenticatorStrengthToPropertyStrength(mStrength); + halControlsPreview = context.getResources().getBoolean( + R.bool.config_faceAuthSupportsSelfIllumination); + commonProps.maxEnrollmentsPerUser = context.getResources().getInteger( + R.integer.config_faceMaxTemplatesPerUser); + commonProps.componentInfo = null; + supportsDetectInteraction = false; + } + + private byte authenticatorStrengthToPropertyStrength( + @BiometricManager.Authenticators.Types int strength) { + switch (strength) { + case BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE: + return SensorStrength.CONVENIENCE; + case BiometricManager.Authenticators.BIOMETRIC_WEAK: + return SensorStrength.WEAK; + case BiometricManager.Authenticators.BIOMETRIC_STRONG: + return SensorStrength.STRONG; + default: + throw new IllegalArgumentException("Unknown strength: " + strength); + } + } +} diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl index 7080133dc597..0096877f548a 100644 --- a/core/java/android/hardware/face/IFaceService.aidl +++ b/core/java/android/hardware/face/IFaceService.aidl @@ -26,6 +26,7 @@ import android.hardware.face.IFaceServiceReceiver; import android.hardware.face.Face; import android.hardware.face.FaceAuthenticateOptions; import android.hardware.face.FaceSensorPropertiesInternal; +import android.hardware.face.FaceSensorConfigurations; import android.view.Surface; /** @@ -167,6 +168,10 @@ interface IFaceService { @EnforcePermission("USE_BIOMETRIC_INTERNAL") void registerAuthenticators(in List<FaceSensorPropertiesInternal> hidlSensors); + //Register all available face sensors. + @EnforcePermission("USE_BIOMETRIC_INTERNAL") + void registerAuthenticatorsLegacy(in FaceSensorConfigurations faceSensorConfigurations); + // Adds a callback which gets called when the service registers all of the face // authenticators. The callback is automatically removed after it's invoked. void addAuthenticatorsRegisteredCallback(IFaceAuthenticatorsRegisteredCallback callback); diff --git a/core/java/android/hardware/fingerprint/FingerprintSensorConfigurations.aidl b/core/java/android/hardware/fingerprint/FingerprintSensorConfigurations.aidl new file mode 100644 index 000000000000..ebb05dc88182 --- /dev/null +++ b/core/java/android/hardware/fingerprint/FingerprintSensorConfigurations.aidl @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hardware.fingerprint; + +parcelable FingerprintSensorConfigurations;
\ No newline at end of file diff --git a/core/java/android/hardware/fingerprint/FingerprintSensorConfigurations.java b/core/java/android/hardware/fingerprint/FingerprintSensorConfigurations.java new file mode 100644 index 000000000000..f214494a5d7b --- /dev/null +++ b/core/java/android/hardware/fingerprint/FingerprintSensorConfigurations.java @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.fingerprint; + +import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.hardware.biometrics.fingerprint.IFingerprint; +import android.hardware.biometrics.fingerprint.SensorProps; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.RemoteException; +import android.util.Log; +import android.util.Pair; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; + +/** + * Provides the sensor props for fingerprint sensor, if available. + * @hide + */ + +public class FingerprintSensorConfigurations implements Parcelable { + private static final String TAG = "FingerprintSensorConfigurations"; + + private final Map<String, SensorProps[]> mSensorPropsMap; + private final boolean mResetLockoutRequiresHardwareAuthToken; + + public static final Creator<FingerprintSensorConfigurations> CREATOR = + new Creator<>() { + @Override + public FingerprintSensorConfigurations createFromParcel(Parcel in) { + return new FingerprintSensorConfigurations(in); + } + + @Override + public FingerprintSensorConfigurations[] newArray(int size) { + return new FingerprintSensorConfigurations[size]; + } + }; + + public FingerprintSensorConfigurations(boolean resetLockoutRequiresHardwareAuthToken) { + mResetLockoutRequiresHardwareAuthToken = resetLockoutRequiresHardwareAuthToken; + mSensorPropsMap = new HashMap<>(); + } + + /** + * Process AIDL instances to extract sensor props and add it to the sensor map. + * @param aidlInstances available face AIDL instances + * @param getIFingerprint function that provides the daemon for the specific instance + */ + public void addAidlSensors(@NonNull String[] aidlInstances, + @NonNull Function<String, IFingerprint> getIFingerprint) { + for (String aidlInstance : aidlInstances) { + try { + final String fqName = IFingerprint.DESCRIPTOR + "/" + aidlInstance; + final IFingerprint fp = getIFingerprint.apply(fqName); + if (fp != null) { + SensorProps[] props = fp.getSensorProps(); + mSensorPropsMap.put(aidlInstance, props); + } else { + Log.d(TAG, "IFingerprint null for instance " + aidlInstance); + } + } catch (RemoteException e) { + Log.d(TAG, "Unable to get sensor properties!"); + } + } + } + + /** + * Parse through HIDL configuration and add it to the sensor map. + */ + public void addHidlSensors(@NonNull String[] hidlConfigStrings, + @NonNull Context context) { + final List<HidlFingerprintSensorConfig> hidlFingerprintSensorConfigs = new ArrayList<>(); + for (String hidlConfigString : hidlConfigStrings) { + final HidlFingerprintSensorConfig hidlFingerprintSensorConfig = + new HidlFingerprintSensorConfig(); + try { + hidlFingerprintSensorConfig.parse(hidlConfigString, context); + } catch (Exception e) { + Log.e(TAG, "HIDL sensor configuration format is incorrect."); + continue; + } + if (hidlFingerprintSensorConfig.getModality() == TYPE_FINGERPRINT) { + hidlFingerprintSensorConfigs.add(hidlFingerprintSensorConfig); + } + } + final String hidlHalInstanceName = "defaultHIDL"; + mSensorPropsMap.put(hidlHalInstanceName, + hidlFingerprintSensorConfigs.toArray( + new HidlFingerprintSensorConfig[hidlFingerprintSensorConfigs.size()])); + } + + protected FingerprintSensorConfigurations(Parcel in) { + mResetLockoutRequiresHardwareAuthToken = in.readByte() != 0; + mSensorPropsMap = in.readHashMap(null /* loader */, String.class, SensorProps[].class); + } + + /** + * Returns true if any fingerprint sensors have been added. + */ + public boolean hasSensorConfigurations() { + return mSensorPropsMap.size() > 0; + } + + /** + * Returns true if there is only a single fingerprint sensor configuration available. + */ + public boolean isSingleSensorConfigurationPresent() { + return mSensorPropsMap.size() == 1; + } + + /** + * Return sensor props for the given instance. If instance is not available, + * then null is returned. + */ + @Nullable + public Pair<String, SensorProps[]> getSensorPairForInstance(String instance) { + if (mSensorPropsMap.containsKey(instance)) { + return new Pair<>(instance, mSensorPropsMap.get(instance)); + } + + return null; + } + + /** + * Return the first pair of instance and sensor props, which does not correspond to the given + * If instance is not available, then null is returned. + */ + @Nullable + public Pair<String, SensorProps[]> getSensorPairNotForInstance(String instance) { + Optional<String> notAVirtualInstance = mSensorPropsMap.keySet().stream().filter( + (instanceName) -> !instanceName.equals(instance)).findFirst(); + return notAVirtualInstance.map(this::getSensorPairForInstance).orElseGet( + this::getSensorPair); + } + + /** + * Returns the first pair of instance and sensor props that has been added to the map. + */ + @Nullable + public Pair<String, SensorProps[]> getSensorPair() { + Optional<String> optionalInstance = mSensorPropsMap.keySet().stream().findFirst(); + return optionalInstance.map(this::getSensorPairForInstance).orElse(null); + + } + + public boolean getResetLockoutRequiresHardwareAuthToken() { + return mResetLockoutRequiresHardwareAuthToken; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeByte((byte) (mResetLockoutRequiresHardwareAuthToken ? 1 : 0)); + dest.writeMap(mSensorPropsMap); + } +} diff --git a/core/java/android/hardware/fingerprint/HidlFingerprintSensorConfig.java b/core/java/android/hardware/fingerprint/HidlFingerprintSensorConfig.java new file mode 100644 index 000000000000..d481153fc642 --- /dev/null +++ b/core/java/android/hardware/fingerprint/HidlFingerprintSensorConfig.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.fingerprint; + +import android.annotation.NonNull; +import android.content.Context; +import android.hardware.biometrics.BiometricAuthenticator; +import android.hardware.biometrics.BiometricManager; +import android.hardware.biometrics.common.CommonProps; +import android.hardware.biometrics.common.SensorStrength; +import android.hardware.biometrics.fingerprint.FingerprintSensorType; +import android.hardware.biometrics.fingerprint.SensorLocation; +import android.hardware.biometrics.fingerprint.SensorProps; + +import com.android.internal.R; +import com.android.internal.util.ArrayUtils; + +/** + * Parse HIDL fingerprint sensor config and map it to SensorProps.aidl to match AIDL. + * See core/res/res/values/config.xml config_biometric_sensors + * @hide + */ +public final class HidlFingerprintSensorConfig extends SensorProps { + private int mSensorId; + private int mModality; + private int mStrength; + + /** + * Parse through the config string and map it to SensorProps.aidl. + * @throws IllegalArgumentException when config string has unexpected format + */ + public void parse(@NonNull String config, @NonNull Context context) + throws IllegalArgumentException { + final String[] elems = config.split(":"); + if (elems.length < 3) { + throw new IllegalArgumentException(); + } + mSensorId = Integer.parseInt(elems[0]); + mModality = Integer.parseInt(elems[1]); + mStrength = Integer.parseInt(elems[2]); + mapHidlToAidlSensorConfiguration(context); + } + + @BiometricAuthenticator.Modality + public int getModality() { + return mModality; + } + + private void mapHidlToAidlSensorConfiguration(@NonNull Context context) { + commonProps = new CommonProps(); + commonProps.componentInfo = null; + commonProps.sensorId = mSensorId; + commonProps.sensorStrength = authenticatorStrengthToPropertyStrength(mStrength); + commonProps.maxEnrollmentsPerUser = context.getResources().getInteger( + R.integer.config_fingerprintMaxTemplatesPerUser); + halControlsIllumination = false; + sensorLocations = new SensorLocation[1]; + + final int[] udfpsProps = context.getResources().getIntArray( + com.android.internal.R.array.config_udfps_sensor_props); + final boolean isUdfps = !ArrayUtils.isEmpty(udfpsProps); + // config_is_powerbutton_fps indicates whether device has a power button fingerprint sensor. + final boolean isPowerbuttonFps = context.getResources().getBoolean( + R.bool.config_is_powerbutton_fps); + + if (isUdfps) { + sensorType = FingerprintSensorType.UNKNOWN; + } else if (isPowerbuttonFps) { + sensorType = FingerprintSensorType.POWER_BUTTON; + } else { + sensorType = FingerprintSensorType.REAR; + } + + if (isUdfps && udfpsProps.length == 3) { + setSensorLocation(udfpsProps[0], udfpsProps[1], udfpsProps[2]); + } else { + setSensorLocation(540 /* sensorLocationX */, 1636 /* sensorLocationY */, + 130 /* sensorRadius */); + } + + } + + private void setSensorLocation(int sensorLocationX, + int sensorLocationY, int sensorRadius) { + sensorLocations[0] = new SensorLocation(); + sensorLocations[0].display = ""; + sensorLocations[0].sensorLocationX = sensorLocationX; + sensorLocations[0].sensorLocationY = sensorLocationY; + sensorLocations[0].sensorRadius = sensorRadius; + } + + private byte authenticatorStrengthToPropertyStrength( + @BiometricManager.Authenticators.Types int strength) { + switch (strength) { + case BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE: + return SensorStrength.CONVENIENCE; + case BiometricManager.Authenticators.BIOMETRIC_WEAK: + return SensorStrength.WEAK; + case BiometricManager.Authenticators.BIOMETRIC_STRONG: + return SensorStrength.STRONG; + default: + throw new IllegalArgumentException("Unknown strength: " + strength); + } + } +} diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl index f594c00b0e47..606b171f36ba 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl @@ -31,6 +31,7 @@ import android.hardware.fingerprint.ISidefpsController; import android.hardware.fingerprint.Fingerprint; import android.hardware.fingerprint.FingerprintAuthenticateOptions; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; +import android.hardware.fingerprint.FingerprintSensorConfigurations; import java.util.List; /** @@ -173,6 +174,10 @@ interface IFingerprintService { @EnforcePermission("MANAGE_FINGERPRINT") void removeClientActiveCallback(IFingerprintClientActiveCallback callback); + //Register all available fingerprint sensors. + @EnforcePermission("USE_BIOMETRIC_INTERNAL") + void registerAuthenticatorsLegacy(in FingerprintSensorConfigurations fingerprintSensorConfigurations); + // Registers all HIDL and AIDL sensors. Only HIDL sensor properties need to be provided, because // AIDL sensor properties are retrieved directly from the available HALs. If no HIDL HALs exist, // hidlSensors must be non-null and empty. See AuthService.java diff --git a/core/tests/coretests/src/android/hardware/face/FaceSensorConfigurationsTest.java b/core/tests/coretests/src/android/hardware/face/FaceSensorConfigurationsTest.java new file mode 100644 index 000000000000..da3a465ade7e --- /dev/null +++ b/core/tests/coretests/src/android/hardware/face/FaceSensorConfigurationsTest.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.face; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.res.Resources; +import android.hardware.biometrics.face.IFace; +import android.hardware.biometrics.face.SensorProps; +import android.os.RemoteException; +import android.platform.test.annotations.Presubmit; +import android.test.suitebuilder.annotation.SmallTest; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.function.Function; + +@Presubmit +@SmallTest +public class FaceSensorConfigurationsTest { + @Rule + public final MockitoRule mockito = MockitoJUnit.rule(); + @Mock + private Context mContext; + @Mock + private Resources mResources; + @Mock + private IFace mFace; + @Mock + private Function<String, IFace> mGetIFace; + + private final String[] mAidlInstances = new String[]{"default", "virtual"}; + private String[] mHidlConfigStrings = new String[]{"0:2:15", "0:8:15"}; + private FaceSensorConfigurations mFaceSensorConfigurations; + + @Before + public void setUp() throws RemoteException { + when(mGetIFace.apply(anyString())).thenReturn(mFace); + when(mFace.getSensorProps()).thenReturn(new SensorProps[]{}); + when(mContext.getResources()).thenReturn(mResources); + } + + @Test + public void testAidlInstanceSensorProps() { + mFaceSensorConfigurations = new FaceSensorConfigurations(false); + mFaceSensorConfigurations.addAidlConfigs(mAidlInstances, mGetIFace); + + assertThat(mFaceSensorConfigurations.hasSensorConfigurations()).isTrue(); + assertThat(!mFaceSensorConfigurations.isSingleSensorConfigurationPresent()).isTrue(); + assertThat(mFaceSensorConfigurations.getResetLockoutRequiresChallenge()) + .isFalse(); + } + + @Test + public void testHidlConfigStrings() { + mFaceSensorConfigurations = new FaceSensorConfigurations(true); + mFaceSensorConfigurations.addHidlConfigs(mHidlConfigStrings, mContext); + + assertThat(mFaceSensorConfigurations.isSingleSensorConfigurationPresent()).isTrue(); + assertThat(mFaceSensorConfigurations.getResetLockoutRequiresChallenge()) + .isTrue(); + } + + @Test + public void testHidlConfigStrings_incorrectFormat() { + mHidlConfigStrings = new String[]{"0:8:15", "0:2", "0:face:15"}; + mFaceSensorConfigurations = new FaceSensorConfigurations(true); + mFaceSensorConfigurations.addHidlConfigs(mHidlConfigStrings, mContext); + + assertThat(mFaceSensorConfigurations.isSingleSensorConfigurationPresent()).isTrue(); + assertThat(mFaceSensorConfigurations.getResetLockoutRequiresChallenge()) + .isTrue(); + } +} diff --git a/core/tests/coretests/src/android/hardware/fingerprint/FingerprintSensorConfigurationsTest.java b/core/tests/coretests/src/android/hardware/fingerprint/FingerprintSensorConfigurationsTest.java new file mode 100644 index 000000000000..613089c8777d --- /dev/null +++ b/core/tests/coretests/src/android/hardware/fingerprint/FingerprintSensorConfigurationsTest.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.fingerprint; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.content.res.Resources; +import android.hardware.biometrics.fingerprint.IFingerprint; +import android.hardware.biometrics.fingerprint.SensorProps; +import android.os.RemoteException; +import android.platform.test.annotations.Presubmit; +import android.test.suitebuilder.annotation.SmallTest; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.function.Function; + + +@Presubmit +@SmallTest +public class FingerprintSensorConfigurationsTest { + @Rule + public final MockitoRule mockito = MockitoJUnit.rule(); + @Mock + private Context mContext; + @Mock + private Resources mResources; + @Mock + private IFingerprint mFingerprint; + @Mock + private Function<String, IFingerprint> mGetIFingerprint; + + private final String[] mAidlInstances = new String[]{"default", "virtual"}; + private String[] mHidlConfigStrings = new String[]{"0:2:15", "0:8:15"}; + private FingerprintSensorConfigurations mFingerprintSensorConfigurations; + + @Before + public void setUp() throws RemoteException { + when(mGetIFingerprint.apply(anyString())).thenReturn(mFingerprint); + when(mFingerprint.getSensorProps()).thenReturn(new SensorProps[]{}); + when(mContext.getResources()).thenReturn(mResources); + } + + @Test + public void testAidlInstanceSensorProps() { + mFingerprintSensorConfigurations = new FingerprintSensorConfigurations( + true /* resetLockoutRequiresHardwareAuthToken */); + mFingerprintSensorConfigurations.addAidlSensors(mAidlInstances, mGetIFingerprint); + + assertThat(mFingerprintSensorConfigurations.hasSensorConfigurations()).isTrue(); + assertThat(!mFingerprintSensorConfigurations.isSingleSensorConfigurationPresent()) + .isTrue(); + assertThat(mFingerprintSensorConfigurations.getResetLockoutRequiresHardwareAuthToken()) + .isTrue(); + } + + @Test + public void testHidlConfigStrings() { + mFingerprintSensorConfigurations = new FingerprintSensorConfigurations( + false /* resetLockoutRequiresHardwareAuthToken */); + mFingerprintSensorConfigurations.addHidlSensors(mHidlConfigStrings, mContext); + + assertThat(mFingerprintSensorConfigurations.isSingleSensorConfigurationPresent()).isTrue(); + assertThat(mFingerprintSensorConfigurations.getResetLockoutRequiresHardwareAuthToken()) + .isFalse(); + } + + @Test + public void testHidlConfigStrings_incorrectFormat() { + mHidlConfigStrings = new String[]{"0:8:15", "0:2", "0:fingerprint:15"}; + mFingerprintSensorConfigurations = new FingerprintSensorConfigurations( + false /* resetLockoutRequiresHardwareAuthToken */); + mFingerprintSensorConfigurations.addHidlSensors(mHidlConfigStrings, mContext); + + assertThat(mFingerprintSensorConfigurations.isSingleSensorConfigurationPresent()).isTrue(); + assertThat(mFingerprintSensorConfigurations.getResetLockoutRequiresHardwareAuthToken()) + .isFalse(); + } +} diff --git a/services/core/Android.bp b/services/core/Android.bp index b5c9ffdfffae..5111b08a1812 100644 --- a/services/core/Android.bp +++ b/services/core/Android.bp @@ -103,7 +103,6 @@ java_library_static { "android.hardware.power-java_shared", ], srcs: [ - ":android.hardware.biometrics.face-V4-java-source", ":android.hardware.tv.hdmi.connection-V1-java-source", ":android.hardware.tv.hdmi.earc-V1-java-source", ":statslog-art-java-gen", diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java index dafea9a199fd..d5d8fd22314b 100644 --- a/services/core/java/com/android/server/biometrics/AuthService.java +++ b/services/core/java/com/android/server/biometrics/AuthService.java @@ -51,9 +51,13 @@ import android.hardware.biometrics.ITestSessionCallback; import android.hardware.biometrics.PromptInfo; import android.hardware.biometrics.SensorLocationInternal; import android.hardware.biometrics.SensorPropertiesInternal; +import android.hardware.biometrics.face.IFace; +import android.hardware.biometrics.fingerprint.IFingerprint; +import android.hardware.face.FaceSensorConfigurations; import android.hardware.face.FaceSensorProperties; import android.hardware.face.FaceSensorPropertiesInternal; import android.hardware.face.IFaceService; +import android.hardware.fingerprint.FingerprintSensorConfigurations; import android.hardware.fingerprint.FingerprintSensorProperties; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.hardware.fingerprint.IFingerprintService; @@ -122,8 +126,6 @@ public class AuthService extends SystemService { /** * Allows to test with various device sensor configurations. - * @param context - * @return */ @VisibleForTesting public String[] getConfiguration(Context context) { @@ -131,6 +133,30 @@ public class AuthService extends SystemService { } /** + * Allows to test with various device sensor configurations. + */ + @VisibleForTesting + public String[] getFingerprintConfiguration(Context context) { + return getConfiguration(context); + } + + /** + * Allows to test with various device sensor configurations. + */ + @VisibleForTesting + public String[] getFaceConfiguration(Context context) { + return getConfiguration(context); + } + + /** + * Allows to test with various device sensor configurations. + */ + @VisibleForTesting + public String[] getIrisConfiguration(Context context) { + return getConfiguration(context); + } + + /** * Allows us to mock FingerprintService for testing */ @VisibleForTesting @@ -173,6 +199,22 @@ public class AuthService extends SystemService { } return false; } + + /** + * Allows to test with various fingerprint aidl instances. + */ + @VisibleForTesting + public String[] getFingerprintAidlInstances() { + return ServiceManager.getDeclaredInstances(IFingerprint.DESCRIPTOR); + } + + /** + * Allows to test with various face aidl instances. + */ + @VisibleForTesting + public String[] getFaceAidlInstances() { + return ServiceManager.getDeclaredInstances(IFace.DESCRIPTOR); + } } private final class AuthServiceImpl extends IAuthService.Stub { @@ -717,12 +759,129 @@ public class AuthService extends SystemService { hidlConfigs = null; } - // Registers HIDL and AIDL authenticators, but only HIDL configs need to be provided. - registerAuthenticators(hidlConfigs); + if (com.android.server.biometrics.Flags.deHidl()) { + Slog.d(TAG, "deHidl flag is on."); + registerAuthenticators(); + } else { + // Registers HIDL and AIDL authenticators, but only HIDL configs need to be provided. + registerAuthenticators(hidlConfigs); + } mInjector.publishBinderService(this, mImpl); } + private void registerAuthenticators() { + registerFingerprintSensors(mInjector.getFingerprintAidlInstances(), + mInjector.getFingerprintConfiguration(getContext())); + registerFaceSensors(mInjector.getFaceAidlInstances(), + mInjector.getFaceConfiguration(getContext())); + registerIrisSensors(mInjector.getIrisConfiguration(getContext())); + } + + private void registerIrisSensors(String[] hidlConfigStrings) { + final SensorConfig[] hidlConfigs; + if (!mInjector.isHidlDisabled(getContext())) { + final int firstApiLevel = SystemProperties.getInt(SYSPROP_FIRST_API_LEVEL, 0); + final int apiLevel = SystemProperties.getInt(SYSPROP_API_LEVEL, firstApiLevel); + if (hidlConfigStrings.length == 0 && apiLevel == Build.VERSION_CODES.R) { + // For backwards compatibility with R where biometrics could work without being + // configured in config_biometric_sensors. In the absence of a vendor provided + // configuration, we assume the weakest biometric strength (i.e. convenience). + Slog.w(TAG, "Found R vendor partition without config_biometric_sensors"); + hidlConfigStrings = generateRSdkCompatibleConfiguration(); + } + hidlConfigs = new SensorConfig[hidlConfigStrings.length]; + for (int i = 0; i < hidlConfigStrings.length; ++i) { + hidlConfigs[i] = new SensorConfig(hidlConfigStrings[i]); + } + } else { + hidlConfigs = null; + } + + final List<SensorPropertiesInternal> hidlIrisSensors = new ArrayList<>(); + + if (hidlConfigs != null) { + for (SensorConfig sensor : hidlConfigs) { + switch (sensor.modality) { + case TYPE_IRIS: + hidlIrisSensors.add(getHidlIrisSensorProps(sensor.id, sensor.strength)); + break; + + default: + Slog.e(TAG, "Unknown modality: " + sensor.modality); + } + } + } + + final IIrisService irisService = mInjector.getIrisService(); + if (irisService != null) { + try { + irisService.registerAuthenticators(hidlIrisSensors); + } catch (RemoteException e) { + Slog.e(TAG, "RemoteException when registering iris authenticators", e); + } + } else if (hidlIrisSensors.size() > 0) { + Slog.e(TAG, "HIDL iris configuration exists, but IrisService is null."); + } + } + + private void registerFaceSensors(final String[] faceAidlInstances, + final String[] hidlConfigStrings) { + final FaceSensorConfigurations mFaceSensorConfigurations = + new FaceSensorConfigurations(hidlConfigStrings != null + && hidlConfigStrings.length > 0); + + if (hidlConfigStrings != null && hidlConfigStrings.length > 0) { + mFaceSensorConfigurations.addHidlConfigs( + hidlConfigStrings, getContext()); + } + + if (faceAidlInstances != null && faceAidlInstances.length > 0) { + mFaceSensorConfigurations.addAidlConfigs(faceAidlInstances, + name -> IFace.Stub.asInterface(Binder.allowBlocking( + ServiceManager.waitForDeclaredService(name)))); + } + + final IFaceService faceService = mInjector.getFaceService(); + if (faceService != null) { + try { + faceService.registerAuthenticatorsLegacy(mFaceSensorConfigurations); + } catch (RemoteException e) { + Slog.e(TAG, "RemoteException when registering face authenticators", e); + } + } else if (mFaceSensorConfigurations.hasSensorConfigurations()) { + Slog.e(TAG, "Face configuration exists, but FaceService is null."); + } + } + + private void registerFingerprintSensors(final String[] fingerprintAidlInstances, + final String[] hidlConfigStrings) { + final FingerprintSensorConfigurations mFingerprintSensorConfigurations = + new FingerprintSensorConfigurations(!(hidlConfigStrings != null + && hidlConfigStrings.length > 0)); + + if (hidlConfigStrings != null && hidlConfigStrings.length > 0) { + mFingerprintSensorConfigurations.addHidlSensors(hidlConfigStrings, getContext()); + } + + if (fingerprintAidlInstances != null && fingerprintAidlInstances.length > 0) { + mFingerprintSensorConfigurations.addAidlSensors(fingerprintAidlInstances, + name -> IFingerprint.Stub.asInterface(Binder.allowBlocking( + ServiceManager.waitForDeclaredService(name)))); + } + + final IFingerprintService fingerprintService = mInjector.getFingerprintService(); + if (fingerprintService != null) { + try { + fingerprintService.registerAuthenticatorsLegacy(mFingerprintSensorConfigurations); + } catch (RemoteException e) { + Slog.e(TAG, "RemoteException when registering fingerprint authenticators", e); + } + } else if (mFingerprintSensorConfigurations.hasSensorConfigurations()) { + Slog.e(TAG, "Fingerprint configuration exists, but FingerprintService is null."); + } + } + /** * Generates an array of string configs with entries that correspond to the biometric features * declared on the device. Returns an empty array if no biometric features are declared. diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java index 037ea38a2d17..89b638be3500 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricScheduler.java @@ -202,7 +202,7 @@ public class BiometricScheduler { }; @VisibleForTesting - BiometricScheduler(@NonNull String tag, + public BiometricScheduler(@NonNull String tag, @NonNull Handler handler, @SensorType int sensorType, @Nullable GestureAvailabilityDispatcher gestureAvailabilityDispatcher, diff --git a/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java b/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java index 57feedc0e68e..0c3dfa7b3b84 100644 --- a/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java +++ b/services/core/java/com/android/server/biometrics/sensors/BiometricSchedulerOperation.java @@ -387,6 +387,11 @@ public class BiometricSchedulerOperation { return isAuthentication || isDetection; } + /** If this operation is {@link StartUserClient}. */ + public boolean isStartUserOperation() { + return mClientMonitor instanceof StartUserClient<?, ?>; + } + /** If this operation performs acquisition {@link AcquisitionClient}. */ public boolean isAcquisitionOperation() { return mClientMonitor instanceof AcquisitionClient; diff --git a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java index 7f8f38f3e9d2..6daaad1baf83 100644 --- a/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/InternalEnumerateClient.java @@ -54,8 +54,6 @@ public abstract class InternalEnumerateClient<T> extends HalClientMonitor<T> // is all done internally. super(context, lazyDaemon, token, null /* ClientMonitorCallbackConverter */, userId, owner, 0 /* cookie */, sensorId, logger, biometricContext); - //, BiometricsProtoEnums.ACTION_ENUMERATE, - // BiometricsProtoEnums.CLIENT_UNKNOWN); mEnrolledList = enrolledList; mUtils = utils; } diff --git a/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java b/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java index 80754702415a..3753bbdba276 100644 --- a/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java +++ b/services/core/java/com/android/server/biometrics/sensors/UserAwareBiometricScheduler.java @@ -135,7 +135,7 @@ public class UserAwareBiometricScheduler extends BiometricScheduler { final int currentUserId = mCurrentUserRetriever.getCurrentUserId(); final int nextUserId = mPendingOperations.getFirst().getTargetUserId(); - if (nextUserId == currentUserId) { + if (nextUserId == currentUserId || mPendingOperations.getFirst().isStartUserOperation()) { super.startNextOperationIfIdle(); } else if (currentUserId == UserHandle.USER_NULL) { final BaseClientMonitor startClient = diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java index 578d9dc2aede..6af223b3660a 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java @@ -36,6 +36,7 @@ import android.hardware.biometrics.face.IFace; import android.hardware.biometrics.face.SensorProps; import android.hardware.face.Face; import android.hardware.face.FaceAuthenticateOptions; +import android.hardware.face.FaceSensorConfigurations; import android.hardware.face.FaceSensorPropertiesInternal; import android.hardware.face.FaceServiceReceiver; import android.hardware.face.IFaceAuthenticatorsRegisteredCallback; @@ -55,6 +56,7 @@ import android.util.Slog; import android.util.proto.ProtoOutputStream; import android.view.Surface; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.DumpUtils; import com.android.internal.widget.LockPatternUtils; import com.android.server.SystemService; @@ -76,6 +78,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.function.Supplier; /** * A service to manage multiple clients that want to access the face HAL API. @@ -86,7 +89,7 @@ public class FaceService extends SystemService { protected static final String TAG = "FaceService"; - private final FaceServiceWrapper mServiceWrapper; + @VisibleForTesting final FaceServiceWrapper mServiceWrapper; private final LockoutResetDispatcher mLockoutResetDispatcher; private final LockPatternUtils mLockPatternUtils; @NonNull @@ -94,11 +97,18 @@ public class FaceService extends SystemService { @NonNull private final BiometricStateCallback<ServiceProvider, FaceSensorPropertiesInternal> mBiometricStateCallback; + @NonNull + private final FaceProviderFunction mFaceProviderFunction; + + interface FaceProviderFunction { + FaceProvider getFaceProvider(Pair<String, SensorProps[]> filteredSensorProps, + boolean resetLockoutRequiresChallenge); + } /** * Receives the incoming binder calls from FaceManager. */ - private final class FaceServiceWrapper extends IFaceService.Stub { + @VisibleForTesting final class FaceServiceWrapper extends IFaceService.Stub { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override public ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback, @@ -672,7 +682,8 @@ public class FaceService extends SystemService { final SensorProps[] props = face.getSensorProps(); final FaceProvider provider = new FaceProvider(getContext(), mBiometricStateCallback, props, instance, mLockoutResetDispatcher, - BiometricContext.getInstance(getContext())); + BiometricContext.getInstance(getContext()), + false /* resetLockoutRequiresChallenge */); providers.add(provider); } catch (RemoteException e) { Slog.e(TAG, "Remote exception in getSensorProps: " + fqName); @@ -704,6 +715,55 @@ public class FaceService extends SystemService { }); } + @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) + public void registerAuthenticatorsLegacy( + FaceSensorConfigurations faceSensorConfigurations) { + super.registerAuthenticatorsLegacy_enforcePermission(); + + if (!faceSensorConfigurations.hasSensorConfigurations()) { + Slog.d(TAG, "No face sensors to register."); + return; + } + mRegistry.registerAll(() -> getProviders(faceSensorConfigurations)); + } + + private List<ServiceProvider> getProviders( + FaceSensorConfigurations faceSensorConfigurations) { + final List<ServiceProvider> providers = new ArrayList<>(); + final Pair<String, SensorProps[]> filteredSensorProps = + filterAvailableHalInstances(faceSensorConfigurations); + providers.add(mFaceProviderFunction.getFaceProvider(filteredSensorProps, + faceSensorConfigurations.getResetLockoutRequiresChallenge())); + return providers; + } + + @NonNull + private Pair<String, SensorProps[]> filterAvailableHalInstances( + FaceSensorConfigurations faceSensorConfigurations) { + Pair<String, SensorProps[]> finalSensorPair = faceSensorConfigurations.getSensorPair(); + + if (faceSensorConfigurations.isSingleSensorConfigurationPresent()) { + return finalSensorPair; + } + + final Pair<String, SensorProps[]> virtualSensorProps = faceSensorConfigurations + .getSensorPairForInstance("virtual"); + + if (Utils.isVirtualEnabled(getContext())) { + if (virtualSensorProps != null) { + return virtualSensorProps; + } else { + Slog.e(TAG, "Could not find virtual interface while it is enabled"); + return finalSensorPair; + } + } else { + if (virtualSensorProps != null) { + return faceSensorConfigurations.getSensorPairNotForInstance("virtual"); + } + } + return finalSensorPair; + } + private Pair<List<FaceSensorPropertiesInternal>, List<String>> filterAvailableHalInstances( @NonNull List<FaceSensorPropertiesInternal> hidlInstances, @@ -752,20 +812,36 @@ public class FaceService extends SystemService { } public FaceService(Context context) { + this(context, null /* faceProviderFunction */, () -> IBiometricService.Stub.asInterface( + ServiceManager.getService(Context.BIOMETRIC_SERVICE))); + } + + @VisibleForTesting FaceService(Context context, FaceProviderFunction faceProviderFunction, + Supplier<IBiometricService> biometricServiceSupplier) { super(context); mServiceWrapper = new FaceServiceWrapper(); mLockoutResetDispatcher = new LockoutResetDispatcher(context); mLockPatternUtils = new LockPatternUtils(context); mBiometricStateCallback = new BiometricStateCallback<>(UserManager.get(context)); - mRegistry = new FaceServiceRegistry(mServiceWrapper, - () -> IBiometricService.Stub.asInterface( - ServiceManager.getService(Context.BIOMETRIC_SERVICE))); + mRegistry = new FaceServiceRegistry(mServiceWrapper, biometricServiceSupplier); mRegistry.addAllRegisteredCallback(new IFaceAuthenticatorsRegisteredCallback.Stub() { @Override public void onAllAuthenticatorsRegistered(List<FaceSensorPropertiesInternal> sensors) { mBiometricStateCallback.start(mRegistry.getProviders()); } }); + + if (Flags.deHidl()) { + mFaceProviderFunction = faceProviderFunction != null ? faceProviderFunction : + ((filteredSensorProps, resetLockoutRequiresChallenge) -> new FaceProvider( + getContext(), mBiometricStateCallback, + filteredSensorProps.second, + filteredSensorProps.first, mLockoutResetDispatcher, + BiometricContext.getInstance(getContext()), + resetLockoutRequiresChallenge)); + } else { + mFaceProviderFunction = ((filteredSensorProps, resetLockoutRequiresChallenge) -> null); + } } @Override diff --git a/services/core/java/com/android/server/biometrics/sensors/face/LockoutHalImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/LockoutHalImpl.java index e5d4a635876d..ef2be790ed34 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/LockoutHalImpl.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/LockoutHalImpl.java @@ -24,7 +24,8 @@ import com.android.server.biometrics.sensors.LockoutTracker; * the user changes. */ public class LockoutHalImpl implements LockoutTracker { - private @LockoutMode int mCurrentUserLockoutMode; + @LockoutMode + private int mCurrentUserLockoutMode; @Override public int getLockoutModeForUser(int userId) { diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlResponseHandler.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlResponseHandler.java index 57f5b34c197a..098be2120e03 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlResponseHandler.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlResponseHandler.java @@ -27,6 +27,7 @@ import android.hardware.face.Face; import android.hardware.keymaster.HardwareAuthToken; import android.util.Slog; +import com.android.server.biometrics.Flags; import com.android.server.biometrics.HardwareAuthTokenUtils; import com.android.server.biometrics.Utils; import com.android.server.biometrics.sensors.AcquisitionClient; @@ -59,6 +60,20 @@ public class AidlResponseHandler extends ISessionCallback.Stub { void onHardwareUnavailable(); } + /** + * Interface to send results to the AidlResponseHandler's owner. + */ + public interface AidlResponseHandlerCallback { + /** + * Invoked when enrollment is successful. + */ + void onEnrollSuccess(); + /** + * Invoked when the HAL sends ERROR_HW_UNAVAILABLE. + */ + void onHardwareUnavailable(); + } + private static final String TAG = "AidlResponseHandler"; @NonNull @@ -68,7 +83,7 @@ public class AidlResponseHandler extends ISessionCallback.Stub { private final int mSensorId; private final int mUserId; @NonNull - private final LockoutTracker mLockoutCache; + private final LockoutTracker mLockoutTracker; @NonNull private final LockoutResetDispatcher mLockoutResetDispatcher; @@ -76,6 +91,8 @@ public class AidlResponseHandler extends ISessionCallback.Stub { private final AuthSessionCoordinator mAuthSessionCoordinator; @NonNull private final HardwareUnavailableCallback mHardwareUnavailableCallback; + @NonNull + private final AidlResponseHandlerCallback mAidlResponseHandlerCallback; public AidlResponseHandler(@NonNull Context context, @NonNull BiometricScheduler scheduler, int sensorId, int userId, @@ -83,14 +100,33 @@ public class AidlResponseHandler extends ISessionCallback.Stub { @NonNull LockoutResetDispatcher lockoutResetDispatcher, @NonNull AuthSessionCoordinator authSessionCoordinator, @NonNull HardwareUnavailableCallback hardwareUnavailableCallback) { + this(context, scheduler, sensorId, userId, lockoutTracker, lockoutResetDispatcher, + authSessionCoordinator, hardwareUnavailableCallback, + new AidlResponseHandlerCallback() { + @Override + public void onEnrollSuccess() {} + + @Override + public void onHardwareUnavailable() {} + }); + } + + public AidlResponseHandler(@NonNull Context context, + @NonNull BiometricScheduler scheduler, int sensorId, int userId, + @NonNull LockoutTracker lockoutTracker, + @NonNull LockoutResetDispatcher lockoutResetDispatcher, + @NonNull AuthSessionCoordinator authSessionCoordinator, + @NonNull HardwareUnavailableCallback hardwareUnavailableCallback, + @NonNull AidlResponseHandlerCallback aidlResponseHandlerCallback) { mContext = context; mScheduler = scheduler; mSensorId = sensorId; mUserId = userId; - mLockoutCache = lockoutTracker; + mLockoutTracker = lockoutTracker; mLockoutResetDispatcher = lockoutResetDispatcher; mAuthSessionCoordinator = authSessionCoordinator; mHardwareUnavailableCallback = hardwareUnavailableCallback; + mAidlResponseHandlerCallback = aidlResponseHandlerCallback; } @Override @@ -106,13 +142,13 @@ public class AidlResponseHandler extends ISessionCallback.Stub { @Override public void onChallengeGenerated(long challenge) { handleResponse(FaceGenerateChallengeClient.class, (c) -> c.onChallengeGenerated(mSensorId, - mUserId, challenge), null); + mUserId, challenge)); } @Override public void onChallengeRevoked(long challenge) { handleResponse(FaceRevokeChallengeClient.class, (c) -> c.onChallengeRevoked(mSensorId, - mUserId, challenge), null); + mUserId, challenge)); } @Override @@ -123,7 +159,7 @@ public class AidlResponseHandler extends ISessionCallback.Stub { return; } c.onAuthenticationFrame(AidlConversionUtils.toFrameworkAuthenticationFrame(frame)); - }, null); + }); } @Override @@ -134,7 +170,7 @@ public class AidlResponseHandler extends ISessionCallback.Stub { return; } c.onEnrollmentFrame(AidlConversionUtils.toFrameworkEnrollmentFrame(frame)); - }, null); + }); } @Override @@ -149,9 +185,13 @@ public class AidlResponseHandler extends ISessionCallback.Stub { handleResponse(ErrorConsumer.class, (c) -> { c.onError(error, vendorCode); if (error == Error.HW_UNAVAILABLE) { - mHardwareUnavailableCallback.onHardwareUnavailable(); + if (Flags.deHidl()) { + mAidlResponseHandlerCallback.onHardwareUnavailable(); + } else { + mHardwareUnavailableCallback.onHardwareUnavailable(); + } } - }, null); + }); } @Override @@ -167,7 +207,12 @@ public class AidlResponseHandler extends ISessionCallback.Stub { .getUniqueName(mContext, currentUserId); final Face face = new Face(name, enrollmentId, mSensorId); - handleResponse(FaceEnrollClient.class, (c) -> c.onEnrollResult(face, remaining), null); + handleResponse(FaceEnrollClient.class, (c) -> { + c.onEnrollResult(face, remaining); + if (remaining == 0) { + mAidlResponseHandlerCallback.onEnrollSuccess(); + } + }); } @Override @@ -179,37 +224,37 @@ public class AidlResponseHandler extends ISessionCallback.Stub { byteList.add(b); } handleResponse(AuthenticationConsumer.class, (c) -> c.onAuthenticated(face, - true /* authenticated */, byteList), null); + true /* authenticated */, byteList)); } @Override public void onAuthenticationFailed() { final Face face = new Face("" /* name */, 0 /* faceId */, mSensorId); handleResponse(AuthenticationConsumer.class, (c) -> c.onAuthenticated(face, - false /* authenticated */, null /* hat */), null); + false /* authenticated */, null /* hat */)); } @Override public void onLockoutTimed(long durationMillis) { - handleResponse(LockoutConsumer.class, (c) -> c.onLockoutTimed(durationMillis), null); + handleResponse(LockoutConsumer.class, (c) -> c.onLockoutTimed(durationMillis)); } @Override public void onLockoutPermanent() { - handleResponse(LockoutConsumer.class, LockoutConsumer::onLockoutPermanent, null); + handleResponse(LockoutConsumer.class, LockoutConsumer::onLockoutPermanent); } @Override public void onLockoutCleared() { handleResponse(FaceResetLockoutClient.class, FaceResetLockoutClient::onLockoutCleared, (c) -> FaceResetLockoutClient.resetLocalLockoutStateToNone(mSensorId, mUserId, - mLockoutCache, mLockoutResetDispatcher, mAuthSessionCoordinator, - Utils.getCurrentStrength(mSensorId), -1 /* requestId */)); + mLockoutTracker, mLockoutResetDispatcher, mAuthSessionCoordinator, + Utils.getCurrentStrength(mSensorId), -1 /* requestId */)); } @Override public void onInteractionDetected() { - handleResponse(FaceDetectClient.class, FaceDetectClient::onInteractionDetected, null); + handleResponse(FaceDetectClient.class, FaceDetectClient::onInteractionDetected); } @Override @@ -219,23 +264,23 @@ public class AidlResponseHandler extends ISessionCallback.Stub { final Face face = new Face("" /* name */, enrollmentIds[i], mSensorId); final int finalI = i; handleResponse(EnumerateConsumer.class, (c) -> c.onEnumerationResult(face, - enrollmentIds.length - finalI - 1), null); + enrollmentIds.length - finalI - 1 /* remaining */)); } } else { handleResponse(EnumerateConsumer.class, (c) -> c.onEnumerationResult( - null /* identifier */, 0 /* remaining */), null); + null /* identifier */, 0 /* remaining */)); } } @Override public void onFeaturesRetrieved(byte[] features) { handleResponse(FaceGetFeatureClient.class, (c) -> c.onFeatureGet(true /* success */, - features), null); + features)); } @Override public void onFeatureSet(byte feature) { - handleResponse(FaceSetFeatureClient.class, (c) -> c.onFeatureSet(true /* success */), null); + handleResponse(FaceSetFeatureClient.class, (c) -> c.onFeatureSet(true /* success */)); } @Override @@ -245,33 +290,32 @@ public class AidlResponseHandler extends ISessionCallback.Stub { final Face face = new Face("" /* name */, enrollmentIds[i], mSensorId); final int finalI = i; handleResponse(RemovalConsumer.class, - (c) -> c.onRemoved(face, enrollmentIds.length - finalI - 1), - null); + (c) -> c.onRemoved(face, + enrollmentIds.length - finalI - 1 /* remaining */)); } } else { handleResponse(RemovalConsumer.class, (c) -> c.onRemoved(null /* identifier */, - 0 /* remaining */), null); + 0 /* remaining */)); } } @Override public void onAuthenticatorIdRetrieved(long authenticatorId) { handleResponse(FaceGetAuthenticatorIdClient.class, (c) -> c.onAuthenticatorIdRetrieved( - authenticatorId), null); + authenticatorId)); } @Override public void onAuthenticatorIdInvalidated(long newAuthenticatorId) { handleResponse(FaceInvalidationClient.class, (c) -> c.onAuthenticatorIdInvalidated( - newAuthenticatorId), null); + newAuthenticatorId)); } /** * Handles acquired messages sent by the HAL (specifically for HIDL HAL). */ public void onAcquired(int acquiredInfo, int vendorCode) { - handleResponse(AcquisitionClient.class, (c) -> c.onAcquired(acquiredInfo, vendorCode), - null); + handleResponse(AcquisitionClient.class, (c) -> c.onAcquired(acquiredInfo, vendorCode)); } /** @@ -288,7 +332,7 @@ public class AidlResponseHandler extends ISessionCallback.Stub { lockoutMode = LockoutTracker.LOCKOUT_TIMED; } - mLockoutCache.setLockoutModeForUser(mUserId, lockoutMode); + mLockoutTracker.setLockoutModeForUser(mUserId, lockoutMode); if (duration == 0) { mLockoutResetDispatcher.notifyLockoutResetCallbacks(mSensorId); @@ -296,6 +340,20 @@ public class AidlResponseHandler extends ISessionCallback.Stub { }); } + /** + * Handle clients which are not supported in HIDL HAL. For face, FaceInvalidationClient + * is the only AIDL client which is not supported in HIDL. + */ + public void onUnsupportedClientScheduled() { + Slog.e(TAG, "FaceInvalidationClient is not supported in the HAL."); + handleResponse(FaceInvalidationClient.class, BaseClientMonitor::cancel); + } + + private <T> void handleResponse(@NonNull Class<T> className, + @NonNull Consumer<T> action) { + handleResponse(className, action, null /* alternateAction */); + } + private <T> void handleResponse(@NonNull Class<T> className, @NonNull Consumer<T> actionIfClassMatchesClient, @Nullable Consumer<BaseClientMonitor> alternateAction) { diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlSession.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlSession.java index e70e25aebe9b..af46f441d6ce 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlSession.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/AidlSession.java @@ -21,7 +21,7 @@ import android.content.Context; import android.hardware.biometrics.face.ISession; import android.hardware.biometrics.face.V1_0.IBiometricsFace; -import com.android.server.biometrics.sensors.face.hidl.AidlToHidlAdapter; +import com.android.server.biometrics.sensors.face.hidl.HidlToAidlSessionAdapter; import java.util.function.Supplier; @@ -47,7 +47,7 @@ public class AidlSession { public AidlSession(Context context, Supplier<IBiometricsFace> session, int userId, AidlResponseHandler aidlResponseHandler) { - mSession = new AidlToHidlAdapter(context, session, userId, aidlResponseHandler); + mSession = new HidlToAidlSessionAdapter(context, session, userId, aidlResponseHandler); mHalInterfaceVersion = 0; mUserId = userId; mAidlResponseHandler = aidlResponseHandler; @@ -64,7 +64,7 @@ public class AidlSession { } /** The HAL callback, which should only be used in tests {@See BiometricTestSessionImpl}. */ - AidlResponseHandler getHalSessionCallback() { + public AidlResponseHandler getHalSessionCallback() { return mAidlResponseHandler; } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java index c15049b48bb2..c41b706555e8 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceGetFeatureClient.java @@ -34,7 +34,7 @@ import com.android.server.biometrics.sensors.ClientMonitorCallback; import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.ErrorConsumer; import com.android.server.biometrics.sensors.HalClientMonitor; -import com.android.server.biometrics.sensors.face.hidl.AidlToHidlAdapter; +import com.android.server.biometrics.sensors.face.hidl.HidlToAidlSessionAdapter; import java.util.HashMap; import java.util.Map; @@ -75,8 +75,8 @@ public class FaceGetFeatureClient extends HalClientMonitor<AidlSession> implemen protected void startHalOperation() { try { ISession session = getFreshDaemon().getSession(); - if (session instanceof AidlToHidlAdapter) { - ((AidlToHidlAdapter) session).setFeature(mFeature); + if (session instanceof HidlToAidlSessionAdapter) { + ((HidlToAidlSessionAdapter) session).setFeature(mFeature); } session.getFeatures(); } catch (RemoteException e) { diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java index dd9c6d50ae9e..9fa15b8ea3a1 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java @@ -24,6 +24,7 @@ import android.app.SynchronousUserSwitchObserver; import android.app.TaskStackListener; import android.content.Context; import android.content.pm.UserInfo; +import android.hardware.biometrics.BiometricFaceConstants; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.ComponentInfoInternal; import android.hardware.biometrics.IInvalidationCallback; @@ -51,6 +52,7 @@ import android.view.Surface; import com.android.internal.annotations.VisibleForTesting; import com.android.server.biometrics.AuthenticationStatsBroadcastReceiver; import com.android.server.biometrics.AuthenticationStatsCollector; +import com.android.server.biometrics.Flags; import com.android.server.biometrics.Utils; import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.log.BiometricLogger; @@ -64,11 +66,13 @@ import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback; import com.android.server.biometrics.sensors.InvalidationRequesterClient; import com.android.server.biometrics.sensors.LockoutResetDispatcher; +import com.android.server.biometrics.sensors.LockoutTracker; import com.android.server.biometrics.sensors.PerformanceTracker; import com.android.server.biometrics.sensors.SensorList; import com.android.server.biometrics.sensors.face.FaceUtils; import com.android.server.biometrics.sensors.face.ServiceProvider; import com.android.server.biometrics.sensors.face.UsageStats; +import com.android.server.biometrics.sensors.face.hidl.HidlToAidlSensorAdapter; import org.json.JSONArray; import org.json.JSONException; @@ -152,9 +156,11 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { @NonNull SensorProps[] props, @NonNull String halInstanceName, @NonNull LockoutResetDispatcher lockoutResetDispatcher, - @NonNull BiometricContext biometricContext) { + @NonNull BiometricContext biometricContext, + boolean resetLockoutRequiresChallenge) { this(context, biometricStateCallback, props, halInstanceName, lockoutResetDispatcher, - biometricContext, null /* daemon */); + biometricContext, null /* daemon */, resetLockoutRequiresChallenge, + false /* testHalEnabled */); } @VisibleForTesting FaceProvider(@NonNull Context context, @@ -163,7 +169,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { @NonNull String halInstanceName, @NonNull LockoutResetDispatcher lockoutResetDispatcher, @NonNull BiometricContext biometricContext, - IFace daemon) { + @Nullable IFace daemon, boolean resetLockoutRequiresChallenge, + boolean testHalEnabled) { mContext = context; mBiometricStateCallback = biometricStateCallback; mHalInstanceName = halInstanceName; @@ -176,48 +183,118 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { mBiometricContext = biometricContext; mAuthSessionCoordinator = mBiometricContext.getAuthSessionCoordinator(); mDaemon = daemon; + mTestHalEnabled = testHalEnabled; - AuthenticationStatsBroadcastReceiver mBroadcastReceiver = - new AuthenticationStatsBroadcastReceiver( - mContext, - BiometricsProtoEnums.MODALITY_FACE, - (AuthenticationStatsCollector collector) -> { - Slog.d(getTag(), "Initializing AuthenticationStatsCollector"); - mAuthenticationStatsCollector = collector; - }); + initAuthenticationBroadcastReceiver(); + initSensors(resetLockoutRequiresChallenge, props); + } - for (SensorProps prop : props) { - final int sensorId = prop.commonProps.sensorId; + private void initAuthenticationBroadcastReceiver() { + new AuthenticationStatsBroadcastReceiver( + mContext, + BiometricsProtoEnums.MODALITY_FACE, + (AuthenticationStatsCollector collector) -> { + Slog.d(getTag(), "Initializing AuthenticationStatsCollector"); + mAuthenticationStatsCollector = collector; + }); + } - final List<ComponentInfoInternal> componentInfo = new ArrayList<>(); - if (prop.commonProps.componentInfo != null) { - for (ComponentInfo info : prop.commonProps.componentInfo) { - componentInfo.add(new ComponentInfoInternal(info.componentId, - info.hardwareVersion, info.firmwareVersion, info.serialNumber, - info.softwareVersion)); + private void initSensors(boolean resetLockoutRequiresChallenge, SensorProps[] props) { + if (Flags.deHidl()) { + if (resetLockoutRequiresChallenge) { + Slog.d(getTag(), "Adding HIDL configs"); + for (SensorProps prop : props) { + addHidlSensors(prop, resetLockoutRequiresChallenge); + } + } else { + Slog.d(getTag(), "Adding AIDL configs"); + for (SensorProps prop : props) { + addAidlSensors(prop, resetLockoutRequiresChallenge); } } + } else { + for (SensorProps prop : props) { + final int sensorId = prop.commonProps.sensorId; + + final List<ComponentInfoInternal> componentInfo = new ArrayList<>(); + if (prop.commonProps.componentInfo != null) { + for (ComponentInfo info : prop.commonProps.componentInfo) { + componentInfo.add(new ComponentInfoInternal(info.componentId, + info.hardwareVersion, info.firmwareVersion, info.serialNumber, + info.softwareVersion)); + } + } - final FaceSensorPropertiesInternal internalProp = new FaceSensorPropertiesInternal( - prop.commonProps.sensorId, prop.commonProps.sensorStrength, - prop.commonProps.maxEnrollmentsPerUser, componentInfo, prop.sensorType, - prop.supportsDetectInteraction, prop.halControlsPreview, - false /* resetLockoutRequiresChallenge */); - final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, mContext, mHandler, - internalProp, lockoutResetDispatcher, mBiometricContext); - final int userId = sensor.getLazySession().get() == null ? UserHandle.USER_NULL : - sensor.getLazySession().get().getUserId(); - mFaceSensors.addSensor(sensorId, sensor, userId, - new SynchronousUserSwitchObserver() { - @Override - public void onUserSwitching(int newUserId) { - scheduleInternalCleanup(sensorId, newUserId, null /* callback */); - } - }); - Slog.d(getTag(), "Added: " + internalProp); + final FaceSensorPropertiesInternal internalProp = new FaceSensorPropertiesInternal( + prop.commonProps.sensorId, prop.commonProps.sensorStrength, + prop.commonProps.maxEnrollmentsPerUser, componentInfo, prop.sensorType, + prop.supportsDetectInteraction, prop.halControlsPreview, + false /* resetLockoutRequiresChallenge */); + final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, + mContext, mHandler, internalProp, mLockoutResetDispatcher, + mBiometricContext); + sensor.init(mLockoutResetDispatcher, this); + final int userId = sensor.getLazySession().get() == null ? UserHandle.USER_NULL : + sensor.getLazySession().get().getUserId(); + mFaceSensors.addSensor(sensorId, sensor, userId, + new SynchronousUserSwitchObserver() { + @Override + public void onUserSwitching(int newUserId) { + scheduleInternalCleanup(sensorId, newUserId, null /* callback */); + } + }); + Slog.d(getTag(), "Added: " + internalProp); + } } } + private void addHidlSensors(SensorProps prop, boolean resetLockoutRequiresChallenge) { + final int sensorId = prop.commonProps.sensorId; + final Sensor sensor = new HidlToAidlSensorAdapter(getTag() + "/" + sensorId, this, + mContext, mHandler, prop, mLockoutResetDispatcher, + mBiometricContext, resetLockoutRequiresChallenge, + () -> { + //TODO: update to make this testable + scheduleInternalCleanup(sensorId, ActivityManager.getCurrentUser(), + null /* callback */); + scheduleGetFeature(sensorId, new Binder(), ActivityManager.getCurrentUser(), + BiometricFaceConstants.FEATURE_REQUIRE_ATTENTION, null, + mContext.getOpPackageName()); + }); + sensor.init(mLockoutResetDispatcher, this); + final int userId = sensor.getLazySession().get() == null ? UserHandle.USER_NULL : + sensor.getLazySession().get().getUserId(); + mFaceSensors.addSensor(sensorId, sensor, userId, + new SynchronousUserSwitchObserver() { + @Override + public void onUserSwitching(int newUserId) { + scheduleInternalCleanup(sensorId, newUserId, null /* callback */); + scheduleGetFeature(sensorId, new Binder(), newUserId, + BiometricFaceConstants.FEATURE_REQUIRE_ATTENTION, + null, mContext.getOpPackageName()); + } + }); + Slog.d(getTag(), "Added: " + mFaceSensors.get(sensorId)); + } + + private void addAidlSensors(SensorProps prop, boolean resetLockoutRequiresChallenge) { + final int sensorId = prop.commonProps.sensorId; + final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, mContext, + mHandler, prop, mLockoutResetDispatcher, mBiometricContext, + resetLockoutRequiresChallenge); + sensor.init(mLockoutResetDispatcher, this); + final int userId = sensor.getLazySession().get() == null ? UserHandle.USER_NULL : + sensor.getLazySession().get().getUserId(); + mFaceSensors.addSensor(sensorId, sensor, userId, + new SynchronousUserSwitchObserver() { + @Override + public void onUserSwitching(int newUserId) { + scheduleInternalCleanup(sensorId, newUserId, null /* callback */); + } + }); + Slog.d(getTag(), "Added: " + mFaceSensors.get(sensorId)); + } + private String getTag() { return "FaceProvider/" + mHalInstanceName; } @@ -290,7 +367,10 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { } } - private void scheduleLoadAuthenticatorIdsForUser(int sensorId, int userId) { + /** + * Schedules FaceGetAuthenticatorIdClient for specific sensor and user. + */ + protected void scheduleLoadAuthenticatorIdsForUser(int sensorId, int userId) { mHandler.post(() -> { final FaceGetAuthenticatorIdClient client = new FaceGetAuthenticatorIdClient( mContext, mFaceSensors.get(sensorId).getLazySession(), userId, @@ -365,8 +445,12 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { @Override public int getLockoutModeForUser(int sensorId, int userId) { - return mBiometricContext.getAuthSessionCoordinator().getLockoutStateFor(userId, - Utils.getCurrentStrength(sensorId)); + if (Flags.deHidl()) { + return mFaceSensors.get(sensorId).getLockoutModeForUser(userId); + } else { + return mBiometricContext.getAuthSessionCoordinator().getLockoutStateFor(userId, + Utils.getCurrentStrength(sensorId)); + } } @Override @@ -376,13 +460,18 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { @Override public boolean isHardwareDetected(int sensorId) { - return hasHalInstance(); + if (Flags.deHidl()) { + return mFaceSensors.get(sensorId).isHardwareDetected(mHalInstanceName); + } else { + return hasHalInstance(); + } } @Override public void scheduleGenerateChallenge(int sensorId, int userId, @NonNull IBinder token, @NonNull IFaceServiceReceiver receiver, String opPackageName) { mHandler.post(() -> { + mFaceSensors.get(sensorId).scheduleFaceUpdateActiveUserClient(userId); final FaceGenerateChallengeClient client = new FaceGenerateChallengeClient(mContext, mFaceSensors.get(sensorId).getLazySession(), token, new ClientMonitorCallbackConverter(receiver), userId, opPackageName, sensorId, @@ -416,6 +505,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { @Nullable Surface previewSurface, boolean debugConsent) { final long id = mRequestCounter.incrementAndGet(); mHandler.post(() -> { + mFaceSensors.get(sensorId).scheduleFaceUpdateActiveUserClient(userId); final int maxTemplatesPerUser = mFaceSensors.get( sensorId).getSensorProperties().maxEnrollmentsPerUser; final FaceEnrollClient client = new FaceEnrollClient(mContext, @@ -427,18 +517,23 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector), mBiometricContext, maxTemplatesPerUser, debugConsent); - scheduleForSensor(sensorId, client, new ClientMonitorCompositeCallback( - mBiometricStateCallback, new ClientMonitorCallback() { - @Override - public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, - boolean success) { - ClientMonitorCallback.super.onClientFinished(clientMonitor, success); - if (success) { - scheduleLoadAuthenticatorIdsForUser(sensorId, userId); - scheduleInvalidationRequest(sensorId, userId); + if (Flags.deHidl()) { + scheduleForSensor(sensorId, client, mBiometricStateCallback); + } else { + scheduleForSensor(sensorId, client, new ClientMonitorCompositeCallback( + mBiometricStateCallback, new ClientMonitorCallback() { + @Override + public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, + boolean success) { + ClientMonitorCallback.super.onClientFinished(clientMonitor, + success); + if (success) { + scheduleLoadAuthenticatorIdsForUser(sensorId, userId); + scheduleInvalidationRequest(sensorId, userId); + } } - } - })); + })); + } }); return id; } @@ -486,6 +581,13 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { final int userId = options.getUserId(); final int sensorId = options.getSensorId(); final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId); + mFaceSensors.get(sensorId).scheduleFaceUpdateActiveUserClient(userId); + final LockoutTracker lockoutTracker; + if (Flags.deHidl()) { + lockoutTracker = mFaceSensors.get(sensorId).getLockoutTracker(true /* forAuth */); + } else { + lockoutTracker = null; + } final FaceAuthenticationClient client = new FaceAuthenticationClient( mContext, mFaceSensors.get(sensorId).getLazySession(), token, requestId, callback, operationId, restricted, options, cookie, @@ -493,7 +595,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { createLogger(BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient, mAuthenticationStatsCollector), mBiometricContext, isStrongBiometric, - mUsageStats, null /* lockoutTracker */, + mUsageStats, lockoutTracker, allowBackgroundAuthentication, Utils.getCurrentStrength(sensorId)); scheduleForSensor(sensorId, client, new ClientMonitorCallback() { @Override @@ -555,6 +657,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { private void scheduleRemoveSpecifiedIds(int sensorId, @NonNull IBinder token, int[] faceIds, int userId, @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) { mHandler.post(() -> { + mFaceSensors.get(sensorId).scheduleFaceUpdateActiveUserClient(userId); final FaceRemovalClient client = new FaceRemovalClient(mContext, mFaceSensors.get(sensorId).getLazySession(), token, new ClientMonitorCallbackConverter(receiver), faceIds, userId, @@ -571,6 +674,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { @Override public void scheduleResetLockout(int sensorId, int userId, @NonNull byte[] hardwareAuthToken) { mHandler.post(() -> { + mFaceSensors.get(sensorId).scheduleFaceUpdateActiveUserClient(userId); final FaceResetLockoutClient client = new FaceResetLockoutClient( mContext, mFaceSensors.get(sensorId).getLazySession(), userId, mContext.getOpPackageName(), sensorId, @@ -578,8 +682,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector), mBiometricContext, hardwareAuthToken, - mFaceSensors.get(sensorId).getLockoutCache(), mLockoutResetDispatcher, - Utils.getCurrentStrength(sensorId)); + mFaceSensors.get(sensorId).getLockoutTracker(false/* forAuth */), + mLockoutResetDispatcher, Utils.getCurrentStrength(sensorId)); scheduleForSensor(sensorId, client); }); @@ -590,6 +694,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { boolean enabled, @NonNull byte[] hardwareAuthToken, @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName) { mHandler.post(() -> { + mFaceSensors.get(sensorId).scheduleFaceUpdateActiveUserClient(userId); final List<Face> faces = FaceUtils.getInstance(sensorId) .getBiometricsForUser(mContext, userId); if (faces.isEmpty()) { @@ -610,6 +715,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { public void scheduleGetFeature(int sensorId, @NonNull IBinder token, int userId, int feature, @NonNull ClientMonitorCallbackConverter callback, @NonNull String opPackageName) { mHandler.post(() -> { + mFaceSensors.get(sensorId).scheduleFaceUpdateActiveUserClient(userId); final List<Face> faces = FaceUtils.getInstance(sensorId) .getBiometricsForUser(mContext, userId); if (faces.isEmpty()) { @@ -641,6 +747,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { public void scheduleInternalCleanup(int sensorId, int userId, @Nullable ClientMonitorCallback callback, boolean favorHalEnrollments) { mHandler.post(() -> { + mFaceSensors.get(sensorId).scheduleFaceUpdateActiveUserClient(userId); final FaceInternalCleanupClient client = new FaceInternalCleanupClient(mContext, mFaceSensors.get(sensorId).getLazySession(), userId, @@ -760,4 +867,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider { } biometricScheduler.startWatchdog(); } + + public boolean getTestHalEnabled() { + return mTestHalEnabled; + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java index 77b5592c5064..d02eefaed101 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceResetLockoutClient.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.content.Context; import android.hardware.biometrics.BiometricManager.Authenticators; import android.hardware.biometrics.face.IFace; +import android.hardware.biometrics.face.ISession; import android.hardware.keymaster.HardwareAuthToken; import android.os.RemoteException; import android.util.Slog; @@ -34,6 +35,7 @@ import com.android.server.biometrics.sensors.ErrorConsumer; import com.android.server.biometrics.sensors.HalClientMonitor; import com.android.server.biometrics.sensors.LockoutResetDispatcher; import com.android.server.biometrics.sensors.LockoutTracker; +import com.android.server.biometrics.sensors.face.hidl.HidlToAidlSessionAdapter; import java.util.function.Supplier; @@ -47,7 +49,7 @@ public class FaceResetLockoutClient extends HalClientMonitor<AidlSession> implem private static final String TAG = "FaceResetLockoutClient"; private final HardwareAuthToken mHardwareAuthToken; - private final LockoutTracker mLockoutCache; + private final LockoutTracker mLockoutTracker; private final LockoutResetDispatcher mLockoutResetDispatcher; private final int mBiometricStrength; @@ -60,7 +62,7 @@ public class FaceResetLockoutClient extends HalClientMonitor<AidlSession> implem super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner, 0 /* cookie */, sensorId, logger, biometricContext); mHardwareAuthToken = HardwareAuthTokenUtils.toHardwareAuthToken(hardwareAuthToken); - mLockoutCache = lockoutTracker; + mLockoutTracker = lockoutTracker; mLockoutResetDispatcher = lockoutResetDispatcher; mBiometricStrength = biometricStrength; } @@ -79,7 +81,11 @@ public class FaceResetLockoutClient extends HalClientMonitor<AidlSession> implem @Override protected void startHalOperation() { try { - getFreshDaemon().getSession().resetLockout(mHardwareAuthToken); + final ISession session = getFreshDaemon().getSession(); + session.resetLockout(mHardwareAuthToken); + if (session instanceof HidlToAidlSessionAdapter) { + mCallback.onClientFinished(this, true /* success */); + } } catch (RemoteException e) { Slog.e(TAG, "Unable to reset lockout", e); mCallback.onClientFinished(this, false /* success */); @@ -87,7 +93,7 @@ public class FaceResetLockoutClient extends HalClientMonitor<AidlSession> implem } void onLockoutCleared() { - resetLocalLockoutStateToNone(getSensorId(), getTargetUserId(), mLockoutCache, + resetLocalLockoutStateToNone(getSensorId(), getTargetUserId(), mLockoutTracker, mLockoutResetDispatcher, getBiometricContext().getAuthSessionCoordinator(), mBiometricStrength, getRequestId()); mCallback.onClientFinished(this, true /* success */); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java index 54e66eb4cca4..3e5c59914913 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/Sensor.java @@ -21,16 +21,20 @@ import android.annotation.Nullable; import android.content.Context; import android.content.pm.UserInfo; import android.hardware.biometrics.BiometricsProtoEnums; +import android.hardware.biometrics.ComponentInfoInternal; import android.hardware.biometrics.ITestSession; import android.hardware.biometrics.ITestSessionCallback; +import android.hardware.biometrics.common.ComponentInfo; import android.hardware.biometrics.face.IFace; import android.hardware.biometrics.face.ISession; +import android.hardware.biometrics.face.SensorProps; import android.hardware.face.FaceManager; import android.hardware.face.FaceSensorPropertiesInternal; import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; +import android.os.ServiceManager; import android.os.UserHandle; import android.os.UserManager; import android.util.Slog; @@ -38,6 +42,7 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.FrameworkStatsLog; +import com.android.server.biometrics.Flags; import com.android.server.biometrics.SensorServiceStateProto; import com.android.server.biometrics.SensorStateProto; import com.android.server.biometrics.UserStateProto; @@ -49,12 +54,15 @@ import com.android.server.biometrics.sensors.BiometricScheduler; import com.android.server.biometrics.sensors.ErrorConsumer; import com.android.server.biometrics.sensors.LockoutCache; import com.android.server.biometrics.sensors.LockoutResetDispatcher; +import com.android.server.biometrics.sensors.LockoutTracker; import com.android.server.biometrics.sensors.StartUserClient; import com.android.server.biometrics.sensors.StopUserClient; import com.android.server.biometrics.sensors.UserAwareBiometricScheduler; import com.android.server.biometrics.sensors.face.FaceUtils; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.function.Supplier; @@ -71,25 +79,53 @@ public class Sensor { @NonNull private final IBinder mToken; @NonNull private final Handler mHandler; @NonNull private final FaceSensorPropertiesInternal mSensorProperties; - @NonNull private final UserAwareBiometricScheduler mScheduler; - @NonNull private final LockoutCache mLockoutCache; + @NonNull private BiometricScheduler mScheduler; + @Nullable private LockoutTracker mLockoutTracker; @NonNull private final Map<Integer, Long> mAuthenticatorIds; - @NonNull private final Supplier<AidlSession> mLazySession; + @NonNull private Supplier<AidlSession> mLazySession; @Nullable AidlSession mCurrentSession; + @NonNull BiometricContext mBiometricContext; Sensor(@NonNull String tag, @NonNull FaceProvider provider, @NonNull Context context, @NonNull Handler handler, @NonNull FaceSensorPropertiesInternal sensorProperties, @NonNull LockoutResetDispatcher lockoutResetDispatcher, - @NonNull BiometricContext biometricContext, AidlSession session) { + @NonNull BiometricContext biometricContext, @Nullable AidlSession session) { mTag = tag; mProvider = provider; mContext = context; mToken = new Binder(); mHandler = handler; mSensorProperties = sensorProperties; - mScheduler = new UserAwareBiometricScheduler(tag, + mBiometricContext = biometricContext; + mAuthenticatorIds = new HashMap<>(); + } + + Sensor(@NonNull String tag, @NonNull FaceProvider provider, @NonNull Context context, + @NonNull Handler handler, @NonNull FaceSensorPropertiesInternal sensorProperties, + @NonNull LockoutResetDispatcher lockoutResetDispatcher, + @NonNull BiometricContext biometricContext) { + this(tag, provider, context, handler, sensorProperties, lockoutResetDispatcher, + biometricContext, null); + } + + public Sensor(@NonNull String tag, @NonNull FaceProvider provider, @NonNull Context context, + @NonNull Handler handler, @NonNull SensorProps prop, + @NonNull LockoutResetDispatcher lockoutResetDispatcher, + @NonNull BiometricContext biometricContext, + boolean resetLockoutRequiresChallenge) { + this(tag, provider, context, handler, + getFaceSensorPropertiesInternal(prop, resetLockoutRequiresChallenge), + lockoutResetDispatcher, biometricContext, null); + } + + /** + * Initialize biometric scheduler, lockout tracker and session for the sensor. + */ + public void init(LockoutResetDispatcher lockoutResetDispatcher, + FaceProvider provider) { + mScheduler = new UserAwareBiometricScheduler(mTag, BiometricScheduler.SENSOR_TYPE_FACE, null /* gestureAvailabilityDispatcher */, () -> mCurrentSession != null ? mCurrentSession.getUserId() : UserHandle.USER_NULL, new UserAwareBiometricScheduler.UserSwitchCallback() { @@ -98,7 +134,7 @@ public class Sensor { public StopUserClient<?> getStopUserClient(int userId) { return new FaceStopUserClient(mContext, mLazySession, mToken, userId, mSensorProperties.sensorId, - BiometricLogger.ofUnknown(mContext), biometricContext, + BiometricLogger.ofUnknown(mContext), mBiometricContext, () -> mCurrentSession = null); } @@ -107,13 +143,36 @@ public class Sensor { public StartUserClient<?, ?> getStartUserClient(int newUserId) { final int sensorId = mSensorProperties.sensorId; - final AidlResponseHandler resultController = new AidlResponseHandler( - mContext, mScheduler, sensorId, newUserId, - mLockoutCache, lockoutResetDispatcher, - biometricContext.getAuthSessionCoordinator(), () -> { - Slog.e(mTag, "Got ERROR_HW_UNAVAILABLE"); - mCurrentSession = null; - }); + final AidlResponseHandler resultController; + if (Flags.deHidl()) { + resultController = new AidlResponseHandler( + mContext, mScheduler, sensorId, newUserId, + mLockoutTracker, lockoutResetDispatcher, + mBiometricContext.getAuthSessionCoordinator(), () -> {}, + new AidlResponseHandler.AidlResponseHandlerCallback() { + @Override + public void onEnrollSuccess() { + mProvider.scheduleLoadAuthenticatorIdsForUser(sensorId, + newUserId); + mProvider.scheduleInvalidationRequest(sensorId, + newUserId); + } + + @Override + public void onHardwareUnavailable() { + Slog.e(mTag, "Face sensor hardware unavailable."); + mCurrentSession = null; + } + }); + } else { + resultController = new AidlResponseHandler( + mContext, mScheduler, sensorId, newUserId, + mLockoutTracker, lockoutResetDispatcher, + mBiometricContext.getAuthSessionCoordinator(), () -> { + Slog.e(mTag, "Got ERROR_HW_UNAVAILABLE"); + mCurrentSession = null; + }); + } final StartUserClient.UserStartedCallback<ISession> userStartedCallback = (userIdStarted, newSession, halInterfaceVersion) -> { @@ -136,32 +195,42 @@ public class Sensor { return new FaceStartUserClient(mContext, provider::getHalInstance, mToken, newUserId, mSensorProperties.sensorId, - BiometricLogger.ofUnknown(mContext), biometricContext, + BiometricLogger.ofUnknown(mContext), mBiometricContext, resultController, userStartedCallback); } }); - mLockoutCache = new LockoutCache(); - mAuthenticatorIds = new HashMap<>(); mLazySession = () -> mCurrentSession != null ? mCurrentSession : null; + mLockoutTracker = new LockoutCache(); } - Sensor(@NonNull String tag, @NonNull FaceProvider provider, @NonNull Context context, - @NonNull Handler handler, @NonNull FaceSensorPropertiesInternal sensorProperties, - @NonNull LockoutResetDispatcher lockoutResetDispatcher, - @NonNull BiometricContext biometricContext) { - this(tag, provider, context, handler, sensorProperties, lockoutResetDispatcher, - biometricContext, null); + private static FaceSensorPropertiesInternal getFaceSensorPropertiesInternal(SensorProps prop, + boolean resetLockoutRequiresChallenge) { + final List<ComponentInfoInternal> componentInfo = new ArrayList<>(); + if (prop.commonProps.componentInfo != null) { + for (ComponentInfo info : prop.commonProps.componentInfo) { + componentInfo.add(new ComponentInfoInternal(info.componentId, + info.hardwareVersion, info.firmwareVersion, info.serialNumber, + info.softwareVersion)); + } + } + final FaceSensorPropertiesInternal internalProp = new FaceSensorPropertiesInternal( + prop.commonProps.sensorId, prop.commonProps.sensorStrength, + prop.commonProps.maxEnrollmentsPerUser, componentInfo, prop.sensorType, + prop.supportsDetectInteraction, prop.halControlsPreview, + resetLockoutRequiresChallenge); + + return internalProp; } - @NonNull Supplier<AidlSession> getLazySession() { + @NonNull public Supplier<AidlSession> getLazySession() { return mLazySession; } - @NonNull FaceSensorPropertiesInternal getSensorProperties() { + @NonNull protected FaceSensorPropertiesInternal getSensorProperties() { return mSensorProperties; } - @VisibleForTesting @Nullable AidlSession getSessionForUser(int userId) { + @VisibleForTesting @Nullable protected AidlSession getSessionForUser(int userId) { if (mCurrentSession != null && mCurrentSession.getUserId() == userId) { return mCurrentSession; } else { @@ -174,15 +243,18 @@ public class Sensor { mProvider, this); } - @NonNull BiometricScheduler getScheduler() { + @NonNull public BiometricScheduler getScheduler() { return mScheduler; } - @NonNull LockoutCache getLockoutCache() { - return mLockoutCache; + @NonNull protected LockoutTracker getLockoutTracker(boolean forAuth) { + if (forAuth) { + return null; + } + return mLockoutTracker; } - @NonNull Map<Integer, Long> getAuthenticatorIds() { + @NonNull protected Map<Integer, Long> getAuthenticatorIds() { return mAuthenticatorIds; } @@ -253,4 +325,49 @@ public class Sensor { mScheduler.reset(); mCurrentSession = null; } + + protected BiometricContext getBiometricContext() { + return mBiometricContext; + } + + protected Handler getHandler() { + return mHandler; + } + + protected Context getContext() { + return mContext; + } + + /** + * Schedules FaceUpdateActiveUserClient for user id. + */ + public void scheduleFaceUpdateActiveUserClient(int userId) {} + + /** + * Returns true if the sensor hardware is detected. + */ + public boolean isHardwareDetected(String halInstanceName) { + if (mTestHalEnabled) { + return true; + } + return ServiceManager.checkService(IFace.DESCRIPTOR + "/" + halInstanceName) != null; + } + + /** + * Returns lockout mode of this sensor. + */ + @LockoutTracker.LockoutMode + public int getLockoutModeForUser(int userId) { + return mBiometricContext.getAuthSessionCoordinator().getLockoutStateFor(userId, + Utils.getCurrentStrength(mSensorProperties.sensorId)); + } + + public void setScheduler(BiometricScheduler scheduler) { + mScheduler = scheduler; + } + + public void setLazySession( + Supplier<AidlSession> lazySession) { + mLazySession = lazySession; + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java index 8385c3fa7103..0c34d702184a 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceUpdateActiveUserClient.java @@ -27,13 +27,14 @@ import com.android.server.biometrics.BiometricsProto; import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.ClientMonitorCallback; -import com.android.server.biometrics.sensors.HalClientMonitor; +import com.android.server.biometrics.sensors.StartUserClient; +import com.android.server.biometrics.sensors.face.aidl.AidlSession; import java.io.File; import java.util.Map; import java.util.function.Supplier; -public class FaceUpdateActiveUserClient extends HalClientMonitor<IBiometricsFace> { +public class FaceUpdateActiveUserClient extends StartUserClient<IBiometricsFace, AidlSession> { private static final String TAG = "FaceUpdateActiveUserClient"; private static final String FACE_DATA_DIR = "facedata"; @@ -45,8 +46,18 @@ public class FaceUpdateActiveUserClient extends HalClientMonitor<IBiometricsFace int sensorId, @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, boolean hasEnrolledBiometrics, @NonNull Map<Integer, Long> authenticatorIds) { - super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner, - 0 /* cookie */, sensorId, logger, biometricContext); + this(context, lazyDaemon, (newUserId, newUser, halInterfaceVersion) -> {}, + userId, owner, sensorId, logger, biometricContext, hasEnrolledBiometrics, + authenticatorIds); + } + + FaceUpdateActiveUserClient(@NonNull Context context, + @NonNull Supplier<IBiometricsFace> lazyDaemon, UserStartedCallback userStartedCallback, + int userId, @NonNull String owner, int sensorId, @NonNull BiometricLogger logger, + @NonNull BiometricContext biometricContext, boolean hasEnrolledBiometrics, + @NonNull Map<Integer, Long> authenticatorIds) { + super(context, lazyDaemon, null /* token */, userId, sensorId, logger, biometricContext, + userStartedCallback); mHasEnrolledBiometrics = hasEnrolledBiometrics; mAuthenticatorIds = authenticatorIds; } @@ -77,6 +88,7 @@ public class FaceUpdateActiveUserClient extends HalClientMonitor<IBiometricsFace daemon.setActiveUser(getTargetUserId(), storePath.getAbsolutePath()); mAuthenticatorIds.put(getTargetUserId(), mHasEnrolledBiometrics ? daemon.getAuthenticatorId().value : 0L); + mUserStartedCallback.onUserStarted(getTargetUserId(), null, 0); mCallback.onClientFinished(this, true /* success */); } catch (RemoteException e) { Slog.e(TAG, "Failed to setActiveUser: " + e); diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlCallbackConverter.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlCallbackConverter.java index 36a9790d2d4b..7a574cecc0b1 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlCallbackConverter.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlCallbackConverter.java @@ -112,4 +112,8 @@ public class HidlToAidlCallbackConverter extends IBiometricsFaceClientCallback.S void onAuthenticatorIdRetrieved(long authenticatorId) { mAidlResponseHandler.onAuthenticatorIdRetrieved(authenticatorId); } + + void onUnsupportedClientScheduled() { + mAidlResponseHandler.onUnsupportedClientScheduled(); + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapter.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapter.java new file mode 100644 index 000000000000..6355cb57a752 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapter.java @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors.face.hidl; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.content.pm.UserInfo; +import android.hardware.biometrics.face.SensorProps; +import android.hardware.biometrics.face.V1_0.IBiometricsFace; +import android.os.Handler; +import android.os.IHwBinder; +import android.os.RemoteException; +import android.os.UserHandle; +import android.os.UserManager; +import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; +import com.android.server.biometrics.sensors.AuthSessionCoordinator; +import com.android.server.biometrics.sensors.BiometricScheduler; +import com.android.server.biometrics.sensors.LockoutResetDispatcher; +import com.android.server.biometrics.sensors.LockoutTracker; +import com.android.server.biometrics.sensors.StartUserClient; +import com.android.server.biometrics.sensors.face.FaceUtils; +import com.android.server.biometrics.sensors.face.LockoutHalImpl; +import com.android.server.biometrics.sensors.face.aidl.AidlResponseHandler; +import com.android.server.biometrics.sensors.face.aidl.AidlSession; +import com.android.server.biometrics.sensors.face.aidl.FaceProvider; +import com.android.server.biometrics.sensors.face.aidl.Sensor; + +/** + * Convert HIDL sensor configurations to an AIDL Sensor. + */ +public class HidlToAidlSensorAdapter extends Sensor implements IHwBinder.DeathRecipient{ + + private static final String TAG = "HidlToAidlSensorAdapter"; + + private IBiometricsFace mDaemon; + private AidlSession mSession; + private int mCurrentUserId = UserHandle.USER_NULL; + private final Runnable mInternalCleanupAndGetFeatureRunnable; + private final FaceProvider mFaceProvider; + private final LockoutResetDispatcher mLockoutResetDispatcher; + private final AuthSessionCoordinator mAuthSessionCoordinator; + private final AidlResponseHandler.AidlResponseHandlerCallback mAidlResponseHandlerCallback; + private final StartUserClient.UserStartedCallback<AidlSession> mUserStartedCallback = + (newUserId, newUser, halInterfaceVersion) -> { + if (newUserId != mCurrentUserId) { + handleUserChanged(newUserId); + } + }; + private LockoutHalImpl mLockoutTracker; + + public HidlToAidlSensorAdapter(@NonNull String tag, + @NonNull FaceProvider provider, + @NonNull Context context, + @NonNull Handler handler, + @NonNull SensorProps prop, + @NonNull LockoutResetDispatcher lockoutResetDispatcher, + @NonNull BiometricContext biometricContext, + boolean resetLockoutRequiresChallenge, + @NonNull Runnable internalCleanupAndGetFeatureRunnable) { + this(tag, provider, context, handler, prop, lockoutResetDispatcher, biometricContext, + resetLockoutRequiresChallenge, internalCleanupAndGetFeatureRunnable, + new AuthSessionCoordinator(), null /* daemon */, + null /* onEnrollSuccessCallback */); + } + + @VisibleForTesting + HidlToAidlSensorAdapter(@NonNull String tag, + @NonNull FaceProvider provider, + @NonNull Context context, + @NonNull Handler handler, + @NonNull SensorProps prop, + @NonNull LockoutResetDispatcher lockoutResetDispatcher, + @NonNull BiometricContext biometricContext, + boolean resetLockoutRequiresChallenge, + @NonNull Runnable internalCleanupAndGetFeatureRunnable, + @NonNull AuthSessionCoordinator authSessionCoordinator, + @Nullable IBiometricsFace daemon, + @Nullable AidlResponseHandler.AidlResponseHandlerCallback aidlResponseHandlerCallback) { + super(tag, provider, context, handler, prop, lockoutResetDispatcher, biometricContext, + resetLockoutRequiresChallenge); + mInternalCleanupAndGetFeatureRunnable = internalCleanupAndGetFeatureRunnable; + mFaceProvider = provider; + mLockoutResetDispatcher = lockoutResetDispatcher; + mAuthSessionCoordinator = authSessionCoordinator; + mDaemon = daemon; + mAidlResponseHandlerCallback = aidlResponseHandlerCallback == null + ? new AidlResponseHandler.AidlResponseHandlerCallback() { + @Override + public void onEnrollSuccess() { + scheduleFaceUpdateActiveUserClient(mCurrentUserId); + } + + @Override + public void onHardwareUnavailable() { + mDaemon = null; + mCurrentUserId = UserHandle.USER_NULL; + } + } : aidlResponseHandlerCallback; + } + + @Override + public void scheduleFaceUpdateActiveUserClient(int userId) { + getScheduler().scheduleClientMonitor(getFaceUpdateActiveUserClient(userId)); + } + + @Override + public void serviceDied(long cookie) { + Slog.d(TAG, "HAL died."); + mDaemon = null; + } + + @Override + public boolean isHardwareDetected(String halInstanceName) { + return getIBiometricsFace() != null; + } + + @Override + @LockoutTracker.LockoutMode + public int getLockoutModeForUser(int userId) { + return mLockoutTracker.getLockoutModeForUser(userId); + } + + @Override + public void init(LockoutResetDispatcher lockoutResetDispatcher, + FaceProvider provider) { + setScheduler(new BiometricScheduler(TAG, BiometricScheduler.SENSOR_TYPE_FACE, + null /* gestureAvailabilityTracker */)); + setLazySession(this::getSession); + mLockoutTracker = new LockoutHalImpl(); + } + + @Override + @VisibleForTesting @Nullable protected AidlSession getSessionForUser(int userId) { + if (mSession != null && mSession.getUserId() == userId) { + return mSession; + } else { + return null; + } + } + + @Override + protected LockoutTracker getLockoutTracker(boolean forAuth) { + return mLockoutTracker; + } + + @NonNull AidlSession getSession() { + if (mDaemon != null && mSession != null) { + return mSession; + } else { + return mSession = new AidlSession(getContext(), this::getIBiometricsFace, + mCurrentUserId, getAidlResponseHandler()); + } + } + + private AidlResponseHandler getAidlResponseHandler() { + return new AidlResponseHandler(getContext(), getScheduler(), getSensorProperties().sensorId, + mCurrentUserId, mLockoutTracker, mLockoutResetDispatcher, + mAuthSessionCoordinator, () -> {}, mAidlResponseHandlerCallback); + } + + private IBiometricsFace getIBiometricsFace() { + if (mFaceProvider.getTestHalEnabled()) { + final TestHal testHal = new TestHal(getContext(), getSensorProperties().sensorId); + testHal.setCallback(new HidlToAidlCallbackConverter(getAidlResponseHandler())); + return testHal; + } + + if (mDaemon != null) { + return mDaemon; + } + + Slog.d(TAG, "Daemon was null, reconnecting, current operation: " + + getScheduler().getCurrentClient()); + + try { + mDaemon = IBiometricsFace.getService(); + } catch (java.util.NoSuchElementException e) { + // Service doesn't exist or cannot be opened. + Slog.w(TAG, "NoSuchElementException", e); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to get face HAL", e); + } + + if (mDaemon == null) { + Slog.w(TAG, "Face HAL not available"); + return null; + } + + mDaemon.asBinder().linkToDeath(this, 0 /* flags */); + + scheduleLoadAuthenticatorIds(); + mInternalCleanupAndGetFeatureRunnable.run(); + return mDaemon; + } + + @VisibleForTesting void handleUserChanged(int newUserId) { + Slog.d(TAG, "User changed. Current user is " + newUserId); + mSession = null; + mCurrentUserId = newUserId; + } + + private void scheduleLoadAuthenticatorIds() { + // Note that this can be performed on the scheduler (as opposed to being done immediately + // when the HAL is (re)loaded, since + // 1) If this is truly the first time it's being performed (e.g. system has just started), + // this will be run very early and way before any applications need to generate keys. + // 2) If this is being performed to refresh the authenticatorIds (e.g. HAL crashed and has + // just been reloaded), the framework already has a cache of the authenticatorIds. This + // is safe because authenticatorIds only change when A) new template has been enrolled, + // or B) all templates are removed. + getHandler().post(() -> { + for (UserInfo user : UserManager.get(getContext()).getAliveUsers()) { + final int targetUserId = user.id; + if (!getAuthenticatorIds().containsKey(targetUserId)) { + scheduleFaceUpdateActiveUserClient(targetUserId); + } + } + }); + } + + private FaceUpdateActiveUserClient getFaceUpdateActiveUserClient(int userId) { + return new FaceUpdateActiveUserClient(getContext(), this::getIBiometricsFace, + mUserStartedCallback, userId, TAG, getSensorProperties().sensorId, + BiometricLogger.ofUnknown(getContext()), getBiometricContext(), + !FaceUtils.getInstance(getSensorProperties().sensorId).getBiometricsForUser( + getContext(), userId).isEmpty(), + getAuthenticatorIds()); + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/AidlToHidlAdapter.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSessionAdapter.java index 489b213677dd..5daf2d4fbcf4 100644 --- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/AidlToHidlAdapter.java +++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSessionAdapter.java @@ -47,34 +47,37 @@ import java.util.List; import java.util.function.Supplier; /** - * Adapter to convert AIDL-specific interface {@link ISession} methods to HIDL implementation. + * Adapter to convert HIDL methods into AIDL interface {@link ISession}. */ -public class AidlToHidlAdapter implements ISession { +public class HidlToAidlSessionAdapter implements ISession { + + private static final String TAG = "HidlToAidlSessionAdapter"; - private final String TAG = "AidlToHidlAdapter"; private static final int CHALLENGE_TIMEOUT_SEC = 600; @DurationMillisLong private static final int GENERATE_CHALLENGE_REUSE_INTERVAL_MILLIS = 60 * 1000; @DurationMillisLong private static final int GENERATE_CHALLENGE_COUNTER_TTL_MILLIS = CHALLENGE_TIMEOUT_SEC * 1000; private static final int INVALID_VALUE = -1; + @VisibleForTesting static final int ENROLL_TIMEOUT_SEC = 75; + private final Clock mClock; private final List<Long> mGeneratedChallengeCount = new ArrayList<>(); - @VisibleForTesting static final int ENROLL_TIMEOUT_SEC = 75; + private final int mUserId; + private final Context mContext; + private long mGenerateChallengeCreatedAt = INVALID_VALUE; private long mGenerateChallengeResult = INVALID_VALUE; @NonNull private Supplier<IBiometricsFace> mSession; - private final int mUserId; private HidlToAidlCallbackConverter mHidlToAidlCallbackConverter; - private final Context mContext; private int mFeature = INVALID_VALUE; - public AidlToHidlAdapter(Context context, Supplier<IBiometricsFace> session, + public HidlToAidlSessionAdapter(Context context, Supplier<IBiometricsFace> session, int userId, AidlResponseHandler aidlResponseHandler) { this(context, session, userId, aidlResponseHandler, Clock.systemUTC()); } - AidlToHidlAdapter(Context context, Supplier<IBiometricsFace> session, int userId, + HidlToAidlSessionAdapter(Context context, Supplier<IBiometricsFace> session, int userId, AidlResponseHandler aidlResponseHandler, Clock clock) { mSession = session; mUserId = userId; @@ -83,42 +86,11 @@ public class AidlToHidlAdapter implements ISession { setCallback(aidlResponseHandler); } - private void setCallback(AidlResponseHandler aidlResponseHandler) { - mHidlToAidlCallbackConverter = new HidlToAidlCallbackConverter(aidlResponseHandler); - try { - mSession.get().setCallback(mHidlToAidlCallbackConverter); - } catch (RemoteException e) { - Slog.d(TAG, "Failed to set callback"); - } - } - @Override public IBinder asBinder() { return null; } - private boolean isGeneratedChallengeCacheValid() { - return mGenerateChallengeCreatedAt != INVALID_VALUE - && mGenerateChallengeResult != INVALID_VALUE - && mClock.millis() - mGenerateChallengeCreatedAt - < GENERATE_CHALLENGE_REUSE_INTERVAL_MILLIS; - } - - private void incrementChallengeCount() { - mGeneratedChallengeCount.add(0, mClock.millis()); - } - - private int decrementChallengeCount() { - final long now = mClock.millis(); - // ignore values that are old in case generate/revoke calls are not matched - // this doesn't ensure revoke if calls are mismatched but it keeps the list from growing - mGeneratedChallengeCount.removeIf(x -> now - x > GENERATE_CHALLENGE_COUNTER_TTL_MILLIS); - if (!mGeneratedChallengeCount.isEmpty()) { - mGeneratedChallengeCount.remove(0); - } - return mGeneratedChallengeCount.size(); - } - @Override public void generateChallenge() throws RemoteException { incrementChallengeCount(); @@ -150,7 +122,7 @@ public class AidlToHidlAdapter implements ISession { @Override public EnrollmentStageConfig[] getEnrollmentConfig(byte enrollmentType) throws RemoteException { - //unsupported in HIDL + Slog.e(TAG, "getEnrollmentConfig unsupported in HIDL"); return null; } @@ -244,19 +216,6 @@ public class AidlToHidlAdapter implements ISession { } } - private int getFaceId() { - FaceManager faceManager = mContext.getSystemService(FaceManager.class); - List<Face> faces = faceManager.getEnrolledFaces(mUserId); - if (faces.isEmpty()) { - Slog.d(TAG, "No faces to get feature from."); - mHidlToAidlCallbackConverter.onError(0 /* deviceId */, mUserId, - BiometricFaceConstants.FACE_ERROR_NOT_ENROLLED, 0 /* vendorCode */); - return INVALID_VALUE; - } - - return faces.get(0).getBiometricId(); - } - @Override public void getAuthenticatorId() throws RemoteException { long authenticatorId = mSession.get().getAuthenticatorId().value; @@ -265,7 +224,8 @@ public class AidlToHidlAdapter implements ISession { @Override public void invalidateAuthenticatorId() throws RemoteException { - //unsupported in HIDL + Slog.e(TAG, "invalidateAuthenticatorId unsupported in HIDL"); + mHidlToAidlCallbackConverter.onUnsupportedClientScheduled(); } @Override @@ -279,47 +239,105 @@ public class AidlToHidlAdapter implements ISession { @Override public void close() throws RemoteException { - //Unsupported in HIDL + Slog.e(TAG, "close unsupported in HIDL"); } @Override public ICancellationSignal authenticateWithContext(long operationId, OperationContext context) throws RemoteException { - //Unsupported in HIDL - return null; + Slog.e(TAG, "authenticateWithContext unsupported in HIDL"); + return authenticate(operationId); } @Override public ICancellationSignal enrollWithContext(HardwareAuthToken hat, byte type, byte[] features, NativeHandle previewSurface, OperationContext context) throws RemoteException { - //Unsupported in HIDL - return null; + Slog.e(TAG, "enrollWithContext unsupported in HIDL"); + return enroll(hat, type, features, previewSurface); } @Override public ICancellationSignal detectInteractionWithContext(OperationContext context) throws RemoteException { - //Unsupported in HIDL - return null; + Slog.e(TAG, "detectInteractionWithContext unsupported in HIDL"); + return detectInteraction(); } @Override public void onContextChanged(OperationContext context) throws RemoteException { - //Unsupported in HIDL + Slog.e(TAG, "onContextChanged unsupported in HIDL"); } @Override public int getInterfaceVersion() throws RemoteException { - //Unsupported in HIDL + Slog.e(TAG, "getInterfaceVersion unsupported in HIDL"); return 0; } @Override public String getInterfaceHash() throws RemoteException { + Slog.e(TAG, "getInterfaceHash unsupported in HIDL"); + return null; + } + + @Override + public ICancellationSignal enrollWithOptions(FaceEnrollOptions options) { //Unsupported in HIDL return null; } + private boolean isGeneratedChallengeCacheValid() { + return mGenerateChallengeCreatedAt != INVALID_VALUE + && mGenerateChallengeResult != INVALID_VALUE + && mClock.millis() - mGenerateChallengeCreatedAt + < GENERATE_CHALLENGE_REUSE_INTERVAL_MILLIS; + } + + private void incrementChallengeCount() { + mGeneratedChallengeCount.add(0, mClock.millis()); + } + + private int decrementChallengeCount() { + final long now = mClock.millis(); + // ignore values that are old in case generate/revoke calls are not matched + // this doesn't ensure revoke if calls are mismatched but it keeps the list from growing + mGeneratedChallengeCount.removeIf(x -> now - x > GENERATE_CHALLENGE_COUNTER_TTL_MILLIS); + if (!mGeneratedChallengeCount.isEmpty()) { + mGeneratedChallengeCount.remove(0); + } + return mGeneratedChallengeCount.size(); + } + + private void setCallback(AidlResponseHandler aidlResponseHandler) { + mHidlToAidlCallbackConverter = new HidlToAidlCallbackConverter(aidlResponseHandler); + try { + if (mSession.get() != null) { + long halId = mSession.get().setCallback(mHidlToAidlCallbackConverter).value; + Slog.d(TAG, "Face HAL ready, HAL ID: " + halId); + if (halId == 0) { + Slog.d(TAG, "Unable to set HIDL callback."); + } + } else { + Slog.e(TAG, "Unable to set HIDL callback. HIDL daemon is null."); + } + } catch (RemoteException e) { + Slog.d(TAG, "Failed to set callback"); + } + } + + private int getFaceId() { + FaceManager faceManager = mContext.getSystemService(FaceManager.class); + List<Face> faces = faceManager.getEnrolledFaces(mUserId); + if (faces.isEmpty()) { + Slog.d(TAG, "No faces to get feature from."); + mHidlToAidlCallbackConverter.onError(0 /* deviceId */, mUserId, + BiometricFaceConstants.FACE_ERROR_NOT_ENROLLED, 0 /* vendorCode */); + return INVALID_VALUE; + } + + return faces.get(0).getBiometricId(); + } + /** * Cancellation in HIDL occurs for the entire session, instead of a specific client. */ @@ -345,10 +363,4 @@ public class AidlToHidlAdapter implements ISession { return null; } } - - @Override - public ICancellationSignal enrollWithOptions(FaceEnrollOptions options) { - //Unsupported in HIDL - return null; - } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java index 83b306b07c27..e01d672d7e34 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java @@ -45,9 +45,11 @@ import android.hardware.biometrics.ITestSession; import android.hardware.biometrics.ITestSessionCallback; import android.hardware.biometrics.fingerprint.IFingerprint; import android.hardware.biometrics.fingerprint.PointerContext; +import android.hardware.biometrics.fingerprint.SensorProps; import android.hardware.fingerprint.Fingerprint; import android.hardware.fingerprint.FingerprintAuthenticateOptions; import android.hardware.fingerprint.FingerprintManager; +import android.hardware.fingerprint.FingerprintSensorConfigurations; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.hardware.fingerprint.FingerprintServiceReceiver; import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback; @@ -80,6 +82,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.DumpUtils; import com.android.internal.widget.LockPatternUtils; import com.android.server.SystemService; +import com.android.server.biometrics.Flags; import com.android.server.biometrics.Utils; import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.sensors.AuthenticationStateListeners; @@ -127,6 +130,8 @@ public class FingerprintService extends SystemService { @NonNull private final Function<String, FingerprintProvider> mFingerprintProvider; @NonNull + private final FingerprintProviderFunction mFingerprintProviderFunction; + @NonNull private final BiometricStateCallback<ServiceProvider, FingerprintSensorPropertiesInternal> mBiometricStateCallback; @NonNull @@ -136,6 +141,11 @@ public class FingerprintService extends SystemService { @NonNull private final FingerprintServiceRegistry mRegistry; + interface FingerprintProviderFunction { + FingerprintProvider getFingerprintProvider(Pair<String, SensorProps[]> filteredSensorProp, + boolean resetLockoutRequiresHardwareAuthToken); + } + /** Receives the incoming binder calls from FingerprintManager. */ @VisibleForTesting final IFingerprintService.Stub mServiceWrapper = new IFingerprintService.Stub() { @@ -874,6 +884,18 @@ public class FingerprintService extends SystemService { @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) @Override // Binder call + public void registerAuthenticatorsLegacy( + @NonNull FingerprintSensorConfigurations fingerprintSensorConfigurations) { + super.registerAuthenticatorsLegacy_enforcePermission(); + if (!fingerprintSensorConfigurations.hasSensorConfigurations()) { + Slog.d(TAG, "No fingerprint sensors available."); + return; + } + mRegistry.registerAll(() -> getProviders(fingerprintSensorConfigurations)); + } + + @android.annotation.EnforcePermission(android.Manifest.permission.USE_BIOMETRIC_INTERNAL) + @Override // Binder call public void registerAuthenticators( @NonNull List<FingerprintSensorPropertiesInternal> hidlSensors) { super.registerAuthenticators_enforcePermission(); @@ -1021,7 +1043,8 @@ public class FingerprintService extends SystemService { () -> IBiometricService.Stub.asInterface( ServiceManager.getService(Context.BIOMETRIC_SERVICE)), () -> ServiceManager.getDeclaredInstances(IFingerprint.DESCRIPTOR), - null /* fingerprintProvider */); + null /* fingerprintProvider */, + null /* fingerprintProviderFunction */); } @VisibleForTesting @@ -1029,7 +1052,8 @@ public class FingerprintService extends SystemService { BiometricContext biometricContext, Supplier<IBiometricService> biometricServiceSupplier, Supplier<String[]> aidlInstanceNameSupplier, - Function<String, FingerprintProvider> fingerprintProvider) { + Function<String, FingerprintProvider> fingerprintProvider, + FingerprintProviderFunction fingerprintProviderFunction) { super(context); mBiometricContext = biometricContext; mAidlInstanceNameSupplier = aidlInstanceNameSupplier; @@ -1049,7 +1073,8 @@ public class FingerprintService extends SystemService { return new FingerprintProvider(getContext(), mBiometricStateCallback, mAuthenticationStateListeners, fp.getSensorProps(), name, mLockoutResetDispatcher, - mGestureAvailabilityDispatcher, mBiometricContext); + mGestureAvailabilityDispatcher, mBiometricContext, + true /* resetLockoutRequiresHardwareAuthToken */); } catch (RemoteException e) { Slog.e(TAG, "Remote exception in getSensorProps: " + fqName); } @@ -1059,6 +1084,22 @@ public class FingerprintService extends SystemService { return null; }; + if (Flags.deHidl()) { + mFingerprintProviderFunction = fingerprintProviderFunction == null + ? (filteredSensorProps, resetLockoutRequiresHardwareAuthToken) -> + new FingerprintProvider( + getContext(), mBiometricStateCallback, + mAuthenticationStateListeners, + filteredSensorProps.second, + filteredSensorProps.first, mLockoutResetDispatcher, + mGestureAvailabilityDispatcher, + mBiometricContext, + resetLockoutRequiresHardwareAuthToken) + : fingerprintProviderFunction; + } else { + mFingerprintProviderFunction = + (filteredSensorProps, resetLockoutRequiresHardwareAuthToken) -> null; + } mHandler = new Handler(Looper.getMainLooper()); mRegistry = new FingerprintServiceRegistry(mServiceWrapper, biometricServiceSupplier); mRegistry.addAllRegisteredCallback(new IFingerprintAuthenticatorsRegisteredCallback.Stub() { @@ -1070,6 +1111,44 @@ public class FingerprintService extends SystemService { }); } + @NonNull + private List<ServiceProvider> getProviders(@NonNull FingerprintSensorConfigurations + fingerprintSensorConfigurations) { + final List<ServiceProvider> providers = new ArrayList<>(); + final Pair<String, SensorProps[]> filteredSensorProps = filterAvailableHalInstances( + fingerprintSensorConfigurations); + providers.add(mFingerprintProviderFunction.getFingerprintProvider(filteredSensorProps, + fingerprintSensorConfigurations.getResetLockoutRequiresHardwareAuthToken())); + + return providers; + } + + @NonNull + private Pair<String, SensorProps[]> filterAvailableHalInstances( + FingerprintSensorConfigurations fingerprintSensorConfigurations) { + Pair<String, SensorProps[]> finalSensorPair = + fingerprintSensorConfigurations.getSensorPair(); + if (fingerprintSensorConfigurations.isSingleSensorConfigurationPresent()) { + return finalSensorPair; + } + + final Pair<String, SensorProps[]> virtualSensorPropsPair = fingerprintSensorConfigurations + .getSensorPairForInstance("virtual"); + if (Utils.isVirtualEnabled(getContext())) { + if (virtualSensorPropsPair != null) { + return virtualSensorPropsPair; + } else { + Slog.e(TAG, "Could not find virtual interface while it is enabled"); + return finalSensorPair; + } + } else { + if (virtualSensorPropsPair != null) { + return fingerprintSensorConfigurations.getSensorPairNotForInstance("virtual"); + } + } + return finalSensorPair; + } + private Pair<List<FingerprintSensorPropertiesInternal>, List<String>> filterAvailableHalInstances( @NonNull List<FingerprintSensorPropertiesInternal> hidlInstances, diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlResponseHandler.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlResponseHandler.java index 4a019436cf6f..bd21cf4002b0 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlResponseHandler.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlResponseHandler.java @@ -25,6 +25,7 @@ import android.hardware.fingerprint.Fingerprint; import android.hardware.keymaster.HardwareAuthToken; import android.util.Slog; +import com.android.server.biometrics.Flags; import com.android.server.biometrics.HardwareAuthTokenUtils; import com.android.server.biometrics.Utils; import com.android.server.biometrics.sensors.AcquisitionClient; @@ -34,9 +35,9 @@ import com.android.server.biometrics.sensors.BaseClientMonitor; import com.android.server.biometrics.sensors.BiometricScheduler; import com.android.server.biometrics.sensors.EnumerateConsumer; import com.android.server.biometrics.sensors.ErrorConsumer; -import com.android.server.biometrics.sensors.LockoutCache; import com.android.server.biometrics.sensors.LockoutConsumer; import com.android.server.biometrics.sensors.LockoutResetDispatcher; +import com.android.server.biometrics.sensors.LockoutTracker; import com.android.server.biometrics.sensors.RemovalConsumer; import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils; @@ -59,6 +60,21 @@ public class AidlResponseHandler extends ISessionCallback.Stub { void onHardwareUnavailable(); } + /** + * Interface to send results to the AidlResponseHandler's owner. + */ + public interface AidlResponseHandlerCallback { + /** + * Invoked when enrollment is successful. + */ + void onEnrollSuccess(); + + /** + * Invoked when the HAL sends ERROR_HW_UNAVAILABLE. + */ + void onHardwareUnavailable(); + } + private static final String TAG = "AidlResponseHandler"; @NonNull @@ -68,28 +84,49 @@ public class AidlResponseHandler extends ISessionCallback.Stub { private final int mSensorId; private final int mUserId; @NonNull - private final LockoutCache mLockoutCache; + private final LockoutTracker mLockoutTracker; @NonNull private final LockoutResetDispatcher mLockoutResetDispatcher; @NonNull private final AuthSessionCoordinator mAuthSessionCoordinator; @NonNull private final HardwareUnavailableCallback mHardwareUnavailableCallback; + @NonNull + private final AidlResponseHandlerCallback mAidlResponseHandlerCallback; public AidlResponseHandler(@NonNull Context context, @NonNull BiometricScheduler scheduler, int sensorId, int userId, - @NonNull LockoutCache lockoutTracker, + @NonNull LockoutTracker lockoutTracker, @NonNull LockoutResetDispatcher lockoutResetDispatcher, @NonNull AuthSessionCoordinator authSessionCoordinator, @NonNull HardwareUnavailableCallback hardwareUnavailableCallback) { + this(context, scheduler, sensorId, userId, lockoutTracker, lockoutResetDispatcher, + authSessionCoordinator, hardwareUnavailableCallback, + new AidlResponseHandlerCallback() { + @Override + public void onEnrollSuccess() {} + + @Override + public void onHardwareUnavailable() {} + }); + } + + public AidlResponseHandler(@NonNull Context context, + @NonNull BiometricScheduler scheduler, int sensorId, int userId, + @NonNull LockoutTracker lockoutTracker, + @NonNull LockoutResetDispatcher lockoutResetDispatcher, + @NonNull AuthSessionCoordinator authSessionCoordinator, + @NonNull HardwareUnavailableCallback hardwareUnavailableCallback, + @NonNull AidlResponseHandlerCallback aidlResponseHandlerCallback) { mContext = context; mScheduler = scheduler; mSensorId = sensorId; mUserId = userId; - mLockoutCache = lockoutTracker; + mLockoutTracker = lockoutTracker; mLockoutResetDispatcher = lockoutResetDispatcher; mAuthSessionCoordinator = authSessionCoordinator; mHardwareUnavailableCallback = hardwareUnavailableCallback; + mAidlResponseHandlerCallback = aidlResponseHandlerCallback; } @Override @@ -105,27 +142,26 @@ public class AidlResponseHandler extends ISessionCallback.Stub { @Override public void onChallengeGenerated(long challenge) { handleResponse(FingerprintGenerateChallengeClient.class, (c) -> c.onChallengeGenerated( - mSensorId, mUserId, challenge), null); + mSensorId, mUserId, challenge)); } @Override public void onChallengeRevoked(long challenge) { handleResponse(FingerprintRevokeChallengeClient.class, (c) -> c.onChallengeRevoked( - challenge), null); + challenge)); } /** * Handles acquired messages sent by the HAL (specifically for HIDL HAL). */ public void onAcquired(int acquiredInfo, int vendorCode) { - handleResponse(AcquisitionClient.class, (c) -> c.onAcquired(acquiredInfo, vendorCode), - null); + handleResponse(AcquisitionClient.class, (c) -> c.onAcquired(acquiredInfo, vendorCode)); } @Override public void onAcquired(byte info, int vendorCode) { handleResponse(AcquisitionClient.class, (c) -> c.onAcquired( - AidlConversionUtils.toFrameworkAcquiredInfo(info), vendorCode), null); + AidlConversionUtils.toFrameworkAcquiredInfo(info), vendorCode)); } /** @@ -135,9 +171,13 @@ public class AidlResponseHandler extends ISessionCallback.Stub { handleResponse(ErrorConsumer.class, (c) -> { c.onError(error, vendorCode); if (error == Error.HW_UNAVAILABLE) { - mHardwareUnavailableCallback.onHardwareUnavailable(); + if (Flags.deHidl()) { + mAidlResponseHandlerCallback.onHardwareUnavailable(); + } else { + mHardwareUnavailableCallback.onHardwareUnavailable(); + } } - }, null); + }); } @Override @@ -158,8 +198,12 @@ public class AidlResponseHandler extends ISessionCallback.Stub { .getUniqueName(mContext, currentUserId); final Fingerprint fingerprint = new Fingerprint(name, currentUserId, enrollmentId, mSensorId); - handleResponse(FingerprintEnrollClient.class, (c) -> c.onEnrollResult(fingerprint, - remaining), null); + handleResponse(FingerprintEnrollClient.class, (c) -> { + c.onEnrollResult(fingerprint, remaining); + if (remaining == 0) { + mAidlResponseHandlerCallback.onEnrollSuccess(); + } + }); } @Override @@ -184,13 +228,12 @@ public class AidlResponseHandler extends ISessionCallback.Stub { @Override public void onLockoutTimed(long durationMillis) { - handleResponse(LockoutConsumer.class, (c) -> c.onLockoutTimed(durationMillis), - null); + handleResponse(LockoutConsumer.class, (c) -> c.onLockoutTimed(durationMillis)); } @Override public void onLockoutPermanent() { - handleResponse(LockoutConsumer.class, LockoutConsumer::onLockoutPermanent, null); + handleResponse(LockoutConsumer.class, LockoutConsumer::onLockoutPermanent); } @Override @@ -198,7 +241,7 @@ public class AidlResponseHandler extends ISessionCallback.Stub { handleResponse(FingerprintResetLockoutClient.class, FingerprintResetLockoutClient::onLockoutCleared, (c) -> FingerprintResetLockoutClient.resetLocalLockoutStateToNone( - mSensorId, mUserId, mLockoutCache, mLockoutResetDispatcher, + mSensorId, mUserId, mLockoutTracker, mLockoutResetDispatcher, mAuthSessionCoordinator, Utils.getCurrentStrength(mSensorId), -1 /* requestId */)); } @@ -206,49 +249,74 @@ public class AidlResponseHandler extends ISessionCallback.Stub { @Override public void onInteractionDetected() { handleResponse(FingerprintDetectClient.class, - FingerprintDetectClient::onInteractionDetected, null); + FingerprintDetectClient::onInteractionDetected); } @Override public void onEnrollmentsEnumerated(int[] enrollmentIds) { if (enrollmentIds.length > 0) { for (int i = 0; i < enrollmentIds.length; i++) { - final Fingerprint fp = new Fingerprint("", enrollmentIds[i], mSensorId); - int finalI = i; - handleResponse(EnumerateConsumer.class, (c) -> c.onEnumerationResult(fp, - enrollmentIds.length - finalI - 1), null); + onEnrollmentEnumerated(enrollmentIds[i], + enrollmentIds.length - i - 1 /* remaining */); } } else { - handleResponse(EnumerateConsumer.class, (c) -> c.onEnumerationResult(null, - 0), null); + handleResponse(EnumerateConsumer.class, (c) -> c.onEnumerationResult( + null /* identifier */, + 0 /* remaining */)); } } + /** + * Handle enumerated fingerprint. + */ + public void onEnrollmentEnumerated(int enrollmentId, int remaining) { + final Fingerprint fp = new Fingerprint("", enrollmentId, mSensorId); + handleResponse(EnumerateConsumer.class, (c) -> c.onEnumerationResult(fp, remaining)); + } + + /** + * Handle removal of fingerprint. + */ + public void onEnrollmentRemoved(int enrollmentId, int remaining) { + final Fingerprint fp = new Fingerprint("", enrollmentId, mSensorId); + handleResponse(RemovalConsumer.class, (c) -> c.onRemoved(fp, remaining)); + } + @Override public void onEnrollmentsRemoved(int[] enrollmentIds) { if (enrollmentIds.length > 0) { for (int i = 0; i < enrollmentIds.length; i++) { - final Fingerprint fp = new Fingerprint("", enrollmentIds[i], mSensorId); - int finalI = i; - handleResponse(RemovalConsumer.class, (c) -> c.onRemoved(fp, - enrollmentIds.length - finalI - 1), null); + onEnrollmentRemoved(enrollmentIds[i], enrollmentIds.length - i - 1 /* remaining */); } } else { - handleResponse(RemovalConsumer.class, (c) -> c.onRemoved(null, 0), - null); + handleResponse(RemovalConsumer.class, (c) -> c.onRemoved(null /* identifier */, + 0 /* remaining */)); } } @Override public void onAuthenticatorIdRetrieved(long authenticatorId) { handleResponse(FingerprintGetAuthenticatorIdClient.class, - (c) -> c.onAuthenticatorIdRetrieved(authenticatorId), null); + (c) -> c.onAuthenticatorIdRetrieved(authenticatorId)); } @Override public void onAuthenticatorIdInvalidated(long newAuthenticatorId) { handleResponse(FingerprintInvalidationClient.class, (c) -> c.onAuthenticatorIdInvalidated( - newAuthenticatorId), null); + newAuthenticatorId)); + } + + /** + * Handle clients which are not supported in HIDL HAL. + */ + public <T extends BaseClientMonitor> void onUnsupportedClientScheduled(Class<T> className) { + Slog.e(TAG, className + " is not supported in the HAL."); + handleResponse(className, (c) -> c.cancel()); + } + + private <T> void handleResponse(@NonNull Class<T> className, + @NonNull Consumer<T> action) { + handleResponse(className, action, null /* alternateAction */); } private <T> void handleResponse(@NonNull Class<T> className, diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlSession.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlSession.java index 299a310caee9..8ff105baa981 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlSession.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/AidlSession.java @@ -20,7 +20,7 @@ import android.annotation.NonNull; import android.hardware.biometrics.fingerprint.ISession; import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint; -import com.android.server.biometrics.sensors.fingerprint.hidl.AidlToHidlAdapter; +import com.android.server.biometrics.sensors.fingerprint.hidl.HidlToAidlSessionAdapter; import java.util.function.Supplier; @@ -45,7 +45,7 @@ public class AidlSession { public AidlSession(@NonNull Supplier<IBiometricsFingerprint> session, int userId, AidlResponseHandler aidlResponseHandler) { - mSession = new AidlToHidlAdapter(session, userId, aidlResponseHandler); + mSession = new HidlToAidlSessionAdapter(session, userId, aidlResponseHandler); mHalInterfaceVersion = 0; mUserId = userId; mAidlResponseHandler = aidlResponseHandler; @@ -62,7 +62,7 @@ public class AidlSession { } /** The HAL callback, which should only be used in tests {@See BiometricTestSessionImpl}. */ - AidlResponseHandler getHalSessionCallback() { + public AidlResponseHandler getHalSessionCallback() { return mAidlResponseHandler; } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java index ea1a622c36ab..03539690c0a8 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintGetAuthenticatorIdClient.java @@ -30,7 +30,7 @@ import com.android.server.biometrics.sensors.HalClientMonitor; import java.util.Map; import java.util.function.Supplier; -class FingerprintGetAuthenticatorIdClient extends HalClientMonitor<AidlSession> { +public class FingerprintGetAuthenticatorIdClient extends HalClientMonitor<AidlSession> { private static final String TAG = "FingerprintGetAuthenticatorIdClient"; diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java index 032ab87196f8..88a11d9c0ceb 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java @@ -59,6 +59,7 @@ import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.VisibleForTesting; import com.android.server.biometrics.AuthenticationStatsBroadcastReceiver; import com.android.server.biometrics.AuthenticationStatsCollector; +import com.android.server.biometrics.Flags; import com.android.server.biometrics.Utils; import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.log.BiometricLogger; @@ -73,6 +74,7 @@ import com.android.server.biometrics.sensors.ClientMonitorCallbackConverter; import com.android.server.biometrics.sensors.ClientMonitorCompositeCallback; import com.android.server.biometrics.sensors.InvalidationRequesterClient; import com.android.server.biometrics.sensors.LockoutResetDispatcher; +import com.android.server.biometrics.sensors.LockoutTracker; import com.android.server.biometrics.sensors.PerformanceTracker; import com.android.server.biometrics.sensors.SensorList; import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils; @@ -80,6 +82,7 @@ import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDisp import com.android.server.biometrics.sensors.fingerprint.PowerPressHandler; import com.android.server.biometrics.sensors.fingerprint.ServiceProvider; import com.android.server.biometrics.sensors.fingerprint.Udfps; +import com.android.server.biometrics.sensors.fingerprint.hidl.HidlToAidlSensorAdapter; import org.json.JSONArray; import org.json.JSONException; @@ -165,10 +168,12 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @NonNull SensorProps[] props, @NonNull String halInstanceName, @NonNull LockoutResetDispatcher lockoutResetDispatcher, @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher, - @NonNull BiometricContext biometricContext) { + @NonNull BiometricContext biometricContext, + boolean resetLockoutRequiresHardwareAuthToken) { this(context, biometricStateCallback, authenticationStateListeners, props, halInstanceName, lockoutResetDispatcher, gestureAvailabilityDispatcher, biometricContext, - null /* daemon */); + null /* daemon */, resetLockoutRequiresHardwareAuthToken, + false /* testHalEnabled */); } @VisibleForTesting FingerprintProvider(@NonNull Context context, @@ -178,7 +183,9 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @NonNull LockoutResetDispatcher lockoutResetDispatcher, @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher, @NonNull BiometricContext biometricContext, - IFingerprint daemon) { + @Nullable IFingerprint daemon, + boolean resetLockoutRequiresHardwareAuthToken, + boolean testHalEnabled) { mContext = context; mBiometricStateCallback = biometricStateCallback; mAuthenticationStateListeners = authenticationStateListeners; @@ -191,62 +198,136 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mBiometricContext = biometricContext; mAuthSessionCoordinator = mBiometricContext.getAuthSessionCoordinator(); mDaemon = daemon; + mTestHalEnabled = testHalEnabled; - AuthenticationStatsBroadcastReceiver mBroadcastReceiver = - new AuthenticationStatsBroadcastReceiver( - mContext, - BiometricsProtoEnums.MODALITY_FINGERPRINT, - (AuthenticationStatsCollector collector) -> { - Slog.d(getTag(), "Initializing AuthenticationStatsCollector"); - mAuthenticationStatsCollector = collector; - }); - - final List<SensorLocationInternal> workaroundLocations = getWorkaroundSensorProps(context); + initAuthenticationBroadcastReceiver(); + initSensors(resetLockoutRequiresHardwareAuthToken, props, gestureAvailabilityDispatcher); + } - for (SensorProps prop : props) { - final int sensorId = prop.commonProps.sensorId; + private void initAuthenticationBroadcastReceiver() { + new AuthenticationStatsBroadcastReceiver( + mContext, + BiometricsProtoEnums.MODALITY_FINGERPRINT, + (AuthenticationStatsCollector collector) -> { + Slog.d(getTag(), "Initializing AuthenticationStatsCollector"); + mAuthenticationStatsCollector = collector; + }); + } - final List<ComponentInfoInternal> componentInfo = new ArrayList<>(); - if (prop.commonProps.componentInfo != null) { - for (ComponentInfo info : prop.commonProps.componentInfo) { - componentInfo.add(new ComponentInfoInternal(info.componentId, - info.hardwareVersion, info.firmwareVersion, info.serialNumber, - info.softwareVersion)); + private void initSensors(boolean resetLockoutRequiresHardwareAuthToken, SensorProps[] props, + GestureAvailabilityDispatcher gestureAvailabilityDispatcher) { + if (Flags.deHidl()) { + if (!resetLockoutRequiresHardwareAuthToken) { + Slog.d(getTag(), "Adding HIDL configs"); + for (SensorProps sensorConfig: props) { + addHidlSensors(sensorConfig, gestureAvailabilityDispatcher, + resetLockoutRequiresHardwareAuthToken); + } + } else { + Slog.d(getTag(), "Adding AIDL configs"); + final List<SensorLocationInternal> workaroundLocations = + getWorkaroundSensorProps(mContext); + for (SensorProps prop : props) { + addAidlSensors(prop, gestureAvailabilityDispatcher, workaroundLocations, + resetLockoutRequiresHardwareAuthToken); } } - - final FingerprintSensorPropertiesInternal internalProp = - new FingerprintSensorPropertiesInternal(prop.commonProps.sensorId, - prop.commonProps.sensorStrength, - prop.commonProps.maxEnrollmentsPerUser, - componentInfo, - prop.sensorType, - prop.halControlsIllumination, - true /* resetLockoutRequiresHardwareAuthToken */, - !workaroundLocations.isEmpty() ? workaroundLocations : - Arrays.stream(prop.sensorLocations).map(location -> - new SensorLocationInternal( - location.display, - location.sensorLocationX, - location.sensorLocationY, - location.sensorRadius)) - .collect(Collectors.toList())); - final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, mContext, mHandler, - internalProp, lockoutResetDispatcher, gestureAvailabilityDispatcher, - mBiometricContext); - final int sessionUserId = sensor.getLazySession().get() == null ? UserHandle.USER_NULL : - sensor.getLazySession().get().getUserId(); - mFingerprintSensors.addSensor(sensorId, sensor, sessionUserId, - new SynchronousUserSwitchObserver() { - @Override - public void onUserSwitching(int newUserId) { - scheduleInternalCleanup(sensorId, newUserId, null /* callback */); - } - }); - Slog.d(getTag(), "Added: " + internalProp); + } else { + final List<SensorLocationInternal> workaroundLocations = + getWorkaroundSensorProps(mContext); + + for (SensorProps prop : props) { + final int sensorId = prop.commonProps.sensorId; + final List<ComponentInfoInternal> componentInfo = new ArrayList<>(); + if (prop.commonProps.componentInfo != null) { + for (ComponentInfo info : prop.commonProps.componentInfo) { + componentInfo.add(new ComponentInfoInternal(info.componentId, + info.hardwareVersion, info.firmwareVersion, info.serialNumber, + info.softwareVersion)); + } + } + final FingerprintSensorPropertiesInternal internalProp = + new FingerprintSensorPropertiesInternal(prop.commonProps.sensorId, + prop.commonProps.sensorStrength, + prop.commonProps.maxEnrollmentsPerUser, + componentInfo, + prop.sensorType, + prop.halControlsIllumination, + true /* resetLockoutRequiresHardwareAuthToken */, + !workaroundLocations.isEmpty() ? workaroundLocations : + Arrays.stream(prop.sensorLocations).map( + location -> new SensorLocationInternal( + location.display, + location.sensorLocationX, + location.sensorLocationY, + location.sensorRadius)) + .collect(Collectors.toList())); + final Sensor sensor = new Sensor(getTag() + "/" + sensorId, this, mContext, + mHandler, internalProp, mLockoutResetDispatcher, + gestureAvailabilityDispatcher, mBiometricContext); + sensor.init(gestureAvailabilityDispatcher, + mLockoutResetDispatcher); + final int sessionUserId = + sensor.getLazySession().get() == null ? UserHandle.USER_NULL : + sensor.getLazySession().get().getUserId(); + mFingerprintSensors.addSensor(sensorId, sensor, sessionUserId, + new SynchronousUserSwitchObserver() { + @Override + public void onUserSwitching(int newUserId) { + scheduleInternalCleanup(sensorId, newUserId, null /* callback */); + } + }); + Slog.d(getTag(), "Added: " + mFingerprintSensors.get(sensorId).toString()); + } } } + private void addHidlSensors(@NonNull SensorProps prop, + @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher, + boolean resetLockoutRequiresHardwareAuthToken) { + final int sensorId = prop.commonProps.sensorId; + final Sensor sensor = new HidlToAidlSensorAdapter(getTag() + "/" + + sensorId, this, mContext, mHandler, + prop, mLockoutResetDispatcher, gestureAvailabilityDispatcher, + mBiometricContext, resetLockoutRequiresHardwareAuthToken, + () -> scheduleInternalCleanup(sensorId, ActivityManager.getCurrentUser(), + null /* callback */)); + sensor.init(gestureAvailabilityDispatcher, mLockoutResetDispatcher); + final int sessionUserId = sensor.getLazySession().get() == null ? UserHandle.USER_NULL : + sensor.getLazySession().get().getUserId(); + mFingerprintSensors.addSensor(sensorId, sensor, sessionUserId, + new SynchronousUserSwitchObserver() { + @Override + public void onUserSwitching(int newUserId) { + scheduleInternalCleanup(sensorId, newUserId, null /* callback */); + } + }); + Slog.d(getTag(), "Added: " + mFingerprintSensors.get(sensorId).toString()); + } + + private void addAidlSensors(@NonNull SensorProps prop, + @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher, + List<SensorLocationInternal> workaroundLocations, + boolean resetLockoutRequiresHardwareAuthToken) { + final int sensorId = prop.commonProps.sensorId; + final Sensor sensor = new Sensor(getTag() + "/" + sensorId, + this, mContext, mHandler, + prop, mLockoutResetDispatcher, gestureAvailabilityDispatcher, + mBiometricContext, workaroundLocations, + resetLockoutRequiresHardwareAuthToken); + sensor.init(gestureAvailabilityDispatcher, mLockoutResetDispatcher); + final int sessionUserId = sensor.getLazySession().get() == null ? UserHandle.USER_NULL : + sensor.getLazySession().get().getUserId(); + mFingerprintSensors.addSensor(sensorId, sensor, sessionUserId, + new SynchronousUserSwitchObserver() { + @Override + public void onUserSwitching(int newUserId) { + scheduleInternalCleanup(sensorId, newUserId, null /* callback */); + } + }); + Slog.d(getTag(), "Added: " + mFingerprintSensors.get(sensorId).toString()); + } + private String getTag() { return "FingerprintProvider/" + mHalInstanceName; } @@ -351,7 +432,10 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi } } - private void scheduleLoadAuthenticatorIdsForUser(int sensorId, int userId) { + /** + * Schedules FingerprintGetAuthenticatorIdClient for specific sensor and user. + */ + protected void scheduleLoadAuthenticatorIdsForUser(int sensorId, int userId) { mHandler.post(() -> { final FingerprintGetAuthenticatorIdClient client = new FingerprintGetAuthenticatorIdClient(mContext, @@ -387,8 +471,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi BiometricsProtoEnums.CLIENT_UNKNOWN, mAuthenticationStatsCollector), mBiometricContext, hardwareAuthToken, - mFingerprintSensors.get(sensorId).getLockoutCache(), mLockoutResetDispatcher, - Utils.getCurrentStrength(sensorId)); + mFingerprintSensors.get(sensorId).getLockoutTracker(false /* forAuth */), + mLockoutResetDispatcher, Utils.getCurrentStrength(sensorId)); scheduleForSensor(sensorId, client); }); } @@ -443,18 +527,23 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mFingerprintSensors.get(sensorId).getSensorProperties(), mUdfpsOverlayController, mSidefpsController, mAuthenticationStateListeners, maxTemplatesPerUser, enrollReason); - scheduleForSensor(sensorId, client, new ClientMonitorCompositeCallback( - mBiometricStateCallback, new ClientMonitorCallback() { - @Override - public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, - boolean success) { - ClientMonitorCallback.super.onClientFinished(clientMonitor, success); - if (success) { - scheduleLoadAuthenticatorIdsForUser(sensorId, userId); - scheduleInvalidationRequest(sensorId, userId); - } - } - })); + if (Flags.deHidl()) { + scheduleForSensor(sensorId, client, mBiometricStateCallback); + } else { + scheduleForSensor(sensorId, client, new ClientMonitorCompositeCallback( + mBiometricStateCallback, new ClientMonitorCallback() { + @Override + public void onClientFinished(@NonNull BaseClientMonitor clientMonitor, + boolean success) { + ClientMonitorCallback.super.onClientFinished( + clientMonitor, success); + if (success) { + scheduleLoadAuthenticatorIdsForUser(sensorId, userId); + scheduleInvalidationRequest(sensorId, userId); + } + } + })); + } }); return id; } @@ -497,6 +586,13 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi final int userId = options.getUserId(); final int sensorId = options.getSensorId(); final boolean isStrongBiometric = Utils.isStrongBiometric(sensorId); + final LockoutTracker lockoutTracker; + if (Flags.deHidl()) { + lockoutTracker = mFingerprintSensors.get(sensorId) + .getLockoutTracker(true /* forAuth */); + } else { + lockoutTracker = null; + } final FingerprintAuthenticationClient client = new FingerprintAuthenticationClient( mContext, mFingerprintSensors.get(sensorId).getLazySession(), token, requestId, callback, operationId, restricted, options, cookie, @@ -510,7 +606,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mFingerprintSensors.get(sensorId).getSensorProperties(), mHandler, Utils.getCurrentStrength(sensorId), SystemClock.elapsedRealtimeClock(), - null /* lockoutTracker */); + lockoutTracker); scheduleForSensor(sensorId, client, new ClientMonitorCallback() { @Override @@ -636,6 +732,9 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @Override public boolean isHardwareDetected(int sensorId) { + if (Flags.deHidl()) { + return mFingerprintSensors.get(sensorId).isHardwareDetected(mHalInstanceName); + } return hasHalInstance(); } @@ -674,8 +773,12 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi @Override public int getLockoutModeForUser(int sensorId, int userId) { - return mBiometricContext.getAuthSessionCoordinator().getLockoutStateFor(userId, - Utils.getCurrentStrength(sensorId)); + if (Flags.deHidl()) { + return mFingerprintSensors.get(sensorId).getLockoutModeForUser(userId); + } else { + return mBiometricContext.getAuthSessionCoordinator().getLockoutStateFor(userId, + Utils.getCurrentStrength(sensorId)); + } } @Override @@ -829,6 +932,10 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi mTestHalEnabled = enabled; } + public boolean getTestHalEnabled() { + return mTestHalEnabled; + } + // TODO(b/174868353): workaround for gaps in HAL interface (remove and get directly from HAL) // reads values via an overlay instead of querying the HAL @NonNull diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java index ec225a60d54b..387ae12147ae 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintResetLockoutClient.java @@ -60,7 +60,8 @@ public class FingerprintResetLockoutClient extends HalClientMonitor<AidlSession> @Authenticators.Types int biometricStrength) { super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner, 0 /* cookie */, sensorId, biometricLogger, biometricContext); - mHardwareAuthToken = HardwareAuthTokenUtils.toHardwareAuthToken(hardwareAuthToken); + mHardwareAuthToken = hardwareAuthToken == null ? null : + HardwareAuthTokenUtils.toHardwareAuthToken(hardwareAuthToken); mLockoutCache = lockoutTracker; mLockoutResetDispatcher = lockoutResetDispatcher; mBiometricStrength = biometricStrength; diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java index 893cb8f9b4fc..dd887bb05c12 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/Sensor.java @@ -21,21 +21,28 @@ import android.annotation.Nullable; import android.content.Context; import android.content.pm.UserInfo; import android.hardware.biometrics.BiometricsProtoEnums; +import android.hardware.biometrics.ComponentInfoInternal; import android.hardware.biometrics.ITestSession; import android.hardware.biometrics.ITestSessionCallback; +import android.hardware.biometrics.SensorLocationInternal; +import android.hardware.biometrics.common.ComponentInfo; +import android.hardware.biometrics.fingerprint.IFingerprint; import android.hardware.biometrics.fingerprint.ISession; +import android.hardware.biometrics.fingerprint.SensorProps; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; +import android.os.ServiceManager; import android.os.UserHandle; import android.os.UserManager; import android.util.Slog; import android.util.proto.ProtoOutputStream; import com.android.internal.util.FrameworkStatsLog; +import com.android.server.biometrics.Flags; import com.android.server.biometrics.SensorServiceStateProto; import com.android.server.biometrics.SensorStateProto; import com.android.server.biometrics.UserStateProto; @@ -48,15 +55,20 @@ import com.android.server.biometrics.sensors.BiometricStateCallback; import com.android.server.biometrics.sensors.ErrorConsumer; import com.android.server.biometrics.sensors.LockoutCache; import com.android.server.biometrics.sensors.LockoutResetDispatcher; +import com.android.server.biometrics.sensors.LockoutTracker; import com.android.server.biometrics.sensors.StartUserClient; import com.android.server.biometrics.sensors.StopUserClient; import com.android.server.biometrics.sensors.UserAwareBiometricScheduler; import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils; import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher; +import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.function.Supplier; +import java.util.stream.Collectors; /** * Maintains the state of a single sensor within an instance of the @@ -73,15 +85,17 @@ public class Sensor { @NonNull private final IBinder mToken; @NonNull private final Handler mHandler; @NonNull private final FingerprintSensorPropertiesInternal mSensorProperties; - @NonNull private final UserAwareBiometricScheduler mScheduler; - @NonNull private final LockoutCache mLockoutCache; + @NonNull private BiometricScheduler mScheduler; + @NonNull private LockoutTracker mLockoutTracker; @NonNull private final Map<Integer, Long> mAuthenticatorIds; + @NonNull private final BiometricContext mBiometricContext; @Nullable AidlSession mCurrentSession; - @NonNull private final Supplier<AidlSession> mLazySession; + @NonNull private Supplier<AidlSession> mLazySession; - Sensor(@NonNull String tag, @NonNull FingerprintProvider provider, @NonNull Context context, - @NonNull Handler handler, @NonNull FingerprintSensorPropertiesInternal sensorProperties, + public Sensor(@NonNull String tag, @NonNull FingerprintProvider provider, + @NonNull Context context, @NonNull Handler handler, + @NonNull FingerprintSensorPropertiesInternal sensorProperties, @NonNull LockoutResetDispatcher lockoutResetDispatcher, @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher, @NonNull BiometricContext biometricContext, AidlSession session) { @@ -91,8 +105,38 @@ public class Sensor { mToken = new Binder(); mHandler = handler; mSensorProperties = sensorProperties; - mLockoutCache = new LockoutCache(); - mScheduler = new UserAwareBiometricScheduler(tag, + mBiometricContext = biometricContext; + mAuthenticatorIds = new HashMap<>(); + mCurrentSession = session; + } + + Sensor(@NonNull String tag, @NonNull FingerprintProvider provider, @NonNull Context context, + @NonNull Handler handler, @NonNull FingerprintSensorPropertiesInternal sensorProperties, + @NonNull LockoutResetDispatcher lockoutResetDispatcher, + @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher, + @NonNull BiometricContext biometricContext) { + this(tag, provider, context, handler, sensorProperties, lockoutResetDispatcher, + gestureAvailabilityDispatcher, biometricContext, null); + } + + Sensor(@NonNull String tag, @NonNull FingerprintProvider provider, @NonNull Context context, + @NonNull Handler handler, @NonNull SensorProps sensorProp, + @NonNull LockoutResetDispatcher lockoutResetDispatcher, + @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher, + @NonNull BiometricContext biometricContext, + @NonNull List<SensorLocationInternal> workaroundLocation, + boolean resetLockoutRequiresHardwareAuthToken) { + this(tag, provider, context, handler, getFingerprintSensorPropertiesInternal(sensorProp, + workaroundLocation, resetLockoutRequiresHardwareAuthToken), + lockoutResetDispatcher, gestureAvailabilityDispatcher, biometricContext, null); + } + + /** + * Initialize biometric scheduler, lockout tracker and session for the sensor. + */ + public void init(GestureAvailabilityDispatcher gestureAvailabilityDispatcher, + LockoutResetDispatcher lockoutResetDispatcher) { + mScheduler = new UserAwareBiometricScheduler(mTag, BiometricScheduler.sensorTypeFromFingerprintProperties(mSensorProperties), gestureAvailabilityDispatcher, () -> mCurrentSession != null ? mCurrentSession.getUserId() : UserHandle.USER_NULL, @@ -102,7 +146,7 @@ public class Sensor { public StopUserClient<?> getStopUserClient(int userId) { return new FingerprintStopUserClient(mContext, mLazySession, mToken, userId, mSensorProperties.sensorId, - BiometricLogger.ofUnknown(mContext), biometricContext, + BiometricLogger.ofUnknown(mContext), mBiometricContext, () -> mCurrentSession = null); } @@ -111,13 +155,38 @@ public class Sensor { public StartUserClient<?, ?> getStartUserClient(int newUserId) { final int sensorId = mSensorProperties.sensorId; - final AidlResponseHandler resultController = new AidlResponseHandler( - mContext, mScheduler, sensorId, newUserId, - mLockoutCache, lockoutResetDispatcher, - biometricContext.getAuthSessionCoordinator(), () -> { - Slog.e(mTag, "Got ERROR_HW_UNAVAILABLE"); - mCurrentSession = null; - }); + final AidlResponseHandler resultController; + + if (Flags.deHidl()) { + resultController = new AidlResponseHandler( + mContext, mScheduler, sensorId, newUserId, + mLockoutTracker, lockoutResetDispatcher, + mBiometricContext.getAuthSessionCoordinator(), () -> {}, + new AidlResponseHandler.AidlResponseHandlerCallback() { + @Override + public void onEnrollSuccess() { + mProvider.scheduleLoadAuthenticatorIdsForUser(sensorId, + newUserId); + mProvider.scheduleInvalidationRequest(sensorId, + newUserId); + } + + @Override + public void onHardwareUnavailable() { + Slog.e(mTag, + "Fingerprint sensor hardware unavailable."); + mCurrentSession = null; + } + }); + } else { + resultController = new AidlResponseHandler( + mContext, mScheduler, sensorId, newUserId, + mLockoutTracker, lockoutResetDispatcher, + mBiometricContext.getAuthSessionCoordinator(), () -> { + Slog.e(mTag, "Got ERROR_HW_UNAVAILABLE"); + mCurrentSession = null; + }); + } final StartUserClient.UserStartedCallback<ISession> userStartedCallback = (userIdStarted, newSession, halInterfaceVersion) -> { @@ -133,40 +202,58 @@ public class Sensor { + "sensor: " + sensorId + ", user: " + userIdStarted); - provider.scheduleInvalidationRequest(sensorId, + mProvider.scheduleInvalidationRequest(sensorId, userIdStarted); } }; - return new FingerprintStartUserClient(mContext, provider::getHalInstance, + return new FingerprintStartUserClient(mContext, mProvider::getHalInstance, mToken, newUserId, mSensorProperties.sensorId, - BiometricLogger.ofUnknown(mContext), biometricContext, + BiometricLogger.ofUnknown(mContext), mBiometricContext, resultController, userStartedCallback); } }); - mAuthenticatorIds = new HashMap<>(); + mLockoutTracker = new LockoutCache(); mLazySession = () -> mCurrentSession != null ? mCurrentSession : null; - mCurrentSession = session; } - Sensor(@NonNull String tag, @NonNull FingerprintProvider provider, @NonNull Context context, - @NonNull Handler handler, @NonNull FingerprintSensorPropertiesInternal sensorProperties, - @NonNull LockoutResetDispatcher lockoutResetDispatcher, - @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher, - @NonNull BiometricContext biometricContext) { - this(tag, provider, context, handler, sensorProperties, lockoutResetDispatcher, - gestureAvailabilityDispatcher, biometricContext, null); + protected static FingerprintSensorPropertiesInternal getFingerprintSensorPropertiesInternal( + SensorProps prop, List<SensorLocationInternal> workaroundLocations, + boolean resetLockoutRequiresHardwareAuthToken) { + final List<ComponentInfoInternal> componentInfo = new ArrayList<>(); + if (prop.commonProps.componentInfo != null) { + for (ComponentInfo info : prop.commonProps.componentInfo) { + componentInfo.add(new ComponentInfoInternal(info.componentId, + info.hardwareVersion, info.firmwareVersion, info.serialNumber, + info.softwareVersion)); + } + } + return new FingerprintSensorPropertiesInternal(prop.commonProps.sensorId, + prop.commonProps.sensorStrength, + prop.commonProps.maxEnrollmentsPerUser, + componentInfo, + prop.sensorType, + prop.halControlsIllumination, + resetLockoutRequiresHardwareAuthToken, + !workaroundLocations.isEmpty() ? workaroundLocations : + Arrays.stream(prop.sensorLocations).map(location -> + new SensorLocationInternal( + location.display, + location.sensorLocationX, + location.sensorLocationY, + location.sensorRadius)) + .collect(Collectors.toList())); } - @NonNull Supplier<AidlSession> getLazySession() { + @NonNull public Supplier<AidlSession> getLazySession() { return mLazySession; } - @NonNull FingerprintSensorPropertiesInternal getSensorProperties() { + @NonNull public FingerprintSensorPropertiesInternal getSensorProperties() { return mSensorProperties; } - @Nullable AidlSession getSessionForUser(int userId) { + @Nullable protected AidlSession getSessionForUser(int userId) { if (mCurrentSession != null && mCurrentSession.getUserId() == userId) { return mCurrentSession; } else { @@ -180,15 +267,18 @@ public class Sensor { biometricStateCallback, mProvider, this); } - @NonNull BiometricScheduler getScheduler() { + @NonNull public BiometricScheduler getScheduler() { return mScheduler; } - @NonNull LockoutCache getLockoutCache() { - return mLockoutCache; + @NonNull protected LockoutTracker getLockoutTracker(boolean forAuth) { + if (forAuth) { + return null; + } + return mLockoutTracker; } - @NonNull Map<Integer, Long> getAuthenticatorIds() { + @NonNull public Map<Integer, Long> getAuthenticatorIds() { return mAuthenticatorIds; } @@ -262,4 +352,49 @@ public class Sensor { mScheduler.reset(); mCurrentSession = null; } + + @NonNull protected Handler getHandler() { + return mHandler; + } + + @NonNull protected Context getContext() { + return mContext; + } + + /** + * Returns true if the sensor hardware is detected. + */ + protected boolean isHardwareDetected(String halInstance) { + if (mTestHalEnabled) { + return true; + } + return (ServiceManager.checkService(IFingerprint.DESCRIPTOR + "/" + halInstance) + != null); + } + + @NonNull protected BiometricContext getBiometricContext() { + return mBiometricContext; + } + + /** + * Returns lockout mode of this sensor. + */ + @LockoutTracker.LockoutMode + public int getLockoutModeForUser(int userId) { + return mBiometricContext.getAuthSessionCoordinator().getLockoutStateFor(userId, + Utils.getCurrentStrength(mSensorProperties.sensorId)); + } + + public void setScheduler(BiometricScheduler scheduler) { + mScheduler = scheduler; + } + + public void setLazySession( + Supplier<AidlSession> lazySession) { + mLazySession = lazySession; + } + + public FingerprintProvider getProvider() { + return mProvider; + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java index a4e602553101..5c5b9928f57a 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintUpdateActiveUserClient.java @@ -29,7 +29,8 @@ import com.android.server.biometrics.BiometricsProto; import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.log.BiometricLogger; import com.android.server.biometrics.sensors.ClientMonitorCallback; -import com.android.server.biometrics.sensors.HalClientMonitor; +import com.android.server.biometrics.sensors.StartUserClient; +import com.android.server.biometrics.sensors.fingerprint.aidl.AidlSession; import java.io.File; import java.util.Map; @@ -38,7 +39,8 @@ import java.util.function.Supplier; /** * Sets the HAL's current active user, and updates the framework's authenticatorId cache. */ -public class FingerprintUpdateActiveUserClient extends HalClientMonitor<IBiometricsFingerprint> { +public class FingerprintUpdateActiveUserClient extends + StartUserClient<IBiometricsFingerprint, AidlSession> { private static final String TAG = "FingerprintUpdateActiveUserClient"; private static final String FP_DATA_DIR = "fpdata"; @@ -53,11 +55,24 @@ public class FingerprintUpdateActiveUserClient extends HalClientMonitor<IBiometr @NonNull Supplier<IBiometricsFingerprint> lazyDaemon, int userId, @NonNull String owner, int sensorId, @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, - Supplier<Integer> currentUserId, + @NonNull Supplier<Integer> currentUserId, boolean hasEnrolledBiometrics, @NonNull Map<Integer, Long> authenticatorIds, boolean forceUpdateAuthenticatorId) { - super(context, lazyDaemon, null /* token */, null /* listener */, userId, owner, - 0 /* cookie */, sensorId, logger, biometricContext); + this(context, lazyDaemon, userId, owner, sensorId, logger, biometricContext, currentUserId, + hasEnrolledBiometrics, authenticatorIds, forceUpdateAuthenticatorId, + (newUserId, newUser, halInterfaceVersion) -> {}); + } + + FingerprintUpdateActiveUserClient(@NonNull Context context, + @NonNull Supplier<IBiometricsFingerprint> lazyDaemon, int userId, + @NonNull String owner, int sensorId, + @NonNull BiometricLogger logger, @NonNull BiometricContext biometricContext, + @NonNull Supplier<Integer> currentUserId, + boolean hasEnrolledBiometrics, @NonNull Map<Integer, Long> authenticatorIds, + boolean forceUpdateAuthenticatorId, + @NonNull UserStartedCallback<AidlSession> userStartedCallback) { + super(context, lazyDaemon, null /* token */, userId, sensorId, logger, biometricContext, + userStartedCallback); mCurrentUserId = currentUserId; mForceUpdateAuthenticatorId = forceUpdateAuthenticatorId; mHasEnrolledBiometrics = hasEnrolledBiometrics; @@ -70,6 +85,7 @@ public class FingerprintUpdateActiveUserClient extends HalClientMonitor<IBiometr if (mCurrentUserId.get() == getTargetUserId() && !mForceUpdateAuthenticatorId) { Slog.d(TAG, "Already user: " + mCurrentUserId + ", returning"); + mUserStartedCallback.onUserStarted(getTargetUserId(), null, 0); callback.onClientFinished(this, true /* success */); return; } @@ -119,6 +135,7 @@ public class FingerprintUpdateActiveUserClient extends HalClientMonitor<IBiometr getFreshDaemon().setActiveGroup(targetId, mDirectory.getAbsolutePath()); mAuthenticatorIds.put(targetId, mHasEnrolledBiometrics ? getFreshDaemon().getAuthenticatorId() : 0L); + mUserStartedCallback.onUserStarted(targetId, null, 0); mCallback.onClientFinished(this, true /* success */); } catch (RemoteException e) { Slog.e(TAG, "Failed to setActiveGroup: " + e); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlCallbackConverter.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlCallbackConverter.java index c3e5cbe7daf4..e9a48e79d245 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlCallbackConverter.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlCallbackConverter.java @@ -20,6 +20,7 @@ import android.annotation.NonNull; import android.hardware.biometrics.fingerprint.V2_2.IBiometricsFingerprintClientCallback; import com.android.server.biometrics.HardwareAuthTokenUtils; +import com.android.server.biometrics.sensors.BaseClientMonitor; import com.android.server.biometrics.sensors.fingerprint.aidl.AidlResponseHandler; import java.util.ArrayList; @@ -73,12 +74,12 @@ public class HidlToAidlCallbackConverter extends IBiometricsFingerprintClientCal @Override public void onRemoved(long deviceId, int fingerId, int groupId, int remaining) { - mAidlResponseHandler.onEnrollmentsRemoved(new int[]{fingerId}); + mAidlResponseHandler.onEnrollmentRemoved(fingerId, remaining); } @Override public void onEnumerate(long deviceId, int fingerId, int groupId, int remaining) { - mAidlResponseHandler.onEnrollmentsEnumerated(new int[]{fingerId}); + mAidlResponseHandler.onEnrollmentEnumerated(fingerId, remaining); } void onChallengeGenerated(long challenge) { @@ -92,4 +93,8 @@ public class HidlToAidlCallbackConverter extends IBiometricsFingerprintClientCal void onResetLockout() { mAidlResponseHandler.onLockoutCleared(); } + + <T extends BaseClientMonitor> void unsupportedClientScheduled(Class<T> className) { + mAidlResponseHandler.onUnsupportedClientScheduled(className); + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapter.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapter.java new file mode 100644 index 000000000000..0bb6141583d5 --- /dev/null +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapter.java @@ -0,0 +1,297 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors.fingerprint.hidl; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.content.pm.UserInfo; +import android.hardware.biometrics.fingerprint.SensorProps; +import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint; +import android.os.Handler; +import android.os.IHwBinder; +import android.os.RemoteException; +import android.os.UserHandle; +import android.os.UserManager; +import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; +import com.android.server.biometrics.sensors.AuthSessionCoordinator; +import com.android.server.biometrics.sensors.BiometricScheduler; +import com.android.server.biometrics.sensors.ClientMonitorCallback; +import com.android.server.biometrics.sensors.LockoutResetDispatcher; +import com.android.server.biometrics.sensors.LockoutTracker; +import com.android.server.biometrics.sensors.StartUserClient; +import com.android.server.biometrics.sensors.StopUserClient; +import com.android.server.biometrics.sensors.UserAwareBiometricScheduler; +import com.android.server.biometrics.sensors.fingerprint.FingerprintUtils; +import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher; +import com.android.server.biometrics.sensors.fingerprint.aidl.AidlResponseHandler; +import com.android.server.biometrics.sensors.fingerprint.aidl.AidlSession; +import com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintProvider; +import com.android.server.biometrics.sensors.fingerprint.aidl.Sensor; + +import java.util.ArrayList; + +/** + * Convert HIDL sensor configurations to an AIDL Sensor. + */ +public class HidlToAidlSensorAdapter extends Sensor implements IHwBinder.DeathRecipient { + private static final String TAG = "HidlToAidlSensorAdapter"; + + private final Runnable mInternalCleanupRunnable; + private final LockoutResetDispatcher mLockoutResetDispatcher; + private LockoutFrameworkImpl mLockoutTracker; + private final AuthSessionCoordinator mAuthSessionCoordinator; + private final AidlResponseHandler.AidlResponseHandlerCallback mAidlResponseHandlerCallback; + private int mCurrentUserId = UserHandle.USER_NULL; + private IBiometricsFingerprint mDaemon; + private AidlSession mSession; + + private final StartUserClient.UserStartedCallback<AidlSession> mUserStartedCallback = + (newUserId, newUser, halInterfaceVersion) -> { + if (mCurrentUserId != newUserId) { + handleUserChanged(newUserId); + } + }; + + public HidlToAidlSensorAdapter(@NonNull String tag, @NonNull FingerprintProvider provider, + @NonNull Context context, @NonNull Handler handler, + @NonNull SensorProps prop, + @NonNull LockoutResetDispatcher lockoutResetDispatcher, + @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher, + @NonNull BiometricContext biometricContext, + boolean resetLockoutRequiresHardwareAuthToken, + @NonNull Runnable internalCleanupRunnable) { + this(tag, provider, context, handler, prop, lockoutResetDispatcher, + gestureAvailabilityDispatcher, biometricContext, + resetLockoutRequiresHardwareAuthToken, internalCleanupRunnable, + new AuthSessionCoordinator(), null /* daemon */, + null /* onEnrollSuccessCallback */); + } + + @VisibleForTesting + HidlToAidlSensorAdapter(@NonNull String tag, @NonNull FingerprintProvider provider, + @NonNull Context context, @NonNull Handler handler, + @NonNull SensorProps prop, + @NonNull LockoutResetDispatcher lockoutResetDispatcher, + @NonNull GestureAvailabilityDispatcher gestureAvailabilityDispatcher, + @NonNull BiometricContext biometricContext, + boolean resetLockoutRequiresHardwareAuthToken, + @NonNull Runnable internalCleanupRunnable, + @NonNull AuthSessionCoordinator authSessionCoordinator, + @Nullable IBiometricsFingerprint daemon, + @Nullable AidlResponseHandler.AidlResponseHandlerCallback aidlResponseHandlerCallback) { + super(tag, provider, context, handler, getFingerprintSensorPropertiesInternal(prop, + new ArrayList<>(), resetLockoutRequiresHardwareAuthToken), + lockoutResetDispatcher, + gestureAvailabilityDispatcher, + biometricContext, null /* session */); + mLockoutResetDispatcher = lockoutResetDispatcher; + mInternalCleanupRunnable = internalCleanupRunnable; + mAuthSessionCoordinator = authSessionCoordinator; + mDaemon = daemon; + mAidlResponseHandlerCallback = aidlResponseHandlerCallback == null + ? new AidlResponseHandler.AidlResponseHandlerCallback() { + @Override + public void onEnrollSuccess() { + getScheduler() + .scheduleClientMonitor(getFingerprintUpdateActiveUserClient( + mCurrentUserId, true /* forceUpdateAuthenticatorIds */)); + } + + @Override + public void onHardwareUnavailable() { + mDaemon = null; + mSession = null; + mCurrentUserId = UserHandle.USER_NULL; + } + } : aidlResponseHandlerCallback; + } + + @Override + public void serviceDied(long cookie) { + Slog.d(TAG, "HAL died."); + mSession = null; + mDaemon = null; + } + + @Override + @LockoutTracker.LockoutMode + public int getLockoutModeForUser(int userId) { + return mLockoutTracker.getLockoutModeForUser(userId); + } + + @Override + public void init(GestureAvailabilityDispatcher gestureAvailabilityDispatcher, + LockoutResetDispatcher lockoutResetDispatcher) { + setLazySession(this::getSession); + setScheduler(new UserAwareBiometricScheduler(TAG, + BiometricScheduler.sensorTypeFromFingerprintProperties(getSensorProperties()), + gestureAvailabilityDispatcher, () -> mCurrentUserId, getUserSwitchCallback())); + mLockoutTracker = new LockoutFrameworkImpl(getContext(), + userId -> mLockoutResetDispatcher.notifyLockoutResetCallbacks( + getSensorProperties().sensorId)); + } + + @Override + @Nullable + protected AidlSession getSessionForUser(int userId) { + if (mSession != null && mSession.getUserId() == userId) { + return mSession; + } else { + return null; + } + } + + @Override + protected boolean isHardwareDetected(String halInstance) { + return getIBiometricsFingerprint() != null; + } + + @NonNull + @Override + protected LockoutTracker getLockoutTracker(boolean forAuth) { + return mLockoutTracker; + } + + private synchronized AidlSession getSession() { + if (mSession != null && mDaemon != null) { + return mSession; + } else { + return mSession = new AidlSession(this::getIBiometricsFingerprint, + mCurrentUserId, getAidlResponseHandler()); + } + } + + private AidlResponseHandler getAidlResponseHandler() { + return new AidlResponseHandler(getContext(), + getScheduler(), + getSensorProperties().sensorId, + mCurrentUserId, + mLockoutTracker, + mLockoutResetDispatcher, + mAuthSessionCoordinator, + () -> {}, mAidlResponseHandlerCallback); + } + + @VisibleForTesting IBiometricsFingerprint getIBiometricsFingerprint() { + if (getProvider().getTestHalEnabled()) { + final TestHal testHal = new TestHal(getContext(), getSensorProperties().sensorId); + testHal.setNotify(new HidlToAidlCallbackConverter(getAidlResponseHandler())); + return testHal; + } + + if (mDaemon != null) { + return mDaemon; + } + + try { + mDaemon = IBiometricsFingerprint.getService(); + } catch (RemoteException e) { + Slog.e(TAG, "Failed to get fingerprint HAL", e); + } catch (java.util.NoSuchElementException e) { + // Service doesn't exist or cannot be opened. + Slog.w(TAG, "NoSuchElementException", e); + } + + if (mDaemon == null) { + Slog.w(TAG, "Fingerprint HAL not available"); + return null; + } + + mDaemon.asBinder().linkToDeath(this, 0 /* flags */); + + Slog.d(TAG, "Fingerprint HAL ready"); + + scheduleLoadAuthenticatorIds(); + mInternalCleanupRunnable.run(); + return mDaemon; + } + + private UserAwareBiometricScheduler.UserSwitchCallback getUserSwitchCallback() { + return new UserAwareBiometricScheduler.UserSwitchCallback() { + @NonNull + @Override + public StopUserClient<?> getStopUserClient(int userId) { + return new StopUserClient<IBiometricsFingerprint>(getContext(), + HidlToAidlSensorAdapter.this::getIBiometricsFingerprint, + null /* token */, userId, getSensorProperties().sensorId, + BiometricLogger.ofUnknown(getContext()), getBiometricContext(), + () -> { + mCurrentUserId = UserHandle.USER_NULL; + mSession = null; + }) { + @Override + public void start(@NonNull ClientMonitorCallback callback) { + super.start(callback); + startHalOperation(); + } + + @Override + protected void startHalOperation() { + onUserStopped(); + } + + @Override + public void unableToStart() { + getCallback().onClientFinished(this, false /* success */); + } + }; + } + + @NonNull + @Override + public StartUserClient<?, ?> getStartUserClient(int newUserId) { + return getFingerprintUpdateActiveUserClient(newUserId, + false /* forceUpdateAuthenticatorId */); + } + }; + } + + private FingerprintUpdateActiveUserClient getFingerprintUpdateActiveUserClient(int newUserId, + boolean forceUpdateAuthenticatorIds) { + return new FingerprintUpdateActiveUserClient(getContext(), + this::getIBiometricsFingerprint, newUserId, TAG, + getSensorProperties().sensorId, BiometricLogger.ofUnknown(getContext()), + getBiometricContext(), () -> mCurrentUserId, + !FingerprintUtils.getInstance(getSensorProperties().sensorId) + .getBiometricsForUser(getContext(), + newUserId).isEmpty(), getAuthenticatorIds(), forceUpdateAuthenticatorIds, + mUserStartedCallback); + } + + private void scheduleLoadAuthenticatorIds() { + getHandler().post(() -> { + for (UserInfo user : UserManager.get(getContext()).getAliveUsers()) { + final int targetUserId = user.id; + if (!getAuthenticatorIds().containsKey(targetUserId)) { + getScheduler().scheduleClientMonitor(getFingerprintUpdateActiveUserClient( + targetUserId, true /* forceUpdateAuthenticatorIds */)); + } + } + }); + } + + @VisibleForTesting void handleUserChanged(int newUserId) { + Slog.d(TAG, "User changed. Current user is " + newUserId); + mSession = null; + mCurrentUserId = newUserId; + } +} diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/AidlToHidlAdapter.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSessionAdapter.java index b48d232e65af..2fc00e126354 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/AidlToHidlAdapter.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSessionAdapter.java @@ -25,20 +25,25 @@ import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint; import android.hardware.keymaster.HardwareAuthToken; import android.os.IBinder; import android.os.RemoteException; +import android.util.Log; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.server.biometrics.HardwareAuthTokenUtils; import com.android.server.biometrics.sensors.fingerprint.UdfpsHelper; import com.android.server.biometrics.sensors.fingerprint.aidl.AidlResponseHandler; +import com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintGetAuthenticatorIdClient; +import com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintInvalidationClient; import java.util.function.Supplier; /** - * Adapter to convert AIDL-specific interface {@link ISession} methods to HIDL implementation. + * Adapter to convert HIDL methods into AIDL interface {@link ISession}. */ -public class AidlToHidlAdapter implements ISession { - private final String TAG = "AidlToHidlAdapter"; +public class HidlToAidlSessionAdapter implements ISession { + + private final String TAG = "HidlToAidlSessionAdapter"; + @VisibleForTesting static final int ENROLL_TIMEOUT_SEC = 60; @NonNull @@ -46,22 +51,13 @@ public class AidlToHidlAdapter implements ISession { private final int mUserId; private HidlToAidlCallbackConverter mHidlToAidlCallbackConverter; - public AidlToHidlAdapter(Supplier<IBiometricsFingerprint> session, int userId, + public HidlToAidlSessionAdapter(Supplier<IBiometricsFingerprint> session, int userId, AidlResponseHandler aidlResponseHandler) { mSession = session; mUserId = userId; setCallback(aidlResponseHandler); } - private void setCallback(AidlResponseHandler aidlResponseHandler) { - mHidlToAidlCallbackConverter = new HidlToAidlCallbackConverter(aidlResponseHandler); - try { - mSession.get().setNotify(mHidlToAidlCallbackConverter); - } catch (RemoteException e) { - Slog.d(TAG, "Failed to set callback"); - } - } - @Override public IBinder asBinder() { return null; @@ -125,12 +121,16 @@ public class AidlToHidlAdapter implements ISession { @Override public void getAuthenticatorId() throws RemoteException { - //Unsupported in HIDL + Log.e(TAG, "getAuthenticatorId unsupported in HIDL"); + mHidlToAidlCallbackConverter.unsupportedClientScheduled( + FingerprintGetAuthenticatorIdClient.class); } @Override public void invalidateAuthenticatorId() throws RemoteException { - //Unsupported in HIDL + Log.e(TAG, "invalidateAuthenticatorId unsupported in HIDL"); + mHidlToAidlCallbackConverter.unsupportedClientScheduled( + FingerprintInvalidationClient.class); } @Override @@ -140,72 +140,92 @@ public class AidlToHidlAdapter implements ISession { @Override public void close() throws RemoteException { - //Unsupported in HIDL + Log.e(TAG, "close unsupported in HIDL"); } @Override public void onUiReady() throws RemoteException { - //Unsupported in HIDL + Log.e(TAG, "onUiReady unsupported in HIDL"); } @Override public ICancellationSignal authenticateWithContext(long operationId, OperationContext context) throws RemoteException { - //Unsupported in HIDL - return null; + Log.e(TAG, "authenticateWithContext unsupported in HIDL"); + return authenticate(operationId); } @Override public ICancellationSignal enrollWithContext(HardwareAuthToken hat, OperationContext context) throws RemoteException { - //Unsupported in HIDL - return null; + Log.e(TAG, "enrollWithContext unsupported in HIDL"); + return enroll(hat); } @Override public ICancellationSignal detectInteractionWithContext(OperationContext context) throws RemoteException { - //Unsupported in HIDL - return null; + Log.e(TAG, "enrollWithContext unsupported in HIDL"); + return detectInteraction(); } @Override public void onPointerDownWithContext(PointerContext context) throws RemoteException { - //Unsupported in HIDL + Log.e(TAG, "onPointerDownWithContext unsupported in HIDL"); + onPointerDown(context.pointerId, (int) context.x, (int) context.y, context.minor, + context.major); } @Override public void onPointerUpWithContext(PointerContext context) throws RemoteException { - //Unsupported in HIDL + Log.e(TAG, "onPointerUpWithContext unsupported in HIDL"); + onPointerUp(context.pointerId); } @Override public void onContextChanged(OperationContext context) throws RemoteException { - //Unsupported in HIDL + Log.e(TAG, "onContextChanged unsupported in HIDL"); } @Override public void onPointerCancelWithContext(PointerContext context) throws RemoteException { - //Unsupported in HIDL + Log.e(TAG, "onPointerCancelWithContext unsupported in HIDL"); } @Override public void setIgnoreDisplayTouches(boolean shouldIgnore) throws RemoteException { - //Unsupported in HIDL + Log.e(TAG, "setIgnoreDisplayTouches unsupported in HIDL"); } @Override public int getInterfaceVersion() throws RemoteException { - //Unsupported in HIDL + Log.e(TAG, "getInterfaceVersion unsupported in HIDL"); return 0; } @Override public String getInterfaceHash() throws RemoteException { - //Unsupported in HIDL + Log.e(TAG, "getInterfaceHash unsupported in HIDL"); return null; } + private void setCallback(AidlResponseHandler aidlResponseHandler) { + mHidlToAidlCallbackConverter = new HidlToAidlCallbackConverter(aidlResponseHandler); + try { + if (mSession.get() != null) { + long halId = mSession.get().setNotify(mHidlToAidlCallbackConverter); + Slog.d(TAG, "Fingerprint HAL ready, HAL ID: " + halId); + if (halId == 0) { + Slog.d(TAG, "Unable to set HIDL callback."); + } + } else { + Slog.e(TAG, "Unable to set HIDL callback. HIDL daemon is null."); + } + } catch (RemoteException e) { + Slog.d(TAG, "Failed to set callback"); + } + } + private class Cancellation extends ICancellationSignal.Stub { Cancellation() {} diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java index 0730c672acd9..2f77275890dd 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/LockoutFrameworkImpl.java @@ -18,6 +18,7 @@ package com.android.server.biometrics.sensors.fingerprint.hidl; import static android.Manifest.permission.RESET_FINGERPRINT_LOCKOUT; +import android.annotation.NonNull; import android.app.AlarmManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; @@ -31,15 +32,18 @@ import android.util.Slog; import android.util.SparseBooleanArray; import android.util.SparseIntArray; +import com.android.internal.annotations.VisibleForTesting; import com.android.server.biometrics.sensors.LockoutTracker; +import java.util.function.Function; + /** * Tracks and enforces biometric lockout for biometric sensors that do not support lockout in the * HAL. */ public class LockoutFrameworkImpl implements LockoutTracker { - private static final String TAG = "LockoutTracker"; + private static final String TAG = "LockoutFrameworkImpl"; private static final String ACTION_LOCKOUT_RESET = "com.android.server.biometrics.sensors.fingerprint.ACTION_LOCKOUT_RESET"; private static final int MAX_FAILED_ATTEMPTS_LOCKOUT_TIMED = 5; @@ -65,22 +69,32 @@ public class LockoutFrameworkImpl implements LockoutTracker { void onLockoutReset(int userId); } - private final Context mContext; private final LockoutResetCallback mLockoutResetCallback; private final SparseBooleanArray mTimedLockoutCleared; private final SparseIntArray mFailedAttempts; private final AlarmManager mAlarmManager; private final LockoutReceiver mLockoutReceiver; private final Handler mHandler; + private final Function<Integer, PendingIntent> mLockoutResetIntent; + + public LockoutFrameworkImpl(@NonNull Context context, + @NonNull LockoutResetCallback lockoutResetCallback) { + this(context, lockoutResetCallback, (userId) -> PendingIntent.getBroadcast(context, userId, + new Intent(ACTION_LOCKOUT_RESET).putExtra(KEY_LOCKOUT_RESET_USER, userId), + PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE)); + } - public LockoutFrameworkImpl(Context context, LockoutResetCallback lockoutResetCallback) { - mContext = context; + @VisibleForTesting + LockoutFrameworkImpl(@NonNull Context context, + @NonNull LockoutResetCallback lockoutResetCallback, + @NonNull Function<Integer, PendingIntent> lockoutResetIntent) { mLockoutResetCallback = lockoutResetCallback; mTimedLockoutCleared = new SparseBooleanArray(); mFailedAttempts = new SparseIntArray(); mAlarmManager = context.getSystemService(AlarmManager.class); mLockoutReceiver = new LockoutReceiver(); mHandler = new Handler(Looper.getMainLooper()); + mLockoutResetIntent = lockoutResetIntent; context.registerReceiver(mLockoutReceiver, new IntentFilter(ACTION_LOCKOUT_RESET), RESET_FINGERPRINT_LOCKOUT, null /* handler */, Context.RECEIVER_EXPORTED); @@ -129,34 +143,18 @@ public class LockoutFrameworkImpl implements LockoutTracker { return LOCKOUT_NONE; } - /** - * Clears lockout for Fingerprint HIDL HAL - */ @Override - public void setLockoutModeForUser(int userId, int mode) { - mFailedAttempts.put(userId, 0); - mTimedLockoutCleared.put(userId, true); - // If we're asked to reset failed attempts externally (i.e. from Keyguard), - // the alarm might still be pending; remove it. - cancelLockoutResetForUser(userId); - mLockoutResetCallback.onLockoutReset(userId); - } + public void setLockoutModeForUser(int userId, int mode) {} private void cancelLockoutResetForUser(int userId) { - mAlarmManager.cancel(getLockoutResetIntentForUser(userId)); + mAlarmManager.cancel(mLockoutResetIntent.apply(userId)); } private void scheduleLockoutResetForUser(int userId) { mHandler.post(() -> { mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, - SystemClock.elapsedRealtime() + FAIL_LOCKOUT_TIMEOUT_MS, - getLockoutResetIntentForUser(userId)); + SystemClock.elapsedRealtime() + FAIL_LOCKOUT_TIMEOUT_MS, + mLockoutResetIntent.apply(userId)); }); } - - private PendingIntent getLockoutResetIntentForUser(int userId) { - return PendingIntent.getBroadcast(mContext, userId, - new Intent(ACTION_LOCKOUT_RESET).putExtra(KEY_LOCKOUT_RESET_USER, userId), - PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE); - } } diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java index 3b5cae328b3c..88b2ed4f79c9 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java @@ -50,14 +50,22 @@ import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback; import android.hardware.biometrics.IBiometricService; import android.hardware.biometrics.IBiometricServiceReceiver; import android.hardware.biometrics.PromptInfo; +import android.hardware.biometrics.fingerprint.SensorProps; +import android.hardware.face.FaceSensorConfigurations; import android.hardware.face.FaceSensorPropertiesInternal; import android.hardware.face.IFaceService; +import android.hardware.fingerprint.FingerprintSensorConfigurations; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.hardware.fingerprint.IFingerprintService; import android.hardware.iris.IIrisService; import android.os.Binder; +import android.os.RemoteException; import android.os.UserHandle; import android.platform.test.annotations.Presubmit; +import android.platform.test.annotations.RequiresFlagsDisabled; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.platform.test.flag.junit.SetFlagsRule; import androidx.test.InstrumentationRegistry; @@ -89,6 +97,9 @@ public class AuthServiceTest { @Rule public MockitoRule mockitorule = MockitoJUnit.rule(); + @Rule + public final CheckFlagsRule mCheckFlagsRule = + DeviceFlagsValueProvider.createCheckFlagsRule(); @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @@ -118,6 +129,10 @@ public class AuthServiceTest { @Captor private ArgumentCaptor<List<FingerprintSensorPropertiesInternal>> mFingerprintPropsCaptor; @Captor + private ArgumentCaptor<FingerprintSensorConfigurations> mFingerprintSensorConfigurationsCaptor; + @Captor + private ArgumentCaptor<FaceSensorConfigurations> mFaceSensorConfigurationsCaptor; + @Captor private ArgumentCaptor<List<FaceSensorPropertiesInternal>> mFacePropsCaptor; @Before @@ -143,6 +158,9 @@ public class AuthServiceTest { when(mContext.getResources()).thenReturn(mResources); when(mInjector.getBiometricService()).thenReturn(mBiometricService); when(mInjector.getConfiguration(any())).thenReturn(config); + when(mInjector.getFaceConfiguration(any())).thenReturn(config); + when(mInjector.getFingerprintConfiguration(any())).thenReturn(config); + when(mInjector.getIrisConfiguration(any())).thenReturn(config); when(mInjector.getFingerprintService()).thenReturn(mFingerprintService); when(mInjector.getFaceService()).thenReturn(mFaceService); when(mInjector.getIrisService()).thenReturn(mIrisService); @@ -173,12 +191,13 @@ public class AuthServiceTest { } @Test + @RequiresFlagsDisabled(com.android.server.biometrics.Flags.FLAG_DE_HIDL) public void testRegisterAuthenticator_registerAuthenticators() throws Exception { final int fingerprintId = 0; final int fingerprintStrength = 15; final int faceId = 1; - final int faceStrength = 4095; + final int faceStrength = 15; final String[] config = { // ID0:Fingerprint:Strong @@ -206,6 +225,51 @@ public class AuthServiceTest { Utils.authenticatorStrengthToPropertyStrength(faceStrength)); } + @Test + @RequiresFlagsEnabled(com.android.server.biometrics.Flags.FLAG_DE_HIDL) + public void testRegisterAuthenticator_registerAuthenticatorsLegacy() throws RemoteException { + final int fingerprintId = 0; + final int fingerprintStrength = 15; + + final int faceId = 1; + final int faceStrength = 4095; + + final String[] config = { + // ID0:Fingerprint:Strong + String.format("%d:2:%d", fingerprintId, fingerprintStrength), + // ID2:Face:Convenience + String.format("%d:8:%d", faceId, faceStrength) + }; + + when(mInjector.getFingerprintConfiguration(any())).thenReturn(config); + when(mInjector.getFaceConfiguration(any())).thenReturn(config); + when(mInjector.getFingerprintAidlInstances()).thenReturn(new String[]{}); + when(mInjector.getFaceAidlInstances()).thenReturn(new String[]{}); + + mAuthService = new AuthService(mContext, mInjector); + mAuthService.onStart(); + + verify(mFingerprintService).registerAuthenticatorsLegacy( + mFingerprintSensorConfigurationsCaptor.capture()); + + final SensorProps[] fingerprintProp = mFingerprintSensorConfigurationsCaptor.getValue() + .getSensorPairForInstance("defaultHIDL").second; + + assertEquals(fingerprintProp[0].commonProps.sensorId, fingerprintId); + assertEquals(fingerprintProp[0].commonProps.sensorStrength, + Utils.authenticatorStrengthToPropertyStrength(fingerprintStrength)); + + verify(mFaceService).registerAuthenticatorsLegacy( + mFaceSensorConfigurationsCaptor.capture()); + + final android.hardware.biometrics.face.SensorProps[] faceProp = + mFaceSensorConfigurationsCaptor.getValue() + .getSensorPairForInstance("defaultHIDL").second; + + assertEquals(faceProp[0].commonProps.sensorId, faceId); + assertEquals(faceProp[0].commonProps.sensorStrength, + Utils.authenticatorStrengthToPropertyStrength(faceStrength)); + } // TODO(b/141025588): Check that an exception is thrown when the userId != callingUserId @Test diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceTest.java new file mode 100644 index 000000000000..c9e1c4a8bfc5 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/FaceServiceTest.java @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors.face; + +import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG; +import static android.hardware.face.FaceSensorProperties.TYPE_UNKNOWN; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.hardware.biometrics.BiometricAuthenticator; +import android.hardware.biometrics.IBiometricService; +import android.hardware.biometrics.face.IFace; +import android.hardware.biometrics.face.SensorProps; +import android.hardware.face.FaceSensorConfigurations; +import android.hardware.face.FaceSensorPropertiesInternal; +import android.hardware.face.IFaceAuthenticatorsRegisteredCallback; +import android.os.RemoteException; +import android.platform.test.annotations.Presubmit; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; +import android.provider.Settings; +import android.testing.TestableContext; + +import androidx.test.filters.SmallTest; +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.internal.util.test.FakeSettingsProvider; +import com.android.internal.util.test.FakeSettingsProviderRule; +import com.android.server.biometrics.Flags; +import com.android.server.biometrics.Utils; +import com.android.server.biometrics.sensors.face.aidl.FaceProvider; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +@Presubmit +@SmallTest +public class FaceServiceTest { + private static final int ID_DEFAULT = 2; + private static final int ID_VIRTUAL = 6; + private static final String NAME_DEFAULT = "default"; + private static final String NAME_VIRTUAL = "virtual"; + + @Rule + public final MockitoRule mMockito = MockitoJUnit.rule(); + @Rule + public final CheckFlagsRule mCheckFlagsRule = + DeviceFlagsValueProvider.createCheckFlagsRule(); + @Rule + public final TestableContext mContext = new TestableContext( + InstrumentationRegistry.getInstrumentation().getTargetContext(), null); + @Rule + public final FakeSettingsProviderRule mSettingsRule = FakeSettingsProvider.rule(); + @Mock + FaceProvider mFaceProviderDefault; + @Mock + FaceProvider mFaceProviderVirtual; + @Mock + IFace mDefaultFaceDaemon; + @Mock + IFace mVirtualFaceDaemon; + @Mock + IBiometricService mIBiometricService; + + private final SensorProps mDefaultSensorProps = new SensorProps(); + private final SensorProps mVirtualSensorProps = new SensorProps(); + private FaceService mFaceService; + private final FaceSensorPropertiesInternal mSensorPropsDefault = + new FaceSensorPropertiesInternal(ID_DEFAULT, STRENGTH_STRONG, + 2 /* maxEnrollmentsPerUser */, + List.of(), + TYPE_UNKNOWN, + true /* supportsFaceDetection */, + true /* supportsSelfIllumination */, + false /* resetLockoutRequiresChallenge */); + private final FaceSensorPropertiesInternal mSensorPropsVirtual = + new FaceSensorPropertiesInternal(ID_VIRTUAL, STRENGTH_STRONG, + 2 /* maxEnrollmentsPerUser */, + List.of(), + TYPE_UNKNOWN, + true /* supportsFaceDetection */, + true /* supportsSelfIllumination */, + false /* resetLockoutRequiresChallenge */); + private FaceSensorConfigurations mFaceSensorConfigurations; + + @Before + public void setUp() throws RemoteException { + when(mDefaultFaceDaemon.getSensorProps()).thenReturn( + new SensorProps[]{mDefaultSensorProps}); + when(mVirtualFaceDaemon.getSensorProps()).thenReturn( + new SensorProps[]{mVirtualSensorProps}); + when(mFaceProviderDefault.getSensorProperties()).thenReturn(List.of(mSensorPropsDefault)); + when(mFaceProviderVirtual.getSensorProperties()).thenReturn(List.of(mSensorPropsVirtual)); + + mFaceSensorConfigurations = new FaceSensorConfigurations(false); + mFaceSensorConfigurations.addAidlConfigs(new String[]{NAME_DEFAULT, NAME_VIRTUAL}, + (name) -> { + if (name.equals(IFace.DESCRIPTOR + "/" + NAME_DEFAULT)) { + return mDefaultFaceDaemon; + } else if (name.equals(IFace.DESCRIPTOR + "/" + NAME_VIRTUAL)) { + return mVirtualFaceDaemon; + } + return null; + }); + } + + private void initService() { + mFaceService = new FaceService(mContext, + (filteredSensorProps, resetLockoutRequiresChallenge) -> { + if (NAME_DEFAULT.equals(filteredSensorProps.first)) return mFaceProviderDefault; + if (NAME_VIRTUAL.equals(filteredSensorProps.first)) return mFaceProviderVirtual; + return null; + }, () -> mIBiometricService); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL) + public void registerAuthenticatorsLegacy_defaultOnly() throws Exception { + initService(); + + mFaceService.mServiceWrapper.registerAuthenticatorsLegacy(mFaceSensorConfigurations); + waitForRegistration(); + + verify(mIBiometricService).registerAuthenticator(eq(ID_DEFAULT), + eq(BiometricAuthenticator.TYPE_FACE), + eq(Utils.propertyStrengthToAuthenticatorStrength(STRENGTH_STRONG)), + any()); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL) + public void registerAuthenticatorsLegacy_virtualOnly() throws Exception { + initService(); + Settings.Secure.putInt(mSettingsRule.mockContentResolver(mContext), + Settings.Secure.BIOMETRIC_VIRTUAL_ENABLED, 1); + + mFaceService.mServiceWrapper.registerAuthenticatorsLegacy(mFaceSensorConfigurations); + waitForRegistration(); + + verify(mIBiometricService).registerAuthenticator(eq(ID_VIRTUAL), + eq(BiometricAuthenticator.TYPE_FACE), + eq(Utils.propertyStrengthToAuthenticatorStrength(STRENGTH_STRONG)), any()); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL) + public void registerAuthenticatorsLegacy_virtualAlwaysWhenNoOther() throws Exception { + mFaceSensorConfigurations = new FaceSensorConfigurations(false); + mFaceSensorConfigurations.addAidlConfigs(new String[]{NAME_VIRTUAL}, + (name) -> { + if (name.equals(IFace.DESCRIPTOR + "/" + NAME_DEFAULT)) { + return mDefaultFaceDaemon; + } else if (name.equals(IFace.DESCRIPTOR + "/" + NAME_VIRTUAL)) { + return mVirtualFaceDaemon; + } + return null; + }); + initService(); + + mFaceService.mServiceWrapper.registerAuthenticatorsLegacy(mFaceSensorConfigurations); + waitForRegistration(); + + verify(mIBiometricService).registerAuthenticator(eq(ID_VIRTUAL), + eq(BiometricAuthenticator.TYPE_FACE), + eq(Utils.propertyStrengthToAuthenticatorStrength(STRENGTH_STRONG)), any()); + } + + private void waitForRegistration() throws Exception { + final CountDownLatch latch = new CountDownLatch(1); + mFaceService.mServiceWrapper.addAuthenticatorsRegisteredCallback( + new IFaceAuthenticatorsRegisteredCallback.Stub() { + public void onAllAuthenticatorsRegistered( + List<FaceSensorPropertiesInternal> sensors) { + latch.countDown(); + } + }); + latch.await(10, TimeUnit.SECONDS); + } +} diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java index f43120d1a755..8b1a2915820a 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/FaceProviderTest.java @@ -16,6 +16,9 @@ package com.android.server.biometrics.sensors.face.aidl; +import static android.os.UserHandle.USER_NULL; +import static android.os.UserHandle.USER_SYSTEM; + import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; @@ -32,15 +35,19 @@ import android.hardware.biometrics.common.CommonProps; import android.hardware.biometrics.face.IFace; import android.hardware.biometrics.face.ISession; import android.hardware.biometrics.face.SensorProps; +import android.hardware.face.HidlFaceSensorConfig; import android.os.RemoteException; -import android.os.UserHandle; import android.os.UserManager; import android.platform.test.annotations.Presubmit; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import com.android.internal.R; +import com.android.server.biometrics.Flags; import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.sensors.BaseClientMonitor; import com.android.server.biometrics.sensors.BiometricScheduler; @@ -49,6 +56,7 @@ import com.android.server.biometrics.sensors.HalClientMonitor; import com.android.server.biometrics.sensors.LockoutResetDispatcher; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -59,6 +67,10 @@ import java.util.ArrayList; @SmallTest public class FaceProviderTest { + @Rule + public final CheckFlagsRule mCheckFlagsRule = + DeviceFlagsValueProvider.createCheckFlagsRule(); + private static final String TAG = "FaceProviderTest"; private static final float FRR_THRESHOLD = 0.2f; @@ -109,7 +121,7 @@ public class FaceProviderTest { mFaceProvider = new FaceProvider(mContext, mBiometricStateCallback, mSensorProps, TAG, mLockoutResetDispatcher, mBiometricContext, - mDaemon); + mDaemon, false /* resetLockoutRequiresChallenge */, false /* testHalEnabled */); } @Test @@ -124,10 +136,38 @@ public class FaceProviderTest { assertThat(currentClient).isInstanceOf(FaceInternalCleanupClient.class); assertThat(currentClient.getSensorId()).isEqualTo(prop.commonProps.sensorId); - assertThat(currentClient.getTargetUserId()).isEqualTo(UserHandle.USER_SYSTEM); + assertThat(currentClient.getTargetUserId()).isEqualTo(USER_SYSTEM); } } + @Test + @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL) + public void testAddingHidlSensors() { + when(mResources.getIntArray(anyInt())).thenReturn(new int[]{}); + when(mResources.getBoolean(anyInt())).thenReturn(false); + + final int faceId = 0; + final int faceStrength = 15; + final String config = String.format("%d:8:%d", faceId, faceStrength); + final HidlFaceSensorConfig faceSensorConfig = new HidlFaceSensorConfig(); + faceSensorConfig.parse(config, mContext); + final HidlFaceSensorConfig[] hidlFaceSensorConfig = + new HidlFaceSensorConfig[]{faceSensorConfig}; + mFaceProvider = new FaceProvider(mContext, + mBiometricStateCallback, hidlFaceSensorConfig, TAG, + mLockoutResetDispatcher, mBiometricContext, mDaemon, + true /* resetLockoutRequiresChallenge */, + true /* testHalEnabled */); + + assertThat(mFaceProvider.mFaceSensors.get(faceId) + .getLazySession().get().getUserId()).isEqualTo(USER_NULL); + + waitForIdle(); + + assertThat(mFaceProvider.mFaceSensors.get(faceId) + .getLazySession().get().getUserId()).isEqualTo(USER_SYSTEM); + } + @SuppressWarnings("rawtypes") @Test public void halServiceDied_resetsAllSchedulers() { diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java index 7a293e80c183..e7f7195588ff 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/aidl/SensorTest.java @@ -157,7 +157,7 @@ public class SensorTest { sensorProps.halControlsPreview, false /* resetLockoutRequiresChallenge */); final Sensor sensor = new Sensor("SensorTest", mFaceProvider, mContext, null /* handler */, internalProp, mLockoutResetDispatcher, mBiometricContext); - + sensor.init(mLockoutResetDispatcher, mFaceProvider); mScheduler.reset(); assertNull(mScheduler.getCurrentClient()); @@ -185,6 +185,7 @@ public class SensorTest { sensorProps.halControlsPreview, false /* resetLockoutRequiresChallenge */); final Sensor sensor = new Sensor("SensorTest", mFaceProvider, mContext, null, internalProp, mLockoutResetDispatcher, mBiometricContext, mCurrentSession); + sensor.init(mLockoutResetDispatcher, mFaceProvider); mScheduler = (UserAwareBiometricScheduler) sensor.getScheduler(); sensor.mCurrentSession = new AidlSession(0, mock(ISession.class), USER_ID, mHalCallback); diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapterTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapterTest.java new file mode 100644 index 000000000000..4e43332ab52c --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSensorAdapterTest.java @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors.face.hidl; + +import static com.android.server.biometrics.sensors.face.hidl.HidlToAidlSessionAdapter.ENROLL_TIMEOUT_SEC; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.hardware.biometrics.IBiometricService; +import android.hardware.biometrics.face.V1_0.IBiometricsFace; +import android.hardware.biometrics.face.V1_0.OptionalUint64; +import android.hardware.biometrics.face.V1_0.Status; +import android.hardware.face.Face; +import android.hardware.face.HidlFaceSensorConfig; +import android.os.Handler; +import android.os.RemoteException; +import android.os.test.TestLooper; +import android.testing.TestableContext; + +import androidx.test.core.app.ApplicationProvider; + +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; +import com.android.server.biometrics.sensors.AuthSessionCoordinator; +import com.android.server.biometrics.sensors.BiometricScheduler; +import com.android.server.biometrics.sensors.BiometricUtils; +import com.android.server.biometrics.sensors.LockoutResetDispatcher; +import com.android.server.biometrics.sensors.LockoutTracker; +import com.android.server.biometrics.sensors.face.aidl.AidlResponseHandler; +import com.android.server.biometrics.sensors.face.aidl.FaceEnrollClient; +import com.android.server.biometrics.sensors.face.aidl.FaceProvider; +import com.android.server.biometrics.sensors.face.aidl.FaceResetLockoutClient; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +public class HidlToAidlSensorAdapterTest { + private static final String TAG = "HidlToAidlSensorAdapterTest"; + private static final int USER_ID = 2; + private static final int SENSOR_ID = 4; + private static final byte[] HAT = new byte[69]; + @Rule + public final MockitoRule mMockito = MockitoJUnit.rule(); + + @Mock + private IBiometricService mBiometricService; + @Mock + private LockoutResetDispatcher mLockoutResetDispatcherForSensor; + @Mock + private LockoutResetDispatcher mLockoutResetDispatcherForClient; + @Mock + private BiometricLogger mLogger; + @Mock + private BiometricContext mBiometricContext; + @Mock + private AuthSessionCoordinator mAuthSessionCoordinator; + @Mock + private FaceProvider mFaceProvider; + @Mock + private Runnable mInternalCleanupAndGetFeatureRunnable; + @Mock + private IBiometricsFace mDaemon; + @Mock + AidlResponseHandler.AidlResponseHandlerCallback mAidlResponseHandlerCallback; + @Mock + BiometricUtils<Face> mBiometricUtils; + + private final TestLooper mLooper = new TestLooper(); + private HidlToAidlSensorAdapter mHidlToAidlSensorAdapter; + private final TestableContext mContext = new TestableContext( + ApplicationProvider.getApplicationContext()); + + @Before + public void setUp() throws RemoteException { + final OptionalUint64 result = new OptionalUint64(); + result.status = Status.OK; + + when(mBiometricContext.getAuthSessionCoordinator()).thenReturn(mAuthSessionCoordinator); + when(mDaemon.setCallback(any())).thenReturn(result); + doAnswer((answer) -> { + mHidlToAidlSensorAdapter.getLazySession().get().getHalSessionCallback() + .onLockoutChanged(0); + return null; + }).when(mDaemon).resetLockout(any()); + doAnswer((answer) -> { + mHidlToAidlSensorAdapter.getLazySession().get().getHalSessionCallback() + .onEnrollmentProgress(1, 0); + return null; + }).when(mDaemon).enroll(any(), anyInt(), any()); + + mContext.getOrCreateTestableResources(); + + final String config = String.format("%d:8:15", SENSOR_ID); + final BiometricScheduler scheduler = new BiometricScheduler(TAG, + new Handler(mLooper.getLooper()), + BiometricScheduler.SENSOR_TYPE_FACE, + null /* gestureAvailabilityTracker */, + mBiometricService, 10 /* recentOperationsLimit */); + final HidlFaceSensorConfig faceSensorConfig = new HidlFaceSensorConfig(); + faceSensorConfig.parse(config, mContext); + mHidlToAidlSensorAdapter = new HidlToAidlSensorAdapter(TAG, mFaceProvider, + mContext, new Handler(mLooper.getLooper()), faceSensorConfig, + mLockoutResetDispatcherForSensor, mBiometricContext, + false /* resetLockoutRequiresChallenge */, mInternalCleanupAndGetFeatureRunnable, + mAuthSessionCoordinator, mDaemon, mAidlResponseHandlerCallback); + mHidlToAidlSensorAdapter.init(mLockoutResetDispatcherForSensor, mFaceProvider); + mHidlToAidlSensorAdapter.setScheduler(scheduler); + mHidlToAidlSensorAdapter.handleUserChanged(USER_ID); + } + + @Test + public void lockoutTimedResetViaClient() { + setLockoutTimed(); + + mHidlToAidlSensorAdapter.getScheduler().scheduleClientMonitor( + new FaceResetLockoutClient(mContext, mHidlToAidlSensorAdapter.getLazySession(), + USER_ID, TAG, SENSOR_ID, mLogger, mBiometricContext, HAT, + mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */), + mLockoutResetDispatcherForClient, 0 /* biometricStrength */)); + mLooper.dispatchAll(); + + assertThat(mHidlToAidlSensorAdapter.getLockoutTracker(false/* forAuth */) + .getLockoutModeForUser(USER_ID)).isEqualTo(LockoutTracker.LOCKOUT_NONE); + verify(mLockoutResetDispatcherForClient, never()).notifyLockoutResetCallbacks(SENSOR_ID); + verify(mLockoutResetDispatcherForSensor).notifyLockoutResetCallbacks(SENSOR_ID); + verify(mAuthSessionCoordinator, never()).resetLockoutFor(eq(USER_ID), anyInt(), anyLong()); + } + + @Test + public void lockoutTimedResetViaCallback() { + setLockoutTimed(); + + mHidlToAidlSensorAdapter.getLazySession().get().getHalSessionCallback().onLockoutChanged(0); + mLooper.dispatchAll(); + + verify(mLockoutResetDispatcherForSensor).notifyLockoutResetCallbacks(eq( + SENSOR_ID)); + assertThat(mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */) + .getLockoutModeForUser(USER_ID)) + .isEqualTo(LockoutTracker.LOCKOUT_NONE); + verify(mAuthSessionCoordinator, never()).resetLockoutFor(eq(USER_ID), anyInt(), anyLong()); + } + + @Test + public void lockoutPermanentResetViaCallback() { + setLockoutPermanent(); + + mHidlToAidlSensorAdapter.getLazySession().get().getHalSessionCallback().onLockoutChanged(0); + mLooper.dispatchAll(); + + verify(mLockoutResetDispatcherForSensor).notifyLockoutResetCallbacks(eq( + SENSOR_ID)); + assertThat(mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */) + .getLockoutModeForUser(USER_ID)).isEqualTo(LockoutTracker.LOCKOUT_NONE); + verify(mAuthSessionCoordinator, never()).resetLockoutFor(eq(USER_ID), anyInt(), anyLong()); + } + + @Test + public void lockoutPermanentResetViaClient() { + setLockoutPermanent(); + + mHidlToAidlSensorAdapter.getScheduler().scheduleClientMonitor( + new FaceResetLockoutClient(mContext, + mHidlToAidlSensorAdapter.getLazySession(), + USER_ID, TAG, SENSOR_ID, mLogger, mBiometricContext, HAT, + mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */), + mLockoutResetDispatcherForClient, 0 /* biometricStrength */)); + mLooper.dispatchAll(); + + verify(mLockoutResetDispatcherForClient, never()).notifyLockoutResetCallbacks(SENSOR_ID); + verify(mLockoutResetDispatcherForSensor).notifyLockoutResetCallbacks(SENSOR_ID); + assertThat(mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */) + .getLockoutModeForUser(USER_ID)).isEqualTo(LockoutTracker.LOCKOUT_NONE); + verify(mAuthSessionCoordinator, never()).resetLockoutFor(eq(USER_ID), anyInt(), anyLong()); + } + + @Test + public void verifyOnEnrollSuccessCallback() { + mHidlToAidlSensorAdapter.getScheduler().scheduleClientMonitor(new FaceEnrollClient(mContext, + mHidlToAidlSensorAdapter.getLazySession(), null /* token */, null /* listener */, + USER_ID, HAT, TAG, 1 /* requestId */, mBiometricUtils, + new int[]{} /* disabledFeatures */, ENROLL_TIMEOUT_SEC, null /* previewSurface */, + SENSOR_ID, mLogger, mBiometricContext, 1 /* maxTemplatesPerUser */, + false /* debugConsent */)); + mLooper.dispatchAll(); + + verify(mAidlResponseHandlerCallback).onEnrollSuccess(); + } + + private void setLockoutTimed() { + mHidlToAidlSensorAdapter.getLazySession().get().getHalSessionCallback() + .onLockoutChanged(1); + mLooper.dispatchAll(); + + assertThat(mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */) + .getLockoutModeForUser(USER_ID)) + .isEqualTo(LockoutTracker.LOCKOUT_TIMED); + } + + private void setLockoutPermanent() { + mHidlToAidlSensorAdapter.getLazySession().get().getHalSessionCallback() + .onLockoutChanged(-1); + mLooper.dispatchAll(); + + assertThat(mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */) + .getLockoutModeForUser(USER_ID)).isEqualTo(LockoutTracker.LOCKOUT_PERMANENT); + } +} diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/AidlToHidlAdapterTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSessionAdapterTest.java index 9a40e8a7201a..b9a4fb4e0939 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/AidlToHidlAdapterTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/face/hidl/HidlToAidlSessionAdapterTest.java @@ -16,7 +16,7 @@ package com.android.server.biometrics.sensors.face.hidl; -import static com.android.server.biometrics.sensors.face.hidl.AidlToHidlAdapter.ENROLL_TIMEOUT_SEC; +import static com.android.server.biometrics.sensors.face.hidl.HidlToAidlSessionAdapter.ENROLL_TIMEOUT_SEC; import static com.android.server.biometrics.sensors.face.hidl.FaceGenerateChallengeClient.CHALLENGE_TIMEOUT_SEC; import static com.google.common.truth.Truth.assertThat; @@ -68,7 +68,7 @@ import java.util.List; @Presubmit @SmallTest -public class AidlToHidlAdapterTest { +public class HidlToAidlSessionAdapterTest { @Rule public final MockitoRule mockito = MockitoJUnit.rule(); @@ -84,25 +84,32 @@ public class AidlToHidlAdapterTest { private Clock mClock; private final long mChallenge = 100L; - private AidlToHidlAdapter mAidlToHidlAdapter; + private HidlToAidlSessionAdapter mHidlToAidlSessionAdapter; private final Face mFace = new Face("face" /* name */, 1 /* faceId */, 0 /* deviceId */); private final int mFeature = BiometricFaceConstants.FEATURE_REQUIRE_REQUIRE_DIVERSITY; private final byte[] mFeatures = new byte[]{Feature.REQUIRE_ATTENTION}; @Before public void setUp() throws RemoteException { + final OptionalUint64 setCallbackResult = new OptionalUint64(); + setCallbackResult.value = 1; + + when(mSession.setCallback(any())).thenReturn(setCallbackResult); + TestableContext testableContext = new TestableContext( InstrumentationRegistry.getInstrumentation().getContext()); testableContext.addMockSystemService(FaceManager.class, mFaceManager); - mAidlToHidlAdapter = new AidlToHidlAdapter(testableContext, () -> mSession, 0 /* userId */, - mAidlResponseHandler, mClock); + + mHidlToAidlSessionAdapter = new HidlToAidlSessionAdapter(testableContext, () -> mSession, + 0 /* userId */, mAidlResponseHandler, mClock); mHardwareAuthToken.timestamp = new Timestamp(); mHardwareAuthToken.mac = new byte[10]; - final OptionalUint64 result = new OptionalUint64(); - result.status = Status.OK; - result.value = mChallenge; - when(mSession.generateChallenge(anyInt())).thenReturn(result); + final OptionalUint64 generateChallengeResult = new OptionalUint64(); + generateChallengeResult.status = Status.OK; + generateChallengeResult.value = mChallenge; + + when(mSession.generateChallenge(anyInt())).thenReturn(generateChallengeResult); when(mFaceManager.getEnrolledFaces(anyInt())).thenReturn(List.of(mFace)); } @@ -112,16 +119,16 @@ public class AidlToHidlAdapterTest { final ArgumentCaptor<Long> challengeCaptor = ArgumentCaptor.forClass(Long.class); - mAidlToHidlAdapter.generateChallenge(); + mHidlToAidlSessionAdapter.generateChallenge(); verify(mSession).generateChallenge(CHALLENGE_TIMEOUT_SEC); verify(mAidlResponseHandler).onChallengeGenerated(challengeCaptor.capture()); assertThat(challengeCaptor.getValue()).isEqualTo(mChallenge); forwardTime(10 /* seconds */); - mAidlToHidlAdapter.generateChallenge(); + mHidlToAidlSessionAdapter.generateChallenge(); forwardTime(20 /* seconds */); - mAidlToHidlAdapter.generateChallenge(); + mHidlToAidlSessionAdapter.generateChallenge(); //Confirms that the challenge is cached and the hal method is not called again verifyNoMoreInteractions(mSession); @@ -129,7 +136,7 @@ public class AidlToHidlAdapterTest { .onChallengeGenerated(mChallenge); forwardTime(60 /* seconds */); - mAidlToHidlAdapter.generateChallenge(); + mHidlToAidlSessionAdapter.generateChallenge(); //HAL method called after challenge has timed out verify(mSession, times(2)).generateChallenge(CHALLENGE_TIMEOUT_SEC); @@ -138,11 +145,11 @@ public class AidlToHidlAdapterTest { @Test public void testRevokeChallenge_waitsUntilEmpty() throws RemoteException { for (int i = 0; i < 3; i++) { - mAidlToHidlAdapter.generateChallenge(); + mHidlToAidlSessionAdapter.generateChallenge(); forwardTime(10 /* seconds */); } for (int i = 0; i < 3; i++) { - mAidlToHidlAdapter.revokeChallenge(0); + mHidlToAidlSessionAdapter.revokeChallenge(0); forwardTime((i + 1) * 10 /* seconds */); } @@ -151,20 +158,19 @@ public class AidlToHidlAdapterTest { @Test public void testRevokeChallenge_timeout() throws RemoteException { - mAidlToHidlAdapter.generateChallenge(); - mAidlToHidlAdapter.generateChallenge(); + mHidlToAidlSessionAdapter.generateChallenge(); + mHidlToAidlSessionAdapter.generateChallenge(); forwardTime(700); - mAidlToHidlAdapter.generateChallenge(); - mAidlToHidlAdapter.revokeChallenge(0); + mHidlToAidlSessionAdapter.generateChallenge(); + mHidlToAidlSessionAdapter.revokeChallenge(0); verify(mSession).revokeChallenge(); } @Test public void testEnroll() throws RemoteException { - ICancellationSignal cancellationSignal = mAidlToHidlAdapter.enroll(mHardwareAuthToken, - EnrollmentType.DEFAULT, mFeatures, - null /* previewSurface */); + ICancellationSignal cancellationSignal = mHidlToAidlSessionAdapter.enroll( + mHardwareAuthToken, EnrollmentType.DEFAULT, mFeatures, null /* previewSurface */); ArgumentCaptor<ArrayList<Integer>> featureCaptor = ArgumentCaptor.forClass(ArrayList.class); verify(mSession).enroll(any(), eq(ENROLL_TIMEOUT_SEC), featureCaptor.capture()); @@ -182,7 +188,8 @@ public class AidlToHidlAdapterTest { @Test public void testAuthenticate() throws RemoteException { final int operationId = 2; - ICancellationSignal cancellationSignal = mAidlToHidlAdapter.authenticate(operationId); + ICancellationSignal cancellationSignal = mHidlToAidlSessionAdapter.authenticate( + operationId); verify(mSession).authenticate(operationId); @@ -193,7 +200,7 @@ public class AidlToHidlAdapterTest { @Test public void testDetectInteraction() throws RemoteException { - ICancellationSignal cancellationSignal = mAidlToHidlAdapter.detectInteraction(); + ICancellationSignal cancellationSignal = mHidlToAidlSessionAdapter.detectInteraction(); verify(mSession).authenticate(0); @@ -204,7 +211,7 @@ public class AidlToHidlAdapterTest { @Test public void testEnumerateEnrollments() throws RemoteException { - mAidlToHidlAdapter.enumerateEnrollments(); + mHidlToAidlSessionAdapter.enumerateEnrollments(); verify(mSession).enumerate(); } @@ -212,7 +219,7 @@ public class AidlToHidlAdapterTest { @Test public void testRemoveEnrollment() throws RemoteException { final int[] enrollments = new int[]{1}; - mAidlToHidlAdapter.removeEnrollments(enrollments); + mHidlToAidlSessionAdapter.removeEnrollments(enrollments); verify(mSession).remove(enrollments[0]); } @@ -226,8 +233,8 @@ public class AidlToHidlAdapterTest { when(mSession.getFeature(eq(mFeature), anyInt())).thenReturn(result); - mAidlToHidlAdapter.setFeature(mFeature); - mAidlToHidlAdapter.getFeatures(); + mHidlToAidlSessionAdapter.setFeature(mFeature); + mHidlToAidlSessionAdapter.getFeatures(); verify(mSession).getFeature(eq(mFeature), anyInt()); verify(mAidlResponseHandler).onFeaturesRetrieved(featureRetrieved.capture()); @@ -244,8 +251,8 @@ public class AidlToHidlAdapterTest { when(mSession.getFeature(eq(mFeature), anyInt())).thenReturn(result); - mAidlToHidlAdapter.setFeature(mFeature); - mAidlToHidlAdapter.getFeatures(); + mHidlToAidlSessionAdapter.setFeature(mFeature); + mHidlToAidlSessionAdapter.getFeatures(); verify(mSession).getFeature(eq(mFeature), anyInt()); verify(mAidlResponseHandler).onFeaturesRetrieved(featureRetrieved.capture()); @@ -260,8 +267,8 @@ public class AidlToHidlAdapterTest { when(mSession.getFeature(eq(mFeature), anyInt())).thenReturn(result); - mAidlToHidlAdapter.setFeature(mFeature); - mAidlToHidlAdapter.getFeatures(); + mHidlToAidlSessionAdapter.setFeature(mFeature); + mHidlToAidlSessionAdapter.getFeatures(); verify(mSession).getFeature(eq(mFeature), anyInt()); verify(mAidlResponseHandler, never()).onFeaturesRetrieved(any()); @@ -270,7 +277,7 @@ public class AidlToHidlAdapterTest { @Test public void testGetFeatures_featureNotSet() throws RemoteException { - mAidlToHidlAdapter.getFeatures(); + mHidlToAidlSessionAdapter.getFeatures(); verify(mSession, never()).getFeature(eq(mFeature), anyInt()); verify(mAidlResponseHandler, never()).onFeaturesRetrieved(any()); @@ -283,7 +290,7 @@ public class AidlToHidlAdapterTest { when(mSession.setFeature(anyInt(), anyBoolean(), any(), anyInt())).thenReturn(Status.OK); - mAidlToHidlAdapter.setFeature(mHardwareAuthToken, feature, enabled); + mHidlToAidlSessionAdapter.setFeature(mHardwareAuthToken, feature, enabled); verify(mAidlResponseHandler).onFeatureSet(feature); } @@ -296,7 +303,7 @@ public class AidlToHidlAdapterTest { when(mSession.setFeature(anyInt(), anyBoolean(), any(), anyInt())) .thenReturn(Status.INTERNAL_ERROR); - mAidlToHidlAdapter.setFeature(mHardwareAuthToken, feature, enabled); + mHidlToAidlSessionAdapter.setFeature(mHardwareAuthToken, feature, enabled); verify(mAidlResponseHandler).onError(BiometricFaceConstants.FACE_ERROR_UNKNOWN, 0 /* vendorCode */); @@ -311,7 +318,7 @@ public class AidlToHidlAdapterTest { when(mSession.getAuthenticatorId()).thenReturn(result); - mAidlToHidlAdapter.getAuthenticatorId(); + mHidlToAidlSessionAdapter.getAuthenticatorId(); verify(mSession).getAuthenticatorId(); verify(mAidlResponseHandler).onAuthenticatorIdRetrieved(authenticatorId); @@ -319,7 +326,7 @@ public class AidlToHidlAdapterTest { @Test public void testResetLockout() throws RemoteException { - mAidlToHidlAdapter.resetLockout(mHardwareAuthToken); + mHidlToAidlSessionAdapter.resetLockout(mHardwareAuthToken); ArgumentCaptor<ArrayList> hatCaptor = ArgumentCaptor.forClass(ArrayList.class); diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java index 2aa62d96168d..f570ba23441d 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/FingerprintServiceTest.java @@ -42,13 +42,19 @@ import static org.mockito.Mockito.when; import android.app.AppOpsManager; import android.content.pm.PackageManager; import android.hardware.biometrics.IBiometricService; +import android.hardware.biometrics.fingerprint.IFingerprint; +import android.hardware.biometrics.fingerprint.SensorProps; import android.hardware.fingerprint.FingerprintAuthenticateOptions; +import android.hardware.fingerprint.FingerprintSensorConfigurations; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.hardware.fingerprint.IFingerprintAuthenticatorsRegisteredCallback; import android.hardware.fingerprint.IFingerprintServiceReceiver; import android.os.Binder; import android.os.IBinder; import android.platform.test.annotations.Presubmit; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.provider.Settings; import android.testing.TestableContext; @@ -58,6 +64,7 @@ import androidx.test.platform.app.InstrumentationRegistry; import com.android.internal.util.test.FakeSettingsProvider; import com.android.internal.util.test.FakeSettingsProviderRule; import com.android.server.LocalServices; +import com.android.server.biometrics.Flags; import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintProvider; import com.android.server.companion.virtual.VirtualDeviceManagerInternal; @@ -89,6 +96,9 @@ public class FingerprintServiceTest { @Rule public final MockitoRule mMockito = MockitoJUnit.rule(); @Rule + public final CheckFlagsRule mCheckFlagsRule = + DeviceFlagsValueProvider.createCheckFlagsRule(); + @Rule public final TestableContext mContext = new TestableContext( InstrumentationRegistry.getInstrumentation().getTargetContext(), null); @Rule @@ -110,6 +120,10 @@ public class FingerprintServiceTest { private IBinder mToken; @Mock private VirtualDeviceManagerInternal mVdmInternal; + @Mock + private IFingerprint mDefaultFingerprintDaemon; + @Mock + private IFingerprint mVirtualFingerprintDaemon; @Captor private ArgumentCaptor<FingerprintAuthenticateOptions> mAuthenticateOptionsCaptor; @@ -126,7 +140,10 @@ public class FingerprintServiceTest { List.of(), TYPE_UDFPS_OPTICAL, false /* resetLockoutRequiresHardwareAuthToken */); + private FingerprintSensorConfigurations mFingerprintSensorConfigurations; private FingerprintService mService; + private final SensorProps mDefaultSensorProps = new SensorProps(); + private final SensorProps mVirtualSensorProps = new SensorProps(); @Before public void setup() throws Exception { @@ -139,6 +156,10 @@ public class FingerprintServiceTest { .thenAnswer(i -> i.getArguments()[0].equals(ID_DEFAULT)); when(mFingerprintVirtual.containsSensor(anyInt())) .thenAnswer(i -> i.getArguments()[0].equals(ID_VIRTUAL)); + when(mDefaultFingerprintDaemon.getSensorProps()).thenReturn( + new SensorProps[]{mDefaultSensorProps}); + when(mVirtualFingerprintDaemon.getSensorProps()).thenReturn( + new SensorProps[]{mVirtualSensorProps}); mContext.addMockSystemService(AppOpsManager.class, mAppOpsManager); for (int permission : List.of(OP_USE_BIOMETRIC, OP_USE_FINGERPRINT)) { @@ -150,6 +171,18 @@ public class FingerprintServiceTest { mContext.getTestablePermissions().setPermission( permission, PackageManager.PERMISSION_GRANTED); } + + mFingerprintSensorConfigurations = new FingerprintSensorConfigurations( + true /* resetLockoutRequiresHardwareAuthToken */); + mFingerprintSensorConfigurations.addAidlSensors(new String[]{NAME_DEFAULT, NAME_VIRTUAL}, + (name) -> { + if (name.equals(IFingerprint.DESCRIPTOR + "/" + NAME_DEFAULT)) { + return mDefaultFingerprintDaemon; + } else if (name.equals(IFingerprint.DESCRIPTOR + "/" + NAME_VIRTUAL)) { + return mVirtualFingerprintDaemon; + } + return null; + }); } private void initServiceWith(String... aidlInstances) { @@ -160,6 +193,10 @@ public class FingerprintServiceTest { if (NAME_DEFAULT.equals(name)) return mFingerprintDefault; if (NAME_VIRTUAL.equals(name)) return mFingerprintVirtual; return null; + }, (sensorPropsPair, resetLockoutRequiresHardwareAuthToken) -> { + if (NAME_DEFAULT.equals(sensorPropsPair.first)) return mFingerprintDefault; + if (NAME_VIRTUAL.equals(sensorPropsPair.first)) return mFingerprintVirtual; + return null; }); } @@ -180,6 +217,17 @@ public class FingerprintServiceTest { } @Test + @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL) + public void registerAuthenticatorsLegacy_defaultOnly() throws Exception { + initServiceWith(NAME_DEFAULT, NAME_VIRTUAL); + + mService.mServiceWrapper.registerAuthenticatorsLegacy(mFingerprintSensorConfigurations); + waitForRegistration(); + + verify(mIBiometricService).registerAuthenticator(eq(ID_DEFAULT), anyInt(), anyInt(), any()); + } + + @Test public void registerAuthenticators_virtualOnly() throws Exception { initServiceWith(NAME_DEFAULT, NAME_VIRTUAL); Settings.Secure.putInt(mSettingsRule.mockContentResolver(mContext), @@ -192,6 +240,19 @@ public class FingerprintServiceTest { } @Test + @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL) + public void registerAuthenticatorsLegacy_virtualOnly() throws Exception { + initServiceWith(NAME_DEFAULT, NAME_VIRTUAL); + Settings.Secure.putInt(mSettingsRule.mockContentResolver(mContext), + Settings.Secure.BIOMETRIC_VIRTUAL_ENABLED, 1); + + mService.mServiceWrapper.registerAuthenticatorsLegacy(mFingerprintSensorConfigurations); + waitForRegistration(); + + verify(mIBiometricService).registerAuthenticator(eq(ID_VIRTUAL), anyInt(), anyInt(), any()); + } + + @Test public void registerAuthenticators_virtualAlwaysWhenNoOther() throws Exception { initServiceWith(NAME_VIRTUAL); @@ -201,6 +262,28 @@ public class FingerprintServiceTest { verify(mIBiometricService).registerAuthenticator(eq(ID_VIRTUAL), anyInt(), anyInt(), any()); } + @Test + @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL) + public void registerAuthenticatorsLegacy_virtualAlwaysWhenNoOther() throws Exception { + mFingerprintSensorConfigurations = + new FingerprintSensorConfigurations(true); + mFingerprintSensorConfigurations.addAidlSensors(new String[]{NAME_VIRTUAL}, + (name) -> { + if (name.equals(IFingerprint.DESCRIPTOR + "/" + NAME_DEFAULT)) { + return mDefaultFingerprintDaemon; + } else if (name.equals(IFingerprint.DESCRIPTOR + "/" + NAME_VIRTUAL)) { + return mVirtualFingerprintDaemon; + } + return null; + }); + initServiceWith(NAME_VIRTUAL); + + mService.mServiceWrapper.registerAuthenticatorsLegacy(mFingerprintSensorConfigurations); + waitForRegistration(); + + verify(mIBiometricService).registerAuthenticator(eq(ID_VIRTUAL), anyInt(), anyInt(), any()); + } + private void waitForRegistration() throws Exception { final CountDownLatch latch = new CountDownLatch(1); mService.mServiceWrapper.addAuthenticatorsRegisteredCallback( diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java index 4cfb83fa1c69..bf5986c1d0f3 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProviderTest.java @@ -16,6 +16,9 @@ package com.android.server.biometrics.sensors.fingerprint.aidl; +import static android.os.UserHandle.USER_NULL; +import static android.os.UserHandle.USER_SYSTEM; + import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; @@ -34,17 +37,20 @@ import android.hardware.biometrics.fingerprint.IFingerprint; import android.hardware.biometrics.fingerprint.ISession; import android.hardware.biometrics.fingerprint.SensorLocation; import android.hardware.biometrics.fingerprint.SensorProps; +import android.hardware.fingerprint.HidlFingerprintSensorConfig; import android.os.RemoteException; -import android.os.UserHandle; import android.os.UserManager; import android.platform.test.annotations.Presubmit; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; +import com.android.server.biometrics.Flags; import com.android.server.biometrics.log.BiometricContext; import com.android.server.biometrics.sensors.AuthenticationStateListeners; -import com.android.server.biometrics.sensors.BaseClientMonitor; import com.android.server.biometrics.sensors.BiometricScheduler; import com.android.server.biometrics.sensors.BiometricStateCallback; import com.android.server.biometrics.sensors.HalClientMonitor; @@ -52,6 +58,7 @@ import com.android.server.biometrics.sensors.LockoutResetDispatcher; import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -64,6 +71,10 @@ public class FingerprintProviderTest { private static final String TAG = "FingerprintProviderTest"; + @Rule + public final CheckFlagsRule mCheckFlagsRule = + DeviceFlagsValueProvider.createCheckFlagsRule(); + @Mock private Context mContext; @Mock @@ -115,7 +126,8 @@ public class FingerprintProviderTest { mFingerprintProvider = new FingerprintProvider(mContext, mBiometricStateCallback, mAuthenticationStateListeners, mSensorProps, TAG, mLockoutResetDispatcher, mGestureAvailabilityDispatcher, mBiometricContext, - mDaemon); + mDaemon, false /* resetLockoutRequiresHardwareAuthToken */, + true /* testHalEnabled */); } @Test @@ -123,17 +135,42 @@ public class FingerprintProviderTest { waitForIdle(); for (SensorProps prop : mSensorProps) { - final BiometricScheduler scheduler = - mFingerprintProvider.mFingerprintSensors.get(prop.commonProps.sensorId) - .getScheduler(); - BaseClientMonitor currentClient = scheduler.getCurrentClient(); - - assertThat(currentClient).isInstanceOf(FingerprintInternalCleanupClient.class); - assertThat(currentClient.getSensorId()).isEqualTo(prop.commonProps.sensorId); - assertThat(currentClient.getTargetUserId()).isEqualTo(UserHandle.USER_SYSTEM); + final Sensor sensor = + mFingerprintProvider.mFingerprintSensors.get(prop.commonProps.sensorId); + assertThat(sensor.getLazySession().get().getUserId()).isEqualTo(USER_SYSTEM); } } + @Test + @RequiresFlagsEnabled(Flags.FLAG_DE_HIDL) + public void testAddingHidlSensors() { + when(mResources.getIntArray(anyInt())).thenReturn(new int[]{}); + when(mResources.getBoolean(anyInt())).thenReturn(false); + + final int fingerprintId = 0; + final int fingerprintStrength = 15; + final String config = String.format("%d:2:%d", fingerprintId, fingerprintStrength); + final HidlFingerprintSensorConfig fingerprintSensorConfig = + new HidlFingerprintSensorConfig(); + fingerprintSensorConfig.parse(config, mContext); + HidlFingerprintSensorConfig[] hidlFingerprintSensorConfigs = + new HidlFingerprintSensorConfig[]{fingerprintSensorConfig}; + mFingerprintProvider = new FingerprintProvider(mContext, + mBiometricStateCallback, mAuthenticationStateListeners, + hidlFingerprintSensorConfigs, TAG, mLockoutResetDispatcher, + mGestureAvailabilityDispatcher, mBiometricContext, mDaemon, + false /* resetLockoutRequiresHardwareAuthToken */, + true /* testHalEnabled */); + + assertThat(mFingerprintProvider.mFingerprintSensors.get(fingerprintId) + .getLazySession().get().getUserId()).isEqualTo(USER_NULL); + + waitForIdle(); + + assertThat(mFingerprintProvider.mFingerprintSensors.get(fingerprintId) + .getLazySession().get().getUserId()).isEqualTo(USER_SYSTEM); + } + @SuppressWarnings("rawtypes") @Test public void halServiceDied_resetsAllSchedulers() { diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java index 410260074fbd..126a05e12628 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/SensorTest.java @@ -96,7 +96,7 @@ public class SensorTest { private final TestLooper mLooper = new TestLooper(); private final LockoutCache mLockoutCache = new LockoutCache(); - private UserAwareBiometricScheduler mScheduler; + private BiometricScheduler mScheduler; private AidlResponseHandler mHalCallback; @Before @@ -164,7 +164,8 @@ public class SensorTest { final Sensor sensor = new Sensor("SensorTest", mFingerprintProvider, mContext, null /* handler */, internalProp, mLockoutResetDispatcher, mGestureAvailabilityDispatcher, mBiometricContext, mCurrentSession); - mScheduler = (UserAwareBiometricScheduler) sensor.getScheduler(); + sensor.init(mGestureAvailabilityDispatcher, mLockoutResetDispatcher); + mScheduler = sensor.getScheduler(); sensor.mCurrentSession = new AidlSession(0, mock(ISession.class), USER_ID, mHalCallback); diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapterTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapterTest.java new file mode 100644 index 000000000000..89a49615dbe1 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSensorAdapterTest.java @@ -0,0 +1,301 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.biometrics.sensors.fingerprint.hidl; + +import static android.hardware.fingerprint.FingerprintManager.ENROLL_ENROLL; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.AlarmManager; +import android.hardware.biometrics.IBiometricService; +import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint; +import android.hardware.fingerprint.Fingerprint; +import android.hardware.fingerprint.HidlFingerprintSensorConfig; +import android.os.Handler; +import android.os.RemoteException; +import android.os.test.TestLooper; +import android.platform.test.annotations.Presubmit; +import android.testing.TestableContext; + +import androidx.annotation.NonNull; +import androidx.test.core.app.ApplicationProvider; +import androidx.test.filters.SmallTest; + +import com.android.server.biometrics.log.BiometricContext; +import com.android.server.biometrics.log.BiometricLogger; +import com.android.server.biometrics.sensors.AuthSessionCoordinator; +import com.android.server.biometrics.sensors.AuthenticationStateListeners; +import com.android.server.biometrics.sensors.BiometricScheduler; +import com.android.server.biometrics.sensors.BiometricUtils; +import com.android.server.biometrics.sensors.ClientMonitorCallback; +import com.android.server.biometrics.sensors.LockoutResetDispatcher; +import com.android.server.biometrics.sensors.LockoutTracker; +import com.android.server.biometrics.sensors.StartUserClient; +import com.android.server.biometrics.sensors.StopUserClient; +import com.android.server.biometrics.sensors.UserAwareBiometricScheduler; +import com.android.server.biometrics.sensors.fingerprint.GestureAvailabilityDispatcher; +import com.android.server.biometrics.sensors.fingerprint.aidl.AidlResponseHandler; +import com.android.server.biometrics.sensors.fingerprint.aidl.AidlSession; +import com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintEnrollClient; +import com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintProvider; +import com.android.server.biometrics.sensors.fingerprint.aidl.FingerprintResetLockoutClient; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnit; +import org.mockito.junit.MockitoRule; + +@Presubmit +@SmallTest +public class HidlToAidlSensorAdapterTest { + + private static final String TAG = "HidlToAidlSensorAdapterTest"; + private static final int USER_ID = 2; + private static final int SENSOR_ID = 4; + private static final byte[] HAT = new byte[69]; + + @Rule + public final MockitoRule mMockito = MockitoJUnit.rule(); + + @Mock + private IBiometricService mBiometricService; + @Mock + private LockoutResetDispatcher mLockoutResetDispatcherForSensor; + @Mock + private LockoutResetDispatcher mLockoutResetDispatcherForClient; + @Mock + private BiometricLogger mLogger; + @Mock + private BiometricContext mBiometricContext; + @Mock + private AuthSessionCoordinator mAuthSessionCoordinator; + @Mock + private FingerprintProvider mFingerprintProvider; + @Mock + private GestureAvailabilityDispatcher mGestureAvailabilityDispatcher; + @Mock + private Runnable mInternalCleanupRunnable; + @Mock + private AlarmManager mAlarmManager; + @Mock + private IBiometricsFingerprint mDaemon; + @Mock + private AidlResponseHandler.AidlResponseHandlerCallback mAidlResponseHandlerCallback; + @Mock + private BiometricUtils<Fingerprint> mBiometricUtils; + @Mock + private AuthenticationStateListeners mAuthenticationStateListeners; + + private final TestLooper mLooper = new TestLooper(); + private HidlToAidlSensorAdapter mHidlToAidlSensorAdapter; + private final TestableContext mContext = new TestableContext( + ApplicationProvider.getApplicationContext()); + + private final UserAwareBiometricScheduler.UserSwitchCallback mUserSwitchCallback = + new UserAwareBiometricScheduler.UserSwitchCallback() { + @NonNull + @Override + public StopUserClient<?> getStopUserClient(int userId) { + return new StopUserClient<IBiometricsFingerprint>(mContext, + mHidlToAidlSensorAdapter::getIBiometricsFingerprint, null, USER_ID, + SENSOR_ID, mLogger, mBiometricContext, () -> {}) { + @Override + protected void startHalOperation() { + getCallback().onClientFinished(this, true /* success */); + } + + @Override + public void unableToStart() {} + }; + } + + @NonNull + @Override + public StartUserClient<?, ?> getStartUserClient(int newUserId) { + return new StartUserClient<IBiometricsFingerprint, AidlSession>(mContext, + mHidlToAidlSensorAdapter::getIBiometricsFingerprint, null, + USER_ID, SENSOR_ID, + mLogger, mBiometricContext, + (newUserId1, newUser, halInterfaceVersion) -> + mHidlToAidlSensorAdapter.handleUserChanged(newUserId1)) { + @Override + public void start(@NonNull ClientMonitorCallback callback) { + super.start(callback); + startHalOperation(); + } + + @Override + protected void startHalOperation() { + mUserStartedCallback.onUserStarted(USER_ID, null, 0); + getCallback().onClientFinished(this, true /* success */); + } + + @Override + public void unableToStart() {} + }; + } + };; + + @Before + public void setUp() throws RemoteException { + when(mBiometricContext.getAuthSessionCoordinator()).thenReturn(mAuthSessionCoordinator); + doAnswer((answer) -> { + mHidlToAidlSensorAdapter.getLazySession().get().getHalSessionCallback() + .onEnrollmentProgress(1 /* enrollmentId */, 0 /* remaining */); + return null; + }).when(mDaemon).enroll(any(), anyInt(), anyInt()); + + mContext.addMockSystemService(AlarmManager.class, mAlarmManager); + mContext.getOrCreateTestableResources(); + + final String config = String.format("%d:2:15", SENSOR_ID); + final UserAwareBiometricScheduler scheduler = new UserAwareBiometricScheduler(TAG, + new Handler(mLooper.getLooper()), + BiometricScheduler.SENSOR_TYPE_FP_OTHER, + null /* gestureAvailabilityDispatcher */, + mBiometricService, + () -> USER_ID, + mUserSwitchCallback); + final HidlFingerprintSensorConfig fingerprintSensorConfig = + new HidlFingerprintSensorConfig(); + fingerprintSensorConfig.parse(config, mContext); + mHidlToAidlSensorAdapter = new HidlToAidlSensorAdapter(TAG, + mFingerprintProvider, mContext, new Handler(mLooper.getLooper()), + fingerprintSensorConfig, mLockoutResetDispatcherForSensor, + mGestureAvailabilityDispatcher, mBiometricContext, + false /* resetLockoutRequiresHardwareAuthToken */, + mInternalCleanupRunnable, mAuthSessionCoordinator, mDaemon, + mAidlResponseHandlerCallback); + mHidlToAidlSensorAdapter.init(mGestureAvailabilityDispatcher, + mLockoutResetDispatcherForSensor); + mHidlToAidlSensorAdapter.setScheduler(scheduler); + mHidlToAidlSensorAdapter.handleUserChanged(USER_ID); + } + + @Test + public void lockoutTimedResetViaClient() { + setLockoutTimed(); + + mHidlToAidlSensorAdapter.getScheduler().scheduleClientMonitor( + new FingerprintResetLockoutClient(mContext, + mHidlToAidlSensorAdapter.getLazySession(), + USER_ID, TAG, SENSOR_ID, mLogger, mBiometricContext, HAT, + mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */), + mLockoutResetDispatcherForClient, 0 /* biometricStrength */)); + mLooper.dispatchAll(); + + verify(mAlarmManager).setExact(anyInt(), anyLong(), any()); + verify(mLockoutResetDispatcherForClient).notifyLockoutResetCallbacks(SENSOR_ID); + verify(mLockoutResetDispatcherForSensor).notifyLockoutResetCallbacks(SENSOR_ID); + assertThat(mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */) + .getLockoutModeForUser(USER_ID)).isEqualTo(LockoutTracker.LOCKOUT_NONE); + verify(mAuthSessionCoordinator).resetLockoutFor(eq(USER_ID), anyInt(), anyLong()); + } + + @Test + public void lockoutTimedResetViaCallback() { + setLockoutTimed(); + + mHidlToAidlSensorAdapter.getLazySession().get().getHalSessionCallback().onLockoutCleared(); + mLooper.dispatchAll(); + + verify(mLockoutResetDispatcherForSensor, times(2)).notifyLockoutResetCallbacks(eq( + SENSOR_ID)); + assertThat(mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */) + .getLockoutModeForUser(USER_ID)).isEqualTo(LockoutTracker.LOCKOUT_NONE); + verify(mAuthSessionCoordinator).resetLockoutFor(eq(USER_ID), anyInt(), anyLong()); + } + + @Test + public void lockoutPermanentResetViaCallback() { + setLockoutPermanent(); + + mHidlToAidlSensorAdapter.getLazySession().get().getHalSessionCallback().onLockoutCleared(); + mLooper.dispatchAll(); + + verify(mLockoutResetDispatcherForSensor, times(2)).notifyLockoutResetCallbacks(eq( + SENSOR_ID)); + assertThat(mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */) + .getLockoutModeForUser(USER_ID)).isEqualTo(LockoutTracker.LOCKOUT_NONE); + verify(mAuthSessionCoordinator).resetLockoutFor(eq(USER_ID), anyInt(), anyLong()); + } + + @Test + public void lockoutPermanentResetViaClient() { + setLockoutPermanent(); + + mHidlToAidlSensorAdapter.getScheduler().scheduleClientMonitor( + new FingerprintResetLockoutClient(mContext, + mHidlToAidlSensorAdapter.getLazySession(), + USER_ID, TAG, SENSOR_ID, mLogger, mBiometricContext, HAT, + mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */), + mLockoutResetDispatcherForClient, 0 /* biometricStrength */)); + mLooper.dispatchAll(); + + verify(mAlarmManager, atLeast(1)).setExact(anyInt(), anyLong(), any()); + verify(mLockoutResetDispatcherForClient).notifyLockoutResetCallbacks(SENSOR_ID); + verify(mLockoutResetDispatcherForSensor).notifyLockoutResetCallbacks(SENSOR_ID); + assertThat(mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */) + .getLockoutModeForUser(USER_ID)).isEqualTo(LockoutTracker.LOCKOUT_NONE); + verify(mAuthSessionCoordinator).resetLockoutFor(eq(USER_ID), anyInt(), anyLong()); + } + + @Test + public void verifyOnEnrollSuccessCallback() { + mHidlToAidlSensorAdapter.getScheduler().scheduleClientMonitor(new FingerprintEnrollClient( + mContext, mHidlToAidlSensorAdapter.getLazySession(), null /* token */, + 1 /* requestId */, null /* listener */, USER_ID, HAT, TAG, mBiometricUtils, + SENSOR_ID, mLogger, mBiometricContext, + mHidlToAidlSensorAdapter.getSensorProperties(), null, null, + mAuthenticationStateListeners, 5 /* maxTemplatesPerUser */, ENROLL_ENROLL)); + mLooper.dispatchAll(); + + verify(mAidlResponseHandlerCallback).onEnrollSuccess(); + } + + private void setLockoutPermanent() { + for (int i = 0; i < 20; i++) { + mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */) + .addFailedAttemptForUser(USER_ID); + } + + assertThat(mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */) + .getLockoutModeForUser(USER_ID)).isEqualTo(LockoutTracker.LOCKOUT_PERMANENT); + } + + private void setLockoutTimed() { + for (int i = 0; i < 5; i++) { + mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */) + .addFailedAttemptForUser(USER_ID); + } + + assertThat(mHidlToAidlSensorAdapter.getLockoutTracker(false /* forAuth */) + .getLockoutModeForUser(USER_ID)).isEqualTo(LockoutTracker.LOCKOUT_TIMED); + } +} diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/AidlToHidlAdapterTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSessionAdapterTest.java index b78ba82bd8fe..d723e87a62ad 100644 --- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/AidlToHidlAdapterTest.java +++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/hidl/HidlToAidlSessionAdapterTest.java @@ -41,7 +41,7 @@ import org.mockito.junit.MockitoRule; @Presubmit @SmallTest -public class AidlToHidlAdapterTest { +public class HidlToAidlSessionAdapterTest { @Rule public final MockitoRule mockito = MockitoJUnit.rule(); @@ -54,11 +54,11 @@ public class AidlToHidlAdapterTest { private final long mChallenge = 100L; private final int mUserId = 0; - private AidlToHidlAdapter mAidlToHidlAdapter; + private HidlToAidlSessionAdapter mHidlToAidlSessionAdapter; @Before public void setUp() { - mAidlToHidlAdapter = new AidlToHidlAdapter(() -> mSession, mUserId, + mHidlToAidlSessionAdapter = new HidlToAidlSessionAdapter(() -> mSession, mUserId, mAidlResponseHandler); mHardwareAuthToken.timestamp = new Timestamp(); mHardwareAuthToken.mac = new byte[10]; @@ -67,7 +67,7 @@ public class AidlToHidlAdapterTest { @Test public void testGenerateChallenge() throws RemoteException { when(mSession.preEnroll()).thenReturn(mChallenge); - mAidlToHidlAdapter.generateChallenge(); + mHidlToAidlSessionAdapter.generateChallenge(); verify(mSession).preEnroll(); verify(mAidlResponseHandler).onChallengeGenerated(mChallenge); @@ -75,7 +75,7 @@ public class AidlToHidlAdapterTest { @Test public void testRevokeChallenge() throws RemoteException { - mAidlToHidlAdapter.revokeChallenge(mChallenge); + mHidlToAidlSessionAdapter.revokeChallenge(mChallenge); verify(mSession).postEnroll(); verify(mAidlResponseHandler).onChallengeRevoked(0L); @@ -84,9 +84,9 @@ public class AidlToHidlAdapterTest { @Test public void testEnroll() throws RemoteException { final ICancellationSignal cancellationSignal = - mAidlToHidlAdapter.enroll(mHardwareAuthToken); + mHidlToAidlSessionAdapter.enroll(mHardwareAuthToken); - verify(mSession).enroll(any(), anyInt(), eq(AidlToHidlAdapter.ENROLL_TIMEOUT_SEC)); + verify(mSession).enroll(any(), anyInt(), eq(HidlToAidlSessionAdapter.ENROLL_TIMEOUT_SEC)); cancellationSignal.cancel(); @@ -96,7 +96,8 @@ public class AidlToHidlAdapterTest { @Test public void testAuthenticate() throws RemoteException { final int operationId = 2; - final ICancellationSignal cancellationSignal = mAidlToHidlAdapter.authenticate(operationId); + final ICancellationSignal cancellationSignal = mHidlToAidlSessionAdapter.authenticate( + operationId); verify(mSession).authenticate(operationId, mUserId); @@ -107,7 +108,8 @@ public class AidlToHidlAdapterTest { @Test public void testDetectInteraction() throws RemoteException { - final ICancellationSignal cancellationSignal = mAidlToHidlAdapter.detectInteraction(); + final ICancellationSignal cancellationSignal = mHidlToAidlSessionAdapter + .detectInteraction(); verify(mSession).authenticate(0 /* operationId */, mUserId); @@ -118,7 +120,7 @@ public class AidlToHidlAdapterTest { @Test public void testEnumerateEnrollments() throws RemoteException { - mAidlToHidlAdapter.enumerateEnrollments(); + mHidlToAidlSessionAdapter.enumerateEnrollments(); verify(mSession).enumerate(); } @@ -126,7 +128,7 @@ public class AidlToHidlAdapterTest { @Test public void testRemoveEnrollment() throws RemoteException { final int[] enrollmentIds = new int[]{1}; - mAidlToHidlAdapter.removeEnrollments(enrollmentIds); + mHidlToAidlSessionAdapter.removeEnrollments(enrollmentIds); verify(mSession).remove(mUserId, enrollmentIds[0]); } @@ -134,14 +136,14 @@ public class AidlToHidlAdapterTest { @Test public void testRemoveMultipleEnrollments() throws RemoteException { final int[] enrollmentIds = new int[]{1, 2}; - mAidlToHidlAdapter.removeEnrollments(enrollmentIds); + mHidlToAidlSessionAdapter.removeEnrollments(enrollmentIds); verify(mSession).remove(mUserId, 0); } @Test public void testResetLockout() throws RemoteException { - mAidlToHidlAdapter.resetLockout(mHardwareAuthToken); + mHidlToAidlSessionAdapter.resetLockout(mHardwareAuthToken); verify(mAidlResponseHandler).onLockoutCleared(); } |