Merge "bootstat: Add support for logging the boot_reason metric." into nyc-dev
am: 5022909765

* commit '50229097656381e52b1fd67e9aa3bd50d1772f42':
  bootstat: Add support for logging the boot_reason metric.
diff --git a/adb/Android.mk b/adb/Android.mk
index d629223..baa4985 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -58,6 +58,7 @@
 LIBADB_TEST_SRCS := \
     adb_io_test.cpp \
     adb_utils_test.cpp \
+    sysdeps_test.cpp \
     transport_test.cpp \
 
 LIBADB_CFLAGS := \
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_utils_test.cpp b/adb/adb_utils_test.cpp
index 794dce6..dfe6f20 100644
--- a/adb/adb_utils_test.cpp
+++ b/adb/adb_utils_test.cpp
@@ -112,7 +112,7 @@
 }
 
 void test_mkdirs(const std::string basepath) {
-  EXPECT_TRUE(mkdirs(basepath));
+  EXPECT_TRUE(mkdirs(adb_dirname(basepath)));
   EXPECT_NE(-1, adb_creat(basepath.c_str(), 0600));
   EXPECT_FALSE(mkdirs(basepath + "/subdir/"));
 }
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/sysdeps.h b/adb/sysdeps.h
index 16796cd..761a4c7 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,57 @@
     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 win_thread_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 win_thread_wrapper(void* args) {
+    win_thread_args thread_args = *static_cast<win_thread_args*>(args);
+    delete static_cast<win_thread_args*>(args);
+    void* result = thread_args.func(thread_args.arg);
+    return reinterpret_cast<unsigned>(result);
+}
+
+static __inline__ bool adb_thread_create(adb_thread_func_t func, void* arg,
+                                         adb_thread_t* thread = nullptr) {
+    win_thread_args* args = new win_thread_args{.func = func, .arg = arg};
+    uintptr_t handle = _beginthreadex(nullptr, 0, win_thread_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__ int adb_thread_setname(const std::string& name) {
@@ -658,14 +703,32 @@
 
 typedef void*  (*adb_thread_func_t)( void*  arg );
 
-static __inline__ bool adb_thread_create(adb_thread_func_t start, void* arg) {
+typedef pthread_t adb_thread_t;
+
+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);
+    errno = pthread_create(&temp, &attr, start, arg);
+    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__ int adb_thread_setname(const std::string& name) {
diff --git a/adb/sysdeps_test.cpp b/adb/sysdeps_test.cpp
new file mode 100644
index 0000000..24a0d6f
--- /dev/null
+++ b/adb/sysdeps_test.cpp
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2065 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 "sysdeps.h"
+
+static void* increment_atomic_int(void* c) {
+    sleep(1);
+    reinterpret_cast<std::atomic<int>*>(c)->fetch_add(1);
+    return nullptr;
+}
+
+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());
+}
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/base/file.cpp b/base/file.cpp
index bcdfc5e..da1adba 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -29,10 +29,6 @@
 #include "cutils/log.h"
 #include "utils/Compat.h"
 
-#if !defined(_WIN32)
-#define O_BINARY 0
-#endif
-
 namespace android {
 namespace base {
 
diff --git a/base/include/android-base/file.h b/base/include/android-base/file.h
index 486befc..5342d98 100644
--- a/base/include/android-base/file.h
+++ b/base/include/android-base/file.h
@@ -20,6 +20,10 @@
 #include <sys/stat.h>
 #include <string>
 
+#if !defined(_WIN32) && !defined(O_BINARY)
+#define O_BINARY 0
+#endif
+
 namespace android {
 namespace base {
 
diff --git a/base/logging.cpp b/base/logging.cpp
index a385902..1741871 100644
--- a/base/logging.cpp
+++ b/base/logging.cpp
@@ -68,7 +68,13 @@
 #include <windows.h>
 #endif
 
-static pid_t GetThreadId() {
+#if defined(_WIN32)
+typedef uint32_t thread_id;
+#else
+typedef pid_t thread_id;
+#endif
+
+static thread_id GetThreadId() {
 #if defined(__BIONIC__)
   return gettid();
 #elif defined(__APPLE__)
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/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/include/utils/ThreadDefs.h b/include/utils/ThreadDefs.h
index 9711c13..ae091e4 100644
--- a/include/utils/ThreadDefs.h
+++ b/include/utils/ThreadDefs.h
@@ -29,7 +29,11 @@
 extern "C" {
 #endif
 
+#ifdef _WIN32
+typedef uint32_t android_thread_id_t;
+#else
 typedef void* android_thread_id_t;
+#endif
 
 typedef int (*android_thread_func_t)(void*);
 
diff --git a/libbacktrace/Android.mk b/libbacktrace/Android.mk
index ee56a5e..397dfda 100644
--- a/libbacktrace/Android.mk
+++ b/libbacktrace/Android.mk
@@ -71,14 +71,22 @@
 build_type := host
 libbacktrace_multilib := both
 include $(LOCAL_PATH)/Android.build.mk
+
+libbacktrace_shared_libraries :=
+
 libbacktrace_static_libraries := \
 	libbase \
 	liblog \
 	libunwind \
+	liblzma \
 
+module := libbacktrace
+build_type := target
 build_target := STATIC_LIBRARY
 include $(LOCAL_PATH)/Android.build.mk
-libbacktrace_static_libraries :=
+build_type := host
+libbacktrace_multilib := both
+include $(LOCAL_PATH)/Android.build.mk
 
 #-------------------------------------------------------------------------
 # The libbacktrace_offline shared library.
@@ -106,7 +114,6 @@
 	libLLVMSupport \
 
 module := libbacktrace_offline
-module_tag := optional
 build_type := target
 build_target := SHARED_LIBRARY
 include $(LOCAL_PATH)/Android.build.mk
@@ -114,6 +121,26 @@
 libbacktrace_multilib := both
 include $(LOCAL_PATH)/Android.build.mk
 
+libbacktrace_offline_shared_libraries :=
+libbacktrace_offline_static_libraries := \
+	libbacktrace \
+	libbase \
+	libcutils \
+	liblog \
+	libunwind \
+	liblzma \
+	libLLVMObject \
+	libLLVMBitReader \
+	libLLVMMC \
+	libLLVMMCParser \
+	libLLVMCore \
+	libLLVMSupport \
+
+module := libbacktrace_offline
+build_type := target
+build_target := STATIC_LIBRARY
+include $(LOCAL_PATH)/Android.build.mk
+
 #-------------------------------------------------------------------------
 # The libbacktrace_test library needed by backtrace_test.
 #-------------------------------------------------------------------------
diff --git a/liblog/fake_log_device.c b/liblog/fake_log_device.c
index cb80ee6..5f7a078 100644
--- a/liblog/fake_log_device.c
+++ b/liblog/fake_log_device.c
@@ -367,7 +367,11 @@
     char prefixBuf[128], suffixBuf[128];
     char priChar;
     time_t when;
+#if !defined(_WIN32)
     pid_t pid, tid;
+#else
+    uint32_t pid, tid;
+#endif
 
     TRACE("LOG %d: %s %s", logPrio, tag, msg);
 
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 8517c9f..8d9c5a3 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -224,7 +224,8 @@
                                                             msgBuf,
                                                             sizeof(msgBuf)));
             fflush(stderr);
-            EXPECT_EQ(31, android_log_printLogLine(logformat, fileno(stderr), &entry));
+            EXPECT_EQ((int)(19 + sizeof(buffer)),
+                      android_log_printLogLine(logformat, fileno(stderr), &entry));
             android_log_format_free(logformat);
         }
     }
@@ -272,7 +273,7 @@
         }
 
         int len = get4LE(eventData + 4 + 1);
-        if (len == 0) {
+        if (len == (sizeof(buffer) - 1)) {
             ++count;
 
             AndroidLogFormat *logformat = android_log_format_new();
@@ -285,7 +286,8 @@
                                                             msgBuf,
                                                             sizeof(msgBuf)));
             fflush(stderr);
-            EXPECT_EQ(20, android_log_printLogLine(logformat, fileno(stderr), &entry));
+            EXPECT_EQ((int)(19 + sizeof(buffer)),
+                      android_log_printLogLine(logformat, fileno(stderr), &entry));
             android_log_format_free(logformat);
         }
     }
@@ -295,6 +297,119 @@
     android_logger_list_close(logger_list);
 }
 
+TEST(liblog, __android_log_bswrite__newline_prefix) {
+    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)));
+
+    static const char buffer[] = "\nHello World\n";
+    log_time ts(android_log_clockid());
+
+    ASSERT_LT(0, __android_log_bswrite(0, buffer));
+    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)
+         || (log_msg.entry.len != (4 + 1 + 4 + sizeof(buffer) - 1))
+         || (log_msg.id() != LOG_ID_EVENTS)) {
+            continue;
+        }
+
+        char *eventData = log_msg.msg();
+
+        if (eventData[4] != EVENT_TYPE_STRING) {
+            continue;
+        }
+
+        int len = get4LE(eventData + 4 + 1);
+        if (len == (sizeof(buffer) - 1)) {
+            ++count;
+
+            AndroidLogFormat *logformat = android_log_format_new();
+            EXPECT_TRUE(NULL != logformat);
+            AndroidLogEntry entry;
+            char msgBuf[1024];
+            EXPECT_EQ(0, android_log_processBinaryLogBuffer(&log_msg.entry_v1,
+                                                            &entry,
+                                                            NULL,
+                                                            msgBuf,
+                                                            sizeof(msgBuf)));
+            fflush(stderr);
+            EXPECT_EQ((int)(19 + 19 - 1 + sizeof(buffer)),
+                      android_log_printLogLine(logformat, fileno(stderr), &entry));
+            android_log_format_free(logformat);
+        }
+    }
+
+    EXPECT_EQ(1, count);
+
+    android_logger_list_close(logger_list);
+}
+
+TEST(liblog, __android_log_buf_write__newline_prefix) {
+    struct logger_list *logger_list;
+
+    pid_t pid = getpid();
+
+    ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
+        LOG_ID_MAIN, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+
+    static const char tag[] = "TEST__android_log_buf_write";
+    static const char buffer[] = "\nHello World\n";
+    log_time ts(android_log_clockid());
+
+    EXPECT_LT(0, __android_log_buf_write(LOG_ID_MAIN, ANDROID_LOG_INFO,
+                                         tag, buffer));
+    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)
+         || (log_msg.entry.len != (sizeof(tag) + sizeof(buffer) + 1))
+         || (log_msg.id() != LOG_ID_MAIN)) {
+            continue;
+        }
+
+        ++count;
+
+        AndroidLogFormat *logformat = android_log_format_new();
+        EXPECT_TRUE(NULL != logformat);
+        AndroidLogEntry entry;
+        EXPECT_EQ(0, android_log_processLogBuffer(&log_msg.entry_v1,
+                                                  &entry));
+        fflush(stderr);
+        EXPECT_EQ((int)(11 + 11 + sizeof(tag) + sizeof(tag) + sizeof(buffer) - 3),
+                  android_log_printLogLine(logformat, fileno(stderr), &entry));
+        android_log_format_free(logformat);
+    }
+
+    EXPECT_EQ(1, count);
+
+    android_logger_list_close(logger_list);
+}
+
 TEST(liblog, __security) {
     static const char persist_key[] = "persist.logd.security";
     static const char readonly_key[] = "ro.device_owner";
@@ -609,7 +724,7 @@
     EXPECT_GT(one_percent_ticks, user_ticks + system_ticks);
 }
 
