Merge "Add comment to keep enum in sync."
diff --git a/adb/Android.mk b/adb/Android.mk
index fe3c9cc..cb6da62 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -346,6 +346,9 @@
     libsquashfs_utils \
     libcutils \
     libbase \
-    libcrypto_static
+    libcrypto_static \
+    libminijail \
+    libminijail_generated \
+    libcap
 
 include $(BUILD_EXECUTABLE)
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 8575c86..a025ed7 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -204,7 +204,10 @@
         "  adb version                  - show version num\n"
         "\n"
         "scripting:\n"
-        "  adb wait-for-device          - block until device is online\n"
+        "  adb wait-for[-<transport>]-<state>\n"
+        "                               - wait for device to be in the given state:\n"
+        "                                 device, recovery, sideload, or bootloader\n"
+        "                                 Transport is: usb, local or any [default=any]\n"
         "  adb start-server             - ensure that there is a server running\n"
         "  adb kill-server              - kill the server if it is running\n"
         "  adb get-state                - prints: offline | bootloader | device\n"
@@ -1010,19 +1013,49 @@
 #endif /* !defined(_WIN32) */
 }
 
+static bool check_wait_for_device_syntax(const char* service) {
+    // TODO: when we have libc++ for Windows, use a regular expression instead.
+    // wait-for-((any|local|usb)-)?(bootloader|device|recovery|sideload)
+
+    char type[20];
+    char state[20];
+    int length = 0;
+    if (sscanf(service, "wait-for-%20[a-z]-%20[a-z]%n", type, state, &length) < 2 ||
+        length != static_cast<int>(strlen(service))) {
+        fprintf(stderr, "adb: couldn't parse 'wait-for' command: %s\n", service);
+        return false;
+    }
+
+    if (strcmp(type, "any") != 0 && strcmp(type, "local") != 0 && strcmp(type, "usb") != 0) {
+        fprintf(stderr, "adb: unknown type %s; expected 'any', 'local', or 'usb'\n", type);
+        return false;
+    }
+    if (strcmp(state, "bootloader") != 0 && strcmp(state, "device") != 0 &&
+        strcmp(state, "recovery") != 0 && strcmp(state, "sideload") != 0) {
+        fprintf(stderr, "adb: unknown state %s; "
+                        "expected 'bootloader', 'device', 'recovery', or 'sideload'\n", state);
+        return false;
+    }
+    return true;
+}
+
 static bool wait_for_device(const char* service, TransportType t, const char* serial) {
     // Was the caller vague about what they'd like us to wait for?
     // If so, check they weren't more specific in their choice of transport type.
     if (strcmp(service, "wait-for-device") == 0) {
         if (t == kTransportUsb) {
-            service = "wait-for-usb";
+            service = "wait-for-usb-device";
         } else if (t == kTransportLocal) {
-            service = "wait-for-local";
+            service = "wait-for-local-device";
         } else {
-            service = "wait-for-any";
+            service = "wait-for-any-device";
         }
     }
 
+    if (!check_wait_for_device_syntax(service)) {
+        return false;
+    }
+
     std::string cmd = format_host_command(service, t, serial);
     return adb_command(cmd);
 }
@@ -1860,8 +1893,7 @@
     adb_close(remoteFd);
 
     if (strncmp("Success", buf, 7)) {
-        fprintf(stderr, "Failed to write %s\n", file);
-        fputs(buf, stderr);
+        fprintf(stderr, "Failed to install %s: %s", file, buf);
         return 1;
     }
     fputs(buf, stderr);
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index feea7a3..78db69d 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -25,11 +25,15 @@
 #include <getopt.h>
 #include <sys/prctl.h>
 
+#include <memory>
+
 #include <android-base/logging.h>
 #include <android-base/stringprintf.h>
+#include <libminijail.h>
+
 #include "cutils/properties.h"
 #include "private/android_filesystem_config.h"
-#include "selinux/selinux.h"
+#include "selinux/android.h"
 
 #include "adb.h"
 #include "adb_auth.h"
@@ -53,12 +57,7 @@
             continue;
         }
 
-        int err = prctl(PR_CAPBSET_DROP, i, 0, 0, 0);
-
-        // Some kernels don't have file capabilities compiled in, and
-        // prctl(PR_CAPBSET_DROP) returns EINVAL. Don't automatically
-        // die when we see such misconfigured kernels.
-        if ((err < 0) && (errno != EINVAL)) {
+        if (prctl(PR_CAPBSET_DROP, i, 0, 0, 0) == -1) {
             PLOG(FATAL) << "Could not drop capabilities";
         }
     }
@@ -91,12 +90,12 @@
     bool adb_root = (strcmp(value, "1") == 0);
     bool adb_unroot = (strcmp(value, "0") == 0);
 
-    // ...except "adb root" lets you keep privileges in a debuggable build.
+    // ... except "adb root" lets you keep privileges in a debuggable build.
     if (ro_debuggable && adb_root) {
         drop = false;
     }
 
-    // ...and "adb unroot" lets you explicitly drop privileges.
+    // ... and "adb unroot" lets you explicitly drop privileges.
     if (adb_unroot) {
         drop = true;
     }
@@ -108,6 +107,9 @@
 }
 
 static void drop_privileges(int server_port) {
+    std::unique_ptr<minijail, void (*)(minijail*)> jail(minijail_new(),
+                                                        &minijail_destroy);
+
     // Add extra groups:
     // AID_ADB to access the USB driver
     // AID_LOG to read system logs (adb logcat)
@@ -121,28 +123,31 @@
     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_READPROC };
-    if (setgroups(sizeof(groups) / sizeof(groups[0]), groups) != 0) {
-        PLOG(FATAL) << "Could not set supplemental groups";
+                      AID_READPROC};
+    if (minijail_set_supplementary_gids(
+            jail.get(),
+            sizeof(groups) / sizeof(groups[0]),
+            groups) != 0) {
+        LOG(FATAL) << "Could not configure supplementary groups";
     }
 
-    /* don't listen on a port (default 5037) if running in secure mode */
-    /* don't run as root if we are running in secure mode */
+    // Don't listen on a port (default 5037) if running in secure mode.
+    // Don't run as root if running in secure mode.
     if (should_drop_privileges()) {
         drop_capabilities_bounding_set_if_needed();
 
-        /* then switch user and group to "shell" */
-        if (setgid(AID_SHELL) != 0) {
-            PLOG(FATAL) << "Could not setgid";
-        }
-        if (setuid(AID_SHELL) != 0) {
-            PLOG(FATAL) << "Could not setuid";
-        }
+        minijail_change_gid(jail.get(), AID_SHELL);
+        minijail_change_uid(jail.get(), AID_SHELL);
+        // minijail_enter() will abort if any priv-dropping step fails.
+        minijail_enter(jail.get());
 
         D("Local port disabled");
     } else {
+        // minijail_enter() will abort if any priv-dropping step fails.
+        minijail_enter(jail.get());
+
         if (root_seclabel != nullptr) {
-            if (setcon(root_seclabel) < 0) {
+            if (selinux_android_setcon(root_seclabel) < 0) {
                 LOG(FATAL) << "Could not set SELinux context";
             }
         }
@@ -152,7 +157,7 @@
         if (install_listener(local_name, "*smartsocket*", nullptr, 0,
                              &error)) {
             LOG(FATAL) << "Could not install *smartsocket* listener: "
-                << error;
+                       << error;
         }
     }
 }
diff --git a/adb/file_sync_client.cpp b/adb/file_sync_client.cpp
index c68f28b..0fa5917 100644
--- a/adb/file_sync_client.cpp
+++ b/adb/file_sync_client.cpp
@@ -51,9 +51,44 @@
     char data[SYNC_DATA_MAX];
 };
 
+static void ensure_trailing_separators(std::string& local_path, std::string& remote_path) {
+    if (!adb_is_separator(local_path.back())) {
+        local_path.push_back(OS_PATH_SEPARATOR);
+    }
+    if (remote_path.back() != '/') {
+        remote_path.push_back('/');
+    }
+}
+
+struct copyinfo {
+    std::string lpath;
+    std::string rpath;
+    unsigned int time = 0;
+    unsigned int mode;
+    uint64_t size = 0;
+    bool skip = false;
+
+    copyinfo(const std::string& local_path,
+             const std::string& remote_path,
+             const std::string& name,
+             unsigned int mode)
+            : lpath(local_path), rpath(remote_path), mode(mode) {
+        ensure_trailing_separators(lpath, rpath);
+        lpath.append(name);
+        rpath.append(name);
+        if (S_ISDIR(mode)) {
+            ensure_trailing_separators(lpath, rpath);
+        }
+    }
+};
+
 class SyncConnection {
   public:
-    SyncConnection() : total_bytes(0), start_time_ms_(CurrentTimeMs()) {
+    SyncConnection()
+            : total_bytes_(0),
+              start_time_ms_(CurrentTimeMs()),
+              expected_total_bytes_(0),
+              expect_multiple_files_(false) {
         max = SYNC_DATA_MAX; // TODO: decide at runtime.
 
         std::string error;
@@ -108,8 +143,6 @@
                        const char* lpath, const char* rpath,
                        unsigned mtime,
                        const char* data, size_t data_length) {
-        Print(rpath);
-
         size_t path_length = strlen(path_and_mode);
         if (path_length > 1024) {
             Error("SendSmallFile failed: path too long: %zu", path_length);
@@ -142,7 +175,7 @@
         p += sizeof(SyncRequest);
 
         WriteOrDie(lpath, rpath, &buf[0], (p - &buf[0]));
-        total_bytes += data_length;
+        total_bytes_ += data_length;
         return true;
     }
 
@@ -184,18 +217,10 @@
             sbuf.size = bytes_read;
             WriteOrDie(lpath, rpath, &sbuf, sizeof(SyncRequest) + bytes_read);
 
-            total_bytes += bytes_read;
+            total_bytes_ += bytes_read;
             bytes_copied += bytes_read;
 
-            if (total_size == 0) {
-                // This case can happen if we're racing against something that wrote to the file
-                // between our stat and our read, or if we're reading a magic file that lies about
-                // its size.
-                Printf("%s: ?%%", rpath);
-            } else {
-                int percentage = static_cast<int>(bytes_copied * 100 / total_size);
-                Printf("%s: %d%%", rpath, percentage);
-            }
+            ReportProgress(rpath, bytes_copied, total_size);
         }
 
         adb_close(lfd);
@@ -236,26 +261,52 @@
 
     std::string TransferRate() {
         uint64_t ms = CurrentTimeMs() - start_time_ms_;
-        if (total_bytes == 0 || ms == 0) return "";
+        if (total_bytes_ == 0 || ms == 0) return "";
 
         double s = static_cast<double>(ms) / 1000LL;
-        double rate = (static_cast<double>(total_bytes) / s) / (1024*1024);
+        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);
+                                           rate, total_bytes_, s);
     }
 
-    void Print(const std::string& s) {
-        line_printer_.Print(s, LinePrinter::INFO);
+    void ReportProgress(const char* file, uint64_t file_copied_bytes, uint64_t file_total_bytes) {
+        char overall_percentage_str[5] = "?";
+        if (expected_total_bytes_ != 0) {
+            int overall_percentage = static_cast<int>(total_bytes_ * 100 / expected_total_bytes_);
+            // If we're pulling symbolic links, we'll pull the target of the link rather than
+            // just create a local link, and that will cause us to go over 100%.
+            if (overall_percentage <= 100) {
+                snprintf(overall_percentage_str, sizeof(overall_percentage_str), "%d%%",
+                         overall_percentage);
+            }
+        }
+
+        if (file_copied_bytes > file_total_bytes || file_total_bytes == 0) {
+            // This case can happen if we're racing against something that wrote to the file
+            // between our stat and our read, or if we're reading a magic file that lies about
+            // its size. Just show how much we've copied.
+            Printf("[%4s] %s: %" PRId64 "/?", overall_percentage_str, file, file_copied_bytes);
+        } else {
+            // If we're transferring multiple files, we want to know how far through the current
+            // file we are, as well as the overall percentage.
+            if (expect_multiple_files_) {
+                int file_percentage = static_cast<int>(file_copied_bytes * 100 / file_total_bytes);
+                Printf("[%4s] %s: %d%%", overall_percentage_str, file, file_percentage);
+            } else {
+                Printf("[%4s] %s", overall_percentage_str, file);
+            }
+        }
     }
 
     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);
+        line_printer_.Print(s, LinePrinter::INFO);
     }
 
     void Error(const char* fmt, ...) __attribute__((__format__(ADB_FORMAT_ARCHETYPE, 2, 3))) {
@@ -280,7 +331,22 @@
         line_printer_.Print(s, LinePrinter::WARNING);
     }
 
-    uint64_t total_bytes;
+    void ComputeExpectedTotalBytes(const std::vector<copyinfo>& file_list) {
+        expected_total_bytes_ = 0;
+        for (const copyinfo& ci : file_list) {
+            // Unfortunately, this doesn't work for symbolic links, because we'll copy the
+            // target of the link rather than just creating a link. (But ci.size is the link size.)
+            if (!ci.skip) expected_total_bytes_ += ci.size;
+        }
+        expect_multiple_files_ = true;
+    }
+
+    void SetExpectedTotalBytes(uint64_t expected_total_bytes) {
+        expected_total_bytes_ = expected_total_bytes;
+        expect_multiple_files_ = false;
+    }
+
+    uint64_t total_bytes_;
 
     // TODO: add a char[max] buffer here, to replace syncsendbuf...
     int fd;
@@ -289,6 +355,9 @@
   private:
     uint64_t start_time_ms_;
 
+    uint64_t expected_total_bytes_;
+    bool expect_multiple_files_;
+
     LinePrinter line_printer_;
 
     bool SendQuit() {
@@ -417,8 +486,6 @@
 }
 
 static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath) {
-    sc.Print(rpath);
-
     unsigned size = 0;
     if (!sync_stat(sc, rpath, nullptr, nullptr, &size)) return false;
 
@@ -476,18 +543,11 @@
             return false;
         }
 
-        sc.total_bytes += msg.data.size;
+        sc.total_bytes_ += msg.data.size;
 
         bytes_copied += msg.data.size;
 
-        if (size == 0) {
-            // This case can happen if we're racing against something that wrote to the file between
-            // our stat and our read, or if we're reading a magic file that lies about its size.
-            sc.Printf("%s: ?%%", rpath);
-        } else {
-            int percentage = static_cast<int>(bytes_copied * 100 / size);
-            sc.Printf("%s: %d%%", rpath, percentage);
-        }
+        sc.ReportProgress(rpath, bytes_copied, size);
     }
 
     adb_close(lfd);
@@ -504,41 +564,11 @@
     });
 }
 
-static void ensure_trailing_separator(std::string& lpath, std::string& rpath) {
-    if (!adb_is_separator(lpath.back())) {
-        lpath.push_back(OS_PATH_SEPARATOR);
-    }
-    if (rpath.back() != '/') {
-        rpath.push_back('/');
-    }
-}
-
-struct copyinfo {
-    std::string lpath;
-    std::string rpath;
-    unsigned int time = 0;
-    unsigned int mode;
-    uint64_t size = 0;
-    bool skip = false;
-
-    copyinfo(const std::string& lpath, const std::string& rpath, const std::string& name,
-             unsigned int mode)
-        : lpath(lpath), rpath(rpath), mode(mode) {
-        ensure_trailing_separator(this->lpath, this->rpath);
-        this->lpath.append(name);
-        this->rpath.append(name);
-
-        if (S_ISDIR(mode)) {
-            ensure_trailing_separator(this->lpath, this->rpath);
-        }
-    }
-};
-
 static bool IsDotOrDotDot(const char* name) {
     return name[0] == '.' && (name[1] == '\0' || (name[1] == '.' && name[2] == '\0'));
 }
 
-static bool local_build_list(SyncConnection& sc, std::vector<copyinfo>* filelist,
+static bool local_build_list(SyncConnection& sc, std::vector<copyinfo>* file_list,
                              const std::string& lpath,
                              const std::string& rpath) {
     std::vector<copyinfo> dirlist;
@@ -576,7 +606,7 @@
                 ci.time = st.st_mtime;
                 ci.size = st.st_size;
             }
-            filelist->push_back(ci);
+            file_list->push_back(ci);
         }
     }
 
@@ -591,12 +621,12 @@
         sc.Warning("skipping empty directory '%s'", lpath.c_str());
         copyinfo ci(adb_dirname(lpath), adb_dirname(rpath), adb_basename(lpath), S_IFDIR);
         ci.skip = true;
-        filelist->push_back(ci);
+        file_list->push_back(ci);
         return true;
     }
 
     for (const copyinfo& ci : dirlist) {
-        local_build_list(sc, filelist, ci.lpath, ci.rpath);
+        local_build_list(sc, file_list, ci.lpath, ci.rpath);
     }
 
     return true;
