Merge "Remove the name filtering from toolbox ps."
diff --git a/adb/Android.mk b/adb/Android.mk
index c507905..903d1e1 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -213,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 += \
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 2632b1f..a720859 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -101,12 +101,10 @@
         "                                 will disconnect from all connected TCP/IP devices.\n"
         "\n"
         "device commands:\n"
-        "  adb push [-p] <local> <remote>\n"
+        "  adb push <local> <remote>\n"
         "                               - copy file/dir to device\n"
-        "                                 ('-p' to display the transfer progress)\n"
-        "  adb pull [-p] [-a] <remote> [<local>]\n"
+        "  adb pull [-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 sync [ <directory> ]     - copy host->device only if changed\n"
         "                                 (-l means list but don't copy)\n"
@@ -1065,15 +1063,14 @@
     return path;
 }
 
-static void parse_push_pull_args(const char **arg, int narg, char const **path1,
-                                 char const **path2, bool* show_progress,
+static void parse_push_pull_args(const char **arg, int narg,
+                                 char const **path1, char const **path2,
                                  int *copy_attrs) {
-    *show_progress = false;
     *copy_attrs = 0;
 
     while (narg > 0) {
         if (!strcmp(*arg, "-p")) {
-            *show_progress = true;
+            // Silently ignore for backwards compatibility.
         } else if (!strcmp(*arg, "-a")) {
             *copy_attrs = 1;
         } else {
@@ -1561,22 +1558,20 @@
         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;
 
-        parse_push_pull_args(&argv[1], argc - 1, &lpath, &rpath, &show_progress, &copy_attrs);
+        parse_push_pull_args(&argv[1], argc - 1, &lpath, &rpath, &copy_attrs);
         if (!lpath || !rpath || copy_attrs != 0) return usage();
-        return do_sync_push(lpath, rpath, show_progress) ? 0 : 1;
+        return do_sync_push(lpath, rpath) ? 0 : 1;
     }
     else if (!strcmp(argv[0], "pull")) {
-        bool show_progress = false;
         int copy_attrs = 0;
         const char* rpath = NULL, *lpath = ".";
 
-        parse_push_pull_args(&argv[1], argc - 1, &rpath, &lpath, &show_progress, &copy_attrs);
+        parse_push_pull_args(&argv[1], argc - 1, &rpath, &lpath, &copy_attrs);
         if (!rpath) return usage();
-        return do_sync_pull(rpath, lpath, show_progress, copy_attrs) ? 0 : 1;
+        return do_sync_pull(rpath, lpath, copy_attrs) ? 0 : 1;
     }
     else if (!strcmp(argv[0], "install")) {
         if (argc < 2) return usage();
@@ -1768,7 +1763,7 @@
     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;
+    if (!do_sync_push(apk_file, apk_dest.c_str())) goto cleanup_apk;
     argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */
     result = pm_command(transport, serial, argc, argv);
 
diff --git a/adb/file_sync_client.cpp b/adb/file_sync_client.cpp
index 1d668bc..b7e835b 100644
--- a/adb/file_sync_client.cpp
+++ b/adb/file_sync_client.cpp
@@ -38,6 +38,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>
@@ -49,36 +50,15 @@
     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, "adb: error: %s\n", error.c_str());
+            Error("connect failed: %s", error.c_str());
         }
     }
 
@@ -86,7 +66,6 @@
         if (!IsValid()) return;
 
         SendQuit();
-        ShowTransferRate();
         adb_close(fd);
     }
 
@@ -95,7 +74,7 @@
     bool SendRequest(int id, const char* path_and_mode) {
         size_t path_length = strlen(path_and_mode);
         if (path_length > 1024) {
-            fprintf(stderr, "adb: SendRequest failed: path too long: %zu\n", path_length);
+            Error("SendRequest failed: path too long: %zu", path_length);
             errno = ENAMETOOLONG;
             return false;
         }
@@ -115,11 +94,14 @@
     // 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, "adb: SendSmallFile failed: path too long: %zu\n", path_length);
+            Error("SendSmallFile failed: path too long: %zu", path_length);
             errno = ENAMETOOLONG;
             return false;
         }
@@ -157,16 +139,14 @@
     bool CopyDone(const char* from, const char* to) {
         syncmsg msg;
         if (!ReadFdExactly(fd, &msg.status, sizeof(msg.status))) {
-            fprintf(stderr, "adb: 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, "adb: failed to copy '%s' to '%s': unknown reason %d\n",
-                    from, to, msg.status.id);
+            Error("failed to copy '%s' to '%s': unknown reason %d", from, to, msg.status.id);
             return false;
         }
         return ReportCopyFailure(from, to, msg);
@@ -175,15 +155,41 @@
     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)) {
-            fprintf(stderr, "adb: failed to copy '%s' to '%s'; failed to read reason (!): %s\n",
-                    from, to, strerror(errno));
+            Error("failed to copy '%s' to '%s'; failed to read reason (!): %s",
+                  from, to, strerror(errno));
             return false;
         }
         buf[msg.status.msglen] = 0;
-        fprintf(stderr, "adb: failed to copy '%s' to '%s': %s\n", from, to, &buf[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 Error(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) {
+        std::string s = "adb: error: ";
+
+        va_list ap;
+        va_start(ap, fmt);
+        android::base::StringAppendV(&s, fmt, ap);
+        va_end(ap);
+
+        line_printer_.Print(s, LinePrinter::FULL);
+    }
+
     uint64_t total_bytes;
 
     // TODO: add a char[max] buffer here, to replace syncsendbuf...
@@ -191,19 +197,18 @@
     size_t max;
 
   private:
-    uint64_t start_time_;
+    uint64_t start_time_ms_;
+
+    LinePrinter line_printer_;
 
     void SendQuit() {
         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;
     }
 };
 
@@ -249,29 +254,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, "adb: 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, "adb: 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, "adb: cannot open '%s': %s\n", path, strerror(errno));
+        sc.Error("cannot open '%s': %s", lpath, strerror(errno));
         return false;
     }
 
