Upload upstream chromium 120.0.6099.5
[platform/framework/web/chromium-efl.git] / dbus / exported_object.cc
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.
4
5 #include "dbus/exported_object.h"
6
7 #include <stdint.h>
8 #include <utility>
9
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"
17 #include "dbus/bus.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"
23
24 namespace dbus {
25
26 ExportedObject::ExportedObject(Bus* bus,
27                                const ObjectPath& object_path)
28     : bus_(bus),
29       object_path_(object_path),
30       object_is_registered_(false) {
31   LOG_IF(FATAL, !object_path_.IsValid()) << object_path_.value();
32 }
33
34 ExportedObject::~ExportedObject() {
35   DCHECK(!object_is_registered_);
36 }
37
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();
43
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";
49     return false;
50   }
51
52   if (!bus_->Connect())
53     return false;
54   if (!bus_->SetUpAsyncOperations())
55     return false;
56   if (!Register())
57     return false;
58
59   // Add the method callback to the method table.
60   method_table_[absolute_method_name] = method_call_callback;
61
62   return true;
63 }
64
65 bool ExportedObject::UnexportMethodAndBlock(const std::string& interface_name,
66                                             const std::string& method_name) {
67   bus_->AssertOnDBusThread();
68
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";
74     return false;
75   }
76
77   method_table_.erase(iter);
78
79   return true;
80 }
81
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();
88
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));
93 }
94
95 void ExportedObject::UnexportMethod(
96     const std::string& interface_name,
97     const std::string& method_name,
98     OnUnexportedCallback on_unexported_callback) {
99   bus_->AssertOnOriginThread();
100
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));
105 }
106
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_));
111
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);
117
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);
125   } else {
126     bus_->GetDBusTaskRunner()->PostTask(
127         FROM_HERE, base::BindOnce(&ExportedObject::SendSignalInternal, this,
128                                   signal_message));
129   }
130 }
131
132 void ExportedObject::Unregister() {
133   bus_->AssertOnDBusThread();
134
135   if (!object_is_registered_)
136     return;
137
138   bus_->UnregisterObjectPath(object_path_);
139   object_is_registered_ = false;
140 }
141
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();
148
149   const bool success = ExportMethodAndBlock(interface_name,
150                                             method_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));
156 }
157
158 void ExportedObject::UnexportMethodInternal(
159     const std::string& interface_name,
160     const std::string& method_name,
161     OnUnexportedCallback on_unexported_callback) {
162   bus_->AssertOnDBusThread();
163
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));
169 }
170
171 void ExportedObject::OnExported(OnExportedCallback on_exported_callback,
172                                 const std::string& interface_name,
173                                 const std::string& method_name,
174                                 bool success) {
175   bus_->AssertOnOriginThread();
176
177   std::move(on_exported_callback).Run(interface_name, method_name, success);
178 }
179
180 void ExportedObject::OnUnexported(OnExportedCallback on_unexported_callback,
181                                   const std::string& interface_name,
182                                   const std::string& method_name,
183                                   bool success) {
184   bus_->AssertOnOriginThread();
185
186   std::move(on_unexported_callback).Run(interface_name, method_name, success);
187 }
188
189 void ExportedObject::SendSignalInternal(DBusMessage* signal_message) {
190   uint32_t serial = 0;
191   bus_->Send(signal_message, &serial);
192   dbus_message_unref(signal_message);
193 }
194
195 bool ExportedObject::Register() {
196   bus_->AssertOnDBusThread();
197
198   if (object_is_registered_)
199     return true;
200
201   Error error;
202
203   DBusObjectPathVTable vtable = {};
204   vtable.message_function = &ExportedObject::HandleMessageThunk;
205   vtable.unregister_function = &ExportedObject::OnUnregisteredThunk;
206   const bool success =
207       bus_->TryRegisterObjectPath(object_path_, &vtable, this, &error);
208   if (!success) {
209     LOG(ERROR) << "Failed to register the object: " << object_path_.value()
210                << ": " << error.message();
211     return false;
212   }
213
214   object_is_registered_ = true;
215   return true;
216 }
217
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.
223   // signal).
224   if (dbus_message_get_type(raw_message) != DBUS_MESSAGE_TYPE_METHOD_CALL)
225     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
226
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();
234
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;
239   }
240
241   // Check if we know about the method.
242   const std::string absolute_method_name = GetAbsoluteMemberName(
243       interface, member);
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;
249   }
250
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)));
256   } else {
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)));
261   }
262
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;
266 }
267
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)));
275 }
276
277 void ExportedObject::SendResponse(std::unique_ptr<MethodCall> method_call,
278                                   std::unique_ptr<Response> response) {
279   DCHECK(method_call);
280   if (bus_->HasDBusThread()) {
281     bus_->GetDBusTaskRunner()->PostTask(
282         FROM_HERE, base::BindOnce(&ExportedObject::OnMethodCompleted, this,
283                                   std::move(method_call), std::move(response)));
284   } else {
285     OnMethodCompleted(std::move(method_call), std::move(response));
286   }
287 }
288
289 void ExportedObject::OnMethodCompleted(std::unique_ptr<MethodCall> method_call,
290                                        std::unique_ptr<Response> response) {
291   bus_->AssertOnDBusThread();
292
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())
296     return;
297
298   if (!response) {
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);
304     return;
305   }
306
307   // The method call was successful.
308   bus_->Send(response->raw_message(), nullptr);
309 }
310
311 void ExportedObject::OnUnregistered(DBusConnection* connection) {
312 }
313
314 DBusHandlerResult ExportedObject::HandleMessageThunk(
315     DBusConnection* connection,
316     DBusMessage* raw_message,
317     void* user_data) {
318   ExportedObject* self = reinterpret_cast<ExportedObject*>(user_data);
319   return self->HandleMessage(connection, raw_message);
320 }
321
322 void ExportedObject::OnUnregisteredThunk(DBusConnection *connection,
323                                          void* user_data) {
324   ExportedObject* self = reinterpret_cast<ExportedObject*>(user_data);
325   return self->OnUnregistered(connection);
326 }
327
328 }  // namespace dbus