adbd: Check USB disabled property after opening FFS am: db01bd2944

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/modules/adb/+/15130281

Change-Id: Iece3ff0a29ace0e8f767feae8b8b7e6ca070b0a7
diff --git a/OWNERS b/OWNERS
index 6f1a311..2108ae5 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,3 +1,4 @@
 jmgao@google.com
+enh@google.com
 
 include platform/packages/modules/common:/MODULES_OWNERS  # see go/mainline-owners-policy
diff --git a/adb.cpp b/adb.cpp
index d692a0b..3b860c0 100644
--- a/adb.cpp
+++ b/adb.cpp
@@ -1360,6 +1360,52 @@
         return HostRequestResult::Handled;
     }
 
+    if (service == "attach") {
+        std::string error;
+        atransport* t = s->transport ? s->transport
+                                     : acquire_one_transport(type, serial, transport_id, nullptr,
+                                                             &error, true);
+        if (!t) {
+            SendFail(reply_fd, error);
+            return HostRequestResult::Handled;
+        }
+
+        if (t->Attach(&error)) {
+            SendOkay(reply_fd,
+                     android::base::StringPrintf("%s attached", t->serial_name().c_str()));
+        } else {
+            SendFail(reply_fd, error);
+        }
+        return HostRequestResult::Handled;
+    }
+
+    if (service == "detach") {
+        std::string error;
+        atransport* t = s->transport ? s->transport
+                                     : acquire_one_transport(type, serial, transport_id, nullptr,
+                                                             &error, true);
+        if (!t) {
+            SendFail(reply_fd, error);
+            return HostRequestResult::Handled;
+        }
+
+        // HACK:
+        // Detaching the transport will lead to all of its sockets being closed,
+        // but we're handling one of those sockets right now!
+        //
+        // Mark the socket as not having a transport, knowing that it'll be cleaned up by the
+        // function that called us.
+        s->transport = nullptr;
+
+        if (t->Detach(&error)) {
+            SendOkay(reply_fd,
+                     android::base::StringPrintf("%s detached", t->serial_name().c_str()));
+        } else {
+            SendFail(reply_fd, error);
+        }
+        return HostRequestResult::Handled;
+    }
+
     // TODO: Switch handle_forward_request to string_view.
     std::string service_str(service);
     auto transport_acquirer = [=](std::string* error) {
diff --git a/adb.h b/adb.h
index 476ed9b..437a207 100644
--- a/adb.h
+++ b/adb.h
@@ -104,6 +104,7 @@
     kCsAuthorizing,     // Authorizing with keys from ADB_VENDOR_KEYS.
     kCsUnauthorized,    // ADB_VENDOR_KEYS exhausted, fell back to user prompt.
     kCsNoPerm,          // Insufficient permissions to communicate with the device.
+    kCsDetached,        // USB device that's detached from the adb server.
     kCsOffline,
 
     kCsBootloader,
diff --git a/apex/Android.bp b/apex/Android.bp
index 3c9f13e..e018565 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -28,6 +28,7 @@
     manifest: "apex_manifest.json",
     key: "com.android.adbd.key",
     certificate: ":com.android.adbd.certificate",
+    compressible: true,
 }
 
 soong_config_module_type_import {
diff --git a/client/adb_client.cpp b/client/adb_client.cpp
index a308732..60dd38f 100644
--- a/client/adb_client.cpp
+++ b/client/adb_client.cpp
@@ -383,9 +383,10 @@
     return true;
 }
 
