• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
DBusInterface(DBusObject * dbus_object,const std::string & interface_name)21 DBusInterface::DBusInterface(DBusObject* dbus_object,
22                              const std::string& interface_name)
23     : dbus_object_(dbus_object), interface_name_(interface_name) {
24 }
25 
AddProperty(const std::string & property_name,ExportedPropertyBase * prop_base)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 
ExportAsync(ExportedObjectManager * object_manager,dbus::Bus *,dbus::ExportedObject * exported_object,const dbus::ObjectPath & object_path,const AsyncEventSequencer::CompletionAction & completion_callback)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 
ExportAndBlock(ExportedObjectManager * object_manager,dbus::Bus *,dbus::ExportedObject * exported_object,const dbus::ObjectPath & object_path)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 
ClaimInterface(base::WeakPtr<ExportedObjectManager> object_manager,const dbus::ObjectPath & object_path,const ExportedPropertySet::PropertyWriter & writer,bool all_succeeded)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 
HandleMethodCall(dbus::MethodCall * method_call,ResponseSender sender)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 
AddHandlerImpl(const std::string & method_name,std::unique_ptr<DBusInterfaceMethodHandlerInterface> handler)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 
AddSignalImpl(const std::string & signal_name,const std::shared_ptr<DBusSignalBase> & signal)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 
DBusObject(ExportedObjectManager * object_manager,const scoped_refptr<dbus::Bus> & bus,const dbus::ObjectPath & object_path)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 
~DBusObject()160 DBusObject::~DBusObject() {
161   if (exported_object_)
162     exported_object_->Unregister();
163 }
164 
AddOrGetInterface(const std::string & interface_name)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 
FindInterface(const std::string & interface_name) const180 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 
RegisterAsync(const AsyncEventSequencer::CompletionAction & completion_callback)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 
RegisterAndBlock()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 
UnregisterAsync()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 
SendSignal(dbus::Signal * signal)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