Home | History | Annotate | Download | only in dbus
      1 // Copyright 2014 The Chromium OS Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include <brillo/dbus/dbus_object.h>
      6 
      7 #include <vector>
      8 
      9 #include <base/bind.h>
     10 #include <base/logging.h>
     11 #include <brillo/dbus/async_event_sequencer.h>
     12 #include <brillo/dbus/exported_object_manager.h>
     13 #include <brillo/dbus/exported_property_set.h>
     14 #include <dbus/property.h>
     15 
     16 namespace brillo {
     17 namespace dbus_utils {
     18 
     19 //////////////////////////////////////////////////////////////////////////////
     20 
     21 DBusInterface::DBusInterface(DBusObject* dbus_object,
     22                              const std::string& interface_name)
     23     : dbus_object_(dbus_object), interface_name_(interface_name) {
     24 }
     25 
     26 void DBusInterface::AddProperty(const std::string& property_name,
     27                                 ExportedPropertyBase* prop_base) {
     28   dbus_object_->property_set_.RegisterProperty(
     29       interface_name_, property_name, prop_base);
     30 }
     31 
     32 void DBusInterface::ExportAsync(
     33     ExportedObjectManager* object_manager,
     34     dbus::Bus* /* bus */,
     35     dbus::ExportedObject* exported_object,
     36     const dbus::ObjectPath& object_path,
     37     const AsyncEventSequencer::CompletionAction& completion_callback) {
     38   VLOG(1) << "Registering D-Bus interface '" << interface_name_ << "' for '"
     39           << object_path.value() << "'";
     40   scoped_refptr<AsyncEventSequencer> sequencer(new AsyncEventSequencer());
     41   for (const auto& pair : handlers_) {
     42     std::string method_name = pair.first;
     43     VLOG(1) << "Exporting method: " << interface_name_ << "." << method_name;
     44     std::string export_error = "Failed exporting " + method_name + " method";
     45     auto export_handler = sequencer->GetExportHandler(
     46         interface_name_, method_name, export_error, true);
     47     auto method_handler =
     48         base::Bind(&DBusInterface::HandleMethodCall, base::Unretained(this));
     49     exported_object->ExportMethod(
     50         interface_name_, method_name, method_handler, export_handler);
     51   }
     52 
     53   std::vector<AsyncEventSequencer::CompletionAction> actions;
     54   if (object_manager) {
     55     auto property_writer_callback =
     56         dbus_object_->property_set_.GetPropertyWriter(interface_name_);
     57     actions.push_back(
     58         base::Bind(&DBusInterface::ClaimInterface,
     59                    weak_factory_.GetWeakPtr(),
     60                    object_manager->AsWeakPtr(),
     61                    object_path,
     62                    property_writer_callback));
     63   }
     64   actions.push_back(completion_callback);
     65   sequencer->OnAllTasksCompletedCall(actions);
     66 }
     67 
     68 void DBusInterface::ExportAndBlock(
     69     ExportedObjectManager* object_manager,
     70     dbus::Bus* /* bus */,
     71     dbus::ExportedObject* exported_object,
     72     const dbus::ObjectPath& object_path) {
     73   VLOG(1) << "Registering D-Bus interface '" << interface_name_ << "' for '"
     74           << object_path.value() << "'";
     75   for (const auto& pair : handlers_) {
     76     std::string method_name = pair.first;
     77     VLOG(1) << "Exporting method: " << interface_name_ << "." << method_name;
     78     auto method_handler =
     79         base::Bind(&DBusInterface::HandleMethodCall, base::Unretained(this));
     80     if (!exported_object->ExportMethodAndBlock(
     81             interface_name_, method_name, method_handler)) {
     82         LOG(FATAL) << "Failed exporting " << method_name << " method";
     83     }
     84   }
     85 
     86   if (object_manager) {
     87     auto property_writer_callback =
     88         dbus_object_->property_set_.GetPropertyWriter(interface_name_);
     89     ClaimInterface(object_manager->AsWeakPtr(),
     90                    object_path,
     91                    property_writer_callback,
     92                    true);
     93   }
     94 }
     95 
     96 void DBusInterface::ClaimInterface(
     97       base::WeakPtr<ExportedObjectManager> object_manager,
     98       const dbus::ObjectPath& object_path,
     99       const ExportedPropertySet::PropertyWriter& writer,
    100       bool all_succeeded) {
    101   if (!all_succeeded || !object_manager) {
    102     LOG(ERROR) << "Skipping claiming interface: " << interface_name_;
    103     return;
    104   }
    105   object_manager->ClaimInterface(object_path, interface_name_, writer);
    106   release_interface_cb_.ReplaceClosure(
    107       base::Bind(&ExportedObjectManager::ReleaseInterface,
    108                  object_manager, object_path, interface_name_));
    109 }
    110 
    111 void DBusInterface::HandleMethodCall(dbus::MethodCall* method_call,
    112                                      ResponseSender sender) {
    113   std::string method_name = method_call->GetMember();
    114   // Make a local copy of |interface_name_| because calling HandleMethod()
    115   // can potentially kill this interface object...
    116   std::string interface_name = interface_name_;
    117   VLOG(1) << "Received method call request: " << interface_name << "."
    118           << method_name << "(" << method_call->GetSignature() << ")";
    119   auto pair = handlers_.find(method_name);
    120   if (pair == handlers_.end()) {
    121     auto response =
    122         dbus::ErrorResponse::FromMethodCall(method_call,
    123                                             DBUS_ERROR_UNKNOWN_METHOD,
    124                                             "Unknown method: " + method_name);
    125     sender.Run(std::move(response));
    126     return;
    127   }
    128   VLOG(1) << "Dispatching DBus method call: " << method_name;
    129   pair->second->HandleMethod(method_call, sender);
    130 }
    131 
    132 void DBusInterface::AddHandlerImpl(
    133     const std::string& method_name,
    134     std::unique_ptr<DBusInterfaceMethodHandlerInterface> handler) {
    135   VLOG(1) << "Declaring method handler: " << interface_name_ << "."
    136           << method_name;
    137   auto res = handlers_.insert(std::make_pair(method_name, std::move(handler)));
    138   CHECK(res.second) << "Method '" << method_name << "' already exists";
    139 }
    140 
    141 void DBusInterface::AddSignalImpl(
    142     const std::string& signal_name,
    143     const std::shared_ptr<DBusSignalBase>& signal) {
    144   VLOG(1) << "Declaring a signal sink: " << interface_name_ << "."
    145           << signal_name;
    146   CHECK(signals_.insert(std::make_pair(signal_name, signal)).second)
    147       << "The signal '" << signal_name << "' is already registered";
    148 }
    149 
    150 ///////////////////////////////////////////////////////////////////////////////
    151 
    152 DBusObject::DBusObject(ExportedObjectManager* object_manager,
    153                        const scoped_refptr<dbus::Bus>& bus,
    154                        const dbus::ObjectPath& object_path)
    155     : property_set_(bus.get()), bus_(bus), object_path_(object_path) {
    156   if (object_manager)
    157     object_manager_ = object_manager->AsWeakPtr();
    158 }
    159 
    160 DBusObject::~DBusObject() {
    161   if (exported_object_)
    162     exported_object_->Unregister();
    163 }
    164 
    165 DBusInterface* DBusObject::AddOrGetInterface(
    166     const std::string& interface_name) {
    167   auto iter = interfaces_.find(interface_name);
    168   if (iter == interfaces_.end()) {
    169     VLOG(1) << "Adding an interface '" << interface_name << "' to object '"
    170             << object_path_.value() << "'.";
    171     // Interface doesn't exist yet. Create one...
    172     std::unique_ptr<DBusInterface> new_itf(
    173         new DBusInterface(this, interface_name));
    174     iter = interfaces_.insert(std::make_pair(interface_name,
    175                                              std::move(new_itf))).first;
    176   }
    177   return iter->second.get();
    178 }
    179 
    180 DBusInterface* DBusObject::FindInterface(
    181     const std::string& interface_name) const {
    182   auto itf_iter = interfaces_.find(interface_name);
    183   return (itf_iter == interfaces_.end()) ? nullptr : itf_iter->second.get();
    184 }
    185 
    186 void DBusObject::RegisterAsync(
    187     const AsyncEventSequencer::CompletionAction& completion_callback) {
    188   VLOG(1) << "Registering D-Bus object '" << object_path_.value() << "'.";
    189   CHECK(exported_object_ == nullptr) << "Object already registered.";
    190   scoped_refptr<AsyncEventSequencer> sequencer(new AsyncEventSequencer());
    191   exported_object_ = bus_->GetExportedObject(object_path_);
    192 
    193   // Add the org.freedesktop.DBus.Properties interface to the object.
    194   DBusInterface* prop_interface = AddOrGetInterface(dbus::kPropertiesInterface);
    195   prop_interface->AddSimpleMethodHandler(
    196       dbus::kPropertiesGetAll,
    197       base::Unretained(&property_set_),
    198       &ExportedPropertySet::HandleGetAll);
    199   prop_interface->AddSimpleMethodHandlerWithError(
    200       dbus::kPropertiesGet,
    201       base::Unretained(&property_set_),
    202       &ExportedPropertySet::HandleGet);
    203   prop_interface->AddSimpleMethodHandlerWithError(
    204       dbus::kPropertiesSet,
    205       base::Unretained(&property_set_),
    206       &ExportedPropertySet::HandleSet);
    207   property_set_.OnPropertiesInterfaceExported(prop_interface);
    208 
    209   // Export interface methods
    210   for (const auto& pair : interfaces_) {
    211     pair.second->ExportAsync(
    212         object_manager_.get(),
    213         bus_.get(),
    214         exported_object_,
    215         object_path_,
    216         sequencer->GetHandler("Failed to export interface " + pair.first,
    217                               false));
    218   }
    219 
    220   sequencer->OnAllTasksCompletedCall({completion_callback});
    221 }
    222 
    223 void DBusObject::RegisterAndBlock() {
    224   VLOG(1) << "Registering D-Bus object '" << object_path_.value() << "'.";
    225   CHECK(exported_object_ == nullptr) << "Object already registered.";
    226   exported_object_ = bus_->GetExportedObject(object_path_);
    227 
    228   // Add the org.freedesktop.DBus.Properties interface to the object.
    229   DBusInterface* prop_interface = AddOrGetInterface(dbus::kPropertiesInterface);
    230   prop_interface->AddSimpleMethodHandler(
    231       dbus::kPropertiesGetAll,
    232       base::Unretained(&property_set_),
    233       &ExportedPropertySet::HandleGetAll);
    234   prop_interface->AddSimpleMethodHandlerWithError(
    235       dbus::kPropertiesGet,
    236       base::Unretained(&property_set_),
    237       &ExportedPropertySet::HandleGet);
    238   prop_interface->AddSimpleMethodHandlerWithError(
    239       dbus::kPropertiesSet,
    240       base::Unretained(&property_set_),
    241       &ExportedPropertySet::HandleSet);
    242   property_set_.OnPropertiesInterfaceExported(prop_interface);
    243 
    244   // Export interface methods
    245   for (const auto& pair : interfaces_) {
    246     pair.second->ExportAndBlock(
    247         object_manager_.get(),
    248         bus_.get(),
    249         exported_object_,
    250         object_path_);
    251   }
    252 }
    253 
    254 void DBusObject::UnregisterAsync() {
    255   VLOG(1) << "Unregistering D-Bus object '" << object_path_.value() << "'.";
    256   CHECK(exported_object_ != nullptr) << "Object not registered.";
    257 
    258   // This will unregister the object path from the bus.
    259   exported_object_->Unregister();
    260   // This will remove |exported_object_| from bus's object table. This function
    261   // will also post a task to unregister |exported_object_| (same as the call
    262   // above), which will be a no-op since it is already done by then.
    263   // By doing both in here, the object path is guarantee to be reusable upon
    264   // return from this function.
    265   bus_->UnregisterExportedObject(object_path_);
    266   exported_object_ = nullptr;
    267 }
    268 
    269 bool DBusObject::SendSignal(dbus::Signal* signal) {
    270   if (exported_object_) {
    271     exported_object_->SendSignal(signal);
    272     return true;
    273   }
    274   LOG(ERROR) << "Trying to send a signal from an object that is not exported";
    275   return false;
    276 }
    277 
    278 }  // namespace dbus_utils
    279 }  // namespace brillo
    280