-bool adb_query(const std::string& service, std::string* result, std::string* error) {
+bool adb_query(const std::string& service, std::string* result, std::string* error,
+               bool force_switch_device) {
     D("adb_query: %s", service.c_str());
-    unique_fd fd(adb_connect(service, error));
+    unique_fd fd(adb_connect(nullptr, service, error, force_switch_device));
     if (fd < 0) {
         return false;
     }
diff --git a/client/adb_client.h b/client/adb_client.h
index d0a4c4e..ab559b2 100644
--- a/client/adb_client.h
+++ b/client/adb_client.h
@@ -51,7 +51,7 @@
 // Connects to the named adb service and fills 'result' with the response.
 // Returns true on success; returns false and fills 'error' on failure.
 bool adb_query(const std::string& service, std::string* _Nonnull result,
-               std::string* _Nonnull error);
+               std::string* _Nonnull error, bool force_switch_device = false);
 
 // Set the preferred transport to connect to.
 void adb_set_transport(TransportType type, const char* _Nullable serial, TransportId transport_id);
diff --git a/client/commandline.cpp b/client/commandline.cpp
index 0caf9e2..bfb7f25 100644
--- a/client/commandline.cpp
+++ b/client/commandline.cpp
@@ -234,6 +234,10 @@
         " reconnect device         kick connection from device side to force reconnect\n"
         " reconnect offline        reset offline/unauthorized devices to force reconnect\n"
         "\n"
+        "usb:\n"
+        " attach                   attach a detached USB device\n"
+        " detach                   detach from a USB device to allow use by other processes\n"
+        ""
         "environment variables:\n"
         " $ADB_TRACE\n"
         "     comma-separated list of debug info to log:\n"
@@ -2162,6 +2166,15 @@
         output_fd = adb_register_socket(output_fd);
         close_on_exec(output_fd);
         return incremental::serve(connection_fd, output_fd, argc - 3, argv + 3);
+    } else if (!strcmp(argv[0], "attach") || !strcmp(argv[0], "detach")) {
+        const char* service = strcmp(argv[0], "attach") == 0 ? "host:attach" : "host:detach";
+        std::string result;
+        std::string error;
+        if (!adb_query(service, &result, &error, true)) {
+            error_exit("failed to %s: %s", argv[0], error.c_str());
+        }
+        printf("%s\n", result.c_str());
+        return 0;
     }
 
     error_exit("unknown command %s", argv[0]);
diff --git a/client/usb_libusb.cpp b/client/usb_libusb.cpp
index a877610..b9ce1d1 100644
--- a/client/usb_libusb.cpp
+++ b/client/usb_libusb.cpp
@@ -47,6 +47,13 @@
 using android::base::ScopedLockAssertion;
 using android::base::StringPrintf;
 
