Merge "Switch fs_mgr_verity.c to C++."
diff --git a/crash_reporter/crash_reporter.rc b/crash_reporter/crash_reporter.rc
index 30e87f5..7bfe0c2 100644
--- a/crash_reporter/crash_reporter.rc
+++ b/crash_reporter/crash_reporter.rc
@@ -1,6 +1,6 @@
 on property:crash_reporter.coredump.enabled=1
     write /proc/sys/kernel/core_pattern \
-          "|/system/bin/crash_reporter --user=%P:%s:%u:%e"
+          "|/system/bin/crash_reporter --user=%P:%s:%u:%g:%e"
 
 on property:crash_reporter.coredump.enabled=0
     write /proc/sys/kernel/core_pattern "core"
@@ -14,11 +14,14 @@
     rmdir /data/misc/crash_reporter/lock/crash_sender
 
     # Create crash directories.
-    mkdir /data/misc/crash_reporter 0700 root root
+    # These directories are group-writable by root so that crash_reporter can
+    # still access them when it switches users.
+    mkdir /data/misc/crash_reporter 0770 root root
+    mkdir /data/misc/crash_reporter/crash 0770 root root
     mkdir /data/misc/crash_reporter/lock 0700 root root
     mkdir /data/misc/crash_reporter/log 0700 root root
     mkdir /data/misc/crash_reporter/run 0700 root root
-    mkdir /data/misc/crash_reporter/tmp 0700 root root
+    mkdir /data/misc/crash_reporter/tmp 0770 root root
 
 service crash_reporter /system/bin/crash_reporter --init
     class late_start
diff --git a/crash_reporter/user_collector.cc b/crash_reporter/user_collector.cc
index a9522cc..c4f02af 100644
--- a/crash_reporter/user_collector.cc
+++ b/crash_reporter/user_collector.cc
@@ -23,9 +23,11 @@
 #include <pwd.h>  // For struct passwd.
 #include <stdint.h>
 #include <sys/cdefs.h>  // For __WORDSIZE
+#include <sys/fsuid.h>
 #include <sys/types.h>  // For getpwuid_r, getgrnam_r, WEXITSTATUS.
 #include <unistd.h>  // For setgroups
 
+#include <iostream>  // For std::oct
 #include <string>
 #include <vector>
 
@@ -49,8 +51,9 @@
 
 static const char kCoreTempFolder[] = "/data/misc/crash_reporter/tmp";
 
-// Define an otherwise invalid value that represents an unknown UID.
+// Define an otherwise invalid value that represents an unknown UID and GID.
 static const uid_t kUnknownUid = -1;
+static const gid_t kUnknownGid = -1;
 
 const char *UserCollector::kUserId = "Uid:\t";
 const char *UserCollector::kGroupId = "Gid:\t";
@@ -87,9 +90,9 @@
   directory_failure_ = directory_failure;
   filter_in_ = filter_in;
 
-  gid_t groups[] = { AID_SYSTEM, AID_DBUS };
+  gid_t groups[] = { AID_ROOT, AID_SYSTEM, AID_DBUS };
   if (setgroups(arraysize(groups), groups) != 0) {
-    PLOG(FATAL) << "Unable to set groups to system and dbus";
+    PLOG(FATAL) << "Unable to set groups to root, system, and dbus";
   }
 }
 
@@ -115,22 +118,6 @@
   }
 }
 
-// Return the string that should be used for the kernel's core_pattern file.
-// Note that if you change the format of the enabled pattern, you'll probably
-// also need to change the ParseCrashAttributes() function below, the
-// user_collector_test.cc unittest, and the logging_UserCrash.py autotest.
-std::string UserCollector::GetPattern(bool enabled) const {
-  if (enabled) {
-    // Combine the four crash attributes into one parameter to try to reduce
-    // the size of the invocation line for crash_reporter, since the kernel
-    // has a fixed-sized (128B) buffer for it (before parameter expansion).
-    // Note that the kernel does not support quoted arguments in core_pattern.
-    return StringPrintf("|%s --user=%%P:%%s:%%u:%%e", our_path_.c_str());
-  } else {
-    return "core";
-  }
-}
-
 bool UserCollector::SetUpInternal(bool enabled) {
   CHECK(initialized_);
   LOG(INFO) << (enabled ? "Enabling" : "Disabling") << " user crash handling";
@@ -227,7 +214,19 @@
 bool UserCollector::CopyOffProcFiles(pid_t pid,
                                      const FilePath &container_dir) {
   if (!base::CreateDirectory(container_dir)) {
-    PLOG(ERROR) << "Could not create " << container_dir.value().c_str();
+    PLOG(ERROR) << "Could not create " << container_dir.value();
+    return false;
+  }
+  int dir_mask = base::FILE_PERMISSION_READ_BY_USER
+                 | base::FILE_PERMISSION_WRITE_BY_USER
+                 | base::FILE_PERMISSION_EXECUTE_BY_USER
+                 | base::FILE_PERMISSION_READ_BY_GROUP
+                 | base::FILE_PERMISSION_WRITE_BY_GROUP;
+  if (!base::SetPosixFilePermissions(container_dir,
+                                     base::FILE_PERMISSION_MASK & dir_mask)) {
+    PLOG(ERROR) << "Could not set permissions for " << container_dir.value()
+                << " to " << std::oct
+                << (base::FILE_PERMISSION_MASK & dir_mask);
     return false;
   }
   FilePath process_path = GetProcessPath(pid);
@@ -410,6 +409,43 @@
   bool proc_files_usable =
       CopyOffProcFiles(pid, container_dir) && ValidateProcFiles(container_dir);
 
+  // Switch back to the original UID/GID.
+  gid_t rgid, egid, sgid;
+  if (getresgid(&rgid, &egid, &sgid) != 0) {
+    PLOG(FATAL) << "Unable to read saved gid";
+  }
+  if (setresgid(sgid, sgid, -1) != 0) {
+    PLOG(FATAL) << "Unable to set real group ID back to saved gid";
+  } else {
+    if (getresgid(&rgid, &egid, &sgid) != 0) {
+      // If the groups cannot be read at this point, the rgid variable will
+      // contain the previously read group ID from before changing it.  This
+      // will cause the chown call below to set the incorrect group for
+      // non-root crashes.  But do not treat this as a fatal error, so that
+      // the rest of the collection will continue for potential manual
+      // collection by a developer.
+      PLOG(ERROR) << "Unable to read real group ID after setting it";
+    }
+  }
+
+  uid_t ruid, euid, suid;
+  if (getresuid(&ruid, &euid, &suid) != 0) {
+    PLOG(FATAL) << "Unable to read saved uid";
+  }
+  if (setresuid(suid, suid, -1) != 0) {
+    PLOG(FATAL) << "Unable to set real user ID back to saved uid";
+  } else {
+    if (getresuid(&ruid, &euid, &suid) != 0) {
+      // If the user ID cannot be read at this point, the ruid variable will
+      // contain the previously read user ID from before changing it.  This
+      // will cause the chown call below to set the incorrect user for
+      // non-root crashes.  But do not treat this as a fatal error, so that
+      // the rest of the collection will continue for potential manual
+      // collection by a developer.
+      PLOG(ERROR) << "Unable to read real user ID after setting it";
+    }
+  }
+
   if (!CopyStdinToCoreFile(core_path)) {
     return kErrorReadCoreData;
   }
@@ -425,6 +461,13 @@
     return error;
   }
 
+  // Chown the temp container directory back to the original user/group that
+  // crash_reporter is run as, so that additional files can be written to
+  // the temp folder.
+  if (chown(container_dir.value().c_str(), ruid, rgid) < 0) {
+    PLOG(ERROR) << "Could not set owner for " << container_dir.value();
+  }
+
   if (!RunCoreToMinidump(core_path,
                          container_dir,  // procfs directory
                          minidump_path,
@@ -496,15 +539,18 @@
 
 bool UserCollector::ParseCrashAttributes(const std::string &crash_attributes,
                                          pid_t *pid, int *signal, uid_t *uid,
+                                         gid_t *gid,
                                          std::string *kernel_supplied_name) {
-  pcrecpp::RE re("(\\d+):(\\d+):(\\d+):(.*)");
-  if (re.FullMatch(crash_attributes, pid, signal, uid, kernel_supplied_name))
+  pcrecpp::RE re("(\\d+):(\\d+):(\\d+):(\\d+):(.*)");
+  if (re.FullMatch(crash_attributes, pid, signal, uid, gid,
+                   kernel_supplied_name))
     return true;
 
   LOG(INFO) << "Falling back to parsing crash attributes '"
-            << crash_attributes << "' without UID";
+            << crash_attributes << "' without UID and GID";
   pcrecpp::RE re_without_uid("(\\d+):(\\d+):(.*)");
   *uid = kUnknownUid;
+  *gid = kUnknownGid;
   return re_without_uid.FullMatch(crash_attributes, pid, signal,
       kernel_supplied_name);
 }
@@ -537,14 +583,25 @@
   pid_t pid = 0;
   int signal = 0;
   uid_t supplied_ruid = kUnknownUid;
+  gid_t supplied_rgid = kUnknownGid;
   std::string kernel_supplied_name;
 
   if (!ParseCrashAttributes(crash_attributes, &pid, &signal, &supplied_ruid,
-                            &kernel_supplied_name)) {
+                            &supplied_rgid, &kernel_supplied_name)) {
     LOG(ERROR) << "Invalid parameter: --user=" <<  crash_attributes;
     return false;
   }
 
+  // Switch to the group and user that ran the crashing binary in order to
+  // access their /proc files.  Do not set suid/sgid, so that we can switch
+  // back after copying the necessary files.
+  if (setresgid(supplied_rgid, supplied_rgid, -1) != 0) {
+    PLOG(FATAL) << "Unable to set real group ID to access process files";
+  }
+  if (setresuid(supplied_ruid, supplied_ruid, -1) != 0) {
+    PLOG(FATAL) << "Unable to set real user ID to access process files";
+  }
+
   std::string exec;
   if (force_exec) {
     exec.assign(force_exec);
diff --git a/crash_reporter/user_collector.h b/crash_reporter/user_collector.h
index 8c38aa2..7261ed4 100644
--- a/crash_reporter/user_collector.h
+++ b/crash_reporter/user_collector.h
@@ -105,7 +105,6 @@
   // crash_reporter-user-collection signature.
   std::string GetErrorTypeSignature(ErrorType error_type) const;
 
-  std::string GetPattern(bool enabled) const;
   bool SetUpInternal(bool enabled);
 
   // Returns, via |line|, the first line in |lines| that starts with |prefix|.
@@ -166,7 +165,7 @@
   ErrorType ConvertAndEnqueueCrash(pid_t pid, const std::string &exec_name,
                                    uid_t supplied_ruid, bool *out_of_capacity);
   bool ParseCrashAttributes(const std::string &crash_attributes,
-                            pid_t *pid, int *signal, uid_t *uid,
+                            pid_t *pid, int *signal, uid_t *uid, gid_t *gid,
                             std::string *kernel_supplied_name);
 
   bool ShouldDump(bool has_owner_consent,
diff --git a/crash_reporter/user_collector_test.cc b/crash_reporter/user_collector_test.cc
index 4419e7c..1dfac2a 100644
--- a/crash_reporter/user_collector_test.cc
+++ b/crash_reporter/user_collector_test.cc
@@ -98,35 +98,38 @@
   pid_t pid;
   int signal;
   uid_t uid;
+  gid_t gid;
   std::string exec_name;
-  EXPECT_TRUE(collector_.ParseCrashAttributes("123456:11:1000:foobar",
-      &pid, &signal, &uid, &exec_name));
+  EXPECT_TRUE(collector_.ParseCrashAttributes("123456:11:1000:2000:foobar",
+      &pid, &signal, &uid, &gid, &exec_name));
   EXPECT_EQ(123456, pid);
   EXPECT_EQ(11, signal);
   EXPECT_EQ(1000, uid);
+  EXPECT_EQ(2000, gid);
   EXPECT_EQ("foobar", exec_name);
   EXPECT_TRUE(collector_.ParseCrashAttributes("4321:6:barfoo",
-      &pid, &signal, &uid, &exec_name));
+      &pid, &signal, &uid, &gid, &exec_name));
   EXPECT_EQ(4321, pid);
   EXPECT_EQ(6, signal);
   EXPECT_EQ(-1, uid);
+  EXPECT_EQ(-1, gid);
   EXPECT_EQ("barfoo", exec_name);
 
   EXPECT_FALSE(collector_.ParseCrashAttributes("123456:11",
-      &pid, &signal, &uid, &exec_name));
+      &pid, &signal, &uid, &gid, &exec_name));
 
   EXPECT_TRUE(collector_.ParseCrashAttributes("123456:11:exec:extra",
-      &pid, &signal, &uid, &exec_name));
+      &pid, &signal, &uid, &gid, &exec_name));
   EXPECT_EQ("exec:extra", exec_name);
 
   EXPECT_FALSE(collector_.ParseCrashAttributes("12345p:11:foobar",
-      &pid, &signal, &uid, &exec_name));
+      &pid, &signal, &uid, &gid, &exec_name));
 
   EXPECT_FALSE(collector_.ParseCrashAttributes("123456:1 :foobar",
-      &pid, &signal, &uid, &exec_name));
+      &pid, &signal, &uid, &gid, &exec_name));
 
   EXPECT_FALSE(collector_.ParseCrashAttributes("123456::foobar",
-      &pid, &signal, &uid, &exec_name));
+      &pid, &signal, &uid, &gid, &exec_name));
 }
 
 TEST_F(UserCollectorTest, ShouldDumpDeveloperImageOverridesConsent) {
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index 599995c..713638d 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -130,31 +130,44 @@
   "dump_backtrace"
 };
 
