• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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