blob: 107d2e5dd1762629ad436c54d76727ea6efba230 [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"
avi@chromium.orga29af562013-07-18 08:00:30 +090010#include "base/message_loop/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"
armansitof4364642014-09-06 02:49:34 +090018#include "dbus/util.h"
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090019
20namespace dbus {
21
22namespace {
23
satorux@chromium.org5a92cf32011-09-07 05:53:30 +090024// Used for success ratio histograms. 1 for success, 0 for failure.
25const int kSuccessRatioHistogramMaxValue = 2;
26
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090027} // namespace
28
29ExportedObject::ExportedObject(Bus* bus,
keybuk@google.combf4649a2012-02-15 06:29:06 +090030 const ObjectPath& object_path)
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090031 : bus_(bus),
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090032 object_path_(object_path),
satorux@chromium.orgc9ebea22011-10-08 01:26:30 +090033 object_is_registered_(false) {
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090034}
35
36ExportedObject::~ExportedObject() {
37 DCHECK(!object_is_registered_);
38}
39
40bool ExportedObject::ExportMethodAndBlock(
41 const std::string& interface_name,
42 const std::string& method_name,
43 MethodCallCallback method_call_callback) {
44 bus_->AssertOnDBusThread();
45
satorux@chromium.org7f0c4512011-08-23 16:29:21 +090046 // Check if the method is already exported.
47 const std::string absolute_method_name =
armansitof4364642014-09-06 02:49:34 +090048 GetAbsoluteMemberName(interface_name, method_name);
satorux@chromium.org7f0c4512011-08-23 16:29:21 +090049 if (method_table_.find(absolute_method_name) != method_table_.end()) {
50 LOG(ERROR) << absolute_method_name << " is already exported";
51 return false;
52 }
53
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090054 if (!bus_->Connect())
55 return false;
56 if (!bus_->SetUpAsyncOperations())
57 return false;
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090058 if (!Register())
59 return false;
60
satorux@chromium.org7f0c4512011-08-23 16:29:21 +090061 // Add the method callback to the method table.
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090062 method_table_[absolute_method_name] = method_call_callback;
63
64 return true;
65}
66
67void 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);
hashimoto@chromium.org955f6482013-09-26 13:32:29 +090079 bus_->GetDBusTaskRunner()->PostTask(FROM_HERE, task);
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090080}
81
satorux@chromium.org7f0c4512011-08-23 16:29:21 +090082void 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.
hashimoto@chromium.orgb0305512012-05-23 15:55:22 +090085 CHECK(signal->SetPath(object_path_));
satorux@chromium.org7f0c4512011-08-23 16:29:21 +090086
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
satorux@chromium.org5a92cf32011-09-07 05:53:30 +090093 const base::TimeTicks start_time = base::TimeTicks::Now();
hashimoto@chromium.org955f6482013-09-26 13:32:29 +090094 bus_->GetDBusTaskRunner()->PostTask(
95 FROM_HERE,
96 base::Bind(&ExportedObject::SendSignalInternal,
97 this,
98 start_time,
99 signal_message));
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900100}
101
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900102void 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
112void 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);
hashimoto@chromium.org955f6482013-09-26 13:32:29 +0900122 bus_->GetOriginTaskRunner()->PostTask(FROM_HERE,
123 base::Bind(&ExportedObject::OnExported,
124 this,
125 on_exported_calback,
126 interface_name,
127 method_name,
128 success));
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900129}
130
131void 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
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900140void ExportedObject::SendSignalInternal(base::TimeTicks start_time,
satorux@chromium.org47d706b2011-10-04 22:47:21 +0900141 DBusMessage* signal_message) {
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900142 uint32 serial = 0;
143 bus_->Send(signal_message, &serial);
144 dbus_message_unref(signal_message);
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900145 // 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);
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900150}
151
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900152bool 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) {
keybuk@google.combf4649a2012-02-15 06:29:06 +0900168 LOG(ERROR) << "Failed to register the object: " << object_path_.value()
169 << ": " << (error.is_set() ? error.message() : "");
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900170 return false;
171 }
172
173 object_is_registered_ = true;
174 return true;
175}
176
177DBusHandlerResult 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.
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900193 LOG(WARNING) << "Interface is missing: " << method_call->ToString();
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900194 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
195 }
196
197 // Check if we know about the method.
armansitof4364642014-09-06 02:49:34 +0900198 const std::string absolute_method_name = GetAbsoluteMemberName(
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900199 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.
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900203 LOG(WARNING) << "Unknown method: " << method_call->ToString();
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900204 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
205 }
206
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900207 const base::TimeTicks start_time = base::TimeTicks::Now();
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900208 if (bus_->HasDBusThread()) {
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900209 // Post a task to run the method in the origin thread.
hashimoto@chromium.org955f6482013-09-26 13:32:29 +0900210 bus_->GetOriginTaskRunner()->PostTask(FROM_HERE,
211 base::Bind(&ExportedObject::RunMethod,
212 this,
213 iter->second,
214 base::Passed(&method_call),
215 start_time));
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900216 } else {
vlaviano@chromium.org241215d2011-11-30 13:57:42 +0900217 // If the D-Bus thread is not used, just call the method directly.
yuki@chromium.orgd4eedf82013-02-07 18:46:24 +0900218 MethodCall* method = method_call.get();
219 iter->second.Run(method,
vlaviano@chromium.org241215d2011-11-30 13:57:42 +0900220 base::Bind(&ExportedObject::SendResponse,
221 this,
222 start_time,
yuki@chromium.orgd4eedf82013-02-07 18:46:24 +0900223 base::Passed(&method_call)));
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900224 }
satorux@chromium.orgc9ebea22011-10-08 01:26:30 +0900225
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
231void ExportedObject::RunMethod(MethodCallCallback method_call_callback,
yuki@chromium.orgd4eedf82013-02-07 18:46:24 +0900232 scoped_ptr<MethodCall> method_call,
satorux@chromium.orgc9ebea22011-10-08 01:26:30 +0900233 base::TimeTicks start_time) {
234 bus_->AssertOnOriginThread();
yuki@chromium.orgd4eedf82013-02-07 18:46:24 +0900235 MethodCall* method = method_call.get();
236 method_call_callback.Run(method,
vlaviano@chromium.org241215d2011-11-30 13:57:42 +0900237 base::Bind(&ExportedObject::SendResponse,
238 this,
239 start_time,
yuki@chromium.orgd4eedf82013-02-07 18:46:24 +0900240 base::Passed(&method_call)));
vlaviano@chromium.org241215d2011-11-30 13:57:42 +0900241}
satorux@chromium.orgc9ebea22011-10-08 01:26:30 +0900242
vlaviano@chromium.org241215d2011-11-30 13:57:42 +0900243void ExportedObject::SendResponse(base::TimeTicks start_time,
yuki@chromium.orgd4eedf82013-02-07 18:46:24 +0900244 scoped_ptr<MethodCall> method_call,
245 scoped_ptr<Response> response) {
vlaviano@chromium.org241215d2011-11-30 13:57:42 +0900246 DCHECK(method_call);
247 if (bus_->HasDBusThread()) {
hashimoto@chromium.org955f6482013-09-26 13:32:29 +0900248 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));
vlaviano@chromium.org241215d2011-11-30 13:57:42 +0900255 } else {
yuki@chromium.orgd4eedf82013-02-07 18:46:24 +0900256 OnMethodCompleted(method_call.Pass(), response.Pass(), start_time);
vlaviano@chromium.org241215d2011-11-30 13:57:42 +0900257 }
satorux@chromium.orgc9ebea22011-10-08 01:26:30 +0900258}
259
yuki@chromium.orgd4eedf82013-02-07 18:46:24 +0900260void ExportedObject::OnMethodCompleted(scoped_ptr<MethodCall> method_call,
261 scoped_ptr<Response> response,
satorux@chromium.orgc9ebea22011-10-08 01:26:30 +0900262 base::TimeTicks start_time) {
263 bus_->AssertOnDBusThread();
satorux@chromium.orgc9ebea22011-10-08 01:26:30 +0900264
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900265 // Record if the method call is successful, or not. 1 if successful.
266 UMA_HISTOGRAM_ENUMERATION("DBus.ExportedMethodHandleSuccess",
267 response ? 1 : 0,
268 kSuccessRatioHistogramMaxValue);
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900269
satorux@chromium.orgc9ebea22011-10-08 01:26:30 +0900270 // 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
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900275 if (!response) {
satorux@chromium.orgc6ac7572011-09-01 03:02:43 +0900276 // Something bad happened in the method call.
thestig@chromium.orgf0b7eac2013-06-13 15:37:19 +0900277 scoped_ptr<ErrorResponse> error_response(
satorux@chromium.orgc9ebea22011-10-08 01:26:30 +0900278 ErrorResponse::FromMethodCall(
yuki@chromium.orgd4eedf82013-02-07 18:46:24 +0900279 method_call.get(),
satorux@chromium.orgc9ebea22011-10-08 01:26:30 +0900280 DBUS_ERROR_FAILED,
281 "error occurred in " + method_call->GetMember()));
282 bus_->Send(error_response->raw_message(), NULL);
283 return;
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900284 }
285
286 // The method call was successful.
satorux@chromium.orgc9ebea22011-10-08 01:26:30 +0900287 bus_->Send(response->raw_message(), NULL);
288
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900289 // 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);
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900292}
293
294void ExportedObject::OnUnregistered(DBusConnection* connection) {
295}
296
297DBusHandlerResult 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
305void 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