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/device_controller.h"
6
7#include <utility>
8
9#include "base/basictypes.h"
10#include "base/bind.h"
11#include "base/callback_helpers.h"
12#include "base/logging.h"
13#include "base/memory/scoped_ptr.h"
14#include "base/message_loop/message_loop_proxy.h"
15#include "base/single_thread_task_runner.h"
16#include "tools/android/forwarder2/command.h"
17#include "tools/android/forwarder2/device_listener.h"
18#include "tools/android/forwarder2/socket.h"
19#include "tools/android/forwarder2/util.h"
20
21namespace forwarder2 {
22
23// static
24scoped_ptr<DeviceController> DeviceController::Create(
25    const std::string& adb_unix_socket,
26    int exit_notifier_fd) {
27  scoped_ptr<DeviceController> device_controller;
28  scoped_ptr<Socket> host_socket(new Socket());
29  if (!host_socket->BindUnix(adb_unix_socket)) {
30    PLOG(ERROR) << "Could not BindAndListen DeviceController socket on port "
31                << adb_unix_socket << ": ";
32    return device_controller.Pass();
33  }
34  LOG(INFO) << "Listening on Unix Domain Socket " << adb_unix_socket;
35  device_controller.reset(
36      new DeviceController(host_socket.Pass(), exit_notifier_fd));
37  return device_controller.Pass();
38}
39
40DeviceController::~DeviceController() {
41  DCHECK(construction_task_runner_->RunsTasksOnCurrentThread());
42}
43
44void DeviceController::Start() {
45  AcceptHostCommandSoon();
46}
47
48DeviceController::DeviceController(scoped_ptr<Socket> host_socket,
49                                   int exit_notifier_fd)
50    : host_socket_(host_socket.Pass()),
51      exit_notifier_fd_(exit_notifier_fd),
52      construction_task_runner_(base::MessageLoopProxy::current()),
53      weak_ptr_factory_(this) {
54  host_socket_->AddEventFd(exit_notifier_fd);
55}
56
57void DeviceController::AcceptHostCommandSoon() {
58  base::MessageLoopProxy::current()->PostTask(
59      FROM_HERE,
60      base::Bind(&DeviceController::AcceptHostCommandInternal,
61                 base::Unretained(this)));
62}
63
64void DeviceController::AcceptHostCommandInternal() {
65  scoped_ptr<Socket> socket(new Socket);
66  if (!host_socket_->Accept(socket.get())) {
67    if (!host_socket_->DidReceiveEvent())
68      PLOG(ERROR) << "Could not Accept DeviceController socket";
69    else
70      LOG(INFO) << "Received exit notification";
71    return;
72  }
73  base::ScopedClosureRunner accept_next_client(
74      base::Bind(&DeviceController::AcceptHostCommandSoon,
75                 base::Unretained(this)));
76  // So that |socket| doesn't block on read if it has notifications.
77  socket->AddEventFd(exit_notifier_fd_);
78  int port;
79  command::Type command;
80  if (!ReadCommand(socket.get(), &port, &command)) {
81    LOG(ERROR) << "Invalid command received.";
82    return;
83  }
84  const ListenersMap::iterator listener_it = listeners_.find(port);
85  DeviceListener* const listener = listener_it == listeners_.end()
86      ? static_cast<DeviceListener*>(NULL) : listener_it->second.get();
87  switch (command) {
88    case command::LISTEN: {
89      if (listener != NULL) {
90        LOG(WARNING) << "Already forwarding port " << port
91                     << ". Attempting to restart the listener.\n";
92        DeleteRefCountedValueInMapFromIterator(listener_it, &listeners_);
93      }
94      scoped_ptr<DeviceListener> new_listener(
95          DeviceListener::Create(
96              socket.Pass(), port,
97              base::Bind(&DeviceController::DeleteListenerOnError,
98                         weak_ptr_factory_.GetWeakPtr())));
99      if (!new_listener)
100        return;
101      new_listener->Start();
102      // |port| can be zero, to allow dynamically allocated port, so instead, we
103      // call DeviceListener::listener_port() to retrieve the currently
104      // allocated port to this new listener.
105      const int listener_port = new_listener->listener_port();
106      listeners_.insert(
107          std::make_pair(listener_port,
108                         linked_ptr<DeviceListener>(new_listener.release())));
109      LOG(INFO) << "Forwarding device port " << listener_port << " to host.";
110      break;
111    }
112    case command::DATA_CONNECTION:
113      if (listener == NULL) {
114        LOG(ERROR) << "Data Connection command received, but "
115                   << "listener has not been set up yet for port " << port;
116        // After this point it is assumed that, once we close our Adb Data
117        // socket, the Adb forwarder command will propagate the closing of
118        // sockets all the way to the host side.
119        break;
120      }
121      listener->SetAdbDataSocket(socket.Pass());
122      break;
123    case command::UNLISTEN:
124      LOG(INFO) << "Unmapping port " << port;
125      if (!listener) {
126        LOG(ERROR) << "No listener found for port " << port;
127        SendCommand(command::UNLISTEN_ERROR, port, socket.get());
128        break;
129      }
130      DeleteRefCountedValueInMapFromIterator(listener_it, &listeners_);
131      SendCommand(command::UNLISTEN_SUCCESS, port, socket.get());
132      break;
133    default:
134      // TODO(felipeg): add a KillAllListeners command.
135      LOG(ERROR) << "Invalid command received. Port: " << port
136                 << " Command: " << command;
137  }
138}
139
140// static
141void DeviceController::DeleteListenerOnError(
142      const base::WeakPtr<DeviceController>& device_controller_ptr,
143      scoped_ptr<DeviceListener> device_listener) {
144  DeviceListener* const listener = device_listener.release();
145  DeviceController* const controller = device_controller_ptr.get();
146  if (!controller) {
147    // |listener| was already deleted by the controller that did have its
148    // ownership.
149    return;
150  }
151  DCHECK(controller->construction_task_runner_->RunsTasksOnCurrentThread());
152  bool listener_did_exist = DeleteRefCountedValueInMap(
153      listener->listener_port(), &controller->listeners_);
154  DCHECK(listener_did_exist);
155  // Note that |listener| was deleted by DeleteRefCountedValueInMap().
156}
157
158}  // namespace forwarder
159