blob: ae24418f12983a31840f6e25e8ec3146975327b7 [file] [log] [blame]
Andrew Lassalle165843c2019-11-05 13:30:34 -08001#!/usr/bin/env python
Alex Deymo6751bbe2017-03-21 11:20:02 -07002#
3# Copyright (C) 2017 The Android Open Source Project
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
18"""Send an A/B update to an Android device over adb."""
19
Kelvin Zhang4b883ea2020-10-08 13:26:44 -040020from __future__ import print_function
Andrew Lassalle165843c2019-11-05 13:30:34 -080021from __future__ import absolute_import
22
Alex Deymo6751bbe2017-03-21 11:20:02 -070023import argparse
Kelvin Zhangaba70ab2020-08-04 10:32:59 -040024import binascii
Sen Jiang3b15b592017-09-26 18:21:04 -070025import hashlib
Alex Deymo6751bbe2017-03-21 11:20:02 -070026import logging
27import os
28import socket
29import subprocess
30import sys
Kelvin Zhangaba70ab2020-08-04 10:32:59 -040031import struct
Kelvin Zhang51aad992021-02-19 14:46:28 -050032import tempfile
Alex Deymo6751bbe2017-03-21 11:20:02 -070033import threading
Sen Jiang144f9f82017-09-26 15:49:45 -070034import xml.etree.ElementTree
Alex Deymo6751bbe2017-03-21 11:20:02 -070035import zipfile
36
Andrew Lassalle165843c2019-11-05 13:30:34 -080037from six.moves import BaseHTTPServer
38
Sen Jianga1784b72017-08-09 17:42:36 -070039import update_payload.payload
40
Alex Deymo6751bbe2017-03-21 11:20:02 -070041
42# The path used to store the OTA package when applying the package from a file.
43OTA_PACKAGE_PATH = '/data/ota_package'
44
Sen Jianga1784b72017-08-09 17:42:36 -070045# The path to the payload public key on the device.
46PAYLOAD_KEY_PATH = '/etc/update_engine/update-payload-key.pub.pem'
47
48# The port on the device that update_engine should connect to.
49DEVICE_PORT = 1234
Alex Deymo6751bbe2017-03-21 11:20:02 -070050
Andrew Lassalle165843c2019-11-05 13:30:34 -080051
Alex Deymo6751bbe2017-03-21 11:20:02 -070052def CopyFileObjLength(fsrc, fdst, buffer_size=128 * 1024, copy_length=None):
53 """Copy from a file object to another.
54
55 This function is similar to shutil.copyfileobj except that it allows to copy
56 less than the full source file.
57
58 Args:
59 fsrc: source file object where to read from.
60 fdst: destination file object where to write to.
61 buffer_size: size of the copy buffer in memory.
62 copy_length: maximum number of bytes to copy, or None to copy everything.
63
64 Returns:
65 the number of bytes copied.
66 """
67 copied = 0
68 while True:
69 chunk_size = buffer_size
70 if copy_length is not None:
71 chunk_size = min(chunk_size, copy_length - copied)
72 if not chunk_size:
73 break
74 buf = fsrc.read(chunk_size)
75 if not buf:
76 break
77 fdst.write(buf)
78 copied += len(buf)
79 return copied
80
81
82class AndroidOTAPackage(object):
83 """Android update payload using the .zip format.
84
85 Android OTA packages traditionally used a .zip file to store the payload. When
86 applying A/B updates over the network, a payload binary is stored RAW inside
87 this .zip file which is used by update_engine to apply the payload. To do
88 this, an offset and size inside the .zip file are provided.
89 """
90
91 # Android OTA package file paths.
92 OTA_PAYLOAD_BIN = 'payload.bin'
93 OTA_PAYLOAD_PROPERTIES_TXT = 'payload_properties.txt'
Tianjie Xu3f9be772019-11-02 18:31:50 -070094 SECONDARY_OTA_PAYLOAD_BIN = 'secondary/payload.bin'
95 SECONDARY_OTA_PAYLOAD_PROPERTIES_TXT = 'secondary/payload_properties.txt'
Kelvin Zhangaba70ab2020-08-04 10:32:59 -040096 PAYLOAD_MAGIC_HEADER = b'CrAU'
Alex Deymo6751bbe2017-03-21 11:20:02 -070097
Tianjie Xu3f9be772019-11-02 18:31:50 -070098 def __init__(self, otafilename, secondary_payload=False):
Alex Deymo6751bbe2017-03-21 11:20:02 -070099 self.otafilename = otafilename
100
101 otazip = zipfile.ZipFile(otafilename, 'r')
Tianjie Xu3f9be772019-11-02 18:31:50 -0700102 payload_entry = (self.SECONDARY_OTA_PAYLOAD_BIN if secondary_payload else
103 self.OTA_PAYLOAD_BIN)
104 payload_info = otazip.getinfo(payload_entry)
Kelvin Zhangaba70ab2020-08-04 10:32:59 -0400105
106 if payload_info.compress_type != 0:
107 logging.error(
Kelvin Zhang07676f52020-12-01 10:45:09 -0500108 "Expected payload to be uncompressed, got compression method %d",
Kelvin Zhangaba70ab2020-08-04 10:32:59 -0400109 payload_info.compress_type)
110 # Don't use len(payload_info.extra). Because that returns size of extra
111 # fields in central directory. We need to look at local file directory,
112 # as these two might have different sizes.
113 with open(otafilename, "rb") as fp:
114 fp.seek(payload_info.header_offset)
115 data = fp.read(zipfile.sizeFileHeader)
116 fheader = struct.unpack(zipfile.structFileHeader, data)
117 # Last two fields of local file header are filename length and
118 # extra length
119 filename_len = fheader[-2]
120 extra_len = fheader[-1]
121 self.offset = payload_info.header_offset
122 self.offset += zipfile.sizeFileHeader
123 self.offset += filename_len + extra_len
124 self.size = payload_info.file_size
125 fp.seek(self.offset)
126 payload_header = fp.read(4)
127 if payload_header != self.PAYLOAD_MAGIC_HEADER:
128 logging.warning(
Kelvin Zhang07676f52020-12-01 10:45:09 -0500129 "Invalid header, expected %s, got %s."
Kelvin Zhangaba70ab2020-08-04 10:32:59 -0400130 "Either the offset is not correct, or payload is corrupted",
131 binascii.hexlify(self.PAYLOAD_MAGIC_HEADER),
Kelvin Zhang07676f52020-12-01 10:45:09 -0500132 binascii.hexlify(payload_header))
Tianjie Xu3f9be772019-11-02 18:31:50 -0700133
134 property_entry = (self.SECONDARY_OTA_PAYLOAD_PROPERTIES_TXT if
135 secondary_payload else self.OTA_PAYLOAD_PROPERTIES_TXT)
136 self.properties = otazip.read(property_entry)
Alex Deymo6751bbe2017-03-21 11:20:02 -0700137
138
139class UpdateHandler(BaseHTTPServer.BaseHTTPRequestHandler):
140 """A HTTPServer that supports single-range requests.
141
142 Attributes:
143 serving_payload: path to the only payload file we are serving.
Sen Jiang3b15b592017-09-26 18:21:04 -0700144 serving_range: the start offset and size tuple of the payload.
Alex Deymo6751bbe2017-03-21 11:20:02 -0700145 """
146
147 @staticmethod
Sen Jiang10485592017-08-15 18:20:24 -0700148 def _parse_range(range_str, file_size):
Alex Deymo6751bbe2017-03-21 11:20:02 -0700149 """Parse an HTTP range string.
150
151 Args:
152 range_str: HTTP Range header in the request, not including "Header:".
153 file_size: total size of the serving file.
154
155 Returns:
156 A tuple (start_range, end_range) with the range of bytes requested.
157 """
158 start_range = 0
159 end_range = file_size
160
161 if range_str:
162 range_str = range_str.split('=', 1)[1]
163 s, e = range_str.split('-', 1)
164 if s:
165 start_range = int(s)
166 if e:
167 end_range = int(e) + 1
168 elif e:
169 if int(e) < file_size:
170 start_range = file_size - int(e)
171 return start_range, end_range
172
Alex Deymo6751bbe2017-03-21 11:20:02 -0700173 def do_GET(self): # pylint: disable=invalid-name
174 """Reply with the requested payload file."""
175 if self.path != '/payload':
176 self.send_error(404, 'Unknown request')
177 return
178
179 if not self.serving_payload:
180 self.send_error(500, 'No serving payload set')
181 return
182
183 try:
184 f = open(self.serving_payload, 'rb')
185 except IOError:
186 self.send_error(404, 'File not found')
187 return
188 # Handle the range request.
189 if 'Range' in self.headers:
190 self.send_response(206)
191 else:
192 self.send_response(200)
193
Sen Jiang3b15b592017-09-26 18:21:04 -0700194 serving_start, serving_size = self.serving_range
Sen Jiang10485592017-08-15 18:20:24 -0700195 start_range, end_range = self._parse_range(self.headers.get('range'),
Sen Jiang3b15b592017-09-26 18:21:04 -0700196 serving_size)
Alex Deymo6751bbe2017-03-21 11:20:02 -0700197 logging.info('Serving request for %s from %s [%d, %d) length: %d',
Sen Jiang3b15b592017-09-26 18:21:04 -0700198 self.path, self.serving_payload, serving_start + start_range,
199 serving_start + end_range, end_range - start_range)
Alex Deymo6751bbe2017-03-21 11:20:02 -0700200
201 self.send_header('Accept-Ranges', 'bytes')
202 self.send_header('Content-Range',
203 'bytes ' + str(start_range) + '-' + str(end_range - 1) +
204 '/' + str(end_range - start_range))
205 self.send_header('Content-Length', end_range - start_range)
206
Sen Jiang3b15b592017-09-26 18:21:04 -0700207 stat = os.fstat(f.fileno())
Alex Deymo6751bbe2017-03-21 11:20:02 -0700208 self.send_header('Last-Modified', self.date_time_string(stat.st_mtime))
209 self.send_header('Content-type', 'application/octet-stream')
210 self.end_headers()
211
Sen Jiang3b15b592017-09-26 18:21:04 -0700212 f.seek(serving_start + start_range)
Alex Deymo6751bbe2017-03-21 11:20:02 -0700213 CopyFileObjLength(f, self.wfile, copy_length=end_range - start_range)
214
Sen Jianga1784b72017-08-09 17:42:36 -0700215 def do_POST(self): # pylint: disable=invalid-name
216 """Reply with the omaha response xml."""
217 if self.path != '/update':
218 self.send_error(404, 'Unknown request')
219 return
220
221 if not self.serving_payload:
222 self.send_error(500, 'No serving payload set')
223 return
224
225 try:
226 f = open(self.serving_payload, 'rb')
227 except IOError:
228 self.send_error(404, 'File not found')
229 return
230
Sen Jiang144f9f82017-09-26 15:49:45 -0700231 content_length = int(self.headers.getheader('Content-Length'))
232 request_xml = self.rfile.read(content_length)
233 xml_root = xml.etree.ElementTree.fromstring(request_xml)
234 appid = None
235 for app in xml_root.iter('app'):
236 if 'appid' in app.attrib:
237 appid = app.attrib['appid']
238 break
239 if not appid:
240 self.send_error(400, 'No appid in Omaha request')
241 return
242
Sen Jianga1784b72017-08-09 17:42:36 -0700243 self.send_response(200)
244 self.send_header("Content-type", "text/xml")
245 self.end_headers()
246
Sen Jiang3b15b592017-09-26 18:21:04 -0700247 serving_start, serving_size = self.serving_range
248 sha256 = hashlib.sha256()
249 f.seek(serving_start)
250 bytes_to_hash = serving_size
251 while bytes_to_hash:
252 buf = f.read(min(bytes_to_hash, 1024 * 1024))
253 if not buf:
254 self.send_error(500, 'Payload too small')
255 return
256 sha256.update(buf)
257 bytes_to_hash -= len(buf)
258
259 payload = update_payload.Payload(f, payload_file_offset=serving_start)
Sen Jianga1784b72017-08-09 17:42:36 -0700260 payload.Init()
261
Sen Jiang144f9f82017-09-26 15:49:45 -0700262 response_xml = '''
Sen Jianga1784b72017-08-09 17:42:36 -0700263 <?xml version="1.0" encoding="UTF-8"?>
264 <response protocol="3.0">
Sen Jiang144f9f82017-09-26 15:49:45 -0700265 <app appid="{appid}">
Sen Jianga1784b72017-08-09 17:42:36 -0700266 <updatecheck status="ok">
267 <urls>
Sen Jiang144f9f82017-09-26 15:49:45 -0700268 <url codebase="http://127.0.0.1:{port}/"/>
Sen Jianga1784b72017-08-09 17:42:36 -0700269 </urls>
270 <manifest version="0.0.0.1">
271 <actions>
272 <action event="install" run="payload"/>
Sen Jiang144f9f82017-09-26 15:49:45 -0700273 <action event="postinstall" MetadataSize="{metadata_size}"/>
Sen Jianga1784b72017-08-09 17:42:36 -0700274 </actions>
275 <packages>
Sen Jiang144f9f82017-09-26 15:49:45 -0700276 <package hash_sha256="{payload_hash}" name="payload" size="{payload_size}"/>
Sen Jianga1784b72017-08-09 17:42:36 -0700277 </packages>
278 </manifest>
279 </updatecheck>
280 </app>
281 </response>
Sen Jiang144f9f82017-09-26 15:49:45 -0700282 '''.format(appid=appid, port=DEVICE_PORT,
Sen Jiang3b15b592017-09-26 18:21:04 -0700283 metadata_size=payload.metadata_size,
284 payload_hash=sha256.hexdigest(),
285 payload_size=serving_size)
Sen Jiang144f9f82017-09-26 15:49:45 -0700286 self.wfile.write(response_xml.strip())
Sen Jianga1784b72017-08-09 17:42:36 -0700287 return
288
289
Alex Deymo6751bbe2017-03-21 11:20:02 -0700290class ServerThread(threading.Thread):
291 """A thread for serving HTTP requests."""
292
Sen Jiang3b15b592017-09-26 18:21:04 -0700293 def __init__(self, ota_filename, serving_range):
Alex Deymo6751bbe2017-03-21 11:20:02 -0700294 threading.Thread.__init__(self)
Sen Jiang3b15b592017-09-26 18:21:04 -0700295 # serving_payload and serving_range are class attributes and the
296 # UpdateHandler class is instantiated with every request.
Alex Deymo6751bbe2017-03-21 11:20:02 -0700297 UpdateHandler.serving_payload = ota_filename
Sen Jiang3b15b592017-09-26 18:21:04 -0700298 UpdateHandler.serving_range = serving_range
Alex Deymo6751bbe2017-03-21 11:20:02 -0700299 self._httpd = BaseHTTPServer.HTTPServer(('127.0.0.1', 0), UpdateHandler)
300 self.port = self._httpd.server_port
301
302 def run(self):
303 try:
304 self._httpd.serve_forever()
305 except (KeyboardInterrupt, socket.error):
306 pass
307 logging.info('Server Terminated')
308
309 def StopServer(self):
Kelvin Zhang4b883ea2020-10-08 13:26:44 -0400310 self._httpd.shutdown()
Alex Deymo6751bbe2017-03-21 11:20:02 -0700311 self._httpd.socket.close()
312
313
Sen Jiang3b15b592017-09-26 18:21:04 -0700314def StartServer(ota_filename, serving_range):
315 t = ServerThread(ota_filename, serving_range)
Alex Deymo6751bbe2017-03-21 11:20:02 -0700316 t.start()
317 return t
318
319
Tianjie Xu3f9be772019-11-02 18:31:50 -0700320def AndroidUpdateCommand(ota_filename, secondary, payload_url, extra_headers):
Alex Deymo6751bbe2017-03-21 11:20:02 -0700321 """Return the command to run to start the update in the Android device."""
Tianjie Xu3f9be772019-11-02 18:31:50 -0700322 ota = AndroidOTAPackage(ota_filename, secondary)
Alex Deymo6751bbe2017-03-21 11:20:02 -0700323 headers = ota.properties
Kelvin Zhang4b883ea2020-10-08 13:26:44 -0400324 headers += b'USER_AGENT=Dalvik (something, something)\n'
325 headers += b'NETWORK_ID=0\n'
326 headers += extra_headers.encode()
Alex Deymo6751bbe2017-03-21 11:20:02 -0700327
328 return ['update_engine_client', '--update', '--follow',
329 '--payload=%s' % payload_url, '--offset=%d' % ota.offset,
Kelvin Zhang4b883ea2020-10-08 13:26:44 -0400330 '--size=%d' % ota.size, '--headers="%s"' % headers.decode()]
Alex Deymo6751bbe2017-03-21 11:20:02 -0700331
332
Sen Jianga1784b72017-08-09 17:42:36 -0700333def OmahaUpdateCommand(omaha_url):
334 """Return the command to run to start the update in a device using Omaha."""
335 return ['update_engine_client', '--update', '--follow',
336 '--omaha_url=%s' % omaha_url]
337
338
Alex Deymo6751bbe2017-03-21 11:20:02 -0700339class AdbHost(object):
340 """Represents a device connected via ADB."""
341
342 def __init__(self, device_serial=None):
343 """Construct an instance.
344
345 Args:
346 device_serial: options string serial number of attached device.
347 """
348 self._device_serial = device_serial
349 self._command_prefix = ['adb']
350 if self._device_serial:
351 self._command_prefix += ['-s', self._device_serial]
352
353 def adb(self, command):
354 """Run an ADB command like "adb push".
355
356 Args:
357 command: list of strings containing command and arguments to run
358
359 Returns:
360 the program's return code.
361
362 Raises:
363 subprocess.CalledProcessError on command exit != 0.
364 """
365 command = self._command_prefix + command
366 logging.info('Running: %s', ' '.join(str(x) for x in command))
367 p = subprocess.Popen(command, universal_newlines=True)
368 p.wait()
369 return p.returncode
370
Sen Jianga1784b72017-08-09 17:42:36 -0700371 def adb_output(self, command):
372 """Run an ADB command like "adb push" and return the output.
373
374 Args:
375 command: list of strings containing command and arguments to run
376
377 Returns:
378 the program's output as a string.
379
380 Raises:
381 subprocess.CalledProcessError on command exit != 0.
382 """
383 command = self._command_prefix + command
384 logging.info('Running: %s', ' '.join(str(x) for x in command))
385 return subprocess.check_output(command, universal_newlines=True)
386
Alex Deymo6751bbe2017-03-21 11:20:02 -0700387
388def main():
389 parser = argparse.ArgumentParser(description='Android A/B OTA helper.')
Sen Jiang3b15b592017-09-26 18:21:04 -0700390 parser.add_argument('otafile', metavar='PAYLOAD', type=str,
391 help='the OTA package file (a .zip file) or raw payload \
392 if device uses Omaha.')
Alex Deymo6751bbe2017-03-21 11:20:02 -0700393 parser.add_argument('--file', action='store_true',
394 help='Push the file to the device before updating.')
395 parser.add_argument('--no-push', action='store_true',
396 help='Skip the "push" command when using --file')
397 parser.add_argument('-s', type=str, default='', metavar='DEVICE',
398 help='The specific device to use.')
399 parser.add_argument('--no-verbose', action='store_true',
400 help='Less verbose output')
Sen Jianga1784b72017-08-09 17:42:36 -0700401 parser.add_argument('--public-key', type=str, default='',
402 help='Override the public key used to verify payload.')
Sen Jiang6fbfd7d2017-10-31 16:16:56 -0700403 parser.add_argument('--extra-headers', type=str, default='',
404 help='Extra headers to pass to the device.')
Tianjie Xu3f9be772019-11-02 18:31:50 -0700405 parser.add_argument('--secondary', action='store_true',
406 help='Update with the secondary payload in the package.')
Kelvin Zhang8212f532020-11-13 16:00:00 -0500407 parser.add_argument('--no-slot-switch', action='store_true',
408 help='Do not perform slot switch after the update.')
Kelvin Zhang51aad992021-02-19 14:46:28 -0500409 parser.add_argument('--allocate_only', action='store_true',
410 help='Allocate space for this OTA, instead of actually \
411 applying the OTA.')
Alex Deymo6751bbe2017-03-21 11:20:02 -0700412 args = parser.parse_args()
413 logging.basicConfig(
414 level=logging.WARNING if args.no_verbose else logging.INFO)
415
416 dut = AdbHost(args.s)
417
418 server_thread = None
419 # List of commands to execute on exit.
420 finalize_cmds = []
421 # Commands to execute when canceling an update.
422 cancel_cmd = ['shell', 'su', '0', 'update_engine_client', '--cancel']
423 # List of commands to perform the update.
424 cmds = []
425
Sen Jianga1784b72017-08-09 17:42:36 -0700426 help_cmd = ['shell', 'su', '0', 'update_engine_client', '--help']
427 use_omaha = 'omaha' in dut.adb_output(help_cmd)
428
Kelvin Zhang51aad992021-02-19 14:46:28 -0500429 if args.allocate_only:
430 metadata_path = "/data/ota_package/metadata"
431 payload = update_payload.Payload(args.otafile)
432 payload.Init()
433 with tempfile.TemporaryDirectory() as tmpdir:
434 with zipfile.ZipFile(args.otafile, "r") as zfp:
435 extracted_path = os.path.join(tmpdir, "payload.bin")
436 with zfp.open("payload.bin") as payload_fp, \
437 open(extracted_path, "wb") as output_fp:
438 # Only extract the first |data_offset| bytes from the payload.
439 # This is because allocateSpaceForPayload only needs to see
440 # the manifest, not the entire payload.
441 # Extracting the entire payload works, but is slow for full
442 # OTA.
443 output_fp.write(payload_fp.read(payload.data_offset))
444
445 dut.adb([
446 "push",
447 extracted_path,
448 metadata_path
449 ])
450 dut.adb([
451 "shell", "update_engine_client", "--allocate",
452 "--metadata={}".format(metadata_path)])
453 # Return 0, as we are executing ADB commands here, no work needed after
454 # this point
455 return 0
456
Kelvin Zhang8212f532020-11-13 16:00:00 -0500457 if args.no_slot_switch:
458 args.extra_headers += "\nSWITCH_SLOT_ON_REBOOT=0"
459
Alex Deymo6751bbe2017-03-21 11:20:02 -0700460 if args.file:
461 # Update via pushing a file to /data.
462 device_ota_file = os.path.join(OTA_PACKAGE_PATH, 'debug.zip')
463 payload_url = 'file://' + device_ota_file
464 if not args.no_push:
Tao Baoabb45a52017-10-25 11:13:03 -0700465 data_local_tmp_file = '/data/local/tmp/debug.zip'
466 cmds.append(['push', args.otafile, data_local_tmp_file])
467 cmds.append(['shell', 'su', '0', 'mv', data_local_tmp_file,
468 device_ota_file])
469 cmds.append(['shell', 'su', '0', 'chcon',
470 'u:object_r:ota_package_file:s0', device_ota_file])
Alex Deymo6751bbe2017-03-21 11:20:02 -0700471 cmds.append(['shell', 'su', '0', 'chown', 'system:cache', device_ota_file])
472 cmds.append(['shell', 'su', '0', 'chmod', '0660', device_ota_file])
473 else:
474 # Update via sending the payload over the network with an "adb reverse"
475 # command.
Sen Jianga1784b72017-08-09 17:42:36 -0700476 payload_url = 'http://127.0.0.1:%d/payload' % DEVICE_PORT
Sen Jiang3b15b592017-09-26 18:21:04 -0700477 if use_omaha and zipfile.is_zipfile(args.otafile):
Tianjie Xu3f9be772019-11-02 18:31:50 -0700478 ota = AndroidOTAPackage(args.otafile, args.secondary)
Sen Jiang3b15b592017-09-26 18:21:04 -0700479 serving_range = (ota.offset, ota.size)
480 else:
481 serving_range = (0, os.stat(args.otafile).st_size)
482 server_thread = StartServer(args.otafile, serving_range)
Alex Deymo6751bbe2017-03-21 11:20:02 -0700483 cmds.append(
Sen Jianga1784b72017-08-09 17:42:36 -0700484 ['reverse', 'tcp:%d' % DEVICE_PORT, 'tcp:%d' % server_thread.port])
485 finalize_cmds.append(['reverse', '--remove', 'tcp:%d' % DEVICE_PORT])
486
487 if args.public_key:
488 payload_key_dir = os.path.dirname(PAYLOAD_KEY_PATH)
489 cmds.append(
490 ['shell', 'su', '0', 'mount', '-t', 'tmpfs', 'tmpfs', payload_key_dir])
491 # Allow adb push to payload_key_dir
492 cmds.append(['shell', 'su', '0', 'chcon', 'u:object_r:shell_data_file:s0',
493 payload_key_dir])
494 cmds.append(['push', args.public_key, PAYLOAD_KEY_PATH])
495 # Allow update_engine to read it.
496 cmds.append(['shell', 'su', '0', 'chcon', '-R', 'u:object_r:system_file:s0',
497 payload_key_dir])
498 finalize_cmds.append(['shell', 'su', '0', 'umount', payload_key_dir])
Alex Deymo6751bbe2017-03-21 11:20:02 -0700499
500 try:
501 # The main update command using the configured payload_url.
Sen Jianga1784b72017-08-09 17:42:36 -0700502 if use_omaha:
503 update_cmd = \
504 OmahaUpdateCommand('http://127.0.0.1:%d/update' % DEVICE_PORT)
505 else:
Tianjie Xu3f9be772019-11-02 18:31:50 -0700506 update_cmd = AndroidUpdateCommand(args.otafile, args.secondary,
507 payload_url, args.extra_headers)
Alex Deymo6751bbe2017-03-21 11:20:02 -0700508 cmds.append(['shell', 'su', '0'] + update_cmd)
509
510 for cmd in cmds:
511 dut.adb(cmd)
512 except KeyboardInterrupt:
513 dut.adb(cancel_cmd)
514 finally:
515 if server_thread:
516 server_thread.StopServer()
517 for cmd in finalize_cmds:
518 dut.adb(cmd)
519
520 return 0
521
Andrew Lassalle165843c2019-11-05 13:30:34 -0800522
Alex Deymo6751bbe2017-03-21 11:20:02 -0700523if __name__ == '__main__':
524 sys.exit(main())