Update lzma_sdk to 9.20
[chromium-blink-merge.git] / dbus / object_proxy.cc
blob0cbd93e285f61ab9eaae5d19c98f5a00e8a44077
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 // Gets the absolute signal name by concatenating the interface name and
28 // the signal name. Used for building keys for method_table_ in
29 // ObjectProxy.
30 std::string GetAbsoluteSignalName(
31 const std::string& interface_name,
32 const std::string& signal_name) {
33 return interface_name + "." + signal_name;
36 // An empty function used for ObjectProxy::EmptyResponseCallback().
37 void EmptyResponseCallbackBody(dbus::Response* unused_response) {
40 } // namespace
42 namespace dbus {
44 ObjectProxy::ObjectProxy(Bus* bus,
45 const std::string& service_name,
46 const ObjectPath& object_path,
47 int options)
48 : bus_(bus),
49 service_name_(service_name),
50 object_path_(object_path),
51 filter_added_(false),
52 ignore_service_unknown_errors_(
53 options & IGNORE_SERVICE_UNKNOWN_ERRORS) {
56 ObjectProxy::~ObjectProxy() {
59 // Originally we tried to make |method_call| a const reference, but we
60 // gave up as dbus_connection_send_with_reply_and_block() takes a
61 // non-const pointer of DBusMessage as the second parameter.
62 Response* ObjectProxy::CallMethodAndBlock(MethodCall* method_call,
63 int timeout_ms) {
64 bus_->AssertOnDBusThread();
66 if (!bus_->Connect())
67 return NULL;
69 method_call->SetDestination(service_name_);
70 method_call->SetPath(object_path_);
71 DBusMessage* request_message = method_call->raw_message();
73 ScopedDBusError error;
75 // Send the message synchronously.
76 const base::TimeTicks start_time = base::TimeTicks::Now();
77 DBusMessage* response_message =
78 bus_->SendWithReplyAndBlock(request_message, timeout_ms, error.get());
79 // Record if the method call is successful, or not. 1 if successful.
80 UMA_HISTOGRAM_ENUMERATION("DBus.SyncMethodCallSuccess",
81 response_message ? 1 : 0,
82 kSuccessRatioHistogramMaxValue);
84 if (!response_message) {
85 LogMethodCallFailure(error.is_set() ? error.name() : "unknown error type",
86 error.is_set() ? error.message() : "");
87 return NULL;
89 // Record time spent for the method call. Don't include failures.
90 UMA_HISTOGRAM_TIMES("DBus.SyncMethodCallTime",
91 base::TimeTicks::Now() - start_time);
93 return Response::FromRawMessage(response_message);
96 void ObjectProxy::CallMethod(MethodCall* method_call,
97 int timeout_ms,
98 ResponseCallback callback) {
99 bus_->AssertOnOriginThread();
101 method_call->SetDestination(service_name_);
102 method_call->SetPath(object_path_);
103 // Increment the reference count so we can safely reference the
104 // underlying request message until the method call is complete. This
105 // will be unref'ed in StartAsyncMethodCall().
106 DBusMessage* request_message = method_call->raw_message();
107 dbus_message_ref(request_message);
109 const base::TimeTicks start_time = base::TimeTicks::Now();
110 base::Closure task = base::Bind(&ObjectProxy::StartAsyncMethodCall,
111 this,
112 timeout_ms,
113 request_message,
114 callback,
115 start_time);
116 // Wait for the response in the D-Bus thread.
117 bus_->PostTaskToDBusThread(FROM_HERE, task);
120 void ObjectProxy::ConnectToSignal(const std::string& interface_name,
121 const std::string& signal_name,
122 SignalCallback signal_callback,
123 OnConnectedCallback on_connected_callback) {
124 bus_->AssertOnOriginThread();
126 bus_->PostTaskToDBusThread(FROM_HERE,
127 base::Bind(&ObjectProxy::ConnectToSignalInternal,
128 this,
129 interface_name,
130 signal_name,
131 signal_callback,
132 on_connected_callback));
135 void ObjectProxy::Detach() {
136 bus_->AssertOnDBusThread();
138 if (filter_added_) {
139 if (!bus_->RemoveFilterFunction(&ObjectProxy::HandleMessageThunk, this)) {
140 LOG(ERROR) << "Failed to remove filter function";
144 for (std::set<std::string>::iterator iter = match_rules_.begin();
145 iter != match_rules_.end(); ++iter) {
146 ScopedDBusError error;
147 bus_->RemoveMatch(*iter, error.get());
148 if (error.is_set()) {
149 // There is nothing we can do to recover, so just print the error.
150 LOG(ERROR) << "Failed to remove match rule: " << *iter;
153 match_rules_.clear();
156 // static
157 ObjectProxy::ResponseCallback ObjectProxy::EmptyResponseCallback() {
158 return base::Bind(&EmptyResponseCallbackBody);
161 ObjectProxy::OnPendingCallIsCompleteData::OnPendingCallIsCompleteData(
162 ObjectProxy* in_object_proxy,
163 ResponseCallback in_response_callback,
164 base::TimeTicks in_start_time)
165 : object_proxy(in_object_proxy),
166 response_callback(in_response_callback),
167 start_time(in_start_time) {
170 ObjectProxy::OnPendingCallIsCompleteData::~OnPendingCallIsCompleteData() {
173 void ObjectProxy::StartAsyncMethodCall(int timeout_ms,
174 DBusMessage* request_message,
175 ResponseCallback response_callback,
176 base::TimeTicks start_time) {
177 bus_->AssertOnDBusThread();
179 if (!bus_->Connect() || !bus_->SetUpAsyncOperations()) {
180 // In case of a failure, run the callback with NULL response, that
181 // indicates a failure.
182 DBusMessage* response_message = NULL;
183 base::Closure task = base::Bind(&ObjectProxy::RunResponseCallback,
184 this,
185 response_callback,
186 start_time,
187 response_message);
188 bus_->PostTaskToOriginThread(FROM_HERE, task);
189 return;
192 DBusPendingCall* pending_call = NULL;
194 bus_->SendWithReply(request_message, &pending_call, timeout_ms);
196 // Prepare the data we'll be passing to OnPendingCallIsCompleteThunk().
197 // The data will be deleted in OnPendingCallIsCompleteThunk().
198 OnPendingCallIsCompleteData* data =
199 new OnPendingCallIsCompleteData(this, response_callback, start_time);
201 // This returns false only when unable to allocate memory.
202 const bool success = dbus_pending_call_set_notify(
203 pending_call,
204 &ObjectProxy::OnPendingCallIsCompleteThunk,
205 data,
206 NULL);
207 CHECK(success) << "Unable to allocate memory";
208 dbus_pending_call_unref(pending_call);
210 // It's now safe to unref the request message.
211 dbus_message_unref(request_message);
214 void ObjectProxy::OnPendingCallIsComplete(DBusPendingCall* pending_call,
215 ResponseCallback response_callback,
216 base::TimeTicks start_time) {
217 bus_->AssertOnDBusThread();
219 DBusMessage* response_message = dbus_pending_call_steal_reply(pending_call);
220 base::Closure task = base::Bind(&ObjectProxy::RunResponseCallback,
221 this,
222 response_callback,
223 start_time,
224 response_message);
225 bus_->PostTaskToOriginThread(FROM_HERE, task);
228 void ObjectProxy::RunResponseCallback(ResponseCallback response_callback,
229 base::TimeTicks start_time,
230 DBusMessage* response_message) {
231 bus_->AssertOnOriginThread();
233 bool method_call_successful = false;
234 if (!response_message) {
235 // The response is not received.
236 response_callback.Run(NULL);
237 } else if (dbus_message_get_type(response_message) ==
238 DBUS_MESSAGE_TYPE_ERROR) {
239 // This will take |response_message| and release (unref) it.
240 scoped_ptr<dbus::ErrorResponse> error_response(
241 dbus::ErrorResponse::FromRawMessage(response_message));
242 // Error message may contain the error message as string.
243 dbus::MessageReader reader(error_response.get());
244 std::string error_message;
245 reader.PopString(&error_message);
246 LogMethodCallFailure(error_response->GetErrorName(), error_message);
247 // We don't give the error message to the callback.
248 response_callback.Run(NULL);
249 } else {
250 // This will take |response_message| and release (unref) it.
251 scoped_ptr<dbus::Response> response(
252 dbus::Response::FromRawMessage(response_message));
253 // The response is successfully received.
254 response_callback.Run(response.get());
255 method_call_successful = true;
256 // Record time spent for the method call. Don't include failures.
257 UMA_HISTOGRAM_TIMES("DBus.AsyncMethodCallTime",
258 base::TimeTicks::Now() - start_time);
260 // Record if the method call is successful, or not. 1 if successful.
261 UMA_HISTOGRAM_ENUMERATION("DBus.AsyncMethodCallSuccess",
262 method_call_successful,
263 kSuccessRatioHistogramMaxValue);
266 void ObjectProxy::OnPendingCallIsCompleteThunk(DBusPendingCall* pending_call,
267 void* user_data) {
268 OnPendingCallIsCompleteData* data =
269 reinterpret_cast<OnPendingCallIsCompleteData*>(user_data);
270 ObjectProxy* self = data->object_proxy;
271 self->OnPendingCallIsComplete(pending_call,
272 data->response_callback,
273 data->start_time);
274 delete data;
277 void ObjectProxy::ConnectToSignalInternal(
278 const std::string& interface_name,
279 const std::string& signal_name,
280 SignalCallback signal_callback,
281 OnConnectedCallback on_connected_callback) {
282 bus_->AssertOnDBusThread();
284 const std::string absolute_signal_name =
285 GetAbsoluteSignalName(interface_name, signal_name);
287 // Will become true, if everything is successful.
288 bool success = false;
290 if (bus_->Connect() && bus_->SetUpAsyncOperations()) {
291 // We should add the filter only once. Otherwise, HandleMessage() will
292 // be called more than once.
293 if (!filter_added_) {
294 if (bus_->AddFilterFunction(&ObjectProxy::HandleMessageThunk, this)) {
295 filter_added_ = true;
296 } else {
297 LOG(ERROR) << "Failed to add filter function";
300 // Add a match rule so the signal goes through HandleMessage().
301 const std::string match_rule =
302 base::StringPrintf("type='signal', interface='%s', path='%s'",
303 interface_name.c_str(),
304 object_path_.value().c_str());
306 // Add the match rule if we don't have it.
307 if (match_rules_.find(match_rule) == match_rules_.end()) {
308 ScopedDBusError error;
309 bus_->AddMatch(match_rule, error.get());;
310 if (error.is_set()) {
311 LOG(ERROR) << "Failed to add match rule: " << match_rule;
312 } else {
313 // Store the match rule, so that we can remove this in Detach().
314 match_rules_.insert(match_rule);
315 // Add the signal callback to the method table.
316 method_table_[absolute_signal_name] = signal_callback;
317 success = true;
319 } else {
320 // We already have the match rule.
321 method_table_[absolute_signal_name] = signal_callback;
322 success = true;
326 // Run on_connected_callback in the origin thread.
327 bus_->PostTaskToOriginThread(
328 FROM_HERE,
329 base::Bind(&ObjectProxy::OnConnected,
330 this,
331 on_connected_callback,
332 interface_name,
333 signal_name,
334 success));
337 void ObjectProxy::OnConnected(OnConnectedCallback on_connected_callback,
338 const std::string& interface_name,
339 const std::string& signal_name,
340 bool success) {
341 bus_->AssertOnOriginThread();
343 on_connected_callback.Run(interface_name, signal_name, success);
346 DBusHandlerResult ObjectProxy::HandleMessage(
347 DBusConnection* connection,
348 DBusMessage* raw_message) {
349 bus_->AssertOnDBusThread();
350 if (dbus_message_get_type(raw_message) != DBUS_MESSAGE_TYPE_SIGNAL)
351 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
353 // raw_message will be unrefed on exit of the function. Increment the
354 // reference so we can use it in Signal.
355 dbus_message_ref(raw_message);
356 scoped_ptr<Signal> signal(
357 Signal::FromRawMessage(raw_message));
359 // Verify the signal comes from the object we're proxying for, this is
360 // our last chance to return DBUS_HANDLER_RESULT_NOT_YET_HANDLED and
361 // allow other object proxies to handle instead.
362 const dbus::ObjectPath path = signal->GetPath();
363 if (path != object_path_) {
364 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
367 const std::string interface = signal->GetInterface();
368 const std::string member = signal->GetMember();
370 // Check if we know about the signal.
371 const std::string absolute_signal_name = GetAbsoluteSignalName(
372 interface, member);
373 MethodTable::const_iterator iter = method_table_.find(absolute_signal_name);
374 if (iter == method_table_.end()) {
375 // Don't know about the signal.
376 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
378 VLOG(1) << "Signal received: " << signal->ToString();
380 const base::TimeTicks start_time = base::TimeTicks::Now();
381 if (bus_->HasDBusThread()) {
382 // Post a task to run the method in the origin thread.
383 // Transfer the ownership of |signal| to RunMethod().
384 // |released_signal| will be deleted in RunMethod().
385 Signal* released_signal = signal.release();
386 bus_->PostTaskToOriginThread(FROM_HERE,
387 base::Bind(&ObjectProxy::RunMethod,
388 this,
389 start_time,
390 iter->second,
391 released_signal));
392 } else {
393 const base::TimeTicks start_time = base::TimeTicks::Now();
394 // If the D-Bus thread is not used, just call the callback on the
395 // current thread. Transfer the ownership of |signal| to RunMethod().
396 Signal* released_signal = signal.release();
397 RunMethod(start_time, iter->second, released_signal);
400 return DBUS_HANDLER_RESULT_HANDLED;
403 void ObjectProxy::RunMethod(base::TimeTicks start_time,
404 SignalCallback signal_callback,
405 Signal* signal) {
406 bus_->AssertOnOriginThread();
408 signal_callback.Run(signal);
409 delete signal;
410 // Record time spent for handling the signal.
411 UMA_HISTOGRAM_TIMES("DBus.SignalHandleTime",
412 base::TimeTicks::Now() - start_time);
415 DBusHandlerResult ObjectProxy::HandleMessageThunk(
416 DBusConnection* connection,
417 DBusMessage* raw_message,
418 void* user_data) {
419 ObjectProxy* self = reinterpret_cast<ObjectProxy*>(user_data);
420 return self->HandleMessage(connection, raw_message);
423 void ObjectProxy::LogMethodCallFailure(
424 const base::StringPiece& error_name,
425 const base::StringPiece& error_message) const {
426 if (ignore_service_unknown_errors_ && error_name == kErrorServiceUnknown)
427 return;
428 LOG(ERROR) << "Failed to call method: " << error_name
429 << ": " << error_message;
432 } // namespace dbus