Merge "Add BMS as a Battery type"
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..b44c296
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1 @@
+subdirs = ["*"]
diff --git a/adb/Android.mk b/adb/Android.mk
index 16ed991..fab8c87 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -330,6 +330,7 @@
 LOCAL_STATIC_LIBRARIES := \
     libadbd \
     libbase \
+    libbootloader_message \
     libfs_mgr \
     libfec \
     libfec_rs \
diff --git a/adb/adb_auth_host.cpp b/adb/adb_auth_host.cpp
index ff2d76d..ec9b1c3 100644
--- a/adb/adb_auth_host.cpp
+++ b/adb/adb_auth_host.cpp
@@ -207,11 +207,6 @@
     }
 
     if (S_ISREG(st.st_mode)) {
-        if (!android::base::EndsWith(path, ".adb_key")) {
-            LOG(INFO) << "skipping non-adb_key '" << path << "'";
-            return false;
-        }
-
         return read_key_file(path);
     } else if (S_ISDIR(st.st_mode)) {
         if (!allow_dir) {
@@ -236,7 +231,12 @@
                 continue;
             }
 
-            result |= read_keys((path + OS_PATH_SEPARATOR + name).c_str(), false);
+            if (!android::base::EndsWith(name, ".adb_key")) {
+                LOG(INFO) << "skipping non-adb_key '" << path << "/" << name << "'";
+                continue;
+            }
+
+            result |= read_key_file((path + OS_PATH_SEPARATOR + name));
         }
         return result;
     }
diff --git a/adb/adb_trace.cpp b/adb/adb_trace.cpp
index 002d061..c369d60 100644
--- a/adb/adb_trace.cpp
+++ b/adb/adb_trace.cpp
@@ -109,8 +109,8 @@
     }
 
     std::unordered_map<std::string, int> trace_flags = {
-        {"1", 0},
-        {"all", 0},
+        {"1", -1},
+        {"all", -1},
         {"adb", ADB},
         {"sockets", SOCKETS},
         {"packets", PACKETS},
@@ -133,8 +133,8 @@
             continue;
         }
 
-        if (flag->second == 0) {
-            // 0 is used for the special values "1" and "all" that enable all
+        if (flag->second == -1) {
+            // -1 is used for the special values "1" and "all" that enable all
             // tracing.
             adb_trace_mask = ~0;
             return;
diff --git a/adb/adb_trace.h b/adb/adb_trace.h
index 5206a99..aaffa29 100644
--- a/adb/adb_trace.h
+++ b/adb/adb_trace.h
@@ -58,4 +58,8 @@
 void adb_trace_init(char**);
 void adb_trace_enable(AdbTrace trace_tag);
 
+#define ATRACE_TAG ATRACE_TAG_ADB
+#include <cutils/trace.h>
+#include <utils/Trace.h>
+
 #endif /* __ADB_TRACE_H */
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index 4ec0fc2..d583516 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -23,6 +23,8 @@
 #include <stdlib.h>
 #include <unistd.h>
 
+#include <thread>
+
 #include <android-base/errors.h>
 #include <android-base/file.h>
 #include <android-base/logging.h>
@@ -33,6 +35,7 @@
 #include "adb_listeners.h"
 #include "adb_utils.h"
 #include "commandline.h"
+#include "sysdeps/chrono.h"
 #include "transport.h"
 
 static std::string GetLogFilePath() {
@@ -113,8 +116,18 @@
     local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT);
 
     std::string error;
-    if (install_listener(socket_spec, "*smartsocket*", nullptr, 0, nullptr, &error)) {
-        fatal("could not install *smartsocket* listener: %s", error.c_str());
+
+    auto start = std::chrono::steady_clock::now();
+
+    // If we told a previous adb server to quit because of version mismatch, we can get to this
+    // point before it's finished exiting. Retry for a while to give it some time.
+    while (install_listener(socket_spec, "*smartsocket*", nullptr, 0, nullptr, &error) !=
+           INSTALL_STATUS_OK) {
+        if (std::chrono::steady_clock::now() - start > 0.5s) {
+            fatal("could not install *smartsocket* listener: %s", error.c_str());
+        }
+
+        std::this_thread::sleep_for(100ms);
     }
 
     if (is_daemon) {
diff --git a/adb/file_sync_client.cpp b/adb/file_sync_client.cpp
index 76119ef..271943d 100644
--- a/adb/file_sync_client.cpp
+++ b/adb/file_sync_client.cpp
@@ -720,13 +720,7 @@
 }
 
 static bool sync_recv(SyncConnection& sc, const char* rpath, const char* lpath,
-                      const char* name=nullptr) {
-    struct stat st;
-    if (!sync_stat_fallback(sc, rpath, &st)) {
-        sc.Error("stat failed when trying to receive %s: %s", rpath, strerror(errno));
-        return false;
-    }
-
+                      const char* name, uint64_t expected_size) {
     if (!sc.SendRequest(ID_RECV, rpath)) return false;
 
     adb_unlink(lpath);
@@ -778,7 +772,7 @@
         bytes_copied += msg.data.size;
 
         sc.RecordBytesTransferred(msg.data.size);
-        sc.ReportProgress(name != nullptr ? name : rpath, bytes_copied, st.st_size);
+        sc.ReportProgress(name != nullptr ? name : rpath, bytes_copied, expected_size);
     }
 
     sc.RecordFilesTransferred(1);
@@ -1121,7 +1115,7 @@
                 continue;
             }
 
-            if (!sync_recv(sc, ci.rpath.c_str(), ci.lpath.c_str())) {
+            if (!sync_recv(sc, ci.rpath.c_str(), ci.lpath.c_str(), nullptr, ci.size)) {
                 return false;
             }
 
@@ -1232,7 +1226,7 @@
 
         sc.NewTransfer();
         sc.SetExpectedTotalBytes(src_st.st_size);
-        if (!sync_recv(sc, src_path, dst_path, name)) {
+        if (!sync_recv(sc, src_path, dst_path, name, src_st.st_size)) {
             success = false;
             continue;
         }
diff --git a/adb/file_sync_service.cpp b/adb/file_sync_service.cpp
index 43c877e..e667bf8 100644
--- a/adb/file_sync_service.cpp
+++ b/adb/file_sync_service.cpp
@@ -39,10 +39,13 @@
 
 #include "adb.h"
 #include "adb_io.h"
+#include "adb_trace.h"
 #include "adb_utils.h"
 #include "security_log_tags.h"
 #include "sysdeps/errno.h"
 
+using android::base::StringPrintf;
+
 static bool should_use_fs_config(const std::string& path) {
     // TODO: use fs_config to configure permissions on /data.
     return android::base::StartsWith(path, "/system/") ||
@@ -152,7 +155,7 @@
     if (!d) goto done;
 
     while ((de = readdir(d.get()))) {
-        std::string filename(android::base::StringPrintf("%s/%s", path, de->d_name));
+        std::string filename(StringPrintf("%s/%s", path, de->d_name));
 
         struct stat st;
         if (lstat(filename.c_str(), &st) == 0) {
@@ -191,7 +194,7 @@
 }
 
 static bool SendSyncFailErrno(int fd, const std::string& reason) {
-    return SendSyncFail(fd, android::base::StringPrintf("%s: %s", reason.c_str(), strerror(errno)));
+    return SendSyncFail(fd, StringPrintf("%s: %s", reason.c_str(), strerror(errno)));
 }
 
 static bool handle_send_file(int s, const char* path, uid_t uid, gid_t gid, uint64_t capabilities,
@@ -433,9 +436,31 @@
     return WriteFdExactly(s, &msg.data, sizeof(msg.data));
 }
 
+static const char* sync_id_to_name(uint32_t id) {
+  switch (id) {
+    case ID_LSTAT_V1:
+      return "lstat_v1";
+    case ID_LSTAT_V2:
+      return "lstat_v2";
+    case ID_STAT_V2:
+      return "stat_v2";
+    case ID_LIST:
+      return "list";
+    case ID_SEND:
+      return "send";
+    case ID_RECV:
+      return "recv";
+    case ID_QUIT:
+        return "quit";
+    default:
+        return "???";
+  }
+}
+
 static bool handle_sync_command(int fd, std::vector<char>& buffer) {
     D("sync: waiting for request");
 
+    ATRACE_CALL();
     SyncRequest request;
     if (!ReadFdExactly(fd, &request, sizeof(request))) {
         SendSyncFail(fd, "command read failure");
@@ -453,9 +478,11 @@
     }
     name[path_length] = 0;
 
-    const char* id = reinterpret_cast<const char*>(&request.id);
-    D("sync: '%.4s' '%s'", id, name);
+    std::string id_name = sync_id_to_name(request.id);
+    std::string trace_name = StringPrintf("%s(%s)", id_name.c_str(), name);
+    ATRACE_NAME(trace_name.c_str());
 
+    D("sync: %s('%s')", id_name.c_str(), name);
     switch (request.id) {
         case ID_LSTAT_V1:
             if (!do_lstat_v1(fd, name)) return false;
@@ -476,8 +503,7 @@
         case ID_QUIT:
             return false;
         default:
-            SendSyncFail(
-                fd, android::base::StringPrintf("unknown command '%.4s' (%08x)", id, request.id));
+            SendSyncFail(fd, StringPrintf("unknown command %08x", request.id));
             return false;
     }
 
diff --git a/adb/services.cpp b/adb/services.cpp
index 2fbc15a..df1b134 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -39,6 +39,7 @@
 
 #if !ADB_HOST
 #include <android-base/properties.h>
+#include <bootloader_message/bootloader_message.h>
 #include <cutils/android_reboot.h>
 #include <private/android_logger.h>
 #endif
@@ -133,17 +134,12 @@
             return false;
         }
 
-        const char* const recovery_dir = "/cache/recovery";
-        const char* const command_file = "/cache/recovery/command";
-        // Ensure /cache/recovery exists.
-        if (adb_mkdir(recovery_dir, 0770) == -1 && errno != EEXIST) {
-            D("Failed to create directory '%s': %s", recovery_dir, strerror(errno));
-            return false;
-        }
-
-        bool write_status = android::base::WriteStringToFile(
-                auto_reboot ? "--sideload_auto_reboot" : "--sideload", command_file);
-        if (!write_status) {
+        const std::vector<std::string> options = {
+            auto_reboot ? "--sideload_auto_reboot" : "--sideload"
+        };
+        std::string err;
+        if (!write_bootloader_message(options, &err)) {
+            D("Failed to set bootloader message: %s", err.c_str());
             return false;
         }
 
diff --git a/adb/sysdeps_win32_test.cpp b/adb/sysdeps_win32_test.cpp
old mode 100755
new mode 100644
diff --git a/adb/trace.sh b/adb/trace.sh
new file mode 100755
index 0000000..49e5026
--- /dev/null
+++ b/adb/trace.sh
@@ -0,0 +1,17 @@
+set -e
+
+if ! [ -e $ANDROID_BUILD_TOP/external/chromium-trace/systrace.py ]; then
+    echo "error: can't find systrace.py at \$ANDROID_BUILD_TOP/external/chromium-trace/systrace.py"
+    exit 1
+fi
+
+adb shell "sleep 1; atrace -b 65536 --async_start adb sched power freq idle disk mmc load"
+adb shell killall adbd
+adb wait-for-device
+echo "press enter to finish..."
+read
+TRACE_TEMP=`mktemp /tmp/trace.XXXXXX`
+echo Saving trace to ${TRACE_TEMP}, html file to ${TRACE_TEMP}.html
+adb shell atrace --async_stop -z > ${TRACE_TEMP}
+$ANDROID_BUILD_TOP/external/chromium-trace/systrace.py --from-file=${TRACE_TEMP} -o ${TRACE_TEMP}.html
+chrome ${TRACE_TEMP}.html
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 7b82b19..60f3b5c 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -37,6 +37,7 @@
 
 #include "adb.h"
 #include "adb_auth.h"
+#include "adb_trace.h"
 #include "adb_utils.h"
 #include "diagnose_usb.h"
 
@@ -52,16 +53,15 @@
 const char* const kFeatureStat2 = "stat_v2";
 
 static std::string dump_packet(const char* name, const char* func, apacket* p) {
-    unsigned  command = p->msg.command;
-    int       len     = p->msg.data_length;
-    char      cmd[9];
-    char      arg0[12], arg1[12];
-    int       n;
+    unsigned command = p->msg.command;
+    int len = p->msg.data_length;
+    char cmd[9];
+    char arg0[12], arg1[12];
+    int n;
 
     for (n = 0; n < 4; n++) {
-        int  b = (command >> (n*8)) & 255;
-        if (b < 32 || b >= 127)
-            break;
+        int b = (command >> (n * 8)) & 255;
+        if (b < 32 || b >= 127) break;
         cmd[n] = (char)b;
     }
     if (n == 4) {
@@ -82,25 +82,24 @@
     else
         snprintf(arg1, sizeof arg1, "0x%x", p->msg.arg1);
 
-    std::string result = android::base::StringPrintf("%s: %s: [%s] arg0=%s arg1=%s (len=%d) ",
-                                                     name, func, cmd, arg0, arg1, len);
+    std::string result = android::base::StringPrintf("%s: %s: [%s] arg0=%s arg1=%s (len=%d) ", name,
+                                                     func, cmd, arg0, arg1, len);
     result += dump_hex(p->data, len);
     return result;
 }
 
-static int
-read_packet(int  fd, const char* name, apacket** ppacket)
-{
+static int read_packet(int fd, const char* name, apacket** ppacket) {
+    ATRACE_NAME("read_packet");
     char buff[8];
     if (!name) {
         snprintf(buff, sizeof buff, "fd=%d", fd);
         name = buff;
     }
-    char* p = reinterpret_cast<char*>(ppacket);  /* really read a packet address */
+    char* p = reinterpret_cast<char*>(ppacket); /* really read a packet address */
     int len = sizeof(apacket*);
-    while(len > 0) {
+    while (len > 0) {
         int r = adb_read(fd, p, len);
-        if(r > 0) {
+        if (r > 0) {
             len -= r;
             p += r;
         } else {
@@ -113,20 +112,19 @@
     return 0;
 }
 
-static int
-write_packet(int  fd, const char* name, apacket** ppacket)
-{
+static int write_packet(int fd, const char* name, apacket** ppacket) {
+    ATRACE_NAME("write_packet");
     char buff[8];
     if (!name) {
         snprintf(buff, sizeof buff, "fd=%d", fd);
         name = buff;
     }
     VLOG(TRANSPORT) << dump_packet(name, "to remote", *ppacket);
-    char* p = reinterpret_cast<char*>(ppacket);  /* we really write the packet address */
+    char* p = reinterpret_cast<char*>(ppacket); /* we really write the packet address */
     int len = sizeof(apacket*);
-    while(len > 0) {
+    while (len > 0) {
         int r = adb_write(fd, p, len);
-        if(r > 0) {
+        if (r > 0) {
             len -= r;
             p += r;
         } else {
@@ -137,16 +135,15 @@
     return 0;
 }
 
-static void transport_socket_events(int fd, unsigned events, void *_t)
-{
-    atransport *t = reinterpret_cast<atransport*>(_t);
+static void transport_socket_events(int fd, unsigned events, void* _t) {
+    atransport* t = reinterpret_cast<atransport*>(_t);
     D("transport_socket_events(fd=%d, events=%04x,...)", fd, events);
-    if(events & FDE_READ){
-        apacket *p = 0;
-        if(read_packet(fd, t->serial, &p)){
+    if (events & FDE_READ) {
+        apacket* p = 0;
+        if (read_packet(fd, t->serial, &p)) {
             D("%s: failed to read packet from transport socket on fd %d", t->serial, fd);
         } else {
-            handle_packet(p, (atransport *) _t);
+            handle_packet(p, (atransport*)_t);
         }
     }
 }
@@ -180,40 +177,43 @@
 // read_transport thread reads data from a transport (representing a usb/tcp connection),
 // and makes the main thread call handle_packet().
 static void read_transport_thread(void* _t) {
-    atransport *t = reinterpret_cast<atransport*>(_t);
-    apacket *p;
+    atransport* t = reinterpret_cast<atransport*>(_t);
+    apacket* p;
 
-    adb_thread_setname(android::base::StringPrintf("<-%s",
-                                                   (t->serial != nullptr ? t->serial : "transport")));
-    D("%s: starting read_transport thread on fd %d, SYNC online (%d)",
-       t->serial, t->fd, t->sync_token + 1);
+    adb_thread_setname(
+        android::base::StringPrintf("<-%s", (t->serial != nullptr ? t->serial : "transport")));
+    D("%s: starting read_transport thread on fd %d, SYNC online (%d)", t->serial, t->fd,
+      t->sync_token + 1);
     p = get_apacket();
     p->msg.command = A_SYNC;
     p->msg.arg0 = 1;
     p->msg.arg1 = ++(t->sync_token);
     p->msg.magic = A_SYNC ^ 0xffffffff;
-    if(write_packet(t->fd, t->serial, &p)) {
+    if (write_packet(t->fd, t->serial, &p)) {
         put_apacket(p);
         D("%s: failed to write SYNC packet", t->serial);
         goto oops;
     }
 
     D("%s: data pump started", t->serial);
-    for(;;) {
+    for (;;) {
+        ATRACE_NAME("read_transport loop");
         p = get_apacket();
 
-        if(t->read_from_remote(p, t) == 0){
-            D("%s: received remote packet, sending to transport",
-              t->serial);
-            if(write_packet(t->fd, t->serial, &p)){
+        {
+            ATRACE_NAME("read_transport read_remote");
+            if (t->read_from_remote(p, t) != 0) {
+                D("%s: remote read failed for transport", t->serial);
                 put_apacket(p);
-                D("%s: failed to write apacket to transport", t->serial);
-                goto oops;
+                break;
             }
-        } else {
-            D("%s: remote read failed for transport", t->serial);
+        }
+
+        D("%s: received remote packet, sending to transport", t->serial);
+        if (write_packet(t->fd, t->serial, &p)) {
             put_apacket(p);
-            break;
+            D("%s: failed to write apacket to transport", t->serial);
+            goto oops;
         }
     }
 
@@ -223,7 +223,7 @@
     p->msg.arg0 = 0;
     p->msg.arg1 = 0;
     p->msg.magic = A_SYNC ^ 0xffffffff;
-    if(write_packet(t->fd, t->serial, &p)) {
+    if (write_packet(t->fd, t->serial, &p)) {
         put_apacket(p);
         D("%s: failed to write SYNC apacket to transport", t->serial);
     }
@@ -237,38 +237,38 @@
 // write_transport thread gets packets sent by the main thread (through send_packet()),
 // and writes to a transport (representing a usb/tcp connection).
 static void write_transport_thread(void* _t) {
-    atransport *t = reinterpret_cast<atransport*>(_t);
-    apacket *p;
+    atransport* t = reinterpret_cast<atransport*>(_t);
+    apacket* p;
     int active = 0;
 
-    adb_thread_setname(android::base::StringPrintf("->%s",
-                                                   (t->serial != nullptr ? t->serial : "transport")));
-    D("%s: starting write_transport thread, reading from fd %d",
-       t->serial, t->fd);
+    adb_thread_setname(
+        android::base::StringPrintf("->%s", (t->serial != nullptr ? t->serial : "transport")));
+    D("%s: starting write_transport thread, reading from fd %d", t->serial, t->fd);
 
-    for(;;){
-        if(read_packet(t->fd, t->serial, &p)) {
-            D("%s: failed to read apacket from transport on fd %d",
-               t->serial, t->fd );
+    for (;;) {
+        ATRACE_NAME("write_transport loop");
+        if (read_packet(t->fd, t->serial, &p)) {
+            D("%s: failed to read apacket from transport on fd %d", t->serial, t->fd);
             break;
         }
-        if(p->msg.command == A_SYNC){
-            if(p->msg.arg0 == 0) {
+
+        if (p->msg.command == A_SYNC) {
+            if (p->msg.arg0 == 0) {
                 D("%s: transport SYNC offline", t->serial);
                 put_apacket(p);
                 break;
             } else {
-                if(p->msg.arg1 == t->sync_token) {
+                if (p->msg.arg1 == t->sync_token) {
                     D("%s: transport SYNC online", t->serial);
                     active = 1;
                 } else {
-                    D("%s: transport ignoring SYNC %d != %d",
-                      t->serial, p->msg.arg1, t->sync_token);
+                    D("%s: transport ignoring SYNC %d != %d", t->serial, p->msg.arg1, t->sync_token);
                 }
             }
         } else {
-            if(active) {
+            if (active) {
                 D("%s: transport got packet, sending to remote", t->serial);
+                ATRACE_NAME("write_transport write_remote");
                 t->write_to_remote(p, t);
             } else {
                 D("%s: transport ignoring packet while offline", t->serial);
@@ -296,7 +296,6 @@
 static int transport_registration_recv = -1;
 static fdevent transport_registration_fde;
 
-
 #if ADB_HOST
 
 /* this adds support required by the 'track-devices' service.
@@ -305,19 +304,17 @@
  * live TCP connection
  */
 struct device_tracker {
-    asocket          socket;
-    int              update_needed;
-    device_tracker*  next;
+    asocket socket;
+    int update_needed;
+    device_tracker* next;
 };
 
 /* linked list of all device trackers */
-static device_tracker*   device_tracker_list;
+static device_tracker* device_tracker_list;
 
-static void
-device_tracker_remove( device_tracker*  tracker )
-{
-    device_tracker**  pnode = &device_tracker_list;
-    device_tracker*   node  = *pnode;
+static void device_tracker_remove(device_tracker* tracker) {
+    device_tracker** pnode = &device_tracker_list;
+    device_tracker* node = *pnode;
 
     std::lock_guard<std::mutex> lock(transport_lock);
     while (node) {
@@ -326,17 +323,15 @@
             break;
         }
         pnode = &node->next;
-        node  = *pnode;
+        node = *pnode;
     }
 }
 
-static void
-device_tracker_close( asocket*  socket )
-{
-    device_tracker*  tracker = (device_tracker*) socket;
-    asocket*         peer    = socket->peer;
+static void device_tracker_close(asocket* socket) {
+    device_tracker* tracker = (device_tracker*)socket;
+    asocket* peer = socket->peer;
 
-    D( "device tracker %p removed", tracker);
+    D("device tracker %p removed", tracker);
     if (peer) {
         peer->peer = NULL;
         peer->close(peer);
@@ -345,9 +340,7 @@
     free(tracker);
 }
 
-static int
-device_tracker_enqueue( asocket*  socket, apacket*  p )
-{
+static int device_tracker_enqueue(asocket* socket, apacket* p) {
     /* you can't read from a device tracker, close immediately */
     put_apacket(p);
     device_tracker_close(socket);
@@ -377,26 +370,23 @@
     }
 }
 
-asocket*
-create_device_tracker(void)
-{
+asocket* create_device_tracker(void) {
     device_tracker* tracker = reinterpret_cast<device_tracker*>(calloc(1, sizeof(*tracker)));
     if (tracker == nullptr) fatal("cannot allocate device tracker");
 
-    D( "device tracker %p created", tracker);
+    D("device tracker %p created", tracker);
 
     tracker->socket.enqueue = device_tracker_enqueue;
-    tracker->socket.ready   = device_tracker_ready;
-    tracker->socket.close   = device_tracker_close;
-    tracker->update_needed  = 1;
+    tracker->socket.ready = device_tracker_ready;
+    tracker->socket.close = device_tracker_close;
+    tracker->update_needed = 1;
 
-    tracker->next       = device_tracker_list;
+    tracker->next = device_tracker_list;
     device_tracker_list = tracker;
 
     return &tracker->socket;
 }
 
-
 // Call this function each time the transport list has changed.
 void update_transports() {
     std::string transports = list_transports(false);
@@ -416,26 +406,23 @@
     // Nothing to do on the device side.
 }
 
-#endif // ADB_HOST
+#endif  // ADB_HOST
 
-struct tmsg
-{
-    atransport *transport;
-    int         action;
+struct tmsg {
+    atransport* transport;
+    int action;
 };
 
-static int
-transport_read_action(int  fd, struct tmsg*  m)
-{
-    char *p   = (char*)m;
-    int   len = sizeof(*m);
-    int   r;
+static int transport_read_action(int fd, struct tmsg* m) {
+    char* p = (char*)m;
+    int len = sizeof(*m);
+    int r;
 
-    while(len > 0) {
+    while (len > 0) {
         r = adb_read(fd, p, len);
-        if(r > 0) {
+        if (r > 0) {
             len -= r;
-            p   += r;
+            p += r;
         } else {
             D("transport_read_action: on fd %d: %s", fd, strerror(errno));
             return -1;
@@ -444,18 +431,16 @@
     return 0;
 }
 
-static int
-transport_write_action(int  fd, struct tmsg*  m)
-{
-    char *p   = (char*)m;
-    int   len = sizeof(*m);
-    int   r;
+static int transport_write_action(int fd, struct tmsg* m) {
+    char* p = (char*)m;
+    int len = sizeof(*m);
+    int r;
 
-    while(len > 0) {
+    while (len > 0) {
         r = adb_write(fd, p, len);
-        if(r > 0) {
+        if (r > 0) {
             len -= r;
-            p   += r;
+            p += r;
         } else {
             D("transport_write_action: on fd %d: %s", fd, strerror(errno));
             return -1;
@@ -464,17 +449,16 @@
     return 0;
 }
 
-static void transport_registration_func(int _fd, unsigned ev, void *data)
-{
+static void transport_registration_func(int _fd, unsigned ev, void* data) {
     tmsg m;
     int s[2];
-    atransport *t;
+    atransport* t;
 
-    if(!(ev & FDE_READ)) {
+    if (!(ev & FDE_READ)) {
         return;
     }
 
-    if(transport_read_action(_fd, &m)) {
+    if (transport_read_action(_fd, &m)) {
         fatal_errno("cannot read transport registration socket");
     }
 
@@ -483,9 +467,9 @@
     if (m.action == 0) {
         D("transport: %s removing and free'ing %d", t->serial, t->transport_socket);
 
-            /* IMPORTANT: the remove closes one half of the
-            ** socket pair.  The close closes the other half.
-            */
+        /* IMPORTANT: the remove closes one half of the
+        ** socket pair.  The close closes the other half.
+        */
         fdevent_remove(&(t->transport_fde));
         adb_close(t->fd);
 
@@ -494,16 +478,11 @@
             transport_list.remove(t);
         }
 
-        if (t->product)
-            free(t->product);
-        if (t->serial)
-            free(t->serial);
-        if (t->model)
-            free(t->model);
-        if (t->device)
-            free(t->device);
-        if (t->devpath)
-            free(t->devpath);
+        if (t->product) free(t->product);
+        if (t->serial) free(t->serial);
+        if (t->model) free(t->model);
+        if (t->device) free(t->device);
+        if (t->devpath) free(t->devpath);
 
         delete t;
 
@@ -525,10 +504,7 @@
         t->transport_socket = s[0];
         t->fd = s[1];
 
-        fdevent_install(&(t->transport_fde),
-                        t->transport_socket,
-                        transport_socket_events,
-                        t);
+        fdevent_install(&(t->transport_fde), t->transport_socket, transport_socket_events, t);
 
         fdevent_set(&(t->transport_fde), FDE_READ);
 
@@ -550,11 +526,10 @@
     update_transports();
 }
 
-void init_transport_registration(void)
-{
+void init_transport_registration(void) {
     int s[2];
 
-    if(adb_socketpair(s)){
+    if (adb_socketpair(s)) {
         fatal_errno("cannot open transport registration socketpair");
     }
     D("socketpair: (%d,%d)", s[0], s[1]);
@@ -562,38 +537,33 @@
     transport_registration_send = s[0];
     transport_registration_recv = s[1];
 
-    fdevent_install(&transport_registration_fde,
-                    transport_registration_recv,
-                    transport_registration_func,
-                    0);
+    fdevent_install(&transport_registration_fde, transport_registration_recv,
+                    transport_registration_func, 0);
 
     fdevent_set(&transport_registration_fde, FDE_READ);
 }
 
 /* the fdevent select pump is single threaded */
-static void register_transport(atransport *transport)
-{
+static void register_transport(atransport* transport) {
     tmsg m;
     m.transport = transport;
     m.action = 1;
     D("transport: %s registered", transport->serial);
-    if(transport_write_action(transport_registration_send, &m)) {
+    if (transport_write_action(transport_registration_send, &m)) {
         fatal_errno("cannot write transport registration socket\n");
     }
 }
 
-static void remove_transport(atransport *transport)
-{
+static void remove_transport(atransport* transport) {
     tmsg m;
     m.transport = transport;
     m.action = 0;
     D("transport: %s removed", transport->serial);
-    if(transport_write_action(transport_registration_send, &m)) {
+    if (transport_write_action(transport_registration_send, &m)) {
         fatal_errno("cannot write transport registration socket\n");
     }
 }
 
-
 static void transport_unref(atransport* t) {
     CHECK(t != nullptr);
 
@@ -609,37 +579,31 @@
     }
 }
 
-static int qual_match(const char *to_test,
-                      const char *prefix, const char *qual, bool sanitize_qual)
-{
-    if (!to_test || !*to_test)
-        /* Return true if both the qual and to_test are null strings. */
+static int qual_match(const char* to_test, const char* prefix, const char* qual,
+                      bool sanitize_qual) {
+    if (!to_test || !*to_test) /* Return true if both the qual and to_test are null strings. */
         return !qual || !*qual;
 
-    if (!qual)
-        return 0;
+    if (!qual) return 0;
 
     if (prefix) {
         while (*prefix) {
-            if (*prefix++ != *to_test++)
-                return 0;
+            if (*prefix++ != *to_test++) return 0;
         }
     }
 
     while (*qual) {
         char ch = *qual++;
-        if (sanitize_qual && !isalnum(ch))
-            ch = '_';
-        if (ch != *to_test++)
-            return 0;
+        if (sanitize_qual && !isalnum(ch)) ch = '_';
+        if (ch != *to_test++) return 0;
     }
 
     /* Everything matched so far.  Return true if *to_test is a NUL. */
     return !*to_test;
 }
 
-atransport* acquire_one_transport(TransportType type, const char* serial,
-                                  bool* is_ambiguous, std::string* error_out) {
+atransport* acquire_one_transport(TransportType type, const char* serial, bool* is_ambiguous,
+                                  std::string* error_out) {
     atransport* result = nullptr;
 
     if (serial) {
@@ -737,15 +701,24 @@
 
 const std::string atransport::connection_state_name() const {
     switch (connection_state) {
-        case kCsOffline: return "offline";
-        case kCsBootloader: return "bootloader";
-        case kCsDevice: return "device";
-        case kCsHost: return "host";
-        case kCsRecovery: return "recovery";
-        case kCsNoPerm: return UsbNoPermissionsShortHelpText();
-        case kCsSideload: return "sideload";
-        case kCsUnauthorized: return "unauthorized";
-        default: return "unknown";
+        case kCsOffline:
+            return "offline";
+        case kCsBootloader:
+            return "bootloader";
+        case kCsDevice:
+            return "device";
+        case kCsHost:
+            return "host";
+        case kCsRecovery:
+            return "recovery";
+        case kCsNoPerm:
+            return UsbNoPermissionsShortHelpText();
+        case kCsSideload:
+            return "sideload";
+        case kCsUnauthorized:
+            return "unauthorized";
+        default:
+            return "unknown";
     }
 }
 
@@ -771,9 +744,7 @@
 const FeatureSet& supported_features() {
     // Local static allocation to avoid global non-POD variables.
     static const FeatureSet* features = new FeatureSet{
-        kFeatureShell2,
-        kFeatureCmd,
-        kFeatureStat2,
+        kFeatureShell2, kFeatureCmd, kFeatureStat2,
         // Increment ADB_SERVER_VERSION whenever the feature list changes to
         // make sure that the adb client and server features stay in sync
         // (http://b/24370690).
@@ -791,14 +762,12 @@
         return FeatureSet();
     }
 
-    auto names = android::base::Split(features_string,
-                                      {kFeatureStringDelimiter});
+    auto names = android::base::Split(features_string, {kFeatureStringDelimiter});
     return FeatureSet(names.begin(), names.end());
 }
 
 bool CanUseFeature(const FeatureSet& feature_set, const std::string& feature) {
-    return feature_set.count(feature) > 0 &&
-            supported_features().count(feature) > 0;
+    return feature_set.count(feature) > 0 && supported_features().count(feature) > 0;
 }
 
 bool atransport::has_feature(const std::string& feature) const {
@@ -834,21 +803,20 @@
 
             // For fastboot compatibility, ignore protocol prefixes.
             if (android::base::StartsWith(target, "tcp:") ||
-                    android::base::StartsWith(target, "udp:")) {
+                android::base::StartsWith(target, "udp:")) {
                 local_target_ptr += 4;
             }
 
             // Parse our |serial| and the given |target| to check if the hostnames and ports match.
             std::string serial_host, error;
             int serial_port = -1;
-            if (android::base::ParseNetAddress(serial, &serial_host, &serial_port, nullptr,
-                                               &error)) {
+            if (android::base::ParseNetAddress(serial, &serial_host, &serial_port, nullptr, &error)) {
                 // |target| may omit the port to default to ours.
                 std::string target_host;
                 int target_port = serial_port;
                 if (android::base::ParseNetAddress(local_target_ptr, &target_host, &target_port,
                                                    nullptr, &error) &&
-                        serial_host == target_host && serial_port == target_port) {
+                    serial_host == target_host && serial_port == target_port) {
                     return true;
                 }
             }
@@ -863,8 +831,8 @@
 
 #if ADB_HOST
 
-static void append_transport_info(std::string* result, const char* key,
-                                  const char* value, bool sanitize) {
+static void append_transport_info(std::string* result, const char* key, const char* value,
+                                  bool sanitize) {
     if (value == nullptr || *value == '\0') {
         return;
     }
@@ -877,8 +845,7 @@
     }
 }
 
-static void append_transport(const atransport* t, std::string* result,
-                             bool long_listing) {
+static void append_transport(const atransport* t, std::string* result, bool long_listing) {
     const char* serial = t->serial;
     if (!serial || !serial[0]) {
         serial = "(no serial number)";
@@ -889,8 +856,7 @@
         *result += '\t';
         *result += t->connection_state_name();
     } else {
-        android::base::StringAppendF(result, "%-22s %s", serial,
-                                     t->connection_state_name().c_str());
+        android::base::StringAppendF(result, "%-22s %s", serial, t->connection_state_name().c_str());
 
         append_transport_info(result, "", t->devpath, false);
         append_transport_info(result, "product:", t->product, false);
@@ -923,9 +889,9 @@
 void close_usb_devices() {
     close_usb_devices([](const atransport*) { return true; });
 }
-#endif // ADB_HOST
+#endif  // ADB_HOST
 
-int register_socket_transport(int s, const char *serial, int port, int local) {
+int register_socket_transport(int s, const char* serial, int port, int local) {
     atransport* t = new atransport();
 
     if (!serial) {
@@ -944,7 +910,7 @@
     for (const auto& transport : pending_list) {
         if (transport->serial && strcmp(serial, transport->serial) == 0) {
             VLOG(TRANSPORT) << "socket transport " << transport->serial
-                << " is already in pending_list and fails to register";
+                            << " is already in pending_list and fails to register";
             delete t;
             return -1;
         }
@@ -953,7 +919,7 @@
     for (const auto& transport : transport_list) {
         if (transport->serial && strcmp(serial, transport->serial) == 0) {
             VLOG(TRANSPORT) << "socket transport " << transport->serial
-                << " is already in transport_list and fails to register";
+                            << " is already in transport_list and fails to register";
             delete t;
             return -1;
         }
@@ -969,7 +935,7 @@
 }
 
 #if ADB_HOST
-atransport *find_transport(const char *serial) {
+atransport* find_transport(const char* serial) {
     atransport* result = nullptr;
 
     std::lock_guard<std::mutex> lock(transport_lock);
@@ -998,14 +964,13 @@
 
 #endif
 
-void register_usb_transport(usb_handle* usb, const char* serial,
-                            const char* devpath, unsigned writeable) {
+void register_usb_transport(usb_handle* usb, const char* serial, const char* devpath,
+                            unsigned writeable) {
     atransport* t = new atransport();
 
-    D("transport: %p init'ing for usb_handle %p (sn='%s')", t, usb,
-      serial ? serial : "");
+    D("transport: %p init'ing for usb_handle %p (sn='%s')", t, usb, serial ? serial : "");
     init_usb_transport(t, usb, (writeable ? kCsOffline : kCsNoPerm));
-    if(serial) {
+    if (serial) {
         t->serial = strdup(serial);
     }
 
@@ -1022,23 +987,21 @@
 }
 
 // This should only be used for transports with connection_state == kCsNoPerm.
-void unregister_usb_transport(usb_handle *usb) {
+void unregister_usb_transport(usb_handle* usb) {
     std::lock_guard<std::mutex> lock(transport_lock);
-    transport_list.remove_if([usb](atransport* t) {
-        return t->usb == usb && t->connection_state == kCsNoPerm;
-    });
+    transport_list.remove_if(
+        [usb](atransport* t) { return t->usb == usb && t->connection_state == kCsNoPerm; });
 }
 
-int check_header(apacket *p, atransport *t)
-{
-    if(p->msg.magic != (p->msg.command ^ 0xffffffff)) {
+int check_header(apacket* p, atransport* t) {
+    if (p->msg.magic != (p->msg.command ^ 0xffffffff)) {
         VLOG(RWX) << "check_header(): invalid magic";
         return -1;
     }
 
-    if(p->msg.data_length > t->get_max_payload()) {
-        VLOG(RWX) << "check_header(): " << p->msg.data_length << " atransport::max_payload = "
-                  << t->get_max_payload();
+    if (p->msg.data_length > t->get_max_payload()) {
+        VLOG(RWX) << "check_header(): " << p->msg.data_length
+                  << " atransport::max_payload = " << t->get_max_payload();
         return -1;
     }
 
diff --git a/base/include/android-base/memory.h b/base/include/android-base/memory.h
index 3a2f8fa..9971226 100644
--- a/base/include/android-base/memory.h
+++ b/base/include/android-base/memory.h
@@ -20,25 +20,19 @@
 namespace android {
 namespace base {
 
-// Use packed structures for access to unaligned data on targets with alignment
+// Use memcpy for access to unaligned data on targets with alignment
 // restrictions.  The compiler will generate appropriate code to access these
 // structures without generating alignment exceptions.
 template <typename T>
-static inline T get_unaligned(const T* address) {
-  struct unaligned {
-    T v;
-  } __attribute__((packed));
-  const unaligned* p = reinterpret_cast<const unaligned*>(address);
-  return p->v;
+static inline T get_unaligned(const void* address) {
+  T result;
+  memcpy(&result, address, sizeof(T));
+  return result;
 }
 
 template <typename T>
-static inline void put_unaligned(T* address, T v) {
-  struct unaligned {
-    T v;
-  } __attribute__((packed));
-  unaligned* p = reinterpret_cast<unaligned*>(address);
-  p->v = v;
+static inline void put_unaligned(void* address, T v) {
+  memcpy(address, &v, sizeof(T));
 }
 
 } // namespace base
diff --git a/base/include/android-base/properties.h b/base/include/android-base/properties.h
index 95d1b6a..4d7082a 100644
--- a/base/include/android-base/properties.h
+++ b/base/include/android-base/properties.h
@@ -61,4 +61,4 @@
 } // namespace base
 } // namespace android
 
-#endif  // ANDROID_BASE_MEMORY_H
+#endif  // ANDROID_BASE_PROPERTIES_H
diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk
index 607745d..e3bdd43 100644
--- a/debuggerd/Android.mk
+++ b/debuggerd/Android.mk
@@ -52,7 +52,7 @@
 
 include $(BUILD_EXECUTABLE)
 
-crasher_cppflags := $(common_cppflags) -fstack-protector-all -Wno-free-nonheap-object -Wno-date-time
+crasher_cppflags := $(common_cppflags) -O0 -fstack-protector-all -Wno-free-nonheap-object
 
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES := crasher.cpp
@@ -65,7 +65,7 @@
 LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
 LOCAL_MODULE_TAGS := optional
 LOCAL_CPPFLAGS := $(crasher_cppflags)
-LOCAL_SHARED_LIBRARIES := libcutils liblog
+LOCAL_SHARED_LIBRARIES := libbase liblog
 
 # The arm emulator has VFP but not VFPv3-D32.
 ifeq ($(ARCH_ARM_HAVE_VFP_D32),true)
@@ -91,7 +91,6 @@
 LOCAL_MODULE_TAGS := optional
 LOCAL_CPPFLAGS := $(crasher_cppflags) -DSTATIC_CRASHER
 LOCAL_FORCE_STATIC_EXECUTABLE := true
-LOCAL_SHARED_LIBRARIES := libcutils liblog
 
 # The arm emulator has VFP but not VFPv3-D32.
 ifeq ($(ARCH_ARM_HAVE_VFP_D32),true)
@@ -103,7 +102,7 @@
 LOCAL_MODULE_STEM_64 := static_crasher64
 LOCAL_MULTILIB := both
 
-LOCAL_STATIC_LIBRARIES := libdebuggerd_client libcutils liblog
+LOCAL_STATIC_LIBRARIES := libdebuggerd_client libbase liblog
 
 include $(BUILD_EXECUTABLE)
 
diff --git a/debuggerd/crasher.cpp b/debuggerd/crasher.cpp
index b0e8b17..e650f22 100644
--- a/debuggerd/crasher.cpp
+++ b/debuggerd/crasher.cpp
@@ -17,47 +17,43 @@
 #define LOG_TAG "crasher"
 
 #include <assert.h>
+#include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <pthread.h>
-#include <sched.h>
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sys/cdefs.h>
 #include <sys/mman.h>
-#include <sys/ptrace.h>
-#include <sys/socket.h>
-#include <sys/wait.h>
 #include <unistd.h>
 
+// We test both kinds of logging.
 #include <android/log.h>
-#include <cutils/sockets.h>
+#include <android-base/logging.h>
 
 #if defined(STATIC_CRASHER)
 #include "debuggerd/client.h"
 #endif
 
-#ifndef __unused
-#define __unused __attribute__((__unused__))
-#endif
+#define noinline __attribute__((__noinline__))
 
-extern const char* __progname;
+// Avoid name mangling so that stacks are more readable.
+extern "C" {
 
-extern "C" void crash1(void);
-extern "C" void crashnostack(void);
+void crash1(void);
+void crashnostack(void);
 
-static int do_action(const char* arg);
+int do_action(const char* arg);
 
-static void maybe_abort() {
+noinline void maybe_abort() {
     if (time(0) != 42) {
         abort();
     }
 }
 
-static char* smash_stack_dummy_buf;
-__attribute__ ((noinline)) static void smash_stack_dummy_function(volatile int* plen) {
+char* smash_stack_dummy_buf;
+noinline void smash_stack_dummy_function(volatile int* plen) {
   smash_stack_dummy_buf[*plen] = 0;
 }
 
@@ -65,8 +61,8 @@
 // compiler generates the proper stack guards around this function.
 // Assign local array address to global variable to force stack guards.
 // Use another noinline function to corrupt the stack.
-__attribute__ ((noinline)) static int smash_stack(volatile int* plen) {
-    printf("%s: deliberately corrupting stack...\n", __progname);
+noinline int smash_stack(volatile int* plen) {
+    printf("%s: deliberately corrupting stack...\n", getprogname());
 
     char buf[128];
     smash_stack_dummy_buf = buf;
@@ -75,91 +71,107 @@
     return 0;
 }
 
-#if defined(__clang__)
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Winfinite-recursion"
-#endif
 
-static void* global = 0; // So GCC doesn't optimize the tail recursion out of overflow_stack.
+void* global = 0; // So GCC doesn't optimize the tail recursion out of overflow_stack.
 
-__attribute__((noinline)) static void overflow_stack(void* p) {
+noinline void overflow_stack(void* p) {
     void* buf[1];
     buf[0] = p;
     global = buf;
     overflow_stack(&buf);
 }
 
-#if defined(__clang__)
 #pragma clang diagnostic pop
-#endif
 
-static void *noisy(void *x)
-{
-    char c = (uintptr_t) x;
-    for(;;) {
-        usleep(250*1000);
-        write(2, &c, 1);
-        if(c == 'C') *((volatile unsigned*) 0) = 42;
-    }
-    return NULL;
+noinline void* thread_callback(void* raw_arg) {
+    const char* arg = reinterpret_cast<const char*>(raw_arg);
+    return reinterpret_cast<void*>(static_cast<uintptr_t>(do_action(arg)));
 }
 
-static int ctest()
-{
-    pthread_t thr;
-    pthread_attr_t attr;
-    pthread_attr_init(&attr);
-    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
-    pthread_create(&thr, &attr, noisy, (void*) 'A');
-    pthread_create(&thr, &attr, noisy, (void*) 'B');
-    pthread_create(&thr, &attr, noisy, (void*) 'C');
-    for(;;) ;
-    return 0;
-}
-
-static void* thread_callback(void* raw_arg)
-{
-    return (void*) (uintptr_t) do_action((const char*) raw_arg);
-}
-
-static int do_action_on_thread(const char* arg)
-{
+noinline int do_action_on_thread(const char* arg) {
     pthread_t t;
-    pthread_create(&t, NULL, thread_callback, (void*) arg);
-    void* result = NULL;
+    pthread_create(&t, nullptr, thread_callback, const_cast<char*>(arg));
+    void* result = nullptr;
     pthread_join(t, &result);
-    return (int) (uintptr_t) result;
+    return reinterpret_cast<uintptr_t>(result);
 }
 
-__attribute__((noinline)) static int crash3(int a) {
-    *((int*) 0xdead) = a;
+noinline int crash3(int a) {
+    *reinterpret_cast<int*>(0xdead) = a;
     return a*4;
 }
 
-__attribute__((noinline)) static int crash2(int a) {
+noinline int crash2(int a) {
     a = crash3(a) + 2;
     return a*3;
 }
 
-__attribute__((noinline)) static int crash(int a) {
+noinline int crash(int a) {
     a = crash2(a) + 1;
     return a*2;
 }
 
-static void abuse_heap() {
+noinline void abuse_heap() {
     char buf[16];
-    free((void*) buf); // GCC is smart enough to warn about this, but we're doing it deliberately.
+    free(buf); // GCC is smart enough to warn about this, but we're doing it deliberately.
 }
 
-static void sigsegv_non_null() {
+noinline void sigsegv_non_null() {
     int* a = (int *)(&do_action);
     *a = 42;
 }
 
-static int do_action(const char* arg)
-{
-    fprintf(stderr, "%s: init pid=%d tid=%d\n", __progname, getpid(), gettid());
+noinline void fprintf_null() {
+    fprintf(nullptr, "oops");
+}
 
+noinline void readdir_null() {
+    readdir(nullptr);
+}
+
+noinline int strlen_null() {
+    char* sneaky_null = nullptr;
+    return strlen(sneaky_null);
+}
+
+static int usage() {
+    fprintf(stderr, "usage: %s KIND\n", getprogname());
+    fprintf(stderr, "\n");
+    fprintf(stderr, "where KIND is:\n");
+    fprintf(stderr, "  smash-stack           overwrite a -fstack-protector guard\n");
+    fprintf(stderr, "  stack-overflow        recurse until the stack overflows\n");
+    fprintf(stderr, "  heap-corruption       cause a libc abort by corrupting the heap\n");
+    fprintf(stderr, "  heap-usage            cause a libc abort by abusing a heap function\n");
+    fprintf(stderr, "  nostack               crash with a NULL stack pointer\n");
+    fprintf(stderr, "  abort                 call abort()\n");
+    fprintf(stderr, "  assert                call assert() without a function\n");
+    fprintf(stderr, "  assert2               call assert() with a function\n");
+    fprintf(stderr, "  exit                  call exit(1)\n");
+    fprintf(stderr, "  fortify               fail a _FORTIFY_SOURCE check\n");
+    fprintf(stderr, "  LOG_ALWAYS_FATAL      call liblog LOG_ALWAYS_FATAL\n");
+    fprintf(stderr, "  LOG_ALWAYS_FATAL_IF   call liblog LOG_ALWAYS_FATAL_IF\n");
+    fprintf(stderr, "  LOG-FATAL             call libbase LOG(FATAL)\n");
+    fprintf(stderr, "  SIGFPE                cause a SIGFPE\n");
+    fprintf(stderr, "  SIGSEGV               cause a SIGSEGV at address 0x0 (synonym: crash)\n");
+    fprintf(stderr, "  SIGSEGV-non-null      cause a SIGSEGV at a non-zero address\n");
+    fprintf(stderr, "  SIGSEGV-unmapped      mmap/munmap a region of memory and then attempt to access it\n");
+    fprintf(stderr, "  SIGTRAP               cause a SIGTRAP\n");
+    fprintf(stderr, "  fprintf-NULL          pass a null pointer to fprintf\n");
+    fprintf(stderr, "  readdir-NULL          pass a null pointer to readdir\n");
+    fprintf(stderr, "  strlen-NULL           pass a null pointer to strlen\n");
+    fprintf(stderr, "\n");
+    fprintf(stderr, "prefix any of the above with 'thread-' to run on a new thread\n");
+    fprintf(stderr, "prefix any of the above with 'exhaustfd-' to exhaust\n");
+    fprintf(stderr, "all available file descriptors before crashing.\n");
+    fprintf(stderr, "prefix any of the above with 'wait-' to wait until input is received on stdin\n");
+
+    return EXIT_FAILURE;
+}
+
+noinline int do_action(const char* arg) {
+    // Prefixes.
     if (!strncmp(arg, "wait-", strlen("wait-"))) {
       char buf[1];
       TEMP_FAILURE_RETRY(read(STDIN_FILENO, buf, sizeof(buf)));
@@ -172,82 +184,66 @@
       return do_action(arg + strlen("exhaustfd-"));
     } else if (!strncmp(arg, "thread-", strlen("thread-"))) {
         return do_action_on_thread(arg + strlen("thread-"));
-    } else if (!strcmp(arg, "SIGSEGV-non-null")) {
+    }
+
+    // Actions.
+    if (!strcasecmp(arg, "SIGSEGV-non-null")) {
         sigsegv_non_null();
-    } else if (!strcmp(arg, "smash-stack")) {
+    } else if (!strcasecmp(arg, "smash-stack")) {
         volatile int len = 128;
         return smash_stack(&len);
-    } else if (!strcmp(arg, "stack-overflow")) {
-        overflow_stack(NULL);
-    } else if (!strcmp(arg, "nostack")) {
+    } else if (!strcasecmp(arg, "stack-overflow")) {
+        overflow_stack(nullptr);
+    } else if (!strcasecmp(arg, "nostack")) {
         crashnostack();
-    } else if (!strcmp(arg, "ctest")) {
-        return ctest();
-    } else if (!strcmp(arg, "exit")) {
+    } else if (!strcasecmp(arg, "exit")) {
         exit(1);
-    } else if (!strcmp(arg, "crash") || !strcmp(arg, "SIGSEGV")) {
+    } else if (!strcasecmp(arg, "crash") || !strcmp(arg, "SIGSEGV")) {
         return crash(42);
-    } else if (!strcmp(arg, "abort")) {
+    } else if (!strcasecmp(arg, "abort")) {
         maybe_abort();
-    } else if (!strcmp(arg, "assert")) {
+    } else if (!strcasecmp(arg, "assert")) {
         __assert("some_file.c", 123, "false");
-    } else if (!strcmp(arg, "assert2")) {
+    } else if (!strcasecmp(arg, "assert2")) {
         __assert2("some_file.c", 123, "some_function", "false");
-    } else if (!strcmp(arg, "fortify")) {
+    } else if (!strcasecmp(arg, "fortify")) {
         char buf[10];
         __read_chk(-1, buf, 32, 10);
         while (true) pause();
-    } else if (!strcmp(arg, "LOG_ALWAYS_FATAL")) {
+    } else if (!strcasecmp(arg, "LOG(FATAL)")) {
+        LOG(FATAL) << "hello " << 123;
+    } else if (!strcasecmp(arg, "LOG_ALWAYS_FATAL")) {
         LOG_ALWAYS_FATAL("hello %s", "world");
-    } else if (!strcmp(arg, "LOG_ALWAYS_FATAL_IF")) {
+    } else if (!strcasecmp(arg, "LOG_ALWAYS_FATAL_IF")) {
         LOG_ALWAYS_FATAL_IF(true, "hello %s", "world");
-    } else if (!strcmp(arg, "SIGFPE")) {
+    } else if (!strcasecmp(arg, "SIGFPE")) {
         raise(SIGFPE);
         return EXIT_SUCCESS;
-    } else if (!strcmp(arg, "SIGTRAP")) {
+    } else if (!strcasecmp(arg, "SIGTRAP")) {
         raise(SIGTRAP);
         return EXIT_SUCCESS;
-    } else if (!strcmp(arg, "heap-usage")) {
+    } else if (!strcasecmp(arg, "fprintf-NULL")) {
+        fprintf_null();
+    } else if (!strcasecmp(arg, "readdir-NULL")) {
+        readdir_null();
+    } else if (!strcasecmp(arg, "strlen-NULL")) {
+        return strlen_null();
+    } else if (!strcasecmp(arg, "heap-usage")) {
         abuse_heap();
-    } else if (!strcmp(arg, "SIGSEGV-unmapped")) {
-        char* map = reinterpret_cast<char*>(mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0));
+    } else if (!strcasecmp(arg, "SIGSEGV-unmapped")) {
+        char* map = reinterpret_cast<char*>(mmap(nullptr, sizeof(int), PROT_READ | PROT_WRITE,
+                                                 MAP_SHARED | MAP_ANONYMOUS, -1, 0));
         munmap(map, sizeof(int));
         map[0] = '8';
+    } else {
+        return usage();
     }
 
-    fprintf(stderr, "%s OP\n", __progname);
-    fprintf(stderr, "where OP is:\n");
-    fprintf(stderr, "  smash-stack           overwrite a stack-guard canary\n");
-    fprintf(stderr, "  stack-overflow        recurse until the stack overflows\n");
-    fprintf(stderr, "  heap-corruption       cause a libc abort by corrupting the heap\n");
-    fprintf(stderr, "  heap-usage            cause a libc abort by abusing a heap function\n");
-    fprintf(stderr, "  nostack               crash with a NULL stack pointer\n");
-    fprintf(stderr, "  ctest                 (obsoleted by thread-crash?)\n");
-    fprintf(stderr, "  exit                  call exit(1)\n");
-    fprintf(stderr, "  abort                 call abort()\n");
-    fprintf(stderr, "  assert                call assert() without a function\n");
-    fprintf(stderr, "  assert2               call assert() with a function\n");
-    fprintf(stderr, "  fortify               fail a _FORTIFY_SOURCE check\n");
-    fprintf(stderr, "  LOG_ALWAYS_FATAL      call LOG_ALWAYS_FATAL\n");
-    fprintf(stderr, "  LOG_ALWAYS_FATAL_IF   call LOG_ALWAYS_FATAL\n");
-    fprintf(stderr, "  SIGFPE                cause a SIGFPE\n");
-    fprintf(stderr, "  SIGSEGV               cause a SIGSEGV at address 0x0 (synonym: crash)\n");
-    fprintf(stderr, "  SIGSEGV-non-null      cause a SIGSEGV at a non-zero address\n");
-    fprintf(stderr, "  SIGSEGV-unmapped      mmap/munmap a region of memory and then attempt to access it\n");
-    fprintf(stderr, "  SIGTRAP               cause a SIGTRAP\n");
-    fprintf(stderr, "prefix any of the above with 'thread-' to not run\n");
-    fprintf(stderr, "on the process' main thread.\n");
-    fprintf(stderr, "prefix any of the above with 'exhaustfd-' to exhaust\n");
-    fprintf(stderr, "all available file descriptors before crashing.\n");
-    fprintf(stderr, "prefix any of the above with 'wait-' to wait until input is received on stdin\n");
-
+    fprintf(stderr, "%s: exiting normally!\n", getprogname());
     return EXIT_SUCCESS;
 }
 
-int main(int argc, char **argv)
-{
-    fprintf(stderr, "%s: built at " __TIME__ "!@\n", __progname);
-
+int main(int argc, char** argv) {
 #if defined(STATIC_CRASHER)
     debuggerd_callbacks_t callbacks = {
       .get_abort_message = []() {
@@ -265,11 +261,10 @@
     debuggerd_init(&callbacks);
 #endif
 
-    if (argc > 1) {
-        return do_action(argv[1]);
-    } else {
-        crash1();
-    }
+    if (argc == 1) crash1();
+    else if (argc == 2) return do_action(argv[1]);
 
-    return 0;
+    return usage();
 }
+
+};
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 7f4a0dd..3f8bc8f 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -1742,6 +1742,14 @@
         } else if(!strcmp(*argv, "set_active")) {
             require(2);
             std::string slot = verify_slot(transport, std::string(argv[1]), false);
+            // Legacy support: verify_slot() removes leading underscores, we need to put them back
+            // in for old bootloaders. Legacy bootloaders do not have the slot-count variable but
+            // do have slot-suffixes.
+            std::string var;
+            if (!fb_getvar(transport, "slot-count", &var) &&
+                    fb_getvar(transport, "slot-suffixes", &var)) {
+                slot = "_" + slot;
+            }
             fb_set_active(slot.c_str());
             skip(2);
         } else if(!strcmp(*argv, "oem")) {
diff --git a/fastboot/fastboot_protocol.txt b/fastboot/fastboot_protocol.txt
index 2801703..b12e420 100644
--- a/fastboot/fastboot_protocol.txt
+++ b/fastboot/fastboot_protocol.txt
@@ -78,7 +78,7 @@
 
 Host:    "getvar:nonexistant"    request some undefined variable
 
-Client:  "OKAY"                  return value ""
+Client:  "FAILUnknown variable"  getvar failure; see getvar details below
 
 Host:    "download:00001234"     request to send 0x1234 bytes of data
 
@@ -113,7 +113,14 @@
 
  "getvar:%s"           Read a config/version variable from the bootloader.
                        The variable contents will be returned after the
-                       OKAY response.
+                       OKAY response. If the variable is unknown, the bootloader
+                       should return a FAIL response, optionally with an error
+                       message.
+
+                       Previous versions of this document indicated that getvar
+                       should return an empty OKAY response for unknown
+                       variables, so older devices might exhibit this behavior,
+                       but new implementations should return FAIL instead.
 
  "download:%08x"       Write data to memory which will be later used
                        by "boot", "ramdisk", "flash", etc.  The client
@@ -215,7 +222,7 @@
 Host    [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x0E]getvar:version
 Device  [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x07]OKAY0.4
 Host    [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x0B]getvar:none
-Device  [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x04]OKAY
+Device  [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x14]FAILUnknown variable
 Host    <disconnect>
 
 
@@ -364,10 +371,10 @@
                                         0x03  0x00  0x00  0x01
 0x03  0x00  0x00  0x02
                                         0x03  0x00  0x00  0x02  OKAY0.4
-0x03  0x00  0x00  0x03  getvar:foo
+0x03  0x00  0x00  0x03  getvar:none
                                         0x03  0x00  0x00  0x03
 0x03  0x00  0x00  0x04
-                                        0x03  0x00  0x00  0x04  OKAY
+                                        0x03  0x00  0x00  0x04  FAILUnknown var
 
 ----------------------------------------------------------------------
 [fastboot "INFO" responses, S = 0x0000]
diff --git a/fingerprintd/FingerprintDaemonProxy.cpp b/fingerprintd/FingerprintDaemonProxy.cpp
index 0119e55..73748f2 100644
--- a/fingerprintd/FingerprintDaemonProxy.cpp
+++ b/fingerprintd/FingerprintDaemonProxy.cpp
@@ -171,7 +171,7 @@
 
     hardware::hidl_array<uint8_t, hw_auth_token_size> hat(token);
     Return<RequestStatus> ret = gBFP->enroll(hat, groupId, timeout);
-    if (!ret.getStatus().isOk()) {
+    if (!ret.isOk()) {
         ALOGE("Unknown transport error");
         return -1;
     }
@@ -186,7 +186,7 @@
 
 int32_t FingerprintDaemonProxy::postEnroll() {
     Return<RequestStatus> ret = gBFP->postEnroll();
-    if (!ret.getStatus().isOk()) {
+    if (!ret.isOk()) {
         ALOGE("Unknown transport error");
         return -1;
     }
@@ -198,7 +198,7 @@
 int32_t FingerprintDaemonProxy::stopEnrollment() {
     ALOG(LOG_VERBOSE, LOG_TAG, "stopEnrollment()\n");
     Return<RequestStatus> ret = gBFP->cancel();
-    if (!ret.getStatus().isOk()) {
+    if (!ret.isOk()) {
         ALOGE("Unknown transport error");
         return -1;
     }
@@ -210,7 +210,7 @@
 int32_t FingerprintDaemonProxy::authenticate(uint64_t sessionId, uint32_t groupId) {
     ALOG(LOG_VERBOSE, LOG_TAG, "authenticate(sid=%" PRId64 ", gid=%d)\n", sessionId, groupId);
     Return<RequestStatus> ret = gBFP->authenticate(sessionId, groupId);
-    if (!ret.getStatus().isOk()) {
+    if (!ret.isOk()) {
         ALOGE("Unknown transport error");
         return -1;
     }
@@ -222,7 +222,7 @@
 int32_t FingerprintDaemonProxy::stopAuthentication() {
     ALOG(LOG_VERBOSE, LOG_TAG, "stopAuthentication()\n");
     Return<RequestStatus> ret = gBFP->cancel();
-    if (!ret.getStatus().isOk()) {
+    if (!ret.isOk()) {
         ALOGE("Unknown transport error");
         return -1;
     }
@@ -234,7 +234,7 @@
 int32_t FingerprintDaemonProxy::remove(int32_t fingerId, int32_t groupId) {
     ALOG(LOG_VERBOSE, LOG_TAG, "remove(fid=%d, gid=%d)\n", fingerId, groupId);
     Return<RequestStatus> ret = gBFP->remove(groupId, fingerId);
-    if (!ret.getStatus().isOk()) {
+    if (!ret.isOk()) {
         ALOGE("Unknown transport error");
         return -1;
     }
@@ -246,7 +246,7 @@
 int32_t FingerprintDaemonProxy::enumerate() {
     ALOG(LOG_VERBOSE, LOG_TAG, "enumerate()\n");
     Return<RequestStatus> ret = gBFP->enumerate();
-    if (!ret.getStatus().isOk()) {
+    if (!ret.isOk()) {
         ALOGE("Unknown transport error");
         return -1;
     }
@@ -269,7 +269,7 @@
     pathname.setToExternal(reinterpret_cast<const char*>(path), pathlen);
     ALOG(LOG_VERBOSE, LOG_TAG, "setActiveGroup(%d, %s, %zu)", groupId, pathname.c_str(), pathlen);
     Return<RequestStatus> ret = gBFP->setActiveGroup(groupId, pathname);
-    if (!ret.getStatus().isOk()) {
+    if (!ret.isOk()) {
         ALOGE("Unknown transport error");
         return -1;
     }
diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c
index f682216..88b5c98 100644
--- a/fs_mgr/fs_mgr.c
+++ b/fs_mgr/fs_mgr.c
@@ -213,6 +213,73 @@
             le32_to_cpu(es->s_r_blocks_count_lo);
 }
 
+static int do_quota(char *blk_device, char *fs_type, struct fstab_rec *rec)
+{
+    int force_check = 0;
+    if (!strcmp(fs_type, "ext4")) {
+        /*
+         * Some system images do not have tune2fs for licensing reasons
+         * Detect these and skip reserve blocks.
+         */
+        if (access(TUNE2FS_BIN, X_OK)) {
+            ERROR("Not running %s on %s (executable not in system image)\n",
+                  TUNE2FS_BIN, blk_device);
+        } else {
+            char* arg1 = NULL;
+            char* arg2 = NULL;
+            int status = 0;
+            int ret = 0;
+            int fd = TEMP_FAILURE_RETRY(open(blk_device, O_RDONLY | O_CLOEXEC));
+            if (fd >= 0) {
+                struct ext4_super_block sb;
+                ret = read_super_block(fd, &sb);
+                if (ret < 0) {
+                    ERROR("Can't read '%s' super block: %s\n", blk_device, strerror(errno));
+                    goto out;
+                }
+
+                int has_quota = (sb.s_feature_ro_compat
+                        & cpu_to_le32(EXT4_FEATURE_RO_COMPAT_QUOTA)) != 0;
+                int want_quota = fs_mgr_is_quota(rec) != 0;
+
+                if (has_quota == want_quota) {
+                    INFO("Requested quota status is match on %s\n", blk_device);
+                    goto out;
+                } else if (want_quota) {
+                    INFO("Enabling quota on %s\n", blk_device);
+                    arg1 = "-Oquota";
+                    arg2 = "-Qusrquota,grpquota";
+                    force_check = 1;
+                } else {
+                    INFO("Disabling quota on %s\n", blk_device);
+                    arg1 = "-Q^usrquota,^grpquota";
+                    arg2 = "-O^quota";
+                }
+            } else {
+                ERROR("Failed to open '%s': %s\n", blk_device, strerror(errno));
+                return force_check;
+            }
+
+            char *tune2fs_argv[] = {
+                TUNE2FS_BIN,
+                arg1,
+                arg2,
+                blk_device,
+            };
+            ret = android_fork_execvp_ext(ARRAY_SIZE(tune2fs_argv), tune2fs_argv,
+                                          &status, true, LOG_KLOG | LOG_FILE,
+                                          true, NULL, NULL, 0);
+            if (ret < 0) {
+                /* No need to check for error in fork, we can't really handle it now */
+                ERROR("Failed trying to run %s\n", TUNE2FS_BIN);
+            }
+      out:
+            close(fd);
+        }
+    }
+    return force_check;
+}
+
 static void do_reserved_size(char *blk_device, char *fs_type, struct fstab_rec *rec)
 {
     /* Check for the types of filesystems we know how to check */
@@ -397,7 +464,7 @@
     if (!end_idx || !attempted_idx || start_idx >= fstab->num_entries) {
       errno = EINVAL;
       if (end_idx) *end_idx = start_idx;
-      if (attempted_idx) *end_idx = start_idx;
+      if (attempted_idx) *attempted_idx = start_idx;
       return -1;
     }
 
@@ -417,7 +484,10 @@
                 continue;
             }
 
-            if (fstab->recs[i].fs_mgr_flags & MF_CHECK) {
+            int force_check = do_quota(fstab->recs[i].blk_device, fstab->recs[i].fs_type,
+                                       &fstab->recs[i]);
+
+            if ((fstab->recs[i].fs_mgr_flags & MF_CHECK) || force_check) {
                 check_fs(fstab->recs[i].blk_device, fstab->recs[i].fs_type,
                          fstab->recs[i].mount_point);
             }
@@ -787,7 +857,10 @@
             wait_for_file(n_blk_device, WAIT_TIMEOUT);
         }
 
-        if (fstab->recs[i].fs_mgr_flags & MF_CHECK) {
+        int force_check = do_quota(fstab->recs[i].blk_device, fstab->recs[i].fs_type,
+                                   &fstab->recs[i]);
+
+        if ((fstab->recs[i].fs_mgr_flags & MF_CHECK) || force_check) {
             check_fs(n_blk_device, fstab->recs[i].fs_type,
                      fstab->recs[i].mount_point);
         }
diff --git a/fs_mgr/fs_mgr_fstab.c b/fs_mgr/fs_mgr_fstab.c
index f25d10c..41fb746 100644
--- a/fs_mgr/fs_mgr_fstab.c
+++ b/fs_mgr/fs_mgr_fstab.c
@@ -62,30 +62,31 @@
 };
 
 static struct flag_list fs_mgr_flags[] = {
-    { "wait",        MF_WAIT },
-    { "check",       MF_CHECK },
-    { "encryptable=",MF_CRYPT },
-    { "forceencrypt=",MF_FORCECRYPT },
-    { "fileencryption=",MF_FILEENCRYPTION },
-    { "forcefdeorfbe=",MF_FORCEFDEORFBE },
-    { "nonremovable",MF_NONREMOVABLE },
-    { "voldmanaged=",MF_VOLDMANAGED},
-    { "length=",     MF_LENGTH },
-    { "recoveryonly",MF_RECOVERYONLY },
-    { "swapprio=",   MF_SWAPPRIO },
-    { "zramsize=",   MF_ZRAMSIZE },
-    { "max_comp_streams=",   MF_MAX_COMP_STREAMS },
-    { "verifyatboot", MF_VERIFYATBOOT },
-    { "verify",      MF_VERIFY },
-    { "noemulatedsd", MF_NOEMULATEDSD },
-    { "notrim",       MF_NOTRIM },
-    { "formattable", MF_FORMATTABLE },
-    { "slotselect",  MF_SLOTSELECT },
-    { "nofail",      MF_NOFAIL },
-    { "latemount",   MF_LATEMOUNT },
-    { "reservedsize=", MF_RESERVEDSIZE },
-    { "defaults",    0 },
-    { 0,             0 },
+    { "wait",               MF_WAIT },
+    { "check",              MF_CHECK },
+    { "encryptable=",       MF_CRYPT },
+    { "forceencrypt=",      MF_FORCECRYPT },
+    { "fileencryption=",    MF_FILEENCRYPTION },
+    { "forcefdeorfbe=",     MF_FORCEFDEORFBE },
+    { "nonremovable",       MF_NONREMOVABLE },
+    { "voldmanaged=",       MF_VOLDMANAGED},
+    { "length=",            MF_LENGTH },
+    { "recoveryonly",       MF_RECOVERYONLY },
+    { "swapprio=",          MF_SWAPPRIO },
+    { "zramsize=",          MF_ZRAMSIZE },
+    { "max_comp_streams=",  MF_MAX_COMP_STREAMS },
+    { "verifyatboot",       MF_VERIFYATBOOT },
+    { "verify",             MF_VERIFY },
+    { "noemulatedsd",       MF_NOEMULATEDSD },
+    { "notrim",             MF_NOTRIM },
+    { "formattable",        MF_FORMATTABLE },
+    { "slotselect",         MF_SLOTSELECT },
+    { "nofail",             MF_NOFAIL },
+    { "latemount",          MF_LATEMOUNT },
+    { "reservedsize=",      MF_RESERVEDSIZE },
+    { "quota",              MF_QUOTA },
+    { "defaults",           0 },
+    { 0,                    0 },
 };
 
 #define EM_SOFTWARE 1
@@ -587,3 +588,8 @@
 {
     return fstab->fs_mgr_flags & MF_LATEMOUNT;
 }
+
+int fs_mgr_is_quota(struct fstab_rec *fstab)
+{
+    return fstab->fs_mgr_flags & MF_QUOTA;
+}
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 4632521..db86afa 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -65,29 +65,30 @@
  *
  */
 
-#define MF_WAIT         0x1
-#define MF_CHECK        0x2
-#define MF_CRYPT        0x4
-#define MF_NONREMOVABLE 0x8
-#define MF_VOLDMANAGED  0x10
-#define MF_LENGTH       0x20
-#define MF_RECOVERYONLY 0x40
-#define MF_SWAPPRIO     0x80
-#define MF_ZRAMSIZE     0x100
-#define MF_VERIFY       0x200
-#define MF_FORCECRYPT   0x400
-#define MF_NOEMULATEDSD 0x800 /* no emulated sdcard daemon, sd card is the only
-                                 external storage */
-#define MF_NOTRIM       0x1000
-#define MF_FILEENCRYPTION 0x2000
-#define MF_FORMATTABLE  0x4000
-#define MF_SLOTSELECT   0x8000
-#define MF_FORCEFDEORFBE 0x10000
-#define MF_LATEMOUNT    0x20000
-#define MF_NOFAIL       0x40000
-#define MF_VERIFYATBOOT 0x80000
+#define MF_WAIT                  0x1
+#define MF_CHECK                 0x2
+#define MF_CRYPT                 0x4
+#define MF_NONREMOVABLE          0x8
+#define MF_VOLDMANAGED          0x10
+#define MF_LENGTH               0x20
+#define MF_RECOVERYONLY         0x40
+#define MF_SWAPPRIO             0x80
+#define MF_ZRAMSIZE            0x100
+#define MF_VERIFY              0x200
+#define MF_FORCECRYPT          0x400
+#define MF_NOEMULATEDSD        0x800 /* no emulated sdcard daemon, sd card is the only
+                                        external storage */
+#define MF_NOTRIM             0x1000
+#define MF_FILEENCRYPTION     0x2000
+#define MF_FORMATTABLE        0x4000
+#define MF_SLOTSELECT         0x8000
+#define MF_FORCEFDEORFBE     0x10000
+#define MF_LATEMOUNT         0x20000
+#define MF_NOFAIL            0x40000
+#define MF_VERIFYATBOOT      0x80000
 #define MF_MAX_COMP_STREAMS 0x100000
-#define MF_RESERVEDSIZE 0x200000
+#define MF_RESERVEDSIZE     0x200000
+#define MF_QUOTA            0x400000
 
 #define DM_BUF_SIZE 4096
 
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 8ecc93c..ef7fdd3 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -123,6 +123,7 @@
 int fs_mgr_is_formattable(struct fstab_rec *fstab);
 int fs_mgr_is_nofail(struct fstab_rec *fstab);
 int fs_mgr_is_latemount(struct fstab_rec *fstab);
+int fs_mgr_is_quota(struct fstab_rec *fstab);
 int fs_mgr_swapon_all(struct fstab *fstab);
 
 int fs_mgr_do_format(struct fstab_rec *fstab, bool reserve_footer);
diff --git a/gatekeeperd/SoftGateKeeper.h b/gatekeeperd/SoftGateKeeper.h
index 8b15d72..cb02a6f 100644
--- a/gatekeeperd/SoftGateKeeper.h
+++ b/gatekeeperd/SoftGateKeeper.h
@@ -152,7 +152,7 @@
     }
 
     bool DoVerify(const password_handle_t *expected_handle, const SizedBuffer &password) {
-        uint64_t user_id = android::base::get_unaligned(&expected_handle->user_id);
+        uint64_t user_id = android::base::get_unaligned<secure_id_t>(&expected_handle->user_id);
         FastHashMap::const_iterator it = fast_hash_map_.find(user_id);
         if (it != fast_hash_map_.end() && VerifyFast(it->second, password)) {
             return true;
diff --git a/gatekeeperd/gatekeeperd.cpp b/gatekeeperd/gatekeeperd.cpp
index d2c119d..bea7ee7 100644
--- a/gatekeeperd/gatekeeperd.cpp
+++ b/gatekeeperd/gatekeeperd.cpp
@@ -184,7 +184,7 @@
                     ret = rsp.timeout;
                 }
             });
-            if (!hwRes.getStatus().isOk()) {
+            if (!hwRes.isOk()) {
                 ALOGE("enroll transaction failed\n");
                 ret = -1;
             }
@@ -196,7 +196,14 @@
                     enrolled_password_handle, enrolled_password_handle_length);
         }
 
-        if (ret == 0) {
+        if (ret == GATEKEEPER_RESPONSE_OK && (*enrolled_password_handle == nullptr ||
+            *enrolled_password_handle_length != sizeof(password_handle_t))) {
+            ret = GATEKEEPER_RESPONSE_ERROR;
+            ALOGE("HAL: password_handle=%p size_of_handle=%" PRIu32 "\n",
+                  *enrolled_password_handle, *enrolled_password_handle_length);
+        }
+
+        if (ret == GATEKEEPER_RESPONSE_OK) {
             gatekeeper::password_handle_t *handle =
                     reinterpret_cast<gatekeeper::password_handle_t *>(*enrolled_password_handle);
             store_sid(uid, handle->user_id);
@@ -267,7 +274,7 @@
                         ret = rsp.timeout;
                     }
                 });
-                if (!hwRes.getStatus().isOk()) {
+                if (!hwRes.isOk()) {
                     ALOGE("verify transaction failed\n");
                     ret = -1;
                 }
diff --git a/include/cutils/multiuser.h b/include/cutils/multiuser.h
index 7e7f815..4f23776 100644
--- a/include/cutils/multiuser.h
+++ b/include/cutils/multiuser.h
@@ -23,19 +23,19 @@
 extern "C" {
 #endif
 
-// NOTE: keep in sync with android.os.UserId
-
-#define MULTIUSER_APP_PER_USER_RANGE 100000
-#define MULTIUSER_FIRST_SHARED_APPLICATION_GID 50000
-#define MULTIUSER_FIRST_APPLICATION_UID 10000
-
 typedef uid_t userid_t;
 typedef uid_t appid_t;
 
 extern userid_t multiuser_get_user_id(uid_t uid);
 extern appid_t multiuser_get_app_id(uid_t uid);
-extern uid_t multiuser_get_uid(userid_t userId, appid_t appId);
-extern appid_t multiuser_get_shared_app_gid(uid_t uid);
+
+extern uid_t multiuser_get_uid(userid_t user_id, appid_t app_id);
+
+extern gid_t multiuser_get_cache_gid(userid_t user_id, appid_t app_id);
+extern gid_t multiuser_get_shared_gid(userid_t user_id, appid_t app_id);
+
+/* TODO: switch callers over to multiuser_get_shared_gid() */
+extern gid_t multiuser_get_shared_app_gid(uid_t uid);
 
 #ifdef __cplusplus
 }
diff --git a/include/cutils/trace.h b/include/cutils/trace.h
index 0f00417..fcbdc9b 100644
--- a/include/cutils/trace.h
+++ b/include/cutils/trace.h
@@ -71,7 +71,8 @@
 #define ATRACE_TAG_SYSTEM_SERVER    (1<<19)
 #define ATRACE_TAG_DATABASE         (1<<20)
 #define ATRACE_TAG_NETWORK          (1<<21)
-#define ATRACE_TAG_LAST             ATRACE_TAG_NETWORK
+#define ATRACE_TAG_ADB              (1<<22)
+#define ATRACE_TAG_LAST             ATRACE_TAG_ADB
 
 // Reserved for initialization.
 #define ATRACE_TAG_NOT_READY        (1ULL<<63)
diff --git a/include/log/log.h b/include/log/log.h
index d6f0eb5..ece9ea6 100644
--- a/include/log/log.h
+++ b/include/log/log.h
@@ -24,7 +24,7 @@
 #include <stdint.h>  /* uint16_t, int32_t */
 #include <stdio.h>
 #include <sys/types.h>
-#include <time.h>    /* clock_gettime */
+#include <time.h>
 #include <unistd.h>
 
 #include <android/log.h>
@@ -812,6 +812,54 @@
 void __android_log_close();
 #endif
 
+#ifndef __ANDROID_USE_LIBLOG_RATELIMIT_INTERFACE
+#ifndef __ANDROID_API__
+#define __ANDROID_USE_LIBLOG_RATELIMIT_INTERFACE 1
+#elif __ANDROID_API__ > 25 /* > OC */
+#define __ANDROID_USE_LIBLOG_RATELIMIT_INTERFACE 1
+#else
+#define __ANDROID_USE_LIBLOG_RATELIMIT_INTERFACE 0
+#endif
+#endif
+
+#if __ANDROID_USE_LIBLOG_RATELIMIT_INTERFACE
+
+/*
+ * if last is NULL, caller _must_ provide a consistent value for seconds.
+ *
+ * Return -1 if we can not acquire a lock, which below will permit the logging,
+ * error on allowing a log message through.
+ */
+int __android_log_ratelimit(time_t seconds, time_t* last);
+
+/*
+ * Usage:
+ *
+ *   // Global default and state
+ *   IF_ALOG_RATELIMIT() {
+ *      ALOG*(...);
+ *   }
+ *
+ *   // local state, 10 seconds ratelimit
+ *   static time_t local_state;
+ *   IF_ALOG_RATELIMIT_LOCAL(10, &local_state) {
+ *     ALOG*(...);
+ *   }
+ */
+
+#define IF_ALOG_RATELIMIT() \
+      if (__android_log_ratelimit(0, NULL) > 0)
+#define IF_ALOG_RATELIMIT_LOCAL(seconds, state) \
+      if (__android_log_ratelimit(seconds, state) > 0)
+
+#else
+
+/* No ratelimiting as API unsupported */
+#define IF_ALOG_RATELIMIT() if (1)
+#define IF_ALOG_RATELIMIT_LOCAL(...) if (1)
+
+#endif
+
 #if defined(__clang__)
 #pragma clang diagnostic pop
 #endif
diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h
index c9e1923..f7cf9b8 100644
--- a/include/private/android_filesystem_config.h
+++ b/include/private/android_filesystem_config.h
@@ -19,6 +19,33 @@
 ** by the device side of adb.
 */
 
+/*
+ * This file is consumed by build/tools/fs_config and is used
+ * for generating various files. Anything #define AID_<name>
+ * becomes the mapping for getpwnam/getpwuid, etc. The <name>
+ * field is lowercased.
+ * For example:
+ * #define AID_FOO_BAR 6666 becomes a friendly name of "foo_bar"
+ *
+ * The above holds true with the exception of:
+ *   mediacodec
+ *   mediaex
+ *   mediadrm
+ * Whose friendly names do not match the #define statements.
+ *
+ * Additionally, AID_OEM_RESERVED_START and AID_OEM_RESERVED_END
+ * can be used to define reserved OEM ranges used for sanity checks
+ * during the build process. The rules are, they must end with START/END
+ * The proper convention is incrementing a number like so:
+ * AID_OEM_RESERVED_START
+ * AID_OEM_RESERVED_1_START
+ * AID_OEM_RESERVED_2_START
+ * ...
+ * The same applies to the END.
+ * They are not required to be in order, but must not overlap each other and
+ * must define a START and END'ing range. START must be smaller than END.
+ */
+
 #ifndef _ANDROID_FILESYSTEM_CONFIG_H_
 #define _ANDROID_FILESYSTEM_CONFIG_H_
 
@@ -130,15 +157,21 @@
 #define AID_MISC          9998  /* access to misc storage */
 #define AID_NOBODY        9999
 
-#define AID_APP          10000  /* first app user */
+#define AID_APP              10000 /* TODO: switch users over to AID_APP_START */
+#define AID_APP_START        10000 /* first app user */
+#define AID_APP_END          19999 /* last app user */
 
-#define AID_ISOLATED_START 99000 /* start of uids for fully isolated sandboxed processes */
-#define AID_ISOLATED_END   99999 /* end of uids for fully isolated sandboxed processes */
-
-#define AID_USER        100000  /* offset for uid ranges for each user */
+#define AID_CACHE_GID_START  20000 /* start of gids for apps to mark cached data */
+#define AID_CACHE_GID_END    29999 /* end of gids for apps to mark cached data */
 
 #define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */
-#define AID_SHARED_GID_END   59999 /* start of gids for apps in each user to share */
+#define AID_SHARED_GID_END   59999 /* end of gids for apps in each user to share */
+
+#define AID_ISOLATED_START   99000 /* start of uids for fully isolated sandboxed processes */
+#define AID_ISOLATED_END     99999 /* end of uids for fully isolated sandboxed processes */
+
+#define AID_USER            100000 /* TODO: switch users over to AID_USER_OFFSET */
+#define AID_USER_OFFSET     100000 /* offset for uid ranges for each user */
 
 #if !defined(EXCLUDE_FS_CONFIG_STRUCTURES)
 /*
diff --git a/include/system/graphics-base.h b/include/system/graphics-base.h
index b86d031..346a318 100644
--- a/include/system/graphics-base.h
+++ b/include/system/graphics-base.h
@@ -1,7 +1,8 @@
 // This file is autogenerated by hidl-gen. Do not edit manually.
+// Source: android.hardware.graphics.common@1.0
 
-#ifndef HIDL_GENERATED_android_hardware_graphics_common_V1_0_EXPORTED_CONSTANTS_H_
-#define HIDL_GENERATED_android_hardware_graphics_common_V1_0_EXPORTED_CONSTANTS_H_
+#ifndef HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_0_EXPORTED_CONSTANTS_H_
+#define HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_0_EXPORTED_CONSTANTS_H_
 
 #ifdef __cplusplus
 extern "C" {
@@ -13,8 +14,7 @@
     HAL_PIXEL_FORMAT_RGB_888 = 3,
     HAL_PIXEL_FORMAT_RGB_565 = 4,
     HAL_PIXEL_FORMAT_BGRA_8888 = 5,
-    HAL_PIXEL_FORMAT_RGBA_FP16 = 16, // 0x10
-    HAL_PIXEL_FORMAT_RGBX_FP16 = 17, // 0x11
+    HAL_PIXEL_FORMAT_RGBA_FP16 = 22, // 0x16
     HAL_PIXEL_FORMAT_YV12 = 842094169, // 0x32315659
     HAL_PIXEL_FORMAT_Y8 = 538982489, // 0x20203859
     HAL_PIXEL_FORMAT_Y16 = 540422489, // 0x20363159
@@ -32,6 +32,7 @@
     HAL_PIXEL_FORMAT_YCBCR_422_SP = 16, // 0x10
     HAL_PIXEL_FORMAT_YCRCB_420_SP = 17, // 0x11
     HAL_PIXEL_FORMAT_YCBCR_422_I = 20, // 0x14
+    HAL_PIXEL_FORMAT_JPEG = 256, // 0x100
 } android_pixel_format_t;
 
 typedef enum {
@@ -130,4 +131,4 @@
 }
 #endif
 
-#endif  // HIDL_GENERATED_android_hardware_graphics_common_V1_0_EXPORTED_CONSTANTS_H_
+#endif  // HIDL_GENERATED_ANDROID_HARDWARE_GRAPHICS_COMMON_V1_0_EXPORTED_CONSTANTS_H_
diff --git a/include/utils/FastStrcmp.h b/include/utils/FastStrcmp.h
new file mode 100644
index 0000000..3844e7d
--- /dev/null
+++ b/include/utils/FastStrcmp.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014-2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_UTILS_FASTSTRCMP_H__
+#define _ANDROID_UTILS_FASTSTRCMP_H__
+
+#ifdef __cplusplus
+
+// Optimized for instruction cache locality
+//
+// Template class fastcmp used to create more time-efficient str*cmp
+// functions by pre-checking the first character before resorting
+// to calling the underlying string function.  Profiled with a
+// measurable speedup when used in hot code.  Usage is of the form:
+//
+//  fastcmp<strncmp>(str1, str2, len)
+//
+// NB: Does not work for the case insensitive str*cmp functions.
+// NB: Returns boolean, do not use if expecting to check negative value.
+//     Thus not semantically identical to the expected function behavior.
+
+template <int (*cmp)(const char *l, const char *r, const size_t s)>
+static inline int fastcmp(const char *l, const char *r, const size_t s) {
+    return (*l != *r) || cmp(l + 1, r + 1, s - 1);
+}
+
+template <int (*cmp)(const void *l, const void *r, const size_t s)>
+static inline int fastcmp(const void *lv, const void *rv, const size_t s) {
+    const char *l = static_cast<const char *>(lv);
+    const char *r = static_cast<const char *>(rv);
+    return (*l != *r) || cmp(l + 1, r + 1, s - 1);
+}
+
+template <int (*cmp)(const char *l, const char *r)>
+static inline int fastcmp(const char *l, const char *r) {
+    return (*l != *r) || cmp(l + 1, r + 1);
+}
+
+#endif
+
+#endif // _ANDROID_UTILS_FASTSTRCMP_H__
diff --git a/include/utils/Trace.h b/include/utils/Trace.h
index 6ba68f6..eeba40d 100644
--- a/include/utils/Trace.h
+++ b/include/utils/Trace.h
@@ -33,10 +33,10 @@
 
 // See <cutils/trace.h> for more ATRACE_* macros.
 
-// ATRACE_NAME traces the beginning and end of the current scope.  To trace
-// the correct start and end times this macro should be declared first in the
-// scope body.
-#define ATRACE_NAME(name) android::ScopedTrace ___tracer(ATRACE_TAG, name)
+// ATRACE_NAME traces from its location until the end of its enclosing scope.
+#define _PASTE(x, y) x ## y
+#define PASTE(x, y) _PASTE(x,y)
+#define ATRACE_NAME(name) android::ScopedTrace PASTE(___tracer, __LINE__) (ATRACE_TAG, name)
 // ATRACE_CALL is an ATRACE_NAME that uses the current function name.
 #define ATRACE_CALL() ATRACE_NAME(__FUNCTION__)
 
diff --git a/include/ziparchive/zip_archive.h b/include/ziparchive/zip_archive.h
index 54946fc..31fc2df 100644
--- a/include/ziparchive/zip_archive.h
+++ b/include/ziparchive/zip_archive.h
@@ -26,8 +26,6 @@
 #include <sys/types.h>
 #include <utils/Compat.h>
 
-__BEGIN_DECLS
-
 /* Zip compression methods we support */
 enum {
   kCompressStored     = 0,        // no compression
@@ -228,6 +226,4 @@
         ProcessZipEntryFunction func, void* cookie);
 #endif
 
-__END_DECLS
-
 #endif  // LIBZIPARCHIVE_ZIPARCHIVE_H_
diff --git a/init/Android.mk b/init/Android.mk
index ecdf5db..111fe89 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -54,7 +54,7 @@
     service.cpp \
     util.cpp \
 
-LOCAL_STATIC_LIBRARIES := libbase libselinux liblog libprocessgroup
+LOCAL_STATIC_LIBRARIES := libbase libselinux liblog libprocessgroup libnl
 LOCAL_WHOLE_STATIC_LIBRARIES := libcap
 LOCAL_MODULE := libinit
 LOCAL_SANITIZE := integer
@@ -103,7 +103,8 @@
     libdl \
     libsparse_static \
     libz \
-    libprocessgroup
+    libprocessgroup \
+    libnl \
 
 # Create symlinks.
 LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT)/sbin; \
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index 8fb55f0..4a9c32e 100644
--- a/init/bootchart.cpp
+++ b/init/bootchart.cpp
@@ -15,7 +15,7 @@
  */
 
 #include "bootchart.h"
-#include "log.h"
+
 #include "property_service.h"
 
 #include <dirent.h>
@@ -29,247 +29,170 @@
 #include <time.h>
 #include <unistd.h>
 
+#include <chrono>
+#include <condition_variable>
 #include <memory>
+#include <mutex>
 #include <string>
+#include <thread>
 #include <vector>
 
 #include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
 
-#define LOG_ROOT        "/data/bootchart"
-#define LOG_STAT        LOG_ROOT"/proc_stat.log"
-#define LOG_PROCS       LOG_ROOT"/proc_ps.log"
-#define LOG_DISK        LOG_ROOT"/proc_diskstats.log"
-#define LOG_HEADER      LOG_ROOT"/header"
-#define LOG_ACCT        LOG_ROOT"/kernel_pacct"
+using android::base::StringPrintf;
+using namespace std::chrono_literals;
 
-#define LOG_STARTFILE   LOG_ROOT"/start"
-#define LOG_STOPFILE    LOG_ROOT"/stop"
+static std::thread* g_bootcharting_thread;
 
-// Polling period in ms.
-static const int BOOTCHART_POLLING_MS = 200;
-
-// Max polling time in seconds.
-static const int BOOTCHART_MAX_TIME_SEC = 10*60;
-
-static long long g_last_bootchart_time;
-static int g_remaining_samples;
-
-static FILE* log_stat;
-static FILE* log_procs;
-static FILE* log_disks;
+static std::mutex g_bootcharting_finished_mutex;
+static std::condition_variable g_bootcharting_finished_cv;
+static bool g_bootcharting_finished;
 
 static long long get_uptime_jiffies() {
-    std::string uptime;
-    if (!android::base::ReadFileToString("/proc/uptime", &uptime)) {
-        return 0;
-    }
-    return 100LL * strtod(uptime.c_str(), NULL);
+  std::string uptime;
+  if (!android::base::ReadFileToString("/proc/uptime", &uptime)) return 0;
+  return 100LL * strtod(uptime.c_str(), NULL);
+}
+
+static std::unique_ptr<FILE, decltype(&fclose)> fopen_unique(const char* filename,
+                                                             const char* mode) {
+  std::unique_ptr<FILE, decltype(&fclose)> result(fopen(filename, mode), fclose);
+  if (!result) PLOG(ERROR) << "bootchart: failed to open " << filename;
+  return result;
 }
 
 static void log_header() {
-    char date[32];
-    time_t now_t = time(NULL);
-    struct tm now = *localtime(&now_t);
-    strftime(date, sizeof(date), "%F %T", &now);
+  char date[32];
+  time_t now_t = time(NULL);
+  struct tm now = *localtime(&now_t);
+  strftime(date, sizeof(date), "%F %T", &now);
 
-    utsname uts;
-    if (uname(&uts) == -1) {
-        return;
-    }
+  utsname uts;
+  if (uname(&uts) == -1) return;
 
-    std::string fingerprint = property_get("ro.build.fingerprint");
-    if (fingerprint.empty()) {
-        return;
-    }
+  std::string fingerprint = property_get("ro.build.fingerprint");
+  if (fingerprint.empty()) return;
 
-    std::string kernel_cmdline;
-    android::base::ReadFileToString("/proc/cmdline", &kernel_cmdline);
+  std::string kernel_cmdline;
+  android::base::ReadFileToString("/proc/cmdline", &kernel_cmdline);
 
-    FILE* out = fopen(LOG_HEADER, "we");
-    if (out == NULL) {
-        return;
-    }
-    fprintf(out, "version = Android init 0.8\n");
-    fprintf(out, "title = Boot chart for Android (%s)\n", date);
-    fprintf(out, "system.uname = %s %s %s %s\n", uts.sysname, uts.release, uts.version, uts.machine);
-    fprintf(out, "system.release = %s\n", fingerprint.c_str());
-    // TODO: use /proc/cpuinfo "model name" line for x86, "Processor" line for arm.
-    fprintf(out, "system.cpu = %s\n", uts.machine);
-    fprintf(out, "system.kernel.options = %s\n", kernel_cmdline.c_str());
-    fclose(out);
+  auto fp = fopen_unique("/data/bootchart/header", "we");
+  if (!fp) return;
+  fprintf(&*fp, "version = Android init 0.8\n");
+  fprintf(&*fp, "title = Boot chart for Android (%s)\n", date);
+  fprintf(&*fp, "system.uname = %s %s %s %s\n", uts.sysname, uts.release, uts.version, uts.machine);
+  fprintf(&*fp, "system.release = %s\n", fingerprint.c_str());
+  // TODO: use /proc/cpuinfo "model name" line for x86, "Processor" line for arm.
+  fprintf(&*fp, "system.cpu = %s\n", uts.machine);
+  fprintf(&*fp, "system.kernel.options = %s\n", kernel_cmdline.c_str());
 }
 
-static void do_log_uptime(FILE* log) {
-    fprintf(log, "%lld\n", get_uptime_jiffies());
+static void log_uptime(FILE* log) {
+  fprintf(log, "%lld\n", get_uptime_jiffies());
 }
 
-static void do_log_file(FILE* log, const char* procfile) {
-    do_log_uptime(log);
+static void log_file(FILE* log, const char* procfile) {
+  log_uptime(log);
 
-    std::string content;
-    if (android::base::ReadFileToString(procfile, &content)) {
-        fprintf(log, "%s\n", content.c_str());
-    }
+  std::string content;
+  if (android::base::ReadFileToString(procfile, &content)) {
+    fprintf(log, "%s\n", content.c_str());
+  }
 }
 
-static void do_log_procs(FILE* log) {
-    do_log_uptime(log);
+static void log_processes(FILE* log) {
+  log_uptime(log);
 
-    std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir("/proc"), closedir);
-    struct dirent* entry;
-    while ((entry = readdir(dir.get())) != NULL) {
-        // Only match numeric values.
-        char* end;
-        int pid = strtol(entry->d_name, &end, 10);
-        if (end != NULL && end > entry->d_name && *end == 0) {
-            char filename[32];
+  std::unique_ptr<DIR, int(*)(DIR*)> dir(opendir("/proc"), closedir);
+  struct dirent* entry;
+  while ((entry = readdir(dir.get())) != NULL) {
+    // Only match numeric values.
+    int pid = atoi(entry->d_name);
+    if (pid == 0) continue;
 
-            // /proc/<pid>/stat only has truncated task names, so get the full
-            // name from /proc/<pid>/cmdline.
-            snprintf(filename, sizeof(filename), "/proc/%d/cmdline", pid);
-            std::string cmdline;
-            android::base::ReadFileToString(filename, &cmdline);
-            const char* full_name = cmdline.c_str(); // So we stop at the first NUL.
+    // /proc/<pid>/stat only has truncated task names, so get the full
+    // name from /proc/<pid>/cmdline.
+    std::string cmdline;
+    android::base::ReadFileToString(StringPrintf("/proc/%d/cmdline", pid), &cmdline);
+    const char* full_name = cmdline.c_str(); // So we stop at the first NUL.
 
-            // Read process stat line.
-            snprintf(filename, sizeof(filename), "/proc/%d/stat", pid);
-            std::string stat;
-            if (android::base::ReadFileToString(filename, &stat)) {
-                if (!cmdline.empty()) {
-                    // Substitute the process name with its real name.
-                    size_t open = stat.find('(');
-                    size_t close = stat.find_last_of(')');
-                    if (open != std::string::npos && close != std::string::npos) {
-                        stat.replace(open + 1, close - open - 1, full_name);
-                    }
-                }
-                fputs(stat.c_str(), log);
-            }
+    // Read process stat line.
+    std::string stat;
+    if (android::base::ReadFileToString(StringPrintf("/proc/%d/stat", pid), &stat)) {
+      if (!cmdline.empty()) {
+        // Substitute the process name with its real name.
+        size_t open = stat.find('(');
+        size_t close = stat.find_last_of(')');
+        if (open != std::string::npos && close != std::string::npos) {
+          stat.replace(open + 1, close - open - 1, full_name);
         }
+      }
+      fputs(stat.c_str(), log);
     }
+  }
 
-    fputc('\n', log);
+  fputc('\n', log);
 }
 
-static int bootchart_init() {
-    int timeout = 0;
+static void bootchart_thread_main() {
+  LOG(INFO) << "Bootcharting started";
 
-    std::string start;
-    android::base::ReadFileToString(LOG_STARTFILE, &start);
-    if (!start.empty()) {
-        timeout = atoi(start.c_str());
-    } else {
-        // When running with emulator, androidboot.bootchart=<timeout>
-        // might be passed by as kernel parameters to specify the bootchart
-        // timeout. this is useful when using -wipe-data since the /data
-        // partition is fresh.
-        std::string cmdline;
-        const char* s;
-        android::base::ReadFileToString("/proc/cmdline", &cmdline);
-#define KERNEL_OPTION  "androidboot.bootchart="
-        if ((s = strstr(cmdline.c_str(), KERNEL_OPTION)) != NULL) {
-            timeout = atoi(s + sizeof(KERNEL_OPTION) - 1);
-        }
-    }
-    if (timeout == 0)
-        return 0;
+  // Open log files.
+  auto stat_log = fopen_unique("/data/bootchart/proc_stat.log", "we");
+  if (!stat_log) return;
+  auto proc_log = fopen_unique("/data/bootchart/proc_ps.log", "we");
+  if (!proc_log) return;
+  auto disk_log = fopen_unique("/data/bootchart/proc_diskstats.log", "we");
+  if (!disk_log) return;
 
-    if (timeout > BOOTCHART_MAX_TIME_SEC)
-        timeout = BOOTCHART_MAX_TIME_SEC;
+  log_header();
 
-    int count = (timeout*1000 + BOOTCHART_POLLING_MS-1)/BOOTCHART_POLLING_MS;
-
-    log_stat = fopen(LOG_STAT, "we");
-    if (log_stat == NULL) {
-        return -1;
-    }
-    log_procs = fopen(LOG_PROCS, "we");
-    if (log_procs == NULL) {
-        fclose(log_stat);
-        return -1;
-    }
-    log_disks = fopen(LOG_DISK, "we");
-    if (log_disks == NULL) {
-        fclose(log_stat);
-        fclose(log_procs);
-        return -1;
+  while (true) {
+    {
+      std::unique_lock<std::mutex> lock(g_bootcharting_finished_mutex);
+      g_bootcharting_finished_cv.wait_for(lock, 200ms);
+      if (g_bootcharting_finished) break;
     }
 
-    // Create kernel process accounting file.
-    close(open(LOG_ACCT, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0644));
-    acct(LOG_ACCT);
+    log_file(&*stat_log, "/proc/stat");
+    log_file(&*disk_log, "/proc/diskstats");
+    log_processes(&*proc_log);
+  }
 
-    log_header();
-    return count;
+  LOG(INFO) << "Bootcharting finished";
 }
 
-int do_bootchart_init(const std::vector<std::string>& args) {
-    g_remaining_samples = bootchart_init();
-    if (g_remaining_samples < 0) {
-        PLOG(ERROR) << "Bootcharting initialization failed";
-    } else if (g_remaining_samples > 0) {
-        LOG(INFO) << "Bootcharting started (will run for "
-                  << ((g_remaining_samples * BOOTCHART_POLLING_MS) / 1000) << " s).";
-    } else {
-        LOG(VERBOSE) << "Not bootcharting.";
-    }
+static int do_bootchart_start() {
+  // We don't care about the content, but we do care that /data/bootchart/enabled actually exists.
+  std::string start;
+  if (!android::base::ReadFileToString("/data/bootchart/enabled", &start)) {
+    LOG(VERBOSE) << "Not bootcharting";
     return 0;
+  }
+
+  g_bootcharting_thread = new std::thread(bootchart_thread_main);
+  return 0;
 }
 
-static int bootchart_step() {
-    do_log_file(log_stat,   "/proc/stat");
-    do_log_file(log_disks,  "/proc/diskstats");
-    do_log_procs(log_procs);
+static int do_bootchart_stop() {
+  if (!g_bootcharting_thread) return 0;
 
-    // Stop if /data/bootchart/stop contains 1.
-    std::string stop;
-    if (android::base::ReadFileToString(LOG_STOPFILE, &stop) && stop == "1") {
-        return -1;
-    }
+  // Tell the worker thread it's time to quit.
+  {
+    std::lock_guard<std::mutex> lock(g_bootcharting_finished_mutex);
+    g_bootcharting_finished = true;
+    g_bootcharting_finished_cv.notify_one();
+  }
 
-    return 0;
+  g_bootcharting_thread->join();
+  delete g_bootcharting_thread;
+  g_bootcharting_thread = nullptr;
+  return 0;
 }
 
-/* called to get time (in ms) used by bootchart */
-static long long bootchart_gettime() {
-    return 10LL*get_uptime_jiffies();
-}
-
-static void bootchart_finish() {
-    unlink(LOG_STOPFILE);
-    fclose(log_stat);
-    fclose(log_disks);
-    fclose(log_procs);
-    acct(NULL);
-    LOG(INFO) << "Bootcharting finished";
-}
-
-void bootchart_sample(int* timeout) {
-    // Do we have any more bootcharting to do?
-    if (g_remaining_samples <= 0) {
-        return;
-    }
-
-    long long current_time = bootchart_gettime();
-    int elapsed_time = current_time - g_last_bootchart_time;
-
-    if (elapsed_time >= BOOTCHART_POLLING_MS) {
-        // Count missed samples.
-        while (elapsed_time >= BOOTCHART_POLLING_MS) {
-            elapsed_time -= BOOTCHART_POLLING_MS;
-            g_remaining_samples--;
-        }
-        // Count may be negative, take a sample anyway.
-        g_last_bootchart_time = current_time;
-        if (bootchart_step() < 0 || g_remaining_samples <= 0) {
-            bootchart_finish();
-            g_remaining_samples = 0;
-        }
-    }
-    if (g_remaining_samples > 0) {
-        int remaining_time = BOOTCHART_POLLING_MS - elapsed_time;
-        if (*timeout < 0 || *timeout > remaining_time) {
-            *timeout = remaining_time;
-        }
-    }
+int do_bootchart(const std::vector<std::string>& args) {
+  if (args[1] == "start") return do_bootchart_start();
+  return do_bootchart_stop();
 }
diff --git a/init/bootchart.h b/init/bootchart.h
index 47eda7a..0e3593d 100644
--- a/init/bootchart.h
+++ b/init/bootchart.h
@@ -20,7 +20,6 @@
 #include <string>
 #include <vector>
 
-int do_bootchart_init(const std::vector<std::string>& args);
-void bootchart_sample(int* timeout);
+int do_bootchart(const std::vector<std::string>& args);
 
 #endif /* _BOOTCHART_H */
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 42dd0c6..1186e9d 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -38,6 +38,7 @@
 #include <linux/loop.h>
 #include <linux/module.h>
 
+#include <string>
 #include <thread>
 
 #include <selinux/android.h>
@@ -67,6 +68,8 @@
 #include "signal_handler.h"
 #include "util.h"
 
+using namespace std::literals::string_literals;
+
 #define chmod DO_NOT_USE_CHMOD_USE_FCHMODAT_SYMLINK_NOFOLLOW
 #define UNMOUNT_CHECK_TIMES 10
 
@@ -139,8 +142,7 @@
     }
 }
 
-static int wipe_data_via_recovery(const std::string& reason) {
-    const std::vector<std::string> options = {"--wipe_data", std::string() + "--reason=" + reason};
+static int reboot_into_recovery(const std::vector<std::string>& options) {
     std::string err;
     if (!write_bootloader_message(options, &err)) {
         LOG(ERROR) << "failed to set bootloader message: " << err;
@@ -247,7 +249,7 @@
 }
 
 static int do_domainname(const std::vector<std::string>& args) {
-    return write_file("/proc/sys/kernel/domainname", args[1].c_str());
+    return write_file("/proc/sys/kernel/domainname", args[1].c_str()) ? 0 : 1;
 }
 
 static int do_enable(const std::vector<std::string>& args) {
@@ -275,7 +277,7 @@
 }
 
 static int do_hostname(const std::vector<std::string>& args) {
-    return write_file("/proc/sys/kernel/hostname", args[1].c_str());
+    return write_file("/proc/sys/kernel/hostname", args[1].c_str()) ? 0 : 1;
 }
 
 static int do_ifup(const std::vector<std::string>& args) {
@@ -338,7 +340,10 @@
 
     if (e4crypt_is_native()) {
         if (e4crypt_set_directory_policy(args[1].c_str())) {
-            wipe_data_via_recovery(std::string() + "set_policy_failed:" + args[1]);
+            const std::vector<std::string> options = {
+                "--prompt_and_wipe_data",
+                "--reason=set_policy_failed:"s + args[1]};
+            reboot_into_recovery(options);
             return -1;
         }
     }
@@ -559,7 +564,8 @@
     } else if (code == FS_MGR_MNTALL_DEV_NEEDS_RECOVERY) {
         /* Setup a wipe via recovery, and reboot into recovery */
         PLOG(ERROR) << "fs_mgr_mount_all suggested recovery, so wiping data via recovery.";
-        ret = wipe_data_via_recovery("wipe_data_via_recovery");
+        const std::vector<std::string> options = {"--wipe_data", "--reason=fs_mgr_mount_all" };
+        ret = reboot_into_recovery(options);
         /* If reboot worked, there is no return. */
     } else if (code == FS_MGR_MNTALL_DEV_FILE_ENCRYPTED) {
         if (e4crypt_install_keyring()) {
@@ -808,7 +814,7 @@
 static int do_write(const std::vector<std::string>& args) {
     const char* path = args[1].c_str();
     const char* value = args[2].c_str();
-    return write_file(path, value);
+    return write_file(path, value) ? 0 : 1;
 }
 
 static int do_copy(const std::vector<std::string>& args) {
@@ -1028,7 +1034,7 @@
 BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
     constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
     static const Map builtin_functions = {
-        {"bootchart_init",          {0,     0,    do_bootchart_init}},
+        {"bootchart",               {1,     1,    do_bootchart}},
         {"chmod",                   {2,     2,    do_chmod}},
         {"chown",                   {2,     3,    do_chown}},
         {"class_reset",             {1,     1,    do_class_reset}},
diff --git a/init/capabilities.cpp b/init/capabilities.cpp
index 4592adc..b8a9ec0 100644
--- a/init/capabilities.cpp
+++ b/init/capabilities.cpp
@@ -25,8 +25,7 @@
 
 #define CAP_MAP_ENTRY(cap) { #cap, CAP_##cap }
 
-namespace {
-const std::map<std::string, int> cap_map = {
+static const std::map<std::string, int> cap_map = {
     CAP_MAP_ENTRY(CHOWN),
     CAP_MAP_ENTRY(DAC_OVERRIDE),
     CAP_MAP_ENTRY(DAC_READ_SEARCH),
@@ -69,9 +68,30 @@
 
 static_assert(CAP_LAST_CAP == CAP_AUDIT_READ, "CAP_LAST_CAP is not CAP_AUDIT_READ");
 
-bool DropBoundingSet(const CapSet& to_keep) {
-    for (size_t cap = 0; cap < to_keep.size(); ++cap) {
-        if (to_keep.test(cap)) {
+static bool ComputeCapAmbientSupported() {
+    return prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, CAP_CHOWN, 0, 0) >= 0;
+}
+
+static unsigned int ComputeLastValidCap() {
+    // Android does not support kernels < 3.8. 'CAP_WAKE_ALARM' has been present since 3.0, see
+    // http://lxr.free-electrons.com/source/include/linux/capability.h?v=3.0#L360.
+    unsigned int last_valid_cap = CAP_WAKE_ALARM;
+    for (; prctl(PR_CAPBSET_READ, last_valid_cap, 0, 0, 0) >= 0; ++last_valid_cap);
+
+    // |last_valid_cap| will be the first failing value.
+    return last_valid_cap - 1;
+}
+
+static bool DropBoundingSet(const CapSet& to_keep) {
+    unsigned int last_valid_cap = GetLastValidCap();
+    // When dropping the bounding set, attempt to drop capabilities reported at
+    // run-time, not at compile-time.
+    // If the run-time kernel is older than the compile-time headers, this
+    // avoids dropping an invalid capability. If the run-time kernel is newer
+    // than the headers, this guarantees all capabilities (even those unknown at
+    // compile time) will be dropped.
+    for (size_t cap = 0; cap <= last_valid_cap; ++cap) {
+        if (cap < to_keep.size() && to_keep.test(cap)) {
             // No need to drop this capability.
             continue;
         }
@@ -83,14 +103,14 @@
     return true;
 }
 
-bool SetProcCaps(const CapSet& to_keep, bool add_setpcap) {
+static bool SetProcCaps(const CapSet& to_keep, bool add_setpcap) {
     cap_t caps = cap_init();
     auto deleter = [](cap_t* p) { cap_free(*p); };
     std::unique_ptr<cap_t, decltype(deleter)> ptr_caps(&caps, deleter);
 
     cap_clear(caps);
     cap_value_t value[1];
-    for (size_t cap = 0; cap <= to_keep.size(); ++cap) {
+    for (size_t cap = 0; cap < to_keep.size(); ++cap) {
         if (to_keep.test(cap)) {
             value[0] = cap;
             if (cap_set_flag(caps, CAP_INHERITABLE, arraysize(value), value, CAP_SET) != 0 ||
@@ -117,7 +137,7 @@
     return true;
 }
 
-bool SetAmbientCaps(const CapSet& to_raise) {
+static bool SetAmbientCaps(const CapSet& to_raise) {
     for (size_t cap = 0; cap < to_raise.size(); ++cap) {
         if (to_raise.test(cap)) {
             if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, cap, 0, 0) != 0) {
@@ -129,8 +149,6 @@
     return true;
 }
 
-}  // namespace anonymous
-
 int LookupCap(const std::string& cap_name) {
     auto e = cap_map.find(cap_name);
     if (e != cap_map.end()) {
@@ -140,6 +158,16 @@
     }
 }
 
+bool CapAmbientSupported() {
+    static bool cap_ambient_supported = ComputeCapAmbientSupported();
+    return cap_ambient_supported;
+}
+
+unsigned int GetLastValidCap() {
+    static unsigned int last_valid_cap = ComputeLastValidCap();
+    return last_valid_cap;
+}
+
 bool SetCapsForExec(const CapSet& to_keep) {
     // Need to keep SETPCAP to drop bounding set below.
     bool add_setpcap = true;
diff --git a/init/capabilities.h b/init/capabilities.h
index 368178d..abd7fb2 100644
--- a/init/capabilities.h
+++ b/init/capabilities.h
@@ -12,6 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#ifndef _INIT_CAPABILITIES_H
+#define _INIT_CAPABILITIES_H
+
 #include <linux/capability.h>
 
 #include <bitset>
@@ -20,4 +23,8 @@
 using CapSet = std::bitset<CAP_LAST_CAP + 1>;
 
 int LookupCap(const std::string& cap_name);
+bool CapAmbientSupported();
+unsigned int GetLastValidCap();
 bool SetCapsForExec(const CapSet& to_keep);
+
+#endif  // _INIT_CAPABILITIES_H
diff --git a/init/grab-bootchart.sh b/init/grab-bootchart.sh
index d6082aa..c4ff6df 100755
--- a/init/grab-bootchart.sh
+++ b/init/grab-bootchart.sh
@@ -11,7 +11,7 @@
 LOGROOT=/data/bootchart
 TARBALL=bootchart.tgz
 
-FILES="header proc_stat.log proc_ps.log proc_diskstats.log kernel_pacct"
+FILES="header proc_stat.log proc_ps.log proc_diskstats.log"
 
 for f in $FILES; do
     adb "${@}" pull $LOGROOT/$f $TMPDIR/$f 2>&1 > /dev/null
diff --git a/init/init.cpp b/init/init.cpp
index 60ee4f7..ee5add8 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -349,13 +349,9 @@
     // TODO: add mips support b/27788820
     ret = 0;
 #else
-    ERROR("Unknown architecture\n");
+    LOG(ERROR) << "Unknown architecture";
 #endif
 
-#ifdef __BRILLO__
-    // TODO: b/27794137
-    ret = 0;
-#endif
     if (ret == -1) {
         LOG(ERROR) << "Unable to set adequate mmap entropy value!";
         security_failure();
@@ -546,7 +542,7 @@
             }
         }
 
-        if (write_file("/sys/fs/selinux/checkreqprot", "0") == -1) {
+        if (!write_file("/sys/fs/selinux/checkreqprot", "0")) {
             security_failure();
         }
 
@@ -854,8 +850,6 @@
         // If there's more work to do, wake up again immediately.
         if (am.HasMoreCommands()) epoll_timeout_ms = 0;
 
-        bootchart_sample(&epoll_timeout_ms);
-
         epoll_event ev;
         int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms));
         if (nr == -1) {
diff --git a/init/log.cpp b/init/log.cpp
index 8618340..6b32526 100644
--- a/init/log.cpp
+++ b/init/log.cpp
@@ -19,6 +19,8 @@
 #include <fcntl.h>
 #include <string.h>
 
+#include <linux/audit.h>
+#include <netlink/netlink.h>
 #include <selinux/selinux.h>
 
 void InitKernelLogging(char* argv[]) {
@@ -38,6 +40,24 @@
     android::base::InitLogging(argv, &android::base::KernelLogger);
 }
 
+static void selinux_avc_log(char* buf, size_t buf_len) {
+    size_t str_len = strnlen(buf, buf_len);
+
+    // trim newline at end of string
+    buf[str_len - 1] = '\0';
+
+    struct nl_sock* sk = nl_socket_alloc();
+    if (sk == NULL) {
+        return;
+    }
+    nl_connect(sk, NETLINK_AUDIT);
+    int result;
+    do {
+        result = nl_send_simple(sk, AUDIT_USER_AVC, 0, buf, str_len);
+    } while (result == -NLE_INTR);
+    nl_socket_free(sk);
+}
+
 int selinux_klog_callback(int type, const char *fmt, ...) {
     android::base::LogSeverity severity = android::base::ERROR;
     if (type == SELINUX_WARNING) {
@@ -48,8 +68,15 @@
     char buf[1024];
     va_list ap;
     va_start(ap, fmt);
-    vsnprintf(buf, sizeof(buf), fmt, ap);
+    int res = vsnprintf(buf, sizeof(buf), fmt, ap);
     va_end(ap);
-    android::base::KernelLogger(android::base::MAIN, severity, "selinux", nullptr, 0, buf);
+    if (res <= 0) {
+        return 0;
+    }
+    if (type == SELINUX_AVC) {
+        selinux_avc_log(buf, sizeof(buf));
+    } else {
+        android::base::KernelLogger(android::base::MAIN, severity, "selinux", nullptr, 0, buf);
+    }
     return 0;
 }
diff --git a/init/readme.txt b/init/readme.txt
index 6f40d6b..530b392 100644
--- a/init/readme.txt
+++ b/init/readme.txt
@@ -253,9 +253,10 @@
 Commands
 --------
 
-bootchart_init
-   Start bootcharting if configured (see below).
-   This is included in the default init.rc.
+bootchart [start|stop]
+   Start/stop bootcharting. These are present in the default init.rc files,
+   but bootcharting is only active if the file /data/bootchart/enabled exists;
+   otherwise bootchart start/stop are no-ops.
 
 chmod <octal-mode> <path>
    Change file access permissions.
@@ -471,19 +472,11 @@
 On the emulator, use the -bootchart <timeout> option to boot with bootcharting
 activated for <timeout> seconds.
 
-On a device, create /data/bootchart/start with a command like the following:
+On a device:
 
-  adb shell 'echo $TIMEOUT > /data/bootchart/start'
+  adb shell 'touch /data/bootchart/enabled'
 
-Where the value of $TIMEOUT corresponds to the desired bootcharted period in
-seconds. Bootcharting will stop after that many seconds have elapsed.
-You can also stop the bootcharting at any moment by doing the following:
-
-  adb shell 'echo 1 > /data/bootchart/stop'
-
-Note that /data/bootchart/stop is deleted automatically by init at the end of
-the bootcharting. This is not the case with /data/bootchart/start, so don't
-forget to delete it when you're done collecting data.
+Don't forget to delete this file when you're done collecting data!
 
 The log files are written to /data/bootchart/. A script is provided to
 retrieve them and create a bootchart.tgz file that can be used with the
diff --git a/init/service.cpp b/init/service.cpp
index a7eaf66..0f7f62f 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -312,13 +312,28 @@
 bool Service::ParseCapabilities(const std::vector<std::string>& args, std::string* err) {
     capabilities_ = 0;
 
+    if (!CapAmbientSupported()) {
+        *err = "capabilities requested but the kernel does not support ambient capabilities";
+        return false;
+    }
+
+    unsigned int last_valid_cap = GetLastValidCap();
+    if (last_valid_cap >= capabilities_.size()) {
+        LOG(WARNING) << "last valid run-time capability is larger than CAP_LAST_CAP";
+    }
+
     for (size_t i = 1; i < args.size(); i++) {
         const std::string& arg = args[i];
-        int cap = LookupCap(arg);
-        if (cap == -1) {
+        int res = LookupCap(arg);
+        if (res < 0) {
             *err = StringPrintf("invalid capability '%s'", arg.c_str());
             return false;
         }
+        unsigned int cap = static_cast<unsigned int>(res);  // |res| is >= 0.
+        if (cap > last_valid_cap) {
+            *err = StringPrintf("capability '%s' not supported by the kernel", arg.c_str());
+            return false;
+        }
         capabilities_[cap] = true;
     }
     return true;
@@ -567,12 +582,15 @@
             console_ = default_console;
         }
 
-        bool have_console = (open(console_.c_str(), O_RDWR | O_CLOEXEC) != -1);
-        if (!have_console) {
+        // Make sure that open call succeeds to ensure a console driver is
+        // properly registered for the device node
+        int console_fd = open(console_.c_str(), O_RDWR | O_CLOEXEC);
+        if (console_fd < 0) {
             PLOG(ERROR) << "service '" << name_ << "' couldn't open console '" << console_ << "'";
             flags_ |= SVC_DISABLED;
             return false;
         }
+        close(console_fd);
     }
 
     struct stat sb;
diff --git a/init/util.cpp b/init/util.cpp
index a79a419..888a366 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -185,18 +185,18 @@
     return okay;
 }
 
-int write_file(const char* path, const char* content) {
+bool write_file(const char* path, const char* content) {
     int fd = TEMP_FAILURE_RETRY(open(path, O_WRONLY|O_CREAT|O_NOFOLLOW|O_CLOEXEC, 0600));
     if (fd == -1) {
         PLOG(ERROR) << "write_file: Unable to open '" << path << "'";
-        return -1;
+        return false;
     }
-    int result = android::base::WriteStringToFd(content, fd) ? 0 : -1;
-    if (result == -1) {
+    bool success = android::base::WriteStringToFd(content, fd);
+    if (!success) {
         PLOG(ERROR) << "write_file: Unable to write to '" << path << "'";
     }
     close(fd);
-    return result;
+    return success;
 }
 
 boot_clock::time_point boot_clock::now() {
diff --git a/init/util.h b/init/util.h
index e63c469..009413d 100644
--- a/init/util.h
+++ b/init/util.h
@@ -33,7 +33,7 @@
                   uid_t uid, gid_t gid, const char *socketcon);
 
 bool read_file(const char* path, std::string* content);
-int write_file(const char* path, const char* content);
+bool write_file(const char* path, const char* content);
 
 // A std::chrono clock based on CLOCK_BOOTTIME.
 class boot_clock {
diff --git a/libappfuse/FuseBuffer.cc b/libappfuse/FuseBuffer.cc
index 3ade31c..8fb2dbc 100644
--- a/libappfuse/FuseBuffer.cc
+++ b/libappfuse/FuseBuffer.cc
@@ -23,6 +23,7 @@
 #include <algorithm>
 #include <type_traits>
 
+#include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/macros.h>
 
@@ -34,44 +35,65 @@
     "FuseBuffer must be standard layout union.");
 
 template <typename T>
-bool FuseMessage<T>::CheckHeaderLength() const {
+bool FuseMessage<T>::CheckHeaderLength(const char* name) const {
   const auto& header = static_cast<const T*>(this)->header;
-  if (sizeof(header) <= header.len && header.len <= sizeof(T)) {
+  if (header.len >= sizeof(header) && header.len <= sizeof(T)) {
     return true;
   } else {
-    LOG(ERROR) << "Packet size is invalid=" << header.len;
-    return false;
-  }
-}
-
-template <typename T>
-bool FuseMessage<T>::CheckResult(
-    int result, const char* operation_name) const {
-  const auto& header = static_cast<const T*>(this)->header;
-  if (result >= 0 && static_cast<uint32_t>(result) == header.len) {
-    return true;
-  } else {
-    PLOG(ERROR) << "Failed to " << operation_name
-        << " a packet. result=" << result << " header.len="
-        << header.len;
+    LOG(ERROR) << "Invalid header length is found in " << name << ": " <<
+        header.len;
     return false;
   }
 }
 
 template <typename T>
 bool FuseMessage<T>::Read(int fd) {
-  const ssize_t result = TEMP_FAILURE_RETRY(::read(fd, this, sizeof(T)));
-  return CheckHeaderLength() && CheckResult(result, "read");
+  char* const buf = reinterpret_cast<char*>(this);
+  const ssize_t result = TEMP_FAILURE_RETRY(::read(fd, buf, sizeof(T)));
+  if (result < 0) {
+    PLOG(ERROR) << "Failed to read a FUSE message";
+    return false;
+  }
+
+  const auto& header = static_cast<const T*>(this)->header;
+  if (result < static_cast<ssize_t>(sizeof(header))) {
+    LOG(ERROR) << "Read bytes " << result << " are shorter than header size " <<
+        sizeof(header);
+    return false;
+  }
+
+  if (!CheckHeaderLength("Read")) {
+    return false;
+  }
+
+  if (static_cast<uint32_t>(result) > header.len) {
+    LOG(ERROR) << "Read bytes " << result << " are longer than header.len " <<
+        header.len;
+    return false;
+  }
+
+  if (!base::ReadFully(fd, buf + result, header.len - result)) {
+    PLOG(ERROR) << "ReadFully failed";
+    return false;
+  }
+
+  return true;
 }
 
 template <typename T>
 bool FuseMessage<T>::Write(int fd) const {
-  const auto& header = static_cast<const T*>(this)->header;
-  if (!CheckHeaderLength()) {
+  if (!CheckHeaderLength("Write")) {
     return false;
   }
-  const ssize_t result = TEMP_FAILURE_RETRY(::write(fd, this, header.len));
-  return CheckResult(result, "write");
+
+  const char* const buf = reinterpret_cast<const char*>(this);
+  const auto& header = static_cast<const T*>(this)->header;
+  if (!base::WriteFully(fd, buf, header.len)) {
+    PLOG(ERROR) << "WriteFully failed";
+    return false;
+  }
+
+  return true;
 }
 
 template class FuseMessage<FuseRequest>;
diff --git a/libappfuse/include/libappfuse/FuseBuffer.h b/libappfuse/include/libappfuse/FuseBuffer.h
index e7f620c..7abd2fa 100644
--- a/libappfuse/include/libappfuse/FuseBuffer.h
+++ b/libappfuse/include/libappfuse/FuseBuffer.h
@@ -34,8 +34,7 @@
   bool Read(int fd);
   bool Write(int fd) const;
  private:
-  bool CheckHeaderLength() const;
-  bool CheckResult(int result, const char* operation_name) const;
+  bool CheckHeaderLength(const char* name) const;
 };
 
 // FuseRequest represents file operation requests from /dev/fuse. It starts
diff --git a/libappfuse/tests/FuseBufferTest.cc b/libappfuse/tests/FuseBufferTest.cc
index c822135..db35d33 100644
--- a/libappfuse/tests/FuseBufferTest.cc
+++ b/libappfuse/tests/FuseBufferTest.cc
@@ -20,6 +20,8 @@
 #include <string.h>
 #include <sys/socket.h>
 
+#include <thread>
+
 #include <android-base/unique_fd.h>
 #include <gtest/gtest.h>
 
@@ -110,6 +112,30 @@
   TestWriteInvalidLength(sizeof(fuse_in_header) - 1);
 }
 
+TEST(FuseMessageTest, ShortWriteAndRead) {
+  int raw_fds[2];
+  ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, raw_fds));
+
+  android::base::unique_fd fds[2];
+  fds[0].reset(raw_fds[0]);
+  fds[1].reset(raw_fds[1]);
+
+  const int send_buffer_size = 1024;
+  ASSERT_EQ(0, setsockopt(fds[0], SOL_SOCKET, SO_SNDBUF, &send_buffer_size,
+                          sizeof(int)));
+
+  bool succeed = false;
+  const int sender_fd = fds[0].get();
+  std::thread thread([sender_fd, &succeed] {
+    FuseRequest request;
+    request.header.len = 1024 * 4;
+    succeed = request.Write(sender_fd);
+  });
+  thread.detach();
+  FuseRequest request;
+  ASSERT_TRUE(request.Read(fds[1]));
+}
+
 TEST(FuseResponseTest, Reset) {
   FuseResponse response;
   // Write 1 to the first ten bytes.
diff --git a/libbacktrace/Android.bp b/libbacktrace/Android.bp
index 200b6d6..5b31ecb 100644
--- a/libbacktrace/Android.bp
+++ b/libbacktrace/Android.bp
@@ -22,27 +22,13 @@
         "-Werror",
     ],
 
+    // The latest clang (r230699) does not allow SP/PC to be declared in inline asm lists.
     clang_cflags: ["-Wno-inline-asm"],
 
-    // The latest clang (r230699) does not allow SP/PC to be declared in inline asm lists.
     include_dirs: ["external/libunwind/include/tdep"],
 
-    // TODO: LLVM_DEVICE_BUILD_MK
-    // TODO: LLVM_HOST_BUILD_MK
 
     target: {
-        host: {
-            // -fno-omit-frame-pointer should be set for host build. Because currently
-            // libunwind can't recognize .debug_frame using dwarf version 4, and it relies
-            // on stack frame pointer to do unwinding on x86.
-            // $(LLVM_HOST_BUILD_MK) overwrites -fno-omit-frame-pointer. so the below line
-            // must be after the include.
-            cflags: [
-                "-Wno-extern-c-compat",
-                "-fno-omit-frame-pointer",
-            ],
-        },
-
         darwin: {
             enabled: false,
         },
@@ -130,4 +116,95 @@
             ],
         },
     }
-}
\ No newline at end of file
+}
+
+//-------------------------------------------------------------------------
+// The libbacktrace_offline static library.
+//-------------------------------------------------------------------------
+cc_library_static {
+    name: "libbacktrace_offline",
+    defaults: ["libbacktrace_common"],
+    host_supported: true,
+    srcs: ["BacktraceOffline.cpp"],
+
+    cflags: [
+        "-D__STDC_CONSTANT_MACROS",
+        "-D__STDC_LIMIT_MACROS",
+        "-D__STDC_FORMAT_MACROS",
+    ],
+
+    header_libs: ["llvm-headers"],
+
+    // Use shared libraries so their headers get included during build.
+    shared_libs = [
+        "libbase",
+        "libunwind",
+    ],
+}
+
+//-------------------------------------------------------------------------
+// The backtrace_test executable.
+//-------------------------------------------------------------------------
+cc_test {
+    name: "backtrace_test",
+    defaults: ["libbacktrace_common"],
+    host_supported: true,
+    srcs: [
+        "backtrace_offline_test.cpp",
+        "backtrace_test.cpp",
+        "GetPss.cpp",
+        "thread_utils.c",
+    ],
+
+    cflags: [
+        "-fno-builtin",
+        "-O0",
+        "-g",
+    ],
+
+    shared_libs: [
+        "libbacktrace_test",
+        "libbacktrace",
+        "libbase",
+        "libcutils",
+        "liblog",
+        "libunwind",
+    ],
+
+    group_static_libs: true,
+
+    // Statically link LLVMlibraries to remove dependency on llvm shared library.
+    static_libs = [
+        "libbacktrace_offline",
+        "libLLVMObject",
+        "libLLVMBitReader",
+        "libLLVMMC",
+        "libLLVMMCParser",
+        "libLLVMCore",
+        "libLLVMSupport",
+
+        "libziparchive",
+        "libz",
+    ],
+
+    header_libs: ["llvm-headers"],
+
+    target: {
+        android: {
+            cflags: ["-DENABLE_PSS_TESTS"],
+            shared_libs: [
+                "libdl",
+                "libutils",
+            ],
+        },
+        linux: {
+            host_ldlibs: [
+                "-lpthread",
+                "-lrt",
+                "-ldl",
+                "-lncurses",
+            ],
+            static_libs: ["libutils"],
+        },
+    },
+}
diff --git a/libbacktrace/Android.build.mk b/libbacktrace/Android.build.mk
deleted file mode 100644
index 2467f3e..0000000
--- a/libbacktrace/Android.build.mk
+++ /dev/null
@@ -1,95 +0,0 @@
-#
-# Copyright (C) 2014 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := $(module)
-LOCAL_MODULE_TAGS := $(module_tag)
-LOCAL_MULTILIB := $($(module)_multilib)
-ifeq ($(LOCAL_MULTILIB),both)
-ifneq ($(build_target),$(filter $(build_target),SHARED_LIBRARY STATIC_LIBRARY))
-  LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
-  LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
-endif
-endif
-
-ifeq ($(build_type),target)
-  include $(LLVM_DEVICE_BUILD_MK)
-else
-  include $(LLVM_HOST_BUILD_MK)
-endif
-
-LOCAL_ADDITIONAL_DEPENDENCIES += \
-    $(LOCAL_PATH)/Android.mk \
-    $(LOCAL_PATH)/Android.build.mk \
-
-LOCAL_CFLAGS += \
-    $(libbacktrace_common_cflags) \
-    $($(module)_cflags) \
-    $($(module)_cflags_$(build_type)) \
-
-LOCAL_CLANG_CFLAGS += \
-    $(libbacktrace_common_clang_cflags) \
-
-LOCAL_CONLYFLAGS += \
-    $(libbacktrace_common_conlyflags) \
-    $($(module)_conlyflags) \
-    $($(module)_conlyflags_$(build_type)) \
-
-LOCAL_CPPFLAGS += \
-    $(libbacktrace_common_cppflags) \
-    $($(module)_cppflags) \
-    $($(module)_cppflags_$(build_type)) \
-
-LOCAL_C_INCLUDES += \
-    $(libbacktrace_common_c_includes) \
-    $($(module)_c_includes) \
-    $($(module)_c_includes_$(build_type)) \
-
-LOCAL_SRC_FILES := \
-    $($(module)_src_files) \
-    $($(module)_src_files_$(build_type)) \
-
-LOCAL_STATIC_LIBRARIES += \
-    $($(module)_static_libraries) \
-    $($(module)_static_libraries_$(build_type)) \
-
-LOCAL_SHARED_LIBRARIES += \
-    $($(module)_shared_libraries) \
-    $($(module)_shared_libraries_$(build_type)) \
-
-LOCAL_LDLIBS += \
-    $($(module)_ldlibs) \
-    $($(module)_ldlibs_$(build_type)) \
-
-LOCAL_STRIP_MODULE := $($(module)_strip_module)
-
-ifeq ($(build_type),target)
-  include $(BUILD_$(build_target))
-endif
-
-ifeq ($(build_type),host)
-  # Only build if host builds are supported.
-  ifeq ($(build_host),true)
-    # -fno-omit-frame-pointer should be set for host build. Because currently
-    # libunwind can't recognize .debug_frame using dwarf version 4, and it relies
-    # on stack frame pointer to do unwinding on x86.
-    # $(LLVM_HOST_BUILD_MK) overwrites -fno-omit-frame-pointer. so the below line
-    # must be after the include.
-    LOCAL_CFLAGS += -Wno-extern-c-compat -fno-omit-frame-pointer
-    include $(BUILD_HOST_$(build_target))
-  endif
-endif
diff --git a/libbacktrace/Android.mk b/libbacktrace/Android.mk
deleted file mode 100644
index f4976e9..0000000
--- a/libbacktrace/Android.mk
+++ /dev/null
@@ -1,121 +0,0 @@
-#
-# Copyright (C) 2014 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH:= $(call my-dir)
-
-libbacktrace_common_cflags := \
-	-Wall \
-	-Werror \
-
-libbacktrace_common_c_includes := \
-	external/libunwind/include/tdep \
-
-# The latest clang (r230699) does not allow SP/PC to be declared in inline asm lists.
-libbacktrace_common_clang_cflags += \
-    -Wno-inline-asm
-
-build_host := false
-ifeq ($(HOST_OS),linux)
-ifeq ($(HOST_ARCH),$(filter $(HOST_ARCH),x86 x86_64))
-build_host := true
-endif
-endif
-
-LLVM_ROOT_PATH := external/llvm
-include $(LLVM_ROOT_PATH)/llvm.mk
-
-#-------------------------------------------------------------------------
-# The libbacktrace_offline static library.
-#-------------------------------------------------------------------------
-libbacktrace_offline_src_files := \
-	BacktraceOffline.cpp \
-
-# Use shared libraries so their headers get included during build.
-libbacktrace_offline_shared_libraries := \
-	libbase \
-	libunwind \
-
-module := libbacktrace_offline
-build_type := target
-build_target := STATIC_LIBRARY
-libbacktrace_offline_multilib := both
-include $(LOCAL_PATH)/Android.build.mk
-build_type := host
-include $(LOCAL_PATH)/Android.build.mk
-
-#-------------------------------------------------------------------------
-# The backtrace_test executable.
-#-------------------------------------------------------------------------
-backtrace_test_cflags := \
-	-fno-builtin \
-	-O0 \
-	-g \
-
-backtrace_test_cflags_target := \
-	-DENABLE_PSS_TESTS \
-
-backtrace_test_src_files := \
-	backtrace_offline_test.cpp \
-	backtrace_test.cpp \
-	GetPss.cpp \
-	thread_utils.c \
-
-backtrace_test_ldlibs_host := \
-	-lpthread \
-	-lrt \
-
-backtrace_test_shared_libraries := \
-	libbacktrace_test \
-	libbacktrace \
-	libbase \
-	libcutils \
-	liblog \
-	libunwind \
-
-backtrace_test_shared_libraries_target += \
-	libdl \
-	libutils \
-
-# Statically link LLVMlibraries to remove dependency on llvm shared library.
-backtrace_test_static_libraries := \
-	libbacktrace_offline \
-	libLLVMObject \
-	libLLVMBitReader \
-	libLLVMMC \
-	libLLVMMCParser \
-	libLLVMCore \
-	libLLVMSupport \
-
-backtrace_test_static_libraries_target := \
-	libziparchive \
-	libz \
-
-backtrace_test_static_libraries_host := \
-	libziparchive \
-	libz \
-	libutils \
-
-backtrace_test_ldlibs_host += \
-	-ldl \
-
-module := backtrace_test
-module_tag := debug
-build_type := target
-build_target := NATIVE_TEST
-backtrace_test_multilib := both
-include $(LOCAL_PATH)/Android.build.mk
-build_type := host
-include $(LOCAL_PATH)/Android.build.mk
diff --git a/libbacktrace/BacktraceOffline.cpp b/libbacktrace/BacktraceOffline.cpp
index 9e95563..5e54328 100644
--- a/libbacktrace/BacktraceOffline.cpp
+++ b/libbacktrace/BacktraceOffline.cpp
@@ -34,6 +34,7 @@
 #include <vector>
 
 #include <android-base/file.h>
+#include <android-base/macros.h>
 #include <backtrace/Backtrace.h>
 #include <backtrace/BacktraceMap.h>
 #include <ziparchive/zip_archive.h>
@@ -534,6 +535,10 @@
     default:
       result = false;
   }
+#else
+  UNUSED(reg);
+  UNUSED(value);
+  result = false;
 #endif
   return result;
 }
diff --git a/libcutils/fs_config.c b/libcutils/fs_config.c
index 99f97d1..dddb289 100644
--- a/libcutils/fs_config.c
+++ b/libcutils/fs_config.c
@@ -149,6 +149,10 @@
                                               "system/bin/run-as" },
     { 00700, AID_SYSTEM,    AID_SHELL,     CAP_MASK_LONG(CAP_BLOCK_SUSPEND),
                                               "system/bin/inputflinger" },
+    { 00750, AID_SYSTEM,    AID_SHELL,     CAP_MASK_LONG(CAP_SETUID) |
+                                           CAP_MASK_LONG(CAP_SETGID) |
+                                           CAP_MASK_LONG(CAP_SYS_PTRACE),
+                                              "system/bin/storaged" },
 
     /* Support FIFO scheduling mode in SurfaceFlinger. */
     { 00755, AID_SYSTEM,    AID_GRAPHICS,  CAP_MASK_LONG(CAP_SYS_NICE),
@@ -189,6 +193,9 @@
     { 00750, AID_ROOT,      AID_SHELL,     0, "init*" },
     { 00750, AID_ROOT,      AID_SHELL,     0, "sbin/fs_mgr" },
     { 00640, AID_ROOT,      AID_SHELL,     0, "fstab.*" },
+    { 00600, AID_ROOT,      AID_ROOT,      0, "system/build.prop" },
+    { 00600, AID_ROOT,      AID_ROOT,      0, "vendor/build.prop" },
+    { 00600, AID_ROOT,      AID_ROOT,      0, "default.prop" },
     { 00644, AID_ROOT,      AID_ROOT,      0, 0 },
 };
 
diff --git a/libcutils/multiuser.c b/libcutils/multiuser.c
index 0f4427b..0ef337d 100644
--- a/libcutils/multiuser.c
+++ b/libcutils/multiuser.c
@@ -15,21 +15,36 @@
  */
 
 #include <cutils/multiuser.h>
+#include <private/android_filesystem_config.h>
 
 userid_t multiuser_get_user_id(uid_t uid) {
-    return uid / MULTIUSER_APP_PER_USER_RANGE;
+    return uid / AID_USER_OFFSET;
 }
 
 appid_t multiuser_get_app_id(uid_t uid) {
-    return uid % MULTIUSER_APP_PER_USER_RANGE;
+    return uid % AID_USER_OFFSET;
 }
 
-uid_t multiuser_get_uid(userid_t userId, appid_t appId) {
-    return userId * MULTIUSER_APP_PER_USER_RANGE + (appId % MULTIUSER_APP_PER_USER_RANGE);
+uid_t multiuser_get_uid(userid_t user_id, appid_t app_id) {
+    return (user_id * AID_USER_OFFSET) + (app_id % AID_USER_OFFSET);
 }
 
-appid_t multiuser_get_shared_app_gid(uid_t id) {
-  return MULTIUSER_FIRST_SHARED_APPLICATION_GID + (id % MULTIUSER_APP_PER_USER_RANGE)
-          - MULTIUSER_FIRST_APPLICATION_UID;
+gid_t multiuser_get_cache_gid(userid_t user_id, appid_t app_id) {
+    if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
+        return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_CACHE_GID_START);
+    } else {
+        return -1;
+    }
+}
 
+gid_t multiuser_get_shared_gid(userid_t user_id, appid_t app_id) {
+    if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
+        return multiuser_get_uid(user_id, (app_id - AID_APP_START) + AID_SHARED_GID_START);
+    } else {
+        return -1;
+    }
+}
+
+gid_t multiuser_get_shared_app_gid(uid_t uid) {
+    return multiuser_get_shared_gid(multiuser_get_user_id(uid), multiuser_get_app_id(uid));
 }
diff --git a/libcutils/properties.c b/libcutils/properties.c
index 5aa6371..740c7a9 100644
--- a/libcutils/properties.c
+++ b/libcutils/properties.c
@@ -36,7 +36,7 @@
     }
 
     int8_t result = default_value;
-    char buf[PROPERTY_VALUE_MAX] = {'\0',};
+    char buf[PROPERTY_VALUE_MAX] = {'\0'};
 
     int len = property_get(key, buf, "");
     if (len == 1) {
@@ -47,7 +47,7 @@
             result = true;
         }
     } else if (len > 1) {
-         if (!strcmp(buf, "no") || !strcmp(buf, "false") || !strcmp(buf, "off")) {
+        if (!strcmp(buf, "no") || !strcmp(buf, "false") || !strcmp(buf, "off")) {
             result = false;
         } else if (!strcmp(buf, "yes") || !strcmp(buf, "true") || !strcmp(buf, "on")) {
             result = true;
@@ -59,13 +59,13 @@
 
 // Convert string property to int (default if fails); return default value if out of bounds
 static intmax_t property_get_imax(const char *key, intmax_t lower_bound, intmax_t upper_bound,
-        intmax_t default_value) {
+                                  intmax_t default_value) {
     if (!key) {
         return default_value;
     }
 
     intmax_t result = default_value;
-    char buf[PROPERTY_VALUE_MAX] = {'\0',};
+    char buf[PROPERTY_VALUE_MAX] = {'\0'};
     char *end = NULL;
 
     int len = property_get(key, buf, "");
@@ -74,7 +74,7 @@
         errno = 0;
 
         // Infer base automatically
-        result = strtoimax(buf, &end, /*base*/0);
+        result = strtoimax(buf, &end, /*base*/ 0);
         if ((result == INTMAX_MIN || result == INTMAX_MAX) && errno == ERANGE) {
             // Over or underflow
             result = default_value;
@@ -86,8 +86,8 @@
         } else if (end == buf) {
             // Numeric conversion failed
             result = default_value;
-            ALOGV("%s(%s,%" PRIdMAX ") - numeric conversion failed",
-                    __FUNCTION__, key, default_value);
+            ALOGV("%s(%s,%" PRIdMAX ") - numeric conversion failed", __FUNCTION__, key,
+                  default_value);
         }
 
         errno = tmp;
@@ -107,38 +107,31 @@
 #define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
 #include <sys/_system_properties.h>
 
-int property_set(const char *key, const char *value)
-{
+int property_set(const char *key, const char *value) {
     return __system_property_set(key, value);
 }
 
-int property_get(const char *key, char *value, const char *default_value)
-{
+int property_get(const char *key, char *value, const char *default_value) {
     int len;
 
     len = __system_property_get(key, value);
-    if(len > 0) {
+    if (len > 0) {
         return len;
     }
-    if(default_value) {
-        len = strlen(default_value);
-        if (len >= PROPERTY_VALUE_MAX) {
-            len = PROPERTY_VALUE_MAX - 1;
-        }
+    if (default_value) {
+        len = strnlen(default_value, PROPERTY_VALUE_MAX - 1);
         memcpy(value, default_value, len);
         value[len] = '\0';
     }
     return len;
 }
 
-struct property_list_callback_data
-{
+struct property_list_callback_data {
     void (*propfn)(const char *key, const char *value, void *cookie);
     void *cookie;
 };
 
-static void property_list_callback(const prop_info *pi, void *cookie)
-{
+static void property_list_callback(const prop_info *pi, void *cookie) {
     char name[PROP_NAME_MAX];
     char value[PROP_VALUE_MAX];
     struct property_list_callback_data *data = cookie;
@@ -147,10 +140,7 @@
     data->propfn(name, value, data->cookie);
 }
 
-int property_list(
-        void (*propfn)(const char *key, const char *value, void *cookie),
-        void *cookie)
-{
-    struct property_list_callback_data data = { propfn, cookie };
+int property_list(void (*propfn)(const char *key, const char *value, void *cookie), void *cookie) {
+    struct property_list_callback_data data = {propfn, cookie};
     return __system_property_foreach(property_list_callback, &data);
 }
diff --git a/libcutils/tests/Android.bp b/libcutils/tests/Android.bp
index abe3c21..0b0dc09 100644
--- a/libcutils/tests/Android.bp
+++ b/libcutils/tests/Android.bp
@@ -26,7 +26,8 @@
                 "trace-dev_test.cpp",
                 "test_str_parms.cpp",
                 "android_get_control_socket_test.cpp",
-                "android_get_control_file_test.cpp"
+                "android_get_control_file_test.cpp",
+                "multiuser_test.cpp"
             ],
         },
 
diff --git a/libcutils/tests/PropertiesTest.cpp b/libcutils/tests/PropertiesTest.cpp
index f0cdffd..7921972 100644
--- a/libcutils/tests/PropertiesTest.cpp
+++ b/libcutils/tests/PropertiesTest.cpp
@@ -159,19 +159,68 @@
 
 TEST_F(PropertiesTest, GetString) {
 
-    // Try to use a default value that's too long => set fails
+    // Try to use a default value that's too long => get truncates the value
     {
         ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
 
-        std::string maxLengthString = std::string(PROPERTY_VALUE_MAX-1, 'a');
+        std::string maxLengthString = std::string(PROPERTY_VALUE_MAX - 1, 'a');
         std::string oneLongerString = std::string(PROPERTY_VALUE_MAX, 'a');
 
         // Expect that the value is truncated since it's too long (by 1)
         int len = property_get(PROPERTY_TEST_KEY, mValue, oneLongerString.c_str());
-        EXPECT_EQ(PROPERTY_VALUE_MAX-1, len);
+        EXPECT_EQ(PROPERTY_VALUE_MAX - 1, len);
         EXPECT_STREQ(maxLengthString.c_str(), mValue);
         ResetValue();
     }
+
+    // Try to use a default value that's the max length => get succeeds
+    {
+        ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
+
+        std::string maxLengthString = std::string(PROPERTY_VALUE_MAX - 1, 'b');
+
+        // Expect that the value matches maxLengthString
+        int len = property_get(PROPERTY_TEST_KEY, mValue, maxLengthString.c_str());
+        EXPECT_EQ(PROPERTY_VALUE_MAX - 1, len);
+        EXPECT_STREQ(maxLengthString.c_str(), mValue);
+        ResetValue();
+    }
+
+    // Try to use a default value of length one => get succeeds
+    {
+        ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
+
+        std::string oneCharString = std::string(1, 'c');
+
+        // Expect that the value matches oneCharString
+        int len = property_get(PROPERTY_TEST_KEY, mValue, oneCharString.c_str());
+        EXPECT_EQ(1, len);
+        EXPECT_STREQ(oneCharString.c_str(), mValue);
+        ResetValue();
+    }
+
+    // Try to use a default value of length zero => get succeeds
+    {
+        ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
+
+        std::string zeroCharString = std::string(0, 'd');
+
+        // Expect that the value matches oneCharString
+        int len = property_get(PROPERTY_TEST_KEY, mValue, zeroCharString.c_str());
+        EXPECT_EQ(0, len);
+        EXPECT_STREQ(zeroCharString.c_str(), mValue);
+        ResetValue();
+    }
+
+    // Try to use a NULL default value => get returns 0
+    {
+        ASSERT_OK(property_set(PROPERTY_TEST_KEY, ""));
+
+        // Expect a return value of 0
+        int len = property_get(PROPERTY_TEST_KEY, mValue, NULL);
+        EXPECT_EQ(0, len);
+        ResetValue();
+    }
 }
 
 TEST_F(PropertiesTest, GetBool) {
diff --git a/libcutils/tests/multiuser_test.cpp b/libcutils/tests/multiuser_test.cpp
new file mode 100644
index 0000000..2307ea8
--- /dev/null
+++ b/libcutils/tests/multiuser_test.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cutils/multiuser.h>
+#include <gtest/gtest.h>
+
+TEST(MultiuserTest, TestMerge) {
+    EXPECT_EQ(0, multiuser_get_uid(0, 0));
+    EXPECT_EQ(1000, multiuser_get_uid(0, 1000));
+    EXPECT_EQ(10000, multiuser_get_uid(0, 10000));
+    EXPECT_EQ(50000, multiuser_get_uid(0, 50000));
+    EXPECT_EQ(1000000, multiuser_get_uid(10, 0));
+    EXPECT_EQ(1001000, multiuser_get_uid(10, 1000));
+    EXPECT_EQ(1010000, multiuser_get_uid(10, 10000));
+    EXPECT_EQ(1050000, multiuser_get_uid(10, 50000));
+}
+
+TEST(MultiuserTest, TestSplitUser) {
+    EXPECT_EQ(0, multiuser_get_user_id(0));
+    EXPECT_EQ(0, multiuser_get_user_id(1000));
+    EXPECT_EQ(0, multiuser_get_user_id(10000));
+    EXPECT_EQ(0, multiuser_get_user_id(50000));
+    EXPECT_EQ(10, multiuser_get_user_id(1000000));
+    EXPECT_EQ(10, multiuser_get_user_id(1001000));
+    EXPECT_EQ(10, multiuser_get_user_id(1010000));
+    EXPECT_EQ(10, multiuser_get_user_id(1050000));
+}
+
+TEST(MultiuserTest, TestSplitApp) {
+    EXPECT_EQ(0, multiuser_get_app_id(0));
+    EXPECT_EQ(1000, multiuser_get_app_id(1000));
+    EXPECT_EQ(10000, multiuser_get_app_id(10000));
+    EXPECT_EQ(50000, multiuser_get_app_id(50000));
+    EXPECT_EQ(0, multiuser_get_app_id(1000000));
+    EXPECT_EQ(1000, multiuser_get_app_id(1001000));
+    EXPECT_EQ(10000, multiuser_get_app_id(1010000));
+    EXPECT_EQ(50000, multiuser_get_app_id(1050000));
+}
+
+TEST(MultiuserTest, TestCache) {
+    EXPECT_EQ(-1, multiuser_get_cache_gid(0, 0));
+    EXPECT_EQ(-1, multiuser_get_cache_gid(0, 1000));
+    EXPECT_EQ(20000, multiuser_get_cache_gid(0, 10000));
+    EXPECT_EQ(-1, multiuser_get_cache_gid(0, 50000));
+    EXPECT_EQ(1020000, multiuser_get_cache_gid(10, 10000));
+}
+
+TEST(MultiuserTest, TestShared) {
+    EXPECT_EQ(-1, multiuser_get_shared_gid(0, 0));
+    EXPECT_EQ(-1, multiuser_get_shared_gid(0, 1000));
+    EXPECT_EQ(50000, multiuser_get_shared_gid(0, 10000));
+    EXPECT_EQ(-1, multiuser_get_shared_gid(0, 50000));
+    EXPECT_EQ(1050000, multiuser_get_shared_gid(10, 10000));
+}
diff --git a/libcutils/uevent.c b/libcutils/uevent.c
index de5d227..f548dca 100644
--- a/libcutils/uevent.c
+++ b/libcutils/uevent.c
@@ -116,7 +116,12 @@
     if(s < 0)
         return -1;
 
-    setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &buf_sz, sizeof(buf_sz));
+    /* buf_sz should be less than net.core.rmem_max for this to succeed */
+    if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &buf_sz, sizeof(buf_sz)) < 0) {
+        close(s);
+        return -1;
+    }
+
     setsockopt(s, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
 
     if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
diff --git a/liblog/Android.bp b/liblog/Android.bp
index e59a460..bbe7d79 100644
--- a/liblog/Android.bp
+++ b/liblog/Android.bp
@@ -21,6 +21,7 @@
     "config_write.c",
     "logger_name.c",
     "logger_lock.c",
+    "log_ratelimit.cpp",
 ]
 liblog_host_sources = [
     "fake_log_device.c",
@@ -30,7 +31,7 @@
     "event_tag_map.cpp",
     "config_read.c",
     "log_time.cpp",
-    "log_is_loggable.c",
+    "properties.c",
     "logprint.c",
     "pmsg_reader.c",
     "pmsg_writer.c",
@@ -114,4 +115,5 @@
     name: "liblog.ndk",
     symbol_file: "liblog.map.txt",
     first_version: "9",
+    unversioned_until: "current",
 }
diff --git a/liblog/event_tag_map.cpp b/liblog/event_tag_map.cpp
index e8e0335..1155422 100644
--- a/liblog/event_tag_map.cpp
+++ b/liblog/event_tag_map.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2007 The Android Open Source Project
+ * Copyright (C) 2007-2016 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -24,362 +24,106 @@
 #include <string.h>
 #include <sys/mman.h>
 
-#include <android/log.h>
+#include <experimental/string_view>
+#include <functional>
+#include <unordered_map>
+
 #include <log/event_tag_map.h>
 
 #include "log_portability.h"
 
 #define OUT_TAG "EventTagMap"
 
-/*
- * Single entry.
- */
-typedef struct EventTag {
-    uint32_t tagIndex;
-    char*    tagStr;
-    size_t   tagLen;
-    char*    fmtStr;
-    size_t   fmtLen;
-} EventTag;
+typedef std::experimental::string_view MapString;
 
-/*
- * Map.
- */
-struct EventTagMap {
-    /* memory-mapped source file; we get strings from here */
-    void*           mapAddr;
-    size_t          mapLen;
+typedef std::pair<MapString, MapString> TagFmt;
 
-    /* array of event tags, sorted numerically by tag index */
-    EventTag*       tagArray;
-    int             numTags;
+template <> struct _LIBCPP_TYPE_VIS_ONLY std::hash<TagFmt>
+        : public std::unary_function<const TagFmt&, size_t> {
+    _LIBCPP_INLINE_VISIBILITY
+    size_t operator()(const TagFmt& __t) const _NOEXCEPT {
+        // Tag is typically unique.  Will cost us an extra 100ns for the
+        // unordered_map lookup if we instead did a hash that combined
+        // both of tag and fmt members, e.g.:
+        //
+        // return std::hash<MapString>()(__t.first) ^
+        //        std::hash<MapString>()(__t.second);
+        return std::hash<MapString>()(__t.first);
+    }
 };
 
-/* fwd */
-static int processFile(EventTagMap* map);
-static int countMapLines(const EventTagMap* map);
-static int parseMapLines(EventTagMap* map);
-static int scanTagLine(char** pData, EventTag* tag, int lineNum);
-static int sortTags(EventTagMap* map);
+// Map
+struct EventTagMap {
+    // memory-mapped source file; we get strings from here
+    void*  mapAddr;
+    size_t mapLen;
 
-/*
- * Open the map file and allocate a structure to manage it.
- *
- * We create a private mapping because we want to terminate the log tag
- * strings with '\0'.
- */
-LIBLOG_ABI_PUBLIC EventTagMap* android_openEventTagMap(const char* fileName)
-{
-    EventTagMap* newTagMap;
-    off_t end;
-    int save_errno;
-    const char* tagfile = fileName ? fileName : EVENT_TAG_MAP_FILE;
+private:
+    std::unordered_map<uint32_t, TagFmt> Idx2TagFmt;
 
-    int fd = open(tagfile, O_RDONLY | O_CLOEXEC);
-    if (fd < 0) {
-        save_errno = errno;
-        fprintf(stderr, "%s: unable to open map '%s': %s\n",
-                OUT_TAG, tagfile, strerror(save_errno));
-        goto fail_errno;
-    }
+public:
+    EventTagMap() : mapAddr(NULL), mapLen(0) { }
 
-    end = lseek(fd, 0L, SEEK_END);
-    save_errno = errno;
-    (void) lseek(fd, 0L, SEEK_SET);
-    if (end < 0) {
-        fprintf(stderr, "%s: unable to seek map '%s' %s\n",
-                OUT_TAG, tagfile, strerror(save_errno));
-        goto fail_close;
-    }
-
-    newTagMap = (EventTagMap*)calloc(1, sizeof(EventTagMap));
-    if (newTagMap == NULL) {
-        save_errno = errno;
-        goto fail_close;
-    }
-
-    newTagMap->mapAddr = mmap(NULL, end, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
-    save_errno = errno;
-    close(fd);
-    fd = -1;
-    if ((newTagMap->mapAddr == MAP_FAILED) || (newTagMap->mapAddr == NULL)) {
-        fprintf(stderr, "%s: mmap(%s) failed: %s\n",
-                OUT_TAG, tagfile, strerror(save_errno));
-        goto fail_free;
-    }
-
-    newTagMap->mapLen = end;
-
-    if (processFile(newTagMap) != 0) goto fail_unmap;
-
-    return newTagMap;
-
-fail_unmap:
-    munmap(newTagMap->mapAddr, newTagMap->mapLen);
-    save_errno = EINVAL;
-fail_free:
-    free(newTagMap);
-fail_close:
-    close(fd);
-fail_errno:
-    errno = save_errno;
-fail:
-    return NULL;
-}
-
-/*
- * Close the map.
- */
-LIBLOG_ABI_PUBLIC void android_closeEventTagMap(EventTagMap* map)
-{
-    if (map == NULL) return;
-
-    munmap(map->mapAddr, map->mapLen);
-    free(map->tagArray);
-    free(map);
-}
-
-/*
- * Look up an entry in the map.
- *
- * The entries are sorted by tag number, so we can do a binary search.
- */
-LIBLOG_ABI_PUBLIC const char* android_lookupEventTag_len(const EventTagMap* map,
-                                                         size_t *len,
-                                                         unsigned int tag)
-{
-    int lo = 0;
-    int hi = map->numTags - 1;
-
-    while (lo <= hi) {
-        int mid = (lo + hi) / 2;
-        int cmp = map->tagArray[mid].tagIndex - tag;
-
-        if (cmp < 0) {
-            /* tag is bigger */
-            lo = mid + 1;
-        } else if (cmp > 0) {
-            /* tag is smaller */
-            hi = mid - 1;
-        } else {
-            /* found */
-            if (len) *len = map->tagArray[mid].tagLen;
-            /*
-             * b/31456426 to check if gTest can detect copy-on-write issue
-             * add the following line to break us:
-             *     map->tagArray[mid].tagStr[map->tagArray[mid].tagLen] = '\0';
-             * or explicitly use deprecated android_lookupEventTag().
-             */
-            return map->tagArray[mid].tagStr;
+    ~EventTagMap() {
+        Idx2TagFmt.clear();
+        if (mapAddr) {
+            munmap(mapAddr, mapLen);
+            mapAddr = 0;
         }
     }
 
-    errno = ENOENT;
-    if (len) *len = 0;
-    return NULL;
-}
+    bool emplaceUnique(uint32_t tag, const TagFmt& tagfmt, bool verbose = false);
+    const TagFmt* find(uint32_t tag) const;
+};
 
-/*
- * Look up an entry in the map.
- *
- * The entries are sorted by tag number, so we can do a binary search.
- */
-LIBLOG_ABI_PUBLIC const char* android_lookupEventFormat_len(
-    const EventTagMap* map, size_t *len, unsigned int tag)
-{
-    int lo = 0;
-    int hi = map->numTags - 1;
-
-    while (lo <= hi) {
-        int mid = (lo + hi) / 2;
-        int cmp = map->tagArray[mid].tagIndex - tag;
-
-        if (cmp < 0) {
-            /* tag is bigger */
-            lo = mid + 1;
-        } else if (cmp > 0) {
-            /* tag is smaller */
-            hi = mid - 1;
-        } else {
-            /* found */
-            if (len) *len = map->tagArray[mid].fmtLen;
-            return map->tagArray[mid].fmtStr;
+bool EventTagMap::emplaceUnique(uint32_t tag, const TagFmt& tagfmt, bool verbose) {
+    std::unordered_map<uint32_t, TagFmt>::const_iterator it;
+    it = Idx2TagFmt.find(tag);
+    if (it != Idx2TagFmt.end()) {
+        if (verbose) {
+            fprintf(stderr,
+                    OUT_TAG ": duplicate tag entries %" PRIu32
+                        ":%.*s:%.*s and %" PRIu32 ":%.*s:%.*s)\n",
+                    it->first,
+                    (int)it->second.first.length(), it->second.first.data(),
+                    (int)it->second.second.length(), it->second.second.data(),
+                    tag,
+                    (int)tagfmt.first.length(), tagfmt.first.data(),
+                    (int)tagfmt.second.length(), tagfmt.second.data());
         }
+        return false;
     }
 
-    errno = ENOENT;
-    if (len) *len = 0;
-    return NULL;
+    Idx2TagFmt.emplace(std::make_pair(tag, tagfmt));
+    return true;
 }
 
-LIBLOG_ABI_PUBLIC const char* android_lookupEventTag(const EventTagMap* map,
-                                                     unsigned int tag)
-{
-    size_t len;
-    const char* tagStr = android_lookupEventTag_len(map, &len, tag);
+const TagFmt* EventTagMap::find(uint32_t tag) const {
+    std::unordered_map<uint32_t, TagFmt>::const_iterator it;
+    it = Idx2TagFmt.find(tag);
+    if (it == Idx2TagFmt.end()) return NULL;
+    return &(it->second);
+}
+
+// Scan one tag line.
+//
+// "*pData" should be pointing to the first digit in the tag number.  On
+// successful return, it will be pointing to the last character in the
+// tag line (i.e. the character before the start of the next line).
+//
+// Returns 0 on success, nonzero on failure.
+static int scanTagLine(EventTagMap* map, char** pData, int lineNum) {
     char* cp;
-
-    if (!tagStr) return tagStr;
-    cp = (char*)tagStr;
-    cp += len;
-    if (*cp) *cp = '\0'; /* Trigger copy on write :-( */
-    return tagStr;
-}
-
-/*
- * Crunch through the file, parsing the contents and creating a tag index.
- */
-static int processFile(EventTagMap* map)
-{
-    /* get a tag count */
-    map->numTags = countMapLines(map);
-    if (map->numTags < 0) {
-        errno = ENOENT;
-        return -1;
-    }
-
-    /* allocate storage for the tag index array */
-    map->tagArray = (EventTag*)calloc(1, sizeof(EventTag) * map->numTags);
-    if (map->tagArray == NULL) return -1;
-
-    /* parse the file, null-terminating tag strings */
-    if (parseMapLines(map) != 0) return -1;
-
-    /* sort the tags and check for duplicates */
-    if (sortTags(map) != 0) return -1;
-
-    return 0;
-}
-
-/*
- * Run through all lines in the file, determining whether they're blank,
- * comments, or possibly have a tag entry.
- *
- * This is a very "loose" scan.  We don't try to detect syntax errors here.
- * The later pass is more careful, but the number of tags found there must
- * match the number of tags found here.
- *
- * Returns the number of potential tag entries found.
- */
-static int countMapLines(const EventTagMap* map)
-{
-    const char* cp = (const char*) map->mapAddr;
-    const char* endp = cp + map->mapLen;
-    int numTags = 0;
-    int unknown = 1;
-
-    while (cp < endp) {
-        if (*cp == '\n') {
-            unknown = 1;
-        } else if (unknown) {
-            if (isdigit(*cp)) {
-                /* looks like a tag to me */
-                numTags++;
-                unknown = 0;
-            } else if (isspace(*cp)) {
-                /* might be leading whitespace before tag num, keep going */
-            } else {
-                /* assume comment; second pass can complain in detail */
-                unknown = 0;
-            }
-        } else {
-            /* we've made up our mind; just scan to end of line */
-        }
-        cp++;
-    }
-
-    return numTags;
-}
-
-/*
- * Parse the tags out of the file.
- */
-static int parseMapLines(EventTagMap* map)
-{
-    int tagNum, lineStart, lineNum;
-    char* cp = (char*) map->mapAddr;
-    char* endp = cp + map->mapLen;
-
-    /* insist on EOL at EOF; simplifies parsing and null-termination */
-    if (*(endp - 1) != '\n') {
-        fprintf(stderr, "%s: map file missing EOL on last line\n", OUT_TAG);
-        errno = EINVAL;
-        return -1;
-    }
-
-    tagNum = 0;
-    lineStart = 1;
-    lineNum = 1;
-    while (cp < endp) {
-        if (*cp == '\n') {
-            lineStart = 1;
-            lineNum++;
-        } else if (lineStart) {
-            if (*cp == '#') {
-                /* comment; just scan to end */
-                lineStart = 0;
-            } else if (isdigit(*cp)) {
-                /* looks like a tag; scan it out */
-                if (tagNum >= map->numTags) {
-                    fprintf(stderr,
-                        "%s: more tags than expected (%d)\n", OUT_TAG, tagNum);
-                    errno = EMFILE;
-                    return -1;
-                }
-                if (scanTagLine(&cp, &map->tagArray[tagNum], lineNum) != 0) {
-                    return -1;
-                }
-                tagNum++;
-                lineNum++;      // we eat the '\n'
-                /* leave lineStart==1 */
-            } else if (isspace(*cp)) {
-                /* looks like leading whitespace; keep scanning */
-            } else {
-                fprintf(stderr,
-                    "%s: unexpected chars (0x%02x) in tag number on line %d\n",
-                    OUT_TAG, *cp, lineNum);
-                errno = EINVAL;
-                return -1;
-            }
-        } else {
-            /* this is a blank or comment line */
-        }
-        cp++;
-    }
-
-    if (tagNum != map->numTags) {
-        fprintf(stderr, "%s: parsed %d tags, expected %d\n",
-            OUT_TAG, tagNum, map->numTags);
-        errno = EINVAL;
-        return -1;
-    }
-
-    return 0;
-}
-
-/*
- * Scan one tag line.
- *
- * "*pData" should be pointing to the first digit in the tag number.  On
- * successful return, it will be pointing to the last character in the
- * tag line (i.e. the character before the start of the next line).
- *
- * Returns 0 on success, nonzero on failure.
- */
-static int scanTagLine(char** pData, EventTag* tag, int lineNum)
-{
-    char* cp;
-
     unsigned long val = strtoul(*pData, &cp, 10);
     if (cp == *pData) {
-        fprintf(stderr, "%s: malformed tag number on line %d\n", OUT_TAG, lineNum);
+        fprintf(stderr, OUT_TAG ": malformed tag number on line %d\n", lineNum);
         errno = EINVAL;
         return -1;
     }
 
-    tag->tagIndex = val;
-    if (tag->tagIndex != val) {
-        fprintf(stderr, "%s: tag number too large on line %d\n", OUT_TAG, lineNum);
+    uint32_t tagIndex = val;
+    if (tagIndex != val) {
+        fprintf(stderr, OUT_TAG ": tag number too large on line %d\n", lineNum);
         errno = ERANGE;
         return -1;
     }
@@ -388,76 +132,192 @@
     }
 
     if (*cp == '\n') {
-        fprintf(stderr, "%s: missing tag string on line %d\n", OUT_TAG, lineNum);
+        fprintf(stderr, OUT_TAG ": missing tag string on line %d\n", lineNum);
         errno = EINVAL;
         return -1;
     }
 
-    tag->tagStr = cp;
-
-    /* Determine whether "c" is a valid tag char. */
-    while (isalnum(*++cp) || (*cp == '_')) {
-    }
-    tag->tagLen = cp - tag->tagStr;
+    const char* tag = cp;
+    // Determine whether "c" is a valid tag char.
+    while (isalnum(*++cp) || (*cp == '_')) { }
+    size_t tagLen = cp - tag;
 
     if (!isspace(*cp)) {
-        fprintf(stderr, "%s: invalid tag chars on line %d\n", OUT_TAG, lineNum);
+        fprintf(stderr, OUT_TAG ": invalid tag chars on line %d\n", lineNum);
         errno = EINVAL;
         return -1;
     }
 
     while (isspace(*cp) && (*cp != '\n')) ++cp;
+    const char* fmt = NULL;
+    size_t fmtLen = 0;
     if (*cp != '#') {
-        tag->fmtStr = cp;
+        fmt = cp;
         while ((*cp != '\n') && (*cp != '#')) ++cp;
-        while ((cp > tag->fmtStr) && isspace(*(cp - 1))) --cp;
-        tag->fmtLen = cp - tag->fmtStr;
+        while ((cp > fmt) && isspace(*(cp - 1))) --cp;
+        fmtLen = cp - fmt;
     }
 
     while (*cp != '\n') ++cp;
+#ifdef DEBUG
+    fprintf(stderr, "%d: %p: %.*s\n", lineNum, tag, (int)(cp - *pData), *pData);
+#endif
     *pData = cp;
 
-    return 0;
+    if (map->emplaceUnique(tagIndex, TagFmt(std::make_pair(
+            MapString(tag, tagLen), MapString(fmt, fmtLen))), true)) {
+        return 0;
+    }
+    errno = EMLINK;
+    return -1;
 }
 
-/*
- * Compare two EventTags.
- */
-static int compareEventTags(const void* v1, const void* v2)
-{
-    const EventTag* tag1 = (const EventTag*) v1;
-    const EventTag* tag2 = (const EventTag*) v2;
+// Parse the tags out of the file.
+static int parseMapLines(EventTagMap* map) {
+    char* cp = static_cast<char*>(map->mapAddr);
+    size_t len = map->mapLen;
+    char* endp = cp + len;
 
-    return tag1->tagIndex - tag2->tagIndex;
-}
+    // insist on EOL at EOF; simplifies parsing and null-termination
+    if (!len || (*(endp - 1) != '\n')) {
+#ifdef DEBUG
+        fprintf(stderr, OUT_TAG ": map file missing EOL on last line\n");
+#endif
+        errno = EINVAL;
+        return -1;
+    }
 
-/*
- * Sort the EventTag array so we can do fast lookups by tag index.  After
- * the sort we do a quick check for duplicate tag indices.
- *
- * Returns 0 on success.
- */
-static int sortTags(EventTagMap* map)
-{
-    int i;
-
-    qsort(map->tagArray, map->numTags, sizeof(EventTag), compareEventTags);
-
-    for (i = 1; i < map->numTags; i++) {
-        if (map->tagArray[i].tagIndex == map->tagArray[i - 1].tagIndex) {
-            fprintf(stderr,
-                "%s: duplicate tag entries (%" PRIu32 ":%.*s:%.*s and %" PRIu32 ":%.*s:%.*s)\n",
-                OUT_TAG,
-                map->tagArray[i].tagIndex,
-                (int)map->tagArray[i].tagLen, map->tagArray[i].tagStr,
-                (int)map->tagArray[i].fmtLen, map->tagArray[i].fmtStr,
-                map->tagArray[i - 1].tagIndex,
-                (int)map->tagArray[i - 1].tagLen, map->tagArray[i - 1].fmtStr,
-                (int)map->tagArray[i - 1].fmtLen, map->tagArray[i - 1].fmtStr);
-            errno = EMLINK;
-            return -1;
+    bool lineStart = true;
+    int lineNum = 1;
+    while (cp < endp) {
+        if (*cp == '\n') {
+            lineStart = true;
+            lineNum++;
+        } else if (lineStart) {
+            if (*cp == '#') {
+                // comment; just scan to end
+                lineStart = false;
+            } else if (isdigit(*cp)) {
+                // looks like a tag; scan it out
+                if (scanTagLine(map, &cp, lineNum) != 0) {
+                    return -1;
+                }
+                lineNum++;      // we eat the '\n'
+                // leave lineStart==true
+            } else if (isspace(*cp)) {
+                // looks like leading whitespace; keep scanning
+            } else {
+                fprintf(stderr,
+                        OUT_TAG ": unexpected chars (0x%02x) in tag number on line %d\n",
+                        *cp, lineNum);
+                errno = EINVAL;
+                return -1;
+            }
+        } else {
+            // this is a blank or comment line
         }
+        cp++;
     }
 
     return 0;
 }
+
+// Open the map file and allocate a structure to manage it.
+//
+// We create a private mapping because we want to terminate the log tag
+// strings with '\0'.
+LIBLOG_ABI_PUBLIC EventTagMap* android_openEventTagMap(const char* fileName) {
+    int save_errno;
+
+    const char* tagfile = fileName ? fileName : EVENT_TAG_MAP_FILE;
+    int fd = open(tagfile, O_RDONLY | O_CLOEXEC);
+    if (fd < 0) {
+        save_errno = errno;
+        fprintf(stderr, OUT_TAG ": unable to open map '%s': %s\n",
+                tagfile, strerror(save_errno));
+        errno = save_errno;
+        return NULL;
+    }
+    off_t end = lseek(fd, 0L, SEEK_END);
+    save_errno = errno;
+    (void)lseek(fd, 0L, SEEK_SET);
+    if (end < 0) {
+        fprintf(stderr, OUT_TAG ": unable to seek map '%s' %s\n",
+                tagfile, strerror(save_errno));
+        close(fd);
+        errno = save_errno;
+        return NULL;
+    }
+
+    EventTagMap* newTagMap = new EventTagMap;
+    if (newTagMap == NULL) {
+        save_errno = errno;
+        close(fd);
+        errno = save_errno;
+        return NULL;
+    }
+
+    newTagMap->mapAddr = mmap(NULL, end, PROT_READ | PROT_WRITE,
+                              MAP_PRIVATE, fd, 0);
+    save_errno = errno;
+    close(fd);
+    fd = -1;
+    if ((newTagMap->mapAddr == MAP_FAILED) || (newTagMap->mapAddr == NULL)) {
+        fprintf(stderr, OUT_TAG ": mmap(%s) failed: %s\n",
+                tagfile, strerror(save_errno));
+        delete newTagMap;
+        errno = save_errno;
+        return NULL;
+    }
+
+    newTagMap->mapLen = end;
+
+    if (parseMapLines(newTagMap) != 0) {
+        delete newTagMap;
+        return NULL;
+    }
+
+    return newTagMap;
+}
+
+// Close the map.
+LIBLOG_ABI_PUBLIC void android_closeEventTagMap(EventTagMap* map) {
+    if (map) delete map;
+}
+
+// Look up an entry in the map.
+LIBLOG_ABI_PUBLIC const char* android_lookupEventTag_len(const EventTagMap* map,
+                                                         size_t *len,
+                                                         unsigned int tag) {
+    if (len) *len = 0;
+    const TagFmt* str = map->find(tag);
+    if (!str) return NULL;
+    if (len) *len = str->first.length();
+    return str->first.data();
+}
+
+// Look up an entry in the map.
+LIBLOG_ABI_PUBLIC const char* android_lookupEventFormat_len(
+        const EventTagMap* map, size_t *len, unsigned int tag) {
+    if (len) *len = 0;
+    const TagFmt* str = map->find(tag);
+    if (!str) return NULL;
+    if (len) *len = str->second.length();
+    return str->second.data();
+}
+
+// This function is deprecated and replaced with android_lookupEventTag_len
+// since it will cause the map to change from Shared and backed by a file,
+// to Private Dirty and backed up by swap, albeit highly compressible. By
+// deprecating this function everywhere, we save 100s of MB of memory space.
+LIBLOG_ABI_PUBLIC const char* android_lookupEventTag(const EventTagMap* map,
+                                                     unsigned int tag) {
+    size_t len;
+    const char* tagStr = android_lookupEventTag_len(map, &len, tag);
+
+    if (!tagStr) return tagStr;
+    char* cp = const_cast<char*>(tagStr);
+    cp += len;
+    if (*cp) *cp = '\0'; // Trigger copy on write :-( and why deprecated.
+    return tagStr;
+}
diff --git a/liblog/legacy-ndk-includes/log.h b/liblog/legacy-ndk-includes/log.h
index 0ea4c29..d40d6fa 100644
--- a/liblog/legacy-ndk-includes/log.h
+++ b/liblog/legacy-ndk-includes/log.h
@@ -98,7 +98,7 @@
  */
 int __android_log_print(int prio, const char *tag,  const char *fmt, ...)
 #if defined(__GNUC__)
-    __attribute__ ((format(printf, 3, 4)))
+    __attribute__((__format__(printf, 3, 4)))
 #endif
     ;
 
@@ -116,8 +116,8 @@
 void __android_log_assert(const char *cond, const char *tag,
 			  const char *fmt, ...)    
 #if defined(__GNUC__)
-    __attribute__ ((noreturn))
-    __attribute__ ((format(printf, 3, 4)))
+    __attribute__((__noreturn__))
+    __attribute__((__format__(printf, 3, 4)))
 #endif
     ;
 
diff --git a/liblog/log_ratelimit.cpp b/liblog/log_ratelimit.cpp
new file mode 100644
index 0000000..dfd4b8f
--- /dev/null
+++ b/liblog/log_ratelimit.cpp
@@ -0,0 +1,86 @@
+/*
+** Copyright 2016, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+#include <errno.h>
+#include <pthread.h>
+#include <time.h>
+
+#include <log/log.h>
+
+#include "log_portability.h"
+
+// Global default if 'last' argument in __android_log_ratelimit is NULL
+static time_t g_last_clock;
+// Global above can not deal well with callers playing games with the
+// seconds argument, so we will also hold on to the maximum value
+// ever provided and use that to gain consistency.  If the caller
+// provides their own 'last' argument, then they can play such games
+// of varying the 'seconds' argument to their pleasure.
+static time_t g_last_seconds;
+static const time_t last_seconds_default = 10;
+static const time_t last_seconds_max = 24 * 60 * 60; // maximum of a day
+static const time_t last_seconds_min = 2; // granularity
+// Lock to protect last_clock and last_seconds, but also 'last'
+// argument (not NULL) as supplied to __android_log_ratelimit.
+static pthread_mutex_t lock_ratelimit = PTHREAD_MUTEX_INITIALIZER;
+
+// if last is NULL, caller _must_ provide a consistent value for
+// seconds, otherwise we will take the maximum ever issued and hold
+// on to that.  Preserves value of non-zero errno.  Return -1 if we
+// can not acquire a lock, 0 if we are not to log a message, and 1
+// if we are ok to log a message.  Caller should check > 0 for true.
+LIBLOG_ABI_PUBLIC int __android_log_ratelimit(time_t seconds, time_t* last) {
+    int save_errno = errno;
+
+    // Two reasons for trylock failure:
+    //   1. In a signal handler. Must prevent deadlock
+    //   2. Too many threads calling __android_log_ratelimit.
+    //      Bonus to not print if they race here because that
+    //      dovetails the goal of ratelimiting. One may print
+    //      and the others will wait their turn ...
+    if (pthread_mutex_trylock(&lock_ratelimit)) {
+        if (save_errno) errno = save_errno;
+        return -1;
+    }
+
+    if (seconds == 0) {
+        seconds = last_seconds_default;
+    } else if (seconds < last_seconds_min) {
+        seconds = last_seconds_min;
+    } else if (seconds > last_seconds_max) {
+        seconds = last_seconds_max;
+    }
+
+    if (!last) {
+        if (g_last_seconds > seconds) {
+            seconds = g_last_seconds;
+        } else if (g_last_seconds < seconds) {
+            g_last_seconds = seconds;
+        }
+        last = &g_last_clock;
+    }
+
+    time_t now = time(NULL);
+    if ((now == (time_t)-1) || ((*last + seconds) > now)) {
+        pthread_mutex_unlock(&lock_ratelimit);
+        if (save_errno) errno = save_errno;
+        return 0;
+    }
+    *last = now;
+    pthread_mutex_unlock(&lock_ratelimit);
+    if (save_errno) errno = save_errno;
+    return 1;
+}
diff --git a/liblog/pmsg_reader.c b/liblog/pmsg_reader.c
index a0a69c1..e1b81aa 100644
--- a/liblog/pmsg_reader.c
+++ b/liblog/pmsg_reader.c
@@ -70,7 +70,7 @@
 /* Determine the credentials of the caller */
 static bool uid_has_log_permission(uid_t uid)
 {
-    return (uid == AID_SYSTEM) || (uid == AID_LOG) || (uid == AID_ROOT);
+    return (uid == AID_SYSTEM) || (uid == AID_LOG) || (uid == AID_ROOT) || (uid == AID_LOGD);
 }
 
 static uid_t get_best_effective_uid()
diff --git a/liblog/log_is_loggable.c b/liblog/properties.c
similarity index 100%
rename from liblog/log_is_loggable.c
rename to liblog/properties.c
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index d80f8b2..25c4a63 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -214,6 +214,8 @@
     }
     return false;
 }
+
+bool tested__android_log_close;
 #endif
 
 TEST(liblog, __android_log_btwrite__android_logger_list_read) {
@@ -228,22 +230,33 @@
     // Check that we can close and reopen the logger
     log_time ts(CLOCK_MONOTONIC);
     ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
-    bool pmsgActiveAfter__android_log_btwrite = isPmsgActive();
-    bool logdwActiveAfter__android_log_btwrite = isLogdwActive();
-    EXPECT_TRUE(pmsgActiveAfter__android_log_btwrite);
-    EXPECT_TRUE(logdwActiveAfter__android_log_btwrite);
+    bool pmsgActiveAfter__android_log_btwrite;
+    bool logdwActiveAfter__android_log_btwrite;
+    if (getuid() == AID_ROOT) {
+        tested__android_log_close = true;
+        pmsgActiveAfter__android_log_btwrite = isPmsgActive();
+        logdwActiveAfter__android_log_btwrite = isLogdwActive();
+        EXPECT_TRUE(pmsgActiveAfter__android_log_btwrite);
+        EXPECT_TRUE(logdwActiveAfter__android_log_btwrite);
+    } else if (!tested__android_log_close) {
+        fprintf(stderr, "WARNING: can not test __android_log_close()\n");
+    }
     __android_log_close();
-    bool pmsgActiveAfter__android_log_close = isPmsgActive();
-    bool logdwActiveAfter__android_log_close = isLogdwActive();
-    EXPECT_FALSE(pmsgActiveAfter__android_log_close);
-    EXPECT_FALSE(logdwActiveAfter__android_log_close);
+    if (getuid() == AID_ROOT) {
+        bool pmsgActiveAfter__android_log_close = isPmsgActive();
+        bool logdwActiveAfter__android_log_close = isLogdwActive();
+        EXPECT_FALSE(pmsgActiveAfter__android_log_close);
+        EXPECT_FALSE(logdwActiveAfter__android_log_close);
+    }
 
     log_time ts1(CLOCK_MONOTONIC);
     ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts1, sizeof(ts1)));
-    pmsgActiveAfter__android_log_btwrite = isPmsgActive();
-    logdwActiveAfter__android_log_btwrite = isLogdwActive();
-    EXPECT_TRUE(pmsgActiveAfter__android_log_btwrite);
-    EXPECT_TRUE(logdwActiveAfter__android_log_btwrite);
+    if (getuid() == AID_ROOT) {
+        pmsgActiveAfter__android_log_btwrite = isPmsgActive();
+        logdwActiveAfter__android_log_btwrite = isLogdwActive();
+        EXPECT_TRUE(pmsgActiveAfter__android_log_btwrite);
+        EXPECT_TRUE(logdwActiveAfter__android_log_btwrite);
+    }
     usleep(1000000);
 
     int count = 0;
@@ -257,18 +270,19 @@
 
         ASSERT_EQ(log_msg.entry.pid, pid);
 
-        if ((log_msg.entry.len != (4 + 1 + 8))
+        if ((log_msg.entry.len != sizeof(android_log_event_long_t))
          || (log_msg.id() != LOG_ID_EVENTS)) {
             continue;
         }
 
-        char *eventData = log_msg.msg();
+        android_log_event_long_t* eventData;
+        eventData = reinterpret_cast<android_log_event_long_t*>(log_msg.msg());
 
-        if (!eventData || (eventData[4] != EVENT_TYPE_LONG)) {
+        if (!eventData || (eventData->payload.type != EVENT_TYPE_LONG)) {
             continue;
         }
 
-        log_time tx(eventData + 4 + 1);
+        log_time tx(reinterpret_cast<char*>(&eventData->payload.data));
         if (ts == tx) {
             ++count;
         } else if (ts1 == tx) {
@@ -339,18 +353,20 @@
 
         if ((log_msg.entry.sec < (ts.tv_sec - 1))
          || ((ts.tv_sec + 1) < log_msg.entry.sec)
-         || ((size_t)log_msg.entry.len != (4 + 1 + 4 + length))
+         || ((size_t)log_msg.entry.len != (sizeof(android_log_event_string_t) +
+                                           length))
          || (log_msg.id() != LOG_ID_EVENTS)) {
             continue;
         }
 
-        char *eventData = log_msg.msg();
+        android_log_event_string_t* eventData;
+        eventData = reinterpret_cast<android_log_event_string_t*>(log_msg.msg());
 
-        if (!eventData || (eventData[4] != EVENT_TYPE_STRING)) {
+        if (!eventData || (eventData->type != EVENT_TYPE_STRING)) {
             continue;
         }
 
-        size_t len = get4LE(eventData + 4 + 1);
+        size_t len = get4LE(reinterpret_cast<char*>(&eventData->length));
         if (len == total) {
             ++count;
 
@@ -611,6 +627,8 @@
         return;
     }
 
+    setuid(AID_SYSTEM); // only one that can read security buffer
+
     pid_t pid = getpid();
 
     ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
@@ -635,18 +653,19 @@
 
         ASSERT_EQ(log_msg.entry.pid, pid);
 
-        if ((log_msg.entry.len != (4 + 1 + 8))
+        if ((log_msg.entry.len != sizeof(android_log_event_long_t))
          || (log_msg.id() != LOG_ID_SECURITY)) {
             continue;
         }
 
-        char *eventData = log_msg.msg();
+        android_log_event_long_t* eventData;
+        eventData = reinterpret_cast<android_log_event_long_t*>(log_msg.msg());
 
-        if (!eventData || (eventData[4] != EVENT_TYPE_LONG)) {
+        if (!eventData || (eventData->payload.type != EVENT_TYPE_LONG)) {
             continue;
         }
 
-        log_time tx(eventData + 4 + 1);
+        log_time tx(reinterpret_cast<char*>(&eventData->payload.data));
         if (ts == tx) {
             ++count;
         }
@@ -771,25 +790,27 @@
 
         ASSERT_EQ(log_msg.entry.pid, pid);
 
-        if ((log_msg.entry.len != (4 + 1 + 8))
+        if ((log_msg.entry.len != sizeof(android_log_event_long_t))
          || (log_msg.id() != LOG_ID_EVENTS)) {
             continue;
         }
 
-        char *eventData = log_msg.msg();
+        android_log_event_long_t* eventData;
+        eventData = reinterpret_cast<android_log_event_long_t*>(log_msg.msg());
 
-        if (!eventData || (eventData[4] != EVENT_TYPE_LONG)) {
+        if (!eventData || (eventData->payload.type != EVENT_TYPE_LONG)) {
             continue;
         }
 
-        unsigned long long l = eventData[4 + 1 + 0] & 0xFF;
-        l |= (unsigned long long) (eventData[4 + 1 + 1] & 0xFF) << 8;
-        l |= (unsigned long long) (eventData[4 + 1 + 2] & 0xFF) << 16;
-        l |= (unsigned long long) (eventData[4 + 1 + 3] & 0xFF) << 24;
-        l |= (unsigned long long) (eventData[4 + 1 + 4] & 0xFF) << 32;
-        l |= (unsigned long long) (eventData[4 + 1 + 5] & 0xFF) << 40;
-        l |= (unsigned long long) (eventData[4 + 1 + 6] & 0xFF) << 48;
-        l |= (unsigned long long) (eventData[4 + 1 + 7] & 0xFF) << 56;
+        char* cp = reinterpret_cast<char*>(&eventData->payload.data);
+        unsigned long long l = cp[0] & 0xFF;
+        l |= (unsigned long long) (cp[1] & 0xFF) << 8;
+        l |= (unsigned long long) (cp[2] & 0xFF) << 16;
+        l |= (unsigned long long) (cp[3] & 0xFF) << 24;
+        l |= (unsigned long long) (cp[4] & 0xFF) << 32;
+        l |= (unsigned long long) (cp[5] & 0xFF) << 40;
+        l |= (unsigned long long) (cp[6] & 0xFF) << 48;
+        l |= (unsigned long long) (cp[7] & 0xFF) << 56;
 
         if (l == v) {
             ++signals;
@@ -928,25 +949,27 @@
 
         ASSERT_EQ(log_msg.entry.pid, pid);
 
-        if ((log_msg.entry.len != (4 + 1 + 8))
+        if ((log_msg.entry.len != sizeof(android_log_event_long_t))
          || (log_msg.id() != LOG_ID_EVENTS)) {
             continue;
         }
 
-        char *eventData = log_msg.msg();
+        android_log_event_long_t* eventData;
+        eventData = reinterpret_cast<android_log_event_long_t*>(log_msg.msg());
 
-        if (!eventData || (eventData[4] != EVENT_TYPE_LONG)) {
+        if (!eventData || (eventData->payload.type != EVENT_TYPE_LONG)) {
             continue;
         }
 
-        unsigned long long l = eventData[4 + 1 + 0] & 0xFF;
-        l |= (unsigned long long) (eventData[4 + 1 + 1] & 0xFF) << 8;
-        l |= (unsigned long long) (eventData[4 + 1 + 2] & 0xFF) << 16;
-        l |= (unsigned long long) (eventData[4 + 1 + 3] & 0xFF) << 24;
-        l |= (unsigned long long) (eventData[4 + 1 + 4] & 0xFF) << 32;
-        l |= (unsigned long long) (eventData[4 + 1 + 5] & 0xFF) << 40;
-        l |= (unsigned long long) (eventData[4 + 1 + 6] & 0xFF) << 48;
-        l |= (unsigned long long) (eventData[4 + 1 + 7] & 0xFF) << 56;
+        char* cp = reinterpret_cast<char*>(&eventData->payload.data);
+        unsigned long long l = cp[0] & 0xFF;
+        l |= (unsigned long long) (cp[1] & 0xFF) << 8;
+        l |= (unsigned long long) (cp[2] & 0xFF) << 16;
+        l |= (unsigned long long) (cp[3] & 0xFF) << 24;
+        l |= (unsigned long long) (cp[4] & 0xFF) << 32;
+        l |= (unsigned long long) (cp[5] & 0xFF) << 40;
+        l |= (unsigned long long) (cp[6] & 0xFF) << 48;
+        l |= (unsigned long long) (cp[7] & 0xFF) << 56;
 
         if (l == v) {
             ++signals;
@@ -1762,12 +1785,10 @@
 #endif
 }
 
-TEST(liblog, android_errorWriteWithInfoLog__android_logger_list_read__typical) {
 #ifdef __ANDROID__
-    const int TAG = 123456781;
-    const char SUBTAG[] = "test-subtag";
-    const int UID = -1;
-    const int DATA_LEN = 200;
+static void android_errorWriteWithInfoLog_helper(int TAG, const char* SUBTAG,
+                                                 int UID, const char* payload,
+                                                 int DATA_LEN, int& count) {
     struct logger_list *logger_list;
 
     pid_t pid = getpid();
@@ -1775,100 +1796,17 @@
     ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
         LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
 
-    ASSERT_LT(0, android_errorWriteWithInfoLog(
-            TAG, SUBTAG, UID, max_payload_buf, DATA_LEN));
-
-    sleep(2);
-
-    int count = 0;
-
-    for (;;) {
-        log_msg log_msg;
-        if (android_logger_list_read(logger_list, &log_msg) <= 0) {
-            break;
-        }
-
-        char *eventData = log_msg.msg();
-        if (!eventData) {
-            continue;
-        }
-
-        // Tag
-        int tag = get4LE(eventData);
-        eventData += 4;
-
-        if (tag != TAG) {
-            continue;
-        }
-
-        // List type
-        ASSERT_EQ(EVENT_TYPE_LIST, eventData[0]);
-        eventData++;
-
-        // Number of elements in list
-        ASSERT_EQ(3, eventData[0]);
-        eventData++;
-
-        // Element #1: string type for subtag
-        ASSERT_EQ(EVENT_TYPE_STRING, eventData[0]);
-        eventData++;
-
-        ASSERT_EQ((int) strlen(SUBTAG), get4LE(eventData));
-        eventData +=4;
-
-        if (memcmp(SUBTAG, eventData, strlen(SUBTAG))) {
-            continue;
-        }
-        eventData += strlen(SUBTAG);
-
-        // Element #2: int type for uid
-        ASSERT_EQ(EVENT_TYPE_INT, eventData[0]);
-        eventData++;
-
-        ASSERT_EQ(UID, get4LE(eventData));
-        eventData += 4;
-
-        // Element #3: string type for data
-        ASSERT_EQ(EVENT_TYPE_STRING, eventData[0]);
-        eventData++;
-
-        ASSERT_EQ(DATA_LEN, get4LE(eventData));
-        eventData += 4;
-
-        if (memcmp(max_payload_buf, eventData, DATA_LEN)) {
-            continue;
-        }
-
-        ++count;
+    int retval_android_errorWriteWithinInfoLog = android_errorWriteWithInfoLog(
+            TAG, SUBTAG, UID, payload, DATA_LEN);
+    if (payload) {
+        ASSERT_LT(0, retval_android_errorWriteWithinInfoLog);
+    } else {
+        ASSERT_GT(0, retval_android_errorWriteWithinInfoLog);
     }
 
-    EXPECT_EQ(1, count);
-
-    android_logger_list_close(logger_list);
-#else
-    GTEST_LOG_(INFO) << "This test does nothing.\n";
-#endif
-}
-
-TEST(liblog, android_errorWriteWithInfoLog__android_logger_list_read__data_too_large) {
-#ifdef __ANDROID__
-    const int TAG = 123456782;
-    const char SUBTAG[] = "test-subtag";
-    const int UID = -1;
-    const int DATA_LEN = sizeof(max_payload_buf);
-    struct logger_list *logger_list;
-
-    pid_t pid = getpid();
-
-    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
-        LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
-
-    ASSERT_LT(0, android_errorWriteWithInfoLog(
-            TAG, SUBTAG, UID, max_payload_buf, DATA_LEN));
-
     sleep(2);
 
-    int count = 0;
+    count = 0;
 
     for (;;) {
         log_msg log_msg;
@@ -1891,6 +1829,12 @@
             continue;
         }
 
+        if (!payload) {
+            // This tag should not have been written because the data was null
+            ++count;
+            break;
+        }
+
         // List type
         ASSERT_EQ(EVENT_TYPE_LIST, eventData[0]);
         eventData++;
@@ -1903,13 +1847,15 @@
         ASSERT_EQ(EVENT_TYPE_STRING, eventData[0]);
         eventData++;
 
-        ASSERT_EQ((int) strlen(SUBTAG), get4LE(eventData));
-        eventData +=4;
+        int subtag_len = strlen(SUBTAG);
+        if (subtag_len > 32) subtag_len = 32;
+        ASSERT_EQ(subtag_len, get4LE(eventData));
+        eventData += 4;
 
-        if (memcmp(SUBTAG, eventData, strlen(SUBTAG))) {
+        if (memcmp(SUBTAG, eventData, subtag_len)) {
             continue;
         }
-        eventData += strlen(SUBTAG);
+        eventData += subtag_len;
 
         // Element #2: int type for uid
         ASSERT_EQ(EVENT_TYPE_INT, eventData[0]);
@@ -1924,22 +1870,53 @@
 
         size_t dataLen = get4LE(eventData);
         eventData += 4;
+        if (DATA_LEN < 512) ASSERT_EQ(DATA_LEN, (int)dataLen);
 
-        if (memcmp(max_payload_buf, eventData, dataLen)) {
+        if (memcmp(payload, eventData, dataLen)) {
             continue;
         }
-        eventData += dataLen;
 
-        // 4 bytes for the tag, and max_payload_buf should be truncated.
-        ASSERT_LE(4 + 512, eventData - original);      // worst expectations
-        ASSERT_GT(4 + DATA_LEN, eventData - original); // must be truncated
+        if (DATA_LEN >= 512) {
+            eventData += dataLen;
+            // 4 bytes for the tag, and max_payload_buf should be truncated.
+            ASSERT_LE(4 + 512, eventData - original);      // worst expectations
+            ASSERT_GT(4 + DATA_LEN, eventData - original); // must be truncated
+        }
 
         ++count;
     }
 
-    EXPECT_EQ(1, count);
-
     android_logger_list_close(logger_list);
+}
+#endif
+
+TEST(liblog, android_errorWriteWithInfoLog__android_logger_list_read__typical) {
+#ifdef __ANDROID__
+    int count;
+    android_errorWriteWithInfoLog_helper(
+            123456781,
+            "test-subtag",
+            -1,
+            max_payload_buf,
+            200,
+            count);
+    EXPECT_EQ(1, count);
+#else
+    GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(liblog, android_errorWriteWithInfoLog__android_logger_list_read__data_too_large) {
+#ifdef __ANDROID__
+    int count;
+    android_errorWriteWithInfoLog_helper(
+            123456782,
+            "test-subtag",
+            -1,
+            max_payload_buf,
+            sizeof(max_payload_buf),
+            count);
+    EXPECT_EQ(1, count);
 #else
     GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
@@ -1947,49 +1924,15 @@
 
 TEST(liblog, android_errorWriteWithInfoLog__android_logger_list_read__null_data) {
 #ifdef __ANDROID__
-    const int TAG = 123456783;
-    const char SUBTAG[] = "test-subtag";
-    const int UID = -1;
-    const int DATA_LEN = 200;
-    struct logger_list *logger_list;
-
-    pid_t pid = getpid();
-
-    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
-        LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
-
-    ASSERT_GT(0, android_errorWriteWithInfoLog(
-            TAG, SUBTAG, UID, NULL, DATA_LEN));
-
-    sleep(2);
-
-    int count = 0;
-
-    for (;;) {
-        log_msg log_msg;
-        if (android_logger_list_read(logger_list, &log_msg) <= 0) {
-            break;
-        }
-
-        char *eventData = log_msg.msg();
-        if (!eventData) {
-            continue;
-        }
-
-        // Tag
-        int tag = get4LE(eventData);
-        eventData += 4;
-
-        if (tag == TAG) {
-            // This tag should not have been written because the data was null
-            count++;
-            break;
-        }
-    }
-
+    int count;
+    android_errorWriteWithInfoLog_helper(
+            123456783,
+            "test-subtag",
+            -1,
+            NULL,
+            200,
+            count);
     EXPECT_EQ(0, count);
-
-    android_logger_list_close(logger_list);
 #else
     GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
@@ -1997,88 +1940,15 @@
 
 TEST(liblog, android_errorWriteWithInfoLog__android_logger_list_read__subtag_too_long) {
 #ifdef __ANDROID__
-    const int TAG = 123456784;
-    const char SUBTAG[] = "abcdefghijklmnopqrstuvwxyz now i know my abc";
-    const int UID = -1;
-    const int DATA_LEN = 200;
-    struct logger_list *logger_list;
-
-    pid_t pid = getpid();
-
-    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
-        LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
-
-    ASSERT_LT(0, android_errorWriteWithInfoLog(
-            TAG, SUBTAG, UID, max_payload_buf, DATA_LEN));
-
-    sleep(2);
-
-    int count = 0;
-
-    for (;;) {
-        log_msg log_msg;
-        if (android_logger_list_read(logger_list, &log_msg) <= 0) {
-            break;
-        }
-
-        char *eventData = log_msg.msg();
-        if (!eventData) {
-            continue;
-        }
-
-        // Tag
-        int tag = get4LE(eventData);
-        eventData += 4;
-
-        if (tag != TAG) {
-            continue;
-        }
-
-        // List type
-        ASSERT_EQ(EVENT_TYPE_LIST, eventData[0]);
-        eventData++;
-
-        // Number of elements in list
-        ASSERT_EQ(3, eventData[0]);
-        eventData++;
-
-        // Element #1: string type for subtag
-        ASSERT_EQ(EVENT_TYPE_STRING, eventData[0]);
-        eventData++;
-
-        // The subtag is longer than 32 and should be truncated to that.
-        ASSERT_EQ(32, get4LE(eventData));
-        eventData +=4;
-
-        if (memcmp(SUBTAG, eventData, 32)) {
-            continue;
-        }
-        eventData += 32;
-
-        // Element #2: int type for uid
-        ASSERT_EQ(EVENT_TYPE_INT, eventData[0]);
-        eventData++;
-
-        ASSERT_EQ(UID, get4LE(eventData));
-        eventData += 4;
-
-        // Element #3: string type for data
-        ASSERT_EQ(EVENT_TYPE_STRING, eventData[0]);
-        eventData++;
-
-        ASSERT_EQ(DATA_LEN, get4LE(eventData));
-        eventData += 4;
-
-        if (memcmp(max_payload_buf, eventData, DATA_LEN)) {
-            continue;
-        }
-
-        ++count;
-    }
-
+    int count;
+    android_errorWriteWithInfoLog_helper(
+            123456784,
+            "abcdefghijklmnopqrstuvwxyz now i know my abc",
+            -1,
+            max_payload_buf,
+            200,
+            count);
     EXPECT_EQ(1, count);
-
-    android_logger_list_close(logger_list);
 #else
     GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
@@ -2092,10 +1962,8 @@
     buf_write_test(max_payload_buf);
 }
 
-TEST(liblog, android_errorWriteLog__android_logger_list_read__success) {
 #ifdef __ANDROID__
-    const int TAG = 123456785;
-    const char SUBTAG[] = "test-subtag";
+static void android_errorWriteLog_helper(int TAG, const char *SUBTAG, int& count) {
     struct logger_list *logger_list;
 
     pid_t pid = getpid();
@@ -2103,11 +1971,16 @@
     ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
         LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
 
-    ASSERT_LT(0, android_errorWriteLog(TAG, SUBTAG));
+    int retval_android_errorWriteLog = android_errorWriteLog(TAG, SUBTAG);
+    if (SUBTAG) {
+        ASSERT_LT(0, retval_android_errorWriteLog);
+    } else {
+        ASSERT_GT(0, retval_android_errorWriteLog);
+    }
 
     sleep(2);
 
-    int count = 0;
+    count = 0;
 
     for (;;) {
         log_msg log_msg;
@@ -2128,6 +2001,12 @@
             continue;
         }
 
+        if (!SUBTAG) {
+            // This tag should not have been written because the data was null
+            ++count;
+            break;
+        }
+
         // List type
         ASSERT_EQ(EVENT_TYPE_LIST, eventData[0]);
         eventData++;
@@ -2149,9 +2028,15 @@
         ++count;
     }
 
-    EXPECT_EQ(1, count);
-
     android_logger_list_close(logger_list);
+}
+#endif
+
+TEST(liblog, android_errorWriteLog__android_logger_list_read__success) {
+#ifdef __ANDROID__
+    int count;
+    android_errorWriteLog_helper(123456785, "test-subtag", count);
+    EXPECT_EQ(1, count);
 #else
     GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
@@ -2159,45 +2044,9 @@
 
 TEST(liblog, android_errorWriteLog__android_logger_list_read__null_subtag) {
 #ifdef __ANDROID__
-    const int TAG = 123456786;
-    struct logger_list *logger_list;
-
-    pid_t pid = getpid();
-
-    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
-        LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
-
-    ASSERT_GT(0, android_errorWriteLog(TAG, NULL));
-
-    sleep(2);
-
-    int count = 0;
-
-    for (;;) {
-        log_msg log_msg;
-        if (android_logger_list_read(logger_list, &log_msg) <= 0) {
-            break;
-        }
-
-        char *eventData = log_msg.msg();
-        if (!eventData) {
-            continue;
-        }
-
-        // Tag
-        int tag = get4LE(eventData);
-        eventData += 4;
-
-        if (tag == TAG) {
-            // This tag should not have been written because the data was null
-            count++;
-            break;
-        }
-    }
-
+    int count;
+    android_errorWriteLog_helper(123456786, NULL, count);
     EXPECT_EQ(0, count);
-
-    android_logger_list_close(logger_list);
 #else
     GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
@@ -2835,10 +2684,15 @@
 TEST(liblog, __android_log_pmsg_file_write) {
 #ifdef __ANDROID__
     __android_log_close();
-    bool pmsgActiveAfter__android_log_close = isPmsgActive();
-    bool logdwActiveAfter__android_log_close = isLogdwActive();
-    EXPECT_FALSE(pmsgActiveAfter__android_log_close);
-    EXPECT_FALSE(logdwActiveAfter__android_log_close);
+    if (getuid() == AID_ROOT) {
+        tested__android_log_close = true;
+        bool pmsgActiveAfter__android_log_close = isPmsgActive();
+        bool logdwActiveAfter__android_log_close = isLogdwActive();
+        EXPECT_FALSE(pmsgActiveAfter__android_log_close);
+        EXPECT_FALSE(logdwActiveAfter__android_log_close);
+    } else if (!tested__android_log_close) {
+        fprintf(stderr, "WARNING: can not test __android_log_close()\n");
+    }
     int return__android_log_pmsg_file_write = __android_log_pmsg_file_write(
             LOG_ID_CRASH, ANDROID_LOG_VERBOSE,
             __pmsg_file, max_payload_buf, sizeof(max_payload_buf));
@@ -2852,24 +2706,32 @@
                         "with liblog.__android_log_msg_file_read test\n",
                         __pmsg_file);
     }
-    bool pmsgActiveAfter__android_pmsg_file_write = isPmsgActive();
-    bool logdwActiveAfter__android_pmsg_file_write = isLogdwActive();
-    EXPECT_FALSE(pmsgActiveAfter__android_pmsg_file_write);
-    EXPECT_FALSE(logdwActiveAfter__android_pmsg_file_write);
+    bool pmsgActiveAfter__android_pmsg_file_write;
+    bool logdwActiveAfter__android_pmsg_file_write;
+    if (getuid() == AID_ROOT) {
+        pmsgActiveAfter__android_pmsg_file_write = isPmsgActive();
+        logdwActiveAfter__android_pmsg_file_write = isLogdwActive();
+        EXPECT_FALSE(pmsgActiveAfter__android_pmsg_file_write);
+        EXPECT_FALSE(logdwActiveAfter__android_pmsg_file_write);
+    }
     EXPECT_LT(0, __android_log_buf_print(LOG_ID_MAIN, ANDROID_LOG_INFO,
                                          "TEST__android_log_pmsg_file_write",
                                          "main"));
-    bool pmsgActiveAfter__android_log_buf_print = isPmsgActive();
-    bool logdwActiveAfter__android_log_buf_print = isLogdwActive();
-    EXPECT_TRUE(pmsgActiveAfter__android_log_buf_print);
-    EXPECT_TRUE(logdwActiveAfter__android_log_buf_print);
+    if (getuid() == AID_ROOT) {
+        bool pmsgActiveAfter__android_log_buf_print = isPmsgActive();
+        bool logdwActiveAfter__android_log_buf_print = isLogdwActive();
+        EXPECT_TRUE(pmsgActiveAfter__android_log_buf_print);
+        EXPECT_TRUE(logdwActiveAfter__android_log_buf_print);
+    }
     EXPECT_LT(0, __android_log_pmsg_file_write(
             LOG_ID_CRASH, ANDROID_LOG_VERBOSE,
             __pmsg_file, max_payload_buf, sizeof(max_payload_buf)));
-    pmsgActiveAfter__android_pmsg_file_write = isPmsgActive();
-    logdwActiveAfter__android_pmsg_file_write = isLogdwActive();
-    EXPECT_TRUE(pmsgActiveAfter__android_pmsg_file_write);
-    EXPECT_TRUE(logdwActiveAfter__android_pmsg_file_write);
+    if (getuid() == AID_ROOT) {
+        pmsgActiveAfter__android_pmsg_file_write = isPmsgActive();
+        logdwActiveAfter__android_pmsg_file_write = isLogdwActive();
+        EXPECT_TRUE(pmsgActiveAfter__android_pmsg_file_write);
+        EXPECT_TRUE(logdwActiveAfter__android_pmsg_file_write);
+    }
 #else
     GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
@@ -2904,19 +2766,26 @@
     signaled = 0;
 
     __android_log_close();
-    bool pmsgActiveAfter__android_log_close = isPmsgActive();
-    bool logdwActiveAfter__android_log_close = isLogdwActive();
-    EXPECT_FALSE(pmsgActiveAfter__android_log_close);
-    EXPECT_FALSE(logdwActiveAfter__android_log_close);
+    if (getuid() == AID_ROOT) {
+        tested__android_log_close = true;
+        bool pmsgActiveAfter__android_log_close = isPmsgActive();
+        bool logdwActiveAfter__android_log_close = isLogdwActive();
+        EXPECT_FALSE(pmsgActiveAfter__android_log_close);
+        EXPECT_FALSE(logdwActiveAfter__android_log_close);
+    } else if (!tested__android_log_close) {
+        fprintf(stderr, "WARNING: can not test __android_log_close()\n");
+    }
 
     ssize_t ret = __android_log_pmsg_file_read(
             LOG_ID_CRASH, ANDROID_LOG_VERBOSE,
             __pmsg_file, __pmsg_fn, NULL);
 
-    bool pmsgActiveAfter__android_log_pmsg_file_read = isPmsgActive();
-    bool logdwActiveAfter__android_log_pmsg_file_read = isLogdwActive();
-    EXPECT_FALSE(pmsgActiveAfter__android_log_pmsg_file_read);
-    EXPECT_FALSE(logdwActiveAfter__android_log_pmsg_file_read);
+    if (getuid() == AID_ROOT) {
+        bool pmsgActiveAfter__android_log_pmsg_file_read = isPmsgActive();
+        bool logdwActiveAfter__android_log_pmsg_file_read = isLogdwActive();
+        EXPECT_FALSE(pmsgActiveAfter__android_log_pmsg_file_read);
+        EXPECT_FALSE(logdwActiveAfter__android_log_pmsg_file_read);
+    }
 
     if (ret == -ENOENT) {
         fprintf(stderr,
@@ -3043,3 +2912,35 @@
     GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
 }
+
+TEST(liblog, __android_log_ratelimit) {
+    time_t state = 0;
+
+    errno = 42;
+    // Prime
+    __android_log_ratelimit(3, &state);
+    EXPECT_EQ(errno, 42);
+    // Check
+    EXPECT_FALSE(__android_log_ratelimit(3, &state));
+    sleep(1);
+    EXPECT_FALSE(__android_log_ratelimit(3, &state));
+    sleep(4);
+    EXPECT_TRUE(__android_log_ratelimit(3, &state));
+    sleep(5);
+    EXPECT_TRUE(__android_log_ratelimit(3, &state));
+
+    // API checks
+    IF_ALOG_RATELIMIT_LOCAL(3, &state) {
+        EXPECT_FALSE(0 != "IF_ALOG_RATELIMIT_LOCAL(3, &state)");
+    }
+
+    IF_ALOG_RATELIMIT() {
+        ;
+    } else {
+        EXPECT_TRUE(0 == "IF_ALOG_RATELIMIT()");
+    }
+    IF_ALOG_RATELIMIT() {
+        EXPECT_FALSE(0 != "IF_ALOG_RATELIMIT()");
+    }
+    // Do not test default seconds, to allow liblog to tune freely
+}
diff --git a/libnativeloader/Android.bp b/libnativeloader/Android.bp
index 9d33899..c1133fb 100644
--- a/libnativeloader/Android.bp
+++ b/libnativeloader/Android.bp
@@ -9,8 +9,8 @@
         "liblog",
         "libcutils",
         "libnativebridge",
+        "libbase",
     ],
-    static_libs: ["libbase"],
     target: {
         android: {
             shared_libs: ["libdl"],
diff --git a/libsync/sync.c b/libsync/sync.c
index 169dc36..6281b20 100644
--- a/libsync/sync.c
+++ b/libsync/sync.c
@@ -21,8 +21,6 @@
 #include <stdint.h>
 #include <string.h>
 
-#include <linux/sw_sync.h>
-
 #include <sys/ioctl.h>
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -42,6 +40,16 @@
 #define SYNC_IOC_MERGE _IOWR(SYNC_IOC_MAGIC, 1, struct sync_merge_data)
 #define SYNC_IOC_FENCE_INFO _IOWR(SYNC_IOC_MAGIC, 2, struct sync_fence_info_data)
 
+struct sw_sync_create_fence_data {
+  __u32 value;
+  char name[32];
+  __s32 fence;
+};
+
+#define SW_SYNC_IOC_MAGIC 'W'
+#define SW_SYNC_IOC_CREATE_FENCE _IOWR(SW_SYNC_IOC_MAGIC, 0, struct sw_sync_create_fence_data)
+#define SW_SYNC_IOC_INC _IOW(SW_SYNC_IOC_MAGIC, 1, __u32)
+
 int sync_wait(int fd, int timeout)
 {
     __s32 to = timeout;
diff --git a/libziparchive/Android.bp b/libziparchive/Android.bp
index fce1378..44daf36 100644
--- a/libziparchive/Android.bp
+++ b/libziparchive/Android.bp
@@ -20,6 +20,7 @@
         "-DZLIB_CONST",
         "-Werror",
         "-Wall",
+        "-D_FILE_OFFSET_BITS=64",
     ],
     cppflags: [
         "-Wold-style-cast",
@@ -56,11 +57,13 @@
     name: "libziparchive",
     host_supported: true,
     defaults: ["libziparchive_defaults", "libziparchive_flags"],
-    static_libs: ["libutils"],
     shared_libs: ["liblog", "libbase"],
     target: {
         android: {
-            static_libs: ["libz"],
+            shared_libs: ["libz", "libutils"],
+        },
+        host: {
+            static_libs: ["libutils"],
         },
         linux_bionic: {
             static_libs: ["libz"],
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index 11cffe6..725d76e 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -451,11 +451,12 @@
       "logcat -v brief -b radio,events,bogo,system,main -g 2>/dev/null"));
 }
 
-static void caught_blocking(int /*signum*/)
+static void caught_blocking(int signum)
 {
     unsigned long long v = 0xDEADBEEFA55A0000ULL;
 
     v += getpid() & 0xFFFF;
+    if (signum == 0) ++v;
 
     LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v)));
 }
@@ -520,11 +521,12 @@
     EXPECT_EQ(1, signals);
 }
 
-static void caught_blocking_tail(int /*signum*/)
+static void caught_blocking_tail(int signum)
 {
     unsigned long long v = 0xA55ADEADBEEF0000ULL;
 
     v += getpid() & 0xFFFF;
+    if (signum == 0) ++v;
 
     LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v)));
 }
@@ -955,10 +957,11 @@
                      " -n 256 -r 1024"));
 }
 
-static void caught_blocking_clear(int /*signum*/) {
+static void caught_blocking_clear(int signum) {
     unsigned long long v = 0xDEADBEEFA55C0000ULL;
 
     v += getpid() & 0xFFFF;
+    if (signum == 0) ++v;
 
     LOG_FAILURE_RETRY(__android_log_btwrite(0, EVENT_TYPE_LONG, &v, sizeof(v)));
 }
diff --git a/logd/Android.mk b/logd/Android.mk
index 7fe48d7..2da9782 100644
--- a/logd/Android.mk
+++ b/logd/Android.mk
@@ -40,6 +40,7 @@
 #  event_flag += $(call event_logtags,logd)
 # so make sure we do not regret hard-coding it as follows:
 event_flag := -DAUDITD_LOG_TAG=1003 -DCHATTY_LOG_TAG=1004
+event_flag += -DLIBLOG_LOG_TAG=1006
 
 LOCAL_CFLAGS := -Werror $(event_flag)
 
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index c3ccd84..92d957f 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -34,6 +34,7 @@
 #include "LogBuffer.h"
 #include "LogKlog.h"
 #include "LogReader.h"
+#include "LogUtils.h"
 
 #define KMSG_PRIORITY(PRI)                          \
     '<',                                            \
@@ -42,23 +43,70 @@
     '>'
 
 LogAudit::LogAudit(LogBuffer *buf, LogReader *reader, int fdDmesg) :
-        SocketListener(getLogSocket(), false),
+        SocketListener(mSock = getLogSocket(), false),
         logbuf(buf),
         reader(reader),
         fdDmesg(fdDmesg),
-        initialized(false) {
+        main(__android_logger_property_get_bool("ro.logd.auditd.main",
+                                                BOOL_DEFAULT_TRUE)),
+        events(__android_logger_property_get_bool("ro.logd.auditd.events",
+                                                  BOOL_DEFAULT_TRUE)),
+        initialized(false),
+        tooFast(false) {
     static const char auditd_message[] = { KMSG_PRIORITY(LOG_INFO),
         'l', 'o', 'g', 'd', '.', 'a', 'u', 'd', 'i', 't', 'd', ':',
         ' ', 's', 't', 'a', 'r', 't', '\n' };
     write(fdDmesg, auditd_message, sizeof(auditd_message));
 }
 
+void LogAudit::checkRateLimit() {
+
+    // trim list for AUDIT_RATE_LIMIT_BURST_DURATION of history
+    log_time oldest(AUDIT_RATE_LIMIT_BURST_DURATION, 0);
+    bucket.emplace(android_log_clockid());
+    oldest = bucket.back() - oldest;
+    while (bucket.front() < oldest) bucket.pop();
+
+    static const size_t upperThreshold =
+        ((AUDIT_RATE_LIMIT_BURST_DURATION *
+          (AUDIT_RATE_LIMIT_DEFAULT + AUDIT_RATE_LIMIT_MAX)) + 1) /
+                              2;
+    if (bucket.size() >= upperThreshold) {
+        // Hit peak, slow down source
+        if (!tooFast) {
+            tooFast = true;
+            audit_rate_limit(mSock, AUDIT_RATE_LIMIT_MAX);
+        }
+
+        // We do not need to hold on to the full set of timing data history,
+        // let's ensure it does not grow without bounds.  This also ensures
+        // that std::dequeue underneath behaves almost like a ring buffer.
+        do {
+            bucket.pop();
+        } while (bucket.size() >= upperThreshold);
+        return;
+    }
+
+    if (!tooFast) return;
+
+    static const size_t lowerThreshold = AUDIT_RATE_LIMIT_BURST_DURATION *
+                                         AUDIT_RATE_LIMIT_MAX;
+
+    if (bucket.size() >= lowerThreshold) return;
+
+    tooFast = false;
+    // Went below max sustained rate, allow source to speed up
+    audit_rate_limit(mSock, AUDIT_RATE_LIMIT_DEFAULT);
+}
+
 bool LogAudit::onDataAvailable(SocketClient *cli) {
     if (!initialized) {
         prctl(PR_SET_NAME, "logd.auditd");
         initialized = true;
     }
 
+    checkRateLimit();
+
     struct audit_message rep;
 
     rep.nlh.nlmsg_type = 0;
@@ -70,8 +118,7 @@
         return false;
     }
 
-    logPrint("type=%d %.*s",
-        rep.nlh.nlmsg_type, rep.nlh.nlmsg_len, rep.data);
+    logPrint("type=%d %.*s", rep.nlh.nlmsg_type, rep.nlh.nlmsg_len, rep.data);
 
     return true;
 }
@@ -93,6 +140,13 @@
     }
 
     char *cp;
+    // Work around kernels missing
+    // https://github.com/torvalds/linux/commit/b8f89caafeb55fba75b74bea25adc4e4cd91be67
+    // Such kernels improperly add newlines inside audit messages.
+    while ((cp = strchr(str, '\n'))) {
+        *cp = ' ';
+    }
+
     while ((cp = strstr(str, "  "))) {
         memmove(cp, cp + 1, strlen(cp + 1) + 1);
     }
@@ -117,7 +171,8 @@
             if (avcl) {
                 char *avcr = strstr(str, avc);
 
-                skip = avcr && !strcmp(avcl + strlen(avc), avcr + strlen(avc));
+                skip = avcr && !fastcmp<strcmp>(avcl + strlen(avc),
+                                                avcr + strlen(avc));
                 if (skip) {
                     ++count;
                     free(last_str);
@@ -170,6 +225,11 @@
         }
     }
 
+    if (!main && !events) {
+        free(str);
+        return 0;
+    }
+
     pid_t pid = getpid();
     pid_t tid = gettid();
     uid_t uid = AID_LOGD;
@@ -220,7 +280,7 @@
 
     bool notify = false;
 
-    {   // begin scope for event buffer
+    if (events) {   // begin scope for event buffer
         uint32_t buffer[(n + sizeof(uint32_t) - 1) / sizeof(uint32_t)];
 
         android_log_event_string_t *event
@@ -275,7 +335,7 @@
     size_t e = strnlen(ecomm, LOGGER_ENTRY_MAX_PAYLOAD - b);
     n = b + e + l + 2;
 
-    {   // begin scope for main buffer
+    if (main) {   // begin scope for main buffer
         char newstr[n];
 
         *newstr = info ? ANDROID_LOG_INFO : ANDROID_LOG_WARN;
@@ -333,5 +393,6 @@
         audit_close(fd);
         fd = -1;
     }
+    (void)audit_rate_limit(fd, AUDIT_RATE_LIMIT_DEFAULT);
     return fd;
 }
diff --git a/logd/LogAudit.h b/logd/LogAudit.h
index ab30e28..045d631 100644
--- a/logd/LogAudit.h
+++ b/logd/LogAudit.h
@@ -17,6 +17,8 @@
 #ifndef _LOGD_LOG_AUDIT_H__
 #define _LOGD_LOG_AUDIT_H__
 
+#include <queue>
+
 #include <sysutils/SocketListener.h>
 
 #include "LogBuffer.h"
@@ -26,9 +28,16 @@
 class LogAudit : public SocketListener {
     LogBuffer *logbuf;
     LogReader *reader;
-    int fdDmesg;
+    int fdDmesg; // fdDmesg >= 0 is functionally bool dmesg
+    bool main;
+    bool events;
     bool initialized;
 
+    bool tooFast;
+    int mSock;
+    std::queue<log_time> bucket;
+    void checkRateLimit();
+
 public:
     LogAudit(LogBuffer *buf, LogReader *reader, int fdDmesg);
     int log(char *buf, size_t len);
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index a009433..cc65d47 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -17,6 +17,7 @@
 //#define DEBUG_CHECK_FOR_STALE_ENTRIES
 
 #include <ctype.h>
+#include <endian.h>
 #include <errno.h>
 #include <stdio.h>
 #include <string.h>
@@ -33,6 +34,7 @@
 #include "LogBuffer.h"
 #include "LogKlog.h"
 #include "LogReader.h"
+#include "LogUtils.h"
 
 #ifndef __predict_false
 #define __predict_false(exp) __builtin_expect((exp) != 0, 0)
@@ -110,9 +112,81 @@
         mTimes(*times) {
     pthread_mutex_init(&mLogElementsLock, NULL);
 
+    log_id_for_each(i) {
+        lastLoggedElements[i] = NULL;
+        droppedElements[i] = NULL;
+    }
+
     init();
 }
 
+LogBuffer::~LogBuffer() {
+    log_id_for_each(i) {
+        delete lastLoggedElements[i];
+        delete droppedElements[i];
+    }
+}
+
+enum match_type {
+    DIFFERENT,
+    SAME,
+    SAME_LIBLOG
+};
+
+static enum match_type identical(LogBufferElement* elem, LogBufferElement* last) {
+    // is it mostly identical?
+//  if (!elem) return DIFFERENT;
+    unsigned short lenl = elem->getMsgLen();
+    if (!lenl) return DIFFERENT;
+//  if (!last) return DIFFERENT;
+    unsigned short lenr = last->getMsgLen();
+    if (!lenr) return DIFFERENT;
+//  if (elem->getLogId() != last->getLogId()) return DIFFERENT;
+    if (elem->getUid() != last->getUid()) return DIFFERENT;
+    if (elem->getPid() != last->getPid()) return DIFFERENT;
+    if (elem->getTid() != last->getTid()) return DIFFERENT;
+
+    // last is more than a minute old, stop squashing identical messages
+    if (elem->getRealTime().nsec() >
+        (last->getRealTime().nsec() + 60 * NS_PER_SEC)) return DIFFERENT;
+
+    // Identical message
+    const char* msgl = elem->getMsg();
+    const char* msgr = last->getMsg();
+    if (lenl == lenr) {
+        if (!fastcmp<memcmp>(msgl, msgr, lenl)) return SAME;
+        // liblog tagged messages (content gets summed)
+        if ((elem->getLogId() == LOG_ID_EVENTS) &&
+            (lenl == sizeof(android_log_event_int_t)) &&
+            !fastcmp<memcmp>(msgl, msgr,
+                             sizeof(android_log_event_int_t) - sizeof(int32_t)) &&
+            (elem->getTag() == LIBLOG_LOG_TAG)) return SAME_LIBLOG;
+    }
+
+    // audit message (except sequence number) identical?
+    static const char avc[] = "): avc: ";
+
+    if (last->isBinary()) {
+        if (fastcmp<memcmp>(msgl, msgr,
+                            sizeof(android_log_event_string_t) -
+                                sizeof(int32_t))) return DIFFERENT;
+        msgl += sizeof(android_log_event_string_t);
+        lenl -= sizeof(android_log_event_string_t);
+        msgr += sizeof(android_log_event_string_t);
+        lenr -= sizeof(android_log_event_string_t);
+    }
+    const char *avcl = android::strnstr(msgl, lenl, avc);
+    if (!avcl) return DIFFERENT;
+    lenl -= avcl - msgl;
+    const char *avcr = android::strnstr(msgr, lenr, avc);
+    if (!avcr) return DIFFERENT;
+    lenr -= avcr - msgr;
+    if (lenl != lenr) return DIFFERENT;
+    if (fastcmp<memcmp>(avcl + strlen(avc),
+                        avcr + strlen(avc), lenl)) return DIFFERENT;
+    return SAME;
+}
+
 int LogBuffer::log(log_id_t log_id, log_time realtime,
                    uid_t uid, pid_t pid, pid_t tid,
                    const char *msg, unsigned short len) {
@@ -145,14 +219,164 @@
     }
 
     pthread_mutex_lock(&mLogElementsLock);
+    LogBufferElement* currentLast = lastLoggedElements[log_id];
+    if (currentLast) {
+        LogBufferElement *dropped = droppedElements[log_id];
+        unsigned short count = dropped ? dropped->getDropped() : 0;
+        //
+        // State Init
+        //     incoming:
+        //         dropped = NULL
+        //         currentLast = NULL;
+        //         elem = incoming message
+        //     outgoing:
+        //         dropped = NULL -> State 0
+        //         currentLast = copy of elem
+        //         log elem
+        // State 0
+        //     incoming:
+        //         count = 0
+        //         dropped = NULL
+        //         currentLast = copy of last message
+        //         elem = incoming message
+        //     outgoing: if match != DIFFERENT
+        //         dropped = copy of first identical message -> State 1
+        //         currentLast = reference to elem
+        //     break: if match == DIFFERENT
+        //         dropped = NULL -> State 0
+        //         delete copy of last message (incoming currentLast)
+        //         currentLast = copy of elem
+        //         log elem
+        // State 1
+        //     incoming:
+        //         count = 0
+        //         dropped = copy of first identical message
+        //         currentLast = reference to last held-back incoming
+        //                       message
+        //         elem = incoming message
+        //     outgoing: if match == SAME
+        //         delete copy of first identical message (dropped)
+        //         dropped = reference to last held-back incoming
+        //                   message set to chatty count of 1 -> State 2
+        //         currentLast = reference to elem
+        //     outgoing: if match == SAME_LIBLOG
+        //         dropped = copy of first identical message -> State 1
+        //         take sum of currentLast and elem
+        //         if sum overflows:
+        //             log currentLast
+        //             currentLast = reference to elem
+        //         else
+        //             delete currentLast
+        //             currentLast = reference to elem, sum liblog.
+        //     break: if match == DIFFERENT
+        //         delete dropped
+        //         dropped = NULL -> State 0
+        //         log reference to last held-back (currentLast)
+        //         currentLast = copy of elem
+        //         log elem
+        // State 2
+        //     incoming:
+        //         count = chatty count
+        //         dropped = chatty message holding count
+        //         currentLast = reference to last held-back incoming
+        //                       message.
+        //         dropped = chatty message holding count
+        //         elem = incoming message
+        //     outgoing: if match != DIFFERENT
+        //         delete chatty message holding count
+        //         dropped = reference to last held-back incoming
+        //                   message, set to chatty count + 1
+        //         currentLast = reference to elem
+        //     break: if match == DIFFERENT
+        //         log dropped (chatty message)
+        //         dropped = NULL -> State 0
+        //         log reference to last held-back (currentLast)
+        //         currentLast = copy of elem
+        //         log elem
+        //
+        enum match_type match = identical(elem, currentLast);
+        if (match != DIFFERENT) {
+            if (dropped) {
+                // Sum up liblog tag messages?
+                if ((count == 0) /* at Pass 1 */ && (match == SAME_LIBLOG)) {
+                    android_log_event_int_t* event =
+                        reinterpret_cast<android_log_event_int_t*>(
+                            const_cast<char*>(currentLast->getMsg()));
+                    //
+                    // To unit test, differentiate with something like:
+                    //    event->header.tag = htole32(CHATTY_LOG_TAG);
+                    // here, then instead of delete currentLast below,
+                    // log(currentLast) to see the incremental sums form.
+                    //
+                    uint32_t swab = event->payload.data;
+                    unsigned long long total = htole32(swab);
+                    event = reinterpret_cast<android_log_event_int_t*>(
+                            const_cast<char*>(elem->getMsg()));
+                    swab = event->payload.data;
 
+                    lastLoggedElements[LOG_ID_EVENTS] = elem;
+                    total += htole32(swab);
+                    // check for overflow
+                    if (total >= UINT32_MAX) {
+                        log(currentLast);
+                        pthread_mutex_unlock(&mLogElementsLock);
+                        return len;
+                    }
+                    stats.add(currentLast);
+                    stats.subtract(currentLast);
+                    delete currentLast;
+                    swab = total;
+                    event->payload.data = htole32(swab);
+                    pthread_mutex_unlock(&mLogElementsLock);
+                    return len;
+                }
+                if (count == USHRT_MAX) {
+                    log(dropped);
+                    count = 1;
+                } else {
+                    delete dropped;
+                    ++count;
+                }
+            }
+            if (count) {
+                stats.add(currentLast);
+                stats.subtract(currentLast);
+                currentLast->setDropped(count);
+            }
+            droppedElements[log_id] = currentLast;
+            lastLoggedElements[log_id] = elem;
+            pthread_mutex_unlock(&mLogElementsLock);
+            return len;
+        }
+        if (dropped) { // State 1 or 2
+            if (count) { // State 2
+               log(dropped); // report chatty
+            } else { // State 1
+               delete dropped;
+            }
+            droppedElements[log_id] = NULL;
+            log(currentLast); // report last message in the series
+        } else { // State 0
+            delete currentLast;
+        }
+    }
+    lastLoggedElements[log_id] = new LogBufferElement(*elem);
+
+    log(elem);
+    pthread_mutex_unlock(&mLogElementsLock);
+
+    return len;
+}
+
+// assumes mLogElementsLock held, owns elem, will look after garbage collection
+void LogBuffer::log(LogBufferElement* elem) {
     // Insert elements in time sorted order if possible
     //  NB: if end is region locked, place element at end of list
     LogBufferElementCollection::iterator it = mLogElements.end();
     LogBufferElementCollection::iterator last = it;
     while (last != mLogElements.begin()) {
         --it;
-        if ((*it)->getRealTime() <= realtime) {
+        if ((*it)->getRealTime() <= elem->getRealTime()) {
             break;
         }
         last = it;
@@ -169,7 +393,7 @@
 
         LastLogTimes::iterator times = mTimes.begin();
         while(times != mTimes.end()) {
-            LogTimeEntry *entry = (*times);
+            LogTimeEntry* entry = (*times);
             if (entry->owned_Locked()) {
                 if (!entry->mNonBlock) {
                     end_always = true;
@@ -187,17 +411,14 @@
                 || (end_set && (end >= (*last)->getSequence()))) {
             mLogElements.push_back(elem);
         } else {
-            mLogElements.insert(last,elem);
+            mLogElements.insert(last, elem);
         }
 
         LogTimeEntry::unlock();
     }
 
     stats.add(elem);
-    maybePrune(log_id);
-    pthread_mutex_unlock(&mLogElementsLock);
-
-    return len;
+    maybePrune(elem->getLogId());
 }
 
 // Prune at most 10% of the log entries or maxPrune, whichever is less.
@@ -313,10 +534,9 @@
 class LogBufferElementKey {
     const union {
         struct {
-            uint16_t uid;
+            uint32_t uid;
             uint16_t pid;
             uint16_t tid;
-            uint16_t padding;
         } __packed;
         uint64_t value;
     } __packed;
@@ -325,8 +545,8 @@
     LogBufferElementKey(uid_t uid, pid_t pid, pid_t tid):
             uid(uid),
             pid(pid),
-            tid(tid),
-            padding(0) {
+            tid(tid)
+    {
     }
     explicit LogBufferElementKey(uint64_t key):value(key) { }
 
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index ff9692e..932d55f 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -99,10 +99,15 @@
 
     bool monotonic;
 
+    LogBufferElement* lastLoggedElements[LOG_ID_MAX];
+    LogBufferElement* droppedElements[LOG_ID_MAX];
+    void log(LogBufferElement* elem);
+
 public:
     LastLogTimes &mTimes;
 
     explicit LogBuffer(LastLogTimes *times);
+    ~LogBuffer();
     void init();
     bool isMonotonic() { return monotonic; }
 
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index f5c60c7..5e37a30 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -50,6 +50,19 @@
         0;
 }
 
+LogBufferElement::LogBufferElement(const LogBufferElement &elem) :
+        mTag(elem.mTag),
+        mUid(elem.mUid),
+        mPid(elem.mPid),
+        mTid(elem.mTid),
+        mSequence(elem.mSequence),
+        mRealTime(elem.mRealTime),
+        mMsgLen(elem.mMsgLen),
+        mLogId(elem.mLogId) {
+    mMsg = new char[mMsgLen];
+    memcpy(mMsg, elem.mMsg, mMsgLen);
+}
+
 LogBufferElement::~LogBufferElement() {
     delete [] mMsg;
 }
@@ -89,7 +102,7 @@
         size_t name_len = strlen(name);
         // KISS: ToDo: Only checks prefix truncated, not suffix, or both
         if ((retval_len < name_len)
-                && !fast<strcmp>(retval, name + name_len - retval_len)) {
+                && !fastcmp<strcmp>(retval, name + name_len - retval_len)) {
             free(retval);
             retval = name;
         } else {
@@ -157,8 +170,6 @@
                           mDropped, (mDropped > 1) ? "s" : "");
 
     size_t hdrLen;
-    // LOG_ID_SECURITY not strictly needed since spam filter not activated,
-    // but required for accuracy.
     if (isBinary()) {
         hdrLen = sizeof(android_log_event_string_t);
     } else {
diff --git a/logd/LogBufferElement.h b/logd/LogBufferElement.h
index fb7fbed..f8ffacd 100644
--- a/logd/LogBufferElement.h
+++ b/logd/LogBufferElement.h
@@ -59,6 +59,7 @@
     LogBufferElement(log_id_t log_id, log_time realtime,
                      uid_t uid, pid_t pid, pid_t tid,
                      const char *msg, unsigned short len);
+    LogBufferElement(const LogBufferElement &elem);
     virtual ~LogBufferElement();
 
     bool isBinary(void) const {
@@ -79,6 +80,7 @@
         return mDropped = value;
     }
     unsigned short getMsgLen() const { return mMsg ? mMsgLen : 0; }
+    const char* getMsg() const { return mMsg; }
     uint64_t getSequence(void) const { return mSequence; }
     static uint64_t getCurrentSequence(void) { return sequence.load(memory_order_relaxed); }
     log_time getRealTime(void) const { return mRealTime; }
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index 7073535..f224079 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -306,22 +306,18 @@
 static const char resumeStr[] = "PM: suspend exit ";
 static const char suspendedStr[] = "Suspended for ";
 
-static const char *strnstr(const char *s, size_t len, const char *needle) {
+const char* android::strnstr(const char* s, size_t len, const char* needle) {
     char c;
 
-    if (!len) {
-        return NULL;
-    }
+    if (!len) return NULL;
     if ((c = *needle++) != 0) {
         size_t needleLen = strlen(needle);
         do {
             do {
-                if (len <= needleLen) {
-                    return NULL;
-                }
+                if (len <= needleLen) return NULL;
                 --len;
             } while (*s++ != c);
-        } while (fast<memcmp>(s, needle, needleLen));
+        } while (fastcmp<memcmp>(s, needle, needleLen));
         s--;
     }
     return s;
@@ -349,25 +345,25 @@
             return;
         }
 
-        const char *b;
-        if (((b = strnstr(cp, len, suspendStr)))
+        const char* b;
+        if (((b = android::strnstr(cp, len, suspendStr)))
                 && ((size_t)((b += sizeof(suspendStr) - 1) - cp) < len)) {
             len -= b - cp;
             calculateCorrection(now, b, len);
-        } else if (((b = strnstr(cp, len, resumeStr)))
+        } else if (((b = android::strnstr(cp, len, resumeStr)))
                 && ((size_t)((b += sizeof(resumeStr) - 1) - cp) < len)) {
             len -= b - cp;
             calculateCorrection(now, b, len);
-        } else if (((b = strnstr(cp, len, healthd)))
+        } else if (((b = android::strnstr(cp, len, healthd)))
                 && ((size_t)((b += sizeof(healthd) - 1) - cp) < len)
-                && ((b = strnstr(b, len -= b - cp, battery)))
+                && ((b = android::strnstr(b, len -= b - cp, battery)))
                 && ((size_t)((b += sizeof(battery) - 1) - cp) < len)) {
             // NB: healthd is roughly 150us late, so we use it instead to
             //     trigger a check for ntp-induced or hardware clock drift.
             log_time real(CLOCK_REALTIME);
             log_time mono(CLOCK_MONOTONIC);
             correction = (real < mono) ? log_time::EPOCH : (real - mono);
-        } else if (((b = strnstr(cp, len, suspendedStr)))
+        } else if (((b = android::strnstr(cp, len, suspendedStr)))
                 && ((size_t)((b += sizeof(suspendStr) - 1) - cp) < len)) {
             len -= b - cp;
             log_time real;
@@ -466,18 +462,14 @@
 
 // Passed the entire SYSLOG_ACTION_READ_ALL buffer and interpret a
 // compensated start time.
-void LogKlog::synchronize(const char *buf, size_t len) {
-    const char *cp = strnstr(buf, len, suspendStr);
+void LogKlog::synchronize(const char* buf, size_t len) {
+    const char* cp = android::strnstr(buf, len, suspendStr);
     if (!cp) {
-        cp = strnstr(buf, len, resumeStr);
-        if (!cp) {
-            return;
-        }
+        cp = android::strnstr(buf, len, resumeStr);
+        if (!cp) return;
     } else {
-        const char *rp = strnstr(buf, len, resumeStr);
-        if (rp && (rp < cp)) {
-            cp = rp;
-        }
+        const char* rp = android::strnstr(buf, len, resumeStr);
+        if (rp && (rp < cp)) cp = rp;
     }
 
     do {
@@ -491,7 +483,7 @@
     log_time now;
     sniffTime(now, &cp, len - (cp - buf), true);
 
-    const char *suspended = strnstr(buf, len, suspendedStr);
+    const char* suspended = android::strnstr(buf, len, suspendedStr);
     if (!suspended || (suspended > cp)) {
         return;
     }
@@ -581,12 +573,12 @@
 //  logd.klogd:
 // return -1 if message logd.klogd: <signature>
 //
-int LogKlog::log(const char *buf, size_t len) {
-    if (auditd && strnstr(buf, len, " audit(")) {
+int LogKlog::log(const char* buf, size_t len) {
+    if (auditd && android::strnstr(buf, len, " audit(")) {
         return 0;
     }
 
-    const char *p = buf;
+    const char* p = buf;
     int pri = parseKernelPrio(&p, len);
 
     log_time now;
@@ -594,7 +586,7 @@
 
     // sniff for start marker
     const char klogd_message[] = "logd.klogd: ";
-    const char *start = strnstr(p, len - (p - buf), klogd_message);
+    const char* start = android::strnstr(p, len - (p - buf), klogd_message);
     if (start) {
         uint64_t sig = strtoll(start + sizeof(klogd_message) - 1, NULL, 10);
         if (sig == signature.nsec()) {
@@ -640,7 +632,7 @@
 
     static const char infoBrace[] = "[INFO]";
     static const size_t infoBraceLen = strlen(infoBrace);
-    if ((taglen >= infoBraceLen) && !fast<strncmp>(p, infoBrace, infoBraceLen)) {
+    if ((taglen >= infoBraceLen) && !fastcmp<strncmp>(p, infoBrace, infoBraceLen)) {
         // <PRI>[<TIME>] "[INFO]"<tag> ":" message
         bt = p + infoBraceLen;
         taglen -= infoBraceLen;
@@ -675,7 +667,7 @@
             p = cp + 1;
         } else if ((taglen > size) && (tolower(*bt) == tolower(*cp))) {
             // clean up any tag stutter
-            if (!fast<strncasecmp>(bt + 1, cp + 1, size - 1)) { // no match
+            if (!fastcmp<strncasecmp>(bt + 1, cp + 1, size - 1)) { // no match
                 // <PRI>[<TIME>] <tag> <tag> : message
                 // <PRI>[<TIME>] <tag> <tag>: message
                 // <PRI>[<TIME>] <tag> '<tag>.<num>' : message
@@ -697,8 +689,8 @@
                 static const char host[] = "_host";
                 static const size_t hostlen = strlen(host);
                 if ((size > hostlen) &&
-                        !fast<strncmp>(bt + size - hostlen, host, hostlen) &&
-                        !fast<strncmp>(bt + 1, cp + 1, size - hostlen - 1)) {
+                        !fastcmp<strncmp>(bt + size - hostlen, host, hostlen) &&
+                        !fastcmp<strncmp>(bt + 1, cp + 1, size - hostlen - 1)) {
                     const char *b = cp;
                     cp += size - hostlen;
                     taglen -= size - hostlen;
@@ -746,10 +738,10 @@
         // register names like x18 but not driver names like en0
             || ((size == 3) && (isdigit(tag[1]) && isdigit(tag[2])))
         // blacklist
-            || ((size == cpuLen) && !fast<strncmp>(tag, cpu, cpuLen))
-            || ((size == warningLen) && !fast<strncasecmp>(tag, warning, warningLen))
-            || ((size == errorLen) && !fast<strncasecmp>(tag, error, errorLen))
-            || ((size == infoLen) && !fast<strncasecmp>(tag, info, infoLen))) {
+            || ((size == cpuLen) && !fastcmp<strncmp>(tag, cpu, cpuLen))
+            || ((size == warningLen) && !fastcmp<strncasecmp>(tag, warning, warningLen))
+            || ((size == errorLen) && !fastcmp<strncasecmp>(tag, error, errorLen))
+            || ((size == infoLen) && !fastcmp<strncasecmp>(tag, info, infoLen))) {
         p = start;
         etag = tag = "";
     }
@@ -761,7 +753,7 @@
     const char *mp = strnrchr(tag, ']', taglen);
     if (mp && (++mp < etag)) {
         size_t s = etag - mp;
-        if (((s + s) < taglen) && !fast<memcmp>(mp, mp - 1 - s, s)) {
+        if (((s + s) < taglen) && !fastcmp<memcmp>(mp, mp - 1 - s, s)) {
             taglen = mp - tag;
         }
     }
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
index 61d4c49..1b50b4e 100644
--- a/logd/LogReader.cpp
+++ b/logd/LogReader.cpp
@@ -108,7 +108,7 @@
     }
 
     bool nonBlock = false;
-    if (!fast<strncmp>(buffer, "dumpAndClose", 12)) {
+    if (!fastcmp<strncmp>(buffer, "dumpAndClose", 12)) {
         // Allow writer to get some cycles, and wait for pending notifications
         sched_yield();
         LogTimeEntry::lock();
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index d4b48ef..273150e 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -27,6 +27,8 @@
 
 #include "LogStatistics.h"
 
+size_t LogStatistics::SizesTotal;
+
 LogStatistics::LogStatistics() : enable(false) {
     log_id_for_each(id) {
         mSizes[id] = 0;
@@ -39,6 +41,8 @@
 
 namespace android {
 
+size_t sizesTotal() { return LogStatistics::sizesTotal(); }
+
 // caller must own and free character string
 char *pidToName(pid_t pid) {
     char *retval = NULL;
@@ -53,7 +57,7 @@
             if (ret > 0) {
                 buffer[sizeof(buffer)-1] = '\0';
                 // frameworks intermediate state
-                if (fast<strcmp>(buffer, "<pre-initialized>")) {
+                if (fastcmp<strcmp>(buffer, "<pre-initialized>")) {
                     retval = strdup(buffer);
                 }
             }
@@ -71,8 +75,18 @@
     mSizes[log_id] += size;
     ++mElements[log_id];
 
-    mSizesTotal[log_id] += size;
-    ++mElementsTotal[log_id];
+    if (element->getDropped()) {
+        ++mDroppedElements[log_id];
+    } else {
+        // When caller adding a chatty entry, they will have already
+        // called add() and subtract() for each entry as they are
+        // evaluated and trimmed, thus recording size and number of
+        // elements, but we must recognize the manufactured dropped
+        // entry as not contributing to the lifetime totals.
+        mSizesTotal[log_id] += size;
+        SizesTotal += size;
+        ++mElementsTotal[log_id];
+    }
 
     if (log_id == LOG_ID_KERNEL) {
         return;
@@ -182,7 +196,7 @@
     }
 
     // Parse /data/system/packages.list
-    uid_t userId = uid % AID_USER;
+    uid_t userId = uid % AID_USER_OFFSET;
     const char *name = android::uidToName(userId);
     if (!name && (userId > (AID_SHARED_GID_START - AID_APP))) {
         name = android::uidToName(userId - (AID_SHARED_GID_START - AID_APP));
@@ -209,7 +223,7 @@
             if (nameTmp) {
                 if (!name) {
                     name = strdup(nameTmp);
-                } else if (fast<strcmp>(name, nameTmp)) {
+                } else if (fastcmp<strcmp>(name, nameTmp)) {
                     free(const_cast<char *>(name));
                     name = NULL;
                     break;
@@ -295,7 +309,7 @@
             if ((spaces <= 0) && pruned.length()) {
                 spaces = 1;
             }
-            if ((spaces > 0) && (pruned.length() != 0)) {
+            if (spaces > 0) {
                 change += android::base::StringPrintf("%*s", (int)spaces, "");
             }
             pruned = change + pruned;
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index 1f598af..7acef6d 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -17,11 +17,12 @@
 #ifndef _LOGD_LOG_STATISTICS_H__
 #define _LOGD_LOG_STATISTICS_H__
 
-#include <memory>
+#include <ctype.h>
 #include <stdlib.h>
 #include <sys/types.h>
 
 #include <algorithm> // std::max
+#include <memory>
 #include <string>    // std::string
 #include <unordered_map>
 
@@ -211,14 +212,16 @@
                                     EntryBaseConstants::total_len
                                         - name.length() - drop_len - 1);
 
-        if (pruned.length()) {
-            return android::base::StringPrintf("%s%*s%*s\n", name.c_str(),
-                                               (int)size_len, size.c_str(),
-                                               (int)drop_len, pruned.c_str());
-        } else {
-            return android::base::StringPrintf("%s%*s\n", name.c_str(),
-                                               (int)size_len, size.c_str());
-        }
+        std::string ret = android::base::StringPrintf("%s%*s%*s",
+                                                      name.c_str(),
+                                                      (int)size_len, size.c_str(),
+                                                      (int)drop_len, pruned.c_str());
+        // remove any trailing spaces
+        size_t pos = ret.size();
+        size_t len = 0;
+        while (pos && isspace(ret[--pos])) ++len;
+        if (len) ret.erase(pos + 1, len);
+        return ret + "\n";
     }
 };
 
@@ -265,7 +268,7 @@
         if (pid != element->getPid()) {
             pid = -1;
         }
-        EntryBase::add(element);
+        EntryBaseDropped::add(element);
     }
 
     std::string formatHeader(const std::string &name, log_id_t id) const;
@@ -307,7 +310,7 @@
     const char*getName() const { return name; }
 
     inline void add(pid_t newPid) {
-        if (name && !fast<strncmp>(name, "zygote", 6)) {
+        if (name && !fastcmp<strncmp>(name, "zygote", 6)) {
             free(name);
             name = NULL;
         }
@@ -368,7 +371,7 @@
     const char*getName() const { return name; }
 
     inline void add(pid_t incomingTid) {
-        if (name && !fast<strncmp>(name, "zygote", 6)) {
+        if (name && !fastcmp<strncmp>(name, "zygote", 6)) {
             free(name);
             name = NULL;
         }
@@ -419,7 +422,7 @@
         if (pid != element->getPid()) {
             pid = -1;
         }
-        EntryBase::add(element);
+        EntryBaseDropped::add(element);
     }
 
     std::string formatHeader(const std::string &name, log_id_t id) const;
@@ -472,6 +475,7 @@
     size_t mDroppedElements[LOG_ID_MAX];
     size_t mSizesTotal[LOG_ID_MAX];
     size_t mElementsTotal[LOG_ID_MAX];
+    static size_t SizesTotal;
     bool enable;
 
     // uid to size list
@@ -554,6 +558,7 @@
     }
     size_t sizesTotal(log_id_t id) const { return mSizesTotal[id]; }
     size_t elementsTotal(log_id_t id) const { return mElementsTotal[id]; }
+    static size_t sizesTotal() { return SizesTotal; }
 
     std::string format(uid_t uid, pid_t pid, unsigned int logMask) const;
 
diff --git a/logd/LogUtils.h b/logd/LogUtils.h
index 44ac742..70f24e4 100644
--- a/logd/LogUtils.h
+++ b/logd/LogUtils.h
@@ -20,6 +20,7 @@
 #include <sys/cdefs.h>
 #include <sys/types.h>
 
+#include <utils/FastStrcmp.h>
 #include <private/android_logger.h>
 #include <sysutils/SocketClient.h>
 
@@ -32,13 +33,18 @@
 char *uidToName(uid_t uid);
 void prdebug(const char *fmt, ...) __printflike(1, 2);
 
-// Furnished in LogStatistics.cpp. Caller must own and free returned value
+// Furnished in LogStatistics.cpp.
+size_t sizesTotal();
+// Caller must own and free returned value
 char *pidToName(pid_t pid);
 char *tidToName(pid_t tid);
 
 // Furnished in main.cpp. Thread safe.
 const char *tagToName(size_t *len, uint32_t tag);
 
+// Furnished by LogKlog.cpp.
+const char* strnstr(const char* s, size_t len, const char* needle);
+
 }
 
 // Furnished in LogCommand.cpp
@@ -50,21 +56,4 @@
             (id == LOG_ID_RADIO) || (id == LOG_ID_EVENTS);
 }
 
-template <int (*cmp)(const char *l, const char *r, const size_t s)>
-static inline int fast(const char *l, const char *r, const size_t s) {
-    return (*l != *r) || cmp(l + 1, r + 1, s - 1);
-}
-
-template <int (*cmp)(const void *l, const void *r, const size_t s)>
-static inline int fast(const void *lv, const void *rv, const size_t s) {
-    const char *l = static_cast<const char *>(lv);
-    const char *r = static_cast<const char *>(rv);
-    return (*l != *r) || cmp(l + 1, r + 1, s - 1);
-}
-
-template <int (*cmp)(const char *l, const char *r)>
-static inline int fast(const char *l, const char *r) {
-    return (*l != *r) || cmp(l + 1, r + 1);
-}
-
 #endif // _LOGD_LOG_UTILS_H__
diff --git a/logd/README.property b/logd/README.property
index 791b1d5..de6767a 100644
--- a/logd/README.property
+++ b/logd/README.property
@@ -2,8 +2,9 @@
 
 name                       type default  description
 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
+ro.logd.auditd.dmesg       bool   true   selinux audit messages sent to dmesg.
+ro.logd.auditd.main        bool   true   selinux audit messages sent to main.
+ro.logd.auditd.events      bool   true   selinux audit messages sent to events.
 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
diff --git a/logd/libaudit.c b/logd/libaudit.c
index d2b212e..216f1a1 100644
--- a/logd/libaudit.c
+++ b/logd/libaudit.c
@@ -149,7 +149,7 @@
     return rc;
 }
 
-int audit_setup(int fd, uint32_t pid)
+int audit_setup(int fd, pid_t pid)
 {
     int rc;
     struct audit_message rep;
@@ -163,8 +163,7 @@
      * and the the mask set to AUDIT_STATUS_PID
      */
     status.pid = pid;
-    status.mask = AUDIT_STATUS_PID | AUDIT_STATUS_RATE_LIMIT;
-    status.rate_limit = 20; // audit entries per second
+    status.mask = AUDIT_STATUS_PID;
 
     /* Let the kernel know this pid will be registering for audit events */
     rc = audit_send(fd, AUDIT_SET, &status, sizeof(status));
@@ -187,6 +186,27 @@
     return 0;
 }
 
+int audit_rate_limit(int fd, unsigned rate_limit)
+{
+    int rc;
+    struct audit_message rep;
+    struct audit_status status;
+
+    memset(&status, 0, sizeof(status));
+
+    status.mask = AUDIT_STATUS_RATE_LIMIT;
+    status.rate_limit = rate_limit; /* audit entries per second */
+
+    rc = audit_send(fd, AUDIT_SET, &status, sizeof(status));
+    if (rc < 0) {
+        return rc;
+    }
+
+    audit_get_reply(fd, &rep, GET_REPLY_NONBLOCKING, 0);
+
+    return 0;
+}
+
 int audit_open()
 {
     return socket(PF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, NETLINK_AUDIT);
diff --git a/logd/libaudit.h b/logd/libaudit.h
index b9e330d..9865d43 100644
--- a/logd/libaudit.h
+++ b/logd/libaudit.h
@@ -33,7 +33,7 @@
 #define MAX_AUDIT_MESSAGE_LENGTH    8970
 
 typedef enum {
-    GET_REPLY_BLOCKING=0,
+    GET_REPLY_BLOCKING = 0,
     GET_REPLY_NONBLOCKING
 } reply_t;
 
@@ -55,7 +55,7 @@
  *  A valid fd on success or < 0 on error with errno set.
  *  Returns the same errors as man 2 socket.
  */
-extern int  audit_open(void);
+extern int audit_open(void);
 
 /**
  * Closes the fd returned from audit_open()
@@ -78,19 +78,36 @@
  * @return
  *  This function returns 0 on success, else -errno.
  */
-extern int  audit_get_reply(int fd, struct audit_message *rep, reply_t block,
-               int peek);
+extern int audit_get_reply(int fd, struct audit_message *rep, reply_t block,
+                           int peek);
 
 /**
- * Sets a pid to recieve audit netlink events from the kernel
+ * Sets a pid to receive audit netlink events from the kernel
  * @param fd
  *  The fd returned by a call to audit_open()
  * @param pid
- *  The pid whom to set as the reciever of audit messages
+ *  The pid whom to set as the receiver of audit messages
  * @return
  *  This function returns 0 on success, -errno on error.
  */
-extern int  audit_setup(int fd, uint32_t pid);
+extern int audit_setup(int fd, pid_t pid);
+
+/**
+ * Sets the rate limit to receive audit netlink events from the kernel
+ * @param fd
+ *  The fd returned by a call to audit_open()
+ * @param max_rate
+ *  The cap of the maximum number of audit messages a second
+ * @return
+ *  This function returns 0 on success, -errno on error.
+ */
+
+/* Guidelines to follow for dynamic rate_limit */
+#define AUDIT_RATE_LIMIT_DEFAULT 20        /* acceptable burst rate      */
+#define AUDIT_RATE_LIMIT_BURST_DURATION 10 /* number of seconds of burst */
+#define AUDIT_RATE_LIMIT_MAX     5         /* acceptable sustained rate  */
+
+extern int audit_rate_limit(int fd, unsigned rate_limit);
 
 __END_DECLS
 
diff --git a/logd/main.cpp b/logd/main.cpp
index c3343d7..5878f15 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -451,9 +451,8 @@
         pthread_attr_destroy(&attr);
     }
 
-    bool auditd = __android_logger_property_get_bool("logd.auditd",
-                                                     BOOL_DEFAULT_TRUE |
-                                                     BOOL_DEFAULT_FLAG_PERSIST);
+    bool auditd = __android_logger_property_get_bool("ro.logd.auditd",
+                                                     BOOL_DEFAULT_TRUE);
     if (drop_privs(klogd, auditd) != 0) {
         return -1;
     }
@@ -513,8 +512,8 @@
     if (auditd) {
         al = new LogAudit(logBuf, reader,
                           __android_logger_property_get_bool(
-                              "logd.auditd.dmesg",
-                              BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_PERSIST)
+                              "ro.logd.auditd.dmesg",
+                              BOOL_DEFAULT_TRUE)
                                   ? fdDmesg
                                   : -1);
     }
diff --git a/logd/tests/Android.mk b/logd/tests/Android.mk
index 808087a..c053993 100644
--- a/logd/tests/Android.mk
+++ b/logd/tests/Android.mk
@@ -27,12 +27,15 @@
 # Unit tests.
 # -----------------------------------------------------------------------------
 
+event_flag := -DAUDITD_LOG_TAG=1003 -DCHATTY_LOG_TAG=1004
+
 test_c_flags := \
     -fstack-protector-all \
     -g \
     -Wall -Wextra \
     -Werror \
     -fno-builtin \
+    $(event_flag)
 
 test_src_files := \
     logd_test.cpp
@@ -43,6 +46,6 @@
 LOCAL_MODULE := $(test_module_prefix)unit-tests
 LOCAL_MODULE_TAGS := $(test_tags)
 LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_SHARED_LIBRARIES := libbase libcutils liblog
+LOCAL_SHARED_LIBRARIES := libbase libcutils liblog libselinux
 LOCAL_SRC_FILES := $(test_src_files)
 include $(BUILD_NATIVE_TEST)
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index 254a3f8..703c0fb 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -20,6 +20,9 @@
 #include <signal.h>
 #include <stdio.h>
 #include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
 
 #include <string>
 
@@ -28,7 +31,12 @@
 #include <cutils/sockets.h>
 #include <gtest/gtest.h>
 #include <log/log.h>
+#include <private/android_filesystem_config.h>
+#ifdef __ANDROID__
+#include <selinux/selinux.h>
+#endif
 
+#include "../libaudit.h" // pickup AUDIT_RATE_LIMIT_*
 #include "../LogReader.h" // pickup LOGD_SNDTIMEO
 
 /*
@@ -415,7 +423,13 @@
 
     // Introduce some extreme spam for the worst UID filter
     ASSERT_TRUE(NULL != (fp = popen(
-        "/data/nativetest/liblog-benchmarks/liblog-benchmarks",
+        "/data/nativetest/liblog-benchmarks/liblog-benchmarks"
+            " BM_log_maximum_retry"
+            " BM_log_maximum"
+            " BM_clock_overhead"
+            " BM_log_overhead"
+            " BM_log_latency"
+            " BM_log_delay",
         "r")));
 
     char buffer[5120];
@@ -581,10 +595,12 @@
             continue;
         }
 
+        // alarm triggers at 50% of the --wrap time out
         content_wrap = recv(fd, msg_wrap.buf, sizeof(msg_wrap), 0) > 0;
 
         alarm_wrap = alarm(5);
 
+        // alarm triggers at 133% of the --wrap time out
         content_timeout = recv(fd, msg_timeout.buf, sizeof(msg_timeout), 0) > 0;
         if (!content_timeout) { // make sure we hit dumpAndClose
             content_timeout = recv(fd, msg_timeout.buf, sizeof(msg_timeout), 0) > 0;
@@ -629,11 +645,24 @@
 
 // b/26447386 refined behavior
 TEST(logd, timeout) {
+    // b/33962045 This test interferes with other log reader tests that
+    // follow because of file descriptor socket persistence in the same
+    // process.  So let's fork it to isolate it from giving us pain.
+
+    pid_t pid = fork();
+
+    if (pid) {
+        siginfo_t info = {};
+        ASSERT_EQ(0, TEMP_FAILURE_RETRY(waitid(P_PID, pid, &info, WEXITED)));
+        ASSERT_EQ(0, info.si_status);
+        return;
+    }
+
     log_msg msg_wrap, msg_timeout;
     bool content_wrap = false, content_timeout = false, written = false;
     unsigned int alarm_wrap = 0, alarm_timeout = 0;
     // A few tries to get it right just in case wrap kicks in due to
-    // content providers being active during the test
+    // content providers being active during the test.
     int i = 5;
     log_time now(android_log_clockid());
     now.tv_sec -= 30; // reach back a moderate period of time
@@ -642,7 +671,8 @@
         int fd = socket_local_client("logdr",
                                      ANDROID_SOCKET_NAMESPACE_RESERVED,
                                      SOCK_SEQPACKET);
-        ASSERT_LT(0, fd);
+        EXPECT_LT(0, fd);
+        if (fd < 0) _exit(fd);
 
         std::string ask = android::base::StringPrintf(
             "dumpAndClose lids=0,1,2,3,4,5 timeout=6 start=%"
@@ -665,10 +695,12 @@
             continue;
         }
 
+        // alarm triggers at 50% of the --wrap time out
         content_wrap = recv(fd, msg_wrap.buf, sizeof(msg_wrap), 0) > 0;
 
         alarm_wrap = alarm(5);
 
+        // alarm triggers at 133% of the --wrap time out
         content_timeout = recv(fd, msg_timeout.buf, sizeof(msg_timeout), 0) > 0;
         if (!content_timeout) { // make sure we hit dumpAndClose
             content_timeout = recv(fd, msg_timeout.buf, sizeof(msg_timeout), 0) > 0;
@@ -692,6 +724,7 @@
         if (content_timeout) {
             log_time msg(msg_timeout.entry.sec, msg_timeout.entry.nsec);
             EXPECT_FALSE(msg < now);
+            if (msg < now) _exit(-1);
             if (msg > now) {
                 now = msg;
                 now.tv_sec += 30;
@@ -724,6 +757,8 @@
     EXPECT_EQ(0U, alarm_wrap);
     EXPECT_TRUE(content_timeout);
     EXPECT_NE(0U, alarm_timeout);
+
+    _exit(!written + content_wrap + alarm_wrap + !content_timeout + !alarm_timeout);
 }
 
 // b/27242723 confirmed fixed
@@ -778,3 +813,257 @@
 
     close(fd);
 }
+
+static inline int32_t get4LE(const char* src)
+{
+    return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+}
+
+void __android_log_btwrite_multiple__helper(int count) {
+    log_time ts(CLOCK_MONOTONIC);
+
+    log_time ts1(CLOCK_MONOTONIC);
+
+    // We fork to create a unique pid for the submitted log messages
+    // so that we do not collide with the other _multiple_ tests.
+
+    pid_t pid = fork();
+
+    if (pid == 0) {
+        // child
+        for (int i = count; i; --i) {
+            ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
+            usleep(100);
+        }
+        ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts1, sizeof(ts1)));
+        usleep(1000000);
+
+        _exit(0);
+    }
+
+    siginfo_t info = {};
+    ASSERT_EQ(0, TEMP_FAILURE_RETRY(waitid(P_PID, pid, &info, WEXITED)));
+    ASSERT_EQ(0, info.si_status);
+
+    struct logger_list *logger_list;
+    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+        LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 0, pid)));
+
+    int expected_count = (count < 2) ? count : 2;
+    int expected_chatty_count = (count <= 2) ? 0 : 1;
+    int expected_expire_count = (count < 2) ? 0 : (count - 2);
+
+    count = 0;
+    int second_count = 0;
+    int chatty_count = 0;
+    int expire_count = 0;
+
+    for (;;) {
+        log_msg log_msg;
+        if (android_logger_list_read(logger_list, &log_msg) <= 0) break;
+
+        if ((log_msg.entry.pid != pid) ||
+            (log_msg.entry.len < (4 + 1 + 8)) ||
+            (log_msg.id() != LOG_ID_EVENTS)) continue;
+
+        char *eventData = log_msg.msg();
+        if (!eventData) continue;
+
+        uint32_t tag = get4LE(eventData);
+
+        if ((eventData[4] == EVENT_TYPE_LONG) && (log_msg.entry.len == (4 + 1 + 8))) {
+            if (tag != 0) continue;
+
+            log_time tx(eventData + 4 + 1);
+            if (ts == tx) {
+                ++count;
+            } else if (ts1 == tx) {
+                ++second_count;
+            }
+        } else if (eventData[4] == EVENT_TYPE_STRING) {
+            if (tag != CHATTY_LOG_TAG) continue;
+            ++chatty_count;
+            // int len = get4LE(eventData + 4 + 1);
+            log_msg.buf[LOGGER_ENTRY_MAX_LEN] = '\0';
+            const char *cp = strstr(eventData + 4 + 1 + 4, " expire ");
+            if (!cp) continue;
+            unsigned val = 0;
+            sscanf(cp, " expire %u lines", &val);
+            expire_count += val;
+        }
+    }
+
+    android_logger_list_close(logger_list);
+
+    EXPECT_EQ(expected_count, count);
+    EXPECT_EQ(1, second_count);
+    EXPECT_EQ(expected_chatty_count, chatty_count);
+    EXPECT_EQ(expected_expire_count, expire_count);
+}
+
+TEST(logd, multiple_test_1) {
+    __android_log_btwrite_multiple__helper(1);
+}
+
+TEST(logd, multiple_test_2) {
+    __android_log_btwrite_multiple__helper(2);
+}
+
+TEST(logd, multiple_test_3) {
+    __android_log_btwrite_multiple__helper(3);
+}
+
+TEST(logd, multiple_test_10) {
+    __android_log_btwrite_multiple__helper(10);
+}
+
+#ifdef __ANDROID__
+// returns violating pid
+static pid_t sepolicy_rate(unsigned rate, unsigned num) {
+    pid_t pid = fork();
+
+    if (pid) {
+        siginfo_t info = {};
+        if (TEMP_FAILURE_RETRY(waitid(P_PID, pid, &info, WEXITED))) return 0;
+        if (info.si_status) return 0;
+        return pid;
+    }
+
+    // We may have DAC, but let's not have MAC
+    if (setcon("u:object_r:shell:s0") < 0) {
+        int save_errno = errno;
+        security_context_t context;
+        getcon(&context);
+        fprintf(stderr, "setcon(\"u:r:shell:s0\") failed @\"%s\" %s\n",
+                context, strerror(save_errno));
+        freecon(context);
+        _exit(-1);
+        // NOTREACHED
+        return 0;
+    }
+
+    // Requests dac_read_search, falls back to request dac_override
+    rate /= 2;
+    useconds_t usec;
+    if (rate == 0) {
+        rate = 1;
+        usec = 2000000;
+    } else {
+        usec = (1000000 + (rate / 2)) / rate;
+    }
+    num = (num + 1) / 2;
+
+    if (usec < 2) usec = 2;
+
+    while (num > 0) {
+        if (access(android::base::StringPrintf(
+                       "/data/misc/logd/cannot_access_directory_%u",
+                       num).c_str(),
+                   F_OK) == 0) {
+            _exit(-1);
+            // NOTREACHED
+            return 0;
+        }
+        usleep(usec);
+        --num;
+    }
+    _exit(0);
+    // NOTREACHED
+    return 0;
+}
+
+static int count_avc(pid_t pid) {
+    int count = 0;
+
+    if (pid == 0) return count;
+
+    struct logger_list *logger_list;
+    if (!(logger_list = android_logger_list_open(LOG_ID_EVENTS,
+                                                 ANDROID_LOG_RDONLY |
+                                                     ANDROID_LOG_NONBLOCK,
+                                                 0,
+                                                 pid))) return count;
+    for (;;) {
+        log_msg log_msg;
+
+        if (android_logger_list_read(logger_list, &log_msg) <= 0) break;
+
+        if ((log_msg.entry.pid != pid) ||
+            (log_msg.entry.len < (4 + 1 + 8)) ||
+            (log_msg.id() != LOG_ID_EVENTS)) continue;
+
+        char *eventData = log_msg.msg();
+        if (!eventData) continue;
+
+        uint32_t tag = get4LE(eventData);
+        if (tag != AUDITD_LOG_TAG) continue;
+
+        if (eventData[4] != EVENT_TYPE_STRING) continue;
+
+        // int len = get4LE(eventData + 4 + 1);
+        log_msg.buf[LOGGER_ENTRY_MAX_LEN] = '\0';
+        const char *cp = strstr(eventData + 4 + 1 + 4, "): avc: ");
+        if (!cp) continue;
+
+        ++count;
+    }
+
+    android_logger_list_close(logger_list);
+
+    return count;
+}
+#endif
+
+TEST(logd, sepolicy_rate_limiter_maximum) {
+#ifdef __ANDROID__
+    static const int rate = AUDIT_RATE_LIMIT_MAX;
+    static const int duration = 2;
+    // Two seconds of a liveable sustained rate
+    EXPECT_EQ(rate * duration, count_avc(sepolicy_rate(rate, rate * duration)));
+#else
+    GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(logd, sepolicy_rate_limiter_sub_burst) {
+#ifdef __ANDROID__
+    // maximum period below half way between sustainable and burst rate.
+    static const int threshold = ((AUDIT_RATE_LIMIT_BURST_DURATION *
+                                   (AUDIT_RATE_LIMIT_DEFAULT +
+                                    AUDIT_RATE_LIMIT_MAX)) +
+                                  1) / 2;
+    static const int rate = (threshold / AUDIT_RATE_LIMIT_BURST_DURATION) - 1;
+    static const int duration = AUDIT_RATE_LIMIT_BURST_DURATION;
+    EXPECT_EQ(rate * duration, count_avc(sepolicy_rate(rate, rate * duration)));
+#else
+    GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
+
+TEST(logd, sepolicy_rate_limiter_spam) {
+#ifdef __ANDROID__
+    // maximum period of double the maximum burst rate
+    static const int threshold = ((AUDIT_RATE_LIMIT_BURST_DURATION *
+                                   (AUDIT_RATE_LIMIT_DEFAULT +
+                                    AUDIT_RATE_LIMIT_MAX)) +
+                                  1) / 2;
+    static const int rate = AUDIT_RATE_LIMIT_DEFAULT * 2;
+    static const int duration = threshold / AUDIT_RATE_LIMIT_DEFAULT;
+    EXPECT_GE(((AUDIT_RATE_LIMIT_DEFAULT * duration) * 115) /
+                                        100, // +15% margin
+              count_avc(sepolicy_rate(rate, rate * duration)));
+    // give logd another 3 seconds to react to the burst before checking
+    sepolicy_rate(rate, rate * 3);
+    // maximum period at double the maximum burst rate (spam filter kicked in)
+    EXPECT_GE(((AUDIT_RATE_LIMIT_MAX * AUDIT_RATE_LIMIT_BURST_DURATION) * 130) /
+                                        100, // +30% margin
+              count_avc(sepolicy_rate(rate,
+                                      rate * AUDIT_RATE_LIMIT_BURST_DURATION)));
+    // cool down, and check unspammy rate still works
+    sleep(2);
+    EXPECT_LE(AUDIT_RATE_LIMIT_BURST_DURATION - 1, // allow _one_ to be lost
+              count_avc(sepolicy_rate(1, AUDIT_RATE_LIMIT_BURST_DURATION)));
+#else
+    GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+}
diff --git a/rootdir/etc/public.libraries.android.txt b/rootdir/etc/public.libraries.android.txt
index e6c94ff..2fe90d9 100644
--- a/rootdir/etc/public.libraries.android.txt
+++ b/rootdir/etc/public.libraries.android.txt
@@ -12,6 +12,7 @@
 liblog.so
 libmediandk.so
 libm.so
+liboboe.so
 libOpenMAXAL.so
 libOpenSLES.so
 libRS.so
diff --git a/rootdir/etc/public.libraries.wear.txt b/rootdir/etc/public.libraries.wear.txt
index 292730a..b35d463 100644
--- a/rootdir/etc/public.libraries.wear.txt
+++ b/rootdir/etc/public.libraries.wear.txt
@@ -12,6 +12,7 @@
 liblog.so
 libmediandk.so
 libm.so
+liboboe.so
 libOpenMAXAL.so
 libOpenSLES.so
 libRS.so
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 11f91ce..ccbad4b 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -125,6 +125,12 @@
     write /proc/sys/kernel/sched_rt_runtime_us 950000
     write /proc/sys/kernel/sched_rt_period_us 1000000
 
+    # Assign reasonable ceiling values for socket rcv/snd buffers.
+    # These should almost always be overridden by the target per the
+    # the corresponding technology maximums.
+    write /proc/sys/net/core/rmem_max  262144
+    write /proc/sys/net/core/wmem_max  262144
+
     # reflect fwmark from incoming packets onto generated replies
     write /proc/sys/net/ipv4/fwmark_reflect 1
     write /proc/sys/net/ipv6/fwmark_reflect 1
@@ -361,7 +367,7 @@
     # Start bootcharting as soon as possible after the data partition is
     # mounted to collect more data.
     mkdir /data/bootchart 0755 shell shell
-    bootchart_init
+    bootchart start
 
     # Avoid predictable entropy pool. Carry over entropy from previous boot.
     copy /data/system/entropy.dat /dev/urandom
@@ -619,6 +625,9 @@
 on property:sys.powerctl=*
     powerctl ${sys.powerctl}
 
+on property:sys.boot_completed=1
+    bootchart stop
+
 # system server cannot write to /proc/sys files,
 # and chown/chmod does not work for /proc/sys/ entries.
 # So proxy writes through init.
diff --git a/rootdir/init.zygote64_32.rc b/rootdir/init.zygote64_32.rc
index e918b67..3612ea2 100644
--- a/rootdir/init.zygote64_32.rc
+++ b/rootdir/init.zygote64_32.rc
@@ -12,7 +12,7 @@
     onrestart restart netd
     writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
 
-service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
+service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary --enable-lazy-preload
     class main
     priority -20
     user root
diff --git a/run-as/run-as.cpp b/run-as/run-as.cpp
index aec51f4..e7b8cc2 100644
--- a/run-as/run-as.cpp
+++ b/run-as/run-as.cpp
@@ -165,12 +165,12 @@
   if (setegid(old_egid) == -1) error(1, errno, "couldn't restore egid");
 
   // Verify that user id is not too big.
-  if ((UID_MAX - info.uid) / AID_USER < (uid_t)userId) {
+  if ((UID_MAX - info.uid) / AID_USER_OFFSET < (uid_t)userId) {
     error(1, 0, "user id too big: %d", userId);
   }
 
   // Calculate user app ID.
-  uid_t userAppId = (AID_USER * userId) + info.uid;
+  uid_t userAppId = (AID_USER_OFFSET * userId) + info.uid;
 
   // Reject system packages.
   if (userAppId < AID_APP) {
diff --git a/storaged/Android.mk b/storaged/Android.mk
new file mode 100644
index 0000000..db97040
--- /dev/null
+++ b/storaged/Android.mk
@@ -0,0 +1,34 @@
+# Copyright 2016 The Android Open Source Project
+
+LOCAL_PATH := $(call my-dir)
+
+LIBSTORAGED_SHARED_LIBRARIES := libbinder libbase libutils libcutils liblog libsysutils libcap
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := storaged.cpp \
+                  storaged_service.cpp \
+                  storaged_utils.cpp \
+                  EventLogTags.logtags
+LOCAL_MODULE := libstoraged
+LOCAL_CFLAGS := -Werror
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include external/googletest/googletest/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_SHARED_LIBRARIES := $(LIBSTORAGED_SHARED_LIBRARIES)
+
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := storaged
+LOCAL_INIT_RC := storaged.rc
+LOCAL_SRC_FILES := main.cpp
+# libstoraged is an internal static library, only main.cpp and storaged_test.cpp should be using it
+LOCAL_STATIC_LIBRARIES := libstoraged
+LOCAL_SHARED_LIBRARIES := $(LIBSTORAGED_SHARED_LIBRARIES)
+LOCAL_CFLAGS := -Wall -Werror -Wno-unused-parameter
+LOCAL_C_INCLUDES := external/googletest/googletest/include
+
+include $(BUILD_EXECUTABLE)
+
+include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/storaged/EventLogTags.logtags b/storaged/EventLogTags.logtags
new file mode 100644
index 0000000..ee92dd1
--- /dev/null
+++ b/storaged/EventLogTags.logtags
@@ -0,0 +1,39 @@
+# The entries in this file map a sparse set of log tag numbers to tag names.
+# This is installed on the device, in /system/etc, and parsed by logcat.
+#
+# Tag numbers are decimal integers, from 0 to 2^31.  (Let's leave the
+# negative values alone for now.)
+#
+# Tag names are one or more ASCII letters and numbers or underscores, i.e.
+# "[A-Z][a-z][0-9]_".  Do not include spaces or punctuation (the former
+# impacts log readability, the latter makes regex searches more annoying).
+#
+# Tag numbers and names are separated by whitespace.  Blank lines and lines
+# starting with '#' are ignored.
+#
+# Optionally, after the tag names can be put a description for the value(s)
+# of the tag. Description are in the format
+#    (<name>|data type[|data unit])
+# Multiple values are separated by commas.
+#
+# The data type is a number from the following values:
+# 1: int
+# 2: long
+# 3: string
+# 4: list
+# 5: float
+#
+# The data unit is a number taken from the following list:
+# 1: Number of objects
+# 2: Number of bytes
+# 3: Number of milliseconds
+# 4: Number of allocations
+# 5: Id
+# 6: Percent
+# Default value for data of type int/long is 2 (bytes).
+#
+# TODO: generate ".java" and ".h" files with integer constants from this file.
+
+2732 storaged_disk_stats (type|3),(start_time|2|3),(end_time|2|3),(read_ios|2|1),(read_merges|2|1),(read_sectors|2|1),(read_ticks|2|3),(write_ios|2|1),(write_merges|2|1),(write_sectors|2|1),(write_ticks|2|3),(o_in_flight|2|1),(io_ticks|2|3),(io_in_queue|2|1)
+
+2733 storaged_emmc_info (mmc_ver|3),(eol|1),(lifetime_a|1),(lifetime_b|1)
diff --git a/storaged/include/storaged.h b/storaged/include/storaged.h
new file mode 100644
index 0000000..eb827cf
--- /dev/null
+++ b/storaged/include/storaged.h
@@ -0,0 +1,340 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _STORAGED_H_
+#define _STORAGED_H_
+
+#define DEBUG
+
+#include <queue>
+#include <semaphore.h>
+#include <stdint.h>
+#include <string>
+#include <syslog.h>
+#include <unordered_map>
+#include <vector>
+
+#define FRIEND_TEST(test_case_name, test_name) \
+friend class test_case_name##_##test_name##_Test
+
+/* For debug */
+#ifdef DEBUG
+#define debuginfo(fmt, ...) \
+ do {printf("%s():\t" fmt "\t[%s:%d]\n", __FUNCTION__, ##__VA_ARGS__, __FILE__, __LINE__);} \
+ while(0)
+#else
+#define debuginfo(...)
+#endif
+
+#define KMSG_PRIORITY(PRI)                            \
+    '<',                                              \
+    '0' + LOG_MAKEPRI(LOG_DAEMON, LOG_PRI(PRI)) / 10, \
+    '0' + LOG_MAKEPRI(LOG_DAEMON, LOG_PRI(PRI)) % 10, \
+    '>'
+
+static char kmsg_error_prefix[] = { KMSG_PRIORITY(LOG_ERR),
+    's', 't', 'o', 'r', 'a', 'g', 'e', 'd', ':', '\0' };
+
+static char kmsg_info_prefix[] = { KMSG_PRIORITY(LOG_INFO),
+    's', 't', 'o', 'r', 'a', 'g', 'e', 'd', ':', '\0' };
+
+static char kmsg_warning_prefix[] = { KMSG_PRIORITY(LOG_WARNING),
+    's', 't', 'o', 'r', 'a', 'g', 'e', 'd', ':', '\0' };
+
+// number of attributes diskstats has
+#define DISK_STATS_SIZE ( 11 )
+// maximum size limit of a stats file
+#define DISK_STATS_FILE_MAX_SIZE ( 256 )
+#define DISK_STATS_IO_IN_FLIGHT_IDX ( 8 )
+struct disk_stats {
+    /* It will be extremely unlikely for any of the following entries to overflow.
+     * For read_bytes(which will be greater than any of the following entries), it
+     * will take 27 years to overflow uint64_t at the reading rate of 20GB/s, which
+     * is the peak memory transfer rate for current memory.
+     * The diskstats entries (first 11) need to be at top in this structure _after_
+     * compiler's optimization.
+     */
+    uint64_t read_ios;       // number of read I/Os processed
+    uint64_t read_merges;    // number of read I/Os merged with in-queue I/Os
+    uint64_t read_sectors;   // number of sectors read
+    uint64_t read_ticks;     // total wait time for read requests
+    uint64_t write_ios;      // number of write I/Os processed
+    uint64_t write_merges;   // number of write I/Os merged with in-queue I/Os
+    uint64_t write_sectors;  // number of sectors written
+    uint64_t write_ticks;    // total wait time for write requests
+    uint64_t io_in_flight;   // number of I/Os currently in flight
+    uint64_t io_ticks;       // total time this block device has been active
+    uint64_t io_in_queue;    // total wait time for all requests
+
+    uint64_t start_time;     // monotonic time accounting starts
+    uint64_t end_time;       // monotonic time accounting ends
+    uint32_t counter;        // private counter for accumulate calculations
+    double   io_avg;         // average io_in_flight for accumulate calculations
+};
+
+#define MMC_VER_STR_LEN ( 8 )   // maximum length of the MMC version string
+// minimum size of a ext_csd file
+#define EXT_CSD_FILE_MIN_SIZE ( 1024 )
+struct emmc_info {
+    int eol;                        // pre-eol (end of life) information
+    int lifetime_a;                 // device life time estimation (type A)
+    int lifetime_b;                 // device life time estimation (type B)
+    char mmc_ver[MMC_VER_STR_LEN];  // device version string
+};
+
+struct disk_perf {
+    uint32_t read_perf;         // read speed (kbytes/s)
+    uint32_t read_ios;          // read I/Os per second
+    uint32_t write_perf;        // write speed (kbytes/s)
+    uint32_t write_ios;         // write I/Os per second
+    uint32_t queue;             // I/Os in queue
+};
+
+#define CMD_MAX_LEN ( 64 )
+struct task_info {
+    uint32_t pid;                   // task id
+    uint64_t rchar;                 // characters read
+    uint64_t wchar;                 // characters written
+    uint64_t syscr;                 // read syscalls
+    uint64_t syscw;                 // write syscalls
+    uint64_t read_bytes;            // bytes read (from storage layer)
+    uint64_t write_bytes;           // bytes written (to storage layer)
+    uint64_t cancelled_write_bytes; // cancelled write byte by truncate
+
+    uint64_t starttime;             // start time of task
+
+    char cmd[CMD_MAX_LEN];          // filename of the executable
+};
+
+class lock_t {
+    sem_t* mSem;
+public:
+    lock_t(sem_t* sem) {
+        mSem = sem;
+        sem_wait(mSem);
+    }
+    ~lock_t() {
+        sem_post(mSem);
+    }
+};
+
+class tasks_t {
+private:
+    FRIEND_TEST(storaged_test, tasks_t);
+    sem_t mSem;
+    // hashmap for all running tasks w/ pid as key
+    std::unordered_map<uint32_t, struct task_info> mRunning;
+    // hashmap for all tasks that have been killed (categorized by cmd) w/ cmd as key
+    std::unordered_map<std::string, struct task_info> mOld;
+    std::unordered_map<std::uint32_t, struct task_info> get_running_tasks();
+public:
+    tasks_t() {
+        sem_init(&mSem, 0, 1); // TODO: constructor don't have a return value, what if sem_init fails
+    }
+
+    ~tasks_t() {
+        sem_destroy(&mSem);
+    }
+
+    void update_running_tasks(void);
+    std::vector<struct task_info> get_tasks(void);
+};
+
+class stream_stats {
+private:
+    double mSum;
+    double mSquareSum;
+    uint32_t mCnt;
+public:
+    stream_stats() : mSum(0), mSquareSum(0), mCnt(0) {};
+    ~stream_stats() {};
+    double get_mean() {
+        return mSum / mCnt;
+    }
+    double get_std() {
+        return sqrt(mSquareSum / mCnt - mSum * mSum / (mCnt * mCnt));
+    }
+    void add(uint32_t num) {
+        mSum += (double)num;
+        mSquareSum += (double)num * (double)num;
+        mCnt++;
+    }
+    void evict(uint32_t num) {
+        if (mSum < num || mSquareSum < (double)num * (double)num) return;
+        mSum -= (double)num;
+        mSquareSum -= (double)num * (double)num;
+        mCnt--;
+    }
+};
+
+#define MMC_DISK_STATS_PATH "/sys/block/mmcblk0/stat"
+#define SDA_DISK_STATS_PATH "/sys/block/sda/stat"
+#define EMMC_ECSD_PATH "/d/mmc0/mmc0:0001/ext_csd"
+class disk_stats_monitor {
+private:
+    FRIEND_TEST(storaged_test, disk_stats_monitor);
+    const char* DISK_STATS_PATH;
+    struct disk_stats mPrevious;
+    struct disk_stats mAccumulate;
+    bool mStall;
+    std::queue<struct disk_perf> mBuffer;
+    struct {
+        stream_stats read_perf;           // read speed (bytes/s)
+        stream_stats read_ios;            // read I/Os per second
+        stream_stats write_perf;          // write speed (bytes/s)
+        stream_stats write_ios;           // write I/O per second
+        stream_stats queue;               // I/Os in queue
+    } mStats;
+    bool mValid;
+    const uint32_t mWindow;
+    const double mSigma;
+    struct disk_perf mMean;
+    struct disk_perf mStd;
+
+    void update_mean();
+    void update_std();
+    void add(struct disk_perf* perf);
+    void evict(struct disk_perf* perf);
+    bool detect(struct disk_perf* perf);
+
+    void update(struct disk_stats* stats);
+
+public:
+    disk_stats_monitor(uint32_t window_size = 5, double sigma = 1.0) :
+            mStall(false),
+            mValid(false),
+            mWindow(window_size),
+            mSigma(sigma) {
+        memset(&mPrevious, 0, sizeof(mPrevious));
+        memset(&mMean, 0, sizeof(mMean));
+        memset(&mStd, 0, sizeof(mStd));
+
+        if (access(MMC_DISK_STATS_PATH, R_OK) >= 0) {
+            DISK_STATS_PATH = MMC_DISK_STATS_PATH;
+        } else {
+            DISK_STATS_PATH = SDA_DISK_STATS_PATH;
+        }
+    }
+    void update(void);
+};
+
+class disk_stats_publisher {
+private:
+    FRIEND_TEST(storaged_test, disk_stats_publisher);
+    const char* DISK_STATS_PATH;
+    struct disk_stats mAccumulate;
+    struct disk_stats mPrevious;
+public:
+    disk_stats_publisher(void) {
+        memset(&mAccumulate, 0, sizeof(struct disk_stats));
+        memset(&mPrevious, 0, sizeof(struct disk_stats));
+
+        if (access(MMC_DISK_STATS_PATH, R_OK) >= 0) {
+            DISK_STATS_PATH = MMC_DISK_STATS_PATH;
+        } else {
+            DISK_STATS_PATH = SDA_DISK_STATS_PATH;
+        }
+    }
+
+    ~disk_stats_publisher(void) {}
+    void publish(void);
+    void update(void);
+};
+
+class emmc_info_t {
+private:
+    struct emmc_info mInfo;
+    bool mValid;
+    int mFdEmmc;
+public:
+    emmc_info_t(void) :
+            mValid(false),
+            mFdEmmc(-1) {
+        memset(&mInfo, 0, sizeof(struct emmc_info));
+    }
+    ~emmc_info_t(void) {}
+
+    void publish(void);
+    void update(void);
+    void set_emmc_fd(int fd) {
+        mFdEmmc = fd;
+    }
+};
+
+// Periodic chores intervals in seconds
+#define DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT ( 20 )
+#define DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH ( 60 )
+#define DEFAULT_PERIODIC_CHORES_INTERVAL_EMMC_INFO_PUBLISH ( 60 * 2 )
+
+struct storaged_config {
+    int periodic_chores_interval_unit;
+    int periodic_chores_interval_disk_stats_publish;
+    int periodic_chores_interval_emmc_info_publish;
+    bool proc_taskio_readable;  // are /proc/[pid]/{io, comm, cmdline, stat} all readable
+    bool emmc_available;        // whether eMMC est_csd file is readable
+    bool diskstats_available;   // whether diskstats is accessible
+};
+
+class storaged_t {
+private:
+    time_t mTimer;
+    storaged_config mConfig;
+    disk_stats_publisher mDiskStats;
+    disk_stats_monitor mDsm;
+    emmc_info_t mEmmcInfo;
+    tasks_t mTasks;
+    time_t mStarttime;
+public:
+    storaged_t(void);
+    ~storaged_t() {}
+    void event(void);
+    void pause(void) {
+        sleep(mConfig.periodic_chores_interval_unit);
+    }
+    void set_unit_interval(int unit) {
+        mConfig.periodic_chores_interval_unit = unit;
+    }
+    void set_diskstats_interval(int disk_stats) {
+        mConfig.periodic_chores_interval_disk_stats_publish = disk_stats;
+    }
+    void set_emmc_interval(int emmc_info) {
+        mConfig.periodic_chores_interval_emmc_info_publish = emmc_info;
+    }
+    std::vector<struct task_info> get_tasks(void) {
+        // There could be a race when get_tasks() and the main thread is updating at the same time
+        // While update_running_tasks() is updating the critical sections at the end of the function
+        // all together atomically, the final state of task_t can only be either the main thread's
+        // update or this update. Since the race can only occur when both threads are updating
+        // "simultaneously", either final state is acceptable.
+        mTasks.update_running_tasks();
+        return mTasks.get_tasks();
+    }
+
+    void set_privileged_fds(int fd_emmc) {
+        mEmmcInfo.set_emmc_fd(fd_emmc);
+    }
+
+    time_t get_starttime(void) {
+        return mStarttime;
+    }
+};
+
+// Eventlog tag
+// The content must match the definition in EventLogTags.logtags
+#define EVENTLOGTAG_DISKSTATS ( 2732 )
+#define EVENTLOGTAG_EMMCINFO ( 2733 )
+
+#endif /* _STORAGED_H_ */
diff --git a/storaged/include/storaged_service.h b/storaged/include/storaged_service.h
new file mode 100644
index 0000000..64a9c81
--- /dev/null
+++ b/storaged/include/storaged_service.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _STORAGED_SERVICE_H_
+#define _STORAGED_SERVICE_H_
+
+#include <vector>
+
+#include <binder/IInterface.h>
+#include <binder/IBinder.h>
+
+#include "storaged.h"
+
+using namespace android;
+
+// Interface
+class IStoraged : public IInterface {
+public:
+    enum {
+        DUMPTASKS = IBinder::FIRST_CALL_TRANSACTION,
+    };
+    // Request the service to run the test function
+    virtual std::vector<struct task_info> dump_tasks(const char* option) = 0;
+
+    DECLARE_META_INTERFACE(Storaged);
+};
+
+// Client
+class BpStoraged : public BpInterface<IStoraged> {
+public:
+    BpStoraged(const sp<IBinder>& impl) : BpInterface<IStoraged>(impl){};
+    virtual std::vector<struct task_info> dump_tasks(const char* option);
+};
+
+// Server
+class BnStoraged : public BnInterface<IStoraged> {
+    virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0);
+};
+
+class Storaged : public BnStoraged {
+    virtual std::vector<struct task_info> dump_tasks(const char* option);
+};
+
+sp<IStoraged> get_storaged_service();
+
+#endif /* _STORAGED_SERVICE_H_ */
\ No newline at end of file
diff --git a/storaged/include/storaged_utils.h b/storaged/include/storaged_utils.h
new file mode 100644
index 0000000..b0e5146
--- /dev/null
+++ b/storaged/include/storaged_utils.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _STORAGED_UTILS_H_
+#define _STORAGED_UTILS_H_
+
+#include <stdint.h>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "storaged.h"
+
+// Diskstats
+bool parse_disk_stats(const char* disk_stats_path, struct disk_stats* stats);
+struct disk_perf get_disk_perf(struct disk_stats* stats);
+struct disk_stats get_inc_disk_stats(struct disk_stats* prev, struct disk_stats* curr);
+void add_disk_stats(struct disk_stats* src, struct disk_stats* dst);
+bool parse_emmc_ecsd(int ext_csd_fd, struct emmc_info* info);
+
+// Task I/O
+bool parse_task_info(uint32_t pid, struct task_info* info);
+void sort_running_tasks_info(std::vector<struct task_info> &tasks);
+
+// Logging
+void log_console_running_tasks_info(std::vector<struct task_info> tasks);
+void log_kernel_disk_stats(struct disk_stats* stats, const char* type);
+void log_kernel_disk_perf(struct disk_perf* perf, const char* type);
+void log_kernel_emmc_info(struct emmc_info* info);
+
+void log_event_disk_stats(struct disk_stats* stats, const char* type);
+void log_event_emmc_info(struct emmc_info* info_);
+#endif /* _STORAGED_UTILS_H_ */
\ No newline at end of file
diff --git a/storaged/main.cpp b/storaged/main.cpp
new file mode 100644
index 0000000..31ada68
--- /dev/null
+++ b/storaged/main.cpp
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "storaged"
+#define KLOG_LEVEL 6
+
+#include <fcntl.h>
+#include <getopt.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <sys/capability.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <vector>
+
+#include <android-base/macros.h>
+#include <android-base/stringprintf.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
+#include <binder/IPCThreadState.h>
+#include <cutils/android_get_control_file.h>
+#include <cutils/klog.h>
+#include <cutils/sched_policy.h>
+#include <private/android_filesystem_config.h>
+
+#include <storaged.h>
+#include <storaged_service.h>
+#include <storaged_utils.h>
+
+storaged_t storaged;
+
+static int drop_privs() {
+    // privilege setting
+    struct sched_param param;
+    memset(&param, 0, sizeof(param));
+
+    if (set_sched_policy(0, SP_BACKGROUND) < 0) return -1;
+
+    if (sched_setscheduler((pid_t) 0, SCHED_BATCH, &param) < 0) return -1;
+
+    if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) return -1;
+
+    if (prctl(PR_SET_KEEPCAPS, 1) < 0) return -1;
+
+    std::unique_ptr<struct _cap_struct, int(*)(void *)> caps(cap_init(), cap_free);
+    if (cap_clear(caps.get()) < 0) return -1;
+    cap_value_t cap_value[] = {
+        CAP_SETGID,
+        CAP_SETUID,
+        CAP_SYS_PTRACE // allow access to proc/<pid>/io as non-root user
+    };
+    if (cap_set_flag(caps.get(), CAP_PERMITTED,
+                     arraysize(cap_value), cap_value,
+                     CAP_SET) < 0) return -1;
+    if (cap_set_flag(caps.get(), CAP_EFFECTIVE,
+                     arraysize(cap_value), cap_value,
+                     CAP_SET) < 0) return -1;
+    if (cap_set_proc(caps.get()) < 0)
+        return -1;
+
+    gid_t groups[] = { AID_READPROC };
+
+    if (setgroups(sizeof(groups) / sizeof(groups[0]), groups) == -1) return -1;
+
+    if (setgid(AID_SYSTEM) != 0) return -1;
+
+    if (setuid(AID_SYSTEM) != 0) return -1;
+
+    if (cap_set_flag(caps.get(), CAP_PERMITTED, 2, cap_value, CAP_CLEAR) < 0) return -1;
+    if (cap_set_flag(caps.get(), CAP_EFFECTIVE, 2, cap_value, CAP_CLEAR) < 0) return -1;
+    if (cap_set_proc(caps.get()) < 0)
+        return -1;
+
+    return 0;
+}
+
+// Function of storaged's main thread
+extern int fd_dmesg;
+void* storaged_main(void* s) {
+    storaged_t* storaged = (storaged_t*)s;
+
+    if (fd_dmesg >= 0) {
+        static const char start_message[] = {KMSG_PRIORITY(LOG_INFO),
+            's', 't', 'o', 'r', 'a', 'g', 'e', 'd', ':', ' ', 'S', 't', 'a', 'r', 't', '\n'};
+        write(fd_dmesg, start_message, sizeof(start_message));
+    }
+
+    for (;;) {
+        storaged->event();
+        storaged->pause();
+    }
+    return NULL;
+}
+
+static void help_message(void) {
+    printf("usage: storaged [OPTION]\n");
+    printf("  -d    --dump                  Dump task I/O usage to stdout\n");
+    printf("  -s    --start                 Start storaged (default)\n");
+    printf("        --emmc=INTERVAL         Set publish interval of emmc lifetime information (in days)\n");
+    printf("        --diskstats=INTERVAL    Set publish interval of diskstats (in hours)\n");
+    printf("        --unit=INTERVAL         Set storaged's refresh interval (in seconds)\n");
+    fflush(stdout);
+}
+
+#define HOUR_TO_SEC ( 3600 )
+#define DAY_TO_SEC ( 3600 * 24 )
+
+int main(int argc, char** argv) {
+    klog_set_level(KLOG_LEVEL);
+    int flag_main_service = 0;
+    int flag_dump_task = 0;
+    int flag_config = 0;
+    int unit_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT;
+    int diskstats_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH;
+    int emmc_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_EMMC_INFO_PUBLISH;
+    int fd_emmc = -1;
+    int opt;
+
+    for (;;) {
+        int opt_idx = 0;
+        static struct option long_options[] = {
+            {"start",       no_argument,        0, 's'},
+            {"kill",        no_argument,        0, 'k'},
+            {"dump",        no_argument,        0, 'd'},
+            {"help",        no_argument,        0, 'h'},
+            {"unit",        required_argument,  0,  0 },
+            {"diskstats",   required_argument,  0,  0 },
+            {"emmc",        required_argument,  0,  0 }
+        };
+        opt = getopt_long(argc, argv, ":skdh0", long_options, &opt_idx);
+        if (opt == -1) {
+            break;
+        }
+
+        switch (opt) {
+        case 0:
+            printf("option %s", long_options[opt_idx].name);
+            if (optarg) {
+                printf(" with arg %s", optarg);
+                if (strcmp(long_options[opt_idx].name, "unit") == 0) {
+                    unit_interval = atoi(optarg);
+                    if (unit_interval == 0) {
+                        fprintf(stderr, "Invalid argument. Option %s requires an integer argument greater than 0.\n",
+                                long_options[opt_idx].name);
+                        help_message();
+                        return -1;
+                    }
+                } else if (strcmp(long_options[opt_idx].name, "diskstats") == 0) {
+                    diskstats_interval = atoi(optarg) * HOUR_TO_SEC;
+                    if (diskstats_interval == 0) {
+                        fprintf(stderr, "Invalid argument. Option %s requires an integer argument greater than 0.\n",
+                                long_options[opt_idx].name);
+                        help_message();
+                        return -1;
+                    }
+
+                } else if (strcmp(long_options[opt_idx].name, "emmc") == 0) {
+                    emmc_interval = atoi(optarg) * DAY_TO_SEC;
+                    if (diskstats_interval == 0) {
+                        fprintf(stderr, "Invalid argument. Option %s requires an integer argument greater than 0.\n",
+                                long_options[opt_idx].name);
+                        help_message();
+                        return -1;
+                    }
+                }
+                flag_config = 1;
+            } else {
+                fprintf(stderr, "Invalid argument. Option %s requires an argument.\n",
+                        long_options[opt_idx].name);
+                help_message();
+                return -1;
+            }
+            printf("\n");
+            break;
+        case 's':
+            flag_main_service = 1;
+            break;
+        case 'd':
+            flag_dump_task = 1;
+            break;
+        case 'h':
+            help_message();
+            return 0;
+        case '?':
+        default:
+            fprintf(stderr, "no supported option\n");
+            help_message();
+            return -1;
+        }
+    }
+
+    if (argc == 1) {
+        flag_main_service = 1;
+    }
+
+    if (flag_main_service && flag_dump_task) {
+        fprintf(stderr, "Invalid arguments. Option \"start\" and \"dump\" cannot be used together.\n");
+        help_message();
+        return -1;
+    }
+
+    if (flag_config && flag_dump_task) {
+        fprintf(stderr, "Invalid arguments. Cannot set configs in \'dump\' option.\n");
+        help_message();
+        return -1;
+    }
+
+    if (flag_main_service) { // start main thread
+        static const char dev_kmsg[] = "/dev/kmsg";
+        fd_dmesg = android_get_control_file(dev_kmsg);
+        if (fd_dmesg < 0)
+            fd_dmesg = TEMP_FAILURE_RETRY(open(dev_kmsg, O_WRONLY));
+
+        static const char mmc0_ext_csd[] = "/d/mmc0/mmc0:0001/ext_csd";
+        fd_emmc = android_get_control_file(mmc0_ext_csd);
+        if (fd_emmc < 0)
+            fd_emmc = TEMP_FAILURE_RETRY(open(mmc0_ext_csd, O_RDONLY));
+
+        if (drop_privs() != 0) {
+            return -1;
+        }
+
+        storaged.set_privileged_fds(fd_emmc);
+
+        if (flag_config) {
+            storaged.set_unit_interval(unit_interval);
+            storaged.set_diskstats_interval(diskstats_interval);
+            storaged.set_emmc_interval(emmc_interval);
+        }
+
+        // Start the main thread of storaged
+        pthread_t storaged_main_thread;
+        if (pthread_create(&storaged_main_thread, NULL, storaged_main, &storaged)) {
+            if (fd_dmesg >= 0) {
+                std::string error_message = android::base::StringPrintf(
+                    "%s Failed to create main thread\n", kmsg_error_prefix);
+                write(fd_dmesg, error_message.c_str(), error_message.length());
+            }
+            return -1;
+        }
+
+        defaultServiceManager()->addService(String16("storaged"), new Storaged());
+        android::ProcessState::self()->startThreadPool();
+        IPCThreadState::self()->joinThreadPool();
+        pthread_join(storaged_main_thread, NULL);
+
+        close(fd_dmesg);
+        close(fd_emmc);
+
+        return 0;
+    }
+
+    if (flag_dump_task) {
+        sp<IStoraged> storaged_service = get_storaged_service();
+        if (storaged_service == NULL) {
+            fprintf(stderr, "Cannot find storaged service.\nMaybe run storaged --start first?\n");
+            return -1;
+        }
+        std::vector<struct task_info> res = storaged_service->dump_tasks(NULL);
+
+        if (res.size() == 0) {
+            fprintf(stderr, "Task I/O is not readable in this version of kernel.\n");
+            return 0;
+        }
+
+        time_t starttime = storaged.get_starttime();
+
+        if (starttime == (time_t)-1) {
+            fprintf(stderr, "Unknown start time\n");
+        } else {
+            char* time_str = ctime(&starttime);
+            printf("Application I/O was collected by storaged since %s", time_str);
+        }
+
+        sort_running_tasks_info(res);
+        log_console_running_tasks_info(res);
+
+        return 0;
+    }
+
+    return 0;
+}
diff --git a/storaged/storaged.cpp b/storaged/storaged.cpp
new file mode 100644
index 0000000..7b0c3ad
--- /dev/null
+++ b/storaged/storaged.cpp
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "storaged"
+
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <android-base/logging.h>
+
+#include <storaged.h>
+#include <storaged_utils.h>
+
+/* disk_stats_publisher */
+void disk_stats_publisher::publish(void) {
+    // Logging
+    log_kernel_disk_stats(&mAccumulate, "regular");
+    struct disk_perf perf = get_disk_perf(&mAccumulate);
+    log_kernel_disk_perf(&perf, "regular");
+    log_event_disk_stats(&mAccumulate, "regular");
+    // Reset global structures
+    memset(&mAccumulate, 0, sizeof(struct disk_stats));
+}
+
+void disk_stats_publisher::update(void) {
+    struct disk_stats curr;
+    if (parse_disk_stats(DISK_STATS_PATH, &curr)) {
+        struct disk_stats inc = get_inc_disk_stats(&mPrevious, &curr);
+        add_disk_stats(&inc, &mAccumulate);
+#ifdef DEBUG
+//            log_kernel_disk_stats(&mPrevious, "prev stats");
+//            log_kernel_disk_stats(&curr, "curr stats");
+//            log_kernel_disk_stats(&inc, "inc stats");
+//            log_kernel_disk_stats(&mAccumulate, "accumulated stats");
+#endif
+        mPrevious = curr;
+    }
+}
+
+/* disk_stats_monitor */
+void disk_stats_monitor::update_mean() {
+    CHECK(mValid);
+    mMean.read_perf = (uint32_t)mStats.read_perf.get_mean();
+    mMean.read_ios = (uint32_t)mStats.read_ios.get_mean();
+    mMean.write_perf = (uint32_t)mStats.write_perf.get_mean();
+    mMean.write_ios = (uint32_t)mStats.write_ios.get_mean();
+    mMean.queue = (uint32_t)mStats.queue.get_mean();
+}
+
+void disk_stats_monitor::update_std() {
+    CHECK(mValid);
+    mStd.read_perf = (uint32_t)mStats.read_perf.get_std();
+    mStd.read_ios = (uint32_t)mStats.read_ios.get_std();
+    mStd.write_perf = (uint32_t)mStats.write_perf.get_std();
+    mStd.write_ios = (uint32_t)mStats.write_ios.get_std();
+    mStd.queue = (uint32_t)mStats.queue.get_std();
+}
+
+void disk_stats_monitor::add(struct disk_perf* perf) {
+    mStats.read_perf.add(perf->read_perf);
+    mStats.read_ios.add(perf->read_ios);
+    mStats.write_perf.add(perf->write_perf);
+    mStats.write_ios.add(perf->write_ios);
+    mStats.queue.add(perf->queue);
+}
+
+void disk_stats_monitor::evict(struct disk_perf* perf) {
+    mStats.read_perf.evict(perf->read_perf);
+    mStats.read_ios.evict(perf->read_ios);
+    mStats.write_perf.evict(perf->write_perf);
+    mStats.write_ios.evict(perf->write_ios);
+    mStats.queue.evict(perf->queue);
+}
+
+bool disk_stats_monitor::detect(struct disk_perf* perf) {
+    return ((double)perf->queue >= (double)mMean.queue + mSigma * (double)mStd.queue) &&
+            ((double)perf->read_perf < (double)mMean.read_perf - mSigma * (double)mStd.read_perf) &&
+            ((double)perf->write_perf < (double)mMean.write_perf - mSigma * (double)mStd.write_perf);
+}
+
+void disk_stats_monitor::update(struct disk_stats* stats) {
+    struct disk_stats inc = get_inc_disk_stats(&mPrevious, stats);
+    struct disk_perf perf = get_disk_perf(&inc);
+    // Update internal data structures
+    if (LIKELY(mValid)) {
+        CHECK_EQ(mBuffer.size(), mWindow);
+
+        if (UNLIKELY(detect(&perf))) {
+            mStall = true;
+            add_disk_stats(&inc, &mAccumulate);
+#ifdef DEBUG
+            log_kernel_disk_perf(&mMean, "stalled_mean");
+            log_kernel_disk_perf(&mStd, "stalled_std");
+#endif
+        } else {
+            if (mStall) {
+                log_kernel_disk_stats(&mAccumulate, "stalled");
+                struct disk_perf acc_perf = get_disk_perf(&mAccumulate);
+                log_kernel_disk_perf(&acc_perf, "stalled");
+
+                log_event_disk_stats(&mAccumulate, "stalled");
+                mStall = false;
+                memset(&mAccumulate, 0, sizeof(mAccumulate));
+            }
+        }
+
+        evict(&mBuffer.front());
+        mBuffer.pop();
+        add(&perf);
+        mBuffer.push(perf);
+
+        update_mean();
+        update_std();
+
+    } else { /* mValid == false */
+        CHECK_LT(mBuffer.size(), mWindow);
+        add(&perf);
+        mBuffer.push(perf);
+        if (mBuffer.size() == mWindow) {
+            mValid = true;
+            update_mean();
+            update_std();
+        }
+    }
+
+    mPrevious = *stats;
+}
+
+void disk_stats_monitor::update(void) {
+    struct disk_stats curr;
+    if (LIKELY(parse_disk_stats(DISK_STATS_PATH, &curr))) {
+        update(&curr);
+    }
+}
+
+/* emmc_info_t */
+void emmc_info_t::publish(void) {
+    if (mValid) {
+        log_kernel_emmc_info(&mInfo);
+        log_event_emmc_info(&mInfo);
+    }
+}
+
+void emmc_info_t::update(void) {
+    if (mFdEmmc >= 0) {
+        mValid = parse_emmc_ecsd(mFdEmmc, &mInfo);
+    }
+}
+
+/* storaged_t */
+storaged_t::storaged_t(void) {
+    mConfig.emmc_available = (access(EMMC_ECSD_PATH, R_OK) >= 0);
+
+    if (access(MMC_DISK_STATS_PATH, R_OK) < 0 && access(SDA_DISK_STATS_PATH, R_OK) < 0) {
+        mConfig.diskstats_available = false;
+    } else {
+        mConfig.diskstats_available = true;
+    }
+
+    mConfig.proc_taskio_readable = true;
+    const char* test_paths[] = {"/proc/1/io", "/proc/1/comm", "/proc/1/cmdline", "/proc/1/stat"};
+    for (uint i = 0; i < sizeof(test_paths) / sizeof(const char*); ++i) {
+        if (access(test_paths[i], R_OK) < 0) {
+            mConfig.proc_taskio_readable = false;
+            break;
+        }
+    }
+
+    mConfig.periodic_chores_interval_unit = DEFAULT_PERIODIC_CHORES_INTERVAL_UNIT;
+    mConfig.periodic_chores_interval_disk_stats_publish = DEFAULT_PERIODIC_CHORES_INTERVAL_DISK_STATS_PUBLISH;
+    mConfig.periodic_chores_interval_emmc_info_publish = DEFAULT_PERIODIC_CHORES_INTERVAL_EMMC_INFO_PUBLISH;
+
+    mStarttime = time(NULL);
+}
+
+void storaged_t::event(void) {
+    if (mConfig.diskstats_available) {
+        mDiskStats.update();
+        mDsm.update();
+        if (mTimer && (mTimer % mConfig.periodic_chores_interval_disk_stats_publish) == 0) {
+            mDiskStats.publish();
+        }
+    }
+
+    if (mConfig.proc_taskio_readable) {
+        mTasks.update_running_tasks();
+    }
+
+    if (mConfig.emmc_available && mTimer &&
+            (mTimer % mConfig.periodic_chores_interval_emmc_info_publish) == 0) {
+        mEmmcInfo.update();
+        mEmmcInfo.publish();
+    }
+
+    mTimer += mConfig.periodic_chores_interval_unit;
+}
diff --git a/storaged/storaged.rc b/storaged/storaged.rc
new file mode 100644
index 0000000..f72521c
--- /dev/null
+++ b/storaged/storaged.rc
@@ -0,0 +1,5 @@
+service storaged /system/bin/storaged
+    class main
+    file /d/mmc0/mmc0:0001/ext_csd r
+    file /dev/kmsg w
+    group root readproc
diff --git a/storaged/storaged_service.cpp b/storaged/storaged_service.cpp
new file mode 100644
index 0000000..aa38ceb
--- /dev/null
+++ b/storaged/storaged_service.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vector>
+
+#include <binder/IBinder.h>
+#include <binder/IInterface.h>
+
+#include <binder/IPCThreadState.h>
+#include <binder/IServiceManager.h>
+
+#include <storaged.h>
+#include <storaged_service.h>
+
+extern storaged_t storaged;
+
+std::vector<struct task_info> BpStoraged::dump_tasks(const char* /*option*/) {
+    Parcel data, reply;
+    data.writeInterfaceToken(IStoraged::getInterfaceDescriptor());
+
+    remote()->transact(DUMPTASKS, data, &reply);
+
+    uint32_t res_size = reply.readInt32();
+    std::vector<struct task_info> res(res_size);
+    for (auto&& task : res) {
+        reply.read(&task, sizeof(task));
+    }
+    return res;
+}
+
+IMPLEMENT_META_INTERFACE(Storaged, "Storaged");
+
+status_t BnStoraged::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
+    data.checkInterface(this);
+
+    switch(code) {
+        case DUMPTASKS: {
+                std::vector<struct task_info> res = dump_tasks(NULL);
+
+                reply->writeInt32(res.size());
+                for (auto task : res) {
+                    reply->write(&task, sizeof(task));
+                }
+                return NO_ERROR;
+            }
+            break;
+        default:
+            return BBinder::onTransact(code, data, reply, flags);
+    }
+}
+std::vector<struct task_info> Storaged::dump_tasks(const char* /* option */) {
+    return storaged.get_tasks();
+}
+
+sp<IStoraged> get_storaged_service() {
+    sp<IServiceManager> sm = defaultServiceManager();
+    if (sm == NULL) return NULL;
+
+    sp<IBinder> binder = sm->getService(String16("storaged"));
+    if (binder == NULL) return NULL;
+
+    sp<IStoraged> storaged = interface_cast<IStoraged>(binder);
+
+    return storaged;
+}
\ No newline at end of file
diff --git a/storaged/storaged_utils.cpp b/storaged/storaged_utils.cpp
new file mode 100644
index 0000000..5e04888
--- /dev/null
+++ b/storaged/storaged_utils.cpp
@@ -0,0 +1,580 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "storaged"
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <linux/time.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <sstream>
+#include <string>
+#include <unordered_map>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+#include <cutils/klog.h>
+#include <log/log.h>
+#include <log/log_event_list.h>
+
+#include <storaged.h>
+#include <storaged_utils.h>
+
+#define SECTOR_SIZE ( 512 )
+#define SEC_TO_MSEC ( 1000 )
+#define MSEC_TO_USEC ( 1000 )
+#define USEC_TO_NSEC ( 1000 )
+
+int fd_dmesg = -1;
+
+bool parse_disk_stats(const char* disk_stats_path, struct disk_stats* stats) {
+    // Get time
+    struct timespec ts;
+    // Use monotonic to exclude suspend time so that we measure IO bytes/sec
+    // when system is running.
+    int ret = clock_gettime(CLOCK_MONOTONIC, &ts);
+    if (ret < 0) {
+        if (fd_dmesg >= 0) {
+            std::string error_message = android::base::StringPrintf(
+                "%s clock_gettime() failed with errno %d\n",
+                kmsg_error_prefix, ret);
+            write(fd_dmesg, error_message.c_str(), error_message.length());
+        }
+        return false;
+    }
+
+    std::string buffer;
+    if (!android::base::ReadFileToString(disk_stats_path, &buffer)) {
+        if (fd_dmesg >= 0) {
+            std::string error_message = android::base::StringPrintf(
+                "%s %s: ReadFileToString failed.\n", kmsg_error_prefix, disk_stats_path);
+            write(fd_dmesg, error_message.c_str(), error_message.length());
+        }
+        return false;
+    }
+
+    // Regular diskstats entries
+    std::stringstream ss(buffer);
+    for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
+        ss >> *((uint64_t*)stats + i);
+    }
+    // Other entries
+    stats->start_time = 0;
+    stats->end_time = (uint64_t)ts.tv_sec * SEC_TO_MSEC +
+        ts.tv_nsec / (MSEC_TO_USEC * USEC_TO_NSEC);
+    stats->counter = 1;
+    stats->io_avg = (double)stats->io_in_flight;
+    return true;
+}
+
+struct disk_perf get_disk_perf(struct disk_stats* stats) {
+    struct disk_perf perf;
+    memset(&perf, 0, sizeof(struct disk_perf));  // initialize
+
+    if (stats->io_ticks) {
+        if (stats->read_ticks) {
+            unsigned long long divisor = stats->read_ticks * stats->io_ticks;
+            perf.read_perf = ((unsigned long long)SECTOR_SIZE *
+                                        stats->read_sectors *
+                                        stats->io_in_queue +
+                                        (divisor >> 1)) /
+                                            divisor;
+            perf.read_ios = ((unsigned long long)SEC_TO_MSEC *
+                                        stats->read_ios *
+                                        stats->io_in_queue +
+                                        (divisor >> 1)) /
+                                            divisor;
+        }
+        if (stats->write_ticks) {
+            unsigned long long divisor = stats->write_ticks * stats->io_ticks;
+                        perf.write_perf = ((unsigned long long)SECTOR_SIZE *
+                                                    stats->write_sectors *
+                                                    stats->io_in_queue +
+                                                    (divisor >> 1)) /
+                                                        divisor;
+                        perf.write_ios = ((unsigned long long)SEC_TO_MSEC *
+                                                    stats->write_ios *
+                                                    stats->io_in_queue +
+                                                    (divisor >> 1)) /
+                                                        divisor;
+        }
+        perf.queue = (stats->io_in_queue + (stats->io_ticks >> 1)) /
+                                stats->io_ticks;
+    }
+    return perf;
+}
+
+struct disk_stats get_inc_disk_stats(struct disk_stats* prev, struct disk_stats* curr) {
+    struct disk_stats inc;
+    for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
+        if (i == DISK_STATS_IO_IN_FLIGHT_IDX) {
+            continue;
+        }
+
+        *((uint64_t*)&inc + i) =
+                *((uint64_t*)curr + i) - *((uint64_t*)prev + i);
+    }
+    // io_in_flight is exception
+    inc.io_in_flight = curr->io_in_flight;
+
+    inc.start_time = prev->end_time;
+    inc.end_time = curr->end_time;
+    inc.io_avg = curr->io_avg;
+    inc.counter = 1;
+
+    return inc;
+}
+
+// Add src to dst
+void add_disk_stats(struct disk_stats* src, struct disk_stats* dst) {
+    if (dst->end_time != 0 && dst->end_time != src->start_time && fd_dmesg >= 0) {
+        std::string warning_message = android::base::StringPrintf(
+            "%s Two dis-continuous periods of diskstats are added. "
+            "dst end with %jd, src start with %jd\n",
+            kmsg_warning_prefix, dst->end_time, src->start_time);
+
+        write(fd_dmesg, warning_message.c_str(), warning_message.length());
+    }
+
+    for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
+        if (i == DISK_STATS_IO_IN_FLIGHT_IDX) {
+            continue;
+        }
+
+        *((uint64_t*)dst + i) += *((uint64_t*)src + i);
+    }
+
+    dst->io_in_flight = src->io_in_flight;
+    if (dst->counter + src->counter) {
+        dst->io_avg = ((dst->io_avg * dst->counter) + (src->io_avg * src->counter)) /
+                        (dst->counter + src->counter);
+    }
+    dst->counter += src->counter;
+    dst->end_time = src->end_time;
+    if (dst->start_time == 0) {
+        dst->start_time = src->start_time;
+    }
+}
+
+bool parse_emmc_ecsd(int ext_csd_fd, struct emmc_info* info) {
+    CHECK(ext_csd_fd >= 0);
+    struct hex {
+        char str[2];
+    };
+    // List of interesting offsets
+    static const size_t EXT_CSD_REV_IDX = 192 * sizeof(hex);
+    static const size_t EXT_PRE_EOL_INFO_IDX = 267 * sizeof(hex);
+    static const size_t EXT_DEVICE_LIFE_TIME_EST_A_IDX = 268 * sizeof(hex);
+    static const size_t EXT_DEVICE_LIFE_TIME_EST_B_IDX = 269 * sizeof(hex);
+
+    // Read file
+    CHECK(lseek(ext_csd_fd, 0, SEEK_SET) == 0);
+    std::string buffer;
+    if (!android::base::ReadFdToString(ext_csd_fd, &buffer)) {
+        if (fd_dmesg >= 0) {
+            std::string error_message = android::base::StringPrintf(
+                "%s ReadFdToString failed.\n", kmsg_error_prefix);
+            write(fd_dmesg, error_message.c_str(), error_message.length());
+        }
+        return false;
+    }
+
+    if (buffer.length() < EXT_CSD_FILE_MIN_SIZE) {
+        if (fd_dmesg >= 0) {
+            std::string error_message = android::base::StringPrintf(
+                "%s EMMC ext csd file has truncated content. File length: %d\n",
+                kmsg_error_prefix, (int)buffer.length());
+            write(fd_dmesg, error_message.c_str(), error_message.length());
+        }
+        return false;
+    }
+
+    std::string sub;
+    std::stringstream ss;
+    // Parse EXT_CSD_REV
+    int ext_csd_rev = -1;
+    sub = buffer.substr(EXT_CSD_REV_IDX, sizeof(hex));
+    ss << sub;
+    ss >> std::hex >> ext_csd_rev;
+    if (ext_csd_rev < 0) {
+        if (fd_dmesg >= 0) {
+            std::string error_message = android::base::StringPrintf(
+                "%s Failure on parsing EXT_CSD_REV.\n", kmsg_error_prefix);
+            write(fd_dmesg, error_message.c_str(), error_message.length());
+        }
+        return false;
+    }
+    ss.clear();
+
+    static const char* ver_str[] = {
+        "4.0", "4.1", "4.2", "4.3", "Obsolete", "4.41", "4.5", "5.0"
+    };
+
+    strncpy(info->mmc_ver,
+            (ext_csd_rev < (int)(sizeof(ver_str) / sizeof(ver_str[0]))) ?
+                           ver_str[ext_csd_rev] :
+                           "Unknown",
+            MMC_VER_STR_LEN);
+
+    if (ext_csd_rev < 7) {
+        return 0;
+    }
+
+    // Parse EXT_PRE_EOL_INFO
+    info->eol = -1;
+    sub = buffer.substr(EXT_PRE_EOL_INFO_IDX, sizeof(hex));
+    ss << sub;
+    ss >> std::hex >> info->eol;
+    if (info->eol < 0) {
+        if (fd_dmesg >= 0) {
+            std::string error_message = android::base::StringPrintf(
+                "%s Failure on parsing EXT_PRE_EOL_INFO.\n", kmsg_error_prefix);
+            write(fd_dmesg, error_message.c_str(), error_message.length());
+        }
+        return false;
+    }
+    ss.clear();
+
+    // Parse DEVICE_LIFE_TIME_EST
+    info->lifetime_a = -1;
+    sub = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_A_IDX, sizeof(hex));
+    ss << sub;
+    ss >> std::hex >> info->lifetime_a;
+    if (info->lifetime_a < 0) {
+        if (fd_dmesg >= 0) {
+            std::string error_message = android::base::StringPrintf(
+                "%s Failure on parsing EXT_DEVICE_LIFE_TIME_EST_TYP_A.\n", kmsg_error_prefix);
+            write(fd_dmesg, error_message.c_str(), error_message.length());
+        }
+        return false;
+    }
+    ss.clear();
+
+    info->lifetime_b = -1;
+    sub = buffer.substr(EXT_DEVICE_LIFE_TIME_EST_B_IDX, sizeof(hex));
+    ss << sub;
+    ss >> std::hex >> info->lifetime_b;
+    if (info->lifetime_b < 0) {
+        if (fd_dmesg >= 0) {
+            std::string error_message = android::base::StringPrintf(
+                "%s Failure on parsing EXT_DEVICE_LIFE_TIME_EST_TYP_B.\n", kmsg_error_prefix);
+            write(fd_dmesg, error_message.c_str(), error_message.length());
+        }
+        return false;
+    }
+    ss.clear();
+
+    return true;
+}
+
+#define PROC_DIR "/proc/"
+#define PROC_STAT_STARTTIME_IDX ( 22 ) // This index is 1 based according to the linux proc man page
+bool parse_task_info(uint32_t pid, struct task_info* info) {
+    std::string buffer;
+    std::string pid_str = std::to_string(pid);
+    info->pid = pid;
+
+    // Get task I/O
+    std::string task_io_path = android::base::StringPrintf(PROC_DIR "%s/io", pid_str.c_str());
+    if (!android::base::ReadFileToString(task_io_path, &buffer)) return false;
+
+    std::stringstream ss(buffer);
+    std::string title;
+
+    ss >> title >> info->rchar
+       >> title >> info->wchar
+       >> title >> info->syscr
+       >> title >> info->syscw
+       >> title >> info->read_bytes
+       >> title >> info->write_bytes
+       >> title >> info->cancelled_write_bytes;
+    ss.clear();
+
+    // Get cmd string
+    std::string task_cmdline_path = android::base::StringPrintf(PROC_DIR "%u/cmdline", pid);
+    if (!android::base::ReadFileToString(task_cmdline_path, &buffer)) return false;
+    strcpy(info->cmd, android::base::Trim(buffer).c_str());
+
+    if (info->cmd[0] == '\0') {
+        std::string task_comm_path = android::base::StringPrintf(PROC_DIR "%u/comm", pid);
+        if (!android::base::ReadFileToString(task_comm_path, &buffer)) return false;
+        strcpy(info->cmd, android::base::Trim(buffer).c_str());
+    }
+
+    // Get task start time
+    std::string task_stat_path = android::base::StringPrintf(PROC_DIR "%u/stat", pid);
+    if (!android::base::ReadFileToString(task_stat_path, &buffer)) return false;
+
+    std::vector<std::string> stat_parts = android::base::Split(buffer, " ");
+    info->starttime = atoll(stat_parts[PROC_STAT_STARTTIME_IDX - 1].c_str());
+
+    return true;
+}
+
+static bool is_pid(char* d_name) {
+    if (!d_name || d_name[0] == '\0') return false;
+    char* c = d_name;
+    while (*c) {
+        if (!isdigit(*c)) return false;
+        ++c;
+    }
+    return true;
+}
+
+static bool cmp_task_info(struct task_info i, struct task_info j) {
+    if (i.write_bytes + i.read_bytes != j.write_bytes + j.read_bytes) {
+        return i.write_bytes + i.read_bytes > j.write_bytes + j.read_bytes;
+    }
+    if (i.wchar + i.rchar != j.wchar + j.rchar) {
+        return i.wchar + i.rchar > j.wchar + j.rchar;
+    }
+    if (i.syscw + i.syscr != j.syscw + j.syscr) {
+        return i.syscw + i.syscr > j.syscw + j.syscr;
+    }
+
+    return strcmp(i.cmd, j.cmd) < 0;
+}
+
+std::unordered_map<uint32_t, struct task_info> tasks_t::get_running_tasks() {
+    std::unordered_map<uint32_t, struct task_info> retval;
+    std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(PROC_DIR), closedir);
+    CHECK(dir != NULL);
+    struct dirent* dp;
+
+    for (;;) {
+        if ((dp = readdir(dir.get())) == NULL) break;
+        if (!is_pid(dp->d_name)) continue;
+
+        uint32_t pid = atol(dp->d_name);
+        struct task_info info;
+        if (parse_task_info(pid, &info)) {
+            retval[pid] = info;
+        }
+    }
+    return retval;
+}
+
+static void add_task_info(struct task_info* src, struct task_info* dst) {
+    CHECK(strcmp(src->cmd, dst->cmd) == 0);
+
+    dst->pid = 0;
+    dst->rchar += src->rchar;
+    dst->wchar += src->wchar;
+    dst->syscr += src->syscr;
+    dst->syscw += src->syscw;
+    dst->read_bytes += src->read_bytes;
+    dst->write_bytes += src->write_bytes;
+    dst->cancelled_write_bytes += src->cancelled_write_bytes;
+    dst->starttime = 0;
+}
+
+void tasks_t::update_running_tasks(void) {
+    std::unordered_map<uint32_t, struct task_info> tasks_latest = get_running_tasks();
+    std::unordered_map<std::string, struct task_info> tasks_old = mOld;
+
+    for (auto t : mRunning) {
+        uint32_t pid = t.first;
+        // old task on mRunning still exist on tasks_latest
+        if (tasks_latest.find(pid) != tasks_latest.end() &&
+                tasks_latest[pid].starttime == t.second.starttime) {
+            continue;
+        } else {
+            // This branch will handle 2 cases:
+            // - Task get killed between the 2 samplings
+            // - Task get killed and its pid is reused
+            std::string cmd = t.second.cmd;
+            struct task_info info = t.second;
+
+            if (tasks_old.find(cmd) == tasks_old.end()) {
+                tasks_old[cmd] = info;
+            } else {
+                add_task_info(&info, &tasks_old[cmd]);
+            }
+        }
+    }
+    {   // update critical area
+        // this is really fast!
+        std::unique_ptr<lock_t> lock(new lock_t(&mSem));
+        mRunning = tasks_latest;
+        mOld = tasks_old;
+    }
+
+}
+
+std::vector<struct task_info> tasks_t::get_tasks(void) {
+    std::unique_ptr<lock_t> lock(new lock_t(&mSem));
+    std::unordered_map<std::string, struct task_info> tasks_map = mOld;
+
+    for (auto i : mRunning) {
+        std::string cmd = i.second.cmd;
+        if (tasks_map.find(cmd) == tasks_map.end()) {
+            tasks_map[cmd] = i.second;
+        } else {
+            add_task_info(&i.second, &tasks_map[cmd]);
+        }
+    }
+
+    std::vector<struct task_info> retval(tasks_map.size());
+    int idx = 0;
+    for (auto i : tasks_map) {
+        retval[idx++]  = i.second;
+    }
+
+    return retval;
+}
+
+void sort_running_tasks_info(std::vector<struct task_info> &tasks) {
+    std::sort(tasks.begin(), tasks.end(), cmp_task_info);
+}
+
+/* Logging functions */
+void log_console_running_tasks_info(std::vector<struct task_info> tasks) {
+// Sample Output:
+//       Application           Read          Write           Read          Write           Read          Write      Cancelled
+//              Name     Characters     Characters       Syscalls       Syscalls          Bytes          Bytes     Writebytes
+//        ----------     ----------     ----------     ----------     ----------     ----------     ----------     ----------
+//          zygote64       37688308        3388467           7607           4363      314519552        5373952           8192
+//     system_server       95874193        2216913          74613          52257      213078016        7237632          16384
+//            zygote         506279        1726194            921            263      128114688        1765376              0
+//  /vendor/bin/qcks       75415632       75154382          21672          25036       63627264       29974528       10485760
+//             /init       86658523        5107871          82113           8633       91015168        1245184              0
+
+    // Title
+    printf("                                       Application           Read          Write           Read          Write           Read          Write      Cancelled\n"
+           "                                              Name     Characters     Characters       Syscalls       Syscalls          Bytes          Bytes     Writebytes\n"
+           "                                        ----------     ----------     ----------     ----------     ----------     ----------     ----------     ----------\n");
+
+    for (struct task_info task : tasks) {
+        printf("%50s%15ju%15ju%15ju%15ju%15ju%15ju%15ju\n",
+            task.cmd, task.rchar, task.wchar, task.syscr, task.syscw,
+            task.read_bytes, task.write_bytes, task.cancelled_write_bytes);
+    }
+    fflush(stdout);
+}
+
+void log_kernel_disk_stats(struct disk_stats* stats, const char* type) {
+    // skip if the input structure are all zeros
+    if (stats == NULL) return;
+    struct disk_stats zero_cmp;
+    memset(&zero_cmp, 0, sizeof(zero_cmp));
+    if (memcmp(&zero_cmp, stats, sizeof(struct disk_stats)) == 0) return;
+
+    if (fd_dmesg >= 0) {
+        std::string info_message = android::base::StringPrintf(
+            "%s diskstats %s: %ju %ju %ju %ju %ju %ju %ju %ju %ju %ju %.1f %ju %ju\n",
+             kmsg_info_prefix, type, stats->start_time, stats->end_time,
+             stats->read_ios, stats->read_merges,
+             stats->read_sectors, stats->read_ticks,
+             stats->write_ios, stats->write_merges,
+             stats->write_sectors, stats->write_ticks,
+             stats->io_avg, stats->io_ticks,
+             stats->io_in_queue);
+
+        write(fd_dmesg, info_message.c_str(), info_message.length());
+    }
+}
+
+void log_kernel_disk_perf(struct disk_perf* perf, const char* type) {
+    // skip if the input structure are all zeros
+    if (perf == NULL) return;
+    struct disk_perf zero_cmp;
+    memset(&zero_cmp, 0, sizeof(zero_cmp));
+    if (memcmp(&zero_cmp, perf, sizeof(struct disk_perf)) == 0) return;
+
+    if (fd_dmesg >= 0) {
+        std::string info_message = android::base::StringPrintf(
+            "%s perf(ios) %s rd:%luKB/s(%lu/s) wr:%luKB/s(%lu/s) q:%lu\n",
+            kmsg_info_prefix, type,
+            (unsigned long)perf->read_perf, (unsigned long)perf->read_ios,
+            (unsigned long)perf->write_perf, (unsigned long)perf->write_ios,
+            (unsigned long)perf->queue);
+
+        write(fd_dmesg, info_message.c_str(), info_message.length());
+    }
+}
+
+void log_kernel_emmc_info(struct emmc_info* info) {
+    // skip if the input structure are all zeros
+    if (info == NULL) return;
+    struct emmc_info zero_cmp;
+    memset(&zero_cmp, 0, sizeof(zero_cmp));
+    if (memcmp(&zero_cmp, info, sizeof(struct emmc_info)) == 0) return;
+
+    if (fd_dmesg >= 0) {
+        std::string info_message = android::base::StringPrintf(
+            "%s MMC %s eol:%d, lifetime typA:%d, typB:%d\n",
+            kmsg_info_prefix, info->mmc_ver, info->eol, info->lifetime_a, info->lifetime_b);
+
+        write(fd_dmesg, info_message.c_str(), info_message.length());
+    }
+}
+
+void log_event_disk_stats(struct disk_stats* stats, const char* type) {
+    // skip if the input structure are all zeros
+    if (stats == NULL) return;
+    struct disk_stats zero_cmp;
+    memset(&zero_cmp, 0, sizeof(zero_cmp));
+    // skip event logging diskstats when it is zero increment (all first 11 entries are zero)
+    if (memcmp(&zero_cmp, stats, sizeof(uint64_t) * DISK_STATS_SIZE) == 0) return;
+
+    // Construct eventlog list
+    android_log_context ctx = create_android_logger(EVENTLOGTAG_DISKSTATS);
+
+    android_log_write_string8(ctx, type);
+    android_log_write_int64(ctx, stats->start_time);
+    android_log_write_int64(ctx, stats->end_time);
+    android_log_write_int64(ctx, stats->read_ios);
+    android_log_write_int64(ctx, stats->read_merges);
+    android_log_write_int64(ctx, stats->read_sectors);
+    android_log_write_int64(ctx, stats->read_ticks);
+    android_log_write_int64(ctx, stats->write_ios);
+    android_log_write_int64(ctx, stats->write_merges);
+    android_log_write_int64(ctx, stats->write_sectors);
+    android_log_write_int64(ctx, stats->write_ticks);
+    android_log_write_int64(ctx, (uint64_t)stats->io_avg);
+    android_log_write_int64(ctx, stats->io_ticks);
+    android_log_write_int64(ctx, stats->io_in_queue);
+
+    android_log_write_list(ctx, LOG_ID_EVENTS);
+    android_log_destroy(&ctx);
+}
+
+void log_event_emmc_info(struct emmc_info* info) {
+    // skip if the input structure are all zeros
+    if (info == NULL) return;
+    struct emmc_info zero_cmp;
+    memset(&zero_cmp, 0, sizeof(zero_cmp));
+    if (memcmp(&zero_cmp, info, sizeof(struct emmc_info)) == 0) return;
+
+    android_log_context ctx = create_android_logger(EVENTLOGTAG_EMMCINFO);
+
+    android_log_write_string8(ctx, info->mmc_ver);
+    android_log_write_int32(ctx, info->eol);
+    android_log_write_int32(ctx, info->lifetime_a);
+    android_log_write_int32(ctx, info->lifetime_b);
+
+    android_log_write_list(ctx, LOG_ID_EVENTS);
+    android_log_destroy(&ctx);
+}
diff --git a/storaged/tests/Android.mk b/storaged/tests/Android.mk
new file mode 100644
index 0000000..4a0e45c
--- /dev/null
+++ b/storaged/tests/Android.mk
@@ -0,0 +1,45 @@
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+test_module_prefix := storaged-
+test_tags := tests
+
+# -----------------------------------------------------------------------------
+# Unit tests.
+# -----------------------------------------------------------------------------
+
+test_c_flags := \
+    -fstack-protector-all \
+    -g \
+    -Wall -Wextra \
+    -Werror \
+    -fno-builtin \
+
+test_src_files := \
+    storaged_test.cpp \
+
+# Build tests for the logger. Run with:
+#   adb shell /data/nativetest/storaged-unit-tests/storaged-unit-tests
+include $(CLEAR_VARS)
+LOCAL_MODULE := $(test_module_prefix)unit-tests
+LOCAL_MODULE_TAGS := $(test_tags)
+LOCAL_CFLAGS += $(test_c_flags)
+LOCAL_STATIC_LIBRARIES := libstoraged
+LOCAL_SHARED_LIBRARIES := libbase libcutils liblog
+LOCAL_SRC_FILES := $(test_src_files)
+include $(BUILD_NATIVE_TEST)
diff --git a/storaged/tests/storaged_test.cpp b/storaged/tests/storaged_test.cpp
new file mode 100644
index 0000000..99b21ac
--- /dev/null
+++ b/storaged/tests/storaged_test.cpp
@@ -0,0 +1,586 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <deque>
+#include <fcntl.h>
+#include <random>
+#include <string.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <gtest/gtest.h>
+
+#include <storaged.h>               // data structures
+#include <storaged_utils.h>         // functions to test
+
+#define MMC_DISK_STATS_PATH "/sys/block/mmcblk0/stat"
+#define SDA_DISK_STATS_PATH "/sys/block/sda/stat"
+#define EMMC_EXT_CSD_PATH "/d/mmc0/mmc0:0001/ext_csd"
+#define INIT_TASK_IO_PATH "/proc/1/io"
+
+static void pause(uint32_t sec) {
+    const char* path = "/cache/test";
+    int fd = open(path, O_WRONLY | O_CREAT);
+    ASSERT_LT(-1, fd);
+    char buffer[2048];
+    memset(buffer, 1, sizeof(buffer));
+    int loop_size = 100;
+    for (int i = 0; i < loop_size; ++i) {
+        ASSERT_EQ(2048, write(fd, buffer, sizeof(buffer)));
+    }
+    fsync(fd);
+    close(fd);
+
+    fd = open(path, O_RDONLY);
+    ASSERT_LT(-1, fd);
+    for (int i = 0; i < loop_size; ++i) {
+        ASSERT_EQ(2048, read(fd, buffer, sizeof(buffer)));
+    }
+    close(fd);
+
+    sleep(sec);
+}
+
+// the return values of the tested functions should be the expected ones
+const char* DISK_STATS_PATH;
+TEST(storaged_test, retvals) {
+    struct disk_stats stats;
+    struct emmc_info info;
+    memset(&stats, 0, sizeof(struct disk_stats));
+    memset(&info, 0, sizeof(struct emmc_info));
+
+    int emmc_fd = open(EMMC_EXT_CSD_PATH, O_RDONLY);
+    if (emmc_fd >= 0) {
+        EXPECT_TRUE(parse_emmc_ecsd(emmc_fd, &info));
+    }
+
+    if (access(MMC_DISK_STATS_PATH, R_OK) >= 0) {
+        DISK_STATS_PATH = MMC_DISK_STATS_PATH;
+    } else if (access(SDA_DISK_STATS_PATH, R_OK) >= 0) {
+        DISK_STATS_PATH = SDA_DISK_STATS_PATH;
+    } else {
+        return;
+    }
+
+    EXPECT_TRUE(parse_disk_stats(DISK_STATS_PATH, &stats));
+
+    struct disk_stats old_stats;
+    memset(&old_stats, 0, sizeof(struct disk_stats));
+    old_stats = stats;
+
+    const char wrong_path[] = "/this/is/wrong";
+    EXPECT_FALSE(parse_disk_stats(wrong_path, &stats));
+
+    // reading a wrong path should not damage the output structure
+    EXPECT_EQ(0, memcmp(&stats, &old_stats, sizeof(disk_stats)));
+}
+
+TEST(storaged_test, disk_stats) {
+    struct disk_stats stats;
+    memset(&stats, 0, sizeof(struct disk_stats));
+
+    ASSERT_TRUE(parse_disk_stats(DISK_STATS_PATH, &stats));
+
+    // every entry of stats (except io_in_flight) should all be greater than 0
+    for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
+        if (i == 8) continue; // skip io_in_flight which can be 0
+        EXPECT_LT((uint64_t)0, *((uint64_t*)&stats + i));
+    }
+
+    // accumulation of the increments should be the same with the overall increment
+    struct disk_stats base, tmp, curr, acc, inc[5];
+    memset(&base, 0, sizeof(struct disk_stats));
+    memset(&tmp, 0, sizeof(struct disk_stats));
+    memset(&acc, 0, sizeof(struct disk_stats));
+
+    for (uint i = 0; i < 5; ++i) {
+        ASSERT_TRUE(parse_disk_stats(DISK_STATS_PATH, &curr));
+        if (i == 0) {
+            base = curr;
+            tmp = curr;
+            sleep(5);
+            continue;
+        }
+        inc[i] = get_inc_disk_stats(&tmp, &curr);
+        add_disk_stats(&inc[i], &acc);
+        tmp = curr;
+        pause(5);
+    }
+    struct disk_stats overall_inc;
+    memset(&overall_inc, 0, sizeof(disk_stats));
+    overall_inc= get_inc_disk_stats(&base, &curr);
+
+    for (uint i = 0; i < DISK_STATS_SIZE; ++i) {
+        if (i == 8) continue; // skip io_in_flight which can be 0
+        EXPECT_EQ(*((uint64_t*)&overall_inc + i), *((uint64_t*)&acc + i));
+    }
+}
+
+TEST(storaged_test, emmc_info) {
+    struct emmc_info info, void_info;
+    memset(&info, 0, sizeof(struct emmc_info));
+    memset(&void_info, 0, sizeof(struct emmc_info));
+
+    if (access(EMMC_EXT_CSD_PATH, R_OK) >= 0) {
+        int emmc_fd = open(EMMC_EXT_CSD_PATH, O_RDONLY);
+        ASSERT_GE(emmc_fd, 0);
+        ASSERT_TRUE(parse_emmc_ecsd(emmc_fd, &info));
+        // parse_emmc_ecsd() should put something in info.
+        EXPECT_NE(0, memcmp(&void_info, &info, sizeof(struct emmc_info)));
+    }
+}
+
+TEST(storaged_test, task_info) {
+    // parse_task_info should read something other than 0 from /proc/1/*
+    struct task_info task_info;
+    memset(&task_info, 0, sizeof(task_info));
+
+    if (!parse_task_info(1, &task_info)) return;
+
+    EXPECT_EQ((uint32_t)1, task_info.pid);
+    EXPECT_LT((uint64_t)0, task_info.rchar);
+    EXPECT_LT((uint64_t)0, task_info.wchar);
+    EXPECT_LT((uint64_t)0, task_info.syscr);
+    EXPECT_LT((uint64_t)0, task_info.syscw);
+    EXPECT_LT((uint64_t)0, task_info.read_bytes);
+    EXPECT_LT((uint64_t)0, task_info.write_bytes);
+    // cancelled_write_bytes of init could be 0, there is no need to test
+    EXPECT_LE((uint64_t)0, task_info.starttime);
+    EXPECT_NE((char*)NULL, strstr(task_info.cmd, "init"));
+
+    // Entries in /proc/1/io should be increasing through time
+    struct task_info task_old, task_new;
+    memset(&task_old, 0, sizeof(task_old));
+    memset(&task_new, 0, sizeof(task_new));
+
+    // parse_task_info should succeed at this point
+    ASSERT_TRUE(parse_task_info(1, &task_old));
+    sleep(1);
+    ASSERT_TRUE(parse_task_info(1, &task_new));
+
+    EXPECT_EQ(task_old.pid, task_new.pid);
+    EXPECT_LE(task_old.rchar, task_new.rchar);
+    EXPECT_LE(task_old.wchar, task_new.wchar);
+    EXPECT_LE(task_old.syscr, task_new.syscr);
+    EXPECT_LE(task_old.syscw, task_new.syscw);
+    EXPECT_LE(task_old.read_bytes, task_new.read_bytes);
+    EXPECT_LE(task_old.write_bytes, task_new.write_bytes);
+    EXPECT_LE(task_old.cancelled_write_bytes, task_new.cancelled_write_bytes);
+    EXPECT_EQ(task_old.starttime, task_new.starttime);
+    EXPECT_EQ(0, strcmp(task_old.cmd, task_new.cmd));
+}
+
+static double mean(std::deque<uint32_t> nums) {
+    double sum = 0.0;
+    for (uint32_t i : nums) {
+    sum += i;
+    }
+    return sum / nums.size();
+}
+
+static double standard_deviation(std::deque<uint32_t> nums) {
+    double sum = 0.0;
+    double avg = mean(nums);
+    for (uint32_t i : nums) {
+    sum += ((double)i - avg) * ((double)i - avg);
+    }
+    return sqrt(sum / nums.size());
+}
+
+TEST(storaged_test, stream_stats) {
+    // 100 random numbers
+    std::vector<uint32_t> data = {8147,9058,1270,9134,6324,975,2785,5469,9575,9649,1576,9706,9572,4854,8003,1419,4218,9157,7922,9595,6557,357,8491,9340,6787,7577,7431,3922,6555,1712,7060,318,2769,462,971,8235,6948,3171,9502,344,4387,3816,7655,7952,1869,4898,4456,6463,7094,7547,2760,6797,6551,1626,1190,4984,9597,3404,5853,2238,7513,2551,5060,6991,8909,9593,5472,1386,1493,2575,8407,2543,8143,2435,9293,3500,1966,2511,6160,4733,3517,8308,5853,5497,9172,2858,7572,7537,3804,5678,759,540,5308,7792,9340,1299,5688,4694,119,3371};
+    std::deque<uint32_t> test_data;
+    stream_stats sstats;
+    for (uint32_t i : data) {
+        test_data.push_back(i);
+        sstats.add(i);
+
+        EXPECT_EQ((int)standard_deviation(test_data), (int)sstats.get_std());
+        EXPECT_EQ((int)mean(test_data), (int)sstats.get_mean());
+    }
+
+    for (uint32_t i : data) {
+        test_data.pop_front();
+        sstats.evict(i);
+
+        EXPECT_EQ((int)standard_deviation(test_data), (int)sstats.get_std());
+        EXPECT_EQ((int)mean(test_data), (int)sstats.get_mean());
+    }
+
+    // some real data
+    std::vector<uint32_t> another_data = {113875,81620,103145,28327,86855,207414,96526,52567,28553,250311};
+    test_data.clear();
+    uint32_t window_size = 2;
+    uint32_t idx;
+    stream_stats sstats1;
+    for (idx = 0; idx < window_size; ++idx) {
+        test_data.push_back(another_data[idx]);
+        sstats1.add(another_data[idx]);
+    }
+    EXPECT_EQ((int)standard_deviation(test_data), (int)sstats1.get_std());
+    EXPECT_EQ((int)mean(test_data), (int)sstats1.get_mean());
+    for (;idx < another_data.size(); ++idx) {
+        test_data.pop_front();
+        sstats1.evict(another_data[idx - window_size]);
+        test_data.push_back(another_data[idx]);
+        sstats1.add(another_data[idx]);
+        EXPECT_EQ((int)standard_deviation(test_data), (int)sstats1.get_std());
+        EXPECT_EQ((int)mean(test_data), (int)sstats1.get_mean());
+    }
+}
+
+static void expect_increasing(struct task_info told, struct task_info tnew) {
+    ASSERT_EQ(told.pid, tnew.pid);
+    ASSERT_EQ(told.starttime, tnew.starttime);
+    ASSERT_EQ(strcmp(told.cmd, tnew.cmd), 0);
+
+    EXPECT_LE(told.rchar, tnew.rchar);
+    EXPECT_LE(told.wchar, tnew.wchar);
+    EXPECT_LE(told.syscr, tnew.syscr);
+    EXPECT_LE(told.syscw, tnew.syscw);
+    EXPECT_LE(told.read_bytes, tnew.read_bytes);
+    EXPECT_LE(told.write_bytes, tnew.write_bytes);
+    EXPECT_LE(told.cancelled_write_bytes, tnew.cancelled_write_bytes);
+}
+
+static void expect_equal(struct task_info told, struct task_info tnew) {
+    ASSERT_EQ(told.pid, tnew.pid);
+    ASSERT_EQ(told.starttime, tnew.starttime);
+    ASSERT_EQ(strcmp(told.cmd, tnew.cmd), 0);
+
+    EXPECT_EQ(told.rchar, tnew.rchar);
+    EXPECT_EQ(told.wchar, tnew.wchar);
+    EXPECT_EQ(told.syscr, tnew.syscr);
+    EXPECT_EQ(told.syscw, tnew.syscw);
+    EXPECT_EQ(told.read_bytes, tnew.read_bytes);
+    EXPECT_EQ(told.write_bytes, tnew.write_bytes);
+    EXPECT_EQ(told.cancelled_write_bytes, tnew.cancelled_write_bytes);
+}
+
+static std::set<uint32_t> find_overlap(std::unordered_map<uint32_t, struct task_info> t1,
+                                       std::unordered_map<uint32_t, struct task_info> t2) {
+    std::set<uint32_t> retval;
+    for (auto i : t1) {
+        if (t2.find(i.first) != t2.end()) {
+            retval.insert(i.first);
+        }
+    }
+
+    return retval;
+}
+
+static std::set<std::string> find_overlap(std::unordered_map<std::string, struct task_info> t1,
+                                          std::unordered_map<std::string, struct task_info> t2) {
+    std::set<std::string> retval;
+    for (auto i : t1) {
+        if (t2.find(i.first) != t2.end()) {
+            retval.insert(i.first);
+        }
+    }
+
+    return retval;
+}
+
+static bool cmp_app_name(struct task_info i, struct task_info j) {
+    return strcmp(i.cmd, j.cmd) > 0;
+}
+
+static void expect_match(std::vector<struct task_info> v1, std::vector<struct task_info> v2) {
+    ASSERT_EQ(v1.size(), v2.size());
+    std::sort(v1.begin(), v1.end(), cmp_app_name);
+    std::sort(v2.begin(), v2.end(), cmp_app_name);
+
+    for (uint i = 0; i < v1.size(); ++i) {
+        expect_equal(v1[i], v2[i]);
+    }
+}
+
+static void add_task_info(struct task_info* src, struct task_info* dst) {
+    ASSERT_EQ(0, strcmp(src->cmd, dst->cmd));
+
+    dst->pid = 0;
+    dst->rchar += src->rchar;
+    dst->wchar += src->wchar;
+    dst->syscr += src->syscr;
+    dst->syscw += src->syscw;
+    dst->read_bytes += src->read_bytes;
+    dst->write_bytes += src->write_bytes;
+    dst->cancelled_write_bytes += src->cancelled_write_bytes;
+    dst->starttime = 0;
+}
+
+static std::vector<struct task_info>
+categorize_tasks(std::unordered_map<uint32_t, struct task_info> tasks) {
+    std::unordered_map<std::string, struct task_info> tasks_cmd;
+    for (auto i : tasks) {
+        std::string cmd = i.second.cmd;
+        if (tasks_cmd.find(cmd) == tasks_cmd.end()) {
+            tasks_cmd[cmd] = i.second;
+        } else {
+            add_task_info(&i.second, &tasks_cmd[cmd]);
+        }
+    }
+
+    std::vector<struct task_info> retval(tasks_cmd.size());
+    int cnt = 0;
+    for (auto i : tasks_cmd) {
+        retval[cnt++] = i.second;
+    }
+
+    return retval;
+}
+
+#define TEST_LOOPS 20
+TEST(storaged_test, tasks_t) {
+    // pass this test if /proc/[pid]/io is not readable
+    const char* test_paths[] = {"/proc/1/io", "/proc/1/comm", "/proc/1/cmdline", "/proc/1/stat"};
+    for (uint i = 0; i < sizeof(test_paths) / sizeof(const char*); ++i) {
+        if (access(test_paths[i], R_OK) < 0) return;
+    }
+
+    tasks_t tasks;
+    EXPECT_EQ((uint32_t)0, tasks.mRunning.size());
+    EXPECT_EQ((uint32_t)0, tasks.mOld.size());
+
+    tasks.update_running_tasks();
+
+    std::unordered_map<uint32_t, struct task_info> prev_running = tasks.mRunning;
+    std::unordered_map<std::string, struct task_info> prev_old = tasks.mOld;
+
+    // hashmap maintaining
+    std::unordered_map<uint32_t, struct task_info> tasks_pid = tasks.mRunning;
+
+    // get_running_tasks() should return something other than a null map
+    std::unordered_map<uint32_t, struct task_info> test = tasks.get_running_tasks();
+    EXPECT_LE((uint32_t)1, test.size());
+
+    for (int i = 0; i < TEST_LOOPS; ++i) {
+        tasks.update_running_tasks();
+
+        std::set<uint32_t> overlap_running = find_overlap(prev_running, tasks.mRunning);
+        std::set<std::string> overlap_old = find_overlap(prev_old, tasks.mOld);
+
+        // overlap_running should capture init(pid == 1), since init never get killed
+        EXPECT_LE((uint32_t)1, overlap_running.size());
+        EXPECT_NE(overlap_running.find((uint32_t)1), overlap_running.end());
+        // overlap_old should never capture init, since init never get killed
+        EXPECT_EQ(overlap_old.find("init"), overlap_old.end());
+
+        // overlapping entries in previous and current running-tasks map should have increasing contents
+        for (uint32_t i : overlap_running) {
+            expect_increasing(prev_running[i], tasks.mRunning[i]);
+        }
+
+        // overlapping entries in previous and current killed-tasks map should have increasing contents
+        // and the map size should also be increasing
+        for (std::string i : overlap_old) {
+            expect_increasing(prev_old[i], tasks.mOld[i]);
+        }
+        EXPECT_LE(prev_old.size(), tasks.mRunning.size());
+
+        // update app name & tasks_pid
+        for (auto i : tasks.mRunning) {
+            // test will fail if the pid got wrapped
+            if (tasks_pid.find(i.first) != tasks_pid.end()) {
+                expect_increasing(tasks_pid[i.first], i.second);
+                tasks_pid[i.first] = i.second;
+            } else {
+                tasks_pid[i.first] = i.second;
+            }
+        }
+
+        // get maintained tasks
+        std::vector<struct task_info> test_tasks = categorize_tasks(tasks_pid);
+        std::vector<struct task_info> real_tasks = tasks.get_tasks();
+
+        expect_match(test_tasks, real_tasks);
+
+        prev_running = tasks.mRunning;
+        prev_old = tasks.mOld;
+
+        pause(5);
+    }
+}
+
+static struct disk_perf disk_perf_multiply(struct disk_perf perf, double mul) {
+    struct disk_perf retval;
+    retval.read_perf = (double)perf.read_perf * mul;
+    retval.read_ios = (double)perf.read_ios * mul;
+    retval.write_perf = (double)perf.write_perf * mul;
+    retval.write_ios = (double)perf.write_ios * mul;
+    retval.queue = (double)perf.queue * mul;
+
+    return retval;
+}
+
+static struct disk_stats disk_stats_add(struct disk_stats stats1, struct disk_stats stats2) {
+    struct disk_stats retval;
+    retval.read_ios = stats1.read_ios + stats2.read_ios;
+    retval.read_merges = stats1.read_merges + stats2.read_merges;
+    retval.read_sectors = stats1.read_sectors + stats2.read_sectors;
+    retval.read_ticks = stats1.read_ticks + stats2.read_ticks;
+    retval.write_ios = stats1.write_ios + stats2.write_ios;
+    retval.write_merges = stats1.write_merges + stats2.write_merges;
+    retval.write_sectors = stats1.write_sectors + stats2.write_sectors;
+    retval.write_ticks = stats1.write_ticks + stats2.write_ticks;
+    retval.io_in_flight = stats1.io_in_flight + stats2.io_in_flight;
+    retval.io_ticks = stats1.io_ticks + stats2.io_ticks;
+    retval.io_in_queue = stats1.io_in_queue + stats2.io_in_queue;
+    retval.end_time = stats1.end_time + stats2.end_time;
+
+    return retval;
+}
+
+TEST(storaged_test, disk_stats_monitor) {
+    // asserting that there is one file for diskstats
+    ASSERT_TRUE(access(MMC_DISK_STATS_PATH, R_OK) >= 0 || access(SDA_DISK_STATS_PATH, R_OK) >= 0);
+    // testing if detect() will return the right value
+    disk_stats_monitor dsm_detect;
+    // feed monitor with constant perf data for io perf baseline
+    // using constant perf is reasonable since the functionality of stream_stats
+    // has already been tested
+    struct disk_perf norm_perf = {
+        .read_perf = 10 * 1024,
+        .read_ios = 50,
+        .write_perf = 5 * 1024,
+        .write_ios = 25,
+        .queue = 5
+    };
+
+    std::random_device rd;
+    std::mt19937 gen(rd());
+    std::uniform_real_distribution<> rand(0.8, 1.2);
+
+    for (uint i = 0; i < dsm_detect.mWindow; ++i) {
+        struct disk_perf perf = disk_perf_multiply(norm_perf, rand(gen));
+
+        dsm_detect.add(&perf);
+        dsm_detect.mBuffer.push(perf);
+        EXPECT_EQ(dsm_detect.mBuffer.size(), (uint64_t)i + 1);
+    }
+
+    dsm_detect.mValid = true;
+    dsm_detect.update_mean();
+    dsm_detect.update_std();
+
+    for (double i = 0; i < 2 * dsm_detect.mSigma; i += 0.5) {
+        struct disk_perf test_perf;
+        struct disk_perf test_mean = dsm_detect.mMean;
+        struct disk_perf test_std = dsm_detect.mStd;
+
+        test_perf.read_perf = (double)test_mean.read_perf - i * test_std.read_perf;
+        test_perf.read_ios = (double)test_mean.read_ios - i * test_std.read_ios;
+        test_perf.write_perf = (double)test_mean.write_perf - i * test_std.write_perf;
+        test_perf.write_ios = (double)test_mean.write_ios - i * test_std.write_ios;
+        test_perf.queue = (double)test_mean.queue + i * test_std.queue;
+
+        EXPECT_EQ((i > dsm_detect.mSigma), dsm_detect.detect(&test_perf));
+    }
+
+    // testing if stalled disk_stats can be correctly accumulated in the monitor
+    disk_stats_monitor dsm_acc;
+    struct disk_stats norm_inc = {
+        .read_ios = 200,
+        .read_merges = 0,
+        .read_sectors = 200,
+        .read_ticks = 200,
+        .write_ios = 100,
+        .write_merges = 0,
+        .write_sectors = 100,
+        .write_ticks = 100,
+        .io_in_flight = 0,
+        .io_ticks = 600,
+        .io_in_queue = 300,
+        .start_time = 0,
+        .end_time = 100,
+        .counter = 0,
+        .io_avg = 0
+    };
+
+    struct disk_stats stall_inc = {
+        .read_ios = 200,
+        .read_merges = 0,
+        .read_sectors = 20,
+        .read_ticks = 200,
+        .write_ios = 100,
+        .write_merges = 0,
+        .write_sectors = 10,
+        .write_ticks = 100,
+        .io_in_flight = 0,
+        .io_ticks = 600,
+        .io_in_queue = 1200,
+        .start_time = 0,
+        .end_time = 100,
+        .counter = 0,
+        .io_avg = 0
+    };
+
+    struct disk_stats stats_base;
+    memset(&stats_base, 0, sizeof(stats_base));
+
+    int loop_size = 100;
+    for (int i = 0; i < loop_size; ++i) {
+        stats_base = disk_stats_add(stats_base, norm_inc);
+        dsm_acc.update(&stats_base);
+        EXPECT_EQ(dsm_acc.mValid, (uint32_t)(i + 1) >= dsm_acc.mWindow);
+        EXPECT_FALSE(dsm_acc.mStall);
+    }
+
+    stats_base = disk_stats_add(stats_base, stall_inc);
+    dsm_acc.update(&stats_base);
+    EXPECT_TRUE(dsm_acc.mValid);
+    EXPECT_TRUE(dsm_acc.mStall);
+
+    for (int i = 0; i < 10; ++i) {
+        stats_base = disk_stats_add(stats_base, norm_inc);
+        dsm_acc.update(&stats_base);
+        EXPECT_TRUE(dsm_acc.mValid);
+        EXPECT_FALSE(dsm_acc.mStall);
+    }
+}
+
+static void expect_increasing(struct disk_stats stats1, struct disk_stats stats2) {
+    EXPECT_LE(stats1.read_ios, stats2.read_ios);
+    EXPECT_LE(stats1.read_merges, stats2.read_merges);
+    EXPECT_LE(stats1.read_sectors, stats2.read_sectors);
+    EXPECT_LE(stats1.read_ticks, stats2.read_ticks);
+
+    EXPECT_LE(stats1.write_ios, stats2.write_ios);
+    EXPECT_LE(stats1.write_merges, stats2.write_merges);
+    EXPECT_LE(stats1.write_sectors, stats2.write_sectors);
+    EXPECT_LE(stats1.write_ticks, stats2.write_ticks);
+
+    EXPECT_LE(stats1.io_ticks, stats2.io_ticks);
+    EXPECT_LE(stats1.io_in_queue, stats2.io_in_queue);
+}
+
+TEST(storaged_test, disk_stats_publisher) {
+    // asserting that there is one file for diskstats
+    ASSERT_TRUE(access(MMC_DISK_STATS_PATH, R_OK) >= 0 || access(SDA_DISK_STATS_PATH, R_OK) >= 0);
+    disk_stats_publisher dsp;
+    struct disk_stats prev;
+    memset(&prev, 0, sizeof(prev));
+
+    for (int i = 0; i < TEST_LOOPS; ++i) {
+        dsp.update();
+        expect_increasing(prev, dsp.mPrevious);
+        prev = dsp.mPrevious;
+        pause(10);
+    }
+}
+