adb: switch daemon_service_to_fd to string_view.

Test: test_adb.py
Test: test_device.py
Test: $ANDROID_HOST_OUT/nativetest64/adb_test/adb_test
Test: adb shell /data/nativetest64/adbd_test/adbd_test
Change-Id: I298517b688650c9d94bf837284e0264ca0ac6702
diff --git a/adb.h b/adb.h
index cdd6346..bd5d550 100644
--- a/adb.h
+++ b/adb.h
@@ -141,7 +141,7 @@
 
 int service_to_fd(const char* name, atransport* transport);
 #if !ADB_HOST
-unique_fd daemon_service_to_fd(const char* name, atransport* transport);
+unique_fd daemon_service_to_fd(std::string_view name, atransport* transport);
 #endif
 
 #if ADB_HOST
diff --git a/adb_utils.h b/adb_utils.h
index 6d12225..8712e1b 100644
--- a/adb_utils.h
+++ b/adb_utils.h
@@ -94,3 +94,16 @@
 };
 
 std::string GetLogFilePath();
+
+inline std::string_view StripTrailingNulls(std::string_view str) {
+    size_t n = 0;
+    for (auto it = str.rbegin(); it != str.rend(); ++it) {
+        if (*it != '\0') {
+            break;
+        }
+        ++n;
+    }
+
+    str.remove_suffix(n);
+    return str;
+}
diff --git a/daemon/services.cpp b/daemon/services.cpp
index 3182ddd..84a7655 100644
--- a/daemon/services.cpp
+++ b/daemon/services.cpp
@@ -151,14 +151,17 @@
     kick_transport(t);
 }
 
