• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2013 The Chromium 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 "dbus/object_manager.h"
6 
7 #include <stddef.h>
8 
9 #include "base/bind.h"
10 #include "base/location.h"
11 #include "base/logging.h"
12 #include "base/metrics/histogram.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/task_runner_util.h"
15 #include "dbus/bus.h"
16 #include "dbus/dbus_statistics.h"
17 #include "dbus/message.h"
18 #include "dbus/object_proxy.h"
19 #include "dbus/property.h"
20 #include "dbus/scoped_dbus_error.h"
21 #include "dbus/util.h"
22 
23 namespace dbus {
24 
Object()25 ObjectManager::Object::Object()
26   : object_proxy(nullptr) {
27 }
28 
29 ObjectManager::Object::~Object() = default;
30 
ObjectManager(Bus * bus,const std::string & service_name,const ObjectPath & object_path)31 ObjectManager::ObjectManager(Bus* bus,
32                              const std::string& service_name,
33                              const ObjectPath& object_path)
34     : bus_(bus),
35       service_name_(service_name),
36       object_path_(object_path),
37       setup_success_(false),
38       cleanup_called_(false),
39       weak_ptr_factory_(this) {
40   LOG_IF(FATAL, !object_path_.IsValid()) << object_path_.value();
41   DVLOG(1) << "Creating ObjectManager for " << service_name_
42            << " " << object_path_.value();
43   DCHECK(bus_);
44   bus_->AssertOnOriginThread();
45   object_proxy_ = bus_->GetObjectProxy(service_name_, object_path_);
46   object_proxy_->SetNameOwnerChangedCallback(
47       base::Bind(&ObjectManager::NameOwnerChanged,
48                  weak_ptr_factory_.GetWeakPtr()));
49 
50   // Set up a match rule and a filter function to handle PropertiesChanged
51   // signals from the service. This is important to avoid any race conditions
52   // that might cause us to miss PropertiesChanged signals once all objects are
53   // initialized via GetManagedObjects.
54   base::PostTaskAndReplyWithResult(
55       bus_->GetDBusTaskRunner(),
56       FROM_HERE,
57       base::Bind(&ObjectManager::SetupMatchRuleAndFilter, this),
58       base::Bind(&ObjectManager::OnSetupMatchRuleAndFilterComplete, this));
59 }
60 
~ObjectManager()61 ObjectManager::~ObjectManager() {
62   // Clean up Object structures
63   for (ObjectMap::iterator iter = object_map_.begin();
64        iter != object_map_.end(); ++iter) {
65     Object* object = iter->second;
66 
67     for (Object::PropertiesMap::iterator piter = object->properties_map.begin();
68          piter != object->properties_map.end(); ++piter) {
69       PropertySet* properties = piter->second;
70       delete properties;
71     }
72 
73     delete object;
74   }
75 }
76 
RegisterInterface(const std::string & interface_name,Interface * interface)77 void ObjectManager::RegisterInterface(const std::string& interface_name,
78                                       Interface* interface) {
79   interface_map_[interface_name] = interface;
80 }
81 
UnregisterInterface(const std::string & interface_name)82 void ObjectManager::UnregisterInterface(const std::string& interface_name) {
83   InterfaceMap::iterator iter = interface_map_.find(interface_name);
84   if (iter != interface_map_.end())
85     interface_map_.erase(iter);
86 }
87 
GetObjects()88 std::vector<ObjectPath> ObjectManager::GetObjects() {
89   std::vector<ObjectPath> object_paths;
90 
91   for (ObjectMap::iterator iter = object_map_.begin();
92        iter != object_map_.end(); ++iter)
93     object_paths.push_back(iter->first);
94 
95   return object_paths;
96 }
97 
GetObjectsWithInterface(const std::string & interface_name)98 std::vector<ObjectPath> ObjectManager::GetObjectsWithInterface(
99       const std::string& interface_name) {
100   std::vector<ObjectPath> object_paths;
101 
102   for (ObjectMap::iterator oiter = object_map_.begin();
103        oiter != object_map_.end(); ++oiter) {
104     Object* object = oiter->second;
105 
106     Object::PropertiesMap::iterator piter =
107         object->properties_map.find(interface_name);
108     if (piter != object->properties_map.end())
109       object_paths.push_back(oiter->first);
110   }
111 
112   return object_paths;
113 }
114 
GetObjectProxy(const ObjectPath & object_path)115 ObjectProxy* ObjectManager::GetObjectProxy(const ObjectPath& object_path) {
116   ObjectMap::iterator iter = object_map_.find(object_path);
117   if (iter == object_map_.end())
118     return nullptr;
119 
120   Object* object = iter->second;
121   return object->object_proxy;
122 }
123 
GetProperties(const ObjectPath & object_path,const std::string & interface_name)124 PropertySet* ObjectManager::GetProperties(const ObjectPath& object_path,
125                                           const std::string& interface_name) {
126   ObjectMap::iterator iter = object_map_.find(object_path);
127   if (iter == object_map_.end())
128     return nullptr;
129 
130   Object* object = iter->second;
131   Object::PropertiesMap::iterator piter =
132       object->properties_map.find(interface_name);
133   if (piter == object->properties_map.end())
134     return nullptr;
135 
136   return piter->second;
137 }
138 
GetManagedObjects()139 void ObjectManager::GetManagedObjects() {
140   MethodCall method_call(kObjectManagerInterface,
141                          kObjectManagerGetManagedObjects);
142 
143   object_proxy_->CallMethod(
144       &method_call,
145       ObjectProxy::TIMEOUT_USE_DEFAULT,
146       base::Bind(&ObjectManager::OnGetManagedObjects,
147                  weak_ptr_factory_.GetWeakPtr()));
148 }
149 
CleanUp()150 void ObjectManager::CleanUp() {
151   DCHECK(bus_);
152   bus_->AssertOnDBusThread();
153   DCHECK(!cleanup_called_);
154 
155   cleanup_called_ = true;
156 
157   if (!setup_success_)
158     return;
159 
160   bus_->RemoveFilterFunction(&ObjectManager::HandleMessageThunk, this);
161 
162   ScopedDBusError error;
163   bus_->RemoveMatch(match_rule_, error.get());
164   if (error.is_set())
165     LOG(ERROR) << "Failed to remove match rule: " << match_rule_;
166 
167   match_rule_.clear();
168 }
169 
SetupMatchRuleAndFilter()170 bool ObjectManager::SetupMatchRuleAndFilter() {
171   DCHECK(bus_);
172   DCHECK(!setup_success_);
173   bus_->AssertOnDBusThread();
174 
175   if (cleanup_called_)
176     return false;
177 
178   if (!bus_->Connect() || !bus_->SetUpAsyncOperations())
179     return false;
180 
181   service_name_owner_ =
182       bus_->GetServiceOwnerAndBlock(service_name_, Bus::SUPPRESS_ERRORS);
183 
184   const std::string match_rule =
185       base::StringPrintf(
186           "type='signal', sender='%s', interface='%s', member='%s'",
187           service_name_.c_str(),
188           kPropertiesInterface,
189           kPropertiesChanged);
190 
191   bus_->AddFilterFunction(&ObjectManager::HandleMessageThunk, this);
192 
193   ScopedDBusError error;
194   bus_->AddMatch(match_rule, error.get());
195   if (error.is_set()) {
196     LOG(ERROR) << "ObjectManager failed to add match rule \"" << match_rule
197                << "\". Got " << error.name() << ": " << error.message();
198     bus_->RemoveFilterFunction(&ObjectManager::HandleMessageThunk, this);
199     return false;
200   }
201 
202   match_rule_ = match_rule;
203   setup_success_ = true;
204 
205   return true;
206 }
207 
OnSetupMatchRuleAndFilterComplete(bool success)208 void ObjectManager::OnSetupMatchRuleAndFilterComplete(bool success) {
209   if (!success) {
210     LOG(WARNING) << service_name_ << " " << object_path_.value()
211                  << ": Failed to set up match rule.";
212     return;
213   }
214 
215   DCHECK(bus_);
216   DCHECK(object_proxy_);
217   DCHECK(setup_success_);
218 
219   // |object_proxy_| is no longer valid if the Bus was shut down before this
220   // call. Don't initiate any other action from the origin thread.
221   if (cleanup_called_)
222     return;
223 
224   object_proxy_->ConnectToSignal(
225       kObjectManagerInterface,
226       kObjectManagerInterfacesAdded,
227       base::Bind(&ObjectManager::InterfacesAddedReceived,
228                  weak_ptr_factory_.GetWeakPtr()),
229       base::Bind(&ObjectManager::InterfacesAddedConnected,
230                  weak_ptr_factory_.GetWeakPtr()));
231 
232   object_proxy_->ConnectToSignal(
233       kObjectManagerInterface,
234       kObjectManagerInterfacesRemoved,
235       base::Bind(&ObjectManager::InterfacesRemovedReceived,
236                  weak_ptr_factory_.GetWeakPtr()),
237       base::Bind(&ObjectManager::InterfacesRemovedConnected,
238                  weak_ptr_factory_.GetWeakPtr()));
239 
240   if (!service_name_owner_.empty())
241     GetManagedObjects();
242 }
243 
244 // static
HandleMessageThunk(DBusConnection * connection,DBusMessage * raw_message,void * user_data)245 DBusHandlerResult ObjectManager::HandleMessageThunk(DBusConnection* connection,
246                                                     DBusMessage* raw_message,
247                                                     void* user_data) {
248   ObjectManager* self = reinterpret_cast<ObjectManager*>(user_data);
249   return self->HandleMessage(connection, raw_message);
250 }
251 
HandleMessage(DBusConnection * connection,DBusMessage * raw_message)252 DBusHandlerResult ObjectManager::HandleMessage(DBusConnection* connection,
253                                                DBusMessage* raw_message) {
254   DCHECK(bus_);
255   bus_->AssertOnDBusThread();
256 
257   // Handle the message only if it is a signal.
258   // Note that the match rule in SetupMatchRuleAndFilter() is configured to
259   // only accept signals, but we check here just in case.
260   if (dbus_message_get_type(raw_message) != DBUS_MESSAGE_TYPE_SIGNAL)
261     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
262 
263   // raw_message will be unrefed on exit of the function. Increment the
264   // reference so we can use it in Signal.
265   dbus_message_ref(raw_message);
266   std::unique_ptr<Signal> signal(Signal::FromRawMessage(raw_message));
267 
268   const std::string interface = signal->GetInterface();
269   const std::string member = signal->GetMember();
270 
271   statistics::AddReceivedSignal(service_name_, interface, member);
272 
273   // Handle the signal only if it is PropertiesChanged.
274   // Note that the match rule in SetupMatchRuleAndFilter() is configured to
275   // only accept PropertiesChanged signals, but we check here just in case.
276   const std::string absolute_signal_name =
277       GetAbsoluteMemberName(interface, member);
278   const std::string properties_changed_signal_name =
279       GetAbsoluteMemberName(kPropertiesInterface, kPropertiesChanged);
280   if (absolute_signal_name != properties_changed_signal_name)
281     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
282 
283   VLOG(1) << "Signal received: " << signal->ToString();
284 
285   // Handle the signal only if it is from the service that the ObjectManager
286   // instance is interested in.
287   // Note that the match rule in SetupMatchRuleAndFilter() is configured to
288   // only accept messages from the service name of our interest. However, the
289   // service='...' filter does not work as intended. See crbug.com/507206#14
290   // and #15 for details, hence it's necessary to check the sender here.
291   std::string sender = signal->GetSender();
292   if (service_name_owner_ != sender)
293     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
294 
295   const ObjectPath path = signal->GetPath();
296 
297   if (bus_->HasDBusThread()) {
298     // Post a task to run the method in the origin thread. Transfer ownership of
299     // |signal| to NotifyPropertiesChanged, which will handle the clean up.
300     Signal* released_signal = signal.release();
301     bus_->GetOriginTaskRunner()->PostTask(
302         FROM_HERE,
303         base::Bind(&ObjectManager::NotifyPropertiesChanged,
304                    this, path,
305                    released_signal));
306   } else {
307     // If the D-Bus thread is not used, just call the callback on the
308     // current thread. Transfer the ownership of |signal| to
309     // NotifyPropertiesChanged.
310     NotifyPropertiesChanged(path, signal.release());
311   }
312 
313   // We don't return DBUS_HANDLER_RESULT_HANDLED for signals because other
314   // objects may be interested in them. (e.g. Signals from org.freedesktop.DBus)
315   return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
316 }
317 
NotifyPropertiesChanged(const dbus::ObjectPath object_path,Signal * signal)318 void ObjectManager::NotifyPropertiesChanged(
319     const dbus::ObjectPath object_path,
320     Signal* signal) {
321   DCHECK(bus_);
322   bus_->AssertOnOriginThread();
323 
324   NotifyPropertiesChangedHelper(object_path, signal);
325 
326   // Delete the message on the D-Bus thread. See comments in HandleMessage.
327   bus_->GetDBusTaskRunner()->PostTask(
328       FROM_HERE,
329       base::Bind(&base::DeletePointer<Signal>, signal));
330 }
331 
NotifyPropertiesChangedHelper(const dbus::ObjectPath object_path,Signal * signal)332 void ObjectManager::NotifyPropertiesChangedHelper(
333     const dbus::ObjectPath object_path,
334     Signal* signal) {
335   DCHECK(bus_);
336   bus_->AssertOnOriginThread();
337 
338   MessageReader reader(signal);
339   std::string interface;
340   if (!reader.PopString(&interface)) {
341     LOG(WARNING) << "Property changed signal has wrong parameters: "
342                  << "expected interface name: " << signal->ToString();
343     return;
344   }
345 
346   PropertySet* properties = GetProperties(object_path, interface);
347   if (properties)
348     properties->ChangedReceived(signal);
349 }
350 
OnGetManagedObjects(Response * response)351 void ObjectManager::OnGetManagedObjects(Response* response) {
352   if (response != nullptr) {
353     MessageReader reader(response);
354     MessageReader array_reader(nullptr);
355     if (!reader.PopArray(&array_reader))
356       return;
357 
358     while (array_reader.HasMoreData()) {
359       MessageReader dict_entry_reader(nullptr);
360       ObjectPath object_path;
361       if (!array_reader.PopDictEntry(&dict_entry_reader) ||
362           !dict_entry_reader.PopObjectPath(&object_path))
363         continue;
364 
365       UpdateObject(object_path, &dict_entry_reader);
366     }
367 
368   } else {
369     LOG(WARNING) << service_name_ << " " << object_path_.value()
370                  << ": Failed to get managed objects";
371   }
372 }
373 
InterfacesAddedReceived(Signal * signal)374 void ObjectManager::InterfacesAddedReceived(Signal* signal) {
375   DCHECK(signal);
376   MessageReader reader(signal);
377   ObjectPath object_path;
378   if (!reader.PopObjectPath(&object_path)) {
379     LOG(WARNING) << service_name_ << " " << object_path_.value()
380                  << ": InterfacesAdded signal has incorrect parameters: "
381                  << signal->ToString();
382     return;
383   }
384 
385   UpdateObject(object_path, &reader);
386 }
387 
InterfacesAddedConnected(const std::string & interface_name,const std::string & signal_name,bool success)388 void ObjectManager::InterfacesAddedConnected(const std::string& interface_name,
389                                              const std::string& signal_name,
390                                              bool success) {
391   LOG_IF(WARNING, !success) << service_name_ << " " << object_path_.value()
392                             << ": Failed to connect to InterfacesAdded signal.";
393 }
394 
InterfacesRemovedReceived(Signal * signal)395 void ObjectManager::InterfacesRemovedReceived(Signal* signal) {
396   DCHECK(signal);
397   MessageReader reader(signal);
398   ObjectPath object_path;
399   std::vector<std::string> interface_names;
400   if (!reader.PopObjectPath(&object_path) ||
401       !reader.PopArrayOfStrings(&interface_names)) {
402     LOG(WARNING) << service_name_ << " " << object_path_.value()
403                  << ": InterfacesRemoved signal has incorrect parameters: "
404                  << signal->ToString();
405     return;
406   }
407 
408   for (size_t i = 0; i < interface_names.size(); ++i)
409     RemoveInterface(object_path, interface_names[i]);
410 }
411 
InterfacesRemovedConnected(const std::string & interface_name,const std::string & signal_name,bool success)412 void ObjectManager::InterfacesRemovedConnected(
413     const std::string& interface_name,
414     const std::string& signal_name,
415     bool success) {
416   LOG_IF(WARNING, !success) << service_name_ << " " << object_path_.value()
417                             << ": Failed to connect to "
418                             << "InterfacesRemoved signal.";
419 }
420 
UpdateObject(const ObjectPath & object_path,MessageReader * reader)421 void ObjectManager::UpdateObject(const ObjectPath& object_path,
422                                  MessageReader* reader) {
423   DCHECK(reader);
424   MessageReader array_reader(nullptr);
425   if (!reader->PopArray(&array_reader))
426     return;
427 
428   while (array_reader.HasMoreData()) {
429     MessageReader dict_entry_reader(nullptr);
430     std::string interface_name;
431     if (!array_reader.PopDictEntry(&dict_entry_reader) ||
432         !dict_entry_reader.PopString(&interface_name))
433       continue;
434 
435     AddInterface(object_path, interface_name, &dict_entry_reader);
436   }
437 }
438 
439 
AddInterface(const ObjectPath & object_path,const std::string & interface_name,MessageReader * reader)440 void ObjectManager::AddInterface(const ObjectPath& object_path,
441                                  const std::string& interface_name,
442                                  MessageReader* reader) {
443   InterfaceMap::iterator iiter = interface_map_.find(interface_name);
444   if (iiter == interface_map_.end())
445     return;
446   Interface* interface = iiter->second;
447 
448   ObjectMap::iterator oiter = object_map_.find(object_path);
449   Object* object;
450   if (oiter == object_map_.end()) {
451     object = object_map_[object_path] = new Object;
452     object->object_proxy = bus_->GetObjectProxy(service_name_, object_path);
453   } else
454     object = oiter->second;
455 
456   Object::PropertiesMap::iterator piter =
457       object->properties_map.find(interface_name);
458   PropertySet* property_set;
459   const bool interface_added = (piter == object->properties_map.end());
460   if (interface_added) {
461     property_set = object->properties_map[interface_name] =
462         interface->CreateProperties(object->object_proxy,
463                                     object_path, interface_name);
464   } else
465     property_set = piter->second;
466 
467   property_set->UpdatePropertiesFromReader(reader);
468 
469   if (interface_added)
470     interface->ObjectAdded(object_path, interface_name);
471 }
472 
RemoveInterface(const ObjectPath & object_path,const std::string & interface_name)473 void ObjectManager::RemoveInterface(const ObjectPath& object_path,
474                                     const std::string& interface_name) {
475   ObjectMap::iterator oiter = object_map_.find(object_path);
476   if (oiter == object_map_.end())
477     return;
478   Object* object = oiter->second;
479 
480   Object::PropertiesMap::iterator piter =
481       object->properties_map.find(interface_name);
482   if (piter == object->properties_map.end())
483     return;
484 
485   // Inform the interface before removing the properties structure or object
486   // in case it needs details from them to make its own decisions.
487   InterfaceMap::iterator iiter = interface_map_.find(interface_name);
488   if (iiter != interface_map_.end()) {
489     Interface* interface = iiter->second;
490     interface->ObjectRemoved(object_path, interface_name);
491   }
492 
493   delete piter->second;
494   object->properties_map.erase(piter);
495 
496   if (object->properties_map.empty()) {
497     object_map_.erase(oiter);
498     delete object;
499   }
500 }
501 
NameOwnerChanged(const std::string & old_owner,const std::string & new_owner)502 void ObjectManager::NameOwnerChanged(const std::string& old_owner,
503                                      const std::string& new_owner) {
504   service_name_owner_ = new_owner;
505 
506   if (!old_owner.empty()) {
507     ObjectMap::iterator iter = object_map_.begin();
508     while (iter != object_map_.end()) {
509       ObjectMap::iterator tmp = iter;
510       ++iter;
511 
512       // PropertiesMap is mutated by RemoveInterface, and also Object is
513       // destroyed; easier to collect the object path and interface names
514       // and remove them safely.
515       const dbus::ObjectPath object_path = tmp->first;
516       Object* object = tmp->second;
517       std::vector<std::string> interfaces;
518 
519       for (Object::PropertiesMap::iterator piter =
520               object->properties_map.begin();
521            piter != object->properties_map.end(); ++piter)
522         interfaces.push_back(piter->first);
523 
524       for (std::vector<std::string>::iterator iiter = interfaces.begin();
525            iiter != interfaces.end(); ++iiter)
526         RemoveInterface(object_path, *iiter);
527     }
528 
529   }
530 
531   if (!new_owner.empty())
532     GetManagedObjects();
533 }
534 
535 }  // namespace dbus
536