Avoid unnecessary writes in ShellWindowGeometryCache
[chromium-blink-merge.git] / dbus / object_proxy.cc
blobc8f00d7cbc35bd200900fb8b93f55b2a96eae115
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.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"
20 namespace {
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
32 // ObjectProxy.
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) {
43 } // namespace
45 namespace dbus {
47 ObjectProxy::ObjectProxy(Bus* bus,
48 const std::string& service_name,
49 const ObjectPath& object_path,
50 int options)
51 : bus_(bus),
52 service_name_(service_name),
53 object_path_(object_path),
54 filter_added_(false),
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,
66 int timeout_ms) {
67 bus_->AssertOnDBusThread();
69 if (!bus_->Connect() ||
70 !method_call->SetDestination(service_name_) ||
71 !method_call->SetPath(object_path_))
72 return NULL;
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() : "");
92 return NULL;
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,
102 int timeout_ms,
103 ResponseCallback callback) {
104 CallMethodWithErrorCallback(method_call, timeout_ms, callback,
105 base::Bind(&ObjectProxy::OnCallMethodError,
106 this,
107 method_call->GetInterface(),
108 method_call->GetMember(),
109 callback));
112 void ObjectProxy::CallMethodWithErrorCallback(MethodCall* method_call,
113 int timeout_ms,
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,
125 this,
126 callback,
127 error_callback,
128 start_time,
129 response_message);
130 bus_->PostTaskToOriginThread(FROM_HERE, task);
131 return;
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,
141 this,
142 timeout_ms,
143 request_message,
144 callback,
145 error_callback,
146 start_time);
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,
159 this,
160 interface_name,
161 signal_name,
162 signal_callback,
163 on_connected_callback));
166 void ObjectProxy::Detach() {
167 bus_->AssertOnDBusThread();
169 if (filter_added_) {
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();
187 // static
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,
217 this,
218 response_callback,
219 error_callback,
220 start_time,
221 response_message);
222 bus_->PostTaskToOriginThread(FROM_HERE, task);
224 dbus_message_unref(request_message);
225 return;
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,
236 start_time);
238 // This returns false only when unable to allocate memory.
239 const bool success = dbus_pending_call_set_notify(
240 pending_call,
241 &ObjectProxy::OnPendingCallIsCompleteThunk,
242 data,
243 NULL);
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,
259 this,
260 response_callback,
261 error_callback,
262 start_time,
263 response_message);
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(
285 FROM_HERE,
286 base::Bind(&base::DeletePointer<dbus::ErrorResponse>,
287 error_response.release()));
288 } else {
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
295 // reason:
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.
306 // */
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(
313 FROM_HERE,
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,
329 void* user_data) {
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,
336 data->start_time);
337 delete data;
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;
359 } else {
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
369 // |service_name_|.
370 const std::string name_owner_changed_match_rule =
371 base::StringPrintf(
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,
378 signal_callback) &&
379 AddMatchRuleWithoutCallback(name_owner_changed_match_rule,
380 "org.freedesktop.DBus.NameOwnerChanged")) {
381 success = true;
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(
393 FROM_HERE,
394 base::Bind(&ObjectProxy::OnConnected,
395 this,
396 on_connected_callback,
397 interface_name,
398 signal_name,
399 success));
402 void ObjectProxy::OnConnected(OnConnectedCallback on_connected_callback,
403 const std::string& interface_name,
404 const std::string& signal_name,
405 bool success) {
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(
443 interface, member);
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,
466 this,
467 start_time,
468 iter->second,
469 released_signal));
470 } else {
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,
483 Signal* signal) {
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(
490 FROM_HERE,
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,
501 void* user_data) {
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)
512 return;
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,
529 method_name,
530 error_response->GetErrorName(),
531 error_message);
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();
550 return false;
551 } else {
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;
556 return true;
558 } else {
559 // We already have the match rule.
560 method_table_[absolute_signal_name] = signal_callback;
561 return true;
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())
573 return true;
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();
580 return false;
582 // Store the match rule, so that we can remove this in Detach().
583 match_rules_.insert(match_rule);
584 return true;
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.";
600 return;
603 DBusMessage* response_message = bus_->SendWithReplyAndBlock(
604 get_name_owner_call.raw_message(),
605 TIMEOUT_USE_DEFAULT,
606 error.get());
607 if (!response_message) {
608 LOG(ERROR) << "Failed to get name owner. Got " << error.name() << ": " <<
609 error.message();
610 return;
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;
618 else
619 service_name_owner_.clear();
622 DBusHandlerResult ObjectProxy::HandleNameOwnerChanged(Signal* signal) {
623 DCHECK(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;
645 } // namespace dbus