Pass $TERM to the device.

Unfortunately, this isn't backwards-compatible with the current shell
protocol because we made unknown shell: arguments errors. We could try
to commit the change to make them just warnings first, but how would
we know when everyone was running adbd with that change? Bumping the
protocol version doesn't help because that only affects the code running
on the host. And although we could add another feature to the reported
features, since shell_v2 is still in development, that doesn't seem
worthwhile.

Bug: http://b/25601436
Change-Id: I12b81aa656cd25b91d14ef691dcbd2b7dab49535
diff --git a/commandline.cpp b/commandline.cpp
index 5113eb8..c13872a 100644
--- a/commandline.cpp
+++ b/commandline.cpp
@@ -595,6 +595,10 @@
     if (!type_arg.empty()) {
         args.push_back(type_arg);
     }
+    const char* terminal_type = getenv("TERM");
+    if (terminal_type != nullptr) {
+        args.push_back(std::string("TERM=") + terminal_type);
+    }
 
     // Shell service string can look like: shell[,arg1,arg2,...]:[command].
     return android::base::StringPrintf("shell%s%s:%s",
@@ -1029,8 +1033,7 @@
         use_shell_protocol = CanUseFeature(features, kFeatureShell2);
     }
 
-    std::string service_string = ShellServiceString(use_shell_protocol, "",
-                                                    command);
+    std::string service_string = ShellServiceString(use_shell_protocol, "", command);
 
     int fd;
     while (true) {
diff --git a/services.cpp b/services.cpp
index 19a6726..41da4b8 100644
--- a/services.cpp
+++ b/services.cpp
@@ -213,9 +213,11 @@
     // Defaults:
     //   PTY for interactive, raw for non-interactive.
     //   No protocol.
+    //   $TERM set to "dumb".
     SubprocessType type(command.empty() ? SubprocessType::kPty
                                         : SubprocessType::kRaw);
     SubprocessProtocol protocol = SubprocessProtocol::kNone;
+    std::string terminal_type = "dumb";
 
     for (const std::string& arg : android::base::Split(service_args, ",")) {
         if (arg == kShellServiceArgRaw) {
@@ -224,14 +226,15 @@
             type = SubprocessType::kPty;
         } else if (arg == kShellServiceArgShellProtocol) {
             protocol = SubprocessProtocol::kShell;
-        }
-        else if (!arg.empty()) {
-            LOG(ERROR) << "Unsupported shell service arguments: " << args;
-            return -1;
+        } else if (android::base::StartsWith(arg, "TERM=")) {
+            terminal_type = arg.substr(5);
+        } 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(), type, protocol);
+    return StartSubprocess(command.c_str(), terminal_type.c_str(), type, protocol);
 }
 
 #endif  // !ADB_HOST
