Merge "Use FUSE_COMPAT_22_INIT_OUT_SIZE always as the resopnse size of FUSE_INIT."
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 29d6e65..8b6b2b5 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -1089,6 +1089,31 @@
         return 1;
     }
 
+    if (!strcmp(service, "reconnect-offline")) {
+        std::string response;
+        close_usb_devices([&response](const atransport* transport) {
+            switch (transport->connection_state) {
+                case kCsOffline:
+                case kCsUnauthorized:
+                    response += "reconnecting ";
+                    if (transport->serial) {
+                        response += transport->serial;
+                    } else {
+                        response += "<unknown>";
+                    }
+                    response += "\n";
+                    return true;
+                default:
+                    return false;
+            }
+        });
+        if (!response.empty()) {
+            response.resize(response.size() - 1);
+        }
+        SendOkay(reply_fd, response);
+        return 0;
+    }
+
     if (!strcmp(service, "features")) {
         std::string error;
         atransport* t = acquire_one_transport(type, serial, nullptr, &error);
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 1f03c1a..e15bcad 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -1950,10 +1950,17 @@
     } else if (!strcmp(argv[0], "reconnect")) {
         if (argc == 1) {
             return adb_query_command("host:reconnect");
-        } else if (argc == 2 && !strcmp(argv[1], "device")) {
-            std::string err;
-            adb_connect("reconnect", &err);
-            return 0;
+        } else if (argc == 2) {
+            if (!strcmp(argv[1], "device")) {
+                std::string err;
+                adb_connect("reconnect", &err);
+                return 0;
+            } else if (!strcmp(argv[1], "offline")) {
+                std::string err;
+                return adb_query_command("host:reconnect-offline");
+            } else {
+                return usage();
+            }
         }
     }
 
diff --git a/adb/file_sync_service.cpp b/adb/file_sync_service.cpp
index 837902a..7a92d2e 100644
--- a/adb/file_sync_service.cpp
+++ b/adb/file_sync_service.cpp
@@ -198,11 +198,6 @@
         // Ignore the result of calling fchmod. It's not supported
         // by all filesystems, so we don't check for success. b/12441485
         fchmod(fd, mode);
-
-        if (!update_capabilities(path, capabilities)) {
-            SendSyncFailErrno(s, "update_capabilities failed");
-            goto fail;
-        }
     }
 
     while (true) {
@@ -232,6 +227,11 @@
 
     adb_close(fd);
 
+    if (!update_capabilities(path, capabilities)) {
+        SendSyncFailErrno(s, "update_capabilities failed");
+        goto fail;
+    }
+
     utimbuf u;
     u.actime = timestamp;
     u.modtime = timestamp;
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 3a57174..c05903f 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -791,11 +791,14 @@
     }
 #endif
 
-    if (!(s->transport) || (s->transport->connection_state == kCsOffline)) {
+    if (!s->transport) {
+        SendFail(s->peer->fd, "device offline (no transport)");
+        goto fail;
+    } else if (s->transport->connection_state == kCsOffline) {
         /* if there's no remote we fail the connection
          ** right here and terminate it
          */
-        SendFail(s->peer->fd, "device offline (x)");
+        SendFail(s->peer->fd, "device offline (transport offline)");
         goto fail;
     }
 
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 7b4bb1c..132702d 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -908,12 +908,18 @@
     return result;
 }
 
+void close_usb_devices(std::function<bool(const atransport*)> predicate) {
+    std::lock_guard<std::mutex> lock(transport_lock);
+    for (auto& t : transport_list) {
+        if (predicate(t)) {
+            t->Kick();
+        }
+    }
+}
+
 /* hack for osx */
 void close_usb_devices() {
-    std::lock_guard<std::mutex> lock(transport_lock);
-    for (const auto& t : transport_list) {
-        t->Kick();
-    }
+    close_usb_devices([](const atransport*) { return true; });
 }
 #endif // ADB_HOST
 
diff --git a/adb/transport.h b/adb/transport.h
index 621516c..b2df838 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -20,6 +20,7 @@
 #include <sys/types.h>
 
 #include <deque>
+#include <functional>
 #include <list>
 #include <memory>
 #include <string>
@@ -199,8 +200,8 @@
 int check_header(apacket* p, atransport* t);
 int check_data(apacket* p);
 
-/* for MacOS X cleanup */
 void close_usb_devices();
+void close_usb_devices(std::function<bool(const atransport*)> predicate);
 
 void send_packet(apacket* p, atransport* t);
 
diff --git a/debuggerd/crasher.cpp b/debuggerd/crasher.cpp
index 3db67b3..b0e8b17 100644
--- a/debuggerd/crasher.cpp
+++ b/debuggerd/crasher.cpp
@@ -160,7 +160,11 @@
 {
     fprintf(stderr, "%s: init pid=%d tid=%d\n", __progname, getpid(), gettid());
 
-    if (!strncmp(arg, "exhaustfd-", strlen("exhaustfd-"))) {
+    if (!strncmp(arg, "wait-", strlen("wait-"))) {
+      char buf[1];
+      TEMP_FAILURE_RETRY(read(STDIN_FILENO, buf, sizeof(buf)));
+      return do_action(arg + strlen("wait-"));
+    } else if (!strncmp(arg, "exhaustfd-", strlen("exhaustfd-"))) {
       errno = 0;
       while (errno != EMFILE) {
         open("/dev/null", O_RDONLY);
@@ -235,6 +239,8 @@
     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");
+
     return EXIT_SUCCESS;
 }
 
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 4cd423a..7f4a0dd 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -43,7 +43,9 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <chrono>
 #include <functional>
+#include <thread>
 #include <utility>
 #include <vector>
 
@@ -301,7 +303,7 @@
             announce = false;
             fprintf(stderr, "< waiting for %s >\n", serial ? serial : "any device");
         }
-        usleep(1000);
+        std::this_thread::sleep_for(std::chrono::milliseconds(1));
     }
 }
 
diff --git a/fastboot/usb_linux.cpp b/fastboot/usb_linux.cpp
index 6db1e27..cdab4f1 100644
--- a/fastboot/usb_linux.cpp
+++ b/fastboot/usb_linux.cpp
@@ -43,11 +43,15 @@
 #include <linux/version.h>
 #include <linux/usb/ch9.h>
 
+#include <chrono>
 #include <memory>
+#include <thread>
 
 #include "fastboot.h"
 #include "usb.h"
 
+using namespace std::chrono_literals;
+
 #define MAX_RETRIES 5
 
 /* Timeout in seconds for usb_wait_for_disconnect.
@@ -426,7 +430,7 @@
         return -1;
     }
 
-    while(len > 0) {
+    while (len > 0) {
         int xfer = (len > MAX_USBFS_BULK_SIZE) ? MAX_USBFS_BULK_SIZE : len;
 
         bulk.ep = handle_->ep_in;
@@ -435,18 +439,17 @@
         bulk.timeout = 0;
         retry = 0;
 
-        do{
-           DBG("[ usb read %d fd = %d], fname=%s\n", xfer, handle_->desc, handle_->fname);
-           n = ioctl(handle_->desc, USBDEVFS_BULK, &bulk);
-           DBG("[ usb read %d ] = %d, fname=%s, Retry %d \n", xfer, n, handle_->fname, retry);
+        do {
+            DBG("[ usb read %d fd = %d], fname=%s\n", xfer, handle_->desc, handle_->fname);
+            n = ioctl(handle_->desc, USBDEVFS_BULK, &bulk);
+            DBG("[ usb read %d ] = %d, fname=%s, Retry %d \n", xfer, n, handle_->fname, retry);
 
-           if( n < 0 ) {
-            DBG1("ERROR: n = %d, errno = %d (%s)\n",n, errno, strerror(errno));
-            if ( ++retry > MAX_RETRIES ) return -1;
-            sleep( 1 );
-           }
-        }
-        while( n < 0 );
+            if (n < 0) {
+                DBG1("ERROR: n = %d, errno = %d (%s)\n",n, errno, strerror(errno));
+                if (++retry > MAX_RETRIES) return -1;
+                std::this_thread::sleep_for(1s);
+            }
+        } while (n < 0);
 
         count += n;
         len -= n;
@@ -488,9 +491,8 @@
 {
   double deadline = now() + WAIT_FOR_DISCONNECT_TIMEOUT;
   while (now() < deadline) {
-    if (access(handle_->fname, F_OK))
-      return 0;
-    usleep(50000);
+    if (access(handle_->fname, F_OK)) return 0;
+    std::this_thread::sleep_for(50ms);
   }
   return -1;
 }
diff --git a/fastboot/usb_windows.cpp b/fastboot/usb_windows.cpp
index 1cdeb32..3dab5ac 100644
--- a/fastboot/usb_windows.cpp
+++ b/fastboot/usb_windows.cpp
@@ -362,9 +362,3 @@
     std::unique_ptr<usb_handle> handle = find_usb_device(callback);
     return handle ? new WindowsUsbTransport(std::move(handle)) : nullptr;
 }
-
-// called from fastboot.c
-void sleep(int seconds)
-{
-    Sleep(seconds * 1000);
-}
diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c
index d570255..b1511fe 100644
--- a/fs_mgr/fs_mgr.c
+++ b/fs_mgr/fs_mgr.c
@@ -241,7 +241,7 @@
     return ret;
 }
 
-static int fs_match(char *in1, char *in2)
+static int fs_match(const char *in1, const char *in2)
 {
     char *n1;
     char *n2;
@@ -651,7 +651,7 @@
  * If multiple fstab entries are to be mounted on "n_name", it will try to mount each one
  * in turn, and stop on 1st success, or no more match.
  */
-int fs_mgr_do_mount(struct fstab *fstab, char *n_name, char *n_blk_device,
+int fs_mgr_do_mount(struct fstab *fstab, const char *n_name, char *n_blk_device,
                     char *tmp_mount_point)
 {
     int i = 0;
diff --git a/fs_mgr/fs_mgr_main.c b/fs_mgr/fs_mgr_main.c
index 33a7496..4bfe202 100644
--- a/fs_mgr/fs_mgr_main.c
+++ b/fs_mgr/fs_mgr_main.c
@@ -14,12 +14,17 @@
  * limitations under the License.
  */
 
+#define _GNU_SOURCE
+
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
-#include <libgen.h>
 #include "fs_mgr_priv.h"
 
+#ifdef _LIBGEN_H
+#warning "libgen.h must not be included"
+#endif
+
 char *me = "";
 
 static void usage(void)
@@ -32,10 +37,10 @@
  * and exit the program, do not return to the caller.
  * Return the number of argv[] entries consumed.
  */
-static void parse_options(int argc, char *argv[], int *a_flag, int *u_flag, int *n_flag,
-                     char **n_name, char **n_blk_dev)
+static void parse_options(int argc, char * const argv[], int *a_flag, int *u_flag, int *n_flag,
+                     const char **n_name, const char **n_blk_dev)
 {
-    me = basename(strdup(argv[0]));
+    me = basename(argv[0]);
 
     if (argc <= 1) {
         usage();
@@ -75,14 +80,14 @@
     return;
 }
 
-int main(int argc, char *argv[])
+int main(int argc, char * const argv[])
 {
     int a_flag=0;
     int u_flag=0;
     int n_flag=0;
-    char *n_name=NULL;
-    char *n_blk_dev=NULL;
-    char *fstab_file=NULL;
+    const char *n_name=NULL;
+    const char *n_blk_dev=NULL;
+    const char *fstab_file=NULL;
     struct fstab *fstab=NULL;
 
     klog_set_level(6);
@@ -97,7 +102,7 @@
     if (a_flag) {
         return fs_mgr_mount_all(fstab, MOUNT_MODE_DEFAULT);
     } else if (n_flag) {
-        return fs_mgr_do_mount(fstab, n_name, n_blk_dev, 0);
+        return fs_mgr_do_mount(fstab, n_name, (char *)n_blk_dev, 0);
     } else if (u_flag) {
         return fs_mgr_unmount_all(fstab);
     } else {
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index c0116ef..b120f7c 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -95,7 +95,7 @@
 
 #define FS_MGR_DOMNT_FAILED (-1)
 #define FS_MGR_DOMNT_BUSY (-2)
-int fs_mgr_do_mount(struct fstab *fstab, char *n_name, char *n_blk_device,
+int fs_mgr_do_mount(struct fstab *fstab, const char *n_name, char *n_blk_device,
                     char *tmp_mount_point);
 int fs_mgr_do_tmpfs_mount(char *n_name);
 int fs_mgr_unmount_all(struct fstab *fstab);
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 612885b..857bcb2 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -257,13 +257,13 @@
 
 static int draw_text(const char *str, int x, int y)
 {
-    int str_len_px = gr_measure(str);
+    int str_len_px = gr_measure(gr_sys_font(), str);
 
     if (x < 0)
         x = (gr_fb_width() - str_len_px) / 2;
     if (y < 0)
         y = (gr_fb_height() - char_height) / 2;
-    gr_text(x, y, str, 0);
+    gr_text(gr_sys_font(), x, y, str, 0);
 
     return y + char_height;
 }
@@ -364,7 +364,7 @@
         }
 
         gr_init();
-        gr_font_size(&char_width, &char_height);
+        gr_font_size(gr_sys_font(), &char_width, &char_height);
 
 #ifndef CHARGER_DISABLE_INIT_BLANK
         gr_fb_blank(true);
diff --git a/include/cutils/files.h b/include/cutils/files.h
new file mode 100644
index 0000000..0210e30
--- /dev/null
+++ b/include/cutils/files.h
@@ -0,0 +1,37 @@
+/*
+ * 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 __CUTILS_FILES_H
+#define __CUTILS_FILES_H
+
+#define ANDROID_FILE_ENV_PREFIX "ANDROID_FILE_"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * android_get_control_file - simple helper function to get the file
+ * descriptor of our init-managed file. `path' is the filename path as
+ * given in init.rc. Returns -1 on error.
+ */
+int android_get_control_file(const char* path);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __CUTILS_FILES_H */
diff --git a/include/log/event_tag_map.h b/include/log/event_tag_map.h
index 69774ba..22e62ec 100644
--- a/include/log/event_tag_map.h
+++ b/include/log/event_tag_map.h
@@ -48,7 +48,15 @@
  * Look up a tag by index.  Returns the tag string & string length, or NULL if
  * not found.  Returned string is not guaranteed to be nul terminated.
  */
-const char* android_lookupEventTag_len(const EventTagMap* map, size_t* len, unsigned int tag);
+const char* android_lookupEventTag_len(const EventTagMap* map,
+                                       size_t* len, unsigned int tag);
+
+/*
+ * Look up a format by index. Returns the format string & string length,
+ * or NULL if not found. Returned string is not guaranteed to be nul terminated.
+ */
+const char* android_lookupEventFormat_len(const EventTagMap* map,
+                                          size_t* len, unsigned int tag);
 
 #ifdef __cplusplus
 }
diff --git a/include/log/logprint.h b/include/log/logprint.h
index 493f9f8..3509e7f 100644
--- a/include/log/logprint.h
+++ b/include/log/logprint.h
@@ -46,6 +46,7 @@
     FORMAT_MODIFIER_EPOCH,     /* Print time as seconds since Jan 1 1970 */
     FORMAT_MODIFIER_MONOTONIC, /* Print cpu time as seconds since start */
     FORMAT_MODIFIER_UID,       /* Adds uid */
+    FORMAT_MODIFIER_DESCRIPT,  /* Adds descriptive */
 } AndroidLogPrintFormat;
 
 typedef struct AndroidLogFormat_t AndroidLogFormat;
diff --git a/include/nativebridge/native_bridge.h b/include/nativebridge/native_bridge.h
index 26556f0..45266de 100644
--- a/include/nativebridge/native_bridge.h
+++ b/include/nativebridge/native_bridge.h
@@ -104,7 +104,7 @@
 
 // Get last error message of native bridge when fail to load library or search symbol.
 // This is reflection of dlerror() for native bridge.
-char* NativeBridgeGetError();
+const char* NativeBridgeGetError();
 
 struct native_bridge_namespace_t;
 
@@ -244,7 +244,7 @@
   // Returns:
   //   A string describing the most recent error that occurred when load library
   //   or lookup symbol via native bridge.
-  char* (*getError)();
+  const char* (*getError)();
 
   // Check whether library paths are supported by native bridge.
   //
diff --git a/include/ziparchive/zip_archive.h b/include/ziparchive/zip_archive.h
index fc845a4..54946fc 100644
--- a/include/ziparchive/zip_archive.h
+++ b/include/ziparchive/zip_archive.h
@@ -195,7 +195,8 @@
  * Uncompress and write an entry to an open file identified by |fd|.
  * |entry->uncompressed_length| bytes will be written to the file at
  * its current offset, and the file will be truncated at the end of
- * the uncompressed data.
+ * the uncompressed data (no truncation if |fd| references a block
+ * device).
  *
  * Returns 0 on success and negative values on failure.
  */
diff --git a/init/Android.mk b/init/Android.mk
index 1be064c..442a5f3 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -45,6 +45,8 @@
 LOCAL_CPPFLAGS := $(init_cflags)
 LOCAL_SRC_FILES:= \
     action.cpp \
+    capabilities.cpp \
+    descriptors.cpp \
     import_parser.cpp \
     init_parser.cpp \
     log.cpp \
@@ -53,6 +55,7 @@
     util.cpp \
 
 LOCAL_STATIC_LIBRARIES := libbase libselinux liblog libprocessgroup
+LOCAL_WHOLE_STATIC_LIBRARIES := libcap
 LOCAL_MODULE := libinit
 LOCAL_SANITIZE := integer
 LOCAL_CLANG := true
@@ -102,7 +105,7 @@
     libz \
     libprocessgroup
 
-# Create symlinks
+# Create symlinks.
 LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT)/sbin; \
     ln -sf ../init $(TARGET_ROOT_OUT)/sbin/ueventd; \
     ln -sf ../init $(TARGET_ROOT_OUT)/sbin/watchdogd
@@ -112,8 +115,8 @@
 include $(BUILD_EXECUTABLE)
 
 
-
-
+# Unit tests.
+# =========================================================
 include $(CLEAR_VARS)
 LOCAL_MODULE := init_tests
 LOCAL_SRC_FILES := \
@@ -123,8 +126,10 @@
 LOCAL_SHARED_LIBRARIES += \
     libcutils \
     libbase \
+    libselinux \
 
 LOCAL_STATIC_LIBRARIES := libinit
 LOCAL_SANITIZE := integer
 LOCAL_CLANG := true
+LOCAL_CPPFLAGS := -Wall -Wextra -Werror
 include $(BUILD_NATIVE_TEST)
diff --git a/init/action.cpp b/init/action.cpp
index ed88f6d..ccc18cf 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -247,7 +247,9 @@
         result += event_trigger_;
         result += ' ';
     }
-    result.pop_back();
+    if (!result.empty()) {
+        result.pop_back();
+    }
     return result;
 }
 
diff --git a/init/builtins.cpp b/init/builtins.cpp
index f37ccc2..ebdc8c9 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -38,6 +38,8 @@
 #include <linux/loop.h>
 #include <linux/module.h>
 
+#include <thread>
+
 #include <selinux/selinux.h>
 #include <selinux/label.h>
 
@@ -65,10 +67,9 @@
 #include "util.h"
 
 #define chmod DO_NOT_USE_CHMOD_USE_FCHMODAT_SYMLINK_NOFOLLOW
-#define UNMOUNT_CHECK_MS 5000
 #define UNMOUNT_CHECK_TIMES 10
 
-static const int kTerminateServiceDelayMicroSeconds = 50000;
+static constexpr std::chrono::nanoseconds kCommandRetryTimeout = 5s;
 
 static int insmod(const char *filename, const char *options, int flags) {
     int fd = open(filename, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
@@ -192,11 +193,9 @@
             close(fd);
             break;
         } else if (errno == EBUSY) {
-            /* Some processes using |entry->mnt_dir| are still alive. Wait for a
-             * while then retry.
-             */
-            TEMP_FAILURE_RETRY(
-                usleep(UNMOUNT_CHECK_MS * 1000 / UNMOUNT_CHECK_TIMES));
+            // Some processes using |entry->mnt_dir| are still alive. Wait for a
+            // while then retry.
+            std::this_thread::sleep_for(5000ms / UNMOUNT_CHECK_TIMES);
             continue;
         } else {
             /* Cannot open the device. Give up. */
@@ -444,7 +443,7 @@
         return -1;
     } else {
         if (wait)
-            wait_for_file(source, COMMAND_RETRY_TIMEOUT);
+            wait_for_file(source, kCommandRetryTimeout);
         if (mount(source, target, system, flags, options) < 0) {
             return -1;
         }
@@ -703,6 +702,15 @@
             callback_on_ro_remount = unmount_and_fsck;
         } else if (cmd == ANDROID_RB_RESTART2) {
             reboot_target = &command[len + 1];
+            // When rebooting to the bootloader notify the bootloader writing
+            // also the BCB.
+            if (strcmp(reboot_target, "bootloader") == 0) {
+                std::string err;
+                if (!write_reboot_bootloader(&err)) {
+                    LOG(ERROR) << "reboot-bootloader: Error writing "
+                                  "bootloader_message: " << err;
+                }
+            }
         }
     } else if (command[len] != '\0') {
         LOG(ERROR) << "powerctl: unrecognized reboot target '" << &command[len] << "'";
@@ -740,7 +748,7 @@
             }
 
             // Wait a bit before recounting the number or running services.
-            usleep(kTerminateServiceDelayMicroSeconds);
+            std::this_thread::sleep_for(50ms);
         }
         LOG(VERBOSE) << "Terminating running services took " << t.duration() << " seconds";
     }
@@ -947,11 +955,11 @@
 
 static int do_wait(const std::vector<std::string>& args) {
     if (args.size() == 2) {
-        return wait_for_file(args[1].c_str(), COMMAND_RETRY_TIMEOUT);
+        return wait_for_file(args[1].c_str(), kCommandRetryTimeout);
     } else if (args.size() == 3) {
         int timeout;
         if (android::base::ParseInt(args[2], &timeout)) {
-            return wait_for_file(args[1].c_str(), timeout);
+            return wait_for_file(args[1].c_str(), std::chrono::seconds(timeout));
         }
     }
     return -1;
diff --git a/init/capabilities.cpp b/init/capabilities.cpp
new file mode 100644
index 0000000..4592adc
--- /dev/null
+++ b/init/capabilities.cpp
@@ -0,0 +1,166 @@
+// 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 "capabilities.h"
+
+#include <sys/capability.h>
+#include <sys/prctl.h>
+
+#include <map>
+#include <memory>
+
+#include <android-base/logging.h>
+#include <android-base/macros.h>
+
+#define CAP_MAP_ENTRY(cap) { #cap, CAP_##cap }
+
+namespace {
+const std::map<std::string, int> cap_map = {
+    CAP_MAP_ENTRY(CHOWN),
+    CAP_MAP_ENTRY(DAC_OVERRIDE),
+    CAP_MAP_ENTRY(DAC_READ_SEARCH),
+    CAP_MAP_ENTRY(FOWNER),
+    CAP_MAP_ENTRY(FSETID),
+    CAP_MAP_ENTRY(KILL),
+    CAP_MAP_ENTRY(SETGID),
+    CAP_MAP_ENTRY(SETUID),
+    CAP_MAP_ENTRY(SETPCAP),
+    CAP_MAP_ENTRY(LINUX_IMMUTABLE),
+    CAP_MAP_ENTRY(NET_BIND_SERVICE),
+    CAP_MAP_ENTRY(NET_BROADCAST),
+    CAP_MAP_ENTRY(NET_ADMIN),
+    CAP_MAP_ENTRY(NET_RAW),
+    CAP_MAP_ENTRY(IPC_LOCK),
+    CAP_MAP_ENTRY(IPC_OWNER),
+    CAP_MAP_ENTRY(SYS_MODULE),
+    CAP_MAP_ENTRY(SYS_RAWIO),
+    CAP_MAP_ENTRY(SYS_CHROOT),
+    CAP_MAP_ENTRY(SYS_PTRACE),
+    CAP_MAP_ENTRY(SYS_PACCT),
+    CAP_MAP_ENTRY(SYS_ADMIN),
+    CAP_MAP_ENTRY(SYS_BOOT),
+    CAP_MAP_ENTRY(SYS_NICE),
+    CAP_MAP_ENTRY(SYS_RESOURCE),
+    CAP_MAP_ENTRY(SYS_TIME),
+    CAP_MAP_ENTRY(SYS_TTY_CONFIG),
+    CAP_MAP_ENTRY(MKNOD),
+    CAP_MAP_ENTRY(LEASE),
+    CAP_MAP_ENTRY(AUDIT_WRITE),
+    CAP_MAP_ENTRY(AUDIT_CONTROL),
+    CAP_MAP_ENTRY(SETFCAP),
+    CAP_MAP_ENTRY(MAC_OVERRIDE),
+    CAP_MAP_ENTRY(MAC_ADMIN),
+    CAP_MAP_ENTRY(SYSLOG),
+    CAP_MAP_ENTRY(WAKE_ALARM),
+    CAP_MAP_ENTRY(BLOCK_SUSPEND),
+    CAP_MAP_ENTRY(AUDIT_READ),
+};
+
+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)) {
+            // No need to drop this capability.
+            continue;
+        }
+        if (cap_drop_bound(cap) == -1) {
+            PLOG(ERROR) << "cap_drop_bound(" << cap << ") failed";
+            return false;
+        }
+    }
+    return true;
+}
+
+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) {
+        if (to_keep.test(cap)) {
+            value[0] = cap;
+            if (cap_set_flag(caps, CAP_INHERITABLE, arraysize(value), value, CAP_SET) != 0 ||
+                cap_set_flag(caps, CAP_PERMITTED, arraysize(value), value, CAP_SET) != 0) {
+                PLOG(ERROR) << "cap_set_flag(INHERITABLE|PERMITTED, " << cap << ") failed";
+                return false;
+            }
+        }
+    }
+
+    if (add_setpcap) {
+        value[0] = CAP_SETPCAP;
+        if (cap_set_flag(caps, CAP_PERMITTED, arraysize(value), value, CAP_SET) != 0 ||
+            cap_set_flag(caps, CAP_EFFECTIVE, arraysize(value), value, CAP_SET) != 0) {
+            PLOG(ERROR) << "cap_set_flag(PERMITTED|EFFECTIVE, " << CAP_SETPCAP << ") failed";
+            return false;
+        }
+    }
+
+    if (cap_set_proc(caps) != 0) {
+        PLOG(ERROR) << "cap_set_proc(" << to_keep.to_ulong() << ") failed";
+        return false;
+    }
+    return true;
+}
+
+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) {
+                PLOG(ERROR) << "prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, " << cap << ") failed";
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+}  // namespace anonymous
+
+int LookupCap(const std::string& cap_name) {
+    auto e = cap_map.find(cap_name);
+    if (e != cap_map.end()) {
+        return e->second;
+    } else {
+        return -1;
+    }
+}
+
+bool SetCapsForExec(const CapSet& to_keep) {
+    // Need to keep SETPCAP to drop bounding set below.
+    bool add_setpcap = true;
+    if (!SetProcCaps(to_keep, add_setpcap)) {
+        LOG(ERROR) << "failed to apply initial capset";
+        return false;
+    }
+
+    if (!DropBoundingSet(to_keep)) {
+        return false;
+    }
+
+    // If SETPCAP wasn't specifically requested, drop it now.
+    add_setpcap = false;
+    if (!SetProcCaps(to_keep, add_setpcap)) {
+        LOG(ERROR) << "failed to apply final capset";
+        return false;
+    }
+
+    // Add the capabilities to the ambient set so that they are preserved across
+    // execve(2).
+    // See http://man7.org/linux/man-pages/man7/capabilities.7.html.
+    return SetAmbientCaps(to_keep);
+}
diff --git a/init/capabilities.h b/init/capabilities.h
new file mode 100644
index 0000000..368178d
--- /dev/null
+++ b/init/capabilities.h
@@ -0,0 +1,23 @@
+// 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 <linux/capability.h>
+
+#include <bitset>
+#include <string>
+
+using CapSet = std::bitset<CAP_LAST_CAP + 1>;
+
+int LookupCap(const std::string& cap_name);
+bool SetCapsForExec(const CapSet& to_keep);
diff --git a/init/descriptors.cpp b/init/descriptors.cpp
new file mode 100644
index 0000000..10aae88
--- /dev/null
+++ b/init/descriptors.cpp
@@ -0,0 +1,104 @@
+/*
+ * 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 "descriptors.h"
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <android-base/stringprintf.h>
+#include <cutils/files.h>
+#include <cutils/sockets.h>
+
+#include "init.h"
+#include "log.h"
+#include "util.h"
+
+DescriptorInfo::DescriptorInfo(const std::string& name, const std::string& type, uid_t uid,
+                               gid_t gid, int perm, const std::string& context)
+        : name_(name), type_(type), uid_(uid), gid_(gid), perm_(perm), context_(context) {
+}
+
+DescriptorInfo::~DescriptorInfo() {
+}
+
+std::ostream& operator<<(std::ostream& os, const DescriptorInfo& info) {
+  return os << "  descriptors " << info.name_ << " " << info.type_ << " " << std::oct << info.perm_;
+}
+
+bool DescriptorInfo::operator==(const DescriptorInfo& other) const {
+  return name_ == other.name_ && type_ == other.type_ && key() == other.key();
+}
+
+void DescriptorInfo::CreateAndPublish(const std::string& globalContext) const {
+  // Create
+  const std::string& contextStr = context_.empty() ? globalContext : context_;
+  int fd = Create(contextStr);
+  if (fd < 0) return;
+
+  // Publish
+  std::string publishedName = key() + name_;
+  std::for_each(publishedName.begin(), publishedName.end(),
+                [] (char& c) { c = isalnum(c) ? c : '_'; });
+
+  std::string val = android::base::StringPrintf("%d", fd);
+  add_environment(publishedName.c_str(), val.c_str());
+
+  // make sure we don't close on exec
+  fcntl(fd, F_SETFD, 0);
+}
+
+void DescriptorInfo::Clean() const {
+}
+
+SocketInfo::SocketInfo(const std::string& name, const std::string& type, uid_t uid,
+                       gid_t gid, int perm, const std::string& context)
+        : DescriptorInfo(name, type, uid, gid, perm, context) {
+}
+
+void SocketInfo::Clean() const {
+  unlink(android::base::StringPrintf(ANDROID_SOCKET_DIR "/%s", name().c_str()).c_str());
+}
+
+int SocketInfo::Create(const std::string& context) const {
+  int flags = ((type() == "stream" ? SOCK_STREAM :
+                (type() == "dgram" ? SOCK_DGRAM :
+                 SOCK_SEQPACKET)));
+  return create_socket(name().c_str(), flags, perm(), uid(), gid(), context.c_str());
+}
+
+const std::string SocketInfo::key() const {
+  return ANDROID_SOCKET_ENV_PREFIX;
+}
+
+FileInfo::FileInfo(const std::string& name, const std::string& type, uid_t uid,
+                   gid_t gid, int perm, const std::string& context)
+        : DescriptorInfo(name, type, uid, gid, perm, context) {
+}
+
+int FileInfo::Create(const std::string& context) const {
+  int flags = ((type() == "r" ? O_RDONLY :
+                (type() == "w" ? (O_WRONLY | O_CREAT) :
+                 (O_RDWR | O_CREAT))));
+  return create_file(name().c_str(), flags, perm(), uid(), gid(), context.c_str());
+}
+
+const std::string FileInfo::key() const {
+  return ANDROID_FILE_ENV_PREFIX;
+}
diff --git a/init/descriptors.h b/init/descriptors.h
new file mode 100644
index 0000000..ff276fb
--- /dev/null
+++ b/init/descriptors.h
@@ -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.
+ */
+
+
+#ifndef _INIT_DESCRIPTORS_H
+#define _INIT_DESCRIPTORS_H
+
+#include <sys/types.h>
+
+#include <string>
+
+class DescriptorInfo {
+ public:
+  DescriptorInfo(const std::string& name, const std::string& type, uid_t uid,
+                 gid_t gid, int perm, const std::string& context);
+  virtual ~DescriptorInfo();
+
+  friend std::ostream& operator<<(std::ostream& os, const class DescriptorInfo& info);
+  bool operator==(const DescriptorInfo& other) const;
+
+  void CreateAndPublish(const std::string& globalContext) const;
+  virtual void Clean() const;
+
+ protected:
+  const std::string& name() const { return name_; }
+  const std::string& type() const { return type_; }
+  uid_t uid() const { return uid_; }
+  gid_t gid() const { return gid_; }
+  int perm() const { return perm_; }
+  const std::string& context() const { return context_; }
+
+ private:
+  std::string name_;
+  std::string type_;
+  uid_t uid_;
+  gid_t gid_;
+  int perm_;
+  std::string context_;
+
+  virtual int Create(const std::string& globalContext) const = 0;
+  virtual const std::string key() const = 0;
+};
+
+std::ostream& operator<<(std::ostream& os, const DescriptorInfo& info);
+
+class SocketInfo : public DescriptorInfo {
+ public:
+  SocketInfo(const std::string& name, const std::string& type, uid_t uid,
+             gid_t gid, int perm, const std::string& context);
+  void Clean() const override;
+ private:
+  virtual int Create(const std::string& context) const override;
+  virtual const std::string key() const override;
+};
+
+class FileInfo : public DescriptorInfo {
+ public:
+  FileInfo(const std::string& name, const std::string& type, uid_t uid,
+           gid_t gid, int perm, const std::string& context);
+ private:
+  virtual int Create(const std::string& context) const override;
+  virtual const std::string key() const override;
+};
+
+#endif
diff --git a/init/devices.cpp b/init/devices.cpp
index 1a6912f..2452c1d 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -23,6 +23,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <sys/sendfile.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/time.h>
@@ -34,6 +35,7 @@
 #include <linux/netlink.h>
 
 #include <memory>
