16751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo#!/usr/bin/env python
26751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo#
36751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo# Copyright (C) 2017 The Android Open Source Project
46751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo#
56751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo# Licensed under the Apache License, Version 2.0 (the "License");
66751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo# you may not use this file except in compliance with the License.
76751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo# You may obtain a copy of the License at
86751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo#
96751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo#      http://www.apache.org/licenses/LICENSE-2.0
106751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo#
116751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo# Unless required by applicable law or agreed to in writing, software
126751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo# distributed under the License is distributed on an "AS IS" BASIS,
136751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
146751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo# See the License for the specific language governing permissions and
156751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo# limitations under the License.
166751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo#
176751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
186751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo"""Send an A/B update to an Android device over adb."""
196751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
206751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymoimport argparse
216751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymoimport BaseHTTPServer
226751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymoimport logging
236751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymoimport os
246751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymoimport socket
256751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymoimport subprocess
266751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymoimport sys
276751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymoimport threading
286751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymoimport zipfile
296751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
306751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
316751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo# The path used to store the OTA package when applying the package from a file.
326751bbe28c2bb1114a941dea0ffa489371d81ce2Alex DeymoOTA_PACKAGE_PATH = '/data/ota_package'
336751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
346751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
356751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymodef CopyFileObjLength(fsrc, fdst, buffer_size=128 * 1024, copy_length=None):
366751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  """Copy from a file object to another.
376751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
386751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  This function is similar to shutil.copyfileobj except that it allows to copy
396751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  less than the full source file.
406751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
416751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  Args:
426751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    fsrc: source file object where to read from.
436751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    fdst: destination file object where to write to.
446751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    buffer_size: size of the copy buffer in memory.
456751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    copy_length: maximum number of bytes to copy, or None to copy everything.
466751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
476751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  Returns:
486751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    the number of bytes copied.
496751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  """
506751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  copied = 0
516751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  while True:
526751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    chunk_size = buffer_size
536751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    if copy_length is not None:
546751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo      chunk_size = min(chunk_size, copy_length - copied)
556751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo      if not chunk_size:
566751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo        break
576751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    buf = fsrc.read(chunk_size)
586751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    if not buf:
596751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo      break
606751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    fdst.write(buf)
616751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    copied += len(buf)
626751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  return copied
636751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
646751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
656751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymoclass AndroidOTAPackage(object):
666751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  """Android update payload using the .zip format.
676751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
686751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  Android OTA packages traditionally used a .zip file to store the payload. When
696751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  applying A/B updates over the network, a payload binary is stored RAW inside
706751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  this .zip file which is used by update_engine to apply the payload. To do
716751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  this, an offset and size inside the .zip file are provided.
726751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  """
736751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
746751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  # Android OTA package file paths.
756751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  OTA_PAYLOAD_BIN = 'payload.bin'
766751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  OTA_PAYLOAD_PROPERTIES_TXT = 'payload_properties.txt'
776751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
786751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  def __init__(self, otafilename):
796751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    self.otafilename = otafilename
806751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
816751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    otazip = zipfile.ZipFile(otafilename, 'r')
826751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    payload_info = otazip.getinfo(self.OTA_PAYLOAD_BIN)
836751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    self.offset = payload_info.header_offset + len(payload_info.FileHeader())
846751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    self.size = payload_info.file_size
856751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    self.properties = otazip.read(self.OTA_PAYLOAD_PROPERTIES_TXT)
866751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
876751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
886751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymoclass UpdateHandler(BaseHTTPServer.BaseHTTPRequestHandler):
896751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  """A HTTPServer that supports single-range requests.
906751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
916751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  Attributes:
926751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    serving_payload: path to the only payload file we are serving.
936751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  """
946751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
956751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  @staticmethod
966751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  def _ParseRange(range_str, file_size):
976751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    """Parse an HTTP range string.
986751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
996751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    Args:
1006751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo      range_str: HTTP Range header in the request, not including "Header:".
1016751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo      file_size: total size of the serving file.
1026751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
1036751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    Returns:
1046751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo      A tuple (start_range, end_range) with the range of bytes requested.
1056751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    """
1066751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    start_range = 0
1076751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    end_range = file_size
1086751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
1096751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    if range_str:
1106751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo      range_str = range_str.split('=', 1)[1]
1116751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo      s, e = range_str.split('-', 1)
1126751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo      if s:
1136751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo        start_range = int(s)
1146751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo        if e:
1156751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo          end_range = int(e) + 1
1166751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo      elif e:
1176751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo        if int(e) < file_size:
1186751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo          start_range = file_size - int(e)
1196751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    return start_range, end_range
1206751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
1216751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
1226751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  def do_GET(self):  # pylint: disable=invalid-name
1236751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    """Reply with the requested payload file."""
1246751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    if self.path != '/payload':
1256751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo      self.send_error(404, 'Unknown request')
1266751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo      return
1276751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
1286751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    if not self.serving_payload:
1296751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo      self.send_error(500, 'No serving payload set')
1306751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo      return
1316751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
1326751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    try:
1336751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo      f = open(self.serving_payload, 'rb')
1346751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    except IOError:
1356751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo      self.send_error(404, 'File not found')
1366751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo      return
1376751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    # Handle the range request.
1386751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    if 'Range' in self.headers:
1396751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo      self.send_response(206)
1406751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    else:
1416751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo      self.send_response(200)
1426751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
1436751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    stat = os.fstat(f.fileno())
1446751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    start_range, end_range = self._ParseRange(self.headers.get('range'),
1456751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo                                              stat.st_size)
1466751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    logging.info('Serving request for %s from %s [%d, %d) length: %d',
1476751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo             self.path, self.serving_payload, start_range, end_range,
1486751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo             end_range - start_range)
1496751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
1506751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    self.send_header('Accept-Ranges', 'bytes')
1516751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    self.send_header('Content-Range',
1526751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo                     'bytes ' + str(start_range) + '-' + str(end_range - 1) +
1536751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo                     '/' + str(end_range - start_range))
1546751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    self.send_header('Content-Length', end_range - start_range)
1556751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
1566751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    self.send_header('Last-Modified', self.date_time_string(stat.st_mtime))
1576751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    self.send_header('Content-type', 'application/octet-stream')
1586751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    self.end_headers()
1596751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
1606751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    f.seek(start_range)
1616751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    CopyFileObjLength(f, self.wfile, copy_length=end_range - start_range)
1626751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
1636751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
1646751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymoclass ServerThread(threading.Thread):
1656751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  """A thread for serving HTTP requests."""
1666751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
1676751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  def __init__(self, ota_filename):
1686751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    threading.Thread.__init__(self)
1696751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    # serving_payload is a class attribute and the UpdateHandler class is
1706751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    # instantiated with every request.
1716751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    UpdateHandler.serving_payload = ota_filename
1726751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    self._httpd = BaseHTTPServer.HTTPServer(('127.0.0.1', 0), UpdateHandler)
1736751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    self.port = self._httpd.server_port
1746751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
1756751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  def run(self):
1766751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    try:
1776751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo      self._httpd.serve_forever()
1786751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    except (KeyboardInterrupt, socket.error):
1796751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo      pass
1806751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    logging.info('Server Terminated')
1816751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
1826751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  def StopServer(self):
1836751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    self._httpd.socket.close()
1846751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
1856751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
1866751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymodef StartServer(ota_filename):
1876751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  t = ServerThread(ota_filename)
1886751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  t.start()
1896751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  return t
1906751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
1916751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
1926751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymodef AndroidUpdateCommand(ota_filename, payload_url):
1936751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  """Return the command to run to start the update in the Android device."""
1946751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  ota = AndroidOTAPackage(ota_filename)
1956751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  headers = ota.properties
1966751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  headers += 'USER_AGENT=Dalvik (something, something)\n'
1976751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
1986751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  # headers += 'POWERWASH=1\n'
1996751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  headers += 'NETWORK_ID=0\n'
2006751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
2016751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  return ['update_engine_client', '--update', '--follow',
2026751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo          '--payload=%s' % payload_url, '--offset=%d' % ota.offset,
2036751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo          '--size=%d' % ota.size, '--headers="%s"' % headers]
2046751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
2056751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
2066751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymoclass AdbHost(object):
2076751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  """Represents a device connected via ADB."""
2086751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
2096751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  def __init__(self, device_serial=None):
2106751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    """Construct an instance.
2116751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
2126751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    Args:
2136751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo        device_serial: options string serial number of attached device.
2146751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    """
2156751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    self._device_serial = device_serial
2166751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    self._command_prefix = ['adb']
2176751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    if self._device_serial:
2186751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo      self._command_prefix += ['-s', self._device_serial]
2196751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
2206751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  def adb(self, command):
2216751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    """Run an ADB command like "adb push".
2226751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
2236751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    Args:
2246751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo      command: list of strings containing command and arguments to run
2256751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
2266751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    Returns:
2276751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo      the program's return code.
2286751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
2296751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    Raises:
2306751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo      subprocess.CalledProcessError on command exit != 0.
2316751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    """
2326751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    command = self._command_prefix + command
2336751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    logging.info('Running: %s', ' '.join(str(x) for x in command))
2346751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    p = subprocess.Popen(command, universal_newlines=True)
2356751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    p.wait()
2366751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    return p.returncode
2376751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
2386751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
2396751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymodef main():
2406751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  parser = argparse.ArgumentParser(description='Android A/B OTA helper.')
2416751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  parser.add_argument('otafile', metavar='ZIP', type=str,
2426751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo                      help='the OTA package file (a .zip file).')
2436751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  parser.add_argument('--file', action='store_true',
2446751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo                      help='Push the file to the device before updating.')
2456751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  parser.add_argument('--no-push', action='store_true',
2466751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo                      help='Skip the "push" command when using --file')
2476751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  parser.add_argument('-s', type=str, default='', metavar='DEVICE',
2486751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo                      help='The specific device to use.')
2496751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  parser.add_argument('--no-verbose', action='store_true',
2506751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo                      help='Less verbose output')
2516751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  args = parser.parse_args()
2526751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  logging.basicConfig(
2536751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo      level=logging.WARNING if args.no_verbose else logging.INFO)
2546751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
2556751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  dut = AdbHost(args.s)
2566751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
2576751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  server_thread = None
2586751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  # List of commands to execute on exit.
2596751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  finalize_cmds = []
2606751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  # Commands to execute when canceling an update.
2616751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  cancel_cmd = ['shell', 'su', '0', 'update_engine_client', '--cancel']
2626751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  # List of commands to perform the update.
2636751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  cmds = []
2646751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
2656751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  if args.file:
2666751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    # Update via pushing a file to /data.
2676751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    device_ota_file = os.path.join(OTA_PACKAGE_PATH, 'debug.zip')
2686751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    payload_url = 'file://' + device_ota_file
2696751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    if not args.no_push:
2706751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo      cmds.append(['push', args.otafile, device_ota_file])
2716751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    cmds.append(['shell', 'su', '0', 'chown', 'system:cache', device_ota_file])
2726751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    cmds.append(['shell', 'su', '0', 'chmod', '0660', device_ota_file])
2736751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  else:
2746751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    # Update via sending the payload over the network with an "adb reverse"
2756751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    # command.
2766751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    device_port = 1234
2776751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    payload_url = 'http://127.0.0.1:%d/payload' % device_port
2786751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    server_thread = StartServer(args.otafile)
2796751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    cmds.append(
2806751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo        ['reverse', 'tcp:%d' % device_port, 'tcp:%d' % server_thread.port])
2816751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    finalize_cmds.append(['reverse', '--remove', 'tcp:%d' % device_port])
2826751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
2836751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  try:
2846751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    # The main update command using the configured payload_url.
2856751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    update_cmd = AndroidUpdateCommand(args.otafile, payload_url)
2866751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    cmds.append(['shell', 'su', '0'] + update_cmd)
2876751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
2886751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    for cmd in cmds:
2896751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo      dut.adb(cmd)
2906751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  except KeyboardInterrupt:
2916751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    dut.adb(cancel_cmd)
2926751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  finally:
2936751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    if server_thread:
2946751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo      server_thread.StopServer()
2956751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo    for cmd in finalize_cmds:
2966751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo      dut.adb(cmd)
2976751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
2986751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  return 0
2996751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo
3006751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymoif __name__ == '__main__':
3016751bbe28c2bb1114a941dea0ffa489371d81ce2Alex Deymo  sys.exit(main())
302