Merge "adb: win32: fix exec-in and exec-out to use binary mode"
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..97ce125 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -41,6 +41,7 @@
 #include "adb_auth.h"
 #include "adb_io.h"
 #include "adb_listeners.h"
+#include "adb_utils.h"
 #include "transport.h"
 
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
@@ -174,7 +175,7 @@
     for (const auto& elem : elements) {
         const auto& flag = trace_flags.find(elem);
         if (flag == trace_flags.end()) {
-            D("Unknown trace flag: %s", flag->first.c_str());
+            D("Unknown trace flag: %s\n", flag->first.c_str());
             continue;
         }
 
@@ -333,10 +334,10 @@
     D("Calling send_connect \n");
     apacket *cp = get_apacket();
     cp->msg.command = A_CNXN;
-    cp->msg.arg0 = A_VERSION;
-    cp->msg.arg1 = MAX_PAYLOAD;
+    cp->msg.arg0 = t->get_protocol_version();
+    cp->msg.arg1 = t->get_max_payload();
     cp->msg.data_length = fill_connect_data((char *)cp->data,
-                                            sizeof(cp->data));
+                                            MAX_PAYLOAD_V1);
     send_packet(cp, t);
 }
 
@@ -423,17 +424,17 @@
         return;
 
     case A_CNXN: /* CONNECT(version, maxdata, "system-id-string") */
-            /* XXX verify version, etc */
         if(t->connection_state != kCsOffline) {
             t->connection_state = kCsOffline;
             handle_offline(t);
         }
 
+        t->update_version(p->msg.arg0, p->msg.arg1);
         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 +836,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 +854,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 +872,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,83 +890,66 @@
             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;
     }
 
     // remove TCP transport
     if (!strncmp(service, "disconnect:", 11)) {
-        char buffer[4096];
-        memset(buffer, 0, sizeof(buffer));
-        const char* serial = service + 11;
-        if (serial[0] == 0) {
+        const std::string address(service + 11);
+        if (address.empty()) {
             // disconnect from all TCP devices
             unregister_all_tcp_transports();
-        } else {
-            char hostbuf[100];
-            // assume port 5555 if no port is specified
-            if (!strchr(serial, ':')) {
-                snprintf(hostbuf, sizeof(hostbuf) - 1, "%s:5555", serial);
-                serial = hostbuf;
-            }
-            atransport *t = find_transport(serial);
-
-            if (t) {
-                unregister_transport(t);
-            } else {
-                snprintf(buffer, sizeof(buffer), "No such device %s", serial);
-            }
+            return SendOkay(reply_fd, "disconnected everything");
         }
 
-        SendOkay(reply_fd);
-        SendProtocolString(reply_fd, buffer);
-        return 0;
+        std::string serial;
+        std::string host;
+        int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
+        std::string error;
+        if (!parse_host_and_port(address, &serial, &host, &port, &error)) {
+            return SendFail(reply_fd, android::base::StringPrintf("couldn't parse '%s': %s",
+                                                                  address.c_str(), error.c_str()));
+        }
+        atransport* t = find_transport(serial.c_str());
+        if (t == nullptr) {
+            return SendFail(reply_fd, android::base::StringPrintf("no such device '%s'",
+                                                                  serial.c_str()));
+        }
+        unregister_transport(t);
+        return SendOkay(reply_fd, android::base::StringPrintf("disconnected %s", address.c_str()));
     }
 
     // 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.h b/adb/adb.h
index 1be83d7..357be51 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -25,7 +25,9 @@
 #include "adb_trace.h"
 #include "fdevent.h"
 
-#define MAX_PAYLOAD 4096
+constexpr size_t MAX_PAYLOAD_V1 = 4 * 1024;
+constexpr size_t MAX_PAYLOAD_V2 = 256 * 1024;
+constexpr size_t MAX_PAYLOAD = MAX_PAYLOAD_V2;
 
 #define A_SYNC 0x434e5953
 #define A_CNXN 0x4e584e43
@@ -137,6 +139,8 @@
 
         /* A socket is bound to atransport */
     atransport *transport;
+
+    size_t get_max_payload() const;
 };
 
 
@@ -193,6 +197,8 @@
     atransport() {
         auth_fde = {};
         transport_fde = {};
+        protocol_version = A_VERSION;
+        max_payload = MAX_PAYLOAD;
     }
 
     virtual ~atransport() {}
@@ -234,7 +240,14 @@
 
     const char* connection_state_name() const;
 
+    void update_version(int version, size_t payload);
+    int get_protocol_version() const;
+    size_t get_max_payload() const;
+
 private:
+    int protocol_version;
+    size_t max_payload;
+
     DISALLOW_COPY_AND_ASSIGN(atransport);
 };
 
@@ -365,7 +378,9 @@
 
 extern const char *adb_device_banner;
 extern int HOST;
+#if !ADB_HOST
 extern int SHELL_EXIT_NOTIFY_FD;
+#endif // !ADB_HOST
 
 #define CHUNK_SIZE (64*1024)
 
diff --git a/adb/adb_auth.cpp b/adb/adb_auth.cpp
index dc01825..8a6b156 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)
 {
@@ -75,7 +75,7 @@
     apacket *p = get_apacket();
     int ret;
 
-    ret = adb_auth_get_userkey(p->data, sizeof(p->data));
+    ret = adb_auth_get_userkey(p->data, MAX_PAYLOAD_V1);
     if (!ret) {
         D("Failed to get user public key\n");
         put_apacket(p);
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/adb_auth_client.cpp b/adb/adb_auth_client.cpp
index 8e7d38b..be28202 100644
--- a/adb/adb_auth_client.cpp
+++ b/adb/adb_auth_client.cpp
@@ -54,7 +54,7 @@
 static void read_keys(const char *file, struct listnode *list)
 {
     FILE *f;
-    char buf[MAX_PAYLOAD];
+    char buf[MAX_PAYLOAD_V1];
     char *sep;
     int ret;
 
@@ -191,7 +191,7 @@
 
 void adb_auth_confirm_key(unsigned char *key, size_t len, atransport *t)
 {
-    char msg[MAX_PAYLOAD];
+    char msg[MAX_PAYLOAD_V1];
     int ret;
 
     if (!usb_transport) {
@@ -212,7 +212,7 @@
 
     ret = snprintf(msg, sizeof(msg), "PK%s", key);
     if (ret >= (signed)sizeof(msg)) {
-        D("Key too long. ret=%d", ret);
+        D("Key too long. ret=%d\n", ret);
         return;
     }
     D("Sending '%s'\n", msg);
diff --git a/adb/adb_auth_host.cpp b/adb/adb_auth_host.cpp
index e878f8b..b6bb00c 100644
--- a/adb/adb_auth_host.cpp
+++ b/adb/adb_auth_host.cpp
@@ -157,7 +157,7 @@
 {
     RSAPublicKey pkey;
     FILE *outfile = NULL;
-    char path[PATH_MAX], info[MAX_PAYLOAD];
+    char path[PATH_MAX], info[MAX_PAYLOAD_V1];
     uint8_t* encoded = nullptr;
     size_t encoded_length;
     int ret = 0;
@@ -183,7 +183,7 @@
 
 #if defined(OPENSSL_IS_BORINGSSL)
     if (!EVP_EncodedLength(&encoded_length, sizeof(pkey))) {
-        D("Public key too large to base64 encode");
+        D("Public key too large to base64 encode\n");
         goto out;
     }
 #else
@@ -194,7 +194,7 @@
 
     encoded = new uint8_t[encoded_length];
     if (encoded == nullptr) {
-        D("Allocation failure");
+        D("Allocation failure\n");
         goto out;
     }
 
@@ -203,7 +203,7 @@
 
     if (fwrite(encoded, encoded_length, 1, outfile) != 1 ||
         fwrite(info, strlen(info), 1, outfile) != 1) {
-        D("Write error while writing public key");
+        D("Write error while writing public key\n");
         goto out;
     }
 
@@ -323,7 +323,7 @@
 
     if (stat(android_dir, &buf)) {
         if (adb_mkdir(android_dir, 0750) < 0) {
-            D("Cannot mkdir '%s'", android_dir);
+            D("Cannot mkdir '%s'\n", android_dir);
             return -1;
         }
     }
@@ -339,7 +339,7 @@
 
     ret = get_user_keyfilepath(path, sizeof(path));
     if (ret < 0 || ret >= (signed)sizeof(path)) {
-        D("Error getting user key filename");
+        D("Error getting user key filename\n");
         return 0;
     }
 
@@ -414,7 +414,7 @@
     char path[PATH_MAX];
     int ret = get_user_keyfilepath(path, sizeof(path) - 4);
     if (ret < 0 || ret >= (signed)(sizeof(path) - 4)) {
-        D("Error getting user key filename");
+        D("Error getting user key filename\n");
         return 0;
     }
     strcat(path, ".pub");
diff --git a/adb/adb_client.cpp b/adb/adb_client.cpp
index ef9a586..75e888d 100644
--- a/adb/adb_client.cpp
+++ b/adb/adb_client.cpp
@@ -243,7 +243,7 @@
 
     fd = _adb_connect(service, error);
     if (fd == -1) {
-        D("_adb_connect error: %s", error->c_str());
+        D("_adb_connect error: %s\n", error->c_str());
     } else if(fd == -2) {
         fprintf(stderr,"** daemon still not running\n");
     }
@@ -277,8 +277,7 @@
     D("adb_query: %s\n", service.c_str());
     int fd = adb_connect(service, error);
     if (fd < 0) {
-        fprintf(stderr,"error: %s\n", error->c_str());
-        return 0;
+        return false;
     }
 
     result->clear();
diff --git a/adb/adb_listeners.cpp b/adb/adb_listeners.cpp
index a335eec..a15c513 100644
--- a/adb/adb_listeners.cpp
+++ b/adb/adb_listeners.cpp
@@ -118,7 +118,7 @@
             return socket_loopback_server(port, SOCK_STREAM);
         }
     }
-#ifndef HAVE_WIN32_IPC  /* no Unix-domain sockets on Win32 */
+#if !defined(_WIN32)  // No Unix-domain sockets on Windows.
     // It's nonsensical to support the "reserved" space on the adb host side
     if (!strncmp(name, "local:", 6)) {
         return socket_local_server(name + 6, ANDROID_SOCKET_NAMESPACE_ABSTRACT, SOCK_STREAM);
@@ -142,8 +142,10 @@
             continue;
         }
         //  <device-serial> " " <local-name> " " <remote-name> "\n"
+        // Entries from "adb reverse" have no serial.
         android::base::StringAppendF(&result, "%s %s %s\n",
-                                     l->transport->serial, l->local_name, l->connect_to);
+                                     l->transport->serial ? l->transport->serial : "(reverse)",
+                                     l->local_name, l->connect_to);
     }
     return result;
 }
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index 604bd57..e2af045 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -25,7 +25,9 @@
 
 #include <algorithm>
 
+#include <base/logging.h>
 #include <base/stringprintf.h>
+#include <base/strings.h>
 
 #include "adb_trace.h"
 #include "sysdeps.h"
@@ -63,6 +65,25 @@
   return result;
 }
 
+int mkdirs(const char *path)
+{
+    int ret;
+    char *x = (char *)path + 1;
+
+    for(;;) {
+        x = adb_dirstart(x);
+        if(x == 0) return 0;
+        *x = 0;
+        ret = adb_mkdir(path, 0775);
+        *x = OS_PATH_SEPARATOR;
+        if((ret < 0) && (errno != EEXIST)) {
+            return ret;
+        }
+        x++;
+    }
+    return 0;
+}
+
 void dump_hex(const void* data, size_t byte_count) {
     byte_count = std::min(byte_count, size_t(16));
 
@@ -84,3 +105,56 @@
 
     DR("%s\n", line.c_str());
 }
+
+bool parse_host_and_port(const std::string& address,
+                         std::string* canonical_address,
+                         std::string* host, int* port,
+                         std::string* error) {
+    host->clear();
+
+    bool ipv6 = true;
+    bool saw_port = false;
+    size_t colons = std::count(address.begin(), address.end(), ':');
+    size_t dots = std::count(address.begin(), address.end(), '.');
+    std::string port_str;
+    if (address[0] == '[') {
+      // [::1]:123
+      if (address.rfind("]:") == std::string::npos) {
+        *error = android::base::StringPrintf("bad IPv6 address '%s'", address.c_str());
+        return false;
+      }
+      *host = address.substr(1, (address.find("]:") - 1));
+      port_str = address.substr(address.rfind("]:") + 2);
+      saw_port = true;
+    } else if (dots == 0 && colons >= 2 && colons <= 7) {
+      // ::1
+      *host = address;
+    } else if (colons <= 1) {
+      // 1.2.3.4 or some.accidental.domain.com
+      ipv6 = false;
+      std::vector<std::string> pieces = android::base::Split(address, ":");
+      *host = pieces[0];
+      if (pieces.size() > 1) {
+        port_str = pieces[1];
+        saw_port = true;
+      }
+    }
+
+    if (host->empty()) {
+      *error = android::base::StringPrintf("no host in '%s'", address.c_str());
+      return false;
+    }
+
+    if (saw_port) {
+      if (sscanf(port_str.c_str(), "%d", port) != 1 || *port <= 0 || *port > 65535) {
+        *error = android::base::StringPrintf("bad port number '%s' in '%s'",
+                                             port_str.c_str(), address.c_str());
+        return false;
+      }
+    }
+
+    *canonical_address = android::base::StringPrintf(ipv6 ? "[%s]:%d" : "%s:%d", host->c_str(), *port);
+    LOG(DEBUG) << "parsed " << address << " as " << *host << " and " << *port
+               << " (" << *canonical_address << ")";
+    return true;
+}
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index 84f7d0c..e0aa1ba 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -22,8 +22,21 @@
 bool getcwd(std::string* cwd);
 bool directory_exists(const std::string& path);
 
+int mkdirs(const char *path);
+
 std::string escape_arg(const std::string& s);
 
 void dump_hex(const void* ptr, size_t byte_count);
 
+// Parses 'address' into 'host' and 'port'.
+// If no port is given, takes the default from *port.
+// 'canonical_address' then becomes "host:port" or "[host]:port" as appropriate.
+// Note that no real checking is done that 'host' or 'port' is valid; that's
+// left to getaddrinfo(3).
+// Returns false on failure and sets *error to an appropriate message.
+bool parse_host_and_port(const std::string& address,
+                         std::string* canonical_address,
+                         std::string* host, int* port,
+                         std::string* error);
+
 #endif
diff --git a/adb/adb_utils_test.cpp b/adb/adb_utils_test.cpp
index 052aea5..7aa610a 100644
--- a/adb/adb_utils_test.cpp
+++ b/adb/adb_utils_test.cpp
@@ -50,3 +50,85 @@
   ASSERT_EQ(R"('abc(')", escape_arg("abc("));
   ASSERT_EQ(R"('abc)')", escape_arg("abc)"));
 }
+
+TEST(adb_utils, parse_host_and_port) {
+  std::string canonical_address;
+  std::string host;
+  int port;
+  std::string error;
+
+  // Name, default port.
+  port = 123;
+  ASSERT_TRUE(parse_host_and_port("www.google.com", &canonical_address, &host, &port, &error));
+  ASSERT_EQ("www.google.com:123", canonical_address);
+  ASSERT_EQ("www.google.com", host);
+  ASSERT_EQ(123, port);
+
+  // Name, explicit port.
+  ASSERT_TRUE(parse_host_and_port("www.google.com:666", &canonical_address, &host, &port, &error));
+  ASSERT_EQ("www.google.com:666", canonical_address);
+  ASSERT_EQ("www.google.com", host);
+  ASSERT_EQ(666, port);
+
+  // IPv4, default port.
+  port = 123;
+  ASSERT_TRUE(parse_host_and_port("1.2.3.4", &canonical_address, &host, &port, &error));
+  ASSERT_EQ("1.2.3.4:123", canonical_address);
+  ASSERT_EQ("1.2.3.4", host);
+  ASSERT_EQ(123, port);
+
+  // IPv4, explicit port.
+  ASSERT_TRUE(parse_host_and_port("1.2.3.4:666", &canonical_address, &host, &port, &error));
+  ASSERT_EQ("1.2.3.4:666", canonical_address);
+  ASSERT_EQ("1.2.3.4", host);
+  ASSERT_EQ(666, port);
+
+  // Simple IPv6, default port.
+  port = 123;
+  ASSERT_TRUE(parse_host_and_port("::1", &canonical_address, &host, &port, &error));
+  ASSERT_EQ("[::1]:123", canonical_address);
+  ASSERT_EQ("::1", host);
+  ASSERT_EQ(123, port);
+
+  // Simple IPv6, explicit port.
+  ASSERT_TRUE(parse_host_and_port("[::1]:666", &canonical_address, &host, &port, &error));
+  ASSERT_EQ("[::1]:666", canonical_address);
+  ASSERT_EQ("::1", host);
+  ASSERT_EQ(666, port);
+
+  // Hairy IPv6, default port.
+  port = 123;
+  ASSERT_TRUE(parse_host_and_port("fe80::200:5aee:feaa:20a2", &canonical_address, &host, &port, &error));
+  ASSERT_EQ("[fe80::200:5aee:feaa:20a2]:123", canonical_address);
+  ASSERT_EQ("fe80::200:5aee:feaa:20a2", host);
+  ASSERT_EQ(123, port);
+
+  // Simple IPv6, explicit port.
+  ASSERT_TRUE(parse_host_and_port("[fe80::200:5aee:feaa:20a2]:666", &canonical_address, &host, &port, &error));
+  ASSERT_EQ("[fe80::200:5aee:feaa:20a2]:666", canonical_address);
+  ASSERT_EQ("fe80::200:5aee:feaa:20a2", host);
+  ASSERT_EQ(666, port);
+
+  // Invalid IPv4.
+  EXPECT_FALSE(parse_host_and_port("1.2.3.4:", &canonical_address, &host, &port, &error));
+  EXPECT_FALSE(parse_host_and_port("1.2.3.4::", &canonical_address, &host, &port, &error));
+  EXPECT_FALSE(parse_host_and_port("1.2.3.4:hello", &canonical_address, &host, &port, &error));
+  EXPECT_FALSE(parse_host_and_port(":123", &canonical_address, &host, &port, &error));
+
+  // Invalid IPv6.
+  EXPECT_FALSE(parse_host_and_port(":1", &canonical_address, &host, &port, &error));
+  EXPECT_FALSE(parse_host_and_port("::::::::1", &canonical_address, &host, &port, &error));
+  EXPECT_FALSE(parse_host_and_port("[::1", &canonical_address, &host, &port, &error));
+  EXPECT_FALSE(parse_host_and_port("[::1]", &canonical_address, &host, &port, &error));
+  EXPECT_FALSE(parse_host_and_port("[::1]:", &canonical_address, &host, &port, &error));
+  EXPECT_FALSE(parse_host_and_port("[::1]::", &canonical_address, &host, &port, &error));
+  EXPECT_FALSE(parse_host_and_port("[::1]:hello", &canonical_address, &host, &port, &error));
+
+  // Invalid ports.
+  EXPECT_FALSE(parse_host_and_port("[::1]:-1", &canonical_address, &host, &port, &error));
+  EXPECT_FALSE(parse_host_and_port("[::1]:0", &canonical_address, &host, &port, &error));
+  EXPECT_FALSE(parse_host_and_port("[::1]:65536", &canonical_address, &host, &port, &error));
+  EXPECT_FALSE(parse_host_and_port("1.2.3.4:-1", &canonical_address, &host, &port, &error));
+  EXPECT_FALSE(parse_host_and_port("1.2.3.4:0", &canonical_address, &host, &port, &error));
+  EXPECT_FALSE(parse_host_and_port("1.2.3.4:65536", &canonical_address, &host, &port, &error));
+}
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index c1375b3..f190336 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"
@@ -756,25 +756,6 @@
     return send_shell_command(transport, serial, cmd);
 }
 
