blob: c721a24d9b02e184770ae4649975ba55a60a5e20 [file] [log] [blame]
Doug Zongkereef39442009-04-02 12:14:19 -07001# Copyright (C) 2008 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
Tao Bao89fbb0f2017-01-10 10:47:58 -080015from __future__ import print_function
16
Doug Zongkerea5d7a92010-09-12 15:26:16 -070017import copy
Doug Zongker8ce7c252009-05-22 13:34:54 -070018import errno
Doug Zongkereef39442009-04-02 12:14:19 -070019import getopt
20import getpass
Doug Zongker05d3dea2009-06-22 11:32:31 -070021import imp
Doug Zongkereef39442009-04-02 12:14:19 -070022import os
Ying Wang7e6d4e42010-12-13 16:25:36 -080023import platform
Doug Zongkereef39442009-04-02 12:14:19 -070024import re
T.R. Fullhart37e10522013-03-18 10:31:26 -070025import shlex
Doug Zongkereef39442009-04-02 12:14:19 -070026import shutil
27import subprocess
28import sys
29import tempfile
Doug Zongkerea5d7a92010-09-12 15:26:16 -070030import threading
31import time
Doug Zongker048e7ca2009-06-15 14:31:53 -070032import zipfile
Doug Zongkereef39442009-04-02 12:14:19 -070033
Doug Zongkerab7ca1d2014-08-26 10:40:28 -070034import blockimgdiff
35
Tao Baof3282b42015-04-01 11:21:55 -070036from hashlib import sha1 as sha1
Doug Zongker55d93282011-01-25 17:03:34 -080037
Doug Zongkereef39442009-04-02 12:14:19 -070038
Dan Albert8b72aef2015-03-23 19:13:21 -070039class Options(object):
40 def __init__(self):
41 platform_search_path = {
42 "linux2": "out/host/linux-x86",
43 "darwin": "out/host/darwin-x86",
Doug Zongker85448772014-09-09 14:59:20 -070044 }
Doug Zongker85448772014-09-09 14:59:20 -070045
Dan Albert8b72aef2015-03-23 19:13:21 -070046 self.search_path = platform_search_path.get(sys.platform, None)
47 self.signapk_path = "framework/signapk.jar" # Relative to search_path
Alex Klyubin9667b182015-12-10 13:38:50 -080048 self.signapk_shared_library_path = "lib64" # Relative to search_path
Dan Albert8b72aef2015-03-23 19:13:21 -070049 self.extra_signapk_args = []
50 self.java_path = "java" # Use the one on the path by default.
Tao Baoe95540e2016-11-08 12:08:53 -080051 self.java_args = ["-Xmx2048m"] # The default JVM args.
Dan Albert8b72aef2015-03-23 19:13:21 -070052 self.public_key_suffix = ".x509.pem"
53 self.private_key_suffix = ".pk8"
Dan Albertcd9ecc02015-03-27 16:37:23 -070054 # use otatools built boot_signer by default
55 self.boot_signer_path = "boot_signer"
Baligh Uddin601ddea2015-06-09 15:48:14 -070056 self.boot_signer_args = []
57 self.verity_signer_path = None
58 self.verity_signer_args = []
Dan Albert8b72aef2015-03-23 19:13:21 -070059 self.verbose = False
60 self.tempfiles = []
61 self.device_specific = None
62 self.extras = {}
63 self.info_dict = None
Tao Bao6f0b2192015-10-13 16:37:12 -070064 self.source_info_dict = None
65 self.target_info_dict = None
Dan Albert8b72aef2015-03-23 19:13:21 -070066 self.worker_threads = None
Tao Bao575d68a2015-08-07 19:49:45 -070067 # Stash size cannot exceed cache_size * threshold.
68 self.cache_size = None
69 self.stash_threshold = 0.8
Dan Albert8b72aef2015-03-23 19:13:21 -070070
71
72OPTIONS = Options()
Doug Zongkereef39442009-04-02 12:14:19 -070073
Doug Zongkerf6a53aa2009-12-15 15:06:55 -080074
75# Values for "certificate" in apkcerts that mean special things.
76SPECIAL_CERT_STRINGS = ("PRESIGNED", "EXTERNAL")
77
Tianjie Xu209db462016-05-24 17:34:52 -070078class ErrorCode(object):
79 """Define error_codes for failures that happen during the actual
80 update package installation.
81
82 Error codes 0-999 are reserved for failures before the package
83 installation (i.e. low battery, package verification failure).
84 Detailed code in 'bootable/recovery/error_code.h' """
85
86 SYSTEM_VERIFICATION_FAILURE = 1000
87 SYSTEM_UPDATE_FAILURE = 1001
88 SYSTEM_UNEXPECTED_CONTENTS = 1002
89 SYSTEM_NONZERO_CONTENTS = 1003
90 SYSTEM_RECOVER_FAILURE = 1004
91 VENDOR_VERIFICATION_FAILURE = 2000
92 VENDOR_UPDATE_FAILURE = 2001
93 VENDOR_UNEXPECTED_CONTENTS = 2002
94 VENDOR_NONZERO_CONTENTS = 2003
95 VENDOR_RECOVER_FAILURE = 2004
96 OEM_PROP_MISMATCH = 3000
97 FINGERPRINT_MISMATCH = 3001
98 THUMBPRINT_MISMATCH = 3002
99 OLDER_BUILD = 3003
100 DEVICE_MISMATCH = 3004
101 BAD_PATCH_FILE = 3005
102 INSUFFICIENT_CACHE_SPACE = 3006
103 TUNE_PARTITION_FAILURE = 3007
104 APPLY_PATCH_FAILURE = 3008
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800105
Dan Albert8b72aef2015-03-23 19:13:21 -0700106class ExternalError(RuntimeError):
107 pass
Doug Zongkereef39442009-04-02 12:14:19 -0700108
109
Tao Bao39451582017-05-04 11:10:47 -0700110def Run(args, verbose=None, **kwargs):
111 """Create and return a subprocess.Popen object.
112
113 Caller can specify if the command line should be printed. The global
114 OPTIONS.verbose will be used if not specified.
115 """
116 if verbose is None:
117 verbose = OPTIONS.verbose
118 if verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800119 print(" running: ", " ".join(args))
Doug Zongkereef39442009-04-02 12:14:19 -0700120 return subprocess.Popen(args, **kwargs)
121
122
Ying Wang7e6d4e42010-12-13 16:25:36 -0800123def CloseInheritedPipes():
124 """ Gmake in MAC OS has file descriptor (PIPE) leak. We close those fds
125 before doing other work."""
126 if platform.system() != "Darwin":
127 return
128 for d in range(3, 1025):
129 try:
130 stat = os.fstat(d)
131 if stat is not None:
132 pipebit = stat[0] & 0x1000
133 if pipebit != 0:
134 os.close(d)
135 except OSError:
136 pass
137
138
Tao Bao2c15d9e2015-07-09 11:51:16 -0700139def LoadInfoDict(input_file, input_dir=None):
Doug Zongkerc19a8d52010-07-01 15:30:11 -0700140 """Read and parse the META/misc_info.txt key/value pairs from the
141 input target files and return a dict."""
142
Doug Zongkerc9253822014-02-04 12:17:58 -0800143 def read_helper(fn):
Dan Albert8b72aef2015-03-23 19:13:21 -0700144 if isinstance(input_file, zipfile.ZipFile):
145 return input_file.read(fn)
Doug Zongkerc9253822014-02-04 12:17:58 -0800146 else:
Dan Albert8b72aef2015-03-23 19:13:21 -0700147 path = os.path.join(input_file, *fn.split("/"))
Doug Zongkerc9253822014-02-04 12:17:58 -0800148 try:
149 with open(path) as f:
150 return f.read()
Dan Albert8b72aef2015-03-23 19:13:21 -0700151 except IOError as e:
Doug Zongkerc9253822014-02-04 12:17:58 -0800152 if e.errno == errno.ENOENT:
153 raise KeyError(fn)
Tao Bao6cd54732017-02-27 15:12:05 -0800154
Doug Zongkerc19a8d52010-07-01 15:30:11 -0700155 try:
Michael Runge6e836112014-04-15 17:40:21 -0700156 d = LoadDictionaryFromLines(read_helper("META/misc_info.txt").split("\n"))
Doug Zongker37974732010-09-16 17:44:38 -0700157 except KeyError:
Tao Bao6cd54732017-02-27 15:12:05 -0800158 raise ValueError("can't find META/misc_info.txt in input target-files")
Doug Zongkerc19a8d52010-07-01 15:30:11 -0700159
Tao Bao6cd54732017-02-27 15:12:05 -0800160 assert "recovery_api_version" in d
Tao Baod1de6f32017-03-01 16:38:48 -0800161 assert "fstab_version" in d
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800162
Tao Bao84e75682015-07-19 02:38:53 -0700163 # A few properties are stored as links to the files in the out/ directory.
164 # It works fine with the build system. However, they are no longer available
165 # when (re)generating from target_files zip. If input_dir is not None, we
166 # are doing repacking. Redirect those properties to the actual files in the
167 # unzipped directory.
Tao Bao2c15d9e2015-07-09 11:51:16 -0700168 if input_dir is not None:
Stephen Smalleyd3a803e2015-08-04 14:59:06 -0400169 # We carry a copy of file_contexts.bin under META/. If not available,
170 # search BOOT/RAMDISK/. Note that sometimes we may need a different file
Tao Bao84e75682015-07-19 02:38:53 -0700171 # to build images than the one running on device, such as when enabling
172 # system_root_image. In that case, we must have the one for image
173 # generation copied to META/.
Tao Bao79735a62015-08-28 10:52:03 -0700174 fc_basename = os.path.basename(d.get("selinux_fc", "file_contexts"))
175 fc_config = os.path.join(input_dir, "META", fc_basename)
Tao Bao84e75682015-07-19 02:38:53 -0700176 if d.get("system_root_image") == "true":
177 assert os.path.exists(fc_config)
Tao Bao2c15d9e2015-07-09 11:51:16 -0700178 if not os.path.exists(fc_config):
Tao Bao79735a62015-08-28 10:52:03 -0700179 fc_config = os.path.join(input_dir, "BOOT", "RAMDISK", fc_basename)
Tao Bao2c15d9e2015-07-09 11:51:16 -0700180 if not os.path.exists(fc_config):
181 fc_config = None
182
183 if fc_config:
184 d["selinux_fc"] = fc_config
185
Tao Bao84e75682015-07-19 02:38:53 -0700186 # Similarly we need to redirect "ramdisk_dir" and "ramdisk_fs_config".
187 if d.get("system_root_image") == "true":
188 d["ramdisk_dir"] = os.path.join(input_dir, "ROOT")
189 d["ramdisk_fs_config"] = os.path.join(
190 input_dir, "META", "root_filesystem_config.txt")
191
Tao Baof54216f2016-03-29 15:12:37 -0700192 # Redirect {system,vendor}_base_fs_file.
193 if "system_base_fs_file" in d:
194 basename = os.path.basename(d["system_base_fs_file"])
195 system_base_fs_file = os.path.join(input_dir, "META", basename)
Tao Baob079b502016-05-03 08:01:19 -0700196 if os.path.exists(system_base_fs_file):
197 d["system_base_fs_file"] = system_base_fs_file
198 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800199 print("Warning: failed to find system base fs file: %s" % (
200 system_base_fs_file,))
Tao Baob079b502016-05-03 08:01:19 -0700201 del d["system_base_fs_file"]
Tao Baof54216f2016-03-29 15:12:37 -0700202
203 if "vendor_base_fs_file" in d:
204 basename = os.path.basename(d["vendor_base_fs_file"])
205 vendor_base_fs_file = os.path.join(input_dir, "META", basename)
Tao Baob079b502016-05-03 08:01:19 -0700206 if os.path.exists(vendor_base_fs_file):
207 d["vendor_base_fs_file"] = vendor_base_fs_file
208 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800209 print("Warning: failed to find vendor base fs file: %s" % (
210 vendor_base_fs_file,))
Tao Baob079b502016-05-03 08:01:19 -0700211 del d["vendor_base_fs_file"]
Tao Baof54216f2016-03-29 15:12:37 -0700212
Doug Zongker37974732010-09-16 17:44:38 -0700213 try:
Doug Zongkerc9253822014-02-04 12:17:58 -0800214 data = read_helper("META/imagesizes.txt")
Doug Zongker37974732010-09-16 17:44:38 -0700215 for line in data.split("\n"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700216 if not line:
217 continue
Doug Zongker1684d9c2010-09-17 07:44:38 -0700218 name, value = line.split(" ", 1)
Dan Albert8b72aef2015-03-23 19:13:21 -0700219 if not value:
220 continue
Doug Zongker37974732010-09-16 17:44:38 -0700221 if name == "blocksize":
222 d[name] = value
223 else:
224 d[name + "_size"] = value
225 except KeyError:
226 pass
227
228 def makeint(key):
229 if key in d:
230 d[key] = int(d[key], 0)
231
232 makeint("recovery_api_version")
233 makeint("blocksize")
234 makeint("system_size")
Daniel Rosenbergf4eabc32014-07-10 15:42:38 -0700235 makeint("vendor_size")
Doug Zongker37974732010-09-16 17:44:38 -0700236 makeint("userdata_size")
Ying Wang9f8e8db2011-11-04 11:37:01 -0700237 makeint("cache_size")
Doug Zongker37974732010-09-16 17:44:38 -0700238 makeint("recovery_size")
239 makeint("boot_size")
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800240 makeint("fstab_version")
Doug Zongkerc19a8d52010-07-01 15:30:11 -0700241
Tianjie Xucfa86222016-03-07 16:31:19 -0800242 system_root_image = d.get("system_root_image", None) == "true"
243 if d.get("no_recovery", None) != "true":
244 recovery_fstab_path = "RECOVERY/RAMDISK/etc/recovery.fstab"
Tao Bao48550cc2015-11-19 17:05:46 -0800245 d["fstab"] = LoadRecoveryFSTab(read_helper, d["fstab_version"],
Tianjie Xucfa86222016-03-07 16:31:19 -0800246 recovery_fstab_path, system_root_image)
247 elif d.get("recovery_as_boot", None) == "true":
248 recovery_fstab_path = "BOOT/RAMDISK/etc/recovery.fstab"
249 d["fstab"] = LoadRecoveryFSTab(read_helper, d["fstab_version"],
250 recovery_fstab_path, system_root_image)
251 else:
252 d["fstab"] = None
253
Doug Zongkerc9253822014-02-04 12:17:58 -0800254 d["build.prop"] = LoadBuildProp(read_helper)
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700255 return d
256
Tao Baod1de6f32017-03-01 16:38:48 -0800257
Doug Zongkerc9253822014-02-04 12:17:58 -0800258def LoadBuildProp(read_helper):
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700259 try:
Doug Zongkerc9253822014-02-04 12:17:58 -0800260 data = read_helper("SYSTEM/build.prop")
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700261 except KeyError:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800262 print("Warning: could not find SYSTEM/build.prop in %s" % (zip,))
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700263 data = ""
Michael Runge6e836112014-04-15 17:40:21 -0700264 return LoadDictionaryFromLines(data.split("\n"))
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700265
Tao Baod1de6f32017-03-01 16:38:48 -0800266
Michael Runge6e836112014-04-15 17:40:21 -0700267def LoadDictionaryFromLines(lines):
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700268 d = {}
Michael Runge6e836112014-04-15 17:40:21 -0700269 for line in lines:
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700270 line = line.strip()
Dan Albert8b72aef2015-03-23 19:13:21 -0700271 if not line or line.startswith("#"):
272 continue
Ying Wang114b46f2014-04-15 11:24:00 -0700273 if "=" in line:
274 name, value = line.split("=", 1)
275 d[name] = value
Doug Zongkerc19a8d52010-07-01 15:30:11 -0700276 return d
277
Tao Baod1de6f32017-03-01 16:38:48 -0800278
Tianjie Xucfa86222016-03-07 16:31:19 -0800279def LoadRecoveryFSTab(read_helper, fstab_version, recovery_fstab_path,
280 system_root_image=False):
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700281 class Partition(object):
Tao Baod1de6f32017-03-01 16:38:48 -0800282 def __init__(self, mount_point, fs_type, device, length, context):
Dan Albert8b72aef2015-03-23 19:13:21 -0700283 self.mount_point = mount_point
284 self.fs_type = fs_type
285 self.device = device
286 self.length = length
Tao Bao548eb762015-06-10 12:32:41 -0700287 self.context = context
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700288
289 try:
Tianjie Xucfa86222016-03-07 16:31:19 -0800290 data = read_helper(recovery_fstab_path)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700291 except KeyError:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800292 print("Warning: could not find {}".format(recovery_fstab_path))
Jeff Davidson033fbe22011-10-26 18:08:09 -0700293 data = ""
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700294
Tao Baod1de6f32017-03-01 16:38:48 -0800295 assert fstab_version == 2
296
297 d = {}
298 for line in data.split("\n"):
299 line = line.strip()
300 if not line or line.startswith("#"):
301 continue
302
303 # <src> <mnt_point> <type> <mnt_flags and options> <fs_mgr_flags>
304 pieces = line.split()
305 if len(pieces) != 5:
306 raise ValueError("malformed recovery.fstab line: \"%s\"" % (line,))
307
308 # Ignore entries that are managed by vold.
309 options = pieces[4]
310 if "voldmanaged=" in options:
311 continue
312
313 # It's a good line, parse it.
314 length = 0
315 options = options.split(",")
316 for i in options:
317 if i.startswith("length="):
318 length = int(i[7:])
Doug Zongker086cbb02011-02-17 15:54:20 -0800319 else:
Tao Baod1de6f32017-03-01 16:38:48 -0800320 # Ignore all unknown options in the unified fstab.
Dan Albert8b72aef2015-03-23 19:13:21 -0700321 continue
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800322
Tao Baod1de6f32017-03-01 16:38:48 -0800323 mount_flags = pieces[3]
324 # Honor the SELinux context if present.
325 context = None
326 for i in mount_flags.split(","):
327 if i.startswith("context="):
328 context = i
Doug Zongker086cbb02011-02-17 15:54:20 -0800329
Tao Baod1de6f32017-03-01 16:38:48 -0800330 mount_point = pieces[1]
331 d[mount_point] = Partition(mount_point=mount_point, fs_type=pieces[2],
332 device=pieces[0], length=length, context=context)
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800333
Daniel Rosenberge6853b02015-06-05 17:59:27 -0700334 # / is used for the system mount point when the root directory is included in
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700335 # system. Other areas assume system is always at "/system" so point /system
336 # at /.
Daniel Rosenberge6853b02015-06-05 17:59:27 -0700337 if system_root_image:
338 assert not d.has_key("/system") and d.has_key("/")
339 d["/system"] = d["/"]
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700340 return d
341
342
Doug Zongker37974732010-09-16 17:44:38 -0700343def DumpInfoDict(d):
344 for k, v in sorted(d.items()):
Tao Bao89fbb0f2017-01-10 10:47:58 -0800345 print("%-25s = (%s) %s" % (k, type(v).__name__, v))
Doug Zongkerc19a8d52010-07-01 15:30:11 -0700346
Dan Albert8b72aef2015-03-23 19:13:21 -0700347
Bowgo Tsai3e599ea2017-05-26 18:30:04 +0800348def AppendAVBSigningArgs(cmd, partition):
349 """Append signing arguments for avbtool."""
350 # e.g., "--key path/to/signing_key --algorithm SHA256_RSA4096"
351 key_path = OPTIONS.info_dict.get("avb_" + partition + "_key_path")
352 algorithm = OPTIONS.info_dict.get("avb_" + partition + "_algorithm")
353 if key_path and algorithm:
354 cmd.extend(["--key", key_path, "--algorithm", algorithm])
355
356
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700357def _BuildBootableImage(sourcedir, fs_config_file, info_dict=None,
Tao Baod42e97e2016-11-30 12:11:57 -0800358 has_ramdisk=False, two_step_image=False):
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700359 """Build a bootable image from the specified sourcedir.
Doug Zongkere1c31ba2009-06-23 17:40:35 -0700360
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700361 Take a kernel, cmdline, and optionally a ramdisk directory from the input (in
Tao Baod42e97e2016-11-30 12:11:57 -0800362 'sourcedir'), and turn them into a boot image. 'two_step_image' indicates if
363 we are building a two-step special image (i.e. building a recovery image to
364 be loaded into /boot in two-step OTAs).
365
366 Return the image data, or None if sourcedir does not appear to contains files
367 for building the requested image.
368 """
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700369
370 def make_ramdisk():
371 ramdisk_img = tempfile.NamedTemporaryFile()
372
373 if os.access(fs_config_file, os.F_OK):
374 cmd = ["mkbootfs", "-f", fs_config_file,
375 os.path.join(sourcedir, "RAMDISK")]
376 else:
377 cmd = ["mkbootfs", os.path.join(sourcedir, "RAMDISK")]
378 p1 = Run(cmd, stdout=subprocess.PIPE)
379 p2 = Run(["minigzip"], stdin=p1.stdout, stdout=ramdisk_img.file.fileno())
380
381 p2.wait()
382 p1.wait()
383 assert p1.returncode == 0, "mkbootfs of %s ramdisk failed" % (sourcedir,)
384 assert p2.returncode == 0, "minigzip of %s ramdisk failed" % (sourcedir,)
385
386 return ramdisk_img
387
388 if not os.access(os.path.join(sourcedir, "kernel"), os.F_OK):
389 return None
390
391 if has_ramdisk and not os.access(os.path.join(sourcedir, "RAMDISK"), os.F_OK):
Doug Zongkere1c31ba2009-06-23 17:40:35 -0700392 return None
Doug Zongkereef39442009-04-02 12:14:19 -0700393
Doug Zongkerd5131602012-08-02 14:46:42 -0700394 if info_dict is None:
395 info_dict = OPTIONS.info_dict
396
Doug Zongkereef39442009-04-02 12:14:19 -0700397 img = tempfile.NamedTemporaryFile()
398
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700399 if has_ramdisk:
400 ramdisk_img = make_ramdisk()
Doug Zongkereef39442009-04-02 12:14:19 -0700401
Bjorn Andersson612e2cd2012-11-25 16:53:44 -0800402 # use MKBOOTIMG from environ, or "mkbootimg" if empty or not set
403 mkbootimg = os.getenv('MKBOOTIMG') or "mkbootimg"
404
405 cmd = [mkbootimg, "--kernel", os.path.join(sourcedir, "kernel")]
Doug Zongker38a649f2009-06-17 09:07:09 -0700406
Benoit Fradina45a8682014-07-14 21:00:43 +0200407 fn = os.path.join(sourcedir, "second")
408 if os.access(fn, os.F_OK):
409 cmd.append("--second")
410 cmd.append(fn)
411
Doug Zongker171f1cd2009-06-15 22:36:37 -0700412 fn = os.path.join(sourcedir, "cmdline")
413 if os.access(fn, os.F_OK):
Doug Zongker38a649f2009-06-17 09:07:09 -0700414 cmd.append("--cmdline")
415 cmd.append(open(fn).read().rstrip("\n"))
416
417 fn = os.path.join(sourcedir, "base")
418 if os.access(fn, os.F_OK):
419 cmd.append("--base")
420 cmd.append(open(fn).read().rstrip("\n"))
421
Ying Wang4de6b5b2010-08-25 14:29:34 -0700422 fn = os.path.join(sourcedir, "pagesize")
423 if os.access(fn, os.F_OK):
424 cmd.append("--pagesize")
425 cmd.append(open(fn).read().rstrip("\n"))
426
Doug Zongkerd5131602012-08-02 14:46:42 -0700427 args = info_dict.get("mkbootimg_args", None)
428 if args and args.strip():
Jianxun Zhang09849492013-04-17 15:19:19 -0700429 cmd.extend(shlex.split(args))
Doug Zongkerd5131602012-08-02 14:46:42 -0700430
Sami Tolvanen3303d902016-03-15 16:49:30 +0000431 args = info_dict.get("mkbootimg_version_args", None)
432 if args and args.strip():
433 cmd.extend(shlex.split(args))
434
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700435 if has_ramdisk:
436 cmd.extend(["--ramdisk", ramdisk_img.name])
437
Tao Baod95e9fd2015-03-29 23:07:41 -0700438 img_unsigned = None
439 if info_dict.get("vboot", None):
440 img_unsigned = tempfile.NamedTemporaryFile()
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700441 cmd.extend(["--output", img_unsigned.name])
Tao Baod95e9fd2015-03-29 23:07:41 -0700442 else:
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700443 cmd.extend(["--output", img.name])
Doug Zongker38a649f2009-06-17 09:07:09 -0700444
445 p = Run(cmd, stdout=subprocess.PIPE)
Doug Zongkereef39442009-04-02 12:14:19 -0700446 p.communicate()
Doug Zongkere1c31ba2009-06-23 17:40:35 -0700447 assert p.returncode == 0, "mkbootimg of %s image failed" % (
448 os.path.basename(sourcedir),)
Doug Zongkereef39442009-04-02 12:14:19 -0700449
Sami Tolvanen8b3f08b2015-04-07 15:08:59 +0100450 if (info_dict.get("boot_signer", None) == "true" and
451 info_dict.get("verity_key", None)):
Tao Baod42e97e2016-11-30 12:11:57 -0800452 # Hard-code the path as "/boot" for two-step special recovery image (which
453 # will be loaded into /boot during the two-step OTA).
454 if two_step_image:
455 path = "/boot"
456 else:
457 path = "/" + os.path.basename(sourcedir).lower()
Baligh Uddin601ddea2015-06-09 15:48:14 -0700458 cmd = [OPTIONS.boot_signer_path]
459 cmd.extend(OPTIONS.boot_signer_args)
460 cmd.extend([path, img.name,
461 info_dict["verity_key"] + ".pk8",
462 info_dict["verity_key"] + ".x509.pem", img.name])
Geremy Condra95ebe7a2014-08-19 17:27:56 -0700463 p = Run(cmd, stdout=subprocess.PIPE)
464 p.communicate()
465 assert p.returncode == 0, "boot_signer of %s image failed" % path
466
Tao Baod95e9fd2015-03-29 23:07:41 -0700467 # Sign the image if vboot is non-empty.
468 elif info_dict.get("vboot", None):
469 path = "/" + os.path.basename(sourcedir).lower()
470 img_keyblock = tempfile.NamedTemporaryFile()
Tao Bao4f104d12017-02-17 23:21:31 -0800471 # We have switched from the prebuilt futility binary to using the tool
472 # (futility-host) built from the source. Override the setting in the old
473 # TF.zip.
474 futility = info_dict["futility"]
475 if futility.startswith("prebuilts/"):
476 futility = "futility-host"
477 cmd = [info_dict["vboot_signer_cmd"], futility,
Tao Baod95e9fd2015-03-29 23:07:41 -0700478 img_unsigned.name, info_dict["vboot_key"] + ".vbpubk",
Furquan Shaikh852b8de2015-08-10 11:43:45 -0700479 info_dict["vboot_key"] + ".vbprivk",
480 info_dict["vboot_subkey"] + ".vbprivk",
481 img_keyblock.name,
Tao Baod95e9fd2015-03-29 23:07:41 -0700482 img.name]
483 p = Run(cmd, stdout=subprocess.PIPE)
484 p.communicate()
485 assert p.returncode == 0, "vboot_signer of %s image failed" % path
486
Tao Baof3282b42015-04-01 11:21:55 -0700487 # Clean up the temp files.
488 img_unsigned.close()
489 img_keyblock.close()
490
David Zeuthen2ce63ed2016-09-15 13:43:54 -0400491 # AVB: if enabled, calculate and add hash to boot.img.
Bowgo Tsai3e599ea2017-05-26 18:30:04 +0800492 if info_dict.get("avb_enable") == "true":
Tao Bao3ebfdde2017-05-23 23:06:55 -0700493 avbtool = os.getenv('AVBTOOL') or info_dict["avb_avbtool"]
494 part_size = info_dict["boot_size"]
David Zeuthen2ce63ed2016-09-15 13:43:54 -0400495 cmd = [avbtool, "add_hash_footer", "--image", img.name,
496 "--partition_size", str(part_size), "--partition_name", "boot"]
Bowgo Tsai3e599ea2017-05-26 18:30:04 +0800497 AppendAVBSigningArgs(cmd, "boot")
498 args = info_dict.get("avb_boot_add_hash_footer_args")
David Zeuthen2ce63ed2016-09-15 13:43:54 -0400499 if args and args.strip():
500 cmd.extend(shlex.split(args))
501 p = Run(cmd, stdout=subprocess.PIPE)
502 p.communicate()
503 assert p.returncode == 0, "avbtool add_hash_footer of %s failed" % (
504 os.path.basename(OPTIONS.input_tmp))
David Zeuthend995f4b2016-01-29 16:59:17 -0500505
506 img.seek(os.SEEK_SET, 0)
507 data = img.read()
508
509 if has_ramdisk:
510 ramdisk_img.close()
511 img.close()
512
513 return data
514
515
Doug Zongkerd5131602012-08-02 14:46:42 -0700516def GetBootableImage(name, prebuilt_name, unpack_dir, tree_subdir,
Tao Baod42e97e2016-11-30 12:11:57 -0800517 info_dict=None, two_step_image=False):
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700518 """Return a File object with the desired bootable image.
519
520 Look for it in 'unpack_dir'/BOOTABLE_IMAGES under the name 'prebuilt_name',
521 otherwise look for it under 'unpack_dir'/IMAGES, otherwise construct it from
522 the source files in 'unpack_dir'/'tree_subdir'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700523
Doug Zongker55d93282011-01-25 17:03:34 -0800524 prebuilt_path = os.path.join(unpack_dir, "BOOTABLE_IMAGES", prebuilt_name)
525 if os.path.exists(prebuilt_path):
Tao Bao89fbb0f2017-01-10 10:47:58 -0800526 print("using prebuilt %s from BOOTABLE_IMAGES..." % (prebuilt_name,))
Doug Zongker55d93282011-01-25 17:03:34 -0800527 return File.FromLocalFile(name, prebuilt_path)
Doug Zongker6f1d0312014-08-22 08:07:12 -0700528
529 prebuilt_path = os.path.join(unpack_dir, "IMAGES", prebuilt_name)
530 if os.path.exists(prebuilt_path):
Tao Bao89fbb0f2017-01-10 10:47:58 -0800531 print("using prebuilt %s from IMAGES..." % (prebuilt_name,))
Doug Zongker6f1d0312014-08-22 08:07:12 -0700532 return File.FromLocalFile(name, prebuilt_path)
533
Tao Bao89fbb0f2017-01-10 10:47:58 -0800534 print("building image from target_files %s..." % (tree_subdir,))
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700535
536 if info_dict is None:
537 info_dict = OPTIONS.info_dict
538
539 # With system_root_image == "true", we don't pack ramdisk into the boot image.
Daniel Rosenberg40ef35b2015-11-10 19:21:34 -0800540 # Unless "recovery_as_boot" is specified, in which case we carry the ramdisk
541 # for recovery.
542 has_ramdisk = (info_dict.get("system_root_image") != "true" or
543 prebuilt_name != "boot.img" or
544 info_dict.get("recovery_as_boot") == "true")
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700545
Doug Zongker6f1d0312014-08-22 08:07:12 -0700546 fs_config = "META/" + tree_subdir.lower() + "_filesystem_config.txt"
David Zeuthen2ce63ed2016-09-15 13:43:54 -0400547 data = _BuildBootableImage(os.path.join(unpack_dir, tree_subdir),
548 os.path.join(unpack_dir, fs_config),
Tao Baod42e97e2016-11-30 12:11:57 -0800549 info_dict, has_ramdisk, two_step_image)
Doug Zongker6f1d0312014-08-22 08:07:12 -0700550 if data:
551 return File(name, data)
552 return None
Doug Zongker55d93282011-01-25 17:03:34 -0800553
Doug Zongkereef39442009-04-02 12:14:19 -0700554
Doug Zongker75f17362009-12-08 13:46:44 -0800555def UnzipTemp(filename, pattern=None):
Doug Zongker55d93282011-01-25 17:03:34 -0800556 """Unzip the given archive into a temporary directory and return the name.
557
558 If filename is of the form "foo.zip+bar.zip", unzip foo.zip into a
559 temp dir, then unzip bar.zip into that_dir/BOOTABLE_IMAGES.
560
561 Returns (tempdir, zipobj) where zipobj is a zipfile.ZipFile (of the
562 main file), open for reading.
563 """
Doug Zongkereef39442009-04-02 12:14:19 -0700564
565 tmp = tempfile.mkdtemp(prefix="targetfiles-")
566 OPTIONS.tempfiles.append(tmp)
Doug Zongker55d93282011-01-25 17:03:34 -0800567
568 def unzip_to_dir(filename, dirname):
569 cmd = ["unzip", "-o", "-q", filename, "-d", dirname]
570 if pattern is not None:
Tao Bao6b0b2f92017-03-05 11:38:11 -0800571 cmd.extend(pattern)
Doug Zongker55d93282011-01-25 17:03:34 -0800572 p = Run(cmd, stdout=subprocess.PIPE)
573 p.communicate()
574 if p.returncode != 0:
575 raise ExternalError("failed to unzip input target-files \"%s\"" %
576 (filename,))
577
578 m = re.match(r"^(.*[.]zip)\+(.*[.]zip)$", filename, re.IGNORECASE)
579 if m:
580 unzip_to_dir(m.group(1), tmp)
581 unzip_to_dir(m.group(2), os.path.join(tmp, "BOOTABLE_IMAGES"))
582 filename = m.group(1)
583 else:
584 unzip_to_dir(filename, tmp)
585
586 return tmp, zipfile.ZipFile(filename, "r")
Doug Zongkereef39442009-04-02 12:14:19 -0700587
588
589def GetKeyPasswords(keylist):
590 """Given a list of keys, prompt the user to enter passwords for
591 those which require them. Return a {key: password} dict. password
592 will be None if the key has no password."""
593
Doug Zongker8ce7c252009-05-22 13:34:54 -0700594 no_passwords = []
595 need_passwords = []
T.R. Fullhart37e10522013-03-18 10:31:26 -0700596 key_passwords = {}
Doug Zongkereef39442009-04-02 12:14:19 -0700597 devnull = open("/dev/null", "w+b")
598 for k in sorted(keylist):
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800599 # We don't need a password for things that aren't really keys.
600 if k in SPECIAL_CERT_STRINGS:
Doug Zongker8ce7c252009-05-22 13:34:54 -0700601 no_passwords.append(k)
Doug Zongker43874f82009-04-14 14:05:15 -0700602 continue
603
T.R. Fullhart37e10522013-03-18 10:31:26 -0700604 p = Run(["openssl", "pkcs8", "-in", k+OPTIONS.private_key_suffix,
Doug Zongker602a84e2009-06-18 08:35:12 -0700605 "-inform", "DER", "-nocrypt"],
606 stdin=devnull.fileno(),
607 stdout=devnull.fileno(),
608 stderr=subprocess.STDOUT)
Doug Zongkereef39442009-04-02 12:14:19 -0700609 p.communicate()
610 if p.returncode == 0:
T.R. Fullhart37e10522013-03-18 10:31:26 -0700611 # Definitely an unencrypted key.
Doug Zongker8ce7c252009-05-22 13:34:54 -0700612 no_passwords.append(k)
Doug Zongkereef39442009-04-02 12:14:19 -0700613 else:
T.R. Fullhart37e10522013-03-18 10:31:26 -0700614 p = Run(["openssl", "pkcs8", "-in", k+OPTIONS.private_key_suffix,
615 "-inform", "DER", "-passin", "pass:"],
616 stdin=devnull.fileno(),
617 stdout=devnull.fileno(),
618 stderr=subprocess.PIPE)
Dan Albert8b72aef2015-03-23 19:13:21 -0700619 _, stderr = p.communicate()
T.R. Fullhart37e10522013-03-18 10:31:26 -0700620 if p.returncode == 0:
621 # Encrypted key with empty string as password.
622 key_passwords[k] = ''
623 elif stderr.startswith('Error decrypting key'):
624 # Definitely encrypted key.
625 # It would have said "Error reading key" if it didn't parse correctly.
626 need_passwords.append(k)
627 else:
628 # Potentially, a type of key that openssl doesn't understand.
629 # We'll let the routines in signapk.jar handle it.
630 no_passwords.append(k)
Doug Zongkereef39442009-04-02 12:14:19 -0700631 devnull.close()
Doug Zongker8ce7c252009-05-22 13:34:54 -0700632
T.R. Fullhart37e10522013-03-18 10:31:26 -0700633 key_passwords.update(PasswordManager().GetPasswords(need_passwords))
Doug Zongker8ce7c252009-05-22 13:34:54 -0700634 key_passwords.update(dict.fromkeys(no_passwords, None))
Doug Zongkereef39442009-04-02 12:14:19 -0700635 return key_passwords
636
637
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800638def GetMinSdkVersion(apk_name):
639 """Get the minSdkVersion delared in the APK. This can be both a decimal number
640 (API Level) or a codename.
641 """
642
643 p = Run(["aapt", "dump", "badging", apk_name], stdout=subprocess.PIPE)
644 output, err = p.communicate()
645 if err:
646 raise ExternalError("Failed to obtain minSdkVersion: aapt return code %s"
647 % (p.returncode,))
648
649 for line in output.split("\n"):
650 # Looking for lines such as sdkVersion:'23' or sdkVersion:'M'
651 m = re.match(r'sdkVersion:\'([^\']*)\'', line)
652 if m:
653 return m.group(1)
654 raise ExternalError("No minSdkVersion returned by aapt")
655
656
657def GetMinSdkVersionInt(apk_name, codename_to_api_level_map):
658 """Get the minSdkVersion declared in the APK as a number (API Level). If
659 minSdkVersion is set to a codename, it is translated to a number using the
660 provided map.
661 """
662
663 version = GetMinSdkVersion(apk_name)
664 try:
665 return int(version)
666 except ValueError:
667 # Not a decimal number. Codename?
668 if version in codename_to_api_level_map:
669 return codename_to_api_level_map[version]
670 else:
671 raise ExternalError("Unknown minSdkVersion: '%s'. Known codenames: %s"
672 % (version, codename_to_api_level_map))
673
674
675def SignFile(input_name, output_name, key, password, min_api_level=None,
676 codename_to_api_level_map=dict(),
677 whole_file=False):
Doug Zongkereef39442009-04-02 12:14:19 -0700678 """Sign the input_name zip/jar/apk, producing output_name. Use the
679 given key and password (the latter may be None if the key does not
680 have a password.
681
Doug Zongker951495f2009-08-14 12:44:19 -0700682 If whole_file is true, use the "-w" option to SignApk to embed a
683 signature that covers the whole file in the archive comment of the
684 zip file.
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800685
686 min_api_level is the API Level (int) of the oldest platform this file may end
687 up on. If not specified for an APK, the API Level is obtained by interpreting
688 the minSdkVersion attribute of the APK's AndroidManifest.xml.
689
690 codename_to_api_level_map is needed to translate the codename which may be
691 encountered as the APK's minSdkVersion.
Doug Zongkereef39442009-04-02 12:14:19 -0700692 """
Doug Zongker951495f2009-08-14 12:44:19 -0700693
Alex Klyubin9667b182015-12-10 13:38:50 -0800694 java_library_path = os.path.join(
695 OPTIONS.search_path, OPTIONS.signapk_shared_library_path)
696
Tao Baoe95540e2016-11-08 12:08:53 -0800697 cmd = ([OPTIONS.java_path] + OPTIONS.java_args +
698 ["-Djava.library.path=" + java_library_path,
699 "-jar", os.path.join(OPTIONS.search_path, OPTIONS.signapk_path)] +
700 OPTIONS.extra_signapk_args)
Doug Zongker951495f2009-08-14 12:44:19 -0700701 if whole_file:
702 cmd.append("-w")
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800703
704 min_sdk_version = min_api_level
705 if min_sdk_version is None:
706 if not whole_file:
707 min_sdk_version = GetMinSdkVersionInt(
708 input_name, codename_to_api_level_map)
709 if min_sdk_version is not None:
710 cmd.extend(["--min-sdk-version", str(min_sdk_version)])
711
T.R. Fullhart37e10522013-03-18 10:31:26 -0700712 cmd.extend([key + OPTIONS.public_key_suffix,
713 key + OPTIONS.private_key_suffix,
Alex Klyubineb756d72015-12-04 09:21:08 -0800714 input_name, output_name])
Doug Zongker951495f2009-08-14 12:44:19 -0700715
716 p = Run(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
Doug Zongkereef39442009-04-02 12:14:19 -0700717 if password is not None:
718 password += "\n"
719 p.communicate(password)
720 if p.returncode != 0:
721 raise ExternalError("signapk.jar failed: return code %s" % (p.returncode,))
722
Doug Zongkereef39442009-04-02 12:14:19 -0700723
Doug Zongker37974732010-09-16 17:44:38 -0700724def CheckSize(data, target, info_dict):
Doug Zongkereef39442009-04-02 12:14:19 -0700725 """Check the data string passed against the max size limit, if
726 any, for the given target. Raise exception if the data is too big.
727 Print a warning if the data is nearing the maximum size."""
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700728
Dan Albert8b72aef2015-03-23 19:13:21 -0700729 if target.endswith(".img"):
730 target = target[:-4]
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700731 mount_point = "/" + target
732
Ying Wangf8824af2014-06-03 14:07:27 -0700733 fs_type = None
734 limit = None
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700735 if info_dict["fstab"]:
Dan Albert8b72aef2015-03-23 19:13:21 -0700736 if mount_point == "/userdata":
737 mount_point = "/data"
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700738 p = info_dict["fstab"][mount_point]
739 fs_type = p.fs_type
Andrew Boie0f9aec82012-02-14 09:32:52 -0800740 device = p.device
741 if "/" in device:
742 device = device[device.rfind("/")+1:]
743 limit = info_dict.get(device + "_size", None)
Dan Albert8b72aef2015-03-23 19:13:21 -0700744 if not fs_type or not limit:
745 return
Doug Zongkereef39442009-04-02 12:14:19 -0700746
Andrew Boie0f9aec82012-02-14 09:32:52 -0800747 size = len(data)
748 pct = float(size) * 100.0 / limit
749 msg = "%s size (%d) is %.2f%% of limit (%d)" % (target, size, pct, limit)
750 if pct >= 99.0:
751 raise ExternalError(msg)
752 elif pct >= 95.0:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800753 print("\n WARNING: %s\n" % (msg,))
Andrew Boie0f9aec82012-02-14 09:32:52 -0800754 elif OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800755 print(" ", msg)
Doug Zongkereef39442009-04-02 12:14:19 -0700756
757
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800758def ReadApkCerts(tf_zip):
759 """Given a target_files ZipFile, parse the META/apkcerts.txt file
760 and return a {package: cert} dict."""
761 certmap = {}
762 for line in tf_zip.read("META/apkcerts.txt").split("\n"):
763 line = line.strip()
Dan Albert8b72aef2015-03-23 19:13:21 -0700764 if not line:
765 continue
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800766 m = re.match(r'^name="(.*)"\s+certificate="(.*)"\s+'
767 r'private_key="(.*)"$', line)
768 if m:
769 name, cert, privkey = m.groups()
T.R. Fullhart37e10522013-03-18 10:31:26 -0700770 public_key_suffix_len = len(OPTIONS.public_key_suffix)
771 private_key_suffix_len = len(OPTIONS.private_key_suffix)
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800772 if cert in SPECIAL_CERT_STRINGS and not privkey:
773 certmap[name] = cert
T.R. Fullhart37e10522013-03-18 10:31:26 -0700774 elif (cert.endswith(OPTIONS.public_key_suffix) and
775 privkey.endswith(OPTIONS.private_key_suffix) and
776 cert[:-public_key_suffix_len] == privkey[:-private_key_suffix_len]):
777 certmap[name] = cert[:-public_key_suffix_len]
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800778 else:
779 raise ValueError("failed to parse line from apkcerts.txt:\n" + line)
780 return certmap
781
782
Doug Zongkereef39442009-04-02 12:14:19 -0700783COMMON_DOCSTRING = """
784 -p (--path) <dir>
Doug Zongker602a84e2009-06-18 08:35:12 -0700785 Prepend <dir>/bin to the list of places to search for binaries
786 run by this script, and expect to find jars in <dir>/framework.
Doug Zongkereef39442009-04-02 12:14:19 -0700787
Doug Zongker05d3dea2009-06-22 11:32:31 -0700788 -s (--device_specific) <file>
789 Path to the python module containing device-specific
790 releasetools code.
791
Doug Zongker8bec09e2009-11-30 15:37:14 -0800792 -x (--extra) <key=value>
793 Add a key/value pair to the 'extras' dict, which device-specific
794 extension code may look at.
795
Doug Zongkereef39442009-04-02 12:14:19 -0700796 -v (--verbose)
797 Show command lines being executed.
798
799 -h (--help)
800 Display this usage message and exit.
801"""
802
803def Usage(docstring):
Tao Bao89fbb0f2017-01-10 10:47:58 -0800804 print(docstring.rstrip("\n"))
805 print(COMMON_DOCSTRING)
Doug Zongkereef39442009-04-02 12:14:19 -0700806
807
808def ParseOptions(argv,
809 docstring,
810 extra_opts="", extra_long_opts=(),
811 extra_option_handler=None):
812 """Parse the options in argv and return any arguments that aren't
813 flags. docstring is the calling module's docstring, to be displayed
814 for errors and -h. extra_opts and extra_long_opts are for flags
815 defined by the caller, which are processed by passing them to
816 extra_option_handler."""
817
818 try:
819 opts, args = getopt.getopt(
Doug Zongker8bec09e2009-11-30 15:37:14 -0800820 argv, "hvp:s:x:" + extra_opts,
Alex Klyubin9667b182015-12-10 13:38:50 -0800821 ["help", "verbose", "path=", "signapk_path=",
822 "signapk_shared_library_path=", "extra_signapk_args=",
Baligh Uddinbdc2e312014-09-05 17:36:20 -0700823 "java_path=", "java_args=", "public_key_suffix=",
Baligh Uddin601ddea2015-06-09 15:48:14 -0700824 "private_key_suffix=", "boot_signer_path=", "boot_signer_args=",
825 "verity_signer_path=", "verity_signer_args=", "device_specific=",
Baligh Uddine2048682014-11-20 09:52:05 -0800826 "extra="] +
T.R. Fullhart37e10522013-03-18 10:31:26 -0700827 list(extra_long_opts))
Dan Albert8b72aef2015-03-23 19:13:21 -0700828 except getopt.GetoptError as err:
Doug Zongkereef39442009-04-02 12:14:19 -0700829 Usage(docstring)
Tao Bao89fbb0f2017-01-10 10:47:58 -0800830 print("**", str(err), "**")
Doug Zongkereef39442009-04-02 12:14:19 -0700831 sys.exit(2)
832
Doug Zongkereef39442009-04-02 12:14:19 -0700833 for o, a in opts:
834 if o in ("-h", "--help"):
835 Usage(docstring)
836 sys.exit()
837 elif o in ("-v", "--verbose"):
838 OPTIONS.verbose = True
839 elif o in ("-p", "--path"):
Doug Zongker602a84e2009-06-18 08:35:12 -0700840 OPTIONS.search_path = a
T.R. Fullhart37e10522013-03-18 10:31:26 -0700841 elif o in ("--signapk_path",):
842 OPTIONS.signapk_path = a
Alex Klyubin9667b182015-12-10 13:38:50 -0800843 elif o in ("--signapk_shared_library_path",):
844 OPTIONS.signapk_shared_library_path = a
T.R. Fullhart37e10522013-03-18 10:31:26 -0700845 elif o in ("--extra_signapk_args",):
846 OPTIONS.extra_signapk_args = shlex.split(a)
847 elif o in ("--java_path",):
848 OPTIONS.java_path = a
Baligh Uddin339ee492014-09-05 11:18:07 -0700849 elif o in ("--java_args",):
Tao Baoe95540e2016-11-08 12:08:53 -0800850 OPTIONS.java_args = shlex.split(a)
T.R. Fullhart37e10522013-03-18 10:31:26 -0700851 elif o in ("--public_key_suffix",):
852 OPTIONS.public_key_suffix = a
853 elif o in ("--private_key_suffix",):
854 OPTIONS.private_key_suffix = a
Baligh Uddine2048682014-11-20 09:52:05 -0800855 elif o in ("--boot_signer_path",):
856 OPTIONS.boot_signer_path = a
Baligh Uddin601ddea2015-06-09 15:48:14 -0700857 elif o in ("--boot_signer_args",):
858 OPTIONS.boot_signer_args = shlex.split(a)
859 elif o in ("--verity_signer_path",):
860 OPTIONS.verity_signer_path = a
861 elif o in ("--verity_signer_args",):
862 OPTIONS.verity_signer_args = shlex.split(a)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700863 elif o in ("-s", "--device_specific"):
864 OPTIONS.device_specific = a
Doug Zongker5ecba702009-12-03 16:36:20 -0800865 elif o in ("-x", "--extra"):
Doug Zongker8bec09e2009-11-30 15:37:14 -0800866 key, value = a.split("=", 1)
867 OPTIONS.extras[key] = value
Doug Zongkereef39442009-04-02 12:14:19 -0700868 else:
869 if extra_option_handler is None or not extra_option_handler(o, a):
870 assert False, "unknown option \"%s\"" % (o,)
871
Doug Zongker85448772014-09-09 14:59:20 -0700872 if OPTIONS.search_path:
873 os.environ["PATH"] = (os.path.join(OPTIONS.search_path, "bin") +
874 os.pathsep + os.environ["PATH"])
Doug Zongkereef39442009-04-02 12:14:19 -0700875
876 return args
877
878
Tao Bao4c851b12016-09-19 13:54:38 -0700879def MakeTempFile(prefix='tmp', suffix=''):
Doug Zongkerfc44a512014-08-26 13:10:25 -0700880 """Make a temp file and add it to the list of things to be deleted
881 when Cleanup() is called. Return the filename."""
882 fd, fn = tempfile.mkstemp(prefix=prefix, suffix=suffix)
883 os.close(fd)
884 OPTIONS.tempfiles.append(fn)
885 return fn
886
887
Doug Zongkereef39442009-04-02 12:14:19 -0700888def Cleanup():
889 for i in OPTIONS.tempfiles:
890 if os.path.isdir(i):
891 shutil.rmtree(i)
892 else:
893 os.remove(i)
Doug Zongker8ce7c252009-05-22 13:34:54 -0700894
895
896class PasswordManager(object):
897 def __init__(self):
898 self.editor = os.getenv("EDITOR", None)
899 self.pwfile = os.getenv("ANDROID_PW_FILE", None)
900
901 def GetPasswords(self, items):
902 """Get passwords corresponding to each string in 'items',
903 returning a dict. (The dict may have keys in addition to the
904 values in 'items'.)
905
906 Uses the passwords in $ANDROID_PW_FILE if available, letting the
907 user edit that file to add more needed passwords. If no editor is
908 available, or $ANDROID_PW_FILE isn't define, prompts the user
909 interactively in the ordinary way.
910 """
911
912 current = self.ReadFile()
913
914 first = True
915 while True:
916 missing = []
917 for i in items:
918 if i not in current or not current[i]:
919 missing.append(i)
920 # Are all the passwords already in the file?
Dan Albert8b72aef2015-03-23 19:13:21 -0700921 if not missing:
922 return current
Doug Zongker8ce7c252009-05-22 13:34:54 -0700923
924 for i in missing:
925 current[i] = ""
926
927 if not first:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800928 print("key file %s still missing some passwords." % (self.pwfile,))
Doug Zongker8ce7c252009-05-22 13:34:54 -0700929 answer = raw_input("try to edit again? [y]> ").strip()
930 if answer and answer[0] not in 'yY':
931 raise RuntimeError("key passwords unavailable")
932 first = False
933
934 current = self.UpdateAndReadFile(current)
935
Dan Albert8b72aef2015-03-23 19:13:21 -0700936 def PromptResult(self, current): # pylint: disable=no-self-use
Doug Zongker8ce7c252009-05-22 13:34:54 -0700937 """Prompt the user to enter a value (password) for each key in
938 'current' whose value is fales. Returns a new dict with all the
939 values.
940 """
941 result = {}
942 for k, v in sorted(current.iteritems()):
943 if v:
944 result[k] = v
945 else:
946 while True:
Dan Albert8b72aef2015-03-23 19:13:21 -0700947 result[k] = getpass.getpass(
948 "Enter password for %s key> " % k).strip()
949 if result[k]:
950 break
Doug Zongker8ce7c252009-05-22 13:34:54 -0700951 return result
952
953 def UpdateAndReadFile(self, current):
954 if not self.editor or not self.pwfile:
955 return self.PromptResult(current)
956
957 f = open(self.pwfile, "w")
Dan Albert8b72aef2015-03-23 19:13:21 -0700958 os.chmod(self.pwfile, 0o600)
Doug Zongker8ce7c252009-05-22 13:34:54 -0700959 f.write("# Enter key passwords between the [[[ ]]] brackets.\n")
960 f.write("# (Additional spaces are harmless.)\n\n")
961
962 first_line = None
Dan Albert8b72aef2015-03-23 19:13:21 -0700963 sorted_list = sorted([(not v, k, v) for (k, v) in current.iteritems()])
964 for i, (_, k, v) in enumerate(sorted_list):
Doug Zongker8ce7c252009-05-22 13:34:54 -0700965 f.write("[[[ %s ]]] %s\n" % (v, k))
966 if not v and first_line is None:
967 # position cursor on first line with no password.
968 first_line = i + 4
969 f.close()
970
971 p = Run([self.editor, "+%d" % (first_line,), self.pwfile])
972 _, _ = p.communicate()
973
974 return self.ReadFile()
975
976 def ReadFile(self):
977 result = {}
Dan Albert8b72aef2015-03-23 19:13:21 -0700978 if self.pwfile is None:
979 return result
Doug Zongker8ce7c252009-05-22 13:34:54 -0700980 try:
981 f = open(self.pwfile, "r")
982 for line in f:
983 line = line.strip()
Dan Albert8b72aef2015-03-23 19:13:21 -0700984 if not line or line[0] == '#':
985 continue
Doug Zongker8ce7c252009-05-22 13:34:54 -0700986 m = re.match(r"^\[\[\[\s*(.*?)\s*\]\]\]\s*(\S+)$", line)
987 if not m:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800988 print("failed to parse password file: ", line)
Doug Zongker8ce7c252009-05-22 13:34:54 -0700989 else:
990 result[m.group(2)] = m.group(1)
991 f.close()
Dan Albert8b72aef2015-03-23 19:13:21 -0700992 except IOError as e:
Doug Zongker8ce7c252009-05-22 13:34:54 -0700993 if e.errno != errno.ENOENT:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800994 print("error reading password file: ", str(e))
Doug Zongker8ce7c252009-05-22 13:34:54 -0700995 return result
Doug Zongker048e7ca2009-06-15 14:31:53 -0700996
997
Dan Albert8e0178d2015-01-27 15:53:15 -0800998def ZipWrite(zip_file, filename, arcname=None, perms=0o644,
999 compress_type=None):
1000 import datetime
1001
1002 # http://b/18015246
1003 # Python 2.7's zipfile implementation wrongly thinks that zip64 is required
1004 # for files larger than 2GiB. We can work around this by adjusting their
1005 # limit. Note that `zipfile.writestr()` will not work for strings larger than
1006 # 2GiB. The Python interpreter sometimes rejects strings that large (though
1007 # it isn't clear to me exactly what circumstances cause this).
1008 # `zipfile.write()` must be used directly to work around this.
1009 #
1010 # This mess can be avoided if we port to python3.
1011 saved_zip64_limit = zipfile.ZIP64_LIMIT
1012 zipfile.ZIP64_LIMIT = (1 << 32) - 1
1013
1014 if compress_type is None:
1015 compress_type = zip_file.compression
1016 if arcname is None:
1017 arcname = filename
1018
1019 saved_stat = os.stat(filename)
1020
1021 try:
1022 # `zipfile.write()` doesn't allow us to pass ZipInfo, so just modify the
1023 # file to be zipped and reset it when we're done.
1024 os.chmod(filename, perms)
1025
1026 # Use a fixed timestamp so the output is repeatable.
1027 epoch = datetime.datetime.fromtimestamp(0)
1028 timestamp = (datetime.datetime(2009, 1, 1) - epoch).total_seconds()
1029 os.utime(filename, (timestamp, timestamp))
1030
1031 zip_file.write(filename, arcname=arcname, compress_type=compress_type)
1032 finally:
1033 os.chmod(filename, saved_stat.st_mode)
1034 os.utime(filename, (saved_stat.st_atime, saved_stat.st_mtime))
1035 zipfile.ZIP64_LIMIT = saved_zip64_limit
1036
1037
Tao Bao58c1b962015-05-20 09:32:18 -07001038def ZipWriteStr(zip_file, zinfo_or_arcname, data, perms=None,
Tao Baof3282b42015-04-01 11:21:55 -07001039 compress_type=None):
1040 """Wrap zipfile.writestr() function to work around the zip64 limit.
1041
1042 Even with the ZIP64_LIMIT workaround, it won't allow writing a string
1043 longer than 2GiB. It gives 'OverflowError: size does not fit in an int'
1044 when calling crc32(bytes).
1045
1046 But it still works fine to write a shorter string into a large zip file.
1047 We should use ZipWrite() whenever possible, and only use ZipWriteStr()
1048 when we know the string won't be too long.
1049 """
1050
1051 saved_zip64_limit = zipfile.ZIP64_LIMIT
1052 zipfile.ZIP64_LIMIT = (1 << 32) - 1
1053
1054 if not isinstance(zinfo_or_arcname, zipfile.ZipInfo):
1055 zinfo = zipfile.ZipInfo(filename=zinfo_or_arcname)
Dan Albert8b72aef2015-03-23 19:13:21 -07001056 zinfo.compress_type = zip_file.compression
Tao Bao58c1b962015-05-20 09:32:18 -07001057 if perms is None:
Tao Bao2a410582015-07-10 17:18:23 -07001058 perms = 0o100644
Geremy Condra36bd3652014-02-06 19:45:10 -08001059 else:
Tao Baof3282b42015-04-01 11:21:55 -07001060 zinfo = zinfo_or_arcname
1061
1062 # If compress_type is given, it overrides the value in zinfo.
1063 if compress_type is not None:
1064 zinfo.compress_type = compress_type
1065
Tao Bao58c1b962015-05-20 09:32:18 -07001066 # If perms is given, it has a priority.
1067 if perms is not None:
Tao Bao2a410582015-07-10 17:18:23 -07001068 # If perms doesn't set the file type, mark it as a regular file.
1069 if perms & 0o770000 == 0:
1070 perms |= 0o100000
Tao Bao58c1b962015-05-20 09:32:18 -07001071 zinfo.external_attr = perms << 16
1072
Tao Baof3282b42015-04-01 11:21:55 -07001073 # Use a fixed timestamp so the output is repeatable.
Tao Baof3282b42015-04-01 11:21:55 -07001074 zinfo.date_time = (2009, 1, 1, 0, 0, 0)
1075
Dan Albert8b72aef2015-03-23 19:13:21 -07001076 zip_file.writestr(zinfo, data)
Tao Baof3282b42015-04-01 11:21:55 -07001077 zipfile.ZIP64_LIMIT = saved_zip64_limit
1078
1079
1080def ZipClose(zip_file):
1081 # http://b/18015246
1082 # zipfile also refers to ZIP64_LIMIT during close() when it writes out the
1083 # central directory.
1084 saved_zip64_limit = zipfile.ZIP64_LIMIT
1085 zipfile.ZIP64_LIMIT = (1 << 32) - 1
1086
1087 zip_file.close()
1088
1089 zipfile.ZIP64_LIMIT = saved_zip64_limit
Doug Zongker05d3dea2009-06-22 11:32:31 -07001090
1091
1092class DeviceSpecificParams(object):
1093 module = None
1094 def __init__(self, **kwargs):
1095 """Keyword arguments to the constructor become attributes of this
1096 object, which is passed to all functions in the device-specific
1097 module."""
1098 for k, v in kwargs.iteritems():
1099 setattr(self, k, v)
Doug Zongker8bec09e2009-11-30 15:37:14 -08001100 self.extras = OPTIONS.extras
Doug Zongker05d3dea2009-06-22 11:32:31 -07001101
1102 if self.module is None:
1103 path = OPTIONS.device_specific
Dan Albert8b72aef2015-03-23 19:13:21 -07001104 if not path:
1105 return
Doug Zongker8e2f2b92009-06-24 14:34:57 -07001106 try:
1107 if os.path.isdir(path):
1108 info = imp.find_module("releasetools", [path])
1109 else:
1110 d, f = os.path.split(path)
1111 b, x = os.path.splitext(f)
1112 if x == ".py":
1113 f = b
1114 info = imp.find_module(f, [d])
Tao Bao89fbb0f2017-01-10 10:47:58 -08001115 print("loaded device-specific extensions from", path)
Doug Zongker8e2f2b92009-06-24 14:34:57 -07001116 self.module = imp.load_module("device_specific", *info)
1117 except ImportError:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001118 print("unable to load device-specific module; assuming none")
Doug Zongker05d3dea2009-06-22 11:32:31 -07001119
1120 def _DoCall(self, function_name, *args, **kwargs):
1121 """Call the named function in the device-specific module, passing
1122 the given args and kwargs. The first argument to the call will be
1123 the DeviceSpecific object itself. If there is no module, or the
1124 module does not define the function, return the value of the
1125 'default' kwarg (which itself defaults to None)."""
1126 if self.module is None or not hasattr(self.module, function_name):
1127 return kwargs.get("default", None)
1128 return getattr(self.module, function_name)(*((self,) + args), **kwargs)
1129
1130 def FullOTA_Assertions(self):
1131 """Called after emitting the block of assertions at the top of a
1132 full OTA package. Implementations can add whatever additional
1133 assertions they like."""
1134 return self._DoCall("FullOTA_Assertions")
1135
Doug Zongkere5ff5902012-01-17 10:55:37 -08001136 def FullOTA_InstallBegin(self):
1137 """Called at the start of full OTA installation."""
1138 return self._DoCall("FullOTA_InstallBegin")
1139
Doug Zongker05d3dea2009-06-22 11:32:31 -07001140 def FullOTA_InstallEnd(self):
1141 """Called at the end of full OTA installation; typically this is
1142 used to install the image for the device's baseband processor."""
1143 return self._DoCall("FullOTA_InstallEnd")
1144
1145 def IncrementalOTA_Assertions(self):
1146 """Called after emitting the block of assertions at the top of an
1147 incremental OTA package. Implementations can add whatever
1148 additional assertions they like."""
1149 return self._DoCall("IncrementalOTA_Assertions")
1150
Doug Zongkere5ff5902012-01-17 10:55:37 -08001151 def IncrementalOTA_VerifyBegin(self):
1152 """Called at the start of the verification phase of incremental
1153 OTA installation; additional checks can be placed here to abort
1154 the script before any changes are made."""
1155 return self._DoCall("IncrementalOTA_VerifyBegin")
1156
Doug Zongker05d3dea2009-06-22 11:32:31 -07001157 def IncrementalOTA_VerifyEnd(self):
1158 """Called at the end of the verification phase of incremental OTA
1159 installation; additional checks can be placed here to abort the
1160 script before any changes are made."""
1161 return self._DoCall("IncrementalOTA_VerifyEnd")
1162
Doug Zongkere5ff5902012-01-17 10:55:37 -08001163 def IncrementalOTA_InstallBegin(self):
1164 """Called at the start of incremental OTA installation (after
1165 verification is complete)."""
1166 return self._DoCall("IncrementalOTA_InstallBegin")
1167
Doug Zongker05d3dea2009-06-22 11:32:31 -07001168 def IncrementalOTA_InstallEnd(self):
1169 """Called at the end of incremental OTA installation; typically
1170 this is used to install the image for the device's baseband
1171 processor."""
1172 return self._DoCall("IncrementalOTA_InstallEnd")
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001173
Tao Bao9bc6bb22015-11-09 16:58:28 -08001174 def VerifyOTA_Assertions(self):
1175 return self._DoCall("VerifyOTA_Assertions")
1176
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001177class File(object):
YOUNG HO CHAccc5c402016-10-13 13:40:46 +09001178 def __init__(self, name, data, compress_size = None):
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001179 self.name = name
1180 self.data = data
1181 self.size = len(data)
YOUNG HO CHAccc5c402016-10-13 13:40:46 +09001182 self.compress_size = compress_size or self.size
Doug Zongker55d93282011-01-25 17:03:34 -08001183 self.sha1 = sha1(data).hexdigest()
1184
1185 @classmethod
1186 def FromLocalFile(cls, name, diskname):
1187 f = open(diskname, "rb")
1188 data = f.read()
1189 f.close()
1190 return File(name, data)
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001191
1192 def WriteToTemp(self):
1193 t = tempfile.NamedTemporaryFile()
1194 t.write(self.data)
1195 t.flush()
1196 return t
1197
Dan Willemsen2ee00d52017-03-05 19:51:56 -08001198 def WriteToDir(self, d):
1199 with open(os.path.join(d, self.name), "wb") as fp:
1200 fp.write(self.data)
1201
Geremy Condra36bd3652014-02-06 19:45:10 -08001202 def AddToZip(self, z, compression=None):
Tao Baof3282b42015-04-01 11:21:55 -07001203 ZipWriteStr(z, self.name, self.data, compress_type=compression)
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001204
1205DIFF_PROGRAM_BY_EXT = {
1206 ".gz" : "imgdiff",
1207 ".zip" : ["imgdiff", "-z"],
1208 ".jar" : ["imgdiff", "-z"],
1209 ".apk" : ["imgdiff", "-z"],
1210 ".img" : "imgdiff",
1211 }
1212
1213class Difference(object):
Doug Zongker24cd2802012-08-14 16:36:15 -07001214 def __init__(self, tf, sf, diff_program=None):
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001215 self.tf = tf
1216 self.sf = sf
1217 self.patch = None
Doug Zongker24cd2802012-08-14 16:36:15 -07001218 self.diff_program = diff_program
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001219
1220 def ComputePatch(self):
1221 """Compute the patch (as a string of data) needed to turn sf into
1222 tf. Returns the same tuple as GetPatch()."""
1223
1224 tf = self.tf
1225 sf = self.sf
1226
Doug Zongker24cd2802012-08-14 16:36:15 -07001227 if self.diff_program:
1228 diff_program = self.diff_program
1229 else:
1230 ext = os.path.splitext(tf.name)[1]
1231 diff_program = DIFF_PROGRAM_BY_EXT.get(ext, "bsdiff")
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001232
1233 ttemp = tf.WriteToTemp()
1234 stemp = sf.WriteToTemp()
1235
1236 ext = os.path.splitext(tf.name)[1]
1237
1238 try:
1239 ptemp = tempfile.NamedTemporaryFile()
1240 if isinstance(diff_program, list):
1241 cmd = copy.copy(diff_program)
1242 else:
1243 cmd = [diff_program]
1244 cmd.append(stemp.name)
1245 cmd.append(ttemp.name)
1246 cmd.append(ptemp.name)
1247 p = Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Doug Zongkerf8340082014-08-05 10:39:37 -07001248 err = []
1249 def run():
1250 _, e = p.communicate()
Dan Albert8b72aef2015-03-23 19:13:21 -07001251 if e:
1252 err.append(e)
Doug Zongkerf8340082014-08-05 10:39:37 -07001253 th = threading.Thread(target=run)
1254 th.start()
1255 th.join(timeout=300) # 5 mins
1256 if th.is_alive():
Tao Bao89fbb0f2017-01-10 10:47:58 -08001257 print("WARNING: diff command timed out")
Doug Zongkerf8340082014-08-05 10:39:37 -07001258 p.terminate()
1259 th.join(5)
1260 if th.is_alive():
1261 p.kill()
1262 th.join()
1263
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001264 if err or p.returncode != 0:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001265 print("WARNING: failure running %s:\n%s\n" % (
1266 diff_program, "".join(err)))
Doug Zongkerf8340082014-08-05 10:39:37 -07001267 self.patch = None
1268 return None, None, None
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001269 diff = ptemp.read()
1270 finally:
1271 ptemp.close()
1272 stemp.close()
1273 ttemp.close()
1274
1275 self.patch = diff
1276 return self.tf, self.sf, self.patch
1277
1278
1279 def GetPatch(self):
1280 """Return a tuple (target_file, source_file, patch_data).
1281 patch_data may be None if ComputePatch hasn't been called, or if
1282 computing the patch failed."""
1283 return self.tf, self.sf, self.patch
1284
1285
1286def ComputeDifferences(diffs):
1287 """Call ComputePatch on all the Difference objects in 'diffs'."""
Tao Bao89fbb0f2017-01-10 10:47:58 -08001288 print(len(diffs), "diffs to compute")
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001289
1290 # Do the largest files first, to try and reduce the long-pole effect.
1291 by_size = [(i.tf.size, i) for i in diffs]
1292 by_size.sort(reverse=True)
1293 by_size = [i[1] for i in by_size]
1294
1295 lock = threading.Lock()
1296 diff_iter = iter(by_size) # accessed under lock
1297
1298 def worker():
1299 try:
1300 lock.acquire()
1301 for d in diff_iter:
1302 lock.release()
1303 start = time.time()
1304 d.ComputePatch()
1305 dur = time.time() - start
1306 lock.acquire()
1307
1308 tf, sf, patch = d.GetPatch()
1309 if sf.name == tf.name:
1310 name = tf.name
1311 else:
1312 name = "%s (%s)" % (tf.name, sf.name)
1313 if patch is None:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001314 print("patching failed! %s" % (name,))
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001315 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001316 print("%8.2f sec %8d / %8d bytes (%6.2f%%) %s" % (
1317 dur, len(patch), tf.size, 100.0 * len(patch) / tf.size, name))
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001318 lock.release()
Dan Albert8b72aef2015-03-23 19:13:21 -07001319 except Exception as e:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001320 print(e)
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001321 raise
1322
1323 # start worker threads; wait for them all to finish.
1324 threads = [threading.Thread(target=worker)
1325 for i in range(OPTIONS.worker_threads)]
1326 for th in threads:
1327 th.start()
1328 while threads:
1329 threads.pop().join()
Doug Zongker96a57e72010-09-26 14:57:41 -07001330
1331
Dan Albert8b72aef2015-03-23 19:13:21 -07001332class BlockDifference(object):
1333 def __init__(self, partition, tgt, src=None, check_first_block=False,
Tao Bao293fd132016-06-11 12:19:23 -07001334 version=None, disable_imgdiff=False):
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001335 self.tgt = tgt
1336 self.src = src
1337 self.partition = partition
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001338 self.check_first_block = check_first_block
Tao Bao293fd132016-06-11 12:19:23 -07001339 self.disable_imgdiff = disable_imgdiff
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001340
Tao Baodd2a5892015-03-12 12:32:37 -07001341 if version is None:
1342 version = 1
1343 if OPTIONS.info_dict:
1344 version = max(
1345 int(i) for i in
1346 OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(","))
Tao Bao8fad03e2017-03-01 14:36:26 -08001347 assert version >= 3
Tao Baodd2a5892015-03-12 12:32:37 -07001348 self.version = version
Doug Zongker62338182014-09-08 08:29:55 -07001349
1350 b = blockimgdiff.BlockImageDiff(tgt, src, threads=OPTIONS.worker_threads,
Tao Bao293fd132016-06-11 12:19:23 -07001351 version=self.version,
1352 disable_imgdiff=self.disable_imgdiff)
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001353 tmpdir = tempfile.mkdtemp()
1354 OPTIONS.tempfiles.append(tmpdir)
1355 self.path = os.path.join(tmpdir, partition)
1356 b.Compute(self.path)
Tao Baod8d14be2016-02-04 14:26:02 -08001357 self._required_cache = b.max_stashed_size
Tao Baod522bdc2016-04-12 15:53:16 -07001358 self.touched_src_ranges = b.touched_src_ranges
1359 self.touched_src_sha1 = b.touched_src_sha1
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001360
Tao Baoaac4ad52015-10-16 15:26:34 -07001361 if src is None:
1362 _, self.device = GetTypeAndDevice("/" + partition, OPTIONS.info_dict)
1363 else:
1364 _, self.device = GetTypeAndDevice("/" + partition,
1365 OPTIONS.source_info_dict)
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001366
Tao Baod8d14be2016-02-04 14:26:02 -08001367 @property
1368 def required_cache(self):
1369 return self._required_cache
1370
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001371 def WriteScript(self, script, output_zip, progress=None):
1372 if not self.src:
1373 # write the output unconditionally
Jesse Zhao75bcea02015-01-06 10:59:53 -08001374 script.Print("Patching %s image unconditionally..." % (self.partition,))
1375 else:
1376 script.Print("Patching %s image after verification." % (self.partition,))
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001377
Dan Albert8b72aef2015-03-23 19:13:21 -07001378 if progress:
1379 script.ShowProgress(progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001380 self._WriteUpdate(script, output_zip)
Tianjie Xub2deb222016-03-25 15:01:33 -07001381 if OPTIONS.verify:
1382 self._WritePostInstallVerifyScript(script)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001383
Tao Bao9bc6bb22015-11-09 16:58:28 -08001384 def WriteStrictVerifyScript(self, script):
1385 """Verify all the blocks in the care_map, including clobbered blocks.
1386
1387 This differs from the WriteVerifyScript() function: a) it prints different
1388 error messages; b) it doesn't allow half-way updated images to pass the
1389 verification."""
1390
1391 partition = self.partition
1392 script.Print("Verifying %s..." % (partition,))
1393 ranges = self.tgt.care_map
1394 ranges_str = ranges.to_string_raw()
1395 script.AppendExtra('range_sha1("%s", "%s") == "%s" && '
1396 'ui_print(" Verified.") || '
1397 'ui_print("\\"%s\\" has unexpected contents.");' % (
1398 self.device, ranges_str,
1399 self.tgt.TotalSha1(include_clobbered_blocks=True),
1400 self.device))
1401 script.AppendExtra("")
1402
Tao Baod522bdc2016-04-12 15:53:16 -07001403 def WriteVerifyScript(self, script, touched_blocks_only=False):
Sami Tolvanendd67a292014-12-09 16:40:34 +00001404 partition = self.partition
Tao Baof9efe282016-04-14 15:58:05 -07001405
1406 # full OTA
Jesse Zhao75bcea02015-01-06 10:59:53 -08001407 if not self.src:
Sami Tolvanendd67a292014-12-09 16:40:34 +00001408 script.Print("Image %s will be patched unconditionally." % (partition,))
Tao Baof9efe282016-04-14 15:58:05 -07001409
1410 # incremental OTA
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001411 else:
Tao Bao8fad03e2017-03-01 14:36:26 -08001412 if touched_blocks_only:
Tao Baod522bdc2016-04-12 15:53:16 -07001413 ranges = self.touched_src_ranges
1414 expected_sha1 = self.touched_src_sha1
1415 else:
1416 ranges = self.src.care_map.subtract(self.src.clobbered_blocks)
1417 expected_sha1 = self.src.TotalSha1()
Tao Baof9efe282016-04-14 15:58:05 -07001418
1419 # No blocks to be checked, skipping.
1420 if not ranges:
1421 return
1422
Tao Bao5ece99d2015-05-12 11:42:31 -07001423 ranges_str = ranges.to_string_raw()
Tao Bao8fad03e2017-03-01 14:36:26 -08001424 script.AppendExtra(('if (range_sha1("%s", "%s") == "%s" || '
1425 'block_image_verify("%s", '
1426 'package_extract_file("%s.transfer.list"), '
1427 '"%s.new.dat", "%s.patch.dat")) then') % (
1428 self.device, ranges_str, expected_sha1,
1429 self.device, partition, partition, partition))
Tao Baodd2a5892015-03-12 12:32:37 -07001430 script.Print('Verified %s image...' % (partition,))
Dan Albert8b72aef2015-03-23 19:13:21 -07001431 script.AppendExtra('else')
Sami Tolvanendd67a292014-12-09 16:40:34 +00001432
Tianjie Xufc3422a2015-12-15 11:53:59 -08001433 if self.version >= 4:
1434
1435 # Bug: 21124327
1436 # When generating incrementals for the system and vendor partitions in
1437 # version 4 or newer, explicitly check the first block (which contains
1438 # the superblock) of the partition to see if it's what we expect. If
1439 # this check fails, give an explicit log message about the partition
1440 # having been remounted R/W (the most likely explanation).
1441 if self.check_first_block:
1442 script.AppendExtra('check_first_block("%s");' % (self.device,))
1443
1444 # If version >= 4, try block recovery before abort update
Tianjie Xu209db462016-05-24 17:34:52 -07001445 if partition == "system":
1446 code = ErrorCode.SYSTEM_RECOVER_FAILURE
1447 else:
1448 code = ErrorCode.VENDOR_RECOVER_FAILURE
Tianjie Xufc3422a2015-12-15 11:53:59 -08001449 script.AppendExtra((
1450 'ifelse (block_image_recover("{device}", "{ranges}") && '
1451 'block_image_verify("{device}", '
1452 'package_extract_file("{partition}.transfer.list"), '
1453 '"{partition}.new.dat", "{partition}.patch.dat"), '
1454 'ui_print("{partition} recovered successfully."), '
Tianjie Xu209db462016-05-24 17:34:52 -07001455 'abort("E{code}: {partition} partition fails to recover"));\n'
Tianjie Xufc3422a2015-12-15 11:53:59 -08001456 'endif;').format(device=self.device, ranges=ranges_str,
Tianjie Xu209db462016-05-24 17:34:52 -07001457 partition=partition, code=code))
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001458
Tao Baodd2a5892015-03-12 12:32:37 -07001459 # Abort the OTA update. Note that the incremental OTA cannot be applied
1460 # even if it may match the checksum of the target partition.
1461 # a) If version < 3, operations like move and erase will make changes
1462 # unconditionally and damage the partition.
1463 # b) If version >= 3, it won't even reach here.
Tianjie Xufc3422a2015-12-15 11:53:59 -08001464 else:
Tianjie Xu209db462016-05-24 17:34:52 -07001465 if partition == "system":
1466 code = ErrorCode.SYSTEM_VERIFICATION_FAILURE
1467 else:
1468 code = ErrorCode.VENDOR_VERIFICATION_FAILURE
1469 script.AppendExtra((
1470 'abort("E%d: %s partition has unexpected contents");\n'
1471 'endif;') % (code, partition))
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001472
Tao Bao5fcaaef2015-06-01 13:40:49 -07001473 def _WritePostInstallVerifyScript(self, script):
1474 partition = self.partition
1475 script.Print('Verifying the updated %s image...' % (partition,))
1476 # Unlike pre-install verification, clobbered_blocks should not be ignored.
1477 ranges = self.tgt.care_map
1478 ranges_str = ranges.to_string_raw()
1479 script.AppendExtra('if range_sha1("%s", "%s") == "%s" then' % (
1480 self.device, ranges_str,
1481 self.tgt.TotalSha1(include_clobbered_blocks=True)))
Tao Baoe9b61912015-07-09 17:37:49 -07001482
1483 # Bug: 20881595
1484 # Verify that extended blocks are really zeroed out.
1485 if self.tgt.extended:
1486 ranges_str = self.tgt.extended.to_string_raw()
1487 script.AppendExtra('if range_sha1("%s", "%s") == "%s" then' % (
1488 self.device, ranges_str,
1489 self._HashZeroBlocks(self.tgt.extended.size())))
1490 script.Print('Verified the updated %s image.' % (partition,))
Tianjie Xu209db462016-05-24 17:34:52 -07001491 if partition == "system":
1492 code = ErrorCode.SYSTEM_NONZERO_CONTENTS
1493 else:
1494 code = ErrorCode.VENDOR_NONZERO_CONTENTS
Tao Baoe9b61912015-07-09 17:37:49 -07001495 script.AppendExtra(
1496 'else\n'
Tianjie Xu209db462016-05-24 17:34:52 -07001497 ' abort("E%d: %s partition has unexpected non-zero contents after '
1498 'OTA update");\n'
1499 'endif;' % (code, partition))
Tao Baoe9b61912015-07-09 17:37:49 -07001500 else:
1501 script.Print('Verified the updated %s image.' % (partition,))
1502
Tianjie Xu209db462016-05-24 17:34:52 -07001503 if partition == "system":
1504 code = ErrorCode.SYSTEM_UNEXPECTED_CONTENTS
1505 else:
1506 code = ErrorCode.VENDOR_UNEXPECTED_CONTENTS
1507
Tao Bao5fcaaef2015-06-01 13:40:49 -07001508 script.AppendExtra(
1509 'else\n'
Tianjie Xu209db462016-05-24 17:34:52 -07001510 ' abort("E%d: %s partition has unexpected contents after OTA '
1511 'update");\n'
1512 'endif;' % (code, partition))
Tao Bao5fcaaef2015-06-01 13:40:49 -07001513
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001514 def _WriteUpdate(self, script, output_zip):
Dan Albert8e0178d2015-01-27 15:53:15 -08001515 ZipWrite(output_zip,
1516 '{}.transfer.list'.format(self.path),
1517 '{}.transfer.list'.format(self.partition))
1518 ZipWrite(output_zip,
1519 '{}.new.dat'.format(self.path),
1520 '{}.new.dat'.format(self.partition))
1521 ZipWrite(output_zip,
1522 '{}.patch.dat'.format(self.path),
1523 '{}.patch.dat'.format(self.partition),
1524 compress_type=zipfile.ZIP_STORED)
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001525
Tianjie Xu209db462016-05-24 17:34:52 -07001526 if self.partition == "system":
1527 code = ErrorCode.SYSTEM_UPDATE_FAILURE
1528 else:
1529 code = ErrorCode.VENDOR_UPDATE_FAILURE
1530
Dan Albert8e0178d2015-01-27 15:53:15 -08001531 call = ('block_image_update("{device}", '
1532 'package_extract_file("{partition}.transfer.list"), '
Tianjie Xub2deb222016-03-25 15:01:33 -07001533 '"{partition}.new.dat", "{partition}.patch.dat") ||\n'
Tianjie Xu209db462016-05-24 17:34:52 -07001534 ' abort("E{code}: Failed to update {partition} image.");'.format(
1535 device=self.device, partition=self.partition, code=code))
Dan Albert8b72aef2015-03-23 19:13:21 -07001536 script.AppendExtra(script.WordWrap(call))
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001537
Dan Albert8b72aef2015-03-23 19:13:21 -07001538 def _HashBlocks(self, source, ranges): # pylint: disable=no-self-use
Sami Tolvanendd67a292014-12-09 16:40:34 +00001539 data = source.ReadRangeSet(ranges)
1540 ctx = sha1()
1541
1542 for p in data:
1543 ctx.update(p)
1544
1545 return ctx.hexdigest()
1546
Tao Baoe9b61912015-07-09 17:37:49 -07001547 def _HashZeroBlocks(self, num_blocks): # pylint: disable=no-self-use
1548 """Return the hash value for all zero blocks."""
1549 zero_block = '\x00' * 4096
1550 ctx = sha1()
1551 for _ in range(num_blocks):
1552 ctx.update(zero_block)
1553
1554 return ctx.hexdigest()
1555
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001556
1557DataImage = blockimgdiff.DataImage
1558
Doug Zongker96a57e72010-09-26 14:57:41 -07001559# map recovery.fstab's fs_types to mount/format "partition types"
Dan Albert8b72aef2015-03-23 19:13:21 -07001560PARTITION_TYPES = {
Dan Albert8b72aef2015-03-23 19:13:21 -07001561 "ext4": "EMMC",
1562 "emmc": "EMMC",
Mohamad Ayyash95e74c12015-05-01 15:39:36 -07001563 "f2fs": "EMMC",
1564 "squashfs": "EMMC"
Dan Albert8b72aef2015-03-23 19:13:21 -07001565}
Doug Zongker96a57e72010-09-26 14:57:41 -07001566
1567def GetTypeAndDevice(mount_point, info):
1568 fstab = info["fstab"]
1569 if fstab:
Dan Albert8b72aef2015-03-23 19:13:21 -07001570 return (PARTITION_TYPES[fstab[mount_point].fs_type],
1571 fstab[mount_point].device)
Doug Zongker96a57e72010-09-26 14:57:41 -07001572 else:
Dan Albert8b72aef2015-03-23 19:13:21 -07001573 raise KeyError
Baligh Uddinbeb6afd2013-11-13 00:22:34 +00001574
1575
1576def ParseCertificate(data):
1577 """Parse a PEM-format certificate."""
1578 cert = []
1579 save = False
1580 for line in data.split("\n"):
1581 if "--END CERTIFICATE--" in line:
1582 break
1583 if save:
1584 cert.append(line)
1585 if "--BEGIN CERTIFICATE--" in line:
1586 save = True
1587 cert = "".join(cert).decode('base64')
1588 return cert
Doug Zongkerc9253822014-02-04 12:17:58 -08001589
Doug Zongker412c02f2014-02-13 10:58:24 -08001590def MakeRecoveryPatch(input_dir, output_sink, recovery_img, boot_img,
1591 info_dict=None):
Doug Zongkerc9253822014-02-04 12:17:58 -08001592 """Generate a binary patch that creates the recovery image starting
1593 with the boot image. (Most of the space in these images is just the
1594 kernel, which is identical for the two, so the resulting patch
1595 should be efficient.) Add it to the output zip, along with a shell
1596 script that is run from init.rc on first boot to actually do the
1597 patching and install the new recovery image.
1598
1599 recovery_img and boot_img should be File objects for the
1600 corresponding images. info should be the dictionary returned by
1601 common.LoadInfoDict() on the input target_files.
1602 """
1603
Doug Zongker412c02f2014-02-13 10:58:24 -08001604 if info_dict is None:
1605 info_dict = OPTIONS.info_dict
1606
Tao Baof2cffbd2015-07-22 12:33:18 -07001607 full_recovery_image = info_dict.get("full_recovery_image", None) == "true"
Doug Zongkerc9253822014-02-04 12:17:58 -08001608
Tao Baof2cffbd2015-07-22 12:33:18 -07001609 if full_recovery_image:
1610 output_sink("etc/recovery.img", recovery_img.data)
1611
1612 else:
1613 diff_program = ["imgdiff"]
1614 path = os.path.join(input_dir, "SYSTEM", "etc", "recovery-resource.dat")
1615 if os.path.exists(path):
1616 diff_program.append("-b")
1617 diff_program.append(path)
1618 bonus_args = "-b /system/etc/recovery-resource.dat"
1619 else:
1620 bonus_args = ""
1621
1622 d = Difference(recovery_img, boot_img, diff_program=diff_program)
1623 _, _, patch = d.ComputePatch()
1624 output_sink("recovery-from-boot.p", patch)
Doug Zongkerc9253822014-02-04 12:17:58 -08001625
Dan Albertebb19aa2015-03-27 19:11:53 -07001626 try:
Tao Bao6f0b2192015-10-13 16:37:12 -07001627 # The following GetTypeAndDevice()s need to use the path in the target
1628 # info_dict instead of source_info_dict.
Dan Albertebb19aa2015-03-27 19:11:53 -07001629 boot_type, boot_device = GetTypeAndDevice("/boot", info_dict)
1630 recovery_type, recovery_device = GetTypeAndDevice("/recovery", info_dict)
1631 except KeyError:
Ying Wanga961a092014-07-29 11:42:37 -07001632 return
Doug Zongkerc9253822014-02-04 12:17:58 -08001633
Tao Baof2cffbd2015-07-22 12:33:18 -07001634 if full_recovery_image:
1635 sh = """#!/system/bin/sh
1636if ! applypatch -c %(type)s:%(device)s:%(size)d:%(sha1)s; then
1637 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"
1638else
1639 log -t recovery "Recovery image already installed"
1640fi
1641""" % {'type': recovery_type,
1642 'device': recovery_device,
1643 'sha1': recovery_img.sha1,
1644 'size': recovery_img.size}
1645 else:
1646 sh = """#!/system/bin/sh
Doug Zongkerc9253822014-02-04 12:17:58 -08001647if ! applypatch -c %(recovery_type)s:%(recovery_device)s:%(recovery_size)d:%(recovery_sha1)s; then
1648 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"
1649else
1650 log -t recovery "Recovery image already installed"
1651fi
Dan Albert8b72aef2015-03-23 19:13:21 -07001652""" % {'boot_size': boot_img.size,
1653 'boot_sha1': boot_img.sha1,
1654 'recovery_size': recovery_img.size,
1655 'recovery_sha1': recovery_img.sha1,
1656 'boot_type': boot_type,
1657 'boot_device': boot_device,
1658 'recovery_type': recovery_type,
1659 'recovery_device': recovery_device,
1660 'bonus_args': bonus_args}
Doug Zongkerc9253822014-02-04 12:17:58 -08001661
1662 # The install script location moved from /system/etc to /system/bin
Tianjie Xu78de9f12017-06-20 16:52:54 -07001663 # in the L release.
1664 sh_location = "bin/install-recovery.sh"
Tao Bao9f0c8df2015-07-07 18:31:47 -07001665
Tao Bao89fbb0f2017-01-10 10:47:58 -08001666 print("putting script in", sh_location)
Doug Zongkerc9253822014-02-04 12:17:58 -08001667
1668 output_sink(sh_location, sh)