@@ -281,7 +283,7 @@
         int ret = adb_read(lfd, sbuf.data, sc.max);
         if (ret <= 0) {
             if (ret < 0) {
-                fprintf(stderr, "adb: cannot read '%s': %s\n", path, strerror(errno));
+                sc.Error("cannot read '%s': %s", lpath, strerror(errno));
                 adb_close(lfd);
                 return false;
             }
@@ -295,9 +297,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.Print(android::base::StringPrintf("%s: %d%%", rpath, percentage));
     }
 
     adb_close(lfd);
@@ -306,8 +309,7 @@
     msg.data.id = ID_DONE;
     msg.data.size = mtime;
     if (!WriteFdExactly(sc.fd, &msg.data, sizeof(msg.data))) {
-        fprintf(stderr, "adb: 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;
     }
 
@@ -315,7 +317,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);
 
@@ -324,44 +326,48 @@
         char buf[PATH_MAX];
         ssize_t data_length = readlink(lpath, buf, PATH_MAX - 1);
         if (data_length == -1) {
-            fprintf(stderr, "adb: 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, "adb: 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, "adb: failed to stat local file '%s': %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, "adb: 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) {
+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;
 
@@ -369,10 +375,11 @@
     mkdirs(lpath);
     int lfd = adb_creat(lpath, 0644);
     if (lfd < 0) {
-        fprintf(stderr, "adb: cannot create '%s': %s\n", lpath, strerror(errno));
+        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))) {
@@ -391,7 +398,7 @@
         }
 
         if (msg.data.size > sc.max) {
-            fprintf(stderr, "adb: msg.data.size too large: %u (max %zu)\n", 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;
@@ -405,7 +412,7 @@
         }
 
         if (!WriteFdExactly(lfd, buffer, msg.data.size)) {
-            fprintf(stderr, "adb: cannot write '%s': %s\n", lpath, strerror(errno));
+            sc.Error("cannot write '%s': %s", lpath, strerror(errno));
             adb_close(lfd);
             adb_unlink(lpath);
             return false;
@@ -413,9 +420,10 @@
 
         sc.total_bytes += msg.data.size;
 
-        if (show_progress) {
-            print_transfer_progress(sc.total_bytes, size);
-        }
+        bytes_copied += msg.data.size;
+
+        int percentage = static_cast<int>(bytes_copied * 100 / size);
+        sc.Print(android::base::StringPrintf("%s: %d%%", rpath, percentage));
     }
 
     adb_close(lfd);
@@ -453,7 +461,7 @@
     int dsize = dlen + nlen + 2;
 
     copyinfo *ci = reinterpret_cast<copyinfo*>(malloc(sizeof(copyinfo) + ssize + dsize));
-    if(ci == 0) {
+    if (ci == 0) {
         fprintf(stderr, "out of memory\n");
         abort();
     }
@@ -475,23 +483,24 @@
     return name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'));
 }
 
-static int local_build_list(copyinfo** filelist, const char* lpath, const char* rpath) {
+static int local_build_list(SyncConnection& sc,
+                            copyinfo** filelist, const char* lpath, const char* rpath) {
     copyinfo *dirlist = 0;
     copyinfo *ci, *next;
 
     std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir(lpath), closedir);
     if (!dir) {
-        fprintf(stderr, "adb: cannot open '%s': %s\n", lpath, strerror(errno));
+        sc.Error("cannot open '%s': %s", lpath, strerror(errno));
         return -1;
     }
 
-    dirent *de;
+    dirent* de;
     while ((de = readdir(dir.get()))) {
         if (IsDotOrDotDot(de->d_name)) continue;
 
         char stat_path[PATH_MAX];
         if (strlen(lpath) + strlen(de->d_name) + 1 > sizeof(stat_path)) {
-            fprintf(stderr, "adb: skipping long path '%s%s'\n", lpath, de->d_name);
+            sc.Error("skipping long path '%s%s'", lpath, de->d_name);
             continue;
         }
         strcpy(stat_path, lpath);
@@ -506,7 +515,7 @@
             } else {
                 ci = mkcopyinfo(lpath, rpath, de->d_name, 0);
                 if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) {
-                    fprintf(stderr, "adb: skipping special file '%s'\n", ci->src);
+                    sc.Error("skipping special file '%s'", ci->src);
                     free(ci);
                 } else {
                     ci->time = st.st_mtime;
@@ -517,7 +526,7 @@
                 }
             }
         } else {
-            fprintf(stderr, "adb: cannot lstat '%s': %s\n",stat_path , strerror(errno));
+            sc.Error("cannot lstat '%s': %s",stat_path , strerror(errno));
         }
     }
 
@@ -525,7 +534,7 @@
     dir.reset();
     for (ci = dirlist; ci != 0; ci = next) {
         next = ci->next;
-        local_build_list(filelist, ci->src, ci->dst);
+        local_build_list(sc, filelist, ci->src, ci->dst);
         free(ci);
     }
 
@@ -555,7 +564,7 @@
         rpath = tmp;
     }
 
-    if (local_build_list(&filelist, lpath, rpath)) {
+    if (local_build_list(sc, &filelist, lpath, rpath)) {
         return false;
     }
 
@@ -578,9 +587,12 @@
     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;
+            if (list_only) {
+                fprintf(stderr, "would push: %s -> %s\n", ci->src, ci->dst);
+            } else {
+                if (!sync_send(sc, ci->src, ci->dst, ci->time, ci->mode)) {
+                  return false;
+                }
             }
             pushed++;
         } else {
@@ -589,20 +601,21 @@
         free(ci);
     }
 
-    fprintf(stderr, "%d file%s pushed. %d file%s skipped.\n",
-            pushed, (pushed == 1) ? "" : "s",
-            skipped, (skipped == 1) ? "" : "s");
-
+    sc.Print(android::base::StringPrintf("%s: %d file%s pushed. %d file%s skipped.%s\n",
+                                         rpath,
+                                         pushed, (pushed == 1) ? "" : "s",
+                                         skipped, (skipped == 1) ? "" : "s",
+                                         sc.TransferRate().c_str()));
     return true;
 }
 
