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