| /* |
| * 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<LibUsbDevice> device) |
| : device_(std::move(device)) {} |
| |
| void LibUsbConnection::Init() { |
| detached_ = attached_devices.ShouldStartDetached(const_cast<LibUsbConnection&>(*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<std::mutex> 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<apacket>(); |
| 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<std::mutex> 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<apacket> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> lock(mutex_); |
| write_queue_.clear(); |
| } |
| } |
| |
| bool LibUsbConnection::Write(std::unique_ptr<apacket> packet) { |
| { |
| std::lock_guard<std::mutex> 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; |
| } |