// 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 <vector> #include <base/bind.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) { } 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::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_.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::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