blob: 4c0a33581eae53f1daa4313fa980ee2790b6317a [file] [log] [blame]
keybuk@google.combf4649a2012-02-15 06:29:06 +09001// Copyright (c) 2012 The Chromium Authors. All rights reserved.
satorux@chromium.org163f1cb2011-08-18 05:58:12 +09002// 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"
satorux@chromium.org5a92cf32011-09-07 05:53:30 +090011#include "base/metrics/histogram.h"
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090012#include "base/threading/thread_restrictions.h"
avi@chromium.org78a7e7b2013-06-29 00:20:02 +090013#include "base/time/time.h"
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090014#include "dbus/bus.h"
15#include "dbus/message.h"
keybuk@google.combf4649a2012-02-15 06:29:06 +090016#include "dbus/object_path.h"
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090017#include "dbus/scoped_dbus_error.h"
18
19namespace dbus {
20
21namespace {
22
satorux@chromium.org5a92cf32011-09-07 05:53:30 +090023// Used for success ratio histograms. 1 for success, 0 for failure.
24const int kSuccessRatioHistogramMaxValue = 2;
25
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090026// 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,
keybuk@google.combf4649a2012-02-15 06:29:06 +090038 const ObjectPath& object_path)
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090039 : bus_(bus),
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090040 object_path_(object_path),
satorux@chromium.orgc9ebea22011-10-08 01:26:30 +090041 object_is_registered_(false) {
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090042}
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
satorux@chromium.org7f0c4512011-08-23 16:29:21 +090054 // 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
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090062 if (!bus_->Connect())
63 return false;
64 if (!bus_->SetUpAsyncOperations())
65 return false;
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090066 if (!Register())
67 return false;
68
satorux@chromium.org7f0c4512011-08-23 16:29:21 +090069 // Add the method callback to the method table.
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090070 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);
87 bus_->PostTaskToDBusThread(FROM_HERE, task);
88}
89
satorux@chromium.org7f0c4512011-08-23 16:29:21 +090090void 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.
hashimoto@chromium.orgb0305512012-05-23 15:55:22 +090093 CHECK(signal->SetPath(object_path_));
satorux@chromium.org7f0c4512011-08-23 16:29:21 +090094
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
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900101 const base::TimeTicks start_time = base::TimeTicks::Now();
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900102 bus_->PostTaskToDBusThread(FROM_HERE,
103 base::Bind(&ExportedObject::SendSignalInternal,
104 this,
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900105 start_time,
satorux@chromium.org47d706b2011-10-04 22:47:21 +0900106 signal_message));
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900107}
108
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900109void ExportedObject::Unregister() {
110 bus_->AssertOnDBusThread();
111
112 if (!object_is_registered_)
113 return;
114
115 bus_->UnregisterObjectPath(object_path_);
116 object_is_registered_ = false;
117}
118
119void ExportedObject::ExportMethodInternal(
120 const std::string& interface_name,
121 const std::string& method_name,
122 MethodCallCallback method_call_callback,
123 OnExportedCallback on_exported_calback) {
124 bus_->AssertOnDBusThread();
125
126 const bool success = ExportMethodAndBlock(interface_name,
127 method_name,
128 method_call_callback);
129 bus_->PostTaskToOriginThread(FROM_HERE,
130 base::Bind(&ExportedObject::OnExported,
131 this,
132 on_exported_calback,
133 interface_name,
134 method_name,
135 success));
136}
137
138void ExportedObject::OnExported(OnExportedCallback on_exported_callback,
139 const std::string& interface_name,
140 const std::string& method_name,
141 bool success) {
142 bus_->AssertOnOriginThread();
143
144 on_exported_callback.Run(interface_name, method_name, success);
145}
146
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900147void ExportedObject::SendSignalInternal(base::TimeTicks start_time,
satorux@chromium.org47d706b2011-10-04 22:47:21 +0900148 DBusMessage* signal_message) {
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900149 uint32 serial = 0;
150 bus_->Send(signal_message, &serial);
151 dbus_message_unref(signal_message);
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900152 // Record time spent to send the the signal. This is not accurate as the
153 // signal will actually be sent from the next run of the message loop,
154 // but we can at least tell the number of signals sent.
155 UMA_HISTOGRAM_TIMES("DBus.SignalSendTime",
156 base::TimeTicks::Now() - start_time);
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900157}
158
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900159bool ExportedObject::Register() {
160 bus_->AssertOnDBusThread();
161
162 if (object_is_registered_)
163 return true;
164
165 ScopedDBusError error;
166
167 DBusObjectPathVTable vtable = {};
168 vtable.message_function = &ExportedObject::HandleMessageThunk;
169 vtable.unregister_function = &ExportedObject::OnUnregisteredThunk;
170 const bool success = bus_->TryRegisterObjectPath(object_path_,
171 &vtable,
172 this,
173 error.get());
174 if (!success) {
keybuk@google.combf4649a2012-02-15 06:29:06 +0900175 LOG(ERROR) << "Failed to register the object: " << object_path_.value()
176 << ": " << (error.is_set() ? error.message() : "");
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900177 return false;
178 }
179
180 object_is_registered_ = true;
181 return true;
182}
183
184DBusHandlerResult ExportedObject::HandleMessage(
185 DBusConnection* connection,
186 DBusMessage* raw_message) {
187 bus_->AssertOnDBusThread();
188 DCHECK_EQ(DBUS_MESSAGE_TYPE_METHOD_CALL, dbus_message_get_type(raw_message));
189
190 // raw_message will be unrefed on exit of the function. Increment the
191 // reference so we can use it in MethodCall.
192 dbus_message_ref(raw_message);
193 scoped_ptr<MethodCall> method_call(
194 MethodCall::FromRawMessage(raw_message));
195 const std::string interface = method_call->GetInterface();
196 const std::string member = method_call->GetMember();
197
198 if (interface.empty()) {
199 // We don't support method calls without interface.
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900200 LOG(WARNING) << "Interface is missing: " << method_call->ToString();
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900201 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
202 }
203
204 // Check if we know about the method.
205 const std::string absolute_method_name = GetAbsoluteMethodName(
206 interface, member);
207 MethodTable::const_iterator iter = method_table_.find(absolute_method_name);
208 if (iter == method_table_.end()) {
209 // Don't know about the method.
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900210 LOG(WARNING) << "Unknown method: " << method_call->ToString();
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900211 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
212 }
213
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900214 const base::TimeTicks start_time = base::TimeTicks::Now();
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900215 if (bus_->HasDBusThread()) {
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900216 // Post a task to run the method in the origin thread.
217 bus_->PostTaskToOriginThread(FROM_HERE,
218 base::Bind(&ExportedObject::RunMethod,
219 this,
220 iter->second,
yuki@chromium.orgd4eedf82013-02-07 18:46:24 +0900221 base::Passed(&method_call),
satorux@chromium.orgc9ebea22011-10-08 01:26:30 +0900222 start_time));
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900223 } else {
vlaviano@chromium.org241215d2011-11-30 13:57:42 +0900224 // If the D-Bus thread is not used, just call the method directly.
yuki@chromium.orgd4eedf82013-02-07 18:46:24 +0900225 MethodCall* method = method_call.get();
226 iter->second.Run(method,
vlaviano@chromium.org241215d2011-11-30 13:57:42 +0900227 base::Bind(&ExportedObject::SendResponse,
228 this,
229 start_time,
yuki@chromium.orgd4eedf82013-02-07 18:46:24 +0900230 base::Passed(&method_call)));
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900231 }
satorux@chromium.orgc9ebea22011-10-08 01:26:30 +0900232
233 // It's valid to say HANDLED here, and send a method response at a later
234 // time from OnMethodCompleted() asynchronously.
235 return DBUS_HANDLER_RESULT_HANDLED;
236}
237
238void ExportedObject::RunMethod(MethodCallCallback method_call_callback,
yuki@chromium.orgd4eedf82013-02-07 18:46:24 +0900239 scoped_ptr<MethodCall> method_call,
satorux@chromium.orgc9ebea22011-10-08 01:26:30 +0900240 base::TimeTicks start_time) {
241 bus_->AssertOnOriginThread();
yuki@chromium.orgd4eedf82013-02-07 18:46:24 +0900242 MethodCall* method = method_call.get();
243 method_call_callback.Run(method,
vlaviano@chromium.org241215d2011-11-30 13:57:42 +0900244 base::Bind(&ExportedObject::SendResponse,
245 this,
246 start_time,
yuki@chromium.orgd4eedf82013-02-07 18:46:24 +0900247 base::Passed(&method_call)));
vlaviano@chromium.org241215d2011-11-30 13:57:42 +0900248}
satorux@chromium.orgc9ebea22011-10-08 01:26:30 +0900249
vlaviano@chromium.org241215d2011-11-30 13:57:42 +0900250void ExportedObject::SendResponse(base::TimeTicks start_time,
yuki@chromium.orgd4eedf82013-02-07 18:46:24 +0900251 scoped_ptr<MethodCall> method_call,
252 scoped_ptr<Response> response) {
vlaviano@chromium.org241215d2011-11-30 13:57:42 +0900253 DCHECK(method_call);
254 if (bus_->HasDBusThread()) {
255 bus_->PostTaskToDBusThread(FROM_HERE,
256 base::Bind(&ExportedObject::OnMethodCompleted,
257 this,
yuki@chromium.orgd4eedf82013-02-07 18:46:24 +0900258 base::Passed(&method_call),
259 base::Passed(&response),
vlaviano@chromium.org241215d2011-11-30 13:57:42 +0900260 start_time));
261 } else {
yuki@chromium.orgd4eedf82013-02-07 18:46:24 +0900262 OnMethodCompleted(method_call.Pass(), response.Pass(), start_time);
vlaviano@chromium.org241215d2011-11-30 13:57:42 +0900263 }
satorux@chromium.orgc9ebea22011-10-08 01:26:30 +0900264}
265
yuki@chromium.orgd4eedf82013-02-07 18:46:24 +0900266void ExportedObject::OnMethodCompleted(scoped_ptr<MethodCall> method_call,
267 scoped_ptr<Response> response,
satorux@chromium.orgc9ebea22011-10-08 01:26:30 +0900268 base::TimeTicks start_time) {
269 bus_->AssertOnDBusThread();
satorux@chromium.orgc9ebea22011-10-08 01:26:30 +0900270
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900271 // Record if the method call is successful, or not. 1 if successful.
272 UMA_HISTOGRAM_ENUMERATION("DBus.ExportedMethodHandleSuccess",
273 response ? 1 : 0,
274 kSuccessRatioHistogramMaxValue);
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900275
satorux@chromium.orgc9ebea22011-10-08 01:26:30 +0900276 // Check if the bus is still connected. If the method takes long to
277 // complete, the bus may be shut down meanwhile.
278 if (!bus_->is_connected())
279 return;
280
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900281 if (!response) {
satorux@chromium.orgc6ac7572011-09-01 03:02:43 +0900282 // Something bad happened in the method call.
thestig@chromium.orgf0b7eac2013-06-13 15:37:19 +0900283 scoped_ptr<ErrorResponse> error_response(
satorux@chromium.orgc9ebea22011-10-08 01:26:30 +0900284 ErrorResponse::FromMethodCall(
yuki@chromium.orgd4eedf82013-02-07 18:46:24 +0900285 method_call.get(),
satorux@chromium.orgc9ebea22011-10-08 01:26:30 +0900286 DBUS_ERROR_FAILED,
287 "error occurred in " + method_call->GetMember()));
288 bus_->Send(error_response->raw_message(), NULL);
289 return;
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900290 }
291
292 // The method call was successful.
satorux@chromium.orgc9ebea22011-10-08 01:26:30 +0900293 bus_->Send(response->raw_message(), NULL);
294
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900295 // Record time spent to handle the the method call. Don't include failures.
296 UMA_HISTOGRAM_TIMES("DBus.ExportedMethodHandleTime",
297 base::TimeTicks::Now() - start_time);
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900298}
299
300void ExportedObject::OnUnregistered(DBusConnection* connection) {
301}
302
303DBusHandlerResult ExportedObject::HandleMessageThunk(
304 DBusConnection* connection,
305 DBusMessage* raw_message,
306 void* user_data) {
307 ExportedObject* self = reinterpret_cast<ExportedObject*>(user_data);
308 return self->HandleMessage(connection, raw_message);
309}
310
311void ExportedObject::OnUnregisteredThunk(DBusConnection *connection,
312 void* user_data) {
313 ExportedObject* self = reinterpret_cast<ExportedObject*>(user_data);
314 return self->OnUnregistered(connection);
315}
316
317} // namespace dbus