@@ -607,29 +637,29 @@
                                   bool list_only) {
     // Make sure that both directory paths end in a slash.
     // Both paths are known to be nonempty, so we don't need to check.
-    ensure_trailing_separator(lpath, rpath);
+    ensure_trailing_separators(lpath, rpath);
 
     // Recursively build the list of files to copy.
-    std::vector<copyinfo> filelist;
+    std::vector<copyinfo> file_list;
     int pushed = 0;
     int skipped = 0;
-    if (!local_build_list(sc, &filelist, lpath, rpath)) {
+    if (!local_build_list(sc, &file_list, lpath, rpath)) {
         return false;
     }
 
     if (check_timestamps) {
-        for (const copyinfo& ci : filelist) {
+        for (const copyinfo& ci : file_list) {
             if (!sc.SendRequest(ID_STAT, ci.rpath.c_str())) {
                 return false;
             }
         }
-        for (copyinfo& ci : filelist) {
+        for (copyinfo& ci : file_list) {
             unsigned int timestamp, mode, size;
             if (!sync_finish_stat(sc, &timestamp, &mode, &size)) {
                 return false;
             }
             if (size == ci.size) {
-                /* for links, we cannot update the atime/mtime */
+                // 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.skip = true;
@@ -638,14 +668,14 @@
         }
     }
 
-    for (const copyinfo& ci : filelist) {
+    sc.ComputeExpectedTotalBytes(file_list);
+
+    for (const copyinfo& ci : file_list) {
         if (!ci.skip) {
             if (list_only) {
-                sc.Error("would push: %s -> %s", ci.lpath.c_str(),
-                         ci.rpath.c_str());
+                sc.Error("would push: %s -> %s", ci.lpath.c_str(), ci.rpath.c_str());
             } else {
-                if (!sync_send(sc, ci.lpath.c_str(), ci.rpath.c_str(), ci.time,
-                               ci.mode)) {
+                if (!sync_send(sc, ci.lpath.c_str(), ci.rpath.c_str(), ci.time, ci.mode)) {
                     return false;
                 }
             }
@@ -727,6 +757,7 @@
                 "%s/%s", dst_path, adb_basename(src_path).c_str());
             dst_path = path_holder.c_str();
         }
+        sc.SetExpectedTotalBytes(st.st_size);
         success &= sync_send(sc, src_path, dst_path, st.st_mtime, st.st_mode);
     }
 
@@ -745,7 +776,7 @@
 }
 
 static bool remote_build_list(SyncConnection& sc,
-                              std::vector<copyinfo>* filelist,
+                              std::vector<copyinfo>* file_list,
                               const std::string& rpath,
                               const std::string& lpath) {
     std::vector<copyinfo> dirlist;
@@ -769,7 +800,7 @@
         } else {
             ci.time = time;
             ci.size = size;
-            filelist->push_back(ci);
+            file_list->push_back(ci);
         }
     };
 
@@ -780,7 +811,7 @@
     // Add the current directory to the list if it was empty, to ensure that it gets created.
     if (empty_dir) {
         copyinfo ci(adb_dirname(lpath), adb_dirname(rpath), adb_basename(rpath), S_IFDIR);
-        filelist->push_back(ci);
+        file_list->push_back(ci);
         return true;
     }
 
@@ -789,7 +820,7 @@
         if (remote_symlink_isdir(sc, link_ci.rpath)) {
             dirlist.emplace_back(std::move(link_ci));
         } else {
-            filelist->emplace_back(std::move(link_ci));
+            file_list->emplace_back(std::move(link_ci));
         }
     }
 
@@ -797,7 +828,7 @@
     while (!dirlist.empty()) {
         copyinfo current = dirlist.back();
         dirlist.pop_back();
-        if (!remote_build_list(sc, filelist, current.rpath, current.lpath)) {
+        if (!remote_build_list(sc, file_list, current.rpath, current.lpath)) {
             return false;
         }
     }
@@ -822,21 +853,21 @@
                                   std::string lpath, bool copy_attrs) {
     // Make sure that both directory paths end in a slash.
     // Both paths are known to be nonempty, so we don't need to check.
-    ensure_trailing_separator(lpath, rpath);
+    ensure_trailing_separators(lpath, rpath);
 
     // Recursively build the list of files to copy.
-    sc.Print("pull: building file list...");
-    std::vector<copyinfo> filelist;
-    if (!remote_build_list(sc, &filelist, rpath.c_str(), lpath.c_str())) {
+    sc.Printf("pull: building file list...");
+    std::vector<copyinfo> file_list;
+    if (!remote_build_list(sc, &file_list, rpath.c_str(), lpath.c_str())) {
         return false;
     }
 
+    sc.ComputeExpectedTotalBytes(file_list);
+
     int pulled = 0;
     int skipped = 0;
-    for (const copyinfo &ci : filelist) {
+    for (const copyinfo &ci : file_list) {
         if (!ci.skip) {
-            sc.Printf("pull: %s -> %s", ci.rpath.c_str(), ci.lpath.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.
@@ -914,8 +945,8 @@
 
     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)) {
+        unsigned src_mode, src_time, src_size;
+        if (!sync_stat(sc, src_path, &src_time, &src_mode, &src_size)) {
             sc.Error("failed to stat remote object '%s'", src_path);
             return false;
         }
@@ -964,6 +995,7 @@
                 dst_path = path_holder.c_str();
             }
 
+            sc.SetExpectedTotalBytes(src_size);
             if (!sync_recv(sc, src_path, dst_path)) {
                 success = false;
                 continue;
diff --git a/adb/services.cpp b/adb/services.cpp
index 523353a..20166ce 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -356,19 +356,19 @@
 #if ADB_HOST
 struct state_info {
     TransportType transport_type;
-    char* serial;
+    std::string serial;
     ConnectionState state;
 };
 
-static void wait_for_state(int fd, void* cookie) {
-    state_info* sinfo = reinterpret_cast<state_info*>(cookie);
+static void wait_for_state(int fd, void* data) {
+    std::unique_ptr<state_info> sinfo(reinterpret_cast<state_info*>(data));
 
     D("wait_for_state %d", sinfo->state);
 
     while (true) {
         bool is_ambiguous = false;
         std::string error = "unknown error";
-        atransport* t = acquire_one_transport(sinfo->transport_type, sinfo->serial,
+        atransport* t = acquire_one_transport(sinfo->transport_type, sinfo->serial.c_str(),
                                               &is_ambiguous, &error);
         if (t != nullptr && t->connection_state == sinfo->state) {
             SendOkay(fd);
@@ -382,10 +382,6 @@
         }
     }
 
-    if (sinfo->serial) {
-        free(sinfo->serial);
-    }
-    free(sinfo);
     adb_close(fd);
     D("wait_for_state is done");
 }
@@ -491,38 +487,43 @@
 asocket* host_service_to_socket(const char* name, const char* serial) {
     if (!strcmp(name,"track-devices")) {
         return create_device_tracker();
-    } else if (!strncmp(name, "wait-for-", strlen("wait-for-"))) {
-        auto sinfo = reinterpret_cast<state_info*>(malloc(sizeof(state_info)));
-        if (sinfo == nullptr) {
-            fprintf(stderr, "couldn't allocate state_info: %s", strerror(errno));
-            return NULL;
-        }
-
-        if (serial)
-            sinfo->serial = strdup(serial);
-        else
-            sinfo->serial = NULL;
-
+    } else if (android::base::StartsWith(name, "wait-for-")) {
         name += strlen("wait-for-");
 
-        if (!strncmp(name, "local", strlen("local"))) {
-            sinfo->transport_type = kTransportLocal;
-            sinfo->state = kCsDevice;
-        } else if (!strncmp(name, "usb", strlen("usb"))) {
-            sinfo->transport_type = kTransportUsb;
-            sinfo->state = kCsDevice;
-        } else if (!strncmp(name, "any", strlen("any"))) {
-            sinfo->transport_type = kTransportAny;
-            sinfo->state = kCsDevice;
-        } else {
-            if (sinfo->serial) {
-                free(sinfo->serial);
-            }
-            free(sinfo);
-            return NULL;
+        std::unique_ptr<state_info> sinfo(new state_info);
+        if (sinfo == nullptr) {
+            fprintf(stderr, "couldn't allocate state_info: %s", strerror(errno));
+            return nullptr;
         }
 
-        int fd = create_service_thread(wait_for_state, sinfo);
+        if (serial) sinfo->serial = serial;
+
+        if (android::base::StartsWith(name, "local")) {
+            name += strlen("local");
+            sinfo->transport_type = kTransportLocal;
+        } else if (android::base::StartsWith(name, "usb")) {
+            name += strlen("usb");
+            sinfo->transport_type = kTransportUsb;
+        } else if (android::base::StartsWith(name, "any")) {
+            name += strlen("any");
+            sinfo->transport_type = kTransportAny;
+        } else {
+            return nullptr;
+        }
+
+        if (!strcmp(name, "-device")) {
+            sinfo->state = kCsDevice;
+        } else if (!strcmp(name, "-recovery")) {
+            sinfo->state = kCsRecovery;
+        } else if (!strcmp(name, "-sideload")) {
+            sinfo->state = kCsSideload;
+        } else if (!strcmp(name, "-bootloader")) {
+            sinfo->state = kCsBootloader;
+        } else {
+            return nullptr;
+        }
+
+        int fd = create_service_thread(wait_for_state, sinfo.release());
         return create_local_socket(fd);
     } else if (!strncmp(name, "connect:", 8)) {
         char* host = strdup(name + 8);
diff --git a/adb/usb_linux_client.cpp b/adb/usb_linux_client.cpp
index ceed8fa..a4f1a70 100644
--- a/adb/usb_linux_client.cpp
+++ b/adb/usb_linux_client.cpp
@@ -30,6 +30,8 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <algorithm>
+
 #include "adb.h"
 #include "transport.h"
 
@@ -37,6 +39,13 @@
 #define MAX_PACKET_SIZE_HS	512
 #define MAX_PACKET_SIZE_SS	1024
 
+// Writes larger than 16k fail on some devices (seed with 3.10.49-g209ea2f in particular).
+#define USB_FFS_MAX_WRITE 16384
+
+// The kernel allocates a contiguous buffer for reads, which can fail for large ones due to
+// fragmentation. 16k chosen arbitrarily to match the write limit.
+#define USB_FFS_MAX_READ 16384
+
 #define cpu_to_le16(x)  htole16(x)
 #define cpu_to_le32(x)  htole32(x)
 
@@ -459,10 +468,9 @@
 static int usb_ffs_write(usb_handle* h, const void* data, int len) {
     D("about to write (fd=%d, len=%d)", h->bulk_in, len);
 
-    // Writes larger than 16k fail on some devices (seed with 3.10.49-g209ea2f in particular).
     const char* buf = static_cast<const char*>(data);
     while (len > 0) {
-        int write_len = (len > 16384) ? 16384 : len;
+        int write_len = std::min(USB_FFS_MAX_WRITE, len);
         int n = adb_write(h->bulk_in, buf, write_len);
         if (n < 0) {
             D("ERROR: fd = %d, n = %d: %s", h->bulk_in, n, strerror(errno));
@@ -481,7 +489,8 @@
 
     char* buf = static_cast<char*>(data);
     while (len > 0) {
-        int n = adb_read(h->bulk_out, buf, len);
+        int read_len = std::min(USB_FFS_MAX_READ, len);
+        int n = adb_read(h->bulk_out, buf, read_len);
         if (n < 0) {
             D("ERROR: fd = %d, n = %d: %s", h->bulk_out, n, strerror(errno));
             return -1;
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index c293b57..8cbc79b 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -33,15 +33,15 @@
 
 LOCAL_CFLAGS += -DFASTBOOT_REVISION='"$(fastboot_version)"'
 
-LOCAL_SRC_FILES_linux := usb_linux.cpp util_linux.cpp
-LOCAL_STATIC_LIBRARIES_linux := libselinux
+LOCAL_SRC_FILES_linux := socket_unix.cpp usb_linux.cpp util_linux.cpp
+LOCAL_STATIC_LIBRARIES_linux := libcutils libselinux
 
-LOCAL_SRC_FILES_darwin := usb_osx.cpp util_osx.cpp
-LOCAL_STATIC_LIBRARIES_darwin := libselinux
+LOCAL_SRC_FILES_darwin := socket_unix.cpp usb_osx.cpp util_osx.cpp
+LOCAL_STATIC_LIBRARIES_darwin := libcutils libselinux
 LOCAL_LDLIBS_darwin := -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
 LOCAL_CFLAGS_darwin := -Wno-unused-parameter
 
-LOCAL_SRC_FILES_windows := usb_windows.cpp util_windows.cpp
+LOCAL_SRC_FILES_windows := socket_windows.cpp usb_windows.cpp util_windows.cpp
 LOCAL_STATIC_LIBRARIES_windows := AdbWinApi
 LOCAL_REQUIRED_MODULES_windows := AdbWinApi
 LOCAL_LDLIBS_windows := -lws2_32
@@ -89,3 +89,28 @@
 LOCAL_STATIC_LIBRARIES := libbase
 include $(BUILD_HOST_EXECUTABLE)
 endif
+
+# fastboot_test
+# =========================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := fastboot_test
+LOCAL_MODULE_HOST_OS := darwin linux windows
+
+LOCAL_SRC_FILES := socket_test.cpp
+LOCAL_STATIC_LIBRARIES := libbase
+
+LOCAL_CFLAGS += -Wall -Wextra -Werror -Wunreachable-code
+
+LOCAL_SRC_FILES_linux := socket_unix.cpp
+LOCAL_STATIC_LIBRARIES_linux := libcutils
+
+LOCAL_SRC_FILES_darwin := socket_unix.cpp
+LOCAL_LDLIBS_darwin := -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
+LOCAL_CFLAGS_darwin := -Wno-unused-parameter
+LOCAL_STATIC_LIBRARIES_darwin := libcutils
+
+LOCAL_SRC_FILES_windows := socket_windows.cpp
+LOCAL_LDLIBS_windows := -lws2_32
+
+include $(BUILD_HOST_NATIVE_TEST)
diff --git a/fastboot/socket.h b/fastboot/socket.h
new file mode 100644
index 0000000..888b530
--- /dev/null
+++ b/fastboot/socket.h
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+
+// This file provides a class interface for cross-platform UDP functionality. The main fastboot
+// engine should not be using this interface directly, but instead should use a higher-level
+// interface that enforces the fastboot UDP protocol.
+
+#ifndef SOCKET_H_
+#define SOCKET_H_
+
+#include "android-base/macros.h"
+
+#include <memory>
+#include <string>
+
+// UdpSocket interface to be implemented for each platform.
+class UdpSocket {
+  public:
+    // Creates a new client connection. Clients are connected to a specific hostname/port and can
+    // only send to that destination.
+    // On failure, |error| is filled (if non-null) and nullptr is returned.
+    static std::unique_ptr<UdpSocket> NewUdpClient(const std::string& hostname, int port,
+                                                   std::string* error);
+
+    // Creates a new server bound to local |port|. This is only meant for testing, during normal
+    // fastboot operation the device acts as the server.
+    // The server saves sender addresses in Receive(), and uses the most recent address during
+    // calls to Send().
+    static std::unique_ptr<UdpSocket> NewUdpServer(int port);
+
+    virtual ~UdpSocket() = default;
+
+    // Sends |length| bytes of |data|. Returns the number of bytes actually sent or -1 on error.
+    virtual ssize_t Send(const void* data, size_t length) = 0;
+
+    // Waits up to |timeout_ms| to receive up to |length| bytes of data. |timout_ms| of 0 will
+    // block forever. Returns the number of bytes received or -1 on error/timeout. On timeout
+    // errno will be set to EAGAIN or EWOULDBLOCK.
+    virtual ssize_t Receive(void* data, size_t length, int timeout_ms) = 0;
+
+    // Closes the socket. Returns 0 on success, -1 on error.
+    virtual int Close() = 0;
+
+  protected:
+    // Protected constructor to force factory function use.
+    UdpSocket() = default;
+
+    DISALLOW_COPY_AND_ASSIGN(UdpSocket);
+};
+
+#endif  // SOCKET_H_
diff --git a/fastboot/socket_test.cpp b/fastboot/socket_test.cpp
new file mode 100644
index 0000000..6ada964
--- /dev/null
+++ b/fastboot/socket_test.cpp
@@ -0,0 +1,197 @@
+/*
+ * 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.
+ */
+
+// Tests UDP functionality using loopback connections. Requires that kDefaultPort is available
+// for loopback communication on the host. These tests also assume that no UDP packets are lost,
+// which should be the case for loopback communication, but is not guaranteed.
+
+#include "socket.h"
+
+#include <errno.h>
+#include <time.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+enum {
+    // This port must be available for loopback communication.
+    kDefaultPort = 54321,
+
+    // Don't wait forever in a unit test.
+    kDefaultTimeoutMs = 3000,
+};
+
+static const char kReceiveStringError[] = "Error receiving string";
+
+// Test fixture to provide some helper functions. Makes each test a little simpler since we can
+// just check a bool for socket creation and don't have to pass hostname or port information.
+class SocketTest : public ::testing::Test {
+  protected:
+    bool StartServer(int port = kDefaultPort) {
+        server_ = UdpSocket::NewUdpServer(port);
+        return server_ != nullptr;
+    }
+
+    bool StartClient(const std::string hostname = "localhost", int port = kDefaultPort) {
+        client_ = UdpSocket::NewUdpClient(hostname, port, nullptr);
+        return client_ != nullptr;
+    }
+
+    bool StartClient2(const std::string hostname = "localhost", int port = kDefaultPort) {
+        client2_ = UdpSocket::NewUdpClient(hostname, port, nullptr);
+        return client2_ != nullptr;
+    }
+
+    std::unique_ptr<UdpSocket> server_, client_, client2_;
+};
+
+// Sends a string over a UdpSocket. Returns true if the full string (without terminating char)
+// was sent.
+static bool SendString(UdpSocket* udp, const std::string& message) {
+    return udp->Send(message.c_str(), message.length()) == static_cast<ssize_t>(message.length());
+}
+
+// Receives a string from a UdpSocket. Returns the string, or kReceiveStringError on failure.
+static std::string ReceiveString(UdpSocket* udp, size_t receive_size = 128) {
+    std::vector<char> buffer(receive_size);
+
+    ssize_t result = udp->Receive(buffer.data(), buffer.size(), kDefaultTimeoutMs);
+    if (result >= 0) {
+        return std::string(buffer.data(), result);
+    }
+    return kReceiveStringError;
+}
+
+// Calls Receive() on the UdpSocket with the given timeout. Returns true if the call timed out.
+static bool ReceiveTimeout(UdpSocket* udp, int timeout_ms) {
+    char buffer[1];
+
+    errno = 0;
+    return udp->Receive(buffer, 1, timeout_ms) == -1 && (errno == EAGAIN || errno == EWOULDBLOCK);
+}
+
+// Tests sending packets client -> server, then server -> client.
+TEST_F(SocketTest, SendAndReceive) {
+    ASSERT_TRUE(StartServer());
+    ASSERT_TRUE(StartClient());
+
+    EXPECT_TRUE(SendString(client_.get(), "foo"));
+    EXPECT_EQ("foo", ReceiveString(server_.get()));
+
+    EXPECT_TRUE(SendString(server_.get(), "bar baz"));
+    EXPECT_EQ("bar baz", ReceiveString(client_.get()));
+}
+
+// Tests sending and receiving large packets.
+TEST_F(SocketTest, LargePackets) {
+    std::string message(512, '\0');
+
+    ASSERT_TRUE(StartServer());
+    ASSERT_TRUE(StartClient());
+
+    // Run through the test a few times.
+    for (int i = 0; i < 10; ++i) {
+        // Use a different message each iteration to prevent false positives.
+        for (size_t j = 0; j < message.length(); ++j) {
+            message[j] = static_cast<char>(i + j);
+        }
+
+        EXPECT_TRUE(SendString(client_.get(), message));
+        EXPECT_EQ(message, ReceiveString(server_.get(), message.length()));
+    }
+}
+
+// Tests IPv4 client/server.
+TEST_F(SocketTest, IPv4) {
+    ASSERT_TRUE(StartServer());
+    ASSERT_TRUE(StartClient("127.0.0.1"));
+
+    EXPECT_TRUE(SendString(client_.get(), "foo"));
+    EXPECT_EQ("foo", ReceiveString(server_.get()));
+
+    EXPECT_TRUE(SendString(server_.get(), "bar"));
+    EXPECT_EQ("bar", ReceiveString(client_.get()));
+}
+
+// Tests IPv6 client/server.
+TEST_F(SocketTest, IPv6) {
+    ASSERT_TRUE(StartServer());
+    ASSERT_TRUE(StartClient("::1"));
+
+    EXPECT_TRUE(SendString(client_.get(), "foo"));
+    EXPECT_EQ("foo", ReceiveString(server_.get()));
+
+    EXPECT_TRUE(SendString(server_.get(), "bar"));
+    EXPECT_EQ("bar", ReceiveString(client_.get()));
+}
+
+// Tests receive timeout. The timing verification logic must be very coarse to make sure different
+// systems running different loads can all pass these tests.
+TEST_F(SocketTest, ReceiveTimeout) {
+    time_t start_time;
+
+    ASSERT_TRUE(StartServer());
+
+    // Make sure a 20ms timeout completes in 1 second or less.
+    start_time = time(nullptr);
+    EXPECT_TRUE(ReceiveTimeout(server_.get(), 20));
+    EXPECT_LE(difftime(time(nullptr), start_time), 1.0);
+
+    // Make sure a 1250ms timeout takes 1 second or more.
+    start_time = time(nullptr);
+    EXPECT_TRUE(ReceiveTimeout(server_.get(), 1250));
+    EXPECT_LE(1.0, difftime(time(nullptr), start_time));
+}
+
+// Tests receive overflow (the UDP packet is larger than the receive buffer).
+TEST_F(SocketTest, ReceiveOverflow) {
+    ASSERT_TRUE(StartServer());
+    ASSERT_TRUE(StartClient());
+
+    EXPECT_TRUE(SendString(client_.get(), "1234567890"));
+
+    // This behaves differently on different systems; some give us a truncated UDP packet, others
+    // will error out and not return anything at all.
+    std::string rx_string = ReceiveString(server_.get(), 5);
+
+    // If we didn't get an error then the packet should have been truncated.
+    if (rx_string != kReceiveStringError) {
+        EXPECT_EQ("12345", rx_string);
+    }
+}
+
+// Tests multiple clients sending to the same server.
+TEST_F(SocketTest, MultipleClients) {
+    ASSERT_TRUE(StartServer());
+    ASSERT_TRUE(StartClient());
+    ASSERT_TRUE(StartClient2());
+
+    EXPECT_TRUE(SendString(client_.get(), "client"));
+    EXPECT_TRUE(SendString(client2_.get(), "client2"));
+
+    // Receive the packets and send a response for each (note that packets may be received
+    // out-of-order).
+    for (int i = 0; i < 2; ++i) {
+        std::string received = ReceiveString(server_.get());
+        EXPECT_TRUE(SendString(server_.get(), received + " response"));
+    }
+
+    EXPECT_EQ("client response", ReceiveString(client_.get()));
+    EXPECT_EQ("client2 response", ReceiveString(client2_.get()));
+}
diff --git a/fastboot/socket_unix.cpp b/fastboot/socket_unix.cpp
new file mode 100644
index 0000000..462256a
--- /dev/null
+++ b/fastboot/socket_unix.cpp
@@ -0,0 +1,131 @@
+/*
+ * 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 "socket.h"
+
+#include <errno.h>
+#include <netdb.h>
+
+#include <android-base/stringprintf.h>
+#include <cutils/sockets.h>
+
+class UnixUdpSocket : public UdpSocket {
+  public:
+    enum class Type { kClient, kServer };
+
+    UnixUdpSocket(int fd, Type type);
+    ~UnixUdpSocket() override;
+
+    ssize_t Send(const void* data, size_t length) override;
+    ssize_t Receive(void* data, size_t length, int timeout_ms) override;
+    int Close() override;
+
+  private:
+    int fd_;
+    int receive_timeout_ms_ = 0;
+    std::unique_ptr<sockaddr_storage> addr_;
+    socklen_t addr_size_ = 0;
+
+    DISALLOW_COPY_AND_ASSIGN(UnixUdpSocket);
+};
+
+UnixUdpSocket::UnixUdpSocket(int fd, Type type) : fd_(fd) {
+    // Only servers need to remember addresses; clients are connected to a server in NewUdpClient()
+    // so will send to that server without needing to specify the address again.
+    if (type == Type::kServer) {
+        addr_.reset(new sockaddr_storage);
+        addr_size_ = sizeof(*addr_);
+        memset(addr_.get(), 0, addr_size_);
+    }
+}
+
+UnixUdpSocket::~UnixUdpSocket() {
+    Close();
+}
+
+ssize_t UnixUdpSocket::Send(const void* data, size_t length) {
+    return TEMP_FAILURE_RETRY(
+            sendto(fd_, data, length, 0, reinterpret_cast<sockaddr*>(addr_.get()), addr_size_));
+}
+
+ssize_t UnixUdpSocket::Receive(void* data, size_t length, int timeout_ms) {
+    // Only set socket timeout if it's changed.
+    if (receive_timeout_ms_ != timeout_ms) {
+        timeval tv;
+        tv.tv_sec = timeout_ms / 1000;
+        tv.tv_usec = (timeout_ms % 1000) * 1000;
+        if (setsockopt(fd_, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
+            return -1;
+        }
+        receive_timeout_ms_ = timeout_ms;
+    }
+
+    socklen_t* addr_size_ptr = nullptr;
+    if (addr_ != nullptr) {
+        // Reset addr_size as it may have been modified by previous recvfrom() calls.
+        addr_size_ = sizeof(*addr_);
+        addr_size_ptr = &addr_size_;
+    }
+    return TEMP_FAILURE_RETRY(recvfrom(fd_, data, length, 0,
+                                       reinterpret_cast<sockaddr*>(addr_.get()), addr_size_ptr));
+}
+
+int UnixUdpSocket::Close() {
+    int result = 0;
+    if (fd_ != -1) {
+        result = close(fd_);
+        fd_ = -1;
+    }
+    return result;
+}
+
+std::unique_ptr<UdpSocket> UdpSocket::NewUdpClient(const std::string& host, int port,
+                                                   std::string* error) {
+    int getaddrinfo_error = 0;
+    int fd = socket_network_client_timeout(host.c_str(), port, SOCK_DGRAM, 0, &getaddrinfo_error);
+    if (fd == -1) {
+        if (error) {
+            *error = android::base::StringPrintf(
+                    "Failed to connect to %s:%d: %s", host.c_str(), port,
+                    getaddrinfo_error ? gai_strerror(getaddrinfo_error) : strerror(errno));
+        }
+        return nullptr;
+    }
+
+    return std::unique_ptr<UdpSocket>(new UnixUdpSocket(fd, UnixUdpSocket::Type::kClient));
+}
+
+std::unique_ptr<UdpSocket> UdpSocket::NewUdpServer(int port) {
+    int fd = socket_inaddr_any_server(port, SOCK_DGRAM);
+    if (fd == -1) {
+        // This is just used in testing, no need for an error message.
+        return nullptr;
+    }
+
+    return std::unique_ptr<UdpSocket>(new UnixUdpSocket(fd, UnixUdpSocket::Type::kServer));
+}
diff --git a/fastboot/socket_windows.cpp b/fastboot/socket_windows.cpp
new file mode 100644
index 0000000..4ad379f
--- /dev/null
+++ b/fastboot/socket_windows.cpp
@@ -0,0 +1,246 @@
+/*
+ * 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 "socket.h"
+
+#include <winsock2.h>
+#include <ws2tcpip.h>
+
+#include <memory>
+
+#include <android-base/stringprintf.h>
+
+// Windows UDP socket functionality.
+class WindowsUdpSocket : public UdpSocket {
+  public:
+    enum class Type { kClient, kServer };
+
+    WindowsUdpSocket(SOCKET sock, Type type);
+    ~WindowsUdpSocket() override;
+
+    ssize_t Send(const void* data, size_t len) override;
+    ssize_t Receive(void* data, size_t len, int timeout_ms) override;
+    int Close() override;
+
+  private:
+    SOCKET sock_;
+    int receive_timeout_ms_ = 0;
+    std::unique_ptr<sockaddr_storage> addr_;
+    int addr_size_ = 0;
+
+    DISALLOW_COPY_AND_ASSIGN(WindowsUdpSocket);
+};
+
+WindowsUdpSocket::WindowsUdpSocket(SOCKET sock, Type type) : sock_(sock) {
+    // Only servers need to remember addresses; clients are connected to a server in NewUdpClient()
+    // so will send to that server without needing to specify the address again.
+    if (type == Type::kServer) {
+        addr_.reset(new sockaddr_storage);
+        addr_size_ = sizeof(*addr_);
+        memset(addr_.get(), 0, addr_size_);
+    }
+}
+
+WindowsUdpSocket::~WindowsUdpSocket() {
+    Close();
+}
+
+ssize_t WindowsUdpSocket::Send(const void* data, size_t len) {
+    return sendto(sock_, reinterpret_cast<const char*>(data), len, 0,
+                  reinterpret_cast<sockaddr*>(addr_.get()), addr_size_);
+}
+
+ssize_t WindowsUdpSocket::Receive(void* data, size_t len, int timeout_ms) {
+    // Only set socket timeout if it's changed.
+    if (receive_timeout_ms_ != timeout_ms) {
+        if (setsockopt(sock_, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<const char*>(&timeout_ms),
+                       sizeof(timeout_ms)) < 0) {
+            return -1;
+        }
+        receive_timeout_ms_ = timeout_ms;
+    }
+
+    int* addr_size_ptr = nullptr;
+    if (addr_ != nullptr) {
+        // Reset addr_size as it may have been modified by previous recvfrom() calls.
+        addr_size_ = sizeof(*addr_);
+        addr_size_ptr = &addr_size_;
+    }
+    int result = recvfrom(sock_, reinterpret_cast<char*>(data), len, 0,
+                          reinterpret_cast<sockaddr*>(addr_.get()), addr_size_ptr);
+    if (result < 0 && WSAGetLastError() == WSAETIMEDOUT) {
+        errno = EAGAIN;
+    }
+    return result;
+}
+
+int WindowsUdpSocket::Close() {
+    int result = 0;
+    if (sock_ != INVALID_SOCKET) {
+        result = closesocket(sock_);
+        sock_ = INVALID_SOCKET;
+    }
+    return result;
+}
+
+static int GetProtocol(int sock_type) {
+    switch (sock_type) {
+        case SOCK_DGRAM:
+            return IPPROTO_UDP;
+        case SOCK_STREAM:
+            return IPPROTO_TCP;
+        default:
+            // 0 lets the system decide which protocol to use.
+            return 0;
+    }
+}
+
+// Windows implementation of this libcutils function. This function does not make any calls to
+// WSAStartup() or WSACleanup() so that must be handled by the caller.
+// TODO(dpursell): share this code with adb.
+static SOCKET socket_network_client(const std::string& host, int port, int type) {
+    // First resolve the host and port parameters into a usable network address.
+    addrinfo hints;
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_socktype = type;
+    hints.ai_protocol = GetProtocol(type);
+
+    addrinfo* address = nullptr;
+    getaddrinfo(host.c_str(), android::base::StringPrintf("%d", port).c_str(), &hints, &address);
+    if (address == nullptr) {
+        return INVALID_SOCKET;
+    }
+
+    // Now create and connect the socket.
+    SOCKET sock = socket(address->ai_family, address->ai_socktype, address->ai_protocol);
+    if (sock == INVALID_SOCKET) {
+        freeaddrinfo(address);
+        return INVALID_SOCKET;
+    }
+
+    if (connect(sock, address->ai_addr, address->ai_addrlen) == SOCKET_ERROR) {
+        closesocket(sock);
+        freeaddrinfo(address);
+        return INVALID_SOCKET;
+    }
+
+    freeaddrinfo(address);
+    return sock;
+}
+
+// Windows implementation of this libcutils function. This implementation creates a dual-stack
+// server socket that can accept incoming IPv4 or IPv6 packets. This function does not make any
+// calls to WSAStartup() or WSACleanup() so that must be handled by the caller.
+// TODO(dpursell): share this code with adb.
+static SOCKET socket_inaddr_any_server(int port, int type) {
+    SOCKET sock = socket(AF_INET6, type, GetProtocol(type));
+    if (sock == INVALID_SOCKET) {
+        return INVALID_SOCKET;
+    }
+
+    // Enforce exclusive addresses (1), and enable dual-stack so both IPv4 and IPv6 work (2).
+    // (1) https://msdn.microsoft.com/en-us/library/windows/desktop/ms740621(v=vs.85).aspx.
+    // (2) https://msdn.microsoft.com/en-us/library/windows/desktop/bb513665(v=vs.85).aspx.
+    int exclusive = 1;
+    DWORD v6_only = 0;
+    if (setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, reinterpret_cast<const char*>(&exclusive),
+                   sizeof(exclusive)) == SOCKET_ERROR ||
+        setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<const char*>(&v6_only),
+                   sizeof(v6_only)) == SOCKET_ERROR) {
+        closesocket(sock);
+        return INVALID_SOCKET;
+    }
+
+    // Bind the socket to our local port.
+    sockaddr_in6 addr;
+    memset(&addr, 0, sizeof(addr));
+    addr.sin6_family = AF_INET6;
+    addr.sin6_port = htons(port);
+    addr.sin6_addr = in6addr_any;
+    if (bind(sock, reinterpret_cast<sockaddr*>(&addr), sizeof(addr)) == SOCKET_ERROR) {
+        closesocket(sock);
+        return INVALID_SOCKET;
+    }
+
+    return sock;
+}
+
+// Documentation at https://msdn.microsoft.com/en-us/library/windows/desktop/ms741549(v=vs.85).aspx
+// claims WSACleanup() should be called before program exit, but general consensus seems to be that
+// it hasn't actually been necessary for a long time, possibly since Windows 3.1.
+//
+// Both adb (1) and Chrome (2) purposefully avoid WSACleanup(), and since no adverse affects have
+// been found we may as well do the same here to keep this code simpler.
+// (1) https://android.googlesource.com/platform/system/core.git/+/master/adb/sysdeps_win32.cpp#816
+// (2) https://code.google.com/p/chromium/codesearch#chromium/src/net/base/winsock_init.cc&l=35
+static bool InitWinsock() {
+    static bool init_success = false;
+
+    if (!init_success) {
+        WSADATA wsaData;
+        init_success = (WSAStartup(MAKEWORD(2, 2), &wsaData) == 0);
+    }
+
+    return init_success;
+}
+
+std::unique_ptr<UdpSocket> UdpSocket::NewUdpClient(const std::string& host, int port,
+                                                   std::string* error) {
+    if (!InitWinsock()) {
+        if (error) {
+            *error = android::base::StringPrintf("Failed to initialize Winsock (error %d)",
+                                                 WSAGetLastError());
+        }
+        return nullptr;
+    }
+
+    SOCKET sock = socket_network_client(host, port, SOCK_DGRAM);
+    if (sock == INVALID_SOCKET) {
+        if (error) {
+            *error = android::base::StringPrintf("Failed to connect to %s:%d (error %d)",
+                                                 host.c_str(), port, WSAGetLastError());
+        }
+        return nullptr;
+    }
+
+    return std::unique_ptr<UdpSocket>(new WindowsUdpSocket(sock, WindowsUdpSocket::Type::kClient));
+}
+
+// This functionality is currently only used by tests so we don't need any error messages.
+std::unique_ptr<UdpSocket> UdpSocket::NewUdpServer(int port) {
+    if (!InitWinsock()) {
+        return nullptr;
+    }
+
+    SOCKET sock = socket_inaddr_any_server(port, SOCK_DGRAM);
+    if (sock == INVALID_SOCKET) {
+        return nullptr;
+    }
+
+    return std::unique_ptr<UdpSocket>(new WindowsUdpSocket(sock, WindowsUdpSocket::Type::kServer));
+}
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 6800ad2..46bad4e 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -699,7 +699,10 @@
 
     GRSurface** scale_frames;
     int scale_count;
-    ret = res_create_multi_display_surface("charger/battery_scale", &scale_count, &scale_frames);
+    int scale_fps;  // Not in use (charger/battery_scale doesn't have FPS text
+                    // chunk). We are using hard-coded frame.disp_time instead.
+    ret = res_create_multi_display_surface("charger/battery_scale", &scale_count, &scale_fps,
+                                           &scale_frames);
     if (ret < 0) {
         LOGE("Cannot load battery_scale image\n");
         charger->batt_anim->num_frames = 0;
diff --git a/include/nativeloader/native_loader.h b/include/nativeloader/native_loader.h
index e7c69d6..da07253 100644
--- a/include/nativeloader/native_loader.h
+++ b/include/nativeloader/native_loader.h
@@ -24,7 +24,8 @@
 
 __attribute__((visibility("default")))
 void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* path,
-                        jobject class_loader, jstring library_path, jstring permitted_path);
+                        jobject class_loader, bool is_shared, jstring library_path,
+                        jstring permitted_path);
 
 };  // namespace android
 
diff --git a/libcutils/ashmem-host.c b/libcutils/ashmem-host.c
index abc4f94..15dd43e 100644
--- a/libcutils/ashmem-host.c
+++ b/libcutils/ashmem-host.c
@@ -43,11 +43,16 @@
     char template[PATH_MAX];
     snprintf(template, sizeof(template), "/tmp/android-ashmem-%d-XXXXXXXXX", getpid());
     int fd = mkstemp(template);
-    if (fd != -1 && TEMP_FAILURE_RETRY(ftruncate(fd, size)) != -1 && unlink(template) != -1) {
-        return fd;
+    if (fd == -1) return -1;
+
+    unlink(template);
+
+    if (TEMP_FAILURE_RETRY(ftruncate(fd, size)) == -1) {
+      close(fd);
+      return -1;
     }
-    close(fd);
-    return -1;
+
+    return fd;
 }
 
 int ashmem_set_prot_region(int fd __unused, int prot __unused)
diff --git a/libcutils/trace-dev.c b/libcutils/trace-dev.c
index a06987e..f025256 100644
--- a/libcutils/trace-dev.c
+++ b/libcutils/trace-dev.c
@@ -104,7 +104,7 @@
 
     if (sys_debuggable || atrace_is_debuggable) {
         // Check whether tracing is enabled for this process.
-        FILE * file = fopen("/proc/self/cmdline", "r");
+        FILE * file = fopen("/proc/self/cmdline", "re");
         if (file) {
             char cmdline[4096];
             if (fgets(cmdline, sizeof(cmdline), file)) {
@@ -173,7 +173,7 @@
 
 static void atrace_init_once()
 {
-    atrace_marker_fd = open("/sys/kernel/debug/tracing/trace_marker", O_WRONLY);
+    atrace_marker_fd = open("/sys/kernel/debug/tracing/trace_marker", O_WRONLY | O_CLOEXEC);
     if (atrace_marker_fd == -1) {
         ALOGE("Error opening trace file: %s (%d)", strerror(errno), errno);
         atrace_enabled_tags = 0;
diff --git a/liblog/log_is_loggable.c b/liblog/log_is_loggable.c
index b4711d2..0f81efc 100644
--- a/liblog/log_is_loggable.c
+++ b/liblog/log_is_loggable.c
@@ -25,13 +25,18 @@
 
 static pthread_mutex_t lock_loggable = PTHREAD_MUTEX_INITIALIZER;
 
-static void lock()
+static int lock()
 {
     /*
      * If we trigger a signal handler in the middle of locked activity and the
      * signal handler logs a message, we could get into a deadlock state.
      */
-    pthread_mutex_lock(&lock_loggable);
+    /*
+     *  Any contention, and we can turn around and use the non-cached method
+     * in less time than the system call associated with a mutex to deal with
+     * the contention.
+     */
+    return pthread_mutex_trylock(&lock_loggable);
 }
 
 static void unlock()
@@ -45,6 +50,12 @@
     unsigned char c;
 };
 
+static int check_cache(struct cache *cache)
+{
+    return cache->pinfo
+        && __system_property_serial(cache->pinfo) != cache->serial;
+}
+
 #define BOOLEAN_TRUE 0xFF
 #define BOOLEAN_FALSE 0xFE
 
@@ -58,6 +69,7 @@
         if (!cache->pinfo) {
             return;
         }
+        cache->serial = -1;
     }
     serial = __system_property_serial(cache->pinfo);
     if (serial == cache->serial) {
@@ -85,7 +97,7 @@
     /* calculate the size of our key temporary buffer */
     const size_t taglen = (tag && *tag) ? strlen(tag) : 0;
     /* sizeof(log_namespace) = strlen(log_namespace) + 1 */
-    char key[sizeof(log_namespace) + taglen];
+    char key[sizeof(log_namespace) + taglen]; /* may be > PROPERTY_KEY_MAX */
     char *kp;
     size_t i;
     char c = 0;
@@ -100,49 +112,77 @@
      */
     static char *last_tag;
     static uint32_t global_serial;
-    uint32_t current_global_serial;
-    static struct cache tag_cache[2] = {
-        { NULL, -1, 0 },
-        { NULL, -1, 0 }
-    };
-    static struct cache global_cache[2] = {
-        { NULL, -1, 0 },
-        { NULL, -1, 0 }
-    };
+    /* some compilers erroneously see uninitialized use. !not_locked */
+    uint32_t current_global_serial = 0;
+    static struct cache tag_cache[2];
+    static struct cache global_cache[2];
+    int change_detected;
+    int global_change_detected;
+    int not_locked;
 
     strcpy(key, log_namespace);
 
-    lock();
+    global_change_detected = change_detected = not_locked = lock();
 
-    current_global_serial = __system_property_area_serial();
+    if (!not_locked) {
+        /*
+         *  check all known serial numbers to changes.
+         */
+        for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
+            if (check_cache(&tag_cache[i])) {
+                change_detected = 1;
+            }
+        }
+        for (i = 0; i < (sizeof(global_cache) / sizeof(global_cache[0])); ++i) {
+            if (check_cache(&global_cache[i])) {
+                global_change_detected = 1;
+            }
+        }
+
+        current_global_serial = __system_property_area_serial();
+        if (current_global_serial != global_serial) {
+            change_detected = 1;
+            global_change_detected = 1;
+        }
+    }
 
     if (taglen) {
-        uint32_t current_local_serial = current_global_serial;
-
-        if (!last_tag || (last_tag[0] != tag[0]) || strcmp(last_tag + 1, tag + 1)) {
-            /* invalidate log.tag.<tag> cache */
-            for(i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
-                tag_cache[i].pinfo = NULL;
-                tag_cache[i].serial = -1;
-                tag_cache[i].c = '\0';
+        int local_change_detected = change_detected;
+        if (!not_locked) {
+            if (!last_tag
+                    || (last_tag[0] != tag[0])
+                    || strcmp(last_tag + 1, tag + 1)) {
+                /* invalidate log.tag.<tag> cache */
+                for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
+                    tag_cache[i].pinfo = NULL;
+                    tag_cache[i].c = '\0';
+                }
+                free(last_tag);
+                last_tag = NULL;
+                local_change_detected = 1;
             }
-            free(last_tag);
-            last_tag = NULL;
-            current_global_serial = -1;
-        }
-        if (!last_tag) {
-            last_tag = strdup(tag);
+            if (!last_tag) {
+                last_tag = strdup(tag);
+            }
         }
         strcpy(key + sizeof(log_namespace) - 1, tag);
 
         kp = key;
-        for(i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
-            if (current_local_serial != global_serial) {
-                refresh_cache(&tag_cache[i], kp);
+        for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
+            struct cache *cache = &tag_cache[i];
+            struct cache temp_cache;
+
+            if (not_locked) {
+                temp_cache.pinfo = NULL;
+                temp_cache.c = '\0';
+                cache = &temp_cache;
+            }
+            if (local_change_detected) {
+                refresh_cache(cache, kp);
             }
 
-            if (tag_cache[i].c) {
-                c = tag_cache[i].c;
+            if (cache->c) {
+                c = cache->c;
                 break;
             }
 
@@ -166,13 +206,24 @@
         key[sizeof(log_namespace) - 2] = '\0';
 
         kp = key;
-        for(i = 0; i < (sizeof(global_cache) / sizeof(global_cache[0])); ++i) {
-            if (current_global_serial != global_serial) {
-                refresh_cache(&global_cache[i], kp);
+        for (i = 0; i < (sizeof(global_cache) / sizeof(global_cache[0])); ++i) {
+            struct cache *cache = &global_cache[i];
+            struct cache temp_cache;
+
+            if (not_locked) {
+                temp_cache = *cache;
+                if (temp_cache.pinfo != cache->pinfo) { /* check atomic */
+                    temp_cache.pinfo = NULL;
+                    temp_cache.c = '\0';
+                }
+                cache = &temp_cache;
+            }
+            if (global_change_detected) {
+                refresh_cache(cache, kp);
             }
 
-            if (global_cache[i].c) {
-                c = global_cache[i].c;
+            if (cache->c) {
+                c = cache->c;
                 break;
             }
 
@@ -181,9 +232,10 @@
         break;
     }
 
-    global_serial = current_global_serial;
-
-    unlock();
+    if (!not_locked) {
+        global_serial = current_global_serial;
+        unlock();
+    }
 
     switch (toupper(c)) {
     case 'V': return ANDROID_LOG_VERBOSE;
@@ -206,70 +258,103 @@
 }
 
 /*
- * Timestamp state generally remains constant, since a change is
- * rare, we can accept a trylock failure gracefully. Use a separate
- * lock from is_loggable to keep contention down b/25563384.
+ * For properties that are read often, but generally remain constant.
+ * Since a change is rare, we will accept a trylock failure gracefully.
+ * Use a separate lock from is_loggable to keep contention down b/25563384.
  */
-static pthread_mutex_t lock_clockid = PTHREAD_MUTEX_INITIALIZER;
+struct cache2 {
+    pthread_mutex_t lock;
+    uint32_t serial;
+    const char *key_persist;
+    struct cache cache_persist;
+    const char *key_ro;
+    struct cache cache_ro;
+    unsigned char (*const evaluate)(const struct cache2 *self);
+};
 
-clockid_t android_log_clockid()
+static inline unsigned char do_cache2(struct cache2 *self)
 {
-    static struct cache r_time_cache = { NULL, -1, 0 };
-    static struct cache p_time_cache = { NULL, -1, 0 };
-    char c;
+    uint32_t current_serial;
+    int change_detected;
+    unsigned char c;
 
-    if (pthread_mutex_trylock(&lock_clockid)) {
+    if (pthread_mutex_trylock(&self->lock)) {
         /* We are willing to accept some race in this context */
-        if (!(c = p_time_cache.c)) {
-            c = r_time_cache.c;
-        }
-    } else {
-        static uint32_t serial;
-        uint32_t 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 (!(c = p_time_cache.c)) {
-            c = r_time_cache.c;
-        }
-
-        pthread_mutex_unlock(&lock_clockid);
+        return self->evaluate(self);
     }
 
-    return (tolower(c) == 'm') ? CLOCK_MONOTONIC : CLOCK_REALTIME;
+    change_detected = check_cache(&self->cache_persist)
+                   || check_cache(&self->cache_ro);
+    current_serial = __system_property_area_serial();
+    if (current_serial != self->serial) {
+        change_detected = 1;
+    }
+    if (change_detected) {
+        refresh_cache(&self->cache_persist, self->key_persist);
+        refresh_cache(&self->cache_ro, self->key_ro);
+        self->serial = current_serial;
+    }
+    c = self->evaluate(self);
+
+    pthread_mutex_unlock(&self->lock);
+
+    return c;
+}
+
+static unsigned char evaluate_persist_ro(const struct cache2 *self)
+{
+    unsigned char c = self->cache_persist.c;
+
+    if (c) {
+        return c;
+    }
+
+    return self->cache_ro.c;
 }
 
 /*
- * security state generally remains constant, since a change is
- * rare, we can accept a trylock failure gracefully.
+ * Timestamp state generally remains constant, but can change at any time
+ * to handle developer requirements.
  */
-static pthread_mutex_t lock_security = PTHREAD_MUTEX_INITIALIZER;
+clockid_t android_log_clockid()
+{
+    static struct cache2 clockid = {
+        PTHREAD_MUTEX_INITIALIZER,
+        0,
+        "persist.logd.timestamp",
+        { NULL, -1, '\0' },
+        "ro.logd.timestamp",
+        { NULL, -1, '\0' },
+        evaluate_persist_ro
+    };
+
+    return (tolower(do_cache2(&clockid)) == 'm')
+        ? CLOCK_MONOTONIC
+        : CLOCK_REALTIME;
+}
+
+/*
+ * Security state generally remains constant, but the DO must be able
+ * to turn off logging should it become spammy after an attack is detected.
+ */
+static unsigned char evaluate_security(const struct cache2 *self)
+{
+    unsigned char c = self->cache_ro.c;
+
+    return (c != BOOLEAN_FALSE) && c && (self->cache_persist.c == BOOLEAN_TRUE);
+}
 
 int __android_log_security()
 {
-    static struct cache r_do_cache = { NULL, -1, BOOLEAN_FALSE };
-    static struct cache p_security_cache = { NULL, -1, BOOLEAN_FALSE };
-    int retval;
+    static struct cache2 security = {
+        PTHREAD_MUTEX_INITIALIZER,
+        0,
+        "persist.logd.security",
+        { NULL, -1, BOOLEAN_FALSE },
+        "ro.device_owner",
+        { NULL, -1, BOOLEAN_FALSE },
+        evaluate_security
+    };
 
-    if (pthread_mutex_trylock(&lock_security)) {
-        /* We are willing to accept some race in this context */
-        retval = (r_do_cache.c != BOOLEAN_FALSE) && r_do_cache.c &&
-                 (p_security_cache.c == BOOLEAN_TRUE);
-    } else {
-        static uint32_t serial;
-        uint32_t current_serial = __system_property_area_serial();
-        if (current_serial != serial) {
-            refresh_cache(&r_do_cache, "ro.device_owner");
-            refresh_cache(&p_security_cache, "persist.logd.security");
-            serial = current_serial;
-        }
-        retval = (r_do_cache.c != BOOLEAN_FALSE) && r_do_cache.c &&
-                 (p_security_cache.c == BOOLEAN_TRUE);
-
-        pthread_mutex_unlock(&lock_security);
-    }
-
-    return retval;
+    return do_cache2(&security);
 }
diff --git a/liblog/log_read.c b/liblog/log_read.c
index 8aa887b..1aff272 100644
--- a/liblog/log_read.c
+++ b/liblog/log_read.c
@@ -501,6 +501,14 @@
         remaining -= n;
         cp += n;
     }
+
+    if (logger_list->pid) {
+        n = snprintf(cp, remaining, " pid=%u", logger_list->pid);
+        n = min(n, remaining);
+        remaining -= n;
+        cp += n;
+    }
+
     return send_log_msg(NULL, NULL, buf, len);
 }
 
@@ -880,18 +888,10 @@
             sigaction(SIGALRM, &old_sigaction, NULL);
         }
 
-        if (ret <= 0) {
-            if ((ret == -1) && e) {
-                return -e;
-            }
-            return ret;
+        if ((ret == -1) && e) {
+            return -e;
         }
-
-        logger_for_each(logger, logger_list) {
-            if (log_msg->entry.lid == logger->id) {
-                return ret;
-            }
-        }
+        return ret;
     }
     /* NOTREACH */
     return ret;
diff --git a/liblog/tests/liblog_benchmark.cpp b/liblog/tests/liblog_benchmark.cpp
index b594634..01fb50f 100644
--- a/liblog/tests/liblog_benchmark.cpp
+++ b/liblog/tests/liblog_benchmark.cpp
@@ -14,11 +14,17 @@
  * limitations under the License.
  */
 
+#include <fcntl.h>
+#include <sys/endian.h>
 #include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
 #include <cutils/sockets.h>
 #include <log/log.h>
 #include <log/logger.h>
 #include <log/log_read.h>
+#include <private/android_logger.h>
 
 #include "benchmark.h"
 
@@ -85,6 +91,380 @@
 BENCHMARK(BM_clock_overhead);
 
 /*
+ * Measure the time it takes to submit the android logging data to pstore
+ */
+static void BM_pmsg_short(int iters) {
+
+    int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
+    if (pstore_fd < 0) {
+        return;
+    }
+
+    /*
+     *  struct {
+     *      // what we provide to pstore
+     *      android_pmsg_log_header_t pmsg_header;
+     *      // what we provide to socket
+     *      android_log_header_t header;
+     *      // caller provides
+     *      union {
+     *          struct {
+     *              char     prio;
+     *              char     payload[];
+     *          } string;
+     *          struct {
+     *              uint32_t tag
+     *              char     payload[];
+     *          } binary;
+     *      };
+     *  };
+     */
+
+    struct timespec ts;
+    clock_gettime(android_log_clockid(), &ts);
+
+    android_pmsg_log_header_t pmsg_header;
+    pmsg_header.magic = LOGGER_MAGIC;
+    pmsg_header.len = sizeof(android_pmsg_log_header_t)
+                    + sizeof(android_log_header_t);
+    pmsg_header.uid = getuid();
+    pmsg_header.pid = getpid();
+
+    android_log_header_t header;
+    header.tid = gettid();
+    header.realtime.tv_sec = ts.tv_sec;
+    header.realtime.tv_nsec = ts.tv_nsec;
+
+    static const unsigned nr = 1;
+    static const unsigned header_length = 2;
+    struct iovec newVec[nr + header_length];
+
+    newVec[0].iov_base   = (unsigned char *) &pmsg_header;
+    newVec[0].iov_len    = sizeof(pmsg_header);
+    newVec[1].iov_base   = (unsigned char *) &header;
+    newVec[1].iov_len    = sizeof(header);
+
+    android_log_event_int_t buffer;
+
+    header.id = LOG_ID_EVENTS;
+    buffer.header.tag = 0;
+    buffer.payload.type = EVENT_TYPE_INT;
+    uint32_t snapshot = 0;
+    buffer.payload.data = htole32(snapshot);
+
+    newVec[2].iov_base = &buffer;
+    newVec[2].iov_len  = sizeof(buffer);
+
+    StartBenchmarkTiming();
+    for (int i = 0; i < iters; ++i) {
+        ++snapshot;
+        buffer.payload.data = htole32(snapshot);
+        writev(pstore_fd, newVec, nr);
+    }
+    StopBenchmarkTiming();
+    close(pstore_fd);
+}
+BENCHMARK(BM_pmsg_short);
+
+/*
+ * Measure the time it takes to submit the android logging data to pstore
+ * best case aligned single block.
+ */
+static void BM_pmsg_short_aligned(int iters) {
+
+    int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
+    if (pstore_fd < 0) {
+        return;
+    }
+
+    /*
+     *  struct {
+     *      // what we provide to pstore
+     *      android_pmsg_log_header_t pmsg_header;
+     *      // what we provide to socket
+     *      android_log_header_t header;
+     *      // caller provides
+     *      union {
+     *          struct {
+     *              char     prio;
+     *              char     payload[];
+     *          } string;
+     *          struct {
+     *              uint32_t tag
+     *              char     payload[];
+     *          } binary;
+     *      };
+     *  };
+     */
+
+    struct timespec ts;
+    clock_gettime(android_log_clockid(), &ts);
+
+    struct packet {
+        android_pmsg_log_header_t pmsg_header;
+        android_log_header_t header;
+        android_log_event_int_t payload;
+    };
+    char buf[sizeof(struct packet) + 8] __aligned(8);
+    memset(buf, 0, sizeof(buf));
+    struct packet *buffer = (struct packet*)(((uintptr_t)buf + 7) & ~7);
+    if (((uintptr_t)&buffer->pmsg_header) & 7) {
+        fprintf (stderr, "&buffer=0x%p iters=%d\n", &buffer->pmsg_header, iters);
+    }
+
+    buffer->pmsg_header.magic = LOGGER_MAGIC;
+    buffer->pmsg_header.len = sizeof(android_pmsg_log_header_t)
+                            + sizeof(android_log_header_t);
+    buffer->pmsg_header.uid = getuid();
+    buffer->pmsg_header.pid = getpid();
+
+    buffer->header.tid = gettid();
+    buffer->header.realtime.tv_sec = ts.tv_sec;
+    buffer->header.realtime.tv_nsec = ts.tv_nsec;
+
+    buffer->header.id = LOG_ID_EVENTS;
+    buffer->payload.header.tag = 0;
+    buffer->payload.payload.type = EVENT_TYPE_INT;
+    uint32_t snapshot = 0;
+    buffer->payload.payload.data = htole32(snapshot);
+
+    StartBenchmarkTiming();
+    for (int i = 0; i < iters; ++i) {
+        ++snapshot;
+        buffer->payload.payload.data = htole32(snapshot);
+        write(pstore_fd, &buffer->pmsg_header,
+            sizeof(android_pmsg_log_header_t) +
+            sizeof(android_log_header_t) +
+            sizeof(android_log_event_int_t));
+    }
+    StopBenchmarkTiming();
+    close(pstore_fd);
+}
+BENCHMARK(BM_pmsg_short_aligned);
+
+/*
+ * Measure the time it takes to submit the android logging data to pstore
+ * best case aligned single block.
+ */
+static void BM_pmsg_short_unaligned1(int iters) {
+
+    int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
+    if (pstore_fd < 0) {
+        return;
+    }
+
+    /*
+     *  struct {
+     *      // what we provide to pstore
+     *      android_pmsg_log_header_t pmsg_header;
+     *      // what we provide to socket
+     *      android_log_header_t header;
+     *      // caller provides
+     *      union {
+     *          struct {
+     *              char     prio;
+     *              char     payload[];
+     *          } string;
+     *          struct {
+     *              uint32_t tag
+     *              char     payload[];
+     *          } binary;
+     *      };
+     *  };
+     */
+
+    struct timespec ts;
+    clock_gettime(android_log_clockid(), &ts);
+
+    struct packet {
+        android_pmsg_log_header_t pmsg_header;
+        android_log_header_t header;
+        android_log_event_int_t payload;
+    };
+    char buf[sizeof(struct packet) + 8] __aligned(8);
+    memset(buf, 0, sizeof(buf));
+    struct packet *buffer = (struct packet*)((((uintptr_t)buf + 7) & ~7) + 1);
+    if ((((uintptr_t)&buffer->pmsg_header) & 7) != 1) {
+        fprintf (stderr, "&buffer=0x%p iters=%d\n", &buffer->pmsg_header, iters);
+    }
+
+    buffer->pmsg_header.magic = LOGGER_MAGIC;
+    buffer->pmsg_header.len = sizeof(android_pmsg_log_header_t)
+                            + sizeof(android_log_header_t);
+    buffer->pmsg_header.uid = getuid();
+    buffer->pmsg_header.pid = getpid();
+
+    buffer->header.tid = gettid();
+    buffer->header.realtime.tv_sec = ts.tv_sec;
+    buffer->header.realtime.tv_nsec = ts.tv_nsec;
+
+    buffer->header.id = LOG_ID_EVENTS;
+    buffer->payload.header.tag = 0;
+    buffer->payload.payload.type = EVENT_TYPE_INT;
+    uint32_t snapshot = 0;
+    buffer->payload.payload.data = htole32(snapshot);
+
+    StartBenchmarkTiming();
+    for (int i = 0; i < iters; ++i) {
+        ++snapshot;
+        buffer->payload.payload.data = htole32(snapshot);
+        write(pstore_fd, &buffer->pmsg_header,
+            sizeof(android_pmsg_log_header_t) +
+            sizeof(android_log_header_t) +
+            sizeof(android_log_event_int_t));
+    }
+    StopBenchmarkTiming();
+    close(pstore_fd);
+}
+BENCHMARK(BM_pmsg_short_unaligned1);
+
+/*
+ * Measure the time it takes to submit the android logging data to pstore
+ * best case aligned single block.
+ */
+static void BM_pmsg_long_aligned(int iters) {
+
+    int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
+    if (pstore_fd < 0) {
+        return;
+    }
+
+    /*
+     *  struct {
+     *      // what we provide to pstore
+     *      android_pmsg_log_header_t pmsg_header;
+     *      // what we provide to socket
+     *      android_log_header_t header;
+     *      // caller provides
+     *      union {
+     *          struct {
+     *              char     prio;
+     *              char     payload[];
+     *          } string;
+     *          struct {
+     *              uint32_t tag
+     *              char     payload[];
+     *          } binary;
+     *      };
+     *  };
+     */
+
+    struct timespec ts;
+    clock_gettime(android_log_clockid(), &ts);
+
+    struct packet {
+        android_pmsg_log_header_t pmsg_header;
+        android_log_header_t header;
+        android_log_event_int_t payload;
+    };
+    char buf[sizeof(struct packet) + 8 + LOGGER_ENTRY_MAX_PAYLOAD] __aligned(8);
+    memset(buf, 0, sizeof(buf));
+    struct packet *buffer = (struct packet*)(((uintptr_t)buf + 7) & ~7);
+    if (((uintptr_t)&buffer->pmsg_header) & 7) {
+        fprintf (stderr, "&buffer=0x%p iters=%d\n", &buffer->pmsg_header, iters);
+    }
+
+    buffer->pmsg_header.magic = LOGGER_MAGIC;
+    buffer->pmsg_header.len = sizeof(android_pmsg_log_header_t)
+                            + sizeof(android_log_header_t);
+    buffer->pmsg_header.uid = getuid();
+    buffer->pmsg_header.pid = getpid();
+
+    buffer->header.tid = gettid();
+    buffer->header.realtime.tv_sec = ts.tv_sec;
+    buffer->header.realtime.tv_nsec = ts.tv_nsec;
+
+    buffer->header.id = LOG_ID_EVENTS;
+    buffer->payload.header.tag = 0;
+    buffer->payload.payload.type = EVENT_TYPE_INT;
+    uint32_t snapshot = 0;
+    buffer->payload.payload.data = htole32(snapshot);
+
+    StartBenchmarkTiming();
+    for (int i = 0; i < iters; ++i) {
+        ++snapshot;
+        buffer->payload.payload.data = htole32(snapshot);
+        write(pstore_fd, &buffer->pmsg_header, LOGGER_ENTRY_MAX_PAYLOAD);
+    }
+    StopBenchmarkTiming();
+    close(pstore_fd);
+}
+BENCHMARK(BM_pmsg_long_aligned);
+
+/*
+ * Measure the time it takes to submit the android logging data to pstore
+ * best case aligned single block.
+ */
+static void BM_pmsg_long_unaligned1(int iters) {
+
+    int pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));
+    if (pstore_fd < 0) {
+        return;
+    }
+
+    /*
+     *  struct {
+     *      // what we provide to pstore
+     *      android_pmsg_log_header_t pmsg_header;
+     *      // what we provide to socket
+     *      android_log_header_t header;
+     *      // caller provides
+     *      union {
+     *          struct {
+     *              char     prio;
+     *              char     payload[];
+     *          } string;
+     *          struct {
+     *              uint32_t tag
+     *              char     payload[];
+     *          } binary;
+     *      };
+     *  };
+     */
+
+    struct timespec ts;
+    clock_gettime(android_log_clockid(), &ts);
+
+    struct packet {
+        android_pmsg_log_header_t pmsg_header;
+        android_log_header_t header;
+        android_log_event_int_t payload;
+    };
+    char buf[sizeof(struct packet) + 8 + LOGGER_ENTRY_MAX_PAYLOAD] __aligned(8);
+    memset(buf, 0, sizeof(buf));
+    struct packet *buffer = (struct packet*)((((uintptr_t)buf + 7) & ~7) + 1);
+    if ((((uintptr_t)&buffer->pmsg_header) & 7) != 1) {
+        fprintf (stderr, "&buffer=0x%p iters=%d\n", &buffer->pmsg_header, iters);
+    }
+
+    buffer->pmsg_header.magic = LOGGER_MAGIC;
+    buffer->pmsg_header.len = sizeof(android_pmsg_log_header_t)
+                            + sizeof(android_log_header_t);
+    buffer->pmsg_header.uid = getuid();
+    buffer->pmsg_header.pid = getpid();
+
+    buffer->header.tid = gettid();
+    buffer->header.realtime.tv_sec = ts.tv_sec;
+    buffer->header.realtime.tv_nsec = ts.tv_nsec;
+
+    buffer->header.id = LOG_ID_EVENTS;
+    buffer->payload.header.tag = 0;
+    buffer->payload.payload.type = EVENT_TYPE_INT;
+    uint32_t snapshot = 0;
+    buffer->payload.payload.data = htole32(snapshot);
+
+    StartBenchmarkTiming();
+    for (int i = 0; i < iters; ++i) {
+        ++snapshot;
+        buffer->payload.payload.data = htole32(snapshot);
+        write(pstore_fd, &buffer->pmsg_header, LOGGER_ENTRY_MAX_PAYLOAD);
+    }
+    StopBenchmarkTiming();
+    close(pstore_fd);
+}
+BENCHMARK(BM_pmsg_long_unaligned1);
+
+/*
  *	Measure the time it takes to submit the android logging call using
  * discrete acquisition under light load. Expect this to be a dozen or so
  * syscall periods (40us).
@@ -280,3 +660,31 @@
     StopBenchmarkTiming();
 }
 BENCHMARK(BM_is_loggable);
+
+/*
+ *	Measure the time it takes for android_log_clockid.
+ */
+static void BM_clockid(int iters) {
+    StartBenchmarkTiming();
+
+    for (int i = 0; i < iters; ++i) {
+        android_log_clockid();
+    }
+
+    StopBenchmarkTiming();
+}
+BENCHMARK(BM_clockid);
+
+/*
+ *	Measure the time it takes for __android_log_security.
+ */
+static void BM_security(int iters) {
+    StartBenchmarkTiming();
+
+    for (int i = 0; i < iters; ++i) {
+        __android_log_security();
+    }
+
+    StopBenchmarkTiming();
+}
+BENCHMARK(BM_security);
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 621101c..50afc5f 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -866,13 +866,27 @@
                 continue;
             }
             fprintf(stderr, "i=%zu j=%zu\r", i, j);
+            bool android_log_is_loggable = __android_log_is_loggable(
+                levels[i].level, tag, levels[j].level);
             if ((levels[i].level < levels[j].level)
                     || (levels[j].level == -1)) {
-                EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
-                                                       levels[j].level));
+                if (android_log_is_loggable) {
+                    fprintf(stderr, "\n");
+                }
+                EXPECT_FALSE(android_log_is_loggable);
+                for(size_t k = 1000; k; --k) {
+                    EXPECT_FALSE(__android_log_is_loggable(
+                        levels[i].level, tag, levels[j].level));
+                }
             } else {
-                EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
-                                                      levels[j].level));
+                if (!android_log_is_loggable) {
+                    fprintf(stderr, "\n");
+                }
+                EXPECT_TRUE(android_log_is_loggable);
+                for(size_t k = 1000; k; --k) {
+                    EXPECT_TRUE(__android_log_is_loggable(
+                        levels[i].level, tag, levels[j].level));
+                }
             }
         }
     }
@@ -891,30 +905,58 @@
             fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r",
                     i, j, key, buf);
             property_set(key, buf);
+            bool android_log_is_loggable = __android_log_is_loggable(
+                levels[i].level, tag, ANDROID_LOG_DEBUG);
             if ((levels[i].level < levels[j].level)
                     || (levels[j].level == -1)
                     || ((levels[i].level < ANDROID_LOG_DEBUG)
                         && (levels[j].level == -2))) {
-                EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
-                                                       ANDROID_LOG_DEBUG));
+                if (android_log_is_loggable) {
+                    fprintf(stderr, "\n");
+                }
+                EXPECT_FALSE(android_log_is_loggable);
+                for(size_t k = 1000; k; --k) {
+                    EXPECT_FALSE(__android_log_is_loggable(
+                        levels[i].level, tag, ANDROID_LOG_DEBUG));
+                }
             } else {
-                EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
-                                                      ANDROID_LOG_DEBUG));
+                if (!android_log_is_loggable) {
+                    fprintf(stderr, "\n");
+                }
+                EXPECT_TRUE(android_log_is_loggable);
+                for(size_t k = 1000; k; --k) {
+                    EXPECT_TRUE(__android_log_is_loggable(
+                        levels[i].level, tag, ANDROID_LOG_DEBUG));
+                }
             }
             property_set(key, "");
 
             fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r",
                     i, j, key + base_offset, buf);
             property_set(key + base_offset, buf);
+            android_log_is_loggable = __android_log_is_loggable(
+                levels[i].level, tag, ANDROID_LOG_DEBUG);
             if ((levels[i].level < levels[j].level)
                     || (levels[j].level == -1)
                     || ((levels[i].level < ANDROID_LOG_DEBUG)
                         && (levels[j].level == -2))) {
-                EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
-                                                       ANDROID_LOG_DEBUG));
+                if (android_log_is_loggable) {
+                    fprintf(stderr, "\n");
+                }
+                EXPECT_FALSE(android_log_is_loggable);
+                for(size_t k = 1000; k; --k) {
+                    EXPECT_FALSE(__android_log_is_loggable(
+                        levels[i].level, tag, ANDROID_LOG_DEBUG));
+                }
             } else {
-                EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
-                                                      ANDROID_LOG_DEBUG));
+                if (!android_log_is_loggable) {
+                    fprintf(stderr, "\n");
+                }
+                EXPECT_TRUE(android_log_is_loggable);
+                for(size_t k = 1000; k; --k) {
+                    EXPECT_TRUE(__android_log_is_loggable(
+                        levels[i].level, tag, ANDROID_LOG_DEBUG));
+                }
             }
             property_set(key + base_offset, "");
 
@@ -923,30 +965,58 @@
             fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r",
                     i, j, key, buf);
             property_set(key, buf);
