| OLD | NEW |
| (Empty) |
| 1 #!/usr/bin/env python | |
| 2 # Copyright (c) 2013 The Chromium Authors. All rights reserved. | |
| 3 # Use of this source code is governed by a BSD-style license that can be | |
| 4 # found in the LICENSE file. | |
| 5 | |
| 6 """A wrapper around adb commands called by chromedriver. | |
| 7 | |
| 8 Preconditions: | |
| 9 - A single device is attached. | |
| 10 - adb is in PATH. | |
| 11 | |
| 12 This script should write everything (including stacktraces) to stdout. | |
| 13 """ | |
| 14 | |
| 15 import collections | |
| 16 import optparse | |
| 17 import subprocess | |
| 18 import sys | |
| 19 import traceback | |
| 20 | |
| 21 | |
| 22 PackageInfo = collections.namedtuple('PackageInfo', ['activity', 'socket']) | |
| 23 CHROME_INFO = PackageInfo('Main', 'chrome_devtools_remote') | |
| 24 PACKAGE_INFO = { | |
| 25 'org.chromium.chrome.testshell': | |
| 26 PackageInfo('ChromiumTestShellActivity', | |
| 27 'chromium_testshell_devtools_remote'), | |
| 28 'com.google.android.apps.chrome': CHROME_INFO, | |
| 29 'com.chrome.dev': CHROME_INFO, | |
| 30 'com.chrome.beta': CHROME_INFO, | |
| 31 'com.android.chrome': CHROME_INFO, | |
| 32 } | |
| 33 | |
| 34 | |
| 35 class AdbError(Exception): | |
| 36 def __init__(self, message, output, cmd): | |
| 37 self.message = message | |
| 38 self.output = output | |
| 39 self.cmd = cmd | |
| 40 | |
| 41 def __str__(self): | |
| 42 return ('%s\nCommand: "%s"\nOutput: "%s"' % | |
| 43 (self.message, self.cmd, self.output)) | |
| 44 | |
| 45 | |
| 46 def RunAdbCommand(args): | |
| 47 """Executes an ADB command and returns its output. | |
| 48 | |
| 49 Args: | |
| 50 args: A sequence of program arguments supplied to adb. | |
| 51 | |
| 52 Returns: | |
| 53 output of the command (stdout + stderr). | |
| 54 | |
| 55 Raises: | |
| 56 AdbError: if exit code is non-zero. | |
| 57 """ | |
| 58 args = ['adb', '-d'] + args | |
| 59 try: | |
| 60 p = subprocess.Popen(args, stdout=subprocess.PIPE, | |
| 61 stderr=subprocess.STDOUT) | |
| 62 out, _ = p.communicate() | |
| 63 if p.returncode: | |
| 64 raise AdbError('Command failed.', out, args) | |
| 65 return out | |
| 66 except OSError as e: | |
| 67 print 'Make sure adb command is in PATH.' | |
| 68 raise e | |
| 69 | |
| 70 | |
| 71 def SetChromeFlags(): | |
| 72 """Sets the command line flags file on device. | |
| 73 | |
| 74 Raises: | |
| 75 AdbError: If failed to write the flags file to device. | |
| 76 """ | |
| 77 cmd = [ | |
| 78 'shell', | |
| 79 'echo chrome --disable-fre --metrics-recording-only ' | |
| 80 '--enable-remote-debugging > /data/local/chrome-command-line;' | |
| 81 'echo $?' | |
| 82 ] | |
| 83 out = RunAdbCommand(cmd).strip() | |
| 84 if out != '0': | |
| 85 raise AdbError('Failed to set the command line flags.', out, cmd) | |
| 86 | |
| 87 | |
| 88 def ClearAppData(package): | |
| 89 """Clears the app data. | |
| 90 | |
| 91 Args: | |
| 92 package: Application package name. | |
| 93 | |
| 94 Raises: | |
| 95 AdbError: if any step fails. | |
| 96 """ | |
| 97 cmd = ['shell', 'pm clear %s' % package] | |
| 98 # am/pm package do not return valid exit codes. | |
| 99 out = RunAdbCommand(cmd) | |
| 100 if 'Success' not in out: | |
| 101 raise AdbError('Failed to clear the profile.', out, cmd) | |
| 102 | |
| 103 | |
| 104 def StartActivity(package): | |
| 105 """Start the activity in the package. | |
| 106 | |
| 107 Args: | |
| 108 package: Application package name. | |
| 109 | |
| 110 Raises: | |
| 111 AdbError: if any step fails. | |
| 112 """ | |
| 113 cmd = [ | |
| 114 'shell', | |
| 115 'am start -a android.intent.action.VIEW -S -W -n %s/.%s ' | |
| 116 '-d "data:text/html;charset=utf-8,"' % | |
| 117 (package, PACKAGE_INFO[package].activity)] | |
| 118 out = RunAdbCommand(cmd) | |
| 119 if 'Complete' not in out: | |
| 120 raise AdbError('Failed to start the activity. %s', out, cmd) | |
| 121 | |
| 122 | |
| 123 def Forward(package, host_port): | |
| 124 """Forward host socket to devtools socket on the device. | |
| 125 | |
| 126 Args: | |
| 127 package: Application package name. | |
| 128 host_port: Port on host to forward. | |
| 129 | |
| 130 Raises: | |
| 131 AdbError: if command fails. | |
| 132 """ | |
| 133 cmd = ['forward', 'tcp:%d' % host_port, | |
| 134 'localabstract:%s' % PACKAGE_INFO[package].socket] | |
| 135 RunAdbCommand(cmd) | |
| 136 | |
| 137 | |
| 138 if __name__ == '__main__': | |
| 139 try: | |
| 140 parser = optparse.OptionParser() | |
| 141 parser.add_option( | |
| 142 '', '--package', help='Application package name.') | |
| 143 parser.add_option( | |
| 144 '', '--launch', action='store_true', | |
| 145 help='Launch the app with a fresh profile and forward devtools port.') | |
| 146 parser.add_option( | |
| 147 '', '--port', type='int', default=33081, | |
| 148 help='Host port to forward for launch operation [default: %default].') | |
| 149 options, _ = parser.parse_args() | |
| 150 | |
| 151 if not options.package: | |
| 152 raise Exception('No package specified.') | |
| 153 | |
| 154 if options.package not in PACKAGE_INFO: | |
| 155 raise Exception('Unknown package provided. Supported packages are:\n %s' % | |
| 156 PACKAGE_INFO.keys()) | |
| 157 | |
| 158 if options.launch: | |
| 159 SetChromeFlags() | |
| 160 ClearAppData(options.package) | |
| 161 StartActivity(options.package) | |
| 162 Forward(options.package, options.port) | |
| 163 else: | |
| 164 raise Exception('No options provided.') | |
| 165 except: | |
| 166 traceback.print_exc(file=sys.stdout) | |
| 167 sys.exit(1) | |
| 168 | |
| OLD | NEW |