blob: f754bb693fd3d909648d978347aae94c27e06aea [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
7#include "base/bind.h"
armansitof4364642014-09-06 02:49:34 +09008#include "base/location.h"
keybuk@chromium.org09715012013-03-26 03:20:08 +09009#include "base/logging.h"
armansitof4364642014-09-06 02:49:34 +090010#include "base/metrics/histogram.h"
11#include "base/strings/stringprintf.h"
12#include "base/task_runner_util.h"
keybuk@chromium.org09715012013-03-26 03:20:08 +090013#include "dbus/bus.h"
armansitof4364642014-09-06 02:49:34 +090014#include "dbus/dbus_statistics.h"
keybuk@chromium.org09715012013-03-26 03:20:08 +090015#include "dbus/message.h"
16#include "dbus/object_proxy.h"
17#include "dbus/property.h"
armansitof4364642014-09-06 02:49:34 +090018#include "dbus/scoped_dbus_error.h"
19#include "dbus/util.h"
keybuk@chromium.org09715012013-03-26 03:20:08 +090020
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),
armansitof4364642014-09-06 02:49:34 +090036 setup_success_(false),
37 cleanup_called_(false),
keybuk@chromium.org09715012013-03-26 03:20:08 +090038 weak_ptr_factory_(this) {
39 DVLOG(1) << "Creating ObjectManager for " << service_name_
40 << " " << object_path_.value();
keybuk@chromium.org09715012013-03-26 03:20:08 +090041 DCHECK(bus_);
armansitof4364642014-09-06 02:49:34 +090042 bus_->AssertOnOriginThread();
keybuk@chromium.org09715012013-03-26 03:20:08 +090043 object_proxy_ = bus_->GetObjectProxy(service_name_, object_path_);
keybuk@chromium.org40b05ba2014-03-07 11:24:33 +090044 object_proxy_->SetNameOwnerChangedCallback(
45 base::Bind(&ObjectManager::NameOwnerChanged,
46 weak_ptr_factory_.GetWeakPtr()));
keybuk@chromium.org09715012013-03-26 03:20:08 +090047
armansitof4364642014-09-06 02:49:34 +090048 // 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));
keybuk@chromium.org09715012013-03-26 03:20:08 +090057}
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
armansitof4364642014-09-06 02:49:34 +0900148void 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
keybuk@chromium.org09715012013-03-26 03:20:08 +0900347void 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);
keybuk@chromium.org09715012013-03-26 03:20:08 +0900460 } 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
keybuk@chromium.org40b05ba2014-03-07 11:24:33 +0900497void ObjectManager::NameOwnerChanged(const std::string& old_owner,
498 const std::string& new_owner) {
armansitof4364642014-09-06 02:49:34 +0900499 service_name_owner_ = new_owner;
500
keybuk@chromium.org40b05ba2014-03-07 11:24:33 +0900501 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
keybuk@chromium.org09715012013-03-26 03:20:08 +0900530} // namespace dbus