blob: 554ccb829d1a0e1925ba961cbcf2ced8e3579027 [file] [log] [blame]
adamk@chromium.org35c0eef2012-02-11 06:45:23 +09001// Copyright (c) 2012 The Chromium Authors. All rights reserved.
satorux@chromium.org163f1cb2011-08-18 05:58:12 +09002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
satorux@chromium.org163f1cb2011-08-18 05:58:12 +09004
5#include "dbus/bus.h"
6
avi0ad0ce02015-12-23 03:12:45 +09007#include <stddef.h>
8
satorux@chromium.org163f1cb2011-08-18 05:58:12 +09009#include "base/bind.h"
10#include "base/logging.h"
avi@chromium.orga29af562013-07-18 08:00:30 +090011#include "base/message_loop/message_loop.h"
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090012#include "base/stl_util.h"
avi@chromium.org66c0f912013-06-21 04:40:12 +090013#include "base/strings/stringprintf.h"
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090014#include "base/threading/thread.h"
15#include "base/threading/thread_restrictions.h"
avi@chromium.org78a7e7b2013-06-29 00:20:02 +090016#include "base/time/time.h"
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090017#include "dbus/exported_object.h"
thestig@chromium.orgc2482f12013-06-11 07:52:34 +090018#include "dbus/message.h"
keybuk@chromium.org09715012013-03-26 03:20:08 +090019#include "dbus/object_manager.h"
20#include "dbus/object_path.h"
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090021#include "dbus/object_proxy.h"
22#include "dbus/scoped_dbus_error.h"
23
24namespace dbus {
25
26namespace {
27
nona@chromium.org5a44d2b2013-02-08 19:53:39 +090028const char kDisconnectedSignal[] = "Disconnected";
29const char kDisconnectedMatchRule[] =
30 "type='signal', path='/org/freedesktop/DBus/Local',"
31 "interface='org.freedesktop.DBus.Local', member='Disconnected'";
32
thestig@chromium.orgc2482f12013-06-11 07:52:34 +090033// The NameOwnerChanged member in org.freedesktop.DBus
34const char kNameOwnerChangedSignal[] = "NameOwnerChanged";
35
36// The match rule used to filter for changes to a given service name owner.
37const char kServiceNameOwnerChangeMatchRule[] =
38 "type='signal',interface='org.freedesktop.DBus',"
39 "member='NameOwnerChanged',path='/org/freedesktop/DBus',"
40 "sender='org.freedesktop.DBus',arg0='%s'";
41
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090042// The class is used for watching the file descriptor used for D-Bus
43// communication.
44class Watch : public base::MessagePumpLibevent::Watcher {
45 public:
thestig@chromium.org074b1db2013-02-20 10:36:53 +090046 explicit Watch(DBusWatch* watch)
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090047 : raw_watch_(watch) {
48 dbus_watch_set_data(raw_watch_, this, NULL);
49 }
50
dcheng7f5750d2014-12-30 03:30:17 +090051 ~Watch() override { dbus_watch_set_data(raw_watch_, NULL, NULL); }
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090052
53 // Returns true if the underlying file descriptor is ready to be watched.
54 bool IsReadyToBeWatched() {
55 return dbus_watch_get_enabled(raw_watch_);
56 }
57
58 // Starts watching the underlying file descriptor.
59 void StartWatching() {
60 const int file_descriptor = dbus_watch_get_unix_fd(raw_watch_);
61 const int flags = dbus_watch_get_flags(raw_watch_);
62
xhwang@chromium.orgdff6b132013-05-02 01:10:30 +090063 base::MessageLoopForIO::Mode mode = base::MessageLoopForIO::WATCH_READ;
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090064 if ((flags & DBUS_WATCH_READABLE) && (flags & DBUS_WATCH_WRITABLE))
xhwang@chromium.orgdff6b132013-05-02 01:10:30 +090065 mode = base::MessageLoopForIO::WATCH_READ_WRITE;
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090066 else if (flags & DBUS_WATCH_READABLE)
xhwang@chromium.orgdff6b132013-05-02 01:10:30 +090067 mode = base::MessageLoopForIO::WATCH_READ;
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090068 else if (flags & DBUS_WATCH_WRITABLE)
xhwang@chromium.orgdff6b132013-05-02 01:10:30 +090069 mode = base::MessageLoopForIO::WATCH_WRITE;
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090070 else
71 NOTREACHED();
72
73 const bool persistent = true; // Watch persistently.
xhwang@chromium.orgdff6b132013-05-02 01:10:30 +090074 const bool success = base::MessageLoopForIO::current()->WatchFileDescriptor(
75 file_descriptor, persistent, mode, &file_descriptor_watcher_, this);
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090076 CHECK(success) << "Unable to allocate memory";
77 }
78
79 // Stops watching the underlying file descriptor.
80 void StopWatching() {
81 file_descriptor_watcher_.StopWatchingFileDescriptor();
82 }
83
84 private:
85 // Implement MessagePumpLibevent::Watcher.
dcheng7f5750d2014-12-30 03:30:17 +090086 void OnFileCanReadWithoutBlocking(int file_descriptor) override {
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090087 const bool success = dbus_watch_handle(raw_watch_, DBUS_WATCH_READABLE);
88 CHECK(success) << "Unable to allocate memory";
89 }
90
91 // Implement MessagePumpLibevent::Watcher.
dcheng7f5750d2014-12-30 03:30:17 +090092 void OnFileCanWriteWithoutBlocking(int file_descriptor) override {
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090093 const bool success = dbus_watch_handle(raw_watch_, DBUS_WATCH_WRITABLE);
94 CHECK(success) << "Unable to allocate memory";
95 }
96
97 DBusWatch* raw_watch_;
98 base::MessagePumpLibevent::FileDescriptorWatcher file_descriptor_watcher_;
99};
100
101// The class is used for monitoring the timeout used for D-Bus method
102// calls.
103//
104// Unlike Watch, Timeout is a ref counted object, to ensure that |this| of
105// the object is is alive when HandleTimeout() is called. It's unlikely
106// but it may be possible that HandleTimeout() is called after
107// Bus::OnRemoveTimeout(). That's why we don't simply delete the object in
108// Bus::OnRemoveTimeout().
109class Timeout : public base::RefCountedThreadSafe<Timeout> {
110 public:
thestig@chromium.org074b1db2013-02-20 10:36:53 +0900111 explicit Timeout(DBusTimeout* timeout)
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900112 : raw_timeout_(timeout),
113 monitoring_is_active_(false),
114 is_completed(false) {
115 dbus_timeout_set_data(raw_timeout_, this, NULL);
116 AddRef(); // Balanced on Complete().
117 }
118
119 // Returns true if the timeout is ready to be monitored.
120 bool IsReadyToBeMonitored() {
121 return dbus_timeout_get_enabled(raw_timeout_);
122 }
123
124 // Starts monitoring the timeout.
thestig@chromium.orgf0b7eac2013-06-13 15:37:19 +0900125 void StartMonitoring(Bus* bus) {
hashimoto@chromium.org955f6482013-09-26 13:32:29 +0900126 bus->GetDBusTaskRunner()->PostDelayedTask(
127 FROM_HERE,
128 base::Bind(&Timeout::HandleTimeout, this),
129 GetInterval());
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900130 monitoring_is_active_ = true;
131 }
132
133 // Stops monitoring the timeout.
134 void StopMonitoring() {
135 // We cannot take back the delayed task we posted in
136 // StartMonitoring(), so we just mark the monitoring is inactive now.
137 monitoring_is_active_ = false;
138 }
139
tedvessenes@gmail.com8d7a8762012-03-11 10:12:20 +0900140 // Returns the interval.
141 base::TimeDelta GetInterval() {
142 return base::TimeDelta::FromMilliseconds(
143 dbus_timeout_get_interval(raw_timeout_));
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900144 }
145
146 // Cleans up the raw_timeout and marks that timeout is completed.
147 // See the class comment above for why we are doing this.
148 void Complete() {
149 dbus_timeout_set_data(raw_timeout_, NULL, NULL);
150 is_completed = true;
151 Release();
152 }
153
154 private:
155 friend class base::RefCountedThreadSafe<Timeout>;
156 ~Timeout() {
157 }
158
159 // Handles the timeout.
160 void HandleTimeout() {
161 // If the timeout is marked completed, we should do nothing. This can
162 // occur if this function is called after Bus::OnRemoveTimeout().
163 if (is_completed)
164 return;
satorux@chromium.orgc6ac7572011-09-01 03:02:43 +0900165 // Skip if monitoring is canceled.
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900166 if (!monitoring_is_active_)
167 return;
168
169 const bool success = dbus_timeout_handle(raw_timeout_);
170 CHECK(success) << "Unable to allocate memory";
171 }
172
173 DBusTimeout* raw_timeout_;
174 bool monitoring_is_active_;
175 bool is_completed;
176};
177
178} // namespace
179
180Bus::Options::Options()
181 : bus_type(SESSION),
mdm@chromium.org45f2c6a2011-09-07 05:03:24 +0900182 connection_type(PRIVATE) {
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900183}
184
185Bus::Options::~Options() {
186}
187
188Bus::Bus(const Options& options)
189 : bus_type_(options.bus_type),
190 connection_type_(options.connection_type),
thestig@chromium.org074b1db2013-02-20 10:36:53 +0900191 dbus_task_runner_(options.dbus_task_runner),
satorux@chromium.orgd336d452011-09-02 15:56:23 +0900192 on_shutdown_(false /* manual_reset */, false /* initially_signaled */),
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900193 connection_(NULL),
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900194 origin_thread_id_(base::PlatformThread::CurrentId()),
satorux@chromium.org326a6f82011-08-27 16:26:34 +0900195 async_operations_set_up_(false),
satorux@chromium.orgd336d452011-09-02 15:56:23 +0900196 shutdown_completed_(false),
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900197 num_pending_watches_(0),
nona@chromium.org9f638e02012-04-19 12:20:03 +0900198 num_pending_timeouts_(0),
hashimoto76b0cff2014-12-09 13:50:23 +0900199 address_(options.address) {
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900200 // This is safe to call multiple times.
201 dbus_threads_init_default();
satorux@chromium.orgcff09492011-09-09 07:28:42 +0900202 // The origin message loop is unnecessary if the client uses synchronous
203 // functions only.
xhwang@chromium.orgdff6b132013-05-02 01:10:30 +0900204 if (base::MessageLoop::current())
skyostile5a8dc42015-06-18 00:46:04 +0900205 origin_task_runner_ = base::MessageLoop::current()->task_runner();
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900206}
207
208Bus::~Bus() {
209 DCHECK(!connection_);
210 DCHECK(owned_service_names_.empty());
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900211 DCHECK(match_rules_added_.empty());
212 DCHECK(filter_functions_added_.empty());
213 DCHECK(registered_object_paths_.empty());
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900214 DCHECK_EQ(0, num_pending_watches_);
jamescook@chromium.org255cd352011-11-24 16:00:43 +0900215 // TODO(satorux): This check fails occasionally in browser_tests for tests
216 // that run very quickly. Perhaps something does not have time to clean up.
217 // Despite the check failing, the tests seem to run fine. crosbug.com/23416
218 // DCHECK_EQ(0, num_pending_timeouts_);
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900219}
220
221ObjectProxy* Bus::GetObjectProxy(const std::string& service_name,
keybuk@google.combf4649a2012-02-15 06:29:06 +0900222 const ObjectPath& object_path) {
adamk@chromium.org35c0eef2012-02-11 06:45:23 +0900223 return GetObjectProxyWithOptions(service_name, object_path,
224 ObjectProxy::DEFAULT_OPTIONS);
225}
226
227ObjectProxy* Bus::GetObjectProxyWithOptions(const std::string& service_name,
thestig@chromium.orgf0b7eac2013-06-13 15:37:19 +0900228 const ObjectPath& object_path,
adamk@chromium.org35c0eef2012-02-11 06:45:23 +0900229 int options) {
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900230 AssertOnOriginThread();
231
satorux@chromium.orgdccbb7b2011-08-24 04:25:20 +0900232 // Check if we already have the requested object proxy.
keybuk@google.combf4649a2012-02-15 06:29:06 +0900233 const ObjectProxyTable::key_type key(service_name + object_path.value(),
234 options);
satorux@chromium.orgdccbb7b2011-08-24 04:25:20 +0900235 ObjectProxyTable::iterator iter = object_proxy_table_.find(key);
236 if (iter != object_proxy_table_.end()) {
rsleevi@chromium.orgc5cb8592013-06-03 08:38:09 +0900237 return iter->second.get();
satorux@chromium.orgdccbb7b2011-08-24 04:25:20 +0900238 }
239
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900240 scoped_refptr<ObjectProxy> object_proxy =
adamk@chromium.org35c0eef2012-02-11 06:45:23 +0900241 new ObjectProxy(this, service_name, object_path, options);
satorux@chromium.orgdccbb7b2011-08-24 04:25:20 +0900242 object_proxy_table_[key] = object_proxy;
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900243
satorux@chromium.orgdccbb7b2011-08-24 04:25:20 +0900244 return object_proxy.get();
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900245}
246
deymo@chromium.org6d168a72013-01-30 05:29:12 +0900247bool Bus::RemoveObjectProxy(const std::string& service_name,
248 const ObjectPath& object_path,
249 const base::Closure& callback) {
250 return RemoveObjectProxyWithOptions(service_name, object_path,
251 ObjectProxy::DEFAULT_OPTIONS,
252 callback);
253}
254
255bool Bus::RemoveObjectProxyWithOptions(const std::string& service_name,
thestig@chromium.orgf0b7eac2013-06-13 15:37:19 +0900256 const ObjectPath& object_path,
deymo@chromium.org6d168a72013-01-30 05:29:12 +0900257 int options,
258 const base::Closure& callback) {
259 AssertOnOriginThread();
260
261 // Check if we have the requested object proxy.
262 const ObjectProxyTable::key_type key(service_name + object_path.value(),
263 options);
264 ObjectProxyTable::iterator iter = object_proxy_table_.find(key);
265 if (iter != object_proxy_table_.end()) {
stevenjb@chromium.orgb53cfb32013-10-08 07:56:57 +0900266 scoped_refptr<ObjectProxy> object_proxy = iter->second;
267 object_proxy_table_.erase(iter);
armansitof4364642014-09-06 02:49:34 +0900268 // Object is present. Remove it now and Detach on the DBus thread.
hashimoto@chromium.org955f6482013-09-26 13:32:29 +0900269 GetDBusTaskRunner()->PostTask(
270 FROM_HERE,
271 base::Bind(&Bus::RemoveObjectProxyInternal,
stevenjb@chromium.orgb53cfb32013-10-08 07:56:57 +0900272 this, object_proxy, callback));
deymo@chromium.org6d168a72013-01-30 05:29:12 +0900273 return true;
274 }
275 return false;
276}
277
thestig@chromium.orgf0b7eac2013-06-13 15:37:19 +0900278void Bus::RemoveObjectProxyInternal(scoped_refptr<ObjectProxy> object_proxy,
279 const base::Closure& callback) {
deymo@chromium.org6d168a72013-01-30 05:29:12 +0900280 AssertOnDBusThread();
281
282 object_proxy.get()->Detach();
283
hashimoto@chromium.org955f6482013-09-26 13:32:29 +0900284 GetOriginTaskRunner()->PostTask(FROM_HERE, callback);
deymo@chromium.org6d168a72013-01-30 05:29:12 +0900285}
286
keybuk@chromium.org9cb73f02012-03-10 10:12:52 +0900287ExportedObject* Bus::GetExportedObject(const ObjectPath& object_path) {
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900288 AssertOnOriginThread();
289
satorux@chromium.orgdccbb7b2011-08-24 04:25:20 +0900290 // Check if we already have the requested exported object.
keybuk@chromium.org9cb73f02012-03-10 10:12:52 +0900291 ExportedObjectTable::iterator iter = exported_object_table_.find(object_path);
satorux@chromium.orgdccbb7b2011-08-24 04:25:20 +0900292 if (iter != exported_object_table_.end()) {
rsleevi@chromium.orgc5cb8592013-06-03 08:38:09 +0900293 return iter->second.get();
satorux@chromium.orgdccbb7b2011-08-24 04:25:20 +0900294 }
295
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900296 scoped_refptr<ExportedObject> exported_object =
keybuk@chromium.org9cb73f02012-03-10 10:12:52 +0900297 new ExportedObject(this, object_path);
298 exported_object_table_[object_path] = exported_object;
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900299
satorux@chromium.orgdccbb7b2011-08-24 04:25:20 +0900300 return exported_object.get();
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900301}
302
keybuk@chromium.orgd2ca8f32012-03-14 10:18:35 +0900303void Bus::UnregisterExportedObject(const ObjectPath& object_path) {
304 AssertOnOriginThread();
305
306 // Remove the registered object from the table first, to allow a new
307 // GetExportedObject() call to return a new object, rather than this one.
308 ExportedObjectTable::iterator iter = exported_object_table_.find(object_path);
309 if (iter == exported_object_table_.end())
310 return;
311
312 scoped_refptr<ExportedObject> exported_object = iter->second;
313 exported_object_table_.erase(iter);
314
315 // Post the task to perform the final unregistration to the D-Bus thread.
316 // Since the registration also happens on the D-Bus thread in
thestig@chromium.org074b1db2013-02-20 10:36:53 +0900317 // TryRegisterObjectPath(), and the task runner we post to is a
318 // SequencedTaskRunner, there is a guarantee that this will happen before any
319 // future registration call.
hashimoto@chromium.org955f6482013-09-26 13:32:29 +0900320 GetDBusTaskRunner()->PostTask(
321 FROM_HERE,
322 base::Bind(&Bus::UnregisterExportedObjectInternal,
323 this, exported_object));
keybuk@chromium.orgd2ca8f32012-03-14 10:18:35 +0900324}
325
326void Bus::UnregisterExportedObjectInternal(
thestig@chromium.orgf0b7eac2013-06-13 15:37:19 +0900327 scoped_refptr<ExportedObject> exported_object) {
keybuk@chromium.orgd2ca8f32012-03-14 10:18:35 +0900328 AssertOnDBusThread();
329
330 exported_object->Unregister();
331}
332
keybuk@chromium.org09715012013-03-26 03:20:08 +0900333ObjectManager* Bus::GetObjectManager(const std::string& service_name,
334 const ObjectPath& object_path) {
335 AssertOnOriginThread();
336
337 // Check if we already have the requested object manager.
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()) {
rsleevi@chromium.orgc5cb8592013-06-03 08:38:09 +0900341 return iter->second.get();
keybuk@chromium.org09715012013-03-26 03:20:08 +0900342 }
343
344 scoped_refptr<ObjectManager> object_manager =
345 new ObjectManager(this, service_name, object_path);
346 object_manager_table_[key] = object_manager;
347
348 return object_manager.get();
349}
350
armansitof4364642014-09-06 02:49:34 +0900351bool Bus::RemoveObjectManager(const std::string& service_name,
352 const ObjectPath& object_path,
353 const base::Closure& callback) {
keybuk@chromium.org09715012013-03-26 03:20:08 +0900354 AssertOnOriginThread();
armansitof4364642014-09-06 02:49:34 +0900355 DCHECK(!callback.is_null());
keybuk@chromium.org09715012013-03-26 03:20:08 +0900356
357 const ObjectManagerTable::key_type key(service_name + object_path.value());
358 ObjectManagerTable::iterator iter = object_manager_table_.find(key);
359 if (iter == object_manager_table_.end())
armansitof4364642014-09-06 02:49:34 +0900360 return false;
keybuk@chromium.org09715012013-03-26 03:20:08 +0900361
armansitof4364642014-09-06 02:49:34 +0900362 // ObjectManager is present. Remove it now and CleanUp on the DBus thread.
keybuk@chromium.org09715012013-03-26 03:20:08 +0900363 scoped_refptr<ObjectManager> object_manager = iter->second;
364 object_manager_table_.erase(iter);
armansitof4364642014-09-06 02:49:34 +0900365
366 GetDBusTaskRunner()->PostTask(
367 FROM_HERE,
368 base::Bind(&Bus::RemoveObjectManagerInternal,
369 this, object_manager, callback));
370
371 return true;
372}
373
374void Bus::RemoveObjectManagerInternal(
375 scoped_refptr<dbus::ObjectManager> object_manager,
376 const base::Closure& callback) {
377 AssertOnDBusThread();
378 DCHECK(object_manager.get());
379
380 object_manager->CleanUp();
381
382 // The ObjectManager has to be deleted on the origin thread since it was
383 // created there.
384 GetOriginTaskRunner()->PostTask(
385 FROM_HERE,
386 base::Bind(&Bus::RemoveObjectManagerInternalHelper,
387 this, object_manager, callback));
388}
389
390void Bus::RemoveObjectManagerInternalHelper(
391 scoped_refptr<dbus::ObjectManager> object_manager,
392 const base::Closure& callback) {
393 AssertOnOriginThread();
394 DCHECK(object_manager.get());
395
396 // Release the object manager and run the callback.
397 object_manager = NULL;
398 callback.Run();
keybuk@chromium.org09715012013-03-26 03:20:08 +0900399}
400
401void Bus::GetManagedObjects() {
402 for (ObjectManagerTable::iterator iter = object_manager_table_.begin();
403 iter != object_manager_table_.end(); ++iter) {
404 iter->second->GetManagedObjects();
405 }
406}
407
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900408bool Bus::Connect() {
409 // dbus_bus_get_private() and dbus_bus_get() are blocking calls.
410 AssertOnDBusThread();
411
412 // Check if it's already initialized.
413 if (connection_)
414 return true;
415
416 ScopedDBusError error;
nona@chromium.org9f638e02012-04-19 12:20:03 +0900417 if (bus_type_ == CUSTOM_ADDRESS) {
418 if (connection_type_ == PRIVATE) {
419 connection_ = dbus_connection_open_private(address_.c_str(), error.get());
420 } else {
421 connection_ = dbus_connection_open(address_.c_str(), error.get());
422 }
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900423 } else {
nona@chromium.org9f638e02012-04-19 12:20:03 +0900424 const DBusBusType dbus_bus_type = static_cast<DBusBusType>(bus_type_);
425 if (connection_type_ == PRIVATE) {
426 connection_ = dbus_bus_get_private(dbus_bus_type, error.get());
427 } else {
428 connection_ = dbus_bus_get(dbus_bus_type, error.get());
429 }
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900430 }
431 if (!connection_) {
432 LOG(ERROR) << "Failed to connect to the bus: "
tfarina@chromium.org56cc3fc2012-10-30 01:43:26 +0900433 << (error.is_set() ? error.message() : "");
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900434 return false;
435 }
nona@chromium.orgeeaf2412012-11-02 17:04:14 +0900436
437 if (bus_type_ == CUSTOM_ADDRESS) {
438 // We should call dbus_bus_register here, otherwise unique name can not be
439 // acquired. According to dbus specification, it is responsible to call
440 // org.freedesktop.DBus.Hello method at the beging of bus connection to
441 // acquire unique name. In the case of dbus_bus_get, dbus_bus_register is
442 // called internally.
443 if (!dbus_bus_register(connection_, error.get())) {
444 LOG(ERROR) << "Failed to register the bus component: "
445 << (error.is_set() ? error.message() : "");
446 return false;
447 }
448 }
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900449 // We shouldn't exit on the disconnected signal.
450 dbus_connection_set_exit_on_disconnect(connection_, false);
451
nona@chromium.org5a44d2b2013-02-08 19:53:39 +0900452 // Watch Disconnected signal.
453 AddFilterFunction(Bus::OnConnectionDisconnectedFilter, this);
454 AddMatch(kDisconnectedMatchRule, error.get());
455
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900456 return true;
457}
458
nona@chromium.org1de76fd2013-02-16 01:44:40 +0900459void Bus::ClosePrivateConnection() {
460 // dbus_connection_close is blocking call.
461 AssertOnDBusThread();
462 DCHECK_EQ(PRIVATE, connection_type_)
463 << "non-private connection should not be closed";
464 dbus_connection_close(connection_);
465}
466
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900467void Bus::ShutdownAndBlock() {
468 AssertOnDBusThread();
469
nona@chromium.org5a44d2b2013-02-08 19:53:39 +0900470 if (shutdown_completed_)
471 return; // Already shutdowned, just return.
472
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900473 // Unregister the exported objects.
satorux@chromium.orgdccbb7b2011-08-24 04:25:20 +0900474 for (ExportedObjectTable::iterator iter = exported_object_table_.begin();
475 iter != exported_object_table_.end(); ++iter) {
476 iter->second->Unregister();
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900477 }
478
479 // Release all service names.
480 for (std::set<std::string>::iterator iter = owned_service_names_.begin();
481 iter != owned_service_names_.end();) {
482 // This is a bit tricky but we should increment the iter here as
483 // ReleaseOwnership() may remove |service_name| from the set.
484 const std::string& service_name = *iter++;
485 ReleaseOwnership(service_name);
486 }
487 if (!owned_service_names_.empty()) {
488 LOG(ERROR) << "Failed to release all service names. # of services left: "
489 << owned_service_names_.size();
490 }
491
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900492 // Detach from the remote objects.
satorux@chromium.orgdccbb7b2011-08-24 04:25:20 +0900493 for (ObjectProxyTable::iterator iter = object_proxy_table_.begin();
494 iter != object_proxy_table_.end(); ++iter) {
495 iter->second->Detach();
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900496 }
497
armansitof4364642014-09-06 02:49:34 +0900498 // Clean up the object managers.
499 for (ObjectManagerTable::iterator iter = object_manager_table_.begin();
500 iter != object_manager_table_.end(); ++iter) {
501 iter->second->CleanUp();
502 }
503
satorux@chromium.orgf06eb892011-10-13 09:45:26 +0900504 // Release object proxies and exported objects here. We should do this
505 // here rather than in the destructor to avoid memory leaks due to
506 // cyclic references.
507 object_proxy_table_.clear();
508 exported_object_table_.clear();
509
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900510 // Private connection should be closed.
satorux@chromium.orgd336d452011-09-02 15:56:23 +0900511 if (connection_) {
nona@chromium.org5a44d2b2013-02-08 19:53:39 +0900512 // Remove Disconnected watcher.
513 ScopedDBusError error;
514 RemoveFilterFunction(Bus::OnConnectionDisconnectedFilter, this);
515 RemoveMatch(kDisconnectedMatchRule, error.get());
516
satorux@chromium.orgd336d452011-09-02 15:56:23 +0900517 if (connection_type_ == PRIVATE)
nona@chromium.org1de76fd2013-02-16 01:44:40 +0900518 ClosePrivateConnection();
satorux@chromium.orgd336d452011-09-02 15:56:23 +0900519 // dbus_connection_close() won't unref.
520 dbus_connection_unref(connection_);
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900521 }
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900522
523 connection_ = NULL;
satorux@chromium.orgd336d452011-09-02 15:56:23 +0900524 shutdown_completed_ = true;
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900525}
526
satorux@chromium.orgd336d452011-09-02 15:56:23 +0900527void Bus::ShutdownOnDBusThreadAndBlock() {
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900528 AssertOnOriginThread();
thestig@chromium.org074b1db2013-02-20 10:36:53 +0900529 DCHECK(dbus_task_runner_.get());
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900530
hashimoto@chromium.org955f6482013-09-26 13:32:29 +0900531 GetDBusTaskRunner()->PostTask(
532 FROM_HERE,
533 base::Bind(&Bus::ShutdownOnDBusThreadAndBlockInternal, this));
satorux@chromium.orgd336d452011-09-02 15:56:23 +0900534
jam@chromium.org17554342012-04-27 04:08:58 +0900535 // http://crbug.com/125222
536 base::ThreadRestrictions::ScopedAllowWait allow_wait;
537
satorux@chromium.orgd336d452011-09-02 15:56:23 +0900538 // Wait until the shutdown is complete on the D-Bus thread.
539 // The shutdown should not hang, but set timeout just in case.
540 const int kTimeoutSecs = 3;
541 const base::TimeDelta timeout(base::TimeDelta::FromSeconds(kTimeoutSecs));
542 const bool signaled = on_shutdown_.TimedWait(timeout);
543 LOG_IF(ERROR, !signaled) << "Failed to shutdown the bus";
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900544}
545
keybuk@chromium.org9cb73f02012-03-10 10:12:52 +0900546void Bus::RequestOwnership(const std::string& service_name,
cmasone@chromium.org989857e2013-07-31 15:34:59 +0900547 ServiceOwnershipOptions options,
keybuk@chromium.org9cb73f02012-03-10 10:12:52 +0900548 OnOwnershipCallback on_ownership_callback) {
549 AssertOnOriginThread();
550
hashimoto@chromium.org955f6482013-09-26 13:32:29 +0900551 GetDBusTaskRunner()->PostTask(
552 FROM_HERE,
553 base::Bind(&Bus::RequestOwnershipInternal,
554 this, service_name, options, on_ownership_callback));
keybuk@chromium.org9cb73f02012-03-10 10:12:52 +0900555}
556
557void Bus::RequestOwnershipInternal(const std::string& service_name,
cmasone@chromium.org989857e2013-07-31 15:34:59 +0900558 ServiceOwnershipOptions options,
keybuk@chromium.org9cb73f02012-03-10 10:12:52 +0900559 OnOwnershipCallback on_ownership_callback) {
560 AssertOnDBusThread();
561
562 bool success = Connect();
563 if (success)
cmasone@chromium.org989857e2013-07-31 15:34:59 +0900564 success = RequestOwnershipAndBlock(service_name, options);
keybuk@chromium.org9cb73f02012-03-10 10:12:52 +0900565
hashimoto@chromium.org955f6482013-09-26 13:32:29 +0900566 GetOriginTaskRunner()->PostTask(FROM_HERE,
567 base::Bind(on_ownership_callback,
568 service_name,
569 success));
keybuk@chromium.org9cb73f02012-03-10 10:12:52 +0900570}
571
cmasone@chromium.org989857e2013-07-31 15:34:59 +0900572bool Bus::RequestOwnershipAndBlock(const std::string& service_name,
573 ServiceOwnershipOptions options) {
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900574 DCHECK(connection_);
575 // dbus_bus_request_name() is a blocking call.
576 AssertOnDBusThread();
577
578 // Check if we already own the service name.
579 if (owned_service_names_.find(service_name) != owned_service_names_.end()) {
580 return true;
581 }
582
583 ScopedDBusError error;
584 const int result = dbus_bus_request_name(connection_,
585 service_name.c_str(),
cmasone@chromium.org989857e2013-07-31 15:34:59 +0900586 options,
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900587 error.get());
588 if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
satorux@chromium.orgc6ac7572011-09-01 03:02:43 +0900589 LOG(ERROR) << "Failed to get the ownership of " << service_name << ": "
tfarina@chromium.org56cc3fc2012-10-30 01:43:26 +0900590 << (error.is_set() ? error.message() : "");
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900591 return false;
592 }
593 owned_service_names_.insert(service_name);
594 return true;
595}
596
597bool Bus::ReleaseOwnership(const std::string& service_name) {
598 DCHECK(connection_);
599 // dbus_bus_request_name() is a blocking call.
600 AssertOnDBusThread();
601
602 // Check if we already own the service name.
603 std::set<std::string>::iterator found =
604 owned_service_names_.find(service_name);
605 if (found == owned_service_names_.end()) {
606 LOG(ERROR) << service_name << " is not owned by the bus";
607 return false;
608 }
609
610 ScopedDBusError error;
611 const int result = dbus_bus_release_name(connection_, service_name.c_str(),
612 error.get());
613 if (result == DBUS_RELEASE_NAME_REPLY_RELEASED) {
614 owned_service_names_.erase(found);
615 return true;
616 } else {
satorux@chromium.orgc6ac7572011-09-01 03:02:43 +0900617 LOG(ERROR) << "Failed to release the ownership of " << service_name << ": "
cmasone@chromium.org989857e2013-07-31 15:34:59 +0900618 << (error.is_set() ? error.message() : "")
619 << ", result code: " << result;
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900620 return false;
621 }
622}
623
624bool Bus::SetUpAsyncOperations() {
625 DCHECK(connection_);
626 AssertOnDBusThread();
627
satorux@chromium.org326a6f82011-08-27 16:26:34 +0900628 if (async_operations_set_up_)
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900629 return true;
630
631 // Process all the incoming data if any, so that OnDispatchStatus() will
632 // be called when the incoming data is ready.
633 ProcessAllIncomingDataIfAny();
634
635 bool success = dbus_connection_set_watch_functions(connection_,
636 &Bus::OnAddWatchThunk,
637 &Bus::OnRemoveWatchThunk,
638 &Bus::OnToggleWatchThunk,
639 this,
640 NULL);
641 CHECK(success) << "Unable to allocate memory";
642
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900643 success = dbus_connection_set_timeout_functions(connection_,
644 &Bus::OnAddTimeoutThunk,
645 &Bus::OnRemoveTimeoutThunk,
646 &Bus::OnToggleTimeoutThunk,
647 this,
648 NULL);
649 CHECK(success) << "Unable to allocate memory";
650
651 dbus_connection_set_dispatch_status_function(
652 connection_,
653 &Bus::OnDispatchStatusChangedThunk,
654 this,
655 NULL);
656
satorux@chromium.org326a6f82011-08-27 16:26:34 +0900657 async_operations_set_up_ = true;
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900658
659 return true;
660}
661
662DBusMessage* Bus::SendWithReplyAndBlock(DBusMessage* request,
663 int timeout_ms,
664 DBusError* error) {
665 DCHECK(connection_);
666 AssertOnDBusThread();
667
668 return dbus_connection_send_with_reply_and_block(
669 connection_, request, timeout_ms, error);
670}
671
672void Bus::SendWithReply(DBusMessage* request,
673 DBusPendingCall** pending_call,
674 int timeout_ms) {
675 DCHECK(connection_);
676 AssertOnDBusThread();
677
678 const bool success = dbus_connection_send_with_reply(
679 connection_, request, pending_call, timeout_ms);
680 CHECK(success) << "Unable to allocate memory";
681}
682
avi0ad0ce02015-12-23 03:12:45 +0900683void Bus::Send(DBusMessage* request, uint32_t* serial) {
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900684 DCHECK(connection_);
685 AssertOnDBusThread();
686
687 const bool success = dbus_connection_send(connection_, request, serial);
688 CHECK(success) << "Unable to allocate memory";
689}
690
hashimoto46be6e92014-12-04 16:41:55 +0900691void Bus::AddFilterFunction(DBusHandleMessageFunction filter_function,
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900692 void* user_data) {
693 DCHECK(connection_);
694 AssertOnDBusThread();
695
satorux@chromium.org66bc4c22011-10-06 09:20:53 +0900696 std::pair<DBusHandleMessageFunction, void*> filter_data_pair =
697 std::make_pair(filter_function, user_data);
698 if (filter_functions_added_.find(filter_data_pair) !=
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900699 filter_functions_added_.end()) {
satorux@chromium.org66bc4c22011-10-06 09:20:53 +0900700 VLOG(1) << "Filter function already exists: " << filter_function
701 << " with associated data: " << user_data;
hashimoto46be6e92014-12-04 16:41:55 +0900702 return;
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900703 }
704
705 const bool success = dbus_connection_add_filter(
706 connection_, filter_function, user_data, NULL);
707 CHECK(success) << "Unable to allocate memory";
satorux@chromium.org66bc4c22011-10-06 09:20:53 +0900708 filter_functions_added_.insert(filter_data_pair);
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900709}
710
hashimoto46be6e92014-12-04 16:41:55 +0900711void Bus::RemoveFilterFunction(DBusHandleMessageFunction filter_function,
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900712 void* user_data) {
713 DCHECK(connection_);
714 AssertOnDBusThread();
715
satorux@chromium.org66bc4c22011-10-06 09:20:53 +0900716 std::pair<DBusHandleMessageFunction, void*> filter_data_pair =
717 std::make_pair(filter_function, user_data);
718 if (filter_functions_added_.find(filter_data_pair) ==
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900719 filter_functions_added_.end()) {
satorux@chromium.org66bc4c22011-10-06 09:20:53 +0900720 VLOG(1) << "Requested to remove an unknown filter function: "
721 << filter_function
722 << " with associated data: " << user_data;
hashimoto46be6e92014-12-04 16:41:55 +0900723 return;
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900724 }
725
726 dbus_connection_remove_filter(connection_, filter_function, user_data);
satorux@chromium.org66bc4c22011-10-06 09:20:53 +0900727 filter_functions_added_.erase(filter_data_pair);
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900728}
729
730void Bus::AddMatch(const std::string& match_rule, DBusError* error) {
731 DCHECK(connection_);
732 AssertOnDBusThread();
733
deymo@chromium.org7894ebf2013-01-31 15:08:02 +0900734 std::map<std::string, int>::iterator iter =
735 match_rules_added_.find(match_rule);
736 if (iter != match_rules_added_.end()) {
737 // The already existing rule's counter is incremented.
738 iter->second++;
739
satorux@chromium.orga3a97932011-10-13 08:47:13 +0900740 VLOG(1) << "Match rule already exists: " << match_rule;
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900741 return;
742 }
743
744 dbus_bus_add_match(connection_, match_rule.c_str(), error);
deymo@chromium.org7894ebf2013-01-31 15:08:02 +0900745 match_rules_added_[match_rule] = 1;
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900746}
747
deymo@chromium.org7894ebf2013-01-31 15:08:02 +0900748bool Bus::RemoveMatch(const std::string& match_rule, DBusError* error) {
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900749 DCHECK(connection_);
750 AssertOnDBusThread();
751
deymo@chromium.org7894ebf2013-01-31 15:08:02 +0900752 std::map<std::string, int>::iterator iter =
753 match_rules_added_.find(match_rule);
754 if (iter == match_rules_added_.end()) {
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900755 LOG(ERROR) << "Requested to remove an unknown match rule: " << match_rule;
deymo@chromium.org7894ebf2013-01-31 15:08:02 +0900756 return false;
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900757 }
758
deymo@chromium.org7894ebf2013-01-31 15:08:02 +0900759 // The rule's counter is decremented and the rule is deleted when reachs 0.
760 iter->second--;
761 if (iter->second == 0) {
762 dbus_bus_remove_match(connection_, match_rule.c_str(), error);
763 match_rules_added_.erase(match_rule);
764 }
765 return true;
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900766}
767
keybuk@google.combf4649a2012-02-15 06:29:06 +0900768bool Bus::TryRegisterObjectPath(const ObjectPath& object_path,
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900769 const DBusObjectPathVTable* vtable,
770 void* user_data,
771 DBusError* error) {
772 DCHECK(connection_);
773 AssertOnDBusThread();
774
satorux@chromium.org326a6f82011-08-27 16:26:34 +0900775 if (registered_object_paths_.find(object_path) !=
776 registered_object_paths_.end()) {
keybuk@google.combf4649a2012-02-15 06:29:06 +0900777 LOG(ERROR) << "Object path already registered: " << object_path.value();
satorux@chromium.org326a6f82011-08-27 16:26:34 +0900778 return false;
779 }
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900780
781 const bool success = dbus_connection_try_register_object_path(
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900782 connection_,
keybuk@google.combf4649a2012-02-15 06:29:06 +0900783 object_path.value().c_str(),
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900784 vtable,
785 user_data,
786 error);
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900787 if (success)
788 registered_object_paths_.insert(object_path);
789 return success;
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900790}
791
keybuk@google.combf4649a2012-02-15 06:29:06 +0900792void Bus::UnregisterObjectPath(const ObjectPath& object_path) {
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900793 DCHECK(connection_);
794 AssertOnDBusThread();
795
satorux@chromium.org326a6f82011-08-27 16:26:34 +0900796 if (registered_object_paths_.find(object_path) ==
797 registered_object_paths_.end()) {
798 LOG(ERROR) << "Requested to unregister an unknown object path: "
keybuk@google.combf4649a2012-02-15 06:29:06 +0900799 << object_path.value();
satorux@chromium.org326a6f82011-08-27 16:26:34 +0900800 return;
801 }
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900802
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900803 const bool success = dbus_connection_unregister_object_path(
804 connection_,
keybuk@google.combf4649a2012-02-15 06:29:06 +0900805 object_path.value().c_str());
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900806 CHECK(success) << "Unable to allocate memory";
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900807 registered_object_paths_.erase(object_path);
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900808}
809
satorux@chromium.orgd336d452011-09-02 15:56:23 +0900810void Bus::ShutdownOnDBusThreadAndBlockInternal() {
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900811 AssertOnDBusThread();
812
813 ShutdownAndBlock();
satorux@chromium.orgd336d452011-09-02 15:56:23 +0900814 on_shutdown_.Signal();
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900815}
816
817void Bus::ProcessAllIncomingDataIfAny() {
818 AssertOnDBusThread();
819
820 // As mentioned at the class comment in .h file, connection_ can be NULL.
nona@chromium.org5a44d2b2013-02-08 19:53:39 +0900821 if (!connection_)
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900822 return;
823
nona@chromium.org5a44d2b2013-02-08 19:53:39 +0900824 // It is safe and necessary to call dbus_connection_get_dispatch_status even
hashimoto76b0cff2014-12-09 13:50:23 +0900825 // if the connection is lost.
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900826 if (dbus_connection_get_dispatch_status(connection_) ==
827 DBUS_DISPATCH_DATA_REMAINS) {
828 while (dbus_connection_dispatch(connection_) ==
thestig@chromium.orgf0b7eac2013-06-13 15:37:19 +0900829 DBUS_DISPATCH_DATA_REMAINS) {
830 }
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900831 }
832}
833
hashimoto@chromium.org955f6482013-09-26 13:32:29 +0900834base::TaskRunner* Bus::GetDBusTaskRunner() {
835 if (dbus_task_runner_.get())
836 return dbus_task_runner_.get();
837 else
838 return GetOriginTaskRunner();
haruki@chromium.org4a1f9562013-05-08 20:57:14 +0900839}
840
hashimoto@chromium.org955f6482013-09-26 13:32:29 +0900841base::TaskRunner* Bus::GetOriginTaskRunner() {
thestig@chromium.org074b1db2013-02-20 10:36:53 +0900842 DCHECK(origin_task_runner_.get());
hashimoto@chromium.org955f6482013-09-26 13:32:29 +0900843 return origin_task_runner_.get();
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900844}
845
846bool Bus::HasDBusThread() {
thestig@chromium.org074b1db2013-02-20 10:36:53 +0900847 return dbus_task_runner_.get() != NULL;
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900848}
849
850void Bus::AssertOnOriginThread() {
851 DCHECK_EQ(origin_thread_id_, base::PlatformThread::CurrentId());
852}
853
854void Bus::AssertOnDBusThread() {
855 base::ThreadRestrictions::AssertIOAllowed();
856
thestig@chromium.org074b1db2013-02-20 10:36:53 +0900857 if (dbus_task_runner_.get()) {
858 DCHECK(dbus_task_runner_->RunsTasksOnCurrentThread());
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900859 } else {
860 AssertOnOriginThread();
861 }
862}
863
thestig@chromium.org56057f22013-05-05 00:48:37 +0900864std::string Bus::GetServiceOwnerAndBlock(const std::string& service_name,
865 GetServiceOwnerOption options) {
866 AssertOnDBusThread();
867
868 MethodCall get_name_owner_call("org.freedesktop.DBus", "GetNameOwner");
869 MessageWriter writer(&get_name_owner_call);
870 writer.AppendString(service_name);
871 VLOG(1) << "Method call: " << get_name_owner_call.ToString();
872
873 const ObjectPath obj_path("/org/freedesktop/DBus");
874 if (!get_name_owner_call.SetDestination("org.freedesktop.DBus") ||
875 !get_name_owner_call.SetPath(obj_path)) {
876 if (options == REPORT_ERRORS)
877 LOG(ERROR) << "Failed to get name owner.";
878 return "";
879 }
880
881 ScopedDBusError error;
882 DBusMessage* response_message =
883 SendWithReplyAndBlock(get_name_owner_call.raw_message(),
884 ObjectProxy::TIMEOUT_USE_DEFAULT,
885 error.get());
886 if (!response_message) {
887 if (options == REPORT_ERRORS) {
888 LOG(ERROR) << "Failed to get name owner. Got " << error.name() << ": "
889 << error.message();
890 }
891 return "";
892 }
893
894 scoped_ptr<Response> response(Response::FromRawMessage(response_message));
895 MessageReader reader(response.get());
896
897 std::string service_owner;
898 if (!reader.PopString(&service_owner))
899 service_owner.clear();
900 return service_owner;
901}
902
903void Bus::GetServiceOwner(const std::string& service_name,
904 const GetServiceOwnerCallback& callback) {
905 AssertOnOriginThread();
906
hashimoto@chromium.org955f6482013-09-26 13:32:29 +0900907 GetDBusTaskRunner()->PostTask(
thestig@chromium.org56057f22013-05-05 00:48:37 +0900908 FROM_HERE,
909 base::Bind(&Bus::GetServiceOwnerInternal, this, service_name, callback));
910}
911
912void Bus::GetServiceOwnerInternal(const std::string& service_name,
913 const GetServiceOwnerCallback& callback) {
914 AssertOnDBusThread();
915
916 std::string service_owner;
917 if (Connect())
thestig@chromium.org493b0ea2013-05-09 05:47:18 +0900918 service_owner = GetServiceOwnerAndBlock(service_name, SUPPRESS_ERRORS);
hashimoto@chromium.org955f6482013-09-26 13:32:29 +0900919 GetOriginTaskRunner()->PostTask(FROM_HERE,
920 base::Bind(callback, service_owner));
thestig@chromium.org56057f22013-05-05 00:48:37 +0900921}
922
thestig@chromium.orgc2482f12013-06-11 07:52:34 +0900923void Bus::ListenForServiceOwnerChange(
924 const std::string& service_name,
925 const GetServiceOwnerCallback& callback) {
926 AssertOnOriginThread();
927 DCHECK(!service_name.empty());
928 DCHECK(!callback.is_null());
929
hashimoto@chromium.org955f6482013-09-26 13:32:29 +0900930 GetDBusTaskRunner()->PostTask(
931 FROM_HERE,
932 base::Bind(&Bus::ListenForServiceOwnerChangeInternal,
933 this, service_name, callback));
thestig@chromium.orgc2482f12013-06-11 07:52:34 +0900934}
935
936void Bus::ListenForServiceOwnerChangeInternal(
937 const std::string& service_name,
938 const GetServiceOwnerCallback& callback) {
939 AssertOnDBusThread();
940 DCHECK(!service_name.empty());
941 DCHECK(!callback.is_null());
942
943 if (!Connect() || !SetUpAsyncOperations())
944 return;
945
hashimoto46be6e92014-12-04 16:41:55 +0900946 if (service_owner_changed_listener_map_.empty())
947 AddFilterFunction(Bus::OnServiceOwnerChangedFilter, this);
thestig@chromium.orgc2482f12013-06-11 07:52:34 +0900948
949 ServiceOwnerChangedListenerMap::iterator it =
950 service_owner_changed_listener_map_.find(service_name);
951 if (it == service_owner_changed_listener_map_.end()) {
952 // Add a match rule for the new service name.
953 const std::string name_owner_changed_match_rule =
954 base::StringPrintf(kServiceNameOwnerChangeMatchRule,
955 service_name.c_str());
956 ScopedDBusError error;
957 AddMatch(name_owner_changed_match_rule, error.get());
958 if (error.is_set()) {
959 LOG(ERROR) << "Failed to add match rule for " << service_name
960 << ". Got " << error.name() << ": " << error.message();
961 return;
962 }
963
964 service_owner_changed_listener_map_[service_name].push_back(callback);
965 return;
966 }
967
968 // Check if the callback has already been added.
969 std::vector<GetServiceOwnerCallback>& callbacks = it->second;
970 for (size_t i = 0; i < callbacks.size(); ++i) {
971 if (callbacks[i].Equals(callback))
972 return;
973 }
974 callbacks.push_back(callback);
975}
976
977void Bus::UnlistenForServiceOwnerChange(
978 const std::string& service_name,
979 const GetServiceOwnerCallback& callback) {
980 AssertOnOriginThread();
981 DCHECK(!service_name.empty());
982 DCHECK(!callback.is_null());
983
hashimoto@chromium.org955f6482013-09-26 13:32:29 +0900984 GetDBusTaskRunner()->PostTask(
985 FROM_HERE,
986 base::Bind(&Bus::UnlistenForServiceOwnerChangeInternal,
987 this, service_name, callback));
thestig@chromium.orgc2482f12013-06-11 07:52:34 +0900988}
989
990void Bus::UnlistenForServiceOwnerChangeInternal(
991 const std::string& service_name,
992 const GetServiceOwnerCallback& callback) {
993 AssertOnDBusThread();
994 DCHECK(!service_name.empty());
995 DCHECK(!callback.is_null());
996
997 ServiceOwnerChangedListenerMap::iterator it =
998 service_owner_changed_listener_map_.find(service_name);
999 if (it == service_owner_changed_listener_map_.end())
1000 return;
1001
1002 std::vector<GetServiceOwnerCallback>& callbacks = it->second;
1003 for (size_t i = 0; i < callbacks.size(); ++i) {
1004 if (callbacks[i].Equals(callback)) {
1005 callbacks.erase(callbacks.begin() + i);
1006 break; // There can be only one.
1007 }
1008 }
1009 if (!callbacks.empty())
1010 return;
1011
1012 // Last callback for |service_name| has been removed, remove match rule.
1013 const std::string name_owner_changed_match_rule =
1014 base::StringPrintf(kServiceNameOwnerChangeMatchRule,
1015 service_name.c_str());
1016 ScopedDBusError error;
1017 RemoveMatch(name_owner_changed_match_rule, error.get());
1018 // And remove |service_owner_changed_listener_map_| entry.
1019 service_owner_changed_listener_map_.erase(it);
1020
hashimoto46be6e92014-12-04 16:41:55 +09001021 if (service_owner_changed_listener_map_.empty())
1022 RemoveFilterFunction(Bus::OnServiceOwnerChangedFilter, this);
thestig@chromium.orgc2482f12013-06-11 07:52:34 +09001023}
1024
zqiua84d25f2015-07-08 11:08:30 +09001025std::string Bus::GetConnectionName() {
1026 if (!connection_)
1027 return "";
1028 return dbus_bus_get_unique_name(connection_);
1029}
1030
satorux@chromium.org163f1cb2011-08-18 05:58:12 +09001031dbus_bool_t Bus::OnAddWatch(DBusWatch* raw_watch) {
1032 AssertOnDBusThread();
1033
1034 // watch will be deleted when raw_watch is removed in OnRemoveWatch().
1035 Watch* watch = new Watch(raw_watch);
1036 if (watch->IsReadyToBeWatched()) {
1037 watch->StartWatching();
1038 }
1039 ++num_pending_watches_;
1040 return true;
1041}
1042
1043void Bus::OnRemoveWatch(DBusWatch* raw_watch) {
1044 AssertOnDBusThread();
1045
1046 Watch* watch = static_cast<Watch*>(dbus_watch_get_data(raw_watch));
1047 delete watch;
1048 --num_pending_watches_;
1049}
1050
1051void Bus::OnToggleWatch(DBusWatch* raw_watch) {
1052 AssertOnDBusThread();
1053
1054 Watch* watch = static_cast<Watch*>(dbus_watch_get_data(raw_watch));
1055 if (watch->IsReadyToBeWatched()) {
1056 watch->StartWatching();
1057 } else {
1058 // It's safe to call this if StartWatching() wasn't called, per
1059 // message_pump_libevent.h.
1060 watch->StopWatching();
1061 }
1062}
1063
1064dbus_bool_t Bus::OnAddTimeout(DBusTimeout* raw_timeout) {
1065 AssertOnDBusThread();
1066
1067 // timeout will be deleted when raw_timeout is removed in
1068 // OnRemoveTimeoutThunk().
1069 Timeout* timeout = new Timeout(raw_timeout);
1070 if (timeout->IsReadyToBeMonitored()) {
1071 timeout->StartMonitoring(this);
1072 }
1073 ++num_pending_timeouts_;
1074 return true;
1075}
1076
1077void Bus::OnRemoveTimeout(DBusTimeout* raw_timeout) {
1078 AssertOnDBusThread();
1079
1080 Timeout* timeout = static_cast<Timeout*>(dbus_timeout_get_data(raw_timeout));
1081 timeout->Complete();
1082 --num_pending_timeouts_;
1083}
1084
1085void Bus::OnToggleTimeout(DBusTimeout* raw_timeout) {
1086 AssertOnDBusThread();
1087
1088 Timeout* timeout = static_cast<Timeout*>(dbus_timeout_get_data(raw_timeout));
1089 if (timeout->IsReadyToBeMonitored()) {
1090 timeout->StartMonitoring(this);
1091 } else {
1092 timeout->StopMonitoring();
1093 }
1094}
1095
1096void Bus::OnDispatchStatusChanged(DBusConnection* connection,
1097 DBusDispatchStatus status) {
1098 DCHECK_EQ(connection, connection_);
1099 AssertOnDBusThread();
1100
satorux@chromium.org163f1cb2011-08-18 05:58:12 +09001101 // We cannot call ProcessAllIncomingDataIfAny() here, as calling
1102 // dbus_connection_dispatch() inside DBusDispatchStatusFunction is
1103 // prohibited by the D-Bus library. Hence, we post a task here instead.
1104 // See comments for dbus_connection_set_dispatch_status_function().
hashimoto@chromium.org955f6482013-09-26 13:32:29 +09001105 GetDBusTaskRunner()->PostTask(FROM_HERE,
1106 base::Bind(&Bus::ProcessAllIncomingDataIfAny,
1107 this));
satorux@chromium.org163f1cb2011-08-18 05:58:12 +09001108}
1109
thestig@chromium.orgc2482f12013-06-11 07:52:34 +09001110void Bus::OnServiceOwnerChanged(DBusMessage* message) {
1111 DCHECK(message);
1112 AssertOnDBusThread();
1113
1114 // |message| will be unrefed on exit of the function. Increment the
1115 // reference so we can use it in Signal::FromRawMessage() below.
1116 dbus_message_ref(message);
1117 scoped_ptr<Signal> signal(Signal::FromRawMessage(message));
1118
1119 // Confirm the validity of the NameOwnerChanged signal.
1120 if (signal->GetMember() != kNameOwnerChangedSignal ||
1121 signal->GetInterface() != DBUS_INTERFACE_DBUS ||
1122 signal->GetSender() != DBUS_SERVICE_DBUS) {
1123 return;
1124 }
1125
1126 MessageReader reader(signal.get());
1127 std::string service_name;
1128 std::string old_owner;
1129 std::string new_owner;
1130 if (!reader.PopString(&service_name) ||
1131 !reader.PopString(&old_owner) ||
1132 !reader.PopString(&new_owner)) {
1133 return;
1134 }
1135
1136 ServiceOwnerChangedListenerMap::const_iterator it =
1137 service_owner_changed_listener_map_.find(service_name);
1138 if (it == service_owner_changed_listener_map_.end())
1139 return;
1140
1141 const std::vector<GetServiceOwnerCallback>& callbacks = it->second;
1142 for (size_t i = 0; i < callbacks.size(); ++i) {
hashimoto@chromium.org955f6482013-09-26 13:32:29 +09001143 GetOriginTaskRunner()->PostTask(FROM_HERE,
1144 base::Bind(callbacks[i], new_owner));
thestig@chromium.orgc2482f12013-06-11 07:52:34 +09001145 }
1146}
1147
1148// static
satorux@chromium.org163f1cb2011-08-18 05:58:12 +09001149dbus_bool_t Bus::OnAddWatchThunk(DBusWatch* raw_watch, void* data) {
1150 Bus* self = static_cast<Bus*>(data);
1151 return self->OnAddWatch(raw_watch);
1152}
1153
thestig@chromium.orgc2482f12013-06-11 07:52:34 +09001154// static
satorux@chromium.org163f1cb2011-08-18 05:58:12 +09001155void Bus::OnRemoveWatchThunk(DBusWatch* raw_watch, void* data) {
1156 Bus* self = static_cast<Bus*>(data);
jhawkins@chromium.org957fc4c2012-04-28 04:40:42 +09001157 self->OnRemoveWatch(raw_watch);
satorux@chromium.org163f1cb2011-08-18 05:58:12 +09001158}
1159
thestig@chromium.orgc2482f12013-06-11 07:52:34 +09001160// static
satorux@chromium.org163f1cb2011-08-18 05:58:12 +09001161void Bus::OnToggleWatchThunk(DBusWatch* raw_watch, void* data) {
1162 Bus* self = static_cast<Bus*>(data);
jhawkins@chromium.org957fc4c2012-04-28 04:40:42 +09001163 self->OnToggleWatch(raw_watch);
satorux@chromium.org163f1cb2011-08-18 05:58:12 +09001164}
1165
thestig@chromium.orgc2482f12013-06-11 07:52:34 +09001166// static
satorux@chromium.org163f1cb2011-08-18 05:58:12 +09001167dbus_bool_t Bus::OnAddTimeoutThunk(DBusTimeout* raw_timeout, void* data) {
1168 Bus* self = static_cast<Bus*>(data);
1169 return self->OnAddTimeout(raw_timeout);
1170}
1171
thestig@chromium.orgc2482f12013-06-11 07:52:34 +09001172// static
satorux@chromium.org163f1cb2011-08-18 05:58:12 +09001173void Bus::OnRemoveTimeoutThunk(DBusTimeout* raw_timeout, void* data) {
1174 Bus* self = static_cast<Bus*>(data);
jhawkins@chromium.org957fc4c2012-04-28 04:40:42 +09001175 self->OnRemoveTimeout(raw_timeout);
satorux@chromium.org163f1cb2011-08-18 05:58:12 +09001176}
1177
thestig@chromium.orgc2482f12013-06-11 07:52:34 +09001178// static
satorux@chromium.org163f1cb2011-08-18 05:58:12 +09001179void Bus::OnToggleTimeoutThunk(DBusTimeout* raw_timeout, void* data) {
1180 Bus* self = static_cast<Bus*>(data);
jhawkins@chromium.org957fc4c2012-04-28 04:40:42 +09001181 self->OnToggleTimeout(raw_timeout);
satorux@chromium.org163f1cb2011-08-18 05:58:12 +09001182}
1183
thestig@chromium.orgc2482f12013-06-11 07:52:34 +09001184// static
satorux@chromium.org163f1cb2011-08-18 05:58:12 +09001185void Bus::OnDispatchStatusChangedThunk(DBusConnection* connection,
1186 DBusDispatchStatus status,
1187 void* data) {
1188 Bus* self = static_cast<Bus*>(data);
jhawkins@chromium.org957fc4c2012-04-28 04:40:42 +09001189 self->OnDispatchStatusChanged(connection, status);
satorux@chromium.org163f1cb2011-08-18 05:58:12 +09001190}
1191
thestig@chromium.orgc2482f12013-06-11 07:52:34 +09001192// static
nona@chromium.org5a44d2b2013-02-08 19:53:39 +09001193DBusHandlerResult Bus::OnConnectionDisconnectedFilter(
thestig@chromium.org074b1db2013-02-20 10:36:53 +09001194 DBusConnection* connection,
1195 DBusMessage* message,
1196 void* data) {
nona@chromium.org5a44d2b2013-02-08 19:53:39 +09001197 if (dbus_message_is_signal(message,
1198 DBUS_INTERFACE_LOCAL,
1199 kDisconnectedSignal)) {
hashimoto76b0cff2014-12-09 13:50:23 +09001200 // Abort when the connection is lost.
1201 LOG(FATAL) << "D-Bus connection was disconnected. Aborting.";
nona@chromium.org5a44d2b2013-02-08 19:53:39 +09001202 }
1203 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1204}
1205
thestig@chromium.orgc2482f12013-06-11 07:52:34 +09001206// static
1207DBusHandlerResult Bus::OnServiceOwnerChangedFilter(
1208 DBusConnection* connection,
1209 DBusMessage* message,
1210 void* data) {
1211 if (dbus_message_is_signal(message,
1212 DBUS_INTERFACE_DBUS,
1213 kNameOwnerChangedSignal)) {
1214 Bus* self = static_cast<Bus*>(data);
1215 self->OnServiceOwnerChanged(message);
1216 }
1217 // Always return unhandled to let others, e.g. ObjectProxies, handle the same
1218 // signal.
1219 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1220}
1221
satorux@chromium.org163f1cb2011-08-18 05:58:12 +09001222} // namespace dbus