-static const char max_payload_tag[] = "TEST_max_payload_XXXX";
+static const char max_payload_tag[] = "TEST_max_payload_and_longish_tag_XXXX";
 #define SIZEOF_MAX_PAYLOAD_BUF (LOGGER_ENTRY_MAX_PAYLOAD - \
                                 sizeof(max_payload_tag) - 1)
 static const char max_payload_buf[] = "LEONATO\n\
diff --git a/libutils/Android.mk b/libutils/Android.mk
index 8c4fd15..9e0d0ac 100644
--- a/libutils/Android.mk
+++ b/libutils/Android.mk
@@ -22,7 +22,6 @@
 	Log.cpp \
 	NativeHandle.cpp \
 	Printer.cpp \
-	ProcessCallStack.cpp \
 	PropertyMap.cpp \
 	RefBase.cpp \
 	SharedBuffer.cpp \
@@ -44,7 +43,7 @@
 # =====================================================
 include $(CLEAR_VARS)
 LOCAL_SRC_FILES:= $(commonSources)
-LOCAL_SRC_FILES_linux := Looper.cpp
+LOCAL_SRC_FILES_linux := Looper.cpp ProcessCallStack.cpp
 LOCAL_CFLAGS_darwin := -Wno-unused-parameter
 LOCAL_MODULE:= libutils
 LOCAL_STATIC_LIBRARIES := liblog
