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