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/strings/string_piece.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/threading/thread.h"
14 #include "base/threading/thread_restrictions.h"
15 #include "dbus/dbus_statistics.h"
16 #include "dbus/message.h"
17 #include "dbus/object_path.h"
18 #include "dbus/object_proxy.h"
19 #include "dbus/scoped_dbus_error.h"
25 const char kErrorServiceUnknown
[] = "org.freedesktop.DBus.Error.ServiceUnknown";
27 // Used for success ratio histograms. 1 for success, 0 for failure.
28 const int kSuccessRatioHistogramMaxValue
= 2;
30 // The path of D-Bus Object sending NameOwnerChanged signal.
31 const char kDBusSystemObjectPath
[] = "/org/freedesktop/DBus";
33 // The D-Bus Object interface.
34 const char kDBusSystemObjectInterface
[] = "org.freedesktop.DBus";
36 // The D-Bus Object address.
37 const char kDBusSystemObjectAddress
[] = "org.freedesktop.DBus";
39 // The NameOwnerChanged member in |kDBusSystemObjectInterface|.
40 const char kNameOwnerChangedMember
[] = "NameOwnerChanged";
42 // Gets the absolute signal name by concatenating the interface name and
43 // the signal name. Used for building keys for method_table_ in
45 std::string
GetAbsoluteSignalName(
46 const std::string
& interface_name
,
47 const std::string
& signal_name
) {
48 return interface_name
+ "." + signal_name
;
51 // An empty function used for ObjectProxy::EmptyResponseCallback().
52 void EmptyResponseCallbackBody(Response
* /*response*/) {
57 ObjectProxy::ObjectProxy(Bus
* bus
,
58 const std::string
& service_name
,
59 const ObjectPath
& object_path
,
62 service_name_(service_name
),
63 object_path_(object_path
),
65 ignore_service_unknown_errors_(
66 options
& IGNORE_SERVICE_UNKNOWN_ERRORS
) {
69 ObjectProxy::~ObjectProxy() {
72 // Originally we tried to make |method_call| a const reference, but we
73 // gave up as dbus_connection_send_with_reply_and_block() takes a
74 // non-const pointer of DBusMessage as the second parameter.
75 scoped_ptr
<Response
> ObjectProxy::CallMethodAndBlock(MethodCall
* method_call
,
77 bus_
->AssertOnDBusThread();
79 if (!bus_
->Connect() ||
80 !method_call
->SetDestination(service_name_
) ||
81 !method_call
->SetPath(object_path_
))
82 return scoped_ptr
<Response
>();
84 DBusMessage
* request_message
= method_call
->raw_message();
86 ScopedDBusError error
;
88 // Send the message synchronously.
89 const base::TimeTicks start_time
= base::TimeTicks::Now();
90 DBusMessage
* response_message
=
91 bus_
->SendWithReplyAndBlock(request_message
, timeout_ms
, error
.get());
92 // Record if the method call is successful, or not. 1 if successful.
93 UMA_HISTOGRAM_ENUMERATION("DBus.SyncMethodCallSuccess",
94 response_message
? 1 : 0,
95 kSuccessRatioHistogramMaxValue
);
96 statistics::AddBlockingSentMethodCall(service_name_
,
97 method_call
->GetInterface(),
98 method_call
->GetMember());
100 if (!response_message
) {
101 LogMethodCallFailure(method_call
->GetInterface(),
102 method_call
->GetMember(),
103 error
.is_set() ? error
.name() : "unknown error type",
104 error
.is_set() ? error
.message() : "");
105 return scoped_ptr
<Response
>();
107 // Record time spent for the method call. Don't include failures.
108 UMA_HISTOGRAM_TIMES("DBus.SyncMethodCallTime",
109 base::TimeTicks::Now() - start_time
);
111 return Response::FromRawMessage(response_message
);
114 void ObjectProxy::CallMethod(MethodCall
* method_call
,
116 ResponseCallback callback
) {
117 CallMethodWithErrorCallback(method_call
, timeout_ms
, callback
,
118 base::Bind(&ObjectProxy::OnCallMethodError
,
120 method_call
->GetInterface(),
121 method_call
->GetMember(),
125 void ObjectProxy::CallMethodWithErrorCallback(MethodCall
* method_call
,
127 ResponseCallback callback
,
128 ErrorCallback error_callback
) {
129 bus_
->AssertOnOriginThread();
131 const base::TimeTicks start_time
= base::TimeTicks::Now();
133 if (!method_call
->SetDestination(service_name_
) ||
134 !method_call
->SetPath(object_path_
)) {
135 // In case of a failure, run the error callback with NULL.
136 DBusMessage
* response_message
= NULL
;
137 base::Closure task
= base::Bind(&ObjectProxy::RunResponseCallback
,
143 bus_
->PostTaskToOriginThread(FROM_HERE
, task
);
147 // Increment the reference count so we can safely reference the
148 // underlying request message until the method call is complete. This
149 // will be unref'ed in StartAsyncMethodCall().
150 DBusMessage
* request_message
= method_call
->raw_message();
151 dbus_message_ref(request_message
);
153 base::Closure task
= base::Bind(&ObjectProxy::StartAsyncMethodCall
,
160 statistics::AddSentMethodCall(service_name_
,
161 method_call
->GetInterface(),
162 method_call
->GetMember());
164 // Wait for the response in the D-Bus thread.
165 bus_
->PostTaskToDBusThread(FROM_HERE
, task
);
168 void ObjectProxy::ConnectToSignal(const std::string
& interface_name
,
169 const std::string
& signal_name
,
170 SignalCallback signal_callback
,
171 OnConnectedCallback on_connected_callback
) {
172 bus_
->AssertOnOriginThread();
174 bus_
->PostTaskToDBusThread(FROM_HERE
,
175 base::Bind(&ObjectProxy::ConnectToSignalInternal
,
180 on_connected_callback
));
183 void ObjectProxy::Detach() {
184 bus_
->AssertOnDBusThread();
187 if (!bus_
->RemoveFilterFunction(&ObjectProxy::HandleMessageThunk
, this)) {
188 LOG(ERROR
) << "Failed to remove filter function";
192 for (std::set
<std::string
>::iterator iter
= match_rules_
.begin();
193 iter
!= match_rules_
.end(); ++iter
) {
194 ScopedDBusError error
;
195 bus_
->RemoveMatch(*iter
, error
.get());
196 if (error
.is_set()) {
197 // There is nothing we can do to recover, so just print the error.
198 LOG(ERROR
) << "Failed to remove match rule: " << *iter
;
201 match_rules_
.clear();
205 ObjectProxy::ResponseCallback
ObjectProxy::EmptyResponseCallback() {
206 return base::Bind(&EmptyResponseCallbackBody
);
209 ObjectProxy::OnPendingCallIsCompleteData::OnPendingCallIsCompleteData(
210 ObjectProxy
* in_object_proxy
,
211 ResponseCallback in_response_callback
,
212 ErrorCallback in_error_callback
,
213 base::TimeTicks in_start_time
)
214 : object_proxy(in_object_proxy
),
215 response_callback(in_response_callback
),
216 error_callback(in_error_callback
),
217 start_time(in_start_time
) {
220 ObjectProxy::OnPendingCallIsCompleteData::~OnPendingCallIsCompleteData() {
223 void ObjectProxy::StartAsyncMethodCall(int timeout_ms
,
224 DBusMessage
* request_message
,
225 ResponseCallback response_callback
,
226 ErrorCallback error_callback
,
227 base::TimeTicks start_time
) {
228 bus_
->AssertOnDBusThread();
230 if (!bus_
->Connect() || !bus_
->SetUpAsyncOperations()) {
231 // In case of a failure, run the error callback with NULL.
232 DBusMessage
* response_message
= NULL
;
233 base::Closure task
= base::Bind(&ObjectProxy::RunResponseCallback
,
239 bus_
->PostTaskToOriginThread(FROM_HERE
, task
);
241 dbus_message_unref(request_message
);
245 DBusPendingCall
* pending_call
= NULL
;
247 bus_
->SendWithReply(request_message
, &pending_call
, timeout_ms
);
249 // Prepare the data we'll be passing to OnPendingCallIsCompleteThunk().
250 // The data will be deleted in OnPendingCallIsCompleteThunk().
251 OnPendingCallIsCompleteData
* data
=
252 new OnPendingCallIsCompleteData(this, response_callback
, error_callback
,
255 // This returns false only when unable to allocate memory.
256 const bool success
= dbus_pending_call_set_notify(
258 &ObjectProxy::OnPendingCallIsCompleteThunk
,
261 CHECK(success
) << "Unable to allocate memory";
262 dbus_pending_call_unref(pending_call
);
264 // It's now safe to unref the request message.
265 dbus_message_unref(request_message
);
268 void ObjectProxy::OnPendingCallIsComplete(DBusPendingCall
* pending_call
,
269 ResponseCallback response_callback
,
270 ErrorCallback error_callback
,
271 base::TimeTicks start_time
) {
272 bus_
->AssertOnDBusThread();
274 DBusMessage
* response_message
= dbus_pending_call_steal_reply(pending_call
);
275 base::Closure task
= base::Bind(&ObjectProxy::RunResponseCallback
,
281 bus_
->PostTaskToOriginThread(FROM_HERE
, task
);
284 void ObjectProxy::RunResponseCallback(ResponseCallback response_callback
,
285 ErrorCallback error_callback
,
286 base::TimeTicks start_time
,
287 DBusMessage
* response_message
) {
288 bus_
->AssertOnOriginThread();
290 bool method_call_successful
= false;
291 if (!response_message
) {
292 // The response is not received.
293 error_callback
.Run(NULL
);
294 } else if (dbus_message_get_type(response_message
) ==
295 DBUS_MESSAGE_TYPE_ERROR
) {
296 // This will take |response_message| and release (unref) it.
297 scoped_ptr
<ErrorResponse
> error_response(
298 ErrorResponse::FromRawMessage(response_message
));
299 error_callback
.Run(error_response
.get());
300 // Delete the message on the D-Bus thread. See below for why.
301 bus_
->PostTaskToDBusThread(
303 base::Bind(&base::DeletePointer
<ErrorResponse
>,
304 error_response
.release()));
306 // This will take |response_message| and release (unref) it.
307 scoped_ptr
<Response
> response(Response::FromRawMessage(response_message
));
308 // The response is successfully received.
309 response_callback
.Run(response
.get());
310 // The message should be deleted on the D-Bus thread for a complicated
313 // libdbus keeps track of the number of bytes in the incoming message
314 // queue to ensure that the data size in the queue is manageable. The
315 // bookkeeping is partly done via dbus_message_unref(), and immediately
316 // asks the client code (Chrome) to stop monitoring the underlying
317 // socket, if the number of bytes exceeds a certian number, which is set
318 // to 63MB, per dbus-transport.cc:
320 // /* Try to default to something that won't totally hose the system,
321 // * but doesn't impose too much of a limitation.
323 // transport->max_live_messages_size = _DBUS_ONE_MEGABYTE * 63;
325 // The monitoring of the socket is done on the D-Bus thread (see Watch
326 // class in bus.cc), hence we should stop the monitoring from D-Bus
327 // thread, not from the current thread here, which is likely UI thread.
328 bus_
->PostTaskToDBusThread(
330 base::Bind(&base::DeletePointer
<Response
>, response
.release()));
332 method_call_successful
= true;
333 // Record time spent for the method call. Don't include failures.
334 UMA_HISTOGRAM_TIMES("DBus.AsyncMethodCallTime",
335 base::TimeTicks::Now() - start_time
);
337 // Record if the method call is successful, or not. 1 if successful.
338 UMA_HISTOGRAM_ENUMERATION("DBus.AsyncMethodCallSuccess",
339 method_call_successful
,
340 kSuccessRatioHistogramMaxValue
);
343 void ObjectProxy::OnPendingCallIsCompleteThunk(DBusPendingCall
* pending_call
,
345 OnPendingCallIsCompleteData
* data
=
346 reinterpret_cast<OnPendingCallIsCompleteData
*>(user_data
);
347 ObjectProxy
* self
= data
->object_proxy
;
348 self
->OnPendingCallIsComplete(pending_call
,
349 data
->response_callback
,
350 data
->error_callback
,
355 void ObjectProxy::ConnectToSignalInternal(
356 const std::string
& interface_name
,
357 const std::string
& signal_name
,
358 SignalCallback signal_callback
,
359 OnConnectedCallback on_connected_callback
) {
360 bus_
->AssertOnDBusThread();
362 const std::string absolute_signal_name
=
363 GetAbsoluteSignalName(interface_name
, signal_name
);
365 // Will become true, if everything is successful.
366 bool success
= false;
368 if (bus_
->Connect() && bus_
->SetUpAsyncOperations()) {
369 // We should add the filter only once. Otherwise, HandleMessage() will
370 // be called more than once.
371 if (!filter_added_
) {
372 if (bus_
->AddFilterFunction(&ObjectProxy::HandleMessageThunk
, this)) {
373 filter_added_
= true;
375 LOG(ERROR
) << "Failed to add filter function";
378 // Add a match rule so the signal goes through HandleMessage().
379 const std::string match_rule
=
380 base::StringPrintf("type='signal', interface='%s', path='%s'",
381 interface_name
.c_str(),
382 object_path_
.value().c_str());
383 // Add a match_rule listening NameOwnerChanged for the well-known name
385 const std::string name_owner_changed_match_rule
=
387 "type='signal',interface='org.freedesktop.DBus',"
388 "member='NameOwnerChanged',path='/org/freedesktop/DBus',"
389 "sender='org.freedesktop.DBus',arg0='%s'",
390 service_name_
.c_str());
391 if (AddMatchRuleWithCallback(match_rule
,
392 absolute_signal_name
,
394 AddMatchRuleWithoutCallback(name_owner_changed_match_rule
,
395 "org.freedesktop.DBus.NameOwnerChanged")) {
399 // Try getting the current name owner. It's not guaranteed that we can get
400 // the name owner at this moment, as the service may not yet be started. If
401 // that's the case, we'll get the name owner via NameOwnerChanged signal,
402 // as soon as the service is started.
403 UpdateNameOwnerAndBlock();
406 // Run on_connected_callback in the origin thread.
407 bus_
->PostTaskToOriginThread(
409 base::Bind(&ObjectProxy::OnConnected
,
411 on_connected_callback
,
417 void ObjectProxy::OnConnected(OnConnectedCallback on_connected_callback
,
418 const std::string
& interface_name
,
419 const std::string
& signal_name
,
421 bus_
->AssertOnOriginThread();
423 on_connected_callback
.Run(interface_name
, signal_name
, success
);
426 void ObjectProxy::SetNameOwnerChangedCallback(SignalCallback callback
) {
427 bus_
->AssertOnOriginThread();
429 name_owner_changed_callback_
= callback
;
432 DBusHandlerResult
ObjectProxy::HandleMessage(
433 DBusConnection
* connection
,
434 DBusMessage
* raw_message
) {
435 bus_
->AssertOnDBusThread();
437 if (dbus_message_get_type(raw_message
) != DBUS_MESSAGE_TYPE_SIGNAL
)
438 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
440 // raw_message will be unrefed on exit of the function. Increment the
441 // reference so we can use it in Signal.
442 dbus_message_ref(raw_message
);
443 scoped_ptr
<Signal
> signal(
444 Signal::FromRawMessage(raw_message
));
446 // Verify the signal comes from the object we're proxying for, this is
447 // our last chance to return DBUS_HANDLER_RESULT_NOT_YET_HANDLED and
448 // allow other object proxies to handle instead.
449 const ObjectPath path
= signal
->GetPath();
450 if (path
!= object_path_
) {
451 if (path
.value() == kDBusSystemObjectPath
&&
452 signal
->GetMember() == kNameOwnerChangedMember
) {
453 // Handle NameOwnerChanged separately
454 return HandleNameOwnerChanged(signal
.Pass());
456 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
459 const std::string interface
= signal
->GetInterface();
460 const std::string member
= signal
->GetMember();
462 statistics::AddReceivedSignal(service_name_
, interface
, member
);
464 // Check if we know about the signal.
465 const std::string absolute_signal_name
= GetAbsoluteSignalName(
467 MethodTable::const_iterator iter
= method_table_
.find(absolute_signal_name
);
468 if (iter
== method_table_
.end()) {
469 // Don't know about the signal.
470 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
472 VLOG(1) << "Signal received: " << signal
->ToString();
474 std::string sender
= signal
->GetSender();
475 if (service_name_owner_
!= sender
) {
476 LOG(ERROR
) << "Rejecting a message from a wrong sender.";
477 UMA_HISTOGRAM_COUNTS("DBus.RejectedSignalCount", 1);
478 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;
481 const base::TimeTicks start_time
= base::TimeTicks::Now();
482 if (bus_
->HasDBusThread()) {
483 // Post a task to run the method in the origin thread.
484 // Transfer the ownership of |signal| to RunMethod().
485 // |released_signal| will be deleted in RunMethod().
486 Signal
* released_signal
= signal
.release();
487 bus_
->PostTaskToOriginThread(FROM_HERE
,
488 base::Bind(&ObjectProxy::RunMethod
,
494 const base::TimeTicks start_time
= base::TimeTicks::Now();
495 // If the D-Bus thread is not used, just call the callback on the
496 // current thread. Transfer the ownership of |signal| to RunMethod().
497 Signal
* released_signal
= signal
.release();
498 RunMethod(start_time
, iter
->second
, released_signal
);
501 return DBUS_HANDLER_RESULT_HANDLED
;
504 void ObjectProxy::RunMethod(base::TimeTicks start_time
,
505 std::vector
<SignalCallback
> signal_callbacks
,
507 bus_
->AssertOnOriginThread();
509 for (std::vector
<SignalCallback
>::iterator iter
= signal_callbacks
.begin();
510 iter
!= signal_callbacks
.end(); ++iter
)
513 // Delete the message on the D-Bus thread. See comments in
514 // RunResponseCallback().
515 bus_
->PostTaskToDBusThread(
517 base::Bind(&base::DeletePointer
<Signal
>, signal
));
519 // Record time spent for handling the signal.
520 UMA_HISTOGRAM_TIMES("DBus.SignalHandleTime",
521 base::TimeTicks::Now() - start_time
);
524 DBusHandlerResult
ObjectProxy::HandleMessageThunk(
525 DBusConnection
* connection
,
526 DBusMessage
* raw_message
,
528 ObjectProxy
* self
= reinterpret_cast<ObjectProxy
*>(user_data
);
529 return self
->HandleMessage(connection
, raw_message
);
532 void ObjectProxy::LogMethodCallFailure(
533 const base::StringPiece
& interface_name
,
534 const base::StringPiece
& method_name
,
535 const base::StringPiece
& error_name
,
536 const base::StringPiece
& error_message
) const {
537 if (ignore_service_unknown_errors_
&& error_name
== kErrorServiceUnknown
)
539 LOG(ERROR
) << "Failed to call method: "
540 << interface_name
<< "." << method_name
541 << ": object_path= " << object_path_
.value()
542 << ": " << error_name
<< ": " << error_message
;
545 void ObjectProxy::OnCallMethodError(const std::string
& interface_name
,
546 const std::string
& method_name
,
547 ResponseCallback response_callback
,
548 ErrorResponse
* error_response
) {
549 if (error_response
) {
550 // Error message may contain the error message as string.
551 MessageReader
reader(error_response
);
552 std::string error_message
;
553 reader
.PopString(&error_message
);
554 LogMethodCallFailure(interface_name
,
556 error_response
->GetErrorName(),
559 response_callback
.Run(NULL
);
562 bool ObjectProxy::AddMatchRuleWithCallback(
563 const std::string
& match_rule
,
564 const std::string
& absolute_signal_name
,
565 SignalCallback signal_callback
) {
566 DCHECK(!match_rule
.empty());
567 DCHECK(!absolute_signal_name
.empty());
568 bus_
->AssertOnDBusThread();
570 if (match_rules_
.find(match_rule
) == match_rules_
.end()) {
571 ScopedDBusError error
;
572 bus_
->AddMatch(match_rule
, error
.get());
573 if (error
.is_set()) {
574 LOG(ERROR
) << "Failed to add match rule \"" << match_rule
<< "\". Got "
575 << error
.name() << ": " << error
.message();
578 // Store the match rule, so that we can remove this in Detach().
579 match_rules_
.insert(match_rule
);
580 // Add the signal callback to the method table.
581 method_table_
[absolute_signal_name
].push_back(signal_callback
);
585 // We already have the match rule.
586 method_table_
[absolute_signal_name
].push_back(signal_callback
);
591 bool ObjectProxy::AddMatchRuleWithoutCallback(
592 const std::string
& match_rule
,
593 const std::string
& absolute_signal_name
) {
594 DCHECK(!match_rule
.empty());
595 DCHECK(!absolute_signal_name
.empty());
596 bus_
->AssertOnDBusThread();
598 if (match_rules_
.find(match_rule
) != match_rules_
.end())
601 ScopedDBusError error
;
602 bus_
->AddMatch(match_rule
, error
.get());
603 if (error
.is_set()) {
604 LOG(ERROR
) << "Failed to add match rule \"" << match_rule
<< "\". Got "
605 << error
.name() << ": " << error
.message();
608 // Store the match rule, so that we can remove this in Detach().
609 match_rules_
.insert(match_rule
);
613 void ObjectProxy::UpdateNameOwnerAndBlock() {
614 bus_
->AssertOnDBusThread();
615 service_name_owner_
=
616 bus_
->GetServiceOwnerAndBlock(service_name_
, Bus::REPORT_ERRORS
);
619 DBusHandlerResult
ObjectProxy::HandleNameOwnerChanged(
620 scoped_ptr
<Signal
> signal
) {
622 bus_
->AssertOnDBusThread();
624 // Confirm the validity of the NameOwnerChanged signal.
625 if (signal
->GetMember() == kNameOwnerChangedMember
&&
626 signal
->GetInterface() == kDBusSystemObjectInterface
&&
627 signal
->GetSender() == kDBusSystemObjectAddress
) {
628 MessageReader
reader(signal
.get());
629 std::string name
, old_owner
, new_owner
;
630 if (reader
.PopString(&name
) &&
631 reader
.PopString(&old_owner
) &&
632 reader
.PopString(&new_owner
) &&
633 name
== service_name_
) {
634 service_name_owner_
= new_owner
;
635 if (!name_owner_changed_callback_
.is_null()) {
636 const base::TimeTicks start_time
= base::TimeTicks::Now();
637 Signal
* released_signal
= signal
.release();
638 std::vector
<SignalCallback
> callbacks
;
639 callbacks
.push_back(name_owner_changed_callback_
);
640 bus_
->PostTaskToOriginThread(FROM_HERE
,
641 base::Bind(&ObjectProxy::RunMethod
,
650 // Always return unhandled to let other object proxies handle the same
652 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED
;