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;
 }