1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "tools/android/forwarder2/host_controller.h"
6
7#include <string>
8
9#include "base/basictypes.h"
10#include "base/bind.h"
11#include "base/bind_helpers.h"
12#include "base/logging.h"
13#include "base/memory/scoped_ptr.h"
14#include "tools/android/forwarder2/command.h"
15#include "tools/android/forwarder2/forwarder.h"
16#include "tools/android/forwarder2/socket.h"
17
18namespace forwarder2 {
19
20// static
21scoped_ptr<HostController> HostController::Create(
22    int device_port,
23    int host_port,
24    int adb_port,
25    int exit_notifier_fd,
26    const ErrorCallback& error_callback) {
27  scoped_ptr<HostController> host_controller;
28  scoped_ptr<PipeNotifier> delete_controller_notifier(new PipeNotifier());
29  scoped_ptr<Socket> adb_control_socket(new Socket());
30  adb_control_socket->AddEventFd(exit_notifier_fd);
31  adb_control_socket->AddEventFd(delete_controller_notifier->receiver_fd());
32  if (!adb_control_socket->ConnectTcp(std::string(), adb_port)) {
33    LOG(ERROR) << "Could not connect HostController socket on port: "
34               << adb_port;
35    return host_controller.Pass();
36  }
37  // Send the command to the device start listening to the "device_forward_port"
38  bool send_command_success = SendCommand(
39      command::LISTEN, device_port, adb_control_socket.get());
40  CHECK(send_command_success);
41  int device_port_allocated;
42  command::Type command;
43  if (!ReadCommand(
44          adb_control_socket.get(), &device_port_allocated, &command) ||
45      command != command::BIND_SUCCESS) {
46    LOG(ERROR) << "Device binding error using port " << device_port;
47    return host_controller.Pass();
48  }
49  host_controller.reset(
50      new HostController(
51          device_port_allocated, host_port, adb_port, exit_notifier_fd,
52          error_callback, adb_control_socket.Pass(),
53          delete_controller_notifier.Pass()));
54  return host_controller.Pass();
55}
56
57HostController::~HostController() {
58  DCHECK(deletion_task_runner_->RunsTasksOnCurrentThread());
59  delete_controller_notifier_->Notify();
60}
61
62void HostController::Start() {
63  thread_.Start();
64  ReadNextCommandSoon();
65}
66
67HostController::HostController(
68    int device_port,
69    int host_port,
70    int adb_port,
71    int exit_notifier_fd,
72    const ErrorCallback& error_callback,
73    scoped_ptr<Socket> adb_control_socket,
74    scoped_ptr<PipeNotifier> delete_controller_notifier)
75    : self_deleter_helper_(this, error_callback),
76      device_port_(device_port),
77      host_port_(host_port),
78      adb_port_(adb_port),
79      global_exit_notifier_fd_(exit_notifier_fd),
80      adb_control_socket_(adb_control_socket.Pass()),
81      delete_controller_notifier_(delete_controller_notifier.Pass()),
82      deletion_task_runner_(base::MessageLoopProxy::current()),
83      thread_("HostControllerThread") {
84}
85
86void HostController::ReadNextCommandSoon() {
87  thread_.message_loop_proxy()->PostTask(
88      FROM_HERE,
89      base::Bind(&HostController::ReadCommandOnInternalThread,
90                 base::Unretained(this)));
91}
92
93void HostController::ReadCommandOnInternalThread() {
94  if (!ReceivedCommand(command::ACCEPT_SUCCESS, adb_control_socket_.get())) {
95    LOG(ERROR) << "Did not receive ACCEPT_SUCCESS for port: "
96               << host_port_;
97    OnInternalThreadError();
98    return;
99  }
100  // Try to connect to host server.
101  scoped_ptr<Socket> host_server_data_socket(new Socket());
102  if (!host_server_data_socket->ConnectTcp(std::string(), host_port_)) {
103    LOG(ERROR) << "Could not Connect HostServerData socket on port: "
104               << host_port_;
105    SendCommand(
106        command::HOST_SERVER_ERROR, device_port_, adb_control_socket_.get());
107    if (ReceivedCommand(command::ACK, adb_control_socket_.get())) {
108      // It can continue if the host forwarder could not connect to the host
109      // server but the device acknowledged that, so that the device could
110      // re-try later.
111      ReadNextCommandSoon();
112      return;
113    }
114    OnInternalThreadError();
115    return;
116  }
117  LOG(INFO) << "Will send HOST_SERVER_SUCCESS: " << host_port_;
118  SendCommand(
119      command::HOST_SERVER_SUCCESS, device_port_, adb_control_socket_.get());
120  StartForwarder(host_server_data_socket.Pass());
121  ReadNextCommandSoon();
122}
123
124void HostController::StartForwarder(
125    scoped_ptr<Socket> host_server_data_socket) {
126  scoped_ptr<Socket> adb_data_socket(new Socket());
127  if (!adb_data_socket->ConnectTcp("", adb_port_)) {
128    LOG(ERROR) << "Could not connect AdbDataSocket on port: " << adb_port_;
129    OnInternalThreadError();
130    return;
131  }
132  // Open the Adb data connection, and send a command with the
133  // |device_forward_port| as a way for the device to identify the connection.
134  SendCommand(command::DATA_CONNECTION, device_port_, adb_data_socket.get());
135
136  // Check that the device received the new Adb Data Connection. Note that this
137  // check is done through the |adb_control_socket_| that is handled in the
138  // DeviceListener thread just after the call to WaitForAdbDataSocket().
139  if (!ReceivedCommand(command::ADB_DATA_SOCKET_SUCCESS,
140                       adb_control_socket_.get())) {
141    LOG(ERROR) << "Device could not handle the new Adb Data Connection.";
142    OnInternalThreadError();
143    return;
144  }
145  forwarders_manager_.CreateAndStartNewForwarder(
146      host_server_data_socket.Pass(), adb_data_socket.Pass());
147}
148
149void HostController::OnInternalThreadError() {
150  UnmapPortOnDevice();
151  self_deleter_helper_.MaybeSelfDeleteSoon();
152}
153
154void HostController::UnmapPortOnDevice() {
155  Socket socket;
156  if (!socket.ConnectTcp("", adb_port_)) {
157    LOG(ERROR) << "Could not connect to device on port " << adb_port_;
158    return;
159  }
160  if (!SendCommand(command::UNLISTEN, device_port_, &socket)) {
161    LOG(ERROR) << "Could not send unmap command for port " << device_port_;
162    return;
163  }
164  if (!ReceivedCommand(command::UNLISTEN_SUCCESS, &socket)) {
165    LOG(ERROR) << "Unamp command failed for port " << device_port_;
166    return;
167  }
168}
169
170}  // namespace forwarder2
171