object_proxy.cc revision b2df76ea8fec9e32f6f3718986dba0d95315b29c
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/bus.h"
6
7#include "base/bind.h"
8#include "base/logging.h"
9#include "base/message_loop.h"
10#include "base/metrics/histogram.h"
11#include "base/stringprintf.h"
12#include "base/strings/string_piece.h"
13#include "base/threading/thread.h"
14#include "base/threading/thread_restrictions.h"
15#include "dbus/dbus_statistics.h"
16#include "dbus/message.h"
17#include "dbus/object_path.h"
18#include "dbus/object_proxy.h"
19#include "dbus/scoped_dbus_error.h"
20
21namespace dbus {
22
23namespace {
24
25const char kErrorServiceUnknown[] = "org.freedesktop.DBus.Error.ServiceUnknown";
26
27// Used for success ratio histograms. 1 for success, 0 for failure.
28const int kSuccessRatioHistogramMaxValue = 2;
29
30// The path of D-Bus Object sending NameOwnerChanged signal.
31const char kDBusSystemObjectPath[] = "/org/freedesktop/DBus";
32
33// The D-Bus Object interface.
34const char kDBusSystemObjectInterface[] = "org.freedesktop.DBus";
35
36// The D-Bus Object address.
37const char kDBusSystemObjectAddress[] = "org.freedesktop.DBus";
38
39// The NameOwnerChanged member in |kDBusSystemObjectInterface|.
40const char kNameOwnerChangedMember[] = "NameOwnerChanged";
41
42// Gets the absolute signal name by concatenating the interface name and
43// the signal name. Used for building keys for method_table_ in
44// ObjectProxy.
45std::string GetAbsoluteSignalName(
46    const std::string& interface_name,
47    const std::string& signal_name) {
48  return interface_name + "." + signal_name;
49}
50
51// An empty function used for ObjectProxy::EmptyResponseCallback().
52void EmptyResponseCallbackBody(Response* /*response*/) {
53}
54
55}  // namespace
56
57ObjectProxy::ObjectProxy(Bus* bus,
58                         const std::string& service_name,
59                         const ObjectPath& object_path,
60                         int options)
61    : bus_(bus),
62      service_name_(service_name),
63      object_path_(object_path),
64      filter_added_(false),
65      ignore_service_unknown_errors_(
66          options & IGNORE_SERVICE_UNKNOWN_ERRORS) {
67}
68
69ObjectProxy::~ObjectProxy() {
70}
71
72// Originally we tried to make |method_call| a const reference, but we
73// gave up as dbus_connection_send_with_reply_and_block() takes a
74// non-const pointer of DBusMessage as the second parameter.
75scoped_ptr<Response> ObjectProxy::CallMethodAndBlock(MethodCall* method_call,
76                                                     int timeout_ms) {
77  bus_->AssertOnDBusThread();
78
79  if (!bus_->Connect() ||
80      !method_call->SetDestination(service_name_) ||
81      !method_call->SetPath(object_path_))
82    return scoped_ptr<Response>();
83
84  DBusMessage* request_message = method_call->raw_message();
85
86  ScopedDBusError error;
87
88  // Send the message synchronously.
89  const base::TimeTicks start_time = base::TimeTicks::Now();
90  DBusMessage* response_message =
91      bus_->SendWithReplyAndBlock(request_message, timeout_ms, error.get());
92  // Record if the method call is successful, or not. 1 if successful.
93  UMA_HISTOGRAM_ENUMERATION("DBus.SyncMethodCallSuccess",
94                            response_message ? 1 : 0,
95                            kSuccessRatioHistogramMaxValue);
96  statistics::AddBlockingSentMethodCall(service_name_,
97                                        method_call->GetInterface(),
98                                        method_call->GetMember());
99
100  if (!response_message) {
101    LogMethodCallFailure(method_call->GetInterface(),
102                         method_call->GetMember(),
103                         error.is_set() ? error.name() : "unknown error type",
104                         error.is_set() ? error.message() : "");
105    return scoped_ptr<Response>();
106  }
107  // Record time spent for the method call. Don't include failures.
108  UMA_HISTOGRAM_TIMES("DBus.SyncMethodCallTime",
109                      base::TimeTicks::Now() - start_time);
110
111  return Response::FromRawMessage(response_message);
112}
113
114void ObjectProxy::CallMethod(MethodCall* method_call,
115                             int timeout_ms,
116                             ResponseCallback callback) {
117  CallMethodWithErrorCallback(method_call, timeout_ms, callback,
118                              base::Bind(&ObjectProxy::OnCallMethodError,
119                                         this,
120                                         method_call->GetInterface(),
121                                         method_call->GetMember(),
122                                         callback));
123}
124
125void ObjectProxy::CallMethodWithErrorCallback(MethodCall* method_call,
126                                              int timeout_ms,
127                                              ResponseCallback callback,
128                                              ErrorCallback error_callback) {
129  bus_->AssertOnOriginThread();
130
131  const base::TimeTicks start_time = base::TimeTicks::Now();
132
133  if (!method_call->SetDestination(service_name_) ||
134      !method_call->SetPath(object_path_)) {
135    // In case of a failure, run the error callback with NULL.
136    DBusMessage* response_message = NULL;
137    base::Closure task = base::Bind(&ObjectProxy::RunResponseCallback,
138                                    this,
139                                    callback,
140                                    error_callback,
141                                    start_time,
142                                    response_message);
143    bus_->PostTaskToOriginThread(FROM_HERE, task);
144    return;
145  }
146
147  // Increment the reference count so we can safely reference the
148  // underlying request message until the method call is complete. This
149  // will be unref'ed in StartAsyncMethodCall().
150  DBusMessage* request_message = method_call->raw_message();
151  dbus_message_ref(request_message);
152
153  base::Closure task = base::Bind(&ObjectProxy::StartAsyncMethodCall,
154                                  this,
155                                  timeout_ms,
156                                  request_message,
157                                  callback,
158                                  error_callback,
159                                  start_time);
160  statistics::AddSentMethodCall(service_name_,
161                                method_call->GetInterface(),
162                                method_call->GetMember());
163
164  // Wait for the response in the D-Bus thread.
165  bus_->PostTaskToDBusThread(FROM_HERE, task);
166}
167
168void ObjectProxy::ConnectToSignal(const std::string& interface_name,
169                                  const std::string& signal_name,
170                                  SignalCallback signal_callback,
171                                  OnConnectedCallback on_connected_callback) {
172  bus_->AssertOnOriginThread();
173
174  bus_->PostTaskToDBusThread(FROM_HERE,
175                             base::Bind(&ObjectProxy::ConnectToSignalInternal,
176                                        this,
177                                        interface_name,
178                                        signal_name,
179                                        signal_callback,
180                                        on_connected_callback));
181}
182
183void ObjectProxy::Detach() {
184  bus_->AssertOnDBusThread();
185
186  if (filter_added_) {
187    if (!bus_->RemoveFilterFunction(&ObjectProxy::HandleMessageThunk, this)) {
188      LOG(ERROR) << "Failed to remove filter function";
189    }
190  }
191
192  for (std::set<std::string>::iterator iter = match_rules_.begin();
193       iter != match_rules_.end(); ++iter) {
194    ScopedDBusError error;
195    bus_->RemoveMatch(*iter, error.get());
196    if (error.is_set()) {
197      // There is nothing we can do to recover, so just print the error.
198      LOG(ERROR) << "Failed to remove match rule: " << *iter;
199    }
200  }
201  match_rules_.clear();
202}
203
204// static
205ObjectProxy::ResponseCallback ObjectProxy::EmptyResponseCallback() {
206  return base::Bind(&EmptyResponseCallbackBody);
207}
208
209ObjectProxy::OnPendingCallIsCompleteData::OnPendingCallIsCompleteData(
210    ObjectProxy* in_object_proxy,
211    ResponseCallback in_response_callback,
212    ErrorCallback in_error_callback,
213    base::TimeTicks in_start_time)
214    : object_proxy(in_object_proxy),
215      response_callback(in_response_callback),
216      error_callback(in_error_callback),
217      start_time(in_start_time) {
218}
219
220ObjectProxy::OnPendingCallIsCompleteData::~OnPendingCallIsCompleteData() {
221}
222
223void ObjectProxy::StartAsyncMethodCall(int timeout_ms,
224                                       DBusMessage* request_message,
225                                       ResponseCallback response_callback,
226                                       ErrorCallback error_callback,
227                                       base::TimeTicks start_time) {
228  bus_->AssertOnDBusThread();
229
230  if (!bus_->Connect() || !bus_->SetUpAsyncOperations()) {
231    // In case of a failure, run the error callback with NULL.
232    DBusMessage* response_message = NULL;
233    base::Closure task = base::Bind(&ObjectProxy::RunResponseCallback,
234                                    this,
235                                    response_callback,
236                                    error_callback,
237                                    start_time,
238                                    response_message);
239    bus_->PostTaskToOriginThread(FROM_HERE, task);
240
241    dbus_message_unref(request_message);
242    return;
243  }
244
245  DBusPendingCall* pending_call = NULL;
246
247  bus_->SendWithReply(request_message, &pending_call, timeout_ms);
248
249  // Prepare the data we'll be passing to OnPendingCallIsCompleteThunk().
250  // The data will be deleted in OnPendingCallIsCompleteThunk().
251  OnPendingCallIsCompleteData* data =
252      new OnPendingCallIsCompleteData(this, response_callback, error_callback,
253                                      start_time);
254
255  // This returns false only when unable to allocate memory.
256  const bool success = dbus_pending_call_set_notify(
257      pending_call,
258      &ObjectProxy::OnPendingCallIsCompleteThunk,
259      data,
260      NULL);
261  CHECK(success) << "Unable to allocate memory";
262  dbus_pending_call_unref(pending_call);
263
264  // It's now safe to unref the request message.
265  dbus_message_unref(request_message);
266}
267
268void ObjectProxy::OnPendingCallIsComplete(DBusPendingCall* pending_call,
269                                          ResponseCallback response_callback,
270                                          ErrorCallback error_callback,
271                                          base::TimeTicks start_time) {
272  bus_->AssertOnDBusThread();
273
274  DBusMessage* response_message = dbus_pending_call_steal_reply(pending_call);
275  base::Closure task = base::Bind(&ObjectProxy::RunResponseCallback,
276                                  this,
277                                  response_callback,
278                                  error_callback,
279                                  start_time,
280                                  response_message);
281  bus_->PostTaskToOriginThread(FROM_HERE, task);
282}
283
284void ObjectProxy::RunResponseCallback(ResponseCallback response_callback,
285                                      ErrorCallback error_callback,
286                                      base::TimeTicks start_time,
287                                      DBusMessage* response_message) {
288  bus_->AssertOnOriginThread();
289
290  bool method_call_successful = false;
291  if (!response_message) {
292    // The response is not received.
293    error_callback.Run(NULL);
294  } else if (dbus_message_get_type(response_message) ==
295             DBUS_MESSAGE_TYPE_ERROR) {
296    // This will take |response_message| and release (unref) it.
297    scoped_ptr<ErrorResponse> error_response(
298        ErrorResponse::FromRawMessage(response_message));
299    error_callback.Run(error_response.get());
300    // Delete the message  on the D-Bus thread. See below for why.
301    bus_->PostTaskToDBusThread(
302        FROM_HERE,
303        base::Bind(&base::DeletePointer<ErrorResponse>,
304                   error_response.release()));
305  } else {
306    // This will take |response_message| and release (unref) it.
307    scoped_ptr<Response> response(Response::FromRawMessage(response_message));
308    // The response is successfully received.
309    response_callback.Run(response.get());
310    // The message should be deleted on the D-Bus thread for a complicated
311    // reason:
312    //
313    // libdbus keeps track of the number of bytes in the incoming message
314    // queue to ensure that the data size in the queue is manageable. The
315    // bookkeeping is partly done via dbus_message_unref(), and immediately
316    // asks the client code (Chrome) to stop monitoring the underlying
317    // socket, if the number of bytes exceeds a certian number, which is set
318    // to 63MB, per dbus-transport.cc:
319    //
320    //   /* Try to default to something that won't totally hose the system,
321    //    * but doesn't impose too much of a limitation.
322    //    */
323    //   transport->max_live_messages_size = _DBUS_ONE_MEGABYTE * 63;
324    //
325    // The monitoring of the socket is done on the D-Bus thread (see Watch
326    // class in bus.cc), hence we should stop the monitoring from D-Bus
327    // thread, not from the current thread here, which is likely UI thread.
328    bus_->PostTaskToDBusThread(
329        FROM_HERE,
330        base::Bind(&base::DeletePointer<Response>, response.release()));
331
332    method_call_successful = true;
333    // Record time spent for the method call. Don't include failures.
334    UMA_HISTOGRAM_TIMES("DBus.AsyncMethodCallTime",
335                        base::TimeTicks::Now() - start_time);
336  }
337  // Record if the method call is successful, or not. 1 if successful.
338  UMA_HISTOGRAM_ENUMERATION("DBus.AsyncMethodCallSuccess",
339                            method_call_successful,
340                            kSuccessRatioHistogramMaxValue);
341}
342
343void ObjectProxy::OnPendingCallIsCompleteThunk(DBusPendingCall* pending_call,
344                                               void* user_data) {
345  OnPendingCallIsCompleteData* data =
346      reinterpret_cast<OnPendingCallIsCompleteData*>(user_data);
347  ObjectProxy* self = data->object_proxy;
348  self->OnPendingCallIsComplete(pending_call,
349                                data->response_callback,
350                                data->error_callback,
351                                data->start_time);
352  delete data;
353}
354
355void ObjectProxy::ConnectToSignalInternal(
356    const std::string& interface_name,
357    const std::string& signal_name,
358    SignalCallback signal_callback,
359    OnConnectedCallback on_connected_callback) {
360  bus_->AssertOnDBusThread();
361
362  const std::string absolute_signal_name =
363      GetAbsoluteSignalName(interface_name, signal_name);
364
365  // Will become true, if everything is successful.
366  bool success = false;
367
368  if (bus_->Connect() && bus_->SetUpAsyncOperations()) {
369    // We should add the filter only once. Otherwise, HandleMessage() will
370    // be called more than once.
371    if (!filter_added_) {
372      if (bus_->AddFilterFunction(&ObjectProxy::HandleMessageThunk, this)) {
373        filter_added_ = true;
374      } else {
375        LOG(ERROR) << "Failed to add filter function";
376      }
377    }
378    // Add a match rule so the signal goes through HandleMessage().
379    const std::string match_rule =
380        base::StringPrintf("type='signal', interface='%s', path='%s'",
381                           interface_name.c_str(),
382                           object_path_.value().c_str());
383    // Add a match_rule listening NameOwnerChanged for the well-known name
384    // |service_name_|.
385    const std::string name_owner_changed_match_rule =
386        base::StringPrintf(
387            "type='signal',interface='org.freedesktop.DBus',"
388            "member='NameOwnerChanged',path='/org/freedesktop/DBus',"
389            "sender='org.freedesktop.DBus',arg0='%s'",
390            service_name_.c_str());
391    if (AddMatchRuleWithCallback(match_rule,
392                                 absolute_signal_name,
393                                 signal_callback) &&
394        AddMatchRuleWithoutCallback(name_owner_changed_match_rule,
395                                    "org.freedesktop.DBus.NameOwnerChanged")) {
396      success = true;
397    }
398
399    // Try getting the current name owner. It's not guaranteed that we can get
400    // the name owner at this moment, as the service may not yet be started. If
401    // that's the case, we'll get the name owner via NameOwnerChanged signal,
402    // as soon as the service is started.
403    UpdateNameOwnerAndBlock();
404  }
405
406  // Run on_connected_callback in the origin thread.
407  bus_->PostTaskToOriginThread(
408      FROM_HERE,
409      base::Bind(&ObjectProxy::OnConnected,
410                 this,
411                 on_connected_callback,
412                 interface_name,
413                 signal_name,
414                 success));
415}
416
417void ObjectProxy::OnConnected(OnConnectedCallback on_connected_callback,
418                              const std::string& interface_name,
419                              const std::string& signal_name,
420                              bool success) {
421  bus_->AssertOnOriginThread();
422
423  on_connected_callback.Run(interface_name, signal_name, success);
424}
425
426void ObjectProxy::SetNameOwnerChangedCallback(SignalCallback callback) {
427  bus_->AssertOnOriginThread();
428
429  name_owner_changed_callback_ = callback;
430}
431
432DBusHandlerResult ObjectProxy::HandleMessage(
433    DBusConnection* connection,
434    DBusMessage* raw_message) {
435  bus_->AssertOnDBusThread();
436
437  if (dbus_message_get_type(raw_message) != DBUS_MESSAGE_TYPE_SIGNAL)
438    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
439
440  // raw_message will be unrefed on exit of the function. Increment the
441  // reference so we can use it in Signal.
442  dbus_message_ref(raw_message);
443  scoped_ptr<Signal> signal(
444      Signal::FromRawMessage(raw_message));
445
446  // Verify the signal comes from the object we're proxying for, this is
447  // our last chance to return DBUS_HANDLER_RESULT_NOT_YET_HANDLED and
448  // allow other object proxies to handle instead.
449  const ObjectPath path = signal->GetPath();
450  if (path != object_path_) {
451    if (path.value() == kDBusSystemObjectPath &&
452        signal->GetMember() == kNameOwnerChangedMember) {
453      // Handle NameOwnerChanged separately
454      return HandleNameOwnerChanged(signal.Pass());
455    }
456    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
457  }
458
459  const std::string interface = signal->GetInterface();
460  const std::string member = signal->GetMember();
461
462  statistics::AddReceivedSignal(service_name_, interface, member);
463
464  // Check if we know about the signal.
465  const std::string absolute_signal_name = GetAbsoluteSignalName(
466      interface, member);
467  MethodTable::const_iterator iter = method_table_.find(absolute_signal_name);
468  if (iter == method_table_.end()) {
469    // Don't know about the signal.
470    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
471  }
472  VLOG(1) << "Signal received: " << signal->ToString();
473
474  std::string sender = signal->GetSender();
475  if (service_name_owner_ != sender) {
476    LOG(ERROR) << "Rejecting a message from a wrong sender.";
477    UMA_HISTOGRAM_COUNTS("DBus.RejectedSignalCount", 1);
478    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
479  }
480
481  const base::TimeTicks start_time = base::TimeTicks::Now();
482  if (bus_->HasDBusThread()) {
483    // Post a task to run the method in the origin thread.
484    // Transfer the ownership of |signal| to RunMethod().
485    // |released_signal| will be deleted in RunMethod().
486    Signal* released_signal = signal.release();
487    bus_->PostTaskToOriginThread(FROM_HERE,
488                                 base::Bind(&ObjectProxy::RunMethod,
489                                            this,
490                                            start_time,
491                                            iter->second,
492                                            released_signal));
493  } else {
494    const base::TimeTicks start_time = base::TimeTicks::Now();
495    // If the D-Bus thread is not used, just call the callback on the
496    // current thread. Transfer the ownership of |signal| to RunMethod().
497    Signal* released_signal = signal.release();
498    RunMethod(start_time, iter->second, released_signal);
499  }
500
501  return DBUS_HANDLER_RESULT_HANDLED;
502}
503
504void ObjectProxy::RunMethod(base::TimeTicks start_time,
505                            std::vector<SignalCallback> signal_callbacks,
506                            Signal* signal) {
507  bus_->AssertOnOriginThread();
508
509  for (std::vector<SignalCallback>::iterator iter = signal_callbacks.begin();
510       iter != signal_callbacks.end(); ++iter)
511    iter->Run(signal);
512
513  // Delete the message on the D-Bus thread. See comments in
514  // RunResponseCallback().
515  bus_->PostTaskToDBusThread(
516      FROM_HERE,
517      base::Bind(&base::DeletePointer<Signal>, signal));
518
519  // Record time spent for handling the signal.
520  UMA_HISTOGRAM_TIMES("DBus.SignalHandleTime",
521                      base::TimeTicks::Now() - start_time);
522}
523
524DBusHandlerResult ObjectProxy::HandleMessageThunk(
525    DBusConnection* connection,
526    DBusMessage* raw_message,
527    void* user_data) {
528  ObjectProxy* self = reinterpret_cast<ObjectProxy*>(user_data);
529  return self->HandleMessage(connection, raw_message);
530}
531
532void ObjectProxy::LogMethodCallFailure(
533    const base::StringPiece& interface_name,
534    const base::StringPiece& method_name,
535    const base::StringPiece& error_name,
536    const base::StringPiece& error_message) const {
537  if (ignore_service_unknown_errors_ && error_name == kErrorServiceUnknown)
538    return;
539  LOG(ERROR) << "Failed to call method: "
540             << interface_name << "." << method_name
541             << ": object_path= " << object_path_.value()
542             << ": " << error_name << ": " << error_message;
543}
544
545void ObjectProxy::OnCallMethodError(const std::string& interface_name,
546                                    const std::string& method_name,
547                                    ResponseCallback response_callback,
548                                    ErrorResponse* error_response) {
549  if (error_response) {
550    // Error message may contain the error message as string.
551    MessageReader reader(error_response);
552    std::string error_message;
553    reader.PopString(&error_message);
554    LogMethodCallFailure(interface_name,
555                         method_name,
556                         error_response->GetErrorName(),
557                         error_message);
558  }
559  response_callback.Run(NULL);
560}
561
562bool ObjectProxy::AddMatchRuleWithCallback(
563    const std::string& match_rule,
564    const std::string& absolute_signal_name,
565    SignalCallback signal_callback) {
566  DCHECK(!match_rule.empty());
567  DCHECK(!absolute_signal_name.empty());
568  bus_->AssertOnDBusThread();
569
570  if (match_rules_.find(match_rule) == match_rules_.end()) {
571    ScopedDBusError error;
572    bus_->AddMatch(match_rule, error.get());
573    if (error.is_set()) {
574      LOG(ERROR) << "Failed to add match rule \"" << match_rule << "\". Got "
575                 << error.name() << ": " << error.message();
576      return false;
577    } else {
578      // Store the match rule, so that we can remove this in Detach().
579      match_rules_.insert(match_rule);
580      // Add the signal callback to the method table.
581      method_table_[absolute_signal_name].push_back(signal_callback);
582      return true;
583    }
584  } else {
585    // We already have the match rule.
586    method_table_[absolute_signal_name].push_back(signal_callback);
587    return true;
588  }
589}
590
591bool ObjectProxy::AddMatchRuleWithoutCallback(
592    const std::string& match_rule,
593    const std::string& absolute_signal_name) {
594  DCHECK(!match_rule.empty());
595  DCHECK(!absolute_signal_name.empty());
596  bus_->AssertOnDBusThread();
597
598  if (match_rules_.find(match_rule) != match_rules_.end())
599    return true;
600
601  ScopedDBusError error;
602  bus_->AddMatch(match_rule, error.get());
603  if (error.is_set()) {
604    LOG(ERROR) << "Failed to add match rule \"" << match_rule << "\". Got "
605               << error.name() << ": " << error.message();
606    return false;
607  }
608  // Store the match rule, so that we can remove this in Detach().
609  match_rules_.insert(match_rule);
610  return true;
611}
612
613void ObjectProxy::UpdateNameOwnerAndBlock() {
614  bus_->AssertOnDBusThread();
615  service_name_owner_ =
616      bus_->GetServiceOwnerAndBlock(service_name_, Bus::REPORT_ERRORS);
617}
618
619DBusHandlerResult ObjectProxy::HandleNameOwnerChanged(
620    scoped_ptr<Signal> signal) {
621  DCHECK(signal);
622  bus_->AssertOnDBusThread();
623
624  // Confirm the validity of the NameOwnerChanged signal.
625  if (signal->GetMember() == kNameOwnerChangedMember &&
626      signal->GetInterface() == kDBusSystemObjectInterface &&
627      signal->GetSender() == kDBusSystemObjectAddress) {
628    MessageReader reader(signal.get());
629    std::string name, old_owner, new_owner;
630    if (reader.PopString(&name) &&
631        reader.PopString(&old_owner) &&
632        reader.PopString(&new_owner) &&
633        name == service_name_) {
634      service_name_owner_ = new_owner;
635      if (!name_owner_changed_callback_.is_null()) {
636        const base::TimeTicks start_time = base::TimeTicks::Now();
637        Signal* released_signal = signal.release();
638        std::vector<SignalCallback> callbacks;
639        callbacks.push_back(name_owner_changed_callback_);
640        bus_->PostTaskToOriginThread(FROM_HERE,
641                                     base::Bind(&ObjectProxy::RunMethod,
642                                                this,
643                                                start_time,
644                                                callbacks,
645                                                released_signal));
646      }
647    }
648  }
649
650  // Always return unhandled to let other object proxies handle the same
651  // signal.
652  return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
653}
654
655}  // namespace dbus
656