VirtualADB: allow usbip to reconnect

Change-Id: Iee81a81a3844f0b603d9d4cfc168007403ba9b6c
(cherry picked from commit 72270a9e2332bcfdec49e99cbd211ca6922541da)
diff --git a/vadb/BUILD b/vadb/BUILD
index 05d9b58..614630a 100644
--- a/vadb/BUILD
+++ b/vadb/BUILD
@@ -11,8 +11,6 @@
         "usb_cmd_data_transfer.h",
         "usb_cmd_device_list.cpp",
         "usb_cmd_device_list.h",
-        "vhci_instrument.cpp",
-        "vhci_instrument.h",
         "virtual_adb.cpp",
         "virtual_adb.h",
     ],
diff --git a/vadb/main.cpp b/vadb/main.cpp
index be81f73..c981f17 100644
--- a/vadb/main.cpp
+++ b/vadb/main.cpp
@@ -20,7 +20,6 @@
 
 #include "common/libs/fs/shared_fd.h"
 #include "host/vadb/usbip/server.h"
-#include "host/vadb/vhci_instrument.h"
 #include "host/vadb/virtual_adb.h"
 
 DEFINE_string(socket, "", "Socket to use to talk to USBForwarder.");
@@ -33,9 +32,6 @@
   vadb::VirtualADB adb(FLAGS_socket);
   CHECK(adb.Init());
 
-  vadb::VHCIInstrument vhci(FLAGS_usbip_socket_name);
-  CHECK(vhci.Init());
-
   vadb::usbip::Server s(FLAGS_usbip_socket_name, adb.Pool());
   CHECK(s.Init()) << "Could not start server";
   s.Serve();
diff --git a/vadb/usbip/BUILD b/vadb/usbip/BUILD
index 5f0bc66..dcbd6fc 100644
--- a/vadb/usbip/BUILD
+++ b/vadb/usbip/BUILD
@@ -10,6 +10,8 @@
         "messages.h",
         "server.cpp",
         "server.h",
+        "vhci_instrument.cpp",
+        "vhci_instrument.h",
     ],
     hdrs = [
         "client.h",
diff --git a/vadb/usbip/server.cpp b/vadb/usbip/server.cpp
index 3c9cd00..c55eaf8 100644
--- a/vadb/usbip/server.cpp
+++ b/vadb/usbip/server.cpp
@@ -32,9 +32,9 @@
 }  // namespace
 
 Server::Server(const std::string &name, const DevicePool &devices)
-    : name_(name), device_pool_(devices) {}
+    : name_{name}, device_pool_{devices}, vhci_{name} {}
 
-bool Server::Init() { return CreateServerSocket(); }
+bool Server::Init() { return CreateServerSocket() && vhci_.Init(); }
 
 // Open new listening server socket.
 // Returns false, if listening socket could not be created.
@@ -68,6 +68,7 @@
         // If client conversation failed, hang up.
         if (!iter->HandleIncomingMessage()) {
           iter = clients_.erase(iter);
+          vhci_.TriggerAttach();
           continue;
         }
       }
diff --git a/vadb/usbip/server.h b/vadb/usbip/server.h
index 7bcb076..b3acafd 100644
--- a/vadb/usbip/server.h
+++ b/vadb/usbip/server.h
@@ -21,6 +21,7 @@
 #include "common/libs/fs/shared_fd.h"
 #include "host/vadb/usbip/device_pool.h"
 #include "host/vadb/usbip/client.h"