-bool do_sync_push(const char* lpath, const char* rpath, bool show_progress) {
+bool do_sync_push(const char* lpath, const char* rpath) {
     SyncConnection sc;
     if (!sc.IsValid()) return false;
 
     struct stat st;
     if (stat(lpath, &st)) {
-        fprintf(stderr, "adb: cannot stat '%s': %s\n", lpath, strerror(errno));
+        sc.Error("cannot stat '%s': %s", lpath, strerror(errno));
         return false;
     }
 
@@ -619,21 +632,23 @@
         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);
+    bool result = sync_send(sc, lpath, rpath, st.st_mtime, st.st_mode);
+    sc.Print("\n");
+    return result;
 }
 
-
 struct sync_ls_build_list_cb_args {
-    copyinfo **filelist;
-    copyinfo **dirlist;
-    const char *rpath;
-    const char *lpath;
+    SyncConnection* sc;
+    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;
+    sync_ls_build_list_cb_args* args = static_cast<sync_ls_build_list_cb_args*>(cookie);
     copyinfo *ci;
 
     if (S_ISDIR(mode)) {
@@ -655,28 +670,29 @@
         ci->next = *filelist;
         *filelist = ci;
     } else {
-        fprintf(stderr, "adb: skipping special file '%s'\n", name);
+        args->sc->Print(android::base::StringPrintf("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;
+    copyinfo* dirlist = nullptr;
 
+    sync_ls_build_list_cb_args args;
+    args.sc = &sc;
     args.filelist = filelist;
     args.dirlist = &dirlist;
     args.rpath = rpath;
     args.lpath = lpath;
 
     // Put the files/dirs in rpath on the lists.
-    if (!sync_ls(sc, rpath, sync_ls_build_list_cb, (void *)&args)) {
+    if (!sync_ls(sc, rpath, sync_ls_build_list_cb, &args)) {
         return false;
     }
 
     // Recurse into each directory we found.
     while (dirlist != NULL) {
-        copyinfo *next = dirlist->next;
+        copyinfo* next = dirlist->next;
         if (!remote_build_list(sc, filelist, dirlist->src, dirlist->dst)) {
             return false;
         }
@@ -710,7 +726,7 @@
     if (lpath_clean.back() != '/') lpath_clean.push_back('/');
 
     // Recursively build the list of files to copy.
-    fprintf(stderr, "pull: building file list...\n");
+    sc.Print("pull: building file list...");
     copyinfo* filelist = nullptr;
     if (!remote_build_list(sc, &filelist, rpath_clean.c_str(), lpath_clean.c_str())) return false;
 
@@ -720,8 +736,8 @@
     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)) {
+            sc.Print(android::base::StringPrintf("pull: %s -> %s", ci->src, ci->dst));
+            if (!sync_recv(sc, ci->src, ci->dst)) {
                 return false;
             }
 
@@ -736,20 +752,22 @@
         ci = next;
     }
 
-    fprintf(stderr, "%d file%s pulled. %d file%s skipped.\n",
-            pulled, (pulled == 1) ? "" : "s",
-            skipped, (skipped == 1) ? "" : "s");
+    sc.Print(android::base::StringPrintf("%s: %d file%s pulled. %d file%s skipped.%s\n",
+                                         rpath,
+                                         pulled, (pulled == 1) ? "" : "s",
+                                         skipped, (skipped == 1) ? "" : "s",
+                                         sc.TransferRate().c_str()));
     return true;
 }
 
-bool do_sync_pull(const char* rpath, const char* lpath, bool show_progress, int copy_attrs) {
+bool do_sync_pull(const char* rpath, const char* lpath, int 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, "adb: remote object '%s' does not exist\n", rpath);
+        sc.Error("remote object '%s' does not exist", rpath);
         return false;
     }
 
@@ -764,25 +782,24 @@
                 lpath = path_holder.c_str();
             }
         }
-        if (!sync_recv(sc, rpath, lpath, show_progress)) {
+        if (!sync_recv(sc, rpath, lpath)) {
             return false;
         } else {
             if (copy_attrs && set_time_and_mode(lpath, time, mode)) {
                 return false;
             }
         }
+        sc.Print("\n");
         return true;
     } else if (S_ISDIR(mode)) {
         return copy_remote_dir_local(sc, rpath, lpath, copy_attrs);
     }
 
-    fprintf(stderr, "adb: remote object '%s' not a file or directory\n", rpath);
+    sc.Error("remote object '%s' not a file or directory", rpath);
     return false;
 }
 
 bool do_sync_sync(const std::string& lpath, const std::string& rpath, bool list_only) {
-    fprintf(stderr, "syncing %s...\n", rpath.c_str());
-
     SyncConnection sc;
     if (!sc.IsValid()) return false;
 
diff --git a/adb/file_sync_service.h b/adb/file_sync_service.h
index 67ed3fc..7c4d554 100644
--- a/adb/file_sync_service.h
+++ b/adb/file_sync_service.h
@@ -64,9 +64,9 @@
 
 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 char* lpath, const char* rpath);
 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);
+bool do_sync_pull(const char* rpath, const char* lpath, 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..81b3f0a
--- /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_ = 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/fastboot/engine.cpp b/fastboot/engine.cpp
index a0e990a..63e8971 100644
--- a/fastboot/engine.cpp
+++ b/fastboot/engine.cpp
@@ -75,41 +75,35 @@
 
 
 
-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);
+    char buf[FB_RESPONSE_SZ + 1];
+    memset(buf, 0, sizeof(buf));
+    if (fb_command_response(usb, cmd.c_str(), buf)) {
+      return false;
+    }
+    *value = buf;
+    return true;
 }
 
 
