1 // Copyright 2014 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/browser/devtools/device/adb/adb_device_info_query.h"
6
7 #include "base/strings/string_number_conversions.h"
8 #include "base/strings/string_util.h"
9 #include "base/strings/stringprintf.h"
10
11 namespace {
12
13
14 const char kDeviceModelCommand[] = "shell:getprop ro.product.model";
15 const char kOpenedUnixSocketsCommand[] = "shell:cat /proc/net/unix";
16 const char kListProcessesCommand[] = "shell:ps";
17 const char kDumpsysCommand[] = "shell:dumpsys window policy";
18 const char kDumpsysScreenSizePrefix[] = "mStable=";
19
20 const char kDevToolsSocketSuffix[] = "_devtools_remote";
21
22 const char kChromeDefaultName[] = "Chrome";
23 const char kChromeDefaultSocket[] = "chrome_devtools_remote";
24
25 const char kWebViewSocketPrefix[] = "webview_devtools_remote";
26 const char kWebViewNameTemplate[] = "WebView in %s";
27
28 struct BrowserDescriptor {
29 const char* package;
30 const char* socket;
31 const char* display_name;
32 };
33
34 const BrowserDescriptor kBrowserDescriptors[] = {
35 {
36 "com.android.chrome",
37 kChromeDefaultSocket,
38 kChromeDefaultName
39 },
40 {
41 "com.chrome.beta",
42 kChromeDefaultSocket,
43 "Chrome Beta"
44 },
45 {
46 "com.google.android.apps.chrome_dev",
47 kChromeDefaultSocket,
48 "Chrome Dev"
49 },
50 {
51 "com.chrome.canary",
52 kChromeDefaultSocket,
53 "Chrome Canary"
54 },
55 {
56 "com.google.android.apps.chrome",
57 kChromeDefaultSocket,
58 "Chromium"
59 },
60 {
61 "org.chromium.content_shell_apk",
62 "content_shell_devtools_remote",
63 "Content Shell"
64 },
65 {
66 "org.chromium.chrome.shell",
67 "chrome_shell_devtools_remote",
68 "Chrome Shell"
69 },
70 {
71 "org.chromium.android_webview.shell",
72 "webview_devtools_remote",
73 "WebView Test Shell"
74 }
75 };
76
FindBrowserDescriptor(const std::string & package)77 const BrowserDescriptor* FindBrowserDescriptor(const std::string& package) {
78 int count = sizeof(kBrowserDescriptors) / sizeof(kBrowserDescriptors[0]);
79 for (int i = 0; i < count; i++)
80 if (kBrowserDescriptors[i].package == package)
81 return &kBrowserDescriptors[i];
82 return NULL;
83 }
84
85 typedef std::map<std::string, std::string> StringMap;
86
MapProcessesToPackages(const std::string & response,StringMap & pid_to_package,StringMap & package_to_pid)87 static void MapProcessesToPackages(const std::string& response,
88 StringMap& pid_to_package,
89 StringMap& package_to_pid) {
90 // Parse 'ps' output which on Android looks like this:
91 //
92 // USER PID PPID VSIZE RSS WCHAN PC ? NAME
93 //
94 std::vector<std::string> entries;
95 Tokenize(response, "\n", &entries);
96 for (size_t i = 1; i < entries.size(); ++i) {
97 std::vector<std::string> fields;
98 Tokenize(entries[i], " \r", &fields);
99 if (fields.size() < 9)
100 continue;
101 std::string pid = fields[1];
102 std::string package = fields[8];
103 pid_to_package[pid] = package;
104 package_to_pid[package] = pid;
105 }
106 }
107
MapSocketsToProcesses(const std::string & response,const std::string & channel_pattern)108 static StringMap MapSocketsToProcesses(const std::string& response,
109 const std::string& channel_pattern) {
110 // Parse 'cat /proc/net/unix' output which on Android looks like this:
111 //
112 // Num RefCount Protocol Flags Type St Inode Path
113 // 00000000: 00000002 00000000 00010000 0001 01 331813 /dev/socket/zygote
114 // 00000000: 00000002 00000000 00010000 0001 01 358606 @xxx_devtools_remote
115 // 00000000: 00000002 00000000 00010000 0001 01 347300 @yyy_devtools_remote
116 //
117 // We need to find records with paths starting from '@' (abstract socket)
118 // and containing the channel pattern ("_devtools_remote").
119 StringMap socket_to_pid;
120 std::vector<std::string> entries;
121 Tokenize(response, "\n", &entries);
122 for (size_t i = 1; i < entries.size(); ++i) {
123 std::vector<std::string> fields;
124 Tokenize(entries[i], " \r", &fields);
125 if (fields.size() < 8)
126 continue;
127 if (fields[3] != "00010000" || fields[5] != "01")
128 continue;
129 std::string path_field = fields[7];
130 if (path_field.size() < 1 || path_field[0] != '@')
131 continue;
132 size_t socket_name_pos = path_field.find(channel_pattern);
133 if (socket_name_pos == std::string::npos)
134 continue;
135
136 std::string socket = path_field.substr(1);
137
138 std::string pid;
139 size_t socket_name_end = socket_name_pos + channel_pattern.size();
140 if (socket_name_end < path_field.size() &&
141 path_field[socket_name_end] == '_') {
142 pid = path_field.substr(socket_name_end + 1);
143 }
144 socket_to_pid[socket] = pid;
145 }
146 return socket_to_pid;
147 }
148
149 } // namespace
150
151 // static
152 AndroidDeviceManager::BrowserInfo::Type
GetBrowserType(const std::string & socket)153 AdbDeviceInfoQuery::GetBrowserType(const std::string& socket) {
154 if (socket.find(kChromeDefaultSocket) == 0)
155 return AndroidDeviceManager::BrowserInfo::kTypeChrome;
156
157 if (socket.find(kWebViewSocketPrefix) == 0)
158 return AndroidDeviceManager::BrowserInfo::kTypeWebView;
159
160 return AndroidDeviceManager::BrowserInfo::kTypeOther;
161 }
162
163 // static
GetDisplayName(const std::string & socket,const std::string & package)164 std::string AdbDeviceInfoQuery::GetDisplayName(const std::string& socket,
165 const std::string& package) {
166 if (package.empty()) {
167 // Derive a fallback display name from the socket name.
168 std::string name = socket.substr(0, socket.find(kDevToolsSocketSuffix));
169 name[0] = base::ToUpperASCII(name[0]);
170 return name;
171 }
172
173 const BrowserDescriptor* descriptor = FindBrowserDescriptor(package);
174 if (descriptor)
175 return descriptor->display_name;
176
177 if (GetBrowserType(socket) ==
178 AndroidDeviceManager::BrowserInfo::kTypeWebView)
179 return base::StringPrintf(kWebViewNameTemplate, package.c_str());
180
181 return package;
182 }
183
184 // static
Start(const RunCommandCallback & command_callback,const DeviceInfoCallback & callback)185 void AdbDeviceInfoQuery::Start(const RunCommandCallback& command_callback,
186 const DeviceInfoCallback& callback) {
187 new AdbDeviceInfoQuery(command_callback, callback);
188 }
189
AdbDeviceInfoQuery(const RunCommandCallback & command_callback,const DeviceInfoCallback & callback)190 AdbDeviceInfoQuery::AdbDeviceInfoQuery(
191 const RunCommandCallback& command_callback,
192 const DeviceInfoCallback& callback)
193 : command_callback_(command_callback),
194 callback_(callback) {
195 DCHECK(CalledOnValidThread());
196 command_callback_.Run(
197 kDeviceModelCommand,
198 base::Bind(&AdbDeviceInfoQuery::ReceivedModel, base::Unretained(this)));
199 }
200
~AdbDeviceInfoQuery()201 AdbDeviceInfoQuery::~AdbDeviceInfoQuery() {
202 }
203
ReceivedModel(int result,const std::string & response)204 void AdbDeviceInfoQuery::ReceivedModel(int result,
205 const std::string& response) {
206 DCHECK(CalledOnValidThread());
207 if (result < 0) {
208 Respond();
209 return;
210 }
211 device_info_.model = response;
212 device_info_.connected = true;
213 command_callback_.Run(
214 kDumpsysCommand,
215 base::Bind(&AdbDeviceInfoQuery::ReceivedDumpsys, base::Unretained(this)));
216 }
217
ReceivedDumpsys(int result,const std::string & response)218 void AdbDeviceInfoQuery::ReceivedDumpsys(int result,
219 const std::string& response) {
220 DCHECK(CalledOnValidThread());
221 if (result >= 0)
222 ParseDumpsysResponse(response);
223
224 command_callback_.Run(
225 kListProcessesCommand,
226 base::Bind(&AdbDeviceInfoQuery::ReceivedProcesses,
227 base::Unretained(this)));
228 }
229
ParseDumpsysResponse(const std::string & response)230 void AdbDeviceInfoQuery::ParseDumpsysResponse(const std::string& response) {
231 std::vector<std::string> lines;
232 Tokenize(response, "\r", &lines);
233 for (size_t i = 0; i < lines.size(); ++i) {
234 std::string line = lines[i];
235 size_t pos = line.find(kDumpsysScreenSizePrefix);
236 if (pos != std::string::npos) {
237 ParseScreenSize(
238 line.substr(pos + std::string(kDumpsysScreenSizePrefix).size()));
239 break;
240 }
241 }
242 }
243
ParseScreenSize(const std::string & str)244 void AdbDeviceInfoQuery::ParseScreenSize(const std::string& str) {
245 std::vector<std::string> pairs;
246 Tokenize(str, "-", &pairs);
247 if (pairs.size() != 2)
248 return;
249
250 int width;
251 int height;
252 std::vector<std::string> numbers;
253 Tokenize(pairs[1].substr(1, pairs[1].size() - 2), ",", &numbers);
254 if (numbers.size() != 2 ||
255 !base::StringToInt(numbers[0], &width) ||
256 !base::StringToInt(numbers[1], &height))
257 return;
258
259 device_info_.screen_size = gfx::Size(width, height);
260 }
261
262
ReceivedProcesses(int result,const std::string & processes_response)263 void AdbDeviceInfoQuery::ReceivedProcesses(
264 int result,
265 const std::string& processes_response) {
266 DCHECK(CalledOnValidThread());
267 if (result < 0) {
268 Respond();
269 return;
270 }
271 command_callback_.Run(
272 kOpenedUnixSocketsCommand,
273 base::Bind(&AdbDeviceInfoQuery::ReceivedSockets,
274 base::Unretained(this),
275 processes_response));
276 }
277
ReceivedSockets(const std::string & processes_response,int result,const std::string & sockets_response)278 void AdbDeviceInfoQuery::ReceivedSockets(
279 const std::string& processes_response,
280 int result,
281 const std::string& sockets_response) {
282 DCHECK(CalledOnValidThread());
283 if (result >= 0)
284 ParseBrowserInfo(processes_response, sockets_response);
285 Respond();
286 }
287
ParseBrowserInfo(const std::string & processes_response,const std::string & sockets_response)288 void AdbDeviceInfoQuery::ParseBrowserInfo(
289 const std::string& processes_response,
290 const std::string& sockets_response) {
291 DCHECK(CalledOnValidThread());
292 StringMap pid_to_package;
293 StringMap package_to_pid;
294 MapProcessesToPackages(processes_response, pid_to_package, package_to_pid);
295
296 StringMap socket_to_pid = MapSocketsToProcesses(sockets_response,
297 kDevToolsSocketSuffix);
298
299 std::set<std::string> packages_for_running_browsers;
300
301 typedef std::map<std::string, int> BrowserMap;
302 BrowserMap socket_to_unnamed_browser_index;
303
304 for (StringMap::iterator it = socket_to_pid.begin();
305 it != socket_to_pid.end(); ++it) {
306 std::string socket = it->first;
307 std::string pid = it->second;
308
309 std::string package;
310 StringMap::iterator pit = pid_to_package.find(pid);
311 if (pit != pid_to_package.end()) {
312 package = pit->second;
313 packages_for_running_browsers.insert(package);
314 } else {
315 socket_to_unnamed_browser_index[socket] =
316 device_info_.browser_info.size();
317 }
318
319 AndroidDeviceManager::BrowserInfo browser_info;
320 browser_info.socket_name = socket;
321 browser_info.type = GetBrowserType(socket);
322 browser_info.display_name = GetDisplayName(socket, package);
323 device_info_.browser_info.push_back(browser_info);
324 }
325 }
326
Respond()327 void AdbDeviceInfoQuery::Respond() {
328 DCHECK(CalledOnValidThread());
329 callback_.Run(device_info_);
330 delete this;
331 }
332