blob: b5c0114068582678ec0cdb089b7a8f1c23f5ce08 [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.
49 self.java_args = "-Xmx2048m" # JVM Args
50 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()
500 cmd = [info_dict["vboot_signer_cmd"], info_dict["futility"],
501 img_unsigned.name, info_dict["vboot_key"] + ".vbpubk",
Furquan Shaikh852b8de2015-08-10 11:43:45 -0700502 info_dict["vboot_key"] + ".vbprivk",
503 info_dict["vboot_subkey"] + ".vbprivk",
504 img_keyblock.name,
Tao Baod95e9fd2015-03-29 23:07:41 -0700505 img.name]
506 p = Run(cmd, stdout=subprocess.PIPE)
507 p.communicate()
508 assert p.returncode == 0, "vboot_signer of %s image failed" % path
509
Tao Baof3282b42015-04-01 11:21:55 -0700510 # Clean up the temp files.
511 img_unsigned.close()
512 img_keyblock.close()
513
Doug Zongkereef39442009-04-02 12:14:19 -0700514 img.seek(os.SEEK_SET, 0)
515 data = img.read()
516
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700517 if has_ramdisk:
518 ramdisk_img.close()
Doug Zongkereef39442009-04-02 12:14:19 -0700519 img.close()
520
521 return data
522
523
Doug Zongkerd5131602012-08-02 14:46:42 -0700524def GetBootableImage(name, prebuilt_name, unpack_dir, tree_subdir,
525 info_dict=None):
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700526 """Return a File object with the desired bootable image.
527
528 Look for it in 'unpack_dir'/BOOTABLE_IMAGES under the name 'prebuilt_name',
529 otherwise look for it under 'unpack_dir'/IMAGES, otherwise construct it from
530 the source files in 'unpack_dir'/'tree_subdir'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700531
Doug Zongker55d93282011-01-25 17:03:34 -0800532 prebuilt_path = os.path.join(unpack_dir, "BOOTABLE_IMAGES", prebuilt_name)
533 if os.path.exists(prebuilt_path):
Doug Zongker6f1d0312014-08-22 08:07:12 -0700534 print "using prebuilt %s from BOOTABLE_IMAGES..." % (prebuilt_name,)
Doug Zongker55d93282011-01-25 17:03:34 -0800535 return File.FromLocalFile(name, prebuilt_path)
Doug Zongker6f1d0312014-08-22 08:07:12 -0700536
537 prebuilt_path = os.path.join(unpack_dir, "IMAGES", prebuilt_name)
538 if os.path.exists(prebuilt_path):
539 print "using prebuilt %s from IMAGES..." % (prebuilt_name,)
540 return File.FromLocalFile(name, prebuilt_path)
541
542 print "building image from target_files %s..." % (tree_subdir,)
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700543
544 if info_dict is None:
545 info_dict = OPTIONS.info_dict
546
547 # With system_root_image == "true", we don't pack ramdisk into the boot image.
Daniel Rosenberg40ef35b2015-11-10 19:21:34 -0800548 # Unless "recovery_as_boot" is specified, in which case we carry the ramdisk
549 # for recovery.
550 has_ramdisk = (info_dict.get("system_root_image") != "true" or
551 prebuilt_name != "boot.img" or
552 info_dict.get("recovery_as_boot") == "true")
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700553
Doug Zongker6f1d0312014-08-22 08:07:12 -0700554 fs_config = "META/" + tree_subdir.lower() + "_filesystem_config.txt"
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700555 data = _BuildBootableImage(os.path.join(unpack_dir, tree_subdir),
556 os.path.join(unpack_dir, fs_config),
557 info_dict, has_ramdisk)
Doug Zongker6f1d0312014-08-22 08:07:12 -0700558 if data:
559 return File(name, data)
560 return None
Doug Zongker55d93282011-01-25 17:03:34 -0800561
Doug Zongkereef39442009-04-02 12:14:19 -0700562
Doug Zongker75f17362009-12-08 13:46:44 -0800563def UnzipTemp(filename, pattern=None):
Doug Zongker55d93282011-01-25 17:03:34 -0800564 """Unzip the given archive into a temporary directory and return the name.
565
566 If filename is of the form "foo.zip+bar.zip", unzip foo.zip into a
567 temp dir, then unzip bar.zip into that_dir/BOOTABLE_IMAGES.
568
569 Returns (tempdir, zipobj) where zipobj is a zipfile.ZipFile (of the
570 main file), open for reading.
571 """
Doug Zongkereef39442009-04-02 12:14:19 -0700572
573 tmp = tempfile.mkdtemp(prefix="targetfiles-")
574 OPTIONS.tempfiles.append(tmp)
Doug Zongker55d93282011-01-25 17:03:34 -0800575
576 def unzip_to_dir(filename, dirname):
577 cmd = ["unzip", "-o", "-q", filename, "-d", dirname]
578 if pattern is not None:
579 cmd.append(pattern)
580 p = Run(cmd, stdout=subprocess.PIPE)
581 p.communicate()
582 if p.returncode != 0:
583 raise ExternalError("failed to unzip input target-files \"%s\"" %
584 (filename,))
585
586 m = re.match(r"^(.*[.]zip)\+(.*[.]zip)$", filename, re.IGNORECASE)
587 if m:
588 unzip_to_dir(m.group(1), tmp)
589 unzip_to_dir(m.group(2), os.path.join(tmp, "BOOTABLE_IMAGES"))
590 filename = m.group(1)
591 else:
592 unzip_to_dir(filename, tmp)
593
594 return tmp, zipfile.ZipFile(filename, "r")
Doug Zongkereef39442009-04-02 12:14:19 -0700595
596
597def GetKeyPasswords(keylist):
598 """Given a list of keys, prompt the user to enter passwords for
599 those which require them. Return a {key: password} dict. password
600 will be None if the key has no password."""
601
Doug Zongker8ce7c252009-05-22 13:34:54 -0700602 no_passwords = []
603 need_passwords = []
T.R. Fullhart37e10522013-03-18 10:31:26 -0700604 key_passwords = {}
Doug Zongkereef39442009-04-02 12:14:19 -0700605 devnull = open("/dev/null", "w+b")
606 for k in sorted(keylist):
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800607 # We don't need a password for things that aren't really keys.
608 if k in SPECIAL_CERT_STRINGS:
Doug Zongker8ce7c252009-05-22 13:34:54 -0700609 no_passwords.append(k)
Doug Zongker43874f82009-04-14 14:05:15 -0700610 continue
611
T.R. Fullhart37e10522013-03-18 10:31:26 -0700612 p = Run(["openssl", "pkcs8", "-in", k+OPTIONS.private_key_suffix,
Doug Zongker602a84e2009-06-18 08:35:12 -0700613 "-inform", "DER", "-nocrypt"],
614 stdin=devnull.fileno(),
615 stdout=devnull.fileno(),
616 stderr=subprocess.STDOUT)
Doug Zongkereef39442009-04-02 12:14:19 -0700617 p.communicate()
618 if p.returncode == 0:
T.R. Fullhart37e10522013-03-18 10:31:26 -0700619 # Definitely an unencrypted key.
Doug Zongker8ce7c252009-05-22 13:34:54 -0700620 no_passwords.append(k)
Doug Zongkereef39442009-04-02 12:14:19 -0700621 else:
T.R. Fullhart37e10522013-03-18 10:31:26 -0700622 p = Run(["openssl", "pkcs8", "-in", k+OPTIONS.private_key_suffix,
623 "-inform", "DER", "-passin", "pass:"],
624 stdin=devnull.fileno(),
625 stdout=devnull.fileno(),
626 stderr=subprocess.PIPE)
Dan Albert8b72aef2015-03-23 19:13:21 -0700627 _, stderr = p.communicate()
T.R. Fullhart37e10522013-03-18 10:31:26 -0700628 if p.returncode == 0:
629 # Encrypted key with empty string as password.
630 key_passwords[k] = ''
631 elif stderr.startswith('Error decrypting key'):
632 # Definitely encrypted key.
633 # It would have said "Error reading key" if it didn't parse correctly.
634 need_passwords.append(k)
635 else:
636 # Potentially, a type of key that openssl doesn't understand.
637 # We'll let the routines in signapk.jar handle it.
638 no_passwords.append(k)
Doug Zongkereef39442009-04-02 12:14:19 -0700639 devnull.close()
Doug Zongker8ce7c252009-05-22 13:34:54 -0700640
T.R. Fullhart37e10522013-03-18 10:31:26 -0700641 key_passwords.update(PasswordManager().GetPasswords(need_passwords))
Doug Zongker8ce7c252009-05-22 13:34:54 -0700642 key_passwords.update(dict.fromkeys(no_passwords, None))
Doug Zongkereef39442009-04-02 12:14:19 -0700643 return key_passwords
644
645
Alex Klyubinb05b62d2016-01-13 10:32:47 -0800646def GetMinSdkVersion(apk_name):
647 """Get the minSdkVersion delared in the APK. This can be both a decimal number
648 (API Level) or a codename.
649 """
650
651 p = Run(["aapt", "dump", "badging", apk_name], stdout=subprocess.PIPE)
652 output, err = p.communicate()
653 if err:
654 raise ExternalError("Failed to obtain minSdkVersion: aapt return code %s"
655 % (p.returncode,))
656
657 for line in output.split("\n"):
658 # Looking for lines such as sdkVersion:'23' or sdkVersion:'M'
659 m = re.match(r'sdkVersion:\'([^\']*)\'', line)
660 if m:
661 return m.group(1)
662 raise ExternalError("No minSdkVersion returned by aapt")
663
664
665def GetMinSdkVersionInt(apk_name, codename_to_api_level_map):
666 """Get the minSdkVersion declared in the APK as a number (API Level). If
667 minSdkVersion is set to a codename, it is translated to a number using the
668 provided map.
669 """
670
671 version = GetMinSdkVersion(apk_name)
672 try:
673 return int(version)
674 except ValueError:
675 # Not a decimal number. Codename?
676 if version in codename_to_api_level_map:
677 return codename_to_api_level_map[version]
678 else:
679 raise ExternalError("Unknown minSdkVersion: '%s'. Known codenames: %s"
680 % (version, codename_to_api_level_map))
681
682
683def SignFile(input_name, output_name, key, password, min_api_level=None,
684 codename_to_api_level_map=dict(),
685 whole_file=False):
Doug Zongkereef39442009-04-02 12:14:19 -0700686 """Sign the input_name zip/jar/apk, producing output_name. Use the
687 given key and password (the latter may be None if the key does not
688 have a password.
689
Doug Zongker951495f2009-08-14 12:44:19 -0700690 If whole_file is true, use the "-w" option to SignApk to embed a
691 signature that covers the whole file in the archive comment of the
692 zip file.
Alex Klyubinb05b62d2016-01-13 10:32:47 -0800693
694 min_api_level is the API Level (int) of the oldest platform this file may end
695 up on. If not specified for an APK, the API Level is obtained by interpreting
696 the minSdkVersion attribute of the APK's AndroidManifest.xml.
697
698 codename_to_api_level_map is needed to translate the codename which may be
699 encountered as the APK's minSdkVersion.
Doug Zongkereef39442009-04-02 12:14:19 -0700700 """
Doug Zongker951495f2009-08-14 12:44:19 -0700701
Alex Klyubin9667b182015-12-10 13:38:50 -0800702 java_library_path = os.path.join(
703 OPTIONS.search_path, OPTIONS.signapk_shared_library_path)
704
705 cmd = [OPTIONS.java_path, OPTIONS.java_args,
706 "-Djava.library.path=" + java_library_path,
707 "-jar",
T.R. Fullhart37e10522013-03-18 10:31:26 -0700708 os.path.join(OPTIONS.search_path, OPTIONS.signapk_path)]
709 cmd.extend(OPTIONS.extra_signapk_args)
Doug Zongker951495f2009-08-14 12:44:19 -0700710 if whole_file:
711 cmd.append("-w")
Alex Klyubinb05b62d2016-01-13 10:32:47 -0800712
713 min_sdk_version = min_api_level
714 if min_sdk_version is None:
715 if not whole_file:
716 min_sdk_version = GetMinSdkVersionInt(
717 input_name, codename_to_api_level_map)
718 if min_sdk_version is not None:
719 cmd.extend(["--min-sdk-version", str(min_sdk_version)])
720
T.R. Fullhart37e10522013-03-18 10:31:26 -0700721 cmd.extend([key + OPTIONS.public_key_suffix,
722 key + OPTIONS.private_key_suffix,
Alex Klyubineb756d72015-12-04 09:21:08 -0800723 input_name, output_name])
Doug Zongker951495f2009-08-14 12:44:19 -0700724
725 p = Run(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
Doug Zongkereef39442009-04-02 12:14:19 -0700726 if password is not None:
727 password += "\n"
728 p.communicate(password)
729 if p.returncode != 0:
730 raise ExternalError("signapk.jar failed: return code %s" % (p.returncode,))
731
Doug Zongkereef39442009-04-02 12:14:19 -0700732
Doug Zongker37974732010-09-16 17:44:38 -0700733def CheckSize(data, target, info_dict):
Doug Zongkereef39442009-04-02 12:14:19 -0700734 """Check the data string passed against the max size limit, if
735 any, for the given target. Raise exception if the data is too big.
736 Print a warning if the data is nearing the maximum size."""
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700737
Dan Albert8b72aef2015-03-23 19:13:21 -0700738 if target.endswith(".img"):
739 target = target[:-4]
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700740 mount_point = "/" + target
741
Ying Wangf8824af2014-06-03 14:07:27 -0700742 fs_type = None
743 limit = None
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700744 if info_dict["fstab"]:
Dan Albert8b72aef2015-03-23 19:13:21 -0700745 if mount_point == "/userdata":
746 mount_point = "/data"
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700747 p = info_dict["fstab"][mount_point]
748 fs_type = p.fs_type
Andrew Boie0f9aec82012-02-14 09:32:52 -0800749 device = p.device
750 if "/" in device:
751 device = device[device.rfind("/")+1:]
752 limit = info_dict.get(device + "_size", None)
Dan Albert8b72aef2015-03-23 19:13:21 -0700753 if not fs_type or not limit:
754 return
Doug Zongkereef39442009-04-02 12:14:19 -0700755
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700756 if fs_type == "yaffs2":
757 # image size should be increased by 1/64th to account for the
758 # spare area (64 bytes per 2k page)
759 limit = limit / 2048 * (2048+64)
Andrew Boie0f9aec82012-02-14 09:32:52 -0800760 size = len(data)
761 pct = float(size) * 100.0 / limit
762 msg = "%s size (%d) is %.2f%% of limit (%d)" % (target, size, pct, limit)
763 if pct >= 99.0:
764 raise ExternalError(msg)
765 elif pct >= 95.0:
766 print
767 print " WARNING: ", msg
768 print
769 elif OPTIONS.verbose:
770 print " ", msg
Doug Zongkereef39442009-04-02 12:14:19 -0700771
772
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800773def ReadApkCerts(tf_zip):
774 """Given a target_files ZipFile, parse the META/apkcerts.txt file
775 and return a {package: cert} dict."""
776 certmap = {}
777 for line in tf_zip.read("META/apkcerts.txt").split("\n"):
778 line = line.strip()
Dan Albert8b72aef2015-03-23 19:13:21 -0700779 if not line:
780 continue
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800781 m = re.match(r'^name="(.*)"\s+certificate="(.*)"\s+'
782 r'private_key="(.*)"$', line)
783 if m:
784 name, cert, privkey = m.groups()
T.R. Fullhart37e10522013-03-18 10:31:26 -0700785 public_key_suffix_len = len(OPTIONS.public_key_suffix)
786 private_key_suffix_len = len(OPTIONS.private_key_suffix)
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800787 if cert in SPECIAL_CERT_STRINGS and not privkey:
788 certmap[name] = cert
T.R. Fullhart37e10522013-03-18 10:31:26 -0700789 elif (cert.endswith(OPTIONS.public_key_suffix) and
790 privkey.endswith(OPTIONS.private_key_suffix) and
791 cert[:-public_key_suffix_len] == privkey[:-private_key_suffix_len]):
792 certmap[name] = cert[:-public_key_suffix_len]
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800793 else:
794 raise ValueError("failed to parse line from apkcerts.txt:\n" + line)
795 return certmap
796
797
Doug Zongkereef39442009-04-02 12:14:19 -0700798COMMON_DOCSTRING = """
799 -p (--path) <dir>
Doug Zongker602a84e2009-06-18 08:35:12 -0700800 Prepend <dir>/bin to the list of places to search for binaries
801 run by this script, and expect to find jars in <dir>/framework.
Doug Zongkereef39442009-04-02 12:14:19 -0700802
Doug Zongker05d3dea2009-06-22 11:32:31 -0700803 -s (--device_specific) <file>
804 Path to the python module containing device-specific
805 releasetools code.
806
Doug Zongker8bec09e2009-11-30 15:37:14 -0800807 -x (--extra) <key=value>
808 Add a key/value pair to the 'extras' dict, which device-specific
809 extension code may look at.
810
Doug Zongkereef39442009-04-02 12:14:19 -0700811 -v (--verbose)
812 Show command lines being executed.
813
814 -h (--help)
815 Display this usage message and exit.
816"""
817
818def Usage(docstring):
819 print docstring.rstrip("\n")
820 print COMMON_DOCSTRING
821
822
823def ParseOptions(argv,
824 docstring,
825 extra_opts="", extra_long_opts=(),
826 extra_option_handler=None):
827 """Parse the options in argv and return any arguments that aren't
828 flags. docstring is the calling module's docstring, to be displayed
829 for errors and -h. extra_opts and extra_long_opts are for flags
830 defined by the caller, which are processed by passing them to
831 extra_option_handler."""
832
833 try:
834 opts, args = getopt.getopt(
Doug Zongker8bec09e2009-11-30 15:37:14 -0800835 argv, "hvp:s:x:" + extra_opts,
Alex Klyubin9667b182015-12-10 13:38:50 -0800836 ["help", "verbose", "path=", "signapk_path=",
837 "signapk_shared_library_path=", "extra_signapk_args=",
Baligh Uddinbdc2e312014-09-05 17:36:20 -0700838 "java_path=", "java_args=", "public_key_suffix=",
Baligh Uddin601ddea2015-06-09 15:48:14 -0700839 "private_key_suffix=", "boot_signer_path=", "boot_signer_args=",
840 "verity_signer_path=", "verity_signer_args=", "device_specific=",
Baligh Uddine2048682014-11-20 09:52:05 -0800841 "extra="] +
T.R. Fullhart37e10522013-03-18 10:31:26 -0700842 list(extra_long_opts))
Dan Albert8b72aef2015-03-23 19:13:21 -0700843 except getopt.GetoptError as err:
Doug Zongkereef39442009-04-02 12:14:19 -0700844 Usage(docstring)
845 print "**", str(err), "**"
846 sys.exit(2)
847
Doug Zongkereef39442009-04-02 12:14:19 -0700848 for o, a in opts:
849 if o in ("-h", "--help"):
850 Usage(docstring)
851 sys.exit()
852 elif o in ("-v", "--verbose"):
853 OPTIONS.verbose = True
854 elif o in ("-p", "--path"):
Doug Zongker602a84e2009-06-18 08:35:12 -0700855 OPTIONS.search_path = a
T.R. Fullhart37e10522013-03-18 10:31:26 -0700856 elif o in ("--signapk_path",):
857 OPTIONS.signapk_path = a
Alex Klyubin9667b182015-12-10 13:38:50 -0800858 elif o in ("--signapk_shared_library_path",):
859 OPTIONS.signapk_shared_library_path = a
T.R. Fullhart37e10522013-03-18 10:31:26 -0700860 elif o in ("--extra_signapk_args",):
861 OPTIONS.extra_signapk_args = shlex.split(a)
862 elif o in ("--java_path",):
863 OPTIONS.java_path = a
Baligh Uddin339ee492014-09-05 11:18:07 -0700864 elif o in ("--java_args",):
865 OPTIONS.java_args = a
T.R. Fullhart37e10522013-03-18 10:31:26 -0700866 elif o in ("--public_key_suffix",):
867 OPTIONS.public_key_suffix = a
868 elif o in ("--private_key_suffix",):
869 OPTIONS.private_key_suffix = a
Baligh Uddine2048682014-11-20 09:52:05 -0800870 elif o in ("--boot_signer_path",):
871 OPTIONS.boot_signer_path = a
Baligh Uddin601ddea2015-06-09 15:48:14 -0700872 elif o in ("--boot_signer_args",):
873 OPTIONS.boot_signer_args = shlex.split(a)
874 elif o in ("--verity_signer_path",):
875 OPTIONS.verity_signer_path = a
876 elif o in ("--verity_signer_args",):
877 OPTIONS.verity_signer_args = shlex.split(a)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700878 elif o in ("-s", "--device_specific"):
879 OPTIONS.device_specific = a
Doug Zongker5ecba702009-12-03 16:36:20 -0800880 elif o in ("-x", "--extra"):
Doug Zongker8bec09e2009-11-30 15:37:14 -0800881 key, value = a.split("=", 1)
882 OPTIONS.extras[key] = value
Doug Zongkereef39442009-04-02 12:14:19 -0700883 else:
884 if extra_option_handler is None or not extra_option_handler(o, a):
885 assert False, "unknown option \"%s\"" % (o,)
886
Doug Zongker85448772014-09-09 14:59:20 -0700887 if OPTIONS.search_path:
888 os.environ["PATH"] = (os.path.join(OPTIONS.search_path, "bin") +
889 os.pathsep + os.environ["PATH"])
Doug Zongkereef39442009-04-02 12:14:19 -0700890
891 return args
892
893
Doug Zongkerfc44a512014-08-26 13:10:25 -0700894def MakeTempFile(prefix=None, suffix=None):
895 """Make a temp file and add it to the list of things to be deleted
896 when Cleanup() is called. Return the filename."""
897 fd, fn = tempfile.mkstemp(prefix=prefix, suffix=suffix)
898 os.close(fd)
899 OPTIONS.tempfiles.append(fn)
900 return fn
901
902
Doug Zongkereef39442009-04-02 12:14:19 -0700903def Cleanup():
904 for i in OPTIONS.tempfiles:
905 if os.path.isdir(i):
906 shutil.rmtree(i)
907 else:
908 os.remove(i)
Doug Zongker8ce7c252009-05-22 13:34:54 -0700909
910
911class PasswordManager(object):
912 def __init__(self):
913 self.editor = os.getenv("EDITOR", None)
914 self.pwfile = os.getenv("ANDROID_PW_FILE", None)
915
916 def GetPasswords(self, items):
917 """Get passwords corresponding to each string in 'items',
918 returning a dict. (The dict may have keys in addition to the
919 values in 'items'.)
920
921 Uses the passwords in $ANDROID_PW_FILE if available, letting the
922 user edit that file to add more needed passwords. If no editor is
923 available, or $ANDROID_PW_FILE isn't define, prompts the user
924 interactively in the ordinary way.
925 """
926
927 current = self.ReadFile()
928
929 first = True
930 while True:
931 missing = []
932 for i in items:
933 if i not in current or not current[i]:
934 missing.append(i)
935 # Are all the passwords already in the file?
Dan Albert8b72aef2015-03-23 19:13:21 -0700936 if not missing:
937 return current
Doug Zongker8ce7c252009-05-22 13:34:54 -0700938
939 for i in missing:
940 current[i] = ""
941
942 if not first:
943 print "key file %s still missing some passwords." % (self.pwfile,)
944 answer = raw_input("try to edit again? [y]> ").strip()
945 if answer and answer[0] not in 'yY':
946 raise RuntimeError("key passwords unavailable")
947 first = False
948
949 current = self.UpdateAndReadFile(current)
950
Dan Albert8b72aef2015-03-23 19:13:21 -0700951 def PromptResult(self, current): # pylint: disable=no-self-use
Doug Zongker8ce7c252009-05-22 13:34:54 -0700952 """Prompt the user to enter a value (password) for each key in
953 'current' whose value is fales. Returns a new dict with all the
954 values.
955 """
956 result = {}
957 for k, v in sorted(current.iteritems()):
958 if v:
959 result[k] = v
960 else:
961 while True:
Dan Albert8b72aef2015-03-23 19:13:21 -0700962 result[k] = getpass.getpass(
963 "Enter password for %s key> " % k).strip()
964 if result[k]:
965 break
Doug Zongker8ce7c252009-05-22 13:34:54 -0700966 return result
967
968 def UpdateAndReadFile(self, current):
969 if not self.editor or not self.pwfile:
970 return self.PromptResult(current)
971
972 f = open(self.pwfile, "w")
Dan Albert8b72aef2015-03-23 19:13:21 -0700973 os.chmod(self.pwfile, 0o600)
Doug Zongker8ce7c252009-05-22 13:34:54 -0700974 f.write("# Enter key passwords between the [[[ ]]] brackets.\n")
975 f.write("# (Additional spaces are harmless.)\n\n")
976
977 first_line = None
Dan Albert8b72aef2015-03-23 19:13:21 -0700978 sorted_list = sorted([(not v, k, v) for (k, v) in current.iteritems()])
979 for i, (_, k, v) in enumerate(sorted_list):
Doug Zongker8ce7c252009-05-22 13:34:54 -0700980 f.write("[[[ %s ]]] %s\n" % (v, k))
981 if not v and first_line is None:
982 # position cursor on first line with no password.
983 first_line = i + 4
984 f.close()
985
986 p = Run([self.editor, "+%d" % (first_line,), self.pwfile])
987 _, _ = p.communicate()
988
989 return self.ReadFile()
990
991 def ReadFile(self):
992 result = {}
Dan Albert8b72aef2015-03-23 19:13:21 -0700993 if self.pwfile is None:
994 return result
Doug Zongker8ce7c252009-05-22 13:34:54 -0700995 try:
996 f = open(self.pwfile, "r")
997 for line in f:
998 line = line.strip()
Dan Albert8b72aef2015-03-23 19:13:21 -0700999 if not line or line[0] == '#':
1000 continue
Doug Zongker8ce7c252009-05-22 13:34:54 -07001001 m = re.match(r"^\[\[\[\s*(.*?)\s*\]\]\]\s*(\S+)$", line)
1002 if not m:
1003 print "failed to parse password file: ", line
1004 else:
1005 result[m.group(2)] = m.group(1)
1006 f.close()
Dan Albert8b72aef2015-03-23 19:13:21 -07001007 except IOError as e:
Doug Zongker8ce7c252009-05-22 13:34:54 -07001008 if e.errno != errno.ENOENT:
1009 print "error reading password file: ", str(e)
1010 return result
Doug Zongker048e7ca2009-06-15 14:31:53 -07001011
1012
Dan Albert8e0178d2015-01-27 15:53:15 -08001013def ZipWrite(zip_file, filename, arcname=None, perms=0o644,
1014 compress_type=None):
1015 import datetime
1016
1017 # http://b/18015246
1018 # Python 2.7's zipfile implementation wrongly thinks that zip64 is required
1019 # for files larger than 2GiB. We can work around this by adjusting their
1020 # limit. Note that `zipfile.writestr()` will not work for strings larger than
1021 # 2GiB. The Python interpreter sometimes rejects strings that large (though
1022 # it isn't clear to me exactly what circumstances cause this).
1023 # `zipfile.write()` must be used directly to work around this.
1024 #
1025 # This mess can be avoided if we port to python3.
1026 saved_zip64_limit = zipfile.ZIP64_LIMIT
1027 zipfile.ZIP64_LIMIT = (1 << 32) - 1
1028
1029 if compress_type is None:
1030 compress_type = zip_file.compression
1031 if arcname is None:
1032 arcname = filename
1033
1034 saved_stat = os.stat(filename)
1035
1036 try:
1037 # `zipfile.write()` doesn't allow us to pass ZipInfo, so just modify the
1038 # file to be zipped and reset it when we're done.
1039 os.chmod(filename, perms)
1040
1041 # Use a fixed timestamp so the output is repeatable.
1042 epoch = datetime.datetime.fromtimestamp(0)
1043 timestamp = (datetime.datetime(2009, 1, 1) - epoch).total_seconds()
1044 os.utime(filename, (timestamp, timestamp))
1045
1046 zip_file.write(filename, arcname=arcname, compress_type=compress_type)
1047 finally:
1048 os.chmod(filename, saved_stat.st_mode)
1049 os.utime(filename, (saved_stat.st_atime, saved_stat.st_mtime))
1050 zipfile.ZIP64_LIMIT = saved_zip64_limit
1051
1052
Tao Bao58c1b962015-05-20 09:32:18 -07001053def ZipWriteStr(zip_file, zinfo_or_arcname, data, perms=None,
Tao Baof3282b42015-04-01 11:21:55 -07001054 compress_type=None):
1055 """Wrap zipfile.writestr() function to work around the zip64 limit.
1056
1057 Even with the ZIP64_LIMIT workaround, it won't allow writing a string
1058 longer than 2GiB. It gives 'OverflowError: size does not fit in an int'
1059 when calling crc32(bytes).
1060
1061 But it still works fine to write a shorter string into a large zip file.
1062 We should use ZipWrite() whenever possible, and only use ZipWriteStr()
1063 when we know the string won't be too long.
1064 """
1065
1066 saved_zip64_limit = zipfile.ZIP64_LIMIT
1067 zipfile.ZIP64_LIMIT = (1 << 32) - 1
1068
1069 if not isinstance(zinfo_or_arcname, zipfile.ZipInfo):
1070 zinfo = zipfile.ZipInfo(filename=zinfo_or_arcname)
Dan Albert8b72aef2015-03-23 19:13:21 -07001071 zinfo.compress_type = zip_file.compression
Tao Bao58c1b962015-05-20 09:32:18 -07001072 if perms is None:
Tao Bao2a410582015-07-10 17:18:23 -07001073 perms = 0o100644
Geremy Condra36bd3652014-02-06 19:45:10 -08001074 else:
Tao Baof3282b42015-04-01 11:21:55 -07001075 zinfo = zinfo_or_arcname
1076
1077 # If compress_type is given, it overrides the value in zinfo.
1078 if compress_type is not None:
1079 zinfo.compress_type = compress_type
1080
Tao Bao58c1b962015-05-20 09:32:18 -07001081 # If perms is given, it has a priority.
1082 if perms is not None:
Tao Bao2a410582015-07-10 17:18:23 -07001083 # If perms doesn't set the file type, mark it as a regular file.
1084 if perms & 0o770000 == 0:
1085 perms |= 0o100000
Tao Bao58c1b962015-05-20 09:32:18 -07001086 zinfo.external_attr = perms << 16
1087
Tao Baof3282b42015-04-01 11:21:55 -07001088 # Use a fixed timestamp so the output is repeatable.
Tao Baof3282b42015-04-01 11:21:55 -07001089 zinfo.date_time = (2009, 1, 1, 0, 0, 0)
1090
Dan Albert8b72aef2015-03-23 19:13:21 -07001091 zip_file.writestr(zinfo, data)
Tao Baof3282b42015-04-01 11:21:55 -07001092 zipfile.ZIP64_LIMIT = saved_zip64_limit
1093
1094
1095def ZipClose(zip_file):
1096 # http://b/18015246
1097 # zipfile also refers to ZIP64_LIMIT during close() when it writes out the
1098 # central directory.
1099 saved_zip64_limit = zipfile.ZIP64_LIMIT
1100 zipfile.ZIP64_LIMIT = (1 << 32) - 1
1101
1102 zip_file.close()
1103
1104 zipfile.ZIP64_LIMIT = saved_zip64_limit
Doug Zongker05d3dea2009-06-22 11:32:31 -07001105
1106
1107class DeviceSpecificParams(object):
1108 module = None
1109 def __init__(self, **kwargs):
1110 """Keyword arguments to the constructor become attributes of this
1111 object, which is passed to all functions in the device-specific
1112 module."""
1113 for k, v in kwargs.iteritems():
1114 setattr(self, k, v)
Doug Zongker8bec09e2009-11-30 15:37:14 -08001115 self.extras = OPTIONS.extras
Doug Zongker05d3dea2009-06-22 11:32:31 -07001116
1117 if self.module is None:
1118 path = OPTIONS.device_specific
Dan Albert8b72aef2015-03-23 19:13:21 -07001119 if not path:
1120 return
Doug Zongker8e2f2b92009-06-24 14:34:57 -07001121 try:
1122 if os.path.isdir(path):
1123 info = imp.find_module("releasetools", [path])
1124 else:
1125 d, f = os.path.split(path)
1126 b, x = os.path.splitext(f)
1127 if x == ".py":
1128 f = b
1129 info = imp.find_module(f, [d])
Doug Zongkereb0a78a2014-01-27 10:01:06 -08001130 print "loaded device-specific extensions from", path
Doug Zongker8e2f2b92009-06-24 14:34:57 -07001131 self.module = imp.load_module("device_specific", *info)
1132 except ImportError:
1133 print "unable to load device-specific module; assuming none"
Doug Zongker05d3dea2009-06-22 11:32:31 -07001134
1135 def _DoCall(self, function_name, *args, **kwargs):
1136 """Call the named function in the device-specific module, passing
1137 the given args and kwargs. The first argument to the call will be
1138 the DeviceSpecific object itself. If there is no module, or the
1139 module does not define the function, return the value of the
1140 'default' kwarg (which itself defaults to None)."""
1141 if self.module is None or not hasattr(self.module, function_name):
1142 return kwargs.get("default", None)
1143 return getattr(self.module, function_name)(*((self,) + args), **kwargs)
1144
1145 def FullOTA_Assertions(self):
1146 """Called after emitting the block of assertions at the top of a
1147 full OTA package. Implementations can add whatever additional
1148 assertions they like."""
1149 return self._DoCall("FullOTA_Assertions")
1150
Doug Zongkere5ff5902012-01-17 10:55:37 -08001151 def FullOTA_InstallBegin(self):
1152 """Called at the start of full OTA installation."""
1153 return self._DoCall("FullOTA_InstallBegin")
1154
Doug Zongker05d3dea2009-06-22 11:32:31 -07001155 def FullOTA_InstallEnd(self):
1156 """Called at the end of full OTA installation; typically this is
1157 used to install the image for the device's baseband processor."""
1158 return self._DoCall("FullOTA_InstallEnd")
1159
1160 def IncrementalOTA_Assertions(self):
1161 """Called after emitting the block of assertions at the top of an
1162 incremental OTA package. Implementations can add whatever
1163 additional assertions they like."""
1164 return self._DoCall("IncrementalOTA_Assertions")
1165
Doug Zongkere5ff5902012-01-17 10:55:37 -08001166 def IncrementalOTA_VerifyBegin(self):
1167 """Called at the start of the verification phase of incremental
1168 OTA installation; additional checks can be placed here to abort
1169 the script before any changes are made."""
1170 return self._DoCall("IncrementalOTA_VerifyBegin")
1171
Doug Zongker05d3dea2009-06-22 11:32:31 -07001172 def IncrementalOTA_VerifyEnd(self):
1173 """Called at the end of the verification phase of incremental OTA
1174 installation; additional checks can be placed here to abort the
1175 script before any changes are made."""
1176 return self._DoCall("IncrementalOTA_VerifyEnd")
1177
Doug Zongkere5ff5902012-01-17 10:55:37 -08001178 def IncrementalOTA_InstallBegin(self):
1179 """Called at the start of incremental OTA installation (after
1180 verification is complete)."""
1181 return self._DoCall("IncrementalOTA_InstallBegin")
1182
Doug Zongker05d3dea2009-06-22 11:32:31 -07001183 def IncrementalOTA_InstallEnd(self):
1184 """Called at the end of incremental OTA installation; typically
1185 this is used to install the image for the device's baseband
1186 processor."""
1187 return self._DoCall("IncrementalOTA_InstallEnd")
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001188
Tao Bao9bc6bb22015-11-09 16:58:28 -08001189 def VerifyOTA_Assertions(self):
1190 return self._DoCall("VerifyOTA_Assertions")
1191
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001192class File(object):
1193 def __init__(self, name, data):
1194 self.name = name
1195 self.data = data
1196 self.size = len(data)
Doug Zongker55d93282011-01-25 17:03:34 -08001197 self.sha1 = sha1(data).hexdigest()
1198
1199 @classmethod
1200 def FromLocalFile(cls, name, diskname):
1201 f = open(diskname, "rb")
1202 data = f.read()
1203 f.close()
1204 return File(name, data)
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001205
1206 def WriteToTemp(self):
1207 t = tempfile.NamedTemporaryFile()
1208 t.write(self.data)
1209 t.flush()
1210 return t
1211
Geremy Condra36bd3652014-02-06 19:45:10 -08001212 def AddToZip(self, z, compression=None):
Tao Baof3282b42015-04-01 11:21:55 -07001213 ZipWriteStr(z, self.name, self.data, compress_type=compression)
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001214
1215DIFF_PROGRAM_BY_EXT = {
1216 ".gz" : "imgdiff",
1217 ".zip" : ["imgdiff", "-z"],
1218 ".jar" : ["imgdiff", "-z"],
1219 ".apk" : ["imgdiff", "-z"],
1220 ".img" : "imgdiff",
1221 }
1222
1223class Difference(object):
Doug Zongker24cd2802012-08-14 16:36:15 -07001224 def __init__(self, tf, sf, diff_program=None):
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001225 self.tf = tf
1226 self.sf = sf
1227 self.patch = None
Doug Zongker24cd2802012-08-14 16:36:15 -07001228 self.diff_program = diff_program
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001229
1230 def ComputePatch(self):
1231 """Compute the patch (as a string of data) needed to turn sf into
1232 tf. Returns the same tuple as GetPatch()."""
1233
1234 tf = self.tf
1235 sf = self.sf
1236
Doug Zongker24cd2802012-08-14 16:36:15 -07001237 if self.diff_program:
1238 diff_program = self.diff_program
1239 else:
1240 ext = os.path.splitext(tf.name)[1]
1241 diff_program = DIFF_PROGRAM_BY_EXT.get(ext, "bsdiff")
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001242
1243 ttemp = tf.WriteToTemp()
1244 stemp = sf.WriteToTemp()
1245
1246 ext = os.path.splitext(tf.name)[1]
1247
1248 try:
1249 ptemp = tempfile.NamedTemporaryFile()
1250 if isinstance(diff_program, list):
1251 cmd = copy.copy(diff_program)
1252 else:
1253 cmd = [diff_program]
1254 cmd.append(stemp.name)
1255 cmd.append(ttemp.name)
1256 cmd.append(ptemp.name)
1257 p = Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Doug Zongkerf8340082014-08-05 10:39:37 -07001258 err = []
1259 def run():
1260 _, e = p.communicate()
Dan Albert8b72aef2015-03-23 19:13:21 -07001261 if e:
1262 err.append(e)
Doug Zongkerf8340082014-08-05 10:39:37 -07001263 th = threading.Thread(target=run)
1264 th.start()
1265 th.join(timeout=300) # 5 mins
1266 if th.is_alive():
1267 print "WARNING: diff command timed out"
1268 p.terminate()
1269 th.join(5)
1270 if th.is_alive():
1271 p.kill()
1272 th.join()
1273
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001274 if err or p.returncode != 0:
Doug Zongkerf8340082014-08-05 10:39:37 -07001275 print "WARNING: failure running %s:\n%s\n" % (
1276 diff_program, "".join(err))
1277 self.patch = None
1278 return None, None, None
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001279 diff = ptemp.read()
1280 finally:
1281 ptemp.close()
1282 stemp.close()
1283 ttemp.close()
1284
1285 self.patch = diff
1286 return self.tf, self.sf, self.patch
1287
1288
1289 def GetPatch(self):
1290 """Return a tuple (target_file, source_file, patch_data).
1291 patch_data may be None if ComputePatch hasn't been called, or if
1292 computing the patch failed."""
1293 return self.tf, self.sf, self.patch
1294
1295
1296def ComputeDifferences(diffs):
1297 """Call ComputePatch on all the Difference objects in 'diffs'."""
1298 print len(diffs), "diffs to compute"
1299
1300 # Do the largest files first, to try and reduce the long-pole effect.
1301 by_size = [(i.tf.size, i) for i in diffs]
1302 by_size.sort(reverse=True)
1303 by_size = [i[1] for i in by_size]
1304
1305 lock = threading.Lock()
1306 diff_iter = iter(by_size) # accessed under lock
1307
1308 def worker():
1309 try:
1310 lock.acquire()
1311 for d in diff_iter:
1312 lock.release()
1313 start = time.time()
1314 d.ComputePatch()
1315 dur = time.time() - start
1316 lock.acquire()
1317
1318 tf, sf, patch = d.GetPatch()
1319 if sf.name == tf.name:
1320 name = tf.name
1321 else:
1322 name = "%s (%s)" % (tf.name, sf.name)
1323 if patch is None:
1324 print "patching failed! %s" % (name,)
1325 else:
1326 print "%8.2f sec %8d / %8d bytes (%6.2f%%) %s" % (
1327 dur, len(patch), tf.size, 100.0 * len(patch) / tf.size, name)
1328 lock.release()
Dan Albert8b72aef2015-03-23 19:13:21 -07001329 except Exception as e:
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001330 print e
1331 raise
1332
1333 # start worker threads; wait for them all to finish.
1334 threads = [threading.Thread(target=worker)
1335 for i in range(OPTIONS.worker_threads)]
1336 for th in threads:
1337 th.start()
1338 while threads:
1339 threads.pop().join()
Doug Zongker96a57e72010-09-26 14:57:41 -07001340
1341
Dan Albert8b72aef2015-03-23 19:13:21 -07001342class BlockDifference(object):
1343 def __init__(self, partition, tgt, src=None, check_first_block=False,
1344 version=None):
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001345 self.tgt = tgt
1346 self.src = src
1347 self.partition = partition
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001348 self.check_first_block = check_first_block
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001349
Tao Baodd2a5892015-03-12 12:32:37 -07001350 if version is None:
1351 version = 1
1352 if OPTIONS.info_dict:
1353 version = max(
1354 int(i) for i in
1355 OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(","))
1356 self.version = version
Doug Zongker62338182014-09-08 08:29:55 -07001357
1358 b = blockimgdiff.BlockImageDiff(tgt, src, threads=OPTIONS.worker_threads,
Michael Runge910b0052015-02-11 19:28:08 -08001359 version=self.version)
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001360 tmpdir = tempfile.mkdtemp()
1361 OPTIONS.tempfiles.append(tmpdir)
1362 self.path = os.path.join(tmpdir, partition)
1363 b.Compute(self.path)
Tao Baob4cfca52016-02-04 14:26:02 -08001364 self._required_cache = b.max_stashed_size
Tao Baod522bdc2016-04-12 15:53:16 -07001365 self.touched_src_ranges = b.touched_src_ranges
1366 self.touched_src_sha1 = b.touched_src_sha1
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001367
Tao Baoaac4ad52015-10-16 15:26:34 -07001368 if src is None:
1369 _, self.device = GetTypeAndDevice("/" + partition, OPTIONS.info_dict)
1370 else:
1371 _, self.device = GetTypeAndDevice("/" + partition,
1372 OPTIONS.source_info_dict)
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001373
Tao Baob4cfca52016-02-04 14:26:02 -08001374 @property
1375 def required_cache(self):
1376 return self._required_cache
1377
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001378 def WriteScript(self, script, output_zip, progress=None):
1379 if not self.src:
1380 # write the output unconditionally
Jesse Zhao75bcea02015-01-06 10:59:53 -08001381 script.Print("Patching %s image unconditionally..." % (self.partition,))
1382 else:
1383 script.Print("Patching %s image after verification." % (self.partition,))
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001384
Dan Albert8b72aef2015-03-23 19:13:21 -07001385 if progress:
1386 script.ShowProgress(progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001387 self._WriteUpdate(script, output_zip)
Tianjie Xub2deb222016-03-25 15:01:33 -07001388 if OPTIONS.verify:
1389 self._WritePostInstallVerifyScript(script)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001390
Tao Bao9bc6bb22015-11-09 16:58:28 -08001391 def WriteStrictVerifyScript(self, script):
1392 """Verify all the blocks in the care_map, including clobbered blocks.
1393
1394 This differs from the WriteVerifyScript() function: a) it prints different
1395 error messages; b) it doesn't allow half-way updated images to pass the
1396 verification."""
1397
1398 partition = self.partition
1399 script.Print("Verifying %s..." % (partition,))
1400 ranges = self.tgt.care_map
1401 ranges_str = ranges.to_string_raw()
1402 script.AppendExtra('range_sha1("%s", "%s") == "%s" && '
1403 'ui_print(" Verified.") || '
1404 'ui_print("\\"%s\\" has unexpected contents.");' % (
1405 self.device, ranges_str,
1406 self.tgt.TotalSha1(include_clobbered_blocks=True),
1407 self.device))
1408 script.AppendExtra("")
1409
Tao Baod522bdc2016-04-12 15:53:16 -07001410 def WriteVerifyScript(self, script, touched_blocks_only=False):
Sami Tolvanendd67a292014-12-09 16:40:34 +00001411 partition = self.partition
Tao Baof9efe282016-04-14 15:58:05 -07001412
1413 # full OTA
Jesse Zhao75bcea02015-01-06 10:59:53 -08001414 if not self.src:
Sami Tolvanendd67a292014-12-09 16:40:34 +00001415 script.Print("Image %s will be patched unconditionally." % (partition,))
Tao Baof9efe282016-04-14 15:58:05 -07001416
1417 # incremental OTA
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001418 else:
Tao Baod522bdc2016-04-12 15:53:16 -07001419 if touched_blocks_only and self.version >= 3:
1420 ranges = self.touched_src_ranges
1421 expected_sha1 = self.touched_src_sha1
1422 else:
1423 ranges = self.src.care_map.subtract(self.src.clobbered_blocks)
1424 expected_sha1 = self.src.TotalSha1()
Tao Baof9efe282016-04-14 15:58:05 -07001425
1426 # No blocks to be checked, skipping.
1427 if not ranges:
1428 return
1429
Tao Bao5ece99d2015-05-12 11:42:31 -07001430 ranges_str = ranges.to_string_raw()
Sami Tolvanenf0a7c762015-06-25 11:48:29 +01001431 if self.version >= 4:
1432 script.AppendExtra(('if (range_sha1("%s", "%s") == "%s" || '
1433 'block_image_verify("%s", '
1434 'package_extract_file("%s.transfer.list"), '
Tianjie Xufc3422a2015-12-15 11:53:59 -08001435 '"%s.new.dat", "%s.patch.dat")) then') % (
Tao Baod522bdc2016-04-12 15:53:16 -07001436 self.device, ranges_str, expected_sha1,
Sami Tolvanenf0a7c762015-06-25 11:48:29 +01001437 self.device, partition, partition, partition))
1438 elif self.version == 3:
Sami Tolvanene09d0962015-04-24 11:54:01 +01001439 script.AppendExtra(('if (range_sha1("%s", "%s") == "%s" || '
1440 'block_image_verify("%s", '
Michael Runge910b0052015-02-11 19:28:08 -08001441 'package_extract_file("%s.transfer.list"), '
Sami Tolvanene09d0962015-04-24 11:54:01 +01001442 '"%s.new.dat", "%s.patch.dat")) then') % (
Tao Baod522bdc2016-04-12 15:53:16 -07001443 self.device, ranges_str, expected_sha1,
Sami Tolvanene09d0962015-04-24 11:54:01 +01001444 self.device, partition, partition, partition))
Michael Runge910b0052015-02-11 19:28:08 -08001445 else:
Dan Albert8b72aef2015-03-23 19:13:21 -07001446 script.AppendExtra('if range_sha1("%s", "%s") == "%s" then' % (
Tao Bao5ece99d2015-05-12 11:42:31 -07001447 self.device, ranges_str, self.src.TotalSha1()))
Tao Baodd2a5892015-03-12 12:32:37 -07001448 script.Print('Verified %s image...' % (partition,))
Dan Albert8b72aef2015-03-23 19:13:21 -07001449 script.AppendExtra('else')
Sami Tolvanendd67a292014-12-09 16:40:34 +00001450
Tianjie Xufc3422a2015-12-15 11:53:59 -08001451 if self.version >= 4:
1452
1453 # Bug: 21124327
1454 # When generating incrementals for the system and vendor partitions in
1455 # version 4 or newer, explicitly check the first block (which contains
1456 # the superblock) of the partition to see if it's what we expect. If
1457 # this check fails, give an explicit log message about the partition
1458 # having been remounted R/W (the most likely explanation).
1459 if self.check_first_block:
1460 script.AppendExtra('check_first_block("%s");' % (self.device,))
1461
1462 # If version >= 4, try block recovery before abort update
Tianjie Xu209db462016-05-24 17:34:52 -07001463 if partition == "system":
1464 code = ErrorCode.SYSTEM_RECOVER_FAILURE
1465 else:
1466 code = ErrorCode.VENDOR_RECOVER_FAILURE
Tianjie Xufc3422a2015-12-15 11:53:59 -08001467 script.AppendExtra((
1468 'ifelse (block_image_recover("{device}", "{ranges}") && '
1469 'block_image_verify("{device}", '
1470 'package_extract_file("{partition}.transfer.list"), '
1471 '"{partition}.new.dat", "{partition}.patch.dat"), '
1472 'ui_print("{partition} recovered successfully."), '
Tianjie Xu209db462016-05-24 17:34:52 -07001473 'abort("E{code}: {partition} partition fails to recover"));\n'
Tianjie Xufc3422a2015-12-15 11:53:59 -08001474 'endif;').format(device=self.device, ranges=ranges_str,
Tianjie Xu209db462016-05-24 17:34:52 -07001475 partition=partition, code=code))
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001476
Tao Baodd2a5892015-03-12 12:32:37 -07001477 # Abort the OTA update. Note that the incremental OTA cannot be applied
1478 # even if it may match the checksum of the target partition.
1479 # a) If version < 3, operations like move and erase will make changes
1480 # unconditionally and damage the partition.
1481 # b) If version >= 3, it won't even reach here.
Tianjie Xufc3422a2015-12-15 11:53:59 -08001482 else:
Tianjie Xu209db462016-05-24 17:34:52 -07001483 if partition == "system":
1484 code = ErrorCode.SYSTEM_VERIFICATION_FAILURE
1485 else:
1486 code = ErrorCode.VENDOR_VERIFICATION_FAILURE
1487 script.AppendExtra((
1488 'abort("E%d: %s partition has unexpected contents");\n'
1489 'endif;') % (code, partition))
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001490
Tao Bao5fcaaef2015-06-01 13:40:49 -07001491 def _WritePostInstallVerifyScript(self, script):
1492 partition = self.partition
1493 script.Print('Verifying the updated %s image...' % (partition,))
1494 # Unlike pre-install verification, clobbered_blocks should not be ignored.
1495 ranges = self.tgt.care_map
1496 ranges_str = ranges.to_string_raw()
1497 script.AppendExtra('if range_sha1("%s", "%s") == "%s" then' % (
1498 self.device, ranges_str,
1499 self.tgt.TotalSha1(include_clobbered_blocks=True)))
Tao Baoe9b61912015-07-09 17:37:49 -07001500
1501 # Bug: 20881595
1502 # Verify that extended blocks are really zeroed out.
1503 if self.tgt.extended:
1504 ranges_str = self.tgt.extended.to_string_raw()
1505 script.AppendExtra('if range_sha1("%s", "%s") == "%s" then' % (
1506 self.device, ranges_str,
1507 self._HashZeroBlocks(self.tgt.extended.size())))
1508 script.Print('Verified the updated %s image.' % (partition,))
Tianjie Xu209db462016-05-24 17:34:52 -07001509 if partition == "system":
1510 code = ErrorCode.SYSTEM_NONZERO_CONTENTS
1511 else:
1512 code = ErrorCode.VENDOR_NONZERO_CONTENTS
Tao Baoe9b61912015-07-09 17:37:49 -07001513 script.AppendExtra(
1514 'else\n'
Tianjie Xu209db462016-05-24 17:34:52 -07001515 ' abort("E%d: %s partition has unexpected non-zero contents after '
1516 'OTA update");\n'
1517 'endif;' % (code, partition))
Tao Baoe9b61912015-07-09 17:37:49 -07001518 else:
1519 script.Print('Verified the updated %s image.' % (partition,))
1520
Tianjie Xu209db462016-05-24 17:34:52 -07001521 if partition == "system":
1522 code = ErrorCode.SYSTEM_UNEXPECTED_CONTENTS
1523 else:
1524 code = ErrorCode.VENDOR_UNEXPECTED_CONTENTS
1525
Tao Bao5fcaaef2015-06-01 13:40:49 -07001526 script.AppendExtra(
1527 'else\n'
Tianjie Xu209db462016-05-24 17:34:52 -07001528 ' abort("E%d: %s partition has unexpected contents after OTA '
1529 'update");\n'
1530 'endif;' % (code, partition))
Tao Bao5fcaaef2015-06-01 13:40:49 -07001531
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001532 def _WriteUpdate(self, script, output_zip):
Dan Albert8e0178d2015-01-27 15:53:15 -08001533 ZipWrite(output_zip,
1534 '{}.transfer.list'.format(self.path),
1535 '{}.transfer.list'.format(self.partition))
1536 ZipWrite(output_zip,
1537 '{}.new.dat'.format(self.path),
1538 '{}.new.dat'.format(self.partition))
1539 ZipWrite(output_zip,
1540 '{}.patch.dat'.format(self.path),
1541 '{}.patch.dat'.format(self.partition),
1542 compress_type=zipfile.ZIP_STORED)
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001543
Tianjie Xu209db462016-05-24 17:34:52 -07001544 if self.partition == "system":
1545 code = ErrorCode.SYSTEM_UPDATE_FAILURE
1546 else:
1547 code = ErrorCode.VENDOR_UPDATE_FAILURE
1548
Dan Albert8e0178d2015-01-27 15:53:15 -08001549 call = ('block_image_update("{device}", '
1550 'package_extract_file("{partition}.transfer.list"), '
Tianjie Xub2deb222016-03-25 15:01:33 -07001551 '"{partition}.new.dat", "{partition}.patch.dat") ||\n'
Tianjie Xu209db462016-05-24 17:34:52 -07001552 ' abort("E{code}: Failed to update {partition} image.");'.format(
1553 device=self.device, partition=self.partition, code=code))
Dan Albert8b72aef2015-03-23 19:13:21 -07001554 script.AppendExtra(script.WordWrap(call))
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001555
Dan Albert8b72aef2015-03-23 19:13:21 -07001556 def _HashBlocks(self, source, ranges): # pylint: disable=no-self-use
Sami Tolvanendd67a292014-12-09 16:40:34 +00001557 data = source.ReadRangeSet(ranges)
1558 ctx = sha1()
1559
1560 for p in data:
1561 ctx.update(p)
1562
1563 return ctx.hexdigest()
1564
Tao Baoe9b61912015-07-09 17:37:49 -07001565 def _HashZeroBlocks(self, num_blocks): # pylint: disable=no-self-use
1566 """Return the hash value for all zero blocks."""
1567 zero_block = '\x00' * 4096
1568 ctx = sha1()
1569 for _ in range(num_blocks):
1570 ctx.update(zero_block)
1571
1572 return ctx.hexdigest()
1573
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001574
1575DataImage = blockimgdiff.DataImage
1576
Doug Zongker96a57e72010-09-26 14:57:41 -07001577# map recovery.fstab's fs_types to mount/format "partition types"
Dan Albert8b72aef2015-03-23 19:13:21 -07001578PARTITION_TYPES = {
1579 "yaffs2": "MTD",
1580 "mtd": "MTD",
1581 "ext4": "EMMC",
1582 "emmc": "EMMC",
Mohamad Ayyash95e74c12015-05-01 15:39:36 -07001583 "f2fs": "EMMC",
1584 "squashfs": "EMMC"
Dan Albert8b72aef2015-03-23 19:13:21 -07001585}
Doug Zongker96a57e72010-09-26 14:57:41 -07001586
1587def GetTypeAndDevice(mount_point, info):
1588 fstab = info["fstab"]
1589 if fstab:
Dan Albert8b72aef2015-03-23 19:13:21 -07001590 return (PARTITION_TYPES[fstab[mount_point].fs_type],
1591 fstab[mount_point].device)
Doug Zongker96a57e72010-09-26 14:57:41 -07001592 else:
Dan Albert8b72aef2015-03-23 19:13:21 -07001593 raise KeyError
Baligh Uddinbeb6afd2013-11-13 00:22:34 +00001594
1595
1596def ParseCertificate(data):
1597 """Parse a PEM-format certificate."""
1598 cert = []
1599 save = False
1600 for line in data.split("\n"):
1601 if "--END CERTIFICATE--" in line:
1602 break
1603 if save:
1604 cert.append(line)
1605 if "--BEGIN CERTIFICATE--" in line:
1606 save = True
1607 cert = "".join(cert).decode('base64')
1608 return cert
Doug Zongkerc9253822014-02-04 12:17:58 -08001609
Doug Zongker412c02f2014-02-13 10:58:24 -08001610def MakeRecoveryPatch(input_dir, output_sink, recovery_img, boot_img,
1611 info_dict=None):
Doug Zongkerc9253822014-02-04 12:17:58 -08001612 """Generate a binary patch that creates the recovery image starting
1613 with the boot image. (Most of the space in these images is just the
1614 kernel, which is identical for the two, so the resulting patch
1615 should be efficient.) Add it to the output zip, along with a shell
1616 script that is run from init.rc on first boot to actually do the
1617 patching and install the new recovery image.
1618
1619 recovery_img and boot_img should be File objects for the
1620 corresponding images. info should be the dictionary returned by
1621 common.LoadInfoDict() on the input target_files.
1622 """
1623
Doug Zongker412c02f2014-02-13 10:58:24 -08001624 if info_dict is None:
1625 info_dict = OPTIONS.info_dict
1626
Tao Baof2cffbd2015-07-22 12:33:18 -07001627 full_recovery_image = info_dict.get("full_recovery_image", None) == "true"
Tao Bao7a5bf8a2015-07-21 18:01:20 -07001628 system_root_image = info_dict.get("system_root_image", None) == "true"
Doug Zongkerc9253822014-02-04 12:17:58 -08001629
Tao Baof2cffbd2015-07-22 12:33:18 -07001630 if full_recovery_image:
1631 output_sink("etc/recovery.img", recovery_img.data)
1632
1633 else:
1634 diff_program = ["imgdiff"]
1635 path = os.path.join(input_dir, "SYSTEM", "etc", "recovery-resource.dat")
1636 if os.path.exists(path):
1637 diff_program.append("-b")
1638 diff_program.append(path)
1639 bonus_args = "-b /system/etc/recovery-resource.dat"
1640 else:
1641 bonus_args = ""
1642
1643 d = Difference(recovery_img, boot_img, diff_program=diff_program)
1644 _, _, patch = d.ComputePatch()
1645 output_sink("recovery-from-boot.p", patch)
Doug Zongkerc9253822014-02-04 12:17:58 -08001646
Dan Albertebb19aa2015-03-27 19:11:53 -07001647 try:
Tao Bao6f0b2192015-10-13 16:37:12 -07001648 # The following GetTypeAndDevice()s need to use the path in the target
1649 # info_dict instead of source_info_dict.
Dan Albertebb19aa2015-03-27 19:11:53 -07001650 boot_type, boot_device = GetTypeAndDevice("/boot", info_dict)
1651 recovery_type, recovery_device = GetTypeAndDevice("/recovery", info_dict)
1652 except KeyError:
Ying Wanga961a092014-07-29 11:42:37 -07001653 return
Doug Zongkerc9253822014-02-04 12:17:58 -08001654
Tao Baof2cffbd2015-07-22 12:33:18 -07001655 if full_recovery_image:
1656 sh = """#!/system/bin/sh
1657if ! applypatch -c %(type)s:%(device)s:%(size)d:%(sha1)s; then
1658 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"
1659else
1660 log -t recovery "Recovery image already installed"
1661fi
1662""" % {'type': recovery_type,
1663 'device': recovery_device,
1664 'sha1': recovery_img.sha1,
1665 'size': recovery_img.size}
1666 else:
1667 sh = """#!/system/bin/sh
Doug Zongkerc9253822014-02-04 12:17:58 -08001668if ! applypatch -c %(recovery_type)s:%(recovery_device)s:%(recovery_size)d:%(recovery_sha1)s; then
1669 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"
1670else
1671 log -t recovery "Recovery image already installed"
1672fi
Dan Albert8b72aef2015-03-23 19:13:21 -07001673""" % {'boot_size': boot_img.size,
1674 'boot_sha1': boot_img.sha1,
1675 'recovery_size': recovery_img.size,
1676 'recovery_sha1': recovery_img.sha1,
1677 'boot_type': boot_type,
1678 'boot_device': boot_device,
1679 'recovery_type': recovery_type,
1680 'recovery_device': recovery_device,
1681 'bonus_args': bonus_args}
Doug Zongkerc9253822014-02-04 12:17:58 -08001682
1683 # The install script location moved from /system/etc to /system/bin
Tao Bao9f0c8df2015-07-07 18:31:47 -07001684 # in the L release. Parse init.*.rc files to find out where the
Doug Zongkerc9253822014-02-04 12:17:58 -08001685 # target-files expects it to be, and put it there.
1686 sh_location = "etc/install-recovery.sh"
Tao Bao9f0c8df2015-07-07 18:31:47 -07001687 found = False
Tao Bao7a5bf8a2015-07-21 18:01:20 -07001688 if system_root_image:
1689 init_rc_dir = os.path.join(input_dir, "ROOT")
1690 else:
1691 init_rc_dir = os.path.join(input_dir, "BOOT", "RAMDISK")
Tao Bao9f0c8df2015-07-07 18:31:47 -07001692 init_rc_files = os.listdir(init_rc_dir)
1693 for init_rc_file in init_rc_files:
1694 if (not init_rc_file.startswith('init.') or
1695 not init_rc_file.endswith('.rc')):
1696 continue
1697
1698 with open(os.path.join(init_rc_dir, init_rc_file)) as f:
Doug Zongkerc9253822014-02-04 12:17:58 -08001699 for line in f:
Dan Albert8b72aef2015-03-23 19:13:21 -07001700 m = re.match(r"^service flash_recovery /system/(\S+)\s*$", line)
Doug Zongkerc9253822014-02-04 12:17:58 -08001701 if m:
1702 sh_location = m.group(1)
Tao Bao9f0c8df2015-07-07 18:31:47 -07001703 found = True
Doug Zongkerc9253822014-02-04 12:17:58 -08001704 break
Tao Bao9f0c8df2015-07-07 18:31:47 -07001705
1706 if found:
1707 break
1708
1709 print "putting script in", sh_location
Doug Zongkerc9253822014-02-04 12:17:58 -08001710
1711 output_sink(sh_location, sh)