summaryrefslogtreecommitdiffstats
path: root/biometrics
diff options
context:
space:
mode:
Diffstat (limited to 'biometrics')
-rw-r--r--biometrics/face/1.0/Android.bp28
-rw-r--r--biometrics/face/1.0/IBiometricsFace.hal251
-rw-r--r--biometrics/face/1.0/IBiometricsFaceClientCallback.hal131
-rw-r--r--biometrics/face/1.0/types.hal372
-rw-r--r--biometrics/face/1.0/vts/functional/Android.bp24
-rw-r--r--biometrics/face/1.0/vts/functional/VtsHalBiometricsFaceV1_0TargetTest.cpp412
-rw-r--r--biometrics/fingerprint/2.1/default/android.hardware.biometrics.fingerprint@2.1-service.rc4
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