blob: 3827fc993b2b801989a572eeceea98e1d040a201 [file] [log] [blame]
// 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 "chromeos/dbus/biod/biod_client.h"
#include <stdint.h>
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/macros.h"
#include "base/strings/stringprintf.h"
#include "chromeos/dbus/biod/fake_biod_client.h"
#include "chromeos/dbus/biod/messages.pb.h"
#include "dbus/bus.h"
#include "dbus/message.h"
#include "dbus/object_path.h"
#include "dbus/object_proxy.h"
namespace chromeos {
namespace {
// D-Bus response handler for methods that use void callbacks.
void OnVoidResponse(VoidDBusMethodCallback callback, dbus::Response* response) {
std::move(callback).Run(response != nullptr);
}
} // namespace
// The BiodClient implementation used in production.
class BiodClientImpl : public BiodClient {
public:
BiodClientImpl() : weak_ptr_factory_(this) {}
~BiodClientImpl() override = default;
// BiodClient overrides:
void AddObserver(Observer* observer) override {
observers_.AddObserver(observer);
}
void RemoveObserver(Observer* observer) override {
observers_.RemoveObserver(observer);
}
bool HasObserver(const Observer* observer) const override {
return observers_.HasObserver(observer);
}
void StartEnrollSession(const std::string& user_id,
const std::string& label,
const ObjectPathCallback& callback) override {
// If we are already in enroll session, just return an invalid ObjectPath.
// The one who initially start the enroll session will have control
// over the life cycle of the session.
if (current_enroll_session_path_) {
callback.Run(dbus::ObjectPath());
return;
}
dbus::MethodCall method_call(
biod::kBiometricsManagerInterface,
biod::kBiometricsManagerStartEnrollSessionMethod);
dbus::MessageWriter writer(&method_call);
writer.AppendString(user_id);
writer.AppendString(label);
biod_proxy_->CallMethod(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&BiodClientImpl::OnStartEnrollSession,
weak_ptr_factory_.GetWeakPtr(), callback));
}
void GetRecordsForUser(const std::string& user_id,
UserRecordsCallback callback) override {
dbus::MethodCall method_call(
biod::kBiometricsManagerInterface,
biod::kBiometricsManagerGetRecordsForUserMethod);
dbus::MessageWriter writer(&method_call);
writer.AppendString(user_id);
biod_proxy_->CallMethod(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&BiodClientImpl::OnGetRecordsForUser,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void DestroyAllRecords(VoidDBusMethodCallback callback) override {
dbus::MethodCall method_call(
biod::kBiometricsManagerInterface,
biod::kBiometricsManagerDestroyAllRecordsMethod);
biod_proxy_->CallMethod(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&OnVoidResponse, std::move(callback)));
}
void StartAuthSession(const ObjectPathCallback& callback) override {
// If we are already in auth session, just return an invalid ObjectPath.
// The one who initially start the auth session will have control
// over the life cycle of the session.
if (current_auth_session_path_) {
callback.Run(dbus::ObjectPath(std::string()));
return;
}
dbus::MethodCall method_call(
biod::kBiometricsManagerInterface,
biod::kBiometricsManagerStartAuthSessionMethod);
biod_proxy_->CallMethod(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&BiodClientImpl::OnStartAuthSession,
weak_ptr_factory_.GetWeakPtr(), callback));
}
void RequestType(BiometricTypeCallback callback) override {
dbus::MethodCall method_call(dbus::kDBusPropertiesInterface,
dbus::kDBusPropertiesGet);
dbus::MessageWriter writer(&method_call);
writer.AppendString(biod::kBiometricsManagerInterface);
writer.AppendString(biod::kBiometricsManagerBiometricTypeProperty);
biod_proxy_->CallMethod(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&BiodClientImpl::OnRequestType,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
void CancelEnrollSession(VoidDBusMethodCallback callback) override {
if (!current_enroll_session_path_) {
std::move(callback).Run(true);
return;
}
dbus::MethodCall method_call(biod::kEnrollSessionInterface,
biod::kEnrollSessionCancelMethod);
dbus::ObjectProxy* enroll_session_proxy = bus_->GetObjectProxy(
biod::kBiodServiceName, *current_enroll_session_path_);
enroll_session_proxy->CallMethod(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&OnVoidResponse, std::move(callback)));
current_enroll_session_path_.reset();
}
void EndAuthSession(VoidDBusMethodCallback callback) override {
if (!current_auth_session_path_) {
std::move(callback).Run(true);
return;
}
dbus::MethodCall method_call(biod::kAuthSessionInterface,
biod::kAuthSessionEndMethod);
dbus::ObjectProxy* auth_session_proxy = bus_->GetObjectProxy(
biod::kBiodServiceName, *current_auth_session_path_);
auth_session_proxy->CallMethod(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&OnVoidResponse, std::move(callback)));
current_auth_session_path_.reset();
}
void SetRecordLabel(const dbus::ObjectPath& record_path,
const std::string& label,
VoidDBusMethodCallback callback) override {
dbus::MethodCall method_call(biod::kRecordInterface,
biod::kRecordSetLabelMethod);
dbus::MessageWriter writer(&method_call);
writer.AppendString(label);
dbus::ObjectProxy* record_proxy =
bus_->GetObjectProxy(biod::kBiodServiceName, record_path);
record_proxy->CallMethod(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&OnVoidResponse, std::move(callback)));
}
void RemoveRecord(const dbus::ObjectPath& record_path,
VoidDBusMethodCallback callback) override {
dbus::MethodCall method_call(biod::kRecordInterface,
biod::kRecordRemoveMethod);
dbus::ObjectProxy* record_proxy =
bus_->GetObjectProxy(biod::kBiodServiceName, record_path);
record_proxy->CallMethod(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&OnVoidResponse, std::move(callback)));
}
void RequestRecordLabel(const dbus::ObjectPath& record_path,
LabelCallback callback) override {
dbus::MethodCall method_call(dbus::kDBusPropertiesInterface,
dbus::kDBusPropertiesGet);
dbus::MessageWriter writer(&method_call);
writer.AppendString(biod::kRecordInterface);
writer.AppendString(biod::kRecordLabelProperty);
dbus::ObjectProxy* record_proxy =
bus_->GetObjectProxy(biod::kBiodServiceName, record_path);
record_proxy->CallMethod(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&BiodClientImpl::OnRequestRecordLabel,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
}
protected:
void Init(dbus::Bus* bus) override {
bus_ = bus;
dbus::ObjectPath fpc_bio_path = dbus::ObjectPath(base::StringPrintf(
"%s/%s", biod::kBiodServicePath, biod::kCrosFpBiometricsManagerName));
biod_proxy_ = bus->GetObjectProxy(biod::kBiodServiceName, fpc_bio_path);
biod_proxy_->SetNameOwnerChangedCallback(
base::Bind(&BiodClientImpl::NameOwnerChangedReceived,
weak_ptr_factory_.GetWeakPtr()));
biod_proxy_->ConnectToSignal(
biod::kBiometricsManagerInterface,
biod::kBiometricsManagerEnrollScanDoneSignal,
base::Bind(&BiodClientImpl::EnrollScanDoneReceived,
weak_ptr_factory_.GetWeakPtr()),
base::BindOnce(&BiodClientImpl::OnSignalConnected,
weak_ptr_factory_.GetWeakPtr()));
biod_proxy_->ConnectToSignal(
biod::kBiometricsManagerInterface,
biod::kBiometricsManagerAuthScanDoneSignal,
base::Bind(&BiodClientImpl::AuthScanDoneReceived,
weak_ptr_factory_.GetWeakPtr()),
base::BindOnce(&BiodClientImpl::OnSignalConnected,
weak_ptr_factory_.GetWeakPtr()));
biod_proxy_->ConnectToSignal(
biod::kBiometricsManagerInterface,
biod::kBiometricsManagerSessionFailedSignal,
base::Bind(&BiodClientImpl::SessionFailedReceived,
weak_ptr_factory_.GetWeakPtr()),
base::BindOnce(&BiodClientImpl::OnSignalConnected,
weak_ptr_factory_.GetWeakPtr()));
}
private:
void OnStartEnrollSession(const ObjectPathCallback& callback,
dbus::Response* response) {
dbus::ObjectPath result;
if (response) {
dbus::MessageReader reader(response);
if (!reader.PopObjectPath(&result)) {
LOG(ERROR) << biod::kBiometricsManagerStartEnrollSessionMethod
<< " had incorrect response.";
}
}
if (result.IsValid())
current_enroll_session_path_ = std::make_unique<dbus::ObjectPath>(result);
callback.Run(result);
}
void OnGetRecordsForUser(UserRecordsCallback callback,
dbus::Response* response) {
std::vector<dbus::ObjectPath> result;
if (response) {
dbus::MessageReader reader(response);
if (!reader.PopArrayOfObjectPaths(&result)) {
LOG(ERROR) << biod::kBiometricsManagerGetRecordsForUserMethod
<< " had incorrect response.";
}
}
std::move(callback).Run(result);
}
void OnStartAuthSession(const ObjectPathCallback& callback,
dbus::Response* response) {
dbus::ObjectPath result;
if (response) {
dbus::MessageReader reader(response);
if (!reader.PopObjectPath(&result)) {
LOG(ERROR) << biod::kBiometricsManagerStartAuthSessionMethod
<< " had incorrect response.";
}
}
if (result.IsValid())
current_auth_session_path_ = std::make_unique<dbus::ObjectPath>(result);
callback.Run(result);
}
void OnRequestType(BiometricTypeCallback callback, dbus::Response* response) {
biod::BiometricType result = biod::BIOMETRIC_TYPE_UNKNOWN;
if (response) {
dbus::MessageReader reader(response);
uint32_t value;
if (reader.PopVariantOfUint32(&value)) {
result = static_cast<biod::BiometricType>(value);
CHECK(result >= 0 && result < biod::BIOMETRIC_TYPE_MAX);
} else {
LOG(ERROR) << biod::kBiometricsManagerBiometricTypeProperty
<< " had incorrect response.";
}
}
std::move(callback).Run(result);
}
void OnRequestRecordLabel(LabelCallback callback, dbus::Response* response) {
std::string result;
if (response) {
dbus::MessageReader reader(response);
if (!reader.PopVariantOfString(&result))
LOG(ERROR) << biod::kRecordLabelProperty << " had incorrect response.";
}
std::move(callback).Run(result);
}
// Called when the biometrics signal is initially connected.
void OnSignalConnected(const std::string& interface_name,
const std::string& signal_name,
bool success) {
LOG_IF(ERROR, !success)
<< "Failed to connect to biometrics signal: " << signal_name;
}
void NameOwnerChangedReceived(const std::string& /* old_owner */,
const std::string& new_owner) {
current_enroll_session_path_.reset();
current_auth_session_path_.reset();
if (!new_owner.empty()) {
for (auto& observer : observers_)
observer.BiodServiceRestarted();
}
}
void EnrollScanDoneReceived(dbus::Signal* signal) {
dbus::MessageReader reader(signal);
biod::EnrollScanDone protobuf;
if (!reader.PopArrayOfBytesAsProto(&protobuf)) {
LOG(ERROR) << "Unable to decode protocol buffer from "
<< biod::kBiometricsManagerEnrollScanDoneSignal << " signal.";
return;
}
int percent_complete =
protobuf.has_percent_complete() ? protobuf.percent_complete() : -1;
// Enroll session is ended automatically when enrollment is done.
if (protobuf.done())
current_enroll_session_path_.reset();
for (auto& observer : observers_) {
observer.BiodEnrollScanDoneReceived(protobuf.scan_result(),
protobuf.done(), percent_complete);
}
}
void AuthScanDoneReceived(dbus::Signal* signal) {
dbus::MessageReader signal_reader(signal);
dbus::MessageReader array_reader(nullptr);
uint32_t scan_result;
AuthScanMatches matches;
if (!signal_reader.PopUint32(&scan_result) ||
!signal_reader.PopArray(&array_reader)) {
LOG(ERROR) << "Error reading signal from biometrics: "
<< signal->ToString();
return;
}
while (array_reader.HasMoreData()) {
dbus::MessageReader entry_reader(nullptr);
std::string user_id;
std::vector<dbus::ObjectPath> paths;
if (!array_reader.PopDictEntry(&entry_reader) ||
!entry_reader.PopString(&user_id) ||
!entry_reader.PopArrayOfObjectPaths(&paths)) {
LOG(ERROR) << "Error reading signal from biometrics: "
<< signal->ToString();
return;
}
matches[user_id] = std::move(paths);
}
for (auto& observer : observers_) {
observer.BiodAuthScanDoneReceived(
static_cast<biod::ScanResult>(scan_result), matches);
}
}
void SessionFailedReceived(dbus::Signal* signal) {
for (auto& observer : observers_)
observer.BiodSessionFailedReceived();
}
dbus::Bus* bus_ = nullptr;
dbus::ObjectProxy* biod_proxy_ = nullptr;
base::ObserverList<Observer>::Unchecked observers_;
std::unique_ptr<dbus::ObjectPath> current_enroll_session_path_;
std::unique_ptr<dbus::ObjectPath> current_auth_session_path_;
// Note: This should remain the last member so it'll be destroyed and
// invalidate its weak pointers before any other members are destroyed.
base::WeakPtrFactory<BiodClientImpl> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(BiodClientImpl);
};
BiodClient::BiodClient() = default;
BiodClient::~BiodClient() = default;
// static
BiodClient* BiodClient::Create(DBusClientImplementationType type) {
if (type == REAL_DBUS_CLIENT_IMPLEMENTATION)
return new BiodClientImpl();
DCHECK_EQ(FAKE_DBUS_CLIENT_IMPLEMENTATION, type);
return new FakeBiodClient();
}
} // namespace chromeos