xref: /aosp_15_r20/external/cronet/build/fuchsia/test/flash_device.py (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1*6777b538SAndroid Build Coastguard Worker#!/usr/bin/env vpython3
2*6777b538SAndroid Build Coastguard Worker# Copyright 2022 The Chromium Authors
3*6777b538SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be
4*6777b538SAndroid Build Coastguard Worker# found in the LICENSE file.
5*6777b538SAndroid Build Coastguard Worker"""Implements commands for flashing a Fuchsia device."""
6*6777b538SAndroid Build Coastguard Worker
7*6777b538SAndroid Build Coastguard Workerimport argparse
8*6777b538SAndroid Build Coastguard Workerimport logging
9*6777b538SAndroid Build Coastguard Workerimport os
10*6777b538SAndroid Build Coastguard Workerimport subprocess
11*6777b538SAndroid Build Coastguard Workerimport sys
12*6777b538SAndroid Build Coastguard Worker
13*6777b538SAndroid Build Coastguard Workerfrom typing import Optional, Tuple
14*6777b538SAndroid Build Coastguard Worker
15*6777b538SAndroid Build Coastguard Workerimport common
16*6777b538SAndroid Build Coastguard Workerfrom boot_device import BootMode, StateTransitionError, boot_device
17*6777b538SAndroid Build Coastguard Workerfrom common import get_system_info, find_image_in_sdk, \
18*6777b538SAndroid Build Coastguard Worker                   register_device_args
19*6777b538SAndroid Build Coastguard Workerfrom compatible_utils import get_sdk_hash, running_unattended
20*6777b538SAndroid Build Coastguard Workerfrom lockfile import lock
21*6777b538SAndroid Build Coastguard Worker
22*6777b538SAndroid Build Coastguard Worker# Flash-file lock. Used to restrict number of flash operations per host.
23*6777b538SAndroid Build Coastguard Worker# File lock should be marked as stale after 15 mins.
24*6777b538SAndroid Build Coastguard Worker_FF_LOCK = os.path.join('/tmp', 'flash.lock')
25*6777b538SAndroid Build Coastguard Worker_FF_LOCK_STALE_SECS = 60 * 15
26*6777b538SAndroid Build Coastguard Worker_FF_LOCK_ACQ_TIMEOUT = _FF_LOCK_STALE_SECS
27*6777b538SAndroid Build Coastguard Worker
28*6777b538SAndroid Build Coastguard Worker
29*6777b538SAndroid Build Coastguard Workerdef _get_system_info(target: Optional[str],
30*6777b538SAndroid Build Coastguard Worker                     serial_num: Optional[str]) -> Tuple[str, str]:
31*6777b538SAndroid Build Coastguard Worker    """Retrieves installed OS version from device.
32*6777b538SAndroid Build Coastguard Worker
33*6777b538SAndroid Build Coastguard Worker    Args:
34*6777b538SAndroid Build Coastguard Worker        target: Target to get system info of.
35*6777b538SAndroid Build Coastguard Worker        serial_num: Serial number of device to get system info of.
36*6777b538SAndroid Build Coastguard Worker    Returns:
37*6777b538SAndroid Build Coastguard Worker        Tuple of strings, containing (product, version number).
38*6777b538SAndroid Build Coastguard Worker    """
39*6777b538SAndroid Build Coastguard Worker
40*6777b538SAndroid Build Coastguard Worker    if running_unattended():
41*6777b538SAndroid Build Coastguard Worker        try:
42*6777b538SAndroid Build Coastguard Worker            boot_device(target, BootMode.REGULAR, serial_num)
43*6777b538SAndroid Build Coastguard Worker        except (subprocess.CalledProcessError, StateTransitionError):
44*6777b538SAndroid Build Coastguard Worker            logging.warning('Could not boot device. Assuming in fastboot')
45*6777b538SAndroid Build Coastguard Worker            return ('', '')
46*6777b538SAndroid Build Coastguard Worker
47*6777b538SAndroid Build Coastguard Worker    return get_system_info(target)
48*6777b538SAndroid Build Coastguard Worker
49*6777b538SAndroid Build Coastguard Worker
50*6777b538SAndroid Build Coastguard Workerdef _update_required(
51*6777b538SAndroid Build Coastguard Worker        os_check,
52*6777b538SAndroid Build Coastguard Worker        system_image_dir: Optional[str],
53*6777b538SAndroid Build Coastguard Worker        target: Optional[str],
54*6777b538SAndroid Build Coastguard Worker        serial_num: Optional[str] = None) -> Tuple[bool, Optional[str]]:
55*6777b538SAndroid Build Coastguard Worker    """Returns True if a system update is required and path to image dir."""
56*6777b538SAndroid Build Coastguard Worker
57*6777b538SAndroid Build Coastguard Worker    if os_check == 'ignore':
58*6777b538SAndroid Build Coastguard Worker        return False, system_image_dir
59*6777b538SAndroid Build Coastguard Worker    if not system_image_dir:
60*6777b538SAndroid Build Coastguard Worker        raise ValueError('System image directory must be specified.')
61*6777b538SAndroid Build Coastguard Worker    if not os.path.exists(system_image_dir):
62*6777b538SAndroid Build Coastguard Worker        logging.warning(
63*6777b538SAndroid Build Coastguard Worker            'System image directory does not exist. Assuming it\'s '
64*6777b538SAndroid Build Coastguard Worker            'a product-bundle name and dynamically searching for '
65*6777b538SAndroid Build Coastguard Worker            'image directory')
66*6777b538SAndroid Build Coastguard Worker        path = find_image_in_sdk(system_image_dir)
67*6777b538SAndroid Build Coastguard Worker        if not path:
68*6777b538SAndroid Build Coastguard Worker            raise FileNotFoundError(
69*6777b538SAndroid Build Coastguard Worker                f'System image directory {system_image_dir} could not'
70*6777b538SAndroid Build Coastguard Worker                'be found')
71*6777b538SAndroid Build Coastguard Worker        system_image_dir = path
72*6777b538SAndroid Build Coastguard Worker    if (os_check == 'check'
73*6777b538SAndroid Build Coastguard Worker            and get_sdk_hash(system_image_dir) == _get_system_info(
74*6777b538SAndroid Build Coastguard Worker                target, serial_num)):
75*6777b538SAndroid Build Coastguard Worker        return False, system_image_dir
76*6777b538SAndroid Build Coastguard Worker    return True, system_image_dir
77*6777b538SAndroid Build Coastguard Worker
78*6777b538SAndroid Build Coastguard Worker
79*6777b538SAndroid Build Coastguard Workerdef _run_flash_command(system_image_dir: str, target_id: Optional[str]):
80*6777b538SAndroid Build Coastguard Worker    """Helper function for running `ffx target flash`."""
81*6777b538SAndroid Build Coastguard Worker    logging.info('Flashing %s to %s', system_image_dir, target_id)
82*6777b538SAndroid Build Coastguard Worker    # Flash only with a file lock acquired.
83*6777b538SAndroid Build Coastguard Worker    # This prevents multiple fastboot binaries from flashing concurrently,
84*6777b538SAndroid Build Coastguard Worker    # which should increase the odds of flashing success.
85*6777b538SAndroid Build Coastguard Worker    with lock(_FF_LOCK, timeout=_FF_LOCK_ACQ_TIMEOUT):
86*6777b538SAndroid Build Coastguard Worker        # The ffx.fastboot.inline_target has negative impact when ffx
87*6777b538SAndroid Build Coastguard Worker        # discovering devices in fastboot, so it's inlined here to limit its
88*6777b538SAndroid Build Coastguard Worker        # scope. See the discussion in https://fxbug.dev/issues/317228141.
89*6777b538SAndroid Build Coastguard Worker        logging.info(
90*6777b538SAndroid Build Coastguard Worker            'Flash result %s',
91*6777b538SAndroid Build Coastguard Worker            common.run_ffx_command(cmd=('target', 'flash', '-b',
92*6777b538SAndroid Build Coastguard Worker                                        system_image_dir,
93*6777b538SAndroid Build Coastguard Worker                                        '--no-bootloader-reboot'),
94*6777b538SAndroid Build Coastguard Worker                                   target_id=target_id,
95*6777b538SAndroid Build Coastguard Worker                                   configs=['ffx.fastboot.inline_target=true'],
96*6777b538SAndroid Build Coastguard Worker                                   capture_output=True).stdout)
97*6777b538SAndroid Build Coastguard Worker
98*6777b538SAndroid Build Coastguard Worker
99*6777b538SAndroid Build Coastguard Workerdef update(system_image_dir: str,
100*6777b538SAndroid Build Coastguard Worker           os_check: str,
101*6777b538SAndroid Build Coastguard Worker           target: Optional[str],
102*6777b538SAndroid Build Coastguard Worker           serial_num: Optional[str] = None) -> None:
103*6777b538SAndroid Build Coastguard Worker    """Conditionally updates target given.
104*6777b538SAndroid Build Coastguard Worker
105*6777b538SAndroid Build Coastguard Worker    Args:
106*6777b538SAndroid Build Coastguard Worker        system_image_dir: string, path to image directory.
107*6777b538SAndroid Build Coastguard Worker        os_check: <check|ignore|update>, which decides how to update the device.
108*6777b538SAndroid Build Coastguard Worker        target: Node-name string indicating device that should be updated.
109*6777b538SAndroid Build Coastguard Worker        serial_num: String of serial number of device that should be updated.
110*6777b538SAndroid Build Coastguard Worker    """
111*6777b538SAndroid Build Coastguard Worker    needs_update, actual_image_dir = _update_required(os_check,
112*6777b538SAndroid Build Coastguard Worker                                                      system_image_dir, target,
113*6777b538SAndroid Build Coastguard Worker                                                      serial_num)
114*6777b538SAndroid Build Coastguard Worker    logging.info('update_required %s, actual_image_dir %s', needs_update,
115*6777b538SAndroid Build Coastguard Worker                 actual_image_dir)
116*6777b538SAndroid Build Coastguard Worker    if not needs_update:
117*6777b538SAndroid Build Coastguard Worker        return
118*6777b538SAndroid Build Coastguard Worker    if serial_num:
119*6777b538SAndroid Build Coastguard Worker        boot_device(target, BootMode.BOOTLOADER, serial_num)
120*6777b538SAndroid Build Coastguard Worker        _run_flash_command(system_image_dir, serial_num)
121*6777b538SAndroid Build Coastguard Worker    else:
122*6777b538SAndroid Build Coastguard Worker        _run_flash_command(system_image_dir, target)
123*6777b538SAndroid Build Coastguard Worker
124*6777b538SAndroid Build Coastguard Worker
125*6777b538SAndroid Build Coastguard Workerdef register_update_args(arg_parser: argparse.ArgumentParser,
126*6777b538SAndroid Build Coastguard Worker                         default_os_check: Optional[str] = 'check') -> None:
127*6777b538SAndroid Build Coastguard Worker    """Register common arguments for device updating."""
128*6777b538SAndroid Build Coastguard Worker    serve_args = arg_parser.add_argument_group('update',
129*6777b538SAndroid Build Coastguard Worker                                               'device updating arguments')
130*6777b538SAndroid Build Coastguard Worker    serve_args.add_argument('--system-image-dir',
131*6777b538SAndroid Build Coastguard Worker                            help='Specify the directory that contains the '
132*6777b538SAndroid Build Coastguard Worker                            'Fuchsia image used to flash the device. Only '
133*6777b538SAndroid Build Coastguard Worker                            'needs to be specified if "os_check" is not '
134*6777b538SAndroid Build Coastguard Worker                            '"ignore".')
135*6777b538SAndroid Build Coastguard Worker    serve_args.add_argument('--serial-num',
136*6777b538SAndroid Build Coastguard Worker                            default=os.environ.get('FUCHSIA_FASTBOOT_SERNUM'),
137*6777b538SAndroid Build Coastguard Worker                            help='Serial number of the device. Should be '
138*6777b538SAndroid Build Coastguard Worker                            'specified for devices that do not have an image '
139*6777b538SAndroid Build Coastguard Worker                            'flashed.')
140*6777b538SAndroid Build Coastguard Worker    serve_args.add_argument('--os-check',
141*6777b538SAndroid Build Coastguard Worker                            choices=['check', 'update', 'ignore'],
142*6777b538SAndroid Build Coastguard Worker                            default=default_os_check,
143*6777b538SAndroid Build Coastguard Worker                            help='Sets the OS version enforcement policy. If '
144*6777b538SAndroid Build Coastguard Worker                            '"check", then the deployment process will halt '
145*6777b538SAndroid Build Coastguard Worker                            'if the target\'s version does not match. If '
146*6777b538SAndroid Build Coastguard Worker                            '"update", then the target device will '
147*6777b538SAndroid Build Coastguard Worker                            'be reflashed. If "ignore", then the OS version '
148*6777b538SAndroid Build Coastguard Worker                            'will not be checked.')
149*6777b538SAndroid Build Coastguard Worker
150*6777b538SAndroid Build Coastguard Worker
151*6777b538SAndroid Build Coastguard Workerdef main():
152*6777b538SAndroid Build Coastguard Worker    """Stand-alone function for flashing a device."""
153*6777b538SAndroid Build Coastguard Worker    parser = argparse.ArgumentParser()
154*6777b538SAndroid Build Coastguard Worker    register_device_args(parser)
155*6777b538SAndroid Build Coastguard Worker    register_update_args(parser, default_os_check='update')
156*6777b538SAndroid Build Coastguard Worker    args = parser.parse_args()
157*6777b538SAndroid Build Coastguard Worker    update(args.system_image_dir, args.os_check, args.target_id,
158*6777b538SAndroid Build Coastguard Worker           args.serial_num)
159*6777b538SAndroid Build Coastguard Worker
160*6777b538SAndroid Build Coastguard Worker
161*6777b538SAndroid Build Coastguard Workerif __name__ == '__main__':
162*6777b538SAndroid Build Coastguard Worker    sys.exit(main())
163