-/* 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;
-
+// 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.
+bool fb_format_supported(usb_handle *usb, const char *partition, const char *type_override) {
     if (type_override) {
-        return !!fs_get_generator(type_override);
+        return fs_get_generator(type_override) != nullptr;
     }
-    status = fb_getvar(usb, fs_type, "partition-type:%s", partition);
-    if (status) {
-        return 0;
+    std::string partition_type;
+    if (!fb_getvar(usb, std::string("partition-type:") + partition, &partition_type)) {
+        return false;
     }
-    return !!fs_get_generator(fs_type);
+    return fs_get_generator(partition_type.c_str()) != nullptr;
 }
 
 static int cb_default(Action* a, int status, const char* resp) {
@@ -394,8 +388,3 @@
     fprintf(stderr,"finished. total time: %.3fs\n", (now() - start));
     return status;
 }
-
-int fb_queue_is_empty(void)
-{
-    return (action_list == nullptr);
-}
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index e2971f2..7bf4b31 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -43,6 +43,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <base/parseint.h>
 #include <sparse/sparse.h>
 #include <ziparchive/zip_archive.h>
 
@@ -567,25 +568,23 @@
     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)) {
+        return 0;
     }
 
+    uint64_t limit;
+    if (!android::base::ParseUint(max_download_size.c_str(), &limit)) {
+        return 0;
+    }
+    if (limit > 0) {
+        fprintf(stderr, "target reported max download size of %" PRId64 " bytes\n", limit);
+    }
     return limit;
 }
 
-static int64_t get_sparse_limit(struct usb_handle *usb, int64_t size)
-{
+static int64_t get_sparse_limit(usb_handle* usb, int64_t size) {
     int64_t limit;
 
     if (sparse_limit == 0) {
@@ -610,16 +609,11 @@
     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* part) {
+    return !fb_format_supported(usb, part, nullptr);
 }
 
 static int load_buf_fd(usb_handle* usb, int fd, struct fastboot_buffer* buf) {
@@ -852,87 +846,84 @@
 }
 
 static void fb_perform_format(usb_handle* usb,
-                              const char *partition, int skip_if_not_supported,
-                              const char *type_override, const char *size_override) {
-    char pTypeBuff[FB_RESPONSE_SZ + 1], pSizeBuff[FB_RESPONSE_SZ + 1];
-    char *pType = pTypeBuff;
-    char *pSize = pSizeBuff;
-    unsigned int limit = INT_MAX;
+                              const char* partition, int skip_if_not_supported,
+                              const char* type_override, const char* size_override) {
+    std::string partition_type, partition_size;
+
     struct fastboot_buffer buf;
-    const char *errMsg = nullptr;
-    const struct fs_generator *gen;
-    uint64_t pSz;
-    int status;
+    const char* errMsg = nullptr;
+    const struct fs_generator* gen = nullptr;
     int fd;
 
-    if (target_sparse_limit > 0 && target_sparse_limit < limit)
+    unsigned int limit = INT_MAX;
+    if (target_sparse_limit > 0 && target_sparse_limit < limit) {
         limit = target_sparse_limit;
-    if (sparse_limit > 0 && sparse_limit < limit)
+    }
+    if (sparse_limit > 0 && sparse_limit < limit) {
         limit = sparse_limit;
+    }
 
-    status = fb_getvar(usb, pType, "partition-type:%s", partition);
-    if (status) {
+    if (!fb_getvar(usb, std::string("partition-type:") + partition, &partition_type)) {
         errMsg = "Can't determine partition type.\n";
         goto failed;
     }
     if (type_override) {
-        if (strcmp(type_override, pType)) {
-            fprintf(stderr,
-                    "Warning: %s type is %s, but %s was requested for formating.\n",
-                    partition, pType, type_override);
+        if (partition_type != type_override) {
+            fprintf(stderr, "Warning: %s type is %s, but %s was requested for formatting.\n",
+                    partition, partition_type.c_str(), type_override);
         }
-        pType = (char *)type_override;
+        partition_type = type_override;
     }
 
-    status = fb_getvar(usb, pSize, "partition-size:%s", partition);
-    if (status) {
+    if (!fb_getvar(usb, std::string("partition-size:") + partition, &partition_size)) {
         errMsg = "Unable to get partition size\n";
         goto failed;
     }
     if (size_override) {
-        if (strcmp(size_override, pSize)) {
-            fprintf(stderr,
-                    "Warning: %s size is %s, but %s was requested for formating.\n",
-                    partition, pSize, size_override);
+        if (partition_size != size_override) {
+            fprintf(stderr, "Warning: %s size is %s, but %s was requested for formatting.\n",
+                    partition, partition_size.c_str(), size_override);
         }
-        pSize = (char *)size_override;
+        partition_size = size_override;
     }
 
-    gen = fs_get_generator(pType);
+    gen = fs_get_generator(partition_type.c_str());
     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());
 }
@@ -945,8 +936,6 @@
     bool erase_first = true;
     void *data;
     int64_t sz;
-    int status;
-    int c;
     int longindex;
 
     const struct option longopts[] = {
@@ -964,7 +953,7 @@
     serial = getenv("ANDROID_SERIAL");
 
     while (1) {
-        c = getopt_long(argc, argv, "wub:k:n:r:t:s:S:lp:c:i:m:h", longopts, &longindex);
+        int c = getopt_long(argc, argv, "wub:k:n:r:t:s:S:lp:c:i:m:h", longopts, &longindex);
         if (c < 0) {
             break;
         }
@@ -1061,14 +1050,14 @@
     usb_handle* usb = open_device();
 
     while (argc > 0) {
-        if(!strcmp(*argv, "getvar")) {
+        if (!strcmp(*argv, "getvar")) {
             require(2);
             fb_queue_display(argv[1], argv[1]);
             skip(2);
         } else if(!strcmp(*argv, "erase")) {
             require(2);
 
-            if (fb_format_supported(usb, argv[1], nullptr)) {
+            if (!fb_format_supported(usb, argv[1], nullptr)) {
                 fprintf(stderr, "******** Did you mean to fastboot format this partition?\n");
             }
 
@@ -1217,10 +1206,16 @@
     }
 
     if (wants_wipe) {
+        fprintf(stderr, "wiping userdata...\n");
         fb_queue_erase("userdata");
         fb_perform_format(usb, "userdata", 1, nullptr, nullptr);
-        fb_queue_erase("cache");
-        fb_perform_format(usb, "cache", 1, nullptr, nullptr);
+
+        std::string cache_type;
+        if (fb_getvar(usb, "partition-type:cache", &cache_type) && !cache_type.empty()) {
+            fprintf(stderr, "wiping cache...\n");
+            fb_queue_erase("cache");
+            fb_perform_format(usb, "cache", 1, nullptr, nullptr);
+        }
     }
     if (wants_reboot) {
         fb_queue_reboot();
@@ -1230,9 +1225,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..a66c211 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,8 @@
 #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);
+bool fb_format_supported(usb_handle* usb, const char* partition, const char* type_override);
 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 +65,6 @@
 void fb_queue_notice(const char *notice);
 void fb_queue_wait_for_disconnect(void);
 int fb_execute_queue(usb_handle *usb);
-int fb_queue_is_empty(void);
 
 /* util stuff */
 double now();
