|  | /* | 
|  | * Copyright (C) 2016 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 "adb.h" | 
|  |  | 
|  | #include "command.h" | 
|  | #include "print.h" | 
|  | #include "util.h" | 
|  |  | 
|  | #include <errno.h> | 
|  | #include <string.h> | 
|  | #include <stdlib.h> | 
|  | #include <unistd.h> | 
|  | #include <sys/types.h> | 
|  | #include <sys/wait.h> | 
|  | #include <limits.h> | 
|  |  | 
|  | #include <iostream> | 
|  | #include <istream> | 
|  | #include <streambuf> | 
|  |  | 
|  | using namespace std; | 
|  |  | 
|  | struct Buffer: public streambuf | 
|  | { | 
|  | Buffer(char* begin, size_t size); | 
|  | }; | 
|  |  | 
|  | Buffer::Buffer(char* begin, size_t size) | 
|  | { | 
|  | this->setg(begin, begin, begin + size); | 
|  | } | 
|  |  | 
|  | int | 
|  | run_adb(const char* first, ...) | 
|  | { | 
|  | Command cmd("adb"); | 
|  |  | 
|  | if (first == NULL) { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | cmd.AddArg(first); | 
|  |  | 
|  | va_list args; | 
|  | va_start(args, first); | 
|  | while (true) { | 
|  | const char* arg = va_arg(args, char*); | 
|  | if (arg == NULL) { | 
|  | break; | 
|  | } | 
|  | cmd.AddArg(arg); | 
|  | } | 
|  | va_end(args); | 
|  |  | 
|  | return run_command(cmd); | 
|  | } | 
|  |  | 
|  | string | 
|  | get_system_property(const string& name, int* err) | 
|  | { | 
|  | Command cmd("adb"); | 
|  | cmd.AddArg("exec-out"); | 
|  | cmd.AddArg("getprop"); | 
|  | cmd.AddArg(name); | 
|  |  | 
|  | return trim(get_command_output(cmd, err, false)); | 
|  | } | 
|  |  | 
|  |  | 
|  | static uint64_t | 
|  | read_varint(int fd, int* err, bool* done) | 
|  | { | 
|  | uint32_t bits = 0; | 
|  | uint64_t result = 0; | 
|  | while (true) { | 
|  | uint8_t byte; | 
|  | ssize_t amt = read(fd, &byte, 1); | 
|  | if (amt == 0) { | 
|  | *done = true; | 
|  | return result; | 
|  | } else if (amt < 0) { | 
|  | return *err = errno; | 
|  | } | 
|  | result |= uint64_t(byte & 0x7F) << bits; | 
|  | if ((byte & 0x80) == 0) { | 
|  | return result; | 
|  | } | 
|  | bits += 7; | 
|  | if (bits > 64) { | 
|  | *err = -1; | 
|  | return 0; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static char* | 
|  | read_sized_buffer(int fd, int* err, size_t* resultSize) | 
|  | { | 
|  | bool done = false; | 
|  | uint64_t size = read_varint(fd, err, &done); | 
|  | if (*err != 0 || done) { | 
|  | return NULL; | 
|  | } | 
|  | if (size == 0) { | 
|  | *resultSize = 0; | 
|  | return NULL; | 
|  | } | 
|  | // 10 MB seems like a reasonable limit. | 
|  | if (size > 10*1024*1024) { | 
|  | print_error("result buffer too large: %llu", size); | 
|  | return NULL; | 
|  | } | 
|  | char* buf = (char*)malloc(size); | 
|  | if (buf == NULL) { | 
|  | print_error("Can't allocate a buffer of size for test results: %llu", size); | 
|  | return NULL; | 
|  | } | 
|  | int pos = 0; | 
|  | while (size - pos > 0) { | 
|  | ssize_t amt = read(fd, buf+pos, size-pos); | 
|  | if (amt == 0) { | 
|  | // early end of pipe | 
|  | print_error("Early end of pipe."); | 
|  | *err = -1; | 
|  | free(buf); | 
|  | return NULL; | 
|  | } else if (amt < 0) { | 
|  | // error | 
|  | *err = errno; | 
|  | free(buf); | 
|  | return NULL; | 
|  | } | 
|  | pos += amt; | 
|  | } | 
|  | *resultSize = (size_t)size; | 
|  | return buf; | 
|  | } | 
|  |  | 
|  | static int | 
|  | read_sized_proto(int fd, Message* message) | 
|  | { | 
|  | int err = 0; | 
|  | size_t size; | 
|  | char* buf = read_sized_buffer(fd, &err, &size); | 
|  | if (err != 0) { | 
|  | if (buf != NULL) { | 
|  | free(buf); | 
|  | } | 
|  | return err; | 
|  | } else if (size == 0) { | 
|  | if (buf != NULL) { | 
|  | free(buf); | 
|  | } | 
|  | return 0; | 
|  | } else if (buf == NULL) { | 
|  | return -1; | 
|  | } | 
|  | Buffer buffer(buf, size); | 
|  | istream in(&buffer); | 
|  |  | 
|  | err = message->ParseFromIstream(&in) ? 0 : -1; | 
|  |  | 
|  | free(buf); | 
|  | return err; | 
|  | } | 
|  |  | 
|  | static int | 
|  | skip_bytes(int fd, ssize_t size, char* scratch, int scratchSize) | 
|  | { | 
|  | while (size > 0) { | 
|  | ssize_t amt = size < scratchSize ? size : scratchSize; | 
|  | fprintf(stderr, "skipping %lu/%ld bytes\n", size, amt); | 
|  | amt = read(fd, scratch, amt); | 
|  | if (amt == 0) { | 
|  | // early end of pipe | 
|  | print_error("Early end of pipe."); | 
|  | return -1; | 
|  | } else if (amt < 0) { | 
|  | // error | 
|  | return errno; | 
|  | } | 
|  | size -= amt; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int | 
|  | skip_unknown_field(int fd, uint64_t tag, char* scratch, int scratchSize) { | 
|  | bool done = false; | 
|  | int err; | 
|  | uint64_t size; | 
|  | switch (tag & 0x7) { | 
|  | case 0: // varint | 
|  | read_varint(fd, &err, &done); | 
|  | if (err != 0) { | 
|  | return err; | 
|  | } else if (done) { | 
|  | return -1; | 
|  | } else { | 
|  | return 0; | 
|  | } | 
|  | case 1: | 
|  | return skip_bytes(fd, 8, scratch, scratchSize); | 
|  | case 2: | 
|  | size = read_varint(fd, &err, &done); | 
|  | if (err != 0) { | 
|  | return err; | 
|  | } else if (done) { | 
|  | return -1; | 
|  | } | 
|  | if (size > INT_MAX) { | 
|  | // we'll be here a long time but this keeps it from overflowing | 
|  | return -1; | 
|  | } | 
|  | return skip_bytes(fd, (ssize_t)size, scratch, scratchSize); | 
|  | case 5: | 
|  | return skip_bytes(fd, 4, scratch, scratchSize); | 
|  | default: | 
|  | print_error("bad wire type for tag 0x%lx\n", tag); | 
|  | return -1; | 
|  | } | 
|  | } | 
|  |  | 
|  | static int | 
|  | read_instrumentation_results(int fd, char* scratch, int scratchSize, | 
|  | InstrumentationCallbacks* callbacks) | 
|  | { | 
|  | bool done = false; | 
|  | int err = 0; | 
|  | string result; | 
|  | while (true) { | 
|  | uint64_t tag = read_varint(fd, &err, &done); | 
|  | if (done) { | 
|  | // Done reading input (this is the only place that a stream end isn't an error). | 
|  | return 0; | 
|  | } else if (err != 0) { | 
|  | return err; | 
|  | } else if (tag == 0xa) { // test_status | 
|  | TestStatus status; | 
|  | err = read_sized_proto(fd, &status); | 
|  | if (err != 0) { | 
|  | return err; | 
|  | } | 
|  | callbacks->OnTestStatus(status); | 
|  | } else if (tag == 0x12) { // session_status | 
|  | SessionStatus status; | 
|  | err = read_sized_proto(fd, &status); | 
|  | if (err != 0) { | 
|  | return err; | 
|  | } | 
|  | callbacks->OnSessionStatus(status); | 
|  | } else { | 
|  | err = skip_unknown_field(fd, tag, scratch, scratchSize); | 
|  | if (err != 0) { | 
|  | return err; | 
|  | } | 
|  | } | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int | 
|  | run_instrumentation_test(const string& packageName, const string& runner, const string& className, | 
|  | InstrumentationCallbacks* callbacks) | 
|  | { | 
|  | Command cmd("adb"); | 
|  | cmd.AddArg("exec-out"); | 
|  | cmd.AddArg("am"); | 
|  | cmd.AddArg("instrument"); | 
|  | cmd.AddArg("-w"); | 
|  | cmd.AddArg("-m"); | 
|  | const int classLen = className.length(); | 
|  | if (classLen > 0) { | 
|  | if (classLen > 1 && className[classLen - 1] == '.') { | 
|  | cmd.AddArg("-e"); | 
|  | cmd.AddArg("package"); | 
|  |  | 
|  | // "am" actually accepts without removing the last ".", but for cleanlines... | 
|  | cmd.AddArg(className.substr(0, classLen - 1)); | 
|  | } else { | 
|  | cmd.AddArg("-e"); | 
|  | cmd.AddArg("class"); | 
|  | cmd.AddArg(className); | 
|  | } | 
|  | } | 
|  | cmd.AddArg(packageName + "/" + runner); | 
|  |  | 
|  | print_command(cmd); | 
|  |  | 
|  | int fds[2]; | 
|  | if (0 != pipe(fds)) { | 
|  | return errno; | 
|  | } | 
|  |  | 
|  | pid_t pid = fork(); | 
|  |  | 
|  | if (pid == -1) { | 
|  | // fork error | 
|  | return errno; | 
|  | } else if (pid == 0) { | 
|  | // child | 
|  | while ((dup2(fds[1], STDOUT_FILENO) == -1) && (errno == EINTR)) {} | 
|  | close(fds[1]); | 
|  | close(fds[0]); | 
|  | const char* prog = cmd.GetProg(); | 
|  | char* const* argv = cmd.GetArgv(); | 
|  | char* const* env = cmd.GetEnv(); | 
|  | exec_with_path_search(prog, argv, env); | 
|  | print_error("Unable to run command: %s", prog); | 
|  | exit(1); | 
|  | } else { | 
|  | // parent | 
|  | close(fds[1]); | 
|  | string result; | 
|  | const int size = 16*1024; | 
|  | char* buf = (char*)malloc(size); | 
|  | int err = read_instrumentation_results(fds[0], buf, size, callbacks); | 
|  | free(buf); | 
|  | int status; | 
|  | waitpid(pid, &status, 0); | 
|  | if (err != 0) { | 
|  | return err; | 
|  | } | 
|  | if (WIFEXITED(status)) { | 
|  | return WEXITSTATUS(status); | 
|  | } else { | 
|  | return -1; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Get the second to last bundle in the args list. Stores the last name found | 
|  | * in last. If the path is not found or if the args list is empty, returns NULL. | 
|  | */ | 
|  | static const ResultsBundleEntry * | 
|  | find_penultimate_entry(const ResultsBundle& bundle, va_list args) | 
|  | { | 
|  | const ResultsBundle* b = &bundle; | 
|  | const char* arg = va_arg(args, char*); | 
|  | while (arg) { | 
|  | string last = arg; | 
|  | arg = va_arg(args, char*); | 
|  | bool found = false; | 
|  | for (int i=0; i<b->entries_size(); i++) { | 
|  | const ResultsBundleEntry& e = b->entries(i); | 
|  | if (e.key() == last) { | 
|  | if (arg == NULL) { | 
|  | return &e; | 
|  | } else if (e.has_value_bundle()) { | 
|  | b = &e.value_bundle(); | 
|  | found = true; | 
|  | } | 
|  | } | 
|  | } | 
|  | if (!found) { | 
|  | return NULL; | 
|  | } | 
|  | if (arg == NULL) { | 
|  | return NULL; | 
|  | } | 
|  | } | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | string | 
|  | get_bundle_string(const ResultsBundle& bundle, bool* found, ...) | 
|  | { | 
|  | va_list args; | 
|  | va_start(args, found); | 
|  | const ResultsBundleEntry* entry = find_penultimate_entry(bundle, args); | 
|  | va_end(args); | 
|  | if (entry == NULL) { | 
|  | *found = false; | 
|  | return string(); | 
|  | } | 
|  | if (entry->has_value_string()) { | 
|  | *found = true; | 
|  | return entry->value_string(); | 
|  | } | 
|  | *found = false; | 
|  | return string(); | 
|  | } | 
|  |  | 
|  | int32_t | 
|  | get_bundle_int(const ResultsBundle& bundle, bool* found, ...) | 
|  | { | 
|  | va_list args; | 
|  | va_start(args, found); | 
|  | const ResultsBundleEntry* entry = find_penultimate_entry(bundle, args); | 
|  | va_end(args); | 
|  | if (entry == NULL) { | 
|  | *found = false; | 
|  | return 0; | 
|  | } | 
|  | if (entry->has_value_int()) { | 
|  | *found = true; | 
|  | return entry->value_int(); | 
|  | } | 
|  | *found = false; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | float | 
|  | get_bundle_float(const ResultsBundle& bundle, bool* found, ...) | 
|  | { | 
|  | va_list args; | 
|  | va_start(args, found); | 
|  | const ResultsBundleEntry* entry = find_penultimate_entry(bundle, args); | 
|  | va_end(args); | 
|  | if (entry == NULL) { | 
|  | *found = false; | 
|  | return 0; | 
|  | } | 
|  | if (entry->has_value_float()) { | 
|  | *found = true; | 
|  | return entry->value_float(); | 
|  | } | 
|  | *found = false; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | double | 
|  | get_bundle_double(const ResultsBundle& bundle, bool* found, ...) | 
|  | { | 
|  | va_list args; | 
|  | va_start(args, found); | 
|  | const ResultsBundleEntry* entry = find_penultimate_entry(bundle, args); | 
|  | va_end(args); | 
|  | if (entry == NULL) { | 
|  | *found = false; | 
|  | return 0; | 
|  | } | 
|  | if (entry->has_value_double()) { | 
|  | *found = true; | 
|  | return entry->value_double(); | 
|  | } | 
|  | *found = false; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int64_t | 
|  | get_bundle_long(const ResultsBundle& bundle, bool* found, ...) | 
|  | { | 
|  | va_list args; | 
|  | va_start(args, found); | 
|  | const ResultsBundleEntry* entry = find_penultimate_entry(bundle, args); | 
|  | va_end(args); | 
|  | if (entry == NULL) { | 
|  | *found = false; | 
|  | return 0; | 
|  | } | 
|  | if (entry->has_value_long()) { | 
|  | *found = true; | 
|  | return entry->value_long(); | 
|  | } | 
|  | *found = false; | 
|  | return 0; | 
|  | } | 
|  |  |