Implement the state machine

Bug: 166800618
Bug: 175070939
Test: atest VtsHalBiometricsFingerprintTargetTest
Change-Id: I3a908b0f910323d643b220e560e9c2d8e4c5675a
This commit is contained in:
Ilya Matyukhin
2021-02-22 13:13:13 -08:00
parent 4f5d6801e9
commit 48ff8969f8
8 changed files with 370 additions and 46 deletions

View File

@@ -6,9 +6,11 @@ cc_binary {
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",

View File

@@ -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

View File

@@ -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 @@ constexpr char SERIAL_NUMBER[] = "00000001";
} // 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 @@ ndk::ScopedAStatus Fingerprint::getSensorProps(std::vector<SensorProps>* out) {
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();
}

View File

@@ -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 @@ ndk::ScopedAStatus Session::onUiReady() {
LOG(INFO) << "onUiReady";
return ndk::ScopedAStatus::ok();
}
} // namespace aidl::android::hardware::biometrics::fingerprint

View File

@@ -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

View File

@@ -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

View File

@@ -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 @@ class Fingerprint final : public BnFingerprint {
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

View File

@@ -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 @@ namespace keymaster = aidl::android::hardware::keymaster;
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 @@ class Session : public BnSession {
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 @@ class Session : public BnSession {
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