+#include <thread>
 
 #include <selinux/selinux.h>
 #include <selinux/label.h>
@@ -109,13 +111,18 @@
         return -ENOMEM;
 
     node->dp.name = strdup(name);
-    if (!node->dp.name)
+    if (!node->dp.name) {
+        free(node);
         return -ENOMEM;
+    }
 
     if (attr) {
         node->dp.attr = strdup(attr);
-        if (!node->dp.attr)
+        if (!node->dp.attr) {
+            free(node->dp.name);
+            free(node);
             return -ENOMEM;
+        }
     }
 
     node->dp.perm = perm;
@@ -784,139 +791,87 @@
     }
 }
 
-static int load_firmware(int fw_fd, int loading_fd, int data_fd)
-{
-    struct stat st;
-    long len_to_copy;
-    int ret = 0;
+static void load_firmware(uevent* uevent, const std::string& root,
+                          int fw_fd, size_t fw_size,
+                          int loading_fd, int data_fd) {
+    // Start transfer.
+    android::base::WriteFully(loading_fd, "1", 1);
 
-    if(fstat(fw_fd, &st) < 0)
-        return -1;
-    len_to_copy = st.st_size;
-
-    write(loading_fd, "1", 1);  /* start transfer */
-
-    while (len_to_copy > 0) {
-        char buf[PAGE_SIZE];
-        ssize_t nr;
-
-        nr = read(fw_fd, buf, sizeof(buf));
-        if(!nr)
-            break;
-        if(nr < 0) {
-            ret = -1;
-            break;
-        }
-        if (!android::base::WriteFully(data_fd, buf, nr)) {
-            ret = -1;
-            break;
-        }
-        len_to_copy -= nr;
+    // Copy the firmware.
+    int rc = sendfile(data_fd, fw_fd, nullptr, fw_size);
+    if (rc == -1) {
+        PLOG(ERROR) << "firmware: sendfile failed { '" << root << "', '" << uevent->firmware << "' }";
     }
 
-    if(!ret)
-        write(loading_fd, "0", 1);  /* successful end of transfer */
-    else
-        write(loading_fd, "-1", 2); /* abort transfer */
-
-    return ret;
+    // Tell the firmware whether to abort or commit.
+    const char* response = (rc != -1) ? "0" : "-1";
+    android::base::WriteFully(loading_fd, response, strlen(response));
 }
 
-static int is_booting(void)
-{
+static int is_booting() {
     return access("/dev/.booting", F_OK) == 0;
 }
 
-static void process_firmware_event(struct uevent *uevent)
-{
-    char *root, *loading, *data;
-    int l, loading_fd, data_fd, fw_fd;
-    size_t i;
+static void process_firmware_event(uevent* uevent) {
     int booting = is_booting();
 
     LOG(INFO) << "firmware: loading '" << uevent->firmware << "' for '" << uevent->path << "'";
 
-    l = asprintf(&root, SYSFS_PREFIX"%s/", uevent->path);
-    if (l == -1)
+    std::string root = android::base::StringPrintf("/sys%s", uevent->path);
+    std::string loading = root + "/loading";
+    std::string data = root + "/data";
+
+    android::base::unique_fd loading_fd(open(loading.c_str(), O_WRONLY|O_CLOEXEC));
+    if (loading_fd == -1) {
+        PLOG(ERROR) << "couldn't open firmware loading fd for " << uevent->firmware;
         return;
+    }
 
-    l = asprintf(&loading, "%sloading", root);
-    if (l == -1)
-        goto root_free_out;
-
-    l = asprintf(&data, "%sdata", root);
-    if (l == -1)
-        goto loading_free_out;
-
-    loading_fd = open(loading, O_WRONLY|O_CLOEXEC);
-    if(loading_fd < 0)
-        goto data_free_out;
-
-    data_fd = open(data, O_WRONLY|O_CLOEXEC);
-    if(data_fd < 0)
-        goto loading_close_out;
+    android::base::unique_fd data_fd(open(data.c_str(), O_WRONLY|O_CLOEXEC));
+    if (data_fd == -1) {
+        PLOG(ERROR) << "couldn't open firmware data fd for " << uevent->firmware;
+        return;
+    }
 
 try_loading_again:
-    for (i = 0; i < arraysize(firmware_dirs); i++) {
-        char *file = NULL;
-        l = asprintf(&file, "%s/%s", firmware_dirs[i], uevent->firmware);
-        if (l == -1)
-            goto data_free_out;
-        fw_fd = open(file, O_RDONLY|O_CLOEXEC);
-        free(file);
-        if (fw_fd >= 0) {
-            if (!load_firmware(fw_fd, loading_fd, data_fd)) {
-                LOG(INFO) << "firmware: copy success { '" << root << "', '" << uevent->firmware << "' }";
-            } else {
-                LOG(ERROR) << "firmware: copy failure { '" << root << "', '" << uevent->firmware << "' }";
-            }
-            break;
+    for (size_t i = 0; i < arraysize(firmware_dirs); i++) {
+        std::string file = android::base::StringPrintf("%s/%s", firmware_dirs[i], uevent->firmware);
+        android::base::unique_fd fw_fd(open(file.c_str(), O_RDONLY|O_CLOEXEC));
+        struct stat sb;
+        if (fw_fd != -1 && fstat(fw_fd, &sb) != -1) {
+            load_firmware(uevent, root, fw_fd, sb.st_size, loading_fd, data_fd);
+            return;
         }
     }
-    if (fw_fd < 0) {
-        if (booting) {
-            /* If we're not fully booted, we may be missing
-             * filesystems needed for firmware, wait and retry.
-             */
-            usleep(100000);
-            booting = is_booting();
-            goto try_loading_again;
-        }
-        PLOG(ERROR) << "firmware: could not open '" << uevent->firmware << "'";
-        write(loading_fd, "-1", 2);
-        goto data_close_out;
-    }
 
-    close(fw_fd);
-data_close_out:
-    close(data_fd);
-loading_close_out:
-    close(loading_fd);
-data_free_out:
-    free(data);
-loading_free_out:
-    free(loading);
-root_free_out:
-    free(root);
+    if (booting) {
+        // If we're not fully booted, we may be missing
+        // filesystems needed for firmware, wait and retry.
+        std::this_thread::sleep_for(100ms);
+        booting = is_booting();
+        goto try_loading_again;
+    }
+
+    LOG(ERROR) << "firmware: could not find firmware for " << uevent->firmware;
+
+    // Write "-1" as our response to the kernel's firmware request, since we have nothing for it.
+    write(loading_fd, "-1", 2);
 }
 
-static void handle_firmware_event(struct uevent *uevent)
-{
-    pid_t pid;
+static void handle_firmware_event(uevent* uevent) {
+    if (strcmp(uevent->subsystem, "firmware")) return;
+    if (strcmp(uevent->action, "add")) return;
 
-    if(strcmp(uevent->subsystem, "firmware"))
-        return;
-
-    if(strcmp(uevent->action, "add"))
-        return;
-
-    /* we fork, to avoid making large memory allocations in init proper */
-    pid = fork();
-    if (!pid) {
+    // Loading the firmware in a child means we can do that in parallel...
+    // (We ignore SIGCHLD rather than wait for our children.)
+    pid_t pid = fork();
+    if (pid == 0) {
+        Timer t;
         process_firmware_event(uevent);
+        LOG(INFO) << "loading " << uevent->path << " took " << t.duration() << "s";
         _exit(EXIT_SUCCESS);
-    } else if (pid < 0) {
-        PLOG(ERROR) << "could not fork to process firmware event";
+    } else if (pid == -1) {
+        PLOG(ERROR) << "could not fork to process firmware event for " << uevent->firmware;
     }
 }
 
@@ -1091,7 +1046,6 @@
     LOG(INFO) << "Coldboot took " << t.duration() << "s.";
 }
 
-int get_device_fd()
-{
+int get_device_fd() {
     return device_fd;
 }
diff --git a/init/init.cpp b/init/init.cpp
index 957527b..cbd46bf 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -18,6 +18,7 @@
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <inttypes.h>
 #include <libgen.h>
 #include <paths.h>
 #include <signal.h>
@@ -67,6 +68,8 @@
 #include "util.h"
 #include "watchdogd.h"
 
+using android::base::StringPrintf;
+
 struct selabel_handle *sehandle;
 struct selabel_handle *sehandle_prop;
 
@@ -75,7 +78,7 @@
 static char qemu[32];
 
 std::string default_console = "/dev/console";
-static time_t process_needs_restart;
+static time_t process_needs_restart_at;
 
 const char *ENV[32];
 
@@ -132,11 +135,10 @@
 
 static void restart_processes()
 {
-    process_needs_restart = 0;
-    ServiceManager::GetInstance().
-        ForEachServiceWithFlags(SVC_RESTARTING, [] (Service* s) {
-                s->RestartIfNeeded(process_needs_restart);
-            });
+    process_needs_restart_at = 0;
+    ServiceManager::GetInstance().ForEachServiceWithFlags(SVC_RESTARTING, [](Service* s) {
+        s->RestartIfNeeded(&process_needs_restart_at);
+    });
 }
 
 void handle_control_message(const std::string& msg, const std::string& name) {
@@ -164,7 +166,7 @@
     // Any longer than 1s is an unreasonable length of time to delay booting.
     // If you're hitting this timeout, check that you didn't make your
     // sepolicy regular expressions too expensive (http://b/19899875).
-    if (wait_for_file(COLDBOOT_DONE, 1)) {
+    if (wait_for_file(COLDBOOT_DONE, 1s)) {
         LOG(ERROR) << "Timed out waiting for " COLDBOOT_DONE;
     }
 
@@ -268,15 +270,14 @@
 
     if (for_emulator) {
         // In the emulator, export any kernel option with the "ro.kernel." prefix.
-        property_set(android::base::StringPrintf("ro.kernel.%s", key.c_str()).c_str(), value.c_str());
+        property_set(StringPrintf("ro.kernel.%s", key.c_str()).c_str(), value.c_str());
         return;
     }
 
     if (key == "qemu") {
         strlcpy(qemu, value.c_str(), sizeof(qemu));
     } else if (android::base::StartsWith(key, "androidboot.")) {
-        property_set(android::base::StringPrintf("ro.boot.%s", key.c_str() + 12).c_str(),
-                     value.c_str());
+        property_set(StringPrintf("ro.boot.%s", key.c_str() + 12).c_str(), value.c_str());
     }
 }
 
@@ -314,7 +315,7 @@
 static void process_kernel_dt() {
     static const char android_dir[] = "/proc/device-tree/firmware/android";
 
-    std::string file_name = android::base::StringPrintf("%s/compatible", android_dir);
+    std::string file_name = StringPrintf("%s/compatible", android_dir);
 
     std::string dt_file;
     android::base::ReadFileToString(file_name, &dt_file);
@@ -332,12 +333,12 @@
             continue;
         }
 
-        file_name = android::base::StringPrintf("%s/%s", android_dir, dp->d_name);
+        file_name = StringPrintf("%s/%s", android_dir, dp->d_name);
 
         android::base::ReadFileToString(file_name, &dt_file);
         std::replace(dt_file.begin(), dt_file.end(), ',', '.');
 
-        std::string property_name = android::base::StringPrintf("ro.boot.%s", dp->d_name);
+        std::string property_name = StringPrintf("ro.boot.%s", dp->d_name);
         property_set(property_name.c_str(), dt_file.c_str());
     }
 }
@@ -566,12 +567,14 @@
         return watchdogd_main(argc, argv);
     }
 
+    boot_clock::time_point start_time = boot_clock::now();
+
     // Clear the umask.
     umask(0);
 
     add_environment("PATH", _PATH_DEFPATH);
 
-    bool is_first_stage = (argc == 1) || (strcmp(argv[1], "--second-stage") != 0);
+    bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);
 
     // Don't expose the raw commandline to unprivileged processes.
     chmod("/proc/cmdline", 0440);
@@ -585,19 +588,45 @@
         mount("devpts", "/dev/pts", "devpts", 0, NULL);
         #define MAKE_STR(x) __STRING(x)
         mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
+        gid_t groups[] = { AID_READPROC };
+        setgroups(arraysize(groups), groups);
         mount("sysfs", "/sys", "sysfs", 0, NULL);
         mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
         mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));
-        early_mount();
     }
 
     // Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually
     // talk to the outside world...
     InitKernelLogging(argv);
 
-    LOG(INFO) << "init " << (is_first_stage ? "first stage" : "second stage") << " started!";
+    LOG(INFO) << "init " << (is_first_stage ? "first" : "second") << " stage started!";
 
-    if (!is_first_stage) {
+    if (is_first_stage) {
+        // Mount devices defined in android.early.* kernel commandline
+        early_mount();
+
+        // Set up SELinux, loading the SELinux policy.
+        selinux_initialize(true);
+
+        // We're in the kernel domain, so re-exec init to transition to the init domain now
+        // that the SELinux policy has been loaded.
+        if (restorecon("/init") == -1) {
+            PLOG(ERROR) << "restorecon failed";
+            security_failure();
+        }
+
+        setenv("INIT_SECOND_STAGE", "true", 1);
+
+        uint64_t start_ns = start_time.time_since_epoch().count();
+        setenv("INIT_STARTED_AT", StringPrintf("%" PRIu64, start_ns).c_str(), 1);
+
+        char* path = argv[0];
+        char* args[] = { path, nullptr };
+        if (execv(path, args) == -1) {
+            PLOG(ERROR) << "execv(\"" << path << "\") failed";
+            security_failure();
+        }
+    } else {
         // Indicate that booting is in progress to background fw loaders, etc.
         close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
 
@@ -611,24 +640,12 @@
         // Propagate the kernel variables to internal variables
         // used by init as well as the current required properties.
         export_kernel_boot_props();
-    }
 
-    // Set up SELinux, including loading the SELinux policy if we're in the kernel domain.
-    selinux_initialize(is_first_stage);
+        // Make the time that init started available for bootstat to log.
+        property_set("init.start", getenv("INIT_STARTED_AT"));
 
-    // If we're in the kernel domain, re-exec init to transition to the init domain now
-    // that the SELinux policy has been loaded.
-    if (is_first_stage) {
-        if (restorecon("/init") == -1) {
-            PLOG(ERROR) << "restorecon failed";
-            security_failure();
-        }
-        char* path = argv[0];
-        char* args[] = { path, const_cast<char*>("--second-stage"), nullptr };
-        if (execv(path, args) == -1) {
-            PLOG(ERROR) << "execv(\"" << path << "\") failed";
-            security_failure();
-        }
+        // Now set up SELinux for second stage.
+        selinux_initialize(false);
     }
 
     // These directories were necessarily created before initial policy load
@@ -701,21 +718,22 @@
             restart_processes();
         }
 
-        int timeout = -1;
-        if (process_needs_restart) {
-            timeout = (process_needs_restart - gettime()) * 1000;
-            if (timeout < 0)
-                timeout = 0;
+        // By default, sleep until something happens.
+        int epoll_timeout_ms = -1;
+
+        // If there's more work to do, wake up again immediately.
+        if (am.HasMoreCommands()) epoll_timeout_ms = 0;
+
+        // If there's a process that needs restarting, wake up in time for that.
+        if (process_needs_restart_at != 0) {
+            epoll_timeout_ms = (process_needs_restart_at - time(nullptr)) * 1000;
+            if (epoll_timeout_ms < 0) epoll_timeout_ms = 0;
         }
 
-        if (am.HasMoreCommands()) {
-            timeout = 0;
-        }
-
-        bootchart_sample(&timeout);
+        bootchart_sample(&epoll_timeout_ms);
 
         epoll_event ev;
-        int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));
+        int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, epoll_timeout_ms));
         if (nr == -1) {
             PLOG(ERROR) << "epoll_wait failed";
         } else if (nr == 1) {
diff --git a/init/init.h b/init/init.h
index 0019337..cfb3139 100644
--- a/init/init.h
+++ b/init/init.h
@@ -22,8 +22,6 @@
 class Action;
 class Service;
 
-#define COMMAND_RETRY_TIMEOUT 5
-
 extern const char *ENV[32];
 extern bool waiting_for_exec;
 extern std::string default_console;
diff --git a/init/readme.txt b/init/readme.txt
index 26225b2..7e9d21b 100644
--- a/init/readme.txt
+++ b/init/readme.txt
@@ -141,33 +141,49 @@
   Set the environment variable <name> to <value> in the launched process.
 
 socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]
-  Create a unix domain socket named /dev/socket/<name> and pass
-  its fd to the launched process.  <type> must be "dgram", "stream" or "seqpacket".
-  User and group default to 0.
-  'seclabel' is the SELinux security context for the socket.
-  It defaults to the service security context, as specified by seclabel or
-  computed based on the service executable file security context.
+  Create a unix domain socket named /dev/socket/<name> and pass its fd to the
+  launched process.  <type> must be "dgram", "stream" or "seqpacket".  User and
+  group default to 0.  'seclabel' is the SELinux security context for the
+  socket.  It defaults to the service security context, as specified by
+  seclabel or computed based on the service executable file security context.
+  For native executables see libcutils android_get_control_socket().
+
+file <path> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]
+  Open/Create a file path and pass its fd to the launched process.  <type> must
+  be "r", "w" or "rw".  User and group default to 0.  'seclabel' is the SELinux
+  security context for the file if it must be created.  It defaults to the
+  service security context, as specified by seclabel or computed based on the
+  service executable file security context.  For native executables see
+  libcutils android_get_control_file().
 
 user <username>
-  Change to username before exec'ing this service.
+  Change to 'username' before exec'ing this service.
   Currently defaults to root.  (??? probably should default to nobody)
   As of Android M, processes should use this option even if they
-  require linux capabilities.  Previously, to acquire linux
+  require Linux capabilities.  Previously, to acquire Linux
   capabilities, a process would need to run as root, request the
   capabilities, then drop to its desired uid.  There is a new
   mechanism through fs_config that allows device manufacturers to add
-  linux capabilities to specific binaries on a file system that should
+  Linux capabilities to specific binaries on a file system that should
   be used instead. This mechanism is described on
   http://source.android.com/devices/tech/config/filesystem.html.  When
   using this new mechanism, processes can use the user option to
   select their desired uid without ever running as root.
+  As of Android O, processes can also request capabilities directly in their .rc
+  files. See the "capabilities" option below.
 
 group <groupname> [ <groupname> ]*
-  Change to groupname before exec'ing this service.  Additional
+  Change to 'groupname' before exec'ing this service.  Additional
   groupnames beyond the (required) first one are used to set the
   supplemental groups of the process (via setgroups()).
   Currently defaults to root.  (??? probably should default to nobody)
 
+capabilities <capability> [ <capability> ]*
+  Set capabilities when exec'ing this service. 'capability' should be a Linux
+  capability without the "CAP_" prefix, like "NET_ADMIN" or "SETPCAP". See
+  http://man7.org/linux/man-pages/man7/capabilities.7.html for a list of Linux
+  capabilities.
+
 seclabel <seclabel>
   Change to 'seclabel' before exec'ing this service.
   Primarily for use by services run from the rootfs, e.g. ueventd, adbd.
@@ -424,8 +440,16 @@
 Init provides information about the services that it is responsible
 for via the below properties.
 
+init.start
+  Time after boot in ns (via the CLOCK_BOOTTIME clock) at which the first
+  stage of init started.
+
 init.svc.<name>
-   State of a named service ("stopped", "stopping", "running", "restarting")
+  State of a named service ("stopped", "stopping", "running", "restarting")
+
+init.svc.<name>.start
+  Time after boot in ns (via the CLOCK_BOOTTIME clock) that the service was
+  most recently started.
 
 
 Bootcharting
@@ -521,10 +545,10 @@
 
 For quicker turnaround when working on init itself, use:
 
-  mm -j
-  m ramdisk-nodeps
-  m bootimage-nodeps
-  adb reboot bootloader
+  mm -j &&
+  m ramdisk-nodeps &&
+  m bootimage-nodeps &&
+  adb reboot bootloader &&
   fastboot boot $ANDROID_PRODUCT_OUT/boot.img
 
 Alternatively, use the emulator:
diff --git a/init/service.cpp b/init/service.cpp
index 6460e71..1f53a1b 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -17,6 +17,8 @@
 #include "service.h"
 
 #include <fcntl.h>
+#include <inttypes.h>
+#include <linux/securebits.h>
 #include <sched.h>
 #include <sys/mount.h>
 #include <sys/prctl.h>
@@ -35,7 +37,6 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <cutils/android_reboot.h>
-#include <cutils/sockets.h>
 #include <system/thread_defs.h>
 
 #include <processgroup/processgroup.h>
@@ -51,9 +52,6 @@
 using android::base::StringPrintf;
 using android::base::WriteStringToFile;
 
-#define CRITICAL_CRASH_THRESHOLD    4       // if we crash >4 times ...
-#define CRITICAL_CRASH_WINDOW       (4*60)  // ... in 4 minutes, goto recovery
-
 static std::string ComputeContextFromExecutable(std::string& service_name,
                                                 const std::string& service_path) {
     std::string computed_context;
@@ -144,14 +142,6 @@
     strs->push_back(nullptr);
 }
 
-SocketInfo::SocketInfo() : uid(0), gid(0), perm(0) {
-}
-
-SocketInfo::SocketInfo(const std::string& name, const std::string& type, uid_t uid,
-                       gid_t gid, int perm, const std::string& socketcon)
-    : name(name), type(type), uid(uid), gid(gid), perm(perm), socketcon(socketcon) {
-}
-
 ServiceEnvironmentInfo::ServiceEnvironmentInfo() {
 }
 
@@ -162,8 +152,8 @@
 
 Service::Service(const std::string& name, const std::string& classname,
                  const std::vector<std::string>& args)