+            android_log_is_loggable = __android_log_is_loggable(
+                levels[i].level, tag, ANDROID_LOG_DEBUG);
             if ((levels[i].level < levels[j].level)
                     || (levels[j].level == -1)
                     || ((levels[i].level < ANDROID_LOG_DEBUG)
                         && (levels[j].level == -2))) {
-                EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
-                                                       ANDROID_LOG_DEBUG));
+                if (android_log_is_loggable) {
+                    fprintf(stderr, "\n");
+                }
+                EXPECT_FALSE(android_log_is_loggable);
+                for(size_t k = 1000; k; --k) {
+                    EXPECT_FALSE(__android_log_is_loggable(
+                        levels[i].level, tag, ANDROID_LOG_DEBUG));
+                }
             } else {
-                EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
-                                                      ANDROID_LOG_DEBUG));
+                if (!android_log_is_loggable) {
+                    fprintf(stderr, "\n");
+                }
+                EXPECT_TRUE(android_log_is_loggable);
+                for(size_t k = 1000; k; --k) {
+                    EXPECT_TRUE(__android_log_is_loggable(
+                        levels[i].level, tag, ANDROID_LOG_DEBUG));
+                }
             }
             property_set(key, "");
 
             fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r",
                     i, j, key + base_offset, buf);
             property_set(key + base_offset, buf);
