root/tools/android/forwarder2/device_forwarder_main.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. KillHandler
  2. GetExitNotifierFD
  3. DeleteControllerOnInternalThread
  4. Init
  5. OnClientConnected
  6. StartController
  7. has_failed
  8. OnDaemonReady
  9. RunDeviceForwarder
  10. main

// 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 <signal.h>
#include <stdlib.h>

#include <iostream>
#include <string>

#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<Socket> 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<Socket> client_socket) {
    DCHECK(!controller_.get());
    scoped_ptr<DeviceController> 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<DeviceController> controller_;
  scoped_ptr<base::Thread> 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);
}

/* [<][>][^][v][top][bottom][index][help] */