summaryrefslogtreecommitdiffstats
path: root/tools/android
diff options
context:
space:
mode:
authorfelipeg@chromium.org <felipeg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-09-11 20:56:52 +0000
committerfelipeg@chromium.org <felipeg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-09-11 20:56:52 +0000
commit8cb5f381e9b31703ef670f30900245f30d36aa3d (patch)
tree3c0e8d939e7c3a480ed0dd5634d7794d1a0f0941 /tools/android
parent9827043f58d7b90a2cccd7017134a7d9ae3bdbca (diff)
downloadchromium_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.cc178
-rw-r--r--tools/android/forwarder2/device_listener.h81
-rw-r--r--tools/android/forwarder2/forwarder.gyp3
-rw-r--r--tools/android/forwarder2/pipe_notifier.cc43
-rw-r--r--tools/android/forwarder2/pipe_notifier.h35
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_