diff --git a/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/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/libziparchive/Android.mk b/libziparchive/Android.mk
index 8ff94d4..8a4921f 100644
--- a/libziparchive/Android.mk
+++ b/libziparchive/Android.mk
@@ -18,9 +18,12 @@
 source_files := zip_archive.cc zip_writer.cc
 test_files := zip_archive_test.cc zip_writer_test.cc entry_name_utils_test.cc
 
+# ZLIB_CONST turns on const for input buffers, which is pretty standard.
+common_c_flags := -Werror -Wall -DZLIB_CONST
+
 # Incorrectly warns when C++11 empty brace {} initializer is used.
 # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61489
-common_cpp_flags := -Wno-missing-field-initializers
+common_cpp_flags := -Wold-style-cast -Wno-missing-field-initializers
 
 include $(CLEAR_VARS)
 LOCAL_CPP_EXTENSION := .cc
@@ -28,8 +31,8 @@
 LOCAL_STATIC_LIBRARIES := libz
 LOCAL_SHARED_LIBRARIES := libutils libbase
 LOCAL_MODULE:= libziparchive
-LOCAL_CFLAGS := -Werror -Wall
-LOCAL_CPPFLAGS := -Wold-style-cast $(common_cpp_flags)
+LOCAL_CFLAGS := $(common_c_flags)
+LOCAL_CPPFLAGS := $(common_cpp_flags)
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
@@ -37,7 +40,7 @@
 LOCAL_SRC_FILES := ${source_files}
 LOCAL_STATIC_LIBRARIES := libz libutils libbase
 LOCAL_MODULE:= libziparchive-host
-LOCAL_CFLAGS := -Werror
+LOCAL_CFLAGS := $(common_c_flags)
 LOCAL_CFLAGS_windows := -mno-ms-bitfields
 LOCAL_CPPFLAGS := $(common_cpp_flags)
 
@@ -51,7 +54,7 @@
 LOCAL_STATIC_LIBRARIES := libutils
 LOCAL_SHARED_LIBRARIES := libz-host liblog libbase
 LOCAL_MODULE:= libziparchive-host
-LOCAL_CFLAGS := -Werror
+LOCAL_CFLAGS := $(common_c_flags)
 LOCAL_CPPFLAGS := $(common_cpp_flags)
 LOCAL_MULTILIB := both
 include $(BUILD_HOST_SHARED_LIBRARY)
@@ -60,7 +63,7 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE := ziparchive-tests
 LOCAL_CPP_EXTENSION := .cc
-LOCAL_CFLAGS := -Werror
+LOCAL_CFLAGS := $(common_c_flags)
 LOCAL_CPPFLAGS := $(common_cpp_flags)
 LOCAL_SRC_FILES := $(test_files)
 LOCAL_SHARED_LIBRARIES := liblog libbase
@@ -70,7 +73,7 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE := ziparchive-tests-host
 LOCAL_CPP_EXTENSION := .cc
-LOCAL_CFLAGS := -Werror
+LOCAL_CFLAGS := $(common_c_flags)
 LOCAL_CPPFLAGS := -Wno-unnamed-type-template-args $(common_cpp_flags)
 LOCAL_SRC_FILES := $(test_files)
 LOCAL_SHARED_LIBRARIES := libziparchive-host liblog libbase
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index f1e13a7..3d18f7c 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -588,7 +588,7 @@
   // and other interesting attributes from the central directory. These
   // will later be compared against values from the local file header.
   data->method = cdr->compression_method;
-  data->mod_time = cdr->last_mod_time;
+  data->mod_time = cdr->last_mod_date << 16 | cdr->last_mod_time;
   data->crc32 = cdr->crc32;
   data->compressed_length = cdr->compressed_size;
   data->uncompressed_length = cdr->uncompressed_size;
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index 9a3cdb4..32b1a38 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -272,6 +272,7 @@
   ASSERT_EQ(static_cast<uint32_t>(17), data.uncompressed_length);
   ASSERT_EQ(static_cast<uint32_t>(13), data.compressed_length);
   ASSERT_EQ(0x950821c5, data.crc32);
+  ASSERT_EQ(static_cast<uint32_t>(0x438a8005), data.mod_time);
 
   // An entry that doesn't exist. Should be a negative return code.
   ZipString absent_name;
diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc
index de75d1e..22a7c53 100644
--- a/libziparchive/zip_writer.cc
+++ b/libziparchive/zip_writer.cc
@@ -18,10 +18,13 @@
 #include "zip_archive_common.h"
 #include "ziparchive/zip_writer.h"
 
+#include <utils/Log.h>
+
 #include <cassert>
 #include <cstdio>
 #include <memory>
 #include <zlib.h>
