Allow VirtualADB control VHCI state.

This change makes VirtualUSB more robust by detecting cases where device
is gone. The effect is that ADB will re-attach to device after it comes
back online, instead of reporting it as 'offline'.

Change-Id: Ib37a5273043ba42c47c8bf4a1f52f15152d05af7
(cherry picked from commit f4f38a878415365bf4d554641e880cd37935bb68)
diff --git a/ivserver/main.cc b/ivserver/main.cc
index 1a6dd3d..75ce28e 100644
--- a/ivserver/main.cc
+++ b/ivserver/main.cc
@@ -65,7 +65,7 @@
   google::InstallFailureSignalHandler();
   google::ParseCommandLineFlags(&argc, &argv, true);
 
-  vadb::VirtualADBServer adb(FLAGS_usbsocket);
+  vadb::VirtualADBServer adb(FLAGS_usbsocket, FLAGS_usbipsocket);
   CHECK(adb.Init());
   vadb::usbip::Server usbip_server(FLAGS_usbipsocket, adb.Pool());
   CHECK(usbip_server.Init()) << "Could not start usb/ip server";
diff --git a/vadb/main.cpp b/vadb/main.cpp
index 2aa5d8f..2e4c2cb 100644
--- a/vadb/main.cpp
+++ b/vadb/main.cpp
@@ -30,7 +30,7 @@
   google::InitGoogleLogging(argv[0]);
   google::ParseCommandLineFlags(&argc, &argv, true);
 
-  vadb::VirtualADBServer adb(FLAGS_socket);
+  vadb::VirtualADBServer adb(FLAGS_socket, FLAGS_usbip_socket_name);
   CHECK(adb.Init());
 
   vadb::usbip::Server s(FLAGS_usbip_socket_name, adb.Pool());
