diff --git a/biometrics/face/1.1/IBiometricsFace.hal b/biometrics/face/1.1/IBiometricsFace.hal index 975001f30b..84e7443c9c 100644 --- a/biometrics/face/1.1/IBiometricsFace.hal +++ b/biometrics/face/1.1/IBiometricsFace.hal @@ -15,6 +15,7 @@ */ package android.hardware.biometrics.face@1.1; + import @1.0::IBiometricsFace; import @1.0::Status; import @1.0::Feature; @@ -77,6 +78,40 @@ interface IBiometricsFace extends @1.0::IBiometricsFace { * enrollment. Note that all features are enabled by default. * @return status The status of this method call. */ - enrollRemotely(vec hat, uint32_t timeoutSec, - vec disabledFeatures) generates (Status status); + enrollRemotely(vec hat, uint32_t timeoutSec, vec disabledFeatures) + generates (Status status); + + /** + * Enrolls a user's face. + * + * Note that the Hardware Authentication Token must be valid for the + * duration of enrollment and thus should be explicitly invalidated by a + * call to revokeChallenge() when enrollment is complete, to reduce the + * window of opportunity to re-use the challenge and HAT. For example, + * Settings calls generateChallenge() once to allow the user to enroll one + * or more faces or toggle secure settings without having to re-enter the + * PIN/pattern/password. Once the user completes the operation, Settings + * invokes revokeChallenge() to close the transaction. If the HAT is expired, + * the implementation must invoke onError with UNABLE_TO_PROCESS. + * + * 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 successful strong authentication request. + * @param timeoutSec A timeout in seconds, after which this enroll + * attempt is cancelled. Note that the framework can continue + * enrollment by calling this again with a valid HAT. This timeout is + * expected to be used to limit power usage if the device becomes idle + * during enrollment. The implementation is expected to send + * ERROR_TIMEOUT if this happens. + * @param disabledFeatures A list of features to be disabled during + * enrollment. Note that all features are enabled by default. + * @param windowId optional ID of a camera preview window for a + * single-camera device. Must be null if not used. + * @return status The status of this method call. + */ + enroll_1_1(vec hat, uint32_t timeoutSec, vec disabledFeatures, + handle windowId) generates (Status status); }; diff --git a/biometrics/face/1.1/default/BiometricsFace.cpp b/biometrics/face/1.1/default/BiometricsFace.cpp index 7bda57fb7f..2143880514 100644 --- a/biometrics/face/1.1/default/BiometricsFace.cpp +++ b/biometrics/face/1.1/default/BiometricsFace.cpp @@ -111,6 +111,14 @@ Return BiometricsFace::resetLockout(const hidl_vec& /* hat */) } // Methods from ::android::hardware::biometrics::face::V1_1::IBiometricsFace follow. +Return BiometricsFace::enroll_1_1(const hidl_vec& /* hat */, + uint32_t /* timeoutSec */, + const hidl_vec& /* disabledFeatures */, + const hidl_handle& /* windowId */) { + mClientCallback->onError(kDeviceId, mUserId, FaceError::UNABLE_TO_PROCESS, 0 /* vendorCode */); + return Status::OK; +} + Return BiometricsFace::enrollRemotely(const hidl_vec& /* hat */, uint32_t /* timeoutSec */, const hidl_vec& /* disabledFeatures */) { diff --git a/biometrics/face/1.1/default/BiometricsFace.h b/biometrics/face/1.1/default/BiometricsFace.h index 5620b45a43..5ce5771eae 100644 --- a/biometrics/face/1.1/default/BiometricsFace.h +++ b/biometrics/face/1.1/default/BiometricsFace.h @@ -72,6 +72,10 @@ class BiometricsFace : public V1_1::IBiometricsFace { Return resetLockout(const hidl_vec& hat) override; // Methods from ::android::hardware::biometrics::face::V1_1::IBiometricsFace follow. + Return enroll_1_1(const hidl_vec& hat, uint32_t timeoutSec, + const hidl_vec& disabledFeatures, + const hidl_handle& windowId) override; + Return enrollRemotely(const hidl_vec& hat, uint32_t timeoutSec, const hidl_vec& disabledFeatures) override; diff --git a/biometrics/face/1.1/vts/functional/VtsHalBiometricsFaceV1_1TargetTest.cpp b/biometrics/face/1.1/vts/functional/VtsHalBiometricsFaceV1_1TargetTest.cpp index c2431c6727..6ada44231f 100644 --- a/biometrics/face/1.1/vts/functional/VtsHalBiometricsFaceV1_1TargetTest.cpp +++ b/biometrics/face/1.1/vts/functional/VtsHalBiometricsFaceV1_1TargetTest.cpp @@ -30,6 +30,7 @@ #include using android::sp; +using android::hardware::hidl_handle; using android::hardware::hidl_vec; using android::hardware::Return; using android::hardware::Void; @@ -116,6 +117,47 @@ class FaceHidlTest : public ::testing::TestWithParam { sp mCallback; }; +// enroll with an invalid (all zeroes) HAT should fail. +TEST_P(FaceHidlTest, Enroll2_2ZeroHatTest) { + // Filling HAT with zeros + hidl_vec token(69); + for (size_t i = 0; i < 69; i++) { + token[i] = 0; + } + + hidl_handle windowId = nullptr; + Return ret = mService->enroll_1_1(token, kTimeoutSec, {}, windowId); + ASSERT_EQ(Status::OK, static_cast(ret)); + + // onError should be called with a meaningful (nonzero) error. + auto res = mCallback->WaitForCallback(kCallbackNameOnError); + EXPECT_TRUE(res.no_timeout); + EXPECT_EQ(kUserId, res.args->userId); + EXPECT_EQ(FaceError::UNABLE_TO_PROCESS, res.args->error); +} + +// enroll with an invalid HAT should fail. +TEST_P(FaceHidlTest, Enroll2_2GarbageHatTest) { + // Filling HAT with pseudorandom invalid data. + // Using default seed to make the test reproducible. + std::mt19937 gen(std::mt19937::default_seed); + std::uniform_int_distribution dist; + hidl_vec token(69); + for (size_t i = 0; i < 69; ++i) { + token[i] = dist(gen); + } + + hidl_handle windowId = nullptr; + Return ret = mService->enroll_1_1(token, kTimeoutSec, {}, windowId); + ASSERT_EQ(Status::OK, static_cast(ret)); + + // onError should be called with a meaningful (nonzero) error. + auto res = mCallback->WaitForCallback(kCallbackNameOnError); + EXPECT_TRUE(res.no_timeout); + EXPECT_EQ(kUserId, res.args->userId); + EXPECT_EQ(FaceError::UNABLE_TO_PROCESS, res.args->error); +} + // enroll with an invalid (all zeroes) HAT should fail. TEST_P(FaceHidlTest, EnrollRemotelyZeroHatTest) { // Filling HAT with zeros