-unique_fd reverse_service(const char* command, atransport* transport) {
+unique_fd reverse_service(std::string_view command, atransport* transport) {
+    // TODO: Switch handle_forward_request to std::string_view.
+    std::string str(command);
+
     int s[2];
     if (adb_socketpair(s)) {
         PLOG(ERROR) << "cannot create service socket pair.";
         return unique_fd{};
     }
     VLOG(SERVICES) << "service socketpair: " << s[0] << ", " << s[1];
-    if (!handle_forward_request(command, transport, s[1])) {
+    if (!handle_forward_request(str.c_str(), transport, s[1])) {
         SendFail(s[1], "not a reverse forwarding command");
     }
     adb_close(s[1]);
@@ -167,15 +170,16 @@
 
 // Shell service string can look like:
 //   shell[,arg1,arg2,...]:[command]
-unique_fd ShellService(const std::string& args, const atransport* transport) {
+unique_fd ShellService(std::string_view args, const atransport* transport) {
     size_t delimiter_index = args.find(':');
     if (delimiter_index == std::string::npos) {
         LOG(ERROR) << "No ':' found in shell service arguments: " << args;
         return unique_fd{};
     }
 
-    const std::string service_args = args.substr(0, delimiter_index);
-    const std::string command = args.substr(delimiter_index + 1);
+    // TODO: android::base::Split(const std::string_view&, ...)
+    std::string service_args(args.substr(0, delimiter_index));
+    std::string command(args.substr(delimiter_index + 1));
 
     // Defaults:
     //   PTY for interactive, raw for non-interactive.
@@ -192,15 +196,15 @@
             type = SubprocessType::kPty;
         } else if (arg == kShellServiceArgShellProtocol) {
             protocol = SubprocessProtocol::kShell;
-        } else if (android::base::StartsWith(arg, "TERM=")) {
-            terminal_type = arg.substr(5);
+        } else if (arg.starts_with("TERM=")) {
+            terminal_type = arg.substr(strlen("TERM="));
         } else if (!arg.empty()) {
             // This is not an error to allow for future expansion.
             LOG(WARNING) << "Ignoring unknown shell service argument: " << arg;
         }
     }
 
-    return StartSubprocess(command.c_str(), terminal_type.c_str(), type, protocol);
+    return StartSubprocess(command, terminal_type.c_str(), type, protocol);
 }
 
 static void spin_service(unique_fd fd) {
@@ -323,59 +327,77 @@
     return nullptr;
 }
 
-unique_fd daemon_service_to_fd(const char* name, atransport* transport) {
-    if (!strncmp("dev:", name, 4)) {
-        return unique_fd{unix_open(name + 4, O_RDWR | O_CLOEXEC)};
-    } else if (!strncmp(name, "framebuffer:", 12)) {
+unique_fd daemon_service_to_fd(std::string_view name, atransport* transport) {
+    // Historically, we received service names as a char*, and stopped at the first NUL byte.
+    // The client unintentionally sent strings with embedded NULs, which post-string_view, start
+    // being interpreted as part of the string, unless we explicitly strip them.
+    // Notably, shell checks that the part after "shell:" is empty to determine whether the session
+    // is interactive, and {'\0'} is non-empty.
+    name = StripTrailingNulls(name);
+
+    if (name.starts_with("dev:")) {
+        name.remove_prefix(strlen("dev:"));
+        return unique_fd{unix_open(name, O_RDWR | O_CLOEXEC)};
+    } else if (name.starts_with("framebuffer:")) {
         return create_service_thread("fb", framebuffer_service);
-    } else if (!strncmp(name, "jdwp:", 5)) {
-        return create_jdwp_connection_fd(atoi(name + 5));
-    } else if (!strncmp(name, "shell", 5)) {
-        return ShellService(name + 5, transport);
-    } else if (!strncmp(name, "exec:", 5)) {
-        return StartSubprocess(name + 5, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
-    } else if (!strncmp(name, "sync:", 5)) {
+    } else if (name.starts_with("jdwp:")) {
+        name.remove_prefix(strlen("jdwp:"));
+        std::string str(name);
+        return create_jdwp_connection_fd(atoi(str.c_str()));
+    } else if (name.starts_with("shell")) {
+        name.remove_prefix(strlen("shell"));
+        return ShellService(name, transport);
+    } else if (name.starts_with("exec:")) {
+        name.remove_prefix(strlen("exec:"));
+        return StartSubprocess(std::string(name), nullptr, SubprocessType::kRaw,
+                               SubprocessProtocol::kNone);
+    } else if (name.starts_with("sync:")) {
         return create_service_thread("sync", file_sync_service);
-    } else if (!strncmp(name, "remount:", 8)) {
-        std::string options(name + strlen("remount:"));
+    } else if (name.starts_with("remount:")) {
+        std::string arg(name.begin() + strlen("remount:"), name.end());
         return create_service_thread("remount",
-                                     std::bind(remount_service, std::placeholders::_1, options));
-    } else if (!strncmp(name, "reboot:", 7)) {
-        std::string arg(name + strlen("reboot:"));
+                                     std::bind(remount_service, std::placeholders::_1, arg));
+    } else if (name.starts_with("reboot:")) {
+        std::string arg(name.begin() + strlen("reboot:"), name.end());
         return create_service_thread("reboot",
                                      std::bind(reboot_service, std::placeholders::_1, arg));
-    } else if (!strncmp(name, "root:", 5)) {
+    } else if (name.starts_with("root:")) {
         return create_service_thread("root", restart_root_service);
-    } else if (!strncmp(name, "unroot:", 7)) {
+    } else if (name.starts_with("unroot:")) {
         return create_service_thread("unroot", restart_unroot_service);
-    } else if (!strncmp(name, "backup:", 7)) {
-        return StartSubprocess(
-                android::base::StringPrintf("/system/bin/bu backup %s", (name + 7)).c_str(),
-                nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
-    } else if (!strncmp(name, "restore:", 8)) {
+    } else if (name.starts_with("backup:")) {
+        name.remove_prefix(strlen("backup:"));
+        std::string cmd = "/system/bin/bu backup ";
+        cmd += name;
+        return StartSubprocess(cmd, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
+    } else if (name.starts_with("restore:")) {
         return StartSubprocess("/system/bin/bu restore", nullptr, SubprocessType::kRaw,
                                SubprocessProtocol::kNone);
-    } else if (!strncmp(name, "tcpip:", 6)) {
+    } else if (name.starts_with("tcpip:")) {
+        name.remove_prefix(strlen("tcpip:"));
+        std::string str(name);
+
         int port;
-        if (sscanf(name + 6, "%d", &port) != 1) {
+        if (sscanf(str.c_str(), "%d", &port) != 1) {
             return unique_fd{};
         }
         return create_service_thread("tcp",
                                      std::bind(restart_tcp_service, std::placeholders::_1, port));
-    } else if (!strncmp(name, "usb:", 4)) {
+    } else if (name.starts_with("usb:")) {
         return create_service_thread("usb", restart_usb_service);
-    } else if (!strncmp(name, "reverse:", 8)) {
-        return reverse_service(name + 8, transport);
-    } else if (!strncmp(name, "disable-verity:", 15)) {
+    } else if (name.starts_with("reverse:")) {
+        name.remove_prefix(strlen("reverse:"));
+        return reverse_service(name, transport);
+    } else if (name.starts_with("disable-verity:")) {
         return create_service_thread("verity-on", std::bind(set_verity_enabled_state_service,
                                                             std::placeholders::_1, false));
-    } else if (!strncmp(name, "enable-verity:", 15)) {
+    } else if (name.starts_with("enable-verity:")) {
         return create_service_thread("verity-off", std::bind(set_verity_enabled_state_service,
                                                              std::placeholders::_1, true));
-    } else if (!strcmp(name, "reconnect")) {
+    } else if (name == "reconnect") {
         return create_service_thread(
                 "reconnect", std::bind(reconnect_service, std::placeholders::_1, transport));
-    } else if (!strcmp(name, "spin")) {
+    } else if (name == "spin") {
         return create_service_thread("spin", spin_service);
     }
 
diff --git a/daemon/shell_service.cpp b/daemon/shell_service.cpp
index 8805fc1..595d5c6 100644
--- a/daemon/shell_service.cpp
+++ b/daemon/shell_service.cpp
@@ -140,8 +140,8 @@
 
 class Subprocess {
   public:
-    Subprocess(const std::string& command, const char* terminal_type,
-               SubprocessType type, SubprocessProtocol protocol);
+    Subprocess(std::string command, const char* terminal_type, SubprocessType type,
+               SubprocessProtocol protocol);
     ~Subprocess();
 
     const std::string& command() const { return command_; }
@@ -191,9 +191,9 @@
     DISALLOW_COPY_AND_ASSIGN(Subprocess);
 };
 
-Subprocess::Subprocess(const std::string& command, const char* terminal_type,
-                       SubprocessType type, SubprocessProtocol protocol)
-    : command_(command),
+Subprocess::Subprocess(std::string command, const char* terminal_type, SubprocessType type,
+                       SubprocessProtocol protocol)
+    : command_(std::move(command)),
       terminal_type_(terminal_type ? terminal_type : ""),
       type_(type),
       protocol_(protocol) {
@@ -745,14 +745,13 @@
     return read;
 }
 
-unique_fd StartSubprocess(const char* name, const char* terminal_type, SubprocessType type,
+unique_fd StartSubprocess(std::string name, const char* terminal_type, SubprocessType type,
                           SubprocessProtocol protocol) {
     D("starting %s subprocess (protocol=%s, TERM=%s): '%s'",
       type == SubprocessType::kRaw ? "raw" : "PTY",
-      protocol == SubprocessProtocol::kNone ? "none" : "shell",
-      terminal_type, name);
+      protocol == SubprocessProtocol::kNone ? "none" : "shell", terminal_type, name.c_str());
 
-    auto subprocess = std::make_unique<Subprocess>(name, terminal_type, type, protocol);
+    auto subprocess = std::make_unique<Subprocess>(std::move(name), terminal_type, type, protocol);
     if (!subprocess) {
         LOG(ERROR) << "failed to allocate new subprocess";
         return ReportError(protocol, "failed to allocate new subprocess");
diff --git a/daemon/shell_service.h b/daemon/shell_service.h
index 2a48923..421d61f 100644
--- a/daemon/shell_service.h
+++ b/daemon/shell_service.h
@@ -16,6 +16,8 @@
 
 #pragma once
 
+#include <string>
+
 #include "adb_unique_fd.h"
 
 enum class SubprocessType {
@@ -32,5 +34,5 @@
 // shell is started, otherwise |name| is executed non-interactively.
 //
 // Returns an open FD connected to the subprocess or -1 on failure.
-unique_fd StartSubprocess(const char* name, const char* terminal_type, SubprocessType type,
+unique_fd StartSubprocess(std::string name, const char* terminal_type, SubprocessType type,
                           SubprocessProtocol protocol);