| // 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/bind_helpers.h" | 
 | #include "base/debug/leak_annotations.h" | 
 | #include "base/logging.h" | 
 | #include "base/message_loop/message_loop.h" | 
 | #include "base/metrics/histogram_macros.h" | 
 | #include "base/strings/string_piece.h" | 
 | #include "base/strings/stringprintf.h" | 
 | #include "base/task_runner.h" | 
 | #include "base/task_runner_util.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::TaskRunner> origin_task_runner, | 
 |     ResponseOrErrorCallback callback) | 
 |     : origin_task_runner_(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 std::unique_ptr<Response>(); | 
 |  | 
 |   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 std::unique_ptr<Response>(); | 
 |   } | 
 |   // 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::ConnectToSignalInternal, 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 | 
 |     // ConnectToSignalInternal directly otherwise we might miss a signal | 
 |     // that is currently queued if we do a PostTask. | 
 |     const bool success = | 
 |         ConnectToSignalInternal(interface_name, signal_name, signal_callback); | 
 |     std::move(on_connected_callback).Run(interface_name, signal_name, success); | 
 |   } | 
 | } | 
 |  | 
 | void ObjectProxy::SetNameOwnerChangedCallback( | 
 |     NameOwnerChangedCallback callback) { | 
 |   bus_->AssertOnOriginThread(); | 
 |  | 
 |   name_owner_changed_callback_ = callback; | 
 | } | 
 |  | 
 | 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_->is_connected()) | 
 |     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_) { | 
 |     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(); | 
 |  | 
 |   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(); | 
 |  | 
 |   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; | 
 | } | 
 |  | 
 | bool ObjectProxy::ConnectToSignalInternal(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', interface='%s', path='%s'", | 
 |                          interface_name.c_str(), | 
 |                          object_path_.value().c_str()); | 
 |   return AddMatchRuleWithCallback(match_rule, | 
 |                                   absolute_signal_name, | 
 |                                   signal_callback); | 
 | } | 
 |  | 
 | 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; | 
 |   } | 
 |  | 
 |   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(); | 
 |  | 
 |   std::string sender = signal->GetSender(); | 
 |   if (service_name_owner_ != sender) { | 
 |     LOG(ERROR) << "Rejecting a message from a wrong sender."; | 
 |     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. | 
 |     // 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::Bind(&ObjectProxy::RunMethod, | 
 |                                                      this, | 
 |                                                      start_time, | 
 |                                                      iter->second, | 
 |                                                      released_signal)); | 
 |   } else { | 
 |     const base::TimeTicks start_time = base::TimeTicks::Now(); | 
 |     // 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::Bind(&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 ConnectToSignalInternal(). | 
 |   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::Bind(&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::Bind(&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 |