blob: 1a4fbb50eb4ad59f03767434fe8e5fe78f912cd1 [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"
[email protected]2a9ec0e2013-07-17 23:00:3010#include "base/message_loop/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"
[email protected]b43e5562013-06-28 15:20:0213#include "base/time/time.h"
[email protected]a51076112011-08-17 20:58:1214#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,
[email protected]216ed0b2012-02-14 21:29:0638 const ObjectPath& object_path)
[email protected]a51076112011-08-17 20:58:1239 : bus_(bus),
[email protected]a51076112011-08-17 20:58:1240 object_path_(object_path),
[email protected]e184cce2011-10-07 16:26:3041 object_is_registered_(false) {
[email protected]a51076112011-08-17 20:58:1242}
43
44ExportedObject::~ExportedObject() {
45 DCHECK(!object_is_registered_);
46}
47
48bool ExportedObject::ExportMethodAndBlock(
49 const std::string& interface_name,
50 const std::string& method_name,
51 MethodCallCallback method_call_callback) {
52 bus_->AssertOnDBusThread();
53
[email protected]3beaaa4e2011-08-23 07:29:2154 // 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
[email protected]a51076112011-08-17 20:58:1262 if (!bus_->Connect())
63 return false;
64 if (!bus_->SetUpAsyncOperations())
65 return false;
[email protected]a51076112011-08-17 20:58:1266 if (!Register())
67 return false;
68
[email protected]3beaaa4e2011-08-23 07:29:2169 // Add the method callback to the method table.
[email protected]a51076112011-08-17 20:58:1270 method_table_[absolute_method_name] = method_call_callback;
71
72 return true;
73}
74
75void 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);
[email protected]8609be262013-09-26 04:32:2987 bus_->GetDBusTaskRunner()->PostTask(FROM_HERE, task);
[email protected]a51076112011-08-17 20:58:1288}
89
[email protected]3beaaa4e2011-08-23 07:29:2190void 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.
[email protected]ca72ff22012-05-23 06:55:2293 CHECK(signal->SetPath(object_path_));
[email protected]3beaaa4e2011-08-23 07:29:2194
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
[email protected]a73389892011-09-06 20:53:30101 const base::TimeTicks start_time = base::TimeTicks::Now();
[email protected]8609be262013-09-26 04:32:29102 bus_->GetDBusTaskRunner()->PostTask(
103 FROM_HERE,
104 base::Bind(&ExportedObject::SendSignalInternal,
105 this,
106 start_time,
107 signal_message));
[email protected]3beaaa4e2011-08-23 07:29:21108}
109
[email protected]a51076112011-08-17 20:58:12110void 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
120void 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);
[email protected]8609be262013-09-26 04:32:29130 bus_->GetOriginTaskRunner()->PostTask(FROM_HERE,
131 base::Bind(&ExportedObject::OnExported,
132 this,
133 on_exported_calback,
134 interface_name,
135 method_name,
136 success));
[email protected]a51076112011-08-17 20:58:12137}
138
139void 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
[email protected]a73389892011-09-06 20:53:30148void ExportedObject::SendSignalInternal(base::TimeTicks start_time,
[email protected]1d887b272011-10-04 13:47:21149 DBusMessage* signal_message) {
[email protected]3beaaa4e2011-08-23 07:29:21150 uint32 serial = 0;
151 bus_->Send(signal_message, &serial);
152 dbus_message_unref(signal_message);
[email protected]a73389892011-09-06 20:53:30153 // 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);
[email protected]3beaaa4e2011-08-23 07:29:21158}
159
[email protected]a51076112011-08-17 20:58:12160bool 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) {
[email protected]216ed0b2012-02-14 21:29:06176 LOG(ERROR) << "Failed to register the object: " << object_path_.value()
177 << ": " << (error.is_set() ? error.message() : "");
[email protected]a51076112011-08-17 20:58:12178 return false;
179 }
180
181 object_is_registered_ = true;
182 return true;
183}
184
185DBusHandlerResult 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.
[email protected]a73389892011-09-06 20:53:30201 LOG(WARNING) << "Interface is missing: " << method_call->ToString();
[email protected]a51076112011-08-17 20:58:12202 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.
[email protected]a73389892011-09-06 20:53:30211 LOG(WARNING) << "Unknown method: " << method_call->ToString();
[email protected]a51076112011-08-17 20:58:12212 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
213 }
214
[email protected]a73389892011-09-06 20:53:30215 const base::TimeTicks start_time = base::TimeTicks::Now();
[email protected]a51076112011-08-17 20:58:12216 if (bus_->HasDBusThread()) {
[email protected]a51076112011-08-17 20:58:12217 // Post a task to run the method in the origin thread.
[email protected]8609be262013-09-26 04:32:29218 bus_->GetOriginTaskRunner()->PostTask(FROM_HERE,
219 base::Bind(&ExportedObject::RunMethod,
220 this,
221 iter->second,
222 base::Passed(&method_call),
223 start_time));
[email protected]a51076112011-08-17 20:58:12224 } else {
[email protected]9aa74cc2011-11-30 04:57:42225 // If the D-Bus thread is not used, just call the method directly.
[email protected]9b25d452013-02-07 09:46:24226 MethodCall* method = method_call.get();
227 iter->second.Run(method,
[email protected]9aa74cc2011-11-30 04:57:42228 base::Bind(&ExportedObject::SendResponse,
229 this,
230 start_time,
[email protected]9b25d452013-02-07 09:46:24231 base::Passed(&method_call)));
[email protected]a51076112011-08-17 20:58:12232 }
[email protected]e184cce2011-10-07 16:26:30233
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
239void ExportedObject::RunMethod(MethodCallCallback method_call_callback,
[email protected]9b25d452013-02-07 09:46:24240 scoped_ptr<MethodCall> method_call,
[email protected]e184cce2011-10-07 16:26:30241 base::TimeTicks start_time) {
242 bus_->AssertOnOriginThread();
[email protected]9b25d452013-02-07 09:46:24243 MethodCall* method = method_call.get();
244 method_call_callback.Run(method,
[email protected]9aa74cc2011-11-30 04:57:42245 base::Bind(&ExportedObject::SendResponse,
246 this,
247 start_time,
[email protected]9b25d452013-02-07 09:46:24248 base::Passed(&method_call)));
[email protected]9aa74cc2011-11-30 04:57:42249}
[email protected]e184cce2011-10-07 16:26:30250
[email protected]9aa74cc2011-11-30 04:57:42251void ExportedObject::SendResponse(base::TimeTicks start_time,
[email protected]9b25d452013-02-07 09:46:24252 scoped_ptr<MethodCall> method_call,
253 scoped_ptr<Response> response) {
[email protected]9aa74cc2011-11-30 04:57:42254 DCHECK(method_call);
255 if (bus_->HasDBusThread()) {
[email protected]8609be262013-09-26 04:32:29256 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));
[email protected]9aa74cc2011-11-30 04:57:42263 } else {
[email protected]9b25d452013-02-07 09:46:24264 OnMethodCompleted(method_call.Pass(), response.Pass(), start_time);
[email protected]9aa74cc2011-11-30 04:57:42265 }
[email protected]e184cce2011-10-07 16:26:30266}
267
[email protected]9b25d452013-02-07 09:46:24268void ExportedObject::OnMethodCompleted(scoped_ptr<MethodCall> method_call,
269 scoped_ptr<Response> response,
[email protected]e184cce2011-10-07 16:26:30270 base::TimeTicks start_time) {
271 bus_->AssertOnDBusThread();
[email protected]e184cce2011-10-07 16:26:30272
[email protected]a73389892011-09-06 20:53:30273 // Record if the method call is successful, or not. 1 if successful.
274 UMA_HISTOGRAM_ENUMERATION("DBus.ExportedMethodHandleSuccess",
275 response ? 1 : 0,
276 kSuccessRatioHistogramMaxValue);
[email protected]a51076112011-08-17 20:58:12277
[email protected]e184cce2011-10-07 16:26:30278 // 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
[email protected]a51076112011-08-17 20:58:12283 if (!response) {
[email protected]829f0e4c2011-08-31 18:02:43284 // Something bad happened in the method call.
[email protected]2a57ca642013-06-13 06:37:19285 scoped_ptr<ErrorResponse> error_response(
[email protected]e184cce2011-10-07 16:26:30286 ErrorResponse::FromMethodCall(
[email protected]9b25d452013-02-07 09:46:24287 method_call.get(),
[email protected]e184cce2011-10-07 16:26:30288 DBUS_ERROR_FAILED,
289 "error occurred in " + method_call->GetMember()));
290 bus_->Send(error_response->raw_message(), NULL);
291 return;
[email protected]a51076112011-08-17 20:58:12292 }
293
294 // The method call was successful.
[email protected]e184cce2011-10-07 16:26:30295 bus_->Send(response->raw_message(), NULL);
296
[email protected]a73389892011-09-06 20:53:30297 // 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);
[email protected]a51076112011-08-17 20:58:12300}
301
302void ExportedObject::OnUnregistered(DBusConnection* connection) {
303}
304
305DBusHandlerResult 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
313void 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