blob: 851fee44b2e140b41c39b6bc8b190319252c6b08 [file] [log] [blame]
Daniel Eratae6a0452015-07-15 16:43:47 -06001// 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 bus_->RemoveFilterFunction(&ObjectManager::HandleMessageThunk, this);
159
160 ScopedDBusError error;
161 bus_->RemoveMatch(match_rule_, error.get());
162 if (error.is_set())
163 LOG(ERROR) << "Failed to remove match rule: " << match_rule_;
164
165 match_rule_.clear();
166}
167
168void ObjectManager::InitializeObjects() {
169 DCHECK(bus_);
170 DCHECK(object_proxy_);
171 DCHECK(setup_success_);
172
173 // |object_proxy_| is no longer valid if the Bus was shut down before this
174 // call. Don't initiate any other action from the origin thread.
175 if (cleanup_called_)
176 return;
177
178 object_proxy_->ConnectToSignal(
179 kObjectManagerInterface,
180 kObjectManagerInterfacesAdded,
181 base::Bind(&ObjectManager::InterfacesAddedReceived,
182 weak_ptr_factory_.GetWeakPtr()),
183 base::Bind(&ObjectManager::InterfacesAddedConnected,
184 weak_ptr_factory_.GetWeakPtr()));
185
186 object_proxy_->ConnectToSignal(
187 kObjectManagerInterface,
188 kObjectManagerInterfacesRemoved,
189 base::Bind(&ObjectManager::InterfacesRemovedReceived,
190 weak_ptr_factory_.GetWeakPtr()),
191 base::Bind(&ObjectManager::InterfacesRemovedConnected,
192 weak_ptr_factory_.GetWeakPtr()));
193
194 GetManagedObjects();
195}
196
197bool ObjectManager::SetupMatchRuleAndFilter() {
198 DCHECK(bus_);
199 DCHECK(!setup_success_);
200 bus_->AssertOnDBusThread();
201
202 if (cleanup_called_)
203 return false;
204
205 if (!bus_->Connect() || !bus_->SetUpAsyncOperations())
206 return false;
207
208 service_name_owner_ =
209 bus_->GetServiceOwnerAndBlock(service_name_, Bus::SUPPRESS_ERRORS);
210
211 const std::string match_rule =
212 base::StringPrintf(
213 "type='signal', sender='%s', interface='%s', member='%s'",
214 service_name_.c_str(),
215 kPropertiesInterface,
216 kPropertiesChanged);
217
218 bus_->AddFilterFunction(&ObjectManager::HandleMessageThunk, this);
219
220 ScopedDBusError error;
221 bus_->AddMatch(match_rule, error.get());
222 if (error.is_set()) {
223 LOG(ERROR) << "ObjectManager failed to add match rule \"" << match_rule
224 << "\". Got " << error.name() << ": " << error.message();
225 bus_->RemoveFilterFunction(&ObjectManager::HandleMessageThunk, this);
226 return false;
227 }
228
229 match_rule_ = match_rule;
230 setup_success_ = true;
231
232 return true;
233}
234
235void ObjectManager::OnSetupMatchRuleAndFilterComplete(bool success) {
236 LOG_IF(WARNING, !success) << service_name_ << " " << object_path_.value()
237 << ": Failed to set up match rule.";
238 if (success)
239 InitializeObjects();
240}
241
242// static
243DBusHandlerResult ObjectManager::HandleMessageThunk(DBusConnection* connection,
244 DBusMessage* raw_message,
245 void* user_data) {
246 ObjectManager* self = reinterpret_cast<ObjectManager*>(user_data);
247 return self->HandleMessage(connection, raw_message);
248}
249
Christopher Wiley88ee3742015-12-29 15:16:44 +0000250DBusHandlerResult ObjectManager::HandleMessage(DBusConnection* connection,
Daniel Eratae6a0452015-07-15 16:43:47 -0600251 DBusMessage* raw_message) {
252 DCHECK(bus_);
253 bus_->AssertOnDBusThread();
254
255 if (dbus_message_get_type(raw_message) != DBUS_MESSAGE_TYPE_SIGNAL)
256 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
257
258 // raw_message will be unrefed on exit of the function. Increment the
259 // reference so we can use it in Signal.
260 dbus_message_ref(raw_message);
261 scoped_ptr<Signal> signal(
262 Signal::FromRawMessage(raw_message));
263
264 const std::string interface = signal->GetInterface();
265 const std::string member = signal->GetMember();
266
267 statistics::AddReceivedSignal(service_name_, interface, member);
268
269 // Only handle the PropertiesChanged signal.
270 const std::string absolute_signal_name =
271 GetAbsoluteMemberName(interface, member);
272 const std::string properties_changed_signal_name =
273 GetAbsoluteMemberName(kPropertiesInterface, kPropertiesChanged);
274 if (absolute_signal_name != properties_changed_signal_name)
275 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
276
277 VLOG(1) << "Signal received: " << signal->ToString();
278
279 // Make sure that the signal originated from the correct sender.
280 std::string sender = signal->GetSender();
281 if (service_name_owner_ != sender) {
282 LOG(ERROR) << "Rejecting a message from a wrong sender.";
283 UMA_HISTOGRAM_COUNTS("DBus.RejectedSignalCount", 1);
284 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
285 }
286
287 const ObjectPath path = signal->GetPath();
288
289 if (bus_->HasDBusThread()) {
290 // Post a task to run the method in the origin thread. Transfer ownership of
291 // |signal| to NotifyPropertiesChanged, which will handle the clean up.
292 Signal* released_signal = signal.release();
293 bus_->GetOriginTaskRunner()->PostTask(
294 FROM_HERE,
295 base::Bind(&ObjectManager::NotifyPropertiesChanged,
296 this, path,
297 released_signal));
298 } else {
299 // If the D-Bus thread is not used, just call the callback on the
300 // current thread. Transfer the ownership of |signal| to
301 // NotifyPropertiesChanged.
302 NotifyPropertiesChanged(path, signal.release());
303 }
304
305 // We don't return DBUS_HANDLER_RESULT_HANDLED for signals because other
306 // objects may be interested in them. (e.g. Signals from org.freedesktop.DBus)
307 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
308}
309
310void ObjectManager::NotifyPropertiesChanged(
311 const dbus::ObjectPath object_path,
312 Signal* signal) {
313 DCHECK(bus_);
314 bus_->AssertOnOriginThread();
315
316 NotifyPropertiesChangedHelper(object_path, signal);
317
318 // Delete the message on the D-Bus thread. See comments in HandleMessage.
319 bus_->GetDBusTaskRunner()->PostTask(
320 FROM_HERE,
321 base::Bind(&base::DeletePointer<Signal>, signal));
322}
323
324void ObjectManager::NotifyPropertiesChangedHelper(
325 const dbus::ObjectPath object_path,
326 Signal* signal) {
327 DCHECK(bus_);
328 bus_->AssertOnOriginThread();
329
330 MessageReader reader(signal);
331 std::string interface;
332 if (!reader.PopString(&interface)) {
333 LOG(WARNING) << "Property changed signal has wrong parameters: "
334 << "expected interface name: " << signal->ToString();
335 return;
336 }
337
338 PropertySet* properties = GetProperties(object_path, interface);
339 if (properties)
340 properties->ChangedReceived(signal);
341}
342
343void ObjectManager::OnGetManagedObjects(Response* response) {
344 if (response != NULL) {
345 MessageReader reader(response);
346 MessageReader array_reader(NULL);
347 if (!reader.PopArray(&array_reader))
348 return;
349
350 while (array_reader.HasMoreData()) {
351 MessageReader dict_entry_reader(NULL);
352 ObjectPath object_path;
353 if (!array_reader.PopDictEntry(&dict_entry_reader) ||
354 !dict_entry_reader.PopObjectPath(&object_path))
355 continue;
356
357 UpdateObject(object_path, &dict_entry_reader);
358 }
359
360 } else {
361 LOG(WARNING) << service_name_ << " " << object_path_.value()
362 << ": Failed to get managed objects";
363 }
364}
365
366void ObjectManager::InterfacesAddedReceived(Signal* signal) {
367 DCHECK(signal);
368 MessageReader reader(signal);
369 ObjectPath object_path;
370 if (!reader.PopObjectPath(&object_path)) {
371 LOG(WARNING) << service_name_ << " " << object_path_.value()
372 << ": InterfacesAdded signal has incorrect parameters: "
373 << signal->ToString();
374 return;
375 }
376
377 UpdateObject(object_path, &reader);
378}
379
Christopher Wiley88ee3742015-12-29 15:16:44 +0000380void ObjectManager::InterfacesAddedConnected(const std::string& interface_name,
381 const std::string& signal_name,
382 bool success) {
Daniel Eratae6a0452015-07-15 16:43:47 -0600383 LOG_IF(WARNING, !success) << service_name_ << " " << object_path_.value()
384 << ": Failed to connect to InterfacesAdded signal.";
385}
386
387void ObjectManager::InterfacesRemovedReceived(Signal* signal) {
388 DCHECK(signal);
389 MessageReader reader(signal);
390 ObjectPath object_path;
391 std::vector<std::string> interface_names;
392 if (!reader.PopObjectPath(&object_path) ||
393 !reader.PopArrayOfStrings(&interface_names)) {
394 LOG(WARNING) << service_name_ << " " << object_path_.value()
395 << ": InterfacesRemoved signal has incorrect parameters: "
396 << signal->ToString();
397 return;
398 }
399
400 for (size_t i = 0; i < interface_names.size(); ++i)
401 RemoveInterface(object_path, interface_names[i]);
402}
403
404void ObjectManager::InterfacesRemovedConnected(
Christopher Wiley88ee3742015-12-29 15:16:44 +0000405 const std::string& interface_name,
406 const std::string& signal_name,
Daniel Eratae6a0452015-07-15 16:43:47 -0600407 bool success) {
408 LOG_IF(WARNING, !success) << service_name_ << " " << object_path_.value()
409 << ": Failed to connect to "
410 << "InterfacesRemoved signal.";
411}
412
413void ObjectManager::UpdateObject(const ObjectPath& object_path,
414 MessageReader* reader) {
415 DCHECK(reader);
416 MessageReader array_reader(NULL);
417 if (!reader->PopArray(&array_reader))
418 return;
419
420 while (array_reader.HasMoreData()) {
421 MessageReader dict_entry_reader(NULL);
422 std::string interface_name;
423 if (!array_reader.PopDictEntry(&dict_entry_reader) ||
424 !dict_entry_reader.PopString(&interface_name))
425 continue;
426
427 AddInterface(object_path, interface_name, &dict_entry_reader);
428 }
429}
430
431
432void ObjectManager::AddInterface(const ObjectPath& object_path,
433 const std::string& interface_name,
434 MessageReader* reader) {
435 InterfaceMap::iterator iiter = interface_map_.find(interface_name);
436 if (iiter == interface_map_.end())
437 return;
438 Interface* interface = iiter->second;
439
440 ObjectMap::iterator oiter = object_map_.find(object_path);
441 Object* object;
442 if (oiter == object_map_.end()) {
443 object = object_map_[object_path] = new Object;
444 object->object_proxy = bus_->GetObjectProxy(service_name_, object_path);
445 } else
446 object = oiter->second;
447
448 Object::PropertiesMap::iterator piter =
449 object->properties_map.find(interface_name);
450 PropertySet* property_set;
451 const bool interface_added = (piter == object->properties_map.end());
452 if (interface_added) {
453 property_set = object->properties_map[interface_name] =
454 interface->CreateProperties(object->object_proxy,
455 object_path, interface_name);
456 } else
457 property_set = piter->second;
458
459 property_set->UpdatePropertiesFromReader(reader);
460
461 if (interface_added)
462 interface->ObjectAdded(object_path, interface_name);
463}
464
465void ObjectManager::RemoveInterface(const ObjectPath& object_path,
466 const std::string& interface_name) {
467 ObjectMap::iterator oiter = object_map_.find(object_path);
468 if (oiter == object_map_.end())
469 return;
470 Object* object = oiter->second;
471
472 Object::PropertiesMap::iterator piter =
473 object->properties_map.find(interface_name);
474 if (piter == object->properties_map.end())
475 return;
476
477 // Inform the interface before removing the properties structure or object
478 // in case it needs details from them to make its own decisions.
479 InterfaceMap::iterator iiter = interface_map_.find(interface_name);
480 if (iiter != interface_map_.end()) {
481 Interface* interface = iiter->second;
482 interface->ObjectRemoved(object_path, interface_name);
483 }
484
485 delete piter->second;
486 object->properties_map.erase(piter);
487
488 if (object->properties_map.empty()) {
489 object_map_.erase(oiter);
490 delete object;
491 }
492}
493
494void ObjectManager::NameOwnerChanged(const std::string& old_owner,
495 const std::string& new_owner) {
496 service_name_owner_ = new_owner;
497
498 if (!old_owner.empty()) {
499 ObjectMap::iterator iter = object_map_.begin();
500 while (iter != object_map_.end()) {
501 ObjectMap::iterator tmp = iter;
502 ++iter;
503
504 // PropertiesMap is mutated by RemoveInterface, and also Object is
505 // destroyed; easier to collect the object path and interface names
506 // and remove them safely.
507 const dbus::ObjectPath object_path = tmp->first;
508 Object* object = tmp->second;
509 std::vector<std::string> interfaces;
510
511 for (Object::PropertiesMap::iterator piter =
512 object->properties_map.begin();
513 piter != object->properties_map.end(); ++piter)
514 interfaces.push_back(piter->first);
515
516 for (std::vector<std::string>::iterator iiter = interfaces.begin();
517 iiter != interfaces.end(); ++iiter)
518 RemoveInterface(object_path, *iiter);
519 }
520
521 }
522
523 if (!new_owner.empty())
524 GetManagedObjects();
525}
526
527} // namespace dbus