blob: 2b975181b3232caff64ddb4cca04a8bded6da966 [file] [log] [blame]
Doug Zongkereef39442009-04-02 12:14:19 -07001# Copyright (C) 2008 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
Tao Bao89fbb0f2017-01-10 10:47:58 -080015from __future__ import print_function
16
Doug Zongkerea5d7a92010-09-12 15:26:16 -070017import copy
Doug Zongker8ce7c252009-05-22 13:34:54 -070018import errno
Doug Zongkereef39442009-04-02 12:14:19 -070019import getopt
20import getpass
Doug Zongker05d3dea2009-06-22 11:32:31 -070021import imp
Doug Zongkereef39442009-04-02 12:14:19 -070022import os
Ying Wang7e6d4e42010-12-13 16:25:36 -080023import platform
Doug Zongkereef39442009-04-02 12:14:19 -070024import re
T.R. Fullhart37e10522013-03-18 10:31:26 -070025import shlex
Doug Zongkereef39442009-04-02 12:14:19 -070026import shutil
27import subprocess
28import sys
29import tempfile
Doug Zongkerea5d7a92010-09-12 15:26:16 -070030import threading
31import time
Doug Zongker048e7ca2009-06-15 14:31:53 -070032import zipfile
Doug Zongkereef39442009-04-02 12:14:19 -070033
Doug Zongkerab7ca1d2014-08-26 10:40:28 -070034import blockimgdiff
35
Tao Baof3282b42015-04-01 11:21:55 -070036from hashlib import sha1 as sha1
Doug Zongker55d93282011-01-25 17:03:34 -080037
Doug Zongkereef39442009-04-02 12:14:19 -070038
Dan Albert8b72aef2015-03-23 19:13:21 -070039class Options(object):
40 def __init__(self):
41 platform_search_path = {
42 "linux2": "out/host/linux-x86",
43 "darwin": "out/host/darwin-x86",
Doug Zongker85448772014-09-09 14:59:20 -070044 }
Doug Zongker85448772014-09-09 14:59:20 -070045
Dan Albert8b72aef2015-03-23 19:13:21 -070046 self.search_path = platform_search_path.get(sys.platform, None)
47 self.signapk_path = "framework/signapk.jar" # Relative to search_path
Alex Klyubin9667b182015-12-10 13:38:50 -080048 self.signapk_shared_library_path = "lib64" # Relative to search_path
Dan Albert8b72aef2015-03-23 19:13:21 -070049 self.extra_signapk_args = []
50 self.java_path = "java" # Use the one on the path by default.
Tao Baoe95540e2016-11-08 12:08:53 -080051 self.java_args = ["-Xmx2048m"] # The default JVM args.
Dan Albert8b72aef2015-03-23 19:13:21 -070052 self.public_key_suffix = ".x509.pem"
53 self.private_key_suffix = ".pk8"
Dan Albertcd9ecc02015-03-27 16:37:23 -070054 # use otatools built boot_signer by default
55 self.boot_signer_path = "boot_signer"
Baligh Uddin601ddea2015-06-09 15:48:14 -070056 self.boot_signer_args = []
57 self.verity_signer_path = None
58 self.verity_signer_args = []
Dan Albert8b72aef2015-03-23 19:13:21 -070059 self.verbose = False
60 self.tempfiles = []
61 self.device_specific = None
62 self.extras = {}
63 self.info_dict = None
Tao Bao6f0b2192015-10-13 16:37:12 -070064 self.source_info_dict = None
65 self.target_info_dict = None
Dan Albert8b72aef2015-03-23 19:13:21 -070066 self.worker_threads = None
Tao Bao575d68a2015-08-07 19:49:45 -070067 # Stash size cannot exceed cache_size * threshold.
68 self.cache_size = None
69 self.stash_threshold = 0.8
Dan Albert8b72aef2015-03-23 19:13:21 -070070
71
72OPTIONS = Options()
Doug Zongkereef39442009-04-02 12:14:19 -070073
Doug Zongkerf6a53aa2009-12-15 15:06:55 -080074
75# Values for "certificate" in apkcerts that mean special things.
76SPECIAL_CERT_STRINGS = ("PRESIGNED", "EXTERNAL")
77
Tianjie Xu209db462016-05-24 17:34:52 -070078class ErrorCode(object):
79 """Define error_codes for failures that happen during the actual
80 update package installation.
81
82 Error codes 0-999 are reserved for failures before the package
83 installation (i.e. low battery, package verification failure).
84 Detailed code in 'bootable/recovery/error_code.h' """
85
86 SYSTEM_VERIFICATION_FAILURE = 1000
87 SYSTEM_UPDATE_FAILURE = 1001
88 SYSTEM_UNEXPECTED_CONTENTS = 1002
89 SYSTEM_NONZERO_CONTENTS = 1003
90 SYSTEM_RECOVER_FAILURE = 1004
91 VENDOR_VERIFICATION_FAILURE = 2000
92 VENDOR_UPDATE_FAILURE = 2001
93 VENDOR_UNEXPECTED_CONTENTS = 2002
94 VENDOR_NONZERO_CONTENTS = 2003
95 VENDOR_RECOVER_FAILURE = 2004
96 OEM_PROP_MISMATCH = 3000
97 FINGERPRINT_MISMATCH = 3001
98 THUMBPRINT_MISMATCH = 3002
99 OLDER_BUILD = 3003
100 DEVICE_MISMATCH = 3004
101 BAD_PATCH_FILE = 3005
102 INSUFFICIENT_CACHE_SPACE = 3006
103 TUNE_PARTITION_FAILURE = 3007
104 APPLY_PATCH_FAILURE = 3008
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800105
Dan Albert8b72aef2015-03-23 19:13:21 -0700106class ExternalError(RuntimeError):
107 pass
Doug Zongkereef39442009-04-02 12:14:19 -0700108
109
110def Run(args, **kwargs):
111 """Create and return a subprocess.Popen object, printing the command
112 line on the terminal if -v was specified."""
113 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800114 print(" running: ", " ".join(args))
Doug Zongkereef39442009-04-02 12:14:19 -0700115 return subprocess.Popen(args, **kwargs)
116
117
Ying Wang7e6d4e42010-12-13 16:25:36 -0800118def CloseInheritedPipes():
119 """ Gmake in MAC OS has file descriptor (PIPE) leak. We close those fds
120 before doing other work."""
121 if platform.system() != "Darwin":
122 return
123 for d in range(3, 1025):
124 try:
125 stat = os.fstat(d)
126 if stat is not None:
127 pipebit = stat[0] & 0x1000
128 if pipebit != 0:
129 os.close(d)
130 except OSError:
131 pass
132
133
Tao Bao2c15d9e2015-07-09 11:51:16 -0700134def LoadInfoDict(input_file, input_dir=None):
Doug Zongkerc19a8d52010-07-01 15:30:11 -0700135 """Read and parse the META/misc_info.txt key/value pairs from the
136 input target files and return a dict."""
137
Doug Zongkerc9253822014-02-04 12:17:58 -0800138 def read_helper(fn):
Dan Albert8b72aef2015-03-23 19:13:21 -0700139 if isinstance(input_file, zipfile.ZipFile):
140 return input_file.read(fn)
Doug Zongkerc9253822014-02-04 12:17:58 -0800141 else:
Dan Albert8b72aef2015-03-23 19:13:21 -0700142 path = os.path.join(input_file, *fn.split("/"))
Doug Zongkerc9253822014-02-04 12:17:58 -0800143 try:
144 with open(path) as f:
145 return f.read()
Dan Albert8b72aef2015-03-23 19:13:21 -0700146 except IOError as e:
Doug Zongkerc9253822014-02-04 12:17:58 -0800147 if e.errno == errno.ENOENT:
148 raise KeyError(fn)
Tao Bao6cd54732017-02-27 15:12:05 -0800149
Doug Zongkerc19a8d52010-07-01 15:30:11 -0700150 try:
Michael Runge6e836112014-04-15 17:40:21 -0700151 d = LoadDictionaryFromLines(read_helper("META/misc_info.txt").split("\n"))
Doug Zongker37974732010-09-16 17:44:38 -0700152 except KeyError:
Tao Bao6cd54732017-02-27 15:12:05 -0800153 raise ValueError("can't find META/misc_info.txt in input target-files")
Doug Zongkerc19a8d52010-07-01 15:30:11 -0700154
Tao Bao6cd54732017-02-27 15:12:05 -0800155 assert "recovery_api_version" in d
Tao Baod1de6f32017-03-01 16:38:48 -0800156 assert "fstab_version" in d
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800157
Tao Bao84e75682015-07-19 02:38:53 -0700158 # A few properties are stored as links to the files in the out/ directory.
159 # It works fine with the build system. However, they are no longer available
160 # when (re)generating from target_files zip. If input_dir is not None, we
161 # are doing repacking. Redirect those properties to the actual files in the
162 # unzipped directory.
Tao Bao2c15d9e2015-07-09 11:51:16 -0700163 if input_dir is not None:
Stephen Smalleyd3a803e2015-08-04 14:59:06 -0400164 # We carry a copy of file_contexts.bin under META/. If not available,
165 # search BOOT/RAMDISK/. Note that sometimes we may need a different file
Tao Bao84e75682015-07-19 02:38:53 -0700166 # to build images than the one running on device, such as when enabling
167 # system_root_image. In that case, we must have the one for image
168 # generation copied to META/.
Tao Bao79735a62015-08-28 10:52:03 -0700169 fc_basename = os.path.basename(d.get("selinux_fc", "file_contexts"))
170 fc_config = os.path.join(input_dir, "META", fc_basename)
Tao Bao84e75682015-07-19 02:38:53 -0700171 if d.get("system_root_image") == "true":
172 assert os.path.exists(fc_config)
Tao Bao2c15d9e2015-07-09 11:51:16 -0700173 if not os.path.exists(fc_config):
Tao Bao79735a62015-08-28 10:52:03 -0700174 fc_config = os.path.join(input_dir, "BOOT", "RAMDISK", fc_basename)
Tao Bao2c15d9e2015-07-09 11:51:16 -0700175 if not os.path.exists(fc_config):
176 fc_config = None
177
178 if fc_config:
179 d["selinux_fc"] = fc_config
180
Tao Bao84e75682015-07-19 02:38:53 -0700181 # Similarly we need to redirect "ramdisk_dir" and "ramdisk_fs_config".
182 if d.get("system_root_image") == "true":
183 d["ramdisk_dir"] = os.path.join(input_dir, "ROOT")
184 d["ramdisk_fs_config"] = os.path.join(
185 input_dir, "META", "root_filesystem_config.txt")
186
Tao Baof54216f2016-03-29 15:12:37 -0700187 # Redirect {system,vendor}_base_fs_file.
188 if "system_base_fs_file" in d:
189 basename = os.path.basename(d["system_base_fs_file"])
190 system_base_fs_file = os.path.join(input_dir, "META", basename)
Tao Baob079b502016-05-03 08:01:19 -0700191 if os.path.exists(system_base_fs_file):
192 d["system_base_fs_file"] = system_base_fs_file
193 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800194 print("Warning: failed to find system base fs file: %s" % (
195 system_base_fs_file,))
Tao Baob079b502016-05-03 08:01:19 -0700196 del d["system_base_fs_file"]
Tao Baof54216f2016-03-29 15:12:37 -0700197
198 if "vendor_base_fs_file" in d:
199 basename = os.path.basename(d["vendor_base_fs_file"])
200 vendor_base_fs_file = os.path.join(input_dir, "META", basename)
Tao Baob079b502016-05-03 08:01:19 -0700201 if os.path.exists(vendor_base_fs_file):
202 d["vendor_base_fs_file"] = vendor_base_fs_file
203 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800204 print("Warning: failed to find vendor base fs file: %s" % (
205 vendor_base_fs_file,))
Tao Baob079b502016-05-03 08:01:19 -0700206 del d["vendor_base_fs_file"]
Tao Baof54216f2016-03-29 15:12:37 -0700207
Doug Zongker37974732010-09-16 17:44:38 -0700208 try:
Doug Zongkerc9253822014-02-04 12:17:58 -0800209 data = read_helper("META/imagesizes.txt")
Doug Zongker37974732010-09-16 17:44:38 -0700210 for line in data.split("\n"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700211 if not line:
212 continue
Doug Zongker1684d9c2010-09-17 07:44:38 -0700213 name, value = line.split(" ", 1)
Dan Albert8b72aef2015-03-23 19:13:21 -0700214 if not value:
215 continue
Doug Zongker37974732010-09-16 17:44:38 -0700216 if name == "blocksize":
217 d[name] = value
218 else:
219 d[name + "_size"] = value
220 except KeyError:
221 pass
222
223 def makeint(key):
224 if key in d:
225 d[key] = int(d[key], 0)
226
227 makeint("recovery_api_version")
228 makeint("blocksize")
229 makeint("system_size")
Daniel Rosenbergf4eabc32014-07-10 15:42:38 -0700230 makeint("vendor_size")
Doug Zongker37974732010-09-16 17:44:38 -0700231 makeint("userdata_size")
Ying Wang9f8e8db2011-11-04 11:37:01 -0700232 makeint("cache_size")
Doug Zongker37974732010-09-16 17:44:38 -0700233 makeint("recovery_size")
234 makeint("boot_size")
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800235 makeint("fstab_version")
Doug Zongkerc19a8d52010-07-01 15:30:11 -0700236
Tianjie Xucfa86222016-03-07 16:31:19 -0800237 system_root_image = d.get("system_root_image", None) == "true"
238 if d.get("no_recovery", None) != "true":
239 recovery_fstab_path = "RECOVERY/RAMDISK/etc/recovery.fstab"
Tao Bao48550cc2015-11-19 17:05:46 -0800240 d["fstab"] = LoadRecoveryFSTab(read_helper, d["fstab_version"],
Tianjie Xucfa86222016-03-07 16:31:19 -0800241 recovery_fstab_path, system_root_image)
242 elif d.get("recovery_as_boot", None) == "true":
243 recovery_fstab_path = "BOOT/RAMDISK/etc/recovery.fstab"
244 d["fstab"] = LoadRecoveryFSTab(read_helper, d["fstab_version"],
245 recovery_fstab_path, system_root_image)
246 else:
247 d["fstab"] = None
248
Doug Zongkerc9253822014-02-04 12:17:58 -0800249 d["build.prop"] = LoadBuildProp(read_helper)
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700250 return d
251
Tao Baod1de6f32017-03-01 16:38:48 -0800252
Doug Zongkerc9253822014-02-04 12:17:58 -0800253def LoadBuildProp(read_helper):
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700254 try:
Doug Zongkerc9253822014-02-04 12:17:58 -0800255 data = read_helper("SYSTEM/build.prop")
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700256 except KeyError:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800257 print("Warning: could not find SYSTEM/build.prop in %s" % (zip,))
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700258 data = ""
Michael Runge6e836112014-04-15 17:40:21 -0700259 return LoadDictionaryFromLines(data.split("\n"))
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700260
Tao Baod1de6f32017-03-01 16:38:48 -0800261
Michael Runge6e836112014-04-15 17:40:21 -0700262def LoadDictionaryFromLines(lines):
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700263 d = {}
Michael Runge6e836112014-04-15 17:40:21 -0700264 for line in lines:
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700265 line = line.strip()
Dan Albert8b72aef2015-03-23 19:13:21 -0700266 if not line or line.startswith("#"):
267 continue
Ying Wang114b46f2014-04-15 11:24:00 -0700268 if "=" in line:
269 name, value = line.split("=", 1)
270 d[name] = value
Doug Zongkerc19a8d52010-07-01 15:30:11 -0700271 return d
272
Tao Baod1de6f32017-03-01 16:38:48 -0800273
Tianjie Xucfa86222016-03-07 16:31:19 -0800274def LoadRecoveryFSTab(read_helper, fstab_version, recovery_fstab_path,
275 system_root_image=False):
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700276 class Partition(object):
Tao Baod1de6f32017-03-01 16:38:48 -0800277 def __init__(self, mount_point, fs_type, device, length, context):
Dan Albert8b72aef2015-03-23 19:13:21 -0700278 self.mount_point = mount_point
279 self.fs_type = fs_type
280 self.device = device
281 self.length = length
Tao Bao548eb762015-06-10 12:32:41 -0700282 self.context = context
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700283
284 try:
Tianjie Xucfa86222016-03-07 16:31:19 -0800285 data = read_helper(recovery_fstab_path)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700286 except KeyError:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800287 print("Warning: could not find {}".format(recovery_fstab_path))
Jeff Davidson033fbe22011-10-26 18:08:09 -0700288 data = ""
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700289
Tao Baod1de6f32017-03-01 16:38:48 -0800290 assert fstab_version == 2
291
292 d = {}
293 for line in data.split("\n"):
294 line = line.strip()
295 if not line or line.startswith("#"):
296 continue
297
298 # <src> <mnt_point> <type> <mnt_flags and options> <fs_mgr_flags>
299 pieces = line.split()
300 if len(pieces) != 5:
301 raise ValueError("malformed recovery.fstab line: \"%s\"" % (line,))
302
303 # Ignore entries that are managed by vold.
304 options = pieces[4]
305 if "voldmanaged=" in options:
306 continue
307
308 # It's a good line, parse it.
309 length = 0
310 options = options.split(",")
311 for i in options:
312 if i.startswith("length="):
313 length = int(i[7:])
Doug Zongker086cbb02011-02-17 15:54:20 -0800314 else:
Tao Baod1de6f32017-03-01 16:38:48 -0800315 # Ignore all unknown options in the unified fstab.
Dan Albert8b72aef2015-03-23 19:13:21 -0700316 continue
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800317
Tao Baod1de6f32017-03-01 16:38:48 -0800318 mount_flags = pieces[3]
319 # Honor the SELinux context if present.
320 context = None
321 for i in mount_flags.split(","):
322 if i.startswith("context="):
323 context = i
Doug Zongker086cbb02011-02-17 15:54:20 -0800324
Tao Baod1de6f32017-03-01 16:38:48 -0800325 mount_point = pieces[1]
326 d[mount_point] = Partition(mount_point=mount_point, fs_type=pieces[2],
327 device=pieces[0], length=length, context=context)
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800328
Daniel Rosenberge6853b02015-06-05 17:59:27 -0700329 # / is used for the system mount point when the root directory is included in
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700330 # system. Other areas assume system is always at "/system" so point /system
331 # at /.
Daniel Rosenberge6853b02015-06-05 17:59:27 -0700332 if system_root_image:
333 assert not d.has_key("/system") and d.has_key("/")
334 d["/system"] = d["/"]
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700335 return d
336
337
Doug Zongker37974732010-09-16 17:44:38 -0700338def DumpInfoDict(d):
339 for k, v in sorted(d.items()):
Tao Bao89fbb0f2017-01-10 10:47:58 -0800340 print("%-25s = (%s) %s" % (k, type(v).__name__, v))
Doug Zongkerc19a8d52010-07-01 15:30:11 -0700341
Dan Albert8b72aef2015-03-23 19:13:21 -0700342
David Zeuthen2ce63ed2016-09-15 13:43:54 -0400343def AppendAVBSigningArgs(cmd):
344 """Append signing arguments for avbtool."""
345 keypath = OPTIONS.info_dict.get("board_avb_key_path", None)
346 algorithm = OPTIONS.info_dict.get("board_avb_algorithm", None)
347 if not keypath or not algorithm:
348 algorithm = "SHA256_RSA4096"
349 keypath = "external/avb/test/data/testkey_rsa4096.pem"
350 cmd.extend(["--key", keypath, "--algorithm", algorithm])
351
352
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700353def _BuildBootableImage(sourcedir, fs_config_file, info_dict=None,
Tao Baod42e97e2016-11-30 12:11:57 -0800354 has_ramdisk=False, two_step_image=False):
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700355 """Build a bootable image from the specified sourcedir.
Doug Zongkere1c31ba2009-06-23 17:40:35 -0700356
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700357 Take a kernel, cmdline, and optionally a ramdisk directory from the input (in
Tao Baod42e97e2016-11-30 12:11:57 -0800358 'sourcedir'), and turn them into a boot image. 'two_step_image' indicates if
359 we are building a two-step special image (i.e. building a recovery image to
360 be loaded into /boot in two-step OTAs).
361
362 Return the image data, or None if sourcedir does not appear to contains files
363 for building the requested image.
364 """
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700365
366 def make_ramdisk():
367 ramdisk_img = tempfile.NamedTemporaryFile()
368
369 if os.access(fs_config_file, os.F_OK):
370 cmd = ["mkbootfs", "-f", fs_config_file,
371 os.path.join(sourcedir, "RAMDISK")]
372 else:
373 cmd = ["mkbootfs", os.path.join(sourcedir, "RAMDISK")]
374 p1 = Run(cmd, stdout=subprocess.PIPE)
375 p2 = Run(["minigzip"], stdin=p1.stdout, stdout=ramdisk_img.file.fileno())
376
377 p2.wait()
378 p1.wait()
379 assert p1.returncode == 0, "mkbootfs of %s ramdisk failed" % (sourcedir,)
380 assert p2.returncode == 0, "minigzip of %s ramdisk failed" % (sourcedir,)
381
382 return ramdisk_img
383
384 if not os.access(os.path.join(sourcedir, "kernel"), os.F_OK):
385 return None
386
387 if has_ramdisk and not os.access(os.path.join(sourcedir, "RAMDISK"), os.F_OK):
Doug Zongkere1c31ba2009-06-23 17:40:35 -0700388 return None
Doug Zongkereef39442009-04-02 12:14:19 -0700389
Doug Zongkerd5131602012-08-02 14:46:42 -0700390 if info_dict is None:
391 info_dict = OPTIONS.info_dict
392
Doug Zongkereef39442009-04-02 12:14:19 -0700393 img = tempfile.NamedTemporaryFile()
394
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700395 if has_ramdisk:
396 ramdisk_img = make_ramdisk()
Doug Zongkereef39442009-04-02 12:14:19 -0700397
Bjorn Andersson612e2cd2012-11-25 16:53:44 -0800398 # use MKBOOTIMG from environ, or "mkbootimg" if empty or not set
399 mkbootimg = os.getenv('MKBOOTIMG') or "mkbootimg"
400
401 cmd = [mkbootimg, "--kernel", os.path.join(sourcedir, "kernel")]
Doug Zongker38a649f2009-06-17 09:07:09 -0700402
Benoit Fradina45a8682014-07-14 21:00:43 +0200403 fn = os.path.join(sourcedir, "second")
404 if os.access(fn, os.F_OK):
405 cmd.append("--second")
406 cmd.append(fn)
407
Doug Zongker171f1cd2009-06-15 22:36:37 -0700408 fn = os.path.join(sourcedir, "cmdline")
409 if os.access(fn, os.F_OK):
Doug Zongker38a649f2009-06-17 09:07:09 -0700410 cmd.append("--cmdline")
411 cmd.append(open(fn).read().rstrip("\n"))
412
413 fn = os.path.join(sourcedir, "base")
414 if os.access(fn, os.F_OK):
415 cmd.append("--base")
416 cmd.append(open(fn).read().rstrip("\n"))
417
Ying Wang4de6b5b2010-08-25 14:29:34 -0700418 fn = os.path.join(sourcedir, "pagesize")
419 if os.access(fn, os.F_OK):
420 cmd.append("--pagesize")
421 cmd.append(open(fn).read().rstrip("\n"))
422
Doug Zongkerd5131602012-08-02 14:46:42 -0700423 args = info_dict.get("mkbootimg_args", None)
424 if args and args.strip():
Jianxun Zhang09849492013-04-17 15:19:19 -0700425 cmd.extend(shlex.split(args))
Doug Zongkerd5131602012-08-02 14:46:42 -0700426
Sami Tolvanen3303d902016-03-15 16:49:30 +0000427 args = info_dict.get("mkbootimg_version_args", None)
428 if args and args.strip():
429 cmd.extend(shlex.split(args))
430
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700431 if has_ramdisk:
432 cmd.extend(["--ramdisk", ramdisk_img.name])
433
Tao Baod95e9fd2015-03-29 23:07:41 -0700434 img_unsigned = None
435 if info_dict.get("vboot", None):
436 img_unsigned = tempfile.NamedTemporaryFile()
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700437 cmd.extend(["--output", img_unsigned.name])
Tao Baod95e9fd2015-03-29 23:07:41 -0700438 else:
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700439 cmd.extend(["--output", img.name])
Doug Zongker38a649f2009-06-17 09:07:09 -0700440
441 p = Run(cmd, stdout=subprocess.PIPE)
Doug Zongkereef39442009-04-02 12:14:19 -0700442 p.communicate()
Doug Zongkere1c31ba2009-06-23 17:40:35 -0700443 assert p.returncode == 0, "mkbootimg of %s image failed" % (
444 os.path.basename(sourcedir),)
Doug Zongkereef39442009-04-02 12:14:19 -0700445
Sami Tolvanen8b3f08b2015-04-07 15:08:59 +0100446 if (info_dict.get("boot_signer", None) == "true" and
447 info_dict.get("verity_key", None)):
Tao Baod42e97e2016-11-30 12:11:57 -0800448 # Hard-code the path as "/boot" for two-step special recovery image (which
449 # will be loaded into /boot during the two-step OTA).
450 if two_step_image:
451 path = "/boot"
452 else:
453 path = "/" + os.path.basename(sourcedir).lower()
Baligh Uddin601ddea2015-06-09 15:48:14 -0700454 cmd = [OPTIONS.boot_signer_path]
455 cmd.extend(OPTIONS.boot_signer_args)
456 cmd.extend([path, img.name,
457 info_dict["verity_key"] + ".pk8",
458 info_dict["verity_key"] + ".x509.pem", img.name])
Geremy Condra95ebe7a2014-08-19 17:27:56 -0700459 p = Run(cmd, stdout=subprocess.PIPE)
460 p.communicate()
461 assert p.returncode == 0, "boot_signer of %s image failed" % path
462
Tao Baod95e9fd2015-03-29 23:07:41 -0700463 # Sign the image if vboot is non-empty.
464 elif info_dict.get("vboot", None):
465 path = "/" + os.path.basename(sourcedir).lower()
466 img_keyblock = tempfile.NamedTemporaryFile()
Tao Bao4f104d12017-02-17 23:21:31 -0800467 # We have switched from the prebuilt futility binary to using the tool
468 # (futility-host) built from the source. Override the setting in the old
469 # TF.zip.
470 futility = info_dict["futility"]
471 if futility.startswith("prebuilts/"):
472 futility = "futility-host"
473 cmd = [info_dict["vboot_signer_cmd"], futility,
Tao Baod95e9fd2015-03-29 23:07:41 -0700474 img_unsigned.name, info_dict["vboot_key"] + ".vbpubk",
Furquan Shaikh852b8de2015-08-10 11:43:45 -0700475 info_dict["vboot_key"] + ".vbprivk",
476 info_dict["vboot_subkey"] + ".vbprivk",
477 img_keyblock.name,
Tao Baod95e9fd2015-03-29 23:07:41 -0700478 img.name]
479 p = Run(cmd, stdout=subprocess.PIPE)
480 p.communicate()
481 assert p.returncode == 0, "vboot_signer of %s image failed" % path
482
Tao Baof3282b42015-04-01 11:21:55 -0700483 # Clean up the temp files.
484 img_unsigned.close()
485 img_keyblock.close()
486
David Zeuthen2ce63ed2016-09-15 13:43:54 -0400487 # AVB: if enabled, calculate and add hash to boot.img.
Tao Baob31b94e2016-09-29 21:59:06 -0700488 if info_dict.get("board_avb_enable", None) == "true":
David Zeuthen2ce63ed2016-09-15 13:43:54 -0400489 avbtool = os.getenv('AVBTOOL') or "avbtool"
Tao Baob31b94e2016-09-29 21:59:06 -0700490 part_size = info_dict.get("boot_size", None)
David Zeuthen2ce63ed2016-09-15 13:43:54 -0400491 cmd = [avbtool, "add_hash_footer", "--image", img.name,
492 "--partition_size", str(part_size), "--partition_name", "boot"]
493 AppendAVBSigningArgs(cmd)
Tao Baob31b94e2016-09-29 21:59:06 -0700494 args = info_dict.get("board_avb_boot_add_hash_footer_args", None)
David Zeuthen2ce63ed2016-09-15 13:43:54 -0400495 if args and args.strip():
496 cmd.extend(shlex.split(args))
497 p = Run(cmd, stdout=subprocess.PIPE)
498 p.communicate()
499 assert p.returncode == 0, "avbtool add_hash_footer of %s failed" % (
500 os.path.basename(OPTIONS.input_tmp))
David Zeuthend995f4b2016-01-29 16:59:17 -0500501
502 img.seek(os.SEEK_SET, 0)
503 data = img.read()
504
505 if has_ramdisk:
506 ramdisk_img.close()
507 img.close()
508
509 return data
510
511
Doug Zongkerd5131602012-08-02 14:46:42 -0700512def GetBootableImage(name, prebuilt_name, unpack_dir, tree_subdir,
Tao Baod42e97e2016-11-30 12:11:57 -0800513 info_dict=None, two_step_image=False):
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700514 """Return a File object with the desired bootable image.
515
516 Look for it in 'unpack_dir'/BOOTABLE_IMAGES under the name 'prebuilt_name',
517 otherwise look for it under 'unpack_dir'/IMAGES, otherwise construct it from
518 the source files in 'unpack_dir'/'tree_subdir'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700519
Doug Zongker55d93282011-01-25 17:03:34 -0800520 prebuilt_path = os.path.join(unpack_dir, "BOOTABLE_IMAGES", prebuilt_name)
521 if os.path.exists(prebuilt_path):
Tao Bao89fbb0f2017-01-10 10:47:58 -0800522 print("using prebuilt %s from BOOTABLE_IMAGES..." % (prebuilt_name,))
Doug Zongker55d93282011-01-25 17:03:34 -0800523 return File.FromLocalFile(name, prebuilt_path)
Doug Zongker6f1d0312014-08-22 08:07:12 -0700524
525 prebuilt_path = os.path.join(unpack_dir, "IMAGES", prebuilt_name)
526 if os.path.exists(prebuilt_path):
Tao Bao89fbb0f2017-01-10 10:47:58 -0800527 print("using prebuilt %s from IMAGES..." % (prebuilt_name,))
Doug Zongker6f1d0312014-08-22 08:07:12 -0700528 return File.FromLocalFile(name, prebuilt_path)
529
Tao Bao89fbb0f2017-01-10 10:47:58 -0800530 print("building image from target_files %s..." % (tree_subdir,))
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700531
532 if info_dict is None:
533 info_dict = OPTIONS.info_dict
534
535 # With system_root_image == "true", we don't pack ramdisk into the boot image.
Daniel Rosenberg40ef35b2015-11-10 19:21:34 -0800536 # Unless "recovery_as_boot" is specified, in which case we carry the ramdisk
537 # for recovery.
538 has_ramdisk = (info_dict.get("system_root_image") != "true" or
539 prebuilt_name != "boot.img" or
540 info_dict.get("recovery_as_boot") == "true")
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700541
Doug Zongker6f1d0312014-08-22 08:07:12 -0700542 fs_config = "META/" + tree_subdir.lower() + "_filesystem_config.txt"
David Zeuthen2ce63ed2016-09-15 13:43:54 -0400543 data = _BuildBootableImage(os.path.join(unpack_dir, tree_subdir),
544 os.path.join(unpack_dir, fs_config),
Tao Baod42e97e2016-11-30 12:11:57 -0800545 info_dict, has_ramdisk, two_step_image)
Doug Zongker6f1d0312014-08-22 08:07:12 -0700546 if data:
547 return File(name, data)
548 return None
Doug Zongker55d93282011-01-25 17:03:34 -0800549
Doug Zongkereef39442009-04-02 12:14:19 -0700550
Doug Zongker75f17362009-12-08 13:46:44 -0800551def UnzipTemp(filename, pattern=None):
Doug Zongker55d93282011-01-25 17:03:34 -0800552 """Unzip the given archive into a temporary directory and return the name.
553
554 If filename is of the form "foo.zip+bar.zip", unzip foo.zip into a
555 temp dir, then unzip bar.zip into that_dir/BOOTABLE_IMAGES.
556
557 Returns (tempdir, zipobj) where zipobj is a zipfile.ZipFile (of the
558 main file), open for reading.
559 """
Doug Zongkereef39442009-04-02 12:14:19 -0700560
561 tmp = tempfile.mkdtemp(prefix="targetfiles-")
562 OPTIONS.tempfiles.append(tmp)
Doug Zongker55d93282011-01-25 17:03:34 -0800563
564 def unzip_to_dir(filename, dirname):
565 cmd = ["unzip", "-o", "-q", filename, "-d", dirname]
566 if pattern is not None:
Tao Bao6b0b2f92017-03-05 11:38:11 -0800567 cmd.extend(pattern)
Doug Zongker55d93282011-01-25 17:03:34 -0800568 p = Run(cmd, stdout=subprocess.PIPE)
569 p.communicate()
570 if p.returncode != 0:
571 raise ExternalError("failed to unzip input target-files \"%s\"" %
572 (filename,))
573
574 m = re.match(r"^(.*[.]zip)\+(.*[.]zip)$", filename, re.IGNORECASE)
575 if m:
576 unzip_to_dir(m.group(1), tmp)
577 unzip_to_dir(m.group(2), os.path.join(tmp, "BOOTABLE_IMAGES"))
578 filename = m.group(1)
579 else:
580 unzip_to_dir(filename, tmp)
581
582 return tmp, zipfile.ZipFile(filename, "r")
Doug Zongkereef39442009-04-02 12:14:19 -0700583
584
585def GetKeyPasswords(keylist):
586 """Given a list of keys, prompt the user to enter passwords for
587 those which require them. Return a {key: password} dict. password
588 will be None if the key has no password."""
589
Doug Zongker8ce7c252009-05-22 13:34:54 -0700590 no_passwords = []
591 need_passwords = []
T.R. Fullhart37e10522013-03-18 10:31:26 -0700592 key_passwords = {}
Doug Zongkereef39442009-04-02 12:14:19 -0700593 devnull = open("/dev/null", "w+b")
594 for k in sorted(keylist):
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800595 # We don't need a password for things that aren't really keys.
596 if k in SPECIAL_CERT_STRINGS:
Doug Zongker8ce7c252009-05-22 13:34:54 -0700597 no_passwords.append(k)
Doug Zongker43874f82009-04-14 14:05:15 -0700598 continue
599
T.R. Fullhart37e10522013-03-18 10:31:26 -0700600 p = Run(["openssl", "pkcs8", "-in", k+OPTIONS.private_key_suffix,
Doug Zongker602a84e2009-06-18 08:35:12 -0700601 "-inform", "DER", "-nocrypt"],
602 stdin=devnull.fileno(),
603 stdout=devnull.fileno(),
604 stderr=subprocess.STDOUT)
Doug Zongkereef39442009-04-02 12:14:19 -0700605 p.communicate()
606 if p.returncode == 0:
T.R. Fullhart37e10522013-03-18 10:31:26 -0700607 # Definitely an unencrypted key.
Doug Zongker8ce7c252009-05-22 13:34:54 -0700608 no_passwords.append(k)
Doug Zongkereef39442009-04-02 12:14:19 -0700609 else:
T.R. Fullhart37e10522013-03-18 10:31:26 -0700610 p = Run(["openssl", "pkcs8", "-in", k+OPTIONS.private_key_suffix,
611 "-inform", "DER", "-passin", "pass:"],
612 stdin=devnull.fileno(),
613 stdout=devnull.fileno(),
614 stderr=subprocess.PIPE)
Dan Albert8b72aef2015-03-23 19:13:21 -0700615 _, stderr = p.communicate()
T.R. Fullhart37e10522013-03-18 10:31:26 -0700616 if p.returncode == 0:
617 # Encrypted key with empty string as password.
618 key_passwords[k] = ''
619 elif stderr.startswith('Error decrypting key'):
620 # Definitely encrypted key.
621 # It would have said "Error reading key" if it didn't parse correctly.
622 need_passwords.append(k)
623 else:
624 # Potentially, a type of key that openssl doesn't understand.
625 # We'll let the routines in signapk.jar handle it.
626 no_passwords.append(k)
Doug Zongkereef39442009-04-02 12:14:19 -0700627 devnull.close()
Doug Zongker8ce7c252009-05-22 13:34:54 -0700628
T.R. Fullhart37e10522013-03-18 10:31:26 -0700629 key_passwords.update(PasswordManager().GetPasswords(need_passwords))
Doug Zongker8ce7c252009-05-22 13:34:54 -0700630 key_passwords.update(dict.fromkeys(no_passwords, None))
Doug Zongkereef39442009-04-02 12:14:19 -0700631 return key_passwords
632
633
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800634def GetMinSdkVersion(apk_name):
635 """Get the minSdkVersion delared in the APK. This can be both a decimal number
636 (API Level) or a codename.
637 """
638
639 p = Run(["aapt", "dump", "badging", apk_name], stdout=subprocess.PIPE)
640 output, err = p.communicate()
641 if err:
642 raise ExternalError("Failed to obtain minSdkVersion: aapt return code %s"
643 % (p.returncode,))
644
645 for line in output.split("\n"):
646 # Looking for lines such as sdkVersion:'23' or sdkVersion:'M'
647 m = re.match(r'sdkVersion:\'([^\']*)\'', line)
648 if m:
649 return m.group(1)
650 raise ExternalError("No minSdkVersion returned by aapt")
651
652
653def GetMinSdkVersionInt(apk_name, codename_to_api_level_map):
654 """Get the minSdkVersion declared in the APK as a number (API Level). If
655 minSdkVersion is set to a codename, it is translated to a number using the
656 provided map.
657 """
658
659 version = GetMinSdkVersion(apk_name)
660 try:
661 return int(version)
662 except ValueError:
663 # Not a decimal number. Codename?
664 if version in codename_to_api_level_map:
665 return codename_to_api_level_map[version]
666 else:
667 raise ExternalError("Unknown minSdkVersion: '%s'. Known codenames: %s"
668 % (version, codename_to_api_level_map))
669
670
671def SignFile(input_name, output_name, key, password, min_api_level=None,
672 codename_to_api_level_map=dict(),
673 whole_file=False):
Doug Zongkereef39442009-04-02 12:14:19 -0700674 """Sign the input_name zip/jar/apk, producing output_name. Use the
675 given key and password (the latter may be None if the key does not
676 have a password.
677
Doug Zongker951495f2009-08-14 12:44:19 -0700678 If whole_file is true, use the "-w" option to SignApk to embed a
679 signature that covers the whole file in the archive comment of the
680 zip file.
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800681
682 min_api_level is the API Level (int) of the oldest platform this file may end
683 up on. If not specified for an APK, the API Level is obtained by interpreting
684 the minSdkVersion attribute of the APK's AndroidManifest.xml.
685
686 codename_to_api_level_map is needed to translate the codename which may be
687 encountered as the APK's minSdkVersion.
Doug Zongkereef39442009-04-02 12:14:19 -0700688 """
Doug Zongker951495f2009-08-14 12:44:19 -0700689
Alex Klyubin9667b182015-12-10 13:38:50 -0800690 java_library_path = os.path.join(
691 OPTIONS.search_path, OPTIONS.signapk_shared_library_path)
692
Tao Baoe95540e2016-11-08 12:08:53 -0800693 cmd = ([OPTIONS.java_path] + OPTIONS.java_args +
694 ["-Djava.library.path=" + java_library_path,
695 "-jar", os.path.join(OPTIONS.search_path, OPTIONS.signapk_path)] +
696 OPTIONS.extra_signapk_args)
Doug Zongker951495f2009-08-14 12:44:19 -0700697 if whole_file:
698 cmd.append("-w")
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800699
700 min_sdk_version = min_api_level
701 if min_sdk_version is None:
702 if not whole_file:
703 min_sdk_version = GetMinSdkVersionInt(
704 input_name, codename_to_api_level_map)
705 if min_sdk_version is not None:
706 cmd.extend(["--min-sdk-version", str(min_sdk_version)])
707
T.R. Fullhart37e10522013-03-18 10:31:26 -0700708 cmd.extend([key + OPTIONS.public_key_suffix,
709 key + OPTIONS.private_key_suffix,
Alex Klyubineb756d72015-12-04 09:21:08 -0800710 input_name, output_name])
Doug Zongker951495f2009-08-14 12:44:19 -0700711
712 p = Run(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
Doug Zongkereef39442009-04-02 12:14:19 -0700713 if password is not None:
714 password += "\n"
715 p.communicate(password)
716 if p.returncode != 0:
717 raise ExternalError("signapk.jar failed: return code %s" % (p.returncode,))
718
Doug Zongkereef39442009-04-02 12:14:19 -0700719
Doug Zongker37974732010-09-16 17:44:38 -0700720def CheckSize(data, target, info_dict):
Doug Zongkereef39442009-04-02 12:14:19 -0700721 """Check the data string passed against the max size limit, if
722 any, for the given target. Raise exception if the data is too big.
723 Print a warning if the data is nearing the maximum size."""
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700724
Dan Albert8b72aef2015-03-23 19:13:21 -0700725 if target.endswith(".img"):
726 target = target[:-4]
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700727 mount_point = "/" + target
728
Ying Wangf8824af2014-06-03 14:07:27 -0700729 fs_type = None
730 limit = None
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700731 if info_dict["fstab"]:
Dan Albert8b72aef2015-03-23 19:13:21 -0700732 if mount_point == "/userdata":
733 mount_point = "/data"
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700734 p = info_dict["fstab"][mount_point]
735 fs_type = p.fs_type
Andrew Boie0f9aec82012-02-14 09:32:52 -0800736 device = p.device
737 if "/" in device:
738 device = device[device.rfind("/")+1:]
739 limit = info_dict.get(device + "_size", None)
Dan Albert8b72aef2015-03-23 19:13:21 -0700740 if not fs_type or not limit:
741 return
Doug Zongkereef39442009-04-02 12:14:19 -0700742
Andrew Boie0f9aec82012-02-14 09:32:52 -0800743 size = len(data)
744 pct = float(size) * 100.0 / limit
745 msg = "%s size (%d) is %.2f%% of limit (%d)" % (target, size, pct, limit)
746 if pct >= 99.0:
747 raise ExternalError(msg)
748 elif pct >= 95.0:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800749 print("\n WARNING: %s\n" % (msg,))
Andrew Boie0f9aec82012-02-14 09:32:52 -0800750 elif OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800751 print(" ", msg)
Doug Zongkereef39442009-04-02 12:14:19 -0700752
753
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800754def ReadApkCerts(tf_zip):
755 """Given a target_files ZipFile, parse the META/apkcerts.txt file
756 and return a {package: cert} dict."""
757 certmap = {}
758 for line in tf_zip.read("META/apkcerts.txt").split("\n"):
759 line = line.strip()
Dan Albert8b72aef2015-03-23 19:13:21 -0700760 if not line:
761 continue
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800762 m = re.match(r'^name="(.*)"\s+certificate="(.*)"\s+'
763 r'private_key="(.*)"$', line)
764 if m:
765 name, cert, privkey = m.groups()
T.R. Fullhart37e10522013-03-18 10:31:26 -0700766 public_key_suffix_len = len(OPTIONS.public_key_suffix)
767 private_key_suffix_len = len(OPTIONS.private_key_suffix)
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800768 if cert in SPECIAL_CERT_STRINGS and not privkey:
769 certmap[name] = cert
T.R. Fullhart37e10522013-03-18 10:31:26 -0700770 elif (cert.endswith(OPTIONS.public_key_suffix) and
771 privkey.endswith(OPTIONS.private_key_suffix) and
772 cert[:-public_key_suffix_len] == privkey[:-private_key_suffix_len]):
773 certmap[name] = cert[:-public_key_suffix_len]
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800774 else:
775 raise ValueError("failed to parse line from apkcerts.txt:\n" + line)
776 return certmap
777
778
Doug Zongkereef39442009-04-02 12:14:19 -0700779COMMON_DOCSTRING = """
780 -p (--path) <dir>
Doug Zongker602a84e2009-06-18 08:35:12 -0700781 Prepend <dir>/bin to the list of places to search for binaries
782 run by this script, and expect to find jars in <dir>/framework.
Doug Zongkereef39442009-04-02 12:14:19 -0700783
Doug Zongker05d3dea2009-06-22 11:32:31 -0700784 -s (--device_specific) <file>
785 Path to the python module containing device-specific
786 releasetools code.
787
Doug Zongker8bec09e2009-11-30 15:37:14 -0800788 -x (--extra) <key=value>
789 Add a key/value pair to the 'extras' dict, which device-specific
790 extension code may look at.
791
Doug Zongkereef39442009-04-02 12:14:19 -0700792 -v (--verbose)
793 Show command lines being executed.
794
795 -h (--help)
796 Display this usage message and exit.
797"""
798
799def Usage(docstring):
Tao Bao89fbb0f2017-01-10 10:47:58 -0800800 print(docstring.rstrip("\n"))
801 print(COMMON_DOCSTRING)
Doug Zongkereef39442009-04-02 12:14:19 -0700802
803
804def ParseOptions(argv,
805 docstring,
806 extra_opts="", extra_long_opts=(),
807 extra_option_handler=None):
808 """Parse the options in argv and return any arguments that aren't
809 flags. docstring is the calling module's docstring, to be displayed
810 for errors and -h. extra_opts and extra_long_opts are for flags
811 defined by the caller, which are processed by passing them to
812 extra_option_handler."""
813
814 try:
815 opts, args = getopt.getopt(
Doug Zongker8bec09e2009-11-30 15:37:14 -0800816 argv, "hvp:s:x:" + extra_opts,
Alex Klyubin9667b182015-12-10 13:38:50 -0800817 ["help", "verbose", "path=", "signapk_path=",
818 "signapk_shared_library_path=", "extra_signapk_args=",
Baligh Uddinbdc2e312014-09-05 17:36:20 -0700819 "java_path=", "java_args=", "public_key_suffix=",
Baligh Uddin601ddea2015-06-09 15:48:14 -0700820 "private_key_suffix=", "boot_signer_path=", "boot_signer_args=",
821 "verity_signer_path=", "verity_signer_args=", "device_specific=",
Baligh Uddine2048682014-11-20 09:52:05 -0800822 "extra="] +
T.R. Fullhart37e10522013-03-18 10:31:26 -0700823 list(extra_long_opts))
Dan Albert8b72aef2015-03-23 19:13:21 -0700824 except getopt.GetoptError as err:
Doug Zongkereef39442009-04-02 12:14:19 -0700825 Usage(docstring)
Tao Bao89fbb0f2017-01-10 10:47:58 -0800826 print("**", str(err), "**")
Doug Zongkereef39442009-04-02 12:14:19 -0700827 sys.exit(2)
828
Doug Zongkereef39442009-04-02 12:14:19 -0700829 for o, a in opts:
830 if o in ("-h", "--help"):
831 Usage(docstring)
832 sys.exit()
833 elif o in ("-v", "--verbose"):
834 OPTIONS.verbose = True
835 elif o in ("-p", "--path"):
Doug Zongker602a84e2009-06-18 08:35:12 -0700836 OPTIONS.search_path = a
T.R. Fullhart37e10522013-03-18 10:31:26 -0700837 elif o in ("--signapk_path",):
838 OPTIONS.signapk_path = a
Alex Klyubin9667b182015-12-10 13:38:50 -0800839 elif o in ("--signapk_shared_library_path",):
840 OPTIONS.signapk_shared_library_path = a
T.R. Fullhart37e10522013-03-18 10:31:26 -0700841 elif o in ("--extra_signapk_args",):
842 OPTIONS.extra_signapk_args = shlex.split(a)
843 elif o in ("--java_path",):
844 OPTIONS.java_path = a
Baligh Uddin339ee492014-09-05 11:18:07 -0700845 elif o in ("--java_args",):
Tao Baoe95540e2016-11-08 12:08:53 -0800846 OPTIONS.java_args = shlex.split(a)
T.R. Fullhart37e10522013-03-18 10:31:26 -0700847 elif o in ("--public_key_suffix",):
848 OPTIONS.public_key_suffix = a
849 elif o in ("--private_key_suffix",):
850 OPTIONS.private_key_suffix = a
Baligh Uddine2048682014-11-20 09:52:05 -0800851 elif o in ("--boot_signer_path",):
852 OPTIONS.boot_signer_path = a
Baligh Uddin601ddea2015-06-09 15:48:14 -0700853 elif o in ("--boot_signer_args",):
854 OPTIONS.boot_signer_args = shlex.split(a)
855 elif o in ("--verity_signer_path",):
856 OPTIONS.verity_signer_path = a
857 elif o in ("--verity_signer_args",):
858 OPTIONS.verity_signer_args = shlex.split(a)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700859 elif o in ("-s", "--device_specific"):
860 OPTIONS.device_specific = a
Doug Zongker5ecba702009-12-03 16:36:20 -0800861 elif o in ("-x", "--extra"):
Doug Zongker8bec09e2009-11-30 15:37:14 -0800862 key, value = a.split("=", 1)
863 OPTIONS.extras[key] = value
Doug Zongkereef39442009-04-02 12:14:19 -0700864 else:
865 if extra_option_handler is None or not extra_option_handler(o, a):
866 assert False, "unknown option \"%s\"" % (o,)
867
Doug Zongker85448772014-09-09 14:59:20 -0700868 if OPTIONS.search_path:
869 os.environ["PATH"] = (os.path.join(OPTIONS.search_path, "bin") +
870 os.pathsep + os.environ["PATH"])
Doug Zongkereef39442009-04-02 12:14:19 -0700871
872 return args
873
874
Tao Bao4c851b12016-09-19 13:54:38 -0700875def MakeTempFile(prefix='tmp', suffix=''):
Doug Zongkerfc44a512014-08-26 13:10:25 -0700876 """Make a temp file and add it to the list of things to be deleted
877 when Cleanup() is called. Return the filename."""
878 fd, fn = tempfile.mkstemp(prefix=prefix, suffix=suffix)
879 os.close(fd)
880 OPTIONS.tempfiles.append(fn)
881 return fn
882
883
Doug Zongkereef39442009-04-02 12:14:19 -0700884def Cleanup():
885 for i in OPTIONS.tempfiles:
886 if os.path.isdir(i):
887 shutil.rmtree(i)
888 else:
889 os.remove(i)
Doug Zongker8ce7c252009-05-22 13:34:54 -0700890
891
892class PasswordManager(object):
893 def __init__(self):
894 self.editor = os.getenv("EDITOR", None)
895 self.pwfile = os.getenv("ANDROID_PW_FILE", None)
896
897 def GetPasswords(self, items):
898 """Get passwords corresponding to each string in 'items',
899 returning a dict. (The dict may have keys in addition to the
900 values in 'items'.)
901
902 Uses the passwords in $ANDROID_PW_FILE if available, letting the
903 user edit that file to add more needed passwords. If no editor is
904 available, or $ANDROID_PW_FILE isn't define, prompts the user
905 interactively in the ordinary way.
906 """
907
908 current = self.ReadFile()
909
910 first = True
911 while True:
912 missing = []
913 for i in items:
914 if i not in current or not current[i]:
915 missing.append(i)
916 # Are all the passwords already in the file?
Dan Albert8b72aef2015-03-23 19:13:21 -0700917 if not missing:
918 return current
Doug Zongker8ce7c252009-05-22 13:34:54 -0700919
920 for i in missing:
921 current[i] = ""
922
923 if not first:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800924 print("key file %s still missing some passwords." % (self.pwfile,))
Doug Zongker8ce7c252009-05-22 13:34:54 -0700925 answer = raw_input("try to edit again? [y]> ").strip()
926 if answer and answer[0] not in 'yY':
927 raise RuntimeError("key passwords unavailable")
928 first = False
929
930 current = self.UpdateAndReadFile(current)
931
Dan Albert8b72aef2015-03-23 19:13:21 -0700932 def PromptResult(self, current): # pylint: disable=no-self-use
Doug Zongker8ce7c252009-05-22 13:34:54 -0700933 """Prompt the user to enter a value (password) for each key in
934 'current' whose value is fales. Returns a new dict with all the
935 values.
936 """
937 result = {}
938 for k, v in sorted(current.iteritems()):
939 if v:
940 result[k] = v
941 else:
942 while True:
Dan Albert8b72aef2015-03-23 19:13:21 -0700943 result[k] = getpass.getpass(
944 "Enter password for %s key> " % k).strip()
945 if result[k]:
946 break
Doug Zongker8ce7c252009-05-22 13:34:54 -0700947 return result
948
949 def UpdateAndReadFile(self, current):
950 if not self.editor or not self.pwfile:
951 return self.PromptResult(current)
952
953 f = open(self.pwfile, "w")
Dan Albert8b72aef2015-03-23 19:13:21 -0700954 os.chmod(self.pwfile, 0o600)
Doug Zongker8ce7c252009-05-22 13:34:54 -0700955 f.write("# Enter key passwords between the [[[ ]]] brackets.\n")
956 f.write("# (Additional spaces are harmless.)\n\n")
957
958 first_line = None
Dan Albert8b72aef2015-03-23 19:13:21 -0700959 sorted_list = sorted([(not v, k, v) for (k, v) in current.iteritems()])
960 for i, (_, k, v) in enumerate(sorted_list):
Doug Zongker8ce7c252009-05-22 13:34:54 -0700961 f.write("[[[ %s ]]] %s\n" % (v, k))
962 if not v and first_line is None:
963 # position cursor on first line with no password.
964 first_line = i + 4
965 f.close()
966
967 p = Run([self.editor, "+%d" % (first_line,), self.pwfile])
968 _, _ = p.communicate()
969
970 return self.ReadFile()
971
972 def ReadFile(self):
973 result = {}
Dan Albert8b72aef2015-03-23 19:13:21 -0700974 if self.pwfile is None:
975 return result
Doug Zongker8ce7c252009-05-22 13:34:54 -0700976 try:
977 f = open(self.pwfile, "r")
978 for line in f:
979 line = line.strip()
Dan Albert8b72aef2015-03-23 19:13:21 -0700980 if not line or line[0] == '#':
981 continue
Doug Zongker8ce7c252009-05-22 13:34:54 -0700982 m = re.match(r"^\[\[\[\s*(.*?)\s*\]\]\]\s*(\S+)$", line)
983 if not m:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800984 print("failed to parse password file: ", line)
Doug Zongker8ce7c252009-05-22 13:34:54 -0700985 else:
986 result[m.group(2)] = m.group(1)
987 f.close()
Dan Albert8b72aef2015-03-23 19:13:21 -0700988 except IOError as e:
Doug Zongker8ce7c252009-05-22 13:34:54 -0700989 if e.errno != errno.ENOENT:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800990 print("error reading password file: ", str(e))
Doug Zongker8ce7c252009-05-22 13:34:54 -0700991 return result
Doug Zongker048e7ca2009-06-15 14:31:53 -0700992
993
Dan Albert8e0178d2015-01-27 15:53:15 -0800994def ZipWrite(zip_file, filename, arcname=None, perms=0o644,
995 compress_type=None):
996 import datetime
997
998 # http://b/18015246
999 # Python 2.7's zipfile implementation wrongly thinks that zip64 is required
1000 # for files larger than 2GiB. We can work around this by adjusting their
1001 # limit. Note that `zipfile.writestr()` will not work for strings larger than
1002 # 2GiB. The Python interpreter sometimes rejects strings that large (though
1003 # it isn't clear to me exactly what circumstances cause this).
1004 # `zipfile.write()` must be used directly to work around this.
1005 #
1006 # This mess can be avoided if we port to python3.
1007 saved_zip64_limit = zipfile.ZIP64_LIMIT
1008 zipfile.ZIP64_LIMIT = (1 << 32) - 1
1009
1010 if compress_type is None:
1011 compress_type = zip_file.compression
1012 if arcname is None:
1013 arcname = filename
1014
1015 saved_stat = os.stat(filename)
1016
1017 try:
1018 # `zipfile.write()` doesn't allow us to pass ZipInfo, so just modify the
1019 # file to be zipped and reset it when we're done.
1020 os.chmod(filename, perms)
1021
1022 # Use a fixed timestamp so the output is repeatable.
1023 epoch = datetime.datetime.fromtimestamp(0)
1024 timestamp = (datetime.datetime(2009, 1, 1) - epoch).total_seconds()
1025 os.utime(filename, (timestamp, timestamp))
1026
1027 zip_file.write(filename, arcname=arcname, compress_type=compress_type)
1028 finally:
1029 os.chmod(filename, saved_stat.st_mode)
1030 os.utime(filename, (saved_stat.st_atime, saved_stat.st_mtime))
1031 zipfile.ZIP64_LIMIT = saved_zip64_limit
1032
1033
Tao Bao58c1b962015-05-20 09:32:18 -07001034def ZipWriteStr(zip_file, zinfo_or_arcname, data, perms=None,
Tao Baof3282b42015-04-01 11:21:55 -07001035 compress_type=None):
1036 """Wrap zipfile.writestr() function to work around the zip64 limit.
1037
1038 Even with the ZIP64_LIMIT workaround, it won't allow writing a string
1039 longer than 2GiB. It gives 'OverflowError: size does not fit in an int'
1040 when calling crc32(bytes).
1041
1042 But it still works fine to write a shorter string into a large zip file.
1043 We should use ZipWrite() whenever possible, and only use ZipWriteStr()
1044 when we know the string won't be too long.
1045 """
1046
1047 saved_zip64_limit = zipfile.ZIP64_LIMIT
1048 zipfile.ZIP64_LIMIT = (1 << 32) - 1
1049
1050 if not isinstance(zinfo_or_arcname, zipfile.ZipInfo):
1051 zinfo = zipfile.ZipInfo(filename=zinfo_or_arcname)
Dan Albert8b72aef2015-03-23 19:13:21 -07001052 zinfo.compress_type = zip_file.compression
Tao Bao58c1b962015-05-20 09:32:18 -07001053 if perms is None:
Tao Bao2a410582015-07-10 17:18:23 -07001054 perms = 0o100644
Geremy Condra36bd3652014-02-06 19:45:10 -08001055 else:
Tao Baof3282b42015-04-01 11:21:55 -07001056 zinfo = zinfo_or_arcname
1057
1058 # If compress_type is given, it overrides the value in zinfo.
1059 if compress_type is not None:
1060 zinfo.compress_type = compress_type
1061
Tao Bao58c1b962015-05-20 09:32:18 -07001062 # If perms is given, it has a priority.
1063 if perms is not None:
Tao Bao2a410582015-07-10 17:18:23 -07001064 # If perms doesn't set the file type, mark it as a regular file.
1065 if perms & 0o770000 == 0:
1066 perms |= 0o100000
Tao Bao58c1b962015-05-20 09:32:18 -07001067 zinfo.external_attr = perms << 16
1068
Tao Baof3282b42015-04-01 11:21:55 -07001069 # Use a fixed timestamp so the output is repeatable.
Tao Baof3282b42015-04-01 11:21:55 -07001070 zinfo.date_time = (2009, 1, 1, 0, 0, 0)
1071
Dan Albert8b72aef2015-03-23 19:13:21 -07001072 zip_file.writestr(zinfo, data)
Tao Baof3282b42015-04-01 11:21:55 -07001073 zipfile.ZIP64_LIMIT = saved_zip64_limit
1074
1075
1076def ZipClose(zip_file):
1077 # http://b/18015246
1078 # zipfile also refers to ZIP64_LIMIT during close() when it writes out the
1079 # central directory.
1080 saved_zip64_limit = zipfile.ZIP64_LIMIT
1081 zipfile.ZIP64_LIMIT = (1 << 32) - 1
1082
1083 zip_file.close()
1084
1085 zipfile.ZIP64_LIMIT = saved_zip64_limit
Doug Zongker05d3dea2009-06-22 11:32:31 -07001086
1087
1088class DeviceSpecificParams(object):
1089 module = None
1090 def __init__(self, **kwargs):
1091 """Keyword arguments to the constructor become attributes of this
1092 object, which is passed to all functions in the device-specific
1093 module."""
1094 for k, v in kwargs.iteritems():
1095 setattr(self, k, v)
Doug Zongker8bec09e2009-11-30 15:37:14 -08001096 self.extras = OPTIONS.extras
Doug Zongker05d3dea2009-06-22 11:32:31 -07001097
1098 if self.module is None:
1099 path = OPTIONS.device_specific
Dan Albert8b72aef2015-03-23 19:13:21 -07001100 if not path:
1101 return
Doug Zongker8e2f2b92009-06-24 14:34:57 -07001102 try:
1103 if os.path.isdir(path):
1104 info = imp.find_module("releasetools", [path])
1105 else:
1106 d, f = os.path.split(path)
1107 b, x = os.path.splitext(f)
1108 if x == ".py":
1109 f = b
1110 info = imp.find_module(f, [d])
Tao Bao89fbb0f2017-01-10 10:47:58 -08001111 print("loaded device-specific extensions from", path)
Doug Zongker8e2f2b92009-06-24 14:34:57 -07001112 self.module = imp.load_module("device_specific", *info)
1113 except ImportError:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001114 print("unable to load device-specific module; assuming none")
Doug Zongker05d3dea2009-06-22 11:32:31 -07001115
1116 def _DoCall(self, function_name, *args, **kwargs):
1117 """Call the named function in the device-specific module, passing
1118 the given args and kwargs. The first argument to the call will be
1119 the DeviceSpecific object itself. If there is no module, or the
1120 module does not define the function, return the value of the
1121 'default' kwarg (which itself defaults to None)."""
1122 if self.module is None or not hasattr(self.module, function_name):
1123 return kwargs.get("default", None)
1124 return getattr(self.module, function_name)(*((self,) + args), **kwargs)
1125
1126 def FullOTA_Assertions(self):
1127 """Called after emitting the block of assertions at the top of a
1128 full OTA package. Implementations can add whatever additional
1129 assertions they like."""
1130 return self._DoCall("FullOTA_Assertions")
1131
Doug Zongkere5ff5902012-01-17 10:55:37 -08001132 def FullOTA_InstallBegin(self):
1133 """Called at the start of full OTA installation."""
1134 return self._DoCall("FullOTA_InstallBegin")
1135
Doug Zongker05d3dea2009-06-22 11:32:31 -07001136 def FullOTA_InstallEnd(self):
1137 """Called at the end of full OTA installation; typically this is
1138 used to install the image for the device's baseband processor."""
1139 return self._DoCall("FullOTA_InstallEnd")
1140
1141 def IncrementalOTA_Assertions(self):
1142 """Called after emitting the block of assertions at the top of an
1143 incremental OTA package. Implementations can add whatever
1144 additional assertions they like."""
1145 return self._DoCall("IncrementalOTA_Assertions")
1146
Doug Zongkere5ff5902012-01-17 10:55:37 -08001147 def IncrementalOTA_VerifyBegin(self):
1148 """Called at the start of the verification phase of incremental
1149 OTA installation; additional checks can be placed here to abort
1150 the script before any changes are made."""
1151 return self._DoCall("IncrementalOTA_VerifyBegin")
1152
Doug Zongker05d3dea2009-06-22 11:32:31 -07001153 def IncrementalOTA_VerifyEnd(self):
1154 """Called at the end of the verification phase of incremental OTA
1155 installation; additional checks can be placed here to abort the
1156 script before any changes are made."""
1157 return self._DoCall("IncrementalOTA_VerifyEnd")
1158
Doug Zongkere5ff5902012-01-17 10:55:37 -08001159 def IncrementalOTA_InstallBegin(self):
1160 """Called at the start of incremental OTA installation (after
1161 verification is complete)."""
1162 return self._DoCall("IncrementalOTA_InstallBegin")
1163
Doug Zongker05d3dea2009-06-22 11:32:31 -07001164 def IncrementalOTA_InstallEnd(self):
1165 """Called at the end of incremental OTA installation; typically
1166 this is used to install the image for the device's baseband
1167 processor."""
1168 return self._DoCall("IncrementalOTA_InstallEnd")
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001169
Tao Bao9bc6bb22015-11-09 16:58:28 -08001170 def VerifyOTA_Assertions(self):
1171 return self._DoCall("VerifyOTA_Assertions")
1172
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001173class File(object):
YOUNG HO CHAccc5c402016-10-13 13:40:46 +09001174 def __init__(self, name, data, compress_size = None):
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001175 self.name = name
1176 self.data = data
1177 self.size = len(data)
YOUNG HO CHAccc5c402016-10-13 13:40:46 +09001178 self.compress_size = compress_size or self.size
Doug Zongker55d93282011-01-25 17:03:34 -08001179 self.sha1 = sha1(data).hexdigest()
1180
1181 @classmethod
1182 def FromLocalFile(cls, name, diskname):
1183 f = open(diskname, "rb")
1184 data = f.read()
1185 f.close()
1186 return File(name, data)
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001187
1188 def WriteToTemp(self):
1189 t = tempfile.NamedTemporaryFile()
1190 t.write(self.data)
1191 t.flush()
1192 return t
1193
Geremy Condra36bd3652014-02-06 19:45:10 -08001194 def AddToZip(self, z, compression=None):
Tao Baof3282b42015-04-01 11:21:55 -07001195 ZipWriteStr(z, self.name, self.data, compress_type=compression)
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001196
1197DIFF_PROGRAM_BY_EXT = {
1198 ".gz" : "imgdiff",
1199 ".zip" : ["imgdiff", "-z"],
1200 ".jar" : ["imgdiff", "-z"],
1201 ".apk" : ["imgdiff", "-z"],
1202 ".img" : "imgdiff",
1203 }
1204
1205class Difference(object):
Doug Zongker24cd2802012-08-14 16:36:15 -07001206 def __init__(self, tf, sf, diff_program=None):
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001207 self.tf = tf
1208 self.sf = sf
1209 self.patch = None
Doug Zongker24cd2802012-08-14 16:36:15 -07001210 self.diff_program = diff_program
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001211
1212 def ComputePatch(self):
1213 """Compute the patch (as a string of data) needed to turn sf into
1214 tf. Returns the same tuple as GetPatch()."""
1215
1216 tf = self.tf
1217 sf = self.sf
1218
Doug Zongker24cd2802012-08-14 16:36:15 -07001219 if self.diff_program:
1220 diff_program = self.diff_program
1221 else:
1222 ext = os.path.splitext(tf.name)[1]
1223 diff_program = DIFF_PROGRAM_BY_EXT.get(ext, "bsdiff")
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001224
1225 ttemp = tf.WriteToTemp()
1226 stemp = sf.WriteToTemp()
1227
1228 ext = os.path.splitext(tf.name)[1]
1229
1230 try:
1231 ptemp = tempfile.NamedTemporaryFile()
1232 if isinstance(diff_program, list):
1233 cmd = copy.copy(diff_program)
1234 else:
1235 cmd = [diff_program]
1236 cmd.append(stemp.name)
1237 cmd.append(ttemp.name)
1238 cmd.append(ptemp.name)
1239 p = Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Doug Zongkerf8340082014-08-05 10:39:37 -07001240 err = []
1241 def run():
1242 _, e = p.communicate()
Dan Albert8b72aef2015-03-23 19:13:21 -07001243 if e:
1244 err.append(e)
Doug Zongkerf8340082014-08-05 10:39:37 -07001245 th = threading.Thread(target=run)
1246 th.start()
1247 th.join(timeout=300) # 5 mins
1248 if th.is_alive():
Tao Bao89fbb0f2017-01-10 10:47:58 -08001249 print("WARNING: diff command timed out")
Doug Zongkerf8340082014-08-05 10:39:37 -07001250 p.terminate()
1251 th.join(5)
1252 if th.is_alive():
1253 p.kill()
1254 th.join()
1255
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001256 if err or p.returncode != 0:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001257 print("WARNING: failure running %s:\n%s\n" % (
1258 diff_program, "".join(err)))
Doug Zongkerf8340082014-08-05 10:39:37 -07001259 self.patch = None
1260 return None, None, None
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001261 diff = ptemp.read()
1262 finally:
1263 ptemp.close()
1264 stemp.close()
1265 ttemp.close()
1266
1267 self.patch = diff
1268 return self.tf, self.sf, self.patch
1269
1270
1271 def GetPatch(self):
1272 """Return a tuple (target_file, source_file, patch_data).
1273 patch_data may be None if ComputePatch hasn't been called, or if
1274 computing the patch failed."""
1275 return self.tf, self.sf, self.patch
1276
1277
1278def ComputeDifferences(diffs):
1279 """Call ComputePatch on all the Difference objects in 'diffs'."""
Tao Bao89fbb0f2017-01-10 10:47:58 -08001280 print(len(diffs), "diffs to compute")
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001281
1282 # Do the largest files first, to try and reduce the long-pole effect.
1283 by_size = [(i.tf.size, i) for i in diffs]
1284 by_size.sort(reverse=True)
1285 by_size = [i[1] for i in by_size]
1286
1287 lock = threading.Lock()
1288 diff_iter = iter(by_size) # accessed under lock
1289
1290 def worker():
1291 try:
1292 lock.acquire()
1293 for d in diff_iter:
1294 lock.release()
1295 start = time.time()
1296 d.ComputePatch()
1297 dur = time.time() - start
1298 lock.acquire()
1299
1300 tf, sf, patch = d.GetPatch()
1301 if sf.name == tf.name:
1302 name = tf.name
1303 else:
1304 name = "%s (%s)" % (tf.name, sf.name)
1305 if patch is None:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001306 print("patching failed! %s" % (name,))
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001307 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001308 print("%8.2f sec %8d / %8d bytes (%6.2f%%) %s" % (
1309 dur, len(patch), tf.size, 100.0 * len(patch) / tf.size, name))
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001310 lock.release()
Dan Albert8b72aef2015-03-23 19:13:21 -07001311 except Exception as e:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001312 print(e)
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001313 raise
1314
1315 # start worker threads; wait for them all to finish.
1316 threads = [threading.Thread(target=worker)
1317 for i in range(OPTIONS.worker_threads)]
1318 for th in threads:
1319 th.start()
1320 while threads:
1321 threads.pop().join()
Doug Zongker96a57e72010-09-26 14:57:41 -07001322
1323
Dan Albert8b72aef2015-03-23 19:13:21 -07001324class BlockDifference(object):
1325 def __init__(self, partition, tgt, src=None, check_first_block=False,
Tao Bao293fd132016-06-11 12:19:23 -07001326 version=None, disable_imgdiff=False):
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001327 self.tgt = tgt
1328 self.src = src
1329 self.partition = partition
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001330 self.check_first_block = check_first_block
Tao Bao293fd132016-06-11 12:19:23 -07001331 self.disable_imgdiff = disable_imgdiff
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001332
Tao Baodd2a5892015-03-12 12:32:37 -07001333 if version is None:
1334 version = 1
1335 if OPTIONS.info_dict:
1336 version = max(
1337 int(i) for i in
1338 OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(","))
1339 self.version = version
Doug Zongker62338182014-09-08 08:29:55 -07001340
1341 b = blockimgdiff.BlockImageDiff(tgt, src, threads=OPTIONS.worker_threads,
Tao Bao293fd132016-06-11 12:19:23 -07001342 version=self.version,
1343 disable_imgdiff=self.disable_imgdiff)
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001344 tmpdir = tempfile.mkdtemp()
1345 OPTIONS.tempfiles.append(tmpdir)
1346 self.path = os.path.join(tmpdir, partition)
1347 b.Compute(self.path)
Tao Baod8d14be2016-02-04 14:26:02 -08001348 self._required_cache = b.max_stashed_size
Tao Baod522bdc2016-04-12 15:53:16 -07001349 self.touched_src_ranges = b.touched_src_ranges
1350 self.touched_src_sha1 = b.touched_src_sha1
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001351
Tao Baoaac4ad52015-10-16 15:26:34 -07001352 if src is None:
1353 _, self.device = GetTypeAndDevice("/" + partition, OPTIONS.info_dict)
1354 else:
1355 _, self.device = GetTypeAndDevice("/" + partition,
1356 OPTIONS.source_info_dict)
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001357
Tao Baod8d14be2016-02-04 14:26:02 -08001358 @property
1359 def required_cache(self):
1360 return self._required_cache
1361
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001362 def WriteScript(self, script, output_zip, progress=None):
1363 if not self.src:
1364 # write the output unconditionally
Jesse Zhao75bcea02015-01-06 10:59:53 -08001365 script.Print("Patching %s image unconditionally..." % (self.partition,))
1366 else:
1367 script.Print("Patching %s image after verification." % (self.partition,))
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001368
Dan Albert8b72aef2015-03-23 19:13:21 -07001369 if progress:
1370 script.ShowProgress(progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001371 self._WriteUpdate(script, output_zip)
Tianjie Xub2deb222016-03-25 15:01:33 -07001372 if OPTIONS.verify:
1373 self._WritePostInstallVerifyScript(script)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001374
Tao Bao9bc6bb22015-11-09 16:58:28 -08001375 def WriteStrictVerifyScript(self, script):
1376 """Verify all the blocks in the care_map, including clobbered blocks.
1377
1378 This differs from the WriteVerifyScript() function: a) it prints different
1379 error messages; b) it doesn't allow half-way updated images to pass the
1380 verification."""
1381
1382 partition = self.partition
1383 script.Print("Verifying %s..." % (partition,))
1384 ranges = self.tgt.care_map
1385 ranges_str = ranges.to_string_raw()
1386 script.AppendExtra('range_sha1("%s", "%s") == "%s" && '
1387 'ui_print(" Verified.") || '
1388 'ui_print("\\"%s\\" has unexpected contents.");' % (
1389 self.device, ranges_str,
1390 self.tgt.TotalSha1(include_clobbered_blocks=True),
1391 self.device))
1392 script.AppendExtra("")
1393
Tao Baod522bdc2016-04-12 15:53:16 -07001394 def WriteVerifyScript(self, script, touched_blocks_only=False):
Sami Tolvanendd67a292014-12-09 16:40:34 +00001395 partition = self.partition
Tao Baof9efe282016-04-14 15:58:05 -07001396
1397 # full OTA
Jesse Zhao75bcea02015-01-06 10:59:53 -08001398 if not self.src:
Sami Tolvanendd67a292014-12-09 16:40:34 +00001399 script.Print("Image %s will be patched unconditionally." % (partition,))
Tao Baof9efe282016-04-14 15:58:05 -07001400
1401 # incremental OTA
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001402 else:
Tao Baod522bdc2016-04-12 15:53:16 -07001403 if touched_blocks_only and self.version >= 3:
1404 ranges = self.touched_src_ranges
1405 expected_sha1 = self.touched_src_sha1
1406 else:
1407 ranges = self.src.care_map.subtract(self.src.clobbered_blocks)
1408 expected_sha1 = self.src.TotalSha1()
Tao Baof9efe282016-04-14 15:58:05 -07001409
1410 # No blocks to be checked, skipping.
1411 if not ranges:
1412 return
1413
Tao Bao5ece99d2015-05-12 11:42:31 -07001414 ranges_str = ranges.to_string_raw()
Tao Bao9beea2a2017-02-28 19:15:21 -08001415 if self.version >= 3:
Sami Tolvanene09d0962015-04-24 11:54:01 +01001416 script.AppendExtra(('if (range_sha1("%s", "%s") == "%s" || '
1417 'block_image_verify("%s", '
Michael Runge910b0052015-02-11 19:28:08 -08001418 'package_extract_file("%s.transfer.list"), '
Sami Tolvanene09d0962015-04-24 11:54:01 +01001419 '"%s.new.dat", "%s.patch.dat")) then') % (
Tao Baod522bdc2016-04-12 15:53:16 -07001420 self.device, ranges_str, expected_sha1,
Sami Tolvanene09d0962015-04-24 11:54:01 +01001421 self.device, partition, partition, partition))
Michael Runge910b0052015-02-11 19:28:08 -08001422 else:
Dan Albert8b72aef2015-03-23 19:13:21 -07001423 script.AppendExtra('if range_sha1("%s", "%s") == "%s" then' % (
Tao Bao5ece99d2015-05-12 11:42:31 -07001424 self.device, ranges_str, self.src.TotalSha1()))
Tao Baodd2a5892015-03-12 12:32:37 -07001425 script.Print('Verified %s image...' % (partition,))
Dan Albert8b72aef2015-03-23 19:13:21 -07001426 script.AppendExtra('else')
Sami Tolvanendd67a292014-12-09 16:40:34 +00001427
Tianjie Xufc3422a2015-12-15 11:53:59 -08001428 if self.version >= 4:
1429
1430 # Bug: 21124327
1431 # When generating incrementals for the system and vendor partitions in
1432 # version 4 or newer, explicitly check the first block (which contains
1433 # the superblock) of the partition to see if it's what we expect. If
1434 # this check fails, give an explicit log message about the partition
1435 # having been remounted R/W (the most likely explanation).
1436 if self.check_first_block:
1437 script.AppendExtra('check_first_block("%s");' % (self.device,))
1438
1439 # If version >= 4, try block recovery before abort update
Tianjie Xu209db462016-05-24 17:34:52 -07001440 if partition == "system":
1441 code = ErrorCode.SYSTEM_RECOVER_FAILURE
1442 else:
1443 code = ErrorCode.VENDOR_RECOVER_FAILURE
Tianjie Xufc3422a2015-12-15 11:53:59 -08001444 script.AppendExtra((
1445 'ifelse (block_image_recover("{device}", "{ranges}") && '
1446 'block_image_verify("{device}", '
1447 'package_extract_file("{partition}.transfer.list"), '
1448 '"{partition}.new.dat", "{partition}.patch.dat"), '
1449 'ui_print("{partition} recovered successfully."), '
Tianjie Xu209db462016-05-24 17:34:52 -07001450 'abort("E{code}: {partition} partition fails to recover"));\n'
Tianjie Xufc3422a2015-12-15 11:53:59 -08001451 'endif;').format(device=self.device, ranges=ranges_str,
Tianjie Xu209db462016-05-24 17:34:52 -07001452 partition=partition, code=code))
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001453
Tao Baodd2a5892015-03-12 12:32:37 -07001454 # Abort the OTA update. Note that the incremental OTA cannot be applied
1455 # even if it may match the checksum of the target partition.
1456 # a) If version < 3, operations like move and erase will make changes
1457 # unconditionally and damage the partition.
1458 # b) If version >= 3, it won't even reach here.
Tianjie Xufc3422a2015-12-15 11:53:59 -08001459 else:
Tianjie Xu209db462016-05-24 17:34:52 -07001460 if partition == "system":
1461 code = ErrorCode.SYSTEM_VERIFICATION_FAILURE
1462 else:
1463 code = ErrorCode.VENDOR_VERIFICATION_FAILURE
1464 script.AppendExtra((
1465 'abort("E%d: %s partition has unexpected contents");\n'
1466 'endif;') % (code, partition))
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001467
Tao Bao5fcaaef2015-06-01 13:40:49 -07001468 def _WritePostInstallVerifyScript(self, script):
1469 partition = self.partition
1470 script.Print('Verifying the updated %s image...' % (partition,))
1471 # Unlike pre-install verification, clobbered_blocks should not be ignored.
1472 ranges = self.tgt.care_map
1473 ranges_str = ranges.to_string_raw()
1474 script.AppendExtra('if range_sha1("%s", "%s") == "%s" then' % (
1475 self.device, ranges_str,
1476 self.tgt.TotalSha1(include_clobbered_blocks=True)))
Tao Baoe9b61912015-07-09 17:37:49 -07001477
1478 # Bug: 20881595
1479 # Verify that extended blocks are really zeroed out.
1480 if self.tgt.extended:
1481 ranges_str = self.tgt.extended.to_string_raw()
1482 script.AppendExtra('if range_sha1("%s", "%s") == "%s" then' % (
1483 self.device, ranges_str,
1484 self._HashZeroBlocks(self.tgt.extended.size())))
1485 script.Print('Verified the updated %s image.' % (partition,))
Tianjie Xu209db462016-05-24 17:34:52 -07001486 if partition == "system":
1487 code = ErrorCode.SYSTEM_NONZERO_CONTENTS
1488 else:
1489 code = ErrorCode.VENDOR_NONZERO_CONTENTS
Tao Baoe9b61912015-07-09 17:37:49 -07001490 script.AppendExtra(
1491 'else\n'
Tianjie Xu209db462016-05-24 17:34:52 -07001492 ' abort("E%d: %s partition has unexpected non-zero contents after '
1493 'OTA update");\n'
1494 'endif;' % (code, partition))
Tao Baoe9b61912015-07-09 17:37:49 -07001495 else:
1496 script.Print('Verified the updated %s image.' % (partition,))
1497
Tianjie Xu209db462016-05-24 17:34:52 -07001498 if partition == "system":
1499 code = ErrorCode.SYSTEM_UNEXPECTED_CONTENTS
1500 else:
1501 code = ErrorCode.VENDOR_UNEXPECTED_CONTENTS
1502
Tao Bao5fcaaef2015-06-01 13:40:49 -07001503 script.AppendExtra(
1504 'else\n'
Tianjie Xu209db462016-05-24 17:34:52 -07001505 ' abort("E%d: %s partition has unexpected contents after OTA '
1506 'update");\n'
1507 'endif;' % (code, partition))
Tao Bao5fcaaef2015-06-01 13:40:49 -07001508
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001509 def _WriteUpdate(self, script, output_zip):
Dan Albert8e0178d2015-01-27 15:53:15 -08001510 ZipWrite(output_zip,
1511 '{}.transfer.list'.format(self.path),
1512 '{}.transfer.list'.format(self.partition))
1513 ZipWrite(output_zip,
1514 '{}.new.dat'.format(self.path),
1515 '{}.new.dat'.format(self.partition))
1516 ZipWrite(output_zip,
1517 '{}.patch.dat'.format(self.path),
1518 '{}.patch.dat'.format(self.partition),
1519 compress_type=zipfile.ZIP_STORED)
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001520
Tianjie Xu209db462016-05-24 17:34:52 -07001521 if self.partition == "system":
1522 code = ErrorCode.SYSTEM_UPDATE_FAILURE
1523 else:
1524 code = ErrorCode.VENDOR_UPDATE_FAILURE
1525
Dan Albert8e0178d2015-01-27 15:53:15 -08001526 call = ('block_image_update("{device}", '
1527 'package_extract_file("{partition}.transfer.list"), '
Tianjie Xub2deb222016-03-25 15:01:33 -07001528 '"{partition}.new.dat", "{partition}.patch.dat") ||\n'
Tianjie Xu209db462016-05-24 17:34:52 -07001529 ' abort("E{code}: Failed to update {partition} image.");'.format(
1530 device=self.device, partition=self.partition, code=code))
Dan Albert8b72aef2015-03-23 19:13:21 -07001531 script.AppendExtra(script.WordWrap(call))
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001532
Dan Albert8b72aef2015-03-23 19:13:21 -07001533 def _HashBlocks(self, source, ranges): # pylint: disable=no-self-use
Sami Tolvanendd67a292014-12-09 16:40:34 +00001534 data = source.ReadRangeSet(ranges)
1535 ctx = sha1()
1536
1537 for p in data:
1538 ctx.update(p)
1539
1540 return ctx.hexdigest()
1541
Tao Baoe9b61912015-07-09 17:37:49 -07001542 def _HashZeroBlocks(self, num_blocks): # pylint: disable=no-self-use
1543 """Return the hash value for all zero blocks."""
1544 zero_block = '\x00' * 4096
1545 ctx = sha1()
1546 for _ in range(num_blocks):
1547 ctx.update(zero_block)
1548
1549 return ctx.hexdigest()
1550
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001551
1552DataImage = blockimgdiff.DataImage
1553
Doug Zongker96a57e72010-09-26 14:57:41 -07001554# map recovery.fstab's fs_types to mount/format "partition types"
Dan Albert8b72aef2015-03-23 19:13:21 -07001555PARTITION_TYPES = {
Dan Albert8b72aef2015-03-23 19:13:21 -07001556 "ext4": "EMMC",
1557 "emmc": "EMMC",
Mohamad Ayyash95e74c12015-05-01 15:39:36 -07001558 "f2fs": "EMMC",
1559 "squashfs": "EMMC"
Dan Albert8b72aef2015-03-23 19:13:21 -07001560}
Doug Zongker96a57e72010-09-26 14:57:41 -07001561
1562def GetTypeAndDevice(mount_point, info):
1563 fstab = info["fstab"]
1564 if fstab:
Dan Albert8b72aef2015-03-23 19:13:21 -07001565 return (PARTITION_TYPES[fstab[mount_point].fs_type],
1566 fstab[mount_point].device)
Doug Zongker96a57e72010-09-26 14:57:41 -07001567 else:
Dan Albert8b72aef2015-03-23 19:13:21 -07001568 raise KeyError
Baligh Uddinbeb6afd2013-11-13 00:22:34 +00001569
1570
1571def ParseCertificate(data):
1572 """Parse a PEM-format certificate."""
1573 cert = []
1574 save = False
1575 for line in data.split("\n"):
1576 if "--END CERTIFICATE--" in line:
1577 break
1578 if save:
1579 cert.append(line)
1580 if "--BEGIN CERTIFICATE--" in line:
1581 save = True
1582 cert = "".join(cert).decode('base64')
1583 return cert
Doug Zongkerc9253822014-02-04 12:17:58 -08001584
Doug Zongker412c02f2014-02-13 10:58:24 -08001585def MakeRecoveryPatch(input_dir, output_sink, recovery_img, boot_img,
1586 info_dict=None):
Doug Zongkerc9253822014-02-04 12:17:58 -08001587 """Generate a binary patch that creates the recovery image starting
1588 with the boot image. (Most of the space in these images is just the
1589 kernel, which is identical for the two, so the resulting patch
1590 should be efficient.) Add it to the output zip, along with a shell
1591 script that is run from init.rc on first boot to actually do the
1592 patching and install the new recovery image.
1593
1594 recovery_img and boot_img should be File objects for the
1595 corresponding images. info should be the dictionary returned by
1596 common.LoadInfoDict() on the input target_files.
1597 """
1598
Doug Zongker412c02f2014-02-13 10:58:24 -08001599 if info_dict is None:
1600 info_dict = OPTIONS.info_dict
1601
Tao Baof2cffbd2015-07-22 12:33:18 -07001602 full_recovery_image = info_dict.get("full_recovery_image", None) == "true"
Tao Bao7a5bf8a2015-07-21 18:01:20 -07001603 system_root_image = info_dict.get("system_root_image", None) == "true"
Doug Zongkerc9253822014-02-04 12:17:58 -08001604
Tao Baof2cffbd2015-07-22 12:33:18 -07001605 if full_recovery_image:
1606 output_sink("etc/recovery.img", recovery_img.data)
1607
1608 else:
1609 diff_program = ["imgdiff"]
1610 path = os.path.join(input_dir, "SYSTEM", "etc", "recovery-resource.dat")
1611 if os.path.exists(path):
1612 diff_program.append("-b")
1613 diff_program.append(path)
1614 bonus_args = "-b /system/etc/recovery-resource.dat"
1615 else:
1616 bonus_args = ""
1617
1618 d = Difference(recovery_img, boot_img, diff_program=diff_program)
1619 _, _, patch = d.ComputePatch()
1620 output_sink("recovery-from-boot.p", patch)
Doug Zongkerc9253822014-02-04 12:17:58 -08001621
Dan Albertebb19aa2015-03-27 19:11:53 -07001622 try:
Tao Bao6f0b2192015-10-13 16:37:12 -07001623 # The following GetTypeAndDevice()s need to use the path in the target
1624 # info_dict instead of source_info_dict.
Dan Albertebb19aa2015-03-27 19:11:53 -07001625 boot_type, boot_device = GetTypeAndDevice("/boot", info_dict)
1626 recovery_type, recovery_device = GetTypeAndDevice("/recovery", info_dict)
1627 except KeyError:
Ying Wanga961a092014-07-29 11:42:37 -07001628 return
Doug Zongkerc9253822014-02-04 12:17:58 -08001629
Tao Baof2cffbd2015-07-22 12:33:18 -07001630 if full_recovery_image:
1631 sh = """#!/system/bin/sh
1632if ! applypatch -c %(type)s:%(device)s:%(size)d:%(sha1)s; then
1633 applypatch /system/etc/recovery.img %(type)s:%(device)s %(sha1)s %(size)d && log -t recovery "Installing new recovery image: succeeded" || log -t recovery "Installing new recovery image: failed"
1634else
1635 log -t recovery "Recovery image already installed"
1636fi
1637""" % {'type': recovery_type,
1638 'device': recovery_device,
1639 'sha1': recovery_img.sha1,
1640 'size': recovery_img.size}
1641 else:
1642 sh = """#!/system/bin/sh
Doug Zongkerc9253822014-02-04 12:17:58 -08001643if ! applypatch -c %(recovery_type)s:%(recovery_device)s:%(recovery_size)d:%(recovery_sha1)s; then
1644 applypatch %(bonus_args)s %(boot_type)s:%(boot_device)s:%(boot_size)d:%(boot_sha1)s %(recovery_type)s:%(recovery_device)s %(recovery_sha1)s %(recovery_size)d %(boot_sha1)s:/system/recovery-from-boot.p && log -t recovery "Installing new recovery image: succeeded" || log -t recovery "Installing new recovery image: failed"
1645else
1646 log -t recovery "Recovery image already installed"
1647fi
Dan Albert8b72aef2015-03-23 19:13:21 -07001648""" % {'boot_size': boot_img.size,
1649 'boot_sha1': boot_img.sha1,
1650 'recovery_size': recovery_img.size,
1651 'recovery_sha1': recovery_img.sha1,
1652 'boot_type': boot_type,
1653 'boot_device': boot_device,
1654 'recovery_type': recovery_type,
1655 'recovery_device': recovery_device,
1656 'bonus_args': bonus_args}
Doug Zongkerc9253822014-02-04 12:17:58 -08001657
1658 # The install script location moved from /system/etc to /system/bin
Tao Bao9f0c8df2015-07-07 18:31:47 -07001659 # in the L release. Parse init.*.rc files to find out where the
Doug Zongkerc9253822014-02-04 12:17:58 -08001660 # target-files expects it to be, and put it there.
1661 sh_location = "etc/install-recovery.sh"
Tao Bao9f0c8df2015-07-07 18:31:47 -07001662 found = False
Tao Bao7a5bf8a2015-07-21 18:01:20 -07001663 if system_root_image:
1664 init_rc_dir = os.path.join(input_dir, "ROOT")
1665 else:
1666 init_rc_dir = os.path.join(input_dir, "BOOT", "RAMDISK")
Tao Bao9f0c8df2015-07-07 18:31:47 -07001667 init_rc_files = os.listdir(init_rc_dir)
1668 for init_rc_file in init_rc_files:
1669 if (not init_rc_file.startswith('init.') or
1670 not init_rc_file.endswith('.rc')):
1671 continue
1672
1673 with open(os.path.join(init_rc_dir, init_rc_file)) as f:
Doug Zongkerc9253822014-02-04 12:17:58 -08001674 for line in f:
Dan Albert8b72aef2015-03-23 19:13:21 -07001675 m = re.match(r"^service flash_recovery /system/(\S+)\s*$", line)
Doug Zongkerc9253822014-02-04 12:17:58 -08001676 if m:
1677 sh_location = m.group(1)
Tao Bao9f0c8df2015-07-07 18:31:47 -07001678 found = True
Doug Zongkerc9253822014-02-04 12:17:58 -08001679 break
Tao Bao9f0c8df2015-07-07 18:31:47 -07001680
1681 if found:
1682 break
1683
Tao Bao89fbb0f2017-01-10 10:47:58 -08001684 print("putting script in", sh_location)
Doug Zongkerc9253822014-02-04 12:17:58 -08001685
1686 output_sink(sh_location, sh)