text: // Copyright 2014 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/device/usb/android_usb_socket.h"
  
  #include <stddef.h>
  
  #include "base/callback_helpers.h"
  #include "base/logging.h"
  #include "net/base/io_buffer.h"
  #include "net/base/ip_address.h"
  #include "net/base/net_errors.h"
  #include "net/traffic_annotation/network_traffic_annotation.h"
  
  namespace {
  
  const int kMaxPayload = 4096;
  
  }  // namespace
  
  AndroidUsbSocket::AndroidUsbSocket(scoped_refptr<AndroidUsbDevice> device,
                                     uint32_t socket_id,
                                     const std::string& command,
                                     base::Closure delete_callback)
      : device_(device),
        command_(command),
        local_id_(socket_id),
        remote_id_(0),
        is_connected_(false),
        delete_callback_(delete_callback),
        weak_factory_(this) {}
  
  AndroidUsbSocket::~AndroidUsbSocket() {
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    if (is_connected_)
      Disconnect();
    if (!delete_callback_.is_null())
      delete_callback_.Run();
  }
  
  void AndroidUsbSocket::HandleIncoming(std::unique_ptr<AdbMessage> message) {
    if (!device_.get())
      return;
  
    CHECK_EQ(message->arg1, local_id_);
    switch (message->command) {
      case AdbMessage::kCommandOKAY:
        if (!is_connected_) {
          remote_id_ = message->arg0;
          is_connected_ = true;
          if (!connect_callback_.is_null())
            std::move(connect_callback_).Run(net::OK);
          // "this" can be deleted.
        } else {
          RespondToWriter(write_length_);
          // "this" can be deleted.
        }
        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;
          if (!connect_callback_.is_null())
            std::move(connect_callback_).Run(net::OK);
          // "this" can be deleted.
        } else {
          RespondToReader(false);
          // "this" can be deleted.
        }
        break;
      case AdbMessage::kCommandCLSE:
        if (is_connected_)
          device_->Send(AdbMessage::kCommandCLSE, local_id_, 0, "");
        Terminated(true);
        // "this" can be deleted.
        break;
      default:
        break;
    }
  }
  
  void AndroidUsbSocket::Terminated(bool closed_by_device) {
    is_connected_ = false;
  
    // Break the socket -> device connection, release the device.
    device_ = nullptr;
    std::move(delete_callback_).Run();
  
    if (!closed_by_device)
      return;
  
    // Respond to pending callbacks.
    if (!connect_callback_.is_null()) {
      std::move(connect_callback_).Run(net::ERR_FAILED);
      // "this" can be deleted.
      return;
    }
    base::WeakPtr<AndroidUsbSocket> weak_this = weak_factory_.GetWeakPtr();
    RespondToReader(true);
    // "this" can be deleted.
    if (weak_this) {
      RespondToWriter(net::ERR_FAILED);
      // "this" can be deleted.
    }
  }
  
  int AndroidUsbSocket::Read(net::IOBuffer* buffer,
                             int length,
                             net::CompletionOnceCallback callback) {
    DCHECK(!callback.is_null());
    if (!is_connected_)
      return device_.get() ? net::ERR_SOCKET_NOT_CONNECTED : 0;
  
    DCHECK(read_callback_.is_null());
    if (read_buffer_.empty()) {
      read_callback_ = std::move(callback);
      read_io_buffer_ = buffer;
      read_length_ = length;
      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_ = std::string();
    return bytes_to_copy;
  }
  
  int AndroidUsbSocket::Write(
      net::IOBuffer* buffer,
      int length,
      net::CompletionOnceCallback callback,
      const net::NetworkTrafficAnnotationTag& /*traffic_annotation*/) {
    DCHECK(!callback.is_null());
    if (!is_connected_)
      return net::ERR_SOCKET_NOT_CONNECTED;
  
    if (length > kMaxPayload)
      length = kMaxPayload;
  
    DCHECK(write_callback_.is_null());
    write_callback_ = std::move(callback);
    write_length_ = length;
    device_->Send(AdbMessage::kCommandWRTE, local_id_, remote_id_,
                  std::string(buffer->data(), length));
    return net::ERR_IO_PENDING;
  }
  
  int AndroidUsbSocket::SetReceiveBufferSize(int32_t size) {
    NOTIMPLEMENTED();
    return net::ERR_NOT_IMPLEMENTED;
  }
  
  int AndroidUsbSocket::SetSendBufferSize(int32_t size) {
    NOTIMPLEMENTED();
    return net::ERR_NOT_IMPLEMENTED;
  }
  
  int AndroidUsbSocket::Connect(net::CompletionOnceCallback callback) {
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    DCHECK(!callback.is_null());
    if (!device_.get())
      return net::ERR_FAILED;
  
    DCHECK(!is_connected_);
    DCHECK(connect_callback_.is_null());
    connect_callback_ = std::move(callback);
    device_->Send(AdbMessage::kCommandOPEN, local_id_, 0, command_);
    return net::ERR_IO_PENDING;
  }
  
  void AndroidUsbSocket::Disconnect() {
    if (!device_.get())
      return;
    device_->Send(AdbMessage::kCommandCLSE, local_id_, remote_id_, "");
    Terminated(false);
  }
  
  bool AndroidUsbSocket::IsConnected() const {
    DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
    return is_connected_;
  }
  
  bool AndroidUsbSocket::IsConnectedAndIdle() const {
    NOTIMPLEMENTED();
    return false;
  }
  
  int AndroidUsbSocket::GetPeerAddress(net::IPEndPoint* address) const {
    *address = net::IPEndPoint(net::IPAddress(0, 0, 0, 0), 0);
    return net::OK;
  }
  
  int AndroidUsbSocket::GetLocalAddress(net::IPEndPoint* address) const {
    NOTIMPLEMENTED();
    return net::ERR_NOT_IMPLEMENTED;
  }
  
  const net::NetLogWithSource& AndroidUsbSocket::NetLog() const {
    return net_log_;
  }
  
  bool AndroidUsbSocket::WasEverUsed() const {
    NOTIMPLEMENTED();
    return true;
  }
  
  bool AndroidUsbSocket::WasAlpnNegotiated() const {
    NOTIMPLEMENTED();
    return true;
  }
  
  net::NextProto AndroidUsbSocket::GetNegotiatedProtocol() const {
    NOTIMPLEMENTED();
    return net::kProtoUnknown;
  }
  
  bool AndroidUsbSocket::GetSSLInfo(net::SSLInfo* ssl_info) {
    return false;
  }
  
  void AndroidUsbSocket::GetConnectionAttempts(
      net::ConnectionAttempts* out) const {
    out->clear();
  }
  
  int64_t AndroidUsbSocket::GetTotalReceivedBytes() const {
    NOTIMPLEMENTED();
    return 0;
  }
  
  void AndroidUsbSocket::ApplySocketTag(const net::SocketTag& tag) {
    NOTIMPLEMENTED();
  }
  
  void AndroidUsbSocket::RespondToReader(bool disconnect) {
    if (read_callback_.is_null() || (read_buffer_.empty() && !disconnect))
      return;
    size_t bytes_to_copy =
        static_cast<size_t>(read_length_) > read_buffer_.length() ?
            read_buffer_.length() : static_cast<size_t>(read_length_);
    memcpy(read_io_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_ = std::string();
    std::move(read_callback_).Run(bytes_to_copy);
  }
  
  void AndroidUsbSocket::RespondToWriter(int result) {
    if (!write_callback_.is_null())
      std::move(write_callback_).Run(result);
  }
  