+            android_log_is_loggable = __android_log_is_loggable(
+                levels[i].level, tag, ANDROID_LOG_DEBUG);
             if ((levels[i].level < levels[j].level)
                     || (levels[j].level == -1)
                     || ((levels[i].level < ANDROID_LOG_DEBUG)
                         && (levels[j].level == -2))) {
-                EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
-                                                       ANDROID_LOG_DEBUG));
+                if (android_log_is_loggable) {
+                    fprintf(stderr, "\n");
+                }
+                EXPECT_FALSE(android_log_is_loggable);
+                for(size_t k = 1000; k; --k) {
+                    EXPECT_FALSE(__android_log_is_loggable(
+                        levels[i].level, tag, ANDROID_LOG_DEBUG));
+                }
             } else {
-                EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
-                                                      ANDROID_LOG_DEBUG));
+                if (!android_log_is_loggable) {
+                    fprintf(stderr, "\n");
+                }
+                EXPECT_TRUE(android_log_is_loggable);
+                for(size_t k = 1000; k; --k) {
+                    EXPECT_TRUE(__android_log_is_loggable(
+                        levels[i].level, tag, ANDROID_LOG_DEBUG));
+                }
             }
             property_set(key + base_offset, "");
         }
@@ -969,30 +1039,58 @@
             fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r",
                     i, j, key, buf);
             property_set(key, buf);
