Upload upstream chromium 67.0.3396
[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/message_loop/message_loop.h"
14 #include "base/metrics/histogram_macros.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 void ExportedObject::ExportMethod(const std::string& interface_name,
72                                   const std::string& method_name,
73                                   MethodCallCallback method_call_callback,
74                                   OnExportedCallback on_exported_calback) {
75   bus_->AssertOnOriginThread();
76
77   base::Closure task = base::Bind(&ExportedObject::ExportMethodInternal,
78                                   this,
79                                   interface_name,
80                                   method_name,
81                                   method_call_callback,
82                                   on_exported_calback);
83   bus_->GetDBusTaskRunner()->PostTask(FROM_HERE, task);
84 }
85
86 void ExportedObject::SendSignal(Signal* signal) {
87   // For signals, the object path should be set to the path to the sender
88   // object, which is this exported object here.
89   CHECK(signal->SetPath(object_path_));
90
91   // Increment the reference count so we can safely reference the
92   // underlying signal message until the signal sending is complete. This
93   // will be unref'ed in SendSignalInternal().
94   DBusMessage* signal_message = signal->raw_message();
95   dbus_message_ref(signal_message);
96
97   const base::TimeTicks start_time = base::TimeTicks::Now();
98   if (bus_->GetDBusTaskRunner()->RunsTasksInCurrentSequence()) {
99     // The Chrome OS power manager doesn't use a dedicated TaskRunner for
100     // sending DBus messages.  Sending signals asynchronously can cause an
101     // inversion in the message order if the power manager calls
102     // ObjectProxy::CallMethodAndBlock() before going back to the top level of
103     // the MessageLoop: crbug.com/472361.
104     SendSignalInternal(start_time, signal_message);
105   } else {
106     bus_->GetDBusTaskRunner()->PostTask(
107         FROM_HERE,
108         base::Bind(&ExportedObject::SendSignalInternal,
109                    this,
110                    start_time,
111                    signal_message));
112   }
113 }
114
115 void ExportedObject::Unregister() {
116   bus_->AssertOnDBusThread();
117
118   if (!object_is_registered_)
119     return;
120
121   bus_->UnregisterObjectPath(object_path_);
122   object_is_registered_ = false;
123 }
124
125 void ExportedObject::ExportMethodInternal(
126     const std::string& interface_name,
127     const std::string& method_name,
128     MethodCallCallback method_call_callback,
129     OnExportedCallback on_exported_calback) {
130   bus_->AssertOnDBusThread();
131
132   const bool success = ExportMethodAndBlock(interface_name,
133                                             method_name,
134                                             method_call_callback);
135   bus_->GetOriginTaskRunner()->PostTask(FROM_HERE,
136                                         base::Bind(&ExportedObject::OnExported,
137                                                    this,
138                                                    on_exported_calback,
139                                                    interface_name,
140                                                    method_name,
141                                                    success));
142 }
143
144 void ExportedObject::OnExported(OnExportedCallback on_exported_callback,
145                                 const std::string& interface_name,
146                                 const std::string& method_name,
147                                 bool success) {
148   bus_->AssertOnOriginThread();
149
150   on_exported_callback.Run(interface_name, method_name, success);
151 }
152
153 void ExportedObject::SendSignalInternal(base::TimeTicks start_time,
154                                         DBusMessage* signal_message) {
155   uint32_t serial = 0;
156   bus_->Send(signal_message, &serial);
157   dbus_message_unref(signal_message);
158   // Record time spent to send the the signal. This is not accurate as the
159   // signal will actually be sent from the next run of the message loop,
160   // but we can at least tell the number of signals sent.
161   UMA_HISTOGRAM_TIMES("DBus.SignalSendTime",
162                       base::TimeTicks::Now() - start_time);
163 }
164
165 bool ExportedObject::Register() {
166   bus_->AssertOnDBusThread();
167
168   if (object_is_registered_)
169     return true;
170
171   ScopedDBusError error;
172
173   DBusObjectPathVTable vtable = {};
174   vtable.message_function = &ExportedObject::HandleMessageThunk;
175   vtable.unregister_function = &ExportedObject::OnUnregisteredThunk;
176   const bool success = bus_->TryRegisterObjectPath(object_path_,
177                                                    &vtable,
178                                                    this,
179                                                    error.get());
180   if (!success) {
181     LOG(ERROR) << "Failed to register the object: " << object_path_.value()
182                << ": " << (error.is_set() ? error.message() : "");
183     return false;
184   }
185
186   object_is_registered_ = true;
187   return true;
188 }
189
190 DBusHandlerResult ExportedObject::HandleMessage(
191     DBusConnection* connection,
192     DBusMessage* raw_message) {
193   bus_->AssertOnDBusThread();
194   DCHECK_EQ(DBUS_MESSAGE_TYPE_METHOD_CALL, dbus_message_get_type(raw_message));
195
196   // raw_message will be unrefed on exit of the function. Increment the
197   // reference so we can use it in MethodCall.
198   dbus_message_ref(raw_message);
199   std::unique_ptr<MethodCall> method_call(
200       MethodCall::FromRawMessage(raw_message));
201   const std::string interface = method_call->GetInterface();
202   const std::string member = method_call->GetMember();
203
204   if (interface.empty()) {
205     // We don't support method calls without interface.
206     LOG(WARNING) << "Interface is missing: " << method_call->ToString();
207     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
208   }
209
210   // Check if we know about the method.
211   const std::string absolute_method_name = GetAbsoluteMemberName(
212       interface, member);
213   MethodTable::const_iterator iter = method_table_.find(absolute_method_name);
214   if (iter == method_table_.end()) {
215     // Don't know about the method.
216     LOG(WARNING) << "Unknown method: " << method_call->ToString();
217     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
218   }
219
220   const base::TimeTicks start_time = base::TimeTicks::Now();
221   if (bus_->HasDBusThread()) {
222     // Post a task to run the method in the origin thread.
223     bus_->GetOriginTaskRunner()->PostTask(
224         FROM_HERE,
225         base::BindOnce(&ExportedObject::RunMethod, this, iter->second,
226                        std::move(method_call), start_time));
227   } else {
228     // If the D-Bus thread is not used, just call the method directly.
229     MethodCall* method = method_call.get();
230     iter->second.Run(method,
231                      base::Bind(&ExportedObject::SendResponse,
232                                 this,
233                                 start_time,
234                                 base::Passed(&method_call)));
235   }
236
237   // It's valid to say HANDLED here, and send a method response at a later
238   // time from OnMethodCompleted() asynchronously.
239   return DBUS_HANDLER_RESULT_HANDLED;
240 }
241
242 void ExportedObject::RunMethod(MethodCallCallback method_call_callback,
243                                std::unique_ptr<MethodCall> method_call,
244                                base::TimeTicks start_time) {
245   bus_->AssertOnOriginThread();
246   MethodCall* method = method_call.get();
247   method_call_callback.Run(method,
248                            base::Bind(&ExportedObject::SendResponse,
249                                       this,
250                                       start_time,
251                                       base::Passed(&method_call)));
252 }
253
254 void ExportedObject::SendResponse(base::TimeTicks start_time,
255                                   std::unique_ptr<MethodCall> method_call,
256                                   std::unique_ptr<Response> response) {
257   DCHECK(method_call);
258   if (bus_->HasDBusThread()) {
259     bus_->GetDBusTaskRunner()->PostTask(
260         FROM_HERE, base::BindOnce(&ExportedObject::OnMethodCompleted, this,
261                                   std::move(method_call), std::move(response),
262                                   start_time));
263   } else {
264     OnMethodCompleted(std::move(method_call), std::move(response), start_time);
265   }
266 }
267
268 void ExportedObject::OnMethodCompleted(std::unique_ptr<MethodCall> method_call,
269                                        std::unique_ptr<Response> response,
270                                        base::TimeTicks start_time) {
271   bus_->AssertOnDBusThread();
272
273   // Record if the method call is successful, or not. 1 if successful.
274   UMA_HISTOGRAM_ENUMERATION("DBus.ExportedMethodHandleSuccess",
275                             response ? 1 : 0,
276                             kSuccessRatioHistogramMaxValue);
277
278   // Check if the bus is still connected. If the method takes long to
279   // complete, the bus may be shut down meanwhile.
280   if (!bus_->is_connected())
281     return;
282
283   if (!response) {
284     // Something bad happened in the method call.
285     std::unique_ptr<ErrorResponse> error_response(ErrorResponse::FromMethodCall(
286         method_call.get(), DBUS_ERROR_FAILED,
287         "error occurred in " + method_call->GetMember()));
288     bus_->Send(error_response->raw_message(), nullptr);
289     return;
290   }
291
292   // The method call was successful.
293   bus_->Send(response->raw_message(), nullptr);
294
295   // Record time spent to handle the the method call. Don't include failures.
296   UMA_HISTOGRAM_TIMES("DBus.ExportedMethodHandleTime",
297                       base::TimeTicks::Now() - start_time);
298 }
299
300 void ExportedObject::OnUnregistered(DBusConnection* connection) {
301 }
302
303 DBusHandlerResult ExportedObject::HandleMessageThunk(
304     DBusConnection* connection,
305     DBusMessage* raw_message,
306     void* user_data) {
307   ExportedObject* self = reinterpret_cast<ExportedObject*>(user_data);
308   return self->HandleMessage(connection, raw_message);
309 }
310
311 void ExportedObject::OnUnregisteredThunk(DBusConnection *connection,
312                                          void* user_data) {
313   ExportedObject* self = reinterpret_cast<ExportedObject*>(user_data);
314   return self->OnUnregistered(connection);
315 }
316
317 }  // namespace dbus