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