// Copyright (c) 2012 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 "tools/android/forwarder2/host_controller.h" #include #include "base/basictypes.h" #include "base/bind.h" #include "base/bind_helpers.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "tools/android/forwarder2/command.h" #include "tools/android/forwarder2/forwarder.h" #include "tools/android/forwarder2/socket.h" namespace forwarder2 { // static scoped_ptr HostController::Create( int device_port, int host_port, int adb_port, int exit_notifier_fd, const ErrorCallback& error_callback) { scoped_ptr host_controller; scoped_ptr delete_controller_notifier(new PipeNotifier()); scoped_ptr adb_control_socket(new Socket()); adb_control_socket->AddEventFd(exit_notifier_fd); adb_control_socket->AddEventFd(delete_controller_notifier->receiver_fd()); if (!adb_control_socket->ConnectTcp(std::string(), adb_port)) { LOG(ERROR) << "Could not connect HostController socket on port: " << adb_port; return host_controller.Pass(); } // Send the command to the device start listening to the "device_forward_port" bool send_command_success = SendCommand( command::LISTEN, device_port, adb_control_socket.get()); CHECK(send_command_success); int device_port_allocated; command::Type command; if (!ReadCommand( adb_control_socket.get(), &device_port_allocated, &command) || command != command::BIND_SUCCESS) { LOG(ERROR) << "Device binding error using port " << device_port; return host_controller.Pass(); } host_controller.reset( new HostController( device_port_allocated, host_port, adb_port, exit_notifier_fd, error_callback, adb_control_socket.Pass(), delete_controller_notifier.Pass())); return host_controller.Pass(); } HostController::~HostController() { DCHECK(deletion_task_runner_->RunsTasksOnCurrentThread()); delete_controller_notifier_->Notify(); } void HostController::Start() { thread_.Start(); ReadNextCommandSoon(); } HostController::HostController( int device_port, int host_port, int adb_port, int exit_notifier_fd, const ErrorCallback& error_callback, scoped_ptr adb_control_socket, scoped_ptr delete_controller_notifier) : self_deleter_helper_(this, error_callback), device_port_(device_port), host_port_(host_port), adb_port_(adb_port), global_exit_notifier_fd_(exit_notifier_fd), adb_control_socket_(adb_control_socket.Pass()), delete_controller_notifier_(delete_controller_notifier.Pass()), deletion_task_runner_(base::MessageLoopProxy::current()), thread_("HostControllerThread") { } void HostController::ReadNextCommandSoon() { thread_.message_loop_proxy()->PostTask( FROM_HERE, base::Bind(&HostController::ReadCommandOnInternalThread, base::Unretained(this))); } void HostController::ReadCommandOnInternalThread() { if (!ReceivedCommand(command::ACCEPT_SUCCESS, adb_control_socket_.get())) { LOG(ERROR) << "Did not receive ACCEPT_SUCCESS for port: " << host_port_; OnInternalThreadError(); return; } // Try to connect to host server. scoped_ptr host_server_data_socket(new Socket()); if (!host_server_data_socket->ConnectTcp(std::string(), host_port_)) { LOG(ERROR) << "Could not Connect HostServerData socket on port: " << host_port_; SendCommand( command::HOST_SERVER_ERROR, device_port_, adb_control_socket_.get()); if (ReceivedCommand(command::ACK, adb_control_socket_.get())) { // It can continue if the host forwarder could not connect to the host // server but the device acknowledged that, so that the device could // re-try later. ReadNextCommandSoon(); return; } OnInternalThreadError(); return; } LOG(INFO) << "Will send HOST_SERVER_SUCCESS: " << host_port_; SendCommand( command::HOST_SERVER_SUCCESS, device_port_, adb_control_socket_.get()); StartForwarder(host_server_data_socket.Pass()); ReadNextCommandSoon(); } void HostController::StartForwarder( scoped_ptr host_server_data_socket) { scoped_ptr adb_data_socket(new Socket()); if (!adb_data_socket->ConnectTcp("", adb_port_)) { LOG(ERROR) << "Could not connect AdbDataSocket on port: " << adb_port_; OnInternalThreadError(); return; } // Open the Adb data connection, and send a command with the // |device_forward_port| as a way for the device to identify the connection. SendCommand(command::DATA_CONNECTION, device_port_, adb_data_socket.get()); // Check that the device received the new Adb Data Connection. Note that this // check is done through the |adb_control_socket_| that is handled in the // DeviceListener thread just after the call to WaitForAdbDataSocket(). if (!ReceivedCommand(command::ADB_DATA_SOCKET_SUCCESS, adb_control_socket_.get())) { LOG(ERROR) << "Device could not handle the new Adb Data Connection."; OnInternalThreadError(); return; } forwarders_manager_.CreateAndStartNewForwarder( host_server_data_socket.Pass(), adb_data_socket.Pass()); } void HostController::OnInternalThreadError() { UnmapPortOnDevice(); self_deleter_helper_.MaybeSelfDeleteSoon(); } void HostController::UnmapPortOnDevice() { Socket socket; if (!socket.ConnectTcp("", adb_port_)) { LOG(ERROR) << "Could not connect to device on port " << adb_port_; return; } if (!SendCommand(command::UNLISTEN, device_port_, &socket)) { LOG(ERROR) << "Could not send unmap command for port " << device_port_; return; } if (!ReceivedCommand(command::UNLISTEN_SUCCESS, &socket)) { LOG(ERROR) << "Unamp command failed for port " << device_port_; return; } } } // namespace forwarder2