adb: create shell protocol class.

Adds a new class ShellProtocol to help read and write data with
`adb shell`. This will allow splitting streams and sending out-of-band
data such as exit codes.

Nothing uses the new class yet except the unit tests.

Bug: http://b/23030641

Change-Id: Ieb02e127095c6dda25b7cb188a2e599173fd97e6
diff --git a/adb/Android.mk b/adb/Android.mk
index 0ec7416..81d94ed 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -131,6 +131,8 @@
 LOCAL_SRC_FILES := \
     $(LIBADB_TEST_SRCS) \
     $(LIBADB_TEST_linux_SRCS) \
+    shell_service_protocol.cpp \
+    shell_service_protocol_test.cpp \
 
 LOCAL_SANITIZE := $(adb_target_sanitize)
 LOCAL_STATIC_LIBRARIES := libadbd
@@ -145,7 +147,12 @@
 LOCAL_CFLAGS := -DADB_HOST=1 $(LIBADB_CFLAGS)
 LOCAL_CFLAGS_windows := $(LIBADB_windows_CFLAGS)
 LOCAL_CFLAGS_linux := $(LIBADB_linux_CFLAGS)
-LOCAL_SRC_FILES := $(LIBADB_TEST_SRCS) services.cpp
+LOCAL_SRC_FILES := \
+    $(LIBADB_TEST_SRCS) \
+    services.cpp \
+    shell_service_protocol.cpp \
+    shell_service_protocol_test.cpp \
+
 LOCAL_SRC_FILES_linux := $(LIBADB_TEST_linux_SRCS)
 LOCAL_SRC_FILES_darwin := $(LIBADB_TEST_darwin_SRCS)
 LOCAL_SANITIZE := $(adb_host_sanitize)
@@ -201,6 +208,7 @@
     adb_client.cpp \
     services.cpp \
     file_sync_client.cpp \
+    shell_service_protocol.cpp \
 
 LOCAL_CFLAGS += \
     $(ADB_COMMON_CFLAGS) \
@@ -249,6 +257,7 @@
     remount_service.cpp \
     set_verity_enable_state_service.cpp \
     shell_service.cpp \
+    shell_service_protocol.cpp \
 
 LOCAL_CFLAGS := \
     $(ADB_COMMON_CFLAGS) \
diff --git a/adb/shell_service.h b/adb/shell_service.h
index c2a048c..81d7036 100644
--- a/adb/shell_service.h
+++ b/adb/shell_service.h
@@ -14,9 +14,109 @@
  * limitations under the License.
  */
 
+// This file contains classes and functionality to launch shell subprocesses
+// in adbd and communicate between those subprocesses and the adb client.
+//
+// The main features exposed here are:
+//   1. A ShellPacket class to wrap data in a simple protocol. Both adbd and
+//      the adb client use this class to transmit data between them.
+//   2. Functions to launch a subprocess on the adbd side.
+
 #ifndef SHELL_SERVICE_H_
 #define SHELL_SERVICE_H_
 