-    : name_(name), classname_(classname), flags_(0), pid_(0), time_started_(0),
-      time_crashed_(0), nr_crashed_(0), uid_(0), gid_(0), namespace_flags_(0),
+    : name_(name), classname_(classname), flags_(0), pid_(0),
+      crash_count_(0), uid_(0), gid_(0), namespace_flags_(0),
       seclabel_(""), ioprio_class_(IoSchedClass_NONE), ioprio_pri_(0),
       priority_(0), oom_score_adjust_(-1000), args_(args) {
     onrestart_.InitSingleTrigger("onrestart");
@@ -171,14 +161,16 @@
 
 Service::Service(const std::string& name, const std::string& classname,
                  unsigned flags, uid_t uid, gid_t gid,
-                 const std::vector<gid_t>& supp_gids, unsigned namespace_flags,
+                 const std::vector<gid_t>& supp_gids,
+                 const CapSet& capabilities, unsigned namespace_flags,
                  const std::string& seclabel,
                  const std::vector<std::string>& args)
     : name_(name), classname_(classname), flags_(flags), pid_(0),
-      time_started_(0), time_crashed_(0), nr_crashed_(0), uid_(uid), gid_(gid),
-      supp_gids_(supp_gids), namespace_flags_(namespace_flags),
-      seclabel_(seclabel), ioprio_class_(IoSchedClass_NONE), ioprio_pri_(0),
-      priority_(0), oom_score_adjust_(-1000), args_(args) {
+      crash_count_(0), uid_(uid), gid_(gid),
+      supp_gids_(supp_gids), capabilities_(capabilities),
+      namespace_flags_(namespace_flags), seclabel_(seclabel),
+      ioprio_class_(IoSchedClass_NONE), ioprio_pri_(0), priority_(0),
+      oom_score_adjust_(-1000), args_(args) {
     onrestart_.InitSingleTrigger("onrestart");
 }
 
@@ -196,6 +188,12 @@
     }
 
     property_set(prop_name.c_str(), new_state.c_str());
+
+    if (new_state == "running") {
+        prop_name += ".start";
+        uint64_t start_ns = time_started_.time_since_epoch().count();
+        property_set(prop_name.c_str(), StringPrintf("%" PRIu64, start_ns).c_str());
+    }
 }
 
 void Service::KillProcessGroup(int signal) {
@@ -210,21 +208,14 @@
     }
 }
 
-void Service::CreateSockets(const std::string& context) {
-    for (const auto& si : sockets_) {
-        int socket_type = ((si.type == "stream" ? SOCK_STREAM :
-                            (si.type == "dgram" ? SOCK_DGRAM :
-                             SOCK_SEQPACKET)));
-        const char* socketcon = !si.socketcon.empty() ? si.socketcon.c_str() : context.c_str();
-
-        int s = create_socket(si.name.c_str(), socket_type, si.perm, si.uid, si.gid, socketcon);
-        if (s >= 0) {
-            PublishSocket(si.name, s);
+void Service::SetProcessAttributes() {
+    // Keep capabilites on uid change.
+    if (capabilities_.any() && uid_) {
+        if (prctl(PR_SET_SECUREBITS, SECBIT_KEEP_CAPS | SECBIT_KEEP_CAPS_LOCKED) != 0) {
+            PLOG(FATAL) << "prtcl(PR_SET_KEEPCAPS) failed for " << name_;
         }
     }
-}
 
-void Service::SetProcessAttributes() {
     // TODO: work out why this fails for `console` then upgrade to FATAL.
     if (setpgid(0, getpid()) == -1) PLOG(ERROR) << "setpgid failed for " << name_;
 
@@ -233,10 +224,8 @@
             PLOG(FATAL) << "setgid failed for " << name_;
         }
     }
-    if (!supp_gids_.empty()) {
-        if (setgroups(supp_gids_.size(), &supp_gids_[0]) != 0) {
-            PLOG(FATAL) << "setgroups failed for " << name_;
-        }
+    if (setgroups(supp_gids_.size(), &supp_gids_[0]) != 0) {
+        PLOG(FATAL) << "setgroups failed for " << name_;
     }
     if (uid_) {
         if (setuid(uid_) != 0) {
@@ -253,6 +242,11 @@
             PLOG(FATAL) << "setpriority failed for " << name_;
         }
     }
+    if (capabilities_.any()) {
+        if (!SetCapsForExec(capabilities_)) {
+            LOG(FATAL) << "cannot set capabilities for " << name_;
+        }
+    }
 }
 
 bool Service::Reap() {
@@ -260,11 +254,9 @@
         KillProcessGroup(SIGKILL);
     }
 
-    // Remove any sockets we may have created.
-    for (const auto& si : sockets_) {
-        std::string tmp = StringPrintf(ANDROID_SOCKET_DIR "/%s", si.name.c_str());
-        unlink(tmp.c_str());
-    }
+    // Remove any descriptor resources we may have created.
+    std::for_each(descriptors_.begin(), descriptors_.end(),
+                  std::bind(&DescriptorInfo::Clean, std::placeholders::_1));
 
     if (flags_ & SVC_EXEC) {
         LOG(INFO) << "SVC_EXEC pid " << pid_ << " finished...";
@@ -286,20 +278,19 @@
         return false;
     }
 
-    time_t now = gettime();
+    // If we crash > 4 times in 4 minutes, reboot into recovery.
+    boot_clock::time_point now = boot_clock::now();
     if ((flags_ & SVC_CRITICAL) && !(flags_ & SVC_RESTART)) {
-        if (time_crashed_ + CRITICAL_CRASH_WINDOW >= now) {
-            if (++nr_crashed_ > CRITICAL_CRASH_THRESHOLD) {
-                LOG(ERROR) << "critical process '" << name_ << "' exited "
-                           << CRITICAL_CRASH_THRESHOLD << " times in "
-                           << (CRITICAL_CRASH_WINDOW / 60) << " minutes; "
+        if (now < time_crashed_ + 4min) {
+            if (++crash_count_ > 4) {
+                LOG(ERROR) << "critical process '" << name_ << "' exited 4 times in 4 minutes; "
                            << "rebooting into recovery mode";
                 android_reboot(ANDROID_RB_RESTART2, 0, "recovery");
                 return false;
             }
         } else {
             time_crashed_ = now;
-            nr_crashed_ = 1;
+            crash_count_ = 1;
         }
     }
 
@@ -317,9 +308,23 @@
     LOG(INFO) << "service " << name_;
     LOG(INFO) << "  class '" << classname_ << "'";
     LOG(INFO) << "  exec "<< android::base::Join(args_, " ");
-    for (const auto& si : sockets_) {
-        LOG(INFO) << "  socket " << si.name << " " << si.type << " " << std::oct << si.perm;
+    std::for_each(descriptors_.begin(), descriptors_.end(),
+                  [] (const auto& info) { LOG(INFO) << *info; });
+}
+
+bool Service::ParseCapabilities(const std::vector<std::string>& args, std::string* err) {
+    capabilities_ = 0;
+
+    for (size_t i = 1; i < args.size(); i++) {
+        const std::string& arg = args[i];
+        int cap = LookupCap(arg);
+        if (cap == -1) {
+            *err = StringPrintf("invalid capability '%s'", arg.c_str());
+            return false;
+        }
+        capabilities_[cap] = true;
     }
+    return true;
 }
 
 bool Service::ParseClass(const std::vector<std::string>& args, std::string* err) {
@@ -355,8 +360,8 @@
 bool Service::ParsePriority(const std::vector<std::string>& args, std::string* err) {
     priority_ = 0;
     if (!ParseInt(args[1], &priority_,
-                  static_cast<int>(ANDROID_PRIORITY_LOWEST),
-                  static_cast<int>(ANDROID_PRIORITY_HIGHEST))) {
+                  static_cast<int>(ANDROID_PRIORITY_HIGHEST), // highest is negative
+                  static_cast<int>(ANDROID_PRIORITY_LOWEST))) {
         *err = StringPrintf("process priority value must be range %d - %d",
                 ANDROID_PRIORITY_HIGHEST, ANDROID_PRIORITY_LOWEST);
         return false;
@@ -441,20 +446,48 @@
     return true;
 }
 
-/* name type perm [ uid gid context ] */
+template <typename T>
+bool Service::AddDescriptor(const std::vector<std::string>& args, std::string* err) {
+    int perm = args.size() > 3 ? std::strtoul(args[3].c_str(), 0, 8) : -1;
+    uid_t uid = args.size() > 4 ? decode_uid(args[4].c_str()) : 0;
+    gid_t gid = args.size() > 5 ? decode_uid(args[5].c_str()) : 0;
+    std::string context = args.size() > 6 ? args[6] : "";
+
+    auto descriptor = std::make_unique<T>(args[1], args[2], uid, gid, perm, context);
+
+    auto old =
+        std::find_if(descriptors_.begin(), descriptors_.end(),
+                     [&descriptor] (const auto& other) { return descriptor.get() == other.get(); });
+
+    if (old != descriptors_.end()) {
+        *err = "duplicate descriptor " + args[1] + " " + args[2];
+        return false;
+    }
+
+    descriptors_.emplace_back(std::move(descriptor));
+    return true;
+}
+
+// name type perm [ uid gid context ]
 bool Service::ParseSocket(const std::vector<std::string>& args, std::string* err) {
     if (args[2] != "dgram" && args[2] != "stream" && args[2] != "seqpacket") {
         *err = "socket type must be 'dgram', 'stream' or 'seqpacket'";
         return false;
     }
+    return AddDescriptor<SocketInfo>(args, err);
+}
 
-    int perm = std::strtoul(args[3].c_str(), 0, 8);
-    uid_t uid = args.size() > 4 ? decode_uid(args[4].c_str()) : 0;
-    gid_t gid = args.size() > 5 ? decode_uid(args[5].c_str()) : 0;
-    std::string socketcon = args.size() > 6 ? args[6] : "";
-
-    sockets_.emplace_back(args[1], args[2], uid, gid, perm, socketcon);
-    return true;
+// name type perm [ uid gid context ]
+bool Service::ParseFile(const std::vector<std::string>& args, std::string* err) {
+    if (args[2] != "r" && args[2] != "w" && args[2] != "rw") {
+        *err = "file type must be 'r', 'w' or 'rw'";
+        return false;
+    }
+    if ((args[1][0] != '/') || (args[1].find("../") != std::string::npos)) {
+        *err = "file name must not be relative";
+        return false;
+    }
+    return AddDescriptor<FileInfo>(args, err);
 }
 
 bool Service::ParseUser(const std::vector<std::string>& args, std::string* err) {
@@ -478,6 +511,8 @@
 Service::OptionParserMap::Map& Service::OptionParserMap::map() const {
     constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
     static const Map option_parsers = {
+        {"capabilities",
+                        {1,     kMax, &Service::ParseCapabilities}},
         {"class",       {1,     1,    &Service::ParseClass}},
         {"console",     {0,     1,    &Service::ParseConsole}},
         {"critical",    {0,     0,    &Service::ParseCritical}},
@@ -494,6 +529,7 @@
         {"seclabel",    {1,     1,    &Service::ParseSeclabel}},
         {"setenv",      {2,     2,    &Service::ParseSetenv}},
         {"socket",      {3,     6,    &Service::ParseSocket}},
+        {"file",        {2,     6,    &Service::ParseFile}},
         {"user",        {1,     1,    &Service::ParseUser}},
         {"writepid",    {1,     kMax, &Service::ParseWritepid}},
     };
@@ -520,7 +556,6 @@
     // Starting a service removes it from the disabled or reset state and
     // immediately takes it out of the restarting state if it was in there.
     flags_ &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET|SVC_RESTART|SVC_DISABLED_START));
-    time_started_ = 0;
 
     // Running processes require no additional work --- if they're in the
     // process of exiting, we've ensured that they will immediately restart
@@ -583,7 +618,8 @@
             add_environment(ei.name.c_str(), ei.value.c_str());
         }
 
-        CreateSockets(scon);
+        std::for_each(descriptors_.begin(), descriptors_.end(),
+                      std::bind(&DescriptorInfo::CreateAndPublish, std::placeholders::_1, scon));
 
         std::string pid_str = StringPrintf("%d", getpid());
         for (const auto& file : writepid_files_) {
@@ -633,7 +669,7 @@
         }
     }
 
-    time_started_ = gettime();
+    time_started_ = boot_clock::now();
     pid_ = pid;
     flags_ |= SVC_RUNNING;
 
@@ -697,18 +733,19 @@
     } /* else: Service is restarting anyways. */
 }
 
-void Service::RestartIfNeeded(time_t& process_needs_restart) {
-    time_t next_start_time = time_started_ + 5;
-
-    if (next_start_time <= gettime()) {
+void Service::RestartIfNeeded(time_t* process_needs_restart_at) {
+    boot_clock::time_point now = boot_clock::now();
+    boot_clock::time_point next_start = time_started_ + 5s;
+    if (now > next_start) {
         flags_ &= (~SVC_RESTARTING);
         Start();
         return;
     }
 
-    if ((next_start_time < process_needs_restart) ||
-        (process_needs_restart == 0)) {
-        process_needs_restart = next_start_time;
+    time_t next_start_time_t = time(nullptr) +
+        time_t(std::chrono::duration_cast<std::chrono::seconds>(next_start - now).count());
+    if (next_start_time_t < *process_needs_restart_at || *process_needs_restart_at == 0) {
+        *process_needs_restart_at = next_start_time_t;
     }
 }
 
@@ -757,15 +794,6 @@
     close(fd);
 }
 
-void Service::PublishSocket(const std::string& name, int fd) const {
-    std::string key = StringPrintf(ANDROID_SOCKET_ENV_PREFIX "%s", name.c_str());
-    std::string val = StringPrintf("%d", fd);
-    add_environment(key.c_str(), val.c_str());
-
-    /* make sure we don't close-on-exec */
-    fcntl(fd, F_SETFD, 0);
-}
-
 int ServiceManager::exec_count_ = 0;
 
 ServiceManager::ServiceManager() {
@@ -809,6 +837,7 @@
     exec_count_++;
     std::string name = StringPrintf("exec %d (%s)", exec_count_, str_args[0].c_str());
     unsigned flags = SVC_EXEC | SVC_ONESHOT;
+    CapSet no_capabilities;
     unsigned namespace_flags = 0;
 
     std::string seclabel = "";
@@ -829,9 +858,9 @@
         }
     }
 
-    std::unique_ptr<Service> svc_p(new Service(name, "default", flags, uid, gid,
-                                               supp_gids, namespace_flags,
-                                               seclabel, str_args));
+    std::unique_ptr<Service> svc_p(new Service(name, "default", flags, uid, gid, supp_gids,
+                                               no_capabilities, namespace_flags, seclabel,
+                                               str_args));
     if (!svc_p) {
         LOG(ERROR) << "Couldn't allocate service for exec of '" << str_args[0] << "'";
         return nullptr;
diff --git a/init/service.h b/init/service.h
index 4a3412c..013e65f 100644
--- a/init/service.h
+++ b/init/service.h
@@ -26,8 +26,11 @@
 #include <vector>
 
 #include "action.h"
+#include "capabilities.h"
+#include "descriptors.h"
 #include "init_parser.h"
 #include "keyword_map.h"
+#include "util.h"
 
 #define SVC_DISABLED       0x001  // do not autostart with class
 #define SVC_ONESHOT        0x002  // do not restart on exit
@@ -47,18 +50,6 @@
 class Action;
 class ServiceManager;
 
-struct SocketInfo {
-    SocketInfo();
-    SocketInfo(const std::string& name, const std::string& type, uid_t uid,
-                       gid_t gid, int perm, const std::string& socketcon);
-    std::string name;
-    std::string type;
-    uid_t uid;
-    gid_t gid;
-    int perm;
-    std::string socketcon;
-};
-
 struct ServiceEnvironmentInfo {
     ServiceEnvironmentInfo();
     ServiceEnvironmentInfo(const std::string& name, const std::string& value);
@@ -73,8 +64,9 @@
 
     Service(const std::string& name, const std::string& classname,
             unsigned flags, uid_t uid, gid_t gid,
-            const std::vector<gid_t>& supp_gids, unsigned namespace_flags,
-            const std::string& seclabel, const std::vector<std::string>& args);
+            const std::vector<gid_t>& supp_gids, const CapSet& capabilities,
+            unsigned namespace_flags, const std::string& seclabel,
+            const std::vector<std::string>& args);
 
     bool ParseLine(const std::vector<std::string>& args, std::string* err);
     bool Start();
@@ -84,7 +76,7 @@
     void Stop();
     void Terminate();
     void Restart();
-    void RestartIfNeeded(time_t& process_needs_restart);
+    void RestartIfNeeded(time_t* process_needs_restart_at);
     bool Reap();
     void DumpState() const;
 
@@ -111,11 +103,10 @@
     void StopOrReset(int how);
     void ZapStdio() const;
     void OpenConsole() const;
-    void PublishSocket(const std::string& name, int fd) const;
     void KillProcessGroup(int signal);
-    void CreateSockets(const std::string& scon);
     void SetProcessAttributes();
 
+    bool ParseCapabilities(const std::vector<std::string>& args, std::string *err);
     bool ParseClass(const std::vector<std::string>& args, std::string* err);
     bool ParseConsole(const std::vector<std::string>& args, std::string* err);
     bool ParseCritical(const std::vector<std::string>& args, std::string* err);
@@ -131,27 +122,32 @@
     bool ParseSeclabel(const std::vector<std::string>& args, std::string* err);
     bool ParseSetenv(const std::vector<std::string>& args, std::string* err);
     bool ParseSocket(const std::vector<std::string>& args, std::string* err);
+    bool ParseFile(const std::vector<std::string>& args, std::string* err);
     bool ParseUser(const std::vector<std::string>& args, std::string* err);
     bool ParseWritepid(const std::vector<std::string>& args, std::string* err);
 
+    template <typename T>
+    bool AddDescriptor(const std::vector<std::string>& args, std::string* err);
+
     std::string name_;
     std::string classname_;
     std::string console_;
 
     unsigned flags_;
     pid_t pid_;
-    time_t time_started_;    // time of last start
-    time_t time_crashed_;    // first crash within inspection window
-    int nr_crashed_;         // number of times crashed within window
+    boot_clock::time_point time_started_; // time of last start
+    boot_clock::time_point time_crashed_; // first crash within inspection window
+    int crash_count_;                     // number of times crashed within window
 
     uid_t uid_;
     gid_t gid_;
     std::vector<gid_t> supp_gids_;
+    CapSet capabilities_;
     unsigned namespace_flags_;
 
     std::string seclabel_;
 
-    std::vector<SocketInfo> sockets_;
+    std::vector<std::unique_ptr<DescriptorInfo>> descriptors_;
     std::vector<ServiceEnvironmentInfo> envvars_;
 
     Action onrestart_;  // Commands to execute on restart.
diff --git a/init/ueventd.cpp b/init/ueventd.cpp
index e7794ec..361b925 100644
--- a/init/ueventd.cpp
+++ b/init/ueventd.cpp
@@ -95,7 +95,6 @@
     int prefix = 0;
     int wildcard = 0;
     char *endptr;
-    char *tmp = 0;
 
     if (nargs == 0)
         return;
@@ -129,14 +128,12 @@
     perm = strtol(args[1], &endptr, 8);
     if (!endptr || *endptr != '\0') {
         LOG(ERROR) << "invalid mode '" << args[1] << "'";
-        free(tmp);
         return;
     }
 
     struct passwd* pwd = getpwnam(args[2]);
     if (!pwd) {
         LOG(ERROR) << "invalid uid '" << args[2] << "'";
-        free(tmp);
         return;
     }
     uid = pwd->pw_uid;
@@ -144,11 +141,17 @@
     struct group* grp = getgrnam(args[3]);
     if (!grp) {
         LOG(ERROR) << "invalid gid '" << args[3] << "'";
-        free(tmp);
         return;
     }
     gid = grp->gr_gid;
 
-    add_dev_perms(name, attr, perm, uid, gid, prefix, wildcard);
-    free(tmp);
+    if (add_dev_perms(name, attr, perm, uid, gid, prefix, wildcard) != 0) {
+        PLOG(ERROR) << "add_dev_perms(name=" << name <<
+                       ", attr=" << attr <<
+                       ", perm=" << std::oct << perm << std::dec <<
+                       ", uid=" << uid << ", gid=" << gid <<
+                       ", prefix=" << prefix << ", wildcard=" << wildcard <<
+                       ")";
+        return;
+    }
 }
diff --git a/init/util.cpp b/init/util.cpp
index e451edd..cb5a094 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -14,32 +14,35 @@
  * limitations under the License.
  */
 
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <ftw.h>
+#include <pwd.h>
 #include <stdarg.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
-#include <fcntl.h>
-#include <ctype.h>
-#include <errno.h>
 #include <time.h>
-#include <ftw.h>
-#include <pwd.h>
+#include <unistd.h>
 
-#include <selinux/label.h>
 #include <selinux/android.h>
+#include <selinux/label.h>
 
+#include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/types.h>
-#include <sys/socket.h>
 #include <sys/un.h>
 
+#include <thread>
+
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/stringprintf.h>
 #include <android-base/strings.h>
-
+#include <android-base/unique_fd.h>
 /* for ANDROID_SOCKET_* */
 #include <cutils/sockets.h>
-#include <android-base/stringprintf.h>
 
 #include "init.h"
 #include "log.h"
@@ -89,10 +92,6 @@
 int create_socket(const char *name, int type, mode_t perm, uid_t uid,
                   gid_t gid, const char *socketcon)
 {
-    struct sockaddr_un addr;
-    int fd, ret, savederrno;
-    char *filecon;
-
     if (socketcon) {
         if (setsockcreatecon(socketcon) == -1) {
             PLOG(ERROR) << "setsockcreatecon(\"" << socketcon << "\") failed";
@@ -100,52 +99,49 @@
         }
     }
 
-    fd = socket(PF_UNIX, type, 0);
+    android::base::unique_fd fd(socket(PF_UNIX, type, 0));
     if (fd < 0) {
         PLOG(ERROR) << "Failed to open socket '" << name << "'";
         return -1;
     }
 
-    if (socketcon)
-        setsockcreatecon(NULL);
+    if (socketcon) setsockcreatecon(NULL);
 
+    struct sockaddr_un addr;
     memset(&addr, 0 , sizeof(addr));
     addr.sun_family = AF_UNIX;
     snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR"/%s",
              name);
 
-    ret = unlink(addr.sun_path);
-    if (ret != 0 && errno != ENOENT) {
+    if ((unlink(addr.sun_path) != 0) && (errno != ENOENT)) {
         PLOG(ERROR) << "Failed to unlink old socket '" << name << "'";
-        goto out_close;
+        return -1;
     }
 
-    filecon = NULL;
+    char *filecon = NULL;
     if (sehandle) {
-        ret = selabel_lookup(sehandle, &filecon, addr.sun_path, S_IFSOCK);
-        if (ret == 0)
+        if (selabel_lookup(sehandle, &filecon, addr.sun_path, S_IFSOCK) == 0) {
             setfscreatecon(filecon);
+        }
     }
 
-    ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr));
-    savederrno = errno;
+    int ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr));
+    int savederrno = errno;
 
     setfscreatecon(NULL);
     freecon(filecon);
 
     if (ret) {
-      errno = savederrno;
+        errno = savederrno;
         PLOG(ERROR) << "Failed to bind socket '" << name << "'";
         goto out_unlink;
     }
 
-    ret = lchown(addr.sun_path, uid, gid);
-    if (ret) {
+    if (lchown(addr.sun_path, uid, gid)) {
         PLOG(ERROR) << "Failed to lchown socket '" << addr.sun_path << "'";
         goto out_unlink;
     }
-    ret = fchmodat(AT_FDCWD, addr.sun_path, perm, AT_SYMLINK_NOFOLLOW);
-    if (ret) {
+    if (fchmodat(AT_FDCWD, addr.sun_path, perm, AT_SYMLINK_NOFOLLOW)) {
         PLOG(ERROR) << "Failed to fchmodat socket '" << addr.sun_path << "'";
         goto out_unlink;
     }
@@ -155,15 +151,76 @@
               << ", user " << uid
               << ", group " << gid;
 
-    return fd;
+    return fd.release();
 
 out_unlink:
     unlink(addr.sun_path);
-out_close:
-    close(fd);
     return -1;
 }
 
+/*
+ * create_file - opens and creates a file as dictated in init.rc.
+ * This file is inherited by the daemon. We communicate the file
+ * descriptor's value via the environment variable ANDROID_FILE_<basename>
+ */
+int create_file(const char *path, int flags, mode_t perm, uid_t uid,
+                  gid_t gid, const char *filecon)
+{
+    char *secontext = NULL;
+
+    if (filecon) {
+        if (setsockcreatecon(filecon) == -1) {
+            PLOG(ERROR) << "setsockcreatecon(\"" << filecon << "\") failed";
+            return -1;
+        }
+    } else if (sehandle) {
+        if (selabel_lookup(sehandle, &secontext, path, perm) != -1) {
+            if (setfscreatecon(secontext) == -1) {
+                freecon(secontext); // does not upset errno value
+                PLOG(ERROR) << "setfscreatecon(\"" << secontext << "\") failed";
+                return -1;
+            }
+        }
+    }
+
+    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path, flags | O_NDELAY, perm)));
+    int savederrno = errno;
+
+    if (filecon) {
+        setsockcreatecon(NULL);
+        lsetfilecon(path, filecon);
+    } else {
+        setfscreatecon(NULL);
+        freecon(secontext);
+    }
+
+    if (fd < 0) {
+        errno = savederrno;
+        PLOG(ERROR) << "Failed to open/create file '" << path << "'";
+        return -1;
+    }
+
+    if (!(flags & O_NDELAY)) fcntl(fd, F_SETFD, flags);
+
+    if (lchown(path, uid, gid)) {
+        PLOG(ERROR) << "Failed to lchown file '" << path << "'";
+        return -1;
+    }
+    if (perm != static_cast<mode_t>(-1)) {
+        if (fchmodat(AT_FDCWD, path, perm, AT_SYMLINK_NOFOLLOW)) {
+            PLOG(ERROR) << "Failed to fchmodat file '" << path << "'";
+            return -1;
+        }
+    }
+
+    LOG(INFO) << "Created file '" << path << "'"
+              << ", mode " << std::oct << perm << std::dec
+              << ", user " << uid
+              << ", group " << gid;
+
+    return fd.release();
+}
+
 bool read_file(const char* path, std::string* content) {
     content->clear();
 
@@ -203,16 +260,11 @@
     return result;
 }
 
-time_t gettime() {
-    timespec now;
-    clock_gettime(CLOCK_MONOTONIC, &now);
-    return now.tv_sec;
-}
-
-uint64_t gettime_ns() {
-    timespec now;
-    clock_gettime(CLOCK_MONOTONIC, &now);
-    return static_cast<uint64_t>(now.tv_sec) * UINT64_C(1000000000) + now.tv_nsec;
+boot_clock::time_point boot_clock::now() {
+  timespec ts;
+  clock_gettime(CLOCK_BOOTTIME, &ts);
+  return boot_clock::time_point(std::chrono::seconds(ts.tv_sec) +
+                                std::chrono::nanoseconds(ts.tv_nsec));
 }
 
 int mkdir_recursive(const char *pathname, mode_t mode)
@@ -270,16 +322,15 @@
     }
 }
 
