blob: 4fdd33abc1e17161726b5ab4e6e9631c21f1468f [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
avi22437c692015-12-22 18:12:457#include <stdint.h>
dchenge48600452015-12-28 02:24:508#include <utility>
avi22437c692015-12-22 18:12:459
[email protected]a51076112011-08-17 20:58:1210#include "base/bind.h"
11#include "base/logging.h"
12#include "base/memory/ref_counted.h"
[email protected]2a9ec0e2013-07-17 23:00:3013#include "base/message_loop/message_loop.h"
asvitkine12d9cda2017-01-19 15:17:3814#include "base/metrics/histogram_macros.h"
[email protected]a51076112011-08-17 20:58:1215#include "base/threading/thread_restrictions.h"
[email protected]b43e5562013-06-28 15:20:0216#include "base/time/time.h"
[email protected]a51076112011-08-17 20:58:1217#include "dbus/bus.h"
18#include "dbus/message.h"
[email protected]216ed0b2012-02-14 21:29:0619#include "dbus/object_path.h"
[email protected]a51076112011-08-17 20:58:1220#include "dbus/scoped_dbus_error.h"
armansitoebff093d2014-09-05 17:49:3421#include "dbus/util.h"
[email protected]a51076112011-08-17 20:58:1222
23namespace dbus {
24
25namespace {
26
[email protected]a73389892011-09-06 20:53:3027// Used for success ratio histograms. 1 for success, 0 for failure.
28const int kSuccessRatioHistogramMaxValue = 2;
29
[email protected]a51076112011-08-17 20:58:1230} // namespace
31
32ExportedObject::ExportedObject(Bus* bus,
[email protected]216ed0b2012-02-14 21:29:0633 const ObjectPath& object_path)
[email protected]a51076112011-08-17 20:58:1234 : bus_(bus),
[email protected]a51076112011-08-17 20:58:1235 object_path_(object_path),
[email protected]e184cce2011-10-07 16:26:3036 object_is_registered_(false) {
Ryo Hashimotoc089a0292017-09-25 07:13:5037 LOG_IF(FATAL, !object_path_.IsValid()) << object_path_.value();
[email protected]a51076112011-08-17 20:58:1238}
39
40ExportedObject::~ExportedObject() {
41 DCHECK(!object_is_registered_);
42}
43
44bool ExportedObject::ExportMethodAndBlock(
45 const std::string& interface_name,
46 const std::string& method_name,
47 MethodCallCallback method_call_callback) {
48 bus_->AssertOnDBusThread();
49
[email protected]3beaaa4e2011-08-23 07:29:2150 // Check if the method is already exported.
51 const std::string absolute_method_name =
armansitoebff093d2014-09-05 17:49:3452 GetAbsoluteMemberName(interface_name, method_name);
[email protected]3beaaa4e2011-08-23 07:29:2153 if (method_table_.find(absolute_method_name) != method_table_.end()) {
54 LOG(ERROR) << absolute_method_name << " is already exported";
55 return false;
56 }
57
[email protected]a51076112011-08-17 20:58:1258 if (!bus_->Connect())
59 return false;
60 if (!bus_->SetUpAsyncOperations())
61 return false;
[email protected]a51076112011-08-17 20:58:1262 if (!Register())
63 return false;
64
[email protected]3beaaa4e2011-08-23 07:29:2165 // Add the method callback to the method table.
[email protected]a51076112011-08-17 20:58:1266 method_table_[absolute_method_name] = method_call_callback;
67
68 return true;
69}
70
71void ExportedObject::ExportMethod(const std::string& interface_name,
72 const std::string& method_name,
73 MethodCallCallback method_call_callback,
74 OnExportedCallback on_exported_calback) {
75 bus_->AssertOnOriginThread();
76
77 base::Closure task = base::Bind(&ExportedObject::ExportMethodInternal,
78 this,
79 interface_name,
80 method_name,
81 method_call_callback,
82 on_exported_calback);
[email protected]8609be262013-09-26 04:32:2983 bus_->GetDBusTaskRunner()->PostTask(FROM_HERE, task);
[email protected]a51076112011-08-17 20:58:1284}
85
[email protected]3beaaa4e2011-08-23 07:29:2186void ExportedObject::SendSignal(Signal* signal) {
87 // For signals, the object path should be set to the path to the sender
88 // object, which is this exported object here.
[email protected]ca72ff22012-05-23 06:55:2289 CHECK(signal->SetPath(object_path_));
[email protected]3beaaa4e2011-08-23 07:29:2190
91 // Increment the reference count so we can safely reference the
92 // underlying signal message until the signal sending is complete. This
93 // will be unref'ed in SendSignalInternal().
94 DBusMessage* signal_message = signal->raw_message();
95 dbus_message_ref(signal_message);
96
[email protected]a73389892011-09-06 20:53:3097 const base::TimeTicks start_time = base::TimeTicks::Now();
peary26739a6932017-05-19 00:38:5298 if (bus_->GetDBusTaskRunner()->RunsTasksInCurrentSequence()) {
chirantan722c9772015-04-09 19:36:0199 // The Chrome OS power manager doesn't use a dedicated TaskRunner for
100 // sending DBus messages. Sending signals asynchronously can cause an
101 // inversion in the message order if the power manager calls
102 // ObjectProxy::CallMethodAndBlock() before going back to the top level of
103 // the MessageLoop: crbug.com/472361.
104 SendSignalInternal(start_time, signal_message);
105 } else {
106 bus_->GetDBusTaskRunner()->PostTask(
107 FROM_HERE,
108 base::Bind(&ExportedObject::SendSignalInternal,
109 this,
110 start_time,
111 signal_message));
112 }
[email protected]3beaaa4e2011-08-23 07:29:21113}
114
[email protected]a51076112011-08-17 20:58:12115void ExportedObject::Unregister() {
116 bus_->AssertOnDBusThread();
117
118 if (!object_is_registered_)
119 return;
120
121 bus_->UnregisterObjectPath(object_path_);
122 object_is_registered_ = false;
123}
124
125void ExportedObject::ExportMethodInternal(
126 const std::string& interface_name,
127 const std::string& method_name,
128 MethodCallCallback method_call_callback,
129 OnExportedCallback on_exported_calback) {
130 bus_->AssertOnDBusThread();
131
132 const bool success = ExportMethodAndBlock(interface_name,
133 method_name,
134 method_call_callback);
[email protected]8609be262013-09-26 04:32:29135 bus_->GetOriginTaskRunner()->PostTask(FROM_HERE,
136 base::Bind(&ExportedObject::OnExported,
137 this,
138 on_exported_calback,
139 interface_name,
140 method_name,
141 success));
[email protected]a51076112011-08-17 20:58:12142}
143
144void ExportedObject::OnExported(OnExportedCallback on_exported_callback,
145 const std::string& interface_name,
146 const std::string& method_name,
147 bool success) {
148 bus_->AssertOnOriginThread();
149
150 on_exported_callback.Run(interface_name, method_name, success);
151}
152
[email protected]a73389892011-09-06 20:53:30153void ExportedObject::SendSignalInternal(base::TimeTicks start_time,
[email protected]1d887b272011-10-04 13:47:21154 DBusMessage* signal_message) {
avi22437c692015-12-22 18:12:45155 uint32_t serial = 0;
[email protected]3beaaa4e2011-08-23 07:29:21156 bus_->Send(signal_message, &serial);
157 dbus_message_unref(signal_message);
[email protected]a73389892011-09-06 20:53:30158 // Record time spent to send the the signal. This is not accurate as the
159 // signal will actually be sent from the next run of the message loop,
160 // but we can at least tell the number of signals sent.
161 UMA_HISTOGRAM_TIMES("DBus.SignalSendTime",
162 base::TimeTicks::Now() - start_time);
[email protected]3beaaa4e2011-08-23 07:29:21163}
164
[email protected]a51076112011-08-17 20:58:12165bool ExportedObject::Register() {
166 bus_->AssertOnDBusThread();
167
168 if (object_is_registered_)
169 return true;
170
171 ScopedDBusError error;
172
173 DBusObjectPathVTable vtable = {};
174 vtable.message_function = &ExportedObject::HandleMessageThunk;
175 vtable.unregister_function = &ExportedObject::OnUnregisteredThunk;
176 const bool success = bus_->TryRegisterObjectPath(object_path_,
177 &vtable,
178 this,
179 error.get());
180 if (!success) {
[email protected]216ed0b2012-02-14 21:29:06181 LOG(ERROR) << "Failed to register the object: " << object_path_.value()
182 << ": " << (error.is_set() ? error.message() : "");
[email protected]a51076112011-08-17 20:58:12183 return false;
184 }
185
186 object_is_registered_ = true;
187 return true;
188}
189
190DBusHandlerResult ExportedObject::HandleMessage(
191 DBusConnection* connection,
192 DBusMessage* raw_message) {
193 bus_->AssertOnDBusThread();
194 DCHECK_EQ(DBUS_MESSAGE_TYPE_METHOD_CALL, dbus_message_get_type(raw_message));
195
196 // raw_message will be unrefed on exit of the function. Increment the
197 // reference so we can use it in MethodCall.
198 dbus_message_ref(raw_message);
dcheng2a193282016-04-08 22:55:04199 std::unique_ptr<MethodCall> method_call(
[email protected]a51076112011-08-17 20:58:12200 MethodCall::FromRawMessage(raw_message));
201 const std::string interface = method_call->GetInterface();
202 const std::string member = method_call->GetMember();
203
204 if (interface.empty()) {
205 // We don't support method calls without interface.
[email protected]a73389892011-09-06 20:53:30206 LOG(WARNING) << "Interface is missing: " << method_call->ToString();
[email protected]a51076112011-08-17 20:58:12207 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
208 }
209
210 // Check if we know about the method.
armansitoebff093d2014-09-05 17:49:34211 const std::string absolute_method_name = GetAbsoluteMemberName(
[email protected]a51076112011-08-17 20:58:12212 interface, member);
213 MethodTable::const_iterator iter = method_table_.find(absolute_method_name);
214 if (iter == method_table_.end()) {
215 // Don't know about the method.
[email protected]a73389892011-09-06 20:53:30216 LOG(WARNING) << "Unknown method: " << method_call->ToString();
[email protected]a51076112011-08-17 20:58:12217 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
218 }
219
[email protected]a73389892011-09-06 20:53:30220 const base::TimeTicks start_time = base::TimeTicks::Now();
[email protected]a51076112011-08-17 20:58:12221 if (bus_->HasDBusThread()) {
[email protected]a51076112011-08-17 20:58:12222 // Post a task to run the method in the origin thread.
[email protected]8609be262013-09-26 04:32:29223 bus_->GetOriginTaskRunner()->PostTask(FROM_HERE,
224 base::Bind(&ExportedObject::RunMethod,
225 this,
226 iter->second,
227 base::Passed(&method_call),
228 start_time));
[email protected]a51076112011-08-17 20:58:12229 } else {
[email protected]9aa74cc2011-11-30 04:57:42230 // If the D-Bus thread is not used, just call the method directly.
[email protected]9b25d452013-02-07 09:46:24231 MethodCall* method = method_call.get();
232 iter->second.Run(method,
[email protected]9aa74cc2011-11-30 04:57:42233 base::Bind(&ExportedObject::SendResponse,
234 this,
235 start_time,
[email protected]9b25d452013-02-07 09:46:24236 base::Passed(&method_call)));
[email protected]a51076112011-08-17 20:58:12237 }
[email protected]e184cce2011-10-07 16:26:30238
239 // It's valid to say HANDLED here, and send a method response at a later
240 // time from OnMethodCompleted() asynchronously.
241 return DBUS_HANDLER_RESULT_HANDLED;
242}
243
244void ExportedObject::RunMethod(MethodCallCallback method_call_callback,
dcheng2a193282016-04-08 22:55:04245 std::unique_ptr<MethodCall> method_call,
[email protected]e184cce2011-10-07 16:26:30246 base::TimeTicks start_time) {
247 bus_->AssertOnOriginThread();
[email protected]9b25d452013-02-07 09:46:24248 MethodCall* method = method_call.get();
249 method_call_callback.Run(method,
[email protected]9aa74cc2011-11-30 04:57:42250 base::Bind(&ExportedObject::SendResponse,
251 this,
252 start_time,
[email protected]9b25d452013-02-07 09:46:24253 base::Passed(&method_call)));
[email protected]9aa74cc2011-11-30 04:57:42254}
[email protected]e184cce2011-10-07 16:26:30255
[email protected]9aa74cc2011-11-30 04:57:42256void ExportedObject::SendResponse(base::TimeTicks start_time,
dcheng2a193282016-04-08 22:55:04257 std::unique_ptr<MethodCall> method_call,
258 std::unique_ptr<Response> response) {
[email protected]9aa74cc2011-11-30 04:57:42259 DCHECK(method_call);
260 if (bus_->HasDBusThread()) {
[email protected]8609be262013-09-26 04:32:29261 bus_->GetDBusTaskRunner()->PostTask(
262 FROM_HERE,
263 base::Bind(&ExportedObject::OnMethodCompleted,
264 this,
265 base::Passed(&method_call),
266 base::Passed(&response),
267 start_time));
[email protected]9aa74cc2011-11-30 04:57:42268 } else {
dchenge48600452015-12-28 02:24:50269 OnMethodCompleted(std::move(method_call), std::move(response), start_time);
[email protected]9aa74cc2011-11-30 04:57:42270 }
[email protected]e184cce2011-10-07 16:26:30271}
272
dcheng2a193282016-04-08 22:55:04273void ExportedObject::OnMethodCompleted(std::unique_ptr<MethodCall> method_call,
274 std::unique_ptr<Response> response,
[email protected]e184cce2011-10-07 16:26:30275 base::TimeTicks start_time) {
276 bus_->AssertOnDBusThread();
[email protected]e184cce2011-10-07 16:26:30277
[email protected]a73389892011-09-06 20:53:30278 // Record if the method call is successful, or not. 1 if successful.
279 UMA_HISTOGRAM_ENUMERATION("DBus.ExportedMethodHandleSuccess",
280 response ? 1 : 0,
281 kSuccessRatioHistogramMaxValue);
[email protected]a51076112011-08-17 20:58:12282
[email protected]e184cce2011-10-07 16:26:30283 // Check if the bus is still connected. If the method takes long to
284 // complete, the bus may be shut down meanwhile.
285 if (!bus_->is_connected())
286 return;
287
[email protected]a51076112011-08-17 20:58:12288 if (!response) {
[email protected]829f0e4c2011-08-31 18:02:43289 // Something bad happened in the method call.
dcheng2a193282016-04-08 22:55:04290 std::unique_ptr<ErrorResponse> error_response(ErrorResponse::FromMethodCall(
291 method_call.get(), DBUS_ERROR_FAILED,
292 "error occurred in " + method_call->GetMember()));
Ben Chan14d500372017-11-09 20:20:16293 bus_->Send(error_response->raw_message(), nullptr);
[email protected]e184cce2011-10-07 16:26:30294 return;
[email protected]a51076112011-08-17 20:58:12295 }
296
297 // The method call was successful.
Ben Chan14d500372017-11-09 20:20:16298 bus_->Send(response->raw_message(), nullptr);
[email protected]e184cce2011-10-07 16:26:30299
[email protected]a73389892011-09-06 20:53:30300 // Record time spent to handle the the method call. Don't include failures.
301 UMA_HISTOGRAM_TIMES("DBus.ExportedMethodHandleTime",
302 base::TimeTicks::Now() - start_time);
[email protected]a51076112011-08-17 20:58:12303}
304
305void ExportedObject::OnUnregistered(DBusConnection* connection) {
306}
307
308DBusHandlerResult ExportedObject::HandleMessageThunk(
309 DBusConnection* connection,
310 DBusMessage* raw_message,
311 void* user_data) {
312 ExportedObject* self = reinterpret_cast<ExportedObject*>(user_data);
313 return self->HandleMessage(connection, raw_message);
314}
315
316void ExportedObject::OnUnregisteredThunk(DBusConnection *connection,
317 void* user_data) {
318 ExportedObject* self = reinterpret_cast<ExportedObject*>(user_data);
319 return self->OnUnregistered(connection);
320}
321
322} // namespace dbus