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