Add a new build, install, test development (bit) tool

This is a cleaned up version of a utility that I've had sitting around
for a while.

The bit tool can run an android platform build, then sync or install
the outputs of that, and then run instrumentation tests.  It's better
than what we usually do:

  make && adb shell stop && adb sync system && adb shell start

Here's the help text:

usage: bit OPTIONS PATTERN

  Build, sync and test android code.

  The -b -i and -t options allow you to specify which phases
  you want to run. If none of those options are given, then
  all phases are run. If any of these options are provided
  then only the listed phases are run.

  OPTIONS
  -b     Run a build
  -i     Install the targets
  -t     Run the tests

  PATTERN
  One or more targets to build, install and test. The target
  names are the names that appear in the LOCAL_MODULE or
  LOCAL_PACKAGE_NAME variables in Android.mk or Android.bp files.

  Building and installing
  -----------------------
  The modules specified will be built and then installed. If the
  files are on the system partition, they will be synced and the
  attached device rebooted. If they are APKs that aren't on the
  system partition they are installed with adb install.

  For example:
    bit framework
      Builds framework.jar, syncs the system partition and reboots.

    bit SystemUI
      Builds SystemUI.apk, syncs the system partition and reboots.

    bit CtsProtoTestCases
      Builds this CTS apk, adb installs it, but does not run any
      tests.

  Running Unit Tests
  ------------------
  To run a unit test, list the test class names and optionally the
  test method after the module.

  For example:
    bit CtsProtoTestCases:*
      Builds this CTS apk, adb installs it, and runs all the tests
      contained in that apk.

    bit framework CtsProtoTestCases:*
      Builds the framework and the apk, syncs and reboots, then
      adb installs CtsProtoTestCases.apk, and runs all tests
      contained in that apk.

    bit CtsProtoTestCases:.ProtoOutputStreamBoolTest
    bit CtsProtoTestCases:android.util.proto.cts.ProtoOutputStreamBoolTest
      Builds and installs CtsProtoTestCases.apk, and runs all the
      tests in the ProtoOutputStreamBoolTest class.

    bit CtsProtoTestCases:.ProtoOutputStreamBoolTest\#testWrite
      Builds and installs CtsProtoTestCases.apk, and runs the testWrite
      test method on that class.

    bit CtsProtoTestCases:.ProtoOutputStreamBoolTest\#testWrite,.ProtoOutputStreamBoolTest\#testRepeated
      Builds and installs CtsProtoTestCases.apk, and runs the testWrite
      and testRepeated test methods on that class.

  Launching an Activity
  ---------------------
  To launch an activity, specify the activity class name after
  the module name.

  For example:
    bit StatusBarTest:NotificationBuilderTest
    bit StatusBarTest:.NotificationBuilderTest
    bit StatusBarTest:com.android.statusbartest.NotificationBuilderTest
      Builds and installs StatusBarTest.apk, launches the
      com.android.statusbartest/.NotificationBuilderTest activity.

