blob: 79b218f5f20d907790ed306d557da52b9b4b59f0 [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
Doug Zongkerea5d7a92010-09-12 15:26:16 -070015import copy
Doug Zongker8ce7c252009-05-22 13:34:54 -070016import errno
Doug Zongkereef39442009-04-02 12:14:19 -070017import getopt
18import getpass
Doug Zongker05d3dea2009-06-22 11:32:31 -070019import imp
Doug Zongkereef39442009-04-02 12:14:19 -070020import os
Ying Wang7e6d4e42010-12-13 16:25:36 -080021import platform
Doug Zongkereef39442009-04-02 12:14:19 -070022import re
T.R. Fullhart37e10522013-03-18 10:31:26 -070023import shlex
Doug Zongkereef39442009-04-02 12:14:19 -070024import shutil
25import subprocess
26import sys
27import tempfile
Doug Zongkerea5d7a92010-09-12 15:26:16 -070028import threading
29import time
Doug Zongker048e7ca2009-06-15 14:31:53 -070030import zipfile
Doug Zongkereef39442009-04-02 12:14:19 -070031
Doug Zongkerab7ca1d2014-08-26 10:40:28 -070032import blockimgdiff
33
Tao Baof3282b42015-04-01 11:21:55 -070034from hashlib import sha1 as sha1
Doug Zongker55d93282011-01-25 17:03:34 -080035
Doug Zongkereef39442009-04-02 12:14:19 -070036
Dan Albert8b72aef2015-03-23 19:13:21 -070037class Options(object):
38 def __init__(self):
39 platform_search_path = {
40 "linux2": "out/host/linux-x86",
41 "darwin": "out/host/darwin-x86",
Doug Zongker85448772014-09-09 14:59:20 -070042 }
Doug Zongker85448772014-09-09 14:59:20 -070043
Dan Albert8b72aef2015-03-23 19:13:21 -070044 self.search_path = platform_search_path.get(sys.platform, None)
45 self.signapk_path = "framework/signapk.jar" # Relative to search_path
Alex Klyubin9667b182015-12-10 13:38:50 -080046 self.signapk_shared_library_path = "lib64" # Relative to search_path
Dan Albert8b72aef2015-03-23 19:13:21 -070047 self.extra_signapk_args = []
48 self.java_path = "java" # Use the one on the path by default.
Tao Baoe95540e2016-11-08 12:08:53 -080049 self.java_args = ["-Xmx2048m"] # The default JVM args.
Dan Albert8b72aef2015-03-23 19:13:21 -070050 self.public_key_suffix = ".x509.pem"
51 self.private_key_suffix = ".pk8"
Dan Albertcd9ecc02015-03-27 16:37:23 -070052 # use otatools built boot_signer by default
53 self.boot_signer_path = "boot_signer"
Baligh Uddin601ddea2015-06-09 15:48:14 -070054 self.boot_signer_args = []
55 self.verity_signer_path = None
56 self.verity_signer_args = []
Dan Albert8b72aef2015-03-23 19:13:21 -070057 self.verbose = False
58 self.tempfiles = []
59 self.device_specific = None
60 self.extras = {}
61 self.info_dict = None
Tao Bao6f0b2192015-10-13 16:37:12 -070062 self.source_info_dict = None
63 self.target_info_dict = None
Dan Albert8b72aef2015-03-23 19:13:21 -070064 self.worker_threads = None
Tao Bao575d68a2015-08-07 19:49:45 -070065 # Stash size cannot exceed cache_size * threshold.
66 self.cache_size = None
67 self.stash_threshold = 0.8
Dan Albert8b72aef2015-03-23 19:13:21 -070068
69
70OPTIONS = Options()
Doug Zongkereef39442009-04-02 12:14:19 -070071
Doug Zongkerf6a53aa2009-12-15 15:06:55 -080072
73# Values for "certificate" in apkcerts that mean special things.
74SPECIAL_CERT_STRINGS = ("PRESIGNED", "EXTERNAL")
75
Tianjie Xu209db462016-05-24 17:34:52 -070076class ErrorCode(object):
77 """Define error_codes for failures that happen during the actual
78 update package installation.
79
80 Error codes 0-999 are reserved for failures before the package
81 installation (i.e. low battery, package verification failure).
82 Detailed code in 'bootable/recovery/error_code.h' """
83
84 SYSTEM_VERIFICATION_FAILURE = 1000
85 SYSTEM_UPDATE_FAILURE = 1001
86 SYSTEM_UNEXPECTED_CONTENTS = 1002
87 SYSTEM_NONZERO_CONTENTS = 1003
88 SYSTEM_RECOVER_FAILURE = 1004
89 VENDOR_VERIFICATION_FAILURE = 2000
90 VENDOR_UPDATE_FAILURE = 2001
91 VENDOR_UNEXPECTED_CONTENTS = 2002
92 VENDOR_NONZERO_CONTENTS = 2003
93 VENDOR_RECOVER_FAILURE = 2004
94 OEM_PROP_MISMATCH = 3000
95 FINGERPRINT_MISMATCH = 3001
96 THUMBPRINT_MISMATCH = 3002
97 OLDER_BUILD = 3003
98 DEVICE_MISMATCH = 3004
99 BAD_PATCH_FILE = 3005
100 INSUFFICIENT_CACHE_SPACE = 3006
101 TUNE_PARTITION_FAILURE = 3007
102 APPLY_PATCH_FAILURE = 3008
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800103
Dan Albert8b72aef2015-03-23 19:13:21 -0700104class ExternalError(RuntimeError):
105 pass
Doug Zongkereef39442009-04-02 12:14:19 -0700106
107
108def Run(args, **kwargs):
109 """Create and return a subprocess.Popen object, printing the command
110 line on the terminal if -v was specified."""
111 if OPTIONS.verbose:
112 print " running: ", " ".join(args)
113 return subprocess.Popen(args, **kwargs)
114
115
Ying Wang7e6d4e42010-12-13 16:25:36 -0800116def CloseInheritedPipes():
117 """ Gmake in MAC OS has file descriptor (PIPE) leak. We close those fds
118 before doing other work."""
119 if platform.system() != "Darwin":
120 return
121 for d in range(3, 1025):
122 try:
123 stat = os.fstat(d)
124 if stat is not None:
125 pipebit = stat[0] & 0x1000
126 if pipebit != 0:
127 os.close(d)
128 except OSError:
129 pass
130
131
Tao Bao2c15d9e2015-07-09 11:51:16 -0700132def LoadInfoDict(input_file, input_dir=None):
Doug Zongkerc19a8d52010-07-01 15:30:11 -0700133 """Read and parse the META/misc_info.txt key/value pairs from the
134 input target files and return a dict."""
135
Doug Zongkerc9253822014-02-04 12:17:58 -0800136 def read_helper(fn):
Dan Albert8b72aef2015-03-23 19:13:21 -0700137 if isinstance(input_file, zipfile.ZipFile):
138 return input_file.read(fn)
Doug Zongkerc9253822014-02-04 12:17:58 -0800139 else:
Dan Albert8b72aef2015-03-23 19:13:21 -0700140 path = os.path.join(input_file, *fn.split("/"))
Doug Zongkerc9253822014-02-04 12:17:58 -0800141 try:
142 with open(path) as f:
143 return f.read()
Dan Albert8b72aef2015-03-23 19:13:21 -0700144 except IOError as e:
Doug Zongkerc9253822014-02-04 12:17:58 -0800145 if e.errno == errno.ENOENT:
146 raise KeyError(fn)
Doug Zongkerc19a8d52010-07-01 15:30:11 -0700147 d = {}
148 try:
Michael Runge6e836112014-04-15 17:40:21 -0700149 d = LoadDictionaryFromLines(read_helper("META/misc_info.txt").split("\n"))
Doug Zongker37974732010-09-16 17:44:38 -0700150 except KeyError:
151 # ok if misc_info.txt doesn't exist
152 pass
Doug Zongkerc19a8d52010-07-01 15:30:11 -0700153
Doug Zongker37974732010-09-16 17:44:38 -0700154 # backwards compatibility: These values used to be in their own
155 # files. Look for them, in case we're processing an old
156 # target_files zip.
157
Doug Zongker37974732010-09-16 17:44:38 -0700158 if "recovery_api_version" not in d:
159 try:
Dan Albert8b72aef2015-03-23 19:13:21 -0700160 d["recovery_api_version"] = read_helper(
161 "META/recovery-api-version.txt").strip()
Doug Zongker37974732010-09-16 17:44:38 -0700162 except KeyError:
163 raise ValueError("can't find recovery API version in input target-files")
164
165 if "tool_extensions" not in d:
166 try:
Doug Zongkerc9253822014-02-04 12:17:58 -0800167 d["tool_extensions"] = read_helper("META/tool-extensions.txt").strip()
Doug Zongker37974732010-09-16 17:44:38 -0700168 except KeyError:
169 # ok if extensions don't exist
170 pass
171
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800172 if "fstab_version" not in d:
173 d["fstab_version"] = "1"
174
Tao Bao84e75682015-07-19 02:38:53 -0700175 # A few properties are stored as links to the files in the out/ directory.
176 # It works fine with the build system. However, they are no longer available
177 # when (re)generating from target_files zip. If input_dir is not None, we
178 # are doing repacking. Redirect those properties to the actual files in the
179 # unzipped directory.
Tao Bao2c15d9e2015-07-09 11:51:16 -0700180 if input_dir is not None:
Stephen Smalleyd3a803e2015-08-04 14:59:06 -0400181 # We carry a copy of file_contexts.bin under META/. If not available,
182 # search BOOT/RAMDISK/. Note that sometimes we may need a different file
Tao Bao84e75682015-07-19 02:38:53 -0700183 # to build images than the one running on device, such as when enabling
184 # system_root_image. In that case, we must have the one for image
185 # generation copied to META/.
Tao Bao79735a62015-08-28 10:52:03 -0700186 fc_basename = os.path.basename(d.get("selinux_fc", "file_contexts"))
187 fc_config = os.path.join(input_dir, "META", fc_basename)
Tao Bao84e75682015-07-19 02:38:53 -0700188 if d.get("system_root_image") == "true":
189 assert os.path.exists(fc_config)
Tao Bao2c15d9e2015-07-09 11:51:16 -0700190 if not os.path.exists(fc_config):
Tao Bao79735a62015-08-28 10:52:03 -0700191 fc_config = os.path.join(input_dir, "BOOT", "RAMDISK", fc_basename)
Tao Bao2c15d9e2015-07-09 11:51:16 -0700192 if not os.path.exists(fc_config):
193 fc_config = None
194
195 if fc_config:
196 d["selinux_fc"] = fc_config
197
Tao Bao84e75682015-07-19 02:38:53 -0700198 # Similarly we need to redirect "ramdisk_dir" and "ramdisk_fs_config".
199 if d.get("system_root_image") == "true":
200 d["ramdisk_dir"] = os.path.join(input_dir, "ROOT")
201 d["ramdisk_fs_config"] = os.path.join(
202 input_dir, "META", "root_filesystem_config.txt")
203
Tao Baof54216f2016-03-29 15:12:37 -0700204 # Redirect {system,vendor}_base_fs_file.
205 if "system_base_fs_file" in d:
206 basename = os.path.basename(d["system_base_fs_file"])
207 system_base_fs_file = os.path.join(input_dir, "META", basename)
Tao Baob079b502016-05-03 08:01:19 -0700208 if os.path.exists(system_base_fs_file):
209 d["system_base_fs_file"] = system_base_fs_file
210 else:
211 print "Warning: failed to find system base fs file: %s" % (
212 system_base_fs_file,)
213 del d["system_base_fs_file"]
Tao Baof54216f2016-03-29 15:12:37 -0700214
215 if "vendor_base_fs_file" in d:
216 basename = os.path.basename(d["vendor_base_fs_file"])
217 vendor_base_fs_file = os.path.join(input_dir, "META", basename)
Tao Baob079b502016-05-03 08:01:19 -0700218 if os.path.exists(vendor_base_fs_file):
219 d["vendor_base_fs_file"] = vendor_base_fs_file
220 else:
221 print "Warning: failed to find vendor base fs file: %s" % (
222 vendor_base_fs_file,)
223 del d["vendor_base_fs_file"]
Tao Baof54216f2016-03-29 15:12:37 -0700224
Doug Zongker37974732010-09-16 17:44:38 -0700225 try:
Doug Zongkerc9253822014-02-04 12:17:58 -0800226 data = read_helper("META/imagesizes.txt")
Doug Zongker37974732010-09-16 17:44:38 -0700227 for line in data.split("\n"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700228 if not line:
229 continue
Doug Zongker1684d9c2010-09-17 07:44:38 -0700230 name, value = line.split(" ", 1)
Dan Albert8b72aef2015-03-23 19:13:21 -0700231 if not value:
232 continue
Doug Zongker37974732010-09-16 17:44:38 -0700233 if name == "blocksize":
234 d[name] = value
235 else:
236 d[name + "_size"] = value
237 except KeyError:
238 pass
239
240 def makeint(key):
241 if key in d:
242 d[key] = int(d[key], 0)
243
244 makeint("recovery_api_version")
245 makeint("blocksize")
246 makeint("system_size")
Daniel Rosenbergf4eabc32014-07-10 15:42:38 -0700247 makeint("vendor_size")
Doug Zongker37974732010-09-16 17:44:38 -0700248 makeint("userdata_size")
Ying Wang9f8e8db2011-11-04 11:37:01 -0700249 makeint("cache_size")
Doug Zongker37974732010-09-16 17:44:38 -0700250 makeint("recovery_size")
251 makeint("boot_size")
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800252 makeint("fstab_version")
Doug Zongkerc19a8d52010-07-01 15:30:11 -0700253
Tao Bao48550cc2015-11-19 17:05:46 -0800254 if d.get("no_recovery", False) == "true":
255 d["fstab"] = None
256 else:
257 d["fstab"] = LoadRecoveryFSTab(read_helper, d["fstab_version"],
258 d.get("system_root_image", False))
Doug Zongkerc9253822014-02-04 12:17:58 -0800259 d["build.prop"] = LoadBuildProp(read_helper)
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700260 return d
261
Doug Zongkerc9253822014-02-04 12:17:58 -0800262def LoadBuildProp(read_helper):
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700263 try:
Doug Zongkerc9253822014-02-04 12:17:58 -0800264 data = read_helper("SYSTEM/build.prop")
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700265 except KeyError:
266 print "Warning: could not find SYSTEM/build.prop in %s" % zip
267 data = ""
Michael Runge6e836112014-04-15 17:40:21 -0700268 return LoadDictionaryFromLines(data.split("\n"))
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700269
Michael Runge6e836112014-04-15 17:40:21 -0700270def LoadDictionaryFromLines(lines):
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700271 d = {}
Michael Runge6e836112014-04-15 17:40:21 -0700272 for line in lines:
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700273 line = line.strip()
Dan Albert8b72aef2015-03-23 19:13:21 -0700274 if not line or line.startswith("#"):
275 continue
Ying Wang114b46f2014-04-15 11:24:00 -0700276 if "=" in line:
277 name, value = line.split("=", 1)
278 d[name] = value
Doug Zongkerc19a8d52010-07-01 15:30:11 -0700279 return d
280
Daniel Rosenberge6853b02015-06-05 17:59:27 -0700281def LoadRecoveryFSTab(read_helper, fstab_version, system_root_image=False):
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700282 class Partition(object):
Tao Bao548eb762015-06-10 12:32:41 -0700283 def __init__(self, mount_point, fs_type, device, length, device2, context):
Dan Albert8b72aef2015-03-23 19:13:21 -0700284 self.mount_point = mount_point
285 self.fs_type = fs_type
286 self.device = device
287 self.length = length
288 self.device2 = device2
Tao Bao548eb762015-06-10 12:32:41 -0700289 self.context = context
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700290
291 try:
Doug Zongkerc9253822014-02-04 12:17:58 -0800292 data = read_helper("RECOVERY/RAMDISK/etc/recovery.fstab")
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700293 except KeyError:
Doug Zongkerc9253822014-02-04 12:17:58 -0800294 print "Warning: could not find RECOVERY/RAMDISK/etc/recovery.fstab"
Jeff Davidson033fbe22011-10-26 18:08:09 -0700295 data = ""
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700296
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800297 if fstab_version == 1:
298 d = {}
299 for line in data.split("\n"):
300 line = line.strip()
Dan Albert8b72aef2015-03-23 19:13:21 -0700301 if not line or line.startswith("#"):
302 continue
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800303 pieces = line.split()
Dan Albert8b72aef2015-03-23 19:13:21 -0700304 if not 3 <= len(pieces) <= 4:
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800305 raise ValueError("malformed recovery.fstab line: \"%s\"" % (line,))
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800306 options = None
307 if len(pieces) >= 4:
308 if pieces[3].startswith("/"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700309 device2 = pieces[3]
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800310 if len(pieces) >= 5:
311 options = pieces[4]
312 else:
Dan Albert8b72aef2015-03-23 19:13:21 -0700313 device2 = None
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800314 options = pieces[3]
Doug Zongker086cbb02011-02-17 15:54:20 -0800315 else:
Dan Albert8b72aef2015-03-23 19:13:21 -0700316 device2 = None
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700317
Dan Albert8b72aef2015-03-23 19:13:21 -0700318 mount_point = pieces[0]
319 length = 0
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800320 if options:
321 options = options.split(",")
322 for i in options:
323 if i.startswith("length="):
Dan Albert8b72aef2015-03-23 19:13:21 -0700324 length = int(i[7:])
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800325 else:
Dan Albert8b72aef2015-03-23 19:13:21 -0700326 print "%s: unknown option \"%s\"" % (mount_point, i)
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800327
Dan Albert8b72aef2015-03-23 19:13:21 -0700328 d[mount_point] = Partition(mount_point=mount_point, fs_type=pieces[1],
329 device=pieces[2], length=length,
330 device2=device2)
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800331
332 elif fstab_version == 2:
333 d = {}
334 for line in data.split("\n"):
335 line = line.strip()
Dan Albert8b72aef2015-03-23 19:13:21 -0700336 if not line or line.startswith("#"):
337 continue
Tao Bao548eb762015-06-10 12:32:41 -0700338 # <src> <mnt_point> <type> <mnt_flags and options> <fs_mgr_flags>
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800339 pieces = line.split()
340 if len(pieces) != 5:
341 raise ValueError("malformed recovery.fstab line: \"%s\"" % (line,))
342
343 # Ignore entries that are managed by vold
344 options = pieces[4]
Dan Albert8b72aef2015-03-23 19:13:21 -0700345 if "voldmanaged=" in options:
346 continue
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800347
348 # It's a good line, parse it
Dan Albert8b72aef2015-03-23 19:13:21 -0700349 length = 0
Doug Zongker086cbb02011-02-17 15:54:20 -0800350 options = options.split(",")
351 for i in options:
352 if i.startswith("length="):
Dan Albert8b72aef2015-03-23 19:13:21 -0700353 length = int(i[7:])
Doug Zongker086cbb02011-02-17 15:54:20 -0800354 else:
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800355 # Ignore all unknown options in the unified fstab
356 continue
Doug Zongker086cbb02011-02-17 15:54:20 -0800357
Tao Bao548eb762015-06-10 12:32:41 -0700358 mount_flags = pieces[3]
359 # Honor the SELinux context if present.
360 context = None
361 for i in mount_flags.split(","):
362 if i.startswith("context="):
363 context = i
364
Dan Albert8b72aef2015-03-23 19:13:21 -0700365 mount_point = pieces[1]
366 d[mount_point] = Partition(mount_point=mount_point, fs_type=pieces[2],
Tao Bao548eb762015-06-10 12:32:41 -0700367 device=pieces[0], length=length,
368 device2=None, context=context)
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800369
370 else:
371 raise ValueError("Unknown fstab_version: \"%d\"" % (fstab_version,))
372
Daniel Rosenberge6853b02015-06-05 17:59:27 -0700373 # / is used for the system mount point when the root directory is included in
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700374 # system. Other areas assume system is always at "/system" so point /system
375 # at /.
Daniel Rosenberge6853b02015-06-05 17:59:27 -0700376 if system_root_image:
377 assert not d.has_key("/system") and d.has_key("/")
378 d["/system"] = d["/"]
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700379 return d
380
381
Doug Zongker37974732010-09-16 17:44:38 -0700382def DumpInfoDict(d):
383 for k, v in sorted(d.items()):
384 print "%-25s = (%s) %s" % (k, type(v).__name__, v)
Doug Zongkerc19a8d52010-07-01 15:30:11 -0700385
Dan Albert8b72aef2015-03-23 19:13:21 -0700386
David Zeuthen2ce63ed2016-09-15 13:43:54 -0400387def AppendAVBSigningArgs(cmd):
388 """Append signing arguments for avbtool."""
389 keypath = OPTIONS.info_dict.get("board_avb_key_path", None)
390 algorithm = OPTIONS.info_dict.get("board_avb_algorithm", None)
391 if not keypath or not algorithm:
392 algorithm = "SHA256_RSA4096"
393 keypath = "external/avb/test/data/testkey_rsa4096.pem"
394 cmd.extend(["--key", keypath, "--algorithm", algorithm])
395
396
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700397def _BuildBootableImage(sourcedir, fs_config_file, info_dict=None,
398 has_ramdisk=False):
399 """Build a bootable image from the specified sourcedir.
Doug Zongkere1c31ba2009-06-23 17:40:35 -0700400
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700401 Take a kernel, cmdline, and optionally a ramdisk directory from the input (in
402 'sourcedir'), and turn them into a boot image. Return the image data, or
403 None if sourcedir does not appear to contains files for building the
404 requested image."""
405
406 def make_ramdisk():
407 ramdisk_img = tempfile.NamedTemporaryFile()
408
409 if os.access(fs_config_file, os.F_OK):
410 cmd = ["mkbootfs", "-f", fs_config_file,
411 os.path.join(sourcedir, "RAMDISK")]
412 else:
413 cmd = ["mkbootfs", os.path.join(sourcedir, "RAMDISK")]
414 p1 = Run(cmd, stdout=subprocess.PIPE)
415 p2 = Run(["minigzip"], stdin=p1.stdout, stdout=ramdisk_img.file.fileno())
416
417 p2.wait()
418 p1.wait()
419 assert p1.returncode == 0, "mkbootfs of %s ramdisk failed" % (sourcedir,)
420 assert p2.returncode == 0, "minigzip of %s ramdisk failed" % (sourcedir,)
421
422 return ramdisk_img
423
424 if not os.access(os.path.join(sourcedir, "kernel"), os.F_OK):
425 return None
426
427 if has_ramdisk and not os.access(os.path.join(sourcedir, "RAMDISK"), os.F_OK):
Doug Zongkere1c31ba2009-06-23 17:40:35 -0700428 return None
Doug Zongkereef39442009-04-02 12:14:19 -0700429
Doug Zongkerd5131602012-08-02 14:46:42 -0700430 if info_dict is None:
431 info_dict = OPTIONS.info_dict
432
Doug Zongkereef39442009-04-02 12:14:19 -0700433 img = tempfile.NamedTemporaryFile()
434
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700435 if has_ramdisk:
436 ramdisk_img = make_ramdisk()
Doug Zongkereef39442009-04-02 12:14:19 -0700437
Bjorn Andersson612e2cd2012-11-25 16:53:44 -0800438 # use MKBOOTIMG from environ, or "mkbootimg" if empty or not set
439 mkbootimg = os.getenv('MKBOOTIMG') or "mkbootimg"
440
441 cmd = [mkbootimg, "--kernel", os.path.join(sourcedir, "kernel")]
Doug Zongker38a649f2009-06-17 09:07:09 -0700442
Benoit Fradina45a8682014-07-14 21:00:43 +0200443 fn = os.path.join(sourcedir, "second")
444 if os.access(fn, os.F_OK):
445 cmd.append("--second")
446 cmd.append(fn)
447
Doug Zongker171f1cd2009-06-15 22:36:37 -0700448 fn = os.path.join(sourcedir, "cmdline")
449 if os.access(fn, os.F_OK):
Doug Zongker38a649f2009-06-17 09:07:09 -0700450 cmd.append("--cmdline")
451 cmd.append(open(fn).read().rstrip("\n"))
452
453 fn = os.path.join(sourcedir, "base")
454 if os.access(fn, os.F_OK):
455 cmd.append("--base")
456 cmd.append(open(fn).read().rstrip("\n"))
457
Ying Wang4de6b5b2010-08-25 14:29:34 -0700458 fn = os.path.join(sourcedir, "pagesize")
459 if os.access(fn, os.F_OK):
460 cmd.append("--pagesize")
461 cmd.append(open(fn).read().rstrip("\n"))
462
Doug Zongkerd5131602012-08-02 14:46:42 -0700463 args = info_dict.get("mkbootimg_args", None)
464 if args and args.strip():
Jianxun Zhang09849492013-04-17 15:19:19 -0700465 cmd.extend(shlex.split(args))
Doug Zongkerd5131602012-08-02 14:46:42 -0700466
Sami Tolvanen3303d902016-03-15 16:49:30 +0000467 args = info_dict.get("mkbootimg_version_args", None)
468 if args and args.strip():
469 cmd.extend(shlex.split(args))
470
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700471 if has_ramdisk:
472 cmd.extend(["--ramdisk", ramdisk_img.name])
473
Tao Baod95e9fd2015-03-29 23:07:41 -0700474 img_unsigned = None
475 if info_dict.get("vboot", None):
476 img_unsigned = tempfile.NamedTemporaryFile()
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700477 cmd.extend(["--output", img_unsigned.name])
Tao Baod95e9fd2015-03-29 23:07:41 -0700478 else:
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700479 cmd.extend(["--output", img.name])
Doug Zongker38a649f2009-06-17 09:07:09 -0700480
481 p = Run(cmd, stdout=subprocess.PIPE)
Doug Zongkereef39442009-04-02 12:14:19 -0700482 p.communicate()
Doug Zongkere1c31ba2009-06-23 17:40:35 -0700483 assert p.returncode == 0, "mkbootimg of %s image failed" % (
484 os.path.basename(sourcedir),)
Doug Zongkereef39442009-04-02 12:14:19 -0700485
Sami Tolvanen8b3f08b2015-04-07 15:08:59 +0100486 if (info_dict.get("boot_signer", None) == "true" and
487 info_dict.get("verity_key", None)):
Geremy Condra95ebe7a2014-08-19 17:27:56 -0700488 path = "/" + os.path.basename(sourcedir).lower()
Baligh Uddin601ddea2015-06-09 15:48:14 -0700489 cmd = [OPTIONS.boot_signer_path]
490 cmd.extend(OPTIONS.boot_signer_args)
491 cmd.extend([path, img.name,
492 info_dict["verity_key"] + ".pk8",
493 info_dict["verity_key"] + ".x509.pem", img.name])
Geremy Condra95ebe7a2014-08-19 17:27:56 -0700494 p = Run(cmd, stdout=subprocess.PIPE)
495 p.communicate()
496 assert p.returncode == 0, "boot_signer of %s image failed" % path
497
Tao Baod95e9fd2015-03-29 23:07:41 -0700498 # Sign the image if vboot is non-empty.
499 elif info_dict.get("vboot", None):
500 path = "/" + os.path.basename(sourcedir).lower()
501 img_keyblock = tempfile.NamedTemporaryFile()
502 cmd = [info_dict["vboot_signer_cmd"], info_dict["futility"],
503 img_unsigned.name, info_dict["vboot_key"] + ".vbpubk",
Furquan Shaikh852b8de2015-08-10 11:43:45 -0700504 info_dict["vboot_key"] + ".vbprivk",
505 info_dict["vboot_subkey"] + ".vbprivk",
506 img_keyblock.name,
Tao Baod95e9fd2015-03-29 23:07:41 -0700507 img.name]
508 p = Run(cmd, stdout=subprocess.PIPE)
509 p.communicate()
510 assert p.returncode == 0, "vboot_signer of %s image failed" % path
511
Tao Baof3282b42015-04-01 11:21:55 -0700512 # Clean up the temp files.
513 img_unsigned.close()
514 img_keyblock.close()
515
David Zeuthen2ce63ed2016-09-15 13:43:54 -0400516 # AVB: if enabled, calculate and add hash to boot.img.
Tao Baob31b94e2016-09-29 21:59:06 -0700517 if info_dict.get("board_avb_enable", None) == "true":
David Zeuthen2ce63ed2016-09-15 13:43:54 -0400518 avbtool = os.getenv('AVBTOOL') or "avbtool"
Tao Baob31b94e2016-09-29 21:59:06 -0700519 part_size = info_dict.get("boot_size", None)
David Zeuthen2ce63ed2016-09-15 13:43:54 -0400520 cmd = [avbtool, "add_hash_footer", "--image", img.name,
521 "--partition_size", str(part_size), "--partition_name", "boot"]
522 AppendAVBSigningArgs(cmd)
Tao Baob31b94e2016-09-29 21:59:06 -0700523 args = info_dict.get("board_avb_boot_add_hash_footer_args", None)
David Zeuthen2ce63ed2016-09-15 13:43:54 -0400524 if args and args.strip():
525 cmd.extend(shlex.split(args))
526 p = Run(cmd, stdout=subprocess.PIPE)
527 p.communicate()
528 assert p.returncode == 0, "avbtool add_hash_footer of %s failed" % (
529 os.path.basename(OPTIONS.input_tmp))
David Zeuthend995f4b2016-01-29 16:59:17 -0500530
531 img.seek(os.SEEK_SET, 0)
532 data = img.read()
533
534 if has_ramdisk:
535 ramdisk_img.close()
536 img.close()
537
538 return data
539
540
Doug Zongkerd5131602012-08-02 14:46:42 -0700541def GetBootableImage(name, prebuilt_name, unpack_dir, tree_subdir,
Tao Bao2a4336b2016-12-01 00:08:05 -0800542 info_dict=None):
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700543 """Return a File object with the desired bootable image.
544
545 Look for it in 'unpack_dir'/BOOTABLE_IMAGES under the name 'prebuilt_name',
546 otherwise look for it under 'unpack_dir'/IMAGES, otherwise construct it from
547 the source files in 'unpack_dir'/'tree_subdir'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700548
Doug Zongker55d93282011-01-25 17:03:34 -0800549 prebuilt_path = os.path.join(unpack_dir, "BOOTABLE_IMAGES", prebuilt_name)
550 if os.path.exists(prebuilt_path):
Doug Zongker6f1d0312014-08-22 08:07:12 -0700551 print "using prebuilt %s from BOOTABLE_IMAGES..." % (prebuilt_name,)
Doug Zongker55d93282011-01-25 17:03:34 -0800552 return File.FromLocalFile(name, prebuilt_path)
Doug Zongker6f1d0312014-08-22 08:07:12 -0700553
554 prebuilt_path = os.path.join(unpack_dir, "IMAGES", prebuilt_name)
555 if os.path.exists(prebuilt_path):
556 print "using prebuilt %s from IMAGES..." % (prebuilt_name,)
557 return File.FromLocalFile(name, prebuilt_path)
558
559 print "building image from target_files %s..." % (tree_subdir,)
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700560
561 if info_dict is None:
562 info_dict = OPTIONS.info_dict
563
564 # With system_root_image == "true", we don't pack ramdisk into the boot image.
Daniel Rosenberg40ef35b2015-11-10 19:21:34 -0800565 # Unless "recovery_as_boot" is specified, in which case we carry the ramdisk
566 # for recovery.
567 has_ramdisk = (info_dict.get("system_root_image") != "true" or
568 prebuilt_name != "boot.img" or
569 info_dict.get("recovery_as_boot") == "true")
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700570
Doug Zongker6f1d0312014-08-22 08:07:12 -0700571 fs_config = "META/" + tree_subdir.lower() + "_filesystem_config.txt"
David Zeuthen2ce63ed2016-09-15 13:43:54 -0400572 data = _BuildBootableImage(os.path.join(unpack_dir, tree_subdir),
573 os.path.join(unpack_dir, fs_config),
574 info_dict, has_ramdisk)
Doug Zongker6f1d0312014-08-22 08:07:12 -0700575 if data:
576 return File(name, data)
577 return None
Doug Zongker55d93282011-01-25 17:03:34 -0800578
Doug Zongkereef39442009-04-02 12:14:19 -0700579
Doug Zongker75f17362009-12-08 13:46:44 -0800580def UnzipTemp(filename, pattern=None):
Doug Zongker55d93282011-01-25 17:03:34 -0800581 """Unzip the given archive into a temporary directory and return the name.
582
583 If filename is of the form "foo.zip+bar.zip", unzip foo.zip into a
584 temp dir, then unzip bar.zip into that_dir/BOOTABLE_IMAGES.
585
586 Returns (tempdir, zipobj) where zipobj is a zipfile.ZipFile (of the
587 main file), open for reading.
588 """
Doug Zongkereef39442009-04-02 12:14:19 -0700589
590 tmp = tempfile.mkdtemp(prefix="targetfiles-")
591 OPTIONS.tempfiles.append(tmp)
Doug Zongker55d93282011-01-25 17:03:34 -0800592
593 def unzip_to_dir(filename, dirname):
594 cmd = ["unzip", "-o", "-q", filename, "-d", dirname]
595 if pattern is not None:
596 cmd.append(pattern)
597 p = Run(cmd, stdout=subprocess.PIPE)
598 p.communicate()
599 if p.returncode != 0:
600 raise ExternalError("failed to unzip input target-files \"%s\"" %
601 (filename,))
602
603 m = re.match(r"^(.*[.]zip)\+(.*[.]zip)$", filename, re.IGNORECASE)
604 if m:
605 unzip_to_dir(m.group(1), tmp)
606 unzip_to_dir(m.group(2), os.path.join(tmp, "BOOTABLE_IMAGES"))
607 filename = m.group(1)
608 else:
609 unzip_to_dir(filename, tmp)
610
611 return tmp, zipfile.ZipFile(filename, "r")
Doug Zongkereef39442009-04-02 12:14:19 -0700612
613
614def GetKeyPasswords(keylist):
615 """Given a list of keys, prompt the user to enter passwords for
616 those which require them. Return a {key: password} dict. password
617 will be None if the key has no password."""
618
Doug Zongker8ce7c252009-05-22 13:34:54 -0700619 no_passwords = []
620 need_passwords = []
T.R. Fullhart37e10522013-03-18 10:31:26 -0700621 key_passwords = {}
Doug Zongkereef39442009-04-02 12:14:19 -0700622 devnull = open("/dev/null", "w+b")
623 for k in sorted(keylist):
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800624 # We don't need a password for things that aren't really keys.
625 if k in SPECIAL_CERT_STRINGS:
Doug Zongker8ce7c252009-05-22 13:34:54 -0700626 no_passwords.append(k)
Doug Zongker43874f82009-04-14 14:05:15 -0700627 continue
628
T.R. Fullhart37e10522013-03-18 10:31:26 -0700629 p = Run(["openssl", "pkcs8", "-in", k+OPTIONS.private_key_suffix,
Doug Zongker602a84e2009-06-18 08:35:12 -0700630 "-inform", "DER", "-nocrypt"],
631 stdin=devnull.fileno(),
632 stdout=devnull.fileno(),
633 stderr=subprocess.STDOUT)
Doug Zongkereef39442009-04-02 12:14:19 -0700634 p.communicate()
635 if p.returncode == 0:
T.R. Fullhart37e10522013-03-18 10:31:26 -0700636 # Definitely an unencrypted key.
Doug Zongker8ce7c252009-05-22 13:34:54 -0700637 no_passwords.append(k)
Doug Zongkereef39442009-04-02 12:14:19 -0700638 else:
T.R. Fullhart37e10522013-03-18 10:31:26 -0700639 p = Run(["openssl", "pkcs8", "-in", k+OPTIONS.private_key_suffix,
640 "-inform", "DER", "-passin", "pass:"],
641 stdin=devnull.fileno(),
642 stdout=devnull.fileno(),
643 stderr=subprocess.PIPE)
Dan Albert8b72aef2015-03-23 19:13:21 -0700644 _, stderr = p.communicate()
T.R. Fullhart37e10522013-03-18 10:31:26 -0700645 if p.returncode == 0:
646 # Encrypted key with empty string as password.
647 key_passwords[k] = ''
648 elif stderr.startswith('Error decrypting key'):
649 # Definitely encrypted key.
650 # It would have said "Error reading key" if it didn't parse correctly.
651 need_passwords.append(k)
652 else:
653 # Potentially, a type of key that openssl doesn't understand.
654 # We'll let the routines in signapk.jar handle it.
655 no_passwords.append(k)
Doug Zongkereef39442009-04-02 12:14:19 -0700656 devnull.close()
Doug Zongker8ce7c252009-05-22 13:34:54 -0700657
T.R. Fullhart37e10522013-03-18 10:31:26 -0700658 key_passwords.update(PasswordManager().GetPasswords(need_passwords))
Doug Zongker8ce7c252009-05-22 13:34:54 -0700659 key_passwords.update(dict.fromkeys(no_passwords, None))
Doug Zongkereef39442009-04-02 12:14:19 -0700660 return key_passwords
661
662
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800663def GetMinSdkVersion(apk_name):
664 """Get the minSdkVersion delared in the APK. This can be both a decimal number
665 (API Level) or a codename.
666 """
667
668 p = Run(["aapt", "dump", "badging", apk_name], stdout=subprocess.PIPE)
669 output, err = p.communicate()
670 if err:
671 raise ExternalError("Failed to obtain minSdkVersion: aapt return code %s"
672 % (p.returncode,))
673
674 for line in output.split("\n"):
675 # Looking for lines such as sdkVersion:'23' or sdkVersion:'M'
676 m = re.match(r'sdkVersion:\'([^\']*)\'', line)
677 if m:
678 return m.group(1)
679 raise ExternalError("No minSdkVersion returned by aapt")
680
681
682def GetMinSdkVersionInt(apk_name, codename_to_api_level_map):
683 """Get the minSdkVersion declared in the APK as a number (API Level). If
684 minSdkVersion is set to a codename, it is translated to a number using the
685 provided map.
686 """
687
688 version = GetMinSdkVersion(apk_name)
689 try:
690 return int(version)
691 except ValueError:
692 # Not a decimal number. Codename?
693 if version in codename_to_api_level_map:
694 return codename_to_api_level_map[version]
695 else:
696 raise ExternalError("Unknown minSdkVersion: '%s'. Known codenames: %s"
697 % (version, codename_to_api_level_map))
698
699
700def SignFile(input_name, output_name, key, password, min_api_level=None,
701 codename_to_api_level_map=dict(),
702 whole_file=False):
Doug Zongkereef39442009-04-02 12:14:19 -0700703 """Sign the input_name zip/jar/apk, producing output_name. Use the
704 given key and password (the latter may be None if the key does not
705 have a password.
706
Doug Zongker951495f2009-08-14 12:44:19 -0700707 If whole_file is true, use the "-w" option to SignApk to embed a
708 signature that covers the whole file in the archive comment of the
709 zip file.
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800710
711 min_api_level is the API Level (int) of the oldest platform this file may end
712 up on. If not specified for an APK, the API Level is obtained by interpreting
713 the minSdkVersion attribute of the APK's AndroidManifest.xml.
714
715 codename_to_api_level_map is needed to translate the codename which may be
716 encountered as the APK's minSdkVersion.
Doug Zongkereef39442009-04-02 12:14:19 -0700717 """
Doug Zongker951495f2009-08-14 12:44:19 -0700718
Alex Klyubin9667b182015-12-10 13:38:50 -0800719 java_library_path = os.path.join(
720 OPTIONS.search_path, OPTIONS.signapk_shared_library_path)
721
Tao Baoe95540e2016-11-08 12:08:53 -0800722 cmd = ([OPTIONS.java_path] + OPTIONS.java_args +
723 ["-Djava.library.path=" + java_library_path,
724 "-jar", os.path.join(OPTIONS.search_path, OPTIONS.signapk_path)] +
725 OPTIONS.extra_signapk_args)
Doug Zongker951495f2009-08-14 12:44:19 -0700726 if whole_file:
727 cmd.append("-w")
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800728
729 min_sdk_version = min_api_level
730 if min_sdk_version is None:
731 if not whole_file:
732 min_sdk_version = GetMinSdkVersionInt(
733 input_name, codename_to_api_level_map)
734 if min_sdk_version is not None:
735 cmd.extend(["--min-sdk-version", str(min_sdk_version)])
736
T.R. Fullhart37e10522013-03-18 10:31:26 -0700737 cmd.extend([key + OPTIONS.public_key_suffix,
738 key + OPTIONS.private_key_suffix,
Alex Klyubineb756d72015-12-04 09:21:08 -0800739 input_name, output_name])
Doug Zongker951495f2009-08-14 12:44:19 -0700740
741 p = Run(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
Doug Zongkereef39442009-04-02 12:14:19 -0700742 if password is not None:
743 password += "\n"
744 p.communicate(password)
745 if p.returncode != 0:
746 raise ExternalError("signapk.jar failed: return code %s" % (p.returncode,))
747
Doug Zongkereef39442009-04-02 12:14:19 -0700748
Doug Zongker37974732010-09-16 17:44:38 -0700749def CheckSize(data, target, info_dict):
Doug Zongkereef39442009-04-02 12:14:19 -0700750 """Check the data string passed against the max size limit, if
751 any, for the given target. Raise exception if the data is too big.
752 Print a warning if the data is nearing the maximum size."""
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700753
Dan Albert8b72aef2015-03-23 19:13:21 -0700754 if target.endswith(".img"):
755 target = target[:-4]
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700756 mount_point = "/" + target
757
Ying Wangf8824af2014-06-03 14:07:27 -0700758 fs_type = None
759 limit = None
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700760 if info_dict["fstab"]:
Dan Albert8b72aef2015-03-23 19:13:21 -0700761 if mount_point == "/userdata":
762 mount_point = "/data"
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700763 p = info_dict["fstab"][mount_point]
764 fs_type = p.fs_type
Andrew Boie0f9aec82012-02-14 09:32:52 -0800765 device = p.device
766 if "/" in device:
767 device = device[device.rfind("/")+1:]
768 limit = info_dict.get(device + "_size", None)
Dan Albert8b72aef2015-03-23 19:13:21 -0700769 if not fs_type or not limit:
770 return
Doug Zongkereef39442009-04-02 12:14:19 -0700771
Andrew Boie0f9aec82012-02-14 09:32:52 -0800772 size = len(data)
773 pct = float(size) * 100.0 / limit
774 msg = "%s size (%d) is %.2f%% of limit (%d)" % (target, size, pct, limit)
775 if pct >= 99.0:
776 raise ExternalError(msg)
777 elif pct >= 95.0:
778 print
779 print " WARNING: ", msg
780 print
781 elif OPTIONS.verbose:
782 print " ", msg
Doug Zongkereef39442009-04-02 12:14:19 -0700783
784
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800785def ReadApkCerts(tf_zip):
786 """Given a target_files ZipFile, parse the META/apkcerts.txt file
787 and return a {package: cert} dict."""
788 certmap = {}
789 for line in tf_zip.read("META/apkcerts.txt").split("\n"):
790 line = line.strip()
Dan Albert8b72aef2015-03-23 19:13:21 -0700791 if not line:
792 continue
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800793 m = re.match(r'^name="(.*)"\s+certificate="(.*)"\s+'
794 r'private_key="(.*)"$', line)
795 if m:
796 name, cert, privkey = m.groups()
T.R. Fullhart37e10522013-03-18 10:31:26 -0700797 public_key_suffix_len = len(OPTIONS.public_key_suffix)
798 private_key_suffix_len = len(OPTIONS.private_key_suffix)
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800799 if cert in SPECIAL_CERT_STRINGS and not privkey:
800 certmap[name] = cert
T.R. Fullhart37e10522013-03-18 10:31:26 -0700801 elif (cert.endswith(OPTIONS.public_key_suffix) and
802 privkey.endswith(OPTIONS.private_key_suffix) and
803 cert[:-public_key_suffix_len] == privkey[:-private_key_suffix_len]):
804 certmap[name] = cert[:-public_key_suffix_len]
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800805 else:
806 raise ValueError("failed to parse line from apkcerts.txt:\n" + line)
807 return certmap
808
809
Doug Zongkereef39442009-04-02 12:14:19 -0700810COMMON_DOCSTRING = """
811 -p (--path) <dir>
Doug Zongker602a84e2009-06-18 08:35:12 -0700812 Prepend <dir>/bin to the list of places to search for binaries
813 run by this script, and expect to find jars in <dir>/framework.
Doug Zongkereef39442009-04-02 12:14:19 -0700814
Doug Zongker05d3dea2009-06-22 11:32:31 -0700815 -s (--device_specific) <file>
816 Path to the python module containing device-specific
817 releasetools code.
818
Doug Zongker8bec09e2009-11-30 15:37:14 -0800819 -x (--extra) <key=value>
820 Add a key/value pair to the 'extras' dict, which device-specific
821 extension code may look at.
822
Doug Zongkereef39442009-04-02 12:14:19 -0700823 -v (--verbose)
824 Show command lines being executed.
825
826 -h (--help)
827 Display this usage message and exit.
828"""
829
830def Usage(docstring):
831 print docstring.rstrip("\n")
832 print COMMON_DOCSTRING
833
834
835def ParseOptions(argv,
836 docstring,
837 extra_opts="", extra_long_opts=(),
838 extra_option_handler=None):
839 """Parse the options in argv and return any arguments that aren't
840 flags. docstring is the calling module's docstring, to be displayed
841 for errors and -h. extra_opts and extra_long_opts are for flags
842 defined by the caller, which are processed by passing them to
843 extra_option_handler."""
844
845 try:
846 opts, args = getopt.getopt(
Doug Zongker8bec09e2009-11-30 15:37:14 -0800847 argv, "hvp:s:x:" + extra_opts,
Alex Klyubin9667b182015-12-10 13:38:50 -0800848 ["help", "verbose", "path=", "signapk_path=",
849 "signapk_shared_library_path=", "extra_signapk_args=",
Baligh Uddinbdc2e312014-09-05 17:36:20 -0700850 "java_path=", "java_args=", "public_key_suffix=",
Baligh Uddin601ddea2015-06-09 15:48:14 -0700851 "private_key_suffix=", "boot_signer_path=", "boot_signer_args=",
852 "verity_signer_path=", "verity_signer_args=", "device_specific=",
Baligh Uddine2048682014-11-20 09:52:05 -0800853 "extra="] +
T.R. Fullhart37e10522013-03-18 10:31:26 -0700854 list(extra_long_opts))
Dan Albert8b72aef2015-03-23 19:13:21 -0700855 except getopt.GetoptError as err:
Doug Zongkereef39442009-04-02 12:14:19 -0700856 Usage(docstring)
857 print "**", str(err), "**"
858 sys.exit(2)
859
Doug Zongkereef39442009-04-02 12:14:19 -0700860 for o, a in opts:
861 if o in ("-h", "--help"):
862 Usage(docstring)
863 sys.exit()
864 elif o in ("-v", "--verbose"):
865 OPTIONS.verbose = True
866 elif o in ("-p", "--path"):
Doug Zongker602a84e2009-06-18 08:35:12 -0700867 OPTIONS.search_path = a
T.R. Fullhart37e10522013-03-18 10:31:26 -0700868 elif o in ("--signapk_path",):
869 OPTIONS.signapk_path = a
Alex Klyubin9667b182015-12-10 13:38:50 -0800870 elif o in ("--signapk_shared_library_path",):
871 OPTIONS.signapk_shared_library_path = a
T.R. Fullhart37e10522013-03-18 10:31:26 -0700872 elif o in ("--extra_signapk_args",):
873 OPTIONS.extra_signapk_args = shlex.split(a)
874 elif o in ("--java_path",):
875 OPTIONS.java_path = a
Baligh Uddin339ee492014-09-05 11:18:07 -0700876 elif o in ("--java_args",):
Tao Baoe95540e2016-11-08 12:08:53 -0800877 OPTIONS.java_args = shlex.split(a)
T.R. Fullhart37e10522013-03-18 10:31:26 -0700878 elif o in ("--public_key_suffix",):
879 OPTIONS.public_key_suffix = a
880 elif o in ("--private_key_suffix",):
881 OPTIONS.private_key_suffix = a
Baligh Uddine2048682014-11-20 09:52:05 -0800882 elif o in ("--boot_signer_path",):
883 OPTIONS.boot_signer_path = a
Baligh Uddin601ddea2015-06-09 15:48:14 -0700884 elif o in ("--boot_signer_args",):
885 OPTIONS.boot_signer_args = shlex.split(a)
886 elif o in ("--verity_signer_path",):
887 OPTIONS.verity_signer_path = a
888 elif o in ("--verity_signer_args",):
889 OPTIONS.verity_signer_args = shlex.split(a)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700890 elif o in ("-s", "--device_specific"):
891 OPTIONS.device_specific = a
Doug Zongker5ecba702009-12-03 16:36:20 -0800892 elif o in ("-x", "--extra"):
Doug Zongker8bec09e2009-11-30 15:37:14 -0800893 key, value = a.split("=", 1)
894 OPTIONS.extras[key] = value
Doug Zongkereef39442009-04-02 12:14:19 -0700895 else:
896 if extra_option_handler is None or not extra_option_handler(o, a):
897 assert False, "unknown option \"%s\"" % (o,)
898
Doug Zongker85448772014-09-09 14:59:20 -0700899 if OPTIONS.search_path:
900 os.environ["PATH"] = (os.path.join(OPTIONS.search_path, "bin") +
901 os.pathsep + os.environ["PATH"])
Doug Zongkereef39442009-04-02 12:14:19 -0700902
903 return args
904
905
Doug Zongkerfc44a512014-08-26 13:10:25 -0700906def MakeTempFile(prefix=None, suffix=None):
907 """Make a temp file and add it to the list of things to be deleted
908 when Cleanup() is called. Return the filename."""
909 fd, fn = tempfile.mkstemp(prefix=prefix, suffix=suffix)
910 os.close(fd)
911 OPTIONS.tempfiles.append(fn)
912 return fn
913
914
Doug Zongkereef39442009-04-02 12:14:19 -0700915def Cleanup():
916 for i in OPTIONS.tempfiles:
917 if os.path.isdir(i):
918 shutil.rmtree(i)
919 else:
920 os.remove(i)
Doug Zongker8ce7c252009-05-22 13:34:54 -0700921
922
923class PasswordManager(object):
924 def __init__(self):
925 self.editor = os.getenv("EDITOR", None)
926 self.pwfile = os.getenv("ANDROID_PW_FILE", None)
927
928 def GetPasswords(self, items):
929 """Get passwords corresponding to each string in 'items',
930 returning a dict. (The dict may have keys in addition to the
931 values in 'items'.)
932
933 Uses the passwords in $ANDROID_PW_FILE if available, letting the
934 user edit that file to add more needed passwords. If no editor is
935 available, or $ANDROID_PW_FILE isn't define, prompts the user
936 interactively in the ordinary way.
937 """
938
939 current = self.ReadFile()
940
941 first = True
942 while True:
943 missing = []
944 for i in items:
945 if i not in current or not current[i]:
946 missing.append(i)
947 # Are all the passwords already in the file?
Dan Albert8b72aef2015-03-23 19:13:21 -0700948 if not missing:
949 return current
Doug Zongker8ce7c252009-05-22 13:34:54 -0700950
951 for i in missing:
952 current[i] = ""
953
954 if not first:
955 print "key file %s still missing some passwords." % (self.pwfile,)
956 answer = raw_input("try to edit again? [y]> ").strip()
957 if answer and answer[0] not in 'yY':
958 raise RuntimeError("key passwords unavailable")
959 first = False
960
961 current = self.UpdateAndReadFile(current)
962
Dan Albert8b72aef2015-03-23 19:13:21 -0700963 def PromptResult(self, current): # pylint: disable=no-self-use
Doug Zongker8ce7c252009-05-22 13:34:54 -0700964 """Prompt the user to enter a value (password) for each key in
965 'current' whose value is fales. Returns a new dict with all the
966 values.
967 """
968 result = {}
969 for k, v in sorted(current.iteritems()):
970 if v:
971 result[k] = v
972 else:
973 while True:
Dan Albert8b72aef2015-03-23 19:13:21 -0700974 result[k] = getpass.getpass(
975 "Enter password for %s key> " % k).strip()
976 if result[k]:
977 break
Doug Zongker8ce7c252009-05-22 13:34:54 -0700978 return result
979
980 def UpdateAndReadFile(self, current):
981 if not self.editor or not self.pwfile:
982 return self.PromptResult(current)
983
984 f = open(self.pwfile, "w")
Dan Albert8b72aef2015-03-23 19:13:21 -0700985 os.chmod(self.pwfile, 0o600)
Doug Zongker8ce7c252009-05-22 13:34:54 -0700986 f.write("# Enter key passwords between the [[[ ]]] brackets.\n")
987 f.write("# (Additional spaces are harmless.)\n\n")
988
989 first_line = None
Dan Albert8b72aef2015-03-23 19:13:21 -0700990 sorted_list = sorted([(not v, k, v) for (k, v) in current.iteritems()])
991 for i, (_, k, v) in enumerate(sorted_list):
Doug Zongker8ce7c252009-05-22 13:34:54 -0700992 f.write("[[[ %s ]]] %s\n" % (v, k))
993 if not v and first_line is None:
994 # position cursor on first line with no password.
995 first_line = i + 4
996 f.close()
997
998 p = Run([self.editor, "+%d" % (first_line,), self.pwfile])
999 _, _ = p.communicate()
1000
1001 return self.ReadFile()
1002
1003 def ReadFile(self):
1004 result = {}
Dan Albert8b72aef2015-03-23 19:13:21 -07001005 if self.pwfile is None:
1006 return result
Doug Zongker8ce7c252009-05-22 13:34:54 -07001007 try:
1008 f = open(self.pwfile, "r")
1009 for line in f:
1010 line = line.strip()
Dan Albert8b72aef2015-03-23 19:13:21 -07001011 if not line or line[0] == '#':
1012 continue
Doug Zongker8ce7c252009-05-22 13:34:54 -07001013 m = re.match(r"^\[\[\[\s*(.*?)\s*\]\]\]\s*(\S+)$", line)
1014 if not m:
1015 print "failed to parse password file: ", line
1016 else:
1017 result[m.group(2)] = m.group(1)
1018 f.close()
Dan Albert8b72aef2015-03-23 19:13:21 -07001019 except IOError as e:
Doug Zongker8ce7c252009-05-22 13:34:54 -07001020 if e.errno != errno.ENOENT:
1021 print "error reading password file: ", str(e)
1022 return result
Doug Zongker048e7ca2009-06-15 14:31:53 -07001023
1024
Dan Albert8e0178d2015-01-27 15:53:15 -08001025def ZipWrite(zip_file, filename, arcname=None, perms=0o644,
1026 compress_type=None):
1027 import datetime
1028
1029 # http://b/18015246
1030 # Python 2.7's zipfile implementation wrongly thinks that zip64 is required
1031 # for files larger than 2GiB. We can work around this by adjusting their
1032 # limit. Note that `zipfile.writestr()` will not work for strings larger than
1033 # 2GiB. The Python interpreter sometimes rejects strings that large (though
1034 # it isn't clear to me exactly what circumstances cause this).
1035 # `zipfile.write()` must be used directly to work around this.
1036 #
1037 # This mess can be avoided if we port to python3.
1038 saved_zip64_limit = zipfile.ZIP64_LIMIT
1039 zipfile.ZIP64_LIMIT = (1 << 32) - 1
1040
1041 if compress_type is None:
1042 compress_type = zip_file.compression
1043 if arcname is None:
1044 arcname = filename
1045
1046 saved_stat = os.stat(filename)
1047
1048 try:
1049 # `zipfile.write()` doesn't allow us to pass ZipInfo, so just modify the
1050 # file to be zipped and reset it when we're done.
1051 os.chmod(filename, perms)
1052
1053 # Use a fixed timestamp so the output is repeatable.
1054 epoch = datetime.datetime.fromtimestamp(0)
1055 timestamp = (datetime.datetime(2009, 1, 1) - epoch).total_seconds()
1056 os.utime(filename, (timestamp, timestamp))
1057
1058 zip_file.write(filename, arcname=arcname, compress_type=compress_type)
1059 finally:
1060 os.chmod(filename, saved_stat.st_mode)
1061 os.utime(filename, (saved_stat.st_atime, saved_stat.st_mtime))
1062 zipfile.ZIP64_LIMIT = saved_zip64_limit
1063
1064
Tao Bao58c1b962015-05-20 09:32:18 -07001065def ZipWriteStr(zip_file, zinfo_or_arcname, data, perms=None,
Tao Baof3282b42015-04-01 11:21:55 -07001066 compress_type=None):
1067 """Wrap zipfile.writestr() function to work around the zip64 limit.
1068
1069 Even with the ZIP64_LIMIT workaround, it won't allow writing a string
1070 longer than 2GiB. It gives 'OverflowError: size does not fit in an int'
1071 when calling crc32(bytes).
1072
1073 But it still works fine to write a shorter string into a large zip file.
1074 We should use ZipWrite() whenever possible, and only use ZipWriteStr()
1075 when we know the string won't be too long.
1076 """
1077
1078 saved_zip64_limit = zipfile.ZIP64_LIMIT
1079 zipfile.ZIP64_LIMIT = (1 << 32) - 1
1080
1081 if not isinstance(zinfo_or_arcname, zipfile.ZipInfo):
1082 zinfo = zipfile.ZipInfo(filename=zinfo_or_arcname)
Dan Albert8b72aef2015-03-23 19:13:21 -07001083 zinfo.compress_type = zip_file.compression
Tao Bao58c1b962015-05-20 09:32:18 -07001084 if perms is None:
Tao Bao2a410582015-07-10 17:18:23 -07001085 perms = 0o100644
Geremy Condra36bd3652014-02-06 19:45:10 -08001086 else:
Tao Baof3282b42015-04-01 11:21:55 -07001087 zinfo = zinfo_or_arcname
1088
1089 # If compress_type is given, it overrides the value in zinfo.
1090 if compress_type is not None:
1091 zinfo.compress_type = compress_type
1092
Tao Bao58c1b962015-05-20 09:32:18 -07001093 # If perms is given, it has a priority.
1094 if perms is not None:
Tao Bao2a410582015-07-10 17:18:23 -07001095 # If perms doesn't set the file type, mark it as a regular file.
1096 if perms & 0o770000 == 0:
1097 perms |= 0o100000
Tao Bao58c1b962015-05-20 09:32:18 -07001098 zinfo.external_attr = perms << 16
1099
Tao Baof3282b42015-04-01 11:21:55 -07001100 # Use a fixed timestamp so the output is repeatable.
Tao Baof3282b42015-04-01 11:21:55 -07001101 zinfo.date_time = (2009, 1, 1, 0, 0, 0)
1102
Dan Albert8b72aef2015-03-23 19:13:21 -07001103 zip_file.writestr(zinfo, data)
Tao Baof3282b42015-04-01 11:21:55 -07001104 zipfile.ZIP64_LIMIT = saved_zip64_limit
1105
1106
1107def ZipClose(zip_file):
1108 # http://b/18015246
1109 # zipfile also refers to ZIP64_LIMIT during close() when it writes out the
1110 # central directory.
1111 saved_zip64_limit = zipfile.ZIP64_LIMIT
1112 zipfile.ZIP64_LIMIT = (1 << 32) - 1
1113
1114 zip_file.close()
1115
1116 zipfile.ZIP64_LIMIT = saved_zip64_limit
Doug Zongker05d3dea2009-06-22 11:32:31 -07001117
1118
1119class DeviceSpecificParams(object):
1120 module = None
1121 def __init__(self, **kwargs):
1122 """Keyword arguments to the constructor become attributes of this
1123 object, which is passed to all functions in the device-specific
1124 module."""
1125 for k, v in kwargs.iteritems():
1126 setattr(self, k, v)
Doug Zongker8bec09e2009-11-30 15:37:14 -08001127 self.extras = OPTIONS.extras
Doug Zongker05d3dea2009-06-22 11:32:31 -07001128
1129 if self.module is None:
1130 path = OPTIONS.device_specific
Dan Albert8b72aef2015-03-23 19:13:21 -07001131 if not path:
1132 return
Doug Zongker8e2f2b92009-06-24 14:34:57 -07001133 try:
1134 if os.path.isdir(path):
1135 info = imp.find_module("releasetools", [path])
1136 else:
1137 d, f = os.path.split(path)
1138 b, x = os.path.splitext(f)
1139 if x == ".py":
1140 f = b
1141 info = imp.find_module(f, [d])
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001142 print "loaded device-specific extensions from", path
Doug Zongker8e2f2b92009-06-24 14:34:57 -07001143 self.module = imp.load_module("device_specific", *info)
1144 except ImportError:
1145 print "unable to load device-specific module; assuming none"
Doug Zongker05d3dea2009-06-22 11:32:31 -07001146
1147 def _DoCall(self, function_name, *args, **kwargs):
1148 """Call the named function in the device-specific module, passing
1149 the given args and kwargs. The first argument to the call will be
1150 the DeviceSpecific object itself. If there is no module, or the
1151 module does not define the function, return the value of the
1152 'default' kwarg (which itself defaults to None)."""
1153 if self.module is None or not hasattr(self.module, function_name):
1154 return kwargs.get("default", None)
1155 return getattr(self.module, function_name)(*((self,) + args), **kwargs)
1156
1157 def FullOTA_Assertions(self):
1158 """Called after emitting the block of assertions at the top of a
1159 full OTA package. Implementations can add whatever additional
1160 assertions they like."""
1161 return self._DoCall("FullOTA_Assertions")
1162
Doug Zongkere5ff5902012-01-17 10:55:37 -08001163 def FullOTA_InstallBegin(self):
1164 """Called at the start of full OTA installation."""
1165 return self._DoCall("FullOTA_InstallBegin")
1166
Doug Zongker05d3dea2009-06-22 11:32:31 -07001167 def FullOTA_InstallEnd(self):
1168 """Called at the end of full OTA installation; typically this is
1169 used to install the image for the device's baseband processor."""
1170 return self._DoCall("FullOTA_InstallEnd")
1171
1172 def IncrementalOTA_Assertions(self):
1173 """Called after emitting the block of assertions at the top of an
1174 incremental OTA package. Implementations can add whatever
1175 additional assertions they like."""
1176 return self._DoCall("IncrementalOTA_Assertions")
1177
Doug Zongkere5ff5902012-01-17 10:55:37 -08001178 def IncrementalOTA_VerifyBegin(self):
1179 """Called at the start of the verification phase of incremental
1180 OTA installation; additional checks can be placed here to abort
1181 the script before any changes are made."""
1182 return self._DoCall("IncrementalOTA_VerifyBegin")
1183
Doug Zongker05d3dea2009-06-22 11:32:31 -07001184 def IncrementalOTA_VerifyEnd(self):
1185 """Called at the end of the verification phase of incremental OTA
1186 installation; additional checks can be placed here to abort the
1187 script before any changes are made."""
1188 return self._DoCall("IncrementalOTA_VerifyEnd")
1189
Doug Zongkere5ff5902012-01-17 10:55:37 -08001190 def IncrementalOTA_InstallBegin(self):
1191 """Called at the start of incremental OTA installation (after
1192 verification is complete)."""
1193 return self._DoCall("IncrementalOTA_InstallBegin")
1194
Doug Zongker05d3dea2009-06-22 11:32:31 -07001195 def IncrementalOTA_InstallEnd(self):
1196 """Called at the end of incremental OTA installation; typically
1197 this is used to install the image for the device's baseband
1198 processor."""
1199 return self._DoCall("IncrementalOTA_InstallEnd")
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001200
Tao Bao9bc6bb22015-11-09 16:58:28 -08001201 def VerifyOTA_Assertions(self):
1202 return self._DoCall("VerifyOTA_Assertions")
1203
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001204class File(object):
YOUNG HO CHAccc5c402016-10-13 13:40:46 +09001205 def __init__(self, name, data, compress_size = None):
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001206 self.name = name
1207 self.data = data
1208 self.size = len(data)
YOUNG HO CHAccc5c402016-10-13 13:40:46 +09001209 self.compress_size = compress_size or self.size
Doug Zongker55d93282011-01-25 17:03:34 -08001210 self.sha1 = sha1(data).hexdigest()
1211
1212 @classmethod
1213 def FromLocalFile(cls, name, diskname):
1214 f = open(diskname, "rb")
1215 data = f.read()
1216 f.close()
1217 return File(name, data)
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001218
1219 def WriteToTemp(self):
1220 t = tempfile.NamedTemporaryFile()
1221 t.write(self.data)
1222 t.flush()
1223 return t
1224
Geremy Condra36bd3652014-02-06 19:45:10 -08001225 def AddToZip(self, z, compression=None):
Tao Baof3282b42015-04-01 11:21:55 -07001226 ZipWriteStr(z, self.name, self.data, compress_type=compression)
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001227
1228DIFF_PROGRAM_BY_EXT = {
1229 ".gz" : "imgdiff",
1230 ".zip" : ["imgdiff", "-z"],
1231 ".jar" : ["imgdiff", "-z"],
1232 ".apk" : ["imgdiff", "-z"],
1233 ".img" : "imgdiff",
1234 }
1235
1236class Difference(object):
Doug Zongker24cd2802012-08-14 16:36:15 -07001237 def __init__(self, tf, sf, diff_program=None):
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001238 self.tf = tf
1239 self.sf = sf
1240 self.patch = None
Doug Zongker24cd2802012-08-14 16:36:15 -07001241 self.diff_program = diff_program
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001242
1243 def ComputePatch(self):
1244 """Compute the patch (as a string of data) needed to turn sf into
1245 tf. Returns the same tuple as GetPatch()."""
1246
1247 tf = self.tf
1248 sf = self.sf
1249
Doug Zongker24cd2802012-08-14 16:36:15 -07001250 if self.diff_program:
1251 diff_program = self.diff_program
1252 else:
1253 ext = os.path.splitext(tf.name)[1]
1254 diff_program = DIFF_PROGRAM_BY_EXT.get(ext, "bsdiff")
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001255
1256 ttemp = tf.WriteToTemp()
1257 stemp = sf.WriteToTemp()
1258
1259 ext = os.path.splitext(tf.name)[1]
1260
1261 try:
1262 ptemp = tempfile.NamedTemporaryFile()
1263 if isinstance(diff_program, list):
1264 cmd = copy.copy(diff_program)
1265 else:
1266 cmd = [diff_program]
1267 cmd.append(stemp.name)
1268 cmd.append(ttemp.name)
1269 cmd.append(ptemp.name)
1270 p = Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Doug Zongkerf8340082014-08-05 10:39:37 -07001271 err = []
1272 def run():
1273 _, e = p.communicate()
Dan Albert8b72aef2015-03-23 19:13:21 -07001274 if e:
1275 err.append(e)
Doug Zongkerf8340082014-08-05 10:39:37 -07001276 th = threading.Thread(target=run)
1277 th.start()
1278 th.join(timeout=300) # 5 mins
1279 if th.is_alive():
1280 print "WARNING: diff command timed out"
1281 p.terminate()
1282 th.join(5)
1283 if th.is_alive():
1284 p.kill()
1285 th.join()
1286
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001287 if err or p.returncode != 0:
Doug Zongkerf8340082014-08-05 10:39:37 -07001288 print "WARNING: failure running %s:\n%s\n" % (
1289 diff_program, "".join(err))
1290 self.patch = None
1291 return None, None, None
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001292 diff = ptemp.read()
1293 finally:
1294 ptemp.close()
1295 stemp.close()
1296 ttemp.close()
1297
1298 self.patch = diff
1299 return self.tf, self.sf, self.patch
1300
1301
1302 def GetPatch(self):
1303 """Return a tuple (target_file, source_file, patch_data).
1304 patch_data may be None if ComputePatch hasn't been called, or if
1305 computing the patch failed."""
1306 return self.tf, self.sf, self.patch
1307
1308
1309def ComputeDifferences(diffs):
1310 """Call ComputePatch on all the Difference objects in 'diffs'."""
1311 print len(diffs), "diffs to compute"
1312
1313 # Do the largest files first, to try and reduce the long-pole effect.
1314 by_size = [(i.tf.size, i) for i in diffs]
1315 by_size.sort(reverse=True)
1316 by_size = [i[1] for i in by_size]
1317
1318 lock = threading.Lock()
1319 diff_iter = iter(by_size) # accessed under lock
1320
1321 def worker():
1322 try:
1323 lock.acquire()
1324 for d in diff_iter:
1325 lock.release()
1326 start = time.time()
1327 d.ComputePatch()
1328 dur = time.time() - start
1329 lock.acquire()
1330
1331 tf, sf, patch = d.GetPatch()
1332 if sf.name == tf.name:
1333 name = tf.name
1334 else:
1335 name = "%s (%s)" % (tf.name, sf.name)
1336 if patch is None:
1337 print "patching failed! %s" % (name,)
1338 else:
1339 print "%8.2f sec %8d / %8d bytes (%6.2f%%) %s" % (
1340 dur, len(patch), tf.size, 100.0 * len(patch) / tf.size, name)
1341 lock.release()
Dan Albert8b72aef2015-03-23 19:13:21 -07001342 except Exception as e:
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001343 print e
1344 raise
1345
1346 # start worker threads; wait for them all to finish.
1347 threads = [threading.Thread(target=worker)
1348 for i in range(OPTIONS.worker_threads)]
1349 for th in threads:
1350 th.start()
1351 while threads:
1352 threads.pop().join()
Doug Zongker96a57e72010-09-26 14:57:41 -07001353
1354
Dan Albert8b72aef2015-03-23 19:13:21 -07001355class BlockDifference(object):
1356 def __init__(self, partition, tgt, src=None, check_first_block=False,
Tao Bao293fd132016-06-11 12:19:23 -07001357 version=None, disable_imgdiff=False):
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001358 self.tgt = tgt
1359 self.src = src
1360 self.partition = partition
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001361 self.check_first_block = check_first_block
Tao Bao293fd132016-06-11 12:19:23 -07001362 self.disable_imgdiff = disable_imgdiff
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001363
Tao Baodd2a5892015-03-12 12:32:37 -07001364 if version is None:
1365 version = 1
1366 if OPTIONS.info_dict:
1367 version = max(
1368 int(i) for i in
1369 OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(","))
1370 self.version = version
Doug Zongker62338182014-09-08 08:29:55 -07001371
1372 b = blockimgdiff.BlockImageDiff(tgt, src, threads=OPTIONS.worker_threads,
Tao Bao293fd132016-06-11 12:19:23 -07001373 version=self.version,
1374 disable_imgdiff=self.disable_imgdiff)
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001375 tmpdir = tempfile.mkdtemp()
1376 OPTIONS.tempfiles.append(tmpdir)
1377 self.path = os.path.join(tmpdir, partition)
1378 b.Compute(self.path)
Tao Baod8d14be2016-02-04 14:26:02 -08001379 self._required_cache = b.max_stashed_size
Tao Baod522bdc2016-04-12 15:53:16 -07001380 self.touched_src_ranges = b.touched_src_ranges
1381 self.touched_src_sha1 = b.touched_src_sha1
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001382
Tao Baoaac4ad52015-10-16 15:26:34 -07001383 if src is None:
1384 _, self.device = GetTypeAndDevice("/" + partition, OPTIONS.info_dict)
1385 else:
1386 _, self.device = GetTypeAndDevice("/" + partition,
1387 OPTIONS.source_info_dict)
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001388
Tao Baod8d14be2016-02-04 14:26:02 -08001389 @property
1390 def required_cache(self):
1391 return self._required_cache
1392
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001393 def WriteScript(self, script, output_zip, progress=None):
1394 if not self.src:
1395 # write the output unconditionally
Jesse Zhao75bcea02015-01-06 10:59:53 -08001396 script.Print("Patching %s image unconditionally..." % (self.partition,))
1397 else:
1398 script.Print("Patching %s image after verification." % (self.partition,))
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001399
Dan Albert8b72aef2015-03-23 19:13:21 -07001400 if progress:
1401 script.ShowProgress(progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001402 self._WriteUpdate(script, output_zip)
Tianjie Xub2deb222016-03-25 15:01:33 -07001403 if OPTIONS.verify:
1404 self._WritePostInstallVerifyScript(script)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001405
Tao Bao9bc6bb22015-11-09 16:58:28 -08001406 def WriteStrictVerifyScript(self, script):
1407 """Verify all the blocks in the care_map, including clobbered blocks.
1408
1409 This differs from the WriteVerifyScript() function: a) it prints different
1410 error messages; b) it doesn't allow half-way updated images to pass the
1411 verification."""
1412
1413 partition = self.partition
1414 script.Print("Verifying %s..." % (partition,))
1415 ranges = self.tgt.care_map
1416 ranges_str = ranges.to_string_raw()
1417 script.AppendExtra('range_sha1("%s", "%s") == "%s" && '
1418 'ui_print(" Verified.") || '
1419 'ui_print("\\"%s\\" has unexpected contents.");' % (
1420 self.device, ranges_str,
1421 self.tgt.TotalSha1(include_clobbered_blocks=True),
1422 self.device))
1423 script.AppendExtra("")
1424
Tao Baod522bdc2016-04-12 15:53:16 -07001425 def WriteVerifyScript(self, script, touched_blocks_only=False):
Sami Tolvanendd67a292014-12-09 16:40:34 +00001426 partition = self.partition
Tao Baof9efe282016-04-14 15:58:05 -07001427
1428 # full OTA
Jesse Zhao75bcea02015-01-06 10:59:53 -08001429 if not self.src:
Sami Tolvanendd67a292014-12-09 16:40:34 +00001430 script.Print("Image %s will be patched unconditionally." % (partition,))
Tao Baof9efe282016-04-14 15:58:05 -07001431
1432 # incremental OTA
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001433 else:
Tao Baod522bdc2016-04-12 15:53:16 -07001434 if touched_blocks_only and self.version >= 3:
1435 ranges = self.touched_src_ranges
1436 expected_sha1 = self.touched_src_sha1
1437 else:
1438 ranges = self.src.care_map.subtract(self.src.clobbered_blocks)
1439 expected_sha1 = self.src.TotalSha1()
Tao Baof9efe282016-04-14 15:58:05 -07001440
1441 # No blocks to be checked, skipping.
1442 if not ranges:
1443 return
1444
Tao Bao5ece99d2015-05-12 11:42:31 -07001445 ranges_str = ranges.to_string_raw()
Sami Tolvanenf0a7c762015-06-25 11:48:29 +01001446 if self.version >= 4:
1447 script.AppendExtra(('if (range_sha1("%s", "%s") == "%s" || '
1448 'block_image_verify("%s", '
1449 'package_extract_file("%s.transfer.list"), '
Tianjie Xufc3422a2015-12-15 11:53:59 -08001450 '"%s.new.dat", "%s.patch.dat")) then') % (
Tao Baod522bdc2016-04-12 15:53:16 -07001451 self.device, ranges_str, expected_sha1,
Sami Tolvanenf0a7c762015-06-25 11:48:29 +01001452 self.device, partition, partition, partition))
1453 elif self.version == 3:
Sami Tolvanene09d0962015-04-24 11:54:01 +01001454 script.AppendExtra(('if (range_sha1("%s", "%s") == "%s" || '
1455 'block_image_verify("%s", '
Michael Runge910b0052015-02-11 19:28:08 -08001456 'package_extract_file("%s.transfer.list"), '
Sami Tolvanene09d0962015-04-24 11:54:01 +01001457 '"%s.new.dat", "%s.patch.dat")) then') % (
Tao Baod522bdc2016-04-12 15:53:16 -07001458 self.device, ranges_str, expected_sha1,
Sami Tolvanene09d0962015-04-24 11:54:01 +01001459 self.device, partition, partition, partition))
Michael Runge910b0052015-02-11 19:28:08 -08001460 else:
Dan Albert8b72aef2015-03-23 19:13:21 -07001461 script.AppendExtra('if range_sha1("%s", "%s") == "%s" then' % (
Tao Bao5ece99d2015-05-12 11:42:31 -07001462 self.device, ranges_str, self.src.TotalSha1()))
Tao Baodd2a5892015-03-12 12:32:37 -07001463 script.Print('Verified %s image...' % (partition,))
Dan Albert8b72aef2015-03-23 19:13:21 -07001464 script.AppendExtra('else')
Sami Tolvanendd67a292014-12-09 16:40:34 +00001465
Tianjie Xufc3422a2015-12-15 11:53:59 -08001466 if self.version >= 4:
1467
1468 # Bug: 21124327
1469 # When generating incrementals for the system and vendor partitions in
1470 # version 4 or newer, explicitly check the first block (which contains
1471 # the superblock) of the partition to see if it's what we expect. If
1472 # this check fails, give an explicit log message about the partition
1473 # having been remounted R/W (the most likely explanation).
1474 if self.check_first_block:
1475 script.AppendExtra('check_first_block("%s");' % (self.device,))
1476
1477 # If version >= 4, try block recovery before abort update
Tianjie Xu209db462016-05-24 17:34:52 -07001478 if partition == "system":
1479 code = ErrorCode.SYSTEM_RECOVER_FAILURE
1480 else:
1481 code = ErrorCode.VENDOR_RECOVER_FAILURE
Tianjie Xufc3422a2015-12-15 11:53:59 -08001482 script.AppendExtra((
1483 'ifelse (block_image_recover("{device}", "{ranges}") && '
1484 'block_image_verify("{device}", '
1485 'package_extract_file("{partition}.transfer.list"), '
1486 '"{partition}.new.dat", "{partition}.patch.dat"), '
1487 'ui_print("{partition} recovered successfully."), '
Tianjie Xu209db462016-05-24 17:34:52 -07001488 'abort("E{code}: {partition} partition fails to recover"));\n'
Tianjie Xufc3422a2015-12-15 11:53:59 -08001489 'endif;').format(device=self.device, ranges=ranges_str,
Tianjie Xu209db462016-05-24 17:34:52 -07001490 partition=partition, code=code))
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001491
Tao Baodd2a5892015-03-12 12:32:37 -07001492 # Abort the OTA update. Note that the incremental OTA cannot be applied
1493 # even if it may match the checksum of the target partition.
1494 # a) If version < 3, operations like move and erase will make changes
1495 # unconditionally and damage the partition.
1496 # b) If version >= 3, it won't even reach here.
Tianjie Xufc3422a2015-12-15 11:53:59 -08001497 else:
Tianjie Xu209db462016-05-24 17:34:52 -07001498 if partition == "system":
1499 code = ErrorCode.SYSTEM_VERIFICATION_FAILURE
1500 else:
1501 code = ErrorCode.VENDOR_VERIFICATION_FAILURE
1502 script.AppendExtra((
1503 'abort("E%d: %s partition has unexpected contents");\n'
1504 'endif;') % (code, partition))
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001505
Tao Bao5fcaaef2015-06-01 13:40:49 -07001506 def _WritePostInstallVerifyScript(self, script):
1507 partition = self.partition
1508 script.Print('Verifying the updated %s image...' % (partition,))
1509 # Unlike pre-install verification, clobbered_blocks should not be ignored.
1510 ranges = self.tgt.care_map
1511 ranges_str = ranges.to_string_raw()
1512 script.AppendExtra('if range_sha1("%s", "%s") == "%s" then' % (
1513 self.device, ranges_str,
1514 self.tgt.TotalSha1(include_clobbered_blocks=True)))
Tao Baoe9b61912015-07-09 17:37:49 -07001515
1516 # Bug: 20881595
1517 # Verify that extended blocks are really zeroed out.
1518 if self.tgt.extended:
1519 ranges_str = self.tgt.extended.to_string_raw()
1520 script.AppendExtra('if range_sha1("%s", "%s") == "%s" then' % (
1521 self.device, ranges_str,
1522 self._HashZeroBlocks(self.tgt.extended.size())))
1523 script.Print('Verified the updated %s image.' % (partition,))
Tianjie Xu209db462016-05-24 17:34:52 -07001524 if partition == "system":
1525 code = ErrorCode.SYSTEM_NONZERO_CONTENTS
1526 else:
1527 code = ErrorCode.VENDOR_NONZERO_CONTENTS
Tao Baoe9b61912015-07-09 17:37:49 -07001528 script.AppendExtra(
1529 'else\n'
Tianjie Xu209db462016-05-24 17:34:52 -07001530 ' abort("E%d: %s partition has unexpected non-zero contents after '
1531 'OTA update");\n'
1532 'endif;' % (code, partition))
Tao Baoe9b61912015-07-09 17:37:49 -07001533 else:
1534 script.Print('Verified the updated %s image.' % (partition,))
1535
Tianjie Xu209db462016-05-24 17:34:52 -07001536 if partition == "system":
1537 code = ErrorCode.SYSTEM_UNEXPECTED_CONTENTS
1538 else:
1539 code = ErrorCode.VENDOR_UNEXPECTED_CONTENTS
1540
Tao Bao5fcaaef2015-06-01 13:40:49 -07001541 script.AppendExtra(
1542 'else\n'
Tianjie Xu209db462016-05-24 17:34:52 -07001543 ' abort("E%d: %s partition has unexpected contents after OTA '
1544 'update");\n'
1545 'endif;' % (code, partition))
Tao Bao5fcaaef2015-06-01 13:40:49 -07001546
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001547 def _WriteUpdate(self, script, output_zip):
Dan Albert8e0178d2015-01-27 15:53:15 -08001548 ZipWrite(output_zip,
1549 '{}.transfer.list'.format(self.path),
1550 '{}.transfer.list'.format(self.partition))
1551 ZipWrite(output_zip,
1552 '{}.new.dat'.format(self.path),
1553 '{}.new.dat'.format(self.partition))
1554 ZipWrite(output_zip,
1555 '{}.patch.dat'.format(self.path),
1556 '{}.patch.dat'.format(self.partition),
1557 compress_type=zipfile.ZIP_STORED)
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001558
Tianjie Xu209db462016-05-24 17:34:52 -07001559 if self.partition == "system":
1560 code = ErrorCode.SYSTEM_UPDATE_FAILURE
1561 else:
1562 code = ErrorCode.VENDOR_UPDATE_FAILURE
1563
Dan Albert8e0178d2015-01-27 15:53:15 -08001564 call = ('block_image_update("{device}", '
1565 'package_extract_file("{partition}.transfer.list"), '
Tianjie Xub2deb222016-03-25 15:01:33 -07001566 '"{partition}.new.dat", "{partition}.patch.dat") ||\n'
Tianjie Xu209db462016-05-24 17:34:52 -07001567 ' abort("E{code}: Failed to update {partition} image.");'.format(
1568 device=self.device, partition=self.partition, code=code))
Dan Albert8b72aef2015-03-23 19:13:21 -07001569 script.AppendExtra(script.WordWrap(call))
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001570
Dan Albert8b72aef2015-03-23 19:13:21 -07001571 def _HashBlocks(self, source, ranges): # pylint: disable=no-self-use
Sami Tolvanendd67a292014-12-09 16:40:34 +00001572 data = source.ReadRangeSet(ranges)
1573 ctx = sha1()
1574
1575 for p in data:
1576 ctx.update(p)
1577
1578 return ctx.hexdigest()
1579
Tao Baoe9b61912015-07-09 17:37:49 -07001580 def _HashZeroBlocks(self, num_blocks): # pylint: disable=no-self-use
1581 """Return the hash value for all zero blocks."""
1582 zero_block = '\x00' * 4096
1583 ctx = sha1()
1584 for _ in range(num_blocks):
1585 ctx.update(zero_block)
1586
1587 return ctx.hexdigest()
1588
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001589
1590DataImage = blockimgdiff.DataImage
1591
Doug Zongker96a57e72010-09-26 14:57:41 -07001592# map recovery.fstab's fs_types to mount/format "partition types"
Dan Albert8b72aef2015-03-23 19:13:21 -07001593PARTITION_TYPES = {
Dan Albert8b72aef2015-03-23 19:13:21 -07001594 "ext4": "EMMC",
1595 "emmc": "EMMC",
Mohamad Ayyash95e74c12015-05-01 15:39:36 -07001596 "f2fs": "EMMC",
1597 "squashfs": "EMMC"
Dan Albert8b72aef2015-03-23 19:13:21 -07001598}
Doug Zongker96a57e72010-09-26 14:57:41 -07001599
1600def GetTypeAndDevice(mount_point, info):
1601 fstab = info["fstab"]
1602 if fstab:
Dan Albert8b72aef2015-03-23 19:13:21 -07001603 return (PARTITION_TYPES[fstab[mount_point].fs_type],
1604 fstab[mount_point].device)
Doug Zongker96a57e72010-09-26 14:57:41 -07001605 else:
Dan Albert8b72aef2015-03-23 19:13:21 -07001606 raise KeyError
Baligh Uddinbeb6afd2013-11-13 00:22:34 +00001607
1608
1609def ParseCertificate(data):
1610 """Parse a PEM-format certificate."""
1611 cert = []
1612 save = False
1613 for line in data.split("\n"):
1614 if "--END CERTIFICATE--" in line:
1615 break
1616 if save:
1617 cert.append(line)
1618 if "--BEGIN CERTIFICATE--" in line:
1619 save = True
1620 cert = "".join(cert).decode('base64')
1621 return cert
Doug Zongkerc9253822014-02-04 12:17:58 -08001622
Doug Zongker412c02f2014-02-13 10:58:24 -08001623def MakeRecoveryPatch(input_dir, output_sink, recovery_img, boot_img,
1624 info_dict=None):
Doug Zongkerc9253822014-02-04 12:17:58 -08001625 """Generate a binary patch that creates the recovery image starting
1626 with the boot image. (Most of the space in these images is just the
1627 kernel, which is identical for the two, so the resulting patch
1628 should be efficient.) Add it to the output zip, along with a shell
1629 script that is run from init.rc on first boot to actually do the
1630 patching and install the new recovery image.
1631
1632 recovery_img and boot_img should be File objects for the
1633 corresponding images. info should be the dictionary returned by
1634 common.LoadInfoDict() on the input target_files.
1635 """
1636
Doug Zongker412c02f2014-02-13 10:58:24 -08001637 if info_dict is None:
1638 info_dict = OPTIONS.info_dict
1639
Tao Baof2cffbd2015-07-22 12:33:18 -07001640 full_recovery_image = info_dict.get("full_recovery_image", None) == "true"
Tao Bao7a5bf8a2015-07-21 18:01:20 -07001641 system_root_image = info_dict.get("system_root_image", None) == "true"
Doug Zongkerc9253822014-02-04 12:17:58 -08001642
Tao Baof2cffbd2015-07-22 12:33:18 -07001643 if full_recovery_image:
1644 output_sink("etc/recovery.img", recovery_img.data)
1645
1646 else:
1647 diff_program = ["imgdiff"]
1648 path = os.path.join(input_dir, "SYSTEM", "etc", "recovery-resource.dat")
1649 if os.path.exists(path):
1650 diff_program.append("-b")
1651 diff_program.append(path)
1652 bonus_args = "-b /system/etc/recovery-resource.dat"
1653 else:
1654 bonus_args = ""
1655
1656 d = Difference(recovery_img, boot_img, diff_program=diff_program)
1657 _, _, patch = d.ComputePatch()
1658 output_sink("recovery-from-boot.p", patch)
Doug Zongkerc9253822014-02-04 12:17:58 -08001659
Dan Albertebb19aa2015-03-27 19:11:53 -07001660 try:
Tao Bao6f0b2192015-10-13 16:37:12 -07001661 # The following GetTypeAndDevice()s need to use the path in the target
1662 # info_dict instead of source_info_dict.
Dan Albertebb19aa2015-03-27 19:11:53 -07001663 boot_type, boot_device = GetTypeAndDevice("/boot", info_dict)
1664 recovery_type, recovery_device = GetTypeAndDevice("/recovery", info_dict)
1665 except KeyError:
Ying Wanga961a092014-07-29 11:42:37 -07001666 return
Doug Zongkerc9253822014-02-04 12:17:58 -08001667
Tao Baof2cffbd2015-07-22 12:33:18 -07001668 if full_recovery_image:
1669 sh = """#!/system/bin/sh
1670if ! applypatch -c %(type)s:%(device)s:%(size)d:%(sha1)s; then
1671 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"
1672else
1673 log -t recovery "Recovery image already installed"
1674fi
1675""" % {'type': recovery_type,
1676 'device': recovery_device,
1677 'sha1': recovery_img.sha1,
1678 'size': recovery_img.size}
1679 else:
1680 sh = """#!/system/bin/sh
Doug Zongkerc9253822014-02-04 12:17:58 -08001681if ! applypatch -c %(recovery_type)s:%(recovery_device)s:%(recovery_size)d:%(recovery_sha1)s; then
1682 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"
1683else
1684 log -t recovery "Recovery image already installed"
1685fi
Dan Albert8b72aef2015-03-23 19:13:21 -07001686""" % {'boot_size': boot_img.size,
1687 'boot_sha1': boot_img.sha1,
1688 'recovery_size': recovery_img.size,
1689 'recovery_sha1': recovery_img.sha1,
1690 'boot_type': boot_type,
1691 'boot_device': boot_device,
1692 'recovery_type': recovery_type,
1693 'recovery_device': recovery_device,
1694 'bonus_args': bonus_args}
Doug Zongkerc9253822014-02-04 12:17:58 -08001695
1696 # The install script location moved from /system/etc to /system/bin
Tao Bao9f0c8df2015-07-07 18:31:47 -07001697 # in the L release. Parse init.*.rc files to find out where the
Doug Zongkerc9253822014-02-04 12:17:58 -08001698 # target-files expects it to be, and put it there.
1699 sh_location = "etc/install-recovery.sh"
Tao Bao9f0c8df2015-07-07 18:31:47 -07001700 found = False
Tao Bao7a5bf8a2015-07-21 18:01:20 -07001701 if system_root_image:
1702 init_rc_dir = os.path.join(input_dir, "ROOT")
1703 else:
1704 init_rc_dir = os.path.join(input_dir, "BOOT", "RAMDISK")
Tao Bao9f0c8df2015-07-07 18:31:47 -07001705 init_rc_files = os.listdir(init_rc_dir)
1706 for init_rc_file in init_rc_files:
1707 if (not init_rc_file.startswith('init.') or
1708 not init_rc_file.endswith('.rc')):
1709 continue
1710
1711 with open(os.path.join(init_rc_dir, init_rc_file)) as f:
Doug Zongkerc9253822014-02-04 12:17:58 -08001712 for line in f:
Dan Albert8b72aef2015-03-23 19:13:21 -07001713 m = re.match(r"^service flash_recovery /system/(\S+)\s*$", line)
Doug Zongkerc9253822014-02-04 12:17:58 -08001714 if m:
1715 sh_location = m.group(1)
Tao Bao9f0c8df2015-07-07 18:31:47 -07001716 found = True
Doug Zongkerc9253822014-02-04 12:17:58 -08001717 break
Tao Bao9f0c8df2015-07-07 18:31:47 -07001718
1719 if found:
1720 break
1721
1722 print "putting script in", sh_location
Doug Zongkerc9253822014-02-04 12:17:58 -08001723
1724 output_sink(sh_location, sh)