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
3 # found in the LICENSE file.
5 """Finds android browsers that can be controlled by telemetry."""
12 from py_utils
import dependency_util
13 from devil
import base_error
14 from devil
.android
import apk_helper
16 from telemetry
.core
import exceptions
17 from telemetry
.core
import platform
18 from telemetry
.core
import util
19 from telemetry
import decorators
20 from telemetry
.internal
.backends
import android_browser_backend_settings
21 from telemetry
.internal
.backends
.chrome
import android_browser_backend
22 from telemetry
.internal
.browser
import browser
23 from telemetry
.internal
.browser
import possible_browser
24 from telemetry
.internal
.platform
import android_device
25 from telemetry
.internal
.util
import binary_manager
28 CHROME_PACKAGE_NAMES
= {
29 'android-content-shell':
30 ['org.chromium.content_shell_apk',
31 android_browser_backend_settings
.ContentShellBackendSettings
,
34 ['org.chromium.webview_shell',
35 android_browser_backend_settings
.WebviewBackendSettings
,
37 'android-webview-shell':
38 ['org.chromium.android_webview.shell',
39 android_browser_backend_settings
.WebviewShellBackendSettings
,
40 'AndroidWebView.apk'],
42 ['org.chromium.chrome',
43 android_browser_backend_settings
.ChromeBackendSettings
,
46 ['com.google.android.apps.chrome',
47 android_browser_backend_settings
.ChromeBackendSettings
,
49 'android-chrome-work':
51 android_browser_backend_settings
.ChromeBackendSettings
,
53 'android-chrome-beta':
55 android_browser_backend_settings
.ChromeBackendSettings
,
59 android_browser_backend_settings
.ChromeBackendSettings
,
61 'android-chrome-canary':
63 android_browser_backend_settings
.ChromeBackendSettings
,
65 'android-system-chrome':
66 ['com.android.chrome',
67 android_browser_backend_settings
.ChromeBackendSettings
,
72 class PossibleAndroidBrowser(possible_browser
.PossibleBrowser
):
73 """A launchable android browser instance."""
74 def __init__(self
, browser_type
, finder_options
, android_platform
,
75 backend_settings
, apk_name
):
76 super(PossibleAndroidBrowser
, self
).__init
__(
77 browser_type
, 'android', backend_settings
.supports_tab_control
)
78 assert browser_type
in FindAllBrowserTypes(finder_options
), (
79 'Please add %s to android_browser_finder.FindAllBrowserTypes' %
81 self
._platform
= android_platform
82 self
._platform
_backend
= (
83 android_platform
._platform
_backend
) # pylint: disable=protected-access
84 self
._backend
_settings
= backend_settings
85 self
._local
_apk
= None
87 if browser_type
== 'exact':
88 if not os
.path
.exists(apk_name
):
89 raise exceptions
.PathMissingError(
90 'Unable to find exact apk %s specified by --browser-executable' %
92 self
._local
_apk
= apk_name
93 elif browser_type
== 'reference':
94 if not os
.path
.exists(apk_name
):
95 raise exceptions
.PathMissingError(
96 'Unable to find reference apk at expected location %s.' % apk_name
)
97 self
._local
_apk
= apk_name
99 assert finder_options
.chrome_root
, (
100 'Must specify Chromium source to use apk_name')
101 chrome_root
= finder_options
.chrome_root
103 for build_path
in util
.GetBuildDirectories(chrome_root
):
104 apk_full_name
= os
.path
.join(build_path
, 'apks', apk_name
)
105 if os
.path
.exists(apk_full_name
):
106 last_changed
= os
.path
.getmtime(apk_full_name
)
107 candidate_apks
.append((last_changed
, apk_full_name
))
110 # Find the candidate .apk with the latest modification time.
111 newest_apk_path
= sorted(candidate_apks
)[-1][1]
112 self
._local
_apk
= newest_apk_path
115 return 'PossibleAndroidBrowser(browser_type=%s)' % self
.browser_type
117 def _InitPlatformIfNeeded(self
):
120 def Create(self
, finder_options
):
121 self
._InitPlatformIfNeeded
()
122 browser_backend
= android_browser_backend
.AndroidBrowserBackend(
123 self
._platform
_backend
,
124 finder_options
.browser_options
, self
._backend
_settings
)
126 return browser
.Browser(
127 browser_backend
, self
._platform
_backend
, self
._credentials
_path
)
129 logging
.exception('Failure while creating Android browser.')
130 original_exception
= sys
.exc_info()
132 browser_backend
.Close()
134 logging
.exception('Secondary failure while closing browser backend.')
136 raise original_exception
[0], original_exception
[1], original_exception
[2]
138 def SupportsOptions(self
, browser_options
):
139 if len(browser_options
.extensions_to_load
) != 0:
143 def HaveLocalAPK(self
):
144 return self
._local
_apk
and os
.path
.exists(self
._local
_apk
)
147 def UpdateExecutableIfNeeded(self
):
148 if self
.HaveLocalAPK():
149 logging
.warn('Installing %s on device if needed.' % self
._local
_apk
)
150 self
.platform
.InstallApplication(self
._local
_apk
)
152 def last_modification_time(self
):
153 if self
.HaveLocalAPK():
154 return os
.path
.getmtime(self
._local
_apk
)
158 def SelectDefaultBrowser(possible_browsers
):
159 """Return the newest possible browser."""
160 if not possible_browsers
:
162 return max(possible_browsers
, key
=lambda b
: b
.last_modification_time())
165 def CanFindAvailableBrowsers():
166 return android_device
.CanDiscoverDevices()
169 def CanPossiblyHandlePath(target_path
):
170 return os
.path
.splitext(target_path
.lower())[1] == '.apk'
173 def FindAllBrowserTypes(options
):
175 return CHROME_PACKAGE_NAMES
.keys() + ['exact', 'reference']
178 def _FindAllPossibleBrowsers(finder_options
, android_platform
):
179 """Testable version of FindAllAvailableBrowsers."""
180 if not android_platform
:
182 possible_browsers
= []
184 # Add the exact APK if given.
185 if (finder_options
.browser_executable
and
186 CanPossiblyHandlePath(finder_options
.browser_executable
)):
187 apk_name
= os
.path
.basename(finder_options
.browser_executable
)
188 normalized_path
= os
.path
.expanduser(finder_options
.browser_executable
)
189 exact_package
= apk_helper
.GetPackageName(normalized_path
)
191 (info
for info
in CHROME_PACKAGE_NAMES
.itervalues()
192 if info
[0] == exact_package
or info
[2] == apk_name
), None)
194 # It is okay if the APK name or package doesn't match any of known chrome
195 # browser APKs, since it may be of a different browser.
197 if not exact_package
:
198 raise exceptions
.PackageDetectionError(
199 'Unable to find package for %s specified by --browser-executable' %
202 [package
, backend_settings
, _
] = package_info
203 if package
== exact_package
:
204 possible_browsers
.append(PossibleAndroidBrowser(
208 backend_settings(package
),
211 raise exceptions
.UnknownPackageError(
212 '%s specified by --browser-executable has an unknown package: %s' %
213 (normalized_path
, exact_package
))
215 # Add the reference build if found.
216 os_version
= dependency_util
.GetChromeApkOsVersion(
217 android_platform
.GetOSVersionName())
218 arch
= android_platform
.GetArchName()
220 reference_build
= binary_manager
.FetchPath(
221 'chrome_stable', arch
, 'android', os_version
)
222 except (binary_manager
.NoPathFoundError
,
223 binary_manager
.CloudStorageError
):
224 reference_build
= None
226 if reference_build
and os
.path
.exists(reference_build
):
227 # TODO(aiolos): how do we stably map the android chrome_stable apk to the
228 # correct package name?
229 package
, backend_settings
, _
= CHROME_PACKAGE_NAMES
['android-chrome']
230 possible_browsers
.append(PossibleAndroidBrowser(
234 backend_settings(package
),
237 # Add any known local versions.
238 for name
, package_info
in CHROME_PACKAGE_NAMES
.iteritems():
239 package
, backend_settings
, apk_name
= package_info
240 if apk_name
and not finder_options
.chrome_root
:
242 b
= PossibleAndroidBrowser(name
,
245 backend_settings(package
),
247 if b
.platform
.CanLaunchApplication(package
) or b
.HaveLocalAPK():
248 possible_browsers
.append(b
)
249 return possible_browsers
252 def FindAllAvailableBrowsers(finder_options
, device
):
253 """Finds all the possible browsers on one device.
255 The device is either the only device on the host platform,
256 or |finder_options| specifies a particular device.
258 if not isinstance(device
, android_device
.AndroidDevice
):
262 android_platform
= platform
.GetPlatformForDevice(device
, finder_options
)
263 return _FindAllPossibleBrowsers(finder_options
, android_platform
)
264 except base_error
.BaseError
as e
:
265 logging
.error('Unable to find browsers on %s: %s', device
.device_id
, str(e
))
266 ps_output
= subprocess
.check_output(['ps', '-ef'])
267 logging
.error('Ongoing processes:\n%s', ps_output
)