diff --git a/biometrics/common/util/include/util/Util.h b/biometrics/common/util/include/util/Util.h index 29ec0f865e..da19dc6afe 100644 --- a/biometrics/common/util/include/util/Util.h +++ b/biometrics/common/util/include/util/Util.h @@ -40,7 +40,7 @@ namespace aidl::android::hardware::biometrics { // 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 @@ class Util { } }; -} // 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 dc0199c98a..77c74e1a77 100644 --- a/biometrics/fingerprint/aidl/default/Android.bp +++ b/biometrics/fingerprint/aidl/default/Android.bp @@ -15,6 +15,7 @@ cc_binary { vintf_fragments: ["fingerprint-example.xml"], local_include_dirs: ["include"], srcs: [ + "FakeLockoutTracker.cpp", "FakeFingerprintEngine.cpp", "FakeFingerprintEngineRear.cpp", "FakeFingerprintEngineUdfps.cpp", @@ -40,6 +41,7 @@ cc_test { srcs: [ "tests/FakeFingerprintEngineTest.cpp", "FakeFingerprintEngine.cpp", + "FakeLockoutTracker.cpp", ], shared_libs: [ "libbase", @@ -65,6 +67,31 @@ cc_test { "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 651c9dc352..90ec8f26ee 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 #include "Fingerprint.h" #include @@ -47,7 +48,7 @@ void FakeFingerprintEngine::revokeChallengeImpl(ISessionCallback* cb, int64_t ch void FakeFingerprintEngine::enrollImpl(ISessionCallback* cb, const keymaster::HardwareAuthToken& hat, const std::future& 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::enrollImpl(ISessionCallback* cb, void FakeFingerprintEngine::authenticateImpl(ISessionCallback* cb, int64_t /* operationId */, const std::future& 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 @@ void FakeFingerprintEngine::authenticateImpl(ISessionCallback* cb, int64_t /* op 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 @@ void FakeFingerprintEngine::authenticateImpl(ISessionCallback* cb, int64_t /* op 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& 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 @@ void FakeFingerprintEngine::resetLockoutImpl(ISessionCallback* cb, } FingerprintHalProperties::lockout(false); cb->onLockoutCleared(); + mLockoutTracker.reset(); } ndk::ScopedAStatus FakeFingerprintEngine::onPointerDownImpl(int32_t /*pointerId*/, int32_t /*x*/, @@ -383,49 +408,52 @@ std::vector FakeFingerprintEngine::parseIntSequence(const std::string& return res; } -std::vector> FakeFingerprintEngine::parseEnrollmentCapture( - const std::string& str) { +bool FakeFingerprintEngine::parseEnrollmentCaptureSingle(const std::string& str, + std::vector>& res) { std::vector defaultAcquiredInfo = {(int32_t)AcquiredInfo::GOOD}; - std::vector> res; - int i = 0, N = str.length(); - std::size_t found = 0; bool aborted = true; - while (found != std::string::npos) { - std::string durationStr, acquiredStr; - found = str.find_first_of("-,", i); - if (found == std::string::npos) { - if (N - i < 1) break; - durationStr = str.substr(i, N - i); - } else { - durationStr = str.substr(i, found - i); - if (str[found] == '-') { - found = str.find_first_of('[', found + 1); - if (found == std::string::npos) break; - i = found + 1; - found = str.find_first_of(']', found + 1); - if (found == std::string::npos) break; - acquiredStr = str.substr(i, found - i); - found = str.find_first_of(',', found + 1); - } - } - std::vector duration{0}; - if (!ParseInt(durationStr, &duration[0])) break; - res.push_back(duration); - if (!acquiredStr.empty()) { - std::vector acquiredInfo = parseIntSequence(acquiredStr); - if (acquiredInfo.empty()) break; - res.push_back(acquiredInfo); + 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> FakeFingerprintEngine::parseEnrollmentCapture( + const std::string& str) { + std::vector> 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 @@ std::pair FakeFingerprintEngine::convertError(int32_t code) { return res; } +int32_t FakeFingerprintEngine::getLatency( + const std::vector>& latencyIn) { + int32_t res = DEFAULT_LATENCY; + + std::vector 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 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 d8579a4517..3cdfc70008 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 @@ ndk::ScopedAStatus FakeFingerprintEngineUdfps::onPointerDownImpl(int32_t /*point 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& cancel) { + updateContext(WorkMode::kEnroll, cb, const_cast&>(cancel), 0, hat); +} + +void FakeFingerprintEngineUdfps::authenticateImpl(ISessionCallback* cb, int64_t operationId, + const std::future& cancel) { + updateContext(WorkMode::kAuthenticate, cb, const_cast&>(cancel), operationId, + keymaster::HardwareAuthToken()); +} + +void FakeFingerprintEngineUdfps::detectInteractionImpl(ISessionCallback* cb, + const std::future& cancel) { + updateContext(WorkMode::kDetectInteract, cb, const_cast&>(cancel), 0, + keymaster::HardwareAuthToken()); +} + +void FakeFingerprintEngineUdfps::updateContext(WorkMode mode, ISessionCallback* cb, + std::future& 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 0000000000..5996406fd0 --- /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 +#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 74e7caff3c..be932246bd 100644 --- a/biometrics/fingerprint/aidl/default/Fingerprint.cpp +++ b/biometrics/fingerprint/aidl/default/Fingerprint.cpp @@ -15,11 +15,13 @@ */ #include "Fingerprint.h" - -#include #include "Session.h" +#include + +#include #include +#include using namespace ::android::fingerprint::virt; @@ -64,7 +66,6 @@ ndk::ScopedAStatus Fingerprint::getSensorProps(std::vector* out) { {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 @@ ndk::ScopedAStatus Fingerprint::getSensorProps(std::vector* out) { 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 @@ ndk::ScopedAStatus Fingerprint::createSession(int32_t sensorId, int32_t userId, 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 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(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 ad471f747d..49b6c9dc0d 100644 --- a/biometrics/fingerprint/aidl/default/README.md +++ b/biometrics/fingerprint/aidl/default/README.md @@ -57,6 +57,7 @@ $ adb shell cmd fingerprint sync ```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 @@ $ adb shell setprop vendor.fingerprint.virtual.operation_authenticate_error 8 ``` 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 @@ To view all the properties of the HAL (see `fingerprint.sysprop` file for the AP ```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 e51f677744..7ab5af3c4d 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 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 fa21663a17..e69de29bb2 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 9b8fada927..6a6c29728b 100644 --- a/biometrics/fingerprint/aidl/default/fingerprint.sysprop +++ b/biometrics/fingerprint/aidl/default/fingerprint.sysprop @@ -7,7 +7,7 @@ owner: Vendor 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 { prop_name: "persist.vendor.fingerprint.virtual.enrollments" type: IntegerList - scope: Public + scope: Internal access: ReadWrite api_name: "enrollments" } @@ -27,7 +27,7 @@ prop { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 22b1744134..1279cd92a5 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 #include +#include +#include #include @@ -24,6 +29,8 @@ #include #include +#include "FakeLockoutTracker.h" + using namespace ::aidl::android::hardware::biometrics::common; namespace aidl::android::hardware::biometrics::fingerprint { @@ -36,11 +43,11 @@ class FakeFingerprintEngine { void generateChallengeImpl(ISessionCallback* cb); void revokeChallengeImpl(ISessionCallback* cb, int64_t challenge); - void enrollImpl(ISessionCallback* cb, const keymaster::HardwareAuthToken& hat, - const std::future& cancel); - void authenticateImpl(ISessionCallback* cb, int64_t operationId, - const std::future& cancel); - void detectInteractionImpl(ISessionCallback* cb, const std::future& cancel); + virtual void enrollImpl(ISessionCallback* cb, const keymaster::HardwareAuthToken& hat, + const std::future& cancel); + virtual void authenticateImpl(ISessionCallback* cb, int64_t operationId, + const std::future& cancel); + virtual void detectInteractionImpl(ISessionCallback* cb, const std::future& cancel); void enumerateEnrollmentsImpl(ISessionCallback* cb); void removeEnrollmentsImpl(ISessionCallback* cb, const std::vector& enrollmentIds); void getAuthenticatorIdImpl(ISessionCallback* cb); @@ -63,13 +70,29 @@ class FakeFingerprintEngine { std::vector> parseEnrollmentCapture(const std::string& str); + int32_t getLatency(const std::vector>& 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 convertAcquiredInfo(int32_t code); std::pair convertError(int32_t code); + bool parseEnrollmentCaptureSingle(const std::string& str, + std::vector>& 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 1600a4b560..14d5399fa4 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 4e44d16d1e..c2fc005ffc 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 b86af736bb..c5e93e72aa 100644 --- a/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineUdfps.h +++ b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngineUdfps.h @@ -28,17 +28,55 @@ class FakeFingerprintEngineUdfps : public FakeFingerprintEngine { 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& cancel); + void authenticateImpl(ISessionCallback* cb, int64_t operationId, + const std::future& cancel); + void detectInteractionImpl(ISessionCallback* cb, const std::future& 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& cancel, + int64_t operationId, const keymaster::HardwareAuthToken& hat); + + WorkMode mWorkMode; + ISessionCallback* mCb; + keymaster::HardwareAuthToken mHat; + std::vector> 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 0000000000..a1b6128a9e --- /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 +#include +#include + +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 64aafa30fb..fc4fb8dbff 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 #include "FakeFingerprintEngine.h" @@ -39,8 +37,13 @@ class Fingerprint : public BnFingerprint { ndk::ScopedAStatus createSession(int32_t sensorId, int32_t userId, const std::shared_ptr& cb, std::shared_ptr* 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 mEngine; WorkerThread mWorker; std::shared_ptr mSession; diff --git a/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineTest.cpp b/biometrics/fingerprint/aidl/default/tests/FakeFingerprintEngineTest.cpp index 32d01f400f..a200b39739 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 #include "FakeFingerprintEngine.h" +#include "util/Util.h" using namespace ::android::fingerprint::virt; using namespace ::aidl::android::hardware::biometrics::fingerprint; @@ -118,9 +119,9 @@ class TestSessionCallback : public BnSessionCallback { 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(); } @@ -128,6 +129,9 @@ class FakeFingerprintEngineTest : public ::testing::Test { 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, AuthenticateAcquired) { } 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, InteractionDetect) { } 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, InteractionDetectCancel) { } 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, InteractionDetectNotEnrolled) { } 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, InteractionDetectError) { } 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 @@ TEST_F(FakeFingerprintEngineTest, parseEnrollmentCaptureFail) { std::vector badStr{"10c", "100-5", "100-[5,6,7", "100-5,6,7]", "100,2x0,300", "200-[f]", "a,b"}; std::vector> ecV; - for (const auto s : badStr) { + 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 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 7c0021fb26..f551899260 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 #include #include #include @@ -29,6 +30,69 @@ using namespace ::aidl::android::hardware::keymaster; 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& /*enrollmentIds*/) override { + return ndk::ScopedAStatus::ok(); + }; + ::ndk::ScopedAStatus onEnrollmentsRemoved( + const std::vector& /*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, getSensorLocationOk) { } 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 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 cb = ndk::SharedRefBase::make(); + std::promise 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 cb = ndk::SharedRefBase::make(); + std::promise 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 cb = ndk::SharedRefBase::make(); + std::promise 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 0000000000..1b071eecd2 --- /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 +#include +#include + +#include + +#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(); +}