@@ -67,6 +66,7 @@
 	$(commonSources) \
 	BlobCache.cpp \
 	Looper.cpp \
+	ProcessCallStack.cpp \
 	Trace.cpp
 
 ifeq ($(TARGET_ARCH),mips)
diff --git a/libutils/ProcessCallStack.cpp b/libutils/ProcessCallStack.cpp
index 011c302..cdb586d 100644
--- a/libutils/ProcessCallStack.cpp
+++ b/libutils/ProcessCallStack.cpp
@@ -146,7 +146,6 @@
     clear();
 
     // Get current time.
-#ifndef USE_MINGW
     {
         time_t t = time(NULL);
         struct tm tm;
@@ -199,7 +198,6 @@
         ALOGE("%s: Failed to readdir from %s: %s",
               __FUNCTION__, PATH_SELF_TASK, strerror(code));
     }
-#endif
 
     closedir(dp);
 }
diff --git a/libziparchive/Android.mk b/libziparchive/Android.mk
index 056b3e1..3cd8b87 100644
--- a/libziparchive/Android.mk
+++ b/libziparchive/Android.mk
@@ -95,13 +95,12 @@
 LOCAL_CFLAGS := $(libziparchive_common_c_flags)
 LOCAL_CPPFLAGS := -Wno-unnamed-type-template-args $(libziparchive_common_cpp_flags)
 LOCAL_SRC_FILES := $(libziparchive_test_files)
