Upload upstream chromium 108.0.5359.1
[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/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"
18 #include "dbus/bus.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 namespace {
27
28 // Used for success ratio histograms. 1 for success, 0 for failure.
29 const int kSuccessRatioHistogramMaxValue = 2;
30
31 }  // namespace
32
33 ExportedObject::ExportedObject(Bus* bus,
34                                const ObjectPath& object_path)
35     : bus_(bus),
36       object_path_(object_path),
37       object_is_registered_(false) {
38   LOG_IF(FATAL, !object_path_.IsValid()) << object_path_.value();
39 }
40
41 ExportedObject::~ExportedObject() {
42   DCHECK(!object_is_registered_);
43 }
44
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();
50
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";
56     return false;
57   }
58
59   if (!bus_->Connect())
60     return false;
61   if (!bus_->SetUpAsyncOperations())
62     return false;
63   if (!Register())
64     return false;
65
66   // Add the method callback to the method table.
67   method_table_[absolute_method_name] = method_call_callback;
68
69   return true;
70 }
71
72 bool ExportedObject::UnexportMethodAndBlock(const std::string& interface_name,
73                                             const std::string& method_name) {
74   bus_->AssertOnDBusThread();
75
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";
81     return false;
82   }
83
84   method_table_.erase(iter);
85
86   return true;
87 }
88
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();
95
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));
100 }
101
102 void ExportedObject::UnexportMethod(
103     const std::string& interface_name,
104     const std::string& method_name,
105     OnUnexportedCallback on_unexported_callback) {
106   bus_->AssertOnOriginThread();
107
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));
112 }
113
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_));
118
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);
124
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);
133   } else {
134     bus_->GetDBusTaskRunner()->PostTask(
135         FROM_HERE, base::BindOnce(&ExportedObject::SendSignalInternal, this,
136                                   start_time, signal_message));
137   }
138 }
139
140 void ExportedObject::Unregister() {
141   bus_->AssertOnDBusThread();
142
143   if (!object_is_registered_)
144     return;
145
146   bus_->UnregisterObjectPath(object_path_);
147   object_is_registered_ = false;
148 }
149
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();
156
157   const bool success = ExportMethodAndBlock(interface_name,
158                                             method_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));
164 }
165
166 void ExportedObject::UnexportMethodInternal(
167     const std::string& interface_name,
168     const std::string& method_name,
169     OnUnexportedCallback on_unexported_callback) {
170   bus_->AssertOnDBusThread();
171
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));
177 }
178
179 void ExportedObject::OnExported(OnExportedCallback on_exported_callback,
180                                 const std::string& interface_name,
181                                 const std::string& method_name,
182                                 bool success) {
183   bus_->AssertOnOriginThread();
184
185   std::move(on_exported_callback).Run(interface_name, method_name, success);
186 }
187
188 void ExportedObject::OnUnexported(OnExportedCallback on_unexported_callback,
189                                   const std::string& interface_name,
190                                   const std::string& method_name,
191                                   bool success) {
192   bus_->AssertOnOriginThread();
193
194   std::move(on_unexported_callback).Run(interface_name, method_name, success);
195 }
196
197 void ExportedObject::SendSignalInternal(base::TimeTicks start_time,
198                                         DBusMessage* signal_message) {
199   uint32_t serial = 0;
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);
207 }
208
209 bool ExportedObject::Register() {
210   bus_->AssertOnDBusThread();
211
212   if (object_is_registered_)
213     return true;
214
215   ScopedDBusError error;
216
217   DBusObjectPathVTable vtable = {};
218   vtable.message_function = &ExportedObject::HandleMessageThunk;
219   vtable.unregister_function = &ExportedObject::OnUnregisteredThunk;
220   const bool success = bus_->TryRegisterObjectPath(object_path_,
221                                                    &vtable,
222                                                    this,
223                                                    error.get());
224   if (!success) {
225     LOG(ERROR) << "Failed to register the object: " << object_path_.value()
226                << ": " << (error.is_set() ? error.message() : "");
227     return false;
228   }
229
230   object_is_registered_ = true;
231   return true;
232 }
233
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.
239   // signal).
240   if (dbus_message_get_type(raw_message) != DBUS_MESSAGE_TYPE_METHOD_CALL)
241     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
242
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();
250
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;
255   }
256
257   // Check if we know about the method.
258   const std::string absolute_method_name = GetAbsoluteMemberName(
259       interface, member);
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;
265   }
266
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(
271         FROM_HERE,
272         base::BindOnce(&ExportedObject::RunMethod, this, iter->second,
273                        std::move(method_call), start_time));
274   } else {
275     // If the D-Bus thread is not used, just call the method directly.
276     MethodCall* method = method_call.get();
277     iter->second.Run(
278         method, base::BindOnce(&ExportedObject::SendResponse, this, start_time,
279                                std::move(method_call)));
280   }
281
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;
285 }
286
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)));
295 }
296
297 void ExportedObject::SendResponse(base::TimeTicks start_time,
298                                   std::unique_ptr<MethodCall> method_call,
299                                   std::unique_ptr<Response> response) {
300   DCHECK(method_call);
301   if (bus_->HasDBusThread()) {
302     bus_->GetDBusTaskRunner()->PostTask(
303         FROM_HERE, base::BindOnce(&ExportedObject::OnMethodCompleted, this,
304                                   std::move(method_call), std::move(response),
305                                   start_time));
306   } else {
307     OnMethodCompleted(std::move(method_call), std::move(response), start_time);
308   }
309 }
310
311 void ExportedObject::OnMethodCompleted(std::unique_ptr<MethodCall> method_call,
312                                        std::unique_ptr<Response> response,
313                                        base::TimeTicks start_time) {
314   bus_->AssertOnDBusThread();
315
316   // Record if the method call is successful, or not. 1 if successful.
317   UMA_HISTOGRAM_ENUMERATION("DBus.ExportedMethodHandleSuccess",
318                             response ? 1 : 0,
319                             kSuccessRatioHistogramMaxValue);
320
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())
324     return;
325
326   if (!response) {
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);
332     return;
333   }
334
335   // The method call was successful.
336   bus_->Send(response->raw_message(), nullptr);
337
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);
341 }
342
343 void ExportedObject::OnUnregistered(DBusConnection* connection) {
344 }
345
346 DBusHandlerResult ExportedObject::HandleMessageThunk(
347     DBusConnection* connection,
348     DBusMessage* raw_message,
349     void* user_data) {
350   ExportedObject* self = reinterpret_cast<ExportedObject*>(user_data);
351   return self->HandleMessage(connection, raw_message);
352 }
353
354 void ExportedObject::OnUnregisteredThunk(DBusConnection *connection,
355                                          void* user_data) {
356   ExportedObject* self = reinterpret_cast<ExportedObject*>(user_data);
357   return self->OnUnregistered(connection);
358 }
359
360 }  // namespace dbus