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.
5 #include "dbus/exported_object.h"
8 #include "base/logging.h"
9 #include "base/memory/ref_counted.h"
10 #include "base/message_loop.h"
11 #include "base/metrics/histogram.h"
12 #include "base/threading/thread_restrictions.h"
13 #include "base/time.h"
15 #include "dbus/message.h"
16 #include "dbus/object_path.h"
17 #include "dbus/scoped_dbus_error.h"
23 // Used for success ratio histograms. 1 for success, 0 for failure.
24 const int kSuccessRatioHistogramMaxValue
= 2;
26 // Gets the absolute method name by concatenating the interface name and
27 // the method name. Used for building keys for method_table_ in
29 std::string
GetAbsoluteMethodName(
30 const std::string
& interface_name
,
31 const std::string
& method_name
) {
32 return interface_name
+ "." + method_name
;
37 ExportedObject::ExportedObject(Bus
* bus
,
38 const ObjectPath
& object_path
)
40 object_path_(object_path
),
41 object_is_registered_(false) {
44 ExportedObject::~ExportedObject() {
45 DCHECK(!object_is_registered_
);
48 bool ExportedObject::ExportMethodAndBlock(
49 const std::string
& interface_name
,
50 const std::string
& method_name
,
51 MethodCallCallback method_call_callback
) {
52 bus_
->AssertOnDBusThread();
54 // Check if the method is already exported.
55 const std::string absolute_method_name
=
56 GetAbsoluteMethodName(interface_name
, method_name
);
57 if (method_table_
.find(absolute_method_name
) != method_table_
.end()) {
58 LOG(ERROR
) << absolute_method_name
<< " is already exported";
64 if (!bus_
->SetUpAsyncOperations())
69 // Add the method callback to the method table.
70 method_table_
[absolute_method_name
] = method_call_callback
;
75 void ExportedObject::ExportMethod(const std::string
& interface_name
,
76 const std::string
& method_name
,
77 MethodCallCallback method_call_callback
,
78 OnExportedCallback on_exported_calback
) {
79 bus_
->AssertOnOriginThread();
81 base::Closure task
= base::Bind(&ExportedObject::ExportMethodInternal
,
87 bus_
->PostTaskToDBusThread(FROM_HERE
, task
);
90 void ExportedObject::SendSignal(Signal
* signal
) {
91 // For signals, the object path should be set to the path to the sender
92 // object, which is this exported object here.
93 CHECK(signal
->SetPath(object_path_
));
95 // Increment the reference count so we can safely reference the
96 // underlying signal message until the signal sending is complete. This
97 // will be unref'ed in SendSignalInternal().
98 DBusMessage
* signal_message
= signal
->raw_message();
99 dbus_message_ref(signal_message
);
101 const base::TimeTicks start_time
= base::TimeTicks::Now();
102 bus_
->PostTaskToDBusThread(FROM_HERE
,
103 base::Bind(&ExportedObject::SendSignalInternal
,
109 void ExportedObject::Unregister() {
110 bus_
->AssertOnDBusThread();
112 if (!object_is_registered_
)
115 bus_
->UnregisterObjectPath(object_path_
);
116 object_is_registered_
= false;
119 void ExportedObject::ExportMethodInternal(
120 const std::string
& interface_name
,
121 const std::string
& method_name
,
122 MethodCallCallback method_call_callback
,
123 OnExportedCallback on_exported_calback
) {
124 bus_
->AssertOnDBusThread();
126 const bool success
= ExportMethodAndBlock(interface_name
,
128 method_call_callback
);
129 bus_
->PostTaskToOriginThread(FROM_HERE
,
130 base::Bind(&ExportedObject::OnExported
,
138 void ExportedObject::OnExported(OnExportedCallback on_exported_callback
,
139 const std::string
& interface_name
,
140 const std::string
& method_name
,
142 bus_
->AssertOnOriginThread();
144 on_exported_callback
.Run(interface_name
, method_name
, success
);
147 void ExportedObject::SendSignalInternal(base::TimeTicks start_time
,
148 DBusMessage
* signal_message
) {
150 bus_
->Send(signal_message
, &serial
);
151 dbus_message_unref(signal_message
);
152 // Record time spent to send the the signal. This is not accurate as the
153 // signal will actually be sent from the next run of the message loop,
154 // but we can at least tell the number of signals sent.
155 UMA_HISTOGRAM_TIMES("DBus.SignalSendTime",
156 base::TimeTicks::Now() - start_time
);
159 bool ExportedObject::Register() {
160 bus_
->AssertOnDBusThread();
162 if (object_is_registered_
)
165 ScopedDBusError error
;
167 DBusObjectPathVTable vtable
= {};
168 vtable
.message_function
= &ExportedObject::HandleMessageThunk
;
169 vtable
.unregister_function
= &ExportedObject::OnUnregisteredThunk
;
170 const bool success
= bus_
->TryRegisterObjectPath(object_path_
,
175 LOG(ERROR
) << "Failed to register the object: " << object_path_
.value()
176 << ": " << (error
.is_set() ? error
.message() : "");
180 object_is_registered_
= true;
184 DBusHandlerResult
ExportedObject::HandleMessage(
185 DBusConnection
* connection
,
186 DBusMessage
* raw_message
) {
187 bus_
->AssertOnDBusThread();
188 DCHECK_EQ(DBUS_MESSAGE_TYPE_METHOD_CALL
, dbus_message_get_type(raw_message
));
190 // raw_message will be unrefed on exit of the function. Increment the
191 // reference so we can use it in MethodCall.
192 dbus_message_ref(raw_message
);
193 scoped_ptr
<MethodCall
> method_call(
194 MethodCall::FromRawMessage(raw_message
));
195 const std::string interface
= method_call
->GetInterface();
196 const std::string member
= method_call
->GetMember();
198 if (interface
.empty()) {
199 // We don't support method calls without interface.
200 LOG(WARNING
) << "Interface is missing: " << method_call
->ToString();
201 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
204 // Check if we know about the method.
205 const std::string absolute_method_name
= GetAbsoluteMethodName(
207 MethodTable::const_iterator iter
= method_table_
.find(absolute_method_name
);
208 if (iter
== method_table_
.end()) {
209 // Don't know about the method.
210 LOG(WARNING
) << "Unknown method: " << method_call
->ToString();
211 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
214 const base::TimeTicks start_time
= base::TimeTicks::Now();
215 if (bus_
->HasDBusThread()) {
216 // Post a task to run the method in the origin thread.
217 bus_
->PostTaskToOriginThread(FROM_HERE
,
218 base::Bind(&ExportedObject::RunMethod
,
221 base::Passed(&method_call
),
224 // If the D-Bus thread is not used, just call the method directly.
225 MethodCall
* method
= method_call
.get();
226 iter
->second
.Run(method
,
227 base::Bind(&ExportedObject::SendResponse
,
230 base::Passed(&method_call
)));
233 // It's valid to say HANDLED here, and send a method response at a later
234 // time from OnMethodCompleted() asynchronously.
235 return DBUS_HANDLER_RESULT_HANDLED
;
238 void ExportedObject::RunMethod(MethodCallCallback method_call_callback
,
239 scoped_ptr
<MethodCall
> method_call
,
240 base::TimeTicks start_time
) {
241 bus_
->AssertOnOriginThread();
242 MethodCall
* method
= method_call
.get();
243 method_call_callback
.Run(method
,
244 base::Bind(&ExportedObject::SendResponse
,
247 base::Passed(&method_call
)));
250 void ExportedObject::SendResponse(base::TimeTicks start_time
,
251 scoped_ptr
<MethodCall
> method_call
,
252 scoped_ptr
<Response
> response
) {
254 if (bus_
->HasDBusThread()) {
255 bus_
->PostTaskToDBusThread(FROM_HERE
,
256 base::Bind(&ExportedObject::OnMethodCompleted
,
258 base::Passed(&method_call
),
259 base::Passed(&response
),
262 OnMethodCompleted(method_call
.Pass(), response
.Pass(), start_time
);
266 void ExportedObject::OnMethodCompleted(scoped_ptr
<MethodCall
> method_call
,
267 scoped_ptr
<Response
> response
,
268 base::TimeTicks start_time
) {
269 bus_
->AssertOnDBusThread();
271 // Record if the method call is successful, or not. 1 if successful.
272 UMA_HISTOGRAM_ENUMERATION("DBus.ExportedMethodHandleSuccess",
274 kSuccessRatioHistogramMaxValue
);
276 // Check if the bus is still connected. If the method takes long to
277 // complete, the bus may be shut down meanwhile.
278 if (!bus_
->is_connected())
282 // Something bad happened in the method call.
283 scoped_ptr
<dbus::ErrorResponse
> error_response(
284 ErrorResponse::FromMethodCall(
287 "error occurred in " + method_call
->GetMember()));
288 bus_
->Send(error_response
->raw_message(), NULL
);
292 // The method call was successful.
293 bus_
->Send(response
->raw_message(), NULL
);
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
);
300 void ExportedObject::OnUnregistered(DBusConnection
* connection
) {
303 DBusHandlerResult
ExportedObject::HandleMessageThunk(
304 DBusConnection
* connection
,
305 DBusMessage
* raw_message
,
307 ExportedObject
* self
= reinterpret_cast<ExportedObject
*>(user_data
);
308 return self
->HandleMessage(connection
, raw_message
);
311 void ExportedObject::OnUnregisteredThunk(DBusConnection
*connection
,
313 ExportedObject
* self
= reinterpret_cast<ExportedObject
*>(user_data
);
314 return self
->OnUnregistered(connection
);