-int wait_for_file(const char *filename, int timeout)
-{
-    struct stat info;
-    uint64_t timeout_time_ns = gettime_ns() + timeout * UINT64_C(1000000000);
-    int ret = -1;
+int wait_for_file(const char* filename, std::chrono::nanoseconds timeout) {
+    boot_clock::time_point timeout_time = boot_clock::now() + timeout;
+    while (boot_clock::now() < timeout_time) {
+        struct stat sb;
+        if (stat(filename, &sb) != -1) return 0;
 
-    while (gettime_ns() < timeout_time_ns && ((ret = stat(filename, &info)) < 0))
-        usleep(10000);
-
-    return ret;
+        std::this_thread::sleep_for(10ms);
+    }
+    return -1;
 }
 
 void import_kernel_cmdline(bool in_qemu,
diff --git a/init/util.h b/init/util.h
index 5fcbdf0..dccec04 100644
--- a/init/util.h
+++ b/init/util.h
@@ -20,38 +20,51 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 
+#include <chrono>
 #include <string>
 #include <functional>
 
 #define COLDBOOT_DONE "/dev/.coldboot_done"
 
+using namespace std::chrono_literals;
+
 int create_socket(const char *name, int type, mode_t perm,
                   uid_t uid, gid_t gid, const char *socketcon);
+int create_file(const char *path, int mode, mode_t perm,
+                uid_t uid, gid_t gid, const char *filecon);
 
 bool read_file(const char* path, std::string* content);
 int write_file(const char* path, const char* content);
 
-time_t gettime();
-uint64_t gettime_ns();
+// A std::chrono clock based on CLOCK_BOOTTIME.
+class boot_clock {
+ public:
+  typedef std::chrono::nanoseconds duration;
+  typedef std::chrono::time_point<boot_clock, duration> time_point;
+  static constexpr bool is_steady = true;
+
+  static time_point now();
+};
 
 class Timer {
  public:
-  Timer() : t0(gettime_ns()) {
+  Timer() : start_(boot_clock::now()) {
   }
 
   double duration() {
-    return static_cast<double>(gettime_ns() - t0) / 1000000000.0;
+    typedef std::chrono::duration<double> double_duration;
+    return std::chrono::duration_cast<double_duration>(boot_clock::now() - start_).count();
   }
 
  private:
-  uint64_t t0;
+  boot_clock::time_point start_;
 };
 
 unsigned int decode_uid(const char *s);
 
 int mkdir_recursive(const char *pathname, mode_t mode);
 void sanitize(char *p);
-int wait_for_file(const char *filename, int timeout);
+int wait_for_file(const char *filename, std::chrono::nanoseconds timeout);
 void import_kernel_cmdline(bool in_qemu,
                            const std::function<void(const std::string&, const std::string&, bool)>&);
 int make_dir(const char *path, mode_t mode);
diff --git a/init/util_test.cpp b/init/util_test.cpp
index 228954b..6ecbf90 100644
--- a/init/util_test.cpp
+++ b/init/util_test.cpp
@@ -17,7 +17,15 @@
 #include "util.h"
 
 #include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <cutils/files.h>
 #include <gtest/gtest.h>
+#include <selinux/android.h>
 
 TEST(util, read_file_ENOENT) {
   std::string s("hello");
@@ -41,3 +49,51 @@
   EXPECT_EQ(UINT_MAX, decode_uid("toot"));
   EXPECT_EQ(123U, decode_uid("123"));
 }
+
+struct selabel_handle *sehandle;
+
+TEST(util, create_file) {
+  if (!sehandle) sehandle = selinux_android_file_context_handle();
+
+  static const char path[] = "/data/local/tmp/util.create_file.test";
+  static const char key[] = ANDROID_FILE_ENV_PREFIX "_data_local_tmp_util_create_file_test";
+  EXPECT_EQ(unsetenv(key), 0);
+  unlink(path);
+
+  int fd;
+  uid_t uid = decode_uid("logd");
+  gid_t gid = decode_uid("system");
+  mode_t perms = S_IRWXU | S_IWGRP | S_IRGRP | S_IROTH;
+  static const char context[] = "u:object_r:misc_logd_file:s0";
+  EXPECT_GE(fd = create_file(path, O_RDWR | O_CREAT, perms, uid, gid, context), 0);
+  if (fd < 0) return;
+  static const char hello[] = "hello world\n";
+  static const ssize_t len = strlen(hello);
+  EXPECT_EQ(write(fd, hello, len), len);
+  char buffer[sizeof(hello)];
+  memset(buffer, 0, sizeof(buffer));
+  EXPECT_GE(lseek(fd, 0, SEEK_SET), 0);
+  EXPECT_EQ(read(fd, buffer, sizeof(buffer)), len);
+  EXPECT_EQ(strcmp(hello, buffer), 0);
+  char val[32];
+  snprintf(val, sizeof(val), "%d", fd);
+  EXPECT_EQ(android_get_control_file(path), -1);
+  setenv(key, val, true);
+  EXPECT_EQ(android_get_control_file(path), fd);
+  close(fd);
+  EXPECT_EQ(android_get_control_file(path), -1);
+  EXPECT_EQ(unsetenv(key), 0);
+  struct stat st;
+  EXPECT_EQ(stat(path, &st), 0);
+  EXPECT_EQ(st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO), perms);
+  EXPECT_EQ(st.st_uid, uid);
+  EXPECT_EQ(st.st_gid, gid);
+  security_context_t con;
+  EXPECT_GE(getfilecon(path, &con), 0);
+  EXPECT_NE(con, static_cast<security_context_t>(NULL));
+  if (con) {
+    EXPECT_EQ(context, std::string(con));
+  }
+  freecon(con);
+  EXPECT_EQ(unlink(path), 0);
+}
diff --git a/libappfuse/Android.bp b/libappfuse/Android.bp
index 8b46154..f729faf 100644
--- a/libappfuse/Android.bp
+++ b/libappfuse/Android.bp
@@ -15,12 +15,20 @@
     name: "libappfuse",
     defaults: ["libappfuse_defaults"],
     export_include_dirs: ["include"],
-    srcs: ["FuseBuffer.cc", "FuseBridgeLoop.cc"]
+    srcs: [
+        "FuseAppLoop.cc",
+        "FuseBuffer.cc",
+        "FuseBridgeLoop.cc",
+    ]
 }
 
 cc_test {
     name: "libappfuse_test",
     defaults: ["libappfuse_defaults"],
     shared_libs: ["libappfuse"],
-    srcs: ["tests/FuseBridgeLoopTest.cc", "tests/FuseBufferTest.cc"]
+    srcs: [
+        "tests/FuseAppLoopTest.cc",
+        "tests/FuseBridgeLoopTest.cc",
+        "tests/FuseBufferTest.cc",
+    ]
 }
diff --git a/libappfuse/FuseAppLoop.cc b/libappfuse/FuseAppLoop.cc
new file mode 100644
index 0000000..a31880e
--- /dev/null
+++ b/libappfuse/FuseAppLoop.cc
@@ -0,0 +1,221 @@
+/*
+ * 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 specic language governing permissions and
+ * limitations under the License.
+ */
+
+#include "libappfuse/FuseAppLoop.h"
+
+#include <sys/stat.h>
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace fuse {
+
+namespace {
+
+void HandleLookUp(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
+  // AppFuse does not support directory structure now.
+  // It can lookup only files under the mount point.
+  if (buffer->request.header.nodeid != FUSE_ROOT_ID) {
+    LOG(ERROR) << "Nodeid is not FUSE_ROOT_ID.";
+    buffer->response.Reset(0, -ENOENT, buffer->request.header.unique);
+    return;
+  }
+
+  // Ensure that the filename ends with 0.
+  const size_t filename_length =
+      buffer->request.header.len - sizeof(fuse_in_header);
+  if (buffer->request.lookup_name[filename_length - 1] != 0) {
+    LOG(ERROR) << "File name does not end with 0.";
+    buffer->response.Reset(0, -ENOENT, buffer->request.header.unique);
+    return;
+  }
+
+  const uint64_t inode =
+      static_cast<uint64_t>(atol(buffer->request.lookup_name));
+  if (inode == 0 || inode == LONG_MAX) {
+    LOG(ERROR) << "Invalid filename";
+    buffer->response.Reset(0, -ENOENT, buffer->request.header.unique);
+    return;
+  }
+
+  const int64_t size = callback->OnGetSize(inode);
+  if (size < 0) {
+    buffer->response.Reset(0, size, buffer->request.header.unique);
+    return;
+  }
+
+  buffer->response.Reset(sizeof(fuse_entry_out), 0,
+                         buffer->request.header.unique);
+  buffer->response.entry_out.nodeid = inode;
+  buffer->response.entry_out.attr_valid = 10;
+  buffer->response.entry_out.entry_valid = 10;
+  buffer->response.entry_out.attr.ino = inode;
+  buffer->response.entry_out.attr.mode = S_IFREG | 0777;
+  buffer->response.entry_out.attr.size = size;
+}
+
+void HandleGetAttr(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
+  const uint64_t nodeid = buffer->request.header.nodeid;
+  int64_t size;
+  uint32_t mode;
+  if (nodeid == FUSE_ROOT_ID) {
+    size = 0;
+    mode = S_IFDIR | 0777;
+  } else {
+    size = callback->OnGetSize(buffer->request.header.nodeid);
+    if (size < 0) {
+      buffer->response.Reset(0, size, buffer->request.header.unique);
+      return;
+    }
+    mode = S_IFREG | 0777;
+  }
+
+  buffer->response.Reset(sizeof(fuse_attr_out), 0,
+                         buffer->request.header.unique);
+  buffer->response.attr_out.attr_valid = 10;
+  buffer->response.attr_out.attr.ino = nodeid;
+  buffer->response.attr_out.attr.mode = mode;
+  buffer->response.attr_out.attr.size = size;
+}
+
+void HandleOpen(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
+  const int32_t file_handle = callback->OnOpen(buffer->request.header.nodeid);
+  if (file_handle < 0) {
+    buffer->response.Reset(0, file_handle, buffer->request.header.unique);
+    return;
+  }
+  buffer->response.Reset(sizeof(fuse_open_out), kFuseSuccess,
+                         buffer->request.header.unique);
+  buffer->response.open_out.fh = file_handle;
+}
+
+void HandleFsync(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
+  buffer->response.Reset(0, callback->OnFsync(buffer->request.header.nodeid),
+                         buffer->request.header.unique);
+}
+
+void HandleRelease(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
+  buffer->response.Reset(0, callback->OnRelease(buffer->request.header.nodeid),
+                         buffer->request.header.unique);
+}
+
+void HandleRead(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
+  const uint64_t unique = buffer->request.header.unique;
+  const uint64_t nodeid = buffer->request.header.nodeid;
+  const uint64_t offset = buffer->request.read_in.offset;
+  const uint32_t size = buffer->request.read_in.size;
+
+  if (size > kFuseMaxRead) {
+    buffer->response.Reset(0, -EINVAL, buffer->request.header.unique);
+    return;
+  }
+
+  const int32_t read_size = callback->OnRead(nodeid, offset, size,
+                                             buffer->response.read_data);
+  if (read_size < 0) {
+    buffer->response.Reset(0, read_size, buffer->request.header.unique);
+    return;
+  }
+
+  buffer->response.ResetHeader(read_size, kFuseSuccess, unique);
+}
+
+void HandleWrite(FuseBuffer* buffer, FuseAppLoopCallback* callback) {
+  const uint64_t unique = buffer->request.header.unique;
+  const uint64_t nodeid = buffer->request.header.nodeid;
+  const uint64_t offset = buffer->request.write_in.offset;
+  const uint32_t size = buffer->request.write_in.size;
+
+  if (size > kFuseMaxWrite) {
+    buffer->response.Reset(0, -EINVAL, buffer->request.header.unique);
+    return;
+  }
+
+  const int32_t write_size = callback->OnWrite(nodeid, offset, size,
+                                               buffer->request.write_data);
+  if (write_size < 0) {
+    buffer->response.Reset(0, write_size, buffer->request.header.unique);
+    return;
+  }
+
+  buffer->response.Reset(sizeof(fuse_write_out), kFuseSuccess, unique);
+  buffer->response.write_out.size = write_size;
+}
+
+} // namespace
+
+bool StartFuseAppLoop(int raw_fd, FuseAppLoopCallback* callback) {
+  base::unique_fd fd(raw_fd);
+  FuseBuffer buffer;
+
+  LOG(DEBUG) << "Start fuse loop.";
+  while (callback->IsActive()) {
+    if (!buffer.request.Read(fd)) {
+      return false;
+    }
+
+    const uint32_t opcode = buffer.request.header.opcode;
+    LOG(VERBOSE) << "Read a fuse packet, opcode=" << opcode;
+    switch (opcode) {
+      case FUSE_FORGET:
+        // Do not reply to FUSE_FORGET.
+        continue;
+
+      case FUSE_LOOKUP:
+        HandleLookUp(&buffer, callback);
+        break;
+
+      case FUSE_GETATTR:
+        HandleGetAttr(&buffer, callback);
+        break;
+
+      case FUSE_OPEN:
+        HandleOpen(&buffer, callback);
+        break;
+
+      case FUSE_READ:
+        HandleRead(&buffer, callback);
+        break;
+
+      case FUSE_WRITE:
+        HandleWrite(&buffer, callback);
+        break;
+
+      case FUSE_RELEASE:
+        HandleRelease(&buffer, callback);
+        break;
+
+      case FUSE_FSYNC:
+        HandleFsync(&buffer, callback);
+        break;
+
+      default:
+        buffer.HandleNotImpl();
+        break;
+    }
+
+    if (!buffer.response.Write(fd)) {
+      LOG(ERROR) << "Failed to write a response to the device.";
+      return false;
+    }
+  }
+
+  return true;
+}
+
+}  // namespace fuse
+}  // namespace android
diff --git a/libappfuse/FuseBridgeLoop.cc b/libappfuse/FuseBridgeLoop.cc
index 332556d..acb963c 100644
--- a/libappfuse/FuseBridgeLoop.cc
+++ b/libappfuse/FuseBridgeLoop.cc
@@ -25,14 +25,15 @@
     int raw_dev_fd, int raw_proxy_fd, FuseBridgeLoop::Callback* callback) {
   base::unique_fd dev_fd(raw_dev_fd);
   base::unique_fd proxy_fd(raw_proxy_fd);
+  fuse::FuseBuffer buffer;
 
   LOG(DEBUG) << "Start fuse loop.";
   while (true) {
-    if (!buffer_.request.Read(dev_fd)) {
+    if (!buffer.request.Read(dev_fd)) {
       return false;
     }
 
-    const uint32_t opcode = buffer_.request.header.opcode;
+    const uint32_t opcode = buffer.request.header.opcode;
     LOG(VERBOSE) << "Read a fuse packet, opcode=" << opcode;
     switch (opcode) {
       case FUSE_FORGET:
@@ -45,27 +46,27 @@
       case FUSE_READ:
       case FUSE_WRITE:
       case FUSE_RELEASE:
-      case FUSE_FLUSH:
-        if (!buffer_.request.Write(proxy_fd)) {
+      case FUSE_FSYNC:
+        if (!buffer.request.Write(proxy_fd)) {
           LOG(ERROR) << "Failed to write a request to the proxy.";
           return false;
         }
-        if (!buffer_.response.Read(proxy_fd)) {
+        if (!buffer.response.Read(proxy_fd)) {
           LOG(ERROR) << "Failed to read a response from the proxy.";
           return false;
         }
         break;
 
       case FUSE_INIT:
-        buffer_.HandleInit();
+        buffer.HandleInit();
         break;
 
       default:
-        buffer_.HandleNotImpl();
+        buffer.HandleNotImpl();
         break;
     }
 
-    if (!buffer_.response.Write(dev_fd)) {
+    if (!buffer.response.Write(dev_fd)) {
       LOG(ERROR) << "Failed to write a response to the device.";
       return false;
     }
@@ -76,4 +77,12 @@
   }
 }
 
+namespace fuse {
+
+bool StartFuseBridgeLoop(
+    int raw_dev_fd, int raw_proxy_fd, FuseBridgeLoopCallback* callback) {
+  return FuseBridgeLoop().Start(raw_dev_fd, raw_proxy_fd, callback);
+}
+
+}  // namespace fuse
 }  // namespace android
diff --git a/libappfuse/FuseBuffer.cc b/libappfuse/FuseBuffer.cc
index 45280a5..3ade31c 100644
--- a/libappfuse/FuseBuffer.cc
+++ b/libappfuse/FuseBuffer.cc
@@ -21,15 +21,22 @@
 #include <unistd.h>
 
 #include <algorithm>
+#include <type_traits>
 
 #include <android-base/logging.h>
 #include <android-base/macros.h>
 
 namespace android {
+namespace fuse {
 
-template <typename T, typename Header>
-bool FuseMessage<T, Header>::CheckHeaderLength() const {
-  if (sizeof(Header) <= header.len && header.len <= sizeof(T)) {
+static_assert(
+    std::is_standard_layout<FuseBuffer>::value,
+    "FuseBuffer must be standard layout union.");
+
+template <typename T>
+bool FuseMessage<T>::CheckHeaderLength() const {
+  const auto& header = static_cast<const T*>(this)->header;
+  if (sizeof(header) <= header.len && header.len <= sizeof(T)) {
     return true;
   } else {
     LOG(ERROR) << "Packet size is invalid=" << header.len;
@@ -37,27 +44,29 @@
   }
 }
 
-template <typename T, typename Header>
-bool FuseMessage<T, Header>::CheckResult(
+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 from FD. result=" << result << " header.len="
+        << " a packet. result=" << result << " header.len="
         << header.len;
     return false;
   }
 }
 
-template <typename T, typename Header>
-bool FuseMessage<T, Header>::Read(int fd) {
+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");
 }
 
-template <typename T, typename Header>
-bool FuseMessage<T, Header>::Write(int fd) const {
+template <typename T>
+bool FuseMessage<T>::Write(int fd) const {
+  const auto& header = static_cast<const T*>(this)->header;
   if (!CheckHeaderLength()) {
     return false;
   }
@@ -65,8 +74,16 @@
   return CheckResult(result, "write");
 }
 
-template struct FuseMessage<FuseRequest, fuse_in_header>;
-template struct FuseMessage<FuseResponse, fuse_out_header>;
+template class FuseMessage<FuseRequest>;
+template class FuseMessage<FuseResponse>;
+
+void FuseRequest::Reset(
+    uint32_t data_length, uint32_t opcode, uint64_t unique) {
+  memset(this, 0, sizeof(fuse_in_header) + data_length);
+  header.len = sizeof(fuse_in_header) + data_length;
+  header.opcode = opcode;
+  header.unique = unique;
+}
 
 void FuseResponse::ResetHeader(
     uint32_t data_length, int32_t error, uint64_t unique) {
@@ -101,23 +118,18 @@
     return;
   }
 
-  // We limit ourselves to 15 because we don't handle BATCH_FORGET yet
-  size_t response_size = sizeof(fuse_init_out);
+  // We limit ourselves to minor=15 because we don't handle BATCH_FORGET yet.
+  // Thus we need to use FUSE_COMPAT_22_INIT_OUT_SIZE.
 #if defined(FUSE_COMPAT_22_INIT_OUT_SIZE)
   // FUSE_KERNEL_VERSION >= 23.
-
-  // If the kernel only works on minor revs older than or equal to 22,
-  // then use the older structure size since this code only uses the 7.22
-  // version of the structure.
-  if (minor <= 22) {
-    response_size = FUSE_COMPAT_22_INIT_OUT_SIZE;
-  }
+  const size_t response_size = FUSE_COMPAT_22_INIT_OUT_SIZE;
+#else
+  const size_t response_size = sizeof(fuse_init_out);
 #endif
 
   response.Reset(response_size, kFuseSuccess, unique);
   fuse_init_out* const out = &response.init_out;
   out->major = FUSE_KERNEL_VERSION;
-  // We limit ourselves to 15 because we don't handle BATCH_FORGET yet.
   out->minor = std::min(minor, 15u);
   out->max_readahead = max_readahead;
   out->flags = FUSE_ATOMIC_O_TRUNC | FUSE_BIG_WRITES;
@@ -133,4 +145,5 @@
   response.Reset(0, -ENOSYS, unique);
 }
 
+}  // namespace fuse
 }  // namespace android
diff --git a/libappfuse/include/libappfuse/FuseAppLoop.h b/libappfuse/include/libappfuse/FuseAppLoop.h
new file mode 100644
index 0000000..c3edfcc
--- /dev/null
+++ b/libappfuse/include/libappfuse/FuseAppLoop.h
@@ -0,0 +1,44 @@
+/*
+ * 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 specic language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_LIBAPPFUSE_FUSEAPPLOOP_H_
+#define ANDROID_LIBAPPFUSE_FUSEAPPLOOP_H_
+
+#include "libappfuse/FuseBuffer.h"
+
+namespace android {
+namespace fuse {
+
+class FuseAppLoopCallback {
+ public:
+  virtual bool IsActive() = 0;
+  virtual int64_t OnGetSize(uint64_t inode) = 0;
+  virtual int32_t OnFsync(uint64_t inode) = 0;
+  virtual int32_t OnWrite(
+      uint64_t inode, uint64_t offset, uint32_t size, const void* data) = 0;
+  virtual int32_t OnRead(
+      uint64_t inode, uint64_t offset, uint32_t size, void* data) = 0;
+  virtual int32_t OnOpen(uint64_t inode) = 0;
+  virtual int32_t OnRelease(uint64_t inode) = 0;
+  virtual ~FuseAppLoopCallback() = default;
+};
+
+bool StartFuseAppLoop(int fd, FuseAppLoopCallback* callback);
+
+}  // namespace fuse
+}  // namespace android
+
+#endif  // ANDROID_LIBAPPFUSE_FUSEAPPLOOP_H_
diff --git a/libappfuse/include/libappfuse/FuseBridgeLoop.h b/libappfuse/include/libappfuse/FuseBridgeLoop.h
index 2006532..38043bc 100644
--- a/libappfuse/include/libappfuse/FuseBridgeLoop.h
+++ b/libappfuse/include/libappfuse/FuseBridgeLoop.h
@@ -21,7 +21,9 @@
 
 namespace android {
 
-class FuseBridgeLoop {
+// TODO: Remove the class after switching to StartFuseBridgeLoop in the
+// framework code.
+class FuseBridgeLoop final {
  public:
   class Callback {
    public:
@@ -30,11 +32,15 @@
   };
 
   bool Start(int dev_fd, int proxy_fd, Callback* callback);
-
- private:
-  FuseBuffer buffer_;
 };
 
+namespace fuse {
+
+class FuseBridgeLoopCallback : public FuseBridgeLoop::Callback {};
+bool StartFuseBridgeLoop(
+    int dev_fd, int proxy_fd, FuseBridgeLoopCallback* callback);
+
+}  // namespace fuse
 }  // namespace android
 
 #endif  // ANDROID_LIBAPPFUSE_FUSEBRIDGELOOP_H_
diff --git a/libappfuse/include/libappfuse/FuseBuffer.h b/libappfuse/include/libappfuse/FuseBuffer.h
index 071b777..e7f620c 100644
--- a/libappfuse/include/libappfuse/FuseBuffer.h
+++ b/libappfuse/include/libappfuse/FuseBuffer.h
@@ -20,6 +20,7 @@
 #include <linux/fuse.h>
 
 namespace android {
+namespace fuse {
 
 // The numbers came from sdcard.c.
 // Maximum number of bytes to write/read in one request/one reply.
@@ -27,9 +28,9 @@
 constexpr size_t kFuseMaxRead = 128 * 1024;
 constexpr int32_t kFuseSuccess = 0;
 
-template<typename T, typename Header>
-struct FuseMessage {
-  Header header;
+template<typename T>
+class FuseMessage {
+ public:
   bool Read(int fd);
   bool Write(int fd) const;
  private:
@@ -37,33 +38,53 @@
   bool CheckResult(int result, const char* operation_name) const;
 };
 
-struct FuseRequest : public FuseMessage<FuseRequest, fuse_in_header> {
+// FuseRequest represents file operation requests from /dev/fuse. It starts
+// from fuse_in_header. The body layout depends on the operation code.
+struct FuseRequest : public FuseMessage<FuseRequest> {
+  fuse_in_header header;
   union {
+    // for FUSE_WRITE
     struct {
       fuse_write_in write_in;
       char write_data[kFuseMaxWrite];
     };
+    // for FUSE_OPEN
     fuse_open_in open_in;
+    // for FUSE_INIT
     fuse_init_in init_in;
+    // for FUSE_READ
     fuse_read_in read_in;
+    // for FUSE_LOOKUP
     char lookup_name[0];
   };
+  void Reset(uint32_t data_length, uint32_t opcode, uint64_t unique);
 };
 
-struct FuseResponse : public FuseMessage<FuseResponse, fuse_out_header> {
+// FuseResponse represents file operation responses to /dev/fuse. It starts
+// from fuse_out_header. The body layout depends on the operation code.
+struct FuseResponse : public FuseMessage<FuseResponse> {
+  fuse_out_header header;
   union {
+    // for FUSE_INIT
     fuse_init_out init_out;
+    // for FUSE_LOOKUP
     fuse_entry_out entry_out;
+    // for FUSE_GETATTR
     fuse_attr_out attr_out;
+    // for FUSE_OPEN
     fuse_open_out open_out;
+    // for FUSE_READ
     char read_data[kFuseMaxRead];
+    // for FUSE_WRITE
     fuse_write_out write_out;
   };
   void Reset(uint32_t data_length, int32_t error, uint64_t unique);
   void ResetHeader(uint32_t data_length, int32_t error, uint64_t unique);
 };
 
-union FuseBuffer {
+// To reduce memory usage, FuseBuffer shares the memory region for request and
+// response.
+union FuseBuffer final {
   FuseRequest request;
   FuseResponse response;
 
@@ -71,19 +92,7 @@
   void HandleNotImpl();
 };
 
-class FuseProxyLoop {
-  class IFuseProxyLoopCallback {
-   public:
-    virtual void OnMount() = 0;
-    virtual ~IFuseProxyLoopCallback() = default;
-  };
-
-  bool Start(int dev_fd, int proxy_fd, IFuseProxyLoopCallback* callback);
-
- private:
-  FuseBuffer buffer_;
-};
-
+}  // namespace fuse
 }  // namespace android
 
 #endif  // ANDROID_LIBAPPFUSE_FUSEBUFFER_H_
diff --git a/libappfuse/tests/FuseAppLoopTest.cc b/libappfuse/tests/FuseAppLoopTest.cc
new file mode 100644
index 0000000..25906cf
--- /dev/null
+++ b/libappfuse/tests/FuseAppLoopTest.cc
@@ -0,0 +1,307 @@
+/*
+ * 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 specic language governing permissions and
+ * limitations under the License.
+ */
+
+#include "libappfuse/FuseAppLoop.h"
+
+#include <sys/socket.h>
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <gtest/gtest.h>
+#include <thread>
+
+namespace android {
+namespace fuse {
+namespace {
+
+constexpr unsigned int kTestFileSize = 1024;
+
+struct CallbackRequest {
+  uint32_t code;
+  uint64_t inode;
+};
+
+class Callback : public FuseAppLoopCallback {
+ public:
+  std::vector<CallbackRequest> requests;
+
+  bool IsActive() override {
+    return true;
+  }
+
+  int64_t OnGetSize(uint64_t inode) override {
+    if (inode == FUSE_ROOT_ID) {
+      return 0;
+    } else {
+      return kTestFileSize;
+    }
+  }
+
+  int32_t OnFsync(uint64_t inode) override {
+    requests.push_back({
+      .code = FUSE_FSYNC,
+      .inode = inode
+    });
+    return 0;
+  }
+
+  int32_t OnWrite(uint64_t inode,
+                  uint64_t offset ATTRIBUTE_UNUSED,
+                  uint32_t size ATTRIBUTE_UNUSED,
+                  const void* data ATTRIBUTE_UNUSED) override {
+    requests.push_back({
+      .code = FUSE_WRITE,
+      .inode = inode
+    });
+    return 0;
+  }
+
+  int32_t OnRead(uint64_t inode,
+                 uint64_t offset ATTRIBUTE_UNUSED,
+                 uint32_t size ATTRIBUTE_UNUSED,
+                 void* data ATTRIBUTE_UNUSED) override {
+    requests.push_back({
+      .code = FUSE_READ,
+      .inode = inode
+    });
+    return 0;
+  }
+
+  int32_t OnOpen(uint64_t inode) override {
+    requests.push_back({
+      .code = FUSE_OPEN,
+      .inode = inode
+    });
+    return 0;
+  }
+
+  int32_t OnRelease(uint64_t inode) override {
+    requests.push_back({
+      .code = FUSE_RELEASE,
+      .inode = inode
+    });
+    return 0;
+  }
+};
+
+class FuseAppLoopTest : public ::testing::Test {
+ private:
+  std::thread thread_;
+
+ protected:
+  base::unique_fd sockets_[2];
+  Callback callback_;
+  FuseRequest request_;
+  FuseResponse response_;
+
+  void SetUp() override {
+    base::SetMinimumLogSeverity(base::VERBOSE);
+    int sockets[2];
+    ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets));
+    sockets_[0].reset(sockets[0]);
+    sockets_[1].reset(sockets[1]);
+    thread_ = std::thread([this] {
+      StartFuseAppLoop(sockets_[1].release(), &callback_);
+    });
+  }
+
+  void CheckCallback(
+      size_t data_size, uint32_t code, size_t expected_out_size) {
+    request_.Reset(data_size, code, 1);
+    request_.header.nodeid = 10;
+
+    ASSERT_TRUE(request_.Write(sockets_[0]));
+    ASSERT_TRUE(response_.Read(sockets_[0]));
+
+    Close();
+
+    EXPECT_EQ(kFuseSuccess, response_.header.error);
+    EXPECT_EQ(sizeof(fuse_out_header) + expected_out_size,
+              response_.header.len);
+    EXPECT_EQ(1u, response_.header.unique);
+
+    ASSERT_EQ(1u, callback_.requests.size());
+    EXPECT_EQ(code, callback_.requests[0].code);
+    EXPECT_EQ(10u, callback_.requests[0].inode);
+  }
+
+  void Close() {
+    sockets_[0].reset();
+    sockets_[1].reset();
+    if (thread_.joinable()) {
+      thread_.join();
+    }
+  }
+
+  void TearDown() override {
+    Close();
+  }
+};
+
+}  // namespace
+
+TEST_F(FuseAppLoopTest, LookUp) {
+  request_.Reset(3u, FUSE_LOOKUP, 1);
+  request_.header.nodeid = FUSE_ROOT_ID;
+  strcpy(request_.lookup_name, "10");
+
+  ASSERT_TRUE(request_.Write(sockets_[0].get()));
+  ASSERT_TRUE(response_.Read(sockets_[0].get()));
+
+  EXPECT_EQ(kFuseSuccess, response_.header.error);
+  EXPECT_EQ(sizeof(fuse_out_header) + sizeof(fuse_entry_out),
+            response_.header.len);
+  EXPECT_EQ(1u, response_.header.unique);
+
+  EXPECT_EQ(10u, response_.entry_out.nodeid);
+  EXPECT_EQ(0u, response_.entry_out.generation);
+  EXPECT_EQ(10u, response_.entry_out.entry_valid);
+  EXPECT_EQ(10u, response_.entry_out.attr_valid);
+  EXPECT_EQ(0u, response_.entry_out.entry_valid_nsec);
+  EXPECT_EQ(0u, response_.entry_out.attr_valid_nsec);
+
+  EXPECT_EQ(10u, response_.entry_out.attr.ino);
+  EXPECT_EQ(kTestFileSize, response_.entry_out.attr.size);
+  EXPECT_EQ(0u, response_.entry_out.attr.blocks);
+  EXPECT_EQ(0u, response_.entry_out.attr.atime);
+  EXPECT_EQ(0u, response_.entry_out.attr.mtime);
+  EXPECT_EQ(0u, response_.entry_out.attr.ctime);
+  EXPECT_EQ(0u, response_.entry_out.attr.atimensec);
+  EXPECT_EQ(0u, response_.entry_out.attr.mtimensec);
+  EXPECT_EQ(0u, response_.entry_out.attr.ctimensec);
+  EXPECT_EQ(S_IFREG | 0777u, response_.entry_out.attr.mode);
+  EXPECT_EQ(0u, response_.entry_out.attr.nlink);
+  EXPECT_EQ(0u, response_.entry_out.attr.uid);
+  EXPECT_EQ(0u, response_.entry_out.attr.gid);
+  EXPECT_EQ(0u, response_.entry_out.attr.rdev);
+  EXPECT_EQ(0u, response_.entry_out.attr.blksize);
+  EXPECT_EQ(0u, response_.entry_out.attr.padding);
+}
+
+TEST_F(FuseAppLoopTest, LookUp_InvalidName) {
+  request_.Reset(3u, FUSE_LOOKUP, 1);
+  request_.header.nodeid = FUSE_ROOT_ID;
+  strcpy(request_.lookup_name, "aa");
+
+  ASSERT_TRUE(request_.Write(sockets_[0].get()));
+  ASSERT_TRUE(response_.Read(sockets_[0].get()));
+
+  EXPECT_EQ(sizeof(fuse_out_header), response_.header.len);
+  EXPECT_EQ(-ENOENT, response_.header.error);
+  EXPECT_EQ(1u, response_.header.unique);
+}
+
+TEST_F(FuseAppLoopTest, LookUp_TooLargeName) {
+  request_.Reset(21u, FUSE_LOOKUP, 1);
+  request_.header.nodeid = FUSE_ROOT_ID;
+  strcpy(request_.lookup_name, "18446744073709551616");
+
+  ASSERT_TRUE(request_.Write(sockets_[0].get()));
+  ASSERT_TRUE(response_.Read(sockets_[0].get()));
+
+  EXPECT_EQ(sizeof(fuse_out_header), response_.header.len);
+  EXPECT_EQ(-ENOENT, response_.header.error);
+  EXPECT_EQ(1u, response_.header.unique);
+}
+
+TEST_F(FuseAppLoopTest, GetAttr) {
+  request_.Reset(sizeof(fuse_getattr_in), FUSE_GETATTR, 1);
+  request_.header.nodeid = 10;
+
+  ASSERT_TRUE(request_.Write(sockets_[0].get()));
+  ASSERT_TRUE(response_.Read(sockets_[0].get()));
+
+  EXPECT_EQ(kFuseSuccess, response_.header.error);
+  EXPECT_EQ(sizeof(fuse_out_header) + sizeof(fuse_attr_out),
+            response_.header.len);
+  EXPECT_EQ(1u, response_.header.unique);
+
+  EXPECT_EQ(10u, response_.attr_out.attr_valid);
+  EXPECT_EQ(0u, response_.attr_out.attr_valid_nsec);
+
+  EXPECT_EQ(10u, response_.attr_out.attr.ino);
+  EXPECT_EQ(kTestFileSize, response_.attr_out.attr.size);
+  EXPECT_EQ(0u, response_.attr_out.attr.blocks);
+  EXPECT_EQ(0u, response_.attr_out.attr.atime);
+  EXPECT_EQ(0u, response_.attr_out.attr.mtime);
+  EXPECT_EQ(0u, response_.attr_out.attr.ctime);
+  EXPECT_EQ(0u, response_.attr_out.attr.atimensec);
+  EXPECT_EQ(0u, response_.attr_out.attr.mtimensec);
+  EXPECT_EQ(0u, response_.attr_out.attr.ctimensec);
+  EXPECT_EQ(S_IFREG | 0777u, response_.attr_out.attr.mode);
+  EXPECT_EQ(0u, response_.attr_out.attr.nlink);
+  EXPECT_EQ(0u, response_.attr_out.attr.uid);
+  EXPECT_EQ(0u, response_.attr_out.attr.gid);
+  EXPECT_EQ(0u, response_.attr_out.attr.rdev);
+  EXPECT_EQ(0u, response_.attr_out.attr.blksize);
+  EXPECT_EQ(0u, response_.attr_out.attr.padding);
+}
+
+TEST_F(FuseAppLoopTest, GetAttr_Root) {
+  request_.Reset(sizeof(fuse_getattr_in), FUSE_GETATTR, 1);
+  request_.header.nodeid = FUSE_ROOT_ID;
+
+  ASSERT_TRUE(request_.Write(sockets_[0].get()));
+  ASSERT_TRUE(response_.Read(sockets_[0].get()));
+
+  EXPECT_EQ(kFuseSuccess, response_.header.error);
+  EXPECT_EQ(sizeof(fuse_out_header) + sizeof(fuse_attr_out),
+            response_.header.len);
+  EXPECT_EQ(1u, response_.header.unique);
+
+  EXPECT_EQ(10u, response_.attr_out.attr_valid);
+  EXPECT_EQ(0u, response_.attr_out.attr_valid_nsec);
+
+  EXPECT_EQ(static_cast<unsigned>(FUSE_ROOT_ID), response_.attr_out.attr.ino);
+  EXPECT_EQ(0u, response_.attr_out.attr.size);
+  EXPECT_EQ(0u, response_.attr_out.attr.blocks);
+  EXPECT_EQ(0u, response_.attr_out.attr.atime);
+  EXPECT_EQ(0u, response_.attr_out.attr.mtime);
+  EXPECT_EQ(0u, response_.attr_out.attr.ctime);
+  EXPECT_EQ(0u, response_.attr_out.attr.atimensec);
+  EXPECT_EQ(0u, response_.attr_out.attr.mtimensec);
+  EXPECT_EQ(0u, response_.attr_out.attr.ctimensec);
+  EXPECT_EQ(S_IFDIR | 0777u, response_.attr_out.attr.mode);
+  EXPECT_EQ(0u, response_.attr_out.attr.nlink);
+  EXPECT_EQ(0u, response_.attr_out.attr.uid);
+  EXPECT_EQ(0u, response_.attr_out.attr.gid);
+  EXPECT_EQ(0u, response_.attr_out.attr.rdev);
+  EXPECT_EQ(0u, response_.attr_out.attr.blksize);
+  EXPECT_EQ(0u, response_.attr_out.attr.padding);
+}
+
+TEST_F(FuseAppLoopTest, Open) {
+  CheckCallback(sizeof(fuse_open_in), FUSE_OPEN, sizeof(fuse_open_out));
+}
+
+TEST_F(FuseAppLoopTest, Fsync) {
+  CheckCallback(0u, FUSE_FSYNC, 0u);
+}
+
+TEST_F(FuseAppLoopTest, Release) {
+  CheckCallback(0u, FUSE_RELEASE, 0u);
+}
+
+TEST_F(FuseAppLoopTest, Read) {
+  CheckCallback(sizeof(fuse_read_in), FUSE_READ, 0u);
+}
+
+TEST_F(FuseAppLoopTest, Write) {
+  CheckCallback(sizeof(fuse_write_in), FUSE_WRITE, sizeof(fuse_write_out));
+}
+
+}  // namespace fuse
+}  // namespace android
diff --git a/libappfuse/tests/FuseBridgeLoopTest.cc b/libappfuse/tests/FuseBridgeLoopTest.cc
index 31e3690..bd503eb 100644
--- a/libappfuse/tests/FuseBridgeLoopTest.cc
+++ b/libappfuse/tests/FuseBridgeLoopTest.cc
@@ -21,11 +21,15 @@
 #include <sstream>
 #include <thread>
 
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
 #include <gtest/gtest.h>
 
 namespace android {
+namespace fuse {
+namespace {
 
-class Callback : public FuseBridgeLoop::Callback {
+class Callback : public FuseBridgeLoopCallback {
  public:
   bool mounted;
   Callback() : mounted(false) {}
@@ -36,20 +40,28 @@
 
 class FuseBridgeLoopTest : public ::testing::Test {
  protected:
-  int dev_sockets_[2];
-  int proxy_sockets_[2];
+  base::unique_fd dev_sockets_[2];
+  base::unique_fd proxy_sockets_[2];
   Callback callback_;
   std::thread thread_;
 
   FuseRequest request_;
   FuseResponse response_;
 
-  void SetUp() {
-    ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, dev_sockets_));
-    ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, proxy_sockets_));
+  void SetUp() override {
+    base::SetMinimumLogSeverity(base::VERBOSE);
+    int dev_sockets[2];
+    int proxy_sockets[2];
+    ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, dev_sockets));
+    ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_SEQPACKET, 0, proxy_sockets));
+    dev_sockets_[0].reset(dev_sockets[0]);
+    dev_sockets_[1].reset(dev_sockets[1]);
+    proxy_sockets_[0].reset(proxy_sockets[0]);
+    proxy_sockets_[1].reset(proxy_sockets[1]);
+
     thread_ = std::thread([this] {
-      FuseBridgeLoop loop;
-      loop.Start(dev_sockets_[1], proxy_sockets_[0], &callback_);
+      StartFuseBridgeLoop(
+          dev_sockets_[1].release(), proxy_sockets_[0].release(), &callback_);
     });
   }
 
@@ -103,20 +115,22 @@
   }
 
   void Close() {
-    close(dev_sockets_[0]);
-    close(dev_sockets_[1]);
-    close(proxy_sockets_[0]);
-    close(proxy_sockets_[1]);
+    dev_sockets_[0].reset();
+    dev_sockets_[1].reset();
+    proxy_sockets_[0].reset();
+    proxy_sockets_[1].reset();
     if (thread_.joinable()) {
       thread_.join();
     }
   }
 
