File: android.py

package info (click to toggle)
chromium 138.0.7204.183-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 6,071,908 kB
  • sloc: cpp: 34,937,088; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,953; asm: 946,768; xml: 739,971; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,806; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (115 lines) | stat: -rw-r--r-- 4,014 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# Copyright 2023 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Android module to prepare APKs and emulators to run webdriver-based tests.
"""

import re
import os
import subprocess
import sys

import packaging
import logging
from typing import List, Optional

from chrome.test.variations.test_utils import SRC_DIR

# The root for the module pylib/android is under build/android.
sys.path.append(os.path.join(SRC_DIR, 'build', 'android'))

# This import adds `devil` to `sys.path`.
import devil_chromium

from devil.android import apk_helper
from devil.android import device_utils
from devil.android import forwarder
from devil.android.sdk import adb_wrapper
from pylib.local.emulator import avd

_INSTALLER_SCRIPT_PY = os.path.join(
  SRC_DIR, 'clank', 'bin', 'utils', 'installer_script_wrapper.py')


def _package_name(channel: str):
  if channel in ('beta', 'dev', 'canary'):
    return f'com.chrome.{channel}'
  return 'com.android.chrome'


def _is_require_signed(channel: str) -> bool:
  """Check if we need to install a signed build."""
  # The stable build has the same package name as prebuilt one, in order
  # to avoid the signature mismatch, we need to install the one with the
  # same signed build.
  return channel == 'stable'


def install_chrome(channel: str, device: device_utils.DeviceUtils) -> str:
  """Installs Chrome to the device and returns the package name."""
  args = [
    _INSTALLER_SCRIPT_PY, f'--product=chrome',
    f'--channel={channel}', f'--serial={device.serial}',
    f'--adb={adb_wrapper.AdbWrapper.GetAdbPath()}',
  ]
  args.append('--signed' if _is_require_signed(channel) else '--unsigned')
  try:
    subprocess.check_output(args=args)
  except subprocess.CalledProcessError as e:
    logging.error('Subprocess error caught %s', e.output.decode())
    raise RuntimeError('Chrome installation failed.')
  return _package_name(channel)


def install_webview(
  channel: str,
  device: device_utils.DeviceUtils
  ) -> packaging.version.Version:
  """Installs Webview to the device and returns the installed version."""
  args = [
    _INSTALLER_SCRIPT_PY, f'--product=webview',
    f'--channel={channel}', f'--serial={device.serial}',
    f'--adb={adb_wrapper.AdbWrapper.GetAdbPath()}',
  ]
  args.append('--signed' if _is_require_signed(channel) else '--unsigned')
  try:
    subprocess.check_output(args=args)
  except subprocess.CalledProcessError as e:
    logging.error('Subprocess error caught %s', e.output.decode())
    raise RuntimeError('Webview installation failed.')

  version_regex = r'\s*Preferred WebView package[^:]*[^\d]*([^\)]+)'
  version_output = device.RunShellCommand(['dumpsys' ,'webviewupdate'])
  version = [
    m.group(1)
    for line in version_output if (m := re.match(version_regex, line))
  ]
  return packaging.version.parse(version[0]) if version else None


def _forward_port(device: device_utils.DeviceUtils,
                  ports: Optional[List[int]] = None):
  # Ideally, we would dynamically allocate ports from the device, and
  # remember the mapping here, it requires how the client redirects ports.
  # Since we currently only allocate ports from a user space whose value is
  # always 3xxxx and above, there is a very rare case to cause issues here.
  # It is possible that the port is already used on the device, however,
  # the likelihood is small, and we will fix once it shows a problem.
  if ports:
    forwarder.Forwarder.Map([(port, port) for port in ports], device)


def launch_emulator(avd_config: str,
                    emulator_window: bool,
                    ports: Optional[List[int]] = None) -> avd._AvdInstance:
  """Launches the emulator and forwards ports from device to host."""
  avd_config = avd.AvdConfig(avd_config)
  avd_config.Install()

  instance = avd_config.CreateInstance()
  instance.Start(writable_system=True,
                 window=emulator_window)

  _forward_port(instance.device, ports)

  return instance