blob: 56143c758b4bb9b40f72e0f55075e0caa1f77ddc [file] [log] [blame]
keybuk@chromium.org09715012013-03-26 03:20:08 +09001// 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
avi0ad0ce02015-12-23 03:12:45 +09007#include <stddef.h>
8
keybuk@chromium.org09715012013-03-26 03:20:08 +09009#include "base/bind.h"
armansitof4364642014-09-06 02:49:34 +090010#include "base/location.h"
keybuk@chromium.org09715012013-03-26 03:20:08 +090011#include "base/logging.h"
armansitof4364642014-09-06 02:49:34 +090012#include "base/metrics/histogram.h"
13#include "base/strings/stringprintf.h"
14#include "base/task_runner_util.h"
keybuk@chromium.org09715012013-03-26 03:20:08 +090015#include "dbus/bus.h"
armansitof4364642014-09-06 02:49:34 +090016#include "dbus/dbus_statistics.h"
keybuk@chromium.org09715012013-03-26 03:20:08 +090017#include "dbus/message.h"
18#include "dbus/object_proxy.h"
19#include "dbus/property.h"
armansitof4364642014-09-06 02:49:34 +090020#include "dbus/scoped_dbus_error.h"
21#include "dbus/util.h"
keybuk@chromium.org09715012013-03-26 03:20:08 +090022
23namespace dbus {
24
25ObjectManager::Object::Object()
26 : object_proxy(NULL) {
27}
28
29ObjectManager::Object::~Object() {
30}
31
32ObjectManager::ObjectManager(Bus* bus,
33 const std::string& service_name,
34 const ObjectPath& object_path)
35 : bus_(bus),
36 service_name_(service_name),
37 object_path_(object_path),
armansitof4364642014-09-06 02:49:34 +090038 setup_success_(false),
39 cleanup_called_(false),
keybuk@chromium.org09715012013-03-26 03:20:08 +090040 weak_ptr_factory_(this) {
41 DVLOG(1) << "Creating ObjectManager for " << service_name_
42 << " " << object_path_.value();
keybuk@chromium.org09715012013-03-26 03:20:08 +090043 DCHECK(bus_);
armansitof4364642014-09-06 02:49:34 +090044 bus_->AssertOnOriginThread();
keybuk@chromium.org09715012013-03-26 03:20:08 +090045 object_proxy_ = bus_->GetObjectProxy(service_name_, object_path_);
keybuk@chromium.org40b05ba2014-03-07 11:24:33 +090046 object_proxy_->SetNameOwnerChangedCallback(
47 base::Bind(&ObjectManager::NameOwnerChanged,
48 weak_ptr_factory_.GetWeakPtr()));
keybuk@chromium.org09715012013-03-26 03:20:08 +090049
armansitof4364642014-09-06 02:49:34 +090050 // 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));
keybuk@chromium.org09715012013-03-26 03:20:08 +090059}
60
61ObjectManager::~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
77void ObjectManager::RegisterInterface(const std::string& interface_name,
78 Interface* interface) {
79 interface_map_[interface_name] = interface;
80}
81
82void 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
88std::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
98std::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
115ObjectProxy* ObjectManager::GetObjectProxy(const ObjectPath& object_path) {
116 ObjectMap::iterator iter = object_map_.find(object_path);
117 if (iter == object_map_.end())
118 return NULL;
119
120 Object* object = iter->second;
121 return object->object_proxy;
122}
123
124PropertySet* 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 NULL;
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 NULL;
135
136 return piter->second;
137}
138
139void 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
armansitof4364642014-09-06 02:49:34 +0900150void 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
hashimoto46be6e92014-12-04 16:41:55 +0900160 bus_->RemoveFilterFunction(&ObjectManager::HandleMessageThunk, this);
armansitof4364642014-09-06 02:49:34 +0900161
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
170void ObjectManager::InitializeObjects() {
171 DCHECK(bus_);
172 DCHECK(object_proxy_);
173 DCHECK(setup_success_);
174
175 // |object_proxy_| is no longer valid if the Bus was shut down before this
176 // call. Don't initiate any other action from the origin thread.
177 if (cleanup_called_)
178 return;
179
180 object_proxy_->ConnectToSignal(
181 kObjectManagerInterface,
182 kObjectManagerInterfacesAdded,
183 base::Bind(&ObjectManager::InterfacesAddedReceived,
184 weak_ptr_factory_.GetWeakPtr()),
185 base::Bind(&ObjectManager::InterfacesAddedConnected,
186 weak_ptr_factory_.GetWeakPtr()));
187
188 object_proxy_->ConnectToSignal(
189 kObjectManagerInterface,
190 kObjectManagerInterfacesRemoved,
191 base::Bind(&ObjectManager::InterfacesRemovedReceived,
192 weak_ptr_factory_.GetWeakPtr()),
193 base::Bind(&ObjectManager::InterfacesRemovedConnected,
194 weak_ptr_factory_.GetWeakPtr()));
195
196 GetManagedObjects();
197}
198
199bool ObjectManager::SetupMatchRuleAndFilter() {
200 DCHECK(bus_);
201 DCHECK(!setup_success_);
202 bus_->AssertOnDBusThread();
203
204 if (cleanup_called_)
205 return false;
206
207 if (!bus_->Connect() || !bus_->SetUpAsyncOperations())
208 return false;
209
210 service_name_owner_ =
211 bus_->GetServiceOwnerAndBlock(service_name_, Bus::SUPPRESS_ERRORS);
212
213 const std::string match_rule =
214 base::StringPrintf(
215 "type='signal', sender='%s', interface='%s', member='%s'",
216 service_name_.c_str(),
217 kPropertiesInterface,
218 kPropertiesChanged);
219
hashimoto46be6e92014-12-04 16:41:55 +0900220 bus_->AddFilterFunction(&ObjectManager::HandleMessageThunk, this);
armansitof4364642014-09-06 02:49:34 +0900221
222 ScopedDBusError error;
223 bus_->AddMatch(match_rule, error.get());
224 if (error.is_set()) {
225 LOG(ERROR) << "ObjectManager failed to add match rule \"" << match_rule
226 << "\". Got " << error.name() << ": " << error.message();
227 bus_->RemoveFilterFunction(&ObjectManager::HandleMessageThunk, this);
228 return false;
229 }
230
231 match_rule_ = match_rule;
232 setup_success_ = true;
233
234 return true;
235}
236
237void ObjectManager::OnSetupMatchRuleAndFilterComplete(bool success) {
238 LOG_IF(WARNING, !success) << service_name_ << " " << object_path_.value()
239 << ": Failed to set up match rule.";
240 if (success)
241 InitializeObjects();
242}
243
244// static
245DBusHandlerResult 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
252DBusHandlerResult ObjectManager::HandleMessage(DBusConnection* connection,
253 DBusMessage* raw_message) {
254 DCHECK(bus_);
255 bus_->AssertOnDBusThread();
256
satorux7838f5f2015-08-17 18:44:35 +0900257 // 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.
armansitof4364642014-09-06 02:49:34 +0900260 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);
dcheng30c5a172016-04-09 07:55:04 +0900266 std::unique_ptr<Signal> signal(Signal::FromRawMessage(raw_message));
armansitof4364642014-09-06 02:49:34 +0900267
268 const std::string interface = signal->GetInterface();
269 const std::string member = signal->GetMember();
270
271 statistics::AddReceivedSignal(service_name_, interface, member);
272
satorux7838f5f2015-08-17 18:44:35 +0900273 // 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.
armansitof4364642014-09-06 02:49:34 +0900276 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
satorux7838f5f2015-08-17 18:44:35 +0900285 // 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.
armansitof4364642014-09-06 02:49:34 +0900291 std::string sender = signal->GetSender();
satorux7838f5f2015-08-17 18:44:35 +0900292 if (service_name_owner_ != sender)
armansitof4364642014-09-06 02:49:34 +0900293 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
armansitof4364642014-09-06 02:49:34 +0900294
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
318void 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
332void 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
keybuk@chromium.org09715012013-03-26 03:20:08 +0900351void ObjectManager::OnGetManagedObjects(Response* response) {
352 if (response != NULL) {
353 MessageReader reader(response);
354 MessageReader array_reader(NULL);
355 if (!reader.PopArray(&array_reader))
356 return;
357
358 while (array_reader.HasMoreData()) {
359 MessageReader dict_entry_reader(NULL);
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
374void 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
388void 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
395void 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
412void 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
421void ObjectManager::UpdateObject(const ObjectPath& object_path,
422 MessageReader* reader) {
423 DCHECK(reader);
424 MessageReader array_reader(NULL);
425 if (!reader->PopArray(&array_reader))
426 return;
427
428 while (array_reader.HasMoreData()) {
429 MessageReader dict_entry_reader(NULL);
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
440void 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);
keybuk@chromium.org09715012013-03-26 03:20:08 +0900464 } 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
473void 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
avakulenkoda49a1a2015-04-03 03:51:32 +0900493 delete piter->second;
keybuk@chromium.org09715012013-03-26 03:20:08 +0900494 object->properties_map.erase(piter);
495
496 if (object->properties_map.empty()) {
497 object_map_.erase(oiter);
498 delete object;
499 }
500}
501
keybuk@chromium.org40b05ba2014-03-07 11:24:33 +0900502void ObjectManager::NameOwnerChanged(const std::string& old_owner,
503 const std::string& new_owner) {
armansitof4364642014-09-06 02:49:34 +0900504 service_name_owner_ = new_owner;
505
keybuk@chromium.org40b05ba2014-03-07 11:24:33 +0900506 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
keybuk@chromium.org09715012013-03-26 03:20:08 +0900535} // namespace dbus