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

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: Addressed comments 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 AndroidDeviceManager::BrowserInfo::Type
179 AdbDeviceInfoQuery::GetBrowserType(const std::string& socket) {
180 if (socket.find(kChromeDefaultSocket) == 0)
181 return AndroidDeviceManager::BrowserInfo::kTypeChrome;
182
183 if (socket.find(kWebViewSocketPrefix) == 0)
184 return AndroidDeviceManager::BrowserInfo::kTypeWebView;
185
186 return AndroidDeviceManager::BrowserInfo::kTypeOther;
187 }
188
189 // static
190 std::string AdbDeviceInfoQuery::GetDisplayName(const std::string& socket,
191 const std::string& package) {
192 if (package.empty()) {
193 // Derive a fallback display name from the socket name.
194 std::string name = socket.substr(0, socket.find(kDevToolsSocketSuffix));
195 name[0] = base::ToUpperASCII(name[0]);
196 return name;
197 }
198
199 const BrowserDescriptor* descriptor = FindBrowserDescriptor(package);
200 if (descriptor)
201 return descriptor->display_name;
202
203 if (GetBrowserType(socket) ==
204 AndroidDeviceManager::BrowserInfo::kTypeWebView)
205 return base::StringPrintf(kWebViewNameTemplate, package.c_str());
206
207 return package;
208 }
209
210 // static
211 void AdbDeviceInfoQuery::Start(const RunCommandCallback& command_callback,
212 const DeviceInfoCallback& callback) {
213 new AdbDeviceInfoQuery(command_callback, callback);
214 }
215
216 AdbDeviceInfoQuery::AdbDeviceInfoQuery(
217 const RunCommandCallback& command_callback,
218 const DeviceInfoCallback& callback)
219 : command_callback_(command_callback),
220 callback_(callback) {
221 DCHECK(CalledOnValidThread());
222 command_callback_.Run(
223 kDeviceModelCommand,
224 base::Bind(&AdbDeviceInfoQuery::ReceivedModel, base::Unretained(this)));
225 }
226
227 AdbDeviceInfoQuery::~AdbDeviceInfoQuery() {
228 }
229
230 void AdbDeviceInfoQuery::ReceivedModel(int result,
231 const std::string& response) {
232 DCHECK(CalledOnValidThread());
233 if (result < 0) {
234 Respond();
235 return;
236 }
237 device_info_.model = response;
238 command_callback_.Run(
239 kDumpsysCommand,
240 base::Bind(&AdbDeviceInfoQuery::ReceivedDumpsys, base::Unretained(this)));
241 }
242
243 void AdbDeviceInfoQuery::ReceivedDumpsys(int result,
244 const std::string& response) {
245 DCHECK(CalledOnValidThread());
246 if (result >= 0)
247 ParseDumpsysResponse(response);
248
249 command_callback_.Run(
250 kInstalledChromePackagesCommand,
251 base::Bind(&AdbDeviceInfoQuery::ReceivedPackages,
252 base::Unretained(this)));
253 }
254
255 void AdbDeviceInfoQuery::ParseDumpsysResponse(const std::string& response) {
256 std::vector<std::string> lines;
257 Tokenize(response, "\r", &lines);
258 for (size_t i = 0; i < lines.size(); ++i) {
259 std::string line = lines[i];
260 size_t pos = line.find(kDumpsysScreenSizePrefix);
261 if (pos != std::string::npos) {
262 ParseScreenSize(
263 line.substr(pos + std::string(kDumpsysScreenSizePrefix).size()));
264 break;
265 }
266 }
267 }
268
269 void AdbDeviceInfoQuery::ParseScreenSize(const std::string& str) {
270 std::vector<std::string> pairs;
271 Tokenize(str, "-", &pairs);
272 if (pairs.size() != 2)
273 return;
274
275 int width;
276 int height;
277 std::vector<std::string> numbers;
278 Tokenize(pairs[1].substr(1, pairs[1].size() - 2), ",", &numbers);
279 if (numbers.size() != 2 ||
280 !base::StringToInt(numbers[0], &width) ||
281 !base::StringToInt(numbers[1], &height))
282 return;
283
284 device_info_.screen_size = gfx::Size(width, height);
285 }
286
287
288 void AdbDeviceInfoQuery::ReceivedPackages(
289 int result,
290 const std::string& packages_response) {
291 DCHECK(CalledOnValidThread());
292 if (result < 0) {
293 Respond();
294 return;
295 }
296 command_callback_.Run(
297 kListProcessesCommand,
298 base::Bind(&AdbDeviceInfoQuery::ReceivedProcesses,
299 base::Unretained(this), packages_response));
300 }
301
302 void AdbDeviceInfoQuery::ReceivedProcesses(
303 const std::string& packages_response,
304 int result,
305 const std::string& processes_response) {
306 DCHECK(CalledOnValidThread());
307 if (result < 0) {
308 Respond();
309 return;
310 }
311 command_callback_.Run(
312 kOpenedUnixSocketsCommand,
313 base::Bind(&AdbDeviceInfoQuery::ReceivedSockets,
314 base::Unretained(this),
315 packages_response,
316 processes_response));
317 }
318
319 void AdbDeviceInfoQuery::ReceivedSockets(
320 const std::string& packages_response,
321 const std::string& processes_response,
322 int result,
323 const std::string& sockets_response) {
324 DCHECK(CalledOnValidThread());
325 if (result >= 0)
326 ParseBrowserInfo(packages_response, processes_response, sockets_response);
327 Respond();
328 }
329
330 void AdbDeviceInfoQuery::ParseBrowserInfo(
331 const std::string& packages_response,
332 const std::string& processes_response,
333 const std::string& sockets_response) {
334 DCHECK(CalledOnValidThread());
335 DescriptorMap package_to_descriptor =
336 FindInstalledBrowserPackages(packages_response);
337 StringMap pid_to_package;
338 StringMap package_to_pid;
339 MapProcessesToPackages(processes_response, pid_to_package, package_to_pid);
340
341 StringMap socket_to_pid = MapSocketsToProcesses(sockets_response,
342 kDevToolsSocketSuffix);
343
344 std::set<std::string> packages_for_running_browsers;
345
346 typedef std::map<std::string, int> BrowserMap;
347 BrowserMap socket_to_unnamed_browser_index;
348
349 for (StringMap::iterator it = socket_to_pid.begin();
350 it != socket_to_pid.end(); ++it) {
351 std::string socket = it->first;
352 std::string pid = it->second;
353
354 std::string package;
355 StringMap::iterator pit = pid_to_package.find(pid);
356 if (pit != pid_to_package.end()) {
357 package = pit->second;
358 packages_for_running_browsers.insert(package);
359 } else {
360 socket_to_unnamed_browser_index[socket] =
361 device_info_.browser_info.size();
362 }
363
364 AndroidDeviceManager::BrowserInfo browser_info;
365 browser_info.socket_name = socket;
366 browser_info.type = GetBrowserType(socket);
367 browser_info.display_name = GetDisplayName(socket, package);
368 device_info_.browser_info.push_back(browser_info);
369 }
370
371 // Find installed packages not mapped to browsers.
372 typedef std::multimap<std::string, const BrowserDescriptor*>
373 DescriptorMultimap;
374 DescriptorMultimap socket_to_descriptor;
375 for (DescriptorMap::iterator it = package_to_descriptor.begin();
376 it != package_to_descriptor.end(); ++it) {
377 std::string package = it->first;
378 const BrowserDescriptor* descriptor = it->second;
379
380 if (packages_for_running_browsers.count(package))
381 continue; // This package is already mapped to a browser.
382
383 if (package_to_pid.find(package) != package_to_pid.end()) {
384 // This package is running but not mapped to a browser.
385 socket_to_descriptor.insert(
386 DescriptorMultimap::value_type(descriptor->socket, descriptor));
387 continue;
388 }
389 }
390
391 // Try naming remaining unnamed browsers.
392 for (DescriptorMultimap::iterator it = socket_to_descriptor.begin();
393 it != socket_to_descriptor.end(); ++it) {
394 std::string socket = it->first;
395 const BrowserDescriptor* descriptor = it->second;
396
397 if (socket_to_descriptor.count(socket) != 1)
398 continue; // No definitive match.
399
400 BrowserMap::iterator bit = socket_to_unnamed_browser_index.find(socket);
401 if (bit != socket_to_unnamed_browser_index.end()) {
402 device_info_.browser_info[bit->second].display_name =
403 descriptor->display_name;
404 }
405 }
406 }
407
408 void AdbDeviceInfoQuery::Respond() {
409 DCHECK(CalledOnValidThread());
410 callback_.Run(device_info_);
411 delete this;
412 }
OLDNEW
« no previous file with comments | « chrome/browser/devtools/device/adb/adb_device_info_query.h ('k') | chrome/browser/devtools/device/adb/adb_device_provider.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698