Index: dbus/bus.cc |
diff --git a/dbus/bus.cc b/dbus/bus.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..b71ce5ad086e4870939bd2c51bfd0261e8c27c88 |
--- /dev/null |
+++ b/dbus/bus.cc |
@@ -0,0 +1,234 @@ |
+// Copyright (c) 2011 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "dbus/bus.h" |
+ |
+#include "base/logging.h" |
+#include "base/message_loop.h" |
+#include "base/threading/thread.h" |
+#include "base/threading/thread_restrictions.h" |
+#include "dbus/object_proxy.h" |
+ |
+namespace dbus { |
+ |
+namespace { |
+ |
+// The class is used for watching the file descriptor used for D-Bus |
+// communication. |
+class Watch : public base::MessagePumpLibevent::Watcher { |
+ public: |
+ Watch(DBusWatch* watch) |
+ : raw_watch_(watch) { |
+ dbus_watch_set_data(raw_watch_, this, NULL); |
+ } |
+ |
+ ~Watch() { |
+ dbus_watch_set_data(raw_watch_, NULL, NULL); |
+ } |
+ |
+ // Returns true if the underlying file descriptor is ready to be watched. |
+ bool IsReadyToBeWatched() { |
+ return dbus_watch_get_enabled(raw_watch_); |
+ } |
+ |
+ // Starts watching the underlying file descriptor. |
+ void StartWatching() { |
+ const int fd = dbus_watch_get_unix_fd(raw_watch_); |
+ const int flags = dbus_watch_get_flags(raw_watch_); |
+ |
+ MessageLoopForIO::Mode mode; |
+ if ((flags & DBUS_WATCH_READABLE) && (flags & DBUS_WATCH_WRITABLE)) |
+ mode = MessageLoopForIO::WATCH_READ_WRITE; |
+ if (flags & DBUS_WATCH_READABLE) |
stevenjb
2011/07/29 21:54:17
else if
satorux1
2011/08/01 19:56:41
Done.
|
+ mode = MessageLoopForIO::WATCH_READ; |
+ if (flags & DBUS_WATCH_WRITABLE) |
stevenjb
2011/07/29 21:54:17
else if
satorux1
2011/08/01 19:56:41
Done.
|
+ mode = MessageLoopForIO::WATCH_WRITE; |
stevenjb
2011/07/29 21:54:17
else NOTREACHED()
satorux1
2011/08/01 19:56:41
Done.
|
+ |
+ const bool persistent = true; // Watch persistently. |
+ const bool success = MessageLoopForIO::current()->WatchFileDescriptor( |
+ fd, |
+ persistent, |
+ mode, |
+ &file_descriptor_watcher_, |
+ this); |
+ DCHECK(success) << "Unable to allocate memory"; |
+ } |
+ |
+ // Stops watching the underlying file descriptor. |
+ void StopWatching() { |
+ file_descriptor_watcher_->StopWatchingFileDescriptor(); |
+ } |
+ |
+ private: |
+ // Implement MessagePumpLibevent::Watcher. |
+ virtual void OnFileCanReadWithoutBlocking(int fd) { |
+ const bool success = dbus_watch_handle(raw_watch_, DBUS_WATCH_READABLE); |
+ DCHECK(success) << "Unable to allocate memory"; |
+ } |
+ |
+ // Implement MessagePumpLibevent::Watcher. |
+ virtual void OnFileCanWriteWithoutBlocking(int fd) { |
+ const bool success = dbus_watch_handle(raw_watch_, DBUS_WATCH_WRITABLE); |
+ DCHECK(success) << "Unable to allocate memory"; |
+ } |
+ |
+ DBusWatch* raw_watch_; |
+ base::MessagePumpLibevent::FileDescriptorWatcher file_descriptor_watcher_; |
+}; |
+ |
+} // namespace |
+ |
+Bus::Options::Options() |
+ : bus_type(SESSION), |
+ connection_type(PRIVATE), |
+ io_thread(NULL) { |
+} |
+ |
+Bus::Bus(const Options& options) |
+ : bus_type_(options.bus_type), |
+ connection_type_(options.connection_type), |
+ io_thread_(options.io_thread), |
+ connection_(NULL) { |
+ if (io_thread_) { |
+ DCHECK(io_thread_->IsRunning()) |
+ << "The IO thread should be running"; |
+ DCHECK_EQ(MessageLoop::TYPE_IO, |
+ io_thread_->message_loop()->type()) |
+ << "The IO thread should have an MessageLoopForIO attached"; |
+ } |
+} |
+ |
+Bus::~Bus() { |
+ // Private connection should be closed. |
+ if (connection_ && connection_type_ == PRIVATE) { |
+ dbus_connection_close(connection_); |
+ } |
+} |
+ |
+ObjectProxy* Bus::GetObjectProxy(const std::string& service_name, |
+ const std::string& object_path) { |
+ return new ObjectProxy(this, service_name, object_path); |
+} |
+ |
+bool Bus::Init() { |
+ // dbus_bus_get_private() and dbus_bus_get() are blocking calls. |
+ base::ThreadRestrictions::AssertIOAllowed(); |
+ |
+ // Check if it's already initialized. |
+ if (connection_) |
+ return true; |
+ |
+ DBusError error = {}; |
stevenjb
2011/07/29 21:54:17
nit: don't need to initialize error (and better no
satorux1
2011/08/01 19:56:41
Done.
|
+ dbus_error_init(&error); |
+ const DBusBusType dbus_bus_type = static_cast<DBusBusType>(bus_type_); |
+ if (connection_type_ == PRIVATE) { |
+ connection_ = dbus_bus_get_private(dbus_bus_type, &error); |
+ } else { |
+ connection_ = dbus_bus_get(dbus_bus_type, &error); |
+ } |
+ if (!connection_) { |
+ if (dbus_error_is_set(&error)) { |
+ LOG(ERROR) << error.message; |
+ } |
+ return false; |
+ } |
+ |
+ // This is unnecessary if client only uses synchronous APIs, but it |
+ // won't hurt. |
+ SetUpAsyncOperations(); |
+ |
+ return true; |
+} |
+ |
+void Bus::SetUpAsyncOperations() { |
+ // Process all the incoming data if any, so that OnDispatchStatus() will |
+ // be called when the incoming data is ready. |
+ ProcessAllIncomingDataIfAny(connection_); |
+ |
+ dbus_connection_set_watch_functions(connection_, |
+ &Bus::OnAddWatchStub, |
+ &Bus::OnRemoveWatchStub, |
+ &Bus::OnToggleWatchStub, |
+ this, |
+ NULL); |
+ // TODO(satorux): Timeout is not yet implemented. |
+ dbus_connection_set_timeout_functions(connection_, |
+ NULL, |
+ NULL, |
+ NULL, |
+ NULL, |
+ NULL); |
+ dbus_connection_set_dispatch_status_function( |
+ connection_, |
+ &Bus::OnDispatchStatusChangedStub, |
+ connection_, |
+ NULL); |
+} |
+ |
+void Bus::ProcessAllIncomingDataIfAny(DBusConnection* connection) { |
+ if (dbus_connection_get_dispatch_status(connection) == |
+ DBUS_DISPATCH_DATA_REMAINS) { |
+ while (dbus_connection_dispatch(connection) == |
+ DBUS_DISPATCH_DATA_REMAINS); |
+ } |
+} |
+ |
+void Bus::PostTaskToIoThread(const tracked_objects::Location& from_here, |
+ const base::Closure& task) { |
+ DCHECK(io_thread_); |
+ io_thread_->message_loop()->PostTask(from_here, task); |
+} |
+ |
+dbus_bool_t Bus::OnAddWatch(DBusWatch* raw_watch) { |
+ Watch* watch = new Watch(raw_watch); |
stevenjb
2011/07/29 21:54:17
This confused me for a while re: when Watch gets d
satorux1
2011/08/01 19:56:41
Good point. I think adding a comment is sufficient
|
+ if (watch->IsReadyToBeWatched()) { |
+ watch->StartWatching(); |
+ } |
+ return true; |
+} |
+ |
+void Bus::OnRemoveWatch(DBusWatch* raw_watch) { |
+ Watch* watch = reinterpret_cast<Watch*>(dbus_watch_get_data(raw_watch)); |
stevenjb
2011/07/29 21:54:17
dbus_watch_get_data should return a void*, so stat
satorux1
2011/08/01 19:56:41
You are right. Done.
|
+ delete watch; |
+} |
+ |
+void Bus::OnToggleWatch(DBusWatch* raw_watch) { |
+ Watch* watch = reinterpret_cast<Watch*>(dbus_watch_get_data(raw_watch)); |
+ if (watch->IsReadyToBeWatched()) { |
+ watch->StartWatching(); |
+ } else { |
+ watch->StopWatching(); |
stevenjb
2011/07/29 21:54:17
Is it safe to call this if StartWatching() was nev
satorux1
2011/08/01 19:56:41
Good point. Yes it's safe. Added a comment about i
|
+ } |
+} |
+ |
+void Bus::OnDispatchStatusChanged(DBusConnection* connection, |
+ DBusDispatchStatus status) { |
+ if (!dbus_connection_get_is_connected(connection)) |
+ return; |
+ ProcessAllIncomingDataIfAny(connection); |
+} |
+ |
+dbus_bool_t Bus::OnAddWatchStub(DBusWatch* raw_watch, void* data) { |
stevenjb
2011/07/29 21:54:17
These could be local functions instead of class st
satorux1
2011/08/01 19:56:41
Here, we need to call self->OnAddWatch(), which is
|
+ Bus* self = reinterpret_cast<Bus*>(data); |
+ return self->OnAddWatch(raw_watch); |
+} |
+ |
+void Bus::OnRemoveWatchStub(DBusWatch* raw_watch, void* data) { |
+ Bus* self = reinterpret_cast<Bus*>(data); |
+ return self->OnRemoveWatch(raw_watch); |
+} |
+ |
+void Bus::OnToggleWatchStub(DBusWatch* raw_watch, void* data) { |
+ Bus* self = reinterpret_cast<Bus*>(data); |
+ return self->OnToggleWatch(raw_watch); |
+} |
+ |
+void Bus::OnDispatchStatusChangedStub(DBusConnection* connection, |
+ DBusDispatchStatus status, |
+ void* data) { |
+ Bus* self = reinterpret_cast<Bus*>(data); |
+ return self->OnDispatchStatusChanged(connection, status); |
+} |
+ |
+} // namespace dbus |