blob: 47c70ffa3ce29787c1279e0c3ba695db61545035 [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
avi0ad0ce02015-12-23 03:12:45 +09007#include <stdint.h>
dcheng6c1ffcf2015-12-28 11:24:50 +09008#include <utility>
avi0ad0ce02015-12-23 03:12:45 +09009
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090010#include "base/bind.h"
11#include "base/logging.h"
12#include "base/memory/ref_counted.h"
avi@chromium.orga29af562013-07-18 08:00:30 +090013#include "base/message_loop/message_loop.h"
asvitkine9481ed82017-01-20 00:17:38 +090014#include "base/metrics/histogram_macros.h"
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090015#include "base/threading/thread_restrictions.h"
avi@chromium.org78a7e7b2013-06-29 00:20:02 +090016#include "base/time/time.h"
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090017#include "dbus/bus.h"
18#include "dbus/message.h"
keybuk@google.combf4649a2012-02-15 06:29:06 +090019#include "dbus/object_path.h"
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090020#include "dbus/scoped_dbus_error.h"
armansitof4364642014-09-06 02:49:34 +090021#include "dbus/util.h"
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090022
23namespace dbus {
24
25namespace {
26
satorux@chromium.org5a92cf32011-09-07 05:53:30 +090027// Used for success ratio histograms. 1 for success, 0 for failure.
28const int kSuccessRatioHistogramMaxValue = 2;
29
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090030} // namespace
31
32ExportedObject::ExportedObject(Bus* bus,
keybuk@google.combf4649a2012-02-15 06:29:06 +090033 const ObjectPath& object_path)
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090034 : bus_(bus),
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090035 object_path_(object_path),
satorux@chromium.orgc9ebea22011-10-08 01:26:30 +090036 object_is_registered_(false) {
Ryo Hashimoto61ab06d2017-09-25 16:13:50 +090037 LOG_IF(FATAL, !object_path_.IsValid()) << object_path_.value();
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090038}
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
satorux@chromium.org7f0c4512011-08-23 16:29:21 +090050 // Check if the method is already exported.
51 const std::string absolute_method_name =
armansitof4364642014-09-06 02:49:34 +090052 GetAbsoluteMemberName(interface_name, method_name);
satorux@chromium.org7f0c4512011-08-23 16:29:21 +090053 if (method_table_.find(absolute_method_name) != method_table_.end()) {
54 LOG(ERROR) << absolute_method_name << " is already exported";
55 return false;
56 }
57
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090058 if (!bus_->Connect())
59 return false;
60 if (!bus_->SetUpAsyncOperations())
61 return false;
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090062 if (!Register())
63 return false;
64
satorux@chromium.org7f0c4512011-08-23 16:29:21 +090065 // Add the method callback to the method table.
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090066 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);
hashimoto@chromium.org955f6482013-09-26 13:32:29 +090083 bus_->GetDBusTaskRunner()->PostTask(FROM_HERE, task);
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090084}
85
satorux@chromium.org7f0c4512011-08-23 16:29:21 +090086void 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.
hashimoto@chromium.orgb0305512012-05-23 15:55:22 +090089 CHECK(signal->SetPath(object_path_));
satorux@chromium.org7f0c4512011-08-23 16:29:21 +090090
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
satorux@chromium.org5a92cf32011-09-07 05:53:30 +090097 const base::TimeTicks start_time = base::TimeTicks::Now();
peary20791de92017-05-19 09:38:52 +090098 if (bus_->GetDBusTaskRunner()->RunsTasksInCurrentSequence()) {
chirantand796bf72015-04-10 04:36:01 +090099 // 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 }
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900113}
114
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900115void 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);
hashimoto@chromium.org955f6482013-09-26 13:32:29 +0900135 bus_->GetOriginTaskRunner()->PostTask(FROM_HERE,
136 base::Bind(&ExportedObject::OnExported,
137 this,
138 on_exported_calback,
139 interface_name,
140 method_name,
141 success));
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900142}
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
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900153void ExportedObject::SendSignalInternal(base::TimeTicks start_time,
satorux@chromium.org47d706b2011-10-04 22:47:21 +0900154 DBusMessage* signal_message) {
avi0ad0ce02015-12-23 03:12:45 +0900155 uint32_t serial = 0;
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900156 bus_->Send(signal_message, &serial);
157 dbus_message_unref(signal_message);
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900158 // 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);
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900163}
164
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900165bool 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) {
keybuk@google.combf4649a2012-02-15 06:29:06 +0900181 LOG(ERROR) << "Failed to register the object: " << object_path_.value()
182 << ": " << (error.is_set() ? error.message() : "");
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900183 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();
Sonny Sasaka28c6d5a2018-04-26 02:53:37 +0900194 // ExportedObject only handles method calls. Ignore other message types (e.g.
195 // signal).
196 if (dbus_message_get_type(raw_message) != DBUS_MESSAGE_TYPE_METHOD_CALL)
197 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900198
199 // raw_message will be unrefed on exit of the function. Increment the
200 // reference so we can use it in MethodCall.
201 dbus_message_ref(raw_message);
dcheng30c5a172016-04-09 07:55:04 +0900202 std::unique_ptr<MethodCall> method_call(
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900203 MethodCall::FromRawMessage(raw_message));
204 const std::string interface = method_call->GetInterface();
205 const std::string member = method_call->GetMember();
206
207 if (interface.empty()) {
208 // We don't support method calls without interface.
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900209 LOG(WARNING) << "Interface is missing: " << method_call->ToString();
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900210 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
211 }
212
213 // Check if we know about the method.
armansitof4364642014-09-06 02:49:34 +0900214 const std::string absolute_method_name = GetAbsoluteMemberName(
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900215 interface, member);
216 MethodTable::const_iterator iter = method_table_.find(absolute_method_name);
217 if (iter == method_table_.end()) {
218 // Don't know about the method.
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900219 LOG(WARNING) << "Unknown method: " << method_call->ToString();
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900220 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
221 }
222
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900223 const base::TimeTicks start_time = base::TimeTicks::Now();
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900224 if (bus_->HasDBusThread()) {
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900225 // Post a task to run the method in the origin thread.
Claudio DeSouza1181b422018-02-22 02:27:16 +0900226 bus_->GetOriginTaskRunner()->PostTask(
227 FROM_HERE,
228 base::BindOnce(&ExportedObject::RunMethod, this, iter->second,
229 std::move(method_call), start_time));
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900230 } else {
vlaviano@chromium.org241215d2011-11-30 13:57:42 +0900231 // If the D-Bus thread is not used, just call the method directly.
yuki@chromium.orgd4eedf82013-02-07 18:46:24 +0900232 MethodCall* method = method_call.get();
233 iter->second.Run(method,
vlaviano@chromium.org241215d2011-11-30 13:57:42 +0900234 base::Bind(&ExportedObject::SendResponse,
235 this,
236 start_time,
yuki@chromium.orgd4eedf82013-02-07 18:46:24 +0900237 base::Passed(&method_call)));
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900238 }
satorux@chromium.orgc9ebea22011-10-08 01:26:30 +0900239
240 // It's valid to say HANDLED here, and send a method response at a later
241 // time from OnMethodCompleted() asynchronously.
242 return DBUS_HANDLER_RESULT_HANDLED;
243}
244
245void ExportedObject::RunMethod(MethodCallCallback method_call_callback,
dcheng30c5a172016-04-09 07:55:04 +0900246 std::unique_ptr<MethodCall> method_call,
satorux@chromium.orgc9ebea22011-10-08 01:26:30 +0900247 base::TimeTicks start_time) {
248 bus_->AssertOnOriginThread();
yuki@chromium.orgd4eedf82013-02-07 18:46:24 +0900249 MethodCall* method = method_call.get();
250 method_call_callback.Run(method,
vlaviano@chromium.org241215d2011-11-30 13:57:42 +0900251 base::Bind(&ExportedObject::SendResponse,
252 this,
253 start_time,
yuki@chromium.orgd4eedf82013-02-07 18:46:24 +0900254 base::Passed(&method_call)));
vlaviano@chromium.org241215d2011-11-30 13:57:42 +0900255}
satorux@chromium.orgc9ebea22011-10-08 01:26:30 +0900256
vlaviano@chromium.org241215d2011-11-30 13:57:42 +0900257void ExportedObject::SendResponse(base::TimeTicks start_time,
dcheng30c5a172016-04-09 07:55:04 +0900258 std::unique_ptr<MethodCall> method_call,
259 std::unique_ptr<Response> response) {
vlaviano@chromium.org241215d2011-11-30 13:57:42 +0900260 DCHECK(method_call);
261 if (bus_->HasDBusThread()) {
hashimoto@chromium.org955f6482013-09-26 13:32:29 +0900262 bus_->GetDBusTaskRunner()->PostTask(
Claudio DeSouza1181b422018-02-22 02:27:16 +0900263 FROM_HERE, base::BindOnce(&ExportedObject::OnMethodCompleted, this,
264 std::move(method_call), std::move(response),
265 start_time));
vlaviano@chromium.org241215d2011-11-30 13:57:42 +0900266 } else {
dcheng6c1ffcf2015-12-28 11:24:50 +0900267 OnMethodCompleted(std::move(method_call), std::move(response), start_time);
vlaviano@chromium.org241215d2011-11-30 13:57:42 +0900268 }
satorux@chromium.orgc9ebea22011-10-08 01:26:30 +0900269}
270
dcheng30c5a172016-04-09 07:55:04 +0900271void ExportedObject::OnMethodCompleted(std::unique_ptr<MethodCall> method_call,
272 std::unique_ptr<Response> response,
satorux@chromium.orgc9ebea22011-10-08 01:26:30 +0900273 base::TimeTicks start_time) {
274 bus_->AssertOnDBusThread();
satorux@chromium.orgc9ebea22011-10-08 01:26:30 +0900275
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900276 // Record if the method call is successful, or not. 1 if successful.
277 UMA_HISTOGRAM_ENUMERATION("DBus.ExportedMethodHandleSuccess",
278 response ? 1 : 0,
279 kSuccessRatioHistogramMaxValue);
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900280
satorux@chromium.orgc9ebea22011-10-08 01:26:30 +0900281 // Check if the bus is still connected. If the method takes long to
282 // complete, the bus may be shut down meanwhile.
283 if (!bus_->is_connected())
284 return;
285
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900286 if (!response) {
satorux@chromium.orgc6ac7572011-09-01 03:02:43 +0900287 // Something bad happened in the method call.
dcheng30c5a172016-04-09 07:55:04 +0900288 std::unique_ptr<ErrorResponse> error_response(ErrorResponse::FromMethodCall(
289 method_call.get(), DBUS_ERROR_FAILED,
290 "error occurred in " + method_call->GetMember()));
Ben Chan333256c2017-11-10 05:20:16 +0900291 bus_->Send(error_response->raw_message(), nullptr);
satorux@chromium.orgc9ebea22011-10-08 01:26:30 +0900292 return;
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900293 }
294
295 // The method call was successful.
Ben Chan333256c2017-11-10 05:20:16 +0900296 bus_->Send(response->raw_message(), nullptr);
satorux@chromium.orgc9ebea22011-10-08 01:26:30 +0900297
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900298 // 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);
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900301}
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