socket_forward_proxy_[host_]ports to launch_cvd

launch_cvd will pass 5555 as the guest port, and a biased host port that
is bumped in increments of 2, (5555, 5557, 5559, ...).

I also needed to split strings in a few spots. I made the interface
match the most simple version of abseil::StrSplit in case we ever manage
to get those string utilities.

BUG: 73011528
Change-Id: Iba0dea36a45789b629d8c73e581a8d05021ef892
diff --git a/common/frontend/socket_forward_proxy/Android.bp b/common/frontend/socket_forward_proxy/Android.bp
index c70983e..caad62b 100644
--- a/common/frontend/socket_forward_proxy/Android.bp
+++ b/common/frontend/socket_forward_proxy/Android.bp
@@ -21,6 +21,7 @@
     shared_libs: [
         "libbase",
         "libcuttlefish_fs",
+        "libcuttlefish_strings",
         "cuttlefish_auto_resources",
         "vsoc_lib",
         "liblog",
diff --git a/common/frontend/socket_forward_proxy/main.cpp b/common/frontend/socket_forward_proxy/main.cpp
index ad1ca10..ed4196e 100644
--- a/common/frontend/socket_forward_proxy/main.cpp
+++ b/common/frontend/socket_forward_proxy/main.cpp
@@ -29,6 +29,7 @@
 #include <unistd.h>
 
 #include "common/libs/fs/shared_fd.h"
+#include "common/libs/strings/str_split.h"
 #include "common/vsoc/lib/socket_forward_region_view.h"
 
 #ifdef CUTTLEFISH_HOST
@@ -39,9 +40,12 @@
 using vsoc::socket_forward::SocketForwardRegionView;
 
 #ifdef CUTTLEFISH_HOST
-DEFINE_string(ports, "",
-              "Comma-separated list of ports from which to forward TCP "
-              "connections.");
+DEFINE_string(guest_ports, "",
+              "Comma-separated list of ports on which to forward TCP "
+              "connections to the guest.");
+DEFINE_string(host_ports, "",
+              "Comma-separated list of ports on which to run TCP servers on "
+              "the host.");
 #endif
 
 namespace {
@@ -154,44 +158,53 @@
 }
 
 #ifdef CUTTLEFISH_HOST
+struct PortPair {
+  int guest_port;
+  int host_port;
+};
+
+
 [[noreturn]] void host_impl(SocketForwardRegionView* shm,
-                            std::vector<int> ports, std::size_t index) {
+                            std::vector<PortPair> ports, std::size_t index) {
   // launch a worker for the following port before handling the current port.
   // recursion (instead of a loop) removes the need fore any join() or having
   // the main thread do no work.
   if (index + 1 < ports.size()) {
     std::thread(host_impl, shm, ports, index + 1).detach();
   }
-  auto remote_port = ports[index];
-  auto local_port = vsoc::GetPerInstanceDefault(remote_port);
-  LOG(INFO) << "starting server on " << local_port
-            << " for guest port " << remote_port;
-  auto server = cvd::SharedFD::SocketLocalServer(local_port, SOCK_STREAM);
-  CHECK(server->IsOpen()) << "Could not start server on port " << local_port;
+  auto guest_port = ports[index].guest_port;
+  auto host_port = ports[index].host_port;
+  LOG(INFO) << "starting server on " << host_port
+            << " 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;
   while (true) {
     auto client_socket = cvd::SharedFD::Accept(*server);
     CHECK(client_socket->IsOpen()) << "error creating client socket";
     LOG(INFO) << "client socket accepted";
-    auto conn = shm->OpenConnection(remote_port);
+    auto conn = shm->OpenConnection(guest_port);
     LOG(INFO) << "shm connection opened";
     LaunchWorkers(std::move(conn), std::move(client_socket));
   }
 }
 
 [[noreturn]] void host(SocketForwardRegionView* shm,
-                           std::vector<int> ports) {
+                       std::vector<PortPair> ports) {
   CHECK(!ports.empty());
   host_impl(shm, ports, 0);
 }
 
-std::vector<int> ParsePortsList(const std::string& ports_flag) {
-  std::istringstream ports_stream{ports_flag};
-  std::vector<int> ports;
-  std::string port_str{};
-  while (std::getline(ports_stream, port_str, ',')) {
-    ports.push_back(std::stoi(port_str));
+std::vector<PortPair> ParsePortsList(const std::string& guest_ports_str,
+                                const std::string& host_ports_str) {
+  std::vector<PortPair> ports{};
+  auto guest_ports = cvd::StrSplit(guest_ports_str, ',');
+  auto host_ports = cvd::StrSplit(host_ports_str, ',');
+  CHECK(guest_ports.size() == host_ports.size());
+  for (std::size_t i = 0; i < guest_ports.size(); ++i) {
+    ports.push_back({std::stoi(guest_ports[i]), std::stoi(host_ports[i])});
   }
   return ports;
+
 }
 
 #else
@@ -250,8 +263,9 @@
   auto worker = shm->StartWorker();
 
 #ifdef CUTTLEFISH_HOST
-  CHECK(!FLAGS_ports.empty()) << "Must specify --ports flag";
-  host(shm, ParsePortsList(FLAGS_ports));
+  CHECK(!FLAGS_guest_ports.empty()) << "Must specify --guest_ports flag";
+  CHECK(!FLAGS_host_ports.empty()) << "Must specify --host_ports flag";
+  host(shm, ParsePortsList(FLAGS_guest_ports, FLAGS_host_ports));
 #else
   guest(shm);
 #endif
diff --git a/common/libs/Android.bp b/common/libs/Android.bp
index b23571f..cb4ec3e 100644
--- a/common/libs/Android.bp
+++ b/common/libs/Android.bp
@@ -17,6 +17,7 @@
     "auto_resources",
     "fs",
     "net",
+    "strings",
     "tcp_socket",
     "threads",
     "time",
diff --git a/common/libs/strings/Android.bp b/common/libs/strings/Android.bp
new file mode 100644
index 0000000..273c79e
--- /dev/null
+++ b/common/libs/strings/Android.bp
@@ -0,0 +1,25 @@
+//
+// 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_shared {
+    name: "libcuttlefish_strings",
+    srcs: [
+        "str_split.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+    ],
+    defaults: ["cuttlefish_host_and_guest"],
+}
diff --git a/common/libs/strings/str_split.cpp b/common/libs/strings/str_split.cpp
new file mode 100644
index 0000000..d76acad
--- /dev/null
+++ b/common/libs/strings/str_split.cpp
@@ -0,0 +1,30 @@
+/*
+ * 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 "common/libs/strings/str_split.h"
+
+#include <vector>
+#include <string>
+#include <sstream>
+
+std::vector<std::string> cvd::StrSplit(const std::string& src, char delimiter) {
+  std::istringstream stream{src};
+  std::vector<std::string> result;
+  for (std::string s; std::getline(stream, s, delimiter); s.clear()) {
+    result.push_back(std::move(s));
+  }
+  return result;
+}
diff --git a/common/libs/strings/str_split.h b/common/libs/strings/str_split.h
new file mode 100644
index 0000000..cd18d15
--- /dev/null
+++ b/common/libs/strings/str_split.h
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+#ifndef CUTTLEFISH_COMMON_COMMON_LIBS_STRINGS_STR_SPLIT_H_
+#define CUTTLEFISH_COMMON_COMMON_LIBS_STRINGS_STR_SPLIT_H_
+
+#include <vector>
+#include <string>
+
+namespace cvd {
+std::vector<std::string> StrSplit(const std::string& src, char delimiter);
+}  // namespace cvd
+
+#endif
diff --git a/host/commands/launch/Android.bp b/host/commands/launch/Android.bp
index 420df07..ac38519 100644
--- a/host/commands/launch/Android.bp
+++ b/host/commands/launch/Android.bp
@@ -11,6 +11,7 @@
     shared_libs: [
         "vsoc_lib",
         "libcuttlefish_fs",
+        "libcuttlefish_strings",
         "cuttlefish_auto_resources",
         "libicuuc",
         "libbase",
diff --git a/host/commands/launch/main.cc b/host/commands/launch/main.cc
index eacd5da..416ebf1 100644
--- a/host/commands/launch/main.cc
+++ b/host/commands/launch/main.cc
@@ -22,15 +22,19 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
+#include <algorithm>
 #include <fstream>
 #include <iomanip>
 #include <memory>
 #include <sstream>
+#include <string>
+#include <vector>
 
 #include <gflags/gflags.h>
 #include <glog/logging.h>
 
 #include "common/libs/fs/shared_select.h"
+#include "common/libs/strings/str_split.h"
 #include "host/commands/launch/pre_launch_initializers.h"
 #include "host/libs/config/file_partition.h"
 #include "host/libs/config/guest_config.h"
@@ -112,17 +116,12 @@
               StringFromEnv("ANDROID_HOST_OUT", StringFromEnv("HOME", ".")) +
                   "/bin/vnc_server",
               "Location of the vnc server binary.");
-DEFINE_int32(vnc_server_port, vsoc::GetPerInstanceDefault(6444),
+DEFINE_int32(vnc_server_port, GetPerInstanceDefault(6444),
              "The port on which the vnc server should listen");
 DEFINE_string(socket_forward_proxy_binary,
               StringFromEnv("ANDROID_HOST_OUT", StringFromEnv("HOME", ".")) +
                   "/bin/socket_forward_proxy",
               "Location of the socket_forward_proxy binary.");
-DEFINE_string(socket_forward_proxy_ports, "5555", "Comma-separated list of "
-              "ports on which to run the socket_forward_proxy server. These "
-              "are the port numbers of the guest-side process. The "
-              "host-side socket_forward_proxy process will bias the port "
-              "numbers.");
 
 DEFINE_bool(start_wifi_relay, true, "Whether to start the wifi_relay process.");
 DEFINE_string(wifi_relay_binary,
@@ -309,6 +308,20 @@
     "/bin/rm", "-f", file.c_str(), NULL};
   subprocess(rm_command, NULL);
 }
+
+
+// Emulators are discovered on odd numbered ports from 5555 to 5585
+constexpr int kFirstEmulatorPort = 5555;
+
+std::string GetGuestPortArg() {
+  return std::string{"--guest_ports="} + std::to_string(kFirstEmulatorPort);
+}
+
+std::string GetHostPortArg() {
+  return std::string{"--host_ports="} +
+      std::to_string(kFirstEmulatorPort + (vsoc::GetDefaultInstance() - 1) * 2);
+}
+
 }  // anonymous namespace
 
 int main(int argc, char** argv) {
@@ -419,9 +432,14 @@
 
   std::string entropy_source = "/dev/urandom";
 
-  auto port_arg = std::string{"--ports="} + FLAGS_socket_forward_proxy_ports;
+  auto guest_port_arg = GetGuestPortArg();
+  auto host_port_arg = GetHostPortArg();
+
   const char* const socket_proxy[] =
-    {FLAGS_socket_forward_proxy_binary.c_str(), port_arg.c_str(), NULL};
+    {FLAGS_socket_forward_proxy_binary.c_str(),
+     guest_port_arg.c_str(),
+     host_port_arg.c_str(),
+     NULL};
   subprocess(socket_proxy, nullptr, false);
 
   config::GuestConfig cfg;