Merge "adb server: don't close stale fd when TCP transport is closed"
diff --git a/adb/Android.mk b/adb/Android.mk
index f030041..7977009 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -238,12 +238,11 @@
     -D_GNU_SOURCE \
     -Wno-deprecated-declarations \
 
-ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
-LOCAL_CFLAGS += -DALLOW_ADBD_ROOT=1
-endif
+LOCAL_CFLAGS += -DALLOW_ADBD_NO_AUTH=$(if $(filter userdebug eng,$(TARGET_BUILD_VARIANT)),1,0)
 
 ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
 LOCAL_CFLAGS += -DALLOW_ADBD_DISABLE_VERITY=1
+LOCAL_CFLAGS += -DALLOW_ADBD_ROOT=1
 endif
 
 LOCAL_MODULE := adbd
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 9af1586..2e59634 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -431,9 +431,9 @@
 
         parse_banner(reinterpret_cast<const char*>(p->data), t);
 
-        if (HOST || !auth_enabled) {
+        if (HOST || !auth_required) {
             handle_online(t);
-            if(!HOST) send_connect(t);
+            if (!HOST) send_connect(t);
         } else {
             send_auth_request(t);
         }
@@ -835,6 +835,14 @@
     return 0;
 }
 
+#if ADB_HOST
+static int SendOkay(int fd, const std::string& s) {
+    SendOkay(fd);
+    SendProtocolString(fd, s);
+    return 0;
+}
+#endif
+
 int handle_host_request(const char* service, TransportType type,
                         const char* serial, int reply_fd, asocket* s) {
     if (strcmp(service, "kill") == 0) {
@@ -845,7 +853,6 @@
     }
 
 #if ADB_HOST
-    atransport *transport = NULL;
     // "transport:" is used for switching transport with a specified serial number
     // "transport-usb:" is used for switching transport to the only USB transport
     // "transport-local:" is used for switching transport to the only local transport
@@ -864,11 +871,10 @@
             serial = service;
         }
 
-        std::string error_msg = "unknown failure";
-        transport = acquire_one_transport(kCsAny, type, serial, &error_msg);
-
-        if (transport) {
-            s->transport = transport;
+        std::string error_msg;
+        atransport* t = acquire_one_transport(kCsAny, type, serial, &error_msg);
+        if (t != nullptr) {
+            s->transport = t;
             SendOkay(reply_fd);
         } else {
             SendFail(reply_fd, error_msg);
@@ -883,9 +889,7 @@
             D("Getting device list...\n");
             std::string device_list = list_transports(long_listing);
             D("Sending device list...\n");
-            SendOkay(reply_fd);
-            SendProtocolString(reply_fd, device_list);
-            return 0;
+            return SendOkay(reply_fd, device_list);
         }
         return 1;
     }
@@ -905,8 +909,7 @@
                 snprintf(hostbuf, sizeof(hostbuf) - 1, "%s:5555", serial);
                 serial = hostbuf;
             }
-            atransport *t = find_transport(serial);
-
+            atransport* t = find_transport(serial);
             if (t) {
                 unregister_transport(t);
             } else {
@@ -914,52 +917,38 @@
             }
         }
 
-        SendOkay(reply_fd);
-        SendProtocolString(reply_fd, buffer);
-        return 0;
+        return SendOkay(reply_fd, buffer);
     }
 
     // returns our value for ADB_SERVER_VERSION
     if (!strcmp(service, "version")) {
-        SendOkay(reply_fd);
-        SendProtocolString(reply_fd, android::base::StringPrintf("%04x", ADB_SERVER_VERSION));
-        return 0;
+        return SendOkay(reply_fd, android::base::StringPrintf("%04x", ADB_SERVER_VERSION));
     }
 
