| OLD | NEW | 
|---|
|  | (Empty) | 
| 1 # Copyright (c) 2012 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 import json |  | 
| 6 import logging |  | 
| 7 import os |  | 
| 8 import subprocess |  | 
| 9 import sys |  | 
| 10 import tempfile |  | 
| 11 import time |  | 
| 12 |  | 
| 13 from telemetry import adb_commands |  | 
| 14 from telemetry import browser_backend |  | 
| 15 from telemetry import browser_gone_exception |  | 
| 16 |  | 
| 17 class AndroidBrowserBackend(browser_backend.BrowserBackend): |  | 
| 18   """The backend for controlling a browser instance running on Android. |  | 
| 19   """ |  | 
| 20   def __init__(self, options, adb, package, is_content_shell, |  | 
| 21                cmdline_file, activity, devtools_remote_port): |  | 
| 22     super(AndroidBrowserBackend, self).__init__( |  | 
| 23         is_content_shell=is_content_shell, |  | 
| 24         supports_extensions=False, options=options) |  | 
| 25     if len(options.extensions_to_load) > 0: |  | 
| 26       raise browser_backend.ExtensionsNotSupportedException( |  | 
| 27           'Android browser does not support extensions.') |  | 
| 28     # Initialize fields so that an explosion during init doesn't break in Close. |  | 
| 29     self._options = options |  | 
| 30     self._adb = adb |  | 
| 31     self._package = package |  | 
| 32     self._cmdline_file = cmdline_file |  | 
| 33     self._activity = activity |  | 
| 34     if not options.keep_test_server_ports: |  | 
| 35       adb_commands.ResetTestServerPortAllocation() |  | 
| 36     self._port = adb_commands.AllocateTestServerPort() |  | 
| 37     self._devtools_remote_port = devtools_remote_port |  | 
| 38 |  | 
| 39     # Kill old browser. |  | 
| 40     self._adb.CloseApplication(self._package) |  | 
| 41     self._adb.KillAll('device_forwarder') |  | 
| 42     self._adb.Forward('tcp:%d' % self._port, self._devtools_remote_port) |  | 
| 43 |  | 
| 44     # Chrome Android doesn't listen to --user-data-dir. |  | 
| 45     # TODO: symlink the app's Default, files and cache dir |  | 
| 46     # to somewhere safe. |  | 
| 47     if not is_content_shell and not options.dont_override_profile: |  | 
| 48       # Set up the temp dir |  | 
| 49       # self._tmpdir = '/sdcard/telemetry_data' |  | 
| 50       # self._adb.RunShellCommand('rm -r %s' %  self._tmpdir) |  | 
| 51       # args.append('--user-data-dir=%s' % self._tmpdir) |  | 
| 52       pass |  | 
| 53 |  | 
| 54     # Set up the command line. |  | 
| 55     if is_content_shell: |  | 
| 56       pseudo_exec_name = 'content_shell' |  | 
| 57     else: |  | 
| 58       pseudo_exec_name = 'chrome' |  | 
| 59 |  | 
| 60     args = [pseudo_exec_name] |  | 
| 61     args.extend(self.GetBrowserStartupArgs()) |  | 
| 62 |  | 
| 63     with tempfile.NamedTemporaryFile() as f: |  | 
| 64       def EscapeIfNeeded(arg): |  | 
| 65         params = arg.split('=') |  | 
| 66         if (len(params) == 2 and |  | 
| 67             params[1] and params[1][0] == '"' and params[1][-1] == '"'): |  | 
| 68           # CommandLine.java requires this extra escaping. |  | 
| 69           return '%s="\\%s\\"' % (params[0], params[1]) |  | 
| 70         return arg.replace(' ', '" "') |  | 
| 71       f.write(' '.join([EscapeIfNeeded(arg) for arg in args])) |  | 
| 72       f.flush() |  | 
| 73       self._adb.Push(f.name, cmdline_file) |  | 
| 74 |  | 
| 75     # Force devtools protocol on, if not already done and we can access |  | 
| 76     # protected files. |  | 
| 77     if (not is_content_shell and |  | 
| 78        self._adb.Adb().CanAccessProtectedFileContents()): |  | 
| 79       # Make sure we can find the apps' prefs file |  | 
| 80       app_data_dir = '/data/data/%s' % self._package |  | 
| 81       prefs_file = (app_data_dir + |  | 
| 82                     '/app_chrome/Default/Preferences') |  | 
| 83       if not self._adb.FileExistsOnDevice(prefs_file): |  | 
| 84         # Start it up the first time so we can tweak the prefs. |  | 
| 85         self._adb.StartActivity(self._package, |  | 
| 86                                 self._activity, |  | 
| 87                                 True, |  | 
| 88                                 None, |  | 
| 89                                 None) |  | 
| 90         retries = 0 |  | 
| 91         timeout = 3 |  | 
| 92         time.sleep(timeout) |  | 
| 93         while not self._adb.Adb().GetProtectedFileContents(prefs_file): |  | 
| 94           time.sleep(timeout) |  | 
| 95           retries += 1 |  | 
| 96           timeout *= 2 |  | 
| 97           if retries == 3: |  | 
| 98             logging.critical('android_browser_backend: Could not find ' |  | 
| 99                              'preferences file %s for %s', |  | 
| 100                              prefs_file, self._package) |  | 
| 101             raise browser_gone_exception.BrowserGoneException( |  | 
| 102                 'Missing preferences file.') |  | 
| 103         self._adb.CloseApplication(self._package) |  | 
| 104 |  | 
| 105       preferences = json.loads(''.join( |  | 
| 106           self._adb.Adb().GetProtectedFileContents(prefs_file))) |  | 
| 107       changed = False |  | 
| 108       if 'devtools' not in preferences: |  | 
| 109         preferences['devtools'] = {} |  | 
| 110         changed = True |  | 
| 111       if not preferences['devtools'].get('remote_enabled'): |  | 
| 112         preferences['devtools']['remote_enabled'] = True |  | 
| 113         changed = True |  | 
| 114       if changed: |  | 
| 115         logging.warning('Manually enabled devtools protocol on %s' % |  | 
| 116                         self._package) |  | 
| 117         txt = json.dumps(preferences, indent=2) |  | 
| 118         self._adb.Adb().SetProtectedFileContents(prefs_file, txt) |  | 
| 119 |  | 
| 120     # Start it up with a fresh log. |  | 
| 121     self._adb.RunShellCommand('logcat -c') |  | 
| 122     self._adb.StartActivity(self._package, |  | 
| 123                             self._activity, |  | 
| 124                             True, |  | 
| 125                             None, |  | 
| 126                             'chrome://newtab/') |  | 
| 127     try: |  | 
| 128       self._WaitForBrowserToComeUp() |  | 
| 129       self._PostBrowserStartupInitialization() |  | 
| 130     except browser_gone_exception.BrowserGoneException: |  | 
| 131       logging.critical('Failed to connect to browser.') |  | 
| 132       if not self._adb.IsRootEnabled(): |  | 
| 133         logging.critical( |  | 
| 134           'Ensure web debugging is enabled in Chrome at ' |  | 
| 135           '"Settings > Developer tools > Enable USB Web debugging".') |  | 
| 136       sys.exit(1) |  | 
| 137     except: |  | 
| 138       import traceback |  | 
| 139       traceback.print_exc() |  | 
| 140       self.Close() |  | 
| 141       raise |  | 
| 142 |  | 
| 143   def GetBrowserStartupArgs(self): |  | 
| 144     args = super(AndroidBrowserBackend, self).GetBrowserStartupArgs() |  | 
| 145     args.append('--disable-fre') |  | 
| 146     return args |  | 
| 147 |  | 
| 148   def __del__(self): |  | 
| 149     self.Close() |  | 
| 150 |  | 
| 151   def Close(self): |  | 
| 152     super(AndroidBrowserBackend, self).Close() |  | 
| 153 |  | 
| 154     self._adb.RunShellCommand('rm %s' % self._cmdline_file) |  | 
| 155     self._adb.CloseApplication(self._package) |  | 
| 156 |  | 
| 157   def IsBrowserRunning(self): |  | 
| 158     pids = self._adb.ExtractPid(self._package) |  | 
| 159     return len(pids) != 0 |  | 
| 160 |  | 
| 161   def GetRemotePort(self, local_port): |  | 
| 162     return local_port |  | 
| 163 |  | 
| 164   def GetStandardOutput(self): |  | 
| 165     # If we can find symbols and there is a stack, output the symbolized stack. |  | 
| 166     symbol_paths = [ |  | 
| 167         os.path.join(adb_commands.GetOutDirectory(), 'Release', 'lib.target'), |  | 
| 168         os.path.join(adb_commands.GetOutDirectory(), 'Debug', 'lib.target')] |  | 
| 169     for symbol_path in symbol_paths: |  | 
| 170       if not os.path.isdir(symbol_path): |  | 
| 171         continue |  | 
| 172       with tempfile.NamedTemporaryFile() as f: |  | 
| 173         lines = self._adb.RunShellCommand('logcat -d') |  | 
| 174         for line in lines: |  | 
| 175           f.write(line + '\n') |  | 
| 176         symbolized_stack = None |  | 
| 177         try: |  | 
| 178           logging.info('Symbolizing stack...') |  | 
| 179           symbolized_stack = subprocess.Popen([ |  | 
| 180               'ndk-stack', '-sym', symbol_path, |  | 
| 181               '-dump', f.name], stdout=subprocess.PIPE).communicate()[0] |  | 
| 182         except Exception: |  | 
| 183           pass |  | 
| 184         if symbolized_stack: |  | 
| 185           return symbolized_stack |  | 
| 186     # Otherwise, just return the last 100 lines of logcat. |  | 
| 187     return '\n'.join(self._adb.RunShellCommand('logcat -d -t 100')) |  | 
| 188 |  | 
| 189   def CreateForwarder(self, *port_pairs): |  | 
| 190     return adb_commands.Forwarder(self._adb, *port_pairs) |  | 
| OLD | NEW | 
|---|