Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Tools / Scripts / webkitpy / layout_tests / port / android.py
1 # Copyright (C) 2012 Google Inc. All rights reserved.
2 #
3 # Redistribution and use in source and binary forms, with or without
4 # modification, are permitted provided that the following conditions are
5 # met:
6 #
7 #     * Redistributions of source code must retain the above copyright
8 # notice, this list of conditions and the following disclaimer.
9 #     * Redistributions in binary form must reproduce the above
10 # copyright notice, this list of conditions and the following disclaimer
11 # in the documentation and/or other materials provided with the
12 # distribution.
13 #     * Neither the name of Google Inc. nor the names of its
14 # contributors may be used to endorse or promote products derived from
15 # this software without specific prior written permission.
16 #
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29 import copy
30 import logging
31 import os
32 import re
33 import signal
34 import sys
35 import subprocess
36 import threading
37 import time
38
39 from multiprocessing.pool import ThreadPool
40
41 from webkitpy.common.system.executive import ScriptError
42 from webkitpy.layout_tests.breakpad.dump_reader_multipart import DumpReaderAndroid
43 from webkitpy.layout_tests.models import test_run_results
44 from webkitpy.layout_tests.port import base
45 from webkitpy.layout_tests.port import linux
46 from webkitpy.layout_tests.port import driver
47 from webkitpy.layout_tests.port import factory
48 from webkitpy.layout_tests.port import server_process
49 from webkitpy.common.system.profiler import SingleFileOutputProfiler
50
51 _log = logging.getLogger(__name__)
52
53 # The root directory for test resources, which has the same structure as the
54 # source root directory of Chromium.
55 # This path is defined in Chromium's base/test/test_support_android.cc.
56 DEVICE_SOURCE_ROOT_DIR = '/data/local/tmp/'
57
58 # The layout tests directory on device, which has two usages:
59 # 1. as a virtual path in file urls that will be bridged to HTTP.
60 # 2. pointing to some files that are pushed to the device for tests that
61 # don't work on file-over-http (e.g. blob protocol tests).
62 DEVICE_WEBKIT_BASE_DIR = DEVICE_SOURCE_ROOT_DIR + 'third_party/WebKit/'
63 DEVICE_LAYOUT_TESTS_DIR = DEVICE_WEBKIT_BASE_DIR + 'LayoutTests/'
64
65 SCALING_GOVERNORS_PATTERN = "/sys/devices/system/cpu/cpu*/cpufreq/scaling_governor"
66 KPTR_RESTRICT_PATH = "/proc/sys/kernel/kptr_restrict"
67
68 # All the test cases are still served to the test runner through file protocol,
69 # but we use a file-to-http feature to bridge the file request to host's http
70 # server to get the real test files and corresponding resources.
71 # See webkit/support/platform_support_android.cc for the other side of this bridge.
72 PERF_TEST_PATH_PREFIX = '/all-perf-tests'
73 LAYOUT_TEST_PATH_PREFIX = '/all-tests'
74
75 # All ports the Android forwarder to forward.
76 # 8000, 8080 and 8443 are for http/https tests.
77 # 8880 and 9323 are for websocket tests
78 # (see http_server.py, apache_http_server.py and websocket_server.py).
79 FORWARD_PORTS = '8000 8080 8443 8880 9323'
80
81 MS_TRUETYPE_FONTS_DIR = '/usr/share/fonts/truetype/msttcorefonts/'
82 MS_TRUETYPE_FONTS_PACKAGE = 'ttf-mscorefonts-installer'
83
84 # Timeout in seconds to wait for starting/stopping the driver.
85 DRIVER_START_STOP_TIMEOUT_SECS = 10
86
87 HOST_FONT_FILES = [
88     [[MS_TRUETYPE_FONTS_DIR], 'Arial.ttf', MS_TRUETYPE_FONTS_PACKAGE],
89     [[MS_TRUETYPE_FONTS_DIR], 'Arial_Bold.ttf', MS_TRUETYPE_FONTS_PACKAGE],
90     [[MS_TRUETYPE_FONTS_DIR], 'Arial_Bold_Italic.ttf', MS_TRUETYPE_FONTS_PACKAGE],
91     [[MS_TRUETYPE_FONTS_DIR], 'Arial_Italic.ttf', MS_TRUETYPE_FONTS_PACKAGE],
92     [[MS_TRUETYPE_FONTS_DIR], 'Comic_Sans_MS.ttf', MS_TRUETYPE_FONTS_PACKAGE],
93     [[MS_TRUETYPE_FONTS_DIR], 'Comic_Sans_MS_Bold.ttf', MS_TRUETYPE_FONTS_PACKAGE],
94     [[MS_TRUETYPE_FONTS_DIR], 'Courier_New.ttf', MS_TRUETYPE_FONTS_PACKAGE],
95     [[MS_TRUETYPE_FONTS_DIR], 'Courier_New_Bold.ttf', MS_TRUETYPE_FONTS_PACKAGE],
96     [[MS_TRUETYPE_FONTS_DIR], 'Courier_New_Bold_Italic.ttf', MS_TRUETYPE_FONTS_PACKAGE],
97     [[MS_TRUETYPE_FONTS_DIR], 'Courier_New_Italic.ttf', MS_TRUETYPE_FONTS_PACKAGE],
98     [[MS_TRUETYPE_FONTS_DIR], 'Georgia.ttf', MS_TRUETYPE_FONTS_PACKAGE],
99     [[MS_TRUETYPE_FONTS_DIR], 'Georgia_Bold.ttf', MS_TRUETYPE_FONTS_PACKAGE],
100     [[MS_TRUETYPE_FONTS_DIR], 'Georgia_Bold_Italic.ttf', MS_TRUETYPE_FONTS_PACKAGE],
101     [[MS_TRUETYPE_FONTS_DIR], 'Georgia_Italic.ttf', MS_TRUETYPE_FONTS_PACKAGE],
102     [[MS_TRUETYPE_FONTS_DIR], 'Impact.ttf', MS_TRUETYPE_FONTS_PACKAGE],
103     [[MS_TRUETYPE_FONTS_DIR], 'Trebuchet_MS.ttf', MS_TRUETYPE_FONTS_PACKAGE],
104     [[MS_TRUETYPE_FONTS_DIR], 'Trebuchet_MS_Bold.ttf', MS_TRUETYPE_FONTS_PACKAGE],
105     [[MS_TRUETYPE_FONTS_DIR], 'Trebuchet_MS_Bold_Italic.ttf', MS_TRUETYPE_FONTS_PACKAGE],
106     [[MS_TRUETYPE_FONTS_DIR], 'Trebuchet_MS_Italic.ttf', MS_TRUETYPE_FONTS_PACKAGE],
107     [[MS_TRUETYPE_FONTS_DIR], 'Times_New_Roman.ttf', MS_TRUETYPE_FONTS_PACKAGE],
108     [[MS_TRUETYPE_FONTS_DIR], 'Times_New_Roman_Bold.ttf', MS_TRUETYPE_FONTS_PACKAGE],
109     [[MS_TRUETYPE_FONTS_DIR], 'Times_New_Roman_Bold_Italic.ttf', MS_TRUETYPE_FONTS_PACKAGE],
110     [[MS_TRUETYPE_FONTS_DIR], 'Times_New_Roman_Italic.ttf', MS_TRUETYPE_FONTS_PACKAGE],
111     [[MS_TRUETYPE_FONTS_DIR], 'Verdana.ttf', MS_TRUETYPE_FONTS_PACKAGE],
112     [[MS_TRUETYPE_FONTS_DIR], 'Verdana_Bold.ttf', MS_TRUETYPE_FONTS_PACKAGE],
113     [[MS_TRUETYPE_FONTS_DIR], 'Verdana_Bold_Italic.ttf', MS_TRUETYPE_FONTS_PACKAGE],
114     [[MS_TRUETYPE_FONTS_DIR], 'Verdana_Italic.ttf', MS_TRUETYPE_FONTS_PACKAGE],
115     # The Microsoft font EULA
116     [['/usr/share/doc/ttf-mscorefonts-installer/'], 'READ_ME!.gz', MS_TRUETYPE_FONTS_PACKAGE],
117     # Other fonts: Arabic, CJK, Indic, Thai, etc.
118     [['/usr/share/fonts/truetype/ttf-dejavu/'], 'DejaVuSans.ttf', 'ttf-dejavu'],
119     [['/usr/share/fonts/truetype/kochi/'], 'kochi-mincho.ttf', 'ttf-kochi-mincho'],
120     [['/usr/share/fonts/truetype/ttf-indic-fonts-core/'], 'lohit_hi.ttf', 'ttf-indic-fonts-core'],
121     [['/usr/share/fonts/truetype/ttf-indic-fonts-core/'], 'lohit_ta.ttf', 'ttf-indic-fonts-core'],
122     [['/usr/share/fonts/truetype/ttf-indic-fonts-core/'], 'MuktiNarrow.ttf', 'ttf-indic-fonts-core'],
123     [['/usr/share/fonts/truetype/thai/', '/usr/share/fonts/truetype/tlwg/'], 'Garuda.ttf', 'fonts-tlwg-garuda'],
124     [['/usr/share/fonts/truetype/ttf-indic-fonts-core/', '/usr/share/fonts/truetype/ttf-punjabi-fonts/'], 'lohit_pa.ttf', 'ttf-indic-fonts-core'],
125 ]
126
127 # Test resources that need to be accessed as files directly.
128 # Each item can be the relative path of a directory or a file.
129 TEST_RESOURCES_TO_PUSH = [
130     # Blob tests need to access files directly.
131     'editing/pasteboard/resources',
132     'fast/files/resources',
133     'http/tests/local/resources',
134     'http/tests/local/formdata/resources',
135     # User style URLs are accessed as local files in webkit_support.
136     'http/tests/security/resources/cssStyle.css',
137     # Media tests need to access audio/video as files.
138     'media/content',
139     'compositing/resources/video.mp4',
140 ]
141
142 MD5SUM_DEVICE_FILE_NAME = 'md5sum_bin'
143 MD5SUM_HOST_FILE_NAME = 'md5sum_bin_host'
144 MD5SUM_DEVICE_PATH = '/data/local/tmp/' + MD5SUM_DEVICE_FILE_NAME
145
146
147 # Information required when running layout tests using content_shell as the test runner.
148 class ContentShellDriverDetails():
149     def device_cache_directory(self):
150         return self.device_directory() + 'cache/'
151
152     def device_fonts_directory(self):
153         return self.device_directory() + 'fonts/'
154
155     def device_forwarder_path(self):
156         return self.device_directory() + 'forwarder'
157
158     def device_fifo_directory(self):
159         return '/data/data/' + self.package_name() + '/files/'
160
161     def apk_name(self):
162         return 'apks/ContentShell.apk'
163
164     def package_name(self):
165         return 'org.chromium.content_shell_apk'
166
167     def activity_name(self):
168         return self.package_name() + '/.ContentShellActivity'
169
170     def library_name(self):
171         return 'libcontent_shell_content_view.so'
172
173     def additional_resources(self):
174         return ['content_resources.pak', 'shell_resources.pak']
175
176     def command_line_file(self):
177         return '/data/local/tmp/content-shell-command-line'
178
179     def device_crash_dumps_directory(self):
180         return '/data/local/tmp/content-shell-crash-dumps'
181
182     def additional_command_line_flags(self, use_breakpad):
183         flags = ['--dump-render-tree', '--encode-binary']
184         if use_breakpad:
185             flags.extend(['--enable-crash-reporter', '--crash-dumps-dir=%s' % self.device_crash_dumps_directory()])
186         return flags
187
188     def device_directory(self):
189         return DEVICE_SOURCE_ROOT_DIR + 'content_shell/'
190
191
192 # The AndroidCommands class encapsulates commands to communicate with an attached device.
193 class AndroidCommands(object):
194     _adb_command_path = None
195     _adb_command_path_options = []
196
197     def __init__(self, executive, device_serial, debug_logging):
198         self._executive = executive
199         self._device_serial = device_serial
200         self._debug_logging = debug_logging
201
202     # Local public methods.
203
204     def file_exists(self, full_path):
205         assert full_path.startswith('/')
206         return self.run(['shell', 'ls', '-d', full_path]).strip() == full_path
207
208     def push(self, host_path, device_path, ignore_error=False):
209         return self.run(['push', host_path, device_path], ignore_error=ignore_error)
210
211     def pull(self, device_path, host_path, ignore_error=False):
212         return self.run(['pull', device_path, host_path], ignore_error=ignore_error)
213
214     def mkdir(self, device_path, chmod=None):
215         self.run(['shell', 'mkdir', '-p', device_path])
216         if chmod:
217             self.run(['shell', 'chmod', chmod, device_path])
218
219     def restart_adb(self):
220         pids = self.extract_pids('adbd')
221         if pids:
222             output = self.run(['shell', 'kill', '-' + str(signal.SIGTERM)] + pids)
223         self.run(['wait-for-device'])
224
225     def restart_as_root(self):
226         output = self.run(['root'])
227         if 'adbd is already running as root' in output:
228             return
229
230         elif not 'restarting adbd as root' in output:
231             self._log_error('Unrecognized output from adb root: %s' % output)
232
233         self.run(['wait-for-device'])
234
235     def extract_pids(self, process_name):
236         pids = []
237         output = self.run(['shell', 'ps'])
238         for line in output.splitlines():
239             data = line.split()
240             try:
241                 if process_name in data[-1]:  # name is in the last column
242                     if process_name == data[-1]:
243                         pids.insert(0, data[1])  # PID is in the second column
244                     else:
245                         pids.append(data[1])
246             except IndexError:
247                 pass
248         return pids
249
250     def run(self, command, ignore_error=False):
251         self._log_debug('Run adb command: ' + str(command))
252         if ignore_error:
253             error_handler = self._executive.ignore_error
254         else:
255             error_handler = None
256
257         result = self._executive.run_command(self.adb_command() + command, error_handler=error_handler, debug_logging=self._debug_logging)
258
259         # We limit the length to avoid outputting too verbose commands, such as "adb logcat".
260         self._log_debug('Run adb result: ' + result[:80])
261         return result
262
263     def get_serial(self):
264         return self._device_serial
265
266     def adb_command(self):
267         return [AndroidCommands.adb_command_path(self._executive, self._debug_logging), '-s', self._device_serial]
268
269     @staticmethod
270     def set_adb_command_path_options(paths):
271         AndroidCommands._adb_command_path_options = paths
272
273     @staticmethod
274     def adb_command_path(executive, debug_logging):
275         if AndroidCommands._adb_command_path:
276             return AndroidCommands._adb_command_path
277
278         assert AndroidCommands._adb_command_path_options, 'No commands paths have been set to look for the "adb" command.'
279
280         command_path = None
281         command_version = None
282         for path_option in AndroidCommands._adb_command_path_options:
283             path_version = AndroidCommands._determine_adb_version(path_option, executive, debug_logging)
284             if not path_version:
285                 continue
286             if command_version != None and path_version < command_version:
287                 continue
288
289             command_path = path_option
290             command_version = path_version
291
292         assert command_path, 'Unable to locate the "adb" command. Are you using an Android checkout of Chromium?'
293
294         AndroidCommands._adb_command_path = command_path
295         return command_path
296
297     # Local private methods.
298
299     def _log_error(self, message):
300         _log.error('[%s] %s' % (self._device_serial, message))
301
302     def _log_info(self, message):
303         _log.info('[%s] %s' % (self._device_serial, message))
304
305     def _log_debug(self, message):
306         if self._debug_logging:
307             _log.debug('[%s] %s' % (self._device_serial, message))
308
309     @staticmethod
310     def _determine_adb_version(adb_command_path, executive, debug_logging):
311         re_version = re.compile('^.*version ([\d\.]+)$')
312         try:
313             output = executive.run_command([adb_command_path, 'version'], error_handler=executive.ignore_error,
314                                            debug_logging=debug_logging)
315         except OSError:
316             return None
317
318         result = re_version.match(output)
319         if not output or not result:
320             return None
321
322         return [int(n) for n in result.group(1).split('.')]
323
324
325 # A class to encapsulate device status and information, such as the AndroidCommands
326 # instances and whether the device has been set up.
327 class AndroidDevices(object):
328     # Percentage of battery a device needs to have in order for it to be considered
329     # to participate in running the layout tests.
330     MINIMUM_BATTERY_PERCENTAGE = 30
331
332     def __init__(self, executive, default_device=None, debug_logging=False):
333         self._usable_devices = []
334         self._default_device = default_device
335         self._prepared_devices = []
336         self._debug_logging = debug_logging
337
338     def prepared_devices(self):
339         return self._prepared_devices
340
341     def usable_devices(self, executive):
342         if self._usable_devices:
343             return self._usable_devices
344
345         if self._default_device:
346             self._usable_devices = [AndroidCommands(executive, self._default_device, self._debug_logging)]
347             return self._usable_devices
348
349         # Example "adb devices" command output:
350         #   List of devices attached
351         #   0123456789ABCDEF        device
352         re_device = re.compile('^([a-zA-Z0-9_:.-]+)\tdevice$', re.MULTILINE)
353
354         result = executive.run_command([AndroidCommands.adb_command_path(executive, debug_logging=self._debug_logging), 'devices'],
355                                        error_handler=executive.ignore_error, debug_logging=self._debug_logging)
356         devices = re_device.findall(result)
357         if not devices:
358             return []
359
360         for device_serial in sorted(devices):
361             commands = AndroidCommands(executive, device_serial, self._debug_logging)
362             if self._battery_level_for_device(commands) < AndroidDevices.MINIMUM_BATTERY_PERCENTAGE:
363                 _log.warning('Device with serial "%s" skipped because it has less than %d percent battery.'
364                     % (commands.get_serial(), AndroidDevices.MINIMUM_BATTERY_PERCENTAGE))
365                 continue
366
367             if not self._is_device_screen_on(commands):
368                 _log.warning('Device with serial "%s" skipped because the screen must be on.' % commands.get_serial())
369                 continue
370
371             self._usable_devices.append(commands)
372
373         return self._usable_devices
374
375     def get_device(self, executive, device_index):
376         devices = self.usable_devices(executive)
377         if device_index >= len(devices):
378             raise AssertionError('Device index exceeds number of usable devices.')
379
380         return devices[device_index]
381
382     def is_device_prepared(self, device_serial):
383         return device_serial in self._prepared_devices
384
385     def set_device_prepared(self, device_serial):
386         self._prepared_devices.append(device_serial)
387
388     # Private methods
389     def _battery_level_for_device(self, commands):
390         battery_status = commands.run(['shell', 'dumpsys', 'battery'])
391         if 'Error' in battery_status or "Can't find service: battery" in battery_status:
392             _log.warning('Unable to read the battery level from device with serial "%s".' % commands.get_serial())
393             return 0
394
395         return int(re.findall('level: (\d+)', battery_status)[0])
396
397     def _is_device_screen_on(self, commands):
398         power_status = commands.run(['shell', 'dumpsys', 'power'])
399         return 'mScreenOn=true' in power_status or 'mScreenOn=SCREEN_ON_BIT' in power_status
400
401
402 class AndroidPort(base.Port):
403     port_name = 'android'
404
405     # Avoid initializing the adb path [worker count]+1 times by storing it as a static member.
406     _adb_path = None
407
408     SUPPORTED_VERSIONS = ('android')
409
410     FALLBACK_PATHS = {'icecreamsandwich': ['android'] + linux.LinuxPort.latest_platform_fallback_path()}
411
412     # Android has aac and mp3 codecs built in.
413     PORT_HAS_AUDIO_CODECS_BUILT_IN = True
414
415     def __init__(self, host, port_name, **kwargs):
416         super(AndroidPort, self).__init__(host, port_name, **kwargs)
417
418         self._operating_system = 'android'
419         self._version = 'icecreamsandwich'
420
421         self._host_port = factory.PortFactory(host).get('chromium', **kwargs)
422         self._server_process_constructor = self._android_server_process_constructor
423
424         if not self.get_option('disable_breakpad'):
425             self._dump_reader = DumpReaderAndroid(host, self._build_path())
426
427         if self.driver_name() != self.CONTENT_SHELL_NAME:
428             raise AssertionError('Layout tests on Android only support content_shell as the driver.')
429
430         self._driver_details = ContentShellDriverDetails()
431
432         # Initialize the AndroidDevices class which tracks available devices.
433         default_device = None
434         if hasattr(self._options, 'adb_device') and len(self._options.adb_device):
435             default_device = self._options.adb_device
436
437         self._debug_logging = self.get_option('android_logging')
438         self._devices = AndroidDevices(self._executive, default_device, self._debug_logging)
439
440         # Tell AndroidCommands where to search for the "adb" command.
441         AndroidCommands.set_adb_command_path_options(['adb',
442             self.path_from_chromium_base('third_party', 'android_tools', 'sdk', 'platform-tools', 'adb')])
443
444         prepared_devices = self.get_option('prepared_devices', [])
445         for serial in prepared_devices:
446             self._devices.set_device_prepared(serial)
447
448     def default_smoke_test_only(self):
449         return True
450
451     # Local public methods.
452     def path_to_forwarder(self):
453         return self._build_path('forwarder')
454
455     def path_to_md5sum(self):
456         return self._build_path(MD5SUM_DEVICE_FILE_NAME)
457
458     def path_to_md5sum_host(self):
459         return self._build_path(MD5SUM_HOST_FILE_NAME)
460
461     def additional_drt_flag(self):
462         return self._driver_details.additional_command_line_flags(use_breakpad=not self.get_option('disable_breakpad'))
463
464     def default_timeout_ms(self):
465         # Android platform has less computing power than desktop platforms.
466         # Using 10 seconds allows us to pass most slow tests which are not
467         # marked as slow tests on desktop platforms.
468         return 10 * 1000
469
470     def driver_stop_timeout(self):
471         # The driver doesn't respond to closing stdin, so we might as well stop the driver immediately.
472         return 0.0
473
474     def default_child_processes(self):
475         usable_devices = self._devices.usable_devices(self._executive)
476         if not usable_devices:
477             raise test_run_results.TestRunException(test_run_results.NO_DEVICES_EXIT_STATUS, "Unable to find any attached Android devices.")
478         return len(usable_devices)
479
480     def check_wdiff(self, logging=True):
481         return self._host_port.check_wdiff(logging)
482
483     def check_build(self, needs_http, printer):
484         exit_status = super(AndroidPort, self).check_build(needs_http, printer)
485         if exit_status:
486             return exit_status
487
488         result = self._check_file_exists(self.path_to_md5sum(), 'md5sum utility')
489         result = self._check_file_exists(self.path_to_md5sum_host(), 'md5sum host utility') and result
490         result = self._check_file_exists(self.path_to_forwarder(), 'forwarder utility') and result
491
492         if not result:
493             # There is a race condition in adb at least <= 4.3 on Linux that causes it to go offline periodically
494             # We set the processor affinity for any running adb process to attempt to work around this.
495             # See crbug.com/268450
496             if self.host.platform.is_linux():
497                 pids = self._executive.running_pids(lambda name: 'adb' in name)
498                 if not pids:
499                     # Apparently adb is not running, which is unusual. Running any adb command should start it.
500                     self._executive.run_command(['adb', 'devices'])
501                     pids = self._executive.running_pids(lambda name: 'adb' in name)
502                 if not pids:
503                     _log.error("The adb daemon does not appear to be running.")
504                     return False
505
506                 for pid in pids:
507                     self._executive.run_command(['taskset', '-p', '-c', '0', str(pid)])
508
509         if not result:
510             _log.error('For complete Android build requirements, please see:')
511             _log.error('')
512             _log.error('    http://code.google.com/p/chromium/wiki/AndroidBuildInstructions')
513             return test_run_results.UNEXPECTED_ERROR_EXIT_STATUS
514
515         return self._check_devices(printer)
516
517     def _check_devices(self, printer):
518         # Printer objects aren't threadsafe, so we need to protect calls to them.
519         lock = threading.Lock()
520         pool = None
521
522         # Push the executables and other files to the devices; doing this now
523         # means we can do this in parallel in the manager process and not mix
524         # this in with starting and stopping workers.
525         def setup_device(worker_number):
526             d = self.create_driver(worker_number)
527             serial = d._android_commands.get_serial()
528
529             def log_safely(msg, throttled=True):
530                 if throttled:
531                     callback = printer.write_throttled_update
532                 else:
533                     callback = printer.write_update
534                 lock.acquire()
535                 try:
536                     callback("[%s] %s" % (serial, msg))
537                 finally:
538                     lock.release()
539
540             log_safely("preparing device", throttled=False)
541             try:
542                 d._setup_test(log_safely)
543                 log_safely("device prepared", throttled=False)
544             except (ScriptError, driver.DeviceFailure) as e:
545                 lock.acquire()
546                 _log.warning("[%s] failed to prepare_device: %s" % (serial, str(e)))
547                 lock.release()
548             except KeyboardInterrupt:
549                 if pool:
550                     pool.terminate()
551
552         # FIXME: It would be nice if we knew how many workers we needed.
553         num_workers = self.default_child_processes()
554         num_child_processes = int(self.get_option('child_processes'))
555         if num_child_processes:
556             num_workers = min(num_workers, num_child_processes)
557         if num_workers > 1:
558             pool = ThreadPool(num_workers)
559             try:
560                 pool.map(setup_device, range(num_workers))
561             except KeyboardInterrupt:
562                 pool.terminate()
563                 raise
564         else:
565             setup_device(0)
566
567         if not self._devices.prepared_devices():
568             _log.error('Could not prepare any devices for testing.')
569             return test_run_results.NO_DEVICES_EXIT_STATUS
570         return test_run_results.OK_EXIT_STATUS
571
572     def setup_test_run(self):
573         super(AndroidPort, self).setup_test_run()
574
575         # By setting this on the options object, we can propagate the list
576         # of prepared devices to the workers (it is read in __init__()).
577         if self._devices._prepared_devices:
578             self._options.prepared_devices = self._devices.prepared_devices()
579         else:
580             # We were called with --no-build, so assume the devices are up to date.
581             self._options.prepared_devices = [d.get_serial() for d in self._devices.usable_devices(self.host.executive)]
582
583     def num_workers(self, requested_num_workers):
584         return min(len(self._options.prepared_devices), requested_num_workers)
585
586     def check_sys_deps(self, needs_http):
587         for (font_dirs, font_file, package) in HOST_FONT_FILES:
588             exists = False
589             for font_dir in font_dirs:
590                 font_path = font_dir + font_file
591                 if self._check_file_exists(font_path, '', logging=False):
592                     exists = True
593                     break
594             if not exists:
595                 _log.error('You are missing %s under %s. Try installing %s. See build instructions.' % (font_file, font_dirs, package))
596                 return test_run_results.SYS_DEPS_EXIT_STATUS
597         return test_run_results.OK_EXIT_STATUS
598
599     def requires_http_server(self):
600         """Chromium Android runs tests on devices, and uses the HTTP server to
601         serve the actual layout tests to the test driver."""
602         return True
603
604     def start_http_server(self, additional_dirs=None, number_of_servers=0):
605         if not additional_dirs:
606             additional_dirs = {}
607         additional_dirs[PERF_TEST_PATH_PREFIX] = self.perf_tests_dir()
608         additional_dirs[LAYOUT_TEST_PATH_PREFIX] = self.layout_tests_dir()
609         super(AndroidPort, self).start_http_server(additional_dirs, number_of_servers)
610
611     def create_driver(self, worker_number, no_timeout=False):
612         return ChromiumAndroidDriver(self, worker_number, pixel_tests=self.get_option('pixel_tests'),
613                                      driver_details=self._driver_details,
614                                      android_devices=self._devices,
615                                      # Force no timeout to avoid test driver timeouts before NRWT.
616                                      no_timeout=True)
617
618     def driver_cmd_line(self):
619         # Override to return the actual test driver's command line.
620         return self.create_driver(0)._android_driver_cmd_line(self.get_option('pixel_tests'), [])
621
622     def clobber_old_port_specific_results(self):
623         if not self.get_option('disable_breakpad'):
624             self._dump_reader.clobber_old_results()
625
626     # Overridden protected methods.
627
628     def _build_path(self, *comps):
629         return self._host_port._build_path(*comps)
630
631     def _build_path_with_configuration(self, configuration, *comps):
632         return self._host_port._build_path_with_configuration(configuration, *comps)
633
634     def path_to_apache(self):
635         return self._host_port.path_to_apache()
636
637     def path_to_apache_config_file(self):
638         return self._host_port.path_to_apache_config_file()
639
640     def _path_to_driver(self, configuration=None):
641         return self._build_path_with_configuration(configuration, self._driver_details.apk_name())
642
643     def _path_to_helper(self):
644         return None
645
646     def _path_to_image_diff(self):
647         return self._host_port._path_to_image_diff()
648
649     def path_to_lighttpd(self):
650         return self._host_port._path_to_lighttpd()
651
652     def path_to_lighttpd_modules(self):
653         return self._host_port._path_to_lighttpd_modules()
654
655     def path_to_lighttpd_php(self):
656         return self._host_port._path_to_lighttpd_php()
657
658     def _path_to_wdiff(self):
659         return self._host_port._path_to_wdiff()
660
661     def _shut_down_http_server(self, pid):
662         return self._host_port._shut_down_http_server(pid)
663
664     def _driver_class(self):
665         return ChromiumAndroidDriver
666
667     # Local private methods.
668
669     @staticmethod
670     def _android_server_process_constructor(port, server_name, cmd_line, env=None, logging=False):
671         return server_process.ServerProcess(port, server_name, cmd_line, env,
672                                             universal_newlines=True, treat_no_data_as_crash=True, logging=logging)
673
674
675 class AndroidPerf(SingleFileOutputProfiler):
676     _cached_perf_host_path = None
677     _have_searched_for_perf_host = False
678
679     def __init__(self, host, executable_path, output_dir, android_commands, symfs_path, kallsyms_path, identifier=None):
680         super(AndroidPerf, self).__init__(host, executable_path, output_dir, "data", identifier)
681         self._android_commands = android_commands
682         self._perf_process = None
683         self._symfs_path = symfs_path
684         self._kallsyms_path = kallsyms_path
685
686     def check_configuration(self):
687         # Check that perf is installed
688         if not self._android_commands.file_exists('/system/bin/perf'):
689             print "Cannot find /system/bin/perf on device %s" % self._android_commands.get_serial()
690             return False
691
692         # Check that the device is a userdebug build (or at least has the necessary libraries).
693         if self._android_commands.run(['shell', 'getprop', 'ro.build.type']).strip() != 'userdebug':
694             print "Device %s is not flashed with a userdebug build of Android" % self._android_commands.get_serial()
695             return False
696
697         # FIXME: Check that the binary actually is perf-able (has stackframe pointers)?
698         # objdump -s a function and make sure it modifies the fp?
699         # Instruct users to rebuild after export GYP_DEFINES="profiling=1 $GYP_DEFINES"
700         return True
701
702     def print_setup_instructions(self):
703         print """
704 perf on android requires a 'userdebug' build of Android, see:
705 http://source.android.com/source/building-devices.html"
706
707 The perf command can be built from:
708 https://android.googlesource.com/platform/external/linux-tools-perf/
709 and requires libefl, libebl, libdw, and libdwfl available in:
710 https://android.googlesource.com/platform/external/elfutils/
711
712 The test driver must be built with profiling=1, make sure you've done:
713 export GYP_DEFINES="profiling=1 $GYP_DEFINES"
714 update-webkit --chromium-android
715 build-webkit --chromium-android
716
717 Googlers should read:
718 http://goto.google.com/cr-android-perf-howto
719 """
720
721     def attach_to_pid(self, pid):
722         assert(pid)
723         assert(self._perf_process == None)
724         # FIXME: This can't be a fixed timeout!
725         cmd = self._android_commands.adb_command() + ['shell', 'perf', 'record', '-g', '-p', pid, 'sleep', 30]
726         self._perf_process = self._host.executive.popen(cmd)
727
728     def _perf_version_string(self, perf_path):
729         try:
730             return self._host.executive.run_command([perf_path, '--version'])
731         except:
732             return None
733
734     def _find_perfhost_binary(self):
735         perfhost_version = self._perf_version_string('perfhost_linux')
736         if perfhost_version:
737             return 'perfhost_linux'
738         perf_version = self._perf_version_string('perf')
739         if perf_version:
740             return 'perf'
741         return None
742
743     def _perfhost_path(self):
744         if self._have_searched_for_perf_host:
745             return self._cached_perf_host_path
746         self._have_searched_for_perf_host = True
747         self._cached_perf_host_path = self._find_perfhost_binary()
748         return self._cached_perf_host_path
749
750     def _first_ten_lines_of_profile(self, perf_output):
751         match = re.search("^#[^\n]*\n((?: [^\n]*\n){1,10})", perf_output, re.MULTILINE)
752         return match.group(1) if match else None
753
754     def profile_after_exit(self):
755         perf_exitcode = self._perf_process.wait()
756         if perf_exitcode != 0:
757             print "Perf failed (exit code: %i), can't process results." % perf_exitcode
758             return
759
760         self._android_commands.pull('/data/perf.data', self._output_path)
761
762         perfhost_path = self._perfhost_path()
763         perfhost_report_command = [
764             'report',
765             '--input', self._output_path,
766             '--symfs', self._symfs_path,
767             '--kallsyms', self._kallsyms_path,
768         ]
769         if perfhost_path:
770             perfhost_args = [perfhost_path] + perfhost_report_command + ['--call-graph', 'none']
771             perf_output = self._host.executive.run_command(perfhost_args)
772             # We could save off the full -g report to a file if users found that useful.
773             print self._first_ten_lines_of_profile(perf_output)
774         else:
775             print """
776 Failed to find perfhost_linux binary, can't process samples from the device.
777
778 perfhost_linux can be built from:
779 https://android.googlesource.com/platform/external/linux-tools-perf/
780 also, modern versions of perf (available from apt-get install goobuntu-kernel-tools-common)
781 may also be able to process the perf.data files from the device.
782
783 Googlers should read:
784 http://goto.google.com/cr-android-perf-howto
785 for instructions on installing pre-built copies of perfhost_linux
786 http://crbug.com/165250 discusses making these pre-built binaries externally available.
787 """
788
789         perfhost_display_patch = perfhost_path if perfhost_path else 'perfhost_linux'
790         print "To view the full profile, run:"
791         print ' '.join([perfhost_display_patch] + perfhost_report_command)
792
793
794 class ChromiumAndroidDriver(driver.Driver):
795     def __init__(self, port, worker_number, pixel_tests, driver_details, android_devices, no_timeout=False):
796         super(ChromiumAndroidDriver, self).__init__(port, worker_number, pixel_tests, no_timeout)
797         self._in_fifo_path = driver_details.device_fifo_directory() + 'stdin.fifo'
798         self._out_fifo_path = driver_details.device_fifo_directory() + 'test.fifo'
799         self._err_fifo_path = driver_details.device_fifo_directory() + 'stderr.fifo'
800         self._read_stdout_process = None
801         self._read_stderr_process = None
802         self._forwarder_process = None
803         self._original_governors = {}
804         self._original_kptr_restrict = None
805
806         self._android_devices = android_devices
807         self._android_commands = android_devices.get_device(port._executive, worker_number)
808         self._driver_details = driver_details
809         self._debug_logging = self._port._debug_logging
810         self._created_cmd_line = False
811         self._device_failed = False
812
813         # FIXME: If we taught ProfileFactory about "target" devices we could
814         # just use the logic in Driver instead of duplicating it here.
815         if self._port.get_option("profile"):
816             # FIXME: This should be done once, instead of per-driver!
817             symfs_path = self._find_or_create_symfs()
818             kallsyms_path = self._update_kallsyms_cache(symfs_path)
819             # FIXME: We should pass this some sort of "Bridge" object abstraction around ADB instead of a path/device pair.
820             self._profiler = AndroidPerf(self._port.host, self._port._path_to_driver(), self._port.results_directory(),
821                 self._android_commands, symfs_path, kallsyms_path)
822             # FIXME: This is a layering violation and should be moved to Port.check_sys_deps
823             # once we have an abstraction around an adb_path/device_serial pair to make it
824             # easy to make these class methods on AndroidPerf.
825             if not self._profiler.check_configuration():
826                 self._profiler.print_setup_instructions()
827                 sys.exit(1)
828         else:
829             self._profiler = None
830
831     def __del__(self):
832         self._teardown_performance()
833         self._clean_up_cmd_line()
834         super(ChromiumAndroidDriver, self).__del__()
835
836     def _update_kallsyms_cache(self, output_dir):
837         kallsyms_name = "%s-kallsyms" % self._android_commands.get_serial()
838         kallsyms_cache_path = self._port.host.filesystem.join(output_dir, kallsyms_name)
839
840         self._android_commands.restart_as_root()
841
842         saved_kptr_restrict = self._android_commands.run(['shell', 'cat', KPTR_RESTRICT_PATH]).strip()
843         self._android_commands.run(['shell', 'echo', '0', '>', KPTR_RESTRICT_PATH])
844
845         print "Updating kallsyms file (%s) from device" % kallsyms_cache_path
846         self._android_commands.pull("/proc/kallsyms", kallsyms_cache_path)
847
848         self._android_commands.run(['shell', 'echo', saved_kptr_restrict, '>', KPTR_RESTRICT_PATH])
849
850         return kallsyms_cache_path
851
852     def _find_or_create_symfs(self):
853         environment = self._port.host.copy_current_environment()
854         env = environment.to_dictionary()
855         fs = self._port.host.filesystem
856
857         if 'ANDROID_SYMFS' in env:
858             symfs_path = env['ANDROID_SYMFS']
859         else:
860             symfs_path = fs.join(self._port.results_directory(), 'symfs')
861             print "ANDROID_SYMFS not set, using %s" % symfs_path
862
863         # find the installed path, and the path of the symboled built library
864         # FIXME: We should get the install path from the device!
865         symfs_library_path = fs.join(symfs_path, "data/app-lib/%s-1/%s" % (self._driver_details.package_name(), self._driver_details.library_name()))
866         built_library_path = self._port._build_path('lib', self._driver_details.library_name())
867         assert(fs.exists(built_library_path))
868
869         # FIXME: Ideally we'd check the sha1's first and make a soft-link instead of copying (since we probably never care about windows).
870         print "Updating symfs libary (%s) from built copy (%s)" % (symfs_library_path, built_library_path)
871         fs.maybe_make_directory(fs.dirname(symfs_library_path))
872         fs.copyfile(built_library_path, symfs_library_path)
873
874         return symfs_path
875
876     def _setup_md5sum_and_push_data_if_needed(self, log_callback):
877         self._md5sum_path = self._port.path_to_md5sum()
878         if not self._android_commands.file_exists(MD5SUM_DEVICE_PATH):
879             if not self._android_commands.push(self._md5sum_path, MD5SUM_DEVICE_PATH):
880                 self._abort('Could not push md5sum to device')
881
882         self._push_executable(log_callback)
883         self._push_fonts(log_callback)
884         self._push_test_resources(log_callback)
885
886     def _setup_test(self, log_callback):
887         # FIXME: Move this routine and its subroutines off of the AndroidDriver
888         # class and onto AndroidCommands or some other helper class, so that we
889         # can initialize the device without needing to create a driver.
890
891         if self._android_devices.is_device_prepared(self._android_commands.get_serial()):
892             return
893
894         self._android_commands.restart_adb()
895         self._android_commands.restart_as_root()
896         self._setup_md5sum_and_push_data_if_needed(log_callback)
897         self._setup_performance()
898
899         # Required by webkit_support::GetWebKitRootDirFilePath().
900         # Other directories will be created automatically by adb push.
901         self._android_commands.mkdir(DEVICE_SOURCE_ROOT_DIR + 'chrome')
902
903         # Allow the test driver to get full read and write access to the directory on the device,
904         # as well as for the FIFOs. We'll need a world writable directory.
905         self._android_commands.mkdir(self._driver_details.device_directory(), chmod='777')
906         self._android_commands.mkdir(self._driver_details.device_fifo_directory(), chmod='777')
907
908         # Make sure that the disk cache on the device resets to a clean state.
909         self._android_commands.run(['shell', 'rm', '-r', self._driver_details.device_cache_directory()])
910
911         # Mark this device as having been set up.
912         self._android_devices.set_device_prepared(self._android_commands.get_serial())
913
914     def _log_error(self, message):
915         _log.error('[%s] %s' % (self._android_commands.get_serial(), message))
916
917     def _log_warning(self, message):
918         _log.warning('[%s] %s' % (self._android_commands.get_serial(), message))
919
920     def _log_debug(self, message):
921         if self._debug_logging:
922             _log.debug('[%s] %s' % (self._android_commands.get_serial(), message))
923
924     def _abort(self, message):
925         self._device_failed = True
926         raise driver.DeviceFailure('[%s] %s' % (self._android_commands.get_serial(), message))
927
928     @staticmethod
929     def _extract_hashes_from_md5sum_output(md5sum_output):
930         assert md5sum_output
931         return [line.split('  ')[0] for line in md5sum_output]
932
933     def _files_match(self, host_file, device_file):
934         assert self._port.host.filesystem.exists(host_file)
935         device_hashes = self._extract_hashes_from_md5sum_output(
936                 self._port.host.executive.popen(self._android_commands.adb_command() + ['shell', MD5SUM_DEVICE_PATH, device_file],
937                                                 stdout=subprocess.PIPE).stdout)
938         host_hashes = self._extract_hashes_from_md5sum_output(
939                 self._port.host.executive.popen(args=['%s_host' % self._md5sum_path, host_file],
940                                                 stdout=subprocess.PIPE).stdout)
941         return host_hashes and device_hashes == host_hashes
942
943     def _push_file_if_needed(self, host_file, device_file, log_callback):
944         basename = self._port.host.filesystem.basename(host_file)
945         log_callback("checking %s" % basename)
946         if not self._files_match(host_file, device_file):
947             log_callback("pushing %s" % basename)
948             self._android_commands.push(host_file, device_file)
949
950     def _push_executable(self, log_callback):
951         self._push_file_if_needed(self._port.path_to_forwarder(), self._driver_details.device_forwarder_path(), log_callback)
952         for resource in self._driver_details.additional_resources():
953             self._push_file_if_needed(self._port._build_path(resource), self._driver_details.device_directory() + resource, log_callback)
954
955         self._push_file_if_needed(self._port._build_path('android_main_fonts.xml'), self._driver_details.device_directory() + 'android_main_fonts.xml', log_callback)
956         self._push_file_if_needed(self._port._build_path('android_fallback_fonts.xml'), self._driver_details.device_directory() + 'android_fallback_fonts.xml', log_callback)
957
958         log_callback("checking apk")
959         if self._files_match(self._port._build_path('apks', 'ContentShell.apk'),
960                              '/data/app/org.chromium.content_shell_apk-1.apk'):
961             return
962
963         log_callback("uninstalling apk")
964         self._android_commands.run(['uninstall', self._driver_details.package_name()])
965         driver_host_path = self._port._path_to_driver()
966         log_callback("installing apk")
967         install_result = self._android_commands.run(['install', driver_host_path])
968         if install_result.find('Success') == -1:
969             self._abort('Failed to install %s onto device: %s' % (driver_host_path, install_result))
970
971     def _push_fonts(self, log_callback):
972         path_to_ahem_font = self._port._build_path('AHEM____.TTF')
973         self._push_file_if_needed(path_to_ahem_font, self._driver_details.device_fonts_directory() + 'AHEM____.TTF', log_callback)
974         for (host_dirs, font_file, package) in HOST_FONT_FILES:
975             for host_dir in host_dirs:
976                 host_font_path = host_dir + font_file
977                 if self._port._check_file_exists(host_font_path, '', logging=False):
978                     self._push_file_if_needed(host_font_path, self._driver_details.device_fonts_directory() + font_file, log_callback)
979
980     def _push_test_resources(self, log_callback):
981         for resource in TEST_RESOURCES_TO_PUSH:
982             self._push_file_if_needed(self._port.layout_tests_dir() + '/' + resource, DEVICE_LAYOUT_TESTS_DIR + resource, log_callback)
983
984     def _get_last_stacktrace(self):
985         tombstones = self._android_commands.run(['shell', 'ls', '-n', '/data/tombstones/tombstone_*'])
986         if not tombstones or tombstones.startswith('/data/tombstones/tombstone_*: No such file or directory'):
987             self._log_error('The driver crashed, but no tombstone found!')
988             return ''
989
990         if tombstones.startswith('/data/tombstones/tombstone_*: Permission denied'):
991             # FIXME: crbug.com/321489 ... figure out why this happens.
992             self._log_error('The driver crashed, but we could not read the tombstones!')
993             return ''
994
995         tombstones = tombstones.rstrip().split('\n')
996         last_tombstone = None
997         for tombstone in tombstones:
998             # Format of fields:
999             # 0          1      2      3     4          5     6
1000             # permission uid    gid    size  date       time  filename
1001             # -rw------- 1000   1000   45859 2011-04-13 06:00 tombstone_00
1002             fields = tombstone.split()
1003             if len(fields) != 7:
1004                 self._log_warning("unexpected line in tombstone output, skipping: '%s'" % tombstone)
1005                 continue
1006
1007             if not last_tombstone or fields[4] + fields[5] >= last_tombstone[4] + last_tombstone[5]:
1008                 last_tombstone = fields
1009             else:
1010                 break
1011
1012         if not last_tombstone:
1013             self._log_error('The driver crashed, but we could not find any valid tombstone!')
1014             return ''
1015
1016         # Use Android tool vendor/google/tools/stack to convert the raw
1017         # stack trace into a human readable format, if needed.
1018         # It takes a long time, so don't do it here.
1019         return '%s\n%s' % (' '.join(last_tombstone),
1020                            self._android_commands.run(['shell', 'cat', '/data/tombstones/' + last_tombstone[6]]))
1021
1022     def _get_logcat(self):
1023         return self._android_commands.run(['logcat', '-d', '-v', 'threadtime'])
1024
1025     def _setup_performance(self):
1026         # Disable CPU scaling and drop ram cache to reduce noise in tests
1027         if not self._original_governors:
1028             governor_files = self._android_commands.run(['shell', 'ls', SCALING_GOVERNORS_PATTERN])
1029             if governor_files.find('No such file or directory') == -1:
1030                 for file in governor_files.split():
1031                     self._original_governors[file] = self._android_commands.run(['shell', 'cat', file]).strip()
1032                     self._android_commands.run(['shell', 'echo', 'performance', '>', file])
1033
1034     def _teardown_performance(self):
1035         for file, original_content in self._original_governors.items():
1036             self._android_commands.run(['shell', 'echo', original_content, '>', file])
1037         self._original_governors = {}
1038
1039     def _get_crash_log(self, stdout, stderr, newer_than):
1040         if not stdout:
1041             stdout = ''
1042         stdout += '********* [%s] Logcat:\n%s' % (self._android_commands.get_serial(), self._get_logcat())
1043         if not stderr:
1044             stderr = ''
1045         stderr += '********* [%s] Tombstone file:\n%s' % (self._android_commands.get_serial(), self._get_last_stacktrace())
1046
1047         if not self._port.get_option('disable_breakpad'):
1048             crashes = self._pull_crash_dumps_from_device()
1049             for crash in crashes:
1050                 stderr += '********* [%s] breakpad minidump %s:\n%s' % (self._port.host.filesystem.basename(crash), self._android_commands.get_serial(), self._port._dump_reader._get_stack_from_dump(crash))
1051
1052         return super(ChromiumAndroidDriver, self)._get_crash_log(stdout, stderr, newer_than)
1053
1054     def cmd_line(self, pixel_tests, per_test_args):
1055         # The returned command line is used to start _server_process. In our case, it's an interactive 'adb shell'.
1056         # The command line passed to the driver process is returned by _driver_cmd_line() instead.
1057         return self._android_commands.adb_command() + ['shell']
1058
1059     def _android_driver_cmd_line(self, pixel_tests, per_test_args):
1060         return driver.Driver.cmd_line(self, pixel_tests, per_test_args)
1061
1062     @staticmethod
1063     def _loop_with_timeout(condition, timeout_secs):
1064         deadline = time.time() + timeout_secs
1065         while time.time() < deadline:
1066             if condition():
1067                 return True
1068         return False
1069
1070     def _all_pipes_created(self):
1071         return (self._android_commands.file_exists(self._in_fifo_path) and
1072                 self._android_commands.file_exists(self._out_fifo_path) and
1073                 self._android_commands.file_exists(self._err_fifo_path))
1074
1075     def _remove_all_pipes(self):
1076         for file in [self._in_fifo_path, self._out_fifo_path, self._err_fifo_path]:
1077             self._android_commands.run(['shell', 'rm', file])
1078
1079         return (not self._android_commands.file_exists(self._in_fifo_path) and
1080                 not self._android_commands.file_exists(self._out_fifo_path) and
1081                 not self._android_commands.file_exists(self._err_fifo_path))
1082
1083     def start(self, pixel_tests, per_test_args):
1084         # We override the default start() so that we can call _android_driver_cmd_line()
1085         # instead of cmd_line().
1086         new_cmd_line = self._android_driver_cmd_line(pixel_tests, per_test_args)
1087
1088         # Since _android_driver_cmd_line() is different than cmd_line() we need to provide
1089         # our own mechanism for detecting when the process should be stopped.
1090         if self._current_cmd_line is None:
1091             self._current_android_cmd_line = None
1092         if new_cmd_line != self._current_android_cmd_line:
1093             self.stop()
1094         self._current_android_cmd_line = new_cmd_line
1095
1096         super(ChromiumAndroidDriver, self).start(pixel_tests, per_test_args)
1097
1098     def _start(self, pixel_tests, per_test_args):
1099         if not self._android_devices.is_device_prepared(self._android_commands.get_serial()):
1100             raise driver.DeviceFailure("%s is not prepared in _start()" % self._android_commands.get_serial())
1101
1102         for retries in range(3):
1103             try:
1104                 if self._start_once(pixel_tests, per_test_args):
1105                     return
1106             except ScriptError as e:
1107                 self._abort('ScriptError("%s") in _start()' % str(e))
1108
1109             self._log_error('Failed to start the content_shell application. Retries=%d. Log:%s' % (retries, self._get_logcat()))
1110             self.stop()
1111             time.sleep(2)
1112         self._abort('Failed to start the content_shell application multiple times. Giving up.')
1113
1114     def _start_once(self, pixel_tests, per_test_args):
1115         super(ChromiumAndroidDriver, self)._start(pixel_tests, per_test_args, wait_for_ready=False)
1116
1117         self._log_debug('Starting forwarder')
1118         self._forwarder_process = self._port._server_process_constructor(
1119             self._port, 'Forwarder', self._android_commands.adb_command() + ['shell', '%s -D %s' % (self._driver_details.device_forwarder_path(), FORWARD_PORTS)])
1120         self._forwarder_process.start()
1121
1122         deadline = time.time() + DRIVER_START_STOP_TIMEOUT_SECS
1123         if not self._wait_for_server_process_output(self._forwarder_process, deadline, 'Forwarding device port'):
1124             return False
1125
1126         self._android_commands.run(['logcat', '-c'])
1127
1128         cmd_line_file_path = self._driver_details.command_line_file()
1129         original_cmd_line_file_path = cmd_line_file_path + '.orig'
1130         if self._android_commands.file_exists(cmd_line_file_path) and not self._android_commands.file_exists(original_cmd_line_file_path):
1131             # We check for both the normal path and the backup because we do not want to step
1132             # on the backup. Otherwise, we'd clobber the backup whenever we changed the
1133             # command line during the run.
1134             self._android_commands.run(['shell', 'mv', cmd_line_file_path, original_cmd_line_file_path])
1135
1136         self._android_commands.run(['shell', 'echo'] + self._android_driver_cmd_line(pixel_tests, per_test_args) + ['>', self._driver_details.command_line_file()])
1137         self._created_cmd_line = True
1138
1139         self._android_commands.run(['shell', 'rm', '-rf', self._driver_details.device_crash_dumps_directory()])
1140         self._android_commands.mkdir(self._driver_details.device_crash_dumps_directory(), chmod='777')
1141
1142         start_result = self._android_commands.run(['shell', 'am', 'start', '-e', 'RunInSubThread', '-n', self._driver_details.activity_name()])
1143         if start_result.find('Exception') != -1:
1144             self._log_error('Failed to start the content_shell application. Exception:\n' + start_result)
1145             return False
1146
1147         if not ChromiumAndroidDriver._loop_with_timeout(self._all_pipes_created, DRIVER_START_STOP_TIMEOUT_SECS):
1148             return False
1149
1150         # Read back the shell prompt to ensure adb shell ready.
1151         deadline = time.time() + DRIVER_START_STOP_TIMEOUT_SECS
1152         self._server_process.start()
1153         self._read_prompt(deadline)
1154         self._log_debug('Interactive shell started')
1155
1156         # Start a process to read from the stdout fifo of the test driver and print to stdout.
1157         self._log_debug('Redirecting stdout to ' + self._out_fifo_path)
1158         self._read_stdout_process = self._port._server_process_constructor(
1159             self._port, 'ReadStdout', self._android_commands.adb_command() + ['shell', 'cat', self._out_fifo_path])
1160         self._read_stdout_process.start()
1161
1162         # Start a process to read from the stderr fifo of the test driver and print to stdout.
1163         self._log_debug('Redirecting stderr to ' + self._err_fifo_path)
1164         self._read_stderr_process = self._port._server_process_constructor(
1165             self._port, 'ReadStderr', self._android_commands.adb_command() + ['shell', 'cat', self._err_fifo_path])
1166         self._read_stderr_process.start()
1167
1168         self._log_debug('Redirecting stdin to ' + self._in_fifo_path)
1169         self._server_process.write('cat >%s\n' % self._in_fifo_path)
1170
1171         # Combine the stdout and stderr pipes into self._server_process.
1172         self._server_process.replace_outputs(self._read_stdout_process._proc.stdout, self._read_stderr_process._proc.stdout)
1173
1174         def deadlock_detector(processes, normal_startup_event):
1175             if not ChromiumAndroidDriver._loop_with_timeout(lambda: normal_startup_event.is_set(), DRIVER_START_STOP_TIMEOUT_SECS):
1176                 # If normal_startup_event is not set in time, the main thread must be blocked at
1177                 # reading/writing the fifo. Kill the fifo reading/writing processes to let the
1178                 # main thread escape from the deadlocked state. After that, the main thread will
1179                 # treat this as a crash.
1180                 self._log_error('Deadlock detected. Processes killed.')
1181                 for i in processes:
1182                     i.kill()
1183
1184         # Start a thread to kill the pipe reading/writing processes on deadlock of the fifos during startup.
1185         normal_startup_event = threading.Event()
1186         threading.Thread(name='DeadlockDetector', target=deadlock_detector,
1187                          args=([self._server_process, self._read_stdout_process, self._read_stderr_process], normal_startup_event)).start()
1188
1189         # The test driver might crash during startup or when the deadlock detector hits
1190         # a deadlock and kills the fifo reading/writing processes.
1191         if not self._wait_for_server_process_output(self._server_process, deadline, '#READY'):
1192             return False
1193
1194         # Inform the deadlock detector that the startup is successful without deadlock.
1195         normal_startup_event.set()
1196         self._log_debug("content_shell is ready")
1197         return True
1198
1199     def _pid_from_android_ps_output(self, ps_output, package_name):
1200         # ps output seems to be fixed width, we only care about the name and the pid
1201         # u0_a72    21630 125   947920 59364 ffffffff 400beee4 S org.chromium.native_test
1202         for line in ps_output.split('\n'):
1203             if line.find(self._driver_details.package_name()) != -1:
1204                 match = re.match(r'\S+\s+(\d+)', line)
1205                 return int(match.group(1))
1206
1207     def _pid_on_target(self):
1208         # FIXME: There must be a better way to do this than grepping ps output!
1209         ps_output = self._android_commands.run(['shell', 'ps'])
1210         return self._pid_from_android_ps_output(ps_output, self._driver_details.package_name())
1211
1212     def stop(self):
1213         if not self._device_failed:
1214             # Do not try to stop the application if there's something wrong with the device; adb may hang.
1215             # FIXME: crbug.com/305040. Figure out if it's really hanging (and why).
1216             self._android_commands.run(['shell', 'am', 'force-stop', self._driver_details.package_name()])
1217
1218         if self._read_stdout_process:
1219             self._read_stdout_process.kill()
1220             self._read_stdout_process = None
1221
1222         if self._read_stderr_process:
1223             self._read_stderr_process.kill()
1224             self._read_stderr_process = None
1225
1226         super(ChromiumAndroidDriver, self).stop()
1227
1228         if self._forwarder_process:
1229             self._forwarder_process.kill()
1230             self._forwarder_process = None
1231
1232         if self._android_devices.is_device_prepared(self._android_commands.get_serial()):
1233             if not ChromiumAndroidDriver._loop_with_timeout(self._remove_all_pipes, DRIVER_START_STOP_TIMEOUT_SECS):
1234                 self._abort('Failed to remove fifo files. May be locked.')
1235
1236         self._clean_up_cmd_line()
1237
1238     def _pull_crash_dumps_from_device(self):
1239         result = []
1240         if not self._android_commands.file_exists(self._driver_details.device_crash_dumps_directory()):
1241             return result
1242         dumps = self._android_commands.run(['shell', 'ls', self._driver_details.device_crash_dumps_directory()])
1243         for dump in dumps.splitlines():
1244             device_dump = '%s/%s' % (self._driver_details.device_crash_dumps_directory(), dump)
1245             local_dump = self._port._filesystem.join(self._port._dump_reader.crash_dumps_directory(), dump)
1246
1247             # FIXME: crbug.com/321489. Figure out why these commands would fail ...
1248             err = self._android_commands.run(['shell', 'chmod', '777', device_dump])
1249             if not err:
1250                 self._android_commands.pull(device_dump, local_dump)
1251             if not err:
1252                 self._android_commands.run(['shell', 'rm', '-f', device_dump])
1253
1254             if self._port._filesystem.exists(local_dump):
1255                 result.append(local_dump)
1256         return result
1257
1258     def _clean_up_cmd_line(self):
1259         if not self._created_cmd_line:
1260             return
1261
1262         cmd_line_file_path = self._driver_details.command_line_file()
1263         original_cmd_line_file_path = cmd_line_file_path + '.orig'
1264         if self._android_commands.file_exists(original_cmd_line_file_path):
1265             self._android_commands.run(['shell', 'mv', original_cmd_line_file_path, cmd_line_file_path])
1266         elif self._android_commands.file_exists(cmd_line_file_path):
1267             self._android_commands.run(['shell', 'rm', cmd_line_file_path])
1268         self._created_cmd_line = False
1269
1270     def _command_from_driver_input(self, driver_input):
1271         command = super(ChromiumAndroidDriver, self)._command_from_driver_input(driver_input)
1272         if command.startswith('/'):
1273             fs = self._port._filesystem
1274             # FIXME: what happens if command lies outside of the layout_tests_dir on the host?
1275             relative_test_filename = fs.relpath(command, fs.dirname(self._port.layout_tests_dir()))
1276             command = DEVICE_WEBKIT_BASE_DIR + relative_test_filename
1277         return command
1278
1279     def _read_prompt(self, deadline):
1280         last_char = ''
1281         while True:
1282             current_char = self._server_process.read_stdout(deadline, 1)
1283             if current_char == ' ':
1284                 if last_char in ('#', '$'):
1285                     return
1286             last_char = current_char