-LOCAL_SHARED_LIBRARIES := \
-    libziparchive-host \
-    liblog \
-    libbase \
-
 LOCAL_STATIC_LIBRARIES := \
-    libutils \
+    libziparchive-host \
     libz \
+    libbase \
+    libutils \
+    liblog \
 
+LOCAL_MODULE_HOST_OS := darwin linux windows
 include $(BUILD_HOST_NATIVE_TEST)
diff --git a/libziparchive/zip_archive.cc b/libziparchive/zip_archive.cc
index 3b1e972..a2d6fcc 100644
--- a/libziparchive/zip_archive.cc
+++ b/libziparchive/zip_archive.cc
@@ -224,9 +224,7 @@
           strerror(errno));
     return kIoError;
   }
-  ssize_t actual = TEMP_FAILURE_RETRY(
-      read(fd, scan_buffer, static_cast<size_t>(read_amount)));
-  if (actual != static_cast<ssize_t>(read_amount)) {
+  if (!android::base::ReadFully(fd, scan_buffer, static_cast<size_t>(read_amount))) {
     ALOGW("Zip: read %" PRId64 " failed: %s", static_cast<int64_t>(read_amount),
           strerror(errno));
     return kIoError;
@@ -481,8 +479,7 @@
 static int32_t UpdateEntryFromDataDescriptor(int fd,
                                              ZipEntry *entry) {
   uint8_t ddBuf[sizeof(DataDescriptor) + sizeof(DataDescriptor::kOptSignature)];
-  ssize_t actual = TEMP_FAILURE_RETRY(read(fd, ddBuf, sizeof(ddBuf)));
-  if (actual != sizeof(ddBuf)) {
+  if (!android::base::ReadFully(fd, ddBuf, sizeof(ddBuf))) {
     return kIoError;
   }
 
@@ -498,26 +495,14 @@
 }
 
 // Attempts to read |len| bytes into |buf| at offset |off|.
-//
-// This method uses pread64 on platforms that support it and
-// lseek64 + read on platforms that don't. This implies that
-// callers should not rely on the |fd| offset being incremented
+// Callers should not rely on the |fd| offset being incremented
 // as a side effect of this call.
-static inline ssize_t ReadAtOffset(int fd, uint8_t* buf, size_t len,
-                                   off64_t off) {
-#if !defined(_WIN32)
-  return TEMP_FAILURE_RETRY(pread64(fd, buf, len, off));
-#else
-  // The only supported platform that doesn't support pread at the moment
-  // is Windows. Only recent versions of windows support unix like forks,
-  // and even there the semantics are quite different.
+static inline bool ReadAtOffset(int fd, uint8_t* buf, size_t len, off64_t off) {
   if (lseek64(fd, off, SEEK_SET) != off) {
     ALOGW("Zip: failed seek to offset %" PRId64, off);
-    return kIoError;
+    return false;
   }
-
-  return TEMP_FAILURE_RETRY(read(fd, buf, len));
-#endif
+  return android::base::ReadFully(fd, buf, len);
 }
 
 static int32_t FindEntry(const ZipArchive* archive, const int ent,
@@ -567,9 +552,7 @@
   }
 
   uint8_t lfh_buf[sizeof(LocalFileHeader)];
-  ssize_t actual = ReadAtOffset(archive->fd, lfh_buf, sizeof(lfh_buf),
-                                 local_header_offset);
-  if (actual != sizeof(lfh_buf)) {
+  if (!ReadAtOffset(archive->fd, lfh_buf, sizeof(lfh_buf), local_header_offset)) {
     ALOGW("Zip: failed reading lfh name from offset %" PRId64,
         static_cast<int64_t>(local_header_offset));
     return kIoError;
@@ -610,10 +593,7 @@
     }
 
     uint8_t* name_buf = reinterpret_cast<uint8_t*>(malloc(nameLen));
-    ssize_t actual = ReadAtOffset(archive->fd, name_buf, nameLen,
-                                  name_offset);
-
-    if (actual != nameLen) {
+    if (!ReadAtOffset(archive->fd, name_buf, nameLen, name_offset)) {
       ALOGW("Zip: failed reading lfh name from offset %" PRId64, static_cast<int64_t>(name_offset));
       free(name_buf);
       return kIoError;
@@ -942,10 +922,9 @@
   do {
     /* read as much as we can */
     if (zstream.avail_in == 0) {
-      const ZD_TYPE getSize = (compressed_length > kBufSize) ? kBufSize : compressed_length;
-      const ZD_TYPE actual = TEMP_FAILURE_RETRY(read(fd, &read_buf[0], getSize));
-      if (actual != getSize) {
-        ALOGW("Zip: inflate read failed (" ZD " vs " ZD ")", actual, getSize);
+      const size_t getSize = (compressed_length > kBufSize) ? kBufSize : compressed_length;
+      if (!android::base::ReadFully(fd, read_buf.data(), getSize)) {
+        ALOGW("Zip: inflate read failed, getSize = %zu: %s", getSize, strerror(errno));
         return kIoError;
       }
 
@@ -1005,11 +984,9 @@
 
     // Safe conversion because kBufSize is narrow enough for a 32 bit signed
     // value.
-    const ssize_t block_size = (remaining > kBufSize) ? kBufSize : remaining;
-    const ssize_t actual = TEMP_FAILURE_RETRY(read(fd, &buf[0], block_size));
-
-    if (actual != block_size) {
-      ALOGW("CopyFileToFile: copy read failed (" ZD " vs " ZD ")", actual, block_size);
+    const size_t block_size = (remaining > kBufSize) ? kBufSize : remaining;
+    if (!android::base::ReadFully(fd, buf.data(), block_size)) {
+      ALOGW("CopyFileToFile: copy read failed, block_size = %zu: %s", block_size, strerror(errno));
       return kIoError;
     }
 
diff --git a/libziparchive/zip_archive_test.cc b/libziparchive/zip_archive_test.cc
index d426dc4..6aee1bb 100644
--- a/libziparchive/zip_archive_test.cc
+++ b/libziparchive/zip_archive_test.cc
@@ -21,9 +21,11 @@
 #include <string.h>
 #include <unistd.h>
 
+#include <memory>
 #include <vector>
 
 #include <android-base/file.h>
+#include <android-base/test_utils.h>
 #include <gtest/gtest.h>
 #include <ziparchive/zip_archive.h>
 #include <ziparchive/zip_archive_stream_entry.h>
@@ -91,7 +93,7 @@
 }
 
 TEST(ziparchive, OpenAssumeFdOwnership) {
-  int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY);
+  int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY | O_BINARY);
   ASSERT_NE(-1, fd);
   ZipArchiveHandle handle;
   ASSERT_EQ(0, OpenArchiveFd(fd, "OpenWithAssumeFdOwnership", &handle));
@@ -101,7 +103,7 @@
 }
 
 TEST(ziparchive, OpenDoNotAssumeFdOwnership) {
-  int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY);
+  int fd = open((test_data_dir + "/" + kValidZip).c_str(), O_RDONLY | O_BINARY);
   ASSERT_NE(-1, fd);
   ZipArchiveHandle handle;
   ASSERT_EQ(0, OpenArchiveFd(fd, "OpenWithAssumeFdOwnership", &handle, false));
@@ -373,30 +375,13 @@
 static const std::string kAbTxtName("ab.txt");
 static const size_t kAbUncompressedSize = 270216;
 
-static int make_temporary_file(const char* file_name_pattern) {
-  char full_path[1024];
-  // Account for differences between the host and the target.
-  //
-  // TODO: Maybe reuse bionic/tests/TemporaryFile.h.
-  snprintf(full_path, sizeof(full_path), "/data/local/tmp/%s", file_name_pattern);
-  int fd = mkstemp(full_path);
-  if (fd == -1) {
-    snprintf(full_path, sizeof(full_path), "/tmp/%s", file_name_pattern);
-    fd = mkstemp(full_path);
-  }
-
-  return fd;
-}
-
 TEST(ziparchive, EmptyEntries) {
-  char temp_file_pattern[] = "empty_entries_test_XXXXXX";
-  int fd = make_temporary_file(temp_file_pattern);
-  ASSERT_NE(-1, fd);
-  const ssize_t file_size = sizeof(kEmptyEntriesZip);
-  ASSERT_EQ(file_size, TEMP_FAILURE_RETRY(write(fd, kEmptyEntriesZip, file_size)));
+  TemporaryFile tmp_file;
+  ASSERT_NE(-1, tmp_file.fd);
+  ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, kEmptyEntriesZip, sizeof(kEmptyEntriesZip)));
 
   ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchiveFd(fd, "EmptyEntriesTest", &handle));
+  ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle));
 
   ZipEntry entry;
   ZipString empty_name;
@@ -406,27 +391,23 @@
   uint8_t buffer[1];
   ASSERT_EQ(0, ExtractToMemory(handle, &entry, buffer, 1));
 
-  char output_file_pattern[] = "empty_entries_output_XXXXXX";
-  int output_fd = make_temporary_file(output_file_pattern);
-  ASSERT_NE(-1, output_fd);
-  ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, output_fd));
+
+  TemporaryFile tmp_output_file;
+  ASSERT_NE(-1, tmp_output_file.fd);
+  ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_output_file.fd));
 
   struct stat stat_buf;
