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/containers/contains.h"
11 #include "base/functional/bind.h"
12 #include "base/logging.h"
13 #include "base/memory/ref_counted.h"
14 #include "base/task/sequenced_task_runner.h"
15 #include "base/task/task_runner.h"
16 #include "base/threading/thread_restrictions.h"
18 #include "dbus/error.h"
19 #include "dbus/message.h"
20 #include "dbus/object_path.h"
21 #include "dbus/scoped_dbus_error.h"
22 #include "dbus/util.h"
26 ExportedObject::ExportedObject(Bus* bus,
27 const ObjectPath& object_path)
29 object_path_(object_path),
30 object_is_registered_(false) {
31 LOG_IF(FATAL, !object_path_.IsValid()) << object_path_.value();
34 ExportedObject::~ExportedObject() {
35 DCHECK(!object_is_registered_);
38 bool ExportedObject::ExportMethodAndBlock(
39 const std::string& interface_name,
40 const std::string& method_name,
41 const MethodCallCallback& method_call_callback) {
42 bus_->AssertOnDBusThread();
44 // Check if the method is already exported.
45 const std::string absolute_method_name =
46 GetAbsoluteMemberName(interface_name, method_name);
47 if (base::Contains(method_table_, absolute_method_name)) {
48 LOG(ERROR) << absolute_method_name << " is already exported";
54 if (!bus_->SetUpAsyncOperations())
59 // Add the method callback to the method table.
60 method_table_[absolute_method_name] = method_call_callback;
65 bool ExportedObject::UnexportMethodAndBlock(const std::string& interface_name,
66 const std::string& method_name) {
67 bus_->AssertOnDBusThread();
69 const std::string absolute_method_name =
70 GetAbsoluteMemberName(interface_name, method_name);
71 MethodTable::const_iterator iter = method_table_.find(absolute_method_name);
72 if (iter == method_table_.end()) {
73 LOG(ERROR) << absolute_method_name << " is not exported";
77 method_table_.erase(iter);
82 void ExportedObject::ExportMethod(
83 const std::string& interface_name,
84 const std::string& method_name,
85 const MethodCallCallback& method_call_callback,
86 OnExportedCallback on_exported_callback) {
87 bus_->AssertOnOriginThread();
89 base::OnceClosure task = base::BindOnce(
90 &ExportedObject::ExportMethodInternal, this, interface_name, method_name,
91 method_call_callback, std::move(on_exported_callback));
92 bus_->GetDBusTaskRunner()->PostTask(FROM_HERE, std::move(task));
95 void ExportedObject::UnexportMethod(
96 const std::string& interface_name,
97 const std::string& method_name,
98 OnUnexportedCallback on_unexported_callback) {
99 bus_->AssertOnOriginThread();
101 base::OnceClosure task = base::BindOnce(
102 &ExportedObject::UnexportMethodInternal, this, interface_name,
103 method_name, std::move(on_unexported_callback));
104 bus_->GetDBusTaskRunner()->PostTask(FROM_HERE, std::move(task));
107 void ExportedObject::SendSignal(Signal* signal) {
108 // For signals, the object path should be set to the path to the sender
109 // object, which is this exported object here.
110 CHECK(signal->SetPath(object_path_));
112 // Increment the reference count so we can safely reference the
113 // underlying signal message until the signal sending is complete. This
114 // will be unref'ed in SendSignalInternal().
115 DBusMessage* signal_message = signal->raw_message();
116 dbus_message_ref(signal_message);
118 if (bus_->GetDBusTaskRunner()->RunsTasksInCurrentSequence()) {
119 // The Chrome OS power manager doesn't use a dedicated TaskRunner for
120 // sending DBus messages. Sending signals asynchronously can cause an
121 // inversion in the message order if the power manager calls
122 // ObjectProxy::CallMethodAndBlock() before going back to the top level of
123 // the MessageLoop: crbug.com/472361.
124 SendSignalInternal(signal_message);
126 bus_->GetDBusTaskRunner()->PostTask(
127 FROM_HERE, base::BindOnce(&ExportedObject::SendSignalInternal, this,
132 void ExportedObject::Unregister() {
133 bus_->AssertOnDBusThread();
135 if (!object_is_registered_)
138 bus_->UnregisterObjectPath(object_path_);
139 object_is_registered_ = false;
142 void ExportedObject::ExportMethodInternal(
143 const std::string& interface_name,
144 const std::string& method_name,
145 const MethodCallCallback& method_call_callback,
146 OnExportedCallback on_exported_callback) {
147 bus_->AssertOnDBusThread();
149 const bool success = ExportMethodAndBlock(interface_name,
151 method_call_callback);
152 bus_->GetOriginTaskRunner()->PostTask(
153 FROM_HERE, base::BindOnce(&ExportedObject::OnExported, this,
154 std::move(on_exported_callback), interface_name,
155 method_name, success));
158 void ExportedObject::UnexportMethodInternal(
159 const std::string& interface_name,
160 const std::string& method_name,
161 OnUnexportedCallback on_unexported_callback) {
162 bus_->AssertOnDBusThread();
164 const bool success = UnexportMethodAndBlock(interface_name, method_name);
165 bus_->GetOriginTaskRunner()->PostTask(
166 FROM_HERE, base::BindOnce(&ExportedObject::OnUnexported, this,
167 std::move(on_unexported_callback),
168 interface_name, method_name, success));
171 void ExportedObject::OnExported(OnExportedCallback on_exported_callback,
172 const std::string& interface_name,
173 const std::string& method_name,
175 bus_->AssertOnOriginThread();
177 std::move(on_exported_callback).Run(interface_name, method_name, success);
180 void ExportedObject::OnUnexported(OnExportedCallback on_unexported_callback,
181 const std::string& interface_name,
182 const std::string& method_name,
184 bus_->AssertOnOriginThread();
186 std::move(on_unexported_callback).Run(interface_name, method_name, success);
189 void ExportedObject::SendSignalInternal(DBusMessage* signal_message) {
191 bus_->Send(signal_message, &serial);
192 dbus_message_unref(signal_message);
195 bool ExportedObject::Register() {
196 bus_->AssertOnDBusThread();
198 if (object_is_registered_)
203 DBusObjectPathVTable vtable = {};
204 vtable.message_function = &ExportedObject::HandleMessageThunk;
205 vtable.unregister_function = &ExportedObject::OnUnregisteredThunk;
207 bus_->TryRegisterObjectPath(object_path_, &vtable, this, &error);
209 LOG(ERROR) << "Failed to register the object: " << object_path_.value()
210 << ": " << error.message();
214 object_is_registered_ = true;
218 DBusHandlerResult ExportedObject::HandleMessage(
219 DBusConnection* connection,
220 DBusMessage* raw_message) {
221 bus_->AssertOnDBusThread();
222 // ExportedObject only handles method calls. Ignore other message types (e.g.
224 if (dbus_message_get_type(raw_message) != DBUS_MESSAGE_TYPE_METHOD_CALL)
225 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
227 // raw_message will be unrefed on exit of the function. Increment the
228 // reference so we can use it in MethodCall.
229 dbus_message_ref(raw_message);
230 std::unique_ptr<MethodCall> method_call(
231 MethodCall::FromRawMessage(raw_message));
232 const std::string interface = method_call->GetInterface();
233 const std::string member = method_call->GetMember();
235 if (interface.empty()) {
236 // We don't support method calls without interface.
237 LOG(WARNING) << "Interface is missing: " << method_call->ToString();
238 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
241 // Check if we know about the method.
242 const std::string absolute_method_name = GetAbsoluteMemberName(
244 MethodTable::const_iterator iter = method_table_.find(absolute_method_name);
245 if (iter == method_table_.end()) {
246 // Don't know about the method.
247 LOG(WARNING) << "Unknown method: " << method_call->ToString();
248 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
251 if (bus_->HasDBusThread()) {
252 // Post a task to run the method in the origin thread.
253 bus_->GetOriginTaskRunner()->PostTask(
254 FROM_HERE, base::BindOnce(&ExportedObject::RunMethod, this,
255 iter->second, std::move(method_call)));
257 // If the D-Bus thread is not used, just call the method directly.
258 MethodCall* method = method_call.get();
259 iter->second.Run(method, base::BindOnce(&ExportedObject::SendResponse, this,
260 std::move(method_call)));
263 // It's valid to say HANDLED here, and send a method response at a later
264 // time from OnMethodCompleted() asynchronously.
265 return DBUS_HANDLER_RESULT_HANDLED;
268 void ExportedObject::RunMethod(const MethodCallCallback& method_call_callback,
269 std::unique_ptr<MethodCall> method_call) {
270 bus_->AssertOnOriginThread();
271 MethodCall* method = method_call.get();
272 method_call_callback.Run(method,
273 base::BindOnce(&ExportedObject::SendResponse, this,
274 std::move(method_call)));
277 void ExportedObject::SendResponse(std::unique_ptr<MethodCall> method_call,
278 std::unique_ptr<Response> response) {
280 if (bus_->HasDBusThread()) {
281 bus_->GetDBusTaskRunner()->PostTask(
282 FROM_HERE, base::BindOnce(&ExportedObject::OnMethodCompleted, this,
283 std::move(method_call), std::move(response)));
285 OnMethodCompleted(std::move(method_call), std::move(response));
289 void ExportedObject::OnMethodCompleted(std::unique_ptr<MethodCall> method_call,
290 std::unique_ptr<Response> response) {
291 bus_->AssertOnDBusThread();
293 // Check if the bus is still connected. If the method takes long to
294 // complete, the bus may be shut down meanwhile.
295 if (!bus_->IsConnected())
299 // Something bad happened in the method call.
300 std::unique_ptr<ErrorResponse> error_response(ErrorResponse::FromMethodCall(
301 method_call.get(), DBUS_ERROR_FAILED,
302 "error occurred in " + method_call->GetMember()));
303 bus_->Send(error_response->raw_message(), nullptr);
307 // The method call was successful.
308 bus_->Send(response->raw_message(), nullptr);
311 void ExportedObject::OnUnregistered(DBusConnection* connection) {
314 DBusHandlerResult ExportedObject::HandleMessageThunk(
315 DBusConnection* connection,
316 DBusMessage* raw_message,
318 ExportedObject* self = reinterpret_cast<ExportedObject*>(user_data);
319 return self->HandleMessage(connection, raw_message);
322 void ExportedObject::OnUnregisteredThunk(DBusConnection *connection,
324 ExportedObject* self = reinterpret_cast<ExportedObject*>(user_data);
325 return self->OnUnregistered(connection);