Merge "Precise command constants on adb/protoxol.txt"
diff --git a/adb/Android.mk b/adb/Android.mk
index 1aa3dc1..b2a0dc4 100644
--- a/adb/Android.mk
+++ b/adb/Android.mk
@@ -81,12 +81,10 @@
     $(ADB_COMMON_windows_CFLAGS) \
 
 LIBADB_darwin_SRC_FILES := \
-    get_my_path_darwin.cpp \
     sysdeps_unix.cpp \
     usb_osx.cpp \
 
 LIBADB_linux_SRC_FILES := \
-    get_my_path_linux.cpp \
     sysdeps_unix.cpp \
     usb_linux.cpp \
 
@@ -184,6 +182,10 @@
 LOCAL_CFLAGS_darwin := $(LIBADB_darwin_CFLAGS)
 LOCAL_SRC_FILES := \
     $(LIBADB_TEST_SRCS) \
+    adb_client.cpp \
+    bugreport.cpp \
+    bugreport_test.cpp \
+    line_printer.cpp \
     services.cpp \
     shell_service_protocol.cpp \
     shell_service_protocol_test.cpp \
@@ -199,6 +201,7 @@
     libcrypto \
     libcutils \
     libdiagnose_usb \
+    libgmock_host \
 
 # Set entrypoint to wmain from sysdeps_win32.cpp instead of main
 LOCAL_LDFLAGS_windows := -municode
@@ -227,6 +230,7 @@
 
 LOCAL_SRC_FILES := \
     adb_client.cpp \
+    bugreport.cpp \
     client/main.cpp \
     console.cpp \
     commandline.cpp \
diff --git a/adb/adb.cpp b/adb/adb.cpp
index be390d9..056dbef 100644
--- a/adb/adb.cpp
+++ b/adb/adb.cpp
@@ -34,6 +34,7 @@
 #include <vector>
 
 #include <android-base/errors.h>
+#include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/macros.h>
 #include <android-base/parsenetaddress.h>
@@ -610,8 +611,7 @@
 
 #endif
 
-int launch_server(int server_port)
-{
+int launch_server(const std::string& socket_spec) {
 #if defined(_WIN32)
     /* we need to start the server in the background                    */
     /* we create a PIPE that will be used to wait for the server's "OK" */
@@ -714,9 +714,8 @@
     }
 
     WCHAR   args[64];
-    snwprintf(args, arraysize(args),
-              L"adb -P %d fork-server server --reply-fd %d", server_port,
-              ack_write_as_int);
+    snwprintf(args, arraysize(args), L"adb -L %s fork-server server --reply-fd %d",
+              socket_spec.c_str(), ack_write_as_int);
 
     PROCESS_INFORMATION   pinfo;
     ZeroMemory(&pinfo, sizeof(pinfo));
@@ -843,30 +842,29 @@
         return -1;
     }
 #else /* !defined(_WIN32) */
-    char    path[PATH_MAX];
-    int     fd[2];
-
     // set up a pipe so the child can tell us when it is ready.
     // fd[0] will be parent's end, and the child will write on fd[1]
+    int fd[2];
     if (pipe(fd)) {
         fprintf(stderr, "pipe failed in launch_server, errno: %d\n", errno);
         return -1;
     }
-    get_my_path(path, PATH_MAX);
+
+    std::string path = android::base::GetExecutablePath();
+
     pid_t pid = fork();
-    if(pid < 0) return -1;
+    if (pid < 0) return -1;
 
     if (pid == 0) {
         // child side of the fork
 
         adb_close(fd[0]);
 
-        char str_port[30];
-        snprintf(str_port, sizeof(str_port), "%d", server_port);
         char reply_fd[30];
         snprintf(reply_fd, sizeof(reply_fd), "%d", fd[1]);
         // child process
-        int result = execl(path, "adb", "-P", str_port, "fork-server", "server", "--reply-fd", reply_fd, NULL);
+        int result = execl(path.c_str(), "adb", "-L", socket_spec.c_str(), "fork-server", "server",
+                           "--reply-fd", reply_fd, NULL);
         // this should not return
         fprintf(stderr, "OOPS! execl returned %d, errno: %d\n", result, errno);
     } else  {
diff --git a/adb/adb.h b/adb/adb.h
index 9227eb1..0b9fe5b 100644
--- a/adb/adb.h
+++ b/adb/adb.h
@@ -126,9 +126,8 @@
 
 void handle_packet(apacket *p, atransport *t);
 
-void get_my_path(char *s, size_t maxLen);
-int launch_server(int server_port);
-int adb_server_main(int is_daemon, int server_port, int ack_reply_fd);
+int launch_server(const std::string& socket_spec);
+int adb_server_main(int is_daemon, const std::string& socket_spec, int ack_reply_fd);
 
 /* initialize a transport object's func pointers and state */
 #if ADB_HOST
@@ -203,8 +202,6 @@
 int is_adb_interface(int vid, int pid, int usb_class, int usb_subclass, int usb_protocol);
 #endif
 
-int adb_commandline(int argc, const char **argv);
-
 ConnectionState connection_state(atransport *t);
 
 extern const char* adb_device_banner;
diff --git a/adb/adb_auth_host.cpp b/adb/adb_auth_host.cpp
index 4f4f382..8366549 100644
--- a/adb/adb_auth_host.cpp
+++ b/adb/adb_auth_host.cpp
@@ -246,21 +246,7 @@
 }
 
 static std::string get_user_key_path() {
-    const std::string home = adb_get_homedir_path(true);
-    LOG(DEBUG) << "adb_get_homedir_path returned '" << home << "'";
-
-    const std::string android_dir = android::base::StringPrintf("%s%c.android", home.c_str(),
-                                                                OS_PATH_SEPARATOR);
-
-    struct stat buf;
-    if (stat(android_dir.c_str(), &buf) == -1) {
-        if (adb_mkdir(android_dir.c_str(), 0750) == -1) {
-            PLOG(ERROR) << "Cannot mkdir '" << android_dir << "'";
-            return "";
-        }
-    }
-
-    return android_dir + OS_PATH_SEPARATOR + "adbkey";
+    return adb_get_android_dir_path() + OS_PATH_SEPARATOR + "adbkey";
 }
 
 static bool get_user_key() {
diff --git a/adb/adb_client.cpp b/adb/adb_client.cpp
index a27dd47..919e1c1 100644
--- a/adb/adb_client.cpp
+++ b/adb/adb_client.cpp
@@ -37,12 +37,12 @@
 
 #include "adb_io.h"
 #include "adb_utils.h"
+#include "socket_spec.h"
 
 static TransportType __adb_transport = kTransportAny;
 static const char* __adb_serial = NULL;
 
-static int __adb_server_port = DEFAULT_ADB_PORT;
-static const char* __adb_server_name = NULL;
+static const char* __adb_server_socket_spec;
 
 void adb_set_transport(TransportType type, const char* serial)
 {
@@ -59,14 +59,11 @@
     }
 }
 
