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