diff options
author | Robert Ginda <rginda@google.com> | 2015-08-20 09:30:11 -0700 |
---|---|---|
committer | Robert Ginda <rginda@google.com> | 2015-08-21 15:36:09 -0700 |
commit | cf92c66d1aa30678beaa64501c9febc3933c23a9 (patch) | |
tree | f30773aa34c979bd4b153240dc2f8a8fced8d89c | |
parent | 892a5c4e23b8dfa5883996417fadcd46361654a5 (diff) | |
download | weaved-cf92c66d1aa30678beaa64501c9febc3933c23a9.tar.gz |
Implement AvahiMdnsClient
Implement the AvahiMdnsClient interface. This is based on code
from peerd/avahi_client.cc and avahi_service_publisher.cc.
Bug: 23288773
Change-Id: If58df0bf3d71c665c764ca8994b43dcff237549e
-rw-r--r-- | Android.mk | 2 | ||||
-rw-r--r-- | buffet/avahi_mdns_client.cc | 314 | ||||
-rw-r--r-- | buffet/avahi_mdns_client.h | 53 | ||||
-rw-r--r-- | buffet/dbus_command_dispatcher.cc | 3 | ||||
-rw-r--r-- | buffet/dbus_command_proxy_unittest.cc | 2 | ||||
-rw-r--r-- | buffet/dbus_constants.cc | 39 | ||||
-rw-r--r-- | buffet/dbus_constants.h | 38 | ||||
-rw-r--r-- | buffet/main.cc | 4 | ||||
-rw-r--r-- | buffet/manager.cc | 2 | ||||
-rw-r--r-- | buffet/mdns_client.h | 12 | ||||
-rw-r--r-- | buffet/webserv_client.cc | 2 |
11 files changed, 451 insertions, 20 deletions
@@ -38,6 +38,7 @@ buffetCommonCIncludes := \ external/gtest/include \ buffetSharedLibraries := \ + libavahi-common \ libchrome \ libchrome-dbus \ libchromeos \ @@ -101,6 +102,7 @@ include $(BUILD_STATIC_LIBRARY) include $(CLEAR_VARS) LOCAL_MODULE := weaved LOCAL_REQUIRED_MODULES := \ + avahi-daemon \ base_state.defaults.json \ base_state.schema.json \ buffet.json \ diff --git a/buffet/avahi_mdns_client.cc b/buffet/avahi_mdns_client.cc index fde28d5..3216546 100644 --- a/buffet/avahi_mdns_client.cc +++ b/buffet/avahi_mdns_client.cc @@ -14,31 +14,331 @@ * limitations under the License. */ +#include <vector> + #include "buffet/avahi_mdns_client.h" +#include "buffet/dbus_constants.h" +#include <avahi-common/defs.h> +#include <avahi-common/address.h> #include <base/guid.h> +#include <chromeos/dbus/async_event_sequencer.h> +#include <chromeos/dbus/dbus_signal_handler.h> +#include <chromeos/dbus/dbus_method_invoker.h> +#include <chromeos/errors/error.h> +#include <dbus/object_path.h> +#include <dbus/object_proxy.h> + +using chromeos::ErrorPtr; +using chromeos::dbus_utils::AsyncEventSequencer; +using chromeos::dbus_utils::CallMethodAndBlock; +using chromeos::dbus_utils::ExtractMethodCallResults; +using CompletionAction = + chromeos::dbus_utils::AsyncEventSequencer::CompletionAction; namespace buffet { -AvahiMdnsClient::AvahiMdnsClient() { +AvahiMdnsClient::AvahiMdnsClient(const scoped_refptr<dbus::Bus> &bus): + bus_(bus) { } AvahiMdnsClient::~AvahiMdnsClient() { } +// NB: This should be the one-and-only definition of this MdnsClient static +// method. +std::unique_ptr<MdnsClient> MdnsClient::CreateInstance( + const scoped_refptr<dbus::Bus> &bus) { + return std::unique_ptr<MdnsClient>{new AvahiMdnsClient(bus)}; +} + +// TODO(rginda): Report errors back to the caller. +// TODO(rginda): Support publishing more than one service. void AvahiMdnsClient::PublishService( - const std::string& service_name, + const std::string& service_type, uint16_t port, const std::map<std::string, std::string>& txt) { - // TODO(avakulenko) + + if (service_state_ == READY) { + // TODO(rginda): Instead of stop/start, we should update the existing + // service when only the txt record changes. + StopPublishing(service_type); + if (service_state_ != UNDEF) { + LOG(ERROR) << "Failed to disable existing service."; + return; + } + } + + service_name_ = base::GenerateGUID(); + service_type_ = service_type; + port_ = port; + txt_ = GetTxtRecord(txt); + + if (avahi_state_ == UNDEF || avahi_state_ == ERROR) { + ConnectToAvahi(); + } else if (avahi_state_ == READY) { + CreateService(); + } else { + CHECK(avahi_state_ == PENDING); + } +} + +// TODO(rginda): If we support publishing more than one service then we +// may need a less ambiguous way of unpublishing them. +void AvahiMdnsClient::StopPublishing(const std::string& service_type) { + if (service_type_.compare(service_type) != 0) { + LOG(ERROR) << "Unknown service type: " << service_type; + return; + } + + if (service_state_ != READY) { + LOG(ERROR) << "Service is not published."; + } + + service_name_.clear(); + service_type_.clear(); + port_ = 0; + + FreeEntryGroup(); +} + +// Transform a service_info to a mDNS compatible TXT record value. +// Concretely, a TXT record consists of a list of strings in the format +// "key=value". Each string must be less than 256 bytes long, since they are +// length/value encoded. Keys may not contain '=' characters, but are +// otherwise unconstrained. +// +// We need a DBus type of "aay", which is a vector<vector<uint8_t>> in our +// bindings. +AvahiMdnsClient::TxtRecord AvahiMdnsClient::GetTxtRecord( + const std::map<std::string, std::string>& txt) { + TxtRecord result; + result.reserve(txt.size()); + for (const auto& kv : txt) { + result.emplace_back(); + std::vector<uint8_t>& record = result.back(); + record.reserve(kv.first.length() + kv.second.length() + 1); + record.insert(record.end(), kv.first.begin(), kv.first.end()); + record.push_back('='); + record.insert(record.end(), kv.second.begin(), kv.second.end()); + } + return result; +} + +void AvahiMdnsClient::ConnectToAvahi() { + avahi_state_ = PENDING; + + avahi_ = bus_->GetObjectProxy( + dbus_constants::avahi::kServiceName, + dbus::ObjectPath(dbus_constants::avahi::kServerPath)); + + // This callback lives for the lifetime of the ObjectProxy. + avahi_->SetNameOwnerChangedCallback( + base::Bind(&AvahiMdnsClient::OnAvahiOwnerChanged, + weak_ptr_factory_.GetWeakPtr())); + + // Reconnect to our signals on a new Avahi instance. + scoped_refptr<AsyncEventSequencer> sequencer(new AsyncEventSequencer()); + chromeos::dbus_utils::ConnectToSignal( + avahi_, + dbus_constants::avahi::kServerInterface, + dbus_constants::avahi::kServerSignalStateChanged, + base::Bind(&AvahiMdnsClient::OnAvahiStateChanged, + weak_ptr_factory_.GetWeakPtr()), + sequencer->GetExportHandler( + dbus_constants::avahi::kServerInterface, + dbus_constants::avahi::kServerSignalStateChanged, + "Failed to subscribe to Avahi state change.", + true)); + sequencer->OnAllTasksCompletedCall( + {// Get a onetime callback with the initial state of Avahi. + AsyncEventSequencer::WrapCompletionTask( + base::Bind(&dbus::ObjectProxy::WaitForServiceToBeAvailable, + avahi_, + base::Bind(&AvahiMdnsClient::OnAvahiAvailable, + weak_ptr_factory_.GetWeakPtr()))), + }); +} + +void AvahiMdnsClient::CreateEntryGroup() { + ErrorPtr error; + + service_state_ = PENDING; + + auto resp = CallMethodAndBlock( + avahi_, dbus_constants::avahi::kServerInterface, + dbus_constants::avahi::kServerMethodEntryGroupNew, + &error); + + dbus::ObjectPath group_path; + if (!resp || !ExtractMethodCallResults(resp.get(), &error, &group_path)) { + service_state_ = ERROR; + LOG(ERROR) << "Error creating group."; + return; + } + entry_group_ = bus_->GetObjectProxy(dbus_constants::avahi::kServiceName, + group_path); + + // If we fail to connect to the the StateChange signal for this group, just + // report that the whole thing has failed. + auto on_connect_cb = [](const std::string& interface_name, + const std::string& signal_name, + bool success) { + if (!success) { + LOG(ERROR) << "Failed to connect to StateChange signal " + "from EntryGroup."; + return; + } + }; + + chromeos::dbus_utils::ConnectToSignal( + entry_group_, + dbus_constants::avahi::kGroupInterface, + dbus_constants::avahi::kGroupSignalStateChanged, + base::Bind(&AvahiMdnsClient::HandleGroupStateChanged, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(on_connect_cb)); + + if (!service_name_.empty()) + CreateService(); +} + +void AvahiMdnsClient::FreeEntryGroup() { + if (!entry_group_) { + LOG(ERROR) << "No group to free."; + return; + } + + ErrorPtr error; + auto resp = CallMethodAndBlock(entry_group_, + dbus_constants::avahi::kGroupInterface, + dbus_constants::avahi::kGroupMethodFree, + &error); + // Extract and log relevant errors. + bool success = resp && ExtractMethodCallResults(resp.get(), &error); + if (!success) { + LOG(ERROR) << "Error freeing service group"; + } + + // Ignore any signals we may have registered for from this proxy. + entry_group_->Detach(); + entry_group_ = nullptr; + + service_state_ = UNDEF; +} + +void AvahiMdnsClient::CreateService() { + ErrorPtr error; + + CHECK_EQ(service_type_, "privet"); + + VLOG(1) << "CreateService: name " << service_name_ << ", type: " << + service_type_ << ", port: " << port_; + + auto resp = CallMethodAndBlock( + entry_group_, + dbus_constants::avahi::kGroupInterface, + dbus_constants::avahi::kGroupMethodAddService, + &error, + int32_t{AVAHI_IF_UNSPEC}, + int32_t{AVAHI_PROTO_UNSPEC}, + uint32_t{0}, // No flags. + service_name_, + // For historical purposes, we convert the string "privet" into a proper + // mdns service type. Everyone else should just pass a proper service + // type. + service_type_.compare("privet") == 0 ? "_privet._tcp" : service_type_, + std::string{}, // domain. + std::string{}, // hostname + port_, + txt_); + if (!resp || !ExtractMethodCallResults(resp.get(), &error)) { + LOG(ERROR) << "Error creating service"; + service_state_ = ERROR; + return; + } + + resp = CallMethodAndBlock(entry_group_, + dbus_constants::avahi::kGroupInterface, + dbus_constants::avahi::kGroupMethodCommit, + &error); + if (!resp || !ExtractMethodCallResults(resp.get(), &error)) { + LOG(ERROR) << "Error committing service."; + service_state_ = ERROR; + return; + } + + service_state_ = READY; +} + +void AvahiMdnsClient::OnAvahiOwnerChanged(const std::string& old_owner, + const std::string& new_owner) { + if (new_owner.empty()) { + OnAvahiAvailable(false); + return; + } + OnAvahiAvailable(true); +} + +void AvahiMdnsClient::OnAvahiStateChanged(int32_t state, + const std::string& error) { + HandleAvahiStateChange(state); +} + +void AvahiMdnsClient::OnAvahiAvailable(bool avahi_is_on_dbus) { + VLOG(1) << "Avahi is " << (avahi_is_on_dbus ? "up." : "down."); + int32_t state = AVAHI_SERVER_FAILURE; + if (avahi_is_on_dbus) { + auto resp = CallMethodAndBlock( + avahi_, dbus_constants::avahi::kServerInterface, + dbus_constants::avahi::kServerMethodGetState, + nullptr); + if (!resp || !ExtractMethodCallResults(resp.get(), nullptr, &state)) { + LOG(WARNING) << "Failed to get avahi initial state. Relying on signal."; + } + } + VLOG(1) << "Initial Avahi state=" << state << "."; + HandleAvahiStateChange(state); } -void AvahiMdnsClient::StopPublishing(const std::string& service_name) { - // TODO(avakulenko) +void AvahiMdnsClient::HandleAvahiStateChange(int32_t state) { + switch (state) { + case AVAHI_SERVER_RUNNING: { + // All host RRs have been established. + VLOG(1) << "Avahi ready for action."; + if (avahi_state_ == READY) { + LOG(INFO) << "Ignoring redundant Avahi up event."; + return; + } + avahi_state_ = READY; + if (!service_name_.empty()) + CreateEntryGroup(); + } break; + case AVAHI_SERVER_INVALID: + // Invalid state (initial). + case AVAHI_SERVER_REGISTERING: + // Host RRs are being registered. + case AVAHI_SERVER_COLLISION: + // There is a collision with a host RR. All host RRs have been withdrawn, + // the user should set a new host name via avahi_server_set_host_name(). + case AVAHI_SERVER_FAILURE: + // Some fatal failure happened, the server is unable to proceed. + avahi_state_ = ERROR; + LOG(ERROR) << "Avahi changed to error state: " << state; + break; + default: + LOG(ERROR) << "Unknown Avahi server state change to " << state; + break; + } } -std::unique_ptr<MdnsClient> MdnsClient::CreateInstance() { - return std::unique_ptr<MdnsClient>{new AvahiMdnsClient}; +void AvahiMdnsClient::HandleGroupStateChanged( + int32_t state, + const std::string& error_message) { + if (state == AVAHI_ENTRY_GROUP_COLLISION || + state == AVAHI_ENTRY_GROUP_FAILURE) { + LOG(ERROR) << "Avahi service group error: " << state; + } } } // namespace buffet diff --git a/buffet/avahi_mdns_client.h b/buffet/avahi_mdns_client.h index a8fd86e..1028620 100644 --- a/buffet/avahi_mdns_client.h +++ b/buffet/avahi_mdns_client.h @@ -20,6 +20,8 @@ #include <map> #include <string> +#include <base/memory/weak_ptr.h> +#include <dbus/bus.h> #include <weave/mdns.h> #include "buffet/mdns_client.h" @@ -29,16 +31,61 @@ namespace buffet { // Publishes privet service on mDns using Avahi. class AvahiMdnsClient : public MdnsClient { public: - AvahiMdnsClient(); + explicit AvahiMdnsClient(const scoped_refptr<dbus::Bus> &bus); ~AvahiMdnsClient() override; // weave::Mdns implementation. - void PublishService(const std::string& service_name, + void PublishService(const std::string& service_type, uint16_t port, const std::map<std::string, std::string>& txt) override; - void StopPublishing(const std::string& service_name) override; + void StopPublishing(const std::string& service_type) override; private: + using TxtRecord = std::vector<std::vector<uint8_t>>; + + // States used to track progress of our asynchronous dbus operations. + typedef enum { + UNDEF, + PENDING, + READY, + ERROR + } AsyncState; + + scoped_refptr<dbus::Bus> bus_; + dbus::ObjectProxy* avahi_{nullptr}; + // The avahi interface we use to add/remove mdns services. + dbus::ObjectProxy* entry_group_{nullptr}; + + // State of our dbus connection to avahi. + AsyncState avahi_state_{UNDEF}; + // State of the group/service publish operation. + AsyncState service_state_{UNDEF}; + + std::string service_name_; + std::string service_type_; + uint16_t port_{0}; + TxtRecord txt_; + + // Must be last member to invalidate pointers before actual destruction. + base::WeakPtrFactory<AvahiMdnsClient> weak_ptr_factory_{this}; + + // Convert a {string:string} text record into something we can send over + // dbus. + TxtRecord GetTxtRecord(const std::map<std::string, std::string>& txt); + + void ConnectToAvahi(); + void CreateEntryGroup(); + void FreeEntryGroup(); + void CreateService(); + + void OnAvahiOwnerChanged(const std::string& old_owner, + const std::string& new_owner); + void OnAvahiStateChanged(int32_t state, + const std::string& error); + void OnAvahiAvailable(bool avahi_is_on_dbus); + void HandleAvahiStateChange(int32_t state); + void HandleGroupStateChanged(int32_t state, + const std::string& error_message); DISALLOW_COPY_AND_ASSIGN(AvahiMdnsClient); }; diff --git a/buffet/dbus_command_dispatcher.cc b/buffet/dbus_command_dispatcher.cc index 1af0983..03f4789 100644 --- a/buffet/dbus_command_dispatcher.cc +++ b/buffet/dbus_command_dispatcher.cc @@ -28,7 +28,8 @@ void DBusCommandDispacher::OnCommandAdded(weave::Command* command) { return; std::unique_ptr<DBusCommandProxy> proxy{new DBusCommandProxy( object_manager_.get(), object_manager_->GetBus(), command, - buffet::kCommandServicePathPrefix + std::to_string(++next_id_))}; + buffet::dbus_constants::kCommandServicePathPrefix + + std::to_string(++next_id_))}; proxy->RegisterAsync(AsyncEventSequencer::GetDefaultCompletionAction()); // DBusCommandProxy::DBusCommandProxy() subscribe itself to weave::Command // notifications. When weave::Command is being destroyed it sends diff --git a/buffet/dbus_command_proxy_unittest.cc b/buffet/dbus_command_proxy_unittest.cc index 1c7cc11..52bcf9f 100644 --- a/buffet/dbus_command_proxy_unittest.cc +++ b/buffet/dbus_command_proxy_unittest.cc @@ -82,7 +82,7 @@ class DBusCommandProxyTest : public ::testing::Test { .WillOnce(ReturnRefOfCopy<std::string>("{}")); // Set up a mock ExportedObject to be used with the DBus command proxy. - std::string cmd_path = buffet::kCommandServicePathPrefix; + std::string cmd_path = buffet::dbus_constants::kCommandServicePathPrefix; cmd_path += kTestCommandId; const dbus::ObjectPath kCmdObjPath(cmd_path); // Use a mock exported object for the exported object manager. diff --git a/buffet/dbus_constants.cc b/buffet/dbus_constants.cc index a1521fb..584bbff 100644 --- a/buffet/dbus_constants.cc +++ b/buffet/dbus_constants.cc @@ -6,8 +6,47 @@ namespace buffet { +namespace dbus_constants { + const char kServiceName[] = "com.android.Weave"; const char kRootServicePath[] = "/com/android/Weave"; const char kCommandServicePathPrefix[] = "/com/android/Weave/commands/"; +namespace avahi { + +const char kServiceName[] = "org.freedesktop.Avahi"; + +const char kServerInterface[] = "org.freedesktop.Avahi.Server"; +const char kServerPath[] = "/"; +const char kServerMethodEntryGroupNew[] = "EntryGroupNew"; +const char kServerMethodServiceBrowserNew[] = "ServiceBrowserNew"; +const char kServerMethodServiceResolverNew[] = "ServiceResolverNew"; +const char kServerMethodGetHostName[] = "GetHostName"; +const char kServerMethodGetState[] = "GetState"; +const char kServerSignalStateChanged[] = "StateChanged"; + +const char kGroupInterface[] = "org.freedesktop.Avahi.EntryGroup"; +const char kGroupMethodAddRecord[] = "AddRecord"; +const char kGroupMethodAddService[] = "AddService"; +const char kGroupMethodCommit[] = "Commit"; +const char kGroupMethodFree[] = "Free"; +const char kGroupMethodReset[]= "Reset"; +const char kGroupSignalStateChanged[] = "StateChanged"; + +const char kServiceBrowserInterface[] = "org.freedesktop.Avahi.ServiceBrowser"; +const char kServiceBrowserMethodFree[] = "Free"; +const char kServiceBrowserSignalItemNew[] = "ItemNew"; +const char kServiceBrowserSignalItemRemove[] = "ItemRemove"; +const char kServiceBrowserSignalFailure[] = "Failure"; + +const char kServiceResolverInterface[] = + "org.freedesktop.Avahi.ServiceResolver"; +const char kServiceResolverMethodFree[] = "Free"; +const char kServiceResolverSignalFound[] = "Found"; +const char kServiceResolverSignalFailure[] = "Failure"; + +} // namespace avahi + +} // namespace dbus constants + } // namespace buffet diff --git a/buffet/dbus_constants.h b/buffet/dbus_constants.h index 0bb72dc..e994c06 100644 --- a/buffet/dbus_constants.h +++ b/buffet/dbus_constants.h @@ -7,6 +7,8 @@ namespace buffet { +namespace dbus_constants { + // The service name claimed by the Buffet daemon. extern const char kServiceName[]; @@ -16,6 +18,42 @@ extern const char kRootServicePath[]; // D-Bus object path prefix for Command objects. extern const char kCommandServicePathPrefix[]; +namespace avahi { + +extern const char kServiceName[]; + +extern const char kServerInterface[]; +extern const char kServerPath[]; +extern const char kServerMethodEntryGroupNew[]; +extern const char kServerMethodServiceBrowserNew[]; +extern const char kServerMethodServiceResolverNew[]; +extern const char kServerMethodGetHostName[]; +extern const char kServerMethodGetState[]; +extern const char kServerSignalStateChanged[]; + +extern const char kGroupInterface[]; +extern const char kGroupMethodAddRecord[]; +extern const char kGroupMethodAddService[]; +extern const char kGroupMethodCommit[]; +extern const char kGroupMethodFree[]; +extern const char kGroupMethodReset[]; +extern const char kGroupSignalStateChanged[]; + +extern const char kServiceBrowserInterface[]; +extern const char kServiceBrowserMethodFree[]; +extern const char kServiceBrowserSignalItemNew[]; +extern const char kServiceBrowserSignalItemRemove[]; +extern const char kServiceBrowserSignalFailure[]; + +extern const char kServiceResolverInterface[]; +extern const char kServiceResolverMethodFree[]; +extern const char kServiceResolverSignalFound[]; +extern const char kServiceResolverSignalFailure[]; + +} // namespace avahi + +} // namespace dbus_constants + } // namespace buffet #endif // BUFFET_DBUS_CONSTANTS_H_ diff --git a/buffet/main.cc b/buffet/main.cc index 203492b..d2ca7e6 100644 --- a/buffet/main.cc +++ b/buffet/main.cc @@ -18,8 +18,8 @@ using chromeos::dbus_utils::AsyncEventSequencer; using chromeos::DBusServiceDaemon; -using buffet::kServiceName; -using buffet::kRootServicePath; +using buffet::dbus_constants::kServiceName; +using buffet::dbus_constants::kRootServicePath; namespace buffet { diff --git a/buffet/manager.cc b/buffet/manager.cc index f8f1f70..f5874f4 100644 --- a/buffet/manager.cc +++ b/buffet/manager.cc @@ -77,7 +77,7 @@ void Manager::Start(const weave::Device::Options& options, network_client_ = NetworkClient::CreateInstance(device_whitelist); #ifdef BUFFET_USE_WIFI_BOOTSTRAPPING if (!options.disable_privet) { - mdns_client_ = MdnsClient::CreateInstance(); + mdns_client_ = MdnsClient::CreateInstance(dbus_object_.GetBus()); web_serv_client_.reset(new WebServClient{dbus_object_.GetBus(), sequencer}); } #endif // BUFFET_USE_WIFI_BOOTSTRAPPING diff --git a/buffet/mdns_client.h b/buffet/mdns_client.h index f486d05..ccfb920 100644 --- a/buffet/mdns_client.h +++ b/buffet/mdns_client.h @@ -22,6 +22,8 @@ #include <string> #include <base/guid.h> +#include <base/memory/ref_counted.h> +#include <dbus/bus.h> #include <weave/mdns.h> namespace buffet { @@ -33,13 +35,15 @@ class MdnsClient : public weave::Mdns { ~MdnsClient() override = default; // weave::Mdns implementation. - void PublishService(const std::string& service_name, + void PublishService(const std::string& service_type, uint16_t port, - const std::map<std::string, std::string>& txt) override {} - void StopPublishing(const std::string& service_name) override {} + const std::map<std::string, std::string>& txt) override { + }; + void StopPublishing(const std::string& service_type) override {} std::string GetId() const override { return device_id_; } - static std::unique_ptr<MdnsClient> CreateInstance(); + static std::unique_ptr<MdnsClient> CreateInstance( + const scoped_refptr<dbus::Bus> &bus); protected: // Cached value of the device ID that we got from peerd. diff --git a/buffet/webserv_client.cc b/buffet/webserv_client.cc index 2ae5910..37c9716 100644 --- a/buffet/webserv_client.cc +++ b/buffet/webserv_client.cc @@ -84,7 +84,7 @@ WebServClient::WebServClient( base::Bind(&WebServClient::OnProtocolHandlerDisconnected, weak_ptr_factory_.GetWeakPtr())); - web_server_->Connect(bus, buffet::kServiceName, + web_server_->Connect(bus, buffet::dbus_constants::kServiceName, sequencer->GetHandler("Server::Connect failed.", true), base::Bind(&base::DoNothing), base::Bind(&base::DoNothing)); |