| Index: dbus/exported_object.cc
|
| diff --git a/dbus/exported_object.cc b/dbus/exported_object.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..1793ed27cfb71428aeee73ec83acfa57b3472c68
|
| --- /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
|
|
|