blob: d0b660b600dbc368fc28e4804d905cc5b90e6198 [file] [log] [blame]
adamk@chromium.org35c0eef2012-02-11 06:45:23 +09001// Copyright (c) 2012 The Chromium Authors. All rights reserved.
satorux@chromium.org163f1cb2011-08-18 05:58:12 +09002// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "dbus/bus.h"
6
7#include "base/bind.h"
8#include "base/logging.h"
avi@chromium.orga29af562013-07-18 08:00:30 +09009#include "base/message_loop/message_loop.h"
satorux@chromium.org5a92cf32011-09-07 05:53:30 +090010#include "base/metrics/histogram.h"
tfarina@chromium.org0eed10b2013-04-18 06:42:40 +090011#include "base/strings/string_piece.h"
avi@chromium.orgffcdb952013-06-11 16:27:01 +090012#include "base/strings/stringprintf.h"
hashimoto@chromium.orgce5c6152013-09-26 15:40:04 +090013#include "base/task_runner_util.h"
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090014#include "base/threading/thread.h"
15#include "base/threading/thread_restrictions.h"
stevenjb@chromium.org3199bd12012-11-15 06:03:29 +090016#include "dbus/dbus_statistics.h"
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090017#include "dbus/message.h"
keybuk@google.combf4649a2012-02-15 06:29:06 +090018#include "dbus/object_path.h"
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090019#include "dbus/object_proxy.h"
20#include "dbus/scoped_dbus_error.h"
21
thestig@chromium.org56057f22013-05-05 00:48:37 +090022namespace dbus {
23
satorux@chromium.org7f0c4512011-08-23 16:29:21 +090024namespace {
25
adamk@chromium.org35c0eef2012-02-11 06:45:23 +090026const char kErrorServiceUnknown[] = "org.freedesktop.DBus.Error.ServiceUnknown";
stevenjb@chromium.orgc415c482014-08-09 06:57:19 +090027const char kErrorObjectUnknown[] = "org.freedesktop.DBus.Error.UnknownObject";
adamk@chromium.org35c0eef2012-02-11 06:45:23 +090028
satorux@chromium.org5a92cf32011-09-07 05:53:30 +090029// Used for success ratio histograms. 1 for success, 0 for failure.
30const int kSuccessRatioHistogramMaxValue = 2;
31
haruki@chromium.orgc5623ec2012-10-29 15:27:33 +090032// The path of D-Bus Object sending NameOwnerChanged signal.
stevenjb@chromium.org3199bd12012-11-15 06:03:29 +090033const char kDBusSystemObjectPath[] = "/org/freedesktop/DBus";
haruki@chromium.orgc5623ec2012-10-29 15:27:33 +090034
thestig@chromium.org56057f22013-05-05 00:48:37 +090035// The D-Bus Object interface.
36const char kDBusSystemObjectInterface[] = "org.freedesktop.DBus";
37
38// The D-Bus Object address.
39const char kDBusSystemObjectAddress[] = "org.freedesktop.DBus";
40
41// The NameOwnerChanged member in |kDBusSystemObjectInterface|.
42const char kNameOwnerChangedMember[] = "NameOwnerChanged";
43
satorux@chromium.org7f0c4512011-08-23 16:29:21 +090044// Gets the absolute signal name by concatenating the interface name and
45// the signal name. Used for building keys for method_table_ in
46// ObjectProxy.
47std::string GetAbsoluteSignalName(
48 const std::string& interface_name,
49 const std::string& signal_name) {
50 return interface_name + "." + signal_name;
51}
52
satorux@chromium.orgb1753152011-11-11 17:36:22 +090053// An empty function used for ObjectProxy::EmptyResponseCallback().
thestig@chromium.org56057f22013-05-05 00:48:37 +090054void EmptyResponseCallbackBody(Response* /*response*/) {
satorux@chromium.orgb1753152011-11-11 17:36:22 +090055}
56
satorux@chromium.org7f0c4512011-08-23 16:29:21 +090057} // namespace
58
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090059ObjectProxy::ObjectProxy(Bus* bus,
60 const std::string& service_name,
keybuk@google.combf4649a2012-02-15 06:29:06 +090061 const ObjectPath& object_path,
adamk@chromium.org35c0eef2012-02-11 06:45:23 +090062 int options)
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090063 : bus_(bus),
64 service_name_(service_name),
satorux@chromium.org7f0c4512011-08-23 16:29:21 +090065 object_path_(object_path),
adamk@chromium.org35c0eef2012-02-11 06:45:23 +090066 filter_added_(false),
67 ignore_service_unknown_errors_(
68 options & IGNORE_SERVICE_UNKNOWN_ERRORS) {
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090069}
70
71ObjectProxy::~ObjectProxy() {
72}
73
74// Originally we tried to make |method_call| a const reference, but we
75// gave up as dbus_connection_send_with_reply_and_block() takes a
76// non-const pointer of DBusMessage as the second parameter.
yuki@chromium.orgd4eedf82013-02-07 18:46:24 +090077scoped_ptr<Response> ObjectProxy::CallMethodAndBlock(MethodCall* method_call,
78 int timeout_ms) {
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090079 bus_->AssertOnDBusThread();
80
hashimoto@chromium.orgb0305512012-05-23 15:55:22 +090081 if (!bus_->Connect() ||
82 !method_call->SetDestination(service_name_) ||
83 !method_call->SetPath(object_path_))
yuki@chromium.orgd4eedf82013-02-07 18:46:24 +090084 return scoped_ptr<Response>();
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090085
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090086 DBusMessage* request_message = method_call->raw_message();
87
88 ScopedDBusError error;
89
90 // Send the message synchronously.
satorux@chromium.org5a92cf32011-09-07 05:53:30 +090091 const base::TimeTicks start_time = base::TimeTicks::Now();
satorux@chromium.org163f1cb2011-08-18 05:58:12 +090092 DBusMessage* response_message =
93 bus_->SendWithReplyAndBlock(request_message, timeout_ms, error.get());
satorux@chromium.org5a92cf32011-09-07 05:53:30 +090094 // Record if the method call is successful, or not. 1 if successful.
95 UMA_HISTOGRAM_ENUMERATION("DBus.SyncMethodCallSuccess",
96 response_message ? 1 : 0,
97 kSuccessRatioHistogramMaxValue);
stevenjb@chromium.org3199bd12012-11-15 06:03:29 +090098 statistics::AddBlockingSentMethodCall(service_name_,
99 method_call->GetInterface(),
100 method_call->GetMember());
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900101
102 if (!response_message) {
satorux@chromium.org31bb21e2012-05-31 15:12:11 +0900103 LogMethodCallFailure(method_call->GetInterface(),
104 method_call->GetMember(),
105 error.is_set() ? error.name() : "unknown error type",
adamk@chromium.org35c0eef2012-02-11 06:45:23 +0900106 error.is_set() ? error.message() : "");
yuki@chromium.orgd4eedf82013-02-07 18:46:24 +0900107 return scoped_ptr<Response>();
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900108 }
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900109 // Record time spent for the method call. Don't include failures.
110 UMA_HISTOGRAM_TIMES("DBus.SyncMethodCallTime",
111 base::TimeTicks::Now() - start_time);
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900112
satorux@chromium.orgffa83a92011-08-24 12:32:06 +0900113 return Response::FromRawMessage(response_message);
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900114}
115
116void ObjectProxy::CallMethod(MethodCall* method_call,
117 int timeout_ms,
118 ResponseCallback callback) {
hashimoto@chromium.org0d2477e2012-04-20 12:18:27 +0900119 CallMethodWithErrorCallback(method_call, timeout_ms, callback,
120 base::Bind(&ObjectProxy::OnCallMethodError,
121 this,
satorux@chromium.org31bb21e2012-05-31 15:12:11 +0900122 method_call->GetInterface(),
123 method_call->GetMember(),
hashimoto@chromium.org0d2477e2012-04-20 12:18:27 +0900124 callback));
125}
126
127void ObjectProxy::CallMethodWithErrorCallback(MethodCall* method_call,
128 int timeout_ms,
129 ResponseCallback callback,
130 ErrorCallback error_callback) {
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900131 bus_->AssertOnOriginThread();
132
hashimoto@chromium.orgb0305512012-05-23 15:55:22 +0900133 const base::TimeTicks start_time = base::TimeTicks::Now();
134
135 if (!method_call->SetDestination(service_name_) ||
136 !method_call->SetPath(object_path_)) {
137 // In case of a failure, run the error callback with NULL.
138 DBusMessage* response_message = NULL;
139 base::Closure task = base::Bind(&ObjectProxy::RunResponseCallback,
140 this,
141 callback,
142 error_callback,
143 start_time,
144 response_message);
hashimoto@chromium.org955f6482013-09-26 13:32:29 +0900145 bus_->GetOriginTaskRunner()->PostTask(FROM_HERE, task);
hashimoto@chromium.orgb0305512012-05-23 15:55:22 +0900146 return;
147 }
148
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900149 // Increment the reference count so we can safely reference the
150 // underlying request message until the method call is complete. This
151 // will be unref'ed in StartAsyncMethodCall().
152 DBusMessage* request_message = method_call->raw_message();
153 dbus_message_ref(request_message);
154
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900155 base::Closure task = base::Bind(&ObjectProxy::StartAsyncMethodCall,
156 this,
157 timeout_ms,
satorux@chromium.org47d706b2011-10-04 22:47:21 +0900158 request_message,
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900159 callback,
hashimoto@chromium.org0d2477e2012-04-20 12:18:27 +0900160 error_callback,
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900161 start_time);
stevenjb@chromium.org3199bd12012-11-15 06:03:29 +0900162 statistics::AddSentMethodCall(service_name_,
163 method_call->GetInterface(),
164 method_call->GetMember());
165
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900166 // Wait for the response in the D-Bus thread.
hashimoto@chromium.org955f6482013-09-26 13:32:29 +0900167 bus_->GetDBusTaskRunner()->PostTask(FROM_HERE, task);
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900168}
169
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900170void ObjectProxy::ConnectToSignal(const std::string& interface_name,
171 const std::string& signal_name,
172 SignalCallback signal_callback,
173 OnConnectedCallback on_connected_callback) {
174 bus_->AssertOnOriginThread();
175
hashimoto@chromium.orgce5c6152013-09-26 15:40:04 +0900176 base::PostTaskAndReplyWithResult(
177 bus_->GetDBusTaskRunner(),
hashimoto@chromium.org955f6482013-09-26 13:32:29 +0900178 FROM_HERE,
179 base::Bind(&ObjectProxy::ConnectToSignalInternal,
180 this,
181 interface_name,
182 signal_name,
hashimoto@chromium.orgce5c6152013-09-26 15:40:04 +0900183 signal_callback),
184 base::Bind(on_connected_callback,
185 interface_name,
186 signal_name));
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900187}
188
hashimoto@chromium.org4f3851c2013-09-27 16:12:03 +0900189void ObjectProxy::SetNameOwnerChangedCallback(
190 NameOwnerChangedCallback callback) {
191 bus_->AssertOnOriginThread();
192
193 name_owner_changed_callback_ = callback;
194}
195
hashimoto@chromium.orged268092013-10-02 16:53:09 +0900196void ObjectProxy::WaitForServiceToBeAvailable(
197 WaitForServiceToBeAvailableCallback callback) {
198 bus_->AssertOnOriginThread();
199
200 wait_for_service_to_be_available_callbacks_.push_back(callback);
201 bus_->GetDBusTaskRunner()->PostTask(
202 FROM_HERE,
203 base::Bind(&ObjectProxy::WaitForServiceToBeAvailableInternal, this));
204}
205
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900206void ObjectProxy::Detach() {
207 bus_->AssertOnDBusThread();
208
satorux@chromium.org66bc4c22011-10-06 09:20:53 +0900209 if (filter_added_) {
210 if (!bus_->RemoveFilterFunction(&ObjectProxy::HandleMessageThunk, this)) {
211 LOG(ERROR) << "Failed to remove filter function";
212 }
213 }
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900214
satorux@chromium.org5b3e4962011-11-24 07:08:38 +0900215 for (std::set<std::string>::iterator iter = match_rules_.begin();
216 iter != match_rules_.end(); ++iter) {
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900217 ScopedDBusError error;
satorux@chromium.org5b3e4962011-11-24 07:08:38 +0900218 bus_->RemoveMatch(*iter, error.get());
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900219 if (error.is_set()) {
220 // There is nothing we can do to recover, so just print the error.
satorux@chromium.org5b3e4962011-11-24 07:08:38 +0900221 LOG(ERROR) << "Failed to remove match rule: " << *iter;
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900222 }
223 }
satorux@chromium.org5b3e4962011-11-24 07:08:38 +0900224 match_rules_.clear();
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900225}
226
satorux@chromium.orgb1753152011-11-11 17:36:22 +0900227// static
228ObjectProxy::ResponseCallback ObjectProxy::EmptyResponseCallback() {
229 return base::Bind(&EmptyResponseCallbackBody);
230}
231
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900232ObjectProxy::OnPendingCallIsCompleteData::OnPendingCallIsCompleteData(
233 ObjectProxy* in_object_proxy,
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900234 ResponseCallback in_response_callback,
hashimoto@chromium.org0d2477e2012-04-20 12:18:27 +0900235 ErrorCallback in_error_callback,
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900236 base::TimeTicks in_start_time)
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900237 : object_proxy(in_object_proxy),
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900238 response_callback(in_response_callback),
hashimoto@chromium.org0d2477e2012-04-20 12:18:27 +0900239 error_callback(in_error_callback),
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900240 start_time(in_start_time) {
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900241}
242
243ObjectProxy::OnPendingCallIsCompleteData::~OnPendingCallIsCompleteData() {
244}
245
246void ObjectProxy::StartAsyncMethodCall(int timeout_ms,
satorux@chromium.org47d706b2011-10-04 22:47:21 +0900247 DBusMessage* request_message,
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900248 ResponseCallback response_callback,
hashimoto@chromium.org0d2477e2012-04-20 12:18:27 +0900249 ErrorCallback error_callback,
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900250 base::TimeTicks start_time) {
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900251 bus_->AssertOnDBusThread();
252
253 if (!bus_->Connect() || !bus_->SetUpAsyncOperations()) {
hashimoto@chromium.org0d2477e2012-04-20 12:18:27 +0900254 // In case of a failure, run the error callback with NULL.
satorux@chromium.org47d706b2011-10-04 22:47:21 +0900255 DBusMessage* response_message = NULL;
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900256 base::Closure task = base::Bind(&ObjectProxy::RunResponseCallback,
257 this,
258 response_callback,
hashimoto@chromium.org0d2477e2012-04-20 12:18:27 +0900259 error_callback,
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900260 start_time,
satorux@chromium.org47d706b2011-10-04 22:47:21 +0900261 response_message);
hashimoto@chromium.org955f6482013-09-26 13:32:29 +0900262 bus_->GetOriginTaskRunner()->PostTask(FROM_HERE, task);
hashimoto@chromium.org0d2477e2012-04-20 12:18:27 +0900263
264 dbus_message_unref(request_message);
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900265 return;
266 }
267
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900268 DBusPendingCall* pending_call = NULL;
269
270 bus_->SendWithReply(request_message, &pending_call, timeout_ms);
271
272 // Prepare the data we'll be passing to OnPendingCallIsCompleteThunk().
273 // The data will be deleted in OnPendingCallIsCompleteThunk().
274 OnPendingCallIsCompleteData* data =
hashimoto@chromium.org0d2477e2012-04-20 12:18:27 +0900275 new OnPendingCallIsCompleteData(this, response_callback, error_callback,
276 start_time);
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900277
278 // This returns false only when unable to allocate memory.
279 const bool success = dbus_pending_call_set_notify(
280 pending_call,
281 &ObjectProxy::OnPendingCallIsCompleteThunk,
282 data,
283 NULL);
284 CHECK(success) << "Unable to allocate memory";
285 dbus_pending_call_unref(pending_call);
286
287 // It's now safe to unref the request message.
288 dbus_message_unref(request_message);
289}
290
291void ObjectProxy::OnPendingCallIsComplete(DBusPendingCall* pending_call,
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900292 ResponseCallback response_callback,
hashimoto@chromium.org0d2477e2012-04-20 12:18:27 +0900293 ErrorCallback error_callback,
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900294 base::TimeTicks start_time) {
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900295 bus_->AssertOnDBusThread();
296
297 DBusMessage* response_message = dbus_pending_call_steal_reply(pending_call);
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900298 base::Closure task = base::Bind(&ObjectProxy::RunResponseCallback,
299 this,
300 response_callback,
hashimoto@chromium.org0d2477e2012-04-20 12:18:27 +0900301 error_callback,
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900302 start_time,
satorux@chromium.org47d706b2011-10-04 22:47:21 +0900303 response_message);
hashimoto@chromium.org955f6482013-09-26 13:32:29 +0900304 bus_->GetOriginTaskRunner()->PostTask(FROM_HERE, task);
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900305}
306
307void ObjectProxy::RunResponseCallback(ResponseCallback response_callback,
hashimoto@chromium.org0d2477e2012-04-20 12:18:27 +0900308 ErrorCallback error_callback,
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900309 base::TimeTicks start_time,
satorux@chromium.org47d706b2011-10-04 22:47:21 +0900310 DBusMessage* response_message) {
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900311 bus_->AssertOnOriginThread();
312
satorux@chromium.orgb1753152011-11-11 17:36:22 +0900313 bool method_call_successful = false;
satorux@chromium.orgffa83a92011-08-24 12:32:06 +0900314 if (!response_message) {
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900315 // The response is not received.
hashimoto@chromium.org0d2477e2012-04-20 12:18:27 +0900316 error_callback.Run(NULL);
satorux@chromium.orgffa83a92011-08-24 12:32:06 +0900317 } else if (dbus_message_get_type(response_message) ==
318 DBUS_MESSAGE_TYPE_ERROR) {
319 // This will take |response_message| and release (unref) it.
thestig@chromium.org56057f22013-05-05 00:48:37 +0900320 scoped_ptr<ErrorResponse> error_response(
321 ErrorResponse::FromRawMessage(response_message));
hashimoto@chromium.org0d2477e2012-04-20 12:18:27 +0900322 error_callback.Run(error_response.get());
satorux@chromium.org9f10a6e2012-06-02 11:53:20 +0900323 // Delete the message on the D-Bus thread. See below for why.
hashimoto@chromium.org955f6482013-09-26 13:32:29 +0900324 bus_->GetDBusTaskRunner()->PostTask(
satorux@chromium.org9f10a6e2012-06-02 11:53:20 +0900325 FROM_HERE,
thestig@chromium.org56057f22013-05-05 00:48:37 +0900326 base::Bind(&base::DeletePointer<ErrorResponse>,
satorux@chromium.org9f10a6e2012-06-02 11:53:20 +0900327 error_response.release()));
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900328 } else {
satorux@chromium.orgffa83a92011-08-24 12:32:06 +0900329 // This will take |response_message| and release (unref) it.
thestig@chromium.org56057f22013-05-05 00:48:37 +0900330 scoped_ptr<Response> response(Response::FromRawMessage(response_message));
satorux@chromium.orgc6ac7572011-09-01 03:02:43 +0900331 // The response is successfully received.
satorux@chromium.orgffa83a92011-08-24 12:32:06 +0900332 response_callback.Run(response.get());
satorux@chromium.org9f10a6e2012-06-02 11:53:20 +0900333 // The message should be deleted on the D-Bus thread for a complicated
334 // reason:
335 //
336 // libdbus keeps track of the number of bytes in the incoming message
337 // queue to ensure that the data size in the queue is manageable. The
338 // bookkeeping is partly done via dbus_message_unref(), and immediately
339 // asks the client code (Chrome) to stop monitoring the underlying
340 // socket, if the number of bytes exceeds a certian number, which is set
341 // to 63MB, per dbus-transport.cc:
342 //
343 // /* Try to default to something that won't totally hose the system,
344 // * but doesn't impose too much of a limitation.
345 // */
346 // transport->max_live_messages_size = _DBUS_ONE_MEGABYTE * 63;
347 //
348 // The monitoring of the socket is done on the D-Bus thread (see Watch
349 // class in bus.cc), hence we should stop the monitoring from D-Bus
350 // thread, not from the current thread here, which is likely UI thread.
hashimoto@chromium.org955f6482013-09-26 13:32:29 +0900351 bus_->GetDBusTaskRunner()->PostTask(
satorux@chromium.org9f10a6e2012-06-02 11:53:20 +0900352 FROM_HERE,
thestig@chromium.org56057f22013-05-05 00:48:37 +0900353 base::Bind(&base::DeletePointer<Response>, response.release()));
satorux@chromium.org9f10a6e2012-06-02 11:53:20 +0900354
satorux@chromium.orgb1753152011-11-11 17:36:22 +0900355 method_call_successful = true;
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900356 // Record time spent for the method call. Don't include failures.
357 UMA_HISTOGRAM_TIMES("DBus.AsyncMethodCallTime",
358 base::TimeTicks::Now() - start_time);
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900359 }
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900360 // Record if the method call is successful, or not. 1 if successful.
361 UMA_HISTOGRAM_ENUMERATION("DBus.AsyncMethodCallSuccess",
satorux@chromium.orgb1753152011-11-11 17:36:22 +0900362 method_call_successful,
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900363 kSuccessRatioHistogramMaxValue);
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900364}
365
366void ObjectProxy::OnPendingCallIsCompleteThunk(DBusPendingCall* pending_call,
367 void* user_data) {
368 OnPendingCallIsCompleteData* data =
369 reinterpret_cast<OnPendingCallIsCompleteData*>(user_data);
370 ObjectProxy* self = data->object_proxy;
371 self->OnPendingCallIsComplete(pending_call,
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900372 data->response_callback,
hashimoto@chromium.org0d2477e2012-04-20 12:18:27 +0900373 data->error_callback,
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900374 data->start_time);
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900375 delete data;
376}
377
hashimoto@chromium.orged268092013-10-02 16:53:09 +0900378bool ObjectProxy::ConnectToNameOwnerChangedSignal() {
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900379 bus_->AssertOnDBusThread();
380
hashimoto@chromium.orgce5c6152013-09-26 15:40:04 +0900381 if (!bus_->Connect() || !bus_->SetUpAsyncOperations())
382 return false;
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900383
hashimoto@chromium.orgce5c6152013-09-26 15:40:04 +0900384 // We should add the filter only once. Otherwise, HandleMessage() will
385 // be called more than once.
386 if (!filter_added_) {
387 if (bus_->AddFilterFunction(&ObjectProxy::HandleMessageThunk, this)) {
388 filter_added_ = true;
389 } else {
390 LOG(ERROR) << "Failed to add filter function";
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900391 }
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900392 }
hashimoto@chromium.orgce5c6152013-09-26 15:40:04 +0900393 // Add a match_rule listening NameOwnerChanged for the well-known name
394 // |service_name_|.
395 const std::string name_owner_changed_match_rule =
396 base::StringPrintf(
397 "type='signal',interface='org.freedesktop.DBus',"
398 "member='NameOwnerChanged',path='/org/freedesktop/DBus',"
399 "sender='org.freedesktop.DBus',arg0='%s'",
400 service_name_.c_str());
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900401
hashimoto@chromium.orgce5c6152013-09-26 15:40:04 +0900402 const bool success =
hashimoto@chromium.orgce5c6152013-09-26 15:40:04 +0900403 AddMatchRuleWithoutCallback(name_owner_changed_match_rule,
404 "org.freedesktop.DBus.NameOwnerChanged");
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900405
hashimoto@chromium.orgce5c6152013-09-26 15:40:04 +0900406 // Try getting the current name owner. It's not guaranteed that we can get
407 // the name owner at this moment, as the service may not yet be started. If
408 // that's the case, we'll get the name owner via NameOwnerChanged signal,
409 // as soon as the service is started.
410 UpdateNameOwnerAndBlock();
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900411
hashimoto@chromium.orgce5c6152013-09-26 15:40:04 +0900412 return success;
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900413}
414
hashimoto@chromium.orged268092013-10-02 16:53:09 +0900415bool ObjectProxy::ConnectToSignalInternal(const std::string& interface_name,
416 const std::string& signal_name,
417 SignalCallback signal_callback) {
418 bus_->AssertOnDBusThread();
419
420 if (!ConnectToNameOwnerChangedSignal())
421 return false;
422
423 const std::string absolute_signal_name =
424 GetAbsoluteSignalName(interface_name, signal_name);
425
426 // Add a match rule so the signal goes through HandleMessage().
427 const std::string match_rule =
428 base::StringPrintf("type='signal', interface='%s', path='%s'",
429 interface_name.c_str(),
430 object_path_.value().c_str());
431 return AddMatchRuleWithCallback(match_rule,
432 absolute_signal_name,
433 signal_callback);
434}
435
436void ObjectProxy::WaitForServiceToBeAvailableInternal() {
437 bus_->AssertOnDBusThread();
438
439 if (!ConnectToNameOwnerChangedSignal()) { // Failed to connect to the signal.
440 const bool service_is_ready = false;
441 bus_->GetOriginTaskRunner()->PostTask(
442 FROM_HERE,
443 base::Bind(&ObjectProxy::RunWaitForServiceToBeAvailableCallbacks,
444 this, service_is_ready));
445 return;
446 }
447
448 const bool service_is_available = !service_name_owner_.empty();
449 if (service_is_available) { // Service is already available.
450 bus_->GetOriginTaskRunner()->PostTask(
451 FROM_HERE,
452 base::Bind(&ObjectProxy::RunWaitForServiceToBeAvailableCallbacks,
453 this, service_is_available));
454 return;
455 }
456}
457
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900458DBusHandlerResult ObjectProxy::HandleMessage(
459 DBusConnection* connection,
460 DBusMessage* raw_message) {
461 bus_->AssertOnDBusThread();
haruki@chromium.orgc5623ec2012-10-29 15:27:33 +0900462
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900463 if (dbus_message_get_type(raw_message) != DBUS_MESSAGE_TYPE_SIGNAL)
464 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
465
466 // raw_message will be unrefed on exit of the function. Increment the
467 // reference so we can use it in Signal.
468 dbus_message_ref(raw_message);
469 scoped_ptr<Signal> signal(
470 Signal::FromRawMessage(raw_message));
471
keybuk@chromium.orgb929d752012-03-01 13:01:05 +0900472 // Verify the signal comes from the object we're proxying for, this is
473 // our last chance to return DBUS_HANDLER_RESULT_NOT_YET_HANDLED and
474 // allow other object proxies to handle instead.
thestig@chromium.org56057f22013-05-05 00:48:37 +0900475 const ObjectPath path = signal->GetPath();
keybuk@chromium.orgb929d752012-03-01 13:01:05 +0900476 if (path != object_path_) {
stevenjb@chromium.org3199bd12012-11-15 06:03:29 +0900477 if (path.value() == kDBusSystemObjectPath &&
thestig@chromium.org56057f22013-05-05 00:48:37 +0900478 signal->GetMember() == kNameOwnerChangedMember) {
haruki@chromium.orgc5623ec2012-10-29 15:27:33 +0900479 // Handle NameOwnerChanged separately
haruki@chromium.orgc8d231a2012-11-14 20:02:59 +0900480 return HandleNameOwnerChanged(signal.Pass());
haruki@chromium.orgc5623ec2012-10-29 15:27:33 +0900481 }
keybuk@chromium.orgb929d752012-03-01 13:01:05 +0900482 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
483 }
484
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900485 const std::string interface = signal->GetInterface();
486 const std::string member = signal->GetMember();
487
stevenjb@chromium.org3199bd12012-11-15 06:03:29 +0900488 statistics::AddReceivedSignal(service_name_, interface, member);
489
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900490 // Check if we know about the signal.
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900491 const std::string absolute_signal_name = GetAbsoluteSignalName(
492 interface, member);
493 MethodTable::const_iterator iter = method_table_.find(absolute_signal_name);
494 if (iter == method_table_.end()) {
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900495 // Don't know about the signal.
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900496 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
497 }
satorux@chromium.org63c284f2011-10-15 07:37:12 +0900498 VLOG(1) << "Signal received: " << signal->ToString();
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900499
haruki@chromium.orgc5623ec2012-10-29 15:27:33 +0900500 std::string sender = signal->GetSender();
501 if (service_name_owner_ != sender) {
502 LOG(ERROR) << "Rejecting a message from a wrong sender.";
503 UMA_HISTOGRAM_COUNTS("DBus.RejectedSignalCount", 1);
504 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
505 }
506
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900507 const base::TimeTicks start_time = base::TimeTicks::Now();
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900508 if (bus_->HasDBusThread()) {
509 // Post a task to run the method in the origin thread.
510 // Transfer the ownership of |signal| to RunMethod().
511 // |released_signal| will be deleted in RunMethod().
512 Signal* released_signal = signal.release();
hashimoto@chromium.org955f6482013-09-26 13:32:29 +0900513 bus_->GetOriginTaskRunner()->PostTask(FROM_HERE,
514 base::Bind(&ObjectProxy::RunMethod,
515 this,
516 start_time,
517 iter->second,
518 released_signal));
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900519 } else {
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900520 const base::TimeTicks start_time = base::TimeTicks::Now();
521 // If the D-Bus thread is not used, just call the callback on the
522 // current thread. Transfer the ownership of |signal| to RunMethod().
523 Signal* released_signal = signal.release();
524 RunMethod(start_time, iter->second, released_signal);
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900525 }
526
hashimoto@chromium.orgfb3930a2013-12-17 17:41:57 +0900527 // We don't return DBUS_HANDLER_RESULT_HANDLED for signals because other
528 // objects may be interested in them. (e.g. Signals from org.freedesktop.DBus)
529 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900530}
531
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900532void ObjectProxy::RunMethod(base::TimeTicks start_time,
keybuk@chromium.org2594a712013-04-24 09:12:35 +0900533 std::vector<SignalCallback> signal_callbacks,
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900534 Signal* signal) {
535 bus_->AssertOnOriginThread();
536
keybuk@chromium.org2594a712013-04-24 09:12:35 +0900537 for (std::vector<SignalCallback>::iterator iter = signal_callbacks.begin();
538 iter != signal_callbacks.end(); ++iter)
539 iter->Run(signal);
540
satorux@chromium.org9f10a6e2012-06-02 11:53:20 +0900541 // Delete the message on the D-Bus thread. See comments in
542 // RunResponseCallback().
hashimoto@chromium.org955f6482013-09-26 13:32:29 +0900543 bus_->GetDBusTaskRunner()->PostTask(
satorux@chromium.org9f10a6e2012-06-02 11:53:20 +0900544 FROM_HERE,
thestig@chromium.org56057f22013-05-05 00:48:37 +0900545 base::Bind(&base::DeletePointer<Signal>, signal));
satorux@chromium.org9f10a6e2012-06-02 11:53:20 +0900546
satorux@chromium.org5a92cf32011-09-07 05:53:30 +0900547 // Record time spent for handling the signal.
548 UMA_HISTOGRAM_TIMES("DBus.SignalHandleTime",
549 base::TimeTicks::Now() - start_time);
satorux@chromium.org7f0c4512011-08-23 16:29:21 +0900550}
551
552DBusHandlerResult ObjectProxy::HandleMessageThunk(
553 DBusConnection* connection,
554 DBusMessage* raw_message,
555 void* user_data) {
556 ObjectProxy* self = reinterpret_cast<ObjectProxy*>(user_data);
557 return self->HandleMessage(connection, raw_message);
558}
559
adamk@chromium.org35c0eef2012-02-11 06:45:23 +0900560void ObjectProxy::LogMethodCallFailure(
satorux@chromium.org31bb21e2012-05-31 15:12:11 +0900561 const base::StringPiece& interface_name,
562 const base::StringPiece& method_name,
adamk@chromium.org35c0eef2012-02-11 06:45:23 +0900563 const base::StringPiece& error_name,
564 const base::StringPiece& error_message) const {
stevenjb@chromium.orgc415c482014-08-09 06:57:19 +0900565 if (ignore_service_unknown_errors_ &&
566 (error_name == kErrorServiceUnknown || error_name == kErrorObjectUnknown))
adamk@chromium.org35c0eef2012-02-11 06:45:23 +0900567 return;
stevenjb@chromium.orgc415c482014-08-09 06:57:19 +0900568 logging::LogSeverity severity = logging::LOG_ERROR;
569 // "UnknownObject" indicates that an object or service is no longer available,
570 // e.g. a Shill network service has gone out of range. Treat these as warnings
571 // not errors.
572 if (error_name == kErrorObjectUnknown)
573 severity = logging::LOG_WARNING;
574 std::ostringstream msg;
575 msg << "Failed to call method: " << interface_name << "." << method_name
576 << ": object_path= " << object_path_.value()
577 << ": " << error_name << ": " << error_message;
578 logging::LogAtLevel(severity, msg.str());
adamk@chromium.org35c0eef2012-02-11 06:45:23 +0900579}
580
satorux@chromium.org31bb21e2012-05-31 15:12:11 +0900581void ObjectProxy::OnCallMethodError(const std::string& interface_name,
582 const std::string& method_name,
583 ResponseCallback response_callback,
hashimoto@chromium.org0d2477e2012-04-20 12:18:27 +0900584 ErrorResponse* error_response) {
585 if (error_response) {
586 // Error message may contain the error message as string.
thestig@chromium.org56057f22013-05-05 00:48:37 +0900587 MessageReader reader(error_response);
hashimoto@chromium.org0d2477e2012-04-20 12:18:27 +0900588 std::string error_message;
589 reader.PopString(&error_message);
satorux@chromium.org31bb21e2012-05-31 15:12:11 +0900590 LogMethodCallFailure(interface_name,
591 method_name,
592 error_response->GetErrorName(),
593 error_message);
hashimoto@chromium.org0d2477e2012-04-20 12:18:27 +0900594 }
595 response_callback.Run(NULL);
596}
597
haruki@chromium.orgc5623ec2012-10-29 15:27:33 +0900598bool ObjectProxy::AddMatchRuleWithCallback(
599 const std::string& match_rule,
600 const std::string& absolute_signal_name,
601 SignalCallback signal_callback) {
602 DCHECK(!match_rule.empty());
603 DCHECK(!absolute_signal_name.empty());
604 bus_->AssertOnDBusThread();
605
606 if (match_rules_.find(match_rule) == match_rules_.end()) {
607 ScopedDBusError error;
608 bus_->AddMatch(match_rule, error.get());
609 if (error.is_set()) {
thestig@chromium.org56057f22013-05-05 00:48:37 +0900610 LOG(ERROR) << "Failed to add match rule \"" << match_rule << "\". Got "
611 << error.name() << ": " << error.message();
haruki@chromium.orgc5623ec2012-10-29 15:27:33 +0900612 return false;
613 } else {
614 // Store the match rule, so that we can remove this in Detach().
615 match_rules_.insert(match_rule);
616 // Add the signal callback to the method table.
keybuk@chromium.org2594a712013-04-24 09:12:35 +0900617 method_table_[absolute_signal_name].push_back(signal_callback);
haruki@chromium.orgc5623ec2012-10-29 15:27:33 +0900618 return true;
619 }
620 } else {
621 // We already have the match rule.
keybuk@chromium.org2594a712013-04-24 09:12:35 +0900622 method_table_[absolute_signal_name].push_back(signal_callback);
haruki@chromium.orgc5623ec2012-10-29 15:27:33 +0900623 return true;
624 }
625}
626
627bool ObjectProxy::AddMatchRuleWithoutCallback(
628 const std::string& match_rule,
629 const std::string& absolute_signal_name) {
630 DCHECK(!match_rule.empty());
631 DCHECK(!absolute_signal_name.empty());
632 bus_->AssertOnDBusThread();
633
634 if (match_rules_.find(match_rule) != match_rules_.end())
635 return true;
636
637 ScopedDBusError error;
638 bus_->AddMatch(match_rule, error.get());
639 if (error.is_set()) {
thestig@chromium.org56057f22013-05-05 00:48:37 +0900640 LOG(ERROR) << "Failed to add match rule \"" << match_rule << "\". Got "
641 << error.name() << ": " << error.message();
haruki@chromium.orgc5623ec2012-10-29 15:27:33 +0900642 return false;
643 }
644 // Store the match rule, so that we can remove this in Detach().
645 match_rules_.insert(match_rule);
646 return true;
647}
648
649void ObjectProxy::UpdateNameOwnerAndBlock() {
650 bus_->AssertOnDBusThread();
satorux@chromium.org9b1f85d2013-08-16 18:16:42 +0900651 // Errors should be suppressed here, as the service may not be yet running
652 // when connecting to signals of the service, which is just fine.
653 // The ObjectProxy will be notified when the service is launched via
654 // NameOwnerChanged signal. See also comments in ConnectToSignalInternal().
thestig@chromium.org56057f22013-05-05 00:48:37 +0900655 service_name_owner_ =
satorux@chromium.org9b1f85d2013-08-16 18:16:42 +0900656 bus_->GetServiceOwnerAndBlock(service_name_, Bus::SUPPRESS_ERRORS);
haruki@chromium.orgc5623ec2012-10-29 15:27:33 +0900657}
658
haruki@chromium.orgc8d231a2012-11-14 20:02:59 +0900659DBusHandlerResult ObjectProxy::HandleNameOwnerChanged(
660 scoped_ptr<Signal> signal) {
haruki@chromium.orgc5623ec2012-10-29 15:27:33 +0900661 DCHECK(signal);
662 bus_->AssertOnDBusThread();
663
664 // Confirm the validity of the NameOwnerChanged signal.
thestig@chromium.org56057f22013-05-05 00:48:37 +0900665 if (signal->GetMember() == kNameOwnerChangedMember &&
666 signal->GetInterface() == kDBusSystemObjectInterface &&
667 signal->GetSender() == kDBusSystemObjectAddress) {
haruki@chromium.orgc8d231a2012-11-14 20:02:59 +0900668 MessageReader reader(signal.get());
haruki@chromium.orgc5623ec2012-10-29 15:27:33 +0900669 std::string name, old_owner, new_owner;
670 if (reader.PopString(&name) &&
671 reader.PopString(&old_owner) &&
672 reader.PopString(&new_owner) &&
673 name == service_name_) {
674 service_name_owner_ = new_owner;
hashimoto@chromium.org4f3851c2013-09-27 16:12:03 +0900675 bus_->GetOriginTaskRunner()->PostTask(
676 FROM_HERE,
677 base::Bind(&ObjectProxy::RunNameOwnerChangedCallback,
678 this, old_owner, new_owner));
hashimoto@chromium.orged268092013-10-02 16:53:09 +0900679
680 const bool service_is_available = !service_name_owner_.empty();
681 if (service_is_available) {
682 bus_->GetOriginTaskRunner()->PostTask(
683 FROM_HERE,
684 base::Bind(&ObjectProxy::RunWaitForServiceToBeAvailableCallbacks,
685 this, service_is_available));
686 }
haruki@chromium.orgc5623ec2012-10-29 15:27:33 +0900687 }
688 }
689
keybuk@chromium.org359c9b62012-11-27 09:23:25 +0900690 // Always return unhandled to let other object proxies handle the same
691 // signal.
haruki@chromium.orgc5623ec2012-10-29 15:27:33 +0900692 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
693}
694
hashimoto@chromium.org4f3851c2013-09-27 16:12:03 +0900695void ObjectProxy::RunNameOwnerChangedCallback(const std::string& old_owner,
696 const std::string& new_owner) {
697 bus_->AssertOnOriginThread();
698 if (!name_owner_changed_callback_.is_null())
699 name_owner_changed_callback_.Run(old_owner, new_owner);
700}
701
hashimoto@chromium.orged268092013-10-02 16:53:09 +0900702void ObjectProxy::RunWaitForServiceToBeAvailableCallbacks(
703 bool service_is_available) {
704 bus_->AssertOnOriginThread();
705
706 std::vector<WaitForServiceToBeAvailableCallback> callbacks;
707 callbacks.swap(wait_for_service_to_be_available_callbacks_);
708 for (size_t i = 0; i < callbacks.size(); ++i)
709 callbacks[i].Run(service_is_available);
710}
711
satorux@chromium.org163f1cb2011-08-18 05:58:12 +0900712} // namespace dbus