diff options
author | felipeg@chromium.org <felipeg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-09-11 20:56:52 +0000 |
---|---|---|
committer | felipeg@chromium.org <felipeg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-09-11 20:56:52 +0000 |
commit | 8cb5f381e9b31703ef670f30900245f30d36aa3d (patch) | |
tree | 3c0e8d939e7c3a480ed0dd5634d7794d1a0f0941 /tools/android | |
parent | 9827043f58d7b90a2cccd7017134a7d9ae3bdbca (diff) | |
download | chromium_src-8cb5f381e9b31703ef670f30900245f30d36aa3d.zip chromium_src-8cb5f381e9b31703ef670f30900245f30d36aa3d.tar.gz chromium_src-8cb5f381e9b31703ef670f30900245f30d36aa3d.tar.bz2 |
Add DeviceListener and PipeNotifier to Forwarder2.
The big picture CL can be seem here: https://chromiumcodereview.appspot.com/10918057/
BUG=146502
Review URL: https://chromiumcodereview.appspot.com/10909170
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@156120 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'tools/android')
-rw-r--r-- | tools/android/forwarder2/device_listener.cc | 178 | ||||
-rw-r--r-- | tools/android/forwarder2/device_listener.h | 81 | ||||
-rw-r--r-- | tools/android/forwarder2/forwarder.gyp | 3 | ||||
-rw-r--r-- | tools/android/forwarder2/pipe_notifier.cc | 43 | ||||
-rw-r--r-- | tools/android/forwarder2/pipe_notifier.h | 35 |
5 files changed, 340 insertions, 0 deletions
diff --git a/tools/android/forwarder2/device_listener.cc b/tools/android/forwarder2/device_listener.cc new file mode 100644 index 0000000..d4575b9 --- /dev/null +++ b/tools/android/forwarder2/device_listener.cc @@ -0,0 +1,178 @@ +// 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_listener.h" + +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> + +#include <string> + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "tools/android/forwarder2/command.h" +#include "tools/android/forwarder2/forwarder.h" +#include "tools/android/forwarder2/socket.h" + +namespace { +// Timeout for the Listener to be waiting for a new Adb Data Connection. +const int kDeviceListenerTimeoutSeconds = 10; +} + +namespace forwarder2 { + +DeviceListener::DeviceListener(scoped_ptr<Socket> adb_control_socket, int port) + : adb_control_socket_(adb_control_socket.Pass()), + listener_port_(port), + is_alive_(true), + must_exit_(false) { + CHECK(adb_control_socket_.get()); + adb_control_socket_->set_exit_notifier_fd(exit_notifier_.receiver_fd()); + listener_socket_.set_exit_notifier_fd(exit_notifier_.receiver_fd()); + pthread_mutex_init(&adb_data_socket_mutex_, NULL); + pthread_cond_init(&adb_data_socket_cond_, NULL); +} + +DeviceListener::~DeviceListener() { + CHECK(!is_alive()); + adb_control_socket_->Close(); + listener_socket_.Close(); +} + +void DeviceListener::SetMustExitLocked() { + must_exit_ = true; + exit_notifier_.Notify(); +} + +void DeviceListener::ForceExit() { + // Set must_exit and wake up the threads waiting. + pthread_mutex_lock(&adb_data_socket_mutex_); + SetMustExitLocked(); + pthread_cond_broadcast(&adb_data_socket_cond_); + pthread_mutex_unlock(&adb_data_socket_mutex_); +} + +bool DeviceListener::WaitForAdbDataSocket() { + timespec time_to_wait = {}; + time_to_wait.tv_sec = time(NULL) + kDeviceListenerTimeoutSeconds; + + pthread_mutex_lock(&adb_data_socket_mutex_); + int ret = 0; + while (!must_exit_ && adb_data_socket_.get() == NULL && ret == 0) { + ret = pthread_cond_timedwait(&adb_data_socket_cond_, + &adb_data_socket_mutex_, + &time_to_wait); + if (ret != 0) { + if (adb_data_socket_.get()) { + adb_data_socket_->Close(); + adb_data_socket_.reset(); + } + LOG(ERROR) << "Error while waiting for Adb Data Socket."; + SetMustExitLocked(); + } + } + pthread_mutex_unlock(&adb_data_socket_mutex_); + if (ret == ETIMEDOUT) { + LOG(ERROR) << "DeviceListener timeout while waiting for " + << "the Adb Data Socket for port: " << listener_port_; + } + // Check both return value and also if set_must_exit() was called. + return ret == 0 && !must_exit_ && adb_data_socket_.get(); +} + +bool DeviceListener::SetAdbDataSocket(scoped_ptr<Socket> adb_data_socket) { + CHECK(adb_data_socket.get()); + pthread_mutex_lock(&adb_data_socket_mutex_); + adb_data_socket_.swap(adb_data_socket); + int ret = pthread_cond_broadcast(&adb_data_socket_cond_); + if (ret != 0) + SetMustExitLocked(); + + // We must check |must_exit_| while still in the lock, since the ownership of + // the adb_data_socket_ must be transactionally transferred to the other + // thread. + if (must_exit_ && adb_data_socket_.get()) { + adb_data_socket_->Close(); + adb_data_socket_.reset(); + } + pthread_mutex_unlock(&adb_data_socket_mutex_); + return ret == 0; +} + +void DeviceListener::RunInternal() { + while (!must_exit_) { + scoped_ptr<Socket> device_data_socket(new Socket); + if (!listener_socket_.Accept(device_data_socket.get())) { + LOG(WARNING) << "Could not Accept in ListenerSocket."; + SendCommand(command::ACCEPT_ERROR, + listener_port_, + adb_control_socket_.get()); + break; + } + SendCommand(command::ACCEPT_SUCCESS, + listener_port_, + adb_control_socket_.get()); + if (!ReceivedCommand(command::HOST_SERVER_SUCCESS, + adb_control_socket_.get())) { + SendCommand(command::ACK, listener_port_, adb_control_socket_.get()); + LOG(ERROR) << "Host could not connect to server."; + device_data_socket->Close(); + if (adb_control_socket_->has_error()) { + LOG(ERROR) << "Adb Control connection lost. " + << "Listener port: " << listener_port_; + break; + } + // It can continue if the host forwarder could not connect to the host + // server but the control connection is still alive (no errors). The + // device acknowledged that (above), and it can re-try later. + continue; + } + if (!WaitForAdbDataSocket()) { + LOG(ERROR) << "Device could not receive an Adb Data connection."; + SendCommand(command::ADB_DATA_SOCKET_ERROR, + listener_port_, + adb_control_socket_.get()); + device_data_socket->Close(); + continue; + } + SendCommand(command::ADB_DATA_SOCKET_SUCCESS, + listener_port_, + adb_control_socket_.get()); + CHECK(adb_data_socket_.get()); + // Forwarder object will self delete after returning from the Run() call. + Forwarder* forwarder = new Forwarder(device_data_socket.Pass(), + adb_data_socket_.Pass()); + forwarder->Start(); + } +} + +bool DeviceListener::BindListenerSocket() { + bool success = listener_socket_.BindTcp("", listener_port_); + if (success) { + SendCommand(command::BIND_SUCCESS, + listener_port_, + adb_control_socket_.get()); + } else { + LOG(ERROR) << "Device could not bind and listen to local port."; + SendCommand(command::BIND_ERROR, + listener_port_, + adb_control_socket_.get()); + } + return success; +} + +void DeviceListener::Run() { + if (BindListenerSocket()) + RunInternal(); + adb_control_socket_->Close(); + listener_socket_.Close(); + // This must be the last statement of the Run() method of the DeviceListener + // class, since the main thread checks if this thread is dead and deletes + // the object. It will be a race condition otherwise. + is_alive_ = false; +} + +} // namespace forwarder diff --git a/tools/android/forwarder2/device_listener.h b/tools/android/forwarder2/device_listener.h new file mode 100644 index 0000000..f496f81 --- /dev/null +++ b/tools/android/forwarder2/device_listener.h @@ -0,0 +1,81 @@ +// 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. + +#ifndef TOOLS_ANDROID_FORWARDER2_DEVICE_LISTENER_H_ +#define TOOLS_ANDROID_FORWARDER2_DEVICE_LISTENER_H_ + +#include <pthread.h> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "tools/android/forwarder2/pipe_notifier.h" +#include "tools/android/forwarder2/socket.h" +#include "tools/android/forwarder2/thread.h" + +namespace forwarder2 { + +class Forwarder; + +class DeviceListener : public Thread { + public: + DeviceListener(scoped_ptr<Socket> adb_control_socket, int port); + virtual ~DeviceListener(); + + bool WaitForAdbDataSocket(); + + bool SetAdbDataSocket(scoped_ptr<Socket> adb_data_socket); + + // |is_alive_| is set only on creation and written once when Run() terminates. + // So even in case of a race condition, the worst that could happen is for the + // main thread to see the listener alive when it isn't. And also, this is not + // a problem since the main thread checks the liveliness of the listeners in a + // loop. + bool is_alive() const { return is_alive_; } + void ForceExit(); + + protected: + // Thread: + virtual void Run() OVERRIDE; + + private: + bool BindListenerSocket(); + void RunInternal(); + + // Must be called after successfully acquired mutex. + void SetMustExitLocked(); + + // The listener socket for sending control commands. + scoped_ptr<Socket> adb_control_socket_; + + // The local device listener socket for accepting connections from the local + // port (listener_port_). + Socket listener_socket_; + + // This is the adb connection to transport the actual data, used for creating + // the forwarder. Ownership transferred to the Forwarder. + scoped_ptr<Socket> adb_data_socket_; + + const int listener_port_; + pthread_mutex_t adb_data_socket_mutex_; + pthread_cond_t adb_data_socket_cond_; + bool is_alive_; + bool must_exit_; + + // Used for the listener thread to be notified from ForceExit() which is + // called from the main thread. We have one notifier per Listener thread since + // each Listener thread may be requested to exit for different reasons + // independently from each other and independent from the main program, + // ex. when the host requests to forward/listen the same port again. Both the + // |adb_control_socket_| and |listener_socket_| must share the same receiver + // file descriptor from |exit_notifier_| and it is set in the constructor. + PipeNotifier exit_notifier_; + + DISALLOW_COPY_AND_ASSIGN(DeviceListener); +}; + +} // namespace forwarder + +#endif // TOOLS_ANDROID_FORWARDER2_DEVICE_LISTENER_H_ diff --git a/tools/android/forwarder2/forwarder.gyp b/tools/android/forwarder2/forwarder.gyp index 5bff33e..b2ab893 100644 --- a/tools/android/forwarder2/forwarder.gyp +++ b/tools/android/forwarder2/forwarder.gyp @@ -39,7 +39,9 @@ 'sources': [ 'command.cc', 'device_forwarder_main.cc', + 'device_listener.cc', 'forwarder.cc', + 'pipe_notifier.cc', 'socket.cc', 'thread.cc', ], @@ -59,6 +61,7 @@ 'command.cc', 'forwarder.cc', 'host_forwarder_main.cc', + 'pipe_notifier.cc', 'socket.cc', 'thread.cc', ], diff --git a/tools/android/forwarder2/pipe_notifier.cc b/tools/android/forwarder2/pipe_notifier.cc new file mode 100644 index 0000000..cf012b1 --- /dev/null +++ b/tools/android/forwarder2/pipe_notifier.cc @@ -0,0 +1,43 @@ +// 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/pipe_notifier.h" + +#include <fcntl.h> +#include <unistd.h> +#include <sys/socket.h> +#include <sys/types.h> + +#include "base/eintr_wrapper.h" +#include "base/logging.h" +#include "base/safe_strerror_posix.h" + +namespace forwarder2 { + +PipeNotifier::PipeNotifier() { + int pipe_fd[2]; + int ret = pipe(pipe_fd); + CHECK_EQ(0, ret); + receiver_fd_ = pipe_fd[0]; + sender_fd_ = pipe_fd[1]; + fcntl(sender_fd_, F_SETFL, O_NONBLOCK); +} + +PipeNotifier::~PipeNotifier() { + (void) HANDLE_EINTR(close(receiver_fd_)); + (void) HANDLE_EINTR(close(sender_fd_)); +} + +bool PipeNotifier::Notify() { + CHECK_NE(-1, sender_fd_); + errno = 0; + int ret = HANDLE_EINTR(write(sender_fd_, "1", 1)); + if (ret < 0) { + LOG(WARNING) << "Error while notifying pipe. " << safe_strerror(errno); + return false; + } + return true; +} + +} // namespace forwarder diff --git a/tools/android/forwarder2/pipe_notifier.h b/tools/android/forwarder2/pipe_notifier.h new file mode 100644 index 0000000..efe303b --- /dev/null +++ b/tools/android/forwarder2/pipe_notifier.h @@ -0,0 +1,35 @@ +// 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. + +#ifndef TOOLS_ANDROID_FORWARDER2_PIPE_NOTIFIER_H_ +#define TOOLS_ANDROID_FORWARDER2_PIPE_NOTIFIER_H_ + +#include "base/basictypes.h" + +namespace forwarder2 { + +// Helper class used to create a unix pipe that sends notifications to the +// |receiver_fd_| file descriptor when called |Notify()|. This should be used +// by the main thread to notify other threads that it must exit. +// The |receiver_fd_| can be put into a fd_set and used in a select together +// with a socket waiting to accept or read. +class PipeNotifier { + public: + PipeNotifier(); + ~PipeNotifier(); + + bool Notify(); + + int receiver_fd() const { return receiver_fd_; } + + private: + int sender_fd_; + int receiver_fd_; + + DISALLOW_COPY_AND_ASSIGN(PipeNotifier); +}; + +} // namespace forwarder + +#endif // TOOLS_ANDROID_FORWARDER2_PIPE_NOTIFIER_H_ |