/* * 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 "usb_libusb.h" #include "android-base/logging.h" #include "adb_trace.h" #include "client/detach.h" #include "client/usb_libusb_inhouse_hotplug.h" #include "usb.h" using namespace std::chrono_literals; using android::base::ScopedLockAssertion; LibUsbConnection::LibUsbConnection(std::unique_ptr device) : device_(std::move(device)) {} void LibUsbConnection::Init() { detached_ = attached_devices.ShouldStartDetached(const_cast(*this)); VLOG(USB) << "Device " << device_->GetSerial() << " created detached=" << detached_; } LibUsbConnection::~LibUsbConnection() { VLOG(USB) << "LibUsbConnection(" << Serial() << "): destructing"; Stop(); } void LibUsbConnection::OnError(const std::string& reason) { std::call_once(this->error_flag_, [this, reason]() { // When a Windows machine goes to sleep it powers off all its USB host controllers to save // energy. When the machine awakens, it powers them up which causes all the endpoints // to be closed (which generates a read/write failure leading to us Close()ing the device). // The USB device also briefly goes away and comes back with the exact same properties // (including address). This makes in-house hotplug miss device reconnection upon wakeup. To // solve that we remove ourselves from the set of known devices. libusb_inhouse_hotplug::report_error(*this); transport_->HandleError(reason); }); } void LibUsbConnection::HandleStop(const std::string& reason) { // If we are detached, we should not report an error condition to the transport // layer. If a connection is detached it has merely been requested to stop transmitting and // release its resources. if (detached_) { VLOG(USB) << "Not reporting error '" << reason << "' because device " << transport_->serial << " is detached"; } else { OnError(reason); } } bool LibUsbConnection::Start() { VLOG(USB) << "LibUsbConnection::Start()"; std::lock_guard lock(mutex_); if (running_) { VLOG(USB) << "LibUsbConnection(" << Serial() << "): already started"; } if (!device_->Open()) { VLOG(USB) << "Unable to start " << Serial() << ": Failed to open device"; return false; } StartReadThread(); StartWriteThread(); running_ = true; return true; } void LibUsbConnection::StartReadThread() { read_thread_ = std::thread([this]() { LOG(INFO) << Serial() << ": read thread spawning"; while (true) { auto packet = std::make_unique(); if (!device_->Read(packet.get())) { PLOG(INFO) << Serial() << ": read failed"; break; } bool got_stls_cmd = false; if (packet->msg.command == A_STLS) { got_stls_cmd = true; } 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) << Serial() << ": Received STLS packet. Stopping read thread."; break; } } HandleStop("read thread stopped"); }); } void LibUsbConnection::StartWriteThread() { write_thread_ = std::thread([this]() { LOG(INFO) << Serial() << ": write thread spawning"; while (true) { std::unique_lock lock(mutex_); ScopedLockAssertion assume_locked(mutex_); cv_write_.wait(lock, [this]() REQUIRES(mutex_) { return !this->running_ || !this->write_queue_.empty(); }); if (!this->running_) { break; } std::unique_ptr packet = std::move(this->write_queue_.front()); this->write_queue_.pop_front(); lock.unlock(); if (!this->device_->Write(packet.get())) { break; } } HandleStop("write thread stopped"); }); } bool LibUsbConnection::DoTlsHandshake(RSA* key, std::string* auth_key) { LOG(WARNING) << "TlsHandshake is not supported by libusb backen"; return false; } void LibUsbConnection::Reset() { { std::lock_guard lock(mutex_); if (!running_) { LOG(INFO) << "LibUsbConnection(" << Serial() << "): not running"; return; } } LOG(INFO) << "LibUsbConnection(" << Serial() << "): RESET"; this->device_->Reset(); Stop(); } void LibUsbConnection::Stop() { { std::lock_guard lock(mutex_); if (!running_) { LOG(INFO) << "LibUsbConnection(" << Serial() << ") Stop: not running"; return; } running_ = false; } LOG(INFO) << "LibUsbConnection(" << Serial() << "): stopping"; this->device_->Close(); this->cv_write_.notify_one(); // Move the threads out into locals with the lock taken, and then unlock to let them exit. std::thread read_thread; std::thread write_thread; { std::lock_guard lock(mutex_); read_thread = std::move(read_thread_); write_thread = std::move(write_thread_); } read_thread.join(); write_thread.join(); HandleStop("stop requested"); { std::lock_guard lock(mutex_); write_queue_.clear(); } } bool LibUsbConnection::Write(std::unique_ptr packet) { { std::lock_guard lock(this->mutex_); write_queue_.emplace_back(std::move(packet)); } cv_write_.notify_one(); return true; } uint64_t LibUsbConnection::NegotiatedSpeedMbps() { return device_->NegotiatedSpeedMbps(); } uint64_t LibUsbConnection::MaxSpeedMbps() { return device_->MaxSpeedMbps(); } bool LibUsbConnection::SupportsDetach() const { return true; } bool LibUsbConnection::Attach(std::string*) { VLOG(USB) << "LibUsbConnection::Attach"; if (!detached_) { VLOG(USB) << "Already attached"; return true; } detached_ = false; return Start(); } bool LibUsbConnection::Detach(std::string*) { VLOG(USB) << "LibUsbConnection::Detach"; if (detached_) { VLOG(USB) << "Already detached"; return true; } detached_ = true; Stop(); return true; } bool LibUsbConnection::IsDetached() { return detached_; } uint64_t LibUsbConnection::GetSessionId() const { return device_->GetSessionId().id; }