adb: add client side shell protocol and enable.
Adds the shell protocol functionality to the client side and enables it
if the transport supports the feature.
Bug:http://b/23031026
Change-Id: I9abe1c8b1d39f8dd09666321b1c761ad708a8854
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 8d50f46..c508b32 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -31,8 +31,10 @@
#include <sys/stat.h>
#include <sys/types.h>
+#include <memory>
#include <string>
+#include <base/logging.h>
#include <base/stringprintf.h>
#if !defined(_WIN32)
@@ -46,6 +48,8 @@
#include "adb_io.h"
#include "adb_utils.h"
#include "file_sync_service.h"
+#include "shell_service.h"
+#include "transport.h"
static int install_app(TransportType t, const char* serial, int argc, const char** argv);
static int install_multiple_app(TransportType t, const char* serial, int argc, const char** argv);
@@ -256,19 +260,60 @@
}
#endif
-static void read_and_dump(int fd) {
+// Reads from |fd| and prints received data. If |use_shell_protocol| is true
+// this expects that incoming data will use the shell protocol, in which case
+// stdout/stderr are routed independently and the remote exit code will be
+// returned.
+static int read_and_dump(int fd, bool use_shell_protocol=false) {
+ int exit_code = 0;
+ std::unique_ptr<ShellProtocol> protocol;
+ int length = 0;
+ FILE* outfile = stdout;
+
+ char raw_buffer[BUFSIZ];
+ char* buffer_ptr = raw_buffer;
+ if (use_shell_protocol) {
+ protocol.reset(new ShellProtocol(fd));
+ if (!protocol) {
+ LOG(ERROR) << "failed to allocate memory for ShellProtocol object";
+ return 1;
+ }
+ buffer_ptr = protocol->data();
+ }
+
while (fd >= 0) {
- D("read_and_dump(): pre adb_read(fd=%d)", fd);
- char buf[BUFSIZ];
- int len = adb_read(fd, buf, sizeof(buf));
- D("read_and_dump(): post adb_read(fd=%d): len=%d", fd, len);
- if (len <= 0) {
- break;
+ if (use_shell_protocol) {
+ if (!protocol->Read()) {
+ break;
+ }
+ switch (protocol->id()) {
+ case ShellProtocol::kIdStdout:
+ outfile = stdout;
+ break;
+ case ShellProtocol::kIdStderr:
+ outfile = stderr;
+ break;
+ case ShellProtocol::kIdExit:
+ exit_code = protocol->data()[0];
+ continue;
+ default:
+ continue;
+ }
+ length = protocol->data_length();
+ } else {
+ D("read_and_dump(): pre adb_read(fd=%d)", fd);
+ length = adb_read(fd, raw_buffer, sizeof(raw_buffer));
+ D("read_and_dump(): post adb_read(fd=%d): length=%d", fd, length);
+ if (length <= 0) {
+ break;
+ }
}
- fwrite(buf, 1, len, stdout);
- fflush(stdout);
+ fwrite(buffer_ptr, 1, length, outfile);
+ fflush(outfile);
}
+
+ return exit_code;
}
static void read_status_line(int fd, char* buf, size_t count)
@@ -362,28 +407,41 @@
free(buf);
}
-static void *stdin_read_thread(void *x)
-{
- int fd, fdi;
- unsigned char buf[1024];
- int r, n;
- int state = 0;
+namespace {
- int *fds = (int*) x;
- fd = fds[0];
- fdi = fds[1];
- free(fds);
+// Used to pass multiple values to the stdin read thread.
+struct StdinReadArgs {
+ int stdin_fd, write_fd;
+ std::unique_ptr<ShellProtocol> protocol;
+};
+
+} // namespace
+
+// Loops to read from stdin and push the data to the given FD.
+// The argument should be a pointer to a StdinReadArgs object. This function
+// will take ownership of the object and delete it when finished.
+static void* stdin_read_thread(void* x) {
+ std::unique_ptr<StdinReadArgs> args(reinterpret_cast<StdinReadArgs*>(x));
+ int state = 0;
adb_thread_setname("stdin reader");
+ char raw_buffer[1024];
+ char* buffer_ptr = raw_buffer;
+ size_t buffer_size = sizeof(raw_buffer);
+ if (args->protocol) {
+ buffer_ptr = args->protocol->data();
+ buffer_size = args->protocol->data_capacity();
+ }
+
while (true) {
- /* fdi is really the client's stdin, so use read, not adb_read here */
- D("stdin_read_thread(): pre unix_read(fdi=%d,...)", fdi);
- r = unix_read(fdi, buf, 1024);
- D("stdin_read_thread(): post unix_read(fdi=%d,...)", fdi);
+ // Use unix_read() rather than adb_read() for stdin.
+ D("stdin_read_thread(): pre unix_read(fdi=%d,...)", args->stdin_fd);
+ int r = unix_read(args->stdin_fd, buffer_ptr, buffer_size);
+ D("stdin_read_thread(): post unix_read(fdi=%d,...)", args->stdin_fd);
if (r <= 0) break;
- for (n = 0; n < r; n++){
- switch(buf[n]) {
+ for (int n = 0; n < r; n++){
+ switch(buffer_ptr[n]) {
case '\n':
state = 1;
break;
@@ -396,47 +454,59 @@
case '.':
if(state == 2) {
fprintf(stderr,"\n* disconnect *\n");
- stdin_raw_restore(fdi);
+ stdin_raw_restore(args->stdin_fd);
exit(0);
}
default:
state = 0;
}
}
- r = adb_write(fd, buf, r);
- if(r <= 0) {
- break;
+ if (args->protocol) {
+ if (!args->protocol->Write(ShellProtocol::kIdStdin, r)) {
+ break;
+ }
+ } else {
+ if (!WriteFdExactly(args->write_fd, buffer_ptr, r)) {
+ break;
+ }
}
}
- return 0;
+
+ return nullptr;
}
-static int interactive_shell() {
- int fdi;
-
+static int interactive_shell(bool use_shell_protocol) {
std::string error;
int fd = adb_connect("shell:", &error);
if (fd < 0) {
fprintf(stderr,"error: %s\n", error.c_str());
return 1;
}
- fdi = 0; //dup(0);
- int* fds = reinterpret_cast<int*>(malloc(sizeof(int) * 2));
- if (fds == nullptr) {
- fprintf(stderr, "couldn't allocate fds array: %s\n", strerror(errno));
+ StdinReadArgs* args = new StdinReadArgs;
+ if (!args) {
+ LOG(ERROR) << "couldn't allocate StdinReadArgs object";
return 1;
}
+ args->stdin_fd = 0;
+ args->write_fd = fd;
+ if (use_shell_protocol) {
+ args->protocol.reset(new ShellProtocol(args->write_fd));
+ }
- fds[0] = fd;
- fds[1] = fdi;
+ stdin_raw_init(args->stdin_fd);
- stdin_raw_init(fdi);
+ int exit_code = 0;
+ if (!adb_thread_create(stdin_read_thread, args)) {
+ PLOG(ERROR) << "error starting stdin read thread";
+ exit_code = 1;
+ delete args;
+ } else {
+ exit_code = read_and_dump(fd, use_shell_protocol);
+ }
- adb_thread_create(stdin_read_thread, fds);
- read_and_dump(fd);
- stdin_raw_restore(fdi);
- return 0;
+ stdin_raw_restore(args->stdin_fd);
+ return exit_code;
}
@@ -943,6 +1013,20 @@
#endif
}
+// Checks whether the device indicated by |transport_type| and |serial| supports
+// |feature|. Returns the response string, which will be empty if the device
+// could not be found or the feature is not supported.
+static std::string CheckFeature(const std::string& feature,
+ TransportType transport_type,
+ const char* serial) {
+ std::string result, error, command("check-feature:" + feature);
+ if (!adb_query(format_host_command(command.c_str(), transport_type, serial),
+ &result, &error)) {
+ return "";
+ }
+ return result;
+}
+
int adb_commandline(int argc, const char **argv) {
int no_daemon = 0;
int is_daemon = 0;
@@ -1156,9 +1240,19 @@
fflush(stdout);
}
+ bool use_shell_protocol;
+ if (CheckFeature(kFeatureShell2, transport_type, serial).empty()) {
+ D("shell protocol not supported, using raw data transfer");
+ use_shell_protocol = false;
+ } else {
+ D("using shell protocol");
+ use_shell_protocol = true;
+ }
+
+
if (argc < 2) {
D("starting interactive shell");
- r = interactive_shell();
+ r = interactive_shell(use_shell_protocol);
if (h) {
printf("\x1b[0m");
fflush(stdout);
@@ -1176,16 +1270,15 @@
}
while (true) {
- D("interactive shell loop. cmd=%s", cmd.c_str());
+ D("non-interactive shell loop. cmd=%s", cmd.c_str());
std::string error;
int fd = adb_connect(cmd, &error);
int r;
if (fd >= 0) {
D("about to read_and_dump(fd=%d)", fd);
- read_and_dump(fd);
+ r = read_and_dump(fd, use_shell_protocol);
D("read_and_dump() done.");
adb_close(fd);
- r = 0;
} else {
fprintf(stderr,"error: %s\n", error.c_str());
r = -1;
@@ -1195,7 +1288,7 @@
printf("\x1b[0m");
fflush(stdout);
}
- D("interactive shell loop. return r=%d", r);
+ D("non-interactive shell loop. return r=%d", r);
return r;
}
}