adb: split shell_service.h into client/daemon/protocol parts.

Test: treehugger
Change-Id: I67042f6b29a8a07619b8d56e7376d49b956a840c
diff --git a/adb/shell_protocol.h b/adb/shell_protocol.h
new file mode 100644
index 0000000..2c82689
--- /dev/null
+++ b/adb/shell_protocol.h
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#include <android-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,
+
+        // Close subprocess stdin if possible.
+        kIdCloseStdin = 4,
+
+        // Window size change (an ASCII version of struct winsize).
+        kIdWindowSizeChange = 5,
+
+        // Indicates an invalid or unknown packet.
+        kIdInvalid = 255,
+    };
+
+    // 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);
+};