Merge "Revert "Add libtrusty and corresponding test utility""
diff --git a/adb/.clang-format b/adb/.clang-format
index 0395c8e..6737535 100644
--- a/adb/.clang-format
+++ b/adb/.clang-format
@@ -2,6 +2,7 @@
 AllowShortBlocksOnASingleLine: false
 AllowShortFunctionsOnASingleLine: false
 
+ColumnLimit: 100
 CommentPragmas: NOLINT:.*
 DerivePointerAlignment: false
 IndentWidth: 4
diff --git a/adb/Android.mk b/adb/Android.mk
index e10e3ef..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 += \
@@ -296,9 +298,9 @@
     libfs_mgr \
     libfec \
     libfec_rs \
+    libselinux \
     liblog \
     libmincrypt \
-    libselinux \
     libext4_utils_static \
     libsquashfs_utils \
     libcutils \
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.h b/adb/adb.h
index c284c8c..491fff3 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -50,7 +50,7 @@
 std::string adb_version();
 
 // Increment this when we want to force users to start a new adb server.
-#define ADB_SERVER_VERSION 34
+#define ADB_SERVER_VERSION 35
 
 class atransport;
 struct usb_handle;
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_trace.cpp b/adb/adb_trace.cpp
index 04b82f6..cf99df7 100644
--- a/adb/adb_trace.cpp
+++ b/adb/adb_trace.cpp
@@ -157,13 +157,13 @@
     // 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();
         }
     }
 #endif
 
-    android::base::InitLogging(argv, AdbLogger);
+    android::base::InitLogging(argv, &AdbLogger);
     setup_trace_mask();
 
     VLOG(ADB) << adb_version();
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index f7b2e4e..42f1c7d 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;
@@ -163,11 +182,8 @@
     line.push_back(' ');
 
     for (size_t i = 0; i < byte_count; ++i) {
-        int c = p[i];
-        if (c < 32 || c > 127) {
-            c = '.';
-        }
-        line.push_back(c);
+        int ch = p[i];
+        line.push_back(isprint(ch) ? ch : '.');
     }
 
     return line;
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 d128df7..6e4c4e8 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
@@ -56,6 +59,8 @@
 static int install_app(TransportType t, const char* serial, int argc, const char** argv);
 static int install_multiple_app(TransportType t, const char* serial, int argc, const char** argv);
 static int uninstall_app(TransportType t, const char* serial, int argc, const char** argv);
+static int install_app_legacy(TransportType t, const char* serial, int argc, const char** argv);
+static int uninstall_app_legacy(TransportType t, const char* serial, int argc, const char** argv);
 
 static std::string gProductOutPath;
 extern int gListenAll;
@@ -100,20 +105,19 @@
         "                                 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"
-        "  adb shell [-Ttx] <command>   - run remote shell command\n"
-        "                                 (-T disables PTY allocation)\n"
-        "                                 (-t forces PTY allocation)\n"
-        "                                 (-x disables remote exit codes and stdout/stderr separation)\n"
+        "  adb shell [-e escape] [-Tt] [-x] [command]\n"
+        "                               - run remote shell command (interactive shell if no command given)\n"
+        "                                 (-e: choose escape character, or \"none\"; default '~')\n"
+        "                                 (-T: disable PTY allocation)\n"
+        "                                 (-t: force PTY allocation)\n"
+        "                                 (-x: disable remote exit codes and stdout/stderr separation)\n"
         "  adb emu <command>            - run emulator console command\n"
         "  adb logcat [ <filter-spec> ] - View device log\n"
         "  adb forward --list           - list all forward socket connections.\n"
@@ -139,7 +143,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 +251,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 +269,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 +362,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 +404,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,39 +424,110 @@
     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 {
     int stdin_fd, write_fd;
     bool raw_stdin;
     std::unique_ptr<ShellProtocol> protocol;
+    char escape_char;
 };
 
-}  // 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
 
-    char raw_buffer[1024];
+#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[BUFSIZ];
     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();
     }
 
+    // If we need to parse escape sequences, make life easy.
+    if (args->raw_stdin && args->escape_char != '\0') {
+        buffer_size = 1;
+    }
+
+    enum EscapeState { kMidFlow, kStartOfLine, kInEscape };
+    EscapeState state = kStartOfLine;
+
     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
@@ -463,32 +538,36 @@
             }
             break;
         }
-        // If we made stdin raw, check input for the "~." escape sequence. In
+        // If we made stdin raw, check input for escape sequences. In
         // this situation signals like Ctrl+C are sent remotely rather than
         // interpreted locally so this provides an emergency out if the remote
         // 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]) {
-                case '\n':
-                    state = 1;
-                    break;
-                case '\r':
-                    state = 1;
-                    break;
-                case '~':
-                    if(state == 1) state++;
-                    break;
-                case '.':
-                    if(state == 2) {
-                        stdin_raw_restore(args->stdin_fd);
-                        fprintf(stderr,"\n* disconnect *\n");
-                        exit(0);
-                    }
-                default:
-                    state = 0;
+        if (args->raw_stdin && args->escape_char != '\0') {
+            char ch = buffer_ptr[0];
+            if (ch == args->escape_char) {
+                if (state == kStartOfLine) {
+                    state = kInEscape;
+                    // Swallow the escape character.
+                    continue;
+                } else {
+                    state = kMidFlow;
                 }
+            } else {
+                if (state == kInEscape) {
+                    if (ch == '.') {
+                        fprintf(stderr,"\r\n[ disconnected ]\r\n");
+                        stdin_raw_restore();
+                        exit(0);
+                    } else {
+                        // We swallowed an escape character that wasn't part of
+                        // a valid escape sequence; time to cough it up.
+                        buffer_ptr[0] = args->escape_char;
+                        buffer_ptr[1] = ch;
+                        ++r;
+                    }
+                }
+                state = (ch == '\n' || ch == '\r') ? kStartOfLine : kMidFlow;
             }
         }
         if (args->protocol) {
@@ -533,6 +612,7 @@
 // On success returns the remote exit code if |use_shell_protocol| is true,
 // 0 otherwise. On failure returns 1.
 static int RemoteShell(bool use_shell_protocol, const std::string& type_arg,
+                       char escape_char,
                        const std::string& command) {
     std::string service_string = ShellServiceString(use_shell_protocol,
                                                     type_arg, command);
@@ -558,57 +638,135 @@
     args->stdin_fd = STDIN_FILENO;
     args->write_fd = fd;
     args->raw_stdin = raw_stdin;
+    args->escape_char = escape_char;
     if (use_shell_protocol) {
         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;
+    char escape_char = '~';
+    while (argc) {
+        if (!strcmp(argv[0], "-e")) {
+            if (argc < 2 || !(strlen(argv[1]) == 1 || strcmp(argv[1], "none") == 0)) {
+                fprintf(stderr, "error: -e requires a single-character argument or 'none'\n");
+                return 1;
+            }
+            escape_char = (strcmp(argv[1], "none") == 0) ? 0 : argv[1][0];
+            argc -= 2;
+            argv += 2;
+        } else 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, escape_char, command);
 }
 
 static int adb_download_buffer(const char *service, const char *fn, const void* data, unsigned sz,
@@ -638,6 +796,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 +812,7 @@
 
     if (!adb_status(fd, &error)) {
         fprintf(stderr,"* error response '%s' *\n", error.c_str());
+        adb_close(fd);
         return -1;
     }
 
@@ -936,7 +1096,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 +1211,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 +1488,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 +1534,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,26 +1625,30 @@
         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();
-        return install_app(transport_type, serial, argc, argv);
+        FeatureSet features = GetFeatureSet(transport_type, serial);
+        if (CanUseFeature(features, kFeatureCmd)) {
+            return install_app(transport_type, serial, argc, argv);
+        }
+        return install_app_legacy(transport_type, serial, argc, argv);
     }
     else if (!strcmp(argv[0], "install-multiple")) {
         if (argc < 2) return usage();
@@ -1543,7 +1656,11 @@
     }
     else if (!strcmp(argv[0], "uninstall")) {
         if (argc < 2) return usage();
-        return uninstall_app(transport_type, serial, argc, argv);
+        FeatureSet features = GetFeatureSet(transport_type, serial);
+        if (CanUseFeature(features, kFeatureCmd)) {
+            return uninstall_app(transport_type, serial, argc, argv);
+        }
+        return uninstall_app_legacy(transport_type, serial, argc, argv);
     }
     else if (!strcmp(argv[0], "sync")) {
         std::string src;
@@ -1651,85 +1768,83 @@
     return 1;
 }
 
-static int pm_command(TransportType transport, const char* serial, int argc, const char** argv) {
-    std::string cmd = "pm";
-
+static int uninstall_app(TransportType transport, const char* serial, int argc, const char** argv) {
+    // 'adb uninstall' takes the same arguments as 'cmd package uninstall' on device
+    std::string cmd = "cmd package";
     while (argc-- > 0) {
+        // deny the '-k' option until the remaining data/cache can be removed with adb/UI
+        if (strcmp(*argv, "-k") == 0) {
+            printf(
+                "The -k option uninstalls the application while retaining the data/cache.\n"
+                "At the moment, there is no way to remove the remaining data.\n"
+                "You will have to reinstall the application with the same signature, and fully uninstall it.\n"
+                "If you truly wish to continue, execute 'adb shell cmd package uninstall -k'.\n");
+            return EXIT_FAILURE;
+        }
         cmd += " " + escape_arg(*argv++);
     }
 
-    // TODO(dpursell): add command-line arguments to install/uninstall to
-    // manually disable shell protocol if needed.
-    return send_shell_command(transport, serial, cmd, false);
-}
-
-static int uninstall_app(TransportType transport, const char* serial, int argc, const char** argv) {
-    /* if the user choose the -k option, we refuse to do it until devices are
-       out with the option to uninstall the remaining data somehow (adb/ui) */
-    if (argc == 3 && strcmp(argv[1], "-k") == 0)
-    {
-        printf(
-            "The -k option uninstalls the application while retaining the data/cache.\n"
-            "At the moment, there is no way to remove the remaining data.\n"
-            "You will have to reinstall the application with the same signature, and fully uninstall it.\n"
-            "If you truly wish to continue, execute 'adb shell pm uninstall -k %s'\n", argv[2]);
-        return -1;
-    }
-
-    /* 'adb uninstall' takes the same arguments as 'pm uninstall' on device */
-    return pm_command(transport, serial, argc, argv);
-}
-
-static int delete_file(TransportType transport, const char* serial, const std::string& filename) {
-    std::string cmd = "rm -f " + escape_arg(filename);
     return send_shell_command(transport, serial, cmd, false);
 }
 
 static int install_app(TransportType transport, const char* serial, int argc, const char** argv) {
-    static const char *const DATA_DEST = "/data/local/tmp/%s";
-    static const char *const SD_DEST = "/sdcard/tmp/%s";
-    const char* where = DATA_DEST;
-    int i;
+    // The last argument must be the APK file
+    const char* file = argv[argc - 1];
+    const char* dot = strrchr(file, '.');
+    bool found_apk = false;
     struct stat sb;
-
-    for (i = 1; i < argc; i++) {
-        if (!strcmp(argv[i], "-s")) {
-            where = SD_DEST;
+    if (dot && !strcasecmp(dot, ".apk")) {
+        if (stat(file, &sb) == -1 || !S_ISREG(sb.st_mode)) {
+            fprintf(stderr, "Invalid APK file: %s\n", file);
+            return EXIT_FAILURE;
         }
+        found_apk = true;
     }
 
-    // Find last APK argument.
-    // All other arguments passed through verbatim.
-    int last_apk = -1;
-    for (i = argc - 1; i >= 0; i--) {
-        const char* file = argv[i];
-        const char* dot = strrchr(file, '.');
-        if (dot && !strcasecmp(dot, ".apk")) {
-            if (stat(file, &sb) == -1 || !S_ISREG(sb.st_mode)) {
-                fprintf(stderr, "Invalid APK file: %s\n", file);
-                return -1;
-            }
-
-            last_apk = i;
-            break;
-        }
-    }
-
-    if (last_apk == -1) {
+    if (!found_apk) {
         fprintf(stderr, "Missing APK file\n");
-        return -1;
+        return EXIT_FAILURE;
     }
 
-    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;
-    argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */
-    result = pm_command(transport, serial, argc, argv);
+    int localFd = adb_open(file, O_RDONLY);
+    if (localFd < 0) {
+        fprintf(stderr, "Failed to open %s: %s\n", file, strerror(errno));
+        return 1;
+    }
 
-cleanup_apk:
-    delete_file(transport, serial, apk_dest);
-    return result;
+    std::string error;
+    std::string cmd = "exec:cmd package";
+
+    // don't copy the APK name, but, copy the rest of the arguments as-is
+    while (argc-- > 1) {
+        cmd += " " + escape_arg(std::string(*argv++));
+    }
+
+    // add size parameter [required for streaming installs]
+    // do last to override any user specified value
+    cmd += " " + android::base::StringPrintf("-S %" PRIu64, static_cast<uint64_t>(sb.st_size));
+
+    int remoteFd = adb_connect(cmd, &error);
+    if (remoteFd < 0) {
+        fprintf(stderr, "Connect error for write: %s\n", error.c_str());
+        adb_close(localFd);
+        return 1;
+    }
+
+    char buf[BUFSIZ];
+    copy_to_file(localFd, remoteFd);
+    read_status_line(remoteFd, buf, sizeof(buf));
+
+    adb_close(localFd);
+    adb_close(remoteFd);
+
+    if (strncmp("Success", buf, 7)) {
+        fprintf(stderr, "Failed to write %s\n", file);
+        fputs(buf, stderr);
+        return 1;
+    }
+    fputs(buf, stderr);
+    return 0;
 }
 
 static int install_multiple_app(TransportType transport, const char* serial, int argc,
@@ -1748,7 +1863,7 @@
         if (dot && !strcasecmp(dot, ".apk")) {
             if (stat(file, &sb) == -1 || !S_ISREG(sb.st_mode)) {
                 fprintf(stderr, "Invalid APK file: %s\n", file);
-                return -1;
+                return EXIT_FAILURE;
             }
 
             total_size += sb.st_size;
@@ -1773,7 +1888,7 @@
     int fd = adb_connect(cmd, &error);
     if (fd < 0) {
         fprintf(stderr, "Connect error for create: %s\n", error.c_str());
-        return -1;
+        return EXIT_FAILURE;
     }
     char buf[BUFSIZ];
     read_status_line(fd, buf, sizeof(buf));
@@ -1791,7 +1906,7 @@
     if (session_id < 0) {
         fprintf(stderr, "Failed to create session\n");
         fputs(buf, stderr);
-        return -1;
+        return EXIT_FAILURE;
     }
 
     // Valid session, now stream the APKs
@@ -1846,7 +1961,7 @@
     fd = adb_connect(service, &error);
     if (fd < 0) {
         fprintf(stderr, "Connect error for finalize: %s\n", error.c_str());
-        return -1;
+        return EXIT_FAILURE;
     }
     read_status_line(fd, buf, sizeof(buf));
     adb_close(fd);
@@ -1857,6 +1972,88 @@
     } else {
         fprintf(stderr, "Failed to finalize session\n");
         fputs(buf, stderr);
-        return -1;
+        return EXIT_FAILURE;
     }
 }
+
+static int pm_command(TransportType transport, const char* serial, int argc, const char** argv) {
+    std::string cmd = "pm";
+
+    while (argc-- > 0) {
+        cmd += " " + escape_arg(*argv++);
+    }
+
+    return send_shell_command(transport, serial, cmd, false);
+}
+
+static int uninstall_app_legacy(TransportType transport, const char* serial, int argc, const char** argv) {
+    /* if the user choose the -k option, we refuse to do it until devices are
+       out with the option to uninstall the remaining data somehow (adb/ui) */
+    int i;
+    for (i = 1; i < argc; i++) {
+        if (!strcmp(argv[i], "-k")) {
+            printf(
+                "The -k option uninstalls the application while retaining the data/cache.\n"
+                "At the moment, there is no way to remove the remaining data.\n"
+                "You will have to reinstall the application with the same signature, and fully uninstall it.\n"
+                "If you truly wish to continue, execute 'adb shell pm uninstall -k'\n.");
+            return EXIT_FAILURE;
+        }
+    }
+
+    /* 'adb uninstall' takes the same arguments as 'pm uninstall' on device */
+    return pm_command(transport, serial, argc, argv);
+}
+
+static int delete_file(TransportType transport, const char* serial, const std::string& filename) {
+    std::string cmd = "rm -f " + escape_arg(filename);
+    return send_shell_command(transport, serial, cmd, false);
+}
+
+static int install_app_legacy(TransportType transport, const char* serial, int argc, const char** argv) {
+    static const char *const DATA_DEST = "/data/local/tmp/%s";
+    static const char *const SD_DEST = "/sdcard/tmp/%s";
+    const char* where = DATA_DEST;
+    int i;
+    struct stat sb;
+
+    for (i = 1; i < argc; i++) {
+        if (!strcmp(argv[i], "-s")) {
+            where = SD_DEST;
+        }
+    }
+
+    // Find last APK argument.
+    // All other arguments passed through verbatim.
+    int last_apk = -1;
+    for (i = argc - 1; i >= 0; i--) {
+        const char* file = argv[i];
+        const char* dot = strrchr(file, '.');
+        if (dot && !strcasecmp(dot, ".apk")) {
+            if (stat(file, &sb) == -1 || !S_ISREG(sb.st_mode)) {
+                fprintf(stderr, "Invalid APK file: %s\n", file);
+                return EXIT_FAILURE;
+            }
+
+            last_apk = i;
+            break;
+        }
+    }
+
+    if (last_apk == -1) {
+        fprintf(stderr, "Missing APK file\n");
+        return EXIT_FAILURE;
+    }
+
+    int result = -1;
+    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);
+
+cleanup_apk:
+    delete_file(transport, serial, apk_dest);
+    return result;
+}
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/daemon/main.cpp b/adb/daemon/main.cpp
index 8c3ca63..f4e054e3 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -142,11 +142,13 @@
     // AID_SDCARD_R to allow reading from the SD card
     // AID_SDCARD_RW to allow writing to the SD card
     // AID_NET_BW_STATS to read out qtaguid statistics
+    // AID_READPROC for reading /proc entries across UID boundaries
     gid_t groups[] = {AID_ADB,      AID_LOG,       AID_INPUT,
                       AID_INET,     AID_NET_BT,    AID_NET_BT_ADMIN,
-                      AID_SDCARD_R, AID_SDCARD_RW, AID_NET_BW_STATS};
+                      AID_SDCARD_R, AID_SDCARD_RW, AID_NET_BW_STATS,
+                      AID_READPROC };
     if (setgroups(sizeof(groups) / sizeof(groups[0]), groups) != 0) {
-        PLOG(FATAL) << "Could not set supplental groups";
+        PLOG(FATAL) << "Could not set supplemental groups";
     }
 
     /* don't listen on a port (default 5037) if running in secure mode */
@@ -166,7 +168,7 @@
     } else {
         if (root_seclabel != nullptr) {
             if (setcon(root_seclabel) < 0) {
-                LOG(FATAL) << "Could not set selinux context";
+                LOG(FATAL) << "Could not set SELinux context";
             }
         }
         std::string error;
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..736411c 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,78 @@
     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);
+    }
+
+    void Warning(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) {
+        std::string s = "adb: warning: ";
+
+        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 +227,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 +262,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 +285,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 +314,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 +328,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 +340,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 +348,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,376 +357,424 @@
         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;
+static copyinfo mkcopyinfo(const std::string& spath, const std::string& dpath,
+                           const std::string& name, unsigned int mode) {
+    copyinfo result;
+    result.src = spath;
+    result.dst = dpath;
 
-    copyinfo *ci = reinterpret_cast<copyinfo*>(malloc(sizeof(copyinfo) + ssize + dsize));
-    if(ci == 0) {
-        fprintf(stderr, "out of memory\n");
-        abort();
+    // FIXME(b/25573669): This is probably broken on win32?
+    if (result.src.back() != '/') {
+      result.src.push_back('/');
+    }
+    if (result.dst.back() != '/') {
+      result.dst.push_back('/');
+    }
+    result.src.append(name);
+    result.dst.append(name);
+
+    bool isdir = S_ISDIR(mode);
+    if (isdir) {
+        result.src.push_back('/');
+        result.dst.push_back('/');
     }
 
-    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;
+    result.time = 0;
+    result.mode = mode;
+    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 bool local_build_list(SyncConnection& sc, std::vector<copyinfo>* filelist,
+                             const std::string& lpath,
+                             const std::string& rpath) {
+    std::vector<copyinfo> dirlist;
+    std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(lpath.c_str()), closedir);
     if (!dir) {
-        fprintf(stderr, "cannot open '%s': %s\n", lpath, strerror(errno));
-        return -1;
+        sc.Error("cannot open '%s': %s", lpath.c_str(), strerror(errno));
+        return false;
     }
 
-    dirent *de;
+    bool empty_dir = true;
+    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);
+        if (IsDotOrDotDot(de->d_name)) {
             continue;
         }
-        strcpy(stat_path, lpath);
-        strcat(stat_path, de->d_name);
+
+        empty_dir = false;
+        std::string stat_path = lpath + de->d_name;
 
         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;
-            } 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);
-                } else {
-                    ci->time = st.st_mtime;
-                    ci->mode = st.st_mode;
-                    ci->size = st.st_size;
-                    ci->next = *filelist;
-                    *filelist = ci;
-                }
-            }
+        if (lstat(stat_path.c_str(), &st) == -1) {
+            sc.Error("cannot lstat '%s': %s", stat_path.c_str(),
+                     strerror(errno));
+            continue;
+        }
+
+        copyinfo ci = mkcopyinfo(lpath, rpath, de->d_name, st.st_mode);
+        if (S_ISDIR(st.st_mode)) {
+            dirlist.push_back(ci);
         } else {
-            fprintf(stderr, "cannot lstat '%s': %s\n",stat_path , strerror(errno));
+            if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
+                sc.Error("skipping special file '%s'", lpath.c_str());
+            } else {
+                ci.time = st.st_mtime;
+                ci.size = st.st_size;
+                filelist->push_back(ci);
+            }
         }
     }
 
     // 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);
+
+    // Add the current directory to the list if it was empty, to ensure that
+    // it gets created.
+    if (empty_dir) {
+        // TODO(b/25566053): Make pushing empty directories work.
+        // TODO(b/25457350): We don't preserve permissions on directories.
+        sc.Warning("skipping empty directory '%s'", lpath.c_str());
+        copyinfo ci = mkcopyinfo(adb_dirname(lpath), adb_dirname(rpath),
+                                 adb_basename(lpath), S_IFDIR);
+        ci.skip = true;
+        filelist->push_back(ci);
+        return true;
     }
 
-    return 0;
+    for (const copyinfo& ci : dirlist) {
+        local_build_list(sc, filelist, ci.src.c_str(), ci.dst.c_str());
+    }
+
+    return true;
 }
 
-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;
+static bool copy_local_dir_remote(SyncConnection& sc, std::string lpath,
+                                  std::string rpath, bool check_timestamps,
+                                  bool list_only) {
+    // Make sure that both directory paths end in a slash.
+    // Both paths are known to be nonempty.
+    //
+    // FIXME(b/25573669): This is probably broken on win32?
+    if (lpath.back() != '/') {
+        lpath.push_back('/');
+    }
+    if (rpath.back() != '/') {
+        rpath.push_back('/');
+    }
+
+    // Recursively build the list of files to copy.
+    std::vector<copyinfo> filelist;
     int pushed = 0;
     int skipped = 0;
-
-    if ((lpath[0] == 0) || (rpath[0] == 0)) return false;
-    if (lpath[strlen(lpath) - 1] != '/') {
-        int  tmplen = strlen(lpath)+2;
-        char *tmp = reinterpret_cast<char*>(malloc(tmplen));
-        if(tmp == 0) return false;
-        snprintf(tmp, tmplen, "%s/",lpath);
-        lpath = tmp;
-    }
-    if (rpath[strlen(rpath) - 1] != '/') {
-        int tmplen = strlen(rpath)+2;
-        char *tmp = reinterpret_cast<char*>(malloc(tmplen));
-        if(tmp == 0) return false;
-        snprintf(tmp, tmplen, "%s/",rpath);
-        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 (!sync_finish_stat(sc, &timestamp, &mode, &size)) {
+                return false;
+            }
+            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) {
+                sc.Error("would push: %s -> %s", 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.c_str(),
+              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;
+    bool success = true;
+    unsigned dst_mode;
+    if (!sync_stat(sc, dst, nullptr, &dst_mode, nullptr)) return false;
+    bool dst_exists = (dst_mode != 0);
+    bool dst_isdir = S_ISDIR(dst_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);
+
+            // A path that ends with a slash doesn't have to be a directory if
+            // it doesn't exist yet.
+            if (dst[dst_len - 1] == '/' && dst_exists) {
+                sc.Error("failed to access '%s': Not a directory", dst);
+                return false;
+            }
+        }
     }
 
-    if (S_ISDIR(st.st_mode)) {
-        return copy_local_dir_remote(sc, lpath, rpath, false, false);
+    for (const char* src_path : srcs) {
+        const char* dst_path = dst;
+        struct stat st;
+        if (stat(src_path, &st) == -1) {
+            sc.Error("cannot stat '%s': %s", src_path, strerror(errno));
+            success = false;
+            continue;
+        }
+
+        if (S_ISDIR(st.st_mode)) {
+            std::string dst_dir = dst;
+
+            // If the destination path existed originally, the source directory
+            // should be copied as a child of the destination.
+            if (dst_exists) {
+                if (!dst_isdir) {
+                    sc.Error("target '%s' is not a directory", dst);
+                    return false;
+                }
+                // dst is a POSIX path, so we don't want to use the sysdeps
+                // helpers here.
+                if (dst_dir.back() != '/') {
+                    dst_dir.push_back('/');
+                }
+                dst_dir.append(adb_basename(src_path));
+            }
+
+            success &= copy_local_dir_remote(sc, src_path, dst_dir.c_str(),
+                                             false, false);
+            continue;
+        }
+
+        std::string path_holder;
+        if (dst_isdir) {
+            // 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);
     }
 
-    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();
-    }
-    return sync_send(sc, lpath, rpath, st.st_mtime, st.st_mode, show_progress);
+    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 std::string& rpath,
+                              const std::string& lpath) {
+    std::vector<copyinfo> dirlist;
+    bool empty_dir = true;
 
     // 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 (IsDotOrDotDot(name)) {
+            return;
+        }
+
+        // We found a child that isn't '.' or '..'.
+        empty_dir = false;
+
+        copyinfo ci = mkcopyinfo(rpath, lpath, name, mode);
+        if (S_ISDIR(mode)) {
+            dirlist.push_back(ci);
+        } else if (S_ISREG(mode) || S_ISLNK(mode)) {
+            ci.time = time;
+            ci.size = size;
+            filelist->push_back(ci);
+        } else {
+            sc.Warning("skipping special file '%s'\n", name);
+        }
+    };
+
+    if (!sync_ls(sc, rpath.c_str(), callback)) {
         return false;
     }
 
+    // Add the current directory to the list if it was empty, to ensure that
+    // it gets created.
+    if (empty_dir) {
+        auto rdname = adb_dirname(rpath);
+        auto ldname = adb_dirname(lpath);
+        auto rbasename = adb_basename(rpath);
+        auto lbasename = adb_basename(lpath);
+        filelist->push_back(mkcopyinfo(adb_dirname(rpath), adb_dirname(lpath),
+                                       adb_basename(rpath), S_IFDIR));
+        return true;
+    }
+
     // 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,98 +786,180 @@
     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);
 
-    return r1 ? : r2;
+    return r1 ? r1 : r2;
 }
 
-static bool copy_remote_dir_local(SyncConnection& sc, const char* rpath, const char* lpath,
-                                  int copy_attrs) {
+static bool copy_remote_dir_local(SyncConnection& sc, std::string rpath,
+                                  std::string lpath, bool copy_attrs) {
     // Make sure that both directory paths end in a slash.
-    std::string rpath_clean(rpath);
-    std::string lpath_clean(lpath);
-    if (rpath_clean.empty() || lpath_clean.empty()) return false;
-    if (rpath_clean.back() != '/') rpath_clean.push_back('/');
-    if (lpath_clean.back() != '/') lpath_clean.push_back('/');
+    // Both paths are known to be nonempty, so we don't need to check.
+    if (rpath.back() != '/') {
+        rpath.push_back('/');
+    }
+
+    // FIXME(b/25573669): This is probably broken on win32?
+    if (lpath.back() != '/') {
+        lpath.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.c_str(), lpath.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 (S_ISDIR(ci.mode)) {
+                // Entry is for an empty directory, create it and continue.
+                // TODO(b/25457350): We don't preserve permissions on directories.
+                if (!mkdirs(ci.dst))  {
+                    sc.Error("failed to create directory '%s': %s",
+                             ci.dst.c_str(), strerror(errno));
+                    return false;
+                }
+                pulled++;
+                continue;
+            }
+
+            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.c_str(),
+              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;
 
-    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;
+    bool success = true;
+    struct stat st;
+    bool dst_exists = true;
+
+    if (stat(dst, &st) == -1) {
+        dst_exists = false;
+
+        // If we're only pulling one path, the destination path might point to
+        // a path that doesn't exist yet.
+        if (srcs.size() == 1 && errno == ENOENT) {
+            // However, its parent must exist.
+            struct stat parent_st;
+            if (stat(adb_dirname(dst).c_str(), &parent_st) == -1) {
+                sc.Error("cannot create file/directory '%s': %s", dst, strerror(errno));
+                return false;
+            }
+        } else {
+            sc.Error("failed to access '%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 = dst_exists && 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);
+
+            // A path that ends with a slash doesn't have to be a directory if
+            // it doesn't exist yet.
+            if (dst[dst_len - 1] == '/' && dst_exists) {
+                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;
+        unsigned src_mode, src_time;
+        if (!sync_stat(sc, src_path, &src_time, &src_mode, nullptr)) {
+            return false;
+        }
+        if (src_mode == 0) {
+            sc.Error("remote object '%s' does not exist", src_path);
+            success = false;
+            continue;
+        }
+
+        if (S_ISREG(src_mode) || S_ISLNK(src_mode)) {
+            // TODO(b/25601283): symlinks shouldn't be handled as files.
+            std::string path_holder;
+            if (dst_isdir) {
+                // 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, src_time, src_mode) != 0) {
+                    success = false;
+                    continue;
+                }
+            }
+        } else if (S_ISDIR(src_mode)) {
+            std::string dst_dir = dst;
+
+            // If the destination path existed originally, the source directory
+            // should be copied as a child of the destination.
+            if (dst_exists) {
+                if (!dst_isdir) {
+                    sc.Error("target '%s' is not a directory", dst);
+                    return false;
+                }
+                if (!adb_is_separator(dst_dir.back())) {
+                    dst_dir.push_back(OS_PATH_SEPARATOR);
+                }
+                dst_dir.append(adb_basename(src_path));
+            }
+
+            success &= copy_remote_dir_local(sc, src_path, dst_dir.c_str(),
+                                             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;
 
-    return copy_local_dir_remote(sc, lpath.c_str(), rpath.c_str(), true, list_only);
+    return copy_local_dir_remote(sc, lpath, rpath, true, list_only);
 }
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/mutex_list.h b/adb/mutex_list.h
index 79c48d8..b59c9f2 100644
--- a/adb/mutex_list.h
+++ b/adb/mutex_list.h
@@ -6,6 +6,7 @@
 #ifndef ADB_MUTEX
 #error ADB_MUTEX not defined when including this file
 #endif
+ADB_MUTEX(basename_lock)
 ADB_MUTEX(dirname_lock)
 ADB_MUTEX(socket_list_lock)
 ADB_MUTEX(transport_lock)
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/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/sockets.cpp b/adb/sockets.cpp
index f8c2f64..eb0ce85 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -25,6 +25,8 @@
 #include <string.h>
 #include <unistd.h>
 
+#include <algorithm>
+
 #if !ADB_HOST
 #include "cutils/properties.h"
 #endif
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 51d09a6..e40fbbc 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -81,6 +81,10 @@
 #define OS_PATH_SEPARATOR_STR "\\"
 #define ENV_PATH_SEPARATOR_STR ";"
 
+static __inline__ bool adb_is_separator(char c) {
+    return c == '\\' || c == '/';
+}
+
 typedef CRITICAL_SECTION          adb_mutex_t;
 
 #define  ADB_MUTEX_DEFINE(x)     adb_mutex_t   x
@@ -181,6 +185,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);
@@ -298,7 +313,12 @@
 #define closedir adb_closedir
 #define rewinddir rewinddir_utf8_not_yet_implemented
 #define telldir telldir_utf8_not_yet_implemented
-#define seekdir seekdir_utf8_not_yet_implemented
+// Some compiler's C++ headers have members named seekdir, so we can't do the
+// macro technique and instead cause a link error if seekdir is called.
+inline void seekdir(DIR*, long) {
+    extern int seekdir_utf8_not_yet_implemented;
+    seekdir_utf8_not_yet_implemented = 1;
+}
 
 #define utime adb_utime
 #define chmod adb_chmod
@@ -409,6 +429,10 @@
 #define OS_PATH_SEPARATOR_STR "/"
 #define ENV_PATH_SEPARATOR_STR ":"
 
+static __inline__ bool adb_is_separator(char c) {
+    return c == '/';
+}
+
 typedef  pthread_mutex_t          adb_mutex_t;
 
 #define  ADB_MUTEX_INITIALIZER    PTHREAD_MUTEX_INITIALIZER
@@ -551,6 +575,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 fd75320..0634da5 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -35,6 +35,7 @@
 #include <base/logging.h>
 #include <base/stringprintf.h>
 #include <base/strings.h>
+#include <base/utf8.h>
 
 #include "adb.h"
 
@@ -567,12 +568,11 @@
 
     // Lookup the string for an unknown error.
     char* errmsg = strerror(-1);
-    char unknown_error[(errmsg == nullptr ? 0 : strlen(errmsg)) + 1];
-    strcpy(unknown_error, errmsg == nullptr ? "" : errmsg);
+    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) && strcmp(errmsg, unknown_error)) {
+    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 {
@@ -2500,20 +2500,61 @@
 //
 // 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")
 
-// Read an input record from the console; one that should be processed.
-static bool _get_interesting_input_record_uncached(const HANDLE console,
-    INPUT_RECORD* const input_record) {
+    // 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;
+}
+
+// Get the next KEY_EVENT_RECORD that should be processed.
+static bool _get_key_event_record(const HANDLE console, INPUT_RECORD* const input_record) {
     for (;;) {
         DWORD read_count = 0;
         memset(input_record, 0, sizeof(*input_record));
         if (!ReadConsoleInputA(console, input_record, 1, &read_count)) {
-            D("_get_interesting_input_record_uncached: ReadConsoleInputA() "
-              "failed: %s\n", SystemErrorCodeToString(GetLastError()).c_str());
+            D("_get_key_event_record: ReadConsoleInputA() failed: %s\n",
+              SystemErrorCodeToString(GetLastError()).c_str());
             errno = EIO;
             return false;
         }
@@ -2539,28 +2580,6 @@
     }
 }
 
-// Cached input record (in case _console_read() is passed a buffer that doesn't
-// have enough space to fit wRepeatCount number of key sequences). A non-zero
-// wRepeatCount indicates that a record is cached.
-static INPUT_RECORD _win32_input_record;
-
-// Get the next KEY_EVENT_RECORD that should be processed.
-static KEY_EVENT_RECORD* _get_key_event_record(const HANDLE console) {
-    // If nothing cached, read directly from the console until we get an
-    // interesting record.
-    if (_win32_input_record.Event.KeyEvent.wRepeatCount == 0) {
-        if (!_get_interesting_input_record_uncached(console,
-            &_win32_input_record)) {
-            // There was an error, so make sure wRepeatCount is zero because
-            // that signifies no cached input record.
-            _win32_input_record.Event.KeyEvent.wRepeatCount = 0;
-            return NULL;
-        }
-    }
-
-    return &_win32_input_record.Event.KeyEvent;
-}
-
 static __inline__ bool _is_shift_pressed(const DWORD control_key_state) {
     return (control_key_state & SHIFT_PRESSED) != 0;
 }
@@ -2905,16 +2924,34 @@
     return len + 1;
 }
 
-// Writes to buffer buf (of length len), returning number of bytes written or
-// -1 on error. Never returns zero because Win32 consoles are never 'closed'
-// (as far as I can tell).
+// Internal buffer to satisfy future _console_read() calls.
+static std::vector<char> g_console_input_buffer;
+
+// Writes to buffer buf (of length len), returning number of bytes written or -1 on error. Never
+// returns zero on console closure because Win32 consoles are never 'closed' (as far as I can tell).
 static int _console_read(const HANDLE console, void* buf, size_t len) {
     for (;;) {
-        KEY_EVENT_RECORD* const key_event = _get_key_event_record(console);
-        if (key_event == NULL) {
+        // Read of zero bytes should not block waiting for something from the console.
+        if (len == 0) {
+            return 0;
+        }
+
+        // Flush as much as possible from input buffer.
+        if (!g_console_input_buffer.empty()) {
+            const int bytes_read = std::min(len, g_console_input_buffer.size());
+            memcpy(buf, g_console_input_buffer.data(), bytes_read);
+            const auto begin = g_console_input_buffer.begin();
+            g_console_input_buffer.erase(begin, begin + bytes_read);
+            return bytes_read;
+        }
+
+        // Read from the actual console. This may block until input.
+        INPUT_RECORD input_record;
+        if (!_get_key_event_record(console, &input_record)) {
             return -1;
         }
 
+        KEY_EVENT_RECORD* const key_event = &input_record.Event.KeyEvent;
         const WORD vk = key_event->wVirtualKeyCode;
         const CHAR ch = key_event->uChar.AsciiChar;
         const DWORD control_key_state = _normalize_altgr_control_key_state(
@@ -3092,7 +3129,12 @@
                 break;
 
                 case 0x32:          // 2
+                case 0x33:          // 3
+                case 0x34:          // 4
+                case 0x35:          // 5
                 case 0x36:          // 6
+                case 0x37:          // 7
+                case 0x38:          // 8
                 case VK_OEM_MINUS:  // -_
                 {
                     seqbuflen = _get_control_character(seqbuf, key_event,
@@ -3108,25 +3150,6 @@
                 }
                 break;
 
-                case 0x33:  // 3
-                case 0x34:  // 4
-                case 0x35:  // 5
-                case 0x37:  // 7
-                case 0x38:  // 8
-                {
-                    seqbuflen = _get_control_character(seqbuf, key_event,
-                        control_key_state);
-
-                    // If Alt is pressed and it isn't Ctrl-Alt-ShiftUp, then
-                    // prefix with escape.
-                    if (_is_alt_pressed(control_key_state) &&
-                        !(_is_ctrl_pressed(control_key_state) &&
-                        !_is_shift_pressed(control_key_state))) {
-                        seqbuflen = _escape_prefix(seqbuf, seqbuflen);
-                    }
-                }
-                break;
-
                 case 0x41:  // a
                 case 0x42:  // b
                 case 0x43:  // c
@@ -3255,103 +3278,56 @@
             // event.
             D("_console_read: unknown virtual key code: %d, enhanced: %s",
                 vk, _is_enhanced_key(control_key_state) ? "true" : "false");
-            key_event->wRepeatCount = 0;
             continue;
         }
 
-        int bytesRead = 0;
-
-        // put output wRepeatCount times into buf/len
-        while (key_event->wRepeatCount > 0) {
-            if (len >= outlen) {
-                // Write to buf/len
-                memcpy(buf, out, outlen);
-                buf = (void*)((char*)buf + outlen);
-                len -= outlen;
-                bytesRead += outlen;
-
-                // consume the input
-                --key_event->wRepeatCount;
-            } else {
-                // Not enough space, so just leave it in _win32_input_record
-                // for a subsequent retrieval.
-                if (bytesRead == 0) {
-                    // We didn't write anything because there wasn't enough
-                    // space to even write one sequence. This should never
-                    // happen if the caller uses sensible buffer sizes
-                    // (i.e. >= maximum sequence length which is probably a
-                    // few bytes long).
-                    D("_console_read: no buffer space to write one sequence; "
-                        "buffer: %ld, sequence: %ld\n", (long)len,
-                        (long)outlen);
-                    errno = ENOMEM;
-                    return -1;
-                } else {
-                    // Stop trying to write to buf/len, just return whatever
-                    // we wrote so far.
-                    break;
-                }
-            }
+        // put output wRepeatCount times into g_console_input_buffer
+        while (key_event->wRepeatCount-- > 0) {
+            g_console_input_buffer.insert(g_console_input_buffer.end(), out, out + outlen);
         }
 
-        return bytesRead;
+        // Loop around and try to flush g_console_input_buffer
     }
 }
 
 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());
         }
     }
 }
@@ -3367,11 +3343,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
@@ -3494,100 +3467,61 @@
     exit(-1);
 }
 
-// TODO: Consider implementing widen() and narrow() out of std::wstring_convert
-// once libcxx is supported on Windows. Or, consider libutils/Unicode.cpp.
-
-// Convert from UTF-8 to UTF-16. A size of -1 specifies a NULL terminated
-// string. Any other size specifies the number of chars to convert, excluding
-// any NULL terminator (if you're passing an explicit size, you probably don't
-// have a NULL terminated string in the first place).
-std::wstring widen(const char* utf8, const int size) {
-    // Note: Do not call SystemErrorCodeToString() from widen() because
-    // SystemErrorCodeToString() calls narrow() which may call fatal() which
-    // calls adb_vfprintf() which calls widen(), potentially causing infinite
-    // recursion.
-    const int chars_to_convert = MultiByteToWideChar(CP_UTF8, 0, utf8, size,
-                                                     NULL, 0);
-    if (chars_to_convert <= 0) {
-        // UTF-8 to UTF-16 should be lossless, so we don't expect this to fail.
-        _widen_fatal("MultiByteToWideChar failed counting: %d, "
-                     "GetLastError: %lu", chars_to_convert, GetLastError());
-    }
-
+// Convert size number of UTF-8 char's to UTF-16. Fatal exit on error.
+std::wstring widen(const char* utf8, const size_t size) {
     std::wstring utf16;
-    size_t chars_to_allocate = chars_to_convert;
-    if (size == -1) {
-        // chars_to_convert includes a NULL terminator, so subtract space
-        // for that because resize() includes that itself.
-        --chars_to_allocate;
+    if (!android::base::UTF8ToWide(utf8, size, &utf16)) {
+        // If we call fatal() here and fatal() calls widen(), then there may be
+        // infinite recursion. To avoid this, call _widen_fatal() instead.
+        _widen_fatal("cannot convert from UTF-8 to UTF-16");
     }
-    utf16.resize(chars_to_allocate);
-
-    // This uses &string[0] to get write-access to the entire string buffer
-    // which may be assuming that the chars are all contiguous, but it seems
-    // to work and saves us the hassle of using a temporary
-    // std::vector<wchar_t>.
-    const int result = MultiByteToWideChar(CP_UTF8, 0, utf8, size, &utf16[0],
-                                           chars_to_convert);
-    if (result != chars_to_convert) {
-        // UTF-8 to UTF-16 should be lossless, so we don't expect this to fail.
-        _widen_fatal("MultiByteToWideChar failed conversion: %d, "
-                     "GetLastError: %lu", result, GetLastError());
-    }
-
-    // If a size was passed in (size != -1), then the string is NULL terminated
-    // by a NULL char that was written by std::string::resize(). If size == -1,
-    // then MultiByteToWideChar() read a NULL terminator from the original
-    // string and converted it to a NULL UTF-16 char in the output.
 
     return utf16;
 }
 
-// Convert a NULL terminated string from UTF-8 to UTF-16.
+// Convert a NULL-terminated string of UTF-8 characters to UTF-16. Fatal exit
+// on error.
 std::wstring widen(const char* utf8) {
-    // Pass -1 to let widen() determine the string length.
-    return widen(utf8, -1);
-}
-
-// Convert from UTF-8 to UTF-16.
-std::wstring widen(const std::string& utf8) {
-    return widen(utf8.c_str(), utf8.length());
-}
-
-// Convert from UTF-16 to UTF-8.
-std::string narrow(const std::wstring& utf16) {
-    return narrow(utf16.c_str());
-}
-
-// Convert from UTF-16 to UTF-8.
-std::string narrow(const wchar_t* utf16) {
-    // Note: Do not call SystemErrorCodeToString() from narrow() because
-    // SystemErrorCodeToString() calls narrow() and we don't want potential
-    // infinite recursion.
-    const int chars_required = WideCharToMultiByte(CP_UTF8, 0, utf16, -1, NULL,
-                                                   0, NULL, NULL);
-    if (chars_required <= 0) {
-        // UTF-16 to UTF-8 should be lossless, so we don't expect this to fail.
-        fatal("WideCharToMultiByte failed counting: %d, GetLastError: %lu",
-              chars_required, GetLastError());
+    std::wstring utf16;
+    if (!android::base::UTF8ToWide(utf8, &utf16)) {
+        // If we call fatal() here and fatal() calls widen(), then there may be
+        // infinite recursion. To avoid this, call _widen_fatal() instead.
+        _widen_fatal("cannot convert from UTF-8 to UTF-16");
     }
 
-    std::string utf8;
-    // Subtract space for the NULL terminator because resize() includes
-    // that itself. Note that this could potentially throw a std::bad_alloc
-    // exception.
-    utf8.resize(chars_required - 1);
+    return utf16;
+}
 
-    // This uses &string[0] to get write-access to the entire string buffer
-    // which may be assuming that the chars are all contiguous, but it seems
-    // to work and saves us the hassle of using a temporary
-    // std::vector<char>.
-    const int result = WideCharToMultiByte(CP_UTF8, 0, utf16, -1, &utf8[0],
-                                           chars_required, NULL, NULL);
-    if (result != chars_required) {
-        // UTF-16 to UTF-8 should be lossless, so we don't expect this to fail.
-        fatal("WideCharToMultiByte failed conversion: %d, GetLastError: %lu",
-              result, GetLastError());
+// Convert a UTF-8 std::string (including any embedded NULL characters) to
+// UTF-16. Fatal exit on error.
+std::wstring widen(const std::string& utf8) {
+    std::wstring utf16;
+    if (!android::base::UTF8ToWide(utf8, &utf16)) {
+        // If we call fatal() here and fatal() calls widen(), then there may be
+        // infinite recursion. To avoid this, call _widen_fatal() instead.
+        _widen_fatal("cannot convert from UTF-8 to UTF-16");
+    }
+
+    return utf16;
+}
+
+// Convert a UTF-16 std::wstring (including any embedded NULL characters) to
+// UTF-8. Fatal exit on error.
+std::string narrow(const std::wstring& utf16) {
+    std::string utf8;
+    if (!android::base::WideToUTF8(utf16, &utf8)) {
+        fatal("cannot convert from UTF-16 to UTF-8");
+    }
+
+    return utf8;
+}
+
+// Convert a NULL-terminated string of UTF-16 characters to UTF-8. Fatal exit
+// on error.
+std::string narrow(const wchar_t* utf16) {
+    std::string utf8;
+    if (!android::base::WideToUTF8(utf16, &utf8)) {
+        fatal("cannot convert from UTF-16 to UTF-8");
     }
 
     return utf8;
@@ -3726,47 +3660,16 @@
     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,
                                HANDLE console) {
-    // Convert from UTF-8 to UTF-16.
+    std::wstring output;
+
+    // Try to convert from data that might be UTF-8 to UTF-16, ignoring errors.
+    // Data might not be UTF-8 if the user cat's random data, runs dmesg, etc.
     // This could throw std::bad_alloc.
-    const std::wstring output(widen(buf, size));
+    (void)android::base::UTF8ToWide(buf, size, &output);
 
     // Note that this does not do \n => \r\n translation because that
     // doesn't seem necessary for the Windows console. For the Windows
diff --git a/adb/sysdeps_win32_test.cpp b/adb/sysdeps_win32_test.cpp
index 66d1ba8..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.
@@ -93,3 +95,45 @@
     // 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/adb/transport.cpp b/adb/transport.cpp
index e9e774f..d39b9f7 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -26,6 +26,7 @@
 #include <string.h>
 #include <unistd.h>
 
+#include <algorithm>
 #include <list>
 
 #include <base/logging.h>
@@ -42,6 +43,9 @@
 
 ADB_MUTEX_DEFINE( transport_lock );
 
+const char* const kFeatureShell2 = "shell_v2";
+const char* const kFeatureCmd = "cmd";
+
 static std::string dump_packet(const char* name, const char* func, apacket* p) {
     unsigned  command = p->msg.command;
     int       len     = p->msg.data_length;
@@ -780,7 +784,8 @@
 const FeatureSet& supported_features() {
     // Local static allocation to avoid global non-POD variables.
     static const FeatureSet* features = new FeatureSet{
-        kFeatureShell2
+        kFeatureShell2,
+        kFeatureCmd
         // Increment ADB_SERVER_VERSION whenever the feature list changes to
         // make sure that the adb client and server features stay in sync
         // (http://b/24370690).
diff --git a/adb/transport.h b/adb/transport.h
index f41a8d4..d9845b6 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -38,7 +38,9 @@
 
 // Do not use any of [:;=,] in feature strings, they have special meaning
 // in the connection banner.
-constexpr char kFeatureShell2[] = "shell_v2";
+extern const char* const kFeatureShell2;
+// The 'cmd' command is available
+extern const char* const kFeatureCmd;
 
 class atransport {
 public:
diff --git a/base/Android.mk b/base/Android.mk
index 613636b..4e6bd10 100644
--- a/base/Android.mk
+++ b/base/Android.mk
@@ -23,6 +23,9 @@
     strings.cpp \
     test_utils.cpp \
 
+libbase_windows_src_files := \
+    utf8.cpp \
+
 libbase_test_src_files := \
     file_test.cpp \
     logging_test.cpp \
@@ -31,6 +34,9 @@
     strings_test.cpp \
     test_main.cpp \
 
+libbase_test_windows_src_files := \
+    utf8_test.cpp \
+
 libbase_cppflags := \
     -Wall \
     -Wextra \
@@ -42,6 +48,9 @@
 LOCAL_MODULE := libbase
 LOCAL_CLANG := true
 LOCAL_SRC_FILES := $(libbase_src_files)
+LOCAL_SRC_FILES_darwin := $(libbase_darwin_src_files)
+LOCAL_SRC_FILES_linux := $(libbase_linux_src_files)
+LOCAL_SRC_FILES_windows := $(libbase_windows_src_files)
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
 LOCAL_CPPFLAGS := $(libbase_cppflags)
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
@@ -64,6 +73,9 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE := libbase
 LOCAL_SRC_FILES := $(libbase_src_files)
+LOCAL_SRC_FILES_darwin := $(libbase_darwin_src_files)
+LOCAL_SRC_FILES_linux := $(libbase_linux_src_files)
+LOCAL_SRC_FILES_windows := $(libbase_windows_src_files)
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
 LOCAL_CPPFLAGS := $(libbase_cppflags)
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
@@ -88,6 +100,9 @@
 LOCAL_MODULE := libbase_test
 LOCAL_CLANG := true
 LOCAL_SRC_FILES := $(libbase_test_src_files)
+LOCAL_SRC_FILES_darwin := $(libbase_test_darwin_src_files)
+LOCAL_SRC_FILES_linux := $(libbase_test_linux_src_files)
+LOCAL_SRC_FILES_windows := $(libbase_test_windows_src_files)
 LOCAL_C_INCLUDES := $(LOCAL_PATH)
 LOCAL_CPPFLAGS := $(libbase_cppflags)
 LOCAL_SHARED_LIBRARIES := libbase
@@ -100,6 +115,9 @@
 LOCAL_MODULE := libbase_test
 LOCAL_MODULE_HOST_OS := darwin linux windows
 LOCAL_SRC_FILES := $(libbase_test_src_files)
+LOCAL_SRC_FILES_darwin := $(libbase_test_darwin_src_files)
+LOCAL_SRC_FILES_linux := $(libbase_test_linux_src_files)
+LOCAL_SRC_FILES_windows := $(libbase_test_windows_src_files)
 LOCAL_C_INCLUDES := $(LOCAL_PATH)
 LOCAL_CPPFLAGS := $(libbase_cppflags)
 LOCAL_SHARED_LIBRARIES := libbase
diff --git a/base/file.cpp b/base/file.cpp
index 3468dcf..7b5e7b1 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -24,6 +24,7 @@
 #include <string>
 
 #include "base/macros.h"  // For TEMP_FAILURE_RETRY on Darwin.
+#include "base/utf8.h"
 #define LOG_TAG "base.file"
 #include "cutils/log.h"
 #include "utils/Compat.h"
@@ -35,6 +36,9 @@
 namespace android {
 namespace base {
 
+// Versions of standard library APIs that support UTF-8 strings.
+using namespace android::base::utf8;
+
 bool ReadFdToString(int fd, std::string* content) {
   content->clear();
 
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/include/base/utf8.h b/base/include/base/utf8.h
new file mode 100755
index 0000000..3cc168d
--- /dev/null
+++ b/base/include/base/utf8.h
@@ -0,0 +1,83 @@
+/*
+ * 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 BASE_UTF8_H
+#define BASE_UTF8_H
+
+#ifdef _WIN32
+#include <string>
+#endif
+
+namespace android {
+namespace base {
+
+// Only available on Windows because this is only needed on Windows.
+#ifdef _WIN32
+// Convert size number of UTF-16 wchar_t's to UTF-8. Returns whether the
+// conversion was done successfully.
+bool WideToUTF8(const wchar_t* utf16, const size_t size, std::string* utf8);
+
+// Convert a NULL-terminated string of UTF-16 characters to UTF-8. Returns
+// whether the conversion was done successfully.
+bool WideToUTF8(const wchar_t* utf16, std::string* utf8);
+
+// Convert a UTF-16 std::wstring (including any embedded NULL characters) to
+// UTF-8. Returns whether the conversion was done successfully.
+bool WideToUTF8(const std::wstring& utf16, std::string* utf8);
+
+// Convert size number of UTF-8 char's to UTF-16. Returns whether the conversion
+// was done successfully.
+bool UTF8ToWide(const char* utf8, const size_t size, std::wstring* utf16);
+
+// Convert a NULL-terminated string of UTF-8 characters to UTF-16. Returns
+// whether the conversion was done successfully.
+bool UTF8ToWide(const char* utf8, std::wstring* utf16);
+
+// Convert a UTF-8 std::string (including any embedded NULL characters) to
+// UTF-16. Returns whether the conversion was done successfully.
+bool UTF8ToWide(const std::string& utf8, std::wstring* utf16);
+#endif
+
+// The functions in the utf8 namespace take UTF-8 strings. For Windows, these
+// are wrappers, for non-Windows these just expose existing APIs. To call these
+// functions, use:
+//
+// // anonymous namespace to avoid conflict with existing open(), unlink(), etc.
+// namespace {
+//   // Import functions into anonymous namespace.
+//   using namespace android::base::utf8;
+//
+//   void SomeFunction(const char* name) {
+//     int fd = open(name, ...);  // Calls android::base::utf8::open().
+//     ...
+//     unlink(name);              // Calls android::base::utf8::unlink().
+//   }
+// }
+namespace utf8 {
+
+#ifdef _WIN32
+int open(const char* name, int flags, ...);
+int unlink(const char* name);
+#else
+using ::open;
+using ::unlink;
+#endif
+
+}  // namespace utf8
+}  // namespace base
+}  // namespace android
+
+#endif  // BASE_UTF8_H
diff --git a/base/logging.cpp b/base/logging.cpp
index 6bfaaec..01a046a 100644
--- a/base/logging.cpp
+++ b/base/logging.cpp
@@ -53,6 +53,33 @@
 #include <unistd.h>
 #endif
 
+// For gettid.
+#if defined(__APPLE__)
+#include "AvailabilityMacros.h"  // For MAC_OS_X_VERSION_MAX_ALLOWED
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/syscall.h>
+#include <sys/time.h>
+#include <unistd.h>
+#elif defined(__linux__) && !defined(__ANDROID__)
+#include <syscall.h>
+#include <unistd.h>
+#elif defined(_WIN32)
+#include <windows.h>
+#endif
+
+static pid_t GetThreadId() {
+#if defined(__BIONIC__)
+  return gettid();
+#elif defined(__APPLE__)
+  return syscall(SYS_thread_selfid);
+#elif defined(__linux__)
+  return syscall(__NR_gettid);
+#elif defined(_WIN32)
+  return GetCurrentThreadId();
+#endif
+}
+
 namespace {
 #ifndef _WIN32
 using std::mutex;
@@ -158,7 +185,7 @@
                 "Mismatch in size of log_characters and values in LogSeverity");
   char severity_char = log_characters[severity];
   fprintf(stderr, "%s %c %5d %5d %s:%u] %s\n", ProgramInvocationName(),
-          severity_char, getpid(), gettid(), file, line, message);
+          severity_char, getpid(), GetThreadId(), file, line, message);
 }
 
 
@@ -212,8 +239,8 @@
   gInitialized = true;
 
   // Stash the command line for later use. We can use /proc/self/cmdline on
-  // Linux to recover this, but we don't have that luxury on the Mac, and there
-  // are a couple of argv[0] variants that are commonly used.
+  // Linux to recover this, but we don't have that luxury on the Mac/Windows,
+  // and there are a couple of argv[0] variants that are commonly used.
   if (argv != nullptr) {
     gProgramInvocationName.reset(new std::string(basename(argv[0])));
   }
@@ -264,11 +291,20 @@
   gLogger = std::move(logger);
 }
 
-// We can't use basename(3) because this code runs on the Mac, which doesn't
-// have a non-modifying basename.
 static const char* GetFileBasename(const char* file) {
+  // We can't use basename(3) even on Unix because the Mac doesn't
+  // have a non-modifying basename.
   const char* last_slash = strrchr(file, '/');
-  return (last_slash == nullptr) ? file : last_slash + 1;
+  if (last_slash != nullptr) {
+    return last_slash + 1;
+  }
+#if defined(_WIN32)
+  const char* last_backslash = strrchr(file, '\\');
+  if (last_backslash != nullptr) {
+    return last_backslash + 1;
+  }
+#endif
+  return file;
 }
 
 // This indirection greatly reduces the stack impact of having lots of
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/base/utf8.cpp b/base/utf8.cpp
new file mode 100755
index 0000000..62a118f
--- /dev/null
+++ b/base/utf8.cpp
@@ -0,0 +1,170 @@
+/*
+ * 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 <windows.h>
+
+#include "base/utf8.h"
+
+#include <fcntl.h>
+
+#include <string>
+
+#include "base/logging.h"
+
+namespace android {
+namespace base {
+
+bool WideToUTF8(const wchar_t* utf16, const size_t size, std::string* utf8) {
+  utf8->clear();
+
+  if (size == 0) {
+    return true;
+  }
+
+  // TODO: Consider using std::wstring_convert once libcxx is supported on
+  // Windows.
+
+  // Only Vista or later has this flag that causes WideCharToMultiByte() to
+  // return an error on invalid characters.
+  const DWORD flags =
+#if (WINVER >= 0x0600)
+    WC_ERR_INVALID_CHARS;
+#else
+    0;
+#endif
+
+  const int chars_required = WideCharToMultiByte(CP_UTF8, flags, utf16, size,
+                                                 NULL, 0, NULL, NULL);
+  if (chars_required <= 0) {
+    return false;
+  }
+
+  // This could potentially throw a std::bad_alloc exception.
+  utf8->resize(chars_required);
+
+  const int result = WideCharToMultiByte(CP_UTF8, flags, utf16, size,
+                                         &(*utf8)[0], chars_required, NULL,
+                                         NULL);
+  if (result != chars_required) {
+    CHECK_LE(result, chars_required) << "WideCharToMultiByte wrote " << result
+        << " chars to buffer of " << chars_required << " chars";
+    utf8->clear();
+    return false;
+  }
+
+  return true;
+}
+
+bool WideToUTF8(const wchar_t* utf16, std::string* utf8) {
+  // Compute string length of NULL-terminated string with wcslen().
+  return WideToUTF8(utf16, wcslen(utf16), utf8);
+}
+
+bool WideToUTF8(const std::wstring& utf16, std::string* utf8) {
+  // Use the stored length of the string which allows embedded NULL characters
+  // to be converted.
+  return WideToUTF8(utf16.c_str(), utf16.length(), utf8);
+}
+
+// Internal helper function that takes MultiByteToWideChar() flags.
+static bool _UTF8ToWideWithFlags(const char* utf8, const size_t size,
+                                 std::wstring* utf16, const DWORD flags) {
+  utf16->clear();
+
+  if (size == 0) {
+    return true;
+  }
+
+  // TODO: Consider using std::wstring_convert once libcxx is supported on
+  // Windows.
+  const int chars_required = MultiByteToWideChar(CP_UTF8, flags, utf8, size,
+                                                 NULL, 0);
+  if (chars_required <= 0) {
+    return false;
+  }
+
+  // This could potentially throw a std::bad_alloc exception.
+  utf16->resize(chars_required);
+
+  const int result = MultiByteToWideChar(CP_UTF8, flags, utf8, size,
+                                         &(*utf16)[0], chars_required);
+  if (result != chars_required) {
+    CHECK_LE(result, chars_required) << "MultiByteToWideChar wrote " << result
+        << " chars to buffer of " << chars_required << " chars";
+    utf16->clear();
+    return false;
+  }
+
+  return true;
+}
+
+bool UTF8ToWide(const char* utf8, const size_t size, std::wstring* utf16) {
+  // If strictly interpreting as UTF-8 succeeds, return success.
+  if (_UTF8ToWideWithFlags(utf8, size, utf16, MB_ERR_INVALID_CHARS)) {
+    return true;
+  }
+
+  // Fallback to non-strict interpretation, allowing invalid characters and
+  // converting as best as possible, and return false to signify a problem.
+  (void)_UTF8ToWideWithFlags(utf8, size, utf16, 0);
+  return false;
+}
+
+bool UTF8ToWide(const char* utf8, std::wstring* utf16) {
+  // Compute string length of NULL-terminated string with strlen().
+  return UTF8ToWide(utf8, strlen(utf8), utf16);
+}
+
+bool UTF8ToWide(const std::string& utf8, std::wstring* utf16) {
+  // Use the stored length of the string which allows embedded NULL characters
+  // to be converted.
+  return UTF8ToWide(utf8.c_str(), utf8.length(), utf16);
+}
+
+// Versions of standard library APIs that support UTF-8 strings.
+namespace utf8 {
+
+int open(const char* name, int flags, ...) {
+  std::wstring name_utf16;
+  if (!UTF8ToWide(name, &name_utf16)) {
+    errno = EINVAL;
+    return -1;
+  }
+
+  int mode = 0;
+  if ((flags & O_CREAT) != 0) {
+    va_list args;
+    va_start(args, flags);
+    mode = va_arg(args, int);
+    va_end(args);
+  }
+
+  return _wopen(name_utf16.c_str(), flags, mode);
+}
+
+int unlink(const char* name) {
+  std::wstring name_utf16;
+  if (!UTF8ToWide(name, &name_utf16)) {
+    errno = EINVAL;
+    return -1;
+  }
+
+  return _wunlink(name_utf16.c_str());
+}
+
+}  // namespace utf8
+}  // namespace base
+}  // namespace android
diff --git a/base/utf8_test.cpp b/base/utf8_test.cpp
new file mode 100755
index 0000000..bbb54b1
--- /dev/null
+++ b/base/utf8_test.cpp
@@ -0,0 +1,402 @@
+/*
+* 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 "base/utf8.h"
+
+#include <gtest/gtest.h>
+
+#include "base/macros.h"
+
+namespace android {
+namespace base {
+
+TEST(UTFStringConversionsTest, ConvertInvalidUTF8) {
+  std::wstring wide;
+
+  // Standalone \xa2 is an invalid UTF-8 sequence, so this should return an
+  // error. Concatenate two C/C++ literal string constants to prevent the
+  // compiler from giving an error about "\xa2af" containing a "hex escape
+  // sequence out of range".
+  EXPECT_FALSE(android::base::UTF8ToWide("before\xa2" "after", &wide));
+
+  // Even if an invalid character is encountered, UTF8ToWide() should still do
+  // its best to convert the rest of the string. sysdeps_win32.cpp:
+  // _console_write_utf8() depends on this behavior.
+  //
+  // Thus, we verify that the valid characters are converted, but we ignore the
+  // specific replacement character that UTF8ToWide() may replace the invalid
+  // UTF-8 characters with because we want to allow that to change if the
+  // implementation changes.
+  EXPECT_EQ(0, wide.find(L"before"));
+  const wchar_t after_wide[] = L"after";
+  EXPECT_EQ(wide.length() - (arraysize(after_wide) - 1), wide.find(after_wide));
+}
+
+// Below is adapted from https://chromium.googlesource.com/chromium/src/+/master/base/strings/utf_string_conversions_unittest.cc
+
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The tests below from utf_string_conversions_unittest.cc check for this
+// preprocessor symbol, so define it, as it is appropriate for Windows.
+#define WCHAR_T_IS_UTF16
+static_assert(sizeof(wchar_t) == 2, "wchar_t is not 2 bytes");
+
+// The tests below from utf_string_conversions_unittest.cc call versions of
+// UTF8ToWide() and WideToUTF8() that don't return success/failure, so these are
+// stub implementations with that signature. These are just for testing and
+// should not be moved to base because they assert/expect no errors which is
+// probably not a good idea (or at least it is something that should be left
+// up to the caller, not a base library).
+
+static std::wstring UTF8ToWide(const std::string& utf8) {
+  std::wstring utf16;
+  EXPECT_TRUE(UTF8ToWide(utf8, &utf16));
+  return utf16;
+}
+
+static std::string WideToUTF8(const std::wstring& utf16) {
+  std::string utf8;
+  EXPECT_TRUE(WideToUTF8(utf16, &utf8));
+  return utf8;
+}
+
+namespace {
+
+const wchar_t* const kConvertRoundtripCases[] = {
+  L"Google Video",
+  // "网页 图片 资讯更多 »"
+  L"\x7f51\x9875\x0020\x56fe\x7247\x0020\x8d44\x8baf\x66f4\x591a\x0020\x00bb",
+  //  "Παγκόσμιος Ιστός"
+  L"\x03a0\x03b1\x03b3\x03ba\x03cc\x03c3\x03bc\x03b9"
+  L"\x03bf\x03c2\x0020\x0399\x03c3\x03c4\x03cc\x03c2",
+  // "Поиск страниц на русском"
+  L"\x041f\x043e\x0438\x0441\x043a\x0020\x0441\x0442"
+  L"\x0440\x0430\x043d\x0438\x0446\x0020\x043d\x0430"
+  L"\x0020\x0440\x0443\x0441\x0441\x043a\x043e\x043c",
+  // "전체서비스"
+  L"\xc804\xccb4\xc11c\xbe44\xc2a4",
+
+  // Test characters that take more than 16 bits. This will depend on whether
+  // wchar_t is 16 or 32 bits.
+#if defined(WCHAR_T_IS_UTF16)
+  L"\xd800\xdf00",
+  // ?????  (Mathematical Alphanumeric Symbols (U+011d40 - U+011d44 : A,B,C,D,E)
+  L"\xd807\xdd40\xd807\xdd41\xd807\xdd42\xd807\xdd43\xd807\xdd44",
+#elif defined(WCHAR_T_IS_UTF32)
+  L"\x10300",
+  // ?????  (Mathematical Alphanumeric Symbols (U+011d40 - U+011d44 : A,B,C,D,E)
+  L"\x11d40\x11d41\x11d42\x11d43\x11d44",
+#endif
+};
+
+}  // namespace
+
+TEST(UTFStringConversionsTest, ConvertUTF8AndWide) {
+  // we round-trip all the wide strings through UTF-8 to make sure everything
+  // agrees on the conversion. This uses the stream operators to test them
+  // simultaneously.
+  for (size_t i = 0; i < arraysize(kConvertRoundtripCases); ++i) {
+    std::ostringstream utf8;
+    utf8 << WideToUTF8(kConvertRoundtripCases[i]);
+    std::wostringstream wide;
+    wide << UTF8ToWide(utf8.str());
+
+    EXPECT_EQ(kConvertRoundtripCases[i], wide.str());
+  }
+}
+
+TEST(UTFStringConversionsTest, ConvertUTF8AndWideEmptyString) {
+  // An empty std::wstring should be converted to an empty std::string,
+  // and vice versa.
+  std::wstring wempty;
+  std::string empty;
+  EXPECT_EQ(empty, WideToUTF8(wempty));
+  EXPECT_EQ(wempty, UTF8ToWide(empty));
+}
+
+TEST(UTFStringConversionsTest, ConvertUTF8ToWide) {
+  struct UTF8ToWideCase {
+    const char* utf8;
+    const wchar_t* wide;
+    bool success;
+  } convert_cases[] = {
+    // Regular UTF-8 input.
+    {"\xe4\xbd\xa0\xe5\xa5\xbd", L"\x4f60\x597d", true},
+    // Non-character is passed through.
+    {"\xef\xbf\xbfHello", L"\xffffHello", true},
+    // Truncated UTF-8 sequence.
+    {"\xe4\xa0\xe5\xa5\xbd", L"\xfffd\x597d", false},
+    // Truncated off the end.
+    {"\xe5\xa5\xbd\xe4\xa0", L"\x597d\xfffd", false},
+    // Non-shortest-form UTF-8.
+    {"\xf0\x84\xbd\xa0\xe5\xa5\xbd", L"\xfffd\x597d", false},
+    // This UTF-8 character decodes to a UTF-16 surrogate, which is illegal.
+    // Note that for whatever reason, this test fails on Windows XP.
+    {"\xed\xb0\x80", L"\xfffd", false},
+    // Non-BMP characters. The second is a non-character regarded as valid.
+    // The result will either be in UTF-16 or UTF-32.
+#if defined(WCHAR_T_IS_UTF16)
+    {"A\xF0\x90\x8C\x80z", L"A\xd800\xdf00z", true},
+    {"A\xF4\x8F\xBF\xBEz", L"A\xdbff\xdffez", true},
+#elif defined(WCHAR_T_IS_UTF32)
+    {"A\xF0\x90\x8C\x80z", L"A\x10300z", true},
+    {"A\xF4\x8F\xBF\xBEz", L"A\x10fffez", true},
+#endif
+  };
+
+  for (size_t i = 0; i < arraysize(convert_cases); i++) {
+    std::wstring converted;
+    const bool success = UTF8ToWide(convert_cases[i].utf8,
+                                    strlen(convert_cases[i].utf8),
+                                    &converted);
+    EXPECT_EQ(convert_cases[i].success, success);
+    // The original test always compared expected and converted, but don't do
+    // that because our implementation of UTF8ToWide() does not guarantee to
+    // produce the same output in error situations.
+    if (success) {
+      std::wstring expected(convert_cases[i].wide);
+      EXPECT_EQ(expected, converted);
+    }
+  }
+
+  // Manually test an embedded NULL.
+  std::wstring converted;
+  EXPECT_TRUE(UTF8ToWide("\00Z\t", 3, &converted));
+  ASSERT_EQ(3U, converted.length());
+  EXPECT_EQ(static_cast<wchar_t>(0), converted[0]);
+  EXPECT_EQ('Z', converted[1]);
+  EXPECT_EQ('\t', converted[2]);
+
+  // Make sure that conversion replaces, not appends.
+  EXPECT_TRUE(UTF8ToWide("B", 1, &converted));
+  ASSERT_EQ(1U, converted.length());
+  EXPECT_EQ('B', converted[0]);
+}
+
+#if defined(WCHAR_T_IS_UTF16)
+// This test is only valid when wchar_t == UTF-16.
+TEST(UTFStringConversionsTest, ConvertUTF16ToUTF8) {
+  struct WideToUTF8Case {
+    const wchar_t* utf16;
+    const char* utf8;
+    bool success;
+  } convert_cases[] = {
+    // Regular UTF-16 input.
+    {L"\x4f60\x597d", "\xe4\xbd\xa0\xe5\xa5\xbd", true},
+    // Test a non-BMP character.
+    {L"\xd800\xdf00", "\xF0\x90\x8C\x80", true},
+    // Non-characters are passed through.
+    {L"\xffffHello", "\xEF\xBF\xBFHello", true},
+    {L"\xdbff\xdffeHello", "\xF4\x8F\xBF\xBEHello", true},
+    // The first character is a truncated UTF-16 character.
+    // Note that for whatever reason, this test fails on Windows XP.
+    {L"\xd800\x597d", "\xef\xbf\xbd\xe5\xa5\xbd",
+#if (WINVER >= 0x0600)
+    // Only Vista and later has a new API/flag that correctly returns false.
+    false
+#else
+    true
+#endif
+    },
+    // Truncated at the end.
+    // Note that for whatever reason, this test fails on Windows XP.
+    {L"\x597d\xd800", "\xe5\xa5\xbd\xef\xbf\xbd",
+#if (WINVER >= 0x0600)
+    // Only Vista and later has a new API/flag that correctly returns false.
+    false
+#else
+    true
+#endif
+    },
+  };
+
+  for (size_t i = 0; i < arraysize(convert_cases); i++) {
+    std::string converted;
+    const bool success = WideToUTF8(convert_cases[i].utf16,
+                                    wcslen(convert_cases[i].utf16),
+                                    &converted);
+    EXPECT_EQ(convert_cases[i].success, success);
+    // The original test always compared expected and converted, but don't do
+    // that because our implementation of WideToUTF8() does not guarantee to
+    // produce the same output in error situations.
+    if (success) {
+      std::string expected(convert_cases[i].utf8);
+      EXPECT_EQ(expected, converted);
+    }
+  }
+}
+
+#elif defined(WCHAR_T_IS_UTF32)
+// This test is only valid when wchar_t == UTF-32.
+TEST(UTFStringConversionsTest, ConvertUTF32ToUTF8) {
+  struct WideToUTF8Case {
+    const wchar_t* utf32;
+    const char* utf8;
+    bool success;
+  } convert_cases[] = {
+    // Regular 16-bit input.
+    {L"\x4f60\x597d", "\xe4\xbd\xa0\xe5\xa5\xbd", true},
+    // Test a non-BMP character.
+    {L"A\x10300z", "A\xF0\x90\x8C\x80z", true},
+    // Non-characters are passed through.
+    {L"\xffffHello", "\xEF\xBF\xBFHello", true},
+    {L"\x10fffeHello", "\xF4\x8F\xBF\xBEHello", true},
+    // Invalid Unicode code points.
+    {L"\xfffffffHello", "\xEF\xBF\xBDHello", false},
+    // The first character is a truncated UTF-16 character.
+    {L"\xd800\x597d", "\xef\xbf\xbd\xe5\xa5\xbd", false},
+    {L"\xdc01Hello", "\xef\xbf\xbdHello", false},
+  };
+
+  for (size_t i = 0; i < arraysize(convert_cases); i++) {
+    std::string converted;
+    EXPECT_EQ(convert_cases[i].success,
+              WideToUTF8(convert_cases[i].utf32,
+                         wcslen(convert_cases[i].utf32),
+                         &converted));
+    std::string expected(convert_cases[i].utf8);
+    EXPECT_EQ(expected, converted);
+  }
+}
+#endif  // defined(WCHAR_T_IS_UTF32)
+
+// The test below uses these types and functions, so just do enough to get the
+// test running.
+typedef wchar_t char16;
+typedef std::wstring string16;
+
+template<typename T>
+static void* WriteInto(T* t, size_t size) {
+  // std::(w)string::resize() already includes space for a NULL terminator.
+  t->resize(size - 1);
+  return &(*t)[0];
+}
+
+// A stub implementation that calls a helper from above, just to get the test
+// below working. This is just for testing and should not be moved to base
+// because this ignores errors which is probably not a good idea, plus it takes
+// a string16 type which we don't really have.
+static std::string UTF16ToUTF8(const string16& utf16) {
+  return WideToUTF8(utf16);
+}
+
+TEST(UTFStringConversionsTest, ConvertMultiString) {
+  static char16 multi16[] = {
+    'f', 'o', 'o', '\0',
+    'b', 'a', 'r', '\0',
+    'b', 'a', 'z', '\0',
+    '\0'
+  };
+  static char multi[] = {
+    'f', 'o', 'o', '\0',
+    'b', 'a', 'r', '\0',
+    'b', 'a', 'z', '\0',
+    '\0'
+  };
+  string16 multistring16;
+  memcpy(WriteInto(&multistring16, arraysize(multi16)), multi16,
+                   sizeof(multi16));
+  EXPECT_EQ(arraysize(multi16) - 1, multistring16.length());
+  std::string expected;
+  memcpy(WriteInto(&expected, arraysize(multi)), multi, sizeof(multi));
+  EXPECT_EQ(arraysize(multi) - 1, expected.length());
+  const std::string& converted = UTF16ToUTF8(multistring16);
+  EXPECT_EQ(arraysize(multi) - 1, converted.length());
+  EXPECT_EQ(expected, converted);
+}
+
+// The tests below from sys_string_conversions_unittest.cc call SysWideToUTF8()
+// and SysUTF8ToWide(), so these are stub implementations that call the helpers
+// above. These are just for testing and should not be moved to base because
+// they ignore errors which is probably not a good idea.
+
+static std::string SysWideToUTF8(const std::wstring& utf16) {
+  return WideToUTF8(utf16);
+}
+
+static std::wstring SysUTF8ToWide(const std::string& utf8) {
+  return UTF8ToWide(utf8);
+}
+
+// Below is adapted from https://chromium.googlesource.com/chromium/src/+/master/base/strings/sys_string_conversions_unittest.cc
+
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifdef WCHAR_T_IS_UTF32
+static const std::wstring kSysWideOldItalicLetterA = L"\x10300";
+#else
+static const std::wstring kSysWideOldItalicLetterA = L"\xd800\xdf00";
+#endif
+
+TEST(SysStrings, SysWideToUTF8) {
+  EXPECT_EQ("Hello, world", SysWideToUTF8(L"Hello, world"));
+  EXPECT_EQ("\xe4\xbd\xa0\xe5\xa5\xbd", SysWideToUTF8(L"\x4f60\x597d"));
+
+  // >16 bits
+  EXPECT_EQ("\xF0\x90\x8C\x80", SysWideToUTF8(kSysWideOldItalicLetterA));
+
+  // Error case. When Windows finds a UTF-16 character going off the end of
+  // a string, it just converts that literal value to UTF-8, even though this
+  // is invalid.
+  //
+  // This is what XP does, but Vista has different behavior, so we don't bother
+  // verifying it:
+  // EXPECT_EQ("\xE4\xBD\xA0\xED\xA0\x80zyxw",
+  //           SysWideToUTF8(L"\x4f60\xd800zyxw"));
+
+  // Test embedded NULLs.
+  std::wstring wide_null(L"a");
+  wide_null.push_back(0);
+  wide_null.push_back('b');
+
+  std::string expected_null("a");
+  expected_null.push_back(0);
+  expected_null.push_back('b');
+
+  EXPECT_EQ(expected_null, SysWideToUTF8(wide_null));
+}
+
+TEST(SysStrings, SysUTF8ToWide) {
+  EXPECT_EQ(L"Hello, world", SysUTF8ToWide("Hello, world"));
+  EXPECT_EQ(L"\x4f60\x597d", SysUTF8ToWide("\xe4\xbd\xa0\xe5\xa5\xbd"));
+  // >16 bits
+  EXPECT_EQ(kSysWideOldItalicLetterA, SysUTF8ToWide("\xF0\x90\x8C\x80"));
+
+  // Error case. When Windows finds an invalid UTF-8 character, it just skips
+  // it. This seems weird because it's inconsistent with the reverse conversion.
+  //
+  // This is what XP does, but Vista has different behavior, so we don't bother
+  // verifying it:
+  // EXPECT_EQ(L"\x4f60zyxw", SysUTF8ToWide("\xe4\xbd\xa0\xe5\xa5zyxw"));
+
+  // Test embedded NULLs.
+  std::string utf8_null("a");
+  utf8_null.push_back(0);
+  utf8_null.push_back('b');
+
+  std::wstring expected_null(L"a");
+  expected_null.push_back(0);
+  expected_null.push_back('b');
+
+  EXPECT_EQ(expected_null, SysUTF8ToWide(utf8_null));
+}
+
+}  // namespace base
+}  // namespace android
diff --git a/crash_reporter/Android.mk b/crash_reporter/Android.mk
index 5dd9418..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,9 +102,13 @@
 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) > $@
@@ -134,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 4be2e5d..e43fe96 100644
--- a/debuggerd/debuggerd.rc
+++ b/debuggerd/debuggerd.rc
@@ -1,3 +1,4 @@
 service debuggerd /system/bin/debuggerd
     class main
+    group root readproc
     writepid /dev/cpuset/system-background/tasks
diff --git a/debuggerd/debuggerd64.rc b/debuggerd/debuggerd64.rc
index c6e7bf2..35b5af3 100644
--- a/debuggerd/debuggerd64.rc
+++ b/debuggerd/debuggerd64.rc
@@ -1,3 +1,4 @@
 service debuggerd64 /system/bin/debuggerd64
     class main
+    group root readproc
     writepid /dev/cpuset/system-background/tasks
diff --git a/debuggerd/elf_utils.cpp b/debuggerd/elf_utils.cpp
index 5ea03e7..65c1904 100644
--- a/debuggerd/elf_utils.cpp
+++ b/debuggerd/elf_utils.cpp
@@ -63,10 +63,10 @@
         if (nhdr.n_type == NT_GNU_BUILD_ID) {
           // Skip the name (which is the owner and should be "GNU").
           addr += NOTE_ALIGN(nhdr.n_namesz);
-          uint8_t build_id_data[128];
-          if (nhdr.n_namesz > sizeof(build_id_data)) {
-            ALOGE("Possible corrupted note, name size value is too large: %u",
-                  nhdr.n_namesz);
+          uint8_t build_id_data[160];
+          if (nhdr.n_descsz > sizeof(build_id_data)) {
+            ALOGE("Possible corrupted note, desc size value is too large: %u",
+                  nhdr.n_descsz);
             return false;
           }
           if (backtrace->Read(addr, build_id_data, nhdr.n_descsz) != nhdr.n_descsz) {
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index 3f201ec..7a7366e 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -49,7 +49,7 @@
     libutils \
     liblog \
     libz \
-    libbase
+    libbase \
 
 LOCAL_STATIC_LIBRARIES_darwin := libselinux
 LOCAL_STATIC_LIBRARIES_linux := libselinux
diff --git a/fastboot/engine.cpp b/fastboot/engine.cpp
index a0e990a..d4be63b 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) {
@@ -154,6 +130,13 @@
     return a;
 }
 
+void fb_set_active(const char *slot)
+{
+    Action *a;
+    a = queue_action(OP_COMMAND, "set_active:%s", slot);
+    a->msg = mkmsg("Setting current slot to '%s'", slot);
+}
+
 void fb_queue_erase(const char *ptn)
 {
     Action *a;
@@ -394,8 +377,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 0901310..b9d03e5 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -42,10 +42,16 @@
 #include <sys/time.h>
 #include <sys/types.h>
 #include <unistd.h>
+#include <functional>
 
+#include <base/parseint.h>
+#include <base/strings.h>
 #include <sparse/sparse.h>
 #include <ziparchive/zip_archive.h>
 
+#include <base/strings.h>
+#include <base/parseint.h>
+
 #include "bootimg_utils.h"
 #include "fastboot.h"
 #include "fs.h"
@@ -271,6 +277,8 @@
             "                                           override the fs type and/or size\n"
             "                                           the bootloader reports.\n"
             "  getvar <variable>                        Display a bootloader variable.\n"
+            "  set_active <suffix>                      Sets the active slot. If slots are\n"
+            "                                           not supported, this does nothing.\n"
             "  boot <kernel> [ <ramdisk> [ <second> ] ] Download and boot kernel.\n"
             "  flash:raw boot <kernel> [ <ramdisk> [ <second> ] ]\n"
             "                                           Create bootimage and flash it.\n"
@@ -297,6 +305,13 @@
             "                                           (default: 2048).\n"
             "  -S <size>[K|M|G]                         Automatically sparse files greater\n"
             "                                           than 'size'. 0 to disable.\n"
+            "  --slot <suffix>                          Specify slot suffix to be used if the\n"
+            "                                           device supports slots. This will be\n"
+            "                                           added to all partition names that use\n"
+            "                                           slots. 'all' can be given to refer\n"
+            "                                           to all slots. If this is not given,\n"
+            "                                           slotted partitions will default to\n"
+            "                                           the current active slot.\n"
         );
 }
 
@@ -572,25 +587,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) {
@@ -615,16 +633,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) {
@@ -686,6 +703,80 @@
     }
 }
 
+static std::vector<std::string> get_suffixes(usb_handle* usb) {
+    std::vector<std::string> suffixes;
+    std::string suffix_list;
+    if (!fb_getvar(usb, "slot-suffixes", &suffix_list)) {
+        die("Could not get suffixes.\n");
+    }
+    return android::base::Split(suffix_list, ",");
+}
+
+static std::string verify_slot(usb_handle* usb, const char *slot) {
+    if (strcmp(slot, "all") == 0) {
+        return "all";
+    }
+    std::vector<std::string> suffixes = get_suffixes(usb);
+    for (const std::string &suffix : suffixes) {
+        if (suffix == slot)
+            return slot;
+    }
+    fprintf(stderr, "Slot %s does not exist. supported slots are:", slot);
+    for (const std::string &suffix : suffixes) {
+        fprintf(stderr, "%s\n", suffix.c_str());
+    }
+    exit(1);
+}
+
+static void do_for_partition(usb_handle* usb, const char *part, const char *slot, std::function<void(const std::string&)> func, bool force_slot) {
+    std::string partition_slot;
+    std::string current_slot;
+
+    if (!fb_getvar(usb, std::string("partition-slot:")+part, &partition_slot)) {
+        /* If partition-slot is not supported, the answer is no. */
+        partition_slot = "";
+    }
+    if (partition_slot == "1") {
+        if (!slot || slot[0] == 0) {
+            if (!fb_getvar(usb, "current-slot", &current_slot)) {
+                die("Failed to identify current slot.\n");
+            }
+            func(std::string(part) + '-' + current_slot);
+        } else {
+            func(std::string(part) + '-' + slot);
+        }
+    } else {
+        if (force_slot && slot && slot[0]) {
+             fprintf(stderr, "Warning: %s does not support slots, and slot %s was requested.\n", part, slot);
+        }
+        func(part);
+    }
+}
+
+/* This function will find the real partition name given a base name, and a slot. If slot is NULL or empty,
+ * it will use the current slot. If slot is "all", it will return a list of all possible partition names.
+ * If force_slot is true, it will fail if a slot is specified, and the given partition does not support slots.
+ */
+static void do_for_partitions(usb_handle* usb, const char *part, const char *slot, std::function<void(const std::string&)> func, bool force_slot) {
+    std::string partition_slot;
+
+    if (slot && strcmp(slot, "all") == 0) {
+        if (!fb_getvar(usb, std::string("partition-slot:") + part, &partition_slot)) {
+            die("Could not check if partition %s has slot.", part);
+        }
+        if (partition_slot == "1") {
+            std::vector<std::string> suffixes = get_suffixes(usb);
+            for (std::string &suffix : suffixes) {
+                do_for_partition(usb, part, suffix.c_str(), func, force_slot);
+            }
+        } else {
+            do_for_partition(usb, part, "", func, force_slot);
+        }
+    } else {
+        do_for_partition(usb, part, slot, func, force_slot);
+    }
+}
+
 static void do_flash(usb_handle* usb, const char* pname, const char* fname) {
     struct fastboot_buffer buf;
 
@@ -703,7 +794,7 @@
     fb_queue_command("signature", "installing signature");
 }
 
-static void do_update(usb_handle* usb, const char* filename, bool erase_first) {
+static void do_update(usb_handle* usb, const char* filename, const char* slot_override, bool erase_first) {
     queue_info_dump();
 
     fb_queue_query_save("product", cur_product, sizeof(cur_product));
@@ -736,15 +827,19 @@
         fastboot_buffer buf;
         int rc = load_buf_fd(usb, fd, &buf);
         if (rc) die("cannot load %s from flash", images[i].img_name);
-        do_update_signature(zip, images[i].sig_name);
-        if (erase_first && needs_erase(usb, images[i].part_name)) {
-            fb_queue_erase(images[i].part_name);
-        }
-        flash_buf(images[i].part_name, &buf);
-        /* not closing the fd here since the sparse code keeps the fd around
-         * but hasn't mmaped data yet. The tmpfile will get cleaned up when the
-         * program exits.
-         */
+
+        auto update = [&](const std::string &partition) {
+            do_update_signature(zip, images[i].sig_name);
+            if (erase_first && needs_erase(usb, partition.c_str())) {
+                fb_queue_erase(partition.c_str());
+            }
+            flash_buf(partition.c_str(), &buf);
+            /* not closing the fd here since the sparse code keeps the fd around
+             * but hasn't mmaped data yet. The tmpfile will get cleaned up when the
+             * program exits.
+             */
+        };
+        do_for_partitions(usb, images[i].part_name, slot_override, update, false);
     }
 
     CloseArchive(zip);
@@ -766,7 +861,7 @@
     fb_queue_command("signature", "installing signature");
 }
 
-static void do_flashall(usb_handle* usb, int erase_first) {
+static void do_flashall(usb_handle* usb, const char *slot_override, int erase_first) {
     queue_info_dump();
 
     fb_queue_query_save("product", cur_product, sizeof(cur_product));
@@ -788,11 +883,15 @@
                 continue;
             die("could not load %s\n", images[i].img_name);
         }
-        do_send_signature(fname);
-        if (erase_first && needs_erase(usb, images[i].part_name)) {
-            fb_queue_erase(images[i].part_name);
-        }
-        flash_buf(images[i].part_name, &buf);
+
+        auto flashall = [&](const std::string &partition) {
+            do_send_signature(fname);
+            if (erase_first && needs_erase(usb, partition.c_str())) {
+                fb_queue_erase(partition.c_str());
+            }
+            flash_buf(partition.c_str(), &buf);
+        };
+        do_for_partitions(usb, images[i].part_name, slot_override, flashall, false);
     }
 }
 
@@ -878,87 +977,89 @@
 }
 
 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;
     }
+    // Some bootloaders (angler, for example), send spurious leading whitespace.
+    partition_size = android::base::Trim(partition_size);
+    // 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;
 
-    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);
+    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());
 }
@@ -971,9 +1072,8 @@
     bool erase_first = true;
     void *data;
     int64_t sz;
-    int status;
-    int c;
     int longindex;
+    std::string slot_override;
 
     const struct option longopts[] = {
         {"base", required_argument, 0, 'b'},
@@ -984,13 +1084,14 @@
         {"help", no_argument, 0, 'h'},
         {"unbuffered", no_argument, 0, 0},
         {"version", no_argument, 0, 0},
+        {"slot", required_argument, 0, 0},
         {0, 0, 0, 0}
     };
 
     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;
         }
@@ -1058,6 +1159,8 @@
             } else if (strcmp("version", longopts[longindex].name) == 0) {
                 fprintf(stdout, "fastboot version %s\n", FASTBOOT_REVISION);
                 return 0;
+            } else if (strcmp("slot", longopts[longindex].name) == 0) {
+                slot_override = std::string(optarg);
             }
             break;
         default:
@@ -1085,20 +1188,28 @@
     }
 
     usb_handle* usb = open_device();
+    if (slot_override != "")
+        slot_override = verify_slot(usb, slot_override.c_str());
 
     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");
-            }
+            auto erase = [&](const std::string &partition) {
+            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]);
+                fb_queue_erase(partition.c_str());
+            };
+            do_for_partitions(usb, argv[1], slot_override.c_str(), erase, true);
             skip(2);
         } else if(!strncmp(*argv, "format", strlen("format"))) {
             char *overrides;
@@ -1126,10 +1237,14 @@
             }
             if (type_override && !type_override[0]) type_override = nullptr;
             if (size_override && !size_override[0]) size_override = nullptr;
-            if (erase_first && needs_erase(usb, argv[1])) {
-                fb_queue_erase(argv[1]);
-            }
-            fb_perform_format(usb, argv[1], 0, type_override, size_override);
+
+            auto format = [&](const std::string &partition) {
+                if (erase_first && needs_erase(usb, partition.c_str())) {
+                    fb_queue_erase(partition.c_str());
+                }
+                fb_perform_format(usb, partition.c_str(), 0, type_override, size_override);
+            };
+            do_for_partitions(usb, argv[1], slot_override.c_str(), format, true);
             skip(2);
         } else if(!strcmp(*argv, "signature")) {
             require(2);
@@ -1189,12 +1304,15 @@
                 skip(2);
             }
             if (fname == 0) die("cannot determine image filename for '%s'", pname);
-            if (erase_first && needs_erase(usb, pname)) {
-                fb_queue_erase(pname);
-            }
-            do_flash(usb, pname, fname);
+
+            auto flash = [&](const std::string &partition) {
+                if (erase_first && needs_erase(usb, partition.c_str())) {
+                    fb_queue_erase(partition.c_str());
+                }
+                do_flash(usb, partition.c_str(), fname);
+            };
+            do_for_partitions(usb, pname, slot_override.c_str(), flash, true);
         } else if(!strcmp(*argv, "flash:raw")) {
-            char *pname = argv[1];
             char *kname = argv[2];
             char *rname = 0;
             char *sname = 0;
@@ -1210,20 +1328,27 @@
             }
             data = load_bootable_image(kname, rname, sname, &sz, cmdline);
             if (data == 0) die("cannot load bootable image");
-            fb_queue_flash(pname, data, sz);
+            auto flashraw = [&](const std::string &partition) {
+                fb_queue_flash(partition.c_str(), data, sz);
+            };
+            do_for_partitions(usb, argv[1], slot_override.c_str(), flashraw, true);
         } else if(!strcmp(*argv, "flashall")) {
             skip(1);
-            do_flashall(usb, erase_first);
+            do_flashall(usb, slot_override.c_str(), erase_first);
             wants_reboot = 1;
         } else if(!strcmp(*argv, "update")) {
             if (argc > 1) {
-                do_update(usb, argv[1], erase_first);
+                do_update(usb, argv[1], slot_override.c_str(), erase_first);
                 skip(2);
             } else {
-                do_update(usb, "update.zip", erase_first);
+                do_update(usb, "update.zip", slot_override.c_str(), erase_first);
                 skip(1);
             }
             wants_reboot = 1;
+        } else if(!strcmp(*argv, "set_active")) {
+            require(2);
+            fb_set_active(argv[1]);
+            skip(2);
         } else if(!strcmp(*argv, "oem")) {
             argc = do_oem_command(argc, argv);
         } else if(!strcmp(*argv, "flashing")) {
@@ -1249,10 +1374,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();
@@ -1262,9 +1393,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..784ec5c 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,7 @@
 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);
+void fb_set_active(const char *slot);
 
 /* 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.c b/fs_mgr/fs_mgr.c
index c47a585..d754516 100644
--- a/fs_mgr/fs_mgr.c
+++ b/fs_mgr/fs_mgr.c
@@ -444,6 +444,7 @@
 {
     /* If this is block encryptable, need to trigger encryption */
     if (   (rec->fs_mgr_flags & MF_FORCECRYPT)
+        || (rec->fs_mgr_flags & MF_FORCEFDEORFBE)
         || (device_is_force_encrypted() && fs_mgr_is_encryptable(rec))) {
         if (umount(rec->mount_point) == 0) {
             return FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION;
@@ -881,7 +882,8 @@
         if (fstab->recs[i].fs_mgr_flags & MF_VOLDMANAGED) {
             continue;
         }
-        if (!(fstab->recs[i].fs_mgr_flags & (MF_CRYPT | MF_FORCECRYPT))) {
+        if (!(fstab->recs[i].fs_mgr_flags
+              & (MF_CRYPT | MF_FORCECRYPT | MF_FORCEFDEORFBE))) {
             continue;
         }
 
diff --git a/fs_mgr/fs_mgr_fstab.c b/fs_mgr/fs_mgr_fstab.c
index cf35b3f..c8c624d 100644
--- a/fs_mgr/fs_mgr_fstab.c
+++ b/fs_mgr/fs_mgr_fstab.c
@@ -64,6 +64,7 @@
     { "encryptable=",MF_CRYPT },
     { "forceencrypt=",MF_FORCECRYPT },
     { "fileencryption",MF_FILEENCRYPTION },
+    { "forcefdeorfbe=",MF_FORCEFDEORFBE },
     { "nonremovable",MF_NONREMOVABLE },
     { "voldmanaged=",MF_VOLDMANAGED},
     { "length=",     MF_LENGTH },
@@ -140,6 +141,11 @@
                      * location of the keys.  Get it and return it.
                      */
                     flag_vals->key_loc = strdup(strchr(p, '=') + 1);
+                } else if ((fl[i].flag == MF_FORCEFDEORFBE) && flag_vals) {
+                    /* The forcefdeorfbe flag is followed by an = and the
+                     * location of the keys.  Get it and return it.
+                     */
+                    flag_vals->key_loc = strdup(strchr(p, '=') + 1);
                 } else if ((fl[i].flag == MF_LENGTH) && flag_vals) {
                     /* The length flag is followed by an = and the
                      * size of the partition.  Get it and return it.
@@ -464,7 +470,7 @@
 
 int fs_mgr_is_encryptable(const struct fstab_rec *fstab)
 {
-    return fstab->fs_mgr_flags & (MF_CRYPT | MF_FORCECRYPT);
+    return fstab->fs_mgr_flags & (MF_CRYPT | MF_FORCECRYPT | MF_FORCEFDEORFBE);
 }
 
 int fs_mgr_is_file_encrypted(const struct fstab_rec *fstab)
@@ -472,6 +478,11 @@
     return fstab->fs_mgr_flags & MF_FILEENCRYPTION;
 }
 
+int fs_mgr_is_convertible_to_fbe(const struct fstab_rec *fstab)
+{
+    return fstab->fs_mgr_flags & MF_FORCEFDEORFBE;
+}
+
 int fs_mgr_is_noemulatedsd(const struct fstab_rec *fstab)
 {
     return fstab->fs_mgr_flags & MF_NOEMULATEDSD;
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index ba0e097..181b6cd 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -82,6 +82,7 @@
 #define MF_FILEENCRYPTION 0x2000
 #define MF_FORMATTABLE  0x4000
 #define MF_SLOTSELECT   0x8000
+#define MF_FORCEFDEORFBE 0x10000
 
 #define DM_BUF_SIZE 4096
 
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/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 27fccf7..eb0a7fc 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -102,6 +102,7 @@
 int fs_mgr_is_verified(const struct fstab_rec *fstab);
 int fs_mgr_is_encryptable(const struct fstab_rec *fstab);
 int fs_mgr_is_file_encrypted(const struct fstab_rec *fstab);
+int fs_mgr_is_convertible_to_fbe(const struct fstab_rec *fstab);
 int fs_mgr_is_noemulatedsd(const struct fstab_rec *fstab);
 int fs_mgr_is_notrim(struct fstab_rec *fstab);
 int fs_mgr_is_formattable(struct fstab_rec *fstab);
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 ddfdd88..34019fa 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -40,6 +40,8 @@
 #define FAKE_BATTERY_CAPACITY 42
 #define FAKE_BATTERY_TEMPERATURE 424
 #define ALWAYS_PLUGGED_CAPACITY 100
+#define MILLION 10000000.0
+#define DEFAULT_VBUS_VOLTAGE 5000000
 
 namespace android {
 
@@ -185,6 +187,7 @@
     props.batteryStatus = BATTERY_STATUS_UNKNOWN;
     props.batteryHealth = BATTERY_HEALTH_UNKNOWN;
     props.maxChargingCurrent = 0;
+    props.maxChargingVoltage = 0;
 
     if (!mHealthdConfig->batteryPresentPath.isEmpty())
         props.batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);
@@ -232,6 +235,7 @@
         props.batteryTechnology = String8(buf);
 
     unsigned int i;
+    double MaxPower = 0;
 
     for (i = 0; i < mChargerNames.size(); i++) {
         String8 path;
@@ -260,11 +264,23 @@
                 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;
-                    }
+                int ChargingCurrent =
+                    (access(path.string(), R_OK) == 0) ? getIntField(path) : 0;
+
+                path.clear();
+                path.appendFormat("%s/%s/voltage_max", POWER_SUPPLY_SYSFS_PATH,
+                                  mChargerNames[i].string());
+
+                int ChargingVoltage =
+                    (access(path.string(), R_OK) == 0) ? getIntField(path) :
+                    DEFAULT_VBUS_VOLTAGE;
+
+                double power = ((double)ChargingCurrent / MILLION) *
+                        ((double)ChargingVoltage / MILLION);
+                if (MaxPower < power) {
+                    props.maxChargingCurrent = ChargingCurrent;
+                    props.maxChargingVoltage = ChargingVoltage;
+                    MaxPower = power;
                 }
             }
         }
@@ -402,9 +418,10 @@
     int v;
     char vs[128];
 
-    snprintf(vs, sizeof(vs), "ac: %d usb: %d wireless: %d current_max: %d\n",
+    snprintf(vs, sizeof(vs), "ac: %d usb: %d wireless: %d current_max: %d voltage_max: %d\n",
              props.chargerAcOnline, props.chargerUsbOnline,
-             props.chargerWirelessOnline, props.maxChargingCurrent);
+             props.chargerWirelessOnline, props.maxChargingCurrent,
+             props.maxChargingVoltage);
     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/private/android_filesystem_config.h b/include/private/android_filesystem_config.h
index c7eb34b..a1d2539 100644
--- a/include/private/android_filesystem_config.h
+++ b/include/private/android_filesystem_config.h
@@ -81,6 +81,7 @@
 #define AID_SHARED_RELRO  1037  /* creator of shared GNU RELRO files */
 #define AID_DBUS          1038  /* dbus-daemon IPC broker process */
 #define AID_TLSDATE       1039  /* tlsdate unprivileged user */
+#define AID_MEDIA_EX      1040  /* mediaextractor process */
 
 #define AID_SHELL         2000  /* adb and debug shell user */
 #define AID_CACHE         2001  /* cache access */
@@ -101,6 +102,7 @@
 #define AID_NET_BW_STATS  3006  /* read bandwidth statistics */
 #define AID_NET_BW_ACCT   3007  /* change bandwidth statistics accounting */
 #define AID_NET_BT_STACK  3008  /* bluetooth: access config files */
+#define AID_READPROC      3009  /* Allow /proc read access */
 
 /* The range 5000-5999 is also reserved for OEM, and must never be used here. */
 #define AID_OEM_RESERVED_2_START 5000
@@ -178,6 +180,7 @@
     { "shared_relro",  AID_SHARED_RELRO, },
     { "dbus",          AID_DBUS, },
     { "tlsdate",       AID_TLSDATE, },
+    { "mediaex",       AID_MEDIA_EX, },
 
     { "shell",         AID_SHELL, },
     { "cache",         AID_CACHE, },
@@ -191,6 +194,7 @@
     { "net_bw_stats",  AID_NET_BW_STATS, },
     { "net_bw_acct",   AID_NET_BW_ACCT, },
     { "net_bt_stack",  AID_NET_BT_STACK, },
+    { "readproc",      AID_READPROC, },
 
     { "everybody",     AID_EVERYBODY, },
     { "misc",          AID_MISC, },
diff --git a/include/system/radio.h b/include/system/radio.h
index a088526..9e291c8 100644
--- a/include/system/radio.h
+++ b/include/system/radio.h
@@ -94,6 +94,7 @@
     radio_rds_t         rds;        /* RDS variants supported */
     bool                ta;         /* Traffic Announcement supported */
     bool                af;         /* Alternate Frequency supported */
+    bool                ea;         /* Emergency announcements supported */
 } radio_hal_fm_band_config_t;
 
 /* Additional attributes for an AM band configuration */
@@ -184,6 +185,7 @@
     RADIO_EVENT_METADATA    = 4,  /* New meta data received */
     RADIO_EVENT_TA          = 5,  /* Traffic announcement start or stop */
     RADIO_EVENT_AF_SWITCH   = 6,  /* Switch to Alternate Frequency */
+    RADIO_EVENT_EA          = 7,  /* Emergency announcement start or stop */
     // begin framework only events
     RADIO_EVENT_CONTROL     = 100, /* loss/gain of tuner control */
     RADIO_EVENT_SERVER_DIED = 101, /* radio service died */
@@ -195,7 +197,8 @@
     radio_event_type_t  type;       /* event type */
     int                 status;     /* used by RADIO_EVENT_CONFIG, RADIO_EVENT_TUNED */
     union {
-        bool                    on;     /* RADIO_EVENT_ANTENNA, RADIO_EVENT_TA */
+        /* RADIO_EVENT_ANTENNA, RADIO_EVENT_TA, RADIO_EVENT_EA */
+        bool                    on;
         radio_hal_band_config_t config; /* RADIO_EVENT_CONFIG */
         radio_program_info_t    info;   /* RADIO_EVENT_TUNED, RADIO_EVENT_AF_SWITCH */
         radio_metadata_t        *metadata; /* RADIO_EVENT_METADATA */
diff --git a/include/system/window.h b/include/system/window.h
index 508ce00..9f6e951 100644
--- a/include/system/window.h
+++ b/include/system/window.h
@@ -25,6 +25,7 @@
 #include <sys/cdefs.h>
 #include <system/graphics.h>
 #include <unistd.h>
+#include <stdbool.h>
 
 #ifndef __UNUSED
 #define __UNUSED __attribute__((__unused__))
@@ -311,6 +312,7 @@
     NATIVE_WINDOW_SET_SIDEBAND_STREAM       = 18,
     NATIVE_WINDOW_SET_BUFFERS_DATASPACE     = 19,
     NATIVE_WINDOW_SET_SURFACE_DAMAGE        = 20,   /* private */
+    NATIVE_WINDOW_SET_SINGLE_BUFFER_MODE    = 21,
 };
 
 /* parameter for NATIVE_WINDOW_[API_][DIS]CONNECT */
@@ -949,6 +951,18 @@
             rects, numRects);
 }
 
+/*
+ * native_window_set_single_buffer_mode(..., bool singleBufferMode)
+ * Enable/disable single buffer mode
+ */
+static inline int native_window_set_single_buffer_mode(
+        struct ANativeWindow* window,
+        bool singleBufferMode)
+{
+    return window->perform(window, NATIVE_WINDOW_SET_SINGLE_BUFFER_MODE,
+            singleBufferMode);
+}
+
 __END_DECLS
 
 #endif /* SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H */
diff --git a/include/utils/Errors.h b/include/utils/Errors.h
index 08ddcd2..16e1fa2 100644
--- a/include/utils/Errors.h
+++ b/include/utils/Errors.h
@@ -72,6 +72,7 @@
     UNKNOWN_TRANSACTION = (UNKNOWN_ERROR + 6),
 #endif    
     FDS_NOT_ALLOWED     = (UNKNOWN_ERROR + 7),
+    UNEXPECTED_NULL     = (UNKNOWN_ERROR + 8),
 };
 
 // Restore define; enumeration is in "android" namespace, so the value defined
diff --git a/include/utils/LruCache.h b/include/utils/LruCache.h
index 20a379e..1741fb2 100644
--- a/include/utils/LruCache.h
+++ b/include/utils/LruCache.h
@@ -17,10 +17,9 @@
 #ifndef ANDROID_UTILS_LRU_CACHE_H
 #define ANDROID_UTILS_LRU_CACHE_H
 
+#include <memory>
 #include <unordered_set>
 
-#include <UniquePtr.h>
-
 #include "utils/TypeHelpers.h"  // hash_t
 
 namespace android {
@@ -91,7 +90,7 @@
         return result;
     }
 
-    UniquePtr<LruCacheSet> mSet;
+    std::unique_ptr<LruCacheSet> mSet;
     OnEntryRemoved<TKey, TValue>* mListener;
     Entry* mOldest;
     Entry* mYoungest;
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/init.cpp b/init/init.cpp
index a898b03..86aed9a 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -386,7 +386,7 @@
 
     struct dirent *dp;
     while ((dp = readdir(dir.get())) != NULL) {
-        if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible")) {
+        if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible") || !strcmp(dp->d_name, "name")) {
             continue;
         }
 
@@ -546,7 +546,8 @@
         mkdir("/dev/pts", 0755);
         mkdir("/dev/socket", 0755);
         mount("devpts", "/dev/pts", "devpts", 0, NULL);
-        mount("proc", "/proc", "proc", 0, NULL);
+        #define MAKE_STR(x) __STRING(x)
+        mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
         mount("sysfs", "/sys", "sysfs", 0, NULL);
     }
 
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/libpackagelistparser/Android.mk b/libpackagelistparser/Android.mk
new file mode 100644
index 0000000..c8be050
--- /dev/null
+++ b/libpackagelistparser/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/libpackagelistparser/include/packagelistparser/packagelistparser.h b/libpackagelistparser/include/packagelistparser/packagelistparser.h
new file mode 100644
index 0000000..d602c26
--- /dev/null
+++ b/libpackagelistparser/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/libpackagelistparser/packagelistparser.c b/libpackagelistparser/packagelistparser.c
new file mode 100644
index 0000000..16052e2
--- /dev/null
+++ b/libpackagelistparser/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/libpixelflinger/Android.mk b/libpixelflinger/Android.mk
index 65a3fdf..9a937f8 100644
--- a/libpixelflinger/Android.mk
+++ b/libpixelflinger/Android.mk
@@ -50,6 +50,14 @@
 	arch-mips/t32cb16blend.S \
 
 endif
+
+PIXELFLINGER_SRC_FILES_mips64 := \
+        codeflinger/MIPSAssembler.cpp \
+	codeflinger/MIPS64Assembler.cpp \
+	codeflinger/mips64_disassem.c \
+	arch-mips64/col32cb16blend.S \
+	arch-mips64/t32cb16blend.S \
+
 #
 # Shared library
 #
@@ -59,6 +67,7 @@
 LOCAL_SRC_FILES_arm := $(PIXELFLINGER_SRC_FILES_arm)
 LOCAL_SRC_FILES_arm64 := $(PIXELFLINGER_SRC_FILES_arm64)
 LOCAL_SRC_FILES_mips := $(PIXELFLINGER_SRC_FILES_mips)
+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) \
diff --git a/libpixelflinger/arch-mips/col32cb16blend.S b/libpixelflinger/arch-mips/col32cb16blend.S
new file mode 100644
index 0000000..5d18e55
--- /dev/null
+++ b/libpixelflinger/arch-mips/col32cb16blend.S
@@ -0,0 +1,134 @@
+/*
+** Copyright 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.
+*/
+
+       .macro pixel dreg src f sR sG sB shift
+
+#if __mips==32 && __mips_isa_rev>=2
+       /* extract red */
+       ext $t4,\src,\shift+11,5
+       mul $t4,$t4,\f
+
+       /* extract green */
+       ext $t5,\src,\shift+5,6
+       mul $t5,$t5,\f
+
+       /* extract blue */
+       ext $t6,\src,\shift,5
+       mul $t6,$t6,\f
+#else
+       /* extract red */
+       srl $t4,\src,\shift+11
+       andi $t4, 0x1f
+       mul $t4,$t4,\f
+
+       /* extract green */
+       srl $t5,\src,\shift+5
+       andi $t5, 0x3f
+       mul $t5,$t5,\f
+
+       /* extract blue */
+       srl $t6,\src,\shift
+       andi $t6, 0x1f
+       mul $t6,$t6,\f
+#endif
+
+       srl $t4,$t4,8
+       srl $t5,$t5,8
+       srl $t6,$t6,8
+       addu $t4,$t4,\sR
+       addu $t5,$t5,\sG
+       addu \dreg,$t6,\sB
+       sll $t4,$t4,11
+       sll $t5,$t5,5
+       or \dreg,\dreg,$t4
+       or \dreg,\dreg,$t5
+       andi \dreg, 0xffff
+       .endm
+
+       .text
+       .align
+
+       .global scanline_col32cb16blend_mips
+       .ent    scanline_col32cb16blend_mips
+scanline_col32cb16blend_mips:
+
+       /* check if count is zero */
+       srl     $v0,$a1,24 /* sA */
+       beqz    $a2,done
+       li      $t4, 0x100
+       srl     $v1,$v0,7
+       addu    $v0,$v1,$v0
+       subu    $v0,$t4,$v0 /* f */
+#if __mips==32 && __mips_isa_rev>=2
+       ext     $a3,$a1,3,5 /* sR */
+       ext     $t0,$a1,10,6 /* sG */
+       ext     $t1,$a1,19,5 /* sB */
+#else
+       srl     $a3, $a1, 3
+       andi    $a3, 0x1f    /* sR */
+       srl     $t0, $a1, 10
+       andi    $t0, 0x3f    /* sG */
+       srl     $t1, $a1, 19
+       andi    $t1, 0x1f    /* sB */
+#endif
+
+       /* check if cnt is at least 4 */
+       addiu   $a2,$a2,-4
+       bltz    $a2,tail
+
+loop_4pixels:
+       lw      $t7,0($a0)
+       lw      $t8,4($a0)
+       addiu   $a0,$a0,8
+       addiu   $a2,$a2,-4
+       pixel   $t2 $t7 $v0 $a3 $t0 $t1 0
+       pixel   $t3 $t7 $v0 $a3 $t0 $t1 16
+#if __mips==32 && __mips_isa_rev>=2
+       ins     $t2,$t3,16,16
+#else
+       sll $t3, 16
+       or  $t2, $t2, $t3
+#endif
+       pixel   $t7 $t8 $v0 $a3 $t0 $t1 0
+       pixel   $t3 $t8 $v0 $a3 $t0 $t1 16
+#if __mips==32 && __mips_isa_rev>=2
+       ins     $t7,$t3,16,16
+#else
+       sll $t3, 16
+       or  $t7, $t7, $t3
+#endif
+       sw      $t2,-8($a0)
+       sw      $t7,-4($a0)
+       bgez    $a2, loop_4pixels
+
+tail:
+       /* the pixel count underran, restore it now */
+       addiu   $a2,$a2,4
+
+       /* handle the last 0..3 pixels */
+       beqz    $a2,done
+
+loop_1pixel:
+       lhu     $t7,0($a0)
+       addiu   $a0,$a0,2
+       addiu   $a2,$a2,-1
+       pixel   $t2 $t7 $v0 $a3 $t0 $t1 0
+       sh      $t2, -2($a0)
+       bnez    $a2,loop_1pixel
+
+done:
+       j       $ra
+       .end    scanline_col32cb16blend_mips
diff --git a/libpixelflinger/arch-mips/t32cb16blend.S b/libpixelflinger/arch-mips/t32cb16blend.S
index c911fbb..236a2c9 100644
--- a/libpixelflinger/arch-mips/t32cb16blend.S
+++ b/libpixelflinger/arch-mips/t32cb16blend.S
@@ -33,232 +33,241 @@
  */
 
 #if __mips==32 && __mips_isa_rev>=2
-	.macro pixel dreg src fb shift
-	/*
-	 * sA = s >> 24
-	 * f = 0x100 - (sA + (sA>>7))
-	 */
-DBG	.set	noat
-DBG	rdhwr	$at,$2
-DBG	.set	at
+    .macro pixel dreg src fb shift
+    /*
+     * sA = s >> 24
+     * f = 0x100 - (sA + (sA>>7))
+     */
+DBG .set    noat
+DBG rdhwr   $at,$2
+DBG .set    at
 
-	srl	$t7,\src,24
-	srl	$t6,$t7,7
-	addu	$t7,$t6
-	li	$t6,0x100
-	subu	$t7,$t6,$t7
+    srl  $t7,\src,24
+    srl  $t6,$t7,7
+    addu $t7,$t6
+    li   $t6,0x100
+    subu $t7,$t6,$t7
 
-	/* red */
-	ext	$t8,\dreg,\shift+6+5,5			# dst[\shift:15..11]
-	mul	$t6,$t8,$t7
-	ext	$t0,\dreg,\shift+5,6			# start green extraction dst[\shift:10..5]
-	ext	$t8,\src,3,5				# src[7..3]
-	srl	$t6,8
-	addu	$t8,$t6
-	ins	\fb,$t8,\shift+6+5,5			# dst[\shift:15..11]
+    /* red */
+    ext  $t8,\dreg,\shift+6+5,5         # dst[\shift:15..11]
+    mul  $t6,$t8,$t7
+    ext  $t0,\dreg,\shift+5,6           # start green extraction dst[\shift:10..5]
+    ext  $t8,\src,3,5               # src[7..3]
+    srl  $t6,8
+    addu $t8,$t6
+.if \shift!=0
+    sll  $t8,\shift+11
+    or   \fb,$t8
+.else
+    sll  \fb,$t8,11
+.endif
 
-        /* green */
-	mul	$t8,$t0,$t7
-	ext	$t0,\dreg,\shift,5			# start blue extraction dst[\shift:4..0]
-	ext	$t6,\src,2+8,6				# src[15..10]
-	srl	$t8,8
-        addu	$t8,$t6
+    /* green */
+    mul  $t8,$t0,$t7
+    ext  $t0,\dreg,\shift,5         # start blue extraction dst[\shift:4..0]
+    ext  $t6,\src,2+8,6             # src[15..10]
+    srl  $t8,8
+    addu $t8,$t6
 
-	/* blue */
-	mul	$t0,$t0,$t7
-	ins	\fb,$t8,\shift+5,6			# finish green insertion dst[\shift:10..5]
-	ext	$t6,\src,(3+8+8),5
-	srl	$t8,$t0,8
-	addu	$t8,$t6
-	ins	\fb,$t8,\shift,5
+    /* blue */
+    mul  $t0,$t0,$t7
+    sll  $t8, $t8, \shift+5
+    or   \fb, \fb, $t8
+    ext  $t6,\src,(3+8+8),5
+    srl  $t8,$t0,8
+    addu $t8,$t6
+    sll  $t8, $t8, \shift
+    or   \fb, \fb, $t8
 
-DBG	.set	noat
-DBG	rdhwr	$t8,$2
-DBG	subu	$t8,$at
-DBG	sltu	$at,$t8,$v0
-DBG	movn	$v0,$t8,$at
-DBG	sgtu	$at,$t8,$v1
-DBG	movn	$v1,$t8,$at
-DBG	.set	at
-	.endm
+DBG .set    noat
+DBG rdhwr $t8,$2
+DBG subu  $t8,$at
+DBG sltu  $at,$t8,$v0
+DBG movn  $v0,$t8,$at
+DBG sgtu  $at,$t8,$v1
+DBG movn  $v1,$t8,$at
+DBG .set    at
+    .endm
 
 #else
 
-	.macro pixel dreg src fb shift
-	/*
-	 * sA = s >> 24
-	 * f = 0x100 - (sA + (sA>>7))
-	 */
-DBG	.set	push
-DBG	.set	noat
-DBG	.set	mips32r2
-DBG 	rdhwr	$at,$2
-DBG	.set	pop
+    .macro pixel dreg src fb shift
+    /*
+     * sA = s >> 24
+     * f = 0x100 - (sA + (sA>>7))
+     */
+DBG .set    push
+DBG .set    noat
+DBG .set    mips32r2
+DBG rdhwr   $at,$2
+DBG .set    pop
 
-	srl	$t7,\src,24
-	srl	$t6,$t7,7
-	addu	$t7,$t6
-	li	$t6,0x100
-	subu	$t7,$t6,$t7
+    srl  $t7,\src,24
+    srl  $t6,$t7,7
+    addu $t7,$t6
+    li   $t6,0x100
+    subu $t7,$t6,$t7
 
-	/*
-	 * red
-	 * dR = (d >> (6 + 5)) & 0x1f;
-	 * dR = (f*dR)>>8
-	 * sR = (s >> (   3)) & 0x1f;
-	 * sR += dR
-	 * fb |= sR << 11
-	 */
-	srl	$t8,\dreg,\shift+6+5
+    /*
+     * red
+     * dR = (d >> (6 + 5)) & 0x1f;
+     * dR = (f*dR)>>8
+     * sR = (s >> (   3)) & 0x1f;
+     * sR += dR
+     * fb |= sR << 11
+     */
+    srl  $t8,\dreg,\shift+6+5
 .if \shift==0
-	and     $t8,0x1f
+    and  $t8,0x1f
 .endif
-	mul	$t8,$t8,$t7
-	srl	$t6,\src,3
-	and	$t6,0x1f
-	srl	$t8,8
-	addu	$t8,$t6
+    mul  $t8,$t8,$t7
+    srl  $t6,\src,3
+    and  $t6,0x1f
+    srl  $t8,8
+    addu $t8,$t6
 .if \shift!=0
-	sll	$t8,\shift+11
-	or	\fb,$t8
+    sll  $t8,\shift+11
+    or   \fb,$t8
 .else
-	sll	\fb,$t8,11
+    sll  \fb,$t8,11
 .endif
 
         /*
-	 * green
-	 * dG = (d >> 5) & 0x3f
-	 * dG = (f*dG) >> 8
-	 * sG = (s >> ( 8+2))&0x3F;
-	 */
-	srl	$t8,\dreg,\shift+5
-        and	$t8,0x3f
-	mul	$t8,$t8,$t7
-        srl	$t6,\src,8+2
-        and     $t6,0x3f
-	srl	$t8,8
-        addu	$t8,$t6
-	sll	$t8,\shift + 5
-	or	\fb,$t8
+     * green
+     * dG = (d >> 5) & 0x3f
+     * dG = (f*dG) >> 8
+     * sG = (s >> ( 8+2))&0x3F;
+     */
+    srl  $t8,\dreg,\shift+5
+    and  $t8,0x3f
+    mul  $t8,$t8,$t7
+    srl  $t6,\src,8+2
+    and  $t6,0x3f
+    srl  $t8,8
+    addu $t8,$t6
+    sll  $t8,\shift + 5
+    or   \fb,$t8
 
-	/* blue */
+    /* blue */
 .if \shift!=0
-	srl	$t8,\dreg,\shift
-	and	$t8,0x1f
+    srl  $t8,\dreg,\shift
+    and  $t8,0x1f
 .else
-	and	$t8,\dreg,0x1f
+    and  $t8,\dreg,0x1f
 .endif
-	mul	$t8,$t8,$t7
-	srl	$t6,\src,(8+8+3)
-	and	$t6,0x1f
-	srl	$t8,8
-	addu	$t8,$t6
+    mul  $t8,$t8,$t7
+    srl  $t6,\src,(8+8+3)
+    and  $t6,0x1f
+    srl  $t8,8
+    addu $t8,$t6
 .if \shift!=0
-	sll	$t8,\shift
+    sll  $t8,\shift
 .endif
-	or	\fb,$t8
-DBG	.set	push
-DBG	.set	noat
-DBG	.set	mips32r2
-DBG	rdhwr	$t8,$2
-DBG	subu	$t8,$at
-DBG	sltu	$at,$t8,$v0
-DBG	movn	$v0,$t8,$at
-DBG	sgtu	$at,$t8,$v1
-DBG	movn	$v1,$t8,$at
-DBG	.set	pop
-	.endm
+    or   \fb,$t8
+DBG .set    push
+DBG .set    noat
+DBG .set    mips32r2
+DBG rdhwr   $t8,$2
+DBG subu    $t8,$at
+DBG sltu    $at,$t8,$v0
+DBG movn    $v0,$t8,$at
+DBG sgtu    $at,$t8,$v1
+DBG movn    $v1,$t8,$at
+DBG .set    pop
+    .endm
 #endif
 
-	.text
-	.align
+    .text
+    .align
 
-	.global scanline_t32cb16blend_mips
-	.ent	scanline_t32cb16blend_mips
+    .global scanline_t32cb16blend_mips
+    .ent    scanline_t32cb16blend_mips
 scanline_t32cb16blend_mips:
-DBG	li	$v0,0xffffffff
-DBG	li	$v1,0
-	/* Align the destination if necessary */
-	and	$t0,$a0,3
-	beqz	$t0,aligned
+DBG li    $v0,0xffffffff
+DBG li    $v1,0
+    /* Align the destination if necessary */
+    and   $t0,$a0,3
+    beqz  $t0,aligned
 
-	/* as long as there is at least one pixel */
-	beqz	$a2,done
+    /* as long as there is at least one pixel */
+    beqz  $a2,done
 
-	lw	$t4,($a1)
-	addu	$a0,2
-	addu	$a1,4
-	beqz	$t4,1f
-	lhu	$t3,-2($a0)
-	pixel   $t3,$t4,$t1,0
-	sh	$t1,-2($a0)
-1:	subu	$a2,1
+    lw    $t4,($a1)
+    addu  $a0,2
+    addu  $a1,4
+    beqz  $t4,1f
+    lhu   $t3,-2($a0)
+    pixel $t3,$t4,$t1,0
+    sh    $t1,-2($a0)
+1:  subu  $a2,1
 
 aligned:
-	/* Check to see if its worth unrolling the loop */
-	subu	$a2,4
-	bltz	$a2,tail
+    /* Check to see if its worth unrolling the loop */
+    subu  $a2,4
+    bltz  $a2,tail
 
-	/* Process 4 pixels at a time */
+    /* Process 4 pixels at a time */
 fourpixels:
-	/* 1st pair of pixels */
-	lw	$t4,0($a1)
-	lw	$t5,4($a1)
-	addu	$a0,8
-	addu	$a1,16
+    /* 1st pair of pixels */
+    lw    $t4,0($a1)
+    lw    $t5,4($a1)
+    addu  $a0,8
+    addu  $a1,16
 
-	/* both are zero, skip this pair */
-	or	$t3,$t4,$t5
-	beqz	$t3,1f
+    /* both are zero, skip this pair */
+    or    $t3,$t4,$t5
+    beqz  $t3,1f
 
-	/* load the destination */
-	lw	$t3,-8($a0)
+    /* load the destination */
+    lw    $t3,-8($a0)
 
-	pixel	$t3,$t4,$t1,0
-	pixel	$t3,$t5,$t1,16
-	sw	$t1,-8($a0)
+    pixel $t3,$t4,$t1,0
+    andi  $t1, 0xFFFF
+    pixel $t3,$t5,$t1,16
+    sw    $t1,-8($a0)
 
 1:
-	/* 2nd pair of pixels */
-	lw	$t4,-8($a1)
-	lw	$t5,-4($a1)
+    /* 2nd pair of pixels */
+    lw    $t4,-8($a1)
+    lw    $t5,-4($a1)
 
-	/* both are zero, skip this pair */
-	or	$t3,$t4,$t5
-	beqz	$t3,1f
+    /* both are zero, skip this pair */
+    or    $t3,$t4,$t5
+    beqz  $t3,1f
 
-	/* load the destination */
-	lw	$t3,-4($a0)
+    /* load the destination */
+    lw    $t3,-4($a0)
 
-	pixel	$t3,$t4,$t1,0
-	pixel	$t3,$t5,$t1,16
-	sw	$t1,-4($a0)
+    pixel $t3,$t4,$t1,0
+    andi  $t1, 0xFFFF
+    pixel $t3,$t5,$t1,16
+    sw    $t1,-4($a0)
 
-1:	subu    $a2,4
-	bgtz	$a2,fourpixels
+1:  subu  $a2,4
+    bgtz  $a2,fourpixels
 
 tail:
-	/* the pixel count underran, restore it now */
-	addu	$a2,4
+    /* the pixel count underran, restore it now */
+    addu  $a2,4
 
-	/* handle the last 0..3 pixels */
-	beqz	$a2,done
+    /* handle the last 0..3 pixels */
+    beqz  $a2,done
 onepixel:
-	lw	$t4,($a1)
-	addu	$a0,2
-	addu	$a1,4
-	beqz	$t4,1f
-	lhu	$t3,-2($a0)
-	pixel   $t3,$t4,$t1,0
-	sh	$t1,-2($a0)
-1:	subu	$a2,1
-	bnez	$a2,onepixel
+    lw    $t4,($a1)
+    addu  $a0,2
+    addu  $a1,4
+    beqz  $t4,1f
+    lhu   $t3,-2($a0)
+    pixel $t3,$t4,$t1,0
+    sh    $t1,-2($a0)
+1:  subu  $a2,1
+    bnez  $a2,onepixel
 done:
-DBG	.set    push
-DBG	.set    mips32r2
-DBG 	rdhwr	$a0,$3
-DBG 	mul	$v0,$a0
-DBG 	mul	$v1,$a0
-DBG	.set    pop
-	j	$ra
-	.end	scanline_t32cb16blend_mips
+DBG .set    push
+DBG .set    mips32r2
+DBG rdhwr   $a0,$3
+DBG mul     $v0,$a0
+DBG mul     $v1,$a0
+DBG .set    pop
+    j     $ra
+    .end    scanline_t32cb16blend_mips
diff --git a/libpixelflinger/arch-mips64/col32cb16blend.S b/libpixelflinger/arch-mips64/col32cb16blend.S
new file mode 100644
index 0000000..fea4491
--- /dev/null
+++ b/libpixelflinger/arch-mips64/col32cb16blend.S
@@ -0,0 +1,108 @@
+/*
+** Copyright 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.
+*/
+
+    .macro pixel dreg src f sR sG sB shift
+
+    /* extract red */
+.if \shift < 32
+    dext   $t0,\src,\shift+11,5
+.else
+    dextu  $t0,\src,\shift+11,5
+.endif
+    mul    $t0,$t0,\f
+
+    /* extract green */
+.if \shift < 32
+    dext   $t1,\src,\shift+5,6
+.else
+    dextu  $t1,\src,\shift+5,6
+.endif
+    mul    $t1,$t1,\f
+
+    /* extract blue */
+.if \shift < 32
+    dext   $t2,\src,\shift,5
+.else
+    dextu  $t2,\src,\shift,5
+.endif
+    mul    $t2,$t2,\f
+
+    srl    $t0,$t0,8
+    srl    $t1,$t1,8
+    srl    $t2,$t2,8
+    addu   $t0,$t0,\sR
+    addu   $t1,$t1,\sG
+    addu   \dreg,$t2,\sB
+    sll    $t0,$t0,11
+    sll    $t1,$t1,5
+    or     \dreg,\dreg,$t0
+    or     \dreg,\dreg,$t1
+    .endm
+
+    .text
+    .align
+
+    .global scanline_col32cb16blend_mips64
+    .ent    scanline_col32cb16blend_mips64
+scanline_col32cb16blend_mips64:
+
+    /* check if count is zero */
+    srl     $v0,$a1,24 /* sA */
+    beqz    $a2,done
+    li      $t0, 0x100
+    srl     $v1,$v0,7
+    addu    $v0,$v1,$v0
+    subu    $v0,$t0,$v0 /* f */
+    ext     $a3,$a1,3,5 /* sR */
+    ext     $a4,$a1,10,6 /* sG */
+    ext     $a5,$a1,19,5 /* sB */
+
+    /* check if cnt is at least 4 */
+    addiu   $a2,$a2,-4
+    bltz    $a2,tail
+
+loop_4pixels:
+    ld      $t3,0($a0)
+    daddiu  $a0,$a0,8
+    addiu   $a2,$a2,-4
+    pixel   $a6 $t3 $v0 $a3 $a4 $a5 0
+    pixel   $a7 $t3 $v0 $a3 $a4 $a5 16
+    pixel   $t8 $t3 $v0 $a3 $a4 $a5 32
+    pixel   $t9 $t3 $v0 $a3 $a4 $a5 48
+    dins    $a6,$a7,16,16
+    dinsu   $a6,$t8,32,16
+    dinsu   $a6,$t9,48,16
+    sd      $a6,-8($a0)
+    bgez    $a2, loop_4pixels
+
+tail:
+    /* the pixel count underran, restore it now */
+    addiu   $a2,$a2,4
+
+    /* handle the last 0..3 pixels */
+    beqz    $a2,done
+
+loop_1pixel:
+    lhu     $t3,0($a0)
+    daddiu  $a0,$a0,2
+    addiu   $a2,$a2,-1
+    pixel   $a6 $t3 $v0 $a3 $a4 $a5 0
+    sh      $a6, -2($a0)
+    bnez    $a2,loop_1pixel
+
+done:
+    j       $ra
+    .end    scanline_col32cb16blend_mips64
diff --git a/libpixelflinger/arch-mips64/t32cb16blend.S b/libpixelflinger/arch-mips64/t32cb16blend.S
new file mode 100644
index 0000000..d2f4d49
--- /dev/null
+++ b/libpixelflinger/arch-mips64/t32cb16blend.S
@@ -0,0 +1,172 @@
+/*
+** Copyright 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.
+*/
+
+#ifdef DEBUG
+#define DBG
+#else
+#define DBG #
+#endif
+
+/*
+ * blend one of 2 16bpp RGB pixels held in dreg selected by shift
+ * with the 32bpp ABGR pixel held in src and store the result in fb
+ *
+ * Assumes that the dreg data is little endian and that
+ * the the second pixel (shift==16) will be merged into
+ * the fb result
+ *
+ * Uses $a4,$t2,$t3,$t8
+ */
+
+    .macro pixel dreg src fb shift
+    /*
+     * sA = s >> 24
+     * f = 0x100 - (sA + (sA>>7))
+     */
+    srl     $t3,\src,24
+    srl     $t2,$t3,7
+    addu    $t3,$t2
+    li      $t2,0x100
+    subu    $t3,$t2,$t3
+
+    /* red */
+    ext     $t8,\dreg,\shift+6+5,5                  # dst[\shift:15..11]
+    mul     $t2,$t8,$t3
+    ext     $a4,\dreg,\shift+5,6                    # start green extraction dst[\shift:10..5]
+    ext     $t8,\src,3,5                            # src[7..3]
+    srl     $t2,8
+    addu    $t8,$t2
+.if \shift!=0
+    sll     $t8,\shift+11                           # dst[\shift:15..11]
+    or      \fb,$t8
+.else
+    sll     \fb,$t8,11
+.endif
+
+    /* green */
+    mul     $t8,$a4,$t3
+    ext     $a4,\dreg,\shift,5                      # start blue extraction dst[\shift:4..0]
+    ext     $t2,\src,2+8,6                          # src[15..10]
+    srl     $t8,8
+    addu    $t8,$t2
+
+    /* blue */
+    mul     $a4,$a4,$t3
+    sll     $t8, $t8, \shift+5                  # finish green insertion dst[\shift:10..5]
+    or      \fb, \fb, $t8
+    ext     $t2,\src,(3+8+8),5
+    srl     $t8,$a4,8
+    addu    $t8,$t2
+    sll     $t8, $t8, \shift
+    or      \fb, \fb, $t8
+    .endm
+
+    .text
+    .align
+
+    .global scanline_t32cb16blend_mips64
+    .ent    scanline_t32cb16blend_mips64
+scanline_t32cb16blend_mips64:
+    daddiu  $sp, $sp, -40
+DBG li      $v0,0xffffffff
+DBG li      $v1,0
+    /* Align the destination if necessary */
+    and     $a4,$a0,3
+    beqz    $a4,aligned
+
+    /* as long as there is at least one pixel */
+    beqz    $a2,done
+
+    lw      $t0,($a1)
+    daddu   $a0,2
+    daddu   $a1,4
+    beqz    $t0,1f
+    lhu     $a7,-2($a0)
+    pixel   $a7,$t0,$a5,0
+    sh      $a5,-2($a0)
+1:  subu    $a2,1
+
+aligned:
+    /* Check to see if its worth unrolling the loop */
+    subu    $a2,4
+    bltz    $a2,tail
+
+    /* Process 4 pixels at a time */
+fourpixels:
+    /* 1st pair of pixels */
+    lw      $t0,0($a1)
+    lw      $t1,4($a1)
+    daddu   $a0,8
+    daddu   $a1,16
+
+    /* both are zero, skip this pair */
+    or      $a7,$t0,$t1
+    beqz    $a7,1f
+
+    /* load the destination */
+    lw      $a7,-8($a0)
+
+    pixel   $a7,$t0,$a5,0
+    andi    $a5, 0xFFFF
+    pixel   $a7,$t1,$a5,16
+    sw      $a5,-8($a0)
+
+1:
+    /* 2nd pair of pixels */
+    lw      $t0,-8($a1)
+    lw      $t1,-4($a1)
+
+    /* both are zero, skip this pair */
+    or      $a7,$t0,$t1
+    beqz    $a7,1f
+
+    /* load the destination */
+    lw      $a7,-4($a0)
+
+    pixel   $a7,$t0,$a5,0
+    andi    $a5, 0xFFFF
+    pixel   $a7,$t1,$a5,16
+    sw      $a5,-4($a0)
+
+1:  subu    $a2,4
+    bgtz    $a2,fourpixels
+
+tail:
+    /* the pixel count underran, restore it now */
+    addu    $a2,4
+
+    /* handle the last 0..3 pixels */
+    beqz    $a2,done
+onepixel:
+    lw      $t0,($a1)
+    daddu   $a0,2
+    daddu   $a1,4
+    beqz    $t0,1f
+    lhu     $a7,-2($a0)
+    pixel   $a7,$t0,$a5,0
+    sh      $a5,-2($a0)
+1:  subu    $a2,1
+    bnez    $a2,onepixel
+done:
+DBG .set    push
+DBG .set    mips32r2
+DBG rdhwr   $a0,$3
+DBG mul     $v0,$a0
+DBG mul     $v1,$a0
+DBG .set    pop
+    daddiu  $sp, $sp, 40
+    j       $ra
+    .end    scanline_t32cb16blend_mips64
diff --git a/libpixelflinger/codeflinger/ARMAssemblerInterface.h b/libpixelflinger/codeflinger/ARMAssemblerInterface.h
index 40cbfcf..72935ac 100644
--- a/libpixelflinger/codeflinger/ARMAssemblerInterface.h
+++ b/libpixelflinger/codeflinger/ARMAssemblerInterface.h
@@ -63,7 +63,7 @@
     };
 
     enum {
-        CODEGEN_ARCH_ARM = 1, CODEGEN_ARCH_MIPS, CODEGEN_ARCH_ARM64
+        CODEGEN_ARCH_ARM = 1, CODEGEN_ARCH_MIPS, CODEGEN_ARCH_ARM64, CODEGEN_ARCH_MIPS64
     };
 
     // -----------------------------------------------------------------------
@@ -115,7 +115,8 @@
     // data processing...
     enum {
         opAND, opEOR, opSUB, opRSB, opADD, opADC, opSBC, opRSC, 
-        opTST, opTEQ, opCMP, opCMN, opORR, opMOV, opBIC, opMVN
+        opTST, opTEQ, opCMP, opCMN, opORR, opMOV, opBIC, opMVN,
+        opADD64, opSUB64
     };
 
     virtual void
diff --git a/libpixelflinger/codeflinger/GGLAssembler.cpp b/libpixelflinger/codeflinger/GGLAssembler.cpp
index 325caba..346779f 100644
--- a/libpixelflinger/codeflinger/GGLAssembler.cpp
+++ b/libpixelflinger/codeflinger/GGLAssembler.cpp
@@ -893,7 +893,8 @@
         return;
     }
     
-    if (getCodegenArch() == CODEGEN_ARCH_MIPS) {
+    if ((getCodegenArch() == CODEGEN_ARCH_MIPS) ||
+        (getCodegenArch() == CODEGEN_ARCH_MIPS64)) {
         // MIPS can do 16-bit imm in 1 instr, 32-bit in 3 instr
         // the below ' while (mask)' code is buggy on mips
         // since mips returns true on isValidImmediate()
@@ -1057,7 +1058,8 @@
 RegisterAllocator::RegisterFile::RegisterFile(int codegen_arch)
     : mRegs(0), mTouched(0), mStatus(0), mArch(codegen_arch), mRegisterOffset(0)
 {
-    if (mArch == ARMAssemblerInterface::CODEGEN_ARCH_MIPS) {
+    if ((mArch == ARMAssemblerInterface::CODEGEN_ARCH_MIPS) ||
+        (mArch == ARMAssemblerInterface::CODEGEN_ARCH_MIPS64)) {
         mRegisterOffset = 2;    // ARM has regs 0..15, MIPS offset to 2..17
     }
     reserve(ARMAssemblerInterface::SP);
@@ -1067,7 +1069,8 @@
 RegisterAllocator::RegisterFile::RegisterFile(const RegisterFile& rhs, int codegen_arch)
     : mRegs(rhs.mRegs), mTouched(rhs.mTouched), mArch(codegen_arch), mRegisterOffset(0)
 {
-    if (mArch == ARMAssemblerInterface::CODEGEN_ARCH_MIPS) {
+    if ((mArch == ARMAssemblerInterface::CODEGEN_ARCH_MIPS) ||
+        (mArch == ARMAssemblerInterface::CODEGEN_ARCH_MIPS64)) {
         mRegisterOffset = 2;    // ARM has regs 0..15, MIPS offset to 2..17
     }
 }
diff --git a/libpixelflinger/codeflinger/MIPS64Assembler.cpp b/libpixelflinger/codeflinger/MIPS64Assembler.cpp
new file mode 100644
index 0000000..a5305cc
--- /dev/null
+++ b/libpixelflinger/codeflinger/MIPS64Assembler.cpp
@@ -0,0 +1,1452 @@
+/* libs/pixelflinger/codeflinger/MIPS64Assembler.cpp
+**
+** Copyright 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.
+*/
+
+
+/* MIPS64 assembler and ARM->MIPS64 assembly translator
+**
+** The approach is utilize MIPSAssembler generator, using inherited MIPS64Assembler
+** that overrides just the specific MIPS64r6 instructions.
+** For now ArmToMips64Assembler is copied over from ArmToMipsAssembler class,
+** changing some MIPS64r6 related stuff.
+**
+*/
+
+
+#define LOG_TAG "MIPS64Assembler"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <cutils/log.h>
+#include <cutils/properties.h>
+
+#if defined(WITH_LIB_HARDWARE)
+#include <hardware_legacy/qemu_tracing.h>
+#endif
+
+#include <private/pixelflinger/ggl_context.h>
+
+#include "MIPS64Assembler.h"
+#include "CodeCache.h"
+#include "mips64_disassem.h"
+
+
+#define NOT_IMPLEMENTED()  LOG_ALWAYS_FATAL("Arm instruction %s not yet implemented\n", __func__)
+
+
+// ----------------------------------------------------------------------------
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+#if 0
+#pragma mark -
+#pragma mark ArmToMips64Assembler...
+#endif
+
+ArmToMips64Assembler::ArmToMips64Assembler(const sp<Assembly>& assembly,
+                                           char *abuf, int linesz, int instr_count)
+    :   ARMAssemblerInterface(),
+        mArmDisassemblyBuffer(abuf),
+        mArmLineLength(linesz),
+        mArmInstrCount(instr_count),
+        mInum(0),
+        mAssembly(assembly)
+{
+    mMips = new MIPS64Assembler(assembly, this);
+    mArmPC = (uint32_t **) malloc(ARM_MAX_INSTUCTIONS * sizeof(uint32_t *));
+    init_conditional_labels();
+}
+
+ArmToMips64Assembler::ArmToMips64Assembler(void* assembly)
+    :   ARMAssemblerInterface(),
+        mArmDisassemblyBuffer(NULL),
+        mInum(0),
+        mAssembly(NULL)
+{
+    mMips = new MIPS64Assembler(assembly, this);
+    mArmPC = (uint32_t **) malloc(ARM_MAX_INSTUCTIONS * sizeof(uint32_t *));
+    init_conditional_labels();
+}
+
+ArmToMips64Assembler::~ArmToMips64Assembler()
+{
+    delete mMips;
+    free((void *) mArmPC);
+}
+
+uint32_t* ArmToMips64Assembler::pc() const
+{
+    return mMips->pc();
+}
+
+uint32_t* ArmToMips64Assembler::base() const
+{
+    return mMips->base();
+}
+
+void ArmToMips64Assembler::reset()
+{
+    cond.labelnum = 0;
+    mInum = 0;
+    mMips->reset();
+}
+
+int ArmToMips64Assembler::getCodegenArch()
+{
+    return CODEGEN_ARCH_MIPS64;
+}
+
+void ArmToMips64Assembler::comment(const char* string)
+{
+    mMips->comment(string);
+}
+
+void ArmToMips64Assembler::label(const char* theLabel)
+{
+    mMips->label(theLabel);
+}
+
+void ArmToMips64Assembler::disassemble(const char* name)
+{
+    mMips->disassemble(name);
+}
+
+void ArmToMips64Assembler::init_conditional_labels()
+{
+    int i;
+    for (i=0;i<99; ++i) {
+        sprintf(cond.label[i], "cond_%d", i);
+    }
+}
+
+
+
+#if 0
+#pragma mark -
+#pragma mark Prolog/Epilog & Generate...
+#endif
+
+void ArmToMips64Assembler::prolog()
+{
+    mArmPC[mInum++] = pc();  // save starting PC for this instr
+
+    mMips->DADDIU(R_sp, R_sp, -(5 * 8));
+    mMips->SD(R_s0, R_sp, 0);
+    mMips->SD(R_s1, R_sp, 8);
+    mMips->SD(R_s2, R_sp, 16);
+    mMips->SD(R_s3, R_sp, 24);
+    mMips->SD(R_s4, R_sp, 32);
+    mMips->MOVE(R_v0, R_a0);    // move context * passed in a0 to v0 (arm r0)
+}
+
+void ArmToMips64Assembler::epilog(uint32_t touched)
+{
+    mArmPC[mInum++] = pc();  // save starting PC for this instr
+
+    mMips->LD(R_s0, R_sp, 0);
+    mMips->LD(R_s1, R_sp, 8);
+    mMips->LD(R_s2, R_sp, 16);
+    mMips->LD(R_s3, R_sp, 24);
+    mMips->LD(R_s4, R_sp, 32);
+    mMips->DADDIU(R_sp, R_sp, (5 * 8));
+    mMips->JR(R_ra);
+
+}
+
+int ArmToMips64Assembler::generate(const char* name)
+{
+    return mMips->generate(name);
+}
+
+void ArmToMips64Assembler::fix_branches()
+{
+    mMips->fix_branches();
+}
+
+uint32_t* ArmToMips64Assembler::pcForLabel(const char* label)
+{
+    return mMips->pcForLabel(label);
+}
+
+void ArmToMips64Assembler::set_condition(int mode, int R1, int R2) {
+    if (mode == 2) {
+        cond.type = SBIT_COND;
+    } else {
+        cond.type = CMP_COND;
+    }
+    cond.r1 = R1;
+    cond.r2 = R2;
+}
+
+//----------------------------------------------------------
+
+#if 0
+#pragma mark -
+#pragma mark Addressing modes & shifters...
+#endif
+
+
+// do not need this for MIPS, but it is in the Interface (virtual)
+int ArmToMips64Assembler::buildImmediate(
+        uint32_t immediate, uint32_t& rot, uint32_t& imm)
+{
+    // for MIPS, any 32-bit immediate is OK
+    rot = 0;
+    imm = immediate;
+    return 0;
+}
+
+// shifters...
+
+bool ArmToMips64Assembler::isValidImmediate(uint32_t immediate)
+{
+    // for MIPS, any 32-bit immediate is OK
+    return true;
+}
+
+uint32_t ArmToMips64Assembler::imm(uint32_t immediate)
+{
+    amode.value = immediate;
+    return AMODE_IMM;
+}
+
+uint32_t ArmToMips64Assembler::reg_imm(int Rm, int type, uint32_t shift)
+{
+    amode.reg = Rm;
+    amode.stype = type;
+    amode.value = shift;
+    return AMODE_REG_IMM;
+}
+
+uint32_t ArmToMips64Assembler::reg_rrx(int Rm)
+{
+    // reg_rrx mode is not used in the GLLAssember code at this time
+    return AMODE_UNSUPPORTED;
+}
+
+uint32_t ArmToMips64Assembler::reg_reg(int Rm, int type, int Rs)
+{
+    // reg_reg mode is not used in the GLLAssember code at this time
+    return AMODE_UNSUPPORTED;
+}
+
+
+// addressing modes...
+// LDR(B)/STR(B)/PLD (immediate and Rm can be negative, which indicate U=0)
+uint32_t ArmToMips64Assembler::immed12_pre(int32_t immed12, int W)
+{
+    LOG_ALWAYS_FATAL_IF(abs(immed12) >= 0x800,
+                        "LDR(B)/STR(B)/PLD immediate too big (%08x)",
+                        immed12);
+    amode.value = immed12;
+    amode.writeback = W;
+    return AMODE_IMM_12_PRE;
+}
+
+uint32_t ArmToMips64Assembler::immed12_post(int32_t immed12)
+{
+    LOG_ALWAYS_FATAL_IF(abs(immed12) >= 0x800,
+                        "LDR(B)/STR(B)/PLD immediate too big (%08x)",
+                        immed12);
+
+    amode.value = immed12;
+    return AMODE_IMM_12_POST;
+}
+
+uint32_t ArmToMips64Assembler::reg_scale_pre(int Rm, int type,
+        uint32_t shift, int W)
+{
+    LOG_ALWAYS_FATAL_IF(W | type | shift, "reg_scale_pre adv modes not yet implemented");
+
+    amode.reg = Rm;
+    // amode.stype = type;      // more advanced modes not used in GGLAssembler yet
+    // amode.value = shift;
+    // amode.writeback = W;
+    return AMODE_REG_SCALE_PRE;
+}
+
+uint32_t ArmToMips64Assembler::reg_scale_post(int Rm, int type, uint32_t shift)
+{
+    LOG_ALWAYS_FATAL("adr mode reg_scale_post not yet implemented\n");
+    return AMODE_UNSUPPORTED;
+}
+
+// LDRH/LDRSB/LDRSH/STRH (immediate and Rm can be negative, which indicate U=0)
+uint32_t ArmToMips64Assembler::immed8_pre(int32_t immed8, int W)
+{
+    LOG_ALWAYS_FATAL("adr mode immed8_pre not yet implemented\n");
+
+    LOG_ALWAYS_FATAL_IF(abs(immed8) >= 0x100,
+                        "LDRH/LDRSB/LDRSH/STRH immediate too big (%08x)",
+                        immed8);
+    return AMODE_IMM_8_PRE;
+}
+
+uint32_t ArmToMips64Assembler::immed8_post(int32_t immed8)
+{
+    LOG_ALWAYS_FATAL_IF(abs(immed8) >= 0x100,
+                        "LDRH/LDRSB/LDRSH/STRH immediate too big (%08x)",
+                        immed8);
+    amode.value = immed8;
+    return AMODE_IMM_8_POST;
+}
+
+uint32_t ArmToMips64Assembler::reg_pre(int Rm, int W)
+{
+    LOG_ALWAYS_FATAL_IF(W, "reg_pre writeback not yet implemented");
+    amode.reg = Rm;
+    return AMODE_REG_PRE;
+}
+
+uint32_t ArmToMips64Assembler::reg_post(int Rm)
+{
+    LOG_ALWAYS_FATAL("adr mode reg_post not yet implemented\n");
+    return AMODE_UNSUPPORTED;
+}
+
+
+
+// ----------------------------------------------------------------------------
+
+#if 0
+#pragma mark -
+#pragma mark Data Processing...
+#endif
+
+
+static const char * const dpOpNames[] = {
+    "AND", "EOR", "SUB", "RSB", "ADD", "ADC", "SBC", "RSC",
+    "TST", "TEQ", "CMP", "CMN", "ORR", "MOV", "BIC", "MVN"
+};
+
+// check if the operand registers from a previous CMP or S-bit instruction
+// would be overwritten by this instruction. If so, move the value to a
+// safe register.
+// Note that we cannot tell at _this_ instruction time if a future (conditional)
+// instruction will _also_ use this value (a defect of the simple 1-pass, one-
+// instruction-at-a-time translation). Therefore we must be conservative and
+// save the value before it is overwritten. This costs an extra MOVE instr.
+
+void ArmToMips64Assembler::protectConditionalOperands(int Rd)
+{
+    if (Rd == cond.r1) {
+        mMips->MOVE(R_cmp, cond.r1);
+        cond.r1 = R_cmp;
+    }
+    if (cond.type == CMP_COND && Rd == cond.r2) {
+        mMips->MOVE(R_cmp2, cond.r2);
+        cond.r2 = R_cmp2;
+    }
+}
+
+
+// interprets the addressing mode, and generates the common code
+// used by the majority of data-processing ops. Many MIPS instructions
+// have a register-based form and a different immediate form. See
+// opAND below for an example. (this could be inlined)
+//
+// this works with the imm(), reg_imm() methods above, which are directly
+// called by the GLLAssembler.
+// note: _signed parameter defaults to false (un-signed)
+// note: tmpReg parameter defaults to 1, MIPS register AT
+int ArmToMips64Assembler::dataProcAdrModes(int op, int& source, bool _signed, int tmpReg)
+{
+    if (op < AMODE_REG) {
+        source = op;
+        return SRC_REG;
+    } else if (op == AMODE_IMM) {
+        if ((!_signed && amode.value > 0xffff)
+                || (_signed && ((int)amode.value < -32768 || (int)amode.value > 32767) )) {
+            mMips->LUI(tmpReg, (amode.value >> 16));
+            if (amode.value & 0x0000ffff) {
+                mMips->ORI(tmpReg, tmpReg, (amode.value & 0x0000ffff));
+            }
+            source = tmpReg;
+            return SRC_REG;
+        } else {
+            source = amode.value;
+            return SRC_IMM;
+        }
+    } else if (op == AMODE_REG_IMM) {
+        switch (amode.stype) {
+            case LSL: mMips->SLL(tmpReg, amode.reg, amode.value); break;
+            case LSR: mMips->SRL(tmpReg, amode.reg, amode.value); break;
+            case ASR: mMips->SRA(tmpReg, amode.reg, amode.value); break;
+            case ROR: mMips->ROTR(tmpReg, amode.reg, amode.value); break;
+        }
+        source = tmpReg;
+        return SRC_REG;
+    } else {  // adr mode RRX is not used in GGL Assembler at this time
+        // we are screwed, this should be exception, assert-fail or something
+        LOG_ALWAYS_FATAL("adr mode reg_rrx not yet implemented\n");
+        return SRC_ERROR;
+    }
+}
+
+
+void ArmToMips64Assembler::dataProcessing(int opcode, int cc,
+        int s, int Rd, int Rn, uint32_t Op2)
+{
+    int src;    // src is modified by dataProcAdrModes() - passed as int&
+
+    if (cc != AL) {
+        protectConditionalOperands(Rd);
+        // the branch tests register(s) set by prev CMP or instr with 'S' bit set
+        // inverse the condition to jump past this conditional instruction
+        ArmToMips64Assembler::B(cc^1, cond.label[++cond.labelnum]);
+    } else {
+        mArmPC[mInum++] = pc();  // save starting PC for this instr
+    }
+
+    switch (opcode) {
+    case opAND:
+        if (dataProcAdrModes(Op2, src) == SRC_REG) {
+            mMips->AND(Rd, Rn, src);
+        } else {                        // adr mode was SRC_IMM
+            mMips->ANDI(Rd, Rn, src);
+        }
+        break;
+
+    case opADD:
+        // set "signed" to true for adr modes
+        if (dataProcAdrModes(Op2, src, true) == SRC_REG) {
+            mMips->ADDU(Rd, Rn, src);
+        } else {                        // adr mode was SRC_IMM
+            mMips->ADDIU(Rd, Rn, src);
+        }
+        break;
+
+    case opSUB:
+        // set "signed" to true for adr modes
+        if (dataProcAdrModes(Op2, src, true) == SRC_REG) {
+            mMips->SUBU(Rd, Rn, src);
+        } else {                        // adr mode was SRC_IMM
+            mMips->SUBIU(Rd, Rn, src);
+        }
+        break;
+
+    case opADD64:
+        // set "signed" to true for adr modes
+        if (dataProcAdrModes(Op2, src, true) == SRC_REG) {
+            mMips->DADDU(Rd, Rn, src);
+        } else {                        // adr mode was SRC_IMM
+            mMips->DADDIU(Rd, Rn, src);
+        }
+        break;
+
+    case opSUB64:
+        // set "signed" to true for adr modes
+        if (dataProcAdrModes(Op2, src, true) == SRC_REG) {
+            mMips->DSUBU(Rd, Rn, src);
+        } else {                        // adr mode was SRC_IMM
+            mMips->DSUBIU(Rd, Rn, src);
+        }
+        break;
+
+    case opEOR:
+        if (dataProcAdrModes(Op2, src) == SRC_REG) {
+            mMips->XOR(Rd, Rn, src);
+        } else {                        // adr mode was SRC_IMM
+            mMips->XORI(Rd, Rn, src);
+        }
+        break;
+
+    case opORR:
+        if (dataProcAdrModes(Op2, src) == SRC_REG) {
+            mMips->OR(Rd, Rn, src);
+        } else {                        // adr mode was SRC_IMM
+            mMips->ORI(Rd, Rn, src);
+        }
+        break;
+
+    case opBIC:
+        if (dataProcAdrModes(Op2, src) == SRC_IMM) {
+            // if we are 16-bit imnmediate, load to AT reg
+            mMips->ORI(R_at, 0, src);
+            src = R_at;
+        }
+        mMips->NOT(R_at, src);
+        mMips->AND(Rd, Rn, R_at);
+        break;
+
+    case opRSB:
+        if (dataProcAdrModes(Op2, src) == SRC_IMM) {
+            // if we are 16-bit imnmediate, load to AT reg
+            mMips->ORI(R_at, 0, src);
+            src = R_at;
+        }
+        mMips->SUBU(Rd, src, Rn);   // subu with the parameters reversed
+        break;
+
+    case opMOV:
+        if (Op2 < AMODE_REG) {  // op2 is reg # in this case
+            mMips->MOVE(Rd, Op2);
+        } else if (Op2 == AMODE_IMM) {
+            if (amode.value > 0xffff) {
+                mMips->LUI(Rd, (amode.value >> 16));
+                if (amode.value & 0x0000ffff) {
+                    mMips->ORI(Rd, Rd, (amode.value & 0x0000ffff));
+                }
+             } else {
+                mMips->ORI(Rd, 0, amode.value);
+            }
+        } else if (Op2 == AMODE_REG_IMM) {
+            switch (amode.stype) {
+            case LSL: mMips->SLL(Rd, amode.reg, amode.value); break;
+            case LSR: mMips->SRL(Rd, amode.reg, amode.value); break;
+            case ASR: mMips->SRA(Rd, amode.reg, amode.value); break;
+            case ROR: mMips->ROTR(Rd, amode.reg, amode.value); break;
+            }
+        }
+        else {
+            // adr mode RRX is not used in GGL Assembler at this time
+            mMips->UNIMPL();
+        }
+        break;
+
+    case opMVN:     // this is a 1's complement: NOT
+        if (Op2 < AMODE_REG) {  // op2 is reg # in this case
+            mMips->NOR(Rd, Op2, 0);     // NOT is NOR with 0
+            break;
+        } else if (Op2 == AMODE_IMM) {
+            if (amode.value > 0xffff) {
+                mMips->LUI(Rd, (amode.value >> 16));
+                if (amode.value & 0x0000ffff) {
+                    mMips->ORI(Rd, Rd, (amode.value & 0x0000ffff));
+                }
+             } else {
+                mMips->ORI(Rd, 0, amode.value);
+             }
+        } else if (Op2 == AMODE_REG_IMM) {
+            switch (amode.stype) {
+            case LSL: mMips->SLL(Rd, amode.reg, amode.value); break;
+            case LSR: mMips->SRL(Rd, amode.reg, amode.value); break;
+            case ASR: mMips->SRA(Rd, amode.reg, amode.value); break;
+            case ROR: mMips->ROTR(Rd, amode.reg, amode.value); break;
+            }
+        }
+        else {
+            // adr mode RRX is not used in GGL Assembler at this time
+            mMips->UNIMPL();
+        }
+        mMips->NOR(Rd, Rd, 0);     // NOT is NOR with 0
+        break;
+
+    case opCMP:
+        // Either operand of a CMP instr could get overwritten by a subsequent
+        // conditional instruction, which is ok, _UNLESS_ there is a _second_
+        // conditional instruction. Under MIPS, this requires doing the comparison
+        // again (SLT), and the original operands must be available. (and this
+        // pattern of multiple conditional instructions from same CMP _is_ used
+        // in GGL-Assembler)
+        //
+        // For now, if a conditional instr overwrites the operands, we will
+        // move them to dedicated temp regs. This is ugly, and inefficient,
+        // and should be optimized.
+        //
+        // WARNING: making an _Assumption_ that CMP operand regs will NOT be
+        // trashed by intervening NON-conditional instructions. In the general
+        // case this is legal, but it is NOT currently done in GGL-Assembler.
+
+        cond.type = CMP_COND;
+        cond.r1 = Rn;
+        if (dataProcAdrModes(Op2, src, false, R_cmp2) == SRC_REG) {
+            cond.r2 = src;
+        } else {                        // adr mode was SRC_IMM
+            mMips->ORI(R_cmp2, R_zero, src);
+            cond.r2 = R_cmp2;
+        }
+
+        break;
+
+
+    case opTST:
+    case opTEQ:
+    case opCMN:
+    case opADC:
+    case opSBC:
+    case opRSC:
+        mMips->UNIMPL(); // currently unused in GGL Assembler code
+        break;
+    }
+
+    if (cc != AL) {
+        mMips->label(cond.label[cond.labelnum]);
+    }
+    if (s && opcode != opCMP) {
+        cond.type = SBIT_COND;
+        cond.r1 = Rd;
+    }
+}
+
+
+
+#if 0
+#pragma mark -
+#pragma mark Multiply...
+#endif
+
+// multiply, accumulate
+void ArmToMips64Assembler::MLA(int cc, int s,
+        int Rd, int Rm, int Rs, int Rn) {
+
+    //ALOGW("MLA");
+    mArmPC[mInum++] = pc();  // save starting PC for this instr
+
+    mMips->MUL(R_at, Rm, Rs);
+    mMips->ADDU(Rd, R_at, Rn);
+    if (s) {
+        cond.type = SBIT_COND;
+        cond.r1 = Rd;
+    }
+}
+
+void ArmToMips64Assembler::MUL(int cc, int s,
+        int Rd, int Rm, int Rs) {
+    mArmPC[mInum++] = pc();
+    mMips->MUL(Rd, Rm, Rs);
+    if (s) {
+        cond.type = SBIT_COND;
+        cond.r1 = Rd;
+    }
+}
+
+void ArmToMips64Assembler::UMULL(int cc, int s,
+        int RdLo, int RdHi, int Rm, int Rs) {
+    mArmPC[mInum++] = pc();
+    mMips->MUH(RdHi, Rm, Rs);
+    mMips->MUL(RdLo, Rm, Rs);
+
+    if (s) {
+        cond.type = SBIT_COND;
+        cond.r1 = RdHi;     // BUG...
+        LOG_ALWAYS_FATAL("Condition on UMULL must be on 64-bit result\n");
+    }
+}
+
+void ArmToMips64Assembler::UMUAL(int cc, int s,
+        int RdLo, int RdHi, int Rm, int Rs) {
+    LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi,
+                        "UMUAL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs);
+    // *mPC++ =    (cc<<28) | (1<<23) | (1<<21) | (s<<20) |
+    //             (RdHi<<16) | (RdLo<<12) | (Rs<<8) | 0x90 | Rm;
+    mArmPC[mInum++] = pc();
+    mMips->NOP2();
+    NOT_IMPLEMENTED();
+    if (s) {
+        cond.type = SBIT_COND;
+        cond.r1 = RdHi;     // BUG...
+        LOG_ALWAYS_FATAL("Condition on UMULL must be on 64-bit result\n");
+    }
+}
+
+void ArmToMips64Assembler::SMULL(int cc, int s,
+        int RdLo, int RdHi, int Rm, int Rs) {
+    LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi,
+                        "SMULL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs);
+    // *mPC++ =    (cc<<28) | (1<<23) | (1<<22) | (s<<20) |
+    //             (RdHi<<16) | (RdLo<<12) | (Rs<<8) | 0x90 | Rm;
+    mArmPC[mInum++] = pc();
+    mMips->NOP2();
+    NOT_IMPLEMENTED();
+    if (s) {
+        cond.type = SBIT_COND;
+        cond.r1 = RdHi;     // BUG...
+        LOG_ALWAYS_FATAL("Condition on SMULL must be on 64-bit result\n");
+    }
+}
+void ArmToMips64Assembler::SMUAL(int cc, int s,
+        int RdLo, int RdHi, int Rm, int Rs) {
+    LOG_FATAL_IF(RdLo==Rm || RdHi==Rm || RdLo==RdHi,
+                        "SMUAL(r%u,r%u,r%u,r%u)", RdLo,RdHi,Rm,Rs);
+    // *mPC++ =    (cc<<28) | (1<<23) | (1<<22) | (1<<21) | (s<<20) |
+    //             (RdHi<<16) | (RdLo<<12) | (Rs<<8) | 0x90 | Rm;
+    mArmPC[mInum++] = pc();
+    mMips->NOP2();
+    NOT_IMPLEMENTED();
+    if (s) {
+        cond.type = SBIT_COND;
+        cond.r1 = RdHi;     // BUG...
+        LOG_ALWAYS_FATAL("Condition on SMUAL must be on 64-bit result\n");
+    }
+}
+
+
+
+#if 0
+#pragma mark -
+#pragma mark Branches...
+#endif
+
+// branches...
+
+void ArmToMips64Assembler::B(int cc, const char* label)
+{
+    mArmPC[mInum++] = pc();
+    if (cond.type == SBIT_COND) { cond.r2 = R_zero; }
+
+    switch(cc) {
+        case EQ: mMips->BEQ(cond.r1, cond.r2, label); break;
+        case NE: mMips->BNE(cond.r1, cond.r2, label); break;
+        case HS: mMips->BGEU(cond.r1, cond.r2, label); break;
+        case LO: mMips->BLTU(cond.r1, cond.r2, label); break;
+        case MI: mMips->BLT(cond.r1, cond.r2, label); break;
+        case PL: mMips->BGE(cond.r1, cond.r2, label); break;
+
+        case HI: mMips->BGTU(cond.r1, cond.r2, label); break;
+        case LS: mMips->BLEU(cond.r1, cond.r2, label); break;
+        case GE: mMips->BGE(cond.r1, cond.r2, label); break;
+        case LT: mMips->BLT(cond.r1, cond.r2, label); break;
+        case GT: mMips->BGT(cond.r1, cond.r2, label); break;
+        case LE: mMips->BLE(cond.r1, cond.r2, label); break;
+        case AL: mMips->B(label); break;
+        case NV: /* B Never - no instruction */ break;
+
+        case VS:
+        case VC:
+        default:
+            LOG_ALWAYS_FATAL("Unsupported cc: %02x\n", cc);
+            break;
+    }
+}
+
+void ArmToMips64Assembler::BL(int cc, const char* label)
+{
+    LOG_ALWAYS_FATAL("branch-and-link not supported yet\n");
+    mArmPC[mInum++] = pc();
+}
+
+// no use for Branches with integer PC, but they're in the Interface class ....
+void ArmToMips64Assembler::B(int cc, uint32_t* to_pc)
+{
+    LOG_ALWAYS_FATAL("branch to absolute PC not supported, use Label\n");
+    mArmPC[mInum++] = pc();
+}
+
+void ArmToMips64Assembler::BL(int cc, uint32_t* to_pc)
+{
+    LOG_ALWAYS_FATAL("branch to absolute PC not supported, use Label\n");
+    mArmPC[mInum++] = pc();
+}
+
+void ArmToMips64Assembler::BX(int cc, int Rn)
+{
+    LOG_ALWAYS_FATAL("branch to absolute PC not supported, use Label\n");
+    mArmPC[mInum++] = pc();
+}
+
+
+
+#if 0
+#pragma mark -
+#pragma mark Data Transfer...
+#endif
+
+// data transfer...
+void ArmToMips64Assembler::LDR(int cc, int Rd, int Rn, uint32_t offset)
+{
+    mArmPC[mInum++] = pc();
+    // work-around for ARM default address mode of immed12_pre(0)
+    if (offset > AMODE_UNSUPPORTED) offset = 0;
+    switch (offset) {
+        case 0:
+            amode.value = 0;
+            amode.writeback = 0;
+            // fall thru to next case ....
+        case AMODE_IMM_12_PRE:
+            if (Rn == ARMAssemblerInterface::SP) {
+                Rn = R_sp;      // convert LDR via Arm SP to LW via Mips SP
+            }
+            mMips->LW(Rd, Rn, amode.value);
+            if (amode.writeback) {      // OPTIONAL writeback on pre-index mode
+                mMips->DADDIU(Rn, Rn, amode.value);
+            }
+            break;
+        case AMODE_IMM_12_POST:
+            if (Rn == ARMAssemblerInterface::SP) {
+                Rn = R_sp;      // convert STR thru Arm SP to STR thru Mips SP
+            }
+            mMips->LW(Rd, Rn, 0);
+            mMips->DADDIU(Rn, Rn, amode.value);
+            break;
+        case AMODE_REG_SCALE_PRE:
+            // we only support simple base + index, no advanced modes for this one yet
+            mMips->DADDU(R_at, Rn, amode.reg);
+            mMips->LW(Rd, R_at, 0);
+            break;
+    }
+}
+
+void ArmToMips64Assembler::LDRB(int cc, int Rd, int Rn, uint32_t offset)
+{
+    mArmPC[mInum++] = pc();
+    // work-around for ARM default address mode of immed12_pre(0)
+    if (offset > AMODE_UNSUPPORTED) offset = 0;
+    switch (offset) {
+        case 0:
+            amode.value = 0;
+            amode.writeback = 0;
+            // fall thru to next case ....
+        case AMODE_IMM_12_PRE:
+            mMips->LBU(Rd, Rn, amode.value);
+            if (amode.writeback) {      // OPTIONAL writeback on pre-index mode
+                mMips->DADDIU(Rn, Rn, amode.value);
+            }
+            break;
+        case AMODE_IMM_12_POST:
+            mMips->LBU(Rd, Rn, 0);
+            mMips->DADDIU(Rn, Rn, amode.value);
+            break;
+        case AMODE_REG_SCALE_PRE:
+            // we only support simple base + index, no advanced modes for this one yet
+            mMips->DADDU(R_at, Rn, amode.reg);
+            mMips->LBU(Rd, R_at, 0);
+            break;
+    }
+
+}
+
+void ArmToMips64Assembler::STR(int cc, int Rd, int Rn, uint32_t offset)
+{
+    mArmPC[mInum++] = pc();
+    // work-around for ARM default address mode of immed12_pre(0)
+    if (offset > AMODE_UNSUPPORTED) offset = 0;
+    switch (offset) {
+        case 0:
+            amode.value = 0;
+            amode.writeback = 0;
+            // fall thru to next case ....
+        case AMODE_IMM_12_PRE:
+            if (Rn == ARMAssemblerInterface::SP) {
+                Rn = R_sp;  // convert STR thru Arm SP to SW thru Mips SP
+            }
+            if (amode.writeback) {      // OPTIONAL writeback on pre-index mode
+                // If we will writeback, then update the index reg, then store.
+                // This correctly handles stack-push case.
+                mMips->DADDIU(Rn, Rn, amode.value);
+                mMips->SW(Rd, Rn, 0);
+            } else {
+                // No writeback so store offset by value
+                mMips->SW(Rd, Rn, amode.value);
+            }
+            break;
+        case AMODE_IMM_12_POST:
+            mMips->SW(Rd, Rn, 0);
+            mMips->DADDIU(Rn, Rn, amode.value);  // post index always writes back
+            break;
+        case AMODE_REG_SCALE_PRE:
+            // we only support simple base + index, no advanced modes for this one yet
+            mMips->DADDU(R_at, Rn, amode.reg);
+            mMips->SW(Rd, R_at, 0);
+            break;
+    }
+}
+
+void ArmToMips64Assembler::STRB(int cc, int Rd, int Rn, uint32_t offset)
+{
+    mArmPC[mInum++] = pc();
+    // work-around for ARM default address mode of immed12_pre(0)
+    if (offset > AMODE_UNSUPPORTED) offset = 0;
+    switch (offset) {
+        case 0:
+            amode.value = 0;
+            amode.writeback = 0;
+            // fall thru to next case ....
+        case AMODE_IMM_12_PRE:
+            mMips->SB(Rd, Rn, amode.value);
+            if (amode.writeback) {      // OPTIONAL writeback on pre-index mode
+                mMips->DADDIU(Rn, Rn, amode.value);
+            }
+            break;
+        case AMODE_IMM_12_POST:
+            mMips->SB(Rd, Rn, 0);
+            mMips->DADDIU(Rn, Rn, amode.value);
+            break;
+        case AMODE_REG_SCALE_PRE:
+            // we only support simple base + index, no advanced modes for this one yet
+            mMips->DADDU(R_at, Rn, amode.reg);
+            mMips->SB(Rd, R_at, 0);
+            break;
+    }
+}
+
+void ArmToMips64Assembler::LDRH(int cc, int Rd, int Rn, uint32_t offset)
+{
+    mArmPC[mInum++] = pc();
+    // work-around for ARM default address mode of immed8_pre(0)
+    if (offset > AMODE_UNSUPPORTED) offset = 0;
+    switch (offset) {
+        case 0:
+            amode.value = 0;
+            // fall thru to next case ....
+        case AMODE_IMM_8_PRE:      // no support yet for writeback
+            mMips->LHU(Rd, Rn, amode.value);
+            break;
+        case AMODE_IMM_8_POST:
+            mMips->LHU(Rd, Rn, 0);
+            mMips->DADDIU(Rn, Rn, amode.value);
+            break;
+        case AMODE_REG_PRE:
+            // we only support simple base +/- index
+            if (amode.reg >= 0) {
+                mMips->DADDU(R_at, Rn, amode.reg);
+            } else {
+                mMips->DSUBU(R_at, Rn, abs(amode.reg));
+            }
+            mMips->LHU(Rd, R_at, 0);
+            break;
+    }
+}
+
+void ArmToMips64Assembler::LDRSB(int cc, int Rd, int Rn, uint32_t offset)
+{
+    mArmPC[mInum++] = pc();
+    mMips->NOP2();
+    NOT_IMPLEMENTED();
+}
+
+void ArmToMips64Assembler::LDRSH(int cc, int Rd, int Rn, uint32_t offset)
+{
+    mArmPC[mInum++] = pc();
+    mMips->NOP2();
+    NOT_IMPLEMENTED();
+}
+
+void ArmToMips64Assembler::STRH(int cc, int Rd, int Rn, uint32_t offset)
+{
+    mArmPC[mInum++] = pc();
+    // work-around for ARM default address mode of immed8_pre(0)
+    if (offset > AMODE_UNSUPPORTED) offset = 0;
+    switch (offset) {
+        case 0:
+            amode.value = 0;
+            // fall thru to next case ....
+        case AMODE_IMM_8_PRE:      // no support yet for writeback
+            mMips->SH(Rd, Rn, amode.value);
+            break;
+        case AMODE_IMM_8_POST:
+            mMips->SH(Rd, Rn, 0);
+            mMips->DADDIU(Rn, Rn, amode.value);
+            break;
+        case AMODE_REG_PRE:
+            // we only support simple base +/- index
+            if (amode.reg >= 0) {
+                mMips->DADDU(R_at, Rn, amode.reg);
+            } else {
+                mMips->DSUBU(R_at, Rn, abs(amode.reg));
+            }
+            mMips->SH(Rd, R_at, 0);
+            break;
+    }
+}
+
+
+
+#if 0
+#pragma mark -
+#pragma mark Block Data Transfer...
+#endif
+
+// block data transfer...
+void ArmToMips64Assembler::LDM(int cc, int dir,
+        int Rn, int W, uint32_t reg_list)
+{   //                        ED FD EA FA      IB IA DB DA
+    // const uint8_t P[8] = { 1, 0, 1, 0,      1, 0, 1, 0 };
+    // const uint8_t U[8] = { 1, 1, 0, 0,      1, 1, 0, 0 };
+    // *mPC++ = (cc<<28) | (4<<25) | (uint32_t(P[dir])<<24) |
+    //         (uint32_t(U[dir])<<23) | (1<<20) | (W<<21) | (Rn<<16) | reg_list;
+    mArmPC[mInum++] = pc();
+    mMips->NOP2();
+    NOT_IMPLEMENTED();
+}
+
+void ArmToMips64Assembler::STM(int cc, int dir,
+        int Rn, int W, uint32_t reg_list)
+{   //                        FA EA FD ED      IB IA DB DA
+    // const uint8_t P[8] = { 0, 1, 0, 1,      1, 0, 1, 0 };
+    // const uint8_t U[8] = { 0, 0, 1, 1,      1, 1, 0, 0 };
+    // *mPC++ = (cc<<28) | (4<<25) | (uint32_t(P[dir])<<24) |
+    //         (uint32_t(U[dir])<<23) | (0<<20) | (W<<21) | (Rn<<16) | reg_list;
+    mArmPC[mInum++] = pc();
+    mMips->NOP2();
+    NOT_IMPLEMENTED();
+}
+
+
+
+#if 0
+#pragma mark -
+#pragma mark Special...
+#endif
+
+// special...
+void ArmToMips64Assembler::SWP(int cc, int Rn, int Rd, int Rm) {
+    // *mPC++ = (cc<<28) | (2<<23) | (Rn<<16) | (Rd << 12) | 0x90 | Rm;
+    mArmPC[mInum++] = pc();
+    mMips->NOP2();
+    NOT_IMPLEMENTED();
+}
+
+void ArmToMips64Assembler::SWPB(int cc, int Rn, int Rd, int Rm) {
+    // *mPC++ = (cc<<28) | (2<<23) | (1<<22) | (Rn<<16) | (Rd << 12) | 0x90 | Rm;
+    mArmPC[mInum++] = pc();
+    mMips->NOP2();
+    NOT_IMPLEMENTED();
+}
+
+void ArmToMips64Assembler::SWI(int cc, uint32_t comment) {
+    // *mPC++ = (cc<<28) | (0xF<<24) | comment;
+    mArmPC[mInum++] = pc();
+    mMips->NOP2();
+    NOT_IMPLEMENTED();
+}
+
+
+#if 0
+#pragma mark -
+#pragma mark DSP instructions...
+#endif
+
+// DSP instructions...
+void ArmToMips64Assembler::PLD(int Rn, uint32_t offset) {
+    LOG_ALWAYS_FATAL_IF(!((offset&(1<<24)) && !(offset&(1<<21))),
+                        "PLD only P=1, W=0");
+    // *mPC++ = 0xF550F000 | (Rn<<16) | offset;
+    mArmPC[mInum++] = pc();
+    mMips->NOP2();
+    NOT_IMPLEMENTED();
+}
+
+void ArmToMips64Assembler::CLZ(int cc, int Rd, int Rm)
+{
+    mArmPC[mInum++] = pc();
+    mMips->CLZ(Rd, Rm);
+}
+
+void ArmToMips64Assembler::QADD(int cc,  int Rd, int Rm, int Rn)
+{
+    // *mPC++ = (cc<<28) | 0x1000050 | (Rn<<16) | (Rd<<12) | Rm;
+    mArmPC[mInum++] = pc();
+    mMips->NOP2();
+    NOT_IMPLEMENTED();
+}
+
+void ArmToMips64Assembler::QDADD(int cc,  int Rd, int Rm, int Rn)
+{
+    // *mPC++ = (cc<<28) | 0x1400050 | (Rn<<16) | (Rd<<12) | Rm;
+    mArmPC[mInum++] = pc();
+    mMips->NOP2();
+    NOT_IMPLEMENTED();
+}
+
+void ArmToMips64Assembler::QSUB(int cc,  int Rd, int Rm, int Rn)
+{
+    // *mPC++ = (cc<<28) | 0x1200050 | (Rn<<16) | (Rd<<12) | Rm;
+    mArmPC[mInum++] = pc();
+    mMips->NOP2();
+    NOT_IMPLEMENTED();
+}
+
+void ArmToMips64Assembler::QDSUB(int cc,  int Rd, int Rm, int Rn)
+{
+    // *mPC++ = (cc<<28) | 0x1600050 | (Rn<<16) | (Rd<<12) | Rm;
+    mArmPC[mInum++] = pc();
+    mMips->NOP2();
+    NOT_IMPLEMENTED();
+}
+
+// 16 x 16 signed multiply (like SMLAxx without the accumulate)
+void ArmToMips64Assembler::SMUL(int cc, int xy,
+                int Rd, int Rm, int Rs)
+{
+    mArmPC[mInum++] = pc();
+
+    // the 16 bits may be in the top or bottom half of 32-bit source reg,
+    // as defined by the codes BB, BT, TB, TT (compressed param xy)
+    // where x corresponds to Rm and y to Rs
+
+    // select half-reg for Rm
+    if (xy & xyTB) {
+        // use top 16-bits
+        mMips->SRA(R_at, Rm, 16);
+    } else {
+        // use bottom 16, but sign-extend to 32
+        mMips->SEH(R_at, Rm);
+    }
+    // select half-reg for Rs
+    if (xy & xyBT) {
+        // use top 16-bits
+        mMips->SRA(R_at2, Rs, 16);
+    } else {
+        // use bottom 16, but sign-extend to 32
+        mMips->SEH(R_at2, Rs);
+    }
+    mMips->MUL(Rd, R_at, R_at2);
+}
+
+// signed 32b x 16b multiple, save top 32-bits of 48-bit result
+void ArmToMips64Assembler::SMULW(int cc, int y,
+                int Rd, int Rm, int Rs)
+{
+    mArmPC[mInum++] = pc();
+
+    // the selector yT or yB refers to reg Rs
+    if (y & yT) {
+        // zero the bottom 16-bits, with 2 shifts, it can affect result
+        mMips->SRL(R_at, Rs, 16);
+        mMips->SLL(R_at, R_at, 16);
+
+    } else {
+        // move low 16-bit half, to high half
+        mMips->SLL(R_at, Rs, 16);
+    }
+    mMips->MUH(Rd, Rm, R_at);
+}
+
+// 16 x 16 signed multiply, accumulate: Rd = Rm{16} * Rs{16} + Rn
+void ArmToMips64Assembler::SMLA(int cc, int xy,
+                int Rd, int Rm, int Rs, int Rn)
+{
+    mArmPC[mInum++] = pc();
+
+    // the 16 bits may be in the top or bottom half of 32-bit source reg,
+    // as defined by the codes BB, BT, TB, TT (compressed param xy)
+    // where x corresponds to Rm and y to Rs
+
+    // select half-reg for Rm
+    if (xy & xyTB) {
+        // use top 16-bits
+        mMips->SRA(R_at, Rm, 16);
+    } else {
+        // use bottom 16, but sign-extend to 32
+        mMips->SEH(R_at, Rm);
+    }
+    // select half-reg for Rs
+    if (xy & xyBT) {
+        // use top 16-bits
+        mMips->SRA(R_at2, Rs, 16);
+    } else {
+        // use bottom 16, but sign-extend to 32
+        mMips->SEH(R_at2, Rs);
+    }
+
+    mMips->MUL(R_at, R_at, R_at2);
+    mMips->ADDU(Rd, R_at, Rn);
+}
+
+void ArmToMips64Assembler::SMLAL(int cc, int xy,
+                int RdHi, int RdLo, int Rs, int Rm)
+{
+    // *mPC++ = (cc<<28) | 0x1400080 | (RdHi<<16) | (RdLo<<12) | (Rs<<8) | (xy<<4) | Rm;
+    mArmPC[mInum++] = pc();
+    mMips->NOP2();
+    NOT_IMPLEMENTED();
+}
+
+void ArmToMips64Assembler::SMLAW(int cc, int y,
+                int Rd, int Rm, int Rs, int Rn)
+{
+    // *mPC++ = (cc<<28) | 0x1200080 | (Rd<<16) | (Rn<<12) | (Rs<<8) | (y<<4) | Rm;
+    mArmPC[mInum++] = pc();
+    mMips->NOP2();
+    NOT_IMPLEMENTED();
+}
+
+// used by ARMv6 version of GGLAssembler::filter32
+void ArmToMips64Assembler::UXTB16(int cc, int Rd, int Rm, int rotate)
+{
+    mArmPC[mInum++] = pc();
+
+    //Rd[31:16] := ZeroExtend((Rm ROR (8 * sh))[23:16]),
+    //Rd[15:0] := ZeroExtend((Rm ROR (8 * sh))[7:0]). sh 0-3.
+
+    mMips->ROTR(R_at2, Rm, rotate * 8);
+    mMips->LUI(R_at, 0xFF);
+    mMips->ORI(R_at, R_at, 0xFF);
+    mMips->AND(Rd, R_at2, R_at);
+}
+
+void ArmToMips64Assembler::UBFX(int cc, int Rd, int Rn, int lsb, int width)
+{
+     /* Placeholder for UBFX */
+     mArmPC[mInum++] = pc();
+
+     mMips->NOP2();
+     NOT_IMPLEMENTED();
+}
+
+// ----------------------------------------------------------------------------
+// Address Processing...
+// ----------------------------------------------------------------------------
+
+void ArmToMips64Assembler::ADDR_ADD(int cc,
+        int s, int Rd, int Rn, uint32_t Op2)
+{
+//    if(cc != AL){ NOT_IMPLEMENTED(); return;} //Not required
+//    if(s  != 0) { NOT_IMPLEMENTED(); return;} //Not required
+    dataProcessing(opADD64, cc, s, Rd, Rn, Op2);
+}
+
+void ArmToMips64Assembler::ADDR_SUB(int cc,
+        int s, int Rd, int Rn, uint32_t Op2)
+{
+//    if(cc != AL){ NOT_IMPLEMENTED(); return;} //Not required
+//    if(s  != 0) { NOT_IMPLEMENTED(); return;} //Not required
+    dataProcessing(opSUB64, cc, s, Rd, Rn, Op2);
+}
+
+void ArmToMips64Assembler::ADDR_LDR(int cc, int Rd, int Rn, uint32_t offset) {
+    mArmPC[mInum++] = pc();
+    // work-around for ARM default address mode of immed12_pre(0)
+    if (offset > AMODE_UNSUPPORTED) offset = 0;
+    switch (offset) {
+        case 0:
+            amode.value = 0;
+            amode.writeback = 0;
+            // fall thru to next case ....
+        case AMODE_IMM_12_PRE:
+            if (Rn == ARMAssemblerInterface::SP) {
+                Rn = R_sp;      // convert LDR via Arm SP to LW via Mips SP
+            }
+            mMips->LD(Rd, Rn, amode.value);
+            if (amode.writeback) {      // OPTIONAL writeback on pre-index mode
+                mMips->DADDIU(Rn, Rn, amode.value);
+            }
+            break;
+        case AMODE_IMM_12_POST:
+            if (Rn == ARMAssemblerInterface::SP) {
+                Rn = R_sp;      // convert STR thru Arm SP to STR thru Mips SP
+            }
+            mMips->LD(Rd, Rn, 0);
+            mMips->DADDIU(Rn, Rn, amode.value);
+            break;
+        case AMODE_REG_SCALE_PRE:
+            // we only support simple base + index, no advanced modes for this one yet
+            mMips->DADDU(R_at, Rn, amode.reg);
+            mMips->LD(Rd, R_at, 0);
+            break;
+    }
+}
+
+void ArmToMips64Assembler::ADDR_STR(int cc, int Rd, int Rn, uint32_t offset) {
+    mArmPC[mInum++] = pc();
+    // work-around for ARM default address mode of immed12_pre(0)
+    if (offset > AMODE_UNSUPPORTED) offset = 0;
+    switch (offset) {
+        case 0:
+            amode.value = 0;
+            amode.writeback = 0;
+            // fall thru to next case ....
+        case AMODE_IMM_12_PRE:
+            if (Rn == ARMAssemblerInterface::SP) {
+                Rn = R_sp;  // convert STR thru Arm SP to SW thru Mips SP
+            }
+            if (amode.writeback) {      // OPTIONAL writeback on pre-index mode
+                // If we will writeback, then update the index reg, then store.
+                // This correctly handles stack-push case.
+                mMips->DADDIU(Rn, Rn, amode.value);
+                mMips->SD(Rd, Rn, 0);
+            } else {
+                // No writeback so store offset by value
+                mMips->SD(Rd, Rn, amode.value);
+            }
+            break;
+        case AMODE_IMM_12_POST:
+            mMips->SD(Rd, Rn, 0);
+            mMips->DADDIU(Rn, Rn, amode.value);  // post index always writes back
+            break;
+        case AMODE_REG_SCALE_PRE:
+            // we only support simple base + index, no advanced modes for this one yet
+            mMips->DADDU(R_at, Rn, amode.reg);
+            mMips->SD(Rd, R_at, 0);
+            break;
+    }
+}
+
+#if 0
+#pragma mark -
+#pragma mark MIPS Assembler...
+#endif
+
+
+//**************************************************************************
+//**************************************************************************
+//**************************************************************************
+
+
+/* MIPS64 assembler
+** this is a subset of mips64r6, targeted specifically at ARM instruction
+** replacement in the pixelflinger/codeflinger code.
+**
+** This class is extended from MIPSAssembler class and overrides only
+** MIPS64r6 specific stuff.
+*/
+
+MIPS64Assembler::MIPS64Assembler(const sp<Assembly>& assembly, ArmToMips64Assembler *parent)
+    : mParent(parent),
+    MIPSAssembler::MIPSAssembler(assembly, NULL)
+{
+}
+
+MIPS64Assembler::MIPS64Assembler(void* assembly, ArmToMips64Assembler *parent)
+    : mParent(parent),
+    MIPSAssembler::MIPSAssembler(NULL, NULL)
+{
+    mBase = mPC = (uint32_t *)assembly;
+}
+
+MIPS64Assembler::~MIPS64Assembler()
+{
+}
+
+void MIPS64Assembler::reset()
+{
+    if (mAssembly != NULL) {
+        mBase = mPC = (uint32_t *)mAssembly->base();
+    } else {
+        mPC = mBase = base();
+    }
+    mBranchTargets.clear();
+    mLabels.clear();
+    mLabelsInverseMapping.clear();
+    mComments.clear();
+}
+
+
+void MIPS64Assembler::disassemble(const char* name)
+{
+    char di_buf[140];
+
+    bool arm_disasm_fmt = (mParent->mArmDisassemblyBuffer == NULL) ? false : true;
+
+    typedef char dstr[40];
+    dstr *lines = (dstr *)mParent->mArmDisassemblyBuffer;
+
+    if (mParent->mArmDisassemblyBuffer != NULL) {
+        for (int i=0; i<mParent->mArmInstrCount; ++i) {
+            string_detab(lines[i]);
+        }
+    }
+
+    // iArm is an index to Arm instructions 1...n for this assembly sequence
+    // mArmPC[iArm] holds the value of the Mips-PC for the first MIPS
+    // instruction corresponding to that Arm instruction number
+
+    int iArm = 0;
+    size_t count = pc()-base();
+    uint32_t* mipsPC = base();
+
+    while (count--) {
+        ssize_t label = mLabelsInverseMapping.indexOfKey(mipsPC);
+        if (label >= 0) {
+            ALOGW("%s:\n", mLabelsInverseMapping.valueAt(label));
+        }
+        ssize_t comment = mComments.indexOfKey(mipsPC);
+        if (comment >= 0) {
+            ALOGW("; %s\n", mComments.valueAt(comment));
+        }
+        ::mips_disassem(mipsPC, di_buf, arm_disasm_fmt);
+        string_detab(di_buf);
+        string_pad(di_buf, 30);
+        ALOGW("%08lx:    %08x    %s", uintptr_t(mipsPC), uint32_t(*mipsPC), di_buf);
+        mipsPC++;
+    }
+}
+
+void MIPS64Assembler::fix_branches()
+{
+    // fixup all the branches
+    size_t count = mBranchTargets.size();
+    while (count--) {
+        const branch_target_t& bt = mBranchTargets[count];
+        uint32_t* target_pc = mLabels.valueFor(bt.label);
+        LOG_ALWAYS_FATAL_IF(!target_pc,
+                "error resolving branch targets, target_pc is null");
+        int32_t offset = int32_t(target_pc - (bt.pc+1));
+        *bt.pc |= offset & 0x00FFFF;
+    }
+}
+
+void MIPS64Assembler::DADDU(int Rd, int Rs, int Rt)
+{
+    *mPC++ = (spec_op<<OP_SHF) | (daddu_fn<<FUNC_SHF)
+                    | (Rs<<RS_SHF) | (Rt<<RT_SHF) | (Rd<<RD_SHF);
+}
+
+void MIPS64Assembler::DADDIU(int Rt, int Rs, int16_t imm)
+{
+    *mPC++ = (daddiu_op<<OP_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF) | (imm & MSK_16);
+}
+
+void MIPS64Assembler::DSUBU(int Rd, int Rs, int Rt)
+{
+    *mPC++ = (spec_op<<OP_SHF) | (dsubu_fn<<FUNC_SHF) |
+                        (Rs<<RS_SHF) | (Rt<<RT_SHF) | (Rd<<RD_SHF) ;
+}
+
+void MIPS64Assembler::DSUBIU(int Rt, int Rs, int16_t imm)   // really addiu(d, s, -j)
+{
+    *mPC++ = (daddiu_op<<OP_SHF) | (Rt<<RT_SHF) | (Rs<<RS_SHF) | ((-imm) & MSK_16);
+}
+
+void MIPS64Assembler::MUL(int Rd, int Rs, int Rt)
+{
+    *mPC++ = (spec_op<<OP_SHF) | (mul_fn<<RE_SHF) | (sop30_fn<<FUNC_SHF) |
+                        (Rs<<RS_SHF) | (Rt<<RT_SHF) | (Rd<<RD_SHF) ;
+}
+
+void MIPS64Assembler::MUH(int Rd, int Rs, int Rt)
+{
+    *mPC++ = (spec_op<<OP_SHF) | (muh_fn<<RE_SHF) | (sop30_fn<<FUNC_SHF) |
+                        (Rs<<RS_SHF) | (Rt<<RT_SHF) | (Rd<<RD_SHF) ;
+}
+
+void MIPS64Assembler::CLO(int Rd, int Rs)
+{
+    *mPC++ = (spec_op<<OP_SHF) | (17<<FUNC_SHF) |
+                        (Rd<<RD_SHF) | (Rs<<RS_SHF) | (1<<RE_SHF);
+}
+
+void MIPS64Assembler::CLZ(int Rd, int Rs)
+{
+    *mPC++ = (spec_op<<OP_SHF) | (16<<FUNC_SHF) |
+                        (Rd<<RD_SHF) | (Rs<<RS_SHF) | (1<<RE_SHF);
+}
+
+void MIPS64Assembler::LD(int Rt, int Rbase, int16_t offset)
+{
+    *mPC++ = (ld_op<<OP_SHF) | (Rbase<<RS_SHF) | (Rt<<RT_SHF) | (offset & MSK_16);
+}
+
+void MIPS64Assembler::SD(int Rt, int Rbase, int16_t offset)
+{
+    *mPC++ = (sd_op<<OP_SHF) | (Rbase<<RS_SHF) | (Rt<<RT_SHF) | (offset & MSK_16);
+}
+
+void MIPS64Assembler::LUI(int Rt, int16_t offset)
+{
+    *mPC++ = (aui_op<<OP_SHF) | (Rt<<RT_SHF) | (offset & MSK_16);
+}
+
+
+void MIPS64Assembler::JR(int Rs)
+{
+        *mPC++ = (spec_op<<OP_SHF) | (Rs<<RS_SHF) | (jalr_fn << FUNC_SHF);
+        MIPS64Assembler::NOP();
+}
+
+}; // namespace android:
diff --git a/libpixelflinger/codeflinger/MIPS64Assembler.h b/libpixelflinger/codeflinger/MIPS64Assembler.h
new file mode 100644
index 0000000..b43e5da
--- /dev/null
+++ b/libpixelflinger/codeflinger/MIPS64Assembler.h
@@ -0,0 +1,404 @@
+/* libs/pixelflinger/codeflinger/MIPS64Assembler.h
+**
+** Copyright 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 ANDROID_MIPS64ASSEMBLER_H
+#define ANDROID_MIPS64ASSEMBLER_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include "utils/KeyedVector.h"
+#include "utils/Vector.h"
+#include "tinyutils/smartpointer.h"
+
+#include "ARMAssemblerInterface.h"
+#include "MIPSAssembler.h"
+#include "CodeCache.h"
+
+namespace android {
+
+class MIPS64Assembler;    // forward reference
+
+// this class mimics ARMAssembler interface
+// intent is to translate each ARM instruction to 1 or more MIPS instr
+// implementation calls MIPS64Assembler class to generate mips code
+class ArmToMips64Assembler : public ARMAssemblerInterface
+{
+public:
+                ArmToMips64Assembler(const sp<Assembly>& assembly,
+                        char *abuf = 0, int linesz = 0, int instr_count = 0);
+                ArmToMips64Assembler(void* assembly);
+    virtual     ~ArmToMips64Assembler();
+
+    uint32_t*   base() const;
+    uint32_t*   pc() const;
+    void        disassemble(const char* name);
+
+    virtual void    reset();
+
+    virtual int     generate(const char* name);
+    virtual int     getCodegenArch();
+
+    virtual void    prolog();
+    virtual void    epilog(uint32_t touched);
+    virtual void    comment(const char* string);
+    // for testing purposes
+    void        fix_branches();
+    void        set_condition(int mode, int R1, int R2);
+
+
+    // -----------------------------------------------------------------------
+    // shifters and addressing modes
+    // -----------------------------------------------------------------------
+
+    // shifters...
+    virtual bool        isValidImmediate(uint32_t immed);
+    virtual int         buildImmediate(uint32_t i, uint32_t& rot, uint32_t& imm);
+
+    virtual uint32_t    imm(uint32_t immediate);
+    virtual uint32_t    reg_imm(int Rm, int type, uint32_t shift);
+    virtual uint32_t    reg_rrx(int Rm);
+    virtual uint32_t    reg_reg(int Rm, int type, int Rs);
+
+    // addressing modes...
+    // LDR(B)/STR(B)/PLD
+    // (immediate and Rm can be negative, which indicates U=0)
+    virtual uint32_t    immed12_pre(int32_t immed12, int W=0);
+    virtual uint32_t    immed12_post(int32_t immed12);
+    virtual uint32_t    reg_scale_pre(int Rm, int type=0, uint32_t shift=0, int W=0);
+    virtual uint32_t    reg_scale_post(int Rm, int type=0, uint32_t shift=0);
+
+    // LDRH/LDRSB/LDRSH/STRH
+    // (immediate and Rm can be negative, which indicates U=0)
+    virtual uint32_t    immed8_pre(int32_t immed8, int W=0);
+    virtual uint32_t    immed8_post(int32_t immed8);
+    virtual uint32_t    reg_pre(int Rm, int W=0);
+    virtual uint32_t    reg_post(int Rm);
+
+
+
+
+    virtual void    dataProcessing(int opcode, int cc, int s,
+                                int Rd, int Rn,
+                                uint32_t Op2);
+    virtual void MLA(int cc, int s,
+                int Rd, int Rm, int Rs, int Rn);
+    virtual void MUL(int cc, int s,
+                int Rd, int Rm, int Rs);
+    virtual void UMULL(int cc, int s,
+                int RdLo, int RdHi, int Rm, int Rs);
+    virtual void UMUAL(int cc, int s,
+                int RdLo, int RdHi, int Rm, int Rs);
+    virtual void SMULL(int cc, int s,
+                int RdLo, int RdHi, int Rm, int Rs);
+    virtual void SMUAL(int cc, int s,
+                int RdLo, int RdHi, int Rm, int Rs);
+
+    virtual void B(int cc, uint32_t* pc);
+    virtual void BL(int cc, uint32_t* pc);
+    virtual void BX(int cc, int Rn);
+    virtual void label(const char* theLabel);
+    virtual void B(int cc, const char* label);
+    virtual void BL(int cc, const char* label);
+
+    virtual uint32_t* pcForLabel(const char* label);
+
+    virtual void LDR (int cc, int Rd,
+                int Rn, uint32_t offset = 0);
+    virtual void LDRB(int cc, int Rd,
+                int Rn, uint32_t offset = 0);
+    virtual void STR (int cc, int Rd,
+                int Rn, uint32_t offset = 0);
+    virtual void STRB(int cc, int Rd,
+                int Rn, uint32_t offset = 0);
+    virtual void LDRH (int cc, int Rd,
+                int Rn, uint32_t offset = 0);
+    virtual void LDRSB(int cc, int Rd,
+                int Rn, uint32_t offset = 0);
+    virtual void LDRSH(int cc, int Rd,
+                int Rn, uint32_t offset = 0);
+    virtual void STRH (int cc, int Rd,
+                int Rn, uint32_t offset = 0);
+
+    virtual void LDM(int cc, int dir,
+                int Rn, int W, uint32_t reg_list);
+    virtual void STM(int cc, int dir,
+                int Rn, int W, uint32_t reg_list);
+
+    virtual void SWP(int cc, int Rn, int Rd, int Rm);
+    virtual void SWPB(int cc, int Rn, int Rd, int Rm);
+    virtual void SWI(int cc, uint32_t comment);
+
+    virtual void PLD(int Rn, uint32_t offset);
+    virtual void CLZ(int cc, int Rd, int Rm);
+    virtual void QADD(int cc, int Rd, int Rm, int Rn);
+    virtual void QDADD(int cc, int Rd, int Rm, int Rn);
+    virtual void QSUB(int cc, int Rd, int Rm, int Rn);
+    virtual void QDSUB(int cc, int Rd, int Rm, int Rn);
+    virtual void SMUL(int cc, int xy,
+                int Rd, int Rm, int Rs);
+    virtual void SMULW(int cc, int y,
+                int Rd, int Rm, int Rs);
+    virtual void SMLA(int cc, int xy,
+                int Rd, int Rm, int Rs, int Rn);
+    virtual void SMLAL(int cc, int xy,
+                int RdHi, int RdLo, int Rs, int Rm);
+    virtual void SMLAW(int cc, int y,
+                int Rd, int Rm, int Rs, int Rn);
+
+    // byte/half word extract...
+    virtual void UXTB16(int cc, int Rd, int Rm, int rotate);
+
+    // bit manipulation...
+    virtual void UBFX(int cc, int Rd, int Rn, int lsb, int width);
+
+    // Address loading/storing/manipulation
+    virtual void ADDR_LDR(int cc, int Rd, int Rn, uint32_t offset = __immed12_pre(0));
+    virtual void ADDR_STR(int cc, int Rd, int Rn, uint32_t offset = __immed12_pre(0));
+    virtual void ADDR_ADD(int cc, int s, int Rd, int Rn, uint32_t Op2);
+    virtual void ADDR_SUB(int cc, int s, int Rd, int Rn, uint32_t Op2);
+
+    // this is some crap to share is MIPS64Assembler class for debug
+    char *      mArmDisassemblyBuffer;
+    int         mArmLineLength;
+    int         mArmInstrCount;
+
+    int         mInum;      // current arm instuction number (0..n)
+    uint32_t**  mArmPC;     // array: PC for 1st mips instr of
+                            //      each translated ARM instr
+
+
+private:
+    ArmToMips64Assembler(const ArmToMips64Assembler& rhs);
+    ArmToMips64Assembler& operator = (const ArmToMips64Assembler& rhs);
+
+    void init_conditional_labels(void);
+
+    void protectConditionalOperands(int Rd);
+
+    // reg__tmp set to MIPS AT, reg 1
+    int dataProcAdrModes(int op, int& source, bool sign = false, int reg_tmp = 1);
+
+    sp<Assembly>        mAssembly;
+    MIPS64Assembler*    mMips;
+
+
+    enum misc_constants_t {
+        ARM_MAX_INSTUCTIONS = 512  // based on ASSEMBLY_SCRATCH_SIZE
+    };
+
+    enum {
+        SRC_REG = 0,
+        SRC_IMM,
+        SRC_ERROR = -1
+    };
+
+    enum addr_modes {
+        // start above the range of legal mips reg #'s (0-31)
+        AMODE_REG = 0x20,
+        AMODE_IMM, AMODE_REG_IMM,               // for data processing
+        AMODE_IMM_12_PRE, AMODE_IMM_12_POST,    // for load/store
+        AMODE_REG_SCALE_PRE, AMODE_IMM_8_PRE,
+        AMODE_IMM_8_POST, AMODE_REG_PRE,
+        AMODE_UNSUPPORTED
+    };
+
+    struct addr_mode_t {    // address modes for current ARM instruction
+        int         reg;
+        int         stype;
+        uint32_t    value;
+        bool        writeback;  // writeback the adr reg after modification
+    } amode;
+
+    enum cond_types {
+        CMP_COND = 1,
+        SBIT_COND
+    };
+
+    struct cond_mode_t {    // conditional-execution info for current ARM instruction
+        cond_types  type;
+        int         r1;
+        int         r2;
+        int         labelnum;
+        char        label[100][10];
+    } cond;
+};
+
+
+
+
+// ----------------------------------------------------------------------------
+// ----------------------------------------------------------------------------
+// ----------------------------------------------------------------------------
+
+// This is the basic MIPS64 assembler, which just creates the opcodes in memory.
+// All the more complicated work is done in ArmToMips64Assember above.
+// Inherits MIPSAssembler class, and overrides only MIPS64r6 specific stuff
+
+class MIPS64Assembler : public MIPSAssembler
+{
+public:
+                MIPS64Assembler(const sp<Assembly>& assembly, ArmToMips64Assembler *parent);
+                MIPS64Assembler(void* assembly, ArmToMips64Assembler *parent);
+    virtual     ~MIPS64Assembler();
+
+    virtual void        reset();
+    virtual void        disassemble(const char* name);
+
+    void        fix_branches();
+
+    // ------------------------------------------------------------------------
+    // MIPS64AssemblerInterface...
+    // ------------------------------------------------------------------------
+
+#if 0
+#pragma mark -
+#pragma mark Arithmetic...
+#endif
+
+    void DADDU(int Rd, int Rs, int Rt);
+    void DADDIU(int Rt, int Rs, int16_t imm);
+    void DSUBU(int Rd, int Rs, int Rt);
+    void DSUBIU(int Rt, int Rs, int16_t imm);
+    virtual void MUL(int Rd, int Rs, int Rt);
+    void MUH(int Rd, int Rs, int Rt);
+
+#if 0
+#pragma mark -
+#pragma mark Logical...
+#endif
+
+    virtual void CLO(int Rd, int Rs);
+    virtual void CLZ(int Rd, int Rs);
+
+#if 0
+#pragma mark -
+#pragma mark Load/store...
+#endif
+
+    void LD(int Rt, int Rbase, int16_t offset);
+    void SD(int Rt, int Rbase, int16_t offset);
+    virtual void LUI(int Rt, int16_t offset);
+
+#if 0
+#pragma mark -
+#pragma mark Branch...
+#endif
+
+    void JR(int Rs);
+
+
+protected:
+    ArmToMips64Assembler *mParent;
+
+    // opcode field of all instructions
+    enum opcode_field {
+        spec_op, regimm_op, j_op, jal_op,                  // 0x00 - 0x03
+        beq_op, bne_op, pop06_op, pop07_op,                // 0x04 - 0x07
+        pop10_op, addiu_op, slti_op, sltiu_op,             // 0x08 - 0x0b
+        andi_op, ori_op, xori_op, aui_op,                  // 0x0c - 0x0f
+        cop0_op, cop1_op, cop2_op, rsrv_opc_0,             // 0x10 - 0x13
+        rsrv_opc_1, rsrv_opc_2, pop26_op, pop27_op,        // 0x14 - 0x17
+        pop30_op, daddiu_op, rsrv_opc_3, rsrv_opc_4,       // 0x18 - 0x1b
+        rsrv_opc_5, daui_op, msa_op, spec3_op,             // 0x1c - 0x1f
+        lb_op, lh_op, rsrv_opc_6, lw_op,                   // 0x20 - 0x23
+        lbu_op, lhu_op, rsrv_opc_7, lwu_op,                // 0x24 - 0x27
+        sb_op, sh_op, rsrv_opc_8, sw_op,                   // 0x28 - 0x2b
+        rsrv_opc_9, rsrv_opc_10, rsrv_opc_11, rsrv_opc_12, // 0x2c - 0x2f
+        rsrv_opc_13, lwc1_op, bc_op, rsrv_opc_14,          // 0x2c - 0x2f
+        rsrv_opc_15, ldc1_op, pop66_op, ld_op,             // 0x30 - 0x33
+        rsrv_opc_16, swc1_op, balc_op, pcrel_op,           // 0x34 - 0x37
+        rsrv_opc_17, sdc1_op, pop76_op, sd_op              // 0x38 - 0x3b
+    };
+
+
+    // func field for special opcode
+    enum func_spec_op {
+        sll_fn, rsrv_spec_0, srl_fn, sra_fn,
+        sllv_fn, lsa_fn, srlv_fn, srav_fn,
+        rsrv_spec_1, jalr_fn, rsrv_spec_2, rsrv_spec_3,
+        syscall_fn, break_fn, sdbbp_fn, sync_fn,
+        clz_fn, clo_fn, dclz_fn, dclo_fn,
+        dsllv_fn, dlsa_fn, dsrlv_fn, dsrav_fn,
+        sop30_fn, sop31_fn, sop32_fn, sop33_fn,
+        sop34_fn, sop35_fn, sop36_fn, sop37_fn,
+        add_fn, addu_fn, sub_fn, subu_fn,
+        and_fn, or_fn, xor_fn, nor_fn,
+        rsrv_spec_4, rsrv_spec_5, slt_fn, sltu_fn,
+        dadd_fn, daddu_fn, dsub_fn, dsubu_fn,
+        tge_fn, tgeu_fn, tlt_fn, tltu_fn,
+        teq_fn, seleqz_fn, tne_fn, selnez_fn,
+        dsll_fn, rsrv_spec_6, dsrl_fn, dsra_fn,
+        dsll32_fn, rsrv_spec_7, dsrl32_fn, dsra32_fn
+    };
+
+    // func field for spec3 opcode
+    enum func_spec3_op {
+        ext_fn, dextm_fn, dextu_fn, dext_fn,
+        ins_fn, dinsm_fn, dinsu_fn, dins_fn,
+        cachee_fn = 0x1b, sbe_fn, she_fn, sce_fn, swe_fn,
+        bshfl_fn, prefe_fn = 0x23, dbshfl_fn, cache_fn, sc_fn, scd_fn,
+        lbue_fn, lhue_fn, lbe_fn = 0x2c, lhe_fn, lle_fn, lwe_fn,
+        pref_fn = 0x35, ll_fn, lld_fn, rdhwr_fn = 0x3b
+    };
+
+    // sa field for spec3 opcodes, with BSHFL function
+    enum func_spec3_bshfl {
+        bitswap_fn,
+        wsbh_fn = 0x02,
+        dshd_fn = 0x05,
+        seb_fn = 0x10,
+        seh_fn = 0x18
+    };
+
+    // rt field of regimm opcodes.
+    enum regimm_fn {
+        bltz_fn, bgez_fn,
+        dahi_fn = 0x6,
+        nal_fn = 0x10, bal_fn, bltzall_fn, bgezall_fn,
+        sigrie_fn = 0x17,
+        dati_fn = 0x1e, synci_fn
+    };
+
+    enum muldiv_fn {
+        mul_fn = 0x02, muh_fn
+    };
+
+    enum mips_inst_shifts {
+        OP_SHF       = 26,
+        JTARGET_SHF  = 0,
+        RS_SHF       = 21,
+        RT_SHF       = 16,
+        RD_SHF       = 11,
+        RE_SHF       = 6,
+        SA_SHF       = RE_SHF,  // synonym
+        IMM_SHF      = 0,
+        FUNC_SHF     = 0,
+
+        // mask values
+        MSK_16       = 0xffff,
+
+
+        CACHEOP_SHF  = 18,
+        CACHESEL_SHF = 16,
+    };
+};
+
+
+}; // namespace android
+
+#endif //ANDROID_MIPS64ASSEMBLER_H
diff --git a/libpixelflinger/codeflinger/MIPSAssembler.cpp b/libpixelflinger/codeflinger/MIPSAssembler.cpp
index a88d2fe..daa231f 100644
--- a/libpixelflinger/codeflinger/MIPSAssembler.cpp
+++ b/libpixelflinger/codeflinger/MIPSAssembler.cpp
@@ -1358,7 +1358,7 @@
         ::mips_disassem(mipsPC, di_buf, arm_disasm_fmt);
         string_detab(di_buf);
         string_pad(di_buf, 30);
-        ALOGW("%08x:    %08x    %s", uint32_t(mipsPC), uint32_t(*mipsPC), di_buf);
+        ALOGW("%08x:    %08x    %s", uintptr_t(mipsPC), uint32_t(*mipsPC), di_buf);
         mipsPC++;
     }
 }
@@ -1407,7 +1407,7 @@
 
 #if defined(WITH_LIB_HARDWARE)
     if (__builtin_expect(mQemuTracing, 0)) {
-        int err = qemu_add_mapping(int(base()), name);
+        int err = qemu_add_mapping(uintptr_t(base()), name);
         mQemuTracing = (err >= 0);
     }
 #endif
diff --git a/libpixelflinger/codeflinger/MIPSAssembler.h b/libpixelflinger/codeflinger/MIPSAssembler.h
index 8fea8cb..06cb0d0 100644
--- a/libpixelflinger/codeflinger/MIPSAssembler.h
+++ b/libpixelflinger/codeflinger/MIPSAssembler.h
@@ -244,20 +244,20 @@
                 MIPSAssembler(const sp<Assembly>& assembly, ArmToMipsAssembler *parent);
     virtual     ~MIPSAssembler();
 
-    uint32_t*   base() const;
-    uint32_t*   pc() const;
-    void        reset();
+    virtual uint32_t*   base() const;
+    virtual uint32_t*   pc() const;
+    virtual void        reset();
 
-    void        disassemble(const char* name);
+    virtual void        disassemble(const char* name);
 
-    void        prolog();
-    void        epilog(uint32_t touched);
-    int         generate(const char* name);
-    void        comment(const char* string);
-    void        label(const char* string);
+    virtual void        prolog();
+    virtual void        epilog(uint32_t touched);
+    virtual int         generate(const char* name);
+    virtual void        comment(const char* string);
+    virtual void        label(const char* string);
 
     // valid only after generate() has been called
-    uint32_t*   pcForLabel(const char* label);
+    virtual uint32_t*   pcForLabel(const char* label);
 
 
     // ------------------------------------------------------------------------
@@ -399,9 +399,9 @@
 
 
 
-private:
-    void string_detab(char *s);
-    void string_pad(char *s, int padded_len);
+protected:
+    virtual void string_detab(char *s);
+    virtual void string_pad(char *s, int padded_len);
 
     ArmToMipsAssembler *mParent;
     sp<Assembly>    mAssembly;
@@ -537,7 +537,11 @@
 enum mips_regnames {
     R_zero = 0,
             R_at,   R_v0,   R_v1,   R_a0,   R_a1,   R_a2,   R_a3,
+#if __mips_isa_rev < 6
     R_t0,   R_t1,   R_t2,   R_t3,   R_t4,   R_t5,   R_t6,   R_t7,
+#else
+    R_a4,   R_a5,   R_a6,   R_a7,   R_t0,   R_t1,   R_t2,   R_t3,
+#endif
     R_s0,   R_s1,   R_s2,   R_s3,   R_s4,   R_s5,   R_s6,   R_s7,
     R_t8,   R_t9,   R_k0,   R_k1,   R_gp,   R_sp,   R_s8,   R_ra,
     R_lr = R_s8,
diff --git a/libpixelflinger/codeflinger/mips64_disassem.c b/libpixelflinger/codeflinger/mips64_disassem.c
new file mode 100644
index 0000000..44b7fe7
--- /dev/null
+++ b/libpixelflinger/codeflinger/mips64_disassem.c
@@ -0,0 +1,582 @@
+/*  $NetBSD: db_disasm.c,v 1.19 2007/02/28 04:21:53 thorpej Exp $   */
+
+/*-
+ * Copyright (c) 1991, 1993
+ *  The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Ralph Campbell.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *  from: @(#)kadb.c    8.1 (Berkeley) 6/10/93
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <sys/cdefs.h>
+
+#include <sys/types.h>
+#include "mips_opcode.h"
+
+#include <cutils/log.h>
+
+static char *sprintf_buffer;
+static int sprintf_buf_len;
+
+
+typedef uint64_t db_addr_t;
+static void db_printf(const char* fmt, ...);
+
+static const char * const op_name[64] = {
+/* 0 */ "spec", "bcond", "j", "jal", "beq", "bne", "blez", "bgtz",
+/* 8 */ "pop10", "addiu", "slti", "sltiu", "andi", "ori", "xori", "aui",
+/*16 */ "cop0", "cop1", "cop2", "?", "?", "?", "pop26", "pop27",
+/*24 */ "pop30", "daddiu", "?", "?", "?", "daui", "msa", "op37",
+/*32 */ "lb", "lh", "?",  "lw", "lbu", "lhu", "?", "lwu",
+/*40 */ "sb", "sh", "?", "sw", "?", "?", "?", "?",
+/*48 */ "?", "lwc1", "bc", "?", "?",  "ldc1", "pop66", "ld",
+/*56 */ "?", "swc1", "balc", "pcrel", "?", "sdc1", "pop76", "sd"
+};
+
+static const char * const spec_name[64] = {
+/* 0 */ "sll", "?", "srl", "sra", "sllv", "?", "srlv", "srav",
+/* 8 */ "?", "jalr", "?", "?", "syscall", "break", "sdbpp", "sync",
+/*16 */ "clz", "clo", "dclz", "dclo", "dsllv", "dlsa", "dsrlv", "dsrav",
+/*24 */ "sop30", "sop31", "sop32", "sop33", "sop34", "sop35", "sop36", "sop37",
+/*32 */ "add", "addu", "sub", "subu", "and", "or", "xor", "nor",
+/*40 */ "?", "?", "slt", "sltu", "dadd", "daddu", "dsub", "dsubu",
+/*48 */ "tge", "tgeu", "tlt", "tltu", "teq", "seleqz", "tne", "selnez",
+/*56 */ "dsll", "?", "dsrl", "dsra", "dsll32", "?", "dsrl32", "dsra32"
+};
+
+static const char * const bcond_name[32] = {
+/* 0 */ "bltz", "bgez", "?", "?", "?", "?", "dahi", "?",
+/* 8 */ "?", "?", "?", "?", "?", "?", "?", "?",
+/*16 */ "nal", "bal", "?", "?", "?", "?", "?", "sigrie",
+/*24 */ "?", "?", "?", "?", "?", "?", "dati", "synci",
+};
+
+static const char * const cop1_name[64] = {
+/* 0 */ "fadd",  "fsub", "fmpy", "fdiv", "fsqrt","fabs", "fmov", "fneg",
+/* 8 */ "fop08","fop09","fop0a","fop0b","fop0c","fop0d","fop0e","fop0f",
+/*16 */ "fop10","fop11","fop12","fop13","fop14","fop15","fop16","fop17",
+/*24 */ "fop18","fop19","fop1a","fop1b","fop1c","fop1d","fop1e","fop1f",
+/*32 */ "fcvts","fcvtd","fcvte","fop23","fcvtw","fop25","fop26","fop27",
+/*40 */ "fop28","fop29","fop2a","fop2b","fop2c","fop2d","fop2e","fop2f",
+/*48 */ "fcmp.f","fcmp.un","fcmp.eq","fcmp.ueq","fcmp.olt","fcmp.ult",
+    "fcmp.ole","fcmp.ule",
+/*56 */ "fcmp.sf","fcmp.ngle","fcmp.seq","fcmp.ngl","fcmp.lt","fcmp.nge",
+    "fcmp.le","fcmp.ngt"
+};
+
+static const char * const fmt_name[16] = {
+    "s",    "d",    "e",    "fmt3",
+    "w",    "fmt5", "fmt6", "fmt7",
+    "fmt8", "fmt9", "fmta", "fmtb",
+    "fmtc", "fmtd", "fmte", "fmtf"
+};
+
+static char * const mips_reg_name[32] = {
+    "zero", "at",   "v0",   "v1",   "a0",   "a1",   "a2",   "a3",
+    "a4",   "a5",   "a6",   "a7",   "t0",   "t1",   "t2",   "t3",
+    "s0",   "s1",   "s2",   "s3",   "s4",   "s5",   "s6",   "s7",
+    "t8",   "t9",   "k0",   "k1",   "gp",   "sp",   "s8",   "ra"
+};
+
+static char * alt_arm_reg_name[32] = {  // hacked names for comparison with ARM code
+    "zero", "at",   "r0",   "r1",   "r2",   "r3",   "r4",   "r5",
+    "r6",   "r7",   "r8",   "r9",   "r10",  "r11",  "r12",  "r13",
+    "r14",  "r15",  "at2",  "cmp",  "s4",   "s5",   "s6",   "s7",
+    "t8",   "t9",   "k0",   "k1",   "gp",   "sp",   "s8",   "ra"
+};
+
+static char ** reg_name =  &mips_reg_name[0];
+
+static const char * const c0_opname[64] = {
+    "c0op00","tlbr",  "tlbwi", "c0op03","c0op04","c0op05","tlbwr", "c0op07",
+    "tlbp",  "c0op11","c0op12","c0op13","c0op14","c0op15","c0op16","c0op17",
+    "rfe",   "c0op21","c0op22","c0op23","c0op24","c0op25","c0op26","c0op27",
+    "eret",  "c0op31","c0op32","c0op33","c0op34","c0op35","c0op36","c0op37",
+    "c0op40","c0op41","c0op42","c0op43","c0op44","c0op45","c0op46","c0op47",
+    "c0op50","c0op51","c0op52","c0op53","c0op54","c0op55","c0op56","c0op57",
+    "c0op60","c0op61","c0op62","c0op63","c0op64","c0op65","c0op66","c0op67",
+    "c0op70","c0op71","c0op72","c0op73","c0op74","c0op75","c0op77","c0op77",
+};
+
+static const char * const c0_reg[32] = {
+    "index",    "random",   "tlblo0",  "tlblo1",
+    "context",  "pagemask", "wired",   "cp0r7",
+    "badvaddr", "count",    "tlbhi",   "compare",
+    "status",   "cause",    "epc",     "prid",
+    "config",   "lladdr",   "watchlo", "watchhi",
+    "xcontext", "cp0r21",   "cp0r22",  "debug",
+    "depc",     "perfcnt",  "ecc",     "cacheerr",
+    "taglo",    "taghi",    "errepc",  "desave"
+};
+
+static void print_addr(db_addr_t);
+db_addr_t mips_disassem(db_addr_t loc, char *di_buffer, int alt_dis_format);
+
+
+/*
+ * Disassemble instruction 'insn' nominally at 'loc'.
+ * 'loc' may in fact contain a breakpoint instruction.
+ */
+static db_addr_t
+db_disasm_insn(int insn, db_addr_t loc, bool altfmt)
+{
+    bool bdslot = false;
+    InstFmt i;
+
+    i.word = insn;
+
+    switch (i.JType.op) {
+    case OP_SPECIAL:
+        if (i.word == 0) {
+            db_printf("nop");
+            break;
+        }
+        if (i.word == 0x0080) {
+            db_printf("NIY");
+            break;
+        }
+        if (i.word == 0x00c0) {
+            db_printf("NOT IMPL");
+            break;
+        }
+        /* Special cases --------------------------------------------------
+         * "addu" is a "move" only in 32-bit mode.  What's the correct
+         * answer - never decode addu/daddu as "move"?
+         */
+        if ( (i.RType.func == OP_ADDU && i.RType.rt == 0)  ||
+             (i.RType.func == OP_OR   && i.RType.rt == 0) ) {
+            db_printf("move\t%s,%s",
+                reg_name[i.RType.rd],
+                reg_name[i.RType.rs]);
+            break;
+        }
+
+        if (i.RType.func == OP_SRL && (i.RType.rs & 1) == 1) {
+            db_printf("rotr\t%s,%s,%d", reg_name[i.RType.rd],
+                reg_name[i.RType.rt], i.RType.shamt);
+            break;
+        }
+        if (i.RType.func == OP_SRLV && (i.RType.shamt & 1) == 1) {
+            db_printf("rotrv\t%s,%s,%s", reg_name[i.RType.rd],
+                reg_name[i.RType.rt], reg_name[i.RType.rs]);
+            break;
+        }
+
+        if (i.RType.func == OP_SOP30) {
+            if (i.RType.shamt == OP_MUL) {
+                db_printf("mul");
+            } else if (i.RType.shamt == OP_MUH) {
+                db_printf("muh");
+            }
+            db_printf("\t%s,%s,%s", reg_name[i.RType.rd],
+                reg_name[i.RType.rs], reg_name[i.RType.rt]);
+            break;
+        }
+        if (i.RType.func == OP_SOP31) {
+            if (i.RType.shamt == OP_MUL) {
+                db_printf("mulu");
+            } else if (i.RType.shamt == OP_MUH) {
+                db_printf("muhu");
+            }
+            db_printf("\t%s,%s,%s", reg_name[i.RType.rd],
+                reg_name[i.RType.rs], reg_name[i.RType.rt]);
+            break;
+        }
+
+        if (i.RType.func == OP_JALR && i.RType.rd == 0) {
+            db_printf("jr\t%s", reg_name[i.RType.rs]);
+            bdslot = true;
+            break;
+        }
+
+        db_printf("%s", spec_name[i.RType.func]);
+        switch (i.RType.func) {
+        case OP_SLL:
+        case OP_SRL:
+        case OP_SRA:
+        case OP_DSLL:
+
+        case OP_DSRL:
+        case OP_DSRA:
+        case OP_DSLL32:
+        case OP_DSRL32:
+        case OP_DSRA32:
+            db_printf("\t%s,%s,%d",
+                reg_name[i.RType.rd],
+                reg_name[i.RType.rt],
+                i.RType.shamt);
+            break;
+
+        case OP_SLLV:
+        case OP_SRLV:
+        case OP_SRAV:
+        case OP_DSLLV:
+        case OP_DSRLV:
+        case OP_DSRAV:
+            db_printf("\t%s,%s,%s",
+                reg_name[i.RType.rd],
+                reg_name[i.RType.rt],
+                reg_name[i.RType.rs]);
+            break;
+
+        case OP_CLZ:
+        case OP_CLO:
+        case OP_DCLZ:
+        case OP_DCLO:
+            db_printf("\t%s,%s",
+                reg_name[i.RType.rd],
+                reg_name[i.RType.rs]);
+            break;
+
+        case OP_JALR:
+            db_printf("\t");
+            if (i.RType.rd != 31) {
+                db_printf("%s,", reg_name[i.RType.rd]);
+            }
+            db_printf("%s", reg_name[i.RType.rs]);
+            bdslot = true;
+            break;
+
+        case OP_SYSCALL:
+        case OP_SYNC:
+            break;
+
+        case OP_BREAK:
+            db_printf("\t%d", (i.RType.rs << 5) | i.RType.rt);
+            break;
+
+        default:
+            db_printf("\t%s,%s,%s",
+                reg_name[i.RType.rd],
+                reg_name[i.RType.rs],
+                reg_name[i.RType.rt]);
+        }
+        break;
+
+    case OP_SPECIAL3:
+        if (i.RType.func == OP_EXT)
+            db_printf("ext\t%s,%s,%d,%d",
+                    reg_name[i.RType.rt],
+                    reg_name[i.RType.rs],
+                    i.RType.shamt,
+                    i.RType.rd+1);
+        else if (i.RType.func == OP_DEXT)
+            db_printf("dext\t%s,%s,%d,%d",
+                    reg_name[i.RType.rt],
+                    reg_name[i.RType.rs],
+                    i.RType.shamt,
+                    i.RType.rd+1);
+        else if (i.RType.func == OP_DEXTM)
+            db_printf("dextm\t%s,%s,%d,%d",
+                    reg_name[i.RType.rt],
+                    reg_name[i.RType.rs],
+                    i.RType.shamt,
+                    i.RType.rd+33);
+        else if (i.RType.func == OP_DEXTU)
+            db_printf("dextu\t%s,%s,%d,%d",
+                    reg_name[i.RType.rt],
+                    reg_name[i.RType.rs],
+                    i.RType.shamt+32,
+                    i.RType.rd+1);
+        else if (i.RType.func == OP_INS)
+            db_printf("ins\t%s,%s,%d,%d",
+                    reg_name[i.RType.rt],
+                    reg_name[i.RType.rs],
+                    i.RType.shamt,
+                    i.RType.rd-i.RType.shamt+1);
+        else if (i.RType.func == OP_DINS)
+            db_printf("dins\t%s,%s,%d,%d",
+                    reg_name[i.RType.rt],
+                    reg_name[i.RType.rs],
+                    i.RType.shamt,
+                    i.RType.rd-i.RType.shamt+1);
+        else if (i.RType.func == OP_DINSM)
+            db_printf("dinsm\t%s,%s,%d,%d",
+                    reg_name[i.RType.rt],
+                    reg_name[i.RType.rs],
+                    i.RType.shamt,
+                    i.RType.rd-i.RType.shamt+33);
+        else if (i.RType.func == OP_DINSU)
+            db_printf("dinsu\t%s,%s,%d,%d",
+                    reg_name[i.RType.rt],
+                    reg_name[i.RType.rs],
+                    i.RType.shamt+32,
+                    i.RType.rd-i.RType.shamt+1);
+        else if (i.RType.func == OP_BSHFL && i.RType.shamt == OP_WSBH)
+            db_printf("wsbh\t%s,%s",
+                reg_name[i.RType.rd],
+                reg_name[i.RType.rt]);
+        else if (i.RType.func == OP_BSHFL && i.RType.shamt == OP_SEB)
+            db_printf("seb\t%s,%s",
+                reg_name[i.RType.rd],
+                reg_name[i.RType.rt]);
+        else if (i.RType.func == OP_BSHFL && i.RType.shamt == OP_SEH)
+            db_printf("seh\t%s,%s",
+                reg_name[i.RType.rd],
+                reg_name[i.RType.rt]);
+        else if (i.RType.func == OP_RDHWR)
+            db_printf("rdhwr\t%s,%s",
+                reg_name[i.RType.rd],
+                reg_name[i.RType.rt]);
+        else
+            db_printf("Unknown");
+        break;
+
+    case OP_BCOND:
+        db_printf("%s\t%s,", bcond_name[i.IType.rt],
+            reg_name[i.IType.rs]);
+        goto pr_displ;
+
+    case OP_BLEZ:
+    case OP_BGTZ:
+        db_printf("%s\t%s,", op_name[i.IType.op],
+            reg_name[i.IType.rs]);
+        goto pr_displ;
+
+    case OP_BEQ:
+        if (i.IType.rs == 0 && i.IType.rt == 0) {
+            db_printf("b\t");
+            goto pr_displ;
+        }
+        /* FALLTHROUGH */
+    case OP_BNE:
+        db_printf("%s\t%s,%s,", op_name[i.IType.op],
+            reg_name[i.IType.rs],
+            reg_name[i.IType.rt]);
+    pr_displ:
+        print_addr(loc + 4 + ((short)i.IType.imm << 2));
+        bdslot = true;
+        break;
+
+    case OP_COP0:
+        switch (i.RType.rs) {
+        case OP_BCx:
+        case OP_BCy:
+
+            db_printf("bc0%c\t",
+                "ft"[i.RType.rt & COPz_BC_TF_MASK]);
+            goto pr_displ;
+
+        case OP_MT:
+            db_printf("mtc0\t%s,%s",
+                reg_name[i.RType.rt],
+                c0_reg[i.RType.rd]);
+            break;
+
+        case OP_DMT:
+            db_printf("dmtc0\t%s,%s",
+                reg_name[i.RType.rt],
+                c0_reg[i.RType.rd]);
+            break;
+
+        case OP_MF:
+            db_printf("mfc0\t%s,%s",
+                reg_name[i.RType.rt],
+                c0_reg[i.RType.rd]);
+            break;
+
+        case OP_DMF:
+            db_printf("dmfc0\t%s,%s",
+                reg_name[i.RType.rt],
+                c0_reg[i.RType.rd]);
+            break;
+
+        default:
+            db_printf("%s", c0_opname[i.FRType.func]);
+        }
+        break;
+
+    case OP_COP1:
+        switch (i.RType.rs) {
+        case OP_BCx:
+        case OP_BCy:
+            db_printf("bc1%c\t",
+                "ft"[i.RType.rt & COPz_BC_TF_MASK]);
+            goto pr_displ;
+
+        case OP_MT:
+            db_printf("mtc1\t%s,f%d",
+                reg_name[i.RType.rt],
+                i.RType.rd);
+            break;
+
+        case OP_MF:
+            db_printf("mfc1\t%s,f%d",
+                reg_name[i.RType.rt],
+                i.RType.rd);
+            break;
+
+        case OP_CT:
+            db_printf("ctc1\t%s,f%d",
+                reg_name[i.RType.rt],
+                i.RType.rd);
+            break;
+
+        case OP_CF:
+            db_printf("cfc1\t%s,f%d",
+                reg_name[i.RType.rt],
+                i.RType.rd);
+            break;
+
+        default:
+            db_printf("%s.%s\tf%d,f%d,f%d",
+                cop1_name[i.FRType.func],
+                fmt_name[i.FRType.fmt],
+                i.FRType.fd, i.FRType.fs, i.FRType.ft);
+        }
+        break;
+
+    case OP_J:
+    case OP_JAL:
+        db_printf("%s\t", op_name[i.JType.op]);
+        print_addr((loc & 0xFFFFFFFFF0000000) | (i.JType.target << 2));
+        bdslot = true;
+        break;
+
+    case OP_LWC1:
+    case OP_SWC1:
+        db_printf("%s\tf%d,", op_name[i.IType.op],
+            i.IType.rt);
+        goto loadstore;
+
+    case OP_LB:
+    case OP_LH:
+    case OP_LW:
+    case OP_LD:
+    case OP_LBU:
+    case OP_LHU:
+    case OP_LWU:
+    case OP_SB:
+    case OP_SH:
+    case OP_SW:
+    case OP_SD:
+        db_printf("%s\t%s,", op_name[i.IType.op],
+            reg_name[i.IType.rt]);
+    loadstore:
+        db_printf("%d(%s)", (short)i.IType.imm,
+            reg_name[i.IType.rs]);
+        break;
+
+    case OP_ORI:
+    case OP_XORI:
+        if (i.IType.rs == 0) {
+            db_printf("li\t%s,0x%x",
+                reg_name[i.IType.rt],
+                i.IType.imm);
+            break;
+        }
+        /* FALLTHROUGH */
+    case OP_ANDI:
+        db_printf("%s\t%s,%s,0x%x", op_name[i.IType.op],
+            reg_name[i.IType.rt],
+            reg_name[i.IType.rs],
+            i.IType.imm);
+        break;
+
+    case OP_AUI:
+        if (i.IType.rs == 0) {
+            db_printf("lui\t%s,0x%x", reg_name[i.IType.rt],
+                i.IType.imm);
+        } else {
+            db_printf("%s\t%s,%s,%d", op_name[i.IType.op],
+            reg_name[i.IType.rt], reg_name[i.IType.rs],
+            (short)i.IType.imm);
+        }
+        break;
+
+    case OP_ADDIU:
+    case OP_DADDIU:
+        if (i.IType.rs == 0) {
+            db_printf("li\t%s,%d",
+                reg_name[i.IType.rt],
+                (short)i.IType.imm);
+            break;
+        }
+        /* FALLTHROUGH */
+    default:
+        db_printf("%s\t%s,%s,%d", op_name[i.IType.op],
+            reg_name[i.IType.rt],
+            reg_name[i.IType.rs],
+            (short)i.IType.imm);
+    }
+    // db_printf("\n");
+    // if (bdslot) {
+    //     db_printf("   bd: ");
+    //     mips_disassem(loc+4);
+    //     return (loc + 8);
+    // }
+    return (loc + 4);
+}
+
+static void
+print_addr(db_addr_t loc)
+{
+    db_printf("0x%08lx", loc);
+}
+
+static void db_printf(const char* fmt, ...)
+{
+    int cnt;
+    va_list argp;
+    va_start(argp, fmt);
+    if (sprintf_buffer) {
+        cnt = vsnprintf(sprintf_buffer, sprintf_buf_len, fmt, argp);
+        sprintf_buffer += cnt;
+        sprintf_buf_len -= cnt;
+    } else {
+        vprintf(fmt, argp);
+    }
+}
+
+/*
+ * Disassemble instruction at 'loc'.
+ * Return address of start of next instruction.
+ * Since this function is used by 'examine' and by 'step'
+ * "next instruction" does NOT mean the next instruction to
+ * be executed but the 'linear' next instruction.
+ */
+db_addr_t
+mips_disassem(db_addr_t loc, char *di_buffer, int alt_dis_format)
+{
+    u_int32_t instr;
+
+    if (alt_dis_format) {   // use ARM register names for disassembly
+        reg_name = &alt_arm_reg_name[0];
+    }
+
+    sprintf_buffer = di_buffer;     // quick 'n' dirty printf() vs sprintf()
+    sprintf_buf_len = 39;           // should be passed in
+
+    instr =  *(u_int32_t *)loc;
+    return (db_disasm_insn(instr, loc, false));
+}
diff --git a/libpixelflinger/codeflinger/mips64_disassem.h b/libpixelflinger/codeflinger/mips64_disassem.h
new file mode 100644
index 0000000..c94f04f
--- /dev/null
+++ b/libpixelflinger/codeflinger/mips64_disassem.h
@@ -0,0 +1,56 @@
+/*  $NetBSD: db_disasm.c,v 1.19 2007/02/28 04:21:53 thorpej Exp $   */
+
+/*-
+ * Copyright (c) 1991, 1993
+ *  The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Ralph Campbell.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *  from: @(#)kadb.c    8.1 (Berkeley) 6/10/93
+ */
+
+
+
+#ifndef ANDROID_MIPS_DISASSEM_H
+#define ANDROID_MIPS_DISASSEM_H
+
+#include <sys/types.h>
+
+#if __cplusplus
+extern "C" {
+#endif
+
+/* Prototypes for callable functions */
+
+void mips_disassem(uint32_t *location, char *di_buffer, int alt_fmt);
+
+#if __cplusplus
+}
+#endif
+
+#endif /* !ANDROID_MIPS_DISASSEM_H */
diff --git a/libpixelflinger/codeflinger/mips_disassem.c b/libpixelflinger/codeflinger/mips_disassem.c
index 4ab9bd3..3007b15 100644
--- a/libpixelflinger/codeflinger/mips_disassem.c
+++ b/libpixelflinger/codeflinger/mips_disassem.c
@@ -323,14 +323,14 @@
             db_printf("ext\t%s,%s,%d,%d",
                     reg_name[i.RType.rt],
                     reg_name[i.RType.rs],
-                    i.RType.rd+1,
-                    i.RType.shamt);
+                    i.RType.shamt,
+                    i.RType.rd+1);
         else if (i.RType.func == OP_INS)
             db_printf("ins\t%s,%s,%d,%d",
                     reg_name[i.RType.rt],
                     reg_name[i.RType.rs],
-                    i.RType.rd+1,
-                    i.RType.shamt);
+                    i.RType.shamt,
+                    i.RType.rd-i.RType.shamt+1);
         else if (i.RType.func == OP_BSHFL && i.RType.shamt == OP_WSBH)
             db_printf("wsbh\t%s,%s",
                 reg_name[i.RType.rd],
diff --git a/libpixelflinger/codeflinger/mips_opcode.h b/libpixelflinger/codeflinger/mips_opcode.h
index 7ed5ef5..45bb19e 100644
--- a/libpixelflinger/codeflinger/mips_opcode.h
+++ b/libpixelflinger/codeflinger/mips_opcode.h
@@ -125,69 +125,118 @@
 #define OP_BLEZ     006
 #define OP_BGTZ     007
 
+#if __mips_isa_rev < 6
 #define OP_ADDI     010
+#else
+#define OP_POP10    010
+#endif
+
 #define OP_ADDIU    011
 #define OP_SLTI     012
 #define OP_SLTIU    013
 #define OP_ANDI     014
 #define OP_ORI      015
 #define OP_XORI     016
+
+#if __mips_isa_rev < 6
 #define OP_LUI      017
+#else
+#define OP_AUI      017
+#endif
 
 #define OP_COP0     020
 #define OP_COP1     021
 #define OP_COP2     022
+
+#if __mips_isa_rev < 6
 #define OP_COP3     023
-#define OP_BEQL     024     /* MIPS-II, for r4000 port */
-#define OP_BNEL     025     /* MIPS-II, for r4000 port */
-#define OP_BLEZL    026     /* MIPS-II, for r4000 port */
-#define OP_BGTZL    027     /* MIPS-II, for r4000 port */
+#define OP_BEQL     024
+#define OP_BNEL     025
+#define OP_BLEZL    026
+#define OP_BGTZL    027
+#define OP_DADDI    030
+#else
+#define OP_POP26    026
+#define OP_POP27    027
+#define OP_POP30    030
+#endif
 
-#define OP_DADDI    030     /* MIPS-II, for r4000 port */
-#define OP_DADDIU   031     /* MIPS-II, for r4000 port */
-#define OP_LDL      032     /* MIPS-II, for r4000 port */
-#define OP_LDR      033     /* MIPS-II, for r4000 port */
+#define OP_DADDIU   031
 
-#define OP_SPECIAL2 034     /* QED opcodes */
-#define OP_SPECIAL3 037     /* mips32r2 opcodes */
+#if __mips_isa_rev < 6
+#define OP_LDL      032
+#define OP_LDR      033
+#define OP_SPECIAL2 034
+#else
+#define OP_DAUI     035
+#endif
+
+#define OP_SPECIAL3 037
 
 #define OP_LB       040
 #define OP_LH       041
+
+#if __mips_isa_rev < 6
 #define OP_LWL      042
+#endif
+
 #define OP_LW       043
 #define OP_LBU      044
 #define OP_LHU      045
 #define OP_LWR      046
 #define OP_LHU      045
+
+#if __mips_isa_rev < 6
 #define OP_LWR      046
-#define OP_LWU      047     /* MIPS-II, for r4000 port */
+#endif
+
+#define OP_LWU      047
 
 #define OP_SB       050
 #define OP_SH       051
-#define OP_SWL      052
-#define OP_SW       053
-#define OP_SDL      054     /* MIPS-II, for r4000 port */
-#define OP_SDR      055     /* MIPS-II, for r4000 port */
-#define OP_SWR      056
-#define OP_CACHE    057     /* MIPS-II, for r4000 port */
 
+#if __mips_isa_rev < 6
+#define OP_SWL      052
+#endif
+
+#define OP_SW       053
+
+#if __mips_isa_rev < 6
+#define OP_SDL      054
+#define OP_SDR      055
+#define OP_SWR      056
+#define OP_CACHE    057
 #define OP_LL       060
-#define OP_LWC0     OP_LL   /* backwards source compatibility */
+#define OP_LWC0     OP_LL
 #define OP_LWC1     061
 #define OP_LWC2     062
 #define OP_LWC3     063
-#define OP_LLD      064     /* MIPS-II, for r4000 port */
-#define OP_LDC1     065
-#define OP_LD       067     /* MIPS-II, for r4000 port */
+#define OP_LLD      064
+#else
+#define OP_LWC1     061
+#define OP_BC       062
+#endif
 
+#define OP_LDC1     065
+#define OP_LD       067
+
+#if __mips_isa_rev < 6
 #define OP_SC       070
-#define OP_SWC0     OP_SC   /* backwards source compatibility */
+#define OP_SWC0     OP_SC
+#endif
+
 #define OP_SWC1     071
+
+#if __mips_isa_rev < 6
 #define OP_SWC2     072
 #define OP_SWC3     073
-#define OP_SCD      074     /* MIPS-II, for r4000 port */
+#define OP_SCD      074
+#else
+#define OP_BALC     072
+#endif
+
 #define OP_SDC1     075
-#define OP_SD       077     /* MIPS-II, for r4000 port */
+#define OP_SD       077
 
 /*
  * Values for the 'func' field when 'op' == OP_SPECIAL.
@@ -199,28 +248,50 @@
 #define OP_SRLV     006
 #define OP_SRAV     007
 
+#if __mips_isa_rev < 6
 #define OP_JR       010
+#endif
+
 #define OP_JALR     011
 #define OP_SYSCALL  014
 #define OP_BREAK    015
-#define OP_SYNC     017     /* MIPS-II, for r4000 port */
+#define OP_SYNC     017
 
+#if __mips_isa_rev < 6
 #define OP_MFHI     020
 #define OP_MTHI     021
 #define OP_MFLO     022
 #define OP_MTLO     023
-#define OP_DSLLV    024     /* MIPS-II, for r4000 port */
-#define OP_DSRLV    026     /* MIPS-II, for r4000 port */
-#define OP_DSRAV    027     /* MIPS-II, for r4000 port */
+#else
+#define OP_CLZ      020
+#define OP_CLO      021
+#define OP_DCLZ     022
+#define OP_DCLO     023
+#endif
 
+#define OP_DSLLV    024
+#define OP_DSRLV    026
+#define OP_DSRAV    027
+
+#if __mips_isa_rev < 6
 #define OP_MULT     030
 #define OP_MULTU    031
 #define OP_DIV      032
 #define OP_DIVU     033
-#define OP_DMULT    034     /* MIPS-II, for r4000 port */
-#define OP_DMULTU   035     /* MIPS-II, for r4000 port */
-#define OP_DDIV     036     /* MIPS-II, for r4000 port */
-#define OP_DDIVU    037     /* MIPS-II, for r4000 port */
+#define OP_DMULT    034
+#define OP_DMULTU   035
+#define OP_DDIV     036
+#define OP_DDIVU    037
+#else
+#define OP_SOP30    030
+#define OP_SOP31    031
+#define OP_SOP32    032
+#define OP_SOP33    033
+#define OP_SOP34    034
+#define OP_SOP35    035
+#define OP_SOP36    036
+#define OP_SOP37    037
+#endif
 
 #define OP_ADD      040
 #define OP_ADDU     041
@@ -233,73 +304,96 @@
 
 #define OP_SLT      052
 #define OP_SLTU     053
-#define OP_DADD     054     /* MIPS-II, for r4000 port */
-#define OP_DADDU    055     /* MIPS-II, for r4000 port */
-#define OP_DSUB     056     /* MIPS-II, for r4000 port */
-#define OP_DSUBU    057     /* MIPS-II, for r4000 port */
+#define OP_DADD     054
+#define OP_DADDU    055
+#define OP_DSUB     056
+#define OP_DSUBU    057
 
-#define OP_TGE      060     /* MIPS-II, for r4000 port */
-#define OP_TGEU     061     /* MIPS-II, for r4000 port */
-#define OP_TLT      062     /* MIPS-II, for r4000 port */
-#define OP_TLTU     063     /* MIPS-II, for r4000 port */
-#define OP_TEQ      064     /* MIPS-II, for r4000 port */
-#define OP_TNE      066     /* MIPS-II, for r4000 port */
+#define OP_TGE      060
+#define OP_TGEU     061
+#define OP_TLT      062
+#define OP_TLTU     063
+#define OP_TEQ      064
+#define OP_TNE      066
 
-#define OP_DSLL     070     /* MIPS-II, for r4000 port */
-#define OP_DSRL     072     /* MIPS-II, for r4000 port */
-#define OP_DSRA     073     /* MIPS-II, for r4000 port */
-#define OP_DSLL32   074     /* MIPS-II, for r4000 port */
-#define OP_DSRL32   076     /* MIPS-II, for r4000 port */
-#define OP_DSRA32   077     /* MIPS-II, for r4000 port */
+#define OP_DSLL     070
+#define OP_DSRL     072
+#define OP_DSRA     073
+#define OP_DSLL32   074
+#define OP_DSRL32   076
+#define OP_DSRA32   077
 
+#if __mips_isa_rev < 6
 /*
  * Values for the 'func' field when 'op' == OP_SPECIAL2.
+ * OP_SPECIAL2 opcodes are removed in mips32r6
  */
 #define OP_MAD      000     /* QED */
 #define OP_MADU     001     /* QED */
 #define OP_MUL      002     /* QED */
+#endif
 
 /*
  * Values for the 'func' field when 'op' == OP_SPECIAL3.
  */
 #define OP_EXT      000
+#define OP_DEXTM    001
+#define OP_DEXTU    002
+#define OP_DEXT     003
 #define OP_INS      004
+#define OP_DINSM    005
+#define OP_DINSU    006
+#define OP_DINS     007
 #define OP_BSHFL    040
+#define OP_RDHWR    073
 
 /*
  * Values for the 'shamt' field when OP_SPECIAL3 && func OP_BSHFL.
  */
+
 #define OP_WSBH     002
 #define OP_SEB      020
 #define OP_SEH      030
 
+#if __mips_isa_rev == 6
+/*
+ * Values for the 'shamt' field when OP_SOP30.
+ */
+#define OP_MUL      002
+#define OP_MUH      003
+#endif
+
 /*
  * Values for the 'func' field when 'op' == OP_BCOND.
  */
 #define OP_BLTZ     000
 #define OP_BGEZ     001
-#define OP_BLTZL    002     /* MIPS-II, for r4000 port */
-#define OP_BGEZL    003     /* MIPS-II, for r4000 port */
 
-#define OP_TGEI     010     /* MIPS-II, for r4000 port */
-#define OP_TGEIU    011     /* MIPS-II, for r4000 port */
-#define OP_TLTI     012     /* MIPS-II, for r4000 port */
-#define OP_TLTIU    013     /* MIPS-II, for r4000 port */
-#define OP_TEQI     014     /* MIPS-II, for r4000 port */
-#define OP_TNEI     016     /* MIPS-II, for r4000 port */
-
-#define OP_BLTZAL   020     /* MIPS-II, for r4000 port */
+#if __mips_isa_rev < 6
+#define OP_BLTZL    002
+#define OP_BGEZL    003
+#define OP_TGEI     010
+#define OP_TGEIU    011
+#define OP_TLTI     012
+#define OP_TLTIU    013
+#define OP_TEQI     014
+#define OP_TNEI     016
+#define OP_BLTZAL   020
 #define OP_BGEZAL   021
 #define OP_BLTZALL  022
 #define OP_BGEZALL  023
+#else
+#define OP_NAL      020
+#define OP_BAL      021
+#endif
 
 /*
  * Values for the 'rs' field when 'op' == OP_COPz.
  */
 #define OP_MF       000
-#define OP_DMF      001     /* MIPS-II, for r4000 port */
+#define OP_DMF      001
 #define OP_MT       004
-#define OP_DMT      005     /* MIPS-II, for r4000 port */
+#define OP_DMT      005
 #define OP_BCx      010
 #define OP_BCy      014
 #define OP_CF       002
@@ -311,6 +405,6 @@
 #define COPz_BC_TF_MASK 0x01
 #define COPz_BC_TRUE    0x01
 #define COPz_BC_FALSE   0x00
-#define COPz_BCL_TF_MASK    0x02        /* MIPS-II, for r4000 port */
-#define COPz_BCL_TRUE   0x02        /* MIPS-II, for r4000 port */
-#define COPz_BCL_FALSE  0x00        /* MIPS-II, for r4000 port */
+#define COPz_BCL_TF_MASK    0x02
+#define COPz_BCL_TRUE   0x02
+#define COPz_BCL_FALSE  0x00
diff --git a/libpixelflinger/include/private/pixelflinger/ggl_context.h b/libpixelflinger/include/private/pixelflinger/ggl_context.h
index d43655c..d45dabc 100644
--- a/libpixelflinger/include/private/pixelflinger/ggl_context.h
+++ b/libpixelflinger/include/private/pixelflinger/ggl_context.h
@@ -42,7 +42,7 @@
 #else
 
 inline uint32_t GGL_RGBA_TO_HOST(uint32_t v) {
-#if defined(__mips__) && __mips==32 && __mips_isa_rev>=2
+#if defined(__mips__) && __mips_isa_rev>=2
     uint32_t r;
     __asm__("wsbh %0, %1;"
         "rotr %0, %0, 16"
@@ -55,7 +55,7 @@
 #endif
 }
 inline uint32_t GGL_HOST_TO_RGBA(uint32_t v) {
-#if defined(__mips__) && __mips==32 && __mips_isa_rev>=2
+#if defined(__mips__) && __mips_isa_rev>=2
     uint32_t r;
     __asm__("wsbh %0, %1;"
         "rotr %0, %0, 16"
@@ -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/include/private/pixelflinger/ggl_fixed.h b/libpixelflinger/include/private/pixelflinger/ggl_fixed.h
index 787f620..17b85dd 100644
--- a/libpixelflinger/include/private/pixelflinger/ggl_fixed.h
+++ b/libpixelflinger/include/private/pixelflinger/ggl_fixed.h
@@ -520,6 +520,252 @@
     return res;
 }
 
+#elif defined(__mips__) && __mips_isa_rev == 6
+
+/*inline MIPS implementations*/
+inline GGLfixed gglMulx(GGLfixed a, GGLfixed b, int shift) CONST;
+inline GGLfixed gglMulx(GGLfixed a, GGLfixed b, int shift) {
+    GGLfixed result,tmp,tmp1,tmp2;
+
+    if (__builtin_constant_p(shift)) {
+        if (shift == 0) {
+            asm ("mul %[res], %[a], %[b] \t\n"
+            : [res]"=&r"(result)
+            : [a]"r"(a),[b]"r"(b)
+            );
+        } else if (shift == 32)
+        {
+            asm ("mul %[res], %[a], %[b] \t\n"
+            "li  %[tmp],1\t\n"
+            "sll  %[tmp],%[tmp],0x1f\t\n"
+            "addu %[tmp1],%[tmp],%[res] \t\n"
+            "muh %[res], %[a], %[b] \t\n"
+            "sltu %[tmp1],%[tmp1],%[tmp]\t\n"   /*obit*/
+            "sra %[tmp],%[tmp],0x1f \t\n"
+            "addu %[res],%[res],%[tmp]\t\n"
+            "addu %[res],%[res],%[tmp1]\t\n"
+            : [res]"=&r"(result),[tmp]"=&r"(tmp),[tmp1]"=&r"(tmp1)
+            : [a]"r"(a),[b]"r"(b),[shift]"I"(shift)
+            );
+        } else if ((shift >0) && (shift < 32))
+        {
+            asm ("mul %[res], %[a], %[b] \t\n"
+            "li  %[tmp],1 \t\n"
+            "sll  %[tmp],%[tmp],%[shiftm1] \t\n"
+            "addu %[tmp1],%[tmp],%[res] \t\n"
+            "sltu %[tmp1],%[tmp1],%[tmp] \t\n"  /*obit?*/
+            "addu  %[res],%[res],%[tmp] \t\n"
+            "muh %[tmp], %[a], %[b] \t\n"
+            "addu  %[tmp],%[tmp],%[tmp1] \t\n"
+            "sll   %[tmp],%[tmp],%[lshift] \t\n"
+            "srl   %[res],%[res],%[rshift]    \t\n"
+            "or    %[res],%[res],%[tmp] \t\n"
+            : [res]"=&r"(result),[tmp]"=&r"(tmp),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2)
+            : [a]"r"(a),[b]"r"(b),[lshift]"I"(32-shift),[rshift]"I"(shift),[shiftm1]"I"(shift-1)
+            );
+        } else {
+            asm ("mul %[res], %[a], %[b] \t\n"
+            "li  %[tmp],1 \t\n"
+            "sll  %[tmp],%[tmp],%[shiftm1] \t\n"
+            "addu %[tmp1],%[tmp],%[res] \t\n"
+            "sltu %[tmp1],%[tmp1],%[tmp] \t\n"  /*obit?*/
+            "sra  %[tmp2],%[tmp],0x1f \t\n"
+            "addu  %[res],%[res],%[tmp] \t\n"
+            "muh  %[tmp], %[a], %[b]   \t\n"
+            "addu  %[tmp],%[tmp],%[tmp2] \t\n"
+            "addu  %[tmp],%[tmp],%[tmp1] \t\n"            /*tmp=hi*/
+            "srl   %[tmp2],%[res],%[rshift]    \t\n"
+            "srav  %[res], %[tmp],%[rshift]\t\n"
+            "sll   %[tmp],%[tmp],1 \t\n"
+            "sll   %[tmp],%[tmp],%[norbits] \t\n"
+            "or    %[tmp],%[tmp],%[tmp2] \t\n"
+            "seleqz  %[tmp],%[tmp],%[bit5] \t\n"
+            "selnez  %[res],%[res],%[bit5] \t\n"
+            "or    %[res],%[res],%[tmp] \t\n"
+            : [res]"=&r"(result),[tmp]"=&r"(tmp),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2)
+            : [a]"r"(a),[b]"r"(b),[norbits]"I"(~(shift)),[rshift]"I"(shift),[shiftm1] "I"(shift-1),[bit5]"I"(shift & 0x20)
+            );
+        }
+    } else {
+        asm ("mul %[res], %[a], %[b] \t\n"
+        "li  %[tmp],1 \t\n"
+        "sll  %[tmp],%[tmp],%[shiftm1] \t\n"
+        "addu %[tmp1],%[tmp],%[res] \t\n"
+        "sltu %[tmp1],%[tmp1],%[tmp] \t\n"  /*obit?*/
+        "sra  %[tmp2],%[tmp],0x1f \t\n"
+        "addu  %[res],%[res],%[tmp] \t\n"
+        "muh  %[tmp], %[a], %[b] \t\n"
+        "addu  %[tmp],%[tmp],%[tmp2] \t\n"
+        "addu  %[tmp],%[tmp],%[tmp1] \t\n"            /*tmp=hi*/
+        "srl   %[tmp2],%[res],%[rshift]    \t\n"
+        "srav  %[res], %[tmp],%[rshift]\t\n"
+        "sll   %[tmp],%[tmp],1 \t\n"
+        "sll   %[tmp],%[tmp],%[norbits] \t\n"
+        "or    %[tmp],%[tmp],%[tmp2] \t\n"
+        "seleqz  %[tmp],%[tmp],%[bit5] \t\n"
+        "selnez  %[res],%[res],%[bit5] \t\n"
+        "or    %[res],%[res],%[tmp] \t\n"
+         : [res]"=&r"(result),[tmp]"=&r"(tmp),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2)
+         : [a]"r"(a),[b]"r"(b),[norbits]"r"(~(shift)),[rshift] "r"(shift),[shiftm1]"r"(shift-1),[bit5] "r"(shift & 0x20)
+         );
+        }
+        return result;
+}
+
+inline GGLfixed gglMulAddx(GGLfixed a, GGLfixed b, GGLfixed c, int shift) CONST;
+inline GGLfixed gglMulAddx(GGLfixed a, GGLfixed b, GGLfixed c, int shift) {
+    GGLfixed result,t,tmp1,tmp2;
+
+    if (__builtin_constant_p(shift)) {
+        if (shift == 0) {
+                 asm ("mul %[lo], %[a], %[b] \t\n"
+                 "addu  %[lo],%[lo],%[c]    \t\n"
+                 : [lo]"=&r"(result)
+                 : [a]"r"(a),[b]"r"(b),[c]"r"(c)
+                 );
+                } else if (shift == 32) {
+                    asm ("muh %[lo], %[a], %[b] \t\n"
+                    "addu  %[lo],%[lo],%[c]    \t\n"
+                    : [lo]"=&r"(result)
+                    : [a]"r"(a),[b]"r"(b),[c]"r"(c)
+                    );
+                } else if ((shift>0) && (shift<32)) {
+                    asm ("mul %[res], %[a], %[b] \t\n"
+                    "muh  %[t], %[a], %[b] \t\n"
+                    "srl   %[res],%[res],%[rshift]    \t\n"
+                    "sll   %[t],%[t],%[lshift]     \t\n"
+                    "or  %[res],%[res],%[t]    \t\n"
+                    "addu  %[res],%[res],%[c]    \t\n"
+                    : [res]"=&r"(result),[t]"=&r"(t)
+                    : [a]"r"(a),[b]"r"(b),[c]"r"(c),[lshift]"I"(32-shift),[rshift]"I"(shift)
+                    );
+                } else {
+                    asm ("mul %[res], %[a], %[b] \t\n"
+                    "muh %[t], %[a], %[b] \t\n"
+                    "nor %[tmp1],$zero,%[shift]\t\n"
+                    "srl   %[res],%[res],%[shift]    \t\n"
+                    "sll   %[tmp2],%[t],1     \t\n"
+                    "sllv  %[tmp2],%[tmp2],%[tmp1]     \t\n"
+                    "or  %[tmp1],%[tmp2],%[res]    \t\n"
+                    "srav  %[res],%[t],%[shift]     \t\n"
+                    "andi %[tmp2],%[shift],0x20\t\n"
+                    "seleqz %[tmp1],%[tmp1],%[tmp2]\t\n"
+                    "selnez %[res],%[res],%[tmp2]\t\n"
+                    "or %[res],%[res],%[tmp1]\t\n"
+                    "addu  %[res],%[res],%[c]    \t\n"
+                    : [res]"=&r"(result),[t]"=&r"(t),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2)
+                    : [a]"r"(a),[b]"r"(b),[c]"r"(c),[shift]"I"(shift)
+                    );
+                }
+            } else {
+                asm ("mul %[res], %[a], %[b] \t\n"
+                "muh %[t], %[a], %[b] \t\n"
+                "nor %[tmp1],$zero,%[shift]\t\n"
+                "srl   %[res],%[res],%[shift]    \t\n"
+                "sll   %[tmp2],%[t],1     \t\n"
+                "sllv  %[tmp2],%[tmp2],%[tmp1]     \t\n"
+                "or  %[tmp1],%[tmp2],%[res]    \t\n"
+                "srav  %[res],%[t],%[shift]     \t\n"
+                "andi %[tmp2],%[shift],0x20\t\n"
+                "seleqz %[tmp1],%[tmp1],%[tmp2]\t\n"
+                "selnez %[res],%[res],%[tmp2]\t\n"
+                "or %[res],%[res],%[tmp1]\t\n"
+                "addu  %[res],%[res],%[c]    \t\n"
+                : [res]"=&r"(result),[t]"=&r"(t),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2)
+                : [a]"r"(a),[b]"r"(b),[c]"r"(c),[shift]"r"(shift)
+                );
+            }
+            return result;
+}
+
+inline GGLfixed gglMulSubx(GGLfixed a, GGLfixed b, GGLfixed c, int shift) CONST;
+inline GGLfixed gglMulSubx(GGLfixed a, GGLfixed b, GGLfixed c, int shift) {
+    GGLfixed result,t,tmp1,tmp2;
+
+    if (__builtin_constant_p(shift)) {
+        if (shift == 0) {
+                 asm ("mul %[lo], %[a], %[b] \t\n"
+                 "subu  %[lo],%[lo],%[c]    \t\n"
+                 : [lo]"=&r"(result)
+                 : [a]"r"(a),[b]"r"(b),[c]"r"(c)
+                 );
+                } else if (shift == 32) {
+                    asm ("muh %[lo], %[a], %[b] \t\n"
+                    "subu  %[lo],%[lo],%[c]    \t\n"
+                    : [lo]"=&r"(result)
+                    : [a]"r"(a),[b]"r"(b),[c]"r"(c)
+                    );
+                } else if ((shift>0) && (shift<32)) {
+                    asm ("mul %[res], %[a], %[b] \t\n"
+                    "muh %[t], %[a], %[b] \t\n"
+                    "srl   %[res],%[res],%[rshift]    \t\n"
+                    "sll   %[t],%[t],%[lshift]     \t\n"
+                    "or  %[res],%[res],%[t]    \t\n"
+                    "subu  %[res],%[res],%[c]    \t\n"
+                    : [res]"=&r"(result),[t]"=&r"(t)
+                    : [a]"r"(a),[b]"r"(b),[c]"r"(c),[lshift]"I"(32-shift),[rshift]"I"(shift)
+                    );
+                } else {
+                    asm ("mul %[res], %[a], %[b] \t\n"
+                    "muh %[t], %[a], %[b] \t\n"
+                    "nor %[tmp1],$zero,%[shift]\t\n"
+                    "srl   %[res],%[res],%[shift]    \t\n"
+                    "sll   %[tmp2],%[t],1     \t\n"
+                    "sllv  %[tmp2],%[tmp2],%[tmp1]     \t\n"
+                    "or  %[tmp1],%[tmp2],%[res]    \t\n"
+                    "srav  %[res],%[t],%[shift]     \t\n"
+                    "andi %[tmp2],%[shift],0x20\t\n"
+                    "seleqz %[tmp1],%[tmp1],%[tmp2]\t\n"
+                    "selnez %[res],%[res],%[tmp2]\t\n"
+                    "or %[res],%[res],%[tmp1]\t\n"
+                    "subu  %[res],%[res],%[c]    \t\n"
+                    : [res]"=&r"(result),[t]"=&r"(t),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2)
+                    : [a]"r"(a),[b]"r"(b),[c]"r"(c),[shift]"I"(shift)
+                     );
+                    }
+                } else {
+                asm ("mul %[res], %[a], %[b] \t\n"
+                "muh %[t], %[a], %[b] \t\n"
+                "nor %[tmp1],$zero,%[shift]\t\n"
+                "srl   %[res],%[res],%[shift]    \t\n"
+                "sll   %[tmp2],%[t],1     \t\n"
+                "sllv  %[tmp2],%[tmp2],%[tmp1]     \t\n"
+                "or  %[tmp1],%[tmp2],%[res]    \t\n"
+                "srav  %[res],%[t],%[shift]     \t\n"
+                "andi %[tmp2],%[shift],0x20\t\n"
+                "seleqz %[tmp1],%[tmp1],%[tmp2]\t\n"
+                "selnez %[res],%[res],%[tmp2]\t\n"
+                "or %[res],%[res],%[tmp1]\t\n"
+                "subu  %[res],%[res],%[c]    \t\n"
+                : [res]"=&r"(result),[t]"=&r"(t),[tmp1]"=&r"(tmp1),[tmp2]"=&r"(tmp2)
+                : [a]"r"(a),[b]"r"(b),[c]"r"(c),[shift]"r"(shift)
+                );
+            }
+    return result;
+}
+
+inline int64_t gglMulii(int32_t x, int32_t y) CONST;
+inline int64_t gglMulii(int32_t x, int32_t y) {
+    union {
+        struct {
+#if defined(__MIPSEL__)
+            int32_t lo;
+            int32_t hi;
+#elif defined(__MIPSEB__)
+            int32_t hi;
+            int32_t lo;
+#endif
+        } s;
+        int64_t res;
+    }u;
+    asm("mul %0, %2, %3 \t\n"
+        "muh %1, %2, %3 \t\n"
+        : "=r"(u.s.lo), "=&r"(u.s.hi)
+        : "%r"(x), "r"(y)
+        );
+    return u.res;
+}
+
 #else // ----------------------------------------------------------------------
 
 inline GGLfixed gglMulx(GGLfixed a, GGLfixed b, int shift) CONST;
diff --git a/libpixelflinger/scanline.cpp b/libpixelflinger/scanline.cpp
index 3d14531..a718b02 100644
--- a/libpixelflinger/scanline.cpp
+++ b/libpixelflinger/scanline.cpp
@@ -41,6 +41,8 @@
 #include "codeflinger/Arm64Assembler.h"
 #elif defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6
 #include "codeflinger/MIPSAssembler.h"
+#elif defined(__mips__) && defined(__LP64__)
+#include "codeflinger/MIPS64Assembler.h"
 #endif
 //#include "codeflinger/ARMAssemblerOptimizer.h"
 
@@ -59,7 +61,7 @@
 #   define ANDROID_CODEGEN      ANDROID_CODEGEN_GENERATED
 #endif
 
-#if defined(__arm__) || (defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6) || defined(__aarch64__)
+#if defined(__arm__) || (defined(__mips__) && ((!defined(__LP64__) && __mips_isa_rev < 6) || defined(__LP64__))) || defined(__aarch64__)
 #   define ANDROID_ARM_CODEGEN  1
 #else
 #   define ANDROID_ARM_CODEGEN  0
@@ -73,7 +75,7 @@
  */
 #define DEBUG_NEEDS  0
 
-#if defined( __mips__) && !defined(__LP64__) && __mips_isa_rev < 6
+#if defined( __mips__) && ((!defined(__LP64__) && __mips_isa_rev < 6) || defined(__LP64__))
 #define ASSEMBLY_SCRATCH_SIZE   4096
 #elif defined(__aarch64__)
 #define ASSEMBLY_SCRATCH_SIZE   8192
@@ -136,6 +138,9 @@
 extern "C" void scanline_col32cb16blend_arm64(uint16_t *dst, uint32_t col, size_t ct);
 #elif defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6
 extern "C" void scanline_t32cb16blend_mips(uint16_t*, uint32_t*, size_t);
+#elif defined(__mips__) && defined(__LP64__)
+extern "C" void scanline_t32cb16blend_mips64(uint16_t*, uint32_t*, size_t);
+extern "C" void scanline_col32cb16blend_mips64(uint16_t *dst, uint32_t col, size_t ct);
 #endif
 
 // ----------------------------------------------------------------------------
@@ -286,7 +291,7 @@
 
 #if ANDROID_ARM_CODEGEN
 
-#if defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6
+#if defined(__mips__) && ((!defined(__LP64__) && __mips_isa_rev < 6) || defined(__LP64__))
 static CodeCache gCodeCache(32 * 1024);
 #elif defined(__aarch64__)
 static CodeCache gCodeCache(48 * 1024);
@@ -406,8 +411,10 @@
         //GGLAssembler assembler(
         //        new ARMAssemblerOptimizer(new ARMAssembler(a)) );
 #endif
-#if defined(__mips__)
+#if defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6
         GGLAssembler assembler( new ArmToMipsAssembler(a) );
+#elif defined(__mips__) && defined(__LP64__)
+        GGLAssembler assembler( new ArmToMips64Assembler(a) );
 #elif defined(__aarch64__)
         GGLAssembler assembler( new ArmToArm64Assembler(a) );
 #endif
@@ -2103,6 +2110,8 @@
 #endif // defined(__ARM_HAVE_NEON) && BYTE_ORDER == LITTLE_ENDIAN
 #elif ((ANDROID_CODEGEN >= ANDROID_CODEGEN_ASM) && defined(__aarch64__))
     scanline_col32cb16blend_arm64(dst, GGL_RGBA_TO_HOST(c->packed8888), ct);
+#elif ((ANDROID_CODEGEN >= ANDROID_CODEGEN_ASM) && (defined(__mips__) && defined(__LP64__)))
+    scanline_col32cb16blend_mips64(dst, GGL_RGBA_TO_HOST(c->packed8888), ct);
 #else
     uint32_t s = GGL_RGBA_TO_HOST(c->packed8888);
     int sA = (s>>24);
@@ -2175,7 +2184,8 @@
 
 void scanline_t32cb16blend(context_t* c)
 {
-#if ((ANDROID_CODEGEN >= ANDROID_CODEGEN_ASM) && (defined(__arm__) || (defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6) || defined(__aarch64__)))
+#if ((ANDROID_CODEGEN >= ANDROID_CODEGEN_ASM) && (defined(__arm__) || defined(__aarch64__) || \
+    (defined(__mips__) && ((!defined(__LP64__) && __mips_isa_rev < 6) || defined(__LP64__)))))
     int32_t x = c->iterators.xl;
     size_t ct = c->iterators.xr - x;
     int32_t y = c->iterators.y;
@@ -2191,8 +2201,10 @@
     scanline_t32cb16blend_arm(dst, src, ct);
 #elif defined(__aarch64__)
     scanline_t32cb16blend_arm64(dst, src, ct);
-#elif defined(__mips__)
+#elif defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6
     scanline_t32cb16blend_mips(dst, src, ct);
+#elif defined(__mips__) && defined(__LP64__)
+    scanline_t32cb16blend_mips64(dst, src, ct);
 #endif
 #else
     dst_iterator16  di(c);
diff --git a/libpixelflinger/tests/arch-mips/Android.mk b/libpixelflinger/tests/arch-mips/Android.mk
new file mode 100644
index 0000000..fe6979e
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips/Android.mk
@@ -0,0 +1,6 @@
+ifeq ($(TARGET_ARCH),mips)
+include $(all-subdir-makefiles)
+endif
+ifeq ($(TARGET_ARCH),mipsel)
+include $(all-subdir-makefiles)
+endif
diff --git a/libpixelflinger/tests/arch-mips/col32cb16blend/Android.mk b/libpixelflinger/tests/arch-mips/col32cb16blend/Android.mk
new file mode 100644
index 0000000..40f197f
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips/col32cb16blend/Android.mk
@@ -0,0 +1,18 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+    col32cb16blend_test.c \
+    ../../../arch-mips/col32cb16blend.S
+
+LOCAL_SHARED_LIBRARIES :=
+
+LOCAL_C_INCLUDES :=
+
+LOCAL_MODULE:= test-pixelflinger-mips-col32cb16blend
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_MULTILIB := 32
+
+include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-mips/col32cb16blend/col32cb16blend_test.c b/libpixelflinger/tests/arch-mips/col32cb16blend/col32cb16blend_test.c
new file mode 100644
index 0000000..dd0e60f
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips/col32cb16blend/col32cb16blend_test.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+
+#define ARGB_8888_MAX   0xFFFFFFFF
+#define ARGB_8888_MIN   0x00000000
+#define RGB_565_MAX 0xFFFF
+#define RGB_565_MIN 0x0000
+
+struct test_t
+{
+    char name[256];
+    uint32_t src_color;
+    uint16_t dst_color;
+    size_t count;
+};
+
+struct test_t tests[] =
+{
+    {"Count 1, Src=Max, Dst=Min", ARGB_8888_MAX, RGB_565_MIN, 1},
+    {"Count 2, Src=Min, Dst=Max", ARGB_8888_MIN, RGB_565_MAX, 2},
+    {"Count 3, Src=Max, Dst=Max", ARGB_8888_MAX, RGB_565_MAX, 3},
+    {"Count 4, Src=Min, Dst=Min", ARGB_8888_MAX, RGB_565_MAX, 4},
+    {"Count 1, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 1},
+    {"Count 2, Src=Rand, Dst=Rand", 0xABCDEF12, 0x2345, 2},
+    {"Count 3, Src=Rand, Dst=Rand", 0x11111111, 0xEDFE, 3},
+    {"Count 4, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 4},
+    {"Count 5, Src=Rand, Dst=Rand", 0xEFEFFEFE, 0xFACC, 5},
+    {"Count 10, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 10}
+};
+
+void scanline_col32cb16blend_mips(uint16_t *dst, uint32_t src, size_t count);
+void scanline_col32cb16blend_c(uint16_t * dst, uint32_t src, size_t count)
+{
+    uint32_t srcAlpha = (src>>24);
+    uint32_t f = 0x100 - (srcAlpha + (srcAlpha>>7));
+
+    while (count--)
+    {
+        uint16_t d = *dst;
+        int dstR = (d>>11)&0x1f;
+        int dstG = (d>>5)&0x3f;
+        int dstB = (d)&0x1f;
+        int srcR = (src >> (   3))&0x1F;
+        int srcG = (src >> ( 8+2))&0x3F;
+        int srcB = (src >> (16+3))&0x1F;
+        srcR += (f*dstR)>>8;
+        srcG += (f*dstG)>>8;
+        srcB += (f*dstB)>>8;
+        *dst++ = (uint16_t)((srcR<<11)|(srcG<<5)|srcB);
+    }
+}
+
+void scanline_col32cb16blend_test()
+{
+    uint16_t dst_c[16], dst_asm[16];
+    uint32_t i, j;
+
+    for(i = 0; i < sizeof(tests)/sizeof(struct test_t); ++i)
+    {
+        struct test_t test = tests[i];
+
+        printf("Testing - %s:",test.name);
+
+        memset(dst_c, 0, sizeof(dst_c));
+        memset(dst_asm, 0, sizeof(dst_asm));
+
+        for(j = 0; j < test.count; ++j)
+        {
+            dst_c[j]   = test.dst_color;
+            dst_asm[j] = test.dst_color;
+        }
+
+
+        scanline_col32cb16blend_c(dst_c, test.src_color, test.count);
+        scanline_col32cb16blend_mips(dst_asm, test.src_color, test.count);
+
+        if(memcmp(dst_c, dst_asm, sizeof(dst_c)) == 0)
+            printf("Passed\n");
+        else
+            printf("Failed\n");
+
+        for(j = 0; j < test.count; ++j)
+        {
+            printf("dst_c[%d] = %x, dst_asm[%d] = %x \n", j, dst_c[j], j, dst_asm[j]);
+        }
+    }
+}
+
+int main()
+{
+    scanline_col32cb16blend_test();
+    return 0;
+}
diff --git a/libpixelflinger/tests/arch-mips/t32cb16blend/Android.mk b/libpixelflinger/tests/arch-mips/t32cb16blend/Android.mk
new file mode 100644
index 0000000..d0c0ae4
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips/t32cb16blend/Android.mk
@@ -0,0 +1,18 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+    t32cb16blend_test.c \
+    ../../../arch-mips/t32cb16blend.S
+
+LOCAL_SHARED_LIBRARIES :=
+
+LOCAL_C_INCLUDES :=
+
+LOCAL_MODULE:= test-pixelflinger-mips-t32cb16blend
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_MULTILIB := 32
+
+include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-mips/t32cb16blend/t32cb16blend_test.c b/libpixelflinger/tests/arch-mips/t32cb16blend/t32cb16blend_test.c
new file mode 100644
index 0000000..c6d6937
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips/t32cb16blend/t32cb16blend_test.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#define ARGB_8888_MAX   0xFFFFFFFF
+#define ARGB_8888_MIN   0x00000000
+#define RGB_565_MAX     0xFFFF
+#define RGB_565_MIN     0x0000
+
+struct test_t
+{
+    char name[256];
+    uint32_t src_color;
+    uint16_t dst_color;
+    size_t count;
+};
+
+struct test_t tests[] =
+{
+    {"Count 0", 0, 0, 0},
+    {"Count 1, Src=Max, Dst=Min", ARGB_8888_MAX, RGB_565_MIN, 1},
+    {"Count 2, Src=Min, Dst=Max", ARGB_8888_MIN, RGB_565_MAX, 2},
+    {"Count 3, Src=Max, Dst=Max", ARGB_8888_MAX, RGB_565_MAX, 3},
+    {"Count 4, Src=Min, Dst=Min", ARGB_8888_MAX, RGB_565_MAX, 4},
+    {"Count 1, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 1},
+    {"Count 2, Src=Rand, Dst=Rand", 0xABCDEF12, 0x2345, 2},
+    {"Count 3, Src=Rand, Dst=Rand", 0x11111111, 0xEDFE, 3},
+    {"Count 4, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 4},
+    {"Count 5, Src=Rand, Dst=Rand", 0xEFEFFEFE, 0xFACC, 5},
+    {"Count 10, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 10}
+
+};
+
+void scanline_t32cb16blend_mips(uint16_t*, uint32_t*, size_t);
+void scanline_t32cb16blend_c(uint16_t * dst, uint32_t* src, size_t count)
+{
+    while (count--)
+    {
+        uint16_t d = *dst;
+        uint32_t s = *src++;
+        int dstR = (d>>11)&0x1f;
+        int dstG = (d>>5)&0x3f;
+        int dstB = (d)&0x1f;
+        int srcR = (s >> (   3))&0x1F;
+        int srcG = (s >> ( 8+2))&0x3F;
+        int srcB = (s >> (16+3))&0x1F;
+        int srcAlpha = (s>>24) & 0xFF;
+
+
+        int f = 0x100 - (srcAlpha + ((srcAlpha>>7) & 0x1));
+        srcR += (f*dstR)>>8;
+        srcG += (f*dstG)>>8;
+        srcB += (f*dstB)>>8;
+        // srcR = srcR > 0x1F? 0x1F: srcR;
+        // srcG = srcG > 0x3F? 0x3F: srcG;
+        // srcB = srcB > 0x1F? 0x1F: srcB;
+        *dst++ = (uint16_t)((srcR<<11)|(srcG<<5)|srcB);
+    }
+}
+
+void scanline_t32cb16blend_test()
+{
+    uint16_t dst_c[16], dst_asm[16];
+    uint32_t src[16];
+    uint32_t i;
+    uint32_t  j;
+
+    for(i = 0; i < sizeof(tests)/sizeof(struct test_t); ++i)
+    {
+        struct test_t test = tests[i];
+
+        printf("Testing - %s:",test.name);
+
+        memset(dst_c, 0, sizeof(dst_c));
+        memset(dst_asm, 0, sizeof(dst_asm));
+
+        for(j = 0; j < test.count; ++j)
+        {
+            dst_c[j]   = test.dst_color;
+            dst_asm[j] = test.dst_color;
+            src[j] = test.src_color;
+        }
+
+        scanline_t32cb16blend_c(dst_c,src,test.count);
+        scanline_t32cb16blend_mips(dst_asm,src,test.count);
+
+
+        if(memcmp(dst_c, dst_asm, sizeof(dst_c)) == 0)
+            printf("Passed\n");
+        else
+            printf("Failed\n");
+
+        for(j = 0; j < test.count; ++j)
+        {
+            printf("dst_c[%d] = %x, dst_asm[%d] = %x \n", j, dst_c[j], j, dst_asm[j]);
+        }
+    }
+}
+
+int main()
+{
+    scanline_t32cb16blend_test();
+    return 0;
+}
diff --git a/libpixelflinger/tests/arch-mips64/Android.mk b/libpixelflinger/tests/arch-mips64/Android.mk
new file mode 100644
index 0000000..3b1c64e
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips64/Android.mk
@@ -0,0 +1,3 @@
+ifeq ($(TARGET_ARCH),mips64)
+include $(all-subdir-makefiles)
+endif
diff --git a/libpixelflinger/tests/arch-mips64/assembler/Android.mk b/libpixelflinger/tests/arch-mips64/assembler/Android.mk
new file mode 100644
index 0000000..4699961
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips64/assembler/Android.mk
@@ -0,0 +1,21 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+    mips64_assembler_test.cpp\
+    asm_mips_test_jacket.S
+
+LOCAL_SHARED_LIBRARIES := \
+    libcutils \
+    libpixelflinger
+
+LOCAL_C_INCLUDES := \
+    $(LOCAL_PATH)/../../..
+
+LOCAL_MODULE:= test-pixelflinger-mips64-assembler-test
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_MULTILIB := 64
+
+include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-mips64/assembler/asm_mips_test_jacket.S b/libpixelflinger/tests/arch-mips64/assembler/asm_mips_test_jacket.S
new file mode 100644
index 0000000..8a7f742
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips64/assembler/asm_mips_test_jacket.S
@@ -0,0 +1,93 @@
+# /*
+#  * Copyright (C) 2015 The Android Open Source Project
+#  * All rights reserved.
+#  *
+#  * Redistribution and use in source and binary forms, with or without
+#  * modification, are permitted provided that the following conditions
+#  * are met:
+#  *  * Redistributions of source code must retain the above copyright
+#  *    notice, this list of conditions and the following disclaimer.
+#  *  * Redistributions in binary form must reproduce the above copyright
+#  *    notice, this list of conditions and the following disclaimer in
+#  *    the documentation and/or other materials provided with the
+#  *    distribution.
+#  *
+#  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+#  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+#  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+#  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+#  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+#  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+#  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+#  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+#  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+#  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+#  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+#  * SUCH DAMAGE.
+#  */
+
+    .text
+    .align 8
+
+    .global asm_mips_test_jacket
+
+    # // Set the register
+    # // Calls the asm function
+    # // Reads the register values to output register
+
+    # // Parameters
+    # // a0 - Function to jump
+    # // a1 - register values array
+    # // a2 - flag values array
+asm_mips_test_jacket:
+    # // Save registers to stack
+    daddiu $sp, $sp, -96
+    sd  $s0, 64($sp)
+    sd  $s1, 72($sp)
+    sd  $s2, 80($sp)
+    sd  $ra, 88($sp)
+
+    move $s0, $a0
+    move $s1, $a1
+    move $s2, $a2
+
+    ld  $v0, 16($s1)
+    ld  $v1, 24($s1)
+    ld  $a0, 32($s1)
+    ld  $a1, 40($s1)
+    ld  $a2, 48($s1)
+    ld  $a3, 56($s1)
+    ld  $a4, 64($s1)
+    ld  $a5, 72($s1)
+    ld  $a6, 80($s1)
+    ld  $a7, 88($s1)
+    ld  $t0, 96($s1)
+    ld  $t1, 104($s1)
+    ld  $t2, 112($s1)
+    ld  $t3, 120($s1)
+
+    jal $s0
+
+    sd  $v0, 16($s1)
+    sd  $v1, 24($s1)
+    sd  $a0, 32($s1)
+    sd  $a1, 40($s1)
+    sd  $a2, 48($s1)
+    sd  $a3, 56($s1)
+    sd  $a4, 64($s1)
+    sd  $a5, 72($s1)
+    sd  $a6, 80($s1)
+    sd  $a7, 88($s1)
+    sd  $t0, 96($s1)
+    sd  $t1, 104($s1)
+    sd  $t2, 112($s1)
+    sd  $t3, 120($s1)
+
+    ld  $s0, 64($sp)
+    ld  $s1, 72($sp)
+    ld  $s2, 80($sp)
+    ld  $ra, 88($sp)
+
+    daddiu $sp, $sp, 96
+
+    j   $ra
diff --git a/libpixelflinger/tests/arch-mips64/assembler/mips64_assembler_test.cpp b/libpixelflinger/tests/arch-mips64/assembler/mips64_assembler_test.cpp
new file mode 100644
index 0000000..2b25223
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips64/assembler/mips64_assembler_test.cpp
@@ -0,0 +1,644 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <sys/mman.h>
+#include <cutils/ashmem.h>
+#include <cutils/atomic.h>
+#include <cutils/log.h>
+
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
+
+#include "codeflinger/ARMAssemblerInterface.h"
+#include "codeflinger/MIPS64Assembler.h"
+using namespace android;
+
+#define TESTS_DATAOP_ENABLE             1
+#define TESTS_DATATRANSFER_ENABLE       1
+#define ASSEMBLY_SCRATCH_SIZE           4096
+
+void *instrMem;
+uint32_t  instrMemSize = 128 * 1024;
+char     dataMem[8192];
+
+typedef void (*asm_function_t)();
+extern "C" void asm_mips_test_jacket(asm_function_t function,
+                                     int64_t regs[], int32_t flags[]);
+
+#define MAX_32BIT (uint32_t)(((uint64_t)1 << 32) - 1)
+#define MAX_64BIT ((uint64_t)0xFFFFFFFFFFFFFFFF)
+const uint32_t NA = 0;
+const uint32_t NUM_REGS = 32;
+const uint32_t NUM_FLAGS = 16;
+
+enum instr_t
+{
+    INSTR_ADD,
+    INSTR_SUB,
+    INSTR_AND,
+    INSTR_ORR,
+    INSTR_RSB,
+    INSTR_BIC,
+    INSTR_CMP,
+    INSTR_MOV,
+    INSTR_MVN,
+    INSTR_MUL,
+    INSTR_MLA,
+    INSTR_SMULBB,
+    INSTR_SMULBT,
+    INSTR_SMULTB,
+    INSTR_SMULTT,
+    INSTR_SMULWB,
+    INSTR_SMULWT,
+    INSTR_SMLABB,
+    INSTR_UXTB16,
+    INSTR_UBFX,
+    INSTR_ADDR_ADD,
+    INSTR_ADDR_SUB,
+    INSTR_LDR,
+    INSTR_LDRB,
+    INSTR_LDRH,
+    INSTR_ADDR_LDR,
+    INSTR_LDM,
+    INSTR_STR,
+    INSTR_STRB,
+    INSTR_STRH,
+    INSTR_ADDR_STR,
+    INSTR_STM
+};
+
+enum shift_t
+{
+    SHIFT_LSL,
+    SHIFT_LSR,
+    SHIFT_ASR,
+    SHIFT_ROR,
+    SHIFT_NONE
+};
+
+enum offset_t
+{
+    REG_SCALE_OFFSET,
+    REG_OFFSET,
+    IMM8_OFFSET,
+    IMM12_OFFSET,
+    NO_OFFSET
+};
+
+enum cond_t
+{
+    EQ, NE, CS, CC, MI, PL, VS, VC, HI, LS, GE, LT, GT, LE, AL, NV,
+    HS = CS,
+    LO = CC
+};
+
+const char * cc_code[] =
+{
+    "EQ", "NE", "CS", "CC", "MI", "PL", "VS", "VC",
+    "HI", "LS","GE","LT", "GT", "LE", "AL", "NV"
+};
+
+struct condTest_t
+{
+    int     mode;
+    int32_t Rcond1;
+    int32_t Rcond2;
+    uint64_t Rcond1Value;
+    uint64_t Rcond2Value;
+};
+
+
+struct dataOpTest_t
+{
+    uint32_t   id;
+    instr_t    op;
+    condTest_t preCond;
+    cond_t     cond;
+    bool       setFlags;
+    uint64_t   RnValue;
+    uint64_t   RsValue;
+    bool       immediate;
+    uint32_t   immValue;
+    uint64_t   RmValue;
+    uint32_t   shiftMode;
+    uint32_t   shiftAmount;
+    uint64_t   RdValue;
+    bool       checkRd;
+    uint64_t   postRdValue;
+};
+
+struct dataTransferTest_t
+{
+    uint32_t id;
+    instr_t  op;
+    uint32_t preFlag;
+    cond_t   cond;
+    bool     setMem;
+    uint64_t memOffset;
+    uint64_t memValue;
+    uint64_t RnValue;
+    offset_t offsetType;
+    uint64_t RmValue;
+    uint32_t immValue;
+    bool     writeBack;
+    bool     preIndex;
+    bool     postIndex;
+    uint64_t RdValue;
+    uint64_t postRdValue;
+    uint64_t postRnValue;
+    bool     checkMem;
+    uint64_t postMemOffset;
+    uint32_t postMemLength;
+    uint64_t postMemValue;
+};
+
+
+dataOpTest_t dataOpTests [] =
+{
+     {0xA000,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,1,MAX_32BIT,NA,NA,NA,NA,1,0},
+     {0xA001,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,1,MAX_32BIT-1,NA,NA,NA,NA,1,MAX_64BIT},
+     {0xA002,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,0,NA,MAX_32BIT,NA,NA,NA,1,0},
+     {0xA003,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,0,NA,MAX_32BIT-1,NA,NA,NA,1,MAX_64BIT},
+     {0xA004,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSL, 0,NA,1,0},
+     {0xA005,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSL,31,NA,1,0xFFFFFFFF80000001},
+     {0xA006,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,0,0,3,SHIFT_LSR,1,NA,1,2},
+     {0xA007,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSR,31,NA,1,2},
+     {0xA008,INSTR_ADD,{0,0,0,0,0},AL,0,0,NA,0,0,3,SHIFT_ASR,1,NA,1,1},
+     {0xA009,INSTR_ADD,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_64BIT,SHIFT_ASR,31,NA,1,0},
+     {0xA010,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,1,MAX_32BIT,0,0,0,NA,1,1},
+     {0xA011,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,1,MAX_32BIT-1,0,0,0,NA,1,0},
+     {0xA012,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,0,0,NA,1,1},
+     {0xA013,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT-1,0,0,NA,1,0},
+     {0xA014,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSL,0,NA,1,1},
+     {0xA015,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSL,31,NA,1,0},
+     {0xA016,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,0,0,3,SHIFT_LSR,1,NA,1,1},
+     {0xA017,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSR,31,NA,1,1},
+     {0xA018,INSTR_AND,{0,0,0,0,0},AL,0,0,NA,0,0,3,SHIFT_ASR,1,NA,1,0},
+     {0xA019,INSTR_AND,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_ASR,31,NA,1,1},
+     {0xA020,INSTR_ORR,{0,0,0,0,0},AL,0,3,NA,1,MAX_32BIT,0,0,0,NA,1,MAX_64BIT},
+     {0xA021,INSTR_ORR,{0,0,0,0,0},AL,0,2,NA,1,MAX_32BIT-1,0,0,0,NA,1,MAX_64BIT-1},
+     {0xA022,INSTR_ORR,{0,0,0,0,0},AL,0,3,NA,0,0,MAX_32BIT,0,0,NA,1,MAX_64BIT},
+     {0xA023,INSTR_ORR,{0,0,0,0,0},AL,0,2,NA,0,0,MAX_32BIT-1,0,0,NA,1,MAX_64BIT-1},
+     {0xA024,INSTR_ORR,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSL,0,NA,1,MAX_64BIT},
+     {0xA025,INSTR_ORR,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,SHIFT_LSL,31,NA,1,0xFFFFFFFF80000001},
+     {0xA026,INSTR_ORR,{0,0,0,0,0},AL,0,1,NA,0,0,3,SHIFT_LSR,1,NA,1,1},
+     {0xA027,INSTR_ORR,{0,0,0,0,0},AL,0,0,NA,0,0,MAX_32BIT,SHIFT_LSR,31,NA,1,1},
+     {0xA028,INSTR_ORR,{0,0,0,0,0},AL,0,0,NA,0,0,3,SHIFT_ASR,1,NA,1,1},
+     {0xA029,INSTR_ORR,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_64BIT,SHIFT_ASR,31,NA,1,MAX_64BIT},
+     {0xA030,INSTR_CMP,{0,0,0,0,0},AL,1,0x10000,NA,1,0x10000,0,0,0,NA,0,0},
+     {0xA031,INSTR_MUL,{0,0,0,0,0},AL,0,0,0x10000,0,0,0x10000,0,0,NA,1,0},
+     {0xA032,INSTR_MUL,{0,0,0,0,0},AL,0,0,0x1000,0,0,0x10000,0,0,NA,1,0x10000000},
+     {0xA033,INSTR_MUL,{0,0,0,0,0},AL,0,0,MAX_32BIT,0,0,1,0,0,NA,1,MAX_64BIT},
+     {0xA034,INSTR_MLA,{0,0,0,0,0},AL,0,0x10000,0x10000,0,0,0x10000,0,0,NA,1,0x10000},
+     {0xA035,INSTR_MLA,{0,0,0,0,0},AL,0,0x10000,0x1000,0,0,0x10000,0,0,NA,1,0x10010000},
+     {0xA036,INSTR_SUB,{1,R_v1,R_a6,2,4},MI,0,2,NA,0,NA,1,NA,NA,2,1,1},
+     {0xA037,INSTR_SUB,{2,R_v1,R_a6,2,0},MI,0,2,NA,0,NA,1,NA,NA,2,1,2},
+     {0xA038,INSTR_SUB,{1,R_v1,R_a6,4,2},GE,0,2,NA,1,1,NA,NA,NA,2,1,1},
+     {0xA039,INSTR_SUB,{1,R_a5,R_a6,2,7},GE,0,2,NA,1,1,NA,NA,NA,2,1,2},
+     {0xA040,INSTR_SUB,{1,R_a5,R_a6,1,1},HS,0,2,NA,1,1,NA,NA,NA,2,1,1},
+     {0xA041,INSTR_SUB,{1,R_a5,R_a6,0,1},HS,0,2,NA,1,1,NA,NA,NA,2,1,2},
+     {0xA042,INSTR_SUB,{0,0,0,0,0},AL,0,1,NA,1,1<< 16,0,0,0,NA,1,(uint64_t)(1 -(1<<16))},
+     {0xA043,INSTR_SUB,{0,0,0,0,0},AL,0,MAX_32BIT,NA,1,1,0,0,0,NA,1,MAX_64BIT-1},
+     {0xA044,INSTR_SUB,{0,0,0,0,0},AL,0,1,NA,1,1,0,0,0,NA,1,0},
+     {0xA045,INSTR_SUB,{0,0,0,0,0},AL,0,1,NA,0,NA,1<<16,0,0,NA,1,(uint64_t)(1 -(1<<16))},
+     {0xA046,INSTR_SUB,{0,0,0,0,0},AL,0,MAX_32BIT,NA,0,NA,1,0,0,NA,1,MAX_64BIT-1},
+     {0xA047,INSTR_SUB,{0,0,0,0,0},AL,0,1,NA,0,NA,1,0,0,NA,1,0},
+     {0xA048,INSTR_SUB,{0,0,0,0,0},AL,0,1,NA,0,NA,1,SHIFT_LSL,16,NA,1,(uint64_t)(1 -(1<<16))},
+     {0xA049,INSTR_SUB,{0,0,0,0,0},AL,0,0x80000001,NA,0,NA,MAX_32BIT,SHIFT_LSL,31,NA,1,1},
+     {0xA050,INSTR_SUB,{0,0,0,0,0},AL,0,1,NA,0,NA,3,SHIFT_LSR,1,NA,1,0},
+     {0xA051,INSTR_SUB,{0,0,0,0,0},AL,0,1,NA,0,NA,MAX_32BIT,SHIFT_LSR,31,NA,1,0},
+     {0xA052,INSTR_RSB,{1,R_a5,R_a6,4,1},GE,0,2,NA,1,0,NA,NA,NA,2,1,(uint64_t)-2},
+     {0xA053,INSTR_RSB,{1,R_a5,R_a6,-1,1},GE,0,2,NA,1,0,NA,NA,NA,2,1,2},
+     {0xA054,INSTR_RSB,{0,0,0,0,0},AL,0,1,NA,1,1<<16,NA,NA,NA,NA,1,(1<<16)-1},
+     {0xA055,INSTR_RSB,{0,0,0,0,0},AL,0,MAX_32BIT,NA,1,1,NA,NA,NA,NA,1,(uint64_t)(1-MAX_64BIT)},
+     {0xA056,INSTR_RSB,{0,0,0,0,0},AL,0,1,NA,1,1,NA,NA,NA,NA,1,0},
+     {0xA057,INSTR_RSB,{0,0,0,0,0},AL,0,1,NA,0,NA,1<<16,0,0,NA,1,(1<<16)-1},
+     {0xA058,INSTR_RSB,{0,0,0,0,0},AL,0,MAX_32BIT,NA,0,NA,1,0,0,NA,1,(uint64_t)(1-MAX_64BIT)},
+     {0xA059,INSTR_RSB,{0,0,0,0,0},AL,0,1,NA,0,NA,1,0,0,NA,1,0},
+     {0xA060,INSTR_RSB,{0,0,0,0,0},AL,0,1,NA,0,NA,1,SHIFT_LSL,16,NA,1,(1<<16)-1},
+     {0xA061,INSTR_RSB,{0,0,0,0,0},AL,0,0x80000001,NA,0,NA,MAX_32BIT ,SHIFT_LSL,31,NA,1,(uint64_t)(-1)},
+     {0xA062,INSTR_RSB,{0,0,0,0,0},AL,0,1,NA,0,NA,3,SHIFT_LSR,1,NA,1,0},
+     {0xA063,INSTR_RSB,{0,0,0,0,0},AL,0,1,NA,0,NA,MAX_32BIT,SHIFT_LSR,31,NA,1,0},
+     {0xA064,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,1,0x80000001,NA,NA,NA,NA,1,0xFFFFFFFF80000001},
+     {0xA065,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,0x80000001,0,0,NA,1,0xFFFFFFFF80000001},
+     {0xA066,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,1,NA,1,MAX_64BIT-1},
+     {0xA067,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,31,NA,1,0xFFFFFFFF80000000},
+     {0xA068,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,3,SHIFT_LSR,1,NA,1,1},
+     {0xA069,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSR,31,NA,1,1},
+     {0xA070,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,3,SHIFT_ASR,1,NA,1,1},
+     {0xA071,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,MAX_64BIT ,SHIFT_ASR,31,NA,1,MAX_64BIT},
+     {0xA072,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,3,SHIFT_ROR,1,NA,1,0xFFFFFFFF80000001},
+     {0xA073,INSTR_MOV,{0,0,0,0,0},AL,0,NA,NA,0,0,0x80000001,SHIFT_ROR,31,NA,1,3},
+     {0xA074,INSTR_MOV,{0,0,0,0,0},AL,1,NA,NA,0,0,MAX_64BIT -1,SHIFT_ASR,1,NA,1,MAX_64BIT},
+     {0xA075,INSTR_MOV,{0,0,0,0,0},AL,1,NA,NA,0,0,3,SHIFT_ASR,1,NA,1,1},
+     {0xA076,INSTR_MOV,{2,R_a5,R_a6,6,8},MI,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2},
+     {0xA077,INSTR_MOV,{2,R_a5,R_a6,-4,-8},MI,0,NA,NA,0,0,0x80000001,0,0,2,1,0xFFFFFFFF80000001},
+     {0xA078,INSTR_MOV,{1,R_a5,R_a6,-1,-1},LT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2},
+     {0xA079,INSTR_MOV,{1,R_a5,R_a6,-1,1},LT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,0xFFFFFFFF80000001},
+     {0xA080,INSTR_MOV,{1,R_a5,R_a6,-1,-5},GE,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,1,2,1,MAX_64BIT-1},
+     {0xA081,INSTR_MOV,{1,R_a5,R_a6,5,5},GE,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,31,2,1,0xFFFFFFFF80000000},
+     {0xA082,INSTR_MOV,{1,R_a5,R_a6,-1,1},GE,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,31,2,1,2},
+     {0xA083,INSTR_MOV,{1,R_a5,R_a6,4,1},LE,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,1,2,1,2},
+     {0xA084,INSTR_MOV,{1,R_a5,R_a6,-1,-1},LE,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,0xFFFFFFFF80000001},
+     {0xA085,INSTR_MOV,{1,R_a5,R_a6,-1,1},LE,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,31,2,1,0xFFFFFFFF80000000},
+     {0xA086,INSTR_MOV,{1,R_a5,R_a6,1,1},GT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2},
+     {0xA087,INSTR_MOV,{1,R_a5,R_a6,-1,-3},GT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,0xFFFFFFFF80000001},
+     {0xA088,INSTR_MOV,{1,R_a5,R_a6,-1,0},GT,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2},
+     {0xA089,INSTR_MOV,{1,R_a5,R_a6,-1,-1},GT,0,NA,NA,0,0,0x80000001,0,0,2,1,2},
+     {0xA090,INSTR_MOV,{1,R_a5,R_a6,6,1},GT,0,NA,NA,0,0,0x80000001,0,0,2,1,0xFFFFFFFF80000001},
+     {0xA091,INSTR_MOV,{1,R_a5,R_a6,-1,1},GT,0,NA,NA,0,0,0x80000001,0,0,2,1,2},
+     {0xA092,INSTR_MOV,{1,R_a5,R_a6,1,1},GT,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,1,2,1,2},
+     {0xA093,INSTR_MOV,{1,R_a5,R_a6,4,1},GT,0,NA,NA,0,0,MAX_32BIT,SHIFT_LSL,1,2,1,MAX_64BIT-1},
+     {0xA094,INSTR_MOV,{1,R_a5,R_a6,-1,1},GT,0,NA,NA,0,0,MAX_32BIT ,SHIFT_LSL,1,2,1,2},
+     {0xA095,INSTR_MOV,{1,R_a5,R_a6,1,-1},HS,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,2},
+     {0xA096,INSTR_MOV,{1,R_a5,R_a6,-1,1},HS,0,NA,NA,1,0x80000001,NA,NA,NA,2,1,0xFFFFFFFF80000001},
+     {0xA097,INSTR_MVN,{1,R_a5,R_a6,1,4},HS,0,NA,NA,1,MAX_32BIT-1,NA,NA,NA,2,1,2},
+     {0xA098,INSTR_MVN,{1,R_a5,R_a6,-1,1},HS,0,NA,NA,1,MAX_32BIT-1,NA,NA,NA,2,1,1},
+     {0xA099,INSTR_MVN,{0,0,0,0,0},AL,0,NA,NA,1,0,NA,NA,NA,2,1,MAX_64BIT},
+     {0xA100,INSTR_MVN,{0,0,0,0,0},AL,0,NA,NA,0,NA,MAX_32BIT-1,NA,0,2,1,1},
+     {0xA101,INSTR_MVN,{0,0,0,0,0},AL,0,NA,NA,0,NA,0x80000001,NA,0,2,1,0x7FFFFFFE},
+     {0xA102,INSTR_BIC,{0,0,0,0,0},AL,0,1,NA,1,MAX_32BIT,NA,NA,NA,NA,1,0},
+     {0xA103,INSTR_BIC,{0,0,0,0,0},AL,0,1,NA,1,MAX_32BIT-1,NA,NA,NA,NA,1,1},
+     {0xA104,INSTR_BIC,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT,0,0,NA,1,0},
+     {0xA105,INSTR_BIC,{0,0,0,0,0},AL,0,1,NA,0,0,MAX_32BIT-1,0,0,NA,1,1},
+     {0xA106,INSTR_BIC,{0,0,0,0,0},AL,0,0xF0,NA,0,0,3,SHIFT_ASR,1,NA,1,0xF0},
+     {0xA107,INSTR_BIC,{0,0,0,0,0},AL,0,0xF0,NA,0,0,MAX_64BIT,SHIFT_ASR,31,NA,1,0},
+     {0xA108,INSTR_SMULBB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCDFFFF,0,NA,0xFFFFFFFFABCD0001,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
+     {0xA109,INSTR_SMULBB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCD0001,0,NA,0xFFFFFFFFABCD0FFF,NA,NA,NA,1,0x00000FFF},
+     {0xA110,INSTR_SMULBB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCD0001,0,NA,0xFFFFFFFFABCDFFFF,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
+     {0xA111,INSTR_SMULBB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCDFFFF,0,NA,0xFFFFFFFFABCDFFFF,NA,NA,NA,1,1},
+     {0xA112,INSTR_SMULBT,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFFFFFABCD,0,NA,0xFFFFFFFFABCD0001,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
+     {0xA113,INSTR_SMULBT,{0,0,0,0,0},AL,0,NA,0x000000000001ABCD,0,NA,0xFFFFFFFFABCD0FFF,NA,NA,NA,1,0x00000FFF},
+     {0xA114,INSTR_SMULBT,{0,0,0,0,0},AL,0,NA,0x000000000001ABCD,0,NA,0xFFFFFFFFABCDFFFF,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
+     {0xA115,INSTR_SMULBT,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFFFFFABCD,0,NA,0xFFFFFFFFABCDFFFF,NA,NA,NA,1,1},
+     {0xA116,INSTR_SMULTB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCDFFFF,0,NA,0x000000000001ABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
+     {0xA117,INSTR_SMULTB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCD0001,0,NA,0x000000000FFFABCD,NA,NA,NA,1,0x00000FFF},
+     {0xA118,INSTR_SMULTB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCD0001,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
+     {0xA119,INSTR_SMULTB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCDFFFF,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,1},
+     {0xA120,INSTR_SMULTT,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFFFFFABCD,0,NA,0x000000000001ABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
+     {0xA121,INSTR_SMULTT,{0,0,0,0,0},AL,0,NA,0x000000000001ABCD,0,NA,0x000000000FFFABCD,NA,NA,NA,1,0x00000FFF},
+     {0xA122,INSTR_SMULTT,{0,0,0,0,0},AL,0,NA,0x000000000001ABCD,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
+     {0xA123,INSTR_SMULTT,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFFFFFABCD,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,1},
+     {0xA124,INSTR_SMULWB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCDFFFF,0,NA,0x000000000001ABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFE},
+     {0xA125,INSTR_SMULWB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCD0001,0,NA,0x000000000FFFABCD,NA,NA,NA,1,0x00000FFF},
+     {0xA126,INSTR_SMULWB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCD0001,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
+     {0xA127,INSTR_SMULWB,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFABCDFFFF,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,0},
+     {0xA128,INSTR_SMULWT,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFFFFFABCD,0,NA,0x000000000001ABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFE},
+     {0xA129,INSTR_SMULWT,{0,0,0,0,0},AL,0,NA,0x000000000001ABCD,0,NA,0x000000000FFFABCD,NA,NA,NA,1,0x00000FFF},
+     {0xA130,INSTR_SMULWT,{0,0,0,0,0},AL,0,NA,0x000000000001ABCD,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,0xFFFFFFFFFFFFFFFF},
+     {0xA131,INSTR_SMULWT,{0,0,0,0,0},AL,0,NA,0xFFFFFFFFFFFFABCD,0,NA,0xFFFFFFFFFFFFABCD,NA,NA,NA,1,0},
+     {0xA132,INSTR_SMLABB,{0,0,0,0,0},AL,0,1,0xFFFFFFFFABCDFFFF,0,NA,0xFFFFFFFFABCD0001,NA,NA,NA,1,0},
+     {0xA133,INSTR_SMLABB,{0,0,0,0,0},AL,0,1,0xFFFFFFFFABCD0001,0,NA,0xFFFFFFFFABCD0FFF,NA,NA,NA,1,0x00001000},
+     {0xA134,INSTR_SMLABB,{0,0,0,0,0},AL,0,0xFFFFFFFFFFFFFFFF,0xFFFFFFFFABCD0001,0,NA,0xABCDFFFF,NA,NA,NA,1,0xFFFFFFFFFFFFFFFE},
+     {0xA135,INSTR_SMLABB,{0,0,0,0,0},AL,0,0xFFFFFFFFFFFFFFFF,0xFFFFFFFFABCDFFFF,0,NA,0xABCDFFFF,NA,NA,NA,1,0},
+     {0xA136,INSTR_UXTB16,{0,0,0,0,0},AL,0,NA,NA,0,NA,0xABCDEF01,SHIFT_ROR,0,NA,1,0x00CD0001},
+     {0xA137,INSTR_UXTB16,{0,0,0,0,0},AL,0,NA,NA,0,NA,0xABCDEF01,SHIFT_ROR,1,NA,1,0x00AB00EF},
+     {0xA138,INSTR_UXTB16,{0,0,0,0,0},AL,0,NA,NA,0,NA,0xABCDEF01,SHIFT_ROR,2,NA,1,0x000100CD},
+     {0xA139,INSTR_UXTB16,{0,0,0,0,0},AL,0,NA,NA,0,NA,0xABCDEF01,SHIFT_ROR,3,NA,1,0x00EF00AB},
+     {0xA140,INSTR_ADDR_ADD,{0,0,0,0,0},AL,0,0xCFFFFFFFF,NA,0,NA,0x1,SHIFT_LSL,1,NA,1,0xD00000001},
+     {0xA141,INSTR_ADDR_ADD,{0,0,0,0,0},AL,0,0x01,NA,0,NA,0x1,SHIFT_LSL,2,NA,1,0x5},
+     {0xA142,INSTR_ADDR_ADD,{0,0,0,0,0},AL,0,0xCFFFFFFFF,NA,0,NA,0x1,NA,0,NA,1,0xD00000000},
+     {0xA143,INSTR_ADDR_SUB,{0,0,0,0,0},AL,0,0xD00000001,NA,0,NA,0x010000,SHIFT_LSR,15,NA,1,0xCFFFFFFFF},
+     {0xA144,INSTR_ADDR_SUB,{0,0,0,0,0},AL,0,0xCFFFFFFFF,NA,0,NA,0x020000,SHIFT_LSR,15,NA,1,0xCFFFFFFFB},
+     {0xA145,INSTR_ADDR_SUB,{0,0,0,0,0},AL,0,3,NA,0,NA,0x010000,SHIFT_LSR,15,NA,1,1},
+};
+
+dataTransferTest_t dataTransferTests [] =
+{
+    {0xB000,INSTR_LDR,AL,AL,1,24,0xABCDEF0123456789,0,REG_SCALE_OFFSET,24,NA,NA,NA,NA,NA,0x23456789,0,0,NA,NA,NA},
+    {0xB001,INSTR_LDR,AL,AL,1,0,0xABCDEF0123456789,0,IMM12_OFFSET,NA,4,1,0,1,NA,0x23456789,4,0,NA,NA,NA},
+    {0xB002,INSTR_LDR,AL,AL,1,0,0xABCDEF0123456789,0,NO_OFFSET,NA,NA,0,0,0,NA,0x23456789,0,0,NA,NA,NA},
+    {0xB003,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,0,REG_SCALE_OFFSET,4064,NA,NA,NA,NA,NA,0x89,0,0,NA,NA,NA},
+    {0xB004,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,4065,IMM12_OFFSET,NA,0,0,1,0,NA,0x67,4065,0,NA,NA,NA},
+    {0xB005,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,4065,IMM12_OFFSET,NA,1,0,1,0,NA,0x45,4065,0,NA,NA,NA},
+    {0xB006,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,4065,IMM12_OFFSET,NA,2,0,1,0,NA,0x23,4065,0,NA,NA,NA},
+    {0xB007,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,4065,IMM12_OFFSET,NA,1,1,0,1,NA,0x67,4066,0,NA,NA,NA},
+    {0xB008,INSTR_LDRB,AL,AL,1,4064,0xABCDEF0123456789,0,NO_OFFSET,NA,NA,0,0,0,NA,0x89,0,0,NA,NA,NA},
+    {0xB009,INSTR_LDRH,AL,AL,1,0,0xABCDEF0123456789,0,IMM8_OFFSET,NA,2,1,0,1,NA,0x6789,2,0,NA,NA,NA},
+    {0xB010,INSTR_LDRH,AL,AL,1,4064,0xABCDEF0123456789,0,REG_OFFSET,4064,0,0,1,0,NA,0x6789,0,0,NA,NA,NA},
+    {0xB011,INSTR_LDRH,AL,AL,1,4064,0xABCDEF0123456789,0,REG_OFFSET,4066,0,0,1,0,NA,0x2345,0,0,NA,NA,NA},
+    {0xB012,INSTR_LDRH,AL,AL,1,0,0xABCDEF0123456789,0,NO_OFFSET,NA,0,0,0,0,NA,0x6789,0,0,NA,NA,NA},
+    {0xB013,INSTR_LDRH,AL,AL,1,0,0xABCDEF0123456789,2,NO_OFFSET,NA,0,0,0,0,NA,0x2345,2,0,NA,NA,NA},
+    {0xB014,INSTR_STR,AL,AL,1,2,0xDEADBEEFDEADBEEF,4,IMM12_OFFSET,NA,4,1,0,1,0xABCDEF0123456789,0xABCDEF0123456789,8,1,2,8,0xDEAD23456789BEEF},
+    {0xB015,INSTR_STR,AL,AL,1,2,0xDEADBEEFDEADBEEF,4,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4,1,2,8,0xDEAD23456789BEEF},
+    {0xB016,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,IMM12_OFFSET,NA,0,0,1,0,0xABCDEF0123456789,0xABCDEF0123456789,1,1,0,8,0xDEADBEEFDEAD89EF},
+    {0xB017,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,IMM12_OFFSET,NA,1,0,1,0,0xABCDEF0123456789,0xABCDEF0123456789,1,1,0,8,0xDEADBEEFDE89BEEF},
+    {0xB018,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,IMM12_OFFSET,NA,2,0,1,0,0xABCDEF0123456789,0xABCDEF0123456789,1,1,0,8,0xDEADBEEF89ADBEEF},
+    {0xB019,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,IMM12_OFFSET,NA,4,1,0,1,0xABCDEF0123456789,0xABCDEF0123456789,5,1,0,8,0xDEADBEEFDEAD89EF},
+    {0xB020,INSTR_STRB,AL,AL,1,0,0xDEADBEEFDEADBEEF,1,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,1,1,0,8,0xDEADBEEFDEAD89EF},
+    {0xB021,INSTR_STRH,AL,AL,1,4066,0xDEADBEEFDEADBEEF,4070,IMM8_OFFSET,NA,2,1,0,1,0xABCDEF0123456789,0xABCDEF0123456789,4072,1,4066,8,0xDEAD6789DEADBEEF},
+    {0xB022,INSTR_STRH,AL,AL,1,4066,0xDEADBEEFDEADBEEF,4070,NO_OFFSET,NA,NA,0,0,0,0xABCDEF0123456789,0xABCDEF0123456789,4070,1,4066,8,0xDEAD6789DEADBEEF},
+};
+
+
+void flushcache()
+{
+    const long base = long(instrMem);
+    const long curr = base + long(instrMemSize);
+    __builtin___clear_cache((char*)base, (char*)curr);
+}
+
+void dataOpTest(dataOpTest_t test, ArmToMips64Assembler *a64asm, uint32_t Rd = R_v1,
+                uint32_t Rn = R_t0, uint32_t Rm = R_t1, uint32_t Rs = R_t2)
+{
+    int64_t  regs[NUM_REGS] = {0};
+    int32_t  flags[NUM_FLAGS] = {0};
+    int64_t  savedRegs[NUM_REGS] = {0};
+    uint32_t i;
+    uint32_t op2;
+
+    for(i = 0; i < NUM_REGS; ++i)
+    {
+        regs[i] = i;
+    }
+
+    regs[Rd] = test.RdValue;
+    regs[Rn] = test.RnValue;
+    regs[Rs] = test.RsValue;
+    a64asm->reset();
+    if (test.preCond.mode) {
+        a64asm->set_condition(test.preCond.mode, test.preCond.Rcond1, test.preCond.Rcond2);
+        regs[test.preCond.Rcond1] = test.preCond.Rcond1Value;
+        regs[test.preCond.Rcond2] = test.preCond.Rcond2Value;
+    }
+    a64asm->prolog();
+    if(test.immediate == true)
+    {
+        op2 = a64asm->imm(test.immValue);
+    }
+    else if(test.immediate == false && test.shiftAmount == 0)
+    {
+        op2 = Rm;
+        regs[Rm] = (int64_t)((int32_t)(test.RmValue));
+    }
+    else
+    {
+        op2 = a64asm->reg_imm(Rm, test.shiftMode, test.shiftAmount);
+        regs[Rm] = (int64_t)((int32_t)(test.RmValue));
+    }
+    switch(test.op)
+    {
+    case INSTR_ADD: a64asm->ADD(test.cond, test.setFlags, Rd,Rn,op2); break;
+    case INSTR_SUB: a64asm->SUB(test.cond, test.setFlags, Rd,Rn,op2); break;
+    case INSTR_RSB: a64asm->RSB(test.cond, test.setFlags, Rd,Rn,op2); break;
+    case INSTR_AND: a64asm->AND(test.cond, test.setFlags, Rd,Rn,op2); break;
+    case INSTR_ORR: a64asm->ORR(test.cond, test.setFlags, Rd,Rn,op2); break;
+    case INSTR_BIC: a64asm->BIC(test.cond, test.setFlags, Rd,Rn,op2); break;
+    case INSTR_MUL: a64asm->MUL(test.cond, test.setFlags, Rd,Rm,Rs); break;
+    case INSTR_MLA: a64asm->MLA(test.cond, test.setFlags, Rd,Rm,Rs,Rn); break;
+    case INSTR_CMP: a64asm->CMP(test.cond, Rn,op2); break;
+    case INSTR_MOV: a64asm->MOV(test.cond, test.setFlags,Rd,op2); break;
+    case INSTR_MVN: a64asm->MVN(test.cond, test.setFlags,Rd,op2); break;
+    case INSTR_SMULBB:a64asm->SMULBB(test.cond, Rd,Rm,Rs); break;
+    case INSTR_SMULBT:a64asm->SMULBT(test.cond, Rd,Rm,Rs); break;
+    case INSTR_SMULTB:a64asm->SMULTB(test.cond, Rd,Rm,Rs); break;
+    case INSTR_SMULTT:a64asm->SMULTT(test.cond, Rd,Rm,Rs); break;
+    case INSTR_SMULWB:a64asm->SMULWB(test.cond, Rd,Rm,Rs); break;
+    case INSTR_SMULWT:a64asm->SMULWT(test.cond, Rd,Rm,Rs); break;
+    case INSTR_SMLABB:a64asm->SMLABB(test.cond, Rd,Rm,Rs,Rn); break;
+    case INSTR_UXTB16:a64asm->UXTB16(test.cond, Rd,Rm,test.shiftAmount); break;
+    case INSTR_ADDR_ADD: a64asm->ADDR_ADD(test.cond, test.setFlags, Rd,Rn,op2); break;
+    case INSTR_ADDR_SUB: a64asm->ADDR_SUB(test.cond, test.setFlags, Rd,Rn,op2); break;
+    default: printf("Error"); return;
+    }
+    a64asm->epilog(0);
+    a64asm->fix_branches();
+    flushcache();
+
+    asm_function_t asm_function = (asm_function_t)(instrMem);
+
+    for(i = 0; i < NUM_REGS; ++i)
+        savedRegs[i] = regs[i];
+
+    asm_mips_test_jacket(asm_function, regs, flags);
+
+    /* Check if all regs except Rd is same */
+    for(i = 0; i < NUM_REGS; ++i)
+    {
+        if((i == Rd) || i == 2) continue;
+        if(regs[i] != savedRegs[i])
+        {
+            printf("Test %x failed Reg(%d) tampered Expected(0x%" PRIx64 "),"
+                   "Actual(0x%" PRIx64 ") t\n", test.id, i, savedRegs[i],
+                   regs[i]);
+            exit(0);
+            return;
+        }
+    }
+
+    if(test.checkRd == 1 && regs[Rd] != test.postRdValue)
+    {
+        printf("Test %x failed, Expected(%" PRIx64 "), Actual(%" PRIx64 ")\n",
+               test.id, test.postRdValue, regs[Rd]);
+        exit(0);
+    }
+    else
+    {
+        printf("Test %x passed\n", test.id);
+    }
+}
+
+
+void dataTransferTest(dataTransferTest_t test, ARMAssemblerInterface *a64asm,
+                      uint32_t Rd = R_v1, uint32_t Rn = R_t0,uint32_t Rm = R_t1)
+{
+    int64_t regs[NUM_REGS] = {0};
+    int64_t savedRegs[NUM_REGS] = {0};
+    int32_t flags[NUM_FLAGS] = {0};
+    uint32_t i;
+    for(i = 0; i < NUM_REGS; ++i)
+    {
+        regs[i] = i;
+    }
+
+    uint32_t op2;
+
+    regs[Rd] = test.RdValue;
+    regs[Rn] = (uint64_t)(&dataMem[test.RnValue]);
+    regs[Rm] = test.RmValue;
+    flags[test.preFlag] = 1;
+
+    if(test.setMem == true)
+    {
+        unsigned char *mem = (unsigned char *)&dataMem[test.memOffset];
+        uint64_t value = test.memValue;
+        for(int j = 0; j < 8; ++j)
+        {
+            mem[j] = value & 0x00FF;
+            value >>= 8;
+        }
+    }
+    a64asm->reset();
+    a64asm->prolog();
+    if(test.offsetType == REG_SCALE_OFFSET)
+    {
+        op2 = a64asm->reg_scale_pre(Rm);
+    }
+    else if(test.offsetType == REG_OFFSET)
+    {
+        op2 = a64asm->reg_pre(Rm);
+    }
+    else if(test.offsetType == IMM12_OFFSET && test.preIndex == true)
+    {
+        op2 = a64asm->immed12_pre(test.immValue, test.writeBack);
+    }
+    else if(test.offsetType == IMM12_OFFSET && test.postIndex == true)
+    {
+        op2 = a64asm->immed12_post(test.immValue);
+    }
+    else if(test.offsetType == IMM8_OFFSET && test.preIndex == true)
+    {
+        op2 = a64asm->immed8_pre(test.immValue, test.writeBack);
+    }
+    else if(test.offsetType == IMM8_OFFSET && test.postIndex == true)
+    {
+        op2 = a64asm->immed8_post(test.immValue);
+    }
+    else if(test.offsetType == NO_OFFSET)
+    {
+        op2 = a64asm->__immed12_pre(0);
+    }
+    else
+    {
+        printf("Error - Unknown offset\n"); return;
+    }
+
+    switch(test.op)
+    {
+    case INSTR_LDR:  a64asm->LDR(test.cond, Rd,Rn,op2); break;
+    case INSTR_LDRB: a64asm->LDRB(test.cond, Rd,Rn,op2); break;
+    case INSTR_LDRH: a64asm->LDRH(test.cond, Rd,Rn,op2); break;
+    case INSTR_ADDR_LDR: a64asm->ADDR_LDR(test.cond, Rd,Rn,op2); break;
+    case INSTR_STR:  a64asm->STR(test.cond, Rd,Rn,op2); break;
+    case INSTR_STRB: a64asm->STRB(test.cond, Rd,Rn,op2); break;
+    case INSTR_STRH: a64asm->STRH(test.cond, Rd,Rn,op2); break;
+    case INSTR_ADDR_STR: a64asm->ADDR_STR(test.cond, Rd,Rn,op2); break;
+    default: printf("Error"); return;
+    }
+    a64asm->epilog(0);
+    flushcache();
+
+    asm_function_t asm_function = (asm_function_t)(instrMem);
+
+    for(i = 0; i < NUM_REGS; ++i)
+        savedRegs[i] = regs[i];
+
+    asm_mips_test_jacket(asm_function, regs, flags);
+
+    /* Check if all regs except Rd/Rn are same */
+    for(i = 0; i < NUM_REGS; ++i)
+    {
+        if(i == Rd || i == Rn || i == R_v0) continue;
+
+        if(regs[i] != savedRegs[i])
+        {
+            printf("Test %x failed Reg(%d) tampered"
+                   " Expected(0x%" PRIx64 "), Actual(0x%" PRIx64 ") t\n",
+                   test.id, i, savedRegs[i], regs[i]);
+            return;
+        }
+    }
+
+    if((uint64_t)regs[Rd] != test.postRdValue)
+    {
+        printf("Test %x failed, "
+               "Expected in Rd(0x%" PRIx64 "), Actual(0x%" PRIx64 ")\n",
+               test.id, test.postRdValue, regs[Rd]);
+    }
+    else if((uint64_t)regs[Rn] != (uint64_t)(&dataMem[test.postRnValue]))
+    {
+        printf("Test %x failed, "
+               "Expected in Rn(0x%" PRIx64 "), Actual(0x%" PRIx64 ")\n",
+               test.id, test.postRnValue, regs[Rn] - (uint64_t)dataMem);
+    }
+    else if(test.checkMem == true)
+    {
+        unsigned char *addr = (unsigned char *)&dataMem[test.postMemOffset];
+        uint64_t value;
+        value = 0;
+        for(uint32_t j = 0; j < test.postMemLength; ++j)
+            value = (value << 8) | addr[test.postMemLength-j-1];
+        if(value != test.postMemValue)
+        {
+            printf("Test %x failed, "
+                   "Expected in Mem(0x%" PRIx64 "), Actual(0x%" PRIx64 ")\n",
+                   test.id, test.postMemValue, value);
+        }
+        else
+        {
+            printf("Test %x passed\n", test.id);
+        }
+    }
+    else
+    {
+        printf("Test %x passed\n", test.id);
+    }
+}
+
+int main(void)
+{
+    uint32_t i;
+
+    /* Allocate memory to store instructions generated by ArmToArm64Assembler */
+    {
+        int fd = ashmem_create_region("code cache", instrMemSize);
+        if(fd < 0) {
+            printf("IF < 0\n");
+            printf("Creating code cache, ashmem_create_region "
+                                "failed with error '%s'", strerror(errno));
+        }
+        instrMem = mmap(NULL, instrMemSize,
+                                    PROT_READ | PROT_WRITE | PROT_EXEC,
+                                MAP_PRIVATE, fd, 0);
+    }
+
+    ArmToMips64Assembler a64asm(instrMem);
+
+    if(TESTS_DATAOP_ENABLE)
+    {
+        printf("Running data processing tests\n");
+        for(i = 0; i < sizeof(dataOpTests)/sizeof(dataOpTest_t); ++i) {
+            dataOpTest(dataOpTests[i], &a64asm);
+        }
+    }
+
+    if(TESTS_DATATRANSFER_ENABLE)
+    {
+        printf("Running data transfer tests\n");
+        for(i = 0; i < sizeof(dataTransferTests)/sizeof(dataTransferTest_t); ++i)
+            dataTransferTest(dataTransferTests[i], &a64asm);
+    }
+
+    return 0;
+}
diff --git a/libpixelflinger/tests/arch-mips64/col32cb16blend/Android.mk b/libpixelflinger/tests/arch-mips64/col32cb16blend/Android.mk
new file mode 100644
index 0000000..7d4177e
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips64/col32cb16blend/Android.mk
@@ -0,0 +1,18 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+    col32cb16blend_test.c \
+    ../../../arch-mips64/col32cb16blend.S
+
+LOCAL_SHARED_LIBRARIES :=
+
+LOCAL_C_INCLUDES :=
+
+LOCAL_MODULE:= test-pixelflinger-mips64-col32cb16blend
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_MULTILIB := 64
+
+include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-mips64/col32cb16blend/col32cb16blend_test.c b/libpixelflinger/tests/arch-mips64/col32cb16blend/col32cb16blend_test.c
new file mode 100644
index 0000000..066eab6
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips64/col32cb16blend/col32cb16blend_test.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+
+#define ARGB_8888_MAX   0xFFFFFFFF
+#define ARGB_8888_MIN   0x00000000
+#define RGB_565_MAX 0xFFFF
+#define RGB_565_MIN 0x0000
+
+struct test_t
+{
+    char name[256];
+    uint32_t src_color;
+    uint16_t dst_color;
+    size_t count;
+};
+
+struct test_t tests[] =
+{
+    {"Count 1, Src=Max, Dst=Min", ARGB_8888_MAX, RGB_565_MIN, 1},
+    {"Count 2, Src=Min, Dst=Max", ARGB_8888_MIN, RGB_565_MAX, 2},
+    {"Count 3, Src=Max, Dst=Max", ARGB_8888_MAX, RGB_565_MAX, 3},
+    {"Count 4, Src=Min, Dst=Min", ARGB_8888_MAX, RGB_565_MAX, 4},
+    {"Count 1, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 1},
+    {"Count 2, Src=Rand, Dst=Rand", 0xABCDEF12, 0x2345, 2},
+    {"Count 3, Src=Rand, Dst=Rand", 0x11111111, 0xEDFE, 3},
+    {"Count 4, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 4},
+    {"Count 5, Src=Rand, Dst=Rand", 0xEFEFFEFE, 0xFACC, 5},
+    {"Count 10, Src=Rand, Dst=Rand", 0x12345678, 0x9ABC, 10}
+};
+
+void scanline_col32cb16blend_mips64(uint16_t *dst, uint32_t src, size_t count);
+void scanline_col32cb16blend_c(uint16_t * dst, uint32_t src, size_t count)
+{
+    uint32_t srcAlpha = (src>>24);
+    uint32_t f = 0x100 - (srcAlpha + (srcAlpha>>7));
+
+    while (count--)
+    {
+        uint16_t d = *dst;
+        int dstR = (d>>11)&0x1f;
+        int dstG = (d>>5)&0x3f;
+        int dstB = (d)&0x1f;
+        int srcR = (src >> (   3))&0x1F;
+        int srcG = (src >> ( 8+2))&0x3F;
+        int srcB = (src >> (16+3))&0x1F;
+        srcR += (f*dstR)>>8;
+        srcG += (f*dstG)>>8;
+        srcB += (f*dstB)>>8;
+        *dst++ = (uint16_t)((srcR<<11)|(srcG<<5)|srcB);
+    }
+}
+
+void scanline_col32cb16blend_test()
+{
+    uint16_t dst_c[16], dst_asm[16];
+    uint32_t i, j;
+
+    for(i = 0; i < sizeof(tests)/sizeof(struct test_t); ++i)
+    {
+        struct test_t test = tests[i];
+
+        printf("Testing - %s:",test.name);
+
+        memset(dst_c, 0, sizeof(dst_c));
+        memset(dst_asm, 0, sizeof(dst_asm));
+
+        for(j = 0; j < test.count; ++j)
+        {
+            dst_c[j]   = test.dst_color;
+            dst_asm[j] = test.dst_color;
+        }
+
+
+        scanline_col32cb16blend_c(dst_c, test.src_color, test.count);
+        scanline_col32cb16blend_mips64(dst_asm, test.src_color, test.count);
+
+        if(memcmp(dst_c, dst_asm, sizeof(dst_c)) == 0)
+            printf("Passed\n");
+        else
+            printf("Failed\n");
+
+        for(j = 0; j < test.count; ++j)
+        {
+            printf("dst_c[%d] = %x, dst_asm[%d] = %x \n", j, dst_c[j], j, dst_asm[j]);
+        }
+    }
+}
+
+int main()
+{
+    scanline_col32cb16blend_test();
+    return 0;
+}
diff --git a/libpixelflinger/tests/arch-mips64/disassembler/Android.mk b/libpixelflinger/tests/arch-mips64/disassembler/Android.mk
new file mode 100644
index 0000000..4e72b57
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips64/disassembler/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+    mips64_disassembler_test.cpp \
+    ../../../codeflinger/mips64_disassem.c
+
+LOCAL_SHARED_LIBRARIES :=
+
+LOCAL_MODULE:= test-pixelflinger-mips64-disassembler-test
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_MULTILIB := 64
+
+include $(BUILD_NATIVE_TEST)
diff --git a/libpixelflinger/tests/arch-mips64/disassembler/mips64_disassembler_test.cpp b/libpixelflinger/tests/arch-mips64/disassembler/mips64_disassembler_test.cpp
new file mode 100644
index 0000000..41f6f3e
--- /dev/null
+++ b/libpixelflinger/tests/arch-mips64/disassembler/mips64_disassembler_test.cpp
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include <stdio.h>
+#include <inttypes.h>
+#include <string.h>
+#include "../../../codeflinger/mips64_disassem.h"
+
+//typedef uint64_t db_addr_t;
+//db_addr_t mips_disassem(db_addr_t loc, char *di_buffer, int alt_format);
+
+struct test_table_entry_t
+{
+     uint32_t code;
+     const char *instr;
+};
+
+static test_table_entry_t test_table [] =
+{
+    { 0x00011020, "add\tv0,zero,at"     },
+    { 0x00832820, "add\ta1,a0,v1"       },
+    { 0x00c74020, "add\ta4,a2,a3"       },
+    { 0x012a5820, "add\ta7,a5,a6"       },
+    { 0x258dffff, "addiu\tt1,t0,-1"     },
+    { 0x25cf0004, "addiu\tt3,t2,4"      },
+    { 0x02119021, "addu\ts2,s0,s1"      },
+    { 0x0274a821, "addu\ts5,s3,s4"      },
+    { 0x02d7c024, "and\tt8,s6,s7"       },
+    { 0x333aff00, "andi\tk0,t9,0xff00"  },
+    { 0x3f7cffff, "aui\tgp,k1,-1"       },
+    { 0x3c1dffff, "lui\tsp,0xffff"      },
+    { 0x00e04051, "clo\ta4,a3"          },
+    { 0x01205050, "clz\ta6,a5"          },
+    { 0x016c682c, "dadd\tt1,a7,t0"      },
+    { 0x65cf0008, "daddiu\tt3,t2,8"     },
+    { 0x0211902d, "daddu\ts2,s0,s1"     },
+    { 0x7e741403, "dext\ts4,s3,16,3"    },
+    { 0x7eb6f801, "dextm\ts6,s5,0,64"   },
+    { 0x7ef87c02, "dextu\tt8,s7,48,16"  },
+    { 0x7f3a8207, "dins\tk0,t9,8,9"     },
+    { 0x7f7c0005, "dinsm\tgp,k1,0,33"   },
+    { 0x7fbe0806, "dinsu\ts8,sp,32,2"   },
+    { 0x03e1102e, "dsub\tv0,ra,at"      },
+    { 0x0064282f, "dsubu\ta1,v1,a0"     },
+    { 0x7cc77a00, "ext\ta3,a2,8,16"     },
+    { 0x7d09fc04, "ins\ta5,a4,16,16"    },
+    { 0x00200009, "jr\tat"              },
+    { 0x00201009, "jalr\tv0,at"         },
+    { 0x0020f809, "jalr\tat"            },
+    { 0x8082fff0, "lb\tv0,-16(a0)"      },
+    { 0x916c0008, "lbu\tt0,8(a7)"       },
+    { 0xdfa3ffe8, "ld\tv1,-24(sp)"      },
+    { 0x84850080, "lh\ta1,128(a0)"      },
+    { 0x94c7ff80, "lhu\ta3,-128(a2)"    },
+    { 0x8d09000c, "lw\ta5,12(a4)"       },
+    { 0x9d4bfff4, "lwu\ta7,-12(a6)"     },
+    { 0x00620898, "mul\tat,v1,v0"       },
+    { 0x006208d8, "muh\tat,v1,v0"       },
+    { 0x00620899, "mulu\tat,v1,v0"      },
+    { 0x006208d9, "muhu\tat,v1,v0"      },
+    { 0x00000000, "nop"                 },
+    { 0x02329827, "nor\ts3,s1,s2"       },
+    { 0x0295b025, "or\ts6,s4,s5"        },
+    { 0x36f0ff00, "ori\ts0,s7,0xff00"   },
+    { 0x7c03103b, "rdhwr\tv0,v1"        },
+    { 0x00242a02, "rotr\ta1,a0,8"       },
+    { 0x00c74046, "rotrv\ta4,a3,a2"     },
+    { 0xa12afff0, "sb\ta6,-16(a5)"      },
+    { 0xfd6c0100, "sd\tt0,256(a7)"      },
+    { 0x7c0d7420, "seb\tt2,t1"          },
+    { 0x7c0f8620, "seh\ts0,t3"          },
+    { 0x02329835, "seleqz\ts3,s1,s2"    },
+    { 0x0295b037, "selnez\ts6,s4,s5"    },
+    { 0xa6f84000, "sh\tt8,16384(s7)"    },
+    { 0x0019d100, "sll\tk0,t9,4"        },
+    { 0x037ce804, "sllv\tsp,gp,k1"      },
+    { 0x03df082a, "slt\tat,s8,ra"       },
+    { 0x28430007, "slti\tv1,v0,7"       },
+    { 0x2c850020, "sltiu\ta1,a0,32"     },
+    { 0x00c7402b, "sltu\ta4,a2,a3"      },
+    { 0x00095103, "sra\ta6,a5,4"        },
+    { 0x016c6807, "srav\tt1,t0,a7"      },
+    { 0x000e7a02, "srl\tt3,t2,8"        },
+    { 0x02119006, "srlv\ts2,s1,s0"      },
+    { 0x0274a822, "sub\ts5,s3,s4"       },
+    { 0x02d7c023, "subu\tt8,s6,s7"      },
+    { 0xaf3afffc, "sw\tk0,-4(t9)"       },
+    { 0x7c1be0a0, "wsbh\tgp,k1"         },
+    { 0x03bef826, "xor\tra,sp,s8"       },
+    { 0x3801ffff, "li\tat,0xffff"       },
+    { 0x3843ffff, "xori\tv1,v0,0xffff"  },
+};
+
+struct test_branches_table_entry_t
+{
+     uint32_t code;
+     const char *instr;
+     int16_t offset;
+};
+
+static test_branches_table_entry_t test_branches_table [] = {
+    { 0x1000ffff, "b\t", 0xffff             },
+    { 0x13df0008, "beq\ts8,ra,", 0x8        },
+    { 0x042100ff, "bgez\tat,", 0xff         },
+    { 0x1c40ff00, "bgtz\tv0,", 0xff00       },
+    { 0x18605555, "blez\tv1,", 0x5555       },
+    { 0x0480aaaa, "bltz\ta0,", 0xaaaa       },
+    { 0x14a68888, "bne\ta1,a2,", 0x8888     },
+};
+
+struct test_jump_table_entry_t
+{
+     uint32_t code;
+     const char *instr;
+     int32_t offset;
+};
+
+static test_jump_table_entry_t test_jump_table [] = {
+    { 0x0956ae66, "j\t", 0x156ae66          },
+    { 0x0d56ae66, "jal\t", 0x156ae66        },
+};
+
+int main()
+{
+    char instr[256];
+    uint32_t failed = 0;
+
+    for(uint32_t i = 0; i < sizeof(test_table)/sizeof(test_table_entry_t); ++i)
+    {
+        test_table_entry_t *test;
+        test = &test_table[i];
+        mips_disassem(&test->code, instr, 0);
+        if(strcmp(instr, test->instr) != 0)
+        {
+            printf("Test Failed \n"
+                   "Code     : 0x%0x\n"
+                   "Expected : %s\n"
+                   "Actual   : %s\n", test->code, test->instr, instr);
+            failed++;
+        }
+    }
+    for(uint32_t i = 0; i < sizeof(test_branches_table)/sizeof(test_branches_table_entry_t); ++i)
+    {
+        test_branches_table_entry_t *test;
+        test = &test_branches_table[i];
+        mips_disassem(&test->code, instr, 0);
+        //printf("DBG code address: %lx\n", (uint64_t)(&test->code));
+        uint64_t loc = (uint64_t)test + 4 + (test->offset << 2);
+        //printf("DBG loc: %lx\n", loc);
+        char temp[256], address[16];
+        strcpy(temp, test->instr);
+        sprintf(address, "0x%lx", loc);
+        strcat(temp, address);
+        if(strcmp(instr, temp) != 0)
+        {
+            printf("Test Failed \n"
+                   "Code     : 0x%0x\n"
+                   "Expected : %s\n"
+                   "Actual   : %s\n", test->code, temp, instr);
+            failed++;
+        }
+    }
+    for(uint32_t i = 0; i < sizeof(test_jump_table)/sizeof(test_jump_table_entry_t); ++i)
+    {
+        test_jump_table_entry_t *test;
+        test = &test_jump_table[i];
+        mips_disassem(&test->code, instr, 0);
+        //printf("DBG code address: %lx\n", (uint64_t)(&test->code));
+        uint64_t loc = ((uint64_t)test & 0xfffffffff0000000) | (test->offset << 2);
+        //printf("DBG loc: %lx\n", loc);
+        char temp[256], address[16];
+        strcpy(temp, test->instr);
+        sprintf(address, "0x%08lx", loc);
+        strcat(temp, address);
+        if(strcmp(instr, temp) != 0)
+        {
+            printf("Test Failed \n"
+                   "Code     : 0x%0x\n"
+                   "Expected : '%s'\n"
+                   "Actual   : '%s'\n", test->code, temp, instr);
+            failed++;
+        }
+    }
+    if(failed == 0)
+    {
+        printf("All tests PASSED\n");
+        return 0;
+    }
+    else
+    {
+        printf("%d tests FAILED\n", failed);
+        return -1;
+    }
+}
diff --git a/libpixelflinger/tests/codegen/codegen.cpp b/libpixelflinger/tests/codegen/codegen.cpp
index 148b6f4..efa6d87 100644
--- a/libpixelflinger/tests/codegen/codegen.cpp
+++ b/libpixelflinger/tests/codegen/codegen.cpp
@@ -11,16 +11,18 @@
 #include "codeflinger/ARMAssembler.h"
 #if defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6
 #include "codeflinger/MIPSAssembler.h"
+#elif defined(__mips__) && defined(__LP64__) && __mips_isa_rev == 6
+#include "codeflinger/MIPS64Assembler.h"
 #endif
 #include "codeflinger/Arm64Assembler.h"
 
-#if defined(__arm__) || (defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6) || defined(__aarch64__)
+#if defined(__arm__) || (defined(__mips__) && ((!defined(__LP64__) && __mips_isa_rev < 6) || (defined(__LP64__) && __mips_isa_rev == 6))) || defined(__aarch64__)
 #   define ANDROID_ARM_CODEGEN  1
 #else
 #   define ANDROID_ARM_CODEGEN  0
 #endif
 
-#if defined(__mips__) && !defined(__LP64__) && __mips_isa_rev < 6
+#if defined(__mips__) && ((!defined(__LP64__) && __mips_isa_rev < 6) || (defined(__LP64__) && __mips_isa_rev == 6))
 #define ASSEMBLY_SCRATCH_SIZE   4096
 #elif defined(__aarch64__)
 #define ASSEMBLY_SCRATCH_SIZE   8192
@@ -58,6 +60,10 @@
     GGLAssembler assembler( new ArmToMipsAssembler(a) );
 #endif
 
+#if defined(__mips__) && defined(__LP64__) && __mips_isa_rev == 6
+    GGLAssembler assembler( new ArmToMips64Assembler(a) );
+#endif
+
 #if defined(__aarch64__)
     GGLAssembler assembler( new ArmToArm64Assembler(a) );
 #endif
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/libsync/tests/sync_test.cpp b/libsync/tests/sync_test.cpp
index 55cd687..2c409dc 100644
--- a/libsync/tests/sync_test.cpp
+++ b/libsync/tests/sync_test.cpp
@@ -50,7 +50,7 @@
     bool isValid() const {
         if (m_fdInitialized) {
             int status = fcntl(m_fd, F_GETFD, 0);
-            if (status == 0)
+            if (status >= 0)
                 return true;
             else
                 return false;
@@ -92,7 +92,7 @@
     bool isValid() const {
         if (m_fdInitialized) {
             int status = fcntl(m_fd, F_GETFD, 0);
-            if (status == 0)
+            if (status >= 0)
                 return true;
             else
                 return false;
diff --git a/libutils/CallStack.cpp b/libutils/CallStack.cpp
index 0bfb520..699da74 100644
--- a/libutils/CallStack.cpp
+++ b/libutils/CallStack.cpp
@@ -16,11 +16,12 @@
 
 #define LOG_TAG "CallStack"
 
+#include <memory>
+
 #include <utils/CallStack.h>
 #include <utils/Printer.h>
 #include <utils/Errors.h>
 #include <utils/Log.h>
-#include <UniquePtr.h>
 
 #include <backtrace/Backtrace.h>
 
@@ -40,7 +41,7 @@
 void CallStack::update(int32_t ignoreDepth, pid_t tid) {
     mFrameLines.clear();
 
-    UniquePtr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid));
+    std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid));
     if (!backtrace->Unwind(ignoreDepth)) {
         ALOGW("%s: Failed to unwind callstack.", __FUNCTION__);
     }
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/tests/Android.mk b/libutils/tests/Android.mk
index 47415ab..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 \
 
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/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..f117cc5 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,152 @@
   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 write_bytes = z_stream_->next_out - buffer_.data();
+      if (fwrite(buffer_.data(), 1, write_bytes, file_) != write_bytes) {
+        return HandleError(kIoError);
+      }
+      file->compressed_size += write_bytes;
+      current_offset_ += write_bytes;
+
+      // 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);
+
+  // Keep deflating while there isn't enough space in the buffer to
+  // to complete the compress.
+  int zerr;
+  while ((zerr = deflate(z_stream_.get(), Z_FINISH)) == Z_OK) {
+    assert(z_stream_->avail_out == 0);
+    size_t write_bytes = z_stream_->next_out - buffer_.data();
+    if (fwrite(buffer_.data(), 1, write_bytes, file_) != write_bytes) {
+      return HandleError(kIoError);
+    }
+    file->compressed_size += write_bytes;
+    current_offset_ += write_bytes;
+
+    z_stream_->next_out = buffer_.data();
+    z_stream_->avail_out = buffer_.size();
+  }
+  if (zerr != Z_STREAM_END) {
+    return HandleError(kZlibError);
+  }
+
+  size_t write_bytes = z_stream_->next_out - buffer_.data();
+  if (write_bytes != 0) {
+    if (fwrite(buffer_.data(), 1, write_bytes, file_) != write_bytes) {
+      return HandleError(kIoError);
+    }
+    file->compressed_size += write_bytes;
+    current_offset_ += write_bytes;
+  }
+  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 +382,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..f752b7e 100644
--- a/libziparchive/zip_writer_test.cc
+++ b/libziparchive/zip_writer_test.cc
@@ -20,6 +20,7 @@
 #include <base/test_utils.h>
 #include <gtest/gtest.h>
 #include <memory>
+#include <vector>
 
 struct zipwriter : public ::testing::Test {
   TemporaryFile* temp_file_;
@@ -44,26 +45,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 +75,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 +125,84 @@
 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);
+}
+
+TEST_F(zipwriter, WriteCompressedZipFlushFull) {
+  // This exact data will cause the Finish() to require multiple calls
+  // to deflate() because the ZipWriter buffer isn't big enough to hold
+  // the entire compressed data buffer.
+  constexpr size_t kBufSize = 10000000;
+  std::vector<uint8_t> buffer(kBufSize);
+  size_t prev = 1;
+  for (size_t i = 0; i < kBufSize; i++) {
+    buffer[i] = i + prev;
+    prev = i;
+  }
+
+  ZipWriter writer(file_);
+  ASSERT_EQ(0, writer.StartEntry("file.txt", ZipWriter::kCompress));
+  ASSERT_EQ(0, writer.WriteBytes(buffer.data(), buffer.size()));
+  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(kBufSize, data.uncompressed_length);
+
+  std::vector<uint8_t> decompress(kBufSize);
+  memset(decompress.data(), 0, kBufSize);
+  ASSERT_EQ(0, ExtractToMemory(handle, &data, decompress.data(), decompress.size()));
+  EXPECT_EQ(0, memcmp(decompress.data(), buffer.data(), kBufSize))
+      << "Input buffer and output buffer are different.";
+
+  CloseArchive(handle);
 }
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index 7bbc811..cb9598e 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -77,12 +77,7 @@
 static int epollfd;
 static int maxevents;
 
-#define OOM_DISABLE (-17)
-/* inclusive */
-#define OOM_ADJUST_MIN (-16)
-#define OOM_ADJUST_MAX 15
-
-/* kernel OOM score values */
+/* OOM score values used by both kernel and framework */
 #define OOM_SCORE_ADJ_MIN       (-1000)
 #define OOM_SCORE_ADJ_MAX       1000
 
@@ -114,8 +109,8 @@
 static struct proc *pidhash[PIDHASH_SZ];
 #define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1))
 
-#define ADJTOSLOT(adj) (adj + -OOM_ADJUST_MIN)
-static struct adjslot_list procadjslot_list[ADJTOSLOT(OOM_ADJUST_MAX) + 1];
+#define ADJTOSLOT(adj) (adj + -OOM_SCORE_ADJ_MIN)
+static struct adjslot_list procadjslot_list[ADJTOSLOT(OOM_SCORE_ADJ_MAX) + 1];
 
 /*
  * Wait 1-2 seconds for the death report of a killed process prior to
@@ -148,14 +143,6 @@
     return ret;
 }
 
-static int lowmem_oom_adj_to_oom_score_adj(int oom_adj)
-{
-    if (oom_adj == OOM_ADJUST_MAX)
-        return OOM_SCORE_ADJ_MAX;
-    else
-        return (oom_adj * OOM_SCORE_ADJ_MAX) / -OOM_DISABLE;
-}
-
 static struct proc *pid_lookup(int pid) {
     struct proc *procp;
 
@@ -254,13 +241,13 @@
     char path[80];
     char val[20];
 
-    if (oomadj < OOM_DISABLE || oomadj > OOM_ADJUST_MAX) {
+    if (oomadj < OOM_SCORE_ADJ_MIN || oomadj > OOM_SCORE_ADJ_MAX) {
         ALOGE("Invalid PROCPRIO oomadj argument %d", oomadj);
         return;
     }
 
     snprintf(path, sizeof(path), "/proc/%d/oom_score_adj", pid);
-    snprintf(val, sizeof(val), "%d", lowmem_oom_adj_to_oom_score_adj(oomadj));
+    snprintf(val, sizeof(val), "%d", oomadj);
     writefilestring(path, val);
 
     if (use_inkernel_interface)
@@ -607,7 +594,7 @@
 static int find_and_kill_process(int other_free, int other_file, bool first)
 {
     int i;
-    int min_score_adj = OOM_ADJUST_MAX + 1;
+    int min_score_adj = OOM_SCORE_ADJ_MAX + 1;
     int minfree = 0;
     int killed_size = 0;
 
@@ -619,10 +606,10 @@
         }
     }
 
-    if (min_score_adj == OOM_ADJUST_MAX + 1)
+    if (min_score_adj == OOM_SCORE_ADJ_MAX + 1)
         return 0;
 
-    for (i = OOM_ADJUST_MAX; i >= min_score_adj; i--) {
+    for (i = OOM_SCORE_ADJ_MAX; i >= min_score_adj; i--) {
         struct proc *procp;
 
 retry:
@@ -783,7 +770,7 @@
             ALOGE("Kernel does not support memory pressure events or in-kernel low memory killer");
     }
 
-    for (i = 0; i <= ADJTOSLOT(OOM_ADJUST_MAX); i++) {
+    for (i = 0; i <= ADJTOSLOT(OOM_SCORE_ADJ_MAX); i++) {
         procadjslot_list[i].next = &procadjslot_list[i];
         procadjslot_list[i].prev = &procadjslot_list[i];
     }
diff --git a/lmkd/lmkd.rc b/lmkd/lmkd.rc
index 7d6cb11..3bb84ab 100644
--- a/lmkd/lmkd.rc
+++ b/lmkd/lmkd.rc
@@ -1,5 +1,6 @@
 service lmkd /system/bin/lmkd
     class core
+    group root readproc
     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/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 ecd2f0a..10f3553 100644
--- a/logd/logd.rc
+++ b/logd/logd.rc
@@ -3,7 +3,7 @@
     socket logd stream 0666 logd logd
     socket logdr seqpacket 0666 logd logd
     socket logdw dgram 0222 logd logd
-    group root system
+    group root system readproc
     writepid /dev/cpuset/system-background/tasks
 
 service logd-reinit /system/bin/logd --reinit
diff --git a/logd/main.cpp b/logd/main.cpp
index 60262e9..8e75b37 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>
 
@@ -104,7 +106,9 @@
         return -1;
     }
 
-    if (setgroups(0, NULL) == -1) {
+    gid_t groups[] = { AID_READPROC };
+
+    if (setgroups(sizeof(groups) / sizeof(groups[0]), groups) == -1) {
         return -1;
     }
 
@@ -166,6 +170,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 +197,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 +214,7 @@
         // Anything that reads persist.<property>
         if (logBuf) {
             logBuf->init();
+            logBuf->initPrune(NULL);
         }
     }
 
@@ -305,7 +300,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/include/metrics/metrics_library.h b/metricsd/include/metrics/metrics_library.h
index b766194..d2e98c8 100644
--- a/metricsd/include/metrics/metrics_library.h
+++ b/metricsd/include/metrics/metrics_library.h
@@ -34,6 +34,7 @@
   virtual bool SendToUMA(const std::string& name, int sample,
                          int min, int max, int nbuckets) = 0;
   virtual bool SendEnumToUMA(const std::string& name, int sample, int max) = 0;
+  virtual bool SendBoolToUMA(const std::string& name, bool sample) = 0;
   virtual bool SendSparseToUMA(const std::string& name, int sample) = 0;
   virtual bool SendUserActionToUMA(const std::string& action) = 0;
   virtual ~MetricsLibraryInterface() {}
@@ -96,6 +97,9 @@
   // normal, while 100 is high).
   bool SendEnumToUMA(const std::string& name, int sample, int max) override;
 
+  // Specialization of SendEnumToUMA for boolean values.
+  bool SendBoolToUMA(const std::string& name, bool sample) override;
+
   // Sends sparse histogram sample to Chrome for transport to UMA.  Returns
   // true on success.
   //
diff --git a/metricsd/include/metrics/metrics_library_mock.h b/metricsd/include/metrics/metrics_library_mock.h
index 3de87a9..db56f9e 100644
--- a/metricsd/include/metrics/metrics_library_mock.h
+++ b/metricsd/include/metrics/metrics_library_mock.h
@@ -32,6 +32,7 @@
                                int min, int max, int nbuckets));
   MOCK_METHOD3(SendEnumToUMA, bool(const std::string& name, int sample,
                                    int max));
+  MOCK_METHOD2(SendBoolToUMA, bool(const std::string& name, bool sample));
   MOCK_METHOD2(SendSparseToUMA, bool(const std::string& name, int sample));
   MOCK_METHOD1(SendUserActionToUMA, bool(const std::string& action));
 
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/metrics_library.cc b/metricsd/metrics_library.cc
index a651b76..735d39f 100644
--- a/metricsd/metrics_library.cc
+++ b/metricsd/metrics_library.cc
@@ -173,6 +173,13 @@
       uma_events_file_.value());
 }
 
+bool MetricsLibrary::SendBoolToUMA(const std::string& name, bool sample) {
+  return metrics::SerializationUtils::WriteMetricToFile(
+      *metrics::MetricSample::LinearHistogramSample(name,
+                                                    sample ? 1 : 0, 2).get(),
+      uma_events_file_.value());
+}
+
 bool MetricsLibrary::SendSparseToUMA(const std::string& name, int sample) {
   return metrics::SerializationUtils::WriteMetricToFile(
       *metrics::MetricSample::SparseHistogramSample(name, sample).get(),
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/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 bdc4567..ddfe168 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -333,6 +333,7 @@
     mkdir /data/misc/vold 0700 root root
     mkdir /data/misc/boottrace 0771 system shell
     mkdir /data/misc/update_engine 0700 root root
+    mkdir /data/misc/trace 0700 root root
 
     # For security reasons, /data/local/tmp should always be empty.
     # Do not place files or directories in /data/local/tmp
@@ -378,9 +379,13 @@
     mkdir /data/backup 0700 system system
     mkdir /data/media 0770 media_rw media_rw
     mkdir /data/ss 0700 system system
+
     mkdir /data/system 0775 system system
     mkdir /data/system/heapdump 0700 system system
+    mkdir /data/system_ce 0770 system system
+
     mkdir /data/user 0711 system system
+    mkdir /data/user_de 0711 system system
 
     setusercryptopolicies /data/user
 
@@ -562,10 +567,13 @@
     console
     disabled
     user shell
-    group shell log
+    group shell log readproc
     seclabel u:r:shell:s0
 
 on property:ro.debuggable=1
+    # Give writes to anyone for the trace folder on debug builds.
+    # The folder is used to store method traces.
+    chmod 0773 /data/misc/trace
     start console
 
 service flash_recovery /system/bin/install-recovery.sh
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);
             }
         }
     }