Merge "Mount schedTune cgroup as /dev/stune" into nyc-dev
diff --git a/adb/.clang-format b/adb/.clang-format
index 6737535..fc4eb1b 100644
--- a/adb/.clang-format
+++ b/adb/.clang-format
@@ -2,6 +2,7 @@
 AllowShortBlocksOnASingleLine: false
 AllowShortFunctionsOnASingleLine: false
 
+AccessModifierOffset: -2
 ColumnLimit: 100
 CommentPragmas: NOLINT:.*
 DerivePointerAlignment: false
diff --git a/adb/Android.mk b/adb/Android.mk
index d629223..4777883 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -50,6 +50,7 @@
     adb_listeners.cpp \
     adb_trace.cpp \
     adb_utils.cpp \
+    fdevent.cpp \
     sockets.cpp \
     transport.cpp \
     transport_local.cpp \
@@ -58,6 +59,9 @@
 LIBADB_TEST_SRCS := \
     adb_io_test.cpp \
     adb_utils_test.cpp \
+    fdevent_test.cpp \
+    socket_test.cpp \
+    sysdeps_test.cpp \
     transport_test.cpp \
 
 LIBADB_CFLAGS := \
@@ -74,12 +78,10 @@
     $(ADB_COMMON_windows_CFLAGS) \
 
 LIBADB_darwin_SRC_FILES := \
-    fdevent.cpp \
     get_my_path_darwin.cpp \
     usb_osx.cpp \
 
 LIBADB_linux_SRC_FILES := \
-    fdevent.cpp \
     get_my_path_linux.cpp \
     usb_linux.cpp \
 
@@ -87,14 +89,6 @@
     sysdeps_win32.cpp \
     usb_windows.cpp \
 
-LIBADB_TEST_linux_SRCS := \
-    fdevent_test.cpp \
-    socket_test.cpp \
-
-LIBADB_TEST_darwin_SRCS := \
-    fdevent_test.cpp \
-    socket_test.cpp \
-
 LIBADB_TEST_windows_SRCS := \
     sysdeps_win32_test.cpp \
 
@@ -105,7 +99,6 @@
 LOCAL_SRC_FILES := \
     $(LIBADB_SRC_FILES) \
     adb_auth_client.cpp \
-    fdevent.cpp \
     jdwp_service.cpp \
     usb_linux_client.cpp \
 
@@ -157,7 +150,7 @@
 
 LOCAL_SANITIZE := $(adb_target_sanitize)
 LOCAL_STATIC_LIBRARIES := libadbd
-LOCAL_SHARED_LIBRARIES := libbase libcutils
+LOCAL_SHARED_LIBRARIES := liblog libbase libcutils
 include $(BUILD_NATIVE_TEST)
 
 # libdiagnose_usb
diff --git a/adb/adb.cpp b/adb/adb.cpp
index 58ccd0a..cb54d04 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -883,8 +883,6 @@
             fprintf(stderr, "ADB server didn't ACK\n" );
             return -1;
         }
-
-        setsid();
     }
 #endif /* !defined(_WIN32) */
     return 0;
diff --git a/adb/adb_auth_client.cpp b/adb/adb_auth_client.cpp
index c4ffc85..8ef9948 100644
--- a/adb/adb_auth_client.cpp
+++ b/adb/adb_auth_client.cpp
@@ -44,6 +44,7 @@
 };
 
 static fdevent listener_fde;
+static fdevent framework_fde;
 static int framework_fd = -1;
 
 static void usb_disconnected(void* unused, atransport* t);
@@ -161,29 +162,30 @@
     return ret;
 }
 
-static void usb_disconnected(void* unused, atransport* t)
-{
+static void usb_disconnected(void* unused, atransport* t) {
     D("USB disconnect");
     usb_transport = NULL;
     needs_retry = false;
 }
 
-static void adb_auth_event(int fd, unsigned events, void *data)
-{
+static void framework_disconnected() {
+    D("Framework disconnect");
+    fdevent_remove(&framework_fde);
+    framework_fd = -1;
+}
+
+static void adb_auth_event(int fd, unsigned events, void*) {
     char response[2];
     int ret;
 
     if (events & FDE_READ) {
         ret = unix_read(fd, response, sizeof(response));
         if (ret <= 0) {
-            D("Framework disconnect");
-            if (usb_transport)
-                fdevent_remove(&usb_transport->auth_fde);
-            framework_fd = -1;
-        }
-        else if (ret == 2 && response[0] == 'O' && response[1] == 'K') {
-            if (usb_transport)
+            framework_disconnected();
+        } else if (ret == 2 && response[0] == 'O' && response[1] == 'K') {
+            if (usb_transport) {
                 adb_auth_verified(usb_transport);
+            }
         }
     }
 }
@@ -221,13 +223,9 @@
         D("Failed to write PK, errno=%d", errno);
         return;
     }
-
-    fdevent_install(&t->auth_fde, framework_fd, adb_auth_event, t);
-    fdevent_add(&t->auth_fde, FDE_READ);
 }
 
-static void adb_auth_listener(int fd, unsigned events, void *data)
-{
+static void adb_auth_listener(int fd, unsigned events, void* data) {
     sockaddr_storage addr;
     socklen_t alen;
     int s;
@@ -240,7 +238,14 @@
         return;
     }
 
+    if (framework_fd >= 0) {
+        PLOG(WARNING) << "adb received framework auth socket connection again";
+        framework_disconnected();
+    }
+
     framework_fd = s;
+    fdevent_install(&framework_fde, framework_fd, adb_auth_event, nullptr);
+    fdevent_add(&framework_fde, FDE_READ);
 
     if (needs_retry) {
         needs_retry = false;
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index 474d1b4..26e376c 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -134,7 +134,7 @@
   return result;
 }
 
-// Given a relative or absolute filepath, create the parent directory hierarchy
+// Given a relative or absolute filepath, create the directory hierarchy
 // as needed. Returns true if the hierarchy is/was setup.
 bool mkdirs(const std::string& path) {
   // TODO: all the callers do unlink && mkdirs && adb_creat ---
@@ -157,12 +157,12 @@
     return true;
   }
 
+  const std::string parent(adb_dirname(path));
+
   // If dirname returned the same path as what we passed in, don't go recursive.
   // This can happen on Windows when walking up the directory hierarchy and not
   // finding anything that already exists (unlike POSIX that will eventually
   // find . or /).
-  const std::string parent(adb_dirname(path));
-
   if (parent == path) {
     errno = ENOENT;
     return false;
@@ -174,14 +174,14 @@
   }
 
   // Now that the parent directory hierarchy of 'path' has been ensured,
-  // create parent itself.
+  // create path itself.
   if (adb_mkdir(path, 0775) == -1) {
-    // Can't just check for errno == EEXIST because it might be a file that
-    // exists.
     const int saved_errno = errno;
-    if (directory_exists(parent)) {
+    // If someone else created the directory, that is ok.
+    if (directory_exists(path)) {
       return true;
     }
+    // There might be a pre-existing file at 'path', or there might have been some other error.
     errno = saved_errno;
     return false;
   }
@@ -213,6 +213,7 @@
 }
 
 #if !defined(_WIN32)
+// Windows version provided in sysdeps_win32.cpp
 bool set_file_block_mode(int fd, bool block) {
     int flags = fcntl(fd, F_GETFL, 0);
     if (flags == -1) {
diff --git a/adb/adb_utils_test.cpp b/adb/adb_utils_test.cpp
index 794dce6..f1ebaa1 100644
--- a/adb/adb_utils_test.cpp
+++ b/adb/adb_utils_test.cpp
@@ -112,20 +112,26 @@
 }
 
 void test_mkdirs(const std::string basepath) {
+  // Test creating a directory hierarchy.
   EXPECT_TRUE(mkdirs(basepath));
-  EXPECT_NE(-1, adb_creat(basepath.c_str(), 0600));
-  EXPECT_FALSE(mkdirs(basepath + "/subdir/"));
+  // Test finding an existing directory hierarchy.
+  EXPECT_TRUE(mkdirs(basepath));
+  const std::string filepath = basepath + "/file";
+  // Verify that the hierarchy was created by trying to create a file in it.
+  EXPECT_NE(-1, adb_creat(filepath.c_str(), 0600));
+  // If a file exists where we want a directory, the operation should fail.
+  EXPECT_FALSE(mkdirs(filepath));
 }
 
 TEST(adb_utils, mkdirs) {
   TemporaryDir td;
 
   // Absolute paths.
-  test_mkdirs(std::string(td.path) + "/dir/subdir/file");
+  test_mkdirs(std::string(td.path) + "/dir/subdir");
 
   // Relative paths.
   ASSERT_EQ(0, chdir(td.path)) << strerror(errno);
-  test_mkdirs(std::string("relative/subrel/file"));
+  test_mkdirs(std::string("relative/subrel"));
 }
 
 #if !defined(_WIN32)
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index b7b30c5..27b7109 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -21,6 +21,7 @@
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <unistd.h>
 
 // We only build the affinity WAR code for Linux.
 #if defined(__linux__)
@@ -125,6 +126,15 @@
         close_stdin();
         setup_daemon_logging();
 
+#if !defined(_WIN32)
+        // Start a new session for the daemon. Do this here instead of after the fork so
+        // that a ctrl-c between the "starting server" and "done starting server" messages
+        // gets a chance to terminate the server.
+        if (setsid() == -1) {
+            fatal("setsid() failed: %s", strerror(errno));
+        }
+#endif
+
         // Any error output written to stderr now goes to adb.log. We could
         // keep around a copy of the stderr fd and use that to write any errors
         // encountered by the following code, but that is probably overkill.
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 85ab4d1..ff4eb22 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -157,7 +157,7 @@
         "                                 (-r: replace existing application)\n"
         "                                 (-t: allow test packages)\n"
         "                                 (-s: install application on sdcard)\n"
-        "                                 (-d: allow version code downgrade)\n"
+        "                                 (-d: allow version code downgrade (debuggable packages only))\n"
         "                                 (-g: grant all runtime permissions)\n"
         "  adb install-multiple [-lrtsdpg] <file...>\n"
         "                               - push this package file to the device and install it\n"
@@ -165,7 +165,7 @@
         "                                 (-r: replace existing application)\n"
         "                                 (-t: allow test packages)\n"
         "                                 (-s: install application on sdcard)\n"
-        "                                 (-d: allow version code downgrade)\n"
+        "                                 (-d: allow version code downgrade (debuggable packages only))\n"
         "                                 (-p: partial application install)\n"
         "                                 (-g: grant all runtime permissions)\n"
         "  adb uninstall [-k] <package> - remove this app package from the device\n"
@@ -482,7 +482,7 @@
 // Loops to read from stdin and push the data to the given FD.
 // The argument should be a pointer to a StdinReadArgs object. This function
 // will take ownership of the object and delete it when finished.
-static void* stdin_read_thread_loop(void* x) {
+static void stdin_read_thread_loop(void* x) {
     std::unique_ptr<StdinReadArgs> args(reinterpret_cast<StdinReadArgs*>(x));
 
 #if !defined(_WIN32)
@@ -586,8 +586,6 @@
             }
         }
     }
-
-    return nullptr;
 }
 
 // Returns a shell service string with the indicated arguments and command.
@@ -1108,8 +1106,9 @@
         }
 
         fprintf(stderr,"- waiting for device -\n");
-        adb_sleep_ms(1000);
-        wait_for_device("wait-for-device", transport_type, serial);
+        if (!wait_for_device("wait-for-device", transport_type, serial)) {
+            return 1;
+        }
     }
 
     int exit_code = read_and_dump(fd, use_shell_protocol);
diff --git a/adb/fdevent.cpp b/adb/fdevent.cpp
index 386f221..902548e 100644
--- a/adb/fdevent.cpp
+++ b/adb/fdevent.cpp
@@ -21,12 +21,11 @@
 #include "fdevent.h"
 
 #include <fcntl.h>
-#include <poll.h>
 #include <stdlib.h>
 #include <string.h>
-#include <sys/ioctl.h>
 #include <unistd.h>
 
+#include <atomic>
 #include <list>
 #include <unordered_map>
 #include <vector>
@@ -54,7 +53,7 @@
 
 struct PollNode {
   fdevent* fde;
-  ::pollfd pollfd;
+  adb_pollfd pollfd;
 
   PollNode(fdevent* fde) : fde(fde) {
       memset(&pollfd, 0, sizeof(pollfd));
@@ -72,18 +71,19 @@
 // That's why we don't need a lock for fdevent.
 static auto& g_poll_node_map = *new std::unordered_map<int, PollNode>();
 static auto& g_pending_list = *new std::list<fdevent*>();
+static std::atomic<bool> terminate_loop(false);
 static bool main_thread_valid;
-static pthread_t main_thread;
+static unsigned long main_thread_id;
 
 static void check_main_thread() {
     if (main_thread_valid) {
-        CHECK_NE(0, pthread_equal(main_thread, pthread_self()));
+        CHECK_EQ(main_thread_id, adb_thread_id());
     }
 }
 
 static void set_main_thread() {
     main_thread_valid = true;
-    main_thread = pthread_self();
+    main_thread_id = adb_thread_id();
 }
 
 static std::string dump_fde(const fdevent* fde) {
@@ -217,7 +217,7 @@
     fdevent_set(fde, (fde->state & FDE_EVENTMASK) & ~events);
 }
 
-static std::string dump_pollfds(const std::vector<pollfd>& pollfds) {
+static std::string dump_pollfds(const std::vector<adb_pollfd>& pollfds) {
     std::string result;
     for (const auto& pollfd : pollfds) {
         std::string op;
@@ -233,13 +233,13 @@
 }
 
 static void fdevent_process() {
-    std::vector<pollfd> pollfds;
+    std::vector<adb_pollfd> pollfds;
     for (const auto& pair : g_poll_node_map) {
         pollfds.push_back(pair.second.pollfd);
     }
     CHECK_GT(pollfds.size(), 0u);
     D("poll(), pollfds = %s", dump_pollfds(pollfds).c_str());
-    int ret = TEMP_FAILURE_RETRY(poll(&pollfds[0], pollfds.size(), -1));
+    int ret = adb_poll(&pollfds[0], pollfds.size(), -1);
     if (ret == -1) {
         PLOG(ERROR) << "poll(), ret = " << ret;
         return;
@@ -289,6 +289,9 @@
 }
 
 #if !ADB_HOST
+
+#include <sys/ioctl.h>
+
 static void fdevent_subproc_event_func(int fd, unsigned ev,
                                        void* /* userdata */)
 {
@@ -363,6 +366,10 @@
 #endif // !ADB_HOST
 
     while (true) {
+        if (terminate_loop) {
+            return;
+        }
+
         D("--- --- waiting for events");
 
         fdevent_process();
@@ -375,6 +382,10 @@
     }
 }
 
+void fdevent_terminate_loop() {
+    terminate_loop = true;
+}
+
 size_t fdevent_installed_count() {
     return g_poll_node_map.size();
 }
@@ -383,4 +394,5 @@
     g_poll_node_map.clear();
     g_pending_list.clear();
     main_thread_valid = false;
+    terminate_loop = false;
 }
diff --git a/adb/fdevent.h b/adb/fdevent.h
index 657fde5..207f9b7 100644
--- a/adb/fdevent.h
+++ b/adb/fdevent.h
@@ -76,9 +76,9 @@
 */
 void fdevent_loop();
 
-// For debugging only.
+// The following functions are used only for tests.
+void fdevent_terminate_loop();
 size_t fdevent_installed_count();
-// For debugging only.
 void fdevent_reset();
 
 #endif
diff --git a/adb/fdevent_test.cpp b/adb/fdevent_test.cpp
index 7fe3d37..c933ed5 100644
--- a/adb/fdevent_test.cpp
+++ b/adb/fdevent_test.cpp
@@ -18,15 +18,13 @@
 
 #include <gtest/gtest.h>
 
-#include <pthread.h>
-#include <signal.h>
-
 #include <limits>
 #include <queue>
 #include <string>
 #include <vector>
 
 #include "adb_io.h"
+#include "fdevent_test.h"
 
 class FdHandler {
   public:
@@ -48,7 +46,7 @@
         if (events & FDE_READ) {
             ASSERT_EQ(fd, handler->read_fd_);
             char c;
-            ASSERT_EQ(1, read(fd, &c, 1));
+            ASSERT_EQ(1, adb_read(fd, &c, 1));
             handler->queue_.push(c);
             fdevent_add(&handler->write_fde_, FDE_WRITE);
         }
@@ -57,7 +55,7 @@
             ASSERT_FALSE(handler->queue_.empty());
             char c = handler->queue_.front();
             handler->queue_.pop();
-            ASSERT_EQ(1, write(fd, &c, 1));
+            ASSERT_EQ(1, adb_write(fd, &c, 1));
             if (handler->queue_.empty()) {
               fdevent_del(&handler->write_fde_, FDE_WRITE);
             }
@@ -72,29 +70,19 @@
     std::queue<char> queue_;
 };
 
-static void signal_handler(int) {
-    pthread_exit(nullptr);
-}
-
-class FdeventTest : public ::testing::Test {
-  protected:
-    static void SetUpTestCase() {
-        ASSERT_NE(SIG_ERR, signal(SIGUSR1, signal_handler));
-        ASSERT_NE(SIG_ERR, signal(SIGPIPE, SIG_IGN));
-    }
-
-    virtual void SetUp() {
-        fdevent_reset();
-        ASSERT_EQ(0u, fdevent_installed_count());
-    }
-};
-
 struct ThreadArg {
     int first_read_fd;
     int last_write_fd;
     size_t middle_pipe_count;
 };
 
+TEST_F(FdeventTest, fdevent_terminate) {
+    adb_thread_t thread;
+    PrepareThread();
+    ASSERT_TRUE(adb_thread_create([](void*) { fdevent_loop(); }, nullptr, &thread));
+    TerminateThread(thread);
+}
+
 static void FdEventThreadFunc(ThreadArg* arg) {
     std::vector<int> read_fds;
     std::vector<int> write_fds;
@@ -102,7 +90,7 @@
     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));
+        ASSERT_EQ(0, adb_socketpair(fds));
         read_fds.push_back(fds[0]);
         write_fds.push_back(fds[1]);
     }
@@ -122,9 +110,9 @@
     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;
+    ASSERT_EQ(0, adb_socketpair(fd_pair1));
+    ASSERT_EQ(0, adb_socketpair(fd_pair2));
+    adb_thread_t thread;
     ThreadArg thread_arg;
     thread_arg.first_read_fd = fd_pair1[0];
     thread_arg.last_write_fd = fd_pair2[1];
@@ -132,9 +120,9 @@
     int writer = fd_pair1[1];
     int reader = fd_pair2[0];
 
-    ASSERT_EQ(0, pthread_create(&thread, nullptr,
-                                reinterpret_cast<void* (*)(void*)>(FdEventThreadFunc),
-                                &thread_arg));
+    PrepareThread();
+    ASSERT_TRUE(adb_thread_create(reinterpret_cast<void (*)(void*)>(FdEventThreadFunc), &thread_arg,
+                                  &thread));
 
     for (size_t i = 0; i < MESSAGE_LOOP_COUNT; ++i) {
         std::string read_buffer = MESSAGE;
@@ -144,10 +132,9 @@
         ASSERT_EQ(read_buffer, write_buffer);
     }
 
-    ASSERT_EQ(0, pthread_kill(thread, SIGUSR1));
-    ASSERT_EQ(0, pthread_join(thread, nullptr));
-    ASSERT_EQ(0, close(writer));
-    ASSERT_EQ(0, close(reader));
+    TerminateThread(thread);
+    ASSERT_EQ(0, adb_close(writer));
+    ASSERT_EQ(0, adb_close(reader));
 }
 
 struct InvalidFdArg {
@@ -161,7 +148,7 @@
     ASSERT_EQ(arg->expected_events, events);
     fdevent_remove(&arg->fde);
     if (++*(arg->happened_event_count) == 2) {
-        pthread_exit(nullptr);
+        fdevent_terminate_loop();
     }
 }
 
@@ -184,9 +171,7 @@
 }
 
 TEST_F(FdeventTest, invalid_fd) {
-    pthread_t thread;
-    ASSERT_EQ(0, pthread_create(&thread, nullptr,
-                                reinterpret_cast<void* (*)(void*)>(InvalidFdThreadFunc),
-                                nullptr));
-    ASSERT_EQ(0, pthread_join(thread, nullptr));
+    adb_thread_t thread;
+    ASSERT_TRUE(adb_thread_create(InvalidFdThreadFunc, nullptr, &thread));
+    ASSERT_TRUE(adb_thread_join(thread));
 }
diff --git a/adb/fdevent_test.h b/adb/fdevent_test.h
new file mode 100644
index 0000000..c853bce
--- /dev/null
+++ b/adb/fdevent_test.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "socket.h"
+#include "sysdeps.h"
+
+class FdeventTest : public ::testing::Test {
+  protected:
+    int dummy = -1;
+
+    static void SetUpTestCase() {
+#if !defined(_WIN32)
+        ASSERT_NE(SIG_ERR, signal(SIGPIPE, SIG_IGN));
+#endif
+    }
+
+    void SetUp() override {
+        fdevent_reset();
+        ASSERT_EQ(0u, fdevent_installed_count());
+    }
+
+    // Register a dummy socket used to wake up the fdevent loop to tell it to die.
+    void PrepareThread() {
+        int dummy_fds[2];
+        if (adb_socketpair(dummy_fds) != 0) {
+            FAIL() << "failed to create socketpair: " << strerror(errno);
+        }
+
+        asocket* dummy_socket = create_local_socket(dummy_fds[1]);
+        if (!dummy_socket) {
+            FAIL() << "failed to create local socket: " << strerror(errno);
+        }
+        dummy_socket->ready(dummy_socket);
+        dummy = dummy_fds[0];
+    }
+
+    void TerminateThread(adb_thread_t thread) {
+        fdevent_terminate_loop();
+        ASSERT_TRUE(WriteFdExactly(dummy, "", 1));
+        ASSERT_TRUE(adb_thread_join(thread));
+        ASSERT_EQ(0, adb_close(dummy));
+    }
+};
diff --git a/adb/file_sync_client.cpp b/adb/file_sync_client.cpp
index 51fc143..85aaa61 100644
--- a/adb/file_sync_client.cpp
+++ b/adb/file_sync_client.cpp
@@ -88,7 +88,8 @@
             : total_bytes_(0),
               start_time_ms_(CurrentTimeMs()),
               expected_total_bytes_(0),
-              expect_multiple_files_(false) {
+              expect_multiple_files_(false),
+              expect_done_(false) {
         max = SYNC_DATA_MAX; // TODO: decide at runtime.
 
         std::string error;
@@ -117,6 +118,16 @@
 
     bool IsValid() { return fd >= 0; }
 
+    bool ReceivedError(const char* from, const char* to) {
+        adb_pollfd pfd = {.fd = fd, .events = POLLIN};
+        int rc = adb_poll(&pfd, 1, 0);
+        if (rc < 0) {
+            Error("failed to poll: %s", strerror(errno));
+            return true;
+        }
+        return rc != 0;
+    }
+
     bool SendRequest(int id, const char* path_and_mode) {
         size_t path_length = strlen(path_and_mode);
         if (path_length > 1024) {
@@ -175,6 +186,7 @@
         p += sizeof(SyncRequest);
 
         WriteOrDie(lpath, rpath, &buf[0], (p - &buf[0]));
+        expect_done_ = true;
         total_bytes_ += data_length;
         return true;
     }
@@ -220,6 +232,11 @@
             total_bytes_ += bytes_read;
             bytes_copied += bytes_read;
 
+            // Check to see if we've received an error from the other side.
+            if (ReceivedError(lpath, rpath)) {
+                break;
+            }
+
             ReportProgress(rpath, bytes_copied, total_size);
         }
 
@@ -228,17 +245,24 @@
         syncmsg msg;
         msg.data.id = ID_DONE;
         msg.data.size = mtime;
+        expect_done_ = true;
         return WriteOrDie(lpath, rpath, &msg.data, sizeof(msg.data));
     }
 
     bool CopyDone(const char* from, const char* to) {
         syncmsg msg;
         if (!ReadFdExactly(fd, &msg.status, sizeof(msg.status))) {
-            Error("failed to copy '%s' to '%s': no ID_DONE: %s", from, to, strerror(errno));
+            Error("failed to copy '%s' to '%s': couldn't read from device", from, to);
             return false;
         }
         if (msg.status.id == ID_OKAY) {
-            return true;
+            if (expect_done_) {
+                expect_done_ = false;
+                return true;
+            } else {
+                Error("failed to copy '%s' to '%s': received premature success", from, to);
+                return true;
+            }
         }
         if (msg.status.id != ID_FAIL) {
             Error("failed to copy '%s' to '%s': unknown reason %d", from, to, msg.status.id);
@@ -357,6 +381,7 @@
 
     uint64_t expected_total_bytes_;
     bool expect_multiple_files_;
+    bool expect_done_;
 
     LinePrinter line_printer_;
 
diff --git a/adb/file_sync_service.cpp b/adb/file_sync_service.cpp
index 29c6629..926dbcf 100644
--- a/adb/file_sync_service.cpp
+++ b/adb/file_sync_service.cpp
@@ -183,8 +183,6 @@
     }
 
     while (true) {
-        unsigned int len;
-
         if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) goto fail;
 
         if (msg.data.id != ID_DATA) {
@@ -193,17 +191,17 @@
                 break;
             }
             SendSyncFail(s, "invalid data message");
-            goto fail;
+            goto abort;
         }
-        len = msg.data.size;
-        if (len > buffer.size()) { // TODO: resize buffer?
+
+        if (msg.data.size > buffer.size()) {  // TODO: resize buffer?
             SendSyncFail(s, "oversize data message");
-            goto fail;
+            goto abort;
         }
 
-        if (!ReadFdExactly(s, &buffer[0], len)) goto fail;
+        if (!ReadFdExactly(s, &buffer[0], msg.data.size)) goto abort;
 
-        if (!WriteFdExactly(fd, &buffer[0], len)) {
+        if (!WriteFdExactly(fd, &buffer[0], msg.data.size)) {
             SendSyncFailErrno(s, "write failed");
             goto fail;
         }
@@ -221,6 +219,35 @@
     return WriteFdExactly(s, &msg.status, sizeof(msg.status));
 
 fail:
+    // If there's a problem on the device, we'll send an ID_FAIL message and
+    // close the socket. Unfortunately the kernel will sometimes throw that
+    // data away if the other end keeps writing without reading (which is
+    // the case with old versions of adb). To maintain compatibility, keep
+    // reading and throwing away ID_DATA packets until the other side notices
+    // that we've reported an error.
+    while (true) {
+        if (!ReadFdExactly(s, &msg.data, sizeof(msg.data))) goto fail;
+
+        if (msg.data.id == ID_DONE) {
+            goto abort;
+        } else if (msg.data.id != ID_DATA) {
+            char id[5];
+            memcpy(id, &msg.data.id, sizeof(msg.data.id));
+            id[4] = '\0';
+            D("handle_send_fail received unexpected id '%s' during failure", id);
+            goto abort;
+        }
+
+        if (msg.data.size > buffer.size()) {
+            D("handle_send_fail received oversized packet of length '%u' during failure",
+              msg.data.size);
+            goto abort;
+        }
+
+        if (!ReadFdExactly(s, &buffer[0], msg.data.size)) goto abort;
+    }
+
+abort:
     if (fd >= 0) adb_close(fd);
     if (do_unlink) adb_unlink(path);
     return false;
@@ -403,18 +430,6 @@
 void file_sync_service(int fd, void* cookie) {
     std::vector<char> buffer(SYNC_DATA_MAX);
 
-    // If there's a problem on the device, we'll send an ID_FAIL message and
-    // close the socket. Unfortunately the kernel will sometimes throw that
-    // data away if the other end keeps writing without reading (which is
-    // the normal case with our protocol --- they won't read until the end).
-    // So set SO_LINGER to give the client 20s to get around to reading our
-    // failure response. Without this, the other side's ability to report
-    // useful errors is reduced.
-    struct linger l;
-    l.l_onoff = 1;
-    l.l_linger = 20;
-    adb_setsockopt(fd, SOL_SOCKET, SO_LINGER, &l, sizeof(l));
-
     while (handle_sync_command(fd, buffer)) {
     }
 
diff --git a/adb/file_sync_service.h b/adb/file_sync_service.h
index 38382c1..460e9dc 100644
--- a/adb/file_sync_service.h
+++ b/adb/file_sync_service.h
@@ -41,25 +41,25 @@
 
 union syncmsg {
     struct __attribute__((packed)) {
-        unsigned id;
-        unsigned mode;
-        unsigned size;
-        unsigned time;
+        uint32_t id;
+        uint32_t mode;
+        uint32_t size;
+        uint32_t time;
     } stat;
     struct __attribute__((packed)) {
-        unsigned id;
-        unsigned mode;
-        unsigned size;
-        unsigned time;
-        unsigned namelen;
+        uint32_t id;
+        uint32_t mode;
+        uint32_t size;
+        uint32_t time;
+        uint32_t namelen;
     } dent;
     struct __attribute__((packed)) {
-        unsigned id;
-        unsigned size;
+        uint32_t id;
+        uint32_t size;
     } data;
     struct __attribute__((packed)) {
-        unsigned id;
-        unsigned msglen;
+        uint32_t id;
+        uint32_t msglen;
     } status;
 };
 
diff --git a/adb/services.cpp b/adb/services.cpp
index 9cbf787..2eef1c2 100644
--- a/adb/services.cpp
+++ b/adb/services.cpp
@@ -49,6 +49,7 @@
 #include "remount_service.h"
 #include "services.h"
 #include "shell_service.h"
+#include "sysdeps.h"
 #include "transport.h"
 
 struct stinfo {
@@ -57,13 +58,11 @@
     void *cookie;
 };
 
-void *service_bootstrap_func(void *x)
-{
+static void service_bootstrap_func(void* x) {
     stinfo* sti = reinterpret_cast<stinfo*>(x);
     adb_thread_setname(android::base::StringPrintf("service %d", sti->fd));
     sti->func(sti->fd, sti->cookie);
     free(sti);
-    return 0;
 }
 
 #if !ADB_HOST
@@ -371,12 +370,21 @@
         std::string error = "unknown error";
         const char* serial = sinfo->serial.length() ? sinfo->serial.c_str() : NULL;
         atransport* t = acquire_one_transport(sinfo->transport_type, serial, &is_ambiguous, &error);
-
         if (t != nullptr && t->connection_state == sinfo->state) {
             SendOkay(fd);
             break;
         } else if (!is_ambiguous) {
-            adb_sleep_ms(1000);
+            adb_pollfd pfd = {.fd = fd, .events = POLLIN };
+            int rc = adb_poll(&pfd, 1, 1000);
+            if (rc < 0) {
+                SendFail(fd, error);
+                break;
+            } else if (rc > 0 && (pfd.revents & POLLHUP) != 0) {
+                // The other end of the socket is closed, probably because the other side was
+                // terminated, bail out.
+                break;
+            }
+
             // Try again...
         } else {
             SendFail(fd, error);
diff --git a/adb/shell_service.cpp b/adb/shell_service.cpp
index d080e09..f84447f 100644
--- a/adb/shell_service.cpp
+++ b/adb/shell_service.cpp
@@ -198,7 +198,7 @@
     // Opens the file at |pts_name|.
     int OpenPtyChildFd(const char* pts_name, ScopedFd* error_sfd);
 
-    static void* ThreadHandler(void* userdata);
+    static void ThreadHandler(void* userdata);
     void PassDataStreams();
     void WaitForExit();
 
@@ -465,7 +465,7 @@
     return child_fd;
 }
 
-void* Subprocess::ThreadHandler(void* userdata) {
+void Subprocess::ThreadHandler(void* userdata) {
     Subprocess* subprocess = reinterpret_cast<Subprocess*>(userdata);
 
     adb_thread_setname(android::base::StringPrintf(
@@ -475,8 +475,6 @@
 
     D("deleting Subprocess for PID %d", subprocess->pid());
     delete subprocess;
-
-    return nullptr;
 }
 
 void Subprocess::PassDataStreams() {
diff --git a/adb/socket_test.cpp b/adb/socket_test.cpp
index 03cab64..471ca09 100644
--- a/adb/socket_test.cpp
+++ b/adb/socket_test.cpp
@@ -18,119 +18,89 @@
 
 #include <gtest/gtest.h>
 
+#include <array>
 #include <limits>
 #include <queue>
 #include <string>
 #include <vector>
 
-#include <pthread.h>
-#include <signal.h>
 #include <unistd.h>
 
 #include "adb.h"
 #include "adb_io.h"
+#include "fdevent_test.h"
 #include "socket.h"
 #include "sysdeps.h"
 
-static void signal_handler(int) {
-    ASSERT_EQ(1u, fdevent_installed_count());
-    pthread_exit(nullptr);
-}
-
-// On host, register a dummy socket, so fdevet_loop() will not abort when previously
-// registered local sockets are all closed. On device, fdevent_subproc_setup() installs
-// one fdevent which can be considered as dummy socket.
-static void InstallDummySocket() {
-#if ADB_HOST
-    int dummy_fds[2];
-    ASSERT_EQ(0, pipe(dummy_fds));
-    asocket* dummy_socket = create_local_socket(dummy_fds[0]);
-    ASSERT_TRUE(dummy_socket != nullptr);
-    dummy_socket->ready(dummy_socket);
-#endif
-}
-
 struct ThreadArg {
     int first_read_fd;
     int last_write_fd;
     size_t middle_pipe_count;
 };
 
-static void FdEventThreadFunc(ThreadArg* arg) {
-    std::vector<int> read_fds;
-    std::vector<int> write_fds;
+class LocalSocketTest : public FdeventTest {};
 
-    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, adb_socketpair(fds));
-        read_fds.push_back(fds[0]);
-        write_fds.push_back(fds[1]);
-    }
-    write_fds.push_back(arg->last_write_fd);
-
-    for (size_t i = 0; i < read_fds.size(); ++i) {
-        asocket* reader = create_local_socket(read_fds[i]);
-        ASSERT_TRUE(reader != nullptr);
-        asocket* writer = create_local_socket(write_fds[i]);
-        ASSERT_TRUE(writer != nullptr);
-        reader->peer = writer;
-        writer->peer = reader;
-        reader->ready(reader);
-    }
-
-    InstallDummySocket();
+static void FdEventThreadFunc(void*) {
     fdevent_loop();
 }
 
-class LocalSocketTest : public ::testing::Test {
-  protected:
-    static void SetUpTestCase() {
-        ASSERT_NE(SIG_ERR, signal(SIGUSR1, signal_handler));
-        ASSERT_NE(SIG_ERR, signal(SIGPIPE, SIG_IGN));
-    }
-
-    virtual void SetUp() {
-        fdevent_reset();
-        ASSERT_EQ(0u, fdevent_installed_count());
-    }
-};
-
 TEST_F(LocalSocketTest, smoke) {
-    const size_t PIPE_COUNT = 100;
-    const size_t MESSAGE_LOOP_COUNT = 100;
+    // Join two socketpairs with a chain of intermediate socketpairs.
+    int first[2];
+    std::vector<std::array<int, 2>> intermediates;
+    int last[2];
+
+    constexpr size_t INTERMEDIATE_COUNT = 50;
+    constexpr size_t MESSAGE_LOOP_COUNT = 100;
     const std::string MESSAGE = "socket_test";
-    int fd_pair1[2];
-    int fd_pair2[2];
-    ASSERT_EQ(0, adb_socketpair(fd_pair1));
-    ASSERT_EQ(0, adb_socketpair(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));
+    intermediates.resize(INTERMEDIATE_COUNT);
+    ASSERT_EQ(0, adb_socketpair(first)) << strerror(errno);
+    ASSERT_EQ(0, adb_socketpair(last)) << strerror(errno);
+    asocket* prev_tail = create_local_socket(first[1]);
+    ASSERT_NE(nullptr, prev_tail);
 
-    usleep(1000);
+    auto connect = [](asocket* tail, asocket* head) {
+        tail->peer = head;
+        head->peer = tail;
+        tail->ready(tail);
+    };
+
+    for (auto& intermediate : intermediates) {
+        ASSERT_EQ(0, adb_socketpair(intermediate.data())) << strerror(errno);
+
+        asocket* head = create_local_socket(intermediate[0]);
+        ASSERT_NE(nullptr, head);
+
+        asocket* tail = create_local_socket(intermediate[1]);
+        ASSERT_NE(nullptr, tail);
+
+        connect(prev_tail, head);
+        prev_tail = tail;
+    }
+
+    asocket* end = create_local_socket(last[0]);
+    ASSERT_NE(nullptr, end);
+    connect(prev_tail, end);
+
+    PrepareThread();
+    adb_thread_t thread;
+    ASSERT_TRUE(adb_thread_create(FdEventThreadFunc, nullptr, &thread));
+
     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_TRUE(WriteFdExactly(first[0], &read_buffer[0], read_buffer.size()));
+        ASSERT_TRUE(ReadFdExactly(last[1], &write_buffer[0], write_buffer.size()));
         ASSERT_EQ(read_buffer, write_buffer);
     }
-    ASSERT_EQ(0, adb_close(writer));
-    ASSERT_EQ(0, adb_close(reader));
-    // Wait until the local sockets are closed.
-    sleep(1);
 
-    ASSERT_EQ(0, pthread_kill(thread, SIGUSR1));
-    ASSERT_EQ(0, pthread_join(thread, nullptr));
+    ASSERT_EQ(0, adb_close(first[0]));
+    ASSERT_EQ(0, adb_close(last[1]));
+
+    // Wait until the local sockets are closed.
+    adb_sleep_ms(100);
+    TerminateThread(thread);
 }
 
 struct CloseWithPacketArg {
@@ -160,7 +130,6 @@
     s->peer = cause_close_s;
     cause_close_s->ready(cause_close_s);
 
-    InstallDummySocket();
     fdevent_loop();
 }
 
@@ -176,21 +145,19 @@
     CloseWithPacketArg arg;
     arg.socket_fd = socket_fd[1];
     arg.cause_close_fd = cause_close_fd[1];
-    pthread_t thread;
-    ASSERT_EQ(0, pthread_create(&thread, nullptr,
-                                reinterpret_cast<void* (*)(void*)>(CloseWithPacketThreadFunc),
-                                &arg));
-    // Wait until the fdevent_loop() starts.
-    sleep(1);
-    ASSERT_EQ(0, adb_close(cause_close_fd[0]));
-    sleep(1);
-    ASSERT_EQ(2u, fdevent_installed_count());
-    ASSERT_EQ(0, adb_close(socket_fd[0]));
-    // Wait until the socket is closed.
-    sleep(1);
 
-    ASSERT_EQ(0, pthread_kill(thread, SIGUSR1));
-    ASSERT_EQ(0, pthread_join(thread, nullptr));
+    PrepareThread();
+    adb_thread_t thread;
+    ASSERT_TRUE(adb_thread_create(reinterpret_cast<void (*)(void*)>(CloseWithPacketThreadFunc),
+                                  &arg, &thread));
+    // Wait until the fdevent_loop() starts.
+    adb_sleep_ms(100);
+    ASSERT_EQ(0, adb_close(cause_close_fd[0]));
+    adb_sleep_ms(100);
+    EXPECT_EQ(2u, fdevent_installed_count());
+    ASSERT_EQ(0, adb_close(socket_fd[0]));
+
+    TerminateThread(thread);
 }
 
 // This test checks if we can read packets from a closing local socket.
@@ -203,26 +170,23 @@
     arg.socket_fd = socket_fd[1];
     arg.cause_close_fd = cause_close_fd[1];
 
-    pthread_t thread;
-    ASSERT_EQ(0, pthread_create(&thread, nullptr,
-                                reinterpret_cast<void* (*)(void*)>(CloseWithPacketThreadFunc),
-                                &arg));
+    PrepareThread();
+    adb_thread_t thread;
+    ASSERT_TRUE(adb_thread_create(reinterpret_cast<void (*)(void*)>(CloseWithPacketThreadFunc),
+                                  &arg, &thread));
     // Wait until the fdevent_loop() starts.
-    sleep(1);
+    adb_sleep_ms(100);
     ASSERT_EQ(0, adb_close(cause_close_fd[0]));
-    sleep(1);
-    ASSERT_EQ(2u, fdevent_installed_count());
+    adb_sleep_ms(100);
+    EXPECT_EQ(2u, fdevent_installed_count());
 
     // Verify if we can read successfully.
     std::vector<char> buf(arg.bytes_written);
+    ASSERT_NE(0u, arg.bytes_written);
     ASSERT_EQ(true, ReadFdExactly(socket_fd[0], buf.data(), buf.size()));
     ASSERT_EQ(0, adb_close(socket_fd[0]));
 
-    // Wait until the socket is closed.
-    sleep(1);
-
-    ASSERT_EQ(0, pthread_kill(thread, SIGUSR1));
-    ASSERT_EQ(0, pthread_join(thread, nullptr));
+    TerminateThread(thread);
 }
 
 // This test checks if we can close local socket in the following situation:
@@ -238,20 +202,17 @@
     arg.socket_fd = socket_fd[1];
     arg.cause_close_fd = cause_close_fd[1];
 
-    pthread_t thread;
-    ASSERT_EQ(0, pthread_create(&thread, nullptr,
-                                reinterpret_cast<void* (*)(void*)>(CloseWithPacketThreadFunc),
-                                &arg));
+    PrepareThread();
+    adb_thread_t thread;
+    ASSERT_TRUE(adb_thread_create(reinterpret_cast<void (*)(void*)>(CloseWithPacketThreadFunc),
+                                  &arg, &thread));
+
     // Wait until the fdevent_loop() starts.
-    sleep(1);
-    ASSERT_EQ(3u, fdevent_installed_count());
+    adb_sleep_ms(100);
+    EXPECT_EQ(3u, fdevent_installed_count());
     ASSERT_EQ(0, adb_close(socket_fd[0]));
 
-    // Wait until the socket is closed.
-    sleep(1);
-
-    ASSERT_EQ(0, pthread_kill(thread, SIGUSR1));
-    ASSERT_EQ(0, pthread_join(thread, nullptr));
+    TerminateThread(thread);
 }
 
 #if defined(__linux__)
@@ -260,50 +221,52 @@
     std::string error;
     int fd = network_loopback_client(5038, SOCK_STREAM, &error);
     ASSERT_GE(fd, 0) << error;
-    sleep(2);
+    adb_sleep_ms(200);
     ASSERT_EQ(0, adb_close(fd));
 }
 
 struct CloseRdHupSocketArg {
-  int socket_fd;
+    int socket_fd;
 };
 
 static void CloseRdHupSocketThreadFunc(CloseRdHupSocketArg* arg) {
-  asocket* s = create_local_socket(arg->socket_fd);
-  ASSERT_TRUE(s != nullptr);
+    asocket* s = create_local_socket(arg->socket_fd);
+    ASSERT_TRUE(s != nullptr);
 
-  InstallDummySocket();
-  fdevent_loop();
+    fdevent_loop();
 }
 
 // This test checks if we can close sockets in CLOSE_WAIT state.
 TEST_F(LocalSocketTest, close_socket_in_CLOSE_WAIT_state) {
-  std::string error;
-  int listen_fd = network_inaddr_any_server(5038, SOCK_STREAM, &error);
-  ASSERT_GE(listen_fd, 0);
-  pthread_t client_thread;
-  ASSERT_EQ(0, pthread_create(&client_thread, nullptr,
-                              reinterpret_cast<void* (*)(void*)>(ClientThreadFunc), nullptr));
+    std::string error;
+    int listen_fd = network_inaddr_any_server(5038, SOCK_STREAM, &error);
+    ASSERT_GE(listen_fd, 0);
 
-  struct sockaddr addr;
-  socklen_t alen;
-  alen = sizeof(addr);
-  int accept_fd = adb_socket_accept(listen_fd, &addr, &alen);
-  ASSERT_GE(accept_fd, 0);
-  CloseRdHupSocketArg arg;
-  arg.socket_fd = accept_fd;
-  pthread_t thread;
-  ASSERT_EQ(0, pthread_create(&thread, nullptr,
-                              reinterpret_cast<void* (*)(void*)>(CloseRdHupSocketThreadFunc),
-                              &arg));
-  // Wait until the fdevent_loop() starts.
-  sleep(1);
-  ASSERT_EQ(2u, fdevent_installed_count());
-  // Wait until the client closes its socket.
-  ASSERT_EQ(0, pthread_join(client_thread, nullptr));
-  sleep(2);
-  ASSERT_EQ(0, pthread_kill(thread, SIGUSR1));
-  ASSERT_EQ(0, pthread_join(thread, nullptr));
+    adb_thread_t client_thread;
+    ASSERT_TRUE(adb_thread_create(reinterpret_cast<void (*)(void*)>(ClientThreadFunc), nullptr,
+                                  &client_thread));
+
+    struct sockaddr addr;
+    socklen_t alen;
+    alen = sizeof(addr);
+    int accept_fd = adb_socket_accept(listen_fd, &addr, &alen);
+    ASSERT_GE(accept_fd, 0);
+    CloseRdHupSocketArg arg;
+    arg.socket_fd = accept_fd;
+
+    PrepareThread();
+    adb_thread_t thread;
+    ASSERT_TRUE(adb_thread_create(reinterpret_cast<void (*)(void*)>(CloseRdHupSocketThreadFunc),
+                                  &arg, &thread));
+
+    // Wait until the fdevent_loop() starts.
+    adb_sleep_ms(100);
+    EXPECT_EQ(2u, fdevent_installed_count());
+
+    // Wait until the client closes its socket.
+    ASSERT_TRUE(adb_thread_join(client_thread));
+
+    TerminateThread(thread);
 }
 
 #endif  // defined(__linux__)
diff --git a/adb/sysdeps.h b/adb/sysdeps.h
index 16796cd..7af2979 100644
--- a/adb/sysdeps.h
+++ b/adb/sysdeps.h
@@ -30,6 +30,7 @@
 #include <vector>
 
 // Include this before open/unlink are defined as macros below.
+#include <android-base/errors.h>
 #include <android-base/utf8.h>
 
 /*
@@ -114,13 +115,62 @@
     LeaveCriticalSection( lock );
 }
 
-typedef  void*  (*adb_thread_func_t)(void*  arg);
+typedef void (*adb_thread_func_t)(void* arg);
+typedef HANDLE adb_thread_t;
 
-typedef  void (*win_thread_func_t)(void*  arg);
+struct adb_winthread_args {
+    adb_thread_func_t func;
+    void* arg;
+};
 
-static __inline__ bool adb_thread_create(adb_thread_func_t func, void* arg) {
-    uintptr_t tid = _beginthread((win_thread_func_t)func, 0, arg);
-    return (tid != static_cast<uintptr_t>(-1L));
+static unsigned __stdcall adb_winthread_wrapper(void* heap_args) {
+    // Move the arguments from the heap onto the thread's stack.
+    adb_winthread_args thread_args = *static_cast<adb_winthread_args*>(heap_args);
+    delete static_cast<adb_winthread_args*>(heap_args);
+    thread_args.func(thread_args.arg);
+    return 0;
+}
+
+static __inline__ bool adb_thread_create(adb_thread_func_t func, void* arg,
+                                         adb_thread_t* thread = nullptr) {
+    adb_winthread_args* args = new adb_winthread_args{.func = func, .arg = arg};
+    uintptr_t handle = _beginthreadex(nullptr, 0, adb_winthread_wrapper, args, 0, nullptr);
+    if (handle != static_cast<uintptr_t>(0)) {
+        if (thread) {
+            *thread = reinterpret_cast<HANDLE>(handle);
+        } else {
+            CloseHandle(thread);
+        }
+        return true;
+    }
+    return false;
+}
+
+static __inline__ bool adb_thread_join(adb_thread_t thread) {
+    switch (WaitForSingleObject(thread, INFINITE)) {
+        case WAIT_OBJECT_0:
+            CloseHandle(thread);
+            return true;
+
+        case WAIT_FAILED:
+            fprintf(stderr, "adb_thread_join failed: %s\n",
+                    android::base::SystemErrorCodeToString(GetLastError()).c_str());
+            break;
+
+        default:
+            abort();
+    }
+
+    return false;
+}
+
+static __inline__ bool adb_thread_detach(adb_thread_t thread) {
+    CloseHandle(thread);
+    return true;
+}
+
+static __inline__ void __attribute__((noreturn)) adb_thread_exit() {
+    _endthreadex(0);
 }
 
 static __inline__ int adb_thread_setname(const std::string& name) {
@@ -130,6 +180,14 @@
     return 0;
 }
 
+static __inline__ adb_thread_t adb_thread_self() {
+    return GetCurrentThread();
+}
+
+static __inline__ bool adb_thread_equal(adb_thread_t lhs, adb_thread_t rhs) {
+    return GetThreadId(lhs) == GetThreadId(rhs);
+}
+
 static __inline__  unsigned long adb_thread_id()
 {
     return GetCurrentThreadId();
@@ -213,24 +271,6 @@
 /* normally provided by <cutils/misc.h> */
 extern void*  load_file(const char*  pathname, unsigned*  psize);
 
-/* normally provided by "fdevent.h" */
-
-#define FDE_READ              0x0001
-#define FDE_WRITE             0x0002
-#define FDE_ERROR             0x0004
-#define FDE_DONT_CLOSE        0x0080
-
-typedef void (*fd_func)(int fd, unsigned events, void *userdata);
-
-fdevent *fdevent_create(int fd, fd_func func, void *arg);
-void     fdevent_destroy(fdevent *fde);
-void     fdevent_install(fdevent *fde, int fd, fd_func func, void *arg);
-void     fdevent_remove(fdevent *item);
-void     fdevent_set(fdevent *fde, unsigned events);
-void     fdevent_add(fdevent *fde, unsigned events);
-void     fdevent_del(fdevent *fde, unsigned events);
-void     fdevent_loop();
-
 static __inline__ void  adb_sleep_ms( int  mseconds )
 {
     Sleep( mseconds );
@@ -254,6 +294,14 @@
 
 extern int  adb_socketpair( int  sv[2] );
 
+struct adb_pollfd {
+    int fd;
+    short events;
+    short revents;
+};
+extern int adb_poll(adb_pollfd* fds, size_t nfds, int timeout);
+#define poll ___xxx_poll
+
 static __inline__ int adb_is_absolute_host_path(const char* path) {
     return isalpha(path[0]) && path[1] == ':' && path[2] == '\\';
 }
@@ -406,14 +454,14 @@
 
 #else /* !_WIN32 a.k.a. Unix */
 
-#include "fdevent.h"
 #include <cutils/misc.h>
 #include <cutils/sockets.h>
 #include <cutils/threads.h>
-#include <signal.h>
-#include <sys/wait.h>
-#include <sys/stat.h>
 #include <fcntl.h>
+#include <poll.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
 
 #include <pthread.h>
 #include <unistd.h>
@@ -656,16 +704,53 @@
 #define  unix_write  adb_write
 #define  unix_close  adb_close
 
-typedef void*  (*adb_thread_func_t)( void*  arg );
+// Win32 is limited to DWORDs for thread return values; limit the POSIX systems to this as well to
+// ensure compatibility.
+typedef void (*adb_thread_func_t)(void* arg);
+typedef pthread_t adb_thread_t;
 
-static __inline__ bool adb_thread_create(adb_thread_func_t start, void* arg) {
+struct adb_pthread_args {
+    adb_thread_func_t func;
+    void* arg;
+};
+
+static void* adb_pthread_wrapper(void* heap_args) {
+    // Move the arguments from the heap onto the thread's stack.
+    adb_pthread_args thread_args = *reinterpret_cast<adb_pthread_args*>(heap_args);
+    delete static_cast<adb_pthread_args*>(heap_args);
+    thread_args.func(thread_args.arg);
+    return nullptr;
+}
+
+static __inline__ bool adb_thread_create(adb_thread_func_t start, void* arg,
+                                         adb_thread_t* thread = nullptr) {
+    pthread_t temp;
     pthread_attr_t attr;
     pthread_attr_init(&attr);
-    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+    pthread_attr_setdetachstate(&attr, thread ? PTHREAD_CREATE_JOINABLE : PTHREAD_CREATE_DETACHED);
+    auto* pthread_args = new adb_pthread_args{.func = start, .arg = arg};
+    errno = pthread_create(&temp, &attr, adb_pthread_wrapper, pthread_args);
+    if (errno == 0) {
+        if (thread) {
+            *thread = temp;
+        }
+        return true;
+    }
+    return false;
+}
 
-    pthread_t thread;
-    errno = pthread_create(&thread, &attr, start, arg);
-    return (errno == 0);
+static __inline__ bool adb_thread_join(adb_thread_t thread) {
+    errno = pthread_join(thread, nullptr);
+    return errno == 0;
+}
+
+static __inline__ bool adb_thread_detach(adb_thread_t thread) {
+    errno = pthread_detach(thread);
+    return errno == 0;
+}
+
+static __inline__ void __attribute__((noreturn)) adb_thread_exit() {
+    pthread_exit(nullptr);
 }
 
 static __inline__ int adb_thread_setname(const std::string& name) {
@@ -716,6 +801,13 @@
 #undef   socketpair
 #define  socketpair   ___xxx_socketpair
 
+typedef struct pollfd adb_pollfd;
+static __inline__ int adb_poll(adb_pollfd* fds, size_t nfds, int timeout) {
+    return TEMP_FAILURE_RETRY(poll(fds, nfds, timeout));
+}
+
+#define poll ___xxx_poll
+
 static __inline__ void  adb_sleep_ms( int  mseconds )
 {
     usleep( mseconds*1000 );
diff --git a/adb/sysdeps_test.cpp b/adb/sysdeps_test.cpp
new file mode 100644
index 0000000..78efea8
--- /dev/null
+++ b/adb/sysdeps_test.cpp
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+#include <unistd.h>
+#include <atomic>
+
+#include "adb_io.h"
+#include "sysdeps.h"
+
+static void increment_atomic_int(void* c) {
+    sleep(1);
+    reinterpret_cast<std::atomic<int>*>(c)->fetch_add(1);
+}
+
+TEST(sysdeps_thread, smoke) {
+    std::atomic<int> counter(0);
+
+    for (int i = 0; i < 100; ++i) {
+        ASSERT_TRUE(adb_thread_create(increment_atomic_int, &counter));
+    }
+
+    sleep(2);
+    ASSERT_EQ(100, counter.load());
+}
+
+TEST(sysdeps_thread, join) {
+    std::atomic<int> counter(0);
+    std::vector<adb_thread_t> threads(500);
+    for (size_t i = 0; i < threads.size(); ++i) {
+        ASSERT_TRUE(adb_thread_create(increment_atomic_int, &counter, &threads[i]));
+    }
+
+    int current = counter.load();
+    ASSERT_GE(current, 0);
+    // Make sure that adb_thread_create actually creates threads, and doesn't do something silly
+    // like synchronously run the function passed in. The sleep in increment_atomic_int should be
+    // enough to keep this from being flakey.
+    ASSERT_LT(current, 500);
+
+    for (const auto& thread : threads) {
+        ASSERT_TRUE(adb_thread_join(thread));
+    }
+
+    ASSERT_EQ(500, counter.load());
+}
+
+TEST(sysdeps_thread, exit) {
+    adb_thread_t thread;
+    ASSERT_TRUE(adb_thread_create(
+        [](void*) {
+            adb_thread_exit();
+            for (;;) continue;
+        },
+        nullptr, &thread));
+    ASSERT_TRUE(adb_thread_join(thread));
+}
+
+TEST(sysdeps_socketpair, smoke) {
+    int fds[2];
+    ASSERT_EQ(0, adb_socketpair(fds)) << strerror(errno);
+    ASSERT_TRUE(WriteFdExactly(fds[0], "foo", 4));
+    ASSERT_TRUE(WriteFdExactly(fds[1], "bar", 4));
+
+    char buf[4];
+    ASSERT_TRUE(ReadFdExactly(fds[1], buf, 4));
+    ASSERT_STREQ(buf, "foo");
+    ASSERT_TRUE(ReadFdExactly(fds[0], buf, 4));
+    ASSERT_STREQ(buf, "bar");
+    ASSERT_EQ(0, adb_close(fds[0]));
+    ASSERT_EQ(0, adb_close(fds[1]));
+}
+
+TEST(sysdeps_fd, exhaustion) {
+    std::vector<int> fds;
+    int socketpair[2];
+
+    while (adb_socketpair(socketpair) == 0) {
+        fds.push_back(socketpair[0]);
+        fds.push_back(socketpair[1]);
+    }
+
+    ASSERT_EQ(EMFILE, errno) << strerror(errno);
+    for (int fd : fds) {
+        ASSERT_EQ(0, adb_close(fd));
+    }
+    ASSERT_EQ(0, adb_socketpair(socketpair));
+    ASSERT_EQ(socketpair[0], fds[0]);
+    ASSERT_EQ(socketpair[1], fds[1]);
+    ASSERT_EQ(0, adb_close(socketpair[0]));
+    ASSERT_EQ(0, adb_close(socketpair[1]));
+}
+
+class sysdeps_poll : public ::testing::Test {
+  protected:
+    int fds[2];
+    void SetUp() override {
+        ASSERT_EQ(0, adb_socketpair(fds)) << strerror(errno);
+    }
+
+    void TearDown() override {
+        if (fds[0] >= 0) {
+            ASSERT_EQ(0, adb_close(fds[0]));
+        }
+        if (fds[1] >= 0) {
+            ASSERT_EQ(0, adb_close(fds[1]));
+        }
+    }
+};
+
+TEST_F(sysdeps_poll, smoke) {
+    adb_pollfd pfd[2] = {};
+    pfd[0].fd = fds[0];
+    pfd[0].events = POLLRDNORM;
+    pfd[1].fd = fds[1];
+    pfd[1].events = POLLWRNORM;
+
+    pfd[0].revents = -1;
+    pfd[1].revents = -1;
+    EXPECT_EQ(1, adb_poll(pfd, 2, 0));
+    EXPECT_EQ(0, pfd[0].revents);
+    EXPECT_EQ(POLLWRNORM, pfd[1].revents);
+
+    ASSERT_TRUE(WriteFdExactly(fds[1], "foo", 4));
+
+    // Wait for the socketpair to be flushed.
+    pfd[0].revents = -1;
+    EXPECT_EQ(1, adb_poll(pfd, 1, 100));
+    EXPECT_EQ(POLLRDNORM, pfd[0].revents);
+    pfd[0].revents = -1;
+    pfd[1].revents = -1;
+    EXPECT_EQ(2, adb_poll(pfd, 2, 0));
+    EXPECT_EQ(POLLRDNORM, pfd[0].revents);
+    EXPECT_EQ(POLLWRNORM, pfd[1].revents);
+}
+
+TEST_F(sysdeps_poll, timeout) {
+    adb_pollfd pfd = {};
+    pfd.fd = fds[0];
+    pfd.events = POLLRDNORM;
+
+    EXPECT_EQ(0, adb_poll(&pfd, 1, 100));
+    EXPECT_EQ(0, pfd.revents);
+
+    ASSERT_TRUE(WriteFdExactly(fds[1], "foo", 4));
+
+    EXPECT_EQ(1, adb_poll(&pfd, 1, 100));
+    EXPECT_EQ(POLLRDNORM, pfd.revents);
+}
+
+TEST_F(sysdeps_poll, invalid_fd) {
+    adb_pollfd pfd[3] = {};
+    pfd[0].fd = fds[0];
+    pfd[0].events = POLLRDNORM;
+    pfd[1].fd = INT_MAX;
+    pfd[1].events = POLLRDNORM;
+    pfd[2].fd = fds[1];
+    pfd[2].events = POLLWRNORM;
+
+    ASSERT_TRUE(WriteFdExactly(fds[1], "foo", 4));
+
+    // Wait for the socketpair to be flushed.
+    EXPECT_EQ(1, adb_poll(pfd, 1, 100));
+    EXPECT_EQ(POLLRDNORM, pfd[0].revents);
+
+    EXPECT_EQ(3, adb_poll(pfd, 3, 0));
+    EXPECT_EQ(POLLRDNORM, pfd[0].revents);
+    EXPECT_EQ(POLLNVAL, pfd[1].revents);
+    EXPECT_EQ(POLLWRNORM, pfd[2].revents);
+}
+
+TEST_F(sysdeps_poll, duplicate_fd) {
+    adb_pollfd pfd[2] = {};
+    pfd[0].fd = fds[0];
+    pfd[0].events = POLLRDNORM;
+    pfd[1] = pfd[0];
+
+    EXPECT_EQ(0, adb_poll(pfd, 2, 0));
+    EXPECT_EQ(0, pfd[0].revents);
+    EXPECT_EQ(0, pfd[1].revents);
+
+    ASSERT_TRUE(WriteFdExactly(fds[1], "foo", 4));
+
+    EXPECT_EQ(2, adb_poll(pfd, 2, 100));
+    EXPECT_EQ(POLLRDNORM, pfd[0].revents);
+    EXPECT_EQ(POLLRDNORM, pfd[1].revents);
+}
+
+TEST_F(sysdeps_poll, disconnect) {
+    adb_pollfd pfd = {};
+    pfd.fd = fds[0];
+    pfd.events = POLLIN;
+
+    EXPECT_EQ(0, adb_poll(&pfd, 1, 0));
+    EXPECT_EQ(0, pfd.revents);
+
+    EXPECT_EQ(0, adb_close(fds[1]));
+    fds[1] = -1;
+
+    EXPECT_EQ(1, adb_poll(&pfd, 1, 100));
+
+    // Linux returns POLLIN | POLLHUP, Windows returns just POLLHUP.
+    EXPECT_EQ(POLLHUP, pfd.revents & POLLHUP);
+}
diff --git a/adb/sysdeps_win32.cpp b/adb/sysdeps_win32.cpp
index 0b08981..7eae186 100644
--- a/adb/sysdeps_win32.cpp
+++ b/adb/sysdeps_win32.cpp
@@ -29,6 +29,7 @@
 #include <memory>
 #include <string>
 #include <unordered_map>
+#include <vector>
 
 #include <cutils/sockets.h>
 
@@ -39,6 +40,7 @@
 #include <android-base/utf8.h>
 
 #include "adb.h"
+#include "adb_utils.h"
 
 extern void fatal(const char *fmt, ...);
 
@@ -54,7 +56,6 @@
     int (*_fh_lseek)(FH, int, int);
     int (*_fh_read)(FH, void*, int);
     int (*_fh_write)(FH, const void*, int);
-    void (*_fh_hook)(FH, int, EventHook);
 } FHClassRec;
 
 static void _fh_file_init(FH);
@@ -62,7 +63,6 @@
 static int _fh_file_lseek(FH, int, int);
 static int _fh_file_read(FH, void*, int);
 static int _fh_file_write(FH, const void*, int);
-static void _fh_file_hook(FH, int, EventHook);
 
 static const FHClassRec _fh_file_class = {
     _fh_file_init,
@@ -70,7 +70,6 @@
     _fh_file_lseek,
     _fh_file_read,
     _fh_file_write,
-    _fh_file_hook
 };
 
 static void _fh_socket_init(FH);
@@ -78,7 +77,6 @@
 static int _fh_socket_lseek(FH, int, int);
 static int _fh_socket_read(FH, void*, int);
 static int _fh_socket_write(FH, const void*, int);
-static void _fh_socket_hook(FH, int, EventHook);
 
 static const FHClassRec _fh_socket_class = {
     _fh_socket_init,
@@ -86,7 +84,6 @@
     _fh_socket_lseek,
     _fh_socket_read,
     _fh_socket_write,
-    _fh_socket_hook
 };
 
 #define assert(cond)                                                                       \
@@ -174,9 +171,6 @@
 /**************************************************************************/
 /**************************************************************************/
 
-/* used to emulate unix-domain socket pairs */
-typedef struct SocketPairRec_*  SocketPair;
-
 typedef struct FHRec_
 {
     FHClass    clazz;
@@ -185,10 +179,8 @@
     union {
         HANDLE      handle;
         SOCKET      socket;
-        SocketPair  pair;
     } u;
 
-    HANDLE    event;
     int       mask;
 
     char  name[32];
@@ -197,10 +189,8 @@
 
 #define  fh_handle  u.handle
 #define  fh_socket  u.socket
-#define  fh_pair    u.pair
 
-#define  WIN32_FH_BASE    100
-
+#define  WIN32_FH_BASE    2048
 #define  WIN32_MAX_FHS    128
 
 static adb_mutex_t   _win32_lock;
@@ -250,17 +240,10 @@
 
     adb_mutex_lock( &_win32_lock );
 
-    // Search entire array, starting from _win32_fh_next.
-    for (int nn = 0; nn < WIN32_MAX_FHS; nn++) {
-        // Keep incrementing _win32_fh_next to avoid giving out an index that
-        // was recently closed, to try to avoid use-after-free.
-        const int index = _win32_fh_next++;
-        // Handle wrap-around of _win32_fh_next.
-        if (_win32_fh_next == WIN32_MAX_FHS) {
-            _win32_fh_next = 0;
-        }
-        if (_win32_fhs[index].clazz == NULL) {
-            f = &_win32_fhs[index];
+    for (int i = _win32_fh_next; i < WIN32_MAX_FHS; ++i) {
+        if (_win32_fhs[i].clazz == NULL) {
+            f = &_win32_fhs[i];
+            _win32_fh_next = i + 1;
             goto Exit;
         }
     }
@@ -285,6 +268,12 @@
     // Use lock so that closing only happens once and so that _fh_alloc can't
     // allocate a FH that we're in the middle of closing.
     adb_mutex_lock(&_win32_lock);
+
+    int offset = f - _win32_fhs;
+    if (_win32_fh_next > offset) {
+        _win32_fh_next = offset;
+    }
+
     if (f->used) {
         f->clazz->_fh_close( f );
         f->name[0] = '\0';
@@ -672,19 +661,56 @@
     }
 }
 
-static void _fh_socket_init( FH  f ) {
-    f->fh_socket = INVALID_SOCKET;
-    f->event     = WSACreateEvent();
-    if (f->event == WSA_INVALID_EVENT) {
-        D("WSACreateEvent failed: %s",
-          android::base::SystemErrorCodeToString(WSAGetLastError()).c_str());
-
-        // _event_socket_start assumes that this field is INVALID_HANDLE_VALUE
-        // on failure, instead of NULL which is what Windows really returns on
-        // error. It might be better to change all the other code to look for
-        // NULL, but that is a much riskier change.
-        f->event = INVALID_HANDLE_VALUE;
+extern int adb_poll(adb_pollfd* fds, size_t nfds, int timeout) {
+    // WSAPoll doesn't handle invalid/non-socket handles, so we need to handle them ourselves.
+    int skipped = 0;
+    std::vector<WSAPOLLFD> sockets;
+    std::vector<adb_pollfd*> original;
+    for (size_t i = 0; i < nfds; ++i) {
+        FH fh = _fh_from_int(fds[i].fd, __func__);
+        if (!fh || !fh->used || fh->clazz != &_fh_socket_class) {
+            D("adb_poll received bad FD %d", fds[i].fd);
+            fds[i].revents = POLLNVAL;
+            ++skipped;
+        } else {
+            WSAPOLLFD wsapollfd = {
+                .fd = fh->u.socket,
+                .events = static_cast<short>(fds[i].events)
+            };
+            sockets.push_back(wsapollfd);
+            original.push_back(&fds[i]);
+        }
     }
+
+    if (sockets.empty()) {
+        return skipped;
+    }
+
+    int result = WSAPoll(sockets.data(), sockets.size(), timeout);
+    if (result == SOCKET_ERROR) {
+        _socket_set_errno(WSAGetLastError());
+        return -1;
+    }
+
+    // Map the results back onto the original set.
+    for (size_t i = 0; i < sockets.size(); ++i) {
+        original[i]->revents = sockets[i].revents;
+    }
+
+    // WSAPoll appears to return the number of unique FDs with avaiable events, instead of how many
+    // of the pollfd elements have a non-zero revents field, which is what it and poll are specified
+    // to do. Ignore its result and calculate the proper return value.
+    result = 0;
+    for (size_t i = 0; i < nfds; ++i) {
+        if (fds[i].revents != 0) {
+            ++result;
+        }
+    }
+    return result;
+}
+
+static void _fh_socket_init(FH f) {
+    f->fh_socket = INVALID_SOCKET;
     f->mask      = 0;
 }
 
@@ -700,18 +726,12 @@
 #endif
         }
         if (closesocket(f->fh_socket) == SOCKET_ERROR) {
-            D("closesocket failed: %s",
-              android::base::SystemErrorCodeToString(WSAGetLastError()).c_str());
+            // Don't set errno here, since adb_close will ignore it.
+            const DWORD err = WSAGetLastError();
+            D("closesocket failed: %s", android::base::SystemErrorCodeToString(err).c_str());
         }
         f->fh_socket = INVALID_SOCKET;
     }
-    if (f->event != NULL) {
-        if (!CloseHandle(f->event)) {
-            D("CloseHandle failed: %s",
-              android::base::SystemErrorCodeToString(GetLastError()).c_str());
-        }
-        f->event = NULL;
-    }
     f->mask = 0;
     return 0;
 }
@@ -820,16 +840,15 @@
 
 int network_loopback_client(int port, int type, std::string* error) {
     struct sockaddr_in addr;
-    SOCKET  s;
+    SOCKET s;
 
-    unique_fh  f(_fh_alloc(&_fh_socket_class));
+    unique_fh f(_fh_alloc(&_fh_socket_class));
     if (!f) {
         *error = strerror(errno);
         return -1;
     }
 
-    if (!_winsock_init)
-        _init_winsock();
+    if (!_winsock_init) _init_winsock();
 
     memset(&addr, 0, sizeof(addr));
     addr.sin_family = AF_INET;
@@ -837,30 +856,32 @@
     addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
 
     s = socket(AF_INET, type, GetSocketProtocolFromSocketType(type));
-    if(s == INVALID_SOCKET) {
+    if (s == INVALID_SOCKET) {
+        const DWORD err = WSAGetLastError();
         *error = android::base::StringPrintf("cannot create socket: %s",
-                android::base::SystemErrorCodeToString(WSAGetLastError()).c_str());
+                                             android::base::SystemErrorCodeToString(err).c_str());
         D("%s", error->c_str());
+        _socket_set_errno(err);
         return -1;
     }
     f->fh_socket = s;
 
-    if(connect(s, (struct sockaddr *) &addr, sizeof(addr)) == SOCKET_ERROR) {
+    if (connect(s, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) {
         // Save err just in case inet_ntoa() or ntohs() changes the last error.
         const DWORD err = WSAGetLastError();
         *error = android::base::StringPrintf("cannot connect to %s:%u: %s",
-                inet_ntoa(addr.sin_addr), ntohs(addr.sin_port),
-                android::base::SystemErrorCodeToString(err).c_str());
-        D("could not connect to %s:%d: %s",
-          type != SOCK_STREAM ? "udp" : "tcp", port, error->c_str());
+                                             inet_ntoa(addr.sin_addr), ntohs(addr.sin_port),
+                                             android::base::SystemErrorCodeToString(err).c_str());
+        D("could not connect to %s:%d: %s", type != SOCK_STREAM ? "udp" : "tcp", port,
+          error->c_str());
+        _socket_set_errno(err);
         return -1;
     }
 
     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", port, type != SOCK_STREAM ? "udp" : "tcp",
-       fd );
+    snprintf(f->name, sizeof(f->name), "%d(lo-client:%s%d)", fd, type != SOCK_STREAM ? "udp:" : "",
+             port);
+    D("port %d type %s => fd %d", port, type != SOCK_STREAM ? "udp" : "tcp", fd);
     f.release();
     return fd;
 }
@@ -868,20 +889,18 @@
 #define LISTEN_BACKLOG 4
 
 // interface_address is INADDR_LOOPBACK or INADDR_ANY.
-static int _network_server(int port, int type, u_long interface_address,
-                           std::string* error) {
+static int _network_server(int port, int type, u_long interface_address, std::string* error) {
     struct sockaddr_in addr;
-    SOCKET  s;
-    int  n;
+    SOCKET s;
+    int n;
 
-    unique_fh   f(_fh_alloc(&_fh_socket_class));
+    unique_fh f(_fh_alloc(&_fh_socket_class));
     if (!f) {
         *error = strerror(errno);
         return -1;
     }
 
-    if (!_winsock_init)
-        _init_winsock();
+    if (!_winsock_init) _init_winsock();
 
     memset(&addr, 0, sizeof(addr));
     addr.sin_family = AF_INET;
@@ -892,9 +911,11 @@
     // IPv4 and IPv6.
     s = socket(AF_INET, type, GetSocketProtocolFromSocketType(type));
     if (s == INVALID_SOCKET) {
+        const DWORD err = WSAGetLastError();
         *error = android::base::StringPrintf("cannot create socket: %s",
-                android::base::SystemErrorCodeToString(WSAGetLastError()).c_str());
+                                             android::base::SystemErrorCodeToString(err).c_str());
         D("%s", error->c_str());
+        _socket_set_errno(err);
         return -1;
     }
 
@@ -903,40 +924,41 @@
     // Note: SO_REUSEADDR on Windows allows multiple processes to bind to the
     // same port, so instead use SO_EXCLUSIVEADDRUSE.
     n = 1;
-    if (setsockopt(s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (const char*)&n,
-                   sizeof(n)) == SOCKET_ERROR) {
-        *error = android::base::StringPrintf(
-                "cannot set socket option SO_EXCLUSIVEADDRUSE: %s",
-                android::base::SystemErrorCodeToString(WSAGetLastError()).c_str());
+    if (setsockopt(s, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (const char*)&n, sizeof(n)) == SOCKET_ERROR) {
+        const DWORD err = WSAGetLastError();
+        *error = android::base::StringPrintf("cannot set socket option SO_EXCLUSIVEADDRUSE: %s",
+                                             android::base::SystemErrorCodeToString(err).c_str());
         D("%s", error->c_str());
+        _socket_set_errno(err);
         return -1;
     }
 
-    if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) == SOCKET_ERROR) {
+    if (bind(s, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR) {
         // Save err just in case inet_ntoa() or ntohs() changes the last error.
         const DWORD err = WSAGetLastError();
-        *error = android::base::StringPrintf("cannot bind to %s:%u: %s",
-                inet_ntoa(addr.sin_addr), ntohs(addr.sin_port),
-                android::base::SystemErrorCodeToString(err).c_str());
-        D("could not bind to %s:%d: %s",
-          type != SOCK_STREAM ? "udp" : "tcp", port, error->c_str());
+        *error = android::base::StringPrintf("cannot bind to %s:%u: %s", inet_ntoa(addr.sin_addr),
+                                             ntohs(addr.sin_port),
+                                             android::base::SystemErrorCodeToString(err).c_str());
+        D("could not bind to %s:%d: %s", type != SOCK_STREAM ? "udp" : "tcp", port, error->c_str());
+        _socket_set_errno(err);
         return -1;
     }
     if (type == SOCK_STREAM) {
         if (listen(s, LISTEN_BACKLOG) == SOCKET_ERROR) {
-            *error = android::base::StringPrintf("cannot listen on socket: %s",
-                    android::base::SystemErrorCodeToString(WSAGetLastError()).c_str());
-            D("could not listen on %s:%d: %s",
-              type != SOCK_STREAM ? "udp" : "tcp", port, error->c_str());
+            const DWORD err = WSAGetLastError();
+            *error = android::base::StringPrintf(
+                "cannot listen on socket: %s", android::base::SystemErrorCodeToString(err).c_str());
+            D("could not listen on %s:%d: %s", type != SOCK_STREAM ? "udp" : "tcp", port,
+              error->c_str());
+            _socket_set_errno(err);
             return -1;
         }
     }
     const int fd = _fh_to_int(f.get());
-    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", port, type != SOCK_STREAM ? "udp" : "tcp",
-       fd );
+    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", port, type != SOCK_STREAM ? "udp" : "tcp", fd);
     f.release();
     return fd;
 }
@@ -970,54 +992,57 @@
     struct addrinfo* addrinfo_ptr = nullptr;
 
 #if (NTDDI_VERSION >= NTDDI_WINXPSP2) || (_WIN32_WINNT >= _WIN32_WINNT_WS03)
-    // TODO: When the Android SDK tools increases the Windows system
-    // requirements >= WinXP SP2, switch to android::base::UTF8ToWide() + GetAddrInfoW().
+// TODO: When the Android SDK tools increases the Windows system
+// requirements >= WinXP SP2, switch to android::base::UTF8ToWide() + GetAddrInfoW().
 #else
-    // Otherwise, keep using getaddrinfo(), or do runtime API detection
-    // with GetProcAddress("GetAddrInfoW").
+// Otherwise, keep using getaddrinfo(), or do runtime API detection
+// with GetProcAddress("GetAddrInfoW").
 #endif
     if (getaddrinfo(host.c_str(), port_str, &hints, &addrinfo_ptr) != 0) {
-        *error = android::base::StringPrintf(
-                "cannot resolve host '%s' and port %s: %s", host.c_str(),
-                port_str, android::base::SystemErrorCodeToString(WSAGetLastError()).c_str());
+        const DWORD err = WSAGetLastError();
+        *error = android::base::StringPrintf("cannot resolve host '%s' and port %s: %s",
+                                             host.c_str(), port_str,
+                                             android::base::SystemErrorCodeToString(err).c_str());
+
         D("%s", error->c_str());
+        _socket_set_errno(err);
         return -1;
     }
-    std::unique_ptr<struct addrinfo, decltype(freeaddrinfo)*>
-        addrinfo(addrinfo_ptr, freeaddrinfo);
+    std::unique_ptr<struct addrinfo, decltype(freeaddrinfo)*> addrinfo(addrinfo_ptr, freeaddrinfo);
     addrinfo_ptr = nullptr;
 
     // TODO: Try all the addresses if there's more than one? This just uses
     // the first. Or, could call WSAConnectByName() (Windows Vista and newer)
     // which tries all addresses, takes a timeout and more.
-    SOCKET s = socket(addrinfo->ai_family, addrinfo->ai_socktype,
-                      addrinfo->ai_protocol);
-    if(s == INVALID_SOCKET) {
+    SOCKET s = socket(addrinfo->ai_family, addrinfo->ai_socktype, addrinfo->ai_protocol);
+    if (s == INVALID_SOCKET) {
+        const DWORD err = WSAGetLastError();
         *error = android::base::StringPrintf("cannot create socket: %s",
-                android::base::SystemErrorCodeToString(WSAGetLastError()).c_str());
+                                             android::base::SystemErrorCodeToString(err).c_str());
         D("%s", error->c_str());
+        _socket_set_errno(err);
         return -1;
     }
     f->fh_socket = s;
 
     // TODO: Implement timeouts for Windows. Seems like the default in theory
     // (according to http://serverfault.com/a/671453) and in practice is 21 sec.
-    if(connect(s, addrinfo->ai_addr, addrinfo->ai_addrlen) == SOCKET_ERROR) {
+    if (connect(s, addrinfo->ai_addr, addrinfo->ai_addrlen) == SOCKET_ERROR) {
         // TODO: Use WSAAddressToString or inet_ntop on address.
-        *error = android::base::StringPrintf("cannot connect to %s:%s: %s",
-                host.c_str(), port_str,
-                android::base::SystemErrorCodeToString(WSAGetLastError()).c_str());
-        D("could not connect to %s:%s:%s: %s",
-          type != SOCK_STREAM ? "udp" : "tcp", host.c_str(), port_str,
-          error->c_str());
+        const DWORD err = WSAGetLastError();
+        *error = android::base::StringPrintf("cannot connect to %s:%s: %s", host.c_str(), port_str,
+                                             android::base::SystemErrorCodeToString(err).c_str());
+        D("could not connect to %s:%s:%s: %s", type != SOCK_STREAM ? "udp" : "tcp", host.c_str(),
+          port_str, error->c_str());
+        _socket_set_errno(err);
         return -1;
     }
 
     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", host.c_str(), port,
-       type != SOCK_STREAM ? "udp" : "tcp", fd );
+    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", host.c_str(), port, type != SOCK_STREAM ? "udp" : "tcp",
+      fd);
     f.release();
     return fd;
 }
@@ -1083,6 +1108,25 @@
     return result;
 }
 
+int adb_getsockname(int fd, struct sockaddr* sockaddr, socklen_t* optlen) {
+    FH fh = _fh_from_int(fd, __func__);
+
+    if (!fh || fh->clazz != &_fh_socket_class) {
+        D("adb_getsockname: invalid fd %d", fd);
+        errno = EBADF;
+        return -1;
+    }
+
+    int result = getsockname(fh->fh_socket, sockaddr, optlen);
+    if (result == SOCKET_ERROR) {
+        const DWORD err = WSAGetLastError();
+        D("adb_getsockname: setsockopt on fd %d failed: %s\n", fd,
+          android::base::SystemErrorCodeToString(err).c_str());
+        _socket_set_errno(err);
+        result = -1;
+    }
+    return result;
+}
 
 int  adb_shutdown(int  fd)
 {
@@ -1105,1352 +1149,85 @@
     return 0;
 }
 
-/**************************************************************************/
-/**************************************************************************/
-/*****                                                                *****/
-/*****    emulated socketpairs                                       *****/
-/*****                                                                *****/
-/**************************************************************************/
-/**************************************************************************/
+// Emulate socketpair(2) by binding and connecting to a socket.
+int adb_socketpair(int sv[2]) {
+    int server = -1;
+    int client = -1;
+    int accepted = -1;
+    sockaddr_storage addr_storage;
+    socklen_t addr_len = sizeof(addr_storage);
+    sockaddr_in* addr = nullptr;
+    std::string error;
 
-/* we implement socketpairs directly in use space for the following reasons:
- *   - it avoids copying data from/to the Nt kernel
- *   - it allows us to implement fdevent hooks easily and cheaply, something
- *     that is not possible with standard Win32 pipes !!
- *
- * basically, we use two circular buffers, each one corresponding to a given
- * direction.
- *
- * each buffer is implemented as two regions:
- *
- *   region A which is (a_start,a_end)
- *   region B which is (0, b_end)  with b_end <= a_start
- *
- * an empty buffer has:  a_start = a_end = b_end = 0
- *
- * a_start is the pointer where we start reading data
- * a_end is the pointer where we start writing data, unless it is BUFFER_SIZE,
- * then you start writing at b_end
- *
- * the buffer is full when  b_end == a_start && a_end == BUFFER_SIZE
- *
- * there is room when b_end < a_start || a_end < BUFER_SIZE
- *
- * when reading, a_start is incremented, it a_start meets a_end, then
- * we do:  a_start = 0, a_end = b_end, b_end = 0, and keep going on..
- */
-
-#define  BIP_BUFFER_SIZE   4096
-
-#if 0
-#include <stdio.h>
-#  define  BIPD(x)      D x
-#  define  BIPDUMP   bip_dump_hex
-
-static void  bip_dump_hex( const unsigned char*  ptr, size_t  len )
-{
-    int  nn, len2 = len;
-
-    if (len2 > 8) len2 = 8;
-
-    for (nn = 0; nn < len2; nn++)
-        printf("%02x", ptr[nn]);
-    printf("  ");
-
-    for (nn = 0; nn < len2; nn++) {
-        int  c = ptr[nn];
-        if (c < 32 || c > 127)
-            c = '.';
-        printf("%c", c);
-    }
-    printf("\n");
-    fflush(stdout);
-}
-
-#else
-#  define  BIPD(x)        do {} while (0)
-#  define  BIPDUMP(p,l)   BIPD(p)
-#endif
-
-typedef struct BipBufferRec_
-{
-    int                a_start;
-    int                a_end;
-    int                b_end;
-    int                fdin;
-    int                fdout;
-    int                closed;
-    int                can_write;  /* boolean */
-    HANDLE             evt_write;  /* event signaled when one can write to a buffer  */
-    int                can_read;   /* boolean */
-    HANDLE             evt_read;   /* event signaled when one can read from a buffer */
-    CRITICAL_SECTION  lock;
-    unsigned char      buff[ BIP_BUFFER_SIZE ];
-
-} BipBufferRec, *BipBuffer;
-
-static void
-bip_buffer_init( BipBuffer  buffer )
-{
-    D( "bit_buffer_init %p", buffer );
-    buffer->a_start   = 0;
-    buffer->a_end     = 0;
-    buffer->b_end     = 0;
-    buffer->can_write = 1;
-    buffer->can_read  = 0;
-    buffer->fdin      = 0;
-    buffer->fdout     = 0;
-    buffer->closed    = 0;
-    buffer->evt_write = CreateEvent( NULL, TRUE, TRUE, NULL );
-    buffer->evt_read  = CreateEvent( NULL, TRUE, FALSE, NULL );
-    InitializeCriticalSection( &buffer->lock );
-}
-
-static void
-bip_buffer_close( BipBuffer  bip )
-{
-    bip->closed = 1;
-
-    if (!bip->can_read) {
-        SetEvent( bip->evt_read );
-    }
-    if (!bip->can_write) {
-        SetEvent( bip->evt_write );
-    }
-}
-
-static void
-bip_buffer_done( BipBuffer  bip )
-{
-    BIPD(( "bip_buffer_done: %d->%d", bip->fdin, bip->fdout ));
-    CloseHandle( bip->evt_read );
-    CloseHandle( bip->evt_write );
-    DeleteCriticalSection( &bip->lock );
-}
-
-static int
-bip_buffer_write( BipBuffer  bip, const void* src, int  len )
-{
-    int  avail, count = 0;
-
-    if (len <= 0)
-        return 0;
-
-    BIPD(( "bip_buffer_write: enter %d->%d len %d", bip->fdin, bip->fdout, len ));
-    BIPDUMP( src, len );
-
-    if (bip->closed) {
-        errno = EPIPE;
-        return -1;
+    server = network_loopback_server(0, SOCK_STREAM, &error);
+    if (server < 0) {
+        D("adb_socketpair: failed to create server: %s", error.c_str());
+        goto fail;
     }
 
-    EnterCriticalSection( &bip->lock );
-
-    while (!bip->can_write) {
-        int  ret;
-        LeaveCriticalSection( &bip->lock );
-
-        if (bip->closed) {
-            errno = EPIPE;
-            return -1;
-        }
-        /* 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", bip->fdin, bip->fdout, ret, GetLastError() );
-            return 0;
-        }
-        if (bip->closed) {
-            errno = EPIPE;
-            return -1;
-        }
-        EnterCriticalSection( &bip->lock );
+    if (adb_getsockname(server, reinterpret_cast<sockaddr*>(&addr_storage), &addr_len) < 0) {
+        D("adb_socketpair: adb_getsockname failed: %s", strerror(errno));
+        goto fail;
     }
 
-    BIPD(( "bip_buffer_write: exec %d->%d len %d", bip->fdin, bip->fdout, len ));
-
-    avail = BIP_BUFFER_SIZE - bip->a_end;
-    if (avail > 0)
-    {
-        /* we can append to region A */
-        if (avail > len)
-            avail = len;
-
-        memcpy( bip->buff + bip->a_end, src, avail );
-        src   = (const char *)src + avail;
-        count += avail;
-        len   -= avail;
-
-        bip->a_end += avail;
-        if (bip->a_end == BIP_BUFFER_SIZE && bip->a_start == 0) {
-            bip->can_write = 0;
-            ResetEvent( bip->evt_write );
-            goto Exit;
-        }
+    if (addr_storage.ss_family != AF_INET) {
+        D("adb_socketpair: unknown address family received: %d", addr_storage.ss_family);
+        errno = ECONNABORTED;
+        goto fail;
     }
 
-    if (len == 0)
-        goto Exit;
-
-    avail = bip->a_start - bip->b_end;
-    assert( avail > 0 );  /* since can_write is TRUE */
-
-    if (avail > len)
-        avail = len;
-
-    memcpy( bip->buff + bip->b_end, src, avail );
-    count += avail;
-    bip->b_end += avail;
-
-    if (bip->b_end == bip->a_start) {
-        bip->can_write = 0;
-        ResetEvent( bip->evt_write );
+    addr = reinterpret_cast<sockaddr_in*>(&addr_storage);
+    D("adb_socketpair: bound on port %d", ntohs(addr->sin_port));
+    client = network_loopback_client(ntohs(addr->sin_port), SOCK_STREAM, &error);
+    if (client < 0) {
+        D("adb_socketpair: failed to connect client: %s", error.c_str());
+        goto fail;
     }
 
-Exit:
-    assert( count > 0 );
-
-    if ( !bip->can_read ) {
-        bip->can_read = 1;
-        SetEvent( bip->evt_read );
+    accepted = adb_socket_accept(server, nullptr, nullptr);
+    if (accepted < 0) {
+        D("adb_socketpair: failed to accept: %s", strerror(errno));
+        goto fail;
     }
-
-    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 );
-
-    return count;
- }
-
-static int
-bip_buffer_read( BipBuffer  bip, void*  dst, int  len )
-{
-    int  avail, count = 0;
-
-    if (len <= 0)
-        return 0;
-
-    BIPD(( "bip_buffer_read: enter %d->%d len %d", bip->fdin, bip->fdout, len ));
-
-    EnterCriticalSection( &bip->lock );
-    while ( !bip->can_read )
-    {
-#if 0
-        LeaveCriticalSection( &bip->lock );
-        errno = EAGAIN;
-        return -1;
-#else
-        int  ret;
-        LeaveCriticalSection( &bip->lock );
-
-        if (bip->closed) {
-            errno = EPIPE;
-            return -1;
-        }
-
-        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", bip->fdin, bip->fdout, ret, GetLastError());
-            return 0;
-        }
-        if (bip->closed) {
-            errno = EPIPE;
-            return -1;
-        }
-        EnterCriticalSection( &bip->lock );
-#endif
-    }
-
-    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 */
-
-    if (avail > len)
-        avail = len;
-
-    memcpy( dst, bip->buff + bip->a_start, avail );
-    dst   = (char *)dst + avail;
-    count += avail;
-    len   -= avail;
-
-    bip->a_start += avail;
-    if (bip->a_start < bip->a_end)
-        goto Exit;
-
-    bip->a_start = 0;
-    bip->a_end   = bip->b_end;
-    bip->b_end   = 0;
-
-    avail = bip->a_end;
-    if (avail > 0) {
-        if (avail > len)
-            avail = len;
-        memcpy( dst, bip->buff, avail );
-        count += avail;
-        bip->a_start += avail;
-
-        if ( bip->a_start < bip->a_end )
-            goto Exit;
-
-        bip->a_start = bip->a_end = 0;
-    }
-
-    bip->can_read = 0;
-    ResetEvent( bip->evt_read );
-
-Exit:
-    assert( count > 0 );
-
-    if (!bip->can_write ) {
-        bip->can_write = 1;
-        SetEvent( bip->evt_write );
-    }
-
-    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",
-            bip->fdin, bip->fdout, count, bip->a_start, bip->a_end, bip->b_end, bip->can_write, bip->can_read ));
-    LeaveCriticalSection( &bip->lock );
-
-    return count;
-}
-
-typedef struct SocketPairRec_
-{
-    BipBufferRec  a2b_bip;
-    BipBufferRec  b2a_bip;
-    FH            a_fd;
-    int           used;
-
-} SocketPairRec;
-
-void _fh_socketpair_init( FH  f )
-{
-    f->fh_pair = NULL;
-}
-
-static int
-_fh_socketpair_close( FH  f )
-{
-    if ( f->fh_pair ) {
-        SocketPair  pair = f->fh_pair;
-
-        if ( f == pair->a_fd ) {
-            pair->a_fd = NULL;
-        }
-
-        bip_buffer_close( &pair->b2a_bip );
-        bip_buffer_close( &pair->a2b_bip );
-
-        if ( --pair->used == 0 ) {
-            bip_buffer_done( &pair->b2a_bip );
-            bip_buffer_done( &pair->a2b_bip );
-            free( pair );
-        }
-        f->fh_pair = NULL;
-    }
+    adb_close(server);
+    sv[0] = client;
+    sv[1] = accepted;
     return 0;
-}
 
-static int
-_fh_socketpair_lseek( FH  f, int pos, int  origin )
-{
-    errno = ESPIPE;
+fail:
+    if (server >= 0) {
+        adb_close(server);
+    }
+    if (client >= 0) {
+        adb_close(client);
+    }
+    if (accepted >= 0) {
+        adb_close(accepted);
+    }
     return -1;
 }
 
-static int
-_fh_socketpair_read( FH  f, void* buf, int  len )
-{
-    SocketPair  pair = f->fh_pair;
-    BipBuffer   bip;
+bool set_file_block_mode(int fd, bool block) {
+    FH fh = _fh_from_int(fd, __func__);
 
-    if (!pair)
-        return -1;
-
-    if ( f == pair->a_fd )
-        bip = &pair->b2a_bip;
-    else
-        bip = &pair->a2b_bip;
-
-    return bip_buffer_read( bip, buf, len );
-}
-
-static int
-_fh_socketpair_write( FH  f, const void*  buf, int  len )
-{
-    SocketPair  pair = f->fh_pair;
-    BipBuffer   bip;
-
-    if (!pair)
-        return -1;
-
-    if ( f == pair->a_fd )
-        bip = &pair->a2b_bip;
-    else
-        bip = &pair->b2a_bip;
-
-    return bip_buffer_write( bip, buf, len );
-}
-
-
-static void  _fh_socketpair_hook( FH  f, int  event, EventHook  hook );  /* forward */
-
-static const FHClassRec  _fh_socketpair_class =
-{
-    _fh_socketpair_init,
-    _fh_socketpair_close,
-    _fh_socketpair_lseek,
-    _fh_socketpair_read,
-    _fh_socketpair_write,
-    _fh_socketpair_hook
-};
-
-
-int  adb_socketpair(int sv[2]) {
-    SocketPair pair;
-
-    unique_fh fa(_fh_alloc(&_fh_socketpair_class));
-    if (!fa) {
-        return -1;
-    }
-    unique_fh fb(_fh_alloc(&_fh_socketpair_class));
-    if (!fb) {
-        return -1;
+    if (!fh || !fh->used) {
+        errno = EBADF;
+        return false;
     }
 
-    pair = reinterpret_cast<SocketPair>(malloc(sizeof(*pair)));
-    if (pair == NULL) {
-        D("adb_socketpair: not enough memory to allocate pipes" );
-        return -1;
-    }
-
-    bip_buffer_init( &pair->a2b_bip );
-    bip_buffer_init( &pair->b2a_bip );
-
-    fa->fh_pair = pair;
-    fb->fh_pair = pair;
-    pair->used  = 2;
-    pair->a_fd  = fa.get();
-
-    sv[0] = _fh_to_int(fa.get());
-    sv[1] = _fh_to_int(fb.get());
-
-    pair->a2b_bip.fdin  = sv[0];
-    pair->a2b_bip.fdout = sv[1];
-    pair->b2a_bip.fdin  = sv[1];
-    pair->b2a_bip.fdout = sv[0];
-
-    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)", sv[0], sv[1] );
-    fa.release();
-    fb.release();
-    return 0;
-}
-
-/**************************************************************************/
-/**************************************************************************/
-/*****                                                                *****/
-/*****    fdevents emulation                                          *****/
-/*****                                                                *****/
-/*****   this is a very simple implementation, we rely on the fact    *****/
-/*****   that ADB doesn't use FDE_ERROR.                              *****/
-/*****                                                                *****/
-/**************************************************************************/
-/**************************************************************************/
-
-#define FATAL(fmt, ...) fatal("%s: " fmt, __FUNCTION__, ##__VA_ARGS__)
-
-#if DEBUG
-static void dump_fde(fdevent *fde, const char *info)
-{
-    fprintf(stderr,"FDE #%03d %c%c%c %s\n", fde->fd,
-            fde->state & FDE_READ ? 'R' : ' ',
-            fde->state & FDE_WRITE ? 'W' : ' ',
-            fde->state & FDE_ERROR ? 'E' : ' ',
-            info);
-}
-#else
-#define dump_fde(fde, info) do { } while(0)
-#endif
-
-#define FDE_EVENTMASK  0x00ff
-#define FDE_STATEMASK  0xff00
-
-#define FDE_ACTIVE     0x0100
-#define FDE_PENDING    0x0200
-#define FDE_CREATED    0x0400
-
-static void fdevent_plist_enqueue(fdevent *node);
-static void fdevent_plist_remove(fdevent *node);
-static fdevent *fdevent_plist_dequeue(void);
-
-static fdevent list_pending = {
-    .next = &list_pending,
-    .prev = &list_pending,
-};
-
-static fdevent **fd_table = 0;
-static int       fd_table_max = 0;
-
-typedef struct EventLooperRec_*  EventLooper;
-
-typedef struct EventHookRec_
-{
-    EventHook    next;
-    FH           fh;
-    HANDLE       h;
-    int          wanted;   /* wanted event flags */
-    int          ready;    /* ready event flags  */
-    void*        aux;
-    void        (*prepare)( EventHook  hook );
-    int         (*start)  ( EventHook  hook );
-    void        (*stop)   ( EventHook  hook );
-    int         (*check)  ( EventHook  hook );
-    int         (*peek)   ( EventHook  hook );
-} EventHookRec;
-
-static EventHook  _free_hooks;
-
-static EventHook
-event_hook_alloc(FH fh) {
-    EventHook hook = _free_hooks;
-    if (hook != NULL) {
-        _free_hooks = hook->next;
+    if (fh->clazz == &_fh_socket_class) {
+        u_long x = !block;
+        if (ioctlsocket(fh->u.socket, FIONBIO, &x) != 0) {
+            _socket_set_errno(WSAGetLastError());
+            return false;
+        }
+        return true;
     } else {
-        hook = reinterpret_cast<EventHook>(malloc(sizeof(*hook)));
-        if (hook == NULL)
-            fatal( "could not allocate event hook\n" );
-    }
-    hook->next   = NULL;
-    hook->fh     = fh;
-    hook->wanted = 0;
-    hook->ready  = 0;
-    hook->h      = INVALID_HANDLE_VALUE;
-    hook->aux    = NULL;
-
-    hook->prepare = NULL;
-    hook->start   = NULL;
-    hook->stop    = NULL;
-    hook->check   = NULL;
-    hook->peek    = NULL;
-
-    return hook;
-}
-
-static void
-event_hook_free( EventHook  hook )
-{
-    hook->fh     = NULL;
-    hook->wanted = 0;
-    hook->ready  = 0;
-    hook->next   = _free_hooks;
-    _free_hooks  = hook;
-}
-
-
-static void
-event_hook_signal( EventHook  hook )
-{
-    FH        f   = hook->fh;
-    int       fd  = _fh_to_int(f);
-    fdevent*  fde = fd_table[ fd - WIN32_FH_BASE ];
-
-    if (fde != NULL && fde->fd == fd) {
-        if ((fde->state & FDE_PENDING) == 0) {
-            fde->state |= FDE_PENDING;
-            fdevent_plist_enqueue( fde );
-        }
-        fde->events |= hook->wanted;
+        errno = ENOTSOCK;
+        return false;
     }
 }
 
-
-#define  MAX_LOOPER_HANDLES  WIN32_MAX_FHS
-
-typedef struct EventLooperRec_
-{
-    EventHook    hooks;
-    HANDLE       htab[ MAX_LOOPER_HANDLES ];
-    int          htab_count;
-
-} EventLooperRec;
-
-static EventHook*
-event_looper_find_p( EventLooper  looper, FH  fh )
-{
-    EventHook  *pnode = &looper->hooks;
-    EventHook   node  = *pnode;
-    for (;;) {
-        if ( node == NULL || node->fh == fh )
-            break;
-        pnode = &node->next;
-        node  = *pnode;
-    }
-    return  pnode;
-}
-
-static void
-event_looper_hook( EventLooper  looper, int  fd, int  events )
-{
-    FH          f = _fh_from_int(fd, __func__);
-    EventHook  *pnode;
-    EventHook   node;
-
-    if (f == NULL)  /* invalid arg */ {
-        D("event_looper_hook: invalid fd=%d", fd);
-        return;
-    }
-
-    pnode = event_looper_find_p( looper, f );
-    node  = *pnode;
-    if ( node == NULL ) {
-        node       = event_hook_alloc( f );
-        node->next = *pnode;
-        *pnode     = node;
-    }
-
-    if ( (node->wanted & events) != events ) {
-        /* this should update start/stop/check/peek */
-        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)",
-           events, fd, node->wanted);
-    }
-}
-
-static void
-event_looper_unhook( EventLooper  looper, int  fd, int  events )
-{
-    FH          fh    = _fh_from_int(fd, __func__);
-    EventHook  *pnode = event_looper_find_p( looper, fh );
-    EventHook   node  = *pnode;
-
-    if (node != NULL) {
-        int  events2 = events & node->wanted;
-        if ( events2 == 0 ) {
-            D( "event_looper_unhook: events %x not registered for fd %d", events, fd );
-            return;
-        }
-        node->wanted &= ~events2;
-        if (!node->wanted) {
-            *pnode = node->next;
-            event_hook_free( node );
-        }
-    }
-}
-
-/*
- * A fixer for WaitForMultipleObjects on condition that there are more than 64
- * handles to wait on.
- *
- * In cetain cases DDMS may establish more than 64 connections with ADB. For
- * instance, this may happen if there are more than 64 processes running on a
- * device, or there are multiple devices connected (including the emulator) with
- * the combined number of running processes greater than 64. In this case using
- * WaitForMultipleObjects to wait on connection events simply wouldn't cut,
- * because of the API limitations (64 handles max). So, we need to provide a way
- * to scale WaitForMultipleObjects to accept an arbitrary number of handles. The
- * easiest (and "Microsoft recommended") way to do that would be dividing the
- * handle array into chunks with the chunk size less than 64, and fire up as many
- * waiting threads as there are chunks. Then each thread would wait on a chunk of
- * handles, and will report back to the caller which handle has been set.
- * Here is the implementation of that algorithm.
- */
-
-/* Number of handles to wait on in each wating thread. */
-#define WAIT_ALL_CHUNK_SIZE 63
-
-/* Descriptor for a wating thread */
-typedef struct WaitForAllParam {
-    /* A handle to an event to signal when waiting is over. This handle is shared
-     * accross all the waiting threads, so each waiting thread knows when any
-     * other thread has exited, so it can exit too. */
-    HANDLE          main_event;
-    /* Upon exit from a waiting thread contains the index of the handle that has
-     * been signaled. The index is an absolute index of the signaled handle in
-     * the original array. This pointer is shared accross all the waiting threads
-     * and it's not guaranteed (due to a race condition) that when all the
-     * waiting threads exit, the value contained here would indicate the first
-     * handle that was signaled. This is fine, because the caller cares only
-     * about any handle being signaled. It doesn't care about the order, nor
-     * about the whole list of handles that were signaled. */
-    LONG volatile   *signaled_index;
-    /* Array of handles to wait on in a waiting thread. */
-    HANDLE*         handles;
-    /* Number of handles in 'handles' array to wait on. */
-    int             handles_count;
-    /* Index inside the main array of the first handle in the 'handles' array. */
-    int             first_handle_index;
-    /* Waiting thread handle. */
-    HANDLE          thread;
-} WaitForAllParam;
-
-/* Waiting thread routine. */
-static unsigned __stdcall
-_in_waiter_thread(void*  arg)
-{
-    HANDLE wait_on[WAIT_ALL_CHUNK_SIZE + 1];
-    int res;
-    WaitForAllParam* const param = (WaitForAllParam*)arg;
-
-    /* We have to wait on the main_event in order to be notified when any of the
-     * sibling threads is exiting. */
-    wait_on[0] = param->main_event;
-    /* The rest of the handles go behind the main event handle. */
-    memcpy(wait_on + 1, param->handles, param->handles_count * sizeof(HANDLE));
-
-    res = WaitForMultipleObjects(param->handles_count + 1, wait_on, FALSE, INFINITE);
-    if (res > 0 && res < (param->handles_count + 1)) {
-        /* One of the original handles got signaled. Save its absolute index into
-         * the output variable. */
-        InterlockedCompareExchange(param->signaled_index,
-                                   res - 1L + param->first_handle_index, -1L);
-    }
-
-    /* Notify the caller (and the siblings) that the wait is over. */
-    SetEvent(param->main_event);
-
-    _endthreadex(0);
-    return 0;
-}
-
-/* WaitForMultipeObjects fixer routine.
- * Param:
- *  handles Array of handles to wait on.
- *  handles_count Number of handles in the array.
- * Return:
- *  (>= 0 && < handles_count) - Index of the signaled handle in the array, or
- *  WAIT_FAILED on an error.
- */
-static int
-_wait_for_all(HANDLE* handles, int handles_count)
-{
-    WaitForAllParam* threads;
-    HANDLE main_event;
-    int chunks, chunk, remains;
-
-    /* This variable is going to be accessed by several threads at the same time,
-     * this is bound to fail randomly when the core is run on multi-core machines.
-     * To solve this, we need to do the following (1 _and_ 2):
-     * 1. Use the "volatile" qualifier to ensure the compiler doesn't optimize
-     *    out the reads/writes in this function unexpectedly.
-     * 2. Ensure correct memory ordering. The "simple" way to do that is to wrap
-     *    all accesses inside a critical section. But we can also use
-     *    InterlockedCompareExchange() which always provide a full memory barrier
-     *    on Win32.
-     */
-    volatile LONG sig_index = -1;
-
-    /* Calculate number of chunks, and allocate thread param array. */
-    chunks = handles_count / WAIT_ALL_CHUNK_SIZE;
-    remains = handles_count % WAIT_ALL_CHUNK_SIZE;
-    threads = (WaitForAllParam*)malloc((chunks + (remains ? 1 : 0)) *
-                                        sizeof(WaitForAllParam));
-    if (threads == NULL) {
-        D("Unable to allocate thread array for %d handles.", handles_count);
-        return (int)WAIT_FAILED;
-    }
-
-    /* Create main event to wait on for all waiting threads. This is a "manualy
-     * 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", GetLastError());
-        free(threads);
-        return (int)WAIT_FAILED;
-    }
-
-    /*
-     * Initialize waiting thread parameters.
-     */
-
-    for (chunk = 0; chunk < chunks; chunk++) {
-        threads[chunk].main_event = main_event;
-        threads[chunk].signaled_index = &sig_index;
-        threads[chunk].first_handle_index = WAIT_ALL_CHUNK_SIZE * chunk;
-        threads[chunk].handles = handles + threads[chunk].first_handle_index;
-        threads[chunk].handles_count = WAIT_ALL_CHUNK_SIZE;
-    }
-    if (remains) {
-        threads[chunk].main_event = main_event;
-        threads[chunk].signaled_index = &sig_index;
-        threads[chunk].first_handle_index = WAIT_ALL_CHUNK_SIZE * chunk;
-        threads[chunk].handles = handles + threads[chunk].first_handle_index;
-        threads[chunk].handles_count = remains;
-        chunks++;
-    }
-
-    /* Start the waiting threads. */
-    for (chunk = 0; chunk < chunks; chunk++) {
-        /* Note that using adb_thread_create is not appropriate here, since we
-         * need a handle to wait on for thread termination. */
-        threads[chunk].thread = (HANDLE)_beginthreadex(NULL, 0, _in_waiter_thread,
-                                                       &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",
-              chunk, chunks, errno);
-            chunks = chunk;
-            SetEvent(main_event);
-            break;
-        }
-    }
-
-    /* Wait on any of the threads to get signaled. */
-    WaitForSingleObject(main_event, INFINITE);
-
-    /* Wait on all the waiting threads to exit. */
-    for (chunk = 0; chunk < chunks; chunk++) {
-        WaitForSingleObject(threads[chunk].thread, INFINITE);
-        CloseHandle(threads[chunk].thread);
-    }
-
-    CloseHandle(main_event);
-    free(threads);
-
-
-    const int ret = (int)InterlockedCompareExchange(&sig_index, -1, -1);
-    return (ret >= 0) ? ret : (int)WAIT_FAILED;
-}
-
-static EventLooperRec  win32_looper;
-
-static void fdevent_init(void)
-{
-    win32_looper.htab_count = 0;
-    win32_looper.hooks      = NULL;
-}
-
-static void fdevent_connect(fdevent *fde)
-{
-    EventLooper  looper = &win32_looper;
-    int          events = fde->state & FDE_EVENTMASK;
-
-    if (events != 0)
-        event_looper_hook( looper, fde->fd, events );
-}
-
-static void fdevent_disconnect(fdevent *fde)
-{
-    EventLooper  looper = &win32_looper;
-    int          events = fde->state & FDE_EVENTMASK;
-
-    if (events != 0)
-        event_looper_unhook( looper, fde->fd, events );
-}
-
-static void fdevent_update(fdevent *fde, unsigned events)
-{
-    EventLooper  looper  = &win32_looper;
-    unsigned     events0 = fde->state & FDE_EVENTMASK;
-
-    if (events != events0) {
-        int  removes = events0 & ~events;
-        int  adds    = events  & ~events0;
-        if (removes) {
-            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", adds, fde->fd);
-            event_looper_hook  ( looper, fde->fd, adds );
-        }
-    }
-}
-
-static void fdevent_process()
-{
-    EventLooper  looper = &win32_looper;
-    EventHook    hook;
-    int          gotone = 0;
-
-    /* if we have at least one ready hook, execute it/them */
-    for (hook = looper->hooks; hook; hook = hook->next) {
-        hook->ready = 0;
-        if (hook->prepare) {
-            hook->prepare(hook);
-            if (hook->ready != 0) {
-                event_hook_signal( hook );
-                gotone = 1;
-            }
-        }
-    }
-
-    /* nothing's ready yet, so wait for something to happen */
-    if (!gotone)
-    {
-        looper->htab_count = 0;
-
-        for (hook = looper->hooks; hook; hook = hook->next)
-        {
-            if (hook->start && !hook->start(hook)) {
-                D( "fdevent_process: error when starting a hook" );
-                return;
-            }
-            if (hook->h != INVALID_HANDLE_VALUE) {
-                int  nn;
-
-                for (nn = 0; nn < looper->htab_count; nn++)
-                {
-                    if ( looper->htab[nn] == hook->h )
-                        goto DontAdd;
-                }
-                looper->htab[ looper->htab_count++ ] = hook->h;
-            DontAdd:
-                ;
-            }
-        }
-
-        if (looper->htab_count == 0) {
-            D( "fdevent_process: nothing to wait for !!" );
-            return;
-        }
-
-        do
-        {
-            int   wait_ret;
-
-            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.", 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", GetLastError() );
-            } else {
-                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
-                 */
-                if ((unsigned)wait_ret < (unsigned)looper->htab_count)
-                {
-                    for (hook = looper->hooks; hook; hook = hook->next)
-                    {
-                        if ( looper->htab[wait_ret] == hook->h       &&
-                         (!hook->check || hook->check(hook)) )
-                        {
-                            D( "adb_win32: signaling %s for %x", hook->fh->name, hook->ready );
-                            event_hook_signal( hook );
-                            gotone = 1;
-                            break;
-                        }
-                    }
-                }
-            }
-        }
-        while (!gotone);
-
-        for (hook = looper->hooks; hook; hook = hook->next) {
-            if (hook->stop)
-                hook->stop( hook );
-        }
-    }
-
-    for (hook = looper->hooks; hook; hook = hook->next) {
-        if (hook->peek && hook->peek(hook))
-                event_hook_signal( hook );
-    }
-}
-
-
-static void fdevent_register(fdevent *fde)
-{
-    int  fd = fde->fd - WIN32_FH_BASE;
-
-    if(fd < 0) {
-        FATAL("bogus negative fd (%d)\n", fde->fd);
-    }
-
-    if(fd >= fd_table_max) {
-        int oldmax = fd_table_max;
-        if(fde->fd > 32000) {
-            FATAL("bogus huuuuge fd (%d)\n", fde->fd);
-        }
-        if(fd_table_max == 0) {
-            fdevent_init();
-            fd_table_max = 256;
-        }
-        while(fd_table_max <= fd) {
-            fd_table_max *= 2;
-        }
-        fd_table = reinterpret_cast<fdevent**>(realloc(fd_table, sizeof(fdevent*) * fd_table_max));
-        if(fd_table == 0) {
-            FATAL("could not expand fd_table to %d entries\n", fd_table_max);
-        }
-        memset(fd_table + oldmax, 0, sizeof(int) * (fd_table_max - oldmax));
-    }
-
-    fd_table[fd] = fde;
-}
-
-static void fdevent_unregister(fdevent *fde)
-{
-    int  fd = fde->fd - WIN32_FH_BASE;
-
-    if((fd < 0) || (fd >= fd_table_max)) {
-        FATAL("fd out of range (%d)\n", fde->fd);
-    }
-
-    if(fd_table[fd] != fde) {
-        FATAL("fd_table out of sync");
-    }
-
-    fd_table[fd] = 0;
-
-    if(!(fde->state & FDE_DONT_CLOSE)) {
-        dump_fde(fde, "close");
-        adb_close(fde->fd);
-    }
-}
-
-static void fdevent_plist_enqueue(fdevent *node)
-{
-    fdevent *list = &list_pending;
-
-    node->next = list;
-    node->prev = list->prev;
-    node->prev->next = node;
-    list->prev = node;
-}
-
-static void fdevent_plist_remove(fdevent *node)
-{
-    node->prev->next = node->next;
-    node->next->prev = node->prev;
-    node->next = 0;
-    node->prev = 0;
-}
-
-static fdevent *fdevent_plist_dequeue(void)
-{
-    fdevent *list = &list_pending;
-    fdevent *node = list->next;
-
-    if(node == list) return 0;
-
-    list->next = node->next;
-    list->next->prev = list;
-    node->next = 0;
-    node->prev = 0;
-
-    return node;
-}
-
-fdevent *fdevent_create(int fd, fd_func func, void *arg)
-{
-    fdevent *fde = (fdevent*) malloc(sizeof(fdevent));
-    if(fde == 0) return 0;
-    fdevent_install(fde, fd, func, arg);
-    fde->state |= FDE_CREATED;
-    return fde;
-}
-
-void fdevent_destroy(fdevent *fde)
-{
-    if(fde == 0) return;
-    if(!(fde->state & FDE_CREATED)) {
-        FATAL("fde %p not created by fdevent_create()\n", fde);
-    }
-    fdevent_remove(fde);
-}
-
-void fdevent_install(fdevent *fde, int fd, fd_func func, void *arg)
-{
-    memset(fde, 0, sizeof(fdevent));
-    fde->state = FDE_ACTIVE;
-    fde->fd = fd;
-    fde->func = func;
-    fde->arg = arg;
-
-    fdevent_register(fde);
-    dump_fde(fde, "connect");
-    fdevent_connect(fde);
-    fde->state |= FDE_ACTIVE;
-}
-
-void fdevent_remove(fdevent *fde)
-{
-    if(fde->state & FDE_PENDING) {
-        fdevent_plist_remove(fde);
-    }
-
-    if(fde->state & FDE_ACTIVE) {
-        fdevent_disconnect(fde);
-        dump_fde(fde, "disconnect");
-        fdevent_unregister(fde);
-    }
-
-    fde->state = 0;
-    fde->events = 0;
-}
-
-
-void fdevent_set(fdevent *fde, unsigned events)
-{
-    events &= FDE_EVENTMASK;
-
-    if((fde->state & FDE_EVENTMASK) == (int)events) return;
-
-    if(fde->state & FDE_ACTIVE) {
-        fdevent_update(fde, events);
-        dump_fde(fde, "update");
-    }
-
-    fde->state = (fde->state & FDE_STATEMASK) | events;
-
-    if(fde->state & FDE_PENDING) {
-            /* if we're pending, make sure
-            ** we don't signal an event that
-            ** is no longer wanted.
-            */
-        fde->events &= (~events);
-        if(fde->events == 0) {
-            fdevent_plist_remove(fde);
-            fde->state &= (~FDE_PENDING);
-        }
-    }
-}
-
-void fdevent_add(fdevent *fde, unsigned events)
-{
-    fdevent_set(
-        fde, (fde->state & FDE_EVENTMASK) | (events & FDE_EVENTMASK));
-}
-
-void fdevent_del(fdevent *fde, unsigned events)
-{
-    fdevent_set(
-        fde, (fde->state & FDE_EVENTMASK) & (~(events & FDE_EVENTMASK)));
-}
-
-void fdevent_loop()
-{
-    fdevent *fde;
-
-    for(;;) {
-#if DEBUG
-        fprintf(stderr,"--- ---- waiting for events\n");
-#endif
-        fdevent_process();
-
-        while((fde = fdevent_plist_dequeue())) {
-            unsigned events = fde->events;
-            fde->events = 0;
-            fde->state &= (~FDE_PENDING);
-            dump_fde(fde, "callback");
-            fde->func(fde->fd, events, fde->arg);
-        }
-    }
-}
-
-/**  FILE EVENT HOOKS
- **/
-
-static void  _event_file_prepare( EventHook  hook )
-{
-    if (hook->wanted & (FDE_READ|FDE_WRITE)) {
-        /* we can always read/write */
-        hook->ready |= hook->wanted & (FDE_READ|FDE_WRITE);
-    }
-}
-
-static int  _event_file_peek( EventHook  hook )
-{
-    return (hook->wanted & (FDE_READ|FDE_WRITE));
-}
-
-static void  _fh_file_hook( FH  f, int  events, EventHook  hook )
-{
-    hook->h       = f->fh_handle;
-    hook->prepare = _event_file_prepare;
-    hook->peek    = _event_file_peek;
-}
-
-/** SOCKET EVENT HOOKS
- **/
-
-static void  _event_socket_verify( EventHook  hook, WSANETWORKEVENTS*  evts )
-{
-    if ( evts->lNetworkEvents & (FD_READ|FD_ACCEPT|FD_CLOSE) ) {
-        if (hook->wanted & FDE_READ)
-            hook->ready |= FDE_READ;
-        if ((evts->iErrorCode[FD_READ] != 0) && hook->wanted & FDE_ERROR)
-            hook->ready |= FDE_ERROR;
-    }
-    if ( evts->lNetworkEvents & (FD_WRITE|FD_CONNECT|FD_CLOSE) ) {
-        if (hook->wanted & FDE_WRITE)
-            hook->ready |= FDE_WRITE;
-        if ((evts->iErrorCode[FD_WRITE] != 0) && hook->wanted & FDE_ERROR)
-            hook->ready |= FDE_ERROR;
-    }
-    if ( evts->lNetworkEvents & FD_OOB ) {
-        if (hook->wanted & FDE_ERROR)
-            hook->ready |= FDE_ERROR;
-    }
-}
-
-static void  _event_socket_prepare( EventHook  hook )
-{
-    WSANETWORKEVENTS  evts;
-
-    /* look if some of the events we want already happened ? */
-    if (!WSAEnumNetworkEvents( hook->fh->fh_socket, NULL, &evts ))
-        _event_socket_verify( hook, &evts );
-}
-
-static int  _socket_wanted_to_flags( int  wanted )
-{
-    int  flags = 0;
-    if (wanted & FDE_READ)
-        flags |= FD_READ | FD_ACCEPT | FD_CLOSE;
-
-    if (wanted & FDE_WRITE)
-        flags |= FD_WRITE | FD_CONNECT | FD_CLOSE;
-
-    if (wanted & FDE_ERROR)
-        flags |= FD_OOB;
-
-    return flags;
-}
-
-static int _event_socket_start( EventHook  hook )
-{
-    /* create an event which we're going to wait for */
-    FH    fh    = hook->fh;
-    long  flags = _socket_wanted_to_flags( hook->wanted );
-
-    hook->h = fh->event;
-    if (hook->h == INVALID_HANDLE_VALUE) {
-        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)", hook->fh->name, hook->wanted, flags );
-        if ( WSAEventSelect( fh->fh_socket, hook->h, flags ) ) {
-            D( "_event_socket_start: WSAEventSelect() for %s failed, error %d", hook->fh->name, WSAGetLastError() );
-            CloseHandle( hook->h );
-            hook->h = INVALID_HANDLE_VALUE;
-            exit(1);
-            return 0;
-        }
-        fh->mask = flags;
-    }
-    return 1;
-}
-
-static void _event_socket_stop( EventHook  hook )
-{
-    hook->h = INVALID_HANDLE_VALUE;
-}
-
-static int  _event_socket_check( EventHook  hook )
-{
-    int               result = 0;
-    FH                fh = hook->fh;
-    WSANETWORKEVENTS  evts;
-
-    if (!WSAEnumNetworkEvents( fh->fh_socket, hook->h, &evts ) ) {
-        _event_socket_verify( hook, &evts );
-        result = (hook->ready != 0);
-        if (result) {
-            ResetEvent( hook->h );
-        }
-    }
-    D( "_event_socket_check %s returns %d", fh->name, result );
-    return  result;
-}
-
-static int  _event_socket_peek( EventHook  hook )
-{
-    WSANETWORKEVENTS  evts;
-    FH                fh = hook->fh;
-
-    /* look if some of the events we want already happened ? */
-    if (!WSAEnumNetworkEvents( fh->fh_socket, NULL, &evts )) {
-        _event_socket_verify( hook, &evts );
-        if (hook->ready)
-            ResetEvent( hook->h );
-    }
-
-    return hook->ready != 0;
-}
-
-
-
-static void  _fh_socket_hook( FH  f, int  events, EventHook  hook )
-{
-    hook->prepare = _event_socket_prepare;
-    hook->start   = _event_socket_start;
-    hook->stop    = _event_socket_stop;
-    hook->check   = _event_socket_check;
-    hook->peek    = _event_socket_peek;
-
-    // TODO: check return value?
-    _event_socket_start( hook );
-}
-
-/** SOCKETPAIR EVENT HOOKS
- **/
-
-static void  _event_socketpair_prepare( EventHook  hook )
-{
-    FH          fh   = hook->fh;
-    SocketPair  pair = fh->fh_pair;
-    BipBuffer   rbip = (pair->a_fd == fh) ? &pair->b2a_bip : &pair->a2b_bip;
-    BipBuffer   wbip = (pair->a_fd == fh) ? &pair->a2b_bip : &pair->b2a_bip;
-
-    if (hook->wanted & FDE_READ && rbip->can_read)
-        hook->ready |= FDE_READ;
-
-    if (hook->wanted & FDE_WRITE && wbip->can_write)
-        hook->ready |= FDE_WRITE;
- }
-
- static int  _event_socketpair_start( EventHook  hook )
- {
-    FH          fh   = hook->fh;
-    SocketPair  pair = fh->fh_pair;
-    BipBuffer   rbip = (pair->a_fd == fh) ? &pair->b2a_bip : &pair->a2b_bip;
-    BipBuffer   wbip = (pair->a_fd == fh) ? &pair->a2b_bip : &pair->b2a_bip;
-
-    if (hook->wanted == FDE_READ)
-        hook->h = rbip->evt_read;
-
-    else if (hook->wanted == FDE_WRITE)
-        hook->h = wbip->evt_write;
-
-    else {
-        D("_event_socketpair_start: can't handle FDE_READ+FDE_WRITE" );
-        return 0;
-    }
-    D( "_event_socketpair_start: hook %s for %x wanted=%x",
-       hook->fh->name, _fh_to_int(fh), hook->wanted);
-    return 1;
-}
-
-static int  _event_socketpair_peek( EventHook  hook )
-{
-    _event_socketpair_prepare( hook );
-    return hook->ready != 0;
-}
-
-static void  _fh_socketpair_hook( FH  fh, int  events, EventHook  hook )
-{
-    hook->prepare = _event_socketpair_prepare;
-    hook->start   = _event_socketpair_start;
-    hook->peek    = _event_socketpair_peek;
-}
-
-
 static adb_mutex_t g_console_output_buffer_lock;
 
 void
diff --git a/adb/sysdeps_win32_test.cpp b/adb/sysdeps_win32_test.cpp
index 8f610cf..c3a3fd7 100755
--- a/adb/sysdeps_win32_test.cpp
+++ b/adb/sysdeps_win32_test.cpp
@@ -87,8 +87,12 @@
     TestAdbStrError(-1, "Unknown error");
     // Test very big, positive unknown error.
     TestAdbStrError(1000000, "Unknown error");
+
     // Test success case.
-    TestAdbStrError(0, "No error");
+    // Wine returns "Success" for strerror(0), Windows returns "No error", so accept both.
+    std::string success = adb_strerror(0);
+    EXPECT_TRUE(success == "Success" || success == "No error") << "strerror(0) = " << success;
+
     // Test error that regular strerror() should have a string for.
     TestAdbStrError(EPERM, "Operation not permitted");
     // Test error that regular strerror() doesn't have a string for, but that
diff --git a/adb/test_device.py b/adb/test_device.py
index afc061a..18174a2 100644
--- a/adb/test_device.py
+++ b/adb/test_device.py
@@ -766,6 +766,22 @@
             if host_dir is not None:
                 shutil.rmtree(host_dir)
 
+    @requires_non_root
+    def test_push_error_reporting(self):
+        """Make sure that errors that occur while pushing a file get reported
+
+        Bug: http://b/26816782
+        """
+        with tempfile.NamedTemporaryFile() as tmp_file:
+            tmp_file.write('\0' * 1024 * 1024)
+            tmp_file.flush()
+            try:
+                self.device.push(local=tmp_file.name, remote='/system/')
+                self.fail("push should not have succeeded")
+            except subprocess.CalledProcessError as e:
+                output = e.output
+
+            self.assertIn("Permission denied", output)
 
     def _test_pull(self, remote_file, checksum):
         tmp_write = tempfile.NamedTemporaryFile(mode='wb', delete=False)
diff --git a/adb/transport.cpp b/adb/transport.cpp
index f8c8c61..8ca1e49 100644
--- a/adb/transport.cpp
+++ b/adb/transport.cpp
@@ -190,8 +190,7 @@
 //
 // 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)
-{
+static void read_transport_thread(void* _t) {
     atransport *t = reinterpret_cast<atransport*>(_t);
     apacket *p;
 
@@ -244,13 +243,11 @@
     D("%s: read_transport thread is exiting", t->serial);
     kick_transport(t);
     transport_unref(t);
-    return 0;
 }
 
 // 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)
-{
+static void write_transport_thread(void* _t) {
     atransport *t = reinterpret_cast<atransport*>(_t);
     apacket *p;
     int active = 0;
@@ -295,7 +292,6 @@
     D("%s: write_transport thread is exiting, fd %d", t->serial, t->fd);
     kick_transport(t);
     transport_unref(t);
-    return 0;
 }
 
 static void kick_transport_locked(atransport* t) {
diff --git a/adb/transport.h b/adb/transport.h
index 76d6afa..4c0c008 100644
--- a/adb/transport.h
+++ b/adb/transport.h
@@ -50,7 +50,6 @@
     // it's better to do this piece by piece.
 
     atransport() {
-        auth_fde = {};
         transport_fde = {};
         protocol_version = A_VERSION;
         max_payload = MAX_PAYLOAD;
@@ -87,7 +86,6 @@
 
     void* key = nullptr;
     unsigned char token[TOKEN_SIZE] = {};
-    fdevent auth_fde;
     size_t failed_auth_attempts = 0;
 
     const std::string connection_state_name() const;
diff --git a/adb/transport_local.cpp b/adb/transport_local.cpp
index d2a375a..e6e699b 100644
--- a/adb/transport_local.cpp
+++ b/adb/transport_local.cpp
@@ -121,8 +121,7 @@
 }
 
 #if ADB_HOST
-static void *client_socket_thread(void *x)
-{
+static void client_socket_thread(void* x) {
     adb_thread_setname("client_socket_thread");
     D("transport: client_socket_thread() starting");
     while (true) {
@@ -135,13 +134,11 @@
         }
         sleep(1);
     }
-    return 0;
 }
 
 #else // ADB_HOST
 
-static void *server_socket_thread(void * arg)
-{
+static void server_socket_thread(void* arg) {
     int serverfd, fd;
     sockaddr_storage ss;
     sockaddr *addrp = reinterpret_cast<sockaddr*>(&ss);
@@ -174,7 +171,6 @@
         }
     }
     D("transport: server_socket_thread() exiting");
-    return 0;
 }
 
 /* This is relevant only for ADB daemon running inside the emulator. */
@@ -220,14 +216,13 @@
  *   the transport registration is completed. That's why we need to send the
  *   'start' request after the transport is registered.
  */
-static void *qemu_socket_thread(void * arg)
-{
-/* 'accept' request to the adb QEMUD service. */
-static const char _accept_req[] = "accept";
-/* 'start' request to the adb QEMUD service. */
-static const char _start_req[]  = "start";
-/* 'ok' reply from the adb QEMUD service. */
-static const char _ok_resp[]    = "ok";
+static void qemu_socket_thread(void* arg) {
+    /* 'accept' request to the adb QEMUD service. */
+    static const char _accept_req[] = "accept";
+    /* 'start' request to the adb QEMUD service. */
+    static const char _start_req[] = "start";
+    /* 'ok' reply from the adb QEMUD service. */
+    static const char _ok_resp[] = "ok";
 
     const int port = (int) (uintptr_t) arg;
     int res, fd;
@@ -247,7 +242,7 @@
          * implement adb QEMUD service. Fall back to the old TCP way. */
         D("adb service is not available. Falling back to TCP socket.");
         adb_thread_create(server_socket_thread, arg);
-        return 0;
+        return;
     }
 
     for(;;) {
@@ -275,21 +270,21 @@
             fd = qemu_pipe_open(con_name);
             if (fd < 0) {
                 D("adb service become unavailable.");
-                return 0;
+                return;
             }
         } else {
             D("Unable to send the '%s' request to ADB service.", _accept_req);
-            return 0;
+            return;
         }
     }
     D("transport: qemu_socket_thread() exiting");
-    return 0;
+    return;
 }
 #endif  // !ADB_HOST
 
 void local_init(int port)
 {
-    void* (*func)(void *);
+    adb_thread_func_t func;
     const char* debug_name = "";
 
 #if ADB_HOST
diff --git a/adb/transport_test.cpp b/adb/transport_test.cpp
index 97fc069..1bdea2a 100644
--- a/adb/transport_test.cpp
+++ b/adb/transport_test.cpp
@@ -53,7 +53,6 @@
 
         EXPECT_EQ(key, rhs.key);
         EXPECT_EQ(0, memcmp(token, rhs.token, TOKEN_SIZE));
-        EXPECT_EQ(0, memcmp(&auth_fde, &rhs.auth_fde, sizeof(fdevent)));
         EXPECT_EQ(failed_auth_attempts, rhs.failed_auth_attempts);
 
         EXPECT_EQ(features(), rhs.features());
diff --git a/adb/usb_linux.cpp b/adb/usb_linux.cpp
index ed5d2d6..500898a 100644
--- a/adb/usb_linux.cpp
+++ b/adb/usb_linux.cpp
@@ -571,7 +571,7 @@
     register_usb_transport(done_usb, serial.c_str(), dev_path, done_usb->writeable);
 }
 
-static void* device_poll_thread(void* unused) {
+static void device_poll_thread(void*) {
     adb_thread_setname("device poll");
     D("Created device thread");
     while (true) {
@@ -580,7 +580,6 @@
         kick_disconnected_devices();
         sleep(1);
     }
-    return nullptr;
 }
 
 void usb_init() {
diff --git a/adb/usb_linux_client.cpp b/adb/usb_linux_client.cpp
index a4f1a70..c863ed2 100644
--- a/adb/usb_linux_client.cpp
+++ b/adb/usb_linux_client.cpp
@@ -232,10 +232,7 @@
     },
 };
 
-
-
-static void *usb_adb_open_thread(void *x)
-{
+static void usb_adb_open_thread(void* x) {
     struct usb_handle *usb = (struct usb_handle *)x;
     int fd;
 
@@ -270,7 +267,7 @@
     }
 
     // never gets here
-    return 0;
+    abort();
 }
 
 static int usb_adb_write(usb_handle *h, const void *data, int len)
@@ -434,8 +431,7 @@
     return;
 }
 
-static void *usb_ffs_open_thread(void *x)
-{
+static void usb_ffs_open_thread(void* x) {
     struct usb_handle *usb = (struct usb_handle *)x;
 
     adb_thread_setname("usb ffs open");
@@ -462,7 +458,7 @@
     }
 
     // never gets here
-    return 0;
+    abort();
 }
 
 static int usb_ffs_write(usb_handle* h, const void* data, int len) {
diff --git a/adb/usb_osx.cpp b/adb/usb_osx.cpp
index 148be1d..54d4c6c 100644
--- a/adb/usb_osx.cpp
+++ b/adb/usb_osx.cpp
@@ -400,9 +400,7 @@
     return NULL;
 }
 
-
-void* RunLoopThread(void* unused)
-{
+static void RunLoopThread(void* unused) {
     adb_thread_setname("RunLoop");
     InitUSB();
 
@@ -420,7 +418,6 @@
     IONotificationPortDestroy(notificationPort);
 
     LOG(DEBUG) << "RunLoopThread done";
-    return NULL;
 }
 
 static void usb_cleanup() {
diff --git a/adb/usb_windows.cpp b/adb/usb_windows.cpp
index e79008f..8ecca37 100644
--- a/adb/usb_windows.cpp
+++ b/adb/usb_windows.cpp
@@ -97,7 +97,7 @@
 
 /// Entry point for thread that polls (every second) for new usb interfaces.
 /// This routine calls find_devices in infinite loop.
-void* device_poll_thread(void* unused);
+static void device_poll_thread(void*);
 
 /// Initializes this module
 void usb_init();
@@ -172,7 +172,7 @@
   return 1;
 }
 
-void* device_poll_thread(void* unused) {
+void device_poll_thread(void*) {
   adb_thread_setname("Device Poll");
   D("Created device thread");
 
@@ -180,8 +180,6 @@
     find_devices();
     adb_sleep_ms(1000);
   }
-
-  return NULL;
 }
 
 static LRESULT CALLBACK _power_window_proc(HWND hwnd, UINT uMsg, WPARAM wParam,
@@ -203,7 +201,7 @@
   return DefWindowProcW(hwnd, uMsg, wParam, lParam);
 }
 
-static void* _power_notification_thread(void* unused) {
+static void _power_notification_thread(void*) {
   // This uses a thread with its own window message pump to get power
   // notifications. If adb runs from a non-interactive service account, this
   // might not work (not sure). If that happens to not work, we could use
@@ -255,8 +253,6 @@
   // shutting down. Not a big deal since the whole process will be going away
   // soon anyway.
   D("Power notification thread exiting");
-
-  return NULL;
 }
 
 void usb_init() {
diff --git a/base/include/base/unique_fd.h b/base/include/base/unique_fd.h
new file mode 100644
index 0000000..4117775
--- /dev/null
+++ b/base/include/base/unique_fd.h
@@ -0,0 +1,79 @@
+/*
+ * 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 ANDROID_BASE_UNIQUE_FD_H
+#define ANDROID_BASE_UNIQUE_FD_H
+
+#include <unistd.h>
+
+#include <base/macros.h>
+
+/* Container for a file descriptor that automatically closes the descriptor as
+ * it goes out of scope.
+ *
+ *      unique_fd ufd(open("/some/path", "r"));
+ *
+ *      if (ufd.get() < 0) // invalid descriptor
+ *          return error;
+ *
+ *      // Do something useful
+ *
+ *      return 0; // descriptor is closed here
+ */
+namespace android {
+namespace base {
+
+class unique_fd final {
+ public:
+  unique_fd() : value_(-1) {}
+
+  explicit unique_fd(int value) : value_(value) {}
+  ~unique_fd() { clear(); }
+
+  unique_fd(unique_fd&& other) : value_(other.release()) {}
+  unique_fd& operator = (unique_fd&& s) {
+    reset(s.release());
+    return *this;
+  }
+
+  void reset(int new_value) {
+    if (value_ >= 0)
+      close(value_);
+    value_ = new_value;
+  }
+
+  void clear() {
+    reset(-1);
+  }
+
+  int get() const { return value_; }
+
+  int release() {
+    int ret = value_;
+    value_ = -1;
+    return ret;
+  }
+
+ private:
+  int value_;
+
+  DISALLOW_COPY_AND_ASSIGN(unique_fd);
+};
+
+}  // namespace base
+}  // namespace android
+
+#endif // ANDROID_BASE_UNIQUE_FD_H
diff --git a/bootstat/README.md b/bootstat/README.md
index b3964ce..76ea6c1 100644
--- a/bootstat/README.md
+++ b/bootstat/README.md
@@ -12,6 +12,7 @@
       -p, --print           Dump the boot event records to the console
       -r, --record          Record the timestamp of a named boot event
       --record_boot_reason  Record the reason why the device booted
+      --record_time_since_factory_reset Record the time since the device was reset
 
 ## Relative time ##
 
diff --git a/bootstat/boot_event_record_store.cpp b/bootstat/boot_event_record_store.cpp
index 22a67bb..8282b40 100644
--- a/bootstat/boot_event_record_store.cpp
+++ b/bootstat/boot_event_record_store.cpp
@@ -50,7 +50,7 @@
   SetStorePath(BOOTSTAT_DATA_DIR);
 }
 
-void BootEventRecordStore::AddBootEvent(const std::string& name) {
+void BootEventRecordStore::AddBootEvent(const std::string& event) {
   std::string uptime_str;
   if (!android::base::ReadFileToString("/proc/uptime", &uptime_str)) {
     LOG(ERROR) << "Failed to read /proc/uptime";
@@ -58,15 +58,15 @@
 
   // Cast intentionally rounds down.
   int32_t uptime = static_cast<int32_t>(strtod(uptime_str.c_str(), NULL));
-  AddBootEventWithValue(name, uptime);
+  AddBootEventWithValue(event, uptime);
 }
 
 // The implementation of AddBootEventValue makes use of the mtime file
 // attribute to store the value associated with a boot event in order to
 // optimize on-disk size requirements and small-file thrashing.
 void BootEventRecordStore::AddBootEventWithValue(
-    const std::string& name, int32_t value) {
-  std::string record_path = GetBootEventPath(name);
+    const std::string& event, int32_t value) {
+  std::string record_path = GetBootEventPath(event);
   if (creat(record_path.c_str(), S_IRUSR | S_IWUSR) == -1) {
     PLOG(ERROR) << "Failed to create " << record_path;
   }
@@ -86,6 +86,22 @@
   }
 }
 
+bool BootEventRecordStore::GetBootEvent(
+    const std::string& event, BootEventRecord* record) const {
+  CHECK_NE(static_cast<BootEventRecord*>(nullptr), record);
+  CHECK(!event.empty());
+
+  const std::string record_path = GetBootEventPath(event);
+  int32_t uptime;
+  if (!ParseRecordEventTime(record_path, &uptime)) {
+    LOG(ERROR) << "Failed to parse boot time record: " << record_path;
+    return false;
+  }
+
+  *record = std::make_pair(event, uptime);
+  return true;
+}
+
 std::vector<BootEventRecordStore::BootEventRecord> BootEventRecordStore::
     GetAllBootEvents() const {
   std::vector<BootEventRecord> events;
@@ -104,14 +120,13 @@
     }
 
     const std::string event = entry->d_name;
-    const std::string record_path = GetBootEventPath(event);
-    int32_t uptime;
-    if (!ParseRecordEventTime(record_path, &uptime)) {
-      LOG(ERROR) << "Failed to parse boot time record: " << record_path;
+    BootEventRecord record;
+    if (!GetBootEvent(event, &record)) {
+      LOG(ERROR) << "Failed to parse boot time event: " << event;
       continue;
     }
 
-    events.push_back(std::make_pair(event, uptime));
+    events.push_back(record);
   }
 
   return events;
diff --git a/bootstat/boot_event_record_store.h b/bootstat/boot_event_record_store.h
index d1b7835..4d5deee 100644
--- a/bootstat/boot_event_record_store.h
+++ b/bootstat/boot_event_record_store.h
@@ -34,12 +34,16 @@
 
   BootEventRecordStore();
 
-  // Persists the boot event named |name| in the record store.
-  void AddBootEvent(const std::string& name);
+  // Persists the boot |event| in the record store.
+  void AddBootEvent(const std::string& event);
 
-  // Persists the boot event named |name| with the associated |value| in the
-  // record store.
-  void AddBootEventWithValue(const std::string& name, int32_t value);
+  // Persists the boot |event| with the associated |value| in the record store.
+  void AddBootEventWithValue(const std::string& event, int32_t value);
+
+  // Queries the named boot |event|. |record| must be non-null. |record|
+  // contains the boot event data on success. Returns true iff the query is
+  // successful.
+  bool GetBootEvent(const std::string& event, BootEventRecord* record) const;
 
   // Returns a list of all of the boot events persisted in the record store.
   std::vector<BootEventRecord> GetAllBootEvents() const;
@@ -50,6 +54,7 @@
   FRIEND_TEST(BootEventRecordStoreTest, AddSingleBootEvent);
   FRIEND_TEST(BootEventRecordStoreTest, AddMultipleBootEvents);
   FRIEND_TEST(BootEventRecordStoreTest, AddBootEventWithValue);
+  FRIEND_TEST(BootEventRecordStoreTest, GetBootEvent);
 
   // Sets the filesystem path of the record store.
   void SetStorePath(const std::string& path);
diff --git a/bootstat/boot_event_record_store_test.cpp b/bootstat/boot_event_record_store_test.cpp
index 3e6d706..0d7bbb0 100644
--- a/bootstat/boot_event_record_store_test.cpp
+++ b/bootstat/boot_event_record_store_test.cpp
@@ -165,4 +165,27 @@
   ASSERT_EQ(1U, events.size());
   EXPECT_EQ("permian", events[0].first);
   EXPECT_EQ(42, events[0].second);
+}
+
+TEST_F(BootEventRecordStoreTest, GetBootEvent) {
+  BootEventRecordStore store;
+  store.SetStorePath(GetStorePathForTesting());
+
+  // Event does not exist.
+  BootEventRecordStore::BootEventRecord record;
+  bool result = store.GetBootEvent("nonexistent", &record);
+  EXPECT_EQ(false, result);
+
+  // Empty path.
+  EXPECT_DEATH(store.GetBootEvent(std::string(), &record), std::string());
+
+  // Success case.
+  store.AddBootEventWithValue("carboniferous", 314);
+  result = store.GetBootEvent("carboniferous", &record);
+  EXPECT_EQ(true, result);
+  EXPECT_EQ("carboniferous", record.first);
+  EXPECT_EQ(314, record.second);
+
+  // Null |record|.
+  EXPECT_DEATH(store.GetBootEvent("carboniferous", nullptr), std::string());
 }
\ No newline at end of file
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 1d16f69..c199190 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -22,6 +22,7 @@
 #include <unistd.h>
 #include <cstddef>
 #include <cstdio>
+#include <ctime>
 #include <map>
 #include <memory>
 #include <string>
@@ -79,7 +80,8 @@
           "  -l, --log             Log all metrics to logstorage\n"
           "  -p, --print           Dump the boot event records to the console\n"
           "  -r, --record          Record the timestamp of a named boot event\n"
-          "  --record_boot_reason  Record the reason why the device booted\n");
+          "  --record_boot_reason  Record the reason why the device booted\n"
+          "  --record_time_since_factory_reset Record the time since the device was reset\n");
 }
 
 // Constructs a readable, printable string from the givencommand line
@@ -155,6 +157,32 @@
   boot_event_store.AddBootEventWithValue("boot_reason", boot_reason);
 }
 
+// Records two metrics related to the user resetting a device: the time at
+// which the device is reset, and the time since the user last reset the
+// device.  The former is only set once per-factory reset.
+void RecordFactoryReset() {
+  BootEventRecordStore boot_event_store;
+  BootEventRecordStore::BootEventRecord record;
+
+  time_t current_time_utc = time(nullptr);
+
+  // The factory_reset boot event does not exist after the device is reset, so
+  // use this signal to mark the time of the factory reset.
+  if (!boot_event_store.GetBootEvent("factory_reset", &record)) {
+    boot_event_store.AddBootEventWithValue("factory_reset", current_time_utc);
+    boot_event_store.AddBootEventWithValue("time_since_factory_reset", 0);
+    return;
+  }
+
+  // Calculate and record the difference in time between now and the
+  // factory_reset time.
+  time_t factory_reset_utc = record.second;
+  time_t time_since_factory_reset = difftime(current_time_utc,
+                                             factory_reset_utc);
+  boot_event_store.AddBootEventWithValue("time_since_factory_reset",
+                                         time_since_factory_reset);
+}
+
 }  // namespace
 
 int main(int argc, char **argv) {
@@ -165,12 +193,14 @@
 
   int option_index = 0;
   static const char boot_reason_str[] = "record_boot_reason";
+  static const char factory_reset_str[] = "record_time_since_factory_reset";
   static const struct option long_options[] = {
     { "help",            no_argument,       NULL,   'h' },
     { "log",             no_argument,       NULL,   'l' },
     { "print",           no_argument,       NULL,   'p' },
     { "record",          required_argument, NULL,   'r' },
     { boot_reason_str,   no_argument,       NULL,   0 },
+    { factory_reset_str, no_argument,       NULL,   0 },
     { NULL,              0,                 NULL,   0 }
   };
 
@@ -182,6 +212,8 @@
         const std::string option_name = long_options[option_index].name;
         if (option_name == boot_reason_str) {
           RecordBootReason();
+        } else if (option_name == factory_reset_str) {
+          RecordFactoryReset();
         } else {
           LOG(ERROR) << "Invalid option: " << option_name;
         }
diff --git a/bootstat/bootstat.rc b/bootstat/bootstat.rc
index 218b9f8..13ef27e 100644
--- a/bootstat/bootstat.rc
+++ b/bootstat/bootstat.rc
@@ -13,5 +13,8 @@
     # Record the boot reason.
     exec - root root -- /system/bin/bootstat --record_boot_reason
 
+    # Record time since factory reset.
+    exec - root root -- /system/bin/bootstat --record_time_since_factory_reset
+
     # Log all boot events.
     exec - root root -- /system/bin/bootstat -l
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index e0f7c73..a326f55 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -32,6 +32,7 @@
     protocol.cpp \
     socket.cpp \
     tcp.cpp \
+    udp.cpp \
     util.cpp \
 
 LOCAL_MODULE := fastboot
@@ -114,6 +115,8 @@
     socket_test.cpp \
     tcp.cpp \
     tcp_test.cpp \
+    udp.cpp \
+    udp_test.cpp \
 
 LOCAL_STATIC_LIBRARIES := libbase libcutils
 
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index 7c7d417..636092e 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -59,6 +59,7 @@
 #include "fs.h"
 #include "tcp.h"
 #include "transport.h"
+#include "udp.h"
 #include "usb.h"
 
 #ifndef O_BINARY
@@ -245,22 +246,41 @@
         return transport;
     }
 
+    Socket::Protocol protocol = Socket::Protocol::kTcp;
     std::string host;
-    int port = tcp::kDefaultPort;
-    if (serial != nullptr && android::base::StartsWith(serial, "tcp:")) {
-        std::string error;
-        const char* address = serial + strlen("tcp:");
+    int port = 0;
+    if (serial != nullptr) {
+        const char* net_address = nullptr;
 
-        if (!android::base::ParseNetAddress(address, &host, &port, nullptr, &error)) {
-            fprintf(stderr, "error: Invalid network address '%s': %s\n", address, error.c_str());
-            return nullptr;
+        if (android::base::StartsWith(serial, "tcp:")) {
+            protocol = Socket::Protocol::kTcp;
+            port = tcp::kDefaultPort;
+            net_address = serial + strlen("tcp:");
+        } else if (android::base::StartsWith(serial, "udp:")) {
+            protocol = Socket::Protocol::kUdp;
+            port = udp::kDefaultPort;
+            net_address = serial + strlen("udp:");
+        }
+
+        if (net_address != nullptr) {
+            std::string error;
+            if (!android::base::ParseNetAddress(net_address, &host, &port, nullptr, &error)) {
+                fprintf(stderr, "error: Invalid network address '%s': %s\n", net_address,
+                        error.c_str());
+                return nullptr;
+            }
         }
     }
 
     while (true) {
         if (!host.empty()) {
             std::string error;
-            transport = tcp::Connect(host, port, &error).release();
+            if (protocol == Socket::Protocol::kTcp) {
+                transport = tcp::Connect(host, port, &error).release();
+            } else if (protocol == Socket::Protocol::kUdp) {
+                transport = udp::Connect(host, port, &error).release();
+            }
+
             if (transport == nullptr && announce) {
                 fprintf(stderr, "error: %s\n", error.c_str());
             }
@@ -337,8 +357,9 @@
             "                                           formatting.\n"
             "  -s <specific device>                     Specify a device. For USB, provide either\n"
             "                                           a serial number or path to device port.\n"
-            "                                           For TCP, provide an address in the form\n"
-            "                                           tcp:<hostname>[:port].\n"
+            "                                           For ethernet, provide an address in the"
+            "                                           form <protocol>:<hostname>[:port] where"
+            "                                           <protocol> is either tcp or udp.\n"
             "  -p <product>                             Specify product name.\n"
             "  -c <cmdline>                             Override kernel commandline.\n"
             "  -i <vendor id>                           Specify a custom USB vendor id.\n"
diff --git a/fastboot/fastboot_protocol.txt b/fastboot/fastboot_protocol.txt
index 4aa48b1..2801703 100644
--- a/fastboot/fastboot_protocol.txt
+++ b/fastboot/fastboot_protocol.txt
@@ -17,9 +17,9 @@
   * The protocol is entirely host-driven and synchronous (unlike the
     multi-channel, bi-directional, asynchronous ADB protocol)
 
-* TCP
+* TCP or UDP
   * Device must be reachable via IP.
-  * Device will act as the TCP server, fastboot will be the client.
+  * Device will act as the server, fastboot will be the client.
   * Fastboot data is wrapped in a simple protocol; see below for details.
 
 
@@ -217,3 +217,226 @@
 Host    [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x0B]getvar:none
 Device  [0x00][0x00][0x00][0x00][0x00][0x00][0x00][0x04]OKAY
 Host    <disconnect>
+
+
+UDP Protocol v1
+---------------
+
+The UDP protocol is more complex than TCP since we must implement reliability
+to ensure no packets are lost, but the general concept of wrapping the fastboot
+protocol is the same.
+
+Overview:
+  1. As with TCP, the device will listen on UDP port 5554.
+  2. Maximum UDP packet size is negotiated during initialization.
+  3. The host drives all communication; the device may only send a packet as a
+     response to a host packet.
+  4. If the host does not receive a response in 500ms it will re-transmit.
+
+-- UDP Packet format --
+  +----------+----+-------+-------+--------------------+
+  | Byte #   | 0  |   1   | 2 - 3 |  4+                |
+  +----------+----+-------+-------+--------------------+
+  | Contents | ID | Flags | Seq # | Data               |
+  +----------+----+-------+-------+--------------------+
+
+  ID      Packet ID:
+            0x00: Error.
+            0x01: Query.
+            0x02: Initialization.
+            0x03: Fastboot.
+
+          Packet types are described in more detail below.
+
+  Flags   Packet flags: 0 0 0 0 0 0 0 C
+            C=1 indicates a continuation packet; the data is too large and will
+                continue in the next packet.
+
+            Remaining bits are reserved for future use and must be set to 0.
+
+  Seq #   2-byte packet sequence number (big-endian). The host will increment
+          this by 1 with each new packet, and the device must provide the
+          corresponding sequence number in the response packets.
+
+  Data    Packet data, not present in all packets.
+
+-- Packet Types --
+Query     The host sends a query packet once on startup to sync with the device.
+          The host will not know the current sequence number, so the device must
+          respond to all query packets regardless of sequence number.
+
+          The response data field should contain a 2-byte big-endian value
+          giving the next expected sequence number.
+
+Init      The host sends an init packet once the query response is returned. The
+          device must abort any in-progress operation and prepare for a new
+          fastboot session. This message is meant to allow recovery if a
+          previous session failed, e.g. due to network error or user Ctrl+C.
+
+          The data field contains two big-endian 2-byte values, a protocol
+          version and the max UDP packet size (including the 4-byte header).
+          Both the host and device will send these values, and in each case
+          the minimum of the sent values must be used.
+
+Fastboot  These packets wrap the fastboot protocol. To write, the host will
+          send a packet with fastboot data, and the device will reply with an
+          empty packet as an ACK. To read, the host will send an empty packet,
+          and the device will reply with fastboot data. The device may not give
+          any data in the ACK packet.
+
+Error     The device may respond to any packet with an error packet to indicate
+          a UDP protocol error. The data field should contain an ASCII string
+          describing the error. This is the only case where a device is allowed
+          to return a packet ID other than the one sent by the host.
+
+-- Packet Size --
+The maximum packet size is negotiated by the host and device in the Init packet.
+Devices must support at least 512-byte packets, but packet size has a direct
+correlation with download speed, so devices are strongly suggested to support at
+least 1024-byte packets. On a local network with 0.5ms round-trip time this will
+provide transfer rates of ~2MB/s. Over WiFi it will likely be significantly
+less.
+
+Query and Initialization packets, which are sent before size negotiation is
+complete, must always be 512 bytes or less.
+
+-- Packet Re-Transmission --
+The host will re-transmit any packet that does not receive a response. The
+requirement of exactly one device response packet per host packet is how we
+achieve reliability and in-order delivery of packets.
+
+For simplicity of implementation, there is no windowing of multiple
+unacknowledged packets in this version of the protocol. The host will continue
+to send the same packet until a response is received. Windowing functionality
+may be implemented in future versions if necessary to increase performance.
+
+The first Query packet will only be attempted a small number of times, but
+subsequent packets will attempt to retransmit for at least 1 minute before
+giving up. This means a device may safely ignore host UDP packets for up to 1
+minute during long operations, e.g. writing to flash.
+
+-- Continuation Packets --
+Any packet may set the continuation flag to indicate that the data is
+incomplete. Large data such as downloading an image may require many
+continuation packets. The receiver should respond to a continuation packet with
+an empty packet to acknowledge receipt. See examples below.
+
+-- Summary --
+The host starts with a Query packet, then an Initialization packet, after
+which only Fastboot packets are sent. Fastboot packets may contain data from
+the host for writes, or from the device for reads, but not both.
+
+Given a next expected sequence number S and a received packet P, the device
+behavior should be:
+  if P is a Query packet:
+    * respond with a Query packet with S in the data field
+  else if P has sequence == S:
+    * process P and take any required action
+    * create a response packet R with the same ID and sequence as P, containing
+      any response data required.
+    * transmit R and save it in case of re-transmission
+    * increment S
+  else if P has sequence == S - 1:
+    * re-transmit the saved response packet R from above
+  else:
+    * ignore the packet
+
+-- Examples --
+In the examples below, S indicates the starting client sequence number.
+
+Host                                    Client
+======================================================================
+[Initialization, S = 0x55AA]
+[Host: version 1, 2048-byte packets. Client: version 2, 1024-byte packets.]
+[Resulting values to use: version = 1, max packet size = 1024]
+ID   Flag SeqH SeqL Data                ID   Flag SeqH SeqL Data
+----------------------------------------------------------------------
+0x01 0x00 0x00 0x00
+                                        0x01 0x00 0x00 0x00 0x55 0xAA
+0x02 0x00 0x55 0xAA 0x00 0x01 0x08 0x00
+                                        0x02 0x00 0x55 0xAA 0x00 0x02 0x04 0x00
+
+----------------------------------------------------------------------
+[fastboot "getvar" commands, S = 0x0001]
+ID    Flags SeqH  SeqL  Data            ID    Flags SeqH  SeqL  Data
+----------------------------------------------------------------------
+0x03  0x00  0x00  0x01  getvar:version
+                                        0x03  0x00  0x00  0x01
+0x03  0x00  0x00  0x02
+                                        0x03  0x00  0x00  0x02  OKAY0.4
+0x03  0x00  0x00  0x03  getvar:foo
+                                        0x03  0x00  0x00  0x03
+0x03  0x00  0x00  0x04
+                                        0x03  0x00  0x00  0x04  OKAY
+
+----------------------------------------------------------------------
+[fastboot "INFO" responses, S = 0x0000]
+ID    Flags SeqH  SeqL  Data            ID    Flags SeqH  SeqL  Data
+----------------------------------------------------------------------
+0x03  0x00  0x00  0x00  <command>
+                                        0x03  0x00  0x00  0x00
+0x03  0x00  0x00  0x01
+                                        0x03  0x00  0x00  0x01  INFOWait1
+0x03  0x00  0x00  0x02
+                                        0x03  0x00  0x00  0x02  INFOWait2
+0x03  0x00  0x00  0x03
+                                        0x03  0x00  0x00  0x03  OKAY
+
+----------------------------------------------------------------------
+[Chunking 2100 bytes of data, max packet size = 1024, S = 0xFFFF]
+ID   Flag SeqH SeqL Data                ID   Flag SeqH SeqL Data
+----------------------------------------------------------------------
+0x03 0x00 0xFF 0xFF download:0000834
+                                        0x03 0x00 0xFF 0xFF
+0x03 0x00 0x00 0x00
+                                        0x03 0x00 0x00 0x00 DATA0000834
+0x03 0x01 0x00 0x01 <1020 bytes>
+                                        0x03 0x00 0x00 0x01
+0x03 0x01 0x00 0x02 <1020 bytes>
+                                        0x03 0x00 0x00 0x02
+0x03 0x00 0x00 0x03 <60 bytes>
+                                        0x03 0x00 0x00 0x03
+0x03 0x00 0x00 0x04
+                                        0x03 0x00 0x00 0x04 OKAY
+
+----------------------------------------------------------------------
+[Unknown ID error, S = 0x0000]
+ID    Flags SeqH  SeqL  Data            ID    Flags SeqH  SeqL  Data
+----------------------------------------------------------------------
+0x10  0x00  0x00  0x00
+                                        0x00  0x00  0x00  0x00  <error message>
+
+----------------------------------------------------------------------
+[Host packet loss and retransmission, S = 0x0000]
+ID    Flags SeqH  SeqL  Data            ID    Flags SeqH  SeqL  Data
+----------------------------------------------------------------------
+0x03  0x00  0x00  0x00  getvar:version [lost]
+0x03  0x00  0x00  0x00  getvar:version [lost]
+0x03  0x00  0x00  0x00  getvar:version
+                                        0x03  0x00  0x00  0x00
+0x03  0x00  0x00  0x01
+                                        0x03  0x00  0x00  0x01  OKAY0.4
+
+----------------------------------------------------------------------
+[Client packet loss and retransmission, S = 0x0000]
+ID    Flags SeqH  SeqL  Data            ID    Flags SeqH  SeqL  Data
+----------------------------------------------------------------------
+0x03  0x00  0x00  0x00  getvar:version
+                                        0x03  0x00  0x00  0x00 [lost]
+0x03  0x00  0x00  0x00  getvar:version
+                                        0x03  0x00  0x00  0x00 [lost]
+0x03  0x00  0x00  0x00  getvar:version
+                                        0x03  0x00  0x00  0x00
+0x03  0x00  0x00  0x01
+                                        0x03  0x00  0x00  0x01  OKAY0.4
+
+----------------------------------------------------------------------
+[Host packet delayed, S = 0x0000]
+ID    Flags SeqH  SeqL  Data            ID    Flags SeqH  SeqL  Data
+----------------------------------------------------------------------
+0x03  0x00  0x00  0x00  getvar:version [delayed]
+0x03  0x00  0x00  0x00  getvar:version
+                                        0x03  0x00  0x00  0x00
+0x03  0x00  0x00  0x01
+                                        0x03  0x00  0x00  0x01  OKAY0.4
+0x03  0x00  0x00  0x00  getvar:version [arrives late with old seq#, is ignored]
diff --git a/fastboot/socket.cpp b/fastboot/socket.cpp
index d49f47f..14ecd93 100644
--- a/fastboot/socket.cpp
+++ b/fastboot/socket.cpp
@@ -48,18 +48,6 @@
     return ret;
 }
 
-bool Socket::SetReceiveTimeout(int timeout_ms) {
-    if (timeout_ms != receive_timeout_ms_) {
-        if (socket_set_receive_timeout(sock_, timeout_ms) == 0) {
-            receive_timeout_ms_ = timeout_ms;
-            return true;
-        }
-        return false;
-    }
-
-    return true;
-}
-
 ssize_t Socket::ReceiveAll(void* data, size_t length, int timeout_ms) {
     size_t total = 0;
 
@@ -82,6 +70,40 @@
     return socket_get_local_port(sock_);
 }
 
+// According to Windows setsockopt() documentation, if a Windows socket times out during send() or
+// recv() the state is indeterminate and should not be used. Our UDP protocol relies on being able
+// to re-send after a timeout, so we must use select() rather than SO_RCVTIMEO.
+// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms740476(v=vs.85).aspx.
+bool Socket::WaitForRecv(int timeout_ms) {
+    receive_timed_out_ = false;
+
+    // In our usage |timeout_ms| <= 0 means block forever, so just return true immediately and let
+    // the subsequent recv() do the blocking.
+    if (timeout_ms <= 0) {
+        return true;
+    }
+
+    // select() doesn't always check this case and will block for |timeout_ms| if we let it.
+    if (sock_ == INVALID_SOCKET) {
+        return false;
+    }
+
+    fd_set read_set;
+    FD_ZERO(&read_set);
+    FD_SET(sock_, &read_set);
+
+    timeval timeout;
+    timeout.tv_sec = timeout_ms / 1000;
+    timeout.tv_usec = (timeout_ms % 1000) * 1000;
+
+    int result = TEMP_FAILURE_RETRY(select(sock_ + 1, &read_set, nullptr, nullptr, &timeout));
+
+    if (result == 0) {
+        receive_timed_out_ = true;
+    }
+    return result == 1;
+}
+
 // Implements the Socket interface for UDP.
 class UdpSocket : public Socket {
   public:
@@ -127,7 +149,7 @@
 }
 
 ssize_t UdpSocket::Receive(void* data, size_t length, int timeout_ms) {
-    if (!SetReceiveTimeout(timeout_ms)) {
+    if (!WaitForRecv(timeout_ms)) {
         return -1;
     }
 
@@ -206,7 +228,7 @@
 }
 
 ssize_t TcpSocket::Receive(void* data, size_t length, int timeout_ms) {
-    if (!SetReceiveTimeout(timeout_ms)) {
+    if (!WaitForRecv(timeout_ms)) {
         return -1;
     }
 
diff --git a/fastboot/socket.h b/fastboot/socket.h
index c0bd7c9..de543db 100644
--- a/fastboot/socket.h
+++ b/fastboot/socket.h
@@ -81,13 +81,17 @@
     virtual bool Send(std::vector<cutils_socket_buffer_t> buffers) = 0;
 
     // Waits up to |timeout_ms| to receive up to |length| bytes of data. |timout_ms| of 0 will
-    // block forever. Returns the number of bytes received or -1 on error/timeout. On timeout
-    // errno will be set to EAGAIN or EWOULDBLOCK.
+    // block forever. Returns the number of bytes received or -1 on error/timeout; see
+    // ReceiveTimedOut() to distinguish between the two.
     virtual ssize_t Receive(void* data, size_t length, int timeout_ms) = 0;
 
     // Calls Receive() until exactly |length| bytes have been received or an error occurs.
     virtual ssize_t ReceiveAll(void* data, size_t length, int timeout_ms);
 
+    // Returns true if the last Receive() call timed out normally and can be retried; fatal errors
+    // or successful reads will return false.
+    bool ReceiveTimedOut() { return receive_timed_out_; }
+
     // Closes the socket. Returns 0 on success, -1 on error.
     virtual int Close();
 
@@ -102,10 +106,13 @@
     // Protected constructor to force factory function use.
     Socket(cutils_socket_t sock);
 
-    // Update the socket receive timeout if necessary.
-    bool SetReceiveTimeout(int timeout_ms);
+    // Blocks up to |timeout_ms| until a read is possible on |sock_|, and sets |receive_timed_out_|
+    // as appropriate to help distinguish between normal timeouts and fatal errors. Returns true if
+    // a subsequent recv() on |sock_| will complete without blocking or if |timeout_ms| <= 0.
+    bool WaitForRecv(int timeout_ms);
 
     cutils_socket_t sock_ = INVALID_SOCKET;
+    bool receive_timed_out_ = false;
 
     // Non-class functions we want to override during tests to verify functionality. Implementation
     // should call this rather than using socket_send_buffers() directly.
@@ -113,8 +120,6 @@
             socket_send_buffers_function_ = &socket_send_buffers;
 
   private:
-    int receive_timeout_ms_ = 0;
-
     FRIEND_TEST(SocketTest, TestTcpSendBuffers);
     FRIEND_TEST(SocketTest, TestUdpSendBuffers);
 
diff --git a/fastboot/socket_mock.cpp b/fastboot/socket_mock.cpp
index c962f30..2531b53 100644
--- a/fastboot/socket_mock.cpp
+++ b/fastboot/socket_mock.cpp
@@ -55,7 +55,7 @@
         return false;
     }
 
-    bool return_value = events_.front().return_value;
+    bool return_value = events_.front().status;
     events_.pop();
     return return_value;
 }
@@ -76,21 +76,28 @@
         return -1;
     }
 
-    if (events_.front().type != EventType::kReceive) {
+    const Event& event = events_.front();
+    if (event.type != EventType::kReceive) {
         ADD_FAILURE() << "Receive() was called out-of-order";
         return -1;
     }
 
-    if (events_.front().return_value > static_cast<ssize_t>(length)) {
-        ADD_FAILURE() << "Receive(): not enough bytes (" << length << ") for "
-                      << events_.front().message;
+    const std::string& message = event.message;
+    if (message.length() > length) {
+        ADD_FAILURE() << "Receive(): not enough bytes (" << length << ") for " << message;
         return -1;
     }
 
-    ssize_t return_value = events_.front().return_value;
-    if (return_value > 0) {
-        memcpy(data, events_.front().message.data(), return_value);
+    receive_timed_out_ = event.status;
+    ssize_t return_value = message.length();
+
+    // Empty message indicates failure.
+    if (message.empty()) {
+        return_value = -1;
+    } else {
+        memcpy(data, message.data(), message.length());
     }
+
     events_.pop();
     return return_value;
 }
@@ -124,18 +131,21 @@
 }
 
 void SocketMock::AddReceive(std::string message) {
-    ssize_t return_value = message.length();
-    events_.push(Event(EventType::kReceive, std::move(message), return_value, nullptr));
+    events_.push(Event(EventType::kReceive, std::move(message), false, nullptr));
+}
+
+void SocketMock::AddReceiveTimeout() {
+    events_.push(Event(EventType::kReceive, "", true, nullptr));
 }
 
 void SocketMock::AddReceiveFailure() {
-    events_.push(Event(EventType::kReceive, "", -1, nullptr));
+    events_.push(Event(EventType::kReceive, "", false, nullptr));
 }
 
 void SocketMock::AddAccept(std::unique_ptr<Socket> sock) {
-    events_.push(Event(EventType::kAccept, "", 0, std::move(sock)));
+    events_.push(Event(EventType::kAccept, "", false, std::move(sock)));
 }
 
-SocketMock::Event::Event(EventType _type, std::string _message, ssize_t _return_value,
+SocketMock::Event::Event(EventType _type, std::string _message, ssize_t _status,
                          std::unique_ptr<Socket> _sock)
-        : type(_type), message(_message), return_value(_return_value), sock(std::move(_sock)) {}
+        : type(_type), message(_message), status(_status), sock(std::move(_sock)) {}
diff --git a/fastboot/socket_mock.h b/fastboot/socket_mock.h
index 41fe06d..eacd6bb 100644
--- a/fastboot/socket_mock.h
+++ b/fastboot/socket_mock.h
@@ -71,7 +71,10 @@
     // Adds data to provide for Receive().
     void AddReceive(std::string message);
 
-    // Adds a Receive() failure.
+    // Adds a Receive() timeout after which ReceiveTimedOut() will return true.
+    void AddReceiveTimeout();
+
+    // Adds a Receive() failure after which ReceiveTimedOut() will return false.
     void AddReceiveFailure();
 
     // Adds a Socket to return from Accept().
@@ -81,12 +84,12 @@
     enum class EventType { kSend, kReceive, kAccept };
 
     struct Event {
-        Event(EventType _type, std::string _message, ssize_t _return_value,
+        Event(EventType _type, std::string _message, ssize_t _status,
               std::unique_ptr<Socket> _sock);
 
         EventType type;
         std::string message;
-        ssize_t return_value;
+        bool status;  // Return value for Send() or timeout status for Receive().
         std::unique_ptr<Socket> sock;
     };
 
diff --git a/fastboot/socket_test.cpp b/fastboot/socket_test.cpp
index cc71075..affbdfd 100644
--- a/fastboot/socket_test.cpp
+++ b/fastboot/socket_test.cpp
@@ -28,7 +28,8 @@
 #include <gtest/gtest-spi.h>
 #include <gtest/gtest.h>
 
-enum { kTestTimeoutMs = 3000 };
+static constexpr int kShortTimeoutMs = 10;
+static constexpr int kTestTimeoutMs = 3000;
 
 // Creates connected sockets |server| and |client|. Returns true on success.
 bool MakeConnectedSockets(Socket::Protocol protocol, std::unique_ptr<Socket>* server,
@@ -87,6 +88,50 @@
     }
 }
 
+TEST(SocketTest, TestReceiveTimeout) {
+    std::unique_ptr<Socket> server, client;
+    char buffer[16];
+
+    for (Socket::Protocol protocol : {Socket::Protocol::kUdp, Socket::Protocol::kTcp}) {
+        ASSERT_TRUE(MakeConnectedSockets(protocol, &server, &client));
+
+        EXPECT_EQ(-1, server->Receive(buffer, sizeof(buffer), kShortTimeoutMs));
+        EXPECT_TRUE(server->ReceiveTimedOut());
+
+        EXPECT_EQ(-1, client->Receive(buffer, sizeof(buffer), kShortTimeoutMs));
+        EXPECT_TRUE(client->ReceiveTimedOut());
+    }
+
+    // UDP will wait for timeout if the other side closes.
+    ASSERT_TRUE(MakeConnectedSockets(Socket::Protocol::kUdp, &server, &client));
+    EXPECT_EQ(0, server->Close());
+    EXPECT_EQ(-1, client->Receive(buffer, sizeof(buffer), kShortTimeoutMs));
+    EXPECT_TRUE(client->ReceiveTimedOut());
+}
+
+TEST(SocketTest, TestReceiveFailure) {
+    std::unique_ptr<Socket> server, client;
+    char buffer[16];
+
+    for (Socket::Protocol protocol : {Socket::Protocol::kUdp, Socket::Protocol::kTcp}) {
+        ASSERT_TRUE(MakeConnectedSockets(protocol, &server, &client));
+
+        EXPECT_EQ(0, server->Close());
+        EXPECT_EQ(-1, server->Receive(buffer, sizeof(buffer), kTestTimeoutMs));
+        EXPECT_FALSE(server->ReceiveTimedOut());
+
+        EXPECT_EQ(0, client->Close());
+        EXPECT_EQ(-1, client->Receive(buffer, sizeof(buffer), kTestTimeoutMs));
+        EXPECT_FALSE(client->ReceiveTimedOut());
+    }
+
+    // TCP knows right away when the other side closes and returns 0 to indicate EOF.
+    ASSERT_TRUE(MakeConnectedSockets(Socket::Protocol::kTcp, &server, &client));
+    EXPECT_EQ(0, server->Close());
+    EXPECT_EQ(0, client->Receive(buffer, sizeof(buffer), kTestTimeoutMs));
+    EXPECT_FALSE(client->ReceiveTimedOut());
+}
+
 // Tests sending and receiving large packets.
 TEST(SocketTest, TestLargePackets) {
     std::string message(1024, '\0');
@@ -290,6 +335,11 @@
 
     mock->AddReceiveFailure();
     EXPECT_FALSE(ReceiveString(mock, "foo"));
+    EXPECT_FALSE(mock->ReceiveTimedOut());
+
+    mock->AddReceiveTimeout();
+    EXPECT_FALSE(ReceiveString(mock, "foo"));
+    EXPECT_TRUE(mock->ReceiveTimedOut());
 
     mock->AddReceive("foo");
     mock->AddReceiveFailure();
diff --git a/fastboot/tcp.cpp b/fastboot/tcp.cpp
index da2880a..e42c4e1 100644
--- a/fastboot/tcp.cpp
+++ b/fastboot/tcp.cpp
@@ -28,6 +28,7 @@
 
 #include "tcp.h"
 
+#include <android-base/parseint.h>
 #include <android-base/stringprintf.h>
 
 namespace tcp {
@@ -98,7 +99,8 @@
         return false;
     }
 
-    char buffer[kHandshakeLength];
+    char buffer[kHandshakeLength + 1];
+    buffer[kHandshakeLength] = '\0';
     if (socket_->ReceiveAll(buffer, kHandshakeLength, kHandshakeTimeoutMs) != kHandshakeLength) {
         *error = android::base::StringPrintf(
                 "No initialization message received (%s). Target may not support TCP fastboot",
@@ -111,9 +113,10 @@
         return false;
     }
 
-    if (memcmp(buffer + 2, "01", 2) != 0) {
+    int version = 0;
+    if (!android::base::ParseInt(buffer + 2, &version) || version < kProtocolVersion) {
         *error = android::base::StringPrintf("Unknown TCP protocol version %s (host version %02d)",
-                                             std::string(buffer + 2, 2).c_str(), kProtocolVersion);
+                                             buffer + 2, kProtocolVersion);
         return false;
     }
 
diff --git a/fastboot/tcp_test.cpp b/fastboot/tcp_test.cpp
index 7d80d76..6e867ae 100644
--- a/fastboot/tcp_test.cpp
+++ b/fastboot/tcp_test.cpp
@@ -42,6 +42,16 @@
     EXPECT_EQ("", error);
 }
 
+TEST(TcpConnectTest, TestNewerVersionSuccess) {
+    std::unique_ptr<SocketMock> mock(new SocketMock);
+    mock->ExpectSend("FB01");
+    mock->AddReceive("FB99");
+
+    std::string error;
+    EXPECT_NE(nullptr, tcp::internal::Connect(std::move(mock), &error));
+    EXPECT_EQ("", error);
+}
+
 TEST(TcpConnectTest, TestSendFailure) {
     std::unique_ptr<SocketMock> mock(new SocketMock);
     mock->ExpectSendFailure("FB01");
@@ -74,11 +84,11 @@
 TEST(TcpConnectTest, TestUnknownVersionFailure) {
     std::unique_ptr<SocketMock> mock(new SocketMock);
     mock->ExpectSend("FB01");
-    mock->AddReceive("FB02");
+    mock->AddReceive("FB00");
 
     std::string error;
     EXPECT_EQ(nullptr, tcp::internal::Connect(std::move(mock), &error));
-    EXPECT_EQ("Unknown TCP protocol version 02 (host version 01)", error);
+    EXPECT_EQ("Unknown TCP protocol version 00 (host version 01)", error);
 }
 
 // Fixture to configure a SocketMock for a successful TCP connection.
diff --git a/fastboot/udp.cpp b/fastboot/udp.cpp
new file mode 100644
index 0000000..b36bd60
--- /dev/null
+++ b/fastboot/udp.cpp
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+// This file implements the fastboot UDP protocol; see fastboot_protocol.txt for documentation.
+
+#include "udp.h"
+
+#include <errno.h>
+#include <stdio.h>
+
+#include <list>
+#include <memory>
+#include <vector>
+
+#include <android-base/macros.h>
+#include <android-base/stringprintf.h>
+
+#include "socket.h"
+
+namespace udp {
+
+using namespace internal;
+
+constexpr size_t kMinPacketSize = 512;
+constexpr size_t kHeaderSize = 4;
+
+enum Index {
+    kIndexId = 0,
+    kIndexFlags = 1,
+    kIndexSeqH = 2,
+    kIndexSeqL = 3,
+};
+
+// Extracts a big-endian uint16_t from a byte array.
+static uint16_t ExtractUint16(const uint8_t* bytes) {
+    return (static_cast<uint16_t>(bytes[0]) << 8) | bytes[1];
+}
+
+// Packet header handling.
+class Header {
+  public:
+    Header();
+    ~Header() = default;
+
+    uint8_t id() const { return bytes_[kIndexId]; }
+    const uint8_t* bytes() const { return bytes_; }
+
+    void Set(uint8_t id, uint16_t sequence, Flag flag);
+
+    // Checks whether |response| is a match for this header.
+    bool Matches(const uint8_t* response);
+
+  private:
+    uint8_t bytes_[kHeaderSize];
+};
+
+Header::Header() {
+    Set(kIdError, 0, kFlagNone);
+}
+
+void Header::Set(uint8_t id, uint16_t sequence, Flag flag) {
+    bytes_[kIndexId] = id;
+    bytes_[kIndexFlags] = flag;
+    bytes_[kIndexSeqH] = sequence >> 8;
+    bytes_[kIndexSeqL] = sequence;
+}
+
+bool Header::Matches(const uint8_t* response) {
+    // Sequence numbers must be the same to match, but the response ID can either be the same
+    // or an error response which is always accepted.
+    return bytes_[kIndexSeqH] == response[kIndexSeqH] &&
+           bytes_[kIndexSeqL] == response[kIndexSeqL] &&
+           (bytes_[kIndexId] == response[kIndexId] || response[kIndexId] == kIdError);
+}
+
+// Implements the Transport interface to work with the fastboot engine.
+class UdpTransport : public Transport {
+  public:
+    // Factory function so we can return nullptr if initialization fails.
+    static std::unique_ptr<UdpTransport> NewTransport(std::unique_ptr<Socket> socket,
+                                                      std::string* error);
+    ~UdpTransport() override = default;
+
+    ssize_t Read(void* data, size_t length) override;
+    ssize_t Write(const void* data, size_t length) override;
+    int Close() override;
+
+  private:
+    UdpTransport(std::unique_ptr<Socket> socket) : socket_(std::move(socket)) {}
+
+    // Performs the UDP initialization procedure. Returns true on success.
+    bool InitializeProtocol(std::string* error);
+
+    // Sends |length| bytes from |data| and waits for the response packet up to |attempts| times.
+    // Continuation packets are handled automatically and any return data is written to |rx_data|.
+    // Excess bytes that cannot fit in |rx_data| are dropped.
+    // On success, returns the number of response data bytes received, which may be greater than
+    // |rx_length|. On failure, returns -1 and fills |error| on failure.
+    ssize_t SendData(Id id, const uint8_t* tx_data, size_t tx_length, uint8_t* rx_data,
+                     size_t rx_length, int attempts, std::string* error);
+
+    // Helper for SendData(); sends a single packet and handles the response. |header| specifies
+    // the initial outgoing packet information but may be modified by this function.
+    ssize_t SendSinglePacketHelper(Header* header, const uint8_t* tx_data, size_t tx_length,
+                                   uint8_t* rx_data, size_t rx_length, int attempts,
+                                   std::string* error);
+
+    std::unique_ptr<Socket> socket_;
+    int sequence_ = -1;
+    size_t max_data_length_ = kMinPacketSize - kHeaderSize;
+    std::vector<uint8_t> rx_packet_;
+
+    DISALLOW_COPY_AND_ASSIGN(UdpTransport);
+};
+
+std::unique_ptr<UdpTransport> UdpTransport::NewTransport(std::unique_ptr<Socket> socket,
+                                                         std::string* error) {
+    std::unique_ptr<UdpTransport> transport(new UdpTransport(std::move(socket)));
+
+    if (!transport->InitializeProtocol(error)) {
+        return nullptr;
+    }
+
+    return transport;
+}
+
+bool UdpTransport::InitializeProtocol(std::string* error) {
+    uint8_t rx_data[4];
+
+    sequence_ = 0;
+    rx_packet_.resize(kMinPacketSize);
+
+    // First send the query packet to sync with the target. Only attempt this a small number of
+    // times so we can fail out quickly if the target isn't available.
+    ssize_t rx_bytes = SendData(kIdDeviceQuery, nullptr, 0, rx_data, sizeof(rx_data),
+                                kMaxConnectAttempts, error);
+    if (rx_bytes == -1) {
+        return false;
+    } else if (rx_bytes < 2) {
+        *error = "invalid query response from target";
+        return false;
+    }
+    // The first two bytes contain the next expected sequence number.
+    sequence_ = ExtractUint16(rx_data);
+
+    // Now send the initialization packet with our version and maximum packet size.
+    uint8_t init_data[] = {kProtocolVersion >> 8, kProtocolVersion & 0xFF,
+                           kHostMaxPacketSize >> 8, kHostMaxPacketSize & 0xFF};
+    rx_bytes = SendData(kIdInitialization, init_data, sizeof(init_data), rx_data, sizeof(rx_data),
+                        kMaxTransmissionAttempts, error);
+    if (rx_bytes == -1) {
+        return false;
+    } else if (rx_bytes < 4) {
+        *error = "invalid initialization response from target";
+        return false;
+    }
+
+    // The first two data bytes contain the version, the second two bytes contain the target max
+    // supported packet size, which must be at least 512 bytes.
+    uint16_t version = ExtractUint16(rx_data);
+    if (version < kProtocolVersion) {
+        *error = android::base::StringPrintf("target reported invalid protocol version %d",
+                                             version);
+        return false;
+    }
+    uint16_t packet_size = ExtractUint16(rx_data + 2);
+    if (packet_size < kMinPacketSize) {
+        *error = android::base::StringPrintf("target reported invalid packet size %d", packet_size);
+        return false;
+    }
+
+    packet_size = std::min(kHostMaxPacketSize, packet_size);
+    max_data_length_ = packet_size - kHeaderSize;
+    rx_packet_.resize(packet_size);
+
+    return true;
+}
+
+// SendData() is just responsible for chunking |data| into packets until it's all been sent.
+// Per-packet timeout/retransmission logic is done in SendSinglePacketHelper().
+ssize_t UdpTransport::SendData(Id id, const uint8_t* tx_data, size_t tx_length, uint8_t* rx_data,
+                               size_t rx_length, int attempts, std::string* error) {
+    if (socket_ == nullptr) {
+        *error = "socket is closed";
+        return -1;
+    }
+
+    Header header;
+    size_t packet_data_length;
+    ssize_t ret = 0;
+    // We often send header-only packets with no data as part of the protocol, so always send at
+    // least once even if |length| == 0, then repeat until we've sent all of |data|.
+    do {
+        // Set the continuation flag and truncate packet data if needed.
+        if (tx_length > max_data_length_) {
+            packet_data_length = max_data_length_;
+            header.Set(id, sequence_, kFlagContinuation);
+        } else {
+            packet_data_length = tx_length;
+            header.Set(id, sequence_, kFlagNone);
+        }
+
+        ssize_t bytes = SendSinglePacketHelper(&header, tx_data, packet_data_length, rx_data,
+                                               rx_length, attempts, error);
+
+        // Advance our read and write buffers for the next packet. Keep going even if we run out
+        // of receive buffer space so we can detect overflows.
+        if (bytes == -1) {
+            return -1;
+        } else if (static_cast<size_t>(bytes) < rx_length) {
+            rx_data += bytes;
+            rx_length -= bytes;
+        } else {
+            rx_data = nullptr;
+            rx_length = 0;
+        }
+
+        tx_length -= packet_data_length;
+        tx_data += packet_data_length;
+
+        ret += bytes;
+    } while (tx_length > 0);
+
+    return ret;
+}
+
+ssize_t UdpTransport::SendSinglePacketHelper(
+        Header* header, const uint8_t* tx_data, size_t tx_length, uint8_t* rx_data,
+        size_t rx_length, const int attempts, std::string* error) {
+    ssize_t total_data_bytes = 0;
+    error->clear();
+
+    int attempts_left = attempts;
+    while (attempts_left > 0) {
+        if (!socket_->Send({{header->bytes(), kHeaderSize}, {tx_data, tx_length}})) {
+            *error = Socket::GetErrorMessage();
+            return -1;
+        }
+
+        // Keep receiving until we get a matching response or we timeout.
+        ssize_t bytes = 0;
+        do {
+            bytes = socket_->Receive(rx_packet_.data(), rx_packet_.size(), kResponseTimeoutMs);
+            if (bytes == -1) {
+                if (socket_->ReceiveTimedOut()) {
+                    break;
+                }
+                *error = Socket::GetErrorMessage();
+                return -1;
+            } else if (bytes < static_cast<ssize_t>(kHeaderSize)) {
+                *error = "protocol error: incomplete header";
+                return -1;
+            }
+        } while (!header->Matches(rx_packet_.data()));
+
+        if (socket_->ReceiveTimedOut()) {
+            --attempts_left;
+            continue;
+        }
+        ++sequence_;
+
+        // Save to |error| or |rx_data| as appropriate.
+        if (rx_packet_[kIndexId] == kIdError) {
+            error->append(rx_packet_.data() + kHeaderSize, rx_packet_.data() + bytes);
+        } else {
+            total_data_bytes += bytes - kHeaderSize;
+            size_t rx_data_bytes = std::min<size_t>(bytes - kHeaderSize, rx_length);
+            if (rx_data_bytes > 0) {
+                memcpy(rx_data, rx_packet_.data() + kHeaderSize, rx_data_bytes);
+                rx_data += rx_data_bytes;
+                rx_length -= rx_data_bytes;
+            }
+        }
+
+        // If the response has a continuation flag we need to prompt for more data by sending
+        // an empty packet.
+        if (rx_packet_[kIndexFlags] & kFlagContinuation) {
+            // We got a valid response so reset our attempt counter.
+            attempts_left = attempts;
+            header->Set(rx_packet_[kIndexId], sequence_, kFlagNone);
+            tx_data = nullptr;
+            tx_length = 0;
+            continue;
+        }
+
+        break;
+    }
+
+    if (attempts_left <= 0) {
+        *error = "no response from target";
+        return -1;
+    }
+
+    if (rx_packet_[kIndexId] == kIdError) {
+        *error = "target reported error: " + *error;
+        return -1;
+    }
+
+    return total_data_bytes;
+}
+
+ssize_t UdpTransport::Read(void* data, size_t length) {
+    // Read from the target by sending an empty packet.
+    std::string error;
+    ssize_t bytes = SendData(kIdFastboot, nullptr, 0, reinterpret_cast<uint8_t*>(data), length,
+                             kMaxTransmissionAttempts, &error);
+
+    if (bytes == -1) {
+        fprintf(stderr, "UDP error: %s\n", error.c_str());
+        return -1;
+    } else if (static_cast<size_t>(bytes) > length) {
+        // Fastboot protocol error: the target sent more data than our fastboot engine was prepared
+        // to receive.
+        fprintf(stderr, "UDP error: receive overflow, target sent too much fastboot data\n");
+        return -1;
+    }
+
+    return bytes;
+}
+
+ssize_t UdpTransport::Write(const void* data, size_t length) {
+    std::string error;
+    ssize_t bytes = SendData(kIdFastboot, reinterpret_cast<const uint8_t*>(data), length, nullptr,
+                             0, kMaxTransmissionAttempts, &error);
+
+    if (bytes == -1) {
+        fprintf(stderr, "UDP error: %s\n", error.c_str());
+        return -1;
+    } else if (bytes > 0) {
+        // UDP protocol error: only empty ACK packets are allowed when writing to a device.
+        fprintf(stderr, "UDP error: target sent fastboot data out-of-turn\n");
+        return -1;
+    }
+
+    return length;
+}
+
+int UdpTransport::Close() {
+    if (socket_ == nullptr) {
+        return 0;
+    }
+
+    int result = socket_->Close();
+    socket_.reset();
+    return result;
+}
+
+std::unique_ptr<Transport> Connect(const std::string& hostname, int port, std::string* error) {
+    return internal::Connect(Socket::NewClient(Socket::Protocol::kUdp, hostname, port, error),
+                             error);
+}
+
+namespace internal {
+
+std::unique_ptr<Transport> Connect(std::unique_ptr<Socket> sock, std::string* error) {
+    if (sock == nullptr) {
+        // If Socket creation failed |error| is already set.
+        return nullptr;
+    }
+
+    return UdpTransport::NewTransport(std::move(sock), error);
+}
+
+}  // namespace internal
+
+}  // namespace udp
diff --git a/fastboot/udp.h b/fastboot/udp.h
new file mode 100644
index 0000000..14f5b35
--- /dev/null
+++ b/fastboot/udp.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef UDP_H_
+#define UDP_H_
+
+#include <memory>
+#include <string>
+
+#include "socket.h"
+#include "transport.h"
+
+namespace udp {
+
+constexpr int kDefaultPort = 5554;
+
+// Returns a newly allocated Transport object connected to |hostname|:|port|. On failure, |error| is
+// filled and nullptr is returned.
+std::unique_ptr<Transport> Connect(const std::string& hostname, int port, std::string* error);
+
+// Internal namespace for test use only.
+namespace internal {
+
+constexpr uint16_t kProtocolVersion = 1;
+
+// This will be negotiated with the device so may end up being smaller.
+constexpr uint16_t kHostMaxPacketSize = 8192;
+
+// Retransmission constants. Retransmission timeout must be at least 500ms, and the host must
+// attempt to send packets for at least 1 minute once the device has connected. See
+// fastboot_protocol.txt for more information.
+constexpr int kResponseTimeoutMs = 500;
+constexpr int kMaxConnectAttempts = 4;
+constexpr int kMaxTransmissionAttempts = 60 * 1000 / kResponseTimeoutMs;
+
+enum Id : uint8_t {
+    kIdError = 0x00,
+    kIdDeviceQuery = 0x01,
+    kIdInitialization = 0x02,
+    kIdFastboot = 0x03
+};
+
+enum Flag : uint8_t {
+    kFlagNone = 0x00,
+    kFlagContinuation = 0x01
+};
+
+// Creates a UDP Transport object using a given Socket. Used for unit tests to create a Transport
+// object that uses a SocketMock.
+std::unique_ptr<Transport> Connect(std::unique_ptr<Socket> sock, std::string* error);
+
+}  // namespace internal
+
+}  // namespace udp
+
+#endif  // UDP_H_
diff --git a/fastboot/udp_test.cpp b/fastboot/udp_test.cpp
new file mode 100644
index 0000000..ff8cf0f
--- /dev/null
+++ b/fastboot/udp_test.cpp
@@ -0,0 +1,531 @@
+/*
+ * 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 "udp.h"
+
+#include <gtest/gtest.h>
+
+#include "socket.h"
+#include "socket_mock.h"
+
+using namespace udp;
+using namespace udp::internal;
+
+// Some possible corner case sequence numbers we want to check.
+static const uint16_t kTestSequenceNumbers[] = {0x0000, 0x0001, 0x00FF, 0x0100,
+                                                0x7FFF, 0x8000, 0xFFFF};
+
+// Converts |value| to a binary big-endian string.
+static std::string PacketValue(uint16_t value) {
+    return std::string{static_cast<char>(value >> 8), static_cast<char>(value)};
+}
+
+// Returns an Error packet.
+static std::string ErrorPacket(uint16_t sequence, const std::string& message = "",
+                               char flags = kFlagNone) {
+    return std::string{kIdError, flags} + PacketValue(sequence) + message;
+}
+
+// Returns a Query packet with no data.
+static std::string QueryPacket(uint16_t sequence) {
+    return std::string{kIdDeviceQuery, kFlagNone} + PacketValue(sequence);
+}
+
+// Returns a Query packet with a 2-byte |new_sequence|.
+static std::string QueryPacket(uint16_t sequence, uint16_t new_sequence) {
+    return std::string{kIdDeviceQuery, kFlagNone} + PacketValue(sequence) +
+           PacketValue(new_sequence);
+}
+
+// Returns an Init packet with a 2-byte |version| and |max_packet_size|.
+static std::string InitPacket(uint16_t sequence, uint16_t version, uint16_t max_packet_size) {
+    return std::string{kIdInitialization, kFlagNone} + PacketValue(sequence) +
+           PacketValue(version) + PacketValue(max_packet_size);
+}
+
+// Returns a Fastboot packet with |data|.
+static std::string FastbootPacket(uint16_t sequence, const std::string& data = "",
+                                  char flags = kFlagNone) {
+    return std::string{kIdFastboot, flags} + PacketValue(sequence) + data;
+}
+
+// Fixture class to test protocol initialization. Usage is to set up the expected calls to the
+// SocketMock object then call UdpConnect() and check the result.
+class UdpConnectTest : public ::testing::Test {
+  public:
+    UdpConnectTest() : mock_socket_(new SocketMock) {}
+
+    // Run the initialization, return whether it was successful or not. This passes ownership of
+    // the current |mock_socket_| but allocates a new one for re-use.
+    bool UdpConnect(std::string* error = nullptr) {
+        std::string local_error;
+        if (error == nullptr) {
+            error = &local_error;
+        }
+        std::unique_ptr<Transport> transport(Connect(std::move(mock_socket_), error));
+        mock_socket_.reset(new SocketMock);
+        return transport != nullptr && error->empty();
+    }
+
+  protected:
+    std::unique_ptr<SocketMock> mock_socket_;
+};
+
+// Tests a successful protocol initialization with various starting sequence numbers.
+TEST_F(UdpConnectTest, InitializationSuccess) {
+    for (uint16_t seq : kTestSequenceNumbers) {
+        mock_socket_->ExpectSend(QueryPacket(0));
+        mock_socket_->AddReceive(QueryPacket(0, seq));
+        mock_socket_->ExpectSend(InitPacket(seq, kProtocolVersion, kHostMaxPacketSize));
+        mock_socket_->AddReceive(InitPacket(seq, kProtocolVersion, 1024));
+
+        EXPECT_TRUE(UdpConnect());
+    }
+}
+
+// Tests continuation packets during initialization.
+TEST_F(UdpConnectTest, InitializationContinuationSuccess) {
+    mock_socket_->ExpectSend(QueryPacket(0));
+    mock_socket_->AddReceive(std::string{kIdDeviceQuery, kFlagContinuation, 0, 0, 0x44});
+    mock_socket_->ExpectSend(std::string{kIdDeviceQuery, kFlagNone, 0, 1});
+    mock_socket_->AddReceive(std::string{kIdDeviceQuery, kFlagNone, 0, 1, 0x55});
+
+    mock_socket_->ExpectSend(InitPacket(0x4455, kProtocolVersion, kHostMaxPacketSize));
+    mock_socket_->AddReceive(std::string{kIdInitialization, kFlagContinuation, 0x44, 0x55, 0});
+    mock_socket_->ExpectSend(std::string{kIdInitialization, kFlagNone, 0x44, 0x56});
+    mock_socket_->AddReceive(std::string{kIdInitialization, kFlagContinuation, 0x44, 0x56, 1});
+    mock_socket_->ExpectSend(std::string{kIdInitialization, kFlagNone, 0x44, 0x57});
+    mock_socket_->AddReceive(std::string{kIdInitialization, kFlagContinuation, 0x44, 0x57, 2});
+    mock_socket_->ExpectSend(std::string{kIdInitialization, kFlagNone, 0x44, 0x58});
+    mock_socket_->AddReceive(std::string{kIdInitialization, kFlagNone, 0x44, 0x58, 0});
+
+    EXPECT_TRUE(UdpConnect());
+}
+
+
+// Tests a mismatched version number; as long as the minimum of the two versions is supported
+// we should allow the connection.
+TEST_F(UdpConnectTest, InitializationVersionMismatch) {
+    mock_socket_->ExpectSend(QueryPacket(0));
+    mock_socket_->AddReceive(QueryPacket(0, 0));
+    mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+    mock_socket_->AddReceive(InitPacket(0, 2, 1024));
+
+    EXPECT_TRUE(UdpConnect());
+
+    mock_socket_->ExpectSend(QueryPacket(0));
+    mock_socket_->AddReceive(QueryPacket(0, 0));
+    mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+    mock_socket_->AddReceive(InitPacket(0, 0, 1024));
+
+    EXPECT_FALSE(UdpConnect());
+}
+
+TEST_F(UdpConnectTest, QueryResponseTimeoutFailure) {
+    for (int i = 0; i < kMaxConnectAttempts; ++i) {
+        mock_socket_->ExpectSend(QueryPacket(0));
+        mock_socket_->AddReceiveTimeout();
+    }
+
+    EXPECT_FALSE(UdpConnect());
+}
+
+TEST_F(UdpConnectTest, QueryResponseReceiveFailure) {
+    mock_socket_->ExpectSend(QueryPacket(0));
+    mock_socket_->AddReceiveFailure();
+
+    EXPECT_FALSE(UdpConnect());
+}
+
+TEST_F(UdpConnectTest, InitResponseTimeoutFailure) {
+    mock_socket_->ExpectSend(QueryPacket(0));
+    mock_socket_->AddReceive(QueryPacket(0, 0));
+    for (int i = 0; i < kMaxTransmissionAttempts; ++i) {
+        mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+        mock_socket_->AddReceiveTimeout();
+    }
+
+    EXPECT_FALSE(UdpConnect());
+}
+
+TEST_F(UdpConnectTest, InitResponseReceiveFailure) {
+    mock_socket_->ExpectSend(QueryPacket(0));
+    mock_socket_->AddReceive(QueryPacket(0, 0));
+    mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+    mock_socket_->AddReceiveFailure();
+
+    EXPECT_FALSE(UdpConnect());
+}
+
+// Tests that we can recover up to the maximum number of allowed retries.
+TEST_F(UdpConnectTest, ResponseRecovery) {
+    // The device query packet can recover from up to (kMaxConnectAttempts - 1) timeouts.
+    for (int i = 0; i < kMaxConnectAttempts - 1; ++i) {
+        mock_socket_->ExpectSend(QueryPacket(0));
+        mock_socket_->AddReceiveTimeout();
+    }
+    mock_socket_->ExpectSend(QueryPacket(0));
+    mock_socket_->AddReceive(QueryPacket(0, 0));
+
+    // Subsequent packets try up to (kMaxTransmissionAttempts - 1) times.
+    for (int i = 0; i < kMaxTransmissionAttempts - 1; ++i) {
+        mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+        mock_socket_->AddReceiveTimeout();
+    }
+    mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+    mock_socket_->AddReceive(InitPacket(0, kProtocolVersion, 1024));
+
+    EXPECT_TRUE(UdpConnect());
+}
+
+// Tests that the host can handle receiving additional bytes for forward compatibility.
+TEST_F(UdpConnectTest, ExtraResponseDataSuccess) {
+    mock_socket_->ExpectSend(QueryPacket(0));
+    mock_socket_->AddReceive(QueryPacket(0, 0) + "foo");
+    mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+    mock_socket_->AddReceive(InitPacket(0, kProtocolVersion, 1024) + "bar");
+
+    EXPECT_TRUE(UdpConnect());
+}
+
+// Tests mismatched response sequence numbers. A wrong sequence number is interpreted as a previous
+// retransmission and just ignored so we should be able to recover.
+TEST_F(UdpConnectTest, WrongSequenceRecovery) {
+    mock_socket_->ExpectSend(QueryPacket(0));
+    mock_socket_->AddReceive(QueryPacket(1, 0));
+    mock_socket_->AddReceive(QueryPacket(0, 0));
+
+    mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+    mock_socket_->AddReceive(InitPacket(1, kProtocolVersion, 1024));
+    mock_socket_->AddReceive(InitPacket(0, kProtocolVersion, 1024));
+
+    EXPECT_TRUE(UdpConnect());
+}
+
+// Tests mismatched response IDs. This should also be interpreted as a retransmission and ignored.
+TEST_F(UdpConnectTest, WrongIdRecovery) {
+    mock_socket_->ExpectSend(QueryPacket(0));
+    mock_socket_->AddReceive(FastbootPacket(0));
+    mock_socket_->AddReceive(QueryPacket(0, 0));
+
+    mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+    mock_socket_->AddReceive(FastbootPacket(0));
+    mock_socket_->AddReceive(InitPacket(0, kProtocolVersion, 1024));
+
+    EXPECT_TRUE(UdpConnect());
+}
+
+// Tests an invalid query response. Query responses must have at least 2 bytes of data.
+TEST_F(UdpConnectTest, InvalidQueryResponseFailure) {
+    std::string error;
+
+    mock_socket_->ExpectSend(QueryPacket(0));
+    mock_socket_->AddReceive(QueryPacket(0));
+
+    EXPECT_FALSE(UdpConnect(&error));
+    EXPECT_EQ("invalid query response from target", error);
+
+    mock_socket_->ExpectSend(QueryPacket(0));
+    mock_socket_->AddReceive(QueryPacket(0) + std::string{0x00});
+
+    EXPECT_FALSE(UdpConnect(&error));
+    EXPECT_EQ("invalid query response from target", error);
+}
+
+// Tests an invalid initialization response. Max packet size must be at least 512 bytes.
+TEST_F(UdpConnectTest, InvalidInitResponseFailure) {
+    std::string error;
+
+    mock_socket_->ExpectSend(QueryPacket(0));
+    mock_socket_->AddReceive(QueryPacket(0, 0));
+    mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+    mock_socket_->AddReceive(InitPacket(0, kProtocolVersion, 511));
+
+    EXPECT_FALSE(UdpConnect(&error));
+    EXPECT_EQ("target reported invalid packet size 511", error);
+
+    mock_socket_->ExpectSend(QueryPacket(0));
+    mock_socket_->AddReceive(QueryPacket(0, 0));
+    mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+    mock_socket_->AddReceive(InitPacket(0, 0, 1024));
+
+    EXPECT_FALSE(UdpConnect(&error));
+    EXPECT_EQ("target reported invalid protocol version 0", error);
+}
+
+TEST_F(UdpConnectTest, ErrorResponseFailure) {
+    std::string error;
+
+    mock_socket_->ExpectSend(QueryPacket(0));
+    mock_socket_->AddReceive(ErrorPacket(0, "error1"));
+
+    EXPECT_FALSE(UdpConnect(&error));
+    EXPECT_NE(std::string::npos, error.find("error1"));
+
+    mock_socket_->ExpectSend(QueryPacket(0));
+    mock_socket_->AddReceive(QueryPacket(0, 0));
+    mock_socket_->ExpectSend(InitPacket(0, kProtocolVersion, kHostMaxPacketSize));
+    mock_socket_->AddReceive(ErrorPacket(0, "error2"));
+
+    EXPECT_FALSE(UdpConnect(&error));
+    EXPECT_NE(std::string::npos, error.find("error2"));
+}
+
+// Tests an error response with continuation flag.
+TEST_F(UdpConnectTest, ErrorContinuationFailure) {
+    std::string error;
+
+    mock_socket_->ExpectSend(QueryPacket(0));
+    mock_socket_->AddReceive(ErrorPacket(0, "error1", kFlagContinuation));
+    mock_socket_->ExpectSend(ErrorPacket(1));
+    mock_socket_->AddReceive(ErrorPacket(1, " ", kFlagContinuation));
+    mock_socket_->ExpectSend(ErrorPacket(2));
+    mock_socket_->AddReceive(ErrorPacket(2, "error2"));
+
+    EXPECT_FALSE(UdpConnect(&error));
+    EXPECT_NE(std::string::npos, error.find("error1 error2"));
+}
+
+// Fixture class to test UDP Transport read/write functionality.
+class UdpTest : public ::testing::Test {
+  public:
+    void SetUp() override {
+        // Create |transport_| starting at sequence 0 with 512 byte max packet size. Tests can call
+        // InitializeTransport() again to change settings.
+        ASSERT_TRUE(InitializeTransport(0, 512));
+    }
+
+    // Sets up |mock_socket_| to correctly initialize the protocol and creates |transport_|. This
+    // can be called multiple times in a test if needed.
+    bool InitializeTransport(uint16_t starting_sequence, int device_max_packet_size = 512) {
+        mock_socket_ = new SocketMock;
+        mock_socket_->ExpectSend(QueryPacket(0));
+        mock_socket_->AddReceive(QueryPacket(0, starting_sequence));
+        mock_socket_->ExpectSend(
+                InitPacket(starting_sequence, kProtocolVersion, kHostMaxPacketSize));
+        mock_socket_->AddReceive(
+                InitPacket(starting_sequence, kProtocolVersion, device_max_packet_size));
+
+        std::string error;
+        transport_ = Connect(std::unique_ptr<Socket>(mock_socket_), &error);
+        return transport_ != nullptr && error.empty();
+    }
+
+    // Writes |message| to |transport_|, returns true on success.
+    bool Write(const std::string& message) {
+        return transport_->Write(message.data(), message.length()) ==
+                static_cast<ssize_t>(message.length());
+    }
+
+    // Reads from |transport_|, returns true if it matches |message|.
+    bool Read(const std::string& message) {
+        std::string buffer(message.length(), '\0');
+        return transport_->Read(&buffer[0], buffer.length()) ==
+                static_cast<ssize_t>(message.length()) && buffer == message;
+    }
+
+  protected:
+    // |mock_socket_| is a raw pointer here because we transfer ownership to |transport_| but we
+    // need to retain a pointer to set send and receive expectations.
+    SocketMock* mock_socket_ = nullptr;
+    std::unique_ptr<Transport> transport_;
+};
+
+// Tests sequence behavior with various starting sequence numbers.
+TEST_F(UdpTest, SequenceIncrementCheck) {
+    for (uint16_t seq : kTestSequenceNumbers) {
+        ASSERT_TRUE(InitializeTransport(seq));
+
+        for (int i = 0; i < 10; ++i) {
+            mock_socket_->ExpectSend(FastbootPacket(++seq, "foo"));
+            mock_socket_->AddReceive(FastbootPacket(seq, ""));
+            mock_socket_->ExpectSend(FastbootPacket(++seq, ""));
+            mock_socket_->AddReceive(FastbootPacket(seq, "bar"));
+
+            EXPECT_TRUE(Write("foo"));
+            EXPECT_TRUE(Read("bar"));
+        }
+    }
+}
+
+// Tests sending and receiving a few small packets.
+TEST_F(UdpTest, ReadAndWriteSmallPackets) {
+    mock_socket_->ExpectSend(FastbootPacket(1, "foo"));
+    mock_socket_->AddReceive(FastbootPacket(1, ""));
+    mock_socket_->ExpectSend(FastbootPacket(2, ""));
+    mock_socket_->AddReceive(FastbootPacket(2, "bar"));
+
+    EXPECT_TRUE(Write("foo"));
+    EXPECT_TRUE(Read("bar"));
+
+    mock_socket_->ExpectSend(FastbootPacket(3, "12345 67890"));
+    mock_socket_->AddReceive(FastbootPacket(3));
+    mock_socket_->ExpectSend(FastbootPacket(4, "\x01\x02\x03\x04\x05"));
+    mock_socket_->AddReceive(FastbootPacket(4));
+
+    EXPECT_TRUE(Write("12345 67890"));
+    EXPECT_TRUE(Write("\x01\x02\x03\x04\x05"));
+
+    // Reads are done by sending empty packets.
+    mock_socket_->ExpectSend(FastbootPacket(5));
+    mock_socket_->AddReceive(FastbootPacket(5, "foo bar baz"));
+    mock_socket_->ExpectSend(FastbootPacket(6));
+    mock_socket_->AddReceive(FastbootPacket(6, "\x01\x02\x03\x04\x05"));
+
+    EXPECT_TRUE(Read("foo bar baz"));
+    EXPECT_TRUE(Read("\x01\x02\x03\x04\x05"));
+}
+
+TEST_F(UdpTest, ResponseTimeoutFailure) {
+    for (int i = 0; i < kMaxTransmissionAttempts; ++i) {
+        mock_socket_->ExpectSend(FastbootPacket(1, "foo"));
+        mock_socket_->AddReceiveTimeout();
+    }
+
+    EXPECT_FALSE(Write("foo"));
+}
+
+TEST_F(UdpTest, ResponseReceiveFailure) {
+    mock_socket_->ExpectSend(FastbootPacket(1, "foo"));
+    mock_socket_->AddReceiveFailure();
+
+    EXPECT_FALSE(Write("foo"));
+}
+
+TEST_F(UdpTest, ResponseTimeoutRecovery) {
+    for (int i = 0; i < kMaxTransmissionAttempts - 1; ++i) {
+        mock_socket_->ExpectSend(FastbootPacket(1, "foo"));
+        mock_socket_->AddReceiveTimeout();
+    }
+    mock_socket_->ExpectSend(FastbootPacket(1, "foo"));
+    mock_socket_->AddReceive(FastbootPacket(1, ""));
+
+    EXPECT_TRUE(Write("foo"));
+}
+
+// Tests continuation packets for various max packet sizes.
+// The important part of this test is that regardless of what kind of packet fragmentation happens
+// at the socket layer, a single call to Transport::Read() and Transport::Write() is all the
+// fastboot code needs to do.
+TEST_F(UdpTest, ContinuationPackets) {
+    for (uint16_t max_packet_size : {512, 1024, 1200}) {
+        ASSERT_TRUE(InitializeTransport(0, max_packet_size));
+
+        // Initialize the data we want to send. Use (size - 4) to leave room for the header.
+        size_t max_data_size = max_packet_size - 4;
+        std::string data(max_data_size * 3, '\0');
+        for (size_t i = 0; i < data.length(); ++i) {
+            data[i] = i;
+        }
+        std::string chunks[] = {data.substr(0, max_data_size),
+                                data.substr(max_data_size, max_data_size),
+                                data.substr(max_data_size * 2, max_data_size)};
+
+        // Write data: split into 3 UDP packets, each of which will be ACKed.
+        mock_socket_->ExpectSend(FastbootPacket(1, chunks[0], kFlagContinuation));
+        mock_socket_->AddReceive(FastbootPacket(1));
+        mock_socket_->ExpectSend(FastbootPacket(2, chunks[1], kFlagContinuation));
+        mock_socket_->AddReceive(FastbootPacket(2));
+        mock_socket_->ExpectSend(FastbootPacket(3, chunks[2]));
+        mock_socket_->AddReceive(FastbootPacket(3));
+        EXPECT_TRUE(Write(data));
+
+        // Same thing for reading the data.
+        mock_socket_->ExpectSend(FastbootPacket(4));
+        mock_socket_->AddReceive(FastbootPacket(4, chunks[0], kFlagContinuation));
+        mock_socket_->ExpectSend(FastbootPacket(5));
+        mock_socket_->AddReceive(FastbootPacket(5, chunks[1], kFlagContinuation));
+        mock_socket_->ExpectSend(FastbootPacket(6));
+        mock_socket_->AddReceive(FastbootPacket(6, chunks[2]));
+        EXPECT_TRUE(Read(data));
+    }
+}
+
+// Tests that the continuation bit is respected even if the packet isn't max size.
+TEST_F(UdpTest, SmallContinuationPackets) {
+    mock_socket_->ExpectSend(FastbootPacket(1));
+    mock_socket_->AddReceive(FastbootPacket(1, "foo", kFlagContinuation));
+    mock_socket_->ExpectSend(FastbootPacket(2));
+    mock_socket_->AddReceive(FastbootPacket(2, "bar"));
+
+    EXPECT_TRUE(Read("foobar"));
+}
+
+// Tests receiving an error packet mid-continuation.
+TEST_F(UdpTest, ContinuationPacketError) {
+    mock_socket_->ExpectSend(FastbootPacket(1));
+    mock_socket_->AddReceive(FastbootPacket(1, "foo", kFlagContinuation));
+    mock_socket_->ExpectSend(FastbootPacket(2));
+    mock_socket_->AddReceive(ErrorPacket(2, "test error"));
+
+    EXPECT_FALSE(Read("foo"));
+}
+
+// Tests timeout during a continuation sequence.
+TEST_F(UdpTest, ContinuationTimeoutRecovery) {
+    mock_socket_->ExpectSend(FastbootPacket(1));
+    mock_socket_->AddReceive(FastbootPacket(1, "foo", kFlagContinuation));
+    mock_socket_->ExpectSend(FastbootPacket(2));
+    mock_socket_->AddReceiveTimeout();
+    mock_socket_->ExpectSend(FastbootPacket(2));
+    mock_socket_->AddReceive(FastbootPacket(2, "bar"));
+
+    EXPECT_TRUE(Read("foobar"));
+}
+
+// Tests read overflow returns -1 to indicate the failure.
+TEST_F(UdpTest, MultipleReadPacket) {
+    mock_socket_->ExpectSend(FastbootPacket(1));
+    mock_socket_->AddReceive(FastbootPacket(1, "foobarbaz"));
+
+    char buffer[3];
+    EXPECT_EQ(-1, transport_->Read(buffer, 3));
+}
+
+// Tests that packets arriving out-of-order are ignored.
+TEST_F(UdpTest, IgnoreOutOfOrderPackets) {
+    mock_socket_->ExpectSend(FastbootPacket(1));
+    mock_socket_->AddReceive(FastbootPacket(0, "sequence too low"));
+    mock_socket_->AddReceive(FastbootPacket(2, "sequence too high"));
+    mock_socket_->AddReceive(QueryPacket(1));
+    mock_socket_->AddReceive(FastbootPacket(1, "correct"));
+
+    EXPECT_TRUE(Read("correct"));
+}
+
+// Tests that an error response with the correct sequence number causes immediate failure.
+TEST_F(UdpTest, ErrorResponse) {
+    // Error packets with the wrong sequence number should be ignored like any other packet.
+    mock_socket_->ExpectSend(FastbootPacket(1, "foo"));
+    mock_socket_->AddReceive(ErrorPacket(0, "ignored error"));
+    mock_socket_->AddReceive(FastbootPacket(1));
+
+    EXPECT_TRUE(Write("foo"));
+
+    // Error packets with the correct sequence should abort immediately without retransmission.
+    mock_socket_->ExpectSend(FastbootPacket(2, "foo"));
+    mock_socket_->AddReceive(ErrorPacket(2, "test error"));
+
+    EXPECT_FALSE(Write("foo"));
+}
+
+// Tests that attempting to use a closed transport returns -1 without making any socket calls.
+TEST_F(UdpTest, CloseTransport) {
+    char buffer[32];
+    EXPECT_EQ(0, transport_->Close());
+    EXPECT_EQ(-1, transport_->Write("foo", 3));
+    EXPECT_EQ(-1, transport_->Read(buffer, sizeof(buffer)));
+}
diff --git a/fs_mgr/fs_mgr_format.c b/fs_mgr/fs_mgr_format.c
index c73045d..f8df081 100644
--- a/fs_mgr/fs_mgr_format.c
+++ b/fs_mgr/fs_mgr_format.c
@@ -52,7 +52,7 @@
     info.len = ((off64_t)nr_sec * 512);
 
     /* Use make_ext4fs_internal to avoid wiping an already-wiped partition. */
-    rc = make_ext4fs_internal(fd, NULL, NULL, fs_mnt_point, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL);
+    rc = make_ext4fs_internal(fd, NULL, NULL, fs_mnt_point, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL);
     if (rc) {
         ERROR("make_ext4fs returned %d.\n", rc);
     }
diff --git a/healthd/Android.mk b/healthd/Android.mk
index 27c985c..a4469fc 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -6,6 +6,16 @@
 LOCAL_SRC_FILES := healthd_board_default.cpp
 LOCAL_MODULE := libhealthd.default
 LOCAL_CFLAGS := -Werror
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+include $(BUILD_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := BatteryMonitor.cpp
+LOCAL_MODULE := libbatterymonitor
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_STATIC_LIBRARIES := libutils
 include $(BUILD_STATIC_LIBRARY)
 
 include $(CLEAR_VARS)
@@ -14,7 +24,6 @@
 	healthd.cpp \
 	healthd_mode_android.cpp \
 	healthd_mode_charger.cpp \
-	BatteryMonitor.cpp \
 	BatteryPropertiesRegistrar.cpp
 
 LOCAL_MODULE := healthd
@@ -35,7 +44,7 @@
 
 LOCAL_C_INCLUDES := bootable/recovery
 
-LOCAL_STATIC_LIBRARIES := libbatteryservice libbinder libminui libpng libz libutils libcutils liblog libm libc
+LOCAL_STATIC_LIBRARIES := libbatterymonitor libbatteryservice libbinder libminui libpng libz libutils libcutils liblog libm libc
 
 ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
 LOCAL_STATIC_LIBRARIES += libsuspend
diff --git a/healthd/BatteryMonitor.cpp b/healthd/BatteryMonitor.cpp
index 6aa360b..110ed03 100644
--- a/healthd/BatteryMonitor.cpp
+++ b/healthd/BatteryMonitor.cpp
@@ -16,8 +16,8 @@
 
 #define LOG_TAG "healthd"
 
-#include "healthd.h"
-#include "BatteryMonitor.h"
+#include <healthd/healthd.h>
+#include <healthd/BatteryMonitor.h>
 
 #include <dirent.h>
 #include <errno.h>
@@ -39,6 +39,8 @@
 #define FAKE_BATTERY_CAPACITY 42
 #define FAKE_BATTERY_TEMPERATURE 424
 #define ALWAYS_PLUGGED_CAPACITY 100
+#define MILLION 10000000.0
+#define DEFAULT_VBUS_VOLTAGE 5000000
 
 namespace android {
 
@@ -56,6 +58,29 @@
     return -1;
 }
 
+static void initBatteryProperties(BatteryProperties* props) {
+    props->chargerAcOnline = false;
+    props->chargerUsbOnline = false;
+    props->chargerWirelessOnline = false;
+    props->maxChargingCurrent = 0;
+    props->maxChargingVoltage = 0;
+    props->batteryStatus = BATTERY_STATUS_UNKNOWN;
+    props->batteryHealth = BATTERY_HEALTH_UNKNOWN;
+    props->batteryPresent = false;
+    props->batteryLevel = 0;
+    props->batteryVoltage = 0;
+    props->batteryTemperature = 0;
+    props->batteryCurrent = 0;
+    props->batteryCycleCount = 0;
+    props->batteryFullCharge = 0;
+    props->batteryTechnology.clear();
+}
+
+BatteryMonitor::BatteryMonitor() : mHealthdConfig(nullptr), mBatteryDevicePresent(false),
+    mAlwaysPluggedDevice(false), mBatteryFixedCapacity(0), mBatteryFixedTemperature(0) {
+    initBatteryProperties(&props);
+}
+
 int BatteryMonitor::getBatteryStatus(const char* status) {
     int ret;
     struct sysfsStringEnumMap batteryStatusMap[] = {
@@ -184,12 +209,7 @@
 bool BatteryMonitor::update(void) {
     bool logthis;
 
-    props.chargerAcOnline = false;
-    props.chargerUsbOnline = false;
-    props.chargerWirelessOnline = false;
-    props.batteryStatus = BATTERY_STATUS_UNKNOWN;
-    props.batteryHealth = BATTERY_HEALTH_UNKNOWN;
-    props.maxChargingCurrent = 0;
+    initBatteryProperties(&props);
 
     if (!mHealthdConfig->batteryPresentPath.isEmpty())
         props.batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);
@@ -237,6 +257,7 @@
         props.batteryTechnology = String8(buf);
 
     unsigned int i;
+    double MaxPower = 0;
 
     for (i = 0; i < mChargerNames.size(); i++) {
         String8 path;
@@ -265,11 +286,23 @@
                 path.clear();
                 path.appendFormat("%s/%s/current_max", POWER_SUPPLY_SYSFS_PATH,
                                   mChargerNames[i].string());
-                if (access(path.string(), R_OK) == 0) {
-                    int maxChargingCurrent = getIntField(path);
-                    if (props.maxChargingCurrent < maxChargingCurrent) {
-                        props.maxChargingCurrent = maxChargingCurrent;
-                    }
+                int ChargingCurrent =
+                    (access(path.string(), R_OK) == 0) ? getIntField(path) : 0;
+
+                path.clear();
+                path.appendFormat("%s/%s/voltage_max", POWER_SUPPLY_SYSFS_PATH,
+                                  mChargerNames[i].string());
+
+                int ChargingVoltage =
+                    (access(path.string(), R_OK) == 0) ? getIntField(path) :
+                    DEFAULT_VBUS_VOLTAGE;
+
+                double power = ((double)ChargingCurrent / MILLION) *
+                        ((double)ChargingVoltage / MILLION);
+                if (MaxPower < power) {
+                    props.maxChargingCurrent = ChargingCurrent;
+                    props.maxChargingVoltage = ChargingVoltage;
+                    MaxPower = power;
                 }
             }
         }
@@ -323,6 +356,17 @@
             props.chargerWirelessOnline;
 }
 
+int BatteryMonitor::getChargeStatus() {
+    int result = BATTERY_STATUS_UNKNOWN;
+    if (!mHealthdConfig->batteryStatusPath.isEmpty()) {
+        char buf[128];
+        if (readFromFile(mHealthdConfig->batteryStatusPath, buf, sizeof(buf)) > 0) {
+            result = getBatteryStatus(buf);
+        }
+    }
+    return result;
+}
+
 status_t BatteryMonitor::getProperty(int id, struct BatteryProperty *val) {
     status_t ret = BAD_VALUE;
 
@@ -388,9 +432,10 @@
     int v;
     char vs[128];
 
-    snprintf(vs, sizeof(vs), "ac: %d usb: %d wireless: %d current_max: %d\n",
+    snprintf(vs, sizeof(vs), "ac: %d usb: %d wireless: %d current_max: %d voltage_max: %d\n",
              props.chargerAcOnline, props.chargerUsbOnline,
-             props.chargerWirelessOnline, props.maxChargingCurrent);
+             props.chargerWirelessOnline, props.maxChargingCurrent,
+             props.maxChargingVoltage);
     write(fd, vs, strlen(vs));
     snprintf(vs, sizeof(vs), "status: %d health: %d present: %d\n",
              props.batteryStatus, props.batteryHealth, props.batteryPresent);
diff --git a/healthd/BatteryPropertiesRegistrar.cpp b/healthd/BatteryPropertiesRegistrar.cpp
index d3a89d7..5d1fa52 100644
--- a/healthd/BatteryPropertiesRegistrar.cpp
+++ b/healthd/BatteryPropertiesRegistrar.cpp
@@ -26,7 +26,7 @@
 #include <utils/Mutex.h>
 #include <utils/String16.h>
 
-#include "healthd.h"
+#include <healthd/healthd.h>
 
 namespace android {
 
diff --git a/healthd/healthd.cpp b/healthd/healthd.cpp
index 85888c3..d9ac356 100644
--- a/healthd/healthd.cpp
+++ b/healthd/healthd.cpp
@@ -17,8 +17,8 @@
 #define LOG_TAG "healthd"
 #define KLOG_LEVEL 6
 
-#include "healthd.h"
-#include "BatteryMonitor.h"
+#include <healthd/healthd.h>
+#include <healthd/BatteryMonitor.h>
 
 #include <errno.h>
 #include <libgen.h>
diff --git a/healthd/healthd_board_default.cpp b/healthd/healthd_board_default.cpp
index ed4ddb4..eb55773 100644
--- a/healthd/healthd_board_default.cpp
+++ b/healthd/healthd_board_default.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include <healthd.h>
+#include <healthd/healthd.h>
 
 void healthd_board_init(struct healthd_config*)
 {
diff --git a/healthd/healthd_mode_android.cpp b/healthd/healthd_mode_android.cpp
index 0a64099..323ef52 100644
--- a/healthd/healthd_mode_android.cpp
+++ b/healthd/healthd_mode_android.cpp
@@ -16,7 +16,7 @@
 
 #define LOG_TAG "healthd-android"
 
-#include "healthd.h"
+#include <healthd/healthd.h>
 #include "BatteryPropertiesRegistrar.h"
 
 #include <binder/IPCThreadState.h>
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 46bad4e..5846626 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -46,7 +46,7 @@
 
 #include "minui/minui.h"
 
-#include "healthd.h"
+#include <healthd/healthd.h>
 
 char *locale;
 
diff --git a/healthd/BatteryMonitor.h b/healthd/include/healthd/BatteryMonitor.h
similarity index 95%
rename from healthd/BatteryMonitor.h
rename to healthd/include/healthd/BatteryMonitor.h
index a61171f..440f2e4 100644
--- a/healthd/BatteryMonitor.h
+++ b/healthd/include/healthd/BatteryMonitor.h
@@ -22,7 +22,7 @@
 #include <utils/String8.h>
 #include <utils/Vector.h>
 
-#include "healthd.h"
+#include <healthd/healthd.h>
 
 namespace android {
 
@@ -37,8 +37,10 @@
         ANDROID_POWER_SUPPLY_TYPE_BATTERY
     };
 
+    BatteryMonitor();
     void init(struct healthd_config *hc);
     bool update(void);
+    int getChargeStatus();
     status_t getProperty(int id, struct BatteryProperty *val);
     void dumpState(int fd);
 
diff --git a/healthd/healthd.h b/healthd/include/healthd/healthd.h
similarity index 100%
rename from healthd/healthd.h
rename to healthd/include/healthd/healthd.h
diff --git a/include/cutils/multiuser.h b/include/cutils/multiuser.h
index 635ddb1..7e7f815 100644
--- a/include/cutils/multiuser.h
+++ b/include/cutils/multiuser.h
@@ -26,6 +26,8 @@
 // NOTE: keep in sync with android.os.UserId
 
 #define MULTIUSER_APP_PER_USER_RANGE 100000
+#define MULTIUSER_FIRST_SHARED_APPLICATION_GID 50000
+#define MULTIUSER_FIRST_APPLICATION_UID 10000
 
 typedef uid_t userid_t;
 typedef uid_t appid_t;
@@ -33,6 +35,7 @@
 extern userid_t multiuser_get_user_id(uid_t uid);
 extern appid_t multiuser_get_app_id(uid_t uid);
 extern uid_t multiuser_get_uid(userid_t userId, appid_t appId);
+extern appid_t multiuser_get_shared_app_gid(uid_t uid);
 
 #ifdef __cplusplus
 }
diff --git a/include/log/log.h b/include/log/log.h
index 1bd9165..e606a84 100644
--- a/include/log/log.h
+++ b/include/log/log.h
@@ -484,15 +484,19 @@
  */
 
 /*
- * Event log entry types.  These must match up with the declarations in
- * java/android/android/util/EventLog.java.
+ * Event log entry types.
  */
 typedef enum {
-    EVENT_TYPE_INT      = 0,
-    EVENT_TYPE_LONG     = 1,
-    EVENT_TYPE_STRING   = 2,
-    EVENT_TYPE_LIST     = 3,
-    EVENT_TYPE_FLOAT    = 4,
+    /* Special markers for android_log_list_element type */
+    EVENT_TYPE_LIST_STOP = '\n', /* declare end of list  */
+    EVENT_TYPE_UNKNOWN   = '?',  /* protocol error       */
+
+    /* must match with declaration in java/android/android/util/EventLog.java */
+    EVENT_TYPE_INT       = 0,    /* uint32_t */
+    EVENT_TYPE_LONG      = 1,    /* uint64_t */
+    EVENT_TYPE_STRING    = 2,
+    EVENT_TYPE_LIST      = 3,
+    EVENT_TYPE_FLOAT     = 4,
 } AndroidEventLogType;
 #define sizeof_AndroidEventLogType sizeof(typeof_AndroidEventLogType)
 #define typeof_AndroidEventLogType unsigned char
@@ -522,7 +526,87 @@
 #define LOG_EVENT_STRING(_tag, _value)                                      \
         (void) __android_log_bswrite(_tag, _value);
 #endif
-/* TODO: something for LIST */
+
+typedef enum log_id {
+    LOG_ID_MIN = 0,
+
+#ifndef LINT_RLOG
+    LOG_ID_MAIN = 0,
+#endif
+    LOG_ID_RADIO = 1,
+#ifndef LINT_RLOG
+    LOG_ID_EVENTS = 2,
+    LOG_ID_SYSTEM = 3,
+    LOG_ID_CRASH = 4,
+    LOG_ID_SECURITY = 5,
+    LOG_ID_KERNEL = 6, /* place last, third-parties can not use it */
+#endif
+
+    LOG_ID_MAX
+} log_id_t;
+#define sizeof_log_id_t sizeof(typeof_log_id_t)
+#define typeof_log_id_t unsigned char
+
+/* For manipulating lists of events. */
+
+#define ANDROID_MAX_LIST_NEST_DEPTH 8
+
+/*
+ * The opaque context used to manipulate lists of events.
+ */
+typedef struct android_log_context_internal *android_log_context;
+
+/*
+ * Elements returned when reading a list of events.
+ */
+typedef struct {
+    AndroidEventLogType type;
+    uint16_t complete;
+    uint16_t len;
+    union {
+        int32_t int32;
+        int64_t int64;
+        char *string;
+        float float32;
+    } data;
+} android_log_list_element;
+
+/*
+ * Creates a context associated with an event tag to write elements to
+ * the list of events.
+ */
+android_log_context create_android_logger(uint32_t tag);
+
+/* All lists must be braced by a begin and end call */
+/*
+ * NB: If the first level braces are missing when specifying multiple
+ *     elements, we will manufacturer a list to embrace it for your API
+ *     convenience. For a single element, it will remain solitary.
+ */
+int android_log_write_list_begin(android_log_context ctx);
+int android_log_write_list_end(android_log_context ctx);
+
+int android_log_write_int32(android_log_context ctx, int32_t value);
+int android_log_write_int64(android_log_context ctx, int64_t value);
+int android_log_write_string8(android_log_context ctx, const char *value);
+int android_log_write_string8_len(android_log_context ctx,
+                                  const char *value, size_t maxlen);
+int android_log_write_float32(android_log_context ctx, float value);
+
+/* Submit the composed list context to the specified logger id */
+/* NB: LOG_ID_EVENTS and LOG_ID_SECURITY only valid binary buffers */
+int android_log_write_list(android_log_context ctx, log_id_t id);
+
+/*
+ * Creates a context from a raw buffer representing a list of events to be read.
+ */
+android_log_context create_android_log_parser(const char *msg, size_t len);
+
+android_log_list_element android_log_read_next(android_log_context ctx);
+android_log_list_element android_log_peek_next(android_log_context ctx);
+
+/* Finished with reader or writer context */
+int android_log_destroy(android_log_context *ctx);
 
 /*
  * ===========================================================================
@@ -585,26 +669,6 @@
     (__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE) != 0)
 #endif
 
-typedef enum log_id {
-    LOG_ID_MIN = 0,
-
-#ifndef LINT_RLOG
-    LOG_ID_MAIN = 0,
-#endif
-    LOG_ID_RADIO = 1,
-#ifndef LINT_RLOG
-    LOG_ID_EVENTS = 2,
-    LOG_ID_SYSTEM = 3,
-    LOG_ID_CRASH = 4,
-    LOG_ID_SECURITY = 5,
-    LOG_ID_KERNEL = 6, /* place last, third-parties can not use it */
-#endif
-
-    LOG_ID_MAX
-} log_id_t;
-#define sizeof_log_id_t sizeof(typeof_log_id_t)
-#define typeof_log_id_t unsigned char
-
 /*
  * Use the per-tag properties "log.tag.<tagname>" to generate a runtime
  * result of non-zero to expose a log. prio is ANDROID_LOG_VERBOSE to
diff --git a/include/system/graphics.h b/include/system/graphics.h
index e255614..880cb9f 100644
--- a/include/system/graphics.h
+++ b/include/system/graphics.h
@@ -927,9 +927,9 @@
      * The values are encoded using the full range ([0,255] for 8-bit) for all
      * components.
      */
-    HAL_DATASPACE_SRGB_LINEAR_LEGACY = 0x200,
+    HAL_DATASPACE_SRGB_LINEAR = 0x200, // deprecated, use HAL_DATASPACE_V0_SRGB_LINEAR
 
-    HAL_DATASPACE_SRGB_LINEAR = HAL_DATASPACE_STANDARD_BT709 |
+    HAL_DATASPACE_V0_SRGB_LINEAR = HAL_DATASPACE_STANDARD_BT709 |
             HAL_DATASPACE_TRANSFER_LINEAR | HAL_DATASPACE_RANGE_FULL,
 
 
@@ -946,9 +946,9 @@
      *
      * Use full range and BT.709 standard.
      */
-    HAL_DATASPACE_SRGB_LEGACY = 0x201,
+    HAL_DATASPACE_SRGB = 0x201, // deprecated, use HAL_DATASPACE_V0_SRGB
 
-    HAL_DATASPACE_SRGB = HAL_DATASPACE_STANDARD_BT709 |
+    HAL_DATASPACE_V0_SRGB = HAL_DATASPACE_STANDARD_BT709 |
             HAL_DATASPACE_TRANSFER_SRGB | HAL_DATASPACE_RANGE_FULL,
 
 
@@ -970,9 +970,9 @@
      *
      * Use full range, BT.601 transfer and BT.601_625 standard.
      */
-    HAL_DATASPACE_JFIF_LEGACY = 0x101,
+    HAL_DATASPACE_JFIF = 0x101, // deprecated, use HAL_DATASPACE_V0_JFIF
 
-    HAL_DATASPACE_JFIF = HAL_DATASPACE_STANDARD_BT601_625 |
+    HAL_DATASPACE_V0_JFIF = HAL_DATASPACE_STANDARD_BT601_625 |
             HAL_DATASPACE_TRANSFER_SMPTE_170M | HAL_DATASPACE_RANGE_FULL,
 
     /*
@@ -982,9 +982,9 @@
      *
      * Use limited range, BT.601 transfer and BT.601_625 standard.
      */
-    HAL_DATASPACE_BT601_625_LEGACY = 0x102,
+    HAL_DATASPACE_BT601_625 = 0x102, // deprecated, use HAL_DATASPACE_V0_BT601_625
 
-    HAL_DATASPACE_BT601_625 = HAL_DATASPACE_STANDARD_BT601_625 |
+    HAL_DATASPACE_V0_BT601_625 = HAL_DATASPACE_STANDARD_BT601_625 |
             HAL_DATASPACE_TRANSFER_SMPTE_170M | HAL_DATASPACE_RANGE_LIMITED,
 
 
@@ -995,9 +995,9 @@
      *
      * Use limited range, BT.601 transfer and BT.601_525 standard.
      */
-    HAL_DATASPACE_BT601_525_LEGACY = 0x103,
+    HAL_DATASPACE_BT601_525 = 0x103, // deprecated, use HAL_DATASPACE_V0_BT601_525
 
-    HAL_DATASPACE_BT601_525 = HAL_DATASPACE_STANDARD_BT601_525 |
+    HAL_DATASPACE_V0_BT601_525 = HAL_DATASPACE_STANDARD_BT601_525 |
             HAL_DATASPACE_TRANSFER_SMPTE_170M | HAL_DATASPACE_RANGE_LIMITED,
 
     /*
@@ -1007,9 +1007,9 @@
      *
      * Use limited range, BT.709 transfer and BT.709 standard.
      */
-    HAL_DATASPACE_BT709_LEGACY = 0x104,
+    HAL_DATASPACE_BT709 = 0x104, // deprecated, use HAL_DATASPACE_V0_BT709
 
-    HAL_DATASPACE_BT709 = HAL_DATASPACE_STANDARD_BT709 |
+    HAL_DATASPACE_V0_BT709 = HAL_DATASPACE_STANDARD_BT709 |
             HAL_DATASPACE_TRANSFER_SMPTE_170M | HAL_DATASPACE_RANGE_LIMITED,
 
     /*
diff --git a/include/system/window.h b/include/system/window.h
index 14cce27..1ca093f 100644
--- a/include/system/window.h
+++ b/include/system/window.h
@@ -313,6 +313,7 @@
     NATIVE_WINDOW_SET_BUFFERS_DATASPACE     = 19,
     NATIVE_WINDOW_SET_SURFACE_DAMAGE        = 20,   /* private */
     NATIVE_WINDOW_SET_SINGLE_BUFFER_MODE    = 21,
+    NATIVE_WINDOW_SET_AUTO_REFRESH          = 22,
 };
 
 /* parameter for NATIVE_WINDOW_[API_][DIS]CONNECT */
@@ -964,6 +965,17 @@
             singleBufferMode);
 }
 
+/*
+ * native_window_set_auto_refresh(..., autoRefresh)
+ * Enable/disable auto refresh when in single buffer mode
+ */
+static inline int native_window_set_auto_refresh(
+        struct ANativeWindow* window,
+        bool autoRefresh)
+{
+    return window->perform(window, NATIVE_WINDOW_SET_AUTO_REFRESH, autoRefresh);
+}
+
 __END_DECLS
 
 #endif /* SYSTEM_CORE_INCLUDE_ANDROID_WINDOW_H */
diff --git a/init/util.cpp b/init/util.cpp
index aefdf8f..84b4155 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -102,7 +102,7 @@
                   gid_t gid, const char *socketcon)
 {
     struct sockaddr_un addr;
-    int fd, ret;
+    int fd, ret, savederrno;
     char *filecon;
 
     if (socketcon) {
@@ -140,16 +140,26 @@
     }
 
     ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr));
-    if (ret) {
-        ERROR("Failed to bind socket '%s': %s\n", name, strerror(errno));
-        goto out_unlink;
-    }
+    savederrno = errno;
 
     setfscreatecon(NULL);
     freecon(filecon);
 
-    chown(addr.sun_path, uid, gid);
-    chmod(addr.sun_path, perm);
+    if (ret) {
+        ERROR("Failed to bind socket '%s': %s\n", name, strerror(savederrno));
+        goto out_unlink;
+    }
+
+    ret = lchown(addr.sun_path, uid, gid);
+    if (ret) {
+        ERROR("Failed to lchown socket '%s': %s\n", addr.sun_path, strerror(errno));
+        goto out_unlink;
+    }
+    ret = fchmodat(AT_FDCWD, addr.sun_path, perm, AT_SYMLINK_NOFOLLOW);
+    if (ret) {
+        ERROR("Failed to fchmodat socket '%s': %s\n", addr.sun_path, strerror(errno));
+        goto out_unlink;
+    }
 
     INFO("Created socket '%s' with mode '%o', user '%d', group '%d'\n",
          addr.sun_path, perm, uid, gid);
diff --git a/libcutils/fs_config.c b/libcutils/fs_config.c
index 645edd1..0abfcbf 100644
--- a/libcutils/fs_config.c
+++ b/libcutils/fs_config.c
@@ -118,7 +118,6 @@
     { 00440, AID_ROOT,      AID_SHELL,     0, "system/etc/init.goldfish.rc" },
     { 00550, AID_ROOT,      AID_SHELL,     0, "system/etc/init.goldfish.sh" },
     { 00550, AID_ROOT,      AID_SHELL,     0, "system/etc/init.ril" },
-    { 00550, AID_DHCP,      AID_SHELL,     0, "system/etc/dhcpcd/dhcpcd-run-hooks" },
     { 00555, AID_ROOT,      AID_ROOT,      0, "system/etc/ppp/*" },
     { 00555, AID_ROOT,      AID_ROOT,      0, "system/etc/rc.*" },
     { 00440, AID_ROOT,      AID_ROOT,      0, "system/etc/recovery.img" },
diff --git a/libcutils/multiuser.c b/libcutils/multiuser.c
index 7c74bb8..0f4427b 100644
--- a/libcutils/multiuser.c
+++ b/libcutils/multiuser.c
@@ -27,3 +27,9 @@
 uid_t multiuser_get_uid(userid_t userId, appid_t appId) {
     return userId * MULTIUSER_APP_PER_USER_RANGE + (appId % MULTIUSER_APP_PER_USER_RANGE);
 }
+
+appid_t multiuser_get_shared_app_gid(uid_t id) {
+  return MULTIUSER_FIRST_SHARED_APPLICATION_GID + (id % MULTIUSER_APP_PER_USER_RANGE)
+          - MULTIUSER_FIRST_APPLICATION_UID;
+
+}
diff --git a/libcutils/sched_policy.c b/libcutils/sched_policy.c
index e56af57..1a26695 100644
--- a/libcutils/sched_policy.c
+++ b/libcutils/sched_policy.c
@@ -266,8 +266,8 @@
     policy = _policy(policy);
     pthread_once(&the_once, __initialize);
 
-    int fd;
-    int boost_fd;
+    int fd = -1;
+    int boost_fd = -1;
     switch (policy) {
     case SP_BACKGROUND:
         fd = bg_cpuset_fd;
diff --git a/libcutils/tests/Android.mk b/libcutils/tests/Android.mk
index 4da5ed6..52cf5f4 100644
--- a/libcutils/tests/Android.mk
+++ b/libcutils/tests/Android.mk
@@ -23,8 +23,9 @@
 test_target_only_src_files := \
     MemsetTest.cpp \
     PropertiesTest.cpp \
+    trace-dev_test.cpp \
 
-test_libraries := libcutils liblog
+test_libraries := libcutils liblog libbase
 
 
 #
diff --git a/libcutils/tests/trace-dev_test.cpp b/libcutils/tests/trace-dev_test.cpp
new file mode 100644
index 0000000..edf981b
--- /dev/null
+++ b/libcutils/tests/trace-dev_test.cpp
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android-base/test_utils.h>
+#include <gtest/gtest.h>
+
+#include "../trace-dev.c"
+
+class TraceDevTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    lseek(tmp_file_.fd, 0, SEEK_SET);
+    atrace_marker_fd = tmp_file_.fd;
+  }
+
+  void TearDown() override {
+    atrace_marker_fd = -1;
+  }
+
+  TemporaryFile tmp_file_;
+
+  static std::string MakeName(size_t length) {
+    std::string name;
+    for (size_t i = 0; i < length; i++) {
+      name += '0' + (i % 10);
+    }
+    return name;
+  }
+};
+
+TEST_F(TraceDevTest, atrace_begin_body_normal) {
+  atrace_begin_body("fake_name");
+
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  std::string expected = android::base::StringPrintf("B|%d|fake_name", getpid());
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_begin_body_exact) {
+  std::string expected = android::base::StringPrintf("B|%d|", getpid());
+  std::string name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 1);
+  atrace_begin_body(name.c_str());
+
+  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  expected += name;
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+
+  // Add a single character and verify we get the exact same value as before.
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+  name += '*';
+  atrace_begin_body(name.c_str());
+  EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_begin_body_truncated) {
+  std::string expected = android::base::StringPrintf("B|%d|", getpid());
+  std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+  atrace_begin_body(name.c_str());
+
+  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 1;
+  expected += android::base::StringPrintf("%.*s", expected_len, name.c_str());
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_begin_body_normal) {
+  atrace_async_begin_body("fake_name", 12345);
+
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  std::string expected = android::base::StringPrintf("S|%d|fake_name|12345", getpid());
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_begin_body_exact) {
+  std::string expected = android::base::StringPrintf("S|%d|", getpid());
+  std::string name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 7);
+  atrace_async_begin_body(name.c_str(), 12345);
+
+  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  expected += name + "|12345";
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+
+  // Add a single character and verify we get the exact same value as before.
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+  name += '*';
+  atrace_async_begin_body(name.c_str(), 12345);
+  EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_begin_body_truncated) {
+  std::string expected = android::base::StringPrintf("S|%d|", getpid());
+  std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+  atrace_async_begin_body(name.c_str(), 12345);
+
+  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 7;
+  expected += android::base::StringPrintf("%.*s|12345", expected_len, name.c_str());
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_end_body_normal) {
+  atrace_async_end_body("fake_name", 12345);
+
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  std::string expected = android::base::StringPrintf("F|%d|fake_name|12345", getpid());
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_end_body_exact) {
+  std::string expected = android::base::StringPrintf("F|%d|", getpid());
+  std::string name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 7);
+  atrace_async_end_body(name.c_str(), 12345);
+
+  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  expected += name + "|12345";
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+
+  // Add a single character and verify we get the exact same value as before.
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+  name += '*';
+  atrace_async_end_body(name.c_str(), 12345);
+  EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_async_end_body_truncated) {
+  std::string expected = android::base::StringPrintf("F|%d|", getpid());
+  std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+  atrace_async_end_body(name.c_str(), 12345);
+
+  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 7;
+  expected += android::base::StringPrintf("%.*s|12345", expected_len, name.c_str());
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_int_body_normal) {
+  atrace_int_body("fake_name", 12345);
+
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  std::string expected = android::base::StringPrintf("C|%d|fake_name|12345", getpid());
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_int_body_exact) {
+  std::string expected = android::base::StringPrintf("C|%d|", getpid());
+  std::string name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 7);
+  atrace_int_body(name.c_str(), 12345);
+
+  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  expected += name + "|12345";
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+
+  // Add a single character and verify we get the exact same value as before.
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+  name += '*';
+  atrace_int_body(name.c_str(), 12345);
+  EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_int_body_truncated) {
+  std::string expected = android::base::StringPrintf("C|%d|", getpid());
+  std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+  atrace_int_body(name.c_str(), 12345);
+
+  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 7;
+  expected += android::base::StringPrintf("%.*s|12345", expected_len, name.c_str());
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_int64_body_normal) {
+  atrace_int64_body("fake_name", 17179869183L);
+
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  std::string expected = android::base::StringPrintf("C|%d|fake_name|17179869183", getpid());
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_int64_body_exact) {
+  std::string expected = android::base::StringPrintf("C|%d|", getpid());
+  std::string name = MakeName(ATRACE_MESSAGE_LENGTH - expected.length() - 13);
+  atrace_int64_body(name.c_str(), 17179869183L);
+
+  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  expected += name + "|17179869183";
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+
+  // Add a single character and verify we get the exact same value as before.
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+  name += '*';
+  atrace_int64_body(name.c_str(), 17179869183L);
+  EXPECT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
+
+TEST_F(TraceDevTest, atrace_int64_body_truncated) {
+  std::string expected = android::base::StringPrintf("C|%d|", getpid());
+  std::string name = MakeName(2 * ATRACE_MESSAGE_LENGTH);
+  atrace_int64_body(name.c_str(), 17179869183L);
+
+  ASSERT_EQ(ATRACE_MESSAGE_LENGTH - 1, lseek(atrace_marker_fd, 0, SEEK_CUR));
+  ASSERT_EQ(0, lseek(atrace_marker_fd, 0, SEEK_SET));
+
+  std::string actual;
+  ASSERT_TRUE(android::base::ReadFdToString(atrace_marker_fd, &actual));
+  int expected_len = ATRACE_MESSAGE_LENGTH - expected.length() - 13;
+  expected += android::base::StringPrintf("%.*s|17179869183", expected_len, name.c_str());
+  ASSERT_STREQ(expected.c_str(), actual.c_str());
+}
diff --git a/libcutils/trace-dev.c b/libcutils/trace-dev.c
index f025256..5df1c5a 100644
--- a/libcutils/trace-dev.c
+++ b/libcutils/trace-dev.c
@@ -194,49 +194,47 @@
 void atrace_begin_body(const char* name)
 {
     char buf[ATRACE_MESSAGE_LENGTH];
-    size_t len;
 
-    len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "B|%d|%s", getpid(), name);
+    int len = snprintf(buf, sizeof(buf), "B|%d|%s", getpid(), name);
+    if (len >= (int) sizeof(buf)) {
+        ALOGW("Truncated name in %s: %s\n", __FUNCTION__, name);
+        len = sizeof(buf) - 1;
+    }
     write(atrace_marker_fd, buf, len);
 }
 
+#define WRITE_MSG(format_begin, format_end, pid, name, value) { \
+    char buf[ATRACE_MESSAGE_LENGTH]; \
+    int len = snprintf(buf, sizeof(buf), format_begin "%s" format_end, pid, \
+        name, value); \
+    if (len >= (int) sizeof(buf)) { \
+        /* Given the sizeof(buf), and all of the current format buffers, \
+         * it is impossible for name_len to be < 0 if len >= sizeof(buf). */ \
+        int name_len = strlen(name) - (len - sizeof(buf)) - 1; \
+        /* Truncate the name to make the message fit. */ \
+        ALOGW("Truncated name in %s: %s\n", __FUNCTION__, name); \
+        len = snprintf(buf, sizeof(buf), format_begin "%.*s" format_end, pid, \
+            name_len, name, value); \
+    } \
+    write(atrace_marker_fd, buf, len); \
+}
 
 void atrace_async_begin_body(const char* name, int32_t cookie)
 {
-    char buf[ATRACE_MESSAGE_LENGTH];
-    size_t len;
-
-    len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "S|%d|%s|%" PRId32,
-            getpid(), name, cookie);
-    write(atrace_marker_fd, buf, len);
+    WRITE_MSG("S|%d|", "|%" PRId32, getpid(), name, cookie);
 }
 
 void atrace_async_end_body(const char* name, int32_t cookie)
 {
-    char buf[ATRACE_MESSAGE_LENGTH];
-    size_t len;
-
-    len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "F|%d|%s|%" PRId32,
-            getpid(), name, cookie);
-    write(atrace_marker_fd, buf, len);
+    WRITE_MSG("F|%d|", "|%" PRId32, getpid(), name, cookie);
 }
 
 void atrace_int_body(const char* name, int32_t value)
 {
-    char buf[ATRACE_MESSAGE_LENGTH];
-    size_t len;
-
-    len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "C|%d|%s|%" PRId32,
-            getpid(), name, value);
-    write(atrace_marker_fd, buf, len);
+    WRITE_MSG("C|%d|", "|%" PRId32, getpid(), name, value);
 }
 
 void atrace_int64_body(const char* name, int64_t value)
 {
-    char buf[ATRACE_MESSAGE_LENGTH];
-    size_t len;
-
-    len = snprintf(buf, ATRACE_MESSAGE_LENGTH, "C|%d|%s|%" PRId64,
-            getpid(), name, value);
-    write(atrace_marker_fd, buf, len);
+    WRITE_MSG("C|%d|", "|%" PRId64, getpid(), name, value);
 }
diff --git a/liblog/Android.mk b/liblog/Android.mk
index a183db8..dd5b518 100644
--- a/liblog/Android.mk
+++ b/liblog/Android.mk
@@ -24,10 +24,10 @@
 # so make sure we do not regret hard-coding it as follows:
 liblog_cflags := -DLIBLOG_LOG_TAG=1005
 
-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
-liblog_target_sources += log_read.c
+liblog_sources := logd_write.c log_event_list.c log_event_write.c
+liblog_host_sources := $(liblog_sources) fake_log_device.c event.logtags
+liblog_target_sources := $(liblog_sources) event_tag_map.c
+liblog_target_sources += log_time.cpp log_is_loggable.c logprint.c log_read.c
 
 # Shared and static library for host
 # ========================================================
diff --git a/liblog/fake_log_device.c b/liblog/fake_log_device.c
index cb80ee6..f4e071b 100644
--- a/liblog/fake_log_device.c
+++ b/liblog/fake_log_device.c
@@ -465,13 +465,13 @@
     if (numLines > MAX_LINES)
         numLines = MAX_LINES;
 
-    numVecs = numLines*3;  // 3 iovecs per line.
+    numVecs = numLines * 3;  // 3 iovecs per line.
     if (numVecs > INLINE_VECS) {
         vec = (struct iovec*)malloc(sizeof(struct iovec)*numVecs);
         if (vec == NULL) {
             msg = "LOG: write failed, no memory";
-            numVecs = 3;
-            numLines = 1;
+            numVecs = INLINE_VECS;
+            numLines = numVecs / 3;
             vec = stackVec;
         }
     }
diff --git a/liblog/log_event_list.c b/liblog/log_event_list.c
new file mode 100644
index 0000000..2213f21
--- /dev/null
+++ b/liblog/log_event_list.c
@@ -0,0 +1,527 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <log/log.h>
+#include <log/logger.h>
+
+#define MAX_EVENT_PAYLOAD (LOGGER_ENTRY_MAX_PAYLOAD - sizeof(int32_t))
+
+typedef struct {
+    uint32_t tag;
+    unsigned pos; /* Read/write position into buffer */
+    unsigned count[ANDROID_MAX_LIST_NEST_DEPTH + 1]; /* Number of elements   */
+    unsigned list[ANDROID_MAX_LIST_NEST_DEPTH + 1];  /* pos for list counter */
+    unsigned list_nest_depth;
+    unsigned len; /* Length or raw buffer. */
+    bool overflow;
+    bool list_stop; /* next call decrement list_nest_depth and issue a stop */
+    enum {
+        kAndroidLoggerRead = 1,
+        kAndroidLoggerWrite = 2,
+    } read_write_flag;
+    uint8_t storage[LOGGER_ENTRY_MAX_PAYLOAD];
+} android_log_context_internal;
+
+android_log_context create_android_logger(uint32_t tag) {
+    size_t needed, i;
+    android_log_context_internal *context;
+
+    context = calloc(1, sizeof(android_log_context_internal));
+    if (!context) {
+        return NULL;
+    }
+    context->tag = tag;
+    context->read_write_flag = kAndroidLoggerWrite;
+    needed = sizeof(uint8_t) + sizeof(uint8_t);
+    if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+        context->overflow = true;
+    }
+    /* Everything is a list */
+    context->storage[context->pos + 0] = EVENT_TYPE_LIST;
+    context->list[0] = context->pos + 1;
+    context->pos += needed;
+
+    return (android_log_context)context;
+}
+
+android_log_context create_android_log_parser(const char *msg, size_t len) {
+    android_log_context_internal *context;
+    size_t i;
+
+    context = calloc(1, sizeof(android_log_context_internal));
+    if (!context) {
+        return NULL;
+    }
+    len = (len <= MAX_EVENT_PAYLOAD) ? len : MAX_EVENT_PAYLOAD;
+    context->len = len;
+    memcpy(context->storage, msg, len);
+    context->read_write_flag = kAndroidLoggerRead;
+
+    return (android_log_context)context;
+}
+
+int android_log_destroy(android_log_context *ctx) {
+    android_log_context_internal *context;
+
+    context = (android_log_context_internal *)*ctx;
+    if (!context) {
+        return -EBADF;
+    }
+    memset(context, 0, sizeof(*context));
+    free(context);
+    *ctx = NULL;
+    return 0;
+}
+
+int android_log_write_list_begin(android_log_context ctx) {
+    size_t needed;
+    android_log_context_internal *context;
+
+    context = (android_log_context_internal *)ctx;
+    if (!context ||
+            (kAndroidLoggerWrite != context->read_write_flag)) {
+        return -EBADF;
+    }
+    if (context->list_nest_depth > ANDROID_MAX_LIST_NEST_DEPTH) {
+        context->overflow = true;
+        return -EOVERFLOW;
+    }
+    needed = sizeof(uint8_t) + sizeof(uint8_t);
+    if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+        context->overflow = true;
+        return -EIO;
+    }
+    context->count[context->list_nest_depth]++;
+    context->list_nest_depth++;
+    if (context->list_nest_depth > ANDROID_MAX_LIST_NEST_DEPTH) {
+        context->overflow = true;
+        return -EOVERFLOW;
+    }
+    if (context->overflow) {
+        return -EIO;
+    }
+    context->storage[context->pos + 0] = EVENT_TYPE_LIST;
+    context->storage[context->pos + 1] = 0;
+    context->list[context->list_nest_depth] = context->pos + 1;
+    context->count[context->list_nest_depth] = 0;
+    context->pos += needed;
+    return 0;
+}
+
+static inline void copy4LE(uint8_t *buf, uint32_t val)
+{
+    buf[0] = val & 0xFF;
+    buf[1] = (val >> 8) & 0xFF;
+    buf[2] = (val >> 16) & 0xFF;
+    buf[3] = (val >> 24) & 0xFF;
+}
+
+int android_log_write_int32(android_log_context ctx, int32_t value) {
+    size_t needed;
+    android_log_context_internal *context;
+
+    context = (android_log_context_internal *)ctx;
+    if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+        return -EBADF;
+    }
+    if (context->overflow) {
+        return -EIO;
+    }
+    needed = sizeof(uint8_t) + sizeof(value);
+    if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+        context->overflow = true;
+        return -EIO;
+    }
+    context->count[context->list_nest_depth]++;
+    context->storage[context->pos + 0] = EVENT_TYPE_INT;
+    copy4LE(&context->storage[context->pos + 1], value);
+    context->pos += needed;
+    return 0;
+}
+
+static inline void copy8LE(uint8_t *buf, uint64_t val)
+{
+    buf[0] = val & 0xFF;
+    buf[1] = (val >> 8) & 0xFF;
+    buf[2] = (val >> 16) & 0xFF;
+    buf[3] = (val >> 24) & 0xFF;
+    buf[4] = (val >> 32) & 0xFF;
+    buf[5] = (val >> 40) & 0xFF;
+    buf[6] = (val >> 48) & 0xFF;
+    buf[7] = (val >> 56) & 0xFF;
+}
+
+int android_log_write_int64(android_log_context ctx, int64_t value) {
+    size_t needed;
+    android_log_context_internal *context;
+
+    context = (android_log_context_internal *)ctx;
+    if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+        return -EBADF;
+    }
+    if (context->overflow) {
+        return -EIO;
+    }
+    needed = sizeof(uint8_t) + sizeof(value);
+    if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+        context->overflow = true;
+        return -EIO;
+    }
+    context->count[context->list_nest_depth]++;
+    context->storage[context->pos + 0] = EVENT_TYPE_LONG;
+    copy8LE(&context->storage[context->pos + 1], value);
+    context->pos += needed;
+    return 0;
+}
+
+int android_log_write_string8_len(android_log_context ctx,
+                                  const char *value, size_t maxlen) {
+    size_t needed;
+    ssize_t len;
+    android_log_context_internal *context;
+
+    context = (android_log_context_internal *)ctx;
+    if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+        return -EBADF;
+    }
+    if (context->overflow) {
+        return -EIO;
+    }
+    if (!value) {
+        value = "";
+    }
+    len = strnlen(value, maxlen);
+    needed = sizeof(uint8_t) + sizeof(int32_t) + len;
+    if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+        /* Truncate string for delivery */
+        len = MAX_EVENT_PAYLOAD - context->pos - 1 - sizeof(int32_t);
+        if (len <= 0) {
+            context->overflow = true;
+            return -EIO;
+        }
+    }
+    context->count[context->list_nest_depth]++;
+    context->storage[context->pos + 0] = EVENT_TYPE_STRING;
+    copy4LE(&context->storage[context->pos + 1], len);
+    if (len) {
+        memcpy(&context->storage[context->pos + 5], value, len);
+    }
+    context->pos += needed;
+    return len;
+}
+
+int android_log_write_string8(android_log_context ctx, const char *value) {
+    return android_log_write_string8_len(ctx, value, MAX_EVENT_PAYLOAD);
+}
+
+int android_log_write_float32(android_log_context ctx, float value) {
+    size_t needed;
+    uint32_t ivalue;
+    android_log_context_internal *context;
+
+    context = (android_log_context_internal *)ctx;
+    if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+        return -EBADF;
+    }
+    if (context->overflow) {
+        return -EIO;
+    }
+    needed = sizeof(uint8_t) + sizeof(ivalue);
+    if ((context->pos + needed) > MAX_EVENT_PAYLOAD) {
+        context->overflow = true;
+        return -EIO;
+    }
+    ivalue = *(uint32_t *)&value;
+    context->count[context->list_nest_depth]++;
+    context->storage[context->pos + 0] = EVENT_TYPE_FLOAT;
+    copy4LE(&context->storage[context->pos + 1], ivalue);
+    context->pos += needed;
+    return 0;
+}
+
+int android_log_write_list_end(android_log_context ctx) {
+    android_log_context_internal *context;
+
+    context = (android_log_context_internal *)ctx;
+    if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+        return -EBADF;
+    }
+    if (context->list_nest_depth > ANDROID_MAX_LIST_NEST_DEPTH) {
+        context->overflow = true;
+        context->list_nest_depth--;
+        return -EOVERFLOW;
+    }
+    if (!context->list_nest_depth) {
+        context->overflow = true;
+        return -EOVERFLOW;
+    }
+    if (context->list[context->list_nest_depth] <= 0) {
+        context->list_nest_depth--;
+        context->overflow = true;
+        return -EOVERFLOW;
+    }
+    context->storage[context->list[context->list_nest_depth]] =
+        context->count[context->list_nest_depth];
+    context->list_nest_depth--;
+    return 0;
+}
+
+/*
+ * Logs the list of elements to the event log.
+ */
+int android_log_write_list(android_log_context ctx, log_id_t id) {
+    android_log_context_internal *context;
+    const char *msg;
+    ssize_t len;
+
+    if ((id != LOG_ID_EVENTS) && (id != LOG_ID_SECURITY)) {
+        return -EINVAL;
+    }
+
+    context = (android_log_context_internal *)ctx;
+    if (!context || (kAndroidLoggerWrite != context->read_write_flag)) {
+        return -EBADF;
+    }
+    if (context->list_nest_depth) {
+        return -EIO;
+    }
+    /* NB: if there was overflow, then log is truncated. Nothing reported */
+    context->storage[1] = context->count[0];
+    len = context->len = context->pos;
+    msg = (const char *)context->storage;
+    /* it'snot a list */
+    if (context->count[0] <= 1) {
+        len -= sizeof(uint8_t) + sizeof(uint8_t);
+        if (len < 0) {
+            len = 0;
+        }
+        msg += sizeof(uint8_t) + sizeof(uint8_t);
+    }
+    return (id == LOG_ID_EVENTS) ?
+        __android_log_bwrite(context->tag, msg, len) :
+        __android_log_security_bwrite(context->tag, msg, len);
+}
+
+/*
+ * Extract a 4-byte value from a byte stream.
+ */
+static inline uint32_t get4LE(const uint8_t* src)
+{
+    return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+}
+
+/*
+ * Extract an 8-byte value from a byte stream.
+ */
+static inline uint64_t get8LE(const uint8_t* src)
+{
+    uint32_t low = src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
+    uint32_t high = src[4] | (src[5] << 8) | (src[6] << 16) | (src[7] << 24);
+    return ((uint64_t) high << 32) | (uint64_t) low;
+}
+
+/*
+ * Gets the next element. Parsing errors result in an EVENT_TYPE_UNKNOWN type.
+ * If there is nothing to process, the complete field is set to non-zero. If
+ * an EVENT_TYPE_UNKNOWN type is returned once, and the caller does not check
+ * this and continues to call this function, the behavior is undefined
+ * (although it won't crash).
+ */
+static android_log_list_element android_log_read_next_internal(
+        android_log_context ctx, int peek) {
+    android_log_list_element elem;
+    unsigned pos;
+    android_log_context_internal *context;
+
+    context = (android_log_context_internal *)ctx;
+
+    memset(&elem, 0, sizeof(elem));
+
+    /* Nothing to parse from this context, so return complete. */
+    if (!context || (kAndroidLoggerRead != context->read_write_flag) ||
+            (context->list_nest_depth > ANDROID_MAX_LIST_NEST_DEPTH) ||
+            (context->count[context->list_nest_depth] >=
+                (MAX_EVENT_PAYLOAD / (sizeof(uint8_t) + sizeof(uint8_t))))) {
+        elem.type = EVENT_TYPE_UNKNOWN;
+        if (context &&
+                (context->list_stop ||
+                ((context->list_nest_depth <= ANDROID_MAX_LIST_NEST_DEPTH) &&
+                    !context->count[context->list_nest_depth]))) {
+            elem.type = EVENT_TYPE_LIST_STOP;
+        }
+        elem.complete = true;
+        return elem;
+    }
+
+    /*
+     * Use a different variable to update the position in case this
+     * operation is a "peek".
+     */
+    pos = context->pos;
+    if (context->list_stop) {
+        elem.type = EVENT_TYPE_LIST_STOP;
+        elem.complete = !context->count[0] && (!context->list_nest_depth ||
+            ((context->list_nest_depth == 1) && !context->count[1]));
+        if (!peek) {
+            /* Suck in superfluous stop */
+            if (context->storage[pos] == EVENT_TYPE_LIST_STOP) {
+                context->pos = pos + 1;
+            }
+            if (context->list_nest_depth) {
+                --context->list_nest_depth;
+                if (context->count[context->list_nest_depth]) {
+                    context->list_stop = false;
+                }
+            } else {
+                context->list_stop = false;
+            }
+        }
+        return elem;
+    }
+    if ((pos + 1) > context->len) {
+        elem.type = EVENT_TYPE_UNKNOWN;
+        elem.complete = true;
+        return elem;
+    }
+
+    elem.type = context->storage[pos++];
+    switch ((int)elem.type) {
+    case EVENT_TYPE_FLOAT:
+        /* Rely on union to translate elem.data.int32 into elem.data.float32 */
+        /* FALLTHRU */
+    case EVENT_TYPE_INT:
+        elem.len = sizeof(int32_t);
+        if ((pos + elem.len) > context->len) {
+            elem.type = EVENT_TYPE_UNKNOWN;
+            return elem;
+        }
+        elem.data.int32 = get4LE(&context->storage[pos]);
+        /* common tangeable object suffix */
+        pos += elem.len;
+        elem.complete = !context->list_nest_depth && !context->count[0];
+        if (!peek) {
+            if (!context->count[context->list_nest_depth] ||
+                    !--(context->count[context->list_nest_depth])) {
+                context->list_stop = true;
+            }
+            context->pos = pos;
+        }
+        return elem;
+
+    case EVENT_TYPE_LONG:
+        elem.len = sizeof(int64_t);
+        if ((pos + elem.len) > context->len) {
+            elem.type = EVENT_TYPE_UNKNOWN;
+            return elem;
+        }
+        elem.data.int64 = get8LE(&context->storage[pos]);
+        /* common tangeable object suffix */
+        pos += elem.len;
+        elem.complete = !context->list_nest_depth && !context->count[0];
+        if (!peek) {
+            if (!context->count[context->list_nest_depth] ||
+                    !--(context->count[context->list_nest_depth])) {
+                context->list_stop = true;
+            }
+            context->pos = pos;
+        }
+        return elem;
+
+    case EVENT_TYPE_STRING:
+        if ((pos + sizeof(int32_t)) > context->len) {
+            elem.type = EVENT_TYPE_UNKNOWN;
+            elem.complete = true;
+            return elem;
+        }
+        elem.len = get4LE(&context->storage[pos]);
+        pos += sizeof(int32_t);
+        if ((pos + elem.len) > context->len) {
+            elem.len = context->len - pos; /* truncate string */
+            elem.complete = true;
+            if (!elem.len) {
+                elem.type = EVENT_TYPE_UNKNOWN;
+                return elem;
+            }
+        }
+        elem.data.string = (char *)&context->storage[pos];
+        /* common tangeable object suffix */
+        pos += elem.len;
+        elem.complete = !context->list_nest_depth && !context->count[0];
+        if (!peek) {
+            if (!context->count[context->list_nest_depth] ||
+                    !--(context->count[context->list_nest_depth])) {
+                context->list_stop = true;
+            }
+            context->pos = pos;
+        }
+        return elem;
+
+    case EVENT_TYPE_LIST:
+        if ((pos + sizeof(uint8_t)) > context->len) {
+            elem.type = EVENT_TYPE_UNKNOWN;
+            elem.complete = true;
+            return elem;
+        }
+        elem.complete = context->list_nest_depth >= ANDROID_MAX_LIST_NEST_DEPTH;
+        if (peek) {
+            return elem;
+        }
+        if (context->count[context->list_nest_depth]) {
+            context->count[context->list_nest_depth]--;
+        }
+        context->list_stop = !context->storage[pos];
+        context->list_nest_depth++;
+        if (context->list_nest_depth <= ANDROID_MAX_LIST_NEST_DEPTH) {
+            context->count[context->list_nest_depth] = context->storage[pos];
+        }
+        context->pos = pos + sizeof(uint8_t);
+        return elem;
+
+    case EVENT_TYPE_LIST_STOP: /* Suprise Newline terminates lists. */
+        if (!peek) {
+            context->pos = pos;
+        }
+        elem.type = EVENT_TYPE_UNKNOWN;
+        elem.complete = !context->list_nest_depth;
+        if (context->list_nest_depth > 0) {
+            elem.type = EVENT_TYPE_LIST_STOP;
+            if (!peek) {
+                context->list_nest_depth--;
+            }
+        }
+        return elem;
+
+    default:
+        elem.type = EVENT_TYPE_UNKNOWN;
+        return elem;
+    }
+}
+
+android_log_list_element android_log_read_next(android_log_context ctx) {
+    return android_log_read_next_internal(ctx, 0);
+}
+
+android_log_list_element android_log_peek_next(android_log_context ctx) {
+    return android_log_read_next_internal(ctx, 1);
+}
diff --git a/liblog/log_event_write.c b/liblog/log_event_write.c
index 0bc42d5..ad42edd 100644
--- a/liblog/log_event_write.c
+++ b/liblog/log_event_write.c
@@ -15,74 +15,33 @@
  */
 
 #include <errno.h>
-#include <string.h>
 
 #include <log/log.h>
-#include <log/logger.h>
 
-#define MAX_EVENT_PAYLOAD 512
 #define MAX_SUBTAG_LEN 32
 
-static inline void copy4LE(uint8_t *buf, size_t pos, int val)
+int __android_log_error_write(int tag, const char *subTag, int32_t uid,
+                              const char *data, uint32_t dataLen)
 {
-    buf[pos] = val & 0xFF;
-    buf[pos+1] = (val >> 8) & 0xFF;
-    buf[pos+2] = (val >> 16) & 0xFF;
-    buf[pos+3] = (val >> 24) & 0xFF;
-}
+    int ret = -EINVAL;
 
-int __android_log_error_write(int tag, const char *subTag, int32_t uid, const char *data,
-                              uint32_t dataLen)
-{
-    uint8_t buf[MAX_EVENT_PAYLOAD];
-    size_t pos = 0;
-    uint32_t subTagLen = 0;
-    uint32_t roomLeftForData = 0;
+    if (subTag && (data || !dataLen)) {
+        android_log_context ctx = create_android_logger(tag);
 
-    if ((subTag == NULL) || ((data == NULL) && (dataLen != 0))) return -EINVAL;
-
-    subTagLen = strlen(subTag);
-
-    // Truncate subtags that are too long.
-    subTagLen = subTagLen > MAX_SUBTAG_LEN ? MAX_SUBTAG_LEN : subTagLen;
-
-    // Truncate dataLen if it is too long.
-    roomLeftForData = MAX_EVENT_PAYLOAD -
-            (1 + // EVENT_TYPE_LIST
-             1 + // Number of elements in list
-             1 + // EVENT_TYPE_STRING
-             sizeof(subTagLen) +
-             subTagLen +
-             1 + // EVENT_TYPE_INT
-             sizeof(uid) +
-             1 + // EVENT_TYPE_STRING
-             sizeof(dataLen));
-    dataLen = dataLen > roomLeftForData ? roomLeftForData : dataLen;
-
-    buf[pos++] = EVENT_TYPE_LIST;
-    buf[pos++] = 3; // Number of elements in the list (subTag, uid, data)
-
-    // Write sub tag.
-    buf[pos++] = EVENT_TYPE_STRING;
-    copy4LE(buf, pos, subTagLen);
-    pos += 4;
-    memcpy(&buf[pos], subTag, subTagLen);
-    pos += subTagLen;
-
-    // Write UID.
-    buf[pos++] = EVENT_TYPE_INT;
-    copy4LE(buf, pos, uid);
-    pos += 4;
-
-    // Write data.
-    buf[pos++] = EVENT_TYPE_STRING;
-    copy4LE(buf, pos, dataLen);
-    pos += 4;
-    if (dataLen != 0)
-    {
-        memcpy(&buf[pos], data, dataLen);
-        pos += dataLen;
+        ret = -ENOMEM;
+        if (ctx) {
+            ret = android_log_write_string8_len(ctx, subTag, MAX_SUBTAG_LEN);
+            if (ret >= 0) {
+                ret = android_log_write_int32(ctx, uid);
+                if (ret >= 0) {
+                    ret = android_log_write_string8_len(ctx, data, dataLen);
+                    if (ret >= 0) {
+                        ret = android_log_write_list(ctx, LOG_ID_EVENTS);
+                    }
+                }
+            }
+            android_log_destroy(&ctx);
+        }
     }
-
-    return __android_log_bwrite(tag, buf, pos);
+    return ret;
 }
diff --git a/liblog/log_read.c b/liblog/log_read.c
index 1aff272..fc63d2c 100644
--- a/liblog/log_read.c
+++ b/liblog/log_read.c
@@ -503,10 +503,7 @@
     }
 
     if (logger_list->pid) {
-        n = snprintf(cp, remaining, " pid=%u", logger_list->pid);
-        n = min(n, remaining);
-        remaining -= n;
-        cp += n;
+        snprintf(cp, remaining, " pid=%u", logger_list->pid);
     }
 
     return send_log_msg(NULL, NULL, buf, len);
@@ -657,7 +654,6 @@
         preread_count = 0;
     }
 
-    ret = 0;
     while(1) {
         if (preread_count < sizeof(buf)) {
             ret = TEMP_FAILURE_RETRY(read(logger_list->sock,
@@ -834,7 +830,6 @@
         if (logger_list->pid) {
             ret = snprintf(cp, remaining, " pid=%u", logger_list->pid);
             ret = min(ret, remaining);
-            remaining -= ret;
             cp += ret;
         }
 
@@ -867,7 +862,6 @@
         logger_list->sock = sock;
     }
 
-    ret = 0;
     while(1) {
         memset(log_msg, 0, sizeof(*log_msg));
 
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 8517c9f..3da4815 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013-2014 The Android Open Source Project
+ * Copyright (C) 2013-2016 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -1368,7 +1368,7 @@
     const int TAG = 123456782;
     const char SUBTAG[] = "test-subtag";
     const int UID = -1;
-    const int DATA_LEN = SIZEOF_MAX_PAYLOAD_BUF;
+    const int DATA_LEN = sizeof(max_payload_buf);
     struct logger_list *logger_list;
 
     pid_t pid = getpid();
@@ -1439,9 +1439,9 @@
         }
         eventData += dataLen;
 
-        // 4 bytes for the tag, and 512 bytes for the log since the
-        // max_payload_buf should be truncated.
-        ASSERT_EQ(4 + 512, eventData - original);
+        // 4 bytes for the tag, and max_payload_buf should be truncated.
+        ASSERT_LE(4 + 512, eventData - original);      // worst expectations
+        ASSERT_GT(4 + DATA_LEN, eventData - original); // must be truncated
 
         ++count;
     }
@@ -1672,3 +1672,561 @@
 
     android_logger_list_close(logger_list);
 }
+
+static int is_real_element(int type) {
+    return ((type == EVENT_TYPE_INT) ||
+            (type == EVENT_TYPE_LONG) ||
+            (type == EVENT_TYPE_STRING) ||
+            (type == EVENT_TYPE_FLOAT));
+}
+
+int android_log_buffer_to_string(const char *msg, size_t len,
+                                 char *strOut, size_t strOutLen) {
+    android_log_context context = create_android_log_parser(msg, len);
+    android_log_list_element elem;
+    bool overflow = false;
+    /* Reserve 1 byte for null terminator. */
+    size_t origStrOutLen = strOutLen--;
+
+    if (!context) {
+        return -EBADF;
+    }
+
+    memset(&elem, 0, sizeof(elem));
+
+    size_t outCount;
+
+    do {
+        elem = android_log_read_next(context);
+        switch ((int)elem.type) {
+        case EVENT_TYPE_LIST:
+            if (strOutLen == 0) {
+                overflow = true;
+            } else {
+                *strOut++ = '[';
+                strOutLen--;
+            }
+            break;
+
+        case EVENT_TYPE_LIST_STOP:
+            if (strOutLen == 0) {
+                overflow = true;
+            } else {
+                *strOut++ = ']';
+                strOutLen--;
+            }
+            break;
+
+        case EVENT_TYPE_INT:
+            /*
+             * snprintf also requires room for the null terminator, which
+             * we don't care about  but we have allocated enough room for
+             * that
+             */
+            outCount = snprintf(strOut, strOutLen + 1,
+                                "%" PRId32, elem.data.int32);
+            if (outCount <= strOutLen) {
+                strOut += outCount;
+                strOutLen -= outCount;
+            } else {
+                overflow = true;
+            }
+            break;
+
+        case EVENT_TYPE_LONG:
+            /*
+             * snprintf also requires room for the null terminator, which
+             * we don't care about but we have allocated enough room for
+             * that
+             */
+            outCount = snprintf(strOut, strOutLen + 1,
+                                "%" PRId64, elem.data.int64);
+            if (outCount <= strOutLen) {
+                strOut += outCount;
+                strOutLen -= outCount;
+            } else {
+                overflow = true;
+            }
+            break;
+
+        case EVENT_TYPE_FLOAT:
+            /*
+             * snprintf also requires room for the null terminator, which
+             * we don't care about but we have allocated enough room for
+             * that
+             */
+            outCount = snprintf(strOut, strOutLen + 1, "%f", elem.data.float32);
+            if (outCount <= strOutLen) {
+                strOut += outCount;
+                strOutLen -= outCount;
+            } else {
+                overflow = true;
+            }
+            break;
+
+        default:
+            elem.complete = true;
+            break;
+
+        case EVENT_TYPE_UNKNOWN:
+#if 0 // Ideal purity in the test, we want to complain about UNKNOWN showing up
+            if (elem.complete) {
+                break;
+            }
+#endif
+            elem.data.string = const_cast<char *>("<unknown>");
+            elem.len = strlen(elem.data.string);
+            /* FALLTHRU */
+        case EVENT_TYPE_STRING:
+            if (elem.len <= strOutLen) {
+                memcpy(strOut, elem.data.string, elem.len);
+                strOut += elem.len;
+                strOutLen -= elem.len;
+            } else if (strOutLen > 0) {
+                /* copy what we can */
+                memcpy(strOut, elem.data.string, strOutLen);
+                strOut += strOutLen;
+                strOutLen = 0;
+                overflow = true;
+            }
+            break;
+        }
+
+        if (elem.complete) {
+            break;
+        }
+        /* Determine whether to put a comma or not. */
+        if (!overflow && (is_real_element(elem.type) ||
+                (elem.type == EVENT_TYPE_LIST_STOP))) {
+            android_log_list_element next = android_log_peek_next(context);
+            if (!next.complete && (is_real_element(next.type) ||
+                    (next.type == EVENT_TYPE_LIST))) {
+                if (strOutLen == 0) {
+                    overflow = true;
+                } else {
+                    *strOut++ = ',';
+                    strOutLen--;
+                }
+            }
+        }
+    } while ((elem.type != EVENT_TYPE_UNKNOWN) && !overflow && !elem.complete);
+
+    android_log_destroy(&context);
+
+    if (overflow) {
+        if (strOutLen < origStrOutLen) {
+            /* leave an indicator */
+            *(strOut-1) = '!';
+        } else {
+            /* nothing was written at all */
+            *strOut++ = '!';
+        }
+    }
+    *strOut++ = '\0';
+
+    if ((elem.type == EVENT_TYPE_UNKNOWN) && !elem.complete) {
+        fprintf(stderr, "Binary log entry conversion failed\n");
+        return -EINVAL;
+    }
+
+    return 0;
+}
+
+static const char *event_test_int32(uint32_t tag, size_t &expected_len) {
+    android_log_context ctx;
+
+    EXPECT_TRUE(NULL != (ctx = create_android_logger(tag)));
+    if (!ctx) {
+        return NULL;
+    }
+    EXPECT_LE(0, android_log_write_int32(ctx, 0x40302010));
+    EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS));
+    EXPECT_LE(0, android_log_destroy(&ctx));
+    EXPECT_TRUE(NULL == ctx);
+
+    expected_len = sizeof(uint32_t) +
+                   sizeof(uint8_t) + sizeof(uint32_t);
+
+    return "1076895760";
+}
+
+static const char *event_test_int64(uint32_t tag, size_t &expected_len) {
+    android_log_context ctx;
+
+    EXPECT_TRUE(NULL != (ctx = create_android_logger(tag)));
+    if (!ctx) {
+        return NULL;
+    }
+    EXPECT_LE(0, android_log_write_int64(ctx, 0x8070605040302010));
+    EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS));
+    EXPECT_LE(0, android_log_destroy(&ctx));
+    EXPECT_TRUE(NULL == ctx);
+
+    expected_len = sizeof(uint32_t) +
+                   sizeof(uint8_t) + sizeof(uint64_t);
+
+    return "-9191740941672636400";
+}
+
+static const char *event_test_list_int64(uint32_t tag, size_t &expected_len) {
+    android_log_context ctx;
+
+    EXPECT_TRUE(NULL != (ctx = create_android_logger(tag)));
+    if (!ctx) {
+        return NULL;
+    }
+    EXPECT_LE(0, android_log_write_list_begin(ctx));
+    EXPECT_LE(0, android_log_write_int64(ctx, 0x8070605040302010));
+    EXPECT_LE(0, android_log_write_list_end(ctx));
+    EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS));
+    EXPECT_LE(0, android_log_destroy(&ctx));
+    EXPECT_TRUE(NULL == ctx);
+
+    expected_len = sizeof(uint32_t) +
+                   sizeof(uint8_t) + sizeof(uint8_t) +
+                       sizeof(uint8_t) + sizeof(uint64_t);
+
+    return "[-9191740941672636400]";
+}
+
+static const char *event_test_simple_automagic_list(uint32_t tag, size_t &expected_len) {
+    android_log_context ctx;
+
+    EXPECT_TRUE(NULL != (ctx = create_android_logger(tag)));
+    if (!ctx) {
+        return NULL;
+    }
+    // The convenience API where we allow a simple list to be
+    // created without explicit begin or end calls.
+    EXPECT_LE(0, android_log_write_int32(ctx, 0x40302010));
+    EXPECT_LE(0, android_log_write_int64(ctx, 0x8070605040302010));
+    EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS));
+    EXPECT_LE(0, android_log_destroy(&ctx));
+    EXPECT_TRUE(NULL == ctx);
+
+    expected_len = sizeof(uint32_t) +
+                   sizeof(uint8_t) + sizeof(uint8_t) +
+                       sizeof(uint8_t) + sizeof(uint32_t) +
+                       sizeof(uint8_t) + sizeof(uint64_t);
+
+    return "[1076895760,-9191740941672636400]";
+}
+
+static const char *event_test_list_empty(uint32_t tag, size_t &expected_len) {
+    android_log_context ctx;
+
+    EXPECT_TRUE(NULL != (ctx = create_android_logger(tag)));
+    if (!ctx) {
+        return NULL;
+    }
+    EXPECT_LE(0, android_log_write_list_begin(ctx));
+    EXPECT_LE(0, android_log_write_list_end(ctx));
+    EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS));
+    EXPECT_LE(0, android_log_destroy(&ctx));
+    EXPECT_TRUE(NULL == ctx);
+
+    expected_len = sizeof(uint32_t) +
+                   sizeof(uint8_t) + sizeof(uint8_t);
+
+    return "[]";
+}
+
+static const char *event_test_complex_nested_list(uint32_t tag, size_t &expected_len) {
+    android_log_context ctx;
+
+    EXPECT_TRUE(NULL != (ctx = create_android_logger(tag)));
+    if (!ctx) {
+        return NULL;
+    }
+
+    EXPECT_LE(0, android_log_write_list_begin(ctx)); // [
+    EXPECT_LE(0, android_log_write_int32(ctx, 0x01020304));
+    EXPECT_LE(0, android_log_write_int64(ctx, 0x0102030405060708));
+    EXPECT_LE(0, android_log_write_string8(ctx, "Hello World"));
+    EXPECT_LE(0, android_log_write_list_begin(ctx)); // [
+    EXPECT_LE(0, android_log_write_int32(ctx, 1));
+    EXPECT_LE(0, android_log_write_int32(ctx, 2));
+    EXPECT_LE(0, android_log_write_int32(ctx, 3));
+    EXPECT_LE(0, android_log_write_int32(ctx, 4));
+    EXPECT_LE(0, android_log_write_list_end(ctx));   // ]
+    EXPECT_LE(0, android_log_write_float32(ctx, 1.0102030405060708));
+    EXPECT_LE(0, android_log_write_list_end(ctx));   // ]
+
+    //
+    // This one checks for the automagic list creation because a list
+    // begin and end was missing for it! This is actually an <oops> corner
+    // case, and not the behavior we morally support. The automagic API is to
+    // allow for a simple case of a series of objects in a single list. e.g.
+    //   int32,int32,int32,string -> [int32,int32,int32,string]
+    //
+    EXPECT_LE(0, android_log_write_string8(ctx, "dlroW olleH"));
+
+    EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS));
+    EXPECT_LE(0, android_log_destroy(&ctx));
+    EXPECT_TRUE(NULL == ctx);
+
+    expected_len = sizeof(uint32_t) +
+                   sizeof(uint8_t) + sizeof(uint8_t) +
+                       sizeof(uint8_t) + sizeof(uint8_t) +
+                           sizeof(uint8_t) + sizeof(uint32_t) +
+                           sizeof(uint8_t) + sizeof(uint64_t) +
+                           sizeof(uint8_t) + sizeof(uint32_t) +
+                                             sizeof("Hello World") - 1 +
+                           sizeof(uint8_t) + sizeof(uint8_t) +
+                               4 * (sizeof(uint8_t) + sizeof(uint32_t)) +
+                           sizeof(uint8_t) + sizeof(uint32_t) +
+                       sizeof(uint8_t) + sizeof(uint32_t) +
+                                         sizeof("dlroW olleH") - 1;
+
+    return "[[16909060,72623859790382856,Hello World,[1,2,3,4],1.010203],dlroW olleH]";
+}
+
+static const char *event_test_7_level_prefix(uint32_t tag, size_t &expected_len) {
+    android_log_context ctx;
+
+    EXPECT_TRUE(NULL != (ctx = create_android_logger(tag)));
+    if (!ctx) {
+        return NULL;
+    }
+    EXPECT_LE(0, android_log_write_list_begin(ctx));
+    EXPECT_LE(0, android_log_write_list_begin(ctx));
+    EXPECT_LE(0, android_log_write_list_begin(ctx));
+    EXPECT_LE(0, android_log_write_list_begin(ctx));
+    EXPECT_LE(0, android_log_write_list_begin(ctx));
+    EXPECT_LE(0, android_log_write_list_begin(ctx));
+    EXPECT_LE(0, android_log_write_list_begin(ctx));
+    EXPECT_LE(0, android_log_write_int32(ctx, 1));
+    EXPECT_LE(0, android_log_write_list_end(ctx));
+    EXPECT_LE(0, android_log_write_int32(ctx, 2));
+    EXPECT_LE(0, android_log_write_list_end(ctx));
+    EXPECT_LE(0, android_log_write_int32(ctx, 3));
+    EXPECT_LE(0, android_log_write_list_end(ctx));
+    EXPECT_LE(0, android_log_write_int32(ctx, 4));
+    EXPECT_LE(0, android_log_write_list_end(ctx));
+    EXPECT_LE(0, android_log_write_int32(ctx, 5));
+    EXPECT_LE(0, android_log_write_list_end(ctx));
+    EXPECT_LE(0, android_log_write_int32(ctx, 6));
+    EXPECT_LE(0, android_log_write_list_end(ctx));
+    EXPECT_LE(0, android_log_write_int32(ctx, 7));
+    EXPECT_LE(0, android_log_write_list_end(ctx));
+    EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS));
+    EXPECT_LE(0, android_log_destroy(&ctx));
+    EXPECT_TRUE(NULL == ctx);
+
+    expected_len = sizeof(uint32_t) + 7 *
+      (sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint32_t));
+
+    return "[[[[[[[1],2],3],4],5],6],7]";
+}
+
+static const char *event_test_7_level_suffix(uint32_t tag, size_t &expected_len) {
+    android_log_context ctx;
+
+    EXPECT_TRUE(NULL != (ctx = create_android_logger(tag)));
+    if (!ctx) {
+        return NULL;
+    }
+    EXPECT_LE(0, android_log_write_list_begin(ctx));
+    EXPECT_LE(0, android_log_write_int32(ctx, 1));
+    EXPECT_LE(0, android_log_write_list_begin(ctx));
+    EXPECT_LE(0, android_log_write_int32(ctx, 2));
+    EXPECT_LE(0, android_log_write_list_begin(ctx));
+    EXPECT_LE(0, android_log_write_int32(ctx, 3));
+    EXPECT_LE(0, android_log_write_list_begin(ctx));
+    EXPECT_LE(0, android_log_write_int32(ctx, 4));
+    EXPECT_LE(0, android_log_write_list_begin(ctx));
+    EXPECT_LE(0, android_log_write_int32(ctx, 5));
+    EXPECT_LE(0, android_log_write_list_begin(ctx));
+    EXPECT_LE(0, android_log_write_int32(ctx, 6));
+    EXPECT_LE(0, android_log_write_list_end(ctx));
+    EXPECT_LE(0, android_log_write_list_end(ctx));
+    EXPECT_LE(0, android_log_write_list_end(ctx));
+    EXPECT_LE(0, android_log_write_list_end(ctx));
+    EXPECT_LE(0, android_log_write_list_end(ctx));
+    EXPECT_LE(0, android_log_write_list_end(ctx));
+    EXPECT_LE(0, android_log_write_list(ctx, LOG_ID_EVENTS));
+    EXPECT_LE(0, android_log_destroy(&ctx));
+    EXPECT_TRUE(NULL == ctx);
+
+    expected_len = sizeof(uint32_t) + 6 *
+      (sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint8_t) + sizeof(uint32_t));
+
+    return "[1,[2,[3,[4,[5,[6]]]]]]";
+}
+
+static const char *event_test_android_log_error_write(uint32_t tag, size_t &expected_len) {
+    EXPECT_LE(0, __android_log_error_write(tag, "Hello World", 42, "dlroW olleH", 11));
+
+    expected_len = sizeof(uint32_t) +
+      sizeof(uint8_t) + sizeof(uint8_t) +
+          sizeof(uint8_t) + sizeof(uint32_t) + sizeof("Hello World") - 1 +
+          sizeof(uint8_t) + sizeof(uint32_t) +
+          sizeof(uint8_t) + sizeof(uint32_t) + sizeof("dlroW olleH") - 1;
+
+    return "[Hello World,42,dlroW olleH]";
+}
+
+static const char *event_test_android_log_error_write_null(uint32_t tag, size_t &expected_len) {
+    EXPECT_LE(0, __android_log_error_write(tag, "Hello World", 42, NULL, 0));
+
+    expected_len = sizeof(uint32_t) +
+      sizeof(uint8_t) + sizeof(uint8_t) +
+          sizeof(uint8_t) + sizeof(uint32_t) + sizeof("Hello World") - 1 +
+          sizeof(uint8_t) + sizeof(uint32_t) +
+          sizeof(uint8_t) + sizeof(uint32_t) + sizeof("") - 1;
+
+    return "[Hello World,42,]";
+}
+
+// make sure all user buffers are flushed
+static void print_barrier() {
+    std::cout.flush();
+    fflush(stdout);
+    std::cerr.flush();
+    fflush(stderr); // everything else is paranoia ...
+}
+
+static void create_android_logger(const char *(*fn)(uint32_t tag, size_t &expected_len)) {
+    struct logger_list *logger_list;
+
+    pid_t pid = getpid();
+
+    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+        LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+
+    log_time ts(android_log_clockid());
+
+    size_t expected_len;
+    const char *expected_string = (*fn)(1005, expected_len);
+
+    if (!expected_string) {
+        android_logger_list_close(logger_list);
+        return;
+    }
+
+    usleep(1000000);
+
+    int count = 0;
+
+    for (;;) {
+        log_msg log_msg;
+        if (android_logger_list_read(logger_list, &log_msg) <= 0) {
+            break;
+        }
+
+        ASSERT_EQ(log_msg.entry.pid, pid);
+
+        if ((log_msg.entry.sec < (ts.tv_sec - 1))
+         || ((ts.tv_sec + 1) < log_msg.entry.sec)
+         || ((size_t)log_msg.entry.len != expected_len)
+         || (log_msg.id() != LOG_ID_EVENTS)) {
+            continue;
+        }
+
+        char *eventData = log_msg.msg();
+
+        ++count;
+
+        AndroidLogFormat *logformat = android_log_format_new();
+        EXPECT_TRUE(NULL != logformat);
+        AndroidLogEntry entry;
+        char msgBuf[1024];
+        int processBinaryLogBuffer = android_log_processBinaryLogBuffer(
+            &log_msg.entry_v1, &entry, NULL, msgBuf, sizeof(msgBuf));
+        EXPECT_EQ(0, processBinaryLogBuffer);
+        if (processBinaryLogBuffer == 0) {
+            print_barrier();
+            int printLogLine = android_log_printLogLine(
+                logformat, fileno(stderr), &entry);
+            print_barrier();
+            EXPECT_EQ(20 + (int)strlen(expected_string), printLogLine);
+        }
+        android_log_format_free(logformat);
+
+        // test buffer reading API
+        snprintf(msgBuf, sizeof(msgBuf), "I/[%d]", get4LE(eventData));
+        print_barrier();
+        fprintf(stderr, "%-10s(%5u): ", msgBuf, pid);
+        memset(msgBuf, 0, sizeof(msgBuf));
+        int buffer_to_string = android_log_buffer_to_string(
+            eventData + sizeof(uint32_t),
+            log_msg.entry.len - sizeof(uint32_t),
+            msgBuf, sizeof(msgBuf));
+        fprintf(stderr, "%s\n", msgBuf);
+        print_barrier();
+        EXPECT_EQ(0, buffer_to_string);
+        EXPECT_EQ(strlen(expected_string), strlen(msgBuf));
+        EXPECT_EQ(0, strcmp(expected_string, msgBuf));
+    }
+
+    EXPECT_EQ(1, count);
+
+    android_logger_list_close(logger_list);
+}
+
+TEST(liblog, create_android_logger_int32) {
+    create_android_logger(event_test_int32);
+}
+
+TEST(liblog, create_android_logger_int64) {
+    create_android_logger(event_test_int64);
+}
+
+TEST(liblog, create_android_logger_list_int64) {
+    create_android_logger(event_test_list_int64);
+}
+
+TEST(liblog, create_android_logger_simple_automagic_list) {
+    create_android_logger(event_test_simple_automagic_list);
+}
+
+TEST(liblog, create_android_logger_list_empty) {
+    create_android_logger(event_test_list_empty);
+}
+
+TEST(liblog, create_android_logger_complex_nested_list) {
+    create_android_logger(event_test_complex_nested_list);
+}
+
+TEST(liblog, create_android_logger_7_level_prefix) {
+    create_android_logger(event_test_7_level_prefix);
+}
+
+TEST(liblog, create_android_logger_7_level_suffix) {
+    create_android_logger(event_test_7_level_suffix);
+}
+
+TEST(liblog, create_android_logger_android_log_error_write) {
+    create_android_logger(event_test_android_log_error_write);
+}
+
+TEST(liblog, create_android_logger_android_log_error_write_null) {
+    create_android_logger(event_test_android_log_error_write_null);
+}
+
+TEST(liblog, create_android_logger_overflow) {
+    android_log_context ctx;
+
+    EXPECT_TRUE(NULL != (ctx = create_android_logger(1005)));
+    if (ctx) {
+        for (size_t i = 0; i < ANDROID_MAX_LIST_NEST_DEPTH; ++i) {
+            EXPECT_LE(0, android_log_write_list_begin(ctx));
+        }
+        EXPECT_GT(0, android_log_write_list_begin(ctx));
+        /* One more for good measure, must be permanently unhappy */
+        EXPECT_GT(0, android_log_write_list_begin(ctx));
+        EXPECT_LE(0, android_log_destroy(&ctx));
+        EXPECT_TRUE(NULL == ctx);
+    }
+
+    ASSERT_TRUE(NULL != (ctx = create_android_logger(1005)));
+    for (size_t i = 0; i < ANDROID_MAX_LIST_NEST_DEPTH; ++i) {
+        EXPECT_LE(0, android_log_write_list_begin(ctx));
+        EXPECT_LE(0, android_log_write_int32(ctx, i));
+    }
+    EXPECT_GT(0, android_log_write_list_begin(ctx));
+    /* One more for good measure, must be permanently unhappy */
+    EXPECT_GT(0, android_log_write_list_begin(ctx));
+    EXPECT_LE(0, android_log_destroy(&ctx));
+    ASSERT_TRUE(NULL == ctx);
+}
diff --git a/libnativeloader/Android.mk b/libnativeloader/Android.mk
index 5e65c4c..6c064c7 100644
--- a/libnativeloader/Android.mk
+++ b/libnativeloader/Android.mk
@@ -17,7 +17,8 @@
 LOCAL_CPPFLAGS := -std=gnu++14 -fvisibility=hidden
 LOCAL_LDFLAGS := -ldl
 LOCAL_MULTILIB := both
-
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
 include $(BUILD_SHARED_LIBRARY)
 
 # Shared library for host
@@ -34,7 +35,8 @@
 LOCAL_CPPFLAGS := -std=gnu++14 -fvisibility=hidden
 LOCAL_LDFLAGS := -ldl
 LOCAL_MULTILIB := both
-
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
 include $(BUILD_HOST_SHARED_LIBRARY)
 
 # Static library for host
@@ -50,5 +52,6 @@
 LOCAL_CPPFLAGS := -std=gnu++14 -fvisibility=hidden
 LOCAL_LDFLAGS := -ldl
 LOCAL_MULTILIB := both
-
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
 include $(BUILD_HOST_STATIC_LIBRARY)
diff --git a/include/nativeloader/native_loader.h b/libnativeloader/include/nativeloader/native_loader.h
similarity index 71%
rename from include/nativeloader/native_loader.h
rename to libnativeloader/include/nativeloader/native_loader.h
index da07253..5644aa6 100644
--- a/include/nativeloader/native_loader.h
+++ b/libnativeloader/include/nativeloader/native_loader.h
@@ -19,14 +19,27 @@
 
 #include "jni.h"
 #include <stdint.h>
+#if defined(__ANDROID__)
+#include <android/dlext.h>
+#endif
 
 namespace android {
 
 __attribute__((visibility("default")))
+void PreloadPublicNativeLibraries();
+
+__attribute__((visibility("default")))
 void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* path,
                         jobject class_loader, bool is_shared, jstring library_path,
                         jstring permitted_path);
 
+#if defined(__ANDROID__)
+// Look up linker namespace by class_loader. Returns nullptr if
+// there is no namespace associated with the class_loader.
+__attribute__((visibility("default")))
+android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader);
+#endif
+
 };  // namespace android
 
 #endif  // NATIVE_BRIDGE_H_
diff --git a/libnativeloader/native_loader.cpp b/libnativeloader/native_loader.cpp
index aaff64c..b763631 100644
--- a/libnativeloader/native_loader.cpp
+++ b/libnativeloader/native_loader.cpp
@@ -36,10 +36,6 @@
 #ifdef __ANDROID__
 // TODO(dimitry): move this to system properties.
 static const char* kPublicNativeLibraries = "libandroid.so:"
-                                            // TODO (dimitry): This is a workaround for http://b/26436837
-                                            // will be removed before the release.
-                                            "libart.so:"
-                                            // END OF WORKAROUND
                                             "libc.so:"
                                             "libcamera2ndk.so:"
                                             "libdl.so:"
@@ -57,19 +53,19 @@
                                             "libOpenSLES.so:"
                                             "libRS.so:"
                                             "libstdc++.so:"
+                                            "libvulkan.so:"
                                             "libwebviewchromium_plat_support.so:"
                                             "libz.so";
 
 class LibraryNamespaces {
  public:
-  LibraryNamespaces() : initialized_(false) {
-    PreloadPublicLibraries();
-  }
+  LibraryNamespaces() : initialized_(false) { }
 
   android_namespace_t* GetOrCreate(JNIEnv* env, jobject class_loader,
                                    bool is_shared,
                                    jstring java_library_path,
-                                   jstring java_permitted_path) {
+                                   jstring java_permitted_path,
+                                   int32_t target_sdk_version) {
     ScopedUtfChars library_path(env, java_library_path);
 
     std::string permitted_path;
@@ -78,16 +74,16 @@
       permitted_path = path.c_str();
     }
 
-    if (!initialized_ && !InitPublicNamespace(library_path.c_str())) {
+    if (!initialized_ && !InitPublicNamespace(library_path.c_str(), target_sdk_version)) {
       return nullptr;
     }
 
     std::lock_guard<std::mutex> guard(mutex_);
 
-    auto it = FindNamespaceByClassLoader(env, class_loader);
+    android_namespace_t* ns = FindNamespaceByClassLoader(env, class_loader);
 
-    if (it != namespaces_.end()) {
-      return it->second;
+    if (ns != nullptr) {
+      return ns;
     }
 
     uint64_t namespace_type = ANDROID_NAMESPACE_TYPE_ISOLATED;
@@ -95,21 +91,27 @@
       namespace_type |= ANDROID_NAMESPACE_TYPE_SHARED;
     }
 
-    android_namespace_t* ns =
-            android_create_namespace("classloader-namespace",
-                                     nullptr,
-                                     library_path.c_str(),
-                                     namespace_type,
-                                     java_permitted_path != nullptr ?
-                                        permitted_path.c_str() :
-                                        nullptr);
+    ns = android_create_namespace("classloader-namespace",
+                                  nullptr,
+                                  library_path.c_str(),
+                                  namespace_type,
+                                  java_permitted_path != nullptr ?
+                                      permitted_path.c_str() :
+                                      nullptr);
 
     namespaces_.push_back(std::make_pair(env->NewWeakGlobalRef(class_loader), ns));
 
     return ns;
   }
 
- private:
+  android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
+    auto it = std::find_if(namespaces_.begin(), namespaces_.end(),
+                [&](const std::pair<jweak, android_namespace_t*>& value) {
+                  return env->IsSameObject(value.first, class_loader);
+                });
+    return it != namespaces_.end() ? it->second : nullptr;
+  }
+
   void PreloadPublicLibraries() {
     // android_init_namespaces() expects all the public libraries
     // to be loaded so that they can be found by soname alone.
@@ -119,22 +121,24 @@
     }
   }
 
-  bool InitPublicNamespace(const char* library_path) {
+ private:
+  bool InitPublicNamespace(const char* library_path, int32_t target_sdk_version) {
     // Some apps call dlopen from generated code unknown to linker in which
     // case linker uses anonymous namespace. See b/25844435 for details.
-    initialized_ = android_init_namespaces(kPublicNativeLibraries, library_path);
+    std::string publicNativeLibraries = kPublicNativeLibraries;
+
+    // TODO (dimitry): This is a workaround for http://b/26436837
+    // will be removed before the release.
+    if (target_sdk_version <= 23) {
+      publicNativeLibraries += ":libart.so";
+    }
+    // END OF WORKAROUND
+
+    initialized_ = android_init_namespaces(publicNativeLibraries.c_str(), library_path);
 
     return initialized_;
   }
 
-  std::vector<std::pair<jweak, android_namespace_t*>>::const_iterator
-  FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
-    return std::find_if(namespaces_.begin(), namespaces_.end(),
-            [&](const std::pair<jweak, android_namespace_t*>& value) {
-              return env->IsSameObject(value.first, class_loader);
-            });
-  }
-
   bool initialized_;
   std::mutex mutex_;
   std::vector<std::pair<jweak, android_namespace_t*>> namespaces_;
@@ -145,6 +149,12 @@
 static LibraryNamespaces* g_namespaces = new LibraryNamespaces;
 #endif
 
+void PreloadPublicNativeLibraries() {
+#if defined(__ANDROID__)
+  g_namespaces->PreloadPublicLibraries();
+#endif
+}
+
 
 void* OpenNativeLibrary(JNIEnv* env, int32_t target_sdk_version, const char* path,
                         jobject class_loader, bool is_shared, jstring java_library_path,
@@ -156,7 +166,7 @@
 
   android_namespace_t* ns =
       g_namespaces->GetOrCreate(env, class_loader, is_shared,
-                                java_library_path, java_permitted_path);
+                                java_library_path, java_permitted_path, target_sdk_version);
 
   if (ns == nullptr) {
     return nullptr;
@@ -174,4 +184,10 @@
 #endif
 }
 
+#if defined(__ANDROID__)
+android_namespace_t* FindNamespaceByClassLoader(JNIEnv* env, jobject class_loader) {
+  return g_namespaces->FindNamespaceByClassLoader(env, class_loader);
+}
+#endif
+
 }; //  android namespace
diff --git a/libnetutils/Android.mk b/libnetutils/Android.mk
index 2060df4..ff899c0 100644
--- a/libnetutils/Android.mk
+++ b/libnetutils/Android.mk
@@ -4,7 +4,6 @@
 LOCAL_SRC_FILES := \
         dhcpclient.c \
         dhcpmsg.c \
-        dhcp_utils.c \
         ifc_utils.c \
         packet.c
 
diff --git a/libnetutils/dhcp_utils.c b/libnetutils/dhcp_utils.c
deleted file mode 100644
index c6b9fe4..0000000
--- a/libnetutils/dhcp_utils.c
+++ /dev/null
@@ -1,372 +0,0 @@
-/*
- * Copyright 2008, 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.
- */
-
-/* Utilities for managing the dhcpcd DHCP client daemon */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <arpa/inet.h>
-#include <netinet/in.h>
-
-#include <cutils/properties.h>
-
-static const char DAEMON_NAME[]        = "dhcpcd";
-static const char DAEMON_PROP_NAME[]   = "init.svc.dhcpcd";
-static const char HOSTNAME_PROP_NAME[] = "net.hostname";
-static const char DHCP_PROP_NAME_PREFIX[]  = "dhcp";
-static const char DHCP_CONFIG_PATH[]   = "/system/etc/dhcpcd/dhcpcd.conf";
-static const int NAP_TIME = 200;   /* wait for 200ms at a time */
-                                  /* when polling for property values */
-static const char DAEMON_NAME_RENEW[]  = "iprenew";
-static char errmsg[100] = "\0";
-/* interface length for dhcpcd daemon start (dhcpcd_<interface> as defined in init.rc file)
- * or for filling up system properties dhcpcd.<interface>.ipaddress, dhcpcd.<interface>.dns1
- * and other properties on a successful bind
- */
-#define MAX_INTERFACE_LENGTH 25
-
-/*
- * P2p interface names increase sequentially p2p-p2p0-1, p2p-p2p0-2.. after
- * group formation. This does not work well with system properties which can quickly
- * exhaust or for specifiying a dhcp start target in init which requires
- * interface to be pre-defined in init.rc file.
- *
- * This function returns a common string p2p for all p2p interfaces.
- */
-void get_p2p_interface_replacement(const char *interface, char *p2p_interface) {
-    /* Use p2p for any interface starting with p2p. */
-    if (strncmp(interface, "p2p",3) == 0) {
-        strncpy(p2p_interface, "p2p", MAX_INTERFACE_LENGTH);
-    } else {
-        strncpy(p2p_interface, interface, MAX_INTERFACE_LENGTH);
-    }
-}
-
-/*
- * Wait for a system property to be assigned a specified value.
- * If desired_value is NULL, then just wait for the property to
- * be created with any value. maxwait is the maximum amount of
- * time in seconds to wait before giving up.
- */
-static int wait_for_property(const char *name, const char *desired_value, int maxwait)
-{
-    char value[PROPERTY_VALUE_MAX] = {'\0'};
-    int maxnaps = (maxwait * 1000) / NAP_TIME;
-
-    if (maxnaps < 1) {
-        maxnaps = 1;
-    }
-
-    while (maxnaps-- >= 0) {
-        if (property_get(name, value, NULL)) {
-            if (desired_value == NULL ||
-                    strcmp(value, desired_value) == 0) {
-                return 0;
-            }
-        }
-        if (maxnaps >= 0) {
-            usleep(NAP_TIME * 1000);
-        }
-    }
-    return -1; /* failure */
-}
-
-static int fill_ip_info(const char *interface,
-                     char *ipaddr,
-                     char *gateway,
-                     uint32_t *prefixLength,
-                     char *dns[],
-                     char *server,
-                     uint32_t *lease,
-                     char *vendorInfo,
-                     char *domain,
-                     char *mtu)
-{
-    char prop_name[PROPERTY_KEY_MAX];
-    char prop_value[PROPERTY_VALUE_MAX];
-    /* Interface name after converting p2p0-p2p0-X to p2p to reuse system properties */
-    char p2p_interface[MAX_INTERFACE_LENGTH];
-    int x;
-
-    get_p2p_interface_replacement(interface, p2p_interface);
-
-    snprintf(prop_name, sizeof(prop_name), "%s.%s.ipaddress", DHCP_PROP_NAME_PREFIX, p2p_interface);
-    property_get(prop_name, ipaddr, NULL);
-
-    snprintf(prop_name, sizeof(prop_name), "%s.%s.gateway", DHCP_PROP_NAME_PREFIX, p2p_interface);
-    property_get(prop_name, gateway, NULL);
-
-    snprintf(prop_name, sizeof(prop_name), "%s.%s.server", DHCP_PROP_NAME_PREFIX, p2p_interface);
-    property_get(prop_name, server, NULL);
-
-    //TODO: Handle IPv6 when we change system property usage
-    if (gateway[0] == '\0' || strncmp(gateway, "0.0.0.0", 7) == 0) {
-        //DHCP server is our best bet as gateway
-        strncpy(gateway, server, PROPERTY_VALUE_MAX);
-    }
-
-    snprintf(prop_name, sizeof(prop_name), "%s.%s.mask", DHCP_PROP_NAME_PREFIX, p2p_interface);
-    if (property_get(prop_name, prop_value, NULL)) {
-        int p;
-        // this conversion is v4 only, but this dhcp client is v4 only anyway
-        in_addr_t mask = ntohl(inet_addr(prop_value));
-        // Check netmask is a valid IP address.  ntohl gives NONE response (all 1's) for
-        // non 255.255.255.255 inputs.  if we get that value check if it is legit..
-        if (mask == INADDR_NONE && strcmp(prop_value, "255.255.255.255") != 0) {
-            snprintf(errmsg, sizeof(errmsg), "DHCP gave invalid net mask %s", prop_value);
-            return -1;
-        }
-        for (p = 0; p < 32; p++) {
-            if (mask == 0) break;
-            // check for non-contiguous netmask, e.g., 255.254.255.0
-            if ((mask & 0x80000000) == 0) {
-                snprintf(errmsg, sizeof(errmsg), "DHCP gave invalid net mask %s", prop_value);
-                return -1;
-            }
-            mask = mask << 1;
-        }
-        *prefixLength = p;
-    }
-
-    for (x=0; dns[x] != NULL; x++) {
-        snprintf(prop_name, sizeof(prop_name), "%s.%s.dns%d", DHCP_PROP_NAME_PREFIX, p2p_interface, x+1);
-        property_get(prop_name, dns[x], NULL);
-    }
-
-    snprintf(prop_name, sizeof(prop_name), "%s.%s.leasetime", DHCP_PROP_NAME_PREFIX, p2p_interface);
-    if (property_get(prop_name, prop_value, NULL)) {
-        *lease = atol(prop_value);
-    }
-
-    snprintf(prop_name, sizeof(prop_name), "%s.%s.vendorInfo", DHCP_PROP_NAME_PREFIX,
-            p2p_interface);
-    property_get(prop_name, vendorInfo, NULL);
-
-    snprintf(prop_name, sizeof(prop_name), "%s.%s.domain", DHCP_PROP_NAME_PREFIX,
-            p2p_interface);
-    property_get(prop_name, domain, NULL);
-
-    snprintf(prop_name, sizeof(prop_name), "%s.%s.mtu", DHCP_PROP_NAME_PREFIX,
-            p2p_interface);
-    property_get(prop_name, mtu, NULL);
-
-    return 0;
-}
-
-/*
- * Get any available DHCP results.
- */
-int dhcp_get_results(const char *interface,
-                     char *ipaddr,
-                     char *gateway,
-                     uint32_t *prefixLength,
-                     char *dns[],
-                     char *server,
-                     uint32_t *lease,
-                     char *vendorInfo,
-                     char *domain,
-                     char *mtu)
-{
-    char result_prop_name[PROPERTY_KEY_MAX];
-    char prop_value[PROPERTY_VALUE_MAX];
-
-    /* Interface name after converting p2p0-p2p0-X to p2p to reuse system properties */
-    char p2p_interface[MAX_INTERFACE_LENGTH];
-    get_p2p_interface_replacement(interface, p2p_interface);
-    snprintf(result_prop_name, sizeof(result_prop_name), "%s.%s.result",
-            DHCP_PROP_NAME_PREFIX,
-            p2p_interface);
-
-    memset(prop_value, '\0', PROPERTY_VALUE_MAX);
-    if (!property_get(result_prop_name, prop_value, NULL)) {
-        snprintf(errmsg, sizeof(errmsg), "%s", "DHCP result property was not set");
-        return -1;
-    }
-    if (strcmp(prop_value, "ok") == 0) {
-        if (fill_ip_info(interface, ipaddr, gateway, prefixLength, dns,
-                server, lease, vendorInfo, domain, mtu) == -1) {
-            return -1;
-        }
-        return 0;
-    } else {
-        snprintf(errmsg, sizeof(errmsg), "DHCP result was %s", prop_value);
-        return -1;
-    }
-}
-
-/*
- * Start the dhcp client daemon, and wait for it to finish
- * configuring the interface.
- *
- * The device init.rc file needs a corresponding entry for this work.
- *
- * Example:
- * service dhcpcd_<interface> /system/bin/dhcpcd -ABKL -f dhcpcd.conf
- */
-int dhcp_start(const char *interface)
-{
-    char result_prop_name[PROPERTY_KEY_MAX];
-    char daemon_prop_name[PROPERTY_KEY_MAX];
-    char prop_value[PROPERTY_VALUE_MAX] = {'\0'};
-    char daemon_cmd[PROPERTY_VALUE_MAX * 2 + sizeof(DHCP_CONFIG_PATH)];
-    const char *ctrl_prop = "ctl.start";
-    const char *desired_status = "running";
-    /* Interface name after converting p2p0-p2p0-X to p2p to reuse system properties */
-    char p2p_interface[MAX_INTERFACE_LENGTH];
-
-    get_p2p_interface_replacement(interface, p2p_interface);
-
-    snprintf(result_prop_name, sizeof(result_prop_name), "%s.%s.result",
-            DHCP_PROP_NAME_PREFIX,
-            p2p_interface);
-
-    snprintf(daemon_prop_name, sizeof(daemon_prop_name), "%s_%s",
-            DAEMON_PROP_NAME,
-            p2p_interface);
-
-    /* Erase any previous setting of the dhcp result property */
-    property_set(result_prop_name, "");
-
-    /* Start the daemon and wait until it's ready */
-    if (property_get(HOSTNAME_PROP_NAME, prop_value, NULL) && (prop_value[0] != '\0'))
-        snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:-f %s -h %s %s", DAEMON_NAME,
-                 p2p_interface, DHCP_CONFIG_PATH, prop_value, interface);
-    else
-        snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:-f %s %s", DAEMON_NAME,
-                 p2p_interface, DHCP_CONFIG_PATH, interface);
-    memset(prop_value, '\0', PROPERTY_VALUE_MAX);
-    property_set(ctrl_prop, daemon_cmd);
-    if (wait_for_property(daemon_prop_name, desired_status, 10) < 0) {
-        snprintf(errmsg, sizeof(errmsg), "%s", "Timed out waiting for dhcpcd to start");
-        return -1;
-    }
-
-    /* Wait for the daemon to return a result */
-    if (wait_for_property(result_prop_name, NULL, 30) < 0) {
-        snprintf(errmsg, sizeof(errmsg), "%s", "Timed out waiting for DHCP to finish");
-        return -1;
-    }
-
-    return 0;
-}
-
-/**
- * Stop the DHCP client daemon.
- */
-int dhcp_stop(const char *interface)
-{
-    char result_prop_name[PROPERTY_KEY_MAX];
-    char daemon_prop_name[PROPERTY_KEY_MAX];
-    char daemon_cmd[PROPERTY_VALUE_MAX * 2];
-    const char *ctrl_prop = "ctl.stop";
-    const char *desired_status = "stopped";
-
-    char p2p_interface[MAX_INTERFACE_LENGTH];
-
-    get_p2p_interface_replacement(interface, p2p_interface);
-
-    snprintf(result_prop_name, sizeof(result_prop_name), "%s.%s.result",
-            DHCP_PROP_NAME_PREFIX,
-            p2p_interface);
-
-    snprintf(daemon_prop_name, sizeof(daemon_prop_name), "%s_%s",
-            DAEMON_PROP_NAME,
-            p2p_interface);
-
-    snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s", DAEMON_NAME, p2p_interface);
-
-    /* Stop the daemon and wait until it's reported to be stopped */
-    property_set(ctrl_prop, daemon_cmd);
-    if (wait_for_property(daemon_prop_name, desired_status, 5) < 0) {
-        return -1;
-    }
-    property_set(result_prop_name, "failed");
-    return 0;
-}
-
-/**
- * Release the current DHCP client lease.
- */
-int dhcp_release_lease(const char *interface)
-{
-    char daemon_prop_name[PROPERTY_KEY_MAX];
-    char daemon_cmd[PROPERTY_VALUE_MAX * 2];
-    const char *ctrl_prop = "ctl.stop";
-    const char *desired_status = "stopped";
-
-    char p2p_interface[MAX_INTERFACE_LENGTH];
-
-    get_p2p_interface_replacement(interface, p2p_interface);
-
-    snprintf(daemon_prop_name, sizeof(daemon_prop_name), "%s_%s",
-            DAEMON_PROP_NAME,
-            p2p_interface);
-
-    snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s", DAEMON_NAME, p2p_interface);
-
-    /* Stop the daemon and wait until it's reported to be stopped */
-    property_set(ctrl_prop, daemon_cmd);
-    if (wait_for_property(daemon_prop_name, desired_status, 5) < 0) {
-        return -1;
-    }
-    return 0;
-}
-
-char *dhcp_get_errmsg() {
-    return errmsg;
-}
-
-/**
- * The device init.rc file needs a corresponding entry.
- *
- * Example:
- * service iprenew_<interface> /system/bin/dhcpcd -n
- *
- */
-int dhcp_start_renew(const char *interface)
-{
-    char result_prop_name[PROPERTY_KEY_MAX];
-    char prop_value[PROPERTY_VALUE_MAX] = {'\0'};
-    char daemon_cmd[PROPERTY_VALUE_MAX * 2];
-    const char *ctrl_prop = "ctl.start";
-
-    char p2p_interface[MAX_INTERFACE_LENGTH];
-
-    get_p2p_interface_replacement(interface, p2p_interface);
-
-    snprintf(result_prop_name, sizeof(result_prop_name), "%s.%s.result",
-            DHCP_PROP_NAME_PREFIX,
-            p2p_interface);
-
-    /* Erase any previous setting of the dhcp result property */
-    property_set(result_prop_name, "");
-
-    /* Start the renew daemon and wait until it's ready */
-    snprintf(daemon_cmd, sizeof(daemon_cmd), "%s_%s:%s", DAEMON_NAME_RENEW,
-            p2p_interface, interface);
-    memset(prop_value, '\0', PROPERTY_VALUE_MAX);
-    property_set(ctrl_prop, daemon_cmd);
-
-    /* Wait for the daemon to return a result */
-    if (wait_for_property(result_prop_name, NULL, 30) < 0) {
-        snprintf(errmsg, sizeof(errmsg), "%s", "Timed out waiting for DHCP Renew to finish");
-        return -1;
-    }
-
-    return 0;
-}
diff --git a/libutils/Android.mk b/libutils/Android.mk
index 8c4fd15..3584b6a 100644
--- a/libutils/Android.mk
+++ b/libutils/Android.mk
@@ -72,7 +72,7 @@
 ifeq ($(TARGET_ARCH),mips)
 LOCAL_CFLAGS += -DALIGN_DOUBLE
 endif
-LOCAL_CFLAGS += -Werror
+LOCAL_CFLAGS += -Werror -fvisibility=protected
 
 LOCAL_STATIC_LIBRARIES := \
 	libcutils \
diff --git a/logd/Android.mk b/logd/Android.mk
index feca8d5..203943c 100644
--- a/logd/Android.mk
+++ b/logd/Android.mk
@@ -42,6 +42,10 @@
 
 LOCAL_CFLAGS := -Werror $(event_flag)
 
+ifeq ($(TARGET_BUILD_VARIANT),user)
+LOCAL_CFLAGS += -DAUDITD_ENFORCE_INTEGRITY=true
+endif
+
 include $(BUILD_EXECUTABLE)
 
 include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/logd/FlushCommand.cpp b/logd/FlushCommand.cpp
index fd45c4a0..6a26d00 100644
--- a/logd/FlushCommand.cpp
+++ b/logd/FlushCommand.cpp
@@ -16,7 +16,10 @@
 
 #include <stdlib.h>
 
+#include <private/android_filesystem_config.h>
+
 #include "FlushCommand.h"
+#include "LogBuffer.h"
 #include "LogBufferElement.h"
 #include "LogCommand.h"
 #include "LogReader.h"
diff --git a/logd/LogAudit.cpp b/logd/LogAudit.cpp
index 143fb04..230dd11 100644
--- a/logd/LogAudit.cpp
+++ b/logd/LogAudit.cpp
@@ -20,17 +20,27 @@
 #include <limits.h>
 #include <stdarg.h>
 #include <stdlib.h>
+#include <string.h>
 #include <sys/prctl.h>
 #include <sys/uio.h>
 #include <syslog.h>
 
+#include <string>
+
+#include <cutils/properties.h>
 #include <log/logger.h>
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
 
 #include "libaudit.h"
 #include "LogAudit.h"
+#include "LogBuffer.h"
 #include "LogKlog.h"
+#include "LogReader.h"
+
+#ifndef AUDITD_ENFORCE_INTEGRITY
+#define AUDITD_ENFORCE_INTEGRITY false
+#endif
 
 #define KMSG_PRIORITY(PRI)                          \
     '<',                                            \
@@ -43,11 +53,10 @@
         logbuf(buf),
         reader(reader),
         fdDmesg(fdDmesg),
+        policyLoaded(false),
+        rebootToSafeMode(false),
         initialized(false) {
-    static const char auditd_message[] = { KMSG_PRIORITY(LOG_INFO),
-        'l', 'o', 'g', 'd', '.', 'a', 'u', 'd', 'i', 't', 'd', ':',
-        ' ', 's', 't', 'a', 'r', 't', '\n' };
-    write(fdDmesg, auditd_message, sizeof(auditd_message));
+    logToDmesg("start");
 }
 
 bool LogAudit::onDataAvailable(SocketClient *cli) {
@@ -73,6 +82,46 @@
     return true;
 }
 
+void LogAudit::logToDmesg(const std::string& str)
+{
+    static const char prefix[] = { KMSG_PRIORITY(LOG_INFO),
+        'l', 'o', 'g', 'd', '.', 'a', 'u', 'd', 'i', 't', 'd', ':',
+        ' ', '\0' };
+    std::string message = prefix + str + "\n";
+    write(fdDmesg, message.c_str(), message.length());
+}
+
+std::string LogAudit::getProperty(const std::string& name)
+{
+    char value[PROP_VALUE_MAX] = {0};
+    property_get(name.c_str(), value, "");
+    return value;
+}
+
+void LogAudit::enforceIntegrity() {
+    if (!AUDITD_ENFORCE_INTEGRITY) {
+        logToDmesg("integrity enforcement suppressed; not rebooting");
+    } else if (rebootToSafeMode) {
+        if (getProperty("persist.sys.safemode") == "1") {
+            logToDmesg("integrity enforcement suppressed; in safe mode");
+            return;
+        }
+
+        logToDmesg("enforcing integrity; rebooting to safe mode");
+        property_set("persist.sys.safemode", "1");
+
+        std::string buildDate = getProperty("ro.build.date.utc");
+        if (!buildDate.empty()) {
+            property_set("persist.sys.audit_safemode", buildDate.c_str());
+        }
+
+        property_set("sys.powerctl", "reboot");
+    } else {
+        logToDmesg("enforcing integrity: rebooting to recovery");
+        property_set("sys.powerctl", "reboot,recovery");
+    }
+}
+
 int LogAudit::logPrint(const char *fmt, ...) {
     if (fmt == NULL) {
         return -EINVAL;
@@ -94,7 +143,27 @@
         memmove(cp, cp + 1, strlen(cp + 1) + 1);
     }
 
-    bool info = strstr(str, " permissive=1") || strstr(str, " policy loaded ");
+    bool loaded = strstr(str, " policy loaded ");
+
+    if (loaded) {
+        if (policyLoaded) {
+            // SELinux policy changes are not allowed
+            enforceIntegrity();
+        } else {
+            logToDmesg("policy loaded");
+            policyLoaded = true;
+        }
+    }
+
+    bool permissive = strstr(str, " enforcing=0") ||
+                      strstr(str, " permissive=1");
+
+    if (permissive) {
+        // SELinux in permissive mode is not allowed
+        enforceIntegrity();
+    }
+
+    bool info = loaded || permissive;
     if ((fdDmesg >= 0) && initialized) {
         struct iovec iov[3];
         static const char log_info[] = { KMSG_PRIORITY(LOG_INFO) };
diff --git a/logd/LogAudit.h b/logd/LogAudit.h
index 8a82630..3a84541 100644
--- a/logd/LogAudit.h
+++ b/logd/LogAudit.h
@@ -18,18 +18,24 @@
 #define _LOGD_LOG_AUDIT_H__
 
 #include <sysutils/SocketListener.h>
-#include "LogReader.h"
+
+#include "LogBuffer.h"
+
+class LogReader;
 
 class LogAudit : public SocketListener {
     LogBuffer *logbuf;
     LogReader *reader;
     int fdDmesg;
+    bool policyLoaded;
+    bool rebootToSafeMode;
     bool initialized;
 
 public:
     LogAudit(LogBuffer *buf, LogReader *reader, int fdDmesg);
     int log(char *buf, size_t len);
     bool isMonotonic() { return logbuf->isMonotonic(); }
+    void allowSafeMode(bool allow = true) { rebootToSafeMode = allow; }
 
 protected:
     virtual bool onDataAvailable(SocketClient *cli);
@@ -38,6 +44,9 @@
     static int getLogSocket();
     int logPrint(const char *fmt, ...)
         __attribute__ ((__format__ (__printf__, 2, 3)));
+    void logToDmesg(const std::string& str);
+    std::string getProperty(const std::string& name);
+    void enforceIntegrity();
 };
 
 #endif
diff --git a/logd/LogBufferElement.cpp b/logd/LogBufferElement.cpp
index fde9ad7..eb5194c 100644
--- a/logd/LogBufferElement.cpp
+++ b/logd/LogBufferElement.cpp
@@ -25,9 +25,11 @@
 #include <log/logger.h>
 #include <private/android_logger.h>
 
+#include "LogBuffer.h"
 #include "LogBufferElement.h"
 #include "LogCommand.h"
 #include "LogReader.h"
+#include "LogUtils.h"
 
 const uint64_t LogBufferElement::FLUSH_ERROR(0);
 atomic_int_fast64_t LogBufferElement::sequence(1);
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index 9690489..ac2b128 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -20,13 +20,16 @@
 #include <limits.h>
 #include <stdarg.h>
 #include <stdlib.h>
+#include <string.h>
 #include <sys/prctl.h>
 #include <sys/uio.h>
 #include <syslog.h>
 
 #include <log/logger.h>
 
+#include "LogBuffer.h"
 #include "LogKlog.h"
+#include "LogReader.h"
 
 #define KMSG_PRIORITY(PRI)           \
     '<',                             \
diff --git a/logd/LogKlog.h b/logd/LogKlog.h
index 3c8cc87..ee73b71 100644
--- a/logd/LogKlog.h
+++ b/logd/LogKlog.h
@@ -19,10 +19,12 @@
 
 #include <sysutils/SocketListener.h>
 #include <log/log_read.h>
-#include "LogReader.h"
 
 char *log_strntok_r(char *s, size_t *len, char **saveptr, size_t *sublen);
 
+class LogBuffer;
+class LogReader;
+
 class LogKlog : public SocketListener {
     LogBuffer *logbuf;
     LogReader *reader;
diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp
index 846dd7c..39dd227 100644
--- a/logd/LogListener.cpp
+++ b/logd/LogListener.cpp
@@ -27,6 +27,7 @@
 #include <private/android_filesystem_config.h>
 #include <private/android_logger.h>
 
+#include "LogBuffer.h"
 #include "LogListener.h"
 #include "LogUtils.h"
 
diff --git a/logd/LogReader.cpp b/logd/LogReader.cpp
index 667a3f2..2c07984 100644
--- a/logd/LogReader.cpp
+++ b/logd/LogReader.cpp
@@ -18,11 +18,15 @@
 #include <poll.h>
 #include <sys/prctl.h>
 #include <sys/socket.h>
+#include <sys/types.h>
 
 #include <cutils/sockets.h>
 
-#include "LogReader.h"
 #include "FlushCommand.h"
+#include "LogBuffer.h"
+#include "LogBufferElement.h"
+#include "LogReader.h"
+#include "LogUtils.h"
 
 LogReader::LogReader(LogBuffer *logbuf) :
         SocketListener(getLogSocket(), true),
@@ -176,6 +180,11 @@
     }
 
     FlushCommand command(*this, nonBlock, tail, logMask, pid, sequence, timeout);
+
+    // Set acceptable upper limit to wait for slow reader processing b/27242723
+    struct timeval t = { LOGD_SNDTIMEO, 0 };
+    setsockopt(cli->getSocket(), SOL_SOCKET, SO_SNDTIMEO, (const char *)&t, sizeof(t));
+
     command.runSocketCommand(cli);
     return true;
 }
diff --git a/logd/LogReader.h b/logd/LogReader.h
index 91559a3..98674b8 100644
--- a/logd/LogReader.h
+++ b/logd/LogReader.h
@@ -18,8 +18,10 @@
 #define _LOGD_LOG_WRITER_H__
 
 #include <sysutils/SocketListener.h>
-#include "LogBuffer.h"
-#include "LogTimes.h"
+
+#define LOGD_SNDTIMEO 32
+
+class LogBuffer;
 
 class LogReader : public SocketListener {
     LogBuffer &mLogbuf;
diff --git a/logd/LogTimes.h b/logd/LogTimes.h
index 1117088..f5969df 100644
--- a/logd/LogTimes.h
+++ b/logd/LogTimes.h
@@ -27,6 +27,7 @@
 #include <log/log.h>
 
 class LogReader;
+class LogBufferElement;
 
 class LogTimeEntry {
     static pthread_mutex_t timesLock;
diff --git a/logd/README.property b/logd/README.property
index 22f86b9..6c84b25 100644
--- a/logd/README.property
+++ b/logd/README.property
@@ -1,7 +1,6 @@
 The properties that logd responds to are:
 
 name                       type default  description
-ro.logd.auditd             bool   true   Enable selinux audit daemon
 ro.logd.auditd.dmesg       bool   true   selinux audit messages duplicated and
                                          sent on to dmesg log
 persist.logd.security      bool   false  Enable security buffer.
diff --git a/logd/main.cpp b/logd/main.cpp
index ba56e57..aa5718e 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -223,6 +223,7 @@
 static sem_t reinit;
 static bool reinit_running = false;
 static LogBuffer *logBuf = NULL;
+static LogAudit *logAudit = NULL;
 
 static bool package_list_parser_cb(pkg_info *info, void * /* userdata */) {
 
@@ -270,6 +271,10 @@
             logBuf->init();
             logBuf->initPrune(NULL);
         }
+
+        if (logAudit) {
+            logAudit->allowSafeMode();
+        }
     }
 
     return NULL;
@@ -490,25 +495,19 @@
     // initiated log messages. New log entries are added to LogBuffer
     // and LogReader is notified to send updates to connected clients.
 
-    bool auditd = property_get_bool("logd.auditd",
-                                    BOOL_DEFAULT_TRUE |
-                                    BOOL_DEFAULT_FLAG_PERSIST);
-    LogAudit *al = NULL;
-    if (auditd) {
-        al = new LogAudit(logBuf, reader,
-                          property_get_bool("logd.auditd.dmesg",
-                                            BOOL_DEFAULT_TRUE |
-                                            BOOL_DEFAULT_FLAG_PERSIST)
-                              ? fdDmesg
-                              : -1);
-    }
+    logAudit = new LogAudit(logBuf, reader,
+                            property_get_bool("logd.auditd.dmesg",
+                                              BOOL_DEFAULT_TRUE |
+                                              BOOL_DEFAULT_FLAG_PERSIST)
+                                ? fdDmesg
+                                : -1);
 
     LogKlog *kl = NULL;
     if (klogd) {
-        kl = new LogKlog(logBuf, reader, fdDmesg, fdPmesg, al != NULL);
+        kl = new LogKlog(logBuf, reader, fdDmesg, fdPmesg, logAudit != NULL);
     }
 
-    readDmesg(al, kl);
+    readDmesg(logAudit, kl);
 
     // failure is an option ... messages are in dmesg (required by standard)
 
@@ -516,8 +515,9 @@
         delete kl;
     }
 
-    if (al && al->startListener()) {
-        delete al;
+    if (logAudit && logAudit->startListener()) {
+        delete logAudit;
+        logAudit = NULL;
     }
 
     TEMP_FAILURE_RETRY(pause());
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index de19790..2014374 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -30,6 +30,8 @@
 #include <log/log.h>
 #include <log/logger.h>
 
+#include "../LogReader.h" // pickup LOGD_SNDTIMEO
+
 /*
  * returns statistics
  */
@@ -253,6 +255,9 @@
         fprintf(stderr, "lid=crash ");
         break;
     case 5:
+        fprintf(stderr, "lid=security ");
+        break;
+    case 6:
         fprintf(stderr, "lid=kernel ");
         break;
     default:
@@ -710,3 +715,56 @@
     EXPECT_TRUE(content_timeout);
     EXPECT_NE(0U, alarm_timeout);
 }
+
+// b/27242723 confirmed fixed
+TEST(logd, SNDTIMEO) {
+    static const unsigned sndtimeo = LOGD_SNDTIMEO; // <sigh> it has to be done!
+    static const unsigned sleep_time = sndtimeo + 3;
+    static const unsigned alarm_time = sleep_time + 5;
+
+    int fd;
+
+    ASSERT_TRUE((fd = socket_local_client("logdr",
+                                 ANDROID_SOCKET_NAMESPACE_RESERVED,
+                                 SOCK_SEQPACKET)) > 0);
+
+    struct sigaction ignore, old_sigaction;
+    memset(&ignore, 0, sizeof(ignore));
+    ignore.sa_handler = caught_signal;
+    sigemptyset(&ignore.sa_mask);
+    sigaction(SIGALRM, &ignore, &old_sigaction);
+    unsigned int old_alarm = alarm(alarm_time);
+
+    static const char ask[] = "stream lids=0,1,2,3,4,5,6"; // all sources
+    bool reader_requested = write(fd, ask, sizeof(ask)) == sizeof(ask);
+    EXPECT_TRUE(reader_requested);
+
+    log_msg msg;
+    bool read_one = recv(fd, msg.buf, sizeof(msg), 0) > 0;
+
+    EXPECT_TRUE(read_one);
+    if (read_one) {
+        dump_log_msg("user", &msg, 3, -1);
+    }
+
+    fprintf (stderr, "Sleep for >%d seconds logd SO_SNDTIMEO ...\n", sndtimeo);
+    sleep(sleep_time);
+
+    // flush will block if we did not trigger. if it did, last entry returns 0
+    int recv_ret;
+    do {
+        recv_ret = recv(fd, msg.buf, sizeof(msg), 0);
+    } while (recv_ret > 0);
+    int save_errno = (recv_ret < 0) ? errno : 0;
+
+    EXPECT_NE(0U, alarm(old_alarm));
+    sigaction(SIGALRM, &old_sigaction, NULL);
+
+    EXPECT_EQ(0, recv_ret);
+    if (recv_ret > 0) {
+        dump_log_msg("user", &msg, 3, -1);
+    }
+    EXPECT_EQ(0, save_errno);
+
+    close(fd);
+}
diff --git a/rootdir/init.rc b/rootdir/init.rc
index b18da3b..3428417 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -359,10 +359,15 @@
     chmod 0660 /data/misc/wifi/wpa_supplicant.conf
     mkdir /data/local 0751 root root
     mkdir /data/misc/media 0700 media media
+    mkdir /data/misc/audioserver 0700 audioserver audioserver
     mkdir /data/misc/vold 0700 root root
     mkdir /data/misc/boottrace 0771 system shell
     mkdir /data/misc/update_engine 0700 root root
     mkdir /data/misc/trace 0700 root root
+    # profile file layout
+    mkdir /data/misc/profiles 0771 system system
+    mkdir /data/misc/profiles/cur 0771 system system
+    mkdir /data/misc/profiles/ref 0771 system system
 
     # For security reasons, /data/local/tmp should always be empty.
     # Do not place files or directories in /data/local/tmp
@@ -400,6 +405,7 @@
     mkdir /data/anr 0775 system system
 
     # symlink to bugreport storage location
+    rm /data/bugreports
     symlink /data/user_de/0/com.android.shell/files/bugreports /data/bugreports
 
     # Separate location for storing security policy files on data