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
This commit is contained in:
Jeff Pu
2022-10-12 16:27:23 -04:00
parent 703977b709
commit 5265318938
19 changed files with 875 additions and 287 deletions

View File

@@ -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
} // namespace aidl::android::hardware::biometrics

View File

@@ -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",

View File

@@ -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::revokeChallengeImpl(ISessionCallback* cb, int64_t ch
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::enrollImpl(ISessionCallback* cb,
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 @@ 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<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 @@ 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<int32_t> FakeFingerprintEngine::parseIntSequence(const std::string&
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 @@ std::pair<Error, int32_t> FakeFingerprintEngine::convertError(int32_t code) {
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

View File

@@ -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<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

View File

@@ -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

View File

@@ -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 @@ ndk::ScopedAStatus Fingerprint::getSensorProps(std::vector<SensorProps>* 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<SensorProps>* 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<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

View File

@@ -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
```

View File

@@ -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,

View File

@@ -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"
}
}

View File

@@ -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"
}

View File

@@ -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 @@ class FakeFingerprintEngine {
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 @@ class FakeFingerprintEngine {
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

View File

@@ -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.

View File

@@ -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.

View File

@@ -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<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

View File

@@ -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

View File

@@ -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 @@ class Fingerprint : public BnFingerprint {
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;

View File

@@ -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 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<TestSessionCallback>();
}
@@ -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<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) {

View File

@@ -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 @@ 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<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, 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<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

View File

@@ -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();
}