@@ -308,8 +311,7 @@
     } else if(!strncmp(name, "shell", 5)) {
         ret = ShellService(name + 5, transport);
     } else if(!strncmp(name, "exec:", 5)) {
-        ret = StartSubprocess(name + 5, SubprocessType::kRaw,
-                              SubprocessProtocol::kNone);
+        ret = StartSubprocess(name + 5, nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
     } else if(!strncmp(name, "sync:", 5)) {
         ret = create_service_thread(file_sync_service, NULL);
     } else if(!strncmp(name, "remount:", 8)) {
@@ -325,9 +327,9 @@
     } else if(!strncmp(name, "backup:", 7)) {
         ret = StartSubprocess(android::base::StringPrintf("/system/bin/bu backup %s",
                                                           (name + 7)).c_str(),
-                              SubprocessType::kRaw, SubprocessProtocol::kNone);
+                              nullptr, SubprocessType::kRaw, SubprocessProtocol::kNone);
     } else if(!strncmp(name, "restore:", 8)) {
-        ret = StartSubprocess("/system/bin/bu restore", SubprocessType::kRaw,
+        ret = StartSubprocess("/system/bin/bu restore", nullptr, SubprocessType::kRaw,
                               SubprocessProtocol::kNone);
     } else if(!strncmp(name, "tcpip:", 6)) {
         int port;
diff --git a/shell_service.cpp b/shell_service.cpp
index e3fde26..2e41fe6 100644
--- a/shell_service.cpp
+++ b/shell_service.cpp
@@ -175,8 +175,8 @@
 
 class Subprocess {
   public:
-    Subprocess(const std::string& command, SubprocessType type,
-               SubprocessProtocol protocol);
+    Subprocess(const std::string& command, const char* terminal_type,
+               SubprocessType type, SubprocessProtocol protocol);
     ~Subprocess();
 
     const std::string& command() const { return command_; }
@@ -207,6 +207,7 @@
     ScopedFd* PassOutput(ScopedFd* sfd, ShellProtocol::Id id);
 
     const std::string command_;
+    const std::string terminal_type_;
     SubprocessType type_;
     SubprocessProtocol protocol_;
     pid_t pid_ = -1;
@@ -220,9 +221,12 @@
     DISALLOW_COPY_AND_ASSIGN(Subprocess);
 };
 
-Subprocess::Subprocess(const std::string& command, SubprocessType type,
-                       SubprocessProtocol protocol)
-        : command_(command), type_(type), protocol_(protocol) {
+Subprocess::Subprocess(const std::string& command, const char* terminal_type,
+                       SubprocessType type, SubprocessProtocol protocol)
+    : command_(command),
+      terminal_type_(terminal_type ? terminal_type : ""),
+      type_(type),
+      protocol_(protocol) {
 }
 
 Subprocess::~Subprocess() {
@@ -290,6 +294,9 @@
             setenv("SHELL", pw->pw_shell, 1);
             setenv("USER", pw->pw_name, 1);
         }
+        if (!terminal_type_.empty()) {
+            setenv("TERM", terminal_type_.c_str(), 1);
+        }
 
         if (is_interactive()) {
             execl(_PATH_BSHELL, _PATH_BSHELL, "-", nullptr);
@@ -644,13 +651,14 @@
 
 }  // namespace
 
-int StartSubprocess(const char *name, SubprocessType type,
-                    SubprocessProtocol protocol) {
-    D("starting %s subprocess (protocol=%s): '%s'",
+int StartSubprocess(const char* 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", name);
+      protocol == SubprocessProtocol::kNone ? "none" : "shell",
+      terminal_type, name);
 
-    Subprocess* subprocess = new Subprocess(name, type, protocol);
+    Subprocess* subprocess = new Subprocess(name, terminal_type, type, protocol);
     if (!subprocess) {
         LOG(ERROR) << "failed to allocate new subprocess";
         return -1;
diff --git a/shell_service.h b/shell_service.h
index 63c00da..6f8ea9b 100644
--- a/shell_service.h
+++ b/shell_service.h
@@ -141,8 +141,8 @@
 // shell is started, otherwise |name| is executed non-interactively.
 //
 // Returns an open FD connected to the subprocess or -1 on failure.
-int StartSubprocess(const char* name, SubprocessType type,
-                    SubprocessProtocol protocol);
+int StartSubprocess(const char* name, const char* terminal_type,
+                    SubprocessType type, SubprocessProtocol protocol);
 
 #endif  // !ADB_HOST
 
diff --git a/shell_service_test.cpp b/shell_service_test.cpp
index e18f905..a012f3e 100644
--- a/shell_service_test.cpp
+++ b/shell_service_test.cpp
@@ -69,7 +69,7 @@
     SHELL_EXIT_NOTIFY_FD = fd[0];
     shell_exit_receiver_fd_ = fd[1];
 
-    subprocess_fd_ = StartSubprocess(command, type, protocol);
+    subprocess_fd_ = StartSubprocess(command, nullptr, type, protocol);
     ASSERT_TRUE(subprocess_fd_ >= 0);
 }