-static int mkdirs(const char *path)
-{
-    int ret;
-    char *x = (char *)path + 1;
-
-    for(;;) {
-        x = adb_dirstart(x);
-        if(x == 0) return 0;
-        *x = 0;
-        ret = adb_mkdir(path, 0775);
-        *x = OS_PATH_SEPARATOR;
-        if((ret < 0) && (errno != EEXIST)) {
-            return ret;
-        }
-        x++;
-    }
-    return 0;
-}
-
 static int backup(int argc, const char** argv) {
     const char* filename = "./backup.ab";
 
@@ -1251,10 +1232,12 @@
             return 0;
         }
     }
+    else if (!strcmp(argv[0], "tcpip") && argc > 1) {
+        return adb_connect_command(android::base::StringPrintf("tcpip:%s", argv[1]));
+    }
     else if (!strcmp(argv[0], "remount") ||
              !strcmp(argv[0], "reboot") ||
              !strcmp(argv[0], "reboot-bootloader") ||
-             !strcmp(argv[0], "tcpip") ||
              !strcmp(argv[0], "usb") ||
              !strcmp(argv[0], "root") ||
              !strcmp(argv[0], "unroot") ||
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/fdevent.cpp b/adb/fdevent.cpp
index 0c43c5e..5cd4988 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -42,7 +42,9 @@
 // This socket is used when a subproc shell service exists.
 // It wakes up the fdevent_loop() and cause the correct handling
 // of the shell's pseudo-tty master. I.e. force close it.
+#if !ADB_HOST
 int SHELL_EXIT_NOTIFY_FD = -1;
+#endif // !ADB_HOST
 
 static void fatal(const char *fn, const char *fmt, ...)
 {
@@ -81,7 +83,6 @@
 static void fdevent_plist_enqueue(fdevent *node);
 static void fdevent_plist_remove(fdevent *node);
 static fdevent *fdevent_plist_dequeue(void);
-static void fdevent_subproc_event_func(int fd, unsigned events, void *userdata);
 
 static fdevent list_pending = {
     .next = &list_pending,
@@ -510,6 +511,7 @@
     fde->func(fde->fd, events, fde->arg);
 }
 
+#if !ADB_HOST
 static void fdevent_subproc_event_func(int fd, unsigned ev,
                                        void* /* userdata */)
 {
@@ -569,6 +571,24 @@
     }
 }
 
+void fdevent_subproc_setup()
+{
+    int s[2];
+
+    if(adb_socketpair(s)) {
+        FATAL("cannot create shell-exit socket-pair\n");
+    }
+    D("socketpair: (%d,%d)\n", s[0], s[1]);
+
+    SHELL_EXIT_NOTIFY_FD = s[0];
+    fdevent *fde;
+    fde = fdevent_create(s[1], fdevent_subproc_event_func, NULL);
+    if(!fde)
+      FATAL("cannot create fdevent for shell-exit handler\n");
+    fdevent_add(fde, FDE_READ);
+}
+#endif // !ADB_HOST
+
 fdevent *fdevent_create(int fd, fd_func func, void *arg)
 {
     fdevent *fde = (fdevent*) malloc(sizeof(fdevent));
@@ -661,27 +681,12 @@
         fde, (fde->state & FDE_EVENTMASK) & (~(events & FDE_EVENTMASK)));
 }
 
-void fdevent_subproc_setup()
-{
-    int s[2];
-
-    if(adb_socketpair(s)) {
-        FATAL("cannot create shell-exit socket-pair\n");
-    }
-    D("socketpair: (%d,%d)", s[0], s[1]);
-
-    SHELL_EXIT_NOTIFY_FD = s[0];
-    fdevent *fde;
-    fde = fdevent_create(s[1], fdevent_subproc_event_func, NULL);
-    if(!fde)
-      FATAL("cannot create fdevent for shell-exit handler\n");
-    fdevent_add(fde, FDE_READ);
-}
-
 void fdevent_loop()
 {
     fdevent *fde;
+#if !ADB_HOST
     fdevent_subproc_setup();
+#endif // !ADB_HOST
 
     for(;;) {
         D("--- ---- waiting for events\n");
diff --git a/adb/file_sync_client.cpp b/adb/file_sync_client.cpp
index 1dc711ae..49d42a3 100644
--- a/adb/file_sync_client.cpp
+++ b/adb/file_sync_client.cpp
@@ -32,6 +32,7 @@
 #include "adb.h"
 #include "adb_client.h"
 #include "adb_io.h"
+#include "adb_utils.h"
 #include "file_sync_service.h"
 
 static unsigned long long total_bytes;
@@ -395,25 +396,6 @@
     return -1;
 }
 
-static int mkdirs(const char *name)
-{
-    int ret;
-    char *x = (char *)name + 1;
-
-    for(;;) {
-        x = adb_dirstart(x);
-        if(x == 0) return 0;
-        *x = 0;
-        ret = adb_mkdir(name, 0775);
-        *x = OS_PATH_SEPARATOR;
-        if((ret < 0) && (errno != EEXIST)) {
-            return ret;
-        }
-        x++;
-    }
-    return 0;
-}
-
 static int sync_recv(int fd, const char* rpath, const char* lpath, int show_progress) {
     syncmsg msg;
     int len;
diff --git a/adb/jdwp_service.cpp b/adb/jdwp_service.cpp
index c0f7ec2..a2e0f88 100644
--- a/adb/jdwp_service.cpp
+++ b/adb/jdwp_service.cpp
@@ -435,7 +435,7 @@
               __FUNCTION__, strerror(errno));
             return -1;
         }
-        D("socketpair: (%d,%d)", fds[0], fds[1]);
+        D("socketpair: (%d,%d)\n", fds[0], fds[1]);
 
         proc->out_fds[ proc->out_count ] = fds[1];
         if (++proc->out_count == 1)
@@ -608,7 +608,7 @@
     */
     if (jdwp->pass == 0) {
         apacket*  p = get_apacket();
-        p->len = jdwp_process_list((char*)p->data, MAX_PAYLOAD);
+        p->len = jdwp_process_list((char*)p->data, s->get_max_payload());
         peer->enqueue(peer, p);
         jdwp->pass = 1;
     }
@@ -695,7 +695,7 @@
     if (t->need_update) {
         apacket*  p = get_apacket();
         t->need_update = 0;
-        p->len = jdwp_process_list_msg((char*)p->data, sizeof(p->data));
+        p->len = jdwp_process_list_msg((char*)p->data, s->get_max_payload());
         s->peer->enqueue(s->peer, p);
     }
 }
diff --git a/adb/protocol.txt b/adb/protocol.txt
index c9d3c24..5c7c6ba 100644
--- a/adb/protocol.txt
+++ b/adb/protocol.txt
@@ -60,11 +60,14 @@
 declares the maximum message body size that the remote system
 is willing to accept.
 
-Currently, version=0x01000000 and maxdata=4096
+Currently, version=0x01000000 and maxdata=256*1024. Older versions of adb
+hard-coded maxdata=4096, so CONNECT and AUTH packets sent to a device must not
+be larger than that because they're sent before the CONNECT from the device
+that tells the adb server what maxdata the device can support.
 
 Both sides send a CONNECT message when the connection between them is
 established.  Until a CONNECT message is received no other messages may
-be sent.  Any messages received before a CONNECT message MUST be ignored.
+be sent. Any messages received before a CONNECT message MUST be ignored.
 
 If a CONNECT message is received with an unknown version or insufficiently
 large maxdata value, the connection with the other side must be closed.
@@ -133,7 +136,7 @@
 
 
 
---- WRITE(0, remote-id, "data") ----------------------------------------
+--- WRITE(local-id, remote-id, "data") ---------------------------------
 
 The WRITE message sends data to the recipient's stream identified by
 remote-id.  The payload MUST be <= maxdata in length.
diff --git a/adb/remount_service.cpp b/adb/remount_service.cpp
index 3a4d2da..2893263 100644
--- a/adb/remount_service.cpp
+++ b/adb/remount_service.cpp
@@ -33,9 +33,12 @@
 #include "adb_io.h"
 #include "adb_utils.h"
 #include "cutils/properties.h"
+#include "fs_mgr.h"
+
+const std::string kFstab_Prefix = "/fstab.";
 
 // Returns the device used to mount a directory in /proc/mounts.
