blob: de2516841420e75de4e3abbe7f4adf0edf57aa3d [file] [log] [blame]
[email protected]02743b72012-02-14 20:22:531// Copyright (c) 2011 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"
16#include "dbus/scoped_dbus_error.h"
17
18namespace dbus {
19
20namespace {
21
[email protected]a73389892011-09-06 20:53:3022// Used for success ratio histograms. 1 for success, 0 for failure.
23const int kSuccessRatioHistogramMaxValue = 2;
24
[email protected]a51076112011-08-17 20:58:1225// Gets the absolute method name by concatenating the interface name and
26// the method name. Used for building keys for method_table_ in
27// ExportedObject.
28std::string GetAbsoluteMethodName(
29 const std::string& interface_name,
30 const std::string& method_name) {
31 return interface_name + "." + method_name;
32}
33
34} // namespace
35
36ExportedObject::ExportedObject(Bus* bus,
37 const std::string& service_name,
[email protected]02743b72012-02-14 20:22:5338 const std::string& object_path)
[email protected]a51076112011-08-17 20:58:1239 : bus_(bus),
40 service_name_(service_name),
41 object_path_(object_path),
[email protected]e184cce2011-10-07 16:26:3042 object_is_registered_(false) {
[email protected]a51076112011-08-17 20:58:1243}
44
45ExportedObject::~ExportedObject() {
46 DCHECK(!object_is_registered_);
47}
48
49bool ExportedObject::ExportMethodAndBlock(
50 const std::string& interface_name,
51 const std::string& method_name,
52 MethodCallCallback method_call_callback) {
53 bus_->AssertOnDBusThread();
54
[email protected]3beaaa4e2011-08-23 07:29:2155 // Check if the method is already exported.
56 const std::string absolute_method_name =
57 GetAbsoluteMethodName(interface_name, method_name);
58 if (method_table_.find(absolute_method_name) != method_table_.end()) {
59 LOG(ERROR) << absolute_method_name << " is already exported";
60 return false;
61 }
62
[email protected]a51076112011-08-17 20:58:1263 if (!bus_->Connect())
64 return false;
65 if (!bus_->SetUpAsyncOperations())
66 return false;
67 if (!bus_->RequestOwnership(service_name_))
68 return false;
69 if (!Register())
70 return false;
71
[email protected]3beaaa4e2011-08-23 07:29:2172 // Add the method callback to the method table.
[email protected]a51076112011-08-17 20:58:1273 method_table_[absolute_method_name] = method_call_callback;
74
75 return true;
76}
77
78void ExportedObject::ExportMethod(const std::string& interface_name,
79 const std::string& method_name,
80 MethodCallCallback method_call_callback,
81 OnExportedCallback on_exported_calback) {
82 bus_->AssertOnOriginThread();
83
84 base::Closure task = base::Bind(&ExportedObject::ExportMethodInternal,
85 this,
86 interface_name,
87 method_name,
88 method_call_callback,
89 on_exported_calback);
90 bus_->PostTaskToDBusThread(FROM_HERE, task);
91}
92
[email protected]3beaaa4e2011-08-23 07:29:2193void ExportedObject::SendSignal(Signal* signal) {
94 // For signals, the object path should be set to the path to the sender
95 // object, which is this exported object here.
96 signal->SetPath(object_path_);
97
98 // Increment the reference count so we can safely reference the
99 // underlying signal message until the signal sending is complete. This
100 // will be unref'ed in SendSignalInternal().
101 DBusMessage* signal_message = signal->raw_message();
102 dbus_message_ref(signal_message);
103
[email protected]a73389892011-09-06 20:53:30104 const base::TimeTicks start_time = base::TimeTicks::Now();
[email protected]3beaaa4e2011-08-23 07:29:21105 bus_->PostTaskToDBusThread(FROM_HERE,
106 base::Bind(&ExportedObject::SendSignalInternal,
107 this,
[email protected]a73389892011-09-06 20:53:30108 start_time,
[email protected]1d887b272011-10-04 13:47:21109 signal_message));
[email protected]3beaaa4e2011-08-23 07:29:21110}
111
[email protected]a51076112011-08-17 20:58:12112void ExportedObject::Unregister() {
113 bus_->AssertOnDBusThread();
114
115 if (!object_is_registered_)
116 return;
117
118 bus_->UnregisterObjectPath(object_path_);
119 object_is_registered_ = false;
120}
121
122void ExportedObject::ExportMethodInternal(
123 const std::string& interface_name,
124 const std::string& method_name,
125 MethodCallCallback method_call_callback,
126 OnExportedCallback on_exported_calback) {
127 bus_->AssertOnDBusThread();
128
129 const bool success = ExportMethodAndBlock(interface_name,
130 method_name,
131 method_call_callback);
132 bus_->PostTaskToOriginThread(FROM_HERE,
133 base::Bind(&ExportedObject::OnExported,
134 this,
135 on_exported_calback,
136 interface_name,
137 method_name,
138 success));
139}
140
141void ExportedObject::OnExported(OnExportedCallback on_exported_callback,
142 const std::string& interface_name,
143 const std::string& method_name,
144 bool success) {
145 bus_->AssertOnOriginThread();
146
147 on_exported_callback.Run(interface_name, method_name, success);
148}
149
[email protected]a73389892011-09-06 20:53:30150void ExportedObject::SendSignalInternal(base::TimeTicks start_time,
[email protected]1d887b272011-10-04 13:47:21151 DBusMessage* signal_message) {
[email protected]3beaaa4e2011-08-23 07:29:21152 uint32 serial = 0;
153 bus_->Send(signal_message, &serial);
154 dbus_message_unref(signal_message);
[email protected]a73389892011-09-06 20:53:30155 // Record time spent to send the the signal. This is not accurate as the
156 // signal will actually be sent from the next run of the message loop,
157 // but we can at least tell the number of signals sent.
158 UMA_HISTOGRAM_TIMES("DBus.SignalSendTime",
159 base::TimeTicks::Now() - start_time);
[email protected]3beaaa4e2011-08-23 07:29:21160}
161
[email protected]a51076112011-08-17 20:58:12162bool ExportedObject::Register() {
163 bus_->AssertOnDBusThread();
164
165 if (object_is_registered_)
166 return true;
167
168 ScopedDBusError error;
169
170 DBusObjectPathVTable vtable = {};
171 vtable.message_function = &ExportedObject::HandleMessageThunk;
172 vtable.unregister_function = &ExportedObject::OnUnregisteredThunk;
173 const bool success = bus_->TryRegisterObjectPath(object_path_,
174 &vtable,
175 this,
176 error.get());
177 if (!success) {
[email protected]02743b72012-02-14 20:22:53178 LOG(ERROR) << "Failed to register the object: " << object_path_ << ": "
179 << (error.is_set() ? error.message() : "");
[email protected]a51076112011-08-17 20:58:12180 return false;
181 }
182
183 object_is_registered_ = true;
184 return true;
185}
186
187DBusHandlerResult ExportedObject::HandleMessage(
188 DBusConnection* connection,
189 DBusMessage* raw_message) {
190 bus_->AssertOnDBusThread();
191 DCHECK_EQ(DBUS_MESSAGE_TYPE_METHOD_CALL, dbus_message_get_type(raw_message));
192
193 // raw_message will be unrefed on exit of the function. Increment the
194 // reference so we can use it in MethodCall.
195 dbus_message_ref(raw_message);
196 scoped_ptr<MethodCall> method_call(
197 MethodCall::FromRawMessage(raw_message));
198 const std::string interface = method_call->GetInterface();
199 const std::string member = method_call->GetMember();
200
201 if (interface.empty()) {
202 // We don't support method calls without interface.
[email protected]a73389892011-09-06 20:53:30203 LOG(WARNING) << "Interface is missing: " << method_call->ToString();
[email protected]a51076112011-08-17 20:58:12204 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
205 }
206
207 // Check if we know about the method.
208 const std::string absolute_method_name = GetAbsoluteMethodName(
209 interface, member);
210 MethodTable::const_iterator iter = method_table_.find(absolute_method_name);
211 if (iter == method_table_.end()) {
212 // Don't know about the method.
[email protected]a73389892011-09-06 20:53:30213 LOG(WARNING) << "Unknown method: " << method_call->ToString();
[email protected]a51076112011-08-17 20:58:12214 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
215 }
216
[email protected]a73389892011-09-06 20:53:30217 const base::TimeTicks start_time = base::TimeTicks::Now();
[email protected]a51076112011-08-17 20:58:12218 if (bus_->HasDBusThread()) {
[email protected]a51076112011-08-17 20:58:12219 // Post a task to run the method in the origin thread.
220 bus_->PostTaskToOriginThread(FROM_HERE,
221 base::Bind(&ExportedObject::RunMethod,
222 this,
223 iter->second,
[email protected]e184cce2011-10-07 16:26:30224 method_call.release(),
225 start_time));
[email protected]a51076112011-08-17 20:58:12226 } else {
[email protected]9aa74cc2011-11-30 04:57:42227 // If the D-Bus thread is not used, just call the method directly.
228 MethodCall* released_method_call = method_call.release();
229 iter->second.Run(released_method_call,
230 base::Bind(&ExportedObject::SendResponse,
231 this,
232 start_time,
233 released_method_call));
[email protected]a51076112011-08-17 20:58:12234 }
[email protected]e184cce2011-10-07 16:26:30235
236 // It's valid to say HANDLED here, and send a method response at a later
237 // time from OnMethodCompleted() asynchronously.
238 return DBUS_HANDLER_RESULT_HANDLED;
239}
240
241void ExportedObject::RunMethod(MethodCallCallback method_call_callback,
242 MethodCall* method_call,
243 base::TimeTicks start_time) {
244 bus_->AssertOnOriginThread();
[email protected]9aa74cc2011-11-30 04:57:42245 method_call_callback.Run(method_call,
246 base::Bind(&ExportedObject::SendResponse,
247 this,
248 start_time,
249 method_call));
250}
[email protected]e184cce2011-10-07 16:26:30251
[email protected]9aa74cc2011-11-30 04:57:42252void ExportedObject::SendResponse(base::TimeTicks start_time,
253 MethodCall* method_call,
254 Response* response) {
255 DCHECK(method_call);
256 if (bus_->HasDBusThread()) {
257 bus_->PostTaskToDBusThread(FROM_HERE,
258 base::Bind(&ExportedObject::OnMethodCompleted,
259 this,
260 method_call,
261 response,
262 start_time));
263 } else {
264 OnMethodCompleted(method_call, response, start_time);
265 }
[email protected]e184cce2011-10-07 16:26:30266}
267
268void ExportedObject::OnMethodCompleted(MethodCall* method_call,
269 Response* response,
270 base::TimeTicks start_time) {
271 bus_->AssertOnDBusThread();
272 scoped_ptr<MethodCall> method_call_deleter(method_call);
273 scoped_ptr<Response> response_deleter(response);
274
[email protected]a73389892011-09-06 20:53:30275 // Record if the method call is successful, or not. 1 if successful.
276 UMA_HISTOGRAM_ENUMERATION("DBus.ExportedMethodHandleSuccess",
277 response ? 1 : 0,
278 kSuccessRatioHistogramMaxValue);
[email protected]a51076112011-08-17 20:58:12279
[email protected]e184cce2011-10-07 16:26:30280 // Check if the bus is still connected. If the method takes long to
281 // complete, the bus may be shut down meanwhile.
282 if (!bus_->is_connected())
283 return;
284
[email protected]a51076112011-08-17 20:58:12285 if (!response) {
[email protected]829f0e4c2011-08-31 18:02:43286 // Something bad happened in the method call.
[email protected]a51076112011-08-17 20:58:12287 scoped_ptr<dbus::ErrorResponse> error_response(
[email protected]e184cce2011-10-07 16:26:30288 ErrorResponse::FromMethodCall(
289 method_call,
290 DBUS_ERROR_FAILED,
291 "error occurred in " + method_call->GetMember()));
292 bus_->Send(error_response->raw_message(), NULL);
293 return;
[email protected]a51076112011-08-17 20:58:12294 }
295
296 // The method call was successful.
[email protected]e184cce2011-10-07 16:26:30297 bus_->Send(response->raw_message(), NULL);
298
[email protected]a73389892011-09-06 20:53:30299 // 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);
[email protected]a51076112011-08-17 20:58:12302}
303
304void ExportedObject::OnUnregistered(DBusConnection* connection) {
305}
306
307DBusHandlerResult 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
315void 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