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
19 // TODO(craigdh): Remove once Chromedriver no longer supports pre-m33 Chrome.
20 const char* kChromeCmdLineFileBeforeM33 = "/data/local/chrome-command-line";
21 const char* kChromeCmdLineFile = "/data/local/tmp/chrome-command-line";
22
Device(const std::string & device_serial,Adb * adb,base::Callback<void ()> release_callback)23 Device::Device(
24 const std::string& device_serial, Adb* adb,
25 base::Callback<void()> release_callback)
26 : serial_(device_serial),
27 adb_(adb),
28 release_callback_(release_callback) {}
29
~Device()30 Device::~Device() {
31 release_callback_.Run();
32 }
33
SetUp(const std::string & package,const std::string & activity,const std::string & process,const std::string & args,bool use_running_app,int port)34 Status Device::SetUp(const std::string& package,
35 const std::string& activity,
36 const std::string& process,
37 const std::string& args,
38 bool use_running_app,
39 int port) {
40 if (!active_package_.empty())
41 return Status(kUnknownError,
42 active_package_ + " was launched and has not been quit");
43
44 Status status = adb_->CheckAppInstalled(serial_, package);
45 if (status.IsError())
46 return status;
47
48 std::string known_activity;
49 std::string command_line_file;
50 std::string device_socket;
51 std::string exec_name;
52 if (package.compare("org.chromium.content_shell_apk") == 0) {
53 // Chromium content shell.
54 known_activity = ".ContentShellActivity";
55 device_socket = "content_shell_devtools_remote";
56 command_line_file = "/data/local/tmp/content-shell-command-line";
57 exec_name = "content_shell";
58 } else if (package.compare("org.chromium.chrome.shell") == 0) {
59 // ChromeShell
60 known_activity = ".ChromeShellActivity";
61 device_socket = "chrome_shell_devtools_remote";
62 command_line_file = "/data/local/tmp/chrome-shell-command-line";
63 exec_name = "chrome_shell";
64 } else if (package.find("chrome") != std::string::npos &&
65 package.find("webview") == std::string::npos) {
66 // Chrome.
67 known_activity = "com.google.android.apps.chrome.Main";
68 device_socket = "chrome_devtools_remote";
69 command_line_file = kChromeCmdLineFileBeforeM33;
70 exec_name = "chrome";
71 status = adb_->SetDebugApp(serial_, package);
72 if (status.IsError())
73 return status;
74 }
75
76 if (!use_running_app) {
77 status = adb_->ClearAppData(serial_, package);
78 if (status.IsError())
79 return status;
80
81 if (!known_activity.empty()) {
82 if (!activity.empty() ||
83 !process.empty())
84 return Status(kUnknownError, "known package " + package +
85 " does not accept activity/process");
86 } else if (activity.empty()) {
87 return Status(kUnknownError, "WebView apps require activity name");
88 }
89
90 if (!command_line_file.empty()) {
91 // If Chrome is set as the debug app it looks in /data/local/tmp/.
92 // There's no way to know if this is set, so write to both locations.
93 // This can be removed once support for pre-M33 is no longer needed.
94 if (command_line_file == kChromeCmdLineFileBeforeM33) {
95 status = adb_->SetCommandLineFile(
96 serial_, kChromeCmdLineFileBeforeM33, exec_name, args);
97 Status status2 = adb_->SetCommandLineFile(
98 serial_, kChromeCmdLineFile, exec_name, args);
99 if (status.IsError() && status2.IsError())
100 return Status(kUnknownError,
101 "Failed to set Chrome's command line file on device " + serial_);
102 } else {
103 status = adb_->SetCommandLineFile(
104 serial_, command_line_file, exec_name, args);
105 if (status.IsError())
106 return status;
107 }
108 }
109
110 status = adb_->Launch(serial_, package,
111 known_activity.empty() ? activity : known_activity);
112 if (status.IsError())
113 return status;
114
115 active_package_ = package;
116 }
117 this->ForwardDevtoolsPort(package, process, device_socket, port);
118
119 return status;
120 }
121
ForwardDevtoolsPort(const std::string & package,const std::string & process,std::string & device_socket,int port)122 Status Device::ForwardDevtoolsPort(const std::string& package,
123 const std::string& process,
124 std::string& device_socket,
125 int port) {
126 if (device_socket.empty()) {
127 // Assume this is a WebView app.
128 int pid;
129 Status status = adb_->GetPidByName(serial_,
130 process.empty() ? package : process,
131 &pid);
132 if (status.IsError()) {
133 if (process.empty())
134 status.AddDetails(
135 "process name must be specified if not equal to package name");
136 return status;
137 }
138 device_socket = base::StringPrintf("webview_devtools_remote_%d", pid);
139 }
140
141 return adb_->ForwardPort(serial_, port, device_socket);
142 }
143
TearDown()144 Status Device::TearDown() {
145 if (!active_package_.empty()) {
146 std::string response;
147 Status status = adb_->ForceStop(serial_, active_package_);
148 if (status.IsError())
149 return status;
150 active_package_ = "";
151 }
152 return Status(kOk);
153 }
154
DeviceManager(Adb * adb)155 DeviceManager::DeviceManager(Adb* adb) : adb_(adb) {
156 CHECK(adb_);
157 }
158
~DeviceManager()159 DeviceManager::~DeviceManager() {}
160
AcquireDevice(scoped_ptr<Device> * device)161 Status DeviceManager::AcquireDevice(scoped_ptr<Device>* device) {
162 std::vector<std::string> devices;
163 Status status = adb_->GetDevices(&devices);
164 if (status.IsError())
165 return status;
166
167 if (devices.empty())
168 return Status(kUnknownError, "There are no devices online");
169
170 base::AutoLock lock(devices_lock_);
171 status = Status(kUnknownError, "All devices are in use (" +
172 base::IntToString(devices.size()) + " online)");
173 std::vector<std::string>::iterator iter;
174 for (iter = devices.begin(); iter != devices.end(); iter++) {
175 if (!IsDeviceLocked(*iter)) {
176 device->reset(LockDevice(*iter));
177 status = Status(kOk);
178 break;
179 }
180 }
181 return status;
182 }
183
AcquireSpecificDevice(const std::string & device_serial,scoped_ptr<Device> * device)184 Status DeviceManager::AcquireSpecificDevice(
185 const std::string& device_serial, scoped_ptr<Device>* device) {
186 std::vector<std::string> devices;
187 Status status = adb_->GetDevices(&devices);
188 if (status.IsError())
189 return status;
190
191 if (std::find(devices.begin(), devices.end(), device_serial) == devices.end())
192 return Status(kUnknownError,
193 "Device " + device_serial + " is not online");
194
195 base::AutoLock lock(devices_lock_);
196 if (IsDeviceLocked(device_serial)) {
197 status = Status(kUnknownError,
198 "Device " + device_serial + " is already in use");
199 } else {
200 device->reset(LockDevice(device_serial));
201 status = Status(kOk);
202 }
203 return status;
204 }
205
ReleaseDevice(const std::string & device_serial)206 void DeviceManager::ReleaseDevice(const std::string& device_serial) {
207 base::AutoLock lock(devices_lock_);
208 active_devices_.remove(device_serial);
209 }
210
LockDevice(const std::string & device_serial)211 Device* DeviceManager::LockDevice(const std::string& device_serial) {
212 active_devices_.push_back(device_serial);
213 return new Device(device_serial, adb_,
214 base::Bind(&DeviceManager::ReleaseDevice, base::Unretained(this),
215 device_serial));
216 }
217
IsDeviceLocked(const std::string & device_serial)218 bool DeviceManager::IsDeviceLocked(const std::string& device_serial) {
219 return std::find(active_devices_.begin(), active_devices_.end(),
220 device_serial) != active_devices_.end();
221 }
222
223