[go: nahoru, domu]

blob: 06207db803716e9fe606192679463e2d47c117b4 [file] [log] [blame]
keybuk@chromium.org9cc40cb2013-03-25 18:20:081// 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
avi22437c692015-12-22 18:12:457#include <stddef.h>
8
keybuk@chromium.org9cc40cb2013-03-25 18:20:089#include "base/bind.h"
danakjdb9ae7942020-11-11 16:01:3510#include "base/callback_helpers.h"
armansitoebff093d2014-09-05 17:49:3411#include "base/location.h"
keybuk@chromium.org9cc40cb2013-03-25 18:20:0812#include "base/logging.h"
armansitoebff093d2014-09-05 17:49:3413#include "base/metrics/histogram.h"
14#include "base/strings/stringprintf.h"
15#include "base/task_runner_util.h"
keybuk@chromium.org9cc40cb2013-03-25 18:20:0816#include "dbus/bus.h"
armansitoebff093d2014-09-05 17:49:3417#include "dbus/dbus_statistics.h"
keybuk@chromium.org9cc40cb2013-03-25 18:20:0818#include "dbus/message.h"
19#include "dbus/object_proxy.h"
20#include "dbus/property.h"
armansitoebff093d2014-09-05 17:49:3421#include "dbus/scoped_dbus_error.h"
22#include "dbus/util.h"
keybuk@chromium.org9cc40cb2013-03-25 18:20:0823
24namespace dbus {
25
26ObjectManager::Object::Object()
Ben Chan14d50032017-11-09 20:20:1627 : object_proxy(nullptr) {
keybuk@chromium.org9cc40cb2013-03-25 18:20:0828}
29
Chris Watkins3740aae2017-11-29 07:44:1130ObjectManager::Object::~Object() = default;
keybuk@chromium.org9cc40cb2013-03-25 18:20:0831
tzik570f47a2018-07-24 09:16:5732scoped_refptr<ObjectManager> ObjectManager::Create(
33 Bus* bus,
34 const std::string& service_name,
35 const ObjectPath& object_path) {
36 auto object_manager =
37 base::WrapRefCounted(new ObjectManager(bus, service_name, object_path));
38
39 // Set up a match rule and a filter function to handle PropertiesChanged
40 // signals from the service. This is important to avoid any race conditions
41 // that might cause us to miss PropertiesChanged signals once all objects are
42 // initialized via GetManagedObjects.
43 base::PostTaskAndReplyWithResult(
44 bus->GetDBusTaskRunner(), FROM_HERE,
45 base::BindOnce(&ObjectManager::SetupMatchRuleAndFilter, object_manager),
46 base::BindOnce(&ObjectManager::OnSetupMatchRuleAndFilterComplete,
47 object_manager));
48 return object_manager;
49}
50
keybuk@chromium.org9cc40cb2013-03-25 18:20:0851ObjectManager::ObjectManager(Bus* bus,
52 const std::string& service_name,
53 const ObjectPath& object_path)
54 : bus_(bus),
55 service_name_(service_name),
56 object_path_(object_path),
armansitoebff093d2014-09-05 17:49:3457 setup_success_(false),
Jeremy Roman7c5cfabd2019-08-12 15:45:2758 cleanup_called_(false) {
Ryo Hashimotoc089a0292017-09-25 07:13:5059 LOG_IF(FATAL, !object_path_.IsValid()) << object_path_.value();
keybuk@chromium.org9cc40cb2013-03-25 18:20:0860 DVLOG(1) << "Creating ObjectManager for " << service_name_
61 << " " << object_path_.value();
keybuk@chromium.org9cc40cb2013-03-25 18:20:0862 DCHECK(bus_);
armansitoebff093d2014-09-05 17:49:3463 bus_->AssertOnOriginThread();
keybuk@chromium.org9cc40cb2013-03-25 18:20:0864 object_proxy_ = bus_->GetObjectProxy(service_name_, object_path_);
Reilly Grantd4e66132019-11-22 17:14:5065 object_proxy_->SetNameOwnerChangedCallback(base::BindRepeating(
66 &ObjectManager::NameOwnerChanged, weak_ptr_factory_.GetWeakPtr()));
keybuk@chromium.org9cc40cb2013-03-25 18:20:0867}
68
69ObjectManager::~ObjectManager() {
70 // Clean up Object structures
71 for (ObjectMap::iterator iter = object_map_.begin();
72 iter != object_map_.end(); ++iter) {
73 Object* object = iter->second;
74
75 for (Object::PropertiesMap::iterator piter = object->properties_map.begin();
76 piter != object->properties_map.end(); ++piter) {
77 PropertySet* properties = piter->second;
78 delete properties;
79 }
80
81 delete object;
82 }
83}
84
85void ObjectManager::RegisterInterface(const std::string& interface_name,
86 Interface* interface) {
87 interface_map_[interface_name] = interface;
88}
89
90void ObjectManager::UnregisterInterface(const std::string& interface_name) {
91 InterfaceMap::iterator iter = interface_map_.find(interface_name);
92 if (iter != interface_map_.end())
93 interface_map_.erase(iter);
94}
95
96std::vector<ObjectPath> ObjectManager::GetObjects() {
97 std::vector<ObjectPath> object_paths;
98
99 for (ObjectMap::iterator iter = object_map_.begin();
100 iter != object_map_.end(); ++iter)
101 object_paths.push_back(iter->first);
102
103 return object_paths;
104}
105
106std::vector<ObjectPath> ObjectManager::GetObjectsWithInterface(
107 const std::string& interface_name) {
108 std::vector<ObjectPath> object_paths;
109
110 for (ObjectMap::iterator oiter = object_map_.begin();
111 oiter != object_map_.end(); ++oiter) {
112 Object* object = oiter->second;
113
114 Object::PropertiesMap::iterator piter =
115 object->properties_map.find(interface_name);
116 if (piter != object->properties_map.end())
117 object_paths.push_back(oiter->first);
118 }
119
120 return object_paths;
121}
122
123ObjectProxy* ObjectManager::GetObjectProxy(const ObjectPath& object_path) {
124 ObjectMap::iterator iter = object_map_.find(object_path);
125 if (iter == object_map_.end())
Ben Chan14d50032017-11-09 20:20:16126 return nullptr;
keybuk@chromium.org9cc40cb2013-03-25 18:20:08127
128 Object* object = iter->second;
129 return object->object_proxy;
130}
131
132PropertySet* ObjectManager::GetProperties(const ObjectPath& object_path,
133 const std::string& interface_name) {
134 ObjectMap::iterator iter = object_map_.find(object_path);
135 if (iter == object_map_.end())
Ben Chan14d50032017-11-09 20:20:16136 return nullptr;
keybuk@chromium.org9cc40cb2013-03-25 18:20:08137
138 Object* object = iter->second;
139 Object::PropertiesMap::iterator piter =
140 object->properties_map.find(interface_name);
141 if (piter == object->properties_map.end())
Ben Chan14d50032017-11-09 20:20:16142 return nullptr;
keybuk@chromium.org9cc40cb2013-03-25 18:20:08143
144 return piter->second;
145}
146
147void ObjectManager::GetManagedObjects() {
148 MethodCall method_call(kObjectManagerInterface,
149 kObjectManagerGetManagedObjects);
150
Reilly Grantd4e66132019-11-22 17:14:50151 object_proxy_->CallMethod(&method_call, ObjectProxy::TIMEOUT_USE_DEFAULT,
152 base::BindOnce(&ObjectManager::OnGetManagedObjects,
153 weak_ptr_factory_.GetWeakPtr()));
keybuk@chromium.org9cc40cb2013-03-25 18:20:08154}
155
armansitoebff093d2014-09-05 17:49:34156void ObjectManager::CleanUp() {
157 DCHECK(bus_);
158 bus_->AssertOnDBusThread();
159 DCHECK(!cleanup_called_);
160
161 cleanup_called_ = true;
162
163 if (!setup_success_)
164 return;
165
hashimotob3870312014-12-04 07:41:55166 bus_->RemoveFilterFunction(&ObjectManager::HandleMessageThunk, this);
armansitoebff093d2014-09-05 17:49:34167
168 ScopedDBusError error;
169 bus_->RemoveMatch(match_rule_, error.get());
170 if (error.is_set())
171 LOG(ERROR) << "Failed to remove match rule: " << match_rule_;
172
173 match_rule_.clear();
174}
175
armansitoebff093d2014-09-05 17:49:34176bool ObjectManager::SetupMatchRuleAndFilter() {
177 DCHECK(bus_);
178 DCHECK(!setup_success_);
179 bus_->AssertOnDBusThread();
180
181 if (cleanup_called_)
182 return false;
183
184 if (!bus_->Connect() || !bus_->SetUpAsyncOperations())
185 return false;
186
Bing Xue55502d8b2019-08-06 17:12:11187 // Try to get |service_name_owner_| from dbus if we haven't received any
188 // NameOwnerChanged signals.
189 if (service_name_owner_.empty()) {
190 service_name_owner_ =
191 bus_->GetServiceOwnerAndBlock(service_name_, Bus::SUPPRESS_ERRORS);
192 }
armansitoebff093d2014-09-05 17:49:34193
194 const std::string match_rule =
195 base::StringPrintf(
196 "type='signal', sender='%s', interface='%s', member='%s'",
197 service_name_.c_str(),
198 kPropertiesInterface,
199 kPropertiesChanged);
200
hashimotob3870312014-12-04 07:41:55201 bus_->AddFilterFunction(&ObjectManager::HandleMessageThunk, this);
armansitoebff093d2014-09-05 17:49:34202
203 ScopedDBusError error;
204 bus_->AddMatch(match_rule, error.get());
205 if (error.is_set()) {
206 LOG(ERROR) << "ObjectManager failed to add match rule \"" << match_rule
207 << "\". Got " << error.name() << ": " << error.message();
208 bus_->RemoveFilterFunction(&ObjectManager::HandleMessageThunk, this);
209 return false;
210 }
211
212 match_rule_ = match_rule;
213 setup_success_ = true;
214
215 return true;
216}
217
218void ObjectManager::OnSetupMatchRuleAndFilterComplete(bool success) {
derat2ef7d0ed2016-09-08 21:35:51219 if (!success) {
220 LOG(WARNING) << service_name_ << " " << object_path_.value()
221 << ": Failed to set up match rule.";
222 return;
223 }
224
225 DCHECK(bus_);
226 DCHECK(object_proxy_);
227 DCHECK(setup_success_);
Bing Xue55502d8b2019-08-06 17:12:11228 bus_->AssertOnOriginThread();
derat2ef7d0ed2016-09-08 21:35:51229
230 // |object_proxy_| is no longer valid if the Bus was shut down before this
231 // call. Don't initiate any other action from the origin thread.
232 if (cleanup_called_)
233 return;
234
235 object_proxy_->ConnectToSignal(
Reilly Grantd4e66132019-11-22 17:14:50236 kObjectManagerInterface, kObjectManagerInterfacesAdded,
237 base::BindRepeating(&ObjectManager::InterfacesAddedReceived,
238 weak_ptr_factory_.GetWeakPtr()),
239 base::BindOnce(&ObjectManager::InterfacesAddedConnected,
240 weak_ptr_factory_.GetWeakPtr()));
derat2ef7d0ed2016-09-08 21:35:51241
242 object_proxy_->ConnectToSignal(
Reilly Grantd4e66132019-11-22 17:14:50243 kObjectManagerInterface, kObjectManagerInterfacesRemoved,
244 base::BindRepeating(&ObjectManager::InterfacesRemovedReceived,
245 weak_ptr_factory_.GetWeakPtr()),
246 base::BindOnce(&ObjectManager::InterfacesRemovedConnected,
247 weak_ptr_factory_.GetWeakPtr()));
derat2ef7d0ed2016-09-08 21:35:51248
249 if (!service_name_owner_.empty())
250 GetManagedObjects();
armansitoebff093d2014-09-05 17:49:34251}
252
253// static
254DBusHandlerResult ObjectManager::HandleMessageThunk(DBusConnection* connection,
255 DBusMessage* raw_message,
256 void* user_data) {
257 ObjectManager* self = reinterpret_cast<ObjectManager*>(user_data);
258 return self->HandleMessage(connection, raw_message);
259}
260
261DBusHandlerResult ObjectManager::HandleMessage(DBusConnection* connection,
262 DBusMessage* raw_message) {
263 DCHECK(bus_);
264 bus_->AssertOnDBusThread();
265
satorux9b780e6b2015-08-17 09:44:35266 // Handle the message only if it is a signal.
267 // Note that the match rule in SetupMatchRuleAndFilter() is configured to
268 // only accept signals, but we check here just in case.
armansitoebff093d2014-09-05 17:49:34269 if (dbus_message_get_type(raw_message) != DBUS_MESSAGE_TYPE_SIGNAL)
270 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
271
272 // raw_message will be unrefed on exit of the function. Increment the
273 // reference so we can use it in Signal.
274 dbus_message_ref(raw_message);
dcheng2a193282016-04-08 22:55:04275 std::unique_ptr<Signal> signal(Signal::FromRawMessage(raw_message));
armansitoebff093d2014-09-05 17:49:34276
277 const std::string interface = signal->GetInterface();
278 const std::string member = signal->GetMember();
279
280 statistics::AddReceivedSignal(service_name_, interface, member);
281
satorux9b780e6b2015-08-17 09:44:35282 // Handle the signal only if it is PropertiesChanged.
283 // Note that the match rule in SetupMatchRuleAndFilter() is configured to
284 // only accept PropertiesChanged signals, but we check here just in case.
armansitoebff093d2014-09-05 17:49:34285 const std::string absolute_signal_name =
286 GetAbsoluteMemberName(interface, member);
287 const std::string properties_changed_signal_name =
288 GetAbsoluteMemberName(kPropertiesInterface, kPropertiesChanged);
289 if (absolute_signal_name != properties_changed_signal_name)
290 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
291
292 VLOG(1) << "Signal received: " << signal->ToString();
293
satorux9b780e6b2015-08-17 09:44:35294 // Handle the signal only if it is from the service that the ObjectManager
295 // instance is interested in.
296 // Note that the match rule in SetupMatchRuleAndFilter() is configured to
297 // only accept messages from the service name of our interest. However, the
298 // service='...' filter does not work as intended. See crbug.com/507206#14
299 // and #15 for details, hence it's necessary to check the sender here.
armansitoebff093d2014-09-05 17:49:34300 std::string sender = signal->GetSender();
satorux9b780e6b2015-08-17 09:44:35301 if (service_name_owner_ != sender)
armansitoebff093d2014-09-05 17:49:34302 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
armansitoebff093d2014-09-05 17:49:34303
304 const ObjectPath path = signal->GetPath();
305
306 if (bus_->HasDBusThread()) {
307 // Post a task to run the method in the origin thread. Transfer ownership of
308 // |signal| to NotifyPropertiesChanged, which will handle the clean up.
309 Signal* released_signal = signal.release();
310 bus_->GetOriginTaskRunner()->PostTask(
kylechar39c39282019-02-19 19:04:04311 FROM_HERE, base::BindOnce(&ObjectManager::NotifyPropertiesChanged, this,
312 path, released_signal));
armansitoebff093d2014-09-05 17:49:34313 } else {
314 // If the D-Bus thread is not used, just call the callback on the
315 // current thread. Transfer the ownership of |signal| to
316 // NotifyPropertiesChanged.
317 NotifyPropertiesChanged(path, signal.release());
318 }
319
320 // We don't return DBUS_HANDLER_RESULT_HANDLED for signals because other
321 // objects may be interested in them. (e.g. Signals from org.freedesktop.DBus)
322 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
323}
324
325void ObjectManager::NotifyPropertiesChanged(
326 const dbus::ObjectPath object_path,
327 Signal* signal) {
328 DCHECK(bus_);
329 bus_->AssertOnOriginThread();
330
331 NotifyPropertiesChangedHelper(object_path, signal);
332
333 // Delete the message on the D-Bus thread. See comments in HandleMessage.
334 bus_->GetDBusTaskRunner()->PostTask(
kylechar39c39282019-02-19 19:04:04335 FROM_HERE, base::BindOnce(&base::DeletePointer<Signal>, signal));
armansitoebff093d2014-09-05 17:49:34336}
337
338void ObjectManager::NotifyPropertiesChangedHelper(
339 const dbus::ObjectPath object_path,
340 Signal* signal) {
341 DCHECK(bus_);
342 bus_->AssertOnOriginThread();
343
344 MessageReader reader(signal);
345 std::string interface;
346 if (!reader.PopString(&interface)) {
347 LOG(WARNING) << "Property changed signal has wrong parameters: "
348 << "expected interface name: " << signal->ToString();
349 return;
350 }
351
352 PropertySet* properties = GetProperties(object_path, interface);
353 if (properties)
354 properties->ChangedReceived(signal);
355}
356
keybuk@chromium.org9cc40cb2013-03-25 18:20:08357void ObjectManager::OnGetManagedObjects(Response* response) {
Ben Chan14d50032017-11-09 20:20:16358 if (response != nullptr) {
keybuk@chromium.org9cc40cb2013-03-25 18:20:08359 MessageReader reader(response);
Ben Chan14d50032017-11-09 20:20:16360 MessageReader array_reader(nullptr);
keybuk@chromium.org9cc40cb2013-03-25 18:20:08361 if (!reader.PopArray(&array_reader))
362 return;
363
364 while (array_reader.HasMoreData()) {
Ben Chan14d50032017-11-09 20:20:16365 MessageReader dict_entry_reader(nullptr);
keybuk@chromium.org9cc40cb2013-03-25 18:20:08366 ObjectPath object_path;
367 if (!array_reader.PopDictEntry(&dict_entry_reader) ||
368 !dict_entry_reader.PopObjectPath(&object_path))
369 continue;
370
371 UpdateObject(object_path, &dict_entry_reader);
372 }
373
374 } else {
375 LOG(WARNING) << service_name_ << " " << object_path_.value()
376 << ": Failed to get managed objects";
377 }
378}
379
380void ObjectManager::InterfacesAddedReceived(Signal* signal) {
381 DCHECK(signal);
382 MessageReader reader(signal);
383 ObjectPath object_path;
384 if (!reader.PopObjectPath(&object_path)) {
385 LOG(WARNING) << service_name_ << " " << object_path_.value()
386 << ": InterfacesAdded signal has incorrect parameters: "
387 << signal->ToString();
388 return;
389 }
390
391 UpdateObject(object_path, &reader);
392}
393
394void ObjectManager::InterfacesAddedConnected(const std::string& interface_name,
395 const std::string& signal_name,
396 bool success) {
397 LOG_IF(WARNING, !success) << service_name_ << " " << object_path_.value()
398 << ": Failed to connect to InterfacesAdded signal.";
399}
400
401void ObjectManager::InterfacesRemovedReceived(Signal* signal) {
402 DCHECK(signal);
403 MessageReader reader(signal);
404 ObjectPath object_path;
405 std::vector<std::string> interface_names;
406 if (!reader.PopObjectPath(&object_path) ||
407 !reader.PopArrayOfStrings(&interface_names)) {
408 LOG(WARNING) << service_name_ << " " << object_path_.value()
409 << ": InterfacesRemoved signal has incorrect parameters: "
410 << signal->ToString();
411 return;
412 }
413
414 for (size_t i = 0; i < interface_names.size(); ++i)
415 RemoveInterface(object_path, interface_names[i]);
416}
417
418void ObjectManager::InterfacesRemovedConnected(
419 const std::string& interface_name,
420 const std::string& signal_name,
421 bool success) {
422 LOG_IF(WARNING, !success) << service_name_ << " " << object_path_.value()
423 << ": Failed to connect to "
424 << "InterfacesRemoved signal.";
425}
426
427void ObjectManager::UpdateObject(const ObjectPath& object_path,
428 MessageReader* reader) {
429 DCHECK(reader);
Ben Chan14d50032017-11-09 20:20:16430 MessageReader array_reader(nullptr);
keybuk@chromium.org9cc40cb2013-03-25 18:20:08431 if (!reader->PopArray(&array_reader))
432 return;
433
434 while (array_reader.HasMoreData()) {
Ben Chan14d50032017-11-09 20:20:16435 MessageReader dict_entry_reader(nullptr);
keybuk@chromium.org9cc40cb2013-03-25 18:20:08436 std::string interface_name;
437 if (!array_reader.PopDictEntry(&dict_entry_reader) ||
438 !dict_entry_reader.PopString(&interface_name))
439 continue;
440
441 AddInterface(object_path, interface_name, &dict_entry_reader);
442 }
443}
444
445
446void ObjectManager::AddInterface(const ObjectPath& object_path,
447 const std::string& interface_name,
448 MessageReader* reader) {
449 InterfaceMap::iterator iiter = interface_map_.find(interface_name);
450 if (iiter == interface_map_.end())
451 return;
452 Interface* interface = iiter->second;
453
454 ObjectMap::iterator oiter = object_map_.find(object_path);
455 Object* object;
456 if (oiter == object_map_.end()) {
457 object = object_map_[object_path] = new Object;
458 object->object_proxy = bus_->GetObjectProxy(service_name_, object_path);
459 } else
460 object = oiter->second;
461
462 Object::PropertiesMap::iterator piter =
463 object->properties_map.find(interface_name);
464 PropertySet* property_set;
465 const bool interface_added = (piter == object->properties_map.end());
466 if (interface_added) {
467 property_set = object->properties_map[interface_name] =
468 interface->CreateProperties(object->object_proxy,
469 object_path, interface_name);
keybuk@chromium.org9cc40cb2013-03-25 18:20:08470 } else
471 property_set = piter->second;
472
473 property_set->UpdatePropertiesFromReader(reader);
474
475 if (interface_added)
476 interface->ObjectAdded(object_path, interface_name);
477}
478
479void ObjectManager::RemoveInterface(const ObjectPath& object_path,
480 const std::string& interface_name) {
481 ObjectMap::iterator oiter = object_map_.find(object_path);
482 if (oiter == object_map_.end())
483 return;
484 Object* object = oiter->second;
485
486 Object::PropertiesMap::iterator piter =
487 object->properties_map.find(interface_name);
488 if (piter == object->properties_map.end())
489 return;
490
491 // Inform the interface before removing the properties structure or object
492 // in case it needs details from them to make its own decisions.
493 InterfaceMap::iterator iiter = interface_map_.find(interface_name);
494 if (iiter != interface_map_.end()) {
495 Interface* interface = iiter->second;
496 interface->ObjectRemoved(object_path, interface_name);
497 }
498
avakulenko9a99965a2015-04-02 18:51:32499 delete piter->second;
keybuk@chromium.org9cc40cb2013-03-25 18:20:08500 object->properties_map.erase(piter);
501
502 if (object->properties_map.empty()) {
503 object_map_.erase(oiter);
504 delete object;
505 }
506}
507
Bing Xue55502d8b2019-08-06 17:12:11508void ObjectManager::UpdateServiceNameOwner(const std::string& new_owner) {
509 bus_->AssertOnDBusThread();
510 service_name_owner_ = new_owner;
511}
512
keybuk@chromium.org043fb8c2014-03-07 02:24:33513void ObjectManager::NameOwnerChanged(const std::string& old_owner,
514 const std::string& new_owner) {
Bing Xue55502d8b2019-08-06 17:12:11515 bus_->AssertOnOriginThread();
516
517 bus_->GetDBusTaskRunner()->PostTask(
518 FROM_HERE,
519 base::BindOnce(&ObjectManager::UpdateServiceNameOwner, this, new_owner));
armansitoebff093d2014-09-05 17:49:34520
keybuk@chromium.org043fb8c2014-03-07 02:24:33521 if (!old_owner.empty()) {
522 ObjectMap::iterator iter = object_map_.begin();
523 while (iter != object_map_.end()) {
524 ObjectMap::iterator tmp = iter;
525 ++iter;
526
527 // PropertiesMap is mutated by RemoveInterface, and also Object is
528 // destroyed; easier to collect the object path and interface names
529 // and remove them safely.
530 const dbus::ObjectPath object_path = tmp->first;
531 Object* object = tmp->second;
532 std::vector<std::string> interfaces;
533
534 for (Object::PropertiesMap::iterator piter =
535 object->properties_map.begin();
536 piter != object->properties_map.end(); ++piter)
537 interfaces.push_back(piter->first);
538
539 for (std::vector<std::string>::iterator iiter = interfaces.begin();
540 iiter != interfaces.end(); ++iiter)
541 RemoveInterface(object_path, *iiter);
542 }
543
544 }
545
546 if (!new_owner.empty())
547 GetManagedObjects();
548}
549
keybuk@chromium.org9cc40cb2013-03-25 18:20:08550} // namespace dbus