+#define LOG_ERR(out, fmt, ...)                                               \
+    do {                                                                     \
+        std::string __err = android::base::StringPrintf(fmt, ##__VA_ARGS__); \
+        LOG(ERROR) << __err;                                                 \
+        *out = std::move(__err);                                             \
+    } while (0)
+
 // RAII wrappers for libusb.
 struct ConfigDescriptorDeleter {
     void operator()(libusb_config_descriptor* desc) { libusb_free_config_descriptor(desc); }
@@ -69,22 +76,22 @@
 static void process_device(libusb_device* device_raw);
 
 static std::string get_device_address(libusb_device* device) {
-    return StringPrintf("usb:%d:%d", libusb_get_bus_number(device),
-                        libusb_get_device_address(device));
-}
-
-#if defined(__linux__)
-static std::string get_device_serial_path(libusb_device* device) {
     uint8_t ports[7];
     int port_count = libusb_get_port_numbers(device, ports, 7);
     if (port_count < 0) return "";
 
-    std::string path =
-            StringPrintf("/sys/bus/usb/devices/%d-%d", libusb_get_bus_number(device), ports[0]);
+    std::string address = StringPrintf("%d-%d", libusb_get_bus_number(device), ports[0]);
     for (int port = 1; port < port_count; ++port) {
-        path += StringPrintf(".%d", ports[port]);
+        address += StringPrintf(".%d", ports[port]);
     }
-    path += "/serial";
+
+    return address;
+}
+
+#if defined(__linux__)
+static std::string get_device_serial_path(libusb_device* device) {
+    std::string address = get_device_address(device);
+    std::string path = StringPrintf("/sys/bus/usb/devices/%s/serial", address.c_str());
     return path;
 }
 #endif
@@ -123,24 +130,26 @@
         if (payload) {
             packet->payload = std::move(*payload);
         }
-        read_callback_(this, std::move(packet));
+        transport_->HandleRead(std::move(packet));
     }
 
     void Cleanup(ReadBlock* read_block) REQUIRES(read_mutex_) {
         libusb_free_transfer(read_block->transfer);
         read_block->active = false;
         read_block->transfer = nullptr;
-        if (terminating_) {
+        if (terminated_) {
             destruction_cv_.notify_one();
         }
     }
 
     bool MaybeCleanup(ReadBlock* read_block) REQUIRES(read_mutex_) {
+        CHECK(read_block);
+        CHECK(read_block->transfer);
         if (read_block->transfer->status == LIBUSB_TRANSFER_CANCELLED) {
-            CHECK(terminating_);
+            CHECK(terminated_);
         }
 
-        if (terminating_) {
+        if (terminated_) {
             Cleanup(read_block);
             return true;
         }
@@ -161,7 +170,9 @@
         if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
             std::string msg = StringPrintf("usb read failed: status = %d", transfer->status);
             LOG(ERROR) << msg;
-            self->OnError(msg);
+            if (!self->detached_) {
+                self->OnError(msg);
+            }
             self->Cleanup(read_block);
             return;
         }
@@ -208,7 +219,9 @@
         if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
             std::string msg = StringPrintf("usb read failed: status = %d", transfer->status);
             LOG(ERROR) << msg;
-            self->OnError(msg);
+            if (!self->detached_) {
+                self->OnError(msg);
+            }
             self->Cleanup(&self->payload_read_);
             return;
         }
@@ -242,12 +255,12 @@
             libusb_free_transfer(transfer);
             self->writes_.erase(write_block->id);
 
-            if (self->terminating_ && self->writes_.empty()) {
+            if (self->terminated_ && self->writes_.empty()) {
                 self->destruction_cv_.notify_one();
             }
         }
 
-        if (!succeeded) {
+        if (!succeeded && !self->detached_) {
             self->OnError("libusb write failed");
         }
     }
@@ -304,10 +317,14 @@
         memcpy(header.data(), &packet->msg, sizeof(packet->msg));
 
         std::lock_guard<std::mutex> lock(write_mutex_);
-        if (terminating_) {
+        if (terminated_) {
             return false;
         }
 
+        if (detached_) {
+            return true;
+        }
+
         SubmitWrite(std::move(header));
         if (!packet->payload.empty()) {
             size_t payload_length = packet->payload.size();
@@ -463,37 +480,15 @@
 
     bool OpenDevice(std::string* error) {
         if (device_handle_) {
-            return true;
+            LOG_ERR(error, "device already open");
+            return false;
         }
 
         libusb_device_handle* handle_raw;
         int rc = libusb_open(device_.get(), &handle_raw);
         if (rc != 0) {
-            std::string err = StringPrintf("failed to open device: %s", libusb_strerror(rc));
-            LOG(ERROR) << err;
-
-#if defined(__linux__)
-            std::string device_serial;
-            // libusb doesn't think we should be messing around with devices we don't have
-            // write access to, but Linux at least lets us get the serial number anyway.
-            if (!android::base::ReadFileToString(get_device_serial_path(device_.get()),
-                                                 &device_serial)) {
-                // We don't actually want to treat an unknown serial as an error because
-                // devices aren't able to communicate a serial number in early bringup.
-                // http://b/20883914
-                serial_ = "<unknown>";
-            } else {
-                serial_ = android::base::Trim(device_serial);
-            }
-#else
-            // On Mac OS and Windows, we're screwed. But I don't think this situation actually
-            // happens on those OSes.
-#endif
-
-            if (error) {
-                *error = std::move(err);
-            }
-
+            // TODO: Handle no permissions.
+            LOG_ERR(error, "failed to open device: %s", libusb_strerror(rc));
             return false;
         }
 
@@ -502,84 +497,47 @@
 
         auto device_desc = GetDeviceDescriptor();
         if (!device_desc) {
+            LOG_ERR(error, "failed to get device descriptor");
             device_handle_.reset();
             return false;
         }
 
         if (!FindInterface(&device_desc.value())) {
+            LOG_ERR(error, "failed to find adb interface");
             device_handle_.reset();
             return false;
         }
 
         serial_ = GetSerial();
-        return true;
-    }
-
-    bool StartImpl(std::string* error) {
-        if (!OpenDevice(error)) {
-            return false;
-        }
 
         LOG(DEBUG) << "successfully opened adb device at " << device_address_ << ", "
                    << StringPrintf("bulk_in = %#x, bulk_out = %#x", read_endpoint_,
                                    write_endpoint_);
 
         // WARNING: this isn't released via RAII.
-        int rc = libusb_claim_interface(device_handle_.get(), interface_num_);
+        rc = libusb_claim_interface(device_handle_.get(), interface_num_);
         if (rc != 0) {
-            LOG(WARNING) << "failed to claim adb interface for device '" << serial_ << "'"
-                         << libusb_error_name(rc);
+            LOG_ERR(error, "failed to claim adb interface for device '%s': %s", serial_.c_str(),
+                    libusb_error_name(rc));
+            device_handle_.reset();
             return false;
         }
 
         for (uint8_t endpoint : {read_endpoint_, write_endpoint_}) {
             rc = libusb_clear_halt(device_handle_.get(), endpoint);
             if (rc != 0) {
-                LOG(WARNING) << "failed to clear halt on device '" << serial_ << "' endpoint 0x"
-                             << std::hex << endpoint << ": " << libusb_error_name(rc);
+                LOG_ERR(error, "failed to clear halt on device '%s' endpoint %#02x: %s",
+                        serial_.c_str(), endpoint, libusb_error_name(rc));
                 libusb_release_interface(device_handle_.get(), interface_num_);
+                device_handle_.reset();
                 return false;
             }
         }
 
-        LOG(INFO) << "registered new usb device '" << serial_ << "'";
-        std::lock_guard lock(read_mutex_);
-        CreateRead(&header_read_, true);
-        CreateRead(&payload_read_, false);
-        SubmitRead(&header_read_, sizeof(amessage));
-
         return true;
     }
 
-    void OnError(const std::string& error) {
-        std::call_once(error_flag_, [this, &error]() {
-            if (error_callback_) {
-                error_callback_(this, error);
-            }
-        });
-    }
-
-    virtual void Reset() override final {
-        Stop();
-
-        if (libusb_reset_device(device_handle_.get()) == 0) {
-            libusb_device* device = libusb_ref_device(device_.get());
-            fdevent_run_on_main_thread([device]() {
-                process_device(device);
-                libusb_unref_device(device);
-            });
-        }
-    }
-
-    virtual void Start() override final {
-        std::string error;
-        if (!StartImpl(&error)) {
-            OnError(error);
-            return;
-        }
-    }
-
-    virtual void Stop() override final {
+    void CloseDevice() {
         // This is rather messy, because of the lifecyle of libusb_transfers.
         //
         // We can't call libusb_free_transfer for a submitted transfer, we have to cancel it
@@ -591,11 +549,11 @@
         // Resolve this by setting an atomic flag before we lock to cancel transfers, and take the
         // lock in the callbacks before checking the flag.
 
-        if (terminating_) {
+        if (terminated_) {
             return;
         }
 
-        terminating_ = true;
+        terminated_ = true;
 
         {
             std::unique_lock<std::mutex> lock(write_mutex_);
@@ -616,11 +574,12 @@
         {
             std::unique_lock<std::mutex> lock(read_mutex_);
             ScopedLockAssertion assumed_locked(read_mutex_);
+
             if (header_read_.transfer) {
                 if (header_read_.active) {
                     libusb_cancel_transfer(header_read_.transfer);
                 } else {
-                    libusb_free_transfer(header_read_.transfer);
+                    Cleanup(&header_read_);
                 }
             }
 
@@ -628,7 +587,7 @@
                 if (payload_read_.active) {
                     libusb_cancel_transfer(payload_read_.transfer);
                 } else {
-                    libusb_free_transfer(payload_read_.transfer);
+                    Cleanup(&payload_read_);
                 }
             }
 
@@ -636,12 +595,85 @@
                 ScopedLockAssertion assumed_locked(read_mutex_);
                 return !header_read_.active && !payload_read_.active;
             });
+
+            incoming_header_.reset();
+            incoming_payload_.clear();
         }
 
         if (device_handle_) {
             libusb_release_interface(device_handle_.get(), interface_num_);
+            device_handle_.reset();
+        }
+    }
+
+    bool StartImpl(std::string* error) {
+        if (!device_handle_) {
+            *error = "device not opened";
+            return false;
         }
 
+        LOG(INFO) << "registered new usb device '" << serial_ << "'";
+        std::lock_guard lock(read_mutex_);
+        CreateRead(&header_read_, true);
+        CreateRead(&payload_read_, false);
+        SubmitRead(&header_read_, sizeof(amessage));
+
+        return true;
+    }
+
+    void OnError(const std::string& error) {
+        std::call_once(error_flag_, [this, &error]() {
+            if (transport_) {
+                transport_->HandleError(error);
+            }
+        });
+    }
+
+    virtual bool Attach(std::string* error) override final {
+        terminated_ = false;
+        detached_ = false;
+
+        if (!OpenDevice(error)) {
+            return false;
+        }
+
+        if (!StartImpl(error)) {
+            CloseDevice();
+            return false;
+        }
+
+        return true;
+    }
+
+    virtual bool Detach(std::string* error) override final {
+        detached_ = true;
+        CloseDevice();
+        return true;
+    }
+
+    virtual void Reset() override final {
+        LOG(INFO) << "resetting " << transport_->serial_name();
+        if (libusb_reset_device(device_handle_.get()) == 0) {
+            libusb_device* device = libusb_ref_device(device_.get());
+
+            Stop();
+
+            fdevent_run_on_main_thread([device]() {
+                process_device(device);
+                libusb_unref_device(device);
+            });
+        }
+    }
+
+    virtual void Start() override final {
+        std::string error;
+        if (!Attach(&error)) {
+            OnError(error);
+        }
+    }
+
+    virtual void Stop() override final {
+        CloseDevice();
         OnError("requested stop");
     }
 
@@ -660,9 +692,25 @@
             return {};
         }
 
+#if defined(__linux__)
+        std::string device_serial;
+        if (android::base::ReadFileToString(get_device_serial_path(connection->device_.get()),
+                                            &device_serial)) {
+            connection->serial_ = android::base::Trim(device_serial);
+        } else {
+            // We don't actually want to treat an unknown serial as an error because
+            // devices aren't able to communicate a serial number in early bringup.
+            // http://b/20883914
+            connection->serial_ = "<unknown>";
+        }
+#else
+        // We need to open the device to get its serial on Windows and OS X.
         if (!connection->OpenDevice(nullptr)) {
             return {};
         }
+        connection->serial_ = connection->GetSerial();
+        connection->CloseDevice();
+#endif
 
         return connection;
     }
@@ -687,7 +735,8 @@
     std::atomic<size_t> next_write_id_ = 0;
 
     std::once_flag error_flag_;
-    std::atomic<bool> terminating_ = false;
+    std::atomic<bool> terminated_ = false;
+    std::atomic<bool> detached_ = false;
     std::condition_variable destruction_cv_;
 
     size_t zero_mask_ = 0;
@@ -711,6 +760,12 @@
     }
 
     auto connection = *connection_opt;
+
+    {
+        std::lock_guard<std::mutex> lock(usb_handles_mutex);
+        usb_handles.emplace(libusb_ref_device(device_raw), connection);
+    }
+
     LOG(INFO) << "constructed LibusbConnection for device " << connection->serial_ << " ("
               << device_address << ")";
 
@@ -744,6 +799,7 @@
     if (it != usb_handles.end()) {
         // We need to ensure that we don't destroy the LibusbConnection on this thread,
         // as we're in a context with internal libusb mutexes held.
+        libusb_device* device = it->first;
         std::weak_ptr<LibusbConnection> connection_weak = it->second;
         usb_handles.erase(it);
         fdevent_run_on_main_thread([connection_weak]() {
@@ -755,6 +811,7 @@
                 LOG(INFO) << "libusb_hotplug: device disconnected: (destroyed)";
             }
         });
+        libusb_unref_device(device);
     }
     usb_handles_mutex.unlock();
 }