-    if(!strncmp(service,"get-serialno",strlen("get-serialno"))) {
-        const char *out = "unknown";
-        transport = acquire_one_transport(kCsAny, type, serial, NULL);
-        if (transport && transport->serial) {
-            out = transport->serial;
-        }
-        SendOkay(reply_fd);
-        SendProtocolString(reply_fd, out);
-        return 0;
+    // These always report "unknown" rather than the actual error, for scripts.
+    if (!strcmp(service, "get-serialno")) {
+        std::string ignored;
+        atransport* t = acquire_one_transport(kCsAny, type, serial, &ignored);
+        return SendOkay(reply_fd, (t && t->serial) ? t->serial : "unknown");
     }
-    if(!strncmp(service,"get-devpath",strlen("get-devpath"))) {
-        const char *out = "unknown";
-        transport = acquire_one_transport(kCsAny, type, serial, NULL);
-        if (transport && transport->devpath) {
-            out = transport->devpath;
-        }
-        SendOkay(reply_fd);
-        SendProtocolString(reply_fd, out);
-        return 0;
+    if (!strcmp(service, "get-devpath")) {
+        std::string ignored;
+        atransport* t = acquire_one_transport(kCsAny, type, serial, &ignored);
+        return SendOkay(reply_fd, (t && t->devpath) ? t->devpath : "unknown");
     }
+    if (!strcmp(service, "get-state")) {
+        std::string ignored;
+        atransport* t = acquire_one_transport(kCsAny, type, serial, &ignored);
+        return SendOkay(reply_fd, t ? t->connection_state_name() : "unknown");
+    }
+
     // indicates a new emulator instance has started
-    if (!strncmp(service,"emulator:",9)) {
+    if (!strncmp(service, "emulator:", 9)) {
         int  port = atoi(service+9);
         local_connect(port);
         /* we don't even need to send a reply */
         return 0;
     }
-
-    if(!strncmp(service,"get-state",strlen("get-state"))) {
-        transport = acquire_one_transport(kCsAny, type, serial, NULL);
-        SendOkay(reply_fd);
-        SendProtocolString(reply_fd, transport->connection_state_name());
-        return 0;
-    }
 #endif // ADB_HOST
 
     int ret = handle_forward_request(service, type, serial, reply_fd);
diff --git a/adb/adb_auth.cpp b/adb/adb_auth.cpp
index dc01825..cff26d6 100644
--- a/adb/adb_auth.cpp
+++ b/adb/adb_auth.cpp
@@ -28,7 +28,7 @@
 #include "adb.h"
 #include "transport.h"
 
-int auth_enabled = 0;
+bool auth_required = true;
 
 void send_auth_request(atransport *t)
 {
diff --git a/adb/adb_auth.h b/adb/adb_auth.h
index 1e1978d..a13604a 100644
--- a/adb/adb_auth.h
+++ b/adb/adb_auth.h
@@ -19,7 +19,7 @@
 
 #include "adb.h"
 
-extern int auth_enabled;
+extern bool auth_required;
 
 int adb_auth_keygen(const char* filename);
 void adb_auth_verified(atransport *t);
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 7fbca31..4adac37 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -110,7 +110,6 @@
         "                                 ('-a' means copy timestamp and mode)\n"
         "  adb sync [ <directory> ]     - copy host->device only if changed\n"
         "                                 (-l means list but don't copy)\n"
-        "                                 (see 'adb help all')\n"
         "  adb shell                    - run remote shell interactively\n"
         "  adb shell <command>          - run remote shell command\n"
         "  adb emu <command>            - run emulator console command\n"
@@ -202,11 +201,12 @@
         "  adb reboot sideload          - reboots the device into the sideload mode in recovery program (adb root required).\n"
         "  adb reboot sideload-auto-reboot\n"
         "                               - reboots into the sideload mode, then reboots automatically after the sideload regardless of the result.\n"
-        "  adb reboot-bootloader        - reboots the device into the bootloader\n"
+        "  adb sideload <file>          - sideloads the given package\n"
         "  adb root                     - restarts the adbd daemon with root permissions\n"
         "  adb unroot                   - restarts the adbd daemon without root permissions\n"
         "  adb usb                      - restarts the adbd daemon listening on USB\n"
         "  adb tcpip <port>             - restarts the adbd daemon listening on TCP on the specified port\n"
+        "\n"
         "networking:\n"
         "  adb ppp <tty> [parameters]   - Run PPP over USB.\n"
         " Note: you should not automatically start a PPP connection.\n"
@@ -221,7 +221,7 @@
         "  - If it is \"system\", \"vendor\", \"oem\" or \"data\", only the corresponding partition\n"
         "    is updated.\n"
         "\n"
-        "environmental variables:\n"
+        "environment variables:\n"
         "  ADB_TRACE                    - Print debug information. A comma separated list of the following values\n"
         "                                 1 or all, adb, sockets, packets, rwx, usb, sync, sysdeps, transport, jdwp\n"
         "  ANDROID_SERIAL               - The serial number to connect to. -s takes priority over this if given.\n"
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index 78ab3f6..157c97b 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -125,11 +125,12 @@
     // descriptor will always be open.
     adbd_cloexec_auth_socket();
 
-    auth_enabled = property_get_bool("ro.adb.secure", 0) != 0;
-    if (auth_enabled) {
-        adbd_auth_init();
+    if (ALLOW_ADBD_NO_AUTH && property_get_bool("ro.adb.secure", 0) == 0) {
+        auth_required = false;
     }
 
+    adbd_auth_init();
+
     // Our external storage path may be different than apps, since
     // we aren't able to bind mount after dropping root.
     const char* adb_external_storage = getenv("ADB_EXTERNAL_STORAGE");
@@ -171,7 +172,7 @@
 
         D("Local port disabled\n");
     } else {
-        if ((root_seclabel != nullptr) && (is_selinux_enabled() > 0)) {
+        if (root_seclabel != nullptr) {
             if (setcon(root_seclabel) < 0) {
                 LOG(FATAL) << "Could not set selinux context";
             }
diff --git a/adb/services.cpp b/adb/services.cpp
index 2fd75ab..227f22a 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -528,7 +528,7 @@
     std::string error_msg = "unknown error";
     atransport* t = acquire_one_transport(sinfo->state, sinfo->transport_type, sinfo->serial,
                                           &error_msg);
-    if (t != 0) {
+    if (t != nullptr) {
         SendOkay(fd);
     } else {
         SendFail(fd, error_msg);
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 379c702..274449b 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -731,12 +731,12 @@
     int ambiguous = 0;
 
 retry:
-    if (error_out) *error_out = android::base::StringPrintf("device '%s' not found", serial);
+    *error_out = serial ? android::base::StringPrintf("device '%s' not found", serial) : "no devices found";
 
     adb_mutex_lock(&transport_lock);
     for (auto t : transport_list) {
         if (t->connection_state == kCsNoPerm) {
-            if (error_out) *error_out = "insufficient permissions for device";
+            *error_out = "insufficient permissions for device";
             continue;
         }
 
@@ -748,7 +748,7 @@
                 qual_match(serial, "model:", t->model, true) ||
                 qual_match(serial, "device:", t->device, false)) {
                 if (result) {
-                    if (error_out) *error_out = "more than one device";
+                    *error_out = "more than one device";
                     ambiguous = 1;
                     result = NULL;
                     break;
@@ -758,7 +758,7 @@
         } else {
             if (type == kTransportUsb && t->type == kTransportUsb) {
                 if (result) {
-                    if (error_out) *error_out = "more than one device";
+                    *error_out = "more than one device";
                     ambiguous = 1;
                     result = NULL;
                     break;
@@ -766,7 +766,7 @@
                 result = t;
             } else if (type == kTransportLocal && t->type == kTransportLocal) {
                 if (result) {
-                    if (error_out) *error_out = "more than one emulator";
+                    *error_out = "more than one emulator";
                     ambiguous = 1;
                     result = NULL;
                     break;
@@ -774,7 +774,7 @@
                 result = t;
             } else if (type == kTransportAny) {
                 if (result) {
-                    if (error_out) *error_out = "more than one device/emulator";
+                    *error_out = "more than one device/emulator";
                     ambiguous = 1;
                     result = NULL;
                     break;
@@ -787,33 +787,31 @@
 
     if (result) {
         if (result->connection_state == kCsUnauthorized) {
-            if (error_out) {
-                *error_out = "device unauthorized.\n";
-                char* ADB_VENDOR_KEYS = getenv("ADB_VENDOR_KEYS");
-                *error_out += "This adbd's $ADB_VENDOR_KEYS is ";
-                *error_out += ADB_VENDOR_KEYS ? ADB_VENDOR_KEYS : "not set";
-                *error_out += "; try 'adb kill-server' if that seems wrong.\n";
-                *error_out += "Otherwise check for a confirmation dialog on your device.";
-            }
+            *error_out = "device unauthorized.\n";
+            char* ADB_VENDOR_KEYS = getenv("ADB_VENDOR_KEYS");
+            *error_out += "This adbd's $ADB_VENDOR_KEYS is ";
+            *error_out += ADB_VENDOR_KEYS ? ADB_VENDOR_KEYS : "not set";
+            *error_out += "; try 'adb kill-server' if that seems wrong.\n";
+            *error_out += "Otherwise check for a confirmation dialog on your device.";
             result = NULL;
         }
 
         /* offline devices are ignored -- they are either being born or dying */
         if (result && result->connection_state == kCsOffline) {
-            if (error_out) *error_out = "device offline";
+            *error_out = "device offline";
             result = NULL;
         }
 
         /* check for required connection state */
         if (result && state != kCsAny && result->connection_state != state) {
-            if (error_out) *error_out = "invalid device state";
+            *error_out = "invalid device state";
             result = NULL;
         }
     }
 
     if (result) {
         /* found one that we can take */
-        if (error_out) *error_out = "success";
+        *error_out = "success";
     } else if (state != kCsAny && (serial || !ambiguous)) {
         adb_sleep_ms(1000);
         goto retry;
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index b84a4e5..26d6389 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -134,8 +134,6 @@
   return fields == 7 ? 0 : -1;
 }
 
-static int selinux_enabled;
-
 /*
  * Corresponds with debugger_action_t enum type in
  * include/cutils/debugger.h.
@@ -153,9 +151,6 @@
   const char *perm;
   bool allowed = false;
 
-  if (selinux_enabled <= 0)
-    return true;
-
   if (action <= 0 || action >= (sizeof(debuggerd_perms)/sizeof(debuggerd_perms[0]))) {
     ALOGE("SELinux:  No permission defined for debugger action %d", action);
     return false;
@@ -589,7 +584,6 @@
 int main(int argc, char** argv) {
   union selinux_callback cb;
   if (argc == 1) {
-    selinux_enabled = is_selinux_enabled();
     cb.func_log = selinux_log_callback;
     selinux_set_callback(SELINUX_CB_LOG, cb);
     return do_server();
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index 66a470a..ce8e15f 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -21,7 +21,7 @@
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/../mkbootimg \
   $(LOCAL_PATH)/../../extras/ext4_utils \
   $(LOCAL_PATH)/../../extras/f2fs_utils
-LOCAL_SRC_FILES := protocol.c engine.c bootimg_utils.cpp fastboot.cpp util.c fs.c
+LOCAL_SRC_FILES := protocol.cpp engine.cpp bootimg_utils.cpp fastboot.cpp util.cpp fs.cpp
 LOCAL_MODULE := fastboot
 LOCAL_MODULE_TAGS := debug
 LOCAL_CONLYFLAGS += -std=gnu99
@@ -30,17 +30,17 @@
 LOCAL_CFLAGS += -DFASTBOOT_REVISION='"$(fastboot_version)"'
 
 ifeq ($(HOST_OS),linux)
-  LOCAL_SRC_FILES += usb_linux.c util_linux.c
+  LOCAL_SRC_FILES += usb_linux.cpp util_linux.cpp
 endif
 
 ifeq ($(HOST_OS),darwin)
-  LOCAL_SRC_FILES += usb_osx.c util_osx.c
+  LOCAL_SRC_FILES += usb_osx.cpp util_osx.cpp
   LOCAL_LDLIBS += -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
   LOCAL_CFLAGS += -Wno-unused-parameter
 endif
 
 ifeq ($(HOST_OS),windows)
-  LOCAL_SRC_FILES += usb_windows.c util_windows.c
+  LOCAL_SRC_FILES += usb_windows.cpp util_windows.cpp
   EXTRA_STATIC_LIBS := AdbWinApi
   ifneq ($(strip $(USE_CYGWIN)),)
     # Pure cygwin case
@@ -97,10 +97,9 @@
 $(call dist-for-goals,dist_files sdk,$(my_dist_files))
 my_dist_files :=
 
-
 ifeq ($(HOST_OS),linux)
 include $(CLEAR_VARS)
-LOCAL_SRC_FILES := usbtest.c usb_linux.c util.c
+LOCAL_SRC_FILES := usbtest.cpp usb_linux.cpp util.cpp
 LOCAL_MODULE := usbtest
 LOCAL_CFLAGS := -Werror
 include $(BUILD_HOST_EXECUTABLE)
diff --git a/fastboot/engine.c b/fastboot/engine.cpp
similarity index 89%
rename from fastboot/engine.c
rename to fastboot/engine.cpp
index 2f90e41..66b8140 100644
--- a/fastboot/engine.c
+++ b/fastboot/engine.cpp
@@ -45,10 +45,6 @@
 #include <sys/mman.h>
 #endif
 
-#ifndef __unused
-#define __unused __attribute__((__unused__))
-#endif
-
 #define ARRAY_SIZE(x)           (sizeof(x)/sizeof(x[0]))
 
 #define OP_DOWNLOAD   1
@@ -73,7 +69,7 @@
     unsigned size;
 
     const char *msg;
-    int (*func)(Action *a, int status, char *resp);
+    int (*func)(Action* a, int status, const char* resp);
 
     double start;
 };
@@ -121,8 +117,7 @@
     return !!fs_get_generator(fs_type);
 }
 
-static int cb_default(Action *a, int status, char *resp)
-{
+static int cb_default(Action* a, int status, const char* resp) {
     if (status) {
         fprintf(stderr,"FAILED (%s)\n", resp);
     } else {
@@ -135,12 +130,11 @@
 
 static Action *queue_action(unsigned op, const char *fmt, ...)
 {
-    Action *a;
     va_list ap;
     size_t cmdsize;
 
-    a = calloc(1, sizeof(Action));
-    if (a == 0) die("out of memory");
+    Action* a = reinterpret_cast<Action*>(calloc(1, sizeof(Action)));
+    if (a == nullptr) die("out of memory");
 
     va_start(ap, fmt);
     cmdsize = vsnprintf(a->cmd, sizeof(a->cmd), fmt, ap);
@@ -198,8 +192,7 @@
     a->msg = mkmsg("writing '%s'", ptn);
 }
 
-static int match(char *str, const char **value, unsigned count)
-{
+static int match(const char* str, const char** value, unsigned count) {
     unsigned n;
 
     for (n = 0; n < count; n++) {
@@ -222,9 +215,9 @@
 
 
 
-static int cb_check(Action *a, int status, char *resp, int invert)
+static int cb_check(Action* a, int status, const char* resp, int invert)
 {
-    const char **value = a->data;
+    const char** value = reinterpret_cast<const char**>(a->data);
     unsigned count = a->size;
     unsigned n;
     int yes;
@@ -265,18 +258,16 @@
     return -1;
 }
 
-static int cb_require(Action *a, int status, char *resp)
-{
+static int cb_require(Action*a, int status, const char* resp) {
     return cb_check(a, status, resp, 0);
 }
 
-static int cb_reject(Action *a, int status, char *resp)
-{
+static int cb_reject(Action* a, int status, const char* resp) {
     return cb_check(a, status, resp, 1);
 }
 
 void fb_queue_require(const char *prod, const char *var,
-		int invert, unsigned nvalues, const char **value)
+                      int invert, unsigned nvalues, const char **value)
 {
     Action *a;
     a = queue_action(OP_QUERY, "getvar:%s", var);
@@ -285,11 +276,10 @@
     a->size = nvalues;
     a->msg = mkmsg("checking %s", var);
     a->func = invert ? cb_reject : cb_require;
-    if (a->data == 0) die("out of memory");
+    if (a->data == nullptr) die("out of memory");
 }
 
-static int cb_display(Action *a, int status, char *resp)
-{
+static int cb_display(Action* a, int status, const char* resp) {
     if (status) {
         fprintf(stderr, "%s FAILED (%s)\n", a->cmd, resp);
         return status;
@@ -303,17 +293,16 @@
     Action *a;
     a = queue_action(OP_QUERY, "getvar:%s", var);
     a->data = strdup(prettyname);
-    if (a->data == 0) die("out of memory");
+    if (a->data == nullptr) die("out of memory");
     a->func = cb_display;
 }
 
-static int cb_save(Action *a, int status, char *resp)
-{
+static int cb_save(Action* a, int status, const char* resp) {
     if (status) {
         fprintf(stderr, "%s FAILED (%s)\n", a->cmd, resp);
         return status;
     }
-    strncpy(a->data, resp, a->size);
+    strncpy(reinterpret_cast<char*>(a->data), resp, a->size);
     return 0;
 }
 
@@ -326,8 +315,7 @@
     a->func = cb_save;
 }
 
-static int cb_do_nothing(Action *a __unused, int status __unused, char *resp __unused)
-{
+static int cb_do_nothing(Action*, int , const char*) {
     fprintf(stderr,"\n");
     return 0;
 }
@@ -398,7 +386,7 @@
         } else if (a->op == OP_NOTICE) {
             fprintf(stderr,"%s\n",(char*)a->data);
         } else if (a->op == OP_DOWNLOAD_SPARSE) {
-            status = fb_download_data_sparse(usb, a->data);
+            status = fb_download_data_sparse(usb, reinterpret_cast<sparse_file*>(a->data));
             status = a->func(a, status, status ? fb_get_error() : "");
             if (status) break;
         } else if (a->op == OP_WAIT_FOR_DISCONNECT) {
@@ -414,5 +402,5 @@
 
 int fb_queue_is_empty(void)
 {
-    return (action_list == NULL);
+    return (action_list == nullptr);
 }
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 0de1def..b964a36 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -275,52 +275,52 @@
             "usage: fastboot [ <option> ] <command>\n"
             "\n"
             "commands:\n"
-            "  update <filename>                        reflash device from update.zip\n"
-            "  flashall                                 flash boot, system, vendor and if found,\n"
-            "                                           recovery\n"
-            "  flash <partition> [ <filename> ]         write a file to a flash partition\n"
-            "  flashing lock                            locks the device. Prevents flashing"
-            "                                           partitions\n"
-            "  flashing unlock                          unlocks the device. Allows user to"
-            "                                           flash any partition except the ones"
-            "                                           that are related to bootloader\n"
-            "  flashing lock_critical                   Prevents flashing bootloader related"
-            "                                           partitions\n"
-            "  flashing unlock_critical                 Enables flashing bootloader related"
-            "                                           partitions\n"
-            "  flashing get_unlock_ability              Queries bootloader to see if the"
-            "                                           device is unlocked\n"
-            "  erase <partition>                        erase a flash partition\n"
-            "  format[:[<fs type>][:[<size>]] <partition> format a flash partition.\n"
-            "                                           Can override the fs type and/or\n"
-            "                                           size the bootloader reports.\n"
-            "  getvar <variable>                        display a bootloader variable\n"
-            "  boot <kernel> [ <ramdisk> [ <second> ] ] download and boot kernel\n"
-            "  flash:raw boot <kernel> [ <ramdisk> [ <second> ] ] create bootimage and \n"
-            "                                           flash it\n"
-            "  devices                                  list all connected devices\n"
-            "  continue                                 continue with autoboot\n"
-            "  reboot [bootloader]                      reboot device, optionally into bootloader\n"
-            "  reboot-bootloader                        reboot device into bootloader\n"
-            "  help                                     show this help message\n"
+            "  update <filename>                        Reflash device from update.zip.\n"
+            "  flashall                                 Flash boot, system, vendor, and --\n"
+            "                                           if found -- recovery.\n"
+            "  flash <partition> [ <filename> ]         Write a file to a flash partition.\n"
+            "  flashing lock                            Locks the device. Prevents flashing.\n"
+            "  flashing unlock                          Unlocks the device. Allows flashing\n"
+            "                                           any partition except\n"
+            "                                           bootloader-related partitions.\n"
+            "  flashing lock_critical                   Prevents flashing bootloader-related\n"
+            "                                           partitions.\n"
+            "  flashing unlock_critical                 Enables flashing bootloader-related\n"
+            "                                           partitions.\n"
+            "  flashing get_unlock_ability              Queries bootloader to see if the\n"
+            "                                           device is unlocked.\n"
+            "  erase <partition>                        Erase a flash partition.\n"
+            "  format[:[<fs type>][:[<size>]] <partition>\n"
+            "                                           Format a flash partition. Can\n"
+            "                                           override the fs type and/or size\n"
+            "                                           the bootloader reports.\n"
+            "  getvar <variable>                        Display a bootloader variable.\n"
+            "  boot <kernel> [ <ramdisk> [ <second> ] ] Download and boot kernel.\n"
+            "  flash:raw boot <kernel> [ <ramdisk> [ <second> ] ]\n"
+            "                                           Create bootimage and flash it.\n"
+            "  devices [-l]                             List all connected devices [with\n"
+            "                                           device paths].\n"
+            "  continue                                 Continue with autoboot.\n"
+            "  reboot [bootloader]                      Reboot device [into bootloader].\n"
+            "  reboot-bootloader                        Reboot device into bootloader.\n"
+            "  help                                     Show this help message.\n"
             "\n"
             "options:\n"
-            "  -w                                       erase userdata and cache (and format\n"
-            "                                           if supported by partition type)\n"
-            "  -u                                       do not first erase partition before\n"
-            "                                           formatting\n"
-            "  -s <specific device>                     specify device serial number\n"
-            "                                           or path to device port\n"
-            "  -l                                       with \"devices\", lists device paths\n"
-            "  -p <product>                             specify product name\n"
-            "  -c <cmdline>                             override kernel commandline\n"
-            "  -i <vendor id>                           specify a custom USB vendor id\n"
-            "  -b <base_addr>                           specify a custom kernel base address.\n"
-            "                                           default: 0x10000000\n"
-            "  -n <page size>                           specify the nand page size.\n"
-            "                                           default: 2048\n"
-            "  -S <size>[K|M|G]                         automatically sparse files greater\n"
-            "                                           than size.  0 to disable\n"
+            "  -w                                       Erase userdata and cache (and format\n"
+            "                                           if supported by partition type).\n"
+            "  -u                                       Do not erase partition before\n"
+            "                                           formatting.\n"
+            "  -s <specific device>                     Specify device serial number\n"
+            "                                           or path to device port.\n"
+            "  -p <product>                             Specify product name.\n"
+            "  -c <cmdline>                             Override kernel commandline.\n"
+            "  -i <vendor id>                           Specify a custom USB vendor id.\n"
+            "  -b <base_addr>                           Specify a custom kernel base\n"
+            "                                           address (default: 0x10000000).\n"
+            "  -n <page size>                           Specify the nand page size\n"
+            "                                           (default: 2048).\n"
+            "  -S <size>[K|M|G]                         Automatically sparse files greater\n"
+            "                                           than 'size'. 0 to disable.\n"
         );
 }
 
@@ -391,7 +391,7 @@
 
 static void* unzip_file(ZipArchiveHandle zip, const char* entry_name, unsigned* sz)
 {
-    ZipEntryName zip_entry_name(entry_name);
+    ZipString zip_entry_name(entry_name);
     ZipEntry zip_entry;
     if (FindEntry(zip, zip_entry_name, &zip_entry) != 0) {
         fprintf(stderr, "archive does not contain '%s'\n", entry_name);
@@ -453,7 +453,7 @@
         return -1;
     }
 
-    ZipEntryName zip_entry_name(entry_name);
+    ZipString zip_entry_name(entry_name);
     ZipEntry zip_entry;
     if (FindEntry(zip, zip_entry_name, &zip_entry) != 0) {
         fprintf(stderr, "archive does not contain '%s'\n", entry_name);
diff --git a/fastboot/fs.c b/fastboot/fs.cpp
similarity index 93%
rename from fastboot/fs.c
rename to fastboot/fs.cpp
index 8a15e6f..d8f9e16 100644
--- a/fastboot/fs.c
+++ b/fastboot/fs.cpp
@@ -38,7 +38,7 @@
 
 static const struct fs_generator {
 
-    char *fs_type;  //must match what fastboot reports for partition type
+    const char* fs_type;  //must match what fastboot reports for partition type
     int (*generate)(int fd, long long partSize); //returns 0 or error value
 
 } generators[] = {
diff --git a/fastboot/protocol.c b/fastboot/protocol.cpp
similarity index 97%
rename from fastboot/protocol.c
rename to fastboot/protocol.cpp
index 5b97600..00c8a03 100644
--- a/fastboot/protocol.c
+++ b/fastboot/protocol.cpp
@@ -223,9 +223,9 @@
 static int fb_download_data_sparse_write(void *priv, const void *data, int len)
 {
     int r;
-    usb_handle *usb = priv;
+    usb_handle* usb = reinterpret_cast<usb_handle*>(priv);
     int to_write;
-    const char *ptr = data;
+    const char* ptr = reinterpret_cast<const char*>(data);
 
     if (usb_buf_len) {
         to_write = min(USB_BUF_SIZE - usb_buf_len, len);
diff --git a/fastboot/usb_linux.c b/fastboot/usb_linux.cpp
similarity index 98%
rename from fastboot/usb_linux.c
rename to fastboot/usb_linux.cpp
index 022f364..9078c8f 100644
--- a/fastboot/usb_linux.c
+++ b/fastboot/usb_linux.cpp
@@ -340,7 +340,7 @@
 
             if(filter_usb_device(de->d_name, desc, n, writable, callback,
                                  &in, &out, &ifc) == 0) {
-                usb = calloc(1, sizeof(usb_handle));
+                usb = reinterpret_cast<usb_handle*>(calloc(1, sizeof(usb_handle)));
                 strcpy(usb->fname, devname);
                 usb->ep_in = in;
                 usb->ep_out = out;
diff --git a/fastboot/usb_osx.c b/fastboot/usb_osx.cpp
similarity index 98%
rename from fastboot/usb_osx.c
rename to fastboot/usb_osx.cpp
index 0b6c515..a959566 100644
--- a/fastboot/usb_osx.c
+++ b/fastboot/usb_osx.cpp
@@ -26,6 +26,7 @@
  * SUCH DAMAGE.
  */
 
+#include <inttypes.h>
 #include <stdio.h>
 #include <CoreFoundation/CoreFoundation.h>
 #include <IOKit/IOKitLib.h>
@@ -121,7 +122,7 @@
         result = (*plugInInterface)->QueryInterface(
                 plugInInterface,
                 CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID),
-                (LPVOID) &interface);
+                (LPVOID*) &interface);
 
         // No longer need the intermediate plugin
         (*plugInInterface)->Release(plugInInterface);
@@ -279,7 +280,7 @@
 
     // Now create the device interface.
     result = (*plugin)->QueryInterface(plugin,
-            CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID) &dev);
+            CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID), (LPVOID*) &dev);
     if ((result != 0) || (dev == NULL)) {
         ERR("Couldn't create a device interface (%08x)\n", (int) result);
         goto error;
@@ -432,7 +433,7 @@
         }
 
         if (h.success) {
-            *handle = calloc(1, sizeof(usb_handle));
+            *handle = reinterpret_cast<usb_handle*>(calloc(1, sizeof(usb_handle)));
             memcpy(*handle, &h, sizeof(usb_handle));
             ret = 0;
             break;
diff --git a/fastboot/usb_windows.c b/fastboot/usb_windows.cpp
similarity index 100%
rename from fastboot/usb_windows.c
rename to fastboot/usb_windows.cpp
diff --git a/fastboot/usbtest.c b/fastboot/usbtest.cpp
similarity index 100%
rename from fastboot/usbtest.c
rename to fastboot/usbtest.cpp
diff --git a/fastboot/util.c b/fastboot/util.cpp
similarity index 100%
rename from fastboot/util.c
rename to fastboot/util.cpp
diff --git a/fastboot/util_linux.c b/fastboot/util_linux.cpp
similarity index 98%
rename from fastboot/util_linux.c
rename to fastboot/util_linux.cpp
index 91c3776..b788199 100644
--- a/fastboot/util_linux.c
+++ b/fastboot/util_linux.cpp
@@ -26,6 +26,8 @@
  * SUCH DAMAGE.
  */
 
+#include "fastboot.h"
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
diff --git a/fastboot/util_osx.c b/fastboot/util_osx.cpp
similarity index 98%
rename from fastboot/util_osx.c
rename to fastboot/util_osx.cpp
index e718562..ae0b024 100644
--- a/fastboot/util_osx.c
+++ b/fastboot/util_osx.cpp
@@ -26,6 +26,8 @@
  * SUCH DAMAGE.
  */
 
+#include "fastboot.h"
+
 #import <Carbon/Carbon.h>
 #include <unistd.h>
 
diff --git a/fastboot/util_windows.c b/fastboot/util_windows.cpp
similarity index 98%
rename from fastboot/util_windows.c
rename to fastboot/util_windows.cpp
index 74a5c27..ec52f39 100644
--- a/fastboot/util_windows.c
+++ b/fastboot/util_windows.cpp
@@ -26,6 +26,8 @@
  * SUCH DAMAGE.
  */
 
+#include "fastboot.h"
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c
index bb2bed2..42f25c7 100644
--- a/fs_mgr/fs_mgr.c
+++ b/fs_mgr/fs_mgr.c
@@ -95,7 +95,7 @@
     int status;
     int ret;
     long tmpmnt_flags = MS_NOATIME | MS_NOEXEC | MS_NOSUID;
-    char *tmpmnt_opts = "nomblk_io_submit,errors=remount-ro";
+    char tmpmnt_opts[64] = "errors=remount-ro";
     char *e2fsck_argv[] = {
         E2FSCK_BIN,
         "-y",
@@ -118,6 +118,10 @@
          * fix the filesystem.
          */
         errno = 0;
+        if (!strcmp(fs_type, "ext4")) {
+            // This option is only valid with ext4
+            strlcat(tmpmnt_opts, ",nomblk_io_submit", sizeof(tmpmnt_opts));
+        }
         ret = mount(blk_device, target, fs_type, tmpmnt_flags, tmpmnt_opts);
         INFO("%s(): mount(%s,%s,%s)=%d: %s\n",
              __func__, blk_device, target, fs_type, ret, strerror(errno));
diff --git a/fs_mgr/fs_mgr_verity.c b/fs_mgr/fs_mgr_verity.c
index 81c7c9a..0a945a3 100644
--- a/fs_mgr/fs_mgr_verity.c
+++ b/fs_mgr/fs_mgr_verity.c
@@ -767,8 +767,24 @@
 
 static int load_verity_state(struct fstab_rec *fstab, int *mode)
 {
-    off64_t offset = 0;
+    char propbuf[PROPERTY_VALUE_MAX];
     int match = 0;
+    off64_t offset = 0;
+
+    /* use the kernel parameter if set */
+    property_get("ro.boot.veritymode", propbuf, "");
+
+    if (*propbuf != '\0') {
+        if (!strcmp(propbuf, "enforcing")) {
+            *mode = VERITY_MODE_DEFAULT;
+            return 0;
+        } else if (!strcmp(propbuf, "logging")) {
+            *mode = VERITY_MODE_LOGGING;
+            return 0;
+        } else {
+            INFO("Unknown value %s for veritymode; ignoring", propbuf);
+        }
+    }
 
     if (get_verity_state_offset(fstab, &offset) < 0) {
         /* fall back to stateless behavior */
@@ -855,6 +871,13 @@
     struct dm_ioctl *io = (struct dm_ioctl *) buffer;
     struct fstab *fstab = NULL;
 
+    /* check if we need to store the state */
+    property_get("ro.boot.veritymode", propbuf, "");
+
+    if (*propbuf != '\0') {
+        return 0; /* state is kept by the bootloader */
+    }
+
     fd = TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC));
 
     if (fd == -1) {
diff --git a/include/utils/TypeHelpers.h b/include/utils/TypeHelpers.h
index 13c9081..61d618e 100644
--- a/include/utils/TypeHelpers.h
+++ b/include/utils/TypeHelpers.h
@@ -131,7 +131,8 @@
 template<typename TYPE> inline
 void construct_type(TYPE* p, size_t n) {
     if (!traits<TYPE>::has_trivial_ctor) {
-        while (n--) {
+        while (n > 0) {
+            n--;
             new(p++) TYPE;
         }
     }
@@ -140,7 +141,8 @@
 template<typename TYPE> inline
 void destroy_type(TYPE* p, size_t n) {
     if (!traits<TYPE>::has_trivial_dtor) {
-        while (n--) {
+        while (n > 0) {
+            n--;
             p->~TYPE();
             p++;
         }
@@ -150,7 +152,8 @@
 template<typename TYPE> inline
 void copy_type(TYPE* d, const TYPE* s, size_t n) {
     if (!traits<TYPE>::has_trivial_copy) {
-        while (n--) {
+        while (n > 0) {
+            n--;
             new(d) TYPE(*s);
             d++, s++;
         }
@@ -162,12 +165,14 @@
 template<typename TYPE> inline
 void splat_type(TYPE* where, const TYPE* what, size_t n) {
     if (!traits<TYPE>::has_trivial_copy) {
-        while (n--) {
+        while (n > 0) {
+            n--;
             new(where) TYPE(*what);
             where++;
         }
     } else {
-        while (n--) {
+        while (n > 0) {
+            n--;
             *where++ = *what;
         }
     }
@@ -182,7 +187,8 @@
     } else {
         d += n;
         s += n;
-        while (n--) {
+        while (n > 0) {
+            n--;
             --d, --s;
             if (!traits<TYPE>::has_trivial_copy) {
                 new(d) TYPE(*s);
@@ -203,7 +209,8 @@
     {
         memmove(d,s,n*sizeof(TYPE));
     } else {
-        while (n--) {
+        while (n > 0) {
+            n--;
             if (!traits<TYPE>::has_trivial_copy) {
                 new(d) TYPE(*s);
             } else {
diff --git a/include/ziparchive/zip_archive.h b/include/ziparchive/zip_archive.h
index 386a390..5ef2ab0 100644
--- a/include/ziparchive/zip_archive.h
+++ b/include/ziparchive/zip_archive.h
@@ -33,17 +33,33 @@
   kCompressDeflated   = 8,        // standard deflate
 };
 
-struct ZipEntryName {
+struct ZipString {
   const uint8_t* name;
   uint16_t name_length;
 
-  ZipEntryName() {}
+  ZipString() {}
 
   /*
    * entry_name has to be an c-style string with only ASCII characters.
    */
-  explicit ZipEntryName(const char* entry_name)
+  explicit ZipString(const char* entry_name)
       : name(reinterpret_cast<const uint8_t*>(entry_name)), name_length(strlen(entry_name)) {}
+
+  bool operator==(const ZipString& rhs) const {
+    return name && (name_length == rhs.name_length) &&
+        (memcmp(name, rhs.name, name_length) == 0);
+  }
+
+  bool StartsWith(const ZipString& prefix) const {
+    return name && (name_length >= prefix.name_length) &&
+        (memcmp(name, prefix.name, prefix.name_length) == 0);
+  }
+
+  bool EndsWith(const ZipString& suffix) const {
+    return name && (name_length >= suffix.name_length) &&
+        (memcmp(name + name_length - suffix.name_length, suffix.name,
+                suffix.name_length) == 0);
+  }
 };
 
 /*
@@ -136,7 +152,7 @@
  * and length, a call to VerifyCrcAndLengths must be made after entry data
  * has been processed.
  */
-int32_t FindEntry(const ZipArchiveHandle handle, const ZipEntryName& entryName,
+int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName,
                   ZipEntry* data);
 
 /*
@@ -147,13 +163,14 @@
  * calls to Next. All calls to StartIteration must be matched by a call to
  * EndIteration to free any allocated memory.
  *
- * This method also accepts an optional prefix to restrict iteration to
- * entry names that start with |optional_prefix|.
+ * This method also accepts optional prefix and suffix to restrict iteration to
+ * entry names that start with |optional_prefix| or end with |optional_suffix|.
  *
  * Returns 0 on success and negative values on failure.
  */
 int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr,
-                       const ZipEntryName* optional_prefix);
+                       const ZipString* optional_prefix,
+                       const ZipString* optional_suffix);
 
 /*
  * Advance to the next element in the zipfile in iteration order.
@@ -161,7 +178,7 @@
  * Returns 0 on success, -1 if there are no more elements in this
  * archive and lower negative values on failure.
  */
-int32_t Next(void* cookie, ZipEntry* data, ZipEntryName *name);
+int32_t Next(void* cookie, ZipEntry* data, ZipString* name);
 
 /*
  * End iteration over all entries of a zip file and frees the memory allocated
diff --git a/init/Android.mk b/init/Android.mk
index b14f9b5..45b002d 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -18,8 +18,6 @@
     -Wno-unused-parameter \
     -Werror \
 
-init_clang := true
-
 # --
 
 include $(CLEAR_VARS)
@@ -32,7 +30,7 @@
 
 LOCAL_STATIC_LIBRARIES := libbase
 LOCAL_MODULE := libinit
-LOCAL_CLANG := $(init_clang)
+LOCAL_CLANG := true
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
@@ -79,7 +77,7 @@
     ln -sf ../init $(TARGET_ROOT_OUT)/sbin/ueventd; \
     ln -sf ../init $(TARGET_ROOT_OUT)/sbin/watchdogd
 
-LOCAL_CLANG := $(init_clang)
+LOCAL_CLANG := true
 include $(BUILD_EXECUTABLE)
 
 
@@ -96,5 +94,5 @@
     libbase \
 
 LOCAL_STATIC_LIBRARIES := libinit
-LOCAL_CLANG := $(init_clang)
+LOCAL_CLANG := true
 include $(BUILD_NATIVE_TEST)
diff --git a/init/compare-bootcharts.py b/init/compare-bootcharts.py
new file mode 100755
index 0000000..2057b55
--- /dev/null
+++ b/init/compare-bootcharts.py
@@ -0,0 +1,146 @@
+#!/usr/bin/env python
+
+# 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.
+
+"""Compare two bootcharts and list start/end timestamps on key processes.
+
+This script extracts two bootchart.tgz files and compares the timestamps
+in proc_ps.log for selected processes. The proc_ps.log file consists of
+repetitive blocks of the following format:
+
+timestamp1 (jiffies)
+dumps of /proc/<pid>/stat
+
+timestamp2
+dumps of /proc/<pid>/stat
+
+The timestamps are 200ms apart, and the creation time of selected processes
+are listed. The termination time of the boot animation process is also listed
+as a coarse indication about when the boot process is complete as perceived by
+the user.
+"""
+
+import sys
+import tarfile
+
+# The bootchart timestamps are 200ms apart, but the USER_HZ value is not
+# reported in the bootchart, so we use the first two timestamps to calculate
+# the wall clock time of a jiffy.
+jiffy_to_wallclock = {
+   '1st_timestamp': -1,
+   '2nd_timestamp': -1,
+   'jiffy_to_wallclock': -1
+}
+
+def analyze_process_maps(process_map1, process_map2, jiffy_record):
+    # List interesting processes here
+    processes_of_interest = [
+        '/init',
+        '/system/bin/surfaceflinger',
+        '/system/bin/bootanimation',
+        'zygote64',
+        'zygote',
+        'system_server'
+    ]
+
+    jw = jiffy_record['jiffy_to_wallclock']
+    print "process: baseline experiment (delta)"
+    print " - Unit is ms (a jiffy is %d ms on the system)" % jw
+    print "------------------------------------"
+    for p in processes_of_interest:
+        # e.g., 32-bit system doesn't have zygote64
+        if p in process_map1 and p in process_map2:
+            print "%s: %d %d (%+d)" % (
+                p, process_map1[p]['start_time'] * jw,
+                process_map2[p]['start_time'] * jw,
+                (process_map2[p]['start_time'] -
+                 process_map1[p]['start_time']) * jw)
+
+    # Print the last tick for the bootanimation process
+    print "bootanimation ends at: %d %d (%+d)" % (
+        process_map1['/system/bin/bootanimation']['last_tick'] * jw,
+        process_map2['/system/bin/bootanimation']['last_tick'] * jw,
+        (process_map2['/system/bin/bootanimation']['last_tick'] -
+            process_map1['/system/bin/bootanimation']['last_tick']) * jw)
+
+def parse_proc_file(pathname, process_map, jiffy_record=None):
+    # Uncompress bootchart.tgz
+    with tarfile.open(pathname + '/bootchart.tgz', 'r:*') as tf:
+        try:
+            # Read proc_ps.log
+            f = tf.extractfile('proc_ps.log')
+
+            # Break proc_ps into chunks based on timestamps
+            blocks = f.read().split('\n\n')
+            for b in blocks:
+                lines = b.split('\n')
+                if not lines[0]:
+                    break
+
+                # 200ms apart in jiffies
+                timestamp = int(lines[0]);
+
+                # Figure out the wall clock time of a jiffy
+                if jiffy_record is not None:
+                    if jiffy_record['1st_timestamp'] == -1:
+                        jiffy_record['1st_timestamp'] = timestamp
+                    elif jiffy_record['jiffy_to_wallclock'] == -1:
+                        # Not really needed but for debugging purposes
+                        jiffy_record['2nd_timestamp'] = timestamp
+                        value = 200 / (timestamp -
+                                       jiffy_record['1st_timestamp'])
+                        # Fix the rounding error
+                        # e.g., 201 jiffies in 200ms when USER_HZ is 1000
+                        if value == 0:
+                            value = 1
+                        # e.g., 21 jiffies in 200ms when USER_HZ is 100
+                        elif value == 9:
+                            value = 10
+                        jiffy_record['jiffy_to_wallclock'] = value
+
+                # Populate the process_map table
+                for line in lines[1:]:
+                    segs = line.split(' ')
+
+                    #  0: pid
+                    #  1: process name
+                    # 17: priority
+                    # 18: nice
+                    # 21: creation time
+
+                    proc_name = segs[1].strip('()')
+                    if proc_name in process_map:
+                        process = process_map[proc_name]
+                    else:
+                        process = {'start_time': int(segs[21])}
+                        process_map[proc_name] = process
+
+                    process['last_tick'] = timestamp
+        finally:
+            f.close()
+
+def main():
+    if len(sys.argv) != 3:
+        print "Usage: %s base_bootchart_dir exp_bootchart_dir" % sys.argv[0]
+        sys.exit(1)
+
+    process_map1 = {}
+    process_map2 = {}
+    parse_proc_file(sys.argv[1], process_map1, jiffy_to_wallclock)
+    parse_proc_file(sys.argv[2], process_map2)
+    analyze_process_maps(process_map1, process_map2, jiffy_to_wallclock)
+
+if __name__ == "__main__":
+    main()
diff --git a/init/devices.cpp b/init/devices.cpp
index 2c7f5a9..3652c57 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -241,10 +241,8 @@
 
     mode = get_device_perm(path, links, &uid, &gid) | (block ? S_IFBLK : S_IFCHR);
 
-    if (sehandle) {
-        selabel_lookup_best_match(sehandle, &secontext, path, links, mode);
-        setfscreatecon(secontext);
-    }
+    selabel_lookup_best_match(sehandle, &secontext, path, links, mode);
+    setfscreatecon(secontext);
 
     dev = makedev(major, minor);
     /* Temporarily change egid to avoid race condition setting the gid of the
@@ -907,7 +905,7 @@
         struct uevent uevent;
         parse_event(msg, &uevent);
 
-        if (sehandle && selinux_status_updated() > 0) {
+        if (selinux_status_updated() > 0) {
             struct selabel_handle *sehandle2;
             sehandle2 = selinux_android_file_context_handle();
             if (sehandle2) {
@@ -974,11 +972,8 @@
 }
 
 void device_init() {
-    sehandle = NULL;
-    if (is_selinux_enabled() > 0) {
-        sehandle = selinux_android_file_context_handle();
-        selinux_status_open(true);
-    }
+    sehandle = selinux_android_file_context_handle();
+    selinux_status_open(true);
 
     /* is 256K enough? udev uses 16MB! */
     device_fd = uevent_open_socket(256*1024, true);
diff --git a/init/init.cpp b/init/init.cpp
index 2500985..f48016f 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -220,40 +220,38 @@
     }
 
     char* scon = NULL;
-    if (is_selinux_enabled() > 0) {
-        if (svc->seclabel) {
-            scon = strdup(svc->seclabel);
-            if (!scon) {
-                ERROR("Out of memory while starting '%s'\n", svc->name);
-                return;
-            }
-        } else {
-            char *mycon = NULL, *fcon = NULL;
+    if (svc->seclabel) {
+        scon = strdup(svc->seclabel);
+        if (!scon) {
+            ERROR("Out of memory while starting '%s'\n", svc->name);
+            return;
+        }
+    } else {
+        char *mycon = NULL, *fcon = NULL;
 
-            INFO("computing context for service '%s'\n", svc->args[0]);
-            int rc = getcon(&mycon);
-            if (rc < 0) {
-                ERROR("could not get context while starting '%s'\n", svc->name);
-                return;
-            }
+        INFO("computing context for service '%s'\n", svc->args[0]);
+        int rc = getcon(&mycon);
+        if (rc < 0) {
+            ERROR("could not get context while starting '%s'\n", svc->name);
+            return;
+        }
 
-            rc = getfilecon(svc->args[0], &fcon);
-            if (rc < 0) {
-                ERROR("could not get context while starting '%s'\n", svc->name);
-                freecon(mycon);
-                return;
-            }
-
-            rc = security_compute_create(mycon, fcon, string_to_security_class("process"), &scon);
-            if (rc == 0 && !strcmp(scon, mycon)) {
-                ERROR("Warning!  Service %s needs a SELinux domain defined; please fix!\n", svc->name);
-            }
+        rc = getfilecon(svc->args[0], &fcon);
+        if (rc < 0) {
+            ERROR("could not get context while starting '%s'\n", svc->name);
             freecon(mycon);
-            freecon(fcon);
-            if (rc < 0) {
-                ERROR("could not get context while starting '%s'\n", svc->name);
-                return;
-            }
+            return;
+        }
+
+        rc = security_compute_create(mycon, fcon, string_to_security_class("process"), &scon);
+        if (rc == 0 && !strcmp(scon, mycon)) {
+            ERROR("Warning!  Service %s needs a SELinux domain defined; please fix!\n", svc->name);
+        }
+        freecon(mycon);
+        freecon(fcon);
+        if (rc < 0) {
+            ERROR("could not get context while starting '%s'\n", svc->name);
+            return;
         }
     }
 
@@ -290,6 +288,16 @@
         freecon(scon);
         scon = NULL;
 
+        if (svc->writepid_files_) {
+            std::string pid_str = android::base::StringPrintf("%d", pid);
+            for (auto& file : *svc->writepid_files_) {
+                if (!android::base::WriteStringToFile(pid_str, file)) {
+                    ERROR("couldn't write %s to %s: %s\n",
+                          pid_str.c_str(), file.c_str(), strerror(errno));
+                }
+            }
+        }
+
         if (svc->ioprio_class != IoSchedClass_NONE) {
             if (android_set_ioprio(getpid(), svc->ioprio_class, svc->ioprio_pri)) {
                 ERROR("Failed to set pid %d ioprio = %d,%d: %s\n",
@@ -335,7 +343,7 @@
             }
         }
         if (svc->seclabel) {
-            if (is_selinux_enabled() > 0 && setexeccon(svc->seclabel) < 0) {
+            if (setexeccon(svc->seclabel) < 0) {
                 ERROR("cannot setexeccon('%s'): %s\n", svc->seclabel, strerror(errno));
                 _exit(127);
             }
diff --git a/init/init.h b/init/init.h
index 1cabb14..c166969 100644
--- a/init/init.h
+++ b/init/init.h
@@ -19,6 +19,9 @@
 
 #include <sys/types.h>
 
+#include <string>
+#include <vector>
+
 #include <cutils/list.h>
 #include <cutils/iosched_policy.h>
 
@@ -116,6 +119,8 @@
 
     struct action onrestart;  /* Actions to execute on restart. */
 
+    std::vector<std::string>* writepid_files_;
+
     /* keycodes for triggering this service via /dev/keychord */
     int *keycodes;
     int nkeycodes;
diff --git a/init/init_parser.cpp b/init/init_parser.cpp
index f975b6c..956ed25 100644
--- a/init/init_parser.cpp
+++ b/init/init_parser.cpp
@@ -206,6 +206,7 @@
         break;
     case 'w':
         if (!strcmp(s, "rite")) return K_write;
+        if (!strcmp(s, "ritepid")) return K_writepid;
         if (!strcmp(s, "ait")) return K_wait;
         break;
     }
@@ -924,6 +925,16 @@
             svc->seclabel = args[1];
         }
         break;
+    case K_writepid:
+        if (nargs < 2) {
+            parse_error(state, "writepid option requires at least one filename\n");
+            break;
+        }
+        svc->writepid_files_ = new std::vector<std::string>;
+        for (int i = 1; i < nargs; ++i) {
+            svc->writepid_files_->push_back(args[i]);
+        }
+        break;
 
     default:
         parse_error(state, "invalid option '%s'\n", args[0]);
diff --git a/init/keywords.h b/init/keywords.h
index 37f01b8..e637d7d 100644
--- a/init/keywords.h
+++ b/init/keywords.h
@@ -43,11 +43,15 @@
 enum {
     K_UNKNOWN,
 #endif
+    KEYWORD(bootchart_init,        COMMAND, 0, do_bootchart_init)
+    KEYWORD(chmod,       COMMAND, 2, do_chmod)
+    KEYWORD(chown,       COMMAND, 2, do_chown)
     KEYWORD(class,       OPTION,  0, 0)
+    KEYWORD(class_reset, COMMAND, 1, do_class_reset)
     KEYWORD(class_start, COMMAND, 1, do_class_start)
     KEYWORD(class_stop,  COMMAND, 1, do_class_stop)
-    KEYWORD(class_reset, COMMAND, 1, do_class_reset)
     KEYWORD(console,     OPTION,  0, 0)
+    KEYWORD(copy,        COMMAND, 2, do_copy)
     KEYWORD(critical,    OPTION,  0, 0)
     KEYWORD(disabled,    OPTION,  0, 0)
     KEYWORD(domainname,  COMMAND, 1, do_domainname)
@@ -57,16 +61,20 @@
     KEYWORD(group,       OPTION,  0, 0)
     KEYWORD(hostname,    COMMAND, 1, do_hostname)
     KEYWORD(ifup,        COMMAND, 1, do_ifup)
+    KEYWORD(import,      SECTION, 1, 0)
     KEYWORD(insmod,      COMMAND, 1, do_insmod)
     KEYWORD(installkey,  COMMAND, 1, do_installkey)
-    KEYWORD(import,      SECTION, 1, 0)
+    KEYWORD(ioprio,      OPTION,  0, 0)
     KEYWORD(keycodes,    OPTION,  0, 0)
+    KEYWORD(load_all_props,        COMMAND, 0, do_load_all_props)
+    KEYWORD(load_persist_props,    COMMAND, 0, do_load_persist_props)
+    KEYWORD(loglevel,    COMMAND, 1, do_loglevel)
     KEYWORD(mkdir,       COMMAND, 1, do_mkdir)
     KEYWORD(mount_all,   COMMAND, 1, do_mount_all)
     KEYWORD(mount,       COMMAND, 3, do_mount)
-    KEYWORD(on,          SECTION, 0, 0)
     KEYWORD(oneshot,     OPTION,  0, 0)
     KEYWORD(onrestart,   OPTION,  0, 0)
+    KEYWORD(on,          SECTION, 0, 0)
     KEYWORD(powerctl,    COMMAND, 1, do_powerctl)
     KEYWORD(restart,     COMMAND, 1, do_restart)
     KEYWORD(restorecon,  COMMAND, 1, do_restorecon)
@@ -82,22 +90,15 @@
     KEYWORD(start,       COMMAND, 1, do_start)
     KEYWORD(stop,        COMMAND, 1, do_stop)
     KEYWORD(swapon_all,  COMMAND, 1, do_swapon_all)
-    KEYWORD(trigger,     COMMAND, 1, do_trigger)
     KEYWORD(symlink,     COMMAND, 1, do_symlink)
     KEYWORD(sysclktz,    COMMAND, 1, do_sysclktz)
+    KEYWORD(trigger,     COMMAND, 1, do_trigger)
     KEYWORD(user,        OPTION,  0, 0)
     KEYWORD(verity_load_state,      COMMAND, 0, do_verity_load_state)
     KEYWORD(verity_update_state,    COMMAND, 0, do_verity_update_state)
     KEYWORD(wait,        COMMAND, 1, do_wait)
     KEYWORD(write,       COMMAND, 2, do_write)
-    KEYWORD(copy,        COMMAND, 2, do_copy)
-    KEYWORD(chown,       COMMAND, 2, do_chown)
-    KEYWORD(chmod,       COMMAND, 2, do_chmod)
-    KEYWORD(loglevel,    COMMAND, 1, do_loglevel)
-    KEYWORD(load_persist_props,    COMMAND, 0, do_load_persist_props)
-    KEYWORD(load_all_props,        COMMAND, 0, do_load_all_props)
-    KEYWORD(ioprio,      OPTION,  0, 0)
-    KEYWORD(bootchart_init,        COMMAND, 0, do_bootchart_init)
+    KEYWORD(writepid,    OPTION,  0, 0)
 #ifdef __MAKE_KEYWORD_ENUM__
     KEYWORD_COUNT,
 };
diff --git a/init/property_service.cpp b/init/property_service.cpp
index 0ee0351..5b7a1cb 100644
--- a/init/property_service.cpp
+++ b/init/property_service.cpp
@@ -92,9 +92,6 @@
 
 static int check_mac_perms(const char *name, char *sctx)
 {
-    if (is_selinux_enabled() <= 0)
-        return 1;
-
     char *tctx = NULL;
     int result = 0;
 
diff --git a/init/readme.txt b/init/readme.txt
index c213041..4dda340 100644
--- a/init/readme.txt
+++ b/init/readme.txt
@@ -60,36 +60,36 @@
 runs the service.
 
 critical
-   This is a device-critical service. If it exits more than four times in
-   four minutes, the device will reboot into recovery mode.
+  This is a device-critical service. If it exits more than four times in
+  four minutes, the device will reboot into recovery mode.
 
 disabled
-   This service will not automatically start with its class.
-   It must be explicitly started by name.
+  This service will not automatically start with its class.
+  It must be explicitly started by name.
 
 setenv <name> <value>
-   Set the environment variable <name> to <value> in the launched process.
+  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.
 
 user <username>
-   Change to username before exec'ing this service.
-   Currently defaults to root.  (??? probably should default to nobody)
-   Currently, if your process requires linux capabilities then you cannot use
-   this command. You must instead request the capabilities in-process while
-   still root, and then drop to your desired uid.
+  Change to username before exec'ing this service.
+  Currently defaults to root.  (??? probably should default to nobody)
+  Currently, if your process requires linux capabilities then you cannot use
+  this command. You must instead request the capabilities in-process while
+  still root, and then drop to your desired uid.
 
 group <groupname> [ <groupname> ]*
-   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)
+  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)
 
 seclabel <seclabel>
   Change to 'seclabel' before exec'ing this service.
@@ -99,22 +99,26 @@
   If not specified and no transition is defined in policy, defaults to the init context.
 
 oneshot
-   Do not restart the service when it exits.
+  Do not restart the service when it exits.
 
 class <name>
-   Specify a class name for the service.  All services in a
-   named class may be started or stopped together.  A service
-   is in the class "default" if one is not specified via the
-   class option.
+  Specify a class name for the service.  All services in a
+  named class may be started or stopped together.  A service
+  is in the class "default" if one is not specified via the
+  class option.
 
 onrestart
-    Execute a Command (see below) when service restarts.
+  Execute a Command (see below) when service restarts.
+
+writepid <file...>
+  Write the child's pid to the given files when it forks. Meant for
+  cgroup/cpuset usage.
 
 
 Triggers
 --------
-   Triggers are strings which can be used to match certain kinds
-   of events and used to cause an action to occur.
+Triggers are strings which can be used to match certain kinds
+of events and used to cause an action to occur.
 
 boot
    This is the first trigger that will occur when init starts
@@ -347,6 +351,29 @@
 actually started init.
 
 
+Comparing two bootcharts
+------------------------
+A handy script named compare-bootcharts.py can be used to compare the
+start/end time of selected processes. The aforementioned grab-bootchart.sh
+will leave a bootchart tarball named bootchart.tgz at /tmp/android-bootchart.
+If two such barballs are preserved on the host machine under different
+directories, the script can list the timestamps differences. For example:
+
+Usage: system/core/init/compare-bootcharts.py base_bootchart_dir
+       exp_bootchart_dir
+
+process: baseline experiment (delta)
+ - Unit is ms (a jiffy is 10 ms on the system)
+------------------------------------
+/init: 50 40 (-10)
+/system/bin/surfaceflinger: 4320 4470 (+150)
+/system/bin/bootanimation: 6980 6990 (+10)
+zygote64: 10410 10640 (+230)
+zygote: 10410 10640 (+230)
+system_server: 15350 15150 (-200)
+bootanimation ends at: 33790 31230 (-2560)
+
+
 Debugging init
 --------------
 By default, programs executed by init will drop stdout and stderr into
diff --git a/init/util.cpp b/init/util.cpp
index 8216892..7f29e94 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -47,7 +47,7 @@
 
 /*
  * android_name_to_id - returns the integer uid/gid associated with the given
- * name, or -1U on error.
+ * name, or UINT_MAX on error.
  */
 static unsigned int android_name_to_id(const char *name)
 {
@@ -59,27 +59,35 @@
             return info[n].aid;
     }
 
-    return -1U;
+    return UINT_MAX;
 }
 
-/*
- * decode_uid - decodes and returns the given string, which can be either the
- * numeric or name representation, into the integer uid or gid. Returns -1U on
- * error.
- */
-unsigned int decode_uid(const char *s)
+static unsigned int do_decode_uid(const char *s)
 {
     unsigned int v;
 
     if (!s || *s == '\0')
-        return -1U;
+        return UINT_MAX;
     if (isalpha(s[0]))
         return android_name_to_id(s);
 
     errno = 0;
     v = (unsigned int) strtoul(s, 0, 0);
     if (errno)
-        return -1U;
+        return UINT_MAX;
+    return v;
+}
+
+/*
+ * decode_uid - decodes and returns the given string, which can be either the
+ * numeric or name representation, into the integer uid or gid. Returns
+ * UINT_MAX on error.
+ */
+unsigned int decode_uid(const char *s) {
+    unsigned int v = do_decode_uid(s);
+    if (v == UINT_MAX) {
+        ERROR("decode_uid: Unable to find UID for '%s'. Returning UINT_MAX\n", s);
+    }
     return v;
 }
 
diff --git a/init/util_test.cpp b/init/util_test.cpp
index 5b3ab50..228954b 100644
--- a/init/util_test.cpp
+++ b/init/util_test.cpp
@@ -38,6 +38,6 @@
 
 TEST(util, decode_uid) {
   EXPECT_EQ(0U, decode_uid("root"));
-  EXPECT_EQ(-1U, decode_uid("toot"));
+  EXPECT_EQ(UINT_MAX, decode_uid("toot"));
   EXPECT_EQ(123U, decode_uid("123"));
 }
diff --git a/libbacktrace/BacktracePtrace.cpp b/libbacktrace/BacktracePtrace.cpp
index e10cce1..fd8b713 100644
--- a/libbacktrace/BacktracePtrace.cpp
+++ b/libbacktrace/BacktracePtrace.cpp
@@ -37,8 +37,6 @@
   errno = 0;
   *out_value = ptrace(PTRACE_PEEKTEXT, tid, reinterpret_cast<void*>(addr), nullptr);
   if (*out_value == static_cast<word_t>(-1) && errno) {
-    BACK_LOGW("invalid pointer %p reading from tid %d, ptrace() strerror(errno)=%s",
-              reinterpret_cast<void*>(addr), tid, strerror(errno));
     return false;
   }
   return true;
diff --git a/liblog/Android.mk b/liblog/Android.mk
index d7766f5..ce282bd 100644
--- a/liblog/Android.mk
+++ b/liblog/Android.mk
@@ -68,6 +68,7 @@
 LOCAL_LDLIBS := -lrt
 endif
 LOCAL_MULTILIB := both
+LOCAL_CXX_STL := none
 include $(BUILD_HOST_SHARED_LIBRARY)
 
 
@@ -77,6 +78,8 @@
 LOCAL_MODULE := liblog
 LOCAL_SRC_FILES := $(liblog_target_sources)
 LOCAL_CFLAGS := -Werror $(liblog_cflags)
+# AddressSanitizer runtime library depends on liblog.
+LOCAL_SANITIZE := never
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
@@ -87,6 +90,9 @@
 # TODO: This is to work around b/19059885. Remove after root cause is fixed
 LOCAL_LDFLAGS_arm := -Wl,--hash-style=both
 
+LOCAL_SANITIZE := never
+LOCAL_CXX_STL := none
+
 include $(BUILD_SHARED_LIBRARY)
 
 include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/liblog/log_is_loggable.c b/liblog/log_is_loggable.c
index 2e09192..7a8e33f 100644
--- a/liblog/log_is_loggable.c
+++ b/liblog/log_is_loggable.c
@@ -15,41 +15,158 @@
 */
 
 #include <ctype.h>
+#include <pthread.h>
+#include <stdlib.h>
 #include <string.h>
-#include <sys/system_properties.h>
+#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
+#include <sys/_system_properties.h>
 
 #include <android/log.h>
 
-static int __android_log_level(const char *tag, int def)
+struct cache {
+    const prop_info *pinfo;
+    uint32_t serial;
+    char c;
+};
+
+static void refresh_cache(struct cache *cache, const char *key)
 {
+    uint32_t serial;
     char buf[PROP_VALUE_MAX];
 
-    if (!tag || !*tag) {
-        return def;
+    if (!cache->pinfo) {
+        cache->pinfo = __system_property_find(key);
+        if (!cache->pinfo) {
+            return;
+        }
     }
-    {
-        static const char log_namespace[] = "persist.log.tag.";
-        char key[sizeof(log_namespace) + strlen(tag)];
+    serial = __system_property_serial(cache->pinfo);
+    if (serial == cache->serial) {
+        return;
+    }
+    cache->serial = serial;
+    __system_property_read(cache->pinfo, 0, buf);
+    cache->c = buf[0];
+}
 
-        strcpy(key, log_namespace);
+static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
+
+static int __android_log_level(const char *tag, int def)
+{
+    /* sizeof() is used on this array below */
+    static const char log_namespace[] = "persist.log.tag.";
+    static const size_t base_offset = 8; /* skip "persist." */
+    /* calculate the size of our key temporary buffer */
+    const size_t taglen = (tag && *tag) ? strlen(tag) : 0;
+    /* sizeof(log_namespace) = strlen(log_namespace) + 1 */
+    char key[sizeof(log_namespace) + taglen];
+    char *kp;
+    size_t i;
+    char c = 0;
+    /*
+     * Single layer cache of four properties. Priorities are:
+     *    log.tag.<tag>
+     *    persist.log.tag.<tag>
+     *    log.tag
+     *    persist.log.tag
+     * Where the missing tag matches all tags and becomes the
+     * system global default. We do not support ro.log.tag* .
+     */
+    static char *last_tag;
+    static uint32_t global_serial;
+    uint32_t current_global_serial;
+    static struct cache tag_cache[2] = {
+        { NULL, -1, 0 },
+        { NULL, -1, 0 }
+    };
+    static struct cache global_cache[2] = {
+        { NULL, -1, 0 },
+        { NULL, -1, 0 }
+    };
+
+    strcpy(key, log_namespace);
+
+    pthread_mutex_lock(&lock);
+
+    current_global_serial = __system_property_area_serial();
+
+    if (taglen) {
+        uint32_t current_local_serial = current_global_serial;
+
+        if (!last_tag || strcmp(last_tag, tag)) {
+            /* invalidate log.tag.<tag> cache */
+            for(i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
+                tag_cache[i].pinfo = NULL;
+                tag_cache[i].serial = -1;
+                tag_cache[i].c = '\0';
+            }
+            free(last_tag);
+            last_tag = NULL;
+            current_global_serial = -1;
+        }
+        if (!last_tag) {
+            last_tag = strdup(tag);
+        }
         strcpy(key + sizeof(log_namespace) - 1, tag);
 
-        if (__system_property_get(key + 8, buf) <= 0) {
-            buf[0] = '\0';
-        }
-        if (!buf[0] && __system_property_get(key, buf) <= 0) {
-            buf[0] = '\0';
+        kp = key;
+        for(i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
+            if (current_local_serial != global_serial) {
+                refresh_cache(&tag_cache[i], kp);
+            }
+
+            if (tag_cache[i].c) {
+                c = tag_cache[i].c;
+                break;
+            }
+
+            kp = key + base_offset;
         }
     }
-    switch (toupper(buf[0])) {
-        case 'V': return ANDROID_LOG_VERBOSE;
-        case 'D': return ANDROID_LOG_DEBUG;
-        case 'I': return ANDROID_LOG_INFO;
-        case 'W': return ANDROID_LOG_WARN;
-        case 'E': return ANDROID_LOG_ERROR;
-        case 'F': /* FALLTHRU */ /* Not officially supported */
-        case 'A': return ANDROID_LOG_FATAL;
-        case 'S': return -1; /* ANDROID_LOG_SUPPRESS */
+
+    switch (toupper(c)) { /* if invalid, resort to global */
+    case 'V':
+    case 'D':
+    case 'I':
+    case 'W':
+    case 'E':
+    case 'F': /* Not officially supported */
+    case 'A':
+    case 'S':
+        break;
+    default:
+        /* clear '.' after log.tag */
+        key[sizeof(log_namespace) - 2] = '\0';
+
+        kp = key;
+        for(i = 0; i < (sizeof(global_cache) / sizeof(global_cache[0])); ++i) {
+            if (current_global_serial != global_serial) {
+                refresh_cache(&global_cache[i], kp);
+            }
+
+            if (global_cache[i].c) {
+                c = global_cache[i].c;
+                break;
+            }
+
+            kp = key + base_offset;
+        }
+        break;
+    }
+
+    global_serial = current_global_serial;
+
+    pthread_mutex_unlock(&lock);
+
+    switch (toupper(c)) {
+    case 'V': return ANDROID_LOG_VERBOSE;
+    case 'D': return ANDROID_LOG_DEBUG;
+    case 'I': return ANDROID_LOG_INFO;
+    case 'W': return ANDROID_LOG_WARN;
+    case 'E': return ANDROID_LOG_ERROR;
+    case 'F': /* FALLTHRU */ /* Not officially supported */
+    case 'A': return ANDROID_LOG_FATAL;
+    case 'S': return -1; /* ANDROID_LOG_SUPPRESS */
     }
     return def;
 }
diff --git a/liblog/tests/Android.mk b/liblog/tests/Android.mk
index d75bbc9..a407c50 100644
--- a/liblog/tests/Android.mk
+++ b/liblog/tests/Android.mk
@@ -77,6 +77,6 @@
 LOCAL_MODULE := $(test_module_prefix)unit-tests
 LOCAL_MODULE_TAGS := $(test_tags)
 LOCAL_CFLAGS += $(test_c_flags)
-LOCAL_SHARED_LIBRARIES := liblog
+LOCAL_SHARED_LIBRARIES := liblog libcutils
 LOCAL_SRC_FILES := $(test_src_files)
 include $(BUILD_NATIVE_TEST)
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 33f6481..abe0239 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -17,6 +17,8 @@
 #include <fcntl.h>
 #include <inttypes.h>
 #include <signal.h>
+
+#include <cutils/properties.h>
 #include <gtest/gtest.h>
 #include <log/log.h>
 #include <log/logger.h>
@@ -439,6 +441,7 @@
 
     LOG_FAILURE_RETRY(__android_log_buf_write(LOG_ID_SYSTEM, ANDROID_LOG_INFO,
                                               tag, max_payload_buf));
+    sleep(2);
 
     struct logger_list *logger_list;
 
@@ -603,10 +606,14 @@
         if (id != android_name_to_log_id(name)) {
             continue;
         }
+        fprintf(stderr, "log buffer %s\r", name);
         struct logger * logger;
         EXPECT_TRUE(NULL != (logger = android_logger_open(logger_list, id)));
         EXPECT_EQ(id, android_logger_get_id(logger));
-        EXPECT_LT(0, android_logger_get_log_size(logger));
+        /* crash buffer is allowed to be empty, that is actually healthy! */
+        if (android_logger_get_log_size(logger) || strcmp("crash", name)) {
+            EXPECT_LT(0, android_logger_get_log_size(logger));
+        }
         EXPECT_LT(0, android_logger_get_log_readable_size(logger));
         EXPECT_LT(0, android_logger_get_log_version(logger));
     }
@@ -682,3 +689,190 @@
 
     android_log_format_free(p_format);
 }
+
+TEST(liblog, is_loggable) {
+    static const char tag[] = "is_loggable";
+    static const char log_namespace[] = "persist.log.tag.";
+    static const size_t base_offset = 8; /* skip "persist." */
+    // sizeof("string") = strlen("string") + 1
+    char key[sizeof(log_namespace) + sizeof(tag) - 1];
+    char hold[4][PROP_VALUE_MAX];
+    static const struct {
+        int level;
+        char type;
+    } levels[] = {
+        { ANDROID_LOG_VERBOSE, 'v' },
+        { ANDROID_LOG_DEBUG  , 'd' },
+        { ANDROID_LOG_INFO   , 'i' },
+        { ANDROID_LOG_WARN   , 'w' },
+        { ANDROID_LOG_ERROR  , 'e' },
+        { ANDROID_LOG_FATAL  , 'a' },
+        { -1                 , 's' },
+        { -2                 , 'g' }, // Illegal value, resort to default
+    };
+
+    // Set up initial test condition
+    memset(hold, 0, sizeof(hold));
+    snprintf(key, sizeof(key), "%s%s", log_namespace, tag);
+    property_get(key, hold[0], "");
+    property_set(key, "");
+    property_get(key + base_offset, hold[1], "");
+    property_set(key + base_offset, "");
+    strcpy(key, log_namespace);
+    key[sizeof(log_namespace) - 2] = '\0';
+    property_get(key, hold[2], "");
+    property_set(key, "");
+    property_get(key, hold[3], "");
+    property_set(key + base_offset, "");
+
+    // All combinations of level and defaults
+    for(size_t i = 0; i < (sizeof(levels) / sizeof(levels[0])); ++i) {
+        if (levels[i].level == -2) {
+            continue;
+        }
+        for(size_t j = 0; j < (sizeof(levels) / sizeof(levels[0])); ++j) {
+            if (levels[j].level == -2) {
+                continue;
+            }
+            fprintf(stderr, "i=%zu j=%zu\r", i, j);
+            if ((levels[i].level < levels[j].level)
+                    || (levels[j].level == -1)) {
+                EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
+                                                       levels[j].level));
+            } else {
+                EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
+                                                      levels[j].level));
+            }
+        }
+    }
+
+    // All combinations of level and tag and global properties
+    for(size_t i = 0; i < (sizeof(levels) / sizeof(levels[0])); ++i) {
+        if (levels[i].level == -2) {
+            continue;
+        }
+        for(size_t j = 0; j < (sizeof(levels) / sizeof(levels[0])); ++j) {
+            char buf[2];
+            buf[0] = levels[j].type;
+            buf[1] = '\0';
+
+            snprintf(key, sizeof(key), "%s%s", log_namespace, tag);
+            fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r",
+                    i, j, key, buf);
+            property_set(key, buf);
+            if ((levels[i].level < levels[j].level)
+                    || (levels[j].level == -1)
+                    || ((levels[i].level < ANDROID_LOG_DEBUG)
+                        && (levels[j].level == -2))) {
+                EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
+                                                       ANDROID_LOG_DEBUG));
+            } else {
+                EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
+                                                      ANDROID_LOG_DEBUG));
+            }
+            property_set(key, "");
+
+            fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r",
+                    i, j, key + base_offset, buf);
+            property_set(key + base_offset, buf);
+            if ((levels[i].level < levels[j].level)
+                    || (levels[j].level == -1)
+                    || ((levels[i].level < ANDROID_LOG_DEBUG)
+                        && (levels[j].level == -2))) {
+                EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
+                                                       ANDROID_LOG_DEBUG));
+            } else {
+                EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
+                                                      ANDROID_LOG_DEBUG));
+            }
+            property_set(key + base_offset, "");
+
+            strcpy(key, log_namespace);
+            key[sizeof(log_namespace) - 2] = '\0';
+            fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r",
+                    i, j, key, buf);
+            property_set(key, buf);
+            if ((levels[i].level < levels[j].level)
+                    || (levels[j].level == -1)
+                    || ((levels[i].level < ANDROID_LOG_DEBUG)
+                        && (levels[j].level == -2))) {
+                EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
+                                                       ANDROID_LOG_DEBUG));
+            } else {
+                EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
+                                                      ANDROID_LOG_DEBUG));
+            }
+            property_set(key, "");
+
+            fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r",
+                    i, j, key + base_offset, buf);
+            property_set(key + base_offset, buf);
+            if ((levels[i].level < levels[j].level)
+                    || (levels[j].level == -1)
+                    || ((levels[i].level < ANDROID_LOG_DEBUG)
+                        && (levels[j].level == -2))) {
+                EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
+                                                       ANDROID_LOG_DEBUG));
+            } else {
+                EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
+                                                      ANDROID_LOG_DEBUG));
+            }
+            property_set(key + base_offset, "");
+        }
+    }
+
+    // All combinations of level and tag properties, but with global set to INFO
+    strcpy(key, log_namespace);
+    key[sizeof(log_namespace) - 2] = '\0';
+    property_set(key, "I");
+    snprintf(key, sizeof(key), "%s%s", log_namespace, tag);
+    for(size_t i = 0; i < (sizeof(levels) / sizeof(levels[0])); ++i) {
+        if (levels[i].level == -2) {
+            continue;
+        }
+        for(size_t j = 0; j < (sizeof(levels) / sizeof(levels[0])); ++j) {
+            char buf[2];
+            buf[0] = levels[j].type;
+            buf[1] = '\0';
+
+            fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r",
+                    i, j, key, buf);
+            property_set(key, buf);
+            if ((levels[i].level < levels[j].level)
+                    || (levels[j].level == -1)
+                    || ((levels[i].level < ANDROID_LOG_INFO) // Yes INFO
+                        && (levels[j].level == -2))) {
+                EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
+                                                       ANDROID_LOG_DEBUG));
+            } else {
+                EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
+                                                      ANDROID_LOG_DEBUG));
+            }
+            property_set(key, "");
+
+            fprintf(stderr, "i=%zu j=%zu property_set(\"%s\",\"%s\")\r",
+                    i, j, key + base_offset, buf);
+            property_set(key + base_offset, buf);
+            if ((levels[i].level < levels[j].level)
+                    || (levels[j].level == -1)
+                    || ((levels[i].level < ANDROID_LOG_INFO) // Yes INFO
+                        && (levels[j].level == -2))) {
+                EXPECT_FALSE(__android_log_is_loggable(levels[i].level, tag,
+                                                       ANDROID_LOG_DEBUG));
+            } else {
+                EXPECT_TRUE(__android_log_is_loggable(levels[i].level, tag,
+                                                      ANDROID_LOG_DEBUG));
+            }
+            property_set(key + base_offset, "");
+        }
+    }
+
+    // reset parms
+    snprintf(key, sizeof(key), "%s%s", log_namespace, tag);
+    property_set(key, hold[0]);
+    property_set(key + base_offset, hold[1]);
+    strcpy(key, log_namespace);
+    key[sizeof(log_namespace) - 2] = '\0';
+    property_set(key, hold[2]);
+    property_set(key + base_offset, hold[3]);
+}
diff --git a/libutils/Android.mk b/libutils/Android.mk
index e9c5f89..8f829f3 100644
--- a/libutils/Android.mk
+++ b/libutils/Android.mk
@@ -91,7 +91,7 @@
         liblog \
         libdl
 
-LOCAL_MODULE:= libutils
+LOCAL_MODULE := libutils
 include $(BUILD_STATIC_LIBRARY)
 
 # For the device, shared
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index d7af34e..3716343 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -307,7 +307,7 @@
    * ((4 * UINT16_MAX) / 3 + 1) which can safely fit into a uint32_t.
    */
   uint32_t hash_table_size;
-  ZipEntryName* hash_table;
+  ZipString* hash_table;
 
   ZipArchive(const int fd, bool assume_ownership) :
       fd(fd),
@@ -343,7 +343,7 @@
   return val;
 }
 
-static uint32_t ComputeHash(const ZipEntryName& name) {
+static uint32_t ComputeHash(const ZipString& name) {
   uint32_t hash = 0;
   uint16_t len = name.name_length;
   const uint8_t* str = name.name;
@@ -359,16 +359,15 @@
  * Convert a ZipEntry to a hash table index, verifying that it's in a
  * valid range.
  */
-static int64_t EntryToIndex(const ZipEntryName* hash_table,
+static int64_t EntryToIndex(const ZipString* hash_table,
                             const uint32_t hash_table_size,
-                            const ZipEntryName& name) {
+                            const ZipString& name) {
   const uint32_t hash = ComputeHash(name);
 
   // NOTE: (hash_table_size - 1) is guaranteed to be non-negative.
   uint32_t ent = hash & (hash_table_size - 1);
   while (hash_table[ent].name != NULL) {
-    if (hash_table[ent].name_length == name.name_length &&
-        memcmp(hash_table[ent].name, name.name, name.name_length) == 0) {
+    if (hash_table[ent] == name) {
       return ent;
     }
 
@@ -382,8 +381,8 @@
 /*
  * Add a new entry to the hash table.
  */
-static int32_t AddToHash(ZipEntryName *hash_table, const uint64_t hash_table_size,
-                         const ZipEntryName& name) {
+static int32_t AddToHash(ZipString *hash_table, const uint64_t hash_table_size,
+                         const ZipString& name) {
   const uint64_t hash = ComputeHash(name);
   uint32_t ent = hash & (hash_table_size - 1);
 
@@ -392,8 +391,7 @@
    * Further, we guarantee that the hashtable size is not 0.
    */
   while (hash_table[ent].name != NULL) {
-    if (hash_table[ent].name_length == name.name_length &&
-        memcmp(hash_table[ent].name, name.name, name.name_length) == 0) {
+    if (hash_table[ent] == name) {
       // We've found a duplicate entry. We don't accept it
       ALOGW("Zip: Found duplicate entry %.*s", name.name_length, name.name);
       return kDuplicateEntry;
@@ -565,8 +563,8 @@
    * least one unused entry to avoid an infinite loop during creation.
    */
   archive->hash_table_size = RoundUpPower2(1 + (num_entries * 4) / 3);
-  archive->hash_table = reinterpret_cast<ZipEntryName*>(calloc(archive->hash_table_size,
-      sizeof(ZipEntryName)));
+  archive->hash_table = reinterpret_cast<ZipString*>(calloc(archive->hash_table_size,
+      sizeof(ZipString)));
 
   /*
    * Walk through the central directory, adding entries to the hash
@@ -605,7 +603,7 @@
     }
 
     /* add the CDE filename to the hash table */
-    ZipEntryName entry_name;
+    ZipString entry_name;
     entry_name.name = file_name;
     entry_name.name_length = file_name_length;
     const int add_result = AddToHash(archive->hash_table,
@@ -851,26 +849,41 @@
   uint32_t position;
   // We're not using vector here because this code is used in the Windows SDK
   // where the STL is not available.
-  const uint8_t* prefix;
-  uint16_t prefix_len;
+  ZipString prefix;
+  ZipString suffix;
   ZipArchive* archive;
 
-  IterationHandle() : prefix(NULL), prefix_len(0) {}
-
-  IterationHandle(const ZipEntryName& prefix_name)
-      : prefix_len(prefix_name.name_length) {
-    uint8_t* prefix_copy = new uint8_t[prefix_len];
-    memcpy(prefix_copy, prefix_name.name, prefix_len);
-    prefix = prefix_copy;
+  IterationHandle(const ZipString* in_prefix,
+                  const ZipString* in_suffix) {
+    if (in_prefix) {
+      uint8_t* name_copy = new uint8_t[in_prefix->name_length];
+      memcpy(name_copy, in_prefix->name, in_prefix->name_length);
+      prefix.name = name_copy;
+      prefix.name_length = in_prefix->name_length;
+    } else {
+      prefix.name = NULL;
+      prefix.name_length = 0;
+    }
+    if (in_suffix) {
+      uint8_t* name_copy = new uint8_t[in_suffix->name_length];
+      memcpy(name_copy, in_suffix->name, in_suffix->name_length);
+      suffix.name = name_copy;
+      suffix.name_length = in_suffix->name_length;
+    } else {
+      suffix.name = NULL;
+      suffix.name_length = 0;
+    }
   }
 
   ~IterationHandle() {
-    delete[] prefix;
+    delete[] prefix.name;
+    delete[] suffix.name;
   }
 };
 
 int32_t StartIteration(ZipArchiveHandle handle, void** cookie_ptr,
-                       const ZipEntryName* optional_prefix) {
+                       const ZipString* optional_prefix,
+                       const ZipString* optional_suffix) {
   ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
 
   if (archive == NULL || archive->hash_table == NULL) {
@@ -878,8 +891,7 @@
     return kInvalidHandle;
   }
 
-  IterationHandle* cookie =
-      optional_prefix != NULL ? new IterationHandle(*optional_prefix) : new IterationHandle();
+  IterationHandle* cookie = new IterationHandle(optional_prefix, optional_suffix);
   cookie->position = 0;
   cookie->archive = archive;
 
@@ -891,7 +903,7 @@
   delete reinterpret_cast<IterationHandle*>(cookie);
 }
 
-int32_t FindEntry(const ZipArchiveHandle handle, const ZipEntryName& entryName,
+int32_t FindEntry(const ZipArchiveHandle handle, const ZipString& entryName,
                   ZipEntry* data) {
   const ZipArchive* archive = reinterpret_cast<ZipArchive*>(handle);
   if (entryName.name_length == 0) {
@@ -910,7 +922,7 @@
   return FindEntry(archive, ent, data);
 }
 
-int32_t Next(void* cookie, ZipEntry* data, ZipEntryName* name) {
+int32_t Next(void* cookie, ZipEntry* data, ZipString* name) {
   IterationHandle* handle = reinterpret_cast<IterationHandle*>(cookie);
   if (handle == NULL) {
     return kInvalidHandle;
@@ -924,12 +936,14 @@
 
   const uint32_t currentOffset = handle->position;
   const uint32_t hash_table_length = archive->hash_table_size;
-  const ZipEntryName *hash_table = archive->hash_table;
+  const ZipString* hash_table = archive->hash_table;
 
   for (uint32_t i = currentOffset; i < hash_table_length; ++i) {
     if (hash_table[i].name != NULL &&
-        (handle->prefix_len == 0 ||
-         (memcmp(handle->prefix, hash_table[i].name, handle->prefix_len) == 0))) {
+        (handle->prefix.name_length == 0 ||
+         hash_table[i].StartsWith(handle->prefix)) &&
+        (handle->suffix.name_length == 0 ||
+         hash_table[i].EndsWith(handle->suffix))) {
       handle->position = (i + 1);
       const int error = FindEntry(archive, i, data);
       if (!error) {
@@ -1265,4 +1279,3 @@
 int GetFileDescriptor(const ZipArchiveHandle handle) {
   return reinterpret_cast<ZipArchive*>(handle)->fd;
 }
-
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index f8952ce..9a3cdb4 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -70,7 +70,7 @@
 }
 
 static void AssertNameEquals(const std::string& name_str,
-                             const ZipEntryName& name) {
+                             const ZipString& name) {
   ASSERT_EQ(name_str.size(), name.name_length);
   ASSERT_EQ(0, memcmp(name_str.c_str(), name.name, name.name_length));
 }
@@ -115,10 +115,10 @@
   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
 
   void* iteration_cookie;
-  ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, NULL));
+  ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, NULL, NULL));
 
   ZipEntry data;
-  ZipEntryName name;
+  ZipString name;
 
   // b/c.txt
   ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
@@ -146,12 +146,122 @@
   CloseArchive(handle);
 }
 
+TEST(ziparchive, IterationWithPrefix) {
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+  void* iteration_cookie;
+  ZipString prefix("b/");
+  ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, &prefix, NULL));
+
+  ZipEntry data;
+  ZipString name;
+
+  // b/c.txt
+  ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
+  AssertNameEquals("b/c.txt", name);
+
+  // b/d.txt
+  ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
+  AssertNameEquals("b/d.txt", name);
+
+  // b/
+  ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
+  AssertNameEquals("b/", name);
+
+  // End of iteration.
+  ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
+
+  CloseArchive(handle);
+}
+
+TEST(ziparchive, IterationWithSuffix) {
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+  void* iteration_cookie;
+  ZipString suffix(".txt");
+  ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, NULL, &suffix));
+
+  ZipEntry data;
+  ZipString name;
+
+  // b/c.txt
+  ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
+  AssertNameEquals("b/c.txt", name);
+
+  // b/d.txt
+  ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
+  AssertNameEquals("b/d.txt", name);
+
+  // a.txt
+  ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
+  AssertNameEquals("a.txt", name);
+
+  // b.txt
+  ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
+  AssertNameEquals("b.txt", name);
+
+  // End of iteration.
+  ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
+
+  CloseArchive(handle);
+}
+
+TEST(ziparchive, IterationWithPrefixAndSuffix) {
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+  void* iteration_cookie;
+  ZipString prefix("b");
+  ZipString suffix(".txt");
+  ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, &prefix, &suffix));
+
+  ZipEntry data;
+  ZipString name;
+
+  // b/c.txt
+  ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
+  AssertNameEquals("b/c.txt", name);
+
+  // b/d.txt
+  ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
+  AssertNameEquals("b/d.txt", name);
+
+  // b.txt
+  ASSERT_EQ(0, Next(iteration_cookie, &data, &name));
+  AssertNameEquals("b.txt", name);
+
+  // End of iteration.
+  ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
+
+  CloseArchive(handle);
+}
+
+TEST(ziparchive, IterationWithBadPrefixAndSuffix) {
+  ZipArchiveHandle handle;
+  ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
+
+  void* iteration_cookie;
+  ZipString prefix("x");
+  ZipString suffix("y");
+  ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, &prefix, &suffix));
+
+  ZipEntry data;
+  ZipString name;
+
+  // End of iteration.
+  ASSERT_EQ(-1, Next(iteration_cookie, &data, &name));
+
+  CloseArchive(handle);
+}
+
 TEST(ziparchive, FindEntry) {
   ZipArchiveHandle handle;
   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
 
   ZipEntry data;
-  ZipEntryName name;
+  ZipString name;
   name.name = kATxtName;
   name.name_length = kATxtNameLength;
   ASSERT_EQ(0, FindEntry(handle, name, &data));
@@ -164,7 +274,7 @@
   ASSERT_EQ(0x950821c5, data.crc32);
 
   // An entry that doesn't exist. Should be a negative return code.
-  ZipEntryName absent_name;
+  ZipString absent_name;
   absent_name.name = kNonexistentTxtName;
   absent_name.name_length = kNonexistentTxtNameLength;
   ASSERT_LT(FindEntry(handle, absent_name, &data), 0);
@@ -177,9 +287,9 @@
   ASSERT_EQ(0, OpenArchiveWrapper("declaredlength.zip", &handle));
 
   void* iteration_cookie;
-  ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, NULL));
+  ASSERT_EQ(0, StartIteration(handle, &iteration_cookie, NULL, NULL));
 
-  ZipEntryName name;
+  ZipString name;
   ZipEntry data;
 
   ASSERT_EQ(Next(iteration_cookie, &data, &name), 0);
@@ -194,7 +304,7 @@
 
   // An entry that's deflated.
   ZipEntry data;
-  ZipEntryName a_name;
+  ZipString a_name;
   a_name.name = kATxtName;
   a_name.name_length = kATxtNameLength;
   ASSERT_EQ(0, FindEntry(handle, a_name, &data));
@@ -206,7 +316,7 @@
   delete[] buffer;
 
   // An entry that's stored.
-  ZipEntryName b_name;
+  ZipString b_name;
   b_name.name = kBTxtName;
   b_name.name_length = kBTxtNameLength;
   ASSERT_EQ(0, FindEntry(handle, b_name, &data));
@@ -293,7 +403,7 @@
   ASSERT_EQ(0, OpenArchiveFd(fd, "EmptyEntriesTest", &handle));
 
   ZipEntry entry;
-  ZipEntryName empty_name;
+  ZipString empty_name;
   empty_name.name = kEmptyTxtName;
   empty_name.name_length = kEmptyTxtNameLength;
   ASSERT_EQ(0, FindEntry(handle, empty_name, &entry));
@@ -324,7 +434,7 @@
   ASSERT_EQ(0, OpenArchiveFd(fd, "EntryLargerThan32KTest", &handle));
 
   ZipEntry entry;
-  ZipEntryName ab_name;
+  ZipString ab_name;
   ab_name.name = kAbTxtName;
   ab_name.name_length = kAbTxtNameLength;
   ASSERT_EQ(0, FindEntry(handle, ab_name, &entry));
@@ -392,7 +502,7 @@
   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
 
   ZipEntry entry;
-  ZipEntryName name;
+  ZipString name;
   name.name = kATxtName;
   name.name_length = kATxtNameLength;
   ASSERT_EQ(0, FindEntry(handle, name, &entry));
diff --git a/logcat/logcat.cpp b/logcat/logcat.cpp
index 736e02e..e598bb8 100644
--- a/logcat/logcat.cpp
+++ b/logcat/logcat.cpp
@@ -14,6 +14,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <sys/cdefs.h>
+#include <sys/resource.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
 #include <sys/types.h>
@@ -33,6 +34,7 @@
 #include <log/logd.h>
 #include <log/logger.h>
 #include <log/logprint.h>
+#include <utils/threads.h>
 
 #define DEFAULT_MAX_ROTATED_LOGS 4
 
@@ -221,6 +223,10 @@
             fprintf(stderr, "failed to set to batch scheduler\n");
         }
 
+        if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) {
+            fprintf(stderr, "failed set to priority\n");
+        }
+
         g_outFD = openLogFile (g_outputFileName);
 
         if (g_outFD < 0) {
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index 4ec2e59..4b3547c 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -145,7 +145,9 @@
             ++cp;
         }
         tid = pid;
+        logbuf->lock();
         uid = logbuf->pidToUid(pid);
+        logbuf->unlock();
         memmove(pidptr, cp, strlen(cp) + 1);
     }
 
@@ -180,14 +182,20 @@
     static const char comm_str[] = " comm=\"";
     const char *comm = strstr(str, comm_str);
     const char *estr = str + strlen(str);
+    char *commfree = NULL;
     if (comm) {
         estr = comm;
         comm += sizeof(comm_str) - 1;
     } else if (pid == getpid()) {
         pid = tid;
         comm = "auditd";
-    } else if (!(comm = logbuf->pidToName(pid))) {
-        comm = "unknown";
+    } else {
+        logbuf->lock();
+        comm = commfree = logbuf->pidToName(pid);
+        logbuf->unlock();
+        if (!comm) {
+            comm = "unknown";
+        }
     }
 
     const char *ecomm = strchr(comm, '"');
@@ -218,6 +226,7 @@
         }
     }
 
+    free(commfree);
     free(str);
 
     if (notify) {
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 4373e2a..0f5071b 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -140,8 +140,26 @@
     if ((log_id >= LOG_ID_MAX) || (log_id < 0)) {
         return -EINVAL;
     }
+
     LogBufferElement *elem = new LogBufferElement(log_id, realtime,
                                                   uid, pid, tid, msg, len);
+    int prio = ANDROID_LOG_INFO;
+    const char *tag = NULL;
+    if (log_id == LOG_ID_EVENTS) {
+        tag = android::tagToName(elem->getTag());
+    } else {
+        prio = *msg;
+        tag = msg + 1;
+    }
+    if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {
+        // Log traffic received to total
+        pthread_mutex_lock(&mLogElementsLock);
+        stats.add(elem);
+        stats.subtract(elem);
+        pthread_mutex_unlock(&mLogElementsLock);
+        delete elem;
+        return -EACCES;
+    }
 
     pthread_mutex_lock(&mLogElementsLock);
 
diff --git a/logd/LogBuffer.h b/logd/LogBuffer.h
index 00b19b6..a13fded 100644
--- a/logd/LogBuffer.h
+++ b/logd/LogBuffer.h
@@ -71,10 +71,12 @@
     // *strp uses malloc, use free to release.
     void formatPrune(char **strp) { mPrune.format(strp); }
 
-    // helper
+    // helper must be protected directly or implicitly by lock()/unlock()
     char *pidToName(pid_t pid) { return stats.pidToName(pid); }
     uid_t pidToUid(pid_t pid) { return stats.pidToUid(pid); }
     char *uidToName(uid_t uid) { return stats.uidToName(uid); }
+    void lock() { pthread_mutex_lock(&mLogElementsLock); }
+    void unlock() { pthread_mutex_unlock(&mLogElementsLock); }
 
 private:
     void maybePrune(log_id_t id);
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index 8238a52..9fb1439 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -30,7 +30,7 @@
 #include "LogReader.h"
 
 const uint64_t LogBufferElement::FLUSH_ERROR(0);
-atomic_int_fast64_t LogBufferElement::sequence;
+atomic_int_fast64_t LogBufferElement::sequence(1);
 
 LogBufferElement::LogBufferElement(log_id_t log_id, log_time realtime,
                                    uid_t uid, pid_t pid, pid_t tid,
@@ -105,15 +105,23 @@
 size_t LogBufferElement::populateDroppedMessage(char *&buffer,
         LogBuffer *parent) {
     static const char tag[] = "chatty";
-    static const char format_uid[] = "uid=%u%s%s expire %u line%s";
 
+    if (!__android_log_is_loggable(ANDROID_LOG_INFO, tag, ANDROID_LOG_VERBOSE)) {
+        return 0;
+    }
+
+    static const char format_uid[] = "uid=%u%s%s expire %u line%s";
+    parent->lock();
     char *name = parent->uidToName(mUid);
+    parent->unlock();
     char *commName = android::tidToName(mTid);
     if (!commName && (mTid != mPid)) {
         commName = android::tidToName(mPid);
     }
     if (!commName) {
+        parent->lock();
         commName = parent->pidToName(mPid);
+        parent->unlock();
     }
     size_t len = name ? strlen(name) : 0;
     if (len && commName && !strncmp(name, commName, len)) {
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index 7d14648..d578c04 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -45,8 +45,8 @@
     }
     char c;
     while ((c = *s++)) {
-        if (!isdigit(c) && (c == '>')) {
-            return s;
+        if (!isdigit(c)) {
+            return (c == '>') ? s : NULL;
         }
     }
     return NULL;
@@ -65,17 +65,15 @@
     while ((c = *s++)) {
         if ((c == '.') && first_period) {
             first_period = false;
-            continue;
-        }
-        if (!isdigit(c) && (c == ']')) {
-            return s;
+        } else if (!isdigit(c)) {
+            return ((c == ']') && !first_period && (*s == ' ')) ? s : NULL;
         }
     }
     return NULL;
 }
 
 // Like strtok_r with "\r\n" except that we look for log signatures (regex)
-//   \(\(<[0-9]+>\)\([[] *[0-9]+[]]\)\{0,1\}\|[[] *[0-9]+[]]\)
+// \(\(<[0-9]+>\)\([[] *[0-9]+[.][0-9]+[]] \)\{0,1\}\|[[] *[0-9]+[.][0-9]+[]] \)
 // and split if we see a second one without a newline.
 
 #define SIGNATURE_MASK     0xF0
@@ -547,10 +545,21 @@
         }
     }
     size_t l = etag - tag;
+    // skip leading space
     while (isspace(*buf)) {
         ++buf;
     }
-    size_t n = 1 + l + 1 + strlen(buf) + 1;
+    // truncate trailing space
+    size_t b = strlen(buf);
+    while (b && isspace(buf[b-1])) {
+        --b;
+    }
+    // trick ... allow tag with empty content to be logged. log() drops empty
+    if (!b && l) {
+        buf = " ";
+        b = 1;
+    }
+    size_t n = 1 + l + 1 + b + 1;
 
     // Allocate a buffer to hold the interpreted log message
     int rc = n;
@@ -572,7 +581,8 @@
     ++np;
 
     // Copy main message to the remainder
-    strcpy(np, buf);
+    strncpy(np, buf, b);
+    np[b] = '\0';
 
     // Log message
     rc = logbuf->log(LOG_ID_KERNEL, now, uid, pid, tid, newstr,
diff --git a/logd/LogStatistics.h b/logd/LogStatistics.h
index b9e9650..760d6b2 100644
--- a/logd/LogStatistics.h
+++ b/logd/LogStatistics.h
@@ -334,7 +334,7 @@
     // *strp = malloc, balance with free
     void format(char **strp, uid_t uid, unsigned int logMask);
 
-    // helper
+    // helper (must be locked directly or implicitly by mLogElementsLock)
     char *pidToName(pid_t pid);
     uid_t pidToUid(pid_t pid);
     char *uidToName(uid_t uid);
diff --git a/logd/main.cpp b/logd/main.cpp
index 805cbe6..9b88983 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -27,16 +27,20 @@
 #include <sys/capability.h>
 #include <sys/klog.h>
 #include <sys/prctl.h>
+#include <sys/resource.h>
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <syslog.h>
 #include <unistd.h>
 
+#include <memory>
+
 #include <cutils/properties.h>
 #include <cutils/sched_policy.h>
 #include <cutils/sockets.h>
 #include <log/event_tag_map.h>
 #include <private/android_filesystem_config.h>
+#include <utils/threads.h>
 
 #include "CommandListener.h"
 #include "LogBuffer.h"
@@ -91,6 +95,10 @@
         return -1;
     }
 
+    if (setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND) < 0) {
+        return -1;
+    }
+
     if (prctl(PR_SET_KEEPCAPS, 1) < 0) {
         return -1;
     }
@@ -156,6 +164,7 @@
 static void *reinit_thread_start(void * /*obj*/) {
     prctl(PR_SET_NAME, "logd.daemon");
     set_sched_policy(0, SP_BACKGROUND);
+    setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_BACKGROUND);
 
     setgid(AID_SYSTEM);
     setuid(AID_SYSTEM);
@@ -268,6 +277,45 @@
             && !property_get_bool("ro.config.low_ram", false));
 }
 
