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.
4
5 #include "dbus/object_proxy.h"
6
7 #include <stddef.h>
8 #include <utility>
9
10 #include "base/bind.h"
11 #include "base/logging.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/metrics/histogram.h"
14 #include "base/strings/string_piece.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/task_runner_util.h"
17 #include "base/threading/thread.h"
18 #include "base/threading/thread_restrictions.h"
19 #include "dbus/bus.h"
20 #include "dbus/dbus_statistics.h"
21 #include "dbus/message.h"
22 #include "dbus/object_path.h"
23 #include "dbus/scoped_dbus_error.h"
24 #include "dbus/util.h"
25
26 namespace dbus {
27
28 namespace {
29
30 const char kErrorServiceUnknown[] = "org.freedesktop.DBus.Error.ServiceUnknown";
31 const char kErrorObjectUnknown[] = "org.freedesktop.DBus.Error.UnknownObject";
32
33 // Used for success ratio histograms. 1 for success, 0 for failure.
34 const int kSuccessRatioHistogramMaxValue = 2;
35
36 // The path of D-Bus Object sending NameOwnerChanged signal.
37 const char kDBusSystemObjectPath[] = "/org/freedesktop/DBus";
38
39 // The D-Bus Object interface.
40 const char kDBusSystemObjectInterface[] = "org.freedesktop.DBus";
41
42 // The D-Bus Object address.
43 const char kDBusSystemObjectAddress[] = "org.freedesktop.DBus";
44
45 // The NameOwnerChanged member in |kDBusSystemObjectInterface|.
46 const char kNameOwnerChangedMember[] = "NameOwnerChanged";
47
48 // An empty function used for ObjectProxy::EmptyResponseCallback().
EmptyResponseCallbackBody(Response *)49 void EmptyResponseCallbackBody(Response* /*response*/) {
50 }
51
52 } // namespace
53
ObjectProxy(Bus * bus,const std::string & service_name,const ObjectPath & object_path,int options)54 ObjectProxy::ObjectProxy(Bus* bus,
55 const std::string& service_name,
56 const ObjectPath& object_path,
57 int options)
58 : bus_(bus),
59 service_name_(service_name),
60 object_path_(object_path),
61 ignore_service_unknown_errors_(
62 options & IGNORE_SERVICE_UNKNOWN_ERRORS) {
63 }
64
~ObjectProxy()65 ObjectProxy::~ObjectProxy() {
66 DCHECK(pending_calls_.empty());
67 }
68
69 // Originally we tried to make |method_call| a const reference, but we
70 // gave up as dbus_connection_send_with_reply_and_block() takes a
71 // non-const pointer of DBusMessage as the second parameter.
CallMethodAndBlockWithErrorDetails(MethodCall * method_call,int timeout_ms,ScopedDBusError * error)72 scoped_ptr<Response> ObjectProxy::CallMethodAndBlockWithErrorDetails(
73 MethodCall* method_call, int timeout_ms, ScopedDBusError* error) {
74 bus_->AssertOnDBusThread();
75
76 if (!bus_->Connect() ||
77 !method_call->SetDestination(service_name_) ||
78 !method_call->SetPath(object_path_))
79 return scoped_ptr<Response>();
80
81 DBusMessage* request_message = method_call->raw_message();
82
83 // Send the message synchronously.
84 const base::TimeTicks start_time = base::TimeTicks::Now();
85 DBusMessage* response_message =
86 bus_->SendWithReplyAndBlock(request_message, timeout_ms, error->get());
87 // Record if the method call is successful, or not. 1 if successful.
88 UMA_HISTOGRAM_ENUMERATION("DBus.SyncMethodCallSuccess",
89 response_message ? 1 : 0,
90 kSuccessRatioHistogramMaxValue);
91 statistics::AddBlockingSentMethodCall(service_name_,
92 method_call->GetInterface(),
93 method_call->GetMember());
94
95 if (!response_message) {
96 LogMethodCallFailure(method_call->GetInterface(),
97 method_call->GetMember(),
98 error->is_set() ? error->name() : "unknown error type",
99 error->is_set() ? error->message() : "");
100 return scoped_ptr<Response>();
101 }
102 // Record time spent for the method call. Don't include failures.
103 UMA_HISTOGRAM_TIMES("DBus.SyncMethodCallTime",
104 base::TimeTicks::Now() - start_time);
105
106 return Response::FromRawMessage(response_message);
107 }
108
CallMethodAndBlock(MethodCall * method_call,int timeout_ms)109 scoped_ptr<Response> ObjectProxy::CallMethodAndBlock(MethodCall* method_call,
110 int timeout_ms) {
111 ScopedDBusError error;
112 return CallMethodAndBlockWithErrorDetails(method_call, timeout_ms, &error);
113 }
114
CallMethod(MethodCall * method_call,int timeout_ms,ResponseCallback callback)115 void ObjectProxy::CallMethod(MethodCall* method_call,
116 int timeout_ms,
117 ResponseCallback callback) {
118 CallMethodWithErrorCallback(method_call, timeout_ms, callback,
119 base::Bind(&ObjectProxy::OnCallMethodError,
120 this,
121 method_call->GetInterface(),
122 method_call->GetMember(),
123 callback));
124 }
125
CallMethodWithErrorCallback(MethodCall * method_call,int timeout_ms,ResponseCallback callback,ErrorCallback error_callback)126 void ObjectProxy::CallMethodWithErrorCallback(MethodCall* method_call,
127 int timeout_ms,
128 ResponseCallback callback,
129 ErrorCallback error_callback) {
130 bus_->AssertOnOriginThread();
131
132 const base::TimeTicks start_time = base::TimeTicks::Now();
133
134 if (!method_call->SetDestination(service_name_) ||
135 !method_call->SetPath(object_path_)) {
136 // In case of a failure, run the error callback with NULL.
137 DBusMessage* response_message = NULL;
138 base::Closure task = base::Bind(&ObjectProxy::RunResponseCallback,
139 this,
140 callback,
141 error_callback,
142 start_time,
143 response_message);
144 bus_->GetOriginTaskRunner()->PostTask(FROM_HERE, task);
145 return;
146 }
147
148 // Increment the reference count so we can safely reference the
149 // underlying request message until the method call is complete. This
150 // will be unref'ed in StartAsyncMethodCall().
151 DBusMessage* request_message = method_call->raw_message();
152 dbus_message_ref(request_message);
153
154 base::Closure task = base::Bind(&ObjectProxy::StartAsyncMethodCall,
155 this,
156 timeout_ms,
157 request_message,
158 callback,
159 error_callback,
160 start_time);
161 statistics::AddSentMethodCall(service_name_,
162 method_call->GetInterface(),
163 method_call->GetMember());
164
165 // Wait for the response in the D-Bus thread.
166 bus_->GetDBusTaskRunner()->PostTask(FROM_HERE, task);
167 }
168
ConnectToSignal(const std::string & interface_name,const std::string & signal_name,SignalCallback signal_callback,OnConnectedCallback on_connected_callback)169 void ObjectProxy::ConnectToSignal(const std::string& interface_name,
170 const std::string& signal_name,
171 SignalCallback signal_callback,
172 OnConnectedCallback on_connected_callback) {
173 bus_->AssertOnOriginThread();
174
175 if (bus_->HasDBusThread()) {
176 base::PostTaskAndReplyWithResult(
177 bus_->GetDBusTaskRunner(), FROM_HERE,
178 base::Bind(&ObjectProxy::ConnectToSignalInternal, this, interface_name,
179 signal_name, signal_callback),
180 base::Bind(on_connected_callback, interface_name, signal_name));
181 } else {
182 // If the bus doesn't have a dedicated dbus thread we need to call
183 // ConnectToSignalInternal directly otherwise we might miss a signal
184 // that is currently queued if we do a PostTask.
185 const bool success =
186 ConnectToSignalInternal(interface_name, signal_name, signal_callback);
187 on_connected_callback.Run(interface_name, signal_name, success);
188 }
189 }
190
SetNameOwnerChangedCallback(NameOwnerChangedCallback callback)191 void ObjectProxy::SetNameOwnerChangedCallback(
192 NameOwnerChangedCallback callback) {
193 bus_->AssertOnOriginThread();
194
195 name_owner_changed_callback_ = callback;
196 }
197
WaitForServiceToBeAvailable(WaitForServiceToBeAvailableCallback callback)198 void ObjectProxy::WaitForServiceToBeAvailable(
199 WaitForServiceToBeAvailableCallback callback) {
200 bus_->AssertOnOriginThread();
201
202 wait_for_service_to_be_available_callbacks_.push_back(callback);
203 bus_->GetDBusTaskRunner()->PostTask(
204 FROM_HERE,
205 base::Bind(&ObjectProxy::WaitForServiceToBeAvailableInternal, this));
206 }
207
Detach()208 void ObjectProxy::Detach() {
209 bus_->AssertOnDBusThread();
210
211 if (bus_->is_connected())
212 bus_->RemoveFilterFunction(&ObjectProxy::HandleMessageThunk, this);
213
214 for (const auto& match_rule : match_rules_) {
215 ScopedDBusError error;
216 bus_->RemoveMatch(match_rule, error.get());
217 if (error.is_set()) {
218 // There is nothing we can do to recover, so just print the error.
219 LOG(ERROR) << "Failed to remove match rule: " << match_rule;
220 }
221 }
222 match_rules_.clear();
223
224 for (auto* pending_call : pending_calls_) {
225 dbus_pending_call_cancel(pending_call);
226 dbus_pending_call_unref(pending_call);
227 }
228 pending_calls_.clear();
229 }
230
231 // static
EmptyResponseCallback()232 ObjectProxy::ResponseCallback ObjectProxy::EmptyResponseCallback() {
233 return base::Bind(&EmptyResponseCallbackBody);
234 }
235
OnPendingCallIsCompleteData(ObjectProxy * in_object_proxy,ResponseCallback in_response_callback,ErrorCallback in_error_callback,base::TimeTicks in_start_time)236 ObjectProxy::OnPendingCallIsCompleteData::OnPendingCallIsCompleteData(
237 ObjectProxy* in_object_proxy,
238 ResponseCallback in_response_callback,
239 ErrorCallback in_error_callback,
240 base::TimeTicks in_start_time)
241 : object_proxy(in_object_proxy),
242 response_callback(in_response_callback),
243 error_callback(in_error_callback),
244 start_time(in_start_time) {
245 }
246
~OnPendingCallIsCompleteData()247 ObjectProxy::OnPendingCallIsCompleteData::~OnPendingCallIsCompleteData() {
248 }
249
StartAsyncMethodCall(int timeout_ms,DBusMessage * request_message,ResponseCallback response_callback,ErrorCallback error_callback,base::TimeTicks start_time)250 void ObjectProxy::StartAsyncMethodCall(int timeout_ms,
251 DBusMessage* request_message,
252 ResponseCallback response_callback,
253 ErrorCallback error_callback,
254 base::TimeTicks start_time) {
255 bus_->AssertOnDBusThread();
256
257 if (!bus_->Connect() || !bus_->SetUpAsyncOperations()) {
258 // In case of a failure, run the error callback with NULL.
259 DBusMessage* response_message = NULL;
260 base::Closure task = base::Bind(&ObjectProxy::RunResponseCallback,
261 this,
262 response_callback,
263 error_callback,
264 start_time,
265 response_message);
266 bus_->GetOriginTaskRunner()->PostTask(FROM_HERE, task);
267
268 dbus_message_unref(request_message);
269 return;
270 }
271
272 DBusPendingCall* pending_call = NULL;
273
274 bus_->SendWithReply(request_message, &pending_call, timeout_ms);
275
276 // Prepare the data we'll be passing to OnPendingCallIsCompleteThunk().
277 // The data will be deleted in OnPendingCallIsCompleteThunk().
278 OnPendingCallIsCompleteData* data =
279 new OnPendingCallIsCompleteData(this, response_callback, error_callback,
280 start_time);
281
282 // This returns false only when unable to allocate memory.
283 const bool success = dbus_pending_call_set_notify(
284 pending_call,
285 &ObjectProxy::OnPendingCallIsCompleteThunk,
286 data,
287 &DeleteVoidPointer<OnPendingCallIsCompleteData>);
288 CHECK(success) << "Unable to allocate memory";
289 pending_calls_.insert(pending_call);
290
291 // It's now safe to unref the request message.
292 dbus_message_unref(request_message);
293 }
294
OnPendingCallIsComplete(DBusPendingCall * pending_call,ResponseCallback response_callback,ErrorCallback error_callback,base::TimeTicks start_time)295 void ObjectProxy::OnPendingCallIsComplete(DBusPendingCall* pending_call,
296 ResponseCallback response_callback,
297 ErrorCallback error_callback,
298 base::TimeTicks start_time) {
299 bus_->AssertOnDBusThread();
300
301 DBusMessage* response_message = dbus_pending_call_steal_reply(pending_call);
302 base::Closure task = base::Bind(&ObjectProxy::RunResponseCallback,
303 this,
304 response_callback,
305 error_callback,
306 start_time,
307 response_message);
308 bus_->GetOriginTaskRunner()->PostTask(FROM_HERE, task);
309
310 // Remove the pending call from the set.
311 pending_calls_.erase(pending_call);
312 dbus_pending_call_unref(pending_call);
313 }
314
RunResponseCallback(ResponseCallback response_callback,ErrorCallback error_callback,base::TimeTicks start_time,DBusMessage * response_message)315 void ObjectProxy::RunResponseCallback(ResponseCallback response_callback,
316 ErrorCallback error_callback,
317 base::TimeTicks start_time,
318 DBusMessage* response_message) {
319 bus_->AssertOnOriginThread();
320
321 bool method_call_successful = false;
322 if (!response_message) {
323 // The response is not received.
324 error_callback.Run(NULL);
325 } else if (dbus_message_get_type(response_message) ==
326 DBUS_MESSAGE_TYPE_ERROR) {
327 // This will take |response_message| and release (unref) it.
328 scoped_ptr<ErrorResponse> error_response(
329 ErrorResponse::FromRawMessage(response_message));
330 error_callback.Run(error_response.get());
331 // Delete the message on the D-Bus thread. See below for why.
332 bus_->GetDBusTaskRunner()->PostTask(
333 FROM_HERE,
334 base::Bind(&base::DeletePointer<ErrorResponse>,
335 error_response.release()));
336 } else {
337 // This will take |response_message| and release (unref) it.
338 scoped_ptr<Response> response(Response::FromRawMessage(response_message));
339 // The response is successfully received.
340 response_callback.Run(response.get());
341 // The message should be deleted on the D-Bus thread for a complicated
342 // reason:
343 //
344 // libdbus keeps track of the number of bytes in the incoming message
345 // queue to ensure that the data size in the queue is manageable. The
346 // bookkeeping is partly done via dbus_message_unref(), and immediately
347 // asks the client code (Chrome) to stop monitoring the underlying
348 // socket, if the number of bytes exceeds a certian number, which is set
349 // to 63MB, per dbus-transport.cc:
350 //
351 // /* Try to default to something that won't totally hose the system,
352 // * but doesn't impose too much of a limitation.
353 // */
354 // transport->max_live_messages_size = _DBUS_ONE_MEGABYTE * 63;
355 //
356 // The monitoring of the socket is done on the D-Bus thread (see Watch
357 // class in bus.cc), hence we should stop the monitoring from D-Bus
358 // thread, not from the current thread here, which is likely UI thread.
359 bus_->GetDBusTaskRunner()->PostTask(
360 FROM_HERE,
361 base::Bind(&base::DeletePointer<Response>, response.release()));
362
363 method_call_successful = true;
364 // Record time spent for the method call. Don't include failures.
365 UMA_HISTOGRAM_TIMES("DBus.AsyncMethodCallTime",
366 base::TimeTicks::Now() - start_time);
367 }
368 // Record if the method call is successful, or not. 1 if successful.
369 UMA_HISTOGRAM_ENUMERATION("DBus.AsyncMethodCallSuccess",
370 method_call_successful,
371 kSuccessRatioHistogramMaxValue);
372 }
373
OnPendingCallIsCompleteThunk(DBusPendingCall * pending_call,void * user_data)374 void ObjectProxy::OnPendingCallIsCompleteThunk(DBusPendingCall* pending_call,
375 void* user_data) {
376 OnPendingCallIsCompleteData* data =
377 reinterpret_cast<OnPendingCallIsCompleteData*>(user_data);
378 ObjectProxy* self = data->object_proxy;
379 self->OnPendingCallIsComplete(pending_call,
380 data->response_callback,
381 data->error_callback,
382 data->start_time);
383 }
384
ConnectToNameOwnerChangedSignal()385 bool ObjectProxy::ConnectToNameOwnerChangedSignal() {
386 bus_->AssertOnDBusThread();
387
388 if (!bus_->Connect() || !bus_->SetUpAsyncOperations())
389 return false;
390
391 bus_->AddFilterFunction(&ObjectProxy::HandleMessageThunk, this);
392
393 // 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());
401
402 const bool success =
403 AddMatchRuleWithoutCallback(name_owner_changed_match_rule,
404 "org.freedesktop.DBus.NameOwnerChanged");
405
406 // 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();
411
412 return success;
413 }
414
ConnectToSignalInternal(const std::string & interface_name,const std::string & signal_name,SignalCallback signal_callback)415 bool 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 GetAbsoluteMemberName(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
WaitForServiceToBeAvailableInternal()436 void 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
HandleMessage(DBusConnection *,DBusMessage * raw_message)458 DBusHandlerResult ObjectProxy::HandleMessage(
459 DBusConnection* /* connection */,
460 DBusMessage* raw_message) {
461 bus_->AssertOnDBusThread();
462
463 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
472 // 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.
475 const ObjectPath path = signal->GetPath();
476 if (path != object_path_) {
477 if (path.value() == kDBusSystemObjectPath &&
478 signal->GetMember() == kNameOwnerChangedMember) {
479 // Handle NameOwnerChanged separately
480 return HandleNameOwnerChanged(std::move(signal));
481 }
482 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
483 }
484
485 const std::string interface = signal->GetInterface();
486 const std::string member = signal->GetMember();
487
488 statistics::AddReceivedSignal(service_name_, interface, member);
489
490 // Check if we know about the signal.
491 const std::string absolute_signal_name = GetAbsoluteMemberName(
492 interface, member);
493 MethodTable::const_iterator iter = method_table_.find(absolute_signal_name);
494 if (iter == method_table_.end()) {
495 // Don't know about the signal.
496 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
497 }
498 VLOG(1) << "Signal received: " << signal->ToString();
499
500 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
507 const base::TimeTicks start_time = base::TimeTicks::Now();
508 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();
513 bus_->GetOriginTaskRunner()->PostTask(FROM_HERE,
514 base::Bind(&ObjectProxy::RunMethod,
515 this,
516 start_time,
517 iter->second,
518 released_signal));
519 } else {
520 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);
525 }
526
527 // 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;
530 }
531
RunMethod(base::TimeTicks start_time,std::vector<SignalCallback> signal_callbacks,Signal * signal)532 void ObjectProxy::RunMethod(base::TimeTicks start_time,
533 std::vector<SignalCallback> signal_callbacks,
534 Signal* signal) {
535 bus_->AssertOnOriginThread();
536
537 for (std::vector<SignalCallback>::iterator iter = signal_callbacks.begin();
538 iter != signal_callbacks.end(); ++iter)
539 iter->Run(signal);
540
541 // Delete the message on the D-Bus thread. See comments in
542 // RunResponseCallback().
543 bus_->GetDBusTaskRunner()->PostTask(
544 FROM_HERE,
545 base::Bind(&base::DeletePointer<Signal>, signal));
546
547 // Record time spent for handling the signal.
548 UMA_HISTOGRAM_TIMES("DBus.SignalHandleTime",
549 base::TimeTicks::Now() - start_time);
550 }
551
HandleMessageThunk(DBusConnection * connection,DBusMessage * raw_message,void * user_data)552 DBusHandlerResult 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
LogMethodCallFailure(const base::StringPiece & interface_name,const base::StringPiece & method_name,const base::StringPiece & error_name,const base::StringPiece & error_message) const560 void ObjectProxy::LogMethodCallFailure(
561 const base::StringPiece& interface_name,
562 const base::StringPiece& method_name,
563 const base::StringPiece& error_name,
564 const base::StringPiece& error_message) const {
565 if (ignore_service_unknown_errors_ &&
566 (error_name == kErrorServiceUnknown || error_name == kErrorObjectUnknown))
567 return;
568 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());
579 }
580
OnCallMethodError(const std::string & interface_name,const std::string & method_name,ResponseCallback response_callback,ErrorResponse * error_response)581 void ObjectProxy::OnCallMethodError(const std::string& interface_name,
582 const std::string& method_name,
583 ResponseCallback response_callback,
584 ErrorResponse* error_response) {
585 if (error_response) {
586 // Error message may contain the error message as string.
587 MessageReader reader(error_response);
588 std::string error_message;
589 reader.PopString(&error_message);
590 LogMethodCallFailure(interface_name,
591 method_name,
592 error_response->GetErrorName(),
593 error_message);
594 }
595 response_callback.Run(NULL);
596 }
597
AddMatchRuleWithCallback(const std::string & match_rule,const std::string & absolute_signal_name,SignalCallback signal_callback)598 bool 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()) {
610 LOG(ERROR) << "Failed to add match rule \"" << match_rule << "\". Got "
611 << error.name() << ": " << error.message();
612 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.
617 method_table_[absolute_signal_name].push_back(signal_callback);
618 return true;
619 }
620 } else {
621 // We already have the match rule.
622 method_table_[absolute_signal_name].push_back(signal_callback);
623 return true;
624 }
625 }
626
AddMatchRuleWithoutCallback(const std::string & match_rule,const std::string & absolute_signal_name)627 bool 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()) {
640 LOG(ERROR) << "Failed to add match rule \"" << match_rule << "\". Got "
641 << error.name() << ": " << error.message();
642 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
UpdateNameOwnerAndBlock()649 void ObjectProxy::UpdateNameOwnerAndBlock() {
650 bus_->AssertOnDBusThread();
651 // 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().
655 service_name_owner_ =
656 bus_->GetServiceOwnerAndBlock(service_name_, Bus::SUPPRESS_ERRORS);
657 }
658
HandleNameOwnerChanged(scoped_ptr<Signal> signal)659 DBusHandlerResult ObjectProxy::HandleNameOwnerChanged(
660 scoped_ptr<Signal> signal) {
661 DCHECK(signal);
662 bus_->AssertOnDBusThread();
663
664 // Confirm the validity of the NameOwnerChanged signal.
665 if (signal->GetMember() == kNameOwnerChangedMember &&
666 signal->GetInterface() == kDBusSystemObjectInterface &&
667 signal->GetSender() == kDBusSystemObjectAddress) {
668 MessageReader reader(signal.get());
669 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;
675 bus_->GetOriginTaskRunner()->PostTask(
676 FROM_HERE,
677 base::Bind(&ObjectProxy::RunNameOwnerChangedCallback,
678 this, old_owner, new_owner));
679
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 }
687 }
688 }
689
690 // Always return unhandled to let other object proxies handle the same
691 // signal.
692 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
693 }
694
RunNameOwnerChangedCallback(const std::string & old_owner,const std::string & new_owner)695 void 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
RunWaitForServiceToBeAvailableCallbacks(bool service_is_available)702 void 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
712 } // namespace dbus
713