11cb0ef41Sopenharmony_ci# Copyright 2018 the V8 project authors. All rights reserved.
21cb0ef41Sopenharmony_ci# Use of this source code is governed by a BSD-style license that can be
31cb0ef41Sopenharmony_ci# found in the LICENSE file.
41cb0ef41Sopenharmony_ci
51cb0ef41Sopenharmony_ci"""
61cb0ef41Sopenharmony_ciWrapper around the Android device abstraction from src/build/android.
71cb0ef41Sopenharmony_ci"""
81cb0ef41Sopenharmony_ci
91cb0ef41Sopenharmony_ciimport logging
101cb0ef41Sopenharmony_ciimport os
111cb0ef41Sopenharmony_ciimport sys
121cb0ef41Sopenharmony_ciimport re
131cb0ef41Sopenharmony_ci
141cb0ef41Sopenharmony_ciBASE_DIR = os.path.normpath(
151cb0ef41Sopenharmony_ci    os.path.join(os.path.dirname(__file__), '..', '..', '..'))
161cb0ef41Sopenharmony_ciANDROID_DIR = os.path.join(BASE_DIR, 'build', 'android')
171cb0ef41Sopenharmony_ciDEVICE_DIR = '/data/local/tmp/v8/'
181cb0ef41Sopenharmony_ci
191cb0ef41Sopenharmony_ci
201cb0ef41Sopenharmony_ciclass TimeoutException(Exception):
211cb0ef41Sopenharmony_ci  def __init__(self, timeout, output=None):
221cb0ef41Sopenharmony_ci    self.timeout = timeout
231cb0ef41Sopenharmony_ci    self.output = output
241cb0ef41Sopenharmony_ci
251cb0ef41Sopenharmony_ci
261cb0ef41Sopenharmony_ciclass CommandFailedException(Exception):
271cb0ef41Sopenharmony_ci  def __init__(self, status, output):
281cb0ef41Sopenharmony_ci    self.status = status
291cb0ef41Sopenharmony_ci    self.output = output
301cb0ef41Sopenharmony_ci
311cb0ef41Sopenharmony_ci
321cb0ef41Sopenharmony_ciclass _Driver(object):
331cb0ef41Sopenharmony_ci  """Helper class to execute shell commands on an Android device."""
341cb0ef41Sopenharmony_ci  def __init__(self, device=None):
351cb0ef41Sopenharmony_ci    assert os.path.exists(ANDROID_DIR)
361cb0ef41Sopenharmony_ci    sys.path.insert(0, ANDROID_DIR)
371cb0ef41Sopenharmony_ci
381cb0ef41Sopenharmony_ci    # We import the dependencies only on demand, so that this file can be
391cb0ef41Sopenharmony_ci    # imported unconditionally.
401cb0ef41Sopenharmony_ci    import devil_chromium
411cb0ef41Sopenharmony_ci    from devil.android import device_errors  # pylint: disable=import-error
421cb0ef41Sopenharmony_ci    from devil.android import device_utils  # pylint: disable=import-error
431cb0ef41Sopenharmony_ci    from devil.android.perf import cache_control  # pylint: disable=import-error
441cb0ef41Sopenharmony_ci    from devil.android.perf import perf_control  # pylint: disable=import-error
451cb0ef41Sopenharmony_ci    global cache_control
461cb0ef41Sopenharmony_ci    global device_errors
471cb0ef41Sopenharmony_ci    global perf_control
481cb0ef41Sopenharmony_ci
491cb0ef41Sopenharmony_ci    devil_chromium.Initialize()
501cb0ef41Sopenharmony_ci
511cb0ef41Sopenharmony_ci    # Find specified device or a single attached device if none was specified.
521cb0ef41Sopenharmony_ci    # In case none or multiple devices are attached, this raises an exception.
531cb0ef41Sopenharmony_ci    self.device = device_utils.DeviceUtils.HealthyDevices(
541cb0ef41Sopenharmony_ci        retries=5, enable_usb_resets=True, device_arg=device)[0]
551cb0ef41Sopenharmony_ci
561cb0ef41Sopenharmony_ci    # This remembers what we have already pushed to the device.
571cb0ef41Sopenharmony_ci    self.pushed = set()
581cb0ef41Sopenharmony_ci
591cb0ef41Sopenharmony_ci  def tear_down(self):
601cb0ef41Sopenharmony_ci    """Clean up files after running all tests."""
611cb0ef41Sopenharmony_ci    self.device.RemovePath(DEVICE_DIR, force=True, recursive=True)
621cb0ef41Sopenharmony_ci
631cb0ef41Sopenharmony_ci  def push_file(self, host_dir, file_name, target_rel='.',
641cb0ef41Sopenharmony_ci                skip_if_missing=False):
651cb0ef41Sopenharmony_ci    """Push a single file to the device (cached).
661cb0ef41Sopenharmony_ci
671cb0ef41Sopenharmony_ci    Args:
681cb0ef41Sopenharmony_ci      host_dir: Absolute parent directory of the file to push.
691cb0ef41Sopenharmony_ci      file_name: Name of the file to push.
701cb0ef41Sopenharmony_ci      target_rel: Parent directory of the target location on the device
711cb0ef41Sopenharmony_ci          (relative to the device's base dir for testing).
721cb0ef41Sopenharmony_ci      skip_if_missing: Keeps silent about missing files when set. Otherwise logs
731cb0ef41Sopenharmony_ci          error.
741cb0ef41Sopenharmony_ci    """
751cb0ef41Sopenharmony_ci    # TODO(sergiyb): Implement this method using self.device.PushChangedFiles to
761cb0ef41Sopenharmony_ci    # avoid accessing low-level self.device.adb.
771cb0ef41Sopenharmony_ci    file_on_host = os.path.join(host_dir, file_name)
781cb0ef41Sopenharmony_ci
791cb0ef41Sopenharmony_ci    # Only push files not yet pushed in one execution.
801cb0ef41Sopenharmony_ci    if file_on_host in self.pushed:
811cb0ef41Sopenharmony_ci      return
821cb0ef41Sopenharmony_ci
831cb0ef41Sopenharmony_ci    file_on_device_tmp = os.path.join(DEVICE_DIR, '_tmp_', file_name)
841cb0ef41Sopenharmony_ci    file_on_device = os.path.join(DEVICE_DIR, target_rel, file_name)
851cb0ef41Sopenharmony_ci    folder_on_device = os.path.dirname(file_on_device)
861cb0ef41Sopenharmony_ci
871cb0ef41Sopenharmony_ci    # Only attempt to push files that exist.
881cb0ef41Sopenharmony_ci    if not os.path.exists(file_on_host):
891cb0ef41Sopenharmony_ci      if not skip_if_missing:
901cb0ef41Sopenharmony_ci        logging.critical('Missing file on host: %s' % file_on_host)
911cb0ef41Sopenharmony_ci      return
921cb0ef41Sopenharmony_ci
931cb0ef41Sopenharmony_ci    # Work-around for 'text file busy' errors. Push the files to a temporary
941cb0ef41Sopenharmony_ci    # location and then copy them with a shell command.
951cb0ef41Sopenharmony_ci    output = self.device.adb.Push(file_on_host, file_on_device_tmp)
961cb0ef41Sopenharmony_ci    # Success looks like this: '3035 KB/s (12512056 bytes in 4.025s)'.
971cb0ef41Sopenharmony_ci    # Errors look like this: 'failed to copy  ... '.
981cb0ef41Sopenharmony_ci    if output and not re.search('^[0-9]', output.splitlines()[-1]):
991cb0ef41Sopenharmony_ci      logging.critical('PUSH FAILED: ' + output)
1001cb0ef41Sopenharmony_ci    self.device.adb.Shell('mkdir -p %s' % folder_on_device)
1011cb0ef41Sopenharmony_ci    self.device.adb.Shell('cp %s %s' % (file_on_device_tmp, file_on_device))
1021cb0ef41Sopenharmony_ci    self.pushed.add(file_on_host)
1031cb0ef41Sopenharmony_ci
1041cb0ef41Sopenharmony_ci  def push_executable(self, shell_dir, target_dir, binary):
1051cb0ef41Sopenharmony_ci    """Push files required to run a V8 executable.
1061cb0ef41Sopenharmony_ci
1071cb0ef41Sopenharmony_ci    Args:
1081cb0ef41Sopenharmony_ci      shell_dir: Absolute parent directory of the executable on the host.
1091cb0ef41Sopenharmony_ci      target_dir: Parent directory of the executable on the device (relative to
1101cb0ef41Sopenharmony_ci          devices' base dir for testing).
1111cb0ef41Sopenharmony_ci      binary: Name of the binary to push.
1121cb0ef41Sopenharmony_ci    """
1131cb0ef41Sopenharmony_ci    self.push_file(shell_dir, binary, target_dir)
1141cb0ef41Sopenharmony_ci
1151cb0ef41Sopenharmony_ci    # Push external startup data. Backwards compatible for revisions where
1161cb0ef41Sopenharmony_ci    # these files didn't exist. Or for bots that don't produce these files.
1171cb0ef41Sopenharmony_ci    self.push_file(
1181cb0ef41Sopenharmony_ci        shell_dir,
1191cb0ef41Sopenharmony_ci        'natives_blob.bin',
1201cb0ef41Sopenharmony_ci        target_dir,
1211cb0ef41Sopenharmony_ci        skip_if_missing=True,
1221cb0ef41Sopenharmony_ci    )
1231cb0ef41Sopenharmony_ci    self.push_file(
1241cb0ef41Sopenharmony_ci        shell_dir,
1251cb0ef41Sopenharmony_ci        'snapshot_blob.bin',
1261cb0ef41Sopenharmony_ci        target_dir,
1271cb0ef41Sopenharmony_ci        skip_if_missing=True,
1281cb0ef41Sopenharmony_ci    )
1291cb0ef41Sopenharmony_ci    self.push_file(
1301cb0ef41Sopenharmony_ci        shell_dir,
1311cb0ef41Sopenharmony_ci        'icudtl.dat',
1321cb0ef41Sopenharmony_ci        target_dir,
1331cb0ef41Sopenharmony_ci        skip_if_missing=True,
1341cb0ef41Sopenharmony_ci    )
1351cb0ef41Sopenharmony_ci
1361cb0ef41Sopenharmony_ci  def run(self, target_dir, binary, args, rel_path, timeout, env=None,
1371cb0ef41Sopenharmony_ci          logcat_file=False):
1381cb0ef41Sopenharmony_ci    """Execute a command on the device's shell.
1391cb0ef41Sopenharmony_ci
1401cb0ef41Sopenharmony_ci    Args:
1411cb0ef41Sopenharmony_ci      target_dir: Parent directory of the executable on the device (relative to
1421cb0ef41Sopenharmony_ci          devices' base dir for testing).
1431cb0ef41Sopenharmony_ci      binary: Name of the binary.
1441cb0ef41Sopenharmony_ci      args: List of arguments to pass to the binary.
1451cb0ef41Sopenharmony_ci      rel_path: Relative path on device to use as CWD.
1461cb0ef41Sopenharmony_ci      timeout: Timeout in seconds.
1471cb0ef41Sopenharmony_ci      env: The environment variables with which the command should be run.
1481cb0ef41Sopenharmony_ci      logcat_file: File into which to stream adb logcat log.
1491cb0ef41Sopenharmony_ci    """
1501cb0ef41Sopenharmony_ci    binary_on_device = os.path.join(DEVICE_DIR, target_dir, binary)
1511cb0ef41Sopenharmony_ci    cmd = [binary_on_device] + args
1521cb0ef41Sopenharmony_ci    def run_inner():
1531cb0ef41Sopenharmony_ci      try:
1541cb0ef41Sopenharmony_ci        output = self.device.RunShellCommand(
1551cb0ef41Sopenharmony_ci            cmd,
1561cb0ef41Sopenharmony_ci            cwd=os.path.join(DEVICE_DIR, rel_path),
1571cb0ef41Sopenharmony_ci            check_return=True,
1581cb0ef41Sopenharmony_ci            env=env,
1591cb0ef41Sopenharmony_ci            timeout=timeout,
1601cb0ef41Sopenharmony_ci            retries=0,
1611cb0ef41Sopenharmony_ci        )
1621cb0ef41Sopenharmony_ci        return '\n'.join(output)
1631cb0ef41Sopenharmony_ci      except device_errors.AdbCommandFailedError as e:
1641cb0ef41Sopenharmony_ci        raise CommandFailedException(e.status, e.output)
1651cb0ef41Sopenharmony_ci      except device_errors.CommandTimeoutError as e:
1661cb0ef41Sopenharmony_ci        raise TimeoutException(timeout, e.output)
1671cb0ef41Sopenharmony_ci
1681cb0ef41Sopenharmony_ci
1691cb0ef41Sopenharmony_ci    if logcat_file:
1701cb0ef41Sopenharmony_ci      with self.device.GetLogcatMonitor(output_file=logcat_file) as logmon:
1711cb0ef41Sopenharmony_ci        result = run_inner()
1721cb0ef41Sopenharmony_ci      logmon.Close()
1731cb0ef41Sopenharmony_ci      return result
1741cb0ef41Sopenharmony_ci    else:
1751cb0ef41Sopenharmony_ci      return run_inner()
1761cb0ef41Sopenharmony_ci
1771cb0ef41Sopenharmony_ci  def drop_ram_caches(self):
1781cb0ef41Sopenharmony_ci    """Drop ran caches on device."""
1791cb0ef41Sopenharmony_ci    cache = cache_control.CacheControl(self.device)
1801cb0ef41Sopenharmony_ci    cache.DropRamCaches()
1811cb0ef41Sopenharmony_ci
1821cb0ef41Sopenharmony_ci  def set_high_perf_mode(self):
1831cb0ef41Sopenharmony_ci    """Set device into high performance mode."""
1841cb0ef41Sopenharmony_ci    perf = perf_control.PerfControl(self.device)
1851cb0ef41Sopenharmony_ci    perf.SetHighPerfMode()
1861cb0ef41Sopenharmony_ci
1871cb0ef41Sopenharmony_ci  def set_default_perf_mode(self):
1881cb0ef41Sopenharmony_ci    """Set device into default performance mode."""
1891cb0ef41Sopenharmony_ci    perf = perf_control.PerfControl(self.device)
1901cb0ef41Sopenharmony_ci    perf.SetDefaultPerfMode()
1911cb0ef41Sopenharmony_ci
1921cb0ef41Sopenharmony_ci
1931cb0ef41Sopenharmony_ci_ANDROID_DRIVER = None
1941cb0ef41Sopenharmony_cidef android_driver(device=None):
1951cb0ef41Sopenharmony_ci  """Singleton access method to the driver class."""
1961cb0ef41Sopenharmony_ci  global _ANDROID_DRIVER
1971cb0ef41Sopenharmony_ci  if not _ANDROID_DRIVER:
1981cb0ef41Sopenharmony_ci    _ANDROID_DRIVER = _Driver(device)
1991cb0ef41Sopenharmony_ci  return _ANDROID_DRIVER
200