Maintains socket forward connections.

host-side socket_forward_proxy will keep a thread running that monitors
the adb connection to localhost:6520 + offset. If the connection is lost
due to adb daemon dying or the device not showing up in the adb devices
response, it will reconnect.

Bug: 79117228
Test: boot with host-adbd running and see the device appear in adb
devices. adb kill-server and wait for device to reappear in adb devices
Change-Id: Ia52fa04f19a2f2ca6b8496ff2f3e8f8f328cbe4f
(cherry picked from commit 6ef8a38e4b0d46e36b49a33b282b6639e0cf4d9d)
diff --git a/common/frontend/socket_forward_proxy/Android.bp b/common/frontend/socket_forward_proxy/Android.bp
index caad62b..4def7bf 100644
--- a/common/frontend/socket_forward_proxy/Android.bp
+++ b/common/frontend/socket_forward_proxy/Android.bp
@@ -36,6 +36,7 @@
         host: {
             static_libs: [
                 "libcuttlefish_host_config",
+                "libadb_connection_maintainer",
             ],
         },
     },
diff --git a/common/frontend/socket_forward_proxy/main.cpp b/common/frontend/socket_forward_proxy/main.cpp
index ed4196e..a04c601 100644
--- a/common/frontend/socket_forward_proxy/main.cpp
+++ b/common/frontend/socket_forward_proxy/main.cpp
@@ -34,6 +34,7 @@
 
 #ifdef CUTTLEFISH_HOST
 #include "host/libs/config/host_config.h"
+#include "host/libs/adb_connection_maintainer/adb_connection_maintainer.h"
 #endif
 
 using vsoc::socket_forward::Packet;
@@ -148,13 +149,11 @@
                              SocketForwardRegionView::Receiver>
                        conn,
                    cvd::SharedFD socket) {
-  // TODO create the SocketSender/Receivers in their respective threads?
-  std::thread threads[] = {
-      std::thread(SocketToShm, SocketReceiver{socket}, std::move(conn.first)),
-      std::thread(ShmToSocket, SocketSender{socket}, std::move(conn.second))};
-  for (auto&& t : threads) {
-    t.detach();
-  }
+  // TODO create the SocketSender/Receiver in their respective threads?
+  std::thread(
+      SocketToShm, SocketReceiver{socket}, std::move(conn.first)).detach();
+  std::thread(
+      ShmToSocket, SocketSender{socket}, std::move(conn.second)).detach();
 }
 
 #ifdef CUTTLEFISH_HOST
@@ -163,6 +162,10 @@
   int host_port;
 };
 
