Upload upstream chromium 73.0.3683.0
[platform/framework/web/chromium-efl.git] / dbus / exported_object.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
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_runner.h"
15 #include "base/threading/thread_restrictions.h"
16 #include "base/time/time.h"
17 #include "dbus/bus.h"
18 #include "dbus/message.h"
19 #include "dbus/object_path.h"
20 #include "dbus/scoped_dbus_error.h"
21 #include "dbus/util.h"
22
23 namespace dbus {
24
25 namespace {
26
27 // Used for success ratio histograms. 1 for success, 0 for failure.
28 const int kSuccessRatioHistogramMaxValue = 2;
29
30 }  // namespace
31
32 ExportedObject::ExportedObject(Bus* bus,
33                                const ObjectPath& object_path)
34     : bus_(bus),
35       object_path_(object_path),
36       object_is_registered_(false) {
37   LOG_IF(FATAL, !object_path_.IsValid()) << object_path_.value();
38 }
39
40 ExportedObject::~ExportedObject() {
41   DCHECK(!object_is_registered_);
42 }
43
44 bool ExportedObject::ExportMethodAndBlock(
45     const std::string& interface_name,
46     const std::string& method_name,
47     MethodCallCallback method_call_callback) {
48   bus_->AssertOnDBusThread();
49
50   // Check if the method is already exported.
51   const std::string absolute_method_name =
52       GetAbsoluteMemberName(interface_name, method_name);
53   if (method_table_.find(absolute_method_name) != method_table_.end()) {
54     LOG(ERROR) << absolute_method_name << " is already exported";
55     return false;
56   }
57
58   if (!bus_->Connect())
59     return false;
60   if (!bus_->SetUpAsyncOperations())
61     return false;
62   if (!Register())
63     return false;
64
65   // Add the method callback to the method table.
66   method_table_[absolute_method_name] = method_call_callback;
67
68   return true;
69 }
70
71 bool ExportedObject::UnexportMethodAndBlock(const std::string& interface_name,
72                                             const std::string& method_name) {
73   bus_->AssertOnDBusThread();
74
75   const std::string absolute_method_name =
76       GetAbsoluteMemberName(interface_name, method_name);
77   MethodTable::const_iterator iter = method_table_.find(absolute_method_name);
78   if (iter == method_table_.end()) {
79     LOG(ERROR) << absolute_method_name << " is not exported";
80     return false;
81   }
82
83   method_table_.erase(iter);
84
85   return true;
86 }
87
88 void ExportedObject::ExportMethod(const std::string& interface_name,
89                                   const std::string& method_name,
90                                   MethodCallCallback method_call_callback,
91                                   OnExportedCallback on_exported_calback) {
92   bus_->AssertOnOriginThread();
93
94   base::Closure task = base::Bind(&ExportedObject::ExportMethodInternal,
95                                   this,
96                                   interface_name,
97                                   method_name,
98                                   method_call_callback,
99                                   on_exported_calback);
100   bus_->GetDBusTaskRunner()->PostTask(FROM_HERE, task);
101 }
102
103 void ExportedObject::UnexportMethod(
104     const std::string& interface_name,
105     const std::string& method_name,
106     OnUnexportedCallback on_unexported_calback) {
107   bus_->AssertOnOriginThread();
108
109   base::Closure task =
110       base::Bind(&ExportedObject::UnexportMethodInternal, this, interface_name,
111                  method_name, on_unexported_calback);
112   bus_->GetDBusTaskRunner()->PostTask(FROM_HERE, task);
113 }
114
115 void ExportedObject::SendSignal(Signal* signal) {
116   // For signals, the object path should be set to the path to the sender
117   // object, which is this exported object here.
118   CHECK(signal->SetPath(object_path_));
119
120   // Increment the reference count so we can safely reference the
121   // underlying signal message until the signal sending is complete. This
122   // will be unref'ed in SendSignalInternal().
123   DBusMessage* signal_message = signal->raw_message();
124   dbus_message_ref(signal_message);
125
126   const base::TimeTicks start_time = base::TimeTicks::Now();
127   if (bus_->GetDBusTaskRunner()->RunsTasksInCurrentSequence()) {
128     // The Chrome OS power manager doesn't use a dedicated TaskRunner for
129     // sending DBus messages.  Sending signals asynchronously can cause an
130     // inversion in the message order if the power manager calls
131     // ObjectProxy::CallMethodAndBlock() before going back to the top level of
132     // the MessageLoop: crbug.com/472361.
133     SendSignalInternal(start_time, signal_message);
134   } else {
135     bus_->GetDBusTaskRunner()->PostTask(
136         FROM_HERE,
137         base::Bind(&ExportedObject::SendSignalInternal,
138                    this,
139                    start_time,
140                    signal_message));
141   }
142 }
143
144 void ExportedObject::Unregister() {
145   bus_->AssertOnDBusThread();
146
147   if (!object_is_registered_)
148     return;
149
150   bus_->UnregisterObjectPath(object_path_);
151   object_is_registered_ = false;
152 }
153
154 void ExportedObject::ExportMethodInternal(
155     const std::string& interface_name,
156     const std::string& method_name,
157     MethodCallCallback method_call_callback,
158     OnExportedCallback on_exported_calback) {
159   bus_->AssertOnDBusThread();
160
161   const bool success = ExportMethodAndBlock(interface_name,
162                                             method_name,
163                                             method_call_callback);
164   bus_->GetOriginTaskRunner()->PostTask(FROM_HERE,
165                                         base::Bind(&ExportedObject::OnExported,
166                                                    this,
167                                                    on_exported_calback,
168                                                    interface_name,
169                                                    method_name,
170                                                    success));
171 }
172
173 void ExportedObject::UnexportMethodInternal(
174     const std::string& interface_name,
175     const std::string& method_name,
176     OnUnexportedCallback on_unexported_calback) {
177   bus_->AssertOnDBusThread();
178
179   const bool success = UnexportMethodAndBlock(interface_name, method_name);
180   bus_->GetOriginTaskRunner()->PostTask(
181       FROM_HERE,
182       base::Bind(&ExportedObject::OnUnexported, this, on_unexported_calback,
183                  interface_name, method_name, success));
184 }
185
186 void ExportedObject::OnExported(OnExportedCallback on_exported_callback,
187                                 const std::string& interface_name,
188                                 const std::string& method_name,
189                                 bool success) {
190   bus_->AssertOnOriginThread();
191
192   on_exported_callback.Run(interface_name, method_name, success);
193 }
194
195 void ExportedObject::OnUnexported(OnExportedCallback on_unexported_callback,
196                                   const std::string& interface_name,
197                                   const std::string& method_name,
198                                   bool success) {
199   bus_->AssertOnOriginThread();
200
201   on_unexported_callback.Run(interface_name, method_name, success);
202 }
203
204 void ExportedObject::SendSignalInternal(base::TimeTicks start_time,
205                                         DBusMessage* signal_message) {
206   uint32_t serial = 0;
207   bus_->Send(signal_message, &serial);
208   dbus_message_unref(signal_message);
209   // Record time spent to send the the signal. This is not accurate as the
210   // signal will actually be sent from the next run of the message loop,
211   // but we can at least tell the number of signals sent.
212   UMA_HISTOGRAM_TIMES("DBus.SignalSendTime",
213                       base::TimeTicks::Now() - start_time);
214 }
215
216 bool ExportedObject::Register() {
217   bus_->AssertOnDBusThread();
218
219   if (object_is_registered_)
220     return true;
221
222   ScopedDBusError error;
223
224   DBusObjectPathVTable vtable = {};
225   vtable.message_function = &ExportedObject::HandleMessageThunk;
226   vtable.unregister_function = &ExportedObject::OnUnregisteredThunk;
227   const bool success = bus_->TryRegisterObjectPath(object_path_,
228                                                    &vtable,
229                                                    this,
230                                                    error.get());
231   if (!success) {
232     LOG(ERROR) << "Failed to register the object: " << object_path_.value()
233                << ": " << (error.is_set() ? error.message() : "");
234     return false;
235   }
236
237   object_is_registered_ = true;
238   return true;
239 }
240
241 DBusHandlerResult ExportedObject::HandleMessage(
242     DBusConnection* connection,
243     DBusMessage* raw_message) {
244   bus_->AssertOnDBusThread();
245   // ExportedObject only handles method calls. Ignore other message types (e.g.
246   // signal).
247   if (dbus_message_get_type(raw_message) != DBUS_MESSAGE_TYPE_METHOD_CALL)
248     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
249
250   // raw_message will be unrefed on exit of the function. Increment the
251   // reference so we can use it in MethodCall.
252   dbus_message_ref(raw_message);
253   std::unique_ptr<MethodCall> method_call(
254       MethodCall::FromRawMessage(raw_message));
255   const std::string interface = method_call->GetInterface();
256   const std::string member = method_call->GetMember();
257
258   if (interface.empty()) {
259     // We don't support method calls without interface.
260     LOG(WARNING) << "Interface is missing: " << method_call->ToString();
261     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
262   }
263
264   // Check if we know about the method.
265   const std::string absolute_method_name = GetAbsoluteMemberName(
266       interface, member);
267   MethodTable::const_iterator iter = method_table_.find(absolute_method_name);
268   if (iter == method_table_.end()) {
269     // Don't know about the method.
270     LOG(WARNING) << "Unknown method: " << method_call->ToString();
271     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
272   }
273
274   const base::TimeTicks start_time = base::TimeTicks::Now();
275   if (bus_->HasDBusThread()) {
276     // Post a task to run the method in the origin thread.
277     bus_->GetOriginTaskRunner()->PostTask(
278         FROM_HERE,
279         base::BindOnce(&ExportedObject::RunMethod, this, iter->second,
280                        std::move(method_call), start_time));
281   } else {
282     // If the D-Bus thread is not used, just call the method directly.
283     MethodCall* method = method_call.get();
284     iter->second.Run(method,
285                      base::Bind(&ExportedObject::SendResponse,
286                                 this,
287                                 start_time,
288                                 base::Passed(&method_call)));
289   }
290
291   // It's valid to say HANDLED here, and send a method response at a later
292   // time from OnMethodCompleted() asynchronously.
293   return DBUS_HANDLER_RESULT_HANDLED;
294 }
295
296 void ExportedObject::RunMethod(MethodCallCallback method_call_callback,
297                                std::unique_ptr<MethodCall> method_call,
298                                base::TimeTicks start_time) {
299   bus_->AssertOnOriginThread();
300   MethodCall* method = method_call.get();
301   method_call_callback.Run(method,
302                            base::Bind(&ExportedObject::SendResponse,
303                                       this,
304                                       start_time,
305                                       base::Passed(&method_call)));
306 }
307
308 void ExportedObject::SendResponse(base::TimeTicks start_time,
309                                   std::unique_ptr<MethodCall> method_call,
310                                   std::unique_ptr<Response> response) {
311   DCHECK(method_call);
312   if (bus_->HasDBusThread()) {
313     bus_->GetDBusTaskRunner()->PostTask(
314         FROM_HERE, base::BindOnce(&ExportedObject::OnMethodCompleted, this,
315                                   std::move(method_call), std::move(response),
316                                   start_time));
317   } else {
318     OnMethodCompleted(std::move(method_call), std::move(response), start_time);
319   }
320 }
321
322 void ExportedObject::OnMethodCompleted(std::unique_ptr<MethodCall> method_call,
323                                        std::unique_ptr<Response> response,
324                                        base::TimeTicks start_time) {
325   bus_->AssertOnDBusThread();
326
327   // Record if the method call is successful, or not. 1 if successful.
328   UMA_HISTOGRAM_ENUMERATION("DBus.ExportedMethodHandleSuccess",
329                             response ? 1 : 0,
330                             kSuccessRatioHistogramMaxValue);
331
332   // Check if the bus is still connected. If the method takes long to
333   // complete, the bus may be shut down meanwhile.
334   if (!bus_->IsConnected())
335     return;
336
337   if (!response) {
338     // Something bad happened in the method call.
339     std::unique_ptr<ErrorResponse> error_response(ErrorResponse::FromMethodCall(
340         method_call.get(), DBUS_ERROR_FAILED,
341         "error occurred in " + method_call->GetMember()));
342     bus_->Send(error_response->raw_message(), nullptr);
343     return;
344   }
345
346   // The method call was successful.
347   bus_->Send(response->raw_message(), nullptr);
348
349   // Record time spent to handle the the method call. Don't include failures.
350   UMA_HISTOGRAM_TIMES("DBus.ExportedMethodHandleTime",
351                       base::TimeTicks::Now() - start_time);
352 }
353
354 void ExportedObject::OnUnregistered(DBusConnection* connection) {
355 }
356
357 DBusHandlerResult ExportedObject::HandleMessageThunk(
358     DBusConnection* connection,
359     DBusMessage* raw_message,
360     void* user_data) {
361   ExportedObject* self = reinterpret_cast<ExportedObject*>(user_data);
362   return self->HandleMessage(connection, raw_message);
363 }
364
365 void ExportedObject::OnUnregisteredThunk(DBusConnection *connection,
366                                          void* user_data) {
367   ExportedObject* self = reinterpret_cast<ExportedObject*>(user_data);
368   return self->OnUnregistered(connection);
369 }
370
371 }  // namespace dbus