| # Copyright 2019 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| from __future__ import absolute_import |
| import logging |
| |
| from six.moves import range # pylint: disable=redefined-builtin |
| from devil import base_error |
| from devil.android import device_errors |
| from devil.android import device_utils |
| from devil.utils import parallelizer |
| from devil.utils import reraiser_thread |
| from devil.utils import timeout_retry |
| from pylib.local.device import local_device_environment |
| from pylib.local.emulator import avd |
| |
| # Mirroring https://bit.ly/2OjuxcS#23 |
| _MAX_ANDROID_EMULATORS = 16 |
| |
| |
| class LocalEmulatorEnvironment(local_device_environment.LocalDeviceEnvironment): |
| |
| def __init__(self, args, output_manager, error_func): |
| super(LocalEmulatorEnvironment, self).__init__(args, output_manager, |
| error_func) |
| self._avd_config = avd.AvdConfig(args.avd_config) |
| if args.emulator_count < 1: |
| error_func('--emulator-count must be >= 1') |
| elif args.emulator_count >= _MAX_ANDROID_EMULATORS: |
| logging.warning('--emulator-count capped at 16.') |
| self._emulator_count = min(_MAX_ANDROID_EMULATORS, args.emulator_count) |
| self._emulator_window = args.emulator_window |
| self._writable_system = ((hasattr(args, 'use_webview_provider') |
| and args.use_webview_provider) |
| or (hasattr(args, 'replace_system_package') |
| and args.replace_system_package) |
| or (hasattr(args, 'system_packages_to_remove') |
| and args.system_packages_to_remove)) |
| |
| self._emulator_instances = [] |
| self._device_serials = [] |
| |
| #override |
| def SetUp(self): |
| self._avd_config.Install() |
| |
| emulator_instances = [ |
| self._avd_config.CreateInstance() for _ in range(self._emulator_count) |
| ] |
| |
| def start_emulator_instance(e): |
| |
| def impl(e): |
| try: |
| e.Start( |
| window=self._emulator_window, |
| writable_system=self._writable_system) |
| except avd.AvdException: |
| logging.exception('Failed to start emulator instance.') |
| return None |
| try: |
| device_utils.DeviceUtils(e.serial).WaitUntilFullyBooted() |
| except base_error.BaseError: |
| e.Stop() |
| raise |
| return e |
| |
| def retry_on_timeout(exc): |
| return (isinstance(exc, device_errors.CommandTimeoutError) |
| or isinstance(exc, reraiser_thread.TimeoutError)) |
| |
| return timeout_retry.Run( |
| impl, |
| timeout=120 if self._writable_system else 30, |
| retries=2, |
| args=[e], |
| retry_if_func=retry_on_timeout) |
| |
| parallel_emulators = parallelizer.SyncParallelizer(emulator_instances) |
| self._emulator_instances = [ |
| emu |
| for emu in parallel_emulators.pMap(start_emulator_instance).pGet(None) |
| if emu is not None |
| ] |
| self._device_serials = [e.serial for e in self._emulator_instances] |
| |
| if not self._emulator_instances: |
| raise Exception('Failed to start any instances of the emulator.') |
| elif len(self._emulator_instances) < self._emulator_count: |
| logging.warning( |
| 'Running with fewer emulator instances than requested (%d vs %d)', |
| len(self._emulator_instances), self._emulator_count) |
| |
| super(LocalEmulatorEnvironment, self).SetUp() |
| |
| #override |
| def TearDown(self): |
| try: |
| super(LocalEmulatorEnvironment, self).TearDown() |
| finally: |
| parallelizer.SyncParallelizer(self._emulator_instances).Stop() |