device_controller.cc revision 90dce4d38c5ff5333bea97d859d4e484e27edf0c
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 <errno.h>
8#include <stdlib.h>
9
10#include "base/logging.h"
11#include "base/memory/scoped_ptr.h"
12#include "base/safe_strerror_posix.h"
13#include "tools/android/forwarder2/command.h"
14#include "tools/android/forwarder2/device_listener.h"
15#include "tools/android/forwarder2/socket.h"
16
17namespace forwarder2 {
18
19DeviceController::DeviceController(int exit_notifier_fd)
20    : exit_notifier_fd_(exit_notifier_fd) {
21  kickstart_adb_socket_.AddEventFd(exit_notifier_fd);
22}
23
24DeviceController::~DeviceController() {
25  KillAllListeners();
26  CleanUpDeadListeners();
27  CHECK_EQ(0, listeners_.size());
28}
29
30void DeviceController::CleanUpDeadListeners() {
31  // Clean up dead listeners.
32  for (ListenersMap::iterator it(&listeners_); !it.IsAtEnd(); it.Advance()) {
33    if (!it.GetCurrentValue()->is_alive())
34      // Remove deletes the listener.
35      listeners_.Remove(it.GetCurrentKey());
36  }
37}
38
39void DeviceController::KillAllListeners() {
40  for (ListenersMap::iterator it(&listeners_); !it.IsAtEnd(); it.Advance())
41    it.GetCurrentValue()->ForceExit();
42  for (ListenersMap::iterator it(&listeners_); !it.IsAtEnd(); it.Advance()) {
43    it.GetCurrentValue()->Join();
44    CHECK(!it.GetCurrentValue()->is_alive());
45  }
46}
47
48bool DeviceController::Init(const std::string& adb_unix_socket) {
49  if (!kickstart_adb_socket_.BindUnix(adb_unix_socket)) {
50    LOG(ERROR) << "Could not BindAndListen DeviceController socket on port "
51               << adb_unix_socket << ": " << safe_strerror(errno);
52    return false;
53  }
54  LOG(INFO) << "Listening on Unix Domain Socket " << adb_unix_socket;
55  return true;
56}
57
58void DeviceController::Start() {
59  while (true) {
60    CleanUpDeadListeners();
61    scoped_ptr<Socket> socket(new Socket);
62    if (!kickstart_adb_socket_.Accept(socket.get())) {
63      if (!kickstart_adb_socket_.DidReceiveEvent()) {
64        LOG(ERROR) << "Could not Accept DeviceController socket: "
65                   << safe_strerror(errno);
66      } else {
67        LOG(INFO) << "Received exit notification";
68      }
69      break;
70    }
71    // So that |socket| doesn't block on read if it has notifications.
72    socket->AddEventFd(exit_notifier_fd_);
73    int port;
74    command::Type command;
75    if (!ReadCommand(socket.get(), &port, &command)) {
76      LOG(ERROR) << "Invalid command received.";
77      continue;
78    }
79    DeviceListener* listener = listeners_.Lookup(port);
80    switch (command) {
81      case command::LISTEN: {
82        if (listener != NULL) {
83          LOG(WARNING) << "Already forwarding port " << port
84                       << ". Attempting to restart the listener.\n";
85          listener->ForceExit();
86          listener->Join();
87          CHECK(!listener->is_alive());
88          // Remove deletes the listener object.
89          listeners_.Remove(port);
90        }
91        scoped_ptr<DeviceListener> new_listener(
92            new DeviceListener(socket.Pass(), port));
93        if (!new_listener->BindListenerSocket())
94          continue;
95        new_listener->Start();
96        // |port| can be zero, to allow dynamically allocated port, so instead,
97        // we call DeviceListener::listener_port() to retrieve the currently
98        // allocated port to this new listener, which has been set by the
99        // BindListenerSocket() method in case of success.
100        const int listener_port = new_listener->listener_port();
101        // |new_listener| is now owned by listeners_ map.
102        listeners_.AddWithID(new_listener.release(), listener_port);
103        LOG(INFO) << "Forwarding device port " << listener_port << " to host.";
104        break;
105      }
106      case command::DATA_CONNECTION:
107        if (listener == NULL) {
108          LOG(ERROR) << "Data Connection command received, but "
109                     << "listener has not been set up yet for port " << port;
110          // After this point it is assumed that, once we close our Adb Data
111          // socket, the Adb forwarder command will propagate the closing of
112          // sockets all the way to the host side.
113          continue;
114        } else if (!listener->SetAdbDataSocket(socket.Pass())) {
115          LOG(ERROR) << "Could not set Adb Data Socket for port: " << port;
116          // Same assumption as above, but in this case the socket is closed
117          // inside SetAdbDataSocket.
118          continue;
119        }
120        break;
121      default:
122        // TODO(felipeg): add a KillAllListeners command.
123        LOG(ERROR) << "Invalid command received. Port: " << port
124                   << " Command: " << command;
125    }
126  }
127  KillAllListeners();
128  CleanUpDeadListeners();
129}
130
131}  // namespace forwarder
132