summaryrefslogtreecommitdiffstats
path: root/dbus/object_proxy.cc
diff options
context:
space:
mode:
Diffstat (limited to 'dbus/object_proxy.cc')
-rw-r--r--dbus/object_proxy.cc177
1 files changed, 176 insertions, 1 deletions
diff --git a/dbus/object_proxy.cc b/dbus/object_proxy.cc
index 24dbde2..0ab3ce5 100644
--- a/dbus/object_proxy.cc
+++ b/dbus/object_proxy.cc
@@ -7,12 +7,26 @@
#include "base/bind.h"
#include "base/logging.h"
#include "base/message_loop.h"
+#include "base/stringprintf.h"
#include "base/threading/thread.h"
#include "base/threading/thread_restrictions.h"
#include "dbus/message.h"
#include "dbus/object_proxy.h"
#include "dbus/scoped_dbus_error.h"
+namespace {
+
+// Gets the absolute signal name by concatenating the interface name and
+// the signal name. Used for building keys for method_table_ in
+// ObjectProxy.
+std::string GetAbsoluteSignalName(
+ const std::string& interface_name,
+ const std::string& signal_name) {
+ return interface_name + "." + signal_name;
+}
+
+} // namespace
+
namespace dbus {
ObjectProxy::ObjectProxy(Bus* bus,
@@ -20,7 +34,8 @@ ObjectProxy::ObjectProxy(Bus* bus,
const std::string& object_path)
: bus_(bus),
service_name_(service_name),
- object_path_(object_path) {
+ object_path_(object_path),
+ filter_added_(false) {
}
ObjectProxy::~ObjectProxy() {
@@ -82,6 +97,37 @@ void ObjectProxy::CallMethod(MethodCall* method_call,
bus_->PostTaskToDBusThread(FROM_HERE, task);
}
+void ObjectProxy::ConnectToSignal(const std::string& interface_name,
+ const std::string& signal_name,
+ SignalCallback signal_callback,
+ OnConnectedCallback on_connected_callback) {
+ bus_->AssertOnOriginThread();
+
+ bus_->PostTaskToDBusThread(FROM_HERE,
+ base::Bind(&ObjectProxy::ConnectToSignalInternal,
+ this,
+ interface_name,
+ signal_name,
+ signal_callback,
+ on_connected_callback));
+}
+
+void ObjectProxy::Detach() {
+ bus_->AssertOnDBusThread();
+
+ if (filter_added_)
+ bus_->RemoveFilterFunction(&ObjectProxy::HandleMessageThunk, this);
+
+ for (size_t i = 0; i < match_rules_.size(); ++i) {
+ ScopedDBusError error;
+ bus_->RemoveMatch(match_rules_[i], error.get());
+ if (error.is_set()) {
+ // There is nothing we can do to recover, so just print the error.
+ LOG(ERROR) << "Failed to remove match rule: " << match_rules_[i];
+ }
+ }
+}
+
ObjectProxy::OnPendingCallIsCompleteData::OnPendingCallIsCompleteData(
ObjectProxy* in_object_proxy,
ResponseCallback in_response_callback)
@@ -194,4 +240,133 @@ void ObjectProxy::OnPendingCallIsCompleteThunk(DBusPendingCall* pending_call,
delete data;
}
+void ObjectProxy::ConnectToSignalInternal(
+ const std::string& interface_name,
+ const std::string& signal_name,
+ SignalCallback signal_callback,
+ OnConnectedCallback on_connected_callback) {
+ bus_->AssertOnDBusThread();
+
+ // Check if the object is already connected to the signal.
+ const std::string absolute_signal_name =
+ GetAbsoluteSignalName(interface_name, signal_name);
+ if (method_table_.find(absolute_signal_name) != method_table_.end()) {
+ LOG(ERROR) << "The object proxy is already connected to "
+ << absolute_signal_name;
+ return;
+ }
+
+ // Will become true, if everything is successful.
+ bool success = false;
+
+ if (bus_->Connect() && bus_->SetUpAsyncOperations()) {
+ // We should add the filter only once. Otherwise, HandleMessage() will
+ // be called more than once.
+ if (!filter_added_) {
+ bus_->AddFilterFunction(&ObjectProxy::HandleMessageThunk, this);
+ filter_added_ = true;
+ }
+ // Add a match rule so the signal goes through HandleMessage().
+ const std::string match_rule =
+ base::StringPrintf("type='signal', interface='%s', path='%s'",
+ interface_name.c_str(),
+ object_path_.c_str());
+ ScopedDBusError error;
+ bus_->AddMatch(match_rule, error.get());;
+ if (error.is_set()) {
+ LOG(ERROR) << "Failed to add match rule: " << match_rule;
+ } else {
+ // Store the match rule, so that we can remove this in Detach().
+ match_rules_.push_back(match_rule);
+ // Add the signal callback to the method table.
+ method_table_[absolute_signal_name] = signal_callback;
+ success = true;
+ }
+ }
+
+ // Run on_connected_callback in the origin thread.
+ bus_->PostTaskToOriginThread(
+ FROM_HERE,
+ base::Bind(&ObjectProxy::OnConnected,
+ this,
+ on_connected_callback,
+ interface_name,
+ signal_name,
+ success));
+}
+
+void ObjectProxy::OnConnected(OnConnectedCallback on_connected_callback,
+ const std::string& interface_name,
+ const std::string& signal_name,
+ bool success) {
+ bus_->AssertOnOriginThread();
+
+ on_connected_callback.Run(interface_name, signal_name, success);
+}
+
+DBusHandlerResult ObjectProxy::HandleMessage(
+ DBusConnection* connection,
+ DBusMessage* raw_message) {
+ bus_->AssertOnDBusThread();
+ if (dbus_message_get_type(raw_message) != DBUS_MESSAGE_TYPE_SIGNAL)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ // raw_message will be unrefed on exit of the function. Increment the
+ // reference so we can use it in Signal.
+ dbus_message_ref(raw_message);
+ scoped_ptr<Signal> signal(
+ Signal::FromRawMessage(raw_message));
+
+ // The signal is not coming from the remote object we are attaching to.
+ if (signal->GetPath() != object_path_)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ const std::string interface = signal->GetInterface();
+ const std::string member = signal->GetMember();
+
+ // Check if we know about the method.
+ const std::string absolute_signal_name = GetAbsoluteSignalName(
+ interface, member);
+ MethodTable::const_iterator iter = method_table_.find(absolute_signal_name);
+ if (iter == method_table_.end()) {
+ // Don't know about the method.
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ if (bus_->HasDBusThread()) {
+ // Post a task to run the method in the origin thread.
+ // Transfer the ownership of |signal| to RunMethod().
+ // |released_signal| will be deleted in RunMethod().
+ Signal* released_signal = signal.release();
+ bus_->PostTaskToOriginThread(FROM_HERE,
+ base::Bind(&ObjectProxy::RunMethod,
+ this,
+ iter->second,
+ released_signal));
+ } else {
+ // If the D-Bus thread is not used, just call the method directly. We
+ // don't need the complicated logic to wait for the method call to be
+ // complete.
+ iter->second.Run(signal.get());
+ }
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+void ObjectProxy::RunMethod(SignalCallback signal_callback,
+ Signal* signal) {
+ bus_->AssertOnOriginThread();
+
+ signal_callback.Run(signal);
+ delete signal;
+}
+
+DBusHandlerResult ObjectProxy::HandleMessageThunk(
+ DBusConnection* connection,
+ DBusMessage* raw_message,
+ void* user_data) {
+ ObjectProxy* self = reinterpret_cast<ObjectProxy*>(user_data);
+ return self->HandleMessage(connection, raw_message);
+}
+
} // namespace dbus