device_controller.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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_.set_exit_notifier_fd(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                                      true /* abstract */)) {
51    LOG(ERROR) << "Could not BindAndListen DeviceController socket on port "
52               << adb_unix_socket << ": " << safe_strerror(errno);
53    return false;
54  }
55  LOG(INFO) << "Listening on Unix Domain Socket " << adb_unix_socket;
56  return true;
57}
58
59void DeviceController::Start() {
60  while (true) {
61    CleanUpDeadListeners();
62    scoped_ptr<Socket> socket(new Socket);
63    if (!kickstart_adb_socket_.Accept(socket.get())) {
64      if (!kickstart_adb_socket_.exited()) {
65        LOG(ERROR) << "Could not Accept DeviceController socket: "
66                   << safe_strerror(errno);
67      } else {
68        LOG(INFO) << "Received exit notification";
69      }
70      break;
71    }
72    // So that |socket| doesn't block on read if it has notifications.
73    socket->set_exit_notifier_fd(exit_notifier_fd_);
74    int port;
75    command::Type command;
76    if (!ReadCommand(socket.get(), &port, &command)) {
77      LOG(ERROR) << "Invalid command received.";
78      continue;
79    }
80    DeviceListener* listener = listeners_.Lookup(port);
81    switch (command) {
82      case command::LISTEN: {
83        if (listener != NULL) {
84          LOG(WARNING) << "Already forwarding port " << port
85                       << ". Attempting to restart the listener.\n";
86          listener->ForceExit();
87          listener->Join();
88          CHECK(!listener->is_alive());
89          // Remove deletes the listener object.
90          listeners_.Remove(port);
91        }
92        scoped_ptr<DeviceListener> new_listener(
93            new DeviceListener(socket.Pass(), port));
94        if (!new_listener->BindListenerSocket())
95          continue;
96        new_listener->Start();
97        // |port| can be zero, to allow dynamically allocated port, so instead,
98        // we call DeviceListener::listener_port() to retrieve the currently
99        // allocated port to this new listener, which has been set by the
100        // BindListenerSocket() method in case of success.
101        const int listener_port = new_listener->listener_port();
102        // |new_listener| is now owned by listeners_ map.
103        listeners_.AddWithID(new_listener.release(), listener_port);
104        LOG(INFO) << "Forwarding device port " << listener_port << " to host.";
105        break;
106      }
107      case command::DATA_CONNECTION:
108        if (listener == NULL) {
109          LOG(ERROR) << "Data Connection command received, but "
110                     << "listener has not been set up yet for port " << port;
111          // After this point it is assumed that, once we close our Adb Data
112          // socket, the Adb forwarder command will propagate the closing of
113          // sockets all the way to the host side.
114          continue;
115        } else if (!listener->SetAdbDataSocket(socket.Pass())) {
116          LOG(ERROR) << "Could not set Adb Data Socket for port: " << port;
117          // Same assumption as above, but in this case the socket is closed
118          // inside SetAdbDataSocket.
119          continue;
120        }
121        break;
122      default:
123        // TODO(felipeg): add a KillAllListeners command.
124        LOG(ERROR) << "Invalid command received. Port: " << port
125                   << " Command: " << command;
126    }
127  }
128  KillAllListeners();
129  CleanUpDeadListeners();
130}
131
132}  // namespace forwarder
133