+#define DEF_MEM_LEVEL 8                // normally in zutil.h?
 
 /* Zip compression methods we support */
 enum {
@@ -29,6 +32,9 @@
   kCompressDeflated   = 8,        // standard deflate
 };
 
+// Size of the output buffer used for compression.
+static const size_t kBufSize = 32768u;
+
 // No error, operation completed successfully.
 static const int32_t kNoError = 0;
 
@@ -41,10 +47,14 @@
 // The zip entry name was invalid.
 static const int32_t kInvalidEntryName = -3;
 
+// An error occurred in zlib.
+static const int32_t kZlibError = -4;
+
 static const char* sErrorCodes[] = {
     "Invalid state",
     "IO error",
     "Invalid entry name",
+    "Zlib error",
 };
 
 const char* ZipWriter::ErrorCodeString(int32_t error_code) {
@@ -54,13 +64,21 @@
   return nullptr;
 }
 
-ZipWriter::ZipWriter(FILE* f) : file_(f), current_offset_(0), state_(State::kWritingZip) {
+static void DeleteZStream(z_stream* stream) {
+  deflateEnd(stream);
+  delete stream;
+}
+
+ZipWriter::ZipWriter(FILE* f) : file_(f), current_offset_(0), state_(State::kWritingZip),
+                                z_stream_(nullptr, DeleteZStream), buffer_(kBufSize) {
 }
 
 ZipWriter::ZipWriter(ZipWriter&& writer) : file_(writer.file_),
                                            current_offset_(writer.current_offset_),
                                            state_(writer.state_),
-                                           files_(std::move(writer.files_)) {
+                                           files_(std::move(writer.files_)),
+                                           z_stream_(std::move(writer.z_stream_)),
+                                           buffer_(std::move(writer.buffer_)){
   writer.file_ = nullptr;
   writer.state_ = State::kError;
 }
@@ -70,6 +88,8 @@
   current_offset_ = writer.current_offset_;
   state_ = writer.state_;
   files_ = std::move(writer.files_);
+  z_stream_ = std::move(writer.z_stream_);
+  buffer_ = std::move(writer.buffer_);
   writer.file_ = nullptr;
   writer.state_ = State::kError;
   return *this;
@@ -77,6 +97,7 @@
 
 int32_t ZipWriter::HandleError(int32_t error_code) {
   state_ = State::kError;
+  z_stream_.reset();
   return error_code;
 }
 
@@ -126,8 +147,16 @@
   // containing the crc and size fields.
   header.gpb_flags |= kGPBDDFlagMask;
 
-  // For now, ignore the ZipWriter::kCompress flag.
-  fileInfo.compression_method = kCompressStored;
+  if (flags & ZipWriter::kCompress) {
+    fileInfo.compression_method = kCompressDeflated;
+
+    int32_t result = PrepareDeflate();
+    if (result != kNoError) {
+      return result;
+    }
+  } else {
+    fileInfo.compression_method = kCompressStored;
+  }
   header.compression_method = fileInfo.compression_method;
 
   ExtractTimeAndDate(time, &fileInfo.last_mod_time, &fileInfo.last_mod_date);
@@ -163,40 +192,138 @@
   return kNoError;
 }
 
+int32_t ZipWriter::PrepareDeflate() {
+  assert(state_ == State::kWritingZip);
+
+  // Initialize the z_stream for compression.
+  z_stream_ = std::unique_ptr<z_stream, void(*)(z_stream*)>(new z_stream(), DeleteZStream);
+
+  int zerr = deflateInit2(z_stream_.get(), Z_BEST_COMPRESSION, Z_DEFLATED, -MAX_WBITS,
+                          DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
+  if (zerr != Z_OK) {
+    if (zerr == Z_VERSION_ERROR) {
+      ALOGE("Installed zlib is not compatible with linked version (%s)", ZLIB_VERSION);
+      return HandleError(kZlibError);
+    } else {
+      ALOGE("deflateInit2 failed (zerr=%d)", zerr);
+      return HandleError(kZlibError);
+    }
+  }
+
+  z_stream_->next_out = buffer_.data();
+  z_stream_->avail_out = buffer_.size();
+  return kNoError;
+}
+
 int32_t ZipWriter::WriteBytes(const void* data, size_t len) {
   if (state_ != State::kWritingEntry) {
     return HandleError(kInvalidState);
   }
 
   FileInfo& currentFile = files_.back();
+  int32_t result = kNoError;
   if (currentFile.compression_method & kCompressDeflated) {
-    // TODO(adamlesinski): Implement compression using zlib deflate.
-    assert(false);
+    result = CompressBytes(&currentFile, data, len);
   } else {
-    if (fwrite(data, 1, len, file_) != len) {
-      return HandleError(kIoError);
-    }
-    currentFile.crc32 = crc32(currentFile.crc32, reinterpret_cast<const Bytef*>(data), len);
-    currentFile.compressed_size += len;
-    current_offset_ += len;
+    result = StoreBytes(&currentFile, data, len);
   }
 
+  if (result != kNoError) {
+    return result;
+  }
+
+  currentFile.crc32 = crc32(currentFile.crc32, reinterpret_cast<const Bytef*>(data), len);
   currentFile.uncompressed_size += len;
   return kNoError;
 }
 
+int32_t ZipWriter::StoreBytes(FileInfo* file, const void* data, size_t len) {
+  assert(state_ == State::kWritingEntry);
+
+  if (fwrite(data, 1, len, file_) != len) {
+    return HandleError(kIoError);
+  }
+  file->compressed_size += len;
+  current_offset_ += len;
+  return kNoError;
+}
+
+int32_t ZipWriter::CompressBytes(FileInfo* file, const void* data, size_t len) {
+  assert(state_ == State::kWritingEntry);
+  assert(z_stream_);
+  assert(z_stream_->next_out != nullptr);
+  assert(z_stream_->avail_out != 0);
+
+  // Prepare the input.
+  z_stream_->next_in = reinterpret_cast<const uint8_t*>(data);
+  z_stream_->avail_in = len;
+
+  while (z_stream_->avail_in > 0) {
+    // We have more data to compress.
+    int zerr = deflate(z_stream_.get(), Z_NO_FLUSH);
+    if (zerr != Z_OK) {
+      return HandleError(kZlibError);
+    }
+
+    if (z_stream_->avail_out == 0) {
+      // The output is full, let's write it to disk.
+      size_t dataToWrite = z_stream_->next_out - buffer_.data();
+      if (fwrite(buffer_.data(), 1, dataToWrite, file_) != dataToWrite) {
+        return HandleError(kIoError);
+      }
+      file->compressed_size += dataToWrite;
+      current_offset_ += dataToWrite;
+
+      // Reset the output buffer for the next input.
+      z_stream_->next_out = buffer_.data();
+      z_stream_->avail_out = buffer_.size();
+    }
+  }
+  return kNoError;
+}
+
+int32_t ZipWriter::FlushCompressedBytes(FileInfo* file) {
+  assert(state_ == State::kWritingEntry);
+  assert(z_stream_);
+  assert(z_stream_->next_out != nullptr);
+  assert(z_stream_->avail_out != 0);
+
+  int zerr = deflate(z_stream_.get(), Z_FINISH);
+  if (zerr != Z_STREAM_END) {
+    return HandleError(kZlibError);
+  }
+
+  size_t dataToWrite = z_stream_->next_out - buffer_.data();
+  if (dataToWrite != 0) {
+    if (fwrite(buffer_.data(), 1, dataToWrite, file_) != dataToWrite) {
+      return HandleError(kIoError);
+    }
+    file->compressed_size += dataToWrite;
+    current_offset_ += dataToWrite;
+  }
+  z_stream_.reset();
+  return kNoError;
+}
+
 int32_t ZipWriter::FinishEntry() {
   if (state_ != State::kWritingEntry) {
     return kInvalidState;
   }
 
+  FileInfo& currentFile = files_.back();
+  if (currentFile.compression_method & kCompressDeflated) {
+    int32_t result = FlushCompressedBytes(&currentFile);
+    if (result != kNoError) {
+      return result;
+    }
+  }
+
   const uint32_t sig = DataDescriptor::kOptSignature;
   if (fwrite(&sig, sizeof(sig), 1, file_) != 1) {
     state_ = State::kError;
     return kIoError;
   }
 
-  FileInfo& currentFile = files_.back();
   DataDescriptor dd = {};
   dd.crc32 = currentFile.crc32;
   dd.compressed_size = currentFile.compressed_size;
@@ -241,8 +368,8 @@
 
   EocdRecord er = {};
   er.eocd_signature = EocdRecord::kSignature;
-  er.disk_num = 1;
-  er.cd_start_disk = 1;
+  er.disk_num = 0;
+  er.cd_start_disk = 0;
   er.num_records_on_disk = files_.size();
   er.num_records = files_.size();
   er.cd_size = current_offset_ - startOfCdr;
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc
index 5269730..046f195 100644
--- a/libziparchive/zip_writer_test.cc
+++ b/libziparchive/zip_writer_test.cc
@@ -44,26 +44,26 @@
 
   const char* expected = "hello";
 
-  ASSERT_EQ(writer.StartEntry("file.txt", 0), 0);
-  ASSERT_EQ(writer.WriteBytes("he", 2), 0);
-  ASSERT_EQ(writer.WriteBytes("llo", 3), 0);
-  ASSERT_EQ(writer.FinishEntry(), 0);
-  ASSERT_EQ(writer.Finish(), 0);
+  ASSERT_EQ(0, writer.StartEntry("file.txt", 0));
+  ASSERT_EQ(0, writer.WriteBytes("he", 2));
+  ASSERT_EQ(0, writer.WriteBytes("llo", 3));
+  ASSERT_EQ(0, writer.FinishEntry());
+  ASSERT_EQ(0, writer.Finish());
 
-  ASSERT_GE(lseek(fd_, 0, SEEK_SET), 0);
+  ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
 
   ZipArchiveHandle handle;
-  ASSERT_EQ(OpenArchiveFd(fd_, "temp", &handle, false), 0);
+  ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
 
   ZipEntry data;
-  ASSERT_EQ(FindEntry(handle, ZipString("file.txt"), &data), 0);
-  EXPECT_EQ(data.compressed_length, strlen(expected));
-  EXPECT_EQ(data.uncompressed_length, strlen(expected));
-  EXPECT_EQ(data.method, kCompressStored);
+  ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+  EXPECT_EQ(strlen(expected), data.compressed_length);
+  EXPECT_EQ(strlen(expected), data.uncompressed_length);
+  EXPECT_EQ(kCompressStored, data.method);
 
   char buffer[6];
-  EXPECT_EQ(ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(&buffer), sizeof(buffer)),
-            0);
+  EXPECT_EQ(0,
+            ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(&buffer), sizeof(buffer)));
   buffer[5] = 0;
 
   EXPECT_STREQ(expected, buffer);
@@ -74,49 +74,49 @@
 TEST_F(zipwriter, WriteUncompressedZipWithMultipleFiles) {
   ZipWriter writer(file_);
 
-  ASSERT_EQ(writer.StartEntry("file.txt", 0), 0);
-  ASSERT_EQ(writer.WriteBytes("he", 2), 0);
-  ASSERT_EQ(writer.FinishEntry(), 0);
+  ASSERT_EQ(0, writer.StartEntry("file.txt", 0));
+  ASSERT_EQ(0, writer.WriteBytes("he", 2));
+  ASSERT_EQ(0, writer.FinishEntry());
 
-  ASSERT_EQ(writer.StartEntry("file/file.txt", 0), 0);
-  ASSERT_EQ(writer.WriteBytes("llo", 3), 0);
-  ASSERT_EQ(writer.FinishEntry(), 0);
+  ASSERT_EQ(0, writer.StartEntry("file/file.txt", 0));
+  ASSERT_EQ(0, writer.WriteBytes("llo", 3));
+  ASSERT_EQ(0, writer.FinishEntry());
 
-  ASSERT_EQ(writer.StartEntry("file/file2.txt", 0), 0);
-  ASSERT_EQ(writer.FinishEntry(), 0);
+  ASSERT_EQ(0, writer.StartEntry("file/file2.txt", 0));
+  ASSERT_EQ(0, writer.FinishEntry());
 
-  ASSERT_EQ(writer.Finish(), 0);
+  ASSERT_EQ(0, writer.Finish());
 
-  ASSERT_GE(lseek(fd_, 0, SEEK_SET), 0);
+  ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
 
   ZipArchiveHandle handle;
-  ASSERT_EQ(OpenArchiveFd(fd_, "temp", &handle, false), 0);
+  ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
 
   char buffer[4];
   ZipEntry data;
 
-  ASSERT_EQ(FindEntry(handle, ZipString("file.txt"), &data), 0);
-  EXPECT_EQ(data.method, kCompressStored);
-  EXPECT_EQ(data.compressed_length, 2u);
-  EXPECT_EQ(data.uncompressed_length, 2u);
-  ASSERT_EQ(ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(buffer), arraysize(buffer)),
-            0);
+  ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+  EXPECT_EQ(kCompressStored, data.method);
+  EXPECT_EQ(2u, data.compressed_length);
+  EXPECT_EQ(2u, data.uncompressed_length);
+  ASSERT_EQ(0,
+            ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(buffer), arraysize(buffer)));
   buffer[2] = 0;
   EXPECT_STREQ("he", buffer);
 
