blob: d20702407c4a3e282834a0149c2fedd73a381f01 [file] [log] [blame]
[email protected]a51076112011-08-17 20:58:121// Copyright (c) 2011 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 "base/bind.h"
8#include "base/logging.h"
9#include "base/memory/ref_counted.h"
10#include "base/message_loop.h"
[email protected]a73389892011-09-06 20:53:3011#include "base/metrics/histogram.h"
[email protected]a51076112011-08-17 20:58:1212#include "base/threading/thread_restrictions.h"
13#include "base/time.h"
14#include "dbus/bus.h"
15#include "dbus/message.h"
16#include "dbus/scoped_dbus_error.h"
17
18namespace dbus {
19
20namespace {
21
[email protected]a73389892011-09-06 20:53:3022// Used for success ratio histograms. 1 for success, 0 for failure.
23const int kSuccessRatioHistogramMaxValue = 2;
24
[email protected]a51076112011-08-17 20:58:1225// Gets the absolute method name by concatenating the interface name and
26// the method name. Used for building keys for method_table_ in
27// ExportedObject.
28std::string GetAbsoluteMethodName(
29 const std::string& interface_name,
30 const std::string& method_name) {
31 return interface_name + "." + method_name;
32}
33
34} // namespace
35
36ExportedObject::ExportedObject(Bus* bus,
37 const std::string& service_name,
38 const std::string& object_path)
39 : bus_(bus),
40 service_name_(service_name),
41 object_path_(object_path),
42 object_is_registered_(false),
[email protected]a51076112011-08-17 20:58:1243 response_from_method_(NULL),
[email protected]12f97662011-08-20 01:07:1744 on_method_is_called_(false /* manual_reset */,
45 false /* initially_signaled */) {
[email protected]a51076112011-08-17 20:58:1246}
47
48ExportedObject::~ExportedObject() {
49 DCHECK(!object_is_registered_);
50}
51
52bool ExportedObject::ExportMethodAndBlock(
53 const std::string& interface_name,
54 const std::string& method_name,
55 MethodCallCallback method_call_callback) {
56 bus_->AssertOnDBusThread();
57
[email protected]3beaaa4e2011-08-23 07:29:2158 // Check if the method is already exported.
59 const std::string absolute_method_name =
60 GetAbsoluteMethodName(interface_name, method_name);
61 if (method_table_.find(absolute_method_name) != method_table_.end()) {
62 LOG(ERROR) << absolute_method_name << " is already exported";
63 return false;
64 }
65
[email protected]a51076112011-08-17 20:58:1266 if (!bus_->Connect())
67 return false;
68 if (!bus_->SetUpAsyncOperations())
69 return false;
70 if (!bus_->RequestOwnership(service_name_))
71 return false;
72 if (!Register())
73 return false;
74
[email protected]3beaaa4e2011-08-23 07:29:2175 // Add the method callback to the method table.
[email protected]a51076112011-08-17 20:58:1276 method_table_[absolute_method_name] = method_call_callback;
77
78 return true;
79}
80
81void ExportedObject::ExportMethod(const std::string& interface_name,
82 const std::string& method_name,
83 MethodCallCallback method_call_callback,
84 OnExportedCallback on_exported_calback) {
85 bus_->AssertOnOriginThread();
86
87 base::Closure task = base::Bind(&ExportedObject::ExportMethodInternal,
88 this,
89 interface_name,
90 method_name,
91 method_call_callback,
92 on_exported_calback);
93 bus_->PostTaskToDBusThread(FROM_HERE, task);
94}
95
[email protected]3beaaa4e2011-08-23 07:29:2196void ExportedObject::SendSignal(Signal* signal) {
97 // For signals, the object path should be set to the path to the sender
98 // object, which is this exported object here.
99 signal->SetPath(object_path_);
100
101 // Increment the reference count so we can safely reference the
102 // underlying signal message until the signal sending is complete. This
103 // will be unref'ed in SendSignalInternal().
104 DBusMessage* signal_message = signal->raw_message();
105 dbus_message_ref(signal_message);
106
[email protected]a73389892011-09-06 20:53:30107 const base::TimeTicks start_time = base::TimeTicks::Now();
[email protected]3beaaa4e2011-08-23 07:29:21108 bus_->PostTaskToDBusThread(FROM_HERE,
109 base::Bind(&ExportedObject::SendSignalInternal,
110 this,
[email protected]a73389892011-09-06 20:53:30111 start_time,
[email protected]1d887b272011-10-04 13:47:21112 signal_message));
[email protected]3beaaa4e2011-08-23 07:29:21113}
114
[email protected]a51076112011-08-17 20:58:12115void 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
125void 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_->PostTaskToOriginThread(FROM_HERE,
136 base::Bind(&ExportedObject::OnExported,
137 this,
138 on_exported_calback,
139 interface_name,
140 method_name,
141 success));
142}
143
144void 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
[email protected]a73389892011-09-06 20:53:30153void ExportedObject::SendSignalInternal(base::TimeTicks start_time,
[email protected]1d887b272011-10-04 13:47:21154 DBusMessage* signal_message) {
[email protected]3beaaa4e2011-08-23 07:29:21155 uint32 serial = 0;
156 bus_->Send(signal_message, &serial);
157 dbus_message_unref(signal_message);
[email protected]a73389892011-09-06 20:53:30158 // 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);
[email protected]3beaaa4e2011-08-23 07:29:21163}
164
[email protected]a51076112011-08-17 20:58:12165bool 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) {
[email protected]829f0e4c2011-08-31 18:02:43181 LOG(ERROR) << "Failed to register the object: " << object_path_ << ": "
[email protected]a51076112011-08-17 20:58:12182 << (error.is_set() ? error.message() : "");
183 return false;
184 }
185
186 object_is_registered_ = true;
187 return true;
188}
189
190DBusHandlerResult 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 scoped_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.
[email protected]a73389892011-09-06 20:53:30206 LOG(WARNING) << "Interface is missing: " << method_call->ToString();
[email protected]a51076112011-08-17 20:58:12207 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
208 }
209
210 // Check if we know about the method.
211 const std::string absolute_method_name = GetAbsoluteMethodName(
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.
[email protected]a73389892011-09-06 20:53:30216 LOG(WARNING) << "Unknown method: " << method_call->ToString();
[email protected]a51076112011-08-17 20:58:12217 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
218 }
219
[email protected]a73389892011-09-06 20:53:30220 const base::TimeTicks start_time = base::TimeTicks::Now();
[email protected]a51076112011-08-17 20:58:12221 Response* response = NULL;
222 if (bus_->HasDBusThread()) {
223 response_from_method_ = NULL;
[email protected]a51076112011-08-17 20:58:12224 // Post a task to run the method in the origin thread.
225 bus_->PostTaskToOriginThread(FROM_HERE,
226 base::Bind(&ExportedObject::RunMethod,
227 this,
228 iter->second,
229 method_call.get()));
230 // Wait until the method call is done. Blocking is not desirable but we
231 // should return the response to the dbus-daemon in the function, so we
232 // don't have a choice. We wait in the D-Bus thread, so it should be ok.
233 {
234 // We need a timeout here in case the method gets stuck.
235 const int kTimeoutSecs = 10;
236 const base::TimeDelta timeout(
237 base::TimeDelta::FromSeconds(kTimeoutSecs));
[email protected]a51076112011-08-17 20:58:12238
[email protected]12f97662011-08-20 01:07:17239 const bool signaled = on_method_is_called_.TimedWait(timeout);
240 // Method not called is a fatal error. The method is likely stuck
241 // infinitely in the origin thread. No way to stop it from here.
242 CHECK(signaled) << "Method " << absolute_method_name << " not called";
[email protected]a51076112011-08-17 20:58:12243 }
244 response = response_from_method_;
245 } else {
246 // If the D-Bus thread is not used, just call the method directly. We
247 // don't need the complicated logic to wait for the method call to be
248 // complete.
249 response = iter->second.Run(method_call.get());
250 }
[email protected]a73389892011-09-06 20:53:30251 // Record if the method call is successful, or not. 1 if successful.
252 UMA_HISTOGRAM_ENUMERATION("DBus.ExportedMethodHandleSuccess",
253 response ? 1 : 0,
254 kSuccessRatioHistogramMaxValue);
[email protected]a51076112011-08-17 20:58:12255
256 if (!response) {
[email protected]829f0e4c2011-08-31 18:02:43257 // Something bad happened in the method call.
[email protected]a51076112011-08-17 20:58:12258 scoped_ptr<dbus::ErrorResponse> error_response(
259 ErrorResponse::FromMethodCall(method_call.get(),
260 DBUS_ERROR_FAILED,
261 "error occurred in " + member));
262 dbus_connection_send(connection, error_response->raw_message(), NULL);
263 return DBUS_HANDLER_RESULT_HANDLED;
264 }
265
266 // The method call was successful.
267 dbus_connection_send(connection, response->raw_message(), NULL);
268 delete response;
[email protected]a73389892011-09-06 20:53:30269 // Record time spent to handle the the method call. Don't include failures.
270 UMA_HISTOGRAM_TIMES("DBus.ExportedMethodHandleTime",
271 base::TimeTicks::Now() - start_time);
[email protected]a51076112011-08-17 20:58:12272
273 return DBUS_HANDLER_RESULT_HANDLED;
274}
275
276void ExportedObject::RunMethod(MethodCallCallback method_call_callback,
277 MethodCall* method_call) {
278 bus_->AssertOnOriginThread();
279
[email protected]a51076112011-08-17 20:58:12280 response_from_method_ = method_call_callback.Run(method_call);
[email protected]a51076112011-08-17 20:58:12281 on_method_is_called_.Signal();
282}
283
284void ExportedObject::OnUnregistered(DBusConnection* connection) {
285}
286
287DBusHandlerResult ExportedObject::HandleMessageThunk(
288 DBusConnection* connection,
289 DBusMessage* raw_message,
290 void* user_data) {
291 ExportedObject* self = reinterpret_cast<ExportedObject*>(user_data);
292 return self->HandleMessage(connection, raw_message);
293}
294
295void ExportedObject::OnUnregisteredThunk(DBusConnection *connection,
296 void* user_data) {
297 ExportedObject* self = reinterpret_cast<ExportedObject*>(user_data);
298 return self->OnUnregistered(connection);
299}
300
301} // namespace dbus