123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360 |
- // Copyright (c) 2012 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 <stdint.h>
- #include <utility>
- #include "base/bind.h"
- #include "base/logging.h"
- #include "base/memory/ref_counted.h"
- #include "base/metrics/histogram_macros.h"
- #include "base/task/sequenced_task_runner.h"
- #include "base/task/task_runner.h"
- #include "base/threading/thread_restrictions.h"
- #include "base/time/time.h"
- #include "dbus/bus.h"
- #include "dbus/message.h"
- #include "dbus/object_path.h"
- #include "dbus/scoped_dbus_error.h"
- #include "dbus/util.h"
- namespace dbus {
- namespace {
- // Used for success ratio histograms. 1 for success, 0 for failure.
- const int kSuccessRatioHistogramMaxValue = 2;
- } // namespace
- ExportedObject::ExportedObject(Bus* bus,
- const ObjectPath& object_path)
- : bus_(bus),
- object_path_(object_path),
- object_is_registered_(false) {
- LOG_IF(FATAL, !object_path_.IsValid()) << object_path_.value();
- }
- ExportedObject::~ExportedObject() {
- DCHECK(!object_is_registered_);
- }
- bool ExportedObject::ExportMethodAndBlock(
- const std::string& interface_name,
- const std::string& method_name,
- const MethodCallCallback& method_call_callback) {
- bus_->AssertOnDBusThread();
- // Check if the method is already exported.
- const std::string absolute_method_name =
- GetAbsoluteMemberName(interface_name, method_name);
- if (method_table_.find(absolute_method_name) != method_table_.end()) {
- LOG(ERROR) << absolute_method_name << " is already exported";
- return false;
- }
- if (!bus_->Connect())
- return false;
- if (!bus_->SetUpAsyncOperations())
- return false;
- if (!Register())
- return false;
- // Add the method callback to the method table.
- method_table_[absolute_method_name] = method_call_callback;
- return true;
- }
- bool ExportedObject::UnexportMethodAndBlock(const std::string& interface_name,
- const std::string& method_name) {
- bus_->AssertOnDBusThread();
- const std::string absolute_method_name =
- GetAbsoluteMemberName(interface_name, method_name);
- MethodTable::const_iterator iter = method_table_.find(absolute_method_name);
- if (iter == method_table_.end()) {
- LOG(ERROR) << absolute_method_name << " is not exported";
- return false;
- }
- method_table_.erase(iter);
- return true;
- }
- void ExportedObject::ExportMethod(
- const std::string& interface_name,
- const std::string& method_name,
- const MethodCallCallback& method_call_callback,
- OnExportedCallback on_exported_callback) {
- bus_->AssertOnOriginThread();
- base::OnceClosure task = base::BindOnce(
- &ExportedObject::ExportMethodInternal, this, interface_name, method_name,
- method_call_callback, std::move(on_exported_callback));
- bus_->GetDBusTaskRunner()->PostTask(FROM_HERE, std::move(task));
- }
- void ExportedObject::UnexportMethod(
- const std::string& interface_name,
- const std::string& method_name,
- OnUnexportedCallback on_unexported_callback) {
- bus_->AssertOnOriginThread();
- base::OnceClosure task = base::BindOnce(
- &ExportedObject::UnexportMethodInternal, this, interface_name,
- method_name, std::move(on_unexported_callback));
- bus_->GetDBusTaskRunner()->PostTask(FROM_HERE, std::move(task));
- }
- void ExportedObject::SendSignal(Signal* signal) {
- // For signals, the object path should be set to the path to the sender
- // object, which is this exported object here.
- CHECK(signal->SetPath(object_path_));
- // Increment the reference count so we can safely reference the
- // underlying signal message until the signal sending is complete. This
- // will be unref'ed in SendSignalInternal().
- DBusMessage* signal_message = signal->raw_message();
- dbus_message_ref(signal_message);
- const base::TimeTicks start_time = base::TimeTicks::Now();
- if (bus_->GetDBusTaskRunner()->RunsTasksInCurrentSequence()) {
- // The Chrome OS power manager doesn't use a dedicated TaskRunner for
- // sending DBus messages. Sending signals asynchronously can cause an
- // inversion in the message order if the power manager calls
- // ObjectProxy::CallMethodAndBlock() before going back to the top level of
- // the MessageLoop: crbug.com/472361.
- SendSignalInternal(start_time, signal_message);
- } else {
- bus_->GetDBusTaskRunner()->PostTask(
- FROM_HERE, base::BindOnce(&ExportedObject::SendSignalInternal, this,
- start_time, signal_message));
- }
- }
- 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,
- const MethodCallCallback& method_call_callback,
- OnExportedCallback on_exported_callback) {
- bus_->AssertOnDBusThread();
- const bool success = ExportMethodAndBlock(interface_name,
- method_name,
- method_call_callback);
- bus_->GetOriginTaskRunner()->PostTask(
- FROM_HERE, base::BindOnce(&ExportedObject::OnExported, this,
- std::move(on_exported_callback), interface_name,
- method_name, success));
- }
- void ExportedObject::UnexportMethodInternal(
- const std::string& interface_name,
- const std::string& method_name,
- OnUnexportedCallback on_unexported_callback) {
- bus_->AssertOnDBusThread();
- const bool success = UnexportMethodAndBlock(interface_name, method_name);
- bus_->GetOriginTaskRunner()->PostTask(
- FROM_HERE, base::BindOnce(&ExportedObject::OnUnexported, this,
- std::move(on_unexported_callback),
- 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();
- std::move(on_exported_callback).Run(interface_name, method_name, success);
- }
- void ExportedObject::OnUnexported(OnExportedCallback on_unexported_callback,
- const std::string& interface_name,
- const std::string& method_name,
- bool success) {
- bus_->AssertOnOriginThread();
- std::move(on_unexported_callback).Run(interface_name, method_name, success);
- }
- void ExportedObject::SendSignalInternal(base::TimeTicks start_time,
- DBusMessage* signal_message) {
- uint32_t serial = 0;
- bus_->Send(signal_message, &serial);
- dbus_message_unref(signal_message);
- // Record time spent to send the the signal. This is not accurate as the
- // signal will actually be sent from the next run of the message loop,
- // but we can at least tell the number of signals sent.
- UMA_HISTOGRAM_TIMES("DBus.SignalSendTime",
- base::TimeTicks::Now() - start_time);
- }
- 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 register the object: " << object_path_.value()
- << ": " << (error.is_set() ? error.message() : "");
- return false;
- }
- object_is_registered_ = true;
- return true;
- }
- DBusHandlerResult ExportedObject::HandleMessage(
- DBusConnection* connection,
- DBusMessage* raw_message) {
- bus_->AssertOnDBusThread();
- // ExportedObject only handles method calls. Ignore other message types (e.g.
- // signal).
- if (dbus_message_get_type(raw_message) != DBUS_MESSAGE_TYPE_METHOD_CALL)
- 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 MethodCall.
- dbus_message_ref(raw_message);
- std::unique_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.
- LOG(WARNING) << "Interface is missing: " << method_call->ToString();
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
- }
- // Check if we know about the method.
- const std::string absolute_method_name = GetAbsoluteMemberName(
- interface, member);
- MethodTable::const_iterator iter = method_table_.find(absolute_method_name);
- if (iter == method_table_.end()) {
- // Don't know about the method.
- LOG(WARNING) << "Unknown method: " << method_call->ToString();
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
- }
- const base::TimeTicks start_time = base::TimeTicks::Now();
- if (bus_->HasDBusThread()) {
- // Post a task to run the method in the origin thread.
- bus_->GetOriginTaskRunner()->PostTask(
- FROM_HERE,
- base::BindOnce(&ExportedObject::RunMethod, this, iter->second,
- std::move(method_call), start_time));
- } else {
- // If the D-Bus thread is not used, just call the method directly.
- MethodCall* method = method_call.get();
- iter->second.Run(
- method, base::BindOnce(&ExportedObject::SendResponse, this, start_time,
- std::move(method_call)));
- }
- // It's valid to say HANDLED here, and send a method response at a later
- // time from OnMethodCompleted() asynchronously.
- return DBUS_HANDLER_RESULT_HANDLED;
- }
- void ExportedObject::RunMethod(const MethodCallCallback& method_call_callback,
- std::unique_ptr<MethodCall> method_call,
- base::TimeTicks start_time) {
- bus_->AssertOnOriginThread();
- MethodCall* method = method_call.get();
- method_call_callback.Run(
- method, base::BindOnce(&ExportedObject::SendResponse, this, start_time,
- std::move(method_call)));
- }
- void ExportedObject::SendResponse(base::TimeTicks start_time,
- std::unique_ptr<MethodCall> method_call,
- std::unique_ptr<Response> response) {
- DCHECK(method_call);
- if (bus_->HasDBusThread()) {
- bus_->GetDBusTaskRunner()->PostTask(
- FROM_HERE, base::BindOnce(&ExportedObject::OnMethodCompleted, this,
- std::move(method_call), std::move(response),
- start_time));
- } else {
- OnMethodCompleted(std::move(method_call), std::move(response), start_time);
- }
- }
- void ExportedObject::OnMethodCompleted(std::unique_ptr<MethodCall> method_call,
- std::unique_ptr<Response> response,
- base::TimeTicks start_time) {
- bus_->AssertOnDBusThread();
- // Record if the method call is successful, or not. 1 if successful.
- UMA_HISTOGRAM_ENUMERATION("DBus.ExportedMethodHandleSuccess",
- response ? 1 : 0,
- kSuccessRatioHistogramMaxValue);
- // Check if the bus is still connected. If the method takes long to
- // complete, the bus may be shut down meanwhile.
- if (!bus_->IsConnected())
- return;
- if (!response) {
- // Something bad happened in the method call.
- std::unique_ptr<ErrorResponse> error_response(ErrorResponse::FromMethodCall(
- method_call.get(), DBUS_ERROR_FAILED,
- "error occurred in " + method_call->GetMember()));
- bus_->Send(error_response->raw_message(), nullptr);
- return;
- }
- // The method call was successful.
- bus_->Send(response->raw_message(), nullptr);
- // Record time spent to handle the the method call. Don't include failures.
- UMA_HISTOGRAM_TIMES("DBus.ExportedMethodHandleTime",
- base::TimeTicks::Now() - start_time);
- }
- 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
|