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 """Finds android browsers that can be controlled by telemetry.""" 5 6 import os 7 import logging as real_logging 8 import re 9 import subprocess 10 import sys 11 12 from telemetry.core import browser 13 from telemetry.core import possible_browser 14 from telemetry.core import profile_types 15 from telemetry.core.chrome import adb_commands 16 from telemetry.core.chrome import android_browser_backend 17 from telemetry.core.platform import android_platform_backend 18 19 CHROME_PACKAGE_NAMES = { 20 'android-chrome': 'com.google.android.apps.chrome', 21 'android-chrome-beta': 'com.chrome.beta', 22 'android-chrome-dev': 'com.google.android.apps.chrome_dev', 23 'android-jb-system-chrome': 'com.android.chrome' 24 } 25 26 ALL_BROWSER_TYPES = ','.join([ 27 'android-chromium-testshell', 28 'android-content-shell', 29 'android-webview', 30 ] + CHROME_PACKAGE_NAMES.keys()) 31 32 CONTENT_SHELL_PACKAGE = 'org.chromium.content_shell_apk' 33 CHROMIUM_TESTSHELL_PACKAGE = 'org.chromium.chrome.testshell' 34 WEBVIEW_PACKAGE = 'com.android.webview.chromium.shell' 35 36 37 # adb shell pm list packages 38 # adb 39 # intents to run (pass -D url for the rest) 40 # com.android.chrome/.Main 41 # com.google.android.apps.chrome/.Main 42 43 class PossibleAndroidBrowser(possible_browser.PossibleBrowser): 44 """A launchable android browser instance.""" 45 def __init__(self, browser_type, options, backend_settings): 46 super(PossibleAndroidBrowser, self).__init__(browser_type, options) 47 self._backend_settings = backend_settings 48 49 def __repr__(self): 50 return 'PossibleAndroidBrowser(browser_type=%s)' % self.browser_type 51 52 def Create(self): 53 if profile_types.GetProfileCreator(self.options.profile_type): 54 raise Exception("Profile creation not currently supported on Android") 55 56 backend = android_browser_backend.AndroidBrowserBackend( 57 self._options, self._backend_settings) 58 platform_backend = android_platform_backend.AndroidPlatformBackend( 59 self._backend_settings.adb.Adb(), self._options.no_performance_mode) 60 b = browser.Browser(backend, platform_backend) 61 return b 62 63 def SupportsOptions(self, options): 64 if len(options.extensions_to_load) != 0: 65 return False 66 return True 67 68 def SelectDefaultBrowser(_): 69 return None 70 71 adb_works = None 72 def CanFindAvailableBrowsers(logging=real_logging): 73 if not adb_commands.IsAndroidSupported(): 74 return False 75 76 global adb_works 77 78 if adb_works == None: 79 try: 80 with open(os.devnull, 'w') as devnull: 81 proc = subprocess.Popen(['adb', 'devices'], 82 stdout=subprocess.PIPE, 83 stderr=subprocess.PIPE, 84 stdin=devnull) 85 stdout, _ = proc.communicate() 86 if re.search(re.escape('????????????\tno permissions'), stdout) != None: 87 logging.warn( 88 ('adb devices reported a permissions error. Consider ' 89 'restarting adb as root:')) 90 logging.warn(' adb kill-server') 91 logging.warn(' sudo `which adb` devices\n\n') 92 adb_works = True 93 except OSError: 94 platform_tools_path = os.path.join( 95 os.path.dirname(__file__), '..', '..', '..', '..', '..' 96 'third_party', 'android_tools', 'sdk', 'platform-tools') 97 if (sys.platform.startswith('linux') and 98 os.path.exists(os.path.join(platform_tools_path, 'adb'))): 99 os.environ['PATH'] = os.pathsep.join([platform_tools_path, 100 os.environ['PATH']]) 101 adb_works = True 102 else: 103 adb_works = False 104 105 return adb_works 106 107 def FindAllAvailableBrowsers(options, logging=real_logging): 108 """Finds all the desktop browsers available on this machine.""" 109 if not CanFindAvailableBrowsers(logging=logging): 110 logging.info('No adb command found. ' + 111 'Will not try searching for Android browsers.') 112 return [] 113 114 device = None 115 if options.android_device: 116 devices = [options.android_device] 117 else: 118 devices = adb_commands.GetAttachedDevices() 119 120 if len(devices) == 0: 121 logging.info('No android devices found.') 122 return [] 123 124 if len(devices) > 1: 125 logging.warn('Multiple devices attached. ' + 126 'Please specify a device explicitly.') 127 return [] 128 129 device = devices[0] 130 131 adb = adb_commands.AdbCommands(device=device) 132 133 packages = adb.RunShellCommand('pm list packages') 134 possible_browsers = [] 135 if 'package:' + CONTENT_SHELL_PACKAGE in packages: 136 b = PossibleAndroidBrowser( 137 'android-content-shell', 138 options, android_browser_backend.ContentShellBackendSettings( 139 adb, CONTENT_SHELL_PACKAGE)) 140 possible_browsers.append(b) 141 142 if 'package:' + CHROMIUM_TESTSHELL_PACKAGE in packages: 143 b = PossibleAndroidBrowser( 144 'android-chromium-testshell', 145 options, android_browser_backend.ChromiumTestShellBackendSettings( 146 adb, CHROMIUM_TESTSHELL_PACKAGE)) 147 possible_browsers.append(b) 148 149 if 'package:' + WEBVIEW_PACKAGE in packages: 150 b = PossibleAndroidBrowser( 151 'android-webview', 152 options, 153 android_browser_backend.WebviewBackendSettings(adb, WEBVIEW_PACKAGE)) 154 possible_browsers.append(b) 155 156 for name, package in CHROME_PACKAGE_NAMES.iteritems(): 157 if 'package:' + package in packages: 158 b = PossibleAndroidBrowser( 159 name, 160 options, 161 android_browser_backend.ChromeBackendSettings(adb, package)) 162 possible_browsers.append(b) 163 164 # See if the "forwarder" is installed -- we need this to host content locally 165 # but make it accessible to the device. 166 if len(possible_browsers) and not adb_commands.HasForwarder(): 167 logging.warn('telemetry detected an android device. However,') 168 logging.warn('Chrome\'s port-forwarder app is not available.') 169 logging.warn('To build:') 170 logging.warn(' ninja -C out/Release forwarder2 md5sum') 171 logging.warn('') 172 logging.warn('') 173 return [] 174 return possible_browsers 175