-  void TearDown() {
+  void TearDown() override {
     Close();
   }
 };
 
+} //  namespace
+
 TEST_F(FuseBridgeLoopTest, FuseInit) {
   SendInitRequest(1u);
 
@@ -156,11 +170,11 @@
   CheckNotImpl(FUSE_RENAME);
   CheckNotImpl(FUSE_LINK);
   CheckNotImpl(FUSE_STATFS);
-  CheckNotImpl(FUSE_FSYNC);
   CheckNotImpl(FUSE_SETXATTR);
   CheckNotImpl(FUSE_GETXATTR);
   CheckNotImpl(FUSE_LISTXATTR);
   CheckNotImpl(FUSE_REMOVEXATTR);
+  CheckNotImpl(FUSE_FLUSH);
   CheckNotImpl(FUSE_OPENDIR);
   CheckNotImpl(FUSE_READDIR);
   CheckNotImpl(FUSE_RELEASEDIR);
@@ -190,7 +204,8 @@
   CheckProxy(FUSE_READ);
   CheckProxy(FUSE_WRITE);
   CheckProxy(FUSE_RELEASE);
-  CheckProxy(FUSE_FLUSH);
+  CheckProxy(FUSE_FSYNC);
 }
 
-}  // android
+}  // namespace fuse
+}  // namespace android
diff --git a/libappfuse/tests/FuseBufferTest.cc b/libappfuse/tests/FuseBufferTest.cc
index 1aacfe3..c822135 100644
--- a/libappfuse/tests/FuseBufferTest.cc
+++ b/libappfuse/tests/FuseBufferTest.cc
@@ -24,6 +24,7 @@
 #include <gtest/gtest.h>
 
 namespace android {
+namespace fuse {
 
 constexpr char kTempFile[] = "/data/local/tmp/appfuse_test_dump";
 
@@ -163,7 +164,7 @@
 
   buffer.HandleInit();
 
-  ASSERT_EQ(sizeof(fuse_out_header) + sizeof(fuse_init_out),
+  ASSERT_EQ(sizeof(fuse_out_header) + FUSE_COMPAT_22_INIT_OUT_SIZE,
             buffer.response.header.len);
   EXPECT_EQ(kFuseSuccess, buffer.response.header.error);
   EXPECT_EQ(static_cast<unsigned int>(FUSE_KERNEL_VERSION),
@@ -183,5 +184,6 @@
   ASSERT_EQ(sizeof(fuse_out_header), buffer.response.header.len);
   EXPECT_EQ(-ENOSYS, buffer.response.header.error);
 }
-}
-  // namespace android
+
+} // namespace fuse
+} // namespace android
diff --git a/libbacktrace/BacktracePtrace.cpp b/libbacktrace/BacktracePtrace.cpp
index 148c418..fd8b713 100644
--- a/libbacktrace/BacktracePtrace.cpp
+++ b/libbacktrace/BacktracePtrace.cpp
@@ -17,7 +17,6 @@
 #include <errno.h>
 #include <stdint.h>
 #include <string.h>
-#include <sys/uio.h>
 #include <sys/param.h>
 #include <sys/ptrace.h>
 #include <sys/types.h>
@@ -73,20 +72,42 @@
   if (!BacktraceMap::IsValid(map) || !(map.flags & PROT_READ)) {
     return 0;
   }
+
   bytes = MIN(map.end - addr, bytes);
-
-  struct iovec local_io;
-  local_io.iov_base = buffer;
-  local_io.iov_len = bytes;
-
-  struct iovec remote_io;
-  remote_io.iov_base = reinterpret_cast<void*>(addr);
-  remote_io.iov_len = bytes;
-
-  ssize_t bytes_read = process_vm_readv(Tid(), &local_io, 1, &remote_io, 1, 0);
-  if (bytes_read == -1) {
-    return 0;
+  size_t bytes_read = 0;
+  word_t data_word;
+  size_t align_bytes = addr & (sizeof(word_t) - 1);
+  if (align_bytes != 0) {
+    if (!PtraceRead(Tid(), addr & ~(sizeof(word_t) - 1), &data_word)) {
+      return 0;
+    }
+    size_t copy_bytes = MIN(sizeof(word_t) - align_bytes, bytes);
+    memcpy(buffer, reinterpret_cast<uint8_t*>(&data_word) + align_bytes, copy_bytes);
+    addr += copy_bytes;
+    buffer += copy_bytes;
+    bytes -= copy_bytes;
+    bytes_read += copy_bytes;
   }
-  return static_cast<size_t>(bytes_read);
+
+  size_t num_words = bytes / sizeof(word_t);
+  for (size_t i = 0; i < num_words; i++) {
+    if (!PtraceRead(Tid(), addr, &data_word)) {
+      return bytes_read;
+    }
+    memcpy(buffer, &data_word, sizeof(word_t));
+    buffer += sizeof(word_t);
+    addr += sizeof(word_t);
+    bytes_read += sizeof(word_t);
+  }
+
+  size_t left_over = bytes & (sizeof(word_t) - 1);
+  if (left_over) {
+    if (!PtraceRead(Tid(), addr, &data_word)) {
+      return bytes_read;
+    }
+    memcpy(buffer, &data_word, left_over);
+    bytes_read += left_over;
+  }
+  return bytes_read;
 #endif
 }
diff --git a/libcutils/Android.bp b/libcutils/Android.bp
index 943926b..f7b497d 100644
--- a/libcutils/Android.bp
+++ b/libcutils/Android.bp
@@ -34,6 +34,7 @@
     host_supported: true,
     srcs: [
         "config_utils.c",
+        "files.cpp",
         "fs_config.c",
         "canned_fs_config.c",
         "hashmap.c",
diff --git a/libcutils/files.cpp b/libcutils/files.cpp
new file mode 100644
index 0000000..bf15b42
--- /dev/null
+++ b/libcutils/files.cpp
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+// This file contains files implementation that can be shared between
+// platforms as long as the correct headers are included.
+#define _GNU_SOURCE 1 // for asprintf
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <cutils/files.h>
+
+#ifndef TEMP_FAILURE_RETRY // _WIN32 does not define
+#define TEMP_FAILURE_RETRY(exp) (exp)
+#endif
+
+int android_get_control_file(const char* path) {
+    if (!path) return -1;
+
+    char *key = NULL;
+    if (asprintf(&key, ANDROID_FILE_ENV_PREFIX "%s", path) < 0) return -1;
+    if (!key) return -1;
+
+    char *cp = key;
+    while (*cp) {
+        if (!isalnum(*cp)) *cp = '_';
+        ++cp;
+    }
+
+    const char* val = getenv(key);
+    free(key);
+    if (!val) return -1;
+
+    errno = 0;
+    long fd = strtol(val, NULL, 10);
+    if (errno) return -1;
+
+    // validity checking
+    if ((fd < 0) || (fd > INT_MAX)) return -1;
+#if defined(_SC_OPEN_MAX)
+    if (fd >= sysconf(_SC_OPEN_MAX)) return -1;
+#elif defined(OPEN_MAX)
+    if (fd >= OPEN_MAX) return -1;
+#elif defined(_POSIX_OPEN_MAX)
+    if (fd >= _POSIX_OPEN_MAX) return -1;
+#endif
+
+#if defined(F_GETFD)
+    if (TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD)) < 0) return -1;
+#elif defined(F_GETFL)
+    if (TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL)) < 0) return -1;
+#else
+    struct stat s;
+    if (TEMP_FAILURE_RETRY(fstat(fd, &s)) < 0) return -1;
+#endif
+
+#if defined(__linux__)
+    char *proc = NULL;
+    if (asprintf(&proc, "/proc/self/fd/%ld", fd) < 0) return -1;
+    if (!proc) return -1;
+
+    size_t len = strlen(path);
+    char *buf = static_cast<char *>(calloc(1, len + 2));
+    if (!buf) {
+        free(proc);
+        return -1;
+    }
+    ssize_t ret = TEMP_FAILURE_RETRY(readlink(proc, buf, len + 1));
+    free(proc);
+    int cmp = (len != static_cast<size_t>(ret)) || strcmp(buf, path);
+    free(buf);
+    if (ret < 0) return -1;
+    if (cmp != 0) return -1;
+#endif
+
+    // It is what we think it is
+    return static_cast<int>(fd);
+}
diff --git a/libcutils/fs_config.c b/libcutils/fs_config.c
index 9908637..60a389b 100644
--- a/libcutils/fs_config.c
+++ b/libcutils/fs_config.c
@@ -139,15 +139,35 @@
     { 06755, AID_ROOT,      AID_ROOT,      0, "system/xbin/procmem" },
 
     /* the following files have enhanced capabilities and ARE included in user builds. */
-    { 00750, AID_ROOT,      AID_SHELL,     CAP_MASK_LONG(CAP_SETUID) | CAP_MASK_LONG(CAP_SETGID), "system/bin/run-as" },
-    { 00700, AID_SYSTEM,    AID_SHELL,     CAP_MASK_LONG(CAP_BLOCK_SUSPEND), "system/bin/inputflinger" },
+    { 00550, AID_LOGD,      AID_LOGD,      CAP_MASK_LONG(CAP_SYSLOG) |
+                                           CAP_MASK_LONG(CAP_AUDIT_CONTROL) |
+                                           CAP_MASK_LONG(CAP_SETGID),
+                                              "system/bin/logd" },
+    { 00750, AID_ROOT,      AID_SHELL,     CAP_MASK_LONG(CAP_SETUID) |
+                                           CAP_MASK_LONG(CAP_SETGID),
+                                              "system/bin/run-as" },
+    { 00700, AID_SYSTEM,    AID_SHELL,     CAP_MASK_LONG(CAP_BLOCK_SUSPEND),
+                                              "system/bin/inputflinger" },
 
     /* Support hostapd administering a network interface. */
-    { 00755, AID_WIFI,      AID_WIFI,     CAP_MASK_LONG(CAP_NET_ADMIN) |
-                                          CAP_MASK_LONG(CAP_NET_RAW),    "system/bin/hostapd" },
+    { 00755, AID_WIFI,      AID_WIFI,      CAP_MASK_LONG(CAP_NET_ADMIN) |
+                                           CAP_MASK_LONG(CAP_NET_RAW),
+                                              "system/bin/hostapd" },
 
     /* Support wifi_hal_legacy administering a network interface. */
-    { 00755, AID_WIFI,      AID_WIFI,     CAP_MASK_LONG(CAP_NET_ADMIN) | CAP_MASK_LONG(CAP_NET_RAW),    "system/bin/hw/android.hardware.wifi@1.0-service" },
+    { 00755, AID_WIFI,      AID_WIFI,      CAP_MASK_LONG(CAP_NET_ADMIN) |
+                                           CAP_MASK_LONG(CAP_NET_RAW),
+                                              "system/bin/hw/android.hardware.wifi@1.0-service" },
+
+    /* A non-privileged zygote that spawns isolated processes for web rendering. */
+    { 0750,  AID_ROOT,      AID_ROOT,      CAP_MASK_LONG(CAP_SETUID) |
+                                           CAP_MASK_LONG(CAP_SETGID) |
+                                           CAP_MASK_LONG(CAP_SETPCAP),
+                                              "system/bin/webview_zygote32" },
+    { 0750,  AID_ROOT,      AID_ROOT,      CAP_MASK_LONG(CAP_SETUID) |
+                                           CAP_MASK_LONG(CAP_SETGID) |
+                                           CAP_MASK_LONG(CAP_SETPCAP),
+                                              "system/bin/webview_zygote64" },
 
     { 00750, AID_ROOT,      AID_ROOT,      0, "system/bin/uncrypt" },
     { 00750, AID_ROOT,      AID_ROOT,      0, "system/bin/install-recovery.sh" },
diff --git a/libcutils/klog.cpp b/libcutils/klog.cpp
index 061af1b..9d823cf 100644
--- a/libcutils/klog.cpp
+++ b/libcutils/klog.cpp
@@ -24,6 +24,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <cutils/files.h>
 #include <cutils/klog.h>
 
 static int klog_level = KLOG_DEFAULT_LEVEL;
@@ -37,7 +38,11 @@
 }
 
 static int __open_klog(void) {
-    return TEMP_FAILURE_RETRY(open("/dev/kmsg", O_WRONLY | O_CLOEXEC));
+    static const char kmsg_device[] = "/dev/kmsg";
+
+    int ret = android_get_control_file(kmsg_device);
+    if (ret >= 0) return ret;
+    return TEMP_FAILURE_RETRY(open(kmsg_device, O_WRONLY | O_CLOEXEC));
 }
 
 #define LOG_BUF_MAX 512
diff --git a/libcutils/sockets.cpp b/libcutils/sockets.cpp
index bba63ac..63761a2 100644
--- a/libcutils/sockets.cpp
+++ b/libcutils/sockets.cpp
@@ -28,11 +28,31 @@
 
 // This file contains socket implementation that can be shared between
 // platforms as long as the correct headers are included.
+#define _GNU_SOURCE 1 // For asprintf
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#if !defined(_WIN32)
+#include <netinet/in.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#if !defined(_WIN32)
+#include <sys/un.h>
+#endif
+#include <unistd.h>
+
+#include <string>
 
 #include <cutils/sockets.h>
 
-#if !defined(_WIN32)
-#include <netinet/in.h>
+#ifndef TEMP_FAILURE_RETRY // _WIN32 does not define
+#define TEMP_FAILURE_RETRY(exp) (exp)
 #endif
 
 int socket_get_local_port(cutils_socket_t sock) {
@@ -47,22 +67,56 @@
 }
 
 int android_get_control_socket(const char* name) {
-    char key[64];
-    snprintf(key, sizeof(key), ANDROID_SOCKET_ENV_PREFIX "%s", name);
+    char *key = NULL;
+    if (asprintf(&key, ANDROID_SOCKET_ENV_PREFIX "%s", name) < 0) return -1;
+    if (!key) return -1;
+
+    char *cp = key;
+    while (*cp) {
+        if (!isalnum(*cp)) *cp = '_';
+        ++cp;
+    }
 
     const char* val = getenv(key);
-    if (!val) {
-        return -1;
-    }
+    free(key);
+    if (!val) return -1;
 
     errno = 0;
-    long ret = strtol(val, NULL, 10);
-    if (errno) {
-        return -1;
-    }
-    if (ret < 0 || ret > INT_MAX) {
-        return -1;
-    }
+    long fd = strtol(val, NULL, 10);
+    if (errno) return -1;
 
-    return static_cast<int>(ret);
+    // validity checking
+    if ((fd < 0) || (fd > INT_MAX)) return -1;
+#if defined(_SC_OPEN_MAX)
+    if (fd >= sysconf(_SC_OPEN_MAX)) return -1;
+#elif defined(OPEN_MAX)
+    if (fd >= OPEN_MAX) return -1;
+#elif defined(_POSIX_OPEN_MAX)
+    if (fd >= _POSIX_OPEN_MAX) return -1;
+#endif
+
+#if defined(F_GETFD)
+    if (TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD)) < 0) return -1;
+#elif defined(F_GETFL)
+    if (TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL)) < 0) return -1;
+#else
+    struct stat s;
+    if (TEMP_FAILURE_RETRY(fstat(fd, &s)) < 0) return -1;
+#endif
+
+#if !defined(_WIN32)
+    struct sockaddr_un addr;
+    socklen_t addrlen = sizeof(addr);
+    int ret = TEMP_FAILURE_RETRY(getsockname(fd, (struct sockaddr *)&addr, &addrlen));
+    if (ret < 0) return -1;
+    char *path = NULL;
+    if (asprintf(&path, ANDROID_SOCKET_DIR"/%s", name) < 0) return -1;
+    if (!path) return -1;
+    int cmp = strcmp(addr.sun_path, path);
+    free(path);
+    if (cmp != 0) return -1;
+#endif
+
+    // It is what we think it is
+    return static_cast<int>(fd);
 }