@@ -768,9 +825,13 @@
             libusb_hotplug_event event = pair.first;
             libusb_device* device = pair.second;
             if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED) {
+                LOG(INFO) << "libusb hotplug: device arrived";
                 device_connected(device);
             } else if (event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) {
+                LOG(INFO) << "libusb hotplug: device left";
                 device_disconnected(device);
+            } else {
+                LOG(WARNING) << "unknown libusb hotplug event: " << event;
             }
         });
     }
diff --git a/daemon/usb.cpp b/daemon/usb.cpp
index 4366ea9..6c3f735 100644
--- a/daemon/usb.cpp
+++ b/daemon/usb.cpp
@@ -582,7 +582,7 @@
 
                 // TODO: Make apacket contain an IOVector so we don't have to coalesce.
                 packet->payload = std::move(incoming_payload_).coalesce();
-                read_callback_(this, std::move(packet));
+                transport_->HandleRead(std::move(packet));
 
                 incoming_header_.reset();
                 // reuse the capacity of the incoming payload while we can.
@@ -678,7 +678,10 @@
 
     void HandleError(const std::string& error) {
         std::call_once(error_flag_, [&]() {
-            error_callback_(this, error);
+            if (transport_) {
+                transport_->HandleError(error);
+            }
+
             if (!stopped_) {
                 Stop();
             }
diff --git a/transport.cpp b/transport.cpp
index fbcf79b..1e13655 100644
--- a/transport.cpp
+++ b/transport.cpp
@@ -55,6 +55,10 @@
 #include "fdevent/fdevent.h"
 #include "sysdeps/chrono.h"
 
+#if ADB_HOST
+#include "client/usb.h"
+#endif
+
 using namespace adb::crypto;
 using namespace adb::tls;
 using android::base::ScopedLockAssertion;
@@ -278,25 +282,28 @@
     Stop();
 }
 
+std::string Connection::Serial() const {
+    return transport_ ? transport_->serial_name() : "<unknown>";
+}
+
 BlockingConnectionAdapter::BlockingConnectionAdapter(std::unique_ptr<BlockingConnection> connection)
     : underlying_(std::move(connection)) {}
 
 BlockingConnectionAdapter::~BlockingConnectionAdapter() {
-    LOG(INFO) << "BlockingConnectionAdapter(" << this->transport_name_ << "): destructing";
+    LOG(INFO) << "BlockingConnectionAdapter(" << Serial() << "): destructing";
     Stop();
 }
 
 void BlockingConnectionAdapter::Start() {
     std::lock_guard<std::mutex> lock(mutex_);
     if (started_) {
-        LOG(FATAL) << "BlockingConnectionAdapter(" << this->transport_name_
-                   << "): started multiple times";
+        LOG(FATAL) << "BlockingConnectionAdapter(" << Serial() << "): started multiple times";
     }
 
     StartReadThread();
 
     write_thread_ = std::thread([this]() {
-        LOG(INFO) << this->transport_name_ << ": write thread spawning";
+        LOG(INFO) << Serial() << ": write thread spawning";
         while (true) {
             std::unique_lock<std::mutex> lock(mutex_);
             ScopedLockAssertion assume_locked(mutex_);
@@ -316,7 +323,7 @@
                 break;
             }
         }
-        std::call_once(this->error_flag_, [this]() { this->error_callback_(this, "write failed"); });
+        std::call_once(this->error_flag_, [this]() { transport_->HandleError("write failed"); });
     });
 
     started_ = true;
@@ -324,11 +331,11 @@
 
 void BlockingConnectionAdapter::StartReadThread() {
     read_thread_ = std::thread([this]() {
-        LOG(INFO) << this->transport_name_ << ": read thread spawning";
+        LOG(INFO) << Serial() << ": read thread spawning";
         while (true) {
             auto packet = std::make_unique<apacket>();
             if (!underlying_->Read(packet.get())) {
-                PLOG(INFO) << this->transport_name_ << ": read failed";
+                PLOG(INFO) << Serial() << ": read failed";
                 break;
             }
 
@@ -337,18 +344,17 @@
                 got_stls_cmd = true;
             }
 
-            read_callback_(this, std::move(packet));
+            transport_->HandleRead(std::move(packet));
 
             // If we received the STLS packet, we are about to perform the TLS
             // handshake. So this read thread must stop and resume after the
             // handshake completes otherwise this will interfere in the process.
             if (got_stls_cmd) {
-                LOG(INFO) << this->transport_name_
-                          << ": Received STLS packet. Stopping read thread.";
+                LOG(INFO) << Serial() << ": Received STLS packet. Stopping read thread.";
                 return;
             }
         }
-        std::call_once(this->error_flag_, [this]() { this->error_callback_(this, "read failed"); });
+        std::call_once(this->error_flag_, [this]() { transport_->HandleError("read failed"); });
     });
 }
 
@@ -366,18 +372,17 @@
     {
         std::lock_guard<std::mutex> lock(mutex_);
         if (!started_) {
-            LOG(INFO) << "BlockingConnectionAdapter(" << this->transport_name_ << "): not started";
+            LOG(INFO) << "BlockingConnectionAdapter(" << Serial() << "): not started";
             return;
         }
 
         if (stopped_) {
-            LOG(INFO) << "BlockingConnectionAdapter(" << this->transport_name_
-                      << "): already stopped";
+            LOG(INFO) << "BlockingConnectionAdapter(" << Serial() << "): already stopped";
             return;
         }
     }
 
-    LOG(INFO) << "BlockingConnectionAdapter(" << this->transport_name_ << "): resetting";
+    LOG(INFO) << "BlockingConnectionAdapter(" << Serial() << "): resetting";
     this->underlying_->Reset();
     Stop();
 }
