Home | History | Annotate | Download | only in at-factory-tool
      1 # !/usr/bin/python
      2 # Copyright 2017 The Android Open Source Project
      3 #
      4 # Licensed under the Apache License, Version 2.0 (the "License");
      5 # you may not use this file except in compliance with the License.
      6 # You may obtain a copy of the License at
      7 #
      8 #     http://www.apache.org/licenses/LICENSE-2.0
      9 #
     10 # Unless required by applicable law or agreed to in writing, software
     11 # distributed under the License is distributed on an "AS IS" BASIS,
     12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 # See the License for the specific language governing permissions and
     14 # limitations under the License.
     15 
     16 """Fastboot Interface Implementation using subprocess library."""
     17 import os
     18 import subprocess
     19 import sys
     20 import threading
     21 
     22 import fastboot_exceptions
     23 
     24 CREATE_NO_WINDOW = 0x08000000
     25 
     26 
     27 def _GetCurrentPath():
     28   if getattr(sys, 'frozen', False):
     29     # we are running in a bundle
     30     path = sys._MEIPASS  # pylint: disable=protected-access
     31   else:
     32     # we are running in a normal Python environment
     33     path = os.path.dirname(os.path.abspath(__file__))
     34   return path
     35 
     36 
     37 class FastbootDevice(object):
     38   """An abstracted fastboot device object.
     39 
     40   Attributes:
     41     serial_number: The serial number of the fastboot device.
     42   """
     43 
     44   current_path = _GetCurrentPath()
     45   fastboot_command = os.path.join(current_path, 'fastboot.exe')
     46   HOST_OS = 'Windows'
     47 
     48   @staticmethod
     49   def ListDevices():
     50     """List all fastboot devices.
     51 
     52     Returns:
     53       A list of serial numbers for all the fastboot devices.
     54     """
     55     try:
     56       out = subprocess.check_output(
     57           [FastbootDevice.fastboot_command, 'devices'],
     58           creationflags=CREATE_NO_WINDOW)
     59       device_serial_numbers = (out.replace('\tfastboot', '')
     60                                .rstrip().splitlines())
     61       # filter out empty string
     62       return filter(None, device_serial_numbers)
     63     except subprocess.CalledProcessError as e:
     64       raise fastboot_exceptions.FastbootFailure(e.output)
     65 
     66   def __init__(self, serial_number):
     67     """Initiate the fastboot device object.
     68 
     69     Args:
     70       serial_number: The serial number of the fastboot device.
     71     """
     72     self.serial_number = serial_number
     73     # Lock to make sure only one fastboot command can be issued to one device
     74     # at one time.
     75     self._lock = threading.Lock()
     76 
     77   def Reboot(self):
     78     """Reboot the device into fastboot mode.
     79 
     80     Returns:
     81       The command output.
     82     """
     83     try:
     84       self._lock.acquire()
     85       out = subprocess.check_output(
     86           [FastbootDevice.fastboot_command, '-s', self.serial_number,
     87            'reboot-bootloader'], creationflags=CREATE_NO_WINDOW)
     88       return out
     89     except subprocess.CalledProcessError as e:
     90       raise fastboot_exceptions.FastbootFailure(e.output)
     91     finally:
     92       self._lock.release()
     93 
     94   def Oem(self, oem_command, err_to_out):
     95     """"Run an OEM command.
     96 
     97     Args:
     98       oem_command: The OEM command to run.
     99       err_to_out: Whether to redirect stderr to stdout.
    100     Returns:
    101       The result message for the OEM command.
    102     Raises:
    103       FastbootFailure: If failure happens during the command.
    104     """
    105     try:
    106       self._lock.acquire()
    107       # We need to redirect the output no matter err_to_out is set
    108       # So that FastbootFailure can catch the right error.
    109       return subprocess.check_output(
    110           [
    111               FastbootDevice.fastboot_command, '-s', self.serial_number,
    112               'oem', oem_command
    113           ],
    114           stderr=subprocess.STDOUT,
    115           creationflags=CREATE_NO_WINDOW)
    116     except subprocess.CalledProcessError as e:
    117       raise fastboot_exceptions.FastbootFailure(e.output)
    118     finally:
    119       self._lock.release()
    120 
    121   def Flash(self, partition, file_path):
    122     """Flash a file to a partition.
    123 
    124     Args:
    125       file_path: The partition file to be flashed.
    126       partition: The partition to be flashed.
    127     Returns:
    128       The output for the fastboot command required.
    129     Raises:
    130       FastbootFailure: If failure happens during the command.
    131     """
    132     try:
    133       self._lock.acquire()
    134       return subprocess.check_output(
    135           [
    136               FastbootDevice.fastboot_command, '-s', self.serial_number,
    137               'flash', partition, file_path
    138           ],
    139           creationflags=CREATE_NO_WINDOW)
    140     except subprocess.CalledProcessError as e:
    141       raise fastboot_exceptions.FastbootFailure(e.output)
    142     finally:
    143       self._lock.release()
    144 
    145   def Upload(self, file_path):
    146     """Pulls a file from the fastboot device to the local file system.
    147 
    148     Args:
    149       file_path: The file path of the file system
    150         that the remote file would be pulled to.
    151     Returns:
    152       The output for the fastboot command required.
    153     Raises:
    154       FastbootFailure: If failure happens during the command.
    155     """
    156     try:
    157       self._lock.acquire()
    158       return subprocess.check_output(
    159           [
    160               FastbootDevice.fastboot_command, '-s', self.serial_number,
    161               'get_staged', file_path
    162           ],
    163           creationflags=CREATE_NO_WINDOW)
    164     except subprocess.CalledProcessError as e:
    165       raise fastboot_exceptions.FastbootFailure(e.output)
    166     finally:
    167       self._lock.release()
    168 
    169   def Download(self, file_path):
    170     """Push a file from the file system to the fastboot device.
    171 
    172     Args:
    173       file_path: The file path of the file on the local file system
    174         that would be pushed to fastboot device.
    175     Returns:
    176       The output for the fastboot command required.
    177     Raises:
    178       FastbootFailure: If failure happens during the command.
    179     """
    180     try:
    181       self._lock.acquire()
    182       return subprocess.check_output(
    183           [
    184               FastbootDevice.fastboot_command, '-s', self.serial_number,
    185               'stage', file_path
    186           ],
    187           creationflags=CREATE_NO_WINDOW)
    188     except subprocess.CalledProcessError as e:
    189       raise fastboot_exceptions.FastbootFailure(e.output)
    190     finally:
    191       self._lock.release()
    192 
    193   def GetVar(self, var):
    194     """Get a variable from the device.
    195 
    196     Note that the return value is in stderr instead of stdout.
    197     Args:
    198       var: The name of the variable.
    199     Returns:
    200       The value for the variable.
    201     Raises:
    202       FastbootFailure: If failure happens during the command.
    203     """
    204     try:
    205       # Fastboot getvar command's output would be in stderr instead of stdout.
    206       # Need to redirect stderr to stdout.
    207       self._lock.acquire()
    208 
    209       # Need the shell=True flag for windows, otherwise it hangs.
    210       out = subprocess.check_output(
    211           [
    212               FastbootDevice.fastboot_command, '-s', self.serial_number,
    213               'getvar', var
    214           ],
    215           stderr=subprocess.STDOUT,
    216           shell=True,
    217           creationflags=CREATE_NO_WINDOW)
    218     except subprocess.CalledProcessError as e:
    219       # Since we redirected stderr, we should print stdout here.
    220       raise fastboot_exceptions.FastbootFailure(e.output)
    221     finally:
    222       self._lock.release()
    223     if var == 'at-vboot-state':
    224       # For the result of vboot-state, it does not follow the standard.
    225       return out
    226     lines = out.splitlines()
    227     for line in lines:
    228       if line.startswith(var + ': '):
    229         value = line.replace(var + ': ', '').replace('\r', '')
    230     return value
    231 
    232   @staticmethod
    233   def GetHostOs():
    234     return FastbootDevice.HOST_OS
    235 
    236   def Disconnect(self):
    237     """Disconnect from the fastboot device."""
    238     pass
    239 
    240   def __del__(self):
    241     self.Disconnect()
    242