1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
11 #include "base/containers/contains.h"
12 #include "base/files/file_descriptor_watcher_posix.h"
13 #include "base/functional/bind.h"
14 #include "base/logging.h"
15 #include "base/memory/raw_ptr.h"
16 #include "base/memory/weak_ptr.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/synchronization/waitable_event.h"
19 #include "base/task/sequenced_task_runner.h"
20 #include "base/threading/scoped_blocking_call.h"
21 #include "base/threading/thread.h"
22 #include "base/threading/thread_restrictions.h"
23 #include "base/time/time.h"
24 #include "base/timer/elapsed_timer.h"
25 #include "dbus/error.h"
26 #include "dbus/exported_object.h"
27 #include "dbus/message.h"
28 #include "dbus/object_manager.h"
29 #include "dbus/object_path.h"
30 #include "dbus/object_proxy.h"
31 #include "dbus/scoped_dbus_error.h"
37 const char kDisconnectedSignal[] = "Disconnected";
38 const char kDisconnectedMatchRule[] =
39 "type='signal', path='/org/freedesktop/DBus/Local',"
40 "interface='org.freedesktop.DBus.Local', member='Disconnected'";
42 // The NameOwnerChanged member in org.freedesktop.DBus
43 const char kNameOwnerChangedSignal[] = "NameOwnerChanged";
45 // The match rule used to filter for changes to a given service name owner.
46 const char kServiceNameOwnerChangeMatchRule[] =
47 "type='signal',interface='org.freedesktop.DBus',"
48 "member='NameOwnerChanged',path='/org/freedesktop/DBus',"
49 "sender='org.freedesktop.DBus',arg0='%s'";
51 // The class is used for watching the file descriptor used for D-Bus
55 explicit Watch(DBusWatch* watch) : raw_watch_(watch) {
56 dbus_watch_set_data(raw_watch_, this, nullptr);
59 Watch(const Watch&) = delete;
60 Watch& operator=(const Watch&) = delete;
62 ~Watch() { dbus_watch_set_data(raw_watch_, nullptr, nullptr); }
64 // Returns true if the underlying file descriptor is ready to be watched.
65 bool IsReadyToBeWatched() {
66 return dbus_watch_get_enabled(raw_watch_);
69 // Starts watching the underlying file descriptor.
70 void StartWatching() {
71 const int file_descriptor = dbus_watch_get_unix_fd(raw_watch_);
72 const unsigned int flags = dbus_watch_get_flags(raw_watch_);
74 // Using base::Unretained(this) is safe because watches are automatically
75 // canceled when |read_watcher_| and |write_watcher_| are destroyed.
76 if (flags & DBUS_WATCH_READABLE) {
77 read_watcher_ = base::FileDescriptorWatcher::WatchReadable(
79 base::BindRepeating(&Watch::OnFileReady, base::Unretained(this),
80 DBUS_WATCH_READABLE));
82 if (flags & DBUS_WATCH_WRITABLE) {
83 write_watcher_ = base::FileDescriptorWatcher::WatchWritable(
85 base::BindRepeating(&Watch::OnFileReady, base::Unretained(this),
86 DBUS_WATCH_WRITABLE));
90 // Stops watching the underlying file descriptor.
92 read_watcher_.reset();
93 write_watcher_.reset();
97 void OnFileReady(unsigned int flags) {
98 CHECK(dbus_watch_handle(raw_watch_, flags)) << "Unable to allocate memory";
101 raw_ptr<DBusWatch> raw_watch_;
102 std::unique_ptr<base::FileDescriptorWatcher::Controller> read_watcher_;
103 std::unique_ptr<base::FileDescriptorWatcher::Controller> write_watcher_;
106 // The class is used for monitoring the timeout used for D-Bus method
110 explicit Timeout(DBusTimeout* timeout) : raw_timeout_(timeout) {
111 // Associated |this| with the underlying DBusTimeout.
112 dbus_timeout_set_data(raw_timeout_, this, nullptr);
115 Timeout(const Timeout&) = delete;
116 Timeout& operator=(const Timeout&) = delete;
119 // Remove the association between |this| and the |raw_timeout_|.
120 dbus_timeout_set_data(raw_timeout_, nullptr, nullptr);
123 // Returns true if the timeout is ready to be monitored.
124 bool IsReadyToBeMonitored() {
125 return dbus_timeout_get_enabled(raw_timeout_);
128 // Starts monitoring the timeout.
129 void StartMonitoring(Bus* bus) {
130 bus->GetDBusTaskRunner()->PostDelayedTask(
132 base::BindOnce(&Timeout::HandleTimeout, weak_ptr_factory_.GetWeakPtr()),
136 // Stops monitoring the timeout.
137 void StopMonitoring() { weak_ptr_factory_.InvalidateWeakPtrs(); }
139 base::TimeDelta GetInterval() {
140 return base::Milliseconds(dbus_timeout_get_interval(raw_timeout_));
144 // Calls DBus to handle the timeout.
145 void HandleTimeout() { CHECK(dbus_timeout_handle(raw_timeout_)); }
147 raw_ptr<DBusTimeout> raw_timeout_;
149 base::WeakPtrFactory<Timeout> weak_ptr_factory_{this};
152 // Converts DBusError into dbus::Error.
153 Error ToError(const internal::ScopedDBusError& error) {
154 return error.is_set() ? Error(error.name(), error.message()) : Error();
159 Bus::Options::Options()
161 connection_type(PRIVATE) {
164 Bus::Options::~Options() = default;
166 Bus::Options::Options(Bus::Options&&) = default;
168 Bus::Options& Bus::Options::operator=(Bus::Options&&) = default;
170 Bus::Bus(const Options& options)
171 : bus_type_(options.bus_type),
172 connection_type_(options.connection_type),
173 dbus_task_runner_(options.dbus_task_runner),
174 on_shutdown_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
175 base::WaitableEvent::InitialState::NOT_SIGNALED),
176 connection_(nullptr),
177 origin_thread_id_(base::PlatformThread::CurrentId()),
178 async_operations_set_up_(false),
179 shutdown_completed_(false),
180 num_pending_watches_(0),
181 num_pending_timeouts_(0),
182 address_(options.address) {
183 // This is safe to call multiple times.
184 dbus_threads_init_default();
185 // The origin message loop is unnecessary if the client uses synchronous
187 if (base::SequencedTaskRunner::HasCurrentDefault())
188 origin_task_runner_ = base::SequencedTaskRunner::GetCurrentDefault();
192 DCHECK(!connection_);
193 DCHECK(owned_service_names_.empty());
194 DCHECK(match_rules_added_.empty());
195 DCHECK(filter_functions_added_.empty());
196 DCHECK(registered_object_paths_.empty());
197 DCHECK_EQ(0, num_pending_watches_);
198 // TODO(satorux): This check fails occasionally in browser_tests for tests
199 // that run very quickly. Perhaps something does not have time to clean up.
200 // Despite the check failing, the tests seem to run fine. crosbug.com/23416
201 // DCHECK_EQ(0, num_pending_timeouts_);
204 ObjectProxy* Bus::GetObjectProxy(const std::string& service_name,
205 const ObjectPath& object_path) {
206 return GetObjectProxyWithOptions(service_name, object_path,
207 ObjectProxy::DEFAULT_OPTIONS);
210 ObjectProxy* Bus::GetObjectProxyWithOptions(const std::string& service_name,
211 const ObjectPath& object_path,
213 AssertOnOriginThread();
215 // Check if we already have the requested object proxy.
216 const ObjectProxyTable::key_type key(service_name + object_path.value(),
218 ObjectProxyTable::iterator iter = object_proxy_table_.find(key);
219 if (iter != object_proxy_table_.end()) {
220 return iter->second.get();
223 scoped_refptr<ObjectProxy> object_proxy =
224 new ObjectProxy(this, service_name, object_path, options);
225 object_proxy_table_[key] = object_proxy;
227 return object_proxy.get();
230 bool Bus::RemoveObjectProxy(const std::string& service_name,
231 const ObjectPath& object_path,
232 base::OnceClosure callback) {
233 return RemoveObjectProxyWithOptions(service_name, object_path,
234 ObjectProxy::DEFAULT_OPTIONS,
235 std::move(callback));
238 bool Bus::RemoveObjectProxyWithOptions(const std::string& service_name,
239 const ObjectPath& object_path,
241 base::OnceClosure callback) {
242 AssertOnOriginThread();
244 // Check if we have the requested object proxy.
245 const ObjectProxyTable::key_type key(service_name + object_path.value(),
247 ObjectProxyTable::iterator iter = object_proxy_table_.find(key);
248 if (iter != object_proxy_table_.end()) {
249 scoped_refptr<ObjectProxy> object_proxy = iter->second;
250 object_proxy_table_.erase(iter);
251 // Object is present. Remove it now and Detach on the DBus thread.
252 GetDBusTaskRunner()->PostTask(
253 FROM_HERE, base::BindOnce(&Bus::RemoveObjectProxyInternal, this,
254 object_proxy, std::move(callback)));
260 void Bus::RemoveObjectProxyInternal(scoped_refptr<ObjectProxy> object_proxy,
261 base::OnceClosure callback) {
262 AssertOnDBusThread();
264 object_proxy->Detach();
266 GetOriginTaskRunner()->PostTask(FROM_HERE, std::move(callback));
269 ExportedObject* Bus::GetExportedObject(const ObjectPath& object_path) {
270 AssertOnOriginThread();
272 // Check if we already have the requested exported object.
273 ExportedObjectTable::iterator iter = exported_object_table_.find(object_path);
274 if (iter != exported_object_table_.end()) {
275 return iter->second.get();
278 scoped_refptr<ExportedObject> exported_object =
279 new ExportedObject(this, object_path);
280 exported_object_table_[object_path] = exported_object;
282 return exported_object.get();
285 void Bus::UnregisterExportedObject(const ObjectPath& object_path) {
286 AssertOnOriginThread();
288 // Remove the registered object from the table first, to allow a new
289 // GetExportedObject() call to return a new object, rather than this one.
290 ExportedObjectTable::iterator iter = exported_object_table_.find(object_path);
291 if (iter == exported_object_table_.end())
294 scoped_refptr<ExportedObject> exported_object = iter->second;
295 exported_object_table_.erase(iter);
297 // Post the task to perform the final unregistration to the D-Bus thread.
298 // Since the registration also happens on the D-Bus thread in
299 // TryRegisterObjectPath(), and the task runner we post to is a
300 // SequencedTaskRunner, there is a guarantee that this will happen before any
301 // future registration call.
302 GetDBusTaskRunner()->PostTask(
303 FROM_HERE, base::BindOnce(&Bus::UnregisterExportedObjectInternal, this,
307 void Bus::UnregisterExportedObjectInternal(
308 scoped_refptr<ExportedObject> exported_object) {
309 AssertOnDBusThread();
311 exported_object->Unregister();
314 ObjectManager* Bus::GetObjectManager(const std::string& service_name,
315 const ObjectPath& object_path) {
316 AssertOnOriginThread();
318 // Check if we already have the requested object manager.
319 const ObjectManagerTable::key_type key(service_name + object_path.value());
320 ObjectManagerTable::iterator iter = object_manager_table_.find(key);
321 if (iter != object_manager_table_.end()) {
322 return iter->second.get();
325 scoped_refptr<ObjectManager> object_manager =
326 ObjectManager::Create(this, service_name, object_path);
327 object_manager_table_[key] = object_manager;
329 return object_manager.get();
332 bool Bus::RemoveObjectManager(const std::string& service_name,
333 const ObjectPath& object_path,
334 base::OnceClosure callback) {
335 AssertOnOriginThread();
336 DCHECK(!callback.is_null());
338 const ObjectManagerTable::key_type key(service_name + object_path.value());
339 ObjectManagerTable::iterator iter = object_manager_table_.find(key);
340 if (iter == object_manager_table_.end())
343 // ObjectManager is present. Remove it now and CleanUp on the DBus thread.
344 scoped_refptr<ObjectManager> object_manager = iter->second;
345 object_manager_table_.erase(iter);
347 GetDBusTaskRunner()->PostTask(
348 FROM_HERE, base::BindOnce(&Bus::RemoveObjectManagerInternal, this,
349 object_manager, std::move(callback)));
354 void Bus::RemoveObjectManagerInternal(
355 scoped_refptr<dbus::ObjectManager> object_manager,
356 base::OnceClosure callback) {
357 AssertOnDBusThread();
358 DCHECK(object_manager.get());
360 object_manager->CleanUp();
362 // The ObjectManager has to be deleted on the origin thread since it was
364 GetOriginTaskRunner()->PostTask(
365 FROM_HERE, base::BindOnce(&Bus::RemoveObjectManagerInternalHelper, this,
366 object_manager, std::move(callback)));
369 void Bus::RemoveObjectManagerInternalHelper(
370 scoped_refptr<dbus::ObjectManager> object_manager,
371 base::OnceClosure callback) {
372 AssertOnOriginThread();
373 DCHECK(object_manager);
375 // Release the object manager and run the callback.
376 object_manager = nullptr;
377 std::move(callback).Run();
380 bool Bus::Connect() {
381 // dbus_bus_get_private() and dbus_bus_get() are blocking calls.
382 AssertOnDBusThread();
383 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
384 base::BlockingType::MAY_BLOCK);
386 // Check if it's already initialized.
390 internal::ScopedDBusError dbus_error;
391 if (bus_type_ == CUSTOM_ADDRESS) {
392 if (connection_type_ == PRIVATE) {
394 dbus_connection_open_private(address_.c_str(), dbus_error.get());
396 connection_ = dbus_connection_open(address_.c_str(), dbus_error.get());
399 const DBusBusType dbus_bus_type = static_cast<DBusBusType>(bus_type_);
400 if (connection_type_ == PRIVATE) {
401 connection_ = dbus_bus_get_private(dbus_bus_type, dbus_error.get());
403 connection_ = dbus_bus_get(dbus_bus_type, dbus_error.get());
407 LOG(ERROR) << "Failed to connect to the bus: "
408 << (dbus_error.is_set() ? dbus_error.message() : "");
412 if (bus_type_ == CUSTOM_ADDRESS) {
413 // We should call dbus_bus_register here, otherwise unique name can not be
414 // acquired. According to dbus specification, it is responsible to call
415 // org.freedesktop.DBus.Hello method at the beging of bus connection to
416 // acquire unique name. In the case of dbus_bus_get, dbus_bus_register is
417 // called internally.
418 if (!dbus_bus_register(connection_, dbus_error.get())) {
419 LOG(ERROR) << "Failed to register the bus component: "
420 << (dbus_error.is_set() ? dbus_error.message() : "");
424 // We shouldn't exit on the disconnected signal.
425 dbus_connection_set_exit_on_disconnect(connection_, false);
427 // Watch Disconnected signal.
428 AddFilterFunction(Bus::OnConnectionDisconnectedFilter, this);
430 AddMatch(kDisconnectedMatchRule, &error);
435 void Bus::ClosePrivateConnection() {
436 // dbus_connection_close is blocking call.
437 AssertOnDBusThread();
438 DCHECK_EQ(PRIVATE, connection_type_)
439 << "non-private connection should not be closed";
440 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
441 base::BlockingType::MAY_BLOCK);
442 dbus_connection_close(connection_);
445 void Bus::ShutdownAndBlock() {
446 AssertOnDBusThread();
448 if (shutdown_completed_)
449 return; // Already shutdowned, just return.
451 // Unregister the exported objects.
452 for (ExportedObjectTable::iterator iter = exported_object_table_.begin();
453 iter != exported_object_table_.end(); ++iter) {
454 iter->second->Unregister();
457 // Release all service names.
458 for (std::set<std::string>::iterator iter = owned_service_names_.begin();
459 iter != owned_service_names_.end();) {
460 // This is a bit tricky but we should increment the iter here as
461 // ReleaseOwnership() may remove |service_name| from the set.
462 const std::string& service_name = *iter++;
463 ReleaseOwnership(service_name);
465 if (!owned_service_names_.empty()) {
466 LOG(ERROR) << "Failed to release all service names. # of services left: "
467 << owned_service_names_.size();
470 // Detach from the remote objects.
471 for (ObjectProxyTable::iterator iter = object_proxy_table_.begin();
472 iter != object_proxy_table_.end(); ++iter) {
473 iter->second->Detach();
476 // Clean up the object managers.
477 for (ObjectManagerTable::iterator iter = object_manager_table_.begin();
478 iter != object_manager_table_.end(); ++iter) {
479 iter->second->CleanUp();
482 // Release object proxies and exported objects here. We should do this
483 // here rather than in the destructor to avoid memory leaks due to
484 // cyclic references.
485 object_proxy_table_.clear();
486 exported_object_table_.clear();
488 // Private connection should be closed.
490 base::ScopedBlockingCall scoped_blocking_call(
491 FROM_HERE, base::BlockingType::MAY_BLOCK);
493 // Remove Disconnected watcher.
495 RemoveFilterFunction(Bus::OnConnectionDisconnectedFilter, this);
496 RemoveMatch(kDisconnectedMatchRule, &error);
498 if (connection_type_ == PRIVATE)
499 ClosePrivateConnection();
500 // dbus_connection_close() won't unref.
501 dbus_connection_unref(connection_);
504 connection_ = nullptr;
505 shutdown_completed_ = true;
508 void Bus::ShutdownOnDBusThreadAndBlock() {
509 AssertOnOriginThread();
510 DCHECK(dbus_task_runner_);
512 GetDBusTaskRunner()->PostTask(
514 base::BindOnce(&Bus::ShutdownOnDBusThreadAndBlockInternal, this));
516 // http://crbug.com/125222
517 base::ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_wait;
519 // Wait until the shutdown is complete on the D-Bus thread.
520 // The shutdown should not hang, but set timeout just in case.
521 const int kTimeoutSecs = 3;
522 const base::TimeDelta timeout(base::Seconds(kTimeoutSecs));
523 const bool signaled = on_shutdown_.TimedWait(timeout);
524 LOG_IF(ERROR, !signaled) << "Failed to shutdown the bus";
527 void Bus::RequestOwnership(const std::string& service_name,
528 ServiceOwnershipOptions options,
529 OnOwnershipCallback on_ownership_callback) {
530 AssertOnOriginThread();
532 GetDBusTaskRunner()->PostTask(
534 base::BindOnce(&Bus::RequestOwnershipInternal, this, service_name,
535 options, std::move(on_ownership_callback)));
538 void Bus::RequestOwnershipInternal(const std::string& service_name,
539 ServiceOwnershipOptions options,
540 OnOwnershipCallback on_ownership_callback) {
541 AssertOnDBusThread();
543 bool success = Connect();
545 success = RequestOwnershipAndBlock(service_name, options);
547 GetOriginTaskRunner()->PostTask(
549 base::BindOnce(std::move(on_ownership_callback), service_name, success));
552 bool Bus::RequestOwnershipAndBlock(const std::string& service_name,
553 ServiceOwnershipOptions options) {
555 // dbus_bus_request_name() is a blocking call.
556 AssertOnDBusThread();
558 // Check if we already own the service name.
559 if (base::Contains(owned_service_names_, service_name)) {
563 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
564 base::BlockingType::MAY_BLOCK);
565 internal::ScopedDBusError error;
566 const int result = dbus_bus_request_name(connection_,
567 service_name.c_str(),
570 if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
571 LOG(ERROR) << "Failed to get the ownership of " << service_name << ": "
572 << (error.is_set() ? error.message() : "");
575 owned_service_names_.insert(service_name);
579 bool Bus::ReleaseOwnership(const std::string& service_name) {
581 // dbus_bus_release_name() is a blocking call.
582 AssertOnDBusThread();
584 // Check if we already own the service name.
585 std::set<std::string>::iterator found =
586 owned_service_names_.find(service_name);
587 if (found == owned_service_names_.end()) {
588 LOG(ERROR) << service_name << " is not owned by the bus";
592 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
593 base::BlockingType::MAY_BLOCK);
594 internal::ScopedDBusError error;
595 const int result = dbus_bus_release_name(connection_, service_name.c_str(),
597 if (result == DBUS_RELEASE_NAME_REPLY_RELEASED) {
598 owned_service_names_.erase(found);
601 LOG(ERROR) << "Failed to release the ownership of " << service_name << ": "
602 << (error.is_set() ? error.message() : "")
603 << ", result code: " << result;
608 bool Bus::SetUpAsyncOperations() {
610 AssertOnDBusThread();
612 if (async_operations_set_up_)
615 // Process all the incoming data if any, so that OnDispatchStatus() will
616 // be called when the incoming data is ready.
617 ProcessAllIncomingDataIfAny();
619 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
620 base::BlockingType::MAY_BLOCK);
621 bool success = dbus_connection_set_watch_functions(
622 connection_, &Bus::OnAddWatchThunk, &Bus::OnRemoveWatchThunk,
623 &Bus::OnToggleWatchThunk, this, nullptr);
624 CHECK(success) << "Unable to allocate memory";
626 success = dbus_connection_set_timeout_functions(
627 connection_, &Bus::OnAddTimeoutThunk, &Bus::OnRemoveTimeoutThunk,
628 &Bus::OnToggleTimeoutThunk, this, nullptr);
629 CHECK(success) << "Unable to allocate memory";
631 dbus_connection_set_dispatch_status_function(
632 connection_, &Bus::OnDispatchStatusChangedThunk, this, nullptr);
634 async_operations_set_up_ = true;
639 base::expected<std::unique_ptr<Response>, Error> Bus::SendWithReplyAndBlock(
640 DBusMessage* request,
643 AssertOnDBusThread();
645 base::ElapsedTimer elapsed;
647 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
648 base::BlockingType::MAY_BLOCK);
649 internal::ScopedDBusError dbus_error;
650 DBusMessage* reply = dbus_connection_send_with_reply_and_block(
651 connection_, request, timeout_ms, dbus_error.get());
652 constexpr base::TimeDelta kLongCall = base::Seconds(1);
653 LOG_IF(WARNING, elapsed.Elapsed() >= kLongCall)
654 << "Bus::SendWithReplyAndBlock took "
655 << elapsed.Elapsed().InMilliseconds() << "ms to process message: "
656 << "type=" << dbus_message_type_to_string(dbus_message_get_type(request))
657 << ", path=" << dbus_message_get_path(request)
658 << ", interface=" << dbus_message_get_interface(request)
659 << ", member=" << dbus_message_get_member(request);
662 return base::unexpected(ToError(dbus_error));
665 return base::ok(Response::FromRawMessage(reply));
668 void Bus::SendWithReply(DBusMessage* request,
669 DBusPendingCall** pending_call,
672 AssertOnDBusThread();
674 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
675 base::BlockingType::MAY_BLOCK);
676 const bool success = dbus_connection_send_with_reply(
677 connection_, request, pending_call, timeout_ms);
678 CHECK(success) << "Unable to allocate memory";
681 void Bus::Send(DBusMessage* request, uint32_t* serial) {
683 AssertOnDBusThread();
685 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
686 base::BlockingType::MAY_BLOCK);
687 const bool success = dbus_connection_send(connection_, request, serial);
688 CHECK(success) << "Unable to allocate memory";
691 void Bus::AddFilterFunction(DBusHandleMessageFunction filter_function,
694 AssertOnDBusThread();
696 std::pair<DBusHandleMessageFunction, void*> filter_data_pair =
697 std::make_pair(filter_function, user_data);
698 if (base::Contains(filter_functions_added_, filter_data_pair)) {
699 VLOG(1) << "Filter function already exists: " << filter_function
700 << " with associated data: " << user_data;
704 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
705 base::BlockingType::MAY_BLOCK);
706 const bool success = dbus_connection_add_filter(connection_, filter_function,
708 CHECK(success) << "Unable to allocate memory";
709 filter_functions_added_.insert(filter_data_pair);
712 void Bus::RemoveFilterFunction(DBusHandleMessageFunction filter_function,
715 AssertOnDBusThread();
717 std::pair<DBusHandleMessageFunction, void*> filter_data_pair =
718 std::make_pair(filter_function, user_data);
719 if (!base::Contains(filter_functions_added_, filter_data_pair)) {
720 VLOG(1) << "Requested to remove an unknown filter function: "
722 << " with associated data: " << user_data;
726 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
727 base::BlockingType::MAY_BLOCK);
728 dbus_connection_remove_filter(connection_, filter_function, user_data);
729 filter_functions_added_.erase(filter_data_pair);
732 void Bus::AddMatch(const std::string& match_rule, Error* error) {
735 AssertOnDBusThread();
737 std::map<std::string, int>::iterator iter =
738 match_rules_added_.find(match_rule);
739 if (iter != match_rules_added_.end()) {
740 // The already existing rule's counter is incremented.
743 VLOG(1) << "Match rule already exists: " << match_rule;
747 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
748 base::BlockingType::MAY_BLOCK);
749 internal::ScopedDBusError dbus_error;
750 dbus_bus_add_match(connection_, match_rule.c_str(), dbus_error.get());
751 if (dbus_error.is_set()) {
752 *error = Error(dbus_error.name(), dbus_error.message());
754 match_rules_added_[match_rule] = 1;
757 bool Bus::RemoveMatch(const std::string& match_rule, Error* error) {
760 AssertOnDBusThread();
762 std::map<std::string, int>::iterator iter =
763 match_rules_added_.find(match_rule);
764 if (iter == match_rules_added_.end()) {
765 LOG(ERROR) << "Requested to remove an unknown match rule: " << match_rule;
769 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
770 base::BlockingType::MAY_BLOCK);
771 // The rule's counter is decremented and the rule is deleted when reachs 0.
773 if (iter->second == 0) {
774 internal::ScopedDBusError dbus_error;
775 dbus_bus_remove_match(connection_, match_rule.c_str(), dbus_error.get());
776 if (dbus_error.is_set()) {
777 *error = Error(dbus_error.name(), dbus_error.message());
779 match_rules_added_.erase(match_rule);
784 bool Bus::TryRegisterObjectPath(const ObjectPath& object_path,
785 const DBusObjectPathVTable* vtable,
788 return TryRegisterObjectPathInternal(
789 object_path, vtable, user_data, error,
790 dbus_connection_try_register_object_path);
793 bool Bus::TryRegisterFallback(const ObjectPath& object_path,
794 const DBusObjectPathVTable* vtable,
798 return TryRegisterObjectPathInternal(object_path, vtable, user_data, error,
799 dbus_connection_try_register_fallback);
802 bool Bus::TryRegisterObjectPathInternal(
803 const ObjectPath& object_path,
804 const DBusObjectPathVTable* vtable,
807 TryRegisterObjectPathFunction* register_function) {
810 AssertOnDBusThread();
811 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
812 base::BlockingType::MAY_BLOCK);
814 if (base::Contains(registered_object_paths_, object_path)) {
815 LOG(ERROR) << "Object path already registered: " << object_path.value();
819 internal::ScopedDBusError dbus_error;
821 register_function(connection_, object_path.value().c_str(), vtable,
822 user_data, dbus_error.get());
824 registered_object_paths_.insert(object_path);
825 } else if (dbus_error.is_set()) {
826 *error = Error(dbus_error.name(), dbus_error.message());
831 void Bus::UnregisterObjectPath(const ObjectPath& object_path) {
833 AssertOnDBusThread();
835 if (!base::Contains(registered_object_paths_, object_path)) {
836 LOG(ERROR) << "Requested to unregister an unknown object path: "
837 << object_path.value();
841 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
842 base::BlockingType::MAY_BLOCK);
843 const bool success = dbus_connection_unregister_object_path(
845 object_path.value().c_str());
846 CHECK(success) << "Unable to allocate memory";
847 registered_object_paths_.erase(object_path);
850 void Bus::ShutdownOnDBusThreadAndBlockInternal() {
851 AssertOnDBusThread();
854 on_shutdown_.Signal();
857 void Bus::ProcessAllIncomingDataIfAny() {
858 AssertOnDBusThread();
860 // As mentioned at the class comment in .h file, connection_ can be NULL.
864 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
865 base::BlockingType::MAY_BLOCK);
867 // It is safe and necessary to call dbus_connection_get_dispatch_status even
868 // if the connection is lost.
869 if (dbus_connection_get_dispatch_status(connection_) ==
870 DBUS_DISPATCH_DATA_REMAINS) {
871 while (dbus_connection_dispatch(connection_) ==
872 DBUS_DISPATCH_DATA_REMAINS) {
877 base::SequencedTaskRunner* Bus::GetDBusTaskRunner() {
878 if (dbus_task_runner_)
879 return dbus_task_runner_.get();
881 return GetOriginTaskRunner();
884 base::SequencedTaskRunner* Bus::GetOriginTaskRunner() {
885 DCHECK(origin_task_runner_);
886 return origin_task_runner_.get();
889 bool Bus::HasDBusThread() {
890 return dbus_task_runner_ != nullptr;
893 void Bus::AssertOnOriginThread() {
894 if (origin_task_runner_) {
895 CHECK(origin_task_runner_->RunsTasksInCurrentSequence());
897 CHECK_EQ(origin_thread_id_, base::PlatformThread::CurrentId());
901 void Bus::AssertOnDBusThread() {
902 if (dbus_task_runner_) {
903 CHECK(dbus_task_runner_->RunsTasksInCurrentSequence());
905 AssertOnOriginThread();
909 std::string Bus::GetServiceOwnerAndBlock(const std::string& service_name,
910 GetServiceOwnerOption options) {
911 AssertOnDBusThread();
913 MethodCall get_name_owner_call("org.freedesktop.DBus", "GetNameOwner");
914 MessageWriter writer(&get_name_owner_call);
915 writer.AppendString(service_name);
916 VLOG(1) << "Method call: " << get_name_owner_call.ToString();
918 const ObjectPath obj_path("/org/freedesktop/DBus");
919 if (!get_name_owner_call.SetDestination("org.freedesktop.DBus") ||
920 !get_name_owner_call.SetPath(obj_path)) {
921 if (options == REPORT_ERRORS)
922 LOG(ERROR) << "Failed to get name owner.";
926 auto result = SendWithReplyAndBlock(get_name_owner_call.raw_message(),
927 ObjectProxy::TIMEOUT_USE_DEFAULT);
928 if (!result.has_value()) {
929 if (options == REPORT_ERRORS) {
930 LOG(ERROR) << "Failed to get name owner. Got " << result.error().name()
931 << ": " << result.error().message();
936 MessageReader reader(result->get());
937 std::string service_owner;
938 if (!reader.PopString(&service_owner))
939 service_owner.clear();
940 return service_owner;
943 void Bus::GetServiceOwner(const std::string& service_name,
944 GetServiceOwnerCallback callback) {
945 AssertOnOriginThread();
947 GetDBusTaskRunner()->PostTask(
948 FROM_HERE, base::BindOnce(&Bus::GetServiceOwnerInternal, this,
949 service_name, std::move(callback)));
952 void Bus::GetServiceOwnerInternal(const std::string& service_name,
953 GetServiceOwnerCallback callback) {
954 AssertOnDBusThread();
956 std::string service_owner;
958 service_owner = GetServiceOwnerAndBlock(service_name, SUPPRESS_ERRORS);
959 GetOriginTaskRunner()->PostTask(
960 FROM_HERE, base::BindOnce(std::move(callback), service_owner));
963 void Bus::ListenForServiceOwnerChange(
964 const std::string& service_name,
965 const ServiceOwnerChangeCallback& callback) {
966 AssertOnOriginThread();
967 DCHECK(!service_name.empty());
968 DCHECK(!callback.is_null());
970 GetDBusTaskRunner()->PostTask(
971 FROM_HERE, base::BindOnce(&Bus::ListenForServiceOwnerChangeInternal, this,
972 service_name, callback));
975 void Bus::ListenForServiceOwnerChangeInternal(
976 const std::string& service_name,
977 const ServiceOwnerChangeCallback& callback) {
978 AssertOnDBusThread();
979 DCHECK(!service_name.empty());
980 DCHECK(!callback.is_null());
982 if (!Connect() || !SetUpAsyncOperations())
985 if (service_owner_changed_listener_map_.empty())
986 AddFilterFunction(Bus::OnServiceOwnerChangedFilter, this);
988 ServiceOwnerChangedListenerMap::iterator it =
989 service_owner_changed_listener_map_.find(service_name);
990 if (it == service_owner_changed_listener_map_.end()) {
991 // Add a match rule for the new service name.
992 const std::string name_owner_changed_match_rule =
993 base::StringPrintf(kServiceNameOwnerChangeMatchRule,
994 service_name.c_str());
996 AddMatch(name_owner_changed_match_rule, &error);
997 if (error.IsValid()) {
998 LOG(ERROR) << "Failed to add match rule for " << service_name
999 << ". Got " << error.name() << ": " << error.message();
1003 service_owner_changed_listener_map_[service_name].push_back(callback);
1007 // Check if the callback has already been added.
1008 std::vector<ServiceOwnerChangeCallback>& callbacks = it->second;
1009 for (size_t i = 0; i < callbacks.size(); ++i) {
1010 if (callbacks[i] == callback)
1013 callbacks.push_back(callback);
1016 void Bus::UnlistenForServiceOwnerChange(
1017 const std::string& service_name,
1018 const ServiceOwnerChangeCallback& callback) {
1019 AssertOnOriginThread();
1020 DCHECK(!service_name.empty());
1021 DCHECK(!callback.is_null());
1023 GetDBusTaskRunner()->PostTask(
1024 FROM_HERE, base::BindOnce(&Bus::UnlistenForServiceOwnerChangeInternal,
1025 this, service_name, callback));
1028 void Bus::UnlistenForServiceOwnerChangeInternal(
1029 const std::string& service_name,
1030 const ServiceOwnerChangeCallback& callback) {
1031 AssertOnDBusThread();
1032 DCHECK(!service_name.empty());
1033 DCHECK(!callback.is_null());
1035 ServiceOwnerChangedListenerMap::iterator it =
1036 service_owner_changed_listener_map_.find(service_name);
1037 if (it == service_owner_changed_listener_map_.end())
1040 std::vector<ServiceOwnerChangeCallback>& callbacks = it->second;
1041 for (size_t i = 0; i < callbacks.size(); ++i) {
1042 if (callbacks[i] == callback) {
1043 callbacks.erase(callbacks.begin() + i);
1044 break; // There can be only one.
1047 if (!callbacks.empty())
1050 // Last callback for |service_name| has been removed, remove match rule.
1051 const std::string name_owner_changed_match_rule =
1052 base::StringPrintf(kServiceNameOwnerChangeMatchRule,
1053 service_name.c_str());
1055 RemoveMatch(name_owner_changed_match_rule, &error);
1056 // And remove |service_owner_changed_lister_map_| entry.
1057 service_owner_changed_listener_map_.erase(it);
1059 if (service_owner_changed_listener_map_.empty())
1060 RemoveFilterFunction(Bus::OnServiceOwnerChangedFilter, this);
1063 std::string Bus::GetConnectionName() {
1066 return dbus_bus_get_unique_name(connection_);
1069 bool Bus::IsConnected() {
1070 return connection_ != nullptr;
1073 dbus_bool_t Bus::OnAddWatch(DBusWatch* raw_watch) {
1074 AssertOnDBusThread();
1076 // watch will be deleted when raw_watch is removed in OnRemoveWatch().
1077 Watch* watch = new Watch(raw_watch);
1078 if (watch->IsReadyToBeWatched()) {
1079 watch->StartWatching();
1081 ++num_pending_watches_;
1085 void Bus::OnRemoveWatch(DBusWatch* raw_watch) {
1086 AssertOnDBusThread();
1088 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
1089 base::BlockingType::MAY_BLOCK);
1090 Watch* watch = static_cast<Watch*>(dbus_watch_get_data(raw_watch));
1092 --num_pending_watches_;
1095 void Bus::OnToggleWatch(DBusWatch* raw_watch) {
1096 AssertOnDBusThread();
1098 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
1099 base::BlockingType::MAY_BLOCK);
1100 Watch* watch = static_cast<Watch*>(dbus_watch_get_data(raw_watch));
1101 if (watch->IsReadyToBeWatched())
1102 watch->StartWatching();
1104 watch->StopWatching();
1107 dbus_bool_t Bus::OnAddTimeout(DBusTimeout* raw_timeout) {
1108 AssertOnDBusThread();
1110 // |timeout| will be deleted by OnRemoveTimeoutThunk().
1111 Timeout* timeout = new Timeout(raw_timeout);
1112 if (timeout->IsReadyToBeMonitored()) {
1113 timeout->StartMonitoring(this);
1115 ++num_pending_timeouts_;
1119 void Bus::OnRemoveTimeout(DBusTimeout* raw_timeout) {
1120 AssertOnDBusThread();
1122 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
1123 base::BlockingType::MAY_BLOCK);
1124 Timeout* timeout = static_cast<Timeout*>(dbus_timeout_get_data(raw_timeout));
1126 --num_pending_timeouts_;
1129 void Bus::OnToggleTimeout(DBusTimeout* raw_timeout) {
1130 AssertOnDBusThread();
1132 base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
1133 base::BlockingType::MAY_BLOCK);
1134 Timeout* timeout = static_cast<Timeout*>(dbus_timeout_get_data(raw_timeout));
1135 if (timeout->IsReadyToBeMonitored()) {
1136 timeout->StartMonitoring(this);
1138 timeout->StopMonitoring();
1142 void Bus::OnDispatchStatusChanged(DBusConnection* connection,
1143 DBusDispatchStatus status) {
1144 DCHECK_EQ(connection, connection_);
1145 AssertOnDBusThread();
1147 // We cannot call ProcessAllIncomingDataIfAny() here, as calling
1148 // dbus_connection_dispatch() inside DBusDispatchStatusFunction is
1149 // prohibited by the D-Bus library. Hence, we post a task here instead.
1150 // See comments for dbus_connection_set_dispatch_status_function().
1151 GetDBusTaskRunner()->PostTask(
1152 FROM_HERE, base::BindOnce(&Bus::ProcessAllIncomingDataIfAny, this));
1155 void Bus::OnServiceOwnerChanged(DBusMessage* message) {
1157 AssertOnDBusThread();
1159 // |message| will be unrefed on exit of the function. Increment the
1160 // reference so we can use it in Signal::FromRawMessage() below.
1161 dbus_message_ref(message);
1162 std::unique_ptr<Signal> signal(Signal::FromRawMessage(message));
1164 // Confirm the validity of the NameOwnerChanged signal.
1165 if (signal->GetMember() != kNameOwnerChangedSignal ||
1166 signal->GetInterface() != DBUS_INTERFACE_DBUS ||
1167 signal->GetSender() != DBUS_SERVICE_DBUS) {
1171 MessageReader reader(signal.get());
1172 std::string service_name;
1173 std::string old_owner;
1174 std::string new_owner;
1175 if (!reader.PopString(&service_name) ||
1176 !reader.PopString(&old_owner) ||
1177 !reader.PopString(&new_owner)) {
1181 ServiceOwnerChangedListenerMap::const_iterator it =
1182 service_owner_changed_listener_map_.find(service_name);
1183 if (it == service_owner_changed_listener_map_.end())
1186 const std::vector<ServiceOwnerChangeCallback>& callbacks = it->second;
1187 for (size_t i = 0; i < callbacks.size(); ++i) {
1188 GetOriginTaskRunner()->PostTask(FROM_HERE,
1189 base::BindOnce(callbacks[i], new_owner));
1194 dbus_bool_t Bus::OnAddWatchThunk(DBusWatch* raw_watch, void* data) {
1195 Bus* self = static_cast<Bus*>(data);
1196 return self->OnAddWatch(raw_watch);
1200 void Bus::OnRemoveWatchThunk(DBusWatch* raw_watch, void* data) {
1201 Bus* self = static_cast<Bus*>(data);
1202 self->OnRemoveWatch(raw_watch);
1206 void Bus::OnToggleWatchThunk(DBusWatch* raw_watch, void* data) {
1207 Bus* self = static_cast<Bus*>(data);
1208 self->OnToggleWatch(raw_watch);
1212 dbus_bool_t Bus::OnAddTimeoutThunk(DBusTimeout* raw_timeout, void* data) {
1213 Bus* self = static_cast<Bus*>(data);
1214 return self->OnAddTimeout(raw_timeout);
1218 void Bus::OnRemoveTimeoutThunk(DBusTimeout* raw_timeout, void* data) {
1219 Bus* self = static_cast<Bus*>(data);
1220 self->OnRemoveTimeout(raw_timeout);
1224 void Bus::OnToggleTimeoutThunk(DBusTimeout* raw_timeout, void* data) {
1225 Bus* self = static_cast<Bus*>(data);
1226 self->OnToggleTimeout(raw_timeout);
1230 void Bus::OnDispatchStatusChangedThunk(DBusConnection* connection,
1231 DBusDispatchStatus status,
1233 Bus* self = static_cast<Bus*>(data);
1234 self->OnDispatchStatusChanged(connection, status);
1238 DBusHandlerResult Bus::OnConnectionDisconnectedFilter(
1239 DBusConnection* connection,
1240 DBusMessage* message,
1242 if (dbus_message_is_signal(message,
1243 DBUS_INTERFACE_LOCAL,
1244 kDisconnectedSignal)) {
1245 // Abort when the connection is lost.
1246 LOG(FATAL) << "D-Bus connection was disconnected. Aborting.";
1248 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1252 DBusHandlerResult Bus::OnServiceOwnerChangedFilter(
1253 DBusConnection* connection,
1254 DBusMessage* message,
1256 if (dbus_message_is_signal(message,
1257 DBUS_INTERFACE_DBUS,
1258 kNameOwnerChangedSignal)) {
1259 Bus* self = static_cast<Bus*>(data);
1260 self->OnServiceOwnerChanged(message);
1262 // Always return unhandled to let others, e.g. ObjectProxies, handle the same
1264 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;