Index: tools/android/forwarder2/device_controller.cc |
diff --git a/tools/android/forwarder2/device_controller.cc b/tools/android/forwarder2/device_controller.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..ca54e2e7f0f7f9b0b1811a786a15cfe19e483b74 |
--- /dev/null |
+++ b/tools/android/forwarder2/device_controller.cc |
@@ -0,0 +1,124 @@ |
+// 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 forwarder { |
+ |
+DeviceController::DeviceController(int notifier_fd) |
+ : exit_notifier_fd_(notifier_fd) { |
+ kickstart_adb_socket_.set_notifier_fd(notifier_fd); |
+} |
+ |
+DeviceController::~DeviceController() { |
+ KillAllListeners(); |
+ CleanUpDeadListeners(); |
+ CHECK_EQ(0, listeners_.size()); |
+} |
+ |
+void DeviceController::CleanUpDeadListeners() { |
+ // Clean up dead listeners. |
+ for (ListenersMap::iterator it = listeners_.begin(); |
+ it != listeners_.end();) { |
+ if (!it->second->is_alive()) { |
+ ListenersMap::iterator to_erase(it); |
+ to_erase->second.reset(); |
+ ++it; |
+ listeners_.erase(to_erase); |
+ } else { |
+ ++it; |
+ } |
+ } |
+} |
+ |
+void DeviceController::KillAllListeners() { |
+ ListenersMap::iterator it; |
+ for (it = listeners_.begin(); it != listeners_.end(); ++it) { |
+ it->second->ForceExit(); |
+ } |
+ for (it = listeners_.begin(); it != listeners_.end(); ++it) { |
+ it->second->Join(); |
+ CHECK(!it->second->is_alive()); |
+ } |
+} |
+ |
+bool DeviceController::Start(const string& adb_unix_socket) { |
+ if (!kickstart_adb_socket_.BindUnix(adb_unix_socket, |
+ /* abstract = */ true)) { |
+ 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_notifier_fd(exit_notifier_fd_); |
+ int port; |
+ command::Type command; |
+ if (!ReadCommand(socket.get(), &port, &command)) { |
+ LOG(ERROR) << "Invalid command received."; |
+ socket->Close(); |
+ continue; |
+ } |
+ ListenersMap::iterator listener_it(listeners_.find(port)); |
+ switch (command) { |
+ case command::LISTEN: |
+ if (listener_it != listeners_.end()) { |
+ LOG(WARNING) << "Already forwarding port " << port |
+ << ". Attempting to restart the listener.\n"; |
+ listener_it->second->ForceExit(); |
+ listener_it->second->Join(); |
+ CHECK(!listener_it->second->is_alive()); |
+ } else { |
+ // Listener object will be deleted by the CleanUpDeadListeners method. |
+ DeviceListener* listener = new DeviceListener(socket.Pass(), port); |
+ listeners_[port].reset(listener); |
+ listener->Start(); |
+ } |
+ break; |
+ case command::DATA_CONNECTION: |
+ if (listener_it == listeners_.end()) { |
+ 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_it->second->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."; |
+ socket->Close(); |
+ continue; |
+ } |
+ } |
+ KillAllListeners(); |
+ CleanUpDeadListeners(); |
+ kickstart_adb_socket_.Close(); |
+} |
+ |
+} // namespace forwarder |