Index: tools/android/forwarder2/device_controller.cc |
diff --git a/tools/android/forwarder2/device_controller.cc b/tools/android/forwarder2/device_controller.cc |
index e9ce9cb7c37eb9f331b6ed670a3d432daae1816b..87d0e17143d22e6b1c81c3c823bfdeddea14ae35 100644 |
--- a/tools/android/forwarder2/device_controller.cc |
+++ b/tools/android/forwarder2/device_controller.cc |
@@ -4,139 +4,151 @@ |
#include "tools/android/forwarder2/device_controller.h" |
-#include <errno.h> |
-#include <stdlib.h> |
+#include <utility> |
+#include "base/bind.h" |
+#include "base/bind_helpers.h" |
#include "base/logging.h" |
#include "base/memory/scoped_ptr.h" |
-#include "base/safe_strerror_posix.h" |
+#include "base/message_loop/message_loop_proxy.h" |
+#include "base/single_thread_task_runner.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_.AddEventFd(exit_notifier_fd); |
+// static |
+scoped_ptr<DeviceController> DeviceController::Create( |
+ const std::string& adb_unix_socket, |
+ int exit_notifier_fd) { |
+ scoped_ptr<DeviceController> device_controller; |
+ scoped_ptr<Socket> host_socket(new Socket()); |
+ if (!host_socket->BindUnix(adb_unix_socket)) { |
+ PLOG(ERROR) << "Could not BindAndListen DeviceController socket on port " |
+ << adb_unix_socket << ": "; |
+ return device_controller.Pass(); |
+ } |
+ LOG(INFO) << "Listening on Unix Domain Socket " << adb_unix_socket; |
+ device_controller.reset( |
+ new DeviceController(host_socket.Pass(), exit_notifier_fd)); |
+ return device_controller.Pass(); |
} |
DeviceController::~DeviceController() { |
- KillAllListeners(); |
- CleanUpDeadListeners(); |
- CHECK_EQ(0, listeners_.size()); |
+ DCHECK(construction_task_runner_->RunsTasksOnCurrentThread()); |
} |
-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::Start() { |
+ AcceptHostCommandSoon(); |
} |
-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()); |
- } |
+DeviceController::DeviceController(scoped_ptr<Socket> host_socket, |
+ int exit_notifier_fd) |
+ : host_socket_(host_socket.Pass()), |
+ exit_notifier_fd_(exit_notifier_fd), |
+ construction_task_runner_(base::MessageLoopProxy::current()), |
+ weak_ptr_factory_(this) { |
+ host_socket_->AddEventFd(exit_notifier_fd); |
} |
-bool DeviceController::Init(const std::string& adb_unix_socket) { |
- if (!kickstart_adb_socket_.BindUnix(adb_unix_socket)) { |
- LOG(ERROR) << "Could not BindAndListen DeviceController socket on port " |
- << adb_unix_socket << ": " << safe_strerror(errno); |
- return false; |
- } |
- LOG(INFO) << "Listening on Unix Domain Socket " << adb_unix_socket; |
- return true; |
+void DeviceController::AcceptHostCommandSoon() { |
+ base::MessageLoopProxy::current()->PostTask( |
+ FROM_HERE, |
+ base::Bind(&DeviceController::AcceptHostCommandInternal, |
+ base::Unretained(this))); |
} |
-void DeviceController::Start() { |
- while (true) { |
- CleanUpDeadListeners(); |
- scoped_ptr<Socket> socket(new Socket); |
- if (!kickstart_adb_socket_.Accept(socket.get())) { |
- if (!kickstart_adb_socket_.DidReceiveEvent()) { |
- LOG(ERROR) << "Could not Accept DeviceController socket: " |
- << safe_strerror(errno); |
- } else { |
- LOG(INFO) << "Received exit notification"; |
+void DeviceController::AcceptHostCommandInternal() { |
+ scoped_ptr<Socket> socket(new Socket); |
+ if (!host_socket_->Accept(socket.get())) { |
+ if (!host_socket_->DidReceiveEvent()) |
+ PLOG(ERROR) << "Could not Accept DeviceController socket"; |
+ else |
+ LOG(INFO) << "Received exit notification"; |
+ return; |
+ } |
+ base::ScopedClosureRunner accept_next_client( |
+ base::Bind(&DeviceController::AcceptHostCommandSoon, |
+ base::Unretained(this))); |
+ // So that |socket| doesn't block on read if it has notifications. |
+ socket->AddEventFd(exit_notifier_fd_); |
+ int port; |
+ command::Type command; |
+ if (!ReadCommand(socket.get(), &port, &command)) { |
+ LOG(ERROR) << "Invalid command received."; |
+ return; |
+ } |
+ const ListenersMap::iterator listener_it = listeners_.find(port); |
+ DeviceListener* const listener = listener_it == listeners_.end() |
+ ? static_cast<DeviceListener*>(NULL) : listener_it->second.get(); |
+ switch (command) { |
+ case command::LISTEN: { |
+ if (listener != NULL) { |
+ LOG(WARNING) << "Already forwarding port " << port |
+ << ". Attempting to restart the listener.\n"; |
+ // Note that this deletes the listener object. |
+ listeners_.erase(listener_it); |
} |
+ scoped_ptr<DeviceListener> new_listener( |
+ DeviceListener::Create( |
+ socket.Pass(), port, base::Bind(&DeviceController::DeleteListener, |
+ weak_ptr_factory_.GetWeakPtr()))); |
+ if (!new_listener) |
+ return; |
+ new_listener->Start(); |
+ // |port| can be zero, to allow dynamically allocated port, so instead, we |
+ // call DeviceListener::listener_port() to retrieve the currently |
+ // allocated port to this new listener. |
+ const int listener_port = new_listener->listener_port(); |
+ listeners_.insert( |
+ std::make_pair(listener_port, |
+ linked_ptr<DeviceListener>(new_listener.release()))); |
+ LOG(INFO) << "Forwarding device port " << listener_port << " to host."; |
break; |
} |
- // So that |socket| doesn't block on read if it has notifications. |
- socket->AddEventFd(exit_notifier_fd_); |
- int port; |
- command::Type command; |
- if (!ReadCommand(socket.get(), &port, &command)) { |
- LOG(ERROR) << "Invalid command received."; |
- 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); |
- } |
- scoped_ptr<DeviceListener> new_listener( |
- new DeviceListener(socket.Pass(), port)); |
- if (!new_listener->BindListenerSocket()) |
- continue; |
- new_listener->Start(); |
- // |port| can be zero, to allow dynamically allocated port, so instead, |
- // we call DeviceListener::listener_port() to retrieve the currently |
- // allocated port to this new listener, which has been set by the |
- // BindListenerSocket() method in case of success. |
- const int listener_port = new_listener->listener_port(); |
- // |new_listener| is now owned by listeners_ map. |
- listeners_.AddWithID(new_listener.release(), listener_port); |
- LOG(INFO) << "Forwarding device port " << listener_port << " to host."; |
+ 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. |
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. |
- 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; |
- case command::UNMAP_PORT: |
- if (!listener) { |
- SendCommand(command::UNMAP_PORT_ERROR, port, socket.get()); |
- break; |
- } |
- listener->ForceExit(); |
- listener->Join(); |
- CHECK(!listener->is_alive()); |
- listeners_.Remove(port); |
- SendCommand(command::UNMAP_PORT_SUCCESS, port, socket.get()); |
+ listener->SetAdbDataSocket(socket.Pass()); |
+ break; |
+ case command::UNLISTEN: |
+ if (!listener) { |
+ SendCommand(command::UNLISTEN_ERROR, port, socket.get()); |
break; |
- default: |
- // TODO(felipeg): add a KillAllListeners command. |
- LOG(ERROR) << "Invalid command received. Port: " << port |
- << " Command: " << command; |
- } |
+ } |
+ listeners_.erase(listener_it); |
+ SendCommand(command::UNLISTEN_SUCCESS, port, socket.get()); |
+ break; |
+ default: |
+ // TODO(felipeg): add a KillAllListeners command. |
+ LOG(ERROR) << "Invalid command received. Port: " << port |
+ << " Command: " << command; |
} |
- KillAllListeners(); |
- CleanUpDeadListeners(); |
+} |
+ |
+// static |
+void DeviceController::DeleteListener( |
+ const base::WeakPtr<DeviceController>& device_controller_ptr, |
+ int listener_port) { |
+ DeviceController* const controller = device_controller_ptr.get(); |
+ if (!controller) |
+ return; |
+ DCHECK(controller->construction_task_runner_->RunsTasksOnCurrentThread()); |
+ const ListenersMap::iterator listener_it = controller->listeners_.find( |
+ listener_port); |
+ if (listener_it == controller->listeners_.end()) |
+ return; |
+ const linked_ptr<DeviceListener> listener = listener_it->second; |
+ // Note that the listener is removed from the map before it gets destroyed in |
+ // case its destructor would access the map. |
+ controller->listeners_.erase(listener_it); |
} |
} // namespace forwarder |