-void adb_set_tcp_specifics(int server_port)
-{
-    __adb_server_port = server_port;
-}
-
-void adb_set_tcp_name(const char* hostname)
-{
-    __adb_server_name = hostname;
+void adb_set_socket_spec(const char* socket_spec) {
+    if (__adb_server_socket_spec) {
+        LOG(FATAL) << "attempted to reinitialize adb_server_socket_spec " << socket_spec << " (was " << __adb_server_socket_spec << ")";
+    }
+    __adb_server_socket_spec = socket_spec;
 }
 
 static int switch_socket_transport(int fd, std::string* error) {
@@ -139,23 +136,12 @@
         return -1;
     }
 
-    int fd;
     std::string reason;
-    if (__adb_server_name) {
-        fd = network_connect(__adb_server_name, __adb_server_port, SOCK_STREAM, 0, &reason);
-        if (fd == -1) {
-            *error = android::base::StringPrintf("can't connect to %s:%d: %s",
-                                                 __adb_server_name, __adb_server_port,
-                                                 reason.c_str());
-            return -2;
-        }
-    } else {
-        fd = network_loopback_client(__adb_server_port, SOCK_STREAM, &reason);
-        if (fd == -1) {
-            *error = android::base::StringPrintf("cannot connect to daemon: %s",
-                                                 reason.c_str());
-            return -2;
-        }
+    int fd = socket_spec_connect(__adb_server_socket_spec, &reason);
+    if (fd < 0) {
+        *error = android::base::StringPrintf("cannot connect to daemon at %s: %s",
+                                             __adb_server_socket_spec, reason.c_str());
+        return -2;
     }
 
     if ((memcmp(&service[0],"host",4) != 0 || service == "host:reconnect") &&
@@ -185,15 +171,14 @@
     int fd = _adb_connect("host:version", error);
 
     D("adb_connect: service %s", service.c_str());
-    if (fd == -2 && __adb_server_name) {
+    if (fd == -2 && !is_local_socket_spec(__adb_server_socket_spec)) {
         fprintf(stderr,"** Cannot start server on remote host\n");
         // error is the original network connection error
         return fd;
     } else if (fd == -2) {
-        fprintf(stdout,"* daemon not running. starting it now on port %d *\n",
-                __adb_server_port);
+        fprintf(stdout, "* daemon not running. starting it now at %s *\n", __adb_server_socket_spec);
     start_server:
-        if (launch_server(__adb_server_port)) {
+        if (launch_server(__adb_server_socket_spec)) {
             fprintf(stderr,"* failed to start daemon *\n");
             // launch_server() has already printed detailed error info, so just
             // return a generic error string about the overall adb_connect()
diff --git a/adb/adb_client.h b/adb/adb_client.h
index d5cd922..d35d705 100644
--- a/adb/adb_client.h
+++ b/adb/adb_client.h
@@ -18,6 +18,7 @@
 #define _ADB_CLIENT_H_
 
 #include "adb.h"
+#include "sysdeps.h"
 #include "transport.h"
 
 #include <string>
@@ -42,11 +43,9 @@
 // Get the preferred transport to connect to.
 void adb_get_transport(TransportType* _Nullable type, const char* _Nullable* _Nullable serial);
 
-// Set TCP specifics of the transport to use.
-void adb_set_tcp_specifics(int server_port);
-
-// Set TCP Hostname of the transport to use.
-void adb_set_tcp_name(const char* _Nullable hostname);
+// Set the socket specification for the adb server.
+// This function can only be called once, and the argument must live to the end of the process.
+void adb_set_socket_spec(const char* _Nonnull socket_spec);
 
 // Send commands to the current emulator instance. Will fail if there is not
 // exactly one emulator connected (or if you use -s <serial> with a <serial>
diff --git a/adb/adb_unique_fd.h b/adb/adb_unique_fd.h
new file mode 100644
index 0000000..34c1bbc
--- /dev/null
+++ b/adb/adb_unique_fd.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android-base/unique_fd.h>
+
+// Helper to automatically close an FD when it goes out of scope.
+struct AdbCloser {
+    static void Close(int fd);
+};
+
+using unique_fd = android::base::unique_fd_impl<AdbCloser>;
diff --git a/adb/adb_utils.cpp b/adb/adb_utils.cpp
index 31ec8af..db39ef4 100644
--- a/adb/adb_utils.cpp
+++ b/adb/adb_utils.cpp
@@ -17,6 +17,7 @@
 #define TRACE_TAG ADB
 
 #include "adb_utils.h"
+#include "adb_unique_fd.h"
 
 #include <libgen.h>
 #include <stdlib.h>
@@ -25,6 +26,7 @@
 #include <unistd.h>
 
 #include <algorithm>
+#include <vector>
 
 #include <android-base/logging.h>
 #include <android-base/parseint.h>
@@ -41,6 +43,8 @@
 #  endif
 #  include "windows.h"
 #  include "shlobj.h"
+#else
+#include <pwd.h>
 #endif
 
 ADB_MUTEX_DEFINE(basename_lock);
@@ -263,14 +267,8 @@
     return true;
 }
 
-std::string adb_get_homedir_path(bool check_env_first) {
+std::string adb_get_homedir_path() {
 #ifdef _WIN32
-    if (check_env_first) {
-        if (const char* const home = getenv("ANDROID_SDK_HOME")) {
-            return home;
-        }
-    }
-
     WCHAR path[MAX_PATH];
     const HRESULT hr = SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, 0, path);
     if (FAILED(hr)) {
@@ -286,6 +284,33 @@
     if (const char* const home = getenv("HOME")) {
         return home;
     }
+
+    struct passwd pwent;
+    struct passwd* result;
+    int pwent_max = sysconf(_SC_GETPW_R_SIZE_MAX);
+    std::vector<char> buf(pwent_max);
+    int rc = getpwuid_r(getuid(), &pwent, buf.data(), buf.size(), &result);
+    if (rc == 0 && result) {
+        return result->pw_dir;
+    }
+
+    LOG(FATAL) << "failed to get user home directory";
     return {};
 #endif
 }
+
+std::string adb_get_android_dir_path() {
+    std::string user_dir = adb_get_homedir_path();
+    std::string android_dir = user_dir + OS_PATH_SEPARATOR + ".android";
+    struct stat buf;
+    if (stat(android_dir.c_str(), &buf) == -1) {
+        if (adb_mkdir(android_dir.c_str(), 0750) == -1) {
+            PLOG(FATAL) << "Cannot mkdir '" << android_dir << "'";
+        }
+    }
+    return android_dir;
+}
+
+void AdbCloser::Close(int fd) {
+    adb_close(fd);
+}
diff --git a/adb/adb_utils.h b/adb/adb_utils.h
index f6b4b26..16317e0 100644
--- a/adb/adb_utils.h
+++ b/adb/adb_utils.h
@@ -20,7 +20,6 @@
 #include <string>
 
 #include <android-base/macros.h>
-#include <android-base/unique_fd.h>
 
 void close_stdin();
 
@@ -33,10 +32,10 @@
 std::string adb_dirname(const std::string& path);
 
 // Return the user's home directory.
-// |check_env_first| - if true, on Windows check the ANDROID_SDK_HOME
-// environment variable before trying the WinAPI call (useful when looking for
-// the .android directory)
-std::string adb_get_homedir_path(bool check_env_first);
+std::string adb_get_homedir_path();
+
+// Return the adb user directory.
+std::string adb_get_android_dir_path();
 
 bool mkdirs(const std::string& path);
 
@@ -57,13 +56,4 @@
 bool forward_targets_are_valid(const std::string& source, const std::string& dest,
                                std::string* error);
 
-// Helper to automatically close an FD when it goes out of scope.
-struct AdbCloser {
-    static void Close(int fd) {
-        adb_close(fd);
-    }
-};
-
-using unique_fd = android::base::unique_fd_impl<AdbCloser>;
-
 #endif
diff --git a/adb/bugreport.cpp b/adb/bugreport.cpp
new file mode 100644
index 0000000..c348dd5
--- /dev/null
+++ b/adb/bugreport.cpp
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TRACE_TAG ADB
+
+#include "bugreport.h"
+
+#include <string>
+#include <vector>
+
+#include <android-base/strings.h>
+
+#include "sysdeps.h"
+#include "adb_utils.h"
+#include "file_sync_service.h"
+
+static constexpr char BUGZ_BEGIN_PREFIX[] = "BEGIN:";
+static constexpr char BUGZ_PROGRESS_PREFIX[] = "PROGRESS:";
+static constexpr char BUGZ_PROGRESS_SEPARATOR[] = "/";
+static constexpr char BUGZ_OK_PREFIX[] = "OK:";
+static constexpr char BUGZ_FAIL_PREFIX[] = "FAIL:";
+
+// Custom callback used to handle the output of zipped bugreports.
+class BugreportStandardStreamsCallback : public StandardStreamsCallbackInterface {
+  public:
+    BugreportStandardStreamsCallback(const std::string& dest_dir, const std::string& dest_file,
+                                     bool show_progress, Bugreport* br)
+        : br_(br),
+          src_file_(),
+          dest_dir_(dest_dir),
+          dest_file_(dest_file),
+          line_message_(),
+          invalid_lines_(),
+          show_progress_(show_progress),
+          status_(0),
+          line_() {
+        SetLineMessage("generating");
+    }
+
+    void OnStdout(const char* buffer, int length) {
+        for (int i = 0; i < length; i++) {
+            char c = buffer[i];
+            if (c == '\n') {
+                ProcessLine(line_);
+                line_.clear();
+            } else {
+                line_.append(1, c);
+            }
+        }
+    }
+
+    void OnStderr(const char* buffer, int length) {
+        OnStream(nullptr, stderr, buffer, length);
+    }
+    int Done(int unused_) {
+        // Process remaining line, if any.
+        ProcessLine(line_);
+
+        // Warn about invalid lines, if any.
+        if (!invalid_lines_.empty()) {
+            fprintf(stderr,
+                    "WARNING: bugreportz generated %zu line(s) with unknown commands, "
+                    "device might not support zipped bugreports:\n",
+                    invalid_lines_.size());
+            for (const auto& line : invalid_lines_) {
+                fprintf(stderr, "\t%s\n", line.c_str());
+            }
+            fprintf(stderr,
+                    "If the zipped bugreport was not generated, try 'adb bugreport' instead.\n");
+        }
+
+        // Pull the generated bug report.
+        if (status_ == 0) {
+            if (src_file_.empty()) {
+                fprintf(stderr, "bugreportz did not return a '%s' or '%s' line\n", BUGZ_OK_PREFIX,
+                        BUGZ_FAIL_PREFIX);
+                return -1;
+            }
+            std::string destination;
+            if (dest_dir_.empty()) {
+                destination = dest_file_;
+            } else {
+                destination = android::base::StringPrintf("%s%c%s", dest_dir_.c_str(),
+                                                          OS_PATH_SEPARATOR, dest_file_.c_str());
+            }
+            std::vector<const char*> srcs{src_file_.c_str()};
+            SetLineMessage("pulling");
+            status_ =
+                br_->DoSyncPull(srcs, destination.c_str(), true, line_message_.c_str()) ? 0 : 1;
+            if (status_ != 0) {
+                fprintf(stderr,
+                        "Bug report finished but could not be copied to '%s'.\n"
+                        "Try to run 'adb pull %s <directory>'\n"
+                        "to copy it to a directory that can be written.\n",
+                        destination.c_str(), src_file_.c_str());
+            }
+        }
+        return status_;
+    }
+
+  private:
+    void SetLineMessage(const std::string& action) {
+        line_message_ = action + " " + adb_basename(dest_file_);
+    }
+
+    void SetSrcFile(const std::string path) {
+        src_file_ = path;
+        if (!dest_dir_.empty()) {
+            // Only uses device-provided name when user passed a directory.
+            dest_file_ = adb_basename(path);
+            SetLineMessage("generating");
+        }
+    }
+
+    void ProcessLine(const std::string& line) {
+        if (line.empty()) return;
+
+        if (android::base::StartsWith(line, BUGZ_BEGIN_PREFIX)) {
+            SetSrcFile(&line[strlen(BUGZ_BEGIN_PREFIX)]);
+        } else if (android::base::StartsWith(line, BUGZ_OK_PREFIX)) {
+            SetSrcFile(&line[strlen(BUGZ_OK_PREFIX)]);
+        } else if (android::base::StartsWith(line, BUGZ_FAIL_PREFIX)) {
+            const char* error_message = &line[strlen(BUGZ_FAIL_PREFIX)];
+            fprintf(stderr, "Device failed to take a zipped bugreport: %s\n", error_message);
+            status_ = -1;
+        } else if (show_progress_ && android::base::StartsWith(line, BUGZ_PROGRESS_PREFIX)) {
+            // progress_line should have the following format:
+            //
+            // BUGZ_PROGRESS_PREFIX:PROGRESS/TOTAL
+            //
+            size_t idx1 = line.rfind(BUGZ_PROGRESS_PREFIX) + strlen(BUGZ_PROGRESS_PREFIX);
+            size_t idx2 = line.rfind(BUGZ_PROGRESS_SEPARATOR);
+            int progress = std::stoi(line.substr(idx1, (idx2 - idx1)));
+            int total = std::stoi(line.substr(idx2 + 1));
+            br_->UpdateProgress(line_message_, progress, total);
+        } else {
+            invalid_lines_.push_back(line);
+        }
+    }
+
+    Bugreport* br_;
+
+    // Path of bugreport on device.
+    std::string src_file_;
+
+    // Bugreport destination on host, depending on argument passed on constructor:
+    // - if argument is a directory, dest_dir_ is set with it and dest_file_ will be the name
+    //   of the bugreport reported by the device.
+    // - if argument is empty, dest_dir is set as the current directory and dest_file_ will be the
+    //   name of the bugreport reported by the device.
+    // - otherwise, dest_dir_ is not set and dest_file_ is set with the value passed on constructor.
+    std::string dest_dir_, dest_file_;
+
+    // Message displayed on LinePrinter, it's updated every time the destination above change.
+    std::string line_message_;
+
+    // Lines sent by bugreportz that contain invalid commands; will be displayed at the end.
+    std::vector<std::string> invalid_lines_;
+
+    // Whether PROGRESS_LINES should be interpreted as progress.
+    bool show_progress_;
+
+    // Overall process of the operation, as returned by Done().
+    int status_;
+
+    // Temporary buffer containing the characters read since the last newline (\n).
+    std::string line_;
+
+    DISALLOW_COPY_AND_ASSIGN(BugreportStandardStreamsCallback);
+};
+
+// Implemented in commandline.cpp
+int usage();
+
+int Bugreport::DoIt(TransportType transport_type, const char* serial, int argc, const char** argv) {
+    if (argc > 2) return usage();
+
+    // Gets bugreportz version.
+    std::string bugz_stdout, bugz_stderr;
+    DefaultStandardStreamsCallback version_callback(&bugz_stdout, &bugz_stderr);
+    int status = SendShellCommand(transport_type, serial, "bugreportz -v", false, &version_callback);
+    std::string bugz_version = android::base::Trim(bugz_stderr);
+    std::string bugz_output = android::base::Trim(bugz_stdout);
+
+    if (status != 0 || bugz_version.empty()) {
+        D("'bugreportz' -v results: status=%d, stdout='%s', stderr='%s'", status,
+          bugz_output.c_str(), bugz_version.c_str());
+        if (argc == 1) {
+            // Device does not support bugreportz: if called as 'adb bugreport', just falls out to
+            // the flat-file version.
+            fprintf(stderr,
+                    "Failed to get bugreportz version, which is only available on devices "
+                    "running Android 7.0 or later.\nTrying a plain-text bug report instead.\n");
+            return SendShellCommand(transport_type, serial, "bugreport", false);
+        }
+
+        // But if user explicitly asked for a zipped bug report, fails instead (otherwise calling
+        // 'bugreport' would generate a lot of output the user might not be prepared to handle).
+        fprintf(stderr,
+                "Failed to get bugreportz version: 'bugreportz -v' returned '%s' (code %d).\n"
+                "If the device does not run Android 7.0 or above, try 'adb bugreport' instead.\n",
+                bugz_output.c_str(), status);
+        return status != 0 ? status : -1;
+    }
+
+    std::string dest_file, dest_dir;
+
+    if (argc == 1) {
+        // No args - use current directory
+        if (!getcwd(&dest_dir)) {
+            perror("adb: getcwd failed");
+            return 1;
+        }
+    } else {
+        // Check whether argument is a directory or file
+        if (directory_exists(argv[1])) {
+            dest_dir = argv[1];
+        } else {
+            dest_file = argv[1];
+        }
+    }
+
+    if (dest_file.empty()) {
+        // Uses a default value until device provides the proper name
+        dest_file = "bugreport.zip";
+    } else {
+        if (!android::base::EndsWith(dest_file, ".zip")) {
+            // TODO: use a case-insensitive comparison (like EndsWithIgnoreCase
+            dest_file += ".zip";
+        }
+    }
+
+    bool show_progress = true;
+    std::string bugz_command = "bugreportz -p";
+    if (bugz_version == "1.0") {
+        // 1.0 does not support progress notifications, so print a disclaimer
+        // message instead.
+        fprintf(stderr,
+                "Bugreport is in progress and it could take minutes to complete.\n"
+                "Please be patient and do not cancel or disconnect your device "
+                "until it completes.\n");
+        show_progress = false;
+        bugz_command = "bugreportz";
+    }
+    BugreportStandardStreamsCallback bugz_callback(dest_dir, dest_file, show_progress, this);
+    return SendShellCommand(transport_type, serial, bugz_command, false, &bugz_callback);
+}
+
+void Bugreport::UpdateProgress(const std::string& message, int progress, int total) {
+    int progress_percentage = (progress * 100 / total);
+    line_printer_.Print(
+        android::base::StringPrintf("[%3d%%] %s", progress_percentage, message.c_str()),
+        LinePrinter::INFO);
+}
+
+int Bugreport::SendShellCommand(TransportType transport_type, const char* serial,
+                                const std::string& command, bool disable_shell_protocol,
+                                StandardStreamsCallbackInterface* callback) {
+    return send_shell_command(transport_type, serial, command, disable_shell_protocol, callback);
+}
+
+bool Bugreport::DoSyncPull(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs,
+                           const char* name) {
+    return do_sync_pull(srcs, dst, copy_attrs, name);
+}
diff --git a/adb/bugreport.h b/adb/bugreport.h
new file mode 100644
index 0000000..ee99cbc
--- /dev/null
+++ b/adb/bugreport.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BUGREPORT_H
+#define BUGREPORT_H
+
+#include <vector>
+
+#include "adb.h"
+#include "commandline.h"
+#include "line_printer.h"
+
+class Bugreport {
+    friend class BugreportStandardStreamsCallback;
+
+  public:
+    Bugreport() : line_printer_() {
+    }
+    int DoIt(TransportType transport_type, const char* serial, int argc, const char** argv);
+
+  protected:
+    // Functions below are abstractions of external functions so they can be
+    // mocked on tests.
+    virtual int SendShellCommand(
+        TransportType transport_type, const char* serial, const std::string& command,
+        bool disable_shell_protocol,
+        StandardStreamsCallbackInterface* callback = &DEFAULT_STANDARD_STREAMS_CALLBACK);
+
+    virtual bool DoSyncPull(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs,
+                            const char* name);
+
+  private:
+    virtual void UpdateProgress(const std::string& file_name, int progress, int total);
+    LinePrinter line_printer_;
+    DISALLOW_COPY_AND_ASSIGN(Bugreport);
+};
+
+#endif  // BUGREPORT_H
diff --git a/adb/bugreport_test.cpp b/adb/bugreport_test.cpp
new file mode 100644
index 0000000..1129285
--- /dev/null
+++ b/adb/bugreport_test.cpp
@@ -0,0 +1,418 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "bugreport.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <android-base/strings.h>
+#include <android-base/test_utils.h>
+
+#include "sysdeps.h"
+#include "adb_utils.h"
+
+using ::testing::_;
+using ::testing::Action;
+using ::testing::ActionInterface;
+using ::testing::DoAll;
+using ::testing::ElementsAre;
+using ::testing::HasSubstr;
+using ::testing::MakeAction;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::testing::WithArg;
+using ::testing::internal::CaptureStderr;
+using ::testing::internal::CaptureStdout;
+using ::testing::internal::GetCapturedStderr;
+using ::testing::internal::GetCapturedStdout;
+
+// Empty function so tests don't need to be linked against file_sync_service.cpp, which requires
+// SELinux and its transitive dependencies...
+bool do_sync_pull(const std::vector<const char*>& srcs, const char* dst, bool copy_attrs,
+                  const char* name) {
+    ADD_FAILURE() << "do_sync_pull() should have been mocked";
+    return false;
+}
+
+// Empty functions so tests don't need to be linked against commandline.cpp
+DefaultStandardStreamsCallback DEFAULT_STANDARD_STREAMS_CALLBACK(nullptr, nullptr);
+int usage() {
+    return -42;
+}
+int send_shell_command(TransportType transport_type, const char* serial, const std::string& command,
+                       bool disable_shell_protocol, StandardStreamsCallbackInterface* callback) {
+    ADD_FAILURE() << "send_shell_command() should have been mocked";
+    return -42;
+}
+
+enum StreamType {
+    kStreamStdout,
+    kStreamStderr,
+};
+
+// gmock black magic to provide a WithArg<4>(WriteOnStdout(output)) matcher
+typedef void OnStandardStreamsCallbackFunction(StandardStreamsCallbackInterface*);
+
+class OnStandardStreamsCallbackAction : public ActionInterface<OnStandardStreamsCallbackFunction> {
+  public:
+    explicit OnStandardStreamsCallbackAction(StreamType type, const std::string& output)
+        : type_(type), output_(output) {
+    }
+    virtual Result Perform(const ArgumentTuple& args) {
+        if (type_ == kStreamStdout) {
+            ::std::tr1::get<0>(args)->OnStdout(output_.c_str(), output_.size());
+        }
+        if (type_ == kStreamStderr) {
+            ::std::tr1::get<0>(args)->OnStderr(output_.c_str(), output_.size());
+        }
+    }
+
+  private:
+    StreamType type_;
+    std::string output_;
+};
+
+// Matcher used to emulated StandardStreamsCallbackInterface.OnStdout(buffer,
+// length)
+Action<OnStandardStreamsCallbackFunction> WriteOnStdout(const std::string& output) {
+    return MakeAction(new OnStandardStreamsCallbackAction(kStreamStdout, output));
+}
+
+// Matcher used to emulated StandardStreamsCallbackInterface.OnStderr(buffer,
+// length)
+Action<OnStandardStreamsCallbackFunction> WriteOnStderr(const std::string& output) {
+    return MakeAction(new OnStandardStreamsCallbackAction(kStreamStderr, output));
+}
+
+typedef int CallbackDoneFunction(StandardStreamsCallbackInterface*);
+
+class CallbackDoneAction : public ActionInterface<CallbackDoneFunction> {
+  public:
+    explicit CallbackDoneAction(int status) : status_(status) {
+    }
+    virtual Result Perform(const ArgumentTuple& args) {
+        int status = ::std::tr1::get<0>(args)->Done(status_);
+        return status;
+    }
+
+  private:
+    int status_;
+};
+
+// Matcher used to emulated StandardStreamsCallbackInterface.Done(status)
+Action<CallbackDoneFunction> ReturnCallbackDone(int status = -1337) {
+    return MakeAction(new CallbackDoneAction(status));
+}
+
+class BugreportMock : public Bugreport {
+  public:
+    MOCK_METHOD5(SendShellCommand,
+                 int(TransportType transport_type, const char* serial, const std::string& command,
+                     bool disable_shell_protocol, StandardStreamsCallbackInterface* callback));
+    MOCK_METHOD4(DoSyncPull, bool(const std::vector<const char*>& srcs, const char* dst,
+                                  bool copy_attrs, const char* name));
+    MOCK_METHOD3(UpdateProgress, void(const std::string&, int, int));
+};
+
+class BugreportTest : public ::testing::Test {
+  public:
+    void SetUp() {
+        if (!getcwd(&cwd_)) {
+            ADD_FAILURE() << "getcwd failed: " << strerror(errno);
+            return;
+        }
+    }
+
+    void ExpectBugreportzVersion(const std::string& version) {
+        EXPECT_CALL(br_,
+                    SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -v", false, _))
+            .WillOnce(DoAll(WithArg<4>(WriteOnStderr(version.c_str())),
+                            WithArg<4>(ReturnCallbackDone(0))));
+    }
+
+    void ExpectProgress(int progress, int total, const std::string& file = "file.zip") {
+        EXPECT_CALL(br_, UpdateProgress(StrEq("generating " + file), progress, total));
+    }
+
+    BugreportMock br_;
+    std::string cwd_;  // TODO: make it static
+};
+
+// Tests when called with invalid number of arguments
+TEST_F(BugreportTest, InvalidNumberArgs) {
+    const char* args[] = {"bugreport", "to", "principal"};
+    ASSERT_EQ(-42, br_.DoIt(kTransportLocal, "HannibalLecter", 3, args));
+}
+
+// Tests the 'adb bugreport' option when the device does not support 'bugreportz' - it falls back
+// to the flat-file format ('bugreport' binary on device)
+TEST_F(BugreportTest, NoArgumentsPreNDevice) {
+    // clang-format off
+    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -v", false, _))
+        .WillOnce(DoAll(WithArg<4>(WriteOnStderr("")),
+                        // Write some bogus output on stdout to make sure it's ignored
+                        WithArg<4>(WriteOnStdout("Dude, where is my bugreportz?")),
+                        WithArg<4>(ReturnCallbackDone(0))));
+    // clang-format on
+    std::string bugreport = "Reported the bug was.";
+    CaptureStdout();
+    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreport", false, _))
+        .WillOnce(DoAll(WithArg<4>(WriteOnStdout(bugreport)), Return(0)));
+
+    const char* args[] = {"bugreport"};
+    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 1, args));
+    ASSERT_THAT(GetCapturedStdout(), StrEq(bugreport));
+}
+
+// Tests the 'adb bugreport' option when the device supports 'bugreportz' version 1.0 - it will
+// save the bugreport in the current directory with the name provided by the device.
+TEST_F(BugreportTest, NoArgumentsNDevice) {
+    ExpectBugreportzVersion("1.0");
+
+    std::string dest_file =
+        android::base::StringPrintf("%s%cda_bugreport.zip", cwd_.c_str(), OS_PATH_SEPARATOR);
+    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _))
+        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip")),
+                        WithArg<4>(ReturnCallbackDone())));
+    EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
+                                true, StrEq("pulling da_bugreport.zip")))
+        .WillOnce(Return(true));
+
+    const char* args[] = {"bugreport"};
+    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 1, args));
+}
+
+// Tests the 'adb bugreport' option when the device supports 'bugreportz' version 1.1 - it will
+// save the bugreport in the current directory with the name provided by the device.
+TEST_F(BugreportTest, NoArgumentsPostNDevice) {
+    ExpectBugreportzVersion("1.1");
+    std::string dest_file =
+        android::base::StringPrintf("%s%cda_bugreport.zip", cwd_.c_str(), OS_PATH_SEPARATOR);
+    ExpectProgress(50, 100, "da_bugreport.zip");
+    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
+        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
+                        WithArg<4>(WriteOnStdout("PROGRESS:50/100\n")),
+                        WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip\n")),
+                        WithArg<4>(ReturnCallbackDone())));
+    EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
+                                true, StrEq("pulling da_bugreport.zip")))
+        .WillOnce(Return(true));
+
+    const char* args[] = {"bugreport"};
+    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 1, args));
+}
+
+// Tests 'adb bugreport file.zip' when it succeeds and device does not support progress.
+TEST_F(BugreportTest, OkNDevice) {
+    ExpectBugreportzVersion("1.0");
+    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _))
+        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip")),
+                        WithArg<4>(ReturnCallbackDone())));
+    EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
+                                true, StrEq("pulling file.zip")))
+        .WillOnce(Return(true));
+
+    const char* args[] = {"bugreport", "file.zip"};
+    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+}
+
+// Tests 'adb bugreport file.zip' when it succeeds but response was sent in
+// multiple buffer writers and without progress updates.
+TEST_F(BugreportTest, OkNDeviceSplitBuffer) {
+    ExpectBugreportzVersion("1.0");
+    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _))
+        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device")),
+                        WithArg<4>(WriteOnStdout("/bugreport.zip")),
+                        WithArg<4>(ReturnCallbackDone())));
+    EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
+                                true, StrEq("pulling file.zip")))
+        .WillOnce(Return(true));
+
+    const char* args[] = {"bugreport", "file.zip"};
+    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+}
+
+// Tests 'adb bugreport file.zip' when it succeeds and displays progress.
+TEST_F(BugreportTest, OkProgress) {
+    ExpectBugreportzVersion("1.1");
+    ExpectProgress(1, 100);
+    ExpectProgress(10, 100);
+    ExpectProgress(50, 100);
+    ExpectProgress(99, 100);
+    // clang-format off
+    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
+        // NOTE: DoAll accepts at most 10 arguments, and we're almost reached that limit...
+        .WillOnce(DoAll(
+            // Name might change on OK, so make sure the right one is picked.
+            WithArg<4>(WriteOnStdout("BEGIN:/device/bugreport___NOT.zip\n")),
+            // Progress line in one write
+            WithArg<4>(WriteOnStdout("PROGRESS:1/100\n")),
+            // Add some bogus lines
+            WithArg<4>(WriteOnStdout("\nDUDE:SWEET\n\nBLA\n\nBLA\nBLA\n\n")),
+            // Multiple progress lines in one write
+            WithArg<4>(WriteOnStdout("PROGRESS:10/100\nPROGRESS:50/100\n")),
+            // Progress line in multiple writes
+            WithArg<4>(WriteOnStdout("PROG")),
+            WithArg<4>(WriteOnStdout("RESS:99")),
+            WithArg<4>(WriteOnStdout("/100\n")),
+            // Split last message as well, just in case
+            WithArg<4>(WriteOnStdout("OK:/device/bugreport")),
+            WithArg<4>(WriteOnStdout(".zip")),
+            WithArg<4>(ReturnCallbackDone())));
+    // clang-format on
+    EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
+                                true, StrEq("pulling file.zip")))
+        .WillOnce(Return(true));
+
+    const char* args[] = {"bugreport", "file.zip"};
+    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+}
+
+// Tests 'adb bugreport dir' when it succeeds and destination is a directory.
+TEST_F(BugreportTest, OkDirectory) {
+    ExpectBugreportzVersion("1.1");
+    TemporaryDir td;
+    std::string dest_file =
+        android::base::StringPrintf("%s%cda_bugreport.zip", td.path, OS_PATH_SEPARATOR);
+
+    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
+        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
+                        WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip")),
+                        WithArg<4>(ReturnCallbackDone())));
+    EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
+                                true, StrEq("pulling da_bugreport.zip")))
+        .WillOnce(Return(true));
+
+    const char* args[] = {"bugreport", td.path};
+    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+}
+
+// Tests 'adb bugreport file' when it succeeds
+TEST_F(BugreportTest, OkNoExtension) {
+    ExpectBugreportzVersion("1.1");
+    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
+        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip\n")),
+                        WithArg<4>(ReturnCallbackDone())));
+    EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
+                                true, StrEq("pulling file.zip")))
+        .WillOnce(Return(true));
+
+    const char* args[] = {"bugreport", "file"};
+    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+}
+
+// Tests 'adb bugreport dir' when it succeeds and destination is a directory and device runs N.
+TEST_F(BugreportTest, OkNDeviceDirectory) {
+    ExpectBugreportzVersion("1.0");
+    TemporaryDir td;
+    std::string dest_file =
+        android::base::StringPrintf("%s%cda_bugreport.zip", td.path, OS_PATH_SEPARATOR);
+
+    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _))
+        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
+                        WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip")),
+                        WithArg<4>(ReturnCallbackDone())));
+    EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
+                                true, StrEq("pulling da_bugreport.zip")))
+        .WillOnce(Return(true));
+
+    const char* args[] = {"bugreport", td.path};
+    ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+}
+
+// Tests 'adb bugreport file.zip' when the bugreport itself failed
+TEST_F(BugreportTest, BugreportzReturnedFail) {
+    ExpectBugreportzVersion("1.1");
+    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
+        .WillOnce(
+            DoAll(WithArg<4>(WriteOnStdout("FAIL:D'OH!\n")), WithArg<4>(ReturnCallbackDone())));
+
+    CaptureStderr();
+    const char* args[] = {"bugreport", "file.zip"};
+    ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+    ASSERT_THAT(GetCapturedStderr(), HasSubstr("D'OH!"));
+}
+
+// Tests 'adb bugreport file.zip' when the bugreport itself failed but response
+// was sent in
+// multiple buffer writes
+TEST_F(BugreportTest, BugreportzReturnedFailSplitBuffer) {
+    ExpectBugreportzVersion("1.1");
+    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
+        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("FAIL")), WithArg<4>(WriteOnStdout(":D'OH!\n")),
+                        WithArg<4>(ReturnCallbackDone())));
+
+    CaptureStderr();
+    const char* args[] = {"bugreport", "file.zip"};
+    ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+    ASSERT_THAT(GetCapturedStderr(), HasSubstr("D'OH!"));
+}
+
+// Tests 'adb bugreport file.zip' when the bugreportz returned an unsupported
+// response.
+TEST_F(BugreportTest, BugreportzReturnedUnsupported) {
+    ExpectBugreportzVersion("1.1");
+    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
+        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("bugreportz? What am I, a zombie?")),
+                        WithArg<4>(ReturnCallbackDone())));
+
+    CaptureStderr();
+    const char* args[] = {"bugreport", "file.zip"};
+    ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+    ASSERT_THAT(GetCapturedStderr(), HasSubstr("bugreportz? What am I, a zombie?"));
+}
+
+// Tests 'adb bugreport file.zip' when the bugreportz -v command failed
+TEST_F(BugreportTest, BugreportzVersionFailed) {
+    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -v", false, _))
+        .WillOnce(Return(666));
+
+    const char* args[] = {"bugreport", "file.zip"};
+    ASSERT_EQ(666, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+}
+
+// Tests 'adb bugreport file.zip' when the bugreportz -v returns status 0 but with no output.
+TEST_F(BugreportTest, BugreportzVersionEmpty) {
+    ExpectBugreportzVersion("");
+
+    const char* args[] = {"bugreport", "file.zip"};
+    ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+}
+
+// Tests 'adb bugreport file.zip' when the main bugreportz command failed
+TEST_F(BugreportTest, BugreportzFailed) {
+    ExpectBugreportzVersion("1.1");
+    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
+        .WillOnce(Return(666));
+
+    const char* args[] = {"bugreport", "file.zip"};
+    ASSERT_EQ(666, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+}
+
+// Tests 'adb bugreport file.zip' when the bugreport could not be pulled
+TEST_F(BugreportTest, PullFails) {
+    ExpectBugreportzVersion("1.1");
+    EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
+        .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip")),
+                        WithArg<4>(ReturnCallbackDone())));
+    EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
+                                true, HasSubstr("file.zip")))
+        .WillOnce(Return(false));
+
+    const char* args[] = {"bugreport", "file.zip"};
+    ASSERT_EQ(1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+}
diff --git a/adb/client/main.cpp b/adb/client/main.cpp
index e160169..571c227 100644
--- a/adb/client/main.cpp
+++ b/adb/client/main.cpp
@@ -32,6 +32,7 @@
 #include "adb_auth.h"
 #include "adb_listeners.h"
 #include "adb_utils.h"
