summaryrefslogtreecommitdiffstats
path: root/dbus/exported_object.cc
diff options
context:
space:
mode:
authorsatorux@chromium.org <satorux@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-08-17 20:58:12 +0000
committersatorux@chromium.org <satorux@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-08-17 20:58:12 +0000
commita510761104333ec7da3943399f059ff693839856 (patch)
tree2a9f81a60bccf4db3367ad1b8f71e9ea130efb7d /dbus/exported_object.cc
parent07f93af15169ee054552a376ac4953abd1346cb2 (diff)
downloadchromium_src-a510761104333ec7da3943399f059ff693839856.zip
chromium_src-a510761104333ec7da3943399f059ff693839856.tar.gz
chromium_src-a510761104333ec7da3943399f059ff693839856.tar.bz2
Implement Bus and ObjectProxy classes for our D-Bus library.
ObjectProxy is used to access remote objects. ExportedObject is used to export objects to other D-Bus BUG=90036 TEST=run unit tests. The code is not yet used in Chrome. Review URL: http://codereview.chromium.org/7491029 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@97204 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'dbus/exported_object.cc')
-rw-r--r--dbus/exported_object.cc259
1 files changed, 259 insertions, 0 deletions
diff --git a/dbus/exported_object.cc b/dbus/exported_object.cc
new file mode 100644
index 0000000..1793ed2
--- /dev/null
+++ b/dbus/exported_object.cc
@@ -0,0 +1,259 @@
+// 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/exported_object.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop.h"
+#include "base/threading/thread_restrictions.h"
+#include "base/time.h"
+#include "dbus/bus.h"
+#include "dbus/message.h"
+#include "dbus/scoped_dbus_error.h"
+
+namespace dbus {
+
+namespace {
+
+// Gets the absolute method name by concatenating the interface name and
+// the method name. Used for building keys for method_table_ in
+// ExportedObject.
+std::string GetAbsoluteMethodName(
+ const std::string& interface_name,
+ const std::string& method_name) {
+ return interface_name + "." + method_name;
+}
+
+} // namespace
+
+ExportedObject::ExportedObject(Bus* bus,
+ const std::string& service_name,
+ const std::string& object_path)
+ : bus_(bus),
+ service_name_(service_name),
+ object_path_(object_path),
+ object_is_registered_(false),
+ method_is_called_(false),
+ response_from_method_(NULL),
+ on_method_is_called_(&method_is_called_lock_) {
+}
+
+ExportedObject::~ExportedObject() {
+ DCHECK(!object_is_registered_);
+}
+
+bool ExportedObject::ExportMethodAndBlock(
+ const std::string& interface_name,
+ const std::string& method_name,
+ MethodCallCallback method_call_callback) {
+ bus_->AssertOnDBusThread();
+
+ if (!bus_->Connect())
+ return false;
+ if (!bus_->SetUpAsyncOperations())
+ return false;
+ if (!bus_->RequestOwnership(service_name_))
+ return false;
+ if (!Register())
+ return false;
+
+ const std::string absolute_method_name =
+ GetAbsoluteMethodName(interface_name, method_name);
+ if (method_table_.find(absolute_method_name) != method_table_.end()) {
+ LOG(ERROR) << absolute_method_name << " is already exported";
+ return false;
+ }
+ method_table_[absolute_method_name] = method_call_callback;
+
+ return true;
+}
+
+void ExportedObject::ExportMethod(const std::string& interface_name,
+ const std::string& method_name,
+ MethodCallCallback method_call_callback,
+ OnExportedCallback on_exported_calback) {
+ bus_->AssertOnOriginThread();
+
+ base::Closure task = base::Bind(&ExportedObject::ExportMethodInternal,
+ this,
+ interface_name,
+ method_name,
+ method_call_callback,
+ on_exported_calback);
+ bus_->PostTaskToDBusThread(FROM_HERE, task);
+}
+
+void ExportedObject::Unregister() {
+ bus_->AssertOnDBusThread();
+
+ if (!object_is_registered_)
+ return;
+
+ bus_->UnregisterObjectPath(object_path_);
+ object_is_registered_ = false;
+}
+
+void ExportedObject::ExportMethodInternal(
+ const std::string& interface_name,
+ const std::string& method_name,
+ MethodCallCallback method_call_callback,
+ OnExportedCallback on_exported_calback) {
+ bus_->AssertOnDBusThread();
+
+ const bool success = ExportMethodAndBlock(interface_name,
+ method_name,
+ method_call_callback);
+ bus_->PostTaskToOriginThread(FROM_HERE,
+ base::Bind(&ExportedObject::OnExported,
+ this,
+ on_exported_calback,
+ interface_name,
+ method_name,
+ success));
+}
+
+void ExportedObject::OnExported(OnExportedCallback on_exported_callback,
+ const std::string& interface_name,
+ const std::string& method_name,
+ bool success) {
+ bus_->AssertOnOriginThread();
+
+ on_exported_callback.Run(interface_name, method_name, success);
+}
+
+bool ExportedObject::Register() {
+ bus_->AssertOnDBusThread();
+
+ if (object_is_registered_)
+ return true;
+
+ ScopedDBusError error;
+
+ DBusObjectPathVTable vtable = {};
+ vtable.message_function = &ExportedObject::HandleMessageThunk;
+ vtable.unregister_function = &ExportedObject::OnUnregisteredThunk;
+ const bool success = bus_->TryRegisterObjectPath(object_path_,
+ &vtable,
+ this,
+ error.get());
+ if (!success) {
+ LOG(ERROR) << "Failed to regiser the object: " << object_path_ << ": "
+ << (error.is_set() ? error.message() : "");
+ return false;
+ }
+
+ object_is_registered_ = true;
+ return true;
+}
+
+DBusHandlerResult ExportedObject::HandleMessage(
+ DBusConnection* connection,
+ DBusMessage* raw_message) {
+ bus_->AssertOnDBusThread();
+ DCHECK_EQ(DBUS_MESSAGE_TYPE_METHOD_CALL, dbus_message_get_type(raw_message));
+
+ // raw_message will be unrefed on exit of the function. Increment the
+ // reference so we can use it in MethodCall.
+ dbus_message_ref(raw_message);
+ scoped_ptr<MethodCall> method_call(
+ MethodCall::FromRawMessage(raw_message));
+ const std::string interface = method_call->GetInterface();
+ const std::string member = method_call->GetMember();
+
+ if (interface.empty()) {
+ // We don't support method calls without interface.
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ // Check if we know about the method.
+ const std::string absolute_method_name = GetAbsoluteMethodName(
+ interface, member);
+ MethodTable::const_iterator iter = method_table_.find(absolute_method_name);
+ if (iter == method_table_.end()) {
+ // Don't know about the method.
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ Response* response = NULL;
+ if (bus_->HasDBusThread()) {
+ response_from_method_ = NULL;
+ method_is_called_ = false;
+ // Post a task to run the method in the origin thread.
+ bus_->PostTaskToOriginThread(FROM_HERE,
+ base::Bind(&ExportedObject::RunMethod,
+ this,
+ iter->second,
+ method_call.get()));
+ // Wait until the method call is done. Blocking is not desirable but we
+ // should return the response to the dbus-daemon in the function, so we
+ // don't have a choice. We wait in the D-Bus thread, so it should be ok.
+ {
+ // We need a timeout here in case the method gets stuck.
+ const int kTimeoutSecs = 10;
+ const base::TimeDelta timeout(
+ base::TimeDelta::FromSeconds(kTimeoutSecs));
+ const base::Time start_time = base::Time::Now();
+
+ base::AutoLock auto_lock(method_is_called_lock_);
+ while (!method_is_called_) {
+ on_method_is_called_.TimedWait(timeout);
+ CHECK(base::Time::Now() - start_time < timeout)
+ << "Method " << absolute_method_name << " timed out";
+ }
+ }
+ response = response_from_method_;
+ } 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.
+ response = iter->second.Run(method_call.get());
+ }
+
+ if (!response) {
+ // Something bad happend in the method call.
+ scoped_ptr<dbus::ErrorResponse> error_response(
+ ErrorResponse::FromMethodCall(method_call.get(),
+ DBUS_ERROR_FAILED,
+ "error occurred in " + member));
+ dbus_connection_send(connection, error_response->raw_message(), NULL);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ // The method call was successful.
+ dbus_connection_send(connection, response->raw_message(), NULL);
+ delete response;
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+void ExportedObject::RunMethod(MethodCallCallback method_call_callback,
+ MethodCall* method_call) {
+ bus_->AssertOnOriginThread();
+
+ base::AutoLock auto_lock(method_is_called_lock_);
+ response_from_method_ = method_call_callback.Run(method_call);
+ method_is_called_ = true;
+ on_method_is_called_.Signal();
+}
+
+void ExportedObject::OnUnregistered(DBusConnection* connection) {
+}
+
+DBusHandlerResult ExportedObject::HandleMessageThunk(
+ DBusConnection* connection,
+ DBusMessage* raw_message,
+ void* user_data) {
+ ExportedObject* self = reinterpret_cast<ExportedObject*>(user_data);
+ return self->HandleMessage(connection, raw_message);
+}
+
+void ExportedObject::OnUnregisteredThunk(DBusConnection *connection,
+ void* user_data) {
+ ExportedObject* self = reinterpret_cast<ExportedObject*>(user_data);
+ return self->OnUnregistered(connection);
+}
+
+} // namespace dbus