diff --git a/libcutils/tests/Android.bp b/libcutils/tests/Android.bp
index 06d0e28..bd35412 100644
--- a/libcutils/tests/Android.bp
+++ b/libcutils/tests/Android.bp
@@ -14,7 +14,7 @@
 
 cc_defaults {
     name: "libcutils_test_default",
-    srcs: ["sockets_test.cpp"],
+    srcs: ["sockets_test.cpp", "files_test.cpp"],
 
     target: {
         android: {
diff --git a/libcutils/tests/files_test.cpp b/libcutils/tests/files_test.cpp
new file mode 100644
index 0000000..1a7d673
--- /dev/null
+++ b/libcutils/tests/files_test.cpp
@@ -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.
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include <cutils/files.h>
+#include <gtest/gtest.h>
+
+TEST(FilesTest, android_get_control_file) {
+    static const char key[] = ANDROID_FILE_ENV_PREFIX "_dev_kmsg";
+    static const char name[] = "/dev/kmsg";
+
+    EXPECT_EQ(unsetenv(key), 0);
+    EXPECT_EQ(android_get_control_file(name), -1);
+
+    int fd;
+    ASSERT_GE(fd = open(name, O_RDONLY | O_CLOEXEC), 0);
+    EXPECT_EQ(android_get_control_file(name), -1);
+
+    char val[32];
+    snprintf(val, sizeof(val), "%d", fd);
+    EXPECT_EQ(setenv(key, val, true), 0);
+
+    EXPECT_EQ(android_get_control_file(name), fd);
+    close(fd);
+    EXPECT_EQ(android_get_control_file(name), -1);
+    EXPECT_EQ(unsetenv(key), 0);
+    EXPECT_EQ(android_get_control_file(name), -1);
+}
diff --git a/libcutils/tests/sockets_test.cpp b/libcutils/tests/sockets_test.cpp
index 0f682a2..adfbf4a 100644
--- a/libcutils/tests/sockets_test.cpp
+++ b/libcutils/tests/sockets_test.cpp
@@ -18,10 +18,14 @@
 // IPv6 capabilities. These tests assume that no UDP packets are lost, which
 // should be the case for loopback communication, but is not guaranteed.
 
-#include <cutils/sockets.h>
-
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
 #include <time.h>
 
+#include <cutils/sockets.h>
 #include <gtest/gtest.h>
 
 // Makes sure the passed sockets are valid, sends data between them, and closes
@@ -185,3 +189,49 @@
 TEST(SocketsTest, TestSocketSendBuffersFailure) {
     EXPECT_EQ(-1, socket_send_buffers(INVALID_SOCKET, nullptr, 0));
 }
+
+#ifndef SOCK_NONBLOCK
+#define SOCK_NONBLOCK 0
+#endif
+
+#ifndef SOCK_CLOEXEC
+#define SOCK_CLOEXEC 0
+#endif
+
+TEST(SocketsTest, android_get_control_socket) {
+    static const char key[] = ANDROID_SOCKET_ENV_PREFIX "SocketsTest_android_get_control_socket";
+    static const char* name = key + strlen(ANDROID_SOCKET_ENV_PREFIX);
+
+    EXPECT_EQ(unsetenv(key), 0);
+    EXPECT_EQ(android_get_control_socket(name), -1);
+
+    int fd;
+    ASSERT_GE(fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0), 0);
+#ifdef F_GETFL
+    int flags;
+    ASSERT_GE(flags = fcntl(fd, F_GETFL), 0);
+    ASSERT_GE(fcntl(fd, F_SETFL, flags | O_NONBLOCK), 0);
+#endif
+    EXPECT_EQ(android_get_control_socket(name), -1);
+
+    struct sockaddr_un addr;
+    memset(&addr, 0, sizeof(addr));
+    addr.sun_family = AF_UNIX;
+    snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR"/%s", name);
+    unlink(addr.sun_path);
+
+    EXPECT_EQ(bind(fd, (struct sockaddr*)&addr, sizeof(addr)), 0);
+    EXPECT_EQ(android_get_control_socket(name), -1);
+
+    char val[32];
+    snprintf(val, sizeof(val), "%d", fd);
+    EXPECT_EQ(setenv(key, val, true), 0);
+
+    EXPECT_EQ(android_get_control_socket(name), fd);
+    socket_close(fd);
+    EXPECT_EQ(android_get_control_socket(name), -1);
+    EXPECT_EQ(unlink(addr.sun_path), 0);
+    EXPECT_EQ(android_get_control_socket(name), -1);
+    EXPECT_EQ(unsetenv(key), 0);
+    EXPECT_EQ(android_get_control_socket(name), -1);
+}
diff --git a/liblog/event_tag_map.c b/liblog/event_tag_map.c
index 0e5a281..f9cad99 100644
--- a/liblog/event_tag_map.c
+++ b/liblog/event_tag_map.c
@@ -38,6 +38,8 @@
     uint32_t tagIndex;
     char*    tagStr;
     size_t   tagLen;
+    char*    fmtStr;
+    size_t   fmtLen;
 } EventTag;
 
 /*
@@ -176,6 +178,39 @@
     return NULL;
 }
 
+/*
+ * 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;
+        }
+    }
+
+    errno = ENOENT;
+    if (len) *len = 0;
+    return NULL;
+}
+
 LIBLOG_ABI_PUBLIC const char* android_lookupEventTag(const EventTagMap* map,
                                                      unsigned int tag)
 {
@@ -364,17 +399,21 @@
     }
     tag->tagLen = cp - tag->tagStr;
 
-    if (isspace(*cp)) {
-        /* just ignore the rest of the line till \n
-        TODO: read the tag description that follows the tag name
-        */
-        while (*cp != '\n') ++cp;
-    } else {
+    if (!isspace(*cp)) {
         fprintf(stderr, "%s: invalid tag chars on line %d\n", OUT_TAG, lineNum);
         errno = EINVAL;
         return -1;
     }
 
+    while (isspace(*cp) && (*cp != '\n')) ++cp;
+    if (*cp != '#') {
+        tag->fmtStr = cp;
+        while ((*cp != '\n') && (*cp != '#')) ++cp;
+        while ((cp > tag->fmtStr) && isspace(*(cp - 1))) --cp;
+        tag->fmtLen = cp - tag->fmtStr;
+    }
+
+    while (*cp != '\n') ++cp;
     *pData = cp;
 
     return 0;
@@ -406,12 +445,14 @@
     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 and %" PRIu32 ":%.*s)\n",
+                "%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,
-                map->tagArray[i - 1].tagIndex, (int)map->tagArray[i - 1].tagLen,
-                map->tagArray[i - 1].tagStr);
+                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;
         }
diff --git a/liblog/logger_write.c b/liblog/logger_write.c
index c481e36..157bd88 100644
--- a/liblog/logger_write.c
+++ b/liblog/logger_write.c
@@ -189,7 +189,7 @@
     __android_log_unlock();
 
 #if defined(__BIONIC__)
-    android_closeEventTagMap(m);
+    if (m != (EventTagMap *)(uintptr_t)-1LL) android_closeEventTagMap(m);
 #endif
 
 }
diff --git a/liblog/logprint.c b/liblog/logprint.c
index 1ff7136..4ff7e01 100644
--- a/liblog/logprint.c
+++ b/liblog/logprint.c
@@ -21,13 +21,13 @@
 #include <assert.h>
 #include <ctype.h>
 #include <errno.h>
+#include <inttypes.h>
 #include <pwd.h>
 #include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <inttypes.h>
 #include <sys/param.h>
 #include <sys/types.h>
 
@@ -58,9 +58,16 @@
     bool epoch_output;
     bool monotonic_output;
     bool uid_output;
+    bool descriptive_output;
 };
 
 /*
+ * API issues prevent us from exposing "descriptive" in AndroidLogFormat_t
+ * during android_log_processBinaryLogBuffer(), so we break layering.
+ */
+static bool descriptive_output = false;
+
+/*
  *  gnome-terminal color tags
  *    See http://misc.flogisoft.com/bash/tip_colors_and_formatting
  *    for ideas on how to set the forground color of the text for xterm.
@@ -209,6 +216,8 @@
     p_ret->epoch_output = false;
     p_ret->monotonic_output = android_log_clockid() == CLOCK_MONOTONIC;
     p_ret->uid_output = false;
+    p_ret->descriptive_output = false;
+    descriptive_output = false;
 
     return p_ret;
 }
@@ -267,6 +276,10 @@
     case FORMAT_MODIFIER_UID:
         p_format->uid_output = true;
         return 0;
+    case FORMAT_MODIFIER_DESCRIPT:
+        p_format->descriptive_output = true;
+        descriptive_output = true;
+        return 0;
     default:
         break;
     }
@@ -294,6 +307,7 @@
     else if (strcmp(formatString, "threadtime") == 0) format = FORMAT_THREADTIME;
     else if (strcmp(formatString, "long") == 0) format = FORMAT_LONG;
     else if (strcmp(formatString, "color") == 0) format = FORMAT_MODIFIER_COLOR;
+    else if (strcmp(formatString, "colour") == 0) format = FORMAT_MODIFIER_COLOR;
     else if (strcmp(formatString, "usec") == 0) format = FORMAT_MODIFIER_TIME_USEC;
     else if (strcmp(formatString, "printable") == 0) format = FORMAT_MODIFIER_PRINTABLE;
     else if (strcmp(formatString, "year") == 0) format = FORMAT_MODIFIER_YEAR;
@@ -301,6 +315,7 @@
     else if (strcmp(formatString, "epoch") == 0) format = FORMAT_MODIFIER_EPOCH;
     else if (strcmp(formatString, "monotonic") == 0) format = FORMAT_MODIFIER_MONOTONIC;
     else if (strcmp(formatString, "uid") == 0) format = FORMAT_MODIFIER_UID;
+    else if (strcmp(formatString, "descriptive") == 0) format = FORMAT_MODIFIER_DESCRIPT;
     else {
         extern char *tzname[2];
         static const char gmt[] = "GMT";
@@ -566,6 +581,19 @@
     return ((uint64_t) high << 32) | (uint64_t) low;
 }
 
+static bool findChar(const char** cp, size_t* len, int c) {
+    while (*len && isspace(**cp)) {
+        ++*cp;
+        --*len;
+    }
+    if (c == INT_MAX) return *len;
+    if (*len && (**cp == c)) {
+        ++*cp;
+        --*len;
+        return true;
+    }
+    return false;
+}
 
 /*
  * Recursively convert binary log data to printable form.
@@ -578,63 +606,151 @@
  *
  * Returns 0 on success, 1 on buffer full, -1 on failure.
  */
