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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: chrome/browser/devtools/device/adb/adb_device_info_query.cc
diff --git a/chrome/browser/devtools/device/adb/adb_device_info_query.cc b/chrome/browser/devtools/device/adb/adb_device_info_query.cc
new file mode 100644
index 0000000000000000000000000000000000000000..f448796bcc2990e551d36a098dd847237d59f89e
--- /dev/null
+++ b/chrome/browser/devtools/device/adb/adb_device_info_query.cc
@@ -0,0 +1,396 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/devtools/device/adb/adb_device_info_query.h"
+
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/strings/stringprintf.h"
+
+namespace {
+
+
+const char kDeviceModelCommand[] = "shell:getprop ro.product.model";
+const char kInstalledChromePackagesCommand[] = "shell:pm list packages";
+const char kOpenedUnixSocketsCommand[] = "shell:cat /proc/net/unix";
+const char kListProcessesCommand[] = "shell:ps";
+const char kDumpsysCommand[] = "shell:dumpsys window policy";
+const char kDumpsysScreenSizePrefix[] = "mStable=";
+
+const char kDevToolsSocketSuffix[] = "_devtools_remote";
+
+const char kChromeDefaultName[] = "Chrome";
+const char kChromeDefaultSocket[] = "chrome_devtools_remote";
+
+const char kWebViewSocketPrefix[] = "webview_devtools_remote";
+const char kWebViewNameTemplate[] = "WebView in %s";
+
+struct BrowserDescriptor {
+ const char* package;
+ const char* socket;
+ const char* display_name;
+};
+
+const BrowserDescriptor kBrowserDescriptors[] = {
+ {
+ "com.android.chrome",
+ kChromeDefaultSocket,
+ kChromeDefaultName
+ },
+ {
+ "com.chrome.beta",
+ kChromeDefaultSocket,
+ "Chrome Beta"
+ },
+ {
+ "com.google.android.apps.chrome_dev",
+ kChromeDefaultSocket,
+ "Chrome Dev"
+ },
+ {
+ "com.chrome.canary",
+ kChromeDefaultSocket,
+ "Chrome Canary"
+ },
+ {
+ "com.google.android.apps.chrome",
+ kChromeDefaultSocket,
+ "Chromium"
+ },
+ {
+ "org.chromium.content_shell_apk",
+ "content_shell_devtools_remote",
+ "Content Shell"
+ },
+ {
+ "org.chromium.chrome.shell",
+ "chrome_shell_devtools_remote",
+ "Chrome Shell"
+ },
+ {
+ "org.chromium.android_webview.shell",
+ "webview_devtools_remote",
+ "WebView Test Shell"
+ }
+};
+
+const BrowserDescriptor* FindBrowserDescriptor(const std::string& package) {
+ int count = sizeof(kBrowserDescriptors) / sizeof(kBrowserDescriptors[0]);
+ for (int i = 0; i < count; i++)
+ if (kBrowserDescriptors[i].package == package)
+ return &kBrowserDescriptors[i];
+ return NULL;
+}
+
+typedef std::map<std::string, const BrowserDescriptor*> DescriptorMap;
+
+static DescriptorMap FindInstalledBrowserPackages(const std::string& response) {
+ // Parse 'pm list packages' output which on Android looks like this:
+ //
+ // package:com.android.chrome
+ // package:com.chrome.beta
+ // package:com.example.app
+ //
+ DescriptorMap package_to_descriptor;
+ const std::string package_prefix = "package:";
+ std::vector<std::string> entries;
+ Tokenize(response, "'\r\n", &entries);
+ for (size_t i = 0; i < entries.size(); ++i) {
+ if (entries[i].find(package_prefix) != 0)
+ continue;
+ std::string package = entries[i].substr(package_prefix.size());
+ const BrowserDescriptor* descriptor = FindBrowserDescriptor(package);
+ if (!descriptor)
+ continue;
+ package_to_descriptor[descriptor->package] = descriptor;
+ }
+ return package_to_descriptor;
+}
+
+typedef std::map<std::string, std::string> StringMap;
+
+static void MapProcessesToPackages(const std::string& response,
+ StringMap& pid_to_package,
+ StringMap& package_to_pid) {
+ // Parse 'ps' output which on Android looks like this:
+ //
+ // USER PID PPID VSIZE RSS WCHAN PC ? NAME
+ //
+ std::vector<std::string> entries;
+ Tokenize(response, "\n", &entries);
+ for (size_t i = 1; i < entries.size(); ++i) {
+ std::vector<std::string> fields;
+ Tokenize(entries[i], " \r", &fields);
+ if (fields.size() < 9)
+ continue;
+ std::string pid = fields[1];
+ std::string package = fields[8];
+ pid_to_package[pid] = package;
+ package_to_pid[package] = pid;
+ }
+}
+
+static StringMap MapSocketsToProcesses(const std::string& response,
+ const std::string& channel_pattern) {
+ // Parse 'cat /proc/net/unix' output which on Android looks like this:
+ //
+ // Num RefCount Protocol Flags Type St Inode Path
+ // 00000000: 00000002 00000000 00010000 0001 01 331813 /dev/socket/zygote
+ // 00000000: 00000002 00000000 00010000 0001 01 358606 @xxx_devtools_remote
+ // 00000000: 00000002 00000000 00010000 0001 01 347300 @yyy_devtools_remote
+ //
+ // We need to find records with paths starting from '@' (abstract socket)
+ // and containing the channel pattern ("_devtools_remote").
+ StringMap socket_to_pid;
+ std::vector<std::string> entries;
+ Tokenize(response, "\n", &entries);
+ for (size_t i = 1; i < entries.size(); ++i) {
+ std::vector<std::string> fields;
+ Tokenize(entries[i], " \r", &fields);
+ if (fields.size() < 8)
+ continue;
+ if (fields[3] != "00010000" || fields[5] != "01")
+ continue;
+ std::string path_field = fields[7];
+ if (path_field.size() < 1 || path_field[0] != '@')
+ continue;
+ size_t socket_name_pos = path_field.find(channel_pattern);
+ if (socket_name_pos == std::string::npos)
+ continue;
+
+ std::string socket = path_field.substr(1);
+
+ std::string pid;
+ size_t socket_name_end = socket_name_pos + channel_pattern.size();
+ if (socket_name_end < path_field.size() &&
+ path_field[socket_name_end] == '_') {
+ pid = path_field.substr(socket_name_end + 1);
+ }
+ socket_to_pid[socket] = pid;
+ }
+ return socket_to_pid;
+}
+
+} // namespace
+
+// static
+std::string AdbDeviceInfoQuery::FindDisplayNameByPackage(
+ const std::string& package) {
+ const BrowserDescriptor* descriptor = FindBrowserDescriptor(package);
+ return descriptor ? descriptor->display_name : std::string();
+}
+
+AdbDeviceInfoQuery::AdbDeviceInfoQuery(
+ const RunCommandClosure& command_closure,
+ const DeviceInfoCallback& callback)
+ : command_closure_(command_closure),
+ callback_(callback) {
+ DCHECK(CalledOnValidThread());
+ command_closure_.Run(
+ kDeviceModelCommand,
+ base::Bind(&AdbDeviceInfoQuery::ReceivedModel, this));
+}
+
+AdbDeviceInfoQuery::~AdbDeviceInfoQuery() {
+}
+
+void AdbDeviceInfoQuery::ReceivedModel(int result,
+ const std::string& response) {
+ DCHECK(CalledOnValidThread());
+ if (result < 0) {
+ Respond();
+ return;
+ }
+ device_info_.model = response;
+ command_closure_.Run(
+ kDumpsysCommand,
+ base::Bind(&AdbDeviceInfoQuery::ReceivedDumpsys, this));
+}
+
+void AdbDeviceInfoQuery::ReceivedDumpsys(int result,
+ const std::string& response) {
+ DCHECK(CalledOnValidThread());
+ if (result >= 0)
+ ParseDumpsysResponse(response);
+
+ command_closure_.Run(
+ kInstalledChromePackagesCommand,
+ base::Bind(&AdbDeviceInfoQuery::ReceivedPackages, this));
+}
+
+void AdbDeviceInfoQuery::ParseDumpsysResponse(const std::string& response) {
+ std::vector<std::string> lines;
+ Tokenize(response, "\r", &lines);
+ for (size_t i = 0; i < lines.size(); ++i) {
+ std::string line = lines[i];
+ size_t pos = line.find(kDumpsysScreenSizePrefix);
+ if (pos != std::string::npos) {
+ ParseScreenSize(
+ line.substr(pos + std::string(kDumpsysScreenSizePrefix).size()));
+ break;
+ }
+ }
+}
+
+void AdbDeviceInfoQuery::ParseScreenSize(const std::string& str) {
+ std::vector<std::string> pairs;
+ Tokenize(str, "-", &pairs);
+ if (pairs.size() != 2)
+ return;
+
+ int width;
+ int height;
+ std::vector<std::string> numbers;
+ Tokenize(pairs[1].substr(1, pairs[1].size() - 2), ",", &numbers);
+ if (numbers.size() != 2 ||
+ !base::StringToInt(numbers[0], &width) ||
+ !base::StringToInt(numbers[1], &height))
+ return;
+
+ device_info_.screen_size = gfx::Size(width, height);
+}
+
+
+void AdbDeviceInfoQuery::ReceivedPackages(
+ int result,
+ const std::string& packages_response) {
+ DCHECK(CalledOnValidThread());
+ if (result < 0) {
+ Respond();
+ return;
+ }
+ command_closure_.Run(
+ kListProcessesCommand,
+ base::Bind(
+ &AdbDeviceInfoQuery::ReceivedProcesses, this, packages_response));
+}
+
+void AdbDeviceInfoQuery::ReceivedProcesses(
+ const std::string& packages_response,
+ int result,
+ const std::string& processes_response) {
+ DCHECK(CalledOnValidThread());
+ if (result < 0) {
+ Respond();
+ return;
+ }
+ command_closure_.Run(
+ kOpenedUnixSocketsCommand,
+ base::Bind(&AdbDeviceInfoQuery::ReceivedSockets,
+ this,
+ packages_response,
+ processes_response));
+}
+
+void AdbDeviceInfoQuery::ReceivedSockets(
+ const std::string& packages_response,
+ const std::string& processes_response,
+ int result,
+ const std::string& sockets_response) {
+ DCHECK(CalledOnValidThread());
+ if (result >= 0)
+ ParseBrowserInfo(packages_response, processes_response, sockets_response);
+ Respond();
+}
+
+void AdbDeviceInfoQuery::ParseBrowserInfo(
+ const std::string& packages_response,
+ const std::string& processes_response,
+ const std::string& sockets_response) {
+ DCHECK(CalledOnValidThread());
+ DescriptorMap package_to_descriptor =
+ FindInstalledBrowserPackages(packages_response);
+ StringMap pid_to_package;
+ StringMap package_to_pid;
+ MapProcessesToPackages(processes_response, pid_to_package, package_to_pid);
+
+ StringMap socket_to_pid = MapSocketsToProcesses(sockets_response,
+ kDevToolsSocketSuffix);
+
+ std::set<std::string> packages_for_running_browsers;
+
+ typedef std::map<std::string, int> BrowserMap;
+ BrowserMap socket_to_unnamed_browser_index;
+
+ for (StringMap::iterator it = socket_to_pid.begin();
+ it != socket_to_pid.end(); ++it) {
+ std::string socket = it->first;
+ std::string pid = it->second;
+
+ AndroidDeviceManager::BrowserInfo browser_info;
+ browser_info.socket = socket;
+ if (socket.find(kChromeDefaultSocket) == 0)
+ browser_info.type = AndroidDeviceManager::BrowserInfo::kTypeChrome;
+ else if (socket.find(kWebViewSocketPrefix) == 0)
+ browser_info.type = AndroidDeviceManager::BrowserInfo::kTypeWebView;
+ else
+ browser_info.type = AndroidDeviceManager::BrowserInfo::kTypeOther;
+
+ StringMap::iterator pit = pid_to_package.find(pid);
+ if (pit != pid_to_package.end()) {
+ std::string package = pit->second;
+ packages_for_running_browsers.insert(package);
+ const BrowserDescriptor* descriptor = FindBrowserDescriptor(package);
+ if (descriptor) {
+ browser_info.display_name = descriptor->display_name;
+ } else if (browser_info.type ==
+ AndroidDeviceManager::BrowserInfo::kTypeWebView) {
+ browser_info.display_name =
+ base::StringPrintf(kWebViewNameTemplate, package.c_str());
+ } else {
+ browser_info.display_name = package;
+ }
+ } else {
+ // Set fallback display name.
+ std::string name = socket.substr(0, socket.find(kDevToolsSocketSuffix));
+ name[0] = base::ToUpperASCII(name[0]);
+ browser_info.display_name = name;
+
+ socket_to_unnamed_browser_index[socket] =
+ device_info_.browser_info.size();
+ }
+ device_info_.browser_info.push_back(browser_info);
+ }
+
+ // Find installed packages not mapped to browsers.
+ typedef std::multimap<std::string, const BrowserDescriptor*>
+ DescriptorMultimap;
+ DescriptorMultimap socket_to_descriptor;
+ for (DescriptorMap::iterator it = package_to_descriptor.begin();
+ it != package_to_descriptor.end(); ++it) {
+ std::string package = it->first;
+ const BrowserDescriptor* descriptor = it->second;
+
+ if (packages_for_running_browsers.count(package))
+ continue; // This package is already mapped to a browser.
+
+ if (package_to_pid.find(package) != package_to_pid.end()) {
+ // This package is running but not mapped to a browser.
+ socket_to_descriptor.insert(
+ DescriptorMultimap::value_type(descriptor->socket, descriptor));
+ continue;
+ }
+ }
+
+ // Try naming remaining unnamed browsers.
+ for (DescriptorMultimap::iterator it = socket_to_descriptor.begin();
+ it != socket_to_descriptor.end(); ++it) {
+ std::string socket = it->first;
+ const BrowserDescriptor* descriptor = it->second;
+
+ if (socket_to_descriptor.count(socket) != 1)
+ continue; // No definitive match.
+
+ BrowserMap::iterator bit = socket_to_unnamed_browser_index.find(socket);
+ if (bit != socket_to_unnamed_browser_index.end()) {
+ device_info_.browser_info[bit->second].display_name =
+ descriptor->display_name;
+ }
+ }
+}
+
+void AdbDeviceInfoQuery::Respond() {
+ DCHECK(CalledOnValidThread());
+ callback_.Run(device_info_);
+}

Powered by Google App Engine
This is Rietveld 408576698