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.
8 #include "base/logging.h"
9 #include "base/message_loop.h"
10 #include "base/metrics/histogram.h"
11 #include "base/string_piece.h"
12 #include "base/stringprintf.h"
13 #include "base/threading/thread.h"
14 #include "base/threading/thread_restrictions.h"
15 #include "dbus/message.h"
16 #include "dbus/object_path.h"
17 #include "dbus/object_proxy.h"
18 #include "dbus/scoped_dbus_error.h"
22 const char kErrorServiceUnknown
[] = "org.freedesktop.DBus.Error.ServiceUnknown";
24 // Used for success ratio histograms. 1 for success, 0 for failure.
25 const int kSuccessRatioHistogramMaxValue
= 2;
27 // The path of D-Bus Object sending NameOwnerChanged signal.
28 const char kDbusSystemObjectPath
[] = "/org/freedesktop/DBus";
30 // Gets the absolute signal name by concatenating the interface name and
31 // the signal name. Used for building keys for method_table_ in
33 std::string
GetAbsoluteSignalName(
34 const std::string
& interface_name
,
35 const std::string
& signal_name
) {
36 return interface_name
+ "." + signal_name
;
39 // An empty function used for ObjectProxy::EmptyResponseCallback().
40 void EmptyResponseCallbackBody(dbus::Response
* unused_response
) {
47 ObjectProxy::ObjectProxy(Bus
* bus
,
48 const std::string
& service_name
,
49 const ObjectPath
& object_path
,
52 service_name_(service_name
),
53 object_path_(object_path
),
55 ignore_service_unknown_errors_(
56 options
& IGNORE_SERVICE_UNKNOWN_ERRORS
) {
59 ObjectProxy::~ObjectProxy() {
62 // Originally we tried to make |method_call| a const reference, but we
63 // gave up as dbus_connection_send_with_reply_and_block() takes a
64 // non-const pointer of DBusMessage as the second parameter.
65 Response
* ObjectProxy::CallMethodAndBlock(MethodCall
* method_call
,
67 bus_
->AssertOnDBusThread();
69 if (!bus_
->Connect() ||
70 !method_call
->SetDestination(service_name_
) ||
71 !method_call
->SetPath(object_path_
))
74 DBusMessage
* request_message
= method_call
->raw_message();
76 ScopedDBusError error
;
78 // Send the message synchronously.
79 const base::TimeTicks start_time
= base::TimeTicks::Now();
80 DBusMessage
* response_message
=
81 bus_
->SendWithReplyAndBlock(request_message
, timeout_ms
, error
.get());
82 // Record if the method call is successful, or not. 1 if successful.
83 UMA_HISTOGRAM_ENUMERATION("DBus.SyncMethodCallSuccess",
84 response_message
? 1 : 0,
85 kSuccessRatioHistogramMaxValue
);
87 if (!response_message
) {
88 LogMethodCallFailure(method_call
->GetInterface(),
89 method_call
->GetMember(),
90 error
.is_set() ? error
.name() : "unknown error type",
91 error
.is_set() ? error
.message() : "");
94 // Record time spent for the method call. Don't include failures.
95 UMA_HISTOGRAM_TIMES("DBus.SyncMethodCallTime",
96 base::TimeTicks::Now() - start_time
);
98 return Response::FromRawMessage(response_message
);
101 void ObjectProxy::CallMethod(MethodCall
* method_call
,
103 ResponseCallback callback
) {
104 CallMethodWithErrorCallback(method_call
, timeout_ms
, callback
,
105 base::Bind(&ObjectProxy::OnCallMethodError
,
107 method_call
->GetInterface(),
108 method_call
->GetMember(),
112 void ObjectProxy::CallMethodWithErrorCallback(MethodCall
* method_call
,
114 ResponseCallback callback
,
115 ErrorCallback error_callback
) {
116 bus_
->AssertOnOriginThread();
118 const base::TimeTicks start_time
= base::TimeTicks::Now();
120 if (!method_call
->SetDestination(service_name_
) ||
121 !method_call
->SetPath(object_path_
)) {
122 // In case of a failure, run the error callback with NULL.
123 DBusMessage
* response_message
= NULL
;
124 base::Closure task
= base::Bind(&ObjectProxy::RunResponseCallback
,
130 bus_
->PostTaskToOriginThread(FROM_HERE
, task
);
134 // Increment the reference count so we can safely reference the
135 // underlying request message until the method call is complete. This
136 // will be unref'ed in StartAsyncMethodCall().
137 DBusMessage
* request_message
= method_call
->raw_message();
138 dbus_message_ref(request_message
);
140 base::Closure task
= base::Bind(&ObjectProxy::StartAsyncMethodCall
,
147 // Wait for the response in the D-Bus thread.
148 bus_
->PostTaskToDBusThread(FROM_HERE
, task
);
151 void ObjectProxy::ConnectToSignal(const std::string
& interface_name
,
152 const std::string
& signal_name
,
153 SignalCallback signal_callback
,
154 OnConnectedCallback on_connected_callback
) {
155 bus_
->AssertOnOriginThread();
157 bus_
->PostTaskToDBusThread(FROM_HERE
,
158 base::Bind(&ObjectProxy::ConnectToSignalInternal
,
163 on_connected_callback
));
166 void ObjectProxy::Detach() {
167 bus_
->AssertOnDBusThread();
170 if (!bus_
->RemoveFilterFunction(&ObjectProxy::HandleMessageThunk
, this)) {
171 LOG(ERROR
) << "Failed to remove filter function";
175 for (std::set
<std::string
>::iterator iter
= match_rules_
.begin();
176 iter
!= match_rules_
.end(); ++iter
) {
177 ScopedDBusError error
;
178 bus_
->RemoveMatch(*iter
, error
.get());
179 if (error
.is_set()) {
180 // There is nothing we can do to recover, so just print the error.
181 LOG(ERROR
) << "Failed to remove match rule: " << *iter
;
184 match_rules_
.clear();
188 ObjectProxy::ResponseCallback
ObjectProxy::EmptyResponseCallback() {
189 return base::Bind(&EmptyResponseCallbackBody
);
192 ObjectProxy::OnPendingCallIsCompleteData::OnPendingCallIsCompleteData(
193 ObjectProxy
* in_object_proxy
,
194 ResponseCallback in_response_callback
,
195 ErrorCallback in_error_callback
,
196 base::TimeTicks in_start_time
)
197 : object_proxy(in_object_proxy
),
198 response_callback(in_response_callback
),
199 error_callback(in_error_callback
),
200 start_time(in_start_time
) {
203 ObjectProxy::OnPendingCallIsCompleteData::~OnPendingCallIsCompleteData() {
206 void ObjectProxy::StartAsyncMethodCall(int timeout_ms
,
207 DBusMessage
* request_message
,
208 ResponseCallback response_callback
,
209 ErrorCallback error_callback
,
210 base::TimeTicks start_time
) {
211 bus_
->AssertOnDBusThread();
213 if (!bus_
->Connect() || !bus_
->SetUpAsyncOperations()) {
214 // In case of a failure, run the error callback with NULL.
215 DBusMessage
* response_message
= NULL
;
216 base::Closure task
= base::Bind(&ObjectProxy::RunResponseCallback
,
222 bus_
->PostTaskToOriginThread(FROM_HERE
, task
);
224 dbus_message_unref(request_message
);
228 DBusPendingCall
* pending_call
= NULL
;
230 bus_
->SendWithReply(request_message
, &pending_call
, timeout_ms
);
232 // Prepare the data we'll be passing to OnPendingCallIsCompleteThunk().
233 // The data will be deleted in OnPendingCallIsCompleteThunk().
234 OnPendingCallIsCompleteData
* data
=
235 new OnPendingCallIsCompleteData(this, response_callback
, error_callback
,
238 // This returns false only when unable to allocate memory.
239 const bool success
= dbus_pending_call_set_notify(
241 &ObjectProxy::OnPendingCallIsCompleteThunk
,
244 CHECK(success
) << "Unable to allocate memory";
245 dbus_pending_call_unref(pending_call
);
247 // It's now safe to unref the request message.
248 dbus_message_unref(request_message
);
251 void ObjectProxy::OnPendingCallIsComplete(DBusPendingCall
* pending_call
,
252 ResponseCallback response_callback
,
253 ErrorCallback error_callback
,
254 base::TimeTicks start_time
) {
255 bus_
->AssertOnDBusThread();
257 DBusMessage
* response_message
= dbus_pending_call_steal_reply(pending_call
);
258 base::Closure task
= base::Bind(&ObjectProxy::RunResponseCallback
,
264 bus_
->PostTaskToOriginThread(FROM_HERE
, task
);
267 void ObjectProxy::RunResponseCallback(ResponseCallback response_callback
,
268 ErrorCallback error_callback
,
269 base::TimeTicks start_time
,
270 DBusMessage
* response_message
) {
271 bus_
->AssertOnOriginThread();
273 bool method_call_successful
= false;
274 if (!response_message
) {
275 // The response is not received.
276 error_callback
.Run(NULL
);
277 } else if (dbus_message_get_type(response_message
) ==
278 DBUS_MESSAGE_TYPE_ERROR
) {
279 // This will take |response_message| and release (unref) it.
280 scoped_ptr
<dbus::ErrorResponse
> error_response(
281 dbus::ErrorResponse::FromRawMessage(response_message
));
282 error_callback
.Run(error_response
.get());
283 // Delete the message on the D-Bus thread. See below for why.
284 bus_
->PostTaskToDBusThread(
286 base::Bind(&base::DeletePointer
<dbus::ErrorResponse
>,
287 error_response
.release()));
289 // This will take |response_message| and release (unref) it.
290 scoped_ptr
<dbus::Response
> response(
291 dbus::Response::FromRawMessage(response_message
));
292 // The response is successfully received.
293 response_callback
.Run(response
.get());
294 // The message should be deleted on the D-Bus thread for a complicated
297 // libdbus keeps track of the number of bytes in the incoming message
298 // queue to ensure that the data size in the queue is manageable. The
299 // bookkeeping is partly done via dbus_message_unref(), and immediately
300 // asks the client code (Chrome) to stop monitoring the underlying
301 // socket, if the number of bytes exceeds a certian number, which is set
302 // to 63MB, per dbus-transport.cc:
304 // /* Try to default to something that won't totally hose the system,
305 // * but doesn't impose too much of a limitation.
307 // transport->max_live_messages_size = _DBUS_ONE_MEGABYTE * 63;
309 // The monitoring of the socket is done on the D-Bus thread (see Watch
310 // class in bus.cc), hence we should stop the monitoring from D-Bus
311 // thread, not from the current thread here, which is likely UI thread.
312 bus_
->PostTaskToDBusThread(
314 base::Bind(&base::DeletePointer
<dbus::Response
>,
315 response
.release()));
317 method_call_successful
= true;
318 // Record time spent for the method call. Don't include failures.
319 UMA_HISTOGRAM_TIMES("DBus.AsyncMethodCallTime",
320 base::TimeTicks::Now() - start_time
);
322 // Record if the method call is successful, or not. 1 if successful.
323 UMA_HISTOGRAM_ENUMERATION("DBus.AsyncMethodCallSuccess",
324 method_call_successful
,
325 kSuccessRatioHistogramMaxValue
);
328 void ObjectProxy::OnPendingCallIsCompleteThunk(DBusPendingCall
* pending_call
,
330 OnPendingCallIsCompleteData
* data
=
331 reinterpret_cast<OnPendingCallIsCompleteData
*>(user_data
);
332 ObjectProxy
* self
= data
->object_proxy
;
333 self
->OnPendingCallIsComplete(pending_call
,
334 data
->response_callback
,
335 data
->error_callback
,
340 void ObjectProxy::ConnectToSignalInternal(
341 const std::string
& interface_name
,
342 const std::string
& signal_name
,
343 SignalCallback signal_callback
,
344 OnConnectedCallback on_connected_callback
) {
345 bus_
->AssertOnDBusThread();
347 const std::string absolute_signal_name
=
348 GetAbsoluteSignalName(interface_name
, signal_name
);
350 // Will become true, if everything is successful.
351 bool success
= false;
353 if (bus_
->Connect() && bus_
->SetUpAsyncOperations()) {
354 // We should add the filter only once. Otherwise, HandleMessage() will
355 // be called more than once.
356 if (!filter_added_
) {
357 if (bus_
->AddFilterFunction(&ObjectProxy::HandleMessageThunk
, this)) {
358 filter_added_
= true;
360 LOG(ERROR
) << "Failed to add filter function";
363 // Add a match rule so the signal goes through HandleMessage().
364 const std::string match_rule
=
365 base::StringPrintf("type='signal', interface='%s', path='%s'",
366 interface_name
.c_str(),
367 object_path_
.value().c_str());
368 // Add a match_rule listening NameOwnerChanged for the well-known name
370 const std::string name_owner_changed_match_rule
=
372 "type='signal',interface='org.freedesktop.DBus',"
373 "member='NameOwnerChanged',path='/org/freedesktop/DBus',"
374 "sender='org.freedesktop.DBus',arg0='%s'",
375 service_name_
.c_str());
376 if (AddMatchRuleWithCallback(match_rule
,
377 absolute_signal_name
,
379 AddMatchRuleWithoutCallback(name_owner_changed_match_rule
,
380 "org.freedesktop.DBus.NameOwnerChanged")) {
384 // Try getting the current name owner. It's not guaranteed that we can get
385 // the name owner at this moment, as the service may not yet be started. If
386 // that's the case, we'll get the name owner via NameOwnerChanged signal,
387 // as soon as the service is started.
388 UpdateNameOwnerAndBlock();
391 // Run on_connected_callback in the origin thread.
392 bus_
->PostTaskToOriginThread(
394 base::Bind(&ObjectProxy::OnConnected
,
396 on_connected_callback
,
402 void ObjectProxy::OnConnected(OnConnectedCallback on_connected_callback
,
403 const std::string
& interface_name
,
404 const std::string
& signal_name
,
406 bus_
->AssertOnOriginThread();
408 on_connected_callback
.Run(interface_name
, signal_name
, success
);
411 DBusHandlerResult
ObjectProxy::HandleMessage(
412 DBusConnection
* connection
,
413 DBusMessage
* raw_message
) {
414 bus_
->AssertOnDBusThread();
416 if (dbus_message_get_type(raw_message
) != DBUS_MESSAGE_TYPE_SIGNAL
)
417 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
419 // raw_message will be unrefed on exit of the function. Increment the
420 // reference so we can use it in Signal.
421 dbus_message_ref(raw_message
);
422 scoped_ptr
<Signal
> signal(
423 Signal::FromRawMessage(raw_message
));
425 // Verify the signal comes from the object we're proxying for, this is
426 // our last chance to return DBUS_HANDLER_RESULT_NOT_YET_HANDLED and
427 // allow other object proxies to handle instead.
428 const dbus::ObjectPath path
= signal
->GetPath();
429 if (path
!= object_path_
) {
430 if (path
.value() == kDbusSystemObjectPath
&&
431 signal
->GetMember() == "NameOwnerChanged") {
432 // Handle NameOwnerChanged separately
433 return HandleNameOwnerChanged(signal
.get());
435 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
438 const std::string interface
= signal
->GetInterface();
439 const std::string member
= signal
->GetMember();
441 // Check if we know about the signal.
442 const std::string absolute_signal_name
= GetAbsoluteSignalName(
444 MethodTable::const_iterator iter
= method_table_
.find(absolute_signal_name
);
445 if (iter
== method_table_
.end()) {
446 // Don't know about the signal.
447 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
449 VLOG(1) << "Signal received: " << signal
->ToString();
451 std::string sender
= signal
->GetSender();
452 if (service_name_owner_
!= sender
) {
453 LOG(ERROR
) << "Rejecting a message from a wrong sender.";
454 UMA_HISTOGRAM_COUNTS("DBus.RejectedSignalCount", 1);
455 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
458 const base::TimeTicks start_time
= base::TimeTicks::Now();
459 if (bus_
->HasDBusThread()) {
460 // Post a task to run the method in the origin thread.
461 // Transfer the ownership of |signal| to RunMethod().
462 // |released_signal| will be deleted in RunMethod().
463 Signal
* released_signal
= signal
.release();
464 bus_
->PostTaskToOriginThread(FROM_HERE
,
465 base::Bind(&ObjectProxy::RunMethod
,
471 const base::TimeTicks start_time
= base::TimeTicks::Now();
472 // If the D-Bus thread is not used, just call the callback on the
473 // current thread. Transfer the ownership of |signal| to RunMethod().
474 Signal
* released_signal
= signal
.release();
475 RunMethod(start_time
, iter
->second
, released_signal
);
478 return DBUS_HANDLER_RESULT_HANDLED
;
481 void ObjectProxy::RunMethod(base::TimeTicks start_time
,
482 SignalCallback signal_callback
,
484 bus_
->AssertOnOriginThread();
486 signal_callback
.Run(signal
);
487 // Delete the message on the D-Bus thread. See comments in
488 // RunResponseCallback().
489 bus_
->PostTaskToDBusThread(
491 base::Bind(&base::DeletePointer
<dbus::Signal
>, signal
));
493 // Record time spent for handling the signal.
494 UMA_HISTOGRAM_TIMES("DBus.SignalHandleTime",
495 base::TimeTicks::Now() - start_time
);
498 DBusHandlerResult
ObjectProxy::HandleMessageThunk(
499 DBusConnection
* connection
,
500 DBusMessage
* raw_message
,
502 ObjectProxy
* self
= reinterpret_cast<ObjectProxy
*>(user_data
);
503 return self
->HandleMessage(connection
, raw_message
);
506 void ObjectProxy::LogMethodCallFailure(
507 const base::StringPiece
& interface_name
,
508 const base::StringPiece
& method_name
,
509 const base::StringPiece
& error_name
,
510 const base::StringPiece
& error_message
) const {
511 if (ignore_service_unknown_errors_
&& error_name
== kErrorServiceUnknown
)
513 LOG(ERROR
) << "Failed to call method: "
514 << interface_name
<< "." << method_name
515 << ": object_path= " << object_path_
.value()
516 << ": " << error_name
<< ": " << error_message
;
519 void ObjectProxy::OnCallMethodError(const std::string
& interface_name
,
520 const std::string
& method_name
,
521 ResponseCallback response_callback
,
522 ErrorResponse
* error_response
) {
523 if (error_response
) {
524 // Error message may contain the error message as string.
525 dbus::MessageReader
reader(error_response
);
526 std::string error_message
;
527 reader
.PopString(&error_message
);
528 LogMethodCallFailure(interface_name
,
530 error_response
->GetErrorName(),
533 response_callback
.Run(NULL
);
536 bool ObjectProxy::AddMatchRuleWithCallback(
537 const std::string
& match_rule
,
538 const std::string
& absolute_signal_name
,
539 SignalCallback signal_callback
) {
540 DCHECK(!match_rule
.empty());
541 DCHECK(!absolute_signal_name
.empty());
542 bus_
->AssertOnDBusThread();
544 if (match_rules_
.find(match_rule
) == match_rules_
.end()) {
545 ScopedDBusError error
;
546 bus_
->AddMatch(match_rule
, error
.get());
547 if (error
.is_set()) {
548 LOG(ERROR
) << "Failed to add match rule \"" << match_rule
<< "\". Got " <<
549 error
.name() << ": " << error
.message();
552 // Store the match rule, so that we can remove this in Detach().
553 match_rules_
.insert(match_rule
);
554 // Add the signal callback to the method table.
555 method_table_
[absolute_signal_name
] = signal_callback
;
559 // We already have the match rule.
560 method_table_
[absolute_signal_name
] = signal_callback
;
565 bool ObjectProxy::AddMatchRuleWithoutCallback(
566 const std::string
& match_rule
,
567 const std::string
& absolute_signal_name
) {
568 DCHECK(!match_rule
.empty());
569 DCHECK(!absolute_signal_name
.empty());
570 bus_
->AssertOnDBusThread();
572 if (match_rules_
.find(match_rule
) != match_rules_
.end())
575 ScopedDBusError error
;
576 bus_
->AddMatch(match_rule
, error
.get());
577 if (error
.is_set()) {
578 LOG(ERROR
) << "Failed to add match rule \"" << match_rule
<< "\". Got " <<
579 error
.name() << ": " << error
.message();
582 // Store the match rule, so that we can remove this in Detach().
583 match_rules_
.insert(match_rule
);
587 void ObjectProxy::UpdateNameOwnerAndBlock() {
588 bus_
->AssertOnDBusThread();
590 MethodCall
get_name_owner_call("org.freedesktop.DBus", "GetNameOwner");
591 MessageWriter
writer(&get_name_owner_call
);
592 writer
.AppendString(service_name_
);
593 VLOG(1) << "Method call: " << get_name_owner_call
.ToString();
595 const dbus::ObjectPath
obj_path("/org/freedesktop/DBus");
596 ScopedDBusError error
;
597 if (!get_name_owner_call
.SetDestination("org.freedesktop.DBus") ||
598 !get_name_owner_call
.SetPath(obj_path
)) {
599 LOG(ERROR
) << "Failed to get name owner.";
603 DBusMessage
* response_message
= bus_
->SendWithReplyAndBlock(
604 get_name_owner_call
.raw_message(),
607 if (!response_message
) {
608 LOG(ERROR
) << "Failed to get name owner. Got " << error
.name() << ": " <<
612 scoped_ptr
<Response
> response(Response::FromRawMessage(response_message
));
613 MessageReader
reader(response
.get());
615 std::string new_service_name_owner
;
616 if (reader
.PopString(&new_service_name_owner
))
617 service_name_owner_
= new_service_name_owner
;
619 service_name_owner_
.clear();
622 DBusHandlerResult
ObjectProxy::HandleNameOwnerChanged(Signal
* signal
) {
624 bus_
->AssertOnDBusThread();
626 // Confirm the validity of the NameOwnerChanged signal.
627 if (signal
->GetMember() == "NameOwnerChanged" &&
628 signal
->GetInterface() == "org.freedesktop.DBus" &&
629 signal
->GetSender() == "org.freedesktop.DBus") {
630 MessageReader
reader(signal
);
631 std::string name
, old_owner
, new_owner
;
632 if (reader
.PopString(&name
) &&
633 reader
.PopString(&old_owner
) &&
634 reader
.PopString(&new_owner
) &&
635 name
== service_name_
) {
636 service_name_owner_
= new_owner
;
637 return DBUS_HANDLER_RESULT_HANDLED
;
641 // Untrusted or uninteresting signal
642 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;