You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
377 lines
14 KiB
377 lines
14 KiB
// Copyright 2014 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 <brillo/dbus/dbus_object.h>
|
|
|
|
#include <memory>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include <base/bind.h>
|
|
#include <base/bind_helpers.h>
|
|
#include <base/logging.h>
|
|
#include <brillo/dbus/async_event_sequencer.h>
|
|
#include <brillo/dbus/exported_object_manager.h>
|
|
#include <brillo/dbus/exported_property_set.h>
|
|
#include <dbus/property.h>
|
|
|
|
namespace brillo {
|
|
namespace dbus_utils {
|
|
|
|
namespace {
|
|
|
|
void SetupDefaultPropertyHandlers(DBusInterface* prop_interface,
|
|
ExportedPropertySet* property_set) {
|
|
prop_interface->AddSimpleMethodHandler(dbus::kPropertiesGetAll,
|
|
base::Unretained(property_set),
|
|
&ExportedPropertySet::HandleGetAll);
|
|
prop_interface->AddSimpleMethodHandlerWithError(
|
|
dbus::kPropertiesGet, base::Unretained(property_set),
|
|
&ExportedPropertySet::HandleGet);
|
|
prop_interface->AddSimpleMethodHandlerWithError(
|
|
dbus::kPropertiesSet, base::Unretained(property_set),
|
|
&ExportedPropertySet::HandleSet);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
DBusInterface::DBusInterface(DBusObject* dbus_object,
|
|
const std::string& interface_name)
|
|
: dbus_object_(dbus_object),
|
|
interface_name_(interface_name),
|
|
release_interface_cb_(base::DoNothing()) {}
|
|
|
|
void DBusInterface::AddProperty(const std::string& property_name,
|
|
ExportedPropertyBase* prop_base) {
|
|
dbus_object_->property_set_.RegisterProperty(
|
|
interface_name_, property_name, prop_base);
|
|
}
|
|
|
|
void DBusInterface::RemoveProperty(const std::string& property_name) {
|
|
dbus_object_->property_set_.UnregisterProperty(interface_name_,
|
|
property_name);
|
|
}
|
|
|
|
void DBusInterface::ExportAsync(
|
|
ExportedObjectManager* object_manager,
|
|
dbus::Bus* /* bus */,
|
|
dbus::ExportedObject* exported_object,
|
|
const dbus::ObjectPath& object_path,
|
|
const AsyncEventSequencer::CompletionAction& completion_callback) {
|
|
VLOG(1) << "Registering D-Bus interface '" << interface_name_ << "' for '"
|
|
<< object_path.value() << "'";
|
|
scoped_refptr<AsyncEventSequencer> sequencer(new AsyncEventSequencer());
|
|
for (const auto& pair : handlers_) {
|
|
std::string method_name = pair.first;
|
|
VLOG(1) << "Exporting method: " << interface_name_ << "." << method_name;
|
|
std::string export_error = "Failed exporting " + method_name + " method";
|
|
auto export_handler = sequencer->GetExportHandler(
|
|
interface_name_, method_name, export_error, true);
|
|
auto method_handler =
|
|
base::Bind(&DBusInterface::HandleMethodCall, base::Unretained(this));
|
|
exported_object->ExportMethod(
|
|
interface_name_, method_name, method_handler, export_handler);
|
|
}
|
|
|
|
std::vector<AsyncEventSequencer::CompletionAction> actions;
|
|
if (object_manager) {
|
|
auto property_writer_callback =
|
|
dbus_object_->property_set_.GetPropertyWriter(interface_name_);
|
|
actions.push_back(
|
|
base::Bind(&DBusInterface::ClaimInterface,
|
|
weak_factory_.GetWeakPtr(),
|
|
object_manager->AsWeakPtr(),
|
|
object_path,
|
|
property_writer_callback));
|
|
}
|
|
actions.push_back(completion_callback);
|
|
sequencer->OnAllTasksCompletedCall(actions);
|
|
}
|
|
|
|
void DBusInterface::ExportAndBlock(
|
|
ExportedObjectManager* object_manager,
|
|
dbus::Bus* /* bus */,
|
|
dbus::ExportedObject* exported_object,
|
|
const dbus::ObjectPath& object_path) {
|
|
VLOG(1) << "Registering D-Bus interface '" << interface_name_ << "' for '"
|
|
<< object_path.value() << "'";
|
|
for (const auto& pair : handlers_) {
|
|
std::string method_name = pair.first;
|
|
VLOG(1) << "Exporting method: " << interface_name_ << "." << method_name;
|
|
auto method_handler =
|
|
base::Bind(&DBusInterface::HandleMethodCall, base::Unretained(this));
|
|
if (!exported_object->ExportMethodAndBlock(
|
|
interface_name_, method_name, method_handler)) {
|
|
LOG(FATAL) << "Failed exporting " << method_name << " method";
|
|
}
|
|
}
|
|
|
|
if (object_manager) {
|
|
auto property_writer_callback =
|
|
dbus_object_->property_set_.GetPropertyWriter(interface_name_);
|
|
ClaimInterface(object_manager->AsWeakPtr(),
|
|
object_path,
|
|
property_writer_callback,
|
|
true);
|
|
}
|
|
}
|
|
|
|
void DBusInterface::UnexportAsync(
|
|
ExportedObjectManager* object_manager,
|
|
dbus::ExportedObject* exported_object,
|
|
const dbus::ObjectPath& object_path,
|
|
const AsyncEventSequencer::CompletionAction& completion_callback) {
|
|
VLOG(1) << "Unexporting D-Bus interface " << interface_name_ << " for "
|
|
<< object_path.value();
|
|
|
|
// Release the interface.
|
|
release_interface_cb_.RunAndReset();
|
|
|
|
// Unexport all method handlers.
|
|
scoped_refptr<AsyncEventSequencer> sequencer(new AsyncEventSequencer());
|
|
for (const auto& pair : handlers_) {
|
|
std::string method_name = pair.first;
|
|
VLOG(1) << "Unexporting method: " << interface_name_ << "." << method_name;
|
|
std::string export_error = "Failed unexporting " + method_name + " method";
|
|
auto export_handler = sequencer->GetExportHandler(
|
|
interface_name_, method_name, export_error, true);
|
|
exported_object->UnexportMethod(interface_name_, method_name,
|
|
export_handler);
|
|
}
|
|
|
|
sequencer->OnAllTasksCompletedCall({completion_callback});
|
|
}
|
|
|
|
void DBusInterface::UnexportAndBlock(ExportedObjectManager* object_manager,
|
|
dbus::ExportedObject* exported_object,
|
|
const dbus::ObjectPath& object_path) {
|
|
VLOG(1) << "Unexporting D-Bus interface " << interface_name_ << " for "
|
|
<< object_path.value();
|
|
|
|
// Release the interface.
|
|
release_interface_cb_.RunAndReset();
|
|
|
|
// Unexport all method handlers.
|
|
for (const auto& pair : handlers_) {
|
|
std::string method_name = pair.first;
|
|
VLOG(1) << "Unexporting method: " << interface_name_ << "." << method_name;
|
|
if (!exported_object->UnexportMethodAndBlock(interface_name_, method_name))
|
|
LOG(FATAL) << "Failed unexporting " << method_name << " method";
|
|
}
|
|
}
|
|
|
|
void DBusInterface::ClaimInterface(
|
|
base::WeakPtr<ExportedObjectManager> object_manager,
|
|
const dbus::ObjectPath& object_path,
|
|
const ExportedPropertySet::PropertyWriter& writer,
|
|
bool all_succeeded) {
|
|
if (!all_succeeded || !object_manager) {
|
|
LOG(ERROR) << "Skipping claiming interface: " << interface_name_;
|
|
return;
|
|
}
|
|
object_manager->ClaimInterface(object_path, interface_name_, writer);
|
|
release_interface_cb_.RunAndReset();
|
|
release_interface_cb_.ReplaceClosure(
|
|
base::Bind(&ExportedObjectManager::ReleaseInterface,
|
|
object_manager, object_path, interface_name_));
|
|
}
|
|
|
|
void DBusInterface::HandleMethodCall(dbus::MethodCall* method_call,
|
|
ResponseSender sender) {
|
|
std::string method_name = method_call->GetMember();
|
|
// Make a local copy of |interface_name_| because calling HandleMethod()
|
|
// can potentially kill this interface object...
|
|
std::string interface_name = interface_name_;
|
|
VLOG(1) << "Received method call request: " << interface_name << "."
|
|
<< method_name << "(" << method_call->GetSignature() << ")";
|
|
auto pair = handlers_.find(method_name);
|
|
if (pair == handlers_.end()) {
|
|
auto response =
|
|
dbus::ErrorResponse::FromMethodCall(method_call,
|
|
DBUS_ERROR_UNKNOWN_METHOD,
|
|
"Unknown method: " + method_name);
|
|
sender.Run(std::move(response));
|
|
return;
|
|
}
|
|
VLOG(1) << "Dispatching DBus method call: " << method_name;
|
|
pair->second->HandleMethod(method_call, sender);
|
|
}
|
|
|
|
void DBusInterface::AddHandlerImpl(
|
|
const std::string& method_name,
|
|
std::unique_ptr<DBusInterfaceMethodHandlerInterface> handler) {
|
|
VLOG(1) << "Declaring method handler: " << interface_name_ << "."
|
|
<< method_name;
|
|
auto res = handlers_.insert(std::make_pair(method_name, std::move(handler)));
|
|
CHECK(res.second) << "Method '" << method_name << "' already exists";
|
|
}
|
|
|
|
void DBusInterface::AddSignalImpl(
|
|
const std::string& signal_name,
|
|
const std::shared_ptr<DBusSignalBase>& signal) {
|
|
VLOG(1) << "Declaring a signal sink: " << interface_name_ << "."
|
|
<< signal_name;
|
|
CHECK(signals_.insert(std::make_pair(signal_name, signal)).second)
|
|
<< "The signal '" << signal_name << "' is already registered";
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
DBusObject::DBusObject(ExportedObjectManager* object_manager,
|
|
const scoped_refptr<dbus::Bus>& bus,
|
|
const dbus::ObjectPath& object_path)
|
|
: DBusObject::DBusObject(object_manager,
|
|
bus,
|
|
object_path,
|
|
base::Bind(&SetupDefaultPropertyHandlers)) {}
|
|
|
|
DBusObject::DBusObject(
|
|
ExportedObjectManager* object_manager,
|
|
const scoped_refptr<dbus::Bus>& bus,
|
|
const dbus::ObjectPath& object_path,
|
|
PropertyHandlerSetupCallback property_handler_setup_callback)
|
|
: property_set_(bus.get()),
|
|
bus_(bus),
|
|
object_path_(object_path),
|
|
property_handler_setup_callback_(
|
|
std::move(property_handler_setup_callback)) {
|
|
if (object_manager)
|
|
object_manager_ = object_manager->AsWeakPtr();
|
|
}
|
|
|
|
DBusObject::~DBusObject() {
|
|
if (exported_object_)
|
|
exported_object_->Unregister();
|
|
}
|
|
|
|
DBusInterface* DBusObject::AddOrGetInterface(
|
|
const std::string& interface_name) {
|
|
auto iter = interfaces_.find(interface_name);
|
|
if (iter == interfaces_.end()) {
|
|
VLOG(1) << "Adding an interface '" << interface_name << "' to object '"
|
|
<< object_path_.value() << "'.";
|
|
// Interface doesn't exist yet. Create one...
|
|
std::unique_ptr<DBusInterface> new_itf(
|
|
new DBusInterface(this, interface_name));
|
|
iter = interfaces_.insert(std::make_pair(interface_name,
|
|
std::move(new_itf))).first;
|
|
}
|
|
return iter->second.get();
|
|
}
|
|
|
|
DBusInterface* DBusObject::FindInterface(
|
|
const std::string& interface_name) const {
|
|
auto itf_iter = interfaces_.find(interface_name);
|
|
return (itf_iter == interfaces_.end()) ? nullptr : itf_iter->second.get();
|
|
}
|
|
|
|
void DBusObject::RemoveInterface(const std::string& interface_name) {
|
|
auto itf_iter = interfaces_.find(interface_name);
|
|
CHECK(itf_iter != interfaces_.end())
|
|
<< "Interface " << interface_name << " has not been added.";
|
|
interfaces_.erase(itf_iter);
|
|
}
|
|
|
|
void DBusObject::ExportInterfaceAsync(
|
|
const std::string& interface_name,
|
|
const AsyncEventSequencer::CompletionAction& completion_callback) {
|
|
AddOrGetInterface(interface_name)
|
|
->ExportAsync(object_manager_.get(), bus_.get(), exported_object_,
|
|
object_path_, completion_callback);
|
|
}
|
|
|
|
void DBusObject::ExportInterfaceAndBlock(const std::string& interface_name) {
|
|
AddOrGetInterface(interface_name)
|
|
->ExportAndBlock(object_manager_.get(), bus_.get(), exported_object_,
|
|
object_path_);
|
|
}
|
|
|
|
void DBusObject::UnexportInterfaceAsync(
|
|
const std::string& interface_name,
|
|
const AsyncEventSequencer::CompletionAction& completion_callback) {
|
|
AddOrGetInterface(interface_name)
|
|
->UnexportAsync(object_manager_.get(), exported_object_, object_path_,
|
|
completion_callback);
|
|
}
|
|
|
|
void DBusObject::UnexportInterfaceAndBlock(const std::string& interface_name) {
|
|
AddOrGetInterface(interface_name)
|
|
->UnexportAndBlock(object_manager_.get(), exported_object_, object_path_);
|
|
}
|
|
|
|
void DBusObject::RegisterAsync(
|
|
const AsyncEventSequencer::CompletionAction& completion_callback) {
|
|
VLOG(1) << "Registering D-Bus object '" << object_path_.value() << "'.";
|
|
CHECK(exported_object_ == nullptr) << "Object already registered.";
|
|
scoped_refptr<AsyncEventSequencer> sequencer(new AsyncEventSequencer());
|
|
exported_object_ = bus_->GetExportedObject(object_path_);
|
|
|
|
RegisterPropertiesInterface();
|
|
|
|
// Export interface methods
|
|
for (const auto& pair : interfaces_) {
|
|
pair.second->ExportAsync(
|
|
object_manager_.get(),
|
|
bus_.get(),
|
|
exported_object_,
|
|
object_path_,
|
|
sequencer->GetHandler("Failed to export interface " + pair.first,
|
|
false));
|
|
}
|
|
|
|
sequencer->OnAllTasksCompletedCall({completion_callback});
|
|
}
|
|
|
|
void DBusObject::RegisterAndBlock() {
|
|
VLOG(1) << "Registering D-Bus object '" << object_path_.value() << "'.";
|
|
CHECK(exported_object_ == nullptr) << "Object already registered.";
|
|
exported_object_ = bus_->GetExportedObject(object_path_);
|
|
|
|
RegisterPropertiesInterface();
|
|
|
|
// Export interface methods
|
|
for (const auto& pair : interfaces_) {
|
|
pair.second->ExportAndBlock(
|
|
object_manager_.get(),
|
|
bus_.get(),
|
|
exported_object_,
|
|
object_path_);
|
|
}
|
|
}
|
|
|
|
void DBusObject::UnregisterAsync() {
|
|
VLOG(1) << "Unregistering D-Bus object '" << object_path_.value() << "'.";
|
|
CHECK(exported_object_ != nullptr) << "Object not registered.";
|
|
|
|
// This will unregister the object path from the bus.
|
|
exported_object_->Unregister();
|
|
// This will remove |exported_object_| from bus's object table. This function
|
|
// will also post a task to unregister |exported_object_| (same as the call
|
|
// above), which will be a no-op since it is already done by then.
|
|
// By doing both in here, the object path is guarantee to be reusable upon
|
|
// return from this function.
|
|
bus_->UnregisterExportedObject(object_path_);
|
|
exported_object_ = nullptr;
|
|
}
|
|
|
|
bool DBusObject::SendSignal(dbus::Signal* signal) {
|
|
if (exported_object_) {
|
|
exported_object_->SendSignal(signal);
|
|
return true;
|
|
}
|
|
LOG(ERROR) << "Trying to send a signal from an object that is not exported";
|
|
return false;
|
|
}
|
|
|
|
void DBusObject::RegisterPropertiesInterface() {
|
|
DBusInterface* prop_interface = AddOrGetInterface(dbus::kPropertiesInterface);
|
|
property_handler_setup_callback_.Run(prop_interface, &property_set_);
|
|
property_set_.OnPropertiesInterfaceExported(prop_interface);
|
|
}
|
|
|
|
} // namespace dbus_utils
|
|
} // namespace brillo
|