-static bool selinux_action_allowed(int s, pid_t tid, debugger_action_t action)
+static int audit_callback(void* data, security_class_t /* cls */, char* buf, size_t len)
+{
+    struct debugger_request_t* req = reinterpret_cast<debugger_request_t*>(data);
+
+    if (!req) {
+        ALOGE("No debuggerd request audit data");
+        return 0;
+    }
+
+    snprintf(buf, len, "pid=%d uid=%d gid=%d", req->pid, req->uid, req->gid);
+    return 0;
+}
+
+static bool selinux_action_allowed(int s, debugger_request_t* request)
 {
   char *scon = NULL, *tcon = NULL;
   const char *tclass = "debuggerd";
   const char *perm;
   bool allowed = false;
 
-  if (action <= 0 || action >= (sizeof(debuggerd_perms)/sizeof(debuggerd_perms[0]))) {
-    ALOGE("SELinux:  No permission defined for debugger action %d", action);
+  if (request->action <= 0 || request->action >= (sizeof(debuggerd_perms)/sizeof(debuggerd_perms[0]))) {
+    ALOGE("SELinux:  No permission defined for debugger action %d", request->action);
     return false;
   }
 
-  perm = debuggerd_perms[action];
+  perm = debuggerd_perms[request->action];
 
   if (getpeercon(s, &scon) < 0) {
     ALOGE("Cannot get peer context from socket\n");
     goto out;
   }
 
-  if (getpidcon(tid, &tcon) < 0) {
-    ALOGE("Cannot get context for tid %d\n", tid);
+  if (getpidcon(request->tid, &tcon) < 0) {
+    ALOGE("Cannot get context for tid %d\n", request->tid);
     goto out;
   }
 
-  allowed = (selinux_check_access(scon, tcon, tclass, perm, NULL) == 0);
+  allowed = (selinux_check_access(scon, tcon, tclass, perm, reinterpret_cast<void*>(request)) == 0);
 
 out:
    freecon(scon);
@@ -225,7 +238,7 @@
       return -1;
     }
 
-    if (!selinux_action_allowed(fd, out_request->tid, out_request->action))
+    if (!selinux_action_allowed(fd, out_request))
       return -1;
   } else {
     // No one else is allowed to dump arbitrary processes.
@@ -566,6 +579,8 @@
 int main(int argc, char** argv) {
   union selinux_callback cb;
   if (argc == 1) {
+    cb.func_audit = audit_callback;
+    selinux_set_callback(SELINUX_CB_AUDIT, cb);
     cb.func_log = selinux_log_callback;
     selinux_set_callback(SELINUX_CB_LOG, cb);
     return do_server();
diff --git a/include/utils/LruCache.h b/include/utils/LruCache.h
index 7818b4e..20a379e 100644
--- a/include/utils/LruCache.h
+++ b/include/utils/LruCache.h
@@ -99,17 +99,29 @@
     TValue mNullValue;
 
 public:
+    // To be used like:
+    // while (it.next()) {
+    //   it.value(); it.key();
+    // }
     class Iterator {
     public:
-        Iterator(const LruCache<TKey, TValue>& cache): mCache(cache), mIterator(mCache.mSet->begin()) {
+        Iterator(const LruCache<TKey, TValue>& cache):
+                mCache(cache), mIterator(mCache.mSet->begin()), mBeginReturned(false) {
         }
 
         bool next() {
             if (mIterator == mCache.mSet->end()) {
                 return false;
             }
-            std::advance(mIterator, 1);
-            return mIterator != mCache.mSet->end();
+            if (!mBeginReturned) {
+                // mIterator has been initialized to the beginning and
+                // hasn't been returned. Do not advance:
+                mBeginReturned = true;
+            } else {
+                std::advance(mIterator, 1);
+            }
+            bool ret = (mIterator != mCache.mSet->end());
+            return ret;
         }
 
         const TValue& value() const {
@@ -122,6 +134,7 @@
     private:
         const LruCache<TKey, TValue>& mCache;
         typename LruCacheSet::iterator mIterator;
+        bool mBeginReturned;
     };
 };
 
diff --git a/init/init.cpp b/init/init.cpp
index ee1351d..a898b03 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -467,7 +467,16 @@
 }
 
 static int audit_callback(void *data, security_class_t /*cls*/, char *buf, size_t len) {
-    snprintf(buf, len, "property=%s", !data ? "NULL" : (char *)data);
+
+    property_audit_data *d = reinterpret_cast<property_audit_data*>(data);
+
+    if (!d || !d->name || !d->cr) {
+        ERROR("audit_callback invoked with null data arguments!");
+        return 0;
+    }
+
+    snprintf(buf, len, "property=%s pid=%d uid=%d gid=%d", d->name,
+            d->cr->pid, d->cr->uid, d->cr->gid);
     return 0;
 }
 
diff --git a/init/init_parser.cpp b/init/init_parser.cpp
index 02b3985..5b8e27b 100644
--- a/init/init_parser.cpp
+++ b/init/init_parser.cpp
@@ -105,7 +105,10 @@
     for (const auto& sp : section_parsers_) {
         sp.second->EndFile(path);
     }
-    DumpState();
+
+    // Turning this on and letting the INFO logging be discarded adds 0.2s to
+    // Nexus 9 boot time, so it's disabled by default.
+    if (false) DumpState();
 
     NOTICE("(Parsing %s took %.2fs.)\n", path.c_str(), t.duration());
     return true;
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 7194820..ebf768e 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -90,10 +90,11 @@
     }
 }
 
-static int check_mac_perms(const char *name, char *sctx)
+static int check_mac_perms(const char *name, char *sctx, struct ucred *cr)
 {
     char *tctx = NULL;
     int result = 0;
+    property_audit_data audit_data;
 
     if (!sctx)
         goto err;
@@ -104,7 +105,10 @@
     if (selabel_lookup(sehandle_prop, &tctx, name, 1) != 0)
         goto err;
 
-    if (selinux_check_access(sctx, tctx, "property_service", "set", (void*) name) == 0)
+    audit_data.name = name;
+    audit_data.cr = cr;
+
+    if (selinux_check_access(sctx, tctx, "property_service", "set", reinterpret_cast<void*>(&audit_data)) == 0)
         result = 1;
 
     freecon(tctx);
@@ -112,7 +116,7 @@
     return result;
 }
 
-static int check_control_mac_perms(const char *name, char *sctx)
+static int check_control_mac_perms(const char *name, char *sctx, struct ucred *cr)
 {
     /*
      *  Create a name prefix out of ctl.<service name>
@@ -126,19 +130,19 @@
     if (ret < 0 || (size_t) ret >= sizeof(ctl_name))
         return 0;
 
-    return check_mac_perms(ctl_name, sctx);
+    return check_mac_perms(ctl_name, sctx, cr);
 }
 
 /*
  * Checks permissions for setting system properties.
  * Returns 1 if uid allowed, 0 otherwise.
  */
-static int check_perms(const char *name, char *sctx)
+static int check_perms(const char *name, char *sctx, struct ucred *cr)
 {
     if(!strncmp(name, "ro.", 3))
         name +=3;
 
-    return check_mac_perms(name, sctx);
+    return check_mac_perms(name, sctx, cr);
 }
 
 std::string property_get(const char* name) {
@@ -321,14 +325,14 @@
             // Keep the old close-socket-early behavior when handling
             // ctl.* properties.
             close(s);
-            if (check_control_mac_perms(msg.value, source_ctx)) {
+            if (check_control_mac_perms(msg.value, source_ctx, &cr)) {
                 handle_control_message((char*) msg.name + 4, (char*) msg.value);
             } else {
                 ERROR("sys_prop: Unable to %s service ctl [%s] uid:%d gid:%d pid:%d\n",
                         msg.name + 4, msg.value, cr.uid, cr.gid, cr.pid);
             }
         } else {
-            if (check_perms(msg.name, source_ctx)) {
+            if (check_perms(msg.name, source_ctx, &cr)) {
                 property_set((char*) msg.name, (char*) msg.value);
             } else {
                 ERROR("sys_prop: permission denied uid:%d  name:%s\n",
diff --git a/init/property_service.h b/init/property_service.h
index 51d7404..1a48fb1 100644
--- a/init/property_service.h
+++ b/init/property_service.h
@@ -18,9 +18,15 @@
 #define _INIT_PROPERTY_H
 
 #include <stddef.h>
+#include <sys/socket.h>
 #include <sys/system_properties.h>
 #include <string>
 
+struct property_audit_data {
+    ucred *cr;
+    const char* name;
+};
+
 extern void property_init(void);
 extern void property_load_boot_defaults(void);
 extern void load_persist_props(void);
diff --git a/libutils/tests/LruCache_test.cpp b/libutils/tests/LruCache_test.cpp
index 2ed84d7..580b980 100644
--- a/libutils/tests/LruCache_test.cpp
+++ b/libutils/tests/LruCache_test.cpp
@@ -293,4 +293,110 @@
     EXPECT_EQ(3, callback.callbackCount);
 }
 
+TEST_F(LruCacheTest, IteratorCheck) {
+    LruCache<int, int> cache(100);
+
+    cache.put(1, 4);
+    cache.put(2, 5);
+    cache.put(3, 6);
+    EXPECT_EQ(3U, cache.size());
+
+    LruCache<int, int>::Iterator it(cache);
+    std::unordered_set<int> returnedValues;
+    while (it.next()) {
+        int v = it.value();
+        // Check we haven't seen the value before.
+        EXPECT_TRUE(returnedValues.find(v) == returnedValues.end());
+        returnedValues.insert(v);
+    }
+    EXPECT_EQ(std::unordered_set<int>({4, 5, 6}), returnedValues);
+}
+
+TEST_F(LruCacheTest, EmptyCacheIterator) {
+    // Check that nothing crashes...
+    LruCache<int, int> cache(100);
+
+    LruCache<int, int>::Iterator it(cache);
+    std::unordered_set<int> returnedValues;
+    while (it.next()) {
+        returnedValues.insert(it.value());
+    }
+    EXPECT_EQ(std::unordered_set<int>(), returnedValues);
+}
+
+TEST_F(LruCacheTest, OneElementCacheIterator) {
+    // Check that nothing crashes...
+    LruCache<int, int> cache(100);
+    cache.put(1, 2);
+
+    LruCache<int, int>::Iterator it(cache);
+    std::unordered_set<int> returnedValues;
+    while (it.next()) {
+        returnedValues.insert(it.value());
+    }
+    EXPECT_EQ(std::unordered_set<int>({ 2 }), returnedValues);
+}
+
+TEST_F(LruCacheTest, OneElementCacheRemove) {
+    LruCache<int, int> cache(100);
+    cache.put(1, 2);
+
+    cache.remove(1);
+
+    LruCache<int, int>::Iterator it(cache);
+    std::unordered_set<int> returnedValues;
+    while (it.next()) {
+        returnedValues.insert(it.value());
+    }
+    EXPECT_EQ(std::unordered_set<int>({ }), returnedValues);
+}
+
+TEST_F(LruCacheTest, Remove) {
+    LruCache<int, int> cache(100);
+    cache.put(1, 4);
+    cache.put(2, 5);
+    cache.put(3, 6);
+
+    cache.remove(2);
+
+    LruCache<int, int>::Iterator it(cache);
+    std::unordered_set<int> returnedValues;
+    while (it.next()) {
+        returnedValues.insert(it.value());
+    }
+    EXPECT_EQ(std::unordered_set<int>({ 4, 6 }), returnedValues);
+}
+
+TEST_F(LruCacheTest, RemoveYoungest) {
+    LruCache<int, int> cache(100);
+    cache.put(1, 4);
+    cache.put(2, 5);
+    cache.put(3, 6);
+
+    cache.remove(3);
+
+    LruCache<int, int>::Iterator it(cache);
+    std::unordered_set<int> returnedValues;
+    while (it.next()) {
+        returnedValues.insert(it.value());
+    }
+    EXPECT_EQ(std::unordered_set<int>({ 4, 5 }), returnedValues);
+}
+
+TEST_F(LruCacheTest, RemoveNonMember) {
+    LruCache<int, int> cache(100);
+    cache.put(1, 4);
+    cache.put(2, 5);
+    cache.put(3, 6);
+
+    cache.remove(7);
+
+    LruCache<int, int>::Iterator it(cache);
+    std::unordered_set<int> returnedValues;
+    while (it.next()) {
+        returnedValues.insert(it.value());
+    }
+    EXPECT_EQ(std::unordered_set<int>({ 4, 5, 6 }), returnedValues);
+}
+
 }
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index 808d17a..2f7cbfb 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -183,7 +183,7 @@
     static const char comm_str[] = " comm=\"";
     const char *comm = strstr(str, comm_str);
     const char *estr = str + strlen(str);
-    char *commfree = NULL;
+    const char *commfree = NULL;
     if (comm) {
         estr = comm;
         comm += sizeof(comm_str) - 1;
@@ -231,7 +231,7 @@
         // end scope for main buffer
     }
 
-    free(commfree);
+    free(const_cast<char *>(commfree));
     free(str);
 
     if (notify) {
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index d8e0b90..371c796 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -78,9 +78,9 @@
     std::string formatPrune() { return mPrune.format(); }
 
     // helper must be protected directly or implicitly by lock()/unlock()
-    char *pidToName(pid_t pid) { return stats.pidToName(pid); }
+    const char *pidToName(pid_t pid) { return stats.pidToName(pid); }
     uid_t pidToUid(pid_t pid) { return stats.pidToUid(pid); }
-    char *uidToName(uid_t uid) { return stats.uidToName(uid); }
+    const char *uidToName(uid_t uid) { return stats.uidToName(uid); }
     void lock() { pthread_mutex_lock(&mLogElementsLock); }
     void unlock() { pthread_mutex_unlock(&mLogElementsLock); }
 
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 150ce22..c4c302b 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -113,9 +113,9 @@
 
     static const char format_uid[] = "uid=%u%s%s expire %u line%s";
     parent->lock();
-    char *name = parent->uidToName(mUid);
+    const char *name = parent->uidToName(mUid);
     parent->unlock();
-    char *commName = android::tidToName(mTid);
+    const char *commName = android::tidToName(mTid);
     if (!commName && (mTid != mPid)) {
         commName = android::tidToName(mPid);
     }
@@ -128,28 +128,28 @@
         size_t len = strlen(name + 1);
         if (!strncmp(name + 1, commName + 1, len)) {
             if (commName[len + 1] == '\0') {
-                free(commName);
+                free(const_cast<char *>(commName));
                 commName = NULL;
             } else {
-                free(name);
+                free(const_cast<char *>(name));
                 name = NULL;
             }
         }
     }
     if (name) {
-        char *p = NULL;
-        asprintf(&p, "(%s)", name);
-        if (p) {
-            free(name);
-            name = p;
+        char *buf = NULL;
+        asprintf(&buf, "(%s)", name);
+        if (buf) {
+            free(const_cast<char *>(name));
+            name = buf;
         }
     }
     if (commName) {
-        char *p = NULL;
-        asprintf(&p, " %s", commName);
-        if (p) {
-            free(commName);
-            commName = p;
+        char *buf = NULL;
+        asprintf(&buf, " %s", commName);
+        if (buf) {
+            free(const_cast<char *>(commName));
+            commName = buf;
         }
     }
     // identical to below to calculate the buffer size required
@@ -166,18 +166,19 @@
 
     buffer = static_cast<char *>(calloc(1, hdrLen + len + 1));
     if (!buffer) {
-        free(name);
-        free(commName);
+        free(const_cast<char *>(name));
+        free(const_cast<char *>(commName));
         return 0;
     }
 
     size_t retval = hdrLen + len;
     if (mLogId == LOG_ID_EVENTS) {
-        android_log_event_string_t *e = reinterpret_cast<android_log_event_string_t *>(buffer);
+        android_log_event_string_t *event =
+            reinterpret_cast<android_log_event_string_t *>(buffer);
 
-        e->header.tag = htole32(LOGD_LOG_TAG);
-        e->type = EVENT_TYPE_STRING;
-        e->length = htole32(len);
+        event->header.tag = htole32(LOGD_LOG_TAG);
+        event->type = EVENT_TYPE_STRING;
+        event->length = htole32(len);
     } else {
         ++retval;
         buffer[0] = ANDROID_LOG_INFO;
@@ -187,8 +188,8 @@
     snprintf(buffer + hdrLen, len + 1, format_uid, mUid, name ? name : "",
              commName ? commName : "",
              mDropped, (mDropped > 1) ? "s" : "");
-    free(name);
-    free(commName);
+    free(const_cast<char *>(name));
+    free(const_cast<char *>(commName));
 
     return retval;
 }
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index a9c3086..c3b10ad 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -14,15 +14,12 @@
  * limitations under the License.
  */
 
-#include <algorithm> // std::max
 #include <fcntl.h>
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
 
-#include <base/stringprintf.h>
 #include <log/logger.h>
-#include <private/android_filesystem_config.h>
 
 #include "LogStatistics.h"
 
@@ -63,9 +60,9 @@
 
 }
 
-void LogStatistics::add(LogBufferElement *e) {
-    log_id_t log_id = e->getLogId();
-    unsigned short size = e->getMsgLen();
+void LogStatistics::add(LogBufferElement *element) {
+    log_id_t log_id = element->getLogId();
+    unsigned short size = element->getMsgLen();
     mSizes[log_id] += size;
     ++mElements[log_id];
 
@@ -76,24 +73,24 @@
         return;
     }
 
-    uidTable[log_id].add(e->getUid(), e);
+    uidTable[log_id].add(element->getUid(), element);
 
     if (!enable) {
         return;
     }
 
-    pidTable.add(e->getPid(), e);
-    tidTable.add(e->getTid(), e);
+    pidTable.add(element->getPid(), element);
+    tidTable.add(element->getTid(), element);
 
-    uint32_t tag = e->getTag();
+    uint32_t tag = element->getTag();
     if (tag) {
-        tagTable.add(tag, e);
+        tagTable.add(tag, element);
     }
 }
 
-void LogStatistics::subtract(LogBufferElement *e) {
-    log_id_t log_id = e->getLogId();
-    unsigned short size = e->getMsgLen();
+void LogStatistics::subtract(LogBufferElement *element) {
+    log_id_t log_id = element->getLogId();
+    unsigned short size = element->getMsgLen();
     mSizes[log_id] -= size;
     --mElements[log_id];
 
@@ -101,40 +98,40 @@
         return;
     }
 
-    uidTable[log_id].subtract(e->getUid(), e);
+    uidTable[log_id].subtract(element->getUid(), element);
 
     if (!enable) {
         return;
     }
 
-    pidTable.subtract(e->getPid(), e);
-    tidTable.subtract(e->getTid(), e);
+    pidTable.subtract(element->getPid(), element);
+    tidTable.subtract(element->getTid(), element);
 
-    uint32_t tag = e->getTag();
+    uint32_t tag = element->getTag();
     if (tag) {
-        tagTable.subtract(tag, e);
+        tagTable.subtract(tag, element);
     }
 }
 
 // Atomically set an entry to drop
 // entry->setDropped(1) must follow this call, caller should do this explicitly.
-void LogStatistics::drop(LogBufferElement *e) {
-    log_id_t log_id = e->getLogId();
-    unsigned short size = e->getMsgLen();
+void LogStatistics::drop(LogBufferElement *element) {
+    log_id_t log_id = element->getLogId();
+    unsigned short size = element->getMsgLen();
     mSizes[log_id] -= size;
 
-    uidTable[log_id].drop(e->getUid(), e);
+    uidTable[log_id].drop(element->getUid(), element);
 
     if (!enable) {
         return;
     }
 
-    pidTable.drop(e->getPid(), e);
-    tidTable.drop(e->getTid(), e);
+    pidTable.drop(element->getPid(), element);
+    tidTable.drop(element->getTid(), element);
 }
 
 // caller must own and free character string
-char *LogStatistics::uidToName(uid_t uid) {
+const char *LogStatistics::uidToName(uid_t uid) const {
     // Local hard coded favourites
     if (uid == AID_LOGD) {
         return strdup("auditd");
@@ -152,7 +149,7 @@
 
     // Parse /data/system/packages.list
     uid_t userId = uid % AID_USER;
-    char *name = android::uidToName(userId);
+    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));
     }
@@ -161,17 +158,17 @@
     }
 
     // report uid -> pid(s) -> pidToName if unique
-    for(pidTable_t::iterator it = pidTable.begin(); it != pidTable.end(); ++it) {
+    for(pidTable_t::const_iterator it = pidTable.begin(); it != pidTable.end(); ++it) {
         const PidEntry &entry = it->second;
 
         if (entry.getUid() == uid) {
-            const char *n = entry.getName();
+            const char *nameTmp = entry.getName();
 
-            if (n) {
+            if (nameTmp) {
                 if (!name) {
-                    name = strdup(n);
-                } else if (fast<strcmp>(name, n)) {
-                    free(name);
+                    name = strdup(nameTmp);
+                } else if (fast<strcmp>(name, nameTmp)) {
+                    free(const_cast<char *>(name));
                     name = NULL;
                     break;
                 }
@@ -183,28 +180,152 @@
     return name;
 }
 
-static std::string format_line(
-        const std::string &name,
-        const std::string &size,
-        const std::string &pruned) {
-    static const size_t pruned_len = 6;
-    static const size_t total_len = 70 + pruned_len;
-
-    ssize_t drop_len = std::max(pruned.length() + 1, pruned_len);
-    ssize_t size_len = std::max(size.length() + 1,
-                                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 UidEntry::formatHeader(const std::string &name, log_id_t id) const {
+    bool isprune = worstUidEnabledForLogid(id);
+    return formatLine(android::base::StringPrintf(
+                          name.c_str(), android_log_id_to_name(id)),
+                      std::string("Size"),
+                      std::string(isprune ? "Pruned" : ""))
+         + formatLine(std::string("UID   PACKAGE"),
+                      std::string("BYTES"),
+                      std::string(isprune ? "NUM" : ""));
 }
 
-std::string LogStatistics::format(uid_t uid, unsigned int logMask) {
+std::string UidEntry::format(const LogStatistics &stat, log_id_t /* id */) const {
+    uid_t uid = getKey();
+    std::string name = android::base::StringPrintf("%u", uid);
+    const char *nameTmp = stat.uidToName(uid);
+    if (nameTmp) {
+        name += android::base::StringPrintf(
+            "%*s%s", (int)std::max(6 - name.length(), (size_t)1),
+            "", nameTmp);
+        free(const_cast<char *>(nameTmp));
+    }
+
+    std::string size = android::base::StringPrintf("%zu", getSizes());
+
+    std::string pruned = "";
+    size_t dropped = getDropped();
+    if (dropped) {
+        pruned = android::base::StringPrintf("%zu", dropped);
+    }
+
+    return formatLine(name, size, pruned);
+}
+
+std::string PidEntry::formatHeader(const std::string &name, log_id_t /* id */) const {
+    return formatLine(name,
+                      std::string("Size"),
+                      std::string("Pruned"))
+         + formatLine(std::string("  PID/UID   COMMAND LINE"),
+                      std::string("BYTES"),
+                      std::string("NUM"));
+}
+
+std::string PidEntry::format(const LogStatistics &stat, log_id_t /* id */) const {
+    uid_t uid = getUid();
+    std::string name = android::base::StringPrintf("%5u/%u",
+                                                   getKey(), uid);
+    const char *nameTmp = getName();
+    if (nameTmp) {
+        name += android::base::StringPrintf(
+            "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
+            "", nameTmp);
+    } else if ((nameTmp = stat.uidToName(uid))) {
+        name += android::base::StringPrintf(
+            "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
+            "", nameTmp);
+        free(const_cast<char *>(nameTmp));
+    }
+
+    std::string size = android::base::StringPrintf("%zu",
+                                                   getSizes());
+
+    std::string pruned = "";
+    size_t dropped = getDropped();
+    if (dropped) {
+        pruned = android::base::StringPrintf("%zu", dropped);
+    }
+
+    return formatLine(name, size, pruned);
+}
+
+std::string TidEntry::formatHeader(const std::string &name, log_id_t /* id */) const {
+    return formatLine(name,
+                      std::string("Size"),
+                      std::string("Pruned"))
+         + formatLine(std::string("  TID/UID   COMM"),
+                      std::string("BYTES"),
+                      std::string("NUM"));
+}
+
+std::string TidEntry::format(const LogStatistics &stat, log_id_t /* id */) const {
+    uid_t uid = getUid();
+    std::string name = android::base::StringPrintf("%5u/%u",
+                                                   getKey(), uid);
+    const char *nameTmp = getName();
+    if (nameTmp) {
+        name += android::base::StringPrintf(
+            "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
+            "", nameTmp);
+    } else if ((nameTmp = stat.uidToName(uid))) {
+        // if we do not have a PID name, lets punt to try UID name?
+        name += android::base::StringPrintf(
+            "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
+            "", nameTmp);
+        free(const_cast<char *>(nameTmp));
+        // We tried, better to not have a name at all, we still
+        // have TID/UID by number to report in any case.
+    }
+
+    std::string size = android::base::StringPrintf("%zu",
+                                                   getSizes());
+
+    std::string pruned = "";
+    size_t dropped = getDropped();
+    if (dropped) {
+        pruned = android::base::StringPrintf("%zu", dropped);
+    }
+
+    return formatLine(name, size, pruned);
+}
+
+std::string TagEntry::formatHeader(const std::string &name, log_id_t id) const {
+    bool isprune = worstUidEnabledForLogid(id);
+    return formatLine(name,
+                      std::string("Size"),
+                      std::string(isprune ? "Prune" : ""))
+         + formatLine(std::string("    TAG/UID   TAGNAME"),
+                      std::string("BYTES"),
+                      std::string(isprune ? "NUM" : ""));
+}
+
+std::string TagEntry::format(const LogStatistics & /* stat */, log_id_t /* id */) const {
+    std::string name;
+    uid_t uid = getUid();
+    if (uid == (uid_t)-1) {
+        name = android::base::StringPrintf("%7u",
+                                           getKey());
+    } else {
+        name = android::base::StringPrintf("%7u/%u",
+                                           getKey(), uid);
+    }
+    const char *nameTmp = getName();
+    if (nameTmp) {
+        name += android::base::StringPrintf(
+            "%*s%s", (int)std::max(14 - name.length(), (size_t)1),
+            "", nameTmp);
+    }
+
+    std::string size = android::base::StringPrintf("%zu",
+                                                   getSizes());
+
+    std::string pruned = "";
+
+    return formatLine(name, size, pruned);
+}
+
+std::string LogStatistics::format(uid_t uid, unsigned int logMask) const {
     static const unsigned short spaces_total = 19;
 
     // Report on total logging, current and for all time
@@ -227,7 +348,7 @@
     }
 
     spaces = 4;
-    output += android::base::StringPrintf("\nTotal");
+    output += "\nTotal";
 
     log_id_for_each(id) {
         if (!(logMask & (1 << id))) {
@@ -244,7 +365,7 @@
     }
 
     spaces = 6;
-    output += android::base::StringPrintf("\nNow");
+    output += "\nNow";
 
     log_id_for_each(id) {
         if (!(logMask & (1 << id))) {
@@ -266,245 +387,30 @@
 
     // Report on Chattiest
 
+    std::string name;
+
     // Chattiest by application (UID)
-    static const size_t maximum_sorted_entries = 32;
     log_id_for_each(id) {
         if (!(logMask & (1 << id))) {
             continue;
         }
 
-        bool headerPrinted = false;
-        std::unique_ptr<const UidEntry *[]> sorted = sort(maximum_sorted_entries, id);
-        ssize_t index = -1;
-        while ((index = uidTable_t::next(index, sorted, maximum_sorted_entries)) >= 0) {
-            const UidEntry *entry = sorted[index];
-            uid_t u = entry->getKey();
-            if ((uid != AID_ROOT) && (u != uid)) {
-                continue;
-            }
-
-            if (!headerPrinted) {
-                output += android::base::StringPrintf("\n\n");
-                std::string name;
-                if (uid == AID_ROOT) {
-                    name = android::base::StringPrintf(
-                        "Chattiest UIDs in %s log buffer:",
-                        android_log_id_to_name(id));
-                } else {
-                    name = android::base::StringPrintf(
-                        "Logging for your UID in %s log buffer:",
-                        android_log_id_to_name(id));
-                }
-                std::string size = "Size";
-                std::string pruned = "Pruned";
-                if (!worstUidEnabledForLogid(id)) {
-                    pruned = "";
-                }
-                output += format_line(name, size, pruned);
-
-                name = "UID   PACKAGE";
-                size = "BYTES";
-                pruned = "LINES";
-                if (!worstUidEnabledForLogid(id)) {
-                    pruned = "";
-                }
-                output += format_line(name, size, pruned);
-
-                headerPrinted = true;
-            }
-
-            std::string name = android::base::StringPrintf("%u", u);
-            char *n = uidToName(u);
-            if (n) {
-                name += android::base::StringPrintf(
-                    "%*s%s", (int)std::max(6 - name.length(), (size_t)1),
-                    "", n);
-                free(n);
-            }
-
-            std::string size = android::base::StringPrintf("%zu",
-                                                           entry->getSizes());
-
-            std::string pruned = "";
-            size_t dropped = entry->getDropped();
-            if (dropped) {
-                pruned = android::base::StringPrintf("%zu", dropped);
-            }
-
-            output += format_line(name, size, pruned);
-        }
+        name = (uid == AID_ROOT)
+            ? "Chattiest UIDs in %s log buffer:"
+            : "Logging for your UID in %s log buffer:";
+        output += uidTable[id].format(*this, uid, name, id);
     }
 
     if (enable) {
-        // Pid table
-        bool headerPrinted = false;
-        std::unique_ptr<const PidEntry *[]> sorted = pidTable.sort(maximum_sorted_entries);
-        ssize_t index = -1;
-        while ((index = pidTable.next(index, sorted, maximum_sorted_entries)) >= 0) {
-            const PidEntry *entry = sorted[index];
-            uid_t u = entry->getUid();
-            if ((uid != AID_ROOT) && (u != uid)) {
-                continue;
-            }
-
-            if (!headerPrinted) {
-                output += android::base::StringPrintf("\n\n");
-                std::string name;
-                if (uid == AID_ROOT) {
-                    name = android::base::StringPrintf("Chattiest PIDs:");
-                } else {
-                    name = android::base::StringPrintf("Logging for this PID:");
-                }
-                std::string size = "Size";
-                std::string pruned = "Pruned";
-                output += format_line(name, size, pruned);
-
-                name = "  PID/UID   COMMAND LINE";
-                size = "BYTES";
-                pruned = "LINES";
-                output += format_line(name, size, pruned);
-
-                headerPrinted = true;
-            }
-
-            std::string name = android::base::StringPrintf("%5u/%u",
-                                                           entry->getKey(), u);
-            const char *n = entry->getName();
-            if (n) {
-                name += android::base::StringPrintf(
-                    "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
-                    "", n);
-            } else {
-                char *un = uidToName(u);
-                if (un) {
-                    name += android::base::StringPrintf(
-                        "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
-                        "", un);
-                    free(un);
-                }
-            }
-
-            std::string size = android::base::StringPrintf("%zu",
-                                                           entry->getSizes());
-
-            std::string pruned = "";
-            size_t dropped = entry->getDropped();
-            if (dropped) {
-                pruned = android::base::StringPrintf("%zu", dropped);
-            }
-
-            output += format_line(name, size, pruned);
-        }
-    }
-
-    if (enable) {
-        // Tid table
-        bool headerPrinted = false;
-        // sort() returns list of references, unique_ptr makes sure self-delete
-        std::unique_ptr<const TidEntry *[]> sorted = tidTable.sort(maximum_sorted_entries);
-        ssize_t index = -1;
-        while ((index = tidTable.next(index, sorted, maximum_sorted_entries)) >= 0) {
-            const TidEntry *entry = sorted[index];
-            uid_t u = entry->getUid();
-            if ((uid != AID_ROOT) && (u != uid)) {
-                continue;
-            }
-
-            if (!headerPrinted) { // Only print header if we have table to print
-                output += android::base::StringPrintf("\n\n");
-                std::string name = "Chattiest TIDs:";
-                std::string size = "Size";
-                std::string pruned = "Pruned";
-                output += format_line(name, size, pruned);
-
-                name = "  TID/UID   COMM";
-                size = "BYTES";
-                pruned = "LINES";
-                output += format_line(name, size, pruned);
-
-                headerPrinted = true;
-            }
-
-            std::string name = android::base::StringPrintf("%5u/%u",
-                                                           entry->getKey(), u);
-            const char *n = entry->getName();
-            if (n) {
-                name += android::base::StringPrintf(
-                    "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
-                    "", n);
-            } else {
-                // if we do not have a PID name, lets punt to try UID name?
-                char *un = uidToName(u);
-                if (un) {
-                    name += android::base::StringPrintf(
-                        "%*s%s", (int)std::max(12 - name.length(), (size_t)1),
-                        "", un);
-                    free(un);
-                }
-                // We tried, better to not have a name at all, we still
-                // have TID/UID by number to report in any case.
-            }
-
-            std::string size = android::base::StringPrintf("%zu",
-                                                           entry->getSizes());
-
-            std::string pruned = "";
-            size_t dropped = entry->getDropped();
-            if (dropped) {
-                pruned = android::base::StringPrintf("%zu", dropped);
-            }
-
-            output += format_line(name, size, pruned);
-        }
+        name = (uid == AID_ROOT) ? "Chattiest PIDs:" : "Logging for this PID:";
+        output += pidTable.format(*this, uid, name);
+        name = "Chattiest TIDs:";
+        output += tidTable.format(*this, uid, name);
     }
 
     if (enable && (logMask & (1 << LOG_ID_EVENTS))) {
-        // Tag table
-        bool headerPrinted = false;
-        std::unique_ptr<const TagEntry *[]> sorted = tagTable.sort(maximum_sorted_entries);
-        ssize_t index = -1;
-        while ((index = tagTable.next(index, sorted, maximum_sorted_entries)) >= 0) {
-            const TagEntry *entry = sorted[index];
-            uid_t u = entry->getUid();
-            if ((uid != AID_ROOT) && (u != uid)) {
-                continue;
-            }
-
-            std::string pruned = "";
-
-            if (!headerPrinted) {
-                output += android::base::StringPrintf("\n\n");
-                std::string name = "Chattiest events log buffer TAGs:";
-                std::string size = "Size";
-                output += format_line(name, size, pruned);
-
-                name = "    TAG/UID   TAGNAME";
-                size = "BYTES";
-                output += format_line(name, size, pruned);
-
-                headerPrinted = true;
-            }
-
-            std::string name;
-            if (u == (uid_t)-1) {
-                name = android::base::StringPrintf("%7u",
-                                                   entry->getKey());
-            } else {
-                name = android::base::StringPrintf("%7u/%u",
-                                                   entry->getKey(), u);
-            }
-            const char *n = entry->getName();
-            if (n) {
-                name += android::base::StringPrintf(
-                    "%*s%s", (int)std::max(14 - name.length(), (size_t)1),
-                    "", n);
-            }
-
-            std::string size = android::base::StringPrintf("%zu",
-                                                           entry->getSizes());
-
-            output += format_line(name, size, pruned);
-        }
+        name = "Chattiest events log buffer TAGs:";
+        output += tagTable.format(*this, uid, name, LOG_ID_EVENTS);
     }
 
     return output;
@@ -536,8 +442,10 @@
 }
 
 // caller must free character string
-char *LogStatistics::pidToName(pid_t pid) {
-    const char *name = pidTable.add(pid)->second.getName();
+const char *LogStatistics::pidToName(pid_t pid) const {
+    // An inconvenient truth ... getName() can alter the object
+    pidTable_t &writablePidTable = const_cast<pidTable_t &>(pidTable);
+    const char *name = writablePidTable.add(pid)->second.getName();
     if (!name) {
         return NULL;
     }
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index 5a44d59..dd13380 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -21,9 +21,13 @@
 #include <stdlib.h>
 #include <sys/types.h>
 
+#include <algorithm> // std::max
+#include <string>    // std::string
 #include <unordered_map>
 
+#include <base/stringprintf.h>
 #include <log/log.h>
+#include <private/android_filesystem_config.h>
 
 #include "LogBufferElement.h"
 #include "LogUtils.h"
@@ -31,6 +35,8 @@
 #define log_id_for_each(i) \
     for (log_id_t i = LOG_ID_MIN; i < LOG_ID_MAX; i = (log_id_t) (i + 1))
 
+class LogStatistics;
+
 template <typename TKey, typename TEntry>
 class LogHashtable {
 
@@ -39,50 +45,43 @@
 public:
 
     typedef typename std::unordered_map<TKey, TEntry>::iterator iterator;
+    typedef typename std::unordered_map<TKey, TEntry>::const_iterator const_iterator;
 
-    std::unique_ptr<const TEntry *[]> sort(size_t n) {
-        if (!n) {
+    std::unique_ptr<const TEntry *[]> sort(size_t len) const {
+        if (!len) {
             std::unique_ptr<const TEntry *[]> sorted(NULL);
             return sorted;
         }
 
-        const TEntry **retval = new const TEntry* [n];
-        memset(retval, 0, sizeof(*retval) * n);
+        const TEntry **retval = new const TEntry* [len];
+        memset(retval, 0, sizeof(*retval) * len);
 
-        for(iterator it = map.begin(); it != map.end(); ++it) {
+        for(const_iterator it = map.begin(); it != map.end(); ++it) {
             const TEntry &entry = it->second;
-            size_t s = entry.getSizes();
-            ssize_t i = n - 1;
-            while ((!retval[i] || (s > retval[i]->getSizes())) && (--i >= 0))
+            size_t sizes = entry.getSizes();
+            ssize_t index = len - 1;
+            while ((!retval[index] || (sizes > retval[index]->getSizes()))
+                    && (--index >= 0))
                 ;
-            if (++i < (ssize_t)n) {
-                size_t b = n - i - 1;
-                if (b) {
-                    memmove(&retval[i+1], &retval[i], b * sizeof(retval[0]));
+            if (++index < (ssize_t)len) {
+                size_t num = len - index - 1;
+                if (num) {
+                    memmove(&retval[index + 1], &retval[index],
+                            num * sizeof(retval[0]));
                 }
-                retval[i] = &entry;
+                retval[index] = &entry;
             }
         }
         std::unique_ptr<const TEntry *[]> sorted(retval);
         return sorted;
     }
 
-    // Iteration handler for the sort method output
-    static ssize_t next(ssize_t index, std::unique_ptr<const TEntry *[]> &sorted, size_t n) {
-        ++index;
-        if (!sorted.get() || (index < 0) || (n <= (size_t)index) || !sorted[index]
-         || (sorted[index]->getSizes() <= (sorted[0]->getSizes() / 100))) {
-            return -1;
-        }
-        return index;
-    }
-
-    inline iterator add(TKey key, LogBufferElement *e) {
+    inline iterator add(TKey key, LogBufferElement *element) {
         iterator it = map.find(key);
         if (it == map.end()) {
-            it = map.insert(std::make_pair(key, TEntry(e))).first;
+            it = map.insert(std::make_pair(key, TEntry(element))).first;
         } else {
-            it->second.add(e);
+            it->second.add(element);
         }
         return it;
     }
@@ -97,65 +96,138 @@
         return it;
     }
 
-    void subtract(TKey key, LogBufferElement *e) {
+    void subtract(TKey key, LogBufferElement *element) {
         iterator it = map.find(key);
-        if ((it != map.end()) && it->second.subtract(e)) {
+        if ((it != map.end()) && it->second.subtract(element)) {
             map.erase(it);
         }
     }
 
-    inline void drop(TKey key, LogBufferElement *e) {
+    inline void drop(TKey key, LogBufferElement *element) {
         iterator it = map.find(key);
         if (it != map.end()) {
-            it->second.drop(e);
+            it->second.drop(element);
         }
     }
 
     inline iterator begin() { return map.begin(); }
+    inline const_iterator begin() const { return map.begin(); }
     inline iterator end() { return map.end(); }
+    inline const_iterator end() const { return map.end(); }
 
+    std::string format(
+            const LogStatistics &stat,
+            uid_t uid,
+            const std::string &name = std::string(""),
+            log_id_t id = LOG_ID_MAX) const {
+        static const size_t maximum_sorted_entries = 32;
+        std::string output;
+        std::unique_ptr<const TEntry *[]> sorted = sort(maximum_sorted_entries);
+
+        if (!sorted.get()) {
+            return output;
+        }
+        bool headerPrinted = false;
+        for (size_t index = 0; index < maximum_sorted_entries; ++index) {
+            const TEntry *entry = sorted[index];
+            if (!entry) {
+                break;
+            }
+            if (entry->getSizes() <= (sorted[0]->getSizes() / 100)) {
+                break;
+            }
+            if ((uid != AID_ROOT) && (uid != entry->getUid())) {
+                continue;
+            }
+            if (!headerPrinted) {
+                output += "\n\n";
+                output += entry->formatHeader(name, id);
+                headerPrinted = true;
+            }
+            output += entry->format(stat, id);
+        }
+        return output;
+    }
 };
 
+namespace EntryBaseConstants {
+    static constexpr size_t pruned_len = 14;
+    static constexpr size_t total_len = 80;
+}
+
 struct EntryBase {
     size_t size;
 
     EntryBase():size(0) { }
-    EntryBase(LogBufferElement *e):size(e->getMsgLen()) { }
+    EntryBase(LogBufferElement *element):size(element->getMsgLen()) { }
 
     size_t getSizes() const { return size; }
 
-    inline void add(LogBufferElement *e) { size += e->getMsgLen(); }
-    inline bool subtract(LogBufferElement *e) { size -= e->getMsgLen(); return !size; }
+    inline void add(LogBufferElement *element) { size += element->getMsgLen(); }
+    inline bool subtract(LogBufferElement *element) {
+        size -= element->getMsgLen();
+        return !size;
+    }
+
+    static std::string formatLine(
+            const std::string &name,
+            const std::string &size,
+            const std::string &pruned) {
+        ssize_t drop_len = std::max(pruned.length() + 1,
+                                    EntryBaseConstants::pruned_len);
+        ssize_t size_len = std::max(size.length() + 1,
+                                    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());
+        }
+    }
 };
 
 struct EntryBaseDropped : public EntryBase {
     size_t dropped;
 
     EntryBaseDropped():dropped(0) { }
-    EntryBaseDropped(LogBufferElement *e):EntryBase(e),dropped(e->getDropped()){ }
+    EntryBaseDropped(LogBufferElement *element):
+            EntryBase(element),
+            dropped(element->getDropped()){
+    }
 
     size_t getDropped() const { return dropped; }
 
-    inline void add(LogBufferElement *e) {
-        dropped += e->getDropped();
-        EntryBase::add(e);
+    inline void add(LogBufferElement *element) {
+        dropped += element->getDropped();
+        EntryBase::add(element);
     }
-    inline bool subtract(LogBufferElement *e) {
-        dropped -= e->getDropped();
-        return EntryBase::subtract(e) && !dropped;
+    inline bool subtract(LogBufferElement *element) {
+        dropped -= element->getDropped();
+        return EntryBase::subtract(element) && !dropped;
     }
-    inline void drop(LogBufferElement *e) {
+    inline void drop(LogBufferElement *element) {
         dropped += 1;
-        EntryBase::subtract(e);
+        EntryBase::subtract(element);
     }
 };
 
 struct UidEntry : public EntryBaseDropped {
     const uid_t uid;
 
-    UidEntry(LogBufferElement *e):EntryBaseDropped(e),uid(e->getUid()) { }
+    UidEntry(LogBufferElement *element):
+            EntryBaseDropped(element),
+            uid(element->getUid()) {
+    }
 
     inline const uid_t&getKey() const { return uid; }
+    inline const uid_t&getUid() const { return uid; }
+
+    std::string formatHeader(const std::string &name, log_id_t id) const;
+    std::string format(const LogStatistics &stat, log_id_t id) const;
 };
 
 namespace android {
@@ -167,51 +239,54 @@
     uid_t uid;
     char *name;
 
-    PidEntry(pid_t p):
-        EntryBaseDropped(),
-        pid(p),
-        uid(android::pidToUid(p)),
-        name(android::pidToName(pid)) { }
-    PidEntry(LogBufferElement *e):
-        EntryBaseDropped(e),
-        pid(e->getPid()),
-        uid(e->getUid()),
-        name(android::pidToName(e->getPid())) { }
-    PidEntry(const PidEntry &c):
-        EntryBaseDropped(c),
-        pid(c.pid),
-        uid(c.uid),
-        name(c.name ? strdup(c.name) : NULL) { }
+    PidEntry(pid_t pid):
+            EntryBaseDropped(),
+            pid(pid),
+            uid(android::pidToUid(pid)),
+            name(android::pidToName(pid)) {
+    }
+    PidEntry(LogBufferElement *element):
+            EntryBaseDropped(element),
+            pid(element->getPid()),
+            uid(element->getUid()),
+            name(android::pidToName(pid)) {
+    }
+    PidEntry(const PidEntry &element):
+            EntryBaseDropped(element),
+            pid(element.pid),
+            uid(element.uid),
+            name(element.name ? strdup(element.name) : NULL) {
+    }
     ~PidEntry() { free(name); }
 
     const pid_t&getKey() const { return pid; }
     const uid_t&getUid() const { return uid; }
     const char*getName() const { return name; }
 
-    inline void add(pid_t p) {
+    inline void add(pid_t newPid) {
         if (name && !fast<strncmp>(name, "zygote", 6)) {
             free(name);
             name = NULL;
         }
         if (!name) {
-            char *n = android::pidToName(p);
-            if (n) {
-                name = n;
-            }
+            name = android::pidToName(newPid);
         }
     }
 
-    inline void add(LogBufferElement *e) {
-        uid_t u = e->getUid();
-        if (getUid() != u) {
-            uid = u;
+    inline void add(LogBufferElement *element) {
+        uid_t incomingUid = element->getUid();
+        if (getUid() != incomingUid) {
+            uid = incomingUid;
             free(name);
-            name = android::pidToName(e->getPid());
+            name = android::pidToName(element->getPid());
         } else {
-            add(e->getPid());
+            add(element->getPid());
         }
-        EntryBaseDropped::add(e);
+        EntryBaseDropped::add(element);
     }
+
+    std::string formatHeader(const std::string &name, log_id_t id) const;
+    std::string format(const LogStatistics &stat, log_id_t id) const;
 };
 
 struct TidEntry : public EntryBaseDropped {
@@ -219,73 +294,80 @@
     uid_t uid;
     char *name;
 
-    TidEntry(pid_t t):
-        EntryBaseDropped(),
-        tid(t),
-        uid(android::pidToUid(t)),
-        name(android::tidToName(tid)) { }
-    TidEntry(LogBufferElement *e):
-        EntryBaseDropped(e),
-        tid(e->getTid()),
-        uid(e->getUid()),
-        name(android::tidToName(e->getTid())) { }
-    TidEntry(const TidEntry &c):
-        EntryBaseDropped(c),
-        tid(c.tid),
-        uid(c.uid),
-        name(c.name ? strdup(c.name) : NULL) { }
+    TidEntry(pid_t tid):
+            EntryBaseDropped(),
+            tid(tid),
+            uid(android::pidToUid(tid)),
+            name(android::tidToName(tid)) {
+    }
+    TidEntry(LogBufferElement *element):
+            EntryBaseDropped(element),
+            tid(element->getTid()),
+            uid(element->getUid()),
+            name(android::tidToName(tid)) {
+    }
+    TidEntry(const TidEntry &element):
+            EntryBaseDropped(element),
+            tid(element.tid),
+            uid(element.uid),
+            name(element.name ? strdup(element.name) : NULL) {
+    }
     ~TidEntry() { free(name); }
 
     const pid_t&getKey() const { return tid; }
     const uid_t&getUid() const { return uid; }
     const char*getName() const { return name; }
 
-    inline void add(pid_t t) {
+    inline void add(pid_t incomingTid) {
         if (name && !fast<strncmp>(name, "zygote", 6)) {
             free(name);
             name = NULL;
         }
         if (!name) {
-            char *n = android::tidToName(t);
-            if (n) {
-                name = n;
-            }
+            name = android::tidToName(incomingTid);
         }
     }
 
-    inline void add(LogBufferElement *e) {
-        uid_t u = e->getUid();
-        if (getUid() != u) {
-            uid = u;
+    inline void add(LogBufferElement *element) {
+        uid_t incomingUid = element->getUid();
+        if (getUid() != incomingUid) {
+            uid = incomingUid;
             free(name);
-            name = android::tidToName(e->getTid());
+            name = android::tidToName(element->getTid());
         } else {
-            add(e->getTid());
+            add(element->getTid());
         }
-        EntryBaseDropped::add(e);
+        EntryBaseDropped::add(element);
     }
+
+    std::string formatHeader(const std::string &name, log_id_t id) const;
+    std::string format(const LogStatistics &stat, log_id_t id) const;
 };
 
 struct TagEntry : public EntryBase {
     const uint32_t tag;
     uid_t uid;
 
-    TagEntry(LogBufferElement *e):
-        EntryBase(e),
-        tag(e->getTag()),
-        uid(e->getUid()) { }
+    TagEntry(LogBufferElement *element):
+            EntryBase(element),
+            tag(element->getTag()),
+            uid(element->getUid()) {
+    }
 
     const uint32_t&getKey() const { return tag; }
     const uid_t&getUid() const { return uid; }
     const char*getName() const { return android::tagToName(tag); }
 
-    inline void add(LogBufferElement *e) {
-        uid_t u = e->getUid();
-        if (uid != u) {
+    inline void add(LogBufferElement *element) {
+        uid_t incomingUid = element->getUid();
+        if (uid != incomingUid) {
             uid = -1;
         }
-        EntryBase::add(e);
+        EntryBase::add(element);
     }
+
+    std::string formatHeader(const std::string &name, log_id_t id) const;
+    std::string format(const LogStatistics &stat, log_id_t id) const;
 };
 
 // Log Statistics
@@ -324,7 +406,9 @@
     // Correct for merging two entries referencing dropped content
     void erase(LogBufferElement *e) { --mElements[e->getLogId()]; }
 
-    std::unique_ptr<const UidEntry *[]> sort(size_t n, log_id i) { return uidTable[i].sort(n); }
+    std::unique_ptr<const UidEntry *[]> sort(size_t len, log_id id) {
+        return uidTable[id].sort(len);
+    }
 
     // fast track current value by id only
     size_t sizes(log_id_t id) const { return mSizes[id]; }
@@ -332,12 +416,12 @@
     size_t sizesTotal(log_id_t id) const { return mSizesTotal[id]; }
     size_t elementsTotal(log_id_t id) const { return mElementsTotal[id]; }
 
-    std::string format(uid_t uid, unsigned int logMask);
+    std::string format(uid_t uid, unsigned int logMask) const;
 
     // helper (must be locked directly or implicitly by mLogElementsLock)
-    char *pidToName(pid_t pid);
+    const char *pidToName(pid_t pid) const;
     uid_t pidToUid(pid_t pid);
-    char *uidToName(uid_t uid);
+    const char *uidToName(uid_t uid) const;
 };
 
 #endif // _LOGD_LOG_STATISTICS_H__
diff --git a/metricsd/Android.mk b/metricsd/Android.mk
index afb45da..3deb7d3 100644
--- a/metricsd/Android.mk
+++ b/metricsd/Android.mk
@@ -74,7 +74,7 @@
   libmetrics \
   libprotobuf-cpp-lite \
   librootdev \
-  libweaved-client \
+  libweaved \
 
 # Shared library for metrics.
 # ========================================================
diff --git a/metricsd/metrics_daemon.cc b/metricsd/metrics_daemon.cc
index 5b80b05..2ea5bbd 100644
--- a/metricsd/metrics_daemon.cc
+++ b/metricsd/metrics_daemon.cc
@@ -283,11 +283,15 @@
       return EX_UNAVAILABLE;
     }
 
-    weaved_object_mgr_.reset(new com::android::Weave::ObjectManagerProxy{bus_});
-    weaved_object_mgr_->SetCommandAddedCallback(
-        base::Bind(&MetricsDaemon::OnWeaveCommand, base::Unretained(this)));
-    weaved_object_mgr_->SetManagerAddedCallback(
+    device_ = weaved::Device::CreateInstance(
+        bus_,
         base::Bind(&MetricsDaemon::UpdateWeaveState, base::Unretained(this)));
+    device_->AddCommandHandler(
+        "_metrics._enableAnalyticsReporting",
+        base::Bind(&MetricsDaemon::OnEnableMetrics, base::Unretained(this)));
+    device_->AddCommandHandler(
+        "_metrics._disableAnalyticsReporting",
+        base::Bind(&MetricsDaemon::OnDisableMetrics, base::Unretained(this)));
   }
 
   base::MessageLoop::current()->PostDelayedTask(FROM_HERE,
@@ -325,56 +329,51 @@
   chromeos::DBusDaemon::OnShutdown(return_code);
 }
 
-void MetricsDaemon::OnWeaveCommand(CommandProxy* command) {
-  if (command->category() != "metrics" || command->status() != "queued") {
+void MetricsDaemon::OnEnableMetrics(const std::weak_ptr<weaved::Command>& cmd) {
+  auto command = cmd.lock();
+  if (!command)
     return;
-  }
 
-  VLOG(1) << "received weave command: " << command->name();
-  if (command->name() == "_metrics._enableAnalyticsReporting") {
-    OnEnableMetrics(command);
-  } else if (command->name() == "_metrics._disableAnalyticsReporting") {
-    OnDisableMetrics(command);
-  }
-}
-
-void MetricsDaemon::OnEnableMetrics(CommandProxy* command) {
   if (base::WriteFile(metrics_directory_.Append(metrics::kConsentFileName),
                       "", 0) != 0) {
     PLOG(ERROR) << "Could not create the consent file.";
-    command->Abort(nullptr);
+    command->Abort("metrics_error", "Could not create the consent file",
+                   nullptr);
     return;
   }
 
-  NotifyStateChanged();
-  command->Done(nullptr);
+  UpdateWeaveState();
+  command->Complete({}, nullptr);
 }
 
-void MetricsDaemon::OnDisableMetrics(CommandProxy* command) {
+void MetricsDaemon::OnDisableMetrics(
+    const std::weak_ptr<weaved::Command>& cmd) {
+  auto command = cmd.lock();
+  if (!command)
+    return;
+
   if (!base::DeleteFile(metrics_directory_.Append(metrics::kConsentFileName),
                         false)) {
-    PLOG(ERROR) << "Cound not delete the consent file.";
-    command->Abort(nullptr);
+    PLOG(ERROR) << "Could not delete the consent file.";
+    command->Abort("metrics_error", "Could not delete the consent file",
+                   nullptr);
     return;
   }
 
-  NotifyStateChanged();
-  command->Done(nullptr);
+  UpdateWeaveState();
+  command->Complete({}, nullptr);
 }
 
-void MetricsDaemon::NotifyStateChanged() {
-  ManagerProxy* manager = weaved_object_mgr_->GetManagerProxy();
-  if (manager)
-    UpdateWeaveState(manager);
-}
+void MetricsDaemon::UpdateWeaveState() {
+  if (!device_)
+    return;
 
-void MetricsDaemon::UpdateWeaveState(ManagerProxy* manager) {
   chromeos::VariantDictionary state_change{
     { "_metrics._AnalyticsReportingState",
       metrics_lib_->AreMetricsEnabled() ? "enabled" : "disabled" }
   };
 
-  if (!manager->UpdateState(state_change, nullptr)) {
+  if (!device_->SetStateProperties(state_change, nullptr)) {
     LOG(ERROR) << "failed to update weave's state";
   }
 }
diff --git a/metricsd/metrics_daemon.h b/metricsd/metrics_daemon.h
index 0a8f6a5..146108a 100644
--- a/metricsd/metrics_daemon.h
+++ b/metricsd/metrics_daemon.h
@@ -26,8 +26,9 @@
 #include <base/files/file_path.h>
 #include <base/memory/scoped_ptr.h>
 #include <base/time/time.h>
-#include <buffet/dbus-proxies.h>
 #include <chromeos/daemons/dbus_daemon.h>
+#include <libweaved/command.h>
+#include <libweaved/device.h>
 #include <gtest/gtest_prod.h>  // for FRIEND_TEST
 
 #include "collectors/averaged_statistics_collector.h"
@@ -122,20 +123,14 @@
                                          DBusMessage* message,
                                          void* user_data);
 
-  // Callback for Weave commands.
-  void OnWeaveCommand(com::android::Weave::CommandProxy* command);
-
   // Enables metrics reporting.
-  void OnEnableMetrics(com::android::Weave::CommandProxy* command);
+  void OnEnableMetrics(const std::weak_ptr<weaved::Command>& cmd);
 
   // Disables metrics reporting.
-  void OnDisableMetrics(com::android::Weave::CommandProxy* command);
+  void OnDisableMetrics(const std::weak_ptr<weaved::Command>& cmd);
 
   // Updates the weave device state.
-  void UpdateWeaveState(com::android::Weave::ManagerProxy* manager);
-
-  // Tells Weave that the state has changed.
-  void NotifyStateChanged();
+  void UpdateWeaveState();
 
   // Updates the active use time and logs time between user-space
   // process crashes.
@@ -317,7 +312,7 @@
   std::string server_;
 
   scoped_ptr<UploadService> upload_service_;
-  scoped_ptr<com::android::Weave::ObjectManagerProxy> weaved_object_mgr_;
+  std::unique_ptr<weaved::Device> device_;
 };
 
 #endif  // METRICS_METRICS_DAEMON_H_
diff --git a/rootdir/init.rc b/rootdir/init.rc
index bd4d156..38c686d 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -13,6 +13,9 @@
     # Set init and its forked children's oom_adj.
     write /proc/1/oom_score_adj -1000
 
+    # Disable sysrq from keyboard
+    write /proc/sys/kernel/sysrq 0
+
     # Set the security context of /adb_keys if present.
     restorecon /adb_keys