+#include "commandline.h"
 #include "transport.h"
 
 static std::string GetLogFilePath() {
@@ -87,7 +88,7 @@
 }
 #endif
 
-int adb_server_main(int is_daemon, int server_port, int ack_reply_fd) {
+int adb_server_main(int is_daemon, const std::string& socket_spec, int ack_reply_fd) {
 #if defined(_WIN32)
     // adb start-server starts us up with stdout and stderr hooked up to
     // anonymous pipes. When the C Runtime sees this, it makes stderr and
@@ -112,8 +113,7 @@
     local_init(DEFAULT_ADB_LOCAL_TRANSPORT_PORT);
 
     std::string error;
-    std::string local_name = android::base::StringPrintf("tcp:%d", server_port);
-    if (install_listener(local_name, "*smartsocket*", nullptr, 0, nullptr, &error)) {
+    if (install_listener(socket_spec, "*smartsocket*", nullptr, 0, nullptr, &error)) {
         fatal("could not install *smartsocket* listener: %s", error.c_str());
     }
 
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 8aab389..77c5f96 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -51,11 +51,13 @@
 #include "adb_auth.h"
 #include "adb_client.h"
 #include "adb_io.h"
+#include "adb_unique_fd.h"
 #include "adb_utils.h"
+#include "bugreport.h"
+#include "commandline.h"
 #include "file_sync_service.h"
 #include "services.h"
 #include "shell_service.h"
-#include "transport.h"
 
 static int install_app(TransportType t, const char* serial, int argc, const char** argv);
 static int install_multiple_app(TransportType t, const char* serial, int argc, const char** argv);
@@ -66,8 +68,7 @@
 static auto& gProductOutPath = *new std::string();
 extern int gListenAll;
 
-static constexpr char BUGZ_OK_PREFIX[] = "OK:";
-static constexpr char BUGZ_FAIL_PREFIX[] = "FAIL:";
+DefaultStandardStreamsCallback DEFAULT_STANDARD_STREAMS_CALLBACK(nullptr, nullptr);
 
 static std::string product_file(const char *extra) {
     if (gProductOutPath.empty()) {
@@ -82,6 +83,7 @@
 
 static void help() {
     fprintf(stderr, "%s\n", adb_version().c_str());
+    // clang-format off
     fprintf(stderr,
         " -a                            - directs adb to listen on all interfaces for a connection\n"
         " -d                            - directs command to the only connected USB device\n"
@@ -99,6 +101,8 @@
         "                                 be an absolute path.\n"
         " -H                            - Name of adb server host (default: localhost)\n"
         " -P                            - Port of adb server (default: 5037)\n"
+        " -L <socket>                   - listen on socket specifier for the adb server\n"
+        "                                 (default: tcp:localhost:5037)\n"
         " devices [-l]                  - list all connected devices\n"
         "                                 ('-l' will also list device qualifiers)\n"
         " connect <host>[:<port>]       - connect to a device via TCP/IP\n"
@@ -174,9 +178,11 @@
         "                                 (-g: grant all runtime permissions)\n"
         "  adb uninstall [-k] <package> - remove this app package from the device\n"
         "                                 ('-k' means keep the data and cache directories)\n"
-        "  adb bugreport [<zip_file>]   - return all information from the device\n"
-        "                                 that should be included in a bug report.\n"
-        "\n"
+        "  adb bugreport [<path>]       - return all information from the device that should be included in a zipped bug report.\n"
+        "                                 If <path> is a file, the bug report will be saved as that file.\n"
+        "                                 If <path> is a directory, the bug report will be saved in that directory with the name provided by the device.\n"
+        "                                 If <path> is omitted, the bug report will be saved in the current directory with the name provided by the device.\n"
+        "                                 NOTE: if the device does not support zipped bug reports, the bug report will be output on stdout.\n"
         "  adb backup [-f <file>] [-apk|-noapk] [-obb|-noobb] [-shared|-noshared] [-all] [-system|-nosystem] [<packages...>]\n"
         "                               - write an archive of the device's data to <file>.\n"
         "                                 If no -f option is supplied then the data is written\n"
@@ -250,11 +256,11 @@
         "  ADB_TRACE                    - Print debug information. A comma separated list of the following values\n"
         "                                 1 or all, adb, sockets, packets, rwx, usb, sync, sysdeps, transport, jdwp\n"
         "  ANDROID_SERIAL               - The serial number to connect to. -s takes priority over this if given.\n"
-        "  ANDROID_LOG_TAGS             - When used with the logcat option, only these debug tags are printed.\n"
-        );
+        "  ANDROID_LOG_TAGS             - When used with the logcat option, only these debug tags are printed.\n");
+    // clang-format on
 }
 
-static int usage() {
+int usage() {
     help();
     return 1;
 }
@@ -292,17 +298,14 @@
 // this expects that incoming data will use the shell protocol, in which case
 // stdout/stderr are routed independently and the remote exit code will be
 // returned.
-// if |output| is non-null, stdout will be appended to it instead.
-// if |err| is non-null, stderr will be appended to it instead.
-static int read_and_dump(int fd, bool use_shell_protocol=false, std::string* output=nullptr,
-                         std::string* err=nullptr) {
+// if |callback| is non-null, stdout/stderr output will be handled by it.
+int read_and_dump(int fd, bool use_shell_protocol = false,
+                  StandardStreamsCallbackInterface* callback = &DEFAULT_STANDARD_STREAMS_CALLBACK) {
     int exit_code = 0;
     if (fd < 0) return exit_code;
 
     std::unique_ptr<ShellProtocol> protocol;
     int length = 0;
-    FILE* outfile = stdout;
-    std::string* outstring = output;
 
     char raw_buffer[BUFSIZ];
     char* buffer_ptr = raw_buffer;
@@ -320,14 +323,13 @@
             if (!protocol->Read()) {
                 break;
             }
+            length = protocol->data_length();
             switch (protocol->id()) {
                 case ShellProtocol::kIdStdout:
-                    outfile = stdout;
-                    outstring = output;
+                    callback->OnStdout(buffer_ptr, length);
                     break;
                 case ShellProtocol::kIdStderr:
-                    outfile = stderr;
-                    outstring = err;
+                    callback->OnStderr(buffer_ptr, length);
                     break;
                 case ShellProtocol::kIdExit:
                     exit_code = protocol->data()[0];
@@ -343,17 +345,11 @@
             if (length <= 0) {
                 break;
             }
-        }
-
-        if (outstring == nullptr) {
-            fwrite(buffer_ptr, 1, length, outfile);
-            fflush(outfile);
-        } else {
-            outstring->append(buffer_ptr, length);
+            callback->OnStdout(buffer_ptr, length);
         }
     }
 
-    return exit_code;
+    return callback->Done(exit_code);
 }
 
 static void read_status_line(int fd, char* buf, size_t count)
@@ -371,19 +367,7 @@
     *buf = '\0';
 }
 
-static void copy_to_file(int inFd, int outFd) {
-    const size_t BUFSIZE = 32 * 1024;
-    char* buf = (char*) malloc(BUFSIZE);
-    if (buf == nullptr) fatal("couldn't allocate buffer for copy_to_file");
-    int len;
-    long total = 0;
-#ifdef _WIN32
-    int old_stdin_mode = -1;
-    int old_stdout_mode = -1;
-#endif
-
-    D("copy_to_file(%d -> %d)", inFd, outFd);
-
+static void stdinout_raw_prologue(int inFd, int outFd, int& old_stdin_mode, int& old_stdout_mode) {
     if (inFd == STDIN_FILENO) {
         stdin_raw_init();
 #ifdef _WIN32
@@ -402,6 +386,39 @@
         }
     }
 #endif
+}
+
+static void stdinout_raw_epilogue(int inFd, int outFd, int old_stdin_mode, int old_stdout_mode) {
+    if (inFd == STDIN_FILENO) {
+        stdin_raw_restore();
+#ifdef _WIN32
+        if (_setmode(STDIN_FILENO, old_stdin_mode) == -1) {
+            fatal_errno("could not restore stdin mode");
+        }
+#endif
+    }
+
+#ifdef _WIN32
+    if (outFd == STDOUT_FILENO) {
+        if (_setmode(STDOUT_FILENO, old_stdout_mode) == -1) {
+            fatal_errno("could not restore stdout mode");
+        }
+    }
+#endif
+}
+
+static void copy_to_file(int inFd, int outFd) {
+    const size_t BUFSIZE = 32 * 1024;
+    char* buf = (char*) malloc(BUFSIZE);
+    if (buf == nullptr) fatal("couldn't allocate buffer for copy_to_file");
+    int len;
+    long total = 0;
+    int old_stdin_mode = -1;
+    int old_stdout_mode = -1;
+
+    D("copy_to_file(%d -> %d)", inFd, outFd);
+
+    stdinout_raw_prologue(inFd, outFd, old_stdin_mode, old_stdout_mode);
 
     while (true) {
         if (inFd == STDIN_FILENO) {
@@ -426,22 +443,7 @@
         total += len;
     }
 
-    if (inFd == STDIN_FILENO) {
-        stdin_raw_restore();
-#ifdef _WIN32
-        if (_setmode(STDIN_FILENO, old_stdin_mode) == -1) {
-            fatal_errno("could not restore stdin mode");
-        }
-#endif
-    }
-
-#ifdef _WIN32
-    if (outFd == STDOUT_FILENO) {
-        if (_setmode(STDOUT_FILENO, old_stdout_mode) == -1) {
-            fatal_errno("could not restore stdout mode");
-        }
-    }
-#endif
+    stdinout_raw_epilogue(inFd, outFd, old_stdin_mode, old_stdout_mode);
 
     D("copy_to_file() finished after %lu bytes", total);
     free(buf);
@@ -1113,20 +1115,16 @@
     return true;
 }
 
-// Connects to the device "shell" service with |command| and prints the
-// resulting output.
-static int send_shell_command(TransportType transport_type, const char* serial,
-                              const std::string& command,
-                              bool disable_shell_protocol,
-                              std::string* output=nullptr,
-                              std::string* err=nullptr) {
+int send_shell_command(TransportType transport_type, const char* serial, const std::string& command,
+                       bool disable_shell_protocol, StandardStreamsCallbackInterface* callback) {
     int fd;
     bool use_shell_protocol = false;
 
     while (true) {
         bool attempt_connection = true;
 
-        // Use shell protocol if it's supported and the caller doesn't explicitly disable it.
+        // Use shell protocol if it's supported and the caller doesn't explicitly
+        // disable it.
         if (!disable_shell_protocol) {
             FeatureSet features;
             std::string error;
@@ -1148,13 +1146,13 @@
             }
         }
 
-        fprintf(stderr,"- waiting for device -\n");
+        fprintf(stderr, "- waiting for device -\n");
         if (!wait_for_device("wait-for-device", transport_type, serial)) {
             return 1;
         }
     }
 
-    int exit_code = read_and_dump(fd, use_shell_protocol, output, err);
+    int exit_code = read_and_dump(fd, use_shell_protocol, callback);
 
     if (adb_close(fd) < 0) {
         PLOG(ERROR) << "failure closing FD " << fd;
@@ -1163,45 +1161,6 @@
     return exit_code;
 }
 
-static int bugreport(TransportType transport_type, const char* serial, int argc,
-                     const char** argv) {
-    if (argc == 1) return send_shell_command(transport_type, serial, "bugreport", false);
-    if (argc != 2) return usage();
-
-    // Zipped bugreport option - will call 'bugreportz', which prints the location of the generated
-    // file, then pull it to the destination file provided by the user.
-    std::string dest_file = argv[1];
-    if (!android::base::EndsWith(argv[1], ".zip")) {
-        // TODO: use a case-insensitive comparison (like EndsWithIgnoreCase
-        dest_file += ".zip";
-    }
-    std::string output;
-
-    fprintf(stderr, "Bugreport is in progress and it could take minutes to complete.\n"
-            "Please be patient and do not cancel or disconnect your device until it completes.\n");
-    int status = send_shell_command(transport_type, serial, "bugreportz", false, &output, nullptr);
-    if (status != 0 || output.empty()) return status;
-    output = android::base::Trim(output);
-
-    if (android::base::StartsWith(output, BUGZ_OK_PREFIX)) {
-        const char* zip_file = &output[strlen(BUGZ_OK_PREFIX)];
-        std::vector<const char*> srcs{zip_file};
-        status = do_sync_pull(srcs, dest_file.c_str(), true, dest_file.c_str()) ? 0 : 1;
-        if (status != 0) {
-            fprintf(stderr, "Could not copy file '%s' to '%s'\n", zip_file, dest_file.c_str());
-        }
-        return status;
-    }
-    if (android::base::StartsWith(output, BUGZ_FAIL_PREFIX)) {
-        const char* error_message = &output[strlen(BUGZ_FAIL_PREFIX)];
-        fprintf(stderr, "Device failed to take a zipped bugreport: %s\n", error_message);
-        return -1;
-    }
-    fprintf(stderr, "Unexpected string (%s) returned by bugreportz, "
-            "device probably does not support -z option\n", output.c_str());
-    return -1;
-}
-
 static int logcat(TransportType transport, const char* serial, int argc, const char** argv) {
     char* log_tags = getenv("ANDROID_LOG_TAGS");
     std::string quoted = escape_arg(log_tags == nullptr ? "" : log_tags);
@@ -1222,6 +1181,29 @@
     return send_shell_command(transport, serial, cmd, true);
 }
 
+static void write_zeros(int bytes, int fd) {
+    int old_stdin_mode = -1;
+    int old_stdout_mode = -1;
+    char* buf = (char*) calloc(1, bytes);
+    if (buf == nullptr) fatal("couldn't allocate buffer for write_zeros");
+
+    D("write_zeros(%d) -> %d", bytes, fd);
+
+    stdinout_raw_prologue(-1, fd, old_stdin_mode, old_stdout_mode);
+
+    if (fd == STDOUT_FILENO) {
+        fwrite(buf, 1, bytes, stdout);
+        fflush(stdout);
+    } else {
+        adb_write(fd, buf, bytes);
+    }
+
+    stdinout_raw_prologue(-1, fd, old_stdin_mode, old_stdout_mode);
+
+    D("write_zeros() finished");
+    free(buf);
+}
+
 static int backup(int argc, const char** argv) {
     const char* filename = "backup.ab";
 
@@ -1302,6 +1284,9 @@
     printf("Now unlock your device and confirm the restore operation.\n");
     copy_to_file(tarFd, fd);
 
+    // Provide an in-band EOD marker in case the archive file is malformed
+    write_zeros(512*2, fd);
+
     // Wait until the other side finishes, or it'll get sent SIGHUP.
     copy_to_file(fd, STDOUT_FILENO);
 
@@ -1337,7 +1322,7 @@
     if (hint.find_first_of(OS_PATH_SEPARATORS) != std::string::npos) {  // NOLINT
         std::string cwd;
         if (!getcwd(&cwd)) {
-            fprintf(stderr, "adb: getcwd failed: %s\n", strerror(errno));
+            perror("adb: getcwd failed");
             return "";
         }
         return android::base::StringPrintf("%s%c%s", cwd.c_str(), OS_PATH_SEPARATOR, hint.c_str());
@@ -1437,6 +1422,16 @@
 #endif
 }
 
+static bool _use_legacy_install() {
+    FeatureSet features;
+    std::string error;
+    if (!adb_get_feature_set(&features, &error)) {
+        fprintf(stderr, "error: %s\n", error.c_str());
+        return true;
+    }
+    return !CanUseFeature(features, kFeatureCmd);
+}
+
 int adb_commandline(int argc, const char **argv) {
     int no_daemon = 0;
     int is_daemon = 0;
@@ -1461,18 +1456,9 @@
     }
     // TODO: also try TARGET_PRODUCT/TARGET_DEVICE as a hint
 
-    /* Validate and assign the server port */
-    const char* server_port_str = getenv("ANDROID_ADB_SERVER_PORT");
-    int server_port = DEFAULT_ADB_PORT;
-    if (server_port_str && strlen(server_port_str) > 0) {
-        server_port = strtol(server_port_str, nullptr, 0);
-        if (server_port <= 0 || server_port > 65535) {
-            fprintf(stderr,
-                    "adb: Env var ANDROID_ADB_SERVER_PORT must be a positive number less than 65536. Got \"%s\"\n",
-                    server_port_str);
-            return usage();
-        }
-    }
+    const char* server_host_str = nullptr;
+    const char* server_port_str = nullptr;
+    const char* server_socket_str = nullptr;
 
     // We need to check for -d and -e before we look at $ANDROID_SERIAL.
     const char* serial = nullptr;
@@ -1526,17 +1512,14 @@
         } else if (!strcmp(argv[0],"-a")) {
             gListenAll = 1;
         } else if (!strncmp(argv[0], "-H", 2)) {
-            const char *hostname = NULL;
             if (argv[0][2] == '\0') {
                 if (argc < 2) return usage();
-                hostname = argv[1];
+                server_host_str = argv[1];
                 argc--;
                 argv++;
             } else {
-                hostname = argv[0] + 2;
+                server_host_str = argv[0] + 2;
             }
-            adb_set_tcp_name(hostname);
-
         } else if (!strncmp(argv[0], "-P", 2)) {
             if (argv[0][2] == '\0') {
                 if (argc < 2) return usage();
@@ -1546,34 +1529,58 @@
             } else {
                 server_port_str = argv[0] + 2;
             }
-            if (strlen(server_port_str) > 0) {
-                server_port = (int) strtol(server_port_str, NULL, 0);
-                if (server_port <= 0 || server_port > 65535) {
-                    fprintf(stderr,
-                            "adb: port number must be a positive number less than 65536. Got \"%s\"\n",
-                            server_port_str);
-                    return usage();
-                }
-            } else {
-                fprintf(stderr,
-                "adb: port number must be a positive number less than 65536. Got empty string.\n");
-                return usage();
-            }
+        } else if (!strcmp(argv[0], "-L")) {
+            if (argc < 2) return usage();
+            server_socket_str = argv[1];
+            argc--;
+            argv++;
         } else {
-                /* out of recognized modifiers and flags */
+            /* out of recognized modifiers and flags */
             break;
         }
         argc--;
         argv++;
     }
 
+    if ((server_host_str || server_port_str) && server_socket_str) {
+        fprintf(stderr, "adb: -L is incompatible with -H or -P\n");
+        exit(1);
+    }
+
+    // If -L, -H, or -P are specified, ignore environment variables.
+    // Otherwise, prefer ADB_SERVER_SOCKET over ANDROID_ADB_SERVER_ADDRESS/PORT.
+    if (!(server_host_str || server_port_str || server_socket_str)) {
+        server_socket_str = server_socket_str ? server_socket_str : getenv("ADB_SERVER_SOCKET");
+    }
+
+    if (!server_socket_str) {
+        // tcp:1234 and tcp:localhost:1234 are different with -a, so don't default to localhost
+        server_host_str = server_host_str ? server_host_str : getenv("ANDROID_ADB_SERVER_ADDRESS");
+
+        long server_port = DEFAULT_ADB_PORT;
+        server_port_str = server_port_str ? server_port_str : getenv("ANDROID_ADB_SERVER_PORT");
+
+        int rc;
+        char* temp;
+        if (server_host_str) {
+            rc = asprintf(&temp, "tcp:%s:%ld", server_host_str, server_port);
+        } else {
+            rc = asprintf(&temp, "tcp:%ld", server_port);
+        }
+        if (rc < 0) {
+            fatal("failed to allocate server socket specification");
+        }
+        server_socket_str = temp;
+    }
+
+    adb_set_socket_spec(server_socket_str);
+
     // If none of -d, -e, or -s were specified, try $ANDROID_SERIAL.
     if (transport_type == kTransportAny && serial == nullptr) {
         serial = getenv("ANDROID_SERIAL");
     }
 
     adb_set_transport(transport_type, serial);
-    adb_set_tcp_specifics(server_port);
 
     if (is_server) {
         if (no_daemon || is_daemon) {
@@ -1581,9 +1588,9 @@
                 fprintf(stderr, "reply fd for adb server to client communication not specified.\n");
                 return usage();
             }
-            r = adb_server_main(is_daemon, server_port, ack_reply_fd);
+            r = adb_server_main(is_daemon, server_socket_str, ack_reply_fd);
         } else {
-            r = launch_server(server_port);
+            r = launch_server(server_socket_str);
         }
         if (r) {
             fprintf(stderr,"* could not start server *\n");
@@ -1737,7 +1744,8 @@
     } else if (!strcmp(argv[0], "root") || !strcmp(argv[0], "unroot")) {
         return adb_root(argv[0]) ? 0 : 1;
     } else if (!strcmp(argv[0], "bugreport")) {
-        return bugreport(transport_type, serial, argc, argv);
+        Bugreport bugreport;
+        return bugreport.DoIt(transport_type, serial, argc, argv);
     } else if (!strcmp(argv[0], "forward") || !strcmp(argv[0], "reverse")) {
         bool reverse = !strcmp(argv[0], "reverse");
         ++argv;
@@ -1831,17 +1839,10 @@
     }
     else if (!strcmp(argv[0], "install")) {
         if (argc < 2) return usage();
-        FeatureSet features;
-        std::string error;
-        if (!adb_get_feature_set(&features, &error)) {
-            fprintf(stderr, "error: %s\n", error.c_str());
-            return 1;
+        if (_use_legacy_install()) {
+            return install_app_legacy(transport_type, serial, argc, argv);
         }
-
-        if (CanUseFeature(features, kFeatureCmd)) {
-            return install_app(transport_type, serial, argc, argv);
-        }
-        return install_app_legacy(transport_type, serial, argc, argv);
+        return install_app(transport_type, serial, argc, argv);
     }
     else if (!strcmp(argv[0], "install-multiple")) {
         if (argc < 2) return usage();
@@ -1849,17 +1850,10 @@
     }
     else if (!strcmp(argv[0], "uninstall")) {
         if (argc < 2) return usage();
-        FeatureSet features;
-        std::string error;
-        if (!adb_get_feature_set(&features, &error)) {
-            fprintf(stderr, "error: %s\n", error.c_str());
-            return 1;
+        if (_use_legacy_install()) {
+            return uninstall_app_legacy(transport_type, serial, argc, argv);
         }
-
-        if (CanUseFeature(features, kFeatureCmd)) {
-            return uninstall_app(transport_type, serial, argc, argv);
-        }
-        return uninstall_app_legacy(transport_type, serial, argc, argv);
+        return uninstall_app(transport_type, serial, argc, argv);
     }
     else if (!strcmp(argv[0], "sync")) {
         std::string src;
@@ -2073,7 +2067,6 @@
     int i;
     struct stat sb;
     uint64_t total_size = 0;
-
     // Find all APK arguments starting at end.
     // All other arguments passed through verbatim.
     int first_apk = -1;
@@ -2098,7 +2091,14 @@
         return 1;
     }
 
-    std::string cmd = android::base::StringPrintf("exec:pm install-create -S %" PRIu64, total_size);
+    std::string install_cmd;
+    if (_use_legacy_install()) {
+        install_cmd = "exec:pm";
+    } else {
+        install_cmd = "exec:cmd package";
+    }
+
+    std::string cmd = android::base::StringPrintf("%s install-create -S %" PRIu64, install_cmd.c_str(), total_size);
     for (i = 1; i < first_apk; i++) {
         cmd += " " + escape_arg(argv[i]);
     }
@@ -2140,8 +2140,8 @@
         }
 
         std::string cmd = android::base::StringPrintf(
-                "exec:pm install-write -S %" PRIu64 " %d %d_%s -",
-                static_cast<uint64_t>(sb.st_size), session_id, i, adb_basename(file).c_str());
+                "%s install-write -S %" PRIu64 " %d %d_%s -",
+                install_cmd.c_str(), static_cast<uint64_t>(sb.st_size), session_id, i, adb_basename(file).c_str());
 
         int localFd = adb_open(file, O_RDONLY);
         if (localFd < 0) {
@@ -2176,8 +2176,8 @@
 finalize_session:
     // Commit session if we streamed everything okay; otherwise abandon
     std::string service =
-            android::base::StringPrintf("exec:pm install-%s %d",
-                                        success ? "commit" : "abandon", session_id);
+            android::base::StringPrintf("%s install-%s %d",
+                                        install_cmd.c_str(), success ? "commit" : "abandon", session_id);
     fd = adb_connect(service, &error);
     if (fd < 0) {
         fprintf(stderr, "Connect error for finalize: %s\n", error.c_str());
diff --git a/adb/commandline.h b/adb/commandline.h
new file mode 100644
index 0000000..0cf655c
--- /dev/null
+++ b/adb/commandline.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef COMMANDLINE_H
+#define COMMANDLINE_H
+
+#include "adb.h"
+
+// Callback used to handle the standard streams (stdout and stderr) sent by the
+// device's upon receiving a command.
+//
+class StandardStreamsCallbackInterface {
+  public:
+    StandardStreamsCallbackInterface() {
+    }
+    // Handles the stdout output from devices supporting the Shell protocol.
+    virtual void OnStdout(const char* buffer, int length) = 0;
+
+    // Handles the stderr output from devices supporting the Shell protocol.
+    virtual void OnStderr(const char* buffer, int length) = 0;
+
+    // Indicates the communication is finished and returns the appropriate error
+    // code.
+    //
+    // |status| has the status code returning by the underlying communication
+    // channels
+    virtual int Done(int status) = 0;
+
+  protected:
+    static void OnStream(std::string* string, FILE* stream, const char* buffer, int length) {
+        if (string != nullptr) {
+            string->append(buffer, length);
+        } else {
+            fwrite(buffer, 1, length, stream);
+            fflush(stream);
+        }
+    }
+
+  private:
+    DISALLOW_COPY_AND_ASSIGN(StandardStreamsCallbackInterface);
+};
+
+// Default implementation that redirects the streams to the equilavent host
+// stream or to a string
+// passed to the constructor.
+class DefaultStandardStreamsCallback : public StandardStreamsCallbackInterface {
+  public:
+    // If |stdout_str| is non-null, OnStdout will append to it.
+    // If |stderr_str| is non-null, OnStderr will append to it.
+    DefaultStandardStreamsCallback(std::string* stdout_str, std::string* stderr_str)
+        : stdout_str_(stdout_str), stderr_str_(stderr_str) {
+    }
+
+    void OnStdout(const char* buffer, int length) {
+        OnStream(stdout_str_, stdout, buffer, length);
+    }
+
+    void OnStderr(const char* buffer, int length) {
+        OnStream(stderr_str_, stderr, buffer, length);
+    }
+
+    int Done(int status) {
+        return status;
+    }
+
+  private:
+    std::string* stdout_str_;
+    std::string* stderr_str_;
+
+    DISALLOW_COPY_AND_ASSIGN(DefaultStandardStreamsCallback);
+};
+
+// Singleton.
+extern DefaultStandardStreamsCallback DEFAULT_STANDARD_STREAMS_CALLBACK;
+
+int adb_commandline(int argc, const char** argv);
+int usage();
+
+// Connects to the device "shell" service with |command| and prints the
+// resulting output.
+// if |callback| is non-null, stdout/stderr output will be handled by it.
+int send_shell_command(TransportType transport_type, const char* serial, const std::string& command,
+                       bool disable_shell_protocol, StandardStreamsCallbackInterface* callback =
+                                                        &DEFAULT_STANDARD_STREAMS_CALLBACK);
+
+#endif  // COMMANDLINE_H
diff --git a/adb/console.cpp b/adb/console.cpp
index e9b90a5..9563eac 100644
--- a/adb/console.cpp
+++ b/adb/console.cpp
@@ -32,7 +32,7 @@
 static std::string adb_construct_auth_command() {
     static const char auth_token_filename[] = ".emulator_console_auth_token";
 
-    std::string auth_token_path = adb_get_homedir_path(false);
+    std::string auth_token_path = adb_get_homedir_path();
     auth_token_path += OS_PATH_SEPARATOR;
     auth_token_path += auth_token_filename;
 
diff --git a/adb/get_my_path_darwin.cpp b/adb/get_my_path_darwin.cpp
deleted file mode 100644
index b0c962e..0000000
--- a/adb/get_my_path_darwin.cpp
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2007 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.
- */
-
-#import <Carbon/Carbon.h>
-#include <unistd.h>
-
-#include "adb.h"
-
-void get_my_path(char *s, size_t maxLen)
-{
-    CFBundleRef mainBundle = CFBundleGetMainBundle();
-    CFURLRef executableURL = CFBundleCopyExecutableURL(mainBundle);
-    CFStringRef executablePathString = CFURLCopyFileSystemPath(executableURL, kCFURLPOSIXPathStyle);
-    CFRelease(executableURL);
-
-    CFStringGetFileSystemRepresentation(executablePathString, s, maxLen);
-    CFRelease(executablePathString);
-}
-
diff --git a/adb/get_my_path_linux.cpp b/adb/get_my_path_linux.cpp
deleted file mode 100644
index 11c0b21..0000000
--- a/adb/get_my_path_linux.cpp
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2007 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 <limits.h>
-#include <stdio.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include "adb.h"
-
-void get_my_path(char *exe, size_t maxLen)
-{
-    char proc[64];
-    snprintf(proc, sizeof proc, "/proc/%d/exe", getpid());
-    int err = readlink(proc, exe, maxLen - 1);
-    if(err > 0) {
-        exe[err] = '\0';
-    } else {
-        exe[0] = '\0';
-    }
-}
-
diff --git a/adb/jdwp_service.cpp b/adb/jdwp_service.cpp
index 027821a..3135aa4 100644
--- a/adb/jdwp_service.cpp
+++ b/adb/jdwp_service.cpp
@@ -34,6 +34,7 @@
 
 #include "adb.h"
 #include "adb_io.h"
+#include "adb_unique_fd.h"
 #include "adb_utils.h"
 
 /* here's how these things work.
diff --git a/adb/shell_service.cpp b/adb/shell_service.cpp
index d83622c..01e206a 100644
--- a/adb/shell_service.cpp
+++ b/adb/shell_service.cpp
@@ -100,6 +100,7 @@
 #include "adb.h"
 #include "adb_io.h"
 #include "adb_trace.h"
+#include "adb_unique_fd.h"
 #include "adb_utils.h"
 #include "security_log_tags.h"
 
diff --git a/adb/socket_spec.cpp b/adb/socket_spec.cpp
index f8bbbb3..18e6e6d 100644
--- a/adb/socket_spec.cpp
+++ b/adb/socket_spec.cpp
@@ -131,6 +131,22 @@
     return StartsWith(spec, "tcp:");
 }
 
+bool is_local_socket_spec(const std::string& spec) {
+    for (const auto& it : kLocalSocketTypes) {
+        std::string prefix = it.first + ":";
+        if (StartsWith(spec, prefix.c_str())) {
+            return true;
+        }
+    }
+
+    std::string error;
+    std::string hostname;
+    if (!parse_tcp_spec(spec, &hostname, nullptr, &error)) {
+        return false;
+    }
+    return tcp_host_is_local(hostname);
+}
+
 int socket_spec_connect(const std::string& spec, std::string* error) {
     if (StartsWith(spec, "tcp:")) {
         std::string hostname;
diff --git a/adb/socket_spec.h b/adb/socket_spec.h
index 69efb07..6302da5 100644
--- a/adb/socket_spec.h
+++ b/adb/socket_spec.h
@@ -20,6 +20,7 @@
 
 // Returns true if the argument starts with a plausible socket prefix.
 bool is_socket_spec(const std::string& spec);
+bool is_local_socket_spec(const std::string& spec);
 
 int socket_spec_connect(const std::string& spec, std::string* error);
 int socket_spec_listen(const std::string& spec, std::string* error,
diff --git a/adb/test_adb.py b/adb/test_adb.py
index 0f1b034..cb3e0d8 100644
--- a/adb/test_adb.py
+++ b/adb/test_adb.py
@@ -207,6 +207,28 @@
             # reading the response from the adb emu kill command (on Windows).
             self.assertEqual(0, p.returncode)
 
+    def test_connect_ipv4_ipv6(self):
+        """Ensure that `adb connect localhost:1234` will try both IPv4 and IPv6.
+
+        Bug: http://b/30313466
+        """
+        ipv4 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        ipv4.bind(('127.0.0.1', 0))
+        ipv4.listen(1)
+
+        ipv6 = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
+        ipv6.bind(('::1', ipv4.getsockname()[1] + 1))
+        ipv6.listen(1)
+
+        for s in (ipv4, ipv6):
+            port = s.getsockname()[1]
+            output = subprocess.check_output(
+                ['adb', 'connect', 'localhost:{}'.format(port)])
+
+            self.assertEqual(
+                output.strip(), 'connected to localhost:{}'.format(port))
+            s.close()
+
 
 def main():
     random.seed(0)
diff --git a/base/file.cpp b/base/file.cpp
index 4e7ac82..721ab2f 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -20,14 +20,24 @@
 #include <fcntl.h>
 #include <sys/stat.h>
 #include <sys/types.h>
+#include <unistd.h>
 
+#include <memory>
 #include <string>
+#include <vector>
 
 #include "android-base/macros.h"  // For TEMP_FAILURE_RETRY on Darwin.
 #include "android-base/logging.h"
 #include "android-base/utf8.h"
 #include "utils/Compat.h"
 
+#if defined(__APPLE__)
+#include <mach-o/dyld.h>
+#endif
+#if defined(_WIN32)
+#include <windows.h>
+#endif
+
 namespace android {
 namespace base {
 
@@ -171,5 +181,55 @@
   return true;
 }
 
+#if !defined(_WIN32)
+bool Readlink(const std::string& path, std::string* result) {
+  result->clear();
+
+  // Most Linux file systems (ext2 and ext4, say) limit symbolic links to
+  // 4095 bytes. Since we'll copy out into the string anyway, it doesn't
+  // waste memory to just start there. We add 1 so that we can recognize
+  // whether it actually fit (rather than being truncated to 4095).
+  std::vector<char> buf(4095 + 1);
+  while (true) {
+    ssize_t size = readlink(path.c_str(), &buf[0], buf.size());
+    // Unrecoverable error?
+    if (size == -1) return false;
+    // It fit! (If size == buf.size(), it may have been truncated.)
+    if (static_cast<size_t>(size) < buf.size()) {
+      result->assign(&buf[0], size);
+      return true;
+    }
+    // Double our buffer and try again.
+    buf.resize(buf.size() * 2);
+  }
+}
+#endif
+
+std::string GetExecutablePath() {
+#if defined(__linux__)
+  std::string path;
+  android::base::Readlink("/proc/self/exe", &path);
+  return path;
+#elif defined(__APPLE__)
+  char path[PATH_MAX + 1];
+  uint32_t path_len = sizeof(path);
+  int rc = _NSGetExecutablePath(path, &path_len);
+  if (rc < 0) {
+    std::unique_ptr<char> path_buf(new char[path_len]);
+    _NSGetExecutablePath(path_buf.get(), &path_len);
+    return path_buf.get();
+  }
+  return path;
+#elif defined(_WIN32)
+  char path[PATH_MAX + 1];
+  DWORD result = GetModuleFileName(NULL, path, sizeof(path) - 1);
+  if (result == 0 || result == sizeof(path) - 1) return "";
+  path[PATH_MAX - 1] = 0;
+  return path;
+#else
+#error unknown OS
+#endif
+}
+
 }  // namespace base
 }  // namespace android
diff --git a/base/file_test.cpp b/base/file_test.cpp
index 17755bf..f5d6062 100644
--- a/base/file_test.cpp
+++ b/base/file_test.cpp
@@ -110,3 +110,33 @@
   ASSERT_FALSE(android::base::RemoveFileIfExists(td.path, &err));
   ASSERT_EQ("is not a regular or symbol link file", err);
 }
+
+TEST(file, Readlink) {
+#if !defined(_WIN32)
+  // Linux doesn't allow empty symbolic links.
+  std::string min("x");
+  // ext2 and ext4 both have PAGE_SIZE limits.
+  std::string max(static_cast<size_t>(4096 - 1), 'x');
+
+  TemporaryDir td;
+  std::string min_path{std::string(td.path) + "/" + "min"};
+  std::string max_path{std::string(td.path) + "/" + "max"};
+
+  ASSERT_EQ(0, symlink(min.c_str(), min_path.c_str()));
+  ASSERT_EQ(0, symlink(max.c_str(), max_path.c_str()));
+
+  std::string result;
+
+  result = "wrong";
+  ASSERT_TRUE(android::base::Readlink(min_path, &result));
+  ASSERT_EQ(min, result);
+
+  result = "wrong";
+  ASSERT_TRUE(android::base::Readlink(max_path, &result));
+  ASSERT_EQ(max, result);
+#endif
+}
+
+TEST(file, GetExecutablePath) {
+  ASSERT_NE("", android::base::GetExecutablePath());
+}
diff --git a/base/include/android-base/file.h b/base/include/android-base/file.h
index aa18ea7..5b22a65 100644
--- a/base/include/android-base/file.h
+++ b/base/include/android-base/file.h
@@ -43,6 +43,12 @@
 
 bool RemoveFileIfExists(const std::string& path, std::string* err = nullptr);
 
+#if !defined(_WIN32)
+bool Readlink(const std::string& path, std::string* result);
+#endif
+
+std::string GetExecutablePath();
+
 }  // namespace base
 }  // namespace android
 
diff --git a/base/include/android-base/logging.h b/base/include/android-base/logging.h
index 66d60c9..721ecce 100644
--- a/base/include/android-base/logging.h
+++ b/base/include/android-base/logging.h
@@ -265,7 +265,7 @@
 // MakeEagerEvaluator to infer the types of LHS and RHS.
 template <typename LHS, typename RHS>
 struct EagerEvaluator {
-  EagerEvaluator(LHS l, RHS r) : lhs(l), rhs(r) {
+  constexpr EagerEvaluator(LHS l, RHS r) : lhs(l), rhs(r) {
   }
   LHS lhs;
   RHS rhs;
@@ -273,7 +273,7 @@
 
 // Helper function for CHECK_xx.
 template <typename LHS, typename RHS>
-static inline EagerEvaluator<LHS, RHS> MakeEagerEvaluator(LHS lhs, RHS rhs) {
+constexpr EagerEvaluator<LHS, RHS> MakeEagerEvaluator(LHS lhs, RHS rhs) {
   return EagerEvaluator<LHS, RHS>(lhs, rhs);
 }
 
diff --git a/base/include/android-base/macros.h b/base/include/android-base/macros.h
index ee16d02..88bbe8a 100644
--- a/base/include/android-base/macros.h
+++ b/base/include/android-base/macros.h
@@ -41,18 +41,9 @@
 // Note, that most uses of DISALLOW_ASSIGN and DISALLOW_COPY are broken
 // semantically, one should either use disallow both or neither. Try to
 // avoid these in new code.
-//
-// When building with C++11 toolchains, just use the language support
-// for explicitly deleted methods.
-#if __cplusplus >= 201103L
 #define DISALLOW_COPY_AND_ASSIGN(TypeName) \
   TypeName(const TypeName&) = delete;      \
   void operator=(const TypeName&) = delete
-#else
-#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
-  TypeName(const TypeName&);               \
-  void operator=(const TypeName&)
-#endif
 
 // A macro to disallow all the implicit constructors, namely the
 // default constructor, copy constructor and operator= functions.
@@ -60,18 +51,9 @@
 // This should be used in the private: declarations for a class
 // that wants to prevent anyone from instantiating it. This is
 // especially useful for classes containing only static methods.
-//
-// When building with C++11 toolchains, just use the language support
-// for explicitly deleted methods.
-#if __cplusplus >= 201103L
 #define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
   TypeName() = delete;                           \
   DISALLOW_COPY_AND_ASSIGN(TypeName)
-#else
-#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
-  TypeName();                                    \
-  DISALLOW_COPY_AND_ASSIGN(TypeName)
-#endif
 
 // The arraysize(arr) macro returns the # of elements in an array arr.
 // The expression is a compile-time constant, and therefore can be
@@ -174,10 +156,10 @@
 //  only if there are no statements on the execution path between it and the
 //  next switch label.
 //
-//  When compiled with clang in C++11 mode, the FALLTHROUGH_INTENDED macro is
-//  expanded to [[clang::fallthrough]] attribute, which is analysed when
-//  performing switch labels fall-through diagnostic ('-Wimplicit-fallthrough').
-//  See clang documentation on language extensions for details:
+//  When compiled with clang, the FALLTHROUGH_INTENDED macro is expanded to
+//  [[clang::fallthrough]] attribute, which is analysed when performing switch
+//  labels fall-through diagnostic ('-Wimplicit-fallthrough'). See clang
+//  documentation on language extensions for details:
 //  http://clang.llvm.org/docs/LanguageExtensions.html#clang__fallthrough
 //
 //  When used with unsupported compilers, the FALLTHROUGH_INTENDED macro has no
@@ -185,7 +167,7 @@
 //
 //  In either case this macro has no effect on runtime behavior and performance
 //  of code.
-#if defined(__clang__) && __cplusplus >= 201103L && defined(__has_warning)
+#if defined(__clang__) && defined(__has_warning)
 #if __has_feature(cxx_attributes) && __has_warning("-Wimplicit-fallthrough")
 #define FALLTHROUGH_INTENDED [[clang::fallthrough]]  // NOLINT
 #endif
diff --git a/base/include/android-base/thread_annotations.h b/base/include/android-base/thread_annotations.h
index 2422102..fbb5923 100644
--- a/base/include/android-base/thread_annotations.h
+++ b/base/include/android-base/thread_annotations.h
@@ -29,6 +29,9 @@
 #define SCOPED_CAPABILITY \
       THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
 
+#define SHARED_CAPABILITY(...) \
+      THREAD_ANNOTATION_ATTRIBUTE__(shared_capability(__VA_ARGS__))
+
 #define GUARDED_BY(x) \
       THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
 
@@ -77,6 +80,27 @@
 #define RETURN_CAPABILITY(x) \
       THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
 
+#define EXCLUSIVE_LOCK_FUNCTION(...) \
+      THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__))
+
+#define EXCLUSIVE_TRYLOCK_FUNCTION(...) \
+      THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__))
+
+#define SHARED_LOCK_FUNCTION(...) \
+      THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__))
+
+#define SHARED_TRYLOCK_FUNCTION(...) \
+      THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__))
+
+#define UNLOCK_FUNCTION(...) \
+      THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__))
+
+#define SCOPED_LOCKABLE \
+      THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
+
+#define LOCK_RETURNED(x) \
+      THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
+
 #define NO_THREAD_SAFETY_ANALYSIS \
       THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
 
diff --git a/base/include/android-base/unique_fd.h b/base/include/android-base/unique_fd.h
index 869e60f..c323311 100644
--- a/base/include/android-base/unique_fd.h
+++ b/base/include/android-base/unique_fd.h
@@ -95,4 +95,14 @@
 }  // namespace base
 }  // namespace android
 
+template <typename T>
+int close(const android::base::unique_fd_impl<T>&)
+#if defined(__clang__)
+  __attribute__((__unavailable__(
+#else
+  __attribute__((__error__(
+#endif
+    "close called on unique_fd"
+  )));
+
 #endif  // ANDROID_BASE_UNIQUE_FD_H
diff --git a/fastboot/Android.mk b/fastboot/Android.mk
index 28e3d4b..7112d1d 100644
--- a/fastboot/Android.mk
+++ b/fastboot/Android.mk
@@ -43,15 +43,15 @@
 
 LOCAL_CFLAGS += -DFASTBOOT_REVISION='"$(fastboot_version)"'
 
-LOCAL_SRC_FILES_linux := usb_linux.cpp util_linux.cpp
+LOCAL_SRC_FILES_linux := usb_linux.cpp
 LOCAL_STATIC_LIBRARIES_linux := libselinux
 
-LOCAL_SRC_FILES_darwin := usb_osx.cpp util_osx.cpp
+LOCAL_SRC_FILES_darwin := usb_osx.cpp
 LOCAL_STATIC_LIBRARIES_darwin := libselinux
 LOCAL_LDLIBS_darwin := -lpthread -framework CoreFoundation -framework IOKit -framework Carbon
 LOCAL_CFLAGS_darwin := -Wno-unused-parameter
 
-LOCAL_SRC_FILES_windows := usb_windows.cpp util_windows.cpp
+LOCAL_SRC_FILES_windows := usb_windows.cpp
 LOCAL_STATIC_LIBRARIES_windows := AdbWinApi
 LOCAL_REQUIRED_MODULES_windows := AdbWinApi
 LOCAL_LDLIBS_windows := -lws2_32
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index df0f651..987ba83 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -47,6 +47,7 @@
 #include <utility>
 #include <vector>
 
+#include <android-base/file.h>
 #include <android-base/macros.h>
 #include <android-base/parseint.h>
 #include <android-base/parsenetaddress.h>
@@ -116,7 +117,7 @@
 
 static std::string find_item_given_name(const char* img_name, const char* product) {
     if(product) {
-        std::string path = get_my_path();
+        std::string path = android::base::GetExecutablePath();
         path.erase(path.find_last_of('/'));
         return android::base::StringPrintf("%s/../../../target/product/%s/%s",
                                            path.c_str(), product, img_name);
diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h
index c2ea551..6699b6a 100644
--- a/fastboot/fastboot.h
+++ b/fastboot/fastboot.h
@@ -72,8 +72,6 @@
 char *mkmsg(const char *fmt, ...);
 __attribute__((__noreturn__)) void die(const char *fmt, ...);
 
-std::string get_my_path();
-
 /* Current product */
 extern char cur_product[FB_RESPONSE_SZ + 1];
 
diff --git a/fastboot/util_linux.cpp b/fastboot/util_linux.cpp
deleted file mode 100644
index 2c6aedb..0000000
--- a/fastboot/util_linux.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2008 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.
- */
-
-#include "fastboot.h"
-
-#include <unistd.h>
-#include <limits.h>
-
-#include <android-base/stringprintf.h>
-
-std::string get_my_path() {
-    std::string proc = android::base::StringPrintf("/proc/%d/exe", getpid());
-    char path[PATH_MAX + 1];
-    int rc = readlink(proc.c_str(), path, sizeof(path) - 1);
-    if (rc == -1) return "";
-    path[rc] = '\0';
-    return path;
-}
diff --git a/fastboot/util_osx.cpp b/fastboot/util_osx.cpp
deleted file mode 100644
index 4bae7c4..0000000
--- a/fastboot/util_osx.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2008 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.
- */
-
-#include "fastboot.h"
-
-#import <Carbon/Carbon.h>
-#include <unistd.h>
-
-std::string get_my_path() {
-    CFBundleRef mainBundle = CFBundleGetMainBundle();
-    CFURLRef executableURL = CFBundleCopyExecutableURL(mainBundle);
-    CFStringRef executablePathString = CFURLCopyFileSystemPath(executableURL, kCFURLPOSIXPathStyle);
-    CFRelease(executableURL);
-
-    char path[PATH_MAX + 1];
-    CFStringGetFileSystemRepresentation(executablePathString, path, sizeof(PATH_MAX)-1);
-    CFRelease(executablePathString);
-
-    return path;
-}
diff --git a/fastboot/util_windows.cpp b/fastboot/util_windows.cpp
deleted file mode 100644
index 3b22c55..0000000
--- a/fastboot/util_windows.cpp
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2008 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.
- */
-
-#include "fastboot.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <limits.h>
-
-#include <windows.h>
-
-std::string get_my_path() {
-    char path[PATH_MAX + 1];
-
-    DWORD result = GetModuleFileName(NULL, path, sizeof(path) - 1);
-    if (result == 0 || result == sizeof(path) - 1) return "";
-    path[PATH_MAX - 1] = 0;
-
-    return path;
-}
diff --git a/init/devices.cpp b/init/devices.cpp
index 830b74c..bad04ae 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -14,21 +14,23 @@
  * limitations under the License.
  */
 
+#include <dirent.h>
 #include <errno.h>
+#include <fcntl.h>
 #include <fnmatch.h>
+#include <libgen.h>
 #include <stddef.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include <fcntl.h>
-#include <dirent.h>
-#include <unistd.h>
 #include <string.h>
-
 #include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
 #include <sys/un.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
 #include <linux/netlink.h>
 
 #include <memory>
@@ -39,8 +41,6 @@
 #include <selinux/avc.h>
 
 #include <private/android_filesystem_config.h>
-#include <sys/time.h>
-#include <sys/wait.h>
 
 #include <android-base/file.h>
 #include <android-base/stringprintf.h>
@@ -541,30 +541,49 @@
     return links;
 }
 
+static void make_link_init(const char* oldpath, const char* newpath) {
+  const char* slash = strrchr(newpath, '/');
+  if (!slash) return;
+
+  if (mkdir_recursive(dirname(newpath), 0755)) {
+    PLOG(ERROR) << "Failed to create directory " << dirname(newpath);
+  }
+
+  if (symlink(oldpath, newpath) && errno != EEXIST) {
+    PLOG(ERROR) << "Failed to symlink " << oldpath << " to " << newpath;
+  }
+}
+
+static void remove_link(const char* oldpath, const char* newpath) {
+  std::string path;
+  if (android::base::Readlink(newpath, &path) && path == oldpath) unlink(newpath);
+}
+
 static void handle_device(const char *action, const char *devpath,
         const char *path, int block, int major, int minor, char **links)
 {
-    int i;
-
     if(!strcmp(action, "add")) {
         make_device(devpath, path, block, major, minor, (const char **)links);
         if (links) {
-            for (i = 0; links[i]; i++)
+            for (int i = 0; links[i]; i++) {
                 make_link_init(devpath, links[i]);
+            }
         }
     }
 
     if(!strcmp(action, "remove")) {
         if (links) {
-            for (i = 0; links[i]; i++)
+            for (int i = 0; links[i]; i++) {
                 remove_link(devpath, links[i]);
+            }
         }
         unlink(devpath);
     }
 
     if (links) {
-        for (i = 0; links[i]; i++)
+        for (int i = 0; links[i]; i++) {
             free(links[i]);
+        }
         free(links);
     }
 }
diff --git a/init/readme.txt b/init/readme.txt
index 4c04f17..bc3874a 100644
--- a/init/readme.txt
+++ b/init/readme.txt
@@ -188,6 +188,14 @@
   Scheduling priority of the service process. This value has to be in range
   -20 to 19. Default priority is 0. Priority is set via setpriority().
 
+namespace <pid|mnt>
+  Enter a new PID or mount namespace when forking the service.
+
+oom_score_adjust <value>
+   Sets the child's /proc/self/oom_score_adj to the specified value,
+   which must range from -1000 to 1000.
+
+
 Triggers
 --------
 Triggers are strings which can be used to match certain kinds of
diff --git a/init/service.cpp b/init/service.cpp
index 32aafd6..1caf7c6 100644
--- a/init/service.cpp
+++ b/init/service.cpp
@@ -163,7 +163,7 @@
     : name_(name), classname_(classname), flags_(0), pid_(0), time_started_(0),
       time_crashed_(0), nr_crashed_(0), uid_(0), gid_(0), namespace_flags_(0),
       seclabel_(""), ioprio_class_(IoSchedClass_NONE), ioprio_pri_(0),
-      priority_(0), args_(args) {
+      priority_(0), oom_score_adjust_(-1000), args_(args) {
     onrestart_.InitSingleTrigger("onrestart");
 }
 
@@ -176,7 +176,7 @@
       time_started_(0), time_crashed_(0), nr_crashed_(0), uid_(uid), gid_(gid),
       supp_gids_(supp_gids), namespace_flags_(namespace_flags),
       seclabel_(seclabel), ioprio_class_(IoSchedClass_NONE), ioprio_pri_(0),
-      priority_(0), args_(args) {
+      priority_(0), oom_score_adjust_(-1000), args_(args) {
     onrestart_.InitSingleTrigger("onrestart");
 }
 
@@ -419,6 +419,18 @@
     return true;
 }
 
+bool Service::ParseOomScoreAdjust(const std::vector<std::string>& args, std::string* err) {
+    oom_score_adjust_ = std::stol(args[1], 0, 10);
+
+    if (oom_score_adjust_ < -1000 || oom_score_adjust_ > 1000) {
+        *err = "oom_score_adjust value must be in range -1000 - +1000";
+        return false;
+    }
+
+    return true;
+}
+
+
 bool Service::ParseSeclabel(const std::vector<std::string>& args, std::string* err) {
     seclabel_ = args[1];
     return true;
@@ -476,6 +488,8 @@
         {"keycodes",    {1,     kMax, &Service::ParseKeycodes}},
         {"oneshot",     {0,     0,    &Service::ParseOneshot}},
         {"onrestart",   {1,     kMax, &Service::ParseOnrestart}},
+        {"oom_score_adjust",
+                        {1,     1,    &Service::ParseOomScoreAdjust}},
         {"namespace",   {1,     2,    &Service::ParseNamespace}},
         {"seclabel",    {1,     1,    &Service::ParseSeclabel}},
         {"setenv",      {2,     2,    &Service::ParseSetenv}},
@@ -611,6 +625,14 @@
         return false;
     }
 
+    if (oom_score_adjust_ != -1000) {
+        std::string oom_str = StringPrintf("%d", oom_score_adjust_);
+        std::string oom_file = StringPrintf("/proc/%d/oom_score_adj", pid);
+        if (!WriteStringToFile(oom_str, oom_file)) {
+            PLOG(ERROR) << "couldn't write oom_score_adj: " << strerror(errno);
+        }
+    }
+
     time_started_ = gettime();
     pid_ = pid;
     flags_ |= SVC_RUNNING;
diff --git a/init/service.h b/init/service.h
index fb03a07..4a3412c 100644
--- a/init/service.h
+++ b/init/service.h
@@ -126,6 +126,7 @@
     bool ParseKeycodes(const std::vector<std::string>& args, std::string* err);
     bool ParseOneshot(const std::vector<std::string>& args, std::string* err);
     bool ParseOnrestart(const std::vector<std::string>& args, std::string* err);
+    bool ParseOomScoreAdjust(const std::vector<std::string>& args, std::string* err);
     bool ParseNamespace(const std::vector<std::string>& args, std::string* err);
     bool ParseSeclabel(const std::vector<std::string>& args, std::string* err);
     bool ParseSetenv(const std::vector<std::string>& args, std::string* err);
@@ -165,6 +166,8 @@
     int ioprio_pri_;
     int priority_;
 
+    int oom_score_adjust_;
+
     std::vector<std::string> args_;
 };
 
diff --git a/init/util.cpp b/init/util.cpp
index 10ab1c7..e451edd 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -270,40 +270,6 @@
     }
 }
 
-void make_link_init(const char *oldpath, const char *newpath)
-{
-    int ret;
-    char buf[256];
-    const char *slash;
-    int width;
-
-    slash = strrchr(newpath, '/');
-    if (!slash)
-        return;
-    width = slash - newpath;
-    if (width <= 0 || width > (int)sizeof(buf) - 1)
-        return;
-    memcpy(buf, newpath, width);
-    buf[width] = 0;
-    ret = mkdir_recursive(buf, 0755);
-    if (ret) PLOG(ERROR) << "Failed to create directory " << buf;
-
-    ret = symlink(oldpath, newpath);
-    if (ret && errno != EEXIST) PLOG(ERROR) << "Failed to symlink " << oldpath << " to " << newpath;
-}
-
-void remove_link(const char *oldpath, const char *newpath)
-{
-    char path[256];
-    ssize_t ret;
-    ret = readlink(newpath, path, sizeof(path) - 1);
-    if (ret <= 0)
-        return;
-    path[ret] = 0;
-    if (!strcmp(path, oldpath))
-        unlink(newpath);
-}
-
 int wait_for_file(const char *filename, int timeout)
 {
     struct stat info;
diff --git a/init/util.h b/init/util.h
index f020da8..5fcbdf0 100644
--- a/init/util.h
+++ b/init/util.h
@@ -51,8 +51,6 @@
 
 int mkdir_recursive(const char *pathname, mode_t mode);
 void sanitize(char *p);
-void make_link_init(const char *oldpath, const char *newpath);
-void remove_link(const char *oldpath, const char *newpath);
 int wait_for_file(const char *filename, int timeout);
 void import_kernel_cmdline(bool in_qemu,
                            const std::function<void(const std::string&, const std::string&, bool)>&);
diff --git a/libcutils/socket_network_client_unix.c b/libcutils/socket_network_client_unix.c
index 3300b8f..46818d6 100644
--- a/libcutils/socket_network_client_unix.c
+++ b/libcutils/socket_network_client_unix.c
@@ -59,64 +59,63 @@
         return -1;
     }
 
-    // TODO: try all the addresses if there's more than one?
-    int family = addrs[0].ai_family;
-    int protocol = addrs[0].ai_protocol;
-    socklen_t addr_len = addrs[0].ai_addrlen;
-    struct sockaddr_storage addr;
-    memcpy(&addr, addrs[0].ai_addr, addr_len);
+    int result = -1;
+    for (struct addrinfo* addr = addrs; addr != NULL; addr = addr->ai_next) {
+        // The Mac doesn't have SOCK_NONBLOCK.
+        int s = socket(addr->ai_family, type, addr->ai_protocol);
+        if (s == -1 || toggle_O_NONBLOCK(s) == -1) return -1;
+
+        int rc = connect(s, addr->ai_addr, addr->ai_addrlen);
+        if (rc == 0) {
+            result = toggle_O_NONBLOCK(s);
+            break;
+        } else if (rc == -1 && errno != EINPROGRESS) {
+            close(s);
+            continue;
+        }
+
+        fd_set r_set;
+        FD_ZERO(&r_set);
+        FD_SET(s, &r_set);
+        fd_set w_set = r_set;
+
+        struct timeval ts;
+        ts.tv_sec = timeout;
+        ts.tv_usec = 0;
+        if ((rc = select(s + 1, &r_set, &w_set, NULL, (timeout != 0) ? &ts : NULL)) == -1) {
+            close(s);
+            break;
+        }
+        if (rc == 0) {  // we had a timeout
+            errno = ETIMEDOUT;
+            close(s);
+            break;
+        }
+
+        int error = 0;
+        socklen_t len = sizeof(error);
+        if (FD_ISSET(s, &r_set) || FD_ISSET(s, &w_set)) {
+            if (getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
+                close(s);
+                break;
+            }
+        } else {
+            close(s);
+            break;
+        }
+
+        if (error) {  // check if we had a socket error
+            // TODO: Update the timeout.
+            errno = error;
+            close(s);
+            continue;
+        }
+
+        result = toggle_O_NONBLOCK(s);
+    }
 
     freeaddrinfo(addrs);
-
-    // The Mac doesn't have SOCK_NONBLOCK.
-    int s = socket(family, type, protocol);
-    if (s == -1 || toggle_O_NONBLOCK(s) == -1) return -1;
-
-    int rc = connect(s, (const struct sockaddr*) &addr, addr_len);
-    if (rc == 0) {
-        return toggle_O_NONBLOCK(s);
-    } else if (rc == -1 && errno != EINPROGRESS) {
-        close(s);
-        return -1;
-    }
-
-    fd_set r_set;
-    FD_ZERO(&r_set);
-    FD_SET(s, &r_set);
-    fd_set w_set = r_set;
-
-    struct timeval ts;
-    ts.tv_sec = timeout;
-    ts.tv_usec = 0;
-    if ((rc = select(s + 1, &r_set, &w_set, NULL, (timeout != 0) ? &ts : NULL)) == -1) {
-        close(s);
-        return -1;
-    }
-    if (rc == 0) {   // we had a timeout
-        errno = ETIMEDOUT;
-        close(s);
-        return -1;
-    }
-
-    int error = 0;
-    socklen_t len = sizeof(error);
-    if (FD_ISSET(s, &r_set) || FD_ISSET(s, &w_set)) {
-        if (getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
-            close(s);
-            return -1;
-        }
-    } else {
-        close(s);
-        return -1;
-    }
-
-    if (error) {  // check if we had a socket error
-        errno = error;
-        close(s);
-        return -1;
-    }
-
-    return toggle_O_NONBLOCK(s);
+    return result;
 }
 
 int socket_network_client(const char* host, int port, int type) {
diff --git a/liblog/event_tag_map.c b/liblog/event_tag_map.c
index 3cb04cf..345f0d3 100644
--- a/liblog/event_tag_map.c
+++ b/liblog/event_tag_map.c
@@ -99,6 +99,9 @@
     if (processFile(newTagMap) != 0)
         goto fail;
 
+    if (fd >= 0)
+      close(fd);
+
     return newTagMap;
 
 fail:
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index fa95733..1ecf99c 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -508,7 +508,8 @@
     LogBufferElementCollection::iterator it;
 
     if (caller_uid != AID_ROOT) {
-        // Only here if clearAll condition (pruneRows == ULONG_MAX)
+        // Only here if clear all request from non system source, so chatty
+        // filter logistics is not required.
         it = mLastSet[id] ? mLast[id] : mLogElements.begin();
         while (it != mLogElements.end()) {
             LogBufferElement *element = *it;
@@ -534,7 +535,9 @@
             }
 
             it = erase(it);
-            pruneRows--;
+            if (--pruneRows == 0) {
+                break;
+            }
         }
         LogTimeEntry::unlock();
         return busy;
@@ -678,7 +681,7 @@
                         && ((!gc && (element->getPid() == worstPid))
                            || (mLastWorstPidOfSystem[id].find(element->getPid())
                                 == mLastWorstPidOfSystem[id].end()))) {
-                    mLastWorstPidOfSystem[id][key] = it;
+                    mLastWorstPidOfSystem[id][element->getPid()] = it;
                 }
                 if ((!gc && !worstPid && (key == worst))
                         || (mLastWorst[id].find(key) == mLastWorst[id].end())) {
diff --git a/sdcard/fuse.cpp b/sdcard/fuse.cpp
index 6a972ea..f549606 100644
--- a/sdcard/fuse.cpp
+++ b/sdcard/fuse.cpp
@@ -23,9 +23,6 @@
 /* FUSE_CANONICAL_PATH is not currently upstreamed */
 #define FUSE_CANONICAL_PATH 2016
 
-#define PROP_SDCARDFS_DEVICE "ro.sys.sdcardfs"
-#define PROP_SDCARDFS_USER "persist.sys.sdcardfs"
-
 #define FUSE_UNKNOWN_INO 0xffffffff
 
 /* Pseudo-error constant used to indicate that no fuse status is needed
diff --git a/sdcard/sdcard.cpp b/sdcard/sdcard.cpp
index 3481ec3..70bbf26 100644
--- a/sdcard/sdcard.cpp
+++ b/sdcard/sdcard.cpp
@@ -28,11 +28,16 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include <android-base/file.h>
 #include <android-base/logging.h>
 #include <android-base/macros.h>
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
 
 #include <cutils/fs.h>
 #include <cutils/multiuser.h>
+#include <cutils/properties.h>
+
 #include <packagelistparser/packagelistparser.h>
 
 #include <libminijail.h>
@@ -73,6 +78,9 @@
 
 #include "fuse.h"
 
+#define PROP_SDCARDFS_DEVICE "ro.sys.sdcardfs"
+#define PROP_SDCARDFS_USER "persist.sys.sdcardfs"
+
 /* Supplementary groups to execute with. */
 static const gid_t kGroups[1] = { AID_PACKAGE_INFO };
 
@@ -307,6 +315,98 @@
     LOG(FATAL) << "terminated prematurely";
 }
 
+static bool sdcardfs_setup(const std::string& source_path, const std::string& dest_path, uid_t fsuid,
+                        gid_t fsgid, bool multi_user, userid_t userid, gid_t gid, mode_t mask) {
+    std::string opts = android::base::StringPrintf("fsuid=%d,fsgid=%d,%smask=%d,userid=%d,gid=%d",
+            fsuid, fsgid, multi_user?"multiuser,":"", mask, userid, gid);
+
+    if (mount(source_path.c_str(), dest_path.c_str(), "sdcardfs",
+                        MS_NOSUID | MS_NODEV | MS_NOEXEC | MS_NOATIME, opts.c_str()) != 0) {
+        PLOG(ERROR) << "failed to mount sdcardfs filesystem";
+        return false;
+    }
+
+    return true;
+}
+
+static void run_sdcardfs(const std::string& source_path, const std::string& label, uid_t uid,
+        gid_t gid, userid_t userid, bool multi_user, bool full_write) {
+    std::string dest_path_default = "/mnt/runtime/default/" + label;
+    std::string dest_path_read = "/mnt/runtime/read/" + label;
+    std::string dest_path_write = "/mnt/runtime/write/" + label;
+
+    umask(0);
+    if (multi_user) {
+        // Multi-user storage is fully isolated per user, so "other"
+        // permissions are completely masked off.
+        if (!sdcardfs_setup(source_path, dest_path_default, uid, gid, multi_user, userid,
+                                                      AID_SDCARD_RW, 0006)
+                || !sdcardfs_setup(source_path, dest_path_read, uid, gid, multi_user, userid,
+                                                      AID_EVERYBODY, 0027)
+                || !sdcardfs_setup(source_path, dest_path_write, uid, gid, multi_user, userid,
+                                                      AID_EVERYBODY, full_write ? 0007 : 0027)) {
+            LOG(FATAL) << "failed to sdcardfs_setup";
+        }
+    } else {
+        // Physical storage is readable by all users on device, but
+        // the Android directories are masked off to a single user
+        // deep inside attr_from_stat().
+        if (!sdcardfs_setup(source_path, dest_path_default, uid, gid, multi_user, userid,
+                                                      AID_SDCARD_RW, 0006)
+                || !sdcardfs_setup(source_path, dest_path_read, uid, gid, multi_user, userid,
+                                                      AID_EVERYBODY, full_write ? 0027 : 0022)
+                || !sdcardfs_setup(source_path, dest_path_write, uid, gid, multi_user, userid,
+                                                      AID_EVERYBODY, full_write ? 0007 : 0022)) {
+            LOG(FATAL) << "failed to sdcardfs_setup";
+        }
+    }
+
+    // Will abort if priv-dropping fails.
+    drop_privs(uid, gid);
+
+    if (multi_user) {
+        std::string obb_path = source_path + "/obb";
+        fs_prepare_dir(obb_path.c_str(), 0775, uid, gid);
+    }
+
+    exit(0);
+}
+
+static bool supports_sdcardfs(void) {
+    std::string filesystems;
+    if (!android::base::ReadFileToString("/proc/filesystems", &filesystems)) {
+        PLOG(ERROR) << "Could not read /proc/filesystems";
+        return false;
+    }
+    for (const auto& fs : android::base::Split(filesystems, "\n")) {
+        if (fs.find("sdcardfs") != std::string::npos) return true;
+    }
+    return false;
+}
+
+static bool should_use_sdcardfs(void) {
+    char property[PROPERTY_VALUE_MAX];
+
+    // Allow user to have a strong opinion about state
+    property_get(PROP_SDCARDFS_USER, property, "");
+    if (!strcmp(property, "force_on")) {
+        LOG(WARNING) << "User explicitly enabled sdcardfs";
+        return supports_sdcardfs();
+    } else if (!strcmp(property, "force_off")) {
+        LOG(WARNING) << "User explicitly disabled sdcardfs";
+        return false;
+    }
+
+    // Fall back to device opinion about state
+    if (property_get_bool(PROP_SDCARDFS_DEVICE, false)) {
+        LOG(WARNING) << "Device explicitly enabled sdcardfs";
+        return supports_sdcardfs();
+    } else {
+        LOG(WARNING) << "Device explicitly disabled sdcardfs";
+        return false;
+    }
+}
+
 static int usage() {
     LOG(ERROR) << "usage: sdcard [OPTIONS] <source_path> <label>"
                << "    -u: specify UID to run as"
@@ -389,6 +489,10 @@
         sleep(1);
     }
 
-    run(source_path, label, uid, gid, userid, multi_user, full_write);
+    if (should_use_sdcardfs()) {
+        run_sdcardfs(source_path, label, uid, gid, userid, multi_user, full_write);
+    } else {
+        run(source_path, label, uid, gid, userid, multi_user, full_write);
+    }
     return 1;
 }