Change-Id: I9cff7a23852fa1a67369e7807f7ae9f6e45d6131
Test: none
diff --git a/tools/bit/Android.mk b/tools/bit/Android.mk
new file mode 100644
index 0000000..7f691a0
--- /dev/null
+++ b/tools/bit/Android.mk
@@ -0,0 +1,43 @@
+#
+# 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.
+#
+LOCAL_PATH:= $(call my-dir)
+
+# ==========================================================
+# Build the host executable: protoc-gen-javastream
+# ==========================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := bit
+
+LOCAL_SRC_FILES := \
+    aapt.cpp \
+    adb.cpp \
+    command.cpp \
+    main.cpp \
+    make.cpp \
+    print.cpp \
+    util.cpp
+
+LOCAL_STATIC_LIBRARIES := \
+    libexpat \
+    libinstrumentation \
+    libjsoncpp
+
+LOCAL_SHARED_LIBRARIES := \
+    libprotobuf-cpp-full
+
+include $(BUILD_HOST_EXECUTABLE)
+
diff --git a/tools/bit/aapt.cpp b/tools/bit/aapt.cpp
new file mode 100644
index 0000000..961b47c
--- /dev/null
+++ b/tools/bit/aapt.cpp
@@ -0,0 +1,270 @@
+/*
+ * 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 "aapt.h"
+
+#include "command.h"
+#include "print.h"
+#include "util.h"
+
+#include <regex>
+
+const regex NS_REGEX("( *)N: ([^=]+)=(.*)");
+const regex ELEMENT_REGEX("( *)E: ([^ ]+) \\(line=(\\d+)\\)");
+const regex ATTR_REGEX("( *)A: ([^\\(=]+)[^=]*=\"([^\"]+)\".*");
+
+const string ANDROID_NS("http://schemas.android.com/apk/res/android");
+
+bool
+Apk::HasActivity(const string& className)
+{
+    string fullClassName = full_class_name(package, className);
+    const size_t N = activities.size();
+    for (size_t i=0; i<N; i++) {
+        if (activities[i] == fullClassName) {
+            return true;
+        }
+    }
+    return false;
+}
+
+struct Attribute {
+    string ns;
+    string name;
+    string value;
+};
+
+struct Element {
+    Element* parent;
+    string ns;
+    string name;
+    int lineno;
+    vector<Attribute> attributes;
+    vector<Element*> children;
+
+    /**
+     * Indentation in the xmltree dump. Might not be equal to the distance
+     * from the root because namespace rows (scopes) have their own indentation.
+     */
+    int depth;
+
+    Element();
+    ~Element();
+
+    string GetAttr(const string& ns, const string& name) const;
+    void FindElements(const string& ns, const string& name, vector<Element*>* result, bool recurse);
+    
+};
+
+Element::Element()
+{
+}
+
+Element::~Element()
+{
+    const size_t N = children.size();
+    for (size_t i=0; i<N; i++) {
+        delete children[i];
+    }
+}
+
+string
+Element::GetAttr(const string& ns, const string& name) const
+{
+    const size_t N = attributes.size();
+    for (size_t i=0; i<N; i++) {
+        const Attribute& attr = attributes[i];
+        if (attr.ns == ns && attr.name == name) {
+            return attr.value;
+        }
+    }
+    return string();
+}
+
+void
+Element::FindElements(const string& ns, const string& name, vector<Element*>* result, bool recurse)
+{
+    const size_t N = children.size();
+    for (size_t i=0; i<N; i++) {
+        Element* child = children[i];
+        if (child->ns == ns && child->name == name) {
+            result->push_back(child);
+        }
+        if (recurse) {
+            child->FindElements(ns, name, result, recurse);
+        }
+    }
+}
+
+struct Scope {
+    Scope* parent;
+    int depth;
+    map<string,string> namespaces;
+
+    Scope(Scope* parent, int depth);
+};
+
+Scope::Scope(Scope* p, int d)
+    :parent(p),
+     depth(d)
+{
+     if (p != NULL) {
+         namespaces = p->namespaces;
+     }
+}
+
+
+string
+full_class_name(const string& packageName, const string& className)
+{
+    if (className.length() == 0) {
+        return "";
+    }
+    if (className[0] == '.') {
+        return packageName + className;
+    }
+    if (className.find('.') == string::npos) {
+        return packageName + "." + className;
+    }
+    return className;
+}
+
+string
+pretty_component_name(const string& packageName, const string& className)
+{
+    if (starts_with(packageName, className)) {
+        size_t pn = packageName.length();
+        size_t cn = className.length();
+        if (cn > pn && className[pn] == '.') {
+            return packageName + "/" + string(className, pn, string::npos);
+        }
+    }
+    return packageName + "/" + className;
+}
+
+int
+inspect_apk(Apk* apk, const string& filename)
+{
+    // Load the manifest xml
+    Command cmd("aapt");
+    cmd.AddArg("dump");
+    cmd.AddArg("xmltree");
+    cmd.AddArg(filename);
+    cmd.AddArg("AndroidManifest.xml");
+
+    int err;
+
+    string output = get_command_output(cmd, &err, false);
+    check_error(err);
+
+    // Parse the manifest xml
+    Scope* scope = new Scope(NULL, -1);
+    Element* root = NULL;
+    Element* current = NULL;
+    vector<string> lines;
+    split_lines(&lines, output);
+    for (size_t i=0; i<lines.size(); i++) {
+        const string& line = lines[i];
+        smatch match;
+        if (regex_match(line, match, NS_REGEX)) {
+            int depth = match[1].length() / 2;
+            while (depth < scope->depth) {
+                Scope* tmp = scope;
+                scope = scope->parent;
+                delete tmp;
+            }
+            scope = new Scope(scope, depth);
+            scope->namespaces[match[2]] = match[3];
+        } else if (regex_match(line, match, ELEMENT_REGEX)) {
+            Element* element = new Element();
+
+            string str = match[2];
+            size_t colon = str.find(':');
+            if (colon == string::npos) {
+                element->name = str;
+            } else {
+                element->ns = scope->namespaces[string(str, 0, colon)];
+                element->name.assign(str, colon+1, string::npos);
+            }
+            element->lineno = atoi(match[3].str().c_str());
+            element->depth = match[1].length() / 2;
+
+            if (root == NULL) {
+                current = element;
+                root = element;
+            } else {
+                while (element->depth <= current->depth && current->parent != NULL) {
+                    current = current->parent;
+                }
+                element->parent = current;
+                current->children.push_back(element);
+                current = element;
+            }
+        } else if (regex_match(line, match, ATTR_REGEX)) {
+            if (current != NULL) {
+                Attribute attr;
+                string str = match[2];
+                size_t colon = str.find(':');
+                if (colon == string::npos) {
+                    attr.name = str;
+                } else {
+                    attr.ns = scope->namespaces[string(str, 0, colon)];
+                    attr.name.assign(str, colon+1, string::npos);
+                }
+                attr.value = match[3];
+                current->attributes.push_back(attr);
+            }
+        }
+    }
+    while (scope != NULL) {
+        Scope* tmp = scope;
+        scope = scope->parent;
+        delete tmp;
+    }
+
+    // Package name
+    apk->package = root->GetAttr("", "package");
+    if (apk->package.size() == 0) {
+        print_error("%s:%d: Manifest root element doesn't contain a package attribute",
+                filename.c_str(), root->lineno);
+        delete root;
+        return 1;
+    }
+
+    // Instrumentation runner
+    vector<Element*> instrumentation;
+    root->FindElements("", "instrumentation", &instrumentation, true);
+    if (instrumentation.size() > 0) {
+        // TODO: How could we deal with multiple instrumentation tags?
+        // We'll just pick the first one.
+        apk->runner = instrumentation[0]->GetAttr(ANDROID_NS, "name");
+    }
+
+    // Activities
+    vector<Element*> activities;
+    root->FindElements("", "activity", &activities, true);
+    for (size_t i=0; i<activities.size(); i++) {
+        string name = activities[i]->GetAttr(ANDROID_NS, "name");
+        if (name.size() == 0) {
+            continue;
+        }
+        apk->activities.push_back(full_class_name(apk->package, name));
+    }
+
+    delete root;
+    return 0;
+}
+
diff --git a/tools/bit/aapt.h b/tools/bit/aapt.h
new file mode 100644
index 0000000..6aeb03f
--- /dev/null
+++ b/tools/bit/aapt.h
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+#ifndef AAPT_H
+#define AAPT_H
+
+#include <string>
+#include <vector>
+
+using namespace std;
+
+struct Apk
+{
+    string package;
+    string runner;
+    vector<string> activities;
+
+    bool HasActivity(const string& className);
+};
+
+string full_class_name(const string& packageName, const string& className);
+string pretty_component_name(const string& packageName, const string& className);
+
+int inspect_apk(Apk* apk, const string& filename);
+
+#endif // AAPT_H
diff --git a/tools/bit/adb.cpp b/tools/bit/adb.cpp
new file mode 100644
index 0000000..eb96dae
--- /dev/null
+++ b/tools/bit/adb.cpp
@@ -0,0 +1,463 @@
+/*
+ * 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("shell");
+    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;
+    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("shell");
+    cmd.AddArg("am");
+    cmd.AddArg("instrument");
+    cmd.AddArg("-w");
+    cmd.AddArg("-m");
+    if (className.length() > 0) {
+        cmd.AddArg("-e");
+        cmd.AddArg("class");
+        cmd.AddArg(className);
+    }
+    cmd.AddArg(packageName + "/" + runner);
+
+    print_command(cmd);
+
+    int fds[2];
+    pipe(fds);
+
+    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();
+        execvpe(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;
+}
+
diff --git a/tools/bit/adb.h b/tools/bit/adb.h
new file mode 100644
index 0000000..dca80c8
--- /dev/null
+++ b/tools/bit/adb.h
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+#ifndef ADB_H
+#define ADB_H
+
+#include "instrumentation_data.pb.h"
+
+#include <string>
+
+using namespace android::am;
+using namespace google::protobuf;
+using namespace std;
+
+class InstrumentationCallbacks {
+public:
+    virtual void OnTestStatus(TestStatus& status) = 0;
+    virtual void OnSessionStatus(SessionStatus& status) = 0;
+};
+
+int run_adb(const char* first, ...);
+
+string get_system_property(const string& name, int* err);
+
+int run_instrumentation_test(const string& packageName, const string& runner,
+        const string& className, InstrumentationCallbacks* callbacks);
+
+string get_bundle_string(const ResultsBundle& bundle, bool* found, ...);
+int32_t get_bundle_int(const ResultsBundle& bundle, bool* found, ...);
+float get_bundle_float(const ResultsBundle& bundle, bool* found, ...);
+double get_bundle_double(const ResultsBundle& bundle, bool* found, ...);
+int64_t get_bundle_long(const ResultsBundle& bundle, bool* found, ...);
+
+#endif // ADB_H
diff --git a/tools/bit/command.cpp b/tools/bit/command.cpp
new file mode 100644
index 0000000..c5c12b4
--- /dev/null
+++ b/tools/bit/command.cpp
@@ -0,0 +1,183 @@
+/*
+ * 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 "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>
+
+Command::Command(const string& prog)
+    :prog(prog)
+{
+}
+
+Command::~Command()
+{
+}
+
+void
+Command::AddArg(const string& arg)
+{
+    args.push_back(arg);
+}
+
+void
+Command::AddEnv(const string& name, const string& value)
+{
+    env[name] = value;
+}
+
+const char*
+Command::GetProg() const
+{
+    return prog.c_str();
+}
+
+char *const *
+Command::GetArgv() const
+{
+    const int N = args.size();
+    char** result = (char**)malloc(sizeof(char*)*(N+2));
+    result[0] = strdup(prog.c_str());
+    for (int i=0; i<N; i++) {
+        result[i+1] = strdup(args[i].c_str());
+    }
+    result[N+1] = 0;
+    return result;
+}
+
+char *const *
+Command::GetEnv() const
+{
+    map<string,string> copy;
+    for (const char** p=(const char**)environ; *p != NULL; p++) {
+        char* name = strdup(*p);
+        char* value = strchr(name, '=');
+        *value = '\0';
+        value++;
+        copy[name] = value;
+        free(name);
+    }
+    for (map<string,string>::const_iterator it=env.begin(); it!=env.end(); it++) {
+        copy[it->first] = it->second;
+    }
+    char** result = (char**)malloc(sizeof(char*)*(copy.size()+1));
+    char** row = result;
+    for (map<string,string>::const_iterator it=copy.begin(); it!=copy.end(); it++) {
+        *row = (char*)malloc(it->first.size() + it->second.size() + 2);
+        strcpy(*row, it->first.c_str());
+        strcat(*row, "=");
+        strcat(*row, it->second.c_str());
+        row++;
+    }
+    *row = NULL;
+    return result;
+}
+
+string
+get_command_output(const Command& command, int* err, bool quiet)
+{
+    if (!quiet) {
+        print_command(command);
+    }
+
+    int fds[2];
+    pipe(fds);
+
+    pid_t pid = fork();
+
+    if (pid == -1) {
+        // fork error
+        *err = errno;
+        return string();
+    } else if (pid == 0) {
+        // child
+        while ((dup2(fds[1], STDOUT_FILENO) == -1) && (errno == EINTR)) {}
+        close(fds[1]);
+        close(fds[0]);
+        const char* prog = command.GetProg();
+        char* const* argv = command.GetArgv();
+        char* const* env = command.GetEnv();
+        execvpe(prog, argv, env);
+        if (!quiet) {
+            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);
+        while (true) {
+            ssize_t amt = read(fds[0], buf, size);
+            if (amt <= 0) {
+                break;
+            } else if (amt > 0) {
+                result.append(buf, amt);
+            }
+        }
+        free(buf);
+        int status;
+        waitpid(pid, &status, 0);
+        if (WIFEXITED(status)) {
+            *err = WEXITSTATUS(status);
+            return result;
+        } else {
+            *err = -1;
+            return string();
+        }
+    }
+}
+
+
+int
+run_command(const Command& command)
+{
+    print_command(command);
+
+    pid_t pid = fork();
+
+    if (pid == -1) {
+        // fork error
+        return errno;
+    } else if (pid == 0) {
+        // child
+        const char* prog = command.GetProg();
+        char* const* argv = command.GetArgv();
+        char* const* env = command.GetEnv();
+        execvpe(prog, argv, env);
+        print_error("Unable to run command: %s", prog);
+        exit(1);
+    } else {
+        // parent
+        int status;
+        waitpid(pid, &status, 0);
+        if (WIFEXITED(status)) {
+            return WEXITSTATUS(status);
+        } else {
+            return -1;
+        }
+    }
+}
+
diff --git a/tools/bit/command.h b/tools/bit/command.h
new file mode 100644
index 0000000..eb0b88f
--- /dev/null
+++ b/tools/bit/command.h
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+
+#ifndef COMMAND_H
+#define COMMAND_H
+
+#include <map>
+#include <string>
+#include <vector>
+
+using namespace std;
+
+struct Command
+{
+    Command(const string& prog);
+    ~Command();
+
+    void AddArg(const string& arg);
+    void AddEnv(const string& name, const string& value);
+
+    const char* GetProg() const;
+    char* const* GetArgv() const;
+    char* const* GetEnv() const;
+
+    string GetCommandline() const;
+
+    string prog;
+    vector<string> args;
+    map<string,string> env;
+};
+
+/**
+ * Run the command and collect stdout.
+ * Returns the exit code.
+ */
+string get_command_output(const Command& command, int* err, bool quiet=false);
+
+/**
+ * Run the command.
+ * Returns the exit code.
+ */
+int run_command(const Command& command);
+
+#endif // COMMAND_H
+
diff --git a/tools/bit/main.cpp b/tools/bit/main.cpp
new file mode 100644
index 0000000..04836ad
--- /dev/null
+++ b/tools/bit/main.cpp
@@ -0,0 +1,981 @@
+/*
+ * 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 "aapt.h"
+#include "adb.h"
+#include "make.h"
+#include "print.h"
+#include "util.h"
+
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <google/protobuf/stubs/common.h>
+
+using namespace std;
+
+/**
+ * An entry from the command line for something that will be built, installed,
+ * and/or tested.
+ */
+struct Target {
+    bool build;
+    bool install;
+    bool test;
+    string pattern;
+    string name;
+    vector<string> actions;
+    Module module;
+
+    int testActionCount;
+
+    int testPassCount;
+    int testFailCount;
+    bool actionsWithNoTests;
+
+    Target(bool b, bool i, bool t, const string& p);
+};
+
+Target::Target(bool b, bool i, bool t, const string& p)
+    :build(b),
+     install(i),
+     test(t),
+     pattern(p),
+     testActionCount(0),
+     testPassCount(0),
+     testFailCount(0),
+     actionsWithNoTests(false)
+{
+}
+
+/**
+ * Command line options.
+ */
+struct Options {
+    // For help
+    bool runHelp;
+
+    // For tab completion
+    bool runTab;
+    string tabPattern;
+
+    // For build/install/test
+    bool reboot;
+    vector<Target*> targets;
+
+    Options();
+    ~Options();
+};
+
+Options::Options()
+    :runHelp(false),
+     runTab(false),
+     reboot(false),
+     targets()
+{
+}
+
+Options::~Options()
+{
+}
+
+struct InstallApk
+{
+    TrackedFile file;
+    bool alwaysInstall;
+    bool installed;
+
+    InstallApk();
+    InstallApk(const InstallApk& that);
+    InstallApk(const string& filename, bool always);
+    ~InstallApk() {};
+};
+
+InstallApk::InstallApk()
+{
+}
+
+InstallApk::InstallApk(const InstallApk& that)
+    :file(that.file),
+     alwaysInstall(that.alwaysInstall),
+     installed(that.installed)
+{
+}
+
+InstallApk::InstallApk(const string& filename, bool always)
+    :file(filename),
+     alwaysInstall(always),
+     installed(false)
+{
+}
+
+
+/**
+ * Record for an test that is going to be launched.
+ */
+struct TestAction {
+    TestAction();
+
+    // The package name from the apk
+    string packageName;
+
+    // The test runner class
+    string runner;
+
+    // The test class, or none if all tests should be run
+    string className;
+
+    // The original target that requested this action
+    Target* target;
+
+    // The number of tests that passed
+    int passCount;
+
+    // The number of tests that failed
+    int failCount;
+};
+
+TestAction::TestAction()
+    :passCount(0),
+     failCount(0)
+{
+}
+
+/**
+ * Record for an activity that is going to be launched.
+ */
+struct ActivityAction {
+    // The package name from the apk
+    string packageName;
+
+    // The test class, or none if all tests should be run
+    string className;
+};
+
+/**
+ * Callback class for the am instrument command.
+ */
+class TestResults: public InstrumentationCallbacks
+{
+public:
+    virtual void OnTestStatus(TestStatus& status);
+    virtual void OnSessionStatus(SessionStatus& status);
+
+    /**
+     * Set the TestAction that the tests are for.
+     * It will be updated with statistics as the tests run.
+     */
+    void SetCurrentAction(TestAction* action);
+
+private:
+    TestAction* m_currentAction;
+};
+
+void
+TestResults::OnTestStatus(TestStatus& status)
+{
+    bool found;
+//    printf("OnTestStatus\n");
+//    status.PrintDebugString();
+    int32_t resultCode = status.has_results() ? status.result_code() : 0;
+
+    if (!status.has_results()) {
+        return;
+    }
+    const ResultsBundle &results = status.results();
+
+    int32_t currentTestNum = get_bundle_int(results, &found, "current", NULL);
+    if (!found) {
+        currentTestNum = -1;
+    }
+
+    int32_t testCount = get_bundle_int(results, &found, "numtests", NULL);
+    if (!found) {
+        testCount = -1;
+    }
+
+    string className = get_bundle_string(results, &found, "class", NULL);
+    if (!found) {
+        return;
+    }
+
+    string testName = get_bundle_string(results, &found, "test", NULL);
+    if (!found) {
+        return;
+    }
+
+    if (resultCode == 0) {
+        // test passed
+        m_currentAction->passCount++;
+        m_currentAction->target->testPassCount++;
+    } else if (resultCode == 1) {
+        // test starting
+        ostringstream line;
+        line << "Running";
+        if (currentTestNum > 0) {
+            line << ": " << currentTestNum;
+            if (testCount > 0) {
+                line << " of " << testCount;
+            }
+        }
+        line << ": " << m_currentAction->target->name << ':' << className << "\\#" << testName;
+        print_one_line("%s", line.str().c_str());
+    } else if (resultCode == -2) {
+        // test failed
+        m_currentAction->failCount++;
+        m_currentAction->target->testFailCount++;
+        printf("%s\n%sFailed: %s:%s\\#%s%s\n", g_escapeClearLine, g_escapeRedBold,
+                m_currentAction->target->name.c_str(), className.c_str(),
+                testName.c_str(), g_escapeEndColor);
+
+        string stack = get_bundle_string(results, &found, "stack", NULL);
+        if (found) {
+            printf("%s\n", stack.c_str());
+        }
+    }
+}
+
+void
+TestResults::OnSessionStatus(SessionStatus& /*status*/)
+{
+    //status.PrintDebugString();
+}
+
+void
+TestResults::SetCurrentAction(TestAction* action)
+{
+    m_currentAction = action;
+}
+
+/**
+ * Prints the usage statement / help text.
+ */
+static void
+print_usage(FILE* out) {
+    fprintf(out, "usage: bit OPTIONS PATTERN\n");
+    fprintf(out, "\n");
+    fprintf(out, "  Build, sync and test android code.\n");
+    fprintf(out, "\n");
+    fprintf(out, "  The -b -i and -t options allow you to specify which phases\n");
+    fprintf(out, "  you want to run. If none of those options are given, then\n");
+    fprintf(out, "  all phases are run. If any of these options are provided\n");
+    fprintf(out, "  then only the listed phases are run.\n");
+    fprintf(out, "\n");
+    fprintf(out, "  OPTIONS\n");
+    fprintf(out, "  -b     Run a build\n");
+    fprintf(out, "  -i     Install the targets\n");
+    fprintf(out, "  -t     Run the tests\n");
+    fprintf(out, "\n");
+    fprintf(out, "  -r     If the runtime needs to be restarted, do a full reboot\n");
+    fprintf(out, "         instead\n");
+    fprintf(out, "\n");
+    fprintf(out, "  PATTERN\n");
+    fprintf(out, "  One or more targets to build, install and test. The target\n");
+    fprintf(out, "  names are the names that appear in the LOCAL_MODULE or\n");
+    fprintf(out, "  LOCAL_PACKAGE_NAME variables in Android.mk or Android.bp files.\n");
+    fprintf(out, "\n");
+    fprintf(out, "  Building and installing\n");
+    fprintf(out, "  -----------------------\n");
+    fprintf(out, "  The modules specified will be built and then installed. If the\n");
+    fprintf(out, "  files are on the system partition, they will be synced and the\n");
+    fprintf(out, "  attached device rebooted. If they are APKs that aren't on the\n");
+    fprintf(out, "  system partition they are installed with adb install.\n");
+    fprintf(out, "\n");
+    fprintf(out, "  For example:\n");
+    fprintf(out, "    bit framework\n");
+    fprintf(out, "      Builds framework.jar, syncs the system partition and reboots.\n");
+    fprintf(out, "\n");
+    fprintf(out, "    bit SystemUI\n");
+    fprintf(out, "      Builds SystemUI.apk, syncs the system partition and reboots.\n");
+    fprintf(out, "\n");
+    fprintf(out, "    bit CtsProtoTestCases\n");
+    fprintf(out, "      Builds this CTS apk, adb installs it, but does not run any\n");
+    fprintf(out, "      tests.\n");
+    fprintf(out, "\n");
+    fprintf(out, "  Running Unit Tests\n");
+    fprintf(out, "  ------------------\n");
+    fprintf(out, "  To run a unit test, list the test class names and optionally the\n");
+    fprintf(out, "  test method after the module.\n");
+    fprintf(out, "\n");
+    fprintf(out, "  For example:\n");
+    fprintf(out, "    bit CtsProtoTestCases:*\n");
+    fprintf(out, "      Builds this CTS apk, adb installs it, and runs all the tests\n");
+    fprintf(out, "      contained in that apk.\n");
+    fprintf(out, "\n");
+    fprintf(out, "    bit framework CtsProtoTestCases:*\n");
+    fprintf(out, "      Builds the framework and the apk, syncs and reboots, then\n");
+    fprintf(out, "      adb installs CtsProtoTestCases.apk, and runs all tests \n");
+    fprintf(out, "      contained in that apk.\n");
+    fprintf(out, "\n");
+    fprintf(out, "    bit CtsProtoTestCases:.ProtoOutputStreamBoolTest\n");
+    fprintf(out, "    bit CtsProtoTestCases:android.util.proto.cts.ProtoOutputStreamBoolTest\n");
+    fprintf(out, "      Builds and installs CtsProtoTestCases.apk, and runs all the\n");
+    fprintf(out, "      tests in the ProtoOutputStreamBoolTest class.\n");
+    fprintf(out, "\n");
+    fprintf(out, "    bit CtsProtoTestCases:.ProtoOutputStreamBoolTest\\#testWrite\n");
+    fprintf(out, "      Builds and installs CtsProtoTestCases.apk, and runs the testWrite\n");
+    fprintf(out, "      test method on that class.\n");
+    fprintf(out, "\n");
+    fprintf(out, "    bit CtsProtoTestCases:.ProtoOutputStreamBoolTest\\#testWrite,.ProtoOutputStreamBoolTest\\#testRepeated\n");
+    fprintf(out, "      Builds and installs CtsProtoTestCases.apk, and runs the testWrite\n");
+    fprintf(out, "      and testRepeated test methods on that class.\n");
+    fprintf(out, "\n");
+    fprintf(out, "  Launching an Activity\n");
+    fprintf(out, "  ---------------------\n");
+    fprintf(out, "  To launch an activity, specify the activity class name after\n");
+    fprintf(out, "  the module name.\n");
+    fprintf(out, "\n");
+    fprintf(out, "  For example:\n");
+    fprintf(out, "    bit StatusBarTest:NotificationBuilderTest\n");
+    fprintf(out, "    bit StatusBarTest:.NotificationBuilderTest\n");
+    fprintf(out, "    bit StatusBarTest:com.android.statusbartest.NotificationBuilderTest\n");
+    fprintf(out, "      Builds and installs StatusBarTest.apk, launches the\n");
+    fprintf(out, "      com.android.statusbartest/.NotificationBuilderTest activity.\n");
+    fprintf(out, "\n");
+    fprintf(out, "\n");
+    fprintf(out, "usage: bit --tab ...\n");
+    fprintf(out, "\n");
+    fprintf(out, "  Lists the targets in a format for tab completion. To get tab\n");
+    fprintf(out, "  completion, add this to your bash environment:\n");
+    fprintf(out, "\n");
+    fprintf(out, "     complete -C \"bit --tab\" bit\n");
+    fprintf(out, "\n");
+    fprintf(out, "  Sourcing android's build/envsetup.sh will do this for you\n");
+    fprintf(out, "  automatically.\n");
+    fprintf(out, "\n");
+    fprintf(out, "\n");
+    fprintf(out, "usage: bit --help\n");
+    fprintf(out, "usage: bit -h\n");
+    fprintf(out, "\n");
+    fprintf(out, "  Print this help message\n");
+    fprintf(out, "\n");
+}
+
+
+/**
+ * Sets the appropriate flag* variables. If there is a problem with the
+ * commandline arguments, prints the help message and exits with an error.
+ */
+static void
+parse_args(Options* options, int argc, const char** argv)
+{
+    // Help
+    if (argc == 2 && (strcmp(argv[1],  "-h") == 0 || strcmp(argv[1], "--help") == 0)) {
+        options->runHelp = true;
+        return;
+    }
+
+    // Tab
+    if (argc >= 4 && strcmp(argv[1], "--tab") == 0) {
+        options->runTab = true;
+        options->tabPattern = argv[3];
+        return;
+    }
+
+    // Normal usage
+    bool anyPhases = false;
+    bool gotPattern = false;
+    bool flagBuild = false;
+    bool flagInstall = false;
+    bool flagTest = false;
+    for (int i=1; i < argc; i++) {
+        string arg(argv[i]);
+        if (arg[0] == '-') {
+            for (size_t j=1; j<arg.size(); j++) {
+                switch (arg[j]) {
+                    case '-':
+                        break;
+                    case 'b':
+                        if (gotPattern) {
+                            gotPattern = false;
+                            flagInstall = false;
+                            flagTest = false;
+                        }
+                        flagBuild = true;
+                        anyPhases = true;
+                        break;
+                    case 'i':
+                        if (gotPattern) {
+                            gotPattern = false;
+                            flagBuild = false;
+                            flagTest = false;
+                        }
+                        flagInstall = true;
+                        anyPhases = true;
+                        break;
+                    case 't':
+                        if (gotPattern) {
+                            gotPattern = false;
+                            flagBuild = false;
+                            flagInstall = false;
+                        }
+                        flagTest = true;
+                        anyPhases = true;
+                        break;
+                    case 'r':
+                        options->reboot = true;
+                        break;
+                    default:
+                        fprintf(stderr, "Unrecognized option '%c'\n", arg[j]);
+                        print_usage(stderr);
+                        exit(1);
+                        break;
+                }
+            }
+        } else {
+            Target* target = new Target(flagBuild || !anyPhases, flagInstall || !anyPhases,
+                    flagTest || !anyPhases, arg);
+            size_t colonPos = arg.find(':');
+            if (colonPos == 0) {
+                fprintf(stderr, "Test / activity supplied without a module to build: %s\n",
+                        arg.c_str());
+                print_usage(stderr);
+                exit(1);
+            } else if (colonPos == string::npos) {
+                target->name = arg;
+            } else {
+                target->name.assign(arg, 0, colonPos);
+                size_t beginPos = colonPos+1;
+                size_t commaPos;
+                while (true) {
+                    commaPos = arg.find(',', beginPos);
+                    if (commaPos == string::npos) {
+                        if (beginPos != arg.size()) {
+                            target->actions.push_back(string(arg, beginPos, commaPos));
+                        }
+                        break;
+                    } else {
+                        if (commaPos != beginPos) {
+                            target->actions.push_back(string(arg, beginPos, commaPos-beginPos));
+                        }
+                        beginPos = commaPos+1;
+                    }
+                }
+            }
+            options->targets.push_back(target);
+            gotPattern = true;
+        }
+    }
+    // If no pattern was supplied, give an error
+    if (options->targets.size() == 0) {
+        fprintf(stderr, "No PATTERN supplied.\n\n");
+        print_usage(stderr);
+        exit(1);
+    }
+}
+
+/**
+ * Get an environment variable.
+ * Exits with an error if it is unset or the empty string.
+ */
+static string
+get_required_env(const char* name, bool quiet)
+{
+    const char* value = getenv(name);
+    if (value == NULL || value[0] == '\0') {
+        if (!quiet) {
+            fprintf(stderr, "%s not set. Did you source build/envsetup.sh,"
+                    " run lunch and do a build?\n", name);
+        }
+        exit(1);
+    }
+    return string(value);
+}
+
+/**
+ * Get the out directory.
+ *
+ * This duplicates the logic in build/make/core/envsetup.mk (which hasn't changed since 2011)
+ * so that we don't have to wait for get_build_var make invocation.
+ */
+string
+get_out_dir()
+{
+    const char* out_dir = getenv("OUT_DIR");
+    if (out_dir == NULL || out_dir[0] == '\0') {
+        const char* common_base = getenv("OUT_DIR_COMMON_BASE");
+        if (common_base == NULL || common_base[0] == '\0') {
+            // We don't prefix with buildTop because we cd there and it
+            // makes all the filenames long when being pretty printed.
+            return "out";
+        } else {
+            char* pwd = get_current_dir_name();
+            const char* slash = strrchr(pwd, '/');
+            if (slash == NULL) {
+                slash = "";
+            }
+            string result(common_base);
+            result += slash;
+            free(pwd);
+            return result;
+        }
+    }
+    return string(out_dir);
+}
+
+/**
+ * Check that a system property on the device matches the expected value.
+ * Exits with an error if they don't.
+ */
+static void
+check_device_property(const string& property, const string& expected)
+{
+    int err;
+    string deviceValue = get_system_property(property, &err);
+    check_error(err);
+    if (deviceValue != expected) {
+        print_error("There is a mismatch between the build you just did and the device you");
+        print_error("are trying to sync it to in the %s system property", property.c_str());
+        print_error("   build:  %s", expected.c_str());
+        print_error("   device: %s", deviceValue.c_str());
+        exit(1);
+    }
+}
+
+/**
+ * Run the build, install, and test actions.
+ */
+void
+run_phases(vector<Target*> targets, bool reboot)
+{
+    int err = 0;
+
+    //
+    // Initialization
+    //
+
+    print_status("Initializing");
+
+    const string buildTop = get_required_env("ANDROID_BUILD_TOP", false);
+    const string buildProduct = get_required_env("TARGET_PRODUCT", false);
+    const string buildVariant = get_required_env("TARGET_BUILD_VARIANT", false);
+    const string buildType = get_required_env("TARGET_BUILD_TYPE", false);
+    const string buildDevice = get_build_var(buildTop, "TARGET_DEVICE", false);
+    const string buildId = get_build_var(buildTop, "BUILD_ID", false);
+    const string buildOut = get_out_dir();
+
+    // TODO: print_command("cd", buildTop.c_str());
+    chdir(buildTop.c_str());
+
+    // Get the modules for the targets
+    map<string,Module> modules;
+    read_modules(buildOut, buildDevice, &modules, false);
+    for (size_t i=0; i<targets.size(); i++) {
+        Target* target = targets[i];
+        map<string,Module>::iterator mod = modules.find(target->name);
+        if (mod != modules.end()) {
+            target->module = mod->second;
+        } else {
+            print_error("Error: Could not find module: %s", target->name.c_str());
+            err = 1;
+        }
+    }
+    if (err != 0) {
+        exit(1);
+    }
+
+    // Choose the goals
+    vector<string> goals;
+    for (size_t i=0; i<targets.size(); i++) {
+        Target* target = targets[i];
+        if (target->build) {
+            goals.push_back(target->name);
+        }
+    }
+
+    // Figure out whether we need to sync the system and which apks to install
+    string systemPath = buildOut + "/target/product/" + buildDevice + "/system/";
+    string dataPath = buildOut + "/target/product/" + buildDevice + "/data/";
+    bool syncSystem = false;
+    bool alwaysSyncSystem = false;
+    vector<InstallApk> installApks;
+    for (size_t i=0; i<targets.size(); i++) {
+        Target* target = targets[i];
+        if (target->install) {
+            for (size_t j=0; j<target->module.installed.size(); j++) {
+                const string& file = target->module.installed[j];
+                // System partition
+                if (starts_with(file, systemPath)) {
+                    syncSystem = true;
+                    if (!target->build) {
+                        // If a system partition target didn't get built then
+                        // it won't change we will always need to do adb sync
+                        alwaysSyncSystem = true;
+                    }
+                    continue;
+                }
+                // Apk in the data partition
+                if (starts_with(file, dataPath) && ends_with(file, ".apk")) {
+                    // Always install it if we didn't build it because otherwise
+                    // it will never have changed.
+                    installApks.push_back(InstallApk(file, !target->build));
+                    continue;
+                }
+            }
+        }
+    }
+    map<string,FileInfo> systemFilesBefore;
+    if (syncSystem && !alwaysSyncSystem) {
+        get_directory_contents(systemPath, &systemFilesBefore);
+    }
+
+    //
+    // Build
+    //
+
+    // Run the build
+    if (goals.size() > 0) {
+        print_status("Building");
+        err = build_goals(goals);
+        check_error(err);
+    }
+
+    //
+    // Install
+    //
+
+    // Sync the system partition and reboot
+    bool skipSync = false;
+    if (syncSystem) {
+        print_status("Syncing /system");
+
+        if (!alwaysSyncSystem) {
+            // If nothing changed and we weren't forced to sync, skip the reboot for speed.
+            map<string,FileInfo> systemFilesAfter;
+            get_directory_contents(systemPath, &systemFilesAfter);
+            skipSync = !directory_contents_differ(systemFilesBefore, systemFilesAfter);
+        }
+        if (skipSync) {
+            printf("Skipping sync because no files changed.\n");
+        } else {
+            // Do some sanity checks
+            check_device_property("ro.build.product", buildProduct);
+            check_device_property("ro.build.type", buildVariant);
+            check_device_property("ro.build.id", buildId);
+
+            // Stop & Sync
+            err = run_adb("shell", "stop", NULL);
+            check_error(err);
+            err = run_adb("remount", NULL);
+            check_error(err);
+            err = run_adb("sync", "system", NULL);
+            check_error(err);
+
+            if (reboot) {
+                print_status("Rebooting");
+
+                err = run_adb("reboot", NULL);
+                check_error(err);
+                err = run_adb("wait-for-device", NULL);
+                check_error(err);
+            } else {
+                print_status("Restarting the runtime");
+
+                err = run_adb("shell", "setprop", "sys.boot_completed", "0", NULL);
+                check_error(err);
+                err = run_adb("shell", "start", NULL);
+                check_error(err);
+            }
+
+            while (true) {
+                string completed = get_system_property("sys.boot_completed", &err);
+                check_error(err);
+                if (completed == "1") {
+                    break;
+                }
+                sleep(2);
+            }
+            sleep(1);
+            err = run_adb("shell", "wm", "dismiss-keyguard", NULL);
+            check_error(err);
+        }
+    }
+
+    // Install APKs
+    if (installApks.size() > 0) {
+        print_status("Installing APKs");
+        for (size_t i=0; i<installApks.size(); i++) {
+            InstallApk& apk = installApks[i];
+            if (!apk.file.fileInfo.exists || apk.file.HasChanged()) {
+                // It didn't exist before or it changed, so int needs install
+                err = run_adb("install", "-r", apk.file.filename.c_str(), NULL);
+                check_error(err);
+                apk.installed = true;
+            } else {
+                printf("APK didn't change. Skipping install of %s\n", apk.file.filename.c_str());
+            }
+        }
+    }
+
+    //
+    // Actions
+    //
+
+    // Inspect the apks, and figure out what is an activity and what needs a test runner
+    bool printedInspecting = false;
+    vector<TestAction> testActions;
+    vector<ActivityAction> activityActions;
+    for (size_t i=0; i<targets.size(); i++) {
+        Target* target = targets[i];
+        if (target->test) {
+            for (size_t j=0; j<target->module.installed.size(); j++) {
+                string filename = target->module.installed[j];
+
+                if (!ends_with(filename, ".apk")) {
+                    continue;
+                }
+
+                if (!printedInspecting) {
+                    printedInspecting = true;
+                    print_status("Inspecting APKs");
+                }
+
+                Apk apk;
+                err = inspect_apk(&apk, filename);
+                check_error(err);
+
+                for (size_t k=0; k<target->actions.size(); k++) {
+                    string actionString = target->actions[k];
+                    if (actionString == "*") {
+                        if (apk.runner.length() == 0) {
+                            print_error("Error: Test requested for apk that doesn't"
+                                    " have an <instrumentation> tag: %s\n",
+                                    target->module.name.c_str());
+                            exit(1);
+                        }
+                        TestAction action;
+                        action.packageName = apk.package;
+                        action.runner = apk.runner;
+                        action.target = target;
+                        testActions.push_back(action);
+                        target->testActionCount++;
+                    } else if (apk.HasActivity(actionString)) {
+                        ActivityAction action;
+                        action.packageName = apk.package;
+                        action.className = full_class_name(apk.package, actionString);
+                        activityActions.push_back(action);
+                    } else {
+                        if (apk.runner.length() == 0) {
+                            print_error("Error: Test requested for apk that doesn't"
+                                    " have an <instrumentation> tag: %s\n",
+                                    target->module.name.c_str());
+                            exit(1);
+                        }
+                        TestAction action;
+                        action.packageName = apk.package;
+                        action.runner = apk.runner;
+                        action.className = full_class_name(apk.package, actionString);
+                        action.target = target;
+                        testActions.push_back(action);
+                        target->testActionCount++;
+                    }
+                }
+            }
+        }
+    }
+
+    // Run the instrumentation tests
+    TestResults testResults;
+    if (testActions.size() > 0) {
+        print_status("Running tests");
+        for (size_t i=0; i<testActions.size(); i++) {
+            TestAction& action = testActions[i];
+            testResults.SetCurrentAction(&action);
+            err = run_instrumentation_test(action.packageName, action.runner, action.className,
+                    &testResults);
+            check_error(err);
+            if (action.passCount == 0 && action.failCount == 0) {
+                action.target->actionsWithNoTests = true;
+            }
+            int total = action.passCount + action.failCount;
+            printf("%sRan %d test%s for %s. ", g_escapeClearLine,
+                    total, total > 1 ? "s" : "", action.target->name.c_str());
+            if (action.passCount == 0 && action.failCount == 0) {
+                printf("%s%d passed, %d failed%s\n", g_escapeYellowBold, action.passCount,
+                        action.failCount, g_escapeEndColor);
+            } else if (action.failCount >  0) {
+                printf("%d passed, %s%d failed%s\n", action.passCount, g_escapeRedBold,
+                        action.failCount, g_escapeEndColor);
+            } else {
+                printf("%s%d passed%s, %d failed\n", g_escapeGreenBold, action.passCount,
+                        g_escapeEndColor, action.failCount);
+            }
+        }
+    }
+
+    // Launch the activity
+    if (activityActions.size() > 0) {
+        print_status("Starting activity");
+
+        if (activityActions.size() > 1) {
+            print_warning("Multiple activities specified.  Will only start the first one:");
+            for (size_t i=0; i<activityActions.size(); i++) {
+                ActivityAction& action = activityActions[i];
+                print_warning("   %s",
+                        pretty_component_name(action.packageName, action.className).c_str());
+            }
+        }
+
+        const ActivityAction& action = activityActions[0];
+        string componentName = action.packageName + "/" + action.className;
+        err = run_adb("shell", "am", "start", componentName.c_str(), NULL);
+        check_error(err);
+    }
+
+    //
+    // Print summary
+    //
+
+    printf("\n%s--------------------------------------------%s\n", g_escapeBold, g_escapeEndColor);
+
+    // Build
+    if (goals.size() > 0) {
+        printf("%sBuilt:%s\n", g_escapeBold, g_escapeEndColor);
+        for (size_t i=0; i<goals.size(); i++) {
+            printf("   %s\n", goals[i].c_str());
+        }
+    }
+
+    // Install
+    if (syncSystem) {
+        if (skipSync) {
+            printf("%sSkipped syncing /system partition%s\n", g_escapeBold, g_escapeEndColor);
+        } else {
+            printf("%sSynced /system partition%s\n", g_escapeBold, g_escapeEndColor);
+        }
+    }
+    if (installApks.size() > 0) {
+        bool printedTitle = false;
+        for (size_t i=0; i<installApks.size(); i++) {
+            const InstallApk& apk = installApks[i];
+            if (apk.installed) {
+                if (!printedTitle) {
+                    printf("%sInstalled:%s\n", g_escapeBold, g_escapeEndColor);
+                    printedTitle = true;
+                }
+                printf("   %s\n", apk.file.filename.c_str());
+            }
+        }
+        printedTitle = false;
+        for (size_t i=0; i<installApks.size(); i++) {
+            const InstallApk& apk = installApks[i];
+            if (!apk.installed) {
+                if (!printedTitle) {
+                    printf("%sSkipped install:%s\n", g_escapeBold, g_escapeEndColor);
+                    printedTitle = true;
+                }
+                printf("   %s\n", apk.file.filename.c_str());
+            }
+        }
+    }
+
+    // Tests
+    if (testActions.size() > 0) {
+        printf("%sRan tests:%s\n", g_escapeBold, g_escapeEndColor);
+        size_t maxNameLength = 0;
+        for (size_t i=0; i<targets.size(); i++) {
+            Target* target = targets[i];
+            if (target->test) {
+                size_t len = target->name.length();
+                if (len > maxNameLength) {
+                    maxNameLength = len;
+                }
+            }
+        }
+        string padding(maxNameLength, ' ');
+        for (size_t i=0; i<targets.size(); i++) {
+            Target* target = targets[i];
+            if (target->testActionCount > 0) {
+                printf("   %s%s", target->name.c_str(), padding.c_str() + target->name.length());
+                if (target->actionsWithNoTests) {
+                    printf("     %s%d passed, %d failed%s\n", g_escapeYellowBold,
+                            target->testPassCount, target->testFailCount, g_escapeEndColor);
+                } else if (target->testFailCount > 0) {
+                    printf("     %d passed, %s%d failed%s\n", target->testPassCount,
+                            g_escapeRedBold, target->testFailCount, g_escapeEndColor);
+                } else {
+                    printf("     %s%d passed%s, %d failed\n", g_escapeGreenBold,
+                            target->testPassCount, g_escapeEndColor, target->testFailCount);
+                }
+            }
+        }
+    }
+    if (activityActions.size() > 1) {
+        printf("%sStarted Activity:%s\n", g_escapeBold, g_escapeEndColor);
+        const ActivityAction& action = activityActions[0];
+        printf("   %s\n", pretty_component_name(action.packageName, action.className).c_str());
+    }
+
+    printf("%s--------------------------------------------%s\n", g_escapeBold, g_escapeEndColor);
+}
+
+/**
+ * Implement tab completion of the target names from the all modules file.
+ */
+void
+run_tab_completion(const string& word)
+{
+    const string buildTop = get_required_env("ANDROID_BUILD_TOP", true);
+    const string buildProduct = get_required_env("TARGET_PRODUCT", false);
+    const string buildOut = get_out_dir();
+
+    chdir(buildTop.c_str());
+
+    string buildDevice = sniff_device_name(buildOut, buildProduct);
+
+    map<string,Module> modules;
+    read_modules(buildOut, buildDevice, &modules, true);
+
+    for (map<string,Module>::const_iterator it = modules.begin(); it != modules.end(); it++) {
+        if (starts_with(it->first, word)) {
+            printf("%s\n", it->first.c_str());
+        }
+    }
+}
+
+/**
+ * Main entry point.
+ */
+int
+main(int argc, const char** argv)
+{
+    GOOGLE_PROTOBUF_VERIFY_VERSION;
+    init_print();
+
+    Options options;
+    parse_args(&options, argc, argv);
+
+    if (options.runHelp) {
+        // Help
+        print_usage(stdout);
+        exit(0);
+    } else if (options.runTab) {
+        run_tab_completion(options.tabPattern);
+        exit(0);
+    } else {
+        // Normal run
+        run_phases(options.targets, options.reboot);
+    }
+
+    return 0;
+}
+
diff --git a/tools/bit/make.cpp b/tools/bit/make.cpp
new file mode 100644
index 0000000..60b5687
--- /dev/null
+++ b/tools/bit/make.cpp
@@ -0,0 +1,210 @@
+/*
+ * 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 "make.h"
+
+#include "command.h"
+#include "print.h"
+#include "util.h"
+
+#include <json/reader.h>
+#include <json/value.h>
+
+#include <fstream>
+#include <string>
+#include <map>
+
+#include <sys/types.h>
+#include <dirent.h>
+#include <string.h>
+
+using namespace std;
+
+map<string,string> g_buildVars;
+
+string
+get_build_var(const string& buildTop, const string& name, bool quiet)
+{
+    int err;
+
+    map<string,string>::iterator it = g_buildVars.find(name);
+    if (it == g_buildVars.end()) {
+        Command cmd("make");
+        cmd.AddArg("--no-print-directory");
+        cmd.AddArg("-C");
+        cmd.AddArg(buildTop);
+        cmd.AddArg("-f");
+        cmd.AddArg("build/core/config.mk");
+        cmd.AddArg(string("dumpvar-") + name);
+        cmd.AddEnv("CALLED_FROM_SETUP", "true");
+        cmd.AddEnv("BUILD_SYSTEM", "build/core");
+
+        string output = trim(get_command_output(cmd, &err, quiet));
+        if (err == 0) {
+            g_buildVars[name] = output;
+            return output;
+        } else {
+            return string();
+        }
+    } else {
+        return it->second;
+    }
+}
+
+string
+sniff_device_name(const string& buildOut, const string& product)
+{
+    string match("ro.build.product=" + product);
+
+    string base(buildOut + "/target/product");
+    DIR* dir = opendir(base.c_str());
+    if (dir == NULL) {
+        return string();
+    }
+
+    dirent* entry;
+    while ((entry = readdir(dir)) != NULL) {
+        if (entry->d_name[0] == '.') {
+            continue;
+        }
+        if (entry->d_type == DT_DIR) {
+            string filename(base + "/" + entry->d_name + "/system/build.prop");
+            vector<string> lines;
+            split_lines(&lines, read_file(filename));
+            for (size_t i=0; i<lines.size(); i++) {
+                if (lines[i] == match) {
+                    return entry->d_name;
+                }
+            }
+        }
+    }
+
+    closedir(dir);
+    return string();
+}
+
+void
+json_error(const string& filename, const char* error, bool quiet)
+{
+    if (!quiet) {
+        print_error("Unable to parse module info file (%s): %s", error, filename.c_str());
+        print_error("Have you done a full build?");
+    }
+    exit(1);
+}
+
+static void
+get_values(const Json::Value& json, const string& name, vector<string>* result)
+{
+    Json::Value nullValue;
+
+    const Json::Value& value = json.get(name, nullValue);
+    if (!value.isArray()) {
+        return;
+    }
+
+    const int N = value.size();
+    for (int i=0; i<N; i++) {
+        const Json::Value& child = value[i];
+        if (child.isString()) {
+            result->push_back(child.asString());
+        }
+    }
+}
+
+void
+read_modules(const string& buildOut, const string& device, map<string,Module>* result, bool quiet)
+{
+    string filename(string(buildOut + "/target/product/") + device + "/module-info.json");
+    std::ifstream stream(filename, std::ifstream::binary);
+
+    if (stream.fail()) {
+        if (!quiet) {
+            print_error("Unable to open module info file: %s", filename.c_str());
+            print_error("Have you done a full build?");
+        }
+        exit(1);
+    }
+
+    Json::Value json;
+    Json::Reader reader;
+    if (!reader.parse(stream, json)) {
+        json_error(filename, "can't parse json format", quiet);
+        return;
+    }
+
+    if (!json.isObject()) {
+        json_error(filename, "root element not an object", quiet);
+        return;
+    }
+
+    vector<string> names = json.getMemberNames();
+    const int N = names.size();
+    for (int i=0; i<N; i++) {
+        const string& name = names[i];
+
+        const Json::Value& value = json[name];
+        if (!value.isObject()) {
+            continue;
+        }
+
+        Module module;
+
+        module.name = name;
+        get_values(value, "class", &module.classes);
+        get_values(value, "path", &module.paths);
+        get_values(value, "installed", &module.installed);
+
+        // Only keep classes we can handle
+        for (ssize_t i = module.classes.size() - 1; i >= 0; i--) {
+            string cl = module.classes[i];
+            if (!(cl == "JAVA_LIBRARIES" || cl == "EXECUTABLES" || cl == "SHARED_LIBRARIES"
+                    || cl == "APPS")) {
+                module.classes.erase(module.classes.begin() + i);
+            }
+        }
+        if (module.classes.size() == 0) {
+            continue;
+        }
+
+        // Only target modules (not host)
+        for (ssize_t i = module.installed.size() - 1; i >= 0; i--) {
+            string fn = module.installed[i];
+            if (!starts_with(fn, buildOut + "/target/")) {
+                module.installed.erase(module.installed.begin() + i);
+            }
+        }
+        if (module.installed.size() == 0) {
+            continue;
+        }
+
+        (*result)[name] = module;
+    }
+}
+
+int
+build_goals(const vector<string>& goals)
+{
+    Command cmd("make");
+    cmd.AddArg("-f");
+    cmd.AddArg("build/core/main.mk");
+    for (size_t i=0; i<goals.size(); i++) {
+        cmd.AddArg(goals[i]);
+    }
+
+    return run_command(cmd);
+}
+
diff --git a/tools/bit/make.h b/tools/bit/make.h
new file mode 100644
index 0000000..bb83c6e
--- /dev/null
+++ b/tools/bit/make.h
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+#ifndef MAKE_H
+#define MAKE_H
+
+#include <map>
+#include <string>
+#include <vector>
+
+using namespace std;
+
+struct Module
+{
+    string name;
+    vector<string> classes;
+    vector<string> paths;
+    vector<string> installed;
+};
+
+string get_build_var(const string& buildTop, const string& name, bool quiet);
+
+/**
+ * Poke around in the out directory and try to find a device name that matches
+ * our product. This is faster than running get_build_var and good enough for
+ * tab completion.
+ *
+ * Returns the empty string if we can't find one.
+ */
+string sniff_device_name(const string& buildOut, const string& product);
+
+void read_modules(const string& buildOut, const string& buildDevice,
+        map<string,Module>* modules, bool quiet);
+
+int build_goals(const vector<string>& goals);
+
+#endif // MAKE_H
diff --git a/tools/bit/print.cpp b/tools/bit/print.cpp
new file mode 100644
index 0000000..790e0b4
--- /dev/null
+++ b/tools/bit/print.cpp
@@ -0,0 +1,155 @@
+/*
+ * 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 "print.h"
+
+#include <sys/ioctl.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "util.h"
+
+bool g_stdoutIsTty;
+char const* g_escapeBold;
+char const* g_escapeRedBold;
+char const* g_escapeGreenBold;
+char const* g_escapeYellowBold;
+char const* g_escapeUnderline;
+char const* g_escapeEndColor;
+char const* g_escapeClearLine;
+
+void
+init_print()
+{
+    if (isatty(fileno(stdout))) {
+		g_stdoutIsTty = true;
+		g_escapeBold = "\033[1m";
+		g_escapeRedBold = "\033[91m\033[1m";
+		g_escapeGreenBold = "\033[92m\033[1m";
+		g_escapeYellowBold = "\033[93m\033[1m";
+		g_escapeUnderline = "\033[4m";
+		g_escapeEndColor = "\033[0m";
+		g_escapeClearLine = "\033[K";
+	} else {
+		g_stdoutIsTty = false;
+		g_escapeBold = "";
+		g_escapeRedBold = "";
+		g_escapeGreenBold = "";
+		g_escapeYellowBold = "";
+		g_escapeUnderline = "";
+		g_escapeEndColor = "";
+		g_escapeClearLine = "";
+    }
+}
+
+void
+print_status(const char* format, ...)
+{
+    printf("\n%s%s", g_escapeBold, g_escapeUnderline);
+
+    va_list args;
+    va_start(args, format);
+    vfprintf(stdout, format, args);
+    va_end(args);
+
+    printf("%s\n", g_escapeEndColor);
+}
+
+void
+print_command(const Command& command)
+{
+    fputs(g_escapeBold, stdout);
+    for (map<string,string>::const_iterator it=command.env.begin(); it!=command.env.end(); it++) {
+        fputs(it->first.c_str(), stdout);
+        fputc('=', stdout);
+        fputs(escape_for_commandline(it->second.c_str()).c_str(), stdout);
+        putc(' ', stdout);
+    }
+    fputs(command.prog.c_str(), stdout);
+    for (vector<string>::const_iterator it=command.args.begin(); it!=command.args.end(); it++) {
+        putc(' ', stdout);
+        fputs(escape_for_commandline(it->c_str()).c_str(), stdout);
+    }
+    fputs(g_escapeEndColor, stdout);
+    fputc('\n', stdout);
+}
+
+void
+print_error(const char* format, ...)
+{
+    fputs(g_escapeRedBold, stderr);
+
+    va_list args;
+    va_start(args, format);
+    vfprintf(stderr, format, args);
+    va_end(args);
+
+    fputs(g_escapeEndColor, stderr);
+    fputc('\n', stderr);
+}
+
+void
+print_warning(const char* format, ...)
+{
+    fputs(g_escapeYellowBold, stderr);
+
+    va_list args;
+    va_start(args, format);
+    vfprintf(stderr, format, args);
+    va_end(args);
+
+    fputs(g_escapeEndColor, stderr);
+    fputc('\n', stderr);
+}
+
+void
+print_one_line(const char* format, ...)
+{
+    if (g_stdoutIsTty) {
+        struct winsize ws;
+        ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws);
+        int size = ws.ws_col + 1;
+        char* buf = (char*)malloc(size);
+
+        va_list args;
+        va_start(args, format);
+        vsnprintf(buf, size, format, args);
+        va_end(args);
+
+        printf("%s%s\r", buf, g_escapeClearLine);
+        free(buf);
+
+        fflush(stdout);
+    } else {
+        va_list args;
+        va_start(args, format);
+        vfprintf(stdout, format, args);
+        va_end(args);
+        printf("\n");
+    }
+}
+
+void
+check_error(int err)
+{
+    if (err != 0) {
+        fputc('\n', stderr);
+        print_error("Stopping due to errors.");
+        exit(1);
+    }
+}
+
+
diff --git a/tools/bit/print.h b/tools/bit/print.h
new file mode 100644
index 0000000..b6c3e9a
--- /dev/null
+++ b/tools/bit/print.h
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+#ifndef PRINT_H
+#define PRINT_H
+
+#include "command.h"
+
+extern bool g_stdoutIsTty;
+extern char const* g_escapeBold;
+extern char const* g_escapeRedBold;
+extern char const* g_escapeGreenBold;
+extern char const* g_escapeYellowBold;
+extern char const* g_escapeUnderline;
+extern char const* g_escapeEndColor;
+extern char const* g_escapeClearLine;
+
+void init_print();
+void print_status(const char* format, ...);
+void print_command(const Command& command);
+void print_error(const char* format, ...);
+void print_warning(const char* format, ...);
+void print_one_line(const char* format, ...);
+void check_error(int err);
+
+#endif // PRINT_H
diff --git a/tools/bit/util.cpp b/tools/bit/util.cpp
new file mode 100644
index 0000000..fc93bcb
--- /dev/null
+++ b/tools/bit/util.cpp
@@ -0,0 +1,254 @@
+/*
+ * 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 "util.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <string.h>
+#include <unistd.h>
+
+
+FileInfo::FileInfo()
+{
+    memset(this, 0, sizeof(FileInfo));
+}
+
+FileInfo::FileInfo(const FileInfo& that)
+{
+    memcpy(this, &that, sizeof(FileInfo));
+}
+
+FileInfo::FileInfo(const string& filename)
+{
+    struct stat st;
+    int err = stat(filename.c_str(), &st);
+    if (err != 0) {
+        memset(this, 0, sizeof(FileInfo));
+    } else {
+        exists = true;
+        mtime = st.st_mtime;
+        ctime = st.st_ctime;
+        size = st.st_size;
+    }
+}
+
+bool
+FileInfo::operator==(const FileInfo& that) const
+{
+    return exists == that.exists
+            && mtime == that.mtime
+            && ctime == that.ctime
+            && size == that.size;
+}
+
+bool
+FileInfo::operator!=(const FileInfo& that) const
+{
+    return exists != that.exists
+            || mtime != that.mtime
+            || ctime != that.ctime
+            || size != that.size;
+}
+
+FileInfo::~FileInfo()
+{
+}
+
+TrackedFile::TrackedFile()
+    :filename(),
+     fileInfo()
+{
+}
+
+TrackedFile::TrackedFile(const TrackedFile& that)
+{
+    filename = that.filename;
+    fileInfo = that.fileInfo;
+}
+
+TrackedFile::TrackedFile(const string& file)
+    :filename(file),
+     fileInfo(file)
+{
+}
+
+TrackedFile::~TrackedFile()
+{
+}
+
+bool
+TrackedFile::HasChanged() const
+{
+    FileInfo updated(filename);
+    return !updated.exists || fileInfo != updated;
+}
+
+void
+get_directory_contents(const string& name, map<string,FileInfo>* results)
+{
+    int err;
+    DIR* dir = opendir(name.c_str());
+    if (dir == NULL) {
+        return;
+    }
+
+    dirent* entry;
+    while ((entry = readdir(dir)) != NULL) {
+        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
+            continue;
+        }
+        if (entry->d_type == DT_DIR) {
+            string subdir = name + "/" + entry->d_name;
+            get_directory_contents(subdir, results);
+        } else if (entry->d_type == DT_LNK || entry->d_type == DT_REG) {
+            string filename(name + "/" + entry->d_name);
+            (*results)[filename] = FileInfo(filename);
+        }
+    }
+
+    closedir(dir);
+}
+
+bool
+directory_contents_differ(const map<string,FileInfo>& before, const map<string,FileInfo>& after)
+{
+    if (before.size() != after.size()) {
+        return true;
+    }
+    map<string,FileInfo>::const_iterator b = before.begin();
+    map<string,FileInfo>::const_iterator a = after.begin();
+    while (b != before.end() && a != after.end()) {
+        if (b->first != a->first) {
+            return true;
+        }
+        if (a->second != b->second) {
+            return true;
+        }
+        a++;
+        b++;
+    }
+    return false;
+}
+
+string
+escape_quotes(const char* str)
+{
+    string result;
+    while (*str) {
+        if (*str == '"') {
+            result += '\\';
+            result += '"';
+        } else {
+            result += *str;
+        }
+    }
+    return result;
+}
+
+string
+escape_for_commandline(const char* str)
+{
+    if (strchr(str, '"') != NULL || strchr(str, ' ') != NULL
+            || strchr(str, '\t') != NULL) {
+        return escape_quotes(str);
+    } else {
+        return str;
+    }
+}
+
+static bool
+spacechr(char c)
+{
+    return c == ' ' || c == '\t' || c == '\n' || c == '\r';
+}
+
+string
+trim(const string& str)
+{
+    const ssize_t N = (ssize_t)str.size();
+    ssize_t begin = 0;
+    while (begin < N && spacechr(str[begin])) {
+        begin++;
+    }
+    ssize_t end = N - 1;
+    while (end >= begin && spacechr(str[end])) {
+        end--;
+    }
+    return string(str, begin, end-begin+1);
+}
+
+bool
+starts_with(const string& str, const string& prefix)
+{
+    return str.compare(0, prefix.length(), prefix) == 0;
+}
+
+bool
+ends_with(const string& str, const string& suffix)
+{
+    if (str.length() < suffix.length()) {
+        return false;
+    } else {
+        return str.compare(str.length()-suffix.length(), suffix.length(), suffix) == 0;
+    }
+}
+
+void
+split_lines(vector<string>* result, const string& str)
+{
+    const int N = str.length();
+    int begin = 0;
+    int end = 0;
+    for (; end < N; end++) {
+        const char c = str[end];
+        if (c == '\r' || c == '\n') {
+            if (begin != end) {
+                result->push_back(string(str, begin, end-begin));
+            }
+            begin = end+1;
+        }
+    }
+    if (begin != end) {
+        result->push_back(string(str, begin, end-begin));
+    }
+}
+
+string
+read_file(const string& filename)
+{
+    FILE* file = fopen(filename.c_str(), "r");
+    if (file == NULL) {
+        return string();
+    }
+    
+    fseek(file, 0, SEEK_END);
+    int size = ftell(file);
+    fseek(file, 0, SEEK_SET);
+
+    char* buf = (char*)malloc(size);
+    fread(buf, 1, size, file);
+
+    string result(buf, size);
+
+    free(buf);
+    fclose(file);
+
+    return result;
+}
+
+
diff --git a/tools/bit/util.h b/tools/bit/util.h
new file mode 100644
index 0000000..718f147
--- /dev/null
+++ b/tools/bit/util.h
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+#ifndef UTIL_H
+#define UTIL_H
+
+#include <map>
+#include <string>
+#include <vector>
+
+using namespace std;
+
+struct FileInfo
+{
+    bool exists;
+    time_t mtime;
+    time_t ctime;
+    off_t size;
+
+    FileInfo();
+    FileInfo(const FileInfo& that);
+    explicit FileInfo(const string& filename);
+    ~FileInfo();
+
+    bool operator==(const FileInfo& that) const;
+    bool operator!=(const FileInfo& that) const;
+};
+
+
+/**
+ * Record for a file that we are watching
+ */
+struct TrackedFile {
+    string filename;
+    FileInfo fileInfo;
+
+    TrackedFile();
+    TrackedFile(const TrackedFile& that);
+    explicit TrackedFile(const string& filename);
+    ~TrackedFile();
+
+    // Returns if the file has changed. If it doesn't currently exist, returns true.
+    bool HasChanged() const;
+};
+
+/**
+ * Get FileInfo structures recursively for all the files and symlinks in a directory.
+ * Does not traverse symlinks, but it does record them.
+ */
+void get_directory_contents(const string& dir, map<string,FileInfo>* results);
+
+bool directory_contents_differ(const map<string,FileInfo>& before,
+        const map<string,FileInfo>& after);
+
+string escape_quotes(const char* str);
+
+string escape_for_commandline(const char* str);
+
+string trim(const string& trim);
+
+bool starts_with(const string& str, const string& prefix);
+
+bool ends_with(const string& str, const string& suffix);
+
+void split_lines(vector<string>* result, const string& str);
+
+string read_file(const string& filename);
+
+#endif // UTIL_H
+