Merge "Add MIPS64r6 support for libpixelflinger"
diff --git a/adb/Android.mk b/adb/Android.mk
index 4ee1ced..903d1e1 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -14,6 +14,7 @@
     -Wall -Wextra -Werror \
     -Wno-unused-parameter \
     -Wno-missing-field-initializers \
+    -Wvla \
     -DADB_REVISION='"$(adb_version)"' \
 
 # Define windows.h and tchar.h Unicode preprocessor symbols so that
@@ -212,12 +213,13 @@
 LOCAL_REQUIRED_MODULES_windows := AdbWinApi AdbWinUsbApi
 
 LOCAL_SRC_FILES := \
+    adb_client.cpp \
     client/main.cpp \
     console.cpp \
     commandline.cpp \
-    adb_client.cpp \
-    services.cpp \
     file_sync_client.cpp \
+    line_printer.cpp \
+    services.cpp \
     shell_service_protocol.cpp \
 
 LOCAL_CFLAGS += \
@@ -294,11 +296,15 @@
     libadbd \
     libbase \
     libfs_mgr \
+    libfec \
+    libfec_rs \
+    libselinux \
     liblog \
     libmincrypt \
-    libselinux \
     libext4_utils_static \
+    libsquashfs_utils \
     libcutils \
     libbase \
+    libcrypto_static
 
 include $(BUILD_EXECUTABLE)
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 1eb3a3c..c39c178 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -963,9 +963,11 @@
         fflush(stdout);
         SendOkay(reply_fd);
 
-        // At least on Windows, if we exit() without shutdown(SD_SEND) or
-        // closesocket(), the client's next recv() will error-out with
-        // WSAECONNRESET and they'll never read the OKAY.
+        // On Windows, if the process exits with open sockets that
+        // shutdown(SD_SEND) has not been called on, TCP RST segments will be
+        // sent to the peers which will cause their next recv() to error-out
+        // with WSAECONNRESET. In the case of this code, that means the client
+        // may not read the OKAY sent above.
         adb_shutdown(reply_fd);
 
         exit(0);
diff --git a/adb/adb_client.cpp b/adb/adb_client.cpp
index fa8ce9e..ddeb5f1 100644
--- a/adb/adb_client.cpp
+++ b/adb/adb_client.cpp
@@ -206,6 +206,7 @@
                 return -1;
             }
 
+            ReadOrderlyShutdown(fd);
             adb_close(fd);
 
             if (sscanf(&version_string[0], "%04x", &version) != 1) {
@@ -227,6 +228,7 @@
                    version, ADB_SERVER_VERSION);
             fd = _adb_connect("host:kill", error);
             if (fd >= 0) {
+                ReadOrderlyShutdown(fd);
                 adb_close(fd);
             } else {
                 // If we couldn't connect to the server or had some other error,
@@ -271,6 +273,8 @@
         return false;
     }
 
+    ReadOrderlyShutdown(fd);
+    adb_close(fd);
     return true;
 }
 
@@ -286,5 +290,8 @@
         adb_close(fd);
         return false;
     }
+
+    ReadOrderlyShutdown(fd);
+    adb_close(fd);
     return true;
 }
diff --git a/adb/adb_io.cpp b/adb/adb_io.cpp
index 7788996..a37fbc0 100644
--- a/adb/adb_io.cpp
+++ b/adb/adb_io.cpp
@@ -137,3 +137,43 @@
 
     return WriteFdExactly(fd, str);
 }
+
+bool ReadOrderlyShutdown(int fd) {
+    char buf[16];
+
+    // Only call this function if you're sure that the peer does
+    // orderly/graceful shutdown of the socket, closing the socket so that
+    // adb_read() will return 0. If the peer keeps the socket open, adb_read()
+    // will never return.
+    int result = adb_read(fd, buf, sizeof(buf));
+    if (result == -1) {
+        // If errno is EAGAIN, that means this function was called on a
+        // nonblocking socket and it would have blocked (which would be bad
+        // because we'd probably block the main thread where nonblocking IO is
+        // done). Don't do that. If you have a nonblocking socket, use the
+        // fdevent APIs to get called on FDE_READ, and then call this function
+        // if you really need to, but it shouldn't be needed for server sockets.
+        CHECK_NE(errno, EAGAIN);
+
+        // Note that on Windows, orderly shutdown sometimes causes
+        // recv() == SOCKET_ERROR && WSAGetLastError() == WSAECONNRESET. That
+        // can be ignored.
+        return false;
+    } else if (result == 0) {
+        // Peer has performed an orderly/graceful shutdown.
+        return true;
+    } else {
+        // Unexpectedly received data. This is essentially a protocol error
+        // because you should not call this function unless you expect no more
+        // data. We don't repeatedly call adb_read() until we get zero because
+        // we don't know how long that would take, but we do know that the
+        // caller wants to close the socket soon.
+        VLOG(RWX) << "ReadOrderlyShutdown(" << fd << ") unexpectedly read "
+                  << dump_hex(buf, result);
+        // Shutdown the socket to prevent the caller from reading or writing to
+        // it which doesn't make sense if we just read and discarded some data.
+        adb_shutdown(fd);
+        errno = EINVAL;
+        return false;
+    }
+}
diff --git a/adb/adb_io.h b/adb/adb_io.h
index 9c3b2a5..aa550af 100644
--- a/adb/adb_io.h
+++ b/adb/adb_io.h
@@ -41,6 +41,24 @@
 // If this function fails, the contents of buf are undefined.
 bool ReadFdExactly(int fd, void* buf, size_t len);
 
+// Given a client socket, wait for orderly/graceful shutdown. Call this:
+//
+// * Before closing a client socket.
+// * Only when no more data is expected to come in.
+// * Only when the server is not waiting for data from the client (because then
+//   the client and server will deadlock waiting for each other).
+// * Only when the server is expected to close its socket right now.
+// * Don't call shutdown(SHUT_WR) before calling this because that will shutdown
+//   the client socket early, defeating the purpose of calling this.
+//
+// Waiting for orderly/graceful shutdown of the server socket will cause the
+// server socket to close before the client socket. That prevents the client
+// socket from staying in TIME_WAIT which eventually causes subsequent
+// connect()s from the client to fail with WSAEADDRINUSE on Windows.
+// Returns true if it is sure that orderly/graceful shutdown has occurred with
+// no additional data read from the server.
+bool ReadOrderlyShutdown(int fd);
+
 // Writes exactly len bytes from buf to fd.
 //
 // Returns false if there is an error or if the fd was closed before the write
diff --git a/adb/adb_io_test.cpp b/adb/adb_io_test.cpp
index 84e3db6..6928a90 100644
--- a/adb/adb_io_test.cpp
+++ b/adb/adb_io_test.cpp
@@ -43,7 +43,7 @@
   ASSERT_NE(-1, tf.fd);
 
   ASSERT_TRUE(android::base::WriteStringToFd(expected, tf.fd)) << strerror(errno);
-  ASSERT_EQ(0, lseek(tf.fd, SEEK_SET, 0));
+  ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
 
   // Test reading the whole file.
   char buf[sizeof(expected)] = {};
@@ -57,7 +57,7 @@
   ASSERT_NE(-1, tf.fd);
 
   ASSERT_TRUE(android::base::WriteStringToFd(expected, tf.fd)) << strerror(errno);
-  ASSERT_EQ(0, lseek(tf.fd, SEEK_SET, 0));
+  ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
 
   // Test that not having enough data will fail.
   char buf[sizeof(expected) + 1] = {};
@@ -71,7 +71,7 @@
   ASSERT_NE(-1, tf.fd);
 
   ASSERT_TRUE(android::base::WriteStringToFd(input, tf.fd)) << strerror(errno);
-  ASSERT_EQ(0, lseek(tf.fd, SEEK_SET, 0));
+  ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
 
   // Test reading a partial file.
   char buf[sizeof(input) - 1] = {};
@@ -90,7 +90,7 @@
   // Test writing the whole string to the file.
   ASSERT_TRUE(WriteFdExactly(tf.fd, expected, sizeof(expected)))
     << strerror(errno);
-  ASSERT_EQ(0, lseek(tf.fd, SEEK_SET, 0));
+  ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
 
   std::string s;
   ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s));
@@ -104,7 +104,7 @@
 
   // Test writing a partial string to the file.
   ASSERT_TRUE(WriteFdExactly(tf.fd, buf, sizeof(buf) - 2)) << strerror(errno);
-  ASSERT_EQ(0, lseek(tf.fd, SEEK_SET, 0));
+  ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
 
   std::string expected(buf);
   expected.pop_back();
@@ -130,7 +130,7 @@
 
   // Test writing a partial string to the file.
   ASSERT_TRUE(WriteFdExactly(tf.fd, str)) << strerror(errno);
-  ASSERT_EQ(0, lseek(tf.fd, SEEK_SET, 0));
+  ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
 
   std::string s;
   ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s));
@@ -143,7 +143,7 @@
 
     // Test writing a partial string to the file.
     ASSERT_TRUE(WriteFdFmt(tf.fd, "Foo%s%d", "bar", 123)) << strerror(errno);
-    ASSERT_EQ(0, lseek(tf.fd, SEEK_SET, 0));
+    ASSERT_EQ(0, lseek(tf.fd, 0, SEEK_SET));
 
     std::string s;
     ASSERT_TRUE(android::base::ReadFdToString(tf.fd, &s));
diff --git a/adb/adb_trace.cpp b/adb/adb_trace.cpp
index 04b82f6..9586f7c 100644
--- a/adb/adb_trace.cpp
+++ b/adb/adb_trace.cpp
@@ -157,7 +157,7 @@
     // Don't open log file if no tracing, since this will block
     // the crypto unmount of /data
     if (!get_trace_setting().empty()) {
-        if (isatty(STDOUT_FILENO) == 0) {
+        if (unix_isatty(STDOUT_FILENO) == 0) {
             start_device_log();
         }
     }
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index f7b2e4e..fd61bda 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -33,6 +33,7 @@
 #include "adb_trace.h"
 #include "sysdeps.h"
 
+ADB_MUTEX_DEFINE(basename_lock);
 ADB_MUTEX_DEFINE(dirname_lock);
 
 bool getcwd(std::string* s) {
@@ -69,13 +70,31 @@
 }
 
 std::string adb_basename(const std::string& path) {
-  size_t base = path.find_last_of(OS_PATH_SEPARATORS);
-  return (base != std::string::npos) ? path.substr(base + 1) : path;
+  // Copy path because basename may modify the string passed in.
+  std::string result(path);
+
+  // Use lock because basename() may write to a process global and return a
+  // pointer to that. Note that this locking strategy only works if all other
+  // callers to dirname in the process also grab this same lock.
+  adb_mutex_lock(&basename_lock);
+
+  // Note that if std::string uses copy-on-write strings, &str[0] will cause
+  // the copy to be made, so there is no chance of us accidentally writing to
+  // the storage for 'path'.
+  char* name = basename(&result[0]);
+
+  // In case dirname returned a pointer to a process global, copy that string
+  // before leaving the lock.
+  result.assign(name);
+
+  adb_mutex_unlock(&basename_lock);
+
+  return result;
 }
 
 std::string adb_dirname(const std::string& path) {
   // Copy path because dirname may modify the string passed in.
-  std::string parent_storage(path);
+  std::string result(path);
 
   // Use lock because dirname() may write to a process global and return a
   // pointer to that. Note that this locking strategy only works if all other
@@ -85,11 +104,11 @@
   // Note that if std::string uses copy-on-write strings, &str[0] will cause
   // the copy to be made, so there is no chance of us accidentally writing to
   // the storage for 'path'.
-  char* parent = dirname(&parent_storage[0]);
+  char* parent = dirname(&result[0]);
 
   // In case dirname returned a pointer to a process global, copy that string
   // before leaving the lock.
-  const std::string result(parent);
+  result.assign(parent);
 
   adb_mutex_unlock(&dirname_lock);
 
@@ -115,9 +134,7 @@
   // - Recursive, so it uses stack space relative to number of directory
   //   components.
 
-  const std::string parent(adb_dirname(path));
-
-  if (directory_exists(parent)) {
+  if (directory_exists(path)) {
     return true;
   }
 
@@ -125,19 +142,21 @@
   // This can happen on Windows when walking up the directory hierarchy and not
   // finding anything that already exists (unlike POSIX that will eventually
   // find . or /).
+  const std::string parent(adb_dirname(path));
+
   if (parent == path) {
     errno = ENOENT;
     return false;
   }
 
-  // Recursively make parent directories of 'parent'.
+  // Recursively make parent directories of 'path'.
   if (!mkdirs(parent)) {
     return false;
   }
 
-  // Now that the parent directory hierarchy of 'parent' has been ensured,
+  // Now that the parent directory hierarchy of 'path' has been ensured,
   // create parent itself.
-  if (adb_mkdir(parent, 0775) == -1) {
+  if (adb_mkdir(path, 0775) == -1) {
     // Can't just check for errno == EEXIST because it might be a file that
     // exists.
     const int saved_errno = errno;
diff --git a/adb/adb_utils_test.cpp b/adb/adb_utils_test.cpp
index 4a2787a..93c20cb 100644
--- a/adb/adb_utils_test.cpp
+++ b/adb/adb_utils_test.cpp
@@ -102,6 +102,13 @@
 TEST(adb_utils, adb_basename) {
   EXPECT_EQ("sh", adb_basename("/system/bin/sh"));
   EXPECT_EQ("sh", adb_basename("sh"));
+  EXPECT_EQ("sh", adb_basename("/system/bin/sh/"));
+}
+
+TEST(adb_utils, adb_dirname) {
+  EXPECT_EQ("/system/bin", adb_dirname("/system/bin/sh"));
+  EXPECT_EQ(".", adb_dirname("sh"));
+  EXPECT_EQ("/system/bin", adb_dirname("/system/bin/sh/"));
 }
 
 TEST(adb_utils, parse_host_and_port) {
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index bc5ba38..785fef3 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -33,12 +33,15 @@
 
 #include <memory>
 #include <string>
+#include <vector>
 
 #include <base/logging.h>
 #include <base/stringprintf.h>
 #include <base/strings.h>
 
 #if !defined(_WIN32)
+#include <signal.h>
+#include <sys/ioctl.h>
 #include <termios.h>
 #include <unistd.h>
 #endif
@@ -100,13 +103,11 @@
         "                                 will disconnect from all connected TCP/IP devices.\n"
         "\n"
         "device commands:\n"
-        "  adb push [-p] <local> <remote>\n"
-        "                               - copy file/dir to device\n"
-        "                                 ('-p' to display the transfer progress)\n"
-        "  adb pull [-p] [-a] <remote> [<local>]\n"
-        "                               - copy file/dir from device\n"
-        "                                 ('-p' to display the transfer progress)\n"
-        "                                 ('-a' means copy timestamp and mode)\n"
+        "  adb push <local>... <remote>\n"
+        "                               - copy files/dirs to device\n"
+        "  adb pull [-a] <remote>... <local>\n"
+        "                               - copy files/dirs from device\n"
+        "                                 (-a preserves file timestamp and mode)\n"
         "  adb sync [ <directory> ]     - copy host->device only if changed\n"
         "                                 (-l means list but don't copy)\n"
         "  adb shell [-Ttx]             - run remote shell interactively\n"
@@ -139,7 +140,7 @@
         "                                   localabstract:<unix domain socket name>\n"
         "                                   localreserved:<unix domain socket name>\n"
         "                                   localfilesystem:<unix domain socket name>\n"
-        "  adb reverse --norebind <remote> <local>\n"
+        "  adb reverse --no-rebind <remote> <local>\n"
         "                               - same as 'adb reverse <remote> <local>' but fails\n"
         "                                 if <remote> is already reversed.\n"
         "  adb reverse --remove <remote>\n"
@@ -247,17 +248,17 @@
 #if defined(_WIN32)
 
 // Implemented in sysdeps_win32.cpp.
-void stdin_raw_init(int fd);
-void stdin_raw_restore(int fd);
+void stdin_raw_init();
+void stdin_raw_restore();
 
 #else
 static termios g_saved_terminal_state;
 
-static void stdin_raw_init(int fd) {
-    if (tcgetattr(fd, &g_saved_terminal_state)) return;
+static void stdin_raw_init() {
+    if (tcgetattr(STDIN_FILENO, &g_saved_terminal_state)) return;
 
     termios tio;
-    if (tcgetattr(fd, &tio)) return;
+    if (tcgetattr(STDIN_FILENO, &tio)) return;
 
     cfmakeraw(&tio);
 
@@ -265,11 +266,11 @@
     tio.c_cc[VTIME] = 0;
     tio.c_cc[VMIN] = 1;
 
-    tcsetattr(fd, TCSAFLUSH, &tio);
+    tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio);
 }
 
-static void stdin_raw_restore(int fd) {
-    tcsetattr(fd, TCSAFLUSH, &g_saved_terminal_state);
+static void stdin_raw_restore() {
+    tcsetattr(STDIN_FILENO, TCSAFLUSH, &g_saved_terminal_state);
 }
 #endif
 
@@ -358,7 +359,7 @@
     D("copy_to_file(%d -> %d)", inFd, outFd);
 
     if (inFd == STDIN_FILENO) {
-        stdin_raw_init(STDIN_FILENO);
+        stdin_raw_init();
 #ifdef _WIN32
         old_stdin_mode = _setmode(STDIN_FILENO, _O_BINARY);
         if (old_stdin_mode == -1) {
@@ -400,7 +401,7 @@
     }
 
     if (inFd == STDIN_FILENO) {
-        stdin_raw_restore(STDIN_FILENO);
+        stdin_raw_restore();
 #ifdef _WIN32
         if (_setmode(STDIN_FILENO, old_stdin_mode) == -1) {
             fatal_errno("could not restore stdin mode");
@@ -420,7 +421,44 @@
     free(buf);
 }
 
-namespace {
+static std::string format_host_command(const char* command,
+                                       TransportType type, const char* serial) {
+    if (serial) {
+        return android::base::StringPrintf("host-serial:%s:%s", serial, command);
+    }
+
+    const char* prefix = "host";
+    if (type == kTransportUsb) {
+        prefix = "host-usb";
+    } else if (type == kTransportLocal) {
+        prefix = "host-local";
+    }
+    return android::base::StringPrintf("%s:%s", prefix, command);
+}
+
+// Returns the FeatureSet for the indicated transport.
+static FeatureSet GetFeatureSet(TransportType transport_type, const char* serial) {
+    std::string result, error;
+    if (adb_query(format_host_command("features", transport_type, serial), &result, &error)) {
+        return StringToFeatureSet(result);
+    }
+    return FeatureSet();
+}
+
+static void send_window_size_change(int fd, std::unique_ptr<ShellProtocol>& shell) {
+#if !defined(_WIN32)
+    // Old devices can't handle window size changes.
+    if (shell == nullptr) return;
+
+    winsize ws;
+    if (ioctl(fd, TIOCGWINSZ, &ws) == -1) return;
+
+    // Send the new window size as human-readable ASCII for debugging convenience.
+    size_t l = snprintf(shell->data(), shell->data_capacity(), "%dx%d,%dx%d",
+                        ws.ws_row, ws.ws_col, ws.ws_xpixel, ws.ws_ypixel);
+    shell->Write(ShellProtocol::kIdWindowSizeChange, l + 1);
+#endif
+}
 
 // Used to pass multiple values to the stdin read thread.
 struct StdinReadArgs {
@@ -429,30 +467,56 @@
     std::unique_ptr<ShellProtocol> protocol;
 };
 
-}  // namespace
-
 // Loops to read from stdin and push the data to the given FD.
 // The argument should be a pointer to a StdinReadArgs object. This function
 // will take ownership of the object and delete it when finished.
-static void* stdin_read_thread(void* x) {
+static void* stdin_read_thread_loop(void* x) {
     std::unique_ptr<StdinReadArgs> args(reinterpret_cast<StdinReadArgs*>(x));
     int state = 0;
 
-    adb_thread_setname("stdin reader");
+#if !defined(_WIN32)
+    // Mask SIGTTIN in case we're in a backgrounded process.
+    sigset_t sigset;
+    sigemptyset(&sigset);
+    sigaddset(&sigset, SIGTTIN);
+    pthread_sigmask(SIG_BLOCK, &sigset, nullptr);
+#endif
+
+#if !defined(_WIN32)
+    // Unblock SIGWINCH for this thread, so our read(2) below will be
+    // interrupted if the window size changes.
+    sigset_t mask;
+    sigemptyset(&mask);
+    sigaddset(&mask, SIGWINCH);
+    pthread_sigmask(SIG_UNBLOCK, &mask, nullptr);
+#endif
+
+    // Set up the initial window size.
+    send_window_size_change(args->stdin_fd, args->protocol);
 
     char raw_buffer[1024];
     char* buffer_ptr = raw_buffer;
     size_t buffer_size = sizeof(raw_buffer);
-    if (args->protocol) {
+    if (args->protocol != nullptr) {
         buffer_ptr = args->protocol->data();
         buffer_size = args->protocol->data_capacity();
     }
 
     while (true) {
         // Use unix_read() rather than adb_read() for stdin.
-        D("stdin_read_thread(): pre unix_read(fdi=%d,...)", args->stdin_fd);
+        D("stdin_read_thread_loop(): pre unix_read(fdi=%d,...)", args->stdin_fd);
+#if !defined(_WIN32)
+#undef read
+        int r = read(args->stdin_fd, buffer_ptr, buffer_size);
+        if (r == -1 && errno == EINTR) {
+            send_window_size_change(args->stdin_fd, args->protocol);
+            continue;
+        }
+#define read ___xxx_read
+#else
         int r = unix_read(args->stdin_fd, buffer_ptr, buffer_size);
-        D("stdin_read_thread(): post unix_read(fdi=%d,...)", args->stdin_fd);
+#endif
+        D("stdin_read_thread_loop(): post unix_read(fdi=%d,...)", args->stdin_fd);
         if (r <= 0) {
             // Only devices using the shell protocol know to close subprocess
             // stdin. For older devices we want to just leave the connection
@@ -469,8 +533,8 @@
         // process starts ignoring the signal. SSH also does this, see the
         // "escape characters" section on the ssh man page for more info.
         if (args->raw_stdin) {
-            for (int n = 0; n < r; n++){
-                switch(buffer_ptr[n]) {
+            for (int n = 0; n < r; n++) {
+                switch (buffer_ptr[n]) {
                 case '\n':
                     state = 1;
                     break;
@@ -478,12 +542,16 @@
                     state = 1;
                     break;
                 case '~':
-                    if(state == 1) state++;
+                    if (state == 1) {
+                        state++;
+                    } else {
+                        state = 0;
+                    }
                     break;
                 case '.':
-                    if(state == 2) {
-                        stdin_raw_restore(args->stdin_fd);
-                        fprintf(stderr,"\n* disconnect *\n");
+                    if (state == 2) {
+                        fprintf(stderr,"\r\n* disconnect *\r\n");
+                        stdin_raw_restore();
                         exit(0);
                     }
                 default:
@@ -562,53 +630,121 @@
         args->protocol.reset(new ShellProtocol(args->write_fd));
     }
 
-    if (raw_stdin) {
-        stdin_raw_init(STDIN_FILENO);
-    }
+    if (raw_stdin) stdin_raw_init();
 
-    int exit_code = 0;
-    if (!adb_thread_create(stdin_read_thread, args)) {
+#if !defined(_WIN32)
+    // Ensure our process is notified if the local window size changes.
+    // We use sigaction(2) to ensure that the SA_RESTART flag is not set,
+    // because the whole reason we're sending signals is to unblock the read(2)!
+    // That also means we don't need to do anything in the signal handler:
+    // the side effect of delivering the signal is all we need.
+    struct sigaction sa;
+    memset(&sa, 0, sizeof(sa));
+    sa.sa_handler = [](int) {};
+    sa.sa_flags = 0;
+    sigaction(SIGWINCH, &sa, nullptr);
+
+    // Now block SIGWINCH in this thread (the main thread) and all threads spawned
+    // from it. The stdin read thread will unblock this signal to ensure that it's
+    // the thread that receives the signal.
+    sigset_t mask;
+    sigemptyset(&mask);
+    sigaddset(&mask, SIGWINCH);
+    pthread_sigmask(SIG_BLOCK, &mask, nullptr);
+#endif
+
+    // TODO: combine read_and_dump with stdin_read_thread to make life simpler?
+    int exit_code = 1;
+    if (!adb_thread_create(stdin_read_thread_loop, args)) {
         PLOG(ERROR) << "error starting stdin read thread";
-        exit_code = 1;
         delete args;
     } else {
         exit_code = read_and_dump(fd, use_shell_protocol);
     }
 
-    if (raw_stdin) {
-        stdin_raw_restore(STDIN_FILENO);
-    }
+    // TODO: properly exit stdin_read_thread_loop and close |fd|.
 
-    // TODO(dpursell): properly exit stdin_read_thread and close |fd|.
+    // TODO: we should probably install signal handlers for this.
+    // TODO: can we use atexit? even on Windows?
+    if (raw_stdin) stdin_raw_restore();
 
     return exit_code;
 }
 
+static int adb_shell(int argc, const char** argv,
+                     TransportType transport_type, const char* serial) {
+    FeatureSet features = GetFeatureSet(transport_type, serial);
 
-static std::string format_host_command(const char* command, TransportType type, const char* serial) {
-    if (serial) {
-        return android::base::StringPrintf("host-serial:%s:%s", serial, command);
+    bool use_shell_protocol = CanUseFeature(features, kFeatureShell2);
+    if (!use_shell_protocol) {
+        D("shell protocol not supported, using raw data transfer");
+    } else {
+        D("using shell protocol");
     }
 
-    const char* prefix = "host";
-    if (type == kTransportUsb) {
-        prefix = "host-usb";
-    } else if (type == kTransportLocal) {
-        prefix = "host-local";
+    // Parse shell-specific command-line options.
+    // argv[0] is always "shell".
+    --argc;
+    ++argv;
+    int t_arg_count = 0;
+    while (argc) {
+        if (!strcmp(argv[0], "-T") || !strcmp(argv[0], "-t")) {
+            if (!CanUseFeature(features, kFeatureShell2)) {
+                fprintf(stderr, "error: target doesn't support PTY args -Tt\n");
+                return 1;
+            }
+            // Like ssh, -t arguments are cumulative so that multiple -t's
+            // are needed to force a PTY.
+            if (argv[0][1] == 't') {
+                ++t_arg_count;
+            } else {
+                t_arg_count = -1;
+            }
+            --argc;
+            ++argv;
+        } else if (!strcmp(argv[0], "-x")) {
+            use_shell_protocol = false;
+            --argc;
+            ++argv;
+        } else {
+            break;
+        }
     }
-    return android::base::StringPrintf("%s:%s", prefix, command);
-}
 
-// Returns the FeatureSet for the indicated transport.
-static FeatureSet GetFeatureSet(TransportType transport_type,
-                                const char* serial) {
-    std::string result, error;
-
-    if (adb_query(format_host_command("features", transport_type, serial),
-                  &result, &error)) {
-        return StringToFeatureSet(result);
+    std::string shell_type_arg;
+    if (CanUseFeature(features, kFeatureShell2)) {
+        if (t_arg_count < 0) {
+            shell_type_arg = kShellServiceArgRaw;
+        } else if (t_arg_count == 0) {
+            // If stdin isn't a TTY, default to a raw shell; this lets
+            // things like `adb shell < my_script.sh` work as expected.
+            // Otherwise leave |shell_type_arg| blank which uses PTY for
+            // interactive shells and raw for non-interactive.
+            if (!unix_isatty(STDIN_FILENO)) {
+                shell_type_arg = kShellServiceArgRaw;
+            }
+        } else if (t_arg_count == 1) {
+            // A single -t arg isn't enough to override implicit -T.
+            if (!unix_isatty(STDIN_FILENO)) {
+                fprintf(stderr,
+                        "Remote PTY will not be allocated because stdin is not a terminal.\n"
+                        "Use multiple -t options to force remote PTY allocation.\n");
+                shell_type_arg = kShellServiceArgRaw;
+            } else {
+                shell_type_arg = kShellServiceArgPty;
+            }
+        } else {
+            shell_type_arg = kShellServiceArgPty;
+        }
     }
-    return FeatureSet();
+
+    std::string command;
+    if (argc) {
+        // We don't escape here, just like ssh(1). http://b/20564385.
+        command = android::base::Join(std::vector<const char*>(argv, argv + argc), ' ');
+    }
+
+    return RemoteShell(use_shell_protocol, shell_type_arg, command);
 }
 
 static int adb_download_buffer(const char *service, const char *fn, const void* data, unsigned sz,
@@ -638,6 +774,7 @@
             std::string error;
             adb_status(fd, &error);
             fprintf(stderr,"* failed to write data '%s' *\n", error.c_str());
+            adb_close(fd);
             return -1;
         }
         sz -= xfer;
@@ -653,6 +790,7 @@
 
     if (!adb_status(fd, &error)) {
         fprintf(stderr,"* error response '%s' *\n", error.c_str());
+        adb_close(fd);
         return -1;
     }
 
@@ -914,7 +1052,7 @@
 }
 
 static int backup(int argc, const char** argv) {
-    const char* filename = "./backup.ab";
+    const char* filename = "backup.ab";
 
     /* find, extract, and use any -f argument */
     for (int i = 1; i < argc; i++) {
@@ -936,7 +1074,6 @@
     if (argc < 2) return usage();
 
     adb_unlink(filename);
-    mkdirs(filename);
     int outFd = adb_creat(filename, 0640);
     if (outFd < 0) {
         fprintf(stderr, "adb: unable to open file %s\n", filename);
@@ -1052,32 +1189,35 @@
     return path;
 }
 
-static void parse_push_pull_args(const char **arg, int narg, char const **path1,
-                                 char const **path2, bool* show_progress,
-                                 int *copy_attrs) {
-    *show_progress = false;
-    *copy_attrs = 0;
+static void parse_push_pull_args(const char** arg, int narg,
+                                 std::vector<const char*>* srcs,
+                                 const char** dst, bool* copy_attrs) {
+    *copy_attrs = false;
 
+    srcs->clear();
+    bool ignore_flags = false;
     while (narg > 0) {
-        if (!strcmp(*arg, "-p")) {
-            *show_progress = true;
-        } else if (!strcmp(*arg, "-a")) {
-            *copy_attrs = 1;
+        if (ignore_flags || *arg[0] != '-') {
+            srcs->push_back(*arg);
         } else {
-            break;
+            if (!strcmp(*arg, "-p")) {
+                // Silently ignore for backwards compatibility.
+            } else if (!strcmp(*arg, "-a")) {
+                *copy_attrs = true;
+            } else if (!strcmp(*arg, "--")) {
+                ignore_flags = true;
+            } else {
+                fprintf(stderr, "adb: unrecognized option '%s'\n", *arg);
+                exit(1);
+            }
         }
         ++arg;
         --narg;
     }
 
-    if (narg > 0) {
-        *path1 = *arg;
-        ++arg;
-        --narg;
-    }
-
-    if (narg > 0) {
-        *path2 = *arg;
+    if (srcs->size() > 1) {
+        *dst = srcs->back();
+        srcs->pop_back();
     }
 }
 
@@ -1326,62 +1466,8 @@
     else if (!strcmp(argv[0], "emu")) {
         return adb_send_emulator_command(argc, argv, serial);
     }
-    else if (!strcmp(argv[0], "shell") || !strcmp(argv[0], "hell")) {
-        char h = (argv[0][0] == 'h');
-
-        FeatureSet features = GetFeatureSet(transport_type, serial);
-
-        bool use_shell_protocol = CanUseFeature(features, kFeatureShell2);
-        if (!use_shell_protocol) {
-            D("shell protocol not supported, using raw data transfer");
-        } else {
-            D("using shell protocol");
-        }
-
-        // Parse shell-specific command-line options.
-        // argv[0] is always "shell".
-        --argc;
-        ++argv;
-        std::string shell_type_arg;
-        while (argc) {
-            if (!strcmp(argv[0], "-T") || !strcmp(argv[0], "-t")) {
-                if (!CanUseFeature(features, kFeatureShell2)) {
-                    fprintf(stderr, "error: target doesn't support PTY args -Tt\n");
-                    return 1;
-                }
-                shell_type_arg = (argv[0][1] == 'T') ? kShellServiceArgRaw
-                                                     : kShellServiceArgPty;
-                --argc;
-                ++argv;
-            } else if (!strcmp(argv[0], "-x")) {
-                use_shell_protocol = false;
-                --argc;
-                ++argv;
-            } else {
-                break;
-            }
-        }
-
-        std::string command;
-        if (argc) {
-            // We don't escape here, just like ssh(1). http://b/20564385.
-            command = android::base::Join(
-                    std::vector<const char*>(argv, argv + argc), ' ');
-        }
-
-        if (h) {
-            printf("\x1b[41;33m");
-            fflush(stdout);
-        }
-
-        r = RemoteShell(use_shell_protocol, shell_type_arg, command);
-
-        if (h) {
-            printf("\x1b[0m");
-            fflush(stdout);
-        }
-
-        return r;
+    else if (!strcmp(argv[0], "shell")) {
+        return adb_shell(argc, argv, transport_type, serial);
     }
     else if (!strcmp(argv[0], "exec-in") || !strcmp(argv[0], "exec-out")) {
         int exec_in = !strcmp(argv[0], "exec-in");
@@ -1426,6 +1512,7 @@
         } else {
             // Successfully connected, kill command sent, okay status came back.
             // Server should exit() in a moment, if not already.
+            ReadOrderlyShutdown(fd);
             adb_close(fd);
             return 0;
         }
@@ -1516,22 +1603,22 @@
         return do_sync_ls(argv[1]) ? 0 : 1;
     }
     else if (!strcmp(argv[0], "push")) {
-        bool show_progress = false;
-        int copy_attrs = 0;
-        const char* lpath = NULL, *rpath = NULL;
+        bool copy_attrs = false;
+        std::vector<const char*> srcs;
+        const char* dst = nullptr;
 
-        parse_push_pull_args(&argv[1], argc - 1, &lpath, &rpath, &show_progress, &copy_attrs);
-        if (!lpath || !rpath || copy_attrs != 0) return usage();
-        return do_sync_push(lpath, rpath, show_progress) ? 0 : 1;
+        parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs);
+        if (srcs.empty() || !dst) return usage();
+        return do_sync_push(srcs, dst) ? 0 : 1;
     }
     else if (!strcmp(argv[0], "pull")) {
-        bool show_progress = false;
-        int copy_attrs = 0;
-        const char* rpath = NULL, *lpath = ".";
+        bool copy_attrs = false;
+        std::vector<const char*> srcs;
+        const char* dst = ".";
 
-        parse_push_pull_args(&argv[1], argc - 1, &rpath, &lpath, &show_progress, &copy_attrs);
-        if (!rpath) return usage();
-        return do_sync_pull(rpath, lpath, show_progress, copy_attrs) ? 0 : 1;
+        parse_push_pull_args(&argv[1], argc - 1, &srcs, &dst, &copy_attrs);
+        if (srcs.empty()) return usage();
+        return do_sync_pull(srcs, dst, copy_attrs) ? 0 : 1;
     }
     else if (!strcmp(argv[0], "install")) {
         if (argc < 2) return usage();
@@ -1721,9 +1808,10 @@
     }
 
     int result = -1;
-    const char* apk_file = argv[last_apk];
-    std::string apk_dest = android::base::StringPrintf(where, adb_basename(apk_file).c_str());
-    if (!do_sync_push(apk_file, apk_dest.c_str(), false)) goto cleanup_apk;
+    std::vector<const char*> apk_file = {argv[last_apk]};
+    std::string apk_dest = android::base::StringPrintf(
+        where, adb_basename(argv[last_apk]).c_str());
+    if (!do_sync_push(apk_file, apk_dest.c_str())) goto cleanup_apk;
     argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */
     result = pm_command(transport, serial, argc, argv);
 
diff --git a/adb/console.cpp b/adb/console.cpp
index ba5a72b..5a9c6ab 100644
--- a/adb/console.cpp
+++ b/adb/console.cpp
@@ -25,6 +25,7 @@
 
 #include "adb.h"
 #include "adb_client.h"
+#include "adb_io.h"
 
 // Return the console port of the currently connected emulator (if any) or -1 if
 // there is no emulator, and -2 if there is more than one.
@@ -87,14 +88,20 @@
         return 1;
     }
 
+    std::string commands;
+
     for (int i = 1; i < argc; i++) {
-        adb_write(fd, argv[i], strlen(argv[i]));
-        adb_write(fd, i == argc - 1 ? "\n" : " ", 1);
+        commands.append(argv[i]);
+        commands.append(i == argc - 1 ? "\n" : " ");
     }
 
-    const char disconnect_command[] = "quit\n";
-    if (adb_write(fd, disconnect_command, sizeof(disconnect_command) - 1) == -1) {
-        LOG(FATAL) << "Could not finalize emulator command";
+    commands.append("quit\n");
+
+    if (!WriteFdExactly(fd, commands)) {
+        fprintf(stderr, "error: cannot write to emulator: %s\n",
+                strerror(errno));
+        adb_close(fd);
+        return 1;
     }
 
     // Drain output that the emulator console has sent us to prevent a problem
@@ -106,11 +113,14 @@
     do {
         char buf[BUFSIZ];
         result = adb_read(fd, buf, sizeof(buf));
-        // Keep reading until zero bytes (EOF) or an error. If 'adb emu kill'
-        // is executed, the emulator calls exit() which causes adb to get
-        // ECONNRESET. Any other emu command is followed by the quit command
-        // that we sent above, and that causes the emulator to close the socket
-        // which should cause zero bytes (EOF) to be returned.
+        // Keep reading until zero bytes (orderly/graceful shutdown) or an
+        // error. If 'adb emu kill' is executed, the emulator calls exit() with
+        // the socket open (and shutdown(SD_SEND) was not called), which causes
+        // Windows to send a TCP RST segment which causes adb to get ECONNRESET.
+        // Any other emu command is followed by the quit command that we
+        // appended above, and that causes the emulator to close the socket
+        // which should cause zero bytes (orderly/graceful shutdown) to be
+        // returned.
     } while (result > 0);
 
     adb_close(fd);
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index ddd15a2..06eb34d 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -54,7 +54,7 @@
 
 struct PollNode {
   fdevent* fde;
-  pollfd pollfd;
+  ::pollfd pollfd;
 
   PollNode(fdevent* fde) : fde(fde) {
       memset(&pollfd, 0, sizeof(pollfd));
@@ -72,6 +72,19 @@
 // That's why we don't need a lock for fdevent.
 static std::unordered_map<int, PollNode> g_poll_node_map;
 static std::list<fdevent*> g_pending_list;
+static bool main_thread_valid;
+static pthread_t main_thread;
+
+static void check_main_thread() {
+    if (main_thread_valid) {
+        CHECK_NE(0, pthread_equal(main_thread, pthread_self()));
+    }
+}
+
+static void set_main_thread() {
+    main_thread_valid = true;
+    main_thread = pthread_self();
+}
 
 static std::string dump_fde(const fdevent* fde) {
     std::string state;
@@ -101,6 +114,7 @@
 
 fdevent *fdevent_create(int fd, fd_func func, void *arg)
 {
+    check_main_thread();
     fdevent *fde = (fdevent*) malloc(sizeof(fdevent));
     if(fde == 0) return 0;
     fdevent_install(fde, fd, func, arg);
@@ -110,6 +124,7 @@
 
 void fdevent_destroy(fdevent *fde)
 {
+    check_main_thread();
     if(fde == 0) return;
     if(!(fde->state & FDE_CREATED)) {
         LOG(FATAL) << "destroying fde not created by fdevent_create(): " << dump_fde(fde);
@@ -119,6 +134,7 @@
 }
 
 void fdevent_install(fdevent* fde, int fd, fd_func func, void* arg) {
+    check_main_thread();
     CHECK_GE(fd, 0);
     memset(fde, 0, sizeof(fdevent));
     fde->state = FDE_ACTIVE;
@@ -137,6 +153,7 @@
 }
 
 void fdevent_remove(fdevent* fde) {
+    check_main_thread();
     D("fdevent_remove %s", dump_fde(fde).c_str());
     if (fde->state & FDE_ACTIVE) {
         g_poll_node_map.erase(fde->fd);
@@ -171,6 +188,7 @@
 }
 
 void fdevent_set(fdevent* fde, unsigned events) {
+    check_main_thread();
     events &= FDE_EVENTMASK;
     if ((fde->state & FDE_EVENTMASK) == events) {
         return;
@@ -190,10 +208,12 @@
 }
 
 void fdevent_add(fdevent* fde, unsigned events) {
+    check_main_thread();
     fdevent_set(fde, (fde->state & FDE_EVENTMASK) | events);
 }
 
 void fdevent_del(fdevent* fde, unsigned events) {
+    check_main_thread();
     fdevent_set(fde, (fde->state & FDE_EVENTMASK) & ~events);
 }
 
@@ -335,6 +355,7 @@
 
 void fdevent_loop()
 {
+    set_main_thread();
 #if !ADB_HOST
     fdevent_subproc_setup();
 #endif // !ADB_HOST
@@ -359,4 +380,5 @@
 void fdevent_reset() {
     g_poll_node_map.clear();
     g_pending_list.clear();
+    main_thread_valid = false;
 }
diff --git a/adb/file_sync_client.cpp b/adb/file_sync_client.cpp
index 5513e8f..d2bc3cd 100644
--- a/adb/file_sync_client.cpp
+++ b/adb/file_sync_client.cpp
@@ -28,7 +28,9 @@
 #include <unistd.h>
 #include <utime.h>
 
+#include <functional>
 #include <memory>
+#include <vector>
 
 #include "sysdeps.h"
 
@@ -37,6 +39,7 @@
 #include "adb_io.h"
 #include "adb_utils.h"
 #include "file_sync_service.h"
+#include "line_printer.h"
 
 #include <base/file.h>
 #include <base/strings.h>
@@ -48,44 +51,30 @@
     char data[SYNC_DATA_MAX];
 };
 
-static long long NOW() {
-    struct timeval tv;
-    gettimeofday(&tv, 0);
-    return ((long long) tv.tv_usec) + 1000000LL * ((long long) tv.tv_sec);
-}
-
-static void print_transfer_progress(uint64_t bytes_current,
-                                    uint64_t bytes_total) {
-    if (bytes_total == 0) return;
-
-    fprintf(stderr, "\rTransferring: %" PRIu64 "/%" PRIu64 " (%d%%)",
-            bytes_current, bytes_total,
-            (int) (bytes_current * 100 / bytes_total));
-
-    if (bytes_current == bytes_total) {
-        fputc('\n', stderr);
-    }
-
-    fflush(stderr);
-}
-
 class SyncConnection {
   public:
-    SyncConnection() : total_bytes(0), start_time_(NOW()) {
+    SyncConnection() : total_bytes(0), start_time_ms_(CurrentTimeMs()) {
         max = SYNC_DATA_MAX; // TODO: decide at runtime.
 
         std::string error;
         fd = adb_connect("sync:", &error);
         if (fd < 0) {
-            fprintf(stderr, "error: %s\n", error.c_str());
+            Error("connect failed: %s", error.c_str());
         }
     }
 
     ~SyncConnection() {
         if (!IsValid()) return;
 
-        SendQuit();
-        ShowTransferRate();
+        if (SendQuit()) {
+            // We sent a quit command, so the server should be doing orderly
+            // shutdown soon. But if we encountered an error while we were using
+            // the connection, the server might still be sending data (before
+            // doing orderly shutdown), in which case we won't wait for all of
+            // the data nor the coming orderly shutdown. In the common success
+            // case, this will wait for the server to do orderly shutdown.
+            ReadOrderlyShutdown(fd);
+        }
         adb_close(fd);
     }
 
@@ -94,39 +83,42 @@
     bool SendRequest(int id, const char* path_and_mode) {
         size_t path_length = strlen(path_and_mode);
         if (path_length > 1024) {
-            fprintf(stderr, "SendRequest failed: path too long: %zu", path_length);
+            Error("SendRequest failed: path too long: %zu", path_length);
             errno = ENAMETOOLONG;
             return false;
         }
 
         // Sending header and payload in a single write makes a noticeable
         // difference to "adb sync" performance.
-        char buf[sizeof(SyncRequest) + path_length];
-        SyncRequest* req = reinterpret_cast<SyncRequest*>(buf);
+        std::vector<char> buf(sizeof(SyncRequest) + path_length);
+        SyncRequest* req = reinterpret_cast<SyncRequest*>(&buf[0]);
         req->id = id;
         req->path_length = path_length;
         char* data = reinterpret_cast<char*>(req + 1);
         memcpy(data, path_and_mode, path_length);
 
-        return WriteFdExactly(fd, buf, sizeof(buf));
+        return WriteFdExactly(fd, &buf[0], buf.size());
     }
 
     // Sending header, payload, and footer in a single write makes a huge
     // difference to "adb sync" performance.
     bool SendSmallFile(const char* path_and_mode,
+                       const char* rpath,
                        const char* data, size_t data_length,
                        unsigned mtime) {
+        Print(rpath);
+
         size_t path_length = strlen(path_and_mode);
         if (path_length > 1024) {
-            fprintf(stderr, "SendSmallFile failed: path too long: %zu", path_length);
+            Error("SendSmallFile failed: path too long: %zu", path_length);
             errno = ENAMETOOLONG;
             return false;
         }
 
-        char buf[sizeof(SyncRequest) + path_length +
+        std::vector<char> buf(sizeof(SyncRequest) + path_length +
                  sizeof(SyncRequest) + data_length +
-                 sizeof(SyncRequest)];
-        char* p = buf;
+                 sizeof(SyncRequest));
+        char* p = &buf[0];
 
         SyncRequest* req_send = reinterpret_cast<SyncRequest*>(p);
         req_send->id = ID_SEND;
@@ -147,7 +139,7 @@
         req_done->path_length = mtime;
         p += sizeof(SyncRequest);
 
-        if (!WriteFdExactly(fd, buf, (p-buf))) return false;
+        if (!WriteFdExactly(fd, &buf[0], (p - &buf[0]))) return false;
 
         total_bytes += data_length;
         return true;
@@ -156,28 +148,67 @@
     bool CopyDone(const char* from, const char* to) {
         syncmsg msg;
         if (!ReadFdExactly(fd, &msg.status, sizeof(msg.status))) {
-            fprintf(stderr, "failed to copy '%s' to '%s': no ID_DONE: %s\n",
-                    from, to, strerror(errno));
+            Error("failed to copy '%s' to '%s': no ID_DONE: %s", from, to, strerror(errno));
             return false;
         }
         if (msg.status.id == ID_OKAY) {
             return true;
         }
         if (msg.status.id != ID_FAIL) {
-            fprintf(stderr, "failed to copy '%s' to '%s': unknown reason\n", from, to);
+            Error("failed to copy '%s' to '%s': unknown reason %d", from, to, msg.status.id);
             return false;
         }
-        char buffer[msg.status.msglen + 1];
-        if (!ReadFdExactly(fd, buffer, msg.status.msglen)) {
-            fprintf(stderr, "failed to copy '%s' to '%s'; failed to read reason (!): %s\n",
-                    from, to, strerror(errno));
+        return ReportCopyFailure(from, to, msg);
+    }
+
+    bool ReportCopyFailure(const char* from, const char* to, const syncmsg& msg) {
+        std::vector<char> buf(msg.status.msglen + 1);
+        if (!ReadFdExactly(fd, &buf[0], msg.status.msglen)) {
+            Error("failed to copy '%s' to '%s'; failed to read reason (!): %s",
+                  from, to, strerror(errno));
             return false;
         }
-        buffer[msg.status.msglen] = 0;
-        fprintf(stderr, "failed to copy '%s' to '%s': %s\n", from, to, buffer);
+        buf[msg.status.msglen] = 0;
+        Error("failed to copy '%s' to '%s': %s", from, to, &buf[0]);
         return false;
     }
 
+    std::string TransferRate() {
+        uint64_t ms = CurrentTimeMs() - start_time_ms_;
+        if (total_bytes == 0 || ms == 0) return "";
+
+        double s = static_cast<double>(ms) / 1000LL;
+        double rate = (static_cast<double>(total_bytes) / s) / (1024*1024);
+        return android::base::StringPrintf(" %.1f MB/s (%" PRId64 " bytes in %.3fs)",
+                                           rate, total_bytes, s);
+    }
+
+    void Print(const std::string& s) {
+        // TODO: we actually don't want ELIDE; we want "ELIDE if smart, FULL if dumb".
+        line_printer_.Print(s, LinePrinter::ELIDE);
+    }
+
+    void Printf(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) {
+        std::string s;
+        va_list ap;
+        va_start(ap, fmt);
+        android::base::StringAppendV(&s, fmt, ap);
+        va_end(ap);
+
+        Print(s);
+    }
+
+    void Error(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) {
+        std::string s = "adb: error: ";
+
+        va_list ap;
+        va_start(ap, fmt);
+        android::base::StringAppendV(&s, fmt, ap);
+        va_end(ap);
+
+        line_printer_.Print(s, LinePrinter::FULL);
+    }
+
     uint64_t total_bytes;
 
     // TODO: add a char[max] buffer here, to replace syncsendbuf...
@@ -185,25 +216,25 @@
     size_t max;
 
   private:
-    uint64_t start_time_;
+    uint64_t start_time_ms_;
 
-    void SendQuit() {
-        SendRequest(ID_QUIT, ""); // TODO: add a SendResponse?
+    LinePrinter line_printer_;
+
+    bool SendQuit() {
+        return SendRequest(ID_QUIT, ""); // TODO: add a SendResponse?
     }
 
-    void ShowTransferRate() {
-        uint64_t t = NOW() - start_time_;
-        if (total_bytes == 0 || t == 0) return;
-
-        fprintf(stderr, "%lld KB/s (%" PRId64 " bytes in %lld.%03llds)\n",
-                ((total_bytes * 1000000LL) / t) / 1024LL,
-                total_bytes, (t / 1000000LL), (t % 1000000LL) / 1000LL);
+    static uint64_t CurrentTimeMs() {
+        struct timeval tv;
+        gettimeofday(&tv, 0); // (Not clock_gettime because of Mac/Windows.)
+        return static_cast<uint64_t>(tv.tv_sec) * 1000 + tv.tv_usec / 1000;
     }
 };
 
-typedef void (*sync_ls_cb)(unsigned mode, unsigned size, unsigned time, const char* name, void* cookie);
+typedef void (sync_ls_cb)(unsigned mode, unsigned size, unsigned time, const char* name);
 
-static bool sync_ls(SyncConnection& sc, const char* path, sync_ls_cb func, void* cookie) {
+static bool sync_ls(SyncConnection& sc, const char* path,
+                    std::function<sync_ls_cb> func) {
     if (!sc.SendRequest(ID_LIST, path)) return false;
 
     while (true) {
@@ -220,7 +251,7 @@
         if (!ReadFdExactly(sc.fd, buf, len)) return false;
         buf[len] = 0;
 
-        func(msg.dent.mode, msg.dent.size, msg.dent.time, buf, cookie);
+        func(msg.dent.mode, msg.dent.size, msg.dent.time, buf);
     }
 }
 
@@ -243,29 +274,26 @@
     return sc.SendRequest(ID_STAT, path) && sync_finish_stat(sc, timestamp, mode, size);
 }
 
-static bool SendLargeFile(SyncConnection& sc, const char* path_and_mode, const char* path,
-                          unsigned mtime, bool show_progress) {
+static bool SendLargeFile(SyncConnection& sc, const char* path_and_mode,
+                          const char* lpath, const char* rpath,
+                          unsigned mtime) {
     if (!sc.SendRequest(ID_SEND, path_and_mode)) {
-        fprintf(stderr, "failed to send ID_SEND message '%s': %s\n",
-                path_and_mode, strerror(errno));
+        sc.Error("failed to send ID_SEND message '%s': %s", path_and_mode, strerror(errno));
         return false;
     }
 
-    unsigned long long size = 0;
-    if (show_progress) {
-        // Determine local file size.
-        struct stat st;
-        if (stat(path, &st) == -1) {
-            fprintf(stderr, "cannot stat '%s': %s\n", path, strerror(errno));
-            return false;
-        }
-
-        size = st.st_size;
+    struct stat st;
+    if (stat(lpath, &st) == -1) {
+        sc.Error("cannot stat '%s': %s", lpath, strerror(errno));
+        return false;
     }
 
-    int lfd = adb_open(path, O_RDONLY);
+    uint64_t total_size = st.st_size;
+    uint64_t bytes_copied = 0;
+
+    int lfd = adb_open(lpath, O_RDONLY);
     if (lfd < 0) {
-        fprintf(stderr, "cannot open '%s': %s\n", path, strerror(errno));
+        sc.Error("cannot open '%s': %s", lpath, strerror(errno));
         return false;
     }
 
@@ -275,7 +303,7 @@
         int ret = adb_read(lfd, sbuf.data, sc.max);
         if (ret <= 0) {
             if (ret < 0) {
-                fprintf(stderr, "cannot read '%s': %s\n", path, strerror(errno));
+                sc.Error("cannot read '%s': %s", lpath, strerror(errno));
                 adb_close(lfd);
                 return false;
             }
@@ -289,9 +317,10 @@
         }
         sc.total_bytes += ret;
 
-        if (show_progress) {
-            print_transfer_progress(sc.total_bytes, size);
-        }
+        bytes_copied += ret;
+
+        int percentage = static_cast<int>(bytes_copied * 100 / total_size);
+        sc.Printf("%s: %d%%", rpath, percentage);
     }
 
     adb_close(lfd);
@@ -300,7 +329,7 @@
     msg.data.id = ID_DONE;
     msg.data.size = mtime;
     if (!WriteFdExactly(sc.fd, &msg.data, sizeof(msg.data))) {
-        fprintf(stderr, "failed to send ID_DONE message for '%s': %s\n", path, strerror(errno));
+        sc.Error("failed to send ID_DONE message for '%s': %s", rpath, strerror(errno));
         return false;
     }
 
@@ -308,7 +337,7 @@
 }
 
 static bool sync_send(SyncConnection& sc, const char* lpath, const char* rpath,
-                      unsigned mtime, mode_t mode, bool show_progress)
+                      unsigned mtime, mode_t mode)
 {
     std::string path_and_mode = android::base::StringPrintf("%s,%d", rpath, mode);
 
@@ -317,186 +346,166 @@
         char buf[PATH_MAX];
         ssize_t data_length = readlink(lpath, buf, PATH_MAX - 1);
         if (data_length == -1) {
-            fprintf(stderr, "readlink '%s' failed: %s\n", lpath, strerror(errno));
+            sc.Error("readlink '%s' failed: %s", lpath, strerror(errno));
             return false;
         }
         buf[data_length++] = '\0';
 
-        if (!sc.SendSmallFile(path_and_mode.c_str(), buf, data_length, mtime)) return false;
+        if (!sc.SendSmallFile(path_and_mode.c_str(), rpath, buf, data_length, mtime)) return false;
         return sc.CopyDone(lpath, rpath);
 #endif
     }
 
     if (!S_ISREG(mode)) {
-        fprintf(stderr, "local file '%s' has unsupported mode: 0o%o\n", lpath, mode);
+        sc.Error("local file '%s' has unsupported mode: 0o%o", lpath, mode);
         return false;
     }
 
     struct stat st;
     if (stat(lpath, &st) == -1) {
-        fprintf(stderr, "stat '%s' failed: %s\n", lpath, strerror(errno));
+        sc.Error("failed to stat local file '%s': %s", lpath, strerror(errno));
         return false;
     }
     if (st.st_size < SYNC_DATA_MAX) {
         std::string data;
         if (!android::base::ReadFileToString(lpath, &data)) {
-            fprintf(stderr, "failed to read all of '%s': %s\n", lpath, strerror(errno));
+            sc.Error("failed to read all of '%s': %s", lpath, strerror(errno));
             return false;
         }
-        if (!sc.SendSmallFile(path_and_mode.c_str(), data.data(), data.size(), mtime)) return false;
+        if (!sc.SendSmallFile(path_and_mode.c_str(), rpath, data.data(), data.size(), mtime)) {
+            return false;
+        }
     } else {
-        if (!SendLargeFile(sc, path_and_mode.c_str(), lpath, mtime, show_progress)) return false;
+        if (!SendLargeFile(sc, path_and_mode.c_str(), lpath, rpath, mtime)) {
+            return false;
+        }
     }
     return sc.CopyDone(lpath, rpath);
 }
 
-static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath, bool show_progress) {
-    syncmsg msg;
-    int lfd = -1;
-
-    size_t len = strlen(rpath);
-    if (len > 1024) return false;
+static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath) {
+    sc.Print(rpath);
 
     unsigned size = 0;
-    if (show_progress) {
-        if (!sync_stat(sc, rpath, nullptr, nullptr, &size)) return false;
-    }
+    if (!sync_stat(sc, rpath, nullptr, nullptr, &size)) return false;
 
     if (!sc.SendRequest(ID_RECV, rpath)) return false;
-    if (!ReadFdExactly(sc.fd, &msg.data, sizeof(msg.data))) return false;
 
-    unsigned id = msg.data.id;
-
-    if (id == ID_DATA || id == ID_DONE) {
-        adb_unlink(lpath);
-        mkdirs(lpath);
-        lfd = adb_creat(lpath, 0644);
-        if(lfd < 0) {
-            fprintf(stderr, "cannot create '%s': %s\n", lpath, strerror(errno));
-            return false;
-        }
-        goto handle_data;
-    } else {
-        goto remote_error;
+    adb_unlink(lpath);
+    if (!mkdirs(adb_dirname(lpath))) {
+        sc.Error("failed to create parent directory '%s': %s",
+                 adb_dirname(lpath).c_str(), strerror(errno));
+        return false;
     }
 
-    while (true) {
-        char buffer[SYNC_DATA_MAX];
+    int lfd = adb_creat(lpath, 0644);
+    if (lfd < 0) {
+        sc.Error("cannot create '%s': %s", lpath, strerror(errno));
+        return false;
+    }
 
+    uint64_t bytes_copied = 0;
+    while (true) {
+        syncmsg msg;
         if (!ReadFdExactly(sc.fd, &msg.data, sizeof(msg.data))) {
             adb_close(lfd);
+            adb_unlink(lpath);
             return false;
         }
-        id = msg.data.id;
 
-    handle_data:
-        len = msg.data.size;
-        if (id == ID_DONE) break;
-        if (id != ID_DATA) goto remote_error;
-        if (len > sc.max) {
-            fprintf(stderr, "msg.data.size too large: %zu (max %zu)\n", len, sc.max);
+        if (msg.data.id == ID_DONE) break;
+
+        if (msg.data.id != ID_DATA) {
             adb_close(lfd);
+            adb_unlink(lpath);
+            sc.ReportCopyFailure(rpath, lpath, msg);
             return false;
         }
 
-        if (!ReadFdExactly(sc.fd, buffer, len)) {
+        if (msg.data.size > sc.max) {
+            sc.Error("msg.data.size too large: %u (max %zu)", msg.data.size, sc.max);
             adb_close(lfd);
+            adb_unlink(lpath);
             return false;
         }
 
-        if (!WriteFdExactly(lfd, buffer, len)) {
-            fprintf(stderr, "cannot write '%s': %s\n", rpath, strerror(errno));
+        char buffer[SYNC_DATA_MAX];
+        if (!ReadFdExactly(sc.fd, buffer, msg.data.size)) {
             adb_close(lfd);
+            adb_unlink(lpath);
             return false;
         }
 
-        sc.total_bytes += len;
-
-        if (show_progress) {
-            print_transfer_progress(sc.total_bytes, size);
+        if (!WriteFdExactly(lfd, buffer, msg.data.size)) {
+            sc.Error("cannot write '%s': %s", lpath, strerror(errno));
+            adb_close(lfd);
+            adb_unlink(lpath);
+            return false;
         }
+
+        sc.total_bytes += msg.data.size;
+
+        bytes_copied += msg.data.size;
+
+        int percentage = static_cast<int>(bytes_copied * 100 / size);
+        sc.Printf("%s: %d%%", rpath, percentage);
     }
 
     adb_close(lfd);
     return true;
-
-remote_error:
-    adb_close(lfd);
-    adb_unlink(lpath);
-    sc.CopyDone(rpath, lpath);
-    return false;
-}
-
-static void do_sync_ls_cb(unsigned mode, unsigned size, unsigned time,
-                          const char* name, void* /*cookie*/) {
-    printf("%08x %08x %08x %s\n", mode, size, time, name);
 }
 
 bool do_sync_ls(const char* path) {
     SyncConnection sc;
     if (!sc.IsValid()) return false;
 
-    return sync_ls(sc, path, do_sync_ls_cb, 0);
+    return sync_ls(sc, path, [](unsigned mode, unsigned size, unsigned time,
+                                const char* name) {
+        printf("%08x %08x %08x %s\n", mode, size, time, name);
+    });
 }
 
 struct copyinfo
 {
-    copyinfo *next;
-    const char *src;
-    const char *dst;
+    std::string src;
+    std::string dst;
     unsigned int time;
     unsigned int mode;
     uint64_t size;
-    int flag;
+    bool skip;
 };
 
-static copyinfo* mkcopyinfo(const char* spath, const char* dpath, const char* name, int isdir) {
-    int slen = strlen(spath);
-    int dlen = strlen(dpath);
-    int nlen = strlen(name);
-    int ssize = slen + nlen + 2;
-    int dsize = dlen + nlen + 2;
-
-    copyinfo *ci = reinterpret_cast<copyinfo*>(malloc(sizeof(copyinfo) + ssize + dsize));
-    if(ci == 0) {
-        fprintf(stderr, "out of memory\n");
-        abort();
-    }
-
-    ci->next = 0;
-    ci->time = 0;
-    ci->mode = 0;
-    ci->size = 0;
-    ci->flag = 0;
-    ci->src = (const char*)(ci + 1);
-    ci->dst = ci->src + ssize;
-    snprintf((char*) ci->src, ssize, isdir ? "%s%s/" : "%s%s", spath, name);
-    snprintf((char*) ci->dst, dsize, isdir ? "%s%s/" : "%s%s", dpath, name);
-
-    return ci;
+static copyinfo mkcopyinfo(const char* spath, const char* dpath, const char* name, bool isdir) {
+    copyinfo result;
+    result.src = android::base::StringPrintf(isdir ? "%s%s/" : "%s%s", spath, name);
+    result.dst = android::base::StringPrintf(isdir ? "%s%s/" : "%s%s", dpath, name);
+    result.time = 0;
+    result.mode = 0;
+    result.size = 0;
+    result.skip = false;
+    return result;
 }
 
 static bool IsDotOrDotDot(const char* name) {
     return name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'));
 }
 
-static int local_build_list(copyinfo** filelist, const char* lpath, const char* rpath) {
-    copyinfo *dirlist = 0;
-    copyinfo *ci, *next;
-
-    std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir(lpath), closedir);
+static int local_build_list(SyncConnection& sc, std::vector<copyinfo>* filelist,
+                            const char* lpath, const char* rpath) {
+    std::vector<copyinfo> dirlist;
+    std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(lpath), closedir);
     if (!dir) {
-        fprintf(stderr, "cannot open '%s': %s\n", lpath, strerror(errno));
+        sc.Error("cannot open '%s': %s", lpath, strerror(errno));
         return -1;
     }
 
-    dirent *de;
+    dirent* de;
     while ((de = readdir(dir.get()))) {
         if (IsDotOrDotDot(de->d_name)) continue;
 
         char stat_path[PATH_MAX];
         if (strlen(lpath) + strlen(de->d_name) + 1 > sizeof(stat_path)) {
-            fprintf(stderr, "skipping long path '%s%s'\n", lpath, de->d_name);
+            sc.Error("skipping long path '%s%s'", lpath, de->d_name);
             continue;
         }
         strcpy(stat_path, lpath);
@@ -505,33 +514,27 @@
         struct stat st;
         if (!lstat(stat_path, &st)) {
             if (S_ISDIR(st.st_mode)) {
-                ci = mkcopyinfo(lpath, rpath, de->d_name, 1);
-                ci->next = dirlist;
-                dirlist = ci;
+                dirlist.push_back(mkcopyinfo(lpath, rpath, de->d_name, 1));
             } else {
-                ci = mkcopyinfo(lpath, rpath, de->d_name, 0);
                 if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
-                    fprintf(stderr, "skipping special file '%s'\n", ci->src);
-                    free(ci);
+                    sc.Error("skipping special file '%s'", lpath);
                 } else {
-                    ci->time = st.st_mtime;
-                    ci->mode = st.st_mode;
-                    ci->size = st.st_size;
-                    ci->next = *filelist;
-                    *filelist = ci;
+                    copyinfo ci = mkcopyinfo(lpath, rpath, de->d_name, 0);
+                    ci.time = st.st_mtime;
+                    ci.mode = st.st_mode;
+                    ci.size = st.st_size;
+                    filelist->push_back(ci);
                 }
             }
         } else {
-            fprintf(stderr, "cannot lstat '%s': %s\n",stat_path , strerror(errno));
+            sc.Error("cannot lstat '%s': %s", stat_path, strerror(errno));
         }
     }
 
     // Close this directory and recurse.
     dir.reset();
-    for (ci = dirlist; ci != 0; ci = next) {
-        next = ci->next;
-        local_build_list(filelist, ci->src, ci->dst);
-        free(ci);
+    for (const copyinfo& ci : dirlist) {
+        local_build_list(sc, filelist, ci.src.c_str(), ci.dst.c_str());
     }
 
     return 0;
@@ -539,8 +542,7 @@
 
 static bool copy_local_dir_remote(SyncConnection& sc, const char* lpath, const char* rpath,
                                   bool check_timestamps, bool list_only) {
-    copyinfo *filelist = 0;
-    copyinfo *ci, *next;
+    std::vector<copyinfo> filelist;
     int pushed = 0;
     int skipped = 0;
 
@@ -560,133 +562,137 @@
         rpath = tmp;
     }
 
-    if (local_build_list(&filelist, lpath, rpath)) {
+    if (local_build_list(sc, &filelist, lpath, rpath)) {
         return false;
     }
 
     if (check_timestamps) {
-        for (ci = filelist; ci != 0; ci = ci->next) {
-            if (!sc.SendRequest(ID_STAT, ci->dst)) return false;
+        for (const copyinfo& ci : filelist) {
+            if (!sc.SendRequest(ID_STAT, ci.dst.c_str())) return false;
         }
-        for(ci = filelist; ci != 0; ci = ci->next) {
+        for (copyinfo& ci : filelist) {
             unsigned int timestamp, mode, size;
             if (!sync_finish_stat(sc, &timestamp, &mode, &size)) return false;
-            if (size == ci->size) {
+            if (size == ci.size) {
                 /* for links, we cannot update the atime/mtime */
-                if ((S_ISREG(ci->mode & mode) && timestamp == ci->time) ||
-                        (S_ISLNK(ci->mode & mode) && timestamp >= ci->time)) {
-                    ci->flag = 1;
+                if ((S_ISREG(ci.mode & mode) && timestamp == ci.time) ||
+                        (S_ISLNK(ci.mode & mode) && timestamp >= ci.time)) {
+                    ci.skip = true;
                 }
             }
         }
     }
-    for (ci = filelist; ci != 0; ci = next) {
-        next = ci->next;
-        if (ci->flag == 0) {
-            fprintf(stderr, "%spush: %s -> %s\n", list_only ? "would " : "", ci->src, ci->dst);
-            if (!list_only && !sync_send(sc, ci->src, ci->dst, ci->time, ci->mode, false)) {
-                return false;
+
+    for (const copyinfo& ci : filelist) {
+        if (!ci.skip) {
+            if (list_only) {
+                fprintf(stderr, "would push: %s -> %s\n", ci.src.c_str(),
+                        ci.dst.c_str());
+            } else {
+                if (!sync_send(sc, ci.src.c_str(), ci.dst.c_str(), ci.time, ci.mode)) {
+                  return false;
+                }
             }
             pushed++;
         } else {
             skipped++;
         }
-        free(ci);
     }
 
-    fprintf(stderr, "%d file%s pushed. %d file%s skipped.\n",
-            pushed, (pushed == 1) ? "" : "s",
-            skipped, (skipped == 1) ? "" : "s");
-
+    sc.Printf("%s: %d file%s pushed. %d file%s skipped.%s\n", rpath, pushed,
+              (pushed == 1) ? "" : "s", skipped, (skipped == 1) ? "" : "s",
+              sc.TransferRate().c_str());
     return true;
 }
 
-bool do_sync_push(const char* lpath, const char* rpath, bool show_progress) {
+bool do_sync_push(const std::vector<const char*>& srcs, const char* dst) {
     SyncConnection sc;
     if (!sc.IsValid()) return false;
 
-    struct stat st;
-    if (stat(lpath, &st)) {
-        fprintf(stderr, "cannot stat '%s': %s\n", lpath, strerror(errno));
-        return false;
-    }
-
-    if (S_ISDIR(st.st_mode)) {
-        return copy_local_dir_remote(sc, lpath, rpath, false, false);
-    }
-
+    bool success = true;
     unsigned mode;
-    if (!sync_stat(sc, rpath, nullptr, &mode, nullptr)) return false;
-    std::string path_holder;
-    if (mode != 0 && S_ISDIR(mode)) {
-        // If we're copying a local file to a remote directory,
-        // we really want to copy to remote_dir + "/" + local_filename.
-        path_holder = android::base::StringPrintf("%s/%s", rpath, adb_basename(lpath).c_str());
-        rpath = path_holder.c_str();
+    if (!sync_stat(sc, dst, nullptr, &mode, nullptr)) return false;
+    bool dst_isdir = mode != 0 && S_ISDIR(mode);
+
+    if (!dst_isdir) {
+        if (srcs.size() > 1) {
+            sc.Error("target '%s' is not a directory", dst);
+            return false;
+        } else {
+            size_t dst_len = strlen(dst);
+            if (dst[dst_len - 1] == '/') {
+                sc.Error("failed to access '%s': Not a directory", dst);
+                return false;
+            }
+        }
     }
-    return sync_send(sc, lpath, rpath, st.st_mtime, st.st_mode, show_progress);
+
+    for (const char* src_path : srcs) {
+        const char* dst_path = dst;
+        struct stat st;
+        if (stat(src_path, &st)) {
+            sc.Error("cannot stat '%s': %s", src_path, strerror(errno));
+            success = false;
+            continue;
+        }
+
+        if (S_ISDIR(st.st_mode)) {
+            success &= copy_local_dir_remote(sc, src_path, dst, false, false);
+            continue;
+        }
+
+        std::string path_holder;
+        if (mode != 0 && S_ISDIR(mode)) {
+            // If we're copying a local file to a remote directory,
+            // we really want to copy to remote_dir + "/" + local_filename.
+            path_holder = android::base::StringPrintf(
+                "%s/%s", dst_path, adb_basename(src_path).c_str());
+            dst_path = path_holder.c_str();
+        }
+        success &= sync_send(sc, src_path, dst_path, st.st_mtime, st.st_mode);
+    }
+
+    sc.Print("\n");
+    return success;
 }
 
-
-struct sync_ls_build_list_cb_args {
-    copyinfo **filelist;
-    copyinfo **dirlist;
-    const char *rpath;
-    const char *lpath;
-};
-
-static void sync_ls_build_list_cb(unsigned mode, unsigned size, unsigned time,
-                                  const char* name, void* cookie)
-{
-    sync_ls_build_list_cb_args *args = (sync_ls_build_list_cb_args *)cookie;
-    copyinfo *ci;
-
-    if (S_ISDIR(mode)) {
-        copyinfo **dirlist = args->dirlist;
-
-        // Don't try recursing down "." or "..".
-        if (IsDotOrDotDot(name)) return;
-
-        ci = mkcopyinfo(args->rpath, args->lpath, name, 1);
-        ci->next = *dirlist;
-        *dirlist = ci;
-    } else if (S_ISREG(mode) || S_ISLNK(mode)) {
-        copyinfo **filelist = args->filelist;
-
-        ci = mkcopyinfo(args->rpath, args->lpath, name, 0);
-        ci->time = time;
-        ci->mode = mode;
-        ci->size = size;
-        ci->next = *filelist;
-        *filelist = ci;
-    } else {
-        fprintf(stderr, "skipping special file '%s'\n", name);
-    }
-}
-
-static bool remote_build_list(SyncConnection& sc, copyinfo **filelist,
-                              const char *rpath, const char *lpath) {
-    copyinfo *dirlist = NULL;
-    sync_ls_build_list_cb_args args;
-
-    args.filelist = filelist;
-    args.dirlist = &dirlist;
-    args.rpath = rpath;
-    args.lpath = lpath;
+static bool remote_build_list(SyncConnection& sc,
+                              std::vector<copyinfo>* filelist,
+                              const char* rpath, const char* lpath) {
+    std::vector<copyinfo> dirlist;
 
     // Put the files/dirs in rpath on the lists.
-    if (!sync_ls(sc, rpath, sync_ls_build_list_cb, (void *)&args)) {
+    auto callback = [&](unsigned mode, unsigned size, unsigned time,
+                        const char* name) {
+        if (S_ISDIR(mode)) {
+            // Don't try recursing down "." or "..".
+            if (IsDotOrDotDot(name)) return;
+
+            dirlist.push_back(mkcopyinfo(rpath, lpath, name, 1));
+        } else if (S_ISREG(mode) || S_ISLNK(mode)) {
+            copyinfo ci = mkcopyinfo(rpath, lpath, name, 0);
+            ci.time = time;
+            ci.mode = mode;
+            ci.size = size;
+            filelist->push_back(ci);
+        } else {
+            sc.Print(android::base::StringPrintf("skipping special file '%s'\n",
+                                                 name));
+        }
+    };
+
+    if (!sync_ls(sc, rpath, callback)) {
         return false;
     }
 
     // Recurse into each directory we found.
-    while (dirlist != NULL) {
-        copyinfo *next = dirlist->next;
-        if (!remote_build_list(sc, filelist, dirlist->src, dirlist->dst)) {
+    while (!dirlist.empty()) {
+        copyinfo current = dirlist.back();
+        dirlist.pop_back();
+        if (!remote_build_list(sc, filelist, current.src.c_str(),
+                               current.dst.c_str())) {
             return false;
         }
-        free(dirlist);
-        dirlist = next;
     }
 
     return true;
@@ -698,7 +704,7 @@
     int r1 = utime(lpath, &times);
 
     /* use umask for permissions */
-    mode_t mask=umask(0000);
+    mode_t mask = umask(0000);
     umask(mask);
     int r2 = chmod(lpath, mode & ~mask);
 
@@ -706,7 +712,7 @@
 }
 
 static bool copy_remote_dir_local(SyncConnection& sc, const char* rpath, const char* lpath,
-                                  int copy_attrs) {
+                                  bool copy_attrs) {
     // Make sure that both directory paths end in a slash.
     std::string rpath_clean(rpath);
     std::string lpath_clean(lpath);
@@ -715,79 +721,115 @@
     if (lpath_clean.back() != '/') lpath_clean.push_back('/');
 
     // Recursively build the list of files to copy.
-    fprintf(stderr, "pull: building file list...\n");
-    copyinfo* filelist = nullptr;
-    if (!remote_build_list(sc, &filelist, rpath_clean.c_str(), lpath_clean.c_str())) return false;
+    sc.Print("pull: building file list...");
+    std::vector<copyinfo> filelist;
+    if (!remote_build_list(sc, &filelist, rpath_clean.c_str(),
+                           lpath_clean.c_str())) {
+        return false;
+    }
 
     int pulled = 0;
     int skipped = 0;
-    copyinfo* ci = filelist;
-    while (ci) {
-        copyinfo* next = ci->next;
-        if (ci->flag == 0) {
-            fprintf(stderr, "pull: %s -> %s\n", ci->src, ci->dst);
-            if (!sync_recv(sc, ci->src, ci->dst, false)) {
+    for (const copyinfo &ci : filelist) {
+        if (!ci.skip) {
+            sc.Printf("pull: %s -> %s", ci.src.c_str(), ci.dst.c_str());
+            if (!sync_recv(sc, ci.src.c_str(), ci.dst.c_str())) {
                 return false;
             }
 
-            if (copy_attrs && set_time_and_mode(ci->dst, ci->time, ci->mode)) {
+            if (copy_attrs &&
+                set_time_and_mode(ci.dst.c_str(), ci.time, ci.mode)) {
                 return false;
             }
             pulled++;
         } else {
             skipped++;
         }
-        free(ci);
-        ci = next;
     }
 
-    fprintf(stderr, "%d file%s pulled. %d file%s skipped.\n",
-            pulled, (pulled == 1) ? "" : "s",
-            skipped, (skipped == 1) ? "" : "s");
+    sc.Printf("%s: %d file%s pulled. %d file%s skipped.%s\n", rpath, pulled,
+              (pulled == 1) ? "" : "s", skipped, (skipped == 1) ? "" : "s",
+              sc.TransferRate().c_str());
     return true;
 }
 
-bool do_sync_pull(const char* rpath, const char* lpath, bool show_progress, int copy_attrs) {
+bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst,
+                  bool copy_attrs) {
     SyncConnection sc;
     if (!sc.IsValid()) return false;
 
+    bool success = true;
     unsigned mode, time;
-    if (!sync_stat(sc, rpath, &time, &mode, nullptr)) return false;
-    if (mode == 0) {
-        fprintf(stderr, "remote object '%s' does not exist\n", rpath);
-        return false;
+    struct stat st;
+    if (stat(dst, &st)) {
+        // If we're only pulling one file, the destination path might point to
+        // a path that doesn't exist yet.
+        if (srcs.size() != 1 || errno != ENOENT) {
+            sc.Error("cannot stat '%s': %s", dst, strerror(errno));
+            return false;
+        }
     }
 
-    if (S_ISREG(mode) || S_ISLNK(mode) || S_ISCHR(mode) || S_ISBLK(mode)) {
-        std::string path_holder;
-        struct stat st;
-        if (stat(lpath, &st) == 0) {
-            if (S_ISDIR(st.st_mode)) {
-                // If we're copying a remote file to a local directory,
-                // we really want to copy to local_dir + "/" + basename(remote).
-                path_holder = android::base::StringPrintf("%s/%s", lpath, adb_basename(rpath).c_str());
-                lpath = path_holder.c_str();
-            }
-        }
-        if (!sync_recv(sc, rpath, lpath, show_progress)) {
+    bool dst_isdir = S_ISDIR(st.st_mode);
+    if (!dst_isdir) {
+        if (srcs.size() > 1) {
+            sc.Error("target '%s' is not a directory", dst);
             return false;
         } else {
-            if (copy_attrs && set_time_and_mode(lpath, time, mode)) {
+            size_t dst_len = strlen(dst);
+            if (dst[dst_len - 1] == '/') {
+                sc.Error("failed to access '%s': Not a directory", dst);
                 return false;
             }
         }
-        return true;
-    } else if (S_ISDIR(mode)) {
-        return copy_remote_dir_local(sc, rpath, lpath, copy_attrs);
     }
 
-    fprintf(stderr, "remote object '%s' not a file or directory\n", rpath);
-    return false;
+    for (const char* src_path : srcs) {
+        const char* dst_path = dst;
+        if (!sync_stat(sc, src_path, &time, &mode, nullptr)) return false;
+        if (mode == 0) {
+            sc.Error("remote object '%s' does not exist", src_path);
+            success = false;
+            continue;
+        }
+
+        if (S_ISREG(mode) || S_ISLNK(mode) || S_ISCHR(mode) || S_ISBLK(mode)) {
+            std::string path_holder;
+            struct stat st;
+            if (stat(dst_path, &st) == 0) {
+                if (S_ISDIR(st.st_mode)) {
+                    // If we're copying a remote file to a local directory,
+                    // we really want to copy to local_dir + "/" +
+                    // basename(remote).
+                    path_holder = android::base::StringPrintf(
+                        "%s/%s", dst_path, adb_basename(src_path).c_str());
+                    dst_path = path_holder.c_str();
+                }
+            }
+            if (!sync_recv(sc, src_path, dst_path)) {
+                success = false;
+                continue;
+            } else {
+                if (copy_attrs && set_time_and_mode(dst_path, time, mode)) {
+                    success = false;
+                    continue;
+                }
+            }
+        } else if (S_ISDIR(mode)) {
+            success &= copy_remote_dir_local(sc, src_path, dst_path, copy_attrs);
+            continue;
+        } else {
+            sc.Error("remote object '%s' not a file or directory", src_path);
+            success = false;
+            continue;
+        }
+    }
+
+    sc.Print("\n");
+    return success;
 }
 
 bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only) {
-    fprintf(stderr, "syncing %s...\n", rpath.c_str());
-
     SyncConnection sc;
     if (!sc.IsValid()) return false;
 
diff --git a/adb/file_sync_service.cpp b/adb/file_sync_service.cpp
index 298ed82..7484a7c 100644
--- a/adb/file_sync_service.cpp
+++ b/adb/file_sync_service.cpp
@@ -32,6 +32,7 @@
 
 #include "adb.h"
 #include "adb_io.h"
+#include "adb_utils.h"
 #include "private/android_filesystem_config.h"
 
 #include <base/stringprintf.h>
@@ -53,8 +54,6 @@
     if (path[0] != '/') return false;
 
     std::vector<std::string> path_components = android::base::Split(path, "/");
-    path_components.pop_back(); // For "/system/bin/sh", only create "/system/bin".
-
     std::string partial_path;
     for (const auto& path_component : path_components) {
         if (partial_path.back() != OS_PATH_SEPARATOR) partial_path += OS_PATH_SEPARATOR;
@@ -149,7 +148,7 @@
 
     int fd = adb_open_mode(path, O_WRONLY | O_CREAT | O_EXCL | O_CLOEXEC, mode);
     if (fd < 0 && errno == ENOENT) {
-        if (!secure_mkdirs(path)) {
+        if (!secure_mkdirs(adb_dirname(path))) {
             SendSyncFailErrno(s, "secure_mkdirs failed");
             goto fail;
         }
@@ -244,7 +243,7 @@
 
     ret = symlink(&buffer[0], path.c_str());
     if (ret && errno == ENOENT) {
-        if (!secure_mkdirs(path)) {
+        if (!secure_mkdirs(adb_dirname(path))) {
             SendSyncFailErrno(s, "secure_mkdirs failed");
             return false;
         }
diff --git a/adb/file_sync_service.h b/adb/file_sync_service.h
index 67ed3fc..38382c1 100644
--- a/adb/file_sync_service.h
+++ b/adb/file_sync_service.h
@@ -18,6 +18,7 @@
 #define _FILE_SYNC_SERVICE_H_
 
 #include <string>
+#include <vector>
 
 #define MKID(a,b,c,d) ((a) | ((b) << 8) | ((c) << 16) | ((d) << 24))
 
@@ -64,9 +65,11 @@
 
 void file_sync_service(int fd, void* cookie);
 bool do_sync_ls(const char* path);
-bool do_sync_push(const char* lpath, const char* rpath, bool show_progress);
+bool do_sync_push(const std::vector<const char*>& srcs, const char* dst);
+bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst,
+                  bool copy_attrs);
+
 bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only);
-bool do_sync_pull(const char* rpath, const char* lpath, bool show_progress, int copy_attrs);
 
 #define SYNC_DATA_MAX (64*1024)
 
diff --git a/adb/line_printer.cpp b/adb/line_printer.cpp
new file mode 100644
index 0000000..aa332f7
--- /dev/null
+++ b/adb/line_printer.cpp
@@ -0,0 +1,161 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// 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 "line_printer.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <sys/time.h>
+#endif
+
+// Make sure printf is really adb_printf which works for UTF-8 on Windows.
+#include <sysdeps.h>
+
+// Stuff from ninja's util.h that's needed below.
+#include <vector>
+using namespace std;
+string ElideMiddle(const string& str, size_t width) {
+  const int kMargin = 3;  // Space for "...".
+  string result = str;
+  if (result.size() + kMargin > width) {
+    size_t elide_size = (width - kMargin) / 2;
+    result = result.substr(0, elide_size)
+      + "..."
+      + result.substr(result.size() - elide_size, elide_size);
+  }
+  return result;
+}
+
+LinePrinter::LinePrinter() : have_blank_line_(true), console_locked_(false) {
+#ifndef _WIN32
+  const char* term = getenv("TERM");
+  smart_terminal_ = unix_isatty(1) && term && string(term) != "dumb";
+#else
+  // Disable output buffer.  It'd be nice to use line buffering but
+  // MSDN says: "For some systems, [_IOLBF] provides line
+  // buffering. However, for Win32, the behavior is the same as _IOFBF
+  // - Full Buffering."
+  setvbuf(stdout, NULL, _IONBF, 0);
+  console_ = GetStdHandle(STD_OUTPUT_HANDLE);
+  CONSOLE_SCREEN_BUFFER_INFO csbi;
+  smart_terminal_ = GetConsoleScreenBufferInfo(console_, &csbi);
+#endif
+}
+
+void LinePrinter::Print(string to_print, LineType type) {
+  if (console_locked_) {
+    line_buffer_ = to_print;
+    line_type_ = type;
+    return;
+  }
+
+  if (smart_terminal_) {
+    printf("\r");  // Print over previous line, if any.
+    // On Windows, calling a C library function writing to stdout also handles
+    // pausing the executable when the "Pause" key or Ctrl-S is pressed.
+  }
+
+  if (smart_terminal_ && type == ELIDE) {
+#ifdef _WIN32
+    CONSOLE_SCREEN_BUFFER_INFO csbi;
+    GetConsoleScreenBufferInfo(console_, &csbi);
+
+    // TODO: const std::wstring to_print_wide = widen(to_print);
+    // TODO: wstring ElideMiddle.
+    to_print = ElideMiddle(to_print, static_cast<size_t>(csbi.dwSize.X));
+    // We don't want to have the cursor spamming back and forth, so instead of
+    // printf use WriteConsoleOutput which updates the contents of the buffer,
+    // but doesn't move the cursor position.
+    COORD buf_size = { csbi.dwSize.X, 1 };
+    COORD zero_zero = { 0, 0 };
+    SMALL_RECT target = {
+      csbi.dwCursorPosition.X, csbi.dwCursorPosition.Y,
+      static_cast<SHORT>(csbi.dwCursorPosition.X + csbi.dwSize.X - 1),
+      csbi.dwCursorPosition.Y
+    };
+    vector<CHAR_INFO> char_data(csbi.dwSize.X);
+    for (size_t i = 0; i < static_cast<size_t>(csbi.dwSize.X); ++i) {
+      // TODO: UnicodeChar instead of AsciiChar, to_print_wide[i].
+      char_data[i].Char.AsciiChar = i < to_print.size() ? to_print[i] : ' ';
+      char_data[i].Attributes = csbi.wAttributes;
+    }
+    // TODO: WriteConsoleOutputW.
+    WriteConsoleOutput(console_, &char_data[0], buf_size, zero_zero, &target);
+#else
+    // Limit output to width of the terminal if provided so we don't cause
+    // line-wrapping.
+    winsize size;
+    if ((ioctl(0, TIOCGWINSZ, &size) == 0) && size.ws_col) {
+      to_print = ElideMiddle(to_print, size.ws_col);
+    }
+    printf("%s", to_print.c_str());
+    printf("\x1B[K");  // Clear to end of line.
+    fflush(stdout);
+#endif
+
+    have_blank_line_ = false;
+  } else {
+    printf("%s\n", to_print.c_str());
+  }
+}
+
+void LinePrinter::PrintOrBuffer(const char* data, size_t size) {
+  if (console_locked_) {
+    output_buffer_.append(data, size);
+  } else {
+    // Avoid printf and C strings, since the actual output might contain null
+    // bytes like UTF-16 does (yuck).
+    fwrite(data, 1, size, stdout);
+  }
+}
+
+void LinePrinter::PrintOnNewLine(const string& to_print) {
+  if (console_locked_ && !line_buffer_.empty()) {
+    output_buffer_.append(line_buffer_);
+    output_buffer_.append(1, '\n');
+    line_buffer_.clear();
+  }
+  if (!have_blank_line_) {
+    PrintOrBuffer("\n", 1);
+  }
+  if (!to_print.empty()) {
+    PrintOrBuffer(&to_print[0], to_print.size());
+  }
+  have_blank_line_ = to_print.empty() || *to_print.rbegin() == '\n';
+}
+
+void LinePrinter::SetConsoleLocked(bool locked) {
+  if (locked == console_locked_)
+    return;
+
+  if (locked)
+    PrintOnNewLine("");
+
+  console_locked_ = locked;
+
+  if (!locked) {
+    PrintOnNewLine(output_buffer_);
+    if (!line_buffer_.empty()) {
+      Print(line_buffer_, line_type_);
+    }
+    output_buffer_.clear();
+    line_buffer_.clear();
+  }
+}
diff --git a/adb/line_printer.h b/adb/line_printer.h
new file mode 100644
index 0000000..3d0a5bd
--- /dev/null
+++ b/adb/line_printer.h
@@ -0,0 +1,71 @@
+// Copyright 2013 Google Inc. All Rights Reserved.
+//
+// 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 NINJA_LINE_PRINTER_H_
+#define NINJA_LINE_PRINTER_H_
+
+#include <stddef.h>
+#include <string>
+
+/// Prints lines of text, possibly overprinting previously printed lines
+/// if the terminal supports it.
+struct LinePrinter {
+  LinePrinter();
+
+  bool is_smart_terminal() const { return smart_terminal_; }
+  void set_smart_terminal(bool smart) { smart_terminal_ = smart; }
+
+  enum LineType {
+    FULL,
+    ELIDE
+  };
+  /// Overprints the current line. If type is ELIDE, elides to_print to fit on
+  /// one line.
+  void Print(std::string to_print, LineType type);
+
+  /// Prints a string on a new line, not overprinting previous output.
+  void PrintOnNewLine(const std::string& to_print);
+
+  /// Lock or unlock the console.  Any output sent to the LinePrinter while the
+  /// console is locked will not be printed until it is unlocked.
+  void SetConsoleLocked(bool locked);
+
+ private:
+  /// Whether we can do fancy terminal control codes.
+  bool smart_terminal_;
+
+  /// Whether the caret is at the beginning of a blank line.
+  bool have_blank_line_;
+
+  /// Whether console is locked.
+  bool console_locked_;
+
+  /// Buffered current line while console is locked.
+  std::string line_buffer_;
+
+  /// Buffered line type while console is locked.
+  LineType line_type_;
+
+  /// Buffered console output while console is locked.
+  std::string output_buffer_;
+
+#ifdef _WIN32
+  void* console_;
+#endif
+
+  /// Print the given data to the console, or buffer it if it is locked.
+  void PrintOrBuffer(const char *data, size_t size);
+};
+
+#endif  // NINJA_LINE_PRINTER_H_
diff --git a/adb/services.cpp b/adb/services.cpp
index e24b470..19a6726 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -184,15 +184,18 @@
     adb_close(fd);
 }
 
-void reverse_service(int fd, void* arg)
-{
-    const char* command = reinterpret_cast<const char*>(arg);
-
-    if (handle_forward_request(command, kTransportAny, NULL, fd) < 0) {
-        SendFail(fd, "not a reverse forwarding command");
+int reverse_service(const char* command) {
+    int s[2];
+    if (adb_socketpair(s)) {
+        PLOG(ERROR) << "cannot create service socket pair.";
+        return -1;
     }
-    free(arg);
-    adb_close(fd);
+    VLOG(SERVICES) << "service socketpair: " << s[0] << ", " << s[1];
+    if (handle_forward_request(command, kTransportAny, nullptr, s[1]) < 0) {
+        SendFail(s[1], "not a reverse forwarding command");
+    }
+    adb_close(s[1]);
+    return s[0];
 }
 
 // Shell service string can look like:
@@ -335,15 +338,7 @@
     } else if(!strncmp(name, "usb:", 4)) {
         ret = create_service_thread(restart_usb_service, NULL);
     } else if (!strncmp(name, "reverse:", 8)) {
-        char* cookie = strdup(name + 8);
-        if (cookie == NULL) {
-            ret = -1;
-        } else {
-            ret = create_service_thread(reverse_service, cookie);
-            if (ret < 0) {
-                free(cookie);
-            }
-        }
+        ret = reverse_service(name + 8);
     } else if(!strncmp(name, "disable-verity:", 15)) {
         ret = create_service_thread(set_verity_enabled_state_service, (void*)0);
     } else if(!strncmp(name, "enable-verity:", 15)) {
diff --git a/adb/set_verity_enable_state_service.cpp b/adb/set_verity_enable_state_service.cpp
index fd1740d..f5188e9 100644
--- a/adb/set_verity_enable_state_service.cpp
+++ b/adb/set_verity_enable_state_service.cpp
@@ -28,10 +28,11 @@
 
 #include "adb.h"
 #include "adb_io.h"
-#include "ext4_sb.h"
 #include "fs_mgr.h"
 #include "remount_service.h"
 
+#include "fec/io.h"
+
 #define FSTAB_PREFIX "/fstab."
 struct fstab *fstab;
 
@@ -41,115 +42,50 @@
 static const bool kAllowDisableVerity = false;
 #endif
 
-static int get_target_device_size(int fd, const char *blk_device,
-                                  uint64_t *device_size)
-{
-    int data_device;
-    struct ext4_super_block sb;
-    struct fs_info info;
-
-    info.len = 0;  /* Only len is set to 0 to ask the device for real size. */
-
-    data_device = adb_open(blk_device, O_RDONLY | O_CLOEXEC);
-    if (data_device < 0) {
-        WriteFdFmt(fd, "Error opening block device (%s)\n", strerror(errno));
-        return -1;
-    }
-
-    if (lseek64(data_device, 1024, SEEK_SET) < 0) {
-        WriteFdFmt(fd, "Error seeking to superblock\n");
-        adb_close(data_device);
-        return -1;
-    }
-
-    if (adb_read(data_device, &sb, sizeof(sb)) != sizeof(sb)) {
-        WriteFdFmt(fd, "Error reading superblock\n");
-        adb_close(data_device);
-        return -1;
-    }
-
-    ext4_parse_sb(&sb, &info);
-    *device_size = info.len;
-
-    adb_close(data_device);
-    return 0;
-}
-
 /* Turn verity on/off */
 static int set_verity_enabled_state(int fd, const char *block_device,
                                     const char* mount_point, bool enable)
 {
-    uint32_t magic_number;
-    const uint32_t new_magic = enable ? VERITY_METADATA_MAGIC_NUMBER
-                                      : VERITY_METADATA_MAGIC_DISABLE;
-    uint64_t device_length = 0;
-    int device = -1;
-    int retval = -1;
-
     if (!make_block_device_writable(block_device)) {
         WriteFdFmt(fd, "Could not make block device %s writable (%s).\n",
                    block_device, strerror(errno));
-        goto errout;
+        return -1;
     }
 
-    device = adb_open(block_device, O_RDWR | O_CLOEXEC);
-    if (device == -1) {
+    fec::io fh(block_device, O_RDWR);
+
+    if (!fh) {
         WriteFdFmt(fd, "Could not open block device %s (%s).\n", block_device, strerror(errno));
-        WriteFdFmt(fd, "Maybe run adb remount?\n");
-        goto errout;
+        WriteFdFmt(fd, "Maybe run adb root?\n");
+        return -1;
     }
 
-    // find the start of the verity metadata
-    if (get_target_device_size(fd, (char*)block_device, &device_length) < 0) {
-        WriteFdFmt(fd, "Could not get target device size.\n");
-        goto errout;
+    fec_verity_metadata metadata;
+
+    if (!fh.get_verity_metadata(metadata)) {
+        WriteFdFmt(fd, "Couldn't find verity metadata!\n");
+        return -1;
     }
 
-    if (lseek64(device, device_length, SEEK_SET) < 0) {
-        WriteFdFmt(fd, "Could not seek to start of verity metadata block.\n");
-        goto errout;
-    }
-
-    // check the magic number
-    if (adb_read(device, &magic_number, sizeof(magic_number)) != sizeof(magic_number)) {
-        WriteFdFmt(fd, "Couldn't read magic number!\n");
-        goto errout;
-    }
-
-    if (!enable && magic_number == VERITY_METADATA_MAGIC_DISABLE) {
+    if (!enable && metadata.disabled) {
         WriteFdFmt(fd, "Verity already disabled on %s\n", mount_point);
-        goto errout;
+        return -1;
     }
 
-    if (enable && magic_number == VERITY_METADATA_MAGIC_NUMBER) {
+    if (enable && !metadata.disabled) {
         WriteFdFmt(fd, "Verity already enabled on %s\n", mount_point);
-        goto errout;
+        return -1;
     }
 
-    if (magic_number != VERITY_METADATA_MAGIC_NUMBER
-            && magic_number != VERITY_METADATA_MAGIC_DISABLE) {
-        WriteFdFmt(fd, "Couldn't find verity metadata at offset %" PRIu64 "!\n", device_length);
-        goto errout;
-    }
-
-    if (lseek64(device, device_length, SEEK_SET) < 0) {
-        WriteFdFmt(fd, "Could not seek to start of verity metadata block.\n");
-        goto errout;
-    }
-
-    if (adb_write(device, &new_magic, sizeof(new_magic)) != sizeof(new_magic)) {
+    if (!fh.set_verity_status(enable)) {
         WriteFdFmt(fd, "Could not set verity %s flag on device %s with error %s\n",
                    enable ? "enabled" : "disabled",
                    block_device, strerror(errno));
-        goto errout;
+        return -1;
     }
 
     WriteFdFmt(fd, "Verity %s on %s\n", enable ? "enabled" : "disabled", mount_point);
-    retval = 0;
-errout:
-    if (device != -1)
-        adb_close(device);
-    return retval;
+    return 0;
 }
 
 void set_verity_enabled_state_service(int fd, void* cookie)
diff --git a/adb/shell_service.cpp b/adb/shell_service.cpp
index 544afce..e3fde26 100644
--- a/adb/shell_service.cpp
+++ b/adb/shell_service.cpp
@@ -83,6 +83,7 @@
 
 #include <errno.h>
 #include <pty.h>
+#include <pwd.h>
 #include <sys/select.h>
 #include <termios.h>
 
@@ -281,6 +282,15 @@
         parent_error_sfd.Reset();
         close_on_exec(child_error_sfd.fd());
 
+        // TODO: $HOSTNAME? Normally bash automatically sets that, but mksh doesn't.
+        passwd* pw = getpwuid(getuid());
+        if (pw != nullptr) {
+            setenv("HOME", pw->pw_dir, 1);
+            setenv("LOGNAME", pw->pw_name, 1);
+            setenv("SHELL", pw->pw_shell, 1);
+            setenv("USER", pw->pw_name, 1);
+        }
+
         if (is_interactive()) {
             execl(_PATH_BSHELL, _PATH_BSHELL, "-", nullptr);
         } else {
@@ -503,6 +513,18 @@
 
         if (stdinout_sfd_.valid()) {
             switch (input_->id()) {
+                case ShellProtocol::kIdWindowSizeChange:
+                    int rows, cols, x_pixels, y_pixels;
+                    if (sscanf(input_->data(), "%dx%d,%dx%d",
+                               &rows, &cols, &x_pixels, &y_pixels) == 4) {
+                        winsize ws;
+                        ws.ws_row = rows;
+                        ws.ws_col = cols;
+                        ws.ws_xpixel = x_pixels;
+                        ws.ws_ypixel = y_pixels;
+                        ioctl(stdinout_sfd_.fd(), TIOCSWINSZ, &ws);
+                    }
+                    break;
                 case ShellProtocol::kIdStdin:
                     input_bytes_left_ = input_->data_length();
                     break;
@@ -521,8 +543,7 @@
                         // non-interactively which is rare and unsupported.
                         // If necessary, the client can manually close the shell
                         // with `exit` or by killing the adb client process.
-                        D("can't close input for PTY FD %d",
-                          stdinout_sfd_.fd());
+                        D("can't close input for PTY FD %d", stdinout_sfd_.fd());
                     }
                     break;
             }
diff --git a/adb/shell_service.h b/adb/shell_service.h
index 01410a9..63c00da 100644
--- a/adb/shell_service.h
+++ b/adb/shell_service.h
@@ -54,8 +54,15 @@
         kIdStdout = 1,
         kIdStderr = 2,
         kIdExit = 3,
-        kIdCloseStdin = 4,  // Close subprocess stdin if possible.
-        kIdInvalid = 255,  // Indicates an invalid or unknown packet.
+
+        // Close subprocess stdin if possible.
+        kIdCloseStdin = 4,
+
+        // Window size change (an ASCII version of struct winsize).
+        kIdWindowSizeChange = 5,
+
+        // Indicates an invalid or unknown packet.
+        kIdInvalid = 255,
     };
 
     // ShellPackets will probably be too large to allocate on the stack so they
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 501a75a..1735627 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -181,6 +181,17 @@
 extern int unix_open(const char* path, int options, ...);
 #define  open    ___xxx_unix_open
 
+// Checks if |fd| corresponds to a console.
+// Standard Windows isatty() returns 1 for both console FDs and character
+// devices like NUL. unix_isatty() performs some extra checking to only match
+// console FDs.
+// |fd| must be a real file descriptor, meaning STDxx_FILENO or unix_open() FDs
+// will work but adb_open() FDs will not. Additionally the OS handle associated
+// with |fd| must have GENERIC_READ access (which console FDs have by default).
+// Returns 1 if |fd| is a console FD, 0 otherwise. The value of errno after
+// calling this function is unreliable and should not be used.
+int unix_isatty(int fd);
+#define  isatty  ___xxx_isatty
 
 /* normally provided by <cutils/misc.h> */
 extern void*  load_file(const char*  pathname, unsigned*  psize);
@@ -319,6 +330,9 @@
 
 #define getcwd adb_getcwd
 
+char* adb_strerror(int err);
+#define strerror adb_strerror
+
 // Convert from UTF-8 to UTF-16, typically used to convert char strings into
 // wchar_t strings that can be passed to wchar_t-based OS and C Runtime APIs
 // on Windows.
@@ -548,6 +562,11 @@
 #undef   creat
 #define  creat  ___xxx_creat
 
+static __inline__ int unix_isatty(int fd) {
+    return isatty(fd);
+}
+#define  isatty  ___xxx_isatty
+
 // Helper for network_* functions.
 inline int _fd_set_error_str(int fd, std::string* error) {
   if (fd == -1) {
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index 994b851..e325889 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -556,6 +556,81 @@
     return 0;
 }
 
+// Overrides strerror() to handle error codes not supported by the Windows C
+// Runtime (MSVCRT.DLL).
+char* adb_strerror(int err) {
+    // sysdeps.h defines strerror to adb_strerror, but in this function, we
+    // want to call the real C Runtime strerror().
+#pragma push_macro("strerror")
+#undef strerror
+    const int saved_err = errno;      // Save because we overwrite it later.
+
+    // Lookup the string for an unknown error.
+    char* errmsg = strerror(-1);
+    const std::string unknown_error = (errmsg == nullptr) ? "" : errmsg;
+
+    // Lookup the string for this error to see if the C Runtime has it.
+    errmsg = strerror(err);
+    if (errmsg != nullptr && unknown_error != errmsg) {
+        // The CRT returned an error message and it is different than the error
+        // message for an unknown error, so it is probably valid, so use it.
+    } else {
+        // Check if we have a string for this error code.
+        const char* custom_msg = nullptr;
+        switch (err) {
+#pragma push_macro("ERR")
+#undef ERR
+#define ERR(errnum, desc) case errnum: custom_msg = desc; break
+            // These error strings are from AOSP bionic/libc/include/sys/_errdefs.h.
+            // Note that these cannot be longer than 94 characters because we
+            // pass this to _strerror() which has that requirement.
+            ERR(ECONNRESET,    "Connection reset by peer");
+            ERR(EHOSTUNREACH,  "No route to host");
+            ERR(ENETDOWN,      "Network is down");
+            ERR(ENETRESET,     "Network dropped connection because of reset");
+            ERR(ENOBUFS,       "No buffer space available");
+            ERR(ENOPROTOOPT,   "Protocol not available");
+            ERR(ENOTCONN,      "Transport endpoint is not connected");
+            ERR(ENOTSOCK,      "Socket operation on non-socket");
+            ERR(EOPNOTSUPP,    "Operation not supported on transport endpoint");
+#pragma pop_macro("ERR")
+        }
+
+        if (custom_msg != nullptr) {
+            // Use _strerror() to write our string into the writable per-thread
+            // buffer used by strerror()/_strerror(). _strerror() appends the
+            // msg for the current value of errno, so set errno to a consistent
+            // value for every call so that our code-path is always the same.
+            errno = 0;
+            errmsg = _strerror(custom_msg);
+            const size_t custom_msg_len = strlen(custom_msg);
+            // Just in case _strerror() returned a read-only string, check if
+            // the returned string starts with our custom message because that
+            // implies that the string is not read-only.
+            if ((errmsg != nullptr) &&
+                !strncmp(custom_msg, errmsg, custom_msg_len)) {
+                // _strerror() puts other text after our custom message, so
+                // remove that by terminating after our message.
+                errmsg[custom_msg_len] = '\0';
+            } else {
+                // For some reason nullptr was returned or a pointer to a
+                // read-only string was returned, so fallback to whatever
+                // strerror() can muster (probably "Unknown error" or some
+                // generic CRT error string).
+                errmsg = strerror(err);
+            }
+        } else {
+            // We don't have a custom message, so use whatever strerror(err)
+            // returned earlier.
+        }
+    }
+
+    errno = saved_err;  // restore
+
+    return errmsg;
+#pragma pop_macro("strerror")
+}
+
 /**************************************************************************/
 /**************************************************************************/
 /*****                                                                *****/
@@ -567,18 +642,38 @@
 #undef setsockopt
 
 static void _socket_set_errno( const DWORD err ) {
-    // The Windows C Runtime (MSVCRT.DLL) strerror() does not support a lot of
-    // POSIX and socket error codes, so this can only meaningfully map so much.
+    // Because the Windows C Runtime (MSVCRT.DLL) strerror() does not support a
+    // lot of POSIX and socket error codes, some of the resulting error codes
+    // are mapped to strings by adb_strerror() above.
     switch ( err ) {
     case 0:              errno = 0; break;
+    // Don't map WSAEINTR since that is only for Winsock 1.1 which we don't use.
+    // case WSAEINTR:    errno = EINTR; break;
+    case WSAEFAULT:      errno = EFAULT; break;
+    case WSAEINVAL:      errno = EINVAL; break;
+    case WSAEMFILE:      errno = EMFILE; break;
     // Mapping WSAEWOULDBLOCK to EAGAIN is absolutely critical because
     // non-blocking sockets can cause an error code of WSAEWOULDBLOCK and
     // callers check specifically for EAGAIN.
     case WSAEWOULDBLOCK: errno = EAGAIN; break;
-    case WSAEINTR:       errno = EINTR; break;
-    case WSAEFAULT:      errno = EFAULT; break;
-    case WSAEINVAL:      errno = EINVAL; break;
-    case WSAEMFILE:      errno = EMFILE; break;
+    case WSAENOTSOCK:    errno = ENOTSOCK; break;
+    case WSAENOPROTOOPT: errno = ENOPROTOOPT; break;
+    case WSAEOPNOTSUPP:  errno = EOPNOTSUPP; break;
+    case WSAENETDOWN:    errno = ENETDOWN; break;
+    case WSAENETRESET:   errno = ENETRESET; break;
+    // Map WSAECONNABORTED to EPIPE instead of ECONNABORTED because POSIX seems
+    // to use EPIPE for these situations and there are some callers that look
+    // for EPIPE.
+    case WSAECONNABORTED: errno = EPIPE; break;
+    case WSAECONNRESET:  errno = ECONNRESET; break;
+    case WSAENOBUFS:     errno = ENOBUFS; break;
+    case WSAENOTCONN:    errno = ENOTCONN; break;
+    // Don't map WSAETIMEDOUT because we don't currently use SO_RCVTIMEO or
+    // SO_SNDTIMEO which would cause WSAETIMEDOUT to be returned. Future
+    // considerations: Reportedly send() can return zero on timeout, and POSIX
+    // code may expect EAGAIN instead of ETIMEDOUT on timeout.
+    // case WSAETIMEDOUT: errno = ETIMEDOUT; break;
+    case WSAEHOSTUNREACH: errno = EHOSTUNREACH; break;
     default:
         errno = EINVAL;
         D( "_socket_set_errno: mapping Windows error code %lu to errno %d",
@@ -655,8 +750,12 @@
     int  result = send(f->fh_socket, reinterpret_cast<const char*>(buf), len, 0);
     if (result == SOCKET_ERROR) {
         const DWORD err = WSAGetLastError();
-        D("send fd %d failed: %s", _fh_to_int(f),
-          SystemErrorCodeToString(err).c_str());
+        // WSAEWOULDBLOCK is normal with a non-blocking socket, so don't trace
+        // that to reduce spam and confusion.
+        if (err != WSAEWOULDBLOCK) {
+            D("send fd %d failed: %s", _fh_to_int(f),
+              SystemErrorCodeToString(err).c_str());
+        }
         _socket_set_errno(err);
         result = -1;
     } else {
@@ -2400,10 +2499,52 @@
 //
 // Code organization:
 //
+// * _get_console_handle() and unix_isatty() provide console information.
 // * stdin_raw_init() and stdin_raw_restore() reconfigure the console.
 // * unix_read() detects console windows (as opposed to pipes, files, etc.).
 // * _console_read() is the main code of the emulation.
 
+// Returns a console HANDLE if |fd| is a console, otherwise returns nullptr.
+// If a valid HANDLE is returned and |mode| is not null, |mode| is also filled
+// with the console mode. Requires GENERIC_READ access to the underlying HANDLE.
+static HANDLE _get_console_handle(int fd, DWORD* mode=nullptr) {
+    // First check isatty(); this is very fast and eliminates most non-console
+    // FDs, but returns 1 for both consoles and character devices like NUL.
+#pragma push_macro("isatty")
+#undef isatty
+    if (!isatty(fd)) {
+        return nullptr;
+    }
+#pragma pop_macro("isatty")
+
+    // To differentiate between character devices and consoles we need to get
+    // the underlying HANDLE and use GetConsoleMode(), which is what requires
+    // GENERIC_READ permissions.
+    const intptr_t intptr_handle = _get_osfhandle(fd);
+    if (intptr_handle == -1) {
+        return nullptr;
+    }
+    const HANDLE handle = reinterpret_cast<const HANDLE>(intptr_handle);
+    DWORD temp_mode = 0;
+    if (!GetConsoleMode(handle, mode ? mode : &temp_mode)) {
+        return nullptr;
+    }
+
+    return handle;
+}
+
+// Returns a console handle if |stream| is a console, otherwise returns nullptr.
+static HANDLE _get_console_handle(FILE* const stream) {
+    const int fd = fileno(stream);
+    if (fd < 0) {
+        return nullptr;
+    }
+    return _get_console_handle(fd);
+}
+
+int unix_isatty(int fd) {
+    return _get_console_handle(fd) ? 1 : 0;
+}
 
 // Read an input record from the console; one that should be processed.
 static bool _get_interesting_input_record_uncached(const HANDLE console,
@@ -3201,57 +3342,41 @@
 static DWORD _old_console_mode; // previous GetConsoleMode() result
 static HANDLE _console_handle;  // when set, console mode should be restored
 
-void stdin_raw_init(const int fd) {
-    if (STDIN_FILENO == fd) {
-        const HANDLE in = GetStdHandle(STD_INPUT_HANDLE);
-        if ((in == INVALID_HANDLE_VALUE) || (in == NULL)) {
-            return;
-        }
+void stdin_raw_init() {
+    const HANDLE in = _get_console_handle(STDIN_FILENO, &_old_console_mode);
 
-        if (GetFileType(in) != FILE_TYPE_CHAR) {
-            // stdin might be a file or pipe.
-            return;
-        }
-
-        if (!GetConsoleMode(in, &_old_console_mode)) {
-            // If GetConsoleMode() fails, stdin is probably is not a console.
-            return;
-        }
-
-        // Disable ENABLE_PROCESSED_INPUT so that Ctrl-C is read instead of
-        // calling the process Ctrl-C routine (configured by
-        // SetConsoleCtrlHandler()).
-        // Disable ENABLE_LINE_INPUT so that input is immediately sent.
-        // Disable ENABLE_ECHO_INPUT to disable local echo. Disabling this
-        // flag also seems necessary to have proper line-ending processing.
-        if (!SetConsoleMode(in, _old_console_mode & ~(ENABLE_PROCESSED_INPUT |
-            ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT))) {
-            // This really should not fail.
-            D("stdin_raw_init: SetConsoleMode() failed: %s",
-              SystemErrorCodeToString(GetLastError()).c_str());
-        }
-
-        // Once this is set, it means that stdin has been configured for
-        // reading from and that the old console mode should be restored later.
-        _console_handle = in;
-
-        // Note that we don't need to configure C Runtime line-ending
-        // translation because _console_read() does not call the C Runtime to
-        // read from the console.
+    // Disable ENABLE_PROCESSED_INPUT so that Ctrl-C is read instead of
+    // calling the process Ctrl-C routine (configured by
+    // SetConsoleCtrlHandler()).
+    // Disable ENABLE_LINE_INPUT so that input is immediately sent.
+    // Disable ENABLE_ECHO_INPUT to disable local echo. Disabling this
+    // flag also seems necessary to have proper line-ending processing.
+    if (!SetConsoleMode(in, _old_console_mode & ~(ENABLE_PROCESSED_INPUT |
+                                                  ENABLE_LINE_INPUT |
+                                                  ENABLE_ECHO_INPUT))) {
+        // This really should not fail.
+        D("stdin_raw_init: SetConsoleMode() failed: %s",
+          SystemErrorCodeToString(GetLastError()).c_str());
     }
+
+    // Once this is set, it means that stdin has been configured for
+    // reading from and that the old console mode should be restored later.
+    _console_handle = in;
+
+    // Note that we don't need to configure C Runtime line-ending
+    // translation because _console_read() does not call the C Runtime to
+    // read from the console.
 }
 
-void stdin_raw_restore(const int fd) {
-    if (STDIN_FILENO == fd) {
-        if (_console_handle != NULL) {
-            const HANDLE in = _console_handle;
-            _console_handle = NULL;  // clear state
+void stdin_raw_restore() {
+    if (_console_handle != NULL) {
+        const HANDLE in = _console_handle;
+        _console_handle = NULL;  // clear state
 
-            if (!SetConsoleMode(in, _old_console_mode)) {
-                // This really should not fail.
-                D("stdin_raw_restore: SetConsoleMode() failed: %s",
-                  SystemErrorCodeToString(GetLastError()).c_str());
-            }
+        if (!SetConsoleMode(in, _old_console_mode)) {
+            // This really should not fail.
+            D("stdin_raw_restore: SetConsoleMode() failed: %s",
+              SystemErrorCodeToString(GetLastError()).c_str());
         }
     }
 }
@@ -3267,11 +3392,8 @@
     } else {
         // On older versions of Windows (definitely 7, definitely not 10),
         // ReadConsole() with a size >= 31367 fails, so if |fd| is a console
-        // we need to limit the read size. This may also catch devices like NUL,
-        // but that is OK as we just want to avoid capping pipes and files which
-        // don't need size limiting. This isatty() test is very simple and quick
-        // and doesn't call the OS.
-        if (isatty(fd) && len > 4096) {
+        // we need to limit the read size.
+        if (len > 4096 && unix_isatty(fd)) {
             len = 4096;
         }
         // Just call into C Runtime which can read from pipes/files and which
@@ -3626,40 +3748,6 @@
     return _wchmod(widen(path).c_str(), mode);
 }
 
-// Internal function to get a Win32 console HANDLE from a C Runtime FILE*.
-static HANDLE _get_console_handle(FILE* const stream) {
-    // Get a C Runtime file descriptor number from the FILE* structure.
-    const int fd = fileno(stream);
-    if (fd < 0) {
-        return NULL;
-    }
-
-    // If it is not a "character device", it is probably a file and not a
-    // console. Do this check early because it is probably cheap. Still do more
-    // checks after this since there are devices that pass this test, but are
-    // not a console, such as NUL, the Windows /dev/null equivalent (I think).
-    if (!isatty(fd)) {
-        return NULL;
-    }
-
-    // Given a C Runtime file descriptor number, get the underlying OS
-    // file handle.
-    const intptr_t osfh = _get_osfhandle(fd);
-    if (osfh == -1) {
-        return NULL;
-    }
-
-    const HANDLE h = reinterpret_cast<const HANDLE>(osfh);
-
-    DWORD old_mode = 0;
-    if (!GetConsoleMode(h, &old_mode)) {
-        return NULL;
-    }
-
-    // If GetConsoleMode() was successful, assume this is a console.
-    return h;
-}
-
 // Internal helper function to write UTF-8 bytes to a console. Returns -1
 // on error.
 static int _console_write_utf8(const char* buf, size_t size, FILE* stream,
diff --git a/adb/sysdeps_win32_test.cpp b/adb/sysdeps_win32_test.cpp
index cc3ac5c..55b5eb4 100755
--- a/adb/sysdeps_win32_test.cpp
+++ b/adb/sysdeps_win32_test.cpp
@@ -18,6 +18,8 @@
 
 #include "sysdeps.h"
 
+#include "base/test_utils.h"
+
 TEST(sysdeps_win32, adb_getenv) {
     // Insert all test env vars before first call to adb_getenv() which will
     // read the env var block only once.
@@ -67,3 +69,71 @@
         EXPECT_GT(strlen(path_val), 0);
     }
 }
+
+void TestAdbStrError(int err, const char* expected) {
+    errno = 12345;
+    const char* result = adb_strerror(err);
+    // Check that errno is not overwritten.
+    EXPECT_EQ(12345, errno);
+    EXPECT_STREQ(expected, result);
+}
+
+TEST(sysdeps_win32, adb_strerror) {
+    // Test an error code that should not have a mapped string. Use an error
+    // code that is not used by the internal implementation of adb_strerror().
+    TestAdbStrError(-2, "Unknown error");
+    // adb_strerror() uses -1 internally, so test that it can still be passed
+    // as a parameter.
+    TestAdbStrError(-1, "Unknown error");
+    // Test very big, positive unknown error.
+    TestAdbStrError(1000000, "Unknown error");
+    // Test success case.
+    TestAdbStrError(0, "No error");
+    // Test error that regular strerror() should have a string for.
+    TestAdbStrError(EPERM, "Operation not permitted");
+    // Test error that regular strerror() doesn't have a string for, but that
+    // adb_strerror() returns.
+    TestAdbStrError(ECONNRESET, "Connection reset by peer");
+}
+
+TEST(sysdeps_win32, unix_isatty) {
+    // stdin and stdout should be consoles. Use CONIN$ and CONOUT$ special files
+    // so that we can test this even if stdin/stdout have been redirected. Read
+    // permissions are required for unix_isatty().
+    int conin_fd = unix_open("CONIN$", O_RDONLY);
+    int conout_fd = unix_open("CONOUT$", O_RDWR);
+    for (const int fd : {conin_fd, conout_fd}) {
+        EXPECT_TRUE(fd >= 0);
+        EXPECT_EQ(1, unix_isatty(fd));
+        EXPECT_EQ(0, unix_close(fd));
+    }
+
+    // nul returns 1 from isatty(), make sure unix_isatty() corrects that.
+    for (auto flags : {O_RDONLY, O_RDWR}) {
+        int nul_fd = unix_open("nul", flags);
+        EXPECT_TRUE(nul_fd >= 0);
+        EXPECT_EQ(0, unix_isatty(nul_fd));
+        EXPECT_EQ(0, unix_close(nul_fd));
+    }
+
+    // Check a real file, both read-write and read-only.
+    TemporaryFile temp_file;
+    EXPECT_TRUE(temp_file.fd >= 0);
+    EXPECT_EQ(0, unix_isatty(temp_file.fd));
+
+    int temp_file_ro_fd = unix_open(temp_file.path, O_RDONLY);
+    EXPECT_TRUE(temp_file_ro_fd >= 0);
+    EXPECT_EQ(0, unix_isatty(temp_file_ro_fd));
+    EXPECT_EQ(0, unix_close(temp_file_ro_fd));
+
+    // Check a real OS pipe.
+    int pipe_fds[2];
+    EXPECT_EQ(0, _pipe(pipe_fds, 64, _O_BINARY));
+    EXPECT_EQ(0, unix_isatty(pipe_fds[0]));
+    EXPECT_EQ(0, unix_isatty(pipe_fds[1]));
+    EXPECT_EQ(0, _close(pipe_fds[0]));
+    EXPECT_EQ(0, _close(pipe_fds[1]));
+
+    // Make sure an invalid FD is handled correctly.
+    EXPECT_EQ(0, unix_isatty(-1));
+}
diff --git a/adb/test_adb.py b/adb/test_adb.py
index 5bda22f..0f1b034 100644
--- a/adb/test_adb.py
+++ b/adb/test_adb.py
@@ -21,8 +21,11 @@
 """
 from __future__ import print_function
 
+import contextlib
 import os
 import random
+import socket
+import struct
 import subprocess
 import threading
 import unittest
@@ -140,6 +143,71 @@
             subprocess.check_output(['adb', '-P', str(port), 'kill-server'],
                                     stderr=subprocess.STDOUT)
 
+    # Use SO_LINGER to cause TCP RST segment to be sent on socket close.
+    def _reset_socket_on_close(self, sock):
+        # The linger structure is two shorts on Windows, but two ints on Unix.
+        linger_format = 'hh' if os.name == 'nt' else 'ii'
+        l_onoff = 1
+        l_linger = 0
+
+        sock.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER,
+                        struct.pack(linger_format, l_onoff, l_linger))
+        # Verify that we set the linger structure properly by retrieving it.
+        linger = sock.getsockopt(socket.SOL_SOCKET, socket.SO_LINGER, 16)
+        self.assertEqual((l_onoff, l_linger),
+                         struct.unpack_from(linger_format, linger))
+
+    def test_emu_kill(self):
+        """Ensure that adb emu kill works.
+
+        Bug: https://code.google.com/p/android/issues/detail?id=21021
+        """
+        port = 12345
+
+        with contextlib.closing(
+                socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as listener:
+            # Use SO_REUSEADDR so subsequent runs of the test can grab the port
+            # even if it is in TIME_WAIT.
+            listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+            listener.bind(('127.0.0.1', port))
+            listener.listen(4)
+
+            # Now that listening has started, start adb emu kill, telling it to
+            # connect to our mock emulator.
+            p = subprocess.Popen(
+                ['adb', '-s', 'emulator-' + str(port), 'emu', 'kill'],
+                stderr=subprocess.STDOUT)
+
+            accepted_connection, addr = listener.accept()
+            with contextlib.closing(accepted_connection) as conn:
+                # If WSAECONNABORTED (10053) is raised by any socket calls,
+                # then adb probably isn't reading the data that we sent it.
+                conn.sendall('Android Console: type \'help\' for a list ' +
+                                'of commands\r\n')
+                conn.sendall('OK\r\n')
+
+                with contextlib.closing(conn.makefile()) as f:
+                    self.assertEqual('kill\n', f.readline())
+                    self.assertEqual('quit\n', f.readline())
+
+                conn.sendall('OK: killing emulator, bye bye\r\n')
+
+                # Use SO_LINGER to send TCP RST segment to test whether adb
+                # ignores WSAECONNRESET on Windows. This happens with the
+                # real emulator because it just calls exit() without closing
+                # the socket or calling shutdown(SD_SEND). At process
+                # termination, Windows sends a TCP RST segment for every
+                # open socket that shutdown(SD_SEND) wasn't used on.
+                self._reset_socket_on_close(conn)
+
+            # Wait for adb to finish, so we can check return code.
+            p.communicate()
+
+            # If this fails, adb probably isn't ignoring WSAECONNRESET when
+            # reading the response from the adb emu kill command (on Windows).
+            self.assertEqual(0, p.returncode)
+
+
 def main():
     random.seed(0)
     if len(adb.get_devices()) > 0:
diff --git a/base/include/base/parseint.h b/base/include/base/parseint.h
index 9ecbfbc..0543795 100644
--- a/base/include/base/parseint.h
+++ b/base/include/base/parseint.h
@@ -31,9 +31,10 @@
 template <typename T>
 bool ParseUint(const char* s, T* out,
                T max = std::numeric_limits<T>::max()) {
+  int base = (s[0] == '0' && s[1] == 'x') ? 16 : 10;
   errno = 0;
   char* end;
-  unsigned long long int result = strtoull(s, &end, 10);
+  unsigned long long int result = strtoull(s, &end, base);
   if (errno != 0 || s == end || *end != '\0') {
     return false;
   }
@@ -52,9 +53,10 @@
 bool ParseInt(const char* s, T* out,
               T min = std::numeric_limits<T>::min(),
               T max = std::numeric_limits<T>::max()) {
+  int base = (s[0] == '0' && s[1] == 'x') ? 16 : 10;
   errno = 0;
   char* end;
-  long long int result = strtoll(s, &end, 10);
+  long long int result = strtoll(s, &end, base);
   if (errno != 0 || s == end || *end != '\0') {
     return false;
   }
diff --git a/base/include/base/strings.h b/base/include/base/strings.h
index 638f845..20da144 100644
--- a/base/include/base/strings.h
+++ b/base/include/base/strings.h
@@ -36,8 +36,8 @@
 std::string Trim(const std::string& s);
 
 // Joins a container of things into a single string, using the given separator.
-template <typename ContainerT>
-std::string Join(const ContainerT& things, char separator) {
+template <typename ContainerT, typename SeparatorT>
+std::string Join(const ContainerT& things, SeparatorT separator) {
   if (things.empty()) {
     return "";
   }
@@ -53,6 +53,8 @@
 // We instantiate the common cases in strings.cpp.
 extern template std::string Join(const std::vector<std::string>&, char);
 extern template std::string Join(const std::vector<const char*>&, char);
+extern template std::string Join(const std::vector<std::string>&, const std::string&);
+extern template std::string Join(const std::vector<const char*>&, const std::string&);
 
 // Tests whether 's' starts with 'prefix'.
 bool StartsWith(const std::string& s, const char* prefix);
diff --git a/base/logging_test.cpp b/base/logging_test.cpp
index 9cf1aad..70f9952 100644
--- a/base/logging_test.cpp
+++ b/base/logging_test.cpp
@@ -139,7 +139,7 @@
   {
     CapturedStderr cap;
     LOG(WARNING) << "foobar";
-    ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
+    ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
 
     std::string output;
     android::base::ReadFdToString(cap.fd(), &output);
@@ -155,7 +155,7 @@
   {
     CapturedStderr cap;
     LOG(INFO) << "foobar";
-    ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
+    ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
 
     std::string output;
     android::base::ReadFdToString(cap.fd(), &output);
@@ -171,7 +171,7 @@
   {
     CapturedStderr cap;
     LOG(DEBUG) << "foobar";
-    ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
+    ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
 
     std::string output;
     android::base::ReadFdToString(cap.fd(), &output);
@@ -182,7 +182,7 @@
     android::base::ScopedLogSeverity severity(android::base::DEBUG);
     CapturedStderr cap;
     LOG(DEBUG) << "foobar";
-    ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
+    ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
 
     std::string output;
     android::base::ReadFdToString(cap.fd(), &output);
@@ -202,7 +202,7 @@
     LOG(INFO) << (errno = 67890);
     EXPECT_EQ(12345, errno) << "errno was not restored";
 
-    ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
+    ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
 
     std::string output;
     android::base::ReadFdToString(cap.fd(), &output);
@@ -245,7 +245,7 @@
     CapturedStderr cap;
     errno = ENOENT;
     PLOG(INFO) << "foobar";
-    ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
+    ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
 
     std::string output;
     android::base::ReadFdToString(cap.fd(), &output);
@@ -264,7 +264,7 @@
     CapturedStderr cap;
     errno = ENOENT;
     UNIMPLEMENTED(ERROR);
-    ASSERT_EQ(0, lseek(cap.fd(), SEEK_SET, 0));
+    ASSERT_EQ(0, lseek(cap.fd(), 0, SEEK_SET));
 
     std::string output;
     android::base::ReadFdToString(cap.fd(), &output);
diff --git a/base/parseint_test.cpp b/base/parseint_test.cpp
index e19c6e3..8a11d29 100644
--- a/base/parseint_test.cpp
+++ b/base/parseint_test.cpp
@@ -66,3 +66,13 @@
   ASSERT_TRUE(android::base::ParseUint("0123", &u));
   ASSERT_EQ(123u, u);
 }
+
+TEST(parseint, explicit_hex) {
+  int i;
+  ASSERT_TRUE(android::base::ParseInt("0x123", &i));
+  ASSERT_EQ(0x123, i);
+
+  unsigned int u;
+  ASSERT_TRUE(android::base::ParseUint("0x123", &u));
+  ASSERT_EQ(0x123u, u);
+}
diff --git a/base/strings.cpp b/base/strings.cpp
index bac983b..d687e3c 100644
--- a/base/strings.cpp
+++ b/base/strings.cpp
@@ -83,6 +83,8 @@
 // aid compile time and binary size.
 template std::string Join(const std::vector<std::string>&, char);
 template std::string Join(const std::vector<const char*>&, char);
+template std::string Join(const std::vector<std::string>&, const std::string&);
+template std::string Join(const std::vector<const char*>&, const std::string&);
 
 bool StartsWith(const std::string& s, const char* prefix) {
   return s.compare(0, strlen(prefix), prefix) == 0;
diff --git a/crash_reporter/Android.mk b/crash_reporter/Android.mk
index 11dfcbc..81cb458 100644
--- a/crash_reporter/Android.mk
+++ b/crash_reporter/Android.mk
@@ -43,7 +43,7 @@
 LOCAL_C_INCLUDES := $(crash_reporter_includes)
 LOCAL_RTTI_FLAG := -frtti
 LOCAL_SHARED_LIBRARIES := libchrome \
-    libchromeos \
+    libbrillo \
     libcutils \
     libdbus \
     libmetrics \
@@ -65,7 +65,7 @@
 LOCAL_INIT_RC := crash_reporter.rc
 LOCAL_RTTI_FLAG := -frtti
 LOCAL_SHARED_LIBRARIES := libchrome \
-    libchromeos \
+    libbrillo \
     libcutils \
     libdbus \
     libmetrics \
@@ -102,10 +102,15 @@
 LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/$(OSRELEASED_DIRECTORY)
 include $(BUILD_SYSTEM)/base_rules.mk
 
+# Optionally populate the BRILLO_CRASH_SERVER variable from a product
+# configuration file: brillo/crash_server.
+LOADED_BRILLO_CRASH_SERVER := $(call cfgtree-get-if-exists,brillo/crash_server)
+
 # If the crash server isn't set, use a blank value.  crash_sender
 # will log it as a configuration error.
-$(LOCAL_BUILT_MODULE): BRILLO_CRASH_SERVER ?= ""
+$(LOCAL_BUILT_MODULE): BRILLO_CRASH_SERVER ?= "$(LOADED_BRILLO_CRASH_SERVER)"
 $(LOCAL_BUILT_MODULE):
+	$(hide)mkdir -p $(dir $@)
 	echo $(BRILLO_CRASH_SERVER) > $@
 endif
 
@@ -133,7 +138,7 @@
 LOCAL_MODULE := crash_reporter_tests
 LOCAL_CPP_EXTENSION := $(crash_reporter_cpp_extension)
 LOCAL_SHARED_LIBRARIES := libchrome \
-    libchromeos \
+    libbrillo \
     libcutils \
     libdbus \
     libpcrecpp
diff --git a/crash_reporter/crash_sender b/crash_reporter/crash_sender
index 5b859a8..95204a4 100755
--- a/crash_reporter/crash_sender
+++ b/crash_reporter/crash_sender
@@ -98,6 +98,10 @@
   log -t "${TAG}" "$@"
 }
 
+lwarn() {
+  lecho -psyslog.warn "$@"
+}
+
 # Returns true if mock is enabled.
 is_mock() {
   [ -f "${MOCK_CRASH_SENDING}" ] && return 0
@@ -294,6 +298,11 @@
   fi
 }
 
+# Return the log string filtered with only JSON-safe white-listed characters.
+filter_log_string() {
+  echo "$1" | tr -cd '[:alnum:]_.\-:;'
+}
+
 send_crash() {
   local meta_path="$1"
   local report_payload="$(get_key_value "${meta_path}" "payload")"
@@ -434,8 +443,7 @@
     ret=$?
     if [ ${ret} -ne 0 ]; then
       proxy=''
-      lecho -psyslog.warn \
-        "Listing proxies failed with exit code ${ret}"
+      lwarn "Listing proxies failed with exit code ${ret}"
     else
       proxy=$(echo "${proxy}" | head -1)
     fi
@@ -446,7 +454,7 @@
   local curl_stderr="${TMP_DIR}/curl_stderr"
 
   set +e
-  curl "${url}" -v ${proxy:+--proxy "$proxy"} \
+  curl "${url}" -f -v ${proxy:+--proxy "$proxy"} \
     --capath "${RESTRICTED_CERTIFICATES_PATH}" --ciphers HIGH \
     -F "prod=${product}" \
     -F "ver=${version}" \
@@ -466,22 +474,17 @@
 
   if [ ${curl_result} -eq 0 ]; then
     local id="$(cat "${report_id}")"
-    local product_name
     local timestamp="$(date +%s)"
-    case ${product} in
-    Chrome_ChromeOS)
-      if is_official_image; then
-        product_name="Chrome"
-      else
-        product_name="Chromium"
-      fi
-      ;;
-    *)
-      product_name="Brillo"
-      ;;
-    esac
-    printf '%s,%s,%s\n' \
-      "${timestamp}" "${id}" "${product_name}" >> "${CRASH_LOG}"
+    local filter_prod="$(filter_log_string "${product}")"
+    local filter_exec="$(filter_log_string "${exec_name}")"
+    if [ "${filter_prod}" != "${product}" ]; then
+      lwarn "Product name filtered to: ${filter_prod}."
+    fi
+    if [ "${filter_exec}" != "${exec_name}" ]; then
+      lwarn "Exec name filtered to: ${filter_exec}."
+    fi
+    printf "{'time':%s,'id':'%s','product':'%s','exec_name':'%s'}\n" \
+      "${timestamp}" "${id}" "${filter_prod}" "${filter_exec}" >> "${CRASH_LOG}"
     lecho "Crash report receipt ID ${id}"
   else
     lecho "Crash sending failed with exit code ${curl_result}: " \
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index 713638d..1287fb9 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -528,7 +528,7 @@
     return 1;
   fcntl(s, F_SETFD, FD_CLOEXEC);
 
-  ALOGI("debuggerd: " __DATE__ " " __TIME__ "\n");
+  ALOGI("debuggerd: starting\n");
 
   for (;;) {
     sockaddr addr;
diff --git a/debuggerd/debuggerd.rc b/debuggerd/debuggerd.rc
index 4338ae9..4be2e5d 100644
--- a/debuggerd/debuggerd.rc
+++ b/debuggerd/debuggerd.rc
@@ -1,2 +1,3 @@
 service debuggerd /system/bin/debuggerd
     class main
+    writepid /dev/cpuset/system-background/tasks
diff --git a/debuggerd/debuggerd64.rc b/debuggerd/debuggerd64.rc
index 341a329..c6e7bf2 100644
--- a/debuggerd/debuggerd64.rc
+++ b/debuggerd/debuggerd64.rc
@@ -1,2 +1,3 @@
 service debuggerd64 /system/bin/debuggerd64
     class main
+    writepid /dev/cpuset/system-background/tasks
diff --git a/fastboot/engine.cpp b/fastboot/engine.cpp
index a0e990a..44796d7 100644
--- a/fastboot/engine.cpp
+++ b/fastboot/engine.cpp
@@ -75,41 +75,17 @@
 
 
 
-int fb_getvar(struct usb_handle *usb, char *response, const char *fmt, ...)
-{
-    char cmd[CMD_SIZE] = "getvar:";
-    int getvar_len = strlen(cmd);
-    va_list args;
+bool fb_getvar(usb_handle* usb, const std::string& key, std::string* value) {
+    std::string cmd = "getvar:";
+    cmd += key;
 
-    response[FB_RESPONSE_SZ] = '\0';
-    va_start(args, fmt);
-    vsnprintf(cmd + getvar_len, sizeof(cmd) - getvar_len, fmt, args);
-    va_end(args);
-    cmd[CMD_SIZE - 1] = '\0';
-    return fb_command_response(usb, cmd, response);
-}
-
-
-/* Return true if this partition is supported by the fastboot format command.
- * It is also used to determine if we should first erase a partition before
- * flashing it with an ext4 filesystem.  See needs_erase()
- *
- * Not all devices report the filesystem type, so don't report any errors,
- * just return false.
- */
-int fb_format_supported(usb_handle *usb, const char *partition, const char *type_override)
-{
-    char fs_type[FB_RESPONSE_SZ + 1] = {0,};
-    int status;
-
-    if (type_override) {
-        return !!fs_get_generator(type_override);
+    char buf[FB_RESPONSE_SZ + 1];
+    memset(buf, 0, sizeof(buf));
+    if (fb_command_response(usb, cmd.c_str(), buf)) {
+      return false;
     }
-    status = fb_getvar(usb, fs_type, "partition-type:%s", partition);
-    if (status) {
-        return 0;
-    }
-    return !!fs_get_generator(fs_type);
+    *value = buf;
+    return true;
 }
 
 static int cb_default(Action* a, int status, const char* resp) {
@@ -394,8 +370,3 @@
     fprintf(stderr,"finished. total time: %.3fs\n", (now() - start));
     return status;
 }
-
-int fb_queue_is_empty(void)
-{
-    return (action_list == nullptr);
-}
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index e2971f2..15dd67d 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -43,6 +43,8 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <base/parseint.h>
+#include <base/strings.h>
 #include <sparse/sparse.h>
 #include <ziparchive/zip_archive.h>
 
@@ -260,6 +262,11 @@
             "                                           partitions.\n"
             "  flashing get_unlock_ability              Queries bootloader to see if the\n"
             "                                           device is unlocked.\n"
+            "  flashing get_unlock_bootloader_nonce     Queries the bootloader to get the\n"
+            "                                           unlock nonce.\n"
+            "  flashing unlock_bootloader <request>     Issue unlock bootloader using request.\n"
+            "  flashing lock_bootloader                 Locks the bootloader to prevent\n"
+            "                                           bootloader version rollback.\n"
             "  erase <partition>                        Erase a flash partition.\n"
             "  format[:[<fs type>][:[<size>]] <partition>\n"
             "                                           Format a flash partition. Can\n"
@@ -567,25 +574,28 @@
     return out_s;
 }
 
-static int64_t get_target_sparse_limit(struct usb_handle *usb)
-{
-    int64_t limit = 0;
-    char response[FB_RESPONSE_SZ + 1];
-    int status = fb_getvar(usb, response, "max-download-size");
-
-    if (!status) {
-        limit = strtoul(response, nullptr, 0);
-        if (limit > 0) {
-            fprintf(stderr, "target reported max download size of %" PRId64 " bytes\n",
-                    limit);
-        }
+static int64_t get_target_sparse_limit(usb_handle* usb) {
+    std::string max_download_size;
+    if (!fb_getvar(usb, "max-download-size", &max_download_size) || max_download_size.empty()) {
+        fprintf(stderr, "target didn't report max-download-size\n");
+        return 0;
     }
 
+    // Some bootloaders (angler, for example) send spurious whitespace too.
+    max_download_size = android::base::Trim(max_download_size);
+
+    uint64_t limit;
+    if (!android::base::ParseUint(max_download_size.c_str(), &limit)) {
+        fprintf(stderr, "couldn't parse max-download-size '%s'\n", max_download_size.c_str());
+        return 0;
+    }
+    if (limit > 0) {
+        fprintf(stderr, "target reported max download size of %" PRId64 " bytes\n", limit);
+    }
     return limit;
 }
 
-static int64_t get_sparse_limit(struct usb_handle *usb, int64_t size)
-{
+static int64_t get_sparse_limit(usb_handle* usb, int64_t size) {
     int64_t limit;
 
     if (sparse_limit == 0) {
@@ -610,16 +620,15 @@
     return 0;
 }
 
-/* Until we get lazy inode table init working in make_ext4fs, we need to
- * erase partitions of type ext4 before flashing a filesystem so no stale
- * inodes are left lying around.  Otherwise, e2fsck gets very upset.
- */
-static int needs_erase(usb_handle* usb, const char *part)
-{
-    /* The function fb_format_supported() currently returns the value
-     * we want, so just call it.
-     */
-     return fb_format_supported(usb, part, nullptr);
+// Until we get lazy inode table init working in make_ext4fs, we need to
+// erase partitions of type ext4 before flashing a filesystem so no stale
+// inodes are left lying around.  Otherwise, e2fsck gets very upset.
+static bool needs_erase(usb_handle* usb, const char* partition) {
+    std::string partition_type;
+    if (!fb_getvar(usb, std::string("partition-type:") + partition, &partition_type)) {
+        return false;
+    }
+    return partition_type == "ext4";
 }
 
 static int load_buf_fd(usb_handle* usb, int fd, struct fastboot_buffer* buf) {
@@ -794,7 +803,28 @@
 #define skip(n) do { argc -= (n); argv += (n); } while (0)
 #define require(n) do { if (argc < (n)) {usage(); exit(1);}} while (0)
 
-static int do_oem_command(int argc, char** argv) {
+static int do_bypass_unlock_command(int argc, char **argv)
+{
+    if (argc <= 2) return 0;
+    skip(2);
+
+    /*
+     * Process unlock_bootloader, we have to load the message file
+     * and send that to the remote device.
+     */
+    require(1);
+
+    int64_t sz;
+    void* data = load_file(*argv, &sz);
+    if (data == nullptr) die("could not load '%s': %s", *argv, strerror(errno));
+    fb_queue_download("unlock_message", data, sz);
+    fb_queue_command("flashing unlock_bootloader", "unlocking bootloader");
+    skip(1);
+    return 0;
+}
+
+static int do_oem_command(int argc, char **argv)
+{
     char command[256];
     if (argc <= 1) return 0;
 
@@ -852,87 +882,88 @@
 }
 
 static void fb_perform_format(usb_handle* usb,
-                              const char *partition, int skip_if_not_supported,
-                              const char *type_override, const char *size_override) {
-    char pTypeBuff[FB_RESPONSE_SZ + 1], pSizeBuff[FB_RESPONSE_SZ + 1];
-    char *pType = pTypeBuff;
-    char *pSize = pSizeBuff;
-    unsigned int limit = INT_MAX;
+                              const char* partition, int skip_if_not_supported,
+                              const char* type_override, const char* size_override) {
+    std::string partition_type, partition_size;
+
     struct fastboot_buffer buf;
-    const char *errMsg = nullptr;
-    const struct fs_generator *gen;
-    uint64_t pSz;
-    int status;
+    const char* errMsg = nullptr;
+    const struct fs_generator* gen = nullptr;
     int fd;
 
-    if (target_sparse_limit > 0 && target_sparse_limit < limit)
+    unsigned int limit = INT_MAX;
+    if (target_sparse_limit > 0 && target_sparse_limit < limit) {
         limit = target_sparse_limit;
-    if (sparse_limit > 0 && sparse_limit < limit)
+    }
+    if (sparse_limit > 0 && sparse_limit < limit) {
         limit = sparse_limit;
+    }
 
-    status = fb_getvar(usb, pType, "partition-type:%s", partition);
-    if (status) {
+    if (!fb_getvar(usb, std::string("partition-type:") + partition, &partition_type)) {
         errMsg = "Can't determine partition type.\n";
         goto failed;
     }
     if (type_override) {
-        if (strcmp(type_override, pType)) {
-            fprintf(stderr,
-                    "Warning: %s type is %s, but %s was requested for formating.\n",
-                    partition, pType, type_override);
+        if (partition_type != type_override) {
+            fprintf(stderr, "Warning: %s type is %s, but %s was requested for formatting.\n",
+                    partition, partition_type.c_str(), type_override);
         }
-        pType = (char *)type_override;
+        partition_type = type_override;
     }
 
-    status = fb_getvar(usb, pSize, "partition-size:%s", partition);
-    if (status) {
+    if (!fb_getvar(usb, std::string("partition-size:") + partition, &partition_size)) {
         errMsg = "Unable to get partition size\n";
         goto failed;
     }
     if (size_override) {
-        if (strcmp(size_override, pSize)) {
-            fprintf(stderr,
-                    "Warning: %s size is %s, but %s was requested for formating.\n",
-                    partition, pSize, size_override);
+        if (partition_size != size_override) {
+            fprintf(stderr, "Warning: %s size is %s, but %s was requested for formatting.\n",
+                    partition, partition_size.c_str(), size_override);
         }
-        pSize = (char *)size_override;
+        partition_size = size_override;
     }
 
-    gen = fs_get_generator(pType);
+    gen = fs_get_generator(partition_type);
     if (!gen) {
         if (skip_if_not_supported) {
             fprintf(stderr, "Erase successful, but not automatically formatting.\n");
-            fprintf(stderr, "File system type %s not supported.\n", pType);
+            fprintf(stderr, "File system type %s not supported.\n", partition_type.c_str());
             return;
         }
-        fprintf(stderr, "Formatting is not supported for filesystem with type '%s'.\n", pType);
+        fprintf(stderr, "Formatting is not supported for file system with type '%s'.\n",
+                partition_type.c_str());
         return;
     }
 
-    pSz = strtoll(pSize, (char **)nullptr, 16);
+    // Some bootloaders (hammerhead, for example) use implicit hex.
+    // This code used to use strtol with base 16.
+    if (!android::base::StartsWith(partition_size, "0x")) partition_size = "0x" + partition_size;
+
+    int64_t size;
+    if (!android::base::ParseInt(partition_size.c_str(), &size)) {
+        fprintf(stderr, "Couldn't parse partition size '%s'.\n", partition_size.c_str());
+        return;
+    }
 
     fd = fileno(tmpfile());
-    if (fs_generator_generate(gen, fd, pSz)) {
+    if (fs_generator_generate(gen, fd, size)) {
+        fprintf(stderr, "Cannot generate image: %s\n", strerror(errno));
         close(fd);
-        fprintf(stderr, "Cannot generate image.\n");
         return;
     }
 
     if (load_buf_fd(usb, fd, &buf)) {
-        fprintf(stderr, "Cannot read image.\n");
+        fprintf(stderr, "Cannot read image: %s\n", strerror(errno));
         close(fd);
         return;
     }
     flash_buf(partition, &buf);
-
     return;
 
-
 failed:
     if (skip_if_not_supported) {
         fprintf(stderr, "Erase successful, but not automatically formatting.\n");
-        if (errMsg)
-            fprintf(stderr, "%s", errMsg);
+        if (errMsg) fprintf(stderr, "%s", errMsg);
     }
     fprintf(stderr,"FAILED (%s)\n", fb_get_error());
 }
@@ -945,8 +976,6 @@
     bool erase_first = true;
     void *data;
     int64_t sz;
-    int status;
-    int c;
     int longindex;
 
     const struct option longopts[] = {
@@ -964,7 +993,7 @@
     serial = getenv("ANDROID_SERIAL");
 
     while (1) {
-        c = getopt_long(argc, argv, "wub:k:n:r:t:s:S:lp:c:i:m:h", longopts, &longindex);
+        int c = getopt_long(argc, argv, "wub:k:n:r:t:s:S:lp:c:i:m:h", longopts, &longindex);
         if (c < 0) {
             break;
         }
@@ -1061,15 +1090,18 @@
     usb_handle* usb = open_device();
 
     while (argc > 0) {
-        if(!strcmp(*argv, "getvar")) {
+        if (!strcmp(*argv, "getvar")) {
             require(2);
             fb_queue_display(argv[1], argv[1]);
             skip(2);
         } else if(!strcmp(*argv, "erase")) {
             require(2);
 
-            if (fb_format_supported(usb, argv[1], nullptr)) {
-                fprintf(stderr, "******** Did you mean to fastboot format this partition?\n");
+            std::string partition_type;
+            if (fb_getvar(usb, std::string("partition-type:") + argv[1], &partition_type) &&
+                fs_get_generator(partition_type) != nullptr) {
+                fprintf(stderr, "******** Did you mean to fastboot format this %s partition?\n",
+                        partition_type.c_str());
             }
 
             fb_queue_erase(argv[1]);
@@ -1200,12 +1232,18 @@
             wants_reboot = 1;
         } else if(!strcmp(*argv, "oem")) {
             argc = do_oem_command(argc, argv);
-        } else if(!strcmp(*argv, "flashing") && argc == 2) {
-            if(!strcmp(*(argv+1), "unlock") || !strcmp(*(argv+1), "lock")
-               || !strcmp(*(argv+1), "unlock_critical")
-               || !strcmp(*(argv+1), "lock_critical")
-               || !strcmp(*(argv+1), "get_unlock_ability")) {
-              argc = do_oem_command(argc, argv);
+        } else if(!strcmp(*argv, "flashing")) {
+            if (argc == 2 && (!strcmp(*(argv+1), "unlock") ||
+                              !strcmp(*(argv+1), "lock") ||
+                              !strcmp(*(argv+1), "unlock_critical") ||
+                              !strcmp(*(argv+1), "lock_critical") ||
+                              !strcmp(*(argv+1), "get_unlock_ability") ||
+                              !strcmp(*(argv+1), "get_unlock_bootloader_nonce") ||
+                              !strcmp(*(argv+1), "lock_bootloader"))) {
+                argc = do_oem_command(argc, argv);
+            } else
+            if (argc == 3 && !strcmp(*(argv+1), "unlock_bootloader")) {
+                argc = do_bypass_unlock_command(argc, argv);
             } else {
               usage();
               return 1;
@@ -1217,10 +1255,16 @@
     }
 
     if (wants_wipe) {
+        fprintf(stderr, "wiping userdata...\n");
         fb_queue_erase("userdata");
         fb_perform_format(usb, "userdata", 1, nullptr, nullptr);
-        fb_queue_erase("cache");
-        fb_perform_format(usb, "cache", 1, nullptr, nullptr);
+
+        std::string cache_type;
+        if (fb_getvar(usb, "partition-type:cache", &cache_type) && !cache_type.empty()) {
+            fprintf(stderr, "wiping cache...\n");
+            fb_queue_erase("cache");
+            fb_perform_format(usb, "cache", 1, nullptr, nullptr);
+        }
     }
     if (wants_reboot) {
         fb_queue_reboot();
@@ -1230,9 +1274,5 @@
         fb_queue_wait_for_disconnect();
     }
 
-    if (fb_queue_is_empty())
-        return 0;
-
-    status = fb_execute_queue(usb);
-    return (status) ? 1 : 0;
+    return fb_execute_queue(usb) ? EXIT_FAILURE : EXIT_SUCCESS;
 }
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index 091a70f..9e33531 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -32,6 +32,8 @@
 #include <inttypes.h>
 #include <stdlib.h>
 
+#include <string>
+
 #include "usb.h"
 
 struct sparse_file;
@@ -47,8 +49,7 @@
 #define FB_RESPONSE_SZ 64
 
 /* engine.c - high level command queue engine */
-int fb_getvar(struct usb_handle *usb, char *response, const char *fmt, ...);
-int fb_format_supported(usb_handle *usb, const char *partition, const char *type_override);
+bool fb_getvar(usb_handle* usb, const std::string& key, std::string* value);
 void fb_queue_flash(const char *ptn, void *data, uint32_t sz);
 void fb_queue_flash_sparse(const char *ptn, struct sparse_file *s, uint32_t sz);
 void fb_queue_erase(const char *ptn);
@@ -63,7 +64,6 @@
 void fb_queue_notice(const char *notice);
 void fb_queue_wait_for_disconnect(void);
 int fb_execute_queue(usb_handle *usb);
-int fb_queue_is_empty(void);
 
 /* util stuff */
 double now();
diff --git a/fastboot/fs.cpp b/fastboot/fs.cpp
index c58a505..90d8474 100644
--- a/fastboot/fs.cpp
+++ b/fastboot/fs.cpp
@@ -39,9 +39,9 @@
 #endif
 };
 
-const struct fs_generator* fs_get_generator(const char* fs_type) {
+const struct fs_generator* fs_get_generator(const std::string& fs_type) {
     for (size_t i = 0; i < sizeof(generators) / sizeof(*generators); i++) {
-        if (strcmp(generators[i].fs_type, fs_type) == 0) {
+        if (fs_type == generators[i].fs_type) {
             return generators + i;
         }
     }
diff --git a/fastboot/fs.h b/fastboot/fs.h
index 8444081..289488b 100644
--- a/fastboot/fs.h
+++ b/fastboot/fs.h
@@ -5,8 +5,7 @@
 
 struct fs_generator;
 
-const struct fs_generator* fs_get_generator(const char *fs_type);
+const struct fs_generator* fs_get_generator(const std::string& fs_type);
 int fs_generator_generate(const struct fs_generator* gen, int tmpFileNo, long long partSize);
 
 #endif
-
diff --git a/fs_mgr/fs_mgr_verity.cpp b/fs_mgr/fs_mgr_verity.cpp
index c358982..6983b72 100644
--- a/fs_mgr/fs_mgr_verity.cpp
+++ b/fs_mgr/fs_mgr_verity.cpp
@@ -765,8 +765,9 @@
             continue;
         }
 
-        if (current == VERITY_MODE_LOGGING) {
+        if (current != VERITY_MODE_DEFAULT) {
             *mode = current;
+            break;
         }
     }
 
@@ -784,7 +785,6 @@
 {
     alignas(dm_ioctl) char buffer[DM_BUF_SIZE];
     bool use_state = true;
-    bool use_state_for_device = true;
     char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)];
     char *mount_point;
     char propbuf[PROPERTY_VALUE_MAX];
@@ -801,12 +801,13 @@
     property_get("ro.boot.veritymode", propbuf, "");
 
     if (*propbuf != '\0') {
-        if (fs_mgr_load_verity_state(&mode) == -1) {
-            return -1;
-        }
         use_state = false; /* state is kept by the bootloader */
     }
 
+    if (fs_mgr_load_verity_state(&mode) == -1) {
+        return -1;
+    }
+
     fd = TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC));
 
     if (fd == -1) {
@@ -829,15 +830,6 @@
             continue;
         }
 
-        use_state_for_device = use_state;
-
-        if (use_state) {
-            if (get_verity_state_offset(&fstab->recs[i], &offset) < 0 ||
-                read_verity_state(fstab->recs[i].verity_loc, offset, &mode) < 0) {
-                use_state_for_device = false;
-            }
-        }
-
         mount_point = basename(fstab->recs[i].mount_point);
         verity_ioctl_init(io, mount_point, 0);
 
@@ -849,7 +841,7 @@
 
         status = &buffer[io->data_start + sizeof(struct dm_target_spec)];
 
-        if (use_state_for_device && *status == 'C') {
+        if (use_state && *status == 'C') {
             if (write_verity_state(fstab->recs[i].verity_loc, offset,
                     VERITY_MODE_LOGGING) < 0) {
                 continue;
diff --git a/healthd/Android.mk b/healthd/Android.mk
index 07e1d73..27c985c 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -35,7 +35,7 @@
 
 LOCAL_C_INCLUDES := bootable/recovery
 
-LOCAL_STATIC_LIBRARIES := libbatteryservice libbinder libminui libpng libz libutils libstdc++ libcutils liblog libm libc
+LOCAL_STATIC_LIBRARIES := libbatteryservice libbinder libminui libpng libz libutils libcutils liblog libm libc
 
 ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
 LOCAL_STATIC_LIBRARIES += libsuspend
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index e280fdc..cb77a8e 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -183,6 +183,7 @@
     props.chargerWirelessOnline = false;
     props.batteryStatus = BATTERY_STATUS_UNKNOWN;
     props.batteryHealth = BATTERY_HEALTH_UNKNOWN;
+    props.maxChargingCurrent = 0;
 
     if (!mHealthdConfig->batteryPresentPath.isEmpty())
         props.batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);
@@ -246,6 +247,15 @@
                     KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n",
                                  mChargerNames[i].string());
                 }
+                path.clear();
+                path.appendFormat("%s/%s/current_max", POWER_SUPPLY_SYSFS_PATH,
+                                  mChargerNames[i].string());
+                if (access(path.string(), R_OK) == 0) {
+                    int maxChargingCurrent = getIntField(path);
+                    if (props.maxChargingCurrent < maxChargingCurrent) {
+                        props.maxChargingCurrent = maxChargingCurrent;
+                    }
+                }
             }
         }
     }
@@ -382,9 +392,9 @@
     int v;
     char vs[128];
 
-    snprintf(vs, sizeof(vs), "ac: %d usb: %d wireless: %d\n",
+    snprintf(vs, sizeof(vs), "ac: %d usb: %d wireless: %d current_max: %d\n",
              props.chargerAcOnline, props.chargerUsbOnline,
-             props.chargerWirelessOnline);
+             props.chargerWirelessOnline, props.maxChargingCurrent);
     write(fd, vs, strlen(vs));
     snprintf(vs, sizeof(vs), "status: %d health: %d present: %d\n",
              props.batteryStatus, props.batteryHealth, props.batteryPresent);
diff --git a/include/log/logger.h b/include/log/logger.h
index f030dab..c795253 100644
--- a/include/log/logger.h
+++ b/include/log/logger.h
@@ -183,6 +183,8 @@
                                              pid_t pid);
 #define android_logger_list_close android_logger_list_free
 
+char android_log_timestamp();
+
 /*
  * log_id_t helpers
  */
diff --git a/include/log/logprint.h b/include/log/logprint.h
index 26b1ee5..204b3f2 100644
--- a/include/log/logprint.h
+++ b/include/log/logprint.h
@@ -42,6 +42,8 @@
     FORMAT_MODIFIER_PRINTABLE, /* converts non-printable to printable escapes */
     FORMAT_MODIFIER_YEAR,      /* Adds year to date */
     FORMAT_MODIFIER_ZONE,      /* Adds zone to date */
+    FORMAT_MODIFIER_EPOCH,     /* Print time as seconds since Jan 1 1970 */
+    FORMAT_MODIFIER_MONOTONIC, /* Print cpu time as seconds since start */
 } AndroidLogPrintFormat;
 
 typedef struct AndroidLogFormat_t AndroidLogFormat;
diff --git a/include/utils/StrongPointer.h b/include/utils/StrongPointer.h
index aba9577..50fde35 100644
--- a/include/utils/StrongPointer.h
+++ b/include/utils/StrongPointer.h
@@ -62,8 +62,10 @@
 
     sp(T* other);
     sp(const sp<T>& other);
+    sp(sp<T>&& other);
     template<typename U> sp(U* other);
     template<typename U> sp(const sp<U>& other);
+    template<typename U> sp(sp<U>&& other);
 
     ~sp();
 
@@ -71,8 +73,10 @@
 
     sp& operator = (T* other);
     sp& operator = (const sp<T>& other);
+    sp& operator = (sp<T>&& other);
 
     template<typename U> sp& operator = (const sp<U>& other);
+    template<typename U> sp& operator = (sp<U>&& other);
     template<typename U> sp& operator = (U* other);
 
     //! Special optimization for use by ProcessState (and nobody else).
@@ -123,6 +127,12 @@
         m_ptr->incStrong(this);
 }
 
+template<typename T>
+sp<T>::sp(sp<T>&& other)
+        : m_ptr(other.m_ptr) {
+    other.m_ptr = nullptr;
+}
+
 template<typename T> template<typename U>
 sp<T>::sp(U* other)
         : m_ptr(other) {
@@ -137,6 +147,12 @@
         m_ptr->incStrong(this);
 }
 
+template<typename T> template<typename U>
+sp<T>::sp(sp<U>&& other)
+        : m_ptr(other.m_ptr) {
+    other.m_ptr = nullptr;
+}
+
 template<typename T>
 sp<T>::~sp() {
     if (m_ptr)
@@ -155,6 +171,15 @@
 }
 
 template<typename T>
+sp<T>& sp<T>::operator =(sp<T>&& other) {
+    if (m_ptr)
+        m_ptr->decStrong(this);
+    m_ptr = other.m_ptr;
+    other.m_ptr = nullptr;
+    return *this;
+}
+
+template<typename T>
 sp<T>& sp<T>::operator =(T* other) {
     if (other)
         other->incStrong(this);
@@ -176,6 +201,15 @@
 }
 
 template<typename T> template<typename U>
+sp<T>& sp<T>::operator =(sp<U>&& other) {
+    if (m_ptr)
+        m_ptr->decStrong(this);
+    m_ptr = other.m_ptr;
+    other.m_ptr = nullptr;
+    return *this;
+}
+
+template<typename T> template<typename U>
 sp<T>& sp<T>::operator =(U* other) {
     if (other)
         ((T*) other)->incStrong(this);
diff --git a/include/ziparchive/zip_writer.h b/include/ziparchive/zip_writer.h
index 9ee4abe..d996c4a 100644
--- a/include/ziparchive/zip_writer.h
+++ b/include/ziparchive/zip_writer.h
@@ -21,9 +21,11 @@
 #include <utils/Compat.h>
 
 #include <cstdio>
-#include <string>
 #include <ctime>
+#include <memory>
+#include <string>
 #include <vector>
+#include <zlib.h>
 
 /**
  * Writes a Zip file via a stateful interface.
@@ -112,8 +114,6 @@
 private:
   DISALLOW_COPY_AND_ASSIGN(ZipWriter);
 
-  int32_t HandleError(int32_t error_code);
-
   struct FileInfo {
     std::string path;
     uint16_t compression_method;
@@ -125,6 +125,12 @@
     uint32_t local_file_header_offset;
   };
 
+  int32_t HandleError(int32_t error_code);
+  int32_t PrepareDeflate();
+  int32_t StoreBytes(FileInfo* file, const void* data, size_t len);
+  int32_t CompressBytes(FileInfo* file, const void* data, size_t len);
+  int32_t FlushCompressedBytes(FileInfo* file);
+
   enum class State {
     kWritingZip,
     kWritingEntry,
@@ -136,6 +142,9 @@
   off64_t current_offset_;
   State state_;
   std::vector<FileInfo> files_;
+
+  std::unique_ptr<z_stream, void(*)(z_stream*)> z_stream_;
+  std::vector<uint8_t> buffer_;
 };
 
 #endif /* LIBZIPARCHIVE_ZIPWRITER_H_ */
diff --git a/init/Android.mk b/init/Android.mk
index 8e45a7a..d6cb4e5 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -92,9 +92,9 @@
     libbase \
     libext4_utils_static \
     libutils \
-    liblog \
     libc \
     libselinux \
+    liblog \
     libmincrypt \
     libcrypto_static \
     libc++_static \
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index a768762..e1e0c48 100644
--- a/init/bootchart.cpp
+++ b/init/bootchart.cpp
@@ -89,7 +89,7 @@
     if (out == NULL) {
         return;
     }
-    fprintf(out, "version = Android init 0.8 " __TIME__  "\n");
+    fprintf(out, "version = Android init 0.8\n");
     fprintf(out, "title = Boot chart for Android (%s)\n", date);
     fprintf(out, "system.uname = %s %s %s %s\n", uts.sysname, uts.release, uts.version, uts.machine);
     fprintf(out, "system.release = %s\n", fingerprint.c_str());
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 3ffa2e8..36ecbb8 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -788,13 +788,13 @@
     return 0;
 }
 
-int do_load_persist_props(const std::vector<std::string>& args) {
+static int do_load_persist_props(const std::vector<std::string>& args) {
     load_persist_props();
     return 0;
 }
 
-static int do_load_all_props(const std::vector<std::string>& args) {
-    load_all_props();
+static int do_load_system_props(const std::vector<std::string>& args) {
+    load_system_props();
     return 0;
 }
 
@@ -818,16 +818,26 @@
     return 0;
 }
 
+static bool is_file_crypto() {
+    std::string value = property_get("ro.crypto.type");
+    return value == "file";
+}
+
 static int do_installkey(const std::vector<std::string>& args) {
-    std::string prop_value = property_get("ro.crypto.type");
-    if (prop_value != "file") {
+    if (!is_file_crypto()) {
         return 0;
     }
-
     return e4crypt_create_device_key(args[1].c_str(),
                                      do_installkeys_ensure_dir_exists);
 }
 
+static int do_setusercryptopolicies(const std::vector<std::string>& args) {
+    if (!is_file_crypto()) {
+        return 0;
+    }
+    return e4crypt_set_user_crypto_policies(args[1].c_str());
+}
+
 BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
     constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
     static const Map builtin_functions = {
@@ -846,8 +856,8 @@
         {"ifup",                    {1,     1,    do_ifup}},
         {"insmod",                  {1,     kMax, do_insmod}},
         {"installkey",              {1,     1,    do_installkey}},
-        {"load_all_props",          {0,     0,    do_load_all_props}},
         {"load_persist_props",      {0,     0,    do_load_persist_props}},
+        {"load_system_props",       {0,     0,    do_load_system_props}},
         {"loglevel",                {1,     1,    do_loglevel}},
         {"mkdir",                   {1,     4,    do_mkdir}},
         {"mount_all",               {1,     1,    do_mount_all}},
@@ -860,6 +870,7 @@
         {"rmdir",                   {1,     1,    do_rmdir}},
         {"setprop",                 {2,     2,    do_setprop}},
         {"setrlimit",               {3,     3,    do_setrlimit}},
+        {"setusercryptopolicies",   {1,     1,    do_setusercryptopolicies}},
         {"start",                   {1,     1,    do_start}},
         {"stop",                    {1,     1,    do_stop}},
         {"swapon_all",              {1,     1,    do_swapon_all}},
diff --git a/init/property_service.cpp b/init/property_service.cpp
index ebf768e..96b4a37 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -559,16 +559,10 @@
     close(fd);
 }
 
-void load_all_props() {
+void load_system_props() {
     load_properties_from_file(PROP_PATH_SYSTEM_BUILD, NULL);
     load_properties_from_file(PROP_PATH_VENDOR_BUILD, NULL);
     load_properties_from_file(PROP_PATH_FACTORY, "ro.*");
-
-    load_override_properties();
-
-    /* Read persistent properties after all default values have been loaded. */
-    load_persistent_properties();
-
     load_recovery_id_prop();
 }
 
diff --git a/init/property_service.h b/init/property_service.h
index 1a48fb1..8b76d2b 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -30,7 +30,7 @@
 extern void property_init(void);
 extern void property_load_boot_defaults(void);
 extern void load_persist_props(void);
-extern void load_all_props(void);
+extern void load_system_props(void);
 extern void start_property_service(void);
 void get_property_workspace(int *fd, int *sz);
 std::string property_get(const char* name);
diff --git a/libcutils/fs_config.c b/libcutils/fs_config.c
index 970b386..2a178aa 100644
--- a/libcutils/fs_config.c
+++ b/libcutils/fs_config.c
@@ -125,6 +125,8 @@
     { 00644, AID_MEDIA_RW,  AID_MEDIA_RW,  0, "data/media/*" },
     { 00644, AID_SYSTEM,    AID_SYSTEM,    0, "data/app-private/*" },
     { 00644, AID_APP,       AID_APP,       0, "data/data/*" },
+    { 00750, AID_ROOT,      AID_SHELL,     0, "data/nativetest/*" },
+    { 00750, AID_ROOT,      AID_SHELL,     0, "data/nativetest64/*" },
 
     /* the following four files are INTENTIONALLY set-uid, but they
      * are NOT included on user builds. */
diff --git a/liblog/log_is_loggable.c b/liblog/log_is_loggable.c
index cd85ff6..814d96d 100644
--- a/liblog/log_is_loggable.c
+++ b/liblog/log_is_loggable.c
@@ -176,3 +176,28 @@
     int logLevel = __android_log_level(tag, def);
     return logLevel >= 0 && prio >= logLevel;
 }
+
+char android_log_timestamp()
+{
+    static struct cache r_time_cache = { NULL, -1, 0 };
+    static struct cache p_time_cache = { NULL, -1, 0 };
+    static uint32_t serial;
+    uint32_t current_serial;
+    char retval;
+
+    pthread_mutex_lock(&lock);
+
+    current_serial = __system_property_area_serial();
+    if (current_serial != serial) {
+        refresh_cache(&r_time_cache, "ro.logd.timestamp");
+        refresh_cache(&p_time_cache, "persist.logd.timestamp");
+        serial = current_serial;
+    }
+    if (!(retval = p_time_cache.c)) {
+        retval = r_time_cache.c;
+    }
+
+    pthread_mutex_unlock(&lock);
+
+    return tolower(retval ?: 'r');
+}
diff --git a/liblog/logd_write.c b/liblog/logd_write.c
index bdee28f..a4310ae 100644
--- a/liblog/logd_write.c
+++ b/liblog/logd_write.c
@@ -191,7 +191,11 @@
      *  };
      */
 
-    clock_gettime(CLOCK_REALTIME, &ts);
+    if (android_log_timestamp() == 'm') {
+        clock_gettime(CLOCK_MONOTONIC, &ts);
+    } else {
+        clock_gettime(CLOCK_REALTIME, &ts);
+    }
 
     pmsg_header.magic = LOGGER_MAGIC;
     pmsg_header.len = sizeof(pmsg_header) + sizeof(header);
diff --git a/liblog/logprint.c b/liblog/logprint.c
index b6dba2e..9f12c96 100644
--- a/liblog/logprint.c
+++ b/liblog/logprint.c
@@ -29,9 +29,13 @@
 #include <inttypes.h>
 #include <sys/param.h>
 
+#include <cutils/list.h>
 #include <log/logd.h>
 #include <log/logprint.h>
 
+#define MS_PER_NSEC 1000000
+#define US_PER_NSEC 1000
+
 /* open coded fragment, prevent circular dependencies */
 #define WEAK static
 
@@ -50,6 +54,8 @@
     bool printable_output;
     bool year_output;
     bool zone_output;
+    bool epoch_output;
+    bool monotonic_output;
 };
 
 /*
@@ -196,10 +202,14 @@
     p_ret->printable_output = false;
     p_ret->year_output = false;
     p_ret->zone_output = false;
+    p_ret->epoch_output = false;
+    p_ret->monotonic_output = android_log_timestamp() == 'm';
 
     return p_ret;
 }
 
+static list_declare(convertHead);
+
 void android_log_format_free(AndroidLogFormat *p_format)
 {
     FilterInfo *p_info, *p_info_old;
@@ -214,10 +224,15 @@
     }
 
     free(p_format);
+
+    /* Free conversion resource, can always be reconstructed */
+    while (!list_empty(&convertHead)) {
+        struct listnode *node = list_head(&convertHead);
+        list_remove(node);
+        free(node);
+    }
 }
 
-
-
 int android_log_setPrintFormat(AndroidLogFormat *p_format,
         AndroidLogPrintFormat format)
 {
@@ -237,6 +252,12 @@
     case FORMAT_MODIFIER_ZONE:
         p_format->zone_output = !p_format->zone_output;
         return 0;
+    case FORMAT_MODIFIER_EPOCH:
+        p_format->epoch_output = true;
+        return 0;
+    case FORMAT_MODIFIER_MONOTONIC:
+        p_format->monotonic_output = true;
+        return 0;
     default:
         break;
     }
@@ -267,6 +288,8 @@
     else if (strcmp(formatString, "printable") == 0) format = FORMAT_MODIFIER_PRINTABLE;
     else if (strcmp(formatString, "year") == 0) format = FORMAT_MODIFIER_YEAR;
     else if (strcmp(formatString, "zone") == 0) format = FORMAT_MODIFIER_ZONE;
+    else if (strcmp(formatString, "epoch") == 0) format = FORMAT_MODIFIER_EPOCH;
+    else if (strcmp(formatString, "monotonic") == 0) format = FORMAT_MODIFIER_MONOTONIC;
     else {
         extern char *tzname[2];
         static const char gmt[] = "GMT";
@@ -913,6 +936,285 @@
     return p - begin;
 }
 
+char *readSeconds(char *e, struct timespec *t)
+{
+    unsigned long multiplier;
+    char *p;
+    t->tv_sec = strtoul(e, &p, 10);
+    if (*p != '.') {
+        return NULL;
+    }
+    t->tv_nsec = 0;
+    multiplier = NS_PER_SEC;
+    while (isdigit(*++p) && (multiplier /= 10)) {
+        t->tv_nsec += (*p - '0') * multiplier;
+    }
+    return p;
+}
+
+static struct timespec *sumTimespec(struct timespec *left,
+                                    struct timespec *right)
+{
+    left->tv_nsec += right->tv_nsec;
+    left->tv_sec += right->tv_sec;
+    if (left->tv_nsec >= (long)NS_PER_SEC) {
+        left->tv_nsec -= NS_PER_SEC;
+        left->tv_sec += 1;
+    }
+    return left;
+}
+
+static struct timespec *subTimespec(struct timespec *result,
+                                    struct timespec *left,
+                                    struct timespec *right)
+{
+    result->tv_nsec = left->tv_nsec - right->tv_nsec;
+    result->tv_sec = left->tv_sec - right->tv_sec;
+    if (result->tv_nsec < 0) {
+        result->tv_nsec += NS_PER_SEC;
+        result->tv_sec -= 1;
+    }
+    return result;
+}
+
+static long long nsecTimespec(struct timespec *now)
+{
+    return (long long)now->tv_sec * NS_PER_SEC + now->tv_nsec;
+}
+
+static void convertMonotonic(struct timespec *result,
+                             const AndroidLogEntry *entry)
+{
+    struct listnode *node;
+    struct conversionList {
+        struct listnode node; /* first */
+        struct timespec time;
+        struct timespec convert;
+    } *list, *next;
+    struct timespec time, convert;
+
+    /* If we do not have a conversion list, build one up */
+    if (list_empty(&convertHead)) {
+        bool suspended_pending = false;
+        struct timespec suspended_monotonic = { 0, 0 };
+        struct timespec suspended_diff = { 0, 0 };
+
+        /*
+         * Read dmesg for _some_ synchronization markers and insert
+         * Anything in the Android Logger before the dmesg logging span will
+         * be highly suspect regarding the monotonic time calculations.
+         */
+        FILE *p = popen("/system/bin/dmesg", "r");
+        if (p) {
+            char *line = NULL;
+            size_t len = 0;
+            while (getline(&line, &len, p) > 0) {
+                static const char suspend[] = "PM: suspend entry ";
+                static const char resume[] = "PM: suspend exit ";
+                static const char healthd[] = "healthd";
+                static const char battery[] = ": battery ";
+                static const char suspended[] = "Suspended for ";
+                struct timespec monotonic;
+                struct tm tm;
+                char *cp, *e = line;
+                bool add_entry = true;
+
+                if (*e == '<') {
+                    while (*e && (*e != '>')) {
+                        ++e;
+                    }
+                    if (*e != '>') {
+                        continue;
+                    }
+                }
+                if (*e != '[') {
+                    continue;
+                }
+                while (*++e == ' ') {
+                    ;
+                }
+                e = readSeconds(e, &monotonic);
+                if (!e || (*e != ']')) {
+                    continue;
+                }
+
+                if ((e = strstr(e, suspend))) {
+                    e += sizeof(suspend) - 1;
+                } else if ((e = strstr(line, resume))) {
+                    e += sizeof(resume) - 1;
+                } else if (((e = strstr(line, healthd)))
+                        && ((e = strstr(e + sizeof(healthd) - 1, battery)))) {
+                    /* NB: healthd is roughly 150us late, worth the price to
+                     * deal with ntp-induced or hardware clock drift. */
+                    e += sizeof(battery) - 1;
+                } else if ((e = strstr(line, suspended))) {
+                    e += sizeof(suspended) - 1;
+                    e = readSeconds(e, &time);
+                    if (!e) {
+                        continue;
+                    }
+                    add_entry = false;
+                    suspended_pending = true;
+                    suspended_monotonic = monotonic;
+                    suspended_diff = time;
+                } else {
+                    continue;
+                }
+                if (add_entry) {
+                    /* look for "????-??-?? ??:??:??.????????? UTC" */
+                    cp = strstr(e, " UTC");
+                    if (!cp || ((cp - e) < 29) || (cp[-10] != '.')) {
+                        continue;
+                    }
+                    e = cp - 29;
+                    cp = readSeconds(cp - 10, &time);
+                    if (!cp) {
+                        continue;
+                    }
+                    cp = strptime(e, "%Y-%m-%d %H:%M:%S.", &tm);
+                    if (!cp) {
+                        continue;
+                    }
+                    cp = getenv(tz);
+                    if (cp) {
+                        cp = strdup(cp);
+                    }
+                    setenv(tz, utc, 1);
+                    time.tv_sec = mktime(&tm);
+                    if (cp) {
+                        setenv(tz, cp, 1);
+                        free(cp);
+                    } else {
+                        unsetenv(tz);
+                    }
+                    list = calloc(1, sizeof(struct conversionList));
+                    list_init(&list->node);
+                    list->time = time;
+                    subTimespec(&list->convert, &time, &monotonic);
+                    list_add_tail(&convertHead, &list->node);
+                }
+                if (suspended_pending && !list_empty(&convertHead)) {
+                    list = node_to_item(list_tail(&convertHead),
+                                        struct conversionList, node);
+                    if (subTimespec(&time,
+                                    subTimespec(&time,
+                                                &list->time,
+                                                &list->convert),
+                                    &suspended_monotonic)->tv_sec > 0) {
+                        /* resume, what is convert factor before? */
+                        subTimespec(&convert, &list->convert, &suspended_diff);
+                    } else {
+                        /* suspend */
+                        convert = list->convert;
+                    }
+                    time = suspended_monotonic;
+                    sumTimespec(&time, &convert);
+                    /* breakpoint just before sleep */
+                    list = calloc(1, sizeof(struct conversionList));
+                    list_init(&list->node);
+                    list->time = time;
+                    list->convert = convert;
+                    list_add_tail(&convertHead, &list->node);
+                    /* breakpoint just after sleep */
+                    list = calloc(1, sizeof(struct conversionList));
+                    list_init(&list->node);
+                    list->time = time;
+                    sumTimespec(&list->time, &suspended_diff);
+                    list->convert = convert;
+                    sumTimespec(&list->convert, &suspended_diff);
+                    list_add_tail(&convertHead, &list->node);
+                    suspended_pending = false;
+                }
+            }
+            pclose(p);
+        }
+        /* last entry is our current time conversion */
+        list = calloc(1, sizeof(struct conversionList));
+        list_init(&list->node);
+        clock_gettime(CLOCK_REALTIME, &list->time);
+        clock_gettime(CLOCK_MONOTONIC, &convert);
+        clock_gettime(CLOCK_MONOTONIC, &time);
+        /* Correct for instant clock_gettime latency (syscall or ~30ns) */
+        subTimespec(&time, &convert, subTimespec(&time, &time, &convert));
+        /* Calculate conversion factor */
+        subTimespec(&list->convert, &list->time, &time);
+        list_add_tail(&convertHead, &list->node);
+        if (suspended_pending) {
+            /* manufacture a suspend @ point before */
+            subTimespec(&convert, &list->convert, &suspended_diff);
+            time = suspended_monotonic;
+            sumTimespec(&time, &convert);
+            /* breakpoint just after sleep */
+            list = calloc(1, sizeof(struct conversionList));
+            list_init(&list->node);
+            list->time = time;
+            sumTimespec(&list->time, &suspended_diff);
+            list->convert = convert;
+            sumTimespec(&list->convert, &suspended_diff);
+            list_add_head(&convertHead, &list->node);
+            /* breakpoint just before sleep */
+            list = calloc(1, sizeof(struct conversionList));
+            list_init(&list->node);
+            list->time = time;
+            list->convert = convert;
+            list_add_head(&convertHead, &list->node);
+        }
+    }
+
+    /* Find the breakpoint in the conversion list */
+    list = node_to_item(list_head(&convertHead), struct conversionList, node);
+    next = NULL;
+    list_for_each(node, &convertHead) {
+        next = node_to_item(node, struct conversionList, node);
+        if (entry->tv_sec < next->time.tv_sec) {
+            break;
+        } else if (entry->tv_sec == next->time.tv_sec) {
+            if (entry->tv_nsec < next->time.tv_nsec) {
+                break;
+            }
+        }
+        list = next;
+    }
+
+    /* blend time from one breakpoint to the next */
+    convert = list->convert;
+    if (next) {
+        unsigned long long total, run;
+
+        total = nsecTimespec(subTimespec(&time, &next->time, &list->time));
+        time.tv_sec = entry->tv_sec;
+        time.tv_nsec = entry->tv_nsec;
+        run = nsecTimespec(subTimespec(&time, &time, &list->time));
+        if (run < total) {
+            long long crun;
+
+            float f = nsecTimespec(subTimespec(&time, &next->convert, &convert));
+            f *= run;
+            f /= total;
+            crun = f;
+            convert.tv_sec += crun / (long long)NS_PER_SEC;
+            if (crun < 0) {
+                convert.tv_nsec -= (-crun) % NS_PER_SEC;
+                if (convert.tv_nsec < 0) {
+                    convert.tv_nsec += NS_PER_SEC;
+                    convert.tv_sec -= 1;
+                }
+            } else {
+                convert.tv_nsec += crun % NS_PER_SEC;
+                if (convert.tv_nsec >= (long)NS_PER_SEC) {
+                    convert.tv_nsec -= NS_PER_SEC;
+                    convert.tv_sec += 1;
+                }
+            }
+        }
+    }
+
+    /* Apply the correction factor */
+    result->tv_sec = entry->tv_sec;
+    result->tv_nsec = entry->tv_nsec;
+    subTimespec(result, result, &convert);
+}
+
 /**
  * Formats a log message into a buffer
  *
@@ -936,7 +1238,9 @@
     char prefixBuf[128], suffixBuf[128];
     char priChar;
     int prefixSuffixIsHeaderFooter = 0;
-    char * ret = NULL;
+    char *ret = NULL;
+    time_t now;
+    unsigned long nsec;
 
     priChar = filterPriToChar(entry->priority);
     size_t prefixLen = 0, suffixLen = 0;
@@ -954,23 +1258,44 @@
      * The caller may have affected the timezone environment, this is
      * expected to be sensitive to that.
      */
+    now = entry->tv_sec;
+    nsec = entry->tv_nsec;
+    if (p_format->monotonic_output) {
+        // prevent convertMonotonic from being called if logd is monotonic
+        if (android_log_timestamp() != 'm') {
+            struct timespec time;
+            convertMonotonic(&time, entry);
+            now = time.tv_sec;
+            nsec = time.tv_nsec;
+        }
+    }
+    if (now < 0) {
+        nsec = NS_PER_SEC - nsec;
+    }
+    if (p_format->epoch_output || p_format->monotonic_output) {
+        ptm = NULL;
+        snprintf(timeBuf, sizeof(timeBuf),
+                 p_format->monotonic_output ? "%6lld" : "%19lld",
+                 (long long)now);
+    } else {
 #if !defined(_WIN32)
-    ptm = localtime_r(&(entry->tv_sec), &tmBuf);
+        ptm = localtime_r(&now, &tmBuf);
 #else
-    ptm = localtime(&(entry->tv_sec));
+        ptm = localtime(&now);
 #endif
-    strftime(timeBuf, sizeof(timeBuf),
-             &"%Y-%m-%d %H:%M:%S"[p_format->year_output ? 0 : 3],
-             ptm);
+        strftime(timeBuf, sizeof(timeBuf),
+                 &"%Y-%m-%d %H:%M:%S"[p_format->year_output ? 0 : 3],
+                 ptm);
+    }
     len = strlen(timeBuf);
     if (p_format->usec_time_output) {
         len += snprintf(timeBuf + len, sizeof(timeBuf) - len,
-                        ".%06ld", entry->tv_nsec / 1000);
+                        ".%06ld", nsec / US_PER_NSEC);
     } else {
         len += snprintf(timeBuf + len, sizeof(timeBuf) - len,
-                        ".%03ld", entry->tv_nsec / 1000000);
+                        ".%03ld", nsec / MS_PER_NSEC);
     }
-    if (p_format->zone_output) {
+    if (p_format->zone_output && ptm) {
         strftime(timeBuf + len, sizeof(timeBuf) - len, " %z", ptm);
     }
 
diff --git a/libpixelflinger/Android.mk b/libpixelflinger/Android.mk
index 87f5a34..55d78f9 100644
--- a/libpixelflinger/Android.mk
+++ b/libpixelflinger/Android.mk
@@ -14,8 +14,6 @@
 	codeflinger/load_store.cpp \
 	codeflinger/blending.cpp \
 	codeflinger/texturing.cpp \
-	codeflinger/tinyutils/SharedBuffer.cpp \
-	codeflinger/tinyutils/VectorImpl.cpp \
 	fixed.cpp.arm \
 	picker.cpp.arm \
 	pixelflinger.cpp.arm \
@@ -72,8 +70,9 @@
 LOCAL_SRC_FILES_mips64 := $(PIXELFLINGER_SRC_FILES_mips64)
 LOCAL_CFLAGS := $(PIXELFLINGER_CFLAGS)
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
-LOCAL_C_INCLUDES += $(LOCAL_EXPORT_C_INCLUDE_DIRS)
-LOCAL_SHARED_LIBRARIES := libcutils liblog
+LOCAL_C_INCLUDES += $(LOCAL_EXPORT_C_INCLUDE_DIRS) \
+		    external/safe-iop/include
+LOCAL_SHARED_LIBRARIES := libcutils liblog libutils
 
 # Really this should go away entirely or at least not depend on
 # libhardware, but this at least gets us built.
diff --git a/libpixelflinger/codeflinger/ARMAssembler.h b/libpixelflinger/codeflinger/ARMAssembler.h
index c03dd9a..e0c7646 100644
--- a/libpixelflinger/codeflinger/ARMAssembler.h
+++ b/libpixelflinger/codeflinger/ARMAssembler.h
@@ -21,9 +21,9 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include "tinyutils/Vector.h"
-#include "tinyutils/KeyedVector.h"
 #include "tinyutils/smartpointer.h"
+#include "utils/Vector.h"
+#include "utils/KeyedVector.h"
 
 #include "ARMAssemblerInterface.h"
 #include "CodeCache.h"
diff --git a/libpixelflinger/codeflinger/Arm64Assembler.h b/libpixelflinger/codeflinger/Arm64Assembler.h
index 8479270..c9be116 100644
--- a/libpixelflinger/codeflinger/Arm64Assembler.h
+++ b/libpixelflinger/codeflinger/Arm64Assembler.h
@@ -32,9 +32,9 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include "tinyutils/Vector.h"
-#include "tinyutils/KeyedVector.h"
 #include "tinyutils/smartpointer.h"
+#include "utils/Vector.h"
+#include "utils/KeyedVector.h"
 
 #include "tinyutils/smartpointer.h"
 #include "codeflinger/ARMAssemblerInterface.h"
diff --git a/libpixelflinger/codeflinger/CodeCache.h b/libpixelflinger/codeflinger/CodeCache.h
index fa67dd0..0fb6fd5 100644
--- a/libpixelflinger/codeflinger/CodeCache.h
+++ b/libpixelflinger/codeflinger/CodeCache.h
@@ -23,7 +23,7 @@
 #include <pthread.h>
 #include <sys/types.h>
 
-#include "tinyutils/KeyedVector.h"
+#include "utils/KeyedVector.h"
 #include "tinyutils/smartpointer.h"
 
 namespace android {
diff --git a/libpixelflinger/codeflinger/MIPSAssembler.h b/libpixelflinger/codeflinger/MIPSAssembler.h
index 68a2b4f..06cb0d0 100644
--- a/libpixelflinger/codeflinger/MIPSAssembler.h
+++ b/libpixelflinger/codeflinger/MIPSAssembler.h
@@ -21,9 +21,9 @@
 #include <stdint.h>
 #include <sys/types.h>
 
-#include "tinyutils/KeyedVector.h"
-#include "tinyutils/Vector.h"
 #include "tinyutils/smartpointer.h"
+#include "utils/KeyedVector.h"
+#include "utils/Vector.h"
 
 #include "ARMAssemblerInterface.h"
 #include "CodeCache.h"
diff --git a/libpixelflinger/codeflinger/tinyutils/Errors.h b/libpixelflinger/codeflinger/tinyutils/Errors.h
deleted file mode 100644
index 47ae9d7..0000000
--- a/libpixelflinger/codeflinger/tinyutils/Errors.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2007 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 ANDROID_PIXELFLINGER_ERRORS_H
-#define ANDROID_PIXELFLINGER_ERRORS_H
-
-#include <sys/types.h>
-#include <errno.h>
-
-namespace android {
-namespace tinyutils {
-
-// use this type to return error codes
-typedef int32_t     status_t;
-
-/*
- * Error codes. 
- * All error codes are negative values.
- */
-
-enum {
-    NO_ERROR          = 0,    // No errors.
-    NO_MEMORY           = -ENOMEM,
-    BAD_VALUE           = -EINVAL,
-    BAD_INDEX           = -EOVERFLOW,
-    NAME_NOT_FOUND      = -ENOENT,
-};
-
-
-} // namespace tinyutils
-} // namespace android
-    
-// ---------------------------------------------------------------------------
-    
-#endif // ANDROID_PIXELFLINGER_ERRORS_H
diff --git a/libpixelflinger/codeflinger/tinyutils/KeyedVector.h b/libpixelflinger/codeflinger/tinyutils/KeyedVector.h
deleted file mode 100644
index 9d8668b..0000000
--- a/libpixelflinger/codeflinger/tinyutils/KeyedVector.h
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright 2005 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 ANDROID_PIXELFLINGER_KEYED_VECTOR_H
-#define ANDROID_PIXELFLINGER_KEYED_VECTOR_H
-
-#include <assert.h>
-#include <stdint.h>
-#include <sys/types.h>
-
-#include "Errors.h"
-#include "SortedVector.h"
-#include "TypeHelpers.h"
-
-// ---------------------------------------------------------------------------
-
-namespace android {
-namespace tinyutils {
-
-template <typename KEY, typename VALUE>
-class KeyedVector
-{
-public:
-    typedef KEY    key_type;
-    typedef VALUE  value_type;
-
-    inline                  KeyedVector();
-
-    /*
-     * empty the vector
-     */
-
-    inline  void            clear()                     { mVector.clear(); }
-
-    /*! 
-     * vector stats
-     */
-
-    //! returns number of items in the vector
-    inline  size_t          size() const                { return mVector.size(); }
-    //! returns wether or not the vector is empty
-    inline  bool            isEmpty() const             { return mVector.isEmpty(); }
-    //! returns how many items can be stored without reallocating the backing store
-    inline  size_t          capacity() const            { return mVector.capacity(); }
-    //! setst the capacity. capacity can never be reduced less than size()
-    inline ssize_t          setCapacity(size_t size)    { return mVector.setCapacity(size); }
-    
-    /*! 
-     * accessors
-     */
-            const VALUE&    valueFor(const KEY& key) const;
-            const VALUE&    valueAt(size_t index) const;
-            const KEY&      keyAt(size_t index) const;
-            ssize_t         indexOfKey(const KEY& key) const;
-
-    /*!
-     * modifing the array
-     */
-
-            VALUE&          editValueFor(const KEY& key);
-            VALUE&          editValueAt(size_t index);
-
-            /*! 
-             * add/insert/replace items
-             */
-             
-            ssize_t         add(const KEY& key, const VALUE& item);
-            ssize_t         replaceValueFor(const KEY& key, const VALUE& item);
-            ssize_t         replaceValueAt(size_t index, const VALUE& item);
-
-    /*!
-     * remove items
-     */
-
-            ssize_t         removeItem(const KEY& key);
-            ssize_t         removeItemsAt(size_t index, size_t count = 1);
-            
-private:
-            SortedVector< key_value_pair_t<KEY, VALUE> >    mVector;
-};
-
-// ---------------------------------------------------------------------------
-
-/**
- * Variation of KeyedVector that holds a default value to return when
- * valueFor() is called with a key that doesn't exist.
- */
-template <typename KEY, typename VALUE>
-class DefaultKeyedVector : public KeyedVector<KEY, VALUE>
-{
-public:
-    inline                  DefaultKeyedVector(const VALUE& defValue = VALUE());
-            const VALUE&    valueFor(const KEY& key) const;
-
-private:
-            VALUE                                           mDefault;
-};
-
-// ---------------------------------------------------------------------------
-
-template<typename KEY, typename VALUE> inline
-KeyedVector<KEY,VALUE>::KeyedVector()
-{
-}
-
-template<typename KEY, typename VALUE> inline
-ssize_t KeyedVector<KEY,VALUE>::indexOfKey(const KEY& key) const {
-    return mVector.indexOf( key_value_pair_t<KEY,VALUE>(key) );
-}
-
-template<typename KEY, typename VALUE> inline
-const VALUE& KeyedVector<KEY,VALUE>::valueFor(const KEY& key) const {
-    ssize_t i = indexOfKey(key);
-    assert(i>=0);
-    return mVector.itemAt(i).value;
-}
-
-template<typename KEY, typename VALUE> inline
-const VALUE& KeyedVector<KEY,VALUE>::valueAt(size_t index) const {
-    return mVector.itemAt(index).value;
-}
-
-template<typename KEY, typename VALUE> inline
-const KEY& KeyedVector<KEY,VALUE>::keyAt(size_t index) const {
-    return mVector.itemAt(index).key;
-}
-
-template<typename KEY, typename VALUE> inline
-VALUE& KeyedVector<KEY,VALUE>::editValueFor(const KEY& key) {
-    ssize_t i = indexOfKey(key);
-    assert(i>=0);
-    return mVector.editItemAt(i).value;
-}
-
-template<typename KEY, typename VALUE> inline
-VALUE& KeyedVector<KEY,VALUE>::editValueAt(size_t index) {
-    return mVector.editItemAt(index).value;
-}
-
-template<typename KEY, typename VALUE> inline
-ssize_t KeyedVector<KEY,VALUE>::add(const KEY& key, const VALUE& value) {
-    return mVector.add( key_value_pair_t<KEY,VALUE>(key, value) );
-}
-
-template<typename KEY, typename VALUE> inline
-ssize_t KeyedVector<KEY,VALUE>::replaceValueFor(const KEY& key, const VALUE& value) {
-    key_value_pair_t<KEY,VALUE> pair(key, value);
-    mVector.remove(pair);
-    return mVector.add(pair);
-}
-
-template<typename KEY, typename VALUE> inline
-ssize_t KeyedVector<KEY,VALUE>::replaceValueAt(size_t index, const VALUE& item) {
-    if (index<size()) {
-        mVector.editValueAt(index).value = item;
-        return index;
-    }
-    return BAD_INDEX;
-}
-
-template<typename KEY, typename VALUE> inline
-ssize_t KeyedVector<KEY,VALUE>::removeItem(const KEY& key) {
-    return mVector.remove(key_value_pair_t<KEY,VALUE>(key));
-}
-
-template<typename KEY, typename VALUE> inline
-ssize_t KeyedVector<KEY, VALUE>::removeItemsAt(size_t index, size_t count) {
-    return mVector.removeItemsAt(index, count);
-}
-
-// ---------------------------------------------------------------------------
-
-template<typename KEY, typename VALUE> inline
-DefaultKeyedVector<KEY,VALUE>::DefaultKeyedVector(const VALUE& defValue)
-    : mDefault(defValue)
-{
-}
-
-template<typename KEY, typename VALUE> inline
-const VALUE& DefaultKeyedVector<KEY,VALUE>::valueFor(const KEY& key) const {
-    ssize_t i = indexOfKey(key);
-    return i >= 0 ? KeyedVector<KEY,VALUE>::valueAt(i) : mDefault;
-}
-
-} // namespace tinyutils
-} // namespace android
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_PIXELFLINGER_KEYED_VECTOR_H
diff --git a/libpixelflinger/codeflinger/tinyutils/SharedBuffer.cpp b/libpixelflinger/codeflinger/tinyutils/SharedBuffer.cpp
deleted file mode 100644
index ef453fa..0000000
--- a/libpixelflinger/codeflinger/tinyutils/SharedBuffer.cpp
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright 2005 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 <stdlib.h>
-#include <string.h>
-
-#include <cutils/atomic.h>
-
-#include "SharedBuffer.h"
-
-// ---------------------------------------------------------------------------
-
-namespace android {
-namespace tinyutils {
-
-SharedBuffer* SharedBuffer::alloc(size_t size)
-{
-    SharedBuffer* sb = static_cast<SharedBuffer *>(malloc(sizeof(SharedBuffer) + size));
-    if (sb) {
-        sb->mRefs = 1;
-        sb->mSize = size;
-    }
-    return sb;
-}
-
-
-ssize_t SharedBuffer::dealloc(const SharedBuffer* released)
-{
-    if (released->mRefs != 0) return -1; // XXX: invalid operation
-    free(const_cast<SharedBuffer*>(released));
-    return 0;
-}
-
-SharedBuffer* SharedBuffer::edit() const
-{
-    if (onlyOwner()) {
-        return const_cast<SharedBuffer*>(this);
-    }
-    SharedBuffer* sb = alloc(mSize);
-    if (sb) {
-        memcpy(sb->data(), data(), size());
-        release();
-    }
-    return sb;    
-}
-
-SharedBuffer* SharedBuffer::editResize(size_t newSize) const
-{
-    if (onlyOwner()) {
-        SharedBuffer* buf = const_cast<SharedBuffer*>(this);
-        if (buf->mSize == newSize) return buf;
-        buf = (SharedBuffer*)realloc(buf, sizeof(SharedBuffer) + newSize);
-        if (buf != NULL) {
-            buf->mSize = newSize;
-            return buf;
-        }
-    }
-    SharedBuffer* sb = alloc(newSize);
-    if (sb) {
-        const size_t mySize = mSize;
-        memcpy(sb->data(), data(), newSize < mySize ? newSize : mySize);
-        release();
-    }
-    return sb;    
-}
-
-SharedBuffer* SharedBuffer::attemptEdit() const
-{
-    if (onlyOwner()) {
-        return const_cast<SharedBuffer*>(this);
-    }
-    return 0;
-}
-
-SharedBuffer* SharedBuffer::reset(size_t new_size) const
-{
-    // cheap-o-reset.
-    SharedBuffer* sb = alloc(new_size);
-    if (sb) {
-        release();
-    }
-    return sb;
-}
-
-void SharedBuffer::acquire() const {
-    android_atomic_inc(&mRefs);
-}
-
-int32_t SharedBuffer::release(uint32_t flags) const
-{
-    int32_t prev = 1;
-    if (onlyOwner() || ((prev = android_atomic_dec(&mRefs)) == 1)) {
-        mRefs = 0;
-        if ((flags & eKeepStorage) == 0) {
-            free(const_cast<SharedBuffer*>(this));
-        }
-    }
-    return prev;
-}
-
-} // namespace tinyutils
-} // namespace android
diff --git a/libpixelflinger/codeflinger/tinyutils/SharedBuffer.h b/libpixelflinger/codeflinger/tinyutils/SharedBuffer.h
deleted file mode 100644
index d69b417..0000000
--- a/libpixelflinger/codeflinger/tinyutils/SharedBuffer.h
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright 2005 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 ANDROID_PIXELFLINGER_SHARED_BUFFER_H
-#define ANDROID_PIXELFLINGER_SHARED_BUFFER_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-// ---------------------------------------------------------------------------
-
-namespace android {
-namespace tinyutils {
-
-class SharedBuffer
-{
-public:
-
-    /* flags to use with release() */
-    enum {
-        eKeepStorage = 0x00000001
-    };
-
-    /*! allocate a buffer of size 'size' and acquire() it.
-     *  call release() to free it.
-     */
-    static          SharedBuffer*           alloc(size_t size);
-    
-    /*! free the memory associated with the SharedBuffer.
-     * Fails if there are any users associated with this SharedBuffer.
-     * In other words, the buffer must have been release by all its
-     * users.
-     */
-    static          ssize_t                 dealloc(const SharedBuffer* released);
-    
-    //! get the SharedBuffer from the data pointer
-    static  inline  const SharedBuffer*     sharedBuffer(const void* data);
-
-    //! access the data for read
-    inline          const void*             data() const;
-    
-    //! access the data for read/write
-    inline          void*                   data();
-
-    //! get size of the buffer
-    inline          size_t                  size() const;
- 
-    //! get back a SharedBuffer object from its data
-    static  inline  SharedBuffer*           bufferFromData(void* data);
-    
-    //! get back a SharedBuffer object from its data
-    static  inline  const SharedBuffer*     bufferFromData(const void* data);
-
-    //! get the size of a SharedBuffer object from its data
-    static  inline  size_t                  sizeFromData(const void* data);
-    
-    //! edit the buffer (get a writtable, or non-const, version of it)
-                    SharedBuffer*           edit() const;
-
-    //! edit the buffer, resizing if needed
-                    SharedBuffer*           editResize(size_t size) const;
-
-    //! like edit() but fails if a copy is required
-                    SharedBuffer*           attemptEdit() const;
-    
-    //! resize and edit the buffer, loose it's content.
-                    SharedBuffer*           reset(size_t size) const;
-
-    //! acquire/release a reference on this buffer
-                    void                    acquire() const;
-                    
-    /*! release a reference on this buffer, with the option of not
-     * freeing the memory associated with it if it was the last reference
-     * returns the previous reference count
-     */     
-                    int32_t                 release(uint32_t flags = 0) const;
-    
-    //! returns wether or not we're the only owner
-    inline          bool                    onlyOwner() const;
-    
-
-private:
-        inline SharedBuffer() { }
-        inline ~SharedBuffer() { }
-        inline SharedBuffer(const SharedBuffer&);
- 
-        // 16 bytes. must be sized to preserve correct alingment.
-        mutable int32_t        mRefs;
-                size_t         mSize;
-                uint32_t       mReserved[2];
-};
-
-// ---------------------------------------------------------------------------
-
-const SharedBuffer* SharedBuffer::sharedBuffer(const void* data) {
-    return data ? reinterpret_cast<const SharedBuffer *>(data)-1 : 0;
-}
-
-const void* SharedBuffer::data() const {
-    return this + 1;
-}
-
-void* SharedBuffer::data() {
-    return this + 1;
-}
-
-size_t SharedBuffer::size() const {
-    return mSize;
-}
-
-SharedBuffer* SharedBuffer::bufferFromData(void* data)
-{
-    return ((SharedBuffer*)data)-1;
-}
-    
-const SharedBuffer* SharedBuffer::bufferFromData(const void* data)
-{
-    return ((const SharedBuffer*)data)-1;
-}
-
-size_t SharedBuffer::sizeFromData(const void* data)
-{
-    return (((const SharedBuffer*)data)-1)->mSize;
-}
-
-bool SharedBuffer::onlyOwner() const {
-    return (mRefs == 1);
-}
-
-} // namespace tinyutils
-} // namespace android
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_PIXELFLINGER_SHARED_BUFFER_H
diff --git a/libpixelflinger/codeflinger/tinyutils/SortedVector.h b/libpixelflinger/codeflinger/tinyutils/SortedVector.h
deleted file mode 100644
index a2b7005..0000000
--- a/libpixelflinger/codeflinger/tinyutils/SortedVector.h
+++ /dev/null
@@ -1,284 +0,0 @@
-/*
- * Copyright 2005 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 ANDROID_PIXELFLINGER_SORTED_VECTOR_H
-#define ANDROID_PIXELFLINGER_SORTED_VECTOR_H
-
-#include <assert.h>
-#include <stdint.h>
-#include <sys/types.h>
-
-#include "Vector.h"
-#include "VectorImpl.h"
-#include "TypeHelpers.h"
-
-// ---------------------------------------------------------------------------
-
-namespace android {
-namespace tinyutils {
-
-template <class TYPE>
-class SortedVector : private SortedVectorImpl
-{
-public:
-            typedef TYPE    value_type;
-    
-    /*! 
-     * Constructors and destructors
-     */
-    
-                            SortedVector();
-                            SortedVector(const SortedVector<TYPE>& rhs);
-    virtual                 ~SortedVector();
-
-    /*! copy operator */
-    const SortedVector<TYPE>&   operator = (const SortedVector<TYPE>& rhs) const;    
-    SortedVector<TYPE>&         operator = (const SortedVector<TYPE>& rhs);    
-
-    /*
-     * empty the vector
-     */
-
-    inline  void            clear()             { VectorImpl::clear(); }
-
-    /*! 
-     * vector stats
-     */
-
-    //! returns number of items in the vector
-    inline  size_t          size() const                { return VectorImpl::size(); }
-    //! returns wether or not the vector is empty
-    inline  bool            isEmpty() const             { return VectorImpl::isEmpty(); }
-    //! returns how many items can be stored without reallocating the backing store
-    inline  size_t          capacity() const            { return VectorImpl::capacity(); }
-    //! setst the capacity. capacity can never be reduced less than size()
-    inline  ssize_t         setCapacity(size_t size)    { return VectorImpl::setCapacity(size); }
-
-    /*! 
-     * C-style array access
-     */
-     
-    //! read-only C-style access 
-    inline  const TYPE*     array() const;
-
-    //! read-write C-style access. BE VERY CAREFUL when modifying the array
-    //! you ust keep it sorted! You usually don't use this function.
-            TYPE*           editArray();
-
-            //! finds the index of an item
-            ssize_t         indexOf(const TYPE& item) const;
-            
-            //! finds where this item should be inserted
-            size_t          orderOf(const TYPE& item) const;
-            
-    
-    /*! 
-     * accessors
-     */
-
-    //! read-only access to an item at a given index
-    inline  const TYPE&     operator [] (size_t index) const;
-    //! alternate name for operator []
-    inline  const TYPE&     itemAt(size_t index) const;
-    //! stack-usage of the vector. returns the top of the stack (last element)
-            const TYPE&     top() const;
-    //! same as operator [], but allows to access the vector backward (from the end) with a negative index
-            const TYPE&     mirrorItemAt(ssize_t index) const;
-
-    /*!
-     * modifing the array
-     */
-
-            //! add an item in the right place (and replace the one that is there)
-            ssize_t         add(const TYPE& item);
-            
-            //! editItemAt() MUST NOT change the order of this item
-            TYPE&           editItemAt(size_t index) {
-                return *( static_cast<TYPE *>(VectorImpl::editItemLocation(index)) );
-            }
-
-            //! merges a vector into this one
-            ssize_t         merge(const Vector<TYPE>& vector);
-            ssize_t         merge(const SortedVector<TYPE>& vector);
-            
-            //! removes an item
-            ssize_t         remove(const TYPE&);
-
-    //! remove several items
-    inline  ssize_t         removeItemsAt(size_t index, size_t count = 1);
-    //! remove one item
-    inline  ssize_t         removeAt(size_t index)  { return removeItemsAt(index); }
-            
-protected:
-    virtual void    do_construct(void* storage, size_t num) const;
-    virtual void    do_destroy(void* storage, size_t num) const;
-    virtual void    do_copy(void* dest, const void* from, size_t num) const;
-    virtual void    do_splat(void* dest, const void* item, size_t num) const;
-    virtual void    do_move_forward(void* dest, const void* from, size_t num) const;
-    virtual void    do_move_backward(void* dest, const void* from, size_t num) const;
-    virtual int     do_compare(const void* lhs, const void* rhs) const;
-};
-
-
-// ---------------------------------------------------------------------------
-// No user serviceable parts from here...
-// ---------------------------------------------------------------------------
-
-template<class TYPE> inline
-SortedVector<TYPE>::SortedVector()
-    : SortedVectorImpl(sizeof(TYPE),
-                ((traits<TYPE>::has_trivial_ctor   ? HAS_TRIVIAL_CTOR   : 0)
-                |(traits<TYPE>::has_trivial_dtor   ? HAS_TRIVIAL_DTOR   : 0)
-                |(traits<TYPE>::has_trivial_copy   ? HAS_TRIVIAL_COPY   : 0)
-                |(traits<TYPE>::has_trivial_assign ? HAS_TRIVIAL_ASSIGN : 0))
-                )
-{
-}
-
-template<class TYPE> inline
-SortedVector<TYPE>::SortedVector(const SortedVector<TYPE>& rhs)
-    : SortedVectorImpl(rhs) {
-}
-
-template<class TYPE> inline
-SortedVector<TYPE>::~SortedVector() {
-    finish_vector();
-}
-
-template<class TYPE> inline
-SortedVector<TYPE>& SortedVector<TYPE>::operator = (const SortedVector<TYPE>& rhs) {
-    SortedVectorImpl::operator = (rhs);
-    return *this; 
-}
-
-template<class TYPE> inline
-const SortedVector<TYPE>& SortedVector<TYPE>::operator = (const SortedVector<TYPE>& rhs) const {
-    SortedVectorImpl::operator = (rhs);
-    return *this; 
-}
-
-template<class TYPE> inline
-const TYPE* SortedVector<TYPE>::array() const {
-    return static_cast<const TYPE *>(arrayImpl());
-}
-
-template<class TYPE> inline
-TYPE* SortedVector<TYPE>::editArray() {
-    return static_cast<TYPE *>(editArrayImpl());
-}
-
-
-template<class TYPE> inline
-const TYPE& SortedVector<TYPE>::operator[](size_t index) const {
-    assert( index<size() );
-    return *(array() + index);
-}
-
-template<class TYPE> inline
-const TYPE& SortedVector<TYPE>::itemAt(size_t index) const {
-    return operator[](index);
-}
-
-template<class TYPE> inline
-const TYPE& SortedVector<TYPE>::mirrorItemAt(ssize_t index) const {
-    assert( (index>0 ? index : -index)<size() );
-    return *(array() + ((index<0) ? (size()-index) : index));
-}
-
-template<class TYPE> inline
-const TYPE& SortedVector<TYPE>::top() const {
-    return *(array() + size() - 1);
-}
-
-template<class TYPE> inline
-ssize_t SortedVector<TYPE>::add(const TYPE& item) {
-    return SortedVectorImpl::add(&item);
-}
-
-template<class TYPE> inline
-ssize_t SortedVector<TYPE>::indexOf(const TYPE& item) const {
-    return SortedVectorImpl::indexOf(&item);
-}
-
-template<class TYPE> inline
-size_t SortedVector<TYPE>::orderOf(const TYPE& item) const {
-    return SortedVectorImpl::orderOf(&item);
-}
-
-template<class TYPE> inline
-ssize_t SortedVector<TYPE>::merge(const Vector<TYPE>& vector) {
-    return SortedVectorImpl::merge(reinterpret_cast<const VectorImpl&>(vector));
-}
-
-template<class TYPE> inline
-ssize_t SortedVector<TYPE>::merge(const SortedVector<TYPE>& vector) {
-    return SortedVectorImpl::merge(reinterpret_cast<const SortedVectorImpl&>(vector));
-}
-
-template<class TYPE> inline
-ssize_t SortedVector<TYPE>::remove(const TYPE& item) {
-    return SortedVectorImpl::remove(&item);
-}
-
-template<class TYPE> inline
-ssize_t SortedVector<TYPE>::removeItemsAt(size_t index, size_t count) {
-    return VectorImpl::removeItemsAt(index, count);
-}
-
-// ---------------------------------------------------------------------------
-
-template<class TYPE>
-void SortedVector<TYPE>::do_construct(void* storage, size_t num) const {
-    construct_type( reinterpret_cast<TYPE*>(storage), num );
-}
-
-template<class TYPE>
-void SortedVector<TYPE>::do_destroy(void* storage, size_t num) const {
-    destroy_type( reinterpret_cast<TYPE*>(storage), num );
-}
-
-template<class TYPE>
-void SortedVector<TYPE>::do_copy(void* dest, const void* from, size_t num) const {
-    copy_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
-}
-
-template<class TYPE>
-void SortedVector<TYPE>::do_splat(void* dest, const void* item, size_t num) const {
-    splat_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(item), num );
-}
-
-template<class TYPE>
-void SortedVector<TYPE>::do_move_forward(void* dest, const void* from, size_t num) const {
-    move_forward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
-}
-
-template<class TYPE>
-void SortedVector<TYPE>::do_move_backward(void* dest, const void* from, size_t num) const {
-    move_backward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
-}
-
-template<class TYPE>
-int SortedVector<TYPE>::do_compare(const void* lhs, const void* rhs) const {
-    return compare_type( *reinterpret_cast<const TYPE*>(lhs), *reinterpret_cast<const TYPE*>(rhs) );
-}
-
-} // namespace tinyutils
-} // namespace android
-
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_PIXELFLINGER_SORTED_VECTOR_H
diff --git a/libpixelflinger/codeflinger/tinyutils/TypeHelpers.h b/libpixelflinger/codeflinger/tinyutils/TypeHelpers.h
deleted file mode 100644
index 7abff07..0000000
--- a/libpixelflinger/codeflinger/tinyutils/TypeHelpers.h
+++ /dev/null
@@ -1,256 +0,0 @@
-/*
- * Copyright 2005 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 ANDROID_PIXELFLINGER_TYPE_HELPERS_H
-#define ANDROID_PIXELFLINGER_TYPE_HELPERS_H
-
-#include <new>
-#include <stdint.h>
-#include <string.h>
-#include <sys/types.h>
-
-// ---------------------------------------------------------------------------
-
-namespace android {
-namespace tinyutils {
-
-/*
- * Types traits
- */
-    
-template <typename T> struct trait_trivial_ctor  { enum { value = false }; };
-template <typename T> struct trait_trivial_dtor  { enum { value = false }; };
-template <typename T> struct trait_trivial_copy  { enum { value = false }; };
-template <typename T> struct trait_trivial_assign{ enum { value = false }; };
-
-template <typename T> struct trait_pointer     { enum { value = false }; };    
-template <typename T> struct trait_pointer<T*> { enum { value = true }; };
-
-#define ANDROID_BASIC_TYPES_TRAITS( T )                                       \
-    template<> struct trait_trivial_ctor< T >  { enum { value = true }; };    \
-    template<> struct trait_trivial_dtor< T >  { enum { value = true }; };    \
-    template<> struct trait_trivial_copy< T >  { enum { value = true }; };    \
-    template<> struct trait_trivial_assign< T >{ enum { value = true }; }; 
-
-#define ANDROID_TYPE_TRAITS( T, ctor, dtor, copy, assign )                    \
-    template<> struct trait_trivial_ctor< T >  { enum { value = ctor }; };    \
-    template<> struct trait_trivial_dtor< T >  { enum { value = dtor }; };    \
-    template<> struct trait_trivial_copy< T >  { enum { value = copy }; };    \
-    template<> struct trait_trivial_assign< T >{ enum { value = assign }; }; 
-
-template <typename TYPE>
-struct traits {
-    enum {
-        is_pointer          = trait_pointer<TYPE>::value,
-        has_trivial_ctor    = is_pointer || trait_trivial_ctor<TYPE>::value,
-        has_trivial_dtor    = is_pointer || trait_trivial_dtor<TYPE>::value,
-        has_trivial_copy    = is_pointer || trait_trivial_copy<TYPE>::value,
-        has_trivial_assign  = is_pointer || trait_trivial_assign<TYPE>::value   
-    };
-};
-
-template <typename T, typename U>
-struct aggregate_traits {
-    enum {
-        is_pointer          = false,
-        has_trivial_ctor    = traits<T>::has_trivial_ctor && traits<U>::has_trivial_ctor,
-        has_trivial_dtor    = traits<T>::has_trivial_dtor && traits<U>::has_trivial_dtor,
-        has_trivial_copy    = traits<T>::has_trivial_copy && traits<U>::has_trivial_copy,
-        has_trivial_assign  = traits<T>::has_trivial_assign && traits<U>::has_trivial_assign
-    };
-};
-
-// ---------------------------------------------------------------------------
-
-/*
- * basic types traits
- */
- 
-ANDROID_BASIC_TYPES_TRAITS( void );
-ANDROID_BASIC_TYPES_TRAITS( bool );
-ANDROID_BASIC_TYPES_TRAITS( char );
-ANDROID_BASIC_TYPES_TRAITS( unsigned char );
-ANDROID_BASIC_TYPES_TRAITS( short );
-ANDROID_BASIC_TYPES_TRAITS( unsigned short );
-ANDROID_BASIC_TYPES_TRAITS( int );
-ANDROID_BASIC_TYPES_TRAITS( unsigned int );
-ANDROID_BASIC_TYPES_TRAITS( long );
-ANDROID_BASIC_TYPES_TRAITS( unsigned long );
-ANDROID_BASIC_TYPES_TRAITS( long long );
-ANDROID_BASIC_TYPES_TRAITS( unsigned long long );
-ANDROID_BASIC_TYPES_TRAITS( float );
-ANDROID_BASIC_TYPES_TRAITS( double );
-
-// ---------------------------------------------------------------------------
-
-    
-/*
- * compare and order types
- */
-
-template<typename TYPE> inline
-int strictly_order_type(const TYPE& lhs, const TYPE& rhs) {
-    return (lhs < rhs) ? 1 : 0;
-}
-
-template<typename TYPE> inline
-int compare_type(const TYPE& lhs, const TYPE& rhs) {
-    return strictly_order_type(rhs, lhs) - strictly_order_type(lhs, rhs);
-}
-
-/*
- * create, destroy, copy and assign types...
- */
- 
-template<typename TYPE> inline
-void construct_type(TYPE* p, size_t n) {
-    if (!traits<TYPE>::has_trivial_ctor) {
-        while (n--) {
-            new(p++) TYPE;
-        }
-    }
-}
-
-template<typename TYPE> inline
-void destroy_type(TYPE* p, size_t n) {
-    if (!traits<TYPE>::has_trivial_dtor) {
-        while (n--) {
-            p->~TYPE();
-            p++;
-        }
-    }
-}
-
-template<typename TYPE> inline
-void copy_type(TYPE* d, const TYPE* s, size_t n) {
-    if (!traits<TYPE>::has_trivial_copy) {
-        while (n--) {
-            new(d) TYPE(*s);
-            d++, s++;
-        }
-    } else {
-        memcpy(d,s,n*sizeof(TYPE));
-    }
-}
-
-template<typename TYPE> inline
-void assign_type(TYPE* d, const TYPE* s, size_t n) {
-    if (!traits<TYPE>::has_trivial_assign) {
-        while (n--) {
-            *d++ = *s++;
-        }
-    } else {
-        memcpy(d,s,n*sizeof(TYPE));
-    }
-}
-
-template<typename TYPE> inline
-void splat_type(TYPE* where, const TYPE* what, size_t n) {
-    if (!traits<TYPE>::has_trivial_copy) {
-        while (n--) {
-            new(where) TYPE(*what);
-            where++;
-        }
-    } else {
-         while (n--) {
-             *where++ = *what;
-        }
-    }
-}
-
-template<typename TYPE> inline
-void move_forward_type(TYPE* d, const TYPE* s, size_t n = 1) {
-    if (!traits<TYPE>::has_trivial_copy || !traits<TYPE>::has_trivial_dtor) {
-        d += n;
-        s += n;
-        while (n--) {
-            --d, --s;
-            if (!traits<TYPE>::has_trivial_copy) {
-                new(d) TYPE(*s);
-            } else {
-                *d = *s;
-            }
-            if (!traits<TYPE>::has_trivial_dtor) {
-                s->~TYPE();
-            }
-        }
-    } else {
-        memmove(d,s,n*sizeof(TYPE));
-    }
-}
-
-template<typename TYPE> inline
-void move_backward_type(TYPE* d, const TYPE* s, size_t n = 1) {
-    if (!traits<TYPE>::has_trivial_copy || !traits<TYPE>::has_trivial_dtor) {
-        while (n--) {
-            if (!traits<TYPE>::has_trivial_copy) {
-                new(d) TYPE(*s);
-            } else {
-                *d = *s;
-            }
-            if (!traits<TYPE>::has_trivial_dtor) {
-                s->~TYPE();
-            }
-            d++, s++;
-        }
-    } else {
-        memmove(d,s,n*sizeof(TYPE));
-    }
-}
-// ---------------------------------------------------------------------------
-
-/*
- * a key/value pair
- */
-
-template <typename KEY, typename VALUE>
-struct key_value_pair_t {
-    KEY     key;
-    VALUE   value;
-    key_value_pair_t() { }
-    key_value_pair_t(const key_value_pair_t& o) : key(o.key), value(o.value) { }
-    key_value_pair_t(const KEY& k, const VALUE& v) : key(k), value(v)  { }
-    key_value_pair_t(const KEY& k) : key(k) { }
-    inline bool operator < (const key_value_pair_t& o) const {
-        return strictly_order_type(key, o.key);
-    }
-};
-
-template<>
-template <typename K, typename V>
-struct trait_trivial_ctor< key_value_pair_t<K, V> >
-{ enum { value = aggregate_traits<K,V>::has_trivial_ctor }; };
-template<> 
-template <typename K, typename V>
-struct trait_trivial_dtor< key_value_pair_t<K, V> >
-{ enum { value = aggregate_traits<K,V>::has_trivial_dtor }; };
-template<> 
-template <typename K, typename V>
-struct trait_trivial_copy< key_value_pair_t<K, V> >
-{ enum { value = aggregate_traits<K,V>::has_trivial_copy }; };
-template<> 
-template <typename K, typename V>
-struct trait_trivial_assign< key_value_pair_t<K, V> >
-{ enum { value = aggregate_traits<K,V>::has_trivial_assign};};
-
-// ---------------------------------------------------------------------------
-
-} // namespace tinyutils
-} // namespace android
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_PIXELFLINGER_TYPE_HELPERS_H
diff --git a/libpixelflinger/codeflinger/tinyutils/Vector.h b/libpixelflinger/codeflinger/tinyutils/Vector.h
deleted file mode 100644
index c07a17a..0000000
--- a/libpixelflinger/codeflinger/tinyutils/Vector.h
+++ /dev/null
@@ -1,353 +0,0 @@
-/*
- * Copyright 2005 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 ANDROID_PIXELFLINGER_VECTOR_H
-#define ANDROID_PIXELFLINGER_VECTOR_H
-
-#include <new>
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <cutils/log.h>
-
-#include "Errors.h"
-#include "VectorImpl.h"
-#include "TypeHelpers.h"
-
-// ---------------------------------------------------------------------------
-
-namespace android {
-namespace tinyutils {
-
-/*!
- * The main templated vector class ensuring type safety
- * while making use of VectorImpl.
- * This is the class users want to use.
- */
-
-template <class TYPE>
-class Vector : private VectorImpl
-{
-public:
-            typedef TYPE    value_type;
-    
-    /*! 
-     * Constructors and destructors
-     */
-    
-                            Vector();
-                            Vector(const Vector<TYPE>& rhs);
-    virtual                 ~Vector();
-
-    /*! copy operator */
-            const Vector<TYPE>&     operator = (const Vector<TYPE>& rhs) const;
-            Vector<TYPE>&           operator = (const Vector<TYPE>& rhs);    
-
-    /*
-     * empty the vector
-     */
-
-    inline  void            clear()             { VectorImpl::clear(); }
-
-    /*! 
-     * vector stats
-     */
-
-    //! returns number of items in the vector
-    inline  size_t          size() const                { return VectorImpl::size(); }
-    //! returns wether or not the vector is empty
-    inline  bool            isEmpty() const             { return VectorImpl::isEmpty(); }
-    //! returns how many items can be stored without reallocating the backing store
-    inline  size_t          capacity() const            { return VectorImpl::capacity(); }
-    //! setst the capacity. capacity can never be reduced less than size()
-    inline  ssize_t         setCapacity(size_t size)    { return VectorImpl::setCapacity(size); }
-
-    /*! 
-     * C-style array access
-     */
-     
-    //! read-only C-style access 
-    inline  const TYPE*     array() const;
-    //! read-write C-style access
-            TYPE*           editArray();
-    
-    /*! 
-     * accessors
-     */
-
-    //! read-only access to an item at a given index
-    inline  const TYPE&     operator [] (size_t index) const;
-    //! alternate name for operator []
-    inline  const TYPE&     itemAt(size_t index) const;
-    //! stack-usage of the vector. returns the top of the stack (last element)
-            const TYPE&     top() const;
-    //! same as operator [], but allows to access the vector backward (from the end) with a negative index
-            const TYPE&     mirrorItemAt(ssize_t index) const;
-
-    /*!
-     * modifing the array
-     */
-
-    //! copy-on write support, grants write access to an item
-            TYPE&           editItemAt(size_t index);
-    //! grants right acces to the top of the stack (last element)
-            TYPE&           editTop();
-
-            /*! 
-             * append/insert another vector
-             */
-            
-    //! insert another vector at a given index
-            ssize_t         insertVectorAt(const Vector<TYPE>& vector, size_t index);
-
-    //! append another vector at the end of this one
-            ssize_t         appendVector(const Vector<TYPE>& vector);
-
-
-            /*! 
-             * add/insert/replace items
-             */
-             
-    //! insert one or several items initialized with their default constructor
-    inline  ssize_t         insertAt(size_t index, size_t numItems = 1);
-    //! insert on onr several items initialized from a prototype item
-            ssize_t         insertAt(const TYPE& prototype_item, size_t index, size_t numItems = 1);
-    //! pop the top of the stack (removes the last element). No-op if the stack's empty
-    inline  void            pop();
-    //! pushes an item initialized with its default constructor
-    inline  void            push();
-    //! pushes an item on the top of the stack
-            void            push(const TYPE& item);
-    //! same as push() but returns the index the item was added at (or an error)
-    inline  ssize_t         add();
-    //! same as push() but returns the index the item was added at (or an error)
-            ssize_t         add(const TYPE& item);            
-    //! replace an item with a new one initialized with its default constructor
-    inline  ssize_t         replaceAt(size_t index);
-    //! replace an item with a new one
-            ssize_t         replaceAt(const TYPE& item, size_t index);
-
-    /*!
-     * remove items
-     */
-
-    //! remove several items
-    inline  ssize_t         removeItemsAt(size_t index, size_t count = 1);
-    //! remove one item
-    inline  ssize_t         removeAt(size_t index)  { return removeItemsAt(index); }
-
-    /*!
-     * sort (stable) the array
-     */
-     
-     typedef int (*compar_t)(const TYPE* lhs, const TYPE* rhs);
-     typedef int (*compar_r_t)(const TYPE* lhs, const TYPE* rhs, void* state);
-     
-     inline status_t        sort(compar_t cmp);
-     inline status_t        sort(compar_r_t cmp, void* state);
-
-protected:
-    virtual void    do_construct(void* storage, size_t num) const;
-    virtual void    do_destroy(void* storage, size_t num) const;
-    virtual void    do_copy(void* dest, const void* from, size_t num) const;
-    virtual void    do_splat(void* dest, const void* item, size_t num) const;
-    virtual void    do_move_forward(void* dest, const void* from, size_t num) const;
-    virtual void    do_move_backward(void* dest, const void* from, size_t num) const;
-};
-
-
-// ---------------------------------------------------------------------------
-// No user serviceable parts from here...
-// ---------------------------------------------------------------------------
-
-template<class TYPE> inline
-Vector<TYPE>::Vector()
-    : VectorImpl(sizeof(TYPE),
-                ((traits<TYPE>::has_trivial_ctor   ? HAS_TRIVIAL_CTOR   : 0)
-                |(traits<TYPE>::has_trivial_dtor   ? HAS_TRIVIAL_DTOR   : 0)
-                |(traits<TYPE>::has_trivial_copy   ? HAS_TRIVIAL_COPY   : 0)
-                |(traits<TYPE>::has_trivial_assign ? HAS_TRIVIAL_ASSIGN : 0))
-                )
-{
-}
-
-template<class TYPE> inline
-Vector<TYPE>::Vector(const Vector<TYPE>& rhs)
-    : VectorImpl(rhs) {
-}
-
-template<class TYPE> inline
-Vector<TYPE>::~Vector() {
-    finish_vector();
-}
-
-template<class TYPE> inline
-Vector<TYPE>& Vector<TYPE>::operator = (const Vector<TYPE>& rhs) {
-    VectorImpl::operator = (rhs);
-    return *this; 
-}
-
-template<class TYPE> inline
-const Vector<TYPE>& Vector<TYPE>::operator = (const Vector<TYPE>& rhs) const {
-    VectorImpl::operator = (rhs);
-    return *this; 
-}
-
-template<class TYPE> inline
-const TYPE* Vector<TYPE>::array() const {
-    return static_cast<const TYPE *>(arrayImpl());
-}
-
-template<class TYPE> inline
-TYPE* Vector<TYPE>::editArray() {
-    return static_cast<TYPE *>(editArrayImpl());
-}
-
-
-template<class TYPE> inline
-const TYPE& Vector<TYPE>::operator[](size_t index) const {
-    LOG_FATAL_IF( index>=size(),
-                  "itemAt: index %d is past size %d", (int)index, (int)size() );
-    return *(array() + index);
-}
-
-template<class TYPE> inline
-const TYPE& Vector<TYPE>::itemAt(size_t index) const {
-    return operator[](index);
-}
-
-template<class TYPE> inline
-const TYPE& Vector<TYPE>::mirrorItemAt(ssize_t index) const {
-    LOG_FATAL_IF( (index>0 ? index : -index)>=size(),
-                  "mirrorItemAt: index %d is past size %d",
-                  (int)index, (int)size() );
-    return *(array() + ((index<0) ? (size()-index) : index));
-}
-
-template<class TYPE> inline
-const TYPE& Vector<TYPE>::top() const {
-    return *(array() + size() - 1);
-}
-
-template<class TYPE> inline
-TYPE& Vector<TYPE>::editItemAt(size_t index) {
-    return *( static_cast<TYPE *>(editItemLocation(index)) );
-}
-
-template<class TYPE> inline
-TYPE& Vector<TYPE>::editTop() {
-    return *( static_cast<TYPE *>(editItemLocation(size()-1)) );
-}
-
-template<class TYPE> inline
-ssize_t Vector<TYPE>::insertVectorAt(const Vector<TYPE>& vector, size_t index) {
-    return VectorImpl::insertVectorAt(reinterpret_cast<const VectorImpl&>(vector), index);
-}
-
-template<class TYPE> inline
-ssize_t Vector<TYPE>::appendVector(const Vector<TYPE>& vector) {
-    return VectorImpl::appendVector(reinterpret_cast<const VectorImpl&>(vector));
-}
-
-template<class TYPE> inline
-ssize_t Vector<TYPE>::insertAt(const TYPE& item, size_t index, size_t numItems) {
-    return VectorImpl::insertAt(&item, index, numItems);
-}
-
-template<class TYPE> inline
-void Vector<TYPE>::push(const TYPE& item) {
-    return VectorImpl::push(&item);
-}
-
-template<class TYPE> inline
-ssize_t Vector<TYPE>::add(const TYPE& item) {
-    return VectorImpl::add(&item);
-}
-
-template<class TYPE> inline
-ssize_t Vector<TYPE>::replaceAt(const TYPE& item, size_t index) {
-    return VectorImpl::replaceAt(&item, index);
-}
-
-template<class TYPE> inline
-ssize_t Vector<TYPE>::insertAt(size_t index, size_t numItems) {
-    return VectorImpl::insertAt(index, numItems);
-}
-
-template<class TYPE> inline
-void Vector<TYPE>::pop() {
-    VectorImpl::pop();
-}
-
-template<class TYPE> inline
-void Vector<TYPE>::push() {
-    VectorImpl::push();
-}
-
-template<class TYPE> inline
-ssize_t Vector<TYPE>::add() {
-    return VectorImpl::add();
-}
-
-template<class TYPE> inline
-ssize_t Vector<TYPE>::replaceAt(size_t index) {
-    return VectorImpl::replaceAt(index);
-}
-
-template<class TYPE> inline
-ssize_t Vector<TYPE>::removeItemsAt(size_t index, size_t count) {
-    return VectorImpl::removeItemsAt(index, count);
-}
-
-// ---------------------------------------------------------------------------
-
-template<class TYPE>
-void Vector<TYPE>::do_construct(void* storage, size_t num) const {
-    construct_type( reinterpret_cast<TYPE*>(storage), num );
-}
-
-template<class TYPE>
-void Vector<TYPE>::do_destroy(void* storage, size_t num) const {
-    destroy_type( reinterpret_cast<TYPE*>(storage), num );
-}
-
-template<class TYPE>
-void Vector<TYPE>::do_copy(void* dest, const void* from, size_t num) const {
-    copy_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
-}
-
-template<class TYPE>
-void Vector<TYPE>::do_splat(void* dest, const void* item, size_t num) const {
-    splat_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(item), num );
-}
-
-template<class TYPE>
-void Vector<TYPE>::do_move_forward(void* dest, const void* from, size_t num) const {
-    move_forward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
-}
-
-template<class TYPE>
-void Vector<TYPE>::do_move_backward(void* dest, const void* from, size_t num) const {
-    move_backward_type( reinterpret_cast<TYPE*>(dest), reinterpret_cast<const TYPE*>(from), num );
-}
-
-} // namespace tinyutils
-} // namespace android
-
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_PIXELFLINGER_VECTOR_H
diff --git a/libpixelflinger/codeflinger/tinyutils/VectorImpl.cpp b/libpixelflinger/codeflinger/tinyutils/VectorImpl.cpp
deleted file mode 100644
index 689129a..0000000
--- a/libpixelflinger/codeflinger/tinyutils/VectorImpl.cpp
+++ /dev/null
@@ -1,555 +0,0 @@
-/*
- * Copyright 2005 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.
- */
-
-#define LOG_TAG "Vector"
-
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <errno.h>
-
-#include <cutils/log.h>
-
-#include "Errors.h"
-#include "SharedBuffer.h"
-#include "VectorImpl.h"
-
-/*****************************************************************************/
-
-
-namespace android {
-namespace tinyutils {
-
-// ----------------------------------------------------------------------------
-
-const size_t kMinVectorCapacity = 4;
-
-static inline size_t max(size_t a, size_t b) {
-    return a>b ? a : b;
-}
-
-// ----------------------------------------------------------------------------
-
-VectorImpl::VectorImpl(size_t itemSize, uint32_t flags)
-    : mStorage(0), mCount(0), mFlags(flags), mItemSize(itemSize)
-{
-}
-
-VectorImpl::VectorImpl(const VectorImpl& rhs)
-    :   mStorage(rhs.mStorage), mCount(rhs.mCount),
-        mFlags(rhs.mFlags), mItemSize(rhs.mItemSize)
-{
-    if (mStorage) {
-        SharedBuffer::sharedBuffer(mStorage)->acquire();
-    }
-}
-
-VectorImpl::~VectorImpl()
-{
-    ALOG_ASSERT(!mCount,
-        "[%p] "
-        "subclasses of VectorImpl must call finish_vector()"
-        " in their destructor. Leaking %d bytes.",
-        this, (int)(mCount*mItemSize));
-    // We can't call _do_destroy() here because the vtable is already gone. 
-}
-
-VectorImpl& VectorImpl::operator = (const VectorImpl& rhs)
-{
-    ALOG_ASSERT(mItemSize == rhs.mItemSize,
-        "Vector<> have different types (this=%p, rhs=%p)", this, &rhs);
-    if (this != &rhs) {
-        release_storage();
-        if (rhs.mCount) {
-            mStorage = rhs.mStorage;
-            mCount = rhs.mCount;
-            SharedBuffer::sharedBuffer(mStorage)->acquire();
-        } else {
-            mStorage = 0;
-            mCount = 0;
-        }
-    }
-    return *this;
-}
-
-void* VectorImpl::editArrayImpl()
-{
-    if (mStorage) {
-        SharedBuffer* sb = SharedBuffer::sharedBuffer(mStorage)->attemptEdit();
-        if (sb == 0) {
-            sb = SharedBuffer::alloc(capacity() * mItemSize);
-            if (sb) {
-                _do_copy(sb->data(), mStorage, mCount);
-                release_storage();
-                mStorage = sb->data();
-            }
-        }
-    }
-    return mStorage;
-}
-
-size_t VectorImpl::capacity() const
-{
-    if (mStorage) {
-        return SharedBuffer::sharedBuffer(mStorage)->size() / mItemSize;
-    }
-    return 0;
-}
-
-ssize_t VectorImpl::insertVectorAt(const VectorImpl& vector, size_t index)
-{
-    if (index > size())
-        return BAD_INDEX;
-    void* where = _grow(index, vector.size());
-    if (where) {
-        _do_copy(where, vector.arrayImpl(), vector.size());
-    }
-    return where ? index : (ssize_t)NO_MEMORY;
-}
-
-ssize_t VectorImpl::appendVector(const VectorImpl& vector)
-{
-    return insertVectorAt(vector, size());
-}
-
-ssize_t VectorImpl::insertAt(size_t index, size_t numItems)
-{
-    return insertAt(0, index, numItems);
-}
-
-ssize_t VectorImpl::insertAt(const void* item, size_t index, size_t numItems)
-{
-    if (index > size())
-        return BAD_INDEX;
-    void* where = _grow(index, numItems);
-    if (where) {
-        if (item) {
-            _do_splat(where, item, numItems);
-        } else {
-            _do_construct(where, numItems);
-        }
-    }
-    return where ? index : (ssize_t)NO_MEMORY;
-}
-
-void VectorImpl::pop()
-{
-    if (size())
-        removeItemsAt(size()-1, 1);
-}
-
-void VectorImpl::push()
-{
-    push(0);
-}
-
-void VectorImpl::push(const void* item)
-{
-    insertAt(item, size());
-}
-
-ssize_t VectorImpl::add()
-{
-    return add(0);
-}
-
-ssize_t VectorImpl::add(const void* item)
-{
-    return insertAt(item, size());
-}
-
-ssize_t VectorImpl::replaceAt(size_t index)
-{
-    return replaceAt(0, index);
-}
-
-ssize_t VectorImpl::replaceAt(const void* prototype, size_t index)
-{
-    ALOG_ASSERT(index<size(),
-        "[%p] replace: index=%d, size=%d", this, (int)index, (int)size());
-
-    void* item = editItemLocation(index);
-    if (item == 0)
-        return NO_MEMORY;
-    _do_destroy(item, 1);
-    if (prototype == 0) {
-        _do_construct(item, 1);
-    } else {
-        _do_copy(item, prototype, 1);
-    }
-    return ssize_t(index);
-}
-
-ssize_t VectorImpl::removeItemsAt(size_t index, size_t count)
-{
-    ALOG_ASSERT((index+count)<=size(),
-        "[%p] remove: index=%d, count=%d, size=%d",
-               this, (int)index, (int)count, (int)size());
-
-    if ((index+count) > size())
-        return BAD_VALUE;
-   _shrink(index, count);
-   return index;
-}
-
-void VectorImpl::finish_vector()
-{
-    release_storage();
-    mStorage = 0;
-    mCount = 0;
-}
-
-void VectorImpl::clear()
-{
-    _shrink(0, mCount);
-}
-
-void* VectorImpl::editItemLocation(size_t index)
-{
-    ALOG_ASSERT(index<capacity(),
-        "[%p] itemLocation: index=%d, capacity=%d, count=%d",
-        this, (int)index, (int)capacity(), (int)mCount);
-            
-    void* buffer = editArrayImpl();
-    if (buffer)
-        return reinterpret_cast<char*>(buffer) + index*mItemSize;
-    return 0;
-}
-
-const void* VectorImpl::itemLocation(size_t index) const
-{
-    ALOG_ASSERT(index<capacity(),
-        "[%p] editItemLocation: index=%d, capacity=%d, count=%d",
-        this, (int)index, (int)capacity(), (int)mCount);
-
-    const  void* buffer = arrayImpl();
-    if (buffer)
-        return reinterpret_cast<const char*>(buffer) + index*mItemSize;
-    return 0;
-}
-
-ssize_t VectorImpl::setCapacity(size_t new_capacity)
-{
-    size_t current_capacity = capacity();
-    ssize_t amount = new_capacity - size();
-    if (amount <= 0) {
-        // we can't reduce the capacity
-        return current_capacity;
-    } 
-    SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize);
-    if (sb) {
-        void* array = sb->data();
-        _do_copy(array, mStorage, size());
-        release_storage();
-        mStorage = const_cast<void*>(array);
-    } else {
-        return NO_MEMORY;
-    }
-    return new_capacity;
-}
-
-void VectorImpl::release_storage()
-{
-    if (mStorage) {
-        const SharedBuffer* sb = SharedBuffer::sharedBuffer(mStorage);
-        if (sb->release(SharedBuffer::eKeepStorage) == 1) {
-            _do_destroy(mStorage, mCount);
-            SharedBuffer::dealloc(sb);
-        } 
-    }
-}
-
-void* VectorImpl::_grow(size_t where, size_t amount)
-{
-//    ALOGV("_grow(this=%p, where=%d, amount=%d) count=%d, capacity=%d",
-//        this, (int)where, (int)amount, (int)mCount, (int)capacity());
-
-    if (where > mCount)
-        where = mCount;
-      
-    const size_t new_size = mCount + amount;
-    if (capacity() < new_size) {
-        const size_t new_capacity = max(kMinVectorCapacity, ((new_size*3)+1)/2);
-//        ALOGV("grow vector %p, new_capacity=%d", this, (int)new_capacity);
-        if ((mStorage) &&
-            (mCount==where) &&
-            (mFlags & HAS_TRIVIAL_COPY) &&
-            (mFlags & HAS_TRIVIAL_DTOR))
-        {
-            const SharedBuffer* cur_sb = SharedBuffer::sharedBuffer(mStorage);
-            SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize);
-            mStorage = sb->data();
-        } else {
-            SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize);
-            if (sb) {
-                void* array = sb->data();
-                if (where>0) {
-                    _do_copy(array, mStorage, where);
-                }
-                if (mCount>where) {
-                    const void* from = reinterpret_cast<const uint8_t *>(mStorage) + where*mItemSize;
-                    void* dest = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize;
-                    _do_copy(dest, from, mCount-where);
-                }
-                release_storage();
-                mStorage = const_cast<void*>(array);
-            }
-        }
-    } else {
-        ssize_t s = mCount-where;
-        if (s>0) {
-            void* array = editArrayImpl();    
-            void* to = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize;
-            const void* from = reinterpret_cast<const uint8_t *>(array) + where*mItemSize;
-            _do_move_forward(to, from, s);
-        }
-    }
-    mCount += amount;
-    void* free_space = const_cast<void*>(itemLocation(where));
-    return free_space;
-}
-
-void VectorImpl::_shrink(size_t where, size_t amount)
-{
-    if (!mStorage)
-        return;
-
-//    ALOGV("_shrink(this=%p, where=%d, amount=%d) count=%d, capacity=%d",
-//        this, (int)where, (int)amount, (int)mCount, (int)capacity());
-
-    if (where >= mCount)
-        where = mCount - amount;
-
-    const size_t new_size = mCount - amount;
-    if (new_size*3 < capacity()) {
-        const size_t new_capacity = max(kMinVectorCapacity, new_size*2);
-//        ALOGV("shrink vector %p, new_capacity=%d", this, (int)new_capacity);
-        if ((where == mCount-amount) &&
-            (mFlags & HAS_TRIVIAL_COPY) &&
-            (mFlags & HAS_TRIVIAL_DTOR))
-        {
-            const SharedBuffer* cur_sb = SharedBuffer::sharedBuffer(mStorage);
-            SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize);
-            mStorage = sb->data();
-        } else {
-            SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize);
-            if (sb) {
-                void* array = sb->data();
-                if (where>0) {
-                    _do_copy(array, mStorage, where);
-                }
-                if (mCount > where+amount) {
-                    const void* from = reinterpret_cast<const uint8_t *>(mStorage) + (where+amount)*mItemSize;
-                    void* dest = reinterpret_cast<uint8_t *>(array) + where*mItemSize;
-                    _do_copy(dest, from, mCount-(where+amount));
-                }
-                release_storage();
-                mStorage = const_cast<void*>(array);
-            }
-        }
-    } else {
-        void* array = editArrayImpl();    
-        void* to = reinterpret_cast<uint8_t *>(array) + where*mItemSize;
-        _do_destroy(to, amount);
-        ssize_t s = mCount-(where+amount);
-        if (s>0) {
-            const void* from = reinterpret_cast<uint8_t *>(array) + (where+amount)*mItemSize;
-            _do_move_backward(to, from, s);
-        }
-    }
-
-    // adjust the number of items...
-    mCount -= amount;
-}
-
-size_t VectorImpl::itemSize() const {
-    return mItemSize;
-}
-
-void VectorImpl::_do_construct(void* storage, size_t num) const
-{
-    if (!(mFlags & HAS_TRIVIAL_CTOR)) {
-        do_construct(storage, num);
-    }
-}
-
-void VectorImpl::_do_destroy(void* storage, size_t num) const
-{
-    if (!(mFlags & HAS_TRIVIAL_DTOR)) {
-        do_destroy(storage, num);
-    }
-}
-
-void VectorImpl::_do_copy(void* dest, const void* from, size_t num) const
-{
-    if (!(mFlags & HAS_TRIVIAL_COPY)) {
-        do_copy(dest, from, num);
-    } else {
-        memcpy(dest, from, num*itemSize());
-    }
-}
-
-void VectorImpl::_do_splat(void* dest, const void* item, size_t num) const {
-    do_splat(dest, item, num);
-}
-
-void VectorImpl::_do_move_forward(void* dest, const void* from, size_t num) const {
-    do_move_forward(dest, from, num);
-}
-
-void VectorImpl::_do_move_backward(void* dest, const void* from, size_t num) const {
-    do_move_backward(dest, from, num);
-}
-
-void VectorImpl::reservedVectorImpl1() { }
-void VectorImpl::reservedVectorImpl2() { }
-void VectorImpl::reservedVectorImpl3() { }
-void VectorImpl::reservedVectorImpl4() { }
-void VectorImpl::reservedVectorImpl5() { }
-void VectorImpl::reservedVectorImpl6() { }
-void VectorImpl::reservedVectorImpl7() { }
-void VectorImpl::reservedVectorImpl8() { }
-
-/*****************************************************************************/
-
-SortedVectorImpl::SortedVectorImpl(size_t itemSize, uint32_t flags)
-    : VectorImpl(itemSize, flags)
-{
-}
-
-SortedVectorImpl::SortedVectorImpl(const VectorImpl& rhs)
-: VectorImpl(rhs)
-{
-}
-
-SortedVectorImpl::~SortedVectorImpl()
-{
-}
-
-SortedVectorImpl& SortedVectorImpl::operator = (const SortedVectorImpl& rhs)
-{
-    return static_cast<SortedVectorImpl&>( VectorImpl::operator = (static_cast<const VectorImpl&>(rhs)) );
-}
-
-ssize_t SortedVectorImpl::indexOf(const void* item) const
-{
-    return _indexOrderOf(item);
-}
-
-size_t SortedVectorImpl::orderOf(const void* item) const
-{
-    size_t o;
-    _indexOrderOf(item, &o);
-    return o;
-}
-
-ssize_t SortedVectorImpl::_indexOrderOf(const void* item, size_t* order) const
-{
-    // binary search
-    ssize_t err = NAME_NOT_FOUND;
-    ssize_t l = 0;
-    ssize_t h = size()-1;
-    ssize_t mid;
-    const void* a = arrayImpl();
-    const size_t s = itemSize();
-    while (l <= h) {
-        mid = l + (h - l)/2;
-        const void* const curr = reinterpret_cast<const char *>(a) + (mid*s);
-        const int c = do_compare(curr, item);
-        if (c == 0) {
-            err = l = mid;
-            break;
-        } else if (c < 0) {
-            l = mid + 1;
-        } else {
-            h = mid - 1;
-        }
-    }
-    if (order) *order = l;
-    return err;
-}
-
-ssize_t SortedVectorImpl::add(const void* item)
-{
-    size_t order;
-    ssize_t index = _indexOrderOf(item, &order);
-    if (index < 0) {
-        index = VectorImpl::insertAt(item, order, 1);
-    } else {
-        index = VectorImpl::replaceAt(item, index);
-    }
-    return index;
-}
-
-ssize_t SortedVectorImpl::merge(const VectorImpl& vector)
-{
-    // naive merge...
-    if (!vector.isEmpty()) {
-        const void* buffer = vector.arrayImpl();
-        const size_t is = itemSize();
-        size_t s = vector.size();
-        for (size_t i=0 ; i<s ; i++) {
-            ssize_t err = add( reinterpret_cast<const char*>(buffer) + i*is );
-            if (err<0) {
-                return err;
-            }
-        }
-    }
-    return NO_ERROR;
-}
-
-ssize_t SortedVectorImpl::merge(const SortedVectorImpl& vector)
-{
-    // we've merging a sorted vector... nice!
-    ssize_t err = NO_ERROR;
-    if (!vector.isEmpty()) {
-        // first take care of the case where the vectors are sorted together
-        if (do_compare(vector.itemLocation(vector.size()-1), arrayImpl()) <= 0) {
-            err = VectorImpl::insertVectorAt(static_cast<const VectorImpl&>(vector), 0);
-        } else if (do_compare(vector.arrayImpl(), itemLocation(size()-1)) >= 0) {
-            err = VectorImpl::appendVector(static_cast<const VectorImpl&>(vector));
-        } else {
-            // this could be made a little better
-            err = merge(static_cast<const VectorImpl&>(vector));
-        }
-    }
-    return err;
-}
-
-ssize_t SortedVectorImpl::remove(const void* item)
-{
-    ssize_t i = indexOf(item);
-    if (i>=0) {
-        VectorImpl::removeItemsAt(i, 1);
-    }
-    return i;
-}
-
-void SortedVectorImpl::reservedSortedVectorImpl1() { };
-void SortedVectorImpl::reservedSortedVectorImpl2() { };
-void SortedVectorImpl::reservedSortedVectorImpl3() { };
-void SortedVectorImpl::reservedSortedVectorImpl4() { };
-void SortedVectorImpl::reservedSortedVectorImpl5() { };
-void SortedVectorImpl::reservedSortedVectorImpl6() { };
-void SortedVectorImpl::reservedSortedVectorImpl7() { };
-void SortedVectorImpl::reservedSortedVectorImpl8() { };
-
-
-/*****************************************************************************/
-
-} // namespace tinyutils
-} // namespace android
-
diff --git a/libpixelflinger/codeflinger/tinyutils/VectorImpl.h b/libpixelflinger/codeflinger/tinyutils/VectorImpl.h
deleted file mode 100644
index 56089b3..0000000
--- a/libpixelflinger/codeflinger/tinyutils/VectorImpl.h
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * Copyright 2005 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 ANDROID_PIXELFLINGER_VECTOR_IMPL_H
-#define ANDROID_PIXELFLINGER_VECTOR_IMPL_H
-
-#include <assert.h>
-#include <stdint.h>
-#include <sys/types.h>
-
-// ---------------------------------------------------------------------------
-// No user serviceable parts in here...
-// ---------------------------------------------------------------------------
-
-namespace android {
-namespace tinyutils {
-
-/*!
- * Implementation of the guts of the vector<> class
- * this ensures backward binary compatibility and
- * reduces code size.
- * For performance reasons, we expose mStorage and mCount
- * so these fields are set in stone.
- *
- */
-
-class VectorImpl
-{
-public:
-    enum { // flags passed to the ctor
-        HAS_TRIVIAL_CTOR    = 0x00000001,
-        HAS_TRIVIAL_DTOR    = 0x00000002,
-        HAS_TRIVIAL_COPY    = 0x00000004,
-        HAS_TRIVIAL_ASSIGN  = 0x00000008
-    };
-
-                            VectorImpl(size_t itemSize, uint32_t flags);
-                            VectorImpl(const VectorImpl& rhs);
-    virtual                 ~VectorImpl();
-
-    /*! must be called from subclasses destructor */
-            void            finish_vector();
-
-            VectorImpl&     operator = (const VectorImpl& rhs);    
-            
-    /*! C-style array access */
-    inline  const void*     arrayImpl() const       { return mStorage; }
-            void*           editArrayImpl();
-            
-    /*! vector stats */
-    inline  size_t          size() const        { return mCount; }
-    inline  bool            isEmpty() const     { return mCount == 0; }
-            size_t          capacity() const;
-            ssize_t         setCapacity(size_t size);
-
-            /*! append/insert another vector */
-            ssize_t         insertVectorAt(const VectorImpl& vector, size_t index);
-            ssize_t         appendVector(const VectorImpl& vector);
-            
-            /*! add/insert/replace items */
-            ssize_t         insertAt(size_t where, size_t numItems = 1);
-            ssize_t         insertAt(const void* item, size_t where, size_t numItems = 1);
-            void            pop();
-            void            push();
-            void            push(const void* item);
-            ssize_t         add();
-            ssize_t         add(const void* item);
-            ssize_t         replaceAt(size_t index);
-            ssize_t         replaceAt(const void* item, size_t index);
-
-            /*! remove items */
-            ssize_t         removeItemsAt(size_t index, size_t count = 1);
-            void            clear();
-
-            const void*     itemLocation(size_t index) const;
-            void*           editItemLocation(size_t index);
-
-protected:
-            size_t          itemSize() const;
-            void            release_storage();
-
-    virtual void            do_construct(void* storage, size_t num) const = 0;
-    virtual void            do_destroy(void* storage, size_t num) const = 0;
-    virtual void            do_copy(void* dest, const void* from, size_t num) const = 0;
-    virtual void            do_splat(void* dest, const void* item, size_t num) const = 0;
-    virtual void            do_move_forward(void* dest, const void* from, size_t num) const = 0;
-    virtual void            do_move_backward(void* dest, const void* from, size_t num) const = 0;
-
-    // take care of FBC...
-    virtual void            reservedVectorImpl1();
-    virtual void            reservedVectorImpl2();
-    virtual void            reservedVectorImpl3();
-    virtual void            reservedVectorImpl4();
-    virtual void            reservedVectorImpl5();
-    virtual void            reservedVectorImpl6();
-    virtual void            reservedVectorImpl7();
-    virtual void            reservedVectorImpl8();
-    
-private:
-        void* _grow(size_t where, size_t amount);
-        void  _shrink(size_t where, size_t amount);
-
-        inline void _do_construct(void* storage, size_t num) const;
-        inline void _do_destroy(void* storage, size_t num) const;
-        inline void _do_copy(void* dest, const void* from, size_t num) const;
-        inline void _do_splat(void* dest, const void* item, size_t num) const;
-        inline void _do_move_forward(void* dest, const void* from, size_t num) const;
-        inline void _do_move_backward(void* dest, const void* from, size_t num) const;
-
-            // These 2 fields are exposed in the inlines below,
-            // so they're set in stone.
-            void *      mStorage;   // base address of the vector
-            size_t      mCount;     // number of items
-
-    const   uint32_t    mFlags;
-    const   size_t      mItemSize;
-};
-
-
-
-class SortedVectorImpl : public VectorImpl
-{
-public:
-                            SortedVectorImpl(size_t itemSize, uint32_t flags);
-                            SortedVectorImpl(const VectorImpl& rhs);
-    virtual                 ~SortedVectorImpl();
-    
-    SortedVectorImpl&     operator = (const SortedVectorImpl& rhs);    
-
-    //! finds the index of an item
-            ssize_t         indexOf(const void* item) const;
-
-    //! finds where this item should be inserted
-            size_t          orderOf(const void* item) const;
-
-    //! add an item in the right place (or replaces it if there is one)
-            ssize_t         add(const void* item);
-
-    //! merges a vector into this one
-            ssize_t         merge(const VectorImpl& vector);
-            ssize_t         merge(const SortedVectorImpl& vector);
-             
-    //! removes an item
-            ssize_t         remove(const void* item);
-        
-protected:
-    virtual int             do_compare(const void* lhs, const void* rhs) const = 0;
-
-    // take care of FBC...
-    virtual void            reservedSortedVectorImpl1();
-    virtual void            reservedSortedVectorImpl2();
-    virtual void            reservedSortedVectorImpl3();
-    virtual void            reservedSortedVectorImpl4();
-    virtual void            reservedSortedVectorImpl5();
-    virtual void            reservedSortedVectorImpl6();
-    virtual void            reservedSortedVectorImpl7();
-    virtual void            reservedSortedVectorImpl8();
-
-private:
-            ssize_t         _indexOrderOf(const void* item, size_t* order = 0) const;
-
-            // these are made private, because they can't be used on a SortedVector
-            // (they don't have an implementation either)
-            ssize_t         add();
-            void            pop();
-            void            push();
-            void            push(const void* item);
-            ssize_t         insertVectorAt(const VectorImpl& vector, size_t index);
-            ssize_t         appendVector(const VectorImpl& vector);
-            ssize_t         insertAt(size_t where, size_t numItems = 1);
-            ssize_t         insertAt(const void* item, size_t where, size_t numItems = 1);
-            ssize_t         replaceAt(size_t index);
-            ssize_t         replaceAt(const void* item, size_t index);
-};
-
-} // namespace tinyutils
-} // namespace android
-
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_PIXELFLINGER_VECTOR_IMPL_H
diff --git a/libpixelflinger/include/private/pixelflinger/ggl_context.h b/libpixelflinger/include/private/pixelflinger/ggl_context.h
index 56a6edb..d45dabc 100644
--- a/libpixelflinger/include/private/pixelflinger/ggl_context.h
+++ b/libpixelflinger/include/private/pixelflinger/ggl_context.h
@@ -234,7 +234,7 @@
 
 // ----------------------------------------------------------------------------
 
-class needs_filter_t;
+struct needs_filter_t;
 struct needs_t {
     inline int match(const needs_filter_t& filter);
     inline bool operator == (const needs_t& rhs) const {
diff --git a/libpixelflinger/trap.cpp b/libpixelflinger/trap.cpp
index 80efeff..ea53625 100644
--- a/libpixelflinger/trap.cpp
+++ b/libpixelflinger/trap.cpp
@@ -563,10 +563,10 @@
     
     c->init_y(c, miny);
     for (int32_t y = miny; y < maxy; y++) {
-        register int32_t ex0 = ey0;
-        register int32_t ex1 = ey1;
-        register int32_t ex2 = ey2;    
-        register int32_t xl, xr;
+        int32_t ex0 = ey0;
+        int32_t ex1 = ey1;
+        int32_t ex2 = ey2;    
+        int32_t xl, xr;
         for (xl=minx ; xl<maxx ; xl++) {
             if (ex0>0 && ex1>0 && ex2>0)
                 break; // all strictly positive
diff --git a/libsparse/simg2img.c b/libsparse/simg2img.c
index 95e9b5b..b9b438e 100644
--- a/libsparse/simg2img.c
+++ b/libsparse/simg2img.c
@@ -40,7 +40,6 @@
 	int in;
 	int out;
 	int i;
-	int ret;
 	struct sparse_file *s;
 
 	if (argc < 3) {
@@ -71,10 +70,12 @@
 			exit(-1);
 		}
 
-		lseek(out, SEEK_SET, 0);
+		if (lseek(out, 0, SEEK_SET) == -1) {
+			perror("lseek failed");
+			exit(EXIT_FAILURE);
+		}
 
-		ret = sparse_file_write(s, out, false, false, false);
-		if (ret < 0) {
+		if (sparse_file_write(s, out, false, false, false) < 0) {
 			fprintf(stderr, "Cannot write output file\n");
 			exit(-1);
 		}
diff --git a/libutils/Android.mk b/libutils/Android.mk
index bae16bb..631b5a3 100644
--- a/libutils/Android.mk
+++ b/libutils/Android.mk
@@ -53,6 +53,7 @@
 LOCAL_CFLAGS_windows := -DMB_CUR_MAX=1
 LOCAL_MULTILIB := both
 LOCAL_MODULE_HOST_OS := darwin linux windows
+LOCAL_C_INCLUDES += external/safe-iop/include
 include $(BUILD_HOST_STATIC_LIBRARY)
 
 
@@ -85,6 +86,7 @@
 LOCAL_MODULE := libutils
 LOCAL_CLANG := true
 LOCAL_SANITIZE := integer
+LOCAL_C_INCLUDES += external/safe-iop/include
 include $(BUILD_STATIC_LIBRARY)
 
 # For the device, shared
@@ -98,6 +100,7 @@
         libdl \
         liblog
 LOCAL_CFLAGS := -Werror
+LOCAL_C_INCLUDES += external/safe-iop/include
 
 LOCAL_CLANG := true
 LOCAL_SANITIZE := integer
diff --git a/libutils/LinearTransform.cpp b/libutils/LinearTransform.cpp
index b7d28d4..138ce8b 100644
--- a/libutils/LinearTransform.cpp
+++ b/libutils/LinearTransform.cpp
@@ -21,11 +21,24 @@
 
 #include <utils/LinearTransform.h>
 
+// disable sanitize as these functions may intentionally overflow (see comments below).
+// the ifdef can be removed when host builds use clang.
+#if defined(__clang__)
+#define ATTRIBUTE_NO_SANITIZE_INTEGER __attribute__((no_sanitize("integer")))
+#else
+#define ATTRIBUTE_NO_SANITIZE_INTEGER
+#endif
+
 namespace android {
 
-template<class T> static inline T ABS(T x) { return (x < 0) ? -x : x; }
+// sanitize failure with T = int32_t and x = 0x80000000
+template<class T>
+ATTRIBUTE_NO_SANITIZE_INTEGER
+static inline T ABS(T x) { return (x < 0) ? -x : x; }
 
 // Static math methods involving linear transformations
+// remote sanitize failure on overflow case.
+ATTRIBUTE_NO_SANITIZE_INTEGER
 static bool scale_u64_to_u64(
         uint64_t val,
         uint32_t N,
@@ -109,6 +122,8 @@
     return true;
 }
 
+// at least one known sanitize failure (see comment below)
+ATTRIBUTE_NO_SANITIZE_INTEGER
 static bool linear_transform_s64_to_s64(
         int64_t  val,
         int64_t  basis1,
@@ -172,7 +187,7 @@
         // (scaled_signbit XOR res_signbit)
 
         if (is_neg)
-            scaled = -scaled;
+            scaled = -scaled; // known sanitize failure
         res = scaled + basis2;
 
         if ((scaled ^ basis2 ^ INT64_MIN) & (scaled ^ res) & INT64_MIN)
@@ -250,6 +265,8 @@
 template void LinearTransform::reduce<uint64_t>(uint64_t* N, uint64_t* D);
 template void LinearTransform::reduce<uint32_t>(uint32_t* N, uint32_t* D);
 
+// sanitize failure if *N = 0x80000000
+ATTRIBUTE_NO_SANITIZE_INTEGER
 void LinearTransform::reduce(int32_t* N, uint32_t* D) {
     if (N && D && *D) {
         if (*N < 0) {
diff --git a/libutils/VectorImpl.cpp b/libutils/VectorImpl.cpp
index 2ac158b..e8d40ed 100644
--- a/libutils/VectorImpl.cpp
+++ b/libutils/VectorImpl.cpp
@@ -21,6 +21,7 @@
 #include <stdio.h>
 
 #include <cutils/log.h>
+#include <safe_iop.h>
 
 #include <utils/Errors.h>
 #include <utils/VectorImpl.h>
@@ -86,14 +87,19 @@
 void* VectorImpl::editArrayImpl()
 {
     if (mStorage) {
-        SharedBuffer* sb = SharedBuffer::bufferFromData(mStorage)->attemptEdit();
-        if (sb == 0) {
-            sb = SharedBuffer::alloc(capacity() * mItemSize);
-            if (sb) {
-                _do_copy(sb->data(), mStorage, mCount);
-                release_storage();
-                mStorage = sb->data();
-            }
+        const SharedBuffer* sb = SharedBuffer::bufferFromData(mStorage);
+        SharedBuffer* editable = sb->attemptEdit();
+        if (editable == 0) {
+            // If we're here, we're not the only owner of the buffer.
+            // We must make a copy of it.
+            editable = SharedBuffer::alloc(sb->size());
+            // Fail instead of returning a pointer to storage that's not
+            // editable. Otherwise we'd be editing the contents of a buffer
+            // for which we're not the only owner, which is undefined behaviour.
+            LOG_ALWAYS_FATAL_IF(editable == NULL);
+            _do_copy(editable->data(), mStorage, mCount);
+            release_storage();
+            mStorage = editable->data();
         }
     }
     return mStorage;
@@ -329,13 +335,15 @@
 
 ssize_t VectorImpl::setCapacity(size_t new_capacity)
 {
-    size_t current_capacity = capacity();
-    ssize_t amount = new_capacity - size();
-    if (amount <= 0) {
-        // we can't reduce the capacity
-        return current_capacity;
-    } 
-    SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize);
+    // The capacity must always be greater than or equal to the size
+    // of this vector.
+    if (new_capacity <= size()) {
+        return capacity();
+    }
+
+    size_t new_allocation_size = 0;
+    LOG_ALWAYS_FATAL_IF(!safe_mul(&new_allocation_size, new_capacity, mItemSize));
+    SharedBuffer* sb = SharedBuffer::alloc(new_allocation_size);
     if (sb) {
         void* array = sb->data();
         _do_copy(array, mStorage, size());
@@ -377,9 +385,28 @@
             "[%p] _grow: where=%d, amount=%d, count=%d",
             this, (int)where, (int)amount, (int)mCount); // caller already checked
 
-    const size_t new_size = mCount + amount;
+    size_t new_size;
+    LOG_ALWAYS_FATAL_IF(!safe_add(&new_size, mCount, amount), "new_size overflow");
+
     if (capacity() < new_size) {
-        const size_t new_capacity = max(kMinVectorCapacity, ((new_size*3)+1)/2);
+        // NOTE: This implementation used to resize vectors as per ((3*x + 1) / 2)
+        // (sigh..). Also note, the " + 1" was necessary to handle the special case
+        // where x == 1, where the resized_capacity will be equal to the old
+        // capacity without the +1. The old calculation wouldn't work properly
+        // if x was zero.
+        //
+        // This approximates the old calculation, using (x + (x/2) + 1) instead.
+        size_t new_capacity = 0;
+        LOG_ALWAYS_FATAL_IF(!safe_add(&new_capacity, new_size, (new_size / 2)),
+                            "new_capacity overflow");
+        LOG_ALWAYS_FATAL_IF(!safe_add(&new_capacity, new_capacity, static_cast<size_t>(1u)),
+                            "new_capacity overflow");
+        new_capacity = max(kMinVectorCapacity, new_capacity);
+
+        size_t new_alloc_size = 0;
+        LOG_ALWAYS_FATAL_IF(!safe_mul(&new_alloc_size, new_capacity, mItemSize),
+                            "new_alloc_size overflow");
+
 //        ALOGV("grow vector %p, new_capacity=%d", this, (int)new_capacity);
         if ((mStorage) &&
             (mCount==where) &&
@@ -387,14 +414,14 @@
             (mFlags & HAS_TRIVIAL_DTOR))
         {
             const SharedBuffer* cur_sb = SharedBuffer::bufferFromData(mStorage);
-            SharedBuffer* sb = cur_sb->editResize(new_capacity * mItemSize);
+            SharedBuffer* sb = cur_sb->editResize(new_alloc_size);
             if (sb) {
                 mStorage = sb->data();
             } else {
                 return NULL;
             }
         } else {
-            SharedBuffer* sb = SharedBuffer::alloc(new_capacity * mItemSize);
+            SharedBuffer* sb = SharedBuffer::alloc(new_alloc_size);
             if (sb) {
                 void* array = sb->data();
                 if (where != 0) {
@@ -436,10 +463,19 @@
             "[%p] _shrink: where=%d, amount=%d, count=%d",
             this, (int)where, (int)amount, (int)mCount); // caller already checked
 
-    const size_t new_size = mCount - amount;
-    if (new_size*3 < capacity()) {
-        const size_t new_capacity = max(kMinVectorCapacity, new_size*2);
-//        ALOGV("shrink vector %p, new_capacity=%d", this, (int)new_capacity);
+    size_t new_size;
+    LOG_ALWAYS_FATAL_IF(!safe_sub(&new_size, mCount, amount));
+
+    if (new_size < (capacity() / 2)) {
+        // NOTE: (new_size * 2) is safe because capacity didn't overflow and
+        // new_size < (capacity / 2)).
+        const size_t new_capacity = max(kMinVectorCapacity, new_size * 2);
+
+        // NOTE: (new_capacity * mItemSize), (where * mItemSize) and
+        // ((where + amount) * mItemSize) beyond this point are safe because
+        // we are always reducing the capacity of the underlying SharedBuffer.
+        // In other words, (old_capacity * mItemSize) did not overflow, and
+        // where < (where + amount) < new_capacity < old_capacity.
         if ((where == new_size) &&
             (mFlags & HAS_TRIVIAL_COPY) &&
             (mFlags & HAS_TRIVIAL_DTOR))
diff --git a/libutils/tests/Android.mk b/libutils/tests/Android.mk
index 514f8c1..8f07f1a 100644
--- a/libutils/tests/Android.mk
+++ b/libutils/tests/Android.mk
@@ -27,6 +27,7 @@
     Looper_test.cpp \
     LruCache_test.cpp \
     String8_test.cpp \
+    StrongPointer_test.cpp \
     Unicode_test.cpp \
     Vector_test.cpp \
 
@@ -37,3 +38,11 @@
     libutils \
 
 include $(BUILD_NATIVE_TEST)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libutils_tests_host
+LOCAL_SRC_FILES := Vector_test.cpp
+LOCAL_STATIC_LIBRARIES := libutils liblog
+
+include $(BUILD_HOST_NATIVE_TEST)
diff --git a/libutils/tests/StrongPointer_test.cpp b/libutils/tests/StrongPointer_test.cpp
new file mode 100644
index 0000000..f46d6d1
--- /dev/null
+++ b/libutils/tests/StrongPointer_test.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <utils/StrongPointer.h>
+#include <utils/RefBase.h>
+
+using namespace android;
+
+class Foo : public LightRefBase<Foo> {
+public:
+    Foo(bool* deleted_check) : mDeleted(deleted_check) {
+        *mDeleted = false;
+    }
+
+    ~Foo() {
+        *mDeleted = true;
+    }
+private:
+    bool* mDeleted;
+};
+
+TEST(StrongPointer, move) {
+    bool isDeleted;
+    Foo* foo = new Foo(&isDeleted);
+    ASSERT_EQ(0, foo->getStrongCount());
+    ASSERT_FALSE(isDeleted) << "Already deleted...?";
+    sp<Foo> sp1(foo);
+    ASSERT_EQ(1, foo->getStrongCount());
+    {
+        sp<Foo> sp2 = std::move(sp1);
+        ASSERT_EQ(1, foo->getStrongCount()) << "std::move failed, incremented refcnt";
+        ASSERT_EQ(nullptr, sp1.get()) << "std::move failed, sp1 is still valid";
+        // The strong count isn't increasing, let's double check the old object
+        // is properly reset and doesn't early delete
+        sp1 = std::move(sp2);
+    }
+    ASSERT_FALSE(isDeleted) << "deleted too early! still has a reference!";
+    {
+        // Now let's double check it deletes on time
+        sp<Foo> sp2 = std::move(sp1);
+    }
+    ASSERT_TRUE(isDeleted) << "foo was leaked!";
+}
diff --git a/libutils/tests/Vector_test.cpp b/libutils/tests/Vector_test.cpp
index 0ba7161..d9b32f9 100644
--- a/libutils/tests/Vector_test.cpp
+++ b/libutils/tests/Vector_test.cpp
@@ -16,6 +16,8 @@
 
 #define LOG_TAG "Vector_test"
 
+#define __STDC_LIMIT_MACROS
+#include <stdint.h>
 #include <utils/Vector.h>
 #include <cutils/log.h>
 #include <gtest/gtest.h>
@@ -71,5 +73,80 @@
     EXPECT_EQ(other[3], 5);
 }
 
+// TODO: gtest isn't capable of parsing Abort messages formatted by
+// Android (fails differently on host and target), so we always need to
+// use an empty error message for death tests.
+TEST_F(VectorTest, SetCapacity_Overflow) {
+  Vector<int> vector;
+  EXPECT_DEATH(vector.setCapacity(SIZE_MAX / sizeof(int) + 1), "");
+}
+
+TEST_F(VectorTest, SetCapacity_ShrinkBelowSize) {
+  Vector<int> vector;
+  vector.add(1);
+  vector.add(2);
+  vector.add(3);
+  vector.add(4);
+
+  vector.setCapacity(8);
+  ASSERT_EQ(8, vector.capacity());
+  vector.setCapacity(2);
+  ASSERT_EQ(8, vector.capacity());
+}
+
+// NOTE: All of the tests below are useless because of the "TODO" above.
+// We have no way of knowing *why* the process crashed. Given that we're
+// inserting a NULL array, we'll fail with a SIGSEGV eventually. We need
+// the ability to make assertions on the abort message to make sure we're
+// failing for the right reasons.
+TEST_F(VectorTest, _grow_OverflowSize) {
+  Vector<int> vector;
+  vector.add(1);
+
+  // Checks that the size calculation (not the capacity calculation) doesn't
+  // overflow : the size here will be (1 + SIZE_MAX).
+  //
+  // EXPECT_DEATH(vector.insertArrayAt(NULL, 0, SIZE_MAX), "new_size_overflow");
+  EXPECT_DEATH(vector.insertArrayAt(NULL, 0, SIZE_MAX), "");
+}
+
+TEST_F(VectorTest, _grow_OverflowCapacityDoubling) {
+  Vector<int> vector;
+
+  // This should fail because the calculated capacity will overflow even though
+  // the size of the vector doesn't.
+  //
+  // EXPECT_DEATH(vector.insertArrayAt(NULL, 0, (SIZE_MAX - 1)), "new_capacity_overflow");
+  EXPECT_DEATH(vector.insertArrayAt(NULL, 0, (SIZE_MAX - 1)), "");
+}
+
+TEST_F(VectorTest, _grow_OverflowBufferAlloc) {
+  Vector<int> vector;
+  // This should fail because the capacity * sizeof(int) overflows, even
+  // though the capacity itself doesn't.
+  //
+  // EXPECT_DEATH(vector.insertArrayAt(NULL, 0, (SIZE_MAX / 2)), "new_alloc_size overflow");
+  EXPECT_DEATH(vector.insertArrayAt(NULL, 0, (SIZE_MAX / 2)), "");
+}
+
+TEST_F(VectorTest, editArray_Shared) {
+  Vector<int> vector1;
+  vector1.add(1);
+  vector1.add(2);
+  vector1.add(3);
+  vector1.add(4);
+
+  Vector<int> vector2 = vector1;
+  ASSERT_EQ(vector1.array(), vector2.array());
+  // We must make a copy here, since we're not the exclusive owners
+  // of this array.
+  ASSERT_NE(vector1.editArray(), vector2.editArray());
+
+  // Vector doesn't implement operator ==.
+  ASSERT_EQ(vector1.size(), vector2.size());
+  for (size_t i = 0; i < vector1.size(); ++i) {
+    EXPECT_EQ(vector1[i], vector2[i]);
+  }
+}
 
 } // namespace android
diff --git a/libziparchive/Android.mk b/libziparchive/Android.mk
index 8ff94d4..8a4921f 100644
--- a/libziparchive/Android.mk
+++ b/libziparchive/Android.mk
@@ -18,9 +18,12 @@
 source_files := zip_archive.cc zip_writer.cc
 test_files := zip_archive_test.cc zip_writer_test.cc entry_name_utils_test.cc
 
+# ZLIB_CONST turns on const for input buffers, which is pretty standard.
+common_c_flags := -Werror -Wall -DZLIB_CONST
+
 # Incorrectly warns when C++11 empty brace {} initializer is used.
 # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61489
-common_cpp_flags := -Wno-missing-field-initializers
+common_cpp_flags := -Wold-style-cast -Wno-missing-field-initializers
 
 include $(CLEAR_VARS)
 LOCAL_CPP_EXTENSION := .cc
@@ -28,8 +31,8 @@
 LOCAL_STATIC_LIBRARIES := libz
 LOCAL_SHARED_LIBRARIES := libutils libbase
 LOCAL_MODULE:= libziparchive
-LOCAL_CFLAGS := -Werror -Wall
-LOCAL_CPPFLAGS := -Wold-style-cast $(common_cpp_flags)
+LOCAL_CFLAGS := $(common_c_flags)
+LOCAL_CPPFLAGS := $(common_cpp_flags)
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
@@ -37,7 +40,7 @@
 LOCAL_SRC_FILES := ${source_files}
 LOCAL_STATIC_LIBRARIES := libz libutils libbase
 LOCAL_MODULE:= libziparchive-host
-LOCAL_CFLAGS := -Werror
+LOCAL_CFLAGS := $(common_c_flags)
 LOCAL_CFLAGS_windows := -mno-ms-bitfields
 LOCAL_CPPFLAGS := $(common_cpp_flags)
 
@@ -51,7 +54,7 @@
 LOCAL_STATIC_LIBRARIES := libutils
 LOCAL_SHARED_LIBRARIES := libz-host liblog libbase
 LOCAL_MODULE:= libziparchive-host
-LOCAL_CFLAGS := -Werror
+LOCAL_CFLAGS := $(common_c_flags)
 LOCAL_CPPFLAGS := $(common_cpp_flags)
 LOCAL_MULTILIB := both
 include $(BUILD_HOST_SHARED_LIBRARY)
@@ -60,7 +63,7 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE := ziparchive-tests
 LOCAL_CPP_EXTENSION := .cc
-LOCAL_CFLAGS := -Werror
+LOCAL_CFLAGS := $(common_c_flags)
 LOCAL_CPPFLAGS := $(common_cpp_flags)
 LOCAL_SRC_FILES := $(test_files)
 LOCAL_SHARED_LIBRARIES := liblog libbase
@@ -70,7 +73,7 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE := ziparchive-tests-host
 LOCAL_CPP_EXTENSION := .cc
-LOCAL_CFLAGS := -Werror
+LOCAL_CFLAGS := $(common_c_flags)
 LOCAL_CPPFLAGS := -Wno-unnamed-type-template-args $(common_cpp_flags)
 LOCAL_SRC_FILES := $(test_files)
 LOCAL_SHARED_LIBRARIES := libziparchive-host liblog libbase
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index f1e13a7..3d18f7c 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -588,7 +588,7 @@
   // and other interesting attributes from the central directory. These
   // will later be compared against values from the local file header.
   data->method = cdr->compression_method;
-  data->mod_time = cdr->last_mod_time;
+  data->mod_time = cdr->last_mod_date << 16 | cdr->last_mod_time;
   data->crc32 = cdr->crc32;
   data->compressed_length = cdr->compressed_size;
   data->uncompressed_length = cdr->uncompressed_size;
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index 9a3cdb4..32b1a38 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -272,6 +272,7 @@
   ASSERT_EQ(static_cast<uint32_t>(17), data.uncompressed_length);
   ASSERT_EQ(static_cast<uint32_t>(13), data.compressed_length);
   ASSERT_EQ(0x950821c5, data.crc32);
+  ASSERT_EQ(static_cast<uint32_t>(0x438a8005), data.mod_time);
 
   // An entry that doesn't exist. Should be a negative return code.
   ZipString absent_name;
diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc
index de75d1e..22a7c53 100644
--- a/libziparchive/zip_writer.cc
+++ b/libziparchive/zip_writer.cc
@@ -18,10 +18,13 @@
 #include "zip_archive_common.h"
 #include "ziparchive/zip_writer.h"
 
+#include <utils/Log.h>
+
 #include <cassert>
 #include <cstdio>
 #include <memory>
 #include <zlib.h>
+#define DEF_MEM_LEVEL 8                // normally in zutil.h?
 
 /* Zip compression methods we support */
 enum {
@@ -29,6 +32,9 @@
   kCompressDeflated   = 8,        // standard deflate
 };
 
+// Size of the output buffer used for compression.
+static const size_t kBufSize = 32768u;
+
 // No error, operation completed successfully.
 static const int32_t kNoError = 0;
 
@@ -41,10 +47,14 @@
 // The zip entry name was invalid.
 static const int32_t kInvalidEntryName = -3;
 
+// An error occurred in zlib.
+static const int32_t kZlibError = -4;
+
 static const char* sErrorCodes[] = {
     "Invalid state",
     "IO error",
     "Invalid entry name",
+    "Zlib error",
 };
 
 const char* ZipWriter::ErrorCodeString(int32_t error_code) {
@@ -54,13 +64,21 @@
   return nullptr;
 }
 
-ZipWriter::ZipWriter(FILE* f) : file_(f), current_offset_(0), state_(State::kWritingZip) {
+static void DeleteZStream(z_stream* stream) {
+  deflateEnd(stream);
+  delete stream;
+}
+
+ZipWriter::ZipWriter(FILE* f) : file_(f), current_offset_(0), state_(State::kWritingZip),
+                                z_stream_(nullptr, DeleteZStream), buffer_(kBufSize) {
 }
 
 ZipWriter::ZipWriter(ZipWriter&& writer) : file_(writer.file_),
                                            current_offset_(writer.current_offset_),
                                            state_(writer.state_),
-                                           files_(std::move(writer.files_)) {
+                                           files_(std::move(writer.files_)),
+                                           z_stream_(std::move(writer.z_stream_)),
+                                           buffer_(std::move(writer.buffer_)){
   writer.file_ = nullptr;
   writer.state_ = State::kError;
 }
@@ -70,6 +88,8 @@
   current_offset_ = writer.current_offset_;
   state_ = writer.state_;
   files_ = std::move(writer.files_);
+  z_stream_ = std::move(writer.z_stream_);
+  buffer_ = std::move(writer.buffer_);
   writer.file_ = nullptr;
   writer.state_ = State::kError;
   return *this;
@@ -77,6 +97,7 @@
 
 int32_t ZipWriter::HandleError(int32_t error_code) {
   state_ = State::kError;
+  z_stream_.reset();
   return error_code;
 }
 
@@ -126,8 +147,16 @@
   // containing the crc and size fields.
   header.gpb_flags |= kGPBDDFlagMask;
 
-  // For now, ignore the ZipWriter::kCompress flag.
-  fileInfo.compression_method = kCompressStored;
+  if (flags & ZipWriter::kCompress) {
+    fileInfo.compression_method = kCompressDeflated;
+
+    int32_t result = PrepareDeflate();
+    if (result != kNoError) {
+      return result;
+    }
+  } else {
+    fileInfo.compression_method = kCompressStored;
+  }
   header.compression_method = fileInfo.compression_method;
 
   ExtractTimeAndDate(time, &fileInfo.last_mod_time, &fileInfo.last_mod_date);
@@ -163,40 +192,138 @@
   return kNoError;
 }
 
+int32_t ZipWriter::PrepareDeflate() {
+  assert(state_ == State::kWritingZip);
+
+  // Initialize the z_stream for compression.
+  z_stream_ = std::unique_ptr<z_stream, void(*)(z_stream*)>(new z_stream(), DeleteZStream);
+
+  int zerr = deflateInit2(z_stream_.get(), Z_BEST_COMPRESSION, Z_DEFLATED, -MAX_WBITS,
+                          DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
+  if (zerr != Z_OK) {
+    if (zerr == Z_VERSION_ERROR) {
+      ALOGE("Installed zlib is not compatible with linked version (%s)", ZLIB_VERSION);
+      return HandleError(kZlibError);
+    } else {
+      ALOGE("deflateInit2 failed (zerr=%d)", zerr);
+      return HandleError(kZlibError);
+    }
+  }
+
+  z_stream_->next_out = buffer_.data();
+  z_stream_->avail_out = buffer_.size();
+  return kNoError;
+}
+
 int32_t ZipWriter::WriteBytes(const void* data, size_t len) {
   if (state_ != State::kWritingEntry) {
     return HandleError(kInvalidState);
   }
 
   FileInfo& currentFile = files_.back();
+  int32_t result = kNoError;
   if (currentFile.compression_method & kCompressDeflated) {
-    // TODO(adamlesinski): Implement compression using zlib deflate.
-    assert(false);
+    result = CompressBytes(&currentFile, data, len);
   } else {
-    if (fwrite(data, 1, len, file_) != len) {
-      return HandleError(kIoError);
-    }
-    currentFile.crc32 = crc32(currentFile.crc32, reinterpret_cast<const Bytef*>(data), len);
-    currentFile.compressed_size += len;
-    current_offset_ += len;
+    result = StoreBytes(&currentFile, data, len);
   }
 
+  if (result != kNoError) {
+    return result;
+  }
+
+  currentFile.crc32 = crc32(currentFile.crc32, reinterpret_cast<const Bytef*>(data), len);
   currentFile.uncompressed_size += len;
   return kNoError;
 }
 
+int32_t ZipWriter::StoreBytes(FileInfo* file, const void* data, size_t len) {
+  assert(state_ == State::kWritingEntry);
+
+  if (fwrite(data, 1, len, file_) != len) {
+    return HandleError(kIoError);
+  }
+  file->compressed_size += len;
+  current_offset_ += len;
+  return kNoError;
+}
+
+int32_t ZipWriter::CompressBytes(FileInfo* file, const void* data, size_t len) {
+  assert(state_ == State::kWritingEntry);
+  assert(z_stream_);
+  assert(z_stream_->next_out != nullptr);
+  assert(z_stream_->avail_out != 0);
+
+  // Prepare the input.
+  z_stream_->next_in = reinterpret_cast<const uint8_t*>(data);
+  z_stream_->avail_in = len;
+
+  while (z_stream_->avail_in > 0) {
+    // We have more data to compress.
+    int zerr = deflate(z_stream_.get(), Z_NO_FLUSH);
+    if (zerr != Z_OK) {
+      return HandleError(kZlibError);
+    }
+
+    if (z_stream_->avail_out == 0) {
+      // The output is full, let's write it to disk.
+      size_t dataToWrite = z_stream_->next_out - buffer_.data();
+      if (fwrite(buffer_.data(), 1, dataToWrite, file_) != dataToWrite) {
+        return HandleError(kIoError);
+      }
+      file->compressed_size += dataToWrite;
+      current_offset_ += dataToWrite;
+
+      // Reset the output buffer for the next input.
+      z_stream_->next_out = buffer_.data();
+      z_stream_->avail_out = buffer_.size();
+    }
+  }
+  return kNoError;
+}
+
+int32_t ZipWriter::FlushCompressedBytes(FileInfo* file) {
+  assert(state_ == State::kWritingEntry);
+  assert(z_stream_);
+  assert(z_stream_->next_out != nullptr);
+  assert(z_stream_->avail_out != 0);
+
+  int zerr = deflate(z_stream_.get(), Z_FINISH);
+  if (zerr != Z_STREAM_END) {
+    return HandleError(kZlibError);
+  }
+
+  size_t dataToWrite = z_stream_->next_out - buffer_.data();
+  if (dataToWrite != 0) {
+    if (fwrite(buffer_.data(), 1, dataToWrite, file_) != dataToWrite) {
+      return HandleError(kIoError);
+    }
+    file->compressed_size += dataToWrite;
+    current_offset_ += dataToWrite;
+  }
+  z_stream_.reset();
+  return kNoError;
+}
+
 int32_t ZipWriter::FinishEntry() {
   if (state_ != State::kWritingEntry) {
     return kInvalidState;
   }
 
+  FileInfo& currentFile = files_.back();
+  if (currentFile.compression_method & kCompressDeflated) {
+    int32_t result = FlushCompressedBytes(&currentFile);
+    if (result != kNoError) {
+      return result;
+    }
+  }
+
   const uint32_t sig = DataDescriptor::kOptSignature;
   if (fwrite(&sig, sizeof(sig), 1, file_) != 1) {
     state_ = State::kError;
     return kIoError;
   }
 
-  FileInfo& currentFile = files_.back();
   DataDescriptor dd = {};
   dd.crc32 = currentFile.crc32;
   dd.compressed_size = currentFile.compressed_size;
@@ -241,8 +368,8 @@
 
   EocdRecord er = {};
   er.eocd_signature = EocdRecord::kSignature;
-  er.disk_num = 1;
-  er.cd_start_disk = 1;
+  er.disk_num = 0;
+  er.cd_start_disk = 0;
   er.num_records_on_disk = files_.size();
   er.num_records = files_.size();
   er.cd_size = current_offset_ - startOfCdr;
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc
index 5269730..046f195 100644
--- a/libziparchive/zip_writer_test.cc
+++ b/libziparchive/zip_writer_test.cc
@@ -44,26 +44,26 @@
 
   const char* expected = "hello";
 
-  ASSERT_EQ(writer.StartEntry("file.txt", 0), 0);
-  ASSERT_EQ(writer.WriteBytes("he", 2), 0);
-  ASSERT_EQ(writer.WriteBytes("llo", 3), 0);
-  ASSERT_EQ(writer.FinishEntry(), 0);
-  ASSERT_EQ(writer.Finish(), 0);
+  ASSERT_EQ(0, writer.StartEntry("file.txt", 0));
+  ASSERT_EQ(0, writer.WriteBytes("he", 2));
+  ASSERT_EQ(0, writer.WriteBytes("llo", 3));
+  ASSERT_EQ(0, writer.FinishEntry());
+  ASSERT_EQ(0, writer.Finish());
 
-  ASSERT_GE(lseek(fd_, 0, SEEK_SET), 0);
+  ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
 
   ZipArchiveHandle handle;
-  ASSERT_EQ(OpenArchiveFd(fd_, "temp", &handle, false), 0);
+  ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
 
   ZipEntry data;
-  ASSERT_EQ(FindEntry(handle, ZipString("file.txt"), &data), 0);
-  EXPECT_EQ(data.compressed_length, strlen(expected));
-  EXPECT_EQ(data.uncompressed_length, strlen(expected));
-  EXPECT_EQ(data.method, kCompressStored);
+  ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+  EXPECT_EQ(strlen(expected), data.compressed_length);
+  EXPECT_EQ(strlen(expected), data.uncompressed_length);
+  EXPECT_EQ(kCompressStored, data.method);
 
   char buffer[6];
-  EXPECT_EQ(ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(&buffer), sizeof(buffer)),
-            0);
+  EXPECT_EQ(0,
+            ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(&buffer), sizeof(buffer)));
   buffer[5] = 0;
 
   EXPECT_STREQ(expected, buffer);
@@ -74,49 +74,49 @@
 TEST_F(zipwriter, WriteUncompressedZipWithMultipleFiles) {
   ZipWriter writer(file_);
 
-  ASSERT_EQ(writer.StartEntry("file.txt", 0), 0);
-  ASSERT_EQ(writer.WriteBytes("he", 2), 0);
-  ASSERT_EQ(writer.FinishEntry(), 0);
+  ASSERT_EQ(0, writer.StartEntry("file.txt", 0));
+  ASSERT_EQ(0, writer.WriteBytes("he", 2));
+  ASSERT_EQ(0, writer.FinishEntry());
 
-  ASSERT_EQ(writer.StartEntry("file/file.txt", 0), 0);
-  ASSERT_EQ(writer.WriteBytes("llo", 3), 0);
-  ASSERT_EQ(writer.FinishEntry(), 0);
+  ASSERT_EQ(0, writer.StartEntry("file/file.txt", 0));
+  ASSERT_EQ(0, writer.WriteBytes("llo", 3));
+  ASSERT_EQ(0, writer.FinishEntry());
 
-  ASSERT_EQ(writer.StartEntry("file/file2.txt", 0), 0);
-  ASSERT_EQ(writer.FinishEntry(), 0);
+  ASSERT_EQ(0, writer.StartEntry("file/file2.txt", 0));
+  ASSERT_EQ(0, writer.FinishEntry());
 
-  ASSERT_EQ(writer.Finish(), 0);
+  ASSERT_EQ(0, writer.Finish());
 
-  ASSERT_GE(lseek(fd_, 0, SEEK_SET), 0);
+  ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
 
   ZipArchiveHandle handle;
-  ASSERT_EQ(OpenArchiveFd(fd_, "temp", &handle, false), 0);
+  ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
 
   char buffer[4];
   ZipEntry data;
 
-  ASSERT_EQ(FindEntry(handle, ZipString("file.txt"), &data), 0);
-  EXPECT_EQ(data.method, kCompressStored);
-  EXPECT_EQ(data.compressed_length, 2u);
-  EXPECT_EQ(data.uncompressed_length, 2u);
-  ASSERT_EQ(ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(buffer), arraysize(buffer)),
-            0);
+  ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+  EXPECT_EQ(kCompressStored, data.method);
+  EXPECT_EQ(2u, data.compressed_length);
+  EXPECT_EQ(2u, data.uncompressed_length);
+  ASSERT_EQ(0,
+            ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(buffer), arraysize(buffer)));
   buffer[2] = 0;
   EXPECT_STREQ("he", buffer);
 
-  ASSERT_EQ(FindEntry(handle, ZipString("file/file.txt"), &data), 0);
-  EXPECT_EQ(data.method, kCompressStored);
-  EXPECT_EQ(data.compressed_length, 3u);
-  EXPECT_EQ(data.uncompressed_length, 3u);
-  ASSERT_EQ(ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(buffer), arraysize(buffer)),
-            0);
+  ASSERT_EQ(0, FindEntry(handle, ZipString("file/file.txt"), &data));
+  EXPECT_EQ(kCompressStored, data.method);
+  EXPECT_EQ(3u, data.compressed_length);
+  EXPECT_EQ(3u, data.uncompressed_length);
+  ASSERT_EQ(0,
+            ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(buffer), arraysize(buffer)));
   buffer[3] = 0;
   EXPECT_STREQ("llo", buffer);
 
-  ASSERT_EQ(FindEntry(handle, ZipString("file/file2.txt"), &data), 0);
-  EXPECT_EQ(data.method, kCompressStored);
-  EXPECT_EQ(data.compressed_length, 0u);
-  EXPECT_EQ(data.uncompressed_length, 0u);
+  ASSERT_EQ(0, FindEntry(handle, ZipString("file/file2.txt"), &data));
+  EXPECT_EQ(kCompressStored, data.method);
+  EXPECT_EQ(0u, data.compressed_length);
+  EXPECT_EQ(0u, data.uncompressed_length);
 
   CloseArchive(handle);
 }
@@ -124,17 +124,47 @@
 TEST_F(zipwriter, WriteUncompressedZipWithAlignedFile) {
   ZipWriter writer(file_);
 
-  ASSERT_EQ(writer.StartEntry("align.txt", ZipWriter::kAlign32), 0);
-  ASSERT_EQ(writer.WriteBytes("he", 2), 0);
-  ASSERT_EQ(writer.FinishEntry(), 0);
-  ASSERT_EQ(writer.Finish(), 0);
+  ASSERT_EQ(0, writer.StartEntry("align.txt", ZipWriter::kAlign32));
+  ASSERT_EQ(0, writer.WriteBytes("he", 2));
+  ASSERT_EQ(0, writer.FinishEntry());
+  ASSERT_EQ(0, writer.Finish());
 
-  ASSERT_GE(lseek(fd_, 0, SEEK_SET), 0);
+  ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
 
   ZipArchiveHandle handle;
-  ASSERT_EQ(OpenArchiveFd(fd_, "temp", &handle, false), 0);
+  ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
 
   ZipEntry data;
-  ASSERT_EQ(FindEntry(handle, ZipString("align.txt"), &data), 0);
-  EXPECT_EQ(data.offset & 0x03, 0);
+  ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
+  EXPECT_EQ(0, data.offset & 0x03);
+
+  CloseArchive(handle);
+}
+
+TEST_F(zipwriter, WriteCompressedZipWithOneFile) {
+  ZipWriter writer(file_);
+
+  ASSERT_EQ(0, writer.StartEntry("file.txt", ZipWriter::kCompress));
+  ASSERT_EQ(0, writer.WriteBytes("helo", 4));
+  ASSERT_EQ(0, writer.FinishEntry());
+  ASSERT_EQ(0, writer.Finish());
+
+  ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
+
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
+
+  ZipEntry data;
+  ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+  EXPECT_EQ(kCompressDeflated, data.method);
+  EXPECT_EQ(4u, data.uncompressed_length);
+
+  char buffer[5];
+  ASSERT_EQ(0,
+            ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(buffer), arraysize(buffer)));
+  buffer[4] = 0;
+
+  EXPECT_STREQ("helo", buffer);
+
+  CloseArchive(handle);
 }
diff --git a/lmkd/lmkd.rc b/lmkd/lmkd.rc
index 83c5ff0..7d6cb11 100644
--- a/lmkd/lmkd.rc
+++ b/lmkd/lmkd.rc
@@ -2,3 +2,4 @@
     class core
     critical
     socket lmkd seqpacket 0660 system system
+    writepid /dev/cpuset/system-background/tasks
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index ddbfb3e..4d7adf1 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -259,8 +259,8 @@
                     "  -r <kbytes>     Rotate log every kbytes. Requires -f\n"
                     "  -n <count>      Sets max number of rotated logs to <count>, default 4\n"
                     "  -v <format>     Sets the log print format, where <format> is:\n\n"
-                    "                      brief color long printable process raw tag thread\n"
-                    "                      threadtime time usec UTC year zone\n\n"
+                    "                      brief color epoch long monotonic printable process raw\n"
+                    "                      tag thread threadtime time usec UTC year zone\n\n"
                     "  -D              print dividers between each log buffer\n"
                     "  -c              clear (flush) the entire log and exit\n"
                     "  -d              dump the log and then exit (don't block)\n"
@@ -269,7 +269,7 @@
                     "  -T <count>      print only the most recent <count> lines (does not imply -d)\n"
                     "  -T '<time>'     print most recent lines since specified time (not imply -d)\n"
                     "                  count is pure numerical, time is 'MM-DD hh:mm:ss.mmm...'\n"
-                    "                  or 'YYYY-MM-DD hh:mm:ss.mmm...' format\n"
+                    "                  'YYYY-MM-DD hh:mm:ss.mmm...' or 'sssss.mmm...' format\n"
                     "  -g              get the size of the log's ring buffer and exit\n"
                     "  -L              dump logs from prior to last reboot\n"
                     "  -b <buffer>     Request alternate ring buffer, 'main', 'system', 'radio',\n"
@@ -384,7 +384,11 @@
     if (ep) {
         return ep;
     }
-    return t.strptime(cp, "%Y-%m-%d %H:%M:%S.%q");
+    ep = t.strptime(cp, "%Y-%m-%d %H:%M:%S.%q");
+    if (ep) {
+        return ep;
+    }
+    return t.strptime(cp, "%s.%q");
 }
 
 // Find last logged line in gestalt of all matching existing output files
@@ -395,6 +399,10 @@
     }
 
     log_time now(CLOCK_REALTIME);
+    bool monotonic = android_log_timestamp() == 'm';
+    if (monotonic) {
+        now = log_time(CLOCK_MONOTONIC);
+    }
 
     std::string directory;
     char *file = strrchr(outputFileName, '/');
@@ -413,7 +421,11 @@
     struct dirent *dp;
     while ((dp = readdir(dir.get())) != NULL) {
         if ((dp->d_type != DT_REG)
-                || strncmp(dp->d_name, file, len)
+                // If we are using realtime, check all files that match the
+                // basename for latest time. If we are using monotonic time
+                // then only check the main file because time cycles on
+                // every reboot.
+                || strncmp(dp->d_name, file, len + monotonic)
                 || (dp->d_name[len]
                     && ((dp->d_name[len] != '.')
                         || !isdigit(dp->d_name[len+1])))) {
diff --git a/logcat/logcatd.rc b/logcat/logcatd.rc
index 33d39ac..cf0e0d2 100644
--- a/logcat/logcatd.rc
+++ b/logcat/logcatd.rc
@@ -11,3 +11,4 @@
     # logd for write to /data/misc/logd, log group for read from log daemon
     user logd
     group log
+    writepid /dev/cpuset/system-background/tasks
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 610a6ec..153a3fd 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -75,6 +75,12 @@
 }
 
 TEST(logcat, year) {
+
+    if (android_log_timestamp() == 'm') {
+        fprintf(stderr, "Skipping test, logd is monotonic time\n");
+        return;
+    }
+
     FILE *fp;
 
     char needle[32];
@@ -108,7 +114,44 @@
     ASSERT_EQ(3, count);
 }
 
+// Return a pointer to each null terminated -v long time field.
+char *fgetLongTime(char *buffer, size_t buflen, FILE *fp) {
+    while (fgets(buffer, buflen, fp)) {
+        char *cp = buffer;
+        if (*cp != '[') {
+            continue;
+        }
+        while (*++cp == ' ') {
+            ;
+        }
+        char *ep = cp;
+        while (isdigit(*ep)) {
+            ++ep;
+        }
+        if ((*ep != '-') && (*ep != '.')) {
+           continue;
+        }
+        // Find PID field
+        while (((ep = strchr(ep, ':'))) && (*++ep != ' ')) {
+            ;
+        }
+        if (!ep) {
+            continue;
+        }
+        ep -= 7;
+        *ep = '\0';
+        return cp;
+    }
+    return NULL;
+}
+
 TEST(logcat, tz) {
+
+    if (android_log_timestamp() == 'm') {
+        fprintf(stderr, "Skipping test, logd is monotonic time\n");
+        return;
+    }
+
     FILE *fp;
 
     ASSERT_TRUE(NULL != (fp = popen(
@@ -119,11 +162,8 @@
 
     int count = 0;
 
-    while (fgets(buffer, sizeof(buffer), fp)) {
-        if ((buffer[0] == '[') && (buffer[1] == ' ')
-         && isdigit(buffer[2]) && isdigit(buffer[3])
-         && (buffer[4] == '-')
-         && (strstr(buffer, " -0700 ") || strstr(buffer, " -0800 "))) {
+    while (fgetLongTime(buffer, sizeof(buffer), fp)) {
+        if (strstr(buffer, " -0700") || strstr(buffer, " -0800")) {
             ++count;
         }
     }
@@ -144,11 +184,8 @@
 
     int count = 0;
 
-    while (fgets(buffer, sizeof(buffer), fp)) {
-        if ((buffer[0] == '[') && (buffer[1] == ' ')
-         && isdigit(buffer[2]) && isdigit(buffer[3])
-         && (buffer[4] == '-')
-         && (strstr(buffer, " -0700 ") || strstr(buffer, " -0800 "))) {
+    while (fgetLongTime(buffer, sizeof(buffer), fp)) {
+        if (strstr(buffer, " -0700") || strstr(buffer, " -0800")) {
             ++count;
         }
     }
@@ -169,12 +206,8 @@
 
     int count = 0;
 
-    while (fgets(buffer, sizeof(buffer), fp)) {
-        if ((buffer[0] == '[') && (buffer[1] == ' ')
-         && isdigit(buffer[2]) && isdigit(buffer[3])
-         && (buffer[4] == '-')) {
-            ++count;
-        }
+    while (fgetLongTime(buffer, sizeof(buffer), fp)) {
+        ++count;
     }
 
     pclose(fp);
@@ -193,12 +226,8 @@
 
     int count = 0;
 
-    while (fgets(buffer, sizeof(buffer), fp)) {
-        if ((buffer[0] == '[') && (buffer[1] == ' ')
-         && isdigit(buffer[2]) && isdigit(buffer[3])
-         && (buffer[4] == '-')) {
-            ++count;
-        }
+    while (fgetLongTime(buffer, sizeof(buffer), fp)) {
+        ++count;
     }
 
     pclose(fp);
@@ -217,12 +246,8 @@
 
     int count = 0;
 
-    while (fgets(buffer, sizeof(buffer), fp)) {
-        if ((buffer[0] == '[') && (buffer[1] == ' ')
-         && isdigit(buffer[2]) && isdigit(buffer[3])
-         && (buffer[4] == '-')) {
-            ++count;
-        }
+    while (fgetLongTime(buffer, sizeof(buffer), fp)) {
+        ++count;
     }
 
     pclose(fp);
@@ -241,12 +266,8 @@
 
     int count = 0;
 
-    while (fgets(buffer, sizeof(buffer), fp)) {
-        if ((buffer[0] == '[') && (buffer[1] == ' ')
-         && isdigit(buffer[2]) && isdigit(buffer[3])
-         && (buffer[4] == '-')) {
-            ++count;
-        }
+    while (fgetLongTime(buffer, sizeof(buffer), fp)) {
+        ++count;
     }
 
     pclose(fp);
@@ -263,21 +284,15 @@
     char *last_timestamp = NULL;
     char *first_timestamp = NULL;
     int count = 0;
-    const unsigned int time_length = 18;
-    const unsigned int time_offset = 2;
 
-    while (fgets(buffer, sizeof(buffer), fp)) {
-        if ((buffer[0] == '[') && (buffer[1] == ' ')
-         && isdigit(buffer[time_offset]) && isdigit(buffer[time_offset + 1])
-         && (buffer[time_offset + 2] == '-')) {
-            ++count;
-            buffer[time_length + time_offset] = '\0';
-            if (!first_timestamp) {
-                first_timestamp = strdup(buffer + time_offset);
-            }
-            free(last_timestamp);
-            last_timestamp = strdup(buffer + time_offset);
+    char *cp;
+    while ((cp = fgetLongTime(buffer, sizeof(buffer), fp))) {
+        ++count;
+        if (!first_timestamp) {
+            first_timestamp = strdup(cp);
         }
+        free(last_timestamp);
+        last_timestamp = strdup(cp);
     }
     pclose(fp);
 
@@ -292,28 +307,24 @@
     int second_count = 0;
     int last_timestamp_count = -1;
 
-    while (fgets(buffer, sizeof(buffer), fp)) {
-        if ((buffer[0] == '[') && (buffer[1] == ' ')
-         && isdigit(buffer[time_offset]) && isdigit(buffer[time_offset + 1])
-         && (buffer[time_offset + 2] == '-')) {
-            ++second_count;
-            buffer[time_length + time_offset] = '\0';
-            if (first_timestamp) {
-                // we can get a transitory *extremely* rare failure if hidden
-                // underneath the time is *exactly* XX-XX XX:XX:XX.XXX000000
-                EXPECT_STREQ(buffer + time_offset, first_timestamp);
-                free(first_timestamp);
-                first_timestamp = NULL;
-            }
-            if (!strcmp(buffer + time_offset, last_timestamp)) {
-                last_timestamp_count = second_count;
-            }
+    while ((cp = fgetLongTime(buffer, sizeof(buffer), fp))) {
+        ++second_count;
+        if (first_timestamp) {
+            // we can get a transitory *extremely* rare failure if hidden
+            // underneath the time is *exactly* XX-XX XX:XX:XX.XXX000000
+            EXPECT_STREQ(cp, first_timestamp);
+            free(first_timestamp);
+            first_timestamp = NULL;
+        }
+        if (!strcmp(cp, last_timestamp)) {
+            last_timestamp_count = second_count;
         }
     }
     pclose(fp);
 
     free(last_timestamp);
     last_timestamp = NULL;
+    free(first_timestamp);
 
     EXPECT_TRUE(first_timestamp == NULL);
     EXPECT_LE(count, second_count);
@@ -601,6 +612,9 @@
                 }
             }
             pclose(fp);
+            if ((count != 7) && (count != 8)) {
+                fprintf(stderr, "count=%d\n", count);
+            }
             EXPECT_TRUE(count == 7 || count == 8);
         }
     }
diff --git a/logd/Android.mk b/logd/Android.mk
index c00061b..feca8d5 100644
--- a/logd/Android.mk
+++ b/logd/Android.mk
@@ -27,7 +27,8 @@
     libsysutils \
     liblog \
     libcutils \
-    libbase
+    libbase \
+    libpackagelistparser
 
 # This is what we want to do:
 #  event_logtags = $(shell \
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index 2f7cbfb..143fb04 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -123,17 +123,19 @@
             && (*cp == ':')) {
         memcpy(timeptr + sizeof(audit_str) - 1, "0.0", 3);
         memmove(timeptr + sizeof(audit_str) - 1 + 3, cp, strlen(cp) + 1);
-        //
-        // We are either in 1970ish (MONOTONIC) or 2015+ish (REALTIME) so to
-        // differentiate without prejudice, we use 1980 to delineate, earlier
-        // is monotonic, later is real.
-        //
-#       define EPOCH_PLUS_10_YEARS (10 * 1461 / 4 * 24 * 60 * 60)
-        if (now.tv_sec < EPOCH_PLUS_10_YEARS) {
-            LogKlog::convertMonotonicToReal(now);
+        if (!isMonotonic()) {
+            if (android::isMonotonic(now)) {
+                LogKlog::convertMonotonicToReal(now);
+            }
+        } else {
+            if (!android::isMonotonic(now)) {
+                LogKlog::convertRealToMonotonic(now);
+            }
         }
+    } else if (isMonotonic()) {
+        now = log_time(CLOCK_MONOTONIC);
     } else {
-        now.strptime("", ""); // side effect of setting CLOCK_REALTIME
+        now = log_time(CLOCK_REALTIME);
     }
 
     static const char pid_str[] = " pid=";
diff --git a/logd/LogAudit.h b/logd/LogAudit.h
index 2342822..8a82630 100644
--- a/logd/LogAudit.h
+++ b/logd/LogAudit.h
@@ -29,6 +29,7 @@
 public:
     LogAudit(LogBuffer *buf, LogReader *reader, int fdDmesg);
     int log(char *buf, size_t len);
+    bool isMonotonic() { return logbuf->isMonotonic(); }
 
 protected:
     virtual bool onDataAvailable(SocketClient *cli);
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 1de8e64..fd5c066 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -28,6 +28,7 @@
 #include <log/logger.h>
 
 #include "LogBuffer.h"
+#include "LogKlog.h"
 #include "LogReader.h"
 
 // Default
@@ -126,9 +127,48 @@
             setSize(i, LOG_BUFFER_MIN_SIZE);
         }
     }
+    bool lastMonotonic = monotonic;
+    monotonic = android_log_timestamp() == 'm';
+    if (lastMonotonic == monotonic) {
+        return;
+    }
+
+    //
+    // Fixup all timestamps, may not be 100% accurate, but better than
+    // throwing what we have away when we get 'surprised' by a change.
+    // In-place element fixup so no need to check reader-lock. Entries
+    // should already be in timestamp order, but we could end up with a
+    // few out-of-order entries if new monotonics come in before we
+    // are notified of the reinit change in status. A Typical example would
+    // be:
+    //  --------- beginning of system
+    //      10.494082   184   201 D Cryptfs : Just triggered post_fs_data
+    //  --------- beginning of kernel
+    //       0.000000     0     0 I         : Initializing cgroup subsys cpuacct
+    // as the act of mounting /data would trigger persist.logd.timestamp to
+    // be corrected. 1/30 corner case YMMV.
+    //
+    pthread_mutex_lock(&mLogElementsLock);
+    LogBufferElementCollection::iterator it = mLogElements.begin();
+    while((it != mLogElements.end())) {
+        LogBufferElement *e = *it;
+        if (monotonic) {
+            if (!android::isMonotonic(e->mRealTime)) {
+                LogKlog::convertRealToMonotonic(e->mRealTime);
+            }
+        } else {
+            if (android::isMonotonic(e->mRealTime)) {
+                LogKlog::convertMonotonicToReal(e->mRealTime);
+            }
+        }
+        ++it;
+    }
+    pthread_mutex_unlock(&mLogElementsLock);
 }
 
-LogBuffer::LogBuffer(LastLogTimes *times) : mTimes(*times) {
+LogBuffer::LogBuffer(LastLogTimes *times):
+        monotonic(android_log_timestamp() == 'm'),
+        mTimes(*times) {
     pthread_mutex_init(&mLogElementsLock, NULL);
 
     init();
@@ -436,7 +476,10 @@
                     worst_sizes = sorted[0]->getSizes();
                     // Calculate threshold as 12.5% of available storage
                     size_t threshold = log_buffer_size(id) / 8;
-                    if (worst_sizes > threshold) {
+                    if ((worst_sizes > threshold)
+                        // Allow time horizon to extend roughly tenfold, assume
+                        // average entry length is 100 characters.
+                            && (worst_sizes > (10 * sorted[0]->getDropped()))) {
                         worst = sorted[0]->getKey();
                         second_worst_sizes = sorted[1]->getSizes();
                         if (second_worst_sizes < threshold) {
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index 7ed92e9..c1fec73 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -32,6 +32,21 @@
 #include "LogStatistics.h"
 #include "LogWhiteBlackList.h"
 
+//
+// We are either in 1970ish (MONOTONIC) or 2015+ish (REALTIME) so to
+// differentiate without prejudice, we use 1980 to delineate, earlier
+// is monotonic, later is real.
+//
+namespace android {
+
+static bool isMonotonic(const log_time &mono) {
+    static const uint32_t EPOCH_PLUS_10_YEARS = 10 * 1461 / 4 * 24 * 60 * 60;
+
+    return mono.tv_sec < EPOCH_PLUS_10_YEARS;
+}
+
+}
+
 typedef std::list<LogBufferElement *> LogBufferElementCollection;
 
 class LogBuffer {
@@ -49,11 +64,14 @@
 
     unsigned long mMaxSize[LOG_ID_MAX];
 
+    bool monotonic;
+
 public:
     LastLogTimes &mTimes;
 
     LogBuffer(LastLogTimes *times);
     void init();
+    bool isMonotonic() { return monotonic; }
 
     int log(log_id_t log_id, log_time realtime,
             uid_t uid, pid_t pid, pid_t tid,
diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h
index 30e43c6..09987ea 100644
--- a/logd/LogBufferElement.h
+++ b/logd/LogBufferElement.h
@@ -34,6 +34,9 @@
 #define EXPIRE_RATELIMIT 10      // maximum rate in seconds to report expiration
 
 class LogBufferElement {
+
+    friend LogBuffer;
+
     const log_id_t mLogId;
     const uid_t mUid;
     const pid_t mPid;
@@ -44,7 +47,7 @@
         unsigned short mDropped;      // mMsg == NULL
     };
     const uint64_t mSequence;
-    const log_time mRealTime;
+    log_time mRealTime;
     static atomic_int_fast64_t sequence;
 
     // assumption: mMsg == NULL
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index d28161e..da5e78d 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -330,6 +330,10 @@
         }
         *buf = cp;
 
+        if (isMonotonic()) {
+            return;
+        }
+
         const char *b;
         if (((b = strnstr(cp, len, suspendStr)))
                 && ((size_t)((b += sizeof(suspendStr) - 1) - cp) < len)) {
@@ -376,7 +380,11 @@
 
         convertMonotonicToReal(now);
     } else {
-        now = log_time(CLOCK_REALTIME);
+        if (isMonotonic()) {
+            now = log_time(CLOCK_MONOTONIC);
+        } else {
+            now = log_time(CLOCK_REALTIME);
+        }
     }
 }
 
diff --git a/logd/LogKlog.h b/logd/LogKlog.h
index 469affd..3c8cc87 100644
--- a/logd/LogKlog.h
+++ b/logd/LogKlog.h
@@ -43,7 +43,9 @@
     int log(const char *buf, size_t len);
     void synchronize(const char *buf, size_t len);
 
+    bool isMonotonic() { return logbuf->isMonotonic(); }
     static void convertMonotonicToReal(log_time &real) { real += correction; }
+    static void convertRealToMonotonic(log_time &real) { real -= correction; }
 
 protected:
     void sniffTime(log_time &now, const char **buf, size_t len, bool reverse);
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
index 3833843..06135dd 100644
--- a/logd/LogReader.cpp
+++ b/logd/LogReader.cpp
@@ -114,15 +114,17 @@
             log_time &start;
             uint64_t &sequence;
             uint64_t last;
+            bool isMonotonic;
 
         public:
-            LogFindStart(unsigned logMask, pid_t pid, log_time &start, uint64_t &sequence) :
+            LogFindStart(unsigned logMask, pid_t pid, log_time &start, uint64_t &sequence, bool isMonotonic) :
                     mPid(pid),
                     mLogMask(logMask),
                     startTimeSet(false),
                     start(start),
                     sequence(sequence),
-                    last(sequence) {
+                    last(sequence),
+                    isMonotonic(isMonotonic) {
             }
 
             static int callback(const LogBufferElement *element, void *obj) {
@@ -133,20 +135,24 @@
                         me->sequence = element->getSequence();
                         me->startTimeSet = true;
                         return -1;
-                    } else {
+                    } else if (!me->isMonotonic ||
+                            android::isMonotonic(element->getRealTime())) {
                         if (me->start < element->getRealTime()) {
                             me->sequence = me->last;
                             me->startTimeSet = true;
                             return -1;
                         }
                         me->last = element->getSequence();
+                    } else {
+                        me->last = element->getSequence();
                     }
                 }
                 return false;
             }
 
             bool found() { return startTimeSet; }
-        } logFindStart(logMask, pid, start, sequence);
+        } logFindStart(logMask, pid, start, sequence,
+                       logbuf().isMonotonic() && android::isMonotonic(start));
 
         logbuf().flushTo(cli, sequence, FlushCommand::hasReadLogs(cli),
                          logFindStart.callback, &logFindStart);
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index fdb2576..416edd8 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -190,13 +190,13 @@
     return formatLine(android::base::StringPrintf(
                           name.c_str(), android_log_id_to_name(id)),
                       std::string("Size"),
-                      std::string(isprune ? "Pruned" : ""))
+                      std::string(isprune ? "+/-  Pruned" : ""))
          + formatLine(std::string("UID   PACKAGE"),
                       std::string("BYTES"),
                       std::string(isprune ? "NUM" : ""));
 }
 
-std::string UidEntry::format(const LogStatistics &stat, log_id_t /* id */) const {
+std::string UidEntry::format(const LogStatistics &stat, log_id_t id) const {
     uid_t uid = getKey();
     std::string name = android::base::StringPrintf("%u", uid);
     const char *nameTmp = stat.uidToName(uid);
@@ -210,9 +210,59 @@
     std::string size = android::base::StringPrintf("%zu", getSizes());
 
     std::string pruned = "";
-    size_t dropped = getDropped();
-    if (dropped) {
-        pruned = android::base::StringPrintf("%zu", dropped);
+    if (worstUidEnabledForLogid(id)) {
+        size_t totalDropped = 0;
+        for (LogStatistics::uidTable_t::const_iterator it = stat.uidTable[id].begin();
+                it != stat.uidTable[id].end(); ++it) {
+            totalDropped += it->second.getDropped();
+        }
+        size_t sizes = stat.sizes(id);
+        size_t totalSize = stat.sizesTotal(id);
+        size_t totalElements = stat.elementsTotal(id);
+        float totalVirtualSize = (float)sizes + (float)totalDropped * totalSize
+                                / totalElements;
+        size_t entrySize = getSizes();
+        float virtualEntrySize = entrySize;
+        int realPermille = virtualEntrySize * 1000.0 / sizes;
+        size_t dropped = getDropped();
+        if (dropped) {
+            pruned = android::base::StringPrintf("%zu", dropped);
+            virtualEntrySize += (float)dropped * totalSize / totalElements;
+        }
+        int virtualPermille = virtualEntrySize * 1000.0 / totalVirtualSize;
+        int permille = (realPermille - virtualPermille) * 1000L
+                     / (virtualPermille ?: 1);
+        if ((permille < -1) || (1 < permille)) {
+            std::string change;
+            const char *units = "%";
+            const char *prefix = (permille > 0) ? "+" : "";
+
+            if (permille > 999) {
+                permille = (permille + 1000) / 100; // Now tenths fold
+                units = "X";
+                prefix = "";
+            }
+            if ((-99 < permille) && (permille < 99)) {
+                change = android::base::StringPrintf("%s%d.%u%s",
+                    prefix,
+                    permille / 10,
+                    ((permille < 0) ? (-permille % 10) : (permille % 10)),
+                    units);
+            } else {
+                change = android::base::StringPrintf("%s%d%s",
+                    prefix,
+                    (permille + 5) / 10, units);
+            }
+            ssize_t spaces = EntryBaseConstants::pruned_len
+                           - 2 - pruned.length() - change.length();
+            if ((spaces <= 0) && pruned.length()) {
+                spaces = 1;
+            }
+            if (spaces > 0) {
+                change += android::base::StringPrintf("%*s", (int)spaces, "");
+            }
+            pruned = change + pruned;
+        }
     }
 
     return formatLine(name, size, pruned);
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index 6943820..41f8b95 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -372,6 +372,8 @@
 
 // Log Statistics
 class LogStatistics {
+    friend UidEntry;
+
     size_t mSizes[LOG_ID_MAX];
     size_t mElements[LOG_ID_MAX];
     size_t mDroppedElements[LOG_ID_MAX];
diff --git a/logd/LogWhiteBlackList.cpp b/logd/LogWhiteBlackList.cpp
index ad005ec..c71beb5 100644
--- a/logd/LogWhiteBlackList.cpp
+++ b/logd/LogWhiteBlackList.cpp
@@ -17,6 +17,7 @@
 #include <ctype.h>
 
 #include <base/stringprintf.h>
+#include <cutils/properties.h>
 
 #include "LogWhiteBlackList.h"
 
@@ -49,7 +50,8 @@
     return std::string("/");
 }
 
-PruneList::PruneList() : mWorstUidEnabled(true) {
+PruneList::PruneList() {
+    init(NULL);
 }
 
 PruneList::~PruneList() {
@@ -72,13 +74,44 @@
         it = mNaughty.erase(it);
     }
 
-    if (!str) {
-        return 0;
+    static const char _default[] = "default";
+    // default here means take ro.logd.filter, persist.logd.filter then
+    // internal default in that order.
+    if (str && !strcmp(str, _default)) {
+        str = NULL;
+    }
+    static const char _disable[] = "disable";
+    if (str && !strcmp(str, _disable)) {
+        str = "";
+    }
+
+    std::string filter;
+
+    if (str) {
+        filter = str;
+    } else {
+        char property[PROPERTY_VALUE_MAX];
+        property_get("ro.logd.filter", property, _default);
+        filter = property;
+        property_get("persist.logd.filter", property, filter.c_str());
+        // default here means take ro.logd.filter
+        if (strcmp(property, _default)) {
+            filter = property;
+        }
+    }
+
+    // default here means take internal default.
+    if (filter == _default) {
+        // See README.property for description of filter format
+        filter = "~!";
+    }
+    if (filter == _disable) {
+        filter = "";
     }
 
     mWorstUidEnabled = false;
 
-    for(; *str; ++str) {
+    for(str = filter.c_str(); *str; ++str) {
         if (isspace(*str)) {
             continue;
         }
diff --git a/logd/README.property b/logd/README.property
index a472efd..e4b23a9 100644
--- a/logd/README.property
+++ b/logd/README.property
@@ -12,16 +12,28 @@
                                          default false
 persist.logd.logpersistd    string       Enable logpersist daemon, "logcatd"
                                          turns on logcat -f in logd context
-persist.logd.size          number 256K   default size of the buffer for all
-                                         log ids at initial startup, at runtime
-                                         use: logcat -b all -G <value>
+persist.logd.size          number 256K   Global default size of the buffer for
+                                         all log ids at initial startup, at
+                                         runtime use: logcat -b all -G <value>
 persist.logd.size.main     number 256K   Size of the buffer for the main log
 persist.logd.size.system   number 256K   Size of the buffer for the system log
 persist.logd.size.radio    number 256K   Size of the buffer for the radio log
 persist.logd.size.event    number 256K   Size of the buffer for the event log
 persist.logd.size.crash    number 256K   Size of the buffer for the crash log
+persist.logd.filter         string       Pruning filter to optimize content,
+                                         default is ro.logd.filter or
+                                         "~!" which means to prune the oldest
+                                         entries of chattiest UID. At runtime
+                                         use: logcat -P "<string>"
+persist.logd.timestamp      string       The recording timestamp source. Default
+                                         is ro.logd.timestamp. "m[onotonic]" is
+                                         the only supported key character,
+                                         otherwise assumes realtime.
 
 NB:
-- number support multipliers (K or M) for convenience. Range is limited
-  to between 64K and 256M for log buffer sizes. Individual logs override the
-  global default.
+- Number support multipliers (K or M) for convenience. Range is limited
+  to between 64K and 256M for log buffer sizes. Individual log buffer ids
+  such as main, system, ... override global default.
+- Pruning filter is of form of a space-separated list of [~][UID][/PID]
+  references, where '~' prefix means to blacklist otherwise whitelist. For
+  blacklisting, UID may be a '!' to instead reference the chattiest client.
diff --git a/logd/logd.rc b/logd/logd.rc
index da6a0bc..ecd2f0a 100644
--- a/logd/logd.rc
+++ b/logd/logd.rc
@@ -4,7 +4,9 @@
     socket logdr seqpacket 0666 logd logd
     socket logdw dgram 0222 logd logd
     group root system
+    writepid /dev/cpuset/system-background/tasks
 
 service logd-reinit /system/bin/logd --reinit
     oneshot
     disabled
+    writepid /dev/cpuset/system-background/tasks
diff --git a/logd/main.cpp b/logd/main.cpp
index 60262e9..ad577d2 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -33,12 +33,14 @@
 #include <syslog.h>
 #include <unistd.h>
 
+#include <cstdbool>
 #include <memory>
 
 #include <cutils/properties.h>
 #include <cutils/sched_policy.h>
 #include <cutils/sockets.h>
 #include <log/event_tag_map.h>
+#include <packagelistparser/packagelistparser.h>
 #include <private/android_filesystem_config.h>
 #include <utils/threads.h>
 
@@ -166,6 +168,19 @@
 static bool reinit_running = false;
 static LogBuffer *logBuf = NULL;
 
+static bool package_list_parser_cb(pkg_info *info, void * /* userdata */) {
+
+    bool rc = true;
+    if (info->uid == uid) {
+        name = strdup(info->name);
+        // false to stop processing
+        rc = false;
+    }
+
+    packagelist_free(info);
+    return rc;
+}
+
 static void *reinit_thread_start(void * /*obj*/) {
     prctl(PR_SET_NAME, "logd.daemon");
     set_sched_policy(0, SP_BACKGROUND);
@@ -180,31 +195,8 @@
         if (uid) {
             name = NULL;
 
-            FILE *fp = fopen("/data/system/packages.list", "r");
-            if (fp) {
-                // This simple parser is sensitive to format changes in
-                // frameworks/base/services/core/java/com/android/server/pm/Settings.java
-                // A dependency note has been added to that file to correct
-                // this parser.
+            packagelist_parse(package_list_parser_cb, NULL);
 
-                char *buffer = NULL;
-                size_t len;
-                while (getline(&buffer, &len, fp) > 0) {
-                    char *userId = strchr(buffer, ' ');
-                    if (!userId) {
-                        continue;
-                    }
-                    *userId = '\0';
-                    unsigned long value = strtoul(userId + 1, NULL, 10);
-                    if (value != uid) {
-                        continue;
-                    }
-                    name = strdup(buffer);
-                    break;
-                }
-                free(buffer);
-                fclose(fp);
-            }
             uid = 0;
             sem_post(&uidName);
             continue;
@@ -220,6 +212,7 @@
         // Anything that reads persist.<property>
         if (logBuf) {
             logBuf->init();
+            logBuf->initPrune(NULL);
         }
     }
 
@@ -305,7 +298,7 @@
     }
     buf[--len] = '\0';
 
-    if (kl) {
+    if (kl && kl->isMonotonic()) {
         kl->synchronize(buf.get(), len);
     }
 
diff --git a/metricsd/Android.mk b/metricsd/Android.mk
index 8dbaad9..291c983 100644
--- a/metricsd/Android.mk
+++ b/metricsd/Android.mk
@@ -27,6 +27,7 @@
 
 metrics_daemon_common := \
   collectors/averaged_statistics_collector.cc \
+  collectors/cpu_usage_collector.cc \
   collectors/disk_usage_collector.cc \
   metrics_daemon.cc \
   persistent_integer.cc \
@@ -41,6 +42,7 @@
 
 metrics_tests_sources := \
   collectors/averaged_statistics_collector_test.cc \
+  collectors/cpu_usage_collector_test.cc \
   metrics_daemon_test.cc \
   metrics_library_test.cc \
   persistent_integer_test.cc \
@@ -64,11 +66,11 @@
   -fvisibility=default
 metrics_includes := external/gtest/include \
   $(LOCAL_PATH)/include
-libmetrics_shared_libraries := libchrome libchromeos
+libmetrics_shared_libraries := libchrome libbrillo
 metrics_daemon_shared_libraries := $(libmetrics_shared_libraries) \
+  libbrillo-http \
+  libbrillo-dbus \
   libchrome-dbus \
-  libchromeos-http \
-  libchromeos-dbus \
   libdbus \
   libmetrics \
   libprotobuf-cpp-lite \
@@ -120,8 +122,7 @@
 # ========================================================
 include $(CLEAR_VARS)
 LOCAL_MODULE := metrics_daemon
-LOCAL_C_INCLUDES := $(metrics_includes) \
-  external/libchromeos
+LOCAL_C_INCLUDES := $(metrics_includes)
 LOCAL_CFLAGS := $(metrics_CFLAGS)
 LOCAL_CPP_EXTENSION := $(metrics_cpp_extension)
 LOCAL_CPPFLAGS := $(metrics_CPPFLAGS)
diff --git a/metricsd/collectors/cpu_usage_collector.cc b/metricsd/collectors/cpu_usage_collector.cc
new file mode 100644
index 0000000..05934b4
--- /dev/null
+++ b/metricsd/collectors/cpu_usage_collector.cc
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "collectors/cpu_usage_collector.h"
+
+#include <base/bind.h>
+#include <base/files/file_path.h>
+#include <base/files/file_util.h>
+#include <base/message_loop/message_loop.h>
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/string_split.h>
+#include <base/strings/string_util.h>
+#include <base/sys_info.h>
+
+#include "metrics/metrics_library.h"
+
+namespace {
+
+const char kCpuUsagePercent[] = "Platform.CpuUsage.Percent";
+const char kMetricsProcStatFileName[] = "/proc/stat";
+const int kMetricsProcStatFirstLineItemsCount = 11;
+
+// Collect every minute.
+const int kCollectionIntervalSecs = 60;
+
+}  // namespace
+
+using base::TimeDelta;
+
+CpuUsageCollector::CpuUsageCollector(MetricsLibraryInterface* metrics_library) {
+  CHECK(metrics_library);
+  metrics_lib_ = metrics_library;
+  collect_interval_ = TimeDelta::FromSeconds(kCollectionIntervalSecs);
+}
+
+void CpuUsageCollector::Init() {
+  num_cpu_ = base::SysInfo::NumberOfProcessors();
+
+  // Get ticks per second (HZ) on this system.
+  // Sysconf cannot fail, so no sanity checks are needed.
+  ticks_per_second_ = sysconf(_SC_CLK_TCK);
+  CHECK_GT(ticks_per_second_, uint64_t(0))
+      << "Number of ticks per seconds should be positive.";
+
+  latest_cpu_use_ = GetCumulativeCpuUse();
+}
+
+void CpuUsageCollector::CollectCallback() {
+  Collect();
+  Schedule();
+}
+
+void CpuUsageCollector::Schedule() {
+  base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
+      base::Bind(&CpuUsageCollector::CollectCallback, base::Unretained(this)),
+      collect_interval_);
+}
+
+void CpuUsageCollector::Collect() {
+  TimeDelta cpu_use = GetCumulativeCpuUse();
+  TimeDelta diff_per_cpu = (cpu_use - latest_cpu_use_) / num_cpu_;
+  latest_cpu_use_ = cpu_use;
+
+  // Report the cpu usage as a percentage of the total cpu usage possible.
+  int percent_use = diff_per_cpu.InMilliseconds() * 100 /
+      (kCollectionIntervalSecs * 1000);
+
+  metrics_lib_->SendEnumToUMA(kCpuUsagePercent, percent_use, 101);
+}
+
+TimeDelta CpuUsageCollector::GetCumulativeCpuUse() {
+  base::FilePath proc_stat_path(kMetricsProcStatFileName);
+  std::string proc_stat_string;
+  if (!base::ReadFileToString(proc_stat_path, &proc_stat_string)) {
+    LOG(WARNING) << "cannot open " << kMetricsProcStatFileName;
+    return TimeDelta();
+  }
+
+  uint64_t user_ticks, user_nice_ticks, system_ticks;
+  if (!ParseProcStat(proc_stat_string, &user_ticks, &user_nice_ticks,
+                     &system_ticks)) {
+    return TimeDelta();
+  }
+
+  uint64_t total = user_ticks + user_nice_ticks + system_ticks;
+  return TimeDelta::FromMicroseconds(
+      total * 1000 * 1000 / ticks_per_second_);
+}
+
+bool CpuUsageCollector::ParseProcStat(const std::string& stat_content,
+                                      uint64_t *user_ticks,
+                                      uint64_t *user_nice_ticks,
+                                      uint64_t *system_ticks) {
+  std::vector<std::string> proc_stat_lines;
+  base::SplitString(stat_content, '\n', &proc_stat_lines);
+  if (proc_stat_lines.empty()) {
+    LOG(WARNING) << "No lines found in " << kMetricsProcStatFileName;
+    return false;
+  }
+  std::vector<std::string> proc_stat_totals;
+  base::SplitStringAlongWhitespace(proc_stat_lines[0], &proc_stat_totals);
+
+  if (proc_stat_totals.size() != kMetricsProcStatFirstLineItemsCount ||
+      proc_stat_totals[0] != "cpu" ||
+      !base::StringToUint64(proc_stat_totals[1], user_ticks) ||
+      !base::StringToUint64(proc_stat_totals[2], user_nice_ticks) ||
+      !base::StringToUint64(proc_stat_totals[3], system_ticks)) {
+    LOG(WARNING) << "cannot parse first line: " << proc_stat_lines[0];
+    return false;
+  }
+  return true;
+}
diff --git a/metricsd/collectors/cpu_usage_collector.h b/metricsd/collectors/cpu_usage_collector.h
new file mode 100644
index 0000000..f81dfcb
--- /dev/null
+++ b/metricsd/collectors/cpu_usage_collector.h
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+#ifndef METRICSD_COLLECTORS_CPU_USAGE_COLLECTOR_H_
+#define METRICSD_COLLECTORS_CPU_USAGE_COLLECTOR_H_
+
+#include <base/time/time.h>
+
+#include "metrics/metrics_library.h"
+
+class CpuUsageCollector {
+ public:
+  CpuUsageCollector(MetricsLibraryInterface* metrics_library);
+
+  // Initialize this collector's state.
+  void Init();
+
+  // Schedule a collection interval.
+  void Schedule();
+
+  // Callback called at the end of the collection interval.
+  void CollectCallback();
+
+  // Measure the cpu use and report it.
+  void Collect();
+
+  // Gets the current cumulated Cpu usage.
+  base::TimeDelta GetCumulativeCpuUse();
+
+ private:
+  FRIEND_TEST(CpuUsageTest, ParseProcStat);
+  bool ParseProcStat(const std::string& stat_content,
+                     uint64_t *user_ticks,
+                     uint64_t *user_nice_ticks,
+                     uint64_t *system_ticks);
+
+  int num_cpu_;
+  uint32_t ticks_per_second_;
+
+  base::TimeDelta collect_interval_;
+  base::TimeDelta latest_cpu_use_;
+
+  MetricsLibraryInterface* metrics_lib_;
+};
+
+#endif  // METRICSD_COLLECTORS_CPU_USAGE_COLLECTOR_H_
diff --git a/metricsd/collectors/cpu_usage_collector_test.cc b/metricsd/collectors/cpu_usage_collector_test.cc
new file mode 100644
index 0000000..ee5c92b
--- /dev/null
+++ b/metricsd/collectors/cpu_usage_collector_test.cc
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "collectors/cpu_usage_collector.h"
+#include "metrics/metrics_library_mock.h"
+
+
+TEST(CpuUsageTest, ParseProcStat) {
+  MetricsLibraryMock metrics_lib_mock;
+  CpuUsageCollector collector(&metrics_lib_mock);
+  std::vector<std::string> invalid_contents = {
+    "",
+    // First line does not start with cpu.
+    "spu  17191 11 36579 151118 289 0 2 0 0 0\n"
+    "cpu0 1564 2 866 48650 68 0 2 0 0 0\n"
+    "cpu1 14299 0 35116 1844 81 0 0 0 0 0\n",
+    // One of the field is not a number.
+    "cpu  a17191 11 36579 151118 289 0 2 0 0 0",
+    // To many numbers in the first line.
+    "cpu  17191 11 36579 151118 289 0 2 0 0 0 102"
+  };
+
+  uint64_t user, nice, system;
+  for (int i = 0; i < invalid_contents.size(); i++) {
+    ASSERT_FALSE(collector.ParseProcStat(invalid_contents[i], &user, &nice,
+                                         &system));
+  }
+
+  ASSERT_TRUE(collector.ParseProcStat(
+      std::string("cpu  17191 11 36579 151118 289 0 2 0 0 0"),
+      &user, &nice, &system));
+  ASSERT_EQ(17191, user);
+  ASSERT_EQ(11, nice);
+  ASSERT_EQ(36579, system);
+}
diff --git a/metricsd/constants.h b/metricsd/constants.h
index 7e1e116..3a7569b 100644
--- a/metricsd/constants.h
+++ b/metricsd/constants.h
@@ -30,6 +30,10 @@
 // Build time properties name.
 static const char kProductId[] = "product_id";
 static const char kProductVersion[] = "product_version";
+
+// Weave configuration.
+static const char kWeaveConfigurationFile[] = "/system/etc/weaved/weaved.conf";
+static const char kModelManifestId[] = "model_id";
 }  // namespace metrics
 
 #endif  // METRICS_CONSTANTS_H_
diff --git a/metricsd/libmetrics.gypi b/metricsd/libmetrics.gypi
index 5b90a55..3c8fc7c 100644
--- a/metricsd/libmetrics.gypi
+++ b/metricsd/libmetrics.gypi
@@ -2,8 +2,8 @@
   'target_defaults': {
     'variables': {
       'deps': [
+        'libbrillo-<(libbase_ver)',
         'libchrome-<(libbase_ver)',
-        'libchromeos-<(libbase_ver)',
       ]
     },
     'cflags_cc': [
diff --git a/metricsd/metrics.gyp b/metricsd/metrics.gyp
index 276ec78..c9c02d7 100644
--- a/metricsd/metrics.gyp
+++ b/metricsd/metrics.gyp
@@ -3,8 +3,8 @@
     'variables': {
       'deps': [
         'dbus-1',
+        'libbrillo-<(libbase_ver)',
         'libchrome-<(libbase_ver)',
-        'libchromeos-<(libbase_ver)',
       ]
     },
     'cflags_cc': [
diff --git a/metricsd/metrics_daemon.cc b/metricsd/metrics_daemon.cc
index ed786e1..b606fd0 100644
--- a/metricsd/metrics_daemon.cc
+++ b/metricsd/metrics_daemon.cc
@@ -71,10 +71,8 @@
 
 const int kMetricMeminfoInterval = 30;    // seconds
 
-const char kMetricsProcStatFileName[] = "/proc/stat";
 const char kMeminfoFileName[] = "/proc/meminfo";
 const char kVmStatFileName[] = "/proc/vmstat";
-const int kMetricsProcStatFirstLineItemsCount = 11;
 
 // Thermal CPU throttling.
 
@@ -103,9 +101,7 @@
 
 MetricsDaemon::MetricsDaemon()
     : memuse_final_time_(0),
-      memuse_interval_index_(0),
-      ticks_per_second_(0),
-      latest_cpu_use_ticks_(0) {}
+      memuse_interval_index_(0) {}
 
 MetricsDaemon::~MetricsDaemon() {
 }
@@ -188,10 +184,6 @@
   upload_interval_ = upload_interval;
   server_ = server;
 
-  // Get ticks per second (HZ) on this system.
-  // Sysconf cannot fail, so no sanity checks are needed.
-  ticks_per_second_ = sysconf(_SC_CLK_TCK);
-
   daily_active_use_.reset(
       new PersistentInteger("Platform.UseTime.PerDay"));
   version_cumulative_active_use_.reset(
@@ -235,6 +227,7 @@
   averaged_stats_collector_.reset(
       new AveragedStatisticsCollector(metrics_lib_, diskstats_path,
                                       kVmStatFileName));
+  cpu_usage_collector_.reset(new CpuUsageCollector(metrics_lib_));
 }
 
 int MetricsDaemon::OnInit() {
@@ -290,6 +283,7 @@
         base::Bind(&MetricsDaemon::OnDisableMetrics, base::Unretained(this)));
   }
 
+  latest_cpu_use_microseconds_ = cpu_usage_collector_->GetCumulativeCpuUse();
   base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
       base::Bind(&MetricsDaemon::HandleUpdateStatsTimeout,
                  base::Unretained(this)),
@@ -404,53 +398,6 @@
   return DBUS_HANDLER_RESULT_HANDLED;
 }
 
-// One might argue that parts of this should go into
-// chromium/src/base/sys_info_chromeos.c instead, but put it here for now.
-
-TimeDelta MetricsDaemon::GetIncrementalCpuUse() {
-  FilePath proc_stat_path = FilePath(kMetricsProcStatFileName);
-  std::string proc_stat_string;
-  if (!base::ReadFileToString(proc_stat_path, &proc_stat_string)) {
-    LOG(WARNING) << "cannot open " << kMetricsProcStatFileName;
-    return TimeDelta();
-  }
-
-  std::vector<std::string> proc_stat_lines;
-  base::SplitString(proc_stat_string, '\n', &proc_stat_lines);
-  if (proc_stat_lines.empty()) {
-    LOG(WARNING) << "cannot parse " << kMetricsProcStatFileName
-                 << ": " << proc_stat_string;
-    return TimeDelta();
-  }
-  std::vector<std::string> proc_stat_totals;
-  base::SplitStringAlongWhitespace(proc_stat_lines[0], &proc_stat_totals);
-
-  uint64_t user_ticks, user_nice_ticks, system_ticks;
-  if (proc_stat_totals.size() != kMetricsProcStatFirstLineItemsCount ||
-      proc_stat_totals[0] != "cpu" ||
-      !base::StringToUint64(proc_stat_totals[1], &user_ticks) ||
-      !base::StringToUint64(proc_stat_totals[2], &user_nice_ticks) ||
-      !base::StringToUint64(proc_stat_totals[3], &system_ticks)) {
-    LOG(WARNING) << "cannot parse first line: " << proc_stat_lines[0];
-    return TimeDelta(base::TimeDelta::FromSeconds(0));
-  }
-
-  uint64_t total_cpu_use_ticks = user_ticks + user_nice_ticks + system_ticks;
-
-  // Sanity check.
-  if (total_cpu_use_ticks < latest_cpu_use_ticks_) {
-    LOG(WARNING) << "CPU time decreasing from " << latest_cpu_use_ticks_
-                 << " to " << total_cpu_use_ticks;
-    return TimeDelta();
-  }
-
-  uint64_t diff = total_cpu_use_ticks - latest_cpu_use_ticks_;
-  latest_cpu_use_ticks_ = total_cpu_use_ticks;
-  // Use microseconds to avoid significant truncations.
-  return base::TimeDelta::FromMicroseconds(
-      diff * 1000 * 1000 / ticks_per_second_);
-}
-
 void MetricsDaemon::ProcessUserCrash() {
   // Counts the active time up to now.
   UpdateStats(TimeTicks::Now(), Time::Now());
@@ -506,6 +453,9 @@
 void MetricsDaemon::StatsReporterInit() {
   disk_usage_collector_->Schedule();
 
+  cpu_usage_collector_->Init();
+  cpu_usage_collector_->Schedule();
+
   // Don't start a collection cycle during the first run to avoid delaying the
   // boot.
   averaged_stats_collector_->ScheduleWait();
@@ -910,7 +860,10 @@
   version_cumulative_active_use_->Add(elapsed_seconds);
   user_crash_interval_->Add(elapsed_seconds);
   kernel_crash_interval_->Add(elapsed_seconds);
-  version_cumulative_cpu_use_->Add(GetIncrementalCpuUse().InMilliseconds());
+  TimeDelta cpu_use = cpu_usage_collector_->GetCumulativeCpuUse();
+  version_cumulative_cpu_use_->Add(
+      (cpu_use - latest_cpu_use_microseconds_).InMilliseconds());
+  latest_cpu_use_microseconds_ = cpu_use;
   last_update_stats_time_ = now_ticks;
 
   const TimeDelta since_epoch = now_wall_time - Time::UnixEpoch();
diff --git a/metricsd/metrics_daemon.h b/metricsd/metrics_daemon.h
index 3d691c5..f12b02e 100644
--- a/metricsd/metrics_daemon.h
+++ b/metricsd/metrics_daemon.h
@@ -32,6 +32,7 @@
 #include <gtest/gtest_prod.h>  // for FRIEND_TEST
 
 #include "collectors/averaged_statistics_collector.h"
+#include "collectors/cpu_usage_collector.h"
 #include "collectors/disk_usage_collector.h"
 #include "metrics/metrics_library.h"
 #include "persistent_integer.h"
@@ -164,10 +165,6 @@
   // total number of kernel crashes since the last version update.
   void SendKernelCrashesCumulativeCountStats();
 
-  // Returns the total (system-wide) CPU usage between the time of the most
-  // recent call to this function and now.
-  base::TimeDelta GetIncrementalCpuUse();
-
   // Sends a sample representing the number of seconds of active use
   // for a 24-hour period and reset |use|.
   void SendAndResetDailyUseSample(
@@ -268,12 +265,9 @@
   // Selects the wait time for the next memory use callback.
   unsigned int memuse_interval_index_;
 
-  // The system "HZ", or frequency of ticks.  Some system data uses ticks as a
-  // unit, and this is used to convert to standard time units.
-  uint32_t ticks_per_second_;
   // Used internally by GetIncrementalCpuUse() to return the CPU utilization
   // between calls.
-  uint64_t latest_cpu_use_ticks_;
+  base::TimeDelta latest_cpu_use_microseconds_;
 
   // Persistent values and accumulators for crash statistics.
   scoped_ptr<PersistentInteger> daily_cycle_;
@@ -302,6 +296,8 @@
   scoped_ptr<PersistentInteger> kernel_crashes_version_count_;
   scoped_ptr<PersistentInteger> unclean_shutdowns_daily_count_;
   scoped_ptr<PersistentInteger> unclean_shutdowns_weekly_count_;
+
+  scoped_ptr<CpuUsageCollector> cpu_usage_collector_;
   scoped_ptr<DiskUsageCollector> disk_usage_collector_;
   scoped_ptr<AveragedStatisticsCollector> averaged_stats_collector_;
 
diff --git a/metricsd/uploader/metrics_log_base.cc b/metricsd/uploader/metrics_log_base.cc
index 311e43a..1a60b4f 100644
--- a/metricsd/uploader/metrics_log_base.cc
+++ b/metricsd/uploader/metrics_log_base.cc
@@ -16,6 +16,7 @@
 
 #include "uploader/metrics_log_base.h"
 
+#include "base/build_time.h"
 #include "base/metrics/histogram_base.h"
 #include "base/metrics/histogram_samples.h"
 #include "uploader/metrics_hashes.h"
@@ -82,10 +83,7 @@
 int64_t MetricsLogBase::GetBuildTime() {
   static int64_t integral_build_time = 0;
   if (!integral_build_time) {
-    Time time;
-    const char* kDateTime = __DATE__ " " __TIME__ " GMT";
-    bool result = Time::FromString(kDateTime, &time);
-    DCHECK(result);
+    Time time = base::GetBuildTime();
     integral_build_time = static_cast<int64_t>(time.ToTimeT());
   }
   return integral_build_time;
diff --git a/metricsd/uploader/sender_http.h b/metricsd/uploader/sender_http.h
index 6249d90..4f1c08f 100644
--- a/metricsd/uploader/sender_http.h
+++ b/metricsd/uploader/sender_http.h
@@ -23,7 +23,7 @@
 
 #include "uploader/sender.h"
 
-// Sender implemented using http_utils from libchromeos
+// Sender implemented using http_utils from libbrillo
 class HttpSender : public Sender {
  public:
   explicit HttpSender(std::string server_url);
diff --git a/metricsd/uploader/system_profile_cache.cc b/metricsd/uploader/system_profile_cache.cc
index 1995510..f7060a2 100644
--- a/metricsd/uploader/system_profile_cache.cc
+++ b/metricsd/uploader/system_profile_cache.cc
@@ -108,7 +108,18 @@
   profile_.client_id = testing_ ?
       "client_id_test" :
       GetPersistentGUID(guid_path);
-  profile_.hardware_class = "unknown";
+  profile_.model_manifest_id = "unknown";
+  if (!testing_) {
+    brillo::KeyValueStore weave_config;
+    if (!weave_config.Load(base::FilePath(metrics::kWeaveConfigurationFile))) {
+      LOG(ERROR) << "Failed to load the weave configuration file.";
+    } else if (!weave_config.GetString(metrics::kModelManifestId,
+                                       &profile_.model_manifest_id)) {
+      LOG(ERROR) << "The model manifest id (model_id) is undefined in "
+                 << metrics::kWeaveConfigurationFile;
+    }
+  }
+
   profile_.channel = ProtoChannelFromString(channel);
 
   // Increment the session_id everytime we initialize this. If metrics_daemon
@@ -143,7 +154,7 @@
   metrics::SystemProfileProto* profile_proto =
       metrics_proto->mutable_system_profile();
   profile_proto->mutable_hardware()->set_hardware_class(
-      profile_.hardware_class);
+      profile_.model_manifest_id);
   profile_proto->set_app_version(profile_.version);
   profile_proto->set_channel(profile_.channel);
   metrics::SystemProfileProto_BrilloDeviceData* device_data =
diff --git a/metricsd/uploader/system_profile_cache.h b/metricsd/uploader/system_profile_cache.h
index 97fb33a..ae54a2a 100644
--- a/metricsd/uploader/system_profile_cache.h
+++ b/metricsd/uploader/system_profile_cache.h
@@ -35,7 +35,7 @@
 
 struct SystemProfile {
   std::string version;
-  std::string hardware_class;
+  std::string model_manifest_id;
   std::string client_id;
   int session_id;
   metrics::SystemProfileProto::Channel channel;
diff --git a/metricsd/uploader/upload_service.h b/metricsd/uploader/upload_service.h
index a4d0a1e..77df74b 100644
--- a/metricsd/uploader/upload_service.h
+++ b/metricsd/uploader/upload_service.h
@@ -46,8 +46,8 @@
 //
 // The two states are the presence or not of a staged log.
 // A staged log is a compressed protobuffer containing both the aggregated
-// metrics and event and information about the client. (product, hardware id,
-// etc...).
+// metrics and event and information about the client. (product,
+// model_manifest_id, etc...).
 //
 // At regular intervals, the upload event will be triggered and the following
 // will happen:
diff --git a/packagelistparser/Android.mk b/packagelistparser/Android.mk
new file mode 100644
index 0000000..c8be050
--- /dev/null
+++ b/packagelistparser/Android.mk
@@ -0,0 +1,32 @@
+LOCAL_PATH:= $(call my-dir)
+
+#########################
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libpackagelistparser
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := packagelistparser.c
+LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+
+LOCAL_CLANG := true
+LOCAL_SANITIZE := integer
+
+include $(BUILD_SHARED_LIBRARY)
+
+#########################
+include $(CLEAR_VARS)
+
+
+LOCAL_MODULE := libpackagelistparser
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := packagelistparser.c
+LOCAL_STATIC_LIBRARIES := liblog
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+
+LOCAL_CLANG := true
+LOCAL_SANITIZE := integer
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/packagelistparser/include/packagelistparser/packagelistparser.h b/packagelistparser/include/packagelistparser/packagelistparser.h
new file mode 100644
index 0000000..d602c26
--- /dev/null
+++ b/packagelistparser/include/packagelistparser/packagelistparser.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2015, Intel Corporation
+ * 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.
+ *
+ * Written by William Roberts <william.c.roberts@intel.com>
+ *
+ * This is a parser library for parsing the packages.list file generated
+ * by PackageManager service.
+ *
+ * This simple parser is sensitive to format changes in
+ * frameworks/base/services/core/java/com/android/server/pm/Settings.java
+ * A dependency note has been added to that file to correct
+ * this parser.
+ */
+
+#ifndef PACKAGELISTPARSER_H_
+#define PACKAGELISTPARSER_H_
+
+#include <stdbool.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+
+__BEGIN_DECLS
+
+/** The file containing the list of installed packages on the system */
+#define PACKAGES_LIST_FILE  "/data/system/packages.list"
+
+typedef struct pkg_info pkg_info;
+typedef struct gid_list gid_list;
+
+struct gid_list {
+    size_t cnt;
+    gid_t *gids;
+};
+
+struct pkg_info {
+    char *name;
+    uid_t uid;
+    bool debuggable;
+    char *data_dir;
+    char *seinfo;
+    gid_list gids;
+    void *private_data;
+};
+
+/**
+ * Callback function to be used by packagelist_parse() routine.
+ * @param info
+ *  The parsed package information
+ * @param userdata
+ *  The supplied userdata pointer to packagelist_parse()
+ * @return
+ *  true to keep processing, false to stop.
+ */
+typedef bool (*pfn_on_package)(pkg_info *info, void *userdata);
+
+/**
+ * Parses the file specified by PACKAGES_LIST_FILE and invokes the callback on
+ * each entry found. Once the callback is invoked, ownership of the pkg_info pointer
+ * is passed to the callback routine, thus they are required to perform any cleanup
+ * desired.
+ * @param callback
+ *  The callback function called on each parsed line of the packages list.
+ * @param userdata
+ *  An optional userdata supplied pointer to pass to the callback function.
+ * @return
+ *  true on success false on failure.
+ */
+extern bool packagelist_parse(pfn_on_package callback, void *userdata);
+
+/**
+ * Frees a pkg_info structure.
+ * @param info
+ *  The struct to free
+ */
+extern void packagelist_free(pkg_info *info);
+
+__END_DECLS
+
+#endif /* PACKAGELISTPARSER_H_ */
diff --git a/packagelistparser/packagelistparser.c b/packagelistparser/packagelistparser.c
new file mode 100644
index 0000000..16052e2
--- /dev/null
+++ b/packagelistparser/packagelistparser.c
@@ -0,0 +1,266 @@
+/*
+ * Copyright 2015, Intel Corporation
+ * 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.
+ *
+ * Written by William Roberts <william.c.roberts@intel.com>
+ *
+ */
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/limits.h>
+
+#define LOG_TAG "packagelistparser"
+#include <utils/Log.h>
+
+#include <packagelistparser/packagelistparser.h>
+
+#define CLOGE(fmt, ...) \
+    do {\
+        IF_ALOGE() {\
+            ALOGE(fmt, ##__VA_ARGS__);\
+        }\
+    } while(0)
+
+static size_t get_gid_cnt(const char *gids)
+{
+    size_t cnt;
+
+    if (*gids == '\0') {
+        return 0;
+    }
+
+    if (!strcmp(gids, "none")) {
+        return 0;
+    }
+
+    for (cnt = 1; gids[cnt]; gids[cnt] == ',' ? cnt++ : *gids++)
+        ;
+
+    return cnt;
+}
+
+static bool parse_gids(char *gids, gid_t *gid_list, size_t *cnt)
+{
+    gid_t gid;
+    char* token;
+    char *endptr;
+    size_t cmp = 0;
+
+    while ((token = strsep(&gids, ",\r\n"))) {
+
+        if (cmp > *cnt) {
+            return false;
+        }
+
+        gid = strtoul(token, &endptr, 10);
+        if (*endptr != '\0') {
+            return false;
+        }
+
+        /*
+         * if unsigned long is greater than size of gid_t,
+         * prevent a truncation based roll-over
+         */
+        if (gid > GID_MAX) {
+            CLOGE("A gid in field \"gid list\" greater than GID_MAX");
+            return false;
+        }
+
+        gid_list[cmp++] = gid;
+    }
+    return true;
+}
+
+extern bool packagelist_parse(pfn_on_package callback, void *userdata)
+{
+
+    FILE *fp;
+    char *cur;
+    char *next;
+    char *endptr;
+    unsigned long tmp;
+    ssize_t bytesread;
+
+    bool rc = false;
+    char *buf = NULL;
+    size_t buflen = 0;
+    unsigned long lineno = 1;
+    const char *errmsg = NULL;
+    struct pkg_info *pkg_info = NULL;
+
+    fp = fopen(PACKAGES_LIST_FILE, "re");
+    if (!fp) {
+        CLOGE("Could not open: \"%s\", error: \"%s\"\n", PACKAGES_LIST_FILE,
+                strerror(errno));
+        return false;
+    }
+
+    while ((bytesread = getline(&buf, &buflen, fp)) > 0) {
+
+        pkg_info = calloc(1, sizeof(*pkg_info));
+        if (!pkg_info) {
+            goto err;
+        }
+
+        next = buf;
+
+        cur = strsep(&next, " \t\r\n");
+        if (!cur) {
+            errmsg = "Could not get next token for \"package name\"";
+            goto err;
+        }
+
+        pkg_info->name = strdup(cur);
+        if (!pkg_info->name) {
+            goto err;
+        }
+
+        cur = strsep(&next, " \t\r\n");
+        if (!cur) {
+            errmsg = "Could not get next token for field \"uid\"";
+            goto err;
+        }
+
+        tmp = strtoul(cur, &endptr, 10);
+        if (*endptr != '\0') {
+            errmsg = "Could not convert field \"uid\" to integer value";
+            goto err;
+        }
+
+        /*
+         * if unsigned long is greater than size of uid_t,
+         * prevent a truncation based roll-over
+         */
+        if (tmp > UID_MAX) {
+            errmsg = "Field \"uid\" greater than UID_MAX";
+            goto err;
+        }
+
+        pkg_info->uid = (uid_t) tmp;
+
+        cur = strsep(&next, " \t\r\n");
+        if (!cur) {
+            errmsg = "Could not get next token for field \"debuggable\"";
+            goto err;
+        }
+
+        tmp = strtoul(cur, &endptr, 10);
+        if (*endptr != '\0') {
+            errmsg = "Could not convert field \"debuggable\" to integer value";
+            goto err;
+        }
+
+        /* should be a valid boolean of 1 or 0 */
+        if (!(tmp == 0 || tmp == 1)) {
+            errmsg = "Field \"debuggable\" is not 0 or 1 boolean value";
+            goto err;
+        }
+
+        pkg_info->debuggable = (bool) tmp;
+
+        cur = strsep(&next, " \t\r\n");
+        if (!cur) {
+            errmsg = "Could not get next token for field \"data dir\"";
+            goto err;
+        }
+
+        pkg_info->data_dir = strdup(cur);
+        if (!pkg_info->data_dir) {
+            goto err;
+        }
+
+        cur = strsep(&next, " \t\r\n");
+        if (!cur) {
+            errmsg = "Could not get next token for field \"seinfo\"";
+            goto err;
+        }
+
+        pkg_info->seinfo = strdup(cur);
+        if (!pkg_info->seinfo) {
+            goto err;
+        }
+
+        cur = strsep(&next, " \t\r\n");
+        if (!cur) {
+            errmsg = "Could not get next token for field \"gid(s)\"";
+            goto err;
+        }
+
+        /*
+         * Parse the gid list, could be in the form of none, single gid or list:
+         * none
+         * gid
+         * gid, gid ...
+         */
+        pkg_info->gids.cnt = get_gid_cnt(cur);
+        if (pkg_info->gids.cnt > 0) {
+
+            pkg_info->gids.gids = calloc(pkg_info->gids.cnt, sizeof(gid_t));
+            if (!pkg_info->gids.gids) {
+                goto err;
+            }
+
+            rc = parse_gids(cur, pkg_info->gids.gids, &pkg_info->gids.cnt);
+            if (!rc) {
+                errmsg = "Could not parse field \"gid list\"";
+                goto err;
+            }
+        }
+
+        rc = callback(pkg_info, userdata);
+        if (rc == false) {
+            /*
+             * We do not log this as this can be intentional from
+             * callback to abort processing. We go to out to not
+             * free the pkg_info
+             */
+            rc = true;
+            goto out;
+        }
+        lineno++;
+    }
+
+    rc = true;
+
+out:
+    free(buf);
+    fclose(fp);
+    return rc;
+
+err:
+    if (errmsg) {
+        CLOGE("Error Parsing \"%s\" on line: %lu for reason: %s",
+                PACKAGES_LIST_FILE, lineno, errmsg);
+    }
+    rc = false;
+    packagelist_free(pkg_info);
+    goto out;
+}
+
+void packagelist_free(pkg_info *info)
+{
+    if (info) {
+        free(info->name);
+        free(info->data_dir);
+        free(info->seinfo);
+        free(info->gids.gids);
+        free(info);
+    }
+}
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 8b16996..b134f93 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -41,17 +41,21 @@
 # because init.rc is conditionally included.
 #
 # create some directories (some are mount points) and symlinks
-local_post_install_cmd_base := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \
-    sbin dev proc sys system data oem acct cache config storage mnt root); \
+LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \
+    sbin dev proc sys system data oem acct cache config storage mnt root $(BOARD_ROOT_EXTRA_FOLDERS)); \
     ln -sf /system/etc $(TARGET_ROOT_OUT)/etc; \
     ln -sf /sys/kernel/debug $(TARGET_ROOT_OUT)/d; \
     ln -sf /storage/self/primary $(TARGET_ROOT_OUT)/sdcard
 ifdef BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE
-  LOCAL_POST_INSTALL_CMD := $(local_post_install_cmd_base); mkdir -p $(TARGET_ROOT_OUT)/vendor
-else
-  LOCAL_POST_INSTALL_CMD := $(local_post_install_cmd_base)
+  LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/vendor
 endif
-local_post_install_cmd_base :=
+ifdef BOARD_ROOT_EXTRA_SYMLINKS
+# BOARD_ROOT_EXTRA_SYMLINKS is a list of <target>:<link_name>.
+  LOCAL_POST_INSTALL_CMD += $(foreach s, $(BOARD_ROOT_EXTRA_SYMLINKS),\
+    $(eval p := $(subst :,$(space),$(s)))\
+    ; mkdir -p $(dir $(TARGET_ROOT_OUT)/$(word 2,$(p))) \
+    ; ln -sf $(word 1,$(p)) $(TARGET_ROOT_OUT)/$(word 2,$(p)))
+endif
 
 include $(BUILD_SYSTEM)/base_rules.mk
 
diff --git a/rootdir/asan.options b/rootdir/asan.options
index 2f12341..43896a1 100644
--- a/rootdir/asan.options
+++ b/rootdir/asan.options
@@ -1 +1,5 @@
-allow_user_segv_handler=1:detect_odr_violation=0:alloc_dealloc_mismatch=0:allocator_may_return_null=1
+allow_user_segv_handler=1
+detect_odr_violation=0
+alloc_dealloc_mismatch=0
+allocator_may_return_null=1
+detect_container_overflow=0
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 38c686d..b80c454 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -7,6 +7,7 @@
 import /init.environ.rc
 import /init.usb.rc
 import /init.${ro.hardware}.rc
+import /init.usb.configfs.rc
 import /init.${ro.zygote}.rc
 
 on early-init
@@ -86,10 +87,17 @@
     write /proc/sys/kernel/panic_on_oops 1
     write /proc/sys/kernel/hung_task_timeout_secs 0
     write /proc/cpu/alignment 4
+
+    # scheduler tunables
+    # Disable auto-scaling of scheduler tunables with hotplug. The tunables
+    # will vary across devices in unpredictable ways if allowed to scale with
+    # cpu cores.
+    write /proc/sys/kernel/sched_tunable_scaling 0
     write /proc/sys/kernel/sched_latency_ns 10000000
     write /proc/sys/kernel/sched_wakeup_granularity_ns 2000000
     write /proc/sys/kernel/sched_compat_yield 1
     write /proc/sys/kernel/sched_child_runs_first 0
+
     write /proc/sys/kernel/randomize_va_space 2
     write /proc/sys/kernel/kptr_restrict 2
     write /proc/sys/vm/mmap_min_addr 32768
@@ -131,20 +139,32 @@
     mkdir /dev/cpuset
     mount cpuset none /dev/cpuset
     mkdir /dev/cpuset/foreground
+    mkdir /dev/cpuset/foreground/boost
     mkdir /dev/cpuset/background
+    # system-background is for system tasks that should only run on
+    # little cores, not on bigs
+    # to be used only by init, so don't change the permissions
+    mkdir /dev/cpuset/system-background
     # this ensures that the cpusets are present and usable, but the device's
     # init.rc must actually set the correct cpus
     write /dev/cpuset/foreground/cpus 0
+    write /dev/cpuset/foreground/boost/cpus 0
     write /dev/cpuset/background/cpus 0
+    write /dev/cpuset/system-background/cpus 0
     write /dev/cpuset/foreground/mems 0
+    write /dev/cpuset/foreground/boost/mems 0
     write /dev/cpuset/background/mems 0
+    write /dev/cpuset/system-background/mems 0
     chown system system /dev/cpuset
     chown system system /dev/cpuset/foreground
+    chown system system /dev/cpuset/foreground/boost
     chown system system /dev/cpuset/background
     chown system system /dev/cpuset/tasks
     chown system system /dev/cpuset/foreground/tasks
+    chown system system /dev/cpuset/foreground/boost/tasks
     chown system system /dev/cpuset/background/tasks
     chmod 0664 /dev/cpuset/foreground/tasks
+    chmod 0664 /dev/cpuset/foreground/boost/tasks
     chmod 0664 /dev/cpuset/background/tasks
     chmod 0664 /dev/cpuset/tasks
 
@@ -180,8 +200,11 @@
     trigger late-init
 
 # Load properties from /system/ + /factory after fs mount.
-on load_all_props_action
-    load_all_props
+on load_system_props_action
+    load_system_props
+
+on load_persist_props_action
+    load_persist_props
     start logd
     start logd-reinit
 
@@ -194,12 +217,16 @@
     trigger early-fs
     trigger fs
     trigger post-fs
-    trigger post-fs-data
 
     # Load properties from /system/ + /factory after fs mount. Place
     # this in another action so that the load will be scheduled after the prior
     # issued fs triggers have completed.
-    trigger load_all_props_action
+    trigger load_system_props_action
+
+    # Now we can mount /data. File encryption requires keymaster to decrypt
+    # /data, which in turn can only be loaded when system properties are present
+    trigger post-fs-data
+    trigger load_persist_props_action
 
     # Remove a file to wake up anything waiting for firmware.
     trigger firmware_mounts_complete
@@ -349,6 +376,8 @@
     mkdir /data/system/heapdump 0700 system system
     mkdir /data/user 0711 system system
 
+    setusercryptopolicies /data/user
+
     # Reload policy from /data/security if present.
     setprop selinux.reload_policy 1
 
diff --git a/rootdir/init.usb.configfs.rc b/rootdir/init.usb.configfs.rc
new file mode 100644
index 0000000..186384b
--- /dev/null
+++ b/rootdir/init.usb.configfs.rc
@@ -0,0 +1,175 @@
+on property:sys.usb.config=none && property:sys.usb.configfs=1
+    write /config/usb_gadget/g1/UDC "none"
+    stop adbd
+    write /config/usb_gadget/g1/bDeviceClass 0
+    write /config/usb_gadget/g1/bDeviceSubClass 0
+    write /config/usb_gadget/g1/bDeviceProtocol 0
+    setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=adb && property:sys.usb.configfs=1
+    start adbd
+
+on property:sys.usb.ffs.ready=1 && property:sys.usb.config=adb && property:sys.usb.configfs=1
+    write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "adb"
+    rm /config/usb_gadget/g1/configs/b.1/f1
+    rm /config/usb_gadget/g1/configs/b.1/f2
+    rm /config/usb_gadget/g1/configs/b.1/f3
+    symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f1
+    write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+    setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=mtp && property:sys.usb.configfs=1
+    write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "mtp"
+    rm /config/usb_gadget/g1/configs/b.1/f1
+    rm /config/usb_gadget/g1/configs/b.1/f2
+    rm /config/usb_gadget/g1/configs/b.1/f3
+    symlink /config/usb_gadget/g1/functions/mtp.gs0 /config/usb_gadget/g1/configs/b.1/f1
+    write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+    setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=mtp,adb && property:sys.usb.configfs=1
+    start adbd
+
+on property:sys.usb.ffs.ready=1 && property:sys.usb.config=mtp,adb && property:sys.usb.configfs=1
+    write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "mtp_adb"
+    rm /config/usb_gadget/g1/configs/b.1/f1
+    rm /config/usb_gadget/g1/configs/b.1/f2
+    rm /config/usb_gadget/g1/configs/b.1/f3
+    symlink /config/usb_gadget/g1/functions/mtp.gs0 /config/usb_gadget/g1/configs/b.1/f1
+    symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2
+    write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+    setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=ptp && property:sys.usb.configfs=1
+    write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "ptp"
+    rm /config/usb_gadget/g1/configs/b.1/f1
+    rm /config/usb_gadget/g1/configs/b.1/f2
+    rm /config/usb_gadget/g1/configs/b.1/f3
+    symlink /config/usb_gadget/g1/functions/ptp.gs1 /config/usb_gadget/g1/configs/b.1/f1
+    write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+    setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=ptp,adb && property:sys.usb.configfs=1
+    start adbd
+
+on property:sys.usb.ffs.ready=1 && property:sys.usb.config=ptp,adb && property:sys.usb.configfs=1
+    write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "ptp_adb"
+    rm /config/usb_gadget/g1/configs/b.1/f1
+    rm /config/usb_gadget/g1/configs/b.1/f2
+    rm /config/usb_gadget/g1/configs/b.1/f3
+    symlink /config/usb_gadget/g1/functions/ptp.gs1 /config/usb_gadget/g1/configs/b.1/f1
+    symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2
+    write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+    setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=accessory && property:sys.usb.configfs=1
+    write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "accessory"
+    rm /config/usb_gadget/g1/configs/b.1/f1
+    rm /config/usb_gadget/g1/configs/b.1/f2
+    rm /config/usb_gadget/g1/configs/b.1/f3
+    symlink /config/usb_gadget/g1/functions/accessory.gs2 /config/usb_gadget/g1/configs/b.1/f1
+    write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+    setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=accessory,adb && property:sys.usb.configfs=1
+    start adbd
+
+on property:sys.usb.ffs.ready=1 && property:sys.usb.config=accessory,adb && property:sys.usb.configfs=1
+    write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "accessory_adb"
+    rm /config/usb_gadget/g1/configs/b.1/f1
+    rm /config/usb_gadget/g1/configs/b.1/f2
+    rm /config/usb_gadget/g1/configs/b.1/f3
+    symlink /config/usb_gadget/g1/functions/accessory.gs2 /config/usb_gadget/g1/configs/b.1/f1
+    symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2
+    write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+    setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=audio_source && property:sys.usb.configfs=1
+    write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "audiosource"
+    rm /config/usb_gadget/g1/configs/b.1/f1
+    rm /config/usb_gadget/g1/configs/b.1/f2
+    rm /config/usb_gadget/g1/configs/b.1/f3
+    symlink /config/usb_gadget/g1/functions/audio_source.gs2 /config/usb_gadget/g1/configs/b.1/f1
+    write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+    setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=audio_source,adb && property:sys.usb.configfs=1
+    start adbd
+
+on property:sys.usb.ffs.ready=1 && property:sys.usb.config=audio_source,adb && property:sys.usb.configfs=1
+    write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "audiosource_adb"
+    rm /config/usb_gadget/g1/configs/b.1/f1
+    rm /config/usb_gadget/g1/configs/b.1/f2
+    rm /config/usb_gadget/g1/configs/b.1/f3
+    symlink /config/usb_gadget/g1/functions/audio_source.gs2 /config/usb_gadget/g1/configs/b.1/f1
+    symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2
+    write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+    setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=accessory,audio_source && property:sys.usb.configfs=1
+    write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "accessory_audiosource"
+    rm /config/usb_gadget/g1/configs/b.1/f1
+    rm /config/usb_gadget/g1/configs/b.1/f2
+    rm /config/usb_gadget/g1/configs/b.1/f3
+    symlink /config/usb_gadget/g1/functions/accessory.gs2 /config/usb_gadget/g1/configs/b.1/f1
+    symlink /config/usb_gadget/g1/functions/audio_source.gs3 /config/usb_gadget/g1/configs/b.1/f2
+    write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+    setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=accessory,audio_source,adb && property:sys.usb.configfs=1
+    start adbd
+
+on property:sys.usb.ffs.ready=1 && property:sys.usb.config=accessory,audio_source,adb && property:sys.usb.configfs=1
+    write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "accessory_audiosource_adb"
+    rm /config/usb_gadget/g1/configs/b.1/f1
+    rm /config/usb_gadget/g1/configs/b.1/f2
+    rm /config/usb_gadget/g1/configs/b.1/f3
+    symlink /config/usb_gadget/g1/functions/accessory.gs2 /config/usb_gadget/g1/configs/b.1/f1
+    symlink /config/usb_gadget/g1/functions/audio_source.gs3 /config/usb_gadget/g1/configs/b.1/f2
+    symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f3
+    write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+    setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=midi && property:sys.usb.configfs=1
+    write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "midi"
+    rm /config/usb_gadget/g1/configs/b.1/f1
+    rm /config/usb_gadget/g1/configs/b.1/f2
+    rm /config/usb_gadget/g1/configs/b.1/f3
+    symlink /config/usb_gadget/g1/functions/midi.gs5 /config/usb_gadget/g1/configs/b.1/f1
+    write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+    setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=midi,adb && property:sys.usb.configfs=1
+    start adbd
+
+on property:sys.usb.ffs.ready=1 && property:sys.usb.config=midi,adb && property:sys.usb.configfs=1
+    write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "midi_adb"
+    rm /config/usb_gadget/g1/configs/b.1/f1
+    rm /config/usb_gadget/g1/configs/b.1/f2
+    rm /config/usb_gadget/g1/configs/b.1/f3
+    symlink /config/usb_gadget/g1/functions/midi.gs5 /config/usb_gadget/g1/configs/b.1/f1
+    symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2
+    write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+    setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=rndis && property:sys.usb.configfs=1
+    write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "rndis"
+    rm /config/usb_gadget/g1/configs/b.1/f1
+    rm /config/usb_gadget/g1/configs/b.1/f2
+    rm /config/usb_gadget/g1/configs/b.1/f3
+    symlink /config/usb_gadget/g1/functions/rndis.gs4 /config/usb_gadget/g1/configs/b.1/f1
+    write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+    setprop sys.usb.state ${sys.usb.config}
+
+on property:sys.usb.config=rndis,adb && property:sys.usb.configfs=1
+    start adbd
+
+on property:sys.usb.ffs.ready=1 && property:sys.usb.config=rndis,adb && property:sys.usb.configfs=1
+    write /config/usb_gadget/g1/configs/b.1/strings/0x409/configuration "rndis_adb"
+    rm /config/usb_gadget/g1/configs/b.1/f1
+    rm /config/usb_gadget/g1/configs/b.1/f2
+    rm /config/usb_gadget/g1/configs/b.1/f3
+    symlink /config/usb_gadget/g1/functions/rndis.gs4 /config/usb_gadget/g1/configs/b.1/f1
+    symlink /config/usb_gadget/g1/functions/ffs.adb /config/usb_gadget/g1/configs/b.1/f2
+    write /config/usb_gadget/g1/UDC ${sys.usb.controller}
+    setprop sys.usb.state ${sys.usb.config}
diff --git a/rootdir/init.usb.rc b/rootdir/init.usb.rc
index 4e6f2a8..1fd1e2a 100644
--- a/rootdir/init.usb.rc
+++ b/rootdir/init.usb.rc
@@ -22,8 +22,11 @@
 on property:ro.kernel.qemu=1
     start adbd
 
+on boot
+    setprop sys.usb.configfs 0
+
 # Used to disable USB when switching states
-on property:sys.usb.config=none
+on property:sys.usb.config=none && property:sys.usb.configfs=0
     stop adbd
     write /sys/class/android_usb/android0/enable 0
     write /sys/class/android_usb/android0/bDeviceClass 0
@@ -32,7 +35,7 @@
 # adb only USB configuration
 # This is the fallback configuration if the
 # USB manager fails to set a standard configuration
-on property:sys.usb.config=adb
+on property:sys.usb.config=adb && property:sys.usb.configfs=0
     write /sys/class/android_usb/android0/enable 0
     write /sys/class/android_usb/android0/idVendor 18d1
     write /sys/class/android_usb/android0/idProduct 4EE7
@@ -42,7 +45,7 @@
     setprop sys.usb.state ${sys.usb.config}
 
 # USB accessory configuration
-on property:sys.usb.config=accessory
+on property:sys.usb.config=accessory && property:sys.usb.configfs=0
     write /sys/class/android_usb/android0/enable 0
     write /sys/class/android_usb/android0/idVendor 18d1
     write /sys/class/android_usb/android0/idProduct 2d00
@@ -51,7 +54,7 @@
     setprop sys.usb.state ${sys.usb.config}
 
 # USB accessory configuration, with adb
-on property:sys.usb.config=accessory,adb
+on property:sys.usb.config=accessory,adb && property:sys.usb.configfs=0
     write /sys/class/android_usb/android0/enable 0
     write /sys/class/android_usb/android0/idVendor 18d1
     write /sys/class/android_usb/android0/idProduct 2d01
@@ -61,7 +64,7 @@
     setprop sys.usb.state ${sys.usb.config}
 
 # audio accessory configuration
-on property:sys.usb.config=audio_source
+on property:sys.usb.config=audio_source && property:sys.usb.configfs=0
     write /sys/class/android_usb/android0/enable 0
     write /sys/class/android_usb/android0/idVendor 18d1
     write /sys/class/android_usb/android0/idProduct 2d02
@@ -70,7 +73,7 @@
     setprop sys.usb.state ${sys.usb.config}
 
 # audio accessory configuration, with adb
-on property:sys.usb.config=audio_source,adb
+on property:sys.usb.config=audio_source,adb && property:sys.usb.configfs=0
     write /sys/class/android_usb/android0/enable 0
     write /sys/class/android_usb/android0/idVendor 18d1
     write /sys/class/android_usb/android0/idProduct 2d03
@@ -80,7 +83,7 @@
     setprop sys.usb.state ${sys.usb.config}
 
 # USB and audio accessory configuration
-on property:sys.usb.config=accessory,audio_source
+on property:sys.usb.config=accessory,audio_source && property:sys.usb.configfs=0
     write /sys/class/android_usb/android0/enable 0
     write /sys/class/android_usb/android0/idVendor 18d1
     write /sys/class/android_usb/android0/idProduct 2d04
@@ -89,7 +92,7 @@
     setprop sys.usb.state ${sys.usb.config}
 
 # USB and audio accessory configuration, with adb
-on property:sys.usb.config=accessory,audio_source,adb
+on property:sys.usb.config=accessory,audio_source,adb && property:sys.usb.configfs=0
     write /sys/class/android_usb/android0/enable 0
     write /sys/class/android_usb/android0/idVendor 18d1
     write /sys/class/android_usb/android0/idProduct 2d05
@@ -102,3 +105,34 @@
 # when changing the default configuration
 on property:persist.sys.usb.config=*
     setprop sys.usb.config ${persist.sys.usb.config}
+
+#
+# USB type C
+#
+
+# USB mode changes
+on property:sys.usb.typec.mode=dfp
+    write /sys/class/dual_role_usb/otg_default/mode ${sys.usb.typec.mode}
+    setprop sys.usb.typec.state ${sys.usb.typec.mode}
+
+on property:sys.usb.typec.mode=ufp
+    write /sys/class/dual_role_usb/otg_default/mode ${sys.usb.typec.mode}
+    setprop sys.usb.typec.state ${sys.usb.typec.mode}
+
+# USB data role changes
+on property:sys.usb.typec.data_role=device
+    write /sys/class/dual_role_usb/otg_default/data_role ${sys.usb.typec.data_role}
+    setprop sys.usb.typec.state ${sys.usb.typec.data_role}
+
+on property:sys.usb.typec.data_role=host
+    write /sys/class/dual_role_usb/otg_default/data_role ${sys.usb.typec.data_role}
+    setprop sys.usb.typec.state ${sys.usb.typec.data_role}
+
+# USB power role changes
+on property:sys.usb.typec.power_role=source
+    write /sys/class/dual_role_usb/otg_default/power_role ${sys.usb.typec.power_role}
+    setprop sys.usb.typec.state ${sys.usb.typec.power_role}
+
+on property:sys.usb.typec.power_role=sink
+    write /sys/class/dual_role_usb/otg_default/power_role ${sys.usb.typec.power_role}
+    setprop sys.usb.typec.state ${sys.usb.typec.power_role}
diff --git a/rootdir/init.zygote32.rc b/rootdir/init.zygote32.rc
index 75961e6..ff25ac2 100644
--- a/rootdir/init.zygote32.rc
+++ b/rootdir/init.zygote32.rc
@@ -5,4 +5,4 @@
     onrestart write /sys/power/state on
     onrestart restart media
     onrestart restart netd
-
+    writepid /dev/cpuset/foreground/tasks
diff --git a/rootdir/init.zygote32_64.rc b/rootdir/init.zygote32_64.rc
index 68c0668..29bb1cf 100644
--- a/rootdir/init.zygote32_64.rc
+++ b/rootdir/init.zygote32_64.rc
@@ -5,8 +5,10 @@
     onrestart write /sys/power/state on
     onrestart restart media
     onrestart restart netd
+    writepid /dev/cpuset/foreground/tasks
 
 service zygote_secondary /system/bin/app_process64 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
     class main
     socket zygote_secondary stream 660 root system
     onrestart restart zygote
+    writepid /dev/cpuset/foreground/tasks
\ No newline at end of file
diff --git a/rootdir/init.zygote64.rc b/rootdir/init.zygote64.rc
index afb6d63..5497524 100644
--- a/rootdir/init.zygote64.rc
+++ b/rootdir/init.zygote64.rc
@@ -5,4 +5,4 @@
     onrestart write /sys/power/state on
     onrestart restart media
     onrestart restart netd
-
+    writepid /dev/cpuset/foreground/tasks
diff --git a/rootdir/init.zygote64_32.rc b/rootdir/init.zygote64_32.rc
index 979ab3b..8ed5e9e 100644
--- a/rootdir/init.zygote64_32.rc
+++ b/rootdir/init.zygote64_32.rc
@@ -5,8 +5,10 @@
     onrestart write /sys/power/state on
     onrestart restart media
     onrestart restart netd
+    writepid /dev/cpuset/foreground/tasks
 
 service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
     class main
     socket zygote_secondary stream 660 root system
     onrestart restart zygote
+    writepid /dev/cpuset/foreground/tasks
\ No newline at end of file
diff --git a/run-as/run-as.c b/run-as/run-as.c
index 3f32e7d..f0fd2fe 100644
--- a/run-as/run-as.c
+++ b/run-as/run-as.c
@@ -20,6 +20,8 @@
 
 #include <dirent.h>
 #include <errno.h>
+#include <paths.h>
+#include <pwd.h>
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -193,10 +195,21 @@
         panic("Could not set SELinux security context: %s\n", strerror(errno));
     }
 
-    /* cd into the data directory */
+    // cd into the data directory, and set $HOME correspondingly.
     if (TEMP_FAILURE_RETRY(chdir(info.dataDir)) < 0) {
         panic("Could not cd to package's data directory: %s\n", strerror(errno));
     }
+    setenv("HOME", info.dataDir, 1);
+
+    // Reset parts of the environment, like su would.
+    setenv("PATH", _PATH_DEFPATH, 1);
+    unsetenv("IFS");
+
+    // Set the user-specific parts for this user.
+    struct passwd* pw = getpwuid(uid);
+    setenv("LOGNAME", pw->pw_name, 1);
+    setenv("SHELL", pw->pw_shell, 1);
+    setenv("USER", pw->pw_name, 1);
 
     /* User specified command for exec. */
     if ((argc >= commandArgvOfs + 1) &&
diff --git a/sdcard/Android.mk b/sdcard/Android.mk
index cb3a8fb..c5f3d1d 100644
--- a/sdcard/Android.mk
+++ b/sdcard/Android.mk
@@ -5,7 +5,6 @@
 LOCAL_SRC_FILES := sdcard.c
 LOCAL_MODULE := sdcard
 LOCAL_CFLAGS := -Wall -Wno-unused-parameter -Werror
-
-LOCAL_SHARED_LIBRARIES := libcutils
+LOCAL_SHARED_LIBRARIES := libcutils libpackagelistparser
 
 include $(BUILD_EXECUTABLE)
diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c
index d2d2315..b6bbe7e 100644
--- a/sdcard/sdcard.c
+++ b/sdcard/sdcard.c
@@ -24,6 +24,7 @@
 #include <limits.h>
 #include <linux/fuse.h>
 #include <pthread.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -34,6 +35,7 @@
 #include <sys/stat.h>
 #include <sys/statfs.h>
 #include <sys/time.h>
+#include <sys/types.h>
 #include <sys/uio.h>
 #include <unistd.h>
 
@@ -41,6 +43,7 @@
 #include <cutils/hashmap.h>
 #include <cutils/log.h>
 #include <cutils/multiuser.h>
+#include <packagelistparser/packagelistparser.h>
 
 #include <private/android_filesystem_config.h>
 
@@ -103,9 +106,6 @@
  * or that a reply has already been written. */
 #define NO_STATUS 1
 
-/* Path to system-provided mapping of package name to appIds */
-static const char* const kPackagesListFile = "/data/system/packages.list";
-
 /* Supplementary groups to execute with */
 static const gid_t kGroups[1] = { AID_PACKAGE_INFO };
 
@@ -1636,35 +1636,27 @@
     return true;
 }
 
-static int read_package_list(struct fuse_global* global) {
+static bool package_parse_callback(pkg_info *info, void *userdata) {
+    struct fuse_global *global = (struct fuse_global *)userdata;
+
+    char* name = strdup(info->name);
+    hashmapPut(global->package_to_appid, name, (void*) (uintptr_t) info->uid);
+    packagelist_free(info);
+    return true;
+}
+
+static bool read_package_list(struct fuse_global* global) {
     pthread_mutex_lock(&global->lock);
 
     hashmapForEach(global->package_to_appid, remove_str_to_int, global->package_to_appid);
 
-    FILE* file = fopen(kPackagesListFile, "r");
-    if (!file) {
-        ERROR("failed to open package list: %s\n", strerror(errno));
-        pthread_mutex_unlock(&global->lock);
-        return -1;
-    }
-
-    char buf[512];
-    while (fgets(buf, sizeof(buf), file) != NULL) {
-        char package_name[512];
-        int appid;
-        char gids[512];
-
-        if (sscanf(buf, "%s %d %*d %*s %*s %s", package_name, &appid, gids) == 3) {
-            char* package_name_dup = strdup(package_name);
-            hashmapPut(global->package_to_appid, package_name_dup, (void*) (uintptr_t) appid);
-        }
-    }
-
+    bool rc = packagelist_parse(package_parse_callback, global);
     TRACE("read_package_list: found %zu packages\n",
             hashmapSize(global->package_to_appid));
-    fclose(file);
+
     pthread_mutex_unlock(&global->lock);
-    return 0;
+
+    return rc;
 }
 
 static void watch_package_list(struct fuse_global* global) {
@@ -1680,11 +1672,11 @@
     bool active = false;
     while (1) {
         if (!active) {
-            int res = inotify_add_watch(nfd, kPackagesListFile, IN_DELETE_SELF);
+            int res = inotify_add_watch(nfd, PACKAGES_LIST_FILE, IN_DELETE_SELF);
             if (res == -1) {
                 if (errno == ENOENT || errno == EACCES) {
                     /* Framework may not have created yet, sleep and retry */
-                    ERROR("missing packages.list; retrying\n");
+                    ERROR("missing \"%s\"; retrying\n", PACKAGES_LIST_FILE);
                     sleep(3);
                     continue;
                 } else {
@@ -1695,8 +1687,8 @@
 
             /* Watch above will tell us about any future changes, so
              * read the current state. */
-            if (read_package_list(global) == -1) {
-                ERROR("read_package_list failed: %s\n", strerror(errno));
+            if (read_package_list(global) == false) {
+                ERROR("read_package_list failed\n");
                 return;
             }
             active = true;
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
index 579d26e..52716e9 100644
--- a/toolbox/Android.mk
+++ b/toolbox/Android.mk
@@ -31,7 +31,6 @@
     dd \
 
 OUR_TOOLS := \
-    df \
     getevent \
     iftop \
     ioctl \
diff --git a/toolbox/df.c b/toolbox/df.c
deleted file mode 100644
index 9cd0743..0000000
--- a/toolbox/df.c
+++ /dev/null
@@ -1,85 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <sys/statfs.h>
-
-static int ok = EXIT_SUCCESS;
-
-static void printsize(long long n)
-{
-    char unit = 'K';
-    long long t;
-
-    n *= 10;
-
-    if (n > 1024*1024*10) {
-        n /= 1024;
-        unit = 'M';
-    }
-
-    if (n > 1024*1024*10) {
-        n /= 1024;
-        unit = 'G';
-    }
-
-    t = (n + 512) / 1024;
-    printf("%4lld.%1lld%c", t/10, t%10, unit);
-}
-
-static void df(char *s, int always) {
-    struct statfs st;
-
-    if (statfs(s, &st) < 0) {
-        fprintf(stderr, "%s: %s\n", s, strerror(errno));
-        ok = EXIT_FAILURE;
-    } else {
-        if (st.f_blocks == 0 && !always)
-            return;        
-        printf("%-20s  ", s);
-        printsize((long long)st.f_blocks * (long long)st.f_bsize);
-        printf("  ");
-        printsize((long long)(st.f_blocks - (long long)st.f_bfree) * st.f_bsize);
-        printf("  ");
-        printsize((long long)st.f_bfree * (long long)st.f_bsize);
-        printf("   %d\n", (int) st.f_bsize);
-    }
-}
-
-int df_main(int argc, char *argv[]) {
-    printf("Filesystem               Size     Used     Free   Blksize\n");
-    if (argc == 1) {
-        char s[2000];
-        FILE *f = fopen("/proc/mounts", "r");
-
-        while (fgets(s, 2000, f)) {
-            char *c, *e = s;
-
-            for (c = s; *c; c++) {
-                if (*c == ' ') {
-                    e = c + 1;
-                    break;
-                }
-            }
-
-            for (c = e; *c; c++) {
-                if (*c == ' ') {
-                    *c = '\0';
-                    break;
-                }
-            }
-
-            df(e, 0);
-        }
-
-        fclose(f);
-    } else {
-        int i;
-
-        for (i = 1; i < argc; i++) {
-            df(argv[i], 1);
-        }
-    }
-
-    exit(ok);
-}
diff --git a/toolbox/ps.c b/toolbox/ps.c
index cf3f05a..3bc540d 100644
--- a/toolbox/ps.c
+++ b/toolbox/ps.c
@@ -41,14 +41,14 @@
 
 static void print_exe_abi(int pid);
 
-static int ps_line(int pid, int tid, char *namefilter)
+static int ps_line(int pid, int tid)
 {
     char statline[1024];
     char cmdline[1024];
     char macline[1024];
     char user[32];
     struct stat stats;
-    int fd, r;
+    int r;
     char *ptr, *name, *state;
     int ppid;
     unsigned rss, vss;
@@ -68,7 +68,7 @@
         sprintf(statline, "/proc/%d/stat", pid);
         sprintf(cmdline, "/proc/%d/cmdline", pid);
         snprintf(macline, sizeof(macline), "/proc/%d/attr/current", pid);
-        fd = open(cmdline, O_RDONLY);
+        int fd = open(cmdline, O_RDONLY);
         if(fd == 0) {
             r = 0;
         } else {
@@ -79,7 +79,7 @@
         cmdline[r] = 0;
     }
 
-    fd = open(statline, O_RDONLY);
+    int fd = open(statline, O_RDONLY);
     if(fd == 0) return -1;
     r = read(fd, statline, 1023);
     close(fd);
@@ -158,51 +158,48 @@
         return 0;
     }
 
-    if(!namefilter || !strncmp(cmdline[0] ? cmdline : name, namefilter, strlen(namefilter))) {
-        if (display_flags & SHOW_MACLABEL) {
-            fd = open(macline, O_RDONLY);
-            strcpy(macline, "-");
-            if (fd >= 0) {
-                r = read(fd, macline, sizeof(macline)-1);
-                close(fd);
-                if (r > 0)
-                    macline[r] = 0;
-            }
-            printf("%-30s %-9s %-5d %-5d %s\n", macline, user, pid, ppid, cmdline[0] ? cmdline : name);
-            return 0;
+    if (display_flags & SHOW_MACLABEL) {
+        fd = open(macline, O_RDONLY);
+        strcpy(macline, "-");
+        if (fd >= 0) {
+            r = read(fd, macline, sizeof(macline)-1);
+            close(fd);
+            if (r > 0)
+                macline[r] = 0;
         }
-
-        printf("%-9s %-5d %-5d %-6d %-5d", user, pid, ppid, vss / 1024, rss * 4);
-        if (display_flags & SHOW_CPU)
-            printf(" %-2d", psr);
-        if (display_flags & SHOW_PRIO)
-            printf(" %-5d %-5d %-5d %-5d", prio, nice, rtprio, sched);
-        if (display_flags & SHOW_POLICY) {
-            SchedPolicy p;
-            if (get_sched_policy(pid, &p) < 0)
-                printf(" un ");
-            else
-                printf(" %.2s ", get_sched_policy_name(p));
-        }
-        char path[PATH_MAX];
-        snprintf(path, sizeof(path), "/proc/%d/wchan", pid);
-        char wchan[10];
-        int fd = open(path, O_RDONLY);
-        ssize_t wchan_len = read(fd, wchan, sizeof(wchan));
-        if (wchan_len == -1) {
-            wchan[wchan_len = 0] = '\0';
-        }
-        close(fd);
-        printf(" %10.*s %0*" PRIxPTR " %s ", (int) wchan_len, wchan, (int) PC_WIDTH, eip, state);
-        if (display_flags & SHOW_ABI) {
-            print_exe_abi(pid);
-        }
-        printf("%s", cmdline[0] ? cmdline : name);
-        if(display_flags&SHOW_TIME)
-            printf(" (u:%d, s:%d)", utime, stime);
-
-        printf("\n");
+        printf("%-30s ", macline);
     }
+
+    printf("%-9s %-5d %-5d %-6d %-5d", user, pid, ppid, vss / 1024, rss * 4);
+    if (display_flags & SHOW_CPU)
+        printf(" %-2d", psr);
+    if (display_flags & SHOW_PRIO)
+        printf(" %-5d %-5d %-5d %-5d", prio, nice, rtprio, sched);
+    if (display_flags & SHOW_POLICY) {
+        SchedPolicy p;
+        if (get_sched_policy(pid, &p) < 0)
+            printf(" un ");
+        else
+            printf(" %.2s ", get_sched_policy_name(p));
+    }
+    char path[PATH_MAX];
+    snprintf(path, sizeof(path), "/proc/%d/wchan", pid);
+    char wchan[10];
+    fd = open(path, O_RDONLY);
+    ssize_t wchan_len = read(fd, wchan, sizeof(wchan));
+    if (wchan_len == -1) {
+        wchan[wchan_len = 0] = '\0';
+    }
+    close(fd);
+    printf(" %10.*s %0*" PRIxPTR " %s ", (int) wchan_len, wchan, (int) PC_WIDTH, eip, state);
+    if (display_flags & SHOW_ABI) {
+        print_exe_abi(pid);
+    }
+    printf("%s", cmdline[0] ? cmdline : name);
+    if(display_flags&SHOW_TIME)
+        printf(" (u:%d, s:%d)", utime, stime);
+
+    printf("\n");
     return 0;
 }
 
@@ -240,7 +237,7 @@
     }
 }
 
-void ps_threads(int pid, char *namefilter)
+void ps_threads(int pid)
 {
     char tmp[128];
     DIR *d;
@@ -254,7 +251,7 @@
         if(isdigit(de->d_name[0])){
             int tid = atoi(de->d_name);
             if(tid == pid) continue;
-            ps_line(pid, tid, namefilter);
+            ps_line(pid, tid);
         }
     }
     closedir(d);
@@ -264,7 +261,6 @@
 {
     DIR *d;
     struct dirent *de;
-    char *namefilter = 0;
     int pidfilter = 0;
     int threads = 0;
 
@@ -290,33 +286,39 @@
             display_flags |= SHOW_ABI;
         } else if(!strcmp(argv[1],"--ppid")) {
             ppid_filter = atoi(argv[2]);
+            if (ppid_filter == 0) {
+                fprintf(stderr, "bad ppid '%s'\n", argv[2]);
+                return 1;
+            }
             argc--;
             argv++;
-        } else if(isdigit(argv[1][0])){
-            pidfilter = atoi(argv[1]);
         } else {
-            namefilter = argv[1];
+            pidfilter = atoi(argv[1]);
+            if (pidfilter == 0) {
+                fprintf(stderr, "bad pid '%s'\n", argv[1]);
+                return 1;
+            }
         }
         argc--;
         argv++;
     }
 
     if (display_flags & SHOW_MACLABEL) {
-        printf("LABEL                          USER      PID   PPID  NAME\n");
-    } else {
-        printf("USER      PID   PPID  VSIZE  RSS  %s%s %sWCHAN      %*s  %sNAME\n",
-               (display_flags&SHOW_CPU)?"CPU ":"",
-               (display_flags&SHOW_PRIO)?"PRIO  NICE  RTPRI SCHED ":"",
-               (display_flags&SHOW_POLICY)?"PCY " : "",
-               (int) PC_WIDTH, "PC",
-               (display_flags&SHOW_ABI)?"ABI " : "");
+        printf("LABEL                          ");
     }
+    printf("USER      PID   PPID  VSIZE  RSS  %s%s %sWCHAN      %*s  %sNAME\n",
+           (display_flags&SHOW_CPU)?"CPU ":"",
+           (display_flags&SHOW_PRIO)?"PRIO  NICE  RTPRI SCHED ":"",
+           (display_flags&SHOW_POLICY)?"PCY " : "",
+           (int) PC_WIDTH, "PC",
+           (display_flags&SHOW_ABI)?"ABI " : "");
+
     while((de = readdir(d)) != 0){
         if(isdigit(de->d_name[0])){
             int pid = atoi(de->d_name);
             if(!pidfilter || (pidfilter == pid)) {
-                ps_line(pid, 0, namefilter);
-                if(threads) ps_threads(pid, namefilter);
+                ps_line(pid, 0);
+                if(threads) ps_threads(pid);
             }
         }
     }