Fix avahi host name resolution scenarios

When host name collision is detected, avahi picks a new host name,
however it fails to update any published services with the new name
which leads to wrong IP address resolution for the service.

As a work-around, when we detect an avahi-client restart, we just
re-publish our service, so it picks up the new host name.

BUG: 26237307
Change-Id: I980735890c62957e1bc433a07c319eff646c358c
diff --git a/buffet/avahi_mdns_client.cc b/buffet/avahi_mdns_client.cc
index 852f59a..c60b727 100644
--- a/buffet/avahi_mdns_client.cc
+++ b/buffet/avahi_mdns_client.cc
@@ -54,8 +54,9 @@
 
   int ret = 0;
 
-  client_.reset(avahi_client_new(avahi_threaded_poll_get(thread_pool_.get()),
-                                 {}, nullptr, this, &ret));
+  client_.reset(avahi_client_new(
+      avahi_threaded_poll_get(thread_pool_.get()), {},
+      &AvahiMdnsClient::OnAvahiClientStateUpdate, this, &ret));
   CHECK(client_) << avahi_strerror(ret);
 
   avahi_threaded_poll_start(thread_pool_.get());
@@ -71,14 +72,17 @@
     avahi_threaded_poll_stop(thread_pool_.get());
 }
 
-// TODO(rginda): Report errors back to the caller.
-// TODO(rginda): Support publishing more than one service.
 void AvahiMdnsClient::PublishService(const std::string& service_type,
                                      uint16_t port,
                                      const std::vector<std::string>& txt) {
   CHECK(group_);
   CHECK_EQ("_privet._tcp", service_type);
 
+  if (prev_port_ == port && prev_service_type_ == service_type &&
+      txt_records_ == txt) {
+    return;
+  }
+
   // Create txt record.
   std::unique_ptr<AvahiStringList, decltype(&avahi_string_list_free)> txt_list{
       nullptr, &avahi_string_list_free};
@@ -95,8 +99,9 @@
   }
 
   int ret = 0;
+  txt_records_ = txt;
 
-  if (prev_port_ == port && prev_type_ == service_type) {
+  if (prev_port_ == port && prev_service_type_ == service_type) {
     ret = avahi_entry_group_update_service_txt_strlst(
         group_.get(), AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, {},
         service_name_.c_str(), service_type.c_str(), nullptr, txt_list.get());
@@ -104,7 +109,7 @@
     CHECK_GE(ret, 0) << avahi_strerror(ret);
   } else {
     prev_port_ = port;
-    prev_type_ = service_type;
+    prev_service_type_ = service_type;
 
     avahi_entry_group_reset(group_.get());
     CHECK(avahi_entry_group_is_empty(group_.get()));
@@ -123,6 +128,33 @@
 void AvahiMdnsClient::StopPublishing(const std::string& service_type) {
   CHECK(group_);
   avahi_entry_group_reset(group_.get());
+  prev_service_type_.clear();
+  prev_port_ = 0;
+  txt_records_.clear();
+}
+
+void AvahiMdnsClient::OnAvahiClientStateUpdate(AvahiClient* s,
+                                               AvahiClientState state,
+                                               void* userdata) {
+  // Avahi service has been re-initialized (probably due to host name conflict),
+  // so we need to republish the service if it has been previously published.
+  if (state == AVAHI_CLIENT_S_RUNNING) {
+    AvahiMdnsClient* self = static_cast<AvahiMdnsClient*>(userdata);
+    self->RepublishService();
+  }
+}
+
+void AvahiMdnsClient::RepublishService() {
+  // If we don't have a service to publish, there is nothing else to do here.
+  if (prev_service_type_.empty())
+    return;
+
+  LOG(INFO) << "Republishing mDNS service";
+  std::string service_type = std::move(prev_service_type_);
+  uint16_t port = prev_port_;
+  std::vector<std::string> txt = std::move(txt_records_);
+  StopPublishing(service_type);
+  PublishService(service_type, port, txt);
 }
 
 }  // namespace buffet
diff --git a/buffet/avahi_mdns_client.h b/buffet/avahi_mdns_client.h
index 405180d..6bc896e 100644
--- a/buffet/avahi_mdns_client.h
+++ b/buffet/avahi_mdns_client.h
@@ -40,9 +40,15 @@
   void StopPublishing(const std::string& service_type) override;
 
  private:
+  static void OnAvahiClientStateUpdate(AvahiClient* s,
+                                       AvahiClientState state,
+                                       void* userdata);
+  void RepublishService();
+
   uint16_t prev_port_{0};
-  std::string prev_type_;
+  std::string prev_service_type_;
   std::string service_name_;
+  std::vector<std::string> txt_records_;
   std::unique_ptr<AvahiThreadedPoll, decltype(&avahi_threaded_poll_free)>
       thread_pool_{nullptr, &avahi_threaded_poll_free};
   std::unique_ptr< ::AvahiClient, decltype(&avahi_client_free)> client_{