adb: fix subprocess termination for legacy shell.

http://r.android.com/166419 changed `adb shell` behavior to not
allocate a remote PTY for non-interactive commands, but adbd relied on
having a PTY to properly terminate the subprocess.

One impact of this is that when using older versions of adb or passing
the -x flag, `adb screenrecord` wasn't properly terminating and closing
out the video file.

This CL restores the old behavior for legacy shell connections: always
use a PTY, but put it in raw mode if the client is doing local PTY
input/output processing itself.

Bug: http://b/26742824
Change-Id: I9ee630c0ff0d2d6a0db367387af7123deea79676
diff --git a/adb/shell_service.cpp b/adb/shell_service.cpp
index e092dc4..b9a22dc 100644
--- a/adb/shell_service.cpp
+++ b/adb/shell_service.cpp
@@ -210,6 +210,7 @@
 
     const std::string command_;
     const std::string terminal_type_;
+    bool make_pty_raw_ = false;
     SubprocessType type_;
     SubprocessProtocol protocol_;
     pid_t pid_ = -1;
@@ -229,6 +230,18 @@
       terminal_type_(terminal_type ? terminal_type : ""),
       type_(type),
       protocol_(protocol) {
+    // If we aren't using the shell protocol we must allocate a PTY to properly close the
+    // subprocess. PTYs automatically send SIGHUP to the slave-side process when the master side
+    // of the PTY closes, which we rely on. If we use a raw pipe, processes that don't read/write,
+    // e.g. screenrecord, will never notice the broken pipe and terminate.
+    // The shell protocol doesn't require a PTY because it's always monitoring the local socket FD
+    // with select() and will send SIGHUP manually to the child process.
+    if (protocol_ == SubprocessProtocol::kNone && type_ == SubprocessType::kRaw) {
+        // Disable PTY input/output processing since the client is expecting raw data.
+        D("Can't create raw subprocess without shell protocol, using PTY in raw mode instead");
+        type_ = SubprocessType::kPty;
+        make_pty_raw_ = true;
+    }
 }
 
 Subprocess::~Subprocess() {
@@ -408,6 +421,24 @@
         exit(-1);
     }
 
+    if (make_pty_raw_) {
+        termios tattr;
+        if (tcgetattr(child_fd, &tattr) == -1) {
+            int saved_errno = errno;
+            WriteFdExactly(error_sfd->fd(), "tcgetattr failed: ");
+            WriteFdExactly(error_sfd->fd(), strerror(saved_errno));
+            exit(-1);
+        }
+
+        cfmakeraw(&tattr);
+        if (tcsetattr(child_fd, TCSADRAIN, &tattr) == -1) {
+            int saved_errno = errno;
+            WriteFdExactly(error_sfd->fd(), "tcsetattr failed: ");
+            WriteFdExactly(error_sfd->fd(), strerror(saved_errno));
+            exit(-1);
+        }
+    }
+
     return child_fd;
 }