Merge "Lose ls to toybox."
diff --git a/adb/Android.mk b/adb/Android.mk
index f9d4441..bbdc2ea 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -5,12 +5,6 @@
 
 LOCAL_PATH:= $(call my-dir)
 
-ifeq ($(HOST_OS),windows)
-    adb_host_clang := false  # libc++ for mingw not ready yet.
-else
-    adb_host_clang := true
-endif
-
 adb_host_sanitize :=
 adb_target_sanitize :=
 
@@ -29,8 +23,6 @@
 ADB_COMMON_windows_CFLAGS := \
     -DUNICODE=1 -D_UNICODE=1 \
 
-ADB_COMMON_CFLAGS += $(ADB_COMMON_$(HOST_OS)_CFLAGS)
-
 # libadb
 # =========================================================
 
@@ -63,7 +55,8 @@
 LIBADB_linux_CFLAGS := \
     -std=c++14 \
 
-LIBADB_CFLAGS += $(LIBADB_$(HOST_OS)_CFLAGS)
+LIBADB_windows_CFLAGS := \
+    $(ADB_COMMON_windows_CFLAGS) \
 
 LIBADB_darwin_SRC_FILES := \
     fdevent.cpp \
@@ -79,6 +72,12 @@
     sysdeps_win32.cpp \
     usb_windows.cpp \
 
+LIBADB_TEST_linux_SRCS := \
+    fdevent_test.cpp \
+
+LIBADB_TEST_darwin_SRCS := \
+    fdevent_test.cpp \
+
 include $(CLEAR_VARS)
 LOCAL_CLANG := true
 LOCAL_MODULE := libadbd
@@ -91,7 +90,6 @@
     usb_linux_client.cpp \
 
 LOCAL_SANITIZE := $(adb_target_sanitize)
-LOCAL_SHARED_LIBRARIES := libbase
 
 # Even though we're building a static library (and thus there's no link step for
 # this to take effect), this adds the includes to our path.
@@ -100,24 +98,27 @@
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
-LOCAL_CLANG := $(adb_host_clang)
 LOCAL_MODULE := libadb
+LOCAL_MODULE_HOST_OS := darwin linux windows
 LOCAL_CFLAGS := $(LIBADB_CFLAGS) -DADB_HOST=1
+LOCAL_CFLAGS_windows := $(LIBADB_windows_CFLAGS)
+LOCAL_CFLAGS_linux := $(LIBADB_linux_CFLAGS)
 LOCAL_SRC_FILES := \
     $(LIBADB_SRC_FILES) \
-    $(LIBADB_$(HOST_OS)_SRC_FILES) \
     adb_auth_host.cpp \
 
+LOCAL_SRC_FILES_darwin := $(LIBADB_darwin_SRC_FILES)
+LOCAL_SRC_FILES_linux := $(LIBADB_linux_SRC_FILES)
+LOCAL_SRC_FILES_windows := $(LIBADB_windows_SRC_FILES)
+
 LOCAL_SANITIZE := $(adb_host_sanitize)
-LOCAL_SHARED_LIBRARIES := libbase
 
 # Even though we're building a static library (and thus there's no link step for
 # this to take effect), this adds the includes to our path.
 LOCAL_STATIC_LIBRARIES := libcrypto_static libbase
 
-ifeq ($(HOST_OS),windows)
-    LOCAL_C_INCLUDES += development/host/windows/usb/api/
-else
+LOCAL_C_INCLUDES_windows := development/host/windows/usb/api/
+ifneq ($(HOST_OS),windows)
     LOCAL_MULTILIB := 64
 endif
 
@@ -127,7 +128,10 @@
 LOCAL_CLANG := true
 LOCAL_MODULE := adbd_test
 LOCAL_CFLAGS := -DADB_HOST=0 $(LIBADB_CFLAGS)
-LOCAL_SRC_FILES := $(LIBADB_TEST_SRCS)
+LOCAL_SRC_FILES := \
+    $(LIBADB_TEST_SRCS) \
+    $(LIBADB_TEST_linux_SRCS) \
+
 LOCAL_SANITIZE := $(adb_target_sanitize)
 LOCAL_STATIC_LIBRARIES := libadbd
 LOCAL_SHARED_LIBRARIES := liblog libbase libcutils
@@ -137,10 +141,13 @@
 # =========================================================
 
 include $(CLEAR_VARS)
-LOCAL_CLANG := $(adb_host_clang)
 LOCAL_MODULE := adb_test
 LOCAL_CFLAGS := -DADB_HOST=1 $(LIBADB_CFLAGS)
+LOCAL_CFLAGS_windows := $(LIBADB_windows_CFLAGS)
+LOCAL_CFLAGS_linux := $(LIBADB_linux_CFLAGS)
 LOCAL_SRC_FILES := $(LIBADB_TEST_SRCS) services.cpp
+LOCAL_SRC_FILES_linux := $(LIBADB_TEST_linux_SRCS)
+LOCAL_SRC_FILES_darwin := $(LIBADB_TEST_darwin_SRCS)
 LOCAL_SANITIZE := $(adb_host_sanitize)
 LOCAL_SHARED_LIBRARIES := liblog libbase
 LOCAL_STATIC_LIBRARIES := \
@@ -148,18 +155,10 @@
     libcrypto_static \
     libcutils \
 
-ifeq ($(HOST_OS),linux)
-    LOCAL_LDLIBS += -lrt -ldl -lpthread
-endif
-
-ifeq ($(HOST_OS),darwin)
-    LOCAL_LDLIBS += -framework CoreFoundation -framework IOKit
-endif
-
-ifeq ($(HOST_OS),windows)
-    LOCAL_LDLIBS += -lws2_32 -luserenv
-    LOCAL_STATIC_LIBRARIES += AdbWinApi
-endif
+LOCAL_LDLIBS_linux := -lrt -ldl -lpthread
+LOCAL_LDLIBS_darwin := -framework CoreFoundation -framework IOKit
+LOCAL_LDLIBS_windows := -lws2_32 -luserenv
+LOCAL_STATIC_LIBRARIES_windows := AdbWinApi
 
 include $(BUILD_HOST_NATIVE_TEST)
 
@@ -168,9 +167,10 @@
 
 ifeq ($(HOST_OS),linux)
 include $(CLEAR_VARS)
-LOCAL_CLANG := $(adb_host_clang)
 LOCAL_MODULE := adb_device_tracker_test
 LOCAL_CFLAGS := -DADB_HOST=1 $(LIBADB_CFLAGS)
+LOCAL_CFLAGS_windows := $(LIBADB_windows_CFLAGS)
+LOCAL_CFLAGS_linux := $(LIBADB_linux_CFLAGS)
 LOCAL_SRC_FILES := test_track_devices.cpp
 LOCAL_SANITIZE := $(adb_host_sanitize)
 LOCAL_SHARED_LIBRARIES := liblog libbase
@@ -183,24 +183,16 @@
 # =========================================================
 include $(CLEAR_VARS)
 
-ifeq ($(HOST_OS),linux)
-    LOCAL_LDLIBS += -lrt -ldl -lpthread
-    LOCAL_CFLAGS += -DWORKAROUND_BUG6558362
-endif
+LOCAL_LDLIBS_linux := -lrt -ldl -lpthread
 
-ifeq ($(HOST_OS),darwin)
-    LOCAL_LDLIBS += -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
-    LOCAL_CFLAGS += -Wno-sizeof-pointer-memaccess -Wno-unused-parameter
-endif
+LOCAL_LDLIBS_darwin := -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
+LOCAL_CFLAGS_darwin := -Wno-sizeof-pointer-memaccess -Wno-unused-parameter
 
-ifeq ($(HOST_OS),windows)
-    # Use wmain instead of main
-    LOCAL_LDFLAGS += -municode
-    LOCAL_LDLIBS += -lws2_32 -lgdi32
-    EXTRA_STATIC_LIBS := AdbWinApi
-endif
-
-LOCAL_CLANG := $(adb_host_clang)
+# Use wmain instead of main
+LOCAL_LDFLAGS_windows := -municode
+LOCAL_LDLIBS_windows := -lws2_32 -lgdi32
+LOCAL_STATIC_LIBRARIES_windows := AdbWinApi
+LOCAL_REQUIRED_MODULES_windows := AdbWinApi AdbWinUsbApi
 
 LOCAL_SRC_FILES := \
     client/main.cpp \
@@ -215,8 +207,12 @@
     -D_GNU_SOURCE \
     -DADB_HOST=1 \
 
+LOCAL_CFLAGS_windows := \
+    $(ADB_COMMON_windows_CFLAGS)
+
 LOCAL_MODULE := adb
 LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE_HOST_OS := darwin linux windows
 
 LOCAL_SANITIZE := $(adb_host_sanitize)
 LOCAL_STATIC_LIBRARIES := \
@@ -225,12 +221,8 @@
     libcrypto_static \
     libcutils \
     liblog \
-    $(EXTRA_STATIC_LIBS) \
 
-# libc++ not available on windows yet
-ifneq ($(HOST_OS),windows)
-    LOCAL_CXX_STL := libc++_static
-endif
+LOCAL_CXX_STL := libc++_static
 
 # Don't add anything here, we don't want additional shared dependencies
 # on the host adb tool, and shared libraries that link against libc++
@@ -241,12 +233,6 @@
 
 $(call dist-for-goals,dist_files sdk,$(LOCAL_BUILT_MODULE))
 
-ifeq ($(HOST_OS),windows)
-$(LOCAL_INSTALLED_MODULE): \
-    $(HOST_OUT_EXECUTABLES)/AdbWinApi.dll \
-    $(HOST_OUT_EXECUTABLES)/AdbWinUsbApi.dll
-endif
-
 
 # adbd device daemon
 # =========================================================
diff --git a/adb/adb.cpp b/adb/adb.cpp
index eb01da8..14df8a1 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -53,8 +53,6 @@
 #include <sys/mount.h>
 #endif
 
-ADB_MUTEX_DEFINE(D_lock);
-
 #if !ADB_HOST
 const char* adb_device_banner = "device";
 static android::base::LogdLogger gLogdLogger;
@@ -184,7 +182,7 @@
     for (const auto& elem : elements) {
         const auto& flag = trace_flags.find(elem);
         if (flag == trace_flags.end()) {
-            D("Unknown trace flag: %s\n", flag->first.c_str());
+            D("Unknown trace flag: %s", flag->first.c_str());
             continue;
         }
 
@@ -234,13 +232,13 @@
 
 void handle_online(atransport *t)
 {
-    D("adb: online\n");
+    D("adb: online");
     t->online = 1;
 }
 
 void handle_offline(atransport *t)
 {
-    D("adb: offline\n");
+    D("adb: offline");
     //Close the associated usb
     t->online = 0;
 
@@ -294,7 +292,7 @@
 
 static void send_ready(unsigned local, unsigned remote, atransport *t)
 {
-    D("Calling send_ready \n");
+    D("Calling send_ready");
     apacket *p = get_apacket();
     p->msg.command = A_OKAY;
     p->msg.arg0 = local;
@@ -304,7 +302,7 @@
 
 static void send_close(unsigned local, unsigned remote, atransport *t)
 {
-    D("Calling send_close \n");
+    D("Calling send_close");
     apacket *p = get_apacket();
     p->msg.command = A_CLSE;
     p->msg.arg0 = local;
@@ -339,7 +337,7 @@
 }
 
 void send_connect(atransport* t) {
-    D("Calling send_connect \n");
+    D("Calling send_connect");
     apacket* cp = get_apacket();
     cp->msg.command = A_CNXN;
     cp->msg.arg0 = t->get_protocol_version();
@@ -369,7 +367,7 @@
 }
 
 void parse_banner(const std::string& banner, atransport* t) {
-    D("parse_banner: %s\n", banner.c_str());
+    D("parse_banner: %s", banner.c_str());
 
     // The format is something like:
     // "device::ro.product.name=x;ro.product.model=y;ro.product.device=z;".
@@ -402,23 +400,23 @@
 
     const std::string& type = pieces[0];
     if (type == "bootloader") {
-        D("setting connection_state to kCsBootloader\n");
+        D("setting connection_state to kCsBootloader");
         t->connection_state = kCsBootloader;
         update_transports();
     } else if (type == "device") {
-        D("setting connection_state to kCsDevice\n");
+        D("setting connection_state to kCsDevice");
         t->connection_state = kCsDevice;
         update_transports();
     } else if (type == "recovery") {
-        D("setting connection_state to kCsRecovery\n");
+        D("setting connection_state to kCsRecovery");
         t->connection_state = kCsRecovery;
         update_transports();
     } else if (type == "sideload") {
-        D("setting connection_state to kCsSideload\n");
+        D("setting connection_state to kCsSideload");
         t->connection_state = kCsSideload;
         update_transports();
     } else {
-        D("setting connection_state to kCsHost\n");
+        D("setting connection_state to kCsHost");
         t->connection_state = kCsHost;
     }
 }
@@ -450,7 +448,7 @@
 {
     asocket *s;
 
-    D("handle_packet() %c%c%c%c\n", ((char*) (&(p->msg.command)))[0],
+    D("handle_packet() %c%c%c%c", ((char*) (&(p->msg.command)))[0],
             ((char*) (&(p->msg.command)))[1],
             ((char*) (&(p->msg.command)))[2],
             ((char*) (&(p->msg.command)))[3]);
@@ -526,7 +524,7 @@
                     /* Other READY messages must use the same local-id */
                     s->ready(s);
                 } else {
-                    D("Invalid A_OKAY(%d,%d), expected A_OKAY(%d,%d) on transport %s\n",
+                    D("Invalid A_OKAY(%d,%d), expected A_OKAY(%d,%d) on transport %s",
                       p->msg.arg0, p->msg.arg1, s->peer->id, p->msg.arg1, t->serial);
                 }
             }
@@ -547,7 +545,7 @@
                  * socket has a peer on the same transport.
                  */
                 if (p->msg.arg0 == 0 && s->peer && s->peer->transport != t) {
-                    D("Invalid A_CLSE(0, %u) from transport %s, expected transport %s\n",
+                    D("Invalid A_CLSE(0, %u) from transport %s, expected transport %s",
                       p->msg.arg1, t->serial, s->peer->transport->serial);
                 } else {
                     s->close(s);
@@ -563,7 +561,7 @@
                 p->len = p->msg.data_length;
 
                 if(s->enqueue(s, p) == 0) {
-                    D("Enqueue the socket\n");
+                    D("Enqueue the socket");
                     send_ready(s->id, rid, t);
                 }
                 return;
@@ -1125,9 +1123,9 @@
     if (!strncmp(service, "devices", 7)) {
         bool long_listing = (strcmp(service+7, "-l") == 0);
         if (long_listing || service[7] == 0) {
-            D("Getting device list...\n");
+            D("Getting device list...");
             std::string device_list = list_transports(long_listing);
-            D("Sending device list...\n");
+            D("Sending device list...");
             return SendOkay(reply_fd, device_list);
         }
         return 1;
diff --git a/adb/adb_auth.cpp b/adb/adb_auth.cpp
index 8a6b156..2364f7b 100644
--- a/adb/adb_auth.cpp
+++ b/adb/adb_auth.cpp
@@ -32,13 +32,13 @@
 
 void send_auth_request(atransport *t)
 {
-    D("Calling send_auth_request\n");
+    D("Calling send_auth_request");
     apacket *p;
     int ret;
 
     ret = adb_auth_generate_token(t->token, sizeof(t->token));
     if (ret != sizeof(t->token)) {
-        D("Error generating token ret=%d\n", ret);
+        D("Error generating token ret=%d", ret);
         return;
     }
 
@@ -52,13 +52,13 @@
 
 void send_auth_response(uint8_t *token, size_t token_size, atransport *t)
 {
-    D("Calling send_auth_response\n");
+    D("Calling send_auth_response");
     apacket *p = get_apacket();
     int ret;
 
     ret = adb_auth_sign(t->key, token, token_size, p->data);
     if (!ret) {
-        D("Error signing the token\n");
+        D("Error signing the token");
         put_apacket(p);
         return;
     }
@@ -71,13 +71,13 @@
 
 void send_auth_publickey(atransport *t)
 {
-    D("Calling send_auth_publickey\n");
+    D("Calling send_auth_publickey");
     apacket *p = get_apacket();
     int ret;
 
     ret = adb_auth_get_userkey(p->data, MAX_PAYLOAD_V1);
     if (!ret) {
-        D("Failed to get user public key\n");
+        D("Failed to get user public key");
         put_apacket(p);
         return;
     }
diff --git a/adb/adb_auth_client.cpp b/adb/adb_auth_client.cpp
index c3af024..cedc847 100644
--- a/adb/adb_auth_client.cpp
+++ b/adb/adb_auth_client.cpp
@@ -60,7 +60,7 @@
 
     f = fopen(file, "re");
     if (!f) {
-        D("Can't open '%s'\n", file);
+        D("Can't open '%s'", file);
         return;
     }
 
@@ -69,7 +69,7 @@
         auto key = reinterpret_cast<adb_public_key*>(
             calloc(1, sizeof(adb_public_key) + 4));
         if (key == nullptr) {
-            D("Can't malloc key\n");
+            D("Can't malloc key");
             break;
         }
 
@@ -79,13 +79,13 @@
 
         ret = __b64_pton(buf, (u_char *)&key->key, sizeof(key->key) + 4);
         if (ret != sizeof(key->key)) {
-            D("%s: Invalid base64 data ret=%d\n", file, ret);
+            D("%s: Invalid base64 data ret=%d", file, ret);
             free(key);
             continue;
         }
 
         if (key->key.len != RSANUMWORDS) {
-            D("%s: Invalid key len %d\n", file, key->key.len);
+            D("%s: Invalid key len %d", file, key->key.len);
             free(key);
             continue;
         }
@@ -117,7 +117,7 @@
 
     while ((path = *paths++)) {
         if (!stat(path, &buf)) {
-            D("Loading keys from '%s'\n", path);
+            D("Loading keys from '%s'", path);
             read_keys(path, list);
         }
     }
@@ -163,7 +163,7 @@
 
 static void usb_disconnected(void* unused, atransport* t)
 {
-    D("USB disconnect\n");
+    D("USB disconnect");
     usb_transport = NULL;
     needs_retry = false;
 }
@@ -176,7 +176,7 @@
     if (events & FDE_READ) {
         ret = unix_read(fd, response, sizeof(response));
         if (ret <= 0) {
-            D("Framework disconnect\n");
+            D("Framework disconnect");
             if (usb_transport)
                 fdevent_remove(&usb_transport->auth_fde);
             framework_fd = -1;
@@ -199,26 +199,26 @@
     }
 
     if (framework_fd < 0) {
-        D("Client not connected\n");
+        D("Client not connected");
         needs_retry = true;
         return;
     }
 
     if (key[len - 1] != '\0') {
-        D("Key must be a null-terminated string\n");
+        D("Key must be a null-terminated string");
         return;
     }
 
     ret = snprintf(msg, sizeof(msg), "PK%s", key);
     if (ret >= (signed)sizeof(msg)) {
-        D("Key too long. ret=%d\n", ret);
+        D("Key too long. ret=%d", ret);
         return;
     }
-    D("Sending '%s'\n", msg);
+    D("Sending '%s'", msg);
 
     ret = unix_write(framework_fd, msg, ret);
     if (ret < 0) {
-        D("Failed to write PK, errno=%d\n", errno);
+        D("Failed to write PK, errno=%d", errno);
         return;
     }
 
@@ -236,7 +236,7 @@
 
     s = adb_socket_accept(fd, &addr, &alen);
     if (s < 0) {
-        D("Failed to accept: errno=%d\n", errno);
+        D("Failed to accept: errno=%d", errno);
         return;
     }
 
@@ -251,7 +251,7 @@
 void adbd_cloexec_auth_socket() {
     int fd = android_get_control_socket("adbd");
     if (fd == -1) {
-        D("Failed to get adbd socket\n");
+        D("Failed to get adbd socket");
         return;
     }
     fcntl(fd, F_SETFD, FD_CLOEXEC);
@@ -260,12 +260,12 @@
 void adbd_auth_init(void) {
     int fd = android_get_control_socket("adbd");
     if (fd == -1) {
-        D("Failed to get adbd socket\n");
+        D("Failed to get adbd socket");
         return;
     }
 
     if (listen(fd, 4) == -1) {
-        D("Failed to listen on '%d'\n", fd);
+        D("Failed to listen on '%d'", fd);
         return;
     }
 
diff --git a/adb/adb_auth_host.cpp b/adb/adb_auth_host.cpp
index e7f82a9..749fec2 100644
--- a/adb/adb_auth_host.cpp
+++ b/adb/adb_auth_host.cpp
@@ -164,26 +164,26 @@
 
     if (snprintf(path, sizeof(path), "%s.pub", private_key_path) >=
         (int)sizeof(path)) {
-        D("Path too long while writing public key\n");
+        D("Path too long while writing public key");
         return 0;
     }
 
     if (!RSA_to_RSAPublicKey(private_key, &pkey)) {
-        D("Failed to convert to publickey\n");
+        D("Failed to convert to publickey");
         return 0;
     }
 
     outfile = fopen(path, "w");
     if (!outfile) {
-        D("Failed to open '%s'\n", path);
+        D("Failed to open '%s'", path);
         return 0;
     }
 
-    D("Writing public key to '%s'\n", path);
+    D("Writing public key to '%s'", path);
 
 #if defined(OPENSSL_IS_BORINGSSL)
     if (!EVP_EncodedLength(&encoded_length, sizeof(pkey))) {
-        D("Public key too large to base64 encode\n");
+        D("Public key too large to base64 encode");
         goto out;
     }
 #else
@@ -194,7 +194,7 @@
 
     encoded = new uint8_t[encoded_length];
     if (encoded == nullptr) {
-        D("Allocation failure\n");
+        D("Allocation failure");
         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\n");
+        D("Write error while writing public key");
         goto out;
     }
 
@@ -226,10 +226,10 @@
     FILE *f = NULL;
     int ret = 0;
 
-    D("generate_key '%s'\n", file);
+    D("generate_key '%s'", file);
 
     if (!pkey || !exponent || !rsa) {
-        D("Failed to allocate key\n");
+        D("Failed to allocate key");
         goto out;
     }
 
@@ -241,7 +241,7 @@
 
     f = fopen(file, "w");
     if (!f) {
-        D("Failed to open '%s'\n", file);
+        D("Failed to open '%s'", file);
         umask(old_mask);
         goto out;
     }
@@ -249,12 +249,12 @@
     umask(old_mask);
 
     if (!PEM_write_PrivateKey(f, pkey, NULL, NULL, 0, NULL, NULL)) {
-        D("Failed to write key\n");
+        D("Failed to write key");
         goto out;
     }
 
     if (!write_public_keyfile(rsa, file)) {
-        D("Failed to write public key\n");
+        D("Failed to write public key");
         goto out;
     }
 
@@ -271,11 +271,11 @@
 
 static int read_key(const char *file, struct listnode *list)
 {
-    D("read_key '%s'\n", file);
+    D("read_key '%s'", file);
 
     FILE* fp = fopen(file, "r");
     if (!fp) {
-        D("Failed to open '%s': %s\n", file, strerror(errno));
+        D("Failed to open '%s': %s", file, strerror(errno));
         return 0;
     }
 
@@ -283,7 +283,7 @@
     key->rsa = RSA_new();
 
     if (!PEM_read_RSAPrivateKey(fp, &key->rsa, NULL, NULL)) {
-        D("Failed to read key\n");
+        D("Failed to read key");
         fclose(fp);
         RSA_free(key->rsa);
         delete key;
@@ -307,7 +307,7 @@
         WCHAR path[MAX_PATH];
         const HRESULT hr = SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, 0, path);
         if (FAILED(hr)) {
-            D("SHGetFolderPathW failed: %s\n",
+            D("SHGetFolderPathW failed: %s",
               SystemErrorCodeToString(hr).c_str());
             return -1;
         }
@@ -322,7 +322,7 @@
     format = "%s/%s";
 #endif
 
-    D("home '%s'\n", home);
+    D("home '%s'", home);
 
     if (snprintf(android_dir, sizeof(android_dir), format, home,
                         ANDROID_PATH) >= (int)sizeof(android_dir))
@@ -330,7 +330,7 @@
 
     if (stat(android_dir, &buf)) {
         if (adb_mkdir(android_dir, 0750) < 0) {
-            D("Cannot mkdir '%s'\n", android_dir);
+            D("Cannot mkdir '%s'", android_dir);
             return -1;
         }
     }
@@ -346,15 +346,15 @@
 
     ret = get_user_keyfilepath(path, sizeof(path));
     if (ret < 0 || ret >= (signed)sizeof(path)) {
-        D("Error getting user key filename\n");
+        D("Error getting user key filename");
         return 0;
     }
 
-    D("user key '%s'\n", path);
+    D("user key '%s'", path);
 
     if (stat(path, &buf) == -1) {
         if (!generate_key(path)) {
-            D("Failed to generate new key\n");
+            D("Failed to generate new key");
             return 0;
         }
     }
@@ -370,7 +370,7 @@
 
     for (auto& path : android::base::Split(adb_keys_path, ENV_PATH_SEPARATOR_STR)) {
         if (!read_key(path.c_str(), key_list)) {
-            D("Failed to read '%s'\n", path.c_str());
+            D("Failed to read '%s'", path.c_str());
         }
     }
 }
@@ -382,7 +382,7 @@
     struct adb_private_key *key = node_to_item(node, struct adb_private_key, node);
 
     if (token_size != TOKEN_SIZE) {
-        D("Unexpected token size %zd\n", token_size);
+        D("Unexpected token size %zd", token_size);
         return 0;
     }
 
@@ -390,7 +390,7 @@
         return 0;
     }
 
-    D("adb_auth_sign len=%d\n", len);
+    D("adb_auth_sign len=%d", len);
     return (int)len;
 }
 
@@ -421,7 +421,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\n");
+        D("Error getting user key filename");
         return 0;
     }
     strcat(path, ".pub");
@@ -433,12 +433,12 @@
     unsigned size;
     char* file_data = reinterpret_cast<char*>(load_file(path, &size));
     if (file_data == nullptr) {
-        D("Can't load '%s'\n", path);
+        D("Can't load '%s'", path);
         return 0;
     }
 
     if (len < (size_t)(size + 1)) {
-        D("%s: Content too large ret=%d\n", path, size);
+        D("%s: Content too large ret=%d", path, size);
         free(file_data);
         return 0;
     }
@@ -460,13 +460,13 @@
 {
     int ret;
 
-    D("adb_auth_init\n");
+    D("adb_auth_init");
 
     list_init(&key_list);
 
     ret = get_user_key(&key_list);
     if (!ret) {
-        D("Failed to get user key\n");
+        D("Failed to get user key");
         return;
     }
 
diff --git a/adb/adb_client.cpp b/adb/adb_client.cpp
index a1bbb78..ff68415 100644
--- a/adb/adb_client.cpp
+++ b/adb/adb_client.cpp
@@ -90,14 +90,14 @@
         adb_close(fd);
         return -1;
     }
-    D("Switch transport in progress\n");
+    D("Switch transport in progress");
 
     if (!adb_status(fd, error)) {
         adb_close(fd);
-        D("Switch transport failed: %s\n", error->c_str());
+        D("Switch transport failed: %s", error->c_str());
         return -1;
     }
-    D("Switch transport success\n");
+    D("Switch transport success");
     return 0;
 }
 
@@ -123,7 +123,7 @@
 }
 
 int _adb_connect(const std::string& service, std::string* error) {
-    D("_adb_connect: %s\n", service.c_str());
+    D("_adb_connect: %s", service.c_str());
     if (service.empty() || service.size() > 1024) {
         *error = android::base::StringPrintf("bad service name length (%zd)",
                                              service.size());
@@ -164,7 +164,7 @@
         return -1;
     }
 
-    D("_adb_connect: return fd %d\n", fd);
+    D("_adb_connect: return fd %d", fd);
     return fd;
 }
 
@@ -172,7 +172,7 @@
     // first query the adb server's version
     int fd = _adb_connect("host:version", error);
 
-    D("adb_connect: service %s\n", service.c_str());
+    D("adb_connect: service %s", service.c_str());
     if (fd == -2 && __adb_server_name) {
         fprintf(stderr,"** Cannot start server on remote host\n");
         // error is the original network connection error
@@ -246,11 +246,11 @@
 
     fd = _adb_connect(service, error);
     if (fd == -1) {
-        D("_adb_connect error: %s\n", error->c_str());
+        D("_adb_connect error: %s", error->c_str());
     } else if(fd == -2) {
         fprintf(stderr,"** daemon still not running\n");
     }
-    D("adb_connect: return fd %d\n", fd);
+    D("adb_connect: return fd %d", fd);
 
     return fd;
 error:
@@ -277,7 +277,7 @@
 }
 
 bool adb_query(const std::string& service, std::string* result, std::string* error) {
-    D("adb_query: %s\n", service.c_str());
+    D("adb_query: %s", service.c_str());
     int fd = adb_connect(service, error);
     if (fd < 0) {
         return false;
diff --git a/adb/adb_io.cpp b/adb/adb_io.cpp
index 7092609..14e295f 100644
--- a/adb/adb_io.cpp
+++ b/adb/adb_io.cpp
@@ -68,23 +68,23 @@
 
     size_t len0 = len;
 
-    D("readx: fd=%d wanted=%zu\n", fd, len);
+    D("readx: fd=%d wanted=%zu", fd, len);
     while (len > 0) {
         int r = adb_read(fd, p, len);
         if (r > 0) {
             len -= r;
             p += r;
         } else if (r == -1) {
-            D("readx: fd=%d error %d: %s\n", fd, errno, strerror(errno));
+            D("readx: fd=%d error %d: %s", fd, errno, strerror(errno));
             return false;
         } else {
-            D("readx: fd=%d disconnected\n", fd);
+            D("readx: fd=%d disconnected", fd);
             errno = 0;
             return false;
         }
     }
 
-    D("readx: fd=%d wanted=%zu got=%zu\n", fd, len0, len0 - len);
+    D("readx: fd=%d wanted=%zu got=%zu", fd, len0, len0 - len);
     if (ADB_TRACING) {
         dump_hex(reinterpret_cast<const unsigned char*>(buf), len0);
     }
@@ -104,12 +104,12 @@
     while (len > 0) {
         r = adb_write(fd, p, len);
         if (r == -1) {
-            D("writex: fd=%d error %d: %s\n", fd, errno, strerror(errno));
+            D("writex: fd=%d error %d: %s", fd, errno, strerror(errno));
             if (errno == EAGAIN) {
                 adb_sleep_ms(1); // just yield some cpu time
                 continue;
             } else if (errno == EPIPE) {
-                D("writex: fd=%d disconnected\n", fd);
+                D("writex: fd=%d disconnected", fd);
                 errno = 0;
                 return false;
             } else {
diff --git a/adb/adb_io_test.cpp b/adb/adb_io_test.cpp
index f637073..84e3db6 100644
--- a/adb/adb_io_test.cpp
+++ b/adb/adb_io_test.cpp
@@ -31,10 +31,11 @@
 #include "base/test_utils.h"
 
 // All of these tests fail on Windows because they use the C Runtime open(),
-// but the adb_io APIs expect file descriptors from adb_open(). Also, the
-// android::base file APIs use the C Runtime which uses CR/LF translation by
-// default (changeable with _setmode()), but the adb_io APIs use adb_read()
-// and adb_write() which do no translation.
+// but the adb_io APIs expect file descriptors from adb_open(). This could
+// theoretically be fixed by making adb_read()/adb_write() fallback to using
+// read()/write() if an unrecognized fd is used, and by making adb_open() return
+// fds far from the range that open() returns. But all of that might defeat the
+// purpose of the tests.
 
 TEST(io, ReadFdExactly_whole) {
   const char expected[] = "Foobar";
diff --git a/adb/adb_trace.h b/adb/adb_trace.h
index 4e03165..67ee854 100644
--- a/adb/adb_trace.h
+++ b/adb/adb_trace.h
@@ -17,11 +17,8 @@
 #ifndef __ADB_TRACE_H
 #define __ADB_TRACE_H
 
-#if !ADB_HOST
-#include <android/log.h>
-#else
-#include <stdio.h>
-#endif
+#include <base/logging.h>
+#include <base/stringprintf.h>
 
 /* IMPORTANT: if you change the following list, don't
  * forget to update the corresponding 'tags' table in
@@ -46,48 +43,16 @@
 extern unsigned char adb_trace_output_count;
 void adb_trace_init(char**);
 
-#  define ADB_TRACING  ((adb_trace_mask & (1 << TRACE_TAG)) != 0)
+#define ADB_TRACING  ((adb_trace_mask & (1 << TRACE_TAG)) != 0)
 
-/* you must define TRACE_TAG before using this macro */
-#if ADB_HOST
-#  define D(fmt, ...) \
+// You must define TRACE_TAG before using this macro.
+#define D(...) \
         do { \
             if (ADB_TRACING) { \
                 int saved_errno = errno; \
-                adb_mutex_lock(&D_lock); \
-                errno = saved_errno; \
-                fprintf(stderr, "%5d:%5lu %s | " fmt, \
-                        getpid(), adb_thread_id(), __FUNCTION__, ## __VA_ARGS__); \
-                fflush(stderr); \
-                adb_mutex_unlock(&D_lock); \
+                LOG(INFO) << android::base::StringPrintf(__VA_ARGS__); \
                 errno = saved_errno; \
            } \
         } while (0)
-#  define DR(...) \
-        do { \
-            if (ADB_TRACING) { \
-                int saved_errno = errno; \
-                adb_mutex_lock(&D_lock); \
-                errno = saved_errno; \
-                fprintf(stderr, __VA_ARGS__); \
-                fflush(stderr); \
-                adb_mutex_unlock(&D_lock); \
-                errno = saved_errno; \
-           } \
-        } while (0)
-#else
-#  define D(...) \
-        do { \
-            if (ADB_TRACING) { \
-                __android_log_print(ANDROID_LOG_INFO, __FUNCTION__, __VA_ARGS__); \
-            } \
-        } while (0)
-#  define DR(...) \
-        do { \
-            if (ADB_TRACING) { \
-                __android_log_print(ANDROID_LOG_INFO, __FUNCTION__, __VA_ARGS__); \
-            } \
-        } while (0)
-#endif /* ADB_HOST */
 
 #endif /* __ADB_TRACE_H */
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index e5dc692..72ee901 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -170,7 +170,7 @@
         line.push_back(c);
     }
 
-    DR("%s\n", line.c_str());
+    D("%s", line.c_str());
 }
 
 bool parse_host_and_port(const std::string& address,
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index f6ddeb4..6e27c0f 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -36,42 +36,6 @@
 #include "adb_listeners.h"
 #include "transport.h"
 
-#if defined(WORKAROUND_BUG6558362) && defined(__linux__)
-static const bool kWorkaroundBug6558362 = true;
-#else
-static const bool kWorkaroundBug6558362 = false;
-#endif
-
-static void adb_workaround_affinity(void) {
-#if defined(__linux__)
-    const char affinity_env[] = "ADB_CPU_AFFINITY_BUG6558362";
-    const char* cpunum_str = getenv(affinity_env);
-    if (cpunum_str == nullptr || *cpunum_str == '\0') {
-        return;
-    }
-
-    char* strtol_res;
-    int cpu_num = strtol(cpunum_str, &strtol_res, 0);
-    if (*strtol_res != '\0') {
-        fatal("bad number (%s) in env var %s. Expecting 0..n.\n", cpunum_str,
-              affinity_env);
-    }
-
-    cpu_set_t cpu_set;
-    sched_getaffinity(0, sizeof(cpu_set), &cpu_set);
-    D("orig cpu_set[0]=0x%08lx\n", cpu_set.__bits[0]);
-
-    CPU_ZERO(&cpu_set);
-    CPU_SET(cpu_num, &cpu_set);
-    sched_setaffinity(0, sizeof(cpu_set), &cpu_set);
-
-    sched_getaffinity(0, sizeof(cpu_set), &cpu_set);
-    D("new cpu_set[0]=0x%08lx\n", cpu_set.__bits[0]);
-#else
-    // No workaround was ever implemented for the other platforms.
-#endif
-}
-
 #if defined(_WIN32)
 static const char kNullFileName[] = "NUL";
 
@@ -157,10 +121,6 @@
 
     init_transport_registration();
 
-    if (kWorkaroundBug6558362 && is_daemon) {
-        adb_workaround_affinity();
-    }
-
     usb_init();
     local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT);
     adb_auth_init();
@@ -203,7 +163,7 @@
 #endif
     }
 
-    D("Event loop starting\n");
+    D("Event loop starting");
     fdevent_loop();
 
     return 0;
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 6325e64..86f9263 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -258,10 +258,10 @@
 
 static void read_and_dump(int fd) {
     while (fd >= 0) {
-        D("read_and_dump(): pre adb_read(fd=%d)\n", fd);
+        D("read_and_dump(): pre adb_read(fd=%d)", fd);
         char buf[BUFSIZ];
         int len = adb_read(fd, buf, sizeof(buf));
-        D("read_and_dump(): post adb_read(fd=%d): len=%d\n", fd, len);
+        D("read_and_dump(): post adb_read(fd=%d): len=%d", fd, len);
         if (len <= 0) {
             break;
         }
@@ -297,7 +297,7 @@
     int old_stdout_mode = -1;
 #endif
 
-    D("copy_to_file(%d -> %d)\n", inFd, outFd);
+    D("copy_to_file(%d -> %d)", inFd, outFd);
 
     if (inFd == STDIN_FILENO) {
         stdin_raw_init(STDIN_FILENO);
@@ -325,11 +325,11 @@
             len = adb_read(inFd, buf, BUFSIZE);
         }
         if (len == 0) {
-            D("copy_to_file() : read 0 bytes; exiting\n");
+            D("copy_to_file() : read 0 bytes; exiting");
             break;
         }
         if (len < 0) {
-            D("copy_to_file(): read failed: %s\n", strerror(errno));
+            D("copy_to_file(): read failed: %s", strerror(errno));
             break;
         }
         if (outFd == STDOUT_FILENO) {
@@ -358,7 +358,7 @@
     }
 #endif
 
-    D("copy_to_file() finished after %lu bytes\n", total);
+    D("copy_to_file() finished after %lu bytes", total);
     free(buf);
 }
 
@@ -378,9 +378,9 @@
 
     while (true) {
         /* fdi is really the client's stdin, so use read, not adb_read here */
-        D("stdin_read_thread(): pre unix_read(fdi=%d,...)\n", fdi);
+        D("stdin_read_thread(): pre unix_read(fdi=%d,...)", fdi);
         r = unix_read(fdi, buf, 1024);
-        D("stdin_read_thread(): post unix_read(fdi=%d,...)\n", fdi);
+        D("stdin_read_thread(): post unix_read(fdi=%d,...)", fdi);
         if (r <= 0) break;
         for (n = 0; n < r; n++){
             switch(buf[n]) {
@@ -777,7 +777,7 @@
         cmd += " " + escape_arg(*argv++);
     }
 
-    D("backup. filename=%s cmd=%s\n", filename, cmd.c_str());
+    D("backup. filename=%s cmd=%s", filename, cmd.c_str());
     std::string error;
     int fd = adb_connect(cmd, &error);
     if (fd < 0) {
@@ -1152,7 +1152,7 @@
         }
 
         if (argc < 2) {
-            D("starting interactive shell\n");
+            D("starting interactive shell");
             r = interactive_shell();
             if (h) {
                 printf("\x1b[0m");
@@ -1171,14 +1171,14 @@
         }
 
         while (true) {
-            D("interactive shell loop. cmd=%s\n", cmd.c_str());
+            D("interactive shell loop. cmd=%s", cmd.c_str());
             std::string error;
             int fd = adb_connect(cmd, &error);
             int r;
             if (fd >= 0) {
-                D("about to read_and_dump(fd=%d)\n", fd);
+                D("about to read_and_dump(fd=%d)", fd);
                 read_and_dump(fd);
-                D("read_and_dump() done.\n");
+                D("read_and_dump() done.");
                 adb_close(fd);
                 r = 0;
             } else {
@@ -1190,7 +1190,7 @@
                 printf("\x1b[0m");
                 fflush(stdout);
             }
-            D("interactive shell loop. return r=%d\n", r);
+            D("interactive shell loop. return r=%d", r);
             return r;
         }
     }
diff --git a/adb/daemon/main.cpp b/adb/daemon/main.cpp
index 2eba625..cb0cb3d 100644
--- a/adb/daemon/main.cpp
+++ b/adb/daemon/main.cpp
@@ -162,7 +162,7 @@
             PLOG(FATAL) << "Could not setuid";
         }
 
-        D("Local port disabled\n");
+        D("Local port disabled");
     } else {
         if (root_seclabel != nullptr) {
             if (setcon(root_seclabel) < 0) {
@@ -197,7 +197,7 @@
 
     int port;
     if (sscanf(prop_port, "%d", &port) == 1 && port > 0) {
-        printf("using port=%d\n", port);
+        D("using port=%d", port);
         // Listen on TCP port specified by service.adb.tcp.port property.
         local_init(port);
     } else if (!is_usb) {
@@ -205,11 +205,11 @@
         local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT);
     }
 
-    D("adbd_main(): pre init_jdwp()\n");
+    D("adbd_main(): pre init_jdwp()");
     init_jdwp();
-    D("adbd_main(): post init_jdwp()\n");
+    D("adbd_main(): post init_jdwp()");
 
-    D("Event loop starting\n");
+    D("Event loop starting");
     fdevent_loop();
 
     return 0;
@@ -261,6 +261,6 @@
 
     adb_trace_init(argv);
 
-    D("Handling main()\n");
+    D("Handling main()");
     return adbd_main(DEFAULT_ADB_PORT);
 }
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index d25bbfb..db5157f 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -331,11 +331,11 @@
     do {                                                                     \
         printed_out = snprintf(pb, max_chars, __VA_ARGS__);                  \
         if (printed_out <= 0) {                                              \
-            D("... snprintf failed.\n");                                     \
+            D("... snprintf failed.");                                     \
             return;                                                          \
         }                                                                    \
         if (max_chars < (unsigned int)printed_out) {                         \
-            D("... snprintf out of space.\n");                               \
+            D("... snprintf out of space.");                               \
             return;                                                          \
         }                                                                    \
         pb += printed_out;                                                   \
@@ -354,7 +354,7 @@
         }
         SAFE_SPRINTF(" ");
     }
-    D("%s fd_table[]->fd = {%s}\n", extra_msg, msg_buff);
+    D("%s fd_table[]->fd = {%s}", extra_msg, msg_buff);
 }
 #endif
 
@@ -373,7 +373,7 @@
 
     n = select(select_n, &rfd, &wfd, &efd, NULL);
     int saved_errno = errno;
-    D("select() returned n=%d, errno=%d\n", n, n<0?saved_errno:0);
+    D("select() returned n=%d, errno=%d", n, n<0?saved_errno:0);
 
     dump_all_fds("post select()");
 
@@ -387,7 +387,7 @@
             FD_ZERO(&rfd);
             break;
         default:
-            D("Unexpected select() error=%d\n", saved_errno);
+            D("Unexpected select() error=%d", saved_errno);
             return;
         }
     }
@@ -410,7 +410,7 @@
 
             fde->events |= events;
 
-            D("got events fde->fd=%d events=%04x, state=%04x\n",
+            D("got events fde->fd=%d events=%04x, state=%04x",
                 fde->fd, fde->events, fde->state);
             if(fde->state & FDE_PENDING) continue;
             fde->state |= FDE_PENDING;
@@ -516,7 +516,7 @@
                                        void* /* userdata */)
 {
 
-    D("subproc handling on fd=%d ev=%04x\n", fd, ev);
+    D("subproc handling on fd=%d ev=%04x", fd, ev);
 
     // Hook oneself back into the fde's suitable for select() on read.
     if((fd < 0) || (fd >= fd_table_max)) {
@@ -532,18 +532,18 @@
           FATAL("Failed to read the subproc's fd from fd=%d\n", fd);
       }
       if((subproc_fd < 0) || (subproc_fd >= fd_table_max)) {
-          D("subproc_fd %d out of range 0, fd_table_max=%d\n",
+          D("subproc_fd %d out of range 0, fd_table_max=%d",
             subproc_fd, fd_table_max);
           return;
       }
       fdevent *subproc_fde = fd_table[subproc_fd];
       if(!subproc_fde) {
-          D("subproc_fd %d cleared from fd_table\n", subproc_fd);
+          D("subproc_fd %d cleared from fd_table", subproc_fd);
           return;
       }
       if(subproc_fde->fd != subproc_fd) {
           // Already reallocated?
-          D("subproc_fd %d != fd_table[].fd %d\n", subproc_fd, subproc_fde->fd);
+          D("subproc_fd %d != fd_table[].fd %d", subproc_fd, subproc_fde->fd);
           return;
       }
 
@@ -551,7 +551,7 @@
 
       int rcount = 0;
       ioctl(subproc_fd, FIONREAD, &rcount);
-      D("subproc with fd=%d  has rcount=%d err=%d\n",
+      D("subproc with fd=%d  has rcount=%d err=%d",
         subproc_fd, rcount, errno);
 
       if(rcount) {
@@ -561,7 +561,7 @@
         return;
       }
 
-      D("subproc_fde.state=%04x\n", subproc_fde->state);
+      D("subproc_fde.state=%04x", subproc_fde->state);
       subproc_fde->events |= FDE_READ;
       if(subproc_fde->state & FDE_PENDING) {
         return;
@@ -578,7 +578,7 @@
     if(adb_socketpair(s)) {
         FATAL("cannot create shell-exit socket-pair\n");
     }
-    D("socketpair: (%d,%d)\n", s[0], s[1]);
+    D("socketpair: (%d,%d)", s[0], s[1]);
 
     SHELL_EXIT_NOTIFY_FD = s[0];
     fdevent *fde;
@@ -689,7 +689,7 @@
 #endif // !ADB_HOST
 
     while (true) {
-        D("--- ---- waiting for events\n");
+        D("--- ---- waiting for events");
 
         fdevent_process();
 
diff --git a/adb/fdevent.h b/adb/fdevent.h
index 8d84b29..ca1494c 100644
--- a/adb/fdevent.h
+++ b/adb/fdevent.h
@@ -23,7 +23,6 @@
 #define FDE_READ              0x0001
 #define FDE_WRITE             0x0002
 #define FDE_ERROR             0x0004
-#define FDE_TIMEOUT           0x0008
 
 /* features that may be set (via the events set/add/del interface) */
 #define FDE_DONT_CLOSE        0x0080
diff --git a/adb/fdevent_test.cpp b/adb/fdevent_test.cpp
new file mode 100644
index 0000000..f7622b5
--- /dev/null
+++ b/adb/fdevent_test.cpp
@@ -0,0 +1,152 @@
+/*
+ * 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.
+ */
+
+#include "fdevent.h"
+
+#include <gtest/gtest.h>
+
+#include <queue>
+#include <string>
+#include <vector>
+
+#include <pthread.h>
+#include <signal.h>
+
+#include "adb_io.h"
+
+class SignalHandlerRegister {
+  public:
+    SignalHandlerRegister(const std::vector<int>& signums, void (*handler)(int)) {
+        for (auto& sig : signums) {
+            sig_t old_handler = signal(sig, handler);
+            saved_signal_handlers_.push_back(std::make_pair(sig, old_handler));
+        }
+    }
+
+    ~SignalHandlerRegister() {
+        for (auto& pair : saved_signal_handlers_) {
+            signal(pair.first, pair.second);
+        }
+    }
+
+  private:
+    std::vector<std::pair<int, sig_t>> saved_signal_handlers_;
+};
+
+class FdHandler {
+  public:
+    FdHandler(int read_fd, int write_fd) : read_fd_(read_fd), write_fd_(write_fd) {
+        fdevent_install(&read_fde_, read_fd_, FdEventCallback, this);
+        fdevent_add(&read_fde_, FDE_READ | FDE_ERROR);
+        fdevent_install(&write_fde_, write_fd_, FdEventCallback, this);
+        fdevent_add(&write_fde_, FDE_ERROR);
+    }
+
+  private:
+    static void FdEventCallback(int fd, unsigned events, void* userdata) {
+        FdHandler* handler = reinterpret_cast<FdHandler*>(userdata);
+        ASSERT_EQ(0u, (events & ~(FDE_READ | FDE_WRITE))) << "unexpected events: " << events;
+        if (events & FDE_READ) {
+            ASSERT_EQ(fd, handler->read_fd_);
+            char c;
+            ASSERT_EQ(1, read(fd, &c, 1));
+            handler->queue_.push(c);
+            fdevent_add(&handler->write_fde_, FDE_WRITE);
+        }
+        if (events & FDE_WRITE) {
+            ASSERT_EQ(fd, handler->write_fd_);
+            ASSERT_FALSE(handler->queue_.empty());
+            char c = handler->queue_.front();
+            handler->queue_.pop();
+            ASSERT_EQ(1, write(fd, &c, 1));
+            if (handler->queue_.empty()) {
+              fdevent_del(&handler->write_fde_, FDE_WRITE);
+            }
+        }
+    }
+
+  private:
+    const int read_fd_;
+    const int write_fd_;
+    fdevent read_fde_;
+    fdevent write_fde_;
+    std::queue<char> queue_;
+};
+
+static void signal_handler(int) {
+    pthread_exit(nullptr);
+}
+
+struct ThreadArg {
+    int first_read_fd;
+    int last_write_fd;
+    size_t middle_pipe_count;
+};
+
+static void FdEventThreadFunc(ThreadArg* arg) {
+    SignalHandlerRegister signal_handler_register({SIGUSR1}, signal_handler);
+
+    std::vector<int> read_fds;
+    std::vector<int> write_fds;
+
+    read_fds.push_back(arg->first_read_fd);
+    for (size_t i = 0; i < arg->middle_pipe_count; ++i) {
+        int fds[2];
+        ASSERT_EQ(0, pipe(fds));
+        read_fds.push_back(fds[0]);
+        write_fds.push_back(fds[1]);
+    }
+    write_fds.push_back(arg->last_write_fd);
+
+    std::vector<std::unique_ptr<FdHandler>> fd_handlers;
+    for (size_t i = 0; i < read_fds.size(); ++i) {
+        fd_handlers.push_back(std::unique_ptr<FdHandler>(new FdHandler(read_fds[i], write_fds[i])));
+    }
+
+    fdevent_loop();
+}
+
+TEST(fdevent, smoke) {
+    const size_t PIPE_COUNT = 10;
+    const size_t MESSAGE_LOOP_COUNT = 100;
+    const std::string MESSAGE = "fdevent_test";
+    int fd_pair1[2];
+    int fd_pair2[2];
+    ASSERT_EQ(0, pipe(fd_pair1));
+    ASSERT_EQ(0, pipe(fd_pair2));
+    pthread_t thread;
+    ThreadArg thread_arg;
+    thread_arg.first_read_fd = fd_pair1[0];
+    thread_arg.last_write_fd = fd_pair2[1];
+    thread_arg.middle_pipe_count = PIPE_COUNT;
+    int writer = fd_pair1[1];
+    int reader = fd_pair2[0];
+
+    ASSERT_EQ(0, pthread_create(&thread, nullptr,
+                                reinterpret_cast<void* (*)(void*)>(FdEventThreadFunc),
+                                &thread_arg));
+
+    for (size_t i = 0; i < MESSAGE_LOOP_COUNT; ++i) {
+        std::string read_buffer = MESSAGE;
+        std::string write_buffer(MESSAGE.size(), 'a');
+        ASSERT_TRUE(WriteFdExactly(writer, read_buffer.c_str(), read_buffer.size()));
+        ASSERT_TRUE(ReadFdExactly(reader, &write_buffer[0], write_buffer.size()));
+        ASSERT_EQ(read_buffer, write_buffer);
+    }
+
+    ASSERT_EQ(0, pthread_kill(thread, SIGUSR1));
+    ASSERT_EQ(0, pthread_join(thread, nullptr));
+}
diff --git a/adb/file_sync_service.cpp b/adb/file_sync_service.cpp
index 3793b8e..8db9ca7 100644
--- a/adb/file_sync_service.cpp
+++ b/adb/file_sync_service.cpp
@@ -130,7 +130,7 @@
 }
 
 static bool SendSyncFail(int fd, const std::string& reason) {
-    D("sync: failure: %s\n", reason.c_str());
+    D("sync: failure: %s", reason.c_str());
 
     syncmsg msg;
     msg.data.id = ID_FAIL;
@@ -346,7 +346,7 @@
 }
 
 static bool handle_sync_command(int fd, std::vector<char>& buffer) {
-    D("sync: waiting for request\n");
+    D("sync: waiting for request");
 
     SyncRequest request;
     if (!ReadFdExactly(fd, &request, sizeof(request))) {
@@ -366,7 +366,7 @@
     name[path_length] = 0;
 
     const char* id = reinterpret_cast<const char*>(&request.id);
-    D("sync: '%.4s' '%s'\n", id, name);
+    D("sync: '%.4s' '%s'", id, name);
 
     switch (request.id) {
       case ID_STAT:
@@ -398,6 +398,6 @@
     while (handle_sync_command(fd, buffer)) {
     }
 
-    D("sync: done\n");
+    D("sync: done");
     adb_close(fd);
 }
diff --git a/adb/jdwp_service.cpp b/adb/jdwp_service.cpp
index 06e7780..0fa0732 100644
--- a/adb/jdwp_service.cpp
+++ b/adb/jdwp_service.cpp
@@ -218,7 +218,7 @@
         calloc(1, sizeof(*proc)));
 
     if (proc == NULL) {
-        D("not enough memory to create new JDWP process\n");
+        D("not enough memory to create new JDWP process");
         return NULL;
     }
 
@@ -229,7 +229,7 @@
 
     proc->fde = fdevent_create( socket, jdwp_process_event, proc );
     if (proc->fde == NULL) {
-        D("could not create fdevent for new JDWP process\n" );
+        D("could not create fdevent for new JDWP process" );
         free(proc);
         return NULL;
     }
@@ -271,13 +271,13 @@
                     if (errno == EAGAIN)
                         return;
                     /* this can fail here if the JDWP process crashes very fast */
-                    D("weird unknown JDWP process failure: %s\n",
+                    D("weird unknown JDWP process failure: %s",
                       strerror(errno));
 
                     goto CloseProcess;
                 }
                 if (len == 0) {  /* end of stream ? */
-                    D("weird end-of-stream from unknown JDWP process\n");
+                    D("weird end-of-stream from unknown JDWP process");
                     goto CloseProcess;
                 }
                 p            += len;
@@ -289,12 +289,12 @@
             temp[4] = 0;
 
             if (sscanf( temp, "%04x", &proc->pid ) != 1) {
-                D("could not decode JDWP %p PID number: '%s'\n", proc, temp);
+                D("could not decode JDWP %p PID number: '%s'", proc, temp);
                 goto CloseProcess;
             }
 
             /* all is well, keep reading to detect connection closure */
-            D("Adding pid %d to jdwp process list\n", proc->pid);
+            D("Adding pid %d to jdwp process list", proc->pid);
             jdwp_process_list_updated();
         }
         else
@@ -312,27 +312,27 @@
                     if (len < 0 && errno == EAGAIN)
                         return;
                     else {
-                        D("terminating JDWP %d connection: %s\n", proc->pid,
+                        D("terminating JDWP %d connection: %s", proc->pid,
                           strerror(errno));
                         break;
                     }
                 }
                 else {
-                    D( "ignoring unexpected JDWP %d control socket activity (%d bytes)\n",
+                    D( "ignoring unexpected JDWP %d control socket activity (%d bytes)",
                        proc->pid, len );
                 }
             }
 
         CloseProcess:
             if (proc->pid >= 0)
-                D( "remove pid %d to jdwp process list\n", proc->pid );
+                D( "remove pid %d to jdwp process list", proc->pid );
             jdwp_process_free(proc);
             return;
         }
     }
 
     if (events & FDE_WRITE) {
-        D("trying to write to JDWP pid controli (count=%d first=%d) %d\n",
+        D("trying to write to JDWP pid controli (count=%d first=%d) %d",
           proc->pid, proc->out_count, proc->out_fds[0]);
         if (proc->out_count > 0) {
             int  fd = proc->out_fds[0];
@@ -363,14 +363,14 @@
             flags = fcntl(proc->socket,F_GETFL,0);
 
             if (flags == -1) {
-                D("failed to get cntl flags for socket %d: %s\n",
+                D("failed to get cntl flags for socket %d: %s",
                   proc->pid, strerror(errno));
                 goto CloseProcess;
 
             }
 
             if (fcntl(proc->socket, F_SETFL, flags & ~O_NONBLOCK) == -1) {
-                D("failed to remove O_NONBLOCK flag for socket %d: %s\n",
+                D("failed to remove O_NONBLOCK flag for socket %d: %s",
                   proc->pid, strerror(errno));
                 goto CloseProcess;
             }
@@ -383,19 +383,19 @@
                 }
                 if (errno == EINTR)
                     continue;
-                D("sending new file descriptor to JDWP %d failed: %s\n",
+                D("sending new file descriptor to JDWP %d failed: %s",
                   proc->pid, strerror(errno));
                 goto CloseProcess;
             }
 
-            D("sent file descriptor %d to JDWP process %d\n",
+            D("sent file descriptor %d to JDWP process %d",
               fd, proc->pid);
 
             for (n = 1; n < proc->out_count; n++)
                 proc->out_fds[n-1] = proc->out_fds[n];
 
             if (fcntl(proc->socket, F_SETFL, flags) == -1) {
-                D("failed to set O_NONBLOCK flag for socket %d: %s\n",
+                D("failed to set O_NONBLOCK flag for socket %d: %s",
                   proc->pid, strerror(errno));
                 goto CloseProcess;
             }
@@ -412,13 +412,13 @@
 {
     JdwpProcess*  proc = _jdwp_list.next;
 
-    D("looking for pid %d in JDWP process list\n", pid);
+    D("looking for pid %d in JDWP process list", pid);
     for ( ; proc != &_jdwp_list; proc = proc->next ) {
         if (proc->pid == pid) {
             goto FoundIt;
         }
     }
-    D("search failed !!\n");
+    D("search failed !!");
     return -1;
 
 FoundIt:
@@ -426,17 +426,17 @@
         int  fds[2];
 
         if (proc->out_count >= MAX_OUT_FDS) {
-            D("%s: too many pending JDWP connection for pid %d\n",
+            D("%s: too many pending JDWP connection for pid %d",
               __FUNCTION__, pid);
             return -1;
         }
 
         if (adb_socketpair(fds) < 0) {
-            D("%s: socket pair creation failed: %s\n",
+            D("%s: socket pair creation failed: %s",
               __FUNCTION__, strerror(errno));
             return -1;
         }
-        D("socketpair: (%d,%d)\n", fds[0], fds[1]);
+        D("socketpair: (%d,%d)", fds[0], fds[1]);
 
         proc->out_fds[ proc->out_count ] = fds[1];
         if (++proc->out_count == 1)
@@ -477,7 +477,7 @@
     int                  pathlen = socknamelen;
 
     if (pathlen >= maxpath) {
-        D( "vm debug control socket name too long (%d extra chars)\n",
+        D( "vm debug control socket name too long (%d extra chars)",
            pathlen+1-maxpath );
         return -1;
     }
@@ -488,7 +488,7 @@
 
     s = socket( AF_UNIX, SOCK_STREAM, 0 );
     if (s < 0) {
-        D( "could not create vm debug control socket. %d: %s\n",
+        D( "could not create vm debug control socket. %d: %s",
            errno, strerror(errno));
         return -1;
     }
@@ -496,14 +496,14 @@
     addrlen = (pathlen + sizeof(addr.sun_family));
 
     if (bind(s, (struct sockaddr*)&addr, addrlen) < 0) {
-        D( "could not bind vm debug control socket: %d: %s\n",
+        D( "could not bind vm debug control socket: %d: %s",
            errno, strerror(errno) );
         adb_close(s);
         return -1;
     }
 
     if ( listen(s, 4) < 0 ) {
-        D("listen failed in jdwp control socket: %d: %s\n",
+        D("listen failed in jdwp control socket: %d: %s",
           errno, strerror(errno));
         adb_close(s);
         return -1;
@@ -513,7 +513,7 @@
 
     control->fde = fdevent_create(s, jdwp_control_event, control);
     if (control->fde == NULL) {
-        D( "could not create fdevent for jdwp control socket\n" );
+        D( "could not create fdevent for jdwp control socket" );
         adb_close(s);
         return -1;
     }
@@ -522,7 +522,7 @@
     fdevent_add(control->fde, FDE_READ);
     close_on_exec(s);
 
-    D("jdwp control socket started (%d)\n", control->listen_socket);
+    D("jdwp control socket started (%d)", control->listen_socket);
     return 0;
 }
 
@@ -545,11 +545,11 @@
                     continue;
                 if (errno == ECONNABORTED) {
                     /* oops, the JDWP process died really quick */
-                    D("oops, the JDWP process died really quick\n");
+                    D("oops, the JDWP process died really quick");
                     return;
                 }
                 /* the socket is probably closed ? */
-                D( "weird accept() failed on jdwp control socket: %s\n",
+                D( "weird accept() failed on jdwp control socket: %s",
                    strerror(errno) );
                 return;
             }
diff --git a/adb/mutex_list.h b/adb/mutex_list.h
index 9003361..79c48d8 100644
--- a/adb/mutex_list.h
+++ b/adb/mutex_list.h
@@ -14,13 +14,4 @@
 #endif
 ADB_MUTEX(usb_lock)
 
-// Sadly logging to /data/adb/adb-... is not thread safe.
-//  After modifying adb.h::D() to count invocations:
-//   DEBUG(jpa):0:Handling main()
-//   DEBUG(jpa):1:[ usb_init - starting thread ]
-// (Oopsies, no :2:, and matching message is also gone.)
-//   DEBUG(jpa):3:[ usb_thread - opening device ]
-//   DEBUG(jpa):4:jdwp control socket started (10)
-ADB_MUTEX(D_lock)
-
 #undef ADB_MUTEX
diff --git a/adb/services.cpp b/adb/services.cpp
index 4606804..ac672f0 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -147,7 +147,7 @@
         const char* const command_file = "/cache/recovery/command";
         // Ensure /cache/recovery exists.
         if (adb_mkdir(recovery_dir, 0770) == -1 && errno != EEXIST) {
-            D("Failed to create directory '%s': %s\n", recovery_dir, strerror(errno));
+            D("Failed to create directory '%s': %s", recovery_dir, strerror(errno));
             return false;
         }
 
@@ -212,7 +212,7 @@
         printf("cannot create service socket pair\n");
         return -1;
     }
-    D("socketpair: (%d,%d)\n", s[0], s[1]);
+    D("socketpair: (%d,%d)", s[0], s[1]);
 
     stinfo* sti = reinterpret_cast<stinfo*>(malloc(sizeof(stinfo)));
     if (sti == nullptr) {
@@ -230,7 +230,7 @@
         return -1;
     }
 
-    D("service thread started, %d:%d\n",s[0], s[1]);
+    D("service thread started, %d:%d",s[0], s[1]);
     return s[0];
 }
 
@@ -246,14 +246,14 @@
         adb_write(fd, "0", 1);
         adb_close(fd);
     } else {
-       D("adb: unable to update oom_score_adj\n");
+       D("adb: unable to update oom_score_adj");
     }
 }
 
 #if !ADB_HOST
 static int create_subproc_pty(const char* cmd, const char* arg0,
                               const char* arg1, pid_t* pid) {
-    D("create_subproc_pty(cmd=%s, arg0=%s, arg1=%s)\n", cmd, arg0, arg1);
+    D("create_subproc_pty(cmd=%s, arg0=%s, arg1=%s)", cmd, arg0, arg1);
     char pts_name[PATH_MAX];
     int ptm;
     *pid = forkpty(&ptm, pts_name, nullptr, nullptr);
@@ -312,7 +312,7 @@
 
 static int create_subproc_raw(const char *cmd, const char *arg0, const char *arg1, pid_t *pid)
 {
-    D("create_subproc_raw(cmd=%s, arg0=%s, arg1=%s)\n", cmd, arg0, arg1);
+    D("create_subproc_raw(cmd=%s, arg0=%s, arg1=%s)", cmd, arg0, arg1);
 #if defined(_WIN32)
     fprintf(stderr, "error: create_subproc_raw not implemented on Win32 (%s %s %s)\n", cmd, arg0, arg1);
     return -1;
@@ -324,7 +324,7 @@
         printf("[ cannot create socket pair - %s ]\n", strerror(errno));
         return -1;
     }
-    D("socketpair: (%d,%d)\n", sv[0], sv[1]);
+    D("socketpair: (%d,%d)", sv[0], sv[1]);
 
     *pid = fork();
     if (*pid < 0) {
@@ -367,29 +367,29 @@
 {
     pid_t pid = (pid_t) (uintptr_t) cookie;
 
-    D("entered. fd=%d of pid=%d\n", fd, pid);
+    D("entered. fd=%d of pid=%d", fd, pid);
     while (true) {
         int status;
         pid_t p = waitpid(pid, &status, 0);
         if (p == pid) {
-            D("fd=%d, post waitpid(pid=%d) status=%04x\n", fd, p, status);
+            D("fd=%d, post waitpid(pid=%d) status=%04x", fd, p, status);
             if (WIFSIGNALED(status)) {
-                D("*** Killed by signal %d\n", WTERMSIG(status));
+                D("*** Killed by signal %d", WTERMSIG(status));
                 break;
             } else if (!WIFEXITED(status)) {
-                D("*** Didn't exit!!. status %d\n", status);
+                D("*** Didn't exit!!. status %d", status);
                 break;
             } else if (WEXITSTATUS(status) >= 0) {
-                D("*** Exit code %d\n", WEXITSTATUS(status));
+                D("*** Exit code %d", WEXITSTATUS(status));
                 break;
             }
          }
     }
-    D("shell exited fd=%d of pid=%d err=%d\n", fd, pid, errno);
+    D("shell exited fd=%d of pid=%d err=%d", fd, pid, errno);
     if (SHELL_EXIT_NOTIFY_FD >=0) {
       int res;
       res = WriteFdExactly(SHELL_EXIT_NOTIFY_FD, &fd, sizeof(fd)) ? 0 : -1;
-      D("notified shell exit via fd=%d for pid=%d res=%d errno=%d\n",
+      D("notified shell exit via fd=%d for pid=%d res=%d errno=%d",
         SHELL_EXIT_NOTIFY_FD, pid, res, errno);
     }
 }
@@ -419,7 +419,7 @@
     } else {
         ret_fd = create_subproc_raw(SHELL_COMMAND, arg0, arg1, &pid);
     }
-    D("create_subproc ret_fd=%d pid=%d\n", ret_fd, pid);
+    D("create_subproc ret_fd=%d pid=%d", ret_fd, pid);
 
     stinfo* sti = reinterpret_cast<stinfo*>(malloc(sizeof(stinfo)));
     if(sti == 0) fatal("cannot allocate stinfo");
@@ -434,7 +434,7 @@
         return -1;
     }
 
-    D("service thread started, fd=%d pid=%d\n", ret_fd, pid);
+    D("service thread started, fd=%d pid=%d", ret_fd, pid);
     return ret_fd;
 }
 #endif
@@ -549,7 +549,7 @@
 {
     state_info* sinfo = reinterpret_cast<state_info*>(cookie);
 
-    D("wait_for_state %d\n", sinfo->state);
+    D("wait_for_state %d", sinfo->state);
 
     std::string error_msg = "unknown error";
     atransport* t = acquire_one_transport(sinfo->state, sinfo->transport_type, sinfo->serial,
@@ -564,7 +564,7 @@
         free(sinfo->serial);
     free(sinfo);
     adb_close(fd);
-    D("wait_for_state is done\n");
+    D("wait_for_state is done");
 }
 
 static void connect_device(const std::string& address, std::string* response) {
@@ -588,7 +588,7 @@
         return;
     }
 
-    D("client: connected %s remote on fd %d\n", serial.c_str(), fd);
+    D("client: connected %s remote on fd %d", serial.c_str(), fd);
     close_on_exec(fd);
     disable_tcp_nagle(fd);
 
diff --git a/adb/sockets.cpp b/adb/sockets.cpp
index 9c13936..f8c22cc 100644
--- a/adb/sockets.cpp
+++ b/adb/sockets.cpp
@@ -133,7 +133,7 @@
 
 static int local_socket_enqueue(asocket *s, apacket *p)
 {
-    D("LS(%d): enqueue %d\n", s->id, p->len);
+    D("LS(%d): enqueue %d", s->id, p->len);
 
     p->ptr = p->data;
 
@@ -156,7 +156,7 @@
             continue;
         }
         if((r == 0) || (errno != EAGAIN)) {
-            D( "LS(%d): not ready, errno=%d: %s\n", s->id, errno, strerror(errno) );
+            D( "LS(%d): not ready, errno=%d: %s", s->id, errno, strerror(errno) );
             s->close(s);
             return 1; /* not ready (error) */
         } else {
@@ -204,7 +204,7 @@
     apacket *p, *n;
     int exit_on_close = s->exit_on_close;
 
-    D("LS(%d): destroying fde.fd=%d\n", s->id, s->fde.fd);
+    D("LS(%d): destroying fde.fd=%d", s->id, s->fde.fd);
 
         /* IMPORTANT: the remove closes the fd
         ** that belongs to this socket
@@ -213,7 +213,7 @@
 
         /* dispose of any unwritten data */
     for(p = s->pkt_first; p; p = n) {
-        D("LS(%d): discarding %d bytes\n", s->id, p->len);
+        D("LS(%d): discarding %d bytes", s->id, p->len);
         n = p->next;
         put_apacket(p);
     }
@@ -221,7 +221,7 @@
     free(s);
 
     if (exit_on_close) {
-        D("local_socket_destroy: exiting\n");
+        D("local_socket_destroy: exiting");
         exit(1);
     }
 }
@@ -229,9 +229,9 @@
 
 static void local_socket_close_locked(asocket *s)
 {
-    D("entered local_socket_close_locked. LS(%d) fd=%d\n", s->id, s->fd);
+    D("entered local_socket_close_locked. LS(%d) fd=%d", s->id, s->fd);
     if(s->peer) {
-        D("LS(%d): closing peer. peer->id=%d peer->fd=%d\n",
+        D("LS(%d): closing peer. peer->id=%d peer->fd=%d",
           s->id, s->peer->id, s->peer->fd);
         /* Note: it's important to call shutdown before disconnecting from
          * the peer, this ensures that remote sockets can still get the id
@@ -255,24 +255,24 @@
     if (s->closing || s->pkt_first == NULL) {
         int   id = s->id;
         local_socket_destroy(s);
-        D("LS(%d): closed\n", id);
+        D("LS(%d): closed", id);
         return;
     }
 
         /* otherwise, put on the closing list
         */
-    D("LS(%d): closing\n", s->id);
+    D("LS(%d): closing", s->id);
     s->closing = 1;
     fdevent_del(&s->fde, FDE_READ);
     remove_socket(s);
-    D("LS(%d): put on socket_closing_list fd=%d\n", s->id, s->fd);
+    D("LS(%d): put on socket_closing_list fd=%d", s->id, s->fd);
     insert_local_socket(s, &local_socket_closing_list);
 }
 
 static void local_socket_event_func(int fd, unsigned ev, void* _s)
 {
     asocket* s = reinterpret_cast<asocket*>(_s);
-    D("LS(%d): event_func(fd=%d(==%d), ev=%04x)\n", s->id, s->fd, fd, ev);
+    D("LS(%d): event_func(fd=%d(==%d), ev=%04x)", s->id, s->fd, fd, ev);
 
     /* put the FDE_WRITE processing before the FDE_READ
     ** in order to simplify the code.
@@ -295,7 +295,7 @@
                     continue;
                 }
 
-                D(" closing after write because r=%d and errno is %d\n", r, errno);
+                D(" closing after write because r=%d and errno is %d", r, errno);
                 s->close(s);
                 return;
             }
@@ -313,7 +313,7 @@
         ** we can now destroy it.
         */
         if (s->closing) {
-            D(" closing because 'closing' is set after write\n");
+            D(" closing because 'closing' is set after write");
             s->close(s);
             return;
         }
@@ -337,7 +337,7 @@
 
         while (avail > 0) {
             r = adb_read(fd, x, avail);
-            D("LS(%d): post adb_read(fd=%d,...) r=%d (errno=%d) avail=%zu\n",
+            D("LS(%d): post adb_read(fd=%d,...) r=%d (errno=%d) avail=%zu",
               s->id, s->fd, r, r < 0 ? errno : 0, avail);
             if (r == -1) {
                 if (errno == EAGAIN) {
@@ -353,7 +353,7 @@
             is_eof = 1;
             break;
         }
-        D("LS(%d): fd=%d post avail loop. r=%d is_eof=%d forced_eof=%d\n",
+        D("LS(%d): fd=%d post avail loop. r=%d is_eof=%d forced_eof=%d",
           s->id, s->fd, r, is_eof, s->fde.force_eof);
         if ((avail == max_payload) || (s->peer == 0)) {
             put_apacket(p);
@@ -365,7 +365,7 @@
             unsigned saved_id = s->id;
             int saved_fd = s->fd;
             r = s->peer->enqueue(s->peer, p);
-            D("LS(%u): fd=%d post peer->enqueue(). r=%d\n", saved_id, saved_fd, r);
+            D("LS(%u): fd=%d post peer->enqueue(). r=%d", saved_id, saved_fd, r);
 
             if (r < 0) {
                     /* error return means they closed us as a side-effect
@@ -389,7 +389,7 @@
         }
         /* Don't allow a forced eof if data is still there */
         if ((s->fde.force_eof && !r) || is_eof) {
-            D(" closing because is_eof=%d r=%d s->fde.force_eof=%d\n",
+            D(" closing because is_eof=%d r=%d s->fde.force_eof=%d",
               is_eof, r, s->fde.force_eof);
             s->close(s);
         }
@@ -400,7 +400,7 @@
             ** catching it here means we may skip the last few
             ** bytes of readable data.
             */
-        D("LS(%d): FDE_ERROR (fd=%d)\n", s->id, s->fd);
+        D("LS(%d): FDE_ERROR (fd=%d)", s->id, s->fd);
 
         return;
     }
@@ -418,7 +418,7 @@
     install_local_socket(s);
 
     fdevent_install(&s->fde, fd, local_socket_event_func, s);
-    D("LS(%d): created (fd=%d)\n", s->id, s->fd);
+    D("LS(%d): created (fd=%d)", s->id, s->fd);
     return s;
 }
 
@@ -436,7 +436,7 @@
     if(fd < 0) return 0;
 
     asocket* s = create_local_socket(fd);
-    D("LS(%d): bound to '%s' via %d\n", s->id, name, fd);
+    D("LS(%d): bound to '%s' via %d", s->id, name, fd);
 
 #if !ADB_HOST
     char debug[PROPERTY_VALUE_MAX];
@@ -447,7 +447,7 @@
         || (!strncmp(name, "unroot:", 7) && getuid() == 0)
         || !strncmp(name, "usb:", 4)
         || !strncmp(name, "tcpip:", 6)) {
-        D("LS(%d): enabling exit_on_close\n", s->id);
+        D("LS(%d): enabling exit_on_close", s->id);
         s->exit_on_close = 1;
     }
 #endif
@@ -463,7 +463,7 @@
     s = host_service_to_socket(name, serial);
 
     if (s != NULL) {
-        D("LS(%d) bound to '%s'\n", s->id, name);
+        D("LS(%d) bound to '%s'", s->id, name);
         return s;
     }
 
@@ -473,7 +473,7 @@
 
 static int remote_socket_enqueue(asocket *s, apacket *p)
 {
-    D("entered remote_socket_enqueue RS(%d) WRITE fd=%d peer.fd=%d\n",
+    D("entered remote_socket_enqueue RS(%d) WRITE fd=%d peer.fd=%d",
       s->id, s->fd, s->peer->fd);
     p->msg.command = A_WRTE;
     p->msg.arg0 = s->peer->id;
@@ -485,7 +485,7 @@
 
 static void remote_socket_ready(asocket *s)
 {
-    D("entered remote_socket_ready RS(%d) OKAY fd=%d peer.fd=%d\n",
+    D("entered remote_socket_ready RS(%d) OKAY fd=%d peer.fd=%d",
       s->id, s->fd, s->peer->fd);
     apacket *p = get_apacket();
     p->msg.command = A_OKAY;
@@ -496,7 +496,7 @@
 
 static void remote_socket_shutdown(asocket *s)
 {
-    D("entered remote_socket_shutdown RS(%d) CLOSE fd=%d peer->fd=%d\n",
+    D("entered remote_socket_shutdown RS(%d) CLOSE fd=%d peer->fd=%d",
       s->id, s->fd, s->peer?s->peer->fd:-1);
     apacket *p = get_apacket();
     p->msg.command = A_CLSE;
@@ -511,13 +511,13 @@
 {
     if (s->peer) {
         s->peer->peer = 0;
-        D("RS(%d) peer->close()ing peer->id=%d peer->fd=%d\n",
+        D("RS(%d) peer->close()ing peer->id=%d peer->fd=%d",
           s->id, s->peer->id, s->peer->fd);
         s->peer->close(s->peer);
     }
-    D("entered remote_socket_close RS(%d) CLOSE fd=%d peer->fd=%d\n",
+    D("entered remote_socket_close RS(%d) CLOSE fd=%d peer->fd=%d",
       s->id, s->fd, s->peer?s->peer->fd:-1);
-    D("RS(%d): closed\n", s->id);
+    D("RS(%d): closed", s->id);
     free(s);
 }
 
@@ -538,13 +538,13 @@
     s->close = remote_socket_close;
     s->transport = t;
 
-    D("RS(%d): created\n", s->id);
+    D("RS(%d): created", s->id);
     return s;
 }
 
 void connect_to_remote(asocket *s, const char *destination)
 {
-    D("Connect_to_remote call RS(%d) fd=%d\n", s->id, s->fd);
+    D("Connect_to_remote call RS(%d) fd=%d", s->id, s->fd);
     apacket *p = get_apacket();
     size_t len = strlen(destination) + 1;
 
@@ -552,7 +552,7 @@
         fatal("destination oversized");
     }
 
-    D("LS(%d): connect('%s')\n", s->id, destination);
+    D("LS(%d): connect('%s')", s->id, destination);
     p->msg.command = A_OPEN;
     p->msg.arg0 = s->id;
     p->msg.data_length = len;
@@ -671,14 +671,14 @@
     TransportType type = kTransportAny;
 #endif
 
-    D("SS(%d): enqueue %d\n", s->id, p->len);
+    D("SS(%d): enqueue %d", s->id, p->len);
 
     if(s->pkt_first == 0) {
         s->pkt_first = p;
         s->pkt_last = p;
     } else {
         if((s->pkt_first->len + p->len) > s->get_max_payload()) {
-            D("SS(%d): overflow\n", s->id);
+            D("SS(%d): overflow", s->id);
             put_apacket(p);
             goto fail;
         }
@@ -696,20 +696,20 @@
 
     len = unhex(p->data, 4);
     if((len < 1) ||  (len > 1024)) {
-        D("SS(%d): bad size (%d)\n", s->id, len);
+        D("SS(%d): bad size (%d)", s->id, len);
         goto fail;
     }
 
-    D("SS(%d): len is %d\n", s->id, len );
+    D("SS(%d): len is %d", s->id, len );
         /* can't do anything until we have the full header */
     if((len + 4) > p->len) {
-        D("SS(%d): waiting for %d more bytes\n", s->id, len+4 - p->len);
+        D("SS(%d): waiting for %d more bytes", s->id, len+4 - p->len);
         return 0;
     }
 
     p->data[len + 4] = 0;
 
-    D("SS(%d): '%s'\n", s->id, (char*) (p->data + 4));
+    D("SS(%d): '%s'", s->id, (char*) (p->data + 4));
 
 #if ADB_HOST
     service = (char *)p->data + 4;
@@ -747,11 +747,11 @@
             */
         if(handle_host_request(service, type, serial, s->peer->fd, s) == 0) {
                 /* XXX fail message? */
-            D( "SS(%d): handled host service '%s'\n", s->id, service );
+            D( "SS(%d): handled host service '%s'", s->id, service );
             goto fail;
         }
         if (!strncmp(service, "transport", strlen("transport"))) {
-            D( "SS(%d): okay transport\n", s->id );
+            D( "SS(%d): okay transport", s->id );
             p->len = 0;
             return 0;
         }
@@ -762,7 +762,7 @@
             */
         s2 = create_host_service_socket(service, serial);
         if(s2 == 0) {
-            D( "SS(%d): couldn't create host service '%s'\n", s->id, service );
+            D( "SS(%d): couldn't create host service '%s'", s->id, service );
             SendFail(s->peer->fd, "unknown host service");
             goto fail;
         }
@@ -782,7 +782,7 @@
         s->peer->peer = s2;
         s2->peer = s->peer;
         s->peer = 0;
-        D( "SS(%d): okay\n", s->id );
+        D( "SS(%d): okay", s->id );
         s->close(s);
 
             /* initial state is "ready" */
@@ -839,12 +839,12 @@
 
 static void smart_socket_ready(asocket *s)
 {
-    D("SS(%d): ready\n", s->id);
+    D("SS(%d): ready", s->id);
 }
 
 static void smart_socket_close(asocket *s)
 {
-    D("SS(%d): closed\n", s->id);
+    D("SS(%d): closed", s->id);
     if(s->pkt_first){
         put_apacket(s->pkt_first);
     }
@@ -858,7 +858,7 @@
 
 static asocket *create_smart_socket(void)
 {
-    D("Creating smart socket \n");
+    D("Creating smart socket");
     asocket *s = reinterpret_cast<asocket*>(calloc(1, sizeof(asocket)));
     if (s == NULL) fatal("cannot allocate socket");
     s->enqueue = smart_socket_enqueue;
@@ -866,13 +866,13 @@
     s->shutdown = NULL;
     s->close = smart_socket_close;
 
-    D("SS(%d)\n", s->id);
+    D("SS(%d)", s->id);
     return s;
 }
 
 void connect_to_smartsocket(asocket *s)
 {
-    D("Connecting to smart socket \n");
+    D("Connecting to smart socket");
     asocket *ss = create_smart_socket();
     s->peer = ss;
     ss->peer = s;
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index 015f89e..b53cd77 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -122,7 +122,7 @@
     // only need to check for INVALID_HANDLE_VALUE.
     if (h != INVALID_HANDLE_VALUE) {
         if (!CloseHandle(h)) {
-            D("CloseHandle(%p) failed: %s\n", h,
+            D("CloseHandle(%p) failed: %s", h,
               SystemErrorCodeToString(GetLastError()).c_str());
         }
     }
@@ -159,7 +159,7 @@
     if (file_size > 0) {
         data = (char*) malloc( file_size + 1 );
         if (data == NULL) {
-            D("load_file: could not allocate %ld bytes\n", file_size );
+            D("load_file: could not allocate %ld bytes", file_size );
             file_size = 0;
         } else {
             DWORD  out_bytes;
@@ -167,7 +167,7 @@
             if ( !ReadFile( file, data, file_size, &out_bytes, NULL ) ||
                  out_bytes != file_size )
             {
-                D("load_file: could not read %ld bytes from '%s'\n", file_size, fn);
+                D("load_file: could not read %ld bytes from '%s'", file_size, fn);
                 free(data);
                 data      = NULL;
                 file_size = 0;
@@ -229,7 +229,7 @@
     fd -= WIN32_FH_BASE;
 
     if (fd < 0 || fd >= WIN32_MAX_FHS) {
-        D( "_fh_from_int: invalid fd %d passed to %s\n", fd + WIN32_FH_BASE,
+        D( "_fh_from_int: invalid fd %d passed to %s", fd + WIN32_FH_BASE,
            func );
         errno = EBADF;
         return NULL;
@@ -238,7 +238,7 @@
     f = &_win32_fhs[fd];
 
     if (f->used == 0) {
-        D( "_fh_from_int: invalid fd %d passed to %s\n", fd + WIN32_FH_BASE,
+        D( "_fh_from_int: invalid fd %d passed to %s", fd + WIN32_FH_BASE,
            func );
         errno = EBADF;
         return NULL;
@@ -278,7 +278,7 @@
             goto Exit;
         }
     }
-    D( "_fh_alloc: no more free file descriptors\n" );
+    D( "_fh_alloc: no more free file descriptors" );
     errno = EMFILE;   // Too many open files
 Exit:
     if (f) {
@@ -349,7 +349,7 @@
     DWORD  read_bytes;
 
     if ( !ReadFile( f->fh_handle, buf, (DWORD)len, &read_bytes, NULL ) ) {
-        D( "adb_read: could not read %d bytes from %s\n", len, f->name );
+        D( "adb_read: could not read %d bytes from %s", len, f->name );
         errno = EIO;
         return -1;
     } else if (read_bytes < (DWORD)len) {
@@ -362,7 +362,7 @@
     DWORD  wrote_bytes;
 
     if ( !WriteFile( f->fh_handle, buf, (DWORD)len, &wrote_bytes, NULL ) ) {
-        D( "adb_file_write: could not write %d bytes from %s\n", len, f->name );
+        D( "adb_file_write: could not write %d bytes from %s", len, f->name );
         errno = EIO;
         return -1;
     } else if (wrote_bytes < (DWORD)len) {
@@ -422,7 +422,7 @@
             desiredAccess = GENERIC_READ | GENERIC_WRITE;
             break;
         default:
-            D("adb_open: invalid options (0x%0x)\n", options);
+            D("adb_open: invalid options (0x%0x)", options);
             errno = EINVAL;
             return -1;
     }
@@ -441,17 +441,17 @@
         D( "adb_open: could not open '%s': ", path );
         switch (err) {
             case ERROR_FILE_NOT_FOUND:
-                D( "file not found\n" );
+                D( "file not found" );
                 errno = ENOENT;
                 return -1;
 
             case ERROR_PATH_NOT_FOUND:
-                D( "path not found\n" );
+                D( "path not found" );
                 errno = ENOTDIR;
                 return -1;
 
             default:
-                D( "unknown error: %s\n",
+                D( "unknown error: %s",
                    SystemErrorCodeToString( err ).c_str() );
                 errno = ENOENT;
                 return -1;
@@ -459,7 +459,7 @@
     }
 
     snprintf( f->name, sizeof(f->name), "%d(%s)", _fh_to_int(f), path );
-    D( "adb_open: '%s' => fd %d\n", path, _fh_to_int(f) );
+    D( "adb_open: '%s' => fd %d", path, _fh_to_int(f) );
     return _fh_to_int(f);
 }
 
@@ -484,24 +484,24 @@
         D( "adb_creat: could not open '%s': ", path );
         switch (err) {
             case ERROR_FILE_NOT_FOUND:
-                D( "file not found\n" );
+                D( "file not found" );
                 errno = ENOENT;
                 return -1;
 
             case ERROR_PATH_NOT_FOUND:
-                D( "path not found\n" );
+                D( "path not found" );
                 errno = ENOTDIR;
                 return -1;
 
             default:
-                D( "unknown error: %s\n",
+                D( "unknown error: %s",
                    SystemErrorCodeToString( err ).c_str() );
                 errno = ENOENT;
                 return -1;
         }
     }
     snprintf( f->name, sizeof(f->name), "%d(%s)", _fh_to_int(f), path );
-    D( "adb_creat: '%s' => fd %d\n", path, _fh_to_int(f) );
+    D( "adb_creat: '%s' => fd %d", path, _fh_to_int(f) );
     return _fh_to_int(f);
 }
 
@@ -550,7 +550,7 @@
         return -1;
     }
 
-    D( "adb_close: %s\n", f->name);
+    D( "adb_close: %s", f->name);
     _fh_close(f);
     return 0;
 }
@@ -580,7 +580,7 @@
     case WSAEMFILE:      errno = EMFILE; break;
     default:
         errno = EINVAL;
-        D( "_socket_set_errno: mapping Windows error code %lu to errno %d\n",
+        D( "_socket_set_errno: mapping Windows error code %lu to errno %d",
            err, errno );
     }
 }
@@ -589,7 +589,7 @@
     f->fh_socket = INVALID_SOCKET;
     f->event     = WSACreateEvent();
     if (f->event == WSA_INVALID_EVENT) {
-        D("WSACreateEvent failed: %s\n",
+        D("WSACreateEvent failed: %s",
           SystemErrorCodeToString(WSAGetLastError()).c_str());
 
         // _event_socket_start assumes that this field is INVALID_HANDLE_VALUE
@@ -608,19 +608,19 @@
             // If the socket is not connected, this returns an error. We want to
             // minimize logging spam, so don't log these errors for now.
 #if 0
-            D("socket shutdown failed: %s\n",
+            D("socket shutdown failed: %s",
               SystemErrorCodeToString(WSAGetLastError()).c_str());
 #endif
         }
         if (closesocket(f->fh_socket) == SOCKET_ERROR) {
-            D("closesocket failed: %s\n",
+            D("closesocket failed: %s",
               SystemErrorCodeToString(WSAGetLastError()).c_str());
         }
         f->fh_socket = INVALID_SOCKET;
     }
     if (f->event != NULL) {
         if (!CloseHandle(f->event)) {
-            D("CloseHandle failed: %s\n",
+            D("CloseHandle failed: %s",
               SystemErrorCodeToString(GetLastError()).c_str());
         }
         f->event = NULL;
@@ -641,7 +641,7 @@
         // WSAEWOULDBLOCK is normal with a non-blocking socket, so don't trace
         // that to reduce spam and confusion.
         if (err != WSAEWOULDBLOCK) {
-            D("recv fd %d failed: %s\n", _fh_to_int(f),
+            D("recv fd %d failed: %s", _fh_to_int(f),
               SystemErrorCodeToString(err).c_str());
         }
         _socket_set_errno(err);
@@ -654,7 +654,7 @@
     int  result = send(f->fh_socket, reinterpret_cast<const char*>(buf), len, 0);
     if (result == SOCKET_ERROR) {
         const DWORD err = WSAGetLastError();
-        D("send fd %d failed: %s\n", _fh_to_int(f),
+        D("send fd %d failed: %s", _fh_to_int(f),
           SystemErrorCodeToString(err).c_str());
         _socket_set_errno(err);
         result = -1;
@@ -726,7 +726,7 @@
     if(s == INVALID_SOCKET) {
         *error = android::base::StringPrintf("cannot create socket: %s",
                 SystemErrorCodeToString(WSAGetLastError()).c_str());
-        D("%s\n", error->c_str());
+        D("%s", error->c_str());
         return -1;
     }
     f->fh_socket = s;
@@ -737,7 +737,7 @@
         *error = android::base::StringPrintf("cannot connect to %s:%u: %s",
                 inet_ntoa(addr.sin_addr), ntohs(addr.sin_port),
                 SystemErrorCodeToString(err).c_str());
-        D("could not connect to %s:%d: %s\n",
+        D("could not connect to %s:%d: %s",
           type != SOCK_STREAM ? "udp" : "tcp", port, error->c_str());
         return -1;
     }
@@ -745,7 +745,7 @@
     const int fd = _fh_to_int(f.get());
     snprintf( f->name, sizeof(f->name), "%d(lo-client:%s%d)", fd,
               type != SOCK_STREAM ? "udp:" : "", port );
-    D( "port %d type %s => fd %d\n", port, type != SOCK_STREAM ? "udp" : "tcp",
+    D( "port %d type %s => fd %d", port, type != SOCK_STREAM ? "udp" : "tcp",
        fd );
     f.release();
     return fd;
@@ -780,7 +780,7 @@
     if (s == INVALID_SOCKET) {
         *error = android::base::StringPrintf("cannot create socket: %s",
                 SystemErrorCodeToString(WSAGetLastError()).c_str());
-        D("%s\n", error->c_str());
+        D("%s", error->c_str());
         return -1;
     }
 
@@ -794,7 +794,7 @@
         *error = android::base::StringPrintf(
                 "cannot set socket option SO_EXCLUSIVEADDRUSE: %s",
                 SystemErrorCodeToString(WSAGetLastError()).c_str());
-        D("%s\n", error->c_str());
+        D("%s", error->c_str());
         return -1;
     }
 
@@ -804,7 +804,7 @@
         *error = android::base::StringPrintf("cannot bind to %s:%u: %s",
                 inet_ntoa(addr.sin_addr), ntohs(addr.sin_port),
                 SystemErrorCodeToString(err).c_str());
-        D("could not bind to %s:%d: %s\n",
+        D("could not bind to %s:%d: %s",
           type != SOCK_STREAM ? "udp" : "tcp", port, error->c_str());
         return -1;
     }
@@ -812,7 +812,7 @@
         if (listen(s, LISTEN_BACKLOG) == SOCKET_ERROR) {
             *error = android::base::StringPrintf("cannot listen on socket: %s",
                     SystemErrorCodeToString(WSAGetLastError()).c_str());
-            D("could not listen on %s:%d: %s\n",
+            D("could not listen on %s:%d: %s",
               type != SOCK_STREAM ? "udp" : "tcp", port, error->c_str());
             return -1;
         }
@@ -821,7 +821,7 @@
     snprintf( f->name, sizeof(f->name), "%d(%s-server:%s%d)", fd,
               interface_address == INADDR_LOOPBACK ? "lo" : "any",
               type != SOCK_STREAM ? "udp:" : "", port );
-    D( "port %d type %s => fd %d\n", port, type != SOCK_STREAM ? "udp" : "tcp",
+    D( "port %d type %s => fd %d", port, type != SOCK_STREAM ? "udp" : "tcp",
        fd );
     f.release();
     return fd;
@@ -865,7 +865,7 @@
         *error = android::base::StringPrintf(
                 "cannot resolve host '%s' and port %s: %s", host.c_str(),
                 port_str, SystemErrorCodeToString(WSAGetLastError()).c_str());
-        D("%s\n", error->c_str());
+        D("%s", error->c_str());
         return -1;
     }
     std::unique_ptr<struct addrinfo, decltype(freeaddrinfo)*>
@@ -880,7 +880,7 @@
     if(s == INVALID_SOCKET) {
         *error = android::base::StringPrintf("cannot create socket: %s",
                 SystemErrorCodeToString(WSAGetLastError()).c_str());
-        D("%s\n", error->c_str());
+        D("%s", error->c_str());
         return -1;
     }
     f->fh_socket = s;
@@ -892,7 +892,7 @@
         *error = android::base::StringPrintf("cannot connect to %s:%s: %s",
                 host.c_str(), port_str,
                 SystemErrorCodeToString(WSAGetLastError()).c_str());
-        D("could not connect to %s:%s:%s: %s\n",
+        D("could not connect to %s:%s:%s: %s",
           type != SOCK_STREAM ? "udp" : "tcp", host.c_str(), port_str,
           error->c_str());
         return -1;
@@ -901,7 +901,7 @@
     const int fd = _fh_to_int(f.get());
     snprintf( f->name, sizeof(f->name), "%d(net-client:%s%d)", fd,
               type != SOCK_STREAM ? "udp:" : "", port );
-    D( "host '%s' port %d type %s => fd %d\n", host.c_str(), port,
+    D( "host '%s' port %d type %s => fd %d", host.c_str(), port,
        type != SOCK_STREAM ? "udp" : "tcp", fd );
     f.release();
     return fd;
@@ -913,7 +913,7 @@
     FH   serverfh = _fh_from_int(serverfd, __func__);
 
     if ( !serverfh || serverfh->clazz != &_fh_socket_class ) {
-        D("adb_socket_accept: invalid fd %d\n", serverfd);
+        D("adb_socket_accept: invalid fd %d", serverfd);
         errno = EBADF;
         return -1;
     }
@@ -936,7 +936,7 @@
 
     const int fd = _fh_to_int(fh.get());
     snprintf( fh->name, sizeof(fh->name), "%d(accept:%s)", fd, serverfh->name );
-    D( "adb_socket_accept on fd %d returns fd %d\n", serverfd, fd );
+    D( "adb_socket_accept on fd %d returns fd %d", serverfd, fd );
     fh.release();
     return  fd;
 }
@@ -947,7 +947,7 @@
     FH   fh = _fh_from_int(fd, __func__);
 
     if ( !fh || fh->clazz != &_fh_socket_class ) {
-        D("adb_setsockopt: invalid fd %d\n", fd);
+        D("adb_setsockopt: invalid fd %d", fd);
         errno = EBADF;
         return -1;
     }
@@ -970,15 +970,15 @@
     FH   f = _fh_from_int(fd, __func__);
 
     if (!f || f->clazz != &_fh_socket_class) {
-        D("adb_shutdown: invalid fd %d\n", fd);
+        D("adb_shutdown: invalid fd %d", fd);
         errno = EBADF;
         return -1;
     }
 
-    D( "adb_shutdown: %s\n", f->name);
+    D( "adb_shutdown: %s", f->name);
     if (shutdown(f->fh_socket, SD_BOTH) == SOCKET_ERROR) {
         const DWORD err = WSAGetLastError();
-        D("socket shutdown fd %d failed: %s\n", fd,
+        D("socket shutdown fd %d failed: %s", fd,
           SystemErrorCodeToString(err).c_str());
         _socket_set_errno(err);
         return -1;
@@ -1073,7 +1073,7 @@
 static void
 bip_buffer_init( BipBuffer  buffer )
 {
-    D( "bit_buffer_init %p\n", buffer );
+    D( "bit_buffer_init %p", buffer );
     buffer->a_start   = 0;
     buffer->a_end     = 0;
     buffer->b_end     = 0;
@@ -1103,7 +1103,7 @@
 static void
 bip_buffer_done( BipBuffer  bip )
 {
-    BIPD(( "bip_buffer_done: %d->%d\n", bip->fdin, bip->fdout ));
+    BIPD(( "bip_buffer_done: %d->%d", bip->fdin, bip->fdout ));
     CloseHandle( bip->evt_read );
     CloseHandle( bip->evt_write );
     DeleteCriticalSection( &bip->lock );
@@ -1117,7 +1117,7 @@
     if (len <= 0)
         return 0;
 
-    BIPD(( "bip_buffer_write: enter %d->%d len %d\n", bip->fdin, bip->fdout, len ));
+    BIPD(( "bip_buffer_write: enter %d->%d len %d", bip->fdin, bip->fdout, len ));
     BIPDUMP( src, len );
 
     EnterCriticalSection( &bip->lock );
@@ -1133,7 +1133,7 @@
         /* spinlocking here is probably unfair, but let's live with it */
         ret = WaitForSingleObject( bip->evt_write, INFINITE );
         if (ret != WAIT_OBJECT_0) {  /* buffer probably closed */
-            D( "bip_buffer_write: error %d->%d WaitForSingleObject returned %d, error %ld\n", bip->fdin, bip->fdout, ret, GetLastError() );
+            D( "bip_buffer_write: error %d->%d WaitForSingleObject returned %d, error %ld", bip->fdin, bip->fdout, ret, GetLastError() );
             return 0;
         }
         if (bip->closed) {
@@ -1143,7 +1143,7 @@
         EnterCriticalSection( &bip->lock );
     }
 
-    BIPD(( "bip_buffer_write: exec %d->%d len %d\n", bip->fdin, bip->fdout, len ));
+    BIPD(( "bip_buffer_write: exec %d->%d len %d", bip->fdin, bip->fdout, len ));
 
     avail = BIP_BUFFER_SIZE - bip->a_end;
     if (avail > 0)
@@ -1191,7 +1191,7 @@
         SetEvent( bip->evt_read );
     }
 
-    BIPD(( "bip_buffer_write: exit %d->%d count %d (as=%d ae=%d be=%d cw=%d cr=%d\n",
+    BIPD(( "bip_buffer_write: exit %d->%d count %d (as=%d ae=%d be=%d cw=%d cr=%d",
             bip->fdin, bip->fdout, count, bip->a_start, bip->a_end, bip->b_end, bip->can_write, bip->can_read ));
     LeaveCriticalSection( &bip->lock );
 
@@ -1206,7 +1206,7 @@
     if (len <= 0)
         return 0;
 
-    BIPD(( "bip_buffer_read: enter %d->%d len %d\n", bip->fdin, bip->fdout, len ));
+    BIPD(( "bip_buffer_read: enter %d->%d len %d", bip->fdin, bip->fdout, len ));
 
     EnterCriticalSection( &bip->lock );
     while ( !bip->can_read )
@@ -1226,7 +1226,7 @@
 
         ret = WaitForSingleObject( bip->evt_read, INFINITE );
         if (ret != WAIT_OBJECT_0) { /* probably closed buffer */
-            D( "bip_buffer_read: error %d->%d WaitForSingleObject returned %d, error %ld\n", bip->fdin, bip->fdout, ret, GetLastError());
+            D( "bip_buffer_read: error %d->%d WaitForSingleObject returned %d, error %ld", bip->fdin, bip->fdout, ret, GetLastError());
             return 0;
         }
         if (bip->closed) {
@@ -1237,7 +1237,7 @@
 #endif
     }
 
-    BIPD(( "bip_buffer_read: exec %d->%d len %d\n", bip->fdin, bip->fdout, len ));
+    BIPD(( "bip_buffer_read: exec %d->%d len %d", bip->fdin, bip->fdout, len ));
 
     avail = bip->a_end - bip->a_start;
     assert( avail > 0 );  /* since can_read is TRUE */
@@ -1284,7 +1284,7 @@
     }
 
     BIPDUMP( (const unsigned char*)dst - count, count );
-    BIPD(( "bip_buffer_read: exit %d->%d count %d (as=%d ae=%d be=%d cw=%d cr=%d\n",
+    BIPD(( "bip_buffer_read: exit %d->%d count %d (as=%d ae=%d be=%d cw=%d cr=%d",
             bip->fdin, bip->fdout, count, bip->a_start, bip->a_end, bip->b_end, bip->can_write, bip->can_read ));
     LeaveCriticalSection( &bip->lock );
 
@@ -1397,7 +1397,7 @@
 
     pair = reinterpret_cast<SocketPair>(malloc(sizeof(*pair)));
     if (pair == NULL) {
-        D("adb_socketpair: not enough memory to allocate pipes\n" );
+        D("adb_socketpair: not enough memory to allocate pipes" );
         return -1;
     }
 
@@ -1419,7 +1419,7 @@
 
     snprintf( fa->name, sizeof(fa->name), "%d(pair:%d)", sv[0], sv[1] );
     snprintf( fb->name, sizeof(fb->name), "%d(pair:%d)", sv[1], sv[0] );
-    D( "adb_socketpair: returns (%d, %d)\n", sv[0], sv[1] );
+    D( "adb_socketpair: returns (%d, %d)", sv[0], sv[1] );
     fa.release();
     fb.release();
     return 0;
@@ -1575,7 +1575,7 @@
     EventHook   node;
 
     if (f == NULL)  /* invalid arg */ {
-        D("event_looper_hook: invalid fd=%d\n", fd);
+        D("event_looper_hook: invalid fd=%d", fd);
         return;
     }
 
@@ -1589,12 +1589,12 @@
 
     if ( (node->wanted & events) != events ) {
         /* this should update start/stop/check/peek */
-        D("event_looper_hook: call hook for %d (new=%x, old=%x)\n",
+        D("event_looper_hook: call hook for %d (new=%x, old=%x)",
            fd, node->wanted, events);
         f->clazz->_fh_hook( f, events & ~node->wanted, node );
         node->wanted |= events;
     } else {
-        D("event_looper_hook: ignoring events %x for %d wanted=%x)\n",
+        D("event_looper_hook: ignoring events %x for %d wanted=%x)",
            events, fd, node->wanted);
     }
 }
@@ -1609,7 +1609,7 @@
     if (node != NULL) {
         int  events2 = events & node->wanted;
         if ( events2 == 0 ) {
-            D( "event_looper_unhook: events %x not registered for fd %d\n", events, fd );
+            D( "event_looper_unhook: events %x not registered for fd %d", events, fd );
             return;
         }
         node->wanted &= ~events2;
@@ -1728,7 +1728,7 @@
     threads = (WaitForAllParam*)malloc((chunks + (remains ? 1 : 0)) *
                                         sizeof(WaitForAllParam));
     if (threads == NULL) {
-        D("Unable to allocate thread array for %d handles.\n", handles_count);
+        D("Unable to allocate thread array for %d handles.", handles_count);
         return (int)WAIT_FAILED;
     }
 
@@ -1736,7 +1736,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: %ld\n", GetLastError());
+        D("Unable to create main event. Error: %ld", GetLastError());
         free(threads);
         return (int)WAIT_FAILED;
     }
@@ -1769,7 +1769,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\n",
+            D("Unable to create a waiting thread %d of %d. errno=%d",
               chunk, chunks, errno);
             chunks = chunk;
             SetEvent(main_event);
@@ -1829,11 +1829,11 @@
         int  removes = events0 & ~events;
         int  adds    = events  & ~events0;
         if (removes) {
-            D("fdevent_update: remove %x from %d\n", removes, fde->fd);
+            D("fdevent_update: remove %x from %d", removes, fde->fd);
             event_looper_unhook( looper, fde->fd, removes );
         }
         if (adds) {
-            D("fdevent_update: add %x to %d\n", adds, fde->fd);
+            D("fdevent_update: add %x to %d", adds, fde->fd);
             event_looper_hook  ( looper, fde->fd, adds );
         }
     }
@@ -1865,7 +1865,7 @@
         for (hook = looper->hooks; hook; hook = hook->next)
         {
             if (hook->start && !hook->start(hook)) {
-                D( "fdevent_process: error when starting a hook\n" );
+                D( "fdevent_process: error when starting a hook" );
                 return;
             }
             if (hook->h != INVALID_HANDLE_VALUE) {
@@ -1883,7 +1883,7 @@
         }
 
         if (looper->htab_count == 0) {
-            D( "fdevent_process: nothing to wait for !!\n" );
+            D( "fdevent_process: nothing to wait for !!" );
             return;
         }
 
@@ -1891,17 +1891,17 @@
         {
             int   wait_ret;
 
-            D( "adb_win32: waiting for %d events\n", looper->htab_count );
+            D( "adb_win32: waiting for %d events", looper->htab_count );
             if (looper->htab_count > MAXIMUM_WAIT_OBJECTS) {
-                D("handle count %d exceeds MAXIMUM_WAIT_OBJECTS.\n", looper->htab_count);
+                D("handle count %d exceeds MAXIMUM_WAIT_OBJECTS.", looper->htab_count);
                 wait_ret = _wait_for_all(looper->htab, looper->htab_count);
             } else {
                 wait_ret = WaitForMultipleObjects( looper->htab_count, looper->htab, FALSE, INFINITE );
             }
             if (wait_ret == (int)WAIT_FAILED) {
-                D( "adb_win32: wait failed, error %ld\n", GetLastError() );
+                D( "adb_win32: wait failed, error %ld", GetLastError() );
             } else {
-                D( "adb_win32: got one (index %d)\n", wait_ret );
+                D( "adb_win32: got one (index %d)", wait_ret );
 
                 /* according to Cygwin, some objects like consoles wake up on "inappropriate" events
                  * like mouse movements. we need to filter these with the "check" function
@@ -1913,7 +1913,7 @@
                         if ( looper->htab[wait_ret] == hook->h       &&
                          (!hook->check || hook->check(hook)) )
                         {
-                            D( "adb_win32: signaling %s for %x\n", hook->fh->name, hook->ready );
+                            D( "adb_win32: signaling %s for %x", hook->fh->name, hook->ready );
                             event_hook_signal( hook );
                             gotone = 1;
                             break;
@@ -2205,14 +2205,14 @@
 
     hook->h = fh->event;
     if (hook->h == INVALID_HANDLE_VALUE) {
-        D( "_event_socket_start: no event for %s\n", fh->name );
+        D( "_event_socket_start: no event for %s", fh->name );
         return 0;
     }
 
     if ( flags != fh->mask ) {
-        D( "_event_socket_start: hooking %s for %x (flags %ld)\n", hook->fh->name, hook->wanted, flags );
+        D( "_event_socket_start: hooking %s for %x (flags %ld)", hook->fh->name, hook->wanted, flags );
         if ( WSAEventSelect( fh->fh_socket, hook->h, flags ) ) {
-            D( "_event_socket_start: WSAEventSelect() for %s failed, error %d\n", hook->fh->name, WSAGetLastError() );
+            D( "_event_socket_start: WSAEventSelect() for %s failed, error %d", hook->fh->name, WSAGetLastError() );
             CloseHandle( hook->h );
             hook->h = INVALID_HANDLE_VALUE;
             exit(1);
@@ -2241,7 +2241,7 @@
             ResetEvent( hook->h );
         }
     }
-    D( "_event_socket_check %s returns %d\n", fh->name, result );
+    D( "_event_socket_check %s returns %d", fh->name, result );
     return  result;
 }
 
@@ -2305,10 +2305,10 @@
         hook->h = wbip->evt_write;
 
     else {
-        D("_event_socketpair_start: can't handle FDE_READ+FDE_WRITE\n" );
+        D("_event_socketpair_start: can't handle FDE_READ+FDE_WRITE" );
         return 0;
     }
-    D( "_event_socketpair_start: hook %s for %x wanted=%x\n",
+    D( "_event_socketpair_start: hook %s for %x wanted=%x",
        hook->fh->name, _fh_to_int(fh), hook->wanted);
     return 1;
 }
@@ -3118,7 +3118,7 @@
             //
             // Consume the input and 'continue' to cause us to get a new key
             // event.
-            D("_console_read: unknown virtual key code: %d, enhanced: %s\n",
+            D("_console_read: unknown virtual key code: %d, enhanced: %s",
                 vk, _is_enhanced_key(control_key_state) ? "true" : "false");
             key_event->wRepeatCount = 0;
             continue;
@@ -3192,7 +3192,7 @@
         if (!SetConsoleMode(in, _old_console_mode & ~(ENABLE_PROCESSED_INPUT |
             ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT))) {
             // This really should not fail.
-            D("stdin_raw_init: SetConsoleMode() failed: %s\n",
+            D("stdin_raw_init: SetConsoleMode() failed: %s",
               SystemErrorCodeToString(GetLastError()).c_str());
         }
 
@@ -3214,7 +3214,7 @@
 
             if (!SetConsoleMode(in, _old_console_mode)) {
                 // This really should not fail.
-                D("stdin_raw_restore: SetConsoleMode() failed: %s\n",
+                D("stdin_raw_restore: SetConsoleMode() failed: %s",
                   SystemErrorCodeToString(GetLastError()).c_str());
             }
         }
diff --git a/adb/transport.cpp b/adb/transport.cpp
index 43691dc..e97c479 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -94,7 +94,7 @@
             len -= r;
             p += r;
         } else {
-            D("%s: read_packet (fd=%d), error ret=%d: %s\n", name, fd, r, strerror(errno));
+            D("%s: read_packet (fd=%d), error ret=%d: %s", name, fd, r, strerror(errno));
             return -1;
         }
     }
@@ -124,7 +124,7 @@
             len -= r;
             p += r;
         } else {
-            D("%s: write_packet (fd=%d) error ret=%d: %s\n", name, fd, r, strerror(errno));
+            D("%s: write_packet (fd=%d) error ret=%d: %s", name, fd, r, strerror(errno));
             return -1;
         }
     }
@@ -134,11 +134,11 @@
 static void transport_socket_events(int fd, unsigned events, void *_t)
 {
     atransport *t = reinterpret_cast<atransport*>(_t);
-    D("transport_socket_events(fd=%d, events=%04x,...)\n", fd, events);
+    D("transport_socket_events(fd=%d, events=%04x,...)", fd, events);
     if(events & FDE_READ){
         apacket *p = 0;
         if(read_packet(fd, t->serial, &p)){
-            D("%s: failed to read packet from transport socket on fd %d\n", t->serial, fd);
+            D("%s: failed to read packet from transport socket on fd %d", t->serial, fd);
         } else {
             handle_packet(p, (atransport *) _t);
         }
@@ -164,7 +164,7 @@
     print_packet("send", p);
 
     if (t == NULL) {
-        D("Transport is null \n");
+        D("Transport is null");
         // Zap errno because print_packet() and other stuff have errno effect.
         errno = 0;
         fatal_errno("Transport is null");
@@ -175,26 +175,27 @@
     }
 }
 
-/* The transport is opened by transport_register_func before
-** the input and output threads are started.
-**
-** The output thread issues a SYNC(1, token) message to let
-** the input thread know to start things up.  In the event
-** of transport IO failure, the output thread will post a
-** SYNC(0,0) message to ensure shutdown.
-**
-** The transport will not actually be closed until both
-** threads exit, but the input thread will kick the transport
-** on its way out to disconnect the underlying device.
-*/
-
-static void *output_thread(void *_t)
+// The transport is opened by transport_register_func before
+// the read_transport and write_transport threads are started.
+//
+// The read_transport thread issues a SYNC(1, token) message to let
+// the write_transport thread know to start things up.  In the event
+// of transport IO failure, the read_transport thread will post a
+// SYNC(0,0) message to ensure shutdown.
+//
+// The transport will not actually be closed until both threads exit, but the threads
+// will kick the transport on their way out to disconnect the underlying device.
+//
+// read_transport thread reads data from a transport (representing a usb/tcp connection),
+// and makes the main thread call handle_packet().
+static void *read_transport_thread(void *_t)
 {
     atransport *t = reinterpret_cast<atransport*>(_t);
     apacket *p;
 
-    adb_thread_setname(android::base::StringPrintf("->%s", t->serial));
-    D("%s: starting transport output thread on fd %d, SYNC online (%d)\n",
+    adb_thread_setname(android::base::StringPrintf("<-%s",
+                                                   (t->serial != nullptr ? t->serial : "transport")));
+    D("%s: starting read_transport thread on fd %d, SYNC online (%d)",
        t->serial, t->fd, t->sync_token + 1);
     p = get_apacket();
     p->msg.command = A_SYNC;
@@ -203,30 +204,30 @@
     p->msg.magic = A_SYNC ^ 0xffffffff;
     if(write_packet(t->fd, t->serial, &p)) {
         put_apacket(p);
-        D("%s: failed to write SYNC packet\n", t->serial);
+        D("%s: failed to write SYNC packet", t->serial);
         goto oops;
     }
 
-    D("%s: data pump started\n", t->serial);
+    D("%s: data pump started", t->serial);
     for(;;) {
         p = get_apacket();
 
         if(t->read_from_remote(p, t) == 0){
-            D("%s: received remote packet, sending to transport\n",
+            D("%s: received remote packet, sending to transport",
               t->serial);
             if(write_packet(t->fd, t->serial, &p)){
                 put_apacket(p);
-                D("%s: failed to write apacket to transport\n", t->serial);
+                D("%s: failed to write apacket to transport", t->serial);
                 goto oops;
             }
         } else {
-            D("%s: remote read failed for transport\n", t->serial);
+            D("%s: remote read failed for transport", t->serial);
             put_apacket(p);
             break;
         }
     }
 
-    D("%s: SYNC offline for transport\n", t->serial);
+    D("%s: SYNC offline for transport", t->serial);
     p = get_apacket();
     p->msg.command = A_SYNC;
     p->msg.arg0 = 0;
@@ -234,59 +235,62 @@
     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\n", t->serial);
+        D("%s: failed to write SYNC apacket to transport", t->serial);
     }
 
 oops:
-    D("%s: transport output thread is exiting\n", t->serial);
+    D("%s: read_transport thread is exiting", t->serial);
     kick_transport(t);
     transport_unref(t);
     return 0;
 }
 
-static void *input_thread(void *_t)
+// write_transport thread gets packets sent by the main thread (through send_packet()),
+// and writes to a transport (representing a usb/tcp connection).
+static void *write_transport_thread(void *_t)
 {
     atransport *t = reinterpret_cast<atransport*>(_t);
     apacket *p;
     int active = 0;
 
-    adb_thread_setname(android::base::StringPrintf("<-%s", t->serial));
-    D("%s: starting transport input thread, reading from fd %d\n",
+    adb_thread_setname(android::base::StringPrintf("->%s",
+                                                   (t->serial != nullptr ? t->serial : "transport")));
+    D("%s: starting write_transport thread, reading from fd %d",
        t->serial, t->fd);
 
     for(;;){
         if(read_packet(t->fd, t->serial, &p)) {
-            D("%s: failed to read apacket from transport on fd %d\n",
+            D("%s: failed to read apacket from transport on fd %d",
                t->serial, t->fd );
             break;
         }
         if(p->msg.command == A_SYNC){
             if(p->msg.arg0 == 0) {
-                D("%s: transport SYNC offline\n", t->serial);
+                D("%s: transport SYNC offline", t->serial);
                 put_apacket(p);
                 break;
             } else {
                 if(p->msg.arg1 == t->sync_token) {
-                    D("%s: transport SYNC online\n", t->serial);
+                    D("%s: transport SYNC online", t->serial);
                     active = 1;
                 } else {
-                    D("%s: transport ignoring SYNC %d != %d\n",
+                    D("%s: transport ignoring SYNC %d != %d",
                       t->serial, p->msg.arg1, t->sync_token);
                 }
             }
         } else {
             if(active) {
-                D("%s: transport got packet, sending to remote\n", t->serial);
+                D("%s: transport got packet, sending to remote", t->serial);
                 t->write_to_remote(p, t);
             } else {
-                D("%s: transport ignoring packet while offline\n", t->serial);
+                D("%s: transport ignoring packet while offline", t->serial);
             }
         }
 
         put_apacket(p);
     }
 
-    D("%s: transport input thread is exiting, fd %d\n", t->serial, t->fd);
+    D("%s: write_transport thread is exiting, fd %d", t->serial, t->fd);
     kick_transport(t);
     transport_unref(t);
     return 0;
@@ -351,7 +355,7 @@
     device_tracker*  tracker = (device_tracker*) socket;
     asocket*         peer    = socket->peer;
 
-    D( "device tracker %p removed\n", tracker);
+    D( "device tracker %p removed", tracker);
     if (peer) {
         peer->peer = NULL;
         peer->close(peer);
@@ -398,7 +402,7 @@
     device_tracker* tracker = reinterpret_cast<device_tracker*>(calloc(1, sizeof(*tracker)));
     if (tracker == nullptr) fatal("cannot allocate device tracker");
 
-    D( "device tracker %p created\n", tracker);
+    D( "device tracker %p created", tracker);
 
     tracker->socket.enqueue = device_tracker_enqueue;
     tracker->socket.ready   = device_tracker_ready;
@@ -452,7 +456,7 @@
             len -= r;
             p   += r;
         } else {
-            D("transport_read_action: on fd %d: %s\n", fd, strerror(errno));
+            D("transport_read_action: on fd %d: %s", fd, strerror(errno));
             return -1;
         }
     }
@@ -472,7 +476,7 @@
             len -= r;
             p   += r;
         } else {
-            D("transport_write_action: on fd %d: %s\n", fd, strerror(errno));
+            D("transport_write_action: on fd %d: %s", fd, strerror(errno));
             return -1;
         }
     }
@@ -496,7 +500,7 @@
     t = m.transport;
 
     if (m.action == 0) {
-        D("transport: %s removing and free'ing %d\n", t->serial, t->transport_socket);
+        D("transport: %s removing and free'ing %d", t->serial, t->transport_socket);
 
             /* IMPORTANT: the remove closes one half of the
             ** socket pair.  The close closes the other half.
@@ -534,7 +538,7 @@
             fatal_errno("cannot open transport socketpair");
         }
 
-        D("transport: %s socketpair: (%d,%d) starting\n", t->serial, s[0], s[1]);
+        D("transport: %s socketpair: (%d,%d) starting", t->serial, s[0], s[1]);
 
         t->transport_socket = s[0];
         t->fd = s[1];
@@ -546,12 +550,12 @@
 
         fdevent_set(&(t->transport_fde), FDE_READ);
 
-        if (!adb_thread_create(input_thread, t)) {
-            fatal_errno("cannot create input thread");
+        if (!adb_thread_create(write_transport_thread, t)) {
+            fatal_errno("cannot create write_transport thread");
         }
 
-        if (!adb_thread_create(output_thread, t)) {
-            fatal_errno("cannot create output thread");
+        if (!adb_thread_create(read_transport_thread, t)) {
+            fatal_errno("cannot create read_transport thread");
         }
     }
 
@@ -570,7 +574,7 @@
     if(adb_socketpair(s)){
         fatal_errno("cannot open transport registration socketpair");
     }
-    D("socketpair: (%d,%d)\n", s[0], s[1]);
+    D("socketpair: (%d,%d)", s[0], s[1]);
 
     transport_registration_send = s[0];
     transport_registration_recv = s[1];
@@ -589,7 +593,7 @@
     tmsg m;
     m.transport = transport;
     m.action = 1;
-    D("transport: %s registered\n", transport->serial);
+    D("transport: %s registered", transport->serial);
     if(transport_write_action(transport_registration_send, &m)) {
         fatal_errno("cannot write transport registration socket\n");
     }
@@ -600,7 +604,7 @@
     tmsg m;
     m.transport = transport;
     m.action = 0;
-    D("transport: %s removed\n", transport->serial);
+    D("transport: %s removed", transport->serial);
     if(transport_write_action(transport_registration_send, &m)) {
         fatal_errno("cannot write transport registration socket\n");
     }
@@ -613,12 +617,12 @@
     CHECK_GT(t->ref_count, 0u);
     t->ref_count--;
     if (t->ref_count == 0) {
-        D("transport: %s unref (kicking and closing)\n", t->serial);
+        D("transport: %s unref (kicking and closing)", t->serial);
         kick_transport_locked(t);
         t->close(t);
         remove_transport(t);
     } else {
-        D("transport: %s unref (count=%zu)\n", t->serial, t->ref_count);
+        D("transport: %s unref (count=%zu)", t->serial, t->ref_count);
     }
     adb_mutex_unlock(&transport_lock);
 }
@@ -885,7 +889,7 @@
         serial = buf;
     }
 
-    D("transport: %s init'ing for socket %d, on port %d\n", serial, s, port);
+    D("transport: %s init'ing for socket %d, on port %d", serial, s, port);
     if (init_socket_transport(t, s, port, local) < 0) {
         delete t;
         return -1;
@@ -937,9 +941,9 @@
     for (auto& t : transport_list) {
         // TCP/IP devices have adb_port == 0.
         if (t->type == kTransportLocal && t->adb_port == 0) {
-            // Kicking breaks the output thread of this transport out of any read, then
-            // the output thread will notify the main thread to make this transport
-            // offline. Then the main thread will notify the input thread to exit.
+            // Kicking breaks the read_transport thread of this transport out of any read, then
+            // the read_transport thread will notify the main thread to make this transport
+            // offline. Then the main thread will notify the write_transport thread to exit.
             // Finally, this transport will be closed and freed in the main thread.
             kick_transport_locked(t);
         }
@@ -953,7 +957,7 @@
                             const char* devpath, unsigned writeable) {
     atransport* t = new atransport();
 
-    D("transport: %p init'ing for usb_handle %p (sn='%s')\n", t, usb,
+    D("transport: %p init'ing for usb_handle %p (sn='%s')", t, usb,
       serial ? serial : "");
     init_usb_transport(t, usb, (writeable ? kCsOffline : kCsNoPerm));
     if(serial) {
@@ -986,12 +990,12 @@
 int check_header(apacket *p, atransport *t)
 {
     if(p->msg.magic != (p->msg.command ^ 0xffffffff)) {
-        D("check_header(): invalid magic\n");
+        D("check_header(): invalid magic");
         return -1;
     }
 
     if(p->msg.data_length > t->get_max_payload()) {
-        D("check_header(): %u > atransport::max_payload = %zu\n",
+        D("check_header(): %u > atransport::max_payload = %zu",
             p->msg.data_length, t->get_max_payload());
         return -1;
     }
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index 6821cfc..0c4315a 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -51,22 +51,22 @@
 static int remote_read(apacket *p, atransport *t)
 {
     if(!ReadFdExactly(t->sfd, &p->msg, sizeof(amessage))){
-        D("remote local: read terminated (message)\n");
+        D("remote local: read terminated (message)");
         return -1;
     }
 
     if(check_header(p, t)) {
-        D("bad header: terminated (data)\n");
+        D("bad header: terminated (data)");
         return -1;
     }
 
     if(!ReadFdExactly(t->sfd, p->data, p->msg.data_length)){
-        D("remote local: terminated (data)\n");
+        D("remote local: terminated (data)");
         return -1;
     }
 
     if(check_data(p)) {
-        D("bad data: terminated (data)\n");
+        D("bad data: terminated (data)");
         return -1;
     }
 
@@ -78,7 +78,7 @@
     int   length = p->msg.data_length;
 
     if(!WriteFdExactly(t->sfd, &p->msg, sizeof(amessage) + length)) {
-        D("remote local: write terminated\n");
+        D("remote local: write terminated");
         return -1;
     }
 
@@ -108,7 +108,7 @@
     }
 
     if (fd >= 0) {
-        D("client: connected on remote on fd %d\n", fd);
+        D("client: connected on remote on fd %d", fd);
         close_on_exec(fd);
         disable_tcp_nagle(fd);
         std::string serial = android::base::StringPrintf("emulator-%d", console_port);
@@ -124,7 +124,7 @@
 static void *client_socket_thread(void *x)
 {
     adb_thread_setname("client_socket_thread");
-    D("transport: client_socket_thread() starting\n");
+    D("transport: client_socket_thread() starting");
     while (true) {
         int port = DEFAULT_ADB_LOCAL_TRANSPORT_PORT;
         int count = ADB_LOCAL_TRANSPORT_MAX;
@@ -148,14 +148,14 @@
     int port = (int) (uintptr_t) arg;
 
     adb_thread_setname("server socket");
-    D("transport: server_socket_thread() starting\n");
+    D("transport: server_socket_thread() starting");
     serverfd = -1;
     for(;;) {
         if(serverfd == -1) {
             std::string error;
             serverfd = network_inaddr_any_server(port, SOCK_STREAM, &error);
             if(serverfd < 0) {
-                D("server: cannot bind socket yet: %s\n", error.c_str());
+                D("server: cannot bind socket yet: %s", error.c_str());
                 adb_sleep_ms(1000);
                 continue;
             }
@@ -163,16 +163,16 @@
         }
 
         alen = sizeof(addr);
-        D("server: trying to get new connection from %d\n", port);
+        D("server: trying to get new connection from %d", port);
         fd = adb_socket_accept(serverfd, &addr, &alen);
         if(fd >= 0) {
-            D("server: new connection on fd %d\n", fd);
+            D("server: new connection on fd %d", fd);
             close_on_exec(fd);
             disable_tcp_nagle(fd);
             register_socket_transport(fd, "host", port, 1);
         }
     }
-    D("transport: server_socket_thread() exiting\n");
+    D("transport: server_socket_thread() exiting");
     return 0;
 }
 
@@ -234,7 +234,7 @@
     char con_name[32];
 
     adb_thread_setname("qemu socket");
-    D("transport: qemu_socket_thread() starting\n");
+    D("transport: qemu_socket_thread() starting");
 
     /* adb QEMUD service connection request. */
     snprintf(con_name, sizeof(con_name), "qemud:adb:%d", port);
@@ -244,7 +244,7 @@
     if (fd < 0) {
         /* This could be an older version of the emulator, that doesn't
          * implement adb QEMUD service. Fall back to the old TCP way. */
-        D("adb service is not available. Falling back to TCP socket.\n");
+        D("adb service is not available. Falling back to TCP socket.");
         adb_thread_create(server_socket_thread, arg);
         return 0;
     }
@@ -261,7 +261,7 @@
              * or 'ko' on failure. */
             res = adb_read(fd, tmp, sizeof(tmp));
             if (res != 2 || memcmp(tmp, _ok_resp, 2)) {
-                D("Accepting ADB host connection has failed.\n");
+                D("Accepting ADB host connection has failed.");
                 adb_close(fd);
             } else {
                 /* Host is connected. Register the transport, and start the
@@ -273,15 +273,15 @@
             /* Prepare for accepting of the next ADB host connection. */
             fd = qemu_pipe_open(con_name);
             if (fd < 0) {
-                D("adb service become unavailable.\n");
+                D("adb service become unavailable.");
                 return 0;
             }
         } else {
-            D("Unable to send the '%s' request to ADB service.\n", _accept_req);
+            D("Unable to send the '%s' request to ADB service.", _accept_req);
             return 0;
         }
     }
-    D("transport: qemu_socket_thread() exiting\n");
+    D("transport: qemu_socket_thread() exiting");
     return 0;
 }
 #endif  // !ADB_HOST
@@ -309,7 +309,7 @@
     debug_name = "server";
 #endif // !ADB_HOST
 
-    D("transport: local %s init\n", debug_name);
+    D("transport: local %s init", debug_name);
     if (!adb_thread_create(func, (void *) (uintptr_t) port)) {
         fatal_errno("cannot create local socket %s thread", debug_name);
     }
@@ -410,12 +410,12 @@
                     find_emulator_transport_by_adb_port_locked(adb_port);
             int index = get_available_local_transport_index_locked();
             if (existing_transport != NULL) {
-                D("local transport for port %d already registered (%p)?\n",
+                D("local transport for port %d already registered (%p)?",
                 adb_port, existing_transport);
                 fail = -1;
             } else if (index < 0) {
                 // Too many emulators.
-                D("cannot register more emulators. Maximum is %d\n",
+                D("cannot register more emulators. Maximum is %d",
                         ADB_LOCAL_TRANSPORT_MAX);
                 fail = -1;
             } else {
diff --git a/adb/transport_usb.cpp b/adb/transport_usb.cpp
index 96ccdad..b520607 100644
--- a/adb/transport_usb.cpp
+++ b/adb/transport_usb.cpp
@@ -28,24 +28,24 @@
 static int remote_read(apacket *p, atransport *t)
 {
     if(usb_read(t->usb, &p->msg, sizeof(amessage))){
-        D("remote usb: read terminated (message)\n");
+        D("remote usb: read terminated (message)");
         return -1;
     }
 
     if(check_header(p, t)) {
-        D("remote usb: check_header failed\n");
+        D("remote usb: check_header failed");
         return -1;
     }
 
     if(p->msg.data_length) {
         if(usb_read(t->usb, p->data, p->msg.data_length)){
-            D("remote usb: terminated (data)\n");
+            D("remote usb: terminated (data)");
             return -1;
         }
     }
 
     if(check_data(p)) {
-        D("remote usb: check_data failed\n");
+        D("remote usb: check_data failed");
         return -1;
     }
 
@@ -57,12 +57,12 @@
     unsigned size = p->msg.data_length;
 
     if(usb_write(t->usb, &p->msg, sizeof(amessage))) {
-        D("remote usb: 1 - write terminated\n");
+        D("remote usb: 1 - write terminated");
         return -1;
     }
     if(p->msg.data_length == 0) return 0;
     if(usb_write(t->usb, &p->data, size)) {
-        D("remote usb: 2 - write terminated\n");
+        D("remote usb: 2 - write terminated");
         return -1;
     }
 
@@ -82,7 +82,7 @@
 
 void init_usb_transport(atransport *t, usb_handle *h, ConnectionState state)
 {
-    D("transport: usb\n");
+    D("transport: usb");
     t->close = remote_close;
     t->kick = remote_kick;
     t->read_from_remote = remote_read;
diff --git a/adb/usb_linux.cpp b/adb/usb_linux.cpp
index 6ccc8e2..f2b9820 100644
--- a/adb/usb_linux.cpp
+++ b/adb/usb_linux.cpp
@@ -159,7 +159,7 @@
 
                 // should have device and configuration descriptors, and atleast two endpoints
             if (desclength < USB_DT_DEVICE_SIZE + USB_DT_CONFIG_SIZE) {
-                D("desclength %zu is too small\n", desclength);
+                D("desclength %zu is too small", desclength);
                 unix_close(fd);
                 continue;
             }
@@ -180,7 +180,7 @@
             config = (struct usb_config_descriptor *)bufptr;
             bufptr += USB_DT_CONFIG_SIZE;
             if (config->bLength != USB_DT_CONFIG_SIZE || config->bDescriptorType != USB_DT_CONFIG) {
-                D("usb_config_descriptor not found\n");
+                D("usb_config_descriptor not found");
                 unix_close(fd);
                 continue;
             }
@@ -195,7 +195,7 @@
                     bufptr += length;
 
                     if (length != USB_DT_INTERFACE_SIZE) {
-                        D("interface descriptor has wrong size\n");
+                        D("interface descriptor has wrong size");
                         break;
                     }
 
@@ -237,14 +237,14 @@
                             ep1->bDescriptorType != USB_DT_ENDPOINT ||
                             ep2->bLength != USB_DT_ENDPOINT_SIZE ||
                             ep2->bDescriptorType != USB_DT_ENDPOINT) {
-                            D("endpoints not found\n");
+                            D("endpoints not found");
                             break;
                         }
 
                             // both endpoints should be bulk
                         if (ep1->bmAttributes != USB_ENDPOINT_XFER_BULK ||
                             ep2->bmAttributes != USB_ENDPOINT_XFER_BULK) {
-                            D("bulk endpoints not found\n");
+                            D("bulk endpoints not found");
                             continue;
                         }
                             /* aproto 01 needs 0 termination */
@@ -295,7 +295,7 @@
 
 static int usb_bulk_write(usb_handle* h, const void* data, int len) {
     std::unique_lock<std::mutex> lock(h->mutex);
-    D("++ usb_bulk_write ++\n");
+    D("++ usb_bulk_write ++");
 
     usbdevfs_urb* urb = &h->urb_out;
     memset(urb, 0, sizeof(*urb));
@@ -334,7 +334,7 @@
 
 static int usb_bulk_read(usb_handle* h, void* data, int len) {
     std::unique_lock<std::mutex> lock(h->mutex);
-    D("++ usb_bulk_read ++\n");
+    D("++ usb_bulk_read ++");
 
     usbdevfs_urb* urb = &h->urb_in;
     memset(urb, 0, sizeof(*urb));
@@ -355,7 +355,7 @@
 
     h->urb_in_busy = true;
     while (true) {
-        D("[ reap urb - wait ]\n");
+        D("[ reap urb - wait ]");
         h->reaper_thread = pthread_self();
         int fd = h->fd;
         lock.unlock();
@@ -375,14 +375,14 @@
             if (saved_errno == EINTR) {
                 continue;
             }
-            D("[ reap urb - error ]\n");
+            D("[ reap urb - error ]");
             errno = saved_errno;
             return -1;
         }
-        D("[ urb @%p status = %d, actual = %d ]\n", out, out->status, out->actual_length);
+        D("[ urb @%p status = %d, actual = %d ]", out, out->status, out->actual_length);
 
         if (out == &h->urb_in) {
-            D("[ reap urb - IN complete ]\n");
+            D("[ reap urb - IN complete ]");
             h->urb_in_busy = false;
             if (urb->status != 0) {
                 errno = -urb->status;
@@ -391,7 +391,7 @@
             return urb->actual_length;
         }
         if (out == &h->urb_out) {
-            D("[ reap urb - OUT compelete ]\n");
+            D("[ reap urb - OUT compelete ]");
             h->urb_out_busy = false;
             h->cv.notify_all();
         }
@@ -401,12 +401,12 @@
 
 int usb_write(usb_handle *h, const void *_data, int len)
 {
-    D("++ usb_write ++\n");
+    D("++ usb_write ++");
 
     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));
+        D("ERROR: n = %d, errno = %d (%s)", n, errno, strerror(errno));
         return -1;
     }
 
@@ -416,7 +416,7 @@
         return usb_bulk_write(h, _data, 0);
     }
 
-    D("-- usb_write --\n");
+    D("-- usb_write --");
     return 0;
 }
 
@@ -425,23 +425,23 @@
     unsigned char *data = (unsigned char*) _data;
     int n;
 
-    D("++ usb_read ++\n");
+    D("++ usb_read ++");
     while(len > 0) {
         int xfer = len;
 
-        D("[ usb read %d fd = %d], path=%s\n", xfer, h->fd, h->path.c_str());
+        D("[ usb read %d fd = %d], path=%s", xfer, h->fd, h->path.c_str());
         n = usb_bulk_read(h, data, xfer);
-        D("[ usb read %d ] = %d, path=%s\n", xfer, n, h->path.c_str());
+        D("[ usb read %d ] = %d, path=%s", xfer, n, h->path.c_str());
         if(n != xfer) {
             if((errno == ETIMEDOUT) && (h->fd != -1)) {
-                D("[ timeout ]\n");
+                D("[ timeout ]");
                 if(n > 0){
                     data += n;
                     len -= n;
                 }
                 continue;
             }
-            D("ERROR: n = %d, errno = %d (%s)\n",
+            D("ERROR: n = %d, errno = %d (%s)",
                 n, errno, strerror(errno));
             return -1;
         }
@@ -450,13 +450,13 @@
         data += xfer;
     }
 
-    D("-- usb_read --\n");
+    D("-- usb_read --");
     return 0;
 }
 
 void usb_kick(usb_handle* h) {
     std::lock_guard<std::mutex> lock(h->mutex);
-    D("[ kicking %p (fd = %d) ]\n", h, h->fd);
+    D("[ kicking %p (fd = %d) ]", h, h->fd);
     if (!h->dead) {
         h->dead = true;
 
@@ -491,7 +491,7 @@
     std::lock_guard<std::mutex> lock(g_usb_handles_mutex);
     g_usb_handles.remove(h);
 
-    D("-- usb close %p (fd = %d) --\n", h, h->fd);
+    D("-- usb close %p (fd = %d) --", h, h->fd);
 
     delete h;
 
@@ -518,7 +518,7 @@
         }
     }
 
-    D("[ usb located new device %s (%d/%d/%d) ]\n", dev_name, ep_in, ep_out, interface);
+    D("[ usb located new device %s (%d/%d/%d) ]", dev_name, ep_in, ep_out, interface);
     std::unique_ptr<usb_handle> usb(new usb_handle);
     usb->path = dev_name;
     usb->ep_in = ep_in;
@@ -533,18 +533,18 @@
         // Opening RW failed, so see if we have RO access.
         usb->fd = unix_open(usb->path.c_str(), O_RDONLY | O_CLOEXEC);
         if (usb->fd == -1) {
-            D("[ usb open %s failed: %s]\n", usb->path.c_str(), strerror(errno));
+            D("[ usb open %s failed: %s]", usb->path.c_str(), strerror(errno));
             return;
         }
         usb->writeable = 0;
     }
 
-    D("[ usb opened %s%s, fd=%d]\n",
+    D("[ usb opened %s%s, fd=%d]",
       usb->path.c_str(), (usb->writeable ? "" : " (read-only)"), usb->fd);
 
     if (usb->writeable) {
         if (ioctl(usb->fd, USBDEVFS_CLAIMINTERFACE, &interface) != 0) {
-            D("[ usb ioctl(%d, USBDEVFS_CLAIMINTERFACE) failed: %s]\n", usb->fd, strerror(errno));
+            D("[ usb ioctl(%d, USBDEVFS_CLAIMINTERFACE) failed: %s]", usb->fd, strerror(errno));
             return;
         }
     }
@@ -554,7 +554,7 @@
         "/sys/bus/usb/devices/%s/serial", dev_path + 4);
     std::string serial;
     if (!android::base::ReadFileToString(serial_path, &serial)) {
-        D("[ usb read %s failed: %s ]\n", serial_path.c_str(), strerror(errno));
+        D("[ usb read %s failed: %s ]", serial_path.c_str(), strerror(errno));
         // We don't actually want to treat an unknown serial as an error because
         // devices aren't able to communicate a serial number in early bringup.
         // http://b/20883914
@@ -573,7 +573,7 @@
 
 static void* device_poll_thread(void* unused) {
     adb_thread_setname("device poll");
-    D("Created device thread\n");
+    D("Created device thread");
     while (true) {
         // TODO: Use inotify.
         find_usb_device("/dev/bus/usb", register_device);
@@ -592,6 +592,6 @@
     sigaction(SIGALRM, &actions, nullptr);
 
     if (!adb_thread_create(device_poll_thread, nullptr)) {
-        fatal_errno("cannot create input thread");
+        fatal_errno("cannot create device_poll thread");
     }
 }
diff --git a/adb/usb_linux_client.cpp b/adb/usb_linux_client.cpp
index e1d7594..c7a9b58 100644
--- a/adb/usb_linux_client.cpp
+++ b/adb/usb_linux_client.cpp
@@ -218,7 +218,7 @@
             adb_cond_wait(&usb->notify, &usb->lock);
         adb_mutex_unlock(&usb->lock);
 
-        D("[ usb_thread - opening device ]\n");
+        D("[ usb_thread - opening device ]");
         do {
             /* XXX use inotify? */
             fd = unix_open("/dev/android_adb", O_RDWR);
@@ -230,12 +230,12 @@
                 adb_sleep_ms(1000);
             }
         } while (fd < 0);
-        D("[ opening device succeeded ]\n");
+        D("[ opening device succeeded ]");
 
         close_on_exec(fd);
         usb->fd = fd;
 
-        D("[ usb_thread - registering device ]\n");
+        D("[ usb_thread - registering device ]");
         register_usb_transport(usb, 0, 0, 1);
     }
 
@@ -247,20 +247,20 @@
 {
     int n;
 
-    D("about to write (fd=%d, len=%d)\n", h->fd, len);
+    D("about to write (fd=%d, len=%d)", h->fd, len);
     n = unix_write(h->fd, data, len);
     if(n != len) {
-        D("ERROR: fd = %d, n = %d, errno = %d (%s)\n",
+        D("ERROR: fd = %d, n = %d, errno = %d (%s)",
             h->fd, n, errno, strerror(errno));
         return -1;
     }
-    D("[ done fd=%d ]\n", h->fd);
+    D("[ done fd=%d ]", h->fd);
     return 0;
 }
 
 static int usb_adb_read(usb_handle *h, void *data, int len)
 {
-    D("about to read (fd=%d, len=%d)\n", h->fd, len);
+    D("about to read (fd=%d, len=%d)", h->fd, len);
     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
@@ -268,20 +268,20 @@
         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",
+            D("ERROR: fd = %d, n = %d, errno = %d (%s)",
                 h->fd, n, errno, strerror(errno));
             return -1;
         }
         len -= n;
         data = ((char*)data) + n;
     }
-    D("[ done fd=%d ]\n", h->fd);
+    D("[ done fd=%d ]", h->fd);
     return 0;
 }
 
 static void usb_adb_kick(usb_handle *h)
 {
-    D("usb_kick\n");
+    D("usb_kick");
     adb_mutex_lock(&h->lock);
     unix_close(h->fd);
     h->fd = -1;
@@ -311,12 +311,12 @@
     // and when we are not.
     int fd = unix_open("/dev/android_adb_enable", O_RDWR);
     if (fd < 0) {
-       D("failed to open /dev/android_adb_enable\n");
+       D("failed to open /dev/android_adb_enable");
     } else {
         close_on_exec(fd);
     }
 
-    D("[ usb_init - starting thread ]\n");
+    D("[ usb_init - starting thread ]");
     if (!adb_thread_create(usb_adb_open_thread, h)) {
         fatal_errno("cannot create usb thread");
     }
@@ -341,10 +341,10 @@
     v2_descriptor.ss_descs = ss_descriptors;
 
     if (h->control < 0) { // might have already done this before
-        D("OPENING %s\n", USB_FFS_ADB_EP0);
+        D("OPENING %s", USB_FFS_ADB_EP0);
         h->control = adb_open(USB_FFS_ADB_EP0, O_RDWR);
         if (h->control < 0) {
-            D("[ %s: cannot open control endpoint: errno=%d]\n", USB_FFS_ADB_EP0, errno);
+            D("[ %s: cannot open control endpoint: errno=%d]", USB_FFS_ADB_EP0, errno);
             goto err;
         }
 
@@ -356,30 +356,30 @@
             v1_descriptor.header.hs_count = 3;
             v1_descriptor.fs_descs = fs_descriptors;
             v1_descriptor.hs_descs = hs_descriptors;
-            D("[ %s: Switching to V1_descriptor format errno=%d ]\n", USB_FFS_ADB_EP0, errno);
+            D("[ %s: Switching to V1_descriptor format errno=%d ]", USB_FFS_ADB_EP0, errno);
             ret = adb_write(h->control, &v1_descriptor, sizeof(v1_descriptor));
             if (ret < 0) {
-                D("[ %s: write descriptors failed: errno=%d ]\n", USB_FFS_ADB_EP0, errno);
+                D("[ %s: write descriptors failed: errno=%d ]", USB_FFS_ADB_EP0, errno);
                 goto err;
             }
         }
 
         ret = adb_write(h->control, &strings, sizeof(strings));
         if (ret < 0) {
-            D("[ %s: writing strings failed: errno=%d]\n", USB_FFS_ADB_EP0, errno);
+            D("[ %s: writing strings failed: errno=%d]", USB_FFS_ADB_EP0, errno);
             goto err;
         }
     }
 
     h->bulk_out = adb_open(USB_FFS_ADB_OUT, O_RDWR);
     if (h->bulk_out < 0) {
-        D("[ %s: cannot open bulk-out ep: errno=%d ]\n", USB_FFS_ADB_OUT, errno);
+        D("[ %s: cannot open bulk-out ep: errno=%d ]", USB_FFS_ADB_OUT, errno);
         goto err;
     }
 
     h->bulk_in = adb_open(USB_FFS_ADB_IN, O_RDWR);
     if (h->bulk_in < 0) {
-        D("[ %s: cannot open bulk-in ep: errno=%d ]\n", USB_FFS_ADB_IN, errno);
+        D("[ %s: cannot open bulk-in ep: errno=%d ]", USB_FFS_ADB_IN, errno);
         goto err;
     }
 
@@ -424,7 +424,7 @@
         }
         property_set("sys.usb.ffs.ready", "1");
 
-        D("[ usb_thread - registering device ]\n");
+        D("[ usb_thread - registering device ]");
         register_usb_transport(usb, 0, 0, 1);
     }
 
@@ -442,19 +442,19 @@
         count += ret;
     }
 
-    D("[ bulk_write done fd=%d ]\n", bulk_in);
+    D("[ bulk_write done fd=%d ]", bulk_in);
     return count;
 }
 
 static int usb_ffs_write(usb_handle* h, const void* data, int len)
 {
-    D("about to write (fd=%d, len=%d)\n", h->bulk_in, len);
+    D("about to write (fd=%d, len=%d)", h->bulk_in, len);
     int n = bulk_write(h->bulk_in, reinterpret_cast<const uint8_t*>(data), len);
     if (n != len) {
-        D("ERROR: fd = %d, n = %d: %s\n", h->bulk_in, n, strerror(errno));
+        D("ERROR: fd = %d, n = %d: %s", h->bulk_in, n, strerror(errno));
         return -1;
     }
-    D("[ done fd=%d ]\n", h->bulk_in);
+    D("[ done fd=%d ]", h->bulk_in);
     return 0;
 }
 
@@ -465,7 +465,7 @@
     while (count < length) {
         int ret = adb_read(bulk_out, buf + count, length - count);
         if (ret < 0) {
-            D("[ bulk_read failed fd=%d length=%zu count=%zu ]\n", bulk_out, length, count);
+            D("[ bulk_read failed fd=%d length=%zu count=%zu ]", bulk_out, length, count);
             return -1;
         }
         count += ret;
@@ -476,13 +476,13 @@
 
 static int usb_ffs_read(usb_handle* h, void* data, int len)
 {
-    D("about to read (fd=%d, len=%d)\n", h->bulk_out, len);
+    D("about to read (fd=%d, len=%d)", h->bulk_out, len);
     int n = bulk_read(h->bulk_out, reinterpret_cast<uint8_t*>(data), len);
     if (n != len) {
-        D("ERROR: fd = %d, n = %d: %s\n", h->bulk_out, n, strerror(errno));
+        D("ERROR: fd = %d, n = %d: %s", h->bulk_out, n, strerror(errno));
         return -1;
     }
-    D("[ done fd=%d ]\n", h->bulk_out);
+    D("[ done fd=%d ]", h->bulk_out);
     return 0;
 }
 
@@ -492,11 +492,11 @@
 
     err = ioctl(h->bulk_in, FUNCTIONFS_CLEAR_HALT);
     if (err < 0)
-        D("[ kick: source (fd=%d) clear halt failed (%d) ]\n", h->bulk_in, errno);
+        D("[ kick: source (fd=%d) clear halt failed (%d) ]", h->bulk_in, errno);
 
     err = ioctl(h->bulk_out, FUNCTIONFS_CLEAR_HALT);
     if (err < 0)
-        D("[ kick: sink (fd=%d) clear halt failed (%d) ]\n", h->bulk_out, errno);
+        D("[ kick: sink (fd=%d) clear halt failed (%d) ]", h->bulk_out, errno);
 
     adb_mutex_lock(&h->lock);
 
@@ -514,7 +514,7 @@
 
 static void usb_ffs_init()
 {
-    D("[ usb_init - using FunctionFS ]\n");
+    D("[ usb_init - using FunctionFS ]");
 
     usb_handle* h = reinterpret_cast<usb_handle*>(calloc(1, sizeof(usb_handle)));
     if (h == nullptr) fatal("couldn't allocate usb_handle");
@@ -529,7 +529,7 @@
     adb_cond_init(&h->notify, 0);
     adb_mutex_init(&h->lock, 0);
 
-    D("[ usb_init - starting thread ]\n");
+    D("[ usb_init - starting thread ]");
     if (!adb_thread_create(usb_ffs_open_thread, h)) {
         fatal_errno("[ cannot create usb thread ]\n");
     }
diff --git a/adb/usb_osx.cpp b/adb/usb_osx.cpp
index d9e2fb2..8037606 100644
--- a/adb/usb_osx.cpp
+++ b/adb/usb_osx.cpp
@@ -439,7 +439,7 @@
         adb_cond_init(&start_cond, NULL);
 
         if (!adb_thread_create(RunLoopThread, nullptr)) {
-            fatal_errno("cannot create input thread");
+            fatal_errno("cannot create RunLoop thread");
         }
 
         // Wait for initialization to finish
diff --git a/adb/usb_windows.cpp b/adb/usb_windows.cpp
index ab36475..5bb0100 100644
--- a/adb/usb_windows.cpp
+++ b/adb/usb_windows.cpp
@@ -172,7 +172,7 @@
 
 void* device_poll_thread(void* unused) {
   adb_thread_setname("Device Poll");
-  D("Created device thread\n");
+  D("Created device thread");
 
   while(1) {
     find_devices();
@@ -208,7 +208,7 @@
   // heavyweight WMI APIs to get power notifications. But for the common case
   // of a developer's interactive session, a window message pump is more
   // appropriate.
-  D("Created power notification thread\n");
+  D("Created power notification thread");
   adb_thread_setname("Power Notifier");
 
   // Window class names are process specific.
@@ -252,7 +252,7 @@
   // do that, but it might be possible for that to occur when logging off or
   // shutting down. Not a big deal since the whole process will be going away
   // soon anyway.
-  D("Power notification thread exiting\n");
+  D("Power notification thread exiting");
 
   return NULL;
 }
@@ -272,7 +272,7 @@
   // Allocate our handle
   usb_handle* ret = (usb_handle*)calloc(1, sizeof(usb_handle));
   if (NULL == ret) {
-    D("Could not allocate %u bytes for usb_handle: %s\n", sizeof(usb_handle),
+    D("Could not allocate %u bytes for usb_handle: %s", sizeof(usb_handle),
       strerror(errno));
     goto fail;
   }
@@ -284,7 +284,7 @@
   // Create interface.
   ret->adb_interface = AdbCreateInterfaceByName(interface_name);
   if (NULL == ret->adb_interface) {
-    D("AdbCreateInterfaceByName failed: %s\n",
+    D("AdbCreateInterfaceByName failed: %s",
       SystemErrorCodeToString(GetLastError()).c_str());
     goto fail;
   }
@@ -295,7 +295,7 @@
                                    AdbOpenAccessTypeReadWrite,
                                    AdbOpenSharingModeReadWrite);
   if (NULL == ret->adb_read_pipe) {
-    D("AdbOpenDefaultBulkReadEndpoint failed: %s\n",
+    D("AdbOpenDefaultBulkReadEndpoint failed: %s",
       SystemErrorCodeToString(GetLastError()).c_str());
     goto fail;
   }
@@ -306,7 +306,7 @@
                                     AdbOpenAccessTypeReadWrite,
                                     AdbOpenSharingModeReadWrite);
   if (NULL == ret->adb_write_pipe) {
-    D("AdbOpenDefaultBulkWriteEndpoint failed: %s\n",
+    D("AdbOpenDefaultBulkWriteEndpoint failed: %s",
       SystemErrorCodeToString(GetLastError()).c_str());
     goto fail;
   }
@@ -318,14 +318,14 @@
                       &name_len,
                       true);
   if (0 == name_len) {
-    D("AdbGetInterfaceName returned name length of zero: %s\n",
+    D("AdbGetInterfaceName returned name length of zero: %s",
       SystemErrorCodeToString(GetLastError()).c_str());
     goto fail;
   }
 
   ret->interface_name = (char*)malloc(name_len);
   if (NULL == ret->interface_name) {
-    D("Could not allocate %lu bytes for interface_name: %s\n", name_len,
+    D("Could not allocate %lu bytes for interface_name: %s", name_len,
       strerror(errno));
     goto fail;
   }
@@ -335,7 +335,7 @@
                            ret->interface_name,
                            &name_len,
                            true)) {
-    D("AdbGetInterfaceName failed: %s\n",
+    D("AdbGetInterfaceName failed: %s",
       SystemErrorCodeToString(GetLastError()).c_str());
     goto fail;
   }
@@ -357,9 +357,9 @@
   unsigned long written = 0;
   int err = 0;
 
-  D("usb_write %d\n", len);
+  D("usb_write %d", len);
   if (NULL == handle) {
-    D("usb_write was passed NULL handle\n");
+    D("usb_write was passed NULL handle");
     err = EINVAL;
     goto fail;
   }
@@ -370,18 +370,18 @@
                             (unsigned long)len,
                             &written,
                             time_out)) {
-    D("AdbWriteEndpointSync failed: %s\n",
+    D("AdbWriteEndpointSync failed: %s",
       SystemErrorCodeToString(GetLastError()).c_str());
     err = EIO;
     goto fail;
   }
 
   // Make sure that we've written what we were asked to write
-  D("usb_write got: %ld, expected: %d\n", written, len);
+  D("usb_write got: %ld, expected: %d", written, len);
   if (written != (unsigned long)len) {
     // If this occurs, this code should be changed to repeatedly call
     // AdbWriteEndpointSync() until all bytes are written.
-    D("AdbWriteEndpointSync was supposed to write %d, but only wrote %ld\n",
+    D("AdbWriteEndpointSync was supposed to write %d, but only wrote %ld",
       len, written);
     err = EIO;
     goto fail;
@@ -394,7 +394,7 @@
                               0,
                               &written,
                               time_out)) {
-      D("AdbWriteEndpointSync of zero length packet failed: %s\n",
+      D("AdbWriteEndpointSync of zero length packet failed: %s",
         SystemErrorCodeToString(GetLastError()).c_str());
       err = EIO;
       goto fail;
@@ -407,11 +407,11 @@
   // Any failure should cause us to kick the device instead of leaving it a
   // zombie state with potential to hang.
   if (NULL != handle) {
-    D("Kicking device due to error in usb_write\n");
+    D("Kicking device due to error in usb_write");
     usb_kick(handle);
   }
 
-  D("usb_write failed\n");
+  D("usb_write failed");
   errno = err;
   return -1;
 }
@@ -421,9 +421,9 @@
   unsigned long read = 0;
   int err = 0;
 
-  D("usb_read %d\n", len);
+  D("usb_read %d", len);
   if (NULL == handle) {
-    D("usb_read was passed NULL handle\n");
+    D("usb_read was passed NULL handle");
     err = EINVAL;
     goto fail;
   }
@@ -431,12 +431,12 @@
   while (len > 0) {
     if (!AdbReadEndpointSync(handle->adb_read_pipe, data, len, &read,
                              time_out)) {
-      D("AdbReadEndpointSync failed: %s\n",
+      D("AdbReadEndpointSync failed: %s",
         SystemErrorCodeToString(GetLastError()).c_str());
       err = EIO;
       goto fail;
     }
-    D("usb_read got: %ld, expected: %d\n", read, len);
+    D("usb_read got: %ld, expected: %d", read, len);
 
     data = (char *)data + read;
     len -= read;
@@ -448,11 +448,11 @@
   // Any failure should cause us to kick the device instead of leaving it a
   // zombie state with potential to hang.
   if (NULL != handle) {
-    D("Kicking device due to error in usb_read\n");
+    D("Kicking device due to error in usb_read");
     usb_kick(handle);
   }
 
-  D("usb_read failed\n");
+  D("usb_read failed");
   errno = err;
   return -1;
 }
@@ -460,13 +460,13 @@
 // Wrapper around AdbCloseHandle() that logs diagnostics.
 static void _adb_close_handle(ADBAPIHANDLE adb_handle) {
   if (!AdbCloseHandle(adb_handle)) {
-    D("AdbCloseHandle(%p) failed: %s\n", adb_handle,
+    D("AdbCloseHandle(%p) failed: %s", adb_handle,
       SystemErrorCodeToString(GetLastError()).c_str());
   }
 }
 
 void usb_cleanup_handle(usb_handle* handle) {
-  D("usb_cleanup_handle\n");
+  D("usb_cleanup_handle");
   if (NULL != handle) {
     if (NULL != handle->interface_name)
       free(handle->interface_name);
@@ -494,7 +494,7 @@
 }
 
 void usb_kick(usb_handle* handle) {
-  D("usb_kick\n");
+  D("usb_kick");
   if (NULL != handle) {
     adb_mutex_lock(&usb_lock);
 
@@ -507,7 +507,7 @@
 }
 
 int usb_close(usb_handle* handle) {
-  D("usb_close\n");
+  D("usb_close");
 
   if (NULL != handle) {
     // Remove handle from the list
@@ -539,7 +539,7 @@
 
   if (!AdbGetUsbDeviceDescriptor(handle->adb_interface,
                                  &device_desc)) {
-    D("AdbGetUsbDeviceDescriptor failed: %s\n",
+    D("AdbGetUsbDeviceDescriptor failed: %s",
       SystemErrorCodeToString(GetLastError()).c_str());
     return 0;
   }
@@ -549,7 +549,7 @@
 
   if (!AdbGetUsbInterfaceDescriptor(handle->adb_interface,
                                     &interf_desc)) {
-    D("AdbGetUsbInterfaceDescriptor failed: %s\n",
+    D("AdbGetUsbInterfaceDescriptor failed: %s",
       SystemErrorCodeToString(GetLastError()).c_str());
     return 0;
   }
@@ -567,9 +567,9 @@
       // assuming zero is a valid bulk endpoint ID
       if (AdbGetEndpointInformation(handle->adb_interface, 0, &endpoint_info)) {
         handle->zero_mask = endpoint_info.max_packet_size - 1;
-        D("device zero_mask: 0x%x\n", handle->zero_mask);
+        D("device zero_mask: 0x%x", handle->zero_mask);
       } else {
-        D("AdbGetEndpointInformation failed: %s\n",
+        D("AdbGetEndpointInformation failed: %s",
           SystemErrorCodeToString(GetLastError()).c_str());
       }
     }
@@ -593,7 +593,7 @@
     AdbEnumInterfaces(usb_class_id, true, true, true);
 
   if (NULL == enum_handle) {
-    D("AdbEnumInterfaces failed: %s\n",
+    D("AdbEnumInterfaces failed: %s",
       SystemErrorCodeToString(GetLastError()).c_str());
     return;
   }
@@ -617,7 +617,7 @@
         if (NULL != handle) {
         // Lets see if this interface (device) belongs to us
         if (recognized_device(handle)) {
-          D("adding a new device %s\n", interf_name);
+          D("adding a new device %s", interf_name);
           char serial_number[512];
           unsigned long serial_number_len = sizeof(serial_number);
           if (AdbGetSerialNumber(handle->adb_interface,
@@ -628,12 +628,12 @@
             if (register_new_device(handle)) {
               register_usb_transport(handle, serial_number, NULL, 1);
             } else {
-              D("register_new_device failed for %s\n", interf_name);
+              D("register_new_device failed for %s", interf_name);
               usb_cleanup_handle(handle);
               free(handle);
             }
           } else {
-            D("cannot get serial number: %s\n",
+            D("cannot get serial number: %s",
               SystemErrorCodeToString(GetLastError()).c_str());
             usb_cleanup_handle(handle);
             free(handle);
@@ -650,7 +650,7 @@
 
   if (GetLastError() != ERROR_NO_MORE_ITEMS) {
     // Only ERROR_NO_MORE_ITEMS is expected at the end of enumeration.
-    D("AdbNextInterface failed: %s\n",
+    D("AdbNextInterface failed: %s",
       SystemErrorCodeToString(GetLastError()).c_str());
   }
 
diff --git a/base/Android.mk b/base/Android.mk
index 4e135f6..51dd736 100644
--- a/base/Android.mk
+++ b/base/Android.mk
@@ -68,6 +68,7 @@
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
 LOCAL_STATIC_LIBRARIES := libcutils
 LOCAL_MULTILIB := both
+LOCAL_MODULE_HOST_OS := darwin linux windows
 include $(BUILD_HOST_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
diff --git a/base/file.cpp b/base/file.cpp
index 9a340b7..3468dcf 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -28,6 +28,10 @@
 #include "cutils/log.h"
 #include "utils/Compat.h"
 
+#if !defined(_WIN32)
+#define O_BINARY 0
+#endif
+
 namespace android {
 namespace base {
 
@@ -45,8 +49,7 @@
 bool ReadFileToString(const std::string& path, std::string* content) {
   content->clear();
 
-  int fd =
-      TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW));
+  int fd = TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_BINARY));
   if (fd == -1) {
     return false;
   }
@@ -80,9 +83,8 @@
 #if !defined(_WIN32)
 bool WriteStringToFile(const std::string& content, const std::string& path,
                        mode_t mode, uid_t owner, gid_t group) {
-  int fd = TEMP_FAILURE_RETRY(
-      open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
-           mode));
+  int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW | O_BINARY;
+  int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode));
   if (fd == -1) {
     ALOGE("android::WriteStringToFile open failed: %s", strerror(errno));
     return false;
@@ -108,9 +110,8 @@
 #endif
 
 bool WriteStringToFile(const std::string& content, const std::string& path) {
-  int fd = TEMP_FAILURE_RETRY(
-      open(path.c_str(), O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
-           DEFFILEMODE));
+  int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW | O_BINARY;
+  int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags, DEFFILEMODE));
   if (fd == -1) {
     return false;
   }
diff --git a/base/test_utils.cpp b/base/test_utils.cpp
index b0c5a12..22641e7 100644
--- a/base/test_utils.cpp
+++ b/base/test_utils.cpp
@@ -37,9 +37,9 @@
     return -1;
   }
   // Use open() to match the close() that TemporaryFile's destructor does.
-  // Note that on Windows, this does CR/LF translation and _setmode() should
-  // be used to change that if appropriate.
-  return open(template_name, O_CREAT | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR);
+  // Use O_BINARY to match base file APIs.
+  return open(template_name, O_CREAT | O_EXCL | O_RDWR | O_BINARY,
+              S_IRUSR | S_IWUSR);
 }
 
 char* mkdtemp(char* template_name) {
diff --git a/crash_reporter/crash_reporter.cc b/crash_reporter/crash_reporter.cc
index 23bd342..72eeeda 100644
--- a/crash_reporter/crash_reporter.cc
+++ b/crash_reporter/crash_reporter.cc
@@ -25,6 +25,7 @@
 #include <base/strings/string_util.h>
 #include <base/strings/stringprintf.h>
 #include <chromeos/flag_helper.h>
+#include <chromeos/process.h>
 #include <chromeos/syslog_logging.h>
 #include <metrics/metrics_library.h>
 
@@ -85,27 +86,32 @@
 
 static void CountUserCrash() {
   SendCrashMetrics(kCrashKindUser, "user");
-  std::string command = StringPrintf(
-      "/system/bin/dbus-send --type=signal --system / \"%s\" &",
-      kUserCrashSignal);
   // Announce through D-Bus whenever a user crash happens. This is
   // used by the metrics daemon to log active use time between
   // crashes.
   //
-  // This could be done more efficiently by explicit fork/exec or
-  // using a dbus library directly. However, this should run
-  // relatively rarely and longer term we may need to implement a
-  // better way to do this that doesn't rely on D-Bus.
-  //
-  // We run in the background in case dbus daemon itself is crashed
+  // We run in the background in case dbus-daemon itself is crashed
   // and not responding.  This allows us to not block and potentially
   // deadlock on a dbus-daemon crash.  If dbus-daemon crashes without
   // restarting, each crash will fork off a lot of dbus-send
   // processes.  Such a system is in a unusable state and will need
   // to be restarted anyway.
+  //
+  // Note: This will mean that the dbus-send process will become a zombie and
+  // reparent to init for reaping, but that's OK -- see above.
 
-  int status = system(command.c_str());
-  LOG_IF(WARNING, status != 0) << "dbus-send running failed";
+  chromeos::ProcessImpl dbus_send;
+  dbus_send.AddArg("/system/bin/dbus-send");
+  dbus_send.AddArg("--type=signal");
+  dbus_send.AddArg("--system");
+  dbus_send.AddArg("/");
+  dbus_send.AddArg(kUserCrashSignal);
+  bool status = dbus_send.Start();
+  if (status) {
+    dbus_send.Release();
+  } else {
+    PLOG(WARNING) << "Sending UserCrash DBus signal failed";
+  }
 }
 
 
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index ce8e15f..3f201ec 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -24,38 +24,25 @@
 LOCAL_SRC_FILES := protocol.cpp engine.cpp bootimg_utils.cpp fastboot.cpp util.cpp fs.cpp
 LOCAL_MODULE := fastboot
 LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE_HOST_OS := darwin linux windows
 LOCAL_CONLYFLAGS += -std=gnu99
 LOCAL_CFLAGS += -Wall -Wextra -Werror -Wunreachable-code
 
 LOCAL_CFLAGS += -DFASTBOOT_REVISION='"$(fastboot_version)"'
 
-ifeq ($(HOST_OS),linux)
-  LOCAL_SRC_FILES += usb_linux.cpp util_linux.cpp
-endif
+LOCAL_SRC_FILES_linux := usb_linux.cpp util_linux.cpp
 
-ifeq ($(HOST_OS),darwin)
-  LOCAL_SRC_FILES += usb_osx.cpp util_osx.cpp
-  LOCAL_LDLIBS += -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
-  LOCAL_CFLAGS += -Wno-unused-parameter
-endif
+LOCAL_SRC_FILES_darwin := usb_osx.cpp util_osx.cpp
+LOCAL_LDLIBS_darwin := -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
+LOCAL_CFLAGS_darwin := -Wno-unused-parameter
 
-ifeq ($(HOST_OS),windows)
-  LOCAL_SRC_FILES += usb_windows.cpp util_windows.cpp
-  EXTRA_STATIC_LIBS := AdbWinApi
-  ifneq ($(strip $(USE_CYGWIN)),)
-    # Pure cygwin case
-    LOCAL_LDLIBS += -lpthread
-  endif
-  ifneq ($(strip $(USE_MINGW)),)
-    # MinGW under Linux case
-    LOCAL_LDLIBS += -lws2_32
-    USE_SYSDEPS_WIN32 := 1
-  endif
-  LOCAL_C_INCLUDES += development/host/windows/usb/api
-endif
+LOCAL_SRC_FILES_windows := usb_windows.cpp util_windows.cpp
+LOCAL_STATIC_LIBRARIES_windows := AdbWinApi
+LOCAL_REQUIRED_MODULES_windows := AdbWinApi
+LOCAL_LDLIBS_windows := -lws2_32
+LOCAL_C_INCLUDES_windows := development/host/windows/usb/api
 
 LOCAL_STATIC_LIBRARIES := \
-    $(EXTRA_STATIC_LIBS) \
     libziparchive-host \
     libext4_utils_host \
     libsparse_host \
@@ -64,24 +51,18 @@
     libz \
     libbase
 
-ifneq ($(HOST_OS),windows)
-LOCAL_STATIC_LIBRARIES += libselinux
-endif # HOST_OS != windows
+LOCAL_STATIC_LIBRARIES_darwin := libselinux
+LOCAL_STATIC_LIBRARIES_linux := libselinux
 
-ifeq ($(HOST_OS),linux)
 # libf2fs_dlutils_host will dlopen("libf2fs_fmt_host_dyn")
-LOCAL_CFLAGS += -DUSE_F2FS
-LOCAL_LDFLAGS += -ldl -rdynamic -Wl,-rpath,.
-LOCAL_REQUIRED_MODULES := libf2fs_fmt_host_dyn
+LOCAL_CFLAGS_linux := -DUSE_F2FS
+LOCAL_LDFLAGS_linux := -ldl -rdynamic -Wl,-rpath,.
+LOCAL_REQUIRED_MODULES_linux := libf2fs_fmt_host_dyn
 # The following libf2fs_* are from system/extras/f2fs_utils,
 # and do not use code in external/f2fs-tools.
-LOCAL_STATIC_LIBRARIES += libf2fs_utils_host libf2fs_ioutils_host libf2fs_dlutils_host
-endif
+LOCAL_STATIC_LIBRARIES_linux += libf2fs_utils_host libf2fs_ioutils_host libf2fs_dlutils_host
 
-# libc++ not available on windows yet
-ifneq ($(HOST_OS),windows)
-    LOCAL_CXX_STL := libc++_static
-endif
+LOCAL_CXX_STL := libc++_static
 
 # Don't add anything here, we don't want additional shared dependencies
 # on the host fastboot tool, and shared libraries that link against libc++
@@ -104,7 +85,3 @@
 LOCAL_CFLAGS := -Werror
 include $(BUILD_HOST_EXECUTABLE)
 endif
-
-ifeq ($(HOST_OS),windows)
-$(LOCAL_INSTALLED_MODULE): $(HOST_OUT_EXECUTABLES)/AdbWinApi.dll
-endif
diff --git a/fs_mgr/fs_mgr_fstab.c b/fs_mgr/fs_mgr_fstab.c
index 8b0f714..d77d41f 100644
--- a/fs_mgr/fs_mgr_fstab.c
+++ b/fs_mgr/fs_mgr_fstab.c
@@ -20,6 +20,8 @@
 #include <string.h>
 #include <sys/mount.h>
 
+#include <cutils/properties.h>
+
 #include "fs_mgr_priv.h"
 
 struct fs_mgr_flag_values {
@@ -70,6 +72,7 @@
     { "zramsize=",   MF_ZRAMSIZE },
     { "verify",      MF_VERIFY },
     { "noemulatedsd", MF_NOEMULATEDSD },
+    { "slotselect",  MF_SLOTSELECT },
     { "defaults",    0 },
     { 0,             0 },
 };
@@ -307,6 +310,23 @@
         fstab->recs[cnt].partnum = flag_vals.partnum;
         fstab->recs[cnt].swap_prio = flag_vals.swap_prio;
         fstab->recs[cnt].zram_size = flag_vals.zram_size;
+
+        /* If an A/B partition, modify block device to be the real block device */
+        if (fstab->recs[cnt].fs_mgr_flags & MF_SLOTSELECT) {
+            char propbuf[PROPERTY_VALUE_MAX];
+            char *tmp;
+
+            /* use the kernel parameter if set */
+            property_get("ro.boot.slot_suffix", propbuf, "");
+
+            if (asprintf(&tmp, "%s%s", fstab->recs[cnt].blk_device, propbuf) > 0) {
+                free(fstab->recs[cnt].blk_device);
+                fstab->recs[cnt].blk_device = tmp;
+            } else {
+                ERROR("Error updating block device name\n");
+                goto err;
+            }
+        }
         cnt++;
     }
     fclose(fstab_file);
@@ -448,3 +468,8 @@
 {
     return fstab->fs_mgr_flags & MF_NOEMULATEDSD;
 }
+
+int fs_mgr_is_slotselect(struct fstab_rec *fstab)
+{
+    return fstab->fs_mgr_flags & MF_SLOTSELECT;
+}
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index d56111a..cc02bac 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -77,6 +77,7 @@
 #define MF_NOEMULATEDSD 0x800 /* no emulated sdcard daemon, sd card is the only
                                  external storage */
 #define MF_FILEENCRYPTION 0x2000
+#define MF_SLOTSELECT   0x8000
 
 #define DM_BUF_SIZE 4096
 
diff --git a/include/cutils/trace.h b/include/cutils/trace.h
index 9c077d6..087a0c4 100644
--- a/include/cutils/trace.h
+++ b/include/cutils/trace.h
@@ -68,7 +68,8 @@
 #define ATRACE_TAG_BIONIC           (1<<16)
 #define ATRACE_TAG_POWER            (1<<17)
 #define ATRACE_TAG_PACKAGE_MANAGER  (1<<18)
-#define ATRACE_TAG_LAST             ATRACE_TAG_PACKAGE_MANAGER
+#define ATRACE_TAG_SYSTEM_SERVER    (1<<19)
+#define ATRACE_TAG_LAST             ATRACE_TAG_SYSTEM_SERVER
 
 // Reserved for initialization.
 #define ATRACE_TAG_NOT_READY        (1ULL<<63)
diff --git a/include/private/android_filesystem_config.h b/include/private/android_filesystem_config.h
index 7047e0f..3e8d62a 100644
--- a/include/private/android_filesystem_config.h
+++ b/include/private/android_filesystem_config.h
@@ -32,6 +32,8 @@
 #include "android_filesystem_capability.h"
 #endif
 
+#define CAP_MASK_LONG(cap_name)  (1ULL << (cap_name))
+
 /* This is the master Users and Groups config for the platform.
  * DO NOT EVER RENUMBER
  */
diff --git a/init/Android.mk b/init/Android.mk
index 58bff58..7670951 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -45,6 +45,7 @@
 LOCAL_CPPFLAGS := $(init_cflags)
 LOCAL_SRC_FILES:= \
     action.cpp \
+    import_parser.cpp \
     init_parser.cpp \
     log.cpp \
     parser.cpp \
diff --git a/init/action.cpp b/init/action.cpp
index dd366d3..c6cbc2e 100644
--- a/init/action.cpp
+++ b/init/action.cpp
@@ -21,46 +21,27 @@
 #include <base/strings.h>
 #include <base/stringprintf.h>
 
+#include "builtins.h"
 #include "error.h"
 #include "init_parser.h"
 #include "log.h"
 #include "property_service.h"
 #include "util.h"
 
-class Action::Command
-{
-public:
-    Command(int (*f)(const std::vector<std::string>& args),
-            const std::vector<std::string>& args,
-            const std::string& filename,
-            int line);
+using android::base::Join;
+using android::base::StringPrintf;
 
-    int InvokeFunc() const;
-    std::string BuildCommandString() const;
-    std::string BuildSourceString() const;
-
-private:
-    int (*func_)(const std::vector<std::string>& args);
-    const std::vector<std::string> args_;
-    const std::string filename_;
-    int line_;
-};
-
-Action::Command::Command(int (*f)(const std::vector<std::string>& args),
-                         const std::vector<std::string>& args,
-                         const std::string& filename,
-                         int line) :
-    func_(f), args_(args), filename_(filename), line_(line)
-{
+Command::Command(BuiltinFunction f, const std::vector<std::string>& args,
+                 const std::string& filename, int line)
+    : func_(f), args_(args), filename_(filename), line_(line) {
 }
 
-int Action::Command::InvokeFunc() const
-{
+int Command::InvokeFunc() const {
     std::vector<std::string> expanded_args;
     expanded_args.resize(args_.size());
     expanded_args[0] = args_[0];
     for (std::size_t i = 1; i < args_.size(); ++i) {
-        if (expand_props(args_[i], &expanded_args[i]) == -1) {
+        if (!expand_props(args_[i], &expanded_args[i])) {
             ERROR("%s: cannot expand '%s'\n", args_[0].c_str(), args_[i].c_str());
             return -EINVAL;
         }
@@ -69,51 +50,71 @@
     return func_(expanded_args);
 }
 
-std::string Action::Command::BuildCommandString() const
-{
-    return android::base::Join(args_, ' ');
+std::string Command::BuildCommandString() const {
+    return Join(args_, ' ');
 }
 
-std::string Action::Command::BuildSourceString() const
-{
+std::string Command::BuildSourceString() const {
     if (!filename_.empty()) {
-        return android::base::StringPrintf(" (%s:%d)", filename_.c_str(), line_);
+        return StringPrintf(" (%s:%d)", filename_.c_str(), line_);
     } else {
         return std::string();
     }
 }
 
-Action::Action()
-{
+Action::Action(bool oneshot) : oneshot_(oneshot) {
 }
 
-void Action::AddCommand(int (*f)(const std::vector<std::string>& args),
+const KeywordMap<BuiltinFunction>* Action::function_map_ = nullptr;
+
+bool Action::AddCommand(const std::vector<std::string>& args,
+                        const std::string& filename, int line, std::string* err) {
+    if (!function_map_) {
+        *err = "no function map available";
+        return false;
+    }
+
+    if (args.empty()) {
+        *err = "command needed, but not provided";
+        return false;
+    }
+
+    auto function = function_map_->FindFunction(args[0], args.size() - 1, err);
+    if (!function) {
+        return false;
+    }
+
+    AddCommand(function, args, filename, line);
+    return true;
+}
+
+void Action::AddCommand(BuiltinFunction f,
                         const std::vector<std::string>& args,
-                        const std::string& filename, int line)
-{
-    Action::Command* cmd = new Action::Command(f, args, filename, line);
-    commands_.push_back(cmd);
+                        const std::string& filename, int line) {
+    commands_.emplace_back(f, args, filename, line);
 }
 
-std::size_t Action::NumCommands() const
-{
-    return commands_.size();
-}
-
-void Action::ExecuteOneCommand(std::size_t command) const
-{
-    ExecuteCommand(*commands_[command]);
-}
-
-void Action::ExecuteAllCommands() const
-{
-    for (const auto& c : commands_) {
-        ExecuteCommand(*c);
+void Action::CombineAction(const Action& action) {
+    for (const auto& c : action.commands_) {
+        commands_.emplace_back(c);
     }
 }
 
-void Action::ExecuteCommand(const Command& command) const
-{
+std::size_t Action::NumCommands() const {
+    return commands_.size();
+}
+
+void Action::ExecuteOneCommand(std::size_t command) const {
+    ExecuteCommand(commands_[command]);
+}
+
+void Action::ExecuteAllCommands() const {
+    for (const auto& c : commands_) {
+        ExecuteCommand(c);
+    }
+}
+
+void Action::ExecuteCommand(const Command& command) const {
     Timer t;
     int result = command.InvokeFunc();
 
@@ -128,8 +129,7 @@
     }
 }
 
-bool Action::ParsePropertyTrigger(const std::string& trigger, std::string* err)
-{
+bool Action::ParsePropertyTrigger(const std::string& trigger, std::string* err) {
     const static std::string prop_str("property:");
     std::string prop_name(trigger.substr(prop_str.length()));
     size_t equal_pos = prop_name.find('=');
@@ -149,8 +149,7 @@
     return true;
 }
 
-bool Action::InitTriggers(const std::vector<std::string>& args, std::string* err)
-{
+bool Action::InitTriggers(const std::vector<std::string>& args, std::string* err) {
     const static std::string prop_str("property:");
     for (std::size_t i = 0; i < args.size(); ++i) {
         if (i % 2) {
@@ -179,21 +178,26 @@
     return true;
 }
 
-bool Action::InitSingleTrigger(const std::string& trigger)
-{
+bool Action::InitSingleTrigger(const std::string& trigger) {
     std::vector<std::string> name_vector{trigger};
     std::string err;
     return InitTriggers(name_vector, &err);
 }
 
+// This function checks that all property triggers are satisfied, that is
+// for each (name, value) in property_triggers_, check that the current
+// value of the property 'name' == value.
+//
+// It takes an optional (name, value) pair, which if provided must
+// be present in property_triggers_; it skips the check of the current
+// property value for this pair.
 bool Action::CheckPropertyTriggers(const std::string& name,
-                                   const std::string& value) const
-{
-    bool found = name.empty();
+                                   const std::string& value) const {
     if (property_triggers_.empty()) {
         return true;
     }
 
+    bool found = name.empty();
     for (const auto& t : property_triggers_) {
         const auto& trigger_name = t.first;
         const auto& trigger_value = t.second;
@@ -214,27 +218,23 @@
     return found;
 }
 
-bool Action::CheckEventTrigger(const std::string& trigger) const
-{
+bool Action::CheckEventTrigger(const std::string& trigger) const {
     return !event_trigger_.empty() &&
         trigger == event_trigger_ &&
         CheckPropertyTriggers();
 }
 
 bool Action::CheckPropertyTrigger(const std::string& name,
-                                  const std::string& value) const
-{
+                                  const std::string& value) const {
     return event_trigger_.empty() && CheckPropertyTriggers(name, value);
 }
 
-bool Action::TriggersEqual(const class Action& other) const
-{
+bool Action::TriggersEqual(const Action& other) const {
     return property_triggers_ == other.property_triggers_ &&
         event_trigger_ == other.event_trigger_;
 }
 
-std::string Action::BuildTriggersString() const
-{
+std::string Action::BuildTriggersString() const {
     std::string result;
 
     for (const auto& t : property_triggers_) {
@@ -251,28 +251,26 @@
     return result;
 }
 
-void Action::DumpState() const
-{
+void Action::DumpState() const {
     std::string trigger_name = BuildTriggersString();
     INFO("on %s\n", trigger_name.c_str());
 
     for (const auto& c : commands_) {
-        std::string cmd_str = c->BuildCommandString();
+        std::string cmd_str = c.BuildCommandString();
         INFO(" %s\n", cmd_str.c_str());
     }
     INFO("\n");
 }
 
-
 class EventTrigger : public Trigger {
 public:
     EventTrigger(const std::string& trigger) : trigger_(trigger) {
     }
-    bool CheckTriggers(const Action* action) override {
-        return action->CheckEventTrigger(trigger_);
+    bool CheckTriggers(const Action& action) const override {
+        return action.CheckEventTrigger(trigger_);
     }
 private:
-    std::string trigger_;
+    const std::string trigger_;
 };
 
 class PropertyTrigger : public Trigger {
@@ -280,27 +278,26 @@
     PropertyTrigger(const std::string& name, const std::string& value)
         : name_(name), value_(value) {
     }
-    bool CheckTriggers(const Action* action) override {
-        return action->CheckPropertyTrigger(name_, value_);
+    bool CheckTriggers(const Action& action) const override {
+        return action.CheckPropertyTrigger(name_, value_);
     }
 private:
-    std::string name_;
-    std::string value_;
+    const std::string name_;
+    const std::string value_;
 };
 
 class BuiltinTrigger : public Trigger {
 public:
     BuiltinTrigger(Action* action) : action_(action) {
     }
-    bool CheckTriggers(const Action* action) override {
-        return action == action_;
+    bool CheckTriggers(const Action& action) const override {
+        return action_ == &action;
     }
 private:
-    Action* action_;
+    const Action* action_;
 };
 
-ActionManager::ActionManager() : current_command_(0)
-{
+ActionManager::ActionManager() : current_command_(0) {
 }
 
 ActionManager& ActionManager::GetInstance() {
@@ -308,45 +305,56 @@
     return instance;
 }
 
-void ActionManager::QueueEventTrigger(const std::string& trigger)
-{
+void ActionManager::AddAction(std::unique_ptr<Action> action) {
+    auto old_action_it =
+        std::find_if(actions_.begin(), actions_.end(),
+                     [&action] (std::unique_ptr<Action>& a) {
+                         return action->TriggersEqual(*a);
+                     });
+
+    if (old_action_it != actions_.end()) {
+        (*old_action_it)->CombineAction(*action);
+    } else {
+        actions_.emplace_back(std::move(action));
+    }
+}
+
+void ActionManager::QueueEventTrigger(const std::string& trigger) {
     trigger_queue_.push(std::make_unique<EventTrigger>(trigger));
 }
 
 void ActionManager::QueuePropertyTrigger(const std::string& name,
-                                         const std::string& value)
-{
+                                         const std::string& value) {
     trigger_queue_.push(std::make_unique<PropertyTrigger>(name, value));
 }
 
-void ActionManager::QueueAllPropertyTriggers()
-{
+void ActionManager::QueueAllPropertyTriggers() {
     QueuePropertyTrigger("", "");
 }
 
-void ActionManager::QueueBuiltinAction(int (*func)(const std::vector<std::string>& args),
-                                       const std::string& name)
-{
-    Action* act = new Action();
+void ActionManager::QueueBuiltinAction(BuiltinFunction func,
+                                       const std::string& name) {
+    auto action = std::make_unique<Action>(true);
     std::vector<std::string> name_vector{name};
 
-    if (!act->InitSingleTrigger(name)) {
+    if (!action->InitSingleTrigger(name)) {
         return;
     }
 
-    act->AddCommand(func, name_vector);
+    action->AddCommand(func, name_vector);
 
-    actions_.push_back(act);
-    trigger_queue_.push(std::make_unique<BuiltinTrigger>(act));
+    trigger_queue_.push(std::make_unique<BuiltinTrigger>(action.get()));
+    actions_.emplace_back(std::move(action));
 }
 
 void ActionManager::ExecuteOneCommand() {
+    // Loop through the trigger queue until we have an action to execute
     while (current_executing_actions_.empty() && !trigger_queue_.empty()) {
-        std::copy_if(actions_.begin(), actions_.end(),
-                     std::back_inserter(current_executing_actions_),
-                     [this] (Action* act) {
-                         return trigger_queue_.front()->CheckTriggers(act);
-                     });
+        for (const auto& action : actions_) {
+            if (trigger_queue_.front()->CheckTriggers(*action)) {
+                current_executing_actions_.emplace(action.get());
+            }
+        }
         trigger_queue_.pop();
     }
 
@@ -354,59 +362,67 @@
         return;
     }
 
-    Action* action = current_executing_actions_.back();
-    if (!action->NumCommands()) {
-        current_executing_actions_.pop_back();
-        return;
-    }
+    auto action = current_executing_actions_.front();
 
     if (current_command_ == 0) {
         std::string trigger_name = action->BuildTriggersString();
-        INFO("processing action %p (%s)\n", action, trigger_name.c_str());
+        INFO("processing action (%s)\n", trigger_name.c_str());
     }
 
-    action->ExecuteOneCommand(current_command_++);
+    action->ExecuteOneCommand(current_command_);
+
+    // If this was the last command in the current action, then remove
+    // the action from the executing list.
+    // If this action was oneshot, then also remove it from actions_.
+    ++current_command_;
     if (current_command_ == action->NumCommands()) {
+        current_executing_actions_.pop();
         current_command_ = 0;
-        current_executing_actions_.pop_back();
+        if (action->oneshot()) {
+            auto eraser = [&action] (std::unique_ptr<Action>& a) {
+                return a.get() == action;
+            };
+            actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser));
+        }
     }
 }
 
-bool ActionManager::HasMoreCommands() const
-{
+bool ActionManager::HasMoreCommands() const {
     return !current_executing_actions_.empty() || !trigger_queue_.empty();
 }
 
-Action* ActionManager::AddNewAction(const std::vector<std::string>& triggers,
-                                    std::string* err)
-{
-    if (triggers.size() < 1) {
-        *err = "actions must have a trigger\n";
-        return nullptr;
-    }
-
-    Action* act = new Action();
-    if (!act->InitTriggers(triggers, err)) {
-        return nullptr;
-    }
-
-    auto old_act_it =
-        std::find_if(actions_.begin(), actions_.end(),
-                     [&act] (Action* a) { return act->TriggersEqual(*a); });
-
-    if (old_act_it != actions_.end()) {
-        delete act;
-        return *old_act_it;
-    }
-
-    actions_.push_back(act);
-    return act;
-}
-
-void ActionManager::DumpState() const
-{
+void ActionManager::DumpState() const {
     for (const auto& a : actions_) {
         a->DumpState();
     }
     INFO("\n");
 }
+
+bool ActionParser::ParseSection(const std::vector<std::string>& args,
+                                std::string* err) {
+    std::vector<std::string> triggers(args.begin() + 1, args.end());
+    if (triggers.size() < 1) {
+        *err = "actions must have a trigger";
+        return false;
+    }
+
+    auto action = std::make_unique<Action>(false);
+    if (!action->InitTriggers(triggers, err)) {
+        return false;
+    }
+
+    action_ = std::move(action);
+    return true;
+}
+
+bool ActionParser::ParseLineSection(const std::vector<std::string>& args,
+                                    const std::string& filename, int line,
+                                    std::string* err) const {
+    return action_ ? action_->AddCommand(args, filename, line, err) : false;
+}
+
+void ActionParser::EndSection() {
+    if (action_ && action_->NumCommands() > 0) {
+        ActionManager::GetInstance().AddAction(std::move(action_));
+    }
+}
diff --git a/init/action.h b/init/action.h
index 5088c71..6dee2d0 100644
--- a/init/action.h
+++ b/init/action.h
@@ -22,13 +22,36 @@
 #include <string>
 #include <vector>
 
+#include "builtins.h"
+#include "init_parser.h"
+#include "keyword_map.h"
+
+class Command {
+public:
+    Command(BuiltinFunction f, const std::vector<std::string>& args,
+            const std::string& filename, int line);
+
+    int InvokeFunc() const;
+    std::string BuildCommandString() const;
+    std::string BuildSourceString() const;
+
+private:
+    BuiltinFunction func_;
+    std::vector<std::string> args_;
+    std::string filename_;
+    int line_;
+};
+
 class Action {
 public:
-    Action();
+    Action(bool oneshot = false);
 
-    void AddCommand(int (*f)(const std::vector<std::string>& args),
+    bool AddCommand(const std::vector<std::string>& args,
+                    const std::string& filename, int line, std::string* err);
+    void AddCommand(BuiltinFunction f,
                     const std::vector<std::string>& args,
                     const std::string& filename = "", int line = 0);
+    void CombineAction(const Action& action);
     bool InitTriggers(const std::vector<std::string>& args, std::string* err);
     bool InitSingleTrigger(const std::string& trigger);
     std::size_t NumCommands() const;
@@ -37,13 +60,17 @@
     bool CheckEventTrigger(const std::string& trigger) const;
     bool CheckPropertyTrigger(const std::string& name,
                               const std::string& value) const;
-    bool TriggersEqual(const class Action& other) const;
+    bool TriggersEqual(const Action& other) const;
     std::string BuildTriggersString() const;
     void DumpState() const;
 
-private:
-    class Command;
+    bool oneshot() const { return oneshot_; }
+    static void set_function_map(const KeywordMap<BuiltinFunction>* function_map) {
+        function_map_ = function_map;
+    }
 
+
+private:
     void ExecuteCommand(const Command& command) const;
     bool CheckPropertyTriggers(const std::string& name = "",
                                const std::string& value = "") const;
@@ -51,27 +78,28 @@
 
     std::map<std::string, std::string> property_triggers_;
     std::string event_trigger_;
-    std::vector<Command*> commands_;
+    std::vector<Command> commands_;
+    bool oneshot_;
+    static const KeywordMap<BuiltinFunction>* function_map_;
 };
 
 class Trigger {
 public:
     virtual ~Trigger() { }
-    virtual bool CheckTriggers(const Action* action) = 0;
+    virtual bool CheckTriggers(const Action& action) const = 0;
 };
 
 class ActionManager {
 public:
     static ActionManager& GetInstance();
+
+    void AddAction(std::unique_ptr<Action> action);
     void QueueEventTrigger(const std::string& trigger);
     void QueuePropertyTrigger(const std::string& name, const std::string& value);
     void QueueAllPropertyTriggers();
-    void QueueBuiltinAction(int (*func)(const std::vector<std::string>& args),
-                            const std::string& name);
+    void QueueBuiltinAction(BuiltinFunction func, const std::string& name);
     void ExecuteOneCommand();
     bool HasMoreCommands() const;
-    Action* AddNewAction(const std::vector<std::string>& triggers,
-                         std::string* err);
     void DumpState() const;
 
 private:
@@ -80,10 +108,26 @@
     ActionManager(ActionManager const&) = delete;
     void operator=(ActionManager const&) = delete;
 
-    std::vector<Action*> actions_;
+    std::vector<std::unique_ptr<Action>> actions_;
     std::queue<std::unique_ptr<Trigger>> trigger_queue_;
-    std::vector<Action*> current_executing_actions_;
+    std::queue<const Action*> current_executing_actions_;
     std::size_t current_command_;
 };
 
+class ActionParser : public SectionParser {
+public:
+    ActionParser() : action_(nullptr) {
+    }
+    bool ParseSection(const std::vector<std::string>& args,
+                      std::string* err) override;
+    bool ParseLineSection(const std::vector<std::string>& args,
+                          const std::string& filename, int line,
+                          std::string* err) const override;
+    void EndSection() override;
+    void EndFile(const std::string&) override {
+    }
+private:
+    std::unique_ptr<Action> action_;
+};
+
 #endif
diff --git a/init/bootchart.cpp b/init/bootchart.cpp
index efaee1c..a768762 100644
--- a/init/bootchart.cpp
+++ b/init/bootchart.cpp
@@ -15,7 +15,6 @@
  */
 
 #include "bootchart.h"
-#include "keywords.h"
 #include "log.h"
 #include "property_service.h"
 
@@ -32,6 +31,7 @@
 
 #include <memory>
 #include <string>
+#include <vector>
 
 #include <base/file.h>
 
diff --git a/init/bootchart.h b/init/bootchart.h
index cf61d83..47eda7a 100644
--- a/init/bootchart.h
+++ b/init/bootchart.h
@@ -17,6 +17,10 @@
 #ifndef _BOOTCHART_H
 #define _BOOTCHART_H
 
+#include <string>
+#include <vector>
+
+int do_bootchart_init(const std::vector<std::string>& args);
 void bootchart_sample(int* timeout);
 
 #endif /* _BOOTCHART_H */
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 97151c0..3ffa2e8 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "builtins.h"
+
 #include <errno.h>
 #include <fcntl.h>
 #include <mntent.h>
@@ -44,10 +46,10 @@
 #include <private/android_filesystem_config.h>
 
 #include "action.h"
+#include "bootchart.h"
 #include "devices.h"
 #include "init.h"
 #include "init_parser.h"
-#include "keywords.h"
 #include "log.h"
 #include "property_service.h"
 #include "service.h"
@@ -60,8 +62,7 @@
 // System call provided by bionic but not in any header file.
 extern "C" int init_module(void *, unsigned long, const char *);
 
-static int insmod(const char *filename, const char *options)
-{
+static int insmod(const char *filename, const char *options) {
     std::string module;
     if (!read_file(filename, &module)) {
         return -1;
@@ -71,8 +72,7 @@
     return init_module(&module[0], module.size(), options);
 }
 
-static int __ifupdown(const char *interface, int up)
-{
+static int __ifupdown(const char *interface, int up) {
     struct ifreq ifr;
     int s, ret;
 
@@ -99,8 +99,7 @@
     return ret;
 }
 
-static void unmount_and_fsck(const struct mntent *entry)
-{
+static void unmount_and_fsck(const struct mntent *entry) {
     if (strcmp(entry->mnt_type, "f2fs") && strcmp(entry->mnt_type, "ext4"))
         return;
 
@@ -160,8 +159,7 @@
     }
 }
 
-int do_class_start(const std::vector<std::string>& args)
-{
+static int do_class_start(const std::vector<std::string>& args) {
         /* Starting a class does not start services
          * which are explicitly disabled.  They must
          * be started individually.
@@ -171,27 +169,23 @@
     return 0;
 }
 
-int do_class_stop(const std::vector<std::string>& args)
-{
+static int do_class_stop(const std::vector<std::string>& args) {
     ServiceManager::GetInstance().
         ForEachServiceInClass(args[1], [] (Service* s) { s->Stop(); });
     return 0;
 }
 
-int do_class_reset(const std::vector<std::string>& args)
-{
+static int do_class_reset(const std::vector<std::string>& args) {
     ServiceManager::GetInstance().
         ForEachServiceInClass(args[1], [] (Service* s) { s->Reset(); });
     return 0;
 }
 
-int do_domainname(const std::vector<std::string>& args)
-{
+static int do_domainname(const std::vector<std::string>& args) {
     return write_file("/proc/sys/kernel/domainname", args[1].c_str());
 }
 
-int do_enable(const std::vector<std::string>& args)
-{
+static int do_enable(const std::vector<std::string>& args) {
     Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
     if (!svc) {
         return -1;
@@ -199,7 +193,7 @@
     return svc->Enable();
 }
 
-int do_exec(const std::vector<std::string>& args) {
+static int do_exec(const std::vector<std::string>& args) {
     Service* svc = ServiceManager::GetInstance().MakeExecOneshotService(args);
     if (!svc) {
         return -1;
@@ -211,23 +205,19 @@
     return 0;
 }
 
-int do_export(const std::vector<std::string>& args)
-{
+static int do_export(const std::vector<std::string>& args) {
     return add_environment(args[1].c_str(), args[2].c_str());
 }
 
-int do_hostname(const std::vector<std::string>& args)
-{
+static int do_hostname(const std::vector<std::string>& args) {
     return write_file("/proc/sys/kernel/hostname", args[1].c_str());
 }
 
-int do_ifup(const std::vector<std::string>& args)
-{
+static int do_ifup(const std::vector<std::string>& args) {
     return __ifupdown(args[1].c_str(), 1);
 }
 
-int do_insmod(const std::vector<std::string>& args)
-{
+static int do_insmod(const std::vector<std::string>& args) {
     std::string options;
 
     if (args.size() > 2) {
@@ -241,8 +231,7 @@
     return insmod(args[1].c_str(), options.c_str());
 }
 
-int do_mkdir(const std::vector<std::string>& args)
-{
+static int do_mkdir(const std::vector<std::string>& args) {
     mode_t mode = 0755;
     int ret;
 
@@ -310,8 +299,7 @@
 #define DATA_MNT_POINT "/data"
 
 /* mount <type> <device> <path> <flags ...> <options> */
-int do_mount(const std::vector<std::string>& args)
-{
+static int do_mount(const std::vector<std::string>& args) {
     char tmp[64];
     const char *source, *target, *system;
     const char *options = NULL;
@@ -411,8 +399,7 @@
 
 }
 
-static int wipe_data_via_recovery()
-{
+static int wipe_data_via_recovery() {
     mkdir("/cache/recovery", 0700);
     int fd = open("/cache/recovery/command", O_RDWR|O_CREAT|O_TRUNC|O_CLOEXEC, 0600);
     if (fd >= 0) {
@@ -427,16 +414,16 @@
     while (1) { pause(); }  // never reached
 }
 
-void import_late()
-{
+static void import_late() {
     static const std::vector<std::string> init_directories = {
         "/system/etc/init",
         "/vendor/etc/init",
         "/odm/etc/init"
     };
 
+    Parser& parser = Parser::GetInstance();
     for (const auto& dir : init_directories) {
-        init_parse_config(dir.c_str());
+        parser.ParseConfig(dir.c_str());
     }
 }
 
@@ -444,17 +431,13 @@
  * This function might request a reboot, in which case it will
  * not return.
  */
-int do_mount_all(const std::vector<std::string>& args)
-{
+static int do_mount_all(const std::vector<std::string>& args) {
     pid_t pid;
     int ret = -1;
     int child_ret = -1;
     int status;
     struct fstab *fstab;
 
-    if (args.size() != 2) {
-        return -1;
-    }
     const char* fstabfile = args[1].c_str();
     /*
      * Call fs_mgr_mount_all() to mount all filesystems.  We fork(2) and
@@ -535,8 +518,7 @@
     return ret;
 }
 
-int do_swapon_all(const std::vector<std::string>& args)
-{
+static int do_swapon_all(const std::vector<std::string>& args) {
     struct fstab *fstab;
     int ret;
 
@@ -547,16 +529,14 @@
     return ret;
 }
 
-int do_setprop(const std::vector<std::string>& args)
-{
+static int do_setprop(const std::vector<std::string>& args) {
     const char* name = args[1].c_str();
     const char* value = args[2].c_str();
     property_set(name, value);
     return 0;
 }
 
-int do_setrlimit(const std::vector<std::string>& args)
-{
+static int do_setrlimit(const std::vector<std::string>& args) {
     struct rlimit limit;
     int resource;
     resource = std::stoi(args[1]);
@@ -565,8 +545,7 @@
     return setrlimit(resource, &limit);
 }
 
-int do_start(const std::vector<std::string>& args)
-{
+static int do_start(const std::vector<std::string>& args) {
     Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
     if (!svc) {
         ERROR("do_start: Service %s not found\n", args[1].c_str());
@@ -577,8 +556,7 @@
     return 0;
 }
 
-int do_stop(const std::vector<std::string>& args)
-{
+static int do_stop(const std::vector<std::string>& args) {
     Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
     if (!svc) {
         ERROR("do_stop: Service %s not found\n", args[1].c_str());
@@ -588,8 +566,7 @@
     return 0;
 }
 
-int do_restart(const std::vector<std::string>& args)
-{
+static int do_restart(const std::vector<std::string>& args) {
     Service* svc = ServiceManager::GetInstance().FindServiceByName(args[1]);
     if (!svc) {
         ERROR("do_restart: Service %s not found\n", args[1].c_str());
@@ -599,8 +576,7 @@
     return 0;
 }
 
-int do_powerctl(const std::vector<std::string>& args)
-{
+static int do_powerctl(const std::vector<std::string>& args) {
     const char* command = args[1].c_str();
     int len = 0;
     unsigned int cmd = 0;
@@ -636,34 +612,26 @@
                                         callback_on_ro_remount);
 }
 
-int do_trigger(const std::vector<std::string>& args)
-{
+static int do_trigger(const std::vector<std::string>& args) {
     ActionManager::GetInstance().QueueEventTrigger(args[1]);
     return 0;
 }
 
-int do_symlink(const std::vector<std::string>& args)
-{
+static int do_symlink(const std::vector<std::string>& args) {
     return symlink(args[1].c_str(), args[2].c_str());
 }
 
-int do_rm(const std::vector<std::string>& args)
-{
+static int do_rm(const std::vector<std::string>& args) {
     return unlink(args[1].c_str());
 }
 
-int do_rmdir(const std::vector<std::string>& args)
-{
+static int do_rmdir(const std::vector<std::string>& args) {
     return rmdir(args[1].c_str());
 }
 
-int do_sysclktz(const std::vector<std::string>& args)
-{
+static int do_sysclktz(const std::vector<std::string>& args) {
     struct timezone tz;
 
-    if (args.size() != 2)
-        return -1;
-
     memset(&tz, 0, sizeof(tz));
     tz.tz_minuteswest = std::stoi(args[1]);
     if (settimeofday(NULL, &tz))
@@ -671,7 +639,7 @@
     return 0;
 }
 
-int do_verity_load_state(const std::vector<std::string>& args) {
+static int do_verity_load_state(const std::vector<std::string>& args) {
     int mode = -1;
     int rc = fs_mgr_load_verity_state(&mode);
     if (rc == 0 && mode == VERITY_MODE_LOGGING) {
@@ -680,24 +648,23 @@
     return rc;
 }
 
-static void verity_update_property(fstab_rec *fstab, const char *mount_point, int mode, int status) {
+static void verity_update_property(fstab_rec *fstab, const char *mount_point,
+                                   int mode, int status) {
     property_set(android::base::StringPrintf("partition.%s.verified", mount_point).c_str(),
                  android::base::StringPrintf("%d", mode).c_str());
 }
 
-int do_verity_update_state(const std::vector<std::string>& args) {
+static int do_verity_update_state(const std::vector<std::string>& args) {
     return fs_mgr_update_verity_state(verity_update_property);
 }
 
-int do_write(const std::vector<std::string>& args)
-{
+static int do_write(const std::vector<std::string>& args) {
     const char* path = args[1].c_str();
     const char* value = args[2].c_str();
     return write_file(path, value);
 }
 
-int do_copy(const std::vector<std::string>& args)
-{
+static int do_copy(const std::vector<std::string>& args) {
     char *buffer = NULL;
     int rc = 0;
     int fd1 = -1, fd2 = -1;
@@ -705,9 +672,6 @@
     int brtw, brtr;
     char *p;
 
-    if (args.size() != 3)
-        return -1;
-
     if (stat(args[1].c_str(), &info) < 0)
         return -1;
 
@@ -758,7 +722,7 @@
     return rc;
 }
 
-int do_chown(const std::vector<std::string>& args) {
+static int do_chown(const std::vector<std::string>& args) {
     /* GID is optional. */
     if (args.size() == 3) {
         if (lchown(args[2].c_str(), decode_uid(args[1].c_str()), -1) == -1)
@@ -786,7 +750,7 @@
     return mode;
 }
 
-int do_chmod(const std::vector<std::string>& args) {
+static int do_chmod(const std::vector<std::string>& args) {
     mode_t mode = get_mode(args[1].c_str());
     if (fchmodat(AT_FDCWD, args[2].c_str(), mode, AT_SYMLINK_NOFOLLOW) < 0) {
         return -errno;
@@ -794,7 +758,7 @@
     return 0;
 }
 
-int do_restorecon(const std::vector<std::string>& args) {
+static int do_restorecon(const std::vector<std::string>& args) {
     int ret = 0;
 
     for (auto it = std::next(args.begin()); it != args.end(); ++it) {
@@ -804,7 +768,7 @@
     return ret;
 }
 
-int do_restorecon_recursive(const std::vector<std::string>& args) {
+static int do_restorecon_recursive(const std::vector<std::string>& args) {
     int ret = 0;
 
     for (auto it = std::next(args.begin()); it != args.end(); ++it) {
@@ -814,12 +778,7 @@
     return ret;
 }
 
-int do_loglevel(const std::vector<std::string>& args) {
-    if (args.size() != 2) {
-        ERROR("loglevel: missing argument\n");
-        return -EINVAL;
-    }
-
+static int do_loglevel(const std::vector<std::string>& args) {
     int log_level = std::stoi(args[1]);
     if (log_level < KLOG_ERROR_LEVEL || log_level > KLOG_DEBUG_LEVEL) {
         ERROR("loglevel: invalid log level'%d'\n", log_level);
@@ -830,23 +789,16 @@
 }
 
 int do_load_persist_props(const std::vector<std::string>& args) {
-    if (args.size() == 1) {
-        load_persist_props();
-        return 0;
-    }
-    return -1;
+    load_persist_props();
+    return 0;
 }
 
-int do_load_all_props(const std::vector<std::string>& args) {
-    if (args.size() == 1) {
-        load_all_props();
-        return 0;
-    }
-    return -1;
+static int do_load_all_props(const std::vector<std::string>& args) {
+    load_all_props();
+    return 0;
 }
 
-int do_wait(const std::vector<std::string>& args)
-{
+static int do_wait(const std::vector<std::string>& args) {
     if (args.size() == 2) {
         return wait_for_file(args[1].c_str(), COMMAND_RETRY_TIMEOUT);
     } else if (args.size() == 3) {
@@ -858,8 +810,7 @@
 /*
  * Callback to make a directory from the ext4 code
  */
-static int do_installkeys_ensure_dir_exists(const char* dir)
-{
+static int do_installkeys_ensure_dir_exists(const char* dir) {
     if (make_dir(dir, 0700) && errno != EEXIST) {
         return -1;
     }
@@ -867,12 +818,7 @@
     return 0;
 }
 
-int do_installkey(const std::vector<std::string>& args)
-{
-    if (args.size() != 2) {
-        return -1;
-    }
-
+static int do_installkey(const std::vector<std::string>& args) {
     std::string prop_value = property_get("ro.crypto.type");
     if (prop_value != "file") {
         return 0;
@@ -881,3 +827,49 @@
     return e4crypt_create_device_key(args[1].c_str(),
                                      do_installkeys_ensure_dir_exists);
 }
+
+BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const {
+    constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
+    static const Map builtin_functions = {
+        {"bootchart_init",          {0,     0,    do_bootchart_init}},
+        {"chmod",                   {2,     2,    do_chmod}},
+        {"chown",                   {2,     3,    do_chown}},
+        {"class_reset",             {1,     1,    do_class_reset}},
+        {"class_start",             {1,     1,    do_class_start}},
+        {"class_stop",              {1,     1,    do_class_stop}},
+        {"copy",                    {2,     2,    do_copy}},
+        {"domainname",              {1,     1,    do_domainname}},
+        {"enable",                  {1,     1,    do_enable}},
+        {"exec",                    {1,     kMax, do_exec}},
+        {"export",                  {2,     2,    do_export}},
+        {"hostname",                {1,     1,    do_hostname}},
+        {"ifup",                    {1,     1,    do_ifup}},
+        {"insmod",                  {1,     kMax, do_insmod}},
+        {"installkey",              {1,     1,    do_installkey}},
+        {"load_all_props",          {0,     0,    do_load_all_props}},
+        {"load_persist_props",      {0,     0,    do_load_persist_props}},
+        {"loglevel",                {1,     1,    do_loglevel}},
+        {"mkdir",                   {1,     4,    do_mkdir}},
+        {"mount_all",               {1,     1,    do_mount_all}},
+        {"mount",                   {3,     kMax, do_mount}},
+        {"powerctl",                {1,     1,    do_powerctl}},
+        {"restart",                 {1,     1,    do_restart}},
+        {"restorecon",              {1,     kMax, do_restorecon}},
+        {"restorecon_recursive",    {1,     kMax, do_restorecon_recursive}},
+        {"rm",                      {1,     1,    do_rm}},
+        {"rmdir",                   {1,     1,    do_rmdir}},
+        {"setprop",                 {2,     2,    do_setprop}},
+        {"setrlimit",               {3,     3,    do_setrlimit}},
+        {"start",                   {1,     1,    do_start}},
+        {"stop",                    {1,     1,    do_stop}},
+        {"swapon_all",              {1,     1,    do_swapon_all}},
+        {"symlink",                 {2,     2,    do_symlink}},
+        {"sysclktz",                {1,     1,    do_sysclktz}},
+        {"trigger",                 {1,     1,    do_trigger}},
+        {"verity_load_state",       {0,     0,    do_verity_load_state}},
+        {"verity_update_state",     {0,     0,    do_verity_update_state}},
+        {"wait",                    {1,     2,    do_wait}},
+        {"write",                   {2,     2,    do_write}},
+    };
+    return builtin_functions;
+}
diff --git a/init/builtins.h b/init/builtins.h
new file mode 100644
index 0000000..53f4a71
--- /dev/null
+++ b/init/builtins.h
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+#ifndef _INIT_BUILTINS_H
+#define _INIT_BUILTINS_H
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "keyword_map.h"
+
+using BuiltinFunction = int (*) (const std::vector<std::string>& args);
+class BuiltinFunctionMap : public KeywordMap<BuiltinFunction> {
+public:
+    BuiltinFunctionMap() {
+    }
+private:
+    Map& map() const override;
+};
+
+#endif
diff --git a/init/import_parser.cpp b/init/import_parser.cpp
new file mode 100644
index 0000000..e2a0f83
--- /dev/null
+++ b/init/import_parser.cpp
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+#include "import_parser.h"
+
+#include "errno.h"
+
+#include <string>
+#include <vector>
+
+#include "log.h"
+#include "util.h"
+
+bool ImportParser::ParseSection(const std::vector<std::string>& args,
+                                std::string* err) {
+    if (args.size() != 2) {
+        *err = "single argument needed for import\n";
+        return false;
+    }
+
+    std::string conf_file;
+    bool ret = expand_props(args[1], &conf_file);
+    if (!ret) {
+        *err = "error while expanding import";
+        return false;
+    }
+
+    INFO("Added '%s' to import list\n", conf_file.c_str());
+    imports_.emplace_back(std::move(conf_file));
+    return true;
+}
+
+void ImportParser::EndFile(const std::string& filename) {
+    auto current_imports = std::move(imports_);
+    imports_.clear();
+    for (const auto& s : current_imports) {
+        if (!Parser::GetInstance().ParseConfig(s)) {
+            ERROR("could not import file '%s' from '%s': %s\n",
+                  s.c_str(), filename.c_str(), strerror(errno));
+        }
+    }
+}
diff --git a/init/import_parser.h b/init/import_parser.h
new file mode 100644
index 0000000..0e91025
--- /dev/null
+++ b/init/import_parser.h
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+#ifndef _INIT_IMPORT_PARSER_H
+#define _INIT_IMPORT_PARSER_H
+
+#include "init_parser.h"
+
+#include <string>
+#include <vector>
+
+class ImportParser : public SectionParser {
+public:
+    ImportParser()  {
+    }
+    bool ParseSection(const std::vector<std::string>& args,
+                      std::string* err) override;
+    bool ParseLineSection(const std::vector<std::string>& args,
+                          const std::string& filename, int line,
+                          std::string* err) const override {
+        return true;
+    }
+    void EndSection() override {
+    }
+    void EndFile(const std::string& filename) override;
+private:
+    std::vector<std::string> imports_;
+};
+
+#endif
diff --git a/init/init.cpp b/init/init.cpp
index c94a6fe..ee1351d 100644
--- a/init/init.cpp
+++ b/init/init.cpp
@@ -55,6 +55,7 @@
 #include "action.h"
 #include "bootchart.h"
 #include "devices.h"
+#include "import_parser.h"
 #include "init.h"
 #include "init_parser.h"
 #include "keychords.h"
@@ -604,7 +605,14 @@
     property_load_boot_defaults();
     start_property_service();
 
-    init_parse_config("/init.rc");
+    const BuiltinFunctionMap function_map;
+    Action::set_function_map(&function_map);
+
+    Parser& parser = Parser::GetInstance();
+    parser.AddSectionParser("service",std::make_unique<ServiceParser>());
+    parser.AddSectionParser("on", std::make_unique<ActionParser>());
+    parser.AddSectionParser("import", std::make_unique<ImportParser>());
+    parser.ParseConfig("/init.rc");
 
     ActionManager& am = ActionManager::GetInstance();
 
diff --git a/init/init_parser.cpp b/init/init_parser.cpp
index 12f44f7..02b3985 100644
--- a/init/init_parser.cpp
+++ b/init/init_parser.cpp
@@ -14,387 +14,117 @@
  * limitations under the License.
  */
 
-#include <ctype.h>
 #include <dirent.h>
 #include <errno.h>
 #include <fcntl.h>
-#include <inttypes.h>
-#include <stdarg.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
 
 #include "action.h"
-#include "init.h"
 #include "init_parser.h"
 #include "log.h"
 #include "parser.h"
-#include "property_service.h"
 #include "service.h"
 #include "util.h"
 
 #include <base/stringprintf.h>
-#include <cutils/iosched_policy.h>
-#include <cutils/list.h>
 
-static list_declare(service_list);
-
-struct import {
-    struct listnode list;
-    const char *filename;
-};
-
-static void *parse_service(struct parse_state *state, int nargs, char **args);
-static void parse_line_service(struct parse_state *state, int nargs, char **args);
-
-static void *parse_action(struct parse_state *state, int nargs, char **args);
-static void parse_line_action(struct parse_state *state, int nargs, char **args);
-
-#define SECTION 0x01
-#define COMMAND 0x02
-#define OPTION  0x04
-
-#include "keywords.h"
-
-#define KEYWORD(symbol, flags, nargs, func) \
-    [ K_##symbol ] = { #symbol, func, nargs + 1, flags, },
-
-static struct {
-    const char *name;
-    int (*func)(const std::vector<std::string>& args);
-    size_t nargs;
-    unsigned char flags;
-} keyword_info[KEYWORD_COUNT] = {
-    [ K_UNKNOWN ] = { "unknown", 0, 0, 0 },
-#include "keywords.h"
-};
-#undef KEYWORD
-
-#define kw_is(kw, type) (keyword_info[kw].flags & (type))
-#define kw_name(kw) (keyword_info[kw].name)
-#define kw_func(kw) (keyword_info[kw].func)
-#define kw_nargs(kw) (keyword_info[kw].nargs)
-
-void dump_parser_state() {
-    ServiceManager::GetInstance().DumpState();
-    ActionManager::GetInstance().DumpState();
+Parser::Parser() {
 }
 
-static int lookup_keyword(const char *s)
-{
-    switch (*s++) {
-    case 'b':
-        if (!strcmp(s, "ootchart_init")) return K_bootchart_init;
-        break;
-    case 'c':
-        if (!strcmp(s, "opy")) return K_copy;
-        if (!strcmp(s, "lass")) return K_class;
-        if (!strcmp(s, "lass_start")) return K_class_start;
-        if (!strcmp(s, "lass_stop")) return K_class_stop;
-        if (!strcmp(s, "lass_reset")) return K_class_reset;
-        if (!strcmp(s, "onsole")) return K_console;
-        if (!strcmp(s, "hown")) return K_chown;
-        if (!strcmp(s, "hmod")) return K_chmod;
-        if (!strcmp(s, "ritical")) return K_critical;
-        break;
-    case 'd':
-        if (!strcmp(s, "isabled")) return K_disabled;
-        if (!strcmp(s, "omainname")) return K_domainname;
-        break;
-    case 'e':
-        if (!strcmp(s, "nable")) return K_enable;
-        if (!strcmp(s, "xec")) return K_exec;
-        if (!strcmp(s, "xport")) return K_export;
-        break;
-    case 'g':
-        if (!strcmp(s, "roup")) return K_group;
-        break;
-    case 'h':
-        if (!strcmp(s, "ostname")) return K_hostname;
-        break;
-    case 'i':
-        if (!strcmp(s, "oprio")) return K_ioprio;
-        if (!strcmp(s, "fup")) return K_ifup;
-        if (!strcmp(s, "nsmod")) return K_insmod;
-        if (!strcmp(s, "mport")) return K_import;
-        if (!strcmp(s, "nstallkey")) return K_installkey;
-        break;
-    case 'k':
-        if (!strcmp(s, "eycodes")) return K_keycodes;
-        break;
-    case 'l':
-        if (!strcmp(s, "oglevel")) return K_loglevel;
-        if (!strcmp(s, "oad_persist_props")) return K_load_persist_props;
-        if (!strcmp(s, "oad_all_props")) return K_load_all_props;
-        break;
-    case 'm':
-        if (!strcmp(s, "kdir")) return K_mkdir;
-        if (!strcmp(s, "ount_all")) return K_mount_all;
-        if (!strcmp(s, "ount")) return K_mount;
-        break;
-    case 'o':
-        if (!strcmp(s, "n")) return K_on;
-        if (!strcmp(s, "neshot")) return K_oneshot;
-        if (!strcmp(s, "nrestart")) return K_onrestart;
-        break;
-    case 'p':
-        if (!strcmp(s, "owerctl")) return K_powerctl;
-        break;
-    case 'r':
-        if (!strcmp(s, "estart")) return K_restart;
-        if (!strcmp(s, "estorecon")) return K_restorecon;
-        if (!strcmp(s, "estorecon_recursive")) return K_restorecon_recursive;
-        if (!strcmp(s, "mdir")) return K_rmdir;
-        if (!strcmp(s, "m")) return K_rm;
-        break;
-    case 's':
-        if (!strcmp(s, "eclabel")) return K_seclabel;
-        if (!strcmp(s, "ervice")) return K_service;
-        if (!strcmp(s, "etenv")) return K_setenv;
-        if (!strcmp(s, "etprop")) return K_setprop;
-        if (!strcmp(s, "etrlimit")) return K_setrlimit;
-        if (!strcmp(s, "ocket")) return K_socket;
-        if (!strcmp(s, "tart")) return K_start;
-        if (!strcmp(s, "top")) return K_stop;
-        if (!strcmp(s, "wapon_all")) return K_swapon_all;
-        if (!strcmp(s, "ymlink")) return K_symlink;
-        if (!strcmp(s, "ysclktz")) return K_sysclktz;
-        break;
-    case 't':
-        if (!strcmp(s, "rigger")) return K_trigger;
-        break;
-    case 'u':
-        if (!strcmp(s, "ser")) return K_user;
-        break;
-    case 'v':
-        if (!strcmp(s, "erity_load_state")) return K_verity_load_state;
-        if (!strcmp(s, "erity_update_state")) return K_verity_update_state;
-        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;
-    }
-    return K_UNKNOWN;
+Parser& Parser::GetInstance() {
+    static Parser instance;
+    return instance;
 }
 
-static void parse_line_no_op(struct parse_state*, int, char**) {
+void Parser::AddSectionParser(const std::string& name,
+                              std::unique_ptr<SectionParser> parser) {
+    section_parsers_[name] = std::move(parser);
 }
 
-int expand_props(const std::string& src, std::string* dst) {
-    const char *src_ptr = src.c_str();
-
-    if (!dst) {
-        return -1;
-    }
-
-    /* - variables can either be $x.y or ${x.y}, in case they are only part
-     *   of the string.
-     * - will accept $$ as a literal $.
-     * - no nested property expansion, i.e. ${foo.${bar}} is not supported,
-     *   bad things will happen
-     */
-    while (*src_ptr) {
-        const char *c;
-
-        c = strchr(src_ptr, '$');
-        if (!c) {
-            dst->append(src_ptr);
-            break;
-        }
-
-        dst->append(src_ptr, c);
-        c++;
-
-        if (*c == '$') {
-            dst->push_back(*(c++));
-            src_ptr = c;
-            continue;
-        } else if (*c == '\0') {
-            break;
-        }
-
-        std::string prop_name;
-        if (*c == '{') {
-            c++;
-            const char* end = strchr(c, '}');
-            if (!end) {
-                // failed to find closing brace, abort.
-                ERROR("unexpected end of string in '%s', looking for }\n", src.c_str());
-                goto err;
-            }
-            prop_name = std::string(c, end);
-            c = end + 1;
-        } else {
-            prop_name = c;
-            ERROR("using deprecated syntax for specifying property '%s', use ${name} instead\n",
-                  c);
-            c += prop_name.size();
-        }
-
-        if (prop_name.empty()) {
-            ERROR("invalid zero-length prop name in '%s'\n", src.c_str());
-            goto err;
-        }
-
-        std::string prop_val = property_get(prop_name.c_str());
-        if (prop_val.empty()) {
-            ERROR("property '%s' doesn't exist while expanding '%s'\n",
-                  prop_name.c_str(), src.c_str());
-            goto err;
-        }
-
-        dst->append(prop_val);
-        src_ptr = c;
-        continue;
-    }
-
-    return 0;
-err:
-    return -1;
-}
-
-static void parse_import(struct parse_state *state, int nargs, char **args)
-{
-    if (nargs != 2) {
-        ERROR("single argument needed for import\n");
-        return;
-    }
-
-    std::string conf_file;
-    int ret = expand_props(args[1], &conf_file);
-    if (ret) {
-        ERROR("error while handling import on line '%d' in '%s'\n",
-              state->line, state->filename);
-        return;
-    }
-
-    struct import* import = (struct import*) calloc(1, sizeof(struct import));
-    import->filename = strdup(conf_file.c_str());
-
-    struct listnode *import_list = (listnode*) state->priv;
-    list_add_tail(import_list, &import->list);
-    INFO("Added '%s' to import list\n", import->filename);
-}
-
-static void parse_new_section(struct parse_state *state, int kw,
-                       int nargs, char **args)
-{
-    printf("[ %s %s ]\n", args[0],
-           nargs > 1 ? args[1] : "");
-    switch(kw) {
-    case K_service:
-        state->context = parse_service(state, nargs, args);
-        if (state->context) {
-            state->parse_line = parse_line_service;
-            return;
-        }
-        break;
-    case K_on:
-        state->context = parse_action(state, nargs, args);
-        if (state->context) {
-            state->parse_line = parse_line_action;
-            return;
-        }
-        break;
-    case K_import:
-        parse_import(state, nargs, args);
-        break;
-    }
-    state->parse_line = parse_line_no_op;
-}
-
-static void parse_config(const char *fn, const std::string& data)
-{
-    struct listnode import_list;
-    struct listnode *node;
-    char *args[INIT_PARSER_MAXARGS];
-
-    int nargs = 0;
-
+void Parser::ParseData(const std::string& filename, const std::string& data) {
     //TODO: Use a parser with const input and remove this copy
     std::vector<char> data_copy(data.begin(), data.end());
     data_copy.push_back('\0');
 
     parse_state state;
-    state.filename = fn;
+    state.filename = filename.c_str();
     state.line = 0;
     state.ptr = &data_copy[0];
     state.nexttoken = 0;
-    state.parse_line = parse_line_no_op;
 
-    list_init(&import_list);
-    state.priv = &import_list;
+    SectionParser* section_parser = nullptr;
+    std::vector<std::string> args;
 
     for (;;) {
         switch (next_token(&state)) {
         case T_EOF:
-            state.parse_line(&state, 0, 0);
-            goto parser_done;
+            if (section_parser) {
+                section_parser->EndSection();
+            }
+            return;
         case T_NEWLINE:
             state.line++;
-            if (nargs) {
-                int kw = lookup_keyword(args[0]);
-                if (kw_is(kw, SECTION)) {
-                    state.parse_line(&state, 0, 0);
-                    parse_new_section(&state, kw, nargs, args);
-                } else {
-                    state.parse_line(&state, nargs, args);
-                }
-                nargs = 0;
+            if (args.empty()) {
+                break;
             }
+            if (section_parsers_.count(args[0])) {
+                if (section_parser) {
+                    section_parser->EndSection();
+                }
+                section_parser = section_parsers_[args[0]].get();
+                std::string ret_err;
+                if (!section_parser->ParseSection(args, &ret_err)) {
+                    parse_error(&state, "%s\n", ret_err.c_str());
+                    section_parser = nullptr;
+                }
+            } else if (section_parser) {
+                std::string ret_err;
+                if (!section_parser->ParseLineSection(args, state.filename,
+                                                      state.line, &ret_err)) {
+                    parse_error(&state, "%s\n", ret_err.c_str());
+                }
+            }
+            args.clear();
             break;
         case T_TEXT:
-            if (nargs < INIT_PARSER_MAXARGS) {
-                args[nargs++] = state.text;
-            }
+            args.emplace_back(state.text);
             break;
         }
     }
-
-parser_done:
-    list_for_each(node, &import_list) {
-         struct import* import = node_to_item(node, struct import, list);
-         if (!init_parse_config(import->filename)) {
-             ERROR("could not import file '%s' from '%s': %s\n",
-                   import->filename, fn, strerror(errno));
-         }
-    }
 }
 
-static bool init_parse_config_file(const char* path) {
-    INFO("Parsing file %s...\n", path);
+bool Parser::ParseConfigFile(const std::string& path) {
+    INFO("Parsing file %s...\n", path.c_str());
     Timer t;
     std::string data;
-    if (!read_file(path, &data)) {
+    if (!read_file(path.c_str(), &data)) {
         return false;
     }
 
     data.push_back('\n'); // TODO: fix parse_config.
-    parse_config(path, data);
-    dump_parser_state();
+    ParseData(path, data);
+    for (const auto& sp : section_parsers_) {
+        sp.second->EndFile(path);
+    }
+    DumpState();
 
-    NOTICE("(Parsing %s took %.2fs.)\n", path, t.duration());
+    NOTICE("(Parsing %s took %.2fs.)\n", path.c_str(), t.duration());
     return true;
 }
 
-static bool init_parse_config_dir(const char* path) {
-    INFO("Parsing directory %s...\n", path);
-    std::unique_ptr<DIR, int(*)(DIR*)> config_dir(opendir(path), closedir);
+bool Parser::ParseConfigDir(const std::string& path) {
+    INFO("Parsing directory %s...\n", path.c_str());
+    std::unique_ptr<DIR, int(*)(DIR*)> config_dir(opendir(path.c_str()), closedir);
     if (!config_dir) {
-        ERROR("Could not import directory '%s'\n", path);
+        ERROR("Could not import directory '%s'\n", path.c_str());
         return false;
     }
     dirent* current_file;
     while ((current_file = readdir(config_dir.get()))) {
         std::string current_path =
-            android::base::StringPrintf("%s/%s", path, current_file->d_name);
+            android::base::StringPrintf("%s/%s", path.c_str(), current_file->d_name);
         // Ignore directories and only process regular files.
         if (current_file->d_type == DT_REG) {
-            if (!init_parse_config_file(current_path.c_str())) {
+            if (!ParseConfigFile(current_path)) {
                 ERROR("could not import file '%s'\n", current_path.c_str());
             }
         }
@@ -402,97 +132,14 @@
     return true;
 }
 
-bool init_parse_config(const char* path) {
-    if (is_dir(path)) {
-        return init_parse_config_dir(path);
+bool Parser::ParseConfig(const std::string& path) {
+    if (is_dir(path.c_str())) {
+        return ParseConfigDir(path);
     }
-    return init_parse_config_file(path);
+    return ParseConfigFile(path);
 }
 
-static void *parse_service(struct parse_state *state, int nargs, char **args)
-{
-    if (nargs < 3) {
-        parse_error(state, "services must have a name and a program\n");
-        return nullptr;
-    }
-    std::vector<std::string> str_args(args + 2, args + nargs);
-    std::string ret_err;
-    Service* svc = ServiceManager::GetInstance().AddNewService(args[1], "default",
-                                                               str_args, &ret_err);
-
-    if (!svc) {
-        parse_error(state, "%s\n", ret_err.c_str());
-    }
-
-    return svc;
-}
-
-static void parse_line_service(struct parse_state *state, int nargs, char **args)
-{
-    if (nargs == 0) {
-        return;
-    }
-
-    Service* svc = static_cast<Service*>(state->context);
-    int kw = lookup_keyword(args[0]);
-    std::vector<std::string> str_args(args, args + nargs);
-    std::string ret_err;
-    bool ret = svc->HandleLine(kw, str_args, &ret_err);
-
-    if (!ret) {
-        parse_error(state, "%s\n", ret_err.c_str());
-    }
-}
-
-static void *parse_action(struct parse_state* state, int nargs, char **args)
-{
-    std::string ret_err;
-    std::vector<std::string> triggers(args + 1, args + nargs);
-    Action* ret = ActionManager::GetInstance().AddNewAction(triggers, &ret_err);
-
-    if (!ret) {
-        parse_error(state, "%s\n", ret_err.c_str());
-    }
-
-    return ret;
-}
-
-bool add_command_to_action(Action* action, const std::vector<std::string>& args,
-                           const std::string& filename, int line, std::string* err)
-{
-    int kw;
-    size_t n;
-
-    kw = lookup_keyword(args[0].c_str());
-    if (!kw_is(kw, COMMAND)) {
-        *err = android::base::StringPrintf("invalid command '%s'\n", args[0].c_str());
-        return false;
-    }
-
-    n = kw_nargs(kw);
-    if (args.size() < n) {
-        *err = android::base::StringPrintf("%s requires %zu %s\n",
-                                           args[0].c_str(), n - 1,
-                                           n > 2 ? "arguments" : "argument");
-        return false;
-    }
-
-    action->AddCommand(kw_func(kw), args, filename, line);
-    return true;
-}
-
-static void parse_line_action(struct parse_state* state, int nargs, char **args)
-{
-    if (nargs == 0) {
-        return;
-    }
-
-    Action* action = static_cast<Action*>(state->context);
-    std::vector<std::string> str_args(args, args + nargs);
-    std::string ret_err;
-    bool ret = add_command_to_action(action, str_args, state->filename,
-                                     state->line, &ret_err);
-    if (!ret) {
-        parse_error(state, "%s\n", ret_err.c_str());
-    }
+void Parser::DumpState() const {
+    ServiceManager::GetInstance().DumpState();
+    ActionManager::GetInstance().DumpState();
 }
diff --git a/init/init_parser.h b/init/init_parser.h
index 709dca8..5ed30ad 100644
--- a/init/init_parser.h
+++ b/init/init_parser.h
@@ -17,16 +17,39 @@
 #ifndef _INIT_INIT_PARSER_H_
 #define _INIT_INIT_PARSER_H_
 
+#include <map>
 #include <string>
 #include <vector>
 
-#define INIT_PARSER_MAXARGS 64
+class SectionParser {
+public:
+    virtual ~SectionParser() {
+    }
+    virtual bool ParseSection(const std::vector<std::string>& args,
+                              std::string* err) = 0;
+    virtual bool ParseLineSection(const std::vector<std::string>& args,
+                                  const std::string& filename, int line,
+                                  std::string* err) const = 0;
+    virtual void EndSection() = 0;
+    virtual void EndFile(const std::string& filename) = 0;
+};
 
-class Action;
+class Parser {
+public:
+    static Parser& GetInstance();
+    void DumpState() const;
+    bool ParseConfig(const std::string& path);
+    void AddSectionParser(const std::string& name,
+                          std::unique_ptr<SectionParser> parser);
 
-bool init_parse_config(const char* path);
-int expand_props(const std::string& src, std::string* dst);
-bool add_command_to_action(Action* action, const std::vector<std::string>& args,
-                           const std::string& filename, int line, std::string* err);
+private:
+    Parser();
+
+    void ParseData(const std::string& filename, const std::string& data);
+    bool ParseConfigFile(const std::string& path);
+    bool ParseConfigDir(const std::string& path);
+
+    std::map<std::string, std::unique_ptr<SectionParser>> section_parsers_;
+};
 
 #endif
diff --git a/init/keyword_map.h b/init/keyword_map.h
new file mode 100644
index 0000000..dc2357b
--- /dev/null
+++ b/init/keyword_map.h
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+
+#ifndef _INIT_KEYWORD_MAP_H_
+#define _INIT_KEYWORD_MAP_H_
+
+#include <map>
+#include <string>
+
+#include <base/stringprintf.h>
+
+template <typename Function>
+class KeywordMap {
+public:
+    using FunctionInfo = std::tuple<std::size_t, std::size_t, Function>;
+    using Map = const std::map<std::string, FunctionInfo>;
+
+    virtual ~KeywordMap() {
+    }
+
+    const Function FindFunction(const std::string& keyword,
+                                size_t num_args,
+                                std::string* err) const {
+        using android::base::StringPrintf;
+
+        auto function_info_it = map().find(keyword);
+        if (function_info_it == map().end()) {
+            *err = StringPrintf("invalid keyword '%s'", keyword.c_str());
+            return nullptr;
+        }
+
+        auto function_info = function_info_it->second;
+
+        auto min_args = std::get<0>(function_info);
+        auto max_args = std::get<1>(function_info);
+        if (min_args == max_args && num_args != min_args) {
+            *err = StringPrintf("%s requires %zu argument%s",
+                                keyword.c_str(), min_args,
+                                (min_args > 1 || min_args == 0) ? "s" : "");
+            return nullptr;
+        }
+
+        if (num_args < min_args || num_args > max_args) {
+            if (max_args == std::numeric_limits<decltype(max_args)>::max()) {
+                *err = StringPrintf("%s requires at least %zu argument%s",
+                                    keyword.c_str(), min_args,
+                                    min_args > 1 ? "s" : "");
+            } else {
+                *err = StringPrintf("%s requires between %zu and %zu arguments",
+                                    keyword.c_str(), min_args, max_args);
+            }
+            return nullptr;
+        }
+
+        return std::get<Function>(function_info);
+    }
+
+private:
+//Map of keyword ->
+//(minimum number of arguments, maximum number of arguments, function pointer)
+    virtual Map& map() const = 0;
+};
+
+#endif
diff --git a/init/keywords.h b/init/keywords.h
deleted file mode 100644
index 922feee..0000000
--- a/init/keywords.h
+++ /dev/null
@@ -1,109 +0,0 @@
-#ifndef KEYWORD
-#include <string>
-#include <vector>
-int do_bootchart_init(const std::vector<std::string>& args);
-int do_class_start(const std::vector<std::string>& args);
-int do_class_stop(const std::vector<std::string>& args);
-int do_class_reset(const std::vector<std::string>& args);
-int do_domainname(const std::vector<std::string>& args);
-int do_enable(const std::vector<std::string>& args);
-int do_exec(const std::vector<std::string>& args);
-int do_export(const std::vector<std::string>& args);
-int do_hostname(const std::vector<std::string>& args);
-int do_ifup(const std::vector<std::string>& args);
-int do_insmod(const std::vector<std::string>& args);
-int do_installkey(const std::vector<std::string>& args);
-int do_mkdir(const std::vector<std::string>& args);
-int do_mount_all(const std::vector<std::string>& args);
-int do_mount(const std::vector<std::string>& args);
-int do_powerctl(const std::vector<std::string>& args);
-int do_restart(const std::vector<std::string>& args);
-int do_restorecon(const std::vector<std::string>& args);
-int do_restorecon_recursive(const std::vector<std::string>& args);
-int do_rm(const std::vector<std::string>& args);
-int do_rmdir(const std::vector<std::string>& args);
-int do_setprop(const std::vector<std::string>& args);
-int do_setrlimit(const std::vector<std::string>& args);
-int do_start(const std::vector<std::string>& args);
-int do_stop(const std::vector<std::string>& args);
-int do_swapon_all(const std::vector<std::string>& args);
-int do_trigger(const std::vector<std::string>& args);
-int do_symlink(const std::vector<std::string>& args);
-int do_sysclktz(const std::vector<std::string>& args);
-int do_write(const std::vector<std::string>& args);
-int do_copy(const std::vector<std::string>& args);
-int do_chown(const std::vector<std::string>& args);
-int do_chmod(const std::vector<std::string>& args);
-int do_loglevel(const std::vector<std::string>& args);
-int do_load_persist_props(const std::vector<std::string>& args);
-int do_load_all_props(const std::vector<std::string>& args);
-int do_verity_load_state(const std::vector<std::string>& args);
-int do_verity_update_state(const std::vector<std::string>& args);
-int do_wait(const std::vector<std::string>& args);
-#define __MAKE_KEYWORD_ENUM__
-#define KEYWORD(symbol, flags, nargs, func) K_##symbol,
-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(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)
-    KEYWORD(enable,      COMMAND, 1, do_enable)
-    KEYWORD(exec,        COMMAND, 1, do_exec)
-    KEYWORD(export,      COMMAND, 2, do_export)
-    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(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(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)
-    KEYWORD(restorecon_recursive,  COMMAND, 1, do_restorecon_recursive)
-    KEYWORD(rm,          COMMAND, 1, do_rm)
-    KEYWORD(rmdir,       COMMAND, 1, do_rmdir)
-    KEYWORD(seclabel,    OPTION,  0, 0)
-    KEYWORD(service,     SECTION, 0, 0)
-    KEYWORD(setenv,      OPTION,  2, 0)
-    KEYWORD(setprop,     COMMAND, 2, do_setprop)
-    KEYWORD(setrlimit,   COMMAND, 3, do_setrlimit)
-    KEYWORD(socket,      OPTION,  0, 0)
-    KEYWORD(start,       COMMAND, 1, do_start)
-    KEYWORD(stop,        COMMAND, 1, do_stop)
-    KEYWORD(swapon_all,  COMMAND, 1, do_swapon_all)
-    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(writepid,    OPTION,  0, 0)
-#ifdef __MAKE_KEYWORD_ENUM__
-    KEYWORD_COUNT,
-};
-#undef __MAKE_KEYWORD_ENUM__
-#undef KEYWORD
-#endif
diff --git a/init/readme.txt b/init/readme.txt
index d70c6f3..bf440c2 100644
--- a/init/readme.txt
+++ b/init/readme.txt
@@ -2,8 +2,8 @@
 Android Init Language
 ---------------------
 
-The Android Init Language consists of four broad classes of statements,
-which are Actions, Commands, Services, and Options.
+The Android Init Language consists of five broad classes of statements,
+which are Actions, Commands, Services, Options, and Imports.
 
 All of these are line-oriented, consisting of tokens separated by
 whitespace.  The c-style backslash escapes may be used to insert
@@ -17,9 +17,37 @@
 or options belong to the section most recently declared.  Commands
 or options before the first section are ignored.
 
-Actions and Services have unique names.  If a second Action or Service
-is declared with the same name as an existing one, it is ignored as
-an error.  (??? should we override instead)
+Actions and Services have unique names.  If a second Action is defined
+with the same name as an existing one, its commands are appended to
+the commands of the existing action.  If a second Service is defined
+with the same name as an existing one, it is ignored and an error
+message is logged.
+
+
+Init .rc Files
+--------------
+The init language is used in plaintext files that take the .rc file
+extension.  These are typically multiple of these in multiple
+locations on the system, described below.
+
+/init.rc is the primary .rc file and is loaded by the init executable
+at the beginning of its execution.  It is responsible for the initial
+set up of the system.  It imports /init.${ro.hardware}.rc which is the
+primary vendor supplied .rc file.
+
+During the mount_all command, the init executable loads all of the
+files contained within the /{system,vendor,odm}/etc/init/ directories.
+These directories are intended for all Actions and Services used after
+file system mounting.
+
+The intention of these directories is as follows
+   1) /system/etc/init/ is for core system items such as
+      SurfaceFlinger and MediaService.
+   2) /vendor/etc/init/ is for SoC vendor items such as actions or
+      daemons needed for core SoC functionality.
+   3) /odm/etc/init/ is for device manufacturer items such as
+      actions or daemons needed for motion sensor or other peripheral
+      functionality.
 
 
 Actions
@@ -37,7 +65,7 @@
 
 Actions take the form of:
 
-on <trigger>
+on <trigger> [&& <trigger>]*
    <command>
    <command>
    <command>
@@ -117,25 +145,36 @@
 
 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
-   (after /init.conf is loaded)
+Triggers are subdivided into event triggers and property triggers.
 
-<name>=<value>
-   Triggers of this form occur when the property <name> is set
-   to the specific value <value>.
+Event triggers are strings triggered by the 'trigger' command or by
+the QueueEventTrigger() function within the init executable.  These
+take the form of a simple string such as 'boot' or 'late-init'.
 
-   One can also test multiple properties to execute a group
-   of commands. For example:
+Property triggers are strings triggered when a named property changes
+value to a given new value or when a named property changes value to
+any new value.  These take the form of 'property:<name>=<value>' and
+'property:<name>=*' respectively.  Property triggers are additionally
+evaluated and triggered accordingly during the initial boot phase of
+init.
 
-   on property:test.a=1 && property:test.b=1
-       setprop test.c 1
+An Action can have multiple property triggers but may only have one
+event trigger.
 
-   The above stub sets test.c to 1 only when
-   both test.a=1 and test.b=1
+For example:
+'on boot && property:a=b' defines an action that is only executed when
+the 'boot' event trigger happens and the property a equals b.
+
+'on property:a=b && property:c=d' defines an action that is executed
+at three times,
+   1) During initial boot if property a=b and property c=d
+   2) Any time that property a transitions to value b, while property
+      c already equals d.
+   3) Any time that property c transitions to value d, while property
+      a already equals b.
 
 
 Commands
@@ -197,12 +236,6 @@
 ifup <interface>
    Bring the network interface <interface> online.
 
-import <path>
-   Parse an init config file, extending the current configuration.
-   If <path> is a directory, each file in the directory is parsed as
-   a config file. It is not recursive, nested directories will
-   not be parsed.
-
 insmod <path>
    Install the module at <path>
 
@@ -304,19 +337,30 @@
    it will be truncated. Properties are expanded within <content>.
 
 
+Imports
+-------
+The import keyword is not a command, but rather its own section and is
+handled immediately after the .rc file that contains it has finished
+being parsed.  It takes the below form:
+
+import <path>
+   Parse an init config file, extending the current configuration.
+   If <path> is a directory, each file in the directory is parsed as
+   a config file. It is not recursive, nested directories will
+   not be parsed.
+
+There are only two times where the init executable imports .rc files,
+   1) When it imports /init.rc during initial boot
+   2) When it imports /{system,vendor,odm}/etc/init/ during mount_all
+
+
 Properties
 ----------
-Init updates some system properties to provide some insight into
-what it's doing:
-
-init.action
-   Equal to the name of the action currently being executed or "" if none
-
-init.command
-   Equal to the command being executed or "" if none.
+Init provides information about the services that it is responsible
+for via the below properties.
 
 init.svc.<name>
-   State of a named service ("stopped", "running", "restarting")
+   State of a named service ("stopped", "stopping", "running", "restarting")
 
 
 Bootcharting
diff --git a/init/service.cpp b/init/service.cpp
index a370d25..a3c5ca4 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -32,11 +32,13 @@
 #include "action.h"
 #include "init.h"
 #include "init_parser.h"
-#include "keywords.h"
 #include "log.h"
 #include "property_service.h"
 #include "util.h"
 
+using android::base::StringPrintf;
+using android::base::WriteStringToFile;
+
 #define CRITICAL_CRASH_THRESHOLD    4       // if we crash >4 times ...
 #define CRITICAL_CRASH_WINDOW       (4*60)  // ... in 4 minutes, goto recovery
 
@@ -84,7 +86,7 @@
         return;
     }
 
-    std::string prop_name = android::base::StringPrintf("init.svc.%s", name_.c_str());
+    std::string prop_name = StringPrintf("init.svc.%s", name_.c_str());
     if (prop_name.length() >= PROP_NAME_MAX) {
         // If the property name would be too long, we can't set it.
         ERROR("Property name \"init.svc.%s\" too long; not setting to %s\n",
@@ -104,8 +106,7 @@
 
     // Remove any sockets we may have created.
     for (const auto& si : sockets_) {
-        std::string tmp = android::base::StringPrintf(ANDROID_SOCKET_DIR "/%s",
-                                                      si.name.c_str());
+        std::string tmp = StringPrintf(ANDROID_SOCKET_DIR "/%s", si.name.c_str());
         unlink(tmp.c_str());
     }
 
@@ -168,148 +169,156 @@
     }
 }
 
-bool Service::HandleLine(int kw, const std::vector<std::string>& args, std::string* err) {
-    std::vector<std::string> str_args;
+bool Service::HandleClass(const std::vector<std::string>& args, std::string* err) {
+    classname_ = args[1];
+    return true;
+}
 
-    ioprio_class_ = IoSchedClass_NONE;
+bool Service::HandleConsole(const std::vector<std::string>& args, std::string* err) {
+    flags_ |= SVC_CONSOLE;
+    return true;
+}
 
-    switch (kw) {
-    case K_class:
-        if (args.size() != 2) {
-            *err = "class option requires a classname\n";
-            return false;
-        } else {
-            classname_ = args[1];
-        }
-        break;
-    case K_console:
-        flags_ |= SVC_CONSOLE;
-        break;
-    case K_disabled:
-        flags_ |= SVC_DISABLED;
-        flags_ |= SVC_RC_DISABLED;
-        break;
-    case K_ioprio:
-        if (args.size() != 3) {
-            *err = "ioprio optin usage: ioprio <rt|be|idle> <ioprio 0-7>\n";
-            return false;
-        } else {
-            ioprio_pri_ = std::stoul(args[2], 0, 8);
+bool Service::HandleCritical(const std::vector<std::string>& args, std::string* err) {
+    flags_ |= SVC_CRITICAL;
+    return true;
+}
 
-            if (ioprio_pri_ < 0 || ioprio_pri_ > 7) {
-                *err = "priority value must be range 0 - 7\n";
-                return false;
-            }
+bool Service::HandleDisabled(const std::vector<std::string>& args, std::string* err) {
+    flags_ |= SVC_DISABLED;
+    flags_ |= SVC_RC_DISABLED;
+    return true;
+}
 
-            if (args[1] == "rt") {
-                ioprio_class_ = IoSchedClass_RT;
-            } else if (args[1] == "be") {
-                ioprio_class_ = IoSchedClass_BE;
-            } else if (args[1] == "idle") {
-                ioprio_class_ = IoSchedClass_IDLE;
-            } else {
-                *err = "ioprio option usage: ioprio <rt|be|idle> <0-7>\n";
-                return false;
-            }
-        }
-        break;
-    case K_group:
-        if (args.size() < 2) {
-            *err = "group option requires a group id\n";
-            return false;
-        } else if (args.size() > NR_SVC_SUPP_GIDS + 2) {
-            *err = android::base::StringPrintf("group option accepts at most %d supp. groups\n",
-                                               NR_SVC_SUPP_GIDS);
-            return false;
-        } else {
-            gid_ = decode_uid(args[1].c_str());
-            for (std::size_t n = 2; n < args.size(); n++) {
-                supp_gids_.push_back(decode_uid(args[n].c_str()));
-            }
-        }
-        break;
-    case K_keycodes:
-        if (args.size() < 2) {
-            *err = "keycodes option requires atleast one keycode\n";
-            return false;
-        } else {
-            for (std::size_t i = 1; i < args.size(); i++) {
-                keycodes_.push_back(std::stoi(args[i]));
-            }
-        }
-        break;
-    case K_oneshot:
-        flags_ |= SVC_ONESHOT;
-        break;
-    case K_onrestart:
-        if (args.size() < 2) {
-            return false;
-        }
-        str_args.assign(args.begin() + 1, args.end());
-        add_command_to_action(&onrestart_, str_args, "", 0, err);
-        break;
-    case K_critical:
-        flags_ |= SVC_CRITICAL;
-        break;
-    case K_setenv: { /* name value */
-        if (args.size() < 3) {
-            *err = "setenv option requires name and value arguments\n";
-            return false;
-        }
-
-        envvars_.push_back({args[1], args[2]});
-        break;
-    }
-    case K_socket: {/* name type perm [ uid gid context ] */
-        if (args.size() < 4) {
-            *err = "socket option requires name, type, perm arguments\n";
-            return false;
-        }
-        if (args[2] != "dgram" && args[2] != "stream" &&
-            args[2] != "seqpacket") {
-            *err = "socket type must be 'dgram', 'stream' or 'seqpacket'\n";
-            return false;
-        }
-
-        int perm = std::stoul(args[3], 0, 8);
-        uid_t uid = args.size() > 4 ? decode_uid(args[4].c_str()) : 0;
-        gid_t gid = args.size() > 5 ? decode_uid(args[5].c_str()) : 0;
-        std::string socketcon = args.size() > 6 ? args[6] : "";
-
-        sockets_.push_back({args[1], args[2], uid, gid, perm, socketcon});
-        break;
-    }
-    case K_user:
-        if (args.size() != 2) {
-            *err = "user option requires a user id\n";
-            return false;
-        } else {
-            uid_ = decode_uid(args[1].c_str());
-        }
-        break;
-    case K_seclabel:
-        if (args.size() != 2) {
-            *err = "seclabel option requires a label string\n";
-            return false;
-        } else {
-            seclabel_ = args[1];
-        }
-        break;
-    case K_writepid:
-        if (args.size() < 2) {
-            *err = "writepid option requires at least one filename\n";
-            return false;
-        }
-        writepid_files_.assign(args.begin() + 1, args.end());
-        break;
-
-    default:
-        *err = android::base::StringPrintf("invalid option '%s'\n", args[0].c_str());
-        return false;
+bool Service::HandleGroup(const std::vector<std::string>& args, std::string* err) {
+    gid_ = decode_uid(args[1].c_str());
+    for (std::size_t n = 2; n < args.size(); n++) {
+        supp_gids_.emplace_back(decode_uid(args[n].c_str()));
     }
     return true;
 }
 
+bool Service::HandleIoprio(const std::vector<std::string>& args, std::string* err) {
+    ioprio_pri_ = std::stoul(args[2], 0, 8);
+
+    if (ioprio_pri_ < 0 || ioprio_pri_ > 7) {
+        *err = "priority value must be range 0 - 7";
+        return false;
+    }
+
+    if (args[1] == "rt") {
+        ioprio_class_ = IoSchedClass_RT;
+    } else if (args[1] == "be") {
+        ioprio_class_ = IoSchedClass_BE;
+    } else if (args[1] == "idle") {
+        ioprio_class_ = IoSchedClass_IDLE;
+    } else {
+        *err = "ioprio option usage: ioprio <rt|be|idle> <0-7>";
+        return false;
+    }
+
+    return true;
+}
+
+bool Service::HandleKeycodes(const std::vector<std::string>& args, std::string* err) {
+    for (std::size_t i = 1; i < args.size(); i++) {
+        keycodes_.emplace_back(std::stoi(args[i]));
+    }
+    return true;
+}
+
+bool Service::HandleOneshot(const std::vector<std::string>& args, std::string* err) {
+    flags_ |= SVC_ONESHOT;
+    return true;
+}
+
+bool Service::HandleOnrestart(const std::vector<std::string>& args, std::string* err) {
+    std::vector<std::string> str_args(args.begin() + 1, args.end());
+    onrestart_.AddCommand(str_args, "", 0, err);
+    return true;
+}
+
+bool Service::HandleSeclabel(const std::vector<std::string>& args, std::string* err) {
+    seclabel_ = args[1];
+    return true;
+}
+
+bool Service::HandleSetenv(const std::vector<std::string>& args, std::string* err) {
+    envvars_.emplace_back(args[1], args[2]);
+    return true;
+}
+
+/* name type perm [ uid gid context ] */
+bool Service::HandleSocket(const std::vector<std::string>& args, std::string* err) {
+    if (args[2] != "dgram" && args[2] != "stream" && args[2] != "seqpacket") {
+        *err = "socket type must be 'dgram', 'stream' or 'seqpacket'";
+        return false;
+    }
+
+    int perm = std::stoul(args[3], 0, 8);
+    uid_t uid = args.size() > 4 ? decode_uid(args[4].c_str()) : 0;
+    gid_t gid = args.size() > 5 ? decode_uid(args[5].c_str()) : 0;
+    std::string socketcon = args.size() > 6 ? args[6] : "";
+
+    sockets_.emplace_back(args[1], args[2], uid, gid, perm, socketcon);
+    return true;
+}
+
+bool Service::HandleUser(const std::vector<std::string>& args, std::string* err) {
+    uid_ = decode_uid(args[1].c_str());
+    return true;
+}
+
+bool Service::HandleWritepid(const std::vector<std::string>& args, std::string* err) {
+    writepid_files_.assign(args.begin() + 1, args.end());
+    return true;
+}
+
+class Service::OptionHandlerMap : public KeywordMap<OptionHandler> {
+public:
+    OptionHandlerMap() {
+    }
+private:
+    Map& map() const override;
+};
+
+Service::OptionHandlerMap::Map& Service::OptionHandlerMap::map() const {
+    constexpr std::size_t kMax = std::numeric_limits<std::size_t>::max();
+    static const Map option_handlers = {
+        {"class",       {1,     1,    &Service::HandleClass}},
+        {"console",     {0,     0,    &Service::HandleConsole}},
+        {"critical",    {0,     0,    &Service::HandleCritical}},
+        {"disabled",    {0,     0,    &Service::HandleDisabled}},
+        {"group",       {1,     NR_SVC_SUPP_GIDS + 1, &Service::HandleGroup}},
+        {"ioprio",      {2,     2,    &Service::HandleIoprio}},
+        {"keycodes",    {1,     kMax, &Service::HandleKeycodes}},
+        {"oneshot",     {0,     0,    &Service::HandleOneshot}},
+        {"onrestart",   {1,     kMax, &Service::HandleOnrestart}},
+        {"seclabel",    {1,     1,    &Service::HandleSeclabel}},
+        {"setenv",      {2,     2,    &Service::HandleSetenv}},
+        {"socket",      {3,     6,    &Service::HandleSocket}},
+        {"user",        {1,     1,    &Service::HandleUser}},
+        {"writepid",    {1,     kMax, &Service::HandleWritepid}},
+    };
+    return option_handlers;
+}
+
+bool Service::HandleLine(const std::vector<std::string>& args, std::string* err) {
+    if (args.empty()) {
+        *err = "option needed, but not provided";
+        return false;
+    }
+
+    static const OptionHandlerMap handler_map;
+    auto handler = handler_map.FindFunction(args[0], args.size() - 1, err);
+
+    if (!handler) {
+        return false;
+    }
+
+    return (this->*handler)(args, err);
+}
+
 bool Service::Start(const std::vector<std::string>& dynamic_args) {
     // Starting a service removes it from the disabled or reset state and
     // immediately takes it out of the restarting state if it was in there.
@@ -396,7 +405,7 @@
         umask(077);
         if (properties_initialized()) {
             get_property_workspace(&fd, &sz);
-            std::string tmp = android::base::StringPrintf("%d,%d", dup(fd), sz);
+            std::string tmp = StringPrintf("%d,%d", dup(fd), sz);
             add_environment("ANDROID_PROPERTY_WORKSPACE", tmp.c_str());
         }
 
@@ -418,9 +427,9 @@
             }
         }
 
-        std::string pid_str = android::base::StringPrintf("%d", pid);
+        std::string pid_str = StringPrintf("%d", pid);
         for (const auto& file : writepid_files_) {
-            if (!android::base::WriteStringToFile(pid_str, file)) {
+            if (!WriteStringToFile(pid_str, file)) {
                 ERROR("couldn't write %s to %s: %s\n",
                       pid_str.c_str(), file.c_str(), strerror(errno));
             }
@@ -609,9 +618,8 @@
 }
 
 void Service::PublishSocket(const std::string& name, int fd) const {
-    std::string key = android::base::StringPrintf(ANDROID_SOCKET_ENV_PREFIX "%s",
-                                                  name.c_str());
-    std::string val = android::base::StringPrintf("%d", fd);
+    std::string key = StringPrintf(ANDROID_SOCKET_ENV_PREFIX "%s", name.c_str());
+    std::string val = StringPrintf("%d", fd);
     add_environment(key.c_str(), val.c_str());
 
     /* make sure we don't close-on-exec */
@@ -628,31 +636,14 @@
     return instance;
 }
 
-Service* ServiceManager::AddNewService(const std::string& name,
-                                       const std::string& classname,
-                                       const std::vector<std::string>& args,
-                                       std::string* err) {
-    if (!IsValidName(name)) {
-        *err = android::base::StringPrintf("invalid service name '%s'\n", name.c_str());
-        return nullptr;
+void ServiceManager::AddService(std::unique_ptr<Service> service) {
+    Service* old_service = FindServiceByName(service->name());
+    if (old_service) {
+        ERROR("ignored duplicate definition of service '%s'",
+              service->name().c_str());
+        return;
     }
-
-    Service* svc = ServiceManager::GetInstance().FindServiceByName(name);
-    if (svc) {
-        *err = android::base::StringPrintf("ignored duplicate definition of service '%s'\n",
-                                           name.c_str());
-        return nullptr;
-    }
-
-    std::unique_ptr<Service> svc_p(new Service(name, classname, args));
-    if (!svc_p) {
-        ERROR("Couldn't allocate service for service '%s'", name.c_str());
-        return nullptr;
-    }
-    svc = svc_p.get();
-    services_.push_back(std::move(svc_p));
-
-    return svc;
+    services_.emplace_back(std::move(service));
 }
 
 Service* ServiceManager::MakeExecOneshotService(const std::vector<std::string>& args) {
@@ -677,8 +668,7 @@
     std::vector<std::string> str_args(args.begin() + command_arg, args.end());
 
     exec_count_++;
-    std::string name = android::base::StringPrintf("exec %d (%s)", exec_count_,
-                                                   str_args[0].c_str());
+    std::string name = StringPrintf("exec %d (%s)", exec_count_, str_args[0].c_str());
     unsigned flags = SVC_EXEC | SVC_ONESHOT;
 
     std::string seclabel = "";
@@ -770,8 +760,7 @@
     }
 }
 
-void ServiceManager::RemoveService(const Service& svc)
-{
+void ServiceManager::RemoveService(const Service& svc) {
     auto svc_it = std::find_if(services_.begin(), services_.end(),
                                [&svc] (const std::unique_ptr<Service>& s) {
                                    return svc.name() == s->name();
@@ -783,8 +772,44 @@
     services_.erase(svc_it);
 }
 
-bool ServiceManager::IsValidName(const std::string& name) const
-{
+void ServiceManager::DumpState() const {
+    for (const auto& s : services_) {
+        s->DumpState();
+    }
+    INFO("\n");
+}
+
+bool ServiceParser::ParseSection(const std::vector<std::string>& args,
+                                 std::string* err) {
+    if (args.size() < 3) {
+        *err = "services must have a name and a program";
+        return false;
+    }
+
+    const std::string& name = args[1];
+    if (!IsValidName(name)) {
+        *err = StringPrintf("invalid service name '%s'", name.c_str());
+        return false;
+    }
+
+    std::vector<std::string> str_args(args.begin() + 2, args.end());
+    service_ = std::make_unique<Service>(name, "default", str_args);
+    return true;
+}
+
+bool ServiceParser::ParseLineSection(const std::vector<std::string>& args,
+                                     const std::string& filename, int line,
+                                     std::string* err) const {
+    return service_ ? service_->HandleLine(args, err) : false;
+}
+
+void ServiceParser::EndSection() {
+    if (service_) {
+        ServiceManager::GetInstance().AddService(std::move(service_));
+    }
+}
+
+bool ServiceParser::IsValidName(const std::string& name) const {
     if (name.size() > 16) {
         return false;
     }
@@ -795,11 +820,3 @@
     }
     return true;
 }
-
-void ServiceManager::DumpState() const
-{
-    for (const auto& s : services_) {
-        s->DumpState();
-    }
-    INFO("\n");
-}
diff --git a/init/service.h b/init/service.h
index 1ecf78a..10eb736 100644
--- a/init/service.h
+++ b/init/service.h
@@ -26,6 +26,8 @@
 #include <vector>
 
 #include "action.h"
+#include "init_parser.h"
+#include "keyword_map.h"
 
 #define SVC_DISABLED       0x001  // do not autostart with class
 #define SVC_ONESHOT        0x002  // do not restart on exit
@@ -73,7 +75,7 @@
             unsigned flags, uid_t uid, gid_t gid, const std::vector<gid_t>& supp_gids,
             const std::string& seclabel,  const std::vector<std::string>& args);
 
-    bool HandleLine(int kw, const std::vector<std::string>& args, std::string* err);
+    bool HandleLine(const std::vector<std::string>& args, std::string* err);
     bool Start(const std::vector<std::string>& dynamic_args);
     bool Start();
     bool StartIfNotDisabled();
@@ -99,12 +101,31 @@
     const std::vector<std::string>& args() const { return args_; }
 
 private:
+    using OptionHandler = bool (Service::*) (const std::vector<std::string>& args,
+                                             std::string* err);
+    class OptionHandlerMap;
+
     void NotifyStateChange(const std::string& new_state) const;
     void StopOrReset(int how);
     void ZapStdio() const;
     void OpenConsole() const;
     void PublishSocket(const std::string& name, int fd) const;
 
+    bool HandleClass(const std::vector<std::string>& args, std::string* err);
+    bool HandleConsole(const std::vector<std::string>& args, std::string* err);
+    bool HandleCritical(const std::vector<std::string>& args, std::string* err);
+    bool HandleDisabled(const std::vector<std::string>& args, std::string* err);
+    bool HandleGroup(const std::vector<std::string>& args, std::string* err);
+    bool HandleIoprio(const std::vector<std::string>& args, std::string* err);
+    bool HandleKeycodes(const std::vector<std::string>& args, std::string* err);
+    bool HandleOneshot(const std::vector<std::string>& args, std::string* err);
+    bool HandleOnrestart(const std::vector<std::string>& args, std::string* err);
+    bool HandleSeclabel(const std::vector<std::string>& args, std::string* err);
+    bool HandleSetenv(const std::vector<std::string>& args, std::string* err);
+    bool HandleSocket(const std::vector<std::string>& args, std::string* err);
+    bool HandleUser(const std::vector<std::string>& args, std::string* err);
+    bool HandleWritepid(const std::vector<std::string>& args, std::string* err);
+
     std::string name_;
     std::string classname_;
 
@@ -141,9 +162,7 @@
 public:
     static ServiceManager& GetInstance();
 
-    Service* AddNewService(const std::string& name, const std::string& classname,
-                                           const std::vector<std::string>& args,
-                                           std::string* err);
+    void AddService(std::unique_ptr<Service> service);
     Service* MakeExecOneshotService(const std::vector<std::string>& args);
     Service* FindServiceByName(const std::string& name) const;
     Service* FindServiceByPid(pid_t pid) const;
@@ -155,13 +174,30 @@
                              void (*func)(Service* svc)) const;
     void RemoveService(const Service& svc);
     void DumpState() const;
+
 private:
     ServiceManager();
 
-    bool IsValidName(const std::string& name) const;
-
     static int exec_count_; // Every service needs a unique name.
     std::vector<std::unique_ptr<Service>> services_;
 };
 
+class ServiceParser : public SectionParser {
+public:
+    ServiceParser() : service_(nullptr) {
+    }
+    bool ParseSection(const std::vector<std::string>& args,
+                      std::string* err) override;
+    bool ParseLineSection(const std::vector<std::string>& args,
+                          const std::string& filename, int line,
+                          std::string* err) const override;
+    void EndSection() override;
+    void EndFile(const std::string&) override {
+    }
+private:
+    bool IsValidName(const std::string& name) const;
+
+    std::unique_ptr<Service> service_;
+};
+
 #endif
diff --git a/init/ueventd_parser.cpp b/init/ueventd_parser.cpp
index 497c606..09f4638 100644
--- a/init/ueventd_parser.cpp
+++ b/init/ueventd_parser.cpp
@@ -233,7 +233,6 @@
 
     data.push_back('\n'); // TODO: fix parse_config.
     parse_config(fn, data);
-    dump_parser_state();
     return 0;
 }
 
diff --git a/init/util.cpp b/init/util.cpp
index f6131e3..1eb90e0 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -43,6 +43,7 @@
 
 #include "init.h"
 #include "log.h"
+#include "property_service.h"
 #include "util.h"
 
 /*
@@ -476,3 +477,73 @@
     }
     return S_ISDIR(info.st_mode);
 }
+
+bool expand_props(const std::string& src, std::string* dst) {
+    const char* src_ptr = src.c_str();
+
+    if (!dst) {
+        return false;
+    }
+
+    /* - variables can either be $x.y or ${x.y}, in case they are only part
+     *   of the string.
+     * - will accept $$ as a literal $.
+     * - no nested property expansion, i.e. ${foo.${bar}} is not supported,
+     *   bad things will happen
+     */
+    while (*src_ptr) {
+        const char* c;
+
+        c = strchr(src_ptr, '$');
+        if (!c) {
+            dst->append(src_ptr);
+            return true;
+        }
+
+        dst->append(src_ptr, c);
+        c++;
+
+        if (*c == '$') {
+            dst->push_back(*(c++));
+            src_ptr = c;
+            continue;
+        } else if (*c == '\0') {
+            return true;
+        }
+
+        std::string prop_name;
+        if (*c == '{') {
+            c++;
+            const char* end = strchr(c, '}');
+            if (!end) {
+                // failed to find closing brace, abort.
+                ERROR("unexpected end of string in '%s', looking for }\n", src.c_str());
+                return false;
+            }
+            prop_name = std::string(c, end);
+            c = end + 1;
+        } else {
+            prop_name = c;
+            ERROR("using deprecated syntax for specifying property '%s', use ${name} instead\n",
+                  c);
+            c += prop_name.size();
+        }
+
+        if (prop_name.empty()) {
+            ERROR("invalid zero-length prop name in '%s'\n", src.c_str());
+            return false;
+        }
+
+        std::string prop_val = property_get(prop_name.c_str());
+        if (prop_val.empty()) {
+            ERROR("property '%s' doesn't exist while expanding '%s'\n",
+                  prop_name.c_str(), src.c_str());
+            return false;
+        }
+
+        dst->append(prop_val);
+        src_ptr = c;
+    }
+
+    return true;
+}
diff --git a/init/util.h b/init/util.h
index f08cb8d..a33209e 100644
--- a/init/util.h
+++ b/init/util.h
@@ -65,4 +65,5 @@
 int restorecon_recursive(const char *pathname);
 std::string bytes_to_hex(const uint8_t *bytes, size_t bytes_len);
 bool is_dir(const char* pathname);
+bool expand_props(const std::string& src, std::string* dst);
 #endif
diff --git a/libcutils/Android.mk b/libcutils/Android.mk
index f9060c4..015bd3f 100644
--- a/libcutils/Android.mk
+++ b/libcutils/Android.mk
@@ -37,19 +37,7 @@
 # some files must not be compiled when building against Mingw
 # they correspond to features not used by our host development tools
 # which are also hard or even impossible to port to native Win32
-WINDOWS_HOST_ONLY :=
-ifeq ($(HOST_OS),windows)
-    ifeq ($(strip $(USE_CYGWIN)),)
-        WINDOWS_HOST_ONLY := 1
-    endif
-endif
-# USE_MINGW is defined when we build against Mingw on Linux
-ifneq ($(strip $(USE_MINGW)),)
-    WINDOWS_HOST_ONLY := 1
-endif
-
-ifneq ($(WINDOWS_HOST_ONLY),1)
-    commonSources += \
+nonWindowsSources := \
         fs.c \
         multiuser.c \
         socket_inaddr_any_server.c \
@@ -60,31 +48,32 @@
         socket_network_client.c \
         sockets.c \
 
-    commonHostSources += \
+nonWindowsHostSources := \
         ashmem-host.c \
         trace-host.c
 
-endif
-
 
 # Shared and static library for host
 # ========================================================
 LOCAL_MODULE := libcutils
-LOCAL_SRC_FILES := $(commonSources) $(commonHostSources) dlmalloc_stubs.c
+LOCAL_SRC_FILES := $(commonSources) dlmalloc_stubs.c
+LOCAL_SRC_FILES_darwin := $(nonWindowsSources) $(nonWindowsHostSources)
+LOCAL_SRC_FILES_linux := $(nonWindowsSources) $(nonWindowsHostSources)
 LOCAL_STATIC_LIBRARIES := liblog
-ifneq ($(HOST_OS),windows)
-LOCAL_CFLAGS += -Werror -Wall -Wextra
-endif
+LOCAL_CFLAGS_darwin := -Werror -Wall -Wextra
+LOCAL_CFLAGS_linux := -Werror -Wall -Wextra
 LOCAL_MULTILIB := both
+LOCAL_MODULE_HOST_OS := darwin linux windows
 include $(BUILD_HOST_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
 LOCAL_MODULE := libcutils
-LOCAL_SRC_FILES := $(commonSources) $(commonHostSources) dlmalloc_stubs.c
+LOCAL_SRC_FILES := $(commonSources) dlmalloc_stubs.c
+LOCAL_SRC_FILES_darwin := $(nonWindowsSources) $(nonWindowsHostSources)
+LOCAL_SRC_FILES_linux := $(nonWindowsSources) $(nonWindowsHostSources)
 LOCAL_SHARED_LIBRARIES := liblog
-ifneq ($(HOST_OS),windows)
-LOCAL_CFLAGS += -Werror -Wall -Wextra
-endif
+LOCAL_CFLAGS_darwin := -Werror -Wall -Wextra
+LOCAL_CFLAGS_linux := -Werror -Wall -Wextra
 LOCAL_MULTILIB := both
 include $(BUILD_HOST_SHARED_LIBRARY)
 
@@ -96,6 +85,7 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE := libcutils
 LOCAL_SRC_FILES := $(commonSources) \
+        $(nonWindowsSources) \
         android_reboot.c \
         ashmem-dev.c \
         debugger.c \
diff --git a/libcutils/fs_config.c b/libcutils/fs_config.c
index 5f6f8f9..27981ff 100644
--- a/libcutils/fs_config.c
+++ b/libcutils/fs_config.c
@@ -135,8 +135,8 @@
     { 04770, AID_ROOT,      AID_RADIO,     0, "system/bin/pppd-ril" },
 
     /* the following files have enhanced capabilities and ARE included in user builds. */
-    { 00750, AID_ROOT,      AID_SHELL,     (1ULL << CAP_SETUID) | (1ULL << CAP_SETGID), "system/bin/run-as" },
-    { 00700, AID_SYSTEM,    AID_SHELL,     (1ULL << CAP_BLOCK_SUSPEND), "system/bin/inputflinger" },
+    { 00750, AID_ROOT,      AID_SHELL,     CAP_MASK_LONG(CAP_SETUID) | CAP_MASK_LONG(CAP_SETGID), "system/bin/run-as" },
+    { 00700, AID_SYSTEM,    AID_SHELL,     CAP_MASK_LONG(CAP_BLOCK_SUSPEND), "system/bin/inputflinger" },
 
     { 00750, AID_ROOT,      AID_ROOT,      0, "system/bin/uncrypt" },
     { 00750, AID_ROOT,      AID_ROOT,      0, "system/bin/install-recovery.sh" },
diff --git a/liblog/Android.mk b/liblog/Android.mk
index 5eed634..3a1a9db 100644
--- a/liblog/Android.mk
+++ b/liblog/Android.mk
@@ -24,41 +24,30 @@
 # so make sure we do not regret hard-coding it as follows:
 liblog_cflags := -DLIBLOG_LOG_TAG=1005
 
-liblog_sources := logd_write.c log_event_write.c
-
-# some files must not be compiled when building against Mingw
-# they correspond to features not used by our host development tools
-# which are also hard or even impossible to port to native Win32
-
-ifeq ($(strip $(USE_MINGW)),)
-    liblog_sources += \
-        event_tag_map.c
-else
-    liblog_sources += \
-        uio.c
-endif
-
-liblog_host_sources := $(liblog_sources) fake_log_device.c event.logtags
-liblog_target_sources := $(liblog_sources) log_time.cpp log_is_loggable.c
-ifeq ($(strip $(USE_MINGW)),)
+liblog_host_sources := logd_write.c log_event_write.c fake_log_device.c event.logtags
+liblog_target_sources := logd_write.c log_event_write.c event_tag_map.c log_time.cpp log_is_loggable.c
 liblog_target_sources += logprint.c
-endif
 liblog_target_sources += log_read.c
 
 # Shared and static library for host
 # ========================================================
 LOCAL_MODULE := liblog
 LOCAL_SRC_FILES := $(liblog_host_sources)
+# some files must not be compiled when building against Mingw
+# they correspond to features not used by our host development tools
+# which are also hard or even impossible to port to native Win32
+LOCAL_SRC_FILES_darwin := event_tag_map.c
+LOCAL_SRC_FILES_linux := event_tag_map.c
+LOCAL_SRC_FILES_windows := uio.c
 LOCAL_CFLAGS := -DFAKE_LOG_DEVICE=1 -Werror $(liblog_cflags)
 LOCAL_MULTILIB := both
+LOCAL_MODULE_HOST_OS := darwin linux windows
 include $(BUILD_HOST_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
 LOCAL_MODULE := liblog
 LOCAL_WHOLE_STATIC_LIBRARIES := liblog
-ifeq ($(strip $(HOST_OS)),linux)
-LOCAL_LDLIBS := -lrt
-endif
+LOCAL_LDLIBS_linux := -lrt
 LOCAL_MULTILIB := both
 LOCAL_CXX_STL := none
 include $(BUILD_HOST_SHARED_LIBRARY)
diff --git a/libsparse/Android.mk b/libsparse/Android.mk
index 925b98b..c77eba9 100644
--- a/libsparse/Android.mk
+++ b/libsparse/Android.mk
@@ -18,6 +18,7 @@
 LOCAL_STATIC_LIBRARIES := libz
 LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
 LOCAL_CFLAGS := -Werror
+LOCAL_MODULE_HOST_OS := darwin linux windows
 include $(BUILD_HOST_STATIC_LIBRARY)
 
 
diff --git a/libutils/Android.mk b/libutils/Android.mk
index 8226db2..1039096 100644
--- a/libutils/Android.mk
+++ b/libutils/Android.mk
@@ -43,27 +43,19 @@
 
 host_commonCflags := -DLIBUTILS_NATIVE=1 $(TOOL_CFLAGS) -Werror
 
-ifeq ($(HOST_OS),windows)
-ifeq ($(strip $(USE_CYGWIN),),)
-# Under MinGW, ctype.h doesn't need multi-byte support
-host_commonCflags += -DMB_CUR_MAX=1
-endif
-endif
-
 # For the host
 # =====================================================
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES:= $(commonSources)
-ifeq ($(HOST_OS), linux)
-LOCAL_SRC_FILES += Looper.cpp
-endif
-ifeq ($(HOST_OS),darwin)
-LOCAL_CFLAGS += -Wno-unused-parameter
-endif
+LOCAL_SRC_FILES_linux := Looper.cpp
+LOCAL_CFLAGS_darwin := -Wno-unused-parameter
 LOCAL_MODULE:= libutils
 LOCAL_STATIC_LIBRARIES := liblog
 LOCAL_CFLAGS += $(host_commonCflags)
+# Under MinGW, ctype.h doesn't need multi-byte support
+LOCAL_CFLAGS_windows := -DMB_CUR_MAX=1
 LOCAL_MULTILIB := both
+LOCAL_MODULE_HOST_OS := darwin linux windows
 include $(BUILD_HOST_STATIC_LIBRARY)
 
 
diff --git a/libziparchive/Android.mk b/libziparchive/Android.mk
index 559c48b..608ff1c 100644
--- a/libziparchive/Android.mk
+++ b/libziparchive/Android.mk
@@ -33,10 +33,9 @@
 LOCAL_STATIC_LIBRARIES := libz libutils libbase
 LOCAL_MODULE:= libziparchive-host
 LOCAL_CFLAGS := -Werror
-ifneq ($(strip $(USE_MINGW)),)
-	LOCAL_CFLAGS += -mno-ms-bitfields
-endif
+LOCAL_CFLAGS_windows := -mno-ms-bitfields
 LOCAL_MULTILIB := both
+LOCAL_MODULE_HOST_OS := darwin linux windows
 include $(BUILD_HOST_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index c609870..a93369e 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -413,6 +413,12 @@
                 it = f->second;
             }
         }
+        static const timespec too_old = {
+            EXPIRE_HOUR_THRESHOLD * 60 * 60, 0
+        };
+        LogBufferElementCollection::iterator lastt;
+        lastt = mLogElements.end();
+        --lastt;
         LogBufferElementLast last;
         while (it != mLogElements.end()) {
             LogBufferElement *e = *it;
@@ -464,6 +470,11 @@
                 continue;
             }
 
+            if ((e->getRealTime() < ((*lastt)->getRealTime() - too_old))
+                    || (e->getRealTime() > (*lastt)->getRealTime())) {
+                break;
+            }
+
             // unmerged drop message
             if (dropped) {
                 last.add(e);
@@ -477,18 +488,6 @@
             }
 
             if (e->getUid() != worst) {
-                if (leading) {
-                    static const timespec too_old = {
-                        EXPIRE_HOUR_THRESHOLD * 60 * 60, 0
-                    };
-                    LogBufferElementCollection::iterator last;
-                    last = mLogElements.end();
-                    --last;
-                    if ((e->getRealTime() < ((*last)->getRealTime() - too_old))
-                            || (e->getRealTime() > (*last)->getRealTime())) {
-                        break;
-                    }
-                }
                 leading = false;
                 last.clear(e);
                 ++it;
diff --git a/metricsd/Android.mk b/metricsd/Android.mk
index 89fa222..12dfa18 100644
--- a/metricsd/Android.mk
+++ b/metricsd/Android.mk
@@ -41,6 +41,28 @@
   serialization/metric_sample.cc \
   serialization/serialization_utils.cc
 
+metrics_tests_sources := \
+  metrics_daemon.cc \
+  metrics_daemon_test.cc \
+  metrics_library_test.cc \
+  persistent_integer.cc \
+  persistent_integer_test.cc \
+  serialization/metric_sample.cc \
+  serialization/serialization_utils.cc \
+  serialization/serialization_utils_unittest.cc \
+  timer.cc \
+  timer_test.cc \
+  uploader/metrics_hashes.cc \
+  uploader/metrics_hashes_unittest.cc \
+  uploader/metrics_log_base.cc \
+  uploader/metrics_log_base_unittest.cc \
+  uploader/metrics_log.cc \
+  uploader/mock/sender_mock.cc \
+  uploader/sender_http.cc \
+  uploader/system_profile_cache.cc \
+  uploader/upload_service.cc \
+  uploader/upload_service_test.cc \
+
 metrics_CFLAGS := -Wall \
   -Wno-char-subscripts \
   -Wno-missing-field-initializers \
@@ -125,4 +147,25 @@
 include $(BUILD_PREBUILT)
 endif # INITRC_TEMPLATE
 
+# Unit tests for metrics.
+# ========================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := metrics_tests
+LOCAL_CFLAGS := $(metrics_CFLAGS)
+LOCAL_CPP_EXTENSION := $(metrics_cpp_extension)
+LOCAL_CPPFLAGS := $(metrics_CPPFLAGS) -Wno-sign-compare
+LOCAL_RTTI_FLAG := -frtti
+LOCAL_SHARED_LIBRARIES := $(metrics_shared_libraries) \
+  libmetrics \
+  libprotobuf-cpp-lite \
+  libchromeos-http \
+  libchromeos-dbus \
+  libcutils \
+  libdbus \
+
+LOCAL_SRC_FILES := $(metrics_tests_sources)
+LOCAL_STATIC_LIBRARIES := libBionicGtestMain libgmock metrics_daemon_protos
+
+include $(BUILD_NATIVE_TEST)
+
 endif # HOST_OS == linux
diff --git a/metricsd/include/metrics/metrics_library.h b/metricsd/include/metrics/metrics_library.h
index 4917a7c..a956b69 100644
--- a/metricsd/include/metrics/metrics_library.h
+++ b/metricsd/include/metrics/metrics_library.h
@@ -129,6 +129,9 @@
   FRIEND_TEST(MetricsLibraryTest, SendMessageToChrome);
   FRIEND_TEST(MetricsLibraryTest, SendMessageToChromeUMAEventsBadFileLocation);
 
+  void InitForTest(const std::string& uma_events_file,
+                   const std::string& consent_file);
+
   // Sets |*result| to whether or not the |mounts_file| indicates that
   // the |device_name| is currently mounted.  Uses |buffer| of
   // |buffer_size| to read the file.  Returns false if any error.
diff --git a/metricsd/metrics_daemon.cc b/metricsd/metrics_daemon.cc
index 5855cee..2838119 100644
--- a/metricsd/metrics_daemon.cc
+++ b/metricsd/metrics_daemon.cc
@@ -51,8 +51,6 @@
 
 namespace {
 
-#define SAFE_MESSAGE(e) (e.message ? e.message : "unknown error")
-
 const char kCrashReporterInterface[] = "org.chromium.CrashReporter";
 const char kCrashReporterUserCrashSignal[] = "UserCrash";
 const char kCrashReporterMatchRule[] =
@@ -73,63 +71,53 @@
 const char kUncleanShutdownDetectedFile[] =
     "/var/run/unclean-shutdown-detected";
 
-}  // namespace
-
 // disk stats metrics
 
 // The {Read,Write}Sectors numbers are in sectors/second.
 // A sector is usually 512 bytes.
 
-const char MetricsDaemon::kMetricReadSectorsLongName[] =
-    "Platform.ReadSectorsLong";
-const char MetricsDaemon::kMetricWriteSectorsLongName[] =
-    "Platform.WriteSectorsLong";
-const char MetricsDaemon::kMetricReadSectorsShortName[] =
-    "Platform.ReadSectorsShort";
-const char MetricsDaemon::kMetricWriteSectorsShortName[] =
-    "Platform.WriteSectorsShort";
+const char kMetricReadSectorsLongName[] = "Platform.ReadSectorsLong";
+const char kMetricWriteSectorsLongName[] = "Platform.WriteSectorsLong";
+const char kMetricReadSectorsShortName[] = "Platform.ReadSectorsShort";
+const char kMetricWriteSectorsShortName[] = "Platform.WriteSectorsShort";
 
-const int MetricsDaemon::kMetricStatsShortInterval = 1;  // seconds
-const int MetricsDaemon::kMetricStatsLongInterval = 30;  // seconds
-
-const int MetricsDaemon::kMetricMeminfoInterval = 30;        // seconds
+const int kMetricStatsShortInterval = 1;  // seconds
+const int kMetricStatsLongInterval = 30;  // seconds
 
 // Assume a max rate of 250Mb/s for reads (worse for writes) and 512 byte
 // sectors.
-const int MetricsDaemon::kMetricSectorsIOMax = 500000;  // sectors/second
-const int MetricsDaemon::kMetricSectorsBuckets = 50;    // buckets
+const int kMetricSectorsIOMax = 500000;  // sectors/second
+const int kMetricSectorsBuckets = 50;    // buckets
 // Page size is 4k, sector size is 0.5k.  We're not interested in page fault
 // rates that the disk cannot sustain.
-const int MetricsDaemon::kMetricPageFaultsMax = kMetricSectorsIOMax / 8;
-const int MetricsDaemon::kMetricPageFaultsBuckets = 50;
+const int kMetricPageFaultsMax = kMetricSectorsIOMax / 8;
+const int kMetricPageFaultsBuckets = 50;
 
 // Major page faults, i.e. the ones that require data to be read from disk.
 
-const char MetricsDaemon::kMetricPageFaultsLongName[] =
-    "Platform.PageFaultsLong";
-const char MetricsDaemon::kMetricPageFaultsShortName[] =
-    "Platform.PageFaultsShort";
+const char kMetricPageFaultsLongName[] = "Platform.PageFaultsLong";
+const char kMetricPageFaultsShortName[] = "Platform.PageFaultsShort";
 
 // Swap in and Swap out
 
-const char MetricsDaemon::kMetricSwapInLongName[] =
-    "Platform.SwapInLong";
-const char MetricsDaemon::kMetricSwapInShortName[] =
-    "Platform.SwapInShort";
+const char kMetricSwapInLongName[] = "Platform.SwapInLong";
+const char kMetricSwapInShortName[] = "Platform.SwapInShort";
 
-const char MetricsDaemon::kMetricSwapOutLongName[] =
-    "Platform.SwapOutLong";
-const char MetricsDaemon::kMetricSwapOutShortName[] =
-    "Platform.SwapOutShort";
+const char kMetricSwapOutLongName[] = "Platform.SwapOutLong";
+const char kMetricSwapOutShortName[] = "Platform.SwapOutShort";
 
-const char MetricsDaemon::kMetricsProcStatFileName[] = "/proc/stat";
-const int MetricsDaemon::kMetricsProcStatFirstLineItemsCount = 11;
+const char kMetricsProcStatFileName[] = "/proc/stat";
+const char kVmStatFileName[] = "/proc/vmstat";
+const char kMeminfoFileName[] = "/proc/meminfo";
+const int kMetricsProcStatFirstLineItemsCount = 11;
 
 // Thermal CPU throttling.
 
-const char MetricsDaemon::kMetricScaledCpuFrequencyName[] =
+const char kMetricScaledCpuFrequencyName[] =
     "Platform.CpuFrequencyThermalScaling";
 
+}  // namespace
+
 // Zram sysfs entries.
 
 const char MetricsDaemon::kComprDataSizeName[] = "compr_data_size";
@@ -195,10 +183,10 @@
 }
 
 void MetricsDaemon::RunUploaderTest() {
-  upload_service_.reset(new UploadService(new SystemProfileCache(true,
-                                                                 config_root_),
-                                          metrics_lib_,
-                                          server_));
+  upload_service_.reset(new UploadService(
+      new SystemProfileCache(true, base::FilePath(config_root_)),
+      metrics_lib_,
+      server_));
   upload_service_->Init(upload_interval_, metrics_file_);
   upload_service_->UploadEvent();
 }
@@ -227,18 +215,17 @@
                          bool uploader_active,
                          bool dbus_enabled,
                          MetricsLibraryInterface* metrics_lib,
-                         const string& vmstats_path,
                          const string& scaling_max_freq_path,
                          const string& cpuinfo_max_freq_path,
                          const base::TimeDelta& upload_interval,
                          const string& server,
                          const string& metrics_file,
                          const string& config_root) {
+  CHECK(metrics_lib);
   testing_ = testing;
   uploader_active_ = uploader_active;
   dbus_enabled_ = dbus_enabled;
   config_root_ = config_root;
-  DCHECK(metrics_lib != nullptr);
   metrics_lib_ = metrics_lib;
 
   upload_interval_ = upload_interval;
@@ -286,7 +273,6 @@
   weekly_cycle_.reset(new PersistentInteger("weekly.cycle"));
   version_cycle_.reset(new PersistentInteger("version.cycle"));
 
-  vmstats_path_ = vmstats_path;
   scaling_max_freq_path_ = scaling_max_freq_path;
   cpuinfo_max_freq_path_ = cpuinfo_max_freq_path;
 }
@@ -510,48 +496,22 @@
 
 bool MetricsDaemon::VmStatsParseStats(const char* stats,
                                       struct VmstatRecord* record) {
-  // a mapping of string name to field in VmstatRecord and whether we found it
-  struct mapping {
-    const string name;
-    uint64_t* value_p;
-    bool found;
-  } map[] =
-      { { .name = "pgmajfault",
-          .value_p = &record->page_faults_,
-          .found = false },
-        { .name = "pswpin",
-          .value_p = &record->swap_in_,
-          .found = false },
-        { .name = "pswpout",
-          .value_p = &record->swap_out_,
-          .found = false }, };
+  CHECK(stats);
+  CHECK(record);
+  base::StringPairs pairs;
+  base::SplitStringIntoKeyValuePairs(stats, ' ', '\n', &pairs);
 
-  // Each line in the file has the form
-  // <ID> <VALUE>
-  // for instance:
-  // nr_free_pages 213427
-  vector<string> lines;
-  Tokenize(stats, "\n", &lines);
-  for (vector<string>::iterator it = lines.begin();
-       it != lines.end(); ++it) {
-    vector<string> tokens;
-    base::SplitString(*it, ' ', &tokens);
-    if (tokens.size() == 2) {
-      for (unsigned int i = 0; i < sizeof(map)/sizeof(struct mapping); i++) {
-        if (!tokens[0].compare(map[i].name)) {
-          if (!base::StringToUint64(tokens[1], map[i].value_p))
-            return false;
-          map[i].found = true;
-        }
-      }
-    } else {
-      LOG(WARNING) << "unexpected vmstat format";
+  for (base::StringPairs::iterator it = pairs.begin(); it != pairs.end(); ++it) {
+    if (it->first == "pgmajfault" &&
+        !base::StringToUint64(it->second, &record->page_faults_)) {
+      return false;
     }
-  }
-  // make sure we got all the stats
-  for (unsigned i = 0; i < sizeof(map)/sizeof(struct mapping); i++) {
-    if (map[i].found == false) {
-      LOG(WARNING) << "vmstat missing " << map[i].name;
+    if (it->first == "pswpin" &&
+        !base::StringToUint64(it->second, &record->swap_in_)) {
+      return false;
+    }
+    if (it->first == "pswpout" &&
+        !base::StringToUint64(it->second, &record->swap_out_)) {
       return false;
     }
   }
@@ -559,14 +519,12 @@
 }
 
 bool MetricsDaemon::VmStatsReadStats(struct VmstatRecord* stats) {
+  CHECK(stats);
   string value_string;
-  FilePath* path = new FilePath(vmstats_path_);
-  if (!base::ReadFileToString(*path, &value_string)) {
-    delete path;
-    LOG(WARNING) << "cannot read " << vmstats_path_;
+  if (!base::ReadFileToString(base::FilePath(kVmStatFileName), &value_string)) {
+    LOG(WARNING) << "cannot read " << kVmStatFileName;
     return false;
   }
-  delete path;
   return VmStatsParseStats(value_string.c_str(), stats);
 }
 
@@ -748,7 +706,7 @@
 
 void MetricsDaemon::MeminfoCallback(base::TimeDelta wait) {
   string meminfo_raw;
-  const FilePath meminfo_path("/proc/meminfo");
+  const FilePath meminfo_path(kMeminfoFileName);
   if (!base::ReadFileToString(meminfo_path, &meminfo_raw)) {
     LOG(WARNING) << "cannot read " << meminfo_path.value().c_str();
     return;
@@ -907,11 +865,8 @@
     Tokenize(lines[iline], ": ", &tokens);
     if (strcmp((*fields)[ifield].match, tokens[0].c_str()) == 0) {
       // Name matches. Parse value and save.
-      char* rest;
-      (*fields)[ifield].value =
-          static_cast<int>(strtol(tokens[1].c_str(), &rest, 10));
-      if (*rest != '\0') {
-        LOG(WARNING) << "missing meminfo value";
+      if (!base::StringToInt(tokens[1], &(*fields)[ifield].value)) {
+        LOG(WARNING) << "Cound not convert " << tokens[1] << " to int";
         return false;
       }
       ifield++;
@@ -958,7 +913,7 @@
 
 bool MetricsDaemon::MemuseCallbackWork() {
   string meminfo_raw;
-  const FilePath meminfo_path("/proc/meminfo");
+  const FilePath meminfo_path(kMeminfoFileName);
   if (!base::ReadFileToString(meminfo_path, &meminfo_raw)) {
     LOG(WARNING) << "cannot read " << meminfo_path.value().c_str();
     return false;
diff --git a/metricsd/metrics_daemon.h b/metricsd/metrics_daemon.h
index 6f5a3bf..9180e23 100644
--- a/metricsd/metrics_daemon.h
+++ b/metricsd/metrics_daemon.h
@@ -45,7 +45,6 @@
             bool uploader_active,
             bool dbus_enabled,
             MetricsLibraryInterface* metrics_lib,
-            const std::string& vmstats_path,
             const std::string& cpuinfo_max_freq_path,
             const std::string& scaling_max_freq_path,
             const base::TimeDelta& upload_interval,
@@ -101,14 +100,6 @@
     kStatsLong,     // final wait before new collection
   };
 
-  // Data record for aggregating daily usage.
-  class UseRecord {
-   public:
-    UseRecord() : day_(0), seconds_(0) {}
-    int day_;
-    int seconds_;
-  };
-
   // Type of scale to use for meminfo histograms.  For most of them we use
   // percent of total RAM, but for some we use absolute numbers, usually in
   // megabytes, on a log scale from 0 to 4000, and 0 to 8000 for compressed
@@ -135,30 +126,6 @@
     uint64_t swap_out_;       // pages swapped out
   };
 
-  // Metric parameters.
-  static const char kMetricReadSectorsLongName[];
-  static const char kMetricReadSectorsShortName[];
-  static const char kMetricWriteSectorsLongName[];
-  static const char kMetricWriteSectorsShortName[];
-  static const char kMetricPageFaultsShortName[];
-  static const char kMetricPageFaultsLongName[];
-  static const char kMetricSwapInLongName[];
-  static const char kMetricSwapInShortName[];
-  static const char kMetricSwapOutLongName[];
-  static const char kMetricSwapOutShortName[];
-  static const char kMetricScaledCpuFrequencyName[];
-  static const int kMetricStatsShortInterval;
-  static const int kMetricStatsLongInterval;
-  static const int kMetricMeminfoInterval;
-  static const int kMetricSectorsIOMax;
-  static const int kMetricSectorsBuckets;
-  static const int kMetricPageFaultsMax;
-  static const int kMetricPageFaultsBuckets;
-  static const char kMetricsDiskStatsPath[];
-  static const char kMetricsVmStatsPath[];
-  static const char kMetricsProcStatFileName[];
-  static const int kMetricsProcStatFirstLineItemsCount;
-
   // Returns the active time since boot (uptime minus sleep time) in seconds.
   double GetActiveTime();
 
@@ -167,13 +134,6 @@
                                          DBusMessage* message,
                                          void* user_data);
 
-  // Updates the daily usage file, if necessary, by adding |seconds|
-  // of active use to the |day| since Epoch. If there's usage data for
-  // day in the past in the usage file, that data is sent to UMA and
-  // removed from the file. If there's already usage data for |day| in
-  // the usage file, the |seconds| are accumulated.
-  void LogDailyUseRecord(int day, int seconds);
-
   // Updates the active use time and logs time between user-space
   // process crashes.
   void ProcessUserCrash();
@@ -312,11 +272,6 @@
   // The metrics library handle.
   MetricsLibraryInterface* metrics_lib_;
 
-  // Timestamps last network state update.  This timestamp is used to
-  // sample the time from the network going online to going offline so
-  // TimeTicks ensures a monotonically increasing TimeDelta.
-  base::TimeTicks network_state_last_;
-
   // The last time that UpdateStats() was called.
   base::TimeTicks last_update_stats_time_;
 
@@ -369,7 +324,6 @@
   scoped_ptr<PersistentInteger> unclean_shutdowns_daily_count_;
   scoped_ptr<PersistentInteger> unclean_shutdowns_weekly_count_;
 
-  std::string vmstats_path_;
   std::string scaling_max_freq_path_;
   std::string cpuinfo_max_freq_path_;
 
diff --git a/metricsd/metrics_daemon_main.cc b/metricsd/metrics_daemon_main.cc
index c3d5cab..7f9ec43 100644
--- a/metricsd/metrics_daemon_main.cc
+++ b/metricsd/metrics_daemon_main.cc
@@ -75,7 +75,6 @@
               FLAGS_uploader | FLAGS_uploader_test,
               FLAGS_withdbus,
               &metrics_lib,
-              "/proc/vmstat",
               kScalingMaxFreqPath,
               kCpuinfoMaxFreqPath,
               base::TimeDelta::FromSeconds(FLAGS_upload_interval_secs),
diff --git a/metricsd/metrics_daemon_test.cc b/metricsd/metrics_daemon_test.cc
index 9b5b58e..0d2229c 100644
--- a/metricsd/metrics_daemon_test.cc
+++ b/metricsd/metrics_daemon_test.cc
@@ -22,11 +22,12 @@
 
 #include <base/at_exit.h>
 #include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
 #include <base/strings/string_number_conversions.h>
 #include <base/strings/stringprintf.h>
-#include <chromeos/dbus/service_constants.h>
 #include <gtest/gtest.h>
 
+#include "constants.h"
 #include "metrics_daemon.h"
 #include "metrics_library_mock.h"
 #include "persistent_integer_mock.h"
@@ -45,7 +46,6 @@
 using ::testing::StrictMock;
 using chromeos_metrics::PersistentIntegerMock;
 
-static const char kFakeDiskStatsName[] = "fake-disk-stats";
 static const char kFakeDiskStatsFormat[] =
     "    1793     1788    %" PRIu64 "   105580    "
     "    196      175     %" PRIu64 "    30290    "
@@ -53,9 +53,6 @@
 static const uint64_t kFakeReadSectors[] = {80000, 100000};
 static const uint64_t kFakeWriteSectors[] = {3000, 4000};
 
-static const char kFakeVmStatsName[] = "fake-vm-stats";
-static const char kFakeScalingMaxFreqPath[] = "fake-scaling-max-freq";
-static const char kFakeCpuinfoMaxFreqPath[] = "fake-cpuinfo-max-freq";
 
 class MetricsDaemonTest : public testing::Test {
  protected:
@@ -63,78 +60,34 @@
   std::string kFakeDiskStats1;
 
   virtual void SetUp() {
+    EXPECT_TRUE(temp_dir_.CreateUniqueTempDir());
+    scaling_max_freq_path_ = temp_dir_.path().Append("scaling_max");
+    cpu_max_freq_path_ = temp_dir_.path().Append("cpu_freq_max");
+    disk_stats_path_ = temp_dir_.path().Append("disk_stats");
+
     kFakeDiskStats0 = base::StringPrintf(kFakeDiskStatsFormat,
                                            kFakeReadSectors[0],
                                            kFakeWriteSectors[0]);
     kFakeDiskStats1 = base::StringPrintf(kFakeDiskStatsFormat,
                                            kFakeReadSectors[1],
                                            kFakeWriteSectors[1]);
-    CreateFakeDiskStatsFile(kFakeDiskStats0.c_str());
-    CreateUint64ValueFile(base::FilePath(kFakeCpuinfoMaxFreqPath), 10000000);
-    CreateUint64ValueFile(base::FilePath(kFakeScalingMaxFreqPath), 10000000);
 
-    chromeos_metrics::PersistentInteger::SetTestingMode(true);
+    CreateFakeDiskStatsFile(kFakeDiskStats0);
+    CreateUint64ValueFile(cpu_max_freq_path_, 10000000);
+    CreateUint64ValueFile(scaling_max_freq_path_, 10000000);
+
+    chromeos_metrics::PersistentInteger::SetMetricsDirectory(
+        temp_dir_.path().value());
     daemon_.Init(true,
                  false,
+                 true,
                  &metrics_lib_,
-                 kFakeDiskStatsName,
-                 kFakeVmStatsName,
-                 kFakeScalingMaxFreqPath,
-                 kFakeCpuinfoMaxFreqPath,
+                 scaling_max_freq_path_.value(),
+                 cpu_max_freq_path_.value(),
                  base::TimeDelta::FromMinutes(30),
-                 kMetricsServer,
-                 kMetricsFilePath,
+                 metrics::kMetricsServer,
+                 metrics::kMetricsEventsFilePath,
                  "/");
-
-    // Replace original persistent values with mock ones.
-    daily_active_use_mock_ =
-        new StrictMock<PersistentIntegerMock>("1.mock");
-    daemon_.daily_active_use_.reset(daily_active_use_mock_);
-
-    kernel_crash_interval_mock_ =
-        new StrictMock<PersistentIntegerMock>("2.mock");
-    daemon_.kernel_crash_interval_.reset(kernel_crash_interval_mock_);
-
-    user_crash_interval_mock_ =
-        new StrictMock<PersistentIntegerMock>("3.mock");
-    daemon_.user_crash_interval_.reset(user_crash_interval_mock_);
-
-    unclean_shutdown_interval_mock_ =
-        new StrictMock<PersistentIntegerMock>("4.mock");
-    daemon_.unclean_shutdown_interval_.reset(unclean_shutdown_interval_mock_);
-  }
-
-  virtual void TearDown() {
-    EXPECT_EQ(0, unlink(kFakeDiskStatsName));
-    EXPECT_EQ(0, unlink(kFakeScalingMaxFreqPath));
-    EXPECT_EQ(0, unlink(kFakeCpuinfoMaxFreqPath));
-  }
-
-  // Adds active use aggregation counters update expectations that the
-  // specified count will be added.
-  void ExpectActiveUseUpdate(int count) {
-    EXPECT_CALL(*daily_active_use_mock_, Add(count))
-        .Times(1)
-        .RetiresOnSaturation();
-    EXPECT_CALL(*kernel_crash_interval_mock_, Add(count))
-        .Times(1)
-        .RetiresOnSaturation();
-    EXPECT_CALL(*user_crash_interval_mock_, Add(count))
-        .Times(1)
-        .RetiresOnSaturation();
-  }
-
-  // As above, but ignore values of counter updates.
-  void IgnoreActiveUseUpdate() {
-    EXPECT_CALL(*daily_active_use_mock_, Add(_))
-        .Times(1)
-        .RetiresOnSaturation();
-    EXPECT_CALL(*kernel_crash_interval_mock_, Add(_))
-        .Times(1)
-        .RetiresOnSaturation();
-    EXPECT_CALL(*user_crash_interval_mock_, Add(_))
-        .Times(1)
-        .RetiresOnSaturation();
   }
 
   // Adds a metrics library mock expectation that the specified metric
@@ -177,19 +130,15 @@
   }
 
   // Creates or overwrites an input file containing fake disk stats.
-  void CreateFakeDiskStatsFile(const char* fake_stats) {
-    if (unlink(kFakeDiskStatsName) < 0) {
-      EXPECT_EQ(errno, ENOENT);
-    }
-    FILE* f = fopen(kFakeDiskStatsName, "w");
-    EXPECT_EQ(1, fwrite(fake_stats, strlen(fake_stats), 1, f));
-    EXPECT_EQ(0, fclose(f));
+  void CreateFakeDiskStatsFile(const string& fake_stats) {
+    EXPECT_EQ(base::WriteFile(disk_stats_path_,
+                              fake_stats.data(), fake_stats.size()),
+              fake_stats.size());
   }
 
   // Creates or overwrites the file in |path| so that it contains the printable
   // representation of |value|.
   void CreateUint64ValueFile(const base::FilePath& path, uint64_t value) {
-    base::DeleteFile(path, false);
     std::string value_string = base::Uint64ToString(value);
     ASSERT_EQ(value_string.length(),
               base::WriteFile(path, value_string.c_str(),
@@ -199,29 +148,19 @@
   // The MetricsDaemon under test.
   MetricsDaemon daemon_;
 
+  // Temporary directory used for tests.
+  base::ScopedTempDir temp_dir_;
+
+  // Path for the fake files.
+  base::FilePath scaling_max_freq_path_;
+  base::FilePath cpu_max_freq_path_;
+  base::FilePath disk_stats_path_;
+
   // Mocks. They are strict mock so that all unexpected
   // calls are marked as failures.
   StrictMock<MetricsLibraryMock> metrics_lib_;
-  StrictMock<PersistentIntegerMock>* daily_active_use_mock_;
-  StrictMock<PersistentIntegerMock>* kernel_crash_interval_mock_;
-  StrictMock<PersistentIntegerMock>* user_crash_interval_mock_;
-  StrictMock<PersistentIntegerMock>* unclean_shutdown_interval_mock_;
 };
 
-TEST_F(MetricsDaemonTest, CheckSystemCrash) {
-  static const char kKernelCrashDetected[] = "test-kernel-crash-detected";
-  EXPECT_FALSE(daemon_.CheckSystemCrash(kKernelCrashDetected));
-
-  base::FilePath crash_detected(kKernelCrashDetected);
-  base::WriteFile(crash_detected, "", 0);
-  EXPECT_TRUE(base::PathExists(crash_detected));
-  EXPECT_TRUE(daemon_.CheckSystemCrash(kKernelCrashDetected));
-  EXPECT_FALSE(base::PathExists(crash_detected));
-  EXPECT_FALSE(daemon_.CheckSystemCrash(kKernelCrashDetected));
-  EXPECT_FALSE(base::PathExists(crash_detected));
-  base::DeleteFile(crash_detected, false);
-}
-
 TEST_F(MetricsDaemonTest, MessageFilter) {
   // Ignore calls to SendToUMA.
   EXPECT_CALL(metrics_lib_, SendToUMA(_, _, _, _, _)).Times(AnyNumber());
@@ -232,7 +171,6 @@
   EXPECT_EQ(DBUS_HANDLER_RESULT_NOT_YET_HANDLED, res);
   DeleteDBusMessage(msg);
 
-  IgnoreActiveUseUpdate();
   vector<string> signal_args;
   msg = NewDBusSignalString("/",
                             "org.chromium.CrashReporter",
@@ -260,25 +198,6 @@
                      /* min */ 1, /* max */ 100, /* buckets */ 50);
 }
 
-TEST_F(MetricsDaemonTest, ReportDiskStats) {
-  uint64_t read_sectors_now, write_sectors_now;
-  CreateFakeDiskStatsFile(kFakeDiskStats1.c_str());
-  daemon_.DiskStatsReadStats(&read_sectors_now, &write_sectors_now);
-  EXPECT_EQ(read_sectors_now, kFakeReadSectors[1]);
-  EXPECT_EQ(write_sectors_now, kFakeWriteSectors[1]);
-
-  MetricsDaemon::StatsState s_state = daemon_.stats_state_;
-  EXPECT_CALL(metrics_lib_,
-              SendToUMA(_, (kFakeReadSectors[1] - kFakeReadSectors[0]) / 30,
-                        _, _, _));
-  EXPECT_CALL(metrics_lib_,
-              SendToUMA(_, (kFakeWriteSectors[1] - kFakeWriteSectors[0]) / 30,
-                        _, _, _));
-  EXPECT_CALL(metrics_lib_, SendEnumToUMA(_, _, _));  // SendCpuThrottleMetrics
-  daemon_.StatsCallback();
-  EXPECT_TRUE(s_state != daemon_.stats_state_);
-}
-
 TEST_F(MetricsDaemonTest, ProcessMeminfo) {
   string meminfo =
       "MemTotal:        2000000 kB\nMemFree:          500000 kB\n"
@@ -337,24 +256,24 @@
   const int fake_max_freq = 2000000;
   int scaled_freq = 0;
   int max_freq = 0;
-  CreateUint64ValueFile(base::FilePath(kFakeScalingMaxFreqPath),
-                        fake_scaled_freq);
-  CreateUint64ValueFile(base::FilePath(kFakeCpuinfoMaxFreqPath), fake_max_freq);
+  CreateUint64ValueFile(scaling_max_freq_path_, fake_scaled_freq);
+  CreateUint64ValueFile(cpu_max_freq_path_, fake_max_freq);
   EXPECT_TRUE(daemon_.testing_);
-  EXPECT_TRUE(daemon_.ReadFreqToInt(kFakeScalingMaxFreqPath, &scaled_freq));
-  EXPECT_TRUE(daemon_.ReadFreqToInt(kFakeCpuinfoMaxFreqPath, &max_freq));
+  EXPECT_TRUE(daemon_.ReadFreqToInt(scaling_max_freq_path_.value(),
+                                    &scaled_freq));
+  EXPECT_TRUE(daemon_.ReadFreqToInt(cpu_max_freq_path_.value(), &max_freq));
   EXPECT_EQ(fake_scaled_freq, scaled_freq);
   EXPECT_EQ(fake_max_freq, max_freq);
 }
 
 TEST_F(MetricsDaemonTest, SendCpuThrottleMetrics) {
-  CreateUint64ValueFile(base::FilePath(kFakeCpuinfoMaxFreqPath), 2001000);
+  CreateUint64ValueFile(cpu_max_freq_path_, 2001000);
   // Test the 101% and 100% cases.
-  CreateUint64ValueFile(base::FilePath(kFakeScalingMaxFreqPath), 2001000);
+  CreateUint64ValueFile(scaling_max_freq_path_, 2001000);
   EXPECT_TRUE(daemon_.testing_);
   EXPECT_CALL(metrics_lib_, SendEnumToUMA(_, 101, 101));
   daemon_.SendCpuThrottleMetrics();
-  CreateUint64ValueFile(base::FilePath(kFakeScalingMaxFreqPath), 2000000);
+  CreateUint64ValueFile(scaling_max_freq_path_, 2000000);
   EXPECT_CALL(metrics_lib_, SendEnumToUMA(_, 100, 101));
   daemon_.SendCpuThrottleMetrics();
 }
@@ -370,12 +289,14 @@
   const uint64_t page_size = 4096;
   const uint64_t zero_pages = 10 * 1000 * 1000 / page_size;
 
-  CreateUint64ValueFile(base::FilePath(MetricsDaemon::kComprDataSizeName),
-                        compr_data_size);
-  CreateUint64ValueFile(base::FilePath(MetricsDaemon::kOrigDataSizeName),
-                        orig_data_size);
-  CreateUint64ValueFile(base::FilePath(MetricsDaemon::kZeroPagesName),
-                        zero_pages);
+  CreateUint64ValueFile(
+      temp_dir_.path().Append(MetricsDaemon::kComprDataSizeName),
+      compr_data_size);
+  CreateUint64ValueFile(
+      temp_dir_.path().Append(MetricsDaemon::kOrigDataSizeName),
+      orig_data_size);
+  CreateUint64ValueFile(
+      temp_dir_.path().Append(MetricsDaemon::kZeroPagesName), zero_pages);
 
   const uint64_t real_orig_size = orig_data_size + zero_pages * page_size;
   const uint64_t zero_ratio_percent =
@@ -390,11 +311,5 @@
   EXPECT_CALL(metrics_lib_, SendToUMA(_, zero_pages, _, _, _));
   EXPECT_CALL(metrics_lib_, SendToUMA(_, zero_ratio_percent, _, _, _));
 
-  EXPECT_TRUE(daemon_.ReportZram(base::FilePath(".")));
-}
-
-int main(int argc, char** argv) {
-  testing::InitGoogleTest(&argc, argv);
-
-  return RUN_ALL_TESTS();
+  EXPECT_TRUE(daemon_.ReportZram(temp_dir_.path()));
 }
diff --git a/metricsd/metrics_library.cc b/metricsd/metrics_library.cc
index c1998a6..5687f1b 100644
--- a/metricsd/metrics_library.cc
+++ b/metricsd/metrics_library.cc
@@ -140,6 +140,12 @@
   uma_events_file_ = metrics::kMetricsEventsFilePath;
 }
 
+void MetricsLibrary::InitForTest(const std::string& uma_events_file,
+                                 const std::string& consent_file) {
+  uma_events_file_ = uma_events_file;
+  consent_file_ = consent_file;
+}
+
 bool MetricsLibrary::SendToUMA(const std::string& name,
                                int sample,
                                int min,
diff --git a/metricsd/metrics_library_test.cc b/metricsd/metrics_library_test.cc
index c58e3fb..7ade6ee 100644
--- a/metricsd/metrics_library_test.cc
+++ b/metricsd/metrics_library_test.cc
@@ -14,130 +14,52 @@
  * limitations under the License.
  */
 
-#include <cstring>
 
 #include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
-#include <policy/mock_device_policy.h>
-#include <policy/libpolicy.h>
 
 #include "metrics/c_metrics_library.h"
 #include "metrics/metrics_library.h"
 
-using base::FilePath;
-using ::testing::_;
-using ::testing::Return;
-using ::testing::AnyNumber;
-
-static const FilePath kTestUMAEventsFile("test-uma-events");
-static const char kTestMounts[] = "test-mounts";
-
-ACTION_P(SetMetricsPolicy, enabled) {
-  *arg0 = enabled;
-  return true;
-}
 
 class MetricsLibraryTest : public testing::Test {
  protected:
   virtual void SetUp() {
-    EXPECT_TRUE(lib_.uma_events_file_.empty());
-    lib_.Init();
-    EXPECT_FALSE(lib_.uma_events_file_.empty());
-    lib_.uma_events_file_ = kTestUMAEventsFile.value();
-    EXPECT_EQ(0, WriteFile(kTestUMAEventsFile, "", 0));
-    device_policy_ = new policy::MockDevicePolicy();
-    EXPECT_CALL(*device_policy_, LoadPolicy())
-        .Times(AnyNumber())
-        .WillRepeatedly(Return(true));
-    EXPECT_CALL(*device_policy_, GetMetricsEnabled(_))
-        .Times(AnyNumber())
-        .WillRepeatedly(SetMetricsPolicy(true));
-    provider_ = new policy::PolicyProvider(device_policy_);
-    lib_.SetPolicyProvider(provider_);
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+    consent_file_ = temp_dir_.path().Append("consent");
+    uma_events_file_ = temp_dir_.path().Append("events");
+    lib_.InitForTest(uma_events_file_.value(), consent_file_.value());
+    EXPECT_EQ(0, WriteFile(uma_events_file_, "", 0));
     // Defeat metrics enabled caching between tests.
     lib_.cached_enabled_time_ = 0;
   }
 
-  virtual void TearDown() {
-    base::DeleteFile(FilePath(kTestMounts), false);
-    base::DeleteFile(kTestUMAEventsFile, false);
+  void SetMetricsConsent(bool enabled) {
+    if (enabled) {
+      ASSERT_EQ(base::WriteFile(consent_file_, "", 0), 0);
+    } else {
+      ASSERT_TRUE(base::DeleteFile(consent_file_, false));
+    }
   }
 
   void VerifyEnabledCacheHit(bool to_value);
   void VerifyEnabledCacheEviction(bool to_value);
 
   MetricsLibrary lib_;
-  policy::MockDevicePolicy* device_policy_;
-  policy::PolicyProvider* provider_;
+  base::ScopedTempDir temp_dir_;
+  base::FilePath consent_file_;
+  base::FilePath uma_events_file_;
 };
 
-TEST_F(MetricsLibraryTest, IsDeviceMounted) {
-  static const char kTestContents[] =
-      "0123456789abcde 0123456789abcde\nguestfs foo bar\n";
-  char buffer[1024];
-  int block_sizes[] = { 1, 2, 3, 4, 5, 6, 8, 12, 14, 16, 32, 1024 };
-  bool result;
-  EXPECT_FALSE(lib_.IsDeviceMounted("guestfs",
-                                    "nonexistent",
-                                    buffer,
-                                    1,
-                                    &result));
-  ASSERT_TRUE(base::WriteFile(base::FilePath(kTestMounts),
-                              kTestContents,
-                              strlen(kTestContents)));
-  EXPECT_FALSE(lib_.IsDeviceMounted("guestfs",
-                                    kTestMounts,
-                                    buffer,
-                                    0,
-                                    &result));
-  for (size_t i = 0; i < arraysize(block_sizes); ++i) {
-    EXPECT_TRUE(lib_.IsDeviceMounted("0123456789abcde",
-                                     kTestMounts,
-                                     buffer,
-                                     block_sizes[i],
-                                     &result));
-    EXPECT_TRUE(result);
-    EXPECT_TRUE(lib_.IsDeviceMounted("guestfs",
-                                     kTestMounts,
-                                     buffer,
-                                     block_sizes[i],
-                                     &result));
-    EXPECT_TRUE(result);
-    EXPECT_TRUE(lib_.IsDeviceMounted("0123456",
-                                     kTestMounts,
-                                     buffer,
-                                     block_sizes[i],
-                                     &result));
-    EXPECT_FALSE(result);
-    EXPECT_TRUE(lib_.IsDeviceMounted("9abcde",
-                                     kTestMounts,
-                                     buffer,
-                                     block_sizes[i],
-                                     &result));
-    EXPECT_FALSE(result);
-    EXPECT_TRUE(lib_.IsDeviceMounted("foo",
-                                     kTestMounts,
-                                     buffer,
-                                     block_sizes[i],
-                                     &result));
-    EXPECT_FALSE(result);
-    EXPECT_TRUE(lib_.IsDeviceMounted("bar",
-                                     kTestMounts,
-                                     buffer,
-                                     block_sizes[i],
-                                     &result));
-    EXPECT_FALSE(result);
-  }
-}
-
 TEST_F(MetricsLibraryTest, AreMetricsEnabledFalse) {
-  EXPECT_CALL(*device_policy_, GetMetricsEnabled(_))
-      .WillOnce(SetMetricsPolicy(false));
+  SetMetricsConsent(false);
   EXPECT_FALSE(lib_.AreMetricsEnabled());
 }
 
 TEST_F(MetricsLibraryTest, AreMetricsEnabledTrue) {
+  SetMetricsConsent(true);
   EXPECT_TRUE(lib_.AreMetricsEnabled());
 }
 
@@ -146,12 +68,12 @@
   // times in a row.
   for (int i = 0; i < 100; ++i) {
     lib_.cached_enabled_time_ = 0;
-    EXPECT_CALL(*device_policy_, GetMetricsEnabled(_))
-        .WillOnce(SetMetricsPolicy(!to_value));
-    ASSERT_EQ(!to_value, lib_.AreMetricsEnabled());
-    ON_CALL(*device_policy_, GetMetricsEnabled(_))
-        .WillByDefault(SetMetricsPolicy(to_value));
-    if (lib_.AreMetricsEnabled() == !to_value)
+    SetMetricsConsent(to_value);
+    lib_.AreMetricsEnabled();
+    // If we check the metrics status twice in a row, we use the cached value
+    // the second time.
+    SetMetricsConsent(!to_value);
+    if (lib_.AreMetricsEnabled() == to_value)
       return;
   }
   ADD_FAILURE() << "Did not see evidence of caching";
@@ -159,14 +81,12 @@
 
 void MetricsLibraryTest::VerifyEnabledCacheEviction(bool to_value) {
   lib_.cached_enabled_time_ = 0;
-  EXPECT_CALL(*device_policy_, GetMetricsEnabled(_))
-      .WillOnce(SetMetricsPolicy(!to_value));
+  SetMetricsConsent(!to_value);
   ASSERT_EQ(!to_value, lib_.AreMetricsEnabled());
-  EXPECT_CALL(*device_policy_, GetMetricsEnabled(_))
-      .WillOnce(SetMetricsPolicy(to_value));
-  ASSERT_LT(abs(static_cast<int>(time(nullptr) - lib_.cached_enabled_time_)),
-            5);
-  // Sleep one second (or cheat to be faster).
+
+  SetMetricsConsent(to_value);
+  // Sleep one second (or cheat to be faster) and check that we are not using
+  // the cached value.
   --lib_.cached_enabled_time_;
   ASSERT_EQ(to_value, lib_.AreMetricsEnabled());
 }
@@ -177,50 +97,3 @@
   VerifyEnabledCacheEviction(false);
   VerifyEnabledCacheEviction(true);
 }
-
-class CMetricsLibraryTest : public testing::Test {
- protected:
-  virtual void SetUp() {
-    lib_ = CMetricsLibraryNew();
-    MetricsLibrary& ml = *reinterpret_cast<MetricsLibrary*>(lib_);
-    EXPECT_TRUE(ml.uma_events_file_.empty());
-    CMetricsLibraryInit(lib_);
-    EXPECT_FALSE(ml.uma_events_file_.empty());
-    ml.uma_events_file_ = kTestUMAEventsFile.value();
-    EXPECT_EQ(0, WriteFile(kTestUMAEventsFile, "", 0));
-    device_policy_ = new policy::MockDevicePolicy();
-    EXPECT_CALL(*device_policy_, LoadPolicy())
-        .Times(AnyNumber())
-        .WillRepeatedly(Return(true));
-    EXPECT_CALL(*device_policy_, GetMetricsEnabled(_))
-        .Times(AnyNumber())
-        .WillRepeatedly(SetMetricsPolicy(true));
-    provider_ = new policy::PolicyProvider(device_policy_);
-    ml.SetPolicyProvider(provider_);
-    reinterpret_cast<MetricsLibrary*>(lib_)->cached_enabled_time_ = 0;
-  }
-
-  virtual void TearDown() {
-    CMetricsLibraryDelete(lib_);
-    base::DeleteFile(kTestUMAEventsFile, false);
-  }
-
-  CMetricsLibrary lib_;
-  policy::MockDevicePolicy* device_policy_;
-  policy::PolicyProvider* provider_;
-};
-
-TEST_F(CMetricsLibraryTest, AreMetricsEnabledFalse) {
-  EXPECT_CALL(*device_policy_, GetMetricsEnabled(_))
-      .WillOnce(SetMetricsPolicy(false));
-  EXPECT_FALSE(CMetricsLibraryAreMetricsEnabled(lib_));
-}
-
-TEST_F(CMetricsLibraryTest, AreMetricsEnabledTrue) {
-  EXPECT_TRUE(CMetricsLibraryAreMetricsEnabled(lib_));
-}
-
-int main(int argc, char** argv) {
-  testing::InitGoogleTest(&argc, argv);
-  return RUN_ALL_TESTS();
-}
diff --git a/metricsd/persistent_integer.cc b/metricsd/persistent_integer.cc
index 9fa5c1e..e849f00 100644
--- a/metricsd/persistent_integer.cc
+++ b/metricsd/persistent_integer.cc
@@ -28,18 +28,14 @@
 namespace chromeos_metrics {
 
 // Static class member instantiation.
-bool PersistentInteger::testing_ = false;
+std::string PersistentInteger::metrics_directory_ = metrics::kMetricsDirectory;
 
 PersistentInteger::PersistentInteger(const std::string& name) :
       value_(0),
       version_(kVersion),
       name_(name),
       synced_(false) {
-  if (testing_) {
-    backing_file_name_ = name_;
-  } else {
-    backing_file_name_ = metrics::kMetricsDirectory + name_;
-  }
+  backing_file_name_ = metrics_directory_ + name_;
 }
 
 PersistentInteger::~PersistentInteger() {}
@@ -100,8 +96,8 @@
   return read_succeeded;
 }
 
-void PersistentInteger::SetTestingMode(bool testing) {
-  testing_ = testing;
+void PersistentInteger::SetMetricsDirectory(const std::string& directory) {
+  metrics_directory_ = directory;
 }
 
 
diff --git a/metricsd/persistent_integer.h b/metricsd/persistent_integer.h
index fec001f..ecef3d1 100644
--- a/metricsd/persistent_integer.h
+++ b/metricsd/persistent_integer.h
@@ -50,10 +50,9 @@
   // Virtual only because of mock.
   virtual void Add(int64_t x);
 
-  // After calling with |testing| = true, changes some behavior for the purpose
-  // of testing.  For instance: instances created while testing use the current
-  // directory for the backing files.
-  static void SetTestingMode(bool testing);
+  // Sets the directory path for all persistent integers.
+  // This is used in unittests to change where the counters are stored.
+  static void SetMetricsDirectory(const std::string& directory);
 
  private:
   static const int kVersion = 1001;
@@ -70,8 +69,8 @@
   int32_t version_;
   std::string name_;
   std::string backing_file_name_;
+  static std::string metrics_directory_;
   bool synced_;
-  static bool testing_;
 };
 
 }  // namespace chromeos_metrics
diff --git a/metricsd/persistent_integer_test.cc b/metricsd/persistent_integer_test.cc
index 19801f9..5e2067f 100644
--- a/metricsd/persistent_integer_test.cc
+++ b/metricsd/persistent_integer_test.cc
@@ -19,6 +19,7 @@
 #include <base/compiler_specific.h>
 #include <base/files/file_enumerator.h>
 #include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
 
 #include "persistent_integer.h"
 
@@ -30,7 +31,9 @@
 class PersistentIntegerTest : public testing::Test {
   void SetUp() override {
     // Set testing mode.
-    chromeos_metrics::PersistentInteger::SetTestingMode(true);
+    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
+    chromeos_metrics::PersistentInteger::SetMetricsDirectory(
+        temp_dir_.path().value());
   }
 
   void TearDown() override {
@@ -45,6 +48,8 @@
       base::DeleteFile(name, false);
     }
   }
+
+  base::ScopedTempDir temp_dir_;
 };
 
 TEST_F(PersistentIntegerTest, BasicChecks) {
@@ -71,8 +76,3 @@
   pi.reset(new PersistentInteger(kBackingFileName));
   EXPECT_EQ(0, pi->Get());
 }
-
-int main(int argc, char** argv) {
-  testing::InitGoogleTest(&argc, argv);
-  return RUN_ALL_TESTS();
-}
diff --git a/metricsd/uploader/metrics_log_base.cc b/metricsd/uploader/metrics_log_base.cc
index ee325ae..311e43a 100644
--- a/metricsd/uploader/metrics_log_base.cc
+++ b/metricsd/uploader/metrics_log_base.cc
@@ -73,7 +73,7 @@
   // name.  We can then use this logging to find out what histogram name was
   // being hashed to a given MD5 value by just running the version of Chromium
   // in question with --enable-logging.
-  DVLOG(1) << "Metrics: Hash numeric [" << value << "]=[" << hash << "]";
+  VLOG(1) << "Metrics: Hash numeric [" << value << "]=[" << hash << "]";
 
   return hash;
 }
diff --git a/metricsd/uploader/mock/mock_system_profile_setter.h b/metricsd/uploader/mock/mock_system_profile_setter.h
index c714e9c..9b20291 100644
--- a/metricsd/uploader/mock/mock_system_profile_setter.h
+++ b/metricsd/uploader/mock/mock_system_profile_setter.h
@@ -26,7 +26,9 @@
 // Mock profile setter used for testing.
 class MockSystemProfileSetter : public SystemProfileSetter {
  public:
-  void Populate(metrics::ChromeUserMetricsExtension* profile_proto) override {}
+  bool Populate(metrics::ChromeUserMetricsExtension* profile_proto) override {
+    return true;
+  }
 };
 
 #endif  // METRICS_UPLOADER_MOCK_MOCK_SYSTEM_PROFILE_SETTER_H_
diff --git a/metricsd/uploader/system_profile_cache.cc b/metricsd/uploader/system_profile_cache.cc
index 21ec229..8635fb0 100644
--- a/metricsd/uploader/system_profile_cache.cc
+++ b/metricsd/uploader/system_profile_cache.cc
@@ -61,7 +61,7 @@
 }
 
 SystemProfileCache::SystemProfileCache(bool testing,
-                                       const std::string& config_root)
+                                       const base::FilePath& config_root)
     : initialized_(false),
       testing_(testing),
       config_root_(config_root),
@@ -73,9 +73,7 @@
   CHECK(!initialized_)
       << "this should be called only once in the metrics_daemon lifetime.";
 
-  char property_value[PROPERTY_VALUE_MAX];
-  property_get(metrics::kBuildTargetIdProperty, property_value, "");
-  profile_.build_target_id = std::string(property_value);
+  profile_.build_target_id = GetProperty(metrics::kBuildTargetIdProperty);
 
   if (profile_.build_target_id.empty()) {
     LOG(ERROR) << "System property " << metrics::kBuildTargetIdProperty
@@ -83,11 +81,8 @@
     return false;
   }
 
-  property_get(metrics::kChannelProperty, property_value, "");
-  std::string channel(property_value);
-
-  property_get(metrics::kProductVersionProperty, property_value, "");
-  profile_.version = std::string(property_value);
+  std::string channel = GetProperty(metrics::kChannelProperty);
+  profile_.version = GetProperty(metrics::kProductVersionProperty);
 
   if (channel.empty() || profile_.version.empty()) {
     // If the channel or version is missing, the image is not official.
@@ -157,6 +152,18 @@
   return guid;
 }
 
+std::string SystemProfileCache::GetProperty(const std::string& name) {
+  if (testing_) {
+    std::string content;
+    base::ReadFileToString(config_root_.Append(name), &content);
+    return content;
+  } else {
+    char value[PROPERTY_VALUE_MAX];
+    property_get(name.data(), value, "");
+    return std::string(value);
+  }
+}
+
 metrics::SystemProfileProto_Channel SystemProfileCache::ProtoChannelFromString(
     const std::string& channel) {
   if (channel == "stable") {
diff --git a/metricsd/uploader/system_profile_cache.h b/metricsd/uploader/system_profile_cache.h
index ac80b47..7157810 100644
--- a/metricsd/uploader/system_profile_cache.h
+++ b/metricsd/uploader/system_profile_cache.h
@@ -22,6 +22,7 @@
 #include <string>
 
 #include "base/compiler_specific.h"
+#include "base/files/file_path.h"
 #include "base/gtest_prod_util.h"
 #include "base/memory/scoped_ptr.h"
 #include "persistent_integer.h"
@@ -49,7 +50,7 @@
  public:
   SystemProfileCache();
 
-  SystemProfileCache(bool testing, const std::string& config_root);
+  SystemProfileCache(bool testing, const base::FilePath& config_root);
 
   // Populates the ProfileSystem protobuf with system information.
   bool Populate(metrics::ChromeUserMetricsExtension* metrics_proto) override;
@@ -75,9 +76,14 @@
   // Initializes |profile_| only if it has not been yet initialized.
   bool InitializeOrCheck();
 
+  // Gets a system property as a string.
+  // When |testing_| is true, reads the value from |config_root_|/|name|
+  // instead.
+  std::string GetProperty(const std::string& name);
+
   bool initialized_;
   bool testing_;
-  std::string config_root_;
+  base::FilePath config_root_;
   scoped_ptr<chromeos_metrics::PersistentInteger> session_id_;
   SystemProfile profile_;
 };
diff --git a/metricsd/uploader/upload_service_test.cc b/metricsd/uploader/upload_service_test.cc
index cbb5277..40c235d 100644
--- a/metricsd/uploader/upload_service_test.cc
+++ b/metricsd/uploader/upload_service_test.cc
@@ -16,11 +16,13 @@
 
 #include <gtest/gtest.h>
 
-#include "base/at_exit.h"
-#include "base/files/file_util.h"
-#include "base/files/scoped_temp_dir.h"
-#include "base/logging.h"
-#include "base/sys_info.h"
+#include <base/at_exit.h>
+#include <base/files/file_util.h>
+#include <base/files/scoped_temp_dir.h>
+#include <base/logging.h>
+#include <base/sys_info.h>
+
+#include "constants.h"
 #include "metrics_library_mock.h"
 #include "serialization/metric_sample.h"
 #include "uploader/metrics_log.h"
@@ -34,35 +36,35 @@
 
 class UploadServiceTest : public testing::Test {
  protected:
-  UploadServiceTest()
-      : cache_(true, "/"),
-        upload_service_(new MockSystemProfileSetter(), &metrics_lib_,
-                        kMetricsServer, true),
-        exit_manager_(new base::AtExitManager()) {
-    sender_ = new SenderMock;
-    upload_service_.sender_.reset(sender_);
-    upload_service_.Init(base::TimeDelta::FromMinutes(30), kMetricsFilePath);
-  }
-
   virtual void SetUp() {
     CHECK(dir_.CreateUniqueTempDir());
-    upload_service_.GatherHistograms();
-    upload_service_.Reset();
-    sender_->Reset();
+    upload_service_.reset(new UploadService(new MockSystemProfileSetter(),
+                                            &metrics_lib_, "", true));
 
-    chromeos_metrics::PersistentInteger::SetTestingMode(true);
-    cache_.session_id_.reset(new chromeos_metrics::PersistentInteger(
-        dir_.path().Append("session_id").value()));
+    upload_service_->sender_.reset(new SenderMock);
+    event_file_ = dir_.path().Append("event");
+    upload_service_->Init(base::TimeDelta::FromMinutes(30), event_file_.value());
+    upload_service_->GatherHistograms();
+    upload_service_->Reset();
+
+    chromeos_metrics::PersistentInteger::SetMetricsDirectory(
+        dir_.path().value());
   }
 
   scoped_ptr<metrics::MetricSample> Crash(const std::string& name) {
     return metrics::MetricSample::CrashSample(name);
   }
 
+  void SetTestingProperty(const std::string& name, const std::string& value) {
+    ASSERT_EQ(
+        value.size(),
+        base::WriteFile(dir_.path().Append(name), value.data(), value.size()));
+  }
+
+  base::FilePath event_file_;
+
   base::ScopedTempDir dir_;
-  SenderMock* sender_;
-  SystemProfileCache cache_;
-  UploadService upload_service_;
+  scoped_ptr<UploadService> upload_service_;
   MetricsLibraryMock metrics_lib_;
 
   scoped_ptr<base::AtExitManager> exit_manager_;
@@ -70,18 +72,18 @@
 
 // Tests that the right crash increments a values.
 TEST_F(UploadServiceTest, LogUserCrash) {
-  upload_service_.AddSample(*Crash("user").get());
+  upload_service_->AddSample(*Crash("user").get());
 
-  MetricsLog* log = upload_service_.current_log_.get();
+  MetricsLog* log = upload_service_->current_log_.get();
   metrics::ChromeUserMetricsExtension* proto = log->uma_proto();
 
   EXPECT_EQ(1, proto->system_profile().stability().other_user_crash_count());
 }
 
 TEST_F(UploadServiceTest, LogUncleanShutdown) {
-  upload_service_.AddSample(*Crash("uncleanshutdown"));
+  upload_service_->AddSample(*Crash("uncleanshutdown"));
 
-  EXPECT_EQ(1, upload_service_.current_log_
+  EXPECT_EQ(1, upload_service_->current_log_
                    ->uma_proto()
                    ->system_profile()
                    .stability()
@@ -89,9 +91,9 @@
 }
 
 TEST_F(UploadServiceTest, LogKernelCrash) {
-  upload_service_.AddSample(*Crash("kernel"));
+  upload_service_->AddSample(*Crash("kernel"));
 
-  EXPECT_EQ(1, upload_service_.current_log_
+  EXPECT_EQ(1, upload_service_->current_log_
                    ->uma_proto()
                    ->system_profile()
                    .stability()
@@ -99,47 +101,56 @@
 }
 
 TEST_F(UploadServiceTest, UnknownCrashIgnored) {
-  upload_service_.AddSample(*Crash("foo"));
+  upload_service_->AddSample(*Crash("foo"));
 
   // The log should be empty.
-  EXPECT_FALSE(upload_service_.current_log_);
+  EXPECT_FALSE(upload_service_->current_log_);
 }
 
 TEST_F(UploadServiceTest, FailedSendAreRetried) {
-  sender_->set_should_succeed(false);
+  SenderMock* sender = new SenderMock();
+  upload_service_->sender_.reset(sender);
 
-  upload_service_.AddSample(*Crash("user"));
-  upload_service_.UploadEvent();
-  EXPECT_EQ(1, sender_->send_call_count());
-  std::string sent_string = sender_->last_message();
+  sender->set_should_succeed(false);
 
-  upload_service_.UploadEvent();
-  EXPECT_EQ(2, sender_->send_call_count());
-  EXPECT_EQ(sent_string, sender_->last_message());
+  upload_service_->AddSample(*Crash("user"));
+  upload_service_->UploadEvent();
+  EXPECT_EQ(1, sender->send_call_count());
+  std::string sent_string = sender->last_message();
+
+  upload_service_->UploadEvent();
+  EXPECT_EQ(2, sender->send_call_count());
+  EXPECT_EQ(sent_string, sender->last_message());
 }
 
 TEST_F(UploadServiceTest, DiscardLogsAfterTooManyFailedUpload) {
-  sender_->set_should_succeed(false);
-  upload_service_.AddSample(*Crash("user"));
+  SenderMock* sender = new SenderMock();
+  upload_service_->sender_.reset(sender);
+
+  sender->set_should_succeed(false);
+
+  upload_service_->AddSample(*Crash("user"));
 
   for (int i = 0; i < UploadService::kMaxFailedUpload; i++) {
-    upload_service_.UploadEvent();
+    upload_service_->UploadEvent();
   }
 
-  EXPECT_TRUE(upload_service_.staged_log_);
-  upload_service_.UploadEvent();
-  EXPECT_FALSE(upload_service_.staged_log_);
+  EXPECT_TRUE(upload_service_->staged_log_);
+  upload_service_->UploadEvent();
+  EXPECT_FALSE(upload_service_->staged_log_);
 }
 
 TEST_F(UploadServiceTest, EmptyLogsAreNotSent) {
-  upload_service_.UploadEvent();
-  EXPECT_FALSE(upload_service_.current_log_);
-  EXPECT_EQ(0, sender_->send_call_count());
+  SenderMock* sender = new SenderMock();
+  upload_service_->sender_.reset(sender);
+  upload_service_->UploadEvent();
+  EXPECT_FALSE(upload_service_->current_log_);
+  EXPECT_EQ(0, sender->send_call_count());
 }
 
 TEST_F(UploadServiceTest, LogEmptyByDefault) {
   UploadService upload_service(new MockSystemProfileSetter(), &metrics_lib_,
-                               kMetricsServer);
+                               "");
 
   // current_log_ should be initialized later as it needs AtExitManager to exit
   // in order to gather system information from SysInfo.
@@ -147,39 +158,42 @@
 }
 
 TEST_F(UploadServiceTest, CanSendMultipleTimes) {
-  upload_service_.AddSample(*Crash("user"));
-  upload_service_.UploadEvent();
+  SenderMock* sender = new SenderMock();
+  upload_service_->sender_.reset(sender);
 
-  std::string first_message = sender_->last_message();
+  upload_service_->AddSample(*Crash("user"));
+  upload_service_->UploadEvent();
 
-  upload_service_.AddSample(*Crash("kernel"));
-  upload_service_.UploadEvent();
+  std::string first_message = sender->last_message();
 
-  EXPECT_NE(first_message, sender_->last_message());
+  upload_service_->AddSample(*Crash("kernel"));
+  upload_service_->UploadEvent();
+
+  EXPECT_NE(first_message, sender->last_message());
 }
 
 TEST_F(UploadServiceTest, LogEmptyAfterUpload) {
-  upload_service_.AddSample(*Crash("user"));
+  upload_service_->AddSample(*Crash("user"));
 
-  EXPECT_TRUE(upload_service_.current_log_);
+  EXPECT_TRUE(upload_service_->current_log_);
 
-  upload_service_.UploadEvent();
-  EXPECT_FALSE(upload_service_.current_log_);
+  upload_service_->UploadEvent();
+  EXPECT_FALSE(upload_service_->current_log_);
 }
 
 TEST_F(UploadServiceTest, LogContainsAggregatedValues) {
   scoped_ptr<metrics::MetricSample> histogram =
       metrics::MetricSample::HistogramSample("foo", 10, 0, 42, 10);
-  upload_service_.AddSample(*histogram.get());
+  upload_service_->AddSample(*histogram.get());
 
 
   scoped_ptr<metrics::MetricSample> histogram2 =
       metrics::MetricSample::HistogramSample("foo", 11, 0, 42, 10);
-  upload_service_.AddSample(*histogram2.get());
+  upload_service_->AddSample(*histogram2.get());
 
-  upload_service_.GatherHistograms();
+  upload_service_->GatherHistograms();
   metrics::ChromeUserMetricsExtension* proto =
-      upload_service_.current_log_->uma_proto();
+      upload_service_->current_log_->uma_proto();
   EXPECT_EQ(1, proto->histogram_event().size());
 }
 
@@ -190,46 +204,41 @@
       metrics::SystemProfileProto::CHANNEL_UNKNOWN);
 
   EXPECT_EQ(metrics::SystemProfileProto::CHANNEL_DEV,
-            SystemProfileCache::ProtoChannelFromString("dev-channel"));
+            SystemProfileCache::ProtoChannelFromString("dev"));
+
+  EXPECT_EQ(metrics::SystemProfileProto::CHANNEL_STABLE,
+            SystemProfileCache::ProtoChannelFromString("stable"));
 
   EXPECT_EQ(metrics::SystemProfileProto::CHANNEL_UNKNOWN,
-            SystemProfileCache::ProtoChannelFromString("dev-channel test"));
+            SystemProfileCache::ProtoChannelFromString("this is a test"));
 }
 
 TEST_F(UploadServiceTest, ValuesInConfigFileAreSent) {
-  std::string name("os name");
-  std::string content(
-      "CHROMEOS_RELEASE_NAME=" + name +
-      "\nCHROMEOS_RELEASE_VERSION=version\n"
-      "CHROMEOS_RELEASE_DESCRIPTION=description beta-channel test\n"
-      "CHROMEOS_RELEASE_TRACK=beta-channel\n"
-      "CHROMEOS_RELEASE_BUILD_TYPE=developer build\n"
-      "CHROMEOS_RELEASE_BOARD=myboard");
+  SenderMock* sender = new SenderMock();
+  upload_service_->sender_.reset(sender);
 
-  base::SysInfo::SetChromeOSVersionInfoForTest(content, base::Time());
+  SetTestingProperty(metrics::kChannelProperty, "beta");
+  SetTestingProperty(metrics::kBuildTargetIdProperty, "hello");
+  SetTestingProperty(metrics::kProductVersionProperty, "1.2.3.4");
+
   scoped_ptr<metrics::MetricSample> histogram =
       metrics::MetricSample::SparseHistogramSample("myhistogram", 1);
-  SystemProfileCache* local_cache_ = new SystemProfileCache(true, "/");
-  local_cache_->session_id_.reset(new chromeos_metrics::PersistentInteger(
-        dir_.path().Append("session_id").value()));
-
-  upload_service_.system_profile_setter_.reset(local_cache_);
   // Reset to create the new log with the profile setter.
-  upload_service_.Reset();
-  upload_service_.AddSample(*histogram.get());
-  upload_service_.UploadEvent();
+  upload_service_->system_profile_setter_.reset(
+      new SystemProfileCache(true, dir_.path()));
+  upload_service_->Reset();
+  upload_service_->AddSample(*histogram.get());
+  upload_service_->UploadEvent();
 
-  EXPECT_EQ(1, sender_->send_call_count());
-  EXPECT_TRUE(sender_->is_good_proto());
-  EXPECT_EQ(1, sender_->last_message_proto().histogram_event().size());
+  EXPECT_EQ(1, sender->send_call_count());
+  EXPECT_TRUE(sender->is_good_proto());
+  EXPECT_EQ(1, sender->last_message_proto().histogram_event().size());
 
-  EXPECT_EQ(name, sender_->last_message_proto().system_profile().os().name());
   EXPECT_EQ(metrics::SystemProfileProto::CHANNEL_BETA,
-            sender_->last_message_proto().system_profile().channel());
-  EXPECT_NE(0, sender_->last_message_proto().client_id());
-  EXPECT_NE(0,
-            sender_->last_message_proto().system_profile().build_timestamp());
-  EXPECT_NE(0, sender_->last_message_proto().session_id());
+            sender->last_message_proto().system_profile().channel());
+  EXPECT_NE(0, sender->last_message_proto().client_id());
+  EXPECT_NE(0, sender->last_message_proto().system_profile().build_timestamp());
+  EXPECT_NE(0, sender->last_message_proto().session_id());
 }
 
 TEST_F(UploadServiceTest, PersistentGUID) {
@@ -252,15 +261,11 @@
 }
 
 TEST_F(UploadServiceTest, SessionIdIncrementedAtInitialization) {
-  cache_.Initialize();
-  int session_id = cache_.profile_.session_id;
-  cache_.initialized_ = false;
-  cache_.Initialize();
-  EXPECT_EQ(cache_.profile_.session_id, session_id + 1);
-}
-
-int main(int argc, char** argv) {
-  testing::InitGoogleTest(&argc, argv);
-
-  return RUN_ALL_TESTS();
+  SetTestingProperty(metrics::kBuildTargetIdProperty, "hello");
+  SystemProfileCache cache(true, dir_.path());
+  cache.Initialize();
+  int session_id = cache.profile_.session_id;
+  cache.initialized_ = false;
+  cache.Initialize();
+  EXPECT_EQ(cache.profile_.session_id, session_id + 1);
 }
diff --git a/toolbox/Android.mk b/toolbox/Android.mk
index d2d13f6..1d3605e 100644
--- a/toolbox/Android.mk
+++ b/toolbox/Android.mk
@@ -52,7 +52,6 @@
     start \
     stop \
     top \
-    uptime \
 
 ALL_TOOLS = $(BSD_TOOLS) $(OUR_TOOLS)
 
diff --git a/toolbox/uptime.c b/toolbox/uptime.c
deleted file mode 100644
index ebfb15e..0000000
--- a/toolbox/uptime.c
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (c) 2010, The Android Open Source Project
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *  * Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- *  * Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in
- *    the documentation and/or other materials provided with the
- *    distribution.
- *  * Neither the name of Google, Inc. nor the names of its contributors
- *    may be used to endorse or promote products derived from this
- *    software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
- * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#include <errno.h>
-#include <stdio.h>
-#include <string.h>
-#include <time.h>
-
-static void format_time(int time, char* buffer) {
-    int seconds = time % 60;
-    time /= 60;
-    int minutes = time % 60;
-    time /= 60;
-    int hours = time % 24;
-    int days = time / 24;
-
-    if (days > 0) {
-        sprintf(buffer, "%d day%s, %02d:%02d:%02d", days, (days == 1) ? "" : "s", hours, minutes, seconds);
-    } else {
-        sprintf(buffer, "%02d:%02d:%02d", hours, minutes, seconds);
-    }
-}
-
-int uptime_main(int argc __attribute__((unused)), char *argv[] __attribute__((unused))) {
-    FILE* file = fopen("/proc/uptime", "r");
-    if (!file) {
-        fprintf(stderr, "Could not open /proc/uptime\n");
-        return -1;
-    }
-    float idle_time;
-    if (fscanf(file, "%*f %f", &idle_time) != 1) {
-        fprintf(stderr, "Could not parse /proc/uptime\n");
-        fclose(file);
-        return -1;
-    }
-    fclose(file);
-
-    struct timespec up_timespec;
-    if (clock_gettime(CLOCK_MONOTONIC, &up_timespec) == -1) {
-        fprintf(stderr, "Could not get monotonic time: %s\n", strerror(errno));
-	return -1;
-    }
-    float up_time = up_timespec.tv_sec + up_timespec.tv_nsec / 1e9;
-
-    struct timespec elapsed_timespec;
-    if (clock_gettime(CLOCK_BOOTTIME, &elapsed_timespec) == -1) {
-        fprintf(stderr, "Could not get boot time: %s\n", strerror(errno));
-        return -1;
-    }
-    int elapsed = elapsed_timespec.tv_sec;
-
-    char up_string[100], idle_string[100], sleep_string[100];
-    format_time(elapsed, up_string);
-    format_time((int)idle_time, idle_string);
-    format_time((int)(elapsed - up_time), sleep_string);
-    printf("up time: %s, idle time: %s, sleep time: %s\n", up_string, idle_string, sleep_string);
-
-    return 0;
-}