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