blob: 669b871e35b227ff4c4e5a22648d5ee02d05772e [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"
armansitoebff093d2014-09-05 17:49:3418#include "dbus/util.h"
[email protected]a51076112011-08-17 20:58:1219
20namespace dbus {
21
22namespace {
23
[email protected]a73389892011-09-06 20:53:3024// Used for success ratio histograms. 1 for success, 0 for failure.
25const int kSuccessRatioHistogramMaxValue = 2;
26
[email protected]a51076112011-08-17 20:58:1227} // namespace
28
29ExportedObject::ExportedObject(Bus* bus,
[email protected]216ed0b2012-02-14 21:29:0630 const ObjectPath& object_path)
[email protected]a51076112011-08-17 20:58:1231 : bus_(bus),
[email protected]a51076112011-08-17 20:58:1232 object_path_(object_path),
[email protected]e184cce2011-10-07 16:26:3033 object_is_registered_(false) {
[email protected]a51076112011-08-17 20:58:1234}
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
[email protected]3beaaa4e2011-08-23 07:29:2146 // Check if the method is already exported.
47 const std::string absolute_method_name =
armansitoebff093d2014-09-05 17:49:3448 GetAbsoluteMemberName(interface_name, method_name);
[email protected]3beaaa4e2011-08-23 07:29:2149 if (method_table_.find(absolute_method_name) != method_table_.end()) {
50 LOG(ERROR) << absolute_method_name << " is already exported";
51 return false;
52 }
53
[email protected]a51076112011-08-17 20:58:1254 if (!bus_->Connect())
55 return false;
56 if (!bus_->SetUpAsyncOperations())
57 return false;
[email protected]a51076112011-08-17 20:58:1258 if (!Register())
59 return false;
60
[email protected]3beaaa4e2011-08-23 07:29:2161 // Add the method callback to the method table.
[email protected]a51076112011-08-17 20:58:1262 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);
[email protected]8609be262013-09-26 04:32:2979 bus_->GetDBusTaskRunner()->PostTask(FROM_HERE, task);
[email protected]a51076112011-08-17 20:58:1280}
81
[email protected]3beaaa4e2011-08-23 07:29:2182void 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.
[email protected]ca72ff22012-05-23 06:55:2285 CHECK(signal->SetPath(object_path_));
[email protected]3beaaa4e2011-08-23 07:29:2186
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
[email protected]a73389892011-09-06 20:53:3093 const base::TimeTicks start_time = base::TimeTicks::Now();
chirantan722c9772015-04-09 19:36:0194 if (bus_->GetDBusTaskRunner()->RunsTasksOnCurrentThread()) {
95 // The Chrome OS power manager doesn't use a dedicated TaskRunner for
96 // sending DBus messages. Sending signals asynchronously can cause an
97 // inversion in the message order if the power manager calls
98 // ObjectProxy::CallMethodAndBlock() before going back to the top level of
99 // the MessageLoop: crbug.com/472361.
100 SendSignalInternal(start_time, signal_message);
101 } else {
102 bus_->GetDBusTaskRunner()->PostTask(
103 FROM_HERE,
104 base::Bind(&ExportedObject::SendSignalInternal,
105 this,
106 start_time,
107 signal_message));
108 }
[email protected]3beaaa4e2011-08-23 07:29:21109}
110
[email protected]a51076112011-08-17 20:58:12111void ExportedObject::Unregister() {
112 bus_->AssertOnDBusThread();
113
114 if (!object_is_registered_)
115 return;
116
117 bus_->UnregisterObjectPath(object_path_);
118 object_is_registered_ = false;
119}
120
121void ExportedObject::ExportMethodInternal(
122 const std::string& interface_name,
123 const std::string& method_name,
124 MethodCallCallback method_call_callback,
125 OnExportedCallback on_exported_calback) {
126 bus_->AssertOnDBusThread();
127
128 const bool success = ExportMethodAndBlock(interface_name,
129 method_name,
130 method_call_callback);
[email protected]8609be262013-09-26 04:32:29131 bus_->GetOriginTaskRunner()->PostTask(FROM_HERE,
132 base::Bind(&ExportedObject::OnExported,
133 this,
134 on_exported_calback,
135 interface_name,
136 method_name,
137 success));
[email protected]a51076112011-08-17 20:58:12138}
139
140void ExportedObject::OnExported(OnExportedCallback on_exported_callback,
141 const std::string& interface_name,
142 const std::string& method_name,
143 bool success) {
144 bus_->AssertOnOriginThread();
145
146 on_exported_callback.Run(interface_name, method_name, success);
147}
148
[email protected]a73389892011-09-06 20:53:30149void ExportedObject::SendSignalInternal(base::TimeTicks start_time,
[email protected]1d887b272011-10-04 13:47:21150 DBusMessage* signal_message) {
[email protected]3beaaa4e2011-08-23 07:29:21151 uint32 serial = 0;
152 bus_->Send(signal_message, &serial);
153 dbus_message_unref(signal_message);
[email protected]a73389892011-09-06 20:53:30154 // Record time spent to send the the signal. This is not accurate as the
155 // signal will actually be sent from the next run of the message loop,
156 // but we can at least tell the number of signals sent.
157 UMA_HISTOGRAM_TIMES("DBus.SignalSendTime",
158 base::TimeTicks::Now() - start_time);
[email protected]3beaaa4e2011-08-23 07:29:21159}
160
[email protected]a51076112011-08-17 20:58:12161bool ExportedObject::Register() {
162 bus_->AssertOnDBusThread();
163
164 if (object_is_registered_)
165 return true;
166
167 ScopedDBusError error;
168
169 DBusObjectPathVTable vtable = {};
170 vtable.message_function = &ExportedObject::HandleMessageThunk;
171 vtable.unregister_function = &ExportedObject::OnUnregisteredThunk;
172 const bool success = bus_->TryRegisterObjectPath(object_path_,
173 &vtable,
174 this,
175 error.get());
176 if (!success) {
[email protected]216ed0b2012-02-14 21:29:06177 LOG(ERROR) << "Failed to register the object: " << object_path_.value()
178 << ": " << (error.is_set() ? error.message() : "");
[email protected]a51076112011-08-17 20:58:12179 return false;
180 }
181
182 object_is_registered_ = true;
183 return true;
184}
185
186DBusHandlerResult ExportedObject::HandleMessage(
187 DBusConnection* connection,
188 DBusMessage* raw_message) {
189 bus_->AssertOnDBusThread();
190 DCHECK_EQ(DBUS_MESSAGE_TYPE_METHOD_CALL, dbus_message_get_type(raw_message));
191
192 // raw_message will be unrefed on exit of the function. Increment the
193 // reference so we can use it in MethodCall.
194 dbus_message_ref(raw_message);
195 scoped_ptr<MethodCall> method_call(
196 MethodCall::FromRawMessage(raw_message));
197 const std::string interface = method_call->GetInterface();
198 const std::string member = method_call->GetMember();
199
200 if (interface.empty()) {
201 // We don't support method calls without interface.
[email protected]a73389892011-09-06 20:53:30202 LOG(WARNING) << "Interface is missing: " << method_call->ToString();
[email protected]a51076112011-08-17 20:58:12203 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
204 }
205
206 // Check if we know about the method.
armansitoebff093d2014-09-05 17:49:34207 const std::string absolute_method_name = GetAbsoluteMemberName(
[email protected]a51076112011-08-17 20:58:12208 interface, member);
209 MethodTable::const_iterator iter = method_table_.find(absolute_method_name);
210 if (iter == method_table_.end()) {
211 // Don't know about the method.
[email protected]a73389892011-09-06 20:53:30212 LOG(WARNING) << "Unknown method: " << method_call->ToString();
[email protected]a51076112011-08-17 20:58:12213 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
214 }
215
[email protected]a73389892011-09-06 20:53:30216 const base::TimeTicks start_time = base::TimeTicks::Now();
[email protected]a51076112011-08-17 20:58:12217 if (bus_->HasDBusThread()) {
[email protected]a51076112011-08-17 20:58:12218 // Post a task to run the method in the origin thread.
[email protected]8609be262013-09-26 04:32:29219 bus_->GetOriginTaskRunner()->PostTask(FROM_HERE,
220 base::Bind(&ExportedObject::RunMethod,
221 this,
222 iter->second,
223 base::Passed(&method_call),
224 start_time));
[email protected]a51076112011-08-17 20:58:12225 } else {
[email protected]9aa74cc2011-11-30 04:57:42226 // If the D-Bus thread is not used, just call the method directly.
[email protected]9b25d452013-02-07 09:46:24227 MethodCall* method = method_call.get();
228 iter->second.Run(method,
[email protected]9aa74cc2011-11-30 04:57:42229 base::Bind(&ExportedObject::SendResponse,
230 this,
231 start_time,
[email protected]9b25d452013-02-07 09:46:24232 base::Passed(&method_call)));
[email protected]a51076112011-08-17 20:58:12233 }
[email protected]e184cce2011-10-07 16:26:30234
235 // It's valid to say HANDLED here, and send a method response at a later
236 // time from OnMethodCompleted() asynchronously.
237 return DBUS_HANDLER_RESULT_HANDLED;
238}
239
240void ExportedObject::RunMethod(MethodCallCallback method_call_callback,
[email protected]9b25d452013-02-07 09:46:24241 scoped_ptr<MethodCall> method_call,
[email protected]e184cce2011-10-07 16:26:30242 base::TimeTicks start_time) {
243 bus_->AssertOnOriginThread();
[email protected]9b25d452013-02-07 09:46:24244 MethodCall* method = method_call.get();
245 method_call_callback.Run(method,
[email protected]9aa74cc2011-11-30 04:57:42246 base::Bind(&ExportedObject::SendResponse,
247 this,
248 start_time,
[email protected]9b25d452013-02-07 09:46:24249 base::Passed(&method_call)));
[email protected]9aa74cc2011-11-30 04:57:42250}
[email protected]e184cce2011-10-07 16:26:30251
[email protected]9aa74cc2011-11-30 04:57:42252void ExportedObject::SendResponse(base::TimeTicks start_time,
[email protected]9b25d452013-02-07 09:46:24253 scoped_ptr<MethodCall> method_call,
254 scoped_ptr<Response> response) {
[email protected]9aa74cc2011-11-30 04:57:42255 DCHECK(method_call);
256 if (bus_->HasDBusThread()) {
[email protected]8609be262013-09-26 04:32:29257 bus_->GetDBusTaskRunner()->PostTask(
258 FROM_HERE,
259 base::Bind(&ExportedObject::OnMethodCompleted,
260 this,
261 base::Passed(&method_call),
262 base::Passed(&response),
263 start_time));
[email protected]9aa74cc2011-11-30 04:57:42264 } else {
[email protected]9b25d452013-02-07 09:46:24265 OnMethodCompleted(method_call.Pass(), response.Pass(), start_time);
[email protected]9aa74cc2011-11-30 04:57:42266 }
[email protected]e184cce2011-10-07 16:26:30267}
268
[email protected]9b25d452013-02-07 09:46:24269void ExportedObject::OnMethodCompleted(scoped_ptr<MethodCall> method_call,
270 scoped_ptr<Response> response,
[email protected]e184cce2011-10-07 16:26:30271 base::TimeTicks start_time) {
272 bus_->AssertOnDBusThread();
[email protected]e184cce2011-10-07 16:26:30273
[email protected]a73389892011-09-06 20:53:30274 // Record if the method call is successful, or not. 1 if successful.
275 UMA_HISTOGRAM_ENUMERATION("DBus.ExportedMethodHandleSuccess",
276 response ? 1 : 0,
277 kSuccessRatioHistogramMaxValue);
[email protected]a51076112011-08-17 20:58:12278
[email protected]e184cce2011-10-07 16:26:30279 // Check if the bus is still connected. If the method takes long to
280 // complete, the bus may be shut down meanwhile.
281 if (!bus_->is_connected())
282 return;
283
[email protected]a51076112011-08-17 20:58:12284 if (!response) {
[email protected]829f0e4c2011-08-31 18:02:43285 // Something bad happened in the method call.
[email protected]2a57ca642013-06-13 06:37:19286 scoped_ptr<ErrorResponse> error_response(
[email protected]e184cce2011-10-07 16:26:30287 ErrorResponse::FromMethodCall(
[email protected]9b25d452013-02-07 09:46:24288 method_call.get(),
[email protected]e184cce2011-10-07 16:26:30289 DBUS_ERROR_FAILED,
290 "error occurred in " + method_call->GetMember()));
291 bus_->Send(error_response->raw_message(), NULL);
292 return;
[email protected]a51076112011-08-17 20:58:12293 }
294
295 // The method call was successful.
[email protected]e184cce2011-10-07 16:26:30296 bus_->Send(response->raw_message(), NULL);
297
[email protected]a73389892011-09-06 20:53:30298 // Record time spent to handle the the method call. Don't include failures.
299 UMA_HISTOGRAM_TIMES("DBus.ExportedMethodHandleTime",
300 base::TimeTicks::Now() - start_time);
[email protected]a51076112011-08-17 20:58:12301}
302
303void ExportedObject::OnUnregistered(DBusConnection* connection) {
304}
305
306DBusHandlerResult ExportedObject::HandleMessageThunk(
307 DBusConnection* connection,
308 DBusMessage* raw_message,
309 void* user_data) {
310 ExportedObject* self = reinterpret_cast<ExportedObject*>(user_data);
311 return self->HandleMessage(connection, raw_message);
312}
313
314void ExportedObject::OnUnregisteredThunk(DBusConnection *connection,
315 void* user_data) {
316 ExportedObject* self = reinterpret_cast<ExportedObject*>(user_data);
317 return self->OnUnregistered(connection);
318}
319
320} // namespace dbus