diff options
Diffstat (limited to 'biometrics')
-rw-r--r-- | biometrics/face/1.0/Android.bp | 28 | ||||
-rw-r--r-- | biometrics/face/1.0/IBiometricsFace.hal | 251 | ||||
-rw-r--r-- | biometrics/face/1.0/IBiometricsFaceClientCallback.hal | 131 | ||||
-rw-r--r-- | biometrics/face/1.0/types.hal | 372 | ||||
-rw-r--r-- | biometrics/face/1.0/vts/functional/Android.bp | 24 | ||||
-rw-r--r-- | biometrics/face/1.0/vts/functional/VtsHalBiometricsFaceV1_0TargetTest.cpp | 412 | ||||
-rw-r--r-- | biometrics/fingerprint/2.1/default/android.hardware.biometrics.fingerprint@2.1-service.rc | 4 |
7 files changed, 1220 insertions, 2 deletions
diff --git a/biometrics/face/1.0/Android.bp b/biometrics/face/1.0/Android.bp new file mode 100644 index 000000000..0f8c6e646 --- /dev/null +++ b/biometrics/face/1.0/Android.bp @@ -0,0 +1,28 @@ +// This file is autogenerated by hidl-gen -Landroidbp. + +hidl_interface { + name: "android.hardware.biometrics.face@1.0", + root: "android.hardware", + vndk: { + enabled: true, + }, + srcs: [ + "types.hal", + "IBiometricsFace.hal", + "IBiometricsFaceClientCallback.hal", + ], + interfaces: [ + "android.hidl.base@1.0", + ], + types: [ + "FaceAcquiredInfo", + "FaceError", + "Feature", + "OptionalBool", + "OptionalUint64", + "Status", + "UserHandle", + ], + gen_java: true, +} + diff --git a/biometrics/face/1.0/IBiometricsFace.hal b/biometrics/face/1.0/IBiometricsFace.hal new file mode 100644 index 000000000..0499c5d0f --- /dev/null +++ b/biometrics/face/1.0/IBiometricsFace.hal @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2018 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.biometrics.face@1.0; + +import IBiometricsFaceClientCallback; + +/** + * The HAL interface for biometric face authentication. + */ +interface IBiometricsFace { + + /** + * Sets the current client callback. + * + * Registers a user function that must receive notifications from the HAL. + * There is usually only one client (FaceService). This call must block + * if the HAL state machine is in busy state until the HAL leaves the + * busy state. + * + * All callback methods pass a deviceId to differentiate callback + * invocations in the case where multiple sensors exist. + * + * @param clientCallback The client defined callback to register. + * @return result, with its "value" parameter representing a "deviceId", + * which must be unique for a given sensor. + */ + @callflow(next={"setActiveUser"}) + @entry + setCallback(IBiometricsFaceClientCallback clientCallback) + generates (OptionalUint64 result); + + /** + * Sets the active user, which all subsequent HAL operations are applied to. + * + * HAL service implementors must ensure that operations are restricted to + * the given user. Clients must not call any part of this interface, except + * for setCallback(), without first having set an active user. The + * implementation is responsible for cancelling the current operation and + * returning to the idle state. Calling this method with the same userId + * should have no effect on the state machine. + * + * @param userId A non-negative user identifier that must be unique and + * persistent for a given user. + * @param storePath filesystem path to the template storage directory. + */ + @callflow(next={"authenticate", "generateChallenge", "enumerate", "remove"}) + setActiveUser(int32_t userId, string storePath) generates (Status status); + + /** + * Begins a secure transaction request, e.g. enrollment. + * + * Generates a unique and cryptographically secure random token used to + * indicate the start of a secure transaction. generateChallenge() and + * revokeChallenge() specify a pin/pattern/password cleared time window where + * the secure transaction is allowed. + * + * generateChallenge() generates a challenge which must then be wrapped by the + * gatekeeper after verifying a successful strong authentication attempt, + * which generates a Hardware Authentication Token. The challenge prevents + * spoofing and replay attacks and ensures that we only update a user’s face + * template if the operation was preceded by some kind of strong credential + * confirmation (e.g. device password). + * + * @param challengeTimeoutSec A timeout in seconds, after which the driver + * must invalidate the challenge. This is to prevent bugs or crashes in + * the system from leaving a challenge enabled indefinitely. + * @return result, with its "value" parameter representing a "challenge": a + * unique and cryptographically secure random token. + */ + @callflow(next={"enroll", "revokeChallenge", "setFeatureDisabled"}) + generateChallenge(uint32_t challengeTimeoutSec) + generates (OptionalUint64 result); + + /** + * Enrolls a user's face. + * + * Note that this interface permits implementations where multiple faces can + * be enrolled for a single user. However, allowing multiple faces to be + * enrolled can be a severe security vulnerability and hence, most + * implementations must ensure that only a single face be enrolled at a + * given time. Multi-enrollment must only be used where there is a clear + * necessity for a shared use case, e.g. TVs or cars. + * + * Note that the Hardware Authentication Token must still be valid after + * this call, and must be explicitly invalidated by a call to + * revokeChallenge(). This allows clients to immediately reattempt + * enrollment (for example, if a user wasn’t satisfied with their enrollment) + * without having to go through another strong authentication flow. + * + * This method triggers the IBiometricsFaceClientCallback#onEnrollResult() + * method. + * + * @param hat A valid Hardware Authentication Token, generated as a result + * of a generateChallenge() challenge being wrapped by the gatekeeper + * after a sucessful strong authentication request. + * @param timeoutSec A timeout in seconds, after which this enrollment + * attempt is cancelled. Note that the client still needs to + * call revokeChallenge() to terminate the enrollment session. + * @param disabledFeatures A list of features to be disabled during + * enrollment. Note that all features are enabled by default. + * @return status The status of this method call. + */ + @callflow(next={"cancel", "enroll", "revokeChallenge", "remove"}) + enroll(vec<uint8_t> hat, uint32_t timeoutSec, vec<Feature> disabledFeatures) + generates (Status status); + + /** + * Finishes the secure transaction by invalidating the challenge generated + * by generateChallenge(). + * + * Clients must call this method once enrollment is complete, and the user's + * face template no longer needs to be updated. + * + * @return status The status of this method call. + */ + @callflow(next={"authenticate", "setActiveUser", "enumerate", "remove"}) + revokeChallenge() generates (Status status); + + /** + * Requires all subsequent enroll/authenticate calls to use the feature. + * This method does not affect enroll, which has its own feature list. + * + * Changes the state of previous enrollment setting. Because this may + * decrease security, the user must enter their password before this method + * is invoked (see @param HAT). The driver must verify the HAT before + * changing any feature state. + * Note: In some cases it may not be possible to change the state of this + * flag without re-enrolling. For example, if the user didn't provide + * attention during the original enrollment. This flag reflects the same + * persistent state as the one passed to enroll(). + * + * @param feature The feature to be enabled or disabled. + * @param enabled True to enable the feature, false to disable. + * @param hat A valid Hardware Authentication Token, generated as a result + * of getChallenge(). + * @return status The status of this method call. + */ + setFeature(Feature feature, bool enabled, vec<uint8_t> hat) + generates(Status status); + + /** + * Retrieves the current state of the feature. + * + * @return enabled True if the feature is enabled, false if disabled. + */ + getFeature(Feature feature) generates (bool enabled); + + /** + * Returns an identifier associated with the current face set. + * + * The authenticator ID must change whenever a new face is enrolled. The + * authenticator ID must not be changed when a face is deleted. The + * authenticator ID must be an entropy-encoded random number which all + * current templates are tied to. The authenticator ID must be immutable + * outside of an active enrollment window to prevent replay attacks. + * + * @return result, with its value parameter representing an + * "authenticatorId": an identifier associated to the user's current + * face enrollment. + */ + @callflow(next={"authenticate"}) + getAuthenticatorId() generates (OptionalUint64 result); + + /** + * Cancels a pending enrollment or authentication request. + * + * @return status The status of this method call. + */ + @callflow(next={"authenticate", "enroll", "enumerate", "remove", + "setActiveUser"}) + cancel() generates (Status status); + + /** + * Enumerates all face templates associated with the active user. + * + * The onEnumerate() callback method is invoked once for each face template + * found. + * + * @return status The status of this method call. + */ + @callflow(next={"remove", "enroll", "authenticate", "setActiveUser"}) + enumerate() generates (Status status); + + /** + * Removes a face template or all face templates associated with the active + * user. + * + * This method triggers the IBiometricsFaceClientCallback#onRemoved() method. + * + * @param faceId The id correpsonding to the face to be removed; or 0 if all + * faces are to be removed. + * @return status The status of this method call. + */ + @callflow(next={"enumerate", "authenticate", "cancel", "getAuthenticatorId", + "setActiveUser"}) + remove(uint32_t faceId) generates (Status status); + + /** + * Authenticates the active user. + * + * An optional operationId can be specified as a token from the transaction + * being authorized. The hardware may enter a standby state during + * authentication, where the device is idle to conserve power while + * authenticating, e.g. after 3 seconds without finding a face. See + * IBiometricsFace#userActivity() for more info. + * + * @param operationId A non-zero operation id associated with a crypto + * object instance; or 0 if not being used. + * @return status The status of this method call. + */ + @callflow(next={"cancel", "generateChallenge", "remove"}) + authenticate(uint64_t operationId) generates (Status status); + + /** + * A hint to the HAL to continue looking for faces. + * + * This method should only be used when the HAL is in the authenticating + * or standby state. Using this method when the HAL is not in one of the + * mentioned states must return OPERATION_NOT_SUPPORTED. Calling this + * method while the HAL is already authenticating may extend the duration + * where it's looking for a face. + * + * @return status The status of this method call. + */ + userActivity() generates (Status status); + + /** + * Reset lockout for the current user. + * + * @param hat A valid Hardware Authentication Token, generated when the + * user authenticates with Pin/Pattern/Pass. When the Hardware + * Authentication Token is verified, lockout must be reset and + * onLockoutChanged must be called with duration 0. + * @return status The status of this method call. + */ + resetLockout(vec<uint8_t> hat) generates (Status status); +}; diff --git a/biometrics/face/1.0/IBiometricsFaceClientCallback.hal b/biometrics/face/1.0/IBiometricsFaceClientCallback.hal new file mode 100644 index 000000000..c9dd0e262 --- /dev/null +++ b/biometrics/face/1.0/IBiometricsFaceClientCallback.hal @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2018 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.biometrics.face@1.0; + +/** + * This callback interface is used by clients to recieve updates from the face + * HAL. + */ +interface IBiometricsFaceClientCallback { + + /** + * A callback invoked when one enrollment step has been completed. + * + * @param deviceId A unique id associated with the HAL implementation + * service that processed this enrollment step. + * @param faceId The id of the face template being enrolled. + * @param userId The active user id for the template being enrolled. + * @param remaining The number of remaining steps before enrolllment is + * complete or 0 if enrollment has completed successfully. + */ + oneway onEnrollResult(uint64_t deviceId, uint32_t faceId, int32_t userId, + uint32_t remaining); + + /** + * A callback invoked when a face has been successfully authenticated. + * + * @param deviceId A unique id associated with the HAL implementation + * service that processed this autentication attempt. + * @param faceId The id of the face template that passed the authentication + * challenge. + * @param userId The active user id for the authenticated face. + * @param token The hardware authentication token associated with this + * authenticate operation. + */ + oneway onAuthenticated(uint64_t deviceId, uint32_t faceId, int32_t userId, + vec<uint8_t> token); + + /** + * A callback invoked when a face is acquired. + * + * If a non-critical, recoverable error occurs during an enrollment or + * authentication attempt, the HAL implementation must invoke this callback + * to allow clients to inform the user that some actionable change must be + * made. + * + * @param deviceId A unique id associated with the HAL implementation + * service that acquired a face. + * @param userId The id of the active user associated with the attempted + * face acquisition. + * @param acquiredInfo A message about the quality of the acquired image. + * @param vendorCode An optional vendor-specific message. This is only valid + * when acquiredInfo == FaceAcquiredInfo.VENDOR. This message is opaque + * to the framework, and vendors must provide code to handle it. For + * example this can be used to guide enrollment in Settings or provide + * a message during authentication that is vendor-specific. The vendor + * is expected to provide help strings to cover all known values. + */ + oneway onAcquired(uint64_t deviceId, int32_t userId, + FaceAcquiredInfo acquiredInfo, int32_t vendorCode); + + /** + * A callback invoked when an error has occured. + * + * @param deviceId A unique id associated with the HAL implementation + * service where this error occured. + * @param userId The id of the active user when the error occured, or + * UserHandle::NONE if an active user had not been set yet. + * @param error A message about the error that occurred. + * @param vendorCode An optional, vendor-speicifc error message. Only valid + * when error == FaceError.VENDOR. This message is opaque to the + * framework, and vendors must provide code to handle it. For example, + * this scan be used to show the user an error message specific to the + * device. The vendor is expected to provide error strings to cover + * all known values. + */ + oneway onError(uint64_t deviceId, int32_t userId, FaceError error, + int32_t vendorCode); + + /** + * A callback invoked when a face template has been removed. + * + * @param deviceId A unique id associated with the HAL implementation + * service that processed this removal. + * @param faceId The id of the face template that was removed. + * @param userId The active user id for the removed face template. + * @param remaining The number of face templates remaining after this + * removal, or 0 if there are no more. + */ + oneway onRemoved(uint64_t deviceId, uint32_t faceId, int32_t userId, + uint32_t remaining); + + /** + * A callback invoked to enumerate all current face templates. + * + * @param deviceId A unique id associated with the HAL implementation + * service that processed this enumeration. + * @param faceIds A list of ids of all currently enrolled face templates. + * @param userId The active user id for the enumerated face template. + */ + oneway onEnumerate(uint64_t deviceId, vec<uint32_t> faceIds, + int32_t userId); + + /** + * A callback invoked when the lockout state changes. + * + * This method must only be invoked when setActiveUser() is called, + * when lockout starts, and when lockout ends. When lockout starts, + * duration must be greater than 0, and when lockout ends, duration must + * be 0. This must be called before calling onError() with parameters + * LOCKOUT or LOCKOUT_PERMANENT. If the user is permanently locked out, + * the duration must be MAX_UINT64. + * + * @param duration the remaining lockout duration in milliseconds, or 0 + * if the user is not locked out. + */ + oneway onLockoutChanged(uint64_t duration); +}; diff --git a/biometrics/face/1.0/types.hal b/biometrics/face/1.0/types.hal new file mode 100644 index 000000000..b5db966b4 --- /dev/null +++ b/biometrics/face/1.0/types.hal @@ -0,0 +1,372 @@ +/* + * Copyright (C) 2018 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.biometrics.face@1.0; + +/* + * In the event setActiveUser is not called, all error messages will return + * this userId. + */ +enum UserHandle : int32_t { + NONE = -1 +}; + +/** + * Status codes returned directly by the HIDL method calls upon critical errors + * where the callback cannot be invoked. Most errors should sent through the + * onError callback using one of the FaceErrors below. + */ +enum Status : uint32_t { + /** + * The method was invoked successfully. + */ + OK = 0, + + /** + * One of the arguments to the method call is invalid. + */ + ILLEGAL_ARGUMENT = 1, + + /** + * This face HAL does not support this operation. + */ + OPERATION_NOT_SUPPORTED = 2, + + /** + * The HAL has encountered an internal error and cannot complete the request. + */ + INTERNAL_ERROR = 3, + + /** + * The operation could not be completed because there are no enrolled + * templates. + */ + NOT_ENROLLED = 4 +}; + +enum Feature : uint32_t { + /** + * Require the user to look at the device during enrollment and + * authentication. Note this is to accommodate people who have limited + * vision. Must be enabled by default. + */ + REQUIRE_ATTENTION = 1, + + /** + * Require a diverse set of poses during enrollment. Note this is to + * accommodate people with limited mobility. Must be enabled by default. + */ + REQUIRE_DIVERSITY = 2 +}; + +/** + * Face errors represent events that can't be immediately recovered by user + * intervention. These are returned in the onError callback. + * + * Upon receiving a face error, clients must terminate the current operation and + * notify the user where possible. + */ +enum FaceError : int32_t { + + /** + * A hardware error has occured that cannot be resolved. Try again later. + */ + HW_UNAVAILABLE = 1, + + /** + * The current enroll or authenticate operation could not be completed; + * the sensor was unable to process the current image. + */ + UNABLE_TO_PROCESS = 2, + + /** + * The current operation took too long to complete. This is intended to + * prevent programs from blocking the face HAL indefinitely. The timeout is + * framework and sensor-specific, but is generally on the order of 30 + * seconds. + */ + TIMEOUT = 3, + + /** + * The current operation could not be completed because there is not enough + * storage space remaining to do so. + */ + NO_SPACE = 4, + + /** + * The current operation has been cancelled. This may happen if a new + * request (authenticate, remove) is initiated while an on-going operation + * is in progress, or if cancel() was called. + */ + CANCELED = 5, + + /** + * The current remove operation could not be completed; the face template + * provided could not be removed. + */ + UNABLE_TO_REMOVE = 6, + + /** + * Face authentication is locked out due to too many unsuccessful attempts. + * This is a "soft" lockout, and authentication can be restarted after + * a period of time, generally on the order of 30 seconds. + */ + LOCKOUT = 7, + + /** + * Used to enable a vendor-specific error message. + */ + VENDOR = 8, + + /** + * Face authentication is disabled until the user unlocks with strong + * authentication (PIN/Pattern/Password). + */ + LOCKOUT_PERMANENT = 9 +}; + +/** + * Face acquisition information provides feedback for the current enrollment + * or authentication operation. + * + * This information indicates that the user can take immediate action to resolve + * an issue, and clients must ensure that this information is surfaced to the + * user. + */ +enum FaceAcquiredInfo : int32_t { + + /** + * The face acquired was good; no further user interaction is necessary. + */ + GOOD = 0, + + /** + * The face data acquired was too noisy or did not have sufficient detail. + * This is a catch-all for all acquisition errors not captured by the other + * constants. + */ + INSUFFICIENT = 1, + + /** + * Because there was too much ambient light, the captured face data was too + * bright. It's reasonable to return this after multiple + * FaceAcquiredInfo.INSUFFICIENT. + * + * The user is expected to take action to retry the operation in better + * lighting conditions when this is returned. + */ + TOO_BRIGHT = 2, + + /** + * Because there was not enough illumination, the captured face data was too + * dark. It's reasonable to return this after multiple + * FaceAcquiredInfo.INSUFFICIENT. + * + * The user is expected to take action to retry the operation in better + * lighting conditions when this is returned. + */ + TOO_DARK = 3, + + /** + * The detected face is too close to the sensor, and the image cannot be + * processed. + * + * The user is expected to be informed to move further from the sensor when + * this is returned. + */ + TOO_CLOSE = 4, + + /** + * The detected face is too small, as the user might be too far away from + * the sensor. + * + * The user is expected to be informed to move closer to the sensor when + * this is returned. + */ + TOO_FAR = 5, + + /** + * Only the upper part of the face was detected. The sensor's field of view + * is too high. + * + * The user should be informed to move up with respect to the sensor when + * this is returned. + */ + FACE_TOO_HIGH = 6, + + /** + * Only the lower part of the face was detected. The sensor's field of view + * is too low. + * + * The user should be informed to move down with respect to the sensor when + * this is returned. + */ + FACE_TOO_LOW = 7, + + /** + * Only the right part of the face was detected. The sensor's field of view + * is too far right. + * + * The user should be informed to move to the right with respect to the + * sensor when this is returned. + */ + FACE_TOO_RIGHT = 8, + + /** + * Only the left part of the face was detected. The sensor's field of view + * is too far left. + * + * The user should be informed to move to the left with respect to the + * sensor when this is returned. + */ + FACE_TOO_LEFT = 9, + + /** + * The user's eyes have strayed away from the sensor. If this message is + * sent, the user should be informed to look at the device. If the user + * can't be found in the frame, one of the other acquisition messages + * must be sent, e.g. NOT_DETECTED. + */ + POOR_GAZE = 10, + + /** + * No face was detected within the sensor's field of view. + * + * The user should be informed to point the sensor to a face when this is + * returned. + */ + NOT_DETECTED = 11, + + /** + * Too much motion was detected. + * + * The user should be informed to keep their face steady relative to the + * sensor. + */ + TOO_MUCH_MOTION = 12, + + /** + * The sensor needs to be re-calibrated. This is an unexpected condition, + * and must only be sent if a serious, uncorrectable, and unrecoverable + * calibration issue is detected which requires user intervention, e.g. + * re-enrolling. The expected response to this message is to direct the + * user to re-enroll. + */ + RECALIBRATE = 13, + + /** + * The face is too different from a previous acquisition. This condition + * only applies to enrollment. This can happen if the user passes the + * device to someone else in the middle of enrollment. + */ + TOO_DIFFERENT = 14, + + /** + * The face is too similar to a previous acquisition. This condition only + * applies to enrollment. The user should change their pose. + */ + TOO_SIMILAR = 15, + + /** + * The magnitude of the pan angle of the user’s face with respect to the sensor’s + * capture plane is too high. + * + * The pan angle is defined as the angle swept out by the user’s face turning + * their neck left and right. The pan angle would be zero if the user faced the + * camera directly. + * + * The user should be informed to look more directly at the camera. + */ + PAN_TOO_EXTREME = 16, + + /** + * The magnitude of the tilt angle of the user’s face with respect to the sensor’s + * capture plane is too high. + * + * The tilt angle is defined as the angle swept out by the user’s face looking up + * and down. The tilt angle would be zero if the user faced the camera directly. + * + * The user should be informed to look more directly at the camera. + */ + TILT_TOO_EXTREME = 17, + + /** + * The magnitude of the roll angle of the user’s face with respect to the sensor’s + * capture plane is too high. + * + * The roll angle is defined as the angle swept out by the user’s face tilting their head + * towards their shoulders to the left and right. The roll angle would be zero if the user's + * head is vertically aligned with the camera. + * + * The user should be informed to look more directly at the camera. + */ + ROLL_TOO_EXTREME = 18, + + /** + * The user’s face has been obscured by some object. + * + * The user should be informed to remove any objects from the line of sight from + * the sensor to the user’s face. + */ + FACE_OBSCURED = 19, + + /** + * This message represents the earliest message sent at the beginning of the authentication + * pipeline. It is expected to be used to measure latency. For example, in a camera-based + * authentication system it's expected to be sent prior to camera initialization. Note this + * should be sent whenever authentication is restarted (see IBiometricsFace#userActivity). + * The framework will measure latency based on the time between the last START message and the + * onAuthenticated callback. + */ + START = 20, + + /** + * Used to enable a vendor-specific acquisition message. + */ + VENDOR = 21 +}; + +/** + * Result structure with an additional uint64_t field. See documentation in + * setCallback(), preEnroll(), and getAuthenticatorId() for usage of the value. + */ +struct OptionalUint64 { + /** + * The return status. + */ + Status status; + + /** + * This value is only meaningful if status is OK. + */ + uint64_t value; +}; + +/** + * Result structure with an addition bool field. See documentation in + * getRequireAttention() for usage of the value. + */ +struct OptionalBool { + /** + * The return status. + */ + Status status; + + /** + * This value is only meaningful if status is OK. + */ + bool value; +};
\ No newline at end of file diff --git a/biometrics/face/1.0/vts/functional/Android.bp b/biometrics/face/1.0/vts/functional/Android.bp new file mode 100644 index 000000000..fa68c4e8d --- /dev/null +++ b/biometrics/face/1.0/vts/functional/Android.bp @@ -0,0 +1,24 @@ +// +// Copyright (C) 2019 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. +// + +cc_test { + name: "VtsHalBiometricsFaceV1_0TargetTest", + defaults: ["VtsHalTargetTestDefaults"], + srcs: ["VtsHalBiometricsFaceV1_0TargetTest.cpp"], + static_libs: ["android.hardware.biometrics.face@1.0"], + test_suites: ["general-tests"], +} + diff --git a/biometrics/face/1.0/vts/functional/VtsHalBiometricsFaceV1_0TargetTest.cpp b/biometrics/face/1.0/vts/functional/VtsHalBiometricsFaceV1_0TargetTest.cpp new file mode 100644 index 000000000..f496bbe64 --- /dev/null +++ b/biometrics/face/1.0/vts/functional/VtsHalBiometricsFaceV1_0TargetTest.cpp @@ -0,0 +1,412 @@ +/* + * Copyright (C) 2019 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. + */ + +#define LOG_TAG "face_hidl_test" + +#include <VtsHalHidlTargetTestBase.h> +#include <VtsHalHidlTargetTestEnvBase.h> +#include <android-base/logging.h> +#include <android-base/properties.h> +#include <android/hardware/biometrics/face/1.0/IBiometricsFace.h> +#include <android/hardware/biometrics/face/1.0/IBiometricsFaceClientCallback.h> +#include <hidl/HidlSupport.h> +#include <hidl/HidlTransportSupport.h> +#include <utils/Condition.h> + +#include <cinttypes> +#include <cstdint> +#include <future> +#include <utility> + +using android::Condition; +using android::Mutex; +using android::sp; +using android::base::GetUintProperty; +using android::hardware::hidl_vec; +using android::hardware::Return; +using android::hardware::biometrics::face::V1_0::FaceAcquiredInfo; +using android::hardware::biometrics::face::V1_0::FaceError; +using android::hardware::biometrics::face::V1_0::Feature; +using android::hardware::biometrics::face::V1_0::IBiometricsFace; +using android::hardware::biometrics::face::V1_0::IBiometricsFaceClientCallback; +using android::hardware::biometrics::face::V1_0::OptionalUint64; +using android::hardware::biometrics::face::V1_0::Status; + +namespace { + +const uint32_t kTimeout = 3; +const std::chrono::seconds kTimeoutInSeconds = std::chrono::seconds(kTimeout); +const uint32_t kUserId = 99; +const uint32_t kFaceId = 5; +const char kTmpDir[] = "/data/system/users/0/facedata"; +const int kIterations = 1000; + +const auto kAssertCallbackIsSet = [](const OptionalUint64& res) { + ASSERT_EQ(Status::OK, res.status); + // Makes sure the "deviceId" represented by "res.value" is not 0. + // 0 would mean the HIDL is not available. + ASSERT_NE(0UL, res.value); +}; + +// Wait for a callback to occur (signaled by the given future) up to the +// provided timeout. If the future is invalid or the callback does not come +// within the given time, returns false. +template <class ReturnType> +bool waitForCallback(std::future<ReturnType> future, + std::chrono::milliseconds timeout = kTimeoutInSeconds) { + auto expiration = std::chrono::system_clock::now() + timeout; + EXPECT_TRUE(future.valid()); + if (future.valid()) { + std::future_status status = future.wait_until(expiration); + EXPECT_NE(std::future_status::timeout, status) << "Timed out waiting for callback"; + if (status == std::future_status::ready) { + return true; + } + } + return false; +} + +// Base callback implementation that just logs all callbacks by default +class FaceCallbackBase : public IBiometricsFaceClientCallback { + public: + Return<void> onEnrollResult(uint64_t, uint32_t, int32_t, uint32_t) override { + ALOGD("Enroll callback called."); + return Return<void>(); + } + + Return<void> onAuthenticated(uint64_t, uint32_t, int32_t, const hidl_vec<uint8_t>&) override { + ALOGD("Authenticated callback called."); + return Return<void>(); + } + + Return<void> onAcquired(uint64_t, int32_t, FaceAcquiredInfo, int32_t) override { + ALOGD("Acquired callback called."); + return Return<void>(); + } + + Return<void> onError(uint64_t, int32_t, FaceError, int32_t) override { + ALOGD("Error callback called."); + EXPECT_TRUE(false); // fail any test that triggers an error + return Return<void>(); + } + + Return<void> onRemoved(uint64_t, uint32_t, int32_t, uint32_t) override { + ALOGD("Removed callback called."); + return Return<void>(); + } + + Return<void> onEnumerate(uint64_t, const hidl_vec<uint32_t>&, int32_t /* userId */) override { + ALOGD("Enumerate callback called."); + return Return<void>(); + } + + Return<void> onLockoutChanged(uint64_t) override { + ALOGD("LockoutChanged callback called."); + return Return<void>(); + } +}; + +class EnumerateCallback : public FaceCallbackBase { + public: + Return<void> onEnumerate(uint64_t, const hidl_vec<uint32_t>&, int32_t) override { + promise.set_value(); + return Return<void>(); + } + + std::promise<void> promise; +}; + +class ErrorCallback : public FaceCallbackBase { + public: + ErrorCallback(bool filterErrors = false, FaceError errorType = FaceError::HW_UNAVAILABLE) + : filterErrors(filterErrors), errorType(errorType), hasError(false) {} + + Return<void> onError(uint64_t, int32_t, FaceError error, int32_t) override { + if ((filterErrors && errorType == error) || !filterErrors) { + hasError = true; + this->error = error; + promise.set_value(); + } + return Return<void>(); + } + + bool filterErrors; + FaceError errorType; + bool hasError; + FaceError error; + std::promise<void> promise; +}; + +class RemoveCallback : public FaceCallbackBase { + public: + explicit RemoveCallback(int32_t userId) : removeUserId(userId) {} + + Return<void> onRemoved(uint64_t, uint32_t, int32_t userId, uint32_t remaining) override { + EXPECT_EQ(removeUserId, userId); + promise.set_value(); + if (remaining == 0UL) { + promise.set_value(); + } + return Return<void>(); + } + + int32_t removeUserId; + std::promise<void> promise; +}; + +// Test environment for Face HIDL HAL. +class FaceHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase { + public: + // get the test environment singleton + static FaceHidlEnvironment* Instance() { + static FaceHidlEnvironment* instance = new FaceHidlEnvironment; + return instance; + } + + void registerTestServices() override { registerTestService<IBiometricsFace>(); } +}; + +class FaceHidlTest : public ::testing::VtsHalHidlTargetTestBase { + public: + void SetUp() override { + mService = ::testing::VtsHalHidlTargetTestBase::getService<IBiometricsFace>( + FaceHidlEnvironment::Instance()->getServiceName<IBiometricsFace>()); + ASSERT_FALSE(mService == nullptr); + Return<Status> res = mService->setActiveUser(kUserId, kTmpDir); + ASSERT_EQ(Status::OK, static_cast<Status>(res)); + } + + void TearDown() override {} + + sp<IBiometricsFace> mService; +}; + +// The service should be reachable. +TEST_F(FaceHidlTest, ConnectTest) { + sp<FaceCallbackBase> cb = new FaceCallbackBase(); + mService->setCallback(cb, kAssertCallbackIsSet); +} + +// Starting the service with null callback should succeed. +TEST_F(FaceHidlTest, ConnectNullTest) { + mService->setCallback(nullptr, kAssertCallbackIsSet); +} + +// generateChallenge should always return a unique, cryptographically secure, +// non-zero number. +TEST_F(FaceHidlTest, GenerateChallengeTest) { + std::map<uint64_t, int> m; + for (int i = 0; i < kIterations; ++i) { + mService->generateChallenge(kTimeout, [&m](const OptionalUint64& res) { + ASSERT_EQ(Status::OK, res.status); + EXPECT_NE(0UL, res.value); + m[res.value]++; + EXPECT_EQ(1UL, m[res.value]); + }); + } +} + +// enroll with an invalid (all zeroes) HAT should fail. +TEST_F(FaceHidlTest, EnrollZeroHatTest) { + sp<ErrorCallback> cb = new ErrorCallback(); + mService->setCallback(cb, kAssertCallbackIsSet); + + hidl_vec<uint8_t> token(69); + for (size_t i = 0; i < 69; i++) { + token[i] = 0; + } + + Return<Status> res = mService->enroll(token, kTimeout, {}); + ASSERT_EQ(Status::OK, static_cast<Status>(res)); + + // At least one call to onError should occur + ASSERT_TRUE(waitForCallback(cb->promise.get_future())); + ASSERT_TRUE(cb->hasError); +} + +// enroll with an invalid HAT should fail. +TEST_F(FaceHidlTest, EnrollGarbageHatTest) { + sp<ErrorCallback> cb = new ErrorCallback(); + mService->setCallback(cb, kAssertCallbackIsSet); + + // Filling HAT with invalid data + hidl_vec<uint8_t> token(69); + for (size_t i = 0; i < 69; ++i) { + token[i] = i; + } + + Return<Status> res = mService->enroll(token, kTimeout, {}); + ASSERT_EQ(Status::OK, static_cast<Status>(res)); + + // At least one call to onError should occur + ASSERT_TRUE(waitForCallback(cb->promise.get_future())); + ASSERT_TRUE(cb->hasError); +} + +// setFeature with an invalid (all zeros) HAT should fail. +TEST_F(FaceHidlTest, SetFeatureZeroHatTest) { + sp<ErrorCallback> cb = new ErrorCallback(); + mService->setCallback(cb, kAssertCallbackIsSet); + + hidl_vec<uint8_t> token(69); + for (size_t i = 0; i < 69; i++) { + token[i] = 0; + } + + Return<Status> res = mService->setFeature(Feature::REQUIRE_DIVERSITY, false, token); + ASSERT_EQ(Status::OK, static_cast<Status>(res)); + + // At least one call to onError should occur + ASSERT_TRUE(waitForCallback(cb->promise.get_future())); + ASSERT_TRUE(cb->hasError); +} + +// setFeature with an invalid HAT should fail. +TEST_F(FaceHidlTest, SetFeatureGarbageHatTest) { + sp<ErrorCallback> cb = new ErrorCallback(); + mService->setCallback(cb, kAssertCallbackIsSet); + + // Filling HAT with invalid data + hidl_vec<uint8_t> token(69); + for (size_t i = 0; i < 69; ++i) { + token[i] = i; + } + + Return<Status> res = mService->setFeature(Feature::REQUIRE_DIVERSITY, false, token); + ASSERT_EQ(Status::OK, static_cast<Status>(res)); + + // At least one call to onError should occur + ASSERT_TRUE(waitForCallback(cb->promise.get_future())); + ASSERT_TRUE(cb->hasError); +} + +// getFeature by default should return true for REQUIRE_ATTENTION. +TEST_F(FaceHidlTest, GetFeatureRequireAttentionTest) { + Return<bool> res = mService->getFeature(Feature::REQUIRE_ATTENTION); + ASSERT_EQ(true, static_cast<bool>(res)); +} + +// getFeature by default should return true for REQUIRE_DIVERSITY. +TEST_F(FaceHidlTest, GetFeatureRequireDiversityTest) { + Return<bool> res = mService->getFeature(Feature::REQUIRE_DIVERSITY); + ASSERT_EQ(true, static_cast<bool>(res)); +} + +// revokeChallenge should always return within the timeout +TEST_F(FaceHidlTest, RevokeChallengeTest) { + sp<FaceCallbackBase> cb = new FaceCallbackBase(); + mService->setCallback(cb, kAssertCallbackIsSet); + + auto start = std::chrono::system_clock::now(); + mService->revokeChallenge(); + auto elapsed = std::chrono::system_clock::now() - start; + ASSERT_GE(kTimeoutInSeconds, elapsed); +} + +// The call to getAuthenticatorId should succeed. +TEST_F(FaceHidlTest, GetAuthenticatorIdTest) { + mService->getAuthenticatorId( + [](const OptionalUint64& res) { ASSERT_EQ(Status::OK, res.status); }); +} + +// The call to enumerate should succeed. +TEST_F(FaceHidlTest, EnumerateTest) { + sp<EnumerateCallback> cb = new EnumerateCallback(); + mService->setCallback(cb, kAssertCallbackIsSet); + Return<Status> res = mService->enumerate(); + ASSERT_EQ(Status::OK, static_cast<Status>(res)); + ASSERT_TRUE(waitForCallback(cb->promise.get_future())); +} + +// The call to remove should succeed for any faceId +TEST_F(FaceHidlTest, RemoveFaceTest) { + sp<ErrorCallback> cb = new ErrorCallback(); + mService->setCallback(cb, kAssertCallbackIsSet); + + // Remove a face + Return<Status> res = mService->remove(kFaceId); + ASSERT_EQ(Status::OK, static_cast<Status>(res)); +} + +// Remove should accept 0 to delete all faces +TEST_F(FaceHidlTest, RemoveAllFacesTest) { + sp<ErrorCallback> cb = new ErrorCallback(); + mService->setCallback(cb, kAssertCallbackIsSet); + + // Remove all faces + Return<Status> res = mService->remove(0); + ASSERT_EQ(Status::OK, static_cast<Status>(res)); +} + +// Active user should successfully set to a writable location. +TEST_F(FaceHidlTest, SetActiveUserTest) { + // Create an active user + Return<Status> res = mService->setActiveUser(2, kTmpDir); + ASSERT_EQ(Status::OK, static_cast<Status>(res)); + + // Reset active user + res = mService->setActiveUser(kUserId, kTmpDir); + ASSERT_EQ(Status::OK, static_cast<Status>(res)); +} + +// Active user should fail to set to an unwritable location. +TEST_F(FaceHidlTest, SetActiveUserUnwritableTest) { + // Create an active user to an unwritable location (device root dir) + Return<Status> res = mService->setActiveUser(3, "/"); + ASSERT_NE(Status::OK, static_cast<Status>(res)); + + // Reset active user + res = mService->setActiveUser(kUserId, kTmpDir); + ASSERT_EQ(Status::OK, static_cast<Status>(res)); +} + +// Active user should fail to set to a null location. +TEST_F(FaceHidlTest, SetActiveUserNullTest) { + // Create an active user to a null location. + Return<Status> res = mService->setActiveUser(4, nullptr); + ASSERT_NE(Status::OK, static_cast<Status>(res)); + + // Reset active user + res = mService->setActiveUser(kUserId, kTmpDir); + ASSERT_EQ(Status::OK, static_cast<Status>(res)); +} + +// Cancel should always return CANCELED from any starting state including +// the IDLE state. +TEST_F(FaceHidlTest, CancelTest) { + sp<ErrorCallback> cb = new ErrorCallback(true, FaceError::CANCELED); + mService->setCallback(cb, kAssertCallbackIsSet); + + Return<Status> res = mService->cancel(); + // check that we were able to make an IPC request successfully + ASSERT_EQ(Status::OK, static_cast<Status>(res)); + + // make sure callback was invoked within kTimeoutInSeconds + ASSERT_TRUE(waitForCallback(cb->promise.get_future())); + // check error should be CANCELED + ASSERT_EQ(FaceError::CANCELED, cb->error); +} + +} // anonymous namespace + +int main(int argc, char** argv) { + ::testing::AddGlobalTestEnvironment(FaceHidlEnvironment::Instance()); + ::testing::InitGoogleTest(&argc, argv); + FaceHidlEnvironment::Instance()->init(&argc, argv); + int status = RUN_ALL_TESTS(); + LOG(INFO) << "Test result = " << status; + return status; +} diff --git a/biometrics/fingerprint/2.1/default/android.hardware.biometrics.fingerprint@2.1-service.rc b/biometrics/fingerprint/2.1/default/android.hardware.biometrics.fingerprint@2.1-service.rc index 9bfd3bac6..1667677bf 100644 --- a/biometrics/fingerprint/2.1/default/android.hardware.biometrics.fingerprint@2.1-service.rc +++ b/biometrics/fingerprint/2.1/default/android.hardware.biometrics.fingerprint@2.1-service.rc @@ -4,5 +4,5 @@ service vendor.fps_hal /vendor/bin/hw/android.hardware.biometrics.fingerprint@2. # /data is mounted. class late_start user system - group system input - writepid /dev/cpuset/system-background/tasks
\ No newline at end of file + group system input uhid + writepid /dev/cpuset/system-background/tasks |