Home | History | Annotate | Download | only in chrome
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/test/chromedriver/chrome/adb_impl.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/bind_helpers.h"
      9 #include "base/json/string_escape.h"
     10 #include "base/logging.h"
     11 #include "base/memory/ref_counted.h"
     12 #include "base/message_loop/message_loop.h"
     13 #include "base/single_thread_task_runner.h"
     14 #include "base/strings/string_number_conversions.h"
     15 #include "base/strings/string_split.h"
     16 #include "base/strings/string_tokenizer.h"
     17 #include "base/strings/stringprintf.h"
     18 #include "base/synchronization/waitable_event.h"
     19 #include "base/time/time.h"
     20 #include "chrome/test/chromedriver/chrome/status.h"
     21 #include "chrome/test/chromedriver/net/adb_client_socket.h"
     22 
     23 namespace {
     24 
     25 // This class is bound in the callback to AdbQuery and isn't freed until the
     26 // callback is run, even if the function that creates the buffer times out.
     27 class ResponseBuffer : public base::RefCountedThreadSafe<ResponseBuffer> {
     28  public:
     29   ResponseBuffer() : ready_(true, false) {}
     30 
     31   void OnResponse(int result, const std::string& response) {
     32     response_ = response;
     33     result_ = result;
     34     ready_.Signal();
     35   }
     36 
     37   Status GetResponse(
     38       std::string* response, const base::TimeDelta& timeout) {
     39     base::TimeTicks deadline = base::TimeTicks::Now() + timeout;
     40     while (!ready_.IsSignaled()) {
     41       base::TimeDelta delta = deadline - base::TimeTicks::Now();
     42       if (delta <= base::TimeDelta())
     43         return Status(kTimeout, base::StringPrintf(
     44             "Adb command timed out after %d seconds",
     45             static_cast<int>(timeout.InSeconds())));
     46       ready_.TimedWait(timeout);
     47     }
     48     if (result_ < 0)
     49       return Status(kUnknownError,
     50           "Failed to run adb command, is the adb server running?");
     51     *response = response_;
     52     return Status(kOk);
     53   }
     54 
     55  private:
     56   friend class base::RefCountedThreadSafe<ResponseBuffer>;
     57   ~ResponseBuffer() {}
     58 
     59   std::string response_;
     60   int result_;
     61   base::WaitableEvent ready_;
     62 };
     63 
     64 void ExecuteCommandOnIOThread(
     65     const std::string& command, scoped_refptr<ResponseBuffer> response_buffer,
     66     int port) {
     67   CHECK(base::MessageLoop::current()->IsType(base::MessageLoop::TYPE_IO));
     68   AdbClientSocket::AdbQuery(port, command,
     69       base::Bind(&ResponseBuffer::OnResponse, response_buffer));
     70 }
     71 
     72 }  // namespace
     73 
     74 AdbImpl::AdbImpl(
     75     const scoped_refptr<base::SingleThreadTaskRunner>& io_task_runner,
     76     int port)
     77     : io_task_runner_(io_task_runner), port_(port) {
     78   CHECK(io_task_runner_.get());
     79 }
     80 
     81 AdbImpl::~AdbImpl() {}
     82 
     83 Status AdbImpl::GetDevices(std::vector<std::string>* devices) {
     84   std::string response;
     85   Status status = ExecuteCommand("host:devices", &response);
     86   if (!status.IsOk())
     87     return status;
     88   base::StringTokenizer lines(response, "\n");
     89   while (lines.GetNext()) {
     90     std::vector<std::string> fields;
     91     base::SplitStringAlongWhitespace(lines.token(), &fields);
     92     if (fields.size() == 2 && fields[1] == "device") {
     93       devices->push_back(fields[0]);
     94     }
     95   }
     96   return Status(kOk);
     97 }
     98 
     99 Status AdbImpl::ForwardPort(
    100     const std::string& device_serial, int local_port,
    101     const std::string& remote_abstract) {
    102   std::string response;
    103   Status status = ExecuteHostCommand(
    104       device_serial,
    105       "forward:tcp:" + base::IntToString(local_port) + ";localabstract:" +
    106           remote_abstract,
    107       &response);
    108   if (!status.IsOk())
    109     return status;
    110   if (response == "OKAY")
    111     return Status(kOk);
    112   return Status(kUnknownError, "Failed to forward ports to device " +
    113                 device_serial + ": " + response);
    114 }
    115 
    116 Status AdbImpl::SetCommandLineFile(const std::string& device_serial,
    117                                    const std::string& command_line_file,
    118                                    const std::string& exec_name,
    119                                    const std::string& args) {
    120   std::string response;
    121   std::string quoted_command =
    122       base::GetQuotedJSONString(exec_name + " " + args);
    123   Status status = ExecuteHostShellCommand(
    124       device_serial,
    125       base::StringPrintf("echo %s > %s; echo $?",
    126                          quoted_command.c_str(),
    127                          command_line_file.c_str()),
    128       &response);
    129   if (!status.IsOk())
    130     return status;
    131   if (response.find("0") == std::string::npos)
    132     return Status(kUnknownError, "Failed to set command line file " +
    133                   command_line_file + " on device " + device_serial);
    134   return Status(kOk);
    135 }
    136 
    137 Status AdbImpl::CheckAppInstalled(
    138     const std::string& device_serial, const std::string& package) {
    139   std::string response;
    140   std::string command = "pm path " + package;
    141   Status status = ExecuteHostShellCommand(device_serial, command, &response);
    142   if (!status.IsOk())
    143     return status;
    144   if (response.find("package") == std::string::npos)
    145     return Status(kUnknownError, package + " is not installed on device " +
    146                   device_serial);
    147   return Status(kOk);
    148 }
    149 
    150 Status AdbImpl::ClearAppData(
    151     const std::string& device_serial, const std::string& package) {
    152   std::string response;
    153   std::string command = "pm clear " + package;
    154   Status status = ExecuteHostShellCommand(device_serial, command, &response);
    155   if (!status.IsOk())
    156     return status;
    157   if (response.find("Success") == std::string::npos)
    158     return Status(kUnknownError, "Failed to clear data for " + package +
    159                   " on device " + device_serial + ": " + response);
    160   return Status(kOk);
    161 }
    162 
    163 Status AdbImpl::Launch(
    164     const std::string& device_serial, const std::string& package,
    165     const std::string& activity) {
    166   std::string response;
    167   Status status = ExecuteHostShellCommand(
    168       device_serial,
    169       "am start -W -n " + package + "/" + activity + " -d data:,",
    170       &response);
    171   if (!status.IsOk())
    172     return status;
    173   if (response.find("Complete") == std::string::npos)
    174     return Status(kUnknownError,
    175                   "Failed to start " + package + " on device " + device_serial +
    176                   ": " + response);
    177   return Status(kOk);
    178 }
    179 
    180 Status AdbImpl::ForceStop(
    181     const std::string& device_serial, const std::string& package) {
    182   std::string response;
    183   return ExecuteHostShellCommand(
    184       device_serial, "am force-stop " + package, &response);
    185 }
    186 
    187 Status AdbImpl::GetPidByName(const std::string& device_serial,
    188                              const std::string& process_name,
    189                              int* pid) {
    190   std::string response;
    191   Status status = ExecuteHostShellCommand(device_serial, "ps", &response);
    192   if (!status.IsOk())
    193     return status;
    194 
    195   std::vector<std::string> lines;
    196   base::SplitString(response, '\n', &lines);
    197   for (size_t i = 0; i < lines.size(); ++i) {
    198     std::string line = lines[i];
    199     if (line.empty())
    200       continue;
    201     std::vector<std::string> tokens;
    202     base::SplitStringAlongWhitespace(line, &tokens);
    203     if (tokens.size() != 9)
    204       continue;
    205     if (tokens[8].compare(process_name) == 0) {
    206       if (base::StringToInt(tokens[1], pid)) {
    207         return Status(kOk);
    208       } else {
    209         break;
    210       }
    211     }
    212   }
    213 
    214   return Status(kUnknownError,
    215                 "Failed to get PID for the following process: " + process_name);
    216 }
    217 
    218 Status AdbImpl::ExecuteCommand(
    219     const std::string& command, std::string* response) {
    220   scoped_refptr<ResponseBuffer> response_buffer = new ResponseBuffer;
    221   VLOG(1) << "Sending adb command: " << command;
    222   io_task_runner_->PostTask(
    223       FROM_HERE,
    224       base::Bind(&ExecuteCommandOnIOThread, command, response_buffer, port_));
    225   Status status = response_buffer->GetResponse(
    226       response, base::TimeDelta::FromSeconds(30));
    227   if (status.IsOk()) {
    228     VLOG(1) << "Received adb response: " << *response;
    229   }
    230   return status;
    231 }
    232 
    233 Status AdbImpl::ExecuteHostCommand(
    234     const std::string& device_serial,
    235     const std::string& host_command, std::string* response) {
    236   return ExecuteCommand(
    237       "host-serial:" + device_serial + ":" + host_command, response);
    238 }
    239 
    240 Status AdbImpl::ExecuteHostShellCommand(
    241     const std::string& device_serial,
    242     const std::string& shell_command,
    243     std::string* response) {
    244   return ExecuteCommand(
    245       "host:transport:" + device_serial + "|shell:" + shell_command,
    246       response);
    247 }
    248 
    249