@@ -386,20 +391,19 @@
     {
         std::lock_guard<std::mutex> lock(mutex_);
         if (!started_) {
-            LOG(INFO) << "BlockingConnectionAdapter(" << this->transport_name_ << "): not started";
+            LOG(INFO) << "BlockingConnectionAdapter(" << Serial() << "): not started";
             return;
         }
 
         if (stopped_) {
-            LOG(INFO) << "BlockingConnectionAdapter(" << this->transport_name_
-                      << "): already stopped";
+            LOG(INFO) << "BlockingConnectionAdapter(" << Serial() << "): already stopped";
             return;
         }
 
         stopped_ = true;
     }
 
-    LOG(INFO) << "BlockingConnectionAdapter(" << this->transport_name_ << "): stopping";
+    LOG(INFO) << "BlockingConnectionAdapter(" << Serial() << "): stopping";
 
     this->underlying_->Close();
     this->cv_.notify_one();
@@ -417,8 +421,8 @@
     read_thread.join();
     write_thread.join();
 
-    LOG(INFO) << "BlockingConnectionAdapter(" << this->transport_name_ << "): stopped";
-    std::call_once(this->error_flag_, [this]() { this->error_callback_(this, "requested stop"); });
+    LOG(INFO) << "BlockingConnectionAdapter(" << Serial() << "): stopped";
+    std::call_once(this->error_flag_, [this]() { transport_->HandleError("requested stop"); });
 }
 
 bool BlockingConnectionAdapter::Write(std::unique_ptr<apacket> packet) {
@@ -762,6 +766,16 @@
     return 0;
 }
 