+static void readDmesg(LogAudit *al, LogKlog *kl) {
+    if (!al && !kl) {
+        return;
+    }
+
+    int len = klogctl(KLOG_SIZE_BUFFER, NULL, 0);
+    if (len <= 0) {
+        return;
+    }
+
+    len += 1024; // Margin for additional input race or trailing nul
+    std::unique_ptr<char []> buf(new char[len]);
+
+    int rc = klogctl(KLOG_READ_ALL, buf.get(), len);
+    if (rc <= 0) {
+        return;
+    }
+
+    if (rc < len) {
+        len = rc + 1;
+    }
+    buf[len - 1] = '\0';
+
+    if (kl) {
+        kl->synchronize(buf.get());
+    }
+
+    for (char *ptr = NULL, *tok = buf.get();
+         (rc >= 0) && ((tok = log_strtok_r(tok, &ptr)));
+         tok = NULL) {
+        if (al) {
+            rc = al->log(tok);
+        }
+        if (kl) {
+            rc = kl->log(tok);
+        }
+    }
+}
+
 // 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
@@ -403,41 +451,16 @@
         kl = new LogKlog(logBuf, reader, fdDmesg, fdPmesg, al != NULL);
     }
 
-    if (al || kl) {
-        int len = klogctl(KLOG_SIZE_BUFFER, NULL, 0);
-        if (len > 0) {
-            len++;
-            char buf[len];
+    readDmesg(al, kl);
 
-            int rc = klogctl(KLOG_READ_ALL, buf, len);
+    // failure is an option ... messages are in dmesg (required by standard)
 
-            buf[len - 1] = '\0';
+    if (kl && kl->startListener()) {
+        delete kl;
+    }
 
-            if ((rc >= 0) && kl) {
-                kl->synchronize(buf);
-            }
-
-            for (char *ptr = NULL, *tok = buf;
-                 (rc >= 0) && ((tok = log_strtok_r(tok, &ptr)));
-                 tok = NULL) {
-                if (al) {
-                    rc = al->log(tok);
-                }
-                if (kl) {
-                    rc = kl->log(tok);
-                }
-            }
-        }
-
-        // failure is an option ... messages are in dmesg (required by standard)
-
-        if (kl && kl->startListener()) {
-            delete kl;
-        }
-
-        if (al && al->startListener()) {
-            delete al;
-        }
+    if (al && al->startListener()) {
+        delete al;
     }
 
     TEMP_FAILURE_RETRY(pause());
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 7ab76b8..05d8fe0 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -30,6 +30,11 @@
 
 include $(BUILD_SYSTEM)/base_rules.mk
 
+EXPORT_GLOBAL_ASAN_OPTIONS :=
+ifeq (address,$(strip $(SANITIZE_TARGET)))
+  EXPORT_GLOBAL_ASAN_OPTIONS := export ASAN_OPTIONS allow_user_segv_handler=1:detect_odr_violation=0:alloc_dealloc_mismatch=0
+endif
+
 # Regenerate init.environ.rc if PRODUCT_BOOTCLASSPATH has changed.
 bcp_md5 := $(word 1, $(shell echo $(PRODUCT_BOOTCLASSPATH) $(PRODUCT_SYSTEM_SERVER_CLASSPATH) | $(MD5SUM)))
 bcp_dep := $(intermediates)/$(bcp_md5).bcp.dep
@@ -41,6 +46,7 @@
 	@mkdir -p $(dir $@)
 	$(hide) sed -e 's?%BOOTCLASSPATH%?$(PRODUCT_BOOTCLASSPATH)?g' $< >$@
 	$(hide) sed -i -e 's?%SYSTEMSERVERCLASSPATH%?$(PRODUCT_SYSTEM_SERVER_CLASSPATH)?g' $@
+	$(hide) sed -i -e 's?%EXPORT_GLOBAL_ASAN_OPTIONS%?$(EXPORT_GLOBAL_ASAN_OPTIONS)?g' $@
 
 bcp_md5 :=
 bcp_dep :=
diff --git a/rootdir/init.environ.rc.in b/rootdir/init.environ.rc.in
index 0064790..e8b46eb 100644
--- a/rootdir/init.environ.rc.in
+++ b/rootdir/init.environ.rc.in
@@ -9,3 +9,4 @@
     export LOOP_MOUNTPOINT /mnt/obb
     export BOOTCLASSPATH %BOOTCLASSPATH%
     export SYSTEMSERVERCLASSPATH %SYSTEMSERVERCLASSPATH%
+    %EXPORT_GLOBAL_ASAN_OPTIONS%
diff --git a/rootdir/init.rc b/rootdir/init.rc
index b5b74ea..72df0f5 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -194,9 +194,9 @@
     # We restorecon /cache in case the cache partition has been reset.
     restorecon_recursive /cache
 
-    # This may have been created by the recovery system with odd permissions
-    chown system cache /cache/recovery
-    chmod 0770 /cache/recovery
+    # Create /cache/recovery in case it's not there. It'll also fix the odd
+    # permissions if created by the recovery system.
+    mkdir /cache/recovery 0770 system cache
 
     #change permissions on vmallocinfo so we can grab it from bugreports
     chown root log /proc/vmallocinfo
@@ -640,7 +640,12 @@
     disabled
     oneshot
 
-service pre-recovery /system/bin/uncrypt
+service uncrypt /system/bin/uncrypt
+    class main
+    disabled
+    oneshot
+
+service pre-recovery /system/bin/uncrypt --reboot
     class main
     disabled
     oneshot