Face Virtual HAL lockout support

Bug: 294254230
Test: atest android.hardware.biometrics.face.FakeFaceEngineTest
Test: atest android.hardware.biometrics.face.FakeLockoutTrackerTest
Change-Id: I4ed3ada4734f595d5f9ac70cf5ed2a94bed378c6
This commit is contained in:
Jeff Pu 2023-12-07 17:25:22 +00:00
parent d27ec7f9b5
commit 3e7448dc2b
8 changed files with 580 additions and 17 deletions

View file

@ -30,6 +30,7 @@ cc_binary {
"libnativewindow",
],
srcs: [
"FakeLockoutTracker.cpp",
"main.cpp",
"Face.cpp",
"FakeFaceEngine.cpp",
@ -63,6 +64,33 @@ cc_test {
srcs: [
"tests/FakeFaceEngineTest.cpp",
"FakeFaceEngine.cpp",
"FakeLockoutTracker.cpp",
],
shared_libs: [
"libbase",
"libbinder_ndk",
"libnativewindow",
],
include_dirs: [
"frameworks/native/aidl/gui",
],
static_libs: [
"libandroid.hardware.biometrics.face.VirtualProps",
"android.hardware.biometrics.face-V4-ndk",
"android.hardware.biometrics.common-V4-ndk",
"android.hardware.keymaster-V4-ndk",
"android.hardware.biometrics.common.util",
],
vendor: true,
test_suites: ["general-tests"],
require_root: true,
}
cc_test {
name: "android.hardware.biometrics.face.FakeLockoutTrackerTest",
srcs: [
"tests/FakeLockoutTrackerTest.cpp",
"FakeLockoutTracker.cpp",
],
shared_libs: [
"libbase",

View file

@ -1,3 +1,21 @@
/*
* Copyright (C) 2023 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.
*/
#define LOG_TAG "FaceVirtualHalEngine"
#include "FakeFaceEngine.h"
#include <android-base/logging.h>
@ -186,6 +204,10 @@ void FakeFaceEngine::authenticateImpl(ISessionCallback* cb, int64_t /*operationI
return;
}
if (mLockoutTracker.checkIfLockout(cb)) {
return;
}
int i = 0;
do {
if (FaceHalProperties::lockout().value_or(false)) {
@ -197,6 +219,7 @@ void FakeFaceEngine::authenticateImpl(ISessionCallback* cb, int64_t /*operationI
if (FaceHalProperties::operation_authenticate_fails().value_or(false)) {
LOG(ERROR) << "Fail: operation_authenticate_fails";
mLockoutTracker.addFailedAttempt(cb);
cb->onAuthenticationFailed();
return;
}
@ -231,10 +254,12 @@ void FakeFaceEngine::authenticateImpl(ISessionCallback* cb, int64_t /*operationI
} while (!Util::hasElapsed(now, duration));
if (id > 0 && isEnrolled) {
mLockoutTracker.reset();
cb->onAuthenticationSucceeded(id, {} /* hat */);
return;
} else {
LOG(ERROR) << "Fail: face not enrolled";
mLockoutTracker.addFailedAttempt(cb);
cb->onAuthenticationFailed();
cb->onError(Error::TIMEOUT, 0 /* vendorError*/);
return;
@ -389,6 +414,7 @@ void FakeFaceEngine::resetLockoutImpl(ISessionCallback* cb,
const keymaster::HardwareAuthToken& /*hat*/) {
BEGIN_OP(0);
FaceHalProperties::lockout(false);
mLockoutTracker.reset();
cb->onLockoutCleared();
}

View file

@ -16,18 +16,17 @@
#pragma once
#define LOG_TAG "FaceVirtualHal"
#include <aidl/android/hardware/biometrics/common/SensorStrength.h>
#include <aidl/android/hardware/biometrics/face/BnSession.h>
#include <aidl/android/hardware/biometrics/face/FaceSensorType.h>
#include <aidl/android/hardware/biometrics/face/ISessionCallback.h>
#include <random>
#include <future>
#include <random>
#include <vector>
#include "FakeLockoutTracker.h"
namespace aidl::android::hardware::biometrics::face {
namespace face = aidl::android::hardware::biometrics::face;
@ -39,6 +38,7 @@ using aidl::android::hardware::common::NativeHandle;
class FakeFaceEngine {
public:
FakeFaceEngine() : mRandom(std::mt19937::default_seed) {}
virtual ~FakeFaceEngine() {}
static face::FaceSensorType GetSensorType();
static common::SensorStrength GetSensorStrength();
@ -61,6 +61,13 @@ class FakeFaceEngine {
void invalidateAuthenticatorIdImpl(ISessionCallback* cb);
void resetLockoutImpl(ISessionCallback* cb, const keymaster::HardwareAuthToken& /*hat*/);
virtual std::string toString() const {
std::ostringstream os;
os << "----- FakeFaceEngine:: -----" << std::endl;
os << mLockoutTracker.toString();
return os.str();
}
std::mt19937 mRandom;
private:
@ -68,6 +75,7 @@ class FakeFaceEngine {
static constexpr int32_t FACE_ERROR_VENDOR_BASE = 1000;
std::pair<AcquiredInfo, int32_t> convertAcquiredInfo(int32_t code);
std::pair<Error, int32_t> convertError(int32_t code);
FakeLockoutTracker mLockoutTracker;
};
} // namespace aidl::android::hardware::biometrics::face

View file

@ -0,0 +1,138 @@
/*
* Copyright (C) 2023 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.
*/
#define LOG_TAG "FaceVirtualHalLockoutTracker"
#include "FakeLockoutTracker.h"
#include <android-base/logging.h>
#include <face.sysprop.h>
#include "util/Util.h"
using namespace ::android::face::virt;
namespace aidl::android::hardware::biometrics::face {
void FakeLockoutTracker::reset(bool dueToTimerExpire) {
if (!dueToTimerExpire) {
mFailedCount = 0;
mLastFailedTime = 0;
}
mTimedFailedCount = 0;
mCurrentMode = LockoutMode::kNone;
abortTimer();
}
void FakeLockoutTracker::addFailedAttempt(ISessionCallback* cb) {
bool lockoutEnabled = FaceHalProperties::lockout_enable().value_or(false);
bool timedLockoutenabled = FaceHalProperties::lockout_timed_enable().value_or(false);
if (lockoutEnabled) {
mFailedCount++;
mTimedFailedCount++;
mLastFailedTime = Util::getSystemNanoTime();
int32_t lockoutTimedThreshold = FaceHalProperties::lockout_timed_threshold().value_or(3);
int32_t lockoutPermanetThreshold =
FaceHalProperties::lockout_permanent_threshold().value_or(5);
if (mFailedCount >= lockoutPermanetThreshold) {
mCurrentMode = LockoutMode::kPermanent;
LOG(ERROR) << "FakeLockoutTracker: lockoutPermanent";
cb->onLockoutPermanent();
abortTimer();
} else if (timedLockoutenabled && mTimedFailedCount >= lockoutTimedThreshold) {
if (mCurrentMode == LockoutMode::kNone) {
mCurrentMode = LockoutMode::kTimed;
startLockoutTimer(getTimedLockoutDuration(), cb);
}
LOG(ERROR) << "FakeLockoutTracker: lockoutTimed";
cb->onLockoutTimed(getLockoutTimeLeft());
}
} else {
reset();
}
}
FakeLockoutTracker::LockoutMode FakeLockoutTracker::getMode() {
return mCurrentMode;
}
int32_t FakeLockoutTracker::getTimedLockoutDuration() {
return FaceHalProperties::lockout_timed_duration().value_or(10 * 1000);
}
int64_t FakeLockoutTracker::getLockoutTimeLeft() {
int64_t res = 0;
if (mLastFailedTime > 0) {
auto now = Util::getSystemNanoTime();
auto elapsed = (now - mLastFailedTime) / 1000000LL;
res = getTimedLockoutDuration() - elapsed;
LOG(INFO) << "elapsed=" << elapsed << " now = " << now
<< " mLastFailedTime=" << mLastFailedTime << " res=" << res;
}
return res;
}
bool FakeLockoutTracker::checkIfLockout(ISessionCallback* cb) {
if (mCurrentMode == LockoutMode::kPermanent) {
LOG(ERROR) << "Lockout permanent";
cb->onLockoutPermanent();
return true;
} else if (mCurrentMode == LockoutMode::kTimed) {
auto timeLeft = getLockoutTimeLeft();
LOG(ERROR) << "Lockout timed " << timeLeft;
cb->onLockoutTimed(timeLeft);
return true;
}
return false;
}
void FakeLockoutTracker::startLockoutTimer(int64_t timeout, ISessionCallback* cb) {
LOG(ERROR) << "startLockoutTimer: to=" << timeout;
if (mIsLockoutTimerStarted) return;
std::function<void(ISessionCallback*)> action =
std::bind(&FakeLockoutTracker::lockoutTimerExpired, this, std::placeholders::_1);
std::thread([timeout, action, cb]() {
std::this_thread::sleep_for(std::chrono::milliseconds(timeout));
action(cb);
}).detach();
mIsLockoutTimerStarted = true;
}
void FakeLockoutTracker::lockoutTimerExpired(ISessionCallback* cb) {
LOG(INFO) << "lockout timer expired";
mIsLockoutTimerStarted = false;
if (mIsLockoutTimerAborted) {
mIsLockoutTimerAborted = false;
return;
}
// if more failures seen since the timer started, need to restart timer again
auto deltaTime = getLockoutTimeLeft();
if (deltaTime <= 0) {
cb->onLockoutCleared();
reset(true);
} else {
startLockoutTimer(deltaTime, cb);
}
}
void FakeLockoutTracker::abortTimer() {
if (mIsLockoutTimerStarted) mIsLockoutTimerAborted = true;
}
} // namespace aidl::android::hardware::biometrics::face

View file

@ -0,0 +1,70 @@
/*
* Copyright (C) 2023 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 <aidl/android/hardware/biometrics/face/ISessionCallback.h>
#include <android/binder_to_string.h>
#include <stdint.h>
#include <string>
namespace aidl::android::hardware::biometrics::face {
// Lockout implementation for Face Virtual HAL
class FakeLockoutTracker {
public:
FakeLockoutTracker()
: mFailedCount(0),
mLastFailedTime(0),
mIsLockoutTimerStarted(false),
mIsLockoutTimerAborted(false) {}
~FakeLockoutTracker() {}
enum class LockoutMode : int8_t { kNone = 0, kTimed, kPermanent };
bool checkIfLockout(ISessionCallback*);
void addFailedAttempt(ISessionCallback*);
int64_t getLockoutTimeLeft();
LockoutMode getMode();
void reset(bool dueToTimerExpire = false);
inline std::string toString() const {
std::ostringstream os;
os << "----- FakeLockoutTracker:: -----" << std::endl;
os << "mFailedCount:" << mFailedCount;
os << ", mCurrentMode:" << (int)mCurrentMode;
os << ", mLastFailedTime:" << (int)(mLastFailedTime / 1000000LL);
os << ", mIsLockoutTimerStarted:" << mIsLockoutTimerStarted;
os << ", mIsLockoutTimerAborted:" << mIsLockoutTimerAborted;
os << std::endl;
return os.str();
}
private:
void startLockoutTimer(int64_t timeout, ISessionCallback* cb);
void lockoutTimerExpired(ISessionCallback* cb);
int32_t getTimedLockoutDuration();
void abortTimer();
private:
int32_t mFailedCount;
int32_t mTimedFailedCount;
int64_t mLastFailedTime;
LockoutMode mCurrentMode;
bool mIsLockoutTimerStarted;
bool mIsLockoutTimerAborted;
};
} // namespace aidl::android::hardware::biometrics::face

View file

@ -51,31 +51,31 @@ $ adb shell cmd face syncadb shell cmd face sync
To authenticate successfully, the captured (hit) must match the enrollment id<br/>
set above. To trigger authentication failure, set the hit id to a different value.
```shell
`shell
$ adb shell setprop vendor.face.virtual.operation_authenticate_duration 800
$ adb shell setprop vendor.face.virtual.enrollment_hit 1
```
$ adb shell setprop vendor.face.virtual.enrollment_hit 1`
### AcquiredInfo
AcquiredInfo codes can be sent during authentication by specifying the sysprop.<br/>
The codes is sent in sequence and in the interval of operation_authentication_duration/numberOfAcquiredInfoCode
```shell
$ adb shell setprop vendor.face.virtual.operation_authenticate_acquired 6,9,1013
```
`shell
$ adb shell setprop vendor.face.virtual.operation_authenticate_acquired 6,9,1013`
Refer to [AcquiredInfo.aidl](https://source.corp.google.com/h/googleplex-android/platform/superproject/main/+/main:hardware/interfaces/biometrics/face/aidl/android/hardware/biometrics/face/AcquiredInfo.aidl) for full face acquiredInfo codes.
Note: For vendor specific acquired info, acquiredInfo = 1000 + vendorCode.
### Error Insertion
Error can be inserted during authentction by specifying the authenticate_error sysprop.
```shell
$ adb shell setprop vendor.face.virtual.operation_authenticate_error 4
```
Refer to [Error.aidl](https://source.corp.google.com/h/googleplex-android/platform/superproject/main/+/main:hardware/interfaces/biometrics/face/aidl/android/hardware/biometrics/face/Error.aidl) for full face error codes
Error can be inserted during authentction by specifying the authenticate_error
sysprop. `shell $ adb shell setprop
vendor.face.virtual.operation_authenticate_error 4` Refer to
[Error.aidl](https://source.corp.google.com/h/googleplex-android/platform/superproject/main/+/main:hardware/interfaces/biometrics/face/aidl/android/hardware/biometrics/face/Error.aidl)
for full face error codes
## Enrollment via Settings
Enrollment process is specified by sysprop `next_enrollment` in the following format
Enrollment process is specified by sysprop `next_enrollment` in the following
format
```shell
Format: <id>:<progress_ms-[acquiredInfo,...],...:<success>
@ -88,7 +88,40 @@ Format: <id>:<progress_ms-[acquiredInfo,...],...:<success>
E.g.
$ adb shell setprop vendor.face.virtual.next_enrollment 1:6000-[21,8,1,1108,1,10,1113,1,1118,1124]:true
```
If next_enrollment prop is not set, the following default value is used:<br/>
&nbsp;&nbsp;defaultNextEnrollment="1:1000-[21,7,1,1103],1500-[1108,1],2000-[1113,1],2500-[1118,1]:true"<br/>
Note: Enrollment data and configuration can be supported upon request in case of needs
## Lockout
Device lockout is based on the number of consecutive failed authentication attempts. There are a few
flavors of lockout mechanisms that are supported by virtula HAL <br/>
### Permanent Lockout
There are two sysprop to control permanent lockout <br/>
1. general lockout feature enable <br/>
2. threshold of failed attempts <br/>
`shell
$ adb shell setprop persist.vendor.face.virtual.lockout_enable true
$ adb shell setprop persist.vendor.face.virtual.lockout_permanent_threshold 3`
### Temporary Lockout
There are a few parameters to control temporary lockout (aka timed lockout): <br/>
1. enable lockout (general lockout feature enable, and timed lcokout enable) <br/>
2. threshold of failed attempts <br/>
3. timeout in ms <br/>
`shell
$ adb shell setprop persist.vendor.face.virtual.lockout_enable true
$ adb shell setprop persist.vendor.face.virtual.lockout_timed_enable true
$ adb shell setprop persist.vendor.face.virtual.lockout_timed_threshold 5
$ adb shell setprop persist.vendor.face.virtual.lockout_timed_duration 10000`
### Forced Lockout
A permanent lockout can be inserted on next authentication attempt independent of the failed <br/>
attempt count. This is a feature purely for test purpose.
`shell
$ adb shell setprop persist.vendor.face.virtual.lockout true`

View file

@ -92,7 +92,7 @@ prop {
api_name: "challenge"
}
# if locked out
# if forced to lock out (Default to false)
prop {
prop_name: "vendor.face.virtual.lockout"
type: Boolean
@ -176,3 +176,47 @@ prop {
api_name: "operation_authenticate_acquired"
}
# whether support lockout based on the failed auth attempts (default: false)
prop {
prop_name: "persist.vendor.face.virtual.lockout_enable"
type: Boolean
scope: Internal
access: ReadWrite
api_name: "lockout_enable"
}
# whether support timed_lockout based on the failed auth attempts (default: false)
prop {
prop_name: "persist.vendor.face.virtual.lockout_timed_enable"
type: Boolean
scope: Internal
access: ReadWrite
api_name: "lockout_timed_enable"
}
# temperory lockout threshold in number of consecutive failed auth attempts
prop {
prop_name: "persist.vendor.face.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.face.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
prop {
prop_name: "persist.vendor.face.virtual.lockout_permanent_threshold"
type: Integer
scope: Internal
access: ReadWrite
api_name: "lockout_permanent_threshold"
}

View file

@ -0,0 +1,216 @@
/*
* Copyright (C) 2023 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 <aidl/android/hardware/biometrics/face/BnSessionCallback.h>
#include <android/binder_process.h>
#include <face.sysprop.h>
#include <gtest/gtest.h>
#include <android-base/logging.h>
#include "FakeLockoutTracker.h"
#include "util/Util.h"
using namespace ::android::face::virt;
using namespace ::aidl::android::hardware::biometrics::face;
namespace aidl::android::hardware::biometrics::face {
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(face::Error, int32_t /*vendorCode*/) override {
return ndk::ScopedAStatus::ok();
};
::ndk::ScopedAStatus onEnrollmentProgress(int32_t /*enrollmentId*/,
int32_t /*remaining*/) override {
return ndk::ScopedAStatus::ok();
};
::ndk::ScopedAStatus onAuthenticationSucceeded(int32_t /*enrollmentId*/,
const keymaster::HardwareAuthToken&) override {
return ndk::ScopedAStatus::ok();
};
::ndk::ScopedAStatus onAuthenticationFailed() override { return ndk::ScopedAStatus::ok(); };
::ndk::ScopedAStatus onInteractionDetected() override { return ndk::ScopedAStatus::ok(); };
::ndk::ScopedAStatus onEnrollmentsEnumerated(const std::vector<int32_t>&) 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 onEnrollmentFrame(const EnrollmentFrame&) override {
return ndk::ScopedAStatus::ok();
}
::ndk::ScopedAStatus onFeaturesRetrieved(const std::vector<Feature>&) {
return ndk::ScopedAStatus::ok();
};
::ndk::ScopedAStatus onFeatureSet(Feature) override { return ndk::ScopedAStatus::ok(); }
::ndk::ScopedAStatus onSessionClosed() override { return ndk::ScopedAStatus::ok(); }
::ndk::ScopedAStatus onAuthenticationFrame(const AuthenticationFrame&) override {
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus onLockoutTimed(int64_t timeLeft) override {
mLockoutTimed++;
mTimeLeft = timeLeft;
return ndk::ScopedAStatus::ok();
};
::ndk::ScopedAStatus onLockoutPermanent() override {
mLockoutPermanent++;
return ndk::ScopedAStatus::ok();
};
::ndk::ScopedAStatus onLockoutCleared() override {
mTimeLeft = 0;
mLockoutTimed = 0;
mLockoutPermanent = 0;
return ndk::ScopedAStatus::ok();
};
int64_t mTimeLeft = 0;
int mLockoutTimed = 0;
int mLockoutPermanent = 0;
};
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 {
FaceHalProperties::lockout_timed_threshold(LOCKOUT_TIMED_THRESHOLD);
FaceHalProperties::lockout_timed_duration(LOCKOUT_TIMED_DURATION);
FaceHalProperties::lockout_permanent_threshold(LOCKOUT_PERMANENT_THRESHOLD);
mCallback = ndk::SharedRefBase::make<TestSessionCallback>();
}
void TearDown() override {
// reset to default
FaceHalProperties::lockout_timed_threshold(5);
FaceHalProperties::lockout_timed_duration(20);
FaceHalProperties::lockout_permanent_threshold(10000);
FaceHalProperties::lockout_enable(false);
FaceHalProperties::lockout(false);
}
FakeLockoutTracker mLockoutTracker;
std::shared_ptr<TestSessionCallback> mCallback;
};
TEST_F(FakeLockoutTrackerTest, addFailedAttemptDisable) {
FaceHalProperties::lockout_enable(false);
for (int i = 0; i < LOCKOUT_TIMED_THRESHOLD + 1; i++)
mLockoutTracker.addFailedAttempt(mCallback.get());
ASSERT_EQ(mLockoutTracker.getMode(), FakeLockoutTracker::LockoutMode::kNone);
ASSERT_EQ(0, mCallback->mLockoutTimed);
}
TEST_F(FakeLockoutTrackerTest, addFailedAttemptPermanent) {
FaceHalProperties::lockout_enable(true);
ASSERT_FALSE(mLockoutTracker.checkIfLockout(mCallback.get()));
for (int i = 0; i < LOCKOUT_PERMANENT_THRESHOLD - 1; i++)
mLockoutTracker.addFailedAttempt(mCallback.get());
ASSERT_NE(mLockoutTracker.getMode(), FakeLockoutTracker::LockoutMode::kPermanent);
ASSERT_EQ(0, mCallback->mLockoutPermanent);
mLockoutTracker.addFailedAttempt(mCallback.get());
ASSERT_EQ(mLockoutTracker.getMode(), FakeLockoutTracker::LockoutMode::kPermanent);
ASSERT_EQ(1, mCallback->mLockoutPermanent);
ASSERT_TRUE(mLockoutTracker.checkIfLockout(mCallback.get()));
ASSERT_EQ(2, mCallback->mLockoutPermanent);
}
TEST_F(FakeLockoutTrackerTest, addFailedAttemptLockoutTimed) {
FaceHalProperties::lockout_enable(true);
FaceHalProperties::lockout_timed_enable(true);
ASSERT_FALSE(mLockoutTracker.checkIfLockout(mCallback.get()));
for (int i = 0; i < LOCKOUT_TIMED_THRESHOLD; i++)
mLockoutTracker.addFailedAttempt(mCallback.get());
ASSERT_EQ(mLockoutTracker.getMode(), FakeLockoutTracker::LockoutMode::kTimed);
ASSERT_EQ(1, mCallback->mLockoutTimed);
ASSERT_TRUE(mLockoutTracker.checkIfLockout(mCallback.get()));
ASSERT_EQ(2, mCallback->mLockoutTimed);
// time left
int N = 5;
int64_t prevTimeLeft = INT_MAX;
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;
}
SLEEP_MS(LOCKOUT_TIMED_DURATION / N);
ASSERT_EQ(mLockoutTracker.getMode(), FakeLockoutTracker::LockoutMode::kNone);
}
TEST_F(FakeLockoutTrackerTest, addFailedAttemptLockout_TimedThenPermanent) {
FaceHalProperties::lockout_enable(true);
FaceHalProperties::lockout_timed_enable(true);
ASSERT_FALSE(mLockoutTracker.checkIfLockout(mCallback.get()));
for (int i = 0; i < LOCKOUT_TIMED_THRESHOLD; i++)
mLockoutTracker.addFailedAttempt(mCallback.get());
ASSERT_EQ(mLockoutTracker.getMode(), FakeLockoutTracker::LockoutMode::kTimed);
SLEEP_MS(LOCKOUT_TIMED_DURATION + 20);
ASSERT_EQ(mLockoutTracker.getMode(), FakeLockoutTracker::LockoutMode::kNone);
for (int i = 0; i < LOCKOUT_PERMANENT_THRESHOLD - LOCKOUT_TIMED_THRESHOLD; i++)
mLockoutTracker.addFailedAttempt(mCallback.get());
ASSERT_EQ(mLockoutTracker.getMode(), FakeLockoutTracker::LockoutMode::kPermanent);
}
TEST_F(FakeLockoutTrackerTest, addFailedAttemptLockoutTimedTwice) {
FaceHalProperties::lockout_enable(true);
FaceHalProperties::lockout_timed_enable(true);
ASSERT_FALSE(mLockoutTracker.checkIfLockout(mCallback.get()));
ASSERT_EQ(0, mCallback->mLockoutTimed);
for (int i = 0; i < LOCKOUT_TIMED_THRESHOLD; i++)
mLockoutTracker.addFailedAttempt(mCallback.get());
SLEEP_MS(LOCKOUT_TIMED_DURATION / 2);
mLockoutTracker.addFailedAttempt(mCallback.get());
SLEEP_MS(LOCKOUT_TIMED_DURATION);
ASSERT_EQ(2, mCallback->mLockoutTimed);
ASSERT_TRUE(mLockoutTracker.checkIfLockout(mCallback.get()));
SLEEP_MS(LOCKOUT_TIMED_DURATION);
ASSERT_FALSE(mLockoutTracker.checkIfLockout(mCallback.get()));
}
TEST_F(FakeLockoutTrackerTest, resetLockout) {
FaceHalProperties::lockout_enable(true);
ASSERT_EQ(mLockoutTracker.getMode(), FakeLockoutTracker::LockoutMode::kNone);
for (int i = 0; i < LOCKOUT_PERMANENT_THRESHOLD; i++)
mLockoutTracker.addFailedAttempt(mCallback.get());
ASSERT_EQ(mLockoutTracker.getMode(), FakeLockoutTracker::LockoutMode::kPermanent);
mLockoutTracker.reset();
ASSERT_FALSE(mLockoutTracker.checkIfLockout(mCallback.get()));
}
} // namespace aidl::android::hardware::biometrics::face
int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
ABinderProcess_startThreadPool();
return RUN_ALL_TESTS();
}