Fingerprint virtual HAL checkin (part 3)
- support randomization
- display touch events
- lockout
- cmd and dumpsys
Bug: 230515082
Bug: 230515086
Test: atest FakeFingerprintEngineTest
atest FakeFingerprintEngineUdfpsTest
atest --no-bazel-mode VtsHalBiometricsFingerprintTargetTest
Change-Id: Ia5399c86b7fec90b41d426c2f82cb257f4dc9a8a
diff --git a/biometrics/common/util/include/util/Util.h b/biometrics/common/util/include/util/Util.h
index 29ec0f8..da19dc6 100644
--- a/biometrics/common/util/include/util/Util.h
+++ b/biometrics/common/util/include/util/Util.h
@@ -40,7 +40,7 @@
// by parts of the UI or fail if there is no latency. For example, the
// Face settings page constantly runs auth and the enrollment UI uses a
// cancel/restart cycle that requires some latency while the activities change.
-#define DEFAULT_LATENCY 800
+#define DEFAULT_LATENCY 400
class Util {
public:
@@ -66,4 +66,4 @@
}
};
-} // namespace aidl::android::hardware::biometrics
\ No newline at end of file
+} // namespace aidl::android::hardware::biometrics
diff --git a/biometrics/fingerprint/aidl/default/Android.bp b/biometrics/fingerprint/aidl/default/Android.bp
index dc0199c..77c74e1 100644
--- a/biometrics/fingerprint/aidl/default/Android.bp
+++ b/biometrics/fingerprint/aidl/default/Android.bp
@@ -15,6 +15,7 @@
vintf_fragments: ["fingerprint-example.xml"],
local_include_dirs: ["include"],
srcs: [
+ "FakeLockoutTracker.cpp",
"FakeFingerprintEngine.cpp",
"FakeFingerprintEngineRear.cpp",
"FakeFingerprintEngineUdfps.cpp",
@@ -40,6 +41,7 @@
srcs: [
"tests/FakeFingerprintEngineTest.cpp",
"FakeFingerprintEngine.cpp",
+ "FakeLockoutTracker.cpp",
],
shared_libs: [
"libbase",
@@ -65,6 +67,31 @@
"tests/FakeFingerprintEngineUdfpsTest.cpp",
"FakeFingerprintEngineUdfps.cpp",
"FakeFingerprintEngine.cpp",
+ "FakeLockoutTracker.cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder_ndk",
+ "android.hardware.biometrics.common.thread",
+ ],
+ static_libs: [
+ "libandroid.hardware.biometrics.fingerprint.VirtualProps",
+ "android.hardware.biometrics.fingerprint-V2-ndk",
+ "android.hardware.biometrics.common-V2-ndk",
+ "android.hardware.keymaster-V3-ndk",
+ "android.hardware.biometrics.common.util",
+ ],
+ vendor: true,
+ test_suites: ["general-tests"],
+ require_root: true,
+}
+
+cc_test {
+ name: "android.hardware.biometrics.fingerprint.FakeLockoutTrackerTest",
+ local_include_dirs: ["include"],
+ srcs: [
+ "tests/FakeLockoutTrackerTest.cpp",
+ "FakeLockoutTracker.cpp",
],
shared_libs: [
"libbase",
diff --git a/biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp b/biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp
index 651c9dc..90ec8f2 100644
--- a/biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp
+++ b/biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -15,6 +15,7 @@
*/
#include "FakeFingerprintEngine.h"
+#include <regex>
#include "Fingerprint.h"
#include <android-base/logging.h>
@@ -47,7 +48,7 @@
void FakeFingerprintEngine::enrollImpl(ISessionCallback* cb,
const keymaster::HardwareAuthToken& hat,
const std::future<void>& cancel) {
- BEGIN_OP(FingerprintHalProperties::operation_enroll_latency().value_or(DEFAULT_LATENCY));
+ BEGIN_OP(getLatency(FingerprintHalProperties::operation_enroll_latency()));
// Do proper HAT verification in the real implementation.
if (hat.mac.empty()) {
@@ -117,7 +118,7 @@
void FakeFingerprintEngine::authenticateImpl(ISessionCallback* cb, int64_t /* operationId */,
const std::future<void>& cancel) {
- BEGIN_OP(FingerprintHalProperties::operation_authenticate_latency().value_or(DEFAULT_LATENCY));
+ BEGIN_OP(getLatency(FingerprintHalProperties::operation_authenticate_latency()));
int64_t now = Util::getSystemNanoTime();
int64_t duration = FingerprintHalProperties::operation_authenticate_duration().value_or(10);
@@ -131,10 +132,23 @@
return;
}
+ // got lockout?
+ FakeLockoutTracker::LockoutMode lockoutMode = mLockoutTracker.getMode();
+ if (lockoutMode == FakeLockoutTracker::LockoutMode::kPermanent) {
+ LOG(ERROR) << "Fail: lockout permanent";
+ cb->onLockoutPermanent();
+ return;
+ } else if (lockoutMode == FakeLockoutTracker::LockoutMode::kTimed) {
+ int64_t timeLeft = mLockoutTracker.getLockoutTimeLeft();
+ LOG(ERROR) << "Fail: lockout timed " << timeLeft;
+ cb->onLockoutTimed(timeLeft);
+ }
+
int i = 0;
do {
if (FingerprintHalProperties::operation_authenticate_fails().value_or(false)) {
LOG(ERROR) << "Fail: operation_authenticate_fails";
+ mLockoutTracker.addFailedAttempt();
cb->onAuthenticationFailed();
return;
}
@@ -174,20 +188,30 @@
auto isEnrolled = std::find(enrolls.begin(), enrolls.end(), id) != enrolls.end();
if (id > 0 && isEnrolled) {
cb->onAuthenticationSucceeded(id, {} /* hat */);
+ mLockoutTracker.reset();
return;
} else {
LOG(ERROR) << "Fail: fingerprint not enrolled";
cb->onAuthenticationFailed();
+ mLockoutTracker.addFailedAttempt();
}
}
void FakeFingerprintEngine::detectInteractionImpl(ISessionCallback* cb,
const std::future<void>& cancel) {
- BEGIN_OP(FingerprintHalProperties::operation_detect_interaction_latency().value_or(
- DEFAULT_LATENCY));
+ BEGIN_OP(getLatency(FingerprintHalProperties::operation_detect_interaction_latency()));
int64_t duration =
FingerprintHalProperties::operation_detect_interaction_duration().value_or(10);
+
+ auto detectInteractionSupported =
+ FingerprintHalProperties::detect_interaction().value_or(false);
+ if (!detectInteractionSupported) {
+ LOG(ERROR) << "Detect interaction is not supported";
+ cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */);
+ return;
+ }
+
auto acquired = FingerprintHalProperties::operation_detect_interaction_acquired().value_or("1");
auto acquiredInfos = parseIntSequence(acquired);
int N = acquiredInfos.size();
@@ -308,6 +332,7 @@
}
FingerprintHalProperties::lockout(false);
cb->onLockoutCleared();
+ mLockoutTracker.reset();
}
ndk::ScopedAStatus FakeFingerprintEngine::onPointerDownImpl(int32_t /*pointerId*/, int32_t /*x*/,
@@ -383,49 +408,52 @@
return res;
}
-std::vector<std::vector<int32_t>> FakeFingerprintEngine::parseEnrollmentCapture(
- const std::string& str) {
+bool FakeFingerprintEngine::parseEnrollmentCaptureSingle(const std::string& str,
+ std::vector<std::vector<int32_t>>& res) {
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);
+ do {
+ std::smatch sms;
+ // Parses strings like "1000-[5,1]" or "500"
+ std::regex ex("((\\d+)(-\\[([\\d|,]+)\\])?)");
+ if (!regex_match(str.cbegin(), str.cend(), sms, ex)) break;
+ int32_t duration;
+ if (!ParseInt(sms.str(2), &duration)) break;
+ res.push_back({duration});
+ if (!sms.str(4).empty()) {
+ auto acqv = parseIntSequence(sms.str(4));
+ if (acqv.empty()) break;
+ res.push_back(acqv);
} else
res.push_back(defaultAcquiredInfo);
+ aborted = false;
+ } while (0);
- i = found + 1;
- if (found == std::string::npos || found == N - 1) aborted = false;
+ return !aborted;
+}
+
+std::vector<std::vector<int32_t>> FakeFingerprintEngine::parseEnrollmentCapture(
+ const std::string& str) {
+ std::vector<std::vector<int32_t>> res;
+
+ std::string s(str);
+ s.erase(std::remove_if(s.begin(), s.end(), ::isspace), s.end());
+ bool aborted = false;
+ std::smatch sms;
+ // Parses strings like "1000-[5,1],500,800-[6,5,1]"
+ // ---------- --- -----------
+ // into parts: A B C
+ while (regex_search(s, sms, std::regex("^(,)?(\\d+(-\\[[\\d|,]+\\])?)"))) {
+ if (!parseEnrollmentCaptureSingle(sms.str(2), res)) {
+ aborted = true;
+ break;
+ }
+ s = sms.suffix();
}
-
- if (aborted) {
- LOG(ERROR) << "Failed to parse enrollment captures:" + str;
+ if (aborted || s.length() != 0) {
res.clear();
+ LOG(ERROR) << "Failed to parse enrollment captures:" + str;
}
return res;
@@ -455,4 +483,34 @@
return res;
}
+int32_t FakeFingerprintEngine::getLatency(
+ const std::vector<std::optional<std::int32_t>>& latencyIn) {
+ int32_t res = DEFAULT_LATENCY;
+
+ std::vector<int32_t> latency;
+ for (auto x : latencyIn)
+ if (x.has_value()) latency.push_back(*x);
+
+ switch (latency.size()) {
+ case 0:
+ break;
+ case 1:
+ res = latency[0];
+ break;
+ case 2:
+ res = getRandomInRange(latency[0], latency[1]);
+ break;
+ default:
+ LOG(ERROR) << "ERROR: unexpected input of size " << latency.size();
+ break;
+ }
+
+ return res;
+}
+
+int32_t FakeFingerprintEngine::getRandomInRange(int32_t bound1, int32_t bound2) {
+ std::uniform_int_distribution<int32_t> dist(std::min(bound1, bound2), std::max(bound1, bound2));
+ return dist(mRandom);
+}
+
} // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/FakeFingerprintEngineUdfps.cpp b/biometrics/fingerprint/aidl/default/FakeFingerprintEngineUdfps.cpp
index d8579a4..3cdfc70 100644
--- a/biometrics/fingerprint/aidl/default/FakeFingerprintEngineUdfps.cpp
+++ b/biometrics/fingerprint/aidl/default/FakeFingerprintEngineUdfps.cpp
@@ -23,10 +23,16 @@
#include "util/CancellationSignal.h"
#include "util/Util.h"
+#undef LOG_TAG
+#define LOG_TAG "FingerprintVirtualHalUdfps"
+
using namespace ::android::fingerprint::virt;
namespace aidl::android::hardware::biometrics::fingerprint {
+FakeFingerprintEngineUdfps::FakeFingerprintEngineUdfps()
+ : FakeFingerprintEngine(), mWorkMode(WorkMode::kIdle), mPointerDownTime(0), mUiReadyTime(0) {}
+
SensorLocation FakeFingerprintEngineUdfps::defaultSensorLocation() {
return {0 /* displayId (not used) */, defaultSensorLocationX /* sensorLocationX */,
defaultSensorLocationY /* sensorLocationY */, defaultSensorRadius /* sensorRadius */,
@@ -37,22 +43,95 @@
int32_t /*x*/, int32_t /*y*/,
float /*minor*/, float /*major*/) {
BEGIN_OP(0);
-
- // TODO(b/230515082): if need to handle display touch events
-
+ // verify whetehr touch coordinates/area matching sensor location ?
+ mPointerDownTime = Util::getSystemNanoTime();
+ if (FingerprintHalProperties::control_illumination().value_or(false)) {
+ fingerDownAction();
+ }
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus FakeFingerprintEngineUdfps::onPointerUpImpl(int32_t /*pointerId*/) {
BEGIN_OP(0);
- // TODO(b/230515082)
+ mUiReadyTime = 0;
+ mPointerDownTime = 0;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus FakeFingerprintEngineUdfps::onUiReadyImpl() {
BEGIN_OP(0);
- // TODO(b/230515082)
+
+ if (Util::hasElapsed(mPointerDownTime, uiReadyTimeoutInMs * 100)) {
+ LOG(ERROR) << "onUiReady() arrives too late after onPointerDown()";
+ } else {
+ fingerDownAction();
+ }
return ndk::ScopedAStatus::ok();
}
+void FakeFingerprintEngineUdfps::fingerDownAction() {
+ switch (mWorkMode) {
+ case WorkMode::kAuthenticate:
+ onAuthenticateFingerDown();
+ break;
+ case WorkMode::kEnroll:
+ onEnrollFingerDown();
+ break;
+ case WorkMode::kDetectInteract:
+ onDetectInteractFingerDown();
+ break;
+ default:
+ LOG(WARNING) << "unexpected call: onUiReady()";
+ break;
+ }
+
+ mUiReadyTime = 0;
+ mPointerDownTime = 0;
+}
+
+void FakeFingerprintEngineUdfps::onAuthenticateFingerDown() {
+ FakeFingerprintEngine::authenticateImpl(mCb, mOperationId, mCancelVec[0]);
+}
+
+void FakeFingerprintEngineUdfps::onEnrollFingerDown() {
+ // Any use case to emulate display touch for each capture during enrollment?
+ FakeFingerprintEngine::enrollImpl(mCb, mHat, mCancelVec[0]);
+}
+
+void FakeFingerprintEngineUdfps::onDetectInteractFingerDown() {
+ FakeFingerprintEngine::detectInteractionImpl(mCb, mCancelVec[0]);
+}
+
+void FakeFingerprintEngineUdfps::enrollImpl(ISessionCallback* cb,
+ const keymaster::HardwareAuthToken& hat,
+ const std::future<void>& cancel) {
+ updateContext(WorkMode::kEnroll, cb, const_cast<std::future<void>&>(cancel), 0, hat);
+}
+
+void FakeFingerprintEngineUdfps::authenticateImpl(ISessionCallback* cb, int64_t operationId,
+ const std::future<void>& cancel) {
+ updateContext(WorkMode::kAuthenticate, cb, const_cast<std::future<void>&>(cancel), operationId,
+ keymaster::HardwareAuthToken());
+}
+
+void FakeFingerprintEngineUdfps::detectInteractionImpl(ISessionCallback* cb,
+ const std::future<void>& cancel) {
+ updateContext(WorkMode::kDetectInteract, cb, const_cast<std::future<void>&>(cancel), 0,
+ keymaster::HardwareAuthToken());
+}
+
+void FakeFingerprintEngineUdfps::updateContext(WorkMode mode, ISessionCallback* cb,
+ std::future<void>& cancel, int64_t operationId,
+ const keymaster::HardwareAuthToken& hat) {
+ mPointerDownTime = 0;
+ mUiReadyTime = 0;
+ mCancelVec.clear();
+
+ mCancelVec.push_back(std::move(cancel));
+ mWorkMode = mode;
+ mCb = cb;
+ mOperationId = operationId;
+ mHat = hat;
+}
+
} // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/FakeLockoutTracker.cpp b/biometrics/fingerprint/aidl/default/FakeLockoutTracker.cpp
new file mode 100644
index 0000000..5996406
--- /dev/null
+++ b/biometrics/fingerprint/aidl/default/FakeLockoutTracker.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include "FakeLockoutTracker.h"
+#include <fingerprint.sysprop.h>
+#include "util/Util.h"
+
+using namespace ::android::fingerprint::virt;
+
+namespace aidl::android::hardware::biometrics::fingerprint {
+
+void FakeLockoutTracker::reset() {
+ mFailedCount = 0;
+ mLockoutTimedStart = 0;
+ mCurrentMode = LockoutMode::kNone;
+}
+
+void FakeLockoutTracker::addFailedAttempt() {
+ bool enabled = FingerprintHalProperties::lockout_enable().value_or(false);
+ if (enabled) {
+ mFailedCount++;
+ int32_t lockoutTimedThreshold =
+ FingerprintHalProperties::lockout_timed_threshold().value_or(5);
+ int32_t lockoutPermanetThreshold =
+ FingerprintHalProperties::lockout_permanent_threshold().value_or(20);
+ if (mFailedCount >= lockoutPermanetThreshold) {
+ mCurrentMode = LockoutMode::kPermanent;
+ FingerprintHalProperties::lockout(true);
+ } else if (mFailedCount >= lockoutTimedThreshold) {
+ if (mCurrentMode == LockoutMode::kNone) {
+ mCurrentMode = LockoutMode::kTimed;
+ mLockoutTimedStart = Util::getSystemNanoTime();
+ }
+ }
+ } else {
+ reset();
+ }
+}
+
+FakeLockoutTracker::LockoutMode FakeLockoutTracker::getMode() {
+ if (mCurrentMode == LockoutMode::kTimed) {
+ int32_t lockoutTimedDuration =
+ FingerprintHalProperties::lockout_timed_duration().value_or(10 * 100);
+ if (Util::hasElapsed(mLockoutTimedStart, lockoutTimedDuration)) {
+ mCurrentMode = LockoutMode::kNone;
+ mLockoutTimedStart = 0;
+ }
+ }
+
+ return mCurrentMode;
+}
+
+int64_t FakeLockoutTracker::getLockoutTimeLeft() {
+ int64_t res = 0;
+
+ if (mLockoutTimedStart > 0) {
+ auto now = Util::getSystemNanoTime();
+ auto left = now - mLockoutTimedStart;
+ res = (left > 0) ? (left / 1000000LL) : 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 74e7caf..be93224 100644
--- a/biometrics/fingerprint/aidl/default/Fingerprint.cpp
+++ b/biometrics/fingerprint/aidl/default/Fingerprint.cpp
@@ -15,11 +15,13 @@
*/
#include "Fingerprint.h"
-
-#include <fingerprint.sysprop.h>
#include "Session.h"
+#include <fingerprint.sysprop.h>
+
+#include <android-base/file.h>
#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
using namespace ::android::fingerprint::virt;
@@ -64,7 +66,6 @@
{HW_COMPONENT_ID, HW_VERSION, FW_VERSION, SERIAL_NUMBER, "" /* softwareVersion */},
{SW_COMPONENT_ID, "" /* hardwareVersion */, "" /* firmwareVersion */,
"" /* serialNumber */, SW_VERSION}};
-
auto sensorId = FingerprintHalProperties::sensor_id().value_or(SENSOR_ID);
auto sensorStrength =
FingerprintHalProperties::sensor_strength().value_or((int)SENSOR_STRENGTH);
@@ -80,7 +81,8 @@
SensorLocation sensorLocation = mEngine->getSensorLocation();
- LOG(INFO) << "sensor type:" << (int)mSensorType << " location:" << sensorLocation.toString();
+ LOG(INFO) << "sensor type:" << ::android::internal::ToString(mSensorType)
+ << " location:" << sensorLocation.toString();
*out = {{commonProps,
mSensorType,
@@ -104,4 +106,96 @@
return ndk::ScopedAStatus::ok();
}
+binder_status_t Fingerprint::dump(int fd, const char** /*args*/, uint32_t numArgs) {
+ if (fd < 0) {
+ LOG(ERROR) << "Fingerprint::dump fd invalid: " << fd;
+ return STATUS_BAD_VALUE;
+ } else {
+ LOG(INFO) << "Fingerprint::dump fd:" << fd << "numArgs:" << numArgs;
+ }
+
+ dprintf(fd, "----- FingerprintVirtualHal::dump -----\n");
+ std::vector<SensorProps> sps(1);
+ getSensorProps(&sps);
+ for (auto& sp : sps) {
+ ::android::base::WriteStringToFd(sp.toString(), fd);
+ }
+ ::android::base::WriteStringToFd(mEngine->toString(), fd);
+
+ fsync(fd);
+ return STATUS_OK;
+}
+
+binder_status_t Fingerprint::handleShellCommand(int in, int out, int err, const char** args,
+ uint32_t numArgs) {
+ LOG(INFO) << "Fingerprint::handleShellCommand in:" << in << " out:" << out << " err:" << err
+ << " numArgs:" << numArgs;
+
+ if (numArgs == 0) {
+ LOG(INFO) << "Fingerprint::handleShellCommand: available commands";
+ onHelp(out);
+ return STATUS_OK;
+ }
+
+ for (auto&& str : std::vector<std::string_view>(args, args + numArgs)) {
+ std::string option = str.data();
+ if (option.find("clearconfig") != std::string::npos ||
+ option.find("resetconfig") != std::string::npos) {
+ resetConfigToDefault();
+ }
+ if (option.find("help") != std::string::npos) {
+ onHelp(out);
+ }
+ }
+
+ return STATUS_OK;
+}
+
+void Fingerprint::onHelp(int fd) {
+ dprintf(fd, "Virtual HAL commands:\n");
+ dprintf(fd, " help: print this help\n");
+ dprintf(fd, " resetconfig: reset all configuration to default\n");
+ dprintf(fd, "\n");
+ fsync(fd);
+}
+
+void Fingerprint::resetConfigToDefault() {
+ LOG(INFO) << "reset virtual HAL configuration to default";
+#define RESET_CONFIG_O(__NAME__) \
+ if (FingerprintHalProperties::__NAME__()) FingerprintHalProperties::__NAME__(std::nullopt)
+#define RESET_CONFIG_V(__NAME__) \
+ if (!FingerprintHalProperties::__NAME__().empty()) \
+ FingerprintHalProperties::__NAME__({std::nullopt})
+
+ RESET_CONFIG_O(type);
+ RESET_CONFIG_V(enrollments);
+ RESET_CONFIG_O(enrollment_hit);
+ RESET_CONFIG_O(authenticator_id);
+ RESET_CONFIG_O(challenge);
+ RESET_CONFIG_O(lockout);
+ RESET_CONFIG_O(operation_authenticate_fails);
+ RESET_CONFIG_O(operation_detect_interaction_error);
+ RESET_CONFIG_O(operation_enroll_error);
+ RESET_CONFIG_V(operation_authenticate_latency);
+ RESET_CONFIG_V(operation_detect_interaction_latency);
+ RESET_CONFIG_V(operation_enroll_latency);
+ RESET_CONFIG_O(operation_authenticate_duration);
+ RESET_CONFIG_O(operation_authenticate_error);
+ RESET_CONFIG_O(sensor_location);
+ RESET_CONFIG_O(operation_authenticate_acquired);
+ RESET_CONFIG_O(operation_detect_interaction_duration);
+ RESET_CONFIG_O(operation_detect_interaction_acquired);
+ RESET_CONFIG_O(sensor_id);
+ RESET_CONFIG_O(sensor_strength);
+ RESET_CONFIG_O(max_enrollments);
+ RESET_CONFIG_O(navigation_guesture);
+ RESET_CONFIG_O(detect_interaction);
+ RESET_CONFIG_O(display_touch);
+ RESET_CONFIG_O(control_illumination);
+ RESET_CONFIG_O(lockout_enable);
+ RESET_CONFIG_O(lockout_timed_threshold);
+ RESET_CONFIG_O(lockout_timed_duration);
+ RESET_CONFIG_O(lockout_permanent_threshold);
+}
+
} // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/README.md b/biometrics/fingerprint/aidl/default/README.md
index ad471f7..49b6c9d 100644
--- a/biometrics/fingerprint/aidl/default/README.md
+++ b/biometrics/fingerprint/aidl/default/README.md
@@ -57,6 +57,7 @@
```shell
$ adb shell setprop vendor.fingerprint.virtual.next_enrollment 1:100,100,100:true
```
+
3. Navigate to `Settings -> Security -> Fingerprint Unlock` and follow the
prompts.
4. Verify the enrollments in the UI:
@@ -119,6 +120,38 @@
```
For vendor specific error, errorCode = 1000 + vendorErrorCode
+## Latency Insertion
+Three HAL operations (authenticate, enrollment and detect interaction) latency can be optionally specified in multiple ways
+1. default latency is fixed at 400 ms if not specified via sysprop
+2. specify authenticate operation latency to 900 ms
+ ```shell adb shell setprop vendor.fingerprint.virtual.operation_authenticate_latency 900```
+3. specify authenticate operation latency between 600 to 1200 ms in unifrom distribution
+ ```shelladb shell setprop vendor.fingerprint.virtual.operation_authenticate_latency 600,1200```
+
+## Lockout
+To force the device into lockout state
+```shell
+$ adb shell setprop persist.vendor.fingerprint.virtual.lockout true
+```
+To test permanent lockout based on the failed authentication attempts (e.g. 7)
+```shell
+$ adb shell setprop persist.vendor.fingerprint.virtual.lockout_permanent_threshold 7
+$ adb shell setprop persist.vendor.fingerprint.virtual.lockout_enable true
+```
+To test timed lockout based on the failed authentication attempts (e.g. 8 seconds on 5 attempts)
+```shell
+$ adb shell setprop persist.vendor.fingerprint.virtual.lockout_timed_duration 8000
+$ adb shell setprop persist.vendor.fingerprint.virtual.lockout_timed_threshold 5
+$ adb shell setprop persist.vendor.fingerprint.virtual.lockout_enable true
+```
+
+## Reset all configurations to default
+The following command will reset virtual configurations (related system properties) to default value.
+```shell
+$ adb shell cmd android.hardware.biometrics.fingerprint.IFingerprint/virtual resetconfig
+$ adb reboot
+```
+
## View HAL State
To view all the properties of the HAL (see `fingerprint.sysprop` file for the API):
@@ -126,3 +159,7 @@
```shell
$ adb shell getprop | grep vendor.fingerprint.virtual
```
+To dump virtual HAL internal data
+```shell
+adb shell dumpsys android.hardware.biometrics.fingerprint.IFingerprint/virtual
+```
diff --git a/biometrics/fingerprint/aidl/default/Session.cpp b/biometrics/fingerprint/aidl/default/Session.cpp
index e51f677..7ab5af3 100644
--- a/biometrics/fingerprint/aidl/default/Session.cpp
+++ b/biometrics/fingerprint/aidl/default/Session.cpp
@@ -20,6 +20,9 @@
#include "util/CancellationSignal.h"
+#undef LOG_TAG
+#define LOG_TAG "FingerprintVirtualHalSession"
+
namespace aidl::android::hardware::biometrics::fingerprint {
Session::Session(int sensorId, int userId, std::shared_ptr<ISessionCallback> cb,
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 fa21663..e69de29 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
@@ -1,155 +0,0 @@
-props {
- owner: Vendor
- module: "android.fingerprint.virt.FingerprintHalProperties"
- prop {
- api_name: "authenticator_id"
- type: Long
- access: ReadWrite
- prop_name: "persist.vendor.fingerprint.virtual.authenticator_id"
- }
- prop {
- api_name: "challenge"
- type: Long
- 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
- access: ReadWrite
- prop_name: "vendor.fingerprint.virtual.enrollment_hit"
- }
- prop {
- api_name: "enrollments"
- type: IntegerList
- access: ReadWrite
- prop_name: "persist.vendor.fingerprint.virtual.enrollments"
- }
- prop {
- api_name: "lockout"
- access: ReadWrite
- 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"
- type: String
- 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
- prop_name: "vendor.fingerprint.virtual.operation_authenticate_fails"
- }
- prop {
- api_name: "operation_authenticate_latency"
- type: Integer
- access: ReadWrite
- prop_name: "vendor.fingerprint.virtual.operation_authenticate_latency"
- }
- prop {
- api_name: "operation_detect_interaction_acquired"
- type: String
- access: ReadWrite
- 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"
- type: Integer
- access: ReadWrite
- prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_latency"
- }
- prop {
- api_name: "operation_enroll_error"
- type: Integer
- access: ReadWrite
- prop_name: "vendor.fingerprint.virtual.operation_enroll_error"
- }
- prop {
- api_name: "operation_enroll_latency"
- type: Integer
- 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
- access: ReadWrite
- prop_name: "persist.vendor.fingerprint.virtual.type"
- enum_values: "default|rear|udfps|side"
- }
-}
diff --git a/biometrics/fingerprint/aidl/default/fingerprint.sysprop b/biometrics/fingerprint/aidl/default/fingerprint.sysprop
index 9b8fada..6a6c297 100644
--- a/biometrics/fingerprint/aidl/default/fingerprint.sysprop
+++ b/biometrics/fingerprint/aidl/default/fingerprint.sysprop
@@ -7,7 +7,7 @@
prop {
prop_name: "persist.vendor.fingerprint.virtual.type"
type: String
- scope: Public
+ scope: Internal
access: ReadWrite
enum_values: "default|rear|udfps|side"
api_name: "type"
@@ -17,7 +17,7 @@
prop {
prop_name: "persist.vendor.fingerprint.virtual.enrollments"
type: IntegerList
- scope: Public
+ scope: Internal
access: ReadWrite
api_name: "enrollments"
}
@@ -27,7 +27,7 @@
prop {
prop_name: "vendor.fingerprint.virtual.enrollment_hit"
type: Integer
- scope: Public
+ scope: Internal
access: ReadWrite
api_name: "enrollment_hit"
}
@@ -42,7 +42,7 @@
prop {
prop_name: "vendor.fingerprint.virtual.next_enrollment"
type: String
- scope: Public
+ scope: Internal
access: ReadWrite
api_name: "next_enrollment"
}
@@ -51,7 +51,7 @@
prop {
prop_name: "persist.vendor.fingerprint.virtual.authenticator_id"
type: Long
- scope: Public
+ scope: Internal
access: ReadWrite
api_name: "authenticator_id"
}
@@ -60,25 +60,16 @@
prop {
prop_name: "vendor.fingerprint.virtual.challenge"
type: Long
- scope: Public
+ scope: Internal
access: ReadWrite
api_name: "challenge"
}
-# if locked out
-prop {
- prop_name: "persist.vendor.fingerprint.virtual.lockout"
- type: Boolean
- scope: Public
- access: ReadWrite
- api_name: "lockout"
-}
-
# force all authenticate operations to fail
prop {
prop_name: "vendor.fingerprint.virtual.operation_authenticate_fails"
type: Boolean
- scope: Public
+ scope: Internal
access: ReadWrite
api_name: "operation_authenticate_fails"
}
@@ -91,7 +82,7 @@
prop {
prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_error"
type: Integer
- scope: Public
+ scope: Internal
access: ReadWrite
api_name: "operation_detect_interaction_error"
}
@@ -100,34 +91,40 @@
prop {
prop_name: "vendor.fingerprint.virtual.operation_enroll_error"
type: Integer
- scope: Public
+ scope: Internal
access: ReadWrite
api_name: "operation_enroll_error"
}
# add a latency to authentication operations
+# default to 400ms
+# [x] = x ms
+# [x,y] = randomly between x and y ms
+# others = invalid
prop {
prop_name: "vendor.fingerprint.virtual.operation_authenticate_latency"
- type: Integer
- scope: Public
+ type: IntegerList
+ scope: Internal
access: ReadWrite
api_name: "operation_authenticate_latency"
}
# add a latency to detectInteraction operations
+# refer to `operation_authenticate_latency` above for usage
prop {
prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_latency"
- type: Integer
- scope: Public
+ type: IntegerList
+ scope: Internal
access: ReadWrite
api_name: "operation_detect_interaction_latency"
}
# add a latency to enroll operations
+# refer to `operation_authenticate_latency` above for usage
prop {
prop_name: "vendor.fingerprint.virtual.operation_enroll_latency"
- type: Integer
- scope: Public
+ type: IntegerList
+ scope: Internal
access: ReadWrite
api_name: "operation_enroll_latency"
}
@@ -137,7 +134,7 @@
prop {
prop_name: "vendor.fingerprint.virtual.operation_authenticate_duration"
type: Integer
- scope: Public
+ scope: Internal
access: ReadWrite
api_name: "operation_authenticate_duration"
}
@@ -146,7 +143,7 @@
prop {
prop_name: "vendor.fingerprint.virtual.operation_authenticate_error"
type: Integer
- scope: Public
+ scope: Internal
access: ReadWrite
api_name: "operation_authenticate_error"
}
@@ -156,7 +153,7 @@
prop {
prop_name: "persist.vendor.fingerprint.virtual.sensor_location"
type: String
- scope: Public
+ scope: Internal
access: ReadWrite
api_name: "sensor_location"
}
@@ -165,7 +162,7 @@
prop {
prop_name: "vendor.fingerprint.virtual.operation_authenticate_acquired"
type: String
- scope: Public
+ scope: Internal
access: ReadWrite
api_name: "operation_authenticate_acquired"
}
@@ -175,7 +172,7 @@
prop {
prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_duration"
type: Integer
- scope: Public
+ scope: Internal
access: ReadWrite
api_name: "operation_detect_interaction_duration"
}
@@ -187,7 +184,7 @@
prop {
prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_acquired"
type: String
- scope: Public
+ scope: Internal
access: ReadWrite
api_name: "operation_detect_interaction_acquired"
}
@@ -196,7 +193,7 @@
prop {
prop_name: "persist.vendor.fingerprint.virtual.sensor_id"
type: Integer
- scope: Public
+ scope: Internal
access: ReadWrite
api_name: "sensor_id"
}
@@ -206,7 +203,7 @@
prop {
prop_name: "persist.vendor.fingerprint.virtual.sensor_strength"
type: Integer
- scope: Public
+ scope: Internal
access: ReadWrite
api_name: "sensor_strength"
}
@@ -216,7 +213,7 @@
prop {
prop_name: "persist.vendor.fingerprint.virtual.max_enrollments"
type: Integer
- scope: Public
+ scope: Internal
access: ReadWrite
api_name: "max_enrollments"
}
@@ -225,7 +222,7 @@
prop {
prop_name: "persist.vendor.fingerprint.virtual.navigation_guesture"
type: Boolean
- scope: Public
+ scope: Internal
access: ReadWrite
api_name: "navigation_guesture"
}
@@ -234,7 +231,7 @@
prop {
prop_name: "persist.vendor.fingerprint.virtual.detect_interaction"
type: Boolean
- scope: Public
+ scope: Internal
access: ReadWrite
api_name: "detect_interaction"
}
@@ -243,7 +240,7 @@
prop {
prop_name: "persist.vendor.fingerprint.virtual.udfps.display_touch"
type: Boolean
- scope: Public
+ scope: Internal
access: ReadWrite
api_name: "display_touch"
}
@@ -252,7 +249,52 @@
prop {
prop_name: "persist.vendor.fingerprint.virtual.udfps.control_illumination"
type: Boolean
- scope: Public
+ scope: Internal
access: ReadWrite
api_name: "control_illumination"
}
+
+# force to be locked out (default: false)
+prop {
+ prop_name: "persist.vendor.fingerprint.virtual.lockout"
+ type: Boolean
+ scope: Internal
+ access: ReadWrite
+ api_name: "lockout"
+}
+
+# whether support lockout based on the failed auth attempts (default: false)
+prop {
+ prop_name: "persist.vendor.fingerprint.virtual.lockout_enable"
+ type: Boolean
+ scope: Internal
+ access: ReadWrite
+ api_name: "lockout_enable"
+}
+
+# temporarily lockout threshold in number of consecutive failed auth attempts (default: 5)
+prop {
+ prop_name: "persist.vendor.fingerprint.virtual.lockout_timed_threshold"
+ type: Integer
+ scope: Internal
+ access: ReadWrite
+ api_name: "lockout_timed_threshold"
+}
+
+# temporary lockout duration in ms (default: 10000ms)
+prop {
+ prop_name: "persist.vendor.fingerprint.virtual.lockout_timed_duration"
+ type: Integer
+ scope: Internal
+ access: ReadWrite
+ api_name: "lockout_timed_duration"
+}
+
+# permanently lockout threshold in number of consecutive failed auth attempts (default: 20)
+prop {
+ prop_name: "persist.vendor.fingerprint.virtual.lockout_permanent_threshold"
+ type: Integer
+ scope: Internal
+ access: ReadWrite
+ api_name: "lockout_permanent_threshold"
+}
diff --git a/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h
index 22b1744..1279cd9 100644
--- a/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h
+++ b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -15,8 +15,13 @@
*/
#pragma once
+
+#define LOG_TAG "FingerprintVirtualHal"
+
#include <aidl/android/hardware/biometrics/common/SensorStrength.h>
#include <aidl/android/hardware/biometrics/fingerprint/ISessionCallback.h>
+#include <android/binder_to_string.h>
+#include <string>
#include <random>
@@ -24,6 +29,8 @@
#include <future>
#include <vector>
+#include "FakeLockoutTracker.h"
+
using namespace ::aidl::android::hardware::biometrics::common;
namespace aidl::android::hardware::biometrics::fingerprint {
@@ -36,11 +43,11 @@
void generateChallengeImpl(ISessionCallback* cb);
void revokeChallengeImpl(ISessionCallback* cb, int64_t challenge);
- void enrollImpl(ISessionCallback* cb, const keymaster::HardwareAuthToken& hat,
- const std::future<void>& cancel);
- void authenticateImpl(ISessionCallback* cb, int64_t operationId,
- const std::future<void>& cancel);
- void detectInteractionImpl(ISessionCallback* cb, const std::future<void>& cancel);
+ virtual void enrollImpl(ISessionCallback* cb, const keymaster::HardwareAuthToken& hat,
+ const std::future<void>& cancel);
+ virtual void authenticateImpl(ISessionCallback* cb, int64_t operationId,
+ const std::future<void>& cancel);
+ virtual void detectInteractionImpl(ISessionCallback* cb, const std::future<void>& cancel);
void enumerateEnrollmentsImpl(ISessionCallback* cb);
void removeEnrollmentsImpl(ISessionCallback* cb, const std::vector<int32_t>& enrollmentIds);
void getAuthenticatorIdImpl(ISessionCallback* cb);
@@ -63,13 +70,29 @@
std::vector<std::vector<int32_t>> parseEnrollmentCapture(const std::string& str);
+ int32_t getLatency(const std::vector<std::optional<std::int32_t>>& latencyVec);
+
std::mt19937 mRandom;
+ virtual std::string toString() const {
+ std::ostringstream os;
+ os << "----- FakeFingerprintEngine:: -----" << std::endl;
+ os << "acquiredVendorInfoBase:" << FINGERPRINT_ACQUIRED_VENDOR_BASE;
+ os << ", errorVendorBase:" << FINGERPRINT_ERROR_VENDOR_BASE << std::endl;
+ os << mLockoutTracker.toString();
+ return os.str();
+ }
+
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);
+ bool parseEnrollmentCaptureSingle(const std::string& str,
+ std::vector<std::vector<int32_t>>& res);
+ int32_t getRandomInRange(int32_t bound1, int32_t bound2);
+
+ FakeLockoutTracker mLockoutTracker;
};
} // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineRear.h b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineRear.h
index 1600a4b..14d5399 100644
--- a/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineRear.h
+++ b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineRear.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 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.
diff --git a/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineSide.h b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineSide.h
index 4e44d16..c2fc005 100644
--- a/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineSide.h
+++ b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineSide.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 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.
diff --git a/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineUdfps.h b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineUdfps.h
index b86af73..c5e93e7 100644
--- a/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineUdfps.h
+++ b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineUdfps.h
@@ -28,17 +28,55 @@
static constexpr int32_t defaultSensorLocationY = 1600;
static constexpr int32_t defaultSensorRadius = 150;
- FakeFingerprintEngineUdfps() : FakeFingerprintEngine() {}
+ static constexpr int32_t uiReadyTimeoutInMs = 5000;
+
+ FakeFingerprintEngineUdfps();
~FakeFingerprintEngineUdfps() {}
- virtual ndk::ScopedAStatus onPointerDownImpl(int32_t pointerId, int32_t x, int32_t y,
- float minor, float major) override;
+ ndk::ScopedAStatus onPointerDownImpl(int32_t pointerId, int32_t x, int32_t y, float minor,
+ float major) override;
- virtual ndk::ScopedAStatus onPointerUpImpl(int32_t pointerId) override;
+ ndk::ScopedAStatus onPointerUpImpl(int32_t pointerId) override;
- virtual ndk::ScopedAStatus onUiReadyImpl() override;
+ ndk::ScopedAStatus onUiReadyImpl() override;
- virtual SensorLocation defaultSensorLocation() override;
+ SensorLocation defaultSensorLocation() override;
+
+ void enrollImpl(ISessionCallback* cb, const keymaster::HardwareAuthToken& hat,
+ const std::future<void>& cancel);
+ void authenticateImpl(ISessionCallback* cb, int64_t operationId,
+ const std::future<void>& cancel);
+ void detectInteractionImpl(ISessionCallback* cb, const std::future<void>& cancel);
+
+ enum class WorkMode : int8_t { kIdle = 0, kAuthenticate, kEnroll, kDetectInteract };
+
+ WorkMode getWorkMode() { return mWorkMode; }
+
+ std::string toString() const {
+ std::ostringstream os;
+ os << FakeFingerprintEngine::toString();
+ os << "----- FakeFingerprintEngineUdfps -----" << std::endl;
+ os << "mWorkMode:" << (int)mWorkMode;
+ os << ", mUiReadyTime:" << mUiReadyTime;
+ os << ", mPointerDownTime:" << mPointerDownTime << std::endl;
+ return os.str();
+ }
+
+ private:
+ void onAuthenticateFingerDown();
+ void onEnrollFingerDown();
+ void onDetectInteractFingerDown();
+ void fingerDownAction();
+ void updateContext(WorkMode mode, ISessionCallback* cb, std::future<void>& cancel,
+ int64_t operationId, const keymaster::HardwareAuthToken& hat);
+
+ WorkMode mWorkMode;
+ ISessionCallback* mCb;
+ keymaster::HardwareAuthToken mHat;
+ std::vector<std::future<void>> mCancelVec;
+ int64_t mOperationId;
+ int64_t mPointerDownTime;
+ int64_t mUiReadyTime;
};
} // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/include/FakeLockoutTracker.h b/biometrics/fingerprint/aidl/default/include/FakeLockoutTracker.h
new file mode 100644
index 0000000..a1b6128
--- /dev/null
+++ b/biometrics/fingerprint/aidl/default/include/FakeLockoutTracker.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#pragma once
+
+#include <android/binder_to_string.h>
+#include <stdint.h>
+#include <string>
+
+namespace aidl::android::hardware::biometrics::fingerprint {
+
+class FakeLockoutTracker {
+ public:
+ FakeLockoutTracker() : mFailedCount(0) {}
+ ~FakeLockoutTracker() {}
+
+ enum class LockoutMode : int8_t { kNone = 0, kTimed, kPermanent };
+
+ void reset();
+ LockoutMode getMode();
+ void addFailedAttempt();
+ int64_t getLockoutTimeLeft();
+ inline std::string toString() const {
+ std::ostringstream os;
+ os << "----- FakeLockoutTracker:: -----" << std::endl;
+ os << "FakeLockoutTracker::mFailedCount:" << mFailedCount;
+ os << ", FakeLockoutTracker::mCurrentMode:" << (int)mCurrentMode;
+ os << std::endl;
+ return os.str();
+ }
+
+ private:
+ int32_t mFailedCount;
+ int64_t mLockoutTimedStart;
+ LockoutMode mCurrentMode;
+};
+
+} // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/include/Fingerprint.h b/biometrics/fingerprint/aidl/default/include/Fingerprint.h
index 64aafa3..fc4fb8d 100644
--- a/biometrics/fingerprint/aidl/default/include/Fingerprint.h
+++ b/biometrics/fingerprint/aidl/default/include/Fingerprint.h
@@ -16,8 +16,6 @@
#pragma once
-#define LOG_TAG "FingerprintVirtualHal"
-
#include <aidl/android/hardware/biometrics/fingerprint/BnFingerprint.h>
#include "FakeFingerprintEngine.h"
@@ -39,8 +37,13 @@
ndk::ScopedAStatus createSession(int32_t sensorId, int32_t userId,
const std::shared_ptr<ISessionCallback>& cb,
std::shared_ptr<ISession>* out) override;
+ binder_status_t dump(int fd, const char** args, uint32_t numArgs);
+ binder_status_t handleShellCommand(int in, int out, int err, const char** argv, uint32_t argc);
private:
+ void resetConfigToDefault();
+ void onHelp(int);
+
std::unique_ptr<FakeFingerprintEngine> mEngine;
WorkerThread mWorker;
std::shared_ptr<Session> mSession;
diff --git a/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineTest.cpp b/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineTest.cpp
index 32d01f4..a200b39 100644
--- a/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineTest.cpp
+++ b/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineTest.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -21,6 +21,7 @@
#include <aidl/android/hardware/biometrics/fingerprint/BnSessionCallback.h>
#include "FakeFingerprintEngine.h"
+#include "util/Util.h"
using namespace ::android::fingerprint::virt;
using namespace ::aidl::android::hardware::biometrics::fingerprint;
@@ -118,9 +119,9 @@
class FakeFingerprintEngineTest : public ::testing::Test {
protected:
void SetUp() override {
- FingerprintHalProperties::operation_enroll_latency(0);
- FingerprintHalProperties::operation_authenticate_latency(0);
- FingerprintHalProperties::operation_detect_interaction_latency(0);
+ FingerprintHalProperties::operation_enroll_latency({0});
+ FingerprintHalProperties::operation_authenticate_latency({0});
+ FingerprintHalProperties::operation_detect_interaction_latency({0});
mCallback = ndk::SharedRefBase::make<TestSessionCallback>();
}
@@ -128,6 +129,9 @@
FingerprintHalProperties::operation_authenticate_error(0);
FingerprintHalProperties::operation_detect_interaction_error(0);
FingerprintHalProperties::operation_authenticate_acquired("");
+ FingerprintHalProperties::operation_enroll_latency({});
+ FingerprintHalProperties::operation_authenticate_latency({});
+ FingerprintHalProperties::operation_detect_interaction_latency({});
}
FakeFingerprintEngine mEngine;
@@ -291,6 +295,7 @@
}
TEST_F(FakeFingerprintEngineTest, InteractionDetect) {
+ FingerprintHalProperties::detect_interaction(true);
FingerprintHalProperties::enrollments({1, 2});
FingerprintHalProperties::enrollment_hit(2);
FingerprintHalProperties::operation_detect_interaction_acquired("");
@@ -300,6 +305,7 @@
}
TEST_F(FakeFingerprintEngineTest, InteractionDetectCancel) {
+ FingerprintHalProperties::detect_interaction(true);
FingerprintHalProperties::enrollments({1, 2});
FingerprintHalProperties::enrollment_hit(2);
mCancel.set_value();
@@ -309,6 +315,7 @@
}
TEST_F(FakeFingerprintEngineTest, InteractionDetectNotSet) {
+ FingerprintHalProperties::detect_interaction(true);
FingerprintHalProperties::enrollments({1, 2});
FingerprintHalProperties::enrollment_hit({});
mEngine.detectInteractionImpl(mCallback.get(), mCancel.get_future());
@@ -323,6 +330,7 @@
}
TEST_F(FakeFingerprintEngineTest, InteractionDetectError) {
+ FingerprintHalProperties::detect_interaction(true);
FingerprintHalProperties::operation_detect_interaction_error(8);
mEngine.detectInteractionImpl(mCallback.get(), mCancel.get_future());
ASSERT_EQ(0, mCallback->mInteractionDetectedCount);
@@ -331,6 +339,7 @@
}
TEST_F(FakeFingerprintEngineTest, InteractionDetectAcquired) {
+ FingerprintHalProperties::detect_interaction(true);
FingerprintHalProperties::enrollments({1, 2});
FingerprintHalProperties::enrollment_hit(2);
FingerprintHalProperties::operation_detect_interaction_acquired("4,1013");
@@ -435,12 +444,29 @@
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) {
+ for (const auto& s : badStr) {
ecV = mEngine.parseEnrollmentCapture(s);
ASSERT_EQ(ecV.size(), 0);
}
}
+TEST_F(FakeFingerprintEngineTest, randomLatency) {
+ FingerprintHalProperties::operation_detect_interaction_latency({});
+ ASSERT_EQ(DEFAULT_LATENCY,
+ mEngine.getLatency(FingerprintHalProperties::operation_detect_interaction_latency()));
+ FingerprintHalProperties::operation_detect_interaction_latency({10});
+ ASSERT_EQ(10,
+ mEngine.getLatency(FingerprintHalProperties::operation_detect_interaction_latency()));
+ FingerprintHalProperties::operation_detect_interaction_latency({1, 1000});
+ std::set<int32_t> latencySet;
+ for (int i = 0; i < 100; i++) {
+ latencySet.insert(mEngine.getLatency(
+ FingerprintHalProperties::operation_detect_interaction_latency()));
+ }
+ ASSERT_TRUE(latencySet.size() > 95);
+ FingerprintHalProperties::operation_detect_interaction_latency({});
+}
+
} // 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 7c0021f..f551899 100644
--- a/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineUdfpsTest.cpp
+++ b/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineUdfpsTest.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <aidl/android/hardware/biometrics/fingerprint/BnSessionCallback.h>
#include <android/binder_process.h>
#include <fingerprint.sysprop.h>
#include <gtest/gtest.h>
@@ -29,6 +30,69 @@
namespace aidl::android::hardware::biometrics::fingerprint {
+class TestSessionCallback : public BnSessionCallback {
+ public:
+ ndk::ScopedAStatus onChallengeGenerated(int64_t /*challenge*/) override {
+ return ndk::ScopedAStatus::ok();
+ };
+ ::ndk::ScopedAStatus onChallengeRevoked(int64_t /*challenge*/) override {
+ return ndk::ScopedAStatus::ok();
+ };
+ ::ndk::ScopedAStatus onError(fingerprint::Error /*error*/, int32_t /*vendorCode*/) override {
+ return ndk::ScopedAStatus::ok();
+ };
+ ::ndk::ScopedAStatus onEnrollmentProgress(int32_t /*enrollmentId*/,
+ int32_t /*remaining*/) override {
+ mEnrollmentProgress++;
+ return ndk::ScopedAStatus::ok();
+ };
+
+ ::ndk::ScopedAStatus onAuthenticationSucceeded(int32_t /*enrollmentId*/,
+ const keymaster::HardwareAuthToken&) override {
+ mAuthenticationSuccess++;
+ return ndk::ScopedAStatus::ok();
+ };
+ ::ndk::ScopedAStatus onAuthenticationFailed() override {
+ mAuthenticationFailure++;
+ return ndk::ScopedAStatus::ok();
+ };
+ ::ndk::ScopedAStatus onInteractionDetected() override {
+ mDetectInteraction++;
+ return ndk::ScopedAStatus::ok();
+ };
+ ndk::ScopedAStatus onAcquired(AcquiredInfo /*info*/, int32_t /*vendorCode*/) override {
+ return ndk::ScopedAStatus::ok();
+ }
+ ::ndk::ScopedAStatus onEnrollmentsEnumerated(
+ const std::vector<int32_t>& /*enrollmentIds*/) override {
+ return ndk::ScopedAStatus::ok();
+ };
+ ::ndk::ScopedAStatus onEnrollmentsRemoved(
+ const std::vector<int32_t>& /*enrollmentIds*/) override {
+ return ndk::ScopedAStatus::ok();
+ };
+ ::ndk::ScopedAStatus onAuthenticatorIdRetrieved(int64_t /*authenticatorId*/) override {
+ return ndk::ScopedAStatus::ok();
+ };
+ ::ndk::ScopedAStatus onAuthenticatorIdInvalidated(int64_t /*authenticatorId*/) override {
+ return ndk::ScopedAStatus::ok();
+ };
+ ::ndk::ScopedAStatus onLockoutPermanent() override { return ndk::ScopedAStatus::ok(); };
+ ndk::ScopedAStatus onLockoutTimed(int64_t /* timeout */) override {
+ return ndk::ScopedAStatus::ok();
+ }
+ ndk::ScopedAStatus onLockoutCleared() override { return ndk::ScopedAStatus::ok(); }
+ ndk::ScopedAStatus onSessionClosed() override { return ndk::ScopedAStatus::ok(); }
+
+ int32_t getAuthenticationCount() { return mAuthenticationSuccess + mAuthenticationFailure; }
+ int32_t getDetectInteractionCount() { return mDetectInteraction; }
+
+ int32_t mAuthenticationSuccess = 0;
+ int32_t mAuthenticationFailure = 0;
+ int32_t mEnrollmentProgress = 0;
+ int32_t mDetectInteraction = 0;
+};
+
class FakeFingerprintEngineUdfpsTest : public ::testing::Test {
protected:
void SetUp() override {}
@@ -65,30 +129,56 @@
}
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));
-
- loc = "10:20";
- FingerprintHalProperties::sensor_location(loc);
- sc = mEngine.getSensorLocation();
- ASSERT_TRUE(isDefaultLocation(sc));
-
- loc = "10,20,5";
- FingerprintHalProperties::sensor_location(loc);
- sc = mEngine.getSensorLocation();
- ASSERT_TRUE(isDefaultLocation(sc));
-
- loc = "a:b:c";
- FingerprintHalProperties::sensor_location(loc);
- sc = mEngine.getSensorLocation();
- ASSERT_TRUE(isDefaultLocation(sc));
+ const std::vector<std::string> badStr{"", "100", "10:20", "10,20,5", "a:b:c"};
+ SensorLocation sc;
+ for (const auto& s : badStr) {
+ FingerprintHalProperties::sensor_location(s);
+ sc = mEngine.getSensorLocation();
+ ASSERT_TRUE(isDefaultLocation(sc));
+ }
}
+TEST_F(FakeFingerprintEngineUdfpsTest, initialization) {
+ ASSERT_TRUE(mEngine.getWorkMode() == FakeFingerprintEngineUdfps::WorkMode::kIdle);
+}
+
+TEST_F(FakeFingerprintEngineUdfpsTest, authenticate) {
+ std::shared_ptr<TestSessionCallback> cb = ndk::SharedRefBase::make<TestSessionCallback>();
+ std::promise<void> cancel;
+ mEngine.authenticateImpl(cb.get(), 1, cancel.get_future());
+ ASSERT_TRUE(mEngine.getWorkMode() == FakeFingerprintEngineUdfps::WorkMode::kAuthenticate);
+ mEngine.onPointerDownImpl(1, 2, 3, 4.0, 5.0);
+ ASSERT_EQ(cb->getAuthenticationCount(), 0);
+ mEngine.onUiReadyImpl();
+ ASSERT_EQ(cb->getAuthenticationCount(), 1);
+}
+
+TEST_F(FakeFingerprintEngineUdfpsTest, enroll) {
+ std::shared_ptr<TestSessionCallback> cb = ndk::SharedRefBase::make<TestSessionCallback>();
+ std::promise<void> cancel;
+ keymaster::HardwareAuthToken hat{.mac = {5, 6}};
+ FingerprintHalProperties::next_enrollment("5:0,0:true");
+ mEngine.enrollImpl(cb.get(), hat, cancel.get_future());
+ ASSERT_TRUE(mEngine.getWorkMode() == FakeFingerprintEngineUdfps::WorkMode::kEnroll);
+ mEngine.onPointerDownImpl(1, 2, 3, 4.0, 5.0);
+ ASSERT_EQ(cb->mEnrollmentProgress, 0);
+ mEngine.onUiReadyImpl();
+ ASSERT_TRUE(cb->mEnrollmentProgress > 0);
+}
+
+TEST_F(FakeFingerprintEngineUdfpsTest, detectInteraction) {
+ FingerprintHalProperties::detect_interaction(true);
+ FingerprintHalProperties::enrollments({1, 2});
+ FingerprintHalProperties::enrollment_hit(2);
+ FingerprintHalProperties::operation_detect_interaction_acquired("");
+ std::shared_ptr<TestSessionCallback> cb = ndk::SharedRefBase::make<TestSessionCallback>();
+ std::promise<void> cancel;
+ mEngine.detectInteractionImpl(cb.get(), cancel.get_future());
+ ASSERT_TRUE(mEngine.getWorkMode() == FakeFingerprintEngineUdfps::WorkMode::kDetectInteract);
+ mEngine.onPointerDownImpl(1, 2, 3, 4.0, 5.0);
+ ASSERT_EQ(cb->getDetectInteractionCount(), 0);
+ mEngine.onUiReadyImpl();
+ ASSERT_EQ(cb->getDetectInteractionCount(), 1);
+}
// More
} // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/tests/FakeLockoutTrackerTest.cpp b/biometrics/fingerprint/aidl/default/tests/FakeLockoutTrackerTest.cpp
new file mode 100644
index 0000000..1b071ee
--- /dev/null
+++ b/biometrics/fingerprint/aidl/default/tests/FakeLockoutTrackerTest.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2022 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.
+ */
+
+#include <android/binder_process.h>
+#include <fingerprint.sysprop.h>
+#include <gtest/gtest.h>
+
+#include <android-base/logging.h>
+
+#include "FakeLockoutTracker.h"
+#include "util/Util.h"
+
+using namespace ::android::fingerprint::virt;
+using namespace ::aidl::android::hardware::biometrics::fingerprint;
+
+namespace aidl::android::hardware::biometrics::fingerprint {
+
+class FakeLockoutTrackerTest : public ::testing::Test {
+ protected:
+ static constexpr int32_t LOCKOUT_TIMED_THRESHOLD = 3;
+ static constexpr int32_t LOCKOUT_PERMANENT_THRESHOLD = 5;
+ static constexpr int32_t LOCKOUT_TIMED_DURATION = 100;
+
+ void SetUp() override {
+ FingerprintHalProperties::lockout_timed_threshold(LOCKOUT_TIMED_THRESHOLD);
+ FingerprintHalProperties::lockout_timed_duration(LOCKOUT_TIMED_DURATION);
+ FingerprintHalProperties::lockout_permanent_threshold(LOCKOUT_PERMANENT_THRESHOLD);
+ }
+
+ void TearDown() override {
+ // reset to default
+ FingerprintHalProperties::lockout_timed_threshold(5);
+ FingerprintHalProperties::lockout_timed_duration(20);
+ FingerprintHalProperties::lockout_permanent_threshold(10000);
+ FingerprintHalProperties::lockout_enable(false);
+ FingerprintHalProperties::lockout(false);
+ }
+
+ FakeLockoutTracker mLockoutTracker;
+};
+
+TEST_F(FakeLockoutTrackerTest, addFailedAttemptDisable) {
+ FingerprintHalProperties::lockout_enable(false);
+ for (int i = 0; i < LOCKOUT_TIMED_THRESHOLD + 1; i++) mLockoutTracker.addFailedAttempt();
+ ASSERT_EQ(mLockoutTracker.getMode(), FakeLockoutTracker::LockoutMode::kNone);
+ mLockoutTracker.reset();
+}
+
+TEST_F(FakeLockoutTrackerTest, addFailedAttemptLockoutTimed) {
+ FingerprintHalProperties::lockout_enable(true);
+ for (int i = 0; i < LOCKOUT_TIMED_THRESHOLD; i++) mLockoutTracker.addFailedAttempt();
+ ASSERT_EQ(mLockoutTracker.getMode(), FakeLockoutTracker::LockoutMode::kTimed);
+ // time left
+ int N = 5;
+ int64_t prevTimeLeft = INT_MIN;
+ for (int i = 0; i < N; i++) {
+ SLEEP_MS(LOCKOUT_TIMED_DURATION / N + 1);
+ int64_t currTimeLeft = mLockoutTracker.getLockoutTimeLeft();
+ ASSERT_TRUE(currTimeLeft > prevTimeLeft);
+ prevTimeLeft = currTimeLeft;
+ }
+ ASSERT_EQ(mLockoutTracker.getMode(), FakeLockoutTracker::LockoutMode::kNone);
+ mLockoutTracker.reset();
+}
+
+TEST_F(FakeLockoutTrackerTest, addFailedAttemptPermanent) {
+ FingerprintHalProperties::lockout_enable(true);
+ for (int i = 0; i < LOCKOUT_PERMANENT_THRESHOLD - 1; i++) mLockoutTracker.addFailedAttempt();
+ ASSERT_NE(mLockoutTracker.getMode(), FakeLockoutTracker::LockoutMode::kPermanent);
+ mLockoutTracker.addFailedAttempt();
+ ASSERT_EQ(mLockoutTracker.getMode(), FakeLockoutTracker::LockoutMode::kPermanent);
+ ASSERT_TRUE(FingerprintHalProperties::lockout());
+ mLockoutTracker.reset();
+}
+
+} // namespace aidl::android::hardware::biometrics::fingerprint
+
+int main(int argc, char** argv) {
+ testing::InitGoogleTest(&argc, argv);
+ ABinderProcess_startThreadPool();
+ return RUN_ALL_TESTS();
+}