| // 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_biometrics_manager.h" |
| |
| #include <algorithm> |
| |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| |
| #include <base/base64.h> |
| #include <base/bind.h> |
| #include <base/logging.h> |
| #include <base/strings/string_piece.h> |
| #include <base/threading/thread_task_runner_handle.h> |
| |
| #include "biod/fpc/fp_sensor.h" |
| |
| namespace biod { |
| |
| class FpcBiometricsManager::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); |
| }; |
| |
| FpcBiometricsManager::SensorLibrary::~SensorLibrary() { |
| if (needs_close_) |
| close_(); |
| } |
| |
| std::unique_ptr<FpcBiometricsManager::SensorLibrary> |
| FpcBiometricsManager::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 FpcBiometricsManager::SensorLibrary::BeginEnrollment() { |
| return bio_sensor_.BeginEnrollment(); |
| } |
| |
| std::tuple<int, BioImage> FpcBiometricsManager::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)) { |
| LOG(ERROR) << "Failed to construct BioImage for image acquired."; |
| return std::tuple<int, BioImage>(-ENOMEM, BioImage()); |
| } |
| |
| return std::tuple<int, BioImage>(0 /* success */, std::move(image)); |
| } |
| |
| bool FpcBiometricsManager::SensorLibrary::WaitFingerUp() { |
| int ret = wait_finger_up_(); |
| if (ret) |
| LOG(ERROR) << "Failed to wait for finger up: " << ret; |
| return ret == 0; |
| } |
| |
| bool FpcBiometricsManager::SensorLibrary::Cancel() { |
| int ret = cancel_(); |
| if (ret) |
| LOG(ERROR) << "Failed to cancel FPC sensor operation: " << ret; |
| return ret == 0; |
| } |
| |
| bool FpcBiometricsManager::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 : " << static_cast<char>(a) |
| << static_cast<char>(a >> 8) << static_cast<char>(a >> 16) |
| << static_cast<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; |
| } |
| |
| const std::string& FpcBiometricsManager::Record::GetId() const { |
| return id_; |
| } |
| |
| const std::string& FpcBiometricsManager::Record::GetUserId() const { |
| CHECK(WithInternal([this](RecordIterator i) { |
| this->local_user_id_ = i->second.user_id; |
| })) << ": Attempted to get user ID for invalid BiometricsManager Record"; |
| return local_user_id_; |
| } |
| |
| const std::string& FpcBiometricsManager::Record::GetLabel() const { |
| CHECK(WithInternal([this](RecordIterator i) { |
| this->local_label_ = i->second.label; |
| })) << ": Attempted to get label for invalid BiometricsManager Record"; |
| return local_label_; |
| } |
| |
| bool FpcBiometricsManager::Record::SetLabel(std::string label) { |
| std::string old_label; |
| std::vector<uint8_t> serialized_tmpl; |
| CHECK(WithInternal([&](RecordIterator i) { |
| old_label = i->second.label; |
| i->second.label = std::move(label); |
| (i->second.tmpl).Serialize(&serialized_tmpl); |
| })) << ": Attempted to reset label for invalid BiometricsManager Record"; |
| |
| if (!biometrics_manager_->WriteRecord(*this, serialized_tmpl.data(), |
| serialized_tmpl.size())) { |
| CHECK(WithInternal( |
| [&](RecordIterator i) { i->second.label = std::move(old_label); })); |
| return false; |
| } |
| return true; |
| } |
| |
| bool FpcBiometricsManager::Record::Remove() { |
| if (!biometrics_manager_) |
| return false; |
| if (!biometrics_manager_->biod_storage_.DeleteRecord(GetUserId(), GetId())) { |
| return false; |
| } |
| return WithInternal( |
| [this](RecordIterator i) { biometrics_manager_->records_.erase(i); }); |
| } |
| |
| int FpcBiometricsManager::g_sensor_fd = -1; |
| |
| std::unique_ptr<BiometricsManager> FpcBiometricsManager::Create() { |
| std::unique_ptr<FpcBiometricsManager> biometrics_manager( |
| new FpcBiometricsManager); |
| if (!biometrics_manager->Init()) |
| return nullptr; |
| |
| return std::unique_ptr<BiometricsManager>(std::move(biometrics_manager)); |
| } |
| |
| BiometricType FpcBiometricsManager::GetType() { |
| return BIOMETRIC_TYPE_FINGERPRINT; |
| } |
| |
| BiometricsManager::EnrollSession FpcBiometricsManager::StartEnrollSession( |
| std::string user_id, std::string label) { |
| if (running_task_) |
| return BiometricsManager::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(&FpcBiometricsManager::DoEnrollSessionTask, |
| base::Unretained(this), base::ThreadTaskRunnerHandle::Get(), |
| tmpl), |
| base::Bind(&FpcBiometricsManager::OnEnrollSessionComplete, |
| weak_factory_.GetWeakPtr(), std::move(user_id), |
| std::move(label), tmpl)); |
| |
| if (!task_will_run) { |
| LOG(ERROR) << "Failed to schedule EnrollSession task"; |
| return BiometricsManager::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 BiometricsManager::EnrollSession(session_weak_factory_.GetWeakPtr()); |
| } |
| |
| BiometricsManager::AuthSession FpcBiometricsManager::StartAuthSession() { |
| if (running_task_) |
| return BiometricsManager::AuthSession(); |
| |
| std::shared_ptr<std::unordered_set<std::string>> updated_record_ids = |
| std::make_shared<std::unordered_set<std::string>>(); |
| |
| kill_task_ = false; |
| bool task_will_run = sensor_thread_.task_runner()->PostTaskAndReply( |
| FROM_HERE, |
| base::Bind(&FpcBiometricsManager::DoAuthSessionTask, |
| base::Unretained(this), base::ThreadTaskRunnerHandle::Get(), |
| updated_record_ids), |
| base::Bind(&FpcBiometricsManager::OnAuthSessionComplete, |
| weak_factory_.GetWeakPtr(), updated_record_ids)); |
| |
| if (!task_will_run) { |
| LOG(ERROR) << "Failed to schedule AuthSession task"; |
| return BiometricsManager::AuthSession(); |
| } |
| |
| // 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 BiometricsManager::AuthSession(session_weak_factory_.GetWeakPtr()); |
| } |
| |
| std::vector<std::unique_ptr<BiometricsManager::Record>> |
| FpcBiometricsManager::GetRecords() { |
| base::AutoLock guard(records_lock_); |
| std::vector<std::unique_ptr<BiometricsManager::Record>> records( |
| records_.size()); |
| std::transform(records_.begin(), records_.end(), records.begin(), |
| [this](decltype(records_)::value_type& record) { |
| return std::unique_ptr<BiometricsManager::Record>( |
| new Record(weak_factory_.GetWeakPtr(), record.first)); |
| }); |
| return records; |
| } |
| |
| bool FpcBiometricsManager::DestroyAllRecords() { |
| base::AutoLock guard(records_lock_); |
| |
| // Enumerate through records_ and delete each record. |
| bool delete_all_records = true; |
| for (auto& record_pair : records_) { |
| std::string record_id(record_pair.first); |
| std::string user_id(record_pair.second.user_id); |
| delete_all_records &= biod_storage_.DeleteRecord(user_id, record_id); |
| } |
| records_.clear(); |
| return delete_all_records; |
| } |
| |
| void FpcBiometricsManager::RemoveRecordsFromMemory() { |
| base::AutoLock guard(records_lock_); |
| records_.clear(); |
| } |
| |
| bool FpcBiometricsManager::ReadRecords( |
| const std::unordered_set<std::string>& user_ids) { |
| return biod_storage_.ReadRecords(user_ids); |
| } |
| |
| void FpcBiometricsManager::SetEnrollScanDoneHandler( |
| const BiometricsManager::EnrollScanDoneCallback& on_enroll_scan_done) { |
| on_enroll_scan_done_ = on_enroll_scan_done; |
| } |
| |
| void FpcBiometricsManager::SetAuthScanDoneHandler( |
| const BiometricsManager::AuthScanDoneCallback& on_auth_scan_done) { |
| on_auth_scan_done_ = on_auth_scan_done; |
| } |
| |
| void FpcBiometricsManager::SetSessionFailedHandler( |
| const BiometricsManager::SessionFailedCallback& on_session_failed) { |
| on_session_failed_ = on_session_failed; |
| } |
| |
| void FpcBiometricsManager::EndEnrollSession() { |
| KillSensorTask(); |
| } |
| |
| void FpcBiometricsManager::EndAuthSession() { |
| KillSensorTask(); |
| } |
| |
| void FpcBiometricsManager::KillSensorTask() { |
| { |
| base::AutoLock guard(kill_task_lock_); |
| kill_task_ = true; |
| } |
| sensor_lib_->Cancel(); |
| } |
| |
| FpcBiometricsManager::FpcBiometricsManager() |
| : sensor_thread_("fpc_sensor"), |
| session_weak_factory_(this), |
| weak_factory_(this), |
| biod_storage_(kFpcBiometricsManagerName, |
| base::Bind(&FpcBiometricsManager::LoadRecord, |
| base::Unretained(this))) {} |
| |
| FpcBiometricsManager::~FpcBiometricsManager() {} |
| |
| bool FpcBiometricsManager::Init() { |
| const char kFpcSensorPath[] = "/dev/fpc_sensor0"; |
| sensor_fd_ = base::ScopedFD(open(kFpcSensorPath, O_RDWR)); |
| g_sensor_fd = sensor_fd_.get(); |
| if (sensor_fd_.get() < 0) { |
| LOG(ERROR) << "Failed to open " << kFpcSensorPath; |
| return false; |
| } |
| |
| const char kFpcLibName[] = "/opt/fpc/lib/libfpsensor.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 FpcBiometricsManager::OnEnrollScanDone( |
| ScanResult result, const BiometricsManager::EnrollStatus& enroll_status) { |
| if (!on_enroll_scan_done_.is_null()) |
| on_enroll_scan_done_.Run(result, enroll_status); |
| } |
| |
| void FpcBiometricsManager::OnAuthScanDone( |
| ScanResult result, const BiometricsManager::AttemptMatches& matches) { |
| if (!on_auth_scan_done_.is_null()) |
| on_auth_scan_done_.Run(result, matches); |
| } |
| |
| void FpcBiometricsManager::OnSessionFailed() { |
| if (!on_session_failed_.is_null()) |
| on_session_failed_.Run(); |
| } |
| |
| FpcBiometricsManager::ScanData FpcBiometricsManager::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, attempts = 0; |
| BioImage image; |
| // If the finger is positioned slightly off the sensor, retry a few times |
| // before failing. Typically the user has put their finger down and is now |
| // moving their finger to the correct position on the sensor. Instead of |
| // immediately failing, retry until we get a good image. |
| // Retry 20 times, which takes ~5s on Eve, before giving up and sending an |
| // error back to the user. Assume ~1s for user noticing that no matching |
| // happened, some time to move the finger on the sensor to allow a full |
| // capture and another 1s for the second matching attempt. 5s gives a bit of |
| // margin to avoid interrupting the user while they're moving the finger on |
| // the sensor. |
| const int kMaxPartialAttempts = 20; |
| do { |
| std::tie(acquire_result, image) = sensor_lib_->AcquireImage(); |
| { |
| base::AutoLock guard(kill_task_lock_); |
| if (kill_task_) |
| return ScanData(ScanData::Killed()); |
| } |
| ++attempts; |
| } while (acquire_result == FP_SENSOR_LOW_SENSOR_COVERAGE && |
| attempts < kMaxPartialAttempts); |
| |
| switch (acquire_result) { |
| case 0: |
| break; |
| case FP_SENSOR_TOO_FAST: |
| return ScanData(ScanResult::SCAN_RESULT_TOO_FAST); |
| case FP_SENSOR_LOW_IMAGE_QUALITY: |
| return ScanData(ScanResult::SCAN_RESULT_INSUFFICIENT); |
| case FP_SENSOR_LOW_SENSOR_COVERAGE: |
| return ScanData(ScanResult::SCAN_RESULT_PARTIAL); |
| default: |
| LOG(ERROR) << "Unexpected result from acquiring image: " |
| << acquire_result; |
| return ScanData(); |
| } |
| |
| return ScanData(std::move(image)); |
| } |
| |
| void FpcBiometricsManager::DoEnrollSessionTask( |
| 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; |
| |
| 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 = ScanResult::SCAN_RESULT_IMMOBILE; |
| break; |
| case BIO_ENROLLMENT_LOW_COVERAGE: |
| scan_result = ScanResult::SCAN_RESULT_PARTIAL; |
| break; |
| case BIO_ENROLLMENT_LOW_QUALITY: |
| scan_result = ScanResult::SCAN_RESULT_INSUFFICIENT; |
| 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 { |
| BiometricsManager::EnrollStatus enroll_status = { |
| false, enrollment.GetPercentComplete()}; |
| |
| // Notice we will only ever post the on_enroll_scan_done task on an |
| // incomplete enrollment. The complete on_enroll_scan_done task is only |
| // posted after the enrollment is added to the enrollments(records) map, |
| // which is done only on the main thread's message loop. |
| bool task_will_run = task_runner->PostTask( |
| FROM_HERE, |
| base::Bind(&FpcBiometricsManager::OnEnrollScanDone, |
| base::Unretained(this), scan_result, enroll_status)); |
| if (!task_will_run) { |
| LOG(ERROR) << "Failed to schedule EnrollScanDone callback"; |
| return; |
| } |
| } |
| } |
| } |
| |
| void FpcBiometricsManager::OnEnrollSessionComplete( |
| 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()) { |
| OnSessionFailed(); |
| return; |
| } |
| |
| std::vector<uint8_t> serialized_tmpl; |
| if (!tmpl->Serialize(&serialized_tmpl)) { |
| OnSessionFailed(); |
| return; |
| } |
| |
| std::string record_id(biod_storage_.GenerateNewRecordId()); |
| { |
| base::AutoLock guard(records_lock_); |
| records_.emplace(record_id, |
| InternalRecord{std::move(user_id), std::move(label), |
| std::move(*tmpl.get())}); |
| } |
| Record current_record(weak_factory_.GetWeakPtr(), record_id); |
| if (!WriteRecord(current_record, serialized_tmpl.data(), |
| serialized_tmpl.size())) { |
| { |
| base::AutoLock guard(records_lock_); |
| records_.erase(record_id); |
| } |
| OnSessionFailed(); |
| return; |
| } |
| |
| BiometricsManager::EnrollStatus enroll_status = {true, 100}; |
| OnEnrollScanDone(ScanResult::SCAN_RESULT_SUCCESS, enroll_status); |
| } |
| |
| void FpcBiometricsManager::DoAuthSessionTask( |
| const TaskRunnerRef& task_runner, |
| std::shared_ptr<std::unordered_set<std::string>> updated_record_ids) { |
| DCHECK(sensor_thread_.task_runner()->RunsTasksOnCurrentThread()); |
| |
| if (kill_task_) |
| return; |
| |
| BiometricsManager::AttemptMatches matches; |
| |
| 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; |
| |
| ScanResult result = scan.result; |
| if (result == ScanResult::SCAN_RESULT_SUCCESS) { |
| matches.clear(); |
| |
| base::AutoLock guard(records_lock_); |
| for (auto& kv : records_) { |
| InternalRecord& record = kv.second; |
| |
| int match_result = record.tmpl.MatchImage(scan.image); |
| switch (match_result) { |
| case BIO_TEMPLATE_NO_MATCH: |
| break; |
| case BIO_TEMPLATE_MATCH_UPDATED: // record.tmpl got updated |
| updated_record_ids->insert(kv.first); |
| case BIO_TEMPLATE_MATCH: { |
| auto emplace_result = |
| matches.emplace(record.user_id, std::vector<std::string>()); |
| emplace_result.first->second.emplace_back(kv.first); |
| break; |
| } |
| case BIO_TEMPLATE_LOW_QUALITY: |
| result = ScanResult::SCAN_RESULT_INSUFFICIENT; |
| break; |
| case BIO_TEMPLATE_LOW_COVERAGE: |
| result = ScanResult::SCAN_RESULT_PARTIAL; |
| 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 (!matches.empty()) |
| result = ScanResult::SCAN_RESULT_SUCCESS; |
| |
| bool task_will_run = task_runner->PostTask( |
| FROM_HERE, |
| base::Bind(&FpcBiometricsManager::OnAuthScanDone, |
| base::Unretained(this), result, std::move(matches))); |
| if (!task_will_run) { |
| LOG(ERROR) << "Failed to schedule AuthScanDone callback"; |
| return; |
| } |
| } |
| } |
| |
| void FpcBiometricsManager::OnAuthSessionComplete( |
| std::shared_ptr<std::unordered_set<std::string>> updated_record_ids) { |
| OnTaskComplete(); |
| |
| // AuthSession never ends except on error or being killed. If no kill |
| // signal was given, we can assume failure. |
| if (!kill_task_) |
| OnSessionFailed(); |
| |
| for (const std::string& record_id : *updated_record_ids) { |
| InternalRecord& record = records_[record_id]; |
| |
| std::vector<uint8_t> serialized_tmpl; |
| if (!record.tmpl.Serialize(&serialized_tmpl)) { |
| LOG(ERROR) << "Cannot update record " << record_id |
| << " in storage during AuthSession because template " |
| "serialization failed."; |
| continue; |
| } |
| |
| Record current_record(weak_factory_.GetWeakPtr(), record_id); |
| if (!WriteRecord(current_record, serialized_tmpl.data(), |
| serialized_tmpl.size())) { |
| LOG(ERROR) << "Cannot update record " << record_id |
| << " in storage during AuthSession because writing failed."; |
| } |
| } |
| } |
| |
| void FpcBiometricsManager::OnTaskComplete() { |
| session_weak_factory_.InvalidateWeakPtrs(); |
| running_task_ = false; |
| } |
| |
| bool FpcBiometricsManager::LoadRecord(const std::string& user_id, |
| const std::string& label, |
| const std::string& record_id, |
| const base::Value& data) { |
| std::string tmpl_data_base64; |
| if (!data.GetAsString(&tmpl_data_base64)) { |
| LOG(ERROR) << "Cannot load data string from record " << record_id << "."; |
| return false; |
| } |
| |
| base::StringPiece tmpl_data_base64_sp(tmpl_data_base64); |
| std::string tmpl_data_str; |
| base::Base64Decode(tmpl_data_base64_sp, &tmpl_data_str); |
| |
| std::vector<uint8_t> tmpl_data_vector(tmpl_data_str.begin(), |
| tmpl_data_str.end()); |
| BioTemplate tmpl(bio_lib_->DeserializeTemplate(tmpl_data_vector)); |
| InternalRecord internal_record = {user_id, label, std::move(tmpl)}; |
| { |
| base::AutoLock guard(records_lock_); |
| records_[record_id] = std::move(internal_record); |
| } |
| LOG(INFO) << "Load record " << record_id << " from disk."; |
| return true; |
| } |
| |
| bool FpcBiometricsManager::WriteRecord(const BiometricsManager::Record& record, |
| uint8_t* tmpl_data, |
| size_t tmpl_size) { |
| base::StringPiece tmpl_sp(reinterpret_cast<char*>(tmpl_data), tmpl_size); |
| std::string tmpl_base64; |
| base::Base64Encode(tmpl_sp, &tmpl_base64); |
| |
| return biod_storage_.WriteRecord(record, |
| std::make_unique<base::Value>(tmpl_base64)); |
| } |
| |
| } // namespace biod |