-static std::string find_mount(const char* dir) {
+static std::string find_proc_mount(const char* dir) {
     std::unique_ptr<FILE, int(*)(FILE*)> fp(setmntent("/proc/mounts", "r"), endmntent);
     if (!fp) {
         return "";
@@ -50,6 +53,29 @@
     return "";
 }
 
+// Returns the device used to mount a directory in the fstab.
+static std::string find_fstab_mount(const char* dir) {
+    char propbuf[PROPERTY_VALUE_MAX];
+
+    property_get("ro.hardware", propbuf, "");
+    std::string fstab_filename = kFstab_Prefix + propbuf;
+    struct fstab* fstab = fs_mgr_read_fstab(fstab_filename.c_str());
+    struct fstab_rec* rec = fs_mgr_get_entry_for_mount_point(fstab, dir);
+    std::string dev = rec ? std::string(rec->blk_device) : "";
+    fs_mgr_free_fstab(fstab);
+    return dev;
+}
+
+// The proc entry for / is full of lies, so check fstab instead.
+// /proc/mounts lists rootfs and /dev/root, neither of which is what we want.
+static std::string find_mount(const char* dir) {
+    if (strcmp(dir, "/") == 0) {
+       return find_fstab_mount(dir);
+    } else {
+       return find_proc_mount(dir);
+    }
+}
+
 bool make_block_device_writable(const std::string& dev) {
     int fd = unix_open(dev.c_str(), O_RDONLY | O_CLOEXEC);
     if (fd == -1) {
@@ -112,7 +138,13 @@
     }
 
     bool success = true;
-    success &= remount_partition(fd, "/system");
+    property_get("ro.build.system_root_image", prop_buf, "");
+    bool system_root = !strcmp(prop_buf, "true");
+    if (system_root) {
+        success &= remount_partition(fd, "/");
+    } else {
+        success &= remount_partition(fd, "/system");
+    }
     success &= remount_partition(fd, "/vendor");
     success &= remount_partition(fd, "/oem");
 
diff --git a/adb/services.cpp b/adb/services.cpp
index 2fd75ab..2e3ad98 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -47,6 +47,7 @@
 
 #include "adb.h"
 #include "adb_io.h"
+#include "adb_utils.h"
 #include "file_sync_service.h"
 #include "remount_service.h"
 #include "transport.h"
@@ -205,7 +206,7 @@
         printf("cannot create service socket pair\n");
         return -1;
     }
-    D("socketpair: (%d,%d)", s[0], s[1]);
+    D("socketpair: (%d,%d)\n", s[0], s[1]);
 
     stinfo* sti = reinterpret_cast<stinfo*>(malloc(sizeof(stinfo)));
     if (sti == nullptr) {
@@ -317,7 +318,7 @@
         printf("[ cannot create socket pair - %s ]\n", strerror(errno));
         return -1;
     }
-    D("socketpair: (%d,%d)", sv[0], sv[1]);
+    D("socketpair: (%d,%d)\n", sv[0], sv[1]);
 
     *pid = fork();
     if (*pid < 0) {
@@ -485,7 +486,7 @@
     } else if(!strncmp(name, "tcpip:", 6)) {
         int port;
         if (sscanf(name + 6, "%d", &port) != 1) {
-            port = 0;
+            return -1;
         }
         ret = create_service_thread(restart_tcp_service, (void *) (uintptr_t) port);
     } else if(!strncmp(name, "usb:", 4)) {
@@ -528,7 +529,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);
@@ -541,35 +542,27 @@
     D("wait_for_state is done\n");
 }
 
-static void connect_device(const std::string& host, std::string* response) {
-    if (host.empty()) {
-        *response = "empty host name";
+static void connect_device(const std::string& address, std::string* response) {
+    if (address.empty()) {
+        *response = "empty address";
         return;
     }
 
-    std::vector<std::string> pieces = android::base::Split(host, ":");
-    const std::string& hostname = pieces[0];
-
+    std::string serial;
+    std::string host;
     int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
-    if (pieces.size() > 1) {
-        if (sscanf(pieces[1].c_str(), "%d", &port) != 1) {
-            *response = android::base::StringPrintf("bad port number %s", pieces[1].c_str());
-            return;
-        }
-    }
-
-    // This may look like we're putting 'host' back together,
-    // but we're actually inserting the default port if necessary.
-    std::string serial = android::base::StringPrintf("%s:%d", hostname.c_str(), port);
-
-    int fd = socket_network_client_timeout(hostname.c_str(), port, SOCK_STREAM, 10);
-    if (fd < 0) {
-        *response = android::base::StringPrintf("unable to connect to %s:%d",
-                                                hostname.c_str(), port);
+    if (!parse_host_and_port(address, &serial, &host, &port, response)) {
         return;
     }
 
-    D("client: connected on remote on fd %d\n", fd);
+    int fd = socket_network_client_timeout(host.c_str(), port, SOCK_STREAM, 10);
+    if (fd == -1) {
+        *response = android::base::StringPrintf("unable to connect to %s: %s",
+                                                serial.c_str(), strerror(errno));
+        return;
+    }
+
+    D("client: connected %s remote on fd %d\n", serial.c_str(), fd);
     close_on_exec(fd);
     disable_tcp_nagle(fd);
 
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 621944e..d8ea2ee 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -330,8 +330,9 @@
     if (ev & FDE_READ) {
         apacket *p = get_apacket();
         unsigned char *x = p->data;
-        size_t avail = MAX_PAYLOAD;
-        int r;
+        const size_t max_payload = s->get_max_payload();
+        size_t avail = max_payload;
+        int r = 0;
         int is_eof = 0;
 
         while (avail > 0) {
@@ -354,10 +355,10 @@
         }
         D("LS(%d): fd=%d post avail loop. r=%d is_eof=%d forced_eof=%d\n",
           s->id, s->fd, r, is_eof, s->fde.force_eof);
-        if ((avail == MAX_PAYLOAD) || (s->peer == 0)) {
+        if ((avail == max_payload) || (s->peer == 0)) {
             put_apacket(p);
         } else {
-            p->len = MAX_PAYLOAD - avail;
+            p->len = max_payload - avail;
 
             r = s->peer->enqueue(s->peer, p);
             D("LS(%d): fd=%d post peer->enqueue(). r=%d\n", s->id, s->fd,
@@ -569,9 +570,9 @@
 {
     D("Connect_to_remote call RS(%d) fd=%d\n", s->id, s->fd);
     apacket *p = get_apacket();
-    int len = strlen(destination) + 1;
+    size_t len = strlen(destination) + 1;
 
-    if(len > (MAX_PAYLOAD-1)) {
+    if(len > (s->get_max_payload()-1)) {
         fatal("destination oversized");
     }
 
@@ -700,7 +701,7 @@
         s->pkt_first = p;
         s->pkt_last = p;
     } else {
-        if((s->pkt_first->len + p->len) > MAX_PAYLOAD) {
+        if((s->pkt_first->len + p->len) > s->get_max_payload()) {
             D("SS(%d): overflow\n", s->id);
             put_apacket(p);
             goto fail;
@@ -901,3 +902,14 @@
     ss->peer = s;
     s->ready(s);
 }
+
+size_t asocket::get_max_payload() const {
+    size_t max_payload = MAX_PAYLOAD;
+    if (transport) {
+        max_payload = std::min(max_payload, transport->get_max_payload());
+    }
+    if (peer && peer->transport) {
+        max_payload = std::min(max_payload, peer->transport->get_max_payload());
+    }
+    return max_payload;
+}
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index b7c16d9..5efdab5 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -276,6 +276,7 @@
 #include "fdevent.h"
 #include <cutils/sockets.h>
 #include <cutils/misc.h>
+#include <cutils/threads.h>
 #include <signal.h>
 #include <sys/wait.h>
 #include <sys/stat.h>
@@ -544,7 +545,7 @@
 
 static __inline__ unsigned long adb_thread_id()
 {
-    return (unsigned long)pthread_self();
+    return (unsigned long)gettid();
 }
 
 #endif /* !_WIN32 */
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index 50c99f1..bdc6027 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -361,9 +361,10 @@
                                0, NULL );
 
     if ( f->fh_handle == INVALID_HANDLE_VALUE ) {
+        const DWORD err = GetLastError();
         _fh_close(f);
-        D( "adb_open: could not open '%s':", path );
-        switch (GetLastError()) {
+        D( "adb_open: could not open '%s': ", path );
+        switch (err) {
             case ERROR_FILE_NOT_FOUND:
                 D( "file not found\n" );
                 errno = ENOENT;
@@ -375,7 +376,7 @@
                 return -1;
 
             default:
-                D( "unknown error\n" );
+                D( "unknown error: %ld\n", err );
                 errno = ENOENT;
                 return -1;
         }
@@ -402,9 +403,10 @@
                                NULL );
 
     if ( f->fh_handle == INVALID_HANDLE_VALUE ) {
+        const DWORD err = GetLastError();
         _fh_close(f);
-        D( "adb_creat: could not open '%s':", path );
-        switch (GetLastError()) {
+        D( "adb_creat: could not open '%s': ", path );
+        switch (err) {
             case ERROR_FILE_NOT_FOUND:
                 D( "file not found\n" );
                 errno = ENOENT;
@@ -416,7 +418,7 @@
                 return -1;
 
             default:
-                D( "unknown error\n" );
+                D( "unknown error: %ld\n", err );
                 errno = ENOENT;
                 return -1;
         }
@@ -781,8 +783,9 @@
 
     fh->fh_socket = accept( serverfh->fh_socket, addr, addrlen );
     if (fh->fh_socket == INVALID_SOCKET) {
+        const DWORD err = WSAGetLastError();
         _fh_close( fh );
-        D( "adb_socket_accept: accept on fd %d return error %ld\n", serverfd, GetLastError() );
+        D( "adb_socket_accept: accept on fd %d return error %ld\n", serverfd, err );
         return -1;
     }
 
@@ -1546,7 +1549,7 @@
     threads = (WaitForAllParam*)malloc((chunks + (remains ? 1 : 0)) *
                                         sizeof(WaitForAllParam));
     if (threads == NULL) {
-        D("Unable to allocate thread array for %d handles.", handles_count);
+        D("Unable to allocate thread array for %d handles.\n", handles_count);
         return (int)WAIT_FAILED;
     }
 
@@ -1554,7 +1557,7 @@
      * reset" event that will remain set once it was set. */
     main_event = CreateEvent(NULL, TRUE, FALSE, NULL);
     if (main_event == NULL) {
-        D("Unable to create main event. Error: %d", (int)GetLastError());
+        D("Unable to create main event. Error: %ld\n", GetLastError());
         free(threads);
         return (int)WAIT_FAILED;
     }
@@ -1587,7 +1590,7 @@
                                                        &threads[chunk], 0, NULL);
         if (threads[chunk].thread == NULL) {
             /* Unable to create a waiter thread. Collapse. */
-            D("Unable to create a waiting thread %d of %d. errno=%d",
+            D("Unable to create a waiting thread %d of %d. errno=%d\n",
               chunk, chunks, errno);
             chunks = chunk;
             SetEvent(main_event);
diff --git a/adb/tests/test_adb.py b/adb/tests/test_adb.py
index 730f668..3099554 100755
--- a/adb/tests/test_adb.py
+++ b/adb/tests/test_adb.py
@@ -186,7 +186,7 @@
                                                                   remote))
 
     def tcpip(self, port):
-        return call_checked(self.adb_cmd + "tcpip {}".format(port))
+        return call_combined(self.adb_cmd + "tcpip {}".format(port))
 
     def usb(self):
         return call_checked(self.adb_cmd + "usb")
@@ -326,6 +326,16 @@
         else:
             self.assertEqual(output, "Linux\n")
 
+    def test_tcpip(self):
+        """adb tcpip requires a port. http://b/22636927"""
+        output, status_code = AdbWrapper().tcpip("")
+        self.assertEqual(1, status_code)
+        self.assertIn("help message", output)
+
+        output, status_code = AdbWrapper().tcpip("blah")
+        self.assertEqual(1, status_code)
+        self.assertIn("error", output)
+
 
 class AdbFile(unittest.TestCase):
     SCRATCH_DIR = "/data/local/tmp"
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 379c702..87aff88 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -283,7 +283,7 @@
     p->msg.magic = A_SYNC ^ 0xffffffff;
     if(write_packet(t->fd, t->serial, &p)) {
         put_apacket(p);
-        D("%s: failed to write SYNC apacket to transport", t->serial);
+        D("%s: failed to write SYNC apacket to transport\n", t->serial);
     }
 
 oops:
@@ -579,7 +579,7 @@
             fatal_errno("cannot open transport socketpair");
         }
 
-        D("transport: %s socketpair: (%d,%d) starting", t->serial, s[0], s[1]);
+        D("transport: %s socketpair: (%d,%d) starting\n", t->serial, s[0], s[1]);
 
         t->transport_socket = s[0];
         t->fd = s[1];
@@ -617,7 +617,7 @@
     if(adb_socketpair(s)){
         fatal_errno("cannot open transport registration socketpair");
     }
-    D("socketpair: (%d,%d)", s[0], s[1]);
+    D("socketpair: (%d,%d)\n", s[0], s[1]);
 
     transport_registration_send = s[0];
     transport_registration_recv = s[1];
@@ -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,32 @@
 
     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 adb server's $ADB_VENDOR_KEYS is ";
+            *error_out += ADB_VENDOR_KEYS ? ADB_VENDOR_KEYS : "not set";
+            *error_out += "\n";
+            *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;
@@ -836,6 +835,19 @@
     }
 }
 
+void atransport::update_version(int version, size_t payload) {
+    protocol_version = std::min(version, A_VERSION);
+    max_payload = std::min(payload, MAX_PAYLOAD);
+}
+
+int atransport::get_protocol_version() const {
+    return protocol_version;
+}
+
+size_t atransport::get_max_payload() const {
+    return max_payload;
+}
+
 #if ADB_HOST
 
 static void append_transport_info(std::string* result, const char* key,
@@ -1021,15 +1033,16 @@
 #undef TRACE_TAG
 #define TRACE_TAG  TRACE_RWX
 
-int check_header(apacket *p)
+int check_header(apacket *p, atransport *t)
 {
     if(p->msg.magic != (p->msg.command ^ 0xffffffff)) {
         D("check_header(): invalid magic\n");
         return -1;
     }
 
-    if(p->msg.data_length > MAX_PAYLOAD) {
-        D("check_header(): %d > MAX_PAYLOAD\n", p->msg.data_length);
+    if(p->msg.data_length > t->get_max_payload()) {
+        D("check_header(): %u > atransport::max_payload = %zu\n",
+            p->msg.data_length, t->get_max_payload());
         return -1;
     }
 
diff --git a/adb/transport.h b/adb/transport.h
index 538f63e..edcc99d 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -57,7 +57,7 @@
 void unregister_transport(atransport* t);
 void unregister_all_tcp_transports();
 
-int check_header(apacket* p);
+int check_header(apacket* p, atransport* t);
 int check_data(apacket* p);
 
 /* for MacOS X cleanup */
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index d3c4c30..1adc69e 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -53,7 +53,7 @@
         return -1;
     }
 
-    if(check_header(p)) {
+    if(check_header(p, t)) {
         D("bad header: terminated (data)\n");
         return -1;
     }
@@ -331,7 +331,11 @@
 
 static void remote_close(atransport *t)
 {
-    adb_close(t->fd);
+    int fd = t->sfd;
+    if (fd != -1) {
+        t->sfd = -1;
+        adb_close(fd);
+    }
 }
 
 
diff --git a/adb/transport_usb.cpp b/adb/transport_usb.cpp
index eb3454d..2c975a9 100644
--- a/adb/transport_usb.cpp
+++ b/adb/transport_usb.cpp
@@ -32,7 +32,7 @@
         return -1;
     }
 
-    if(check_header(p)) {
+    if(check_header(p, t)) {
         D("remote usb: check_header failed\n");
         return -1;
     }
diff --git a/adb/usb_linux.cpp b/adb/usb_linux.cpp
index c6f712b..439826a 100644
--- a/adb/usb_linux.cpp
+++ b/adb/usb_linux.cpp
@@ -439,36 +439,21 @@
 
 int usb_write(usb_handle *h, const void *_data, int len)
 {
-    unsigned char *data = (unsigned char*) _data;
-    int n;
-    int need_zero = 0;
-
     D("++ usb_write ++\n");
-    if(h->zero_mask) {
+
+    unsigned char *data = (unsigned char*) _data;
+    int n = usb_bulk_write(h, data, len);
+    if(n != len) {
+        D("ERROR: n = %d, errno = %d (%s)\n",
+            n, errno, strerror(errno));
+        return -1;
+    }
+
+    if(h->zero_mask && !(len & h->zero_mask)) {
             /* if we need 0-markers and our transfer
             ** is an even multiple of the packet size,
-            ** we make note of it
+            ** then send the zero markers.
             */
-        if(!(len & h->zero_mask)) {
-            need_zero = 1;
-        }
-    }
-
-    while(len > 0) {
-        int xfer = (len > 4096) ? 4096 : len;
-
-        n = usb_bulk_write(h, data, xfer);
-        if(n != xfer) {
-            D("ERROR: n = %d, errno = %d (%s)\n",
-                n, errno, strerror(errno));
-            return -1;
-        }
-
-        len -= xfer;
-        data += xfer;
-    }
-
-    if(need_zero){
         n = usb_bulk_write(h, _data, 0);
         return n;
     }
@@ -484,7 +469,7 @@
 
     D("++ usb_read ++\n");
     while(len > 0) {
-        int xfer = (len > 4096) ? 4096 : len;
+        int xfer = len;
 
         D("[ usb read %d fd = %d], fname=%s\n", xfer, h->desc, h->fname);
         n = usb_bulk_read(h, data, xfer);
diff --git a/adb/usb_linux_client.cpp b/adb/usb_linux_client.cpp
index d34c454..b1b3538 100644
--- a/adb/usb_linux_client.cpp
+++ b/adb/usb_linux_client.cpp
@@ -213,14 +213,20 @@
 
 static int usb_adb_read(usb_handle *h, void *data, int len)
 {
-    int n;
-
     D("about to read (fd=%d, len=%d)\n", h->fd, len);
-    n = unix_read(h->fd, data, len);
-    if(n != len) {
-        D("ERROR: fd = %d, n = %d, errno = %d (%s)\n",
-            h->fd, n, errno, strerror(errno));
-        return -1;
+    while (len > 0) {
+        // The kernel implementation of adb_read in f_adb.c doesn't support
+        // reads larger then 4096 bytes. Read the data in 4096 byte chunks to
+        // avoid the issue. (The ffs implementation doesn't have this limit.)
+        int bytes_to_read = len < 4096 ? len : 4096;
+        int n = unix_read(h->fd, data, bytes_to_read);
+        if (n != bytes_to_read) {
+            D("ERROR: fd = %d, n = %d, errno = %d (%s)\n",
+                h->fd, n, errno, strerror(errno));
+            return -1;
+        }
+        len -= n;
+        data = ((char*)data) + n;
     }
     D("[ done fd=%d ]\n", h->fd);
     return 0;
@@ -444,11 +450,11 @@
 
     err = ioctl(h->bulk_in, FUNCTIONFS_CLEAR_HALT);
     if (err < 0)
-        D("[ kick: source (fd=%d) clear halt failed (%d) ]", h->bulk_in, errno);
+        D("[ kick: source (fd=%d) clear halt failed (%d) ]\n", h->bulk_in, errno);
 
     err = ioctl(h->bulk_out, FUNCTIONFS_CLEAR_HALT);
     if (err < 0)
-        D("[ kick: sink (fd=%d) clear halt failed (%d) ]", h->bulk_out, errno);
+        D("[ kick: sink (fd=%d) clear halt failed (%d) ]\n", h->bulk_out, errno);
 
     adb_mutex_lock(&h->lock);
 
diff --git a/debuggerd/Android.mk b/debuggerd/Android.mk
index 6cfb541..3fca709 100644
--- a/debuggerd/Android.mk
+++ b/debuggerd/Android.mk
@@ -77,12 +77,12 @@
 
 debuggerd_test_src_files := \
     utility.cpp \
-    test/dump_maps_test.cpp \
     test/dump_memory_test.cpp \
     test/elf_fake.cpp \
     test/log_fake.cpp \
     test/property_fake.cpp \
     test/ptrace_fake.cpp \
+    test/tombstone_test.cpp \
     test/selinux_fake.cpp \
 
 debuggerd_shared_libraries := \
diff --git a/debuggerd/arm/machine.cpp b/debuggerd/arm/machine.cpp
index b7d6997..78c2306 100644
--- a/debuggerd/arm/machine.cpp
+++ b/debuggerd/arm/machine.cpp
@@ -15,12 +15,15 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "DEBUG"
+
 #include <errno.h>
 #include <stdint.h>
 #include <string.h>
 #include <sys/ptrace.h>
 
 #include <backtrace/Backtrace.h>
+#include <log/log.h>
 
 #include "machine.h"
 #include "utility.h"
@@ -28,7 +31,7 @@
 void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
   pt_regs regs;
   if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &regs)) {
-    _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+    ALOGE("cannot get registers: %s\n", strerror(errno));
     return;
   }
 
@@ -48,7 +51,7 @@
 void dump_registers(log_t* log, pid_t tid) {
   pt_regs r;
   if (ptrace(PTRACE_GETREGS, tid, 0, &r)) {
-    _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+    ALOGE("cannot get registers: %s\n", strerror(errno));
     return;
   }
 
@@ -68,7 +71,7 @@
 
   user_vfp vfp_regs;
   if (ptrace(PTRACE_GETVFPREGS, tid, 0, &vfp_regs)) {
-    _LOG(log, logtype::ERROR, "cannot get FP registers: %s\n", strerror(errno));
+    ALOGE("cannot get FP registers: %s\n", strerror(errno));
     return;
   }
 
diff --git a/debuggerd/arm64/machine.cpp b/debuggerd/arm64/machine.cpp
index 2e097da..e7bf79a 100644
--- a/debuggerd/arm64/machine.cpp
+++ b/debuggerd/arm64/machine.cpp
@@ -15,6 +15,8 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "DEBUG"
+
 #include <elf.h>
 #include <errno.h>
 #include <stdint.h>
@@ -23,6 +25,7 @@
 #include <sys/uio.h>
 
 #include <backtrace/Backtrace.h>
+#include <log/log.h>
 
 #include "machine.h"
 #include "utility.h"
@@ -34,8 +37,7 @@
   io.iov_len = sizeof(regs);
 
   if (ptrace(PTRACE_GETREGSET, backtrace->Tid(), reinterpret_cast<void*>(NT_PRSTATUS), &io) == -1) {
-    _LOG(log, logtype::ERROR, "%s: ptrace failed to get registers: %s",
-         __func__, strerror(errno));
+    ALOGE("ptrace failed to get registers: %s", strerror(errno));
     return;
   }
 
@@ -57,7 +59,7 @@
   io.iov_len = sizeof(r);
 
   if (ptrace(PTRACE_GETREGSET, tid, (void*) NT_PRSTATUS, (void*) &io) == -1) {
-    _LOG(log, logtype::ERROR, "ptrace error: %s\n", strerror(errno));
+    ALOGE("ptrace error: %s\n", strerror(errno));
     return;
   }
 
@@ -81,7 +83,7 @@
   io.iov_len = sizeof(f);
 
   if (ptrace(PTRACE_GETREGSET, tid, (void*) NT_PRFPREG, (void*) &io) == -1) {
-    _LOG(log, logtype::ERROR, "ptrace error: %s\n", strerror(errno));
+    ALOGE("ptrace error: %s\n", strerror(errno));
     return;
   }
 
diff --git a/debuggerd/backtrace.cpp b/debuggerd/backtrace.cpp
index b8084c5..b46f8f4 100644
--- a/debuggerd/backtrace.cpp
+++ b/debuggerd/backtrace.cpp
@@ -105,7 +105,7 @@
   }
 
   if (!attached && ptrace(PTRACE_DETACH, tid, 0, 0) != 0) {
-    _LOG(log, logtype::ERROR, "ptrace detach from %d failed: %s\n", tid, strerror(errno));
+    ALOGE("ptrace detach from %d failed: %s\n", tid, strerror(errno));
     *detach_failed = true;
   }
 }
diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp
index b84a4e5..787b7aa 100644
--- a/debuggerd/debuggerd.cpp
+++ b/debuggerd/debuggerd.cpp
@@ -63,32 +63,18 @@
   int32_t original_si_code;
 };
 
-static void wait_for_user_action(const debugger_request_t &request) {
-  // Find out the name of the process that crashed.
-  char path[64];
-  snprintf(path, sizeof(path), "/proc/%d/exe", request.pid);
-
-  char exe[PATH_MAX];
-  int count;
-  if ((count = readlink(path, exe, sizeof(exe) - 1)) == -1) {
-    ALOGE("readlink('%s') failed: %s", path, strerror(errno));
-    strlcpy(exe, "unknown", sizeof(exe));
-  } else {
-    exe[count] = '\0';
-  }
-
+static void wait_for_user_action(const debugger_request_t& request) {
   // Explain how to attach the debugger.
-  ALOGI("********************************************************\n"
+  ALOGI("***********************************************************\n"
         "* Process %d has been suspended while crashing.\n"
-        "* To attach gdbserver for a gdb connection on port 5039\n"
-        "* and start gdbclient:\n"
+        "* To attach gdbserver and start gdb, run this on the host:\n"
         "*\n"
-        "*     gdbclient %s :5039 %d\n"
+        "*     gdbclient %d\n"
         "*\n"
         "* Wait for gdb to start, then press the VOLUME DOWN key\n"
         "* to let the process continue crashing.\n"
-        "********************************************************",
-        request.pid, exe, request.tid);
+        "***********************************************************",
+        request.pid, request.tid);
 
   // Wait for VOLUME DOWN.
   if (init_getevent() == 0) {
@@ -134,8 +120,6 @@
   return fields == 7 ? 0 : -1;
 }
 
-static int selinux_enabled;
-
 /*
  * Corresponds with debugger_action_t enum type in
  * include/cutils/debugger.h.
@@ -153,9 +137,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;
@@ -255,10 +236,7 @@
 
 static bool should_attach_gdb(debugger_request_t* request) {
   if (request->action == DEBUGGER_ACTION_CRASH) {
-    char value[PROPERTY_VALUE_MAX];
-    property_get("debug.db.uid", value, "-1");
-    int debug_uid = atoi(value);
-    return debug_uid >= 0 && request->uid <= (uid_t)debug_uid;
+    return property_get_bool("debug.debuggerd.wait_for_gdb", false);
   }
   return false;
 }
@@ -589,7 +567,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/debuggerd/mips/machine.cpp b/debuggerd/mips/machine.cpp
index f7b8a86..cbf272a 100644
--- a/debuggerd/mips/machine.cpp
+++ b/debuggerd/mips/machine.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "DEBUG"
+
 #include <errno.h>
 #include <inttypes.h>
 #include <stdint.h>
@@ -21,6 +23,7 @@
 #include <sys/ptrace.h>
 
 #include <backtrace/Backtrace.h>
+#include <log/log.h>
 
 #include "machine.h"
 #include "utility.h"
@@ -32,7 +35,7 @@
 void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
   pt_regs r;
   if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r)) {
-    _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+    ALOGE("cannot get registers: %s\n", strerror(errno));
     return;
   }
 
@@ -61,7 +64,7 @@
 void dump_registers(log_t* log, pid_t tid) {
   pt_regs r;
   if(ptrace(PTRACE_GETREGS, tid, 0, &r)) {
-    _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+    ALOGE("cannot get registers: %s\n", strerror(errno));
     return;
   }
 
diff --git a/debuggerd/mips64/machine.cpp b/debuggerd/mips64/machine.cpp
index 293dcf6..0a8d532 100644
--- a/debuggerd/mips64/machine.cpp
+++ b/debuggerd/mips64/machine.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "DEBUG"
+
 #include <errno.h>
 #include <inttypes.h>
 #include <stdint.h>
@@ -21,6 +23,7 @@
 #include <sys/ptrace.h>
 
 #include <backtrace/Backtrace.h>
+#include <log/log.h>
 
 #include "machine.h"
 #include "utility.h"
@@ -32,7 +35,7 @@
 void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
   pt_regs r;
   if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r)) {
-    _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+    ALOGE("cannot get registers: %s\n", strerror(errno));
     return;
   }
 
@@ -61,7 +64,7 @@
 void dump_registers(log_t* log, pid_t tid) {
   pt_regs r;
   if(ptrace(PTRACE_GETREGS, tid, 0, &r)) {
-    _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+    ALOGE("cannot get registers: %s\n", strerror(errno));
     return;
   }
 
diff --git a/debuggerd/test/BacktraceMock.h b/debuggerd/test/BacktraceMock.h
index 5c252ab..f75534e 100644
--- a/debuggerd/test/BacktraceMock.h
+++ b/debuggerd/test/BacktraceMock.h
@@ -60,12 +60,14 @@
     }
     size_t bytes_available = buffer_.size() - offset;
 
-    if (bytes_partial_read_ > 0) {
+    if (do_partial_read_) {
       // Do a partial read.
       if (bytes > bytes_partial_read_) {
         bytes = bytes_partial_read_;
       }
       bytes_partial_read_ -= bytes;
+      // Only support a single partial read.
+      do_partial_read_ = false;
     } else if (bytes > bytes_available) {
       bytes = bytes_available;
     }
@@ -82,6 +84,7 @@
     buffer_.resize(bytes);
     memcpy(buffer_.data(), buffer, bytes);
     bytes_partial_read_ = 0;
+    do_partial_read_ = false;
     last_read_addr_ = 0;
   }
 
@@ -90,12 +93,14 @@
       abort();
     }
     bytes_partial_read_ = bytes;
+    do_partial_read_ = true;
   }
 
  private:
   std::vector<uint8_t> buffer_;
   size_t bytes_partial_read_ = 0;
   uintptr_t last_read_addr_ = 0;
+  bool do_partial_read_ = false;
 };
 
 #endif //  _DEBUGGERD_TEST_BACKTRACE_MOCK_H
diff --git a/debuggerd/test/dump_memory_test.cpp b/debuggerd/test/dump_memory_test.cpp
index fcb0108..75e7028 100644
--- a/debuggerd/test/dump_memory_test.cpp
+++ b/debuggerd/test/dump_memory_test.cpp
@@ -288,9 +288,9 @@
   ASSERT_STREQ(g_expected_partial_dump, tombstone_contents.c_str());
 
 #if defined(__LP64__)
-  ASSERT_STREQ("DEBUG Bytes read 102, is not a multiple of 8\n", getFakeLogPrint().c_str());
+  ASSERT_STREQ("6 DEBUG Bytes read 102, is not a multiple of 8\n", getFakeLogPrint().c_str());
 #else
-  ASSERT_STREQ("DEBUG Bytes read 102, is not a multiple of 4\n", getFakeLogPrint().c_str());
+  ASSERT_STREQ("6 DEBUG Bytes read 102, is not a multiple of 4\n", getFakeLogPrint().c_str());
 #endif
 
   // Verify that the log buf is empty, and no error messages.
@@ -313,12 +313,12 @@
   ASSERT_STREQ(g_expected_partial_dump, tombstone_contents.c_str());
 
 #if defined(__LP64__)
-  ASSERT_STREQ("DEBUG Bytes read 45, is not a multiple of 8\n"
-               "DEBUG Bytes after second read 106, is not a multiple of 8\n",
+  ASSERT_STREQ("6 DEBUG Bytes read 45, is not a multiple of 8\n"
+               "6 DEBUG Bytes after second read 106, is not a multiple of 8\n",
                getFakeLogPrint().c_str());
 #else
-  ASSERT_STREQ("DEBUG Bytes read 45, is not a multiple of 4\n"
-               "DEBUG Bytes after second read 106, is not a multiple of 4\n",
+  ASSERT_STREQ("6 DEBUG Bytes read 45, is not a multiple of 4\n"
+               "6 DEBUG Bytes after second read 106, is not a multiple of 4\n",
                getFakeLogPrint().c_str());
 #endif
 
@@ -502,3 +502,239 @@
   ASSERT_STREQ("", getFakeLogBuf().c_str());
   ASSERT_STREQ("", getFakeLogPrint().c_str());
 }
+
+TEST_F(DumpMemoryTest, first_read_empty) {
+  uint8_t buffer[256];
+  for (size_t i = 0; i < sizeof(buffer); i++) {
+    buffer[i] = i;
+  }
+  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+  backtrace_mock_->SetPartialReadAmount(0);
+
+  size_t page_size = sysconf(_SC_PAGE_SIZE);
+  uintptr_t addr = 0x10000020 + page_size - 120;
+  dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4");
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  const char* expected_dump = \
+"\nmemory near r4:\n"
+#if defined(__LP64__)
+"    0000000010000f88 ---------------- ----------------  ................\n"
+"    0000000010000f98 ---------------- ----------------  ................\n"
+"    0000000010000fa8 ---------------- ----------------  ................\n"
+"    0000000010000fb8 ---------------- ----------------  ................\n"
+"    0000000010000fc8 ---------------- ----------------  ................\n"
+"    0000000010000fd8 ---------------- ----------------  ................\n"
+"    0000000010000fe8 ---------------- ----------------  ................\n"
+"    0000000010000ff8 ---------------- 7f7e7d7c7b7a7978  ........xyz{|}~.\n"
+"    0000000010001008 8786858483828180 8f8e8d8c8b8a8988  ................\n"
+"    0000000010001018 9796959493929190 9f9e9d9c9b9a9998  ................\n"
+"    0000000010001028 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8  ................\n"
+"    0000000010001038 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8  ................\n"
+"    0000000010001048 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8  ................\n"
+"    0000000010001058 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8  ................\n"
+"    0000000010001068 e7e6e5e4e3e2e1e0 efeeedecebeae9e8  ................\n"
+"    0000000010001078 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8  ................\n";
+#else
+"    10000f88 -------- -------- -------- --------  ................\n"
+"    10000f98 -------- -------- -------- --------  ................\n"
+"    10000fa8 -------- -------- -------- --------  ................\n"
+"    10000fb8 -------- -------- -------- --------  ................\n"
+"    10000fc8 -------- -------- -------- --------  ................\n"
+"    10000fd8 -------- -------- -------- --------  ................\n"
+"    10000fe8 -------- -------- -------- --------  ................\n"
+"    10000ff8 -------- -------- 7b7a7978 7f7e7d7c  ........xyz{|}~.\n"
+"    10001008 83828180 87868584 8b8a8988 8f8e8d8c  ................\n"
+"    10001018 93929190 97969594 9b9a9998 9f9e9d9c  ................\n"
+"    10001028 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac  ................\n"
+"    10001038 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc  ................\n"
+"    10001048 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc  ................\n"
+"    10001058 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc  ................\n"
+"    10001068 e3e2e1e0 e7e6e5e4 ebeae9e8 efeeedec  ................\n"
+"    10001078 f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc  ................\n";
+#endif
+  ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+  // Verify that the log buf is empty, and no error messages.
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, first_read_empty_second_read_stops) {
+  uint8_t buffer[224];
+  for (size_t i = 0; i < sizeof(buffer); i++) {
+    buffer[i] = i;
+  }
+  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+  backtrace_mock_->SetPartialReadAmount(0);
+
+  size_t page_size = sysconf(_SC_PAGE_SIZE);
+  uintptr_t addr = 0x10000020 + page_size - 192;
+  dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4");
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  const char* expected_dump = \
+"\nmemory near r4:\n"
+#if defined(__LP64__)
+"    0000000010000f40 ---------------- ----------------  ................\n"
+"    0000000010000f50 ---------------- ----------------  ................\n"
+"    0000000010000f60 ---------------- ----------------  ................\n"
+"    0000000010000f70 ---------------- ----------------  ................\n"
+"    0000000010000f80 ---------------- ----------------  ................\n"
+"    0000000010000f90 ---------------- ----------------  ................\n"
+"    0000000010000fa0 ---------------- ----------------  ................\n"
+"    0000000010000fb0 ---------------- ----------------  ................\n"
+"    0000000010000fc0 ---------------- ----------------  ................\n"
+"    0000000010000fd0 ---------------- ----------------  ................\n"
+"    0000000010000fe0 ---------------- ----------------  ................\n"
+"    0000000010000ff0 ---------------- ----------------  ................\n"
+"    0000000010001000 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8  ................\n"
+"    0000000010001010 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8  ................\n"
+"    0000000010001020 ---------------- ----------------  ................\n"
+"    0000000010001030 ---------------- ----------------  ................\n";
+#else
+"    10000f40 -------- -------- -------- --------  ................\n"
+"    10000f50 -------- -------- -------- --------  ................\n"
+"    10000f60 -------- -------- -------- --------  ................\n"
+"    10000f70 -------- -------- -------- --------  ................\n"
+"    10000f80 -------- -------- -------- --------  ................\n"
+"    10000f90 -------- -------- -------- --------  ................\n"
+"    10000fa0 -------- -------- -------- --------  ................\n"
+"    10000fb0 -------- -------- -------- --------  ................\n"
+"    10000fc0 -------- -------- -------- --------  ................\n"
+"    10000fd0 -------- -------- -------- --------  ................\n"
+"    10000fe0 -------- -------- -------- --------  ................\n"
+"    10000ff0 -------- -------- -------- --------  ................\n"
+"    10001000 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc  ................\n"
+"    10001010 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc  ................\n"
+"    10001020 -------- -------- -------- --------  ................\n"
+"    10001030 -------- -------- -------- --------  ................\n";
+#endif
+  ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+  // Verify that the log buf is empty, and no error messages.
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, first_read_empty_next_page_out_of_range) {
+  uint8_t buffer[256];
+  for (size_t i = 0; i < sizeof(buffer); i++) {
+    buffer[i] = i;
+  }
+  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+  backtrace_mock_->SetPartialReadAmount(0);
+
+  uintptr_t addr = 0x10000020;
+  dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4");
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  const char* expected_dump = \
+"\nmemory near r4:\n"
+#if defined(__LP64__)
+"    0000000010000000 ---------------- ----------------  ................\n"
+"    0000000010000010 ---------------- ----------------  ................\n"
+"    0000000010000020 ---------------- ----------------  ................\n"
+"    0000000010000030 ---------------- ----------------  ................\n"
+"    0000000010000040 ---------------- ----------------  ................\n"
+"    0000000010000050 ---------------- ----------------  ................\n"
+"    0000000010000060 ---------------- ----------------  ................\n"
+"    0000000010000070 ---------------- ----------------  ................\n"
+"    0000000010000080 ---------------- ----------------  ................\n"
+"    0000000010000090 ---------------- ----------------  ................\n"
+"    00000000100000a0 ---------------- ----------------  ................\n"
+"    00000000100000b0 ---------------- ----------------  ................\n"
+"    00000000100000c0 ---------------- ----------------  ................\n"
+"    00000000100000d0 ---------------- ----------------  ................\n"
+"    00000000100000e0 ---------------- ----------------  ................\n"
+"    00000000100000f0 ---------------- ----------------  ................\n";
+#else
+"    10000000 -------- -------- -------- --------  ................\n"
+"    10000010 -------- -------- -------- --------  ................\n"
+"    10000020 -------- -------- -------- --------  ................\n"
+"    10000030 -------- -------- -------- --------  ................\n"
+"    10000040 -------- -------- -------- --------  ................\n"
+"    10000050 -------- -------- -------- --------  ................\n"
+"    10000060 -------- -------- -------- --------  ................\n"
+"    10000070 -------- -------- -------- --------  ................\n"
+"    10000080 -------- -------- -------- --------  ................\n"
+"    10000090 -------- -------- -------- --------  ................\n"
+"    100000a0 -------- -------- -------- --------  ................\n"
+"    100000b0 -------- -------- -------- --------  ................\n"
+"    100000c0 -------- -------- -------- --------  ................\n"
+"    100000d0 -------- -------- -------- --------  ................\n"
+"    100000e0 -------- -------- -------- --------  ................\n"
+"    100000f0 -------- -------- -------- --------  ................\n";
+#endif
+  ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+  // Verify that the log buf is empty, and no error messages.
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
+
+TEST_F(DumpMemoryTest, first_read_empty_next_page_out_of_range_fence_post) {
+  uint8_t buffer[256];
+  for (size_t i = 0; i < sizeof(buffer); i++) {
+    buffer[i] = i;
+  }
+  backtrace_mock_->SetReadData(buffer, sizeof(buffer));
+  backtrace_mock_->SetPartialReadAmount(0);
+
+  size_t page_size = sysconf(_SC_PAGE_SIZE);
+  uintptr_t addr = 0x10000020 + page_size - 256;
+
+  dump_memory(&log_, backtrace_mock_.get(), addr, "memory near %.2s:", "r4");
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  const char* expected_dump = \
+"\nmemory near r4:\n"
+#if defined(__LP64__)
+"    0000000010000f00 ---------------- ----------------  ................\n"
+"    0000000010000f10 ---------------- ----------------  ................\n"
+"    0000000010000f20 ---------------- ----------------  ................\n"
+"    0000000010000f30 ---------------- ----------------  ................\n"
+"    0000000010000f40 ---------------- ----------------  ................\n"
+"    0000000010000f50 ---------------- ----------------  ................\n"
+"    0000000010000f60 ---------------- ----------------  ................\n"
+"    0000000010000f70 ---------------- ----------------  ................\n"
+"    0000000010000f80 ---------------- ----------------  ................\n"
+"    0000000010000f90 ---------------- ----------------  ................\n"
+"    0000000010000fa0 ---------------- ----------------  ................\n"
+"    0000000010000fb0 ---------------- ----------------  ................\n"
+"    0000000010000fc0 ---------------- ----------------  ................\n"
+"    0000000010000fd0 ---------------- ----------------  ................\n"
+"    0000000010000fe0 ---------------- ----------------  ................\n"
+"    0000000010000ff0 ---------------- ----------------  ................\n";
+#else
+"    10000f00 -------- -------- -------- --------  ................\n"
+"    10000f10 -------- -------- -------- --------  ................\n"
+"    10000f20 -------- -------- -------- --------  ................\n"
+"    10000f30 -------- -------- -------- --------  ................\n"
+"    10000f40 -------- -------- -------- --------  ................\n"
+"    10000f50 -------- -------- -------- --------  ................\n"
+"    10000f60 -------- -------- -------- --------  ................\n"
+"    10000f70 -------- -------- -------- --------  ................\n"
+"    10000f80 -------- -------- -------- --------  ................\n"
+"    10000f90 -------- -------- -------- --------  ................\n"
+"    10000fa0 -------- -------- -------- --------  ................\n"
+"    10000fb0 -------- -------- -------- --------  ................\n"
+"    10000fc0 -------- -------- -------- --------  ................\n"
+"    10000fd0 -------- -------- -------- --------  ................\n"
+"    10000fe0 -------- -------- -------- --------  ................\n"
+"    10000ff0 -------- -------- -------- --------  ................\n";
+#endif
+  ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
+
+  // Verify that the log buf is empty, and no error messages.
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("", getFakeLogPrint().c_str());
+}
diff --git a/debuggerd/test/log_fake.cpp b/debuggerd/test/log_fake.cpp
index 26523ad..d584a5e 100644
--- a/debuggerd/test/log_fake.cpp
+++ b/debuggerd/test/log_fake.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <errno.h>
 #include <stdarg.h>
 
 #include <string>
@@ -44,14 +45,16 @@
   return g_fake_log_print;
 }
 
-extern "C" int __android_log_buf_write(int, int, const char* tag, const char* msg) {
+extern "C" int __android_log_buf_write(int bufId, int prio, const char* tag, const char* msg) {
+  g_fake_log_buf += std::to_string(bufId) + ' ' + std::to_string(prio) + ' ';
   g_fake_log_buf += tag;
   g_fake_log_buf += ' ';
   g_fake_log_buf += msg;
   return 1;
 }
 
-extern "C" int __android_log_print(int, const char* tag, const char* fmt, ...) {
+extern "C" int __android_log_print(int prio, const char* tag, const char* fmt, ...) {
+  g_fake_log_print += std::to_string(prio) + ' ';
   g_fake_log_print += tag;
   g_fake_log_print += ' ';
 
@@ -70,6 +73,7 @@
 }
 
 extern "C" struct logger_list* android_logger_list_open(log_id_t, int, unsigned int, pid_t) {
+  errno = EACCES;
   return nullptr;
 }
 
diff --git a/debuggerd/test/dump_maps_test.cpp b/debuggerd/test/tombstone_test.cpp
similarity index 90%
rename from debuggerd/test/dump_maps_test.cpp
rename to debuggerd/test/tombstone_test.cpp
index 230f4f5..a771a39 100644
--- a/debuggerd/test/dump_maps_test.cpp
+++ b/debuggerd/test/tombstone_test.cpp
@@ -45,7 +45,7 @@
 void dump_backtrace_to_log(Backtrace*, log_t*, char const*) {
 }
 
-class DumpMapsTest : public ::testing::Test {
+class TombstoneTest : public ::testing::Test {
  protected:
   virtual void SetUp() {
     map_mock_.reset(new BacktraceMapMock());
@@ -92,7 +92,7 @@
   log_t log_;
 };
 
-TEST_F(DumpMapsTest, single_map) {
+TEST_F(TombstoneTest, single_map) {
   backtrace_map_t map;
 #if defined(__LP64__)
   map.start = 0x123456789abcd000UL;
@@ -122,7 +122,7 @@
   ASSERT_STREQ("", getFakeLogPrint().c_str());
 }
 
-TEST_F(DumpMapsTest, single_map_elf_build_id) {
+TEST_F(TombstoneTest, single_map_elf_build_id) {
   backtrace_map_t map;
 #if defined(__LP64__)
   map.start = 0x123456789abcd000UL;
@@ -157,7 +157,7 @@
 
 // Even though build id is present, it should not be printed in either of
 // these cases.
-TEST_F(DumpMapsTest, single_map_no_build_id) {
+TEST_F(TombstoneTest, single_map_no_build_id) {
   backtrace_map_t map;
 #if defined(__LP64__)
   map.start = 0x123456789abcd000UL;
@@ -194,7 +194,7 @@
   ASSERT_STREQ("", getFakeLogPrint().c_str());
 }
 
-TEST_F(DumpMapsTest, multiple_maps) {
+TEST_F(TombstoneTest, multiple_maps) {
   backtrace_map_t map;
 
   map.start = 0xa234000;
@@ -256,7 +256,7 @@
   ASSERT_STREQ("", getFakeLogPrint().c_str());
 }
 
-TEST_F(DumpMapsTest, multiple_maps_fault_address_before) {
+TEST_F(TombstoneTest, multiple_maps_fault_address_before) {
   backtrace_map_t map;
 
   map.start = 0xa434000;
@@ -310,7 +310,7 @@
   ASSERT_STREQ("", getFakeLogPrint().c_str());
 }
 
-TEST_F(DumpMapsTest, multiple_maps_fault_address_between) {
+TEST_F(TombstoneTest, multiple_maps_fault_address_between) {
   backtrace_map_t map;
 
   map.start = 0xa434000;
@@ -364,7 +364,7 @@
   ASSERT_STREQ("", getFakeLogPrint().c_str());
 }
 
-TEST_F(DumpMapsTest, multiple_maps_fault_address_in_map) {
+TEST_F(TombstoneTest, multiple_maps_fault_address_in_map) {
   backtrace_map_t map;
 
   map.start = 0xa434000;
@@ -416,7 +416,7 @@
   ASSERT_STREQ("", getFakeLogPrint().c_str());
 }
 
-TEST_F(DumpMapsTest, multiple_maps_fault_address_after) {
+TEST_F(TombstoneTest, multiple_maps_fault_address_after) {
   backtrace_map_t map;
 
   map.start = 0xa434000;
@@ -474,7 +474,7 @@
   ASSERT_STREQ("", getFakeLogPrint().c_str());
 }
 
-TEST_F(DumpMapsTest, multiple_maps_getsiginfo_fail) {
+TEST_F(TombstoneTest, multiple_maps_getsiginfo_fail) {
   backtrace_map_t map;
 
   map.start = 0xa434000;
@@ -493,7 +493,6 @@
   ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
   ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
   const char* expected_dump = \
-"Cannot get siginfo for 100: Bad address\n"
 "\nmemory map:\n"
 #if defined(__LP64__)
 "    00000000'0a434000-00000000'0a434fff -w-      1000      1000  (load base 0xd000)\n";
@@ -503,12 +502,11 @@
   ASSERT_STREQ(expected_dump, tombstone_contents.c_str());
 
   // Verify that the log buf is empty, and no error messages.
-  ASSERT_STREQ("DEBUG Cannot get siginfo for 100: Bad address\n",
-               getFakeLogBuf().c_str());
-  ASSERT_STREQ("", getFakeLogPrint().c_str());
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("6 DEBUG Cannot get siginfo for 100: Bad address\n\n", getFakeLogPrint().c_str());
 }
 
-TEST_F(DumpMapsTest, multiple_maps_check_signal_has_si_addr) {
+TEST_F(TombstoneTest, multiple_maps_check_signal_has_si_addr) {
   backtrace_map_t map;
 
   map.start = 0xa434000;
@@ -569,3 +567,33 @@
     ASSERT_STREQ("", getFakeLogPrint().c_str());
   }
 }
+
+TEST_F(TombstoneTest, dump_signal_info_error) {
+  siginfo_t si;
+  si.si_signo = 0;
+  ptrace_set_fake_getsiginfo(si);
+
+  dump_signal_info(&log_, 123, SIGSEGV, 10);
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  ASSERT_STREQ("", tombstone_contents.c_str());
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("6 DEBUG cannot get siginfo: Bad address\n\n", getFakeLogPrint().c_str());
+}
+
+TEST_F(TombstoneTest, dump_log_file_error) {
+  log_.should_retrieve_logcat = true;
+  dump_log_file(&log_, 123, "/fake/filename", 10);
+
+  std::string tombstone_contents;
+  ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0);
+  ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents));
+  ASSERT_STREQ("", tombstone_contents.c_str());
+
+  ASSERT_STREQ("", getFakeLogBuf().c_str());
+  ASSERT_STREQ("6 DEBUG Unable to open /fake/filename: Permission denied\n\n",
+               getFakeLogPrint().c_str());
+}
diff --git a/debuggerd/tombstone.cpp b/debuggerd/tombstone.cpp
index b0ad274..114c7e4 100644
--- a/debuggerd/tombstone.cpp
+++ b/debuggerd/tombstone.cpp
@@ -180,7 +180,7 @@
   siginfo_t si;
   memset(&si, 0, sizeof(si));
   if (ptrace(PTRACE_GETSIGINFO, tid, 0, &si) == -1) {
-    _LOG(log, logtype::HEADER, "cannot get siginfo: %s\n", strerror(errno));
+    ALOGE("cannot get siginfo: %s\n", strerror(errno));
     return;
   }
 
@@ -338,7 +338,7 @@
     print_fault_address_marker = signal_has_si_addr(si.si_signo);
     addr = reinterpret_cast<uintptr_t>(si.si_addr);
   } else {
-    _LOG(log, logtype::ERROR, "Cannot get siginfo for %d: %s\n", tid, strerror(errno));
+    ALOGE("Cannot get siginfo for %d: %s\n", tid, strerror(errno));
   }
 
   _LOG(log, logtype::MAPS, "\n");
@@ -448,7 +448,7 @@
 
     // Skip this thread if cannot ptrace it
     if (ptrace(PTRACE_ATTACH, new_tid, 0, 0) < 0) {
-      _LOG(log, logtype::ERROR, "ptrace attach to %d failed: %s\n", new_tid, strerror(errno));
+      ALOGE("ptrace attach to %d failed: %s\n", new_tid, strerror(errno));
       continue;
     }
 
@@ -471,7 +471,7 @@
     log->current_tid = log->crashed_tid;
 
     if (ptrace(PTRACE_DETACH, new_tid, 0, 0) != 0) {
-      _LOG(log, logtype::ERROR, "ptrace detach from %d failed: %s\n", new_tid, strerror(errno));
+      ALOGE("ptrace detach from %d failed: %s\n", new_tid, strerror(errno));
       detach_failed = true;
     }
   }
@@ -517,13 +517,11 @@
         // non-blocking EOF; we're done
         break;
       } else {
-        _LOG(log, logtype::ERROR, "Error while reading log: %s\n",
-          strerror(-actual));
+        ALOGE("Error while reading log: %s\n", strerror(-actual));
         break;
       }
     } else if (actual == 0) {
-      _LOG(log, logtype::ERROR, "Got zero bytes while reading log: %s\n",
-        strerror(errno));
+      ALOGE("Got zero bytes while reading log: %s\n", strerror(errno));
       break;
     }
 
@@ -789,11 +787,11 @@
   log.crashed_tid = tid;
 
   if ((mkdir(TOMBSTONE_DIR, 0755) == -1) && (errno != EEXIST)) {
-    _LOG(&log, logtype::ERROR, "failed to create %s: %s\n", TOMBSTONE_DIR, strerror(errno));
+    ALOGE("failed to create %s: %s\n", TOMBSTONE_DIR, strerror(errno));
   }
 
   if (chown(TOMBSTONE_DIR, AID_SYSTEM, AID_SYSTEM) == -1) {
-    _LOG(&log, logtype::ERROR, "failed to change ownership of %s: %s\n", TOMBSTONE_DIR, strerror(errno));
+    ALOGE("failed to change ownership of %s: %s\n", TOMBSTONE_DIR, strerror(errno));
   }
 
   int fd = -1;
@@ -801,11 +799,11 @@
   if (selinux_android_restorecon(TOMBSTONE_DIR, 0) == 0) {
     path = find_and_open_tombstone(&fd);
   } else {
-    _LOG(&log, logtype::ERROR, "Failed to restore security context, not writing tombstone.\n");
+    ALOGE("Failed to restore security context, not writing tombstone.\n");
   }
 
   if (fd < 0) {
-    _LOG(&log, logtype::ERROR, "Skipping tombstone write, nothing to do.\n");
+    ALOGE("Skipping tombstone write, nothing to do.\n");
     *detach_failed = false;
     return NULL;
   }
diff --git a/debuggerd/utility.cpp b/debuggerd/utility.cpp
index 9f340a8..f5d6ec1 100644
--- a/debuggerd/utility.cpp
+++ b/debuggerd/utility.cpp
@@ -35,8 +35,7 @@
 
 // Whitelist output desired in the logcat output.
 bool is_allowed_in_logcat(enum logtype ltype) {
-  if ((ltype == ERROR)
-   || (ltype == HEADER)
+  if ((ltype == HEADER)
    || (ltype == REGISTERS)
    || (ltype == BACKTRACE)) {
     return true;
@@ -157,13 +156,28 @@
     bytes &= ~(sizeof(uintptr_t) - 1);
   }
 
-  if (bytes < MEMORY_BYTES_TO_DUMP && bytes > 0) {
-    // Try to do one more read. This could happen if a read crosses a map, but
-    // the maps do not have any break between them. Only requires one extra
-    // read because a map has to contain at least one page, and the total
-    // number of bytes to dump is smaller than a page.
-    size_t bytes2 = backtrace->Read(addr + bytes, reinterpret_cast<uint8_t*>(data) + bytes,
-                                    sizeof(data) - bytes);
+  uintptr_t start = 0;
+  bool skip_2nd_read = false;
+  if (bytes == 0) {
+    // In this case, we might want to try another read at the beginning of
+    // the next page only if it's within the amount of memory we would have
+    // read.
+    size_t page_size = sysconf(_SC_PAGE_SIZE);
+    start = ((addr + (page_size - 1)) & ~(page_size - 1)) - addr;
+    if (start == 0 || start >= MEMORY_BYTES_TO_DUMP) {
+      skip_2nd_read = true;
+    }
+  }
+
+  if (bytes < MEMORY_BYTES_TO_DUMP && !skip_2nd_read) {
+    // Try to do one more read. This could happen if a read crosses a map,
+    // but the maps do not have any break between them. Or it could happen
+    // if reading from an unreadable map, but the read would cross back
+    // into a readable map. Only requires one extra read because a map has
+    // to contain at least one page, and the total number of bytes to dump
+    // is smaller than a page.
+    size_t bytes2 = backtrace->Read(addr + start + bytes, reinterpret_cast<uint8_t*>(data) + bytes,
+                                    sizeof(data) - bytes - start);
     bytes += bytes2;
     if (bytes2 > 0 && bytes % sizeof(uintptr_t) != 0) {
       // This should never happen, but we'll try and continue any way.
@@ -179,15 +193,16 @@
   // On 32-bit machines, there are still 16 bytes per line but addresses and
   // words are of course presented differently.
   uintptr_t* data_ptr = data;
+  size_t current = 0;
+  size_t total_bytes = start + bytes;
   for (size_t line = 0; line < MEMORY_BYTES_TO_DUMP / MEMORY_BYTES_PER_LINE; line++) {
     std::string logline;
     android::base::StringAppendF(&logline, "    %" PRIPTR, addr);
 
     addr += MEMORY_BYTES_PER_LINE;
     std::string ascii;
-    for (size_t i = 0; i < MEMORY_BYTES_PER_LINE / sizeof(uintptr_t); i++, data_ptr++) {
-      if (bytes >= sizeof(uintptr_t)) {
-        bytes -= sizeof(uintptr_t);
+    for (size_t i = 0; i < MEMORY_BYTES_PER_LINE / sizeof(uintptr_t); i++) {
+      if (current >= start && current + sizeof(uintptr_t) <= total_bytes) {
         android::base::StringAppendF(&logline, " %" PRIPTR, *data_ptr);
 
         // Fill out the ascii string from the data.
@@ -199,10 +214,12 @@
             ascii += '.';
           }
         }
+        data_ptr++;
       } else {
         logline += ' ' + std::string(sizeof(uintptr_t) * 2, '-');
         ascii += std::string(sizeof(uintptr_t), '.');
       }
+      current += sizeof(uintptr_t);
     }
     _LOG(log, logtype::MEMORY, "%s  %s\n", logline.c_str(), ascii.c_str());
   }
diff --git a/debuggerd/utility.h b/debuggerd/utility.h
index 263374d..8bef192 100644
--- a/debuggerd/utility.h
+++ b/debuggerd/utility.h
@@ -59,7 +59,6 @@
 
 // List of types of logs to simplify the logging decision in _LOG
 enum logtype {
-  ERROR,
   HEADER,
   THREAD,
   REGISTERS,
diff --git a/debuggerd/x86/machine.cpp b/debuggerd/x86/machine.cpp
index c5f9259..af10817 100644
--- a/debuggerd/x86/machine.cpp
+++ b/debuggerd/x86/machine.cpp
@@ -14,12 +14,15 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "DEBUG"
+
 #include <errno.h>
 #include <stdint.h>
 #include <string.h>
 #include <sys/ptrace.h>
 
 #include <backtrace/Backtrace.h>
+#include <log/log.h>
 
 #include "machine.h"
 #include "utility.h"
@@ -27,7 +30,7 @@
 void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
   struct pt_regs r;
   if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r) == -1) {
-    _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+    ALOGE("cannot get registers: %s\n", strerror(errno));
     return;
   }
 
@@ -44,7 +47,7 @@
 void dump_registers(log_t* log, pid_t tid) {
   struct pt_regs r;
   if (ptrace(PTRACE_GETREGS, tid, 0, &r) == -1) {
-    _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+    ALOGE("cannot get registers: %s\n", strerror(errno));
     return;
   }
 
diff --git a/debuggerd/x86_64/machine.cpp b/debuggerd/x86_64/machine.cpp
index 4f09a5d..fc86bc2 100644
--- a/debuggerd/x86_64/machine.cpp
+++ b/debuggerd/x86_64/machine.cpp
@@ -14,6 +14,8 @@
 ** limitations under the License.
 */
 
+#define LOG_TAG "DEBUG"
+
 #include <errno.h>
 #include <stdint.h>
 #include <sys/ptrace.h>
@@ -21,6 +23,7 @@
 #include <sys/user.h>
 
 #include <backtrace/Backtrace.h>
+#include <log/log.h>
 
 #include "machine.h"
 #include "utility.h"
@@ -28,7 +31,7 @@
 void dump_memory_and_code(log_t* log, Backtrace* backtrace) {
   struct user_regs_struct r;
   if (ptrace(PTRACE_GETREGS, backtrace->Tid(), 0, &r) == -1) {
-    _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+    ALOGE("cannot get registers: %s\n", strerror(errno));
     return;
   }
 
@@ -45,7 +48,7 @@
 void dump_registers(log_t* log, pid_t tid) {
   struct user_regs_struct r;
   if (ptrace(PTRACE_GETREGS, tid, 0, &r) == -1) {
-    _LOG(log, logtype::ERROR, "cannot get registers: %s\n", strerror(errno));
+    ALOGE("cannot get registers: %s\n", strerror(errno));
     return;
   }
 
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..f467f81 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));
@@ -158,10 +162,10 @@
     } else if (!strcmp(fs_type, "f2fs")) {
             char *f2fs_fsck_argv[] = {
                     F2FS_FSCK_BIN,
-                    "-f",
+                    "-a",
                     blk_device
             };
-        INFO("Running %s -f %s\n", F2FS_FSCK_BIN, blk_device);
+        INFO("Running %s -a %s\n", F2FS_FSCK_BIN, blk_device);
 
         ret = android_fork_execvp_ext(ARRAY_SIZE(f2fs_fsck_argv), f2fs_fsck_argv,
                                       &status, true, LOG_KLOG | LOG_FILE,
diff --git a/fs_mgr/fs_mgr_verity.c b/fs_mgr/fs_mgr_verity.c
index 81c7c9a..a4a99c3 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 */
@@ -843,6 +859,7 @@
 int fs_mgr_update_verity_state(fs_mgr_verity_state_callback callback)
 {
     _Alignas(struct dm_ioctl) char buffer[DM_BUF_SIZE];
+    bool use_state = true;
     char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)];
     char *mount_point;
     char propbuf[PROPERTY_VALUE_MAX];
@@ -855,6 +872,16 @@
     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') {
+        if (fs_mgr_load_verity_state(&mode) == -1) {
+            return -1;
+        }
+        use_state = false; /* state is kept by the bootloader */
+    }
+
     fd = TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC));
 
     if (fd == -1) {
@@ -877,9 +904,11 @@
             continue;
         }
 
-        if (get_verity_state_offset(&fstab->recs[i], &offset) < 0 ||
-            read_verity_state(fstab->recs[i].verity_loc, offset, &mode) < 0) {
-            continue;
+        if (use_state) {
+            if (get_verity_state_offset(&fstab->recs[i], &offset) < 0 ||
+                read_verity_state(fstab->recs[i].verity_loc, offset, &mode) < 0) {
+                continue;
+            }
         }
 
         mount_point = basename(fstab->recs[i].mount_point);
@@ -893,7 +922,7 @@
 
         status = &buffer[io->data_start + sizeof(struct dm_target_spec)];
 
-        if (*status == 'C') {
+        if (use_state && *status == 'C') {
             if (write_verity_state(fstab->recs[i].verity_loc, offset,
                     VERITY_MODE_LOGGING) < 0) {
                 continue;
diff --git a/include/cutils/android_reboot.h b/include/cutils/android_reboot.h
index 85e1b7e..a3861a0 100644
--- a/include/cutils/android_reboot.h
+++ b/include/cutils/android_reboot.h
@@ -17,6 +17,8 @@
 #ifndef __CUTILS_ANDROID_REBOOT_H__
 #define __CUTILS_ANDROID_REBOOT_H__
 
+#include <mntent.h>
+
 __BEGIN_DECLS
 
 /* Commands */
@@ -28,6 +30,9 @@
 #define ANDROID_RB_PROPERTY "sys.powerctl"
 
 int android_reboot(int cmd, int flags, const char *arg);
+int android_reboot_with_callback(
+    int cmd, int flags, const char *arg,
+    void (*cb_on_remount)(const struct mntent*));
 
 __END_DECLS
 
diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h
index 02fe2b5..0c071ca 100644
--- a/include/private/android_filesystem_config.h
+++ b/include/private/android_filesystem_config.h
@@ -77,6 +77,7 @@
 #define AID_SDCARD_ALL    1035  /* access all users external storage */
 #define AID_LOGD          1036  /* log daemon */
 #define AID_SHARED_RELRO  1037  /* creator of shared GNU RELRO files */
+#define AID_DBUS          1038  /* dbus-daemon IPC broker process */
 
 #define AID_SHELL         2000  /* adb and debug shell user */
 #define AID_CACHE         2001  /* cache access */
@@ -168,6 +169,7 @@
     { "sdcard_all",    AID_SDCARD_ALL, },
     { "logd",          AID_LOGD, },
     { "shared_relro",  AID_SHARED_RELRO, },
+    { "dbus",          AID_DBUS, },
 
     { "shell",         AID_SHELL, },
     { "cache",         AID_CACHE, },
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/bootchart.cpp b/init/bootchart.cpp
index 95687cb..81b20fa 100644
--- a/init/bootchart.cpp
+++ b/init/bootchart.cpp
@@ -164,10 +164,11 @@
         // timeout. this is useful when using -wipe-data since the /data
         // partition is fresh.
         std::string cmdline;
+        const char* s;
         android::base::ReadFileToString("/proc/cmdline", &cmdline);
 #define KERNEL_OPTION  "androidboot.bootchart="
-        if (strstr(cmdline.c_str(), KERNEL_OPTION) != NULL) {
-            timeout = atoi(cmdline.c_str() + sizeof(KERNEL_OPTION) - 1);
+        if ((s = strstr(cmdline.c_str(), KERNEL_OPTION)) != NULL) {
+            timeout = atoi(s + sizeof(KERNEL_OPTION) - 1);
         }
     }
     if (timeout == 0)
diff --git a/init/builtins.cpp b/init/builtins.cpp
index ca31c50..8d47da4 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -16,7 +16,9 @@
 
 #include <errno.h>
 #include <fcntl.h>
+#include <mntent.h>
 #include <net/if.h>
+#include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -38,6 +40,7 @@
 #include <base/stringprintf.h>
 #include <cutils/partition_utils.h>
 #include <cutils/android_reboot.h>
+#include <logwrap/logwrap.h>
 #include <private/android_filesystem_config.h>
 
 #include "init.h"
@@ -49,6 +52,8 @@
 #include "log.h"
 
 #define chmod DO_NOT_USE_CHMOD_USE_FCHMODAT_SYMLINK_NOFOLLOW
+#define UNMOUNT_CHECK_MS 5000
+#define UNMOUNT_CHECK_TIMES 10
 
 int add_environment(const char *name, const char *value);
 
@@ -109,6 +114,67 @@
     }
 }
 
+static void unmount_and_fsck(const struct mntent *entry)
+{
+    if (strcmp(entry->mnt_type, "f2fs") && strcmp(entry->mnt_type, "ext4"))
+        return;
+
+    /* First, lazily unmount the directory. This unmount request finishes when
+     * all processes that open a file or directory in |entry->mnt_dir| exit.
+     */
+    TEMP_FAILURE_RETRY(umount2(entry->mnt_dir, MNT_DETACH));
+
+    /* Next, kill all processes except init, kthreadd, and kthreadd's
+     * children to finish the lazy unmount. Killing all processes here is okay
+     * because this callback function is only called right before reboot().
+     * It might be cleaner to selectively kill processes that actually use
+     * |entry->mnt_dir| rather than killing all, probably by reusing a function
+     * like killProcessesWithOpenFiles() in vold/, but the selinux policy does
+     * not allow init to scan /proc/<pid> files which the utility function
+     * heavily relies on. The policy does not allow the process to execute
+     * killall/pkill binaries either. Note that some processes might
+     * automatically restart after kill(), but that is not really a problem
+     * because |entry->mnt_dir| is no longer visible to such new processes.
+     */
+    service_for_each(service_stop);
+    TEMP_FAILURE_RETRY(kill(-1, SIGKILL));
+
+    int count = 0;
+    while (count++ < UNMOUNT_CHECK_TIMES) {
+        int fd = TEMP_FAILURE_RETRY(open(entry->mnt_fsname, O_RDONLY | O_EXCL));
+        if (fd >= 0) {
+            /* |entry->mnt_dir| has sucessfully been unmounted. */
+            close(fd);
+            break;
+        } else if (errno == EBUSY) {
+            /* Some processes using |entry->mnt_dir| are still alive. Wait for a
+             * while then retry.
+             */
+            TEMP_FAILURE_RETRY(
+                usleep(UNMOUNT_CHECK_MS * 1000 / UNMOUNT_CHECK_TIMES));
+            continue;
+        } else {
+            /* Cannot open the device. Give up. */
+            return;
+        }
+    }
+
+    int st;
+    if (!strcmp(entry->mnt_type, "f2fs")) {
+        const char *f2fs_argv[] = {
+            "/system/bin/fsck.f2fs", "-f", entry->mnt_fsname,
+        };
+        android_fork_execvp_ext(ARRAY_SIZE(f2fs_argv), (char **)f2fs_argv,
+                                &st, true, LOG_KLOG, true, NULL);
+    } else if (!strcmp(entry->mnt_type, "ext4")) {
+        const char *ext4_argv[] = {
+            "/system/bin/e2fsck", "-f", "-y", entry->mnt_fsname,
+        };
+        android_fork_execvp_ext(ARRAY_SIZE(ext4_argv), (char **)ext4_argv,
+                                &st, true, LOG_KLOG, true, NULL);
+    }
+}
+
 int do_class_start(int nargs, char **args)
 {
         /* Starting a class does not start services
@@ -402,11 +468,17 @@
     int child_ret = -1;
     int status;
     struct fstab *fstab;
+    char fstabfile[PROP_VALUE_MAX];
 
     if (nargs != 2) {
         return -1;
     }
 
+    if (expand_props(fstabfile, args[1], sizeof(fstabfile)) == -1) {
+        ERROR("mount_all: cannot expand '%s' \n", args[1]);
+        return -EINVAL;
+    }
+
     /*
      * Call fs_mgr_mount_all() to mount all filesystems.  We fork(2) and
      * do the call in the child to provide protection to the main init
@@ -430,7 +502,7 @@
     } else if (pid == 0) {
         /* child, call fs_mgr_mount_all() */
         klog_set_level(6);  /* So we can see what fs_mgr_mount_all() does */
-        fstab = fs_mgr_read_fstab(args[1]);
+        fstab = fs_mgr_read_fstab(fstabfile);
         child_ret = fs_mgr_mount_all(fstab);
         fs_mgr_free_fstab(fstab);
         if (child_ret == -1) {
@@ -559,6 +631,7 @@
     int len = 0;
     int cmd = 0;
     const char *reboot_target;
+    void (*callback_on_ro_remount)(const struct mntent*) = NULL;
 
     res = expand_props(command, args[1], sizeof(command));
     if (res) {
@@ -569,6 +642,7 @@
     if (strncmp(command, "shutdown", 8) == 0) {
         cmd = ANDROID_RB_POWEROFF;
         len = 8;
+        callback_on_ro_remount = unmount_and_fsck;
     } else if (strncmp(command, "reboot", 6) == 0) {
         cmd = ANDROID_RB_RESTART2;
         len = 6;
@@ -586,7 +660,8 @@
         return -EINVAL;
     }
 
-    return android_reboot(cmd, 0, reboot_target);
+    return android_reboot_with_callback(cmd, 0, reboot_target,
+                                        callback_on_ro_remount);
 }
 
 int do_trigger(int nargs, char **args)
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..4d62c87 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -220,40 +220,42 @@
     }
 
     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 = getfilecon(svc->args[0], &fcon);
+        if (rc < 0) {
+            ERROR("could not get context while starting '%s'\n", svc->name);
+            free(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);
-            }
-            freecon(mycon);
-            freecon(fcon);
-            if (rc < 0) {
-                ERROR("could not get context while starting '%s'\n", svc->name);
-                return;
-            }
+        rc = security_compute_create(mycon, fcon, string_to_security_class("process"), &scon);
+        if (rc == 0 && !strcmp(scon, mycon)) {
+            ERROR("Service %s does not have a SELinux domain defined.\n", svc->name);
+            free(mycon);
+            free(fcon);
+            free(scon);
+            return;
+        }
+        free(mycon);
+        free(fcon);
+        if (rc < 0) {
+            ERROR("could not get context while starting '%s'\n", svc->name);
+            return;
         }
     }
 
@@ -287,9 +289,19 @@
             }
         }
 
-        freecon(scon);
+        free(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 +347,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);
             }
@@ -366,7 +378,7 @@
         _exit(127);
     }
 
-    freecon(scon);
+    free(scon);
 
     if (pid < 0) {
         ERROR("failed to start '%s'\n", svc->name);
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/log.h b/init/log.h
index b804d1f..c5c30af 100644
--- a/init/log.h
+++ b/init/log.h
@@ -20,8 +20,11 @@
 #include <cutils/klog.h>
 
 #define ERROR(x...)   init_klog_write(KLOG_ERROR_LEVEL, x)
+#define WARNING(x...) init_klog_write(KLOG_WARNING_LEVEL, x)
 #define NOTICE(x...)  init_klog_write(KLOG_NOTICE_LEVEL, x)
 #define INFO(x...)    init_klog_write(KLOG_INFO_LEVEL, x)
+#define DEBUG(x...)   init_klog_write(KLOG_DEBUG_LEVEL, x)
+#define VERBOSE(x...) init_klog_write(KLOG_DEBUG_LEVEL, x)
 
 void init_klog_write(int level, const char* fmt, ...) __printflike(2, 3);
 int selinux_klog_callback(int level, const char* fmt, ...) __printflike(2, 3);
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/init/watchdogd.cpp b/init/watchdogd.cpp
index 881a4df..0d16db9 100644
--- a/init/watchdogd.cpp
+++ b/init/watchdogd.cpp
@@ -38,29 +38,30 @@
     int margin = 10;
     if (argc >= 3) margin = atoi(argv[2]);
 
-    NOTICE("watchdogd started (interval %d, margin %d)!\n", interval, margin);
+    NOTICE("started (interval %d, margin %d)!\n", interval, margin);
 
     int fd = open(DEV_NAME, O_RDWR|O_CLOEXEC);
     if (fd == -1) {
-        ERROR("watchdogd: Failed to open %s: %s\n", DEV_NAME, strerror(errno));
+        ERROR("Failed to open %s: %s\n", DEV_NAME, strerror(errno));
         return 1;
     }
 
     int timeout = interval + margin;
     int ret = ioctl(fd, WDIOC_SETTIMEOUT, &timeout);
     if (ret) {
-        ERROR("watchdogd: Failed to set timeout to %d: %s\n", timeout, strerror(errno));
+        ERROR("Failed to set timeout to %d: %s\n", timeout, strerror(errno));
         ret = ioctl(fd, WDIOC_GETTIMEOUT, &timeout);
         if (ret) {
-            ERROR("watchdogd: Failed to get timeout: %s\n", strerror(errno));
+            ERROR("Failed to get timeout: %s\n", strerror(errno));
         } else {
             if (timeout > margin) {
                 interval = timeout - margin;
             } else {
                 interval = 1;
             }
-            ERROR("watchdogd: Adjusted interval to timeout returned by driver: timeout %d, interval %d, margin %d\n",
-                  timeout, interval, margin);
+            WARNING("Adjusted interval to timeout returned by driver:"
+                    " timeout %d, interval %d, margin %d\n",
+                    timeout, interval, margin);
         }
     }
 
diff --git a/libbacktrace/Android.build.mk b/libbacktrace/Android.build.mk
index 35fed6d..4983b55 100644
--- a/libbacktrace/Android.build.mk
+++ b/libbacktrace/Android.build.mk
@@ -20,7 +20,7 @@
 LOCAL_MODULE_TAGS := $(module_tag)
 LOCAL_MULTILIB := $($(module)_multilib)
 ifeq ($(LOCAL_MULTILIB),both)
-ifneq ($(build_target),$(filter $(build_target),SHARED_LIBRARY STATIC_LIBRRARY))
+ifneq ($(build_target),$(filter $(build_target),SHARED_LIBRARY STATIC_LIBRARY))
   LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
   LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
 endif
diff --git a/libbacktrace/Android.mk b/libbacktrace/Android.mk
index be8b803..395d677 100644
--- a/libbacktrace/Android.mk
+++ b/libbacktrace/Android.mk
@@ -68,6 +68,14 @@
 build_type := host
 libbacktrace_multilib := both
 include $(LOCAL_PATH)/Android.build.mk
+libbacktrace_static_libraries := \
+	libbase \
+	liblog \
+	libunwind \
+
+build_target := STATIC_LIBRARY
+include $(LOCAL_PATH)/Android.build.mk
+libbacktrace_static_libraries :=
 
 #-------------------------------------------------------------------------
 # The libbacktrace_test library needed by backtrace_test.
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/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index 760f5cc..6bd7529 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -974,8 +974,8 @@
           << "Offset at " << i << " length " << j << " wrote too much data";
     }
   }
-  delete data;
-  delete expected;
+  delete[] data;
+  delete[] expected;
 }
 
 TEST(libbacktrace, thread_read) {
diff --git a/libcutils/android_reboot.c b/libcutils/android_reboot.c
index 6ae23c1..af7e189 100644
--- a/libcutils/android_reboot.c
+++ b/libcutils/android_reboot.c
@@ -14,43 +14,108 @@
  * limitations under the License.
  */
 
-#include <unistd.h>
-#include <sys/reboot.h>
-#include <sys/syscall.h>
-#include <sys/types.h>
-#include <sys/stat.h>
+#include <errno.h>
 #include <fcntl.h>
 #include <mntent.h>
+#include <stdbool.h>
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
+#include <sys/cdefs.h>
+#include <sys/mount.h>
+#include <sys/reboot.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
 
 #include <cutils/android_reboot.h>
+#include <cutils/klog.h>
+#include <cutils/list.h>
 
-#define UNUSED __attribute__((unused))
+#define TAG "android_reboot"
+#define READONLY_CHECK_MS 5000
+#define READONLY_CHECK_TIMES 50
 
-/* Check to see if /proc/mounts contains any writeable filesystems
- * backed by a block device.
- * Return true if none found, else return false.
+typedef struct {
+    struct listnode list;
+    struct mntent entry;
+} mntent_list;
+
+static bool has_mount_option(const char* opts, const char* opt_to_find)
+{
+  bool ret = false;
+  char* copy = NULL;
+  char* opt;
+  char* rem;
+
+  while ((opt = strtok_r(copy ? NULL : (copy = strdup(opts)), ",", &rem))) {
+      if (!strcmp(opt, opt_to_find)) {
+          ret = true;
+          break;
+      }
+  }
+
+  free(copy);
+  return ret;
+}
+
+static bool is_block_device(const char* fsname)
+{
+    return !strncmp(fsname, "/dev/block", 10);
+}
+
+/* Find all read+write block devices in /proc/mounts and add them to
+ * |rw_entries|.
  */
-static int remount_ro_done(void)
+static void find_rw(struct listnode* rw_entries)
 {
     FILE* fp;
     struct mntent* mentry;
-    int found_rw_fs = 0;
 
     if ((fp = setmntent("/proc/mounts", "r")) == NULL) {
-        /* If we can't read /proc/mounts, just give up. */
-        return 1;
+        KLOG_WARNING(TAG, "Failed to open /proc/mounts.\n");
+        return;
     }
     while ((mentry = getmntent(fp)) != NULL) {
-        if (!strncmp(mentry->mnt_fsname, "/dev/block", 10) && strstr(mentry->mnt_opts, "rw,")) {
-            found_rw_fs = 1;
-            break;
+        if (is_block_device(mentry->mnt_fsname) &&
+            has_mount_option(mentry->mnt_opts, "rw")) {
+            mntent_list* item = (mntent_list*)calloc(1, sizeof(mntent_list));
+            item->entry = *mentry;
+            item->entry.mnt_fsname = strdup(mentry->mnt_fsname);
+            item->entry.mnt_dir = strdup(mentry->mnt_dir);
+            item->entry.mnt_type = strdup(mentry->mnt_type);
+            item->entry.mnt_opts = strdup(mentry->mnt_opts);
+            list_add_tail(rw_entries, &item->list);
         }
     }
     endmntent(fp);
+}
 
-    return !found_rw_fs;
+static void free_entries(struct listnode* entries)
+{
+    struct listnode* node;
+    struct listnode* n;
+    list_for_each_safe(node, n, entries) {
+        mntent_list* item = node_to_item(node, mntent_list, list);
+        free(item->entry.mnt_fsname);
+        free(item->entry.mnt_dir);
+        free(item->entry.mnt_type);
+        free(item->entry.mnt_opts);
+        free(item);
+    }
+}
+
+static mntent_list* find_item(struct listnode* rw_entries, const char* fsname_to_find)
+{
+    struct listnode* node;
+    list_for_each(node, rw_entries) {
+        mntent_list* item = node_to_item(node, mntent_list, list);
+        if (!strcmp(item->entry.mnt_fsname, fsname_to_find)) {
+            return item;
+        }
+    }
+    return NULL;
 }
 
 /* Remounting filesystems read-only is difficult when there are files
@@ -64,38 +129,92 @@
  * repeatedly until there are no more writable filesystems mounted on
  * block devices.
  */
-static void remount_ro(void)
+static void remount_ro(void (*cb_on_remount)(const struct mntent*))
 {
-    int fd, cnt = 0;
+    int fd, cnt;
+    FILE* fp;
+    struct mntent* mentry;
+    struct listnode* node;
+
+    list_declare(rw_entries);
+    list_declare(ro_entries);
+
+    sync();
+    find_rw(&rw_entries);
 
     /* Trigger the remount of the filesystems as read-only,
      * which also marks them clean.
      */
-    fd = open("/proc/sysrq-trigger", O_WRONLY);
+    fd = TEMP_FAILURE_RETRY(open("/proc/sysrq-trigger", O_WRONLY));
     if (fd < 0) {
-        return;
+        KLOG_WARNING(TAG, "Failed to open sysrq-trigger.\n");
+        /* TODO: Try to remount each rw parition manually in readonly mode.
+         * This may succeed if no process is using the partition.
+         */
+        goto out;
     }
-    write(fd, "u", 1);
+    if (TEMP_FAILURE_RETRY(write(fd, "u", 1)) != 1) {
+        close(fd);
+        KLOG_WARNING(TAG, "Failed to write to sysrq-trigger.\n");
+        /* TODO: The same. Manually remount the paritions. */
+        goto out;
+    }
     close(fd);
 
-
     /* Now poll /proc/mounts till it's done */
-    while (!remount_ro_done() && (cnt < 50)) {
-        usleep(100000);
+    cnt = 0;
+    while (cnt < READONLY_CHECK_TIMES) {
+        if ((fp = setmntent("/proc/mounts", "r")) == NULL) {
+            /* If we can't read /proc/mounts, just give up. */
+            KLOG_WARNING(TAG, "Failed to open /proc/mounts.\n");
+            goto out;
+        }
+        while ((mentry = getmntent(fp)) != NULL) {
+            if (!is_block_device(mentry->mnt_fsname) ||
+                !has_mount_option(mentry->mnt_opts, "ro")) {
+                continue;
+            }
+            mntent_list* item = find_item(&rw_entries, mentry->mnt_fsname);
+            if (item) {
+                /* |item| has now been ro remounted. */
+                list_remove(&item->list);
+                list_add_tail(&ro_entries, &item->list);
+            }
+        }
+        endmntent(fp);
+        if (list_empty(&rw_entries)) {
+            /* All rw block devices are now readonly. */
+            break;
+        }
+        TEMP_FAILURE_RETRY(
+            usleep(READONLY_CHECK_MS * 1000 / READONLY_CHECK_TIMES));
         cnt++;
     }
 
-    return;
+    list_for_each(node, &rw_entries) {
+        mntent_list* item = node_to_item(node, mntent_list, list);
+        KLOG_WARNING(TAG, "Failed to remount %s in readonly mode.\n",
+                     item->entry.mnt_fsname);
+    }
+
+    if (cb_on_remount) {
+        list_for_each(node, &ro_entries) {
+            mntent_list* item = node_to_item(node, mntent_list, list);
+            cb_on_remount(&item->entry);
+        }
+    }
+
+out:
+    free_entries(&rw_entries);
+    free_entries(&ro_entries);
 }
 
-
-int android_reboot(int cmd, int flags UNUSED, const char *arg)
+int android_reboot_with_callback(
+    int cmd, int flags __unused, const char *arg,
+    void (*cb_on_remount)(const struct mntent*))
 {
     int ret;
-
-    sync();
-    remount_ro();
-
+    remount_ro(cb_on_remount);
     switch (cmd) {
         case ANDROID_RB_RESTART:
             ret = reboot(RB_AUTOBOOT);
@@ -117,3 +236,7 @@
     return ret;
 }
 
+int android_reboot(int cmd, int flags, const char *arg)
+{
+    return android_reboot_with_callback(cmd, flags, arg, NULL);
+}
diff --git a/libcutils/fs_config.c b/libcutils/fs_config.c
index 9f8023e..cb77ee6 100644
--- a/libcutils/fs_config.c
+++ b/libcutils/fs_config.c
@@ -76,6 +76,7 @@
 
 static const struct fs_path_config android_dirs[] = {
     { 00770, AID_SYSTEM, AID_CACHE,  0, "cache" },
+    { 00500, AID_ROOT,   AID_ROOT,   0, "config" },
     { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app" },
     { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data/app-private" },
     { 00771, AID_ROOT,   AID_ROOT,   0, "data/dalvik-cache" },
@@ -88,7 +89,10 @@
     { 00775, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media" },
     { 00775, AID_MEDIA_RW, AID_MEDIA_RW, 0, "data/media/Music" },
     { 00771, AID_SYSTEM, AID_SYSTEM, 0, "data" },
+    { 00755, AID_ROOT,   AID_SYSTEM, 0, "mnt" },
+    { 00755, AID_ROOT,   AID_ROOT,   0, "root" },
     { 00750, AID_ROOT,   AID_SHELL,  0, "sbin" },
+    { 00751, AID_ROOT,   AID_SDCARD_R, 0, "storage" },
     { 00755, AID_ROOT,   AID_SHELL,  0, "system/bin" },
     { 00755, AID_ROOT,   AID_SHELL,  0, "system/vendor" },
     { 00755, AID_ROOT,   AID_SHELL,  0, "system/xbin" },
diff --git a/libcutils/socket_network_client.c b/libcutils/socket_network_client.c
index e0031ba..2610254 100644
--- a/libcutils/socket_network_client.c
+++ b/libcutils/socket_network_client.c
@@ -29,77 +29,74 @@
 
 #include <cutils/sockets.h>
 
-/* Connect to port on the IP interface. type is
- * SOCK_STREAM or SOCK_DGRAM. 
- * return is a file descriptor or -1 on error
- */
-int socket_network_client(const char *host, int port, int type)
-{
-    return socket_network_client_timeout(host, port, type, 0);
+static int toggle_O_NONBLOCK(int s) {
+    int flags = fcntl(s, F_GETFL);
+    if (flags == -1 || fcntl(s, F_SETFL, flags ^ O_NONBLOCK) == -1) {
+        close(s);
+        return -1;
+    }
+    return s;
 }
 
-/* Connect to port on the IP interface. type is SOCK_STREAM or SOCK_DGRAM.
- * timeout in seconds return is a file descriptor or -1 on error
- */
-int socket_network_client_timeout(const char *host, int port, int type, int timeout)
-{
-    struct hostent *hp;
-    struct sockaddr_in addr;
-    int s;
-    int flags = 0, error = 0, ret = 0;
-    fd_set rset, wset;
-    socklen_t len = sizeof(error);
-    struct timeval ts;
+// Connect to the given host and port.
+// 'timeout' is in seconds (0 for no timeout).
+// Returns a file descriptor or -1 on error.
+int socket_network_client_timeout(const char* host, int port, int type, int timeout) {
+    struct addrinfo hints;
+    memset(&hints, 0, sizeof(hints));
+    hints.ai_family = AF_UNSPEC;
+    hints.ai_socktype = type;
 
+    char port_str[16];
+    snprintf(port_str, sizeof(port_str), "%d", port);
+
+    struct addrinfo* addrs;
+    if (getaddrinfo(host, port_str, &hints, &addrs) != 0) {
+        return -1;
+    }
+
+    // TODO: try all the addresses if there's more than one?
+    int family = addrs[0].ai_family;
+    int protocol = addrs[0].ai_protocol;
+    socklen_t addr_len = addrs[0].ai_addrlen;
+    struct sockaddr_storage addr;
+    memcpy(&addr, addrs[0].ai_addr, addr_len);
+
+    freeaddrinfo(addrs);
+
+    // The Mac doesn't have SOCK_NONBLOCK.
+    int s = socket(family, type, protocol);
+    if (s == -1 || toggle_O_NONBLOCK(s) == -1) return -1;
+
+    int rc = connect(s, (const struct sockaddr*) &addr, addr_len);
+    if (rc == 0) {
+        return toggle_O_NONBLOCK(s);
+    } else if (rc == -1 && errno != EINPROGRESS) {
+        close(s);
+        return -1;
+    }
+
+    fd_set r_set;
+    FD_ZERO(&r_set);
+    FD_SET(s, &r_set);
+    fd_set w_set = r_set;
+
+    struct timeval ts;
     ts.tv_sec = timeout;
     ts.tv_usec = 0;
-
-    hp = gethostbyname(host);
-    if (hp == 0) return -1;
-
-    memset(&addr, 0, sizeof(addr));
-    addr.sin_family = hp->h_addrtype;
-    addr.sin_port = htons(port);
-    memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
-
-    s = socket(hp->h_addrtype, type, 0);
-    if (s < 0) return -1;
-
-    if ((flags = fcntl(s, F_GETFL, 0)) < 0) {
+    if ((rc = select(s + 1, &r_set, &w_set, NULL, (timeout != 0) ? &ts : NULL)) == -1) {
         close(s);
         return -1;
     }
-
-    if (fcntl(s, F_SETFL, flags | O_NONBLOCK) < 0) {
-        close(s);
-        return -1;
-    }
-
-    if ((ret = connect(s, (struct sockaddr *) &addr, sizeof(addr))) < 0) {
-        if (errno != EINPROGRESS) {
-            close(s);
-            return -1;
-        }
-    }
-
-    if (ret == 0)
-        goto done;
-
-    FD_ZERO(&rset);
-    FD_SET(s, &rset);
-    wset = rset;
-
-    if ((ret = select(s + 1, &rset, &wset, NULL, (timeout) ? &ts : NULL)) < 0) {
-        close(s);
-        return -1;
-    }
-    if (ret == 0) {   // we had a timeout
+    if (rc == 0) {   // we had a timeout
         errno = ETIMEDOUT;
         close(s);
         return -1;
     }
 
-    if (FD_ISSET(s, &rset) || FD_ISSET(s, &wset)) {
+    int error = 0;
+    socklen_t len = sizeof(error);
+    if (FD_ISSET(s, &r_set) || FD_ISSET(s, &w_set)) {
         if (getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
             close(s);
             return -1;
@@ -115,11 +112,9 @@
         return -1;
     }
 
-done:
-    if (fcntl(s, F_SETFL, flags) < 0) {
-        close(s);
-        return -1;
-    }
+    return toggle_O_NONBLOCK(s);
+}
 
-    return s;
+int socket_network_client(const char* host, int port, int type) {
+    return socket_network_client_timeout(host, port, type, 0);
 }
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/libnativebridge/Android.mk b/libnativebridge/Android.mk
index 83169eb..d20d44c 100644
--- a/libnativebridge/Android.mk
+++ b/libnativebridge/Android.mk
@@ -37,4 +37,22 @@
 
 include $(BUILD_HOST_SHARED_LIBRARY)
 
+# Static library for host
+# ========================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE:= libnativebridge
+
+LOCAL_SRC_FILES:= $(NATIVE_BRIDGE_COMMON_SRC_FILES)
+LOCAL_STATIC_LIBRARIES := liblog
+LOCAL_CLANG := true
+LOCAL_CPP_EXTENSION := .cc
+LOCAL_CFLAGS += -Werror -Wall
+LOCAL_CPPFLAGS := -std=gnu++11 -fvisibility=protected
+LOCAL_LDFLAGS := -ldl
+LOCAL_MULTILIB := both
+
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+
 include $(LOCAL_PATH)/tests/Android.mk
diff --git a/libnativebridge/native_bridge.cc b/libnativebridge/native_bridge.cc
index f63497b..a9671a9 100644
--- a/libnativebridge/native_bridge.cc
+++ b/libnativebridge/native_bridge.cc
@@ -109,6 +109,13 @@
   }
 }
 
+static void ReleaseAppCodeCacheDir() {
+  if (app_code_cache_dir != nullptr) {
+    delete[] app_code_cache_dir;
+    app_code_cache_dir = nullptr;
+  }
+}
+
 // We only allow simple names for the library. It is supposed to be a file in
 // /system/lib or /vendor/lib. Only allow a small range of characters, that is
 // names consisting of [a-zA-Z0-9._-] and starting with [a-zA-Z].
@@ -162,8 +169,7 @@
 static void CloseNativeBridge(bool with_error) {
   state = NativeBridgeState::kClosed;
   had_error |= with_error;
-  delete[] app_code_cache_dir;
-  app_code_cache_dir = nullptr;
+  ReleaseAppCodeCacheDir();
 }
 
 bool LoadNativeBridge(const char* nb_library_filename,
@@ -406,16 +412,16 @@
     if (stat(app_code_cache_dir, &st) == -1) {
       if (errno == ENOENT) {
         if (mkdir(app_code_cache_dir, S_IRWXU | S_IRWXG | S_IXOTH) == -1) {
-          ALOGE("Cannot create code cache directory %s: %s.", app_code_cache_dir, strerror(errno));
-          CloseNativeBridge(true);
+          ALOGW("Cannot create code cache directory %s: %s.", app_code_cache_dir, strerror(errno));
+          ReleaseAppCodeCacheDir();
         }
       } else {
-        ALOGE("Cannot stat code cache directory %s: %s.", app_code_cache_dir, strerror(errno));
-        CloseNativeBridge(true);
+        ALOGW("Cannot stat code cache directory %s: %s.", app_code_cache_dir, strerror(errno));
+        ReleaseAppCodeCacheDir();
       }
     } else if (!S_ISDIR(st.st_mode)) {
-      ALOGE("Code cache is not a directory %s.", app_code_cache_dir);
-      CloseNativeBridge(true);
+      ALOGW("Code cache is not a directory %s.", app_code_cache_dir);
+      ReleaseAppCodeCacheDir();
     }
 
     // If we're still PreInitialized (dind't fail the code cache checks) try to initialize.
@@ -424,8 +430,7 @@
         SetupEnvironment(callbacks, env, instruction_set);
         state = NativeBridgeState::kInitialized;
         // We no longer need the code cache path, release the memory.
-        delete[] app_code_cache_dir;
-        app_code_cache_dir = nullptr;
+        ReleaseAppCodeCacheDir();
       } else {
         // Unload the library.
         dlclose(native_bridge_handle);
diff --git a/libnativebridge/tests/Android.mk b/libnativebridge/tests/Android.mk
index 285e8c2..7265939 100644
--- a/libnativebridge/tests/Android.mk
+++ b/libnativebridge/tests/Android.mk
@@ -9,6 +9,7 @@
 test_src_files := \
     CodeCacheCreate_test.cpp \
     CodeCacheExists_test.cpp \
+    CodeCacheStatFail_test.cpp \
     CompleteFlow_test.cpp \
     InvalidCharsNativeBridge_test.cpp \
     NativeBridge2Signal_test.cpp \
diff --git a/libnativebridge/tests/CodeCacheStatFail_test.cpp b/libnativebridge/tests/CodeCacheStatFail_test.cpp
new file mode 100644
index 0000000..4ea519e
--- /dev/null
+++ b/libnativebridge/tests/CodeCacheStatFail_test.cpp
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "NativeBridgeTest.h"
+
+#include <errno.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+namespace android {
+
+// Tests that the bridge is initialized without errors if the code_cache is
+// existed as a file.
+TEST_F(NativeBridgeTest, CodeCacheStatFail) {
+    int fd = creat(kCodeCache, O_RDWR);
+    ASSERT_NE(-1, fd);
+    close(fd);
+
+    struct stat st;
+    ASSERT_EQ(-1, stat(kCodeCacheStatFail, &st));
+    ASSERT_EQ(ENOTDIR, errno);
+
+    // Init
+    ASSERT_TRUE(LoadNativeBridge(kNativeBridgeLibrary, nullptr));
+    ASSERT_TRUE(PreInitializeNativeBridge(kCodeCacheStatFail, "isa"));
+    ASSERT_TRUE(InitializeNativeBridge(nullptr, nullptr));
+    ASSERT_TRUE(NativeBridgeAvailable());
+    ASSERT_FALSE(NativeBridgeError());
+
+    // Clean up
+    UnloadNativeBridge();
+
+    ASSERT_FALSE(NativeBridgeError());
+    unlink(kCodeCache);
+}
+
+}  // namespace android
diff --git a/libnativebridge/tests/NativeBridgeTest.h b/libnativebridge/tests/NativeBridgeTest.h
index 6a5c126..d489420 100644
--- a/libnativebridge/tests/NativeBridgeTest.h
+++ b/libnativebridge/tests/NativeBridgeTest.h
@@ -24,6 +24,7 @@
 
 constexpr const char* kNativeBridgeLibrary = "libnativebridge-dummy.so";
 constexpr const char* kCodeCache = "./code_cache";
+constexpr const char* kCodeCacheStatFail = "./code_cache/temp";
 
 namespace android {
 
diff --git a/libsparse/sparse_crc32.h b/libsparse/sparse_crc32.h
index cad8a86..50cd9e9 100644
--- a/libsparse/sparse_crc32.h
+++ b/libsparse/sparse_crc32.h
@@ -14,7 +14,19 @@
  * limitations under the License.
  */
 
+#ifndef _LIBSPARSE_SPARSE_CRC32_H_
+#define _LIBSPARSE_SPARSE_CRC32_H_
+
 #include <stdint.h>
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 uint32_t sparse_crc32(uint32_t crc, const void *buf, size_t size);
 
+#ifdef __cplusplus
+}
+#endif
+
+#endif
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/libutils/Looper.cpp b/libutils/Looper.cpp
index 9a2dd6c..20c1e92 100644
--- a/libutils/Looper.cpp
+++ b/libutils/Looper.cpp
@@ -17,9 +17,11 @@
 #include <utils/Looper.h>
 #include <utils/Timers.h>
 
-#include <unistd.h>
+#include <errno.h>
 #include <fcntl.h>
 #include <limits.h>
+#include <string.h>
+#include <unistd.h>
 
 
 namespace android {
@@ -71,32 +73,32 @@
         mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
     int wakeFds[2];
     int result = pipe(wakeFds);
-    LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe.  errno=%d", errno);
+    LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe: %s", strerror(errno));
 
     mWakeReadPipeFd = wakeFds[0];
     mWakeWritePipeFd = wakeFds[1];
 
     result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK);
-    LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking.  errno=%d",
-            errno);
+    LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking: %s",
+            strerror(errno));
 
     result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK);
-    LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking.  errno=%d",
-            errno);
+    LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking: %s",
+            strerror(errno));
 
     mIdling = false;
 
     // Allocate the epoll instance and register the wake pipe.
     mEpollFd = epoll_create(EPOLL_SIZE_HINT);
-    LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance.  errno=%d", errno);
+    LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance: %s", strerror(errno));
 
     struct epoll_event eventItem;
     memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
     eventItem.events = EPOLLIN;
     eventItem.data.fd = mWakeReadPipeFd;
     result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, & eventItem);
-    LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance.  errno=%d",
-            errno);
+    LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance: %s",
+            strerror(errno));
 }
 
 Looper::~Looper() {
@@ -233,7 +235,7 @@
         if (errno == EINTR) {
             goto Done;
         }
-        ALOGW("Poll failed with an unexpected error, errno=%d", errno);
+        ALOGW("Poll failed with an unexpected error: %s", strerror(errno));
         result = POLL_ERROR;
         goto Done;
     }
@@ -377,7 +379,7 @@
 
     if (nWrite != 1) {
         if (errno != EAGAIN) {
-            ALOGW("Could not write wake signal, errno=%d", errno);
+            ALOGW("Could not write wake signal: %s", strerror(errno));
         }
     }
 }
@@ -447,14 +449,14 @@
         if (requestIndex < 0) {
             int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
             if (epollResult < 0) {
-                ALOGE("Error adding epoll events for fd %d, errno=%d", fd, errno);
+                ALOGE("Error adding epoll events for fd %d: %s", fd, strerror(errno));
                 return -1;
             }
             mRequests.add(fd, request);
         } else {
             int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem);
             if (epollResult < 0) {
-                ALOGE("Error modifying epoll events for fd %d, errno=%d", fd, errno);
+                ALOGE("Error modifying epoll events for fd %d: %s", fd, strerror(errno));
                 return -1;
             }
             mRequests.replaceValueAt(requestIndex, request);
@@ -477,7 +479,7 @@
 
         int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_DEL, fd, NULL);
         if (epollResult < 0) {
-            ALOGE("Error removing epoll events for fd %d, errno=%d", fd, errno);
+            ALOGE("Error removing epoll events for fd %d: %s", fd, strerror(errno));
             return -1;
         }
 
diff --git a/libutils/ProcessCallStack.cpp b/libutils/ProcessCallStack.cpp
index db07e56..011c302 100644
--- a/libutils/ProcessCallStack.cpp
+++ b/libutils/ProcessCallStack.cpp
@@ -17,9 +17,10 @@
 #define LOG_TAG "ProcessCallStack"
 // #define LOG_NDEBUG 0
 
-#include <string.h>
-#include <stdio.h>
 #include <dirent.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
 
 #include <utils/Log.h>
 #include <utils/Errors.h>
@@ -135,8 +136,8 @@
 
     dp = opendir(PATH_SELF_TASK);
     if (dp == NULL) {
-        ALOGE("%s: Failed to update the process's call stacks (errno = %d, '%s')",
-              __FUNCTION__, errno, strerror(errno));
+        ALOGE("%s: Failed to update the process's call stacks: %s",
+              __FUNCTION__, strerror(errno));
         return;
     }
 
@@ -172,8 +173,8 @@
 
         ssize_t idx = mThreadMap.add(tid, ThreadInfo());
         if (idx < 0) { // returns negative error value on error
-            ALOGE("%s: Failed to add new ThreadInfo (errno = %zd, '%s')",
-                  __FUNCTION__, idx, strerror(-idx));
+            ALOGE("%s: Failed to add new ThreadInfo: %s",
+                  __FUNCTION__, strerror(-idx));
             continue;
         }
 
@@ -195,8 +196,8 @@
               __FUNCTION__, tid, threadInfo.callStack.size());
     }
     if (code != 0) { // returns positive error value on error
-        ALOGE("%s: Failed to readdir from %s (errno = %d, '%s')",
-              __FUNCTION__, PATH_SELF_TASK, -code, strerror(code));
+        ALOGE("%s: Failed to readdir from %s: %s",
+              __FUNCTION__, PATH_SELF_TASK, strerror(code));
     }
 #endif
 
diff --git a/libutils/SystemClock.cpp b/libutils/SystemClock.cpp
index ac3dd98..64204a8 100644
--- a/libutils/SystemClock.cpp
+++ b/libutils/SystemClock.cpp
@@ -29,7 +29,6 @@
 #include <sys/time.h>
 #include <limits.h>
 #include <fcntl.h>
-#include <errno.h>
 #include <string.h>
 
 #include <utils/SystemClock.h>
diff --git a/libutils/Threads.cpp b/libutils/Threads.cpp
index 1e014c6..c3666e4 100644
--- a/libutils/Threads.cpp
+++ b/libutils/Threads.cpp
@@ -22,6 +22,7 @@
 #include <memory.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include <unistd.h>
 
 #if !defined(_WIN32)
@@ -160,9 +161,9 @@
                     (android_pthread_entry)entryFunction, userData);
     pthread_attr_destroy(&attr);
     if (result != 0) {
-        ALOGE("androidCreateRawThreadEtc failed (entry=%p, res=%d, errno=%d)\n"
+        ALOGE("androidCreateRawThreadEtc failed (entry=%p, res=%d, %s)\n"
              "(android threadPriority=%d)",
-            entryFunction, result, errno, threadPriority);
+            entryFunction, result, strerror(errno), threadPriority);
         return 0;
     }
 
diff --git a/libutils/Tokenizer.cpp b/libutils/Tokenizer.cpp
index 610002f..2d0e83d 100644
--- a/libutils/Tokenizer.cpp
+++ b/libutils/Tokenizer.cpp
@@ -56,12 +56,12 @@
     int fd = ::open(filename.string(), O_RDONLY);
     if (fd < 0) {
         result = -errno;
-        ALOGE("Error opening file '%s', %s.", filename.string(), strerror(errno));
+        ALOGE("Error opening file '%s': %s", filename.string(), strerror(errno));
     } else {
         struct stat stat;
         if (fstat(fd, &stat)) {
             result = -errno;
-            ALOGE("Error getting size of file '%s', %s.", filename.string(), strerror(errno));
+            ALOGE("Error getting size of file '%s': %s", filename.string(), strerror(errno));
         } else {
             size_t length = size_t(stat.st_size);
 
@@ -83,7 +83,7 @@
                 ssize_t nrd = read(fd, buffer, length);
                 if (nrd < 0) {
                     result = -errno;
-                    ALOGE("Error reading file '%s', %s.", filename.string(), strerror(errno));
+                    ALOGE("Error reading file '%s': %s", filename.string(), strerror(errno));
                     delete[] buffer;
                     buffer = NULL;
                 } else {
diff --git a/libutils/misc.cpp b/libutils/misc.cpp
index ed1ba23..216dc14 100644
--- a/libutils/misc.cpp
+++ b/libutils/misc.cpp
@@ -24,7 +24,6 @@
 
 #include <sys/stat.h>
 #include <string.h>
-#include <errno.h>
 #include <stdio.h>
 
 #if !defined(_WIN32)
diff --git a/libziparchive/Android.mk b/libziparchive/Android.mk
index a3087ee..559c48b 100644
--- a/libziparchive/Android.mk
+++ b/libziparchive/Android.mk
@@ -42,8 +42,8 @@
 include $(CLEAR_VARS)
 LOCAL_CPP_EXTENSION := .cc
 LOCAL_SRC_FILES := ${source_files}
-LOCAL_STATIC_LIBRARIES := libz libutils
-LOCAL_SHARED_LIBRARIES := liblog libbase
+LOCAL_STATIC_LIBRARIES := libutils
+LOCAL_SHARED_LIBRARIES := libz-host liblog libbase
 LOCAL_MODULE:= libziparchive-host
 LOCAL_CFLAGS := -Werror
 LOCAL_MULTILIB := both
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..d3a73a1 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -43,10 +43,12 @@
     if (!isdigit(*s++)) {
         return NULL;
     }
+    static const size_t max_prio_len = 4;
+    size_t len = 0;
     char c;
-    while ((c = *s++)) {
-        if (!isdigit(c) && (c == '>')) {
-            return s;
+    while (((c = *s++)) && (++len <= max_prio_len)) {
+        if (!isdigit(c)) {
+            return (c == '>') ? s : NULL;
         }
     }
     return NULL;
@@ -65,17 +67,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]\{1,4\}>\)\([[] *[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
@@ -167,7 +167,7 @@
             break;
         }
     }
-    /* NOTREACHED */
+    // NOTREACHED
 }
 
 log_time LogKlog::correction = log_time(CLOCK_REALTIME) - log_time(CLOCK_MONOTONIC);
@@ -467,7 +467,7 @@
             if (strncmp(bt, cp, size)) {
                 // <PRI>[<TIME>] <tag>_host '<tag>.<num>' : message
                 if (!strncmp(bt + size - 5, "_host", 5)
-                 && !strncmp(bt, cp, size - 5)) {
+                        && !strncmp(bt, cp, size - 5)) {
                     const char *b = cp;
                     cp += size - 5;
                     if (*cp == '.') {
@@ -537,20 +537,35 @@
         }
         size = etag - tag;
         if ((size <= 1)
-         || ((size == 2) && (isdigit(tag[0]) || isdigit(tag[1])))
-         || ((size == 3) && !strncmp(tag, "CPU", 3))
-         || ((size == 7) && !strncmp(tag, "WARNING", 7))
-         || ((size == 5) && !strncmp(tag, "ERROR", 5))
-         || ((size == 4) && !strncmp(tag, "INFO", 4))) {
+            // register names like x9
+                || ((size == 2) && (isdigit(tag[0]) || isdigit(tag[1])))
+            // register names like x18 but not driver names like en0
+                || ((size == 3) && (isdigit(tag[1]) && isdigit(tag[2])))
+            // blacklist
+                || ((size == 3) && !strncmp(tag, "CPU", 3))
+                || ((size == 7) && !strncmp(tag, "WARNING", 7))
+                || ((size == 5) && !strncmp(tag, "ERROR", 5))
+                || ((size == 4) && !strncmp(tag, "INFO", 4))) {
             buf = start;
             etag = tag = "";
         }
     }
     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 +587,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/logwrapper/logwrap.c b/logwrapper/logwrap.c
index 3a6276e..777dafe 100644
--- a/logwrapper/logwrap.c
+++ b/logwrapper/logwrap.c
@@ -355,7 +355,8 @@
         }
 
         if (poll_fds[0].revents & POLLIN) {
-            sz = read(parent_read, &buffer[b], sizeof(buffer) - 1 - b);
+            sz = TEMP_FAILURE_RETRY(
+                read(parent_read, &buffer[b], sizeof(buffer) - 1 - b));
 
             sz += b;
             // Log one line at a time
@@ -490,7 +491,7 @@
     }
 
     /* Use ptty instead of socketpair so that STDOUT is not buffered */
-    parent_ptty = open("/dev/ptmx", O_RDWR);
+    parent_ptty = TEMP_FAILURE_RETRY(open("/dev/ptmx", O_RDWR));
     if (parent_ptty < 0) {
         ERROR("Cannot create parent ptty\n");
         rc = -1;
@@ -505,7 +506,7 @@
         goto err_ptty;
     }
 
-    child_ptty = open(child_devname, O_RDWR);
+    child_ptty = TEMP_FAILURE_RETRY(open(child_devname, O_RDWR));
     if (child_ptty < 0) {
         ERROR("Cannot open child_ptty\n");
         rc = -1;
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 7ab76b8..d6dad2d 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -26,10 +26,15 @@
 #
 # create some directories (some are mount points)
 LOCAL_POST_INSTALL_CMD := mkdir -p $(addprefix $(TARGET_ROOT_OUT)/, \
-    sbin dev proc sys system data oem)
+    sbin dev proc sys system data oem acct cache config storage mnt root)
 
 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..b7a593f 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -17,11 +17,11 @@
     # Set the security context of /adb_keys if present.
     restorecon /adb_keys
 
-    start ueventd
-
-    # create mountpoints
+    # Shouldn't be necessary, but sdcard won't start without it. http://b/22568628.
     mkdir /mnt 0775 root system
 
+    start ueventd
+
 on init
     sysclktz 0
 
@@ -32,8 +32,7 @@
     # Link /vendor to /system/vendor for devices without a vendor partition.
     symlink /system/vendor /vendor
 
-    # Create cgroup mount point for cpu accounting
-    mkdir /acct
+    # Mount cgroup mount point for cpu accounting
     mount cgroup none /acct cpuacct
     mkdir /acct/uid
 
@@ -50,11 +49,6 @@
     chown root system /sys/fs/cgroup/memory/sw/tasks
     chmod 0660 /sys/fs/cgroup/memory/sw/tasks
 
-    mkdir /system
-    mkdir /data 0771 system system
-    mkdir /cache 0770 system cache
-    mkdir /config 0500 root root
-
     # See storage config details at http://source.android.com/tech/storage/
     mkdir /mnt/shell 0700 shell shell
     mkdir /mnt/media_rw 0700 media_rw media_rw
@@ -194,9 +188,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 +634,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