Home | History | Annotate | Download | only in dbus
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "dbus/exported_object.h"
      6 
      7 #include <stdint.h>
      8 #include <utility>
      9 
     10 #include "base/bind.h"
     11 #include "base/logging.h"
     12 #include "base/memory/ref_counted.h"
     13 #include "base/metrics/histogram_macros.h"
     14 #include "base/task_runner.h"
     15 #include "base/threading/thread_restrictions.h"
     16 #include "base/time/time.h"
     17 #include "dbus/bus.h"
     18 #include "dbus/message.h"
     19 #include "dbus/object_path.h"
     20 #include "dbus/scoped_dbus_error.h"
     21 #include "dbus/util.h"
     22 
     23 namespace dbus {
     24 
     25 namespace {
     26 
     27 // Used for success ratio histograms. 1 for success, 0 for failure.
     28 const int kSuccessRatioHistogramMaxValue = 2;
     29 
     30 }  // namespace
     31 
     32 ExportedObject::ExportedObject(Bus* bus,
     33                                const ObjectPath& object_path)
     34     : bus_(bus),
     35       object_path_(object_path),
     36       object_is_registered_(false) {
     37   LOG_IF(FATAL, !object_path_.IsValid()) << object_path_.value();
     38 }
     39 
     40 ExportedObject::~ExportedObject() {
     41   DCHECK(!object_is_registered_);
     42 }
     43 
     44 bool ExportedObject::ExportMethodAndBlock(
     45     const std::string& interface_name,
     46     const std::string& method_name,
     47     MethodCallCallback method_call_callback) {
     48   bus_->AssertOnDBusThread();
     49 
     50   // Check if the method is already exported.
     51   const std::string absolute_method_name =
     52       GetAbsoluteMemberName(interface_name, method_name);
     53   if (method_table_.find(absolute_method_name) != method_table_.end()) {
     54     LOG(ERROR) << absolute_method_name << " is already exported";
     55     return false;
     56   }
     57 
     58   if (!bus_->Connect())
     59     return false;
     60   if (!bus_->SetUpAsyncOperations())
     61     return false;
     62   if (!Register())
     63     return false;
     64 
     65   // Add the method callback to the method table.
     66   method_table_[absolute_method_name] = method_call_callback;
     67 
     68   return true;
     69 }
     70 
     71 void ExportedObject::ExportMethod(const std::string& interface_name,
     72                                   const std::string& method_name,
     73                                   MethodCallCallback method_call_callback,
     74                                   OnExportedCallback on_exported_calback) {
     75   bus_->AssertOnOriginThread();
     76 
     77   base::Closure task = base::Bind(&ExportedObject::ExportMethodInternal,
     78                                   this,
     79                                   interface_name,
     80                                   method_name,
     81                                   method_call_callback,
     82                                   on_exported_calback);
     83   bus_->GetDBusTaskRunner()->PostTask(FROM_HERE, task);
     84 }
     85 
     86 void ExportedObject::SendSignal(Signal* signal) {
     87   // For signals, the object path should be set to the path to the sender
     88   // object, which is this exported object here.
     89   CHECK(signal->SetPath(object_path_));
     90 
     91   // Increment the reference count so we can safely reference the
     92   // underlying signal message until the signal sending is complete. This
     93   // will be unref'ed in SendSignalInternal().
     94   DBusMessage* signal_message = signal->raw_message();
     95   dbus_message_ref(signal_message);
     96 
     97   const base::TimeTicks start_time = base::TimeTicks::Now();
     98   if (bus_->GetDBusTaskRunner()->RunsTasksInCurrentSequence()) {
     99     // The Chrome OS power manager doesn't use a dedicated TaskRunner for
    100     // sending DBus messages.  Sending signals asynchronously can cause an
    101     // inversion in the message order if the power manager calls
    102     // ObjectProxy::CallMethodAndBlock() before going back to the top level of
    103     // the MessageLoop: crbug.com/472361.
    104     SendSignalInternal(start_time, signal_message);
    105   } else {
    106     bus_->GetDBusTaskRunner()->PostTask(
    107         FROM_HERE,
    108         base::Bind(&ExportedObject::SendSignalInternal,
    109                    this,
    110                    start_time,
    111                    signal_message));
    112   }
    113 }
    114 
    115 void ExportedObject::Unregister() {
    116   bus_->AssertOnDBusThread();
    117 
    118   if (!object_is_registered_)
    119     return;
    120 
    121   bus_->UnregisterObjectPath(object_path_);
    122   object_is_registered_ = false;
    123 }
    124 
    125 void ExportedObject::ExportMethodInternal(
    126     const std::string& interface_name,
    127     const std::string& method_name,
    128     MethodCallCallback method_call_callback,
    129     OnExportedCallback on_exported_calback) {
    130   bus_->AssertOnDBusThread();
    131 
    132   const bool success = ExportMethodAndBlock(interface_name,
    133                                             method_name,
    134                                             method_call_callback);
    135   bus_->GetOriginTaskRunner()->PostTask(FROM_HERE,
    136                                         base::Bind(&ExportedObject::OnExported,
    137                                                    this,
    138                                                    on_exported_calback,
    139                                                    interface_name,
    140                                                    method_name,
    141                                                    success));
    142 }
    143 
    144 void ExportedObject::OnExported(OnExportedCallback on_exported_callback,
    145                                 const std::string& interface_name,
    146                                 const std::string& method_name,
    147                                 bool success) {
    148   bus_->AssertOnOriginThread();
    149 
    150   on_exported_callback.Run(interface_name, method_name, success);
    151 }
    152 
    153 void ExportedObject::SendSignalInternal(base::TimeTicks start_time,
    154                                         DBusMessage* signal_message) {
    155   uint32_t serial = 0;
    156   bus_->Send(signal_message, &serial);
    157   dbus_message_unref(signal_message);
    158   // Record time spent to send the the signal. This is not accurate as the
    159   // signal will actually be sent from the next run of the message loop,
    160   // but we can at least tell the number of signals sent.
    161   UMA_HISTOGRAM_TIMES("DBus.SignalSendTime",
    162                       base::TimeTicks::Now() - start_time);
    163 }
    164 
    165 bool ExportedObject::Register() {
    166   bus_->AssertOnDBusThread();
    167 
    168   if (object_is_registered_)
    169     return true;
    170 
    171   ScopedDBusError error;
    172 
    173   DBusObjectPathVTable vtable = {};
    174   vtable.message_function = &ExportedObject::HandleMessageThunk;
    175   vtable.unregister_function = &ExportedObject::OnUnregisteredThunk;
    176   const bool success = bus_->TryRegisterObjectPath(object_path_,
    177                                                    &vtable,
    178                                                    this,
    179                                                    error.get());
    180   if (!success) {
    181     LOG(ERROR) << "Failed to register the object: " << object_path_.value()
    182                << ": " << (error.is_set() ? error.message() : "");
    183     return false;
    184   }
    185 
    186   object_is_registered_ = true;
    187   return true;
    188 }
    189 
    190 DBusHandlerResult ExportedObject::HandleMessage(
    191     DBusConnection* connection,
    192     DBusMessage* raw_message) {
    193   bus_->AssertOnDBusThread();
    194   // ExportedObject only handles method calls. Ignore other message types (e.g.
    195   // signal).
    196   if (dbus_message_get_type(raw_message) != DBUS_MESSAGE_TYPE_METHOD_CALL)
    197     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    198 
    199   // raw_message will be unrefed on exit of the function. Increment the
    200   // reference so we can use it in MethodCall.
    201   dbus_message_ref(raw_message);
    202   std::unique_ptr<MethodCall> method_call(
    203       MethodCall::FromRawMessage(raw_message));
    204   const std::string interface = method_call->GetInterface();
    205   const std::string member = method_call->GetMember();
    206 
    207   if (interface.empty()) {
    208     // We don't support method calls without interface.
    209     LOG(WARNING) << "Interface is missing: " << method_call->ToString();
    210     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    211   }
    212 
    213   // Check if we know about the method.
    214   const std::string absolute_method_name = GetAbsoluteMemberName(
    215       interface, member);
    216   MethodTable::const_iterator iter = method_table_.find(absolute_method_name);
    217   if (iter == method_table_.end()) {
    218     // Don't know about the method.
    219     LOG(WARNING) << "Unknown method: " << method_call->ToString();
    220     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
    221   }
    222 
    223   const base::TimeTicks start_time = base::TimeTicks::Now();
    224   if (bus_->HasDBusThread()) {
    225     // Post a task to run the method in the origin thread.
    226     bus_->GetOriginTaskRunner()->PostTask(
    227         FROM_HERE,
    228         base::BindOnce(&ExportedObject::RunMethod, this, iter->second,
    229                        std::move(method_call), start_time));
    230   } else {
    231     // If the D-Bus thread is not used, just call the method directly.
    232     MethodCall* method = method_call.get();
    233     iter->second.Run(method,
    234                      base::Bind(&ExportedObject::SendResponse,
    235                                 this,
    236                                 start_time,
    237                                 base::Passed(&method_call)));
    238   }
    239 
    240   // It's valid to say HANDLED here, and send a method response at a later
    241   // time from OnMethodCompleted() asynchronously.
    242   return DBUS_HANDLER_RESULT_HANDLED;
    243 }
    244 
    245 void ExportedObject::RunMethod(MethodCallCallback method_call_callback,
    246                                std::unique_ptr<MethodCall> method_call,
    247                                base::TimeTicks start_time) {
    248   bus_->AssertOnOriginThread();
    249   MethodCall* method = method_call.get();
    250   method_call_callback.Run(method,
    251                            base::Bind(&ExportedObject::SendResponse,
    252                                       this,
    253                                       start_time,
    254                                       base::Passed(&method_call)));
    255 }
    256 
    257 void ExportedObject::SendResponse(base::TimeTicks start_time,
    258                                   std::unique_ptr<MethodCall> method_call,
    259                                   std::unique_ptr<Response> response) {
    260   DCHECK(method_call);
    261   if (bus_->HasDBusThread()) {
    262     bus_->GetDBusTaskRunner()->PostTask(
    263         FROM_HERE, base::BindOnce(&ExportedObject::OnMethodCompleted, this,
    264                                   std::move(method_call), std::move(response),
    265                                   start_time));
    266   } else {
    267     OnMethodCompleted(std::move(method_call), std::move(response), start_time);
    268   }
    269 }
    270 
    271 void ExportedObject::OnMethodCompleted(std::unique_ptr<MethodCall> method_call,
    272                                        std::unique_ptr<Response> response,
    273                                        base::TimeTicks start_time) {
    274   bus_->AssertOnDBusThread();
    275 
    276   // Record if the method call is successful, or not. 1 if successful.
    277   UMA_HISTOGRAM_ENUMERATION("DBus.ExportedMethodHandleSuccess",
    278                             response ? 1 : 0,
    279                             kSuccessRatioHistogramMaxValue);
    280 
    281   // Check if the bus is still connected. If the method takes long to
    282   // complete, the bus may be shut down meanwhile.
    283   if (!bus_->is_connected())
    284     return;
    285 
    286   if (!response) {
    287     // Something bad happened in the method call.
    288     std::unique_ptr<ErrorResponse> error_response(ErrorResponse::FromMethodCall(
    289         method_call.get(), DBUS_ERROR_FAILED,
    290         "error occurred in " + method_call->GetMember()));
    291     bus_->Send(error_response->raw_message(), nullptr);
    292     return;
    293   }
    294 
    295   // The method call was successful.
    296   bus_->Send(response->raw_message(), nullptr);
    297 
    298   // Record time spent to handle the the method call. Don't include failures.
    299   UMA_HISTOGRAM_TIMES("DBus.ExportedMethodHandleTime",
    300                       base::TimeTicks::Now() - start_time);
    301 }
    302 
    303 void ExportedObject::OnUnregistered(DBusConnection* connection) {
    304 }
    305 
    306 DBusHandlerResult ExportedObject::HandleMessageThunk(
    307     DBusConnection* connection,
    308     DBusMessage* raw_message,
    309     void* user_data) {
    310   ExportedObject* self = reinterpret_cast<ExportedObject*>(user_data);
    311   return self->HandleMessage(connection, raw_message);
    312 }
    313 
    314 void ExportedObject::OnUnregisteredThunk(DBusConnection *connection,
    315                                          void* user_data) {
    316   ExportedObject* self = reinterpret_cast<ExportedObject*>(user_data);
    317   return self->OnUnregistered(connection);
    318 }
    319 
    320 }  // namespace dbus
    321