| // Copyright 2017 The Chromium 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 "services/device/fingerprint/fingerprint_chromeos.h" |
| |
| #include <string.h> |
| |
| #include "base/bind.h" |
| #include "chromeos/dbus/biod/biod_client.h" |
| #include "dbus/object_path.h" |
| #include "mojo/public/cpp/bindings/strong_binding.h" |
| #include "services/device/fingerprint/fingerprint.h" |
| #include "services/device/public/mojom/fingerprint.mojom.h" |
| |
| namespace device { |
| |
| namespace { |
| |
| chromeos::BiodClient* GetBiodClient() { |
| return chromeos::BiodClient::Get(); |
| } |
| |
| // Helper functions to convert between dbus and mojo types. The dbus type comes |
| // from code imported from cros, so it is hard to use the mojo type there. Since |
| // the dbus type is imported, there are DEPs restrictions w.r.t. using it across |
| // the entire code-base. Chrome code outside of the interop layer with dbus |
| // exclusively uses the mojo type. |
| device::mojom::BiometricType ToMojom(biod::BiometricType type) { |
| switch (type) { |
| case biod::BIOMETRIC_TYPE_UNKNOWN: |
| return device::mojom::BiometricType::UNKNOWN; |
| case biod::BIOMETRIC_TYPE_FINGERPRINT: |
| return device::mojom::BiometricType::FINGERPRINT; |
| case biod::BIOMETRIC_TYPE_MAX: |
| return device::mojom::BiometricType::kMaxValue; |
| } |
| NOTREACHED(); |
| return device::mojom::BiometricType::UNKNOWN; |
| } |
| device::mojom::ScanResult ToMojom(biod::ScanResult type) { |
| switch (type) { |
| case biod::SCAN_RESULT_SUCCESS: |
| return device::mojom::ScanResult::SUCCESS; |
| case biod::SCAN_RESULT_PARTIAL: |
| return device::mojom::ScanResult::PARTIAL; |
| case biod::SCAN_RESULT_INSUFFICIENT: |
| return device::mojom::ScanResult::INSUFFICIENT; |
| case biod::SCAN_RESULT_SENSOR_DIRTY: |
| return device::mojom::ScanResult::SENSOR_DIRTY; |
| case biod::SCAN_RESULT_TOO_SLOW: |
| return device::mojom::ScanResult::TOO_SLOW; |
| case biod::SCAN_RESULT_TOO_FAST: |
| return device::mojom::ScanResult::TOO_FAST; |
| case biod::SCAN_RESULT_IMMOBILE: |
| return device::mojom::ScanResult::IMMOBILE; |
| case biod::SCAN_RESULT_MAX: |
| return device::mojom::ScanResult::kMaxValue; |
| } |
| NOTREACHED(); |
| return device::mojom::ScanResult::INSUFFICIENT; |
| } |
| |
| } // namespace |
| |
| FingerprintChromeOS::FingerprintChromeOS() : weak_ptr_factory_(this) { |
| CHECK(GetBiodClient()); |
| GetBiodClient()->AddObserver(this); |
| } |
| |
| FingerprintChromeOS::~FingerprintChromeOS() { |
| GetBiodClient()->RemoveObserver(this); |
| if (opened_session_ == FingerprintSession::ENROLL) { |
| GetBiodClient()->CancelEnrollSession( |
| chromeos::EmptyVoidDBusMethodCallback()); |
| } else if (opened_session_ == FingerprintSession::AUTH) { |
| GetBiodClient()->EndAuthSession(chromeos::EmptyVoidDBusMethodCallback()); |
| } |
| } |
| |
| void FingerprintChromeOS::GetRecordsForUser( |
| const std::string& user_id, |
| GetRecordsForUserCallback callback) { |
| get_records_pending_requests_.push(base::BindOnce( |
| &FingerprintChromeOS::RunGetRecordsForUser, |
| weak_ptr_factory_.GetWeakPtr(), user_id, std::move(callback))); |
| if (is_request_running_) |
| return; |
| |
| is_request_running_ = true; |
| StartNextRequest(); |
| } |
| |
| void FingerprintChromeOS::RunGetRecordsForUser( |
| const std::string& user_id, |
| GetRecordsForUserCallback callback) { |
| GetBiodClient()->GetRecordsForUser( |
| user_id, |
| base::BindOnce(&FingerprintChromeOS::OnGetRecordsForUser, |
| weak_ptr_factory_.GetWeakPtr(), std::move(callback))); |
| } |
| |
| void FingerprintChromeOS::StartEnrollSession(const std::string& user_id, |
| const std::string& label) { |
| if (opened_session_ == FingerprintSession::ENROLL) |
| return; |
| |
| GetBiodClient()->EndAuthSession( |
| base::Bind(&FingerprintChromeOS::OnCloseAuthSessionForEnroll, |
| weak_ptr_factory_.GetWeakPtr(), user_id, label)); |
| } |
| |
| void FingerprintChromeOS::OnCloseAuthSessionForEnroll( |
| const std::string& user_id, |
| const std::string& label, |
| bool result) { |
| if (!result) |
| return; |
| |
| GetBiodClient()->StartEnrollSession( |
| user_id, label, |
| base::Bind(&FingerprintChromeOS::OnStartEnrollSession, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void FingerprintChromeOS::CancelCurrentEnrollSession( |
| CancelCurrentEnrollSessionCallback callback) { |
| if (opened_session_ == FingerprintSession::ENROLL) { |
| GetBiodClient()->CancelEnrollSession(std::move(callback)); |
| opened_session_ = FingerprintSession::NONE; |
| } else { |
| std::move(callback).Run(true); |
| } |
| } |
| |
| void FingerprintChromeOS::RequestRecordLabel( |
| const std::string& record_path, |
| RequestRecordLabelCallback callback) { |
| GetBiodClient()->RequestRecordLabel( |
| dbus::ObjectPath(record_path), |
| base::AdaptCallbackForRepeating(std::move(callback))); |
| } |
| |
| void FingerprintChromeOS::SetRecordLabel(const std::string& new_label, |
| const std::string& record_path, |
| SetRecordLabelCallback callback) { |
| GetBiodClient()->SetRecordLabel(dbus::ObjectPath(record_path), new_label, |
| std::move(callback)); |
| } |
| |
| void FingerprintChromeOS::RemoveRecord(const std::string& record_path, |
| RemoveRecordCallback callback) { |
| GetBiodClient()->RemoveRecord(dbus::ObjectPath(record_path), |
| std::move(callback)); |
| } |
| |
| void FingerprintChromeOS::StartAuthSession() { |
| if (opened_session_ == FingerprintSession::AUTH) |
| return; |
| |
| if (opened_session_ == FingerprintSession::ENROLL) { |
| GetBiodClient()->CancelEnrollSession( |
| base::BindRepeating(&FingerprintChromeOS::OnCloseEnrollSessionForAuth, |
| weak_ptr_factory_.GetWeakPtr())); |
| } else { |
| GetBiodClient()->StartAuthSession( |
| base::Bind(&FingerprintChromeOS::OnStartAuthSession, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| } |
| |
| void FingerprintChromeOS::OnCloseEnrollSessionForAuth(bool result) { |
| if (!result) |
| return; |
| |
| GetBiodClient()->StartAuthSession( |
| base::Bind(&FingerprintChromeOS::OnStartAuthSession, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void FingerprintChromeOS::EndCurrentAuthSession( |
| EndCurrentAuthSessionCallback callback) { |
| if (opened_session_ == FingerprintSession::AUTH) { |
| GetBiodClient()->EndAuthSession(std::move(callback)); |
| opened_session_ = FingerprintSession::NONE; |
| } else { |
| std::move(callback).Run(true); |
| } |
| } |
| |
| void FingerprintChromeOS::DestroyAllRecords( |
| DestroyAllRecordsCallback callback) { |
| GetBiodClient()->DestroyAllRecords(std::move(callback)); |
| } |
| |
| void FingerprintChromeOS::RequestType(RequestTypeCallback callback) { |
| GetBiodClient()->RequestType(base::BindOnce( |
| [](RequestTypeCallback callback, biod::BiometricType type) { |
| std::move(callback).Run(ToMojom(type)); |
| }, |
| std::move(callback))); |
| } |
| |
| void FingerprintChromeOS::AddFingerprintObserver( |
| mojom::FingerprintObserverPtr observer) { |
| observer.set_connection_error_handler( |
| base::Bind(&FingerprintChromeOS::OnFingerprintObserverDisconnected, |
| base::Unretained(this), observer.get())); |
| observers_.push_back(std::move(observer)); |
| } |
| |
| void FingerprintChromeOS::BiodServiceRestarted() { |
| opened_session_ = FingerprintSession::NONE; |
| for (auto& observer : observers_) |
| observer->OnRestarted(); |
| } |
| |
| void FingerprintChromeOS::BiodEnrollScanDoneReceived( |
| biod::ScanResult scan_result, |
| bool enroll_session_complete, |
| int percent_complete) { |
| if (enroll_session_complete) |
| opened_session_ = FingerprintSession::NONE; |
| |
| for (auto& observer : observers_) { |
| observer->OnEnrollScanDone(ToMojom(scan_result), enroll_session_complete, |
| percent_complete); |
| } |
| } |
| |
| void FingerprintChromeOS::BiodAuthScanDoneReceived( |
| biod::ScanResult scan_result, |
| const chromeos::AuthScanMatches& matches) { |
| // Convert ObjectPath to string, since mojom doesn't know definition of |
| // dbus ObjectPath. |
| std::vector<std::pair<std::string, std::vector<std::string>>> entries; |
| for (auto& item : matches) { |
| std::vector<std::string> paths; |
| for (auto& object_path : item.second) { |
| paths.push_back(object_path.value()); |
| } |
| entries.emplace_back(std::move(item.first), std::move(paths)); |
| } |
| |
| auto casted_scan_result = static_cast<device::mojom::ScanResult>(scan_result); |
| CHECK(device::mojom::IsKnownEnumValue(casted_scan_result)); |
| |
| for (auto& observer : observers_) { |
| observer->OnAuthScanDone( |
| casted_scan_result, |
| base::flat_map<std::string, std::vector<std::string>>( |
| std::move(entries))); |
| } |
| } |
| |
| void FingerprintChromeOS::BiodSessionFailedReceived() { |
| for (auto& observer : observers_) |
| observer->OnSessionFailed(); |
| } |
| |
| void FingerprintChromeOS::OnFingerprintObserverDisconnected( |
| mojom::FingerprintObserver* observer) { |
| for (auto item = observers_.begin(); item != observers_.end(); ++item) { |
| if (item->get() == observer) { |
| observers_.erase(item); |
| break; |
| } |
| } |
| } |
| |
| void FingerprintChromeOS::OnStartEnrollSession( |
| const dbus::ObjectPath& enroll_path) { |
| if (enroll_path.IsValid()) { |
| DCHECK_NE(opened_session_, FingerprintSession::ENROLL); |
| opened_session_ = FingerprintSession::ENROLL; |
| } |
| } |
| |
| void FingerprintChromeOS::OnStartAuthSession( |
| const dbus::ObjectPath& auth_path) { |
| if (auth_path.IsValid()) { |
| DCHECK_NE(opened_session_, FingerprintSession::AUTH); |
| opened_session_ = FingerprintSession::AUTH; |
| } |
| } |
| |
| void FingerprintChromeOS::OnGetRecordsForUser( |
| GetRecordsForUserCallback callback, |
| const std::vector<dbus::ObjectPath>& records) { |
| if (records.size() == 0) { |
| std::move(callback).Run({base::flat_map<std::string, std::string>()}); |
| StartNextRequest(); |
| return; |
| } |
| |
| DCHECK(!on_get_records_); |
| on_get_records_ = std::move(callback); |
| |
| for (auto& record : records) { |
| GetBiodClient()->RequestRecordLabel( |
| record, |
| base::BindOnce(&FingerprintChromeOS::OnGetLabelFromRecordPath, |
| weak_ptr_factory_.GetWeakPtr(), records.size(), record)); |
| } |
| } |
| |
| void FingerprintChromeOS::OnGetLabelFromRecordPath( |
| size_t num_records, |
| const dbus::ObjectPath& record_path, |
| const std::string& label) { |
| records_path_to_label_[record_path.value()] = label; |
| if (records_path_to_label_.size() == num_records) { |
| DCHECK(on_get_records_); |
| std::move(on_get_records_).Run(records_path_to_label_); |
| StartNextRequest(); |
| } |
| } |
| |
| void FingerprintChromeOS::StartNextRequest() { |
| records_path_to_label_.clear(); |
| |
| // All the pending requests complete, toggle |is_request_running_|. |
| if (get_records_pending_requests_.empty()) { |
| is_request_running_ = false; |
| return; |
| } |
| |
| // Current request completes, start running next request. |
| std::move(get_records_pending_requests_.front()).Run(); |
| get_records_pending_requests_.pop(); |
| } |
| |
| // static |
| void Fingerprint::Create(device::mojom::FingerprintRequest request) { |
| mojo::MakeStrongBinding(std::make_unique<FingerprintChromeOS>(), |
| std::move(request)); |
| } |
| |
| } // namespace device |