SimpleCache: add an histogram to record the age of entry on open
[chromium-blink-merge.git] / dbus / object_proxy.cc
blobea76fd72f091f9393d647e7da5f6ebfe85468a1f
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/bus.h"
7 #include "base/bind.h"
8 #include "base/logging.h"
9 #include "base/message_loop/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"
21 namespace dbus {
23 namespace {
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
44 // ObjectProxy.
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*/) {
55 } // namespace
57 ObjectProxy::ObjectProxy(Bus* bus,
58 const std::string& service_name,
59 const ObjectPath& object_path,
60 int options)
61 : bus_(bus),
62 service_name_(service_name),
63 object_path_(object_path),
64 filter_added_(false),
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,
76 int timeout_ms) {
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,
115 int timeout_ms,
116 ResponseCallback callback) {
117 CallMethodWithErrorCallback(method_call, timeout_ms, callback,
118 base::Bind(&ObjectProxy::OnCallMethodError,
119 this,
120 method_call->GetInterface(),
121 method_call->GetMember(),
122 callback));
125 void ObjectProxy::CallMethodWithErrorCallback(MethodCall* method_call,
126 int timeout_ms,
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,
138 this,
139 callback,
140 error_callback,
141 start_time,
142 response_message);
143 bus_->PostTaskToOriginThread(FROM_HERE, task);
144 return;
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,
154 this,
155 timeout_ms,
156 request_message,
157 callback,
158 error_callback,
159 start_time);
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,
176 this,
177 interface_name,
178 signal_name,
179 signal_callback,
180 on_connected_callback));
183 void ObjectProxy::Detach() {
184 bus_->AssertOnDBusThread();
186 if (filter_added_) {
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();
204 // static
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,
234 this,
235 response_callback,
236 error_callback,
237 start_time,
238 response_message);
239 bus_->PostTaskToOriginThread(FROM_HERE, task);
241 dbus_message_unref(request_message);
242 return;
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,
253 start_time);
255 // This returns false only when unable to allocate memory.
256 const bool success = dbus_pending_call_set_notify(
257 pending_call,
258 &ObjectProxy::OnPendingCallIsCompleteThunk,
259 data,
260 NULL);
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,
276 this,
277 response_callback,
278 error_callback,
279 start_time,
280 response_message);
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(
302 FROM_HERE,
303 base::Bind(&base::DeletePointer<ErrorResponse>,
304 error_response.release()));
305 } else {
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
311 // reason:
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.
322 // */
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(
329 FROM_HERE,
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,
344 void* user_data) {
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,
351 data->start_time);
352 delete data;
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;
374 } else {
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
384 // |service_name_|.
385 const std::string name_owner_changed_match_rule =
386 base::StringPrintf(
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,
393 signal_callback) &&
394 AddMatchRuleWithoutCallback(name_owner_changed_match_rule,
395 "org.freedesktop.DBus.NameOwnerChanged")) {
396 success = true;
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(
408 FROM_HERE,
409 base::Bind(&ObjectProxy::OnConnected,
410 this,
411 on_connected_callback,
412 interface_name,
413 signal_name,
414 success));
417 void ObjectProxy::OnConnected(OnConnectedCallback on_connected_callback,
418 const std::string& interface_name,
419 const std::string& signal_name,
420 bool success) {
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(
466 interface, member);
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,
489 this,
490 start_time,
491 iter->second,
492 released_signal));
493 } else {
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,
506 Signal* signal) {
507 bus_->AssertOnOriginThread();
509 for (std::vector<SignalCallback>::iterator iter = signal_callbacks.begin();
510 iter != signal_callbacks.end(); ++iter)
511 iter->Run(signal);
513 // Delete the message on the D-Bus thread. See comments in
514 // RunResponseCallback().
515 bus_->PostTaskToDBusThread(
516 FROM_HERE,
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,
527 void* user_data) {
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)
538 return;
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,
555 method_name,
556 error_response->GetErrorName(),
557 error_message);
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();
576 return false;
577 } else {
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);
582 return true;
584 } else {
585 // We already have the match rule.
586 method_table_[absolute_signal_name].push_back(signal_callback);
587 return true;
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())
599 return true;
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();
606 return false;
608 // Store the match rule, so that we can remove this in Detach().
609 match_rules_.insert(match_rule);
610 return true;
613 void ObjectProxy::UpdateNameOwnerAndBlock() {
614 bus_->AssertOnDBusThread();
615 // Errors should be suppressed here, as the service may not be yet running
616 // when connecting to signals of the service, which is just fine.
617 // The ObjectProxy will be notified when the service is launched via
618 // NameOwnerChanged signal. See also comments in ConnectToSignalInternal().
619 service_name_owner_ =
620 bus_->GetServiceOwnerAndBlock(service_name_, Bus::SUPPRESS_ERRORS);
623 DBusHandlerResult ObjectProxy::HandleNameOwnerChanged(
624 scoped_ptr<Signal> signal) {
625 DCHECK(signal);
626 bus_->AssertOnDBusThread();
628 // Confirm the validity of the NameOwnerChanged signal.
629 if (signal->GetMember() == kNameOwnerChangedMember &&
630 signal->GetInterface() == kDBusSystemObjectInterface &&
631 signal->GetSender() == kDBusSystemObjectAddress) {
632 MessageReader reader(signal.get());
633 std::string name, old_owner, new_owner;
634 if (reader.PopString(&name) &&
635 reader.PopString(&old_owner) &&
636 reader.PopString(&new_owner) &&
637 name == service_name_) {
638 service_name_owner_ = new_owner;
639 if (!name_owner_changed_callback_.is_null()) {
640 const base::TimeTicks start_time = base::TimeTicks::Now();
641 Signal* released_signal = signal.release();
642 std::vector<SignalCallback> callbacks;
643 callbacks.push_back(name_owner_changed_callback_);
644 bus_->PostTaskToOriginThread(FROM_HERE,
645 base::Bind(&ObjectProxy::RunMethod,
646 this,
647 start_time,
648 callbacks,
649 released_signal));
654 // Always return unhandled to let other object proxies handle the same
655 // signal.
656 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
659 } // namespace dbus