Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(72)

Side by Side Diff: chrome/browser/devtools/device/adb/adb_device_info_query.cc

Issue 274573002: DevTools: Extract ADB specific requests from DevToolsAndroidBridge into a separate class. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebased build files Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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 kInstalledChromePackagesCommand[] = "shell:pm list packages";
16 const char kOpenedUnixSocketsCommand[] = "shell:cat /proc/net/unix";
17 const char kListProcessesCommand[] = "shell:ps";
18 const char kDumpsysCommand[] = "shell:dumpsys window policy";
19 const char kDumpsysScreenSizePrefix[] = "mStable=";
20
21 const char kDevToolsSocketSuffix[] = "_devtools_remote";
22
23 const char kChromeDefaultName[] = "Chrome";
24 const char kChromeDefaultSocket[] = "chrome_devtools_remote";
25
26 const char kWebViewSocketPrefix[] = "webview_devtools_remote";
27 const char kWebViewNameTemplate[] = "WebView in %s";
28
29 struct BrowserDescriptor {
30 const char* package;
31 const char* socket;
32 const char* display_name;
33 };
34
35 const BrowserDescriptor kBrowserDescriptors[] = {
36 {
37 "com.android.chrome",
38 kChromeDefaultSocket,
39 kChromeDefaultName
40 },
41 {
42 "com.chrome.beta",
43 kChromeDefaultSocket,
44 "Chrome Beta"
45 },
46 {
47 "com.google.android.apps.chrome_dev",
48 kChromeDefaultSocket,
49 "Chrome Dev"
50 },
51 {
52 "com.chrome.canary",
53 kChromeDefaultSocket,
54 "Chrome Canary"
55 },
56 {
57 "com.google.android.apps.chrome",
58 kChromeDefaultSocket,
59 "Chromium"
60 },
61 {
62 "org.chromium.content_shell_apk",
63 "content_shell_devtools_remote",
64 "Content Shell"
65 },
66 {
67 "org.chromium.chrome.shell",
68 "chrome_shell_devtools_remote",
69 "Chrome Shell"
70 },
71 {
72 "org.chromium.android_webview.shell",
73 "webview_devtools_remote",
74 "WebView Test Shell"
75 }
76 };
77
78 const BrowserDescriptor* FindBrowserDescriptor(const std::string& package) {
79 int count = sizeof(kBrowserDescriptors) / sizeof(kBrowserDescriptors[0]);
80 for (int i = 0; i < count; i++)
81 if (kBrowserDescriptors[i].package == package)
82 return &kBrowserDescriptors[i];
83 return NULL;
84 }
85
86 typedef std::map<std::string, const BrowserDescriptor*> DescriptorMap;
87
88 static DescriptorMap FindInstalledBrowserPackages(const std::string& response) {
89 // Parse 'pm list packages' output which on Android looks like this:
90 //
91 // package:com.android.chrome
92 // package:com.chrome.beta
93 // package:com.example.app
94 //
95 DescriptorMap package_to_descriptor;
96 const std::string package_prefix = "package:";
97 std::vector<std::string> entries;
98 Tokenize(response, "'\r\n", &entries);
99 for (size_t i = 0; i < entries.size(); ++i) {
100 if (entries[i].find(package_prefix) != 0)
101 continue;
102 std::string package = entries[i].substr(package_prefix.size());
103 const BrowserDescriptor* descriptor = FindBrowserDescriptor(package);
104 if (!descriptor)
105 continue;
106 package_to_descriptor[descriptor->package] = descriptor;
107 }
108 return package_to_descriptor;
109 }
110
111 typedef std::map<std::string, std::string> StringMap;
112
113 static void MapProcessesToPackages(const std::string& response,
114 StringMap& pid_to_package,
115 StringMap& package_to_pid) {
116 // Parse 'ps' output which on Android looks like this:
117 //
118 // USER PID PPID VSIZE RSS WCHAN PC ? NAME
119 //
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() < 9)
126 continue;
127 std::string pid = fields[1];
128 std::string package = fields[8];
129 pid_to_package[pid] = package;
130 package_to_pid[package] = pid;
131 }
132 }
133
134 static StringMap MapSocketsToProcesses(const std::string& response,
135 const std::string& channel_pattern) {
136 // Parse 'cat /proc/net/unix' output which on Android looks like this:
137 //
138 // Num RefCount Protocol Flags Type St Inode Path
139 // 00000000: 00000002 00000000 00010000 0001 01 331813 /dev/socket/zygote
140 // 00000000: 00000002 00000000 00010000 0001 01 358606 @xxx_devtools_remote
141 // 00000000: 00000002 00000000 00010000 0001 01 347300 @yyy_devtools_remote
142 //
143 // We need to find records with paths starting from '@' (abstract socket)
144 // and containing the channel pattern ("_devtools_remote").
145 StringMap socket_to_pid;
146 std::vector<std::string> entries;
147 Tokenize(response, "\n", &entries);
148 for (size_t i = 1; i < entries.size(); ++i) {
149 std::vector<std::string> fields;
150 Tokenize(entries[i], " \r", &fields);
151 if (fields.size() < 8)
152 continue;
153 if (fields[3] != "00010000" || fields[5] != "01")
154 continue;
155 std::string path_field = fields[7];
156 if (path_field.size() < 1 || path_field[0] != '@')
157 continue;
158 size_t socket_name_pos = path_field.find(channel_pattern);
159 if (socket_name_pos == std::string::npos)
160 continue;
161
162 std::string socket = path_field.substr(1);
163
164 std::string pid;
165 size_t socket_name_end = socket_name_pos + channel_pattern.size();
166 if (socket_name_end < path_field.size() &&
167 path_field[socket_name_end] == '_') {
168 pid = path_field.substr(socket_name_end + 1);
169 }
170 socket_to_pid[socket] = pid;
171 }
172 return socket_to_pid;
173 }
174
175 } // namespace
176
177 // static
178 std::string AdbDeviceInfoQuery::FindDisplayNameByPackage(
179 const std::string& package) {
180 const BrowserDescriptor* descriptor = FindBrowserDescriptor(package);
181 return descriptor ? descriptor->display_name : std::string();
182 }
183
184 AdbDeviceInfoQuery::AdbDeviceInfoQuery(
185 const RunCommandClosure& command_closure,
186 const DeviceInfoCallback& callback)
187 : command_closure_(command_closure),
188 callback_(callback) {
189 DCHECK(CalledOnValidThread());
190 command_closure_.Run(
191 kDeviceModelCommand,
192 base::Bind(&AdbDeviceInfoQuery::ReceivedModel, this));
193 }
194
195 AdbDeviceInfoQuery::~AdbDeviceInfoQuery() {
196 }
197
198 void AdbDeviceInfoQuery::ReceivedModel(int result,
199 const std::string& response) {
200 DCHECK(CalledOnValidThread());
201 if (result < 0) {
202 Respond();
203 return;
204 }
205 device_info_.model = response;
206 command_closure_.Run(
207 kDumpsysCommand,
208 base::Bind(&AdbDeviceInfoQuery::ReceivedDumpsys, this));
209 }
210
211 void AdbDeviceInfoQuery::ReceivedDumpsys(int result,
212 const std::string& response) {
213 DCHECK(CalledOnValidThread());
214 if (result >= 0)
215 ParseDumpsysResponse(response);
216
217 command_closure_.Run(
218 kInstalledChromePackagesCommand,
219 base::Bind(&AdbDeviceInfoQuery::ReceivedPackages, this));
220 }
221
222 void AdbDeviceInfoQuery::ParseDumpsysResponse(const std::string& response) {
223 std::vector<std::string> lines;
224 Tokenize(response, "\r", &lines);
225 for (size_t i = 0; i < lines.size(); ++i) {
226 std::string line = lines[i];
227 size_t pos = line.find(kDumpsysScreenSizePrefix);
228 if (pos != std::string::npos) {
229 ParseScreenSize(
230 line.substr(pos + std::string(kDumpsysScreenSizePrefix).size()));
231 break;
232 }
233 }
234 }
235
236 void AdbDeviceInfoQuery::ParseScreenSize(const std::string& str) {
237 std::vector<std::string> pairs;
238 Tokenize(str, "-", &pairs);
239 if (pairs.size() != 2)
240 return;
241
242 int width;
243 int height;
244 std::vector<std::string> numbers;
245 Tokenize(pairs[1].substr(1, pairs[1].size() - 2), ",", &numbers);
246 if (numbers.size() != 2 ||
247 !base::StringToInt(numbers[0], &width) ||
248 !base::StringToInt(numbers[1], &height))
249 return;
250
251 device_info_.screen_size = gfx::Size(width, height);
252 }
253
254
255 void AdbDeviceInfoQuery::ReceivedPackages(
256 int result,
257 const std::string& packages_response) {
258 DCHECK(CalledOnValidThread());
259 if (result < 0) {
260 Respond();
261 return;
262 }
263 command_closure_.Run(
264 kListProcessesCommand,
265 base::Bind(
266 &AdbDeviceInfoQuery::ReceivedProcesses, this, packages_response));
267 }
268
269 void AdbDeviceInfoQuery::ReceivedProcesses(
270 const std::string& packages_response,
271 int result,
272 const std::string& processes_response) {
273 DCHECK(CalledOnValidThread());
274 if (result < 0) {
275 Respond();
276 return;
277 }
278 command_closure_.Run(
279 kOpenedUnixSocketsCommand,
280 base::Bind(&AdbDeviceInfoQuery::ReceivedSockets,
281 this,
282 packages_response,
283 processes_response));
284 }
285
286 void AdbDeviceInfoQuery::ReceivedSockets(
287 const std::string& packages_response,
288 const std::string& processes_response,
289 int result,
290 const std::string& sockets_response) {
291 DCHECK(CalledOnValidThread());
292 if (result >= 0)
293 ParseBrowserInfo(packages_response, processes_response, sockets_response);
294 Respond();
295 }
296
297 void AdbDeviceInfoQuery::ParseBrowserInfo(
298 const std::string& packages_response,
299 const std::string& processes_response,
300 const std::string& sockets_response) {
301 DCHECK(CalledOnValidThread());
302 DescriptorMap package_to_descriptor =
303 FindInstalledBrowserPackages(packages_response);
304 StringMap pid_to_package;
305 StringMap package_to_pid;
306 MapProcessesToPackages(processes_response, pid_to_package, package_to_pid);
307
308 StringMap socket_to_pid = MapSocketsToProcesses(sockets_response,
309 kDevToolsSocketSuffix);
310
311 std::set<std::string> packages_for_running_browsers;
312
313 typedef std::map<std::string, int> BrowserMap;
314 BrowserMap socket_to_unnamed_browser_index;
315
316 for (StringMap::iterator it = socket_to_pid.begin();
317 it != socket_to_pid.end(); ++it) {
318 std::string socket = it->first;
319 std::string pid = it->second;
320
321 AndroidDeviceManager::BrowserInfo browser_info;
322 browser_info.socket = socket;
323 if (socket.find(kChromeDefaultSocket) == 0)
324 browser_info.type = AndroidDeviceManager::BrowserInfo::kTypeChrome;
325 else if (socket.find(kWebViewSocketPrefix) == 0)
326 browser_info.type = AndroidDeviceManager::BrowserInfo::kTypeWebView;
327 else
328 browser_info.type = AndroidDeviceManager::BrowserInfo::kTypeOther;
329
330 StringMap::iterator pit = pid_to_package.find(pid);
331 if (pit != pid_to_package.end()) {
332 std::string package = pit->second;
333 packages_for_running_browsers.insert(package);
334 const BrowserDescriptor* descriptor = FindBrowserDescriptor(package);
335 if (descriptor) {
336 browser_info.display_name = descriptor->display_name;
337 } else if (browser_info.type ==
338 AndroidDeviceManager::BrowserInfo::kTypeWebView) {
339 browser_info.display_name =
340 base::StringPrintf(kWebViewNameTemplate, package.c_str());
341 } else {
342 browser_info.display_name = package;
343 }
344 } else {
345 // Set fallback display name.
346 std::string name = socket.substr(0, socket.find(kDevToolsSocketSuffix));
347 name[0] = base::ToUpperASCII(name[0]);
348 browser_info.display_name = name;
349
350 socket_to_unnamed_browser_index[socket] =
351 device_info_.browser_info.size();
352 }
353 device_info_.browser_info.push_back(browser_info);
354 }
355
356 // Find installed packages not mapped to browsers.
357 typedef std::multimap<std::string, const BrowserDescriptor*>
358 DescriptorMultimap;
359 DescriptorMultimap socket_to_descriptor;
360 for (DescriptorMap::iterator it = package_to_descriptor.begin();
361 it != package_to_descriptor.end(); ++it) {
362 std::string package = it->first;
363 const BrowserDescriptor* descriptor = it->second;
364
365 if (packages_for_running_browsers.count(package))
366 continue; // This package is already mapped to a browser.
367
368 if (package_to_pid.find(package) != package_to_pid.end()) {
369 // This package is running but not mapped to a browser.
370 socket_to_descriptor.insert(
371 DescriptorMultimap::value_type(descriptor->socket, descriptor));
372 continue;
373 }
374 }
375
376 // Try naming remaining unnamed browsers.
377 for (DescriptorMultimap::iterator it = socket_to_descriptor.begin();
378 it != socket_to_descriptor.end(); ++it) {
379 std::string socket = it->first;
380 const BrowserDescriptor* descriptor = it->second;
381
382 if (socket_to_descriptor.count(socket) != 1)
383 continue; // No definitive match.
384
385 BrowserMap::iterator bit = socket_to_unnamed_browser_index.find(socket);
386 if (bit != socket_to_unnamed_browser_index.end()) {
387 device_info_.browser_info[bit->second].display_name =
388 descriptor->display_name;
389 }
390 }
391 }
392
393 void AdbDeviceInfoQuery::Respond() {
394 DCHECK(CalledOnValidThread());
395 callback_.Run(device_info_);
396 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698