+#include <stdint.h>
+
+#include <base/macros.h>
+
+#include "adb.h"
+
+// Class to send and receive shell protocol packets.
+//
+// To keep things simple and predictable, reads and writes block until an entire
+// packet is complete.
+//
+// Example: read raw data from |fd| and send it in a packet.
+//   ShellProtocol* p = new ShellProtocol(protocol_fd);
+//   int len = adb_read(stdout_fd, p->data(), p->data_capacity());
+//   packet->WritePacket(ShellProtocol::kIdStdout, len);
+//
+// Example: read a packet and print it to |stdout|.
+//   ShellProtocol* p = new ShellProtocol(protocol_fd);
+//   if (p->ReadPacket() && p->id() == kIdStdout) {
+//       fwrite(p->data(), 1, p->data_length(), stdout);
+//   }
+class ShellProtocol {
+  public:
+    // This is an unscoped enum to make it easier to compare against raw bytes.
+    enum Id : uint8_t {
+        kIdStdin  = 0,
+        kIdStdout = 1,
+        kIdStderr = 2,
+        kIdExit   = 3,
+        kIdInvalid  = 255,  // Indicates an invalid or unknown packet.
+    };
+
+    // ShellPackets will probably be too large to allocate on the stack so they
+    // should be dynamically allocated on the heap instead.
+    //
+    // |fd| is an open file descriptor to be used to send or receive packets.
+    explicit ShellProtocol(int fd);
+    virtual ~ShellProtocol();
+
+    // Returns a pointer to the data buffer.
+    const char* data() const { return buffer_ + kHeaderSize; }
+    char* data() { return buffer_ + kHeaderSize; }
+
+    // Returns the total capacity of the data buffer.
+    size_t data_capacity() const { return buffer_end_ - data(); }
+
+    // Reads a packet from the FD.
+    //
+    // If a packet is too big to fit in the buffer then Read() will split the
+    // packet across multiple calls. For example, reading a 50-byte packet into
+    // a 20-byte buffer would read 20 bytes, 20 bytes, then 10 bytes.
+    //
+    // Returns false if the FD closed or errored.
+    bool Read();
+
+    // Returns the ID of the packet in the buffer.
+    int id() const { return buffer_[0]; }
+
+    // Returns the number of bytes that have been read into the data buffer.
+    size_t data_length() const { return data_length_; }
+
+    // Writes the packet currently in the buffer to the FD.
+    //
+    // Returns false if the FD closed or errored.
+    bool Write(Id id, size_t length);
+
+  private:
+    // Packets support 4-byte lengths.
+    typedef uint32_t length_t;
+
+    enum {
+        // It's OK if MAX_PAYLOAD doesn't match on the sending and receiving
+        // end, reading will split larger packets into multiple smaller ones.
+        kBufferSize = MAX_PAYLOAD,
+
+        // Header is 1 byte ID + 4 bytes length.
+        kHeaderSize = sizeof(Id) + sizeof(length_t)
+    };
+
+    int fd_;
+    char buffer_[kBufferSize];
+    size_t data_length_ = 0, bytes_left_ = 0;
+
+    // We need to be able to modify this value for testing purposes, but it
+    // will stay constant during actual program use.
+    char* buffer_end_ = buffer_ + sizeof(buffer_);
+
+    friend class ShellProtocolTest;
+
+    DISALLOW_COPY_AND_ASSIGN(ShellProtocol);
+};
+
 #if !ADB_HOST
 
 enum class SubprocessType {
diff --git a/adb/shell_service_protocol.cpp b/adb/shell_service_protocol.cpp
new file mode 100644
index 0000000..623629c
--- /dev/null
+++ b/adb/shell_service_protocol.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2015 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 "shell_service.h"
+
+#include <string.h>
+
+#include <algorithm>
+
+#include "adb_io.h"
+
+ShellProtocol::ShellProtocol(int fd) : fd_(fd) {
+    buffer_[0] = kIdInvalid;
+}
+
+ShellProtocol::~ShellProtocol() {
+}
+
+bool ShellProtocol::Read() {
+    // Only read a new header if we've finished the last packet.
+    if (!bytes_left_) {
+        if (!ReadFdExactly(fd_, buffer_, kHeaderSize)) {
+            return false;
+        }
+
+        length_t packet_length;
+        memcpy(&packet_length, &buffer_[1], sizeof(packet_length));
+        bytes_left_ = packet_length;
+        data_length_ = 0;
+    }
+
+    size_t read_length = std::min(bytes_left_, data_capacity());
+    if (read_length && !ReadFdExactly(fd_, data(), read_length)) {
+        return false;
+    }
+
+    bytes_left_ -= read_length;
+    data_length_ = read_length;
+
+    return true;
+}
+
+bool ShellProtocol::Write(Id id, size_t length) {
+    buffer_[0] = id;
+    length_t typed_length = length;
+    memcpy(&buffer_[1], &typed_length, sizeof(typed_length));
+
+    return WriteFdExactly(fd_, buffer_, kHeaderSize + length);
+}
diff --git a/adb/shell_service_protocol_test.cpp b/adb/shell_service_protocol_test.cpp
new file mode 100644
index 0000000..6c75215
--- /dev/null
+++ b/adb/shell_service_protocol_test.cpp
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2015 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 "shell_service.h"
+
+#include <gtest/gtest.h>
+
+#include <signal.h>
+#include <string.h>
+
+#include "sysdeps.h"
+
+class ShellProtocolTest : public ::testing::Test {
+  public:
+    static void SetUpTestCase() {
+#if !defined(_WIN32)
+        // This is normally done in main.cpp.
+        saved_sigpipe_handler_ = signal(SIGPIPE, SIG_IGN);
+#endif
+    }
+
+    static void TearDownTestCase() {
+#if !defined(_WIN32)
+        signal(SIGPIPE, saved_sigpipe_handler_);
+#endif
+    }
+
+    // Initializes the socketpair and ShellProtocols needed for testing.
+    void SetUp() {
+        int fds[2];
+        ASSERT_EQ(0, adb_socketpair(fds));
+        read_fd_ = fds[0];
+        write_fd_ = fds[1];
+
+        write_protocol_ = new ShellProtocol(write_fd_);
+        ASSERT_TRUE(write_protocol_ != nullptr);
+
+        read_protocol_ = new ShellProtocol(read_fd_);
+        ASSERT_TRUE(read_protocol_ != nullptr);
+    }
+
+    // Cleans up FDs and ShellProtocols. If an FD is closed manually during a
+    // test, set it to -1 to prevent TearDown() trying to close it again.
+    void TearDown() {
+        for (int fd : {read_fd_, write_fd_}) {
+            if (fd >= 0) {
+                adb_close(fd);
+            }
+        }
+        for (ShellProtocol* protocol : {read_protocol_, write_protocol_}) {
+            if (protocol) {
+                delete protocol;
+            }
+        }
+    }
+
+    // Fakes the buffer size so we can test filling buffers.
+    void SetReadDataCapacity(size_t size) {
+        read_protocol_->buffer_end_ = read_protocol_->data() + size;
+    }
+
+    static sighandler_t saved_sigpipe_handler_;
+
+    int read_fd_ = -1, write_fd_ = -1;
+    ShellProtocol *read_protocol_ = nullptr, *write_protocol_ = nullptr;
+};
+
+sighandler_t ShellProtocolTest::saved_sigpipe_handler_ = nullptr;
+
+namespace {
+
+// Returns true if the packet contains the given values.
+bool PacketEquals(const ShellProtocol* protocol, ShellProtocol::Id id,
+                    const void* data, size_t data_length) {
+    return (protocol->id() == id &&
+            protocol->data_length() == data_length &&
+            !memcmp(data, protocol->data(), data_length));
+}
+
+}  // namespace
+
+// Tests data that can fit in a single packet.
+TEST_F(ShellProtocolTest, FullPacket) {
+    ShellProtocol::Id id = ShellProtocol::kIdStdout;
+    char data[] = "abc 123 \0\r\n";
+
+    memcpy(write_protocol_->data(), data, sizeof(data));
+    ASSERT_TRUE(write_protocol_->Write(id, sizeof(data)));
+
+    ASSERT_TRUE(read_protocol_->Read());
+    ASSERT_TRUE(PacketEquals(read_protocol_, id, data, sizeof(data)));
+}
+
+// Tests data that has to be read multiple times due to smaller read buffer.
+TEST_F(ShellProtocolTest, ReadBufferOverflow) {
+    ShellProtocol::Id id = ShellProtocol::kIdStdin;
+
+    memcpy(write_protocol_->data(), "1234567890", 10);
+    ASSERT_TRUE(write_protocol_->Write(id, 10));
+
+    SetReadDataCapacity(4);
+    ASSERT_TRUE(read_protocol_->Read());
+    ASSERT_TRUE(PacketEquals(read_protocol_, id, "1234", 4));
+    ASSERT_TRUE(read_protocol_->Read());
+    ASSERT_TRUE(PacketEquals(read_protocol_, id, "5678", 4));
+    ASSERT_TRUE(read_protocol_->Read());
+    ASSERT_TRUE(PacketEquals(read_protocol_, id, "90", 2));
+}
+
+// Tests a zero length packet.
+TEST_F(ShellProtocolTest, ZeroLengthPacket) {
+    ShellProtocol::Id id = ShellProtocol::kIdStderr;
+
+    ASSERT_TRUE(write_protocol_->Write(id, 0));
+    ASSERT_TRUE(read_protocol_->Read());
+    ASSERT_TRUE(PacketEquals(read_protocol_, id, nullptr, 0));
+}
+
+// Tests exit code packets.
+TEST_F(ShellProtocolTest, ExitCodePacket) {
+    write_protocol_->data()[0] = 20;
+    ASSERT_TRUE(write_protocol_->Write(ShellProtocol::kIdExit, 1));
+
+    ASSERT_TRUE(read_protocol_->Read());
+    ASSERT_EQ(ShellProtocol::kIdExit, read_protocol_->id());
+    ASSERT_EQ(20, read_protocol_->data()[0]);
+}
+
+// Tests writing to a closed pipe.
+TEST_F(ShellProtocolTest, WriteToClosedPipeFail) {
+    adb_close(read_fd_);
+    read_fd_ = -1;
+
+    ASSERT_FALSE(write_protocol_->Write(ShellProtocol::kIdStdout, 0));
+}
+
+// Tests writing to a closed FD.
+TEST_F(ShellProtocolTest, WriteToClosedFdFail) {
+    adb_close(write_fd_);
+    write_fd_ = -1;
+
+    ASSERT_FALSE(write_protocol_->Write(ShellProtocol::kIdStdout, 0));
+}
+
+// Tests reading from a closed pipe.
+TEST_F(ShellProtocolTest, ReadFromClosedPipeFail) {
+    adb_close(write_fd_);
+    write_fd_ = -1;
+
+    ASSERT_FALSE(read_protocol_->Read());
+}
+
+// Tests reading from a closed FD.
+TEST_F(ShellProtocolTest, ReadFromClosedFdFail) {
+    adb_close(read_fd_);
+    read_fd_ = -1;
+
+    ASSERT_FALSE(read_protocol_->Read());
+}
+
+// Tests reading from a closed pipe that has a packet waiting. This checks that
+// even if the pipe closes before we can fully read its contents we will still
+// be able to access the last packets.
+TEST_F(ShellProtocolTest, ReadPacketFromClosedPipe) {
+    ShellProtocol::Id id = ShellProtocol::kIdStdout;
+    char data[] = "foo bar";
+
+    memcpy(write_protocol_->data(), data, sizeof(data));
+    ASSERT_TRUE(write_protocol_->Write(id, sizeof(data)));
+    adb_close(write_fd_);
+    write_fd_ = -1;
+
+    // First read should grab the packet.
+    ASSERT_TRUE(read_protocol_->Read());
+    ASSERT_TRUE(PacketEquals(read_protocol_, id, data, sizeof(data)));
+
+    // Second read should fail.
+    ASSERT_FALSE(read_protocol_->Read());
+}