Face Virtual HAL lockout support
Bug: 294254230
Test: atest android.hardware.biometrics.face.FakeFaceEngineTest
Test: atest android.hardware.biometrics.face.FakeLockoutTrackerTest
Change-Id: I4ed3ada4734f595d5f9ac70cf5ed2a94bed378c6
diff --git a/biometrics/face/aidl/default/FakeLockoutTracker.cpp b/biometrics/face/aidl/default/FakeLockoutTracker.cpp
new file mode 100644
index 0000000..70bf08e
--- /dev/null
+++ b/biometrics/face/aidl/default/FakeLockoutTracker.cpp
@@ -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