1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "dbus/exported_object.h"
10 #include "base/bind.h"
11 #include "base/logging.h"
12 #include "base/memory/ref_counted.h"
13 #include "base/metrics/histogram_macros.h"
14 #include "base/task/sequenced_task_runner.h"
15 #include "base/task/task_runner.h"
16 #include "base/threading/thread_restrictions.h"
17 #include "base/time/time.h"
19 #include "dbus/message.h"
20 #include "dbus/object_path.h"
21 #include "dbus/scoped_dbus_error.h"
22 #include "dbus/util.h"
28 // Used for success ratio histograms. 1 for success, 0 for failure.
29 const int kSuccessRatioHistogramMaxValue = 2;
33 ExportedObject::ExportedObject(Bus* bus,
34 const ObjectPath& object_path)
36 object_path_(object_path),
37 object_is_registered_(false) {
38 LOG_IF(FATAL, !object_path_.IsValid()) << object_path_.value();
41 ExportedObject::~ExportedObject() {
42 DCHECK(!object_is_registered_);
45 bool ExportedObject::ExportMethodAndBlock(
46 const std::string& interface_name,
47 const std::string& method_name,
48 const MethodCallCallback& method_call_callback) {
49 bus_->AssertOnDBusThread();
51 // Check if the method is already exported.
52 const std::string absolute_method_name =
53 GetAbsoluteMemberName(interface_name, method_name);
54 if (method_table_.find(absolute_method_name) != method_table_.end()) {
55 LOG(ERROR) << absolute_method_name << " is already exported";
61 if (!bus_->SetUpAsyncOperations())
66 // Add the method callback to the method table.
67 method_table_[absolute_method_name] = method_call_callback;
72 bool ExportedObject::UnexportMethodAndBlock(const std::string& interface_name,
73 const std::string& method_name) {
74 bus_->AssertOnDBusThread();
76 const std::string absolute_method_name =
77 GetAbsoluteMemberName(interface_name, method_name);
78 MethodTable::const_iterator iter = method_table_.find(absolute_method_name);
79 if (iter == method_table_.end()) {
80 LOG(ERROR) << absolute_method_name << " is not exported";
84 method_table_.erase(iter);
89 void ExportedObject::ExportMethod(
90 const std::string& interface_name,
91 const std::string& method_name,
92 const MethodCallCallback& method_call_callback,
93 OnExportedCallback on_exported_callback) {
94 bus_->AssertOnOriginThread();
96 base::OnceClosure task = base::BindOnce(
97 &ExportedObject::ExportMethodInternal, this, interface_name, method_name,
98 method_call_callback, std::move(on_exported_callback));
99 bus_->GetDBusTaskRunner()->PostTask(FROM_HERE, std::move(task));
102 void ExportedObject::UnexportMethod(
103 const std::string& interface_name,
104 const std::string& method_name,
105 OnUnexportedCallback on_unexported_callback) {
106 bus_->AssertOnOriginThread();
108 base::OnceClosure task = base::BindOnce(
109 &ExportedObject::UnexportMethodInternal, this, interface_name,
110 method_name, std::move(on_unexported_callback));
111 bus_->GetDBusTaskRunner()->PostTask(FROM_HERE, std::move(task));
114 void ExportedObject::SendSignal(Signal* signal) {
115 // For signals, the object path should be set to the path to the sender
116 // object, which is this exported object here.
117 CHECK(signal->SetPath(object_path_));
119 // Increment the reference count so we can safely reference the
120 // underlying signal message until the signal sending is complete. This
121 // will be unref'ed in SendSignalInternal().
122 DBusMessage* signal_message = signal->raw_message();
123 dbus_message_ref(signal_message);
125 const base::TimeTicks start_time = base::TimeTicks::Now();
126 if (bus_->GetDBusTaskRunner()->RunsTasksInCurrentSequence()) {
127 // The Chrome OS power manager doesn't use a dedicated TaskRunner for
128 // sending DBus messages. Sending signals asynchronously can cause an
129 // inversion in the message order if the power manager calls
130 // ObjectProxy::CallMethodAndBlock() before going back to the top level of
131 // the MessageLoop: crbug.com/472361.
132 SendSignalInternal(start_time, signal_message);
134 bus_->GetDBusTaskRunner()->PostTask(
135 FROM_HERE, base::BindOnce(&ExportedObject::SendSignalInternal, this,
136 start_time, signal_message));
140 void ExportedObject::Unregister() {
141 bus_->AssertOnDBusThread();
143 if (!object_is_registered_)
146 bus_->UnregisterObjectPath(object_path_);
147 object_is_registered_ = false;
150 void ExportedObject::ExportMethodInternal(
151 const std::string& interface_name,
152 const std::string& method_name,
153 const MethodCallCallback& method_call_callback,
154 OnExportedCallback on_exported_callback) {
155 bus_->AssertOnDBusThread();
157 const bool success = ExportMethodAndBlock(interface_name,
159 method_call_callback);
160 bus_->GetOriginTaskRunner()->PostTask(
161 FROM_HERE, base::BindOnce(&ExportedObject::OnExported, this,
162 std::move(on_exported_callback), interface_name,
163 method_name, success));
166 void ExportedObject::UnexportMethodInternal(
167 const std::string& interface_name,
168 const std::string& method_name,
169 OnUnexportedCallback on_unexported_callback) {
170 bus_->AssertOnDBusThread();
172 const bool success = UnexportMethodAndBlock(interface_name, method_name);
173 bus_->GetOriginTaskRunner()->PostTask(
174 FROM_HERE, base::BindOnce(&ExportedObject::OnUnexported, this,
175 std::move(on_unexported_callback),
176 interface_name, method_name, success));
179 void ExportedObject::OnExported(OnExportedCallback on_exported_callback,
180 const std::string& interface_name,
181 const std::string& method_name,
183 bus_->AssertOnOriginThread();
185 std::move(on_exported_callback).Run(interface_name, method_name, success);
188 void ExportedObject::OnUnexported(OnExportedCallback on_unexported_callback,
189 const std::string& interface_name,
190 const std::string& method_name,
192 bus_->AssertOnOriginThread();
194 std::move(on_unexported_callback).Run(interface_name, method_name, success);
197 void ExportedObject::SendSignalInternal(base::TimeTicks start_time,
198 DBusMessage* signal_message) {
200 bus_->Send(signal_message, &serial);
201 dbus_message_unref(signal_message);
202 // Record time spent to send the the signal. This is not accurate as the
203 // signal will actually be sent from the next run of the message loop,
204 // but we can at least tell the number of signals sent.
205 UMA_HISTOGRAM_TIMES("DBus.SignalSendTime",
206 base::TimeTicks::Now() - start_time);
209 bool ExportedObject::Register() {
210 bus_->AssertOnDBusThread();
212 if (object_is_registered_)
215 ScopedDBusError error;
217 DBusObjectPathVTable vtable = {};
218 vtable.message_function = &ExportedObject::HandleMessageThunk;
219 vtable.unregister_function = &ExportedObject::OnUnregisteredThunk;
220 const bool success = bus_->TryRegisterObjectPath(object_path_,
225 LOG(ERROR) << "Failed to register the object: " << object_path_.value()
226 << ": " << (error.is_set() ? error.message() : "");
230 object_is_registered_ = true;
234 DBusHandlerResult ExportedObject::HandleMessage(
235 DBusConnection* connection,
236 DBusMessage* raw_message) {
237 bus_->AssertOnDBusThread();
238 // ExportedObject only handles method calls. Ignore other message types (e.g.
240 if (dbus_message_get_type(raw_message) != DBUS_MESSAGE_TYPE_METHOD_CALL)
241 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
243 // raw_message will be unrefed on exit of the function. Increment the
244 // reference so we can use it in MethodCall.
245 dbus_message_ref(raw_message);
246 std::unique_ptr<MethodCall> method_call(
247 MethodCall::FromRawMessage(raw_message));
248 const std::string interface = method_call->GetInterface();
249 const std::string member = method_call->GetMember();
251 if (interface.empty()) {
252 // We don't support method calls without interface.
253 LOG(WARNING) << "Interface is missing: " << method_call->ToString();
254 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
257 // Check if we know about the method.
258 const std::string absolute_method_name = GetAbsoluteMemberName(
260 MethodTable::const_iterator iter = method_table_.find(absolute_method_name);
261 if (iter == method_table_.end()) {
262 // Don't know about the method.
263 LOG(WARNING) << "Unknown method: " << method_call->ToString();
264 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
267 const base::TimeTicks start_time = base::TimeTicks::Now();
268 if (bus_->HasDBusThread()) {
269 // Post a task to run the method in the origin thread.
270 bus_->GetOriginTaskRunner()->PostTask(
272 base::BindOnce(&ExportedObject::RunMethod, this, iter->second,
273 std::move(method_call), start_time));
275 // If the D-Bus thread is not used, just call the method directly.
276 MethodCall* method = method_call.get();
278 method, base::BindOnce(&ExportedObject::SendResponse, this, start_time,
279 std::move(method_call)));
282 // It's valid to say HANDLED here, and send a method response at a later
283 // time from OnMethodCompleted() asynchronously.
284 return DBUS_HANDLER_RESULT_HANDLED;
287 void ExportedObject::RunMethod(const MethodCallCallback& method_call_callback,
288 std::unique_ptr<MethodCall> method_call,
289 base::TimeTicks start_time) {
290 bus_->AssertOnOriginThread();
291 MethodCall* method = method_call.get();
292 method_call_callback.Run(
293 method, base::BindOnce(&ExportedObject::SendResponse, this, start_time,
294 std::move(method_call)));
297 void ExportedObject::SendResponse(base::TimeTicks start_time,
298 std::unique_ptr<MethodCall> method_call,
299 std::unique_ptr<Response> response) {
301 if (bus_->HasDBusThread()) {
302 bus_->GetDBusTaskRunner()->PostTask(
303 FROM_HERE, base::BindOnce(&ExportedObject::OnMethodCompleted, this,
304 std::move(method_call), std::move(response),
307 OnMethodCompleted(std::move(method_call), std::move(response), start_time);
311 void ExportedObject::OnMethodCompleted(std::unique_ptr<MethodCall> method_call,
312 std::unique_ptr<Response> response,
313 base::TimeTicks start_time) {
314 bus_->AssertOnDBusThread();
316 // Record if the method call is successful, or not. 1 if successful.
317 UMA_HISTOGRAM_ENUMERATION("DBus.ExportedMethodHandleSuccess",
319 kSuccessRatioHistogramMaxValue);
321 // Check if the bus is still connected. If the method takes long to
322 // complete, the bus may be shut down meanwhile.
323 if (!bus_->IsConnected())
327 // Something bad happened in the method call.
328 std::unique_ptr<ErrorResponse> error_response(ErrorResponse::FromMethodCall(
329 method_call.get(), DBUS_ERROR_FAILED,
330 "error occurred in " + method_call->GetMember()));
331 bus_->Send(error_response->raw_message(), nullptr);
335 // The method call was successful.
336 bus_->Send(response->raw_message(), nullptr);
338 // Record time spent to handle the the method call. Don't include failures.
339 UMA_HISTOGRAM_TIMES("DBus.ExportedMethodHandleTime",
340 base::TimeTicks::Now() - start_time);
343 void ExportedObject::OnUnregistered(DBusConnection* connection) {
346 DBusHandlerResult ExportedObject::HandleMessageThunk(
347 DBusConnection* connection,
348 DBusMessage* raw_message,
350 ExportedObject* self = reinterpret_cast<ExportedObject*>(user_data);
351 return self->HandleMessage(connection, raw_message);
354 void ExportedObject::OnUnregisteredThunk(DBusConnection *connection,
356 ExportedObject* self = reinterpret_cast<ExportedObject*>(user_data);
357 return self->OnUnregistered(connection);