+            bool android_log_is_loggable = __android_log_is_loggable(
+                levels[i].level, tag, ANDROID_LOG_DEBUG);
             if ((levels[i].level < levels[j].level)
                     || (levels[j].level == -1)
                     || ((levels[i].level < ANDROID_LOG_INFO) // Yes INFO
                         && (levels[j].level == -2))) {
-                EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
-                                                       ANDROID_LOG_DEBUG));
+                if (android_log_is_loggable) {
+                    fprintf(stderr, "\n");
+                }
+                EXPECT_FALSE(android_log_is_loggable);
+                for(size_t k = 1000; k; --k) {
+                    EXPECT_FALSE(__android_log_is_loggable(
+                        levels[i].level, tag, ANDROID_LOG_DEBUG));
+                }
             } else {
-                EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
-                                                      ANDROID_LOG_DEBUG));
+                if (!android_log_is_loggable) {
+                    fprintf(stderr, "\n");
+                }
+                EXPECT_TRUE(android_log_is_loggable);
+                for(size_t k = 1000; k; --k) {
+                    EXPECT_TRUE(__android_log_is_loggable(
+                        levels[i].level, tag, ANDROID_LOG_DEBUG));
+                }
             }
             property_set(key, "");
 
             fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r",
                     i, j, key + base_offset, buf);
             property_set(key + base_offset, buf);
