From 343ca94b13602718e6751d3eeee646ce95bcb468 Mon Sep 17 00:00:00 2001 From: Jeff Pu Date: Wed, 14 Sep 2022 15:56:30 -0400 Subject: [PATCH] Fingerprint virtual HAL checkin (part 2) - acquiredInfo support for HAL operations - error insertions - FPS configurations Bug: 230515082 Bug: 230515086 Test: atest FakeFingerprintEngineTest atest FakeFingerprintEngineUdfpsTest atest --no-bazel-mode VtsHalBiometricsFingerprintTargetTest Change-Id: Iedd1056e516358c3c0a99bd4a720016cc0f880e4 --- .../aidl/default/FakeFingerprintEngine.cpp | 251 +++++++++++++++--- .../fingerprint/aidl/default/Fingerprint.cpp | 20 +- biometrics/fingerprint/aidl/default/README.md | 49 +++- ...trics.fingerprint.VirtualProps-current.txt | 76 +++++- .../aidl/default/fingerprint.sysprop | 137 +++++++++- .../default/include/FakeFingerprintEngine.h | 10 + .../tests/FakeFingerprintEngineTest.cpp | 161 ++++++++++- .../tests/FakeFingerprintEngineUdfpsTest.cpp | 16 +- 8 files changed, 643 insertions(+), 77 deletions(-) diff --git a/biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp b/biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp index 68a1f26a0b..651c9dc352 100644 --- a/biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp +++ b/biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp @@ -56,43 +56,58 @@ void FakeFingerprintEngine::enrollImpl(ISessionCallback* cb, return; } - if (FingerprintHalProperties::operation_enroll_fails().value_or(false)) { - LOG(ERROR) << "Fail: operation_enroll_fails"; - cb->onError(Error::VENDOR, 0 /* vendorError */); + // Force error-out + auto err = FingerprintHalProperties::operation_enroll_error().value_or(0); + if (err != 0) { + LOG(ERROR) << "Fail: operation_enroll_error"; + auto ec = convertError(err); + cb->onError(ec.first, ec.second); return; } - // format is ":,,...: + // Format is ":,...: auto nextEnroll = FingerprintHalProperties::next_enrollment().value_or(""); auto parts = Util::split(nextEnroll, ":"); if (parts.size() != 3) { - LOG(ERROR) << "Fail: invalid next_enrollment"; + LOG(ERROR) << "Fail: invalid next_enrollment:" << nextEnroll; cb->onError(Error::VENDOR, 0 /* vendorError */); return; } auto enrollmentId = std::stoi(parts[0]); - auto progress = Util::split(parts[1], ","); - for (size_t i = 0; i < progress.size(); i++) { - auto left = progress.size() - i - 1; - SLEEP_MS(std::stoi(progress[i])); + auto progress = parseEnrollmentCapture(parts[1]); + for (size_t i = 0; i < progress.size(); i += 2) { + auto left = (progress.size() - i) / 2 - 1; + auto duration = progress[i][0]; + auto acquired = progress[i + 1]; + auto N = acquired.size(); - if (shouldCancel(cancel)) { - LOG(ERROR) << "Fail: cancel"; - cb->onError(Error::CANCELED, 0 /* vendorCode */); - return; + for (int j = 0; j < N; j++) { + SLEEP_MS(duration / N); + + if (shouldCancel(cancel)) { + LOG(ERROR) << "Fail: cancel"; + cb->onError(Error::CANCELED, 0 /* vendorCode */); + return; + } + auto ac = convertAcquiredInfo(acquired[j]); + cb->onAcquired(ac.first, ac.second); } - cb->onAcquired(AcquiredInfo::GOOD, 0 /* vendorCode */); if (left == 0 && !IS_TRUE(parts[2])) { // end and failed LOG(ERROR) << "Fail: requested by caller: " << nextEnroll; FingerprintHalProperties::next_enrollment({}); cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorCode */); } else { // progress and update props if last time + LOG(INFO) << "onEnroll: " << enrollmentId << " left: " << left; if (left == 0) { auto enrollments = FingerprintHalProperties::enrollments(); enrollments.emplace_back(enrollmentId); FingerprintHalProperties::enrollments(enrollments); FingerprintHalProperties::next_enrollment({}); + // change authenticatorId after new enrollment + auto id = FingerprintHalProperties::authenticator_id().value_or(0); + auto newId = id + 1; + FingerprintHalProperties::authenticator_id(newId); LOG(INFO) << "Enrolled: " << enrollmentId; } cb->onEnrollmentProgress(enrollmentId, left); @@ -104,12 +119,31 @@ void FakeFingerprintEngine::authenticateImpl(ISessionCallback* cb, int64_t /* op const std::future& cancel) { BEGIN_OP(FingerprintHalProperties::operation_authenticate_latency().value_or(DEFAULT_LATENCY)); - auto now = Util::getSystemNanoTime(); - int64_t duration = FingerprintHalProperties::operation_authenticate_duration().value_or(0); + int64_t now = Util::getSystemNanoTime(); + int64_t duration = FingerprintHalProperties::operation_authenticate_duration().value_or(10); + auto acquired = FingerprintHalProperties::operation_authenticate_acquired().value_or("1"); + auto acquiredInfos = parseIntSequence(acquired); + int N = acquiredInfos.size(); + + if (N == 0) { + LOG(ERROR) << "Fail to parse authentiate acquired info: " + acquired; + cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */); + return; + } + + int i = 0; do { if (FingerprintHalProperties::operation_authenticate_fails().value_or(false)) { LOG(ERROR) << "Fail: operation_authenticate_fails"; - cb->onError(Error::VENDOR, 0 /* vendorError */); + cb->onAuthenticationFailed(); + return; + } + + auto err = FingerprintHalProperties::operation_authenticate_error().value_or(0); + if (err != 0) { + LOG(ERROR) << "Fail: operation_authenticate_error"; + auto ec = convertError(err); + cb->onError(ec.first, ec.second); return; } @@ -126,20 +160,25 @@ void FakeFingerprintEngine::authenticateImpl(ISessionCallback* cb, int64_t /* op return; } - auto id = FingerprintHalProperties::enrollment_hit().value_or(0); - auto enrolls = FingerprintHalProperties::enrollments(); - auto isEnrolled = std::find(enrolls.begin(), enrolls.end(), id) != enrolls.end(); - if (id > 0 && isEnrolled) { - cb->onAuthenticationSucceeded(id, {} /* hat */); - return; + if (i < N) { + auto ac = convertAcquiredInfo(acquiredInfos[i]); + cb->onAcquired(ac.first, ac.second); + i++; } - SLEEP_MS(100); + SLEEP_MS(duration / N); } while (!Util::hasElapsed(now, duration)); - LOG(ERROR) << "Fail: not enrolled"; - cb->onAuthenticationFailed(); - cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */); + auto id = FingerprintHalProperties::enrollment_hit().value_or(0); + auto enrolls = FingerprintHalProperties::enrollments(); + auto isEnrolled = std::find(enrolls.begin(), enrolls.end(), id) != enrolls.end(); + if (id > 0 && isEnrolled) { + cb->onAuthenticationSucceeded(id, {} /* hat */); + return; + } else { + LOG(ERROR) << "Fail: fingerprint not enrolled"; + cb->onAuthenticationFailed(); + } } void FakeFingerprintEngine::detectInteractionImpl(ISessionCallback* cb, @@ -147,17 +186,42 @@ void FakeFingerprintEngine::detectInteractionImpl(ISessionCallback* cb, BEGIN_OP(FingerprintHalProperties::operation_detect_interaction_latency().value_or( DEFAULT_LATENCY)); - if (FingerprintHalProperties::operation_detect_interaction_fails().value_or(false)) { - LOG(ERROR) << "Fail: operation_detect_interaction_fails"; - cb->onError(Error::VENDOR, 0 /* vendorError */); + int64_t duration = + FingerprintHalProperties::operation_detect_interaction_duration().value_or(10); + auto acquired = FingerprintHalProperties::operation_detect_interaction_acquired().value_or("1"); + auto acquiredInfos = parseIntSequence(acquired); + int N = acquiredInfos.size(); + int64_t now = Util::getSystemNanoTime(); + + if (N == 0) { + LOG(ERROR) << "Fail to parse detect interaction acquired info: " + acquired; + cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */); return; } - if (shouldCancel(cancel)) { - LOG(ERROR) << "Fail: cancel"; - cb->onError(Error::CANCELED, 0 /* vendorCode */); - return; - } + int i = 0; + do { + auto err = FingerprintHalProperties::operation_detect_interaction_error().value_or(0); + if (err != 0) { + LOG(ERROR) << "Fail: operation_detect_interaction_error"; + auto ec = convertError(err); + cb->onError(ec.first, ec.second); + return; + } + + if (shouldCancel(cancel)) { + LOG(ERROR) << "Fail: cancel"; + cb->onError(Error::CANCELED, 0 /* vendorCode */); + return; + } + + if (i < N) { + auto ac = convertAcquiredInfo(acquiredInfos[i]); + cb->onAcquired(ac.first, ac.second); + i++; + } + SLEEP_MS(duration / N); + } while (!Util::hasElapsed(now, duration)); auto id = FingerprintHalProperties::enrollment_hit().value_or(0); auto enrolls = FingerprintHalProperties::enrollments(); @@ -211,24 +275,37 @@ void FakeFingerprintEngine::removeEnrollmentsImpl(ISessionCallback* cb, void FakeFingerprintEngine::getAuthenticatorIdImpl(ISessionCallback* cb) { BEGIN_OP(0); - int64_t authenticatorId = FingerprintHalProperties::authenticator_id().value_or(0); - if (FingerprintHalProperties::enrollments().size() > 0 && authenticatorId == 0) { - authenticatorId = 99999999; // default authenticatorId, TODO(b/230515082) + int64_t authenticatorId; + if (FingerprintHalProperties::enrollments().size() == 0) { + authenticatorId = 0; + } else { + authenticatorId = FingerprintHalProperties::authenticator_id().value_or(0); + if (authenticatorId == 0) authenticatorId = 1; } cb->onAuthenticatorIdRetrieved(authenticatorId); } void FakeFingerprintEngine::invalidateAuthenticatorIdImpl(ISessionCallback* cb) { BEGIN_OP(0); - auto id = FingerprintHalProperties::authenticator_id().value_or(0); - auto newId = id + 1; + int64_t newId; + if (FingerprintHalProperties::enrollments().size() == 0) { + newId = 0; + } else { + auto id = FingerprintHalProperties::authenticator_id().value_or(0); + newId = id + 1; + } FingerprintHalProperties::authenticator_id(newId); cb->onAuthenticatorIdInvalidated(newId); } void FakeFingerprintEngine::resetLockoutImpl(ISessionCallback* cb, - const keymaster::HardwareAuthToken& /*hat*/) { + const keymaster::HardwareAuthToken& hat) { BEGIN_OP(0); + if (hat.mac.empty()) { + LOG(ERROR) << "Fail: hat in resetLockout()"; + cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */); + return; + } FingerprintHalProperties::lockout(false); cb->onLockoutCleared(); } @@ -286,4 +363,96 @@ SensorLocation FakeFingerprintEngine::defaultSensorLocation() { return {0 /* displayId (not used) */, 0 /* sensorLocationX */, 0 /* sensorLocationY */, 0 /* sensorRadius */, "" /* display */}; } + +std::vector FakeFingerprintEngine::parseIntSequence(const std::string& str, + const std::string& sep) { + std::vector seqs = Util::split(str, sep); + std::vector res; + + for (const auto& seq : seqs) { + int32_t val; + if (ParseInt(seq, &val)) { + res.push_back(val); + } else { + LOG(WARNING) << "Invalid int sequence:" + str; + res.clear(); + break; + } + } + + return res; +} + +std::vector> FakeFingerprintEngine::parseEnrollmentCapture( + const std::string& str) { + std::vector defaultAcquiredInfo = {(int32_t)AcquiredInfo::GOOD}; + std::vector> res; + int i = 0, N = str.length(); + std::size_t found = 0; + bool aborted = true; + + while (found != std::string::npos) { + std::string durationStr, acquiredStr; + found = str.find_first_of("-,", i); + if (found == std::string::npos) { + if (N - i < 1) break; + durationStr = str.substr(i, N - i); + } else { + durationStr = str.substr(i, found - i); + if (str[found] == '-') { + found = str.find_first_of('[', found + 1); + if (found == std::string::npos) break; + i = found + 1; + found = str.find_first_of(']', found + 1); + if (found == std::string::npos) break; + acquiredStr = str.substr(i, found - i); + found = str.find_first_of(',', found + 1); + } + } + std::vector duration{0}; + if (!ParseInt(durationStr, &duration[0])) break; + res.push_back(duration); + if (!acquiredStr.empty()) { + std::vector acquiredInfo = parseIntSequence(acquiredStr); + if (acquiredInfo.empty()) break; + res.push_back(acquiredInfo); + } else + res.push_back(defaultAcquiredInfo); + + i = found + 1; + if (found == std::string::npos || found == N - 1) aborted = false; + } + + if (aborted) { + LOG(ERROR) << "Failed to parse enrollment captures:" + str; + res.clear(); + } + + return res; +} + +std::pair FakeFingerprintEngine::convertAcquiredInfo(int32_t code) { + std::pair res; + if (code > FINGERPRINT_ACQUIRED_VENDOR_BASE) { + res.first = AcquiredInfo::VENDOR; + res.second = code - FINGERPRINT_ACQUIRED_VENDOR_BASE; + } else { + res.first = (AcquiredInfo)code; + res.second = 0; + } + return res; +} + +std::pair FakeFingerprintEngine::convertError(int32_t code) { + std::pair res; + if (code > FINGERPRINT_ERROR_VENDOR_BASE) { + res.first = Error::VENDOR; + res.second = code - FINGERPRINT_ERROR_VENDOR_BASE; + } else { + res.first = (Error)code; + res.second = 0; + } + return res; +} + } // namespace aidl::android::hardware::biometrics::fingerprint diff --git a/biometrics/fingerprint/aidl/default/Fingerprint.cpp b/biometrics/fingerprint/aidl/default/Fingerprint.cpp index 922781ce65..74e7caff3c 100644 --- a/biometrics/fingerprint/aidl/default/Fingerprint.cpp +++ b/biometrics/fingerprint/aidl/default/Fingerprint.cpp @@ -65,8 +65,18 @@ ndk::ScopedAStatus Fingerprint::getSensorProps(std::vector* out) { {SW_COMPONENT_ID, "" /* hardwareVersion */, "" /* firmwareVersion */, "" /* serialNumber */, SW_VERSION}}; - common::CommonProps commonProps = {SENSOR_ID, SENSOR_STRENGTH, MAX_ENROLLMENTS_PER_USER, - componentInfo}; + auto sensorId = FingerprintHalProperties::sensor_id().value_or(SENSOR_ID); + auto sensorStrength = + FingerprintHalProperties::sensor_strength().value_or((int)SENSOR_STRENGTH); + auto maxEnrollments = + FingerprintHalProperties::max_enrollments().value_or(MAX_ENROLLMENTS_PER_USER); + auto navigationGuesture = FingerprintHalProperties::navigation_guesture().value_or(false); + auto detectInteraction = FingerprintHalProperties::detect_interaction().value_or(false); + auto displayTouch = FingerprintHalProperties::display_touch().value_or(true); + auto controlIllumination = FingerprintHalProperties::control_illumination().value_or(false); + + common::CommonProps commonProps = {sensorId, (common::SensorStrength)sensorStrength, + maxEnrollments, componentInfo}; SensorLocation sensorLocation = mEngine->getSensorLocation(); @@ -75,8 +85,10 @@ ndk::ScopedAStatus Fingerprint::getSensorProps(std::vector* out) { *out = {{commonProps, mSensorType, {sensorLocation}, - SUPPORTS_NAVIGATION_GESTURES, - false /* supportsDetectInteraction */}}; + navigationGuesture, + detectInteraction, + displayTouch, + controlIllumination}}; return ndk::ScopedAStatus::ok(); } diff --git a/biometrics/fingerprint/aidl/default/README.md b/biometrics/fingerprint/aidl/default/README.md index bb6bb48dd0..ad471f747d 100644 --- a/biometrics/fingerprint/aidl/default/README.md +++ b/biometrics/fingerprint/aidl/default/README.md @@ -65,7 +65,7 @@ $ adb shell cmd fingerprint sync $ adb shell getprop persist.vendor.fingerprint.virtual.enrollments ``` -### Authenticate +## Authenticate To authenticate successfully set the enrolled id that should succeed. Unset it or change the value to make authenticate operations fail: @@ -74,7 +74,52 @@ or change the value to make authenticate operations fail: $ adb shell setprop vendor.fingerprint.virtual.enrollment_hit 1 ```` -### View HAL State +## Acquired Info Insertion + +Fingerprint image acquisition states at HAL are reported to framework via onAcquired() callback. The valid acquired state info for AIDL HAL include + +{UNKNOWN(0), GOOD(1), PARTIAL(2), INSUFFICIENT(3), SENSOR_DIRTY(4), TOO_SLOW(5), TOO_FAST(6), VENDOR(7), START(8), TOO_DARK(9), TOO_BRIGHT(10), IMMOBILE(11), RETRYING_CAPTURE(12)} + +Refer to [AcquiredInfo.aidl](../android/hardware/biometrics/fingerprint/AcquiredInfo.aidl) for details + + +The states can be specified in sequence for the HAL operations involving fingerprint image captures, namely authenticate, enrollment and detectInteraction + +```shell +$ adb shell setprop vendor.fingerprint.virtual.operation_authenticate_acquired 6,9,1 +$ adb shell setprop vendor.fingerprint.virtual.operation_detect_interaction_acquired 6,1 +$ adb shell setprop vendor.fingerprint.virtual.next_enrollment 2:1000-[5,1],500:true + +#next_enrollment format example: +.---------------------- enrollment id (2) +| .------------------ the image capture 1 duration (1000ms) +| | .-------------- acquired info first (TOO_SLOW) +| | | .------------ acquired info second (GOOD) +| | | | .-------- the image capture 2 duration (500ms) +| | | | | .---- enrollment end status (success) +| | | | | | +| | | | | | +| | | | | | +2:1000-[5,1],500:true +``` +For vendor specific acquired info, acquiredInfo = 1000 + vendorAcquiredInfo + +## Error Insertion +The valid error codes for AIDL HAL include + +{UNKNOWN(0), HW_UNAVAILABLE(1), UNABLE_TO_PROCESS(2), TIMEOUT(3), NO_SPACE(4), CANCELED(5), UNABLE_TO_REMOVE(6), VENDOR(7), BAD_CALIBRATION(8)} + +Refer to [Error.aidl](../android/hardware/biometrics/fingerprint/Error.aidl) for details + + +There are many HAL operations which can result in errors, refer to [here](fingerprint.sysprop) file for details. + +```shell +$ adb shell setprop vendor.fingerprint.virtual.operation_authenticate_error 8 +``` +For vendor specific error, errorCode = 1000 + vendorErrorCode + +## View HAL State To view all the properties of the HAL (see `fingerprint.sysprop` file for the API): diff --git a/biometrics/fingerprint/aidl/default/api/android.hardware.biometrics.fingerprint.VirtualProps-current.txt b/biometrics/fingerprint/aidl/default/api/android.hardware.biometrics.fingerprint.VirtualProps-current.txt index 9dfb74d0ef..fa21663a17 100644 --- a/biometrics/fingerprint/aidl/default/api/android.hardware.biometrics.fingerprint.VirtualProps-current.txt +++ b/biometrics/fingerprint/aidl/default/api/android.hardware.biometrics.fingerprint.VirtualProps-current.txt @@ -5,7 +5,7 @@ props { api_name: "authenticator_id" type: Long access: ReadWrite - prop_name: "vendor.fingerprint.virtual.authenticator_id" + prop_name: "persist.vendor.fingerprint.virtual.authenticator_id" } prop { api_name: "challenge" @@ -13,6 +13,21 @@ props { access: ReadWrite prop_name: "vendor.fingerprint.virtual.challenge" } + prop { + api_name: "control_illumination" + access: ReadWrite + prop_name: "persist.vendor.fingerprint.virtual.udfps.control_illumination" + } + prop { + api_name: "detect_interaction" + access: ReadWrite + prop_name: "persist.vendor.fingerprint.virtual.detect_interaction" + } + prop { + api_name: "display_touch" + access: ReadWrite + prop_name: "persist.vendor.fingerprint.virtual.udfps.display_touch" + } prop { api_name: "enrollment_hit" type: Integer @@ -28,7 +43,18 @@ props { prop { api_name: "lockout" access: ReadWrite - prop_name: "vendor.fingerprint.virtual.lockout" + prop_name: "persist.vendor.fingerprint.virtual.lockout" + } + prop { + api_name: "max_enrollments" + type: Integer + access: ReadWrite + prop_name: "persist.vendor.fingerprint.virtual.max_enrollments" + } + prop { + api_name: "navigation_guesture" + access: ReadWrite + prop_name: "persist.vendor.fingerprint.virtual.navigation_guesture" } prop { api_name: "next_enrollment" @@ -36,12 +62,24 @@ props { access: ReadWrite prop_name: "vendor.fingerprint.virtual.next_enrollment" } + prop { + api_name: "operation_authenticate_acquired" + type: String + access: ReadWrite + prop_name: "vendor.fingerprint.virtual.operation_authenticate_acquired" + } prop { api_name: "operation_authenticate_duration" type: Integer access: ReadWrite prop_name: "vendor.fingerprint.virtual.operation_authenticate_duration" } + prop { + api_name: "operation_authenticate_error" + type: Integer + access: ReadWrite + prop_name: "vendor.fingerprint.virtual.operation_authenticate_error" + } prop { api_name: "operation_authenticate_fails" access: ReadWrite @@ -54,9 +92,22 @@ props { prop_name: "vendor.fingerprint.virtual.operation_authenticate_latency" } prop { - api_name: "operation_detect_interaction_fails" + api_name: "operation_detect_interaction_acquired" + type: String access: ReadWrite - prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_fails" + prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_acquired" + } + prop { + api_name: "operation_detect_interaction_duration" + type: Integer + access: ReadWrite + prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_duration" + } + prop { + api_name: "operation_detect_interaction_error" + type: Integer + access: ReadWrite + prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_error" } prop { api_name: "operation_detect_interaction_latency" @@ -65,9 +116,10 @@ props { prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_latency" } prop { - api_name: "operation_enroll_fails" + api_name: "operation_enroll_error" + type: Integer access: ReadWrite - prop_name: "vendor.fingerprint.virtual.operation_enroll_fails" + prop_name: "vendor.fingerprint.virtual.operation_enroll_error" } prop { api_name: "operation_enroll_latency" @@ -75,12 +127,24 @@ props { access: ReadWrite prop_name: "vendor.fingerprint.virtual.operation_enroll_latency" } + prop { + api_name: "sensor_id" + type: Integer + access: ReadWrite + prop_name: "persist.vendor.fingerprint.virtual.sensor_id" + } prop { api_name: "sensor_location" type: String access: ReadWrite prop_name: "persist.vendor.fingerprint.virtual.sensor_location" } + prop { + api_name: "sensor_strength" + type: Integer + access: ReadWrite + prop_name: "persist.vendor.fingerprint.virtual.sensor_strength" + } prop { api_name: "type" type: String diff --git a/biometrics/fingerprint/aidl/default/fingerprint.sysprop b/biometrics/fingerprint/aidl/default/fingerprint.sysprop index 85e93b0aa8..9b8fada927 100644 --- a/biometrics/fingerprint/aidl/default/fingerprint.sysprop +++ b/biometrics/fingerprint/aidl/default/fingerprint.sysprop @@ -32,8 +32,12 @@ prop { api_name: "enrollment_hit" } -# the next enrollment in the format: ":,,...:" -# for example: "2:0:true" +# the next enrollment in the format of: +# ":,,...:" +# = +# [acquiredInfos] = [acquiredInfo1, acquiredInfo2, ...] +# (refer to README.md file for acquiredInfo values) +# e.g. "2:100,20:true", "2:100-[5,1],20:true" # this property is reset after enroll completes prop { prop_name: "vendor.fingerprint.virtual.next_enrollment" @@ -45,7 +49,7 @@ prop { # value for getAuthenticatorId or 0 prop { - prop_name: "vendor.fingerprint.virtual.authenticator_id" + prop_name: "persist.vendor.fingerprint.virtual.authenticator_id" type: Long scope: Public access: ReadWrite @@ -63,7 +67,7 @@ prop { # if locked out prop { - prop_name: "vendor.fingerprint.virtual.lockout" + prop_name: "persist.vendor.fingerprint.virtual.lockout" type: Boolean scope: Public access: ReadWrite @@ -79,22 +83,26 @@ prop { api_name: "operation_authenticate_fails" } -# force all detectInteraction operations to fail +# force all detectInteraction operations to error out +# error consists of errorCode and vendorErrorCode +# valid errorCodes are listed in README.md file +# vendorErrorCode = (error>1000) ? error-1000 : 0 +# e.g. error(1002) --> errorCode(7) and vendorErrorCode(2) prop { - prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_fails" - type: Boolean + prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_error" + type: Integer scope: Public access: ReadWrite - api_name: "operation_detect_interaction_fails" + api_name: "operation_detect_interaction_error" } -# force all enroll operations to fail +# force all enroll operations to result in error prop { - prop_name: "vendor.fingerprint.virtual.operation_enroll_fails" - type: Boolean + prop_name: "vendor.fingerprint.virtual.operation_enroll_error" + type: Integer scope: Public access: ReadWrite - api_name: "operation_enroll_fails" + api_name: "operation_enroll_error" } # add a latency to authentication operations @@ -134,6 +142,15 @@ prop { api_name: "operation_authenticate_duration" } +# insert error for authenticate operations +prop { + prop_name: "vendor.fingerprint.virtual.operation_authenticate_error" + type: Integer + scope: Public + access: ReadWrite + api_name: "operation_authenticate_error" +} + # sensor location # :: in pixel prop { @@ -143,3 +160,99 @@ prop { access: ReadWrite api_name: "sensor_location" } + +# acquired info during authentication in format of sequence +prop { + prop_name: "vendor.fingerprint.virtual.operation_authenticate_acquired" + type: String + scope: Public + access: ReadWrite + api_name: "operation_authenticate_acquired" +} + +# millisecond duration for detect interaction operations +# (waits for changes to enrollment_hit) +prop { + prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_duration" + type: Integer + scope: Public + access: ReadWrite + api_name: "operation_detect_interaction_duration" +} + +# acquired info during detect interaction operation in format of sequence +# e.g. 5,6,1 (TOO_SLOW, TOO_FAST, GOOD) +# onAcquired() callback will be invoked in sequence +# vendorAcquiredCode = (acquired>1000) ? acquired-1000 : 0 +prop { + prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_acquired" + type: String + scope: Public + access: ReadWrite + api_name: "operation_detect_interaction_acquired" +} + +# sensor id (default: 5) +prop { + prop_name: "persist.vendor.fingerprint.virtual.sensor_id" + type: Integer + scope: Public + access: ReadWrite + api_name: "sensor_id" +} + +# sensor strength (default: 2) +# [0=CONVENECE, 1=WEAK, 2=STRONG] +prop { + prop_name: "persist.vendor.fingerprint.virtual.sensor_strength" + type: Integer + scope: Public + access: ReadWrite + api_name: "sensor_strength" +} + +# max enrollments per user (default: 5) +# +prop { + prop_name: "persist.vendor.fingerprint.virtual.max_enrollments" + type: Integer + scope: Public + access: ReadWrite + api_name: "max_enrollments" +} + +# whether support navigation guestures (default: false) +prop { + prop_name: "persist.vendor.fingerprint.virtual.navigation_guesture" + type: Boolean + scope: Public + access: ReadWrite + api_name: "navigation_guesture" +} + +# whether support detect interaction (default: false) +prop { + prop_name: "persist.vendor.fingerprint.virtual.detect_interaction" + type: Boolean + scope: Public + access: ReadWrite + api_name: "detect_interaction" +} + +# whether support display touch by hal (default: true) +prop { + prop_name: "persist.vendor.fingerprint.virtual.udfps.display_touch" + type: Boolean + scope: Public + access: ReadWrite + api_name: "display_touch" +} + +# whether support illumination control by hal (default: false) +prop { + prop_name: "persist.vendor.fingerprint.virtual.udfps.control_illumination" + type: Boolean + scope: Public + access: ReadWrite + api_name: "control_illumination" +} diff --git a/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h index d7df8182de..22b1744134 100644 --- a/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h +++ b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h @@ -59,7 +59,17 @@ class FakeFingerprintEngine { virtual SensorLocation defaultSensorLocation(); + std::vector parseIntSequence(const std::string& str, const std::string& sep = ","); + + std::vector> parseEnrollmentCapture(const std::string& str); + std::mt19937 mRandom; + + private: + static constexpr int32_t FINGERPRINT_ACQUIRED_VENDOR_BASE = 1000; + static constexpr int32_t FINGERPRINT_ERROR_VENDOR_BASE = 1000; + std::pair convertAcquiredInfo(int32_t code); + std::pair convertError(int32_t code); }; } // namespace aidl::android::hardware::biometrics::fingerprint diff --git a/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineTest.cpp b/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineTest.cpp index 8696d2624a..32d01f400f 100644 --- a/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineTest.cpp +++ b/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineTest.cpp @@ -38,8 +38,9 @@ class TestSessionCallback : public BnSessionCallback { mLastChallengeRevoked = challenge; return ndk::ScopedAStatus::ok(); }; - ::ndk::ScopedAStatus onError(fingerprint::Error error, int32_t) override { + ::ndk::ScopedAStatus onError(fingerprint::Error error, int32_t vendorCode) override { mError = error; + mErrorVendorCode = vendorCode; return ndk::ScopedAStatus::ok(); }; ::ndk::ScopedAStatus onEnrollmentProgress(int32_t enrollmentId, int32_t remaining) override { @@ -62,7 +63,10 @@ class TestSessionCallback : public BnSessionCallback { mInteractionDetectedCount++; return ndk::ScopedAStatus::ok(); }; - ndk::ScopedAStatus onAcquired(AcquiredInfo /*info*/, int32_t /*vendorCode*/) override { + ndk::ScopedAStatus onAcquired(AcquiredInfo info, int32_t vendorCode) override { + mLastAcquiredInfo = (int32_t)info; + mLastAcquiredVendorCode = vendorCode; + mLastAcquiredCount++; return ndk::ScopedAStatus::ok(); } ::ndk::ScopedAStatus onEnrollmentsEnumerated( @@ -94,6 +98,7 @@ class TestSessionCallback : public BnSessionCallback { ndk::ScopedAStatus onSessionClosed() override { return ndk::ScopedAStatus::ok(); } Error mError = Error::UNKNOWN; + int32_t mErrorVendorCode = 0; int64_t mLastChallenge = -1; int64_t mLastChallengeRevoked = -1; int32_t mLastEnrolled = -1; @@ -105,6 +110,9 @@ class TestSessionCallback : public BnSessionCallback { bool mAuthenticatorIdInvalidated = false; bool mLockoutPermanent = false; int mInteractionDetectedCount = 0; + int32_t mLastAcquiredInfo = -1; + int32_t mLastAcquiredVendorCode = -1; + int32_t mLastAcquiredCount = 0; }; class FakeFingerprintEngineTest : public ::testing::Test { @@ -116,6 +124,12 @@ class FakeFingerprintEngineTest : public ::testing::Test { mCallback = ndk::SharedRefBase::make(); } + void TearDown() override { + FingerprintHalProperties::operation_authenticate_error(0); + FingerprintHalProperties::operation_detect_interaction_error(0); + FingerprintHalProperties::operation_authenticate_acquired(""); + } + FakeFingerprintEngine mEngine; std::shared_ptr mCallback; std::promise mCancel; @@ -135,11 +149,13 @@ TEST_F(FakeFingerprintEngineTest, RevokeChallenge) { TEST_F(FakeFingerprintEngineTest, ResetLockout) { FingerprintHalProperties::lockout(true); - mEngine.resetLockoutImpl(mCallback.get(), {}); + keymaster::HardwareAuthToken hat{.mac = {2, 4}}; + mEngine.resetLockoutImpl(mCallback.get(), hat); ASSERT_FALSE(FingerprintHalProperties::lockout().value_or(true)); } TEST_F(FakeFingerprintEngineTest, AuthenticatorId) { + FingerprintHalProperties::enrollments({1}); FingerprintHalProperties::authenticator_id(50); mEngine.getAuthenticatorIdImpl(mCallback.get()); ASSERT_EQ(50, mCallback->mLastAuthenticatorId); @@ -162,6 +178,7 @@ TEST_F(FakeFingerprintEngineTest, Enroll) { ASSERT_EQ(1, FingerprintHalProperties::enrollments().size()); ASSERT_EQ(4, FingerprintHalProperties::enrollments()[0].value()); ASSERT_EQ(4, mCallback->mLastEnrolled); + ASSERT_EQ(1, mCallback->mLastAcquiredInfo); } TEST_F(FakeFingerprintEngineTest, EnrollCancel) { @@ -189,12 +206,28 @@ TEST_F(FakeFingerprintEngineTest, EnrollFail) { ASSERT_FALSE(FingerprintHalProperties::next_enrollment().has_value()); } +TEST_F(FakeFingerprintEngineTest, EnrollAcquired) { + FingerprintHalProperties::enrollments({}); + FingerprintHalProperties::next_enrollment("4:0,5-[12,1013]:true"); + keymaster::HardwareAuthToken hat{.mac = {2, 4}}; + int32_t prevCnt = mCallback->mLastAcquiredCount; + mEngine.enrollImpl(mCallback.get(), hat, mCancel.get_future()); + ASSERT_FALSE(FingerprintHalProperties::next_enrollment().has_value()); + ASSERT_EQ(1, FingerprintHalProperties::enrollments().size()); + ASSERT_EQ(4, FingerprintHalProperties::enrollments()[0].value()); + ASSERT_EQ(4, mCallback->mLastEnrolled); + ASSERT_EQ(prevCnt + 3, mCallback->mLastAcquiredCount); + ASSERT_EQ(7, mCallback->mLastAcquiredInfo); + ASSERT_EQ(13, mCallback->mLastAcquiredVendorCode); +} + TEST_F(FakeFingerprintEngineTest, Authenticate) { FingerprintHalProperties::enrollments({1, 2}); FingerprintHalProperties::enrollment_hit(2); mEngine.authenticateImpl(mCallback.get(), 0, mCancel.get_future()); ASSERT_FALSE(mCallback->mAuthenticateFailed); ASSERT_EQ(2, mCallback->mLastAuthenticated); + ASSERT_EQ(1, mCallback->mLastAcquiredInfo); } TEST_F(FakeFingerprintEngineTest, AuthenticateCancel) { @@ -211,7 +244,6 @@ TEST_F(FakeFingerprintEngineTest, AuthenticateNotSet) { FingerprintHalProperties::enrollment_hit({}); mEngine.authenticateImpl(mCallback.get(), 0, mCancel.get_future()); ASSERT_TRUE(mCallback->mAuthenticateFailed); - ASSERT_EQ(mCallback->mError, Error::UNABLE_TO_PROCESS); } TEST_F(FakeFingerprintEngineTest, AuthenticateNotEnrolled) { @@ -219,7 +251,6 @@ TEST_F(FakeFingerprintEngineTest, AuthenticateNotEnrolled) { FingerprintHalProperties::enrollment_hit(3); mEngine.authenticateImpl(mCallback.get(), 0, mCancel.get_future()); ASSERT_TRUE(mCallback->mAuthenticateFailed); - ASSERT_EQ(mCallback->mError, Error::UNABLE_TO_PROCESS); } TEST_F(FakeFingerprintEngineTest, AuthenticateLockout) { @@ -231,11 +262,41 @@ TEST_F(FakeFingerprintEngineTest, AuthenticateLockout) { ASSERT_NE(mCallback->mError, Error::UNKNOWN); } +TEST_F(FakeFingerprintEngineTest, AuthenticateError8) { + FingerprintHalProperties::operation_authenticate_error(8); + mEngine.authenticateImpl(mCallback.get(), 0, mCancel.get_future()); + ASSERT_EQ(mCallback->mError, (Error)8); + ASSERT_EQ(mCallback->mErrorVendorCode, 0); +} + +TEST_F(FakeFingerprintEngineTest, AuthenticateError9) { + FingerprintHalProperties::operation_authenticate_error(1009); + mEngine.authenticateImpl(mCallback.get(), 0, mCancel.get_future()); + ASSERT_EQ(mCallback->mError, (Error)7); + ASSERT_EQ(mCallback->mErrorVendorCode, 9); +} + +TEST_F(FakeFingerprintEngineTest, AuthenticateAcquired) { + FingerprintHalProperties::lockout(false); + FingerprintHalProperties::enrollments({1, 2}); + FingerprintHalProperties::enrollment_hit(2); + FingerprintHalProperties::operation_authenticate_acquired("4,1009"); + int32_t prevCount = mCallback->mLastAcquiredCount; + mEngine.authenticateImpl(mCallback.get(), 0, mCancel.get_future()); + ASSERT_FALSE(mCallback->mAuthenticateFailed); + ASSERT_EQ(2, mCallback->mLastAuthenticated); + ASSERT_EQ(prevCount + 2, mCallback->mLastAcquiredCount); + ASSERT_EQ(7, mCallback->mLastAcquiredInfo); + ASSERT_EQ(9, mCallback->mLastAcquiredVendorCode); +} + TEST_F(FakeFingerprintEngineTest, InteractionDetect) { FingerprintHalProperties::enrollments({1, 2}); FingerprintHalProperties::enrollment_hit(2); + FingerprintHalProperties::operation_detect_interaction_acquired(""); mEngine.detectInteractionImpl(mCallback.get(), mCancel.get_future()); ASSERT_EQ(1, mCallback->mInteractionDetectedCount); + ASSERT_EQ(1, mCallback->mLastAcquiredInfo); } TEST_F(FakeFingerprintEngineTest, InteractionDetectCancel) { @@ -261,6 +322,26 @@ TEST_F(FakeFingerprintEngineTest, InteractionDetectNotEnrolled) { ASSERT_EQ(0, mCallback->mInteractionDetectedCount); } +TEST_F(FakeFingerprintEngineTest, InteractionDetectError) { + FingerprintHalProperties::operation_detect_interaction_error(8); + mEngine.detectInteractionImpl(mCallback.get(), mCancel.get_future()); + ASSERT_EQ(0, mCallback->mInteractionDetectedCount); + ASSERT_EQ(mCallback->mError, (Error)8); + ASSERT_EQ(mCallback->mErrorVendorCode, 0); +} + +TEST_F(FakeFingerprintEngineTest, InteractionDetectAcquired) { + FingerprintHalProperties::enrollments({1, 2}); + FingerprintHalProperties::enrollment_hit(2); + FingerprintHalProperties::operation_detect_interaction_acquired("4,1013"); + int32_t prevCount = mCallback->mLastAcquiredCount; + mEngine.detectInteractionImpl(mCallback.get(), mCancel.get_future()); + ASSERT_EQ(1, mCallback->mInteractionDetectedCount); + ASSERT_EQ(prevCount + 2, mCallback->mLastAcquiredCount); + ASSERT_EQ(7, mCallback->mLastAcquiredInfo); + ASSERT_EQ(13, mCallback->mLastAcquiredVendorCode); +} + TEST_F(FakeFingerprintEngineTest, EnumerateEnrolled) { FingerprintHalProperties::enrollments({2, 4, 8}); mEngine.enumerateEnrollmentsImpl(mCallback.get()); @@ -290,6 +371,76 @@ TEST_F(FakeFingerprintEngineTest, RemoveEnrolled) { } } +TEST_F(FakeFingerprintEngineTest, parseIntSequence) { + std::vector seqV; + seqV = mEngine.parseIntSequence(""); + ASSERT_EQ(0, seqV.size()); + seqV = mEngine.parseIntSequence("2"); + ASSERT_EQ(1, seqV.size()); + ASSERT_EQ(2, seqV[0]); + seqV = mEngine.parseIntSequence("2,3,4"); + std::vector expV{2, 3, 4}; + ASSERT_EQ(expV, seqV); + seqV = mEngine.parseIntSequence("2,3,a"); + ASSERT_EQ(0, seqV.size()); + seqV = mEngine.parseIntSequence("2, 3, 4"); + ASSERT_EQ(expV, seqV); + seqV = mEngine.parseIntSequence("123,456"); + ASSERT_EQ(2, seqV.size()); + std::vector expV1{123, 456}; + ASSERT_EQ(expV1, seqV); + seqV = mEngine.parseIntSequence("12f3,456"); + ASSERT_EQ(0, seqV.size()); +} + +TEST_F(FakeFingerprintEngineTest, parseEnrollmentCaptureOk) { + std::vector> ecV; + ecV = mEngine.parseEnrollmentCapture("100,200,300"); + ASSERT_EQ(6, ecV.size()); + std::vector> expE{{100}, {200}, {300}}; + std::vector defC{1}; + for (int i = 0; i < ecV.size(); i += 2) { + ASSERT_EQ(expE[i / 2], ecV[i]); + ASSERT_EQ(defC, ecV[i + 1]); + } + ecV = mEngine.parseEnrollmentCapture("100"); + ASSERT_EQ(2, ecV.size()); + ASSERT_EQ(expE[0], ecV[0]); + ASSERT_EQ(defC, ecV[1]); + + ecV = mEngine.parseEnrollmentCapture("100-[5,6,7]"); + std::vector expC{5, 6, 7}; + ASSERT_EQ(2, ecV.size()); + for (int i = 0; i < ecV.size(); i += 2) { + ASSERT_EQ(expE[i / 2], ecV[i]); + ASSERT_EQ(expC, ecV[i + 1]); + } + ecV = mEngine.parseEnrollmentCapture("100-[5,6,7], 200, 300-[9,10]"); + std::vector> expC1{{5, 6, 7}, {1}, {9, 10}}; + ASSERT_EQ(6, ecV.size()); + for (int i = 0; i < ecV.size(); i += 2) { + ASSERT_EQ(expE[i / 2], ecV[i]); + ASSERT_EQ(expC1[i / 2], ecV[i + 1]); + } + ecV = mEngine.parseEnrollmentCapture("100-[5,6,7], 200-[2,1], 300-[9]"); + std::vector> expC2{{5, 6, 7}, {2, 1}, {9}}; + ASSERT_EQ(ecV.size(), 6); + for (int i = 0; i < ecV.size(); i += 2) { + ASSERT_EQ(expE[i / 2], ecV[i]); + ASSERT_EQ(expC2[i / 2], ecV[i + 1]); + } +} + +TEST_F(FakeFingerprintEngineTest, parseEnrollmentCaptureFail) { + std::vector badStr{"10c", "100-5", "100-[5,6,7", "100-5,6,7]", + "100,2x0,300", "200-[f]", "a,b"}; + std::vector> ecV; + for (const auto s : badStr) { + ecV = mEngine.parseEnrollmentCapture(s); + ASSERT_EQ(ecV.size(), 0); + } +} + } // namespace aidl::android::hardware::biometrics::fingerprint int main(int argc, char** argv) { diff --git a/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineUdfpsTest.cpp b/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineUdfpsTest.cpp index 485f401504..7c0021fb26 100644 --- a/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineUdfpsTest.cpp +++ b/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineUdfpsTest.cpp @@ -47,14 +47,10 @@ bool isDefaultLocation(SensorLocation& sc) { sc.sensorRadius == FakeFingerprintEngineUdfps::defaultSensorRadius && sc.display == ""); } -TEST_F(FakeFingerprintEngineUdfpsTest, getSensorLocation) { - FingerprintHalProperties::sensor_location(""); - SensorLocation sc = mEngine.getSensorLocation(); - ASSERT_TRUE(isDefaultLocation(sc)); - +TEST_F(FakeFingerprintEngineUdfpsTest, getSensorLocationOk) { auto loc = "100:200:30"; FingerprintHalProperties::sensor_location(loc); - sc = mEngine.getSensorLocation(); + SensorLocation sc = mEngine.getSensorLocation(); ASSERT_TRUE(sc.sensorLocationX == 100); ASSERT_TRUE(sc.sensorLocationY == 200); ASSERT_TRUE(sc.sensorRadius == 30); @@ -66,8 +62,14 @@ TEST_F(FakeFingerprintEngineUdfpsTest, getSensorLocation) { ASSERT_TRUE(sc.sensorLocationY == 200); ASSERT_TRUE(sc.sensorRadius == 30); ASSERT_TRUE(sc.display == "screen1"); +} - loc = "100"; +TEST_F(FakeFingerprintEngineUdfpsTest, getSensorLocationBad) { + FingerprintHalProperties::sensor_location(""); + SensorLocation sc = mEngine.getSensorLocation(); + ASSERT_TRUE(isDefaultLocation(sc)); + + auto loc = "100"; FingerprintHalProperties::sensor_location(loc); sc = mEngine.getSensorLocation(); ASSERT_TRUE(isDefaultLocation(sc));