-  ASSERT_EQ(FindEntry(handle, ZipString("file/file.txt"), &data), 0);
-  EXPECT_EQ(data.method, kCompressStored);
-  EXPECT_EQ(data.compressed_length, 3u);
-  EXPECT_EQ(data.uncompressed_length, 3u);
-  ASSERT_EQ(ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(buffer), arraysize(buffer)),
-            0);
+  ASSERT_EQ(0, FindEntry(handle, ZipString("file/file.txt"), &data));
+  EXPECT_EQ(kCompressStored, data.method);
+  EXPECT_EQ(3u, data.compressed_length);
+  EXPECT_EQ(3u, data.uncompressed_length);
+  ASSERT_EQ(0,
+            ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(buffer), arraysize(buffer)));
   buffer[3] = 0;
   EXPECT_STREQ("llo", buffer);
 
-  ASSERT_EQ(FindEntry(handle, ZipString("file/file2.txt"), &data), 0);
-  EXPECT_EQ(data.method, kCompressStored);
-  EXPECT_EQ(data.compressed_length, 0u);
-  EXPECT_EQ(data.uncompressed_length, 0u);
+  ASSERT_EQ(0, FindEntry(handle, ZipString("file/file2.txt"), &data));
+  EXPECT_EQ(kCompressStored, data.method);
+  EXPECT_EQ(0u, data.compressed_length);
+  EXPECT_EQ(0u, data.uncompressed_length);
 
   CloseArchive(handle);
 }