diff --git a/vadb/usbip/server.cpp b/vadb/usbip/server.cpp
index 9f53a95..ceb70e8 100644
--- a/vadb/usbip/server.cpp
+++ b/vadb/usbip/server.cpp
@@ -25,9 +25,9 @@
 namespace vadb {
 namespace usbip {
 Server::Server(const std::string& name, const DevicePool& devices)
-    : name_{name}, device_pool_{devices}, vhci_{name} {}
+    : name_{name}, device_pool_{devices} {}
 
-bool Server::Init() { return CreateServerSocket() && vhci_.Init(); }
+bool Server::Init() { return CreateServerSocket(); }
 
 // Open new listening server socket.
 // Returns false, if listening socket could not be created.
@@ -54,7 +54,6 @@
     if (!iter->AfterSelect(fd_read)) {
       // If client conversation failed, hang up.
       iter = clients_.erase(iter);
-      vhci_.TriggerAttach();
       continue;
     }
     ++iter;
diff --git a/vadb/usbip/server.h b/vadb/usbip/server.h
index 3602732..06b5e30 100644
--- a/vadb/usbip/server.h
+++ b/vadb/usbip/server.h
@@ -21,7 +21,6 @@
 #include "common/libs/fs/shared_fd.h"
 #include "host/vadb/usbip/client.h"
 #include "host/vadb/usbip/device_pool.h"
-#include "host/vadb/usbip/vhci_instrument.h"
 
 namespace vadb {
 namespace usbip {
@@ -57,7 +56,6 @@
   std::list<Client> clients_;
 
   const DevicePool& device_pool_;
-  VHCIInstrument vhci_;
 
   Server(const Server&) = delete;
   Server& operator=(const Server&) = delete;
diff --git a/vadb/usbip/vhci_instrument.cpp b/vadb/usbip/vhci_instrument.cpp
index e1e3725..a3744eb 100644
--- a/vadb/usbip/vhci_instrument.cpp
+++ b/vadb/usbip/vhci_instrument.cpp
@@ -18,9 +18,9 @@
 #include <netdb.h>
 #include <sys/socket.h>
 
+#include <glog/logging.h>
 #include <fstream>
 #include <sstream>
-#include <glog/logging.h>
 #include "common/libs/fs/shared_select.h"
 
 #include "common/libs/fs/shared_fd.h"
@@ -42,6 +42,13 @@
 constexpr char kVHCISubsystem[] = "platform";
 constexpr char kVHCIDevType[] = "vhci_hcd";
 
+// Control messages.
+// Attach tells thread to attach remote device.
+// Detach tells thread to detach remote device.
+using ControlMsgType = uint8_t;
+constexpr ControlMsgType kControlAttach = 'A';
+constexpr ControlMsgType kControlDetach = 'D';
+
 // Port status values deducted from /sys/devices/platform/vhci_hcd/status
 enum {
   // kVHCIPortFree indicates the port is not currently in use.
@@ -55,15 +62,18 @@
                    [](udev_device* device) { udev_device_unref(device); }),
       name_(name) {}
 
+VHCIInstrument::~VHCIInstrument() {
+  if (sys_fd_ > 0) close(sys_fd_);
+}
+
 bool VHCIInstrument::Init() {
-  wake_event_ = avd::SharedFD::Event(0, 0);
+  avd::SharedFD::Pipe(&control_read_end_, &control_write_end_);
 
   udev_.reset(udev_new());
   CHECK(udev_) << "Could not create libudev context.";
 
-  vhci_device_.reset(udev_device_new_from_subsystem_sysname(udev_.get(),
-                                                            kVHCISubsystem,
-                                                            kVHCIDevType));
+  vhci_device_.reset(udev_device_new_from_subsystem_sysname(
+      udev_.get(), kVHCISubsystem, kVHCIDevType));
   if (!vhci_device_) {
     LOG(ERROR) << "VHCI not available. Is the driver loaded?";
     LOG(ERROR) << "Try: sudo modprobe vhci_hcd";
@@ -80,8 +90,7 @@
     return false;
   }
 
-  attach_thread_.reset(new std::thread([this]() {
-    AttachThread(); }));
+  attach_thread_.reset(new std::thread([this]() { AttachThread(); }));
   return true;
 }
 
@@ -112,8 +121,11 @@
 }
 
 void VHCIInstrument::TriggerAttach() {
-  uint64_t count = 1;
-  wake_event_->Write(&count, sizeof(count));
+  control_write_end_->Write(&kControlAttach, sizeof(kControlAttach));
+}
+
+void VHCIInstrument::TriggerDetach() {
+  control_write_end_->Write(&kControlDetach, sizeof(kControlDetach));
 }
 
 void VHCIInstrument::AttachThread() {
@@ -123,42 +135,73 @@
   timeval period = {1, 0};
   // Trigger attach upon start.
   bool want_attach = true;
+  // Indicate running operation on start.
+  bool is_pending = true;
 
   while (true) {
     rset.Zero();
-    rset.Set(wake_event_);
+    rset.Set(control_read_end_);
     // 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;
+                         (is_pending ? &period : nullptr))) {
+      ControlMsgType request_type;
+      control_read_end_->Read(&request_type, sizeof(request_type));
+      is_pending = true;
+      want_attach = request_type == kControlAttach;
+      LOG(INFO) << (want_attach ? "Attach" : "Detach") << " triggered.";
     }
 
     // Make an attempt to re-attach. If successful, clear pending attach flag.
-    if (Attach()) {
-      want_attach = false;
-    } else {
-      LOG(WARNING) << "Attach failed.";
-      sleep(1);
+    if (is_pending) {
+      if (want_attach && Attach()) {
+        is_pending = false;
+      } else if (!want_attach && Detach()) {
+        is_pending = false;
+      } else {
+        LOG(INFO) << (want_attach ? "Attach" : "Detach") << " unsuccessful. "
+                  << "Will re-try.";
+        sleep(1);
+      }
     }
   }
 }
 
+bool VHCIInstrument::Detach() {
+  // sys_fd_ is the descriptor we supplied to the system to allow it to talk to
+  // (remote) USB device. By closing this descriptor we effectively force close
+  // connection to remote USB device.
+  if (sys_fd_ > 0) {
+    close(sys_fd_);
+    sys_fd_ = -1;
+  }
+
+  std::stringstream result;
+  result << port_;
+  std::ofstream detach(syspath_ + "/detach");
+
+  if (!detach.is_open()) {
+    LOG(WARNING) << "Could not open VHCI detach file.";
+    return false;
+  }
+  detach << result.str();
+  return detach.rdstate() == std::ios_base::goodbit;
+}
+
 bool VHCIInstrument::Attach() {
   avd::SharedFD socket =
       avd::SharedFD::SocketLocalClient(name_.c_str(), true, SOCK_STREAM);
   if (!socket->IsOpen()) return false;
-  int dup_fd = socket->UNMANAGED_Dup();
+  sys_fd_ = socket->UNMANAGED_Dup();
 
   std::stringstream result;
-  result << port_ << ' ' << dup_fd << ' ' << kDefaultDeviceID << ' ' <<
-         kDefaultDeviceSpeed;
+  result << port_ << ' ' << sys_fd_ << ' ' << kDefaultDeviceID << ' '
+         << kDefaultDeviceSpeed;
   std::ofstream attach(syspath_ + "/attach");
 
   if (!attach.is_open()) {
     LOG(WARNING) << "Could not open VHCI attach file.";
+    close(sys_fd_);
+    sys_fd_ = -1;
     return false;
   }
   attach << result.str();
@@ -168,7 +211,12 @@
   // Kernel 4.10 is having problems communicating with USB/IP server if the
   // socket is closed after it's passed to kernel. It is a clear indication that
   // the kernel requires the socket to be kept open.
-  return attach.rdstate() == std::ios_base::goodbit;
+  bool success = attach.rdstate() == std::ios_base::goodbit;
+  if (!success) {
+    close(sys_fd_);
+    sys_fd_ = -1;
+  }
+  return success;
 }
 
 }  // namespace usbip
diff --git a/vadb/usbip/vhci_instrument.h b/vadb/usbip/vhci_instrument.h
index 107e140..f669fc0 100644
--- a/vadb/usbip/vhci_instrument.h
+++ b/vadb/usbip/vhci_instrument.h
@@ -29,7 +29,7 @@
 class VHCIInstrument {
  public:
   VHCIInstrument(const std::string& name);
-  virtual ~VHCIInstrument() = default;
+  virtual ~VHCIInstrument();
 
   // Init opens vhci-hcd driver and allocates port to which remote USB device
   // will be attached.
@@ -41,11 +41,18 @@
   // device.
   void TriggerAttach();
 
+  // TriggerDetach tells underlying thread to disconnect remote USB device.
+  void TriggerDetach();
+
  private:
   // Attach makes an attempt to configure VHCI to enable virtual USB device.
   // Returns true, if configuration attempt was successful.
   bool Attach();
 
+  // Detach disconnects virtual USB device.
+  // Returns true, if attempt was successful.
+  bool Detach();
+
   // AttachThread is a background thread that responds to configuration
   // requests.
   void AttachThread();
@@ -57,8 +64,12 @@
   std::string name_;
   std::unique_ptr<std::thread> attach_thread_;
   std::string syspath_;
-  avd::SharedFD wake_event_;
+  avd::SharedFD control_write_end_;
+  avd::SharedFD control_read_end_;
   int port_;
+  // Raw FD used by system to access remote USB device.
+  // This FD must remain valid until no longer needed.
+  int sys_fd_ = -1;
 
   VHCIInstrument(const VHCIInstrument& other) = delete;
   VHCIInstrument& operator=(const VHCIInstrument& other) = delete;
diff --git a/vadb/virtual_adb_client.cpp b/vadb/virtual_adb_client.cpp
index 440db10..b165287 100644
--- a/vadb/virtual_adb_client.cpp
+++ b/vadb/virtual_adb_client.cpp
@@ -28,8 +28,10 @@
 constexpr int kHeartbeatTimeoutSeconds = 3;
 }  // namespace
 
-VirtualADBClient::VirtualADBClient(usbip::DevicePool* pool, avd::SharedFD fd)
-    : pool_(pool), fd_(fd) {
+VirtualADBClient::VirtualADBClient(usbip::DevicePool* pool, avd::SharedFD fd,
+                                   const std::string& usbip_socket_name)
+    : pool_{pool}, fd_{fd}, vhci_{usbip_socket_name} {
+  CHECK(vhci_.Init());
   timer_ = avd::SharedFD::TimerFD(CLOCK_MONOTONIC, 0);
   SendHeartbeat();
 }
@@ -119,7 +121,7 @@
 
 bool VirtualADBClient::SendHeartbeat() {
   VLOG(1) << "Sending heartbeat...";
-  struct itimerspec spec{};
+  struct itimerspec spec {};
   spec.it_value.tv_sec = kHeartbeatTimeoutSeconds;
   timer_->TimerSet(0, &spec, nullptr);
 
@@ -134,7 +136,9 @@
   if (is_ready && !is_remote_server_ready_) {
     LOG(INFO) << "Remote server is now ready.";
     PopulateRemoteDevices();
+    vhci_.TriggerAttach();
   } else if (is_remote_server_ready_ && !is_ready) {
+    vhci_.TriggerDetach();
     LOG(WARNING) << "Remote server connection lost.";
     // It makes perfect sense to cancel all outstanding USB requests, as device
     // is not going to answer any of these anyway.
diff --git a/vadb/virtual_adb_client.h b/vadb/virtual_adb_client.h
index 2ade53e..740df21 100644
--- a/vadb/virtual_adb_client.h
+++ b/vadb/virtual_adb_client.h
@@ -24,6 +24,7 @@
 #include "host/vadb/usbip/device.h"
 #include "host/vadb/usbip/device_pool.h"
 #include "host/vadb/usbip/messages.h"
+#include "host/vadb/usbip/vhci_instrument.h"
 
 namespace vadb {
 // VirtualADBClient is a companion class for USBForwarder, running on
@@ -34,7 +35,8 @@
 // remote USB devices possible with help of USB/IP protocol.
 class VirtualADBClient {
  public:
-  VirtualADBClient(usbip::DevicePool* pool, avd::SharedFD fd);
+  VirtualADBClient(usbip::DevicePool* pool, avd::SharedFD fd,
+                   const std::string& usbip_socket_name);
 
   virtual ~VirtualADBClient() = default;
 
@@ -90,6 +92,7 @@
   usbip::DevicePool* pool_;
   avd::SharedFD fd_;
   avd::SharedFD timer_;
+  usbip::VHCIInstrument vhci_;
   bool is_remote_server_ready_ = false;
 
   uint32_t tag_ = 0;
diff --git a/vadb/virtual_adb_server.cpp b/vadb/virtual_adb_server.cpp
index 8d331e7..bf39bb3 100644
--- a/vadb/virtual_adb_server.cpp
+++ b/vadb/virtual_adb_server.cpp
@@ -58,7 +58,7 @@
     return;
   }
 
-  clients_.emplace_back(&pool_, client);
+  clients_.emplace_back(&pool_, client, usbip_name_);
 }
 
 }  // namespace vadb
diff --git a/vadb/virtual_adb_server.h b/vadb/virtual_adb_server.h
index b1f82fb..91f6611 100644
--- a/vadb/virtual_adb_server.h
+++ b/vadb/virtual_adb_server.h
@@ -26,7 +26,10 @@
 // VirtualADBServer manages incoming VirtualUSB/ADB connections from QEmu.
 class VirtualADBServer {
  public:
-  VirtualADBServer(const std::string& name) : name_(name) {}
+  VirtualADBServer(const std::string& usb_socket_name,
+                   const std::string& usbip_socket_name)
+      : name_(usb_socket_name), usbip_name_(usbip_socket_name) {}
+
   ~VirtualADBServer() = default;
 
   // Initialize this instance of Server.
@@ -49,6 +52,7 @@
 
   usbip::DevicePool pool_;
   std::string name_;
+  std::string usbip_name_;
   avd::SharedFD server_;
   std::list<VirtualADBClient> clients_;