1// Copyright (c) 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/device_manager.h"
6
7#include <algorithm>
8#include <vector>
9
10#include "base/bind.h"
11#include "base/bind_helpers.h"
12#include "base/callback.h"
13#include "base/logging.h"
14#include "base/strings/string_number_conversions.h"
15#include "base/strings/stringprintf.h"
16#include "chrome/test/chromedriver/chrome/adb.h"
17#include "chrome/test/chromedriver/chrome/status.h"
18
19Device::Device(
20    const std::string& device_serial, Adb* adb,
21    base::Callback<void()> release_callback)
22    : serial_(device_serial),
23      adb_(adb),
24      release_callback_(release_callback) {}
25
26Device::~Device() {
27  release_callback_.Run();
28}
29
30Status Device::SetUp(const std::string& package,
31                     const std::string& activity,
32                     const std::string& process,
33                     const std::string& args,
34                     bool use_running_app,
35                     int port) {
36  if (!active_package_.empty())
37    return Status(kUnknownError,
38        active_package_ + " was launched and has not been quit");
39
40  Status status = adb_->CheckAppInstalled(serial_, package);
41  if (!status.IsOk())
42    return status;
43
44  std::string known_activity;
45  std::string command_line_file;
46  std::string device_socket;
47  std::string exec_name;
48  if (package.compare("org.chromium.content_shell_apk") == 0) {
49    known_activity = ".ContentShellActivity";
50    device_socket = "content_shell_devtools_remote";
51    command_line_file = "/data/local/tmp/content-shell-command-line";
52    exec_name = "content_shell";
53  } else if (package.compare("org.chromium.chrome.testshell") == 0) {
54    known_activity = ".ChromiumTestShellActivity";
55    device_socket = "chromium_testshell_devtools_remote";
56    command_line_file = "/data/local/tmp/chromium-testshell-command-line";
57    exec_name = "chromium_testshell";
58  } else if (package.find("chrome") != std::string::npos &&
59             package.find("webview") == std::string::npos) {
60    known_activity = "com.google.android.apps.chrome.Main";
61    device_socket = "chrome_devtools_remote";
62    command_line_file = "/data/local/chrome-command-line";
63    exec_name = "chrome";
64  }
65
66  if (!use_running_app) {
67    status = adb_->ClearAppData(serial_, package);
68    if (!status.IsOk())
69      return status;
70
71    if (!known_activity.empty()) {
72      if (!activity.empty() ||
73          !process.empty())
74        return Status(kUnknownError, "known package " + package +
75                      " does not accept activity/process");
76    } else if (activity.empty()) {
77      return Status(kUnknownError, "WebView apps require activity name");
78    }
79
80    if (!command_line_file.empty()) {
81      status = adb_->SetCommandLineFile(serial_, command_line_file, exec_name,
82                                        args);
83      if (!status.IsOk())
84        return status;
85    }
86
87    status = adb_->Launch(serial_, package,
88                          known_activity.empty() ? activity : known_activity);
89    if (!status.IsOk())
90      return status;
91
92    active_package_ = package;
93  }
94  this->ForwardDevtoolsPort(package, process, device_socket, port);
95
96  return status;
97}
98
99Status Device::ForwardDevtoolsPort(const std::string& package,
100                                   const std::string& process,
101                                   std::string& device_socket,
102                                   int port) {
103  if (device_socket.empty()) {
104    // Assume this is a WebView app.
105    int pid;
106    Status status = adb_->GetPidByName(serial_,
107                                       process.empty() ? package : process,
108                                       &pid);
109    if (!status.IsOk()) {
110      if (process.empty())
111        status.AddDetails(
112            "process name must be specified if not equal to package name");
113      return status;
114    }
115    device_socket = base::StringPrintf("webview_devtools_remote_%d", pid);
116  }
117
118  return adb_->ForwardPort(serial_, port, device_socket);
119}
120
121Status Device::TearDown() {
122  if (!active_package_.empty()) {
123    std::string response;
124    Status status = adb_->ForceStop(serial_, active_package_);
125    if (!status.IsOk())
126      return status;
127    active_package_ = "";
128  }
129  return Status(kOk);
130}
131
132DeviceManager::DeviceManager(Adb* adb) : adb_(adb) {
133  CHECK(adb_);
134}
135
136DeviceManager::~DeviceManager() {}
137
138Status DeviceManager::AcquireDevice(scoped_ptr<Device>* device) {
139  std::vector<std::string> devices;
140  Status status = adb_->GetDevices(&devices);
141  if (!status.IsOk())
142    return status;
143
144  if (devices.empty())
145    return Status(kUnknownError, "There are no devices online");
146
147  base::AutoLock lock(devices_lock_);
148  status = Status(kUnknownError, "All devices are in use (" +
149                  base::IntToString(devices.size()) + " online)");
150  std::vector<std::string>::iterator iter;
151  for (iter = devices.begin(); iter != devices.end(); iter++) {
152    if (!IsDeviceLocked(*iter)) {
153      device->reset(LockDevice(*iter));
154      status = Status(kOk);
155      break;
156    }
157  }
158  return status;
159}
160
161Status DeviceManager::AcquireSpecificDevice(
162    const std::string& device_serial, scoped_ptr<Device>* device) {
163  std::vector<std::string> devices;
164  Status status = adb_->GetDevices(&devices);
165  if (!status.IsOk())
166    return status;
167
168  if (std::find(devices.begin(), devices.end(), device_serial) == devices.end())
169    return Status(kUnknownError,
170        "Device " + device_serial + " is not online");
171
172  base::AutoLock lock(devices_lock_);
173  if (IsDeviceLocked(device_serial)) {
174    status = Status(kUnknownError,
175        "Device " + device_serial + " is already in use");
176  } else {
177    device->reset(LockDevice(device_serial));
178    status = Status(kOk);
179  }
180  return status;
181}
182
183void DeviceManager::ReleaseDevice(const std::string& device_serial) {
184  base::AutoLock lock(devices_lock_);
185  active_devices_.remove(device_serial);
186}
187
188Device* DeviceManager::LockDevice(const std::string& device_serial) {
189  active_devices_.push_back(device_serial);
190  return new Device(device_serial, adb_,
191      base::Bind(&DeviceManager::ReleaseDevice, base::Unretained(this),
192                 device_serial));
193}
194
195bool DeviceManager::IsDeviceLocked(const std::string& device_serial) {
196  return std::find(active_devices_.begin(), active_devices_.end(),
197                   device_serial) != active_devices_.end();
198}
199
200