+void LaunchConnectionMaintainer(int port) {
+  std::thread(cvd::EstablishAndMaintainConnection, port).detach();
+}
+
 
 [[noreturn]] void host_impl(SocketForwardRegionView* shm,
                             std::vector<PortPair> ports, std::size_t index) {
@@ -178,6 +181,7 @@
             << " for guest port " << guest_port;
   auto server = cvd::SharedFD::SocketLocalServer(host_port, SOCK_STREAM);
   CHECK(server->IsOpen()) << "Could not start server on port " << host_port;
+  LaunchConnectionMaintainer(host_port);
   while (true) {
     auto client_socket = cvd::SharedFD::Accept(*server);
     CHECK(client_socket->IsOpen()) << "error creating client socket";
diff --git a/host/libs/Android.bp b/host/libs/Android.bp
index c7aa2ec..12fde97 100644
--- a/host/libs/Android.bp
+++ b/host/libs/Android.bp
@@ -14,6 +14,7 @@
 // limitations under the License.
 
 subdirs = [
+    "adb_connection_maintainer",
     "config",
     "monitor",
     "ivserver",
diff --git a/host/libs/adb_connection_maintainer/Android.bp b/host/libs/adb_connection_maintainer/Android.bp
new file mode 100644
index 0000000..4a89da3
--- /dev/null
+++ b/host/libs/adb_connection_maintainer/Android.bp
@@ -0,0 +1,28 @@
+//
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_library_host_static {
+    name: "libadb_connection_maintainer",
+    srcs: [
+        "adb_connection_maintainer.cpp",
+    ],
+    header_libs: [
+        "cuttlefish_glog",
+    ],
+    shared_libs: [
+        "libbase",
+    ],
+    defaults: ["cuttlefish_host_only"],
+}
diff --git a/host/libs/adb_connection_maintainer/adb_connection_maintainer.cpp b/host/libs/adb_connection_maintainer/adb_connection_maintainer.cpp
new file mode 100644
index 0000000..22db608
--- /dev/null
+++ b/host/libs/adb_connection_maintainer/adb_connection_maintainer.cpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <iomanip>
+#include <sstream>
+#include <string>
+#include <memory>
+#include <glog/logging.h>
+
+#include <unistd.h>
+
+#include "common/libs/fs/shared_fd.h"
+#include "host/libs/adb_connection_maintainer/adb_connection_maintainer.h"
+
+namespace {
+
+std::string MakeMessage(const std::string& user_message) {
+  static constexpr char kPrefix[] = "host:";
+  static constexpr std::size_t kPrefixLen = sizeof kPrefix - 1;
+  std::ostringstream ss;
+  ss << std::setfill('0') << std::setw(4) << std::hex
+     << (kPrefixLen + user_message.size()) << kPrefix << user_message;
+  return ss.str();
+}
+
+std::string MakeIPAndPort(int port) {
+  static constexpr char kLocalHostPrefix[] = "127.0.0.1:";
+  return kLocalHostPrefix + std::to_string(port);
+}
+
+std::string MakeConnectMessage(int port) {
+  static constexpr char kConnectPrefix[] = "connect:";
+  return MakeMessage(kConnectPrefix + MakeIPAndPort(port));
+}
+
+// returns true if successfully sent the whole message
+bool SendAll(cvd::SharedFD sock, const std::string& msg) {
+  ssize_t total_written{};
+  while (total_written < static_cast<ssize_t>(msg.size())) {
+    if (!sock->IsOpen()) {
+      return false;
+    }
+    auto just_written = sock->Send(msg.c_str() + total_written,
+                                   msg.size() - total_written, MSG_NOSIGNAL);
+    if (just_written <= 0) {
+      return false;
+    }
+    total_written += just_written;
+  }
+  return true;
+}
+
+std::string RecvAll(cvd::SharedFD sock, const size_t count) {
+  size_t total_read{};
+  std::unique_ptr<char[]> data(new char[count]);
+  while (total_read < count) {
+    auto just_read = sock->Read(data.get() + total_read, count - total_read);
+    if (just_read <= 0) {
+      return {};
+    }
+    total_read += just_read;
+  }
+  return {data.get(), count};
+}
+
+// Response will either be OKAY or FAIL
+constexpr char kAdbOkayStatusResponse[] = "OKAY";
+constexpr std::size_t kAdbStatusResponseLength =
+    sizeof kAdbOkayStatusResponse - 1;
+// adb sends the length of what is to follow as a 4 characters string of hex
+// digits
+constexpr std::size_t kAdbMessageLengthLength = 4;
+
+constexpr int kAdbDaemonPort = 5037;
+
+bool AdbConnect(cvd::SharedFD sock, int port) {
+  if (!SendAll(sock, MakeConnectMessage(port))) {
+    return false;
+  }
+  return RecvAll(sock, kAdbStatusResponseLength) == kAdbOkayStatusResponse;
+}
+
+// assumes the OKAY/FAIL status has already been read
+std::string RecvAdbResponse(cvd::SharedFD sock) {
+  auto length_as_hex_str = RecvAll(sock, kAdbMessageLengthLength);
+  auto length = std::stoi(length_as_hex_str, nullptr, 16);
+  return RecvAll(sock, length);
+}
+
+void EstablishConnection(int port) {
+  while (true) {
+    LOG(INFO) << "Attempting to connect to device on port " << port;
+    auto sock = cvd::SharedFD::SocketLocalClient(kAdbDaemonPort, SOCK_STREAM);
+    if (sock->IsOpen() && AdbConnect(sock, port)) {
+      LOG(INFO) << "connected to device on port " << port << '\n';
+      break;
+    }
+    sleep(2);
+  }
+}
+
+void WaitForAdbDisconnection(int port) {
+  LOG(INFO) << "Watching for disconnect on port " << port;
+  while (true) {
+    auto sock = cvd::SharedFD::SocketLocalClient(kAdbDaemonPort, SOCK_STREAM);
+    if (!SendAll(sock, MakeMessage("devices"))) {
+      break;
+    }
+    if (RecvAll(sock, 4) != kAdbOkayStatusResponse) {
+      break;
+    }
+    auto devices_str = RecvAdbResponse(sock);
+    if (devices_str.find(MakeIPAndPort(port)) == std::string::npos) {
+      break;
+    }
+    sleep(2);
+  }
+}
+
+}  // namespace
+
+[[noreturn]] void cvd::EstablishAndMaintainConnection(int port) {
+  while (true) {
+    EstablishConnection(port);
+    WaitForAdbDisconnection(port);
+  }
+}
diff --git a/host/libs/adb_connection_maintainer/adb_connection_maintainer.h b/host/libs/adb_connection_maintainer/adb_connection_maintainer.h
new file mode 100644
index 0000000..d3378ce
--- /dev/null
+++ b/host/libs/adb_connection_maintainer/adb_connection_maintainer.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+namespace cvd {
+
+[[noreturn]] void EstablishAndMaintainConnection(int port);
+
+}  // namespace cvd