blob: 8ed12dc3810d95d16e816474b4e0dfd4444ae2f4 [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,
Tao Baod42e97e2016-11-30 12:11:57 -0800398 has_ramdisk=False, two_step_image=False):
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700399 """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
Tao Baod42e97e2016-11-30 12:11:57 -0800402 'sourcedir'), and turn them into a boot image. 'two_step_image' indicates if
403 we are building a two-step special image (i.e. building a recovery image to
404 be loaded into /boot in two-step OTAs).
405
406 Return the image data, or None if sourcedir does not appear to contains files
407 for building the requested image.
408 """
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700409
410 def make_ramdisk():
411 ramdisk_img = tempfile.NamedTemporaryFile()
412
413 if os.access(fs_config_file, os.F_OK):
414 cmd = ["mkbootfs", "-f", fs_config_file,
415 os.path.join(sourcedir, "RAMDISK")]
416 else:
417 cmd = ["mkbootfs", os.path.join(sourcedir, "RAMDISK")]
418 p1 = Run(cmd, stdout=subprocess.PIPE)
419 p2 = Run(["minigzip"], stdin=p1.stdout, stdout=ramdisk_img.file.fileno())
420
421 p2.wait()
422 p1.wait()
423 assert p1.returncode == 0, "mkbootfs of %s ramdisk failed" % (sourcedir,)
424 assert p2.returncode == 0, "minigzip of %s ramdisk failed" % (sourcedir,)
425
426 return ramdisk_img
427
428 if not os.access(os.path.join(sourcedir, "kernel"), os.F_OK):
429 return None
430
431 if has_ramdisk and not os.access(os.path.join(sourcedir, "RAMDISK"), os.F_OK):
Doug Zongkere1c31ba2009-06-23 17:40:35 -0700432 return None
Doug Zongkereef39442009-04-02 12:14:19 -0700433
Doug Zongkerd5131602012-08-02 14:46:42 -0700434 if info_dict is None:
435 info_dict = OPTIONS.info_dict
436
Doug Zongkereef39442009-04-02 12:14:19 -0700437 img = tempfile.NamedTemporaryFile()
438
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700439 if has_ramdisk:
440 ramdisk_img = make_ramdisk()
Doug Zongkereef39442009-04-02 12:14:19 -0700441
Bjorn Andersson612e2cd2012-11-25 16:53:44 -0800442 # use MKBOOTIMG from environ, or "mkbootimg" if empty or not set
443 mkbootimg = os.getenv('MKBOOTIMG') or "mkbootimg"
444
445 cmd = [mkbootimg, "--kernel", os.path.join(sourcedir, "kernel")]
Doug Zongker38a649f2009-06-17 09:07:09 -0700446
Benoit Fradina45a8682014-07-14 21:00:43 +0200447 fn = os.path.join(sourcedir, "second")
448 if os.access(fn, os.F_OK):
449 cmd.append("--second")
450 cmd.append(fn)
451
Doug Zongker171f1cd2009-06-15 22:36:37 -0700452 fn = os.path.join(sourcedir, "cmdline")
453 if os.access(fn, os.F_OK):
Doug Zongker38a649f2009-06-17 09:07:09 -0700454 cmd.append("--cmdline")
455 cmd.append(open(fn).read().rstrip("\n"))
456
457 fn = os.path.join(sourcedir, "base")
458 if os.access(fn, os.F_OK):
459 cmd.append("--base")
460 cmd.append(open(fn).read().rstrip("\n"))
461
Ying Wang4de6b5b2010-08-25 14:29:34 -0700462 fn = os.path.join(sourcedir, "pagesize")
463 if os.access(fn, os.F_OK):
464 cmd.append("--pagesize")
465 cmd.append(open(fn).read().rstrip("\n"))
466
Doug Zongkerd5131602012-08-02 14:46:42 -0700467 args = info_dict.get("mkbootimg_args", None)
468 if args and args.strip():
Jianxun Zhang09849492013-04-17 15:19:19 -0700469 cmd.extend(shlex.split(args))
Doug Zongkerd5131602012-08-02 14:46:42 -0700470
Sami Tolvanen3303d902016-03-15 16:49:30 +0000471 args = info_dict.get("mkbootimg_version_args", None)
472 if args and args.strip():
473 cmd.extend(shlex.split(args))
474
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700475 if has_ramdisk:
476 cmd.extend(["--ramdisk", ramdisk_img.name])
477
Tao Baod95e9fd2015-03-29 23:07:41 -0700478 img_unsigned = None
479 if info_dict.get("vboot", None):
480 img_unsigned = tempfile.NamedTemporaryFile()
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700481 cmd.extend(["--output", img_unsigned.name])
Tao Baod95e9fd2015-03-29 23:07:41 -0700482 else:
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700483 cmd.extend(["--output", img.name])
Doug Zongker38a649f2009-06-17 09:07:09 -0700484
485 p = Run(cmd, stdout=subprocess.PIPE)
Doug Zongkereef39442009-04-02 12:14:19 -0700486 p.communicate()
Doug Zongkere1c31ba2009-06-23 17:40:35 -0700487 assert p.returncode == 0, "mkbootimg of %s image failed" % (
488 os.path.basename(sourcedir),)
Doug Zongkereef39442009-04-02 12:14:19 -0700489
Sami Tolvanen8b3f08b2015-04-07 15:08:59 +0100490 if (info_dict.get("boot_signer", None) == "true" and
491 info_dict.get("verity_key", None)):
Tao Baod42e97e2016-11-30 12:11:57 -0800492 # Hard-code the path as "/boot" for two-step special recovery image (which
493 # will be loaded into /boot during the two-step OTA).
494 if two_step_image:
495 path = "/boot"
496 else:
497 path = "/" + os.path.basename(sourcedir).lower()
Baligh Uddin601ddea2015-06-09 15:48:14 -0700498 cmd = [OPTIONS.boot_signer_path]
499 cmd.extend(OPTIONS.boot_signer_args)
500 cmd.extend([path, img.name,
501 info_dict["verity_key"] + ".pk8",
502 info_dict["verity_key"] + ".x509.pem", img.name])
Geremy Condra95ebe7a2014-08-19 17:27:56 -0700503 p = Run(cmd, stdout=subprocess.PIPE)
504 p.communicate()
505 assert p.returncode == 0, "boot_signer of %s image failed" % path
506
Tao Baod95e9fd2015-03-29 23:07:41 -0700507 # Sign the image if vboot is non-empty.
508 elif info_dict.get("vboot", None):
509 path = "/" + os.path.basename(sourcedir).lower()
510 img_keyblock = tempfile.NamedTemporaryFile()
511 cmd = [info_dict["vboot_signer_cmd"], info_dict["futility"],
512 img_unsigned.name, info_dict["vboot_key"] + ".vbpubk",
Furquan Shaikh852b8de2015-08-10 11:43:45 -0700513 info_dict["vboot_key"] + ".vbprivk",
514 info_dict["vboot_subkey"] + ".vbprivk",
515 img_keyblock.name,
Tao Baod95e9fd2015-03-29 23:07:41 -0700516 img.name]
517 p = Run(cmd, stdout=subprocess.PIPE)
518 p.communicate()
519 assert p.returncode == 0, "vboot_signer of %s image failed" % path
520
Tao Baof3282b42015-04-01 11:21:55 -0700521 # Clean up the temp files.
522 img_unsigned.close()
523 img_keyblock.close()
524
David Zeuthen2ce63ed2016-09-15 13:43:54 -0400525 # AVB: if enabled, calculate and add hash to boot.img.
Tao Baob31b94e2016-09-29 21:59:06 -0700526 if info_dict.get("board_avb_enable", None) == "true":
David Zeuthen2ce63ed2016-09-15 13:43:54 -0400527 avbtool = os.getenv('AVBTOOL') or "avbtool"
Tao Baob31b94e2016-09-29 21:59:06 -0700528 part_size = info_dict.get("boot_size", None)
David Zeuthen2ce63ed2016-09-15 13:43:54 -0400529 cmd = [avbtool, "add_hash_footer", "--image", img.name,
530 "--partition_size", str(part_size), "--partition_name", "boot"]
531 AppendAVBSigningArgs(cmd)
Tao Baob31b94e2016-09-29 21:59:06 -0700532 args = info_dict.get("board_avb_boot_add_hash_footer_args", None)
David Zeuthen2ce63ed2016-09-15 13:43:54 -0400533 if args and args.strip():
534 cmd.extend(shlex.split(args))
535 p = Run(cmd, stdout=subprocess.PIPE)
536 p.communicate()
537 assert p.returncode == 0, "avbtool add_hash_footer of %s failed" % (
538 os.path.basename(OPTIONS.input_tmp))
David Zeuthend995f4b2016-01-29 16:59:17 -0500539
540 img.seek(os.SEEK_SET, 0)
541 data = img.read()
542
543 if has_ramdisk:
544 ramdisk_img.close()
545 img.close()
546
547 return data
548
549
Doug Zongkerd5131602012-08-02 14:46:42 -0700550def GetBootableImage(name, prebuilt_name, unpack_dir, tree_subdir,
Tao Baod42e97e2016-11-30 12:11:57 -0800551 info_dict=None, two_step_image=False):
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700552 """Return a File object with the desired bootable image.
553
554 Look for it in 'unpack_dir'/BOOTABLE_IMAGES under the name 'prebuilt_name',
555 otherwise look for it under 'unpack_dir'/IMAGES, otherwise construct it from
556 the source files in 'unpack_dir'/'tree_subdir'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700557
Doug Zongker55d93282011-01-25 17:03:34 -0800558 prebuilt_path = os.path.join(unpack_dir, "BOOTABLE_IMAGES", prebuilt_name)
559 if os.path.exists(prebuilt_path):
Doug Zongker6f1d0312014-08-22 08:07:12 -0700560 print "using prebuilt %s from BOOTABLE_IMAGES..." % (prebuilt_name,)
Doug Zongker55d93282011-01-25 17:03:34 -0800561 return File.FromLocalFile(name, prebuilt_path)
Doug Zongker6f1d0312014-08-22 08:07:12 -0700562
563 prebuilt_path = os.path.join(unpack_dir, "IMAGES", prebuilt_name)
564 if os.path.exists(prebuilt_path):
565 print "using prebuilt %s from IMAGES..." % (prebuilt_name,)
566 return File.FromLocalFile(name, prebuilt_path)
567
568 print "building image from target_files %s..." % (tree_subdir,)
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700569
570 if info_dict is None:
571 info_dict = OPTIONS.info_dict
572
573 # With system_root_image == "true", we don't pack ramdisk into the boot image.
Daniel Rosenberg40ef35b2015-11-10 19:21:34 -0800574 # Unless "recovery_as_boot" is specified, in which case we carry the ramdisk
575 # for recovery.
576 has_ramdisk = (info_dict.get("system_root_image") != "true" or
577 prebuilt_name != "boot.img" or
578 info_dict.get("recovery_as_boot") == "true")
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700579
Doug Zongker6f1d0312014-08-22 08:07:12 -0700580 fs_config = "META/" + tree_subdir.lower() + "_filesystem_config.txt"
David Zeuthen2ce63ed2016-09-15 13:43:54 -0400581 data = _BuildBootableImage(os.path.join(unpack_dir, tree_subdir),
582 os.path.join(unpack_dir, fs_config),
Tao Baod42e97e2016-11-30 12:11:57 -0800583 info_dict, has_ramdisk, two_step_image)
Doug Zongker6f1d0312014-08-22 08:07:12 -0700584 if data:
585 return File(name, data)
586 return None
Doug Zongker55d93282011-01-25 17:03:34 -0800587
Doug Zongkereef39442009-04-02 12:14:19 -0700588
Doug Zongker75f17362009-12-08 13:46:44 -0800589def UnzipTemp(filename, pattern=None):
Doug Zongker55d93282011-01-25 17:03:34 -0800590 """Unzip the given archive into a temporary directory and return the name.
591
592 If filename is of the form "foo.zip+bar.zip", unzip foo.zip into a
593 temp dir, then unzip bar.zip into that_dir/BOOTABLE_IMAGES.
594
595 Returns (tempdir, zipobj) where zipobj is a zipfile.ZipFile (of the
596 main file), open for reading.
597 """
Doug Zongkereef39442009-04-02 12:14:19 -0700598
599 tmp = tempfile.mkdtemp(prefix="targetfiles-")
600 OPTIONS.tempfiles.append(tmp)
Doug Zongker55d93282011-01-25 17:03:34 -0800601
602 def unzip_to_dir(filename, dirname):
603 cmd = ["unzip", "-o", "-q", filename, "-d", dirname]
604 if pattern is not None:
605 cmd.append(pattern)
606 p = Run(cmd, stdout=subprocess.PIPE)
607 p.communicate()
608 if p.returncode != 0:
609 raise ExternalError("failed to unzip input target-files \"%s\"" %
610 (filename,))
611
612 m = re.match(r"^(.*[.]zip)\+(.*[.]zip)$", filename, re.IGNORECASE)
613 if m:
614 unzip_to_dir(m.group(1), tmp)
615 unzip_to_dir(m.group(2), os.path.join(tmp, "BOOTABLE_IMAGES"))
616 filename = m.group(1)
617 else:
618 unzip_to_dir(filename, tmp)
619
620 return tmp, zipfile.ZipFile(filename, "r")
Doug Zongkereef39442009-04-02 12:14:19 -0700621
622
623def GetKeyPasswords(keylist):
624 """Given a list of keys, prompt the user to enter passwords for
625 those which require them. Return a {key: password} dict. password
626 will be None if the key has no password."""
627
Doug Zongker8ce7c252009-05-22 13:34:54 -0700628 no_passwords = []
629 need_passwords = []
T.R. Fullhart37e10522013-03-18 10:31:26 -0700630 key_passwords = {}
Doug Zongkereef39442009-04-02 12:14:19 -0700631 devnull = open("/dev/null", "w+b")
632 for k in sorted(keylist):
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800633 # We don't need a password for things that aren't really keys.
634 if k in SPECIAL_CERT_STRINGS:
Doug Zongker8ce7c252009-05-22 13:34:54 -0700635 no_passwords.append(k)
Doug Zongker43874f82009-04-14 14:05:15 -0700636 continue
637
T.R. Fullhart37e10522013-03-18 10:31:26 -0700638 p = Run(["openssl", "pkcs8", "-in", k+OPTIONS.private_key_suffix,
Doug Zongker602a84e2009-06-18 08:35:12 -0700639 "-inform", "DER", "-nocrypt"],
640 stdin=devnull.fileno(),
641 stdout=devnull.fileno(),
642 stderr=subprocess.STDOUT)
Doug Zongkereef39442009-04-02 12:14:19 -0700643 p.communicate()
644 if p.returncode == 0:
T.R. Fullhart37e10522013-03-18 10:31:26 -0700645 # Definitely an unencrypted key.
Doug Zongker8ce7c252009-05-22 13:34:54 -0700646 no_passwords.append(k)
Doug Zongkereef39442009-04-02 12:14:19 -0700647 else:
T.R. Fullhart37e10522013-03-18 10:31:26 -0700648 p = Run(["openssl", "pkcs8", "-in", k+OPTIONS.private_key_suffix,
649 "-inform", "DER", "-passin", "pass:"],
650 stdin=devnull.fileno(),
651 stdout=devnull.fileno(),
652 stderr=subprocess.PIPE)
Dan Albert8b72aef2015-03-23 19:13:21 -0700653 _, stderr = p.communicate()
T.R. Fullhart37e10522013-03-18 10:31:26 -0700654 if p.returncode == 0:
655 # Encrypted key with empty string as password.
656 key_passwords[k] = ''
657 elif stderr.startswith('Error decrypting key'):
658 # Definitely encrypted key.
659 # It would have said "Error reading key" if it didn't parse correctly.
660 need_passwords.append(k)
661 else:
662 # Potentially, a type of key that openssl doesn't understand.
663 # We'll let the routines in signapk.jar handle it.
664 no_passwords.append(k)
Doug Zongkereef39442009-04-02 12:14:19 -0700665 devnull.close()
Doug Zongker8ce7c252009-05-22 13:34:54 -0700666
T.R. Fullhart37e10522013-03-18 10:31:26 -0700667 key_passwords.update(PasswordManager().GetPasswords(need_passwords))
Doug Zongker8ce7c252009-05-22 13:34:54 -0700668 key_passwords.update(dict.fromkeys(no_passwords, None))
Doug Zongkereef39442009-04-02 12:14:19 -0700669 return key_passwords
670
671
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800672def GetMinSdkVersion(apk_name):
673 """Get the minSdkVersion delared in the APK. This can be both a decimal number
674 (API Level) or a codename.
675 """
676
677 p = Run(["aapt", "dump", "badging", apk_name], stdout=subprocess.PIPE)
678 output, err = p.communicate()
679 if err:
680 raise ExternalError("Failed to obtain minSdkVersion: aapt return code %s"
681 % (p.returncode,))
682
683 for line in output.split("\n"):
684 # Looking for lines such as sdkVersion:'23' or sdkVersion:'M'
685 m = re.match(r'sdkVersion:\'([^\']*)\'', line)
686 if m:
687 return m.group(1)
688 raise ExternalError("No minSdkVersion returned by aapt")
689
690
691def GetMinSdkVersionInt(apk_name, codename_to_api_level_map):
692 """Get the minSdkVersion declared in the APK as a number (API Level). If
693 minSdkVersion is set to a codename, it is translated to a number using the
694 provided map.
695 """
696
697 version = GetMinSdkVersion(apk_name)
698 try:
699 return int(version)
700 except ValueError:
701 # Not a decimal number. Codename?
702 if version in codename_to_api_level_map:
703 return codename_to_api_level_map[version]
704 else:
705 raise ExternalError("Unknown minSdkVersion: '%s'. Known codenames: %s"
706 % (version, codename_to_api_level_map))
707
708
709def SignFile(input_name, output_name, key, password, min_api_level=None,
710 codename_to_api_level_map=dict(),
711 whole_file=False):
Doug Zongkereef39442009-04-02 12:14:19 -0700712 """Sign the input_name zip/jar/apk, producing output_name. Use the
713 given key and password (the latter may be None if the key does not
714 have a password.
715
Doug Zongker951495f2009-08-14 12:44:19 -0700716 If whole_file is true, use the "-w" option to SignApk to embed a
717 signature that covers the whole file in the archive comment of the
718 zip file.
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800719
720 min_api_level is the API Level (int) of the oldest platform this file may end
721 up on. If not specified for an APK, the API Level is obtained by interpreting
722 the minSdkVersion attribute of the APK's AndroidManifest.xml.
723
724 codename_to_api_level_map is needed to translate the codename which may be
725 encountered as the APK's minSdkVersion.
Doug Zongkereef39442009-04-02 12:14:19 -0700726 """
Doug Zongker951495f2009-08-14 12:44:19 -0700727
Alex Klyubin9667b182015-12-10 13:38:50 -0800728 java_library_path = os.path.join(
729 OPTIONS.search_path, OPTIONS.signapk_shared_library_path)
730
Tao Baoe95540e2016-11-08 12:08:53 -0800731 cmd = ([OPTIONS.java_path] + OPTIONS.java_args +
732 ["-Djava.library.path=" + java_library_path,
733 "-jar", os.path.join(OPTIONS.search_path, OPTIONS.signapk_path)] +
734 OPTIONS.extra_signapk_args)
Doug Zongker951495f2009-08-14 12:44:19 -0700735 if whole_file:
736 cmd.append("-w")
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800737
738 min_sdk_version = min_api_level
739 if min_sdk_version is None:
740 if not whole_file:
741 min_sdk_version = GetMinSdkVersionInt(
742 input_name, codename_to_api_level_map)
743 if min_sdk_version is not None:
744 cmd.extend(["--min-sdk-version", str(min_sdk_version)])
745
T.R. Fullhart37e10522013-03-18 10:31:26 -0700746 cmd.extend([key + OPTIONS.public_key_suffix,
747 key + OPTIONS.private_key_suffix,
Alex Klyubineb756d72015-12-04 09:21:08 -0800748 input_name, output_name])
Doug Zongker951495f2009-08-14 12:44:19 -0700749
750 p = Run(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
Doug Zongkereef39442009-04-02 12:14:19 -0700751 if password is not None:
752 password += "\n"
753 p.communicate(password)
754 if p.returncode != 0:
755 raise ExternalError("signapk.jar failed: return code %s" % (p.returncode,))
756
Doug Zongkereef39442009-04-02 12:14:19 -0700757
Doug Zongker37974732010-09-16 17:44:38 -0700758def CheckSize(data, target, info_dict):
Doug Zongkereef39442009-04-02 12:14:19 -0700759 """Check the data string passed against the max size limit, if
760 any, for the given target. Raise exception if the data is too big.
761 Print a warning if the data is nearing the maximum size."""
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700762
Dan Albert8b72aef2015-03-23 19:13:21 -0700763 if target.endswith(".img"):
764 target = target[:-4]
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700765 mount_point = "/" + target
766
Ying Wangf8824af2014-06-03 14:07:27 -0700767 fs_type = None
768 limit = None
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700769 if info_dict["fstab"]:
Dan Albert8b72aef2015-03-23 19:13:21 -0700770 if mount_point == "/userdata":
771 mount_point = "/data"
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700772 p = info_dict["fstab"][mount_point]
773 fs_type = p.fs_type
Andrew Boie0f9aec82012-02-14 09:32:52 -0800774 device = p.device
775 if "/" in device:
776 device = device[device.rfind("/")+1:]
777 limit = info_dict.get(device + "_size", None)
Dan Albert8b72aef2015-03-23 19:13:21 -0700778 if not fs_type or not limit:
779 return
Doug Zongkereef39442009-04-02 12:14:19 -0700780
Andrew Boie0f9aec82012-02-14 09:32:52 -0800781 size = len(data)
782 pct = float(size) * 100.0 / limit
783 msg = "%s size (%d) is %.2f%% of limit (%d)" % (target, size, pct, limit)
784 if pct >= 99.0:
785 raise ExternalError(msg)
786 elif pct >= 95.0:
787 print
788 print " WARNING: ", msg
789 print
790 elif OPTIONS.verbose:
791 print " ", msg
Doug Zongkereef39442009-04-02 12:14:19 -0700792
793
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800794def ReadApkCerts(tf_zip):
795 """Given a target_files ZipFile, parse the META/apkcerts.txt file
796 and return a {package: cert} dict."""
797 certmap = {}
798 for line in tf_zip.read("META/apkcerts.txt").split("\n"):
799 line = line.strip()
Dan Albert8b72aef2015-03-23 19:13:21 -0700800 if not line:
801 continue
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800802 m = re.match(r'^name="(.*)"\s+certificate="(.*)"\s+'
803 r'private_key="(.*)"$', line)
804 if m:
805 name, cert, privkey = m.groups()
T.R. Fullhart37e10522013-03-18 10:31:26 -0700806 public_key_suffix_len = len(OPTIONS.public_key_suffix)
807 private_key_suffix_len = len(OPTIONS.private_key_suffix)
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800808 if cert in SPECIAL_CERT_STRINGS and not privkey:
809 certmap[name] = cert
T.R. Fullhart37e10522013-03-18 10:31:26 -0700810 elif (cert.endswith(OPTIONS.public_key_suffix) and
811 privkey.endswith(OPTIONS.private_key_suffix) and
812 cert[:-public_key_suffix_len] == privkey[:-private_key_suffix_len]):
813 certmap[name] = cert[:-public_key_suffix_len]
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800814 else:
815 raise ValueError("failed to parse line from apkcerts.txt:\n" + line)
816 return certmap
817
818
Doug Zongkereef39442009-04-02 12:14:19 -0700819COMMON_DOCSTRING = """
820 -p (--path) <dir>
Doug Zongker602a84e2009-06-18 08:35:12 -0700821 Prepend <dir>/bin to the list of places to search for binaries
822 run by this script, and expect to find jars in <dir>/framework.
Doug Zongkereef39442009-04-02 12:14:19 -0700823
Doug Zongker05d3dea2009-06-22 11:32:31 -0700824 -s (--device_specific) <file>
825 Path to the python module containing device-specific
826 releasetools code.
827
Doug Zongker8bec09e2009-11-30 15:37:14 -0800828 -x (--extra) <key=value>
829 Add a key/value pair to the 'extras' dict, which device-specific
830 extension code may look at.
831
Doug Zongkereef39442009-04-02 12:14:19 -0700832 -v (--verbose)
833 Show command lines being executed.
834
835 -h (--help)
836 Display this usage message and exit.
837"""
838
839def Usage(docstring):
840 print docstring.rstrip("\n")
841 print COMMON_DOCSTRING
842
843
844def ParseOptions(argv,
845 docstring,
846 extra_opts="", extra_long_opts=(),
847 extra_option_handler=None):
848 """Parse the options in argv and return any arguments that aren't
849 flags. docstring is the calling module's docstring, to be displayed
850 for errors and -h. extra_opts and extra_long_opts are for flags
851 defined by the caller, which are processed by passing them to
852 extra_option_handler."""
853
854 try:
855 opts, args = getopt.getopt(
Doug Zongker8bec09e2009-11-30 15:37:14 -0800856 argv, "hvp:s:x:" + extra_opts,
Alex Klyubin9667b182015-12-10 13:38:50 -0800857 ["help", "verbose", "path=", "signapk_path=",
858 "signapk_shared_library_path=", "extra_signapk_args=",
Baligh Uddinbdc2e312014-09-05 17:36:20 -0700859 "java_path=", "java_args=", "public_key_suffix=",
Baligh Uddin601ddea2015-06-09 15:48:14 -0700860 "private_key_suffix=", "boot_signer_path=", "boot_signer_args=",
861 "verity_signer_path=", "verity_signer_args=", "device_specific=",
Baligh Uddine2048682014-11-20 09:52:05 -0800862 "extra="] +
T.R. Fullhart37e10522013-03-18 10:31:26 -0700863 list(extra_long_opts))
Dan Albert8b72aef2015-03-23 19:13:21 -0700864 except getopt.GetoptError as err:
Doug Zongkereef39442009-04-02 12:14:19 -0700865 Usage(docstring)
866 print "**", str(err), "**"
867 sys.exit(2)
868
Doug Zongkereef39442009-04-02 12:14:19 -0700869 for o, a in opts:
870 if o in ("-h", "--help"):
871 Usage(docstring)
872 sys.exit()
873 elif o in ("-v", "--verbose"):
874 OPTIONS.verbose = True
875 elif o in ("-p", "--path"):
Doug Zongker602a84e2009-06-18 08:35:12 -0700876 OPTIONS.search_path = a
T.R. Fullhart37e10522013-03-18 10:31:26 -0700877 elif o in ("--signapk_path",):
878 OPTIONS.signapk_path = a
Alex Klyubin9667b182015-12-10 13:38:50 -0800879 elif o in ("--signapk_shared_library_path",):
880 OPTIONS.signapk_shared_library_path = a
T.R. Fullhart37e10522013-03-18 10:31:26 -0700881 elif o in ("--extra_signapk_args",):
882 OPTIONS.extra_signapk_args = shlex.split(a)
883 elif o in ("--java_path",):
884 OPTIONS.java_path = a
Baligh Uddin339ee492014-09-05 11:18:07 -0700885 elif o in ("--java_args",):
Tao Baoe95540e2016-11-08 12:08:53 -0800886 OPTIONS.java_args = shlex.split(a)
T.R. Fullhart37e10522013-03-18 10:31:26 -0700887 elif o in ("--public_key_suffix",):
888 OPTIONS.public_key_suffix = a
889 elif o in ("--private_key_suffix",):
890 OPTIONS.private_key_suffix = a
Baligh Uddine2048682014-11-20 09:52:05 -0800891 elif o in ("--boot_signer_path",):
892 OPTIONS.boot_signer_path = a
Baligh Uddin601ddea2015-06-09 15:48:14 -0700893 elif o in ("--boot_signer_args",):
894 OPTIONS.boot_signer_args = shlex.split(a)
895 elif o in ("--verity_signer_path",):
896 OPTIONS.verity_signer_path = a
897 elif o in ("--verity_signer_args",):
898 OPTIONS.verity_signer_args = shlex.split(a)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700899 elif o in ("-s", "--device_specific"):
900 OPTIONS.device_specific = a
Doug Zongker5ecba702009-12-03 16:36:20 -0800901 elif o in ("-x", "--extra"):
Doug Zongker8bec09e2009-11-30 15:37:14 -0800902 key, value = a.split("=", 1)
903 OPTIONS.extras[key] = value
Doug Zongkereef39442009-04-02 12:14:19 -0700904 else:
905 if extra_option_handler is None or not extra_option_handler(o, a):
906 assert False, "unknown option \"%s\"" % (o,)
907
Doug Zongker85448772014-09-09 14:59:20 -0700908 if OPTIONS.search_path:
909 os.environ["PATH"] = (os.path.join(OPTIONS.search_path, "bin") +
910 os.pathsep + os.environ["PATH"])
Doug Zongkereef39442009-04-02 12:14:19 -0700911
912 return args
913
914
Doug Zongkerfc44a512014-08-26 13:10:25 -0700915def MakeTempFile(prefix=None, suffix=None):
916 """Make a temp file and add it to the list of things to be deleted
917 when Cleanup() is called. Return the filename."""
918 fd, fn = tempfile.mkstemp(prefix=prefix, suffix=suffix)
919 os.close(fd)
920 OPTIONS.tempfiles.append(fn)
921 return fn
922
923
Doug Zongkereef39442009-04-02 12:14:19 -0700924def Cleanup():
925 for i in OPTIONS.tempfiles:
926 if os.path.isdir(i):
927 shutil.rmtree(i)
928 else:
929 os.remove(i)
Doug Zongker8ce7c252009-05-22 13:34:54 -0700930
931
932class PasswordManager(object):
933 def __init__(self):
934 self.editor = os.getenv("EDITOR", None)
935 self.pwfile = os.getenv("ANDROID_PW_FILE", None)
936
937 def GetPasswords(self, items):
938 """Get passwords corresponding to each string in 'items',
939 returning a dict. (The dict may have keys in addition to the
940 values in 'items'.)
941
942 Uses the passwords in $ANDROID_PW_FILE if available, letting the
943 user edit that file to add more needed passwords. If no editor is
944 available, or $ANDROID_PW_FILE isn't define, prompts the user
945 interactively in the ordinary way.
946 """
947
948 current = self.ReadFile()
949
950 first = True
951 while True:
952 missing = []
953 for i in items:
954 if i not in current or not current[i]:
955 missing.append(i)
956 # Are all the passwords already in the file?
Dan Albert8b72aef2015-03-23 19:13:21 -0700957 if not missing:
958 return current
Doug Zongker8ce7c252009-05-22 13:34:54 -0700959
960 for i in missing:
961 current[i] = ""
962
963 if not first:
964 print "key file %s still missing some passwords." % (self.pwfile,)
965 answer = raw_input("try to edit again? [y]> ").strip()
966 if answer and answer[0] not in 'yY':
967 raise RuntimeError("key passwords unavailable")
968 first = False
969
970 current = self.UpdateAndReadFile(current)
971
Dan Albert8b72aef2015-03-23 19:13:21 -0700972 def PromptResult(self, current): # pylint: disable=no-self-use
Doug Zongker8ce7c252009-05-22 13:34:54 -0700973 """Prompt the user to enter a value (password) for each key in
974 'current' whose value is fales. Returns a new dict with all the
975 values.
976 """
977 result = {}
978 for k, v in sorted(current.iteritems()):
979 if v:
980 result[k] = v
981 else:
982 while True:
Dan Albert8b72aef2015-03-23 19:13:21 -0700983 result[k] = getpass.getpass(
984 "Enter password for %s key> " % k).strip()
985 if result[k]:
986 break
Doug Zongker8ce7c252009-05-22 13:34:54 -0700987 return result
988
989 def UpdateAndReadFile(self, current):
990 if not self.editor or not self.pwfile:
991 return self.PromptResult(current)
992
993 f = open(self.pwfile, "w")
Dan Albert8b72aef2015-03-23 19:13:21 -0700994 os.chmod(self.pwfile, 0o600)
Doug Zongker8ce7c252009-05-22 13:34:54 -0700995 f.write("# Enter key passwords between the [[[ ]]] brackets.\n")
996 f.write("# (Additional spaces are harmless.)\n\n")
997
998 first_line = None
Dan Albert8b72aef2015-03-23 19:13:21 -0700999 sorted_list = sorted([(not v, k, v) for (k, v) in current.iteritems()])
1000 for i, (_, k, v) in enumerate(sorted_list):
Doug Zongker8ce7c252009-05-22 13:34:54 -07001001 f.write("[[[ %s ]]] %s\n" % (v, k))
1002 if not v and first_line is None:
1003 # position cursor on first line with no password.
1004 first_line = i + 4
1005 f.close()
1006
1007 p = Run([self.editor, "+%d" % (first_line,), self.pwfile])
1008 _, _ = p.communicate()
1009
1010 return self.ReadFile()
1011
1012 def ReadFile(self):
1013 result = {}
Dan Albert8b72aef2015-03-23 19:13:21 -07001014 if self.pwfile is None:
1015 return result
Doug Zongker8ce7c252009-05-22 13:34:54 -07001016 try:
1017 f = open(self.pwfile, "r")
1018 for line in f:
1019 line = line.strip()
Dan Albert8b72aef2015-03-23 19:13:21 -07001020 if not line or line[0] == '#':
1021 continue
Doug Zongker8ce7c252009-05-22 13:34:54 -07001022 m = re.match(r"^\[\[\[\s*(.*?)\s*\]\]\]\s*(\S+)$", line)
1023 if not m:
1024 print "failed to parse password file: ", line
1025 else:
1026 result[m.group(2)] = m.group(1)
1027 f.close()
Dan Albert8b72aef2015-03-23 19:13:21 -07001028 except IOError as e:
Doug Zongker8ce7c252009-05-22 13:34:54 -07001029 if e.errno != errno.ENOENT:
1030 print "error reading password file: ", str(e)
1031 return result
Doug Zongker048e7ca2009-06-15 14:31:53 -07001032
1033
Dan Albert8e0178d2015-01-27 15:53:15 -08001034def ZipWrite(zip_file, filename, arcname=None, perms=0o644,
1035 compress_type=None):
1036 import datetime
1037
1038 # http://b/18015246
1039 # Python 2.7's zipfile implementation wrongly thinks that zip64 is required
1040 # for files larger than 2GiB. We can work around this by adjusting their
1041 # limit. Note that `zipfile.writestr()` will not work for strings larger than
1042 # 2GiB. The Python interpreter sometimes rejects strings that large (though
1043 # it isn't clear to me exactly what circumstances cause this).
1044 # `zipfile.write()` must be used directly to work around this.
1045 #
1046 # This mess can be avoided if we port to python3.
1047 saved_zip64_limit = zipfile.ZIP64_LIMIT
1048 zipfile.ZIP64_LIMIT = (1 << 32) - 1
1049
1050 if compress_type is None:
1051 compress_type = zip_file.compression
1052 if arcname is None:
1053 arcname = filename
1054
1055 saved_stat = os.stat(filename)
1056
1057 try:
1058 # `zipfile.write()` doesn't allow us to pass ZipInfo, so just modify the
1059 # file to be zipped and reset it when we're done.
1060 os.chmod(filename, perms)
1061
1062 # Use a fixed timestamp so the output is repeatable.
1063 epoch = datetime.datetime.fromtimestamp(0)
1064 timestamp = (datetime.datetime(2009, 1, 1) - epoch).total_seconds()
1065 os.utime(filename, (timestamp, timestamp))
1066
1067 zip_file.write(filename, arcname=arcname, compress_type=compress_type)
1068 finally:
1069 os.chmod(filename, saved_stat.st_mode)
1070 os.utime(filename, (saved_stat.st_atime, saved_stat.st_mtime))
1071 zipfile.ZIP64_LIMIT = saved_zip64_limit
1072
1073
Tao Bao58c1b962015-05-20 09:32:18 -07001074def ZipWriteStr(zip_file, zinfo_or_arcname, data, perms=None,
Tao Baof3282b42015-04-01 11:21:55 -07001075 compress_type=None):
1076 """Wrap zipfile.writestr() function to work around the zip64 limit.
1077
1078 Even with the ZIP64_LIMIT workaround, it won't allow writing a string
1079 longer than 2GiB. It gives 'OverflowError: size does not fit in an int'
1080 when calling crc32(bytes).
1081
1082 But it still works fine to write a shorter string into a large zip file.
1083 We should use ZipWrite() whenever possible, and only use ZipWriteStr()
1084 when we know the string won't be too long.
1085 """
1086
1087 saved_zip64_limit = zipfile.ZIP64_LIMIT
1088 zipfile.ZIP64_LIMIT = (1 << 32) - 1
1089
1090 if not isinstance(zinfo_or_arcname, zipfile.ZipInfo):
1091 zinfo = zipfile.ZipInfo(filename=zinfo_or_arcname)
Dan Albert8b72aef2015-03-23 19:13:21 -07001092 zinfo.compress_type = zip_file.compression
Tao Bao58c1b962015-05-20 09:32:18 -07001093 if perms is None:
Tao Bao2a410582015-07-10 17:18:23 -07001094 perms = 0o100644
Geremy Condra36bd3652014-02-06 19:45:10 -08001095 else:
Tao Baof3282b42015-04-01 11:21:55 -07001096 zinfo = zinfo_or_arcname
1097
1098 # If compress_type is given, it overrides the value in zinfo.
1099 if compress_type is not None:
1100 zinfo.compress_type = compress_type
1101
Tao Bao58c1b962015-05-20 09:32:18 -07001102 # If perms is given, it has a priority.
1103 if perms is not None:
Tao Bao2a410582015-07-10 17:18:23 -07001104 # If perms doesn't set the file type, mark it as a regular file.
1105 if perms & 0o770000 == 0:
1106 perms |= 0o100000
Tao Bao58c1b962015-05-20 09:32:18 -07001107 zinfo.external_attr = perms << 16
1108
Tao Baof3282b42015-04-01 11:21:55 -07001109 # Use a fixed timestamp so the output is repeatable.
Tao Baof3282b42015-04-01 11:21:55 -07001110 zinfo.date_time = (2009, 1, 1, 0, 0, 0)
1111
Dan Albert8b72aef2015-03-23 19:13:21 -07001112 zip_file.writestr(zinfo, data)
Tao Baof3282b42015-04-01 11:21:55 -07001113 zipfile.ZIP64_LIMIT = saved_zip64_limit
1114
1115
1116def ZipClose(zip_file):
1117 # http://b/18015246
1118 # zipfile also refers to ZIP64_LIMIT during close() when it writes out the
1119 # central directory.
1120 saved_zip64_limit = zipfile.ZIP64_LIMIT
1121 zipfile.ZIP64_LIMIT = (1 << 32) - 1
1122
1123 zip_file.close()
1124
1125 zipfile.ZIP64_LIMIT = saved_zip64_limit
Doug Zongker05d3dea2009-06-22 11:32:31 -07001126
1127
1128class DeviceSpecificParams(object):
1129 module = None
1130 def __init__(self, **kwargs):
1131 """Keyword arguments to the constructor become attributes of this
1132 object, which is passed to all functions in the device-specific
1133 module."""
1134 for k, v in kwargs.iteritems():
1135 setattr(self, k, v)
Doug Zongker8bec09e2009-11-30 15:37:14 -08001136 self.extras = OPTIONS.extras
Doug Zongker05d3dea2009-06-22 11:32:31 -07001137
1138 if self.module is None:
1139 path = OPTIONS.device_specific
Dan Albert8b72aef2015-03-23 19:13:21 -07001140 if not path:
1141 return
Doug Zongker8e2f2b92009-06-24 14:34:57 -07001142 try:
1143 if os.path.isdir(path):
1144 info = imp.find_module("releasetools", [path])
1145 else:
1146 d, f = os.path.split(path)
1147 b, x = os.path.splitext(f)
1148 if x == ".py":
1149 f = b
1150 info = imp.find_module(f, [d])
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001151 print "loaded device-specific extensions from", path
Doug Zongker8e2f2b92009-06-24 14:34:57 -07001152 self.module = imp.load_module("device_specific", *info)
1153 except ImportError:
1154 print "unable to load device-specific module; assuming none"
Doug Zongker05d3dea2009-06-22 11:32:31 -07001155
1156 def _DoCall(self, function_name, *args, **kwargs):
1157 """Call the named function in the device-specific module, passing
1158 the given args and kwargs. The first argument to the call will be
1159 the DeviceSpecific object itself. If there is no module, or the
1160 module does not define the function, return the value of the
1161 'default' kwarg (which itself defaults to None)."""
1162 if self.module is None or not hasattr(self.module, function_name):
1163 return kwargs.get("default", None)
1164 return getattr(self.module, function_name)(*((self,) + args), **kwargs)
1165
1166 def FullOTA_Assertions(self):
1167 """Called after emitting the block of assertions at the top of a
1168 full OTA package. Implementations can add whatever additional
1169 assertions they like."""
1170 return self._DoCall("FullOTA_Assertions")
1171
Doug Zongkere5ff5902012-01-17 10:55:37 -08001172 def FullOTA_InstallBegin(self):
1173 """Called at the start of full OTA installation."""
1174 return self._DoCall("FullOTA_InstallBegin")
1175
Doug Zongker05d3dea2009-06-22 11:32:31 -07001176 def FullOTA_InstallEnd(self):
1177 """Called at the end of full OTA installation; typically this is
1178 used to install the image for the device's baseband processor."""
1179 return self._DoCall("FullOTA_InstallEnd")
1180
1181 def IncrementalOTA_Assertions(self):
1182 """Called after emitting the block of assertions at the top of an
1183 incremental OTA package. Implementations can add whatever
1184 additional assertions they like."""
1185 return self._DoCall("IncrementalOTA_Assertions")
1186
Doug Zongkere5ff5902012-01-17 10:55:37 -08001187 def IncrementalOTA_VerifyBegin(self):
1188 """Called at the start of the verification phase of incremental
1189 OTA installation; additional checks can be placed here to abort
1190 the script before any changes are made."""
1191 return self._DoCall("IncrementalOTA_VerifyBegin")
1192
Doug Zongker05d3dea2009-06-22 11:32:31 -07001193 def IncrementalOTA_VerifyEnd(self):
1194 """Called at the end of the verification phase of incremental OTA
1195 installation; additional checks can be placed here to abort the
1196 script before any changes are made."""
1197 return self._DoCall("IncrementalOTA_VerifyEnd")
1198
Doug Zongkere5ff5902012-01-17 10:55:37 -08001199 def IncrementalOTA_InstallBegin(self):
1200 """Called at the start of incremental OTA installation (after
1201 verification is complete)."""
1202 return self._DoCall("IncrementalOTA_InstallBegin")
1203
Doug Zongker05d3dea2009-06-22 11:32:31 -07001204 def IncrementalOTA_InstallEnd(self):
1205 """Called at the end of incremental OTA installation; typically
1206 this is used to install the image for the device's baseband
1207 processor."""
1208 return self._DoCall("IncrementalOTA_InstallEnd")
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001209
Tao Bao9bc6bb22015-11-09 16:58:28 -08001210 def VerifyOTA_Assertions(self):
1211 return self._DoCall("VerifyOTA_Assertions")
1212
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001213class File(object):
YOUNG HO CHAccc5c402016-10-13 13:40:46 +09001214 def __init__(self, name, data, compress_size = None):
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001215 self.name = name
1216 self.data = data
1217 self.size = len(data)
YOUNG HO CHAccc5c402016-10-13 13:40:46 +09001218 self.compress_size = compress_size or self.size
Doug Zongker55d93282011-01-25 17:03:34 -08001219 self.sha1 = sha1(data).hexdigest()
1220
1221 @classmethod
1222 def FromLocalFile(cls, name, diskname):
1223 f = open(diskname, "rb")
1224 data = f.read()
1225 f.close()
1226 return File(name, data)
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001227
1228 def WriteToTemp(self):
1229 t = tempfile.NamedTemporaryFile()
1230 t.write(self.data)
1231 t.flush()
1232 return t
1233
Geremy Condra36bd3652014-02-06 19:45:10 -08001234 def AddToZip(self, z, compression=None):
Tao Baof3282b42015-04-01 11:21:55 -07001235 ZipWriteStr(z, self.name, self.data, compress_type=compression)
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001236
1237DIFF_PROGRAM_BY_EXT = {
1238 ".gz" : "imgdiff",
1239 ".zip" : ["imgdiff", "-z"],
1240 ".jar" : ["imgdiff", "-z"],
1241 ".apk" : ["imgdiff", "-z"],
1242 ".img" : "imgdiff",
1243 }
1244
1245class Difference(object):
Doug Zongker24cd2802012-08-14 16:36:15 -07001246 def __init__(self, tf, sf, diff_program=None):
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001247 self.tf = tf
1248 self.sf = sf
1249 self.patch = None
Doug Zongker24cd2802012-08-14 16:36:15 -07001250 self.diff_program = diff_program
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001251
1252 def ComputePatch(self):
1253 """Compute the patch (as a string of data) needed to turn sf into
1254 tf. Returns the same tuple as GetPatch()."""
1255
1256 tf = self.tf
1257 sf = self.sf
1258
Doug Zongker24cd2802012-08-14 16:36:15 -07001259 if self.diff_program:
1260 diff_program = self.diff_program
1261 else:
1262 ext = os.path.splitext(tf.name)[1]
1263 diff_program = DIFF_PROGRAM_BY_EXT.get(ext, "bsdiff")
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001264
1265 ttemp = tf.WriteToTemp()
1266 stemp = sf.WriteToTemp()
1267
1268 ext = os.path.splitext(tf.name)[1]
1269
1270 try:
1271 ptemp = tempfile.NamedTemporaryFile()
1272 if isinstance(diff_program, list):
1273 cmd = copy.copy(diff_program)
1274 else:
1275 cmd = [diff_program]
1276 cmd.append(stemp.name)
1277 cmd.append(ttemp.name)
1278 cmd.append(ptemp.name)
1279 p = Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Doug Zongkerf8340082014-08-05 10:39:37 -07001280 err = []
1281 def run():
1282 _, e = p.communicate()
Dan Albert8b72aef2015-03-23 19:13:21 -07001283 if e:
1284 err.append(e)
Doug Zongkerf8340082014-08-05 10:39:37 -07001285 th = threading.Thread(target=run)
1286 th.start()
1287 th.join(timeout=300) # 5 mins
1288 if th.is_alive():
1289 print "WARNING: diff command timed out"
1290 p.terminate()
1291 th.join(5)
1292 if th.is_alive():
1293 p.kill()
1294 th.join()
1295
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001296 if err or p.returncode != 0:
Doug Zongkerf8340082014-08-05 10:39:37 -07001297 print "WARNING: failure running %s:\n%s\n" % (
1298 diff_program, "".join(err))
1299 self.patch = None
1300 return None, None, None
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001301 diff = ptemp.read()
1302 finally:
1303 ptemp.close()
1304 stemp.close()
1305 ttemp.close()
1306
1307 self.patch = diff
1308 return self.tf, self.sf, self.patch
1309
1310
1311 def GetPatch(self):
1312 """Return a tuple (target_file, source_file, patch_data).
1313 patch_data may be None if ComputePatch hasn't been called, or if
1314 computing the patch failed."""
1315 return self.tf, self.sf, self.patch
1316
1317
1318def ComputeDifferences(diffs):
1319 """Call ComputePatch on all the Difference objects in 'diffs'."""
1320 print len(diffs), "diffs to compute"
1321
1322 # Do the largest files first, to try and reduce the long-pole effect.
1323 by_size = [(i.tf.size, i) for i in diffs]
1324 by_size.sort(reverse=True)
1325 by_size = [i[1] for i in by_size]
1326
1327 lock = threading.Lock()
1328 diff_iter = iter(by_size) # accessed under lock
1329
1330 def worker():
1331 try:
1332 lock.acquire()
1333 for d in diff_iter:
1334 lock.release()
1335 start = time.time()
1336 d.ComputePatch()
1337 dur = time.time() - start
1338 lock.acquire()
1339
1340 tf, sf, patch = d.GetPatch()
1341 if sf.name == tf.name:
1342 name = tf.name
1343 else:
1344 name = "%s (%s)" % (tf.name, sf.name)
1345 if patch is None:
1346 print "patching failed! %s" % (name,)
1347 else:
1348 print "%8.2f sec %8d / %8d bytes (%6.2f%%) %s" % (
1349 dur, len(patch), tf.size, 100.0 * len(patch) / tf.size, name)
1350 lock.release()
Dan Albert8b72aef2015-03-23 19:13:21 -07001351 except Exception as e:
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001352 print e
1353 raise
1354
1355 # start worker threads; wait for them all to finish.
1356 threads = [threading.Thread(target=worker)
1357 for i in range(OPTIONS.worker_threads)]
1358 for th in threads:
1359 th.start()
1360 while threads:
1361 threads.pop().join()
Doug Zongker96a57e72010-09-26 14:57:41 -07001362
1363
Dan Albert8b72aef2015-03-23 19:13:21 -07001364class BlockDifference(object):
1365 def __init__(self, partition, tgt, src=None, check_first_block=False,
Tao Bao293fd132016-06-11 12:19:23 -07001366 version=None, disable_imgdiff=False):
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001367 self.tgt = tgt
1368 self.src = src
1369 self.partition = partition
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001370 self.check_first_block = check_first_block
Tao Bao293fd132016-06-11 12:19:23 -07001371 self.disable_imgdiff = disable_imgdiff
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001372
Tao Baodd2a5892015-03-12 12:32:37 -07001373 if version is None:
1374 version = 1
1375 if OPTIONS.info_dict:
1376 version = max(
1377 int(i) for i in
1378 OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(","))
1379 self.version = version
Doug Zongker62338182014-09-08 08:29:55 -07001380
1381 b = blockimgdiff.BlockImageDiff(tgt, src, threads=OPTIONS.worker_threads,
Tao Bao293fd132016-06-11 12:19:23 -07001382 version=self.version,
1383 disable_imgdiff=self.disable_imgdiff)
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001384 tmpdir = tempfile.mkdtemp()
1385 OPTIONS.tempfiles.append(tmpdir)
1386 self.path = os.path.join(tmpdir, partition)
1387 b.Compute(self.path)
Tao Baod8d14be2016-02-04 14:26:02 -08001388 self._required_cache = b.max_stashed_size
Tao Baod522bdc2016-04-12 15:53:16 -07001389 self.touched_src_ranges = b.touched_src_ranges
1390 self.touched_src_sha1 = b.touched_src_sha1
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001391
Tao Baoaac4ad52015-10-16 15:26:34 -07001392 if src is None:
1393 _, self.device = GetTypeAndDevice("/" + partition, OPTIONS.info_dict)
1394 else:
1395 _, self.device = GetTypeAndDevice("/" + partition,
1396 OPTIONS.source_info_dict)
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001397
Tao Baod8d14be2016-02-04 14:26:02 -08001398 @property
1399 def required_cache(self):
1400 return self._required_cache
1401
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001402 def WriteScript(self, script, output_zip, progress=None):
1403 if not self.src:
1404 # write the output unconditionally
Jesse Zhao75bcea02015-01-06 10:59:53 -08001405 script.Print("Patching %s image unconditionally..." % (self.partition,))
1406 else:
1407 script.Print("Patching %s image after verification." % (self.partition,))
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001408
Dan Albert8b72aef2015-03-23 19:13:21 -07001409 if progress:
1410 script.ShowProgress(progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001411 self._WriteUpdate(script, output_zip)
Tianjie Xub2deb222016-03-25 15:01:33 -07001412 if OPTIONS.verify:
1413 self._WritePostInstallVerifyScript(script)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001414
Tao Bao9bc6bb22015-11-09 16:58:28 -08001415 def WriteStrictVerifyScript(self, script):
1416 """Verify all the blocks in the care_map, including clobbered blocks.
1417
1418 This differs from the WriteVerifyScript() function: a) it prints different
1419 error messages; b) it doesn't allow half-way updated images to pass the
1420 verification."""
1421
1422 partition = self.partition
1423 script.Print("Verifying %s..." % (partition,))
1424 ranges = self.tgt.care_map
1425 ranges_str = ranges.to_string_raw()
1426 script.AppendExtra('range_sha1("%s", "%s") == "%s" && '
1427 'ui_print(" Verified.") || '
1428 'ui_print("\\"%s\\" has unexpected contents.");' % (
1429 self.device, ranges_str,
1430 self.tgt.TotalSha1(include_clobbered_blocks=True),
1431 self.device))
1432 script.AppendExtra("")
1433
Tao Baod522bdc2016-04-12 15:53:16 -07001434 def WriteVerifyScript(self, script, touched_blocks_only=False):
Sami Tolvanendd67a292014-12-09 16:40:34 +00001435 partition = self.partition
Tao Baof9efe282016-04-14 15:58:05 -07001436
1437 # full OTA
Jesse Zhao75bcea02015-01-06 10:59:53 -08001438 if not self.src:
Sami Tolvanendd67a292014-12-09 16:40:34 +00001439 script.Print("Image %s will be patched unconditionally." % (partition,))
Tao Baof9efe282016-04-14 15:58:05 -07001440
1441 # incremental OTA
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001442 else:
Tao Baod522bdc2016-04-12 15:53:16 -07001443 if touched_blocks_only and self.version >= 3:
1444 ranges = self.touched_src_ranges
1445 expected_sha1 = self.touched_src_sha1
1446 else:
1447 ranges = self.src.care_map.subtract(self.src.clobbered_blocks)
1448 expected_sha1 = self.src.TotalSha1()
Tao Baof9efe282016-04-14 15:58:05 -07001449
1450 # No blocks to be checked, skipping.
1451 if not ranges:
1452 return
1453
Tao Bao5ece99d2015-05-12 11:42:31 -07001454 ranges_str = ranges.to_string_raw()
Sami Tolvanenf0a7c762015-06-25 11:48:29 +01001455 if self.version >= 4:
1456 script.AppendExtra(('if (range_sha1("%s", "%s") == "%s" || '
1457 'block_image_verify("%s", '
1458 'package_extract_file("%s.transfer.list"), '
Tianjie Xufc3422a2015-12-15 11:53:59 -08001459 '"%s.new.dat", "%s.patch.dat")) then') % (
Tao Baod522bdc2016-04-12 15:53:16 -07001460 self.device, ranges_str, expected_sha1,
Sami Tolvanenf0a7c762015-06-25 11:48:29 +01001461 self.device, partition, partition, partition))
1462 elif self.version == 3:
Sami Tolvanene09d0962015-04-24 11:54:01 +01001463 script.AppendExtra(('if (range_sha1("%s", "%s") == "%s" || '
1464 'block_image_verify("%s", '
Michael Runge910b0052015-02-11 19:28:08 -08001465 'package_extract_file("%s.transfer.list"), '
Sami Tolvanene09d0962015-04-24 11:54:01 +01001466 '"%s.new.dat", "%s.patch.dat")) then') % (
Tao Baod522bdc2016-04-12 15:53:16 -07001467 self.device, ranges_str, expected_sha1,
Sami Tolvanene09d0962015-04-24 11:54:01 +01001468 self.device, partition, partition, partition))
Michael Runge910b0052015-02-11 19:28:08 -08001469 else:
Dan Albert8b72aef2015-03-23 19:13:21 -07001470 script.AppendExtra('if range_sha1("%s", "%s") == "%s" then' % (
Tao Bao5ece99d2015-05-12 11:42:31 -07001471 self.device, ranges_str, self.src.TotalSha1()))
Tao Baodd2a5892015-03-12 12:32:37 -07001472 script.Print('Verified %s image...' % (partition,))
Dan Albert8b72aef2015-03-23 19:13:21 -07001473 script.AppendExtra('else')
Sami Tolvanendd67a292014-12-09 16:40:34 +00001474
Tianjie Xufc3422a2015-12-15 11:53:59 -08001475 if self.version >= 4:
1476
1477 # Bug: 21124327
1478 # When generating incrementals for the system and vendor partitions in
1479 # version 4 or newer, explicitly check the first block (which contains
1480 # the superblock) of the partition to see if it's what we expect. If
1481 # this check fails, give an explicit log message about the partition
1482 # having been remounted R/W (the most likely explanation).
1483 if self.check_first_block:
1484 script.AppendExtra('check_first_block("%s");' % (self.device,))
1485
1486 # If version >= 4, try block recovery before abort update
Tianjie Xu209db462016-05-24 17:34:52 -07001487 if partition == "system":
1488 code = ErrorCode.SYSTEM_RECOVER_FAILURE
1489 else:
1490 code = ErrorCode.VENDOR_RECOVER_FAILURE
Tianjie Xufc3422a2015-12-15 11:53:59 -08001491 script.AppendExtra((
1492 'ifelse (block_image_recover("{device}", "{ranges}") && '
1493 'block_image_verify("{device}", '
1494 'package_extract_file("{partition}.transfer.list"), '
1495 '"{partition}.new.dat", "{partition}.patch.dat"), '
1496 'ui_print("{partition} recovered successfully."), '
Tianjie Xu209db462016-05-24 17:34:52 -07001497 'abort("E{code}: {partition} partition fails to recover"));\n'
Tianjie Xufc3422a2015-12-15 11:53:59 -08001498 'endif;').format(device=self.device, ranges=ranges_str,
Tianjie Xu209db462016-05-24 17:34:52 -07001499 partition=partition, code=code))
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001500
Tao Baodd2a5892015-03-12 12:32:37 -07001501 # Abort the OTA update. Note that the incremental OTA cannot be applied
1502 # even if it may match the checksum of the target partition.
1503 # a) If version < 3, operations like move and erase will make changes
1504 # unconditionally and damage the partition.
1505 # b) If version >= 3, it won't even reach here.
Tianjie Xufc3422a2015-12-15 11:53:59 -08001506 else:
Tianjie Xu209db462016-05-24 17:34:52 -07001507 if partition == "system":
1508 code = ErrorCode.SYSTEM_VERIFICATION_FAILURE
1509 else:
1510 code = ErrorCode.VENDOR_VERIFICATION_FAILURE
1511 script.AppendExtra((
1512 'abort("E%d: %s partition has unexpected contents");\n'
1513 'endif;') % (code, partition))
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001514
Tao Bao5fcaaef2015-06-01 13:40:49 -07001515 def _WritePostInstallVerifyScript(self, script):
1516 partition = self.partition
1517 script.Print('Verifying the updated %s image...' % (partition,))
1518 # Unlike pre-install verification, clobbered_blocks should not be ignored.
1519 ranges = self.tgt.care_map
1520 ranges_str = ranges.to_string_raw()
1521 script.AppendExtra('if range_sha1("%s", "%s") == "%s" then' % (
1522 self.device, ranges_str,
1523 self.tgt.TotalSha1(include_clobbered_blocks=True)))
Tao Baoe9b61912015-07-09 17:37:49 -07001524
1525 # Bug: 20881595
1526 # Verify that extended blocks are really zeroed out.
1527 if self.tgt.extended:
1528 ranges_str = self.tgt.extended.to_string_raw()
1529 script.AppendExtra('if range_sha1("%s", "%s") == "%s" then' % (
1530 self.device, ranges_str,
1531 self._HashZeroBlocks(self.tgt.extended.size())))
1532 script.Print('Verified the updated %s image.' % (partition,))
Tianjie Xu209db462016-05-24 17:34:52 -07001533 if partition == "system":
1534 code = ErrorCode.SYSTEM_NONZERO_CONTENTS
1535 else:
1536 code = ErrorCode.VENDOR_NONZERO_CONTENTS
Tao Baoe9b61912015-07-09 17:37:49 -07001537 script.AppendExtra(
1538 'else\n'
Tianjie Xu209db462016-05-24 17:34:52 -07001539 ' abort("E%d: %s partition has unexpected non-zero contents after '
1540 'OTA update");\n'
1541 'endif;' % (code, partition))
Tao Baoe9b61912015-07-09 17:37:49 -07001542 else:
1543 script.Print('Verified the updated %s image.' % (partition,))
1544
Tianjie Xu209db462016-05-24 17:34:52 -07001545 if partition == "system":
1546 code = ErrorCode.SYSTEM_UNEXPECTED_CONTENTS
1547 else:
1548 code = ErrorCode.VENDOR_UNEXPECTED_CONTENTS
1549
Tao Bao5fcaaef2015-06-01 13:40:49 -07001550 script.AppendExtra(
1551 'else\n'
Tianjie Xu209db462016-05-24 17:34:52 -07001552 ' abort("E%d: %s partition has unexpected contents after OTA '
1553 'update");\n'
1554 'endif;' % (code, partition))
Tao Bao5fcaaef2015-06-01 13:40:49 -07001555
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001556 def _WriteUpdate(self, script, output_zip):
Dan Albert8e0178d2015-01-27 15:53:15 -08001557 ZipWrite(output_zip,
1558 '{}.transfer.list'.format(self.path),
1559 '{}.transfer.list'.format(self.partition))
1560 ZipWrite(output_zip,
1561 '{}.new.dat'.format(self.path),
1562 '{}.new.dat'.format(self.partition))
1563 ZipWrite(output_zip,
1564 '{}.patch.dat'.format(self.path),
1565 '{}.patch.dat'.format(self.partition),
1566 compress_type=zipfile.ZIP_STORED)
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001567
Tianjie Xu209db462016-05-24 17:34:52 -07001568 if self.partition == "system":
1569 code = ErrorCode.SYSTEM_UPDATE_FAILURE
1570 else:
1571 code = ErrorCode.VENDOR_UPDATE_FAILURE
1572
Dan Albert8e0178d2015-01-27 15:53:15 -08001573 call = ('block_image_update("{device}", '
1574 'package_extract_file("{partition}.transfer.list"), '
Tianjie Xub2deb222016-03-25 15:01:33 -07001575 '"{partition}.new.dat", "{partition}.patch.dat") ||\n'
Tianjie Xu209db462016-05-24 17:34:52 -07001576 ' abort("E{code}: Failed to update {partition} image.");'.format(
1577 device=self.device, partition=self.partition, code=code))
Dan Albert8b72aef2015-03-23 19:13:21 -07001578 script.AppendExtra(script.WordWrap(call))
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001579
Dan Albert8b72aef2015-03-23 19:13:21 -07001580 def _HashBlocks(self, source, ranges): # pylint: disable=no-self-use
Sami Tolvanendd67a292014-12-09 16:40:34 +00001581 data = source.ReadRangeSet(ranges)
1582 ctx = sha1()
1583
1584 for p in data:
1585 ctx.update(p)
1586
1587 return ctx.hexdigest()
1588
Tao Baoe9b61912015-07-09 17:37:49 -07001589 def _HashZeroBlocks(self, num_blocks): # pylint: disable=no-self-use
1590 """Return the hash value for all zero blocks."""
1591 zero_block = '\x00' * 4096
1592 ctx = sha1()
1593 for _ in range(num_blocks):
1594 ctx.update(zero_block)
1595
1596 return ctx.hexdigest()
1597
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001598
1599DataImage = blockimgdiff.DataImage
1600
Doug Zongker96a57e72010-09-26 14:57:41 -07001601# map recovery.fstab's fs_types to mount/format "partition types"
Dan Albert8b72aef2015-03-23 19:13:21 -07001602PARTITION_TYPES = {
Dan Albert8b72aef2015-03-23 19:13:21 -07001603 "ext4": "EMMC",
1604 "emmc": "EMMC",
Mohamad Ayyash95e74c12015-05-01 15:39:36 -07001605 "f2fs": "EMMC",
1606 "squashfs": "EMMC"
Dan Albert8b72aef2015-03-23 19:13:21 -07001607}
Doug Zongker96a57e72010-09-26 14:57:41 -07001608
1609def GetTypeAndDevice(mount_point, info):
1610 fstab = info["fstab"]
1611 if fstab:
Dan Albert8b72aef2015-03-23 19:13:21 -07001612 return (PARTITION_TYPES[fstab[mount_point].fs_type],
1613 fstab[mount_point].device)
Doug Zongker96a57e72010-09-26 14:57:41 -07001614 else:
Dan Albert8b72aef2015-03-23 19:13:21 -07001615 raise KeyError
Baligh Uddinbeb6afd2013-11-13 00:22:34 +00001616
1617
1618def ParseCertificate(data):
1619 """Parse a PEM-format certificate."""
1620 cert = []
1621 save = False
1622 for line in data.split("\n"):
1623 if "--END CERTIFICATE--" in line:
1624 break
1625 if save:
1626 cert.append(line)
1627 if "--BEGIN CERTIFICATE--" in line:
1628 save = True
1629 cert = "".join(cert).decode('base64')
1630 return cert
Doug Zongkerc9253822014-02-04 12:17:58 -08001631
Doug Zongker412c02f2014-02-13 10:58:24 -08001632def MakeRecoveryPatch(input_dir, output_sink, recovery_img, boot_img,
1633 info_dict=None):
Doug Zongkerc9253822014-02-04 12:17:58 -08001634 """Generate a binary patch that creates the recovery image starting
1635 with the boot image. (Most of the space in these images is just the
1636 kernel, which is identical for the two, so the resulting patch
1637 should be efficient.) Add it to the output zip, along with a shell
1638 script that is run from init.rc on first boot to actually do the
1639 patching and install the new recovery image.
1640
1641 recovery_img and boot_img should be File objects for the
1642 corresponding images. info should be the dictionary returned by
1643 common.LoadInfoDict() on the input target_files.
1644 """
1645
Doug Zongker412c02f2014-02-13 10:58:24 -08001646 if info_dict is None:
1647 info_dict = OPTIONS.info_dict
1648
Tao Baof2cffbd2015-07-22 12:33:18 -07001649 full_recovery_image = info_dict.get("full_recovery_image", None) == "true"
Tao Bao7a5bf8a2015-07-21 18:01:20 -07001650 system_root_image = info_dict.get("system_root_image", None) == "true"
Doug Zongkerc9253822014-02-04 12:17:58 -08001651
Tao Baof2cffbd2015-07-22 12:33:18 -07001652 if full_recovery_image:
1653 output_sink("etc/recovery.img", recovery_img.data)
1654
1655 else:
1656 diff_program = ["imgdiff"]
1657 path = os.path.join(input_dir, "SYSTEM", "etc", "recovery-resource.dat")
1658 if os.path.exists(path):
1659 diff_program.append("-b")
1660 diff_program.append(path)
1661 bonus_args = "-b /system/etc/recovery-resource.dat"
1662 else:
1663 bonus_args = ""
1664
1665 d = Difference(recovery_img, boot_img, diff_program=diff_program)
1666 _, _, patch = d.ComputePatch()
1667 output_sink("recovery-from-boot.p", patch)
Doug Zongkerc9253822014-02-04 12:17:58 -08001668
Dan Albertebb19aa2015-03-27 19:11:53 -07001669 try:
Tao Bao6f0b2192015-10-13 16:37:12 -07001670 # The following GetTypeAndDevice()s need to use the path in the target
1671 # info_dict instead of source_info_dict.
Dan Albertebb19aa2015-03-27 19:11:53 -07001672 boot_type, boot_device = GetTypeAndDevice("/boot", info_dict)
1673 recovery_type, recovery_device = GetTypeAndDevice("/recovery", info_dict)
1674 except KeyError:
Ying Wanga961a092014-07-29 11:42:37 -07001675 return
Doug Zongkerc9253822014-02-04 12:17:58 -08001676
Tao Baof2cffbd2015-07-22 12:33:18 -07001677 if full_recovery_image:
1678 sh = """#!/system/bin/sh
1679if ! applypatch -c %(type)s:%(device)s:%(size)d:%(sha1)s; then
1680 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"
1681else
1682 log -t recovery "Recovery image already installed"
1683fi
1684""" % {'type': recovery_type,
1685 'device': recovery_device,
1686 'sha1': recovery_img.sha1,
1687 'size': recovery_img.size}
1688 else:
1689 sh = """#!/system/bin/sh
Doug Zongkerc9253822014-02-04 12:17:58 -08001690if ! applypatch -c %(recovery_type)s:%(recovery_device)s:%(recovery_size)d:%(recovery_sha1)s; then
1691 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"
1692else
1693 log -t recovery "Recovery image already installed"
1694fi
Dan Albert8b72aef2015-03-23 19:13:21 -07001695""" % {'boot_size': boot_img.size,
1696 'boot_sha1': boot_img.sha1,
1697 'recovery_size': recovery_img.size,
1698 'recovery_sha1': recovery_img.sha1,
1699 'boot_type': boot_type,
1700 'boot_device': boot_device,
1701 'recovery_type': recovery_type,
1702 'recovery_device': recovery_device,
1703 'bonus_args': bonus_args}
Doug Zongkerc9253822014-02-04 12:17:58 -08001704
1705 # The install script location moved from /system/etc to /system/bin
Tao Bao9f0c8df2015-07-07 18:31:47 -07001706 # in the L release. Parse init.*.rc files to find out where the
Doug Zongkerc9253822014-02-04 12:17:58 -08001707 # target-files expects it to be, and put it there.
1708 sh_location = "etc/install-recovery.sh"
Tao Bao9f0c8df2015-07-07 18:31:47 -07001709 found = False
Tao Bao7a5bf8a2015-07-21 18:01:20 -07001710 if system_root_image:
1711 init_rc_dir = os.path.join(input_dir, "ROOT")
1712 else:
1713 init_rc_dir = os.path.join(input_dir, "BOOT", "RAMDISK")
Tao Bao9f0c8df2015-07-07 18:31:47 -07001714 init_rc_files = os.listdir(init_rc_dir)
1715 for init_rc_file in init_rc_files:
1716 if (not init_rc_file.startswith('init.') or
1717 not init_rc_file.endswith('.rc')):
1718 continue
1719
1720 with open(os.path.join(init_rc_dir, init_rc_file)) as f:
Doug Zongkerc9253822014-02-04 12:17:58 -08001721 for line in f:
Dan Albert8b72aef2015-03-23 19:13:21 -07001722 m = re.match(r"^service flash_recovery /system/(\S+)\s*$", line)
Doug Zongkerc9253822014-02-04 12:17:58 -08001723 if m:
1724 sh_location = m.group(1)
Tao Bao9f0c8df2015-07-07 18:31:47 -07001725 found = True
Doug Zongkerc9253822014-02-04 12:17:58 -08001726 break
Tao Bao9f0c8df2015-07-07 18:31:47 -07001727
1728 if found:
1729 break
1730
1731 print "putting script in", sh_location
Doug Zongkerc9253822014-02-04 12:17:58 -08001732
1733 output_sink(sh_location, sh)