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