+#include "host/vadb/usbip/vhci_instrument.h"
 
 namespace vadb {
 namespace usbip {
@@ -50,7 +51,9 @@
   std::string name_;
   avd::SharedFD server_;
   std::list<Client> clients_;
+
   const DevicePool& device_pool_;
+  VHCIInstrument vhci_;
 
   Server(const Server&) = delete;
   Server& operator=(const Server&) = delete;
diff --git a/vadb/vhci_instrument.cpp b/vadb/usbip/vhci_instrument.cpp
similarity index 82%
rename from vadb/vhci_instrument.cpp
rename to vadb/usbip/vhci_instrument.cpp
index 1e89fc3..9a2b8a2 100644
--- a/vadb/vhci_instrument.cpp
+++ b/vadb/usbip/vhci_instrument.cpp
@@ -21,11 +21,13 @@
 #include <fstream>
 #include <sstream>
 #include <glog/logging.h>
+#include "common/libs/fs/shared_select.h"
 
 #include "common/libs/fs/shared_fd.h"
 #include "host/vadb/vhci_instrument.h"
 
 namespace vadb {
+namespace usbip {
 namespace {
 // Device ID is specified as a concatenated pair of BUS and DEVICE id.
 // Since we only export one device and our server doesn't care much about
@@ -54,6 +56,8 @@
       name_(name) {}
 
 bool VHCIInstrument::Init() {
+  wake_event_ = avd::SharedFD::Event(0, 0);
+
   udev_.reset(udev_new());
   CHECK(udev_) << "Could not create libudev context.";
 
@@ -107,12 +111,34 @@
   return false;
 }
 
+void VHCIInstrument::TriggerAttach() {
+  uint64_t count = 1;
+  wake_event_->Write(&count, sizeof(count));
+}
+
 void VHCIInstrument::AttachThread() {
+  avd::SharedFDSet rset;
+  // If we're attempting connection, make sure to re-try every second until
+  // we're successful.
+  timeval period = {1, 0};
+  // Trigger attach upon start.
+  bool want_attach = true;
+
   while (true) {
-    sleep(3);
+    rset.Zero();
+    rset.Set(wake_event_);
+    // Wait until poked.
+    if (0 != avd::Select(&rset, nullptr, nullptr,
+                         (want_attach ? &period : nullptr))) {
+      uint64_t ignore;
+      wake_event_->Read(&ignore, sizeof(ignore));
+      LOG(INFO) << "Attach triggered.";
+      want_attach = true;
+    }
+
+    // Make an attempt to re-attach. If successful, clear pending attach flag.
     if (Attach()) {
-      LOG(INFO) << "Attach successful.";
-      break;
+      want_attach = false;
     }
   }
 }
@@ -140,4 +166,5 @@
   return attach.rdstate() == std::ios_base::goodbit;
 }
 
+}  // namespace usbip
 }  // namespace vadb
diff --git a/vadb/vhci_instrument.h b/vadb/usbip/vhci_instrument.h
similarity index 64%
rename from vadb/vhci_instrument.h
rename to vadb/usbip/vhci_instrument.h
index ea2a6da..107e140 100644
--- a/vadb/vhci_instrument.h
+++ b/vadb/usbip/vhci_instrument.h
@@ -21,15 +21,33 @@
 
 #include <libudev.h>
 
+#include "common/libs/fs/shared_fd.h"
+
 namespace vadb {
+namespace usbip {
+// VHCIInstrument class configures VHCI-HCD on local kernel.
 class VHCIInstrument {
  public:
   VHCIInstrument(const std::string& name);
   virtual ~VHCIInstrument() = default;
 
+  // Init opens vhci-hcd driver and allocates port to which remote USB device
+  // will be attached.
+  // Returns false, if vhci-hcd driver could not be opened, or if no free port
+  // was found.
   bool Init();
 
+  // TriggerAttach tells underlying thread to make attempt to re-attach USB
+  // device.
+  void TriggerAttach();
+
+ private:
+  // Attach makes an attempt to configure VHCI to enable virtual USB device.
+  // Returns true, if configuration attempt was successful.
   bool Attach();
+
+  // AttachThread is a background thread that responds to configuration
+  // requests.
   void AttachThread();
   bool FindFreePort();
 
@@ -39,9 +57,11 @@
   std::string name_;
   std::unique_ptr<std::thread> attach_thread_;
   std::string syspath_;
+  avd::SharedFD wake_event_;
   int port_;
 
   VHCIInstrument(const VHCIInstrument& other) = delete;
   VHCIInstrument& operator=(const VHCIInstrument& other) = delete;
 };
+}  // namespace usbip
 }  // namespace vadb
diff --git a/vadb/virtual_adb.h b/vadb/virtual_adb.h
index edbee8d..76a6f31 100644
--- a/vadb/virtual_adb.h
+++ b/vadb/virtual_adb.h
@@ -25,7 +25,6 @@
 #include "host/vadb/usbip/device.h"
 #include "host/vadb/usbip/device_pool.h"
 #include "host/vadb/usbip/messages.h"
-#include "host/vadb/vhci_instrument.h"
 
 namespace vadb {
 // VirtualADB is a companion class for USBForwarder, running on Cuttlefish.