blob: 730c98b2ee7634fd8cbf9f4595d03a5872e7892a [file] [log] [blame]
[email protected]216ed0b2012-02-14 21:29:061// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]a51076112011-08-17 20:58:122// 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.h"
[email protected]a73389892011-09-06 20:53:3011#include "base/metrics/histogram.h"
[email protected]a51076112011-08-17 20:58:1212#include "base/threading/thread_restrictions.h"
13#include "base/time.h"
14#include "dbus/bus.h"
15#include "dbus/message.h"
[email protected]216ed0b2012-02-14 21:29:0616#include "dbus/object_path.h"
[email protected]a51076112011-08-17 20:58:1217#include "dbus/scoped_dbus_error.h"
18
19namespace dbus {
20
21namespace {
22
[email protected]a73389892011-09-06 20:53:3023// Used for success ratio histograms. 1 for success, 0 for failure.
24const int kSuccessRatioHistogramMaxValue = 2;
25
[email protected]a51076112011-08-17 20:58:1226// 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.
29std::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
37ExportedObject::ExportedObject(Bus* bus,
38 const std::string& service_name,
[email protected]216ed0b2012-02-14 21:29:0639 const ObjectPath& object_path)
[email protected]a51076112011-08-17 20:58:1240 : bus_(bus),
41 service_name_(service_name),
42 object_path_(object_path),
[email protected]e184cce2011-10-07 16:26:3043 object_is_registered_(false) {
[email protected]a51076112011-08-17 20:58:1244}
45
46ExportedObject::~ExportedObject() {
47 DCHECK(!object_is_registered_);
48}
49
50bool ExportedObject::ExportMethodAndBlock(
51 const std::string& interface_name,
52 const std::string& method_name,
53 MethodCallCallback method_call_callback) {
54 bus_->AssertOnDBusThread();
55
[email protected]3beaaa4e2011-08-23 07:29:2156 // Check if the method is already exported.
57 const std::string absolute_method_name =
58 GetAbsoluteMethodName(interface_name, method_name);
59 if (method_table_.find(absolute_method_name) != method_table_.end()) {
60 LOG(ERROR) << absolute_method_name << " is already exported";
61 return false;
62 }
63
[email protected]a51076112011-08-17 20:58:1264 if (!bus_->Connect())
65 return false;
66 if (!bus_->SetUpAsyncOperations())
67 return false;
68 if (!bus_->RequestOwnership(service_name_))
69 return false;
70 if (!Register())
71 return false;
72
[email protected]3beaaa4e2011-08-23 07:29:2173 // Add the method callback to the method table.
[email protected]a51076112011-08-17 20:58:1274 method_table_[absolute_method_name] = method_call_callback;
75
76 return true;
77}
78
79void ExportedObject::ExportMethod(const std::string& interface_name,
80 const std::string& method_name,
81 MethodCallCallback method_call_callback,
82 OnExportedCallback on_exported_calback) {
83 bus_->AssertOnOriginThread();
84
85 base::Closure task = base::Bind(&ExportedObject::ExportMethodInternal,
86 this,
87 interface_name,
88 method_name,
89 method_call_callback,
90 on_exported_calback);
91 bus_->PostTaskToDBusThread(FROM_HERE, task);
92}
93
[email protected]3beaaa4e2011-08-23 07:29:2194void ExportedObject::SendSignal(Signal* signal) {
95 // For signals, the object path should be set to the path to the sender
96 // object, which is this exported object here.
97 signal->SetPath(object_path_);
98
99 // Increment the reference count so we can safely reference the
100 // underlying signal message until the signal sending is complete. This
101 // will be unref'ed in SendSignalInternal().
102 DBusMessage* signal_message = signal->raw_message();
103 dbus_message_ref(signal_message);
104
[email protected]a73389892011-09-06 20:53:30105 const base::TimeTicks start_time = base::TimeTicks::Now();
[email protected]3beaaa4e2011-08-23 07:29:21106 bus_->PostTaskToDBusThread(FROM_HERE,
107 base::Bind(&ExportedObject::SendSignalInternal,
108 this,
[email protected]a73389892011-09-06 20:53:30109 start_time,
[email protected]1d887b272011-10-04 13:47:21110 signal_message));
[email protected]3beaaa4e2011-08-23 07:29:21111}
112
[email protected]a51076112011-08-17 20:58:12113void ExportedObject::Unregister() {
114 bus_->AssertOnDBusThread();
115
116 if (!object_is_registered_)
117 return;
118
119 bus_->UnregisterObjectPath(object_path_);
120 object_is_registered_ = false;
121}
122
123void ExportedObject::ExportMethodInternal(
124 const std::string& interface_name,
125 const std::string& method_name,
126 MethodCallCallback method_call_callback,
127 OnExportedCallback on_exported_calback) {
128 bus_->AssertOnDBusThread();
129
130 const bool success = ExportMethodAndBlock(interface_name,
131 method_name,
132 method_call_callback);
133 bus_->PostTaskToOriginThread(FROM_HERE,
134 base::Bind(&ExportedObject::OnExported,
135 this,
136 on_exported_calback,
137 interface_name,
138 method_name,
139 success));
140}
141
142void ExportedObject::OnExported(OnExportedCallback on_exported_callback,
143 const std::string& interface_name,
144 const std::string& method_name,
145 bool success) {
146 bus_->AssertOnOriginThread();
147
148 on_exported_callback.Run(interface_name, method_name, success);
149}
150
[email protected]a73389892011-09-06 20:53:30151void ExportedObject::SendSignalInternal(base::TimeTicks start_time,
[email protected]1d887b272011-10-04 13:47:21152 DBusMessage* signal_message) {
[email protected]3beaaa4e2011-08-23 07:29:21153 uint32 serial = 0;
154 bus_->Send(signal_message, &serial);
155 dbus_message_unref(signal_message);
[email protected]a73389892011-09-06 20:53:30156 // Record time spent to send the the signal. This is not accurate as the
157 // signal will actually be sent from the next run of the message loop,
158 // but we can at least tell the number of signals sent.
159 UMA_HISTOGRAM_TIMES("DBus.SignalSendTime",
160 base::TimeTicks::Now() - start_time);
[email protected]3beaaa4e2011-08-23 07:29:21161}
162
[email protected]a51076112011-08-17 20:58:12163bool ExportedObject::Register() {
164 bus_->AssertOnDBusThread();
165
166 if (object_is_registered_)
167 return true;
168
169 ScopedDBusError error;
170
171 DBusObjectPathVTable vtable = {};
172 vtable.message_function = &ExportedObject::HandleMessageThunk;
173 vtable.unregister_function = &ExportedObject::OnUnregisteredThunk;
174 const bool success = bus_->TryRegisterObjectPath(object_path_,
175 &vtable,
176 this,
177 error.get());
178 if (!success) {
[email protected]216ed0b2012-02-14 21:29:06179 LOG(ERROR) << "Failed to register the object: " << object_path_.value()
180 << ": " << (error.is_set() ? error.message() : "");
[email protected]a51076112011-08-17 20:58:12181 return false;
182 }
183
184 object_is_registered_ = true;
185 return true;
186}
187
188DBusHandlerResult ExportedObject::HandleMessage(
189 DBusConnection* connection,
190 DBusMessage* raw_message) {
191 bus_->AssertOnDBusThread();
192 DCHECK_EQ(DBUS_MESSAGE_TYPE_METHOD_CALL, dbus_message_get_type(raw_message));
193
194 // raw_message will be unrefed on exit of the function. Increment the
195 // reference so we can use it in MethodCall.
196 dbus_message_ref(raw_message);
197 scoped_ptr<MethodCall> method_call(
198 MethodCall::FromRawMessage(raw_message));
199 const std::string interface = method_call->GetInterface();
200 const std::string member = method_call->GetMember();
201
202 if (interface.empty()) {
203 // We don't support method calls without interface.
[email protected]a73389892011-09-06 20:53:30204 LOG(WARNING) << "Interface is missing: " << method_call->ToString();
[email protected]a51076112011-08-17 20:58:12205 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
206 }
207
208 // Check if we know about the method.
209 const std::string absolute_method_name = GetAbsoluteMethodName(
210 interface, member);
211 MethodTable::const_iterator iter = method_table_.find(absolute_method_name);
212 if (iter == method_table_.end()) {
213 // Don't know about the method.
[email protected]a73389892011-09-06 20:53:30214 LOG(WARNING) << "Unknown method: " << method_call->ToString();
[email protected]a51076112011-08-17 20:58:12215 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
216 }
217
[email protected]a73389892011-09-06 20:53:30218 const base::TimeTicks start_time = base::TimeTicks::Now();
[email protected]a51076112011-08-17 20:58:12219 if (bus_->HasDBusThread()) {
[email protected]a51076112011-08-17 20:58:12220 // Post a task to run the method in the origin thread.
221 bus_->PostTaskToOriginThread(FROM_HERE,
222 base::Bind(&ExportedObject::RunMethod,
223 this,
224 iter->second,
[email protected]e184cce2011-10-07 16:26:30225 method_call.release(),
226 start_time));
[email protected]a51076112011-08-17 20:58:12227 } else {
[email protected]9aa74cc2011-11-30 04:57:42228 // If the D-Bus thread is not used, just call the method directly.
229 MethodCall* released_method_call = method_call.release();
230 iter->second.Run(released_method_call,
231 base::Bind(&ExportedObject::SendResponse,
232 this,
233 start_time,
234 released_method_call));
[email protected]a51076112011-08-17 20:58:12235 }
[email protected]e184cce2011-10-07 16:26:30236
237 // It's valid to say HANDLED here, and send a method response at a later
238 // time from OnMethodCompleted() asynchronously.
239 return DBUS_HANDLER_RESULT_HANDLED;
240}
241
242void ExportedObject::RunMethod(MethodCallCallback method_call_callback,
243 MethodCall* method_call,
244 base::TimeTicks start_time) {
245 bus_->AssertOnOriginThread();
[email protected]9aa74cc2011-11-30 04:57:42246 method_call_callback.Run(method_call,
247 base::Bind(&ExportedObject::SendResponse,
248 this,
249 start_time,
250 method_call));
251}
[email protected]e184cce2011-10-07 16:26:30252
[email protected]9aa74cc2011-11-30 04:57:42253void ExportedObject::SendResponse(base::TimeTicks start_time,
254 MethodCall* method_call,
255 Response* response) {
256 DCHECK(method_call);
257 if (bus_->HasDBusThread()) {
258 bus_->PostTaskToDBusThread(FROM_HERE,
259 base::Bind(&ExportedObject::OnMethodCompleted,
260 this,
261 method_call,
262 response,
263 start_time));
264 } else {
265 OnMethodCompleted(method_call, response, start_time);
266 }
[email protected]e184cce2011-10-07 16:26:30267}
268
269void ExportedObject::OnMethodCompleted(MethodCall* method_call,
270 Response* response,
271 base::TimeTicks start_time) {
272 bus_->AssertOnDBusThread();
273 scoped_ptr<MethodCall> method_call_deleter(method_call);
274 scoped_ptr<Response> response_deleter(response);
275
[email protected]a73389892011-09-06 20:53:30276 // Record if the method call is successful, or not. 1 if successful.
277 UMA_HISTOGRAM_ENUMERATION("DBus.ExportedMethodHandleSuccess",
278 response ? 1 : 0,
279 kSuccessRatioHistogramMaxValue);
[email protected]a51076112011-08-17 20:58:12280
[email protected]e184cce2011-10-07 16:26:30281 // 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
[email protected]a51076112011-08-17 20:58:12286 if (!response) {
[email protected]829f0e4c2011-08-31 18:02:43287 // Something bad happened in the method call.
[email protected]a51076112011-08-17 20:58:12288 scoped_ptr<dbus::ErrorResponse> error_response(
[email protected]e184cce2011-10-07 16:26:30289 ErrorResponse::FromMethodCall(
290 method_call,
291 DBUS_ERROR_FAILED,
292 "error occurred in " + method_call->GetMember()));
293 bus_->Send(error_response->raw_message(), NULL);
294 return;
[email protected]a51076112011-08-17 20:58:12295 }
296
297 // The method call was successful.
[email protected]e184cce2011-10-07 16:26:30298 bus_->Send(response->raw_message(), NULL);
299
[email protected]a73389892011-09-06 20:53:30300 // Record time spent to handle the the method call. Don't include failures.
301 UMA_HISTOGRAM_TIMES("DBus.ExportedMethodHandleTime",
302 base::TimeTicks::Now() - start_time);
[email protected]a51076112011-08-17 20:58:12303}
304
305void ExportedObject::OnUnregistered(DBusConnection* connection) {
306}
307
308DBusHandlerResult ExportedObject::HandleMessageThunk(
309 DBusConnection* connection,
310 DBusMessage* raw_message,
311 void* user_data) {
312 ExportedObject* self = reinterpret_cast<ExportedObject*>(user_data);
313 return self->HandleMessage(connection, raw_message);
314}
315
316void ExportedObject::OnUnregisteredThunk(DBusConnection *connection,
317 void* user_data) {
318 ExportedObject* self = reinterpret_cast<ExportedObject*>(user_data);
319 return self->OnUnregistered(connection);
320}
321
322} // namespace dbus