summaryrefslogtreecommitdiffstats
path: root/tools/android
diff options
context:
space:
mode:
authorilevy@chromium.org <ilevy@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-01-29 12:11:15 +0000
committerilevy@chromium.org <ilevy@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-01-29 12:11:15 +0000
commit43d81ae62fbcdb09c5bf91a7f2dcd38406cfba7f (patch)
treea3c649279b9b1d2c45d090cac8baa3af731efc09 /tools/android
parent635fe1908663039e700f6540f1b5a1b11e085e63 (diff)
downloadchromium_src-43d81ae62fbcdb09c5bf91a7f2dcd38406cfba7f.zip
chromium_src-43d81ae62fbcdb09c5bf91a7f2dcd38406cfba7f.tar.gz
chromium_src-43d81ae62fbcdb09c5bf91a7f2dcd38406cfba7f.tar.bz2
Add a binary for android device-side reboots
This binary is upstreamed from the internal android tools repo. It compiles, but I have not tested it. Rather than pinging the tcp echo service, we should modify the host side component of forwarder2 to respond directly. BUG=170213 NOTRY=True Review URL: https://chromiumcodereview.appspot.com/12077015 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@179315 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'tools/android')
-rw-r--r--tools/android/android_tools.gyp1
-rw-r--r--tools/android/rebootondisconnect/rebootondisconnect.cc223
-rw-r--r--tools/android/rebootondisconnect/rebootondisconnect.gyp47
3 files changed, 271 insertions, 0 deletions
diff --git a/tools/android/android_tools.gyp b/tools/android/android_tools.gyp
index 43151a5..b936af3 100644
--- a/tools/android/android_tools.gyp
+++ b/tools/android/android_tools.gyp
@@ -13,6 +13,7 @@
'fake_dns/fake_dns.gyp:fake_dns',
'forwarder2/forwarder.gyp:forwarder2',
'md5sum/md5sum.gyp:md5sum',
+ 'rebootondisconnect/rebootondisconnect.gyp:rebootondisconnect',
],
},
],
diff --git a/tools/android/rebootondisconnect/rebootondisconnect.cc b/tools/android/rebootondisconnect/rebootondisconnect.cc
new file mode 100644
index 0000000..6afcc88
--- /dev/null
+++ b/tools/android/rebootondisconnect/rebootondisconnect.cc
@@ -0,0 +1,223 @@
+// 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 <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <sys/reboot.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/posix/eintr_wrapper.h"
+#include "tools/android/common/adb_connection.h"
+#include "tools/android/common/daemon.h"
+#include "tools/android/common/net.h"
+
+namespace {
+
+const pthread_t kInvalidThread = static_cast<pthread_t>(-1);
+bool g_killed = false;
+const int kMessagePeriodSeconds = 2;
+const int kMessageTimeoutSeconds = 1;
+const int kWarmupTimeInSeconds = 60;
+const int kMaxFailedPingRequests = 5;
+
+class PingThread {
+ public:
+ PingThread()
+ : thread_(kInvalidThread),
+ requests_issued_(0),
+ subsequent_requests_failed_(0) {
+ }
+
+ ~PingThread() {
+ }
+
+ void DumpInformation() {
+ LOG(INFO) << "RebootOnDisconnect information: ";
+ LOG(INFO) << "Ping attempts made: " << requests_issued_;
+ LOG(INFO) << "Subsequent attempts failed: " << subsequent_requests_failed_;
+ }
+
+ void Start() {
+ pthread_create(&thread_, NULL, WorkerThread, this);
+ }
+
+ void Join() {
+ if (thread_ != kInvalidThread)
+ pthread_join(thread_, NULL);
+ }
+
+ private:
+ void PingSucceeded() {
+ subsequent_requests_failed_ = 0;
+ }
+
+ void PingFailed() {
+ subsequent_requests_failed_++;
+ }
+
+ void OnRequestIssued() {
+ requests_issued_++;
+ }
+
+ bool ShouldReboot() {
+ // We should reboot if more than kMaxFailedPingRequests requests have
+ // failed, but only if we've made at least warmupPingRequests already.
+ const int warmupPingRequests = kWarmupTimeInSeconds / kMessagePeriodSeconds;
+ bool reboot = false;
+ if (requests_issued_ >= warmupPingRequests)
+ reboot = (subsequent_requests_failed_ >= kMaxFailedPingRequests);
+ return reboot;
+ }
+
+ bool DoPing(int socket);
+
+ static void* WorkerThread(void* arg);
+ static void DoReboot();
+
+ pthread_t thread_;
+
+ volatile int requests_issued_;
+ volatile int subsequent_requests_failed_;
+};
+
+bool PingThread::DoPing(int socket) {
+ bool succeeded = false;
+ pollfd fds;
+ time_t start_time = time(NULL);
+ const size_t buf_size = sizeof(start_time) / sizeof(char);
+ char buf[buf_size];
+ char buf_recv[buf_size + 1];
+
+ memset(buf_recv, 0, buf_size + 1);
+
+ // Use the current time to form a unique data packet.
+ memcpy(buf, &start_time, sizeof(start_time));
+
+ memset(&fds, 0, sizeof(fds));
+ fds.fd = socket;
+ fds.events = POLLIN | POLLERR | POLLHUP;
+ fds.revents = 0;
+
+ do {
+ // Send the message that we want echoed back to us.
+ if (HANDLE_EINTR(send(socket, buf, buf_size, 0)) < 0) {
+ LOG(ERROR) << "Error sending data: " << errno;
+ break;
+ }
+
+ if (HANDLE_EINTR(poll(&fds, 1, kMessageTimeoutSeconds * 1000)) != 1) {
+ // Timeout or error.
+ LOG(ERROR) << "Timeout or error: " << errno;
+ break;
+ }
+ if (fds.revents & POLLERR) {
+ LOG(ERROR) << "Poll error";
+ break;
+ }
+ if (fds.revents & POLLHUP) {
+ LOG(ERROR) << "Hang up error";
+ break;
+ }
+
+ // Since this is over TCP it shouldn't be possible for the 8 byte
+ // packet to get segmented, especially that we're disabling Nagle's
+ // algorithm.
+ const int read_status = HANDLE_EINTR(read(socket, buf_recv, buf_size + 1));
+ if (read_status != buf_size) {
+ // Timeout or did not receive right# of bytes.
+ LOG(ERROR) << "Wrong read. Status: " << read_status
+ << " err: " << errno;
+ break;
+ }
+
+ if (memcmp(buf, buf_recv, buf_size) != 0) {
+ LOG(ERROR) << "Data did not match";
+ break;
+ }
+
+ succeeded = true;
+
+ } while (false);
+
+ return succeeded;
+}
+
+// Every 10 seconds pings the host using the Echo service.
+// If a certain number of subsequent attempts fail we will reboot the device.
+void* PingThread::WorkerThread(void* arg) {
+ PingThread* pingThread = reinterpret_cast<PingThread*>(arg);
+ while (!g_killed) {
+ if (pingThread->ShouldReboot()) {
+ LOG(ERROR) << "PingThread failed. Rebooting device.";
+ DoReboot();
+ }
+
+ pingThread->OnRequestIssued();
+
+ int host_socket = tools::ConnectAdbHostSocket("7:127.0.0.1");
+ // We need to disable Nagle's algorithm, otherwise ping requests might get
+ // queued up and we'll always timeout waiting.
+ tools::DisableNagle(host_socket);
+
+ if (host_socket >= 0) {
+ fcntl(host_socket, F_SETFL, fcntl(host_socket, F_GETFL) | O_NONBLOCK);
+ if (pingThread->DoPing(host_socket)) {
+ pingThread->PingSucceeded();
+ } else {
+ LOG(ERROR) << "PingThread failed (host_socket >=0).";
+ pingThread->PingFailed();
+ }
+ close(host_socket);
+ } else {
+ LOG(ERROR) << "PingThread failed (host_socket < 0)";
+ pingThread->PingFailed();
+ }
+ sleep(kMessagePeriodSeconds);
+ }
+ return NULL;
+}
+
+void PingThread::DoReboot() {
+ LOG(ERROR) << "Too many requests failed.. Rebooting";
+ reboot(LINUX_REBOOT_CMD_RESTART);
+}
+
+PingThread* g_thread = NULL;
+
+void DumpInformation(int) {
+ if (g_thread != NULL) {
+ g_thread->DumpInformation();
+ }
+}
+
+} // namespace
+
+int main(int argc, char** argv) {
+ printf("Keep android alive device-side\n");
+
+ CommandLine command_line(argc, argv);
+ if (tools::HasHelpSwitch(command_line) || command_line.GetArgs().size()) {
+ tools::ShowHelp(argv[0], "", "");
+ return 0;
+ }
+
+ if (!tools::HasNoSpawnDaemonSwitch(command_line))
+ tools::SpawnDaemon(0);
+
+ signal(SIGUSR2, DumpInformation);
+
+ g_thread = new PingThread();
+ g_thread->Start();
+ g_thread->Join();
+ delete g_thread;
+
+ return 0;
+}
diff --git a/tools/android/rebootondisconnect/rebootondisconnect.gyp b/tools/android/rebootondisconnect/rebootondisconnect.gyp
new file mode 100644
index 0000000..c67d16d
--- /dev/null
+++ b/tools/android/rebootondisconnect/rebootondisconnect.gyp
@@ -0,0 +1,47 @@
+# 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.
+{
+ 'targets': [
+ {
+ 'target_name': 'rebootondisconnect',
+ 'type': 'none',
+ 'dependencies': [
+ 'rebootondisconnect_symbols',
+ ],
+ 'actions': [
+ {
+ 'action_name': 'strip_rebootondisconnect',
+ 'inputs': ['<(PRODUCT_DIR)/rebootondisconnect_symbols'],
+ 'outputs': ['<(PRODUCT_DIR)/rebootondisconnect'],
+ 'action': [
+ '<!(/bin/echo -n $STRIP)',
+ '--strip-unneeded',
+ '<@(_inputs)',
+ '-o',
+ '<@(_outputs)',
+ ],
+ },
+ ],
+ }, {
+ 'target_name': 'rebootondisconnect_symbols',
+ 'type': 'executable',
+ 'dependencies': [
+ '../../../base/base.gyp:base',
+ '../common/common.gyp:android_tools_common',
+ ],
+ 'include_dirs': [
+ '..',
+ ],
+ 'sources': [
+ 'rebootondisconnect.cc',
+ ],
+ },
+ ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2: