123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764 |
- // 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/object_proxy.h"
- #include <stddef.h>
- #include <utility>
- #include "base/bind.h"
- #include "base/callback_helpers.h"
- #include "base/debug/alias.h"
- #include "base/debug/leak_annotations.h"
- #include "base/logging.h"
- #include "base/metrics/histogram_macros.h"
- #include "base/strings/string_piece.h"
- #include "base/strings/stringprintf.h"
- #include "base/task/task_runner_util.h"
- #include "base/threading/scoped_blocking_call.h"
- #include "base/threading/thread.h"
- #include "base/threading/thread_restrictions.h"
- #include "dbus/bus.h"
- #include "dbus/dbus_statistics.h"
- #include "dbus/message.h"
- #include "dbus/object_path.h"
- #include "dbus/scoped_dbus_error.h"
- #include "dbus/util.h"
- namespace dbus {
- namespace {
- constexpr char kErrorServiceUnknown[] =
- "org.freedesktop.DBus.Error.ServiceUnknown";
- constexpr char kErrorObjectUnknown[] =
- "org.freedesktop.DBus.Error.UnknownObject";
- // Used for success ratio histograms. 1 for success, 0 for failure.
- constexpr int kSuccessRatioHistogramMaxValue = 2;
- // The path of D-Bus Object sending NameOwnerChanged signal.
- constexpr char kDBusSystemObjectPath[] = "/org/freedesktop/DBus";
- // The D-Bus Object interface.
- constexpr char kDBusSystemObjectInterface[] = "org.freedesktop.DBus";
- // The D-Bus Object address.
- constexpr char kDBusSystemObjectAddress[] = "org.freedesktop.DBus";
- // The NameOwnerChanged member in |kDBusSystemObjectInterface|.
- constexpr char kNameOwnerChangedMember[] = "NameOwnerChanged";
- } // namespace
- ObjectProxy::ReplyCallbackHolder::ReplyCallbackHolder(
- scoped_refptr<base::SequencedTaskRunner> origin_task_runner,
- ResponseOrErrorCallback callback)
- : origin_task_runner_(std::move(origin_task_runner)),
- callback_(std::move(callback)) {
- DCHECK(origin_task_runner_.get());
- DCHECK(!callback_.is_null());
- }
- ObjectProxy::ReplyCallbackHolder::ReplyCallbackHolder(
- ReplyCallbackHolder&& other) = default;
- ObjectProxy::ReplyCallbackHolder::~ReplyCallbackHolder() {
- if (callback_.is_null()) {
- // This is the regular case.
- // CallMethod and its family creates this object on the origin thread,
- // PostTask()s to the D-Bus thread for actual D-Bus communication,
- // then PostTask()s back to the origin thread to invoke the |callback_|.
- // At that timing, the ownership of callback should be released via
- // ReleaseCallback().
- // Otherwise, this instance was moved to another one. Do nothing in
- // either case.
- return;
- }
- // The only case where |origin_task_runner_| becomes nullptr is that
- // this is moved. In such a case, |callback_| should be nullptr, too, so it
- // should be handled above. Thus, here |origin_task_runner_| must not be
- // nullptr.
- DCHECK(origin_task_runner_.get());
- if (origin_task_runner_->RunsTasksInCurrentSequence()) {
- // Destroyed on the origin thread. This happens when PostTask()ing to
- // the D-Bus thread fails. The |callback_| can be destroyed on the
- // current thread safely. Do nothing here, and let member destruction
- // destroy the callback.
- return;
- }
- // Here is on D-Bus thread, so try to PostTask() to destroy the callback.
- // to the origin thread.
- // The |origin_task_runner_| may already have stopped. E.g., on Chrome's
- // shutdown the message loop of the UI thread (= the origin thread) stops
- // before D-Bus threaed's. In such a case, PostTask() fails. Because we
- // cannot do much thing here, instead, simply leak the callback rather than
- // destroying it on the D-Bus thread, which could be unexpected from the
- // direct or indirect caller of CallMethod.
- auto* callback_to_be_deleted =
- new ResponseOrErrorCallback(std::move(callback_));
- ANNOTATE_LEAKING_OBJECT_PTR(callback_to_be_deleted);
- origin_task_runner_->PostTask(
- FROM_HERE, base::BindOnce(&base::DeletePointer<ResponseOrErrorCallback>,
- callback_to_be_deleted));
- }
- ObjectProxy::ResponseOrErrorCallback
- ObjectProxy::ReplyCallbackHolder::ReleaseCallback() {
- DCHECK(origin_task_runner_->RunsTasksInCurrentSequence());
- return std::move(callback_);
- }
- ObjectProxy::ObjectProxy(Bus* bus,
- const std::string& service_name,
- const ObjectPath& object_path,
- int options)
- : bus_(bus),
- service_name_(service_name),
- object_path_(object_path),
- ignore_service_unknown_errors_(options & IGNORE_SERVICE_UNKNOWN_ERRORS) {
- LOG_IF(FATAL, !object_path_.IsValid()) << object_path_.value();
- }
- ObjectProxy::~ObjectProxy() {
- DCHECK(pending_calls_.empty());
- }
- // Originally we tried to make |method_call| a const reference, but we
- // gave up as dbus_connection_send_with_reply_and_block() takes a
- // non-const pointer of DBusMessage as the second parameter.
- std::unique_ptr<Response> ObjectProxy::CallMethodAndBlockWithErrorDetails(
- MethodCall* method_call,
- int timeout_ms,
- ScopedDBusError* error) {
- bus_->AssertOnDBusThread();
- if (!bus_->Connect() || !method_call->SetDestination(service_name_) ||
- !method_call->SetPath(object_path_)) {
- return nullptr;
- }
- DBusMessage* request_message = method_call->raw_message();
- // Send the message synchronously.
- const base::TimeTicks start_time = base::TimeTicks::Now();
- DBusMessage* response_message =
- bus_->SendWithReplyAndBlock(request_message, timeout_ms, error->get());
- // Record if the method call is successful, or not. 1 if successful.
- UMA_HISTOGRAM_ENUMERATION("DBus.SyncMethodCallSuccess",
- response_message ? 1 : 0,
- kSuccessRatioHistogramMaxValue);
- statistics::AddBlockingSentMethodCall(
- service_name_, method_call->GetInterface(), method_call->GetMember());
- if (!response_message) {
- LogMethodCallFailure(method_call->GetInterface(), method_call->GetMember(),
- error->is_set() ? error->name() : "unknown error type",
- error->is_set() ? error->message() : "");
- return nullptr;
- }
- // Record time spent for the method call. Don't include failures.
- UMA_HISTOGRAM_TIMES("DBus.SyncMethodCallTime",
- base::TimeTicks::Now() - start_time);
- return Response::FromRawMessage(response_message);
- }
- std::unique_ptr<Response> ObjectProxy::CallMethodAndBlock(
- MethodCall* method_call,
- int timeout_ms) {
- ScopedDBusError error;
- return CallMethodAndBlockWithErrorDetails(method_call, timeout_ms, &error);
- }
- void ObjectProxy::CallMethod(MethodCall* method_call,
- int timeout_ms,
- ResponseCallback callback) {
- auto internal_callback = base::BindOnce(
- &ObjectProxy::OnCallMethod, this, method_call->GetInterface(),
- method_call->GetMember(), std::move(callback));
- CallMethodWithErrorResponse(method_call, timeout_ms,
- std::move(internal_callback));
- }
- void ObjectProxy::CallMethodWithErrorResponse(
- MethodCall* method_call,
- int timeout_ms,
- ResponseOrErrorCallback callback) {
- bus_->AssertOnOriginThread();
- const base::TimeTicks start_time = base::TimeTicks::Now();
- ReplyCallbackHolder callback_holder(bus_->GetOriginTaskRunner(),
- std::move(callback));
- if (!method_call->SetDestination(service_name_) ||
- !method_call->SetPath(object_path_)) {
- // In case of a failure, run the error callback with nullptr.
- base::OnceClosure task =
- base::BindOnce(&ObjectProxy::RunResponseOrErrorCallback, this,
- std::move(callback_holder), start_time,
- nullptr /* response */, nullptr /* error_response */);
- bus_->GetOriginTaskRunner()->PostTask(FROM_HERE, std::move(task));
- return;
- }
- // Increment the reference count so we can safely reference the
- // underlying request message until the method call is complete. This
- // will be unref'ed in StartAsyncMethodCall().
- DBusMessage* request_message = method_call->raw_message();
- dbus_message_ref(request_message);
- statistics::AddSentMethodCall(service_name_, method_call->GetInterface(),
- method_call->GetMember());
- // Wait for the response in the D-Bus thread.
- base::OnceClosure task =
- base::BindOnce(&ObjectProxy::StartAsyncMethodCall, this, timeout_ms,
- request_message, std::move(callback_holder), start_time);
- bus_->GetDBusTaskRunner()->PostTask(FROM_HERE, std::move(task));
- }
- void ObjectProxy::CallMethodWithErrorCallback(MethodCall* method_call,
- int timeout_ms,
- ResponseCallback callback,
- ErrorCallback error_callback) {
- auto internal_callback = base::BindOnce(
- [](ResponseCallback callback, ErrorCallback error_callback,
- Response* response, ErrorResponse* error_response) {
- if (response) {
- std::move(callback).Run(response);
- } else {
- std::move(error_callback).Run(error_response);
- }
- },
- std::move(callback), std::move(error_callback));
- CallMethodWithErrorResponse(method_call, timeout_ms,
- std::move(internal_callback));
- }
- void ObjectProxy::ConnectToSignal(const std::string& interface_name,
- const std::string& signal_name,
- SignalCallback signal_callback,
- OnConnectedCallback on_connected_callback) {
- bus_->AssertOnOriginThread();
- if (bus_->HasDBusThread()) {
- base::PostTaskAndReplyWithResult(
- bus_->GetDBusTaskRunner(), FROM_HERE,
- base::BindOnce(&ObjectProxy::ConnectToSignalAndBlock, this,
- interface_name, signal_name, signal_callback),
- base::BindOnce(std::move(on_connected_callback), interface_name,
- signal_name));
- } else {
- // If the bus doesn't have a dedicated dbus thread we need to call
- // ConnectToSignalAndBlock directly otherwise we might miss a signal
- // that is currently queued if we do a PostTask.
- const bool success =
- ConnectToSignalAndBlock(interface_name, signal_name, signal_callback);
- std::move(on_connected_callback).Run(interface_name, signal_name, success);
- }
- }
- bool ObjectProxy::ConnectToSignalAndBlock(const std::string& interface_name,
- const std::string& signal_name,
- SignalCallback signal_callback) {
- bus_->AssertOnDBusThread();
- if (!ConnectToNameOwnerChangedSignal())
- return false;
- const std::string absolute_signal_name =
- GetAbsoluteMemberName(interface_name, signal_name);
- // Add a match rule so the signal goes through HandleMessage().
- const std::string match_rule = base::StringPrintf(
- "type='signal', sender='%s', interface='%s', path='%s'",
- service_name_.c_str(), interface_name.c_str(),
- object_path_.value().c_str());
- return AddMatchRuleWithCallback(match_rule, absolute_signal_name,
- signal_callback);
- }
- void ObjectProxy::SetNameOwnerChangedCallback(
- NameOwnerChangedCallback callback) {
- bus_->AssertOnOriginThread();
- name_owner_changed_callback_ = callback;
- bus_->GetDBusTaskRunner()->PostTask(
- FROM_HERE,
- base::BindOnce(&ObjectProxy::TryConnectToNameOwnerChangedSignal, this));
- }
- void ObjectProxy::WaitForServiceToBeAvailable(
- WaitForServiceToBeAvailableCallback callback) {
- bus_->AssertOnOriginThread();
- wait_for_service_to_be_available_callbacks_.push_back(std::move(callback));
- bus_->GetDBusTaskRunner()->PostTask(
- FROM_HERE,
- base::BindOnce(&ObjectProxy::WaitForServiceToBeAvailableInternal, this));
- }
- void ObjectProxy::Detach() {
- bus_->AssertOnDBusThread();
- if (bus_->IsConnected())
- bus_->RemoveFilterFunction(&ObjectProxy::HandleMessageThunk, this);
- for (const auto& match_rule : match_rules_) {
- ScopedDBusError error;
- bus_->RemoveMatch(match_rule, 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_rule;
- }
- }
- match_rules_.clear();
- for (auto* pending_call : pending_calls_) {
- base::ScopedBlockingCall scoped_blocking_call(
- FROM_HERE, base::BlockingType::MAY_BLOCK);
- dbus_pending_call_cancel(pending_call);
- dbus_pending_call_unref(pending_call);
- }
- pending_calls_.clear();
- }
- void ObjectProxy::StartAsyncMethodCall(int timeout_ms,
- DBusMessage* request_message,
- ReplyCallbackHolder callback_holder,
- base::TimeTicks start_time) {
- bus_->AssertOnDBusThread();
- base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
- base::BlockingType::MAY_BLOCK);
- if (!bus_->Connect() || !bus_->SetUpAsyncOperations()) {
- // In case of a failure, run the error callback with nullptr.
- base::OnceClosure task =
- base::BindOnce(&ObjectProxy::RunResponseOrErrorCallback, this,
- std::move(callback_holder), start_time,
- nullptr /* response */, nullptr /* error_response */);
- bus_->GetOriginTaskRunner()->PostTask(FROM_HERE, std::move(task));
- dbus_message_unref(request_message);
- return;
- }
- DBusPendingCall* dbus_pending_call = nullptr;
- bus_->SendWithReply(request_message, &dbus_pending_call, timeout_ms);
- using PendingCallback =
- base::OnceCallback<void(DBusPendingCall * pending_call)>;
- // This returns false only when unable to allocate memory.
- const bool success = dbus_pending_call_set_notify(
- dbus_pending_call,
- [](DBusPendingCall* pending_call, void* user_data) {
- std::move(*static_cast<PendingCallback*>(user_data)).Run(pending_call);
- },
- // PendingCallback instance is owned by libdbus.
- new PendingCallback(base::BindOnce(&ObjectProxy::OnPendingCallIsComplete,
- this, std::move(callback_holder),
- start_time)),
- [](void* user_data) { delete static_cast<PendingCallback*>(user_data); });
- CHECK(success) << "Unable to allocate memory";
- pending_calls_.insert(dbus_pending_call);
- // It's now safe to unref the request message.
- dbus_message_unref(request_message);
- }
- void ObjectProxy::OnPendingCallIsComplete(ReplyCallbackHolder callback_holder,
- base::TimeTicks start_time,
- DBusPendingCall* pending_call) {
- bus_->AssertOnDBusThread();
- base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
- base::BlockingType::MAY_BLOCK);
- DBusMessage* response_message = dbus_pending_call_steal_reply(pending_call);
- // Either |response| or |error_response| takes ownership of the
- // |response_message|.
- std::unique_ptr<Response> response;
- std::unique_ptr<ErrorResponse> error_response;
- if (dbus_message_get_type(response_message) == DBUS_MESSAGE_TYPE_ERROR) {
- error_response = ErrorResponse::FromRawMessage(response_message);
- } else {
- response = Response::FromRawMessage(response_message);
- }
- base::OnceClosure task =
- base::BindOnce(&ObjectProxy::RunResponseOrErrorCallback, this,
- std::move(callback_holder), start_time, response.get(),
- error_response.get());
- // The message should be deleted on the D-Bus thread for a complicated
- // reason:
- //
- // libdbus keeps track of the number of bytes in the incoming message
- // queue to ensure that the data size in the queue is manageable. The
- // bookkeeping is partly done via dbus_message_unref(), and immediately
- // asks the client code (Chrome) to stop monitoring the underlying
- // socket, if the number of bytes exceeds a certian number, which is set
- // to 63MB, per dbus-transport.cc:
- //
- // /* Try to default to something that won't totally hose the system,
- // * but doesn't impose too much of a limitation.
- // */
- // transport->max_live_messages_size = _DBUS_ONE_MEGABYTE * 63;
- //
- // The monitoring of the socket is done on the D-Bus thread (see Watch
- // class in bus.cc), hence we should stop the monitoring on D-Bus thread.
- bus_->GetOriginTaskRunner()->PostTaskAndReply(
- FROM_HERE, std::move(task),
- base::BindOnce(
- [](Response* response, ErrorResponse* error_response) {
- // Do nothing.
- },
- base::Owned(response.release()),
- base::Owned(error_response.release())));
- // Remove the pending call from the set.
- pending_calls_.erase(pending_call);
- dbus_pending_call_unref(pending_call);
- }
- void ObjectProxy::RunResponseOrErrorCallback(
- ReplyCallbackHolder callback_holder,
- base::TimeTicks start_time,
- Response* response,
- ErrorResponse* error_response) {
- bus_->AssertOnOriginThread();
- callback_holder.ReleaseCallback().Run(response, error_response);
- if (response) {
- // Record time spent for the method call. Don't include failures.
- UMA_HISTOGRAM_TIMES("DBus.AsyncMethodCallTime",
- base::TimeTicks::Now() - start_time);
- }
- // Record if the method call is successful, or not. 1 if successful.
- UMA_HISTOGRAM_ENUMERATION("DBus.AsyncMethodCallSuccess", response ? 1 : 0,
- kSuccessRatioHistogramMaxValue);
- }
- bool ObjectProxy::ConnectToNameOwnerChangedSignal() {
- bus_->AssertOnDBusThread();
- if (!bus_->Connect() || !bus_->SetUpAsyncOperations())
- return false;
- bus_->AddFilterFunction(&ObjectProxy::HandleMessageThunk, this);
- // Add a match_rule listening NameOwnerChanged for the well-known name
- // |service_name_|.
- const std::string name_owner_changed_match_rule = base::StringPrintf(
- "type='signal',interface='org.freedesktop.DBus',"
- "member='NameOwnerChanged',path='/org/freedesktop/DBus',"
- "sender='org.freedesktop.DBus',arg0='%s'",
- service_name_.c_str());
- const bool success = AddMatchRuleWithoutCallback(
- name_owner_changed_match_rule, "org.freedesktop.DBus.NameOwnerChanged");
- // Try getting the current name owner. It's not guaranteed that we can get
- // the name owner at this moment, as the service may not yet be started. If
- // that's the case, we'll get the name owner via NameOwnerChanged signal,
- // as soon as the service is started.
- UpdateNameOwnerAndBlock();
- return success;
- }
- void ObjectProxy::TryConnectToNameOwnerChangedSignal() {
- bus_->AssertOnDBusThread();
- bool success = ConnectToNameOwnerChangedSignal();
- LOG_IF(WARNING, !success)
- << "Failed to connect to NameOwnerChanged signal for object: "
- << object_path_.value();
- }
- void ObjectProxy::WaitForServiceToBeAvailableInternal() {
- bus_->AssertOnDBusThread();
- if (!ConnectToNameOwnerChangedSignal()) { // Failed to connect to the signal.
- const bool service_is_ready = false;
- bus_->GetOriginTaskRunner()->PostTask(
- FROM_HERE,
- base::BindOnce(&ObjectProxy::RunWaitForServiceToBeAvailableCallbacks,
- this, service_is_ready));
- return;
- }
- const bool service_is_available = !service_name_owner_.empty();
- if (service_is_available) { // Service is already available.
- bus_->GetOriginTaskRunner()->PostTask(
- FROM_HERE,
- base::BindOnce(&ObjectProxy::RunWaitForServiceToBeAvailableCallbacks,
- this, service_is_available));
- return;
- }
- }
- 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);
- std::unique_ptr<Signal> signal(Signal::FromRawMessage(raw_message));
- // Verify the signal comes from the object we're proxying for, this is
- // our last chance to return DBUS_HANDLER_RESULT_NOT_YET_HANDLED and
- // allow other object proxies to handle instead.
- const ObjectPath path = signal->GetPath();
- if (path != object_path_) {
- if (path.value() == kDBusSystemObjectPath &&
- signal->GetMember() == kNameOwnerChangedMember) {
- // Handle NameOwnerChanged separately
- return HandleNameOwnerChanged(std::move(signal));
- }
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
- }
- std::string sender = signal->GetSender();
- // Ignore message from sender we are not interested in.
- if (service_name_owner_ != sender)
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
- const std::string interface = signal->GetInterface();
- const std::string member = signal->GetMember();
- statistics::AddReceivedSignal(service_name_, interface, member);
- // Check if we know about the signal.
- const std::string absolute_signal_name =
- GetAbsoluteMemberName(interface, member);
- MethodTable::const_iterator iter = method_table_.find(absolute_signal_name);
- if (iter == method_table_.end()) {
- // Don't know about the signal.
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
- }
- VLOG(1) << "Signal received: " << signal->ToString();
- const base::TimeTicks start_time = base::TimeTicks::Now();
- 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_->GetOriginTaskRunner()->PostTask(
- FROM_HERE, base::BindOnce(&ObjectProxy::RunMethod, this, start_time,
- iter->second, released_signal));
- } else {
- // If the D-Bus thread is not used, just call the callback on the
- // current thread. Transfer the ownership of |signal| to RunMethod().
- Signal* released_signal = signal.release();
- RunMethod(start_time, iter->second, released_signal);
- }
- // We don't return DBUS_HANDLER_RESULT_HANDLED for signals because other
- // objects may be interested in them. (e.g. Signals from org.freedesktop.DBus)
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
- }
- void ObjectProxy::RunMethod(base::TimeTicks start_time,
- std::vector<SignalCallback> signal_callbacks,
- Signal* signal) {
- bus_->AssertOnOriginThread();
- for (std::vector<SignalCallback>::iterator iter = signal_callbacks.begin();
- iter != signal_callbacks.end(); ++iter)
- iter->Run(signal);
- // Delete the message on the D-Bus thread. See comments in
- // RunResponseOrErrorCallback().
- bus_->GetDBusTaskRunner()->PostTask(
- FROM_HERE, base::BindOnce(&base::DeletePointer<Signal>, signal));
- // Record time spent for handling the signal.
- UMA_HISTOGRAM_TIMES("DBus.SignalHandleTime",
- base::TimeTicks::Now() - start_time);
- }
- DBusHandlerResult ObjectProxy::HandleMessageThunk(DBusConnection* connection,
- DBusMessage* raw_message,
- void* user_data) {
- ObjectProxy* self = reinterpret_cast<ObjectProxy*>(user_data);
- return self->HandleMessage(connection, raw_message);
- }
- void ObjectProxy::LogMethodCallFailure(
- const base::StringPiece& interface_name,
- const base::StringPiece& method_name,
- const base::StringPiece& error_name,
- const base::StringPiece& error_message) const {
- if (ignore_service_unknown_errors_ &&
- (error_name == kErrorServiceUnknown || error_name == kErrorObjectUnknown))
- return;
- std::ostringstream msg;
- msg << "Failed to call method: " << interface_name << "." << method_name
- << ": object_path= " << object_path_.value() << ": " << error_name << ": "
- << error_message;
- // "UnknownObject" indicates that an object or service is no longer available,
- // e.g. a Shill network service has gone out of range. Treat these as warnings
- // not errors.
- if (error_name == kErrorObjectUnknown)
- LOG(WARNING) << msg.str();
- else
- LOG(ERROR) << msg.str();
- }
- void ObjectProxy::OnCallMethod(const std::string& interface_name,
- const std::string& method_name,
- ResponseCallback response_callback,
- Response* response,
- ErrorResponse* error_response) {
- if (response) {
- // Method call was successful.
- std::move(response_callback).Run(response);
- return;
- }
- // Method call failed.
- std::string error_name;
- std::string error_message;
- if (error_response) {
- // Error message may contain the error message as string.
- error_name = error_response->GetErrorName();
- MessageReader reader(error_response);
- reader.PopString(&error_message);
- } else {
- error_name = "unknown error type";
- }
- LogMethodCallFailure(interface_name, method_name, error_name, error_message);
- std::move(response_callback).Run(nullptr);
- }
- bool ObjectProxy::AddMatchRuleWithCallback(
- const std::string& match_rule,
- const std::string& absolute_signal_name,
- SignalCallback signal_callback) {
- DCHECK(!match_rule.empty());
- DCHECK(!absolute_signal_name.empty());
- bus_->AssertOnDBusThread();
- if (match_rules_.find(match_rule) == match_rules_.end()) {
- ScopedDBusError error;
- bus_->AddMatch(match_rule, error.get());
- if (error.is_set()) {
- LOG(ERROR) << "Failed to add match rule \"" << match_rule << "\". Got "
- << error.name() << ": " << error.message();
- return false;
- } else {
- // Store the match rule, so that we can remove this in Detach().
- match_rules_.insert(match_rule);
- // Add the signal callback to the method table.
- method_table_[absolute_signal_name].push_back(signal_callback);
- return true;
- }
- } else {
- // We already have the match rule.
- method_table_[absolute_signal_name].push_back(signal_callback);
- return true;
- }
- }
- bool ObjectProxy::AddMatchRuleWithoutCallback(
- const std::string& match_rule,
- const std::string& absolute_signal_name) {
- DCHECK(!match_rule.empty());
- DCHECK(!absolute_signal_name.empty());
- bus_->AssertOnDBusThread();
- if (match_rules_.find(match_rule) != match_rules_.end())
- return true;
- ScopedDBusError error;
- bus_->AddMatch(match_rule, error.get());
- if (error.is_set()) {
- LOG(ERROR) << "Failed to add match rule \"" << match_rule << "\". Got "
- << error.name() << ": " << error.message();
- return false;
- }
- // Store the match rule, so that we can remove this in Detach().
- match_rules_.insert(match_rule);
- return true;
- }
- void ObjectProxy::UpdateNameOwnerAndBlock() {
- bus_->AssertOnDBusThread();
- // Errors should be suppressed here, as the service may not be yet running
- // when connecting to signals of the service, which is just fine.
- // The ObjectProxy will be notified when the service is launched via
- // NameOwnerChanged signal. See also comments in ConnectToSignalAndBlock().
- service_name_owner_ =
- bus_->GetServiceOwnerAndBlock(service_name_, Bus::SUPPRESS_ERRORS);
- }
- DBusHandlerResult ObjectProxy::HandleNameOwnerChanged(
- std::unique_ptr<Signal> signal) {
- DCHECK(signal);
- bus_->AssertOnDBusThread();
- // Confirm the validity of the NameOwnerChanged signal.
- if (signal->GetMember() == kNameOwnerChangedMember &&
- signal->GetInterface() == kDBusSystemObjectInterface &&
- signal->GetSender() == kDBusSystemObjectAddress) {
- MessageReader reader(signal.get());
- std::string name, old_owner, new_owner;
- if (reader.PopString(&name) && reader.PopString(&old_owner) &&
- reader.PopString(&new_owner) && name == service_name_) {
- service_name_owner_ = new_owner;
- bus_->GetOriginTaskRunner()->PostTask(
- FROM_HERE, base::BindOnce(&ObjectProxy::RunNameOwnerChangedCallback,
- this, old_owner, new_owner));
- const bool service_is_available = !service_name_owner_.empty();
- if (service_is_available) {
- bus_->GetOriginTaskRunner()->PostTask(
- FROM_HERE,
- base::BindOnce(
- &ObjectProxy::RunWaitForServiceToBeAvailableCallbacks, this,
- service_is_available));
- }
- }
- }
- // Always return unhandled to let other object proxies handle the same
- // signal.
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
- }
- void ObjectProxy::RunNameOwnerChangedCallback(const std::string& old_owner,
- const std::string& new_owner) {
- bus_->AssertOnOriginThread();
- if (!name_owner_changed_callback_.is_null())
- name_owner_changed_callback_.Run(old_owner, new_owner);
- }
- void ObjectProxy::RunWaitForServiceToBeAvailableCallbacks(
- bool service_is_available) {
- bus_->AssertOnOriginThread();
- std::vector<WaitForServiceToBeAvailableCallback> callbacks;
- callbacks.swap(wait_for_service_to_be_available_callbacks_);
- for (size_t i = 0; i < callbacks.size(); ++i)
- std::move(callbacks[i]).Run(service_is_available);
- }
- } // namespace dbus
|