Updating trunk VERSION from 912.0 to 913.0
[chromium-blink-merge.git] / dbus / object_proxy.cc
blob1a3b7e04872b64801c9f4e5fbf65ad90a2007d06
1 // Copyright (c) 2011 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/stringprintf.h"
12 #include "base/threading/thread.h"
13 #include "base/threading/thread_restrictions.h"
14 #include "dbus/message.h"
15 #include "dbus/object_proxy.h"
16 #include "dbus/scoped_dbus_error.h"
18 namespace {
20 // Used for success ratio histograms. 1 for success, 0 for failure.
21 const int kSuccessRatioHistogramMaxValue = 2;
23 // Gets the absolute signal name by concatenating the interface name and
24 // the signal name. Used for building keys for method_table_ in
25 // ObjectProxy.
26 std::string GetAbsoluteSignalName(
27 const std::string& interface_name,
28 const std::string& signal_name) {
29 return interface_name + "." + signal_name;
32 } // namespace
34 namespace dbus {
36 ObjectProxy::ObjectProxy(Bus* bus,
37 const std::string& service_name,
38 const std::string& object_path)
39 : bus_(bus),
40 service_name_(service_name),
41 object_path_(object_path),
42 filter_added_(false) {
45 ObjectProxy::~ObjectProxy() {
48 // Originally we tried to make |method_call| a const reference, but we
49 // gave up as dbus_connection_send_with_reply_and_block() takes a
50 // non-const pointer of DBusMessage as the second parameter.
51 Response* ObjectProxy::CallMethodAndBlock(MethodCall* method_call,
52 int timeout_ms) {
53 bus_->AssertOnDBusThread();
55 if (!bus_->Connect())
56 return NULL;
58 method_call->SetDestination(service_name_);
59 method_call->SetPath(object_path_);
60 DBusMessage* request_message = method_call->raw_message();
62 ScopedDBusError error;
64 // Send the message synchronously.
65 const base::TimeTicks start_time = base::TimeTicks::Now();
66 DBusMessage* response_message =
67 bus_->SendWithReplyAndBlock(request_message, timeout_ms, error.get());
68 // Record if the method call is successful, or not. 1 if successful.
69 UMA_HISTOGRAM_ENUMERATION("DBus.SyncMethodCallSuccess",
70 response_message ? 1 : 0,
71 kSuccessRatioHistogramMaxValue);
73 if (!response_message) {
74 LOG(ERROR) << "Failed to call method: "
75 << (error.is_set() ? error.message() : "");
76 return NULL;
78 // Record time spent for the method call. Don't include failures.
79 UMA_HISTOGRAM_TIMES("DBus.SyncMethodCallTime",
80 base::TimeTicks::Now() - start_time);
82 return Response::FromRawMessage(response_message);
85 void ObjectProxy::CallMethod(MethodCall* method_call,
86 int timeout_ms,
87 ResponseCallback callback) {
88 bus_->AssertOnOriginThread();
90 method_call->SetDestination(service_name_);
91 method_call->SetPath(object_path_);
92 // Increment the reference count so we can safely reference the
93 // underlying request message until the method call is complete. This
94 // will be unref'ed in StartAsyncMethodCall().
95 DBusMessage* request_message = method_call->raw_message();
96 dbus_message_ref(request_message);
98 const base::TimeTicks start_time = base::TimeTicks::Now();
99 base::Closure task = base::Bind(&ObjectProxy::StartAsyncMethodCall,
100 this,
101 timeout_ms,
102 request_message,
103 callback,
104 start_time);
105 // Wait for the response in the D-Bus thread.
106 bus_->PostTaskToDBusThread(FROM_HERE, task);
109 void ObjectProxy::ConnectToSignal(const std::string& interface_name,
110 const std::string& signal_name,
111 SignalCallback signal_callback,
112 OnConnectedCallback on_connected_callback) {
113 bus_->AssertOnOriginThread();
115 bus_->PostTaskToDBusThread(FROM_HERE,
116 base::Bind(&ObjectProxy::ConnectToSignalInternal,
117 this,
118 interface_name,
119 signal_name,
120 signal_callback,
121 on_connected_callback));
124 void ObjectProxy::Detach() {
125 bus_->AssertOnDBusThread();
127 if (filter_added_) {
128 if (!bus_->RemoveFilterFunction(&ObjectProxy::HandleMessageThunk, this)) {
129 LOG(ERROR) << "Failed to remove filter function";
133 for (size_t i = 0; i < match_rules_.size(); ++i) {
134 ScopedDBusError error;
135 bus_->RemoveMatch(match_rules_[i], error.get());
136 if (error.is_set()) {
137 // There is nothing we can do to recover, so just print the error.
138 LOG(ERROR) << "Failed to remove match rule: " << match_rules_[i];
143 ObjectProxy::OnPendingCallIsCompleteData::OnPendingCallIsCompleteData(
144 ObjectProxy* in_object_proxy,
145 ResponseCallback in_response_callback,
146 base::TimeTicks in_start_time)
147 : object_proxy(in_object_proxy),
148 response_callback(in_response_callback),
149 start_time(in_start_time) {
152 ObjectProxy::OnPendingCallIsCompleteData::~OnPendingCallIsCompleteData() {
155 void ObjectProxy::StartAsyncMethodCall(int timeout_ms,
156 DBusMessage* request_message,
157 ResponseCallback response_callback,
158 base::TimeTicks start_time) {
159 bus_->AssertOnDBusThread();
161 if (!bus_->Connect() || !bus_->SetUpAsyncOperations()) {
162 // In case of a failure, run the callback with NULL response, that
163 // indicates a failure.
164 DBusMessage* response_message = NULL;
165 base::Closure task = base::Bind(&ObjectProxy::RunResponseCallback,
166 this,
167 response_callback,
168 start_time,
169 response_message);
170 bus_->PostTaskToOriginThread(FROM_HERE, task);
171 return;
174 DBusPendingCall* pending_call = NULL;
176 bus_->SendWithReply(request_message, &pending_call, timeout_ms);
178 // Prepare the data we'll be passing to OnPendingCallIsCompleteThunk().
179 // The data will be deleted in OnPendingCallIsCompleteThunk().
180 OnPendingCallIsCompleteData* data =
181 new OnPendingCallIsCompleteData(this, response_callback, start_time);
183 // This returns false only when unable to allocate memory.
184 const bool success = dbus_pending_call_set_notify(
185 pending_call,
186 &ObjectProxy::OnPendingCallIsCompleteThunk,
187 data,
188 NULL);
189 CHECK(success) << "Unable to allocate memory";
190 dbus_pending_call_unref(pending_call);
192 // It's now safe to unref the request message.
193 dbus_message_unref(request_message);
196 void ObjectProxy::OnPendingCallIsComplete(DBusPendingCall* pending_call,
197 ResponseCallback response_callback,
198 base::TimeTicks start_time) {
199 bus_->AssertOnDBusThread();
201 DBusMessage* response_message = dbus_pending_call_steal_reply(pending_call);
202 base::Closure task = base::Bind(&ObjectProxy::RunResponseCallback,
203 this,
204 response_callback,
205 start_time,
206 response_message);
207 bus_->PostTaskToOriginThread(FROM_HERE, task);
210 void ObjectProxy::RunResponseCallback(ResponseCallback response_callback,
211 base::TimeTicks start_time,
212 DBusMessage* response_message) {
213 bus_->AssertOnOriginThread();
215 bool response_callback_called = false;
216 if (!response_message) {
217 // The response is not received.
218 response_callback.Run(NULL);
219 } else if (dbus_message_get_type(response_message) ==
220 DBUS_MESSAGE_TYPE_ERROR) {
221 // This will take |response_message| and release (unref) it.
222 scoped_ptr<dbus::ErrorResponse> error_response(
223 dbus::ErrorResponse::FromRawMessage(response_message));
224 // Error message may contain the error message as string.
225 dbus::MessageReader reader(error_response.get());
226 std::string error_message;
227 reader.PopString(&error_message);
228 LOG(ERROR) << "Failed to call method: " << error_response->GetErrorName()
229 << ": " << error_message;
230 // We don't give the error message to the callback.
231 response_callback.Run(NULL);
232 } else {
233 // This will take |response_message| and release (unref) it.
234 scoped_ptr<dbus::Response> response(
235 dbus::Response::FromRawMessage(response_message));
236 // The response is successfully received.
237 response_callback.Run(response.get());
238 response_callback_called = true;
239 // Record time spent for the method call. Don't include failures.
240 UMA_HISTOGRAM_TIMES("DBus.AsyncMethodCallTime",
241 base::TimeTicks::Now() - start_time);
243 // Record if the method call is successful, or not. 1 if successful.
244 UMA_HISTOGRAM_ENUMERATION("DBus.AsyncMethodCallSuccess",
245 response_callback_called,
246 kSuccessRatioHistogramMaxValue);
249 void ObjectProxy::OnPendingCallIsCompleteThunk(DBusPendingCall* pending_call,
250 void* user_data) {
251 OnPendingCallIsCompleteData* data =
252 reinterpret_cast<OnPendingCallIsCompleteData*>(user_data);
253 ObjectProxy* self = data->object_proxy;
254 self->OnPendingCallIsComplete(pending_call,
255 data->response_callback,
256 data->start_time);
257 delete data;
260 void ObjectProxy::ConnectToSignalInternal(
261 const std::string& interface_name,
262 const std::string& signal_name,
263 SignalCallback signal_callback,
264 OnConnectedCallback on_connected_callback) {
265 bus_->AssertOnDBusThread();
267 // Check if the object is already connected to the signal.
268 const std::string absolute_signal_name =
269 GetAbsoluteSignalName(interface_name, signal_name);
270 if (method_table_.find(absolute_signal_name) != method_table_.end()) {
271 LOG(ERROR) << "The object proxy is already connected to "
272 << absolute_signal_name;
273 return;
276 // Will become true, if everything is successful.
277 bool success = false;
279 if (bus_->Connect() && bus_->SetUpAsyncOperations()) {
280 // We should add the filter only once. Otherwise, HandleMessage() will
281 // be called more than once.
282 if (!filter_added_) {
283 if (bus_->AddFilterFunction(&ObjectProxy::HandleMessageThunk, this)) {
284 filter_added_ = true;
285 } else {
286 LOG(ERROR) << "Failed to add filter function";
289 // Add a match rule so the signal goes through HandleMessage().
291 // We don't restrict the sender object path to be |object_path_| here,
292 // to make it easy to test D-Bus signal handling with dbus-send, that
293 // uses "/" as the sender object path. We can make the object path
294 // restriction customizable when it becomes necessary.
295 const std::string match_rule =
296 base::StringPrintf("type='signal', interface='%s'",
297 interface_name.c_str());
298 ScopedDBusError error;
299 bus_->AddMatch(match_rule, error.get());;
300 if (error.is_set()) {
301 LOG(ERROR) << "Failed to add match rule: " << match_rule;
302 } else {
303 // Store the match rule, so that we can remove this in Detach().
304 match_rules_.push_back(match_rule);
305 // Add the signal callback to the method table.
306 method_table_[absolute_signal_name] = signal_callback;
307 success = true;
311 // Run on_connected_callback in the origin thread.
312 bus_->PostTaskToOriginThread(
313 FROM_HERE,
314 base::Bind(&ObjectProxy::OnConnected,
315 this,
316 on_connected_callback,
317 interface_name,
318 signal_name,
319 success));
322 void ObjectProxy::OnConnected(OnConnectedCallback on_connected_callback,
323 const std::string& interface_name,
324 const std::string& signal_name,
325 bool success) {
326 bus_->AssertOnOriginThread();
328 on_connected_callback.Run(interface_name, signal_name, success);
331 DBusHandlerResult ObjectProxy::HandleMessage(
332 DBusConnection* connection,
333 DBusMessage* raw_message) {
334 bus_->AssertOnDBusThread();
335 if (dbus_message_get_type(raw_message) != DBUS_MESSAGE_TYPE_SIGNAL)
336 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
338 // raw_message will be unrefed on exit of the function. Increment the
339 // reference so we can use it in Signal.
340 dbus_message_ref(raw_message);
341 scoped_ptr<Signal> signal(
342 Signal::FromRawMessage(raw_message));
344 const std::string interface = signal->GetInterface();
345 const std::string member = signal->GetMember();
347 // Check if we know about the signal.
348 const std::string absolute_signal_name = GetAbsoluteSignalName(
349 interface, member);
350 MethodTable::const_iterator iter = method_table_.find(absolute_signal_name);
351 if (iter == method_table_.end()) {
352 // Don't know about the signal.
353 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
355 VLOG(1) << "Signal received: " << signal->ToString();
357 const base::TimeTicks start_time = base::TimeTicks::Now();
358 if (bus_->HasDBusThread()) {
359 // Post a task to run the method in the origin thread.
360 // Transfer the ownership of |signal| to RunMethod().
361 // |released_signal| will be deleted in RunMethod().
362 Signal* released_signal = signal.release();
363 bus_->PostTaskToOriginThread(FROM_HERE,
364 base::Bind(&ObjectProxy::RunMethod,
365 this,
366 start_time,
367 iter->second,
368 released_signal));
369 } else {
370 const base::TimeTicks start_time = base::TimeTicks::Now();
371 // If the D-Bus thread is not used, just call the callback on the
372 // current thread. Transfer the ownership of |signal| to RunMethod().
373 Signal* released_signal = signal.release();
374 RunMethod(start_time, iter->second, released_signal);
377 return DBUS_HANDLER_RESULT_HANDLED;
380 void ObjectProxy::RunMethod(base::TimeTicks start_time,
381 SignalCallback signal_callback,
382 Signal* signal) {
383 bus_->AssertOnOriginThread();
385 signal_callback.Run(signal);
386 delete signal;
387 // Record time spent for handling the signal.
388 UMA_HISTOGRAM_TIMES("DBus.SignalHandleTime",
389 base::TimeTicks::Now() - start_time);
392 DBusHandlerResult ObjectProxy::HandleMessageThunk(
393 DBusConnection* connection,
394 DBusMessage* raw_message,
395 void* user_data) {
396 ObjectProxy* self = reinterpret_cast<ObjectProxy*>(user_data);
397 return self->HandleMessage(connection, raw_message);
400 } // namespace dbus