Merge changes I3a908b0f,I582d44d5 into sc-dev
* changes:
Implement the state machine
Add move-only lambdas support
diff --git a/biometrics/fingerprint/aidl/default/Android.bp b/biometrics/fingerprint/aidl/default/Android.bp
index 24087cf..6b43bff 100644
--- a/biometrics/fingerprint/aidl/default/Android.bp
+++ b/biometrics/fingerprint/aidl/default/Android.bp
@@ -6,9 +6,11 @@
vintf_fragments: ["fingerprint-default.xml"],
local_include_dirs: ["include"],
srcs: [
- "main.cpp",
+ "CancellationSignal.cpp",
"Fingerprint.cpp",
"Session.cpp",
+ "WorkerThread.cpp",
+ "main.cpp",
],
shared_libs: [
"libbase",
diff --git a/biometrics/fingerprint/aidl/default/CancellationSignal.cpp b/biometrics/fingerprint/aidl/default/CancellationSignal.cpp
new file mode 100644
index 0000000..6598316
--- /dev/null
+++ b/biometrics/fingerprint/aidl/default/CancellationSignal.cpp
@@ -0,0 +1,37 @@
+/*
+ * 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 "CancellationSignal.h"
+
+#include <android-base/logging.h>
+#include <chrono>
+
+namespace aidl::android::hardware::biometrics::fingerprint {
+
+CancellationSignal::CancellationSignal(std::promise<void>&& cancellationPromise)
+ : mCancellationPromise(std::move(cancellationPromise)) {}
+
+ndk::ScopedAStatus CancellationSignal::cancel() {
+ mCancellationPromise.set_value();
+ return ndk::ScopedAStatus::ok();
+}
+
+bool shouldCancel(const std::future<void>& f) {
+ CHECK(f.valid());
+ return f.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
+}
+
+} // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/Fingerprint.cpp b/biometrics/fingerprint/aidl/default/Fingerprint.cpp
index 6f89346..67dc34f 100644
--- a/biometrics/fingerprint/aidl/default/Fingerprint.cpp
+++ b/biometrics/fingerprint/aidl/default/Fingerprint.cpp
@@ -15,11 +15,12 @@
*/
#include "Fingerprint.h"
+
#include "Session.h"
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;
@@ -32,7 +33,8 @@
} // namespace
-Fingerprint::Fingerprint() {}
+Fingerprint::Fingerprint()
+ : mEngine(std::make_unique<FakeFingerprintEngine>()), mWorker(MAX_WORKER_QUEUE_SIZE) {}
ndk::ScopedAStatus Fingerprint::getSensorProps(std::vector<SensorProps>* out) {
std::vector<common::HardwareInfo> hardwareInfos = {
@@ -52,14 +54,21 @@
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Fingerprint::createSession(int32_t /*sensorId*/, int32_t /*userId*/,
+ndk::ScopedAStatus Fingerprint::createSession(int32_t sensorId, int32_t userId,
const std::shared_ptr<ISessionCallback>& cb,
std::shared_ptr<ISession>* out) {
- *out = SharedRefBase::make<Session>(cb);
+ auto sessionSp = mSession.lock();
+ CHECK(sessionSp == nullptr || sessionSp->isClosed()) << "Open session already exists!";
+
+ auto session = SharedRefBase::make<Session>(sensorId, userId, cb, mEngine.get(), &mWorker);
+ mSession = session;
+ *out = session;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus Fingerprint::reset() {
+ // Crash. The system will start a fresh instance of the HAL.
+ CHECK(false) << "Unable to reset. Crashing.";
return ndk::ScopedAStatus::ok();
}
diff --git a/biometrics/fingerprint/aidl/default/Session.cpp b/biometrics/fingerprint/aidl/default/Session.cpp
index c928df4..f6a0314 100644
--- a/biometrics/fingerprint/aidl/default/Session.cpp
+++ b/biometrics/fingerprint/aidl/default/Session.cpp
@@ -14,93 +14,216 @@
* limitations under the License.
*/
-#include <aidl/android/hardware/biometrics/common/BnCancellationSignal.h>
+#include "Session.h"
+
#include <android-base/logging.h>
-#include "Session.h"
+#include "CancellationSignal.h"
namespace aidl::android::hardware::biometrics::fingerprint {
-class CancellationSignal : public common::BnCancellationSignal {
- public:
- ndk::ScopedAStatus cancel() override { return ndk::ScopedAStatus::ok(); }
-};
+Session::Session(int sensorId, int userId, std::shared_ptr<ISessionCallback> cb,
+ FakeFingerprintEngine* engine, WorkerThread* worker)
+ : mSensorId(sensorId),
+ mUserId(userId),
+ mCb(std::move(cb)),
+ mEngine(engine),
+ mWorker(worker),
+ mScheduledState(SessionState::IDLING),
+ mCurrentState(SessionState::IDLING) {
+ CHECK_GE(mSensorId, 0);
+ CHECK_GE(mUserId, 0);
+ CHECK(mEngine);
+ CHECK(mWorker);
+ CHECK(mCb);
+}
-Session::Session(std::shared_ptr<ISessionCallback> cb) : mCb(std::move(cb)) {}
+void Session::scheduleStateOrCrash(SessionState state) {
+ CHECK(mScheduledState == SessionState::IDLING);
+ CHECK(mCurrentState == SessionState::IDLING);
+ mScheduledState = state;
+}
-ndk::ScopedAStatus Session::generateChallenge(int32_t /*cookie*/, int32_t /*timeoutSec*/) {
+void Session::enterStateOrCrash(int cookie, SessionState state) {
+ CHECK(mScheduledState == state);
+ mCurrentState = mScheduledState;
+ mScheduledState = SessionState::IDLING;
+ mCb->onStateChanged(cookie, mCurrentState);
+}
+
+void Session::enterIdling(int cookie) {
+ mCurrentState = SessionState::IDLING;
+ mCb->onStateChanged(cookie, mCurrentState);
+}
+
+bool Session::isClosed() {
+ return mCurrentState == SessionState::CLOSED;
+}
+
+ndk::ScopedAStatus Session::generateChallenge(int32_t cookie, int32_t timeoutSec) {
LOG(INFO) << "generateChallenge";
+ scheduleStateOrCrash(SessionState::GENERATING_CHALLENGE);
+
+ mWorker->schedule(Callable::from([this, cookie, timeoutSec] {
+ enterStateOrCrash(cookie, SessionState::GENERATING_CHALLENGE);
+ mEngine->generateChallengeImpl(mCb.get(), timeoutSec);
+ enterIdling(cookie);
+ }));
+
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Session::revokeChallenge(int32_t /*cookie*/, int64_t /*challenge*/) {
+ndk::ScopedAStatus Session::revokeChallenge(int32_t cookie, int64_t challenge) {
LOG(INFO) << "revokeChallenge";
+ scheduleStateOrCrash(SessionState::REVOKING_CHALLENGE);
+
+ mWorker->schedule(Callable::from([this, cookie, challenge] {
+ enterStateOrCrash(cookie, SessionState::REVOKING_CHALLENGE);
+ mEngine->revokeChallengeImpl(mCb.get(), challenge);
+ enterIdling(cookie);
+ }));
+
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Session::enroll(int32_t /*cookie*/, const keymaster::HardwareAuthToken& /*hat*/,
- std::shared_ptr<common::ICancellationSignal>* /*out*/) {
+ndk::ScopedAStatus Session::enroll(int32_t cookie, const keymaster::HardwareAuthToken& hat,
+ std::shared_ptr<common::ICancellationSignal>* out) {
LOG(INFO) << "enroll";
+ scheduleStateOrCrash(SessionState::ENROLLING);
+
+ std::promise<void> cancellationPromise;
+ auto cancFuture = cancellationPromise.get_future();
+
+ mWorker->schedule(Callable::from([this, cookie, hat, cancFuture = std::move(cancFuture)] {
+ enterStateOrCrash(cookie, SessionState::ENROLLING);
+ if (shouldCancel(cancFuture)) {
+ mCb->onError(Error::CANCELED, 0 /* vendorCode */);
+ } else {
+ mEngine->enrollImpl(mCb.get(), hat);
+ }
+ enterIdling(cookie);
+ }));
+
+ *out = SharedRefBase::make<CancellationSignal>(std::move(cancellationPromise));
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Session::authenticate(int32_t /*cookie*/, int64_t /*keystoreOperationId*/,
+ndk::ScopedAStatus Session::authenticate(int32_t cookie, int64_t operationId,
std::shared_ptr<common::ICancellationSignal>* out) {
LOG(INFO) << "authenticate";
- if (mCb) {
- mCb->onStateChanged(0, SessionState::AUTHENTICATING);
- }
- *out = SharedRefBase::make<CancellationSignal>();
+ scheduleStateOrCrash(SessionState::AUTHENTICATING);
+
+ std::promise<void> cancPromise;
+ auto cancFuture = cancPromise.get_future();
+
+ mWorker->schedule(
+ Callable::from([this, cookie, operationId, cancFuture = std::move(cancFuture)] {
+ enterStateOrCrash(cookie, SessionState::AUTHENTICATING);
+ if (shouldCancel(cancFuture)) {
+ mCb->onError(Error::CANCELED, 0 /* vendorCode */);
+ } else {
+ mEngine->authenticateImpl(mCb.get(), operationId);
+ }
+ enterIdling(cookie);
+ }));
+
+ *out = SharedRefBase::make<CancellationSignal>(std::move(cancPromise));
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Session::detectInteraction(
- int32_t /*cookie*/, std::shared_ptr<common::ICancellationSignal>* /*out*/) {
+ndk::ScopedAStatus Session::detectInteraction(int32_t cookie,
+ std::shared_ptr<common::ICancellationSignal>* out) {
LOG(INFO) << "detectInteraction";
+ scheduleStateOrCrash(SessionState::DETECTING_INTERACTION);
+
+ std::promise<void> cancellationPromise;
+ auto cancFuture = cancellationPromise.get_future();
+
+ mWorker->schedule(Callable::from([this, cookie, cancFuture = std::move(cancFuture)] {
+ enterStateOrCrash(cookie, SessionState::DETECTING_INTERACTION);
+ if (shouldCancel(cancFuture)) {
+ mCb->onError(Error::CANCELED, 0 /* vendorCode */);
+ } else {
+ mEngine->detectInteractionImpl(mCb.get());
+ }
+ enterIdling(cookie);
+ }));
+
+ *out = SharedRefBase::make<CancellationSignal>(std::move(cancellationPromise));
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Session::enumerateEnrollments(int32_t /*cookie*/) {
+ndk::ScopedAStatus Session::enumerateEnrollments(int32_t cookie) {
LOG(INFO) << "enumerateEnrollments";
- if (mCb) {
- mCb->onStateChanged(0, SessionState::ENUMERATING_ENROLLMENTS);
- mCb->onEnrollmentsEnumerated(std::vector<int32_t>());
- }
+ scheduleStateOrCrash(SessionState::ENUMERATING_ENROLLMENTS);
+
+ mWorker->schedule(Callable::from([this, cookie] {
+ enterStateOrCrash(cookie, SessionState::ENUMERATING_ENROLLMENTS);
+ mEngine->enumerateEnrollmentsImpl(mCb.get());
+ enterIdling(cookie);
+ }));
+
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Session::removeEnrollments(int32_t /*cookie*/,
- const std::vector<int32_t>& /*enrollmentIds*/) {
+ndk::ScopedAStatus Session::removeEnrollments(int32_t cookie,
+ const std::vector<int32_t>& enrollmentIds) {
LOG(INFO) << "removeEnrollments";
- if (mCb) {
- mCb->onStateChanged(0, SessionState::REMOVING_ENROLLMENTS);
- mCb->onEnrollmentsRemoved(std::vector<int32_t>());
- }
+ scheduleStateOrCrash(SessionState::REMOVING_ENROLLMENTS);
+
+ mWorker->schedule(Callable::from([this, cookie, enrollmentIds] {
+ enterStateOrCrash(cookie, SessionState::REMOVING_ENROLLMENTS);
+ mEngine->removeEnrollmentsImpl(mCb.get(), enrollmentIds);
+ enterIdling(cookie);
+ }));
+
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Session::getAuthenticatorId(int32_t /*cookie*/) {
+ndk::ScopedAStatus Session::getAuthenticatorId(int32_t cookie) {
LOG(INFO) << "getAuthenticatorId";
- if (mCb) {
- mCb->onStateChanged(0, SessionState::GETTING_AUTHENTICATOR_ID);
- mCb->onAuthenticatorIdRetrieved(0 /* authenticatorId */);
- }
+ scheduleStateOrCrash(SessionState::GETTING_AUTHENTICATOR_ID);
+
+ mWorker->schedule(Callable::from([this, cookie] {
+ enterStateOrCrash(cookie, SessionState::GETTING_AUTHENTICATOR_ID);
+ mEngine->getAuthenticatorIdImpl(mCb.get());
+ enterIdling(cookie);
+ }));
+
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Session::invalidateAuthenticatorId(int32_t /*cookie*/) {
+ndk::ScopedAStatus Session::invalidateAuthenticatorId(int32_t cookie) {
LOG(INFO) << "invalidateAuthenticatorId";
+ scheduleStateOrCrash(SessionState::INVALIDATING_AUTHENTICATOR_ID);
+
+ mWorker->schedule(Callable::from([this, cookie] {
+ enterStateOrCrash(cookie, SessionState::INVALIDATING_AUTHENTICATOR_ID);
+ mEngine->invalidateAuthenticatorIdImpl(mCb.get());
+ enterIdling(cookie);
+ }));
+
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Session::resetLockout(int32_t /*cookie*/,
- const keymaster::HardwareAuthToken& /*hat*/) {
+ndk::ScopedAStatus Session::resetLockout(int32_t cookie, const keymaster::HardwareAuthToken& hat) {
LOG(INFO) << "resetLockout";
+ scheduleStateOrCrash(SessionState::RESETTING_LOCKOUT);
+
+ mWorker->schedule(Callable::from([this, cookie, hat] {
+ enterStateOrCrash(cookie, SessionState::RESETTING_LOCKOUT);
+ mEngine->resetLockoutImpl(mCb.get(), hat);
+ enterIdling(cookie);
+ }));
+
return ndk::ScopedAStatus::ok();
}
-ndk::ScopedAStatus Session::close(int32_t /*cookie*/) {
+ndk::ScopedAStatus Session::close(int32_t cookie) {
LOG(INFO) << "close";
+ CHECK(mCurrentState == SessionState::IDLING) << "Can't close a non-idling session. Crashing.";
+ mCurrentState = SessionState::CLOSED;
+ mCb->onStateChanged(cookie, mCurrentState);
return ndk::ScopedAStatus::ok();
}
@@ -119,4 +242,5 @@
LOG(INFO) << "onUiReady";
return ndk::ScopedAStatus::ok();
}
+
} // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/WorkerThread.cpp b/biometrics/fingerprint/aidl/default/WorkerThread.cpp
index 512efb8..d1a63d0 100644
--- a/biometrics/fingerprint/aidl/default/WorkerThread.cpp
+++ b/biometrics/fingerprint/aidl/default/WorkerThread.cpp
@@ -36,7 +36,7 @@
mThread.join();
}
-bool WorkerThread::schedule(Task&& task) {
+bool WorkerThread::schedule(std::unique_ptr<Callable> task) {
if (mIsDestructing) {
return false;
}
@@ -58,10 +58,10 @@
if (mIsDestructing) {
return;
}
- Task task = std::move(mQueue.front());
+ std::unique_ptr<Callable> task = std::move(mQueue.front());
mQueue.pop_front();
lock.unlock();
- task();
+ (*task)();
}
}
diff --git a/biometrics/fingerprint/aidl/default/include/Callable.h b/biometrics/fingerprint/aidl/default/include/Callable.h
new file mode 100644
index 0000000..c629511
--- /dev/null
+++ b/biometrics/fingerprint/aidl/default/include/Callable.h
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+namespace aidl::android::hardware::biometrics::fingerprint {
+
+// Interface for representing parameterless functions. Unlike std::function<void()>, this can also
+// represent move-only lambdas.
+class Callable {
+ public:
+ virtual void operator()() = 0;
+ virtual ~Callable() = default;
+
+ // Creates a heap-allocated Callable instance from any function object.
+ template <typename T>
+ static std::unique_ptr<Callable> from(T func);
+
+ private:
+ template <typename T>
+ class AnyFuncWrapper;
+};
+
+// Private helper class for wrapping any function object into a Callable.
+template <typename T>
+class Callable::AnyFuncWrapper : public Callable {
+ public:
+ explicit AnyFuncWrapper(T func) : mFunc(std::move(func)) {}
+
+ void operator()() override { mFunc(); }
+
+ private:
+ T mFunc;
+};
+
+template <typename T>
+std::unique_ptr<Callable> Callable::from(T func) {
+ return std::make_unique<AnyFuncWrapper<T>>(std::move(func));
+}
+
+} // namespace aidl::android::hardware::biometrics::fingerprint
\ No newline at end of file
diff --git a/biometrics/fingerprint/aidl/default/include/CancellationSignal.h b/biometrics/fingerprint/aidl/default/include/CancellationSignal.h
new file mode 100644
index 0000000..99f2fba
--- /dev/null
+++ b/biometrics/fingerprint/aidl/default/include/CancellationSignal.h
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/biometrics/common/BnCancellationSignal.h>
+#include <aidl/android/hardware/biometrics/fingerprint/ISessionCallback.h>
+#include <functional>
+#include <future>
+
+#include "WorkerThread.h"
+
+namespace aidl::android::hardware::biometrics::fingerprint {
+
+class CancellationSignal : public common::BnCancellationSignal {
+ public:
+ explicit CancellationSignal(std::promise<void>&& cancellationPromise);
+
+ ndk::ScopedAStatus cancel() override;
+
+ private:
+ std::promise<void> mCancellationPromise;
+};
+
+// Returns whether the given cancellation future is ready, i.e. whether the operation corresponding
+// to this future should be cancelled.
+bool shouldCancel(const std::future<void>& cancellationFuture);
+
+} // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h
new file mode 100644
index 0000000..9343316
--- /dev/null
+++ b/biometrics/fingerprint/aidl/default/include/FakeFingerprintEngine.h
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <android-base/logging.h>
+
+namespace aidl::android::hardware::biometrics::fingerprint {
+
+class FakeFingerprintEngine {
+ public:
+ void generateChallengeImpl(ISessionCallback* cb, int32_t /*timeoutSec*/) {
+ LOG(INFO) << "generateChallengeImpl";
+ cb->onChallengeGenerated(0 /* 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";
+ 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();
+ }
+};
+
+} // namespace aidl::android::hardware::biometrics::fingerprint
\ No newline at end of file
diff --git a/biometrics/fingerprint/aidl/default/include/Fingerprint.h b/biometrics/fingerprint/aidl/default/include/Fingerprint.h
index ce1366c..9b8eef8 100644
--- a/biometrics/fingerprint/aidl/default/include/Fingerprint.h
+++ b/biometrics/fingerprint/aidl/default/include/Fingerprint.h
@@ -18,9 +18,13 @@
#include <aidl/android/hardware/biometrics/fingerprint/BnFingerprint.h>
+#include "FakeFingerprintEngine.h"
+#include "Session.h"
+#include "WorkerThread.h"
+
namespace aidl::android::hardware::biometrics::fingerprint {
-class Fingerprint final : public BnFingerprint {
+class Fingerprint : public BnFingerprint {
public:
Fingerprint();
@@ -31,6 +35,11 @@
std::shared_ptr<ISession>* out) override;
ndk::ScopedAStatus reset() override;
+
+ private:
+ std::unique_ptr<FakeFingerprintEngine> mEngine;
+ WorkerThread mWorker;
+ std::weak_ptr<Session> mSession;
};
} // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/include/Session.h b/biometrics/fingerprint/aidl/default/include/Session.h
index 99d806b..adda831 100644
--- a/biometrics/fingerprint/aidl/default/include/Session.h
+++ b/biometrics/fingerprint/aidl/default/include/Session.h
@@ -19,6 +19,9 @@
#include <aidl/android/hardware/biometrics/fingerprint/BnSession.h>
#include <aidl/android/hardware/biometrics/fingerprint/ISessionCallback.h>
+#include "FakeFingerprintEngine.h"
+#include "WorkerThread.h"
+
namespace aidl::android::hardware::biometrics::fingerprint {
namespace common = aidl::android::hardware::biometrics::common;
@@ -26,7 +29,8 @@
class Session : public BnSession {
public:
- explicit Session(std::shared_ptr<ISessionCallback> cb);
+ Session(int sensorId, int userId, std::shared_ptr<ISessionCallback> cb,
+ FakeFingerprintEngine* engine, WorkerThread* worker);
ndk::ScopedAStatus generateChallenge(int32_t cookie, int32_t timeoutSec) override;
@@ -35,7 +39,7 @@
ndk::ScopedAStatus enroll(int32_t cookie, const keymaster::HardwareAuthToken& hat,
std::shared_ptr<common::ICancellationSignal>* out) override;
- ndk::ScopedAStatus authenticate(int32_t cookie, int64_t keystoreOperationId,
+ ndk::ScopedAStatus authenticate(int32_t cookie, int64_t operationId,
std::shared_ptr<common::ICancellationSignal>* out) override;
ndk::ScopedAStatus detectInteraction(
@@ -62,8 +66,29 @@
ndk::ScopedAStatus onUiReady() override;
+ bool isClosed();
+
private:
+ // Crashes the HAL if it's not currently idling because that would be an invalid state machine
+ // transition. Otherwise, sets the scheduled state to the given state.
+ void scheduleStateOrCrash(SessionState state);
+
+ // Crashes the HAL if the provided state doesn't match the previously scheduled state.
+ // Otherwise, transitions into the provided state, clears the scheduled state, and notifies
+ // the client about the transition by calling ISessionCallback#onStateChanged.
+ void enterStateOrCrash(int cookie, SessionState state);
+
+ // Sets the current state to SessionState::IDLING and notifies the client about the transition
+ // by calling ISessionCallback#onStateChanged.
+ void enterIdling(int cookie);
+
+ int32_t mSensorId;
+ int32_t mUserId;
std::shared_ptr<ISessionCallback> mCb;
+ FakeFingerprintEngine* mEngine;
+ WorkerThread* mWorker;
+ SessionState mScheduledState;
+ SessionState mCurrentState;
};
} // namespace aidl::android::hardware::biometrics::fingerprint
diff --git a/biometrics/fingerprint/aidl/default/include/WorkerThread.h b/biometrics/fingerprint/aidl/default/include/WorkerThread.h
index 49104c8..6fff4f2 100644
--- a/biometrics/fingerprint/aidl/default/include/WorkerThread.h
+++ b/biometrics/fingerprint/aidl/default/include/WorkerThread.h
@@ -21,9 +21,9 @@
#include <queue>
#include <thread>
-namespace aidl::android::hardware::biometrics::fingerprint {
+#include "Callable.h"
-using Task = std::function<void()>;
+namespace aidl::android::hardware::biometrics::fingerprint {
// A class that encapsulates a worker thread and a task queue, and provides a convenient interface
// for a Session to schedule its tasks for asynchronous execution.
@@ -47,7 +47,12 @@
// If the internal queue is not full, pushes a task at the end of the queue and returns true.
// Otherwise, returns false. If the queue is busy, blocks until it becomes available.
- bool schedule(Task&& task);
+ // This method expects heap-allocated tasks because it's the simplest way to represent function
+ // objects of any type. Stack-allocated std::function could be used instead, but it cannot
+ // represent functions with move-only captures because std::function is inherently copyable.
+ // Not being able to pass move-only lambdas is a major limitation for the HAL implementation,
+ // so heap-allocated tasks that share a common interface (Callable) were chosen instead.
+ bool schedule(std::unique_ptr<Callable> task);
private:
// The function that runs on the internal thread. Sequentially runs the available tasks from
@@ -63,7 +68,7 @@
std::atomic<bool> mIsDestructing;
// Queue that's guarded by mQueueMutex and mQueueCond.
- std::deque<Task> mQueue;
+ std::deque<std::unique_ptr<Callable>> mQueue;
std::mutex mQueueMutex;
std::condition_variable mQueueCond;
diff --git a/biometrics/fingerprint/aidl/default/tests/WorkerThreadTest.cpp b/biometrics/fingerprint/aidl/default/tests/WorkerThreadTest.cpp
index ba901ad..0d5014bb 100644
--- a/biometrics/fingerprint/aidl/default/tests/WorkerThreadTest.cpp
+++ b/biometrics/fingerprint/aidl/default/tests/WorkerThreadTest.cpp
@@ -25,13 +25,14 @@
namespace {
+using aidl::android::hardware::biometrics::fingerprint::Callable;
using aidl::android::hardware::biometrics::fingerprint::WorkerThread;
using namespace std::chrono_literals;
TEST(WorkerThreadTest, ScheduleReturnsTrueWhenQueueHasSpace) {
WorkerThread worker(1 /*maxQueueSize*/);
for (int i = 0; i < 100; ++i) {
- EXPECT_TRUE(worker.schedule([] {}));
+ EXPECT_TRUE(worker.schedule(Callable::from([] {})));
// Allow enough time for the previous task to be processed.
std::this_thread::sleep_for(2ms);
}
@@ -40,16 +41,16 @@
TEST(WorkerThreadTest, ScheduleReturnsFalseWhenQueueIsFull) {
WorkerThread worker(2 /*maxQueueSize*/);
// Add a long-running task.
- worker.schedule([] { std::this_thread::sleep_for(1s); });
+ worker.schedule(Callable::from([] { std::this_thread::sleep_for(1s); }));
// Allow enough time for the worker to start working on the previous task.
std::this_thread::sleep_for(2ms);
// Fill the worker's queue to the maximum.
- worker.schedule([] {});
- worker.schedule([] {});
+ worker.schedule(Callable::from([] {}));
+ worker.schedule(Callable::from([] {}));
- EXPECT_FALSE(worker.schedule([] {}));
+ EXPECT_FALSE(worker.schedule(Callable::from([] {})));
}
TEST(WorkerThreadTest, TasksExecuteInOrder) {
@@ -58,19 +59,19 @@
std::vector<int> results;
for (int i = 0; i < NUM_TASKS; ++i) {
- worker.schedule([&results, i] {
+ worker.schedule(Callable::from([&results, i] {
// Delay tasks differently to provoke races.
std::this_thread::sleep_for(std::chrono::nanoseconds(100 - i % 100));
// Unguarded write to results to provoke races.
results.push_back(i);
- });
+ }));
}
std::promise<void> promise;
auto future = promise.get_future();
// Schedule a special task to signal when all of the tasks are finished.
- worker.schedule([&promise] { promise.set_value(); });
+ worker.schedule(Callable::from([&promise] { promise.set_value(); }));
auto status = future.wait_for(1s);
ASSERT_EQ(status, std::future_status::ready);
@@ -86,11 +87,11 @@
{
WorkerThread worker(2 /*maxQueueSize*/);
- worker.schedule([&promise1] {
+ worker.schedule(Callable::from([&promise1] {
promise1.set_value();
std::this_thread::sleep_for(200ms);
- });
- worker.schedule([&promise2] { promise2.set_value(); });
+ }));
+ worker.schedule(Callable::from([&promise2] { promise2.set_value(); }));
// Make sure the first task is executing.
auto status1 = future1.wait_for(1s);