Implement the state machine

Bug: 166800618
Bug: 175070939
Test: atest VtsHalBiometricsFingerprintTargetTest
Change-Id: I3a908b0f910323d643b220e560e9c2d8e4c5675a
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