-  ASSERT_EQ(0, fstat(output_fd, &stat_buf));
+  ASSERT_EQ(0, fstat(tmp_output_file.fd, &stat_buf));
   ASSERT_EQ(0, stat_buf.st_size);
-
-  close(fd);
-  close(output_fd);
 }
 
 TEST(ziparchive, EntryLargerThan32K) {
-  char temp_file_pattern[] = "entry_larger_than_32k_test_XXXXXX";
-  int fd = make_temporary_file(temp_file_pattern);
-  ASSERT_NE(-1, fd);
-  ASSERT_TRUE(android::base::WriteFully(fd, reinterpret_cast<const uint8_t*>(kAbZip),
+  TemporaryFile tmp_file;
+  ASSERT_NE(-1, tmp_file.fd);
+  ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, reinterpret_cast<const uint8_t*>(kAbZip),
                          sizeof(kAbZip) - 1));
   ZipArchiveHandle handle;
-  ASSERT_EQ(0, OpenArchiveFd(fd, "EntryLargerThan32KTest", &handle));
+  ASSERT_EQ(0, OpenArchiveFd(tmp_file.fd, "EntryLargerThan32KTest", &handle));
 
   ZipEntry entry;
   ZipString ab_name;
@@ -439,21 +420,21 @@
   ASSERT_EQ(0, ExtractToMemory(handle, &entry, &buffer[0], buffer.size()));
 
   // Extract the entry to a file.
-  char output_file_pattern[] = "entry_larger_than_32k_test_output_XXXXXX";
-  int output_fd = make_temporary_file(output_file_pattern);
-  ASSERT_NE(-1, output_fd);
-  ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, output_fd));
+  TemporaryFile tmp_output_file;
+  ASSERT_NE(-1, tmp_output_file.fd);
+  ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_output_file.fd));
 
   // Make sure the extracted file size is as expected.
   struct stat stat_buf;
