• 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 <memory>
8 #include <utility>
9 #include <vector>
10 
11 #include <base/bind.h>
12 #include <base/bind_helpers.h>
13 #include <base/logging.h>
14 #include <brillo/dbus/async_event_sequencer.h>
15 #include <brillo/dbus/exported_object_manager.h>
16 #include <brillo/dbus/exported_property_set.h>
17 #include <dbus/property.h>
18 
19 namespace brillo {
20 namespace dbus_utils {
21 
22 namespace {
23 
SetupDefaultPropertyHandlers(DBusInterface * prop_interface,ExportedPropertySet * property_set)24 void SetupDefaultPropertyHandlers(DBusInterface* prop_interface,
25                                   ExportedPropertySet* property_set) {
26   prop_interface->AddSimpleMethodHandler(dbus::kPropertiesGetAll,
27                                          base::Unretained(property_set),
28                                          &ExportedPropertySet::HandleGetAll);
29   prop_interface->AddSimpleMethodHandlerWithError(
30       dbus::kPropertiesGet, base::Unretained(property_set),
31       &ExportedPropertySet::HandleGet);
32   prop_interface->AddSimpleMethodHandlerWithError(
33       dbus::kPropertiesSet, base::Unretained(property_set),
34       &ExportedPropertySet::HandleSet);
35 }
36 
37 }  // namespace
38 
39 //////////////////////////////////////////////////////////////////////////////
40 
DBusInterface(DBusObject * dbus_object,const std::string & interface_name)41 DBusInterface::DBusInterface(DBusObject* dbus_object,
42                              const std::string& interface_name)
43     : dbus_object_(dbus_object),
44       interface_name_(interface_name),
45       release_interface_cb_(base::DoNothing()) {}
46 
AddProperty(const std::string & property_name,ExportedPropertyBase * prop_base)47 void DBusInterface::AddProperty(const std::string& property_name,
48                                 ExportedPropertyBase* prop_base) {
49   dbus_object_->property_set_.RegisterProperty(
50       interface_name_, property_name, prop_base);
51 }
52 
RemoveProperty(const std::string & property_name)53 void DBusInterface::RemoveProperty(const std::string& property_name) {
54   dbus_object_->property_set_.UnregisterProperty(interface_name_,
55                                                  property_name);
56 }
57 
ExportAsync(ExportedObjectManager * object_manager,dbus::Bus *,dbus::ExportedObject * exported_object,const dbus::ObjectPath & object_path,const AsyncEventSequencer::CompletionAction & completion_callback)58 void DBusInterface::ExportAsync(
59     ExportedObjectManager* object_manager,
60     dbus::Bus* /* bus */,
61     dbus::ExportedObject* exported_object,
62     const dbus::ObjectPath& object_path,
63     const AsyncEventSequencer::CompletionAction& completion_callback) {
64   VLOG(1) << "Registering D-Bus interface '" << interface_name_ << "' for '"
65           << object_path.value() << "'";
66   scoped_refptr<AsyncEventSequencer> sequencer(new AsyncEventSequencer());
67   for (const auto& pair : handlers_) {
68     std::string method_name = pair.first;
69     VLOG(1) << "Exporting method: " << interface_name_ << "." << method_name;
70     std::string export_error = "Failed exporting " + method_name + " method";
71     auto export_handler = sequencer->GetExportHandler(
72         interface_name_, method_name, export_error, true);
73     auto method_handler =
74         base::Bind(&DBusInterface::HandleMethodCall, base::Unretained(this));
75     exported_object->ExportMethod(
76         interface_name_, method_name, method_handler, export_handler);
77   }
78 
79   std::vector<AsyncEventSequencer::CompletionAction> actions;
80   if (object_manager) {
81     auto property_writer_callback =
82         dbus_object_->property_set_.GetPropertyWriter(interface_name_);
83     actions.push_back(
84         base::Bind(&DBusInterface::ClaimInterface,
85                    weak_factory_.GetWeakPtr(),
86                    object_manager->AsWeakPtr(),
87                    object_path,
88                    property_writer_callback));
89   }
90   actions.push_back(completion_callback);
91   sequencer->OnAllTasksCompletedCall(actions);
92 }
93 
ExportAndBlock(ExportedObjectManager * object_manager,dbus::Bus *,dbus::ExportedObject * exported_object,const dbus::ObjectPath & object_path)94 void DBusInterface::ExportAndBlock(
95     ExportedObjectManager* object_manager,
96     dbus::Bus* /* bus */,
97     dbus::ExportedObject* exported_object,
98     const dbus::ObjectPath& object_path) {
99   VLOG(1) << "Registering D-Bus interface '" << interface_name_ << "' for '"
100           << object_path.value() << "'";
101   for (const auto& pair : handlers_) {
102     std::string method_name = pair.first;
103     VLOG(1) << "Exporting method: " << interface_name_ << "." << method_name;
104     auto method_handler =
105         base::Bind(&DBusInterface::HandleMethodCall, base::Unretained(this));
106     if (!exported_object->ExportMethodAndBlock(
107             interface_name_, method_name, method_handler)) {
108         LOG(FATAL) << "Failed exporting " << method_name << " method";
109     }
110   }
111 
112   if (object_manager) {
113     auto property_writer_callback =
114         dbus_object_->property_set_.GetPropertyWriter(interface_name_);
115     ClaimInterface(object_manager->AsWeakPtr(),
116                    object_path,
117                    property_writer_callback,
118                    true);
119   }
120 }
121 
UnexportAsync(ExportedObjectManager * object_manager,dbus::ExportedObject * exported_object,const dbus::ObjectPath & object_path,const AsyncEventSequencer::CompletionAction & completion_callback)122 void DBusInterface::UnexportAsync(
123     ExportedObjectManager* object_manager,
124     dbus::ExportedObject* exported_object,
125     const dbus::ObjectPath& object_path,
126     const AsyncEventSequencer::CompletionAction& completion_callback) {
127   VLOG(1) << "Unexporting D-Bus interface " << interface_name_ << " for "
128           << object_path.value();
129 
130   // Release the interface.
131   release_interface_cb_.RunAndReset();
132 
133   // Unexport all method handlers.
134   scoped_refptr<AsyncEventSequencer> sequencer(new AsyncEventSequencer());
135   for (const auto& pair : handlers_) {
136     std::string method_name = pair.first;
137     VLOG(1) << "Unexporting method: " << interface_name_ << "." << method_name;
138     std::string export_error = "Failed unexporting " + method_name + " method";
139     auto export_handler = sequencer->GetExportHandler(
140         interface_name_, method_name, export_error, true);
141     exported_object->UnexportMethod(interface_name_, method_name,
142                                     export_handler);
143   }
144 
145   sequencer->OnAllTasksCompletedCall({completion_callback});
146 }
147 
UnexportAndBlock(ExportedObjectManager * object_manager,dbus::ExportedObject * exported_object,const dbus::ObjectPath & object_path)148 void DBusInterface::UnexportAndBlock(ExportedObjectManager* object_manager,
149                                      dbus::ExportedObject* exported_object,
150                                      const dbus::ObjectPath& object_path) {
151   VLOG(1) << "Unexporting D-Bus interface " << interface_name_ << " for "
152           << object_path.value();
153 
154   // Release the interface.
155   release_interface_cb_.RunAndReset();
156 
157   // Unexport all method handlers.
158   for (const auto& pair : handlers_) {
159     std::string method_name = pair.first;
160     VLOG(1) << "Unexporting method: " << interface_name_ << "." << method_name;
161     if (!exported_object->UnexportMethodAndBlock(interface_name_, method_name))
162       LOG(FATAL) << "Failed unexporting " << method_name << " method";
163   }
164 }
165 
ClaimInterface(base::WeakPtr<ExportedObjectManager> object_manager,const dbus::ObjectPath & object_path,const ExportedPropertySet::PropertyWriter & writer,bool all_succeeded)166 void DBusInterface::ClaimInterface(
167       base::WeakPtr<ExportedObjectManager> object_manager,
168       const dbus::ObjectPath& object_path,
169       const ExportedPropertySet::PropertyWriter& writer,
170       bool all_succeeded) {
171   if (!all_succeeded || !object_manager) {
172     LOG(ERROR) << "Skipping claiming interface: " << interface_name_;
173     return;
174   }
175   object_manager->ClaimInterface(object_path, interface_name_, writer);
176   release_interface_cb_.RunAndReset();
177   release_interface_cb_.ReplaceClosure(
178       base::Bind(&ExportedObjectManager::ReleaseInterface,
179                  object_manager, object_path, interface_name_));
180 }
181 
HandleMethodCall(dbus::MethodCall * method_call,ResponseSender sender)182 void DBusInterface::HandleMethodCall(dbus::MethodCall* method_call,
183                                      ResponseSender sender) {
184   std::string method_name = method_call->GetMember();
185   // Make a local copy of |interface_name_| because calling HandleMethod()
186   // can potentially kill this interface object...
187   std::string interface_name = interface_name_;
188   VLOG(1) << "Received method call request: " << interface_name << "."
189           << method_name << "(" << method_call->GetSignature() << ")";
190   auto pair = handlers_.find(method_name);
191   if (pair == handlers_.end()) {
192     auto response =
193         dbus::ErrorResponse::FromMethodCall(method_call,
194                                             DBUS_ERROR_UNKNOWN_METHOD,
195                                             "Unknown method: " + method_name);
196     sender.Run(std::move(response));
197     return;
198   }
199   VLOG(1) << "Dispatching DBus method call: " << method_name;
200   pair->second->HandleMethod(method_call, sender);
201 }
202 
AddHandlerImpl(const std::string & method_name,std::unique_ptr<DBusInterfaceMethodHandlerInterface> handler)203 void DBusInterface::AddHandlerImpl(
204     const std::string& method_name,
205     std::unique_ptr<DBusInterfaceMethodHandlerInterface> handler) {
206   VLOG(1) << "Declaring method handler: " << interface_name_ << "."
207           << method_name;
208   auto res = handlers_.insert(std::make_pair(method_name, std::move(handler)));
209   CHECK(res.second) << "Method '" << method_name << "' already exists";
210 }
211 
AddSignalImpl(const std::string & signal_name,const std::shared_ptr<DBusSignalBase> & signal)212 void DBusInterface::AddSignalImpl(
213     const std::string& signal_name,
214     const std::shared_ptr<DBusSignalBase>& signal) {
215   VLOG(1) << "Declaring a signal sink: " << interface_name_ << "."
216           << signal_name;
217   CHECK(signals_.insert(std::make_pair(signal_name, signal)).second)
218       << "The signal '" << signal_name << "' is already registered";
219 }
220 
221 ///////////////////////////////////////////////////////////////////////////////
222 
DBusObject(ExportedObjectManager * object_manager,const scoped_refptr<dbus::Bus> & bus,const dbus::ObjectPath & object_path)223 DBusObject::DBusObject(ExportedObjectManager* object_manager,
224                        const scoped_refptr<dbus::Bus>& bus,
225                        const dbus::ObjectPath& object_path)
226     : DBusObject::DBusObject(object_manager,
227                              bus,
228                              object_path,
229                              base::Bind(&SetupDefaultPropertyHandlers)) {}
230 
DBusObject(ExportedObjectManager * object_manager,const scoped_refptr<dbus::Bus> & bus,const dbus::ObjectPath & object_path,PropertyHandlerSetupCallback property_handler_setup_callback)231 DBusObject::DBusObject(
232     ExportedObjectManager* object_manager,
233     const scoped_refptr<dbus::Bus>& bus,
234     const dbus::ObjectPath& object_path,
235     PropertyHandlerSetupCallback property_handler_setup_callback)
236     : property_set_(bus.get()),
237       bus_(bus),
238       object_path_(object_path),
239       property_handler_setup_callback_(
240           std::move(property_handler_setup_callback)) {
241   if (object_manager)
242     object_manager_ = object_manager->AsWeakPtr();
243 }
244 
~DBusObject()245 DBusObject::~DBusObject() {
246   if (exported_object_)
247     exported_object_->Unregister();
248 }
249 
AddOrGetInterface(const std::string & interface_name)250 DBusInterface* DBusObject::AddOrGetInterface(
251     const std::string& interface_name) {
252   auto iter = interfaces_.find(interface_name);
253   if (iter == interfaces_.end()) {
254     VLOG(1) << "Adding an interface '" << interface_name << "' to object '"
255             << object_path_.value() << "'.";
256     // Interface doesn't exist yet. Create one...
257     std::unique_ptr<DBusInterface> new_itf(
258         new DBusInterface(this, interface_name));
259     iter = interfaces_.insert(std::make_pair(interface_name,
260                                              std::move(new_itf))).first;
261   }
262   return iter->second.get();
263 }
264 
FindInterface(const std::string & interface_name) const265 DBusInterface* DBusObject::FindInterface(
266     const std::string& interface_name) const {
267   auto itf_iter = interfaces_.find(interface_name);
268   return (itf_iter == interfaces_.end()) ? nullptr : itf_iter->second.get();
269 }
270 
RemoveInterface(const std::string & interface_name)271 void DBusObject::RemoveInterface(const std::string& interface_name) {
272   auto itf_iter = interfaces_.find(interface_name);
273   CHECK(itf_iter != interfaces_.end())
274       << "Interface " << interface_name << " has not been added.";
275   interfaces_.erase(itf_iter);
276 }
277 
ExportInterfaceAsync(const std::string & interface_name,const AsyncEventSequencer::CompletionAction & completion_callback)278 void DBusObject::ExportInterfaceAsync(
279     const std::string& interface_name,
280     const AsyncEventSequencer::CompletionAction& completion_callback) {
281   AddOrGetInterface(interface_name)
282       ->ExportAsync(object_manager_.get(), bus_.get(), exported_object_,
283                     object_path_, completion_callback);
284 }
285 
ExportInterfaceAndBlock(const std::string & interface_name)286 void DBusObject::ExportInterfaceAndBlock(const std::string& interface_name) {
287   AddOrGetInterface(interface_name)
288       ->ExportAndBlock(object_manager_.get(), bus_.get(), exported_object_,
289                        object_path_);
290 }
291 
UnexportInterfaceAsync(const std::string & interface_name,const AsyncEventSequencer::CompletionAction & completion_callback)292 void DBusObject::UnexportInterfaceAsync(
293     const std::string& interface_name,
294     const AsyncEventSequencer::CompletionAction& completion_callback) {
295   AddOrGetInterface(interface_name)
296       ->UnexportAsync(object_manager_.get(), exported_object_, object_path_,
297                       completion_callback);
298 }
299 
UnexportInterfaceAndBlock(const std::string & interface_name)300 void DBusObject::UnexportInterfaceAndBlock(const std::string& interface_name) {
301   AddOrGetInterface(interface_name)
302       ->UnexportAndBlock(object_manager_.get(), exported_object_, object_path_);
303 }
304 
RegisterAsync(const AsyncEventSequencer::CompletionAction & completion_callback)305 void DBusObject::RegisterAsync(
306     const AsyncEventSequencer::CompletionAction& completion_callback) {
307   VLOG(1) << "Registering D-Bus object '" << object_path_.value() << "'.";
308   CHECK(exported_object_ == nullptr) << "Object already registered.";
309   scoped_refptr<AsyncEventSequencer> sequencer(new AsyncEventSequencer());
310   exported_object_ = bus_->GetExportedObject(object_path_);
311 
312   RegisterPropertiesInterface();
313 
314   // Export interface methods
315   for (const auto& pair : interfaces_) {
316     pair.second->ExportAsync(
317         object_manager_.get(),
318         bus_.get(),
319         exported_object_,
320         object_path_,
321         sequencer->GetHandler("Failed to export interface " + pair.first,
322                               false));
323   }
324 
325   sequencer->OnAllTasksCompletedCall({completion_callback});
326 }
327 
RegisterAndBlock()328 void DBusObject::RegisterAndBlock() {
329   VLOG(1) << "Registering D-Bus object '" << object_path_.value() << "'.";
330   CHECK(exported_object_ == nullptr) << "Object already registered.";
331   exported_object_ = bus_->GetExportedObject(object_path_);
332 
333   RegisterPropertiesInterface();
334 
335   // Export interface methods
336   for (const auto& pair : interfaces_) {
337     pair.second->ExportAndBlock(
338         object_manager_.get(),
339         bus_.get(),
340         exported_object_,
341         object_path_);
342   }
343 }
344 
UnregisterAsync()345 void DBusObject::UnregisterAsync() {
346   VLOG(1) << "Unregistering D-Bus object '" << object_path_.value() << "'.";
347   CHECK(exported_object_ != nullptr) << "Object not registered.";
348 
349   // This will unregister the object path from the bus.
350   exported_object_->Unregister();
351   // This will remove |exported_object_| from bus's object table. This function
352   // will also post a task to unregister |exported_object_| (same as the call
353   // above), which will be a no-op since it is already done by then.
354   // By doing both in here, the object path is guarantee to be reusable upon
355   // return from this function.
356   bus_->UnregisterExportedObject(object_path_);
357   exported_object_ = nullptr;
358 }
359 
SendSignal(dbus::Signal * signal)360 bool DBusObject::SendSignal(dbus::Signal* signal) {
361   if (exported_object_) {
362     exported_object_->SendSignal(signal);
363     return true;
364   }
365   LOG(ERROR) << "Trying to send a signal from an object that is not exported";
366   return false;
367 }
368 
RegisterPropertiesInterface()369 void DBusObject::RegisterPropertiesInterface() {
370   DBusInterface* prop_interface = AddOrGetInterface(dbus::kPropertiesInterface);
371   property_handler_setup_callback_.Run(prop_interface, &property_set_);
372   property_set_.OnPropertiesInterfaceExported(prop_interface);
373 }
374 
375 }  // namespace dbus_utils
376 }  // namespace brillo
377