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