-  ASSERT_EQ(0, fstat(output_fd, &stat_buf));
+  ASSERT_EQ(0, fstat(tmp_output_file.fd, &stat_buf));
   ASSERT_EQ(kAbUncompressedSize, static_cast<size_t>(stat_buf.st_size));
 
   // Read the file back to a buffer and make sure the contents are
   // the same as the memory buffer we extracted directly to.
   std::vector<uint8_t> file_contents(kAbUncompressedSize);
-  ASSERT_EQ(0, lseek64(output_fd, 0, SEEK_SET));
-  ASSERT_TRUE(android::base::ReadFully(output_fd, &file_contents[0], file_contents.size()));
+  ASSERT_EQ(0, lseek64(tmp_output_file.fd, 0, SEEK_SET));
+  ASSERT_TRUE(android::base::ReadFully(tmp_output_file.fd, &file_contents[0],
+                                       file_contents.size()));
   ASSERT_EQ(file_contents, buffer);
 
   for (int i = 0; i < 90072; ++i) {
@@ -462,35 +443,28 @@
     ASSERT_EQ('b', line[1]);
     ASSERT_EQ('\n', line[2]);
   }
-
-  close(fd);
-  close(output_fd);
 }
 
 TEST(ziparchive, TrailerAfterEOCD) {
-  char temp_file_pattern[] = "trailer_after_eocd_test_XXXXXX";
-  int fd = make_temporary_file(temp_file_pattern);
-  ASSERT_NE(-1, fd);
+  TemporaryFile tmp_file;
+  ASSERT_NE(-1, tmp_file.fd);
 
   // Create a file with 8 bytes of random garbage.
   static const uint8_t trailer[] = { 'A' ,'n', 'd', 'r', 'o', 'i', 'd', 'z' };
-  const ssize_t file_size = sizeof(kEmptyEntriesZip);
-  const ssize_t trailer_size = sizeof(trailer);
-  ASSERT_EQ(file_size, TEMP_FAILURE_RETRY(write(fd, kEmptyEntriesZip, file_size)));
-  ASSERT_EQ(trailer_size, TEMP_FAILURE_RETRY(write(fd, trailer, trailer_size)));
+  ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, kEmptyEntriesZip, sizeof(kEmptyEntriesZip)));
+  ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, trailer, sizeof(trailer)));
 
   ZipArchiveHandle handle;
-  ASSERT_GT(0, OpenArchiveFd(fd, "EmptyEntriesTest", &handle));
+  ASSERT_GT(0, OpenArchiveFd(tmp_file.fd, "EmptyEntriesTest", &handle));
 }
 
 TEST(ziparchive, ExtractToFile) {
-  char kTempFilePattern[] = "zip_archive_input_XXXXXX";
-  int fd = make_temporary_file(kTempFilePattern);
-  ASSERT_NE(-1, fd);
+  TemporaryFile tmp_file;
+  ASSERT_NE(-1, tmp_file.fd);
   const uint8_t data[8] = { '1', '2', '3', '4', '5', '6', '7', '8' };
-  const ssize_t data_size = sizeof(data);
+  const size_t data_size = sizeof(data);
 
-  ASSERT_EQ(data_size, TEMP_FAILURE_RETRY(write(fd, data, data_size)));
+  ASSERT_TRUE(android::base::WriteFully(tmp_file.fd, data, data_size));
 
   ZipArchiveHandle handle;
   ASSERT_EQ(0, OpenArchiveWrapper(kValidZip, &handle));
@@ -499,28 +473,25 @@
   ZipString name;
   SetZipString(&name, kATxtName);
   ASSERT_EQ(0, FindEntry(handle, name, &entry));
-  ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, fd));
+  ASSERT_EQ(0, ExtractEntryToFile(handle, &entry, tmp_file.fd));
 
 
   // Assert that the first 8 bytes of the file haven't been clobbered.
   uint8_t read_buffer[data_size];
