// 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 #include #include #include #include "base/at_exit.h" #include "base/bind.h" #include "base/command_line.h" #include "base/compiler_specific.h" #include "base/logging.h" #include "base/strings/string_piece.h" #include "base/strings/stringprintf.h" #include "base/threading/thread.h" #include "tools/android/forwarder2/common.h" #include "tools/android/forwarder2/daemon.h" #include "tools/android/forwarder2/device_controller.h" #include "tools/android/forwarder2/pipe_notifier.h" namespace forwarder2 { namespace { // Leaky global instance, accessed from the signal handler. forwarder2::PipeNotifier* g_notifier = NULL; const int kBufSize = 256; const char kUnixDomainSocketPath[] = "chrome_device_forwarder"; const char kDaemonIdentifier[] = "chrome_device_forwarder_daemon"; void KillHandler(int /* unused */) { CHECK(g_notifier); if (!g_notifier->Notify()) exit(1); } // Lets the daemon fetch the exit notifier file descriptor. int GetExitNotifierFD() { DCHECK(g_notifier); return g_notifier->receiver_fd(); } class ServerDelegate : public Daemon::ServerDelegate { public: ServerDelegate() : initialized_(false) {} virtual ~ServerDelegate() { if (!controller_thread_.get()) return; // The DeviceController instance, if any, is constructed on the controller // thread. Make sure that it gets deleted on that same thread. Note that // DeleteSoon() is not used here since it would imply reading |controller_| // from the main thread while it's set on the internal thread. controller_thread_->message_loop_proxy()->PostTask( FROM_HERE, base::Bind(&ServerDelegate::DeleteControllerOnInternalThread, base::Unretained(this))); } void DeleteControllerOnInternalThread() { DCHECK( controller_thread_->message_loop_proxy()->RunsTasksOnCurrentThread()); controller_.reset(); } // Daemon::ServerDelegate: virtual void Init() OVERRIDE { DCHECK(!g_notifier); g_notifier = new forwarder2::PipeNotifier(); signal(SIGTERM, KillHandler); signal(SIGINT, KillHandler); controller_thread_.reset(new base::Thread("controller_thread")); controller_thread_->Start(); } virtual void OnClientConnected(scoped_ptr client_socket) OVERRIDE { if (initialized_) { client_socket->WriteString("OK"); return; } controller_thread_->message_loop()->PostTask( FROM_HERE, base::Bind(&ServerDelegate::StartController, base::Unretained(this), GetExitNotifierFD(), base::Passed(&client_socket))); initialized_ = true; } private: void StartController(int exit_notifier_fd, scoped_ptr client_socket) { DCHECK(!controller_.get()); scoped_ptr controller( DeviceController::Create(kUnixDomainSocketPath, exit_notifier_fd)); if (!controller.get()) { client_socket->WriteString( base::StringPrintf("ERROR: Could not initialize device controller " "with ADB socket path: %s", kUnixDomainSocketPath)); return; } controller_.swap(controller); controller_->Start(); client_socket->WriteString("OK"); client_socket->Close(); } scoped_ptr controller_; scoped_ptr controller_thread_; bool initialized_; }; class ClientDelegate : public Daemon::ClientDelegate { public: ClientDelegate() : has_failed_(false) {} bool has_failed() const { return has_failed_; } // Daemon::ClientDelegate: virtual void OnDaemonReady(Socket* daemon_socket) OVERRIDE { char buf[kBufSize]; const int bytes_read = daemon_socket->Read( buf, sizeof(buf) - 1 /* leave space for null terminator */); CHECK_GT(bytes_read, 0); DCHECK(bytes_read < sizeof(buf)); buf[bytes_read] = 0; base::StringPiece msg(buf, bytes_read); if (msg.starts_with("ERROR")) { LOG(ERROR) << msg; has_failed_ = true; return; } } private: bool has_failed_; }; int RunDeviceForwarder(int argc, char** argv) { CommandLine::Init(argc, argv); // Needed by logging. const bool kill_server = CommandLine::ForCurrentProcess()->HasSwitch( "kill-server"); if ((kill_server && argc != 2) || (!kill_server && argc != 1)) { std::cerr << "Usage: device_forwarder [--kill-server]" << std::endl; return 1; } base::AtExitManager at_exit_manager; // Used by base::Thread. ClientDelegate client_delegate; ServerDelegate daemon_delegate; const char kLogFilePath[] = ""; // Log to logcat. Daemon daemon(kLogFilePath, kDaemonIdentifier, &client_delegate, &daemon_delegate, &GetExitNotifierFD); if (kill_server) return !daemon.Kill(); if (!daemon.SpawnIfNeeded()) return 1; return client_delegate.has_failed(); } } // namespace } // namespace forwarder2 int main(int argc, char** argv) { return forwarder2::RunDeviceForwarder(argc, argv); }