+            android_log_is_loggable = __android_log_is_loggable(
+                levels[i].level, tag, ANDROID_LOG_DEBUG);
             if ((levels[i].level < levels[j].level)
                     || (levels[j].level == -1)
                     || ((levels[i].level < ANDROID_LOG_INFO) // Yes INFO
                         && (levels[j].level == -2))) {
-                EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
-                                                       ANDROID_LOG_DEBUG));
+                if (android_log_is_loggable) {
+                    fprintf(stderr, "\n");
+                }
+                EXPECT_FALSE(android_log_is_loggable);
+                for(size_t k = 1000; k; --k) {
+                    EXPECT_FALSE(__android_log_is_loggable(
+                        levels[i].level, tag, ANDROID_LOG_DEBUG));
+                }
             } else {
-                EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
-                                                      ANDROID_LOG_DEBUG));
+                if (!android_log_is_loggable) {
+                    fprintf(stderr, "\n");
+                }
+                EXPECT_TRUE(android_log_is_loggable);
+                for(size_t k = 1000; k; --k) {
+                    EXPECT_TRUE(__android_log_is_loggable(
+                        levels[i].level, tag, ANDROID_LOG_DEBUG));
+                }
             }
             property_set(key + base_offset, "");
         }
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index 17b7742..8020b41 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -56,6 +56,7 @@
   LibraryNamespaces() : initialized_(false) { }
 
   android_namespace_t* GetOrCreate(JNIEnv* env, jobject class_loader,
+                                   bool is_shared,
                                    jstring java_library_path,
                                    jstring java_permitted_path) {
     ScopedUtfChars library_path(env, java_library_path);
@@ -78,11 +79,16 @@
       return it->second;
     }
 
+    uint64_t namespace_type = ANDROID_NAMESPACE_TYPE_ISOLATED;
+    if (is_shared) {
+      namespace_type |= ANDROID_NAMESPACE_TYPE_SHARED;
+    }
+
     android_namespace_t* ns =
             android_create_namespace("classloader-namespace",
                                      nullptr,
                                      library_path.c_str(),
-                                     true,
+                                     namespace_type,
                                      java_permitted_path != nullptr ?
                                         permitted_path.c_str() :
                                         nullptr);
@@ -129,7 +135,7 @@
 
 
 void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* path,