-  ASSERT_EQ(0, lseek64(fd, 0, SEEK_SET));
-  ASSERT_EQ(data_size, TEMP_FAILURE_RETRY(read(fd, read_buffer, data_size)));
+  ASSERT_EQ(0, lseek64(tmp_file.fd, 0, SEEK_SET));
+  ASSERT_TRUE(android::base::ReadFully(tmp_file.fd, read_buffer, data_size));
   ASSERT_EQ(0, memcmp(read_buffer, data, data_size));
 
   // Assert that the remainder of the file contains the incompressed data.
   std::vector<uint8_t> uncompressed_data(entry.uncompressed_length);
-  ASSERT_EQ(static_cast<ssize_t>(entry.uncompressed_length),
-            TEMP_FAILURE_RETRY(
-                read(fd, &uncompressed_data[0], entry.uncompressed_length)));
+  ASSERT_TRUE(android::base::ReadFully(tmp_file.fd, uncompressed_data.data(),
+                                       entry.uncompressed_length));
   ASSERT_EQ(0, memcmp(&uncompressed_data[0], kATxtContents.data(),
                       kATxtContents.size()));
 
   // Assert that the total length of the file is sane
-  ASSERT_EQ(data_size + static_cast<ssize_t>(kATxtContents.size()),
-            lseek64(fd, 0, SEEK_END));
-
-  close(fd);
+  ASSERT_EQ(static_cast<ssize_t>(data_size + kATxtContents.size()),
+            lseek64(tmp_file.fd, 0, SEEK_END));
 }
 
 static void ZipArchiveStreamTest(
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc
index fe0846d..16a574d 100644
--- a/libziparchive/zip_writer_test.cc
+++ b/libziparchive/zip_writer_test.cc
@@ -154,12 +154,22 @@
   tm->tm_mday = (zip_time >> 16) & 0x1f;
 }
 
+static struct tm MakeTm() {
+  struct tm tm;
+  memset(&tm, 0, sizeof(struct tm));
+  tm.tm_year = 2001 - 1900;
+  tm.tm_mon = 1;
+  tm.tm_mday = 12;
+  tm.tm_hour = 18;
+  tm.tm_min = 30;
+  tm.tm_sec = 20;
+  return tm;
+}
+
 TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedFlagAndTime) {
   ZipWriter writer(file_);
 
-  struct tm tm;
-  memset(&tm, 0, sizeof(struct tm));
-  ASSERT_TRUE(strptime("18:30:20 1/12/2001", "%H:%M:%S %d/%m/%Y", &tm) != nullptr);
+  struct tm tm = MakeTm();
   time_t time = mktime(&tm);
   ASSERT_EQ(0, writer.StartEntryWithTime("align.txt", ZipWriter::kAlign32, time));
   ASSERT_EQ(0, writer.WriteBytes("he", 2));
@@ -210,9 +220,7 @@
 TEST_F(zipwriter, WriteUncompressedZipFileWithAlignedValueAndTime) {
   ZipWriter writer(file_);
 
-  struct tm tm;
-  memset(&tm, 0, sizeof(struct tm));
-  ASSERT_TRUE(strptime("18:30:20 1/12/2001", "%H:%M:%S %d/%m/%Y", &tm) != nullptr);
+  struct tm tm = MakeTm();
   time_t time = mktime(&tm);
   ASSERT_EQ(0, writer.StartAlignedEntryWithTime("align.txt", 0, time, 4096));
   ASSERT_EQ(0, writer.WriteBytes("he", 2));
diff --git a/logcat/logpersist b/logcat/logpersist
index dab466d..8762ff1 100755
--- a/logcat/logpersist
+++ b/logcat/logpersist
@@ -1,8 +1,8 @@
 #! /system/bin/sh
 # logpersist cat start and stop handlers
 progname="${0##*/}"
-case `getprop ro.build.type` in
-userdebug|eng) ;;
+case `getprop ro.debuggable` in
+1) ;;
 *) echo "${progname} - Permission denied"
    exit 1
    ;;
diff --git a/logd/LogUtils.h b/logd/LogUtils.h
index fd4800e..aa4b6e1 100644
--- a/logd/LogUtils.h
+++ b/logd/LogUtils.h
@@ -17,6 +17,7 @@
 #ifndef _LOGD_LOG_UTILS_H__
 #define _LOGD_LOG_UTILS_H__
 
