biod: add fpc biometric support
To support FPC, their sensor library header is included as well.
Also adds the AtExitManager to the main file because base::Thread needs
that.
TEST=ran enrollments and authentication on gru via dbus interface
BUG=chromium:629657
Change-Id: I74569a588db678d241b6e77b1f4ae5ff9b41c738
Reviewed-on: https://chromium-review.googlesource.com/398463
Commit-Ready: Zach Reizner <zachr@chromium.org>
Tested-by: Zach Reizner <zachr@chromium.org>
Reviewed-by: Chirantan Ekbote <chirantan@chromium.org>
diff --git a/biod/biod.gyp b/biod/biod.gyp
index 6948f75..f8f235f 100644
--- a/biod/biod.gyp
+++ b/biod/biod.gyp
@@ -20,6 +20,7 @@
'main.cc',
'biometrics_daemon.cc',
'fake_biometric.cc',
+ 'fpc_biometric.cc',
'bio_library.cc',
],
},
diff --git a/biod/biometrics_daemon.cc b/biod/biometrics_daemon.cc
index 349156b..4105840 100644
--- a/biod/biometrics_daemon.cc
+++ b/biod/biometrics_daemon.cc
@@ -11,6 +11,7 @@
#include <brillo/dbus/async_event_sequencer.h>
#include "biod/fake_biometric.h"
+#include "biod/fpc_biometric.h"
namespace biod {
@@ -395,6 +396,16 @@
fake_bio_path,
sequencer->GetHandler("Failed to register biometric object", true)));
+ ObjectPath fpc_bio_path =
+ ObjectPath(dbus_constants::kServicePath + std::string("/FpcBiometric"));
+ std::unique_ptr<Biometric> fpc_bio = FpcBiometric::Create();
+ CHECK(fpc_bio);
+ biometrics_.emplace_back(new BiometricWrapper(
+ std::move(fpc_bio),
+ object_manager_.get(),
+ fpc_bio_path,
+ sequencer->GetHandler("Failed to register biometric object", true)));
+
CHECK(bus_->RequestOwnershipAndBlock(dbus_constants::kServiceName,
dbus::Bus::REQUIRE_PRIMARY));
}
diff --git a/biod/fpc/fp_sensor.h b/biod/fpc/fp_sensor.h
new file mode 100644
index 0000000..ccde42e
--- /dev/null
+++ b/biod/fpc/fp_sensor.h
@@ -0,0 +1,105 @@
+/* Copyright 2016 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef FP_SENSOR_H_
+#define FP_SENSOR_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <sys/types.h>
+
+/*
+ * Opens the sensor library.
+ *
+ * fp_sensor_open is called once before subsequent usage of sensor api's.
+ * fp_sensor_close must be called when sensor functionality is no longer
+ * needed. The fd parameter carries an open file descriptor to the sensor
+ * driver.
+ *
+ * Returns 0 on success, negative error code (such as -ENOMEM) on failure.
+ */
+int fp_sensor_open(int fd);
+
+/*
+ * Closes the sensor library and frees resources held by the library.
+ *
+ * Returns 0 on success, negative error code on failure.
+ */
+int fp_sensor_close(void);
+
+/*
+ * Retrieves product information about the sensor.
+ *
+ * Returns 0 on success, negative error code (such as -EINVAL) on failure.
+ */
+int fp_sensor_get_model(uint32_t *vendor_id, uint32_t *product_id,
+ uint32_t *model_id, uint32_t *version);
+
+/*
+ * Retrieves the pixel format used by the sensor library.
+ * This is a fourcc value defined by V4L2 API.
+ * Could be a new define for biometric sensors or V4L2_PIX_FMT_GREY.
+ *
+ * Returns 0 on success, negative error code (such as -EINVAL) on failure.
+ */
+int fp_sensor_get_pixel_format(uint32_t *pixel_format);
+
+/*
+ * Returns the size of image data returned from the sensor.
+ *
+ * Returns negative error code on failure, or size of image data in bytes.
+ */
+ssize_t fp_sensor_get_image_data_size(void);
+
+/*
+ * Retrieves the width and height in pixels of images captured from the sensor.
+ *
+ * Returns 0 on success, negative error code (such as -EINVAL) on failure.
+ */
+int fp_sensor_get_image_dimensions(uint32_t *width, uint32_t *height);
+
+/*
+ * Acquires a fingerprint image.
+ *
+ * Function blocks waiting for a finger to be placed on sensor and then
+ * captures an image. The operation can be cancelled by calling
+ * fp_sensor_cancel. The image_data parameter points to an image data buffer
+ * allocated by the caller.
+ *
+ * Returns:
+ * - 0 on success
+ * - negative value on error
+ * - FP_SENSOR_LOW_IMAGE_QUALITY on image captured but quality is too low
+ * - FP_SENSOR_TOO_FAST on finger removed before image was captured
+ */
+#define FP_SENSOR_LOW_IMAGE_QUALITY 1
+#define FP_SENSOR_TOO_FAST 2
+int fp_sensor_acquire_image(uint8_t *image_data, size_t size);
+
+/*
+ * Waits for finger to be lifted from sensor.
+ *
+ * The operation blocks as long as a finger is present on the sensor.
+ * The operation can be cancelled by calling fp_sensor_cancel.
+ *
+ * Returns 0 on success, negative error code on failure.
+ */
+int fp_sensor_wait_finger_up(void);
+
+/*
+ * Cancels an ongoing blocking fp lib operation.
+ *
+ * Returns 0 on success, negative error code on failure.
+ */
+int fp_sensor_cancel(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FP_SENSOR_H_ */
diff --git a/biod/fpc_biometric.cc b/biod/fpc_biometric.cc
new file mode 100644
index 0000000..0c74513
--- /dev/null
+++ b/biod/fpc_biometric.cc
@@ -0,0 +1,608 @@
+// Copyright 2016 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "biod/fpc_biometric.h"
+
+#include <algorithm>
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <base/bind.h>
+#include <base/logging.h>
+#include <base/threading/thread_task_runner_handle.h>
+
+#include "biod/fpc/fp_sensor.h"
+
+namespace biod {
+
+class FpcBiometric::SensorLibrary {
+ public:
+ ~SensorLibrary();
+
+ static std::unique_ptr<SensorLibrary> Open(
+ const std::shared_ptr<BioLibrary>& bio_lib, int fd);
+
+ BioEnrollment BeginEnrollment();
+ std::tuple<int, BioImage> AcquireImage();
+ bool WaitFingerUp();
+ bool Cancel();
+
+ private:
+ using fp_sensor_open_fp = int (*)(int fd);
+ using fp_sensor_close_fp = int (*)(void);
+ using fp_sensor_get_model_fp = int (*)(uint32_t* vendor_id,
+ uint32_t* product_id,
+ uint32_t* model_id,
+ uint32_t* version);
+ using fp_sensor_get_pixel_format_fp = int (*)(uint32_t* pixel_format);
+ using fp_sensor_get_image_data_size_fp = ssize_t (*)(void);
+ using fp_sensor_get_image_dimensions_fp = int (*)(uint32_t* width,
+ uint32_t* height);
+ using fp_sensor_acquire_image_fp = int (*)(uint8_t* image_data, size_t size);
+ using fp_sensor_wait_finger_up_fp = int (*)(void);
+ using fp_sensor_cancel_fp = int (*)(void);
+
+ explicit SensorLibrary(const std::shared_ptr<BioLibrary>& bio_lib)
+ : bio_lib_(bio_lib) {}
+ bool Init(int fd);
+
+ fp_sensor_open_fp open_;
+ fp_sensor_close_fp close_;
+ fp_sensor_get_model_fp get_model_;
+ fp_sensor_get_pixel_format_fp get_pixel_format_;
+ fp_sensor_get_image_data_size_fp get_image_data_size_;
+ fp_sensor_get_image_dimensions_fp get_image_dimensions_;
+ fp_sensor_acquire_image_fp acquire_image_;
+ fp_sensor_wait_finger_up_fp wait_finger_up_;
+ fp_sensor_cancel_fp cancel_;
+
+ std::shared_ptr<BioLibrary> bio_lib_;
+ bool needs_close_ = false;
+ size_t image_data_size_ = 0;
+ BioSensor bio_sensor_;
+
+ DISALLOW_COPY_AND_ASSIGN(SensorLibrary);
+};
+
+FpcBiometric::SensorLibrary::~SensorLibrary() {
+ if (needs_close_)
+ close_();
+}
+
+std::unique_ptr<FpcBiometric::SensorLibrary> FpcBiometric::SensorLibrary::Open(
+ const std::shared_ptr<BioLibrary>& bio_lib, int fd) {
+ std::unique_ptr<SensorLibrary> lib(new SensorLibrary(bio_lib));
+ if (!lib->Init(fd))
+ return nullptr;
+ return lib;
+}
+
+BioEnrollment FpcBiometric::SensorLibrary::BeginEnrollment() {
+ return bio_sensor_.BeginEnrollment();
+}
+
+std::tuple<int, BioImage> FpcBiometric::SensorLibrary::AcquireImage() {
+ std::vector<uint8_t> image_data(image_data_size_);
+ int acquire_result = acquire_image_(image_data.data(), image_data.size());
+ if (acquire_result)
+ return std::tuple<int, BioImage>(acquire_result, BioImage());
+
+ BioImage image = bio_sensor_.CreateImage();
+ if (!image || !image.SetData(image_data))
+ return std::tuple<int, BioImage>(acquire_result, BioImage());
+
+ return std::tuple<int, BioImage>(0 /* success */, std::move(image));
+}
+
+bool FpcBiometric::SensorLibrary::WaitFingerUp() {
+ int ret = wait_finger_up_();
+ if (ret)
+ LOG(ERROR) << "Failed to wait for finger up: " << ret;
+ return ret == 0;
+}
+
+bool FpcBiometric::SensorLibrary::Cancel() {
+ int ret = cancel_();
+ if (ret)
+ LOG(ERROR) << "Failed to cancel FPC sensor operation: " << ret;
+ return ret == 0;
+}
+
+bool FpcBiometric::SensorLibrary::Init(int fd) {
+#define SENSOR_SYM(x) \
+ do { \
+ x##_ = bio_lib_->GetFunction<fp_sensor_##x##_fp>("fp_sensor_" #x); \
+ if (!x##_) { \
+ LOG(ERROR) << #x " is missing from library"; \
+ return false; \
+ } \
+ } while (0)
+
+ // Here we use the very same shared object loaded by BioLibrary to pull out
+ // some private functions that interface with the FPC sensor.
+ SENSOR_SYM(open);
+ SENSOR_SYM(close);
+ SENSOR_SYM(get_model);
+ SENSOR_SYM(get_pixel_format);
+ SENSOR_SYM(get_image_data_size);
+ SENSOR_SYM(get_image_dimensions);
+ SENSOR_SYM(acquire_image);
+ SENSOR_SYM(wait_finger_up);
+ SENSOR_SYM(cancel);
+
+#undef SENSOR_SYM
+
+ int ret = open_(fd);
+ if (ret) {
+ LOG(ERROR) << "Failed to open sensor library: " << ret;
+ return false;
+ }
+ needs_close_ = true;
+
+ BioSensor::Model model;
+ ret = get_model_(
+ &model.vendor_id, &model.product_id, &model.model_id, &model.version);
+ if (ret) {
+ LOG(ERROR) << "Failed to get sensor model: " << ret;
+ return false;
+ }
+
+ uint32_t pixel_format;
+ ret = get_pixel_format_(&pixel_format);
+ if (ret) {
+ LOG(ERROR) << "Failed to get sensor pixel format: " << ret;
+ return false;
+ }
+
+ ssize_t image_data_size = get_image_data_size_();
+ if (image_data_size <= 0) {
+ LOG(ERROR) << "Failed to get sensor image data size: " << image_data_size;
+ return false;
+ }
+ image_data_size_ = image_data_size;
+
+ uint32_t width, height;
+ ret = get_image_dimensions_(&width, &height);
+ if (ret) {
+ LOG(ERROR) << "Failed to get sensor image dimensions: " << ret;
+ return false;
+ }
+
+ LOG(INFO) << "FPC Sensor Info ";
+ LOG(INFO) << " Vendor ID : " << model.vendor_id;
+ LOG(INFO) << " Product ID : " << model.product_id;
+ LOG(INFO) << " Model ID : " << model.model_id;
+ LOG(INFO) << " Version : " << model.version;
+ LOG(INFO) << "FPC Image Info ";
+ // Prints the pixel format in FOURCC format.
+ const uint32_t a = pixel_format;
+ LOG(INFO) << " Pixel Format : " << (char)(a) << (char)(a >> 8)
+ << (char)(a >> 16) << (char)(a >> 24);
+ LOG(INFO) << " Image Data Size : " << image_data_size;
+ LOG(INFO) << " Image Dimensions : " << width << "x" << height;
+
+ bio_sensor_ = bio_lib_->CreateSensor();
+ if (!bio_sensor_)
+ return false;
+
+ if (!bio_sensor_.SetModel(model))
+ return false;
+
+ if (!bio_sensor_.SetFormat(pixel_format))
+ return false;
+
+ if (!bio_sensor_.SetSize(width, height))
+ return false;
+
+ return true;
+}
+
+uint64_t FpcBiometric::Enrollment::GetId() const {
+ return static_cast<uint64_t>(id_);
+}
+
+const std::string& FpcBiometric::Enrollment::GetUserId() const {
+ CHECK(WithInternal([this](EnrollmentIterator i) {
+ this->local_user_id_ = i->second.user_id;
+ })) << ": Attempted to get user ID for invalid Biometric Enrollment";
+ return local_user_id_;
+}
+
+const std::string& FpcBiometric::Enrollment::GetLabel() const {
+ CHECK(WithInternal([this](EnrollmentIterator i) {
+ this->local_label_ = i->second.label;
+ })) << ": Attempted to get label for invalid Biometric Enrollment";
+ return local_label_;
+}
+
+bool FpcBiometric::Enrollment::SetLabel(std::string label) {
+ return WithInternal(
+ [&](EnrollmentIterator i) { i->second.label = std::move(label); });
+}
+
+bool FpcBiometric::Enrollment::Remove() {
+ return WithInternal(
+ [this](EnrollmentIterator i) { biometric_->enrollments_.erase(i); });
+}
+
+std::unique_ptr<Biometric> FpcBiometric::Create() {
+ std::unique_ptr<FpcBiometric> biometric(new FpcBiometric);
+ if (!biometric->Init())
+ return nullptr;
+
+ return std::unique_ptr<Biometric>(std::move(biometric));
+}
+
+Biometric::Type FpcBiometric::GetType() {
+ return Biometric::Type::kFingerprint;
+}
+
+Biometric::EnrollSession FpcBiometric::StartEnroll(std::string user_id,
+ std::string label) {
+ if (running_task_)
+ return Biometric::EnrollSession();
+
+ std::shared_ptr<BioTemplate> tmpl = std::make_shared<BioTemplate>();
+
+ kill_task_ = false;
+ bool task_will_run = sensor_thread_.task_runner()->PostTaskAndReply(
+ FROM_HERE,
+ base::Bind(&FpcBiometric::DoEnrollTask,
+ base::Unretained(this),
+ base::ThreadTaskRunnerHandle::Get(),
+ tmpl),
+ base::Bind(&FpcBiometric::OnEnrollComplete,
+ weak_factory_.GetWeakPtr(),
+ std::move(user_id),
+ std::move(label),
+ tmpl));
+
+ if (!task_will_run) {
+ LOG(ERROR) << "Failed to schedule enrollment task";
+ return Biometric::EnrollSession();
+ }
+
+ // Note that the On*Complete function sets running_task_ to false on this
+ // thread's message loop, so setting it here does not result in a race
+ // condition.
+ running_task_ = true;
+
+ return Biometric::EnrollSession(session_weak_factory_.GetWeakPtr());
+}
+
+Biometric::AuthenticationSession FpcBiometric::StartAuthentication() {
+ if (running_task_)
+ return Biometric::AuthenticationSession();
+
+ kill_task_ = false;
+ bool task_will_run = sensor_thread_.task_runner()->PostTaskAndReply(
+ FROM_HERE,
+ base::Bind(&FpcBiometric::DoAuthenticationTask,
+ base::Unretained(this),
+ base::ThreadTaskRunnerHandle::Get()),
+ base::Bind(&FpcBiometric::OnAuthenticationComplete,
+ weak_factory_.GetWeakPtr()));
+
+ if (!task_will_run) {
+ LOG(ERROR) << "Failed to schedule authentication task";
+ return Biometric::AuthenticationSession();
+ }
+
+ // Note that the On*Complete function sets running_task_ to false on this
+ // thread's message loop, so setting it here does not result in a race
+ // condition.
+ running_task_ = true;
+
+ return Biometric::AuthenticationSession(session_weak_factory_.GetWeakPtr());
+}
+
+std::vector<std::unique_ptr<Biometric::Enrollment>>
+FpcBiometric::GetEnrollments() {
+ base::AutoLock guard(enrollments_lock_);
+ std::vector<std::unique_ptr<Biometric::Enrollment>> enrollments(
+ enrollments_.size());
+ std::transform(enrollments_.begin(),
+ enrollments_.end(),
+ enrollments.begin(),
+ [this](decltype(enrollments_)::value_type& enrollment) {
+ return std::unique_ptr<Biometric::Enrollment>(new Enrollment(
+ weak_factory_.GetWeakPtr(), enrollment.first));
+ });
+ return enrollments;
+}
+
+bool FpcBiometric::DestroyAllEnrollments() {
+ base::AutoLock guard(enrollments_lock_);
+ enrollments_.clear();
+ return true;
+}
+
+void FpcBiometric::SetScannedHandler(const Biometric::ScanCallback& on_scan) {
+ on_scan_ = on_scan;
+}
+
+void FpcBiometric::SetAttemptHandler(
+ const Biometric::AttemptCallback& on_attempt) {
+ on_attempt_ = on_attempt;
+}
+
+void FpcBiometric::SetFailureHandler(
+ const Biometric::FailureCallback& on_failure) {
+ on_failure_ = on_failure;
+}
+
+void FpcBiometric::EndEnroll() {
+ KillSensorTask();
+}
+
+void FpcBiometric::EndAuthentication() {
+ KillSensorTask();
+}
+
+void FpcBiometric::KillSensorTask() {
+ {
+ base::AutoLock guard(kill_task_lock_);
+ kill_task_ = true;
+ }
+ sensor_lib_->Cancel();
+}
+
+FpcBiometric::FpcBiometric()
+ : sensor_thread_("fpc_sensor"),
+ session_weak_factory_(this),
+ weak_factory_(this) {}
+
+FpcBiometric::~FpcBiometric() {}
+
+bool FpcBiometric::Init() {
+ const char kFpcSensorPath[] = "/dev/fpc_sensor0";
+ sensor_fd_ = base::ScopedFD(open(kFpcSensorPath, O_RDWR));
+ if (sensor_fd_.get() < 0) {
+ LOG(ERROR) << "Failed to open " << kFpcSensorPath;
+ return false;
+ }
+
+ const char kFpcLibName[] = "/usr/lib/libfp.so";
+ bio_lib_ = BioLibrary::Load(base::FilePath(kFpcLibName));
+ if (!bio_lib_)
+ return false;
+
+ sensor_lib_ = SensorLibrary::Open(bio_lib_, sensor_fd_.get());
+ if (!sensor_lib_)
+ return false;
+
+ if (!sensor_thread_.Start()) {
+ LOG(ERROR) << "Failed to start sensor thread";
+ return false;
+ }
+
+ return true;
+}
+
+void FpcBiometric::OnScan(Biometric::ScanResult result, bool done) {
+ if (!on_scan_.is_null())
+ on_scan_.Run(result, done);
+}
+
+void FpcBiometric::OnAttempt(
+ Biometric::ScanResult result,
+ const std::vector<std::string>& recognized_user_ids) {
+ if (!on_attempt_.is_null())
+ on_attempt_.Run(result, recognized_user_ids);
+}
+
+void FpcBiometric::OnFailure() {
+ if (!on_failure_.is_null())
+ on_failure_.Run();
+}
+
+FpcBiometric::ScanData FpcBiometric::ScanImage() {
+ DCHECK(sensor_thread_.task_runner()->RunsTasksOnCurrentThread());
+
+ bool success = sensor_lib_->WaitFingerUp();
+ {
+ base::AutoLock guard(kill_task_lock_);
+ if (kill_task_)
+ return ScanData(ScanData::Killed());
+ }
+
+ if (!success)
+ return ScanData();
+
+ int acquire_result;
+ BioImage image;
+ std::tie(acquire_result, image) = sensor_lib_->AcquireImage();
+ {
+ base::AutoLock guard(kill_task_lock_);
+ if (kill_task_)
+ return ScanData(ScanData::Killed());
+ }
+
+ switch (acquire_result) {
+ case 0:
+ break;
+ case FP_SENSOR_TOO_FAST:
+ return ScanData(Biometric::ScanResult::kTooFast);
+ case FP_SENSOR_LOW_IMAGE_QUALITY:
+ return ScanData(Biometric::ScanResult::kInsufficient);
+ default:
+ LOG(ERROR) << "Unexpected result from acquiring image: "
+ << acquire_result;
+ return ScanData();
+ }
+
+ return ScanData(std::move(image));
+}
+
+void FpcBiometric::DoEnrollTask(const TaskRunnerRef& task_runner,
+ const std::shared_ptr<BioTemplate>& tmpl) {
+ DCHECK(sensor_thread_.task_runner()->RunsTasksOnCurrentThread());
+
+ if (kill_task_)
+ return;
+
+ BioEnrollment enrollment = sensor_lib_->BeginEnrollment();
+ if (!enrollment)
+ return;
+
+ for (;;) {
+ ScanData scan = ScanImage();
+
+ // The previous function can return early if this task was killed, or there
+ // was an unrecoverable hardware failure.
+ if (scan.killed || !scan.success)
+ return;
+
+ Biometric::ScanResult scan_result = scan.result;
+ if (scan) {
+ int add_result = enrollment.AddImage(scan.image);
+ switch (add_result) {
+ case BIO_ENROLLMENT_OK:
+ break;
+ case BIO_ENROLLMENT_IMMOBILE:
+ scan_result = Biometric::ScanResult::kImmobile;
+ case BIO_ENROLLMENT_LOW_COVERAGE:
+ scan_result = Biometric::ScanResult::kPartial;
+ case BIO_ENROLLMENT_LOW_QUALITY:
+ scan_result = Biometric::ScanResult::kInsufficient;
+ break;
+ default:
+ LOG(ERROR) << "Unexpected result from add image: " << add_result;
+ return;
+ }
+ }
+
+ int complete_result = enrollment.IsComplete();
+ if (complete_result < 0) {
+ LOG(ERROR) << "Failed to get enrollment completion: " << complete_result;
+ return;
+ } else if (complete_result == 1) {
+ *tmpl = enrollment.Finish();
+ return;
+ } else {
+ // Notice we will only ever post the on_scan task on an incomplete
+ // enrollment. The complete on_scan task is only posted after the
+ // enrollment is added to the enrollments map, which is done only on the
+ // main thread's message loop.
+ bool task_will_run =
+ task_runner->PostTask(FROM_HERE,
+ base::Bind(&FpcBiometric::OnScan,
+ base::Unretained(this),
+ scan_result,
+ false));
+ if (!task_will_run) {
+ LOG(ERROR) << "Failed to schedule Scan callback";
+ return;
+ }
+ }
+ }
+}
+
+void FpcBiometric::OnEnrollComplete(std::string user_id,
+ std::string label,
+ const std::shared_ptr<BioTemplate>& tmpl) {
+ OnTaskComplete();
+
+ if (kill_task_)
+ return;
+
+ // Remember tmpl stores a shared pointer to a /pointer/ which contains the
+ // actual result.
+ if (*tmpl.get()) {
+ {
+ base::AutoLock guard(enrollments_lock_);
+ enrollments_.emplace(
+ next_enrollment_id_,
+ InternalEnrollment{
+ std::move(user_id), std::move(label), std::move(*tmpl.get())});
+ next_enrollment_id_++;
+ }
+
+ OnScan(Biometric::ScanResult::kSuccess, true);
+ } else {
+ OnFailure();
+ }
+}
+
+void FpcBiometric::DoAuthenticationTask(const TaskRunnerRef& task_runner) {
+ DCHECK(sensor_thread_.task_runner()->RunsTasksOnCurrentThread());
+
+ if (kill_task_)
+ return;
+
+ std::vector<std::string> recognized_user_ids;
+
+ for (;;) {
+ ScanData scan = ScanImage();
+
+ // The previous function can return early if this task was killed, or there
+ // was an unrecoverable hardware failure.
+ if (scan.killed || !scan.success)
+ break;
+
+ Biometric::ScanResult result = scan.result;
+ if (result == Biometric::ScanResult::kSuccess) {
+ recognized_user_ids.clear();
+
+ base::AutoLock guard(enrollments_lock_);
+ for (auto& kv : enrollments_) {
+ InternalEnrollment& enrollment = kv.second;
+
+ int match_result = enrollment.tmpl.MatchImage(scan.image);
+ switch (match_result) {
+ case BIO_TEMPLATE_NO_MATCH:
+ break;
+ case BIO_TEMPLATE_MATCH_UPDATED:
+ case BIO_TEMPLATE_MATCH:
+ recognized_user_ids.emplace_back(enrollment.user_id);
+ break;
+ case BIO_TEMPLATE_LOW_QUALITY:
+ result = Biometric::ScanResult::kInsufficient;
+ break;
+ case BIO_TEMPLATE_LOW_COVERAGE:
+ result = Biometric::ScanResult::kPartial;
+ break;
+ default:
+ LOG(ERROR) << "Unexpected result from matching templates: "
+ << match_result;
+ return;
+ }
+ }
+ }
+
+ // Assuming there was at least one match, we don't want to bother the user
+ // with error messages.
+ if (!recognized_user_ids.empty())
+ result = Biometric::ScanResult::kSuccess;
+
+ bool task_will_run =
+ task_runner->PostTask(FROM_HERE,
+ base::Bind(&FpcBiometric::OnAttempt,
+ base::Unretained(this),
+ result,
+ std::move(recognized_user_ids)));
+ if (!task_will_run) {
+ LOG(ERROR) << "Failed to schedule Attempt callback";
+ return;
+ }
+ }
+}
+
+void FpcBiometric::OnAuthenticationComplete() {
+ OnTaskComplete();
+
+ // Authentication never ends except on error or being killed. If no kill
+ // signal was given, we can assume failure.
+ if (!kill_task_)
+ OnFailure();
+}
+
+void FpcBiometric::OnTaskComplete() {
+ session_weak_factory_.InvalidateWeakPtrs();
+ running_task_ = false;
+}
+
+} // namespace biod
diff --git a/biod/fpc_biometric.h b/biod/fpc_biometric.h
new file mode 100644
index 0000000..dffb4b7
--- /dev/null
+++ b/biod/fpc_biometric.h
@@ -0,0 +1,194 @@
+// Copyright 2016 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BIOD_FPC_BIOMETRIC_H_
+#define BIOD_FPC_BIOMETRIC_H_
+
+#include "biod/biometric.h"
+
+#include <memory>
+#include <string>
+#include <tuple>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+
+#include <base/files/file_util.h>
+#include <base/synchronization/lock.h>
+#include <base/threading/thread.h>
+
+#include "biod/bio_library.h"
+
+namespace biod {
+
+class FpcBiometric : public Biometric {
+ public:
+ static std::unique_ptr<Biometric> Create();
+
+ // Biometric overrides:
+ ~FpcBiometric() override;
+
+ Biometric::Type GetType() override;
+ Biometric::EnrollSession StartEnroll(std::string user_id,
+ std::string label) override;
+ Biometric::AuthenticationSession StartAuthentication() override;
+ std::vector<std::unique_ptr<Biometric::Enrollment>> GetEnrollments() override;
+ bool DestroyAllEnrollments() override;
+
+ void SetScannedHandler(const Biometric::ScanCallback& on_scan) override;
+ void SetAttemptHandler(const Biometric::AttemptCallback& on_attempt) override;
+ void SetFailureHandler(const Biometric::FailureCallback& on_failure) override;
+
+ protected:
+ void EndEnroll() override;
+ void EndAuthentication() override;
+
+ private:
+ // Keep this function here because it's used directly by the End* functions.
+ void KillSensorTask();
+
+ using TaskRunnerRef = scoped_refptr<base::SingleThreadTaskRunner>;
+
+ class SensorLibrary;
+
+ struct ScanData {
+ struct Killed {};
+ ScanData() = default;
+ explicit ScanData(const Killed&) : killed(true) {}
+ explicit ScanData(Biometric::ScanResult r) : success(true), result(r) {}
+ explicit ScanData(BioImage i) : success(true), image(std::move(i)) {}
+
+ // True if the scan ended because of kill_task_
+ bool killed = false;
+ // True if there were NO systemic errors
+ bool success = false;
+ // Meaningless if success is false, kSuccess on a good scan, user
+ // correctable error otherwise
+ Biometric::ScanResult result = Biometric::ScanResult::kSuccess;
+ // If success and result is kSuccess, this contains the scanned image
+ BioImage image;
+
+ explicit operator bool() {
+ return !killed && success && result == Biometric::ScanResult::kSuccess &&
+ image;
+ }
+ };
+
+ struct InternalEnrollment {
+ std::string user_id;
+ std::string label;
+ BioTemplate tmpl;
+ };
+
+ // Our Enrollment implementation is just a proxy for InternalEnrollment, which
+ // are all stored inside the FakeBiometric object's enrollments map.
+ class Enrollment : public Biometric::Enrollment {
+ public:
+ Enrollment(const base::WeakPtr<FpcBiometric>& biometric, uint64_t id)
+ : biometric_(biometric), id_(id) {}
+
+ // Biometric::Enrollment overrides:
+ uint64_t GetId() const override;
+ const std::string& GetUserId() const override;
+ const std::string& GetLabel() const override;
+ bool SetLabel(std::string label) override;
+ bool Remove() override;
+
+ private:
+ base::WeakPtr<FpcBiometric> biometric_;
+ uint64_t id_;
+
+ // These are mutable because the const GetUserId and GetLabel methods use
+ // them as storage for the their respective return string refs.
+ mutable std::string local_user_id_;
+ mutable std::string local_label_;
+
+ // // Only call while holding the enrollments_lock_
+ InternalEnrollment* GetInternalLocked() const;
+
+ using EnrollmentIterator =
+ std::unordered_map<uint64_t, InternalEnrollment>::iterator;
+ // Used to ensure that the internal enrollment is always handled with proper
+ // locks and checking.
+ template <typename F>
+ bool WithInternal(F f) const {
+ if (!biometric_)
+ return false;
+ base::AutoLock guard(biometric_->enrollments_lock_);
+ auto internal_enrollment = biometric_->enrollments_.find(id_);
+ if (internal_enrollment == biometric_->enrollments_.end())
+ return false;
+ f(internal_enrollment);
+ return true;
+ }
+ };
+
+ FpcBiometric();
+ bool Init();
+
+ // These are basic wrappers for the client callback functions. We use them for
+ // two reasons:
+ // - these will always work even if the underlying callbacks are null
+ // - the address of these methods never changes, which is important when
+ // posting tasks onto the main thread from the sensor thread.
+ void OnScan(Biometric::ScanResult result, bool done);
+ void OnAttempt(Biometric::ScanResult result,
+ const std::vector<std::string>& recognized_user_ids);
+ void OnFailure();
+
+ // This function are sensor thread only.
+ ScanData ScanImage();
+
+ // The following Do*Task and On*Complete functions are meant to be run as a
+ // pair using TaskRunner::PostTaskAndReply. The Do*Task functions run in the
+ // sensor thread and only read sensor_data_, the dynamically loaded functions,
+ // kill_task_, and the sensor itself. The On*Complete functions interpret the
+ // result of the task function on the main thread and change the state of this
+ // Biometric and its enrollments as needed.
+
+ void DoEnrollTask(const TaskRunnerRef& task_runner,
+ const std::shared_ptr<BioTemplate>& tmpl);
+ void OnEnrollComplete(std::string user_id,
+ std::string label,
+ const std::shared_ptr<BioTemplate>& tmpl);
+
+ void DoAuthenticationTask(const TaskRunnerRef& task_runner);
+ void OnAuthenticationComplete();
+
+ void OnTaskComplete();
+
+ // The following variables are const after Init and therefore totally thread
+ // safe.
+
+ base::ScopedFD sensor_fd_;
+
+ // Only used by the sensor thread after Init.
+ std::shared_ptr<BioLibrary> bio_lib_;
+ std::unique_ptr<SensorLibrary> sensor_lib_;
+
+ // Variables used to control the sensor thread and are shared.
+ bool running_task_ = false;
+ base::Lock kill_task_lock_;
+ bool kill_task_ = false;
+ base::Thread sensor_thread_;
+
+ // This lock protects next_enrollment_id_ and enrollments_.
+ base::Lock enrollments_lock_;
+ size_t next_enrollment_id_ = 0;
+ std::unordered_map<uint64_t, InternalEnrollment> enrollments_;
+
+ // All the following variables are main thread only.
+
+ Biometric::ScanCallback on_scan_;
+ Biometric::AttemptCallback on_attempt_;
+ Biometric::FailureCallback on_failure_;
+
+ base::WeakPtrFactory<FpcBiometric> session_weak_factory_;
+ base::WeakPtrFactory<FpcBiometric> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(FpcBiometric);
+};
+} // namespace biod
+
+#endif // BIOD_FPC_BIOMETRIC_H_
diff --git a/biod/main.cc b/biod/main.cc
index 43b663a..fecbe0e 100644
--- a/biod/main.cc
+++ b/biod/main.cc
@@ -4,6 +4,7 @@
#include <string>
+#include <base/at_exit.h>
#include <base/command_line.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
@@ -68,9 +69,11 @@
LOG(INFO) << "vcsid " << VCSID;
base::MessageLoopForIO message_loop;
+ base::AtExitManager at_exit_manager;
biod::BiometricsDaemon bio_daemon;
+
message_loop.Run();
return 0;
}