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