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

Side by Side Diff: tools/telemetry/telemetry/core/backends/chrome/android_browser_backend.py

Issue 811703007: Create AndroidCommandLineBackend to handle setting up cmdline args (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: add context manager for command line backend Created 5 years, 11 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
1 # Copyright 2013 The Chromium Authors. All rights reserved. 1 # Copyright 2013 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 import logging 5 import logging
6 import pipes 6 import pipes
7 import sys 7 import sys
8 import time 8 import time
9 9
10 from telemetry.core import exceptions 10 from telemetry.core import exceptions
11 from telemetry.core import forwarders 11 from telemetry.core import forwarders
12 from telemetry.core import util 12 from telemetry.core import util
13 from telemetry.core.backends import adb_commands 13 from telemetry.core.backends import adb_commands
14 from telemetry.core.backends import android_command_line_backend
14 from telemetry.core.backends import browser_backend 15 from telemetry.core.backends import browser_backend
15 from telemetry.core.backends.chrome import chrome_browser_backend 16 from telemetry.core.backends.chrome import chrome_browser_backend
16 from telemetry.core.platform import android_platform_backend as \ 17 from telemetry.core.platform import android_platform_backend as \
17 android_platform_backend_module 18 android_platform_backend_module
18 from telemetry.core.forwarders import android_forwarder 19 from telemetry.core.forwarders import android_forwarder
19 20
20 util.AddDirToPythonPath(util.GetChromiumSrcDir(), 'build', 'android') 21 util.AddDirToPythonPath(util.GetChromiumSrcDir(), 'build', 'android')
21 from pylib.device import device_errors # pylint: disable=F0401 22 from pylib.device import device_errors # pylint: disable=F0401
22 from pylib.device import intent # pylint: disable=F0401 23 from pylib.device import intent # pylint: disable=F0401
23 24
24 25
25 class AndroidBrowserBackendSettings(object):
26
27 def __init__(self, activity, cmdline_file, package, pseudo_exec_name,
28 supports_tab_control):
29 self.activity = activity
30 self._cmdline_file = cmdline_file
31 self.package = package
32 self.pseudo_exec_name = pseudo_exec_name
33 self.supports_tab_control = supports_tab_control
34
35 def GetCommandLineFile(self, is_user_debug_build): # pylint: disable=W0613
36 return self._cmdline_file
37
38 def GetDevtoolsRemotePort(self, adb):
39 raise NotImplementedError()
40
41 @property
42 def profile_ignore_list(self):
43 # Don't delete lib, since it is created by the installer.
44 return ['lib']
45
46
47 class ChromeBackendSettings(AndroidBrowserBackendSettings):
48 # Stores a default Preferences file, re-used to speed up "--page-repeat".
49 _default_preferences_file = None
50
51 def GetCommandLineFile(self, is_user_debug_build):
52 if is_user_debug_build:
53 return '/data/local/tmp/chrome-command-line'
54 else:
55 return '/data/local/chrome-command-line'
56
57 def __init__(self, package):
58 super(ChromeBackendSettings, self).__init__(
59 activity='com.google.android.apps.chrome.Main',
60 cmdline_file=None,
61 package=package,
62 pseudo_exec_name='chrome',
63 supports_tab_control=True)
64
65 def GetDevtoolsRemotePort(self, adb):
66 return 'localabstract:chrome_devtools_remote'
67
68
69 class ContentShellBackendSettings(AndroidBrowserBackendSettings):
70 def __init__(self, package):
71 super(ContentShellBackendSettings, self).__init__(
72 activity='org.chromium.content_shell_apk.ContentShellActivity',
73 cmdline_file='/data/local/tmp/content-shell-command-line',
74 package=package,
75 pseudo_exec_name='content_shell',
76 supports_tab_control=False)
77
78 def GetDevtoolsRemotePort(self, adb):
79 return 'localabstract:content_shell_devtools_remote'
80
81
82 class ChromeShellBackendSettings(AndroidBrowserBackendSettings):
83 def __init__(self, package):
84 super(ChromeShellBackendSettings, self).__init__(
85 activity='org.chromium.chrome.shell.ChromeShellActivity',
86 cmdline_file='/data/local/tmp/chrome-shell-command-line',
87 package=package,
88 pseudo_exec_name='chrome_shell',
89 supports_tab_control=False)
90
91 def GetDevtoolsRemotePort(self, adb):
92 return 'localabstract:chrome_shell_devtools_remote'
93
94
95 class WebviewBackendSettings(AndroidBrowserBackendSettings):
96 def __init__(self, package,
97 activity='org.chromium.telemetry_shell.TelemetryActivity',
98 cmdline_file='/data/local/tmp/webview-command-line'):
99 super(WebviewBackendSettings, self).__init__(
100 activity=activity,
101 cmdline_file=cmdline_file,
102 package=package,
103 pseudo_exec_name='webview',
104 supports_tab_control=False)
105
106 def GetDevtoolsRemotePort(self, adb):
107 # The DevTools socket name for WebView depends on the activity PID's.
108 retries = 0
109 timeout = 1
110 pid = None
111 while True:
112 pids = adb.ExtractPid(self.package)
113 if len(pids) > 0:
114 pid = pids[-1]
115 break
116 time.sleep(timeout)
117 retries += 1
118 timeout *= 2
119 if retries == 4:
120 logging.critical('android_browser_backend: Timeout while waiting for '
121 'activity %s:%s to come up',
122 self.package,
123 self.activity)
124 raise exceptions.BrowserGoneException(self.browser,
125 'Timeout waiting for PID.')
126 return 'localabstract:webview_devtools_remote_%s' % str(pid)
127
128
129 class WebviewShellBackendSettings(WebviewBackendSettings):
130 def __init__(self, package):
131 super(WebviewShellBackendSettings, self).__init__(
132 activity='org.chromium.android_webview.shell.AwShellActivity',
133 cmdline_file='/data/local/tmp/android-webview-command-line',
134 package=package)
135
136
137 class AndroidBrowserBackend(chrome_browser_backend.ChromeBrowserBackend): 26 class AndroidBrowserBackend(chrome_browser_backend.ChromeBrowserBackend):
138 """The backend for controlling a browser instance running on Android.""" 27 """The backend for controlling a browser instance running on Android."""
139 def __init__(self, android_platform_backend, browser_options, 28 def __init__(self, android_platform_backend, browser_options,
140 backend_settings, use_rndis_forwarder, output_profile_path, 29 backend_settings, use_rndis_forwarder, output_profile_path,
141 extensions_to_load, target_arch): 30 extensions_to_load, target_arch):
142 assert isinstance(android_platform_backend, 31 assert isinstance(android_platform_backend,
143 android_platform_backend_module.AndroidPlatformBackend) 32 android_platform_backend_module.AndroidPlatformBackend)
144 super(AndroidBrowserBackend, self).__init__( 33 super(AndroidBrowserBackend, self).__init__(
145 android_platform_backend, 34 android_platform_backend,
146 supports_tab_control=backend_settings.supports_tab_control, 35 supports_tab_control=backend_settings.supports_tab_control,
147 supports_extensions=False, browser_options=browser_options, 36 supports_extensions=False, browser_options=browser_options,
148 output_profile_path=output_profile_path, 37 output_profile_path=output_profile_path,
149 extensions_to_load=extensions_to_load) 38 extensions_to_load=extensions_to_load)
150 if len(extensions_to_load) > 0: 39 if len(extensions_to_load) > 0:
151 raise browser_backend.ExtensionsNotSupportedException( 40 raise browser_backend.ExtensionsNotSupportedException(
152 'Android browser does not support extensions.') 41 'Android browser does not support extensions.')
153 42
154 # Initialize fields so that an explosion during init doesn't break in Close. 43 # Initialize fields so that an explosion during init doesn't break in Close.
155 self._backend_settings = backend_settings 44 self._backend_settings = backend_settings
156 self._saved_cmdline = ''
157 self._target_arch = target_arch 45 self._target_arch = target_arch
158 self._saved_sslflag = '' 46 self._saved_sslflag = ''
159 47
160 # TODO(tonyg): This is flaky because it doesn't reserve the port that it 48 # TODO(tonyg): This is flaky because it doesn't reserve the port that it
161 # allocates. Need to fix this. 49 # allocates. Need to fix this.
162 self._port = adb_commands.AllocateTestServerPort() 50 self._port = adb_commands.AllocateTestServerPort()
163 51
164 # TODO(wuhu): Move to network controller backend. 52 # TODO(wuhu): Move to network controller backend.
165 self.platform_backend.InstallTestCa() 53 self.platform_backend.InstallTestCa()
166 54
(...skipping 23 matching lines...) Expand all
190 # Set the debug app if needed. 78 # Set the debug app if needed.
191 self.platform_backend.SetDebugApp(self._backend_settings.package) 79 self.platform_backend.SetDebugApp(self._backend_settings.package)
192 80
193 @property 81 @property
194 def _adb(self): 82 def _adb(self):
195 return self.platform_backend.adb 83 return self.platform_backend.adb
196 84
197 def _KillBrowser(self): 85 def _KillBrowser(self):
198 self.platform_backend.KillApplication(self._backend_settings.package) 86 self.platform_backend.KillApplication(self._backend_settings.package)
199 87
200 def _SetUpCommandLine(self):
201 def QuoteIfNeeded(arg):
202 # Properly escape "key=valueA valueB" to "key='valueA valueB'"
203 # Values without spaces, or that seem to be quoted are left untouched.
204 # This is required so CommandLine.java can parse valueB correctly rather
205 # than as a separate switch.
206 params = arg.split('=', 1)
207 if len(params) != 2:
208 return arg
209 key, values = params
210 if ' ' not in values:
211 return arg
212 if values[0] in '"\'' and values[-1] == values[0]:
213 return arg
214 return '%s=%s' % (key, pipes.quote(values))
215 args = [self._backend_settings.pseudo_exec_name]
216 args.extend(self.GetBrowserStartupArgs())
217 content = ' '.join(QuoteIfNeeded(arg) for arg in args)
218 cmdline_file = self._backend_settings.GetCommandLineFile(
219 self._adb.IsUserBuild())
220
221 try:
222 # Save the current command line to restore later, except if it appears to
223 # be a Telemetry created one. This is to prevent a common bug where
224 # --host-resolver-rules borks people's browsers if something goes wrong
225 # with Telemetry.
226 self._saved_cmdline = ''.join(self._adb.device().ReadFile(cmdline_file))
227 if '--host-resolver-rules' in self._saved_cmdline:
228 self._saved_cmdline = ''
229 self._adb.device().WriteFile(cmdline_file, content, as_root=True)
230 except device_errors.CommandFailedError:
231 logging.critical('Cannot set Chrome command line. '
232 'Fix this by flashing to a userdebug build.')
233 sys.exit(1)
234
235 def _RestoreCommandLine(self):
236 cmdline_file = self._backend_settings.GetCommandLineFile(
237 self._adb.IsUserBuild())
238 self._adb.device().WriteFile(cmdline_file, self._saved_cmdline,
239 as_root=True)
240
241 def Start(self): 88 def Start(self):
242 self._SetUpCommandLine()
243
244 self._adb.device().RunShellCommand('logcat -c') 89 self._adb.device().RunShellCommand('logcat -c')
245 if self.browser_options.startup_url: 90 if self.browser_options.startup_url:
246 url = self.browser_options.startup_url 91 url = self.browser_options.startup_url
247 elif self.browser_options.profile_dir: 92 elif self.browser_options.profile_dir:
248 url = None 93 url = None
249 else: 94 else:
250 # If we have no existing tabs start with a blank page since default 95 # If we have no existing tabs start with a blank page since default
251 # startup with the NTP can lead to race conditions with Telemetry 96 # startup with the NTP can lead to race conditions with Telemetry
252 url = 'about:blank' 97 url = 'about:blank'
253 98
254 self.platform_backend.DismissCrashDialogIfNeeded() 99 self.platform_backend.DismissCrashDialogIfNeeded()
255 100
256 self._adb.device().StartActivity(
257 intent.Intent(package=self._backend_settings.package,
258 activity=self._backend_settings.activity,
259 action=None, data=url, category=None),
260 blocking=True)
261
262 remote_devtools_port = self._backend_settings.GetDevtoolsRemotePort( 101 remote_devtools_port = self._backend_settings.GetDevtoolsRemotePort(
263 self._adb) 102 self._adb)
264 self.platform_backend.ForwardHostToDevice(self._port, remote_devtools_port) 103 self.platform_backend.ForwardHostToDevice(self._port, remote_devtools_port)
265 104
266 try: 105 browser_startup_args = self.GetBrowserStartupArgs()
267 self._WaitForBrowserToComeUp(remote_devtools_port=remote_devtools_port) 106 with android_command_line_backend.SetUpCommandLineFlags(
268 except exceptions.BrowserGoneException: 107 self._adb, self._backend_settings, browser_startup_args):
269 logging.critical('Failed to connect to browser.') 108 self._adb.device().StartActivity(
270 if not self._adb.device().old_interface.CanAccessProtectedFileContents(): 109 intent.Intent(package=self._backend_settings.package,
271 logging.critical( 110 activity=self._backend_settings.activity,
272 'Resolve this by either: ' 111 action=None, data=url, category=None),
273 '(1) Flashing to a userdebug build OR ' 112 blocking=True)
274 '(2) Manually enabling web debugging in Chrome at ' 113
275 'Settings > Developer tools > Enable USB Web debugging.') 114 try:
276 sys.exit(1) 115 self._WaitForBrowserToComeUp(remote_devtools_port=remote_devtools_port)
277 except: 116 except exceptions.BrowserGoneException:
278 import traceback 117 logging.critical('Failed to connect to browser.')
279 traceback.print_exc() 118 device = self._adb.device()
280 self.Close() 119 if not device.old_interface.CanAccessProtectedFileContents():
281 raise 120 logging.critical(
282 finally: 121 'Resolve this by either: '
283 self._RestoreCommandLine() 122 '(1) Flashing to a userdebug build OR '
123 '(2) Manually enabling web debugging in Chrome at '
124 'Settings > Developer tools > Enable USB Web debugging.')
125 sys.exit(1)
126 except:
127 import traceback
128 traceback.print_exc()
129 self.Close()
130 raise
284 131
285 def GetBrowserStartupArgs(self): 132 def GetBrowserStartupArgs(self):
286 args = super(AndroidBrowserBackend, self).GetBrowserStartupArgs() 133 args = super(AndroidBrowserBackend, self).GetBrowserStartupArgs()
287 args.append('--enable-remote-debugging') 134 args.append('--enable-remote-debugging')
288 args.append('--disable-fre') 135 args.append('--disable-fre')
289 args.append('--disable-external-intent-requests') 136 args.append('--disable-external-intent-requests')
290 return args 137 return args
291 138
292 @property 139 @property
293 def forwarder_factory(self): 140 def forwarder_factory(self):
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
342 189
343 def GetStandardOutput(self): 190 def GetStandardOutput(self):
344 return self.platform_backend.GetStandardOutput() 191 return self.platform_backend.GetStandardOutput()
345 192
346 def GetStackTrace(self): 193 def GetStackTrace(self):
347 return self.platform_backend.GetStackTrace(self._target_arch) 194 return self.platform_backend.GetStackTrace(self._target_arch)
348 195
349 @property 196 @property
350 def should_ignore_certificate_errors(self): 197 def should_ignore_certificate_errors(self):
351 return not self.platform_backend.is_test_ca_installed 198 return not self.platform_backend.is_test_ca_installed
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698