+static bool usb_devices_start_detached() {
+#if ADB_HOST
+    static const char* env = getenv("ADB_LIBUSB_START_DETACHED");
+    static bool result = env && strcmp("1", env) == 0;
+    return should_use_libusb() && result;
+#else
+    return false;
+#endif
+}
+
 static void transport_registration_func(int _fd, unsigned ev, void*) {
     tmsg m;
     atransport* t;
@@ -792,34 +806,16 @@
 
     /* don't create transport threads for inaccessible devices */
     if (t->GetConnectionState() != kCsNoPerm) {
-        // The connection gets a reference to the atransport. It will release it
-        // upon a read/write error.
-        t->connection()->SetTransportName(t->serial_name());
-        t->connection()->SetReadCallback([t](Connection*, std::unique_ptr<apacket> p) {
-            if (!check_header(p.get(), t)) {
-                D("%s: remote read: bad header", t->serial.c_str());
-                return false;
-            }
+        t->connection()->SetTransport(t);
 
-            VLOG(TRANSPORT) << dump_packet(t->serial.c_str(), "from remote", p.get());
-            apacket* packet = p.release();
-
-            // TODO: Does this need to run on the main thread?
-            fdevent_run_on_main_thread([packet, t]() { handle_packet(packet, t); });
-            return true;
-        });
-        t->connection()->SetErrorCallback([t](Connection*, const std::string& error) {
-            LOG(INFO) << t->serial_name() << ": connection terminated: " << error;
-            fdevent_run_on_main_thread([t]() {
-                handle_offline(t);
-                transport_destroy(t);
-            });
-        });
-
-        t->connection()->Start();
+        if (t->type == kTransportUsb && usb_devices_start_detached()) {
+            t->SetConnectionState(kCsDetached);
+        } else {
+            t->connection()->Start();
 #if ADB_HOST
-        send_connect(t);
+            send_connect(t);
 #endif
+        }
     }
 
     {
@@ -852,7 +848,7 @@
     transport_registration_recv = s[1];
 
     transport_registration_fde =
-        fdevent_create(transport_registration_recv, transport_registration_func, nullptr);
+            fdevent_create(transport_registration_recv, transport_registration_func, nullptr);
     fdevent_set(transport_registration_fde, FDE_READ);
 }
 
@@ -961,8 +957,8 @@
     atransport* result = nullptr;
 
     if (transport_id != 0) {
-        *error_out =
-            android::base::StringPrintf("no device with transport id '%" PRIu64 "'", transport_id);
+        *error_out = android::base::StringPrintf("no device with transport id '%" PRIu64 "'",
+                                                 transport_id);
     } else if (serial) {
         *error_out = android::base::StringPrintf("device '%s' not found", serial);
     } else if (type == kTransportLocal) {
@@ -1124,11 +1120,89 @@
     update_transports();
 }
 
+#if ADB_HOST
+bool atransport::Attach(std::string* error) {
+    D("%s: attach", serial.c_str());
+    check_main_thread();
+
+    if (!should_use_libusb()) {
+        *error = "attach/detach only implemented for libusb backend";
+        return false;
+    }
+
+    if (GetConnectionState() != ConnectionState::kCsDetached) {
+        *error = android::base::StringPrintf("transport %s is not detached", serial.c_str());
+        return false;
+    }
+
+    ResetKeys();
+
+    {
+        std::lock_guard<std::mutex> lock(mutex_);
+        if (!connection_->Attach(error)) {
+            return false;
+        }
+    }
+
+    send_connect(this);
+    return true;
+}
+
+bool atransport::Detach(std::string* error) {
+    D("%s: detach", serial.c_str());
+    check_main_thread();
+
+    if (!should_use_libusb()) {
+        *error = "attach/detach only implemented for libusb backend";
+        return false;
+    }
+
+    if (GetConnectionState() == ConnectionState::kCsDetached) {
+        *error = android::base::StringPrintf("transport %s is already detached", serial.c_str());
+        return false;
+    }
+
+    handle_offline(this);
+
+    {
+        std::lock_guard<std::mutex> lock(mutex_);
+        if (!connection_->Detach(error)) {
+            return false;
+        }
+    }
+
+    this->SetConnectionState(kCsDetached);
+    return true;
+}
+#endif
+
 void atransport::SetConnection(std::shared_ptr<Connection> connection) {
     std::lock_guard<std::mutex> lock(mutex_);
     connection_ = std::shared_ptr<Connection>(std::move(connection));
 }
 
+bool atransport::HandleRead(std::unique_ptr<apacket> p) {
+    if (!check_header(p.get(), this)) {
+        D("%s: remote read: bad header", serial.c_str());
+        return false;
+    }
+
+    VLOG(TRANSPORT) << dump_packet(serial.c_str(), "from remote", p.get());
+    apacket* packet = p.release();
+
+    // TODO: Does this need to run on the main thread?
+    fdevent_run_on_main_thread([packet, this]() { handle_packet(packet, this); });
+    return true;
+}
+
+void atransport::HandleError(const std::string& error) {
+    LOG(INFO) << serial_name() << ": connection terminated: " << error;
+    fdevent_run_on_main_thread([this]() {
+        handle_offline(this);
+        transport_destroy(this);
+    });
+}
+
 std::string atransport::connection_state_name() const {
     ConnectionState state = GetConnectionState();
     switch (state) {
@@ -1154,6 +1228,8 @@
             return "authorizing";
         case kCsConnecting:
             return "connecting";
+        case kCsDetached:
+            return "detached";
         default:
             return "unknown";
     }
@@ -1268,7 +1344,8 @@
             // Parse our |serial| and the given |target| to check if the hostnames and ports match.
             std::string serial_host, error;
             int serial_port = -1;
-            if (android::base::ParseNetAddress(serial, &serial_host, &serial_port, nullptr, &error)) {
+            if (android::base::ParseNetAddress(serial, &serial_host, &serial_port, nullptr,
+                                               &error)) {
                 // |target| may omit the port to default to ours.
                 std::string target_host;
                 int target_port = serial_port;
diff --git a/transport.h b/transport.h
index d098b7c..20c47a1 100644
--- a/transport.h
+++ b/transport.h
@@ -106,22 +106,7 @@
     Connection() = default;
     virtual ~Connection() = default;
 
-    void SetTransportName(std::string transport_name) {
-        transport_name_ = std::move(transport_name);
-    }
-
-    using ReadCallback = std::function<bool(Connection*, std::unique_ptr<apacket>)>;
-    void SetReadCallback(ReadCallback callback) {
-        CHECK(!read_callback_);
-        read_callback_ = callback;
-    }
-
-    // Called after the Connection has terminated, either by an error or because Stop was called.
-    using ErrorCallback = std::function<void(Connection*, const std::string&)>;
-    void SetErrorCallback(ErrorCallback callback) {
-        CHECK(!error_callback_);
-        error_callback_ = callback;
-    }
+    void SetTransport(atransport* transport) { transport_ = transport; }
 
     virtual bool Write(std::unique_ptr<apacket> packet) = 0;
 
@@ -133,9 +118,19 @@
     // Stop, and reset the device if it's a USB connection.
     virtual void Reset();
 
-    std::string transport_name_;
-    ReadCallback read_callback_;
-    ErrorCallback error_callback_;
+    virtual bool Attach(std::string* error) {
+        *error = "transport type doesn't support attach";
+        return false;
+    }
+
+    virtual bool Detach(std::string* error) {
+        *error = "transport type doesn't support detach";
+        return false;
+    }
+
+    std::string Serial() const;
+
+    atransport* transport_ = nullptr;
 
     static std::unique_ptr<Connection> FromFd(unique_fd fd);
 };
@@ -295,6 +290,9 @@
         return connection_;
     }
 
+    bool HandleRead(std::unique_ptr<apacket> p);
+    void HandleError(const std::string& error);
+
 #if ADB_HOST
     void SetUsbHandle(usb_handle* h) { usb_handle_ = h; }
     usb_handle* GetUsbHandle() { return usb_handle_; }
@@ -355,6 +353,11 @@
     void RunDisconnects();
 
 #if ADB_HOST
+    bool Attach(std::string* error);
+    bool Detach(std::string* error);
+#endif
+
+#if ADB_HOST
     // Returns true if |target| matches this transport. A matching |target| can be any of:
     //   * <serial>
     //   * <devpath>
diff --git a/transport_fd.cpp b/transport_fd.cpp
index b9b4f42..d88d57d 100644
--- a/transport_fd.cpp
+++ b/transport_fd.cpp
@@ -121,7 +121,7 @@
                         packet->msg = *read_header_;
                         packet->payload = std::move(payload);
                         read_header_ = nullptr;
-                        read_callback_(this, std::move(packet));
+                        transport_->HandleRead(std::move(packet));
                     }
                 }
             }
@@ -145,7 +145,7 @@
         thread_ = std::thread([this]() {
             std::string error = "connection closed";
             Run(&error);
-            this->error_callback_(this, error);
+            transport_->HandleError(error);
         });
     }