+#include <sys/cdefs.h>
 #include <sys/types.h>
 
 #include <log/log.h>
@@ -29,6 +30,7 @@
 
 // Furnished in main.cpp. Caller must own and free returned value
 char *uidToName(uid_t uid);
+void prdebug(const char *fmt, ...) __printflike(1, 2);
 
 // Furnished in LogStatistics.cpp. Caller must own and free returned value
 char *pidToName(pid_t pid);
diff --git a/logd/README.property b/logd/README.property
index 22f86b9..6200d3e 100644
--- a/logd/README.property
+++ b/logd/README.property
@@ -8,7 +8,7 @@
 ro.device_owner            bool   false  Override persist.logd.security to false
 ro.logd.kernel             bool+ svelte+ Enable klogd daemon
 ro.logd.statistics         bool+ svelte+ Enable logcat -S statistics.
-ro.build.type              string        if user, logd.statistics &
+ro.debuggable              number        if not "1", logd.statistics &
                                          ro.logd.kernel default false.
 persist.logd.logpersistd   string        Enable logpersist daemon, "logcatd"
                                          turns on logcat -f in logd context
@@ -46,10 +46,10 @@
 
 NB:
 - bool+ - "true", "false" and comma separated list of "eng" (forced false if
-  ro.build.type is "user") or "svelte" (forced false if ro.config.low_ram is
+  ro.debuggable is not "1") or "svelte" (forced false if ro.config.low_ram is
   true).
 - svelte - see ro.config.low_ram for details.
-- svelte+ - see ro.config.low_ram and ro.build.type for details.
+- svelte+ - see ro.config.low_ram and ro.debuggable for details.
 - ro - <base property> temporary override, ro.<base property> platform default.
 - persist - <base property> override, persist.<base property> platform default.
 - build - VERBOSE for native, DEBUG for jvm isLoggable, or developer option.
diff --git a/logd/main.cpp b/logd/main.cpp
index ba56e57..bef40c7 100644
--- a/logd/main.cpp
+++ b/logd/main.cpp
@@ -202,8 +202,8 @@
         return false;
     }
     if (flag & BOOL_DEFAULT_FLAG_ENG) {
-        property_get("ro.build.type", property, "");
-        if (!strcmp(property, "user")) {
+        property_get("ro.debuggable", property, "");
+        if (strcmp(property, "1")) {
             return false;
         }
     }
@@ -211,10 +211,32 @@
     return (flag & BOOL_DEFAULT_FLAG_TRUE_FALSE) != BOOL_DEFAULT_FALSE;
 }
 
-// Remove the static, and use this variable
-// globally for debugging if necessary. eg:
-//   write(fdDmesg, "I am here\n", 10);
 static int fdDmesg = -1;
+void inline android::prdebug(const char *fmt, ...) {
+    if (fdDmesg < 0) {
+        return;
+    }
+
+    static const char message[] = {
+        KMSG_PRIORITY(LOG_DEBUG), 'l', 'o', 'g', 'd', ':', ' '
+    };
+    char buffer[256];
+    memcpy(buffer, message, sizeof(message));
+
+    va_list ap;
+    va_start(ap, fmt);
+    int n = vsnprintf(buffer + sizeof(message),
+                      sizeof(buffer) - sizeof(message), fmt, ap);
+    va_end(ap);
+    if (n > 0) {
+        buffer[sizeof(buffer) - 1] = '\0';
+        if (!strchr(buffer, '\n')) {
+            buffer[sizeof(buffer) - 2] = '\0';
+            strlcat(buffer, "\n", sizeof(buffer));
+        }
+        write(fdDmesg, buffer, strlen(buffer));
+    }
+}
 
 static sem_t uidName;
 static uid_t uid;
diff --git a/metricsd/uploader/upload_service_test.cc b/metricsd/uploader/upload_service_test.cc
index 70112f4..0f77fe4 100644
--- a/metricsd/uploader/upload_service_test.cc
+++ b/metricsd/uploader/upload_service_test.cc
@@ -304,6 +304,8 @@
   upload_service_->PersistToDisk();
   EXPECT_EQ(
       1, upload_service_->current_log_->uma_proto()->histogram_event().size());
+  // Destroy the old service before creating a new one.
+  upload_service_.reset();
   upload_service_.reset(new UploadService(
       "", base::TimeDelta(), base::TimeDelta(), private_dir_, shared_dir_));
   upload_service_->InitForTest(nullptr);
@@ -325,6 +327,8 @@
   // Write a bogus saved log.
   EXPECT_EQ(5, base::WriteFile(upload_service_->saved_log_path_, "hello", 5));
 
+  // Destroy the old service before creating a new one.
+  upload_service_.reset();
   upload_service_.reset(new UploadService(
       "", base::TimeDelta(), base::TimeDelta(), private_dir_, shared_dir_));