summaryrefslogtreecommitdiffstats
path: root/tools/android/forwarder2/device_controller.cc
blob: e0effb8dcd2feee4c159c6d403a3024b5e51bd5d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
// 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/device_controller.h"

#include <errno.h>
#include <stdlib.h>

#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/safe_strerror_posix.h"
#include "tools/android/forwarder2/command.h"
#include "tools/android/forwarder2/device_listener.h"
#include "tools/android/forwarder2/socket.h"

namespace forwarder2 {

DeviceController::DeviceController(int exit_notifier_fd)
    : exit_notifier_fd_(exit_notifier_fd) {
  kickstart_adb_socket_.set_exit_notifier_fd(exit_notifier_fd);
}

DeviceController::~DeviceController() {
  KillAllListeners();
  CleanUpDeadListeners();
  CHECK_EQ(0, listeners_.size());
}

void DeviceController::CleanUpDeadListeners() {
  // Clean up dead listeners.
  for (ListenersMap::iterator it(&listeners_); !it.IsAtEnd(); it.Advance()) {
    if (!it.GetCurrentValue()->is_alive())
      // Remove deletes the listener.
      listeners_.Remove(it.GetCurrentKey());
  }
}

void DeviceController::KillAllListeners() {
  for (ListenersMap::iterator it(&listeners_); !it.IsAtEnd(); it.Advance())
    it.GetCurrentValue()->ForceExit();
  for (ListenersMap::iterator it(&listeners_); !it.IsAtEnd(); it.Advance()) {
    it.GetCurrentValue()->Join();
    CHECK(!it.GetCurrentValue()->is_alive());
  }
}

bool DeviceController::Start(const std::string& adb_unix_socket) {
  if (!kickstart_adb_socket_.BindUnix(adb_unix_socket,
                                      true /* abstract */)) {
    LOG(ERROR) << "Could not BindAndListen DeviceController socket on port: "
               << adb_unix_socket;
    return false;
  }
  while (true) {
    CleanUpDeadListeners();
    scoped_ptr<Socket> socket(new Socket);
    if (!kickstart_adb_socket_.Accept(socket.get())) {
      LOG(ERROR) << "Could not Accept DeviceController socket: "
                 << safe_strerror(errno);
      break;
    }
    // So that |socket| doesn't block on read if it has notifications.
    socket->set_exit_notifier_fd(exit_notifier_fd_);
    int port;
    command::Type command;
    if (!ReadCommand(socket.get(), &port, &command)) {
      LOG(ERROR) << "Invalid command received.";
      socket->Close();
      continue;
    }
    DeviceListener* listener = listeners_.Lookup(port);
    switch (command) {
      case command::LISTEN: {
        if (listener != NULL) {
          LOG(WARNING) << "Already forwarding port " << port
                       << ". Attempting to restart the listener.\n";
          listener->ForceExit();
          listener->Join();
          CHECK(!listener->is_alive());
          // Remove deletes the listener object.
          listeners_.Remove(port);
        }
        // Listener object will be deleted by the CleanUpDeadListeners method.
        DeviceListener* new_listener = new DeviceListener(socket.Pass(), port);
        listeners_.AddWithID(new_listener, port);
        new_listener->Start();
        break;
      }
      case command::DATA_CONNECTION:
        if (listener == NULL) {
          LOG(ERROR) << "Data Connection command received, but "
                     << "listener has not been set up yet for port " << port;
          // After this point it is assumed that, once we close our Adb Data
          // socket, the Adb forwarder command will propagate the closing of
          // sockets all the way to the host side.
          socket->Close();
          continue;
        } else if (!listener->SetAdbDataSocket(socket.Pass())) {
          LOG(ERROR) << "Could not set Adb Data Socket for port: " << port;
          // Same assumption as above, but in this case the socket is closed
          // inside SetAdbDataSocket.
          continue;
        }
        break;
      default:
        // TODO(felipeg): add a KillAllListeners command.
        LOG(ERROR) << "Invalid command received. Port: " << port
                   << " Command: " << command;
        socket->Close();
        continue;
        break;
    }
  }
  KillAllListeners();
  CleanUpDeadListeners();
  kickstart_adb_socket_.Close();
}

}  // namespace forwarder