• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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."""
17import os
18import subprocess
19import sys
20import threading
21
22import fastboot_exceptions
23
24CREATE_NO_WINDOW = 0x08000000
25
26
27def _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
37class 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