blob: 0c6a4217235790b3315e1583a31193af49dfa900 [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.
4//
5// TODO(satorux):
6// - Handle "disconnected" signal.
satorux@chromium.org163f1cb2011-08-18 05:58:12 +09007
8#include "dbus/bus.h"
9
10#include "base/bind.h"
11#include "base/logging.h"
12#include "base/message_loop.h"
mdm@chromium.org45f2c6a2011-09-07 05:03:24 +090013#include "base/message_loop_proxy.h"
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090014#include "base/stl_util.h"
15#include "base/threading/thread.h"
16#include "base/threading/thread_restrictions.h"
satorux@chromium.orgd336d452011-09-02 15:56:23 +090017#include "base/time.h"
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090018#include "dbus/exported_object.h"
19#include "dbus/object_proxy.h"
20#include "dbus/scoped_dbus_error.h"
21
22namespace dbus {
23
24namespace {
25
26// The class is used for watching the file descriptor used for D-Bus
27// communication.
28class Watch : public base::MessagePumpLibevent::Watcher {
29 public:
30 Watch(DBusWatch* watch)
31 : raw_watch_(watch) {
32 dbus_watch_set_data(raw_watch_, this, NULL);
33 }
34
35 ~Watch() {
36 dbus_watch_set_data(raw_watch_, NULL, NULL);
37 }
38
39 // Returns true if the underlying file descriptor is ready to be watched.
40 bool IsReadyToBeWatched() {
41 return dbus_watch_get_enabled(raw_watch_);
42 }
43
44 // Starts watching the underlying file descriptor.
45 void StartWatching() {
46 const int file_descriptor = dbus_watch_get_unix_fd(raw_watch_);
47 const int flags = dbus_watch_get_flags(raw_watch_);
48
49 MessageLoopForIO::Mode mode = MessageLoopForIO::WATCH_READ;
50 if ((flags & DBUS_WATCH_READABLE) && (flags & DBUS_WATCH_WRITABLE))
51 mode = MessageLoopForIO::WATCH_READ_WRITE;
52 else if (flags & DBUS_WATCH_READABLE)
53 mode = MessageLoopForIO::WATCH_READ;
54 else if (flags & DBUS_WATCH_WRITABLE)
55 mode = MessageLoopForIO::WATCH_WRITE;
56 else
57 NOTREACHED();
58
59 const bool persistent = true; // Watch persistently.
60 const bool success = MessageLoopForIO::current()->WatchFileDescriptor(
61 file_descriptor,
62 persistent,
63 mode,
64 &file_descriptor_watcher_,
65 this);
66 CHECK(success) << "Unable to allocate memory";
67 }
68
69 // Stops watching the underlying file descriptor.
70 void StopWatching() {
71 file_descriptor_watcher_.StopWatchingFileDescriptor();
72 }
73
74 private:
75 // Implement MessagePumpLibevent::Watcher.
76 virtual void OnFileCanReadWithoutBlocking(int file_descriptor) {
77 const bool success = dbus_watch_handle(raw_watch_, DBUS_WATCH_READABLE);
78 CHECK(success) << "Unable to allocate memory";
79 }
80
81 // Implement MessagePumpLibevent::Watcher.
82 virtual void OnFileCanWriteWithoutBlocking(int file_descriptor) {
83 const bool success = dbus_watch_handle(raw_watch_, DBUS_WATCH_WRITABLE);
84 CHECK(success) << "Unable to allocate memory";
85 }
86
87 DBusWatch* raw_watch_;
88 base::MessagePumpLibevent::FileDescriptorWatcher file_descriptor_watcher_;
89};
90
91// The class is used for monitoring the timeout used for D-Bus method
92// calls.
93//
94// Unlike Watch, Timeout is a ref counted object, to ensure that |this| of
95// the object is is alive when HandleTimeout() is called. It's unlikely
96// but it may be possible that HandleTimeout() is called after
97// Bus::OnRemoveTimeout(). That's why we don't simply delete the object in
98// Bus::OnRemoveTimeout().
99class Timeout : public base::RefCountedThreadSafe<Timeout> {
100 public:
101 Timeout(DBusTimeout* timeout)
102 : raw_timeout_(timeout),
103 monitoring_is_active_(false),
104 is_completed(false) {
105 dbus_timeout_set_data(raw_timeout_, this, NULL);
106 AddRef(); // Balanced on Complete().
107 }
108
109 // Returns true if the timeout is ready to be monitored.
110 bool IsReadyToBeMonitored() {
111 return dbus_timeout_get_enabled(raw_timeout_);
112 }
113
114 // Starts monitoring the timeout.
115 void StartMonitoring(dbus::Bus* bus) {
116 bus->PostDelayedTaskToDBusThread(FROM_HERE,
117 base::Bind(&Timeout::HandleTimeout,
118 this),
119 GetIntervalInMs());
120 monitoring_is_active_ = true;
121 }
122
123 // Stops monitoring the timeout.
124 void StopMonitoring() {
125 // We cannot take back the delayed task we posted in
126 // StartMonitoring(), so we just mark the monitoring is inactive now.
127 monitoring_is_active_ = false;
128 }
129
130 // Returns the interval in milliseconds.
131 int GetIntervalInMs() {
132 return dbus_timeout_get_interval(raw_timeout_);
133 }
134
135 // Cleans up the raw_timeout and marks that timeout is completed.
136 // See the class comment above for why we are doing this.
137 void Complete() {
138 dbus_timeout_set_data(raw_timeout_, NULL, NULL);
139 is_completed = true;
140 Release();
141 }
142
143 private:
144 friend class base::RefCountedThreadSafe<Timeout>;
145 ~Timeout() {
146 }
147
148 // Handles the timeout.
149 void HandleTimeout() {
150 // If the timeout is marked completed, we should do nothing. This can
151 // occur if this function is called after Bus::OnRemoveTimeout().
152 if (is_completed)
153 return;
satorux@chromium.orgc6ac7572011-09-01 03:02:43 +0900154 // Skip if monitoring is canceled.
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900155 if (!monitoring_is_active_)
156 return;
157
158 const bool success = dbus_timeout_handle(raw_timeout_);
159 CHECK(success) << "Unable to allocate memory";
160 }
161
162 DBusTimeout* raw_timeout_;
163 bool monitoring_is_active_;
164 bool is_completed;
165};
166
167} // namespace
168
169Bus::Options::Options()
170 : bus_type(SESSION),
mdm@chromium.org45f2c6a2011-09-07 05:03:24 +0900171 connection_type(PRIVATE) {
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900172}
173
174Bus::Options::~Options() {
175}
176
177Bus::Bus(const Options& options)
178 : bus_type_(options.bus_type),
179 connection_type_(options.connection_type),
mdm@chromium.org45f2c6a2011-09-07 05:03:24 +0900180 dbus_thread_message_loop_proxy_(options.dbus_thread_message_loop_proxy),
satorux@chromium.orgd336d452011-09-02 15:56:23 +0900181 on_shutdown_(false /* manual_reset */, false /* initially_signaled */),
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900182 connection_(NULL),
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900183 origin_thread_id_(base::PlatformThread::CurrentId()),
satorux@chromium.org326a6f82011-08-27 16:26:34 +0900184 async_operations_set_up_(false),
satorux@chromium.orgd336d452011-09-02 15:56:23 +0900185 shutdown_completed_(false),
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900186 num_pending_watches_(0),
187 num_pending_timeouts_(0) {
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900188 // This is safe to call multiple times.
189 dbus_threads_init_default();
satorux@chromium.orgcff09492011-09-09 07:28:42 +0900190 // The origin message loop is unnecessary if the client uses synchronous
191 // functions only.
192 if (MessageLoop::current())
193 origin_message_loop_proxy_ = MessageLoop::current()->message_loop_proxy();
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900194}
195
196Bus::~Bus() {
197 DCHECK(!connection_);
198 DCHECK(owned_service_names_.empty());
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900199 DCHECK(match_rules_added_.empty());
200 DCHECK(filter_functions_added_.empty());
201 DCHECK(registered_object_paths_.empty());
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900202 DCHECK_EQ(0, num_pending_watches_);
jamescook@chromium.org255cd352011-11-24 16:00:43 +0900203 // TODO(satorux): This check fails occasionally in browser_tests for tests
204 // that run very quickly. Perhaps something does not have time to clean up.
205 // Despite the check failing, the tests seem to run fine. crosbug.com/23416
206 // DCHECK_EQ(0, num_pending_timeouts_);
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900207}
208
209ObjectProxy* Bus::GetObjectProxy(const std::string& service_name,
210 const std::string& object_path) {
adamk@chromium.org35c0eef2012-02-11 06:45:23 +0900211 return GetObjectProxyWithOptions(service_name, object_path,
212 ObjectProxy::DEFAULT_OPTIONS);
213}
214
215ObjectProxy* Bus::GetObjectProxyWithOptions(const std::string& service_name,
216 const std::string& object_path,
217 int options) {
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900218 AssertOnOriginThread();
219
satorux@chromium.orgdccbb7b2011-08-24 04:25:20 +0900220 // Check if we already have the requested object proxy.
adamk@chromium.org35c0eef2012-02-11 06:45:23 +0900221 const ObjectProxyTable::key_type key(service_name + object_path, options);
satorux@chromium.orgdccbb7b2011-08-24 04:25:20 +0900222 ObjectProxyTable::iterator iter = object_proxy_table_.find(key);
223 if (iter != object_proxy_table_.end()) {
224 return iter->second;
225 }
226
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900227 scoped_refptr<ObjectProxy> object_proxy =
adamk@chromium.org35c0eef2012-02-11 06:45:23 +0900228 new ObjectProxy(this, service_name, object_path, options);
satorux@chromium.orgdccbb7b2011-08-24 04:25:20 +0900229 object_proxy_table_[key] = object_proxy;
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900230
satorux@chromium.orgdccbb7b2011-08-24 04:25:20 +0900231 return object_proxy.get();
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900232}
233
234ExportedObject* Bus::GetExportedObject(const std::string& service_name,
235 const std::string& object_path) {
236 AssertOnOriginThread();
237
satorux@chromium.orgdccbb7b2011-08-24 04:25:20 +0900238 // Check if we already have the requested exported object.
239 const std::string key = service_name + object_path;
240 ExportedObjectTable::iterator iter = exported_object_table_.find(key);
241 if (iter != exported_object_table_.end()) {
242 return iter->second;
243 }
244
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900245 scoped_refptr<ExportedObject> exported_object =
246 new ExportedObject(this, service_name, object_path);
satorux@chromium.orgdccbb7b2011-08-24 04:25:20 +0900247 exported_object_table_[key] = exported_object;
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900248
satorux@chromium.orgdccbb7b2011-08-24 04:25:20 +0900249 return exported_object.get();
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900250}
251
252bool Bus::Connect() {
253 // dbus_bus_get_private() and dbus_bus_get() are blocking calls.
254 AssertOnDBusThread();
255
256 // Check if it's already initialized.
257 if (connection_)
258 return true;
259
260 ScopedDBusError error;
261 const DBusBusType dbus_bus_type = static_cast<DBusBusType>(bus_type_);
262 if (connection_type_ == PRIVATE) {
263 connection_ = dbus_bus_get_private(dbus_bus_type, error.get());
264 } else {
265 connection_ = dbus_bus_get(dbus_bus_type, error.get());
266 }
267 if (!connection_) {
268 LOG(ERROR) << "Failed to connect to the bus: "
269 << (dbus_error_is_set(error.get()) ? error.message() : "");
270 return false;
271 }
272 // We shouldn't exit on the disconnected signal.
273 dbus_connection_set_exit_on_disconnect(connection_, false);
274
275 return true;
276}
277
278void Bus::ShutdownAndBlock() {
279 AssertOnDBusThread();
280
281 // Unregister the exported objects.
satorux@chromium.orgdccbb7b2011-08-24 04:25:20 +0900282 for (ExportedObjectTable::iterator iter = exported_object_table_.begin();
283 iter != exported_object_table_.end(); ++iter) {
284 iter->second->Unregister();
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900285 }
286
287 // Release all service names.
288 for (std::set<std::string>::iterator iter = owned_service_names_.begin();
289 iter != owned_service_names_.end();) {
290 // This is a bit tricky but we should increment the iter here as
291 // ReleaseOwnership() may remove |service_name| from the set.
292 const std::string& service_name = *iter++;
293 ReleaseOwnership(service_name);
294 }
295 if (!owned_service_names_.empty()) {
296 LOG(ERROR) << "Failed to release all service names. # of services left: "
297 << owned_service_names_.size();
298 }
299
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900300 // Detach from the remote objects.
satorux@chromium.orgdccbb7b2011-08-24 04:25:20 +0900301 for (ObjectProxyTable::iterator iter = object_proxy_table_.begin();
302 iter != object_proxy_table_.end(); ++iter) {
303 iter->second->Detach();
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900304 }
305
satorux@chromium.orgf06eb892011-10-13 09:45:26 +0900306 // Release object proxies and exported objects here. We should do this
307 // here rather than in the destructor to avoid memory leaks due to
308 // cyclic references.
309 object_proxy_table_.clear();
310 exported_object_table_.clear();
311
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900312 // Private connection should be closed.
satorux@chromium.orgd336d452011-09-02 15:56:23 +0900313 if (connection_) {
314 if (connection_type_ == PRIVATE)
315 dbus_connection_close(connection_);
316 // dbus_connection_close() won't unref.
317 dbus_connection_unref(connection_);
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900318 }
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900319
320 connection_ = NULL;
satorux@chromium.orgd336d452011-09-02 15:56:23 +0900321 shutdown_completed_ = true;
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900322}
323
satorux@chromium.orgd336d452011-09-02 15:56:23 +0900324void Bus::ShutdownOnDBusThreadAndBlock() {
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900325 AssertOnOriginThread();
mdm@chromium.org45f2c6a2011-09-07 05:03:24 +0900326 DCHECK(dbus_thread_message_loop_proxy_.get());
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900327
satorux@chromium.orgd336d452011-09-02 15:56:23 +0900328 PostTaskToDBusThread(FROM_HERE, base::Bind(
329 &Bus::ShutdownOnDBusThreadAndBlockInternal,
330 this));
331
332 // Wait until the shutdown is complete on the D-Bus thread.
333 // The shutdown should not hang, but set timeout just in case.
334 const int kTimeoutSecs = 3;
335 const base::TimeDelta timeout(base::TimeDelta::FromSeconds(kTimeoutSecs));
336 const bool signaled = on_shutdown_.TimedWait(timeout);
337 LOG_IF(ERROR, !signaled) << "Failed to shutdown the bus";
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900338}
339
340bool Bus::RequestOwnership(const std::string& service_name) {
341 DCHECK(connection_);
342 // dbus_bus_request_name() is a blocking call.
343 AssertOnDBusThread();
344
345 // Check if we already own the service name.
346 if (owned_service_names_.find(service_name) != owned_service_names_.end()) {
347 return true;
348 }
349
350 ScopedDBusError error;
351 const int result = dbus_bus_request_name(connection_,
352 service_name.c_str(),
353 DBUS_NAME_FLAG_DO_NOT_QUEUE,
354 error.get());
355 if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
satorux@chromium.orgc6ac7572011-09-01 03:02:43 +0900356 LOG(ERROR) << "Failed to get the ownership of " << service_name << ": "
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900357 << (dbus_error_is_set(error.get()) ? error.message() : "");
358 return false;
359 }
360 owned_service_names_.insert(service_name);
361 return true;
362}
363
364bool Bus::ReleaseOwnership(const std::string& service_name) {
365 DCHECK(connection_);
366 // dbus_bus_request_name() is a blocking call.
367 AssertOnDBusThread();
368
369 // Check if we already own the service name.
370 std::set<std::string>::iterator found =
371 owned_service_names_.find(service_name);
372 if (found == owned_service_names_.end()) {
373 LOG(ERROR) << service_name << " is not owned by the bus";
374 return false;
375 }
376
377 ScopedDBusError error;
378 const int result = dbus_bus_release_name(connection_, service_name.c_str(),
379 error.get());
380 if (result == DBUS_RELEASE_NAME_REPLY_RELEASED) {
381 owned_service_names_.erase(found);
382 return true;
383 } else {
satorux@chromium.orgc6ac7572011-09-01 03:02:43 +0900384 LOG(ERROR) << "Failed to release the ownership of " << service_name << ": "
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900385 << (error.is_set() ? error.message() : "");
386 return false;
387 }
388}
389
390bool Bus::SetUpAsyncOperations() {
391 DCHECK(connection_);
392 AssertOnDBusThread();
393
satorux@chromium.org326a6f82011-08-27 16:26:34 +0900394 if (async_operations_set_up_)
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900395 return true;
396
397 // Process all the incoming data if any, so that OnDispatchStatus() will
398 // be called when the incoming data is ready.
399 ProcessAllIncomingDataIfAny();
400
401 bool success = dbus_connection_set_watch_functions(connection_,
402 &Bus::OnAddWatchThunk,
403 &Bus::OnRemoveWatchThunk,
404 &Bus::OnToggleWatchThunk,
405 this,
406 NULL);
407 CHECK(success) << "Unable to allocate memory";
408
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900409 success = dbus_connection_set_timeout_functions(connection_,
410 &Bus::OnAddTimeoutThunk,
411 &Bus::OnRemoveTimeoutThunk,
412 &Bus::OnToggleTimeoutThunk,
413 this,
414 NULL);
415 CHECK(success) << "Unable to allocate memory";
416
417 dbus_connection_set_dispatch_status_function(
418 connection_,
419 &Bus::OnDispatchStatusChangedThunk,
420 this,
421 NULL);
422
satorux@chromium.org326a6f82011-08-27 16:26:34 +0900423 async_operations_set_up_ = true;
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900424
425 return true;
426}
427
428DBusMessage* Bus::SendWithReplyAndBlock(DBusMessage* request,
429 int timeout_ms,
430 DBusError* error) {
431 DCHECK(connection_);
432 AssertOnDBusThread();
433
434 return dbus_connection_send_with_reply_and_block(
435 connection_, request, timeout_ms, error);
436}
437
438void Bus::SendWithReply(DBusMessage* request,
439 DBusPendingCall** pending_call,
440 int timeout_ms) {
441 DCHECK(connection_);
442 AssertOnDBusThread();
443
444 const bool success = dbus_connection_send_with_reply(
445 connection_, request, pending_call, timeout_ms);
446 CHECK(success) << "Unable to allocate memory";
447}
448
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900449void Bus::Send(DBusMessage* request, uint32* serial) {
450 DCHECK(connection_);
451 AssertOnDBusThread();
452
453 const bool success = dbus_connection_send(connection_, request, serial);
454 CHECK(success) << "Unable to allocate memory";
455}
456
satorux@chromium.org66bc4c22011-10-06 09:20:53 +0900457bool Bus::AddFilterFunction(DBusHandleMessageFunction filter_function,
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900458 void* user_data) {
459 DCHECK(connection_);
460 AssertOnDBusThread();
461
satorux@chromium.org66bc4c22011-10-06 09:20:53 +0900462 std::pair<DBusHandleMessageFunction, void*> filter_data_pair =
463 std::make_pair(filter_function, user_data);
464 if (filter_functions_added_.find(filter_data_pair) !=
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900465 filter_functions_added_.end()) {
satorux@chromium.org66bc4c22011-10-06 09:20:53 +0900466 VLOG(1) << "Filter function already exists: " << filter_function
467 << " with associated data: " << user_data;
468 return false;
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900469 }
470
471 const bool success = dbus_connection_add_filter(
472 connection_, filter_function, user_data, NULL);
473 CHECK(success) << "Unable to allocate memory";
satorux@chromium.org66bc4c22011-10-06 09:20:53 +0900474 filter_functions_added_.insert(filter_data_pair);
475 return true;
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900476}
477
satorux@chromium.org66bc4c22011-10-06 09:20:53 +0900478bool Bus::RemoveFilterFunction(DBusHandleMessageFunction filter_function,
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900479 void* user_data) {
480 DCHECK(connection_);
481 AssertOnDBusThread();
482
satorux@chromium.org66bc4c22011-10-06 09:20:53 +0900483 std::pair<DBusHandleMessageFunction, void*> filter_data_pair =
484 std::make_pair(filter_function, user_data);
485 if (filter_functions_added_.find(filter_data_pair) ==
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900486 filter_functions_added_.end()) {
satorux@chromium.org66bc4c22011-10-06 09:20:53 +0900487 VLOG(1) << "Requested to remove an unknown filter function: "
488 << filter_function
489 << " with associated data: " << user_data;
490 return false;
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900491 }
492
493 dbus_connection_remove_filter(connection_, filter_function, user_data);
satorux@chromium.org66bc4c22011-10-06 09:20:53 +0900494 filter_functions_added_.erase(filter_data_pair);
495 return true;
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900496}
497
498void Bus::AddMatch(const std::string& match_rule, DBusError* error) {
499 DCHECK(connection_);
500 AssertOnDBusThread();
501
502 if (match_rules_added_.find(match_rule) != match_rules_added_.end()) {
satorux@chromium.orga3a97932011-10-13 08:47:13 +0900503 VLOG(1) << "Match rule already exists: " << match_rule;
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900504 return;
505 }
506
507 dbus_bus_add_match(connection_, match_rule.c_str(), error);
508 match_rules_added_.insert(match_rule);
509}
510
511void Bus::RemoveMatch(const std::string& match_rule, DBusError* error) {
512 DCHECK(connection_);
513 AssertOnDBusThread();
514
515 if (match_rules_added_.find(match_rule) == match_rules_added_.end()) {
516 LOG(ERROR) << "Requested to remove an unknown match rule: " << match_rule;
517 return;
518 }
519
520 dbus_bus_remove_match(connection_, match_rule.c_str(), error);
521 match_rules_added_.erase(match_rule);
522}
523
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900524bool Bus::TryRegisterObjectPath(const std::string& object_path,
525 const DBusObjectPathVTable* vtable,
526 void* user_data,
527 DBusError* error) {
528 DCHECK(connection_);
529 AssertOnDBusThread();
530
satorux@chromium.org326a6f82011-08-27 16:26:34 +0900531 if (registered_object_paths_.find(object_path) !=
532 registered_object_paths_.end()) {
533 LOG(ERROR) << "Object path already registered: " << object_path;
534 return false;
535 }
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900536
537 const bool success = dbus_connection_try_register_object_path(
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900538 connection_,
539 object_path.c_str(),
540 vtable,
541 user_data,
542 error);
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900543 if (success)
544 registered_object_paths_.insert(object_path);
545 return success;
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900546}
547
548void Bus::UnregisterObjectPath(const std::string& object_path) {
549 DCHECK(connection_);
550 AssertOnDBusThread();
551
satorux@chromium.org326a6f82011-08-27 16:26:34 +0900552 if (registered_object_paths_.find(object_path) ==
553 registered_object_paths_.end()) {
554 LOG(ERROR) << "Requested to unregister an unknown object path: "
555 << object_path;
556 return;
557 }
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900558
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900559 const bool success = dbus_connection_unregister_object_path(
560 connection_,
561 object_path.c_str());
562 CHECK(success) << "Unable to allocate memory";
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900563 registered_object_paths_.erase(object_path);
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900564}
565
satorux@chromium.orgd336d452011-09-02 15:56:23 +0900566void Bus::ShutdownOnDBusThreadAndBlockInternal() {
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900567 AssertOnDBusThread();
568
569 ShutdownAndBlock();
satorux@chromium.orgd336d452011-09-02 15:56:23 +0900570 on_shutdown_.Signal();
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900571}
572
573void Bus::ProcessAllIncomingDataIfAny() {
574 AssertOnDBusThread();
575
576 // As mentioned at the class comment in .h file, connection_ can be NULL.
577 if (!connection_ || !dbus_connection_get_is_connected(connection_))
578 return;
579
580 if (dbus_connection_get_dispatch_status(connection_) ==
581 DBUS_DISPATCH_DATA_REMAINS) {
582 while (dbus_connection_dispatch(connection_) ==
583 DBUS_DISPATCH_DATA_REMAINS);
584 }
585}
586
587void Bus::PostTaskToOriginThread(const tracked_objects::Location& from_here,
588 const base::Closure& task) {
satorux@chromium.orgcff09492011-09-09 07:28:42 +0900589 DCHECK(origin_message_loop_proxy_.get());
590 if (!origin_message_loop_proxy_->PostTask(from_here, task)) {
591 LOG(WARNING) << "Failed to post a task to the origin message loop";
592 }
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900593}
594
595void Bus::PostTaskToDBusThread(const tracked_objects::Location& from_here,
596 const base::Closure& task) {
satorux@chromium.orgcff09492011-09-09 07:28:42 +0900597 if (dbus_thread_message_loop_proxy_.get()) {
598 if (!dbus_thread_message_loop_proxy_->PostTask(from_here, task)) {
599 LOG(WARNING) << "Failed to post a task to the D-Bus thread message loop";
600 }
601 } else {
602 DCHECK(origin_message_loop_proxy_.get());
603 if (!origin_message_loop_proxy_->PostTask(from_here, task)) {
604 LOG(WARNING) << "Failed to post a task to the origin message loop";
605 }
606 }
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900607}
608
609void Bus::PostDelayedTaskToDBusThread(
610 const tracked_objects::Location& from_here,
611 const base::Closure& task,
612 int delay_ms) {
satorux@chromium.orgcff09492011-09-09 07:28:42 +0900613 if (dbus_thread_message_loop_proxy_.get()) {
614 if (!dbus_thread_message_loop_proxy_->PostDelayedTask(
615 from_here, task, delay_ms)) {
616 LOG(WARNING) << "Failed to post a task to the D-Bus thread message loop";
617 }
618 } else {
619 DCHECK(origin_message_loop_proxy_.get());
620 if (!origin_message_loop_proxy_->PostDelayedTask(
621 from_here, task, delay_ms)) {
622 LOG(WARNING) << "Failed to post a task to the origin message loop";
623 }
624 }
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900625}
626
627bool Bus::HasDBusThread() {
mdm@chromium.org45f2c6a2011-09-07 05:03:24 +0900628 return dbus_thread_message_loop_proxy_.get() != NULL;
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900629}
630
631void Bus::AssertOnOriginThread() {
632 DCHECK_EQ(origin_thread_id_, base::PlatformThread::CurrentId());
633}
634
635void Bus::AssertOnDBusThread() {
636 base::ThreadRestrictions::AssertIOAllowed();
637
mdm@chromium.org45f2c6a2011-09-07 05:03:24 +0900638 if (dbus_thread_message_loop_proxy_.get()) {
639 DCHECK(dbus_thread_message_loop_proxy_->BelongsToCurrentThread());
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900640 } else {
641 AssertOnOriginThread();
642 }
643}
644
645dbus_bool_t Bus::OnAddWatch(DBusWatch* raw_watch) {
646 AssertOnDBusThread();
647
648 // watch will be deleted when raw_watch is removed in OnRemoveWatch().
649 Watch* watch = new Watch(raw_watch);
650 if (watch->IsReadyToBeWatched()) {
651 watch->StartWatching();
652 }
653 ++num_pending_watches_;
654 return true;
655}
656
657void Bus::OnRemoveWatch(DBusWatch* raw_watch) {
658 AssertOnDBusThread();
659
660 Watch* watch = static_cast<Watch*>(dbus_watch_get_data(raw_watch));
661 delete watch;
662 --num_pending_watches_;
663}
664
665void Bus::OnToggleWatch(DBusWatch* raw_watch) {
666 AssertOnDBusThread();
667
668 Watch* watch = static_cast<Watch*>(dbus_watch_get_data(raw_watch));
669 if (watch->IsReadyToBeWatched()) {
670 watch->StartWatching();
671 } else {
672 // It's safe to call this if StartWatching() wasn't called, per
673 // message_pump_libevent.h.
674 watch->StopWatching();
675 }
676}
677
678dbus_bool_t Bus::OnAddTimeout(DBusTimeout* raw_timeout) {
679 AssertOnDBusThread();
680
681 // timeout will be deleted when raw_timeout is removed in
682 // OnRemoveTimeoutThunk().
683 Timeout* timeout = new Timeout(raw_timeout);
684 if (timeout->IsReadyToBeMonitored()) {
685 timeout->StartMonitoring(this);
686 }
687 ++num_pending_timeouts_;
688 return true;
689}
690
691void Bus::OnRemoveTimeout(DBusTimeout* raw_timeout) {
692 AssertOnDBusThread();
693
694 Timeout* timeout = static_cast<Timeout*>(dbus_timeout_get_data(raw_timeout));
695 timeout->Complete();
696 --num_pending_timeouts_;
697}
698
699void Bus::OnToggleTimeout(DBusTimeout* raw_timeout) {
700 AssertOnDBusThread();
701
702 Timeout* timeout = static_cast<Timeout*>(dbus_timeout_get_data(raw_timeout));
703 if (timeout->IsReadyToBeMonitored()) {
704 timeout->StartMonitoring(this);
705 } else {
706 timeout->StopMonitoring();
707 }
708}
709
710void Bus::OnDispatchStatusChanged(DBusConnection* connection,
711 DBusDispatchStatus status) {
712 DCHECK_EQ(connection, connection_);
713 AssertOnDBusThread();
714
715 if (!dbus_connection_get_is_connected(connection))
716 return;
717
718 // We cannot call ProcessAllIncomingDataIfAny() here, as calling
719 // dbus_connection_dispatch() inside DBusDispatchStatusFunction is
720 // prohibited by the D-Bus library. Hence, we post a task here instead.
721 // See comments for dbus_connection_set_dispatch_status_function().
722 PostTaskToDBusThread(FROM_HERE,
723 base::Bind(&Bus::ProcessAllIncomingDataIfAny,
724 this));
725}
726
727dbus_bool_t Bus::OnAddWatchThunk(DBusWatch* raw_watch, void* data) {
728 Bus* self = static_cast<Bus*>(data);
729 return self->OnAddWatch(raw_watch);
730}
731
732void Bus::OnRemoveWatchThunk(DBusWatch* raw_watch, void* data) {
733 Bus* self = static_cast<Bus*>(data);
734 return self->OnRemoveWatch(raw_watch);
735}
736
737void Bus::OnToggleWatchThunk(DBusWatch* raw_watch, void* data) {
738 Bus* self = static_cast<Bus*>(data);
739 return self->OnToggleWatch(raw_watch);
740}
741
742dbus_bool_t Bus::OnAddTimeoutThunk(DBusTimeout* raw_timeout, void* data) {
743 Bus* self = static_cast<Bus*>(data);
744 return self->OnAddTimeout(raw_timeout);
745}
746
747void Bus::OnRemoveTimeoutThunk(DBusTimeout* raw_timeout, void* data) {
748 Bus* self = static_cast<Bus*>(data);
749 return self->OnRemoveTimeout(raw_timeout);
750}
751
752void Bus::OnToggleTimeoutThunk(DBusTimeout* raw_timeout, void* data) {
753 Bus* self = static_cast<Bus*>(data);
754 return self->OnToggleTimeout(raw_timeout);
755}
756
757void Bus::OnDispatchStatusChangedThunk(DBusConnection* connection,
758 DBusDispatchStatus status,
759 void* data) {
760 Bus* self = static_cast<Bus*>(data);
761 return self->OnDispatchStatusChanged(connection, status);
762}
763
764} // namespace dbus