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