Add rear fps virtual HAL.
Bug: 228638448 Test: atest FakeFingerprintEngineTest Test: manual (see README.md) Change-Id: Ifecf6b5667352eb2127f820bfde47c7d325ab1b2
This commit is contained in:
parent
e6c21ee4cc
commit
de94aa0354
10 changed files with 926 additions and 64 deletions
|
@ -16,6 +16,7 @@ cc_binary {
|
|||
local_include_dirs: ["include"],
|
||||
srcs: [
|
||||
"CancellationSignal.cpp",
|
||||
"FakeFingerprintEngine.cpp",
|
||||
"Fingerprint.cpp",
|
||||
"Session.cpp",
|
||||
"WorkerThread.cpp",
|
||||
|
@ -27,6 +28,7 @@ cc_binary {
|
|||
"android.hardware.biometrics.fingerprint-V2-ndk",
|
||||
"android.hardware.biometrics.common-V2-ndk",
|
||||
],
|
||||
static_libs: ["android.hardware.biometrics.fingerprint.VirtualProps"],
|
||||
}
|
||||
|
||||
cc_test_host {
|
||||
|
@ -41,3 +43,33 @@ cc_test_host {
|
|||
],
|
||||
test_suites: ["general-tests"],
|
||||
}
|
||||
|
||||
cc_test {
|
||||
name: "android.hardware.biometrics.fingerprint.FakeFingerprintEngineTest",
|
||||
local_include_dirs: ["include"],
|
||||
srcs: [
|
||||
"CancellationSignal.cpp",
|
||||
"tests/FakeFingerprintEngineTest.cpp",
|
||||
"FakeFingerprintEngine.cpp",
|
||||
],
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libbinder_ndk",
|
||||
],
|
||||
static_libs: [
|
||||
"android.hardware.biometrics.fingerprint.VirtualProps",
|
||||
"android.hardware.biometrics.fingerprint-V2-ndk",
|
||||
"android.hardware.biometrics.common-V2-ndk",
|
||||
"android.hardware.keymaster-V3-ndk",
|
||||
],
|
||||
vendor: true,
|
||||
test_suites: ["general-tests"],
|
||||
require_root: true,
|
||||
}
|
||||
|
||||
sysprop_library {
|
||||
name: "android.hardware.biometrics.fingerprint.VirtualProps",
|
||||
srcs: ["fingerprint.sysprop"],
|
||||
property_owner: "Vendor",
|
||||
vendor: true,
|
||||
}
|
||||
|
|
263
biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp
Normal file
263
biometrics/fingerprint/aidl/default/FakeFingerprintEngine.cpp
Normal file
|
@ -0,0 +1,263 @@
|
|||
/*
|
||||
* Copyright (C) 2021 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 "FakeFingerprintEngine.h"
|
||||
|
||||
#include <fingerprint.sysprop.h>
|
||||
#include "CancellationSignal.h"
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <chrono>
|
||||
#include <regex>
|
||||
#include <thread>
|
||||
|
||||
#define SLEEP_MS(x) \
|
||||
if (x > 0) std::this_thread::sleep_for(std::chrono::milliseconds(x))
|
||||
#define BEGIN_OP(x) \
|
||||
do { \
|
||||
LOG(INFO) << __func__; \
|
||||
SLEEP_MS(x); \
|
||||
} while (0)
|
||||
#define IS_TRUE(x) ((x == "1") || (x == "true"))
|
||||
|
||||
// This is for non-test situations, such as casual cuttlefish users, that don't
|
||||
// set an explicit value.
|
||||
// Some operations (i.e. enroll, authenticate) will be executed in tight loops
|
||||
// by parts of the UI or fail if there is no latency. For example, the
|
||||
// fingerprint 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 2000
|
||||
|
||||
using namespace ::android::fingerprint::virt;
|
||||
using namespace ::aidl::android::hardware::biometrics::fingerprint;
|
||||
|
||||
int64_t getSystemNanoTime() {
|
||||
timespec now;
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
return now.tv_sec * 1000000000LL + now.tv_nsec;
|
||||
}
|
||||
|
||||
bool hasElapsed(int64_t start, int64_t durationMillis) {
|
||||
auto now = getSystemNanoTime();
|
||||
if (now < start) return true;
|
||||
if (durationMillis <= 0) return true;
|
||||
return ((now - start) / 1000000LL) > durationMillis;
|
||||
}
|
||||
|
||||
std::vector<std::string> split(const std::string& str, const std::string& sep) {
|
||||
std::regex regex(sep);
|
||||
std::vector<std::string> parts(std::sregex_token_iterator(str.begin(), str.end(), regex, -1),
|
||||
std::sregex_token_iterator());
|
||||
return parts;
|
||||
}
|
||||
|
||||
namespace aidl::android::hardware::biometrics::fingerprint {
|
||||
|
||||
void FakeFingerprintEngine::generateChallengeImpl(ISessionCallback* cb) {
|
||||
BEGIN_OP(0);
|
||||
std::uniform_int_distribution<int64_t> dist;
|
||||
auto challenge = dist(mRandom);
|
||||
FingerprintHalProperties::challenge(challenge);
|
||||
cb->onChallengeGenerated(challenge);
|
||||
}
|
||||
|
||||
void FakeFingerprintEngine::revokeChallengeImpl(ISessionCallback* cb, int64_t challenge) {
|
||||
BEGIN_OP(0);
|
||||
FingerprintHalProperties::challenge({});
|
||||
cb->onChallengeRevoked(challenge);
|
||||
}
|
||||
|
||||
void FakeFingerprintEngine::enrollImpl(ISessionCallback* cb,
|
||||
const keymaster::HardwareAuthToken& hat,
|
||||
const std::future<void>& cancel) {
|
||||
BEGIN_OP(FingerprintHalProperties::operation_enroll_latency().value_or(DEFAULT_LATENCY));
|
||||
|
||||
// Do proper HAT verification in the real implementation.
|
||||
if (hat.mac.empty()) {
|
||||
LOG(ERROR) << "Fail: hat";
|
||||
cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */);
|
||||
return;
|
||||
}
|
||||
|
||||
if (FingerprintHalProperties::operation_enroll_fails().value_or(false)) {
|
||||
LOG(ERROR) << "Fail: operation_enroll_fails";
|
||||
cb->onError(Error::VENDOR, 0 /* vendorError */);
|
||||
return;
|
||||
}
|
||||
|
||||
// format is "<id>:<progress_ms>,<progress_ms>,...:<result>
|
||||
auto nextEnroll = FingerprintHalProperties::next_enrollment().value_or("");
|
||||
auto parts = split(nextEnroll, ":");
|
||||
if (parts.size() != 3) {
|
||||
LOG(ERROR) << "Fail: invalid next_enrollment";
|
||||
cb->onError(Error::VENDOR, 0 /* vendorError */);
|
||||
return;
|
||||
}
|
||||
auto enrollmentId = std::stoi(parts[0]);
|
||||
auto progress = split(parts[1], ",");
|
||||
for (size_t i = 0; i < progress.size(); i++) {
|
||||
auto left = progress.size() - i - 1;
|
||||
SLEEP_MS(std::stoi(progress[i]));
|
||||
|
||||
if (shouldCancel(cancel)) {
|
||||
LOG(ERROR) << "Fail: cancel";
|
||||
cb->onError(Error::CANCELED, 0 /* vendorCode */);
|
||||
return;
|
||||
}
|
||||
|
||||
cb->onAcquired(AcquiredInfo::GOOD, 0 /* vendorCode */);
|
||||
if (left == 0 && !IS_TRUE(parts[2])) { // end and failed
|
||||
LOG(ERROR) << "Fail: requested by caller: " << nextEnroll;
|
||||
FingerprintHalProperties::next_enrollment({});
|
||||
cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorCode */);
|
||||
} else { // progress and update props if last time
|
||||
if (left == 0) {
|
||||
auto enrollments = FingerprintHalProperties::enrollments();
|
||||
enrollments.emplace_back(enrollmentId);
|
||||
FingerprintHalProperties::enrollments(enrollments);
|
||||
FingerprintHalProperties::next_enrollment({});
|
||||
LOG(INFO) << "Enrolled: " << enrollmentId;
|
||||
}
|
||||
cb->onEnrollmentProgress(enrollmentId, left);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FakeFingerprintEngine::authenticateImpl(ISessionCallback* cb, int64_t /* operationId */,
|
||||
const std::future<void>& cancel) {
|
||||
BEGIN_OP(FingerprintHalProperties::operation_authenticate_latency().value_or(DEFAULT_LATENCY));
|
||||
|
||||
auto now = getSystemNanoTime();
|
||||
int64_t duration = FingerprintHalProperties::operation_authenticate_duration().value_or(0);
|
||||
do {
|
||||
if (FingerprintHalProperties::operation_authenticate_fails().value_or(false)) {
|
||||
LOG(ERROR) << "Fail: operation_authenticate_fails";
|
||||
cb->onError(Error::VENDOR, 0 /* vendorError */);
|
||||
return;
|
||||
}
|
||||
|
||||
if (FingerprintHalProperties::lockout().value_or(false)) {
|
||||
LOG(ERROR) << "Fail: lockout";
|
||||
cb->onLockoutPermanent();
|
||||
cb->onError(Error::HW_UNAVAILABLE, 0 /* vendorError */);
|
||||
return;
|
||||
}
|
||||
|
||||
if (shouldCancel(cancel)) {
|
||||
LOG(ERROR) << "Fail: cancel";
|
||||
cb->onError(Error::CANCELED, 0 /* vendorCode */);
|
||||
return;
|
||||
}
|
||||
|
||||
auto id = FingerprintHalProperties::enrollment_hit().value_or(0);
|
||||
auto enrolls = FingerprintHalProperties::enrollments();
|
||||
auto isEnrolled = std::find(enrolls.begin(), enrolls.end(), id) != enrolls.end();
|
||||
if (id > 0 && isEnrolled) {
|
||||
cb->onAuthenticationSucceeded(id, {} /* hat */);
|
||||
return;
|
||||
}
|
||||
|
||||
SLEEP_MS(100);
|
||||
} while (!hasElapsed(now, duration));
|
||||
|
||||
LOG(ERROR) << "Fail: not enrolled";
|
||||
cb->onAuthenticationFailed();
|
||||
cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */);
|
||||
}
|
||||
|
||||
void FakeFingerprintEngine::detectInteractionImpl(ISessionCallback* cb,
|
||||
const std::future<void>& cancel) {
|
||||
BEGIN_OP(FingerprintHalProperties::operation_detect_interaction_latency().value_or(
|
||||
DEFAULT_LATENCY));
|
||||
|
||||
if (FingerprintHalProperties::operation_detect_interaction_fails().value_or(false)) {
|
||||
LOG(ERROR) << "Fail: operation_detect_interaction_fails";
|
||||
cb->onError(Error::VENDOR, 0 /* vendorError */);
|
||||
return;
|
||||
}
|
||||
|
||||
if (shouldCancel(cancel)) {
|
||||
LOG(ERROR) << "Fail: cancel";
|
||||
cb->onError(Error::CANCELED, 0 /* vendorCode */);
|
||||
return;
|
||||
}
|
||||
|
||||
auto id = FingerprintHalProperties::enrollment_hit().value_or(0);
|
||||
auto enrolls = FingerprintHalProperties::enrollments();
|
||||
auto isEnrolled = std::find(enrolls.begin(), enrolls.end(), id) != enrolls.end();
|
||||
if (id <= 0 || !isEnrolled) {
|
||||
LOG(ERROR) << "Fail: not enrolled";
|
||||
cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */);
|
||||
return;
|
||||
}
|
||||
|
||||
cb->onInteractionDetected();
|
||||
}
|
||||
|
||||
void FakeFingerprintEngine::enumerateEnrollmentsImpl(ISessionCallback* cb) {
|
||||
BEGIN_OP(0);
|
||||
|
||||
std::vector<int32_t> ids;
|
||||
for (auto& enrollment : FingerprintHalProperties::enrollments()) {
|
||||
auto id = enrollment.value_or(0);
|
||||
if (id > 0) {
|
||||
ids.push_back(id);
|
||||
}
|
||||
}
|
||||
|
||||
cb->onEnrollmentsEnumerated(ids);
|
||||
}
|
||||
|
||||
void FakeFingerprintEngine::removeEnrollmentsImpl(ISessionCallback* cb,
|
||||
const std::vector<int32_t>& enrollmentIds) {
|
||||
BEGIN_OP(0);
|
||||
|
||||
std::vector<std::optional<int32_t>> newEnrollments;
|
||||
std::vector<int32_t> removed;
|
||||
for (auto& enrollment : FingerprintHalProperties::enrollments()) {
|
||||
auto id = enrollment.value_or(0);
|
||||
if (std::find(enrollmentIds.begin(), enrollmentIds.end(), id) != enrollmentIds.end()) {
|
||||
removed.push_back(id);
|
||||
} else if (id > 0) {
|
||||
newEnrollments.emplace_back(id);
|
||||
}
|
||||
}
|
||||
FingerprintHalProperties::enrollments(newEnrollments);
|
||||
|
||||
cb->onEnrollmentsRemoved(enrollmentIds);
|
||||
}
|
||||
|
||||
void FakeFingerprintEngine::getAuthenticatorIdImpl(ISessionCallback* cb) {
|
||||
BEGIN_OP(0);
|
||||
cb->onAuthenticatorIdRetrieved(FingerprintHalProperties::authenticator_id().value_or(0));
|
||||
}
|
||||
|
||||
void FakeFingerprintEngine::invalidateAuthenticatorIdImpl(ISessionCallback* cb) {
|
||||
BEGIN_OP(0);
|
||||
auto id = FingerprintHalProperties::authenticator_id().value_or(0);
|
||||
auto newId = id + 1;
|
||||
FingerprintHalProperties::authenticator_id(newId);
|
||||
cb->onAuthenticatorIdInvalidated(newId);
|
||||
}
|
||||
|
||||
void FakeFingerprintEngine::resetLockoutImpl(ISessionCallback* cb,
|
||||
const keymaster::HardwareAuthToken& /*hat*/) {
|
||||
BEGIN_OP(0);
|
||||
FingerprintHalProperties::lockout(false);
|
||||
cb->onLockoutCleared();
|
||||
}
|
||||
|
||||
} // namespace aidl::android::hardware::biometrics::fingerprint
|
|
@ -16,15 +16,19 @@
|
|||
|
||||
#include "Fingerprint.h"
|
||||
|
||||
#include <fingerprint.sysprop.h>
|
||||
#include "Session.h"
|
||||
|
||||
#include <android-base/logging.h>
|
||||
|
||||
using namespace ::android::fingerprint::virt;
|
||||
|
||||
namespace aidl::android::hardware::biometrics::fingerprint {
|
||||
namespace {
|
||||
constexpr size_t MAX_WORKER_QUEUE_SIZE = 5;
|
||||
constexpr int SENSOR_ID = 1;
|
||||
constexpr common::SensorStrength SENSOR_STRENGTH = common::SensorStrength::STRONG;
|
||||
constexpr int MAX_ENROLLMENTS_PER_USER = 5;
|
||||
constexpr FingerprintSensorType SENSOR_TYPE = FingerprintSensorType::REAR;
|
||||
constexpr bool SUPPORTS_NAVIGATION_GESTURES = true;
|
||||
constexpr char HW_COMPONENT_ID[] = "fingerprintSensor";
|
||||
constexpr char HW_VERSION[] = "vendor/model/revision";
|
||||
|
@ -51,8 +55,18 @@ ndk::ScopedAStatus Fingerprint::getSensorProps(std::vector<SensorProps>* out) {
|
|||
0 /* sensorLocationY */, 0 /* sensorRadius */,
|
||||
"" /* display */};
|
||||
|
||||
FingerprintSensorType sensorType = FingerprintSensorType::UNKNOWN;
|
||||
std::string sensorTypeProp = FingerprintHalProperties::type().value_or("");
|
||||
if (sensorTypeProp == "" || sensorTypeProp == "default" || sensorTypeProp == "rear") {
|
||||
sensorType = FingerprintSensorType::REAR;
|
||||
}
|
||||
if (sensorType == FingerprintSensorType::UNKNOWN) {
|
||||
UNIMPLEMENTED(FATAL) << "unrecognized or unimplemented fingerprint behavior: "
|
||||
<< sensorTypeProp;
|
||||
}
|
||||
|
||||
*out = {{commonProps,
|
||||
SENSOR_TYPE,
|
||||
sensorType,
|
||||
{sensorLocation},
|
||||
SUPPORTS_NAVIGATION_GESTURES,
|
||||
false /* supportsDetectInteraction */}};
|
||||
|
|
74
biometrics/fingerprint/aidl/default/README.md
Normal file
74
biometrics/fingerprint/aidl/default/README.md
Normal file
|
@ -0,0 +1,74 @@
|
|||
# Virtual Fingerprint HAL
|
||||
|
||||
This is a virtual HAL implementation that is backed by system properties
|
||||
instead of actual hardware. It's intended for testing and UI development
|
||||
on debuggable builds to allow devices to masquerade as alternative device
|
||||
types and for emulators.
|
||||
|
||||
## Getting Started
|
||||
|
||||
First, set the type of sensor the device should use, enable the virtual
|
||||
extensions in the framework, and reboot.
|
||||
|
||||
This doesn't work with HIDL and you typically need to have a PIN or password
|
||||
set for things to work correctly, so this is a good time to set those too.
|
||||
|
||||
```shell
|
||||
$ adb root
|
||||
$ adb shell settings put secure biometric_virtual_enabled 1
|
||||
$ adb shell setprop persist.vendor.fingerprint.virtual.type rear
|
||||
$ adb shell locksettings set-pin 0000
|
||||
$ adb shell settings put secure com.android.server.biometrics.AuthService.hidlDisabled 1
|
||||
$ adb reboot
|
||||
```
|
||||
|
||||
### Enrollments
|
||||
|
||||
Next, setup enrollments on the device. This can either be done through
|
||||
the UI, or via adb.
|
||||
|
||||
#### UI Enrollment
|
||||
|
||||
1. Tee up the results of the enrollment before starting the process:
|
||||
|
||||
```shell
|
||||
$ adb shell setprop vendor.fingerprint.virtual.next_enrollment 1:100,100,100:true
|
||||
```
|
||||
2. Navigate to `Settings -> Security -> Fingerprint Unlock` and follow the prompts.
|
||||
3. Verify the enrollments in the UI:
|
||||
|
||||
```shell
|
||||
$ adb shell getprop persist.vendor.fingerprint.virtual.enrollments
|
||||
```
|
||||
|
||||
#### Direct Enrollment
|
||||
|
||||
To set enrollment directly without the UI:
|
||||
|
||||
```shell
|
||||
$ adb root
|
||||
$ adb shell setprop persist.vendor.fingerprint.virtual.enrollments 1
|
||||
$ adb shell cmd fingerprint sync
|
||||
```
|
||||
|
||||
Note: You may need to do this twice. The templates are checked
|
||||
as part of some lazy operations, like user switching and startup, which can
|
||||
cause the framework to delete the enrollments before the sync operation runs.
|
||||
Until this is fixed, just run the commands twice as a workaround.
|
||||
|
||||
### Authenticate
|
||||
|
||||
To authenticate successfully set the enrolled id that should succeed. Unset it
|
||||
or change the value to make authenticate operations fail:
|
||||
|
||||
````shell
|
||||
$ adb shell setprop vendor.fingerprint.virtual.enrollment_hit 1
|
||||
````
|
||||
|
||||
### View HAL State
|
||||
|
||||
To view all the properties of the HAL (see `fingerprint.sysprop` for the API):
|
||||
|
||||
```shell
|
||||
$ adb shell getprop | grep vendor.fingerprint.virtual
|
||||
```
|
|
@ -101,7 +101,7 @@ ndk::ScopedAStatus Session::enroll(const keymaster::HardwareAuthToken& hat,
|
|||
if (shouldCancel(cancFuture)) {
|
||||
mCb->onError(Error::CANCELED, 0 /* vendorCode */);
|
||||
} else {
|
||||
mEngine->enrollImpl(mCb.get(), hat);
|
||||
mEngine->enrollImpl(mCb.get(), hat, cancFuture);
|
||||
}
|
||||
enterIdling();
|
||||
}));
|
||||
|
@ -123,7 +123,7 @@ ndk::ScopedAStatus Session::authenticate(int64_t operationId,
|
|||
if (shouldCancel(cancFuture)) {
|
||||
mCb->onError(Error::CANCELED, 0 /* vendorCode */);
|
||||
} else {
|
||||
mEngine->authenticateImpl(mCb.get(), operationId);
|
||||
mEngine->authenticateImpl(mCb.get(), operationId, cancFuture);
|
||||
}
|
||||
enterIdling();
|
||||
}));
|
||||
|
@ -144,7 +144,7 @@ ndk::ScopedAStatus Session::detectInteraction(std::shared_ptr<common::ICancellat
|
|||
if (shouldCancel(cancFuture)) {
|
||||
mCb->onError(Error::CANCELED, 0 /* vendorCode */);
|
||||
} else {
|
||||
mEngine->detectInteractionImpl(mCb.get());
|
||||
mEngine->detectInteractionImpl(mCb.get(), cancFuture);
|
||||
}
|
||||
enterIdling();
|
||||
}));
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
props {
|
||||
owner: Vendor
|
||||
module: "android.fingerprint.virt.FingerprintHalProperties"
|
||||
prop {
|
||||
api_name: "authenticator_id"
|
||||
type: Long
|
||||
access: ReadWrite
|
||||
prop_name: "vendor.fingerprint.virtual.authenticator_id"
|
||||
}
|
||||
prop {
|
||||
api_name: "challenge"
|
||||
type: Long
|
||||
access: ReadWrite
|
||||
prop_name: "vendor.fingerprint.virtual.challenge"
|
||||
}
|
||||
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: "vendor.fingerprint.virtual.lockout"
|
||||
}
|
||||
prop {
|
||||
api_name: "next_enrollment"
|
||||
type: String
|
||||
access: ReadWrite
|
||||
prop_name: "vendor.fingerprint.virtual.next_enrollment"
|
||||
}
|
||||
prop {
|
||||
api_name: "operation_authenticate_duration"
|
||||
type: Integer
|
||||
access: ReadWrite
|
||||
prop_name: "vendor.fingerprint.virtual.operation_authenticate_duration"
|
||||
}
|
||||
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_fails"
|
||||
access: ReadWrite
|
||||
prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_fails"
|
||||
}
|
||||
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_fails"
|
||||
access: ReadWrite
|
||||
prop_name: "vendor.fingerprint.virtual.operation_enroll_fails"
|
||||
}
|
||||
prop {
|
||||
api_name: "operation_enroll_latency"
|
||||
type: Integer
|
||||
access: ReadWrite
|
||||
prop_name: "vendor.fingerprint.virtual.operation_enroll_latency"
|
||||
}
|
||||
prop {
|
||||
api_name: "type"
|
||||
type: String
|
||||
access: ReadWrite
|
||||
prop_name: "persist.vendor.fingerprint.virtual.type"
|
||||
enum_values: "default|rear|udfps|side"
|
||||
}
|
||||
}
|
135
biometrics/fingerprint/aidl/default/fingerprint.sysprop
Normal file
135
biometrics/fingerprint/aidl/default/fingerprint.sysprop
Normal file
|
@ -0,0 +1,135 @@
|
|||
# fingerprint.sysprop
|
||||
# module becomes static class (Java) / namespace (C++) for serving API
|
||||
module: "android.fingerprint.virt.FingerprintHalProperties"
|
||||
owner: Vendor
|
||||
|
||||
# type of fingerprint sensor
|
||||
prop {
|
||||
prop_name: "persist.vendor.fingerprint.virtual.type"
|
||||
type: String
|
||||
scope: Public
|
||||
access: ReadWrite
|
||||
enum_values: "default|rear|udfps|side"
|
||||
api_name: "type"
|
||||
}
|
||||
|
||||
# ids of call current enrollments
|
||||
prop {
|
||||
prop_name: "persist.vendor.fingerprint.virtual.enrollments"
|
||||
type: IntegerList
|
||||
scope: Public
|
||||
access: ReadWrite
|
||||
api_name: "enrollments"
|
||||
}
|
||||
|
||||
# authenticate and detectInteraction will succeed with this
|
||||
# enrollment id, when present, otherwise they will error
|
||||
prop {
|
||||
prop_name: "vendor.fingerprint.virtual.enrollment_hit"
|
||||
type: Integer
|
||||
scope: Public
|
||||
access: ReadWrite
|
||||
api_name: "enrollment_hit"
|
||||
}
|
||||
|
||||
# the next enrollment in the format: "<id>:<delay>,<delay>,...:<result>"
|
||||
# for example: "2:0:true"
|
||||
# this property is reset after enroll completes
|
||||
prop {
|
||||
prop_name: "vendor.fingerprint.virtual.next_enrollment"
|
||||
type: String
|
||||
scope: Public
|
||||
access: ReadWrite
|
||||
api_name: "next_enrollment"
|
||||
}
|
||||
|
||||
# value for getAuthenticatorId or 0
|
||||
prop {
|
||||
prop_name: "vendor.fingerprint.virtual.authenticator_id"
|
||||
type: Long
|
||||
scope: Public
|
||||
access: ReadWrite
|
||||
api_name: "authenticator_id"
|
||||
}
|
||||
|
||||
# value for generateChallenge
|
||||
prop {
|
||||
prop_name: "vendor.fingerprint.virtual.challenge"
|
||||
type: Long
|
||||
scope: Public
|
||||
access: ReadWrite
|
||||
api_name: "challenge"
|
||||
}
|
||||
|
||||
# if locked out
|
||||
prop {
|
||||
prop_name: "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
|
||||
access: ReadWrite
|
||||
api_name: "operation_authenticate_fails"
|
||||
}
|
||||
|
||||
# force all detectInteraction operations to fail
|
||||
prop {
|
||||
prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_fails"
|
||||
type: Boolean
|
||||
scope: Public
|
||||
access: ReadWrite
|
||||
api_name: "operation_detect_interaction_fails"
|
||||
}
|
||||
|
||||
# force all enroll operations to fail
|
||||
prop {
|
||||
prop_name: "vendor.fingerprint.virtual.operation_enroll_fails"
|
||||
type: Boolean
|
||||
scope: Public
|
||||
access: ReadWrite
|
||||
api_name: "operation_enroll_fails"
|
||||
}
|
||||
|
||||
# add a latency to authentication operations
|
||||
prop {
|
||||
prop_name: "vendor.fingerprint.virtual.operation_authenticate_latency"
|
||||
type: Integer
|
||||
scope: Public
|
||||
access: ReadWrite
|
||||
api_name: "operation_authenticate_latency"
|
||||
}
|
||||
|
||||
# add a latency to detectInteraction operations
|
||||
prop {
|
||||
prop_name: "vendor.fingerprint.virtual.operation_detect_interaction_latency"
|
||||
type: Integer
|
||||
scope: Public
|
||||
access: ReadWrite
|
||||
api_name: "operation_detect_interaction_latency"
|
||||
}
|
||||
|
||||
# add a latency to enroll operations
|
||||
prop {
|
||||
prop_name: "vendor.fingerprint.virtual.operation_enroll_latency"
|
||||
type: Integer
|
||||
scope: Public
|
||||
access: ReadWrite
|
||||
api_name: "operation_enroll_latency"
|
||||
}
|
||||
|
||||
# millisecond duration for authenticate operations
|
||||
# (waits for changes to enrollment_hit)
|
||||
prop {
|
||||
prop_name: "vendor.fingerprint.virtual.operation_authenticate_duration"
|
||||
type: Integer
|
||||
scope: Public
|
||||
access: ReadWrite
|
||||
api_name: "operation_authenticate_duration"
|
||||
}
|
|
@ -16,71 +16,33 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <aidl/android/hardware/biometrics/fingerprint/ISessionCallback.h>
|
||||
|
||||
#include <random>
|
||||
|
||||
#include "CancellationSignal.h"
|
||||
|
||||
using namespace ::aidl::android::hardware::biometrics::common;
|
||||
|
||||
namespace aidl::android::hardware::biometrics::fingerprint {
|
||||
|
||||
// A fake engine that is backed by system properties instead of hardware.
|
||||
class FakeFingerprintEngine {
|
||||
public:
|
||||
FakeFingerprintEngine() : mRandom(std::mt19937::default_seed) {}
|
||||
|
||||
void generateChallengeImpl(ISessionCallback* cb) {
|
||||
LOG(INFO) << "generateChallengeImpl";
|
||||
std::uniform_int_distribution<int64_t> dist;
|
||||
auto challenge = dist(mRandom);
|
||||
cb->onChallengeGenerated(challenge);
|
||||
}
|
||||
|
||||
void revokeChallengeImpl(ISessionCallback* cb, int64_t challenge) {
|
||||
LOG(INFO) << "revokeChallengeImpl";
|
||||
cb->onChallengeRevoked(challenge);
|
||||
}
|
||||
|
||||
void enrollImpl(ISessionCallback* cb, const keymaster::HardwareAuthToken& hat) {
|
||||
LOG(INFO) << "enrollImpl";
|
||||
// Do proper HAT verification in the real implementation.
|
||||
if (hat.mac.empty()) {
|
||||
cb->onError(Error::UNABLE_TO_PROCESS, 0 /* vendorError */);
|
||||
return;
|
||||
}
|
||||
cb->onEnrollmentProgress(0 /* enrollmentId */, 0 /* remaining */);
|
||||
}
|
||||
|
||||
void authenticateImpl(ISessionCallback* cb, int64_t /* operationId */) {
|
||||
LOG(INFO) << "authenticateImpl";
|
||||
cb->onAuthenticationSucceeded(0 /* enrollmentId */, {} /* hat */);
|
||||
}
|
||||
|
||||
void detectInteractionImpl(ISessionCallback* cb) {
|
||||
LOG(INFO) << "detectInteractionImpl";
|
||||
cb->onInteractionDetected();
|
||||
}
|
||||
|
||||
void enumerateEnrollmentsImpl(ISessionCallback* cb) {
|
||||
LOG(INFO) << "enumerateEnrollmentsImpl";
|
||||
cb->onEnrollmentsEnumerated({} /* enrollmentIds */);
|
||||
}
|
||||
|
||||
void removeEnrollmentsImpl(ISessionCallback* cb, const std::vector<int32_t>& enrollmentIds) {
|
||||
LOG(INFO) << "removeEnrollmentsImpl";
|
||||
cb->onEnrollmentsRemoved(enrollmentIds);
|
||||
}
|
||||
|
||||
void getAuthenticatorIdImpl(ISessionCallback* cb) {
|
||||
LOG(INFO) << "getAuthenticatorIdImpl";
|
||||
cb->onAuthenticatorIdRetrieved(0 /* authenticatorId */);
|
||||
}
|
||||
|
||||
void invalidateAuthenticatorIdImpl(ISessionCallback* cb) {
|
||||
LOG(INFO) << "invalidateAuthenticatorIdImpl";
|
||||
cb->onAuthenticatorIdInvalidated(0 /* newAuthenticatorId */);
|
||||
}
|
||||
|
||||
void resetLockoutImpl(ISessionCallback* cb, const keymaster::HardwareAuthToken& /*hat*/) {
|
||||
LOG(INFO) << "resetLockoutImpl";
|
||||
cb->onLockoutCleared();
|
||||
}
|
||||
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);
|
||||
void enumerateEnrollmentsImpl(ISessionCallback* cb);
|
||||
void removeEnrollmentsImpl(ISessionCallback* cb, const std::vector<int32_t>& enrollmentIds);
|
||||
void getAuthenticatorIdImpl(ISessionCallback* cb);
|
||||
void invalidateAuthenticatorIdImpl(ISessionCallback* cb);
|
||||
void resetLockoutImpl(ISessionCallback* cb, const keymaster::HardwareAuthToken& /*hat*/);
|
||||
|
||||
std::mt19937 mRandom;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,297 @@
|
|||
/*
|
||||
* Copyright (C) 2021 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 <aidl/android/hardware/biometrics/fingerprint/BnSessionCallback.h>
|
||||
|
||||
#include "FakeFingerprintEngine.h"
|
||||
|
||||
using namespace ::android::fingerprint::virt;
|
||||
using namespace ::aidl::android::hardware::biometrics::fingerprint;
|
||||
using namespace ::aidl::android::hardware::keymaster;
|
||||
|
||||
namespace aidl::android::hardware::biometrics::fingerprint {
|
||||
|
||||
class TestSessionCallback : public BnSessionCallback {
|
||||
public:
|
||||
ndk::ScopedAStatus onChallengeGenerated(int64_t challenge) override {
|
||||
mLastChallenge = challenge;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
};
|
||||
::ndk::ScopedAStatus onChallengeRevoked(int64_t challenge) override {
|
||||
mLastChallengeRevoked = challenge;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
};
|
||||
::ndk::ScopedAStatus onError(fingerprint::Error error, int32_t) override {
|
||||
mError = error;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
};
|
||||
::ndk::ScopedAStatus onEnrollmentProgress(int32_t enrollmentId, int32_t remaining) override {
|
||||
if (remaining == 0) mLastEnrolled = enrollmentId;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
};
|
||||
|
||||
::ndk::ScopedAStatus onAuthenticationSucceeded(int32_t enrollmentId,
|
||||
const keymaster::HardwareAuthToken&) override {
|
||||
mLastAuthenticated = enrollmentId;
|
||||
mAuthenticateFailed = false;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
};
|
||||
::ndk::ScopedAStatus onAuthenticationFailed() override {
|
||||
mLastAuthenticated = 0;
|
||||
mAuthenticateFailed = true;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
};
|
||||
::ndk::ScopedAStatus onInteractionDetected() override {
|
||||
mInteractionDetectedCount++;
|
||||
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 {
|
||||
mLastEnrollmentEnumerated = enrollmentIds;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
};
|
||||
::ndk::ScopedAStatus onEnrollmentsRemoved(const std::vector<int32_t>& enrollmentIds) override {
|
||||
mLastEnrollmentRemoved = enrollmentIds;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
};
|
||||
::ndk::ScopedAStatus onAuthenticatorIdRetrieved(int64_t authenticatorId) override {
|
||||
mLastAuthenticatorId = authenticatorId;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
};
|
||||
::ndk::ScopedAStatus onAuthenticatorIdInvalidated(int64_t authenticatorId) override {
|
||||
mLastAuthenticatorId = authenticatorId;
|
||||
mAuthenticatorIdInvalidated = true;
|
||||
return ndk::ScopedAStatus::ok();
|
||||
};
|
||||
::ndk::ScopedAStatus onLockoutPermanent() override {
|
||||
mLockoutPermanent = true;
|
||||
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(); }
|
||||
|
||||
Error mError = Error::UNKNOWN;
|
||||
int64_t mLastChallenge = -1;
|
||||
int64_t mLastChallengeRevoked = -1;
|
||||
int32_t mLastEnrolled = -1;
|
||||
int32_t mLastAuthenticated = -1;
|
||||
int64_t mLastAuthenticatorId = -1;
|
||||
std::vector<int32_t> mLastEnrollmentEnumerated;
|
||||
std::vector<int32_t> mLastEnrollmentRemoved;
|
||||
bool mAuthenticateFailed = false;
|
||||
bool mAuthenticatorIdInvalidated = false;
|
||||
bool mLockoutPermanent = false;
|
||||
int mInteractionDetectedCount = 0;
|
||||
};
|
||||
|
||||
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);
|
||||
mCallback = ndk::SharedRefBase::make<TestSessionCallback>();
|
||||
}
|
||||
|
||||
FakeFingerprintEngine mEngine;
|
||||
std::shared_ptr<TestSessionCallback> mCallback;
|
||||
std::promise<void> mCancel;
|
||||
};
|
||||
|
||||
TEST_F(FakeFingerprintEngineTest, GenerateChallenge) {
|
||||
mEngine.generateChallengeImpl(mCallback.get());
|
||||
ASSERT_EQ(FingerprintHalProperties::challenge().value(), mCallback->mLastChallenge);
|
||||
}
|
||||
|
||||
TEST_F(FakeFingerprintEngineTest, RevokeChallenge) {
|
||||
auto challenge = FingerprintHalProperties::challenge().value_or(10);
|
||||
mEngine.revokeChallengeImpl(mCallback.get(), challenge);
|
||||
ASSERT_FALSE(FingerprintHalProperties::challenge().has_value());
|
||||
ASSERT_EQ(challenge, mCallback->mLastChallengeRevoked);
|
||||
}
|
||||
|
||||
TEST_F(FakeFingerprintEngineTest, ResetLockout) {
|
||||
FingerprintHalProperties::lockout(true);
|
||||
mEngine.resetLockoutImpl(mCallback.get(), {});
|
||||
ASSERT_FALSE(FingerprintHalProperties::lockout().value_or(true));
|
||||
}
|
||||
|
||||
TEST_F(FakeFingerprintEngineTest, AuthenticatorId) {
|
||||
FingerprintHalProperties::authenticator_id(50);
|
||||
mEngine.getAuthenticatorIdImpl(mCallback.get());
|
||||
ASSERT_EQ(50, mCallback->mLastAuthenticatorId);
|
||||
ASSERT_FALSE(mCallback->mAuthenticatorIdInvalidated);
|
||||
}
|
||||
|
||||
TEST_F(FakeFingerprintEngineTest, AuthenticatorIdInvalidate) {
|
||||
FingerprintHalProperties::authenticator_id(500);
|
||||
mEngine.invalidateAuthenticatorIdImpl(mCallback.get());
|
||||
ASSERT_NE(500, FingerprintHalProperties::authenticator_id().value());
|
||||
ASSERT_TRUE(mCallback->mAuthenticatorIdInvalidated);
|
||||
}
|
||||
|
||||
TEST_F(FakeFingerprintEngineTest, Enroll) {
|
||||
FingerprintHalProperties::enrollments({});
|
||||
FingerprintHalProperties::next_enrollment("4:0,0:true");
|
||||
keymaster::HardwareAuthToken hat{.mac = {2, 4}};
|
||||
mEngine.enrollImpl(mCallback.get(), hat, mCancel.get_future());
|
||||
ASSERT_FALSE(FingerprintHalProperties::next_enrollment().has_value());
|
||||
ASSERT_EQ(1, FingerprintHalProperties::enrollments().size());
|
||||
ASSERT_EQ(4, FingerprintHalProperties::enrollments()[0].value());
|
||||
ASSERT_EQ(4, mCallback->mLastEnrolled);
|
||||
}
|
||||
|
||||
TEST_F(FakeFingerprintEngineTest, EnrollCancel) {
|
||||
FingerprintHalProperties::enrollments({});
|
||||
auto next = "4:0,0:true";
|
||||
FingerprintHalProperties::next_enrollment(next);
|
||||
keymaster::HardwareAuthToken hat{.mac = {2, 4}};
|
||||
mCancel.set_value();
|
||||
mEngine.enrollImpl(mCallback.get(), hat, mCancel.get_future());
|
||||
ASSERT_EQ(Error::CANCELED, mCallback->mError);
|
||||
ASSERT_EQ(-1, mCallback->mLastEnrolled);
|
||||
ASSERT_EQ(0, FingerprintHalProperties::enrollments().size());
|
||||
ASSERT_EQ(next, FingerprintHalProperties::next_enrollment().value_or(""));
|
||||
}
|
||||
|
||||
TEST_F(FakeFingerprintEngineTest, EnrollFail) {
|
||||
FingerprintHalProperties::enrollments({});
|
||||
auto next = "2:0,0:false";
|
||||
FingerprintHalProperties::next_enrollment(next);
|
||||
keymaster::HardwareAuthToken hat{.mac = {2, 4}};
|
||||
mEngine.enrollImpl(mCallback.get(), hat, mCancel.get_future());
|
||||
ASSERT_EQ(Error::UNABLE_TO_PROCESS, mCallback->mError);
|
||||
ASSERT_EQ(-1, mCallback->mLastEnrolled);
|
||||
ASSERT_EQ(0, FingerprintHalProperties::enrollments().size());
|
||||
ASSERT_FALSE(FingerprintHalProperties::next_enrollment().has_value());
|
||||
}
|
||||
|
||||
TEST_F(FakeFingerprintEngineTest, Authenticate) {
|
||||
FingerprintHalProperties::enrollments({1, 2});
|
||||
FingerprintHalProperties::enrollment_hit(2);
|
||||
mEngine.authenticateImpl(mCallback.get(), 0, mCancel.get_future());
|
||||
ASSERT_FALSE(mCallback->mAuthenticateFailed);
|
||||
ASSERT_EQ(2, mCallback->mLastAuthenticated);
|
||||
}
|
||||
|
||||
TEST_F(FakeFingerprintEngineTest, AuthenticateCancel) {
|
||||
FingerprintHalProperties::enrollments({2});
|
||||
FingerprintHalProperties::enrollment_hit(2);
|
||||
mCancel.set_value();
|
||||
mEngine.authenticateImpl(mCallback.get(), 0, mCancel.get_future());
|
||||
ASSERT_EQ(Error::CANCELED, mCallback->mError);
|
||||
ASSERT_EQ(-1, mCallback->mLastAuthenticated);
|
||||
}
|
||||
|
||||
TEST_F(FakeFingerprintEngineTest, AuthenticateNotSet) {
|
||||
FingerprintHalProperties::enrollments({1, 2});
|
||||
FingerprintHalProperties::enrollment_hit({});
|
||||
mEngine.authenticateImpl(mCallback.get(), 0, mCancel.get_future());
|
||||
ASSERT_TRUE(mCallback->mAuthenticateFailed);
|
||||
ASSERT_EQ(mCallback->mError, Error::UNABLE_TO_PROCESS);
|
||||
}
|
||||
|
||||
TEST_F(FakeFingerprintEngineTest, AuthenticateNotEnrolled) {
|
||||
FingerprintHalProperties::enrollments({1, 2});
|
||||
FingerprintHalProperties::enrollment_hit(3);
|
||||
mEngine.authenticateImpl(mCallback.get(), 0, mCancel.get_future());
|
||||
ASSERT_TRUE(mCallback->mAuthenticateFailed);
|
||||
ASSERT_EQ(mCallback->mError, Error::UNABLE_TO_PROCESS);
|
||||
}
|
||||
|
||||
TEST_F(FakeFingerprintEngineTest, AuthenticateLockout) {
|
||||
FingerprintHalProperties::enrollments({22, 2});
|
||||
FingerprintHalProperties::enrollment_hit(2);
|
||||
FingerprintHalProperties::lockout(true);
|
||||
mEngine.authenticateImpl(mCallback.get(), 0, mCancel.get_future());
|
||||
ASSERT_TRUE(mCallback->mLockoutPermanent);
|
||||
ASSERT_NE(mCallback->mError, Error::UNKNOWN);
|
||||
}
|
||||
|
||||
TEST_F(FakeFingerprintEngineTest, InteractionDetect) {
|
||||
FingerprintHalProperties::enrollments({1, 2});
|
||||
FingerprintHalProperties::enrollment_hit(2);
|
||||
mEngine.detectInteractionImpl(mCallback.get(), mCancel.get_future());
|
||||
ASSERT_EQ(1, mCallback->mInteractionDetectedCount);
|
||||
}
|
||||
|
||||
TEST_F(FakeFingerprintEngineTest, InteractionDetectCancel) {
|
||||
FingerprintHalProperties::enrollments({1, 2});
|
||||
FingerprintHalProperties::enrollment_hit(2);
|
||||
mCancel.set_value();
|
||||
mEngine.detectInteractionImpl(mCallback.get(), mCancel.get_future());
|
||||
ASSERT_EQ(Error::CANCELED, mCallback->mError);
|
||||
ASSERT_EQ(0, mCallback->mInteractionDetectedCount);
|
||||
}
|
||||
|
||||
TEST_F(FakeFingerprintEngineTest, InteractionDetectNotSet) {
|
||||
FingerprintHalProperties::enrollments({1, 2});
|
||||
FingerprintHalProperties::enrollment_hit({});
|
||||
mEngine.detectInteractionImpl(mCallback.get(), mCancel.get_future());
|
||||
ASSERT_EQ(0, mCallback->mInteractionDetectedCount);
|
||||
}
|
||||
|
||||
TEST_F(FakeFingerprintEngineTest, InteractionDetectNotEnrolled) {
|
||||
FingerprintHalProperties::enrollments({1, 2});
|
||||
FingerprintHalProperties::enrollment_hit(25);
|
||||
mEngine.detectInteractionImpl(mCallback.get(), mCancel.get_future());
|
||||
ASSERT_EQ(0, mCallback->mInteractionDetectedCount);
|
||||
}
|
||||
|
||||
TEST_F(FakeFingerprintEngineTest, EnumerateEnrolled) {
|
||||
FingerprintHalProperties::enrollments({2, 4, 8});
|
||||
mEngine.enumerateEnrollmentsImpl(mCallback.get());
|
||||
ASSERT_EQ(3, mCallback->mLastEnrollmentEnumerated.size());
|
||||
for (auto id : FingerprintHalProperties::enrollments()) {
|
||||
ASSERT_TRUE(std::find(mCallback->mLastEnrollmentEnumerated.begin(),
|
||||
mCallback->mLastEnrollmentEnumerated.end(),
|
||||
id) != mCallback->mLastEnrollmentEnumerated.end());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(FakeFingerprintEngineTest, RemoveEnrolled) {
|
||||
FingerprintHalProperties::enrollments({2, 4, 8, 1});
|
||||
mEngine.removeEnrollmentsImpl(mCallback.get(), {2, 8});
|
||||
auto enrolls = FingerprintHalProperties::enrollments();
|
||||
ASSERT_EQ(2, mCallback->mLastEnrollmentRemoved.size());
|
||||
for (auto id : {2, 8}) {
|
||||
ASSERT_TRUE(std::find(mCallback->mLastEnrollmentRemoved.begin(),
|
||||
mCallback->mLastEnrollmentRemoved.end(),
|
||||
id) != mCallback->mLastEnrollmentRemoved.end());
|
||||
}
|
||||
ASSERT_EQ(2, enrolls.size());
|
||||
for (auto id : {1, 4}) {
|
||||
ASSERT_TRUE(std::find(enrolls.begin(), enrolls.end(), id) != enrolls.end());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace aidl::android::hardware::biometrics::fingerprint
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
testing::InitGoogleTest(&argc, argv);
|
||||
ABinderProcess_startThreadPool();
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
|
@ -15,8 +15,8 @@ cc_test {
|
|||
],
|
||||
srcs: ["VtsHalBiometricsFingerprintTargetTest.cpp"],
|
||||
static_libs: [
|
||||
"android.hardware.biometrics.common-V1-ndk",
|
||||
"android.hardware.biometrics.fingerprint-V1-ndk",
|
||||
"android.hardware.biometrics.common-V2-ndk",
|
||||
"android.hardware.biometrics.fingerprint-V2-ndk",
|
||||
"android.hardware.keymaster-V3-ndk",
|
||||
],
|
||||
shared_libs: [
|
||||
|
|
Loading…
Reference in a new issue