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