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 std::unique_ptr<Response> ObjectProxy::CallMethodAndBlockWithErrorDetails(
73 MethodCall* method_call,
74 int timeout_ms,
75 ScopedDBusError* error) {
76 bus_->AssertOnDBusThread();
77
78 if (!bus_->Connect() ||
79 !method_call->SetDestination(service_name_) ||
80 !method_call->SetPath(object_path_))
81 return std::unique_ptr<Response>();
82
83 DBusMessage* request_message = method_call->raw_message();
84
85 // Send the message synchronously.
86 const base::TimeTicks start_time = base::TimeTicks::Now();
87 DBusMessage* response_message =
88 bus_->SendWithReplyAndBlock(request_message, timeout_ms, error->get());
89 // Record if the method call is successful, or not. 1 if successful.
90 UMA_HISTOGRAM_ENUMERATION("DBus.SyncMethodCallSuccess",
91 response_message ? 1 : 0,
92 kSuccessRatioHistogramMaxValue);
93 statistics::AddBlockingSentMethodCall(service_name_,
94 method_call->GetInterface(),
95 method_call->GetMember());
96
97 if (!response_message) {
98 LogMethodCallFailure(method_call->GetInterface(),
99 method_call->GetMember(),
100 error->is_set() ? error->name() : "unknown error type",
101 error->is_set() ? error->message() : "");
102 return std::unique_ptr<Response>();
103 }
104 // Record time spent for the method call. Don't include failures.
105 UMA_HISTOGRAM_TIMES("DBus.SyncMethodCallTime",
106 base::TimeTicks::Now() - start_time);
107
108 return Response::FromRawMessage(response_message);
109 }
110
CallMethodAndBlock(MethodCall * method_call,int timeout_ms)111 std::unique_ptr<Response> ObjectProxy::CallMethodAndBlock(
112 MethodCall* method_call,
113 int timeout_ms) {
114 ScopedDBusError error;
115 return CallMethodAndBlockWithErrorDetails(method_call, timeout_ms, &error);
116 }
117
CallMethod(MethodCall * method_call,int timeout_ms,ResponseCallback callback)118 void ObjectProxy::CallMethod(MethodCall* method_call,
119 int timeout_ms,
120 ResponseCallback callback) {
121 CallMethodWithErrorCallback(method_call, timeout_ms, callback,
122 base::Bind(&ObjectProxy::OnCallMethodError,
123 this,
124 method_call->GetInterface(),
125 method_call->GetMember(),
126 callback));
127 }
128
CallMethodWithErrorCallback(MethodCall * method_call,int timeout_ms,ResponseCallback callback,ErrorCallback error_callback)129 void ObjectProxy::CallMethodWithErrorCallback(MethodCall* method_call,
130 int timeout_ms,
131 ResponseCallback callback,
132 ErrorCallback error_callback) {
133 bus_->AssertOnOriginThread();
134
135 const base::TimeTicks start_time = base::TimeTicks::Now();
136
137 if (!method_call->SetDestination(service_name_) ||
138 !method_call->SetPath(object_path_)) {
139 // In case of a failure, run the error callback with NULL.
140 DBusMessage* response_message = NULL;
141 base::Closure task = base::Bind(&ObjectProxy::RunResponseCallback,
142 this,
143 callback,
144 error_callback,
145 start_time,
146 response_message);
147 bus_->GetOriginTaskRunner()->PostTask(FROM_HERE, task);
148 return;
149 }
150
151 // Increment the reference count so we can safely reference the
152 // underlying request message until the method call is complete. This
153 // will be unref'ed in StartAsyncMethodCall().
154 DBusMessage* request_message = method_call->raw_message();
155 dbus_message_ref(request_message);
156
157 base::Closure task = base::Bind(&ObjectProxy::StartAsyncMethodCall,
158 this,
159 timeout_ms,
160 request_message,
161 callback,
162 error_callback,
163 start_time);
164 statistics::AddSentMethodCall(service_name_,
165 method_call->GetInterface(),
166 method_call->GetMember());
167
168 // Wait for the response in the D-Bus thread.
169 bus_->GetDBusTaskRunner()->PostTask(FROM_HERE, task);
170 }
171
ConnectToSignal(const std::string & interface_name,const std::string & signal_name,SignalCallback signal_callback,OnConnectedCallback on_connected_callback)172 void ObjectProxy::ConnectToSignal(const std::string& interface_name,
173 const std::string& signal_name,
174 SignalCallback signal_callback,
175 OnConnectedCallback on_connected_callback) {
176 bus_->AssertOnOriginThread();
177
178 if (bus_->HasDBusThread()) {
179 base::PostTaskAndReplyWithResult(
180 bus_->GetDBusTaskRunner(), FROM_HERE,
181 base::Bind(&ObjectProxy::ConnectToSignalInternal, this, interface_name,
182 signal_name, signal_callback),
183 base::Bind(on_connected_callback, interface_name, signal_name));
184 } else {
185 // If the bus doesn't have a dedicated dbus thread we need to call
186 // ConnectToSignalInternal directly otherwise we might miss a signal
187 // that is currently queued if we do a PostTask.
188 const bool success =
189 ConnectToSignalInternal(interface_name, signal_name, signal_callback);
190 on_connected_callback.Run(interface_name, signal_name, success);
191 }
192 }
193
SetNameOwnerChangedCallback(NameOwnerChangedCallback callback)194 void ObjectProxy::SetNameOwnerChangedCallback(
195 NameOwnerChangedCallback callback) {
196 bus_->AssertOnOriginThread();
197
198 name_owner_changed_callback_ = callback;
199 }
200
WaitForServiceToBeAvailable(WaitForServiceToBeAvailableCallback callback)201 void ObjectProxy::WaitForServiceToBeAvailable(
202 WaitForServiceToBeAvailableCallback callback) {
203 bus_->AssertOnOriginThread();
204
205 wait_for_service_to_be_available_callbacks_.push_back(callback);
206 bus_->GetDBusTaskRunner()->PostTask(
207 FROM_HERE,
208 base::Bind(&ObjectProxy::WaitForServiceToBeAvailableInternal, this));
209 }
210
Detach()211 void ObjectProxy::Detach() {
212 bus_->AssertOnDBusThread();
213
214 if (bus_->is_connected())
215 bus_->RemoveFilterFunction(&ObjectProxy::HandleMessageThunk, this);
216
217 for (const auto& match_rule : match_rules_) {
218 ScopedDBusError error;
219 bus_->RemoveMatch(match_rule, error.get());
220 if (error.is_set()) {
221 // There is nothing we can do to recover, so just print the error.
222 LOG(ERROR) << "Failed to remove match rule: " << match_rule;
223 }
224 }
225 match_rules_.clear();
226
227 for (auto* pending_call : pending_calls_) {
228 dbus_pending_call_cancel(pending_call);
229 dbus_pending_call_unref(pending_call);
230 }
231 pending_calls_.clear();
232 }
233
234 // static
EmptyResponseCallback()235 ObjectProxy::ResponseCallback ObjectProxy::EmptyResponseCallback() {
236 return base::Bind(&EmptyResponseCallbackBody);
237 }
238
OnPendingCallIsCompleteData(ObjectProxy * in_object_proxy,ResponseCallback in_response_callback,ErrorCallback in_error_callback,base::TimeTicks in_start_time)239 ObjectProxy::OnPendingCallIsCompleteData::OnPendingCallIsCompleteData(
240 ObjectProxy* in_object_proxy,
241 ResponseCallback in_response_callback,
242 ErrorCallback in_error_callback,
243 base::TimeTicks in_start_time)
244 : object_proxy(in_object_proxy),
245 response_callback(in_response_callback),
246 error_callback(in_error_callback),
247 start_time(in_start_time) {
248 }
249
~OnPendingCallIsCompleteData()250 ObjectProxy::OnPendingCallIsCompleteData::~OnPendingCallIsCompleteData() {
251 }
252
StartAsyncMethodCall(int timeout_ms,DBusMessage * request_message,ResponseCallback response_callback,ErrorCallback error_callback,base::TimeTicks start_time)253 void ObjectProxy::StartAsyncMethodCall(int timeout_ms,
254 DBusMessage* request_message,
255 ResponseCallback response_callback,
256 ErrorCallback error_callback,
257 base::TimeTicks start_time) {
258 bus_->AssertOnDBusThread();
259
260 if (!bus_->Connect() || !bus_->SetUpAsyncOperations()) {
261 // In case of a failure, run the error callback with NULL.
262 DBusMessage* response_message = NULL;
263 base::Closure task = base::Bind(&ObjectProxy::RunResponseCallback,
264 this,
265 response_callback,
266 error_callback,
267 start_time,
268 response_message);
269 bus_->GetOriginTaskRunner()->PostTask(FROM_HERE, task);
270
271 dbus_message_unref(request_message);
272 return;
273 }
274
275 DBusPendingCall* pending_call = NULL;
276
277 bus_->SendWithReply(request_message, &pending_call, timeout_ms);
278
279 // Prepare the data we'll be passing to OnPendingCallIsCompleteThunk().
280 // The data will be deleted in OnPendingCallIsCompleteThunk().
281 OnPendingCallIsCompleteData* data =
282 new OnPendingCallIsCompleteData(this, response_callback, error_callback,
283 start_time);
284
285 // This returns false only when unable to allocate memory.
286 const bool success = dbus_pending_call_set_notify(
287 pending_call,
288 &ObjectProxy::OnPendingCallIsCompleteThunk,
289 data,
290 &DeleteVoidPointer<OnPendingCallIsCompleteData>);
291 CHECK(success) << "Unable to allocate memory";
292 pending_calls_.insert(pending_call);
293
294 // It's now safe to unref the request message.
295 dbus_message_unref(request_message);
296 }
297
OnPendingCallIsComplete(DBusPendingCall * pending_call,ResponseCallback response_callback,ErrorCallback error_callback,base::TimeTicks start_time)298 void ObjectProxy::OnPendingCallIsComplete(DBusPendingCall* pending_call,
299 ResponseCallback response_callback,
300 ErrorCallback error_callback,
301 base::TimeTicks start_time) {
302 bus_->AssertOnDBusThread();
303
304 DBusMessage* response_message = dbus_pending_call_steal_reply(pending_call);
305 base::Closure task = base::Bind(&ObjectProxy::RunResponseCallback,
306 this,
307 response_callback,
308 error_callback,
309 start_time,
310 response_message);
311 bus_->GetOriginTaskRunner()->PostTask(FROM_HERE, task);
312
313 // Remove the pending call from the set.
314 pending_calls_.erase(pending_call);
315 dbus_pending_call_unref(pending_call);
316 }
317
RunResponseCallback(ResponseCallback response_callback,ErrorCallback error_callback,base::TimeTicks start_time,DBusMessage * response_message)318 void ObjectProxy::RunResponseCallback(ResponseCallback response_callback,
319 ErrorCallback error_callback,
320 base::TimeTicks start_time,
321 DBusMessage* response_message) {
322 bus_->AssertOnOriginThread();
323
324 bool method_call_successful = false;
325 if (!response_message) {
326 // The response is not received.
327 error_callback.Run(NULL);
328 } else if (dbus_message_get_type(response_message) ==
329 DBUS_MESSAGE_TYPE_ERROR) {
330 // This will take |response_message| and release (unref) it.
331 std::unique_ptr<ErrorResponse> error_response(
332 ErrorResponse::FromRawMessage(response_message));
333 error_callback.Run(error_response.get());
334 // Delete the message on the D-Bus thread. See below for why.
335 bus_->GetDBusTaskRunner()->PostTask(
336 FROM_HERE,
337 base::Bind(&base::DeletePointer<ErrorResponse>,
338 error_response.release()));
339 } else {
340 // This will take |response_message| and release (unref) it.
341 std::unique_ptr<Response> response(
342 Response::FromRawMessage(response_message));
343 // The response is successfully received.
344 response_callback.Run(response.get());
345 // The message should be deleted on the D-Bus thread for a complicated
346 // reason:
347 //
348 // libdbus keeps track of the number of bytes in the incoming message
349 // queue to ensure that the data size in the queue is manageable. The
350 // bookkeeping is partly done via dbus_message_unref(), and immediately
351 // asks the client code (Chrome) to stop monitoring the underlying
352 // socket, if the number of bytes exceeds a certian number, which is set
353 // to 63MB, per dbus-transport.cc:
354 //
355 // /* Try to default to something that won't totally hose the system,
356 // * but doesn't impose too much of a limitation.
357 // */
358 // transport->max_live_messages_size = _DBUS_ONE_MEGABYTE * 63;
359 //
360 // The monitoring of the socket is done on the D-Bus thread (see Watch
361 // class in bus.cc), hence we should stop the monitoring from D-Bus
362 // thread, not from the current thread here, which is likely UI thread.
363 bus_->GetDBusTaskRunner()->PostTask(
364 FROM_HERE,
365 base::Bind(&base::DeletePointer<Response>, response.release()));
366
367 method_call_successful = true;
368 // Record time spent for the method call. Don't include failures.
369 UMA_HISTOGRAM_TIMES("DBus.AsyncMethodCallTime",
370 base::TimeTicks::Now() - start_time);
371 }
372 // Record if the method call is successful, or not. 1 if successful.
373 UMA_HISTOGRAM_ENUMERATION("DBus.AsyncMethodCallSuccess",
374 method_call_successful,
375 kSuccessRatioHistogramMaxValue);
376 }
377
OnPendingCallIsCompleteThunk(DBusPendingCall * pending_call,void * user_data)378 void ObjectProxy::OnPendingCallIsCompleteThunk(DBusPendingCall* pending_call,
379 void* user_data) {
380 OnPendingCallIsCompleteData* data =
381 reinterpret_cast<OnPendingCallIsCompleteData*>(user_data);
382 ObjectProxy* self = data->object_proxy;
383 self->OnPendingCallIsComplete(pending_call,
384 data->response_callback,
385 data->error_callback,
386 data->start_time);
387 }
388
ConnectToNameOwnerChangedSignal()389 bool ObjectProxy::ConnectToNameOwnerChangedSignal() {
390 bus_->AssertOnDBusThread();
391
392 if (!bus_->Connect() || !bus_->SetUpAsyncOperations())
393 return false;
394
395 bus_->AddFilterFunction(&ObjectProxy::HandleMessageThunk, this);
396
397 // Add a match_rule listening NameOwnerChanged for the well-known name
398 // |service_name_|.
399 const std::string name_owner_changed_match_rule =
400 base::StringPrintf(
401 "type='signal',interface='org.freedesktop.DBus',"
402 "member='NameOwnerChanged',path='/org/freedesktop/DBus',"
403 "sender='org.freedesktop.DBus',arg0='%s'",
404 service_name_.c_str());
405
406 const bool success =
407 AddMatchRuleWithoutCallback(name_owner_changed_match_rule,
408 "org.freedesktop.DBus.NameOwnerChanged");
409
410 // Try getting the current name owner. It's not guaranteed that we can get
411 // the name owner at this moment, as the service may not yet be started. If
412 // that's the case, we'll get the name owner via NameOwnerChanged signal,
413 // as soon as the service is started.
414 UpdateNameOwnerAndBlock();
415
416 return success;
417 }
418
ConnectToSignalInternal(const std::string & interface_name,const std::string & signal_name,SignalCallback signal_callback)419 bool ObjectProxy::ConnectToSignalInternal(const std::string& interface_name,
420 const std::string& signal_name,
421 SignalCallback signal_callback) {
422 bus_->AssertOnDBusThread();
423
424 if (!ConnectToNameOwnerChangedSignal())
425 return false;
426
427 const std::string absolute_signal_name =
428 GetAbsoluteMemberName(interface_name, signal_name);
429
430 // Add a match rule so the signal goes through HandleMessage().
431 const std::string match_rule =
432 base::StringPrintf("type='signal', interface='%s', path='%s'",
433 interface_name.c_str(),
434 object_path_.value().c_str());
435 return AddMatchRuleWithCallback(match_rule,
436 absolute_signal_name,
437 signal_callback);
438 }
439
WaitForServiceToBeAvailableInternal()440 void ObjectProxy::WaitForServiceToBeAvailableInternal() {
441 bus_->AssertOnDBusThread();
442
443 if (!ConnectToNameOwnerChangedSignal()) { // Failed to connect to the signal.
444 const bool service_is_ready = false;
445 bus_->GetOriginTaskRunner()->PostTask(
446 FROM_HERE,
447 base::Bind(&ObjectProxy::RunWaitForServiceToBeAvailableCallbacks,
448 this, service_is_ready));
449 return;
450 }
451
452 const bool service_is_available = !service_name_owner_.empty();
453 if (service_is_available) { // Service is already available.
454 bus_->GetOriginTaskRunner()->PostTask(
455 FROM_HERE,
456 base::Bind(&ObjectProxy::RunWaitForServiceToBeAvailableCallbacks,
457 this, service_is_available));
458 return;
459 }
460 }
461
HandleMessage(DBusConnection *,DBusMessage * raw_message)462 DBusHandlerResult ObjectProxy::HandleMessage(DBusConnection*,
463 DBusMessage* raw_message) {
464 bus_->AssertOnDBusThread();
465
466 if (dbus_message_get_type(raw_message) != DBUS_MESSAGE_TYPE_SIGNAL)
467 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
468
469 // raw_message will be unrefed on exit of the function. Increment the
470 // reference so we can use it in Signal.
471 dbus_message_ref(raw_message);
472 std::unique_ptr<Signal> signal(Signal::FromRawMessage(raw_message));
473
474 // Verify the signal comes from the object we're proxying for, this is
475 // our last chance to return DBUS_HANDLER_RESULT_NOT_YET_HANDLED and
476 // allow other object proxies to handle instead.
477 const ObjectPath path = signal->GetPath();
478 if (path != object_path_) {
479 if (path.value() == kDBusSystemObjectPath &&
480 signal->GetMember() == kNameOwnerChangedMember) {
481 // Handle NameOwnerChanged separately
482 return HandleNameOwnerChanged(std::move(signal));
483 }
484 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
485 }
486
487 const std::string interface = signal->GetInterface();
488 const std::string member = signal->GetMember();
489
490 statistics::AddReceivedSignal(service_name_, interface, member);
491
492 // Check if we know about the signal.
493 const std::string absolute_signal_name = GetAbsoluteMemberName(
494 interface, member);
495 MethodTable::const_iterator iter = method_table_.find(absolute_signal_name);
496 if (iter == method_table_.end()) {
497 // Don't know about the signal.
498 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
499 }
500 VLOG(1) << "Signal received: " << signal->ToString();
501
502 std::string sender = signal->GetSender();
503 if (service_name_owner_ != sender) {
504 LOG(ERROR) << "Rejecting a message from a wrong sender.";
505 UMA_HISTOGRAM_COUNTS("DBus.RejectedSignalCount", 1);
506 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
507 }
508
509 const base::TimeTicks start_time = base::TimeTicks::Now();
510 if (bus_->HasDBusThread()) {
511 // Post a task to run the method in the origin thread.
512 // Transfer the ownership of |signal| to RunMethod().
513 // |released_signal| will be deleted in RunMethod().
514 Signal* released_signal = signal.release();
515 bus_->GetOriginTaskRunner()->PostTask(FROM_HERE,
516 base::Bind(&ObjectProxy::RunMethod,
517 this,
518 start_time,
519 iter->second,
520 released_signal));
521 } else {
522 const base::TimeTicks start_time = base::TimeTicks::Now();
523 // If the D-Bus thread is not used, just call the callback on the
524 // current thread. Transfer the ownership of |signal| to RunMethod().
525 Signal* released_signal = signal.release();
526 RunMethod(start_time, iter->second, released_signal);
527 }
528
529 // We don't return DBUS_HANDLER_RESULT_HANDLED for signals because other
530 // objects may be interested in them. (e.g. Signals from org.freedesktop.DBus)
531 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
532 }
533
RunMethod(base::TimeTicks start_time,std::vector<SignalCallback> signal_callbacks,Signal * signal)534 void ObjectProxy::RunMethod(base::TimeTicks start_time,
535 std::vector<SignalCallback> signal_callbacks,
536 Signal* signal) {
537 bus_->AssertOnOriginThread();
538
539 for (std::vector<SignalCallback>::iterator iter = signal_callbacks.begin();
540 iter != signal_callbacks.end(); ++iter)
541 iter->Run(signal);
542
543 // Delete the message on the D-Bus thread. See comments in
544 // RunResponseCallback().
545 bus_->GetDBusTaskRunner()->PostTask(
546 FROM_HERE,
547 base::Bind(&base::DeletePointer<Signal>, signal));
548
549 // Record time spent for handling the signal.
550 UMA_HISTOGRAM_TIMES("DBus.SignalHandleTime",
551 base::TimeTicks::Now() - start_time);
552 }
553
HandleMessageThunk(DBusConnection * connection,DBusMessage * raw_message,void * user_data)554 DBusHandlerResult ObjectProxy::HandleMessageThunk(
555 DBusConnection* connection,
556 DBusMessage* raw_message,
557 void* user_data) {
558 ObjectProxy* self = reinterpret_cast<ObjectProxy*>(user_data);
559 return self->HandleMessage(connection, raw_message);
560 }
561
LogMethodCallFailure(const base::StringPiece & interface_name,const base::StringPiece & method_name,const base::StringPiece & error_name,const base::StringPiece & error_message) const562 void ObjectProxy::LogMethodCallFailure(
563 const base::StringPiece& interface_name,
564 const base::StringPiece& method_name,
565 const base::StringPiece& error_name,
566 const base::StringPiece& error_message) const {
567 if (ignore_service_unknown_errors_ &&
568 (error_name == kErrorServiceUnknown || error_name == kErrorObjectUnknown))
569 return;
570
571 std::ostringstream msg;
572 msg << "Failed to call method: " << interface_name << "." << method_name
573 << ": object_path= " << object_path_.value()
574 << ": " << error_name << ": " << error_message;
575
576 // "UnknownObject" indicates that an object or service is no longer available,
577 // e.g. a Shill network service has gone out of range. Treat these as warnings
578 // not errors.
579 if (error_name == kErrorObjectUnknown)
580 LOG(WARNING) << msg.str();
581 else
582 LOG(ERROR) << msg.str();
583 }
584
OnCallMethodError(const std::string & interface_name,const std::string & method_name,ResponseCallback response_callback,ErrorResponse * error_response)585 void ObjectProxy::OnCallMethodError(const std::string& interface_name,
586 const std::string& method_name,
587 ResponseCallback response_callback,
588 ErrorResponse* error_response) {
589 if (error_response) {
590 // Error message may contain the error message as string.
591 MessageReader reader(error_response);
592 std::string error_message;
593 reader.PopString(&error_message);
594 LogMethodCallFailure(interface_name,
595 method_name,
596 error_response->GetErrorName(),
597 error_message);
598 }
599 response_callback.Run(NULL);
600 }
601
AddMatchRuleWithCallback(const std::string & match_rule,const std::string & absolute_signal_name,SignalCallback signal_callback)602 bool ObjectProxy::AddMatchRuleWithCallback(
603 const std::string& match_rule,
604 const std::string& absolute_signal_name,
605 SignalCallback signal_callback) {
606 DCHECK(!match_rule.empty());
607 DCHECK(!absolute_signal_name.empty());
608 bus_->AssertOnDBusThread();
609
610 if (match_rules_.find(match_rule) == match_rules_.end()) {
611 ScopedDBusError error;
612 bus_->AddMatch(match_rule, error.get());
613 if (error.is_set()) {
614 LOG(ERROR) << "Failed to add match rule \"" << match_rule << "\". Got "
615 << error.name() << ": " << error.message();
616 return false;
617 } else {
618 // Store the match rule, so that we can remove this in Detach().
619 match_rules_.insert(match_rule);
620 // Add the signal callback to the method table.
621 method_table_[absolute_signal_name].push_back(signal_callback);
622 return true;
623 }
624 } else {
625 // We already have the match rule.
626 method_table_[absolute_signal_name].push_back(signal_callback);
627 return true;
628 }
629 }
630
AddMatchRuleWithoutCallback(const std::string & match_rule,const std::string & absolute_signal_name)631 bool ObjectProxy::AddMatchRuleWithoutCallback(
632 const std::string& match_rule,
633 const std::string& absolute_signal_name) {
634 DCHECK(!match_rule.empty());
635 DCHECK(!absolute_signal_name.empty());
636 bus_->AssertOnDBusThread();
637
638 if (match_rules_.find(match_rule) != match_rules_.end())
639 return true;
640
641 ScopedDBusError error;
642 bus_->AddMatch(match_rule, error.get());
643 if (error.is_set()) {
644 LOG(ERROR) << "Failed to add match rule \"" << match_rule << "\". Got "
645 << error.name() << ": " << error.message();
646 return false;
647 }
648 // Store the match rule, so that we can remove this in Detach().
649 match_rules_.insert(match_rule);
650 return true;
651 }
652
UpdateNameOwnerAndBlock()653 void ObjectProxy::UpdateNameOwnerAndBlock() {
654 bus_->AssertOnDBusThread();
655 // Errors should be suppressed here, as the service may not be yet running
656 // when connecting to signals of the service, which is just fine.
657 // The ObjectProxy will be notified when the service is launched via
658 // NameOwnerChanged signal. See also comments in ConnectToSignalInternal().
659 service_name_owner_ =
660 bus_->GetServiceOwnerAndBlock(service_name_, Bus::SUPPRESS_ERRORS);
661 }
662
HandleNameOwnerChanged(std::unique_ptr<Signal> signal)663 DBusHandlerResult ObjectProxy::HandleNameOwnerChanged(
664 std::unique_ptr<Signal> signal) {
665 DCHECK(signal);
666 bus_->AssertOnDBusThread();
667
668 // Confirm the validity of the NameOwnerChanged signal.
669 if (signal->GetMember() == kNameOwnerChangedMember &&
670 signal->GetInterface() == kDBusSystemObjectInterface &&
671 signal->GetSender() == kDBusSystemObjectAddress) {
672 MessageReader reader(signal.get());
673 std::string name, old_owner, new_owner;
674 if (reader.PopString(&name) &&
675 reader.PopString(&old_owner) &&
676 reader.PopString(&new_owner) &&
677 name == service_name_) {
678 service_name_owner_ = new_owner;
679 bus_->GetOriginTaskRunner()->PostTask(
680 FROM_HERE,
681 base::Bind(&ObjectProxy::RunNameOwnerChangedCallback,
682 this, old_owner, new_owner));
683
684 const bool service_is_available = !service_name_owner_.empty();
685 if (service_is_available) {
686 bus_->GetOriginTaskRunner()->PostTask(
687 FROM_HERE,
688 base::Bind(&ObjectProxy::RunWaitForServiceToBeAvailableCallbacks,
689 this, service_is_available));
690 }
691 }
692 }
693
694 // Always return unhandled to let other object proxies handle the same
695 // signal.
696 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
697 }
698
RunNameOwnerChangedCallback(const std::string & old_owner,const std::string & new_owner)699 void ObjectProxy::RunNameOwnerChangedCallback(const std::string& old_owner,
700 const std::string& new_owner) {
701 bus_->AssertOnOriginThread();
702 if (!name_owner_changed_callback_.is_null())
703 name_owner_changed_callback_.Run(old_owner, new_owner);
704 }
705
RunWaitForServiceToBeAvailableCallbacks(bool service_is_available)706 void ObjectProxy::RunWaitForServiceToBeAvailableCallbacks(
707 bool service_is_available) {
708 bus_->AssertOnOriginThread();
709
710 std::vector<WaitForServiceToBeAvailableCallback> callbacks;
711 callbacks.swap(wait_for_service_to_be_available_callbacks_);
712 for (size_t i = 0; i < callbacks.size(); ++i)
713 callbacks[i].Run(service_is_available);
714 }
715
716 } // namespace dbus
717