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
diff --git a/biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp b/biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp
index 68a1f26..651c9dc 100644
--- a/biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp
+++ b/biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp
@@ -56,43 +56,58 @@
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 "<id>:<progress_ms>,<progress_ms>,...:<result>
+ // Format is "<id>:<progress_ms-[acquiredInfo..]>,...:<result>
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 @@
const std::future<void>& 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 @@
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 @@
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::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 @@
return {0 /* displayId (not used) */, 0 /* sensorLocationX */, 0 /* sensorLocationY */,
0 /* sensorRadius */, "" /* display */};
}
+
+std::vector<int32_t> FakeFingerprintEngine::parseIntSequence(const std::string& str,
+ const std::string& sep) {
+ std::vector<std::string> seqs = Util::split(str, sep);
+ std::vector<int32_t> 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<std::vector<int32_t>> FakeFingerprintEngine::parseEnrollmentCapture(
+ const std::string& str) {
+ std::vector<int32_t> defaultAcquiredInfo = {(int32_t)AcquiredInfo::GOOD};
+ std::vector<std::vector<int32_t>> 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<int32_t> duration{0};
+ if (!ParseInt(durationStr, &duration[0])) break;
+ res.push_back(duration);
+ if (!acquiredStr.empty()) {
+ std::vector<int32_t> 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<AcquiredInfo, int32_t> FakeFingerprintEngine::convertAcquiredInfo(int32_t code) {
+ std::pair<AcquiredInfo, int32_t> 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<Error, int32_t> FakeFingerprintEngine::convertError(int32_t code) {
+ std::pair<Error, int32_t> 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 922781c..74e7caf 100644
--- a/biometrics/fingerprint/aidl/default/Fingerprint.cpp
+++ b/biometrics/fingerprint/aidl/default/Fingerprint.cpp
@@ -65,8 +65,18 @@
{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 @@
*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 bb6bb48..ad471f7 100644
--- a/biometrics/fingerprint/aidl/default/README.md
+++ b/biometrics/fingerprint/aidl/default/README.md
@@ -65,7 +65,7 @@
$ 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 @@
$ 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 9dfb74d..fa21663 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 @@
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"
@@ -14,6 +14,21 @@
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
access: ReadWrite
@@ -28,7 +43,18 @@
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"
@@ -37,12 +63,24 @@
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
prop_name: "vendor.fingerprint.virtual.operation_authenticate_fails"
@@ -54,9 +92,22 @@
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 @@
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"
@@ -76,12 +128,24 @@
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
access: ReadWrite
diff --git a/biometrics/fingerprint/aidl/default/fingerprint.sysprop b/biometrics/fingerprint/aidl/default/fingerprint.sysprop
index 85e93b0..9b8fada 100644
--- a/biometrics/fingerprint/aidl/default/fingerprint.sysprop
+++ b/biometrics/fingerprint/aidl/default/fingerprint.sysprop
@@ -32,8 +32,12 @@
api_name: "enrollment_hit"
}
-# the next enrollment in the format: "<id>:<delay>,<delay>,...:<result>"
-# for example: "2:0:true"
+# the next enrollment in the format of:
+# "<id>:<delay>,<delay>,...:<result>"
+# <delay> = <duration-[acquiredInfos]>
+# [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 @@
# 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 @@
# 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 @@
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 @@
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
# <x>:<y>:<radius> in pixel
prop {
@@ -143,3 +160,99 @@
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 d7df818..22b1744 100644
--- a/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h
+++ b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h
@@ -59,7 +59,17 @@
virtual SensorLocation defaultSensorLocation();
+ std::vector<int32_t> parseIntSequence(const std::string& str, const std::string& sep = ",");
+
+ std::vector<std::vector<int32_t>> 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<AcquiredInfo, int32_t> convertAcquiredInfo(int32_t code);
+ std::pair<Error, int32_t> 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 8696d26..32d01f4 100644
--- a/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineTest.cpp
+++ b/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineTest.cpp
@@ -38,8 +38,9 @@
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 @@
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 @@
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 @@
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 @@
mCallback = ndk::SharedRefBase::make<TestSessionCallback>();
}
+ void TearDown() override {
+ FingerprintHalProperties::operation_authenticate_error(0);
+ FingerprintHalProperties::operation_detect_interaction_error(0);
+ FingerprintHalProperties::operation_authenticate_acquired("");
+ }
+
FakeFingerprintEngine mEngine;
std::shared_ptr<TestSessionCallback> mCallback;
std::promise<void> mCancel;
@@ -135,11 +149,13 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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, parseIntSequence) {
+ std::vector<int32_t> 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<int32_t> 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<int32_t> expV1{123, 456};
+ ASSERT_EQ(expV1, seqV);
+ seqV = mEngine.parseIntSequence("12f3,456");
+ ASSERT_EQ(0, seqV.size());
+}
+
+TEST_F(FakeFingerprintEngineTest, parseEnrollmentCaptureOk) {
+ std::vector<std::vector<int32_t>> ecV;
+ ecV = mEngine.parseEnrollmentCapture("100,200,300");
+ ASSERT_EQ(6, ecV.size());
+ std::vector<std::vector<int32_t>> expE{{100}, {200}, {300}};
+ std::vector<int32_t> 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<int32_t> 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<std::vector<int32_t>> 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<std::vector<int32_t>> 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<std::string> badStr{"10c", "100-5", "100-[5,6,7", "100-5,6,7]",
+ "100,2x0,300", "200-[f]", "a,b"};
+ std::vector<std::vector<int32_t>> 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 485f401..7c0021f 100644
--- a/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineUdfpsTest.cpp
+++ b/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineUdfpsTest.cpp
@@ -47,14 +47,10 @@
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 @@
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));