+enum objectType {
+    TYPE_OBJECTS      = '1',
+    TYPE_BYTES        = '2',
+    TYPE_MILLISECONDS = '3',
+    TYPE_ALLOCATIONS  = '4',
+    TYPE_ID           = '5',
+    TYPE_PERCENT      = '6'
+};
+
 static int android_log_printBinaryEvent(const unsigned char** pEventData,
-    size_t* pEventDataLen, char** pOutBuf, size_t* pOutBufLen)
+    size_t* pEventDataLen, char** pOutBuf, size_t* pOutBufLen,
+    const char** fmtStr, size_t* fmtLen)
 {
     const unsigned char* eventData = *pEventData;
     size_t eventDataLen = *pEventDataLen;
     char* outBuf = *pOutBuf;
+    char* outBufSave = outBuf;
     size_t outBufLen = *pOutBufLen;
+    size_t outBufLenSave = outBufLen;
     unsigned char type;
     size_t outCount;
     int result = 0;
+    const char* cp;
+    size_t len;
+    int64_t lval;
 
-    if (eventDataLen < 1)
-        return -1;
+    if (eventDataLen < 1) return -1;
+
     type = *eventData++;
     eventDataLen--;
 
+    cp = NULL;
+    len = 0;
+    if (fmtStr && *fmtStr && fmtLen && *fmtLen && **fmtStr) {
+        cp = *fmtStr;
+        len = *fmtLen;
+    }
+    /*
+     * event.logtag format specification:
+     *
+     * 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).
+     */
+    if (!cp || !findChar(&cp, &len, '(')) {
+        len = 0;
+    } else {
+        char* outBufLastSpace = NULL;
+
+        findChar(&cp, &len, INT_MAX);
+        while (len && *cp && (*cp != '|') && (*cp != ')')) {
+            if (outBufLen <= 0) {
+                /* halt output */
+                goto no_room;
+            }
+            outBufLastSpace = isspace(*cp) ? outBuf : NULL;
+            *outBuf = *cp;
+            ++outBuf;
+            ++cp;
+            --outBufLen;
+            --len;
+        }
+        if (outBufLastSpace) {
+            outBufLen += outBuf - outBufLastSpace;
+            outBuf = outBufLastSpace;
+        }
+        if (outBufLen <= 0) {
+            /* halt output */
+            goto no_room;
+        }
+        if (outBufSave != outBuf) {
+            *outBuf = '=';
+            ++outBuf;
+            --outBufLen;
+        }
+
+        if (findChar(&cp, &len, '|') && findChar(&cp, &len, INT_MAX)) {
+            static const unsigned char typeTable[] = {
+                EVENT_TYPE_INT,
+                EVENT_TYPE_LONG,
+                EVENT_TYPE_STRING,
+                EVENT_TYPE_LIST,
+                EVENT_TYPE_FLOAT
+            };
+
+            if ((*cp >= '1') &&
+                (*cp < (char)('1' + (sizeof(typeTable) / sizeof(typeTable[0])))) &&
+                (type != typeTable[(size_t)(*cp - '1')])) len = 0;
+
+            if (len) {
+                ++cp;
+                --len;
+            } else {
+                /* reset the format */
+                outBuf = outBufSave;
+                outBufLen = outBufLenSave;
+            }
+        }
+    }
+    lval = 0;
     switch (type) {
     case EVENT_TYPE_INT:
         /* 32-bit signed int */
         {
-            int ival;
+            int32_t ival;
 
-            if (eventDataLen < 4)
-                return -1;
+            if (eventDataLen < 4) return -1;
             ival = get4LE(eventData);
             eventData += 4;
             eventDataLen -= 4;
 
-            outCount = snprintf(outBuf, outBufLen, "%d", ival);
-            if (outCount < outBufLen) {
-                outBuf += outCount;
-                outBufLen -= outCount;
-            } else {
-                /* halt output */
-                goto no_room;
-            }
+            lval = ival;
         }
-        break;
+        goto pr_lval;
     case EVENT_TYPE_LONG:
         /* 64-bit signed long */
-        {
-            uint64_t lval;
-
-            if (eventDataLen < 8)
-                return -1;
-            lval = get8LE(eventData);
-            eventData += 8;
-            eventDataLen -= 8;
-
-            outCount = snprintf(outBuf, outBufLen, "%" PRId64, lval);
-            if (outCount < outBufLen) {
-                outBuf += outCount;
-                outBufLen -= outCount;
-            } else {
-                /* halt output */
-                goto no_room;
-            }
+        if (eventDataLen < 8) return -1;
+        lval = get8LE(eventData);
+        eventData += 8;
+        eventDataLen -= 8;
+    pr_lval:
+        outCount = snprintf(outBuf, outBufLen, "%" PRId64, lval);
+        if (outCount < outBufLen) {
+            outBuf += outCount;
+            outBufLen -= outCount;
+        } else {
+            /* halt output */
+            goto no_room;
         }
         break;
     case EVENT_TYPE_FLOAT:
@@ -643,8 +759,7 @@
             uint32_t ival;
             float fval;
 
-            if (eventDataLen < 4)
-                return -1;
+            if (eventDataLen < 4) return -1;
             ival = get4LE(eventData);
             fval = *(float*)&ival;
             eventData += 4;
@@ -665,15 +780,18 @@
         {
             unsigned int strLen;
 
-            if (eventDataLen < 4)
-                return -1;
+            if (eventDataLen < 4) return -1;
             strLen = get4LE(eventData);
             eventData += 4;
             eventDataLen -= 4;
 
-            if (eventDataLen < strLen)
-                return -1;
+            if (eventDataLen < strLen) return -1;
 
+            if (cp && (strLen == 0)) {
+                /* reset the format if no content */
+                outBuf = outBufSave;
+                outBufLen = outBufLenSave;
+            }
             if (strLen < outBufLen) {
                 memcpy(outBuf, eventData, strLen);
                 outBuf += strLen;
@@ -695,53 +813,122 @@
             unsigned char count;
             int i;
 
-            if (eventDataLen < 1)
-                return -1;
+            if (eventDataLen < 1) return -1;
 
             count = *eventData++;
             eventDataLen--;
 
-            if (outBufLen > 0) {
-                *outBuf++ = '[';
-                outBufLen--;
-            } else {
-                goto no_room;
-            }
+            if (outBufLen <= 0) goto no_room;
+
+            *outBuf++ = '[';
+            outBufLen--;
 
             for (i = 0; i < count; i++) {
                 result = android_log_printBinaryEvent(&eventData, &eventDataLen,
-                        &outBuf, &outBufLen);
-                if (result != 0)
-                    goto bail;
+                        &outBuf, &outBufLen, fmtStr, fmtLen);
+                if (result != 0) goto bail;
 
-                if (i < count-1) {
-                    if (outBufLen > 0) {
-                        *outBuf++ = ',';
-                        outBufLen--;
-                    } else {
-                        goto no_room;
-                    }
+                if (i < (count - 1)) {
+                    if (outBufLen <= 0) goto no_room;
+                    *outBuf++ = ',';
+                    outBufLen--;
                 }
             }
 
-            if (outBufLen > 0) {
-                *outBuf++ = ']';
-                outBufLen--;
-            } else {
-                goto no_room;
-            }
+            if (outBufLen <= 0) goto no_room;
+
+            *outBuf++ = ']';
+            outBufLen--;
         }
         break;
     default:
         fprintf(stderr, "Unknown binary event type %d\n", type);
         return -1;
     }
+    if (cp && len) {
+        if (findChar(&cp, &len, '|') && findChar(&cp, &len, INT_MAX)) {
+            switch (*cp) {
+            case TYPE_OBJECTS:
+                outCount = 0;
+                /* outCount = snprintf(outBuf, outBufLen, " objects"); */
+                break;
+            case TYPE_BYTES:
+                if ((lval != 0) && ((lval % 1024) == 0)) {
+                    /* repaint with multiplier */
+                    static const char suffixTable[] = { 'K', 'M', 'G', 'T' };
+                    size_t idx = 0;
+                    outBuf -= outCount;
+                    outBufLen += outCount;
+                    do {
+                        lval /= 1024;
+                        if ((lval % 1024) != 0) break;
+                    } while (++idx < ((sizeof(suffixTable) /
+                                       sizeof(suffixTable[0])) - 1));
+                    outCount = snprintf(outBuf, outBufLen,
+                                        "%" PRId64 "%cB",
+                                        lval, suffixTable[idx]);
+                } else {
+                    outCount = snprintf(outBuf, outBufLen, "B");
+                }
+                break;
+            case TYPE_MILLISECONDS:
+                if (((lval <= -1000) || (1000 <= lval)) &&
+                        (outBufLen || (outBuf[-1] == '0'))) {
+                    /* repaint as (fractional) seconds, possibly saving space */
+                    if (outBufLen) outBuf[0] = outBuf[-1];
+                    outBuf[-1] = outBuf[-2];
+                    outBuf[-2] = outBuf[-3];
+                    outBuf[-3] = '.';
+                    while ((outBufLen == 0) || (*outBuf == '0')) {
+                        --outBuf;
+                        ++outBufLen;
+                    }
+                    if (*outBuf != '.') {
+                       ++outBuf;
+                       --outBufLen;
+                    }
+                    outCount = snprintf(outBuf, outBufLen, "s");
+                } else {
+                    outCount = snprintf(outBuf, outBufLen, "ms");
+                }
+                break;
+            case TYPE_ALLOCATIONS:
+                outCount = 0;
+                /* outCount = snprintf(outBuf, outBufLen, " allocations"); */
+                break;
+            case TYPE_ID:
+                outCount = 0;
+                break;
+            case TYPE_PERCENT:
+                outCount = snprintf(outBuf, outBufLen, "%%");
+                break;
+            default: /* ? */
+                outCount = 0;
+                break;
+            }
+            ++cp;
+            --len;
+            if (outCount < outBufLen) {
+                outBuf += outCount;
+                outBufLen -= outCount;
+            } else if (outCount) {
+                /* halt output */
+                goto no_room;
+            }
+        }
+        if (!findChar(&cp, &len, ')')) len = 0;
+        if (!findChar(&cp, &len, ',')) len = 0;
+    }
 
 bail:
     *pEventData = eventData;
     *pEventDataLen = eventDataLen;
     *pOutBuf = outBuf;
     *pOutBufLen = outBufLen;
+    if (cp) {
+        *fmtStr = cp;
+        *fmtLen = len;
+    }
     return result;
 
 no_room:
@@ -764,7 +951,7 @@
         char *messageBuf, int messageBufLen)
 {
     size_t inCount;
-    unsigned int tagIndex;
+    uint32_t tagIndex;
     const unsigned char* eventData;
 
     entry->tv_sec = buf->sec;
@@ -796,8 +983,7 @@
         }
     }
     inCount = buf->len;
-    if (inCount < 4)
-        return -1;
+    if (inCount < 4) return -1;
     tagIndex = get4LE(eventData);
     eventData += 4;
     inCount -= 4;
@@ -817,7 +1003,7 @@
     if (entry->tag == NULL) {
         size_t tagLen;
 
-        tagLen = snprintf(messageBuf, messageBufLen, "[%d]", tagIndex);
+        tagLen = snprintf(messageBuf, messageBufLen, "[%" PRIu32 "]", tagIndex);
         if (tagLen >= (size_t)messageBufLen) {
             tagLen = messageBufLen - 1;
         }
@@ -830,24 +1016,44 @@
     /*
      * Format the event log data into the buffer.
      */
+    const char* fmtStr = NULL;
+    size_t fmtLen = 0;
+    if (descriptive_output && map) {
+        fmtStr = android_lookupEventFormat_len(map, &fmtLen, tagIndex);
+    }
+
     char* outBuf = messageBuf;
-    size_t outRemaining = messageBufLen-1;      /* leave one for nul byte */
-    int result;
-    result = android_log_printBinaryEvent(&eventData, &inCount, &outBuf,
-                &outRemaining);
+    size_t outRemaining = messageBufLen - 1; /* leave one for nul byte */
+    int result = 0;
+
+    if ((inCount > 0) || fmtLen) {
+        result = android_log_printBinaryEvent(&eventData, &inCount, &outBuf,
+                                              &outRemaining, &fmtStr, &fmtLen);
+    }
+    if ((result == 1) && fmtStr) {
+        /* We overflowed :-(, let's repaint the line w/o format dressings */
+        eventData = (const unsigned char*)buf->msg;
+        if (buf2->hdr_size) {
+            eventData = ((unsigned char *)buf2) + buf2->hdr_size;
+        }
+        eventData += 4;
+        outBuf = messageBuf;
+        outRemaining = messageBufLen - 1;
+        result = android_log_printBinaryEvent(&eventData, &inCount, &outBuf,
+                                              &outRemaining, NULL, NULL);
+    }
     if (result < 0) {
         fprintf(stderr, "Binary log entry conversion failed\n");
-        return -1;
-    } else if (result == 1) {
-        if (outBuf > messageBuf) {
-            /* leave an indicator */
-            *(outBuf-1) = '!';
-        } else {
-            /* no room to output anything at all */
-            *outBuf++ = '!';
-            outRemaining--;
+    }
+    if (result) {
+        if (!outRemaining) {
+            /* make space to leave an indicator */
+            --outBuf;
+            ++outRemaining;
         }
-        /* pretend we ate all the data */
+        *outBuf++ = (result < 0) ? '!' : '^'; /* Error or Truncation? */
+        outRemaining--;
+        /* pretend we ate all the data to prevent log stutter */
         inCount = 0;
     }
 
@@ -1584,8 +1790,7 @@
     outBuffer = android_log_formatLogLine(p_format, defaultBuffer,
             sizeof(defaultBuffer), entry, &totalLen);
 
-    if (!outBuffer)
-        return -1;
+    if (!outBuffer) return -1;
 
     do {
         ret = write(fd, outBuffer, totalLen);
diff --git a/liblog/tests/libc_test.cpp b/liblog/tests/libc_test.cpp
index 3d58147..f05a955 100644
--- a/liblog/tests/libc_test.cpp
+++ b/liblog/tests/libc_test.cpp
@@ -16,6 +16,7 @@
 
 #include <gtest/gtest.h>
 
+#include <errno.h>
 #include <stdio.h>
 
 TEST(libc, __pstore_append) {
@@ -23,6 +24,22 @@
     ASSERT_TRUE(NULL != (fp = fopen("/dev/pmsg0", "a")));
     static const char message[] = "libc.__pstore_append\n";
     ASSERT_EQ((size_t)1, fwrite(message, sizeof(message), 1, fp));
-    ASSERT_EQ(0, fclose(fp));
-    fprintf(stderr, "Reboot, ensure string libc.__pstore_append is in /sys/fs/pstore/pmsg-ramoops-0\n");
+    int fflushReturn = fflush(fp);
+    int fflushErrno = fflushReturn ? errno : 0;
+    ASSERT_EQ(0, fflushReturn);
+    ASSERT_EQ(0, fflushErrno);
+    int fcloseReturn = fclose(fp);
+    int fcloseErrno = fcloseReturn ? errno : 0;
+    ASSERT_EQ(0, fcloseReturn);
+    ASSERT_EQ(0, fcloseErrno);
+    if ((fcloseErrno == ENOMEM) || (fflushErrno == ENOMEM)) {
+        fprintf(stderr,
+                "Kernel does not have space allocated to pmsg pstore driver configured\n"
+               );
+    }
+    if (!fcloseReturn && !fcloseErrno && !fflushReturn && !fflushReturn) {
+        fprintf(stderr,
+                "Reboot, ensure string libc.__pstore_append is in /sys/fs/pstore/pmsg-ramoops-0\n"
+               );
+    }
 }
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index fd38849..9c09523 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -16,6 +16,7 @@
 
 #include <ctype.h>
 #include <dirent.h>
+#include <errno.h>
 #include <fcntl.h>
 #include <inttypes.h>
 #include <semaphore.h>
@@ -2655,12 +2656,19 @@
     bool logdwActiveAfter__android_log_close = isLogdwActive();
     EXPECT_FALSE(pmsgActiveAfter__android_log_close);
     EXPECT_FALSE(logdwActiveAfter__android_log_close);
-    EXPECT_LT(0, __android_log_pmsg_file_write(
+    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)));
-    fprintf(stderr, "Reboot, ensure file %s matches\n"
-                    "with liblog.__android_log_msg_file_read test\n",
-                    __pmsg_file);
+            __pmsg_file, max_payload_buf, sizeof(max_payload_buf));
+    EXPECT_LT(0, return__android_log_pmsg_file_write);
+    if (return__android_log_pmsg_file_write == -ENOMEM) {
+        fprintf(stderr,
+                "Kernel does not have space allocated to pmsg pstore driver configured\n"
+               );
+    } else if (!return__android_log_pmsg_file_write) {
+        fprintf(stderr, "Reboot, ensure file %s matches\n"
+                        "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);
diff --git a/libnativebridge/native_bridge.cc b/libnativebridge/native_bridge.cc
index 43e6c0a..eafc53d 100644
--- a/libnativebridge/native_bridge.cc
+++ b/libnativebridge/native_bridge.cc
@@ -551,15 +551,15 @@
   return -1;
 }
 
-char* NativeBridgeGetError() {
+const char* NativeBridgeGetError() {
   if (NativeBridgeInitialized()) {
     if (isCompatibleWith(NAMESPACE_VERSION)) {
       return callbacks->getError();
     } else {
-      ALOGE("not compatible with version %d, cannot get message", NAMESPACE_VERSION);
+      return "native bridge implementation is not compatible with version 3, cannot get message";
     }
   }
-  return nullptr;
+  return "native bridge is not initialized";
 }
 
 bool NativeBridgeIsPathSupported(const char* path) {
diff --git a/libnativebridge/tests/DummyNativeBridge3.cpp b/libnativebridge/tests/DummyNativeBridge3.cpp
index c538fa0..13fce85 100644
--- a/libnativebridge/tests/DummyNativeBridge3.cpp
+++ b/libnativebridge/tests/DummyNativeBridge3.cpp
@@ -68,7 +68,7 @@
   return 0;
 }
 
-extern "C" char* native_bridge3_getError() {
+extern "C" const char* native_bridge3_getError() {
   return nullptr;
 }
 
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index fb95cb6..15fe054 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -308,13 +308,17 @@
     // code is one example) unknown to linker in which  case linker uses anonymous
     // namespace. The second argument specifies the search path for the anonymous
     // namespace which is the library_path of the classloader.
-    if (!is_native_bridge) {
-      initialized_ = android_init_namespaces(public_libraries_.c_str(), library_path);
-      if (!initialized_) {
-        *error_msg = dlerror();
-      }
-    } else {
-      initialized_ = NativeBridgeInitNamespace(public_libraries_.c_str(), library_path);
+    initialized_ = android_init_namespaces(public_libraries_.c_str(),
+                                           is_native_bridge ? nullptr : library_path);
+    if (!initialized_) {
+      *error_msg = dlerror();
+      return false;
+    }
+
+    // and now initialize native bridge namespaces if necessary.
+    if (NativeBridgeInitialized()) {
+      initialized_ = NativeBridgeInitNamespace(public_libraries_.c_str(),
+                                               is_native_bridge ? library_path : nullptr);
       if (!initialized_) {
         *error_msg = NativeBridgeGetError();
       }
@@ -473,14 +477,14 @@
 }
 
 #if defined(__ANDROID__)
+// native_bridge_namespaces are not supported for callers of this function.
+// This function will return nullptr in the case when application is running
+// on native bridge.
 android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
   std::lock_guard<std::mutex> guard(g_namespaces_mutex);
-  // native_bridge_namespaces are not supported for callers of this function.
-  // At the moment this is libwebviewchromium_loader and vulkan.
   NativeLoaderNamespace ns;
   if (g_namespaces->FindNamespaceByClassLoader(env, class_loader, &ns)) {
-    CHECK(ns.is_android_namespace());
-    return ns.get_android_ns();
+    return ns.is_android_namespace() ? ns.get_android_ns() : nullptr;
   }
 
   return nullptr;
diff --git a/libprocessgroup/processgroup.cpp b/libprocessgroup/processgroup.cpp
index 7c15429..eb66727 100644
--- a/libprocessgroup/processgroup.cpp
+++ b/libprocessgroup/processgroup.cpp
@@ -31,12 +31,15 @@
 #include <chrono>
 #include <memory>
 #include <mutex>
+#include <thread>
 
 #include <android-base/logging.h>
 #include <private/android_filesystem_config.h>
 
 #include <processgroup/processgroup.h>
 
+using namespace std::chrono_literals;
+
 // Uncomment line below use memory cgroups for keeping track of (forked) PIDs
 // #define USE_MEMCG 1
 
@@ -288,7 +291,7 @@
     while ((processes = killProcessGroupOnce(uid, initialPid, signal)) > 0) {
         LOG(VERBOSE) << "killed " << processes << " processes for processgroup " << initialPid;
         if (retry > 0) {
-            usleep(5 * 1000); // 5ms
+            std::this_thread::sleep_for(5ms);
             --retry;
         } else {
             LOG(ERROR) << "failed to kill " << processes << " processes for processgroup "
diff --git a/libprocinfo/.clang-format b/libprocinfo/.clang-format
new file mode 100644
index 0000000..b8c6428
--- /dev/null
+++ b/libprocinfo/.clang-format
@@ -0,0 +1,14 @@
+BasedOnStyle: Google
+AllowShortBlocksOnASingleLine: false
+AllowShortFunctionsOnASingleLine: false
+
+ColumnLimit: 100
+CommentPragmas: NOLINT:.*
+DerivePointerAlignment: false
+IndentWidth: 2
+PointerAlignment: Left
+TabWidth: 2
+UseTab: Never
+PenaltyExcessCharacter: 32
+
+Cpp11BracedListStyle: false
diff --git a/libprocinfo/Android.bp b/libprocinfo/Android.bp
new file mode 100644
index 0000000..8e17f1b
--- /dev/null
+++ b/libprocinfo/Android.bp
@@ -0,0 +1,73 @@
+//
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+libprocinfo_cppflags = [
+    "-Wall",
+    "-Wextra",
+    "-Werror",
+]
+
+cc_library {
+    name: "libprocinfo",
+    host_supported: true,
+    srcs: [
+        "process.cpp",
+    ],
+    cppflags: libprocinfo_cppflags,
+
+    local_include_dirs: ["include"],
+    export_include_dirs: ["include"],
+    shared_libs: ["libbase"],
+    target: {
+        darwin: {
+            enabled: false,
+        },
+        windows: {
+            enabled: false,
+        },
+    },
+}
+
+// Tests
+// ------------------------------------------------------------------------------
+cc_test {
+    name: "libprocinfo_test",
+    host_supported: true,
+    srcs: [
+        "process_test.cpp",
+    ],
+    target: {
+        darwin: {
+            enabled: false,
+        },
+        windows: {
+            enabled: false,
+        },
+    },
+
+    cppflags: libprocinfo_cppflags,
+    shared_libs: ["libbase", "libprocinfo"],
+
+    compile_multilib: "both",
+    multilib: {
+        lib32: {
+            suffix: "32",
+        },
+        lib64: {
+            suffix: "64",
+        },
+    },
+}
diff --git a/libprocinfo/include/procinfo/process.h b/libprocinfo/include/procinfo/process.h
new file mode 100644
index 0000000..fb140ff
--- /dev/null
+++ b/libprocinfo/include/procinfo/process.h
@@ -0,0 +1,106 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <type_traits>
+
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/unique_fd.h>
+
+namespace android {
+namespace procinfo {
+
+#if defined(__linux__)
+
+struct ProcessInfo {
+  std::string name;
+  pid_t tid;
+  pid_t pid;
+  pid_t ppid;
+  pid_t tracer;
+  uid_t uid;
+  uid_t gid;
+};
+
+// Parse the contents of /proc/<tid>/status into |process_info|.
+bool GetProcessInfo(pid_t tid, ProcessInfo* process_info);
+
+// Parse the contents of <fd>/status into |process_info|.
+// |fd| should be an fd pointing at a /proc/<pid> directory.
+bool GetProcessInfoFromProcPidFd(int fd, ProcessInfo* process_info);
+
+// Fetch the list of threads from a given process's /proc/<pid> directory.
+// |fd| should be an fd pointing at a /proc/<pid> directory.
+template <typename Collection>
+auto GetProcessTidsFromProcPidFd(int fd, Collection* out) ->
+    typename std::enable_if<sizeof(typename Collection::value_type) >= sizeof(pid_t), bool>::type {
+  out->clear();
+
+  int task_fd = openat(fd, "task", O_DIRECTORY | O_RDONLY | O_CLOEXEC);
+  std::unique_ptr<DIR, int (*)(DIR*)> dir(fdopendir(task_fd), closedir);
+  if (!dir) {
+    PLOG(ERROR) << "failed to open task directory";
+    return false;
+  }
+
+  struct dirent* dent;
+  while ((dent = readdir(dir.get()))) {
+    if (strcmp(dent->d_name, ".") != 0 && strcmp(dent->d_name, "..") != 0) {
+      pid_t tid;
+      if (!android::base::ParseInt(dent->d_name, &tid, 1, std::numeric_limits<pid_t>::max())) {
+        LOG(ERROR) << "failed to parse task id: " << dent->d_name;
+        return false;
+      }
+
+      out->insert(out->end(), tid);
+    }
+  }
+
+  return true;
+}
+
+template <typename Collection>
+auto GetProcessTids(pid_t pid, Collection* out) ->
+    typename std::enable_if<sizeof(typename Collection::value_type) >= sizeof(pid_t), bool>::type {
+  char task_path[PATH_MAX];
+  if (snprintf(task_path, PATH_MAX, "/proc/%d", pid) >= PATH_MAX) {
+    LOG(ERROR) << "task path overflow (pid = " << pid << ")";
+    return false;
+  }
+
+  android::base::unique_fd fd(open(task_path, O_DIRECTORY | O_RDONLY | O_CLOEXEC));
+  if (fd == -1) {
+    PLOG(ERROR) << "failed to open " << task_path;
+    return false;
+  }
+
+  return GetProcessTidsFromProcPidFd(fd.get(), out);
+}
+
+#endif
+
+} /* namespace procinfo */
+} /* namespace android */
diff --git a/libprocinfo/process.cpp b/libprocinfo/process.cpp
new file mode 100644
index 0000000..c513e16
--- /dev/null
+++ b/libprocinfo/process.cpp
@@ -0,0 +1,109 @@
+/*
+ * 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 <procinfo/process.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <android-base/unique_fd.h>
+
+using android::base::unique_fd;
+
+namespace android {
+namespace procinfo {
+
+bool GetProcessInfo(pid_t tid, ProcessInfo* process_info) {
+  char path[PATH_MAX];
+  snprintf(path, sizeof(path), "/proc/%d", tid);
+
+  unique_fd dirfd(open(path, O_DIRECTORY | O_RDONLY));
+  if (dirfd == -1) {
+    PLOG(ERROR) << "failed to open " << path;
+    return false;
+  }
+
+  return GetProcessInfoFromProcPidFd(dirfd.get(), process_info);
+}
+
+bool GetProcessInfoFromProcPidFd(int fd, ProcessInfo* process_info) {
+  int status_fd = openat(fd, "status", O_RDONLY | O_CLOEXEC);
+
+  if (status_fd == -1) {
+    PLOG(ERROR) << "failed to open status fd in GetProcessInfoFromProcPidFd";
+    return false;
+  }
+
+  std::unique_ptr<FILE, decltype(&fclose)> fp(fdopen(status_fd, "r"), fclose);
+  if (!fp) {
+    PLOG(ERROR) << "failed to open status file in GetProcessInfoFromProcPidFd";
+    close(status_fd);
+    return false;
+  }
+
+  int field_bitmap = 0;
+  static constexpr int finished_bitmap = 127;
+  char* line = nullptr;
+  size_t len = 0;
+
+  while (getline(&line, &len, fp.get()) != -1 && field_bitmap != finished_bitmap) {
+    char* tab = strchr(line, '\t');
+    if (tab == nullptr) {
+      continue;
+    }
+
+    size_t header_len = tab - line;
+    std::string header = std::string(line, header_len);
+    if (header == "Name:") {
+      std::string name = line + header_len + 1;
+
+      // line includes the trailing newline.
+      name.pop_back();
+      process_info->name = std::move(name);
+
+      field_bitmap |= 1;
+    } else if (header == "Pid:") {
+      process_info->tid = atoi(tab + 1);
+      field_bitmap |= 2;
+    } else if (header == "Tgid:") {
+      process_info->pid = atoi(tab + 1);
+      field_bitmap |= 4;
+    } else if (header == "PPid:") {
+      process_info->ppid = atoi(tab + 1);
+      field_bitmap |= 8;
+    } else if (header == "TracerPid:") {
+      process_info->tracer = atoi(tab + 1);
+      field_bitmap |= 16;
+    } else if (header == "Uid:") {
+      process_info->uid = atoi(tab + 1);
+      field_bitmap |= 32;
+    } else if (header == "Gid:") {
+      process_info->gid = atoi(tab + 1);
+      field_bitmap |= 64;
+    }
+  }
+
+  free(line);
+  return field_bitmap == finished_bitmap;
+}
+
+} /* namespace procinfo */
+} /* namespace android */
diff --git a/libprocinfo/process_test.cpp b/libprocinfo/process_test.cpp
new file mode 100644
index 0000000..5ffd236
--- /dev/null
+++ b/libprocinfo/process_test.cpp
@@ -0,0 +1,84 @@
+/*
+ * 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 <procinfo/process.h>
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <set>
+#include <thread>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <android-base/stringprintf.h>
+
+#if !defined(__BIONIC__)
+#include <syscall.h>
+static pid_t gettid() {
+  return syscall(__NR_gettid);
+}
+#endif
+
+TEST(process_info, process_info_smoke) {
+  android::procinfo::ProcessInfo self;
+  ASSERT_TRUE(android::procinfo::GetProcessInfo(gettid(), &self));
+  ASSERT_EQ(gettid(), self.tid);
+  ASSERT_EQ(getpid(), self.pid);
+  ASSERT_EQ(getppid(), self.ppid);
+  ASSERT_EQ(getuid(), self.uid);
+  ASSERT_EQ(getgid(), self.gid);
+}
+
+TEST(process_info, process_info_proc_pid_fd_smoke) {
+  android::procinfo::ProcessInfo self;
+  int fd = open(android::base::StringPrintf("/proc/%d", gettid()).c_str(), O_DIRECTORY | O_RDONLY);
+  ASSERT_NE(-1, fd);
+  ASSERT_TRUE(android::procinfo::GetProcessInfoFromProcPidFd(fd, &self));
+
+  // Process name is capped at 15 bytes.
+  ASSERT_EQ("libprocinfo_tes", self.name);
+  ASSERT_EQ(gettid(), self.tid);
+  ASSERT_EQ(getpid(), self.pid);
+  ASSERT_EQ(getppid(), self.ppid);
+  ASSERT_EQ(getuid(), self.uid);
+  ASSERT_EQ(getgid(), self.gid);
+  close(fd);
+}
+
+TEST(process_info, process_tids_smoke) {
+  pid_t main_tid = gettid();
+  std::thread([main_tid]() {
+    pid_t thread_tid = gettid();
+
+    {
+      std::vector<pid_t> vec;
+      ASSERT_TRUE(android::procinfo::GetProcessTids(getpid(), &vec));
+      ASSERT_EQ(1, std::count(vec.begin(), vec.end(), main_tid));
+      ASSERT_EQ(1, std::count(vec.begin(), vec.end(), thread_tid));
+    }
+
+    {
+      std::set<pid_t> set;
+      ASSERT_TRUE(android::procinfo::GetProcessTids(getpid(), &set));
+      ASSERT_EQ(1, std::count(set.begin(), set.end(), main_tid));
+      ASSERT_EQ(1, std::count(set.begin(), set.end(), thread_tid));
+    }
+  }).join();
+}
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index d36cc3f..e6e0276 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -781,7 +781,8 @@
   // Creates a FileWriter for |fd| and prepare to write |entry| to it,
   // guaranteeing that the file descriptor is valid and that there's enough
   // space on the volume to write out the entry completely and that the file
-  // is truncated to the correct length.
+  // is truncated to the correct length (no truncation if |fd| references a
+  // block device).
   //
   // Returns a valid FileWriter on success, |nullptr| if an error occurred.
   static std::unique_ptr<FileWriter> Create(int fd, const ZipEntry* entry) {
@@ -814,13 +815,22 @@
     }
 #endif  // __linux__
 
-    result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset));
-    if (result == -1) {
-      ALOGW("Zip: unable to truncate file to %" PRId64 ": %s",
-            static_cast<int64_t>(declared_length + current_offset), strerror(errno));
+    struct stat sb;
+    if (fstat(fd, &sb) == -1) {
+      ALOGW("Zip: unable to fstat file: %s", strerror(errno));
       return std::unique_ptr<FileWriter>(nullptr);
     }
 
+    // Block device doesn't support ftruncate(2).
+    if (!S_ISBLK(sb.st_mode)) {
+      result = TEMP_FAILURE_RETRY(ftruncate(fd, declared_length + current_offset));
+      if (result == -1) {
+        ALOGW("Zip: unable to truncate file to %" PRId64 ": %s",
+              static_cast<int64_t>(declared_length + current_offset), strerror(errno));
+        return std::unique_ptr<FileWriter>(nullptr);
+      }
+    }
+
     return std::unique_ptr<FileWriter>(new FileWriter(fd, declared_length));
   }
 
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index f08a6cd..d0c693d 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -82,8 +82,14 @@
 static size_t g_printCount;
 static bool g_printItAnyways;
 
+enum helpType {
+    HELP_FALSE,
+    HELP_TRUE,
+    HELP_FORMAT
+};
+
 // if showHelp is set, newline required in fmt statement to transition to usage
-__noreturn static void logcat_panic(bool showHelp, const char *fmt, ...) __printflike(2,3);
+__noreturn static void logcat_panic(enum helpType showHelp, const char *fmt, ...) __printflike(2,3);
 
 static int openLogFile (const char *pathname)
 {
@@ -133,7 +139,7 @@
     g_outFD = openLogFile(g_outputFileName);
 
     if (g_outFD < 0) {
-        logcat_panic(false, "couldn't open output file");
+        logcat_panic(HELP_FALSE, "couldn't open output file");
     }
 
     g_outByteCount = 0;
@@ -196,7 +202,7 @@
             bytesWritten = android_log_printLogLine(g_logformat, g_outFD, &entry);
 
             if (bytesWritten < 0) {
-                logcat_panic(false, "output error");
+                logcat_panic(HELP_FALSE, "output error");
             }
         }
     }
@@ -210,7 +216,6 @@
     }
 
 error:
-    //fprintf (stderr, "Error processing record\n");
     return;
 }
 
@@ -222,7 +227,7 @@
                      dev->printed ? "switch to" : "beginning of",
                      dev->device);
             if (write(g_outFD, buf, strlen(buf)) < 0) {
-                logcat_panic(false, "output error");
+                logcat_panic(HELP_FALSE, "output error");
             }
         }
         dev->printed = true;
@@ -256,18 +261,18 @@
     g_outFD = openLogFile (g_outputFileName);
 
     if (g_outFD < 0) {
-        logcat_panic(false, "couldn't open output file");
+        logcat_panic(HELP_FALSE, "couldn't open output file");
     }
 
     struct stat statbuf;
     if (fstat(g_outFD, &statbuf) == -1) {
         close(g_outFD);
-        logcat_panic(false, "couldn't get output file stat\n");
+        logcat_panic(HELP_FALSE, "couldn't get output file stat\n");
     }
 
     if ((size_t) statbuf.st_size > SIZE_MAX || statbuf.st_size < 0) {
         close(g_outFD);
-        logcat_panic(false, "invalid output file stat\n");
+        logcat_panic(HELP_FALSE, "invalid output file stat\n");
     }
 
     g_outByteCount = statbuf.st_size;
@@ -288,9 +293,10 @@
                     "                  the fileset and continue\n"
                     "  -v <format>, --format=<format>\n"
                     "                  Sets log print format verb and adverbs, where <format> is:\n"
-                    "                    brief long process raw tag thread threadtime time\n"
+                    "                    brief help long process raw tag thread threadtime time\n"
                     "                  and individually flagged modifying adverbs can be added:\n"
-                    "                    color epoch monotonic printable uid usec UTC year zone\n"
+                    "                    color descriptive epoch monotonic printable uid\n"
+                    "                    usec UTC year zone\n"
                     "  -D, --dividers  Print dividers between each log buffer\n"
                     "  -c, --clear     Clear (flush) the entire log and exit\n"
                     "                  if Log to File specified, clear fileset instead\n"
@@ -356,6 +362,40 @@
                    "or defaults to \"threadtime\"\n\n");
 }
 
+static void show_format_help()
+{
+    fprintf(stderr,
+        "-v <format>, --format=<format> options:\n"
+        "  Sets log print format verb and adverbs, where <format> is:\n"
+        "    brief long process raw tag thread threadtime time\n"
+        "  and individually flagged modifying adverbs can be added:\n"
+        "    color descriptive epoch monotonic printable uid usec UTC year zone\n"
+        "\nSingle format verbs:\n"
+        "  brief      — Display priority/tag and PID of the process issuing the message.\n"
+        "  long       — Display all metadata fields, separate messages with blank lines.\n"
+        "  process    — Display PID only.\n"
+        "  raw        — Display the raw log message, with no other metadata fields.\n"
+        "  tag        — Display the priority/tag only.\n"
+        "  threadtime — Display the date, invocation time, priority, tag, and the PID\n"
+        "               and TID of the thread issuing the message. (the default format).\n"
+        "  time       — Display the date, invocation time, priority/tag, and PID of the\n"
+        "             process issuing the message.\n"
+        "\nAdverb modifiers can be used in combination:\n"
+        "  color       — Display in highlighted color to match priority. i.e. \x1B[38;5;231mVERBOSE\n"
+        "                \x1B[38;5;75mDEBUG \x1B[38;5;40mINFO \x1B[38;5;166mWARNING \x1B[38;5;196mERROR FATAL\x1B[0m\n"
+        "  descriptive — events logs only, descriptions from event-log-tags database.\n"
+        "  epoch       — Display time as seconds since Jan 1 1970.\n"
+        "  monotonic   — Display time as cpu seconds since last boot.\n"
+        "  printable   — Ensure that any binary logging content is escaped.\n"
+        "  uid         — If permitted, display the UID or Android ID of logged process.\n"
+        "  usec        — Display time down the microsecond precision.\n"
+        "  UTC         — Display time as UTC.\n"
+        "  year        — Add the year to the displayed time.\n"
+        "  zone        — Add the local timezone to the displayed time.\n"
+        "  \"<zone>\"    — Print using this public named timezone (experimental).\n\n"
+    );
+}
+
 static int setLogFormat(const char * formatString)
 {
     static AndroidLogPrintFormat format;
@@ -418,15 +458,23 @@
     return true;
 }
 
-static void logcat_panic(bool showHelp, const char *fmt, ...)
+static void logcat_panic(enum helpType showHelp, const char *fmt, ...)
 {
     va_list  args;
     va_start(args, fmt);
     vfprintf(stderr, fmt,  args);
     va_end(args);
 
-    if (showHelp) {
+    switch (showHelp) {
+    case HELP_TRUE:
        show_help(getprogname());
+       break;
+    case HELP_FORMAT:
+       show_format_help();
+       break;
+    case HELP_FALSE:
+    default:
+      break;
     }
 
     exit(EXIT_FAILURE);
@@ -616,7 +664,7 @@
                 if (long_options[option_index].name == pid_str) {
                     // ToDo: determine runtime PID_MAX?
                     if (!getSizeTArg(optarg, &pid, 1)) {
-                        logcat_panic(true, "%s %s out of range\n",
+                        logcat_panic(HELP_TRUE, "%s %s out of range\n",
                                      long_options[option_index].name, optarg);
                     }
                     break;
@@ -628,7 +676,7 @@
                     // ToDo: implement API that supports setting a wrap timeout
                     size_t dummy = ANDROID_LOG_WRAP_DEFAULT_TIMEOUT;
                     if (optarg && !getSizeTArg(optarg, &dummy, 1)) {
-                        logcat_panic(true, "%s %s out of range\n",
+                        logcat_panic(HELP_TRUE, "%s %s out of range\n",
                                      long_options[option_index].name, optarg);
                     }
                     if (dummy != ANDROID_LOG_WRAP_DEFAULT_TIMEOUT) {
@@ -675,7 +723,8 @@
                 if (strspn(optarg, "0123456789") != strlen(optarg)) {
                     char *cp = parseTime(tail_time, optarg);
                     if (!cp) {
-                        logcat_panic(false, "-%c \"%s\" not in time format\n",
+                        logcat_panic(HELP_FALSE,
+                                     "-%c \"%s\" not in time format\n",
                                      ret, optarg);
                     }
                     if (*cp) {
@@ -707,7 +756,7 @@
             case 'm': {
                 char *end = NULL;
                 if (!getSizeTArg(optarg, &g_maxCount)) {
-                    logcat_panic(false, "-%c \"%s\" isn't an "
+                    logcat_panic(HELP_FALSE, "-%c \"%s\" isn't an "
                                  "integer greater than zero\n", ret, optarg);
                 }
             }
@@ -780,7 +829,8 @@
                         const char *name = android_log_id_to_name(log_id);
 
                         if (strcmp(name, optarg) != 0) {
-                            logcat_panic(true, "unknown buffer %s\n", optarg);
+                            logcat_panic(HELP_TRUE,
+                                         "unknown buffer %s\n", optarg);
                         }
                         idMask |= (1 << log_id);
                     }
@@ -841,20 +891,27 @@
 
             case 'r':
                 if (!getSizeTArg(optarg, &g_logRotateSizeKBytes, 1)) {
-                    logcat_panic(true, "Invalid parameter %s to -r\n", optarg);
+                    logcat_panic(HELP_TRUE,
+                                 "Invalid parameter \"%s\" to -r\n", optarg);
                 }
             break;
 
             case 'n':
                 if (!getSizeTArg(optarg, &g_maxRotatedLogs, 1)) {
-                    logcat_panic(true, "Invalid parameter %s to -n\n", optarg);
+                    logcat_panic(HELP_TRUE,
+                                 "Invalid parameter \"%s\" to -n\n", optarg);
                 }
             break;
 
             case 'v':
-                err = setLogFormat (optarg);
+                if (!strcmp(optarg, "help") || !strcmp(optarg, "--help")) {
+                    show_format_help();
+                    exit(0);
+                }
+                err = setLogFormat(optarg);
                 if (err < 0) {
-                    logcat_panic(true, "Invalid parameter %s to -v\n", optarg);
+                    logcat_panic(HELP_FORMAT,
+                                 "Invalid parameter \"%s\" to -v\n", optarg);
                 }
                 hasSetLogFormat |= err;
             break;
@@ -932,17 +989,20 @@
                 break;
 
             case ':':
-                logcat_panic(true, "Option -%c needs an argument\n", optopt);
+                logcat_panic(HELP_TRUE,
+                             "Option -%c needs an argument\n", optopt);
                 break;
 
             default:
-                logcat_panic(true, "Unrecognized Option %c\n", optopt);
+                logcat_panic(HELP_TRUE,
+                             "Unrecognized Option %c\n", optopt);
                 break;
         }
     }
 
     if (g_maxCount && got_t) {
-        logcat_panic(true, "Cannot use -m (--max-count) and -t together\n");
+        logcat_panic(HELP_TRUE,
+                     "Cannot use -m (--max-count) and -t together\n");
     }
     if (g_printItAnyways && (!g_regex || !g_maxCount)) {
         // One day it would be nice if --print -v color and --regex <expr>
@@ -968,12 +1028,12 @@
     }
 
     if (g_logRotateSizeKBytes != 0 && g_outputFileName == NULL) {
-        logcat_panic(true, "-r requires -f as well\n");
+        logcat_panic(HELP_TRUE, "-r requires -f as well\n");
     }
 
     if (setId != NULL) {
         if (g_outputFileName == NULL) {
-            logcat_panic(true, "--id='%s' requires -f as well\n", setId);
+            logcat_panic(HELP_TRUE, "--id='%s' requires -f as well\n", setId);
         }
 
         std::string file_name = android::base::StringPrintf("%s.id", g_outputFileName);
@@ -1003,7 +1063,8 @@
     if (forceFilters) {
         err = android_log_addFilterString(g_logformat, forceFilters);
         if (err < 0) {
-            logcat_panic(false, "Invalid filter expression in logcat args\n");
+            logcat_panic(HELP_FALSE,
+                         "Invalid filter expression in logcat args\n");
         }
     } else if (argc == optind) {
         // Add from environment variable
@@ -1013,7 +1074,7 @@
             err = android_log_addFilterString(g_logformat, env_tags_orig);
 
             if (err < 0) {
-                logcat_panic(true,
+                logcat_panic(HELP_TRUE,
                             "Invalid filter expression in ANDROID_LOG_TAGS\n");
             }
         }
@@ -1023,7 +1084,8 @@
             err = android_log_addFilterString(g_logformat, argv[i]);
 
             if (err < 0) {
-                logcat_panic(true, "Invalid filter expression '%s'\n", argv[i]);
+                logcat_panic(HELP_TRUE,
+                             "Invalid filter expression '%s'\n", argv[i]);
             }
         }
     }
@@ -1109,17 +1171,20 @@
     }
     // report any errors in the above loop and exit
     if (openDeviceFail) {
-        logcat_panic(false, "Unable to open log device '%s'\n", openDeviceFail);
+        logcat_panic(HELP_FALSE,
+                     "Unable to open log device '%s'\n", openDeviceFail);
     }
     if (clearFail) {
-        logcat_panic(false, "failed to clear the '%s' log\n", clearFail);
+        logcat_panic(HELP_FALSE,
+                     "failed to clear the '%s' log\n", clearFail);
     }
     if (setSizeFail) {
-        logcat_panic(false, "failed to set the '%s' log size\n", setSizeFail);
+        logcat_panic(HELP_FALSE,
+                     "failed to set the '%s' log size\n", setSizeFail);
     }
     if (getSizeFail) {
-        logcat_panic(false, "failed to get the readable '%s' log size",
-                     getSizeFail);
+        logcat_panic(HELP_FALSE,
+                     "failed to get the readable '%s' log size", getSizeFail);
     }
 
     if (setPruneList) {
@@ -1130,11 +1195,11 @@
         if (asprintf(&buf, "%-*s", (int)(bLen - 1), setPruneList) > 0) {
             buf[len] = '\0';
             if (android_logger_set_prune_list(logger_list, buf, bLen)) {
-                logcat_panic(false, "failed to set the prune list");
+                logcat_panic(HELP_FALSE, "failed to set the prune list");
             }
             free(buf);
         } else {
-            logcat_panic(false, "failed to set the prune list (alloc)");
+            logcat_panic(HELP_FALSE, "failed to set the prune list (alloc)");
         }
     }
 
@@ -1165,7 +1230,7 @@
         }
 
         if (!buf) {
-            logcat_panic(false, "failed to read data");
+            logcat_panic(HELP_FALSE, "failed to read data");
         }
 
         // remove trailing FF
@@ -1217,7 +1282,7 @@
         int ret = android_logger_list_read(logger_list, &log_msg);
 
         if (ret == 0) {
-            logcat_panic(false, "read: unexpected EOF!\n");
+            logcat_panic(HELP_FALSE, "read: unexpected EOF!\n");
         }
 
         if (ret < 0) {
@@ -1226,12 +1291,12 @@
             }
 
             if (ret == -EIO) {
-                logcat_panic(false, "read: unexpected EOF!\n");
+                logcat_panic(HELP_FALSE, "read: unexpected EOF!\n");
             }
             if (ret == -EINVAL) {
-                logcat_panic(false, "read: unexpected length.\n");
+                logcat_panic(HELP_FALSE, "read: unexpected length.\n");
             }
-            logcat_panic(false, "logcat read failure");
+            logcat_panic(HELP_FALSE, "logcat read failure");
         }
 
         for (d = devices; d; d = d->next) {
diff --git a/logcat/tests/logcat_test.cpp b/logcat/tests/logcat_test.cpp
index bc0ea52..8d7c04e 100644
--- a/logcat/tests/logcat_test.cpp
+++ b/logcat/tests/logcat_test.cpp
@@ -25,6 +25,7 @@
 #include <sys/wait.h>
 
 #include <memory>
+#include <string>
 
 #include <gtest/gtest.h>
 #include <log/log.h>
@@ -1235,3 +1236,175 @@
 
     ASSERT_EQ(3, count);
 }
+
+static bool End_to_End(const char* tag, const char* fmt, ...)
+#if defined(__GNUC__)
+    __attribute__((__format__(printf, 2, 3)))
+#endif
+    ;
+
+static bool End_to_End(const char* tag, const char* fmt, ...) {
+    FILE *fp = popen("logcat -v brief -b events -v descriptive -t 100 2>/dev/null", "r");
+    if (!fp) return false;
+
+    char buffer[BIG_BUFFER];
+    va_list ap;
+
+    va_start(ap, fmt);
+    vsnprintf(buffer, sizeof(buffer), fmt, ap);
+    va_end(ap);
+
+    char *str = NULL;
+    asprintf(&str, "I/%s ( %%d):%%c%s%%c", tag, buffer);
+    std::string expect(str);
+    free(str);
+
+    int count = 0;
+    pid_t pid = getpid();
+    std::string lastMatch;
+    int maxMatch = 1;
+    while (fgets(buffer, sizeof(buffer), fp)) {
+        char space;
+        char newline;
+        int p;
+        int ret = sscanf(buffer, expect.c_str(), &p, &space, &newline);
+        if ((ret == 3) && (p == pid) && (space == ' ') && (newline == '\n')) {
+            ++count;
+        } else if ((ret >= maxMatch) && (p == pid) && (count == 0)) {
+            lastMatch = buffer;
+            maxMatch = ret;
+        }
+    }
+
+    pclose(fp);
+
+    if ((count == 0) && (lastMatch.length() > 0)) {
+        // Help us pinpoint where things went wrong ...
+        fprintf(stderr, "Closest match for\n    %s\n  is\n    %s",
+                expect.c_str(), lastMatch.c_str());
+    }
+
+    return count == 1;
+}
+
+TEST(logcat, descriptive) {
+    struct tag {
+        uint32_t tagNo;
+        const char* tagStr;
+    };
+
+    {
+        static const struct tag hhgtg = { 42, "answer" };
+        android_log_event_context ctx(hhgtg.tagNo);
+        static const char theAnswer[] = "what is five by seven";
+        ctx << theAnswer;
+        ctx.write();
+        EXPECT_TRUE(End_to_End(hhgtg.tagStr,
+                               "to life the universe etc=%s", theAnswer));
+    }
+
+    {
+        static const struct tag sync = { 2720, "sync" };
+        static const char id[] = "logcat.decriptive";
+        {
+            android_log_event_context ctx(sync.tagNo);
+            ctx << id << (int32_t)42 << (int32_t)-1 << (int32_t)0;
+            ctx.write();
+            EXPECT_TRUE(End_to_End(sync.tagStr,
+                                   "[id=%s,event=42,source=-1,account=0]",
+                                   id));
+        }
+
+        // Partial match to description
+        {
+            android_log_event_context ctx(sync.tagNo);
+            ctx << id << (int32_t)43 << (int64_t)-1 << (int32_t)0;
+            ctx.write();
+            EXPECT_TRUE(End_to_End(sync.tagStr,
+                                   "[id=%s,event=43,-1,0]",
+                                   id));
+        }
+
+        // Negative Test of End_to_End, ensure it is working
+        {
+            android_log_event_context ctx(sync.tagNo);
+            ctx << id << (int32_t)44 << (int32_t)-1 << (int64_t)0;
+            ctx.write();
+            fprintf(stderr, "Expect a \"Closest match\" message\n");
+            EXPECT_FALSE(End_to_End(sync.tagStr,
+                                    "[id=%s,event=44,source=-1,account=0]",
+                                    id));
+        }
+    }
+
+    {
+        static const struct tag sync = { 2747, "contacts_aggregation" };
+        {
+            android_log_event_context ctx(sync.tagNo);
+            ctx << (uint64_t)30 << (int32_t)2;
+            ctx.write();
+            EXPECT_TRUE(End_to_End(sync.tagStr,
+                                   "[aggregation time=30ms,count=2]"));
+        }
+
+        {
+            android_log_event_context ctx(sync.tagNo);
+            ctx << (uint64_t)31570 << (int32_t)911;
+            ctx.write();
+            EXPECT_TRUE(End_to_End(sync.tagStr,
+                                   "[aggregation time=31.57s,count=911]"));
+        }
+    }
+
+    {
+        static const struct tag sync = { 75000, "sqlite_mem_alarm_current" };
+        {
+            android_log_event_context ctx(sync.tagNo);
+            ctx << (uint32_t)512;
+            ctx.write();
+            EXPECT_TRUE(End_to_End(sync.tagStr, "current=512B"));
+        }
+
+        {
+            android_log_event_context ctx(sync.tagNo);
+            ctx << (uint32_t)3072;
+            ctx.write();
+            EXPECT_TRUE(End_to_End(sync.tagStr, "current=3KB"));
+        }
+
+        {
+            android_log_event_context ctx(sync.tagNo);
+            ctx << (uint32_t)2097152;
+            ctx.write();
+            EXPECT_TRUE(End_to_End(sync.tagStr, "current=2MB"));
+        }
+
+        {
+            android_log_event_context ctx(sync.tagNo);
+            ctx << (uint32_t)2097153;
+            ctx.write();
+            EXPECT_TRUE(End_to_End(sync.tagStr, "current=2097153B"));
+        }
+
+        {
+            android_log_event_context ctx(sync.tagNo);
+            ctx << (uint32_t)1073741824;
+            ctx.write();
+            EXPECT_TRUE(End_to_End(sync.tagStr, "current=1GB"));
+        }
+
+        {
+            android_log_event_context ctx(sync.tagNo);
+            ctx << (uint32_t)3221225472; // 3MB, but on purpose overflowed
+            ctx.write();
+            EXPECT_TRUE(End_to_End(sync.tagStr, "current=-1GB"));
+        }
+    }
+
+    {
+        static const struct tag sync = { 27501, "notification_panel_hidden" };
+        android_log_event_context ctx(sync.tagNo);
+        ctx.write();
+        EXPECT_TRUE(End_to_End(sync.tagStr, ""));
+    }
+}
diff --git a/logd/Android.mk b/logd/Android.mk
index 81637d2..7fe48d7 100644
--- a/logd/Android.mk
+++ b/logd/Android.mk
@@ -29,7 +29,7 @@
     libcutils \
     libbase \
     libpackagelistparser \
-    libminijail
+    libcap
 
 # This is what we want to do:
 #  event_logtags = $(shell \
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index 3811daa..c3ccd84 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -25,6 +25,7 @@
 #include <sys/uio.h>
 #include <syslog.h>
 
+#include <android-base/macros.h>
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
 
@@ -143,7 +144,7 @@
                     iov[2].iov_len = strlen(newline);
                 }
 
-                writev(fdDmesg, iov, sizeof(iov) / sizeof(iov[0]));
+                writev(fdDmesg, iov, arraysize(iov));
                 free(last_str);
                 last_str = NULL;
             }
@@ -165,7 +166,7 @@
             iov[2].iov_base = const_cast<char *>(newline);
             iov[2].iov_len = strlen(newline);
 
-            writev(fdDmesg, iov, sizeof(iov) / sizeof(iov[0]));
+            writev(fdDmesg, iov, arraysize(iov));
         }
     }
 
diff --git a/logd/LogStatistics.cpp b/logd/LogStatistics.cpp
index d68bfee..d4b48ef 100644
--- a/logd/LogStatistics.cpp
+++ b/logd/LogStatistics.cpp
@@ -547,6 +547,7 @@
         }
         spaces += spaces_total;
     }
+    totalSize += sizeOf();
     if (spaces < 0) spaces = 0;
     output += android::base::StringPrintf("%*s%zu", spaces, "", totalSize);
 
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index bfaafa4..1f598af 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -42,8 +42,32 @@
 
     std::unordered_map<TKey, TEntry> map;
 
+    size_t bucket_size() const {
+        size_t count = 0;
+        for (size_t idx = 0; idx < map.bucket_count(); ++idx) {
+            size_t bucket_size = map.bucket_size(idx);
+            if (bucket_size == 0) bucket_size = 1;
+            count += bucket_size;
+        }
+        float load_factor = map.max_load_factor();
+        if (load_factor < 1.0) return count;
+        return count * load_factor;
+    }
+
+    static const size_t unordered_map_per_entry_overhead = sizeof(void*);
+    static const size_t unordered_map_bucket_overhead = sizeof(void*);
+
 public:
 
+    size_t size() const { return map.size(); }
+
+    // Estimate unordered_map memory usage.
+    size_t sizeOf() const {
+        return sizeof(*this) +
+               (size() * (sizeof(TEntry) + unordered_map_per_entry_overhead)) +
+               (bucket_size() * sizeof(size_t) + unordered_map_bucket_overhead);
+    }
+
     typedef typename std::unordered_map<TKey, TEntry>::iterator iterator;
     typedef typename std::unordered_map<TKey, TEntry>::const_iterator const_iterator;
 
@@ -155,6 +179,7 @@
         }
         return output;
     }
+
 };
 
 namespace EntryBaseConstants {
@@ -472,7 +497,30 @@
     // security tag list
     tagTable_t securityTagTable;
 
+    size_t sizeOf() const {
+        size_t size = sizeof(*this) + pidTable.sizeOf() + tidTable.sizeOf() +
+                      tagTable.sizeOf() + securityTagTable.sizeOf() +
+                      (pidTable.size() * sizeof(pidTable_t::iterator)) +
+                      (tagTable.size() * sizeof(tagTable_t::iterator));
+        for(auto it : pidTable) {
+            const char* name = it.second.getName();
+            if (name) size += strlen(name) + 1;
+        }
+        for(auto it : tidTable) {
+            const char* name = it.second.getName();
+            if (name) size += strlen(name) + 1;
+        }
+        log_id_for_each(id) {
+            size += uidTable[id].sizeOf();
+            size += uidTable[id].size() * sizeof(uidTable_t::iterator);
+            size += pidSystemTable[id].sizeOf();
+            size += pidSystemTable[id].size() * sizeof(pidSystemTable_t::iterator);
+        }
+        return size;
+    }
+
 public:
+
     LogStatistics();
 
     void enableStatistics() { enable = true; }
diff --git a/logd/logd.rc b/logd/logd.rc
index 31ed4df..54349dd 100644
--- a/logd/logd.rc
+++ b/logd/logd.rc
@@ -2,10 +2,15 @@
     socket logd stream 0666 logd logd
     socket logdr seqpacket 0666 logd logd
     socket logdw dgram 0222 logd logd
-    group root system readproc
+    file /proc/kmsg r
+    file /dev/kmsg w
+    user logd
+    group logd system readproc
     writepid /dev/cpuset/system-background/tasks
 
 service logd-reinit /system/bin/logd --reinit
     oneshot
     disabled
+    user logd
+    group logd
     writepid /dev/cpuset/system-background/tasks
diff --git a/logd/main.cpp b/logd/main.cpp
index 0cb26dc..d698976 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -39,13 +39,12 @@
 #include <android-base/macros.h>
 #include <cutils/properties.h>
 #include <cutils/sched_policy.h>
+#include <cutils/files.h>
 #include <cutils/sockets.h>
-#include <libminijail.h>
 #include <log/event_tag_map.h>
 #include <packagelistparser/packagelistparser.h>
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
-#include <scoped_minijail.h>
 #include <utils/threads.h>
 
 #include "CommandListener.h"
@@ -90,29 +89,81 @@
 //    logd
 //
 
-static int drop_privs() {
+static int drop_privs(bool klogd, bool auditd) {
+    // Tricky, if ro.build.type is "eng" then this is true because of the
+    // side effect that ro.debuggable == 1 as well, else it is false.
+    bool eng = __android_logger_property_get_bool("ro.build.type", BOOL_DEFAULT_FALSE);
+
     struct sched_param param;
     memset(&param, 0, sizeof(param));
 
     if (set_sched_policy(0, SP_BACKGROUND) < 0) {
-        return -1;
+        android::prdebug("failed to set background scheduling policy");
+        if (!eng) return -1;
     }
 
     if (sched_setscheduler((pid_t) 0, SCHED_BATCH, &param) < 0) {
-        return -1;
+        android::prdebug("failed to set batch scheduler");
+        if (!eng) return -1;
     }
 
     if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) {
+        android::prdebug("failed to set background cgroup");
+        if (!eng) return -1;
+    }
+
+    if (!eng && (prctl(PR_SET_DUMPABLE, 0) < 0)) {
+        android::prdebug("failed to clear PR_SET_DUMPABLE");
         return -1;
     }
 
+    if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
+        android::prdebug("failed to set PR_SET_KEEPCAPS");
+        if (!eng) 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, // must be first for below
+        klogd ? CAP_SYSLOG : CAP_SETGID,
+        auditd ? CAP_AUDIT_CONTROL : CAP_SETGID
+    };
+    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) {
+        android::prdebug("failed to set CAP_SETGID, CAP_SYSLOG or CAP_AUDIT_CONTROL (%d)", errno);
+        if (!eng) return -1;
+    }
+
     gid_t groups[] = { AID_READPROC };
-    ScopedMinijail j(minijail_new());
-    minijail_set_supplementary_gids(j.get(), arraysize(groups), groups);
-    minijail_change_uid(j.get(), AID_LOGD);
-    minijail_change_gid(j.get(), AID_LOGD);
-    minijail_use_caps(j.get(), CAP_TO_MASK(CAP_SYSLOG) | CAP_TO_MASK(CAP_AUDIT_CONTROL));
-    minijail_enter(j.get());
+
+    if (setgroups(arraysize(groups), groups) == -1) {
+        android::prdebug("failed to set AID_READPROC groups");
+        if (!eng) return -1;
+    }
+
+    if (setgid(AID_LOGD) != 0) {
+        android::prdebug("failed to set AID_LOGD gid");
+        if (!eng) return -1;
+    }
+
+    if (setuid(AID_LOGD) != 0) {
+        android::prdebug("failed to set AID_LOGD uid");
+        if (!eng) return -1;
+    }
+
+    if (cap_set_flag(caps.get(), CAP_PERMITTED, 1, cap_value, CAP_CLEAR) < 0) return -1;
+    if (cap_set_flag(caps.get(), CAP_EFFECTIVE, 1, cap_value, CAP_CLEAR) < 0) return -1;
+    if (cap_set_proc(caps.get()) < 0) {
+        android::prdebug("failed to clear CAP_SETGID (%d)", errno);
+        if (!eng) return -1;
+    }
+
     return 0;
 }
 
@@ -184,11 +235,16 @@
     set_sched_policy(0, SP_BACKGROUND);
     setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND);
 
-    // If we are AID_ROOT, we should drop to AID_SYSTEM, if we are anything
-    // else, we have even lesser privileges and accept our fate. Not worth
-    // checking for error returns setting this thread's privileges.
-    (void)setgid(AID_SYSTEM);
-    (void)setuid(AID_SYSTEM);
+    cap_t caps = cap_init();
+    (void)cap_clear(caps);
+    (void)cap_set_proc(caps);
+    (void)cap_free(caps);
+
+    // If we are AID_ROOT, we should drop to AID_LOGD+AID_SYSTEM, if we are
+    // anything else, we have even lesser privileges and accept our fate. Not
+    // worth checking for error returns setting this thread's privileges.
+    (void)setgid(AID_SYSTEM); // readonly access to /data/system/packages.list
+    (void)setuid(AID_LOGD);   // access to everything logd.
 
     while (reinit_running && !sem_wait(&reinit) && reinit_running) {
 
@@ -306,6 +362,39 @@
     }
 }
 
+static int issueReinit() {
+    cap_t caps = cap_init();
+    (void)cap_clear(caps);
+    (void)cap_set_proc(caps);
+    (void)cap_free(caps);
+
+    int sock = TEMP_FAILURE_RETRY(
+        socket_local_client("logd",
+                            ANDROID_SOCKET_NAMESPACE_RESERVED,
+                            SOCK_STREAM));
+    if (sock < 0) return -errno;
+
+    static const char reinitStr[] = "reinit";
+    ssize_t ret = TEMP_FAILURE_RETRY(write(sock, reinitStr, sizeof(reinitStr)));
+    if (ret < 0) return -errno;
+
+    struct pollfd p;
+    memset(&p, 0, sizeof(p));
+    p.fd = sock;
+    p.events = POLLIN;
+    ret = TEMP_FAILURE_RETRY(poll(&p, 1, 1000));
+    if (ret < 0) return -errno;
+    if ((ret == 0) || !(p.revents & POLLIN)) return -ETIME;
+
+    static const char success[] = "success";
+    char buffer[sizeof(success) - 1];
+    memset(buffer, 0, sizeof(buffer));
+    ret = TEMP_FAILURE_RETRY(read(sock, buffer, sizeof(buffer)));
+    if (ret < 0) return -errno;
+
+    return strncmp(buffer, success, sizeof(success) - 1) != 0;
+}
+
 // Foreground waits for exit of the main persistent threads
 // that are started here. The threads are created to manage
 // UNIX domain client sockets for writing, reading and
@@ -313,6 +402,17 @@
 // logging plugins like auditd and restart control. Additional
 // transitory per-client threads are created for each reader.
 int main(int argc, char *argv[]) {
+    // issue reinit command. KISS argument parsing.
+    if ((argc > 1) && argv[1] && !strcmp(argv[1], "--reinit")) {
+        return issueReinit();
+    }
+
+    static const char dev_kmsg[] = "/dev/kmsg";
+    fdDmesg = android_get_control_file(dev_kmsg);
+    if (fdDmesg < 0) {
+        fdDmesg = TEMP_FAILURE_RETRY(open(dev_kmsg, O_WRONLY | O_CLOEXEC));
+    }
+
     int fdPmesg = -1;
     bool klogd = __android_logger_property_get_bool("logd.kernel",
                                                     BOOL_DEFAULT_TRUE |
@@ -320,43 +420,13 @@
                                                     BOOL_DEFAULT_FLAG_ENG |
                                                     BOOL_DEFAULT_FLAG_SVELTE);
     if (klogd) {
-        fdPmesg = open("/proc/kmsg", O_RDONLY | O_NDELAY);
-    }
-    fdDmesg = open("/dev/kmsg", O_WRONLY);
-
-    // issue reinit command. KISS argument parsing.
-    if ((argc > 1) && argv[1] && !strcmp(argv[1], "--reinit")) {
-        int sock = TEMP_FAILURE_RETRY(
-            socket_local_client("logd",
-                                ANDROID_SOCKET_NAMESPACE_RESERVED,
-                                SOCK_STREAM));
-        if (sock < 0) {
-            return -errno;
+        static const char proc_kmsg[] = "/proc/kmsg";
+        fdPmesg = android_get_control_file(proc_kmsg);
+        if (fdPmesg < 0) {
+            fdPmesg = TEMP_FAILURE_RETRY(open(proc_kmsg,
+                                              O_RDONLY | O_NDELAY | O_CLOEXEC));
         }
-        static const char reinit[] = "reinit";
-        ssize_t ret = TEMP_FAILURE_RETRY(write(sock, reinit, sizeof(reinit)));
-        if (ret < 0) {
-            return -errno;
-        }
-        struct pollfd p;
-        memset(&p, 0, sizeof(p));
-        p.fd = sock;
-        p.events = POLLIN;
-        ret = TEMP_FAILURE_RETRY(poll(&p, 1, 1000));
-        if (ret < 0) {
-            return -errno;
-        }
-        if ((ret == 0) || !(p.revents & POLLIN)) {
-            return -ETIME;
-        }
-        static const char success[] = "success";
-        char buffer[sizeof(success) - 1];
-        memset(buffer, 0, sizeof(buffer));
-        ret = TEMP_FAILURE_RETRY(read(sock, buffer, sizeof(buffer)));
-        if (ret < 0) {
-            return -errno;
-        }
-        return strncmp(buffer, success, sizeof(success) - 1) != 0;
+        if (fdPmesg < 0) android::prdebug("Failed to open %s\n", proc_kmsg);
     }
 
     // Reinit Thread
@@ -381,7 +451,10 @@
         pthread_attr_destroy(&attr);
     }
 
-    if (drop_privs() != 0) {
+    bool auditd = __android_logger_property_get_bool("logd.auditd",
+                                                     BOOL_DEFAULT_TRUE |
+                                                     BOOL_DEFAULT_FLAG_PERSIST);
+    if (drop_privs(klogd, auditd) != 0) {
         return -1;
     }
 
@@ -436,9 +509,6 @@
     // initiated log messages. New log entries are added to LogBuffer
     // and LogReader is notified to send updates to connected clients.
 
-    bool auditd = __android_logger_property_get_bool("logd.auditd",
-                                                     BOOL_DEFAULT_TRUE |
-                                                     BOOL_DEFAULT_FLAG_PERSIST);
     LogAudit *al = NULL;
     if (auditd) {
         al = new LogAudit(logBuf, reader,
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index e0a4cc3..254a3f8 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -23,6 +23,7 @@
 
 #include <string>
 
+#include <android-base/macros.h>
 #include <android-base/stringprintf.h>
 #include <cutils/sockets.h>
 #include <gtest/gtest.h>
@@ -351,7 +352,7 @@
         "/dev/log/system", "/dev/log_system",
     };
 
-    for (unsigned int i = 0; i < (sizeof(loggers) / sizeof(loggers[0])); ++i) {
+    for (unsigned int i = 0; i < arraysize(loggers); ++i) {
         fd = open(loggers[i], O_RDONLY);
         if (fd < 0) {
             continue;
@@ -434,12 +435,12 @@
     static const unsigned int log_latency = 4;
     static const unsigned int log_delay = 5;
 
-    unsigned long ns[sizeof(benchmarks) / sizeof(benchmarks[0])];
+    unsigned long ns[arraysize(benchmarks)];
 
     memset(ns, 0, sizeof(ns));
 
     while (fgets(buffer, sizeof(buffer), fp)) {
-        for (unsigned i = 0; i < sizeof(ns) / sizeof(ns[0]); ++i) {
+        for (unsigned i = 0; i < arraysize(ns); ++i) {
             char *cp = strstr(buffer, benchmarks[i]);
             if (!cp) {
                 continue;
@@ -470,7 +471,7 @@
 
     EXPECT_GE(20000000UL, ns[log_delay]); // 10500289 user
 
-    for (unsigned i = 0; i < sizeof(ns) / sizeof(ns[0]); ++i) {
+    for (unsigned i = 0; i < arraysize(ns); ++i) {
         EXPECT_NE(0UL, ns[i]);
     }
 
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 50ee110..4e766bb 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -295,6 +295,10 @@
     # Make sure /sys/kernel/debug (if present) is labeled properly
     restorecon_recursive /sys/kernel/debug
 
+    # On systems with tracefs, tracing is a separate mount, so make sure
+    # it too is correctly labeled
+    restorecon_recursive /sys/kernel/debug/tracing
+
     # We chown/chmod /cache again so because mount is run as root + defaults
     chown system cache /cache
     chmod 0770 /cache
@@ -562,7 +566,7 @@
 
 on nonencrypted
     # A/B update verifier that marks a successful boot.
-    exec - root -- /system/bin/update_verifier nonencrypted
+    exec - root cache -- /system/bin/update_verifier nonencrypted
     class_start main
     class_start late_start
 
@@ -585,12 +589,12 @@
 
 on property:vold.decrypt=trigger_restart_min_framework
     # A/B update verifier that marks a successful boot.
-    exec - root -- /system/bin/update_verifier trigger_restart_min_framework
+    exec - root cache -- /system/bin/update_verifier trigger_restart_min_framework
     class_start main
 
 on property:vold.decrypt=trigger_restart_framework
     # A/B update verifier that marks a successful boot.
-    exec - root -- /system/bin/update_verifier trigger_restart_framework
+    exec - root cache -- /system/bin/update_verifier trigger_restart_framework
     class_start main
     class_start late_start
 
@@ -647,13 +651,3 @@
 service flash_recovery /system/bin/install-recovery.sh
     class main
     oneshot
-
-service hwservicemanager /system/bin/hwservicemanager
-    user system
-    disabled
-    group system readproc
-    critical
-    writepid /dev/cpuset/system-background/tasks
-
-on property:hwservicemanager.ready=true
-    class_start hal
diff --git a/rootdir/init.zygote32.rc b/rootdir/init.zygote32.rc
index 807f9bc..bfddcfa 100644
--- a/rootdir/init.zygote32.rc
+++ b/rootdir/init.zygote32.rc
@@ -1,6 +1,8 @@
 service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
     class main
     priority -20
+    user root
+    group root readproc
     socket zygote stream 660 root system
     onrestart write /sys/android_power/request_state wake
     onrestart write /sys/power/state on
diff --git a/rootdir/init.zygote32_64.rc b/rootdir/init.zygote32_64.rc
index 3bfa0af..1bbb007 100644
--- a/rootdir/init.zygote32_64.rc
+++ b/rootdir/init.zygote32_64.rc
@@ -1,6 +1,8 @@
 service zygote /system/bin/app_process32 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
     class main
     priority -20
+    user root
+    group root readproc
     socket zygote stream 660 root system
     onrestart write /sys/android_power/request_state wake
     onrestart write /sys/power/state on
@@ -13,6 +15,8 @@
 service zygote_secondary /system/bin/app_process64 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
     class main
     priority -20
+    user root
+    group root readproc
     socket zygote_secondary stream 660 root system
     onrestart restart zygote
     writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
diff --git a/rootdir/init.zygote64.rc b/rootdir/init.zygote64.rc
index 13ffd7e..6742127 100644
--- a/rootdir/init.zygote64.rc
+++ b/rootdir/init.zygote64.rc
@@ -1,6 +1,8 @@
 service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
     class main
     priority -20
+    user root
+    group root readproc
     socket zygote stream 660 root system
     onrestart write /sys/android_power/request_state wake
     onrestart write /sys/power/state on
diff --git a/rootdir/init.zygote64_32.rc b/rootdir/init.zygote64_32.rc
index 5640490..81a7609 100644
--- a/rootdir/init.zygote64_32.rc
+++ b/rootdir/init.zygote64_32.rc
@@ -1,6 +1,8 @@
 service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
     class main
     priority -20
+    user root
+    group root readproc
     socket zygote stream 660 root system
     onrestart write /sys/android_power/request_state wake
     onrestart write /sys/power/state on
@@ -13,6 +15,8 @@
 service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
     class main
     priority -20
+    user root
+    group root readproc
     socket zygote_secondary stream 660 root system
     onrestart restart zygote
     writepid /dev/cpuset/foreground/tasks /dev/stune/foreground/tasks
diff --git a/toolbox/newfs_msdos.c b/toolbox/newfs_msdos.c
index 27ea9e8..d7047e2 100644
--- a/toolbox/newfs_msdos.c
+++ b/toolbox/newfs_msdos.c
@@ -741,6 +741,7 @@
                 exit(1);
             }
         }
+        free(img);
     }
     return 0;
 }