Index: build/android/devil/android/fastboot_utils.py |
diff --git a/build/android/devil/android/fastboot_utils.py b/build/android/devil/android/fastboot_utils.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..587f42fd02ce0bec28a2521c5afa9c65d30c0bbc |
--- /dev/null |
+++ b/build/android/devil/android/fastboot_utils.py |
@@ -0,0 +1,245 @@ |
+# Copyright 2015 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. |
+ |
+"""Provides a variety of device interactions based on fastboot.""" |
+# pylint: disable=unused-argument |
+ |
+import contextlib |
+import fnmatch |
+import logging |
+import os |
+import re |
+ |
+from devil.android import decorators |
+from devil.android import device_errors |
+from devil.android.sdk import fastboot |
+from devil.utils import timeout_retry |
+ |
+_DEFAULT_TIMEOUT = 30 |
+_DEFAULT_RETRIES = 3 |
+_FASTBOOT_REBOOT_TIMEOUT = 10 * _DEFAULT_TIMEOUT |
+ALL_PARTITIONS = [ |
+ 'bootloader', |
+ 'radio', |
+ 'boot', |
+ 'recovery', |
+ 'system', |
+ 'userdata', |
+ 'cache', |
+] |
+ |
+class FastbootUtils(object): |
+ |
+ _FASTBOOT_WAIT_TIME = 1 |
+ _RESTART_WHEN_FLASHING = ['bootloader', 'radio'] |
+ _BOARD_VERIFICATION_FILE = 'android-info.txt' |
+ _FLASH_IMAGE_FILES = { |
+ 'bootloader': 'bootloader*.img', |
+ 'radio': 'radio*.img', |
+ 'boot': 'boot.img', |
+ 'recovery': 'recovery.img', |
+ 'system': 'system.img', |
+ 'userdata': 'userdata.img', |
+ 'cache': 'cache.img', |
+ } |
+ |
+ def __init__(self, device, fastbooter=None, default_timeout=_DEFAULT_TIMEOUT, |
+ default_retries=_DEFAULT_RETRIES): |
+ """FastbootUtils constructor. |
+ |
+ Example Usage to flash a device: |
+ fastboot = fastboot_utils.FastbootUtils(device) |
+ fastboot.FlashDevice('/path/to/build/directory') |
+ |
+ Args: |
+ device: A DeviceUtils instance. |
+ fastbooter: Optional fastboot object. If none is passed, one will |
+ be created. |
+ default_timeout: An integer containing the default number of seconds to |
+ wait for an operation to complete if no explicit value is provided. |
+ default_retries: An integer containing the default number or times an |
+ operation should be retried on failure if no explicit value is provided. |
+ """ |
+ self._device = device |
+ self._board = device.product_board |
+ self._serial = str(device) |
+ self._default_timeout = default_timeout |
+ self._default_retries = default_retries |
+ if fastbooter: |
+ self.fastboot = fastbooter |
+ else: |
+ self.fastboot = fastboot.Fastboot(self._serial) |
+ |
+ @decorators.WithTimeoutAndRetriesFromInstance() |
+ def WaitForFastbootMode(self, timeout=None, retries=None): |
+ """Wait for device to boot into fastboot mode. |
+ |
+ This waits for the device serial to show up in fastboot devices output. |
+ """ |
+ def fastboot_mode(): |
+ return self._serial in self.fastboot.Devices() |
+ |
+ timeout_retry.WaitFor(fastboot_mode, wait_period=self._FASTBOOT_WAIT_TIME) |
+ |
+ @decorators.WithTimeoutAndRetriesFromInstance( |
+ min_default_timeout=_FASTBOOT_REBOOT_TIMEOUT) |
+ def EnableFastbootMode(self, timeout=None, retries=None): |
+ """Reboots phone into fastboot mode. |
+ |
+ Roots phone if needed, then reboots phone into fastboot mode and waits. |
+ """ |
+ self._device.EnableRoot() |
+ self._device.adb.Reboot(to_bootloader=True) |
+ self.WaitForFastbootMode() |
+ |
+ @decorators.WithTimeoutAndRetriesFromInstance( |
+ min_default_timeout=_FASTBOOT_REBOOT_TIMEOUT) |
+ def Reboot(self, bootloader=False, timeout=None, retries=None): |
+ """Reboots out of fastboot mode. |
+ |
+ It reboots the phone either back into fastboot, or to a regular boot. It |
+ then blocks until the device is ready. |
+ |
+ Args: |
+ bootloader: If set to True, reboots back into bootloader. |
+ """ |
+ if bootloader: |
+ self.fastboot.RebootBootloader() |
+ self.WaitForFastbootMode() |
+ else: |
+ self.fastboot.Reboot() |
+ self._device.WaitUntilFullyBooted(timeout=_FASTBOOT_REBOOT_TIMEOUT) |
+ |
+ def _VerifyBoard(self, directory): |
+ """Validate as best as possible that the android build matches the device. |
+ |
+ Goes through build files and checks if the board name is mentioned in the |
+ |self._BOARD_VERIFICATION_FILE| or in the build archive. |
+ |
+ Args: |
+ directory: directory where build files are located. |
+ """ |
+ files = os.listdir(directory) |
+ board_regex = re.compile(r'require board=(\w+)') |
+ if self._BOARD_VERIFICATION_FILE in files: |
+ with open(os.path.join(directory, self._BOARD_VERIFICATION_FILE)) as f: |
+ for line in f: |
+ m = board_regex.match(line) |
+ if m: |
+ board_name = m.group(1) |
+ if board_name == self._board: |
+ return True |
+ elif board_name: |
+ return False |
+ else: |
+ logging.warning('No board type found in %s.', |
+ self._BOARD_VERIFICATION_FILE) |
+ else: |
+ logging.warning('%s not found. Unable to use it to verify device.', |
+ self._BOARD_VERIFICATION_FILE) |
+ |
+ zip_regex = re.compile(r'.*%s.*\.zip' % re.escape(self._board)) |
+ for f in files: |
+ if zip_regex.match(f): |
+ return True |
+ |
+ return False |
+ |
+ def _FindAndVerifyPartitionsAndImages(self, partitions, directory): |
+ """Validate partitions and images. |
+ |
+ Validate all partition names and partition directories. Cannot stop mid |
+ flash so its important to validate everything first. |
+ |
+ Args: |
+ Partitions: partitions to be tested. |
+ directory: directory containing the images. |
+ |
+ Returns: |
+ Dictionary with exact partition, image name mapping. |
+ """ |
+ files = os.listdir(directory) |
+ |
+ def find_file(pattern): |
+ for filename in files: |
+ if fnmatch.fnmatch(filename, pattern): |
+ return os.path.join(directory, filename) |
+ raise device_errors.FastbootCommandFailedError( |
+ 'Failed to flash device. Counld not find image for %s.', pattern) |
+ |
+ return {name: find_file(self._FLASH_IMAGE_FILES[name]) |
+ for name in partitions} |
+ |
+ def _FlashPartitions(self, partitions, directory, wipe=False, force=False): |
+ """Flashes all given partiitons with all given images. |
+ |
+ Args: |
+ partitions: List of partitions to flash. |
+ directory: Directory where all partitions can be found. |
+ wipe: If set to true, will automatically detect if cache and userdata |
+ partitions are sent, and if so ignore them. |
+ force: boolean to decide to ignore board name safety checks. |
+ |
+ Raises: |
+ device_errors.CommandFailedError(): If image cannot be found or if bad |
+ partition name is give. |
+ """ |
+ if not self._VerifyBoard(directory): |
+ if force: |
+ logging.warning('Could not verify build is meant to be installed on ' |
+ 'the current device type, but force flag is set. ' |
+ 'Flashing device. Possibly dangerous operation.') |
+ else: |
+ raise device_errors.CommandFailedError( |
+ 'Could not verify build is meant to be installed on the current ' |
+ 'device type. Run again with force=True to force flashing with an ' |
+ 'unverified board.') |
+ |
+ flash_image_files = self._FindAndVerifyPartitionsAndImages(partitions, |
+ directory) |
+ for partition in partitions: |
+ if partition in ['cache', 'userdata'] and not wipe: |
+ logging.info( |
+ 'Not flashing in wipe mode. Skipping partition %s.', partition) |
+ else: |
+ logging.info( |
+ 'Flashing %s with %s', partition, flash_image_files[partition]) |
+ self.fastboot.Flash(partition, flash_image_files[partition]) |
+ if partition in self._RESTART_WHEN_FLASHING: |
+ self.Reboot(bootloader=True) |
jbudorick
2015/11/04 22:55:57
This will do multiple reboots in the course of fla
rnephew (Wrong account)
2015/11/04 22:58:42
Yes. After flashing those partitions, a reboot is
jbudorick
2015/11/04 22:59:08
sgtm
|
+ |
+ @contextlib.contextmanager |
+ def FastbootMode(self, timeout=None, retries=None): |
+ """Context manager that enables fastboot mode, and reboots after. |
+ |
+ Example usage: |
+ with FastbootMode(): |
+ Flash Device |
+ # Anything that runs after flashing. |
+ """ |
+ self.EnableFastbootMode() |
+ self.fastboot.SetOemOffModeCharge(False) |
+ try: |
+ yield self |
+ finally: |
+ self.fastboot.SetOemOffModeCharge(True) |
+ self.Reboot() |
+ |
+ def FlashDevice(self, directory, partitions=None, wipe=False): |
+ """Flash device with build in |directory|. |
+ |
+ Directory must contain bootloader, radio, boot, recovery, system, userdata, |
+ and cache .img files from an android build. This is a dangerous operation so |
+ use with care. |
+ |
+ Args: |
+ fastboot: A FastbootUtils instance. |
+ directory: Directory with build files. |
+ wipe: Wipes cache and userdata if set to true. |
+ partitions: List of partitions to flash. Defaults to all. |
+ """ |
+ if partitions is None: |
+ partitions = ALL_PARTITIONS |
+ with self.FastbootMode(): |
+ self._FlashPartitions(partitions, directory, wipe=wipe) |