| // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/devtools/adb/android_usb_socket.h" |
| |
| #include "base/message_loop/message_loop.h" |
| |
| namespace { |
| |
| const int kMaxPayload = 4096; |
| |
| } // namespace |
| |
| AndroidUsbSocket::IORequest::IORequest( |
| net::IOBuffer* buffer, |
| int length, |
| const net::CompletionCallback& callback) |
| : buffer(buffer), |
| length(length), |
| callback(callback) { |
| } |
| |
| AndroidUsbSocket::IORequest::~IORequest() { |
| } |
| |
| AndroidUsbSocket::AndroidUsbSocket(scoped_refptr<AndroidUsbDevice> device, |
| uint32 socket_id, |
| const std::string& command, |
| base::Callback<void(uint32)> delete_callback) |
| : device_(device), |
| command_(command), |
| delete_callback_(delete_callback), |
| local_id_(socket_id), |
| remote_id_(0), |
| is_connected_(false), |
| is_closed_(false) { |
| } |
| |
| AndroidUsbSocket::~AndroidUsbSocket() { |
| DCHECK(CalledOnValidThread()); |
| if (is_connected_) |
| Disconnect(); |
| delete_callback_.Run(local_id_); |
| } |
| |
| void AndroidUsbSocket::HandleIncoming(scoped_refptr<AdbMessage> message) { |
| CHECK_EQ(message->arg1, local_id_); |
| switch (message->command) { |
| case AdbMessage::kCommandOKAY: |
| if (!is_connected_) { |
| remote_id_ = message->arg0; |
| is_connected_ = true; |
| net::CompletionCallback callback = connect_callback_; |
| connect_callback_.Reset(); |
| callback.Run(net::OK); |
| // "this" can be NULL. |
| } else { |
| RespondToWriters(); |
| // "this" can be NULL. |
| } |
| break; |
| case AdbMessage::kCommandWRTE: |
| device_->Send(AdbMessage::kCommandOKAY, local_id_, message->arg0, ""); |
| read_buffer_ += message->body; |
| // Allow WRTE over new connection even though OKAY ack was not received. |
| if (!is_connected_) { |
| remote_id_ = message->arg0; |
| is_connected_ = true; |
| net::CompletionCallback callback = connect_callback_; |
| connect_callback_.Reset(); |
| callback.Run(net::OK); |
| // "this" can be NULL. |
| } else { |
| RespondToReaders(false); |
| // "this" can be NULL. |
| } |
| break; |
| case AdbMessage::kCommandCLSE: |
| if (is_connected_) |
| device_->Send(AdbMessage::kCommandCLSE, local_id_, 0, ""); |
| is_connected_ = false; |
| is_closed_ = true; |
| RespondToReaders(true); |
| // "this" can be NULL. |
| break; |
| default: |
| break; |
| } |
| } |
| |
| void AndroidUsbSocket::Terminated() { |
| is_connected_ = false; |
| is_closed_ = true; |
| if (!connect_callback_.is_null()) { |
| net::CompletionCallback callback = connect_callback_; |
| connect_callback_.Reset(); |
| callback.Run(net::ERR_FAILED); |
| // "this" can be NULL. |
| return; |
| } |
| RespondToReaders(true); |
| } |
| |
| int AndroidUsbSocket::Read(net::IOBuffer* buffer, |
| int length, |
| const net::CompletionCallback& callback) { |
| if (!is_connected_) |
| return is_closed_ ? 0 : net::ERR_SOCKET_NOT_CONNECTED; |
| |
| if (read_buffer_.empty()) { |
| read_requests_.push_back(IORequest(buffer, length, callback)); |
| return net::ERR_IO_PENDING; |
| } |
| |
| size_t bytes_to_copy = static_cast<size_t>(length) > read_buffer_.length() ? |
| read_buffer_.length() : static_cast<size_t>(length); |
| memcpy(buffer->data(), read_buffer_.data(), bytes_to_copy); |
| if (read_buffer_.length() > bytes_to_copy) |
| read_buffer_ = read_buffer_.substr(bytes_to_copy); |
| else |
| read_buffer_ = ""; |
| return bytes_to_copy; |
| } |
| |
| int AndroidUsbSocket::Write(net::IOBuffer* buffer, |
| int length, |
| const net::CompletionCallback& callback) { |
| if (!is_connected_) |
| return net::ERR_SOCKET_NOT_CONNECTED; |
| |
| if (length > kMaxPayload) |
| length = kMaxPayload; |
| write_requests_.push_back(IORequest(NULL, length, callback)); |
| device_->Send(AdbMessage::kCommandWRTE, local_id_, remote_id_, |
| std::string(buffer->data(), length)); |
| return net::ERR_IO_PENDING; |
| } |
| |
| bool AndroidUsbSocket::SetReceiveBufferSize(int32 size) { |
| NOTIMPLEMENTED(); |
| return false; |
| } |
| |
| bool AndroidUsbSocket::SetSendBufferSize(int32 size) { |
| NOTIMPLEMENTED(); |
| return false; |
| } |
| |
| int AndroidUsbSocket::Connect(const net::CompletionCallback& callback) { |
| DCHECK(CalledOnValidThread()); |
| if (device_->terminated()) |
| return net::ERR_FAILED; |
| connect_callback_ = callback; |
| device_->Send(AdbMessage::kCommandOPEN, local_id_, 0, command_); |
| return net::ERR_IO_PENDING; |
| } |
| |
| void AndroidUsbSocket::Disconnect() { |
| is_connected_ = false; |
| device_->Send(AdbMessage::kCommandCLSE, local_id_, remote_id_, ""); |
| RespondToReaders(true); |
| } |
| |
| bool AndroidUsbSocket::IsConnected() const { |
| DCHECK(CalledOnValidThread()); |
| return is_connected_; |
| } |
| |
| bool AndroidUsbSocket::IsConnectedAndIdle() const { |
| NOTIMPLEMENTED(); |
| return false; |
| } |
| |
| int AndroidUsbSocket::GetPeerAddress(net::IPEndPoint* address) const { |
| net::IPAddressNumber ip(net::kIPv4AddressSize); |
| *address = net::IPEndPoint(ip, 0); |
| return net::OK; |
| } |
| |
| int AndroidUsbSocket::GetLocalAddress(net::IPEndPoint* address) const { |
| NOTIMPLEMENTED(); |
| return net::ERR_FAILED; |
| } |
| |
| const net::BoundNetLog& AndroidUsbSocket::NetLog() const { |
| return net_log_; |
| } |
| |
| void AndroidUsbSocket::SetSubresourceSpeculation() { |
| NOTIMPLEMENTED(); |
| } |
| |
| void AndroidUsbSocket::SetOmniboxSpeculation() { |
| NOTIMPLEMENTED(); |
| } |
| |
| bool AndroidUsbSocket::WasEverUsed() const { |
| NOTIMPLEMENTED(); |
| return true; |
| } |
| |
| bool AndroidUsbSocket::UsingTCPFastOpen() const { |
| NOTIMPLEMENTED(); |
| return true; |
| } |
| |
| bool AndroidUsbSocket::WasNpnNegotiated() const { |
| NOTIMPLEMENTED(); |
| return true; |
| } |
| |
| net::NextProto AndroidUsbSocket::GetNegotiatedProtocol() const { |
| NOTIMPLEMENTED(); |
| return net::kProtoUnknown; |
| } |
| |
| bool AndroidUsbSocket::GetSSLInfo(net::SSLInfo* ssl_info) { |
| return false; |
| } |
| |
| void AndroidUsbSocket::RespondToReaders(bool disconnect) { |
| std::deque<IORequest> read_requests; |
| read_requests.swap(read_requests_); |
| while (!read_requests.empty() && (!read_buffer_.empty() || disconnect)) { |
| IORequest read_request = read_requests.front(); |
| read_requests.pop_front(); |
| size_t bytes_to_copy = |
| static_cast<size_t>(read_request.length) > read_buffer_.length() ? |
| read_buffer_.length() : static_cast<size_t>(read_request.length); |
| memcpy(read_request.buffer->data(), read_buffer_.data(), bytes_to_copy); |
| if (read_buffer_.length() > bytes_to_copy) |
| read_buffer_ = read_buffer_.substr(bytes_to_copy); |
| else |
| read_buffer_ = ""; |
| read_request.callback.Run(bytes_to_copy); |
| } |
| } |
| |
| void AndroidUsbSocket::RespondToWriters() { |
| if (!write_requests_.empty()) { |
| IORequest write_request = write_requests_.front(); |
| write_requests_.pop_front(); |
| write_request.callback.Run(write_request.length); |
| } |
| } |