-                        jobject class_loader, jstring java_library_path,
+                        jobject class_loader, bool is_shared, jstring java_library_path,
                         jstring java_permitted_path) {
 #if defined(__ANDROID__)
   if (target_sdk_version <= INT_MAX || class_loader == nullptr) {
@@ -137,8 +143,8 @@
   }
 
   android_namespace_t* ns =
-      g_namespaces->GetOrCreate(env, class_loader, java_library_path,
-                                java_permitted_path);
+      g_namespaces->GetOrCreate(env, class_loader, is_shared,
+                                java_library_path, java_permitted_path);
 
   if (ns == nullptr) {
     return nullptr;
@@ -150,7 +156,7 @@
 
   return android_dlopen_ext(path, RTLD_NOW, &extinfo);
 #else
-  UNUSED(env, target_sdk_version, class_loader,
+  UNUSED(env, target_sdk_version, class_loader, is_shared,
          java_library_path, java_permitted_path);
   return dlopen(path, RTLD_NOW);
 #endif
diff --git a/libpixelflinger/codeflinger/MIPS64Assembler.cpp b/libpixelflinger/codeflinger/MIPS64Assembler.cpp
index a5305cc..672040b 100644
--- a/libpixelflinger/codeflinger/MIPS64Assembler.cpp
+++ b/libpixelflinger/codeflinger/MIPS64Assembler.cpp
@@ -1304,9 +1304,8 @@
 
 MIPS64Assembler::MIPS64Assembler(void* assembly, ArmToMips64Assembler *parent)
     : mParent(parent),
-    MIPSAssembler::MIPSAssembler(NULL, NULL)
+    MIPSAssembler::MIPSAssembler(assembly)
 {
-    mBase = mPC = (uint32_t *)assembly;
 }
 
 MIPS64Assembler::~MIPS64Assembler()
diff --git a/libpixelflinger/codeflinger/MIPSAssembler.cpp b/libpixelflinger/codeflinger/MIPSAssembler.cpp
index daa231f..5497fae 100644
--- a/libpixelflinger/codeflinger/MIPSAssembler.cpp
+++ b/libpixelflinger/codeflinger/MIPSAssembler.cpp
@@ -1256,6 +1256,12 @@
     mDuration = ggl_system_time();
 }
 
+MIPSAssembler::MIPSAssembler(void* assembly)
+    : mParent(NULL), mAssembly(NULL)
+{
+    mBase = mPC = (uint32_t *)assembly;
+}
+
 MIPSAssembler::~MIPSAssembler()
 {
 }
diff --git a/libpixelflinger/codeflinger/MIPSAssembler.h b/libpixelflinger/codeflinger/MIPSAssembler.h
index 06cb0d0..b53fefb 100644
--- a/libpixelflinger/codeflinger/MIPSAssembler.h
+++ b/libpixelflinger/codeflinger/MIPSAssembler.h
@@ -242,6 +242,7 @@
 {
 public:
                 MIPSAssembler(const sp<Assembly>& assembly, ArmToMipsAssembler *parent);
+                MIPSAssembler(void* assembly);
     virtual     ~MIPSAssembler();
 
     virtual uint32_t*   base() const;
diff --git a/libsparse/append2simg.c b/libsparse/append2simg.c
index 1cf827c..eef8764 100644
--- a/libsparse/append2simg.c
+++ b/libsparse/append2simg.c
@@ -82,7 +82,7 @@
         exit(-1);
     }
 
-    sparse_output = sparse_file_import_auto(output, true, true);
+    sparse_output = sparse_file_import_auto(output, false, true);
     if (!sparse_output) {
         fprintf(stderr, "Couldn't import output file\n");
         exit(-1);
diff --git a/libutils/Looper.cpp b/libutils/Looper.cpp
index b14884b..952c992 100644
--- a/libutils/Looper.cpp
+++ b/libutils/Looper.cpp
@@ -74,7 +74,7 @@
         mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
         mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),
         mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
-    mWakeEventFd = eventfd(0, EFD_NONBLOCK);
+    mWakeEventFd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
     LOG_ALWAYS_FATAL_IF(mWakeEventFd < 0, "Could not make wake event fd: %s",
                         strerror(errno));
 
diff --git a/lmkd/lmkd.c b/lmkd/lmkd.c
index ebcebd6..511bf68 100644
--- a/lmkd/lmkd.c
+++ b/lmkd/lmkd.c
@@ -217,7 +217,7 @@
 }
 
 static void writefilestring(char *path, char *s) {
-    int fd = open(path, O_WRONLY);
+    int fd = open(path, O_WRONLY | O_CLOEXEC);
     int len = strlen(s);
     int ret;
 
@@ -474,7 +474,7 @@
 
     memset(mip, 0, sizeof(struct sysmeminfo));
 
-    fd = open(ZONEINFO_PATH, O_RDONLY);
+    fd = open(ZONEINFO_PATH, O_RDONLY | O_CLOEXEC);
     if (fd == -1) {
         ALOGE("%s open: errno=%d", ZONEINFO_PATH, errno);
         return -1;
@@ -505,7 +505,7 @@
     ssize_t ret;
 
     snprintf(path, PATH_MAX, "/proc/%d/statm", pid);
-    fd = open(path, O_RDONLY);
+    fd = open(path, O_RDONLY | O_CLOEXEC);
     if (fd == -1)
         return -1;
 
@@ -528,7 +528,7 @@
     ssize_t ret;
 
     snprintf(path, PATH_MAX, "/proc/%d/cmdline", pid);
-    fd = open(path, O_RDONLY);
+    fd = open(path, O_RDONLY | O_CLOEXEC);
     if (fd == -1)
         return NULL;
     ret = read_all(fd, line, sizeof(line) - 1);
@@ -673,19 +673,19 @@
     struct epoll_event epev;
     int ret;
 
-    mpfd = open(MEMCG_SYSFS_PATH "memory.pressure_level", O_RDONLY);
+    mpfd = open(MEMCG_SYSFS_PATH "memory.pressure_level", O_RDONLY | O_CLOEXEC);
     if (mpfd < 0) {
         ALOGI("No kernel memory.pressure_level support (errno=%d)", errno);
         goto err_open_mpfd;
     }
 
-    evctlfd = open(MEMCG_SYSFS_PATH "cgroup.event_control", O_WRONLY);
+    evctlfd = open(MEMCG_SYSFS_PATH "cgroup.event_control", O_WRONLY | O_CLOEXEC);
     if (evctlfd < 0) {
         ALOGI("No kernel memory cgroup event control (errno=%d)", errno);
         goto err_open_evctlfd;
     }
 
-    evfd = eventfd(0, EFD_NONBLOCK);
+    evfd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
     if (evfd < 0) {
         ALOGE("eventfd failed for level %s; errno=%d", levelstr, errno);
         goto err_eventfd;
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 61b020c..4f517bb 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -605,7 +605,7 @@
                 char c;
 
                 if ((2 == sscanf(buffer, "%d log.tx%c", &num, &c)) &&
-                        (num <= 24)) {
+                        (num <= 40)) {
                     ++count;
                 } else if (strncmp(buffer, total, sizeof(total) - 1)) {
                     fprintf(stderr, "WARNING: Parse error: %s", buffer);
diff --git a/logd/CommandListener.cpp b/logd/CommandListener.cpp
index e103359..7394f11 100644
--- a/logd/CommandListener.cpp
+++ b/logd/CommandListener.cpp
@@ -210,9 +210,20 @@
     }
 
     unsigned int logMask = -1;
+    pid_t pid = 0;
     if (argc > 1) {
         logMask = 0;
         for (int i = 1; i < argc; ++i) {
+            static const char _pid[] = "pid=";
+            if (!strncmp(argv[i], _pid, sizeof(_pid) - 1)) {
+                pid = atol(argv[i] + sizeof(_pid) - 1);
+                if (pid == 0) {
+                    cli->sendMsg("PID Error");
+                    return 0;
+                }
+                continue;
+            }
+
             int id = atoi(argv[i]);
             if ((id < LOG_ID_MIN) || (LOG_ID_MAX <= id)) {
                 cli->sendMsg("Range Error");
@@ -222,7 +233,8 @@
         }
     }
 
-    cli->sendMsg(package_string(mBuf.formatStatistics(uid, logMask)).c_str());
+    cli->sendMsg(package_string(mBuf.formatStatistics(uid, pid,
+                                                      logMask)).c_str());
     return 0;
 }
 
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 1e4f3b0..110395c 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -100,6 +100,12 @@
     unsigned long default_size = property_get_size(global_tuneable);
     if (!default_size) {
         default_size = property_get_size(global_default);
+        if (!default_size) {
+            default_size = property_get_bool("ro.config.low_ram",
+                                             BOOL_DEFAULT_FALSE)
+                ? LOG_BUFFER_MIN_SIZE // 64K
+                : LOG_BUFFER_SIZE;    // 256K
+        }
     }
 
     log_id_for_each(i) {
@@ -494,7 +500,8 @@
         size_t second_worst_sizes = 0;
 
         if (worstUidEnabledForLogid(id) && mPrune.worstUidEnabled()) {
-            std::unique_ptr<const UidEntry *[]> sorted = stats.sort(2, id);
+            std::unique_ptr<const UidEntry *[]> sorted = stats.sort(
+                AID_ROOT, (pid_t)0, 2, id);
 
             if (sorted.get()) {
                 if (sorted[0] && sorted[1]) {
@@ -861,10 +868,11 @@
     return max;
 }
 
-std::string LogBuffer::formatStatistics(uid_t uid, unsigned int logMask) {
+std::string LogBuffer::formatStatistics(uid_t uid, pid_t pid,
+                                        unsigned int logMask) {
     pthread_mutex_lock(&mLogElementsLock);
 
-    std::string ret = stats.format(uid, logMask);
+    std::string ret = stats.format(uid, pid, logMask);
 
     pthread_mutex_unlock(&mLogElementsLock);
 
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index c1fec73..c1614e8 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -86,7 +86,7 @@
     int setSize(log_id_t id, unsigned long size);
     unsigned long getSizeUsed(log_id_t id);
     // *strp uses malloc, use free to release.
-    std::string formatStatistics(uid_t uid, unsigned int logMask);
+    std::string formatStatistics(uid_t uid, pid_t pid, unsigned int logMask);
 
     void enableStatistics() {
         stats.enableStatistics();
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp
index 9bbf9e8..5348a2d 100644
--- a/logd/LogListener.cpp
+++ b/logd/LogListener.cpp
@@ -45,7 +45,6 @@
     char buffer[sizeof_log_id_t + sizeof(uint16_t) + sizeof(log_time)
         + LOGGER_ENTRY_MAX_PAYLOAD];
     struct iovec iov = { buffer, sizeof(buffer) };
-    memset(buffer, 0, sizeof(buffer));
 
     char control[CMSG_SPACE(sizeof(struct ucred))];
     struct msghdr hdr = {
@@ -60,6 +59,9 @@
 
     int socket = cli->getSocket();
 
+    // To clear the entire buffer is secure/safe, but this contributes to 1.68%
+    // overhead under logging load. We are safe because we check counts.
+    // memset(buffer, 0, sizeof(buffer));
     ssize_t n = recvmsg(socket, &hdr, 0);
     if (n <= (ssize_t)(sizeof(android_log_header_t))) {
         return false;
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index bf0e30b..afaefc2 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -205,7 +205,7 @@
 }
 
 std::string UidEntry::format(const LogStatistics &stat, log_id_t id) const {
-    uid_t uid = getKey();
+    uid_t uid = getUid();
     std::string name = android::base::StringPrintf("%u", uid);
     const char *nameTmp = stat.uidToName(uid);
     if (nameTmp) {
@@ -287,8 +287,8 @@
 
 std::string PidEntry::format(const LogStatistics &stat, log_id_t /* id */) const {
     uid_t uid = getUid();
-    std::string name = android::base::StringPrintf("%5u/%u",
-                                                   getKey(), uid);
+    pid_t pid = getPid();
+    std::string name = android::base::StringPrintf("%5u/%u", pid, uid);
     const char *nameTmp = getName();
     if (nameTmp) {
         name += android::base::StringPrintf(
@@ -325,7 +325,7 @@
 std::string TidEntry::format(const LogStatistics &stat, log_id_t /* id */) const {
     uid_t uid = getUid();
     std::string name = android::base::StringPrintf("%5u/%u",
-                                                   getKey(), uid);
+                                                   getTid(), uid);
     const char *nameTmp = getName();
     if (nameTmp) {
         name += android::base::StringPrintf(
@@ -388,7 +388,8 @@
     return formatLine(name, size, pruned);
 }
 
-std::string LogStatistics::format(uid_t uid, unsigned int logMask) const {
+std::string LogStatistics::format(uid_t uid, pid_t pid,
+                                  unsigned int logMask) const {
     static const unsigned short spaces_total = 19;
 
     // Report on total logging, current and for all time
@@ -461,24 +462,38 @@
         name = (uid == AID_ROOT)
             ? "Chattiest UIDs in %s log buffer:"
             : "Logging for your UID in %s log buffer:";
-        output += uidTable[id].format(*this, uid, name, id);
+        output += uidTable[id].format(*this, uid, pid, name, id);
     }
 
     if (enable) {
-        name = (uid == AID_ROOT) ? "Chattiest PIDs:" : "Logging for this PID:";
-        output += pidTable.format(*this, uid, name);
-        name = "Chattiest TIDs:";
-        output += tidTable.format(*this, uid, name);
+        name = ((uid == AID_ROOT) && !pid)
+            ? "Chattiest PIDs:"
+            : "Logging for this PID:";
+        output += pidTable.format(*this, uid, pid, name);
+        name = "Chattiest TIDs";
+        if (pid) {
+            name += android::base::StringPrintf(" for PID %d", pid);
+        }
+        name += ":";
+        output += tidTable.format(*this, uid, pid, name);
     }
 
     if (enable && (logMask & (1 << LOG_ID_EVENTS))) {
-        name = "Chattiest events log buffer TAGs:";
-        output += tagTable.format(*this, uid, name, LOG_ID_EVENTS);
+        name = "Chattiest events log buffer TAGs";
+        if (pid) {
+            name += android::base::StringPrintf(" for PID %d", pid);
+        }
+        name += ":";
+        output += tagTable.format(*this, uid, pid, name, LOG_ID_EVENTS);
     }
 
     if (enable && (logMask & (1 << LOG_ID_SECURITY))) {
-        name = "Chattiest security log buffer TAGs:";
-        output += securityTagTable.format(*this, uid, name, LOG_ID_SECURITY);
+        name = "Chattiest security log buffer TAGs";
+        if (pid) {
+            name += android::base::StringPrintf(" for PID %d", pid);
+        }
+        name += ":";
+        output += securityTagTable.format(*this, uid, pid, name, LOG_ID_SECURITY);
     }
 
     return output;
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index 8558b06..6d999d2 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -47,7 +47,8 @@
     typedef typename std::unordered_map<TKey, TEntry>::iterator iterator;
     typedef typename std::unordered_map<TKey, TEntry>::const_iterator const_iterator;
 
-    std::unique_ptr<const TEntry *[]> sort(size_t len) const {
+    std::unique_ptr<const TEntry *[]> sort(uid_t uid, pid_t pid,
+                                           size_t len) const {
         if (!len) {
             std::unique_ptr<const TEntry *[]> sorted(NULL);
             return sorted;
@@ -58,6 +59,14 @@
 
         for(const_iterator it = map.begin(); it != map.end(); ++it) {
             const TEntry &entry = it->second;
+
+            if ((uid != AID_ROOT) && (uid != entry.getUid())) {
+                continue;
+            }
+            if (pid && entry.getPid() && (pid != entry.getPid())) {
+                continue;
+            }
+
             size_t sizes = entry.getSizes();
             ssize_t index = len - 1;
             while ((!retval[index] || (sizes > retval[index]->getSizes()))
@@ -118,12 +127,13 @@
     std::string format(
             const LogStatistics &stat,
             uid_t uid,
+            pid_t pid,
             const std::string &name = std::string(""),
             log_id_t id = LOG_ID_MAX) const {
         static const size_t maximum_sorted_entries = 32;
         std::string output;
-        std::unique_ptr<const TEntry *[]> sorted = sort(maximum_sorted_entries);
-
+        std::unique_ptr<const TEntry *[]> sorted = sort(uid, pid,
+                                                        maximum_sorted_entries);
         if (!sorted.get()) {
             return output;
         }
@@ -136,9 +146,6 @@
             if (entry->getSizes() <= (sorted[0]->getSizes() / 100)) {
                 break;
             }
-            if ((uid != AID_ROOT) && (uid != entry->getUid())) {
-                continue;
-            }
             if (!headerPrinted) {
                 output += "\n\n";
                 output += entry->formatHeader(name, id);
@@ -217,14 +224,24 @@
 
 struct UidEntry : public EntryBaseDropped {
     const uid_t uid;
+    pid_t pid;
 
     UidEntry(LogBufferElement *element):
             EntryBaseDropped(element),
-            uid(element->getUid()) {
+            uid(element->getUid()),
+            pid(element->getPid()) {
     }
 
     inline const uid_t&getKey() const { return uid; }
-    inline const uid_t&getUid() const { return uid; }
+    inline const uid_t&getUid() const { return getKey(); }
+    inline const pid_t&getPid() const { return pid; }
+
+    inline void add(LogBufferElement *element) {
+        if (pid != element->getPid()) {
+            pid = -1;
+        }
+        EntryBase::add(element);
+    }
 
     std::string formatHeader(const std::string &name, log_id_t id) const;
     std::string format(const LogStatistics &stat, log_id_t id) const;
@@ -260,6 +277,7 @@
     ~PidEntry() { free(name); }
 
     const pid_t&getKey() const { return pid; }
+    const pid_t&getPid() const { return getKey(); }
     const uid_t&getUid() const { return uid; }
     const char*getName() const { return name; }
 
@@ -291,30 +309,36 @@
 
 struct TidEntry : public EntryBaseDropped {
     const pid_t tid;
+    pid_t pid;
     uid_t uid;
     char *name;
 
-    TidEntry(pid_t tid):
+    TidEntry(pid_t tid, pid_t pid):
             EntryBaseDropped(),
             tid(tid),
+            pid(pid),
             uid(android::pidToUid(tid)),
             name(android::tidToName(tid)) {
     }
     TidEntry(LogBufferElement *element):
             EntryBaseDropped(element),
             tid(element->getTid()),
+            pid(element->getPid()),
             uid(element->getUid()),
             name(android::tidToName(tid)) {
     }
     TidEntry(const TidEntry &element):
             EntryBaseDropped(element),
             tid(element.tid),
+            pid(element.pid),
             uid(element.uid),
             name(element.name ? strdup(element.name) : NULL) {
     }
     ~TidEntry() { free(name); }
 
     const pid_t&getKey() const { return tid; }
+    const pid_t&getTid() const { return getKey(); }
+    const pid_t&getPid() const { return pid; }
     const uid_t&getUid() const { return uid; }
     const char*getName() const { return name; }
 
@@ -330,8 +354,10 @@
 
     inline void add(LogBufferElement *element) {
         uid_t incomingUid = element->getUid();
-        if (getUid() != incomingUid) {
+        pid_t incomingPid = element->getPid();
+        if ((getUid() != incomingUid) || (getPid() != incomingPid)) {
             uid = incomingUid;
+            pid = incomingPid;
             free(name);
             name = android::tidToName(element->getTid());
         } else {
@@ -346,23 +372,28 @@
 
 struct TagEntry : public EntryBase {
     const uint32_t tag;
+    pid_t pid;
     uid_t uid;
 
     TagEntry(LogBufferElement *element):
             EntryBase(element),
             tag(element->getTag()),
+            pid(element->getPid()),
             uid(element->getUid()) {
     }
 
     const uint32_t&getKey() const { return tag; }
+    const pid_t&getPid() const { return pid; }
     const uid_t&getUid() const { return uid; }
     const char*getName() const { return android::tagToName(tag); }
 
     inline void add(LogBufferElement *element) {
-        uid_t incomingUid = element->getUid();
-        if (uid != incomingUid) {
+        if (uid != element->getUid()) {
             uid = -1;
         }
+        if (pid != element->getPid()) {
+            pid = -1;
+        }
         EntryBase::add(element);
     }
 
@@ -416,8 +447,9 @@
         --mDroppedElements[log_id];
     }
 
-    std::unique_ptr<const UidEntry *[]> sort(size_t len, log_id id) {
-        return uidTable[id].sort(len);
+    std::unique_ptr<const UidEntry *[]> sort(uid_t uid, pid_t pid,
+                                             size_t len, log_id id) {
+        return uidTable[id].sort(uid, pid, len);
     }
 
     // fast track current value by id only
@@ -429,7 +461,7 @@
     size_t sizesTotal(log_id_t id) const { return mSizesTotal[id]; }
     size_t elementsTotal(log_id_t id) const { return mElementsTotal[id]; }
 
-    std::string format(uid_t uid, unsigned int logMask) const;
+    std::string format(uid_t uid, pid_t pid, unsigned int logMask) const;
 
     // helper (must be locked directly or implicitly by mLogElementsLock)
     const char *pidToName(pid_t pid) const;
diff --git a/logd/LogUtils.h b/logd/LogUtils.h
index b591f28..fd4800e 100644
--- a/logd/LogUtils.h
+++ b/logd/LogUtils.h
@@ -43,6 +43,16 @@
 bool clientHasLogCredentials(uid_t uid, gid_t gid, pid_t pid);
 bool clientHasLogCredentials(SocketClient *cli);
 
+// Furnished in main.cpp
+#define BOOL_DEFAULT_FLAG_TRUE_FALSE 0x1
+#define BOOL_DEFAULT_FALSE       0x0     // false if property not present
+#define BOOL_DEFAULT_TRUE        0x1     // true if property not present
+#define BOOL_DEFAULT_FLAG_PERSIST    0x2 // <key>, persist.<key>, ro.<key>
+#define BOOL_DEFAULT_FLAG_ENG        0x4 // off for user
+#define BOOL_DEFAULT_FLAG_SVELTE     0x8 // off for low_ram
+
+bool property_get_bool(const char *key, int def);
+
 static inline bool worstUidEnabledForLogid(log_id_t id) {
     return (id == LOG_ID_MAIN) || (id == LOG_ID_SYSTEM) || (id == LOG_ID_RADIO);
 }
diff --git a/logd/README.property b/logd/README.property
index e4b23a9..019bd40 100644
--- a/logd/README.property
+++ b/logd/README.property
@@ -1,37 +1,53 @@
 The properties that logd responds to are:
 
 name                       type default  description
-logd.auditd                 bool  true   Enable selinux audit daemon
-logd.auditd.dmesg           bool  true   selinux audit messages duplicated and
+ro.logd.auditd             bool   true   Enable selinux audit daemon
+ro.logd.auditd.dmesg       bool   true   selinux audit messages duplicated and
                                          sent on to dmesg log
-logd.klogd                  bool depends Enable klogd daemon
-logd.statistics             bool depends Enable logcat -S statistics.
-ro.config.low_ram           bool  false  if true, logd.statistics & logd.klogd
-                                         default false
-ro.build.type               string       if user, logd.statistics & logd.klogd
-                                         default false
-persist.logd.logpersistd    string       Enable logpersist daemon, "logcatd"
+persist.logd.security      bool   false  Enable security buffer.
+ro.device_owner            bool   false  Override persist.logd.security to false
+ro.logd.kernel             bool+ svelte+ Enable klogd daemon
+ro.logd.statistics         bool+ svelte+ Enable logcat -S statistics.
+ro.build.type              string        if user, logd.statistics &
+                                         ro.logd.kernel default false.
+persist.logd.logpersistd   string        Enable logpersist daemon, "logcatd"
                                          turns on logcat -f in logd context
-persist.logd.size          number 256K   Global default size of the buffer for
+persist.logd.size          number  ro    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.
+ro.logd.size               number svelte default for persist.logd.size
+persist.logd.size.<buffer> number  ro    Size of the buffer for <buffer> log
+ro.logd.size.<buffer>      number svelte default for persist.logd.size.<buffer>
+ro.config.low_ram          bool   false  if true, logd.statistics, logd.kernel
+                                         default false, logd.size 64K instead
+                                         of 256K.
+persist.logd.filter        string        Pruning filter to optimize content.
+                                         At runtime use: logcat -P "<string>"
+ro.logd.filter             string "~!"   default for persist.logd.filter.
+                                         This default means to prune the
+                                         oldest entries of chattiest UID.
+persist.logd.timestamp     string  ro    The recording timestamp source.
+                                         "m[onotonic]" is the only supported
+                                         key character, otherwise realtime.
+ro.logd.timestamp        string realtime default for persist.logd.timestamp
+log.tag                   string persist The global logging level, VERBOSE,
+                                         DEBUG, INFO, WARN, ERROR, ASSERT or
+                                         SILENT. Only the first character is
+                                         the key character.
+persist.log.tag            string build  default for log.tag
+log.tag.<tag>             string persist The <tag> specific logging level.
+persist.log.tag.<tag>      string build  default for log.tag.<tag>
 
 NB:
-- Number support multipliers (K or M) for convenience. Range is limited
+- bool+ - "true", "false" and comma separated list of "eng" (forced false if
+  ro.build.type is "user") or "svelte" (forced false if ro.config.low_ram is
+  true).
+- svelte - see ro.config.low_ram for details.
+- svelte+ - see ro.config.low_ram and ro.build.type for details.
+- ro - <base property> temporary override, ro.<base property> platform default.
+- persist - <base property> override, persist.<base property> platform default.
+- build - VERBOSE for native, DEBUG for jvm isLoggable, or developer option.
+- 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]
diff --git a/logd/main.cpp b/logd/main.cpp
index 0f55d60..ba56e57 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -143,18 +143,72 @@
 }
 
 // Property helper
-static bool property_get_bool(const char *key, bool def) {
-    char property[PROPERTY_VALUE_MAX];
-    property_get(key, property, "");
-
-    if (!strcasecmp(property, "true")) {
-        return true;
-    }
-    if (!strcasecmp(property, "false")) {
+static bool check_flag(const char *prop, const char *flag) {
+    const char *cp = strcasestr(prop, flag);
+    if (!cp) {
         return false;
     }
+    // We only will document comma (,)
+    static const char sep[] = ",:;|+ \t\f";
+    if ((cp != prop) && !strchr(sep, cp[-1])) {
+        return false;
+    }
+    cp += strlen(flag);
+    return !*cp || !!strchr(sep, *cp);
+}
 
-    return def;
+bool property_get_bool(const char *key, int flag) {
+    char def[PROPERTY_VALUE_MAX];
+    char property[PROPERTY_VALUE_MAX];
+    def[0] = '\0';
+    if (flag & BOOL_DEFAULT_FLAG_PERSIST) {
+        char newkey[PROPERTY_KEY_MAX];
+        snprintf(newkey, sizeof(newkey), "ro.%s", key);
+        property_get(newkey, property, "");
+        // persist properties set by /data require innoculation with
+        // logd-reinit. They may be set in init.rc early and function, but
+        // otherwise are defunct unless reset. Do not rely on persist
+        // properties for startup-only keys unless you are willing to restart
+        // logd daemon (not advised).
+        snprintf(newkey, sizeof(newkey), "persist.%s", key);
+        property_get(newkey, def, property);
+    }
+
+    property_get(key, property, def);
+
+    if (check_flag(property, "true")) {
+        return true;
+    }
+    if (check_flag(property, "false")) {
+        return false;
+    }
+    if (check_flag(property, "eng")) {
+       flag |= BOOL_DEFAULT_FLAG_ENG;
+    }
+    // this is really a "not" flag
+    if (check_flag(property, "svelte")) {
+       flag |= BOOL_DEFAULT_FLAG_SVELTE;
+    }
+
+    // Sanity Check
+    if (flag & (BOOL_DEFAULT_FLAG_SVELTE | BOOL_DEFAULT_FLAG_ENG)) {
+        flag &= ~BOOL_DEFAULT_FLAG_TRUE_FALSE;
+        flag |= BOOL_DEFAULT_TRUE;
+    }
+
+    if ((flag & BOOL_DEFAULT_FLAG_SVELTE)
+            && property_get_bool("ro.config.low_ram",
+                                 BOOL_DEFAULT_FALSE)) {
+        return false;
+    }
+    if (flag & BOOL_DEFAULT_FLAG_ENG) {
+        property_get("ro.build.type", property, "");
+        if (!strcmp(property, "user")) {
+            return false;
+        }
+    }
+
+    return (flag & BOOL_DEFAULT_FLAG_TRUE_FALSE) != BOOL_DEFAULT_FALSE;
 }
 
 // Remove the static, and use this variable
@@ -266,17 +320,6 @@
     return android_lookupEventTag(map, tag);
 }
 
-static bool property_get_bool_svelte(const char *key) {
-    bool not_user;
-    {
-        char property[PROPERTY_VALUE_MAX];
-        property_get("ro.build.type", property, "");
-        not_user = !!strcmp(property, "user");
-    }
-    return property_get_bool(key, not_user
-            && !property_get_bool("ro.config.low_ram", false));
-}
-
 static void readDmesg(LogAudit *al, LogKlog *kl) {
     if (!al && !kl) {
         return;
@@ -325,7 +368,11 @@
 // transitory per-client threads are created for each reader.
 int main(int argc, char *argv[]) {
     int fdPmesg = -1;
-    bool klogd = property_get_bool_svelte("logd.klogd");
+    bool klogd = property_get_bool("logd.kernel",
+                                   BOOL_DEFAULT_TRUE |
+                                   BOOL_DEFAULT_FLAG_PERSIST |
+                                   BOOL_DEFAULT_FLAG_ENG |
+                                   BOOL_DEFAULT_FLAG_SVELTE);
     if (klogd) {
         fdPmesg = open("/proc/kmsg", O_RDONLY | O_NDELAY);
     }
@@ -405,7 +452,11 @@
 
     signal(SIGHUP, reinit_signal_handler);
 
-    if (property_get_bool_svelte("logd.statistics")) {
+    if (property_get_bool("logd.statistics",
+                          BOOL_DEFAULT_TRUE |
+                          BOOL_DEFAULT_FLAG_PERSIST |
+                          BOOL_DEFAULT_FLAG_ENG |
+                          BOOL_DEFAULT_FLAG_SVELTE)) {
         logBuf->enableStatistics();
     }
 
@@ -439,12 +490,17 @@
     // initiated log messages. New log entries are added to LogBuffer
     // and LogReader is notified to send updates to connected clients.
 
-    bool auditd = property_get_bool("logd.auditd", true);
-
+    bool auditd = property_get_bool("logd.auditd",
+                                    BOOL_DEFAULT_TRUE |
+                                    BOOL_DEFAULT_FLAG_PERSIST);
     LogAudit *al = NULL;
     if (auditd) {
-        bool dmesg = property_get_bool("logd.auditd.dmesg", true);
-        al = new LogAudit(logBuf, reader, dmesg ? fdDmesg : -1);
+        al = new LogAudit(logBuf, reader,
+                          property_get_bool("logd.auditd.dmesg",
+                                            BOOL_DEFAULT_TRUE |
+                                            BOOL_DEFAULT_FLAG_PERSIST)
+                              ? fdDmesg
+                              : -1);
     }
 
     LogKlog *kl = NULL;
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index 4472c1d..7d0dd44 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -26,8 +26,6 @@
 #include "log/log.h"
 #include "log/logger.h"
 
-#define __unused __attribute__((__unused__))
-
 /*
  * returns statistics
  */
@@ -196,7 +194,7 @@
     delete [] buf;
 }
 
-static void caught_signal(int signum __unused) { }
+static void caught_signal(int /* signum */) { }
 
 static void dump_log_msg(const char *prefix,
                          log_msg *msg, unsigned int version, int lid) {
diff --git a/metricsd/collectors/averaged_statistics_collector_test.cc b/metricsd/collectors/averaged_statistics_collector_test.cc
index 9c97f00..68f9f2f 100644
--- a/metricsd/collectors/averaged_statistics_collector_test.cc
+++ b/metricsd/collectors/averaged_statistics_collector_test.cc
@@ -16,11 +16,12 @@
 
 #include "averaged_statistics_collector.h"
 
+#include <memory>
+
 #include <inttypes.h>
 
 #include <base/files/file_util.h>
 #include <base/files/scoped_temp_dir.h>
-#include <base/memory/scoped_ptr.h>
 #include <base/strings/stringprintf.h>
 #include <gtest/gtest.h>
 
@@ -62,7 +63,7 @@
   }
 
   // Collector used for tests.
-  scoped_ptr<AveragedStatisticsCollector> collector_;
+  std::unique_ptr<AveragedStatisticsCollector> collector_;
 
   // Temporary directory used for tests.
   base::ScopedTempDir temp_dir_;
diff --git a/metricsd/etc/weaved/traits/metrics.json b/metricsd/etc/weaved/traits/metrics.json
index 7d17c77..7583270 100644
--- a/metricsd/etc/weaved/traits/metrics.json
+++ b/metricsd/etc/weaved/traits/metrics.json
@@ -1,15 +1,17 @@
 {
   "_metrics": {
     "commands": {
-      "_enableAnalyticsReporting": {
-        "minimalRole": "manager"
+      "enableAnalyticsReporting": {
+        "minimalRole": "manager",
+        "parameters": {}
       },
-      "_disableAnalyticsReporting": {
-        "minimalRole": "manager"
+      "disableAnalyticsReporting": {
+        "minimalRole": "manager",
+        "parameters": {}
       }
     },
     "state": {
-      "_AnalyticsReportingState": {
+      "analyticsReportingState": {
         "type": "string",
         "enum": [ "enabled", "disabled" ]
       }
diff --git a/metricsd/include/metrics/timer.h b/metricsd/include/metrics/timer.h
index b36ffff..c1b8ede 100644
--- a/metricsd/include/metrics/timer.h
+++ b/metricsd/include/metrics/timer.h
@@ -19,10 +19,10 @@
 #ifndef METRICS_TIMER_H_
 #define METRICS_TIMER_H_
 
+#include <memory>
 #include <string>
 
 #include <base/macros.h>
-#include <base/memory/scoped_ptr.h>
 #include <base/time/time.h>
 #include <gtest/gtest_prod.h>  // for FRIEND_TEST
 
@@ -121,7 +121,7 @@
   TimerState timer_state_;
 
   // Wrapper for the calls to the system clock.
-  scoped_ptr<ClockWrapper> clock_wrapper_;
+  std::unique_ptr<ClockWrapper> clock_wrapper_;
 
   DISALLOW_COPY_AND_ASSIGN(Timer);
 };
diff --git a/metricsd/metrics_collector.cc b/metricsd/metrics_collector.cc
index b5c2289..a5daab5 100644
--- a/metricsd/metrics_collector.cc
+++ b/metricsd/metrics_collector.cc
@@ -19,6 +19,8 @@
 #include <sysexits.h>
 #include <time.h>
 
+#include <memory>
+
 #include <base/bind.h>
 #include <base/files/file_path.h>
 #include <base/files/file_util.h>
@@ -229,11 +231,11 @@
   device_->AddComponent(kWeaveComponent, {"_metrics"});
   device_->AddCommandHandler(
       kWeaveComponent,
-      "_metrics._enableAnalyticsReporting",
+      "_metrics.enableAnalyticsReporting",
       base::Bind(&MetricsCollector::OnEnableMetrics, base::Unretained(this)));
   device_->AddCommandHandler(
       kWeaveComponent,
-      "_metrics._disableAnalyticsReporting",
+      "_metrics.disableAnalyticsReporting",
       base::Bind(&MetricsCollector::OnDisableMetrics, base::Unretained(this)));
 
   latest_cpu_use_microseconds_ = cpu_usage_collector_->GetCumulativeCpuUse();
@@ -294,7 +296,7 @@
       metrics_lib_->AreMetricsEnabled() ? "enabled" : "disabled";
 
   if (!device_->SetStateProperty(kWeaveComponent,
-                                 "_metrics._AnalyticsReportingState",
+                                 "_metrics.analyticsReportingState",
                                  enabled,
                                  nullptr)) {
     LOG(ERROR) << "failed to update weave's state";
@@ -665,7 +667,7 @@
 }
 
 void MetricsCollector::SendAndResetDailyUseSample(
-    const scoped_ptr<PersistentInteger>& use) {
+    const unique_ptr<PersistentInteger>& use) {
   SendSample(use->Name(),
              use->GetAndClear(),
              1,                        // value of first bucket
@@ -674,7 +676,7 @@
 }
 
 void MetricsCollector::SendAndResetCrashIntervalSample(
-    const scoped_ptr<PersistentInteger>& interval) {
+    const unique_ptr<PersistentInteger>& interval) {
   SendSample(interval->Name(),
              interval->GetAndClear(),
              1,                        // value of first bucket
@@ -683,7 +685,7 @@
 }
 
 void MetricsCollector::SendAndResetCrashFrequencySample(
-    const scoped_ptr<PersistentInteger>& frequency) {
+    const unique_ptr<PersistentInteger>& frequency) {
   SendSample(frequency->Name(),
              frequency->GetAndClear(),
              1,                        // value of first bucket
diff --git a/metricsd/metrics_collector.h b/metricsd/metrics_collector.h
index 422ed7c..45ef63d 100644
--- a/metricsd/metrics_collector.h
+++ b/metricsd/metrics_collector.h
@@ -20,11 +20,11 @@
 #include <stdint.h>
 
 #include <map>
+#include <memory>
 #include <string>
 #include <vector>
 
 #include <base/files/file_path.h>
-#include <base/memory/scoped_ptr.h>
 #include <base/time/time.h>
 #include <brillo/daemons/dbus_daemon.h>
 #include <libweaved/command.h>
@@ -38,6 +38,7 @@
 #include "persistent_integer.h"
 
 using chromeos_metrics::PersistentInteger;
+using std::unique_ptr;
 
 class MetricsCollector : public brillo::DBusDaemon {
  public:
@@ -151,18 +152,17 @@
 
   // Sends a sample representing the number of seconds of active use
   // for a 24-hour period and reset |use|.
-  void SendAndResetDailyUseSample(
-      const scoped_ptr<PersistentInteger>& use);
+  void SendAndResetDailyUseSample(const unique_ptr<PersistentInteger>& use);
 
   // Sends a sample representing a time interval between two crashes of the
   // same type and reset |interval|.
   void SendAndResetCrashIntervalSample(
-      const scoped_ptr<PersistentInteger>& interval);
+      const unique_ptr<PersistentInteger>& interval);
 
   // Sends a sample representing a frequency of crashes of some type and reset
   // |frequency|.
   void SendAndResetCrashFrequencySample(
-      const scoped_ptr<PersistentInteger>& frequency);
+      const unique_ptr<PersistentInteger>& frequency);
 
   // Initializes vm and disk stats reporting.
   void StatsReporterInit();
@@ -241,36 +241,36 @@
   base::TimeDelta latest_cpu_use_microseconds_;
 
   // Persistent values and accumulators for crash statistics.
-  scoped_ptr<PersistentInteger> daily_cycle_;
-  scoped_ptr<PersistentInteger> weekly_cycle_;
-  scoped_ptr<PersistentInteger> version_cycle_;
+  unique_ptr<PersistentInteger> daily_cycle_;
+  unique_ptr<PersistentInteger> weekly_cycle_;
+  unique_ptr<PersistentInteger> version_cycle_;
 
   // Active use accumulated in a day.
-  scoped_ptr<PersistentInteger> daily_active_use_;
+  unique_ptr<PersistentInteger> daily_active_use_;
   // Active use accumulated since the latest version update.
-  scoped_ptr<PersistentInteger> version_cumulative_active_use_;
+  unique_ptr<PersistentInteger> version_cumulative_active_use_;
 
   // The CPU time accumulator.  This contains the CPU time, in milliseconds,
   // used by the system since the most recent OS version update.
-  scoped_ptr<PersistentInteger> version_cumulative_cpu_use_;
+  unique_ptr<PersistentInteger> version_cumulative_cpu_use_;
 
-  scoped_ptr<PersistentInteger> user_crash_interval_;
-  scoped_ptr<PersistentInteger> kernel_crash_interval_;
-  scoped_ptr<PersistentInteger> unclean_shutdown_interval_;
+  unique_ptr<PersistentInteger> user_crash_interval_;
+  unique_ptr<PersistentInteger> kernel_crash_interval_;
+  unique_ptr<PersistentInteger> unclean_shutdown_interval_;
 
-  scoped_ptr<PersistentInteger> any_crashes_daily_count_;
-  scoped_ptr<PersistentInteger> any_crashes_weekly_count_;
-  scoped_ptr<PersistentInteger> user_crashes_daily_count_;
-  scoped_ptr<PersistentInteger> user_crashes_weekly_count_;
-  scoped_ptr<PersistentInteger> kernel_crashes_daily_count_;
-  scoped_ptr<PersistentInteger> kernel_crashes_weekly_count_;
-  scoped_ptr<PersistentInteger> kernel_crashes_version_count_;
-  scoped_ptr<PersistentInteger> unclean_shutdowns_daily_count_;
-  scoped_ptr<PersistentInteger> unclean_shutdowns_weekly_count_;
+  unique_ptr<PersistentInteger> any_crashes_daily_count_;
+  unique_ptr<PersistentInteger> any_crashes_weekly_count_;
+  unique_ptr<PersistentInteger> user_crashes_daily_count_;
+  unique_ptr<PersistentInteger> user_crashes_weekly_count_;
+  unique_ptr<PersistentInteger> kernel_crashes_daily_count_;
+  unique_ptr<PersistentInteger> kernel_crashes_weekly_count_;
+  unique_ptr<PersistentInteger> kernel_crashes_version_count_;
+  unique_ptr<PersistentInteger> unclean_shutdowns_daily_count_;
+  unique_ptr<PersistentInteger> unclean_shutdowns_weekly_count_;
 
-  scoped_ptr<CpuUsageCollector> cpu_usage_collector_;
-  scoped_ptr<DiskUsageCollector> disk_usage_collector_;
-  scoped_ptr<AveragedStatisticsCollector> averaged_stats_collector_;
+  unique_ptr<CpuUsageCollector> cpu_usage_collector_;
+  unique_ptr<DiskUsageCollector> disk_usage_collector_;
+  unique_ptr<AveragedStatisticsCollector> averaged_stats_collector_;
 
   std::unique_ptr<weaved::Device> device_;
 };
diff --git a/metricsd/persistent_integer_test.cc b/metricsd/persistent_integer_test.cc
index 55d6cbc..bf76261 100644
--- a/metricsd/persistent_integer_test.cc
+++ b/metricsd/persistent_integer_test.cc
@@ -14,12 +14,13 @@
  * limitations under the License.
  */
 
-#include <gtest/gtest.h>
+#include <memory>
 
 #include <base/compiler_specific.h>
 #include <base/files/file_enumerator.h>
 #include <base/files/file_util.h>
 #include <base/files/scoped_temp_dir.h>
+#include <gtest/gtest.h>
 
 #include "persistent_integer.h"
 
@@ -38,7 +39,7 @@
 };
 
 TEST_F(PersistentIntegerTest, BasicChecks) {
-  scoped_ptr<PersistentInteger> pi(
+  std::unique_ptr<PersistentInteger> pi(
       new PersistentInteger(kBackingFileName, temp_dir_.path()));
 
   // Test initialization.
diff --git a/metricsd/timer.cc b/metricsd/timer.cc
index 0c2c119..06fc336 100644
--- a/metricsd/timer.cc
+++ b/metricsd/timer.cc
@@ -18,8 +18,6 @@
 
 #include <string>
 
-#include <base/memory/scoped_ptr.h>
-
 #include "metrics/metrics_library.h"
 
 namespace chromeos_metrics {
diff --git a/metricsd/timer_test.cc b/metricsd/timer_test.cc
index bc7a2a1..7a67e11 100644
--- a/metricsd/timer_test.cc
+++ b/metricsd/timer_test.cc
@@ -16,9 +16,9 @@
 
 #include <stdint.h>
 
-#include <base/memory/scoped_ptr.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
+#include <memory>
 
 #include "metrics/metrics_library_mock.h"
 #include "metrics/timer.h"
@@ -61,7 +61,7 @@
   virtual void TearDown() {}
 
   Timer timer_;
-  scoped_ptr<ClockWrapperMock> clock_wrapper_mock_;
+  std::unique_ptr<ClockWrapperMock> clock_wrapper_mock_;
   base::TimeTicks stime, etime, stime2, etime2, stime3, etime3;
 };
 
@@ -436,7 +436,7 @@
 
   TimerReporter timer_reporter_;
   MetricsLibraryMock lib_;
-  scoped_ptr<ClockWrapperMock> clock_wrapper_mock_;
+  std::unique_ptr<ClockWrapperMock> clock_wrapper_mock_;
   base::TimeTicks stime, etime;
 };
 
diff --git a/metricsd/uploader/system_profile_cache.h b/metricsd/uploader/system_profile_cache.h
index 0a21ad4..f9c484c 100644
--- a/metricsd/uploader/system_profile_cache.h
+++ b/metricsd/uploader/system_profile_cache.h
@@ -19,12 +19,12 @@
 
 #include <stdint.h>
 
+#include <memory>
 #include <string>
 
 #include "base/compiler_specific.h"
 #include "base/files/file_path.h"
 #include "base/gtest_prod_util.h"
-#include "base/memory/scoped_ptr.h"
 #include "persistent_integer.h"
 #include "uploader/proto/system_profile.pb.h"
 #include "uploader/system_profile_setter.h"
@@ -80,7 +80,7 @@
   bool initialized_;
   bool testing_;
   base::FilePath metrics_directory_;
-  scoped_ptr<chromeos_metrics::PersistentInteger> session_id_;
+  std::unique_ptr<chromeos_metrics::PersistentInteger> session_id_;
   SystemProfile profile_;
 };
 
diff --git a/metricsd/uploader/upload_service.cc b/metricsd/uploader/upload_service.cc
index ea8427a..2fb30c3 100644
--- a/metricsd/uploader/upload_service.cc
+++ b/metricsd/uploader/upload_service.cc
@@ -18,6 +18,7 @@
 
 #include <sysexits.h>
 
+#include <memory>
 #include <string>
 
 #include <base/bind.h>
@@ -169,7 +170,7 @@
   if (!current_log_)
     return;
 
-  scoped_ptr<MetricsLog> staged_log;
+  std::unique_ptr<MetricsLog> staged_log;
   staged_log.swap(current_log_);
   staged_log->CloseLog();
   if (!staged_log->PopulateSystemProfile(system_profile_setter_.get())) {
diff --git a/metricsd/uploader/upload_service.h b/metricsd/uploader/upload_service.h
index fe064b8..1d36121 100644
--- a/metricsd/uploader/upload_service.h
+++ b/metricsd/uploader/upload_service.h
@@ -17,6 +17,7 @@
 #ifndef METRICS_UPLOADER_UPLOAD_SERVICE_H_
 #define METRICS_UPLOADER_UPLOAD_SERVICE_H_
 
+#include <memory>
 #include <string>
 
 #include <base/metrics/histogram_base.h>
@@ -141,11 +142,11 @@
   // Returns the current log. If there is no current log, creates it first.
   MetricsLog* GetOrCreateCurrentLog();
 
-  scoped_ptr<SystemProfileSetter> system_profile_setter_;
+  std::unique_ptr<SystemProfileSetter> system_profile_setter_;
   base::HistogramSnapshotManager histogram_snapshot_manager_;
-  scoped_ptr<Sender> sender_;
+  std::unique_ptr<Sender> sender_;
   chromeos_metrics::PersistentInteger failed_upload_count_;
-  scoped_ptr<MetricsLog> current_log_;
+  std::unique_ptr<MetricsLog> current_log_;
   std::shared_ptr<CrashCounters> counters_;
 
   base::TimeDelta upload_interval_;
diff --git a/metricsd/uploader/upload_service_test.cc b/metricsd/uploader/upload_service_test.cc
index 0e2ba8f..ec507e8 100644
--- a/metricsd/uploader/upload_service_test.cc
+++ b/metricsd/uploader/upload_service_test.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include <gtest/gtest.h>
+#include <memory>
 
 #include <base/at_exit.h>
 #include <base/files/file_util.h>
@@ -23,6 +23,7 @@
 #include <base/metrics/sparse_histogram.h>
 #include <base/metrics/statistics_recorder.h>
 #include <base/sys_info.h>
+#include <gtest/gtest.h>
 
 #include "constants.h"
 #include "persistent_integer.h"
@@ -86,15 +87,15 @@
   }
 
   const metrics::SystemProfileProto_Stability GetCurrentStability() {
-    EXPECT_TRUE(upload_service_->current_log_);
+    EXPECT_TRUE(upload_service_->current_log_.get());
 
     return upload_service_->current_log_->uma_proto()->system_profile().stability();
   }
 
   base::ScopedTempDir dir_;
-  scoped_ptr<UploadService> upload_service_;
+  std::unique_ptr<UploadService> upload_service_;
 
-  scoped_ptr<base::AtExitManager> exit_manager_;
+  std::unique_ptr<base::AtExitManager> exit_manager_;
   std::shared_ptr<CrashCounters> counters_;
 };
 
diff --git a/sdcard/sdcard.c b/sdcard/sdcard.c
index b6bbe7e..45efe36 100644
--- a/sdcard/sdcard.c
+++ b/sdcard/sdcard.c
@@ -507,6 +507,16 @@
     }
 }
 
+static void derive_permissions_recursive_locked(struct fuse* fuse, struct node *parent) {
+    struct node *node;
+    for (node = parent->child; node; node = node->next) {
+        derive_permissions_locked(fuse, parent, node);
+        if (node->child) {
+            derive_permissions_recursive_locked(fuse, node);
+        }
+    }
+}
+
 /* Kernel has already enforced everything we returned through
  * derive_permissions_locked(), so this is used to lock down access
  * even further, such as enforcing that apps hold sdcard_rw. */
@@ -1145,6 +1155,8 @@
     res = rename_node_locked(child_node, new_name, new_actual_name);
     if (!res) {
         remove_node_from_parent_locked(child_node);
+        derive_permissions_locked(fuse, new_parent_node, child_node);
+        derive_permissions_recursive_locked(fuse, child_node);
         add_node_to_parent_locked(child_node, new_parent_node);
     }
     goto done;
@@ -1654,6 +1666,9 @@
     TRACE("read_package_list: found %zu packages\n",
             hashmapSize(global->package_to_appid));
 
+    /* Regenerate ownership details using newly loaded mapping */
+    derive_permissions_recursive_locked(global->fuse_default, &global->root);
+
     pthread_mutex_unlock(&global->lock);
 
     return rc;