@@ -124,17 +124,47 @@
 TEST_F(zipwriter, WriteUncompressedZipWithAlignedFile) {
   ZipWriter writer(file_);
 
-  ASSERT_EQ(writer.StartEntry("align.txt", ZipWriter::kAlign32), 0);
-  ASSERT_EQ(writer.WriteBytes("he", 2), 0);
-  ASSERT_EQ(writer.FinishEntry(), 0);
-  ASSERT_EQ(writer.Finish(), 0);
+  ASSERT_EQ(0, writer.StartEntry("align.txt", ZipWriter::kAlign32));
+  ASSERT_EQ(0, writer.WriteBytes("he", 2));
+  ASSERT_EQ(0, writer.FinishEntry());
+  ASSERT_EQ(0, writer.Finish());
 
-  ASSERT_GE(lseek(fd_, 0, SEEK_SET), 0);
+  ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
 
   ZipArchiveHandle handle;
-  ASSERT_EQ(OpenArchiveFd(fd_, "temp", &handle, false), 0);
+  ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
 
   ZipEntry data;
-  ASSERT_EQ(FindEntry(handle, ZipString("align.txt"), &data), 0);
-  EXPECT_EQ(data.offset & 0x03, 0);
+  ASSERT_EQ(0, FindEntry(handle, ZipString("align.txt"), &data));
+  EXPECT_EQ(0, data.offset & 0x03);
+
+  CloseArchive(handle);
+}
+
+TEST_F(zipwriter, WriteCompressedZipWithOneFile) {
+  ZipWriter writer(file_);
+
+  ASSERT_EQ(0, writer.StartEntry("file.txt", ZipWriter::kCompress));
+  ASSERT_EQ(0, writer.WriteBytes("helo", 4));
+  ASSERT_EQ(0, writer.FinishEntry());
+  ASSERT_EQ(0, writer.Finish());
+
+  ASSERT_GE(0, lseek(fd_, 0, SEEK_SET));
+
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchiveFd(fd_, "temp", &handle, false));
+
+  ZipEntry data;
+  ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
+  EXPECT_EQ(kCompressDeflated, data.method);
+  EXPECT_EQ(4u, data.uncompressed_length);
+
+  char buffer[5];
+  ASSERT_EQ(0,
+            ExtractToMemory(handle, &data, reinterpret_cast<uint8_t*>(buffer), arraysize(buffer)));
+  buffer[4] = 0;
+
+  EXPECT_STREQ("helo", buffer);
+
+  CloseArchive(handle);
 }
diff --git a/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..05ef528 100644
--- a/logd/README.property
+++ b/logd/README.property
@@ -12,16 +12,24 @@
                                          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>"
 
 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/main.cpp b/logd/main.cpp
index 13dda78..cf8cb8f 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -212,6 +212,7 @@
         // Anything that reads persist.<property>
         if (logBuf) {
             logBuf->init();
+            logBuf->initPrune(NULL);
         }
     }