blob: 43e38706814306455bd59ca5adef9a275b64c0ef [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 Baocd4f7c92016-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
158 if "mkyaffs2_extra_flags" not in d:
159 try:
Dan Albert8b72aef2015-03-23 19:13:21 -0700160 d["mkyaffs2_extra_flags"] = read_helper(
161 "META/mkyaffs2-extra-flags.txt").strip()
Doug Zongker37974732010-09-16 17:44:38 -0700162 except KeyError:
163 # ok if flags don't exist
164 pass
165
166 if "recovery_api_version" not in d:
167 try:
Dan Albert8b72aef2015-03-23 19:13:21 -0700168 d["recovery_api_version"] = read_helper(
169 "META/recovery-api-version.txt").strip()
Doug Zongker37974732010-09-16 17:44:38 -0700170 except KeyError:
171 raise ValueError("can't find recovery API version in input target-files")
172
173 if "tool_extensions" not in d:
174 try:
Doug Zongkerc9253822014-02-04 12:17:58 -0800175 d["tool_extensions"] = read_helper("META/tool-extensions.txt").strip()
Doug Zongker37974732010-09-16 17:44:38 -0700176 except KeyError:
177 # ok if extensions don't exist
178 pass
179
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800180 if "fstab_version" not in d:
181 d["fstab_version"] = "1"
182
Tao Bao84e75682015-07-19 02:38:53 -0700183 # A few properties are stored as links to the files in the out/ directory.
184 # It works fine with the build system. However, they are no longer available
185 # when (re)generating from target_files zip. If input_dir is not None, we
186 # are doing repacking. Redirect those properties to the actual files in the
187 # unzipped directory.
Tao Bao2c15d9e2015-07-09 11:51:16 -0700188 if input_dir is not None:
Stephen Smalleyd3a803e2015-08-04 14:59:06 -0400189 # We carry a copy of file_contexts.bin under META/. If not available,
190 # search BOOT/RAMDISK/. Note that sometimes we may need a different file
Tao Bao84e75682015-07-19 02:38:53 -0700191 # to build images than the one running on device, such as when enabling
192 # system_root_image. In that case, we must have the one for image
193 # generation copied to META/.
Tao Bao79735a62015-08-28 10:52:03 -0700194 fc_basename = os.path.basename(d.get("selinux_fc", "file_contexts"))
195 fc_config = os.path.join(input_dir, "META", fc_basename)
Tao Bao84e75682015-07-19 02:38:53 -0700196 if d.get("system_root_image") == "true":
197 assert os.path.exists(fc_config)
Tao Bao2c15d9e2015-07-09 11:51:16 -0700198 if not os.path.exists(fc_config):
Tao Bao79735a62015-08-28 10:52:03 -0700199 fc_config = os.path.join(input_dir, "BOOT", "RAMDISK", fc_basename)
Tao Bao2c15d9e2015-07-09 11:51:16 -0700200 if not os.path.exists(fc_config):
201 fc_config = None
202
203 if fc_config:
204 d["selinux_fc"] = fc_config
205
Tao Bao84e75682015-07-19 02:38:53 -0700206 # Similarly we need to redirect "ramdisk_dir" and "ramdisk_fs_config".
207 if d.get("system_root_image") == "true":
208 d["ramdisk_dir"] = os.path.join(input_dir, "ROOT")
209 d["ramdisk_fs_config"] = os.path.join(
210 input_dir, "META", "root_filesystem_config.txt")
211
Tao Baof54216f2016-03-29 15:12:37 -0700212 # Redirect {system,vendor}_base_fs_file.
213 if "system_base_fs_file" in d:
214 basename = os.path.basename(d["system_base_fs_file"])
215 system_base_fs_file = os.path.join(input_dir, "META", basename)
Tao Baob079b502016-05-03 08:01:19 -0700216 if os.path.exists(system_base_fs_file):
217 d["system_base_fs_file"] = system_base_fs_file
218 else:
219 print "Warning: failed to find system base fs file: %s" % (
220 system_base_fs_file,)
221 del d["system_base_fs_file"]
Tao Baof54216f2016-03-29 15:12:37 -0700222
223 if "vendor_base_fs_file" in d:
224 basename = os.path.basename(d["vendor_base_fs_file"])
225 vendor_base_fs_file = os.path.join(input_dir, "META", basename)
Tao Baob079b502016-05-03 08:01:19 -0700226 if os.path.exists(vendor_base_fs_file):
227 d["vendor_base_fs_file"] = vendor_base_fs_file
228 else:
229 print "Warning: failed to find vendor base fs file: %s" % (
230 vendor_base_fs_file,)
231 del d["vendor_base_fs_file"]
Tao Baof54216f2016-03-29 15:12:37 -0700232
Doug Zongker37974732010-09-16 17:44:38 -0700233 try:
Doug Zongkerc9253822014-02-04 12:17:58 -0800234 data = read_helper("META/imagesizes.txt")
Doug Zongker37974732010-09-16 17:44:38 -0700235 for line in data.split("\n"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700236 if not line:
237 continue
Doug Zongker1684d9c2010-09-17 07:44:38 -0700238 name, value = line.split(" ", 1)
Dan Albert8b72aef2015-03-23 19:13:21 -0700239 if not value:
240 continue
Doug Zongker37974732010-09-16 17:44:38 -0700241 if name == "blocksize":
242 d[name] = value
243 else:
244 d[name + "_size"] = value
245 except KeyError:
246 pass
247
248 def makeint(key):
249 if key in d:
250 d[key] = int(d[key], 0)
251
252 makeint("recovery_api_version")
253 makeint("blocksize")
254 makeint("system_size")
Daniel Rosenbergf4eabc32014-07-10 15:42:38 -0700255 makeint("vendor_size")
Doug Zongker37974732010-09-16 17:44:38 -0700256 makeint("userdata_size")
Ying Wang9f8e8db2011-11-04 11:37:01 -0700257 makeint("cache_size")
Doug Zongker37974732010-09-16 17:44:38 -0700258 makeint("recovery_size")
259 makeint("boot_size")
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800260 makeint("fstab_version")
Doug Zongkerc19a8d52010-07-01 15:30:11 -0700261
Tao Bao48550cc2015-11-19 17:05:46 -0800262 if d.get("no_recovery", False) == "true":
263 d["fstab"] = None
264 else:
265 d["fstab"] = LoadRecoveryFSTab(read_helper, d["fstab_version"],
266 d.get("system_root_image", False))
Doug Zongkerc9253822014-02-04 12:17:58 -0800267 d["build.prop"] = LoadBuildProp(read_helper)
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700268 return d
269
Doug Zongkerc9253822014-02-04 12:17:58 -0800270def LoadBuildProp(read_helper):
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700271 try:
Doug Zongkerc9253822014-02-04 12:17:58 -0800272 data = read_helper("SYSTEM/build.prop")
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700273 except KeyError:
274 print "Warning: could not find SYSTEM/build.prop in %s" % zip
275 data = ""
Michael Runge6e836112014-04-15 17:40:21 -0700276 return LoadDictionaryFromLines(data.split("\n"))
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700277
Michael Runge6e836112014-04-15 17:40:21 -0700278def LoadDictionaryFromLines(lines):
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700279 d = {}
Michael Runge6e836112014-04-15 17:40:21 -0700280 for line in lines:
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700281 line = line.strip()
Dan Albert8b72aef2015-03-23 19:13:21 -0700282 if not line or line.startswith("#"):
283 continue
Ying Wang114b46f2014-04-15 11:24:00 -0700284 if "=" in line:
285 name, value = line.split("=", 1)
286 d[name] = value
Doug Zongkerc19a8d52010-07-01 15:30:11 -0700287 return d
288
Daniel Rosenberge6853b02015-06-05 17:59:27 -0700289def LoadRecoveryFSTab(read_helper, fstab_version, system_root_image=False):
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700290 class Partition(object):
Tao Bao548eb762015-06-10 12:32:41 -0700291 def __init__(self, mount_point, fs_type, device, length, device2, context):
Dan Albert8b72aef2015-03-23 19:13:21 -0700292 self.mount_point = mount_point
293 self.fs_type = fs_type
294 self.device = device
295 self.length = length
296 self.device2 = device2
Tao Bao548eb762015-06-10 12:32:41 -0700297 self.context = context
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700298
299 try:
Doug Zongkerc9253822014-02-04 12:17:58 -0800300 data = read_helper("RECOVERY/RAMDISK/etc/recovery.fstab")
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700301 except KeyError:
Doug Zongkerc9253822014-02-04 12:17:58 -0800302 print "Warning: could not find RECOVERY/RAMDISK/etc/recovery.fstab"
Jeff Davidson033fbe22011-10-26 18:08:09 -0700303 data = ""
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700304
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800305 if fstab_version == 1:
306 d = {}
307 for line in data.split("\n"):
308 line = line.strip()
Dan Albert8b72aef2015-03-23 19:13:21 -0700309 if not line or line.startswith("#"):
310 continue
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800311 pieces = line.split()
Dan Albert8b72aef2015-03-23 19:13:21 -0700312 if not 3 <= len(pieces) <= 4:
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800313 raise ValueError("malformed recovery.fstab line: \"%s\"" % (line,))
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800314 options = None
315 if len(pieces) >= 4:
316 if pieces[3].startswith("/"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700317 device2 = pieces[3]
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800318 if len(pieces) >= 5:
319 options = pieces[4]
320 else:
Dan Albert8b72aef2015-03-23 19:13:21 -0700321 device2 = None
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800322 options = pieces[3]
Doug Zongker086cbb02011-02-17 15:54:20 -0800323 else:
Dan Albert8b72aef2015-03-23 19:13:21 -0700324 device2 = None
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700325
Dan Albert8b72aef2015-03-23 19:13:21 -0700326 mount_point = pieces[0]
327 length = 0
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800328 if options:
329 options = options.split(",")
330 for i in options:
331 if i.startswith("length="):
Dan Albert8b72aef2015-03-23 19:13:21 -0700332 length = int(i[7:])
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800333 else:
Dan Albert8b72aef2015-03-23 19:13:21 -0700334 print "%s: unknown option \"%s\"" % (mount_point, i)
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800335
Dan Albert8b72aef2015-03-23 19:13:21 -0700336 d[mount_point] = Partition(mount_point=mount_point, fs_type=pieces[1],
337 device=pieces[2], length=length,
338 device2=device2)
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800339
340 elif fstab_version == 2:
341 d = {}
342 for line in data.split("\n"):
343 line = line.strip()
Dan Albert8b72aef2015-03-23 19:13:21 -0700344 if not line or line.startswith("#"):
345 continue
Tao Bao548eb762015-06-10 12:32:41 -0700346 # <src> <mnt_point> <type> <mnt_flags and options> <fs_mgr_flags>
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800347 pieces = line.split()
348 if len(pieces) != 5:
349 raise ValueError("malformed recovery.fstab line: \"%s\"" % (line,))
350
351 # Ignore entries that are managed by vold
352 options = pieces[4]
Dan Albert8b72aef2015-03-23 19:13:21 -0700353 if "voldmanaged=" in options:
354 continue
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800355
356 # It's a good line, parse it
Dan Albert8b72aef2015-03-23 19:13:21 -0700357 length = 0
Doug Zongker086cbb02011-02-17 15:54:20 -0800358 options = options.split(",")
359 for i in options:
360 if i.startswith("length="):
Dan Albert8b72aef2015-03-23 19:13:21 -0700361 length = int(i[7:])
Doug Zongker086cbb02011-02-17 15:54:20 -0800362 else:
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800363 # Ignore all unknown options in the unified fstab
364 continue
Doug Zongker086cbb02011-02-17 15:54:20 -0800365
Tao Bao548eb762015-06-10 12:32:41 -0700366 mount_flags = pieces[3]
367 # Honor the SELinux context if present.
368 context = None
369 for i in mount_flags.split(","):
370 if i.startswith("context="):
371 context = i
372
Dan Albert8b72aef2015-03-23 19:13:21 -0700373 mount_point = pieces[1]
374 d[mount_point] = Partition(mount_point=mount_point, fs_type=pieces[2],
Tao Bao548eb762015-06-10 12:32:41 -0700375 device=pieces[0], length=length,
376 device2=None, context=context)
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800377
378 else:
379 raise ValueError("Unknown fstab_version: \"%d\"" % (fstab_version,))
380
Daniel Rosenberge6853b02015-06-05 17:59:27 -0700381 # / is used for the system mount point when the root directory is included in
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700382 # system. Other areas assume system is always at "/system" so point /system
383 # at /.
Daniel Rosenberge6853b02015-06-05 17:59:27 -0700384 if system_root_image:
385 assert not d.has_key("/system") and d.has_key("/")
386 d["/system"] = d["/"]
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700387 return d
388
389
Doug Zongker37974732010-09-16 17:44:38 -0700390def DumpInfoDict(d):
391 for k, v in sorted(d.items()):
392 print "%-25s = (%s) %s" % (k, type(v).__name__, v)
Doug Zongkerc19a8d52010-07-01 15:30:11 -0700393
Dan Albert8b72aef2015-03-23 19:13:21 -0700394
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700395def _BuildBootableImage(sourcedir, fs_config_file, info_dict=None,
396 has_ramdisk=False):
397 """Build a bootable image from the specified sourcedir.
Doug Zongkere1c31ba2009-06-23 17:40:35 -0700398
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700399 Take a kernel, cmdline, and optionally a ramdisk directory from the input (in
400 'sourcedir'), and turn them into a boot image. Return the image data, or
401 None if sourcedir does not appear to contains files for building the
402 requested image."""
403
404 def make_ramdisk():
405 ramdisk_img = tempfile.NamedTemporaryFile()
406
407 if os.access(fs_config_file, os.F_OK):
408 cmd = ["mkbootfs", "-f", fs_config_file,
409 os.path.join(sourcedir, "RAMDISK")]
410 else:
411 cmd = ["mkbootfs", os.path.join(sourcedir, "RAMDISK")]
412 p1 = Run(cmd, stdout=subprocess.PIPE)
413 p2 = Run(["minigzip"], stdin=p1.stdout, stdout=ramdisk_img.file.fileno())
414
415 p2.wait()
416 p1.wait()
417 assert p1.returncode == 0, "mkbootfs of %s ramdisk failed" % (sourcedir,)
418 assert p2.returncode == 0, "minigzip of %s ramdisk failed" % (sourcedir,)
419
420 return ramdisk_img
421
422 if not os.access(os.path.join(sourcedir, "kernel"), os.F_OK):
423 return None
424
425 if has_ramdisk and not os.access(os.path.join(sourcedir, "RAMDISK"), os.F_OK):
Doug Zongkere1c31ba2009-06-23 17:40:35 -0700426 return None
Doug Zongkereef39442009-04-02 12:14:19 -0700427
Doug Zongkerd5131602012-08-02 14:46:42 -0700428 if info_dict is None:
429 info_dict = OPTIONS.info_dict
430
Doug Zongkereef39442009-04-02 12:14:19 -0700431 img = tempfile.NamedTemporaryFile()
432
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700433 if has_ramdisk:
434 ramdisk_img = make_ramdisk()
Doug Zongkereef39442009-04-02 12:14:19 -0700435
Bjorn Andersson612e2cd2012-11-25 16:53:44 -0800436 # use MKBOOTIMG from environ, or "mkbootimg" if empty or not set
437 mkbootimg = os.getenv('MKBOOTIMG') or "mkbootimg"
438
439 cmd = [mkbootimg, "--kernel", os.path.join(sourcedir, "kernel")]
Doug Zongker38a649f2009-06-17 09:07:09 -0700440
Benoit Fradina45a8682014-07-14 21:00:43 +0200441 fn = os.path.join(sourcedir, "second")
442 if os.access(fn, os.F_OK):
443 cmd.append("--second")
444 cmd.append(fn)
445
Doug Zongker171f1cd2009-06-15 22:36:37 -0700446 fn = os.path.join(sourcedir, "cmdline")
447 if os.access(fn, os.F_OK):
Doug Zongker38a649f2009-06-17 09:07:09 -0700448 cmd.append("--cmdline")
449 cmd.append(open(fn).read().rstrip("\n"))
450
451 fn = os.path.join(sourcedir, "base")
452 if os.access(fn, os.F_OK):
453 cmd.append("--base")
454 cmd.append(open(fn).read().rstrip("\n"))
455
Ying Wang4de6b5b2010-08-25 14:29:34 -0700456 fn = os.path.join(sourcedir, "pagesize")
457 if os.access(fn, os.F_OK):
458 cmd.append("--pagesize")
459 cmd.append(open(fn).read().rstrip("\n"))
460
Doug Zongkerd5131602012-08-02 14:46:42 -0700461 args = info_dict.get("mkbootimg_args", None)
462 if args and args.strip():
Jianxun Zhang09849492013-04-17 15:19:19 -0700463 cmd.extend(shlex.split(args))
Doug Zongkerd5131602012-08-02 14:46:42 -0700464
Sami Tolvanena8c37be2016-03-15 16:49:30 +0000465 args = info_dict.get("mkbootimg_version_args", None)
466 if args and args.strip():
467 cmd.extend(shlex.split(args))
468
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700469 if has_ramdisk:
470 cmd.extend(["--ramdisk", ramdisk_img.name])
471
Tao Baod95e9fd2015-03-29 23:07:41 -0700472 img_unsigned = None
473 if info_dict.get("vboot", None):
474 img_unsigned = tempfile.NamedTemporaryFile()
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700475 cmd.extend(["--output", img_unsigned.name])
Tao Baod95e9fd2015-03-29 23:07:41 -0700476 else:
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700477 cmd.extend(["--output", img.name])
Doug Zongker38a649f2009-06-17 09:07:09 -0700478
479 p = Run(cmd, stdout=subprocess.PIPE)
Doug Zongkereef39442009-04-02 12:14:19 -0700480 p.communicate()
Doug Zongkere1c31ba2009-06-23 17:40:35 -0700481 assert p.returncode == 0, "mkbootimg of %s image failed" % (
482 os.path.basename(sourcedir),)
Doug Zongkereef39442009-04-02 12:14:19 -0700483
Sami Tolvanen8b3f08b2015-04-07 15:08:59 +0100484 if (info_dict.get("boot_signer", None) == "true" and
485 info_dict.get("verity_key", None)):
Geremy Condra95ebe7a2014-08-19 17:27:56 -0700486 path = "/" + os.path.basename(sourcedir).lower()
Baligh Uddin601ddea2015-06-09 15:48:14 -0700487 cmd = [OPTIONS.boot_signer_path]
488 cmd.extend(OPTIONS.boot_signer_args)
489 cmd.extend([path, img.name,
490 info_dict["verity_key"] + ".pk8",
491 info_dict["verity_key"] + ".x509.pem", img.name])
Geremy Condra95ebe7a2014-08-19 17:27:56 -0700492 p = Run(cmd, stdout=subprocess.PIPE)
493 p.communicate()
494 assert p.returncode == 0, "boot_signer of %s image failed" % path
495
Tao Baod95e9fd2015-03-29 23:07:41 -0700496 # Sign the image if vboot is non-empty.
497 elif info_dict.get("vboot", None):
498 path = "/" + os.path.basename(sourcedir).lower()
499 img_keyblock = tempfile.NamedTemporaryFile()
Tao Bao77620b52017-02-23 11:04:36 -0800500 # We have switched from the prebuilt futility binary to using the tool
501 # (futility-host) built from the source. Override the setting in the old
502 # TF.zip.
503 futility = info_dict["futility"]
504 if futility.startswith("prebuilts/"):
505 futility = "futility-host"
506 cmd = [info_dict["vboot_signer_cmd"], futility,
Tao Baod95e9fd2015-03-29 23:07:41 -0700507 img_unsigned.name, info_dict["vboot_key"] + ".vbpubk",
Furquan Shaikh852b8de2015-08-10 11:43:45 -0700508 info_dict["vboot_key"] + ".vbprivk",
509 info_dict["vboot_subkey"] + ".vbprivk",
510 img_keyblock.name,
Tao Baod95e9fd2015-03-29 23:07:41 -0700511 img.name]
512 p = Run(cmd, stdout=subprocess.PIPE)
513 p.communicate()
514 assert p.returncode == 0, "vboot_signer of %s image failed" % path
515
Tao Baof3282b42015-04-01 11:21:55 -0700516 # Clean up the temp files.
517 img_unsigned.close()
518 img_keyblock.close()
519
Doug Zongkereef39442009-04-02 12:14:19 -0700520 img.seek(os.SEEK_SET, 0)
521 data = img.read()
522
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700523 if has_ramdisk:
524 ramdisk_img.close()
Doug Zongkereef39442009-04-02 12:14:19 -0700525 img.close()
526
527 return data
528
529
Doug Zongkerd5131602012-08-02 14:46:42 -0700530def GetBootableImage(name, prebuilt_name, unpack_dir, tree_subdir,
531 info_dict=None):
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700532 """Return a File object with the desired bootable image.
533
534 Look for it in 'unpack_dir'/BOOTABLE_IMAGES under the name 'prebuilt_name',
535 otherwise look for it under 'unpack_dir'/IMAGES, otherwise construct it from
536 the source files in 'unpack_dir'/'tree_subdir'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700537
Doug Zongker55d93282011-01-25 17:03:34 -0800538 prebuilt_path = os.path.join(unpack_dir, "BOOTABLE_IMAGES", prebuilt_name)
539 if os.path.exists(prebuilt_path):
Doug Zongker6f1d0312014-08-22 08:07:12 -0700540 print "using prebuilt %s from BOOTABLE_IMAGES..." % (prebuilt_name,)
Doug Zongker55d93282011-01-25 17:03:34 -0800541 return File.FromLocalFile(name, prebuilt_path)
Doug Zongker6f1d0312014-08-22 08:07:12 -0700542
543 prebuilt_path = os.path.join(unpack_dir, "IMAGES", prebuilt_name)
544 if os.path.exists(prebuilt_path):
545 print "using prebuilt %s from IMAGES..." % (prebuilt_name,)
546 return File.FromLocalFile(name, prebuilt_path)
547
548 print "building image from target_files %s..." % (tree_subdir,)
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700549
550 if info_dict is None:
551 info_dict = OPTIONS.info_dict
552
553 # With system_root_image == "true", we don't pack ramdisk into the boot image.
Daniel Rosenberg40ef35b2015-11-10 19:21:34 -0800554 # Unless "recovery_as_boot" is specified, in which case we carry the ramdisk
555 # for recovery.
556 has_ramdisk = (info_dict.get("system_root_image") != "true" or
557 prebuilt_name != "boot.img" or
558 info_dict.get("recovery_as_boot") == "true")
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700559
Doug Zongker6f1d0312014-08-22 08:07:12 -0700560 fs_config = "META/" + tree_subdir.lower() + "_filesystem_config.txt"
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700561 data = _BuildBootableImage(os.path.join(unpack_dir, tree_subdir),
562 os.path.join(unpack_dir, fs_config),
563 info_dict, has_ramdisk)
Doug Zongker6f1d0312014-08-22 08:07:12 -0700564 if data:
565 return File(name, data)
566 return None
Doug Zongker55d93282011-01-25 17:03:34 -0800567
Doug Zongkereef39442009-04-02 12:14:19 -0700568
Doug Zongker75f17362009-12-08 13:46:44 -0800569def UnzipTemp(filename, pattern=None):
Doug Zongker55d93282011-01-25 17:03:34 -0800570 """Unzip the given archive into a temporary directory and return the name.
571
572 If filename is of the form "foo.zip+bar.zip", unzip foo.zip into a
573 temp dir, then unzip bar.zip into that_dir/BOOTABLE_IMAGES.
574
575 Returns (tempdir, zipobj) where zipobj is a zipfile.ZipFile (of the
576 main file), open for reading.
577 """
Doug Zongkereef39442009-04-02 12:14:19 -0700578
579 tmp = tempfile.mkdtemp(prefix="targetfiles-")
580 OPTIONS.tempfiles.append(tmp)
Doug Zongker55d93282011-01-25 17:03:34 -0800581
582 def unzip_to_dir(filename, dirname):
583 cmd = ["unzip", "-o", "-q", filename, "-d", dirname]
584 if pattern is not None:
585 cmd.append(pattern)
586 p = Run(cmd, stdout=subprocess.PIPE)
587 p.communicate()
588 if p.returncode != 0:
589 raise ExternalError("failed to unzip input target-files \"%s\"" %
590 (filename,))
591
592 m = re.match(r"^(.*[.]zip)\+(.*[.]zip)$", filename, re.IGNORECASE)
593 if m:
594 unzip_to_dir(m.group(1), tmp)
595 unzip_to_dir(m.group(2), os.path.join(tmp, "BOOTABLE_IMAGES"))
596 filename = m.group(1)
597 else:
598 unzip_to_dir(filename, tmp)
599
600 return tmp, zipfile.ZipFile(filename, "r")
Doug Zongkereef39442009-04-02 12:14:19 -0700601
602
603def GetKeyPasswords(keylist):
604 """Given a list of keys, prompt the user to enter passwords for
605 those which require them. Return a {key: password} dict. password
606 will be None if the key has no password."""
607
Doug Zongker8ce7c252009-05-22 13:34:54 -0700608 no_passwords = []
609 need_passwords = []
T.R. Fullhart37e10522013-03-18 10:31:26 -0700610 key_passwords = {}
Doug Zongkereef39442009-04-02 12:14:19 -0700611 devnull = open("/dev/null", "w+b")
612 for k in sorted(keylist):
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800613 # We don't need a password for things that aren't really keys.
614 if k in SPECIAL_CERT_STRINGS:
Doug Zongker8ce7c252009-05-22 13:34:54 -0700615 no_passwords.append(k)
Doug Zongker43874f82009-04-14 14:05:15 -0700616 continue
617
T.R. Fullhart37e10522013-03-18 10:31:26 -0700618 p = Run(["openssl", "pkcs8", "-in", k+OPTIONS.private_key_suffix,
Doug Zongker602a84e2009-06-18 08:35:12 -0700619 "-inform", "DER", "-nocrypt"],
620 stdin=devnull.fileno(),
621 stdout=devnull.fileno(),
622 stderr=subprocess.STDOUT)
Doug Zongkereef39442009-04-02 12:14:19 -0700623 p.communicate()
624 if p.returncode == 0:
T.R. Fullhart37e10522013-03-18 10:31:26 -0700625 # Definitely an unencrypted key.
Doug Zongker8ce7c252009-05-22 13:34:54 -0700626 no_passwords.append(k)
Doug Zongkereef39442009-04-02 12:14:19 -0700627 else:
T.R. Fullhart37e10522013-03-18 10:31:26 -0700628 p = Run(["openssl", "pkcs8", "-in", k+OPTIONS.private_key_suffix,
629 "-inform", "DER", "-passin", "pass:"],
630 stdin=devnull.fileno(),
631 stdout=devnull.fileno(),
632 stderr=subprocess.PIPE)
Dan Albert8b72aef2015-03-23 19:13:21 -0700633 _, stderr = p.communicate()
T.R. Fullhart37e10522013-03-18 10:31:26 -0700634 if p.returncode == 0:
635 # Encrypted key with empty string as password.
636 key_passwords[k] = ''
637 elif stderr.startswith('Error decrypting key'):
638 # Definitely encrypted key.
639 # It would have said "Error reading key" if it didn't parse correctly.
640 need_passwords.append(k)
641 else:
642 # Potentially, a type of key that openssl doesn't understand.
643 # We'll let the routines in signapk.jar handle it.
644 no_passwords.append(k)
Doug Zongkereef39442009-04-02 12:14:19 -0700645 devnull.close()
Doug Zongker8ce7c252009-05-22 13:34:54 -0700646
T.R. Fullhart37e10522013-03-18 10:31:26 -0700647 key_passwords.update(PasswordManager().GetPasswords(need_passwords))
Doug Zongker8ce7c252009-05-22 13:34:54 -0700648 key_passwords.update(dict.fromkeys(no_passwords, None))
Doug Zongkereef39442009-04-02 12:14:19 -0700649 return key_passwords
650
651
Alex Klyubinb05b62d2016-01-13 10:32:47 -0800652def GetMinSdkVersion(apk_name):
653 """Get the minSdkVersion delared in the APK. This can be both a decimal number
654 (API Level) or a codename.
655 """
656
657 p = Run(["aapt", "dump", "badging", apk_name], stdout=subprocess.PIPE)
658 output, err = p.communicate()
659 if err:
660 raise ExternalError("Failed to obtain minSdkVersion: aapt return code %s"
661 % (p.returncode,))
662
663 for line in output.split("\n"):
664 # Looking for lines such as sdkVersion:'23' or sdkVersion:'M'
665 m = re.match(r'sdkVersion:\'([^\']*)\'', line)
666 if m:
667 return m.group(1)
668 raise ExternalError("No minSdkVersion returned by aapt")
669
670
671def GetMinSdkVersionInt(apk_name, codename_to_api_level_map):
672 """Get the minSdkVersion declared in the APK as a number (API Level). If
673 minSdkVersion is set to a codename, it is translated to a number using the
674 provided map.
675 """
676
677 version = GetMinSdkVersion(apk_name)
678 try:
679 return int(version)
680 except ValueError:
681 # Not a decimal number. Codename?
682 if version in codename_to_api_level_map:
683 return codename_to_api_level_map[version]
684 else:
685 raise ExternalError("Unknown minSdkVersion: '%s'. Known codenames: %s"
686 % (version, codename_to_api_level_map))
687
688
689def SignFile(input_name, output_name, key, password, min_api_level=None,
690 codename_to_api_level_map=dict(),
691 whole_file=False):
Doug Zongkereef39442009-04-02 12:14:19 -0700692 """Sign the input_name zip/jar/apk, producing output_name. Use the
693 given key and password (the latter may be None if the key does not
694 have a password.
695
Doug Zongker951495f2009-08-14 12:44:19 -0700696 If whole_file is true, use the "-w" option to SignApk to embed a
697 signature that covers the whole file in the archive comment of the
698 zip file.
Alex Klyubinb05b62d2016-01-13 10:32:47 -0800699
700 min_api_level is the API Level (int) of the oldest platform this file may end
701 up on. If not specified for an APK, the API Level is obtained by interpreting
702 the minSdkVersion attribute of the APK's AndroidManifest.xml.
703
704 codename_to_api_level_map is needed to translate the codename which may be
705 encountered as the APK's minSdkVersion.
Doug Zongkereef39442009-04-02 12:14:19 -0700706 """
Doug Zongker951495f2009-08-14 12:44:19 -0700707
Alex Klyubin9667b182015-12-10 13:38:50 -0800708 java_library_path = os.path.join(
709 OPTIONS.search_path, OPTIONS.signapk_shared_library_path)
710
Tao Baocd4f7c92016-11-08 12:08:53 -0800711 cmd = ([OPTIONS.java_path] + OPTIONS.java_args +
712 ["-Djava.library.path=" + java_library_path,
713 "-jar", os.path.join(OPTIONS.search_path, OPTIONS.signapk_path)] +
714 OPTIONS.extra_signapk_args)
Doug Zongker951495f2009-08-14 12:44:19 -0700715 if whole_file:
716 cmd.append("-w")
Alex Klyubinb05b62d2016-01-13 10:32:47 -0800717
718 min_sdk_version = min_api_level
719 if min_sdk_version is None:
720 if not whole_file:
721 min_sdk_version = GetMinSdkVersionInt(
722 input_name, codename_to_api_level_map)
723 if min_sdk_version is not None:
724 cmd.extend(["--min-sdk-version", str(min_sdk_version)])
725
T.R. Fullhart37e10522013-03-18 10:31:26 -0700726 cmd.extend([key + OPTIONS.public_key_suffix,
727 key + OPTIONS.private_key_suffix,
Alex Klyubineb756d72015-12-04 09:21:08 -0800728 input_name, output_name])
Doug Zongker951495f2009-08-14 12:44:19 -0700729
730 p = Run(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
Doug Zongkereef39442009-04-02 12:14:19 -0700731 if password is not None:
732 password += "\n"
733 p.communicate(password)
734 if p.returncode != 0:
735 raise ExternalError("signapk.jar failed: return code %s" % (p.returncode,))
736
Doug Zongkereef39442009-04-02 12:14:19 -0700737
Doug Zongker37974732010-09-16 17:44:38 -0700738def CheckSize(data, target, info_dict):
Doug Zongkereef39442009-04-02 12:14:19 -0700739 """Check the data string passed against the max size limit, if
740 any, for the given target. Raise exception if the data is too big.
741 Print a warning if the data is nearing the maximum size."""
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700742
Dan Albert8b72aef2015-03-23 19:13:21 -0700743 if target.endswith(".img"):
744 target = target[:-4]
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700745 mount_point = "/" + target
746
Ying Wangf8824af2014-06-03 14:07:27 -0700747 fs_type = None
748 limit = None
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700749 if info_dict["fstab"]:
Dan Albert8b72aef2015-03-23 19:13:21 -0700750 if mount_point == "/userdata":
751 mount_point = "/data"
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700752 p = info_dict["fstab"][mount_point]
753 fs_type = p.fs_type
Andrew Boie0f9aec82012-02-14 09:32:52 -0800754 device = p.device
755 if "/" in device:
756 device = device[device.rfind("/")+1:]
757 limit = info_dict.get(device + "_size", None)
Dan Albert8b72aef2015-03-23 19:13:21 -0700758 if not fs_type or not limit:
759 return
Doug Zongkereef39442009-04-02 12:14:19 -0700760
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700761 if fs_type == "yaffs2":
762 # image size should be increased by 1/64th to account for the
763 # spare area (64 bytes per 2k page)
764 limit = limit / 2048 * (2048+64)
Andrew Boie0f9aec82012-02-14 09:32:52 -0800765 size = len(data)
766 pct = float(size) * 100.0 / limit
767 msg = "%s size (%d) is %.2f%% of limit (%d)" % (target, size, pct, limit)
768 if pct >= 99.0:
769 raise ExternalError(msg)
770 elif pct >= 95.0:
771 print
772 print " WARNING: ", msg
773 print
774 elif OPTIONS.verbose:
775 print " ", msg
Doug Zongkereef39442009-04-02 12:14:19 -0700776
777
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800778def ReadApkCerts(tf_zip):
779 """Given a target_files ZipFile, parse the META/apkcerts.txt file
780 and return a {package: cert} dict."""
781 certmap = {}
782 for line in tf_zip.read("META/apkcerts.txt").split("\n"):
783 line = line.strip()
Dan Albert8b72aef2015-03-23 19:13:21 -0700784 if not line:
785 continue
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800786 m = re.match(r'^name="(.*)"\s+certificate="(.*)"\s+'
787 r'private_key="(.*)"$', line)
788 if m:
789 name, cert, privkey = m.groups()
T.R. Fullhart37e10522013-03-18 10:31:26 -0700790 public_key_suffix_len = len(OPTIONS.public_key_suffix)
791 private_key_suffix_len = len(OPTIONS.private_key_suffix)
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800792 if cert in SPECIAL_CERT_STRINGS and not privkey:
793 certmap[name] = cert
T.R. Fullhart37e10522013-03-18 10:31:26 -0700794 elif (cert.endswith(OPTIONS.public_key_suffix) and
795 privkey.endswith(OPTIONS.private_key_suffix) and
796 cert[:-public_key_suffix_len] == privkey[:-private_key_suffix_len]):
797 certmap[name] = cert[:-public_key_suffix_len]
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800798 else:
799 raise ValueError("failed to parse line from apkcerts.txt:\n" + line)
800 return certmap
801
802
Doug Zongkereef39442009-04-02 12:14:19 -0700803COMMON_DOCSTRING = """
804 -p (--path) <dir>
Doug Zongker602a84e2009-06-18 08:35:12 -0700805 Prepend <dir>/bin to the list of places to search for binaries
806 run by this script, and expect to find jars in <dir>/framework.
Doug Zongkereef39442009-04-02 12:14:19 -0700807
Doug Zongker05d3dea2009-06-22 11:32:31 -0700808 -s (--device_specific) <file>
809 Path to the python module containing device-specific
810 releasetools code.
811
Doug Zongker8bec09e2009-11-30 15:37:14 -0800812 -x (--extra) <key=value>
813 Add a key/value pair to the 'extras' dict, which device-specific
814 extension code may look at.
815
Doug Zongkereef39442009-04-02 12:14:19 -0700816 -v (--verbose)
817 Show command lines being executed.
818
819 -h (--help)
820 Display this usage message and exit.
821"""
822
823def Usage(docstring):
824 print docstring.rstrip("\n")
825 print COMMON_DOCSTRING
826
827
828def ParseOptions(argv,
829 docstring,
830 extra_opts="", extra_long_opts=(),
831 extra_option_handler=None):
832 """Parse the options in argv and return any arguments that aren't
833 flags. docstring is the calling module's docstring, to be displayed
834 for errors and -h. extra_opts and extra_long_opts are for flags
835 defined by the caller, which are processed by passing them to
836 extra_option_handler."""
837
838 try:
839 opts, args = getopt.getopt(
Doug Zongker8bec09e2009-11-30 15:37:14 -0800840 argv, "hvp:s:x:" + extra_opts,
Alex Klyubin9667b182015-12-10 13:38:50 -0800841 ["help", "verbose", "path=", "signapk_path=",
842 "signapk_shared_library_path=", "extra_signapk_args=",
Baligh Uddinbdc2e312014-09-05 17:36:20 -0700843 "java_path=", "java_args=", "public_key_suffix=",
Baligh Uddin601ddea2015-06-09 15:48:14 -0700844 "private_key_suffix=", "boot_signer_path=", "boot_signer_args=",
845 "verity_signer_path=", "verity_signer_args=", "device_specific=",
Baligh Uddine2048682014-11-20 09:52:05 -0800846 "extra="] +
T.R. Fullhart37e10522013-03-18 10:31:26 -0700847 list(extra_long_opts))
Dan Albert8b72aef2015-03-23 19:13:21 -0700848 except getopt.GetoptError as err:
Doug Zongkereef39442009-04-02 12:14:19 -0700849 Usage(docstring)
850 print "**", str(err), "**"
851 sys.exit(2)
852
Doug Zongkereef39442009-04-02 12:14:19 -0700853 for o, a in opts:
854 if o in ("-h", "--help"):
855 Usage(docstring)
856 sys.exit()
857 elif o in ("-v", "--verbose"):
858 OPTIONS.verbose = True
859 elif o in ("-p", "--path"):
Doug Zongker602a84e2009-06-18 08:35:12 -0700860 OPTIONS.search_path = a
T.R. Fullhart37e10522013-03-18 10:31:26 -0700861 elif o in ("--signapk_path",):
862 OPTIONS.signapk_path = a
Alex Klyubin9667b182015-12-10 13:38:50 -0800863 elif o in ("--signapk_shared_library_path",):
864 OPTIONS.signapk_shared_library_path = a
T.R. Fullhart37e10522013-03-18 10:31:26 -0700865 elif o in ("--extra_signapk_args",):
866 OPTIONS.extra_signapk_args = shlex.split(a)
867 elif o in ("--java_path",):
868 OPTIONS.java_path = a
Baligh Uddin339ee492014-09-05 11:18:07 -0700869 elif o in ("--java_args",):
Tao Baocd4f7c92016-11-08 12:08:53 -0800870 OPTIONS.java_args = shlex.split(a)
T.R. Fullhart37e10522013-03-18 10:31:26 -0700871 elif o in ("--public_key_suffix",):
872 OPTIONS.public_key_suffix = a
873 elif o in ("--private_key_suffix",):
874 OPTIONS.private_key_suffix = a
Baligh Uddine2048682014-11-20 09:52:05 -0800875 elif o in ("--boot_signer_path",):
876 OPTIONS.boot_signer_path = a
Baligh Uddin601ddea2015-06-09 15:48:14 -0700877 elif o in ("--boot_signer_args",):
878 OPTIONS.boot_signer_args = shlex.split(a)
879 elif o in ("--verity_signer_path",):
880 OPTIONS.verity_signer_path = a
881 elif o in ("--verity_signer_args",):
882 OPTIONS.verity_signer_args = shlex.split(a)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700883 elif o in ("-s", "--device_specific"):
884 OPTIONS.device_specific = a
Doug Zongker5ecba702009-12-03 16:36:20 -0800885 elif o in ("-x", "--extra"):
Doug Zongker8bec09e2009-11-30 15:37:14 -0800886 key, value = a.split("=", 1)
887 OPTIONS.extras[key] = value
Doug Zongkereef39442009-04-02 12:14:19 -0700888 else:
889 if extra_option_handler is None or not extra_option_handler(o, a):
890 assert False, "unknown option \"%s\"" % (o,)
891
Doug Zongker85448772014-09-09 14:59:20 -0700892 if OPTIONS.search_path:
893 os.environ["PATH"] = (os.path.join(OPTIONS.search_path, "bin") +
894 os.pathsep + os.environ["PATH"])
Doug Zongkereef39442009-04-02 12:14:19 -0700895
896 return args
897
898
Doug Zongkerfc44a512014-08-26 13:10:25 -0700899def MakeTempFile(prefix=None, suffix=None):
900 """Make a temp file and add it to the list of things to be deleted
901 when Cleanup() is called. Return the filename."""
902 fd, fn = tempfile.mkstemp(prefix=prefix, suffix=suffix)
903 os.close(fd)
904 OPTIONS.tempfiles.append(fn)
905 return fn
906
907
Doug Zongkereef39442009-04-02 12:14:19 -0700908def Cleanup():
909 for i in OPTIONS.tempfiles:
910 if os.path.isdir(i):
911 shutil.rmtree(i)
912 else:
913 os.remove(i)
Doug Zongker8ce7c252009-05-22 13:34:54 -0700914
915
916class PasswordManager(object):
917 def __init__(self):
918 self.editor = os.getenv("EDITOR", None)
919 self.pwfile = os.getenv("ANDROID_PW_FILE", None)
920
921 def GetPasswords(self, items):
922 """Get passwords corresponding to each string in 'items',
923 returning a dict. (The dict may have keys in addition to the
924 values in 'items'.)
925
926 Uses the passwords in $ANDROID_PW_FILE if available, letting the
927 user edit that file to add more needed passwords. If no editor is
928 available, or $ANDROID_PW_FILE isn't define, prompts the user
929 interactively in the ordinary way.
930 """
931
932 current = self.ReadFile()
933
934 first = True
935 while True:
936 missing = []
937 for i in items:
938 if i not in current or not current[i]:
939 missing.append(i)
940 # Are all the passwords already in the file?
Dan Albert8b72aef2015-03-23 19:13:21 -0700941 if not missing:
942 return current
Doug Zongker8ce7c252009-05-22 13:34:54 -0700943
944 for i in missing:
945 current[i] = ""
946
947 if not first:
948 print "key file %s still missing some passwords." % (self.pwfile,)
949 answer = raw_input("try to edit again? [y]> ").strip()
950 if answer and answer[0] not in 'yY':
951 raise RuntimeError("key passwords unavailable")
952 first = False
953
954 current = self.UpdateAndReadFile(current)
955
Dan Albert8b72aef2015-03-23 19:13:21 -0700956 def PromptResult(self, current): # pylint: disable=no-self-use
Doug Zongker8ce7c252009-05-22 13:34:54 -0700957 """Prompt the user to enter a value (password) for each key in
958 'current' whose value is fales. Returns a new dict with all the
959 values.
960 """
961 result = {}
962 for k, v in sorted(current.iteritems()):
963 if v:
964 result[k] = v
965 else:
966 while True:
Dan Albert8b72aef2015-03-23 19:13:21 -0700967 result[k] = getpass.getpass(
968 "Enter password for %s key> " % k).strip()
969 if result[k]:
970 break
Doug Zongker8ce7c252009-05-22 13:34:54 -0700971 return result
972
973 def UpdateAndReadFile(self, current):
974 if not self.editor or not self.pwfile:
975 return self.PromptResult(current)
976
977 f = open(self.pwfile, "w")
Dan Albert8b72aef2015-03-23 19:13:21 -0700978 os.chmod(self.pwfile, 0o600)
Doug Zongker8ce7c252009-05-22 13:34:54 -0700979 f.write("# Enter key passwords between the [[[ ]]] brackets.\n")
980 f.write("# (Additional spaces are harmless.)\n\n")
981
982 first_line = None
Dan Albert8b72aef2015-03-23 19:13:21 -0700983 sorted_list = sorted([(not v, k, v) for (k, v) in current.iteritems()])
984 for i, (_, k, v) in enumerate(sorted_list):
Doug Zongker8ce7c252009-05-22 13:34:54 -0700985 f.write("[[[ %s ]]] %s\n" % (v, k))
986 if not v and first_line is None:
987 # position cursor on first line with no password.
988 first_line = i + 4
989 f.close()
990
991 p = Run([self.editor, "+%d" % (first_line,), self.pwfile])
992 _, _ = p.communicate()
993
994 return self.ReadFile()
995
996 def ReadFile(self):
997 result = {}
Dan Albert8b72aef2015-03-23 19:13:21 -0700998 if self.pwfile is None:
999 return result
Doug Zongker8ce7c252009-05-22 13:34:54 -07001000 try:
1001 f = open(self.pwfile, "r")
1002 for line in f:
1003 line = line.strip()
Dan Albert8b72aef2015-03-23 19:13:21 -07001004 if not line or line[0] == '#':
1005 continue
Doug Zongker8ce7c252009-05-22 13:34:54 -07001006 m = re.match(r"^\[\[\[\s*(.*?)\s*\]\]\]\s*(\S+)$", line)
1007 if not m:
1008 print "failed to parse password file: ", line
1009 else:
1010 result[m.group(2)] = m.group(1)
1011 f.close()
Dan Albert8b72aef2015-03-23 19:13:21 -07001012 except IOError as e:
Doug Zongker8ce7c252009-05-22 13:34:54 -07001013 if e.errno != errno.ENOENT:
1014 print "error reading password file: ", str(e)
1015 return result
Doug Zongker048e7ca2009-06-15 14:31:53 -07001016
1017
Dan Albert8e0178d2015-01-27 15:53:15 -08001018def ZipWrite(zip_file, filename, arcname=None, perms=0o644,
1019 compress_type=None):
1020 import datetime
1021
1022 # http://b/18015246
1023 # Python 2.7's zipfile implementation wrongly thinks that zip64 is required
1024 # for files larger than 2GiB. We can work around this by adjusting their
1025 # limit. Note that `zipfile.writestr()` will not work for strings larger than
1026 # 2GiB. The Python interpreter sometimes rejects strings that large (though
1027 # it isn't clear to me exactly what circumstances cause this).
1028 # `zipfile.write()` must be used directly to work around this.
1029 #
1030 # This mess can be avoided if we port to python3.
1031 saved_zip64_limit = zipfile.ZIP64_LIMIT
1032 zipfile.ZIP64_LIMIT = (1 << 32) - 1
1033
1034 if compress_type is None:
1035 compress_type = zip_file.compression
1036 if arcname is None:
1037 arcname = filename
1038
1039 saved_stat = os.stat(filename)
1040
1041 try:
1042 # `zipfile.write()` doesn't allow us to pass ZipInfo, so just modify the
1043 # file to be zipped and reset it when we're done.
1044 os.chmod(filename, perms)
1045
1046 # Use a fixed timestamp so the output is repeatable.
1047 epoch = datetime.datetime.fromtimestamp(0)
1048 timestamp = (datetime.datetime(2009, 1, 1) - epoch).total_seconds()
1049 os.utime(filename, (timestamp, timestamp))
1050
1051 zip_file.write(filename, arcname=arcname, compress_type=compress_type)
1052 finally:
1053 os.chmod(filename, saved_stat.st_mode)
1054 os.utime(filename, (saved_stat.st_atime, saved_stat.st_mtime))
1055 zipfile.ZIP64_LIMIT = saved_zip64_limit
1056
1057
Tao Bao58c1b962015-05-20 09:32:18 -07001058def ZipWriteStr(zip_file, zinfo_or_arcname, data, perms=None,
Tao Baof3282b42015-04-01 11:21:55 -07001059 compress_type=None):
1060 """Wrap zipfile.writestr() function to work around the zip64 limit.
1061
1062 Even with the ZIP64_LIMIT workaround, it won't allow writing a string
1063 longer than 2GiB. It gives 'OverflowError: size does not fit in an int'
1064 when calling crc32(bytes).
1065
1066 But it still works fine to write a shorter string into a large zip file.
1067 We should use ZipWrite() whenever possible, and only use ZipWriteStr()
1068 when we know the string won't be too long.
1069 """
1070
1071 saved_zip64_limit = zipfile.ZIP64_LIMIT
1072 zipfile.ZIP64_LIMIT = (1 << 32) - 1
1073
1074 if not isinstance(zinfo_or_arcname, zipfile.ZipInfo):
1075 zinfo = zipfile.ZipInfo(filename=zinfo_or_arcname)
Dan Albert8b72aef2015-03-23 19:13:21 -07001076 zinfo.compress_type = zip_file.compression
Tao Bao58c1b962015-05-20 09:32:18 -07001077 if perms is None:
Tao Bao2a410582015-07-10 17:18:23 -07001078 perms = 0o100644
Geremy Condra36bd3652014-02-06 19:45:10 -08001079 else:
Tao Baof3282b42015-04-01 11:21:55 -07001080 zinfo = zinfo_or_arcname
1081
1082 # If compress_type is given, it overrides the value in zinfo.
1083 if compress_type is not None:
1084 zinfo.compress_type = compress_type
1085
Tao Bao58c1b962015-05-20 09:32:18 -07001086 # If perms is given, it has a priority.
1087 if perms is not None:
Tao Bao2a410582015-07-10 17:18:23 -07001088 # If perms doesn't set the file type, mark it as a regular file.
1089 if perms & 0o770000 == 0:
1090 perms |= 0o100000
Tao Bao58c1b962015-05-20 09:32:18 -07001091 zinfo.external_attr = perms << 16
1092
Tao Baof3282b42015-04-01 11:21:55 -07001093 # Use a fixed timestamp so the output is repeatable.
Tao Baof3282b42015-04-01 11:21:55 -07001094 zinfo.date_time = (2009, 1, 1, 0, 0, 0)
1095
Dan Albert8b72aef2015-03-23 19:13:21 -07001096 zip_file.writestr(zinfo, data)
Tao Baof3282b42015-04-01 11:21:55 -07001097 zipfile.ZIP64_LIMIT = saved_zip64_limit
1098
1099
1100def ZipClose(zip_file):
1101 # http://b/18015246
1102 # zipfile also refers to ZIP64_LIMIT during close() when it writes out the
1103 # central directory.
1104 saved_zip64_limit = zipfile.ZIP64_LIMIT
1105 zipfile.ZIP64_LIMIT = (1 << 32) - 1
1106
1107 zip_file.close()
1108
1109 zipfile.ZIP64_LIMIT = saved_zip64_limit
Doug Zongker05d3dea2009-06-22 11:32:31 -07001110
1111
1112class DeviceSpecificParams(object):
1113 module = None
1114 def __init__(self, **kwargs):
1115 """Keyword arguments to the constructor become attributes of this
1116 object, which is passed to all functions in the device-specific
1117 module."""
1118 for k, v in kwargs.iteritems():
1119 setattr(self, k, v)
Doug Zongker8bec09e2009-11-30 15:37:14 -08001120 self.extras = OPTIONS.extras
Doug Zongker05d3dea2009-06-22 11:32:31 -07001121
1122 if self.module is None:
1123 path = OPTIONS.device_specific
Dan Albert8b72aef2015-03-23 19:13:21 -07001124 if not path:
1125 return
Doug Zongker8e2f2b92009-06-24 14:34:57 -07001126 try:
1127 if os.path.isdir(path):
1128 info = imp.find_module("releasetools", [path])
1129 else:
1130 d, f = os.path.split(path)
1131 b, x = os.path.splitext(f)
1132 if x == ".py":
1133 f = b
1134 info = imp.find_module(f, [d])
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001135 print "loaded device-specific extensions from", path
Doug Zongker8e2f2b92009-06-24 14:34:57 -07001136 self.module = imp.load_module("device_specific", *info)
1137 except ImportError:
1138 print "unable to load device-specific module; assuming none"
Doug Zongker05d3dea2009-06-22 11:32:31 -07001139
1140 def _DoCall(self, function_name, *args, **kwargs):
1141 """Call the named function in the device-specific module, passing
1142 the given args and kwargs. The first argument to the call will be
1143 the DeviceSpecific object itself. If there is no module, or the
1144 module does not define the function, return the value of the
1145 'default' kwarg (which itself defaults to None)."""
1146 if self.module is None or not hasattr(self.module, function_name):
1147 return kwargs.get("default", None)
1148 return getattr(self.module, function_name)(*((self,) + args), **kwargs)
1149
1150 def FullOTA_Assertions(self):
1151 """Called after emitting the block of assertions at the top of a
1152 full OTA package. Implementations can add whatever additional
1153 assertions they like."""
1154 return self._DoCall("FullOTA_Assertions")
1155
Doug Zongkere5ff5902012-01-17 10:55:37 -08001156 def FullOTA_InstallBegin(self):
1157 """Called at the start of full OTA installation."""
1158 return self._DoCall("FullOTA_InstallBegin")
1159
Doug Zongker05d3dea2009-06-22 11:32:31 -07001160 def FullOTA_InstallEnd(self):
1161 """Called at the end of full OTA installation; typically this is
1162 used to install the image for the device's baseband processor."""
1163 return self._DoCall("FullOTA_InstallEnd")
1164
1165 def IncrementalOTA_Assertions(self):
1166 """Called after emitting the block of assertions at the top of an
1167 incremental OTA package. Implementations can add whatever
1168 additional assertions they like."""
1169 return self._DoCall("IncrementalOTA_Assertions")
1170
Doug Zongkere5ff5902012-01-17 10:55:37 -08001171 def IncrementalOTA_VerifyBegin(self):
1172 """Called at the start of the verification phase of incremental
1173 OTA installation; additional checks can be placed here to abort
1174 the script before any changes are made."""
1175 return self._DoCall("IncrementalOTA_VerifyBegin")
1176
Doug Zongker05d3dea2009-06-22 11:32:31 -07001177 def IncrementalOTA_VerifyEnd(self):
1178 """Called at the end of the verification phase of incremental OTA
1179 installation; additional checks can be placed here to abort the
1180 script before any changes are made."""
1181 return self._DoCall("IncrementalOTA_VerifyEnd")
1182
Doug Zongkere5ff5902012-01-17 10:55:37 -08001183 def IncrementalOTA_InstallBegin(self):
1184 """Called at the start of incremental OTA installation (after
1185 verification is complete)."""
1186 return self._DoCall("IncrementalOTA_InstallBegin")
1187
Doug Zongker05d3dea2009-06-22 11:32:31 -07001188 def IncrementalOTA_InstallEnd(self):
1189 """Called at the end of incremental OTA installation; typically
1190 this is used to install the image for the device's baseband
1191 processor."""
1192 return self._DoCall("IncrementalOTA_InstallEnd")
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001193
Tao Bao9bc6bb22015-11-09 16:58:28 -08001194 def VerifyOTA_Assertions(self):
1195 return self._DoCall("VerifyOTA_Assertions")
1196
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001197class File(object):
1198 def __init__(self, name, data):
1199 self.name = name
1200 self.data = data
1201 self.size = len(data)
Doug Zongker55d93282011-01-25 17:03:34 -08001202 self.sha1 = sha1(data).hexdigest()
1203
1204 @classmethod
1205 def FromLocalFile(cls, name, diskname):
1206 f = open(diskname, "rb")
1207 data = f.read()
1208 f.close()
1209 return File(name, data)
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001210
1211 def WriteToTemp(self):
1212 t = tempfile.NamedTemporaryFile()
1213 t.write(self.data)
1214 t.flush()
1215 return t
1216
Geremy Condra36bd3652014-02-06 19:45:10 -08001217 def AddToZip(self, z, compression=None):
Tao Baof3282b42015-04-01 11:21:55 -07001218 ZipWriteStr(z, self.name, self.data, compress_type=compression)
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001219
1220DIFF_PROGRAM_BY_EXT = {
1221 ".gz" : "imgdiff",
1222 ".zip" : ["imgdiff", "-z"],
1223 ".jar" : ["imgdiff", "-z"],
1224 ".apk" : ["imgdiff", "-z"],
1225 ".img" : "imgdiff",
1226 }
1227
1228class Difference(object):
Doug Zongker24cd2802012-08-14 16:36:15 -07001229 def __init__(self, tf, sf, diff_program=None):
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001230 self.tf = tf
1231 self.sf = sf
1232 self.patch = None
Doug Zongker24cd2802012-08-14 16:36:15 -07001233 self.diff_program = diff_program
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001234
1235 def ComputePatch(self):
1236 """Compute the patch (as a string of data) needed to turn sf into
1237 tf. Returns the same tuple as GetPatch()."""
1238
1239 tf = self.tf
1240 sf = self.sf
1241
Doug Zongker24cd2802012-08-14 16:36:15 -07001242 if self.diff_program:
1243 diff_program = self.diff_program
1244 else:
1245 ext = os.path.splitext(tf.name)[1]
1246 diff_program = DIFF_PROGRAM_BY_EXT.get(ext, "bsdiff")
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001247
1248 ttemp = tf.WriteToTemp()
1249 stemp = sf.WriteToTemp()
1250
1251 ext = os.path.splitext(tf.name)[1]
1252
1253 try:
1254 ptemp = tempfile.NamedTemporaryFile()
1255 if isinstance(diff_program, list):
1256 cmd = copy.copy(diff_program)
1257 else:
1258 cmd = [diff_program]
1259 cmd.append(stemp.name)
1260 cmd.append(ttemp.name)
1261 cmd.append(ptemp.name)
1262 p = Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Doug Zongkerf8340082014-08-05 10:39:37 -07001263 err = []
1264 def run():
1265 _, e = p.communicate()
Dan Albert8b72aef2015-03-23 19:13:21 -07001266 if e:
1267 err.append(e)
Doug Zongkerf8340082014-08-05 10:39:37 -07001268 th = threading.Thread(target=run)
1269 th.start()
1270 th.join(timeout=300) # 5 mins
1271 if th.is_alive():
1272 print "WARNING: diff command timed out"
1273 p.terminate()
1274 th.join(5)
1275 if th.is_alive():
1276 p.kill()
1277 th.join()
1278
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001279 if err or p.returncode != 0:
Doug Zongkerf8340082014-08-05 10:39:37 -07001280 print "WARNING: failure running %s:\n%s\n" % (
1281 diff_program, "".join(err))
1282 self.patch = None
1283 return None, None, None
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001284 diff = ptemp.read()
1285 finally:
1286 ptemp.close()
1287 stemp.close()
1288 ttemp.close()
1289
1290 self.patch = diff
1291 return self.tf, self.sf, self.patch
1292
1293
1294 def GetPatch(self):
1295 """Return a tuple (target_file, source_file, patch_data).
1296 patch_data may be None if ComputePatch hasn't been called, or if
1297 computing the patch failed."""
1298 return self.tf, self.sf, self.patch
1299
1300
1301def ComputeDifferences(diffs):
1302 """Call ComputePatch on all the Difference objects in 'diffs'."""
1303 print len(diffs), "diffs to compute"
1304
1305 # Do the largest files first, to try and reduce the long-pole effect.
1306 by_size = [(i.tf.size, i) for i in diffs]
1307 by_size.sort(reverse=True)
1308 by_size = [i[1] for i in by_size]
1309
1310 lock = threading.Lock()
1311 diff_iter = iter(by_size) # accessed under lock
1312
1313 def worker():
1314 try:
1315 lock.acquire()
1316 for d in diff_iter:
1317 lock.release()
1318 start = time.time()
1319 d.ComputePatch()
1320 dur = time.time() - start
1321 lock.acquire()
1322
1323 tf, sf, patch = d.GetPatch()
1324 if sf.name == tf.name:
1325 name = tf.name
1326 else:
1327 name = "%s (%s)" % (tf.name, sf.name)
1328 if patch is None:
1329 print "patching failed! %s" % (name,)
1330 else:
1331 print "%8.2f sec %8d / %8d bytes (%6.2f%%) %s" % (
1332 dur, len(patch), tf.size, 100.0 * len(patch) / tf.size, name)
1333 lock.release()
Dan Albert8b72aef2015-03-23 19:13:21 -07001334 except Exception as e:
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001335 print e
1336 raise
1337
1338 # start worker threads; wait for them all to finish.
1339 threads = [threading.Thread(target=worker)
1340 for i in range(OPTIONS.worker_threads)]
1341 for th in threads:
1342 th.start()
1343 while threads:
1344 threads.pop().join()
Doug Zongker96a57e72010-09-26 14:57:41 -07001345
1346
Dan Albert8b72aef2015-03-23 19:13:21 -07001347class BlockDifference(object):
1348 def __init__(self, partition, tgt, src=None, check_first_block=False,
Tao Bao293fd132016-06-11 12:19:23 -07001349 version=None, disable_imgdiff=False):
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001350 self.tgt = tgt
1351 self.src = src
1352 self.partition = partition
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001353 self.check_first_block = check_first_block
Tao Bao293fd132016-06-11 12:19:23 -07001354 self.disable_imgdiff = disable_imgdiff
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001355
Tao Baodd2a5892015-03-12 12:32:37 -07001356 if version is None:
1357 version = 1
1358 if OPTIONS.info_dict:
1359 version = max(
1360 int(i) for i in
1361 OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(","))
1362 self.version = version
Doug Zongker62338182014-09-08 08:29:55 -07001363
1364 b = blockimgdiff.BlockImageDiff(tgt, src, threads=OPTIONS.worker_threads,
Tao Bao293fd132016-06-11 12:19:23 -07001365 version=self.version,
1366 disable_imgdiff=self.disable_imgdiff)
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001367 tmpdir = tempfile.mkdtemp()
1368 OPTIONS.tempfiles.append(tmpdir)
1369 self.path = os.path.join(tmpdir, partition)
1370 b.Compute(self.path)
Tao Baob4cfca52016-02-04 14:26:02 -08001371 self._required_cache = b.max_stashed_size
Tao Baod522bdc2016-04-12 15:53:16 -07001372 self.touched_src_ranges = b.touched_src_ranges
1373 self.touched_src_sha1 = b.touched_src_sha1
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001374
Tao Baoaac4ad52015-10-16 15:26:34 -07001375 if src is None:
1376 _, self.device = GetTypeAndDevice("/" + partition, OPTIONS.info_dict)
1377 else:
1378 _, self.device = GetTypeAndDevice("/" + partition,
1379 OPTIONS.source_info_dict)
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001380
Tao Baob4cfca52016-02-04 14:26:02 -08001381 @property
1382 def required_cache(self):
1383 return self._required_cache
1384
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001385 def WriteScript(self, script, output_zip, progress=None):
1386 if not self.src:
1387 # write the output unconditionally
Jesse Zhao75bcea02015-01-06 10:59:53 -08001388 script.Print("Patching %s image unconditionally..." % (self.partition,))
1389 else:
1390 script.Print("Patching %s image after verification." % (self.partition,))
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001391
Dan Albert8b72aef2015-03-23 19:13:21 -07001392 if progress:
1393 script.ShowProgress(progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001394 self._WriteUpdate(script, output_zip)
Tianjie Xub2deb222016-03-25 15:01:33 -07001395 if OPTIONS.verify:
1396 self._WritePostInstallVerifyScript(script)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001397
Tao Bao9bc6bb22015-11-09 16:58:28 -08001398 def WriteStrictVerifyScript(self, script):
1399 """Verify all the blocks in the care_map, including clobbered blocks.
1400
1401 This differs from the WriteVerifyScript() function: a) it prints different
1402 error messages; b) it doesn't allow half-way updated images to pass the
1403 verification."""
1404
1405 partition = self.partition
1406 script.Print("Verifying %s..." % (partition,))
1407 ranges = self.tgt.care_map
1408 ranges_str = ranges.to_string_raw()
1409 script.AppendExtra('range_sha1("%s", "%s") == "%s" && '
1410 'ui_print(" Verified.") || '
1411 'ui_print("\\"%s\\" has unexpected contents.");' % (
1412 self.device, ranges_str,
1413 self.tgt.TotalSha1(include_clobbered_blocks=True),
1414 self.device))
1415 script.AppendExtra("")
1416
Tao Baod522bdc2016-04-12 15:53:16 -07001417 def WriteVerifyScript(self, script, touched_blocks_only=False):
Sami Tolvanendd67a292014-12-09 16:40:34 +00001418 partition = self.partition
Tao Baof9efe282016-04-14 15:58:05 -07001419
1420 # full OTA
Jesse Zhao75bcea02015-01-06 10:59:53 -08001421 if not self.src:
Sami Tolvanendd67a292014-12-09 16:40:34 +00001422 script.Print("Image %s will be patched unconditionally." % (partition,))
Tao Baof9efe282016-04-14 15:58:05 -07001423
1424 # incremental OTA
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001425 else:
Tao Baod522bdc2016-04-12 15:53:16 -07001426 if touched_blocks_only and self.version >= 3:
1427 ranges = self.touched_src_ranges
1428 expected_sha1 = self.touched_src_sha1
1429 else:
1430 ranges = self.src.care_map.subtract(self.src.clobbered_blocks)
1431 expected_sha1 = self.src.TotalSha1()
Tao Baof9efe282016-04-14 15:58:05 -07001432
1433 # No blocks to be checked, skipping.
1434 if not ranges:
1435 return
1436
Tao Bao5ece99d2015-05-12 11:42:31 -07001437 ranges_str = ranges.to_string_raw()
Sami Tolvanenf0a7c762015-06-25 11:48:29 +01001438 if self.version >= 4:
1439 script.AppendExtra(('if (range_sha1("%s", "%s") == "%s" || '
1440 'block_image_verify("%s", '
1441 'package_extract_file("%s.transfer.list"), '
Tianjie Xufc3422a2015-12-15 11:53:59 -08001442 '"%s.new.dat", "%s.patch.dat")) then') % (
Tao Baod522bdc2016-04-12 15:53:16 -07001443 self.device, ranges_str, expected_sha1,
Sami Tolvanenf0a7c762015-06-25 11:48:29 +01001444 self.device, partition, partition, partition))
1445 elif self.version == 3:
Sami Tolvanene09d0962015-04-24 11:54:01 +01001446 script.AppendExtra(('if (range_sha1("%s", "%s") == "%s" || '
1447 'block_image_verify("%s", '
Michael Runge910b0052015-02-11 19:28:08 -08001448 'package_extract_file("%s.transfer.list"), '
Sami Tolvanene09d0962015-04-24 11:54:01 +01001449 '"%s.new.dat", "%s.patch.dat")) then') % (
Tao Baod522bdc2016-04-12 15:53:16 -07001450 self.device, ranges_str, expected_sha1,
Sami Tolvanene09d0962015-04-24 11:54:01 +01001451 self.device, partition, partition, partition))
Michael Runge910b0052015-02-11 19:28:08 -08001452 else:
Dan Albert8b72aef2015-03-23 19:13:21 -07001453 script.AppendExtra('if range_sha1("%s", "%s") == "%s" then' % (
Tao Bao5ece99d2015-05-12 11:42:31 -07001454 self.device, ranges_str, self.src.TotalSha1()))
Tao Baodd2a5892015-03-12 12:32:37 -07001455 script.Print('Verified %s image...' % (partition,))
Dan Albert8b72aef2015-03-23 19:13:21 -07001456 script.AppendExtra('else')
Sami Tolvanendd67a292014-12-09 16:40:34 +00001457
Tianjie Xufc3422a2015-12-15 11:53:59 -08001458 if self.version >= 4:
1459
1460 # Bug: 21124327
1461 # When generating incrementals for the system and vendor partitions in
1462 # version 4 or newer, explicitly check the first block (which contains
1463 # the superblock) of the partition to see if it's what we expect. If
1464 # this check fails, give an explicit log message about the partition
1465 # having been remounted R/W (the most likely explanation).
1466 if self.check_first_block:
1467 script.AppendExtra('check_first_block("%s");' % (self.device,))
1468
1469 # If version >= 4, try block recovery before abort update
Tianjie Xu209db462016-05-24 17:34:52 -07001470 if partition == "system":
1471 code = ErrorCode.SYSTEM_RECOVER_FAILURE
1472 else:
1473 code = ErrorCode.VENDOR_RECOVER_FAILURE
Tianjie Xufc3422a2015-12-15 11:53:59 -08001474 script.AppendExtra((
1475 'ifelse (block_image_recover("{device}", "{ranges}") && '
1476 'block_image_verify("{device}", '
1477 'package_extract_file("{partition}.transfer.list"), '
1478 '"{partition}.new.dat", "{partition}.patch.dat"), '
1479 'ui_print("{partition} recovered successfully."), '
Tianjie Xu209db462016-05-24 17:34:52 -07001480 'abort("E{code}: {partition} partition fails to recover"));\n'
Tianjie Xufc3422a2015-12-15 11:53:59 -08001481 'endif;').format(device=self.device, ranges=ranges_str,
Tianjie Xu209db462016-05-24 17:34:52 -07001482 partition=partition, code=code))
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001483
Tao Baodd2a5892015-03-12 12:32:37 -07001484 # Abort the OTA update. Note that the incremental OTA cannot be applied
1485 # even if it may match the checksum of the target partition.
1486 # a) If version < 3, operations like move and erase will make changes
1487 # unconditionally and damage the partition.
1488 # b) If version >= 3, it won't even reach here.
Tianjie Xufc3422a2015-12-15 11:53:59 -08001489 else:
Tianjie Xu209db462016-05-24 17:34:52 -07001490 if partition == "system":
1491 code = ErrorCode.SYSTEM_VERIFICATION_FAILURE
1492 else:
1493 code = ErrorCode.VENDOR_VERIFICATION_FAILURE
1494 script.AppendExtra((
1495 'abort("E%d: %s partition has unexpected contents");\n'
1496 'endif;') % (code, partition))
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001497
Tao Bao5fcaaef2015-06-01 13:40:49 -07001498 def _WritePostInstallVerifyScript(self, script):
1499 partition = self.partition
1500 script.Print('Verifying the updated %s image...' % (partition,))
1501 # Unlike pre-install verification, clobbered_blocks should not be ignored.
1502 ranges = self.tgt.care_map
1503 ranges_str = ranges.to_string_raw()
1504 script.AppendExtra('if range_sha1("%s", "%s") == "%s" then' % (
1505 self.device, ranges_str,
1506 self.tgt.TotalSha1(include_clobbered_blocks=True)))
Tao Baoe9b61912015-07-09 17:37:49 -07001507
1508 # Bug: 20881595
1509 # Verify that extended blocks are really zeroed out.
1510 if self.tgt.extended:
1511 ranges_str = self.tgt.extended.to_string_raw()
1512 script.AppendExtra('if range_sha1("%s", "%s") == "%s" then' % (
1513 self.device, ranges_str,
1514 self._HashZeroBlocks(self.tgt.extended.size())))
1515 script.Print('Verified the updated %s image.' % (partition,))
Tianjie Xu209db462016-05-24 17:34:52 -07001516 if partition == "system":
1517 code = ErrorCode.SYSTEM_NONZERO_CONTENTS
1518 else:
1519 code = ErrorCode.VENDOR_NONZERO_CONTENTS
Tao Baoe9b61912015-07-09 17:37:49 -07001520 script.AppendExtra(
1521 'else\n'
Tianjie Xu209db462016-05-24 17:34:52 -07001522 ' abort("E%d: %s partition has unexpected non-zero contents after '
1523 'OTA update");\n'
1524 'endif;' % (code, partition))
Tao Baoe9b61912015-07-09 17:37:49 -07001525 else:
1526 script.Print('Verified the updated %s image.' % (partition,))
1527
Tianjie Xu209db462016-05-24 17:34:52 -07001528 if partition == "system":
1529 code = ErrorCode.SYSTEM_UNEXPECTED_CONTENTS
1530 else:
1531 code = ErrorCode.VENDOR_UNEXPECTED_CONTENTS
1532
Tao Bao5fcaaef2015-06-01 13:40:49 -07001533 script.AppendExtra(
1534 'else\n'
Tianjie Xu209db462016-05-24 17:34:52 -07001535 ' abort("E%d: %s partition has unexpected contents after OTA '
1536 'update");\n'
1537 'endif;' % (code, partition))
Tao Bao5fcaaef2015-06-01 13:40:49 -07001538
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001539 def _WriteUpdate(self, script, output_zip):
Dan Albert8e0178d2015-01-27 15:53:15 -08001540 ZipWrite(output_zip,
1541 '{}.transfer.list'.format(self.path),
1542 '{}.transfer.list'.format(self.partition))
1543 ZipWrite(output_zip,
1544 '{}.new.dat'.format(self.path),
1545 '{}.new.dat'.format(self.partition))
1546 ZipWrite(output_zip,
1547 '{}.patch.dat'.format(self.path),
1548 '{}.patch.dat'.format(self.partition),
1549 compress_type=zipfile.ZIP_STORED)
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001550
Tianjie Xu209db462016-05-24 17:34:52 -07001551 if self.partition == "system":
1552 code = ErrorCode.SYSTEM_UPDATE_FAILURE
1553 else:
1554 code = ErrorCode.VENDOR_UPDATE_FAILURE
1555
Dan Albert8e0178d2015-01-27 15:53:15 -08001556 call = ('block_image_update("{device}", '
1557 'package_extract_file("{partition}.transfer.list"), '
Tianjie Xub2deb222016-03-25 15:01:33 -07001558 '"{partition}.new.dat", "{partition}.patch.dat") ||\n'
Tianjie Xu209db462016-05-24 17:34:52 -07001559 ' abort("E{code}: Failed to update {partition} image.");'.format(
1560 device=self.device, partition=self.partition, code=code))
Dan Albert8b72aef2015-03-23 19:13:21 -07001561 script.AppendExtra(script.WordWrap(call))
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001562
Dan Albert8b72aef2015-03-23 19:13:21 -07001563 def _HashBlocks(self, source, ranges): # pylint: disable=no-self-use
Sami Tolvanendd67a292014-12-09 16:40:34 +00001564 data = source.ReadRangeSet(ranges)
1565 ctx = sha1()
1566
1567 for p in data:
1568 ctx.update(p)
1569
1570 return ctx.hexdigest()
1571
Tao Baoe9b61912015-07-09 17:37:49 -07001572 def _HashZeroBlocks(self, num_blocks): # pylint: disable=no-self-use
1573 """Return the hash value for all zero blocks."""
1574 zero_block = '\x00' * 4096
1575 ctx = sha1()
1576 for _ in range(num_blocks):
1577 ctx.update(zero_block)
1578
1579 return ctx.hexdigest()
1580
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001581
1582DataImage = blockimgdiff.DataImage
1583
Doug Zongker96a57e72010-09-26 14:57:41 -07001584# map recovery.fstab's fs_types to mount/format "partition types"
Dan Albert8b72aef2015-03-23 19:13:21 -07001585PARTITION_TYPES = {
1586 "yaffs2": "MTD",
1587 "mtd": "MTD",
1588 "ext4": "EMMC",
1589 "emmc": "EMMC",
Mohamad Ayyash95e74c12015-05-01 15:39:36 -07001590 "f2fs": "EMMC",
1591 "squashfs": "EMMC"
Dan Albert8b72aef2015-03-23 19:13:21 -07001592}
Doug Zongker96a57e72010-09-26 14:57:41 -07001593
1594def GetTypeAndDevice(mount_point, info):
1595 fstab = info["fstab"]
1596 if fstab:
Dan Albert8b72aef2015-03-23 19:13:21 -07001597 return (PARTITION_TYPES[fstab[mount_point].fs_type],
1598 fstab[mount_point].device)
Doug Zongker96a57e72010-09-26 14:57:41 -07001599 else:
Dan Albert8b72aef2015-03-23 19:13:21 -07001600 raise KeyError
Baligh Uddinbeb6afd2013-11-13 00:22:34 +00001601
1602
1603def ParseCertificate(data):
1604 """Parse a PEM-format certificate."""
1605 cert = []
1606 save = False
1607 for line in data.split("\n"):
1608 if "--END CERTIFICATE--" in line:
1609 break
1610 if save:
1611 cert.append(line)
1612 if "--BEGIN CERTIFICATE--" in line:
1613 save = True
1614 cert = "".join(cert).decode('base64')
1615 return cert
Doug Zongkerc9253822014-02-04 12:17:58 -08001616
Doug Zongker412c02f2014-02-13 10:58:24 -08001617def MakeRecoveryPatch(input_dir, output_sink, recovery_img, boot_img,
1618 info_dict=None):
Doug Zongkerc9253822014-02-04 12:17:58 -08001619 """Generate a binary patch that creates the recovery image starting
1620 with the boot image. (Most of the space in these images is just the
1621 kernel, which is identical for the two, so the resulting patch
1622 should be efficient.) Add it to the output zip, along with a shell
1623 script that is run from init.rc on first boot to actually do the
1624 patching and install the new recovery image.
1625
1626 recovery_img and boot_img should be File objects for the
1627 corresponding images. info should be the dictionary returned by
1628 common.LoadInfoDict() on the input target_files.
1629 """
1630
Doug Zongker412c02f2014-02-13 10:58:24 -08001631 if info_dict is None:
1632 info_dict = OPTIONS.info_dict
1633
Tao Baof2cffbd2015-07-22 12:33:18 -07001634 full_recovery_image = info_dict.get("full_recovery_image", None) == "true"
Tao Bao7a5bf8a2015-07-21 18:01:20 -07001635 system_root_image = info_dict.get("system_root_image", None) == "true"
Doug Zongkerc9253822014-02-04 12:17:58 -08001636
Tao Baof2cffbd2015-07-22 12:33:18 -07001637 if full_recovery_image:
1638 output_sink("etc/recovery.img", recovery_img.data)
1639
1640 else:
1641 diff_program = ["imgdiff"]
1642 path = os.path.join(input_dir, "SYSTEM", "etc", "recovery-resource.dat")
1643 if os.path.exists(path):
1644 diff_program.append("-b")
1645 diff_program.append(path)
1646 bonus_args = "-b /system/etc/recovery-resource.dat"
1647 else:
1648 bonus_args = ""
1649
1650 d = Difference(recovery_img, boot_img, diff_program=diff_program)
1651 _, _, patch = d.ComputePatch()
1652 output_sink("recovery-from-boot.p", patch)
Doug Zongkerc9253822014-02-04 12:17:58 -08001653
Dan Albertebb19aa2015-03-27 19:11:53 -07001654 try:
Tao Bao6f0b2192015-10-13 16:37:12 -07001655 # The following GetTypeAndDevice()s need to use the path in the target
1656 # info_dict instead of source_info_dict.
Dan Albertebb19aa2015-03-27 19:11:53 -07001657 boot_type, boot_device = GetTypeAndDevice("/boot", info_dict)
1658 recovery_type, recovery_device = GetTypeAndDevice("/recovery", info_dict)
1659 except KeyError:
Ying Wanga961a092014-07-29 11:42:37 -07001660 return
Doug Zongkerc9253822014-02-04 12:17:58 -08001661
Tao Baof2cffbd2015-07-22 12:33:18 -07001662 if full_recovery_image:
1663 sh = """#!/system/bin/sh
1664if ! applypatch -c %(type)s:%(device)s:%(size)d:%(sha1)s; then
1665 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"
1666else
1667 log -t recovery "Recovery image already installed"
1668fi
1669""" % {'type': recovery_type,
1670 'device': recovery_device,
1671 'sha1': recovery_img.sha1,
1672 'size': recovery_img.size}
1673 else:
1674 sh = """#!/system/bin/sh
Doug Zongkerc9253822014-02-04 12:17:58 -08001675if ! applypatch -c %(recovery_type)s:%(recovery_device)s:%(recovery_size)d:%(recovery_sha1)s; then
1676 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"
1677else
1678 log -t recovery "Recovery image already installed"
1679fi
Dan Albert8b72aef2015-03-23 19:13:21 -07001680""" % {'boot_size': boot_img.size,
1681 'boot_sha1': boot_img.sha1,
1682 'recovery_size': recovery_img.size,
1683 'recovery_sha1': recovery_img.sha1,
1684 'boot_type': boot_type,
1685 'boot_device': boot_device,
1686 'recovery_type': recovery_type,
1687 'recovery_device': recovery_device,
1688 'bonus_args': bonus_args}
Doug Zongkerc9253822014-02-04 12:17:58 -08001689
1690 # The install script location moved from /system/etc to /system/bin
Tao Bao9f0c8df2015-07-07 18:31:47 -07001691 # in the L release. Parse init.*.rc files to find out where the
Doug Zongkerc9253822014-02-04 12:17:58 -08001692 # target-files expects it to be, and put it there.
1693 sh_location = "etc/install-recovery.sh"
Tao Bao9f0c8df2015-07-07 18:31:47 -07001694 found = False
Tao Bao7a5bf8a2015-07-21 18:01:20 -07001695 if system_root_image:
1696 init_rc_dir = os.path.join(input_dir, "ROOT")
1697 else:
1698 init_rc_dir = os.path.join(input_dir, "BOOT", "RAMDISK")
Tao Bao9f0c8df2015-07-07 18:31:47 -07001699 init_rc_files = os.listdir(init_rc_dir)
1700 for init_rc_file in init_rc_files:
1701 if (not init_rc_file.startswith('init.') or
1702 not init_rc_file.endswith('.rc')):
1703 continue
1704
1705 with open(os.path.join(init_rc_dir, init_rc_file)) as f:
Doug Zongkerc9253822014-02-04 12:17:58 -08001706 for line in f:
Dan Albert8b72aef2015-03-23 19:13:21 -07001707 m = re.match(r"^service flash_recovery /system/(\S+)\s*$", line)
Doug Zongkerc9253822014-02-04 12:17:58 -08001708 if m:
1709 sh_location = m.group(1)
Tao Bao9f0c8df2015-07-07 18:31:47 -07001710 found = True
Doug Zongkerc9253822014-02-04 12:17:58 -08001711 break
Tao Bao9f0c8df2015-07-07 18:31:47 -07001712
1713 if found:
1714 break
1715
1716 print "putting script in", sh_location
Doug Zongkerc9253822014-02-04 12:17:58 -08001717
1718 output_sink(sh_location, sh)