blob: 322433313a069f25bdca7de3311ec6da13e087b1 [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
110def Run(args, **kwargs):
111 """Create and return a subprocess.Popen object, printing the command
112 line on the terminal if -v was specified."""
113 if OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800114 print(" running: ", " ".join(args))
Doug Zongkereef39442009-04-02 12:14:19 -0700115 return subprocess.Popen(args, **kwargs)
116
117
Ying Wang7e6d4e42010-12-13 16:25:36 -0800118def CloseInheritedPipes():
119 """ Gmake in MAC OS has file descriptor (PIPE) leak. We close those fds
120 before doing other work."""
121 if platform.system() != "Darwin":
122 return
123 for d in range(3, 1025):
124 try:
125 stat = os.fstat(d)
126 if stat is not None:
127 pipebit = stat[0] & 0x1000
128 if pipebit != 0:
129 os.close(d)
130 except OSError:
131 pass
132
133
Tao Bao2c15d9e2015-07-09 11:51:16 -0700134def LoadInfoDict(input_file, input_dir=None):
Doug Zongkerc19a8d52010-07-01 15:30:11 -0700135 """Read and parse the META/misc_info.txt key/value pairs from the
136 input target files and return a dict."""
137
Doug Zongkerc9253822014-02-04 12:17:58 -0800138 def read_helper(fn):
Dan Albert8b72aef2015-03-23 19:13:21 -0700139 if isinstance(input_file, zipfile.ZipFile):
140 return input_file.read(fn)
Doug Zongkerc9253822014-02-04 12:17:58 -0800141 else:
Dan Albert8b72aef2015-03-23 19:13:21 -0700142 path = os.path.join(input_file, *fn.split("/"))
Doug Zongkerc9253822014-02-04 12:17:58 -0800143 try:
144 with open(path) as f:
145 return f.read()
Dan Albert8b72aef2015-03-23 19:13:21 -0700146 except IOError as e:
Doug Zongkerc9253822014-02-04 12:17:58 -0800147 if e.errno == errno.ENOENT:
148 raise KeyError(fn)
Tao Bao6cd54732017-02-27 15:12:05 -0800149
Doug Zongkerc19a8d52010-07-01 15:30:11 -0700150 try:
Michael Runge6e836112014-04-15 17:40:21 -0700151 d = LoadDictionaryFromLines(read_helper("META/misc_info.txt").split("\n"))
Doug Zongker37974732010-09-16 17:44:38 -0700152 except KeyError:
Tao Bao6cd54732017-02-27 15:12:05 -0800153 raise ValueError("can't find META/misc_info.txt in input target-files")
Doug Zongkerc19a8d52010-07-01 15:30:11 -0700154
Tao Bao6cd54732017-02-27 15:12:05 -0800155 assert "recovery_api_version" in d
Tao Baod1de6f32017-03-01 16:38:48 -0800156 assert "fstab_version" in d
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800157
Tao Bao84e75682015-07-19 02:38:53 -0700158 # A few properties are stored as links to the files in the out/ directory.
159 # It works fine with the build system. However, they are no longer available
160 # when (re)generating from target_files zip. If input_dir is not None, we
161 # are doing repacking. Redirect those properties to the actual files in the
162 # unzipped directory.
Tao Bao2c15d9e2015-07-09 11:51:16 -0700163 if input_dir is not None:
Stephen Smalleyd3a803e2015-08-04 14:59:06 -0400164 # We carry a copy of file_contexts.bin under META/. If not available,
165 # search BOOT/RAMDISK/. Note that sometimes we may need a different file
Tao Bao84e75682015-07-19 02:38:53 -0700166 # to build images than the one running on device, such as when enabling
167 # system_root_image. In that case, we must have the one for image
168 # generation copied to META/.
Tao Bao79735a62015-08-28 10:52:03 -0700169 fc_basename = os.path.basename(d.get("selinux_fc", "file_contexts"))
170 fc_config = os.path.join(input_dir, "META", fc_basename)
Tao Bao84e75682015-07-19 02:38:53 -0700171 if d.get("system_root_image") == "true":
172 assert os.path.exists(fc_config)
Tao Bao2c15d9e2015-07-09 11:51:16 -0700173 if not os.path.exists(fc_config):
Tao Bao79735a62015-08-28 10:52:03 -0700174 fc_config = os.path.join(input_dir, "BOOT", "RAMDISK", fc_basename)
Tao Bao2c15d9e2015-07-09 11:51:16 -0700175 if not os.path.exists(fc_config):
176 fc_config = None
177
178 if fc_config:
179 d["selinux_fc"] = fc_config
180
Tao Bao84e75682015-07-19 02:38:53 -0700181 # Similarly we need to redirect "ramdisk_dir" and "ramdisk_fs_config".
182 if d.get("system_root_image") == "true":
183 d["ramdisk_dir"] = os.path.join(input_dir, "ROOT")
184 d["ramdisk_fs_config"] = os.path.join(
185 input_dir, "META", "root_filesystem_config.txt")
186
Tao Baof54216f2016-03-29 15:12:37 -0700187 # Redirect {system,vendor}_base_fs_file.
188 if "system_base_fs_file" in d:
189 basename = os.path.basename(d["system_base_fs_file"])
190 system_base_fs_file = os.path.join(input_dir, "META", basename)
Tao Baob079b502016-05-03 08:01:19 -0700191 if os.path.exists(system_base_fs_file):
192 d["system_base_fs_file"] = system_base_fs_file
193 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800194 print("Warning: failed to find system base fs file: %s" % (
195 system_base_fs_file,))
Tao Baob079b502016-05-03 08:01:19 -0700196 del d["system_base_fs_file"]
Tao Baof54216f2016-03-29 15:12:37 -0700197
198 if "vendor_base_fs_file" in d:
199 basename = os.path.basename(d["vendor_base_fs_file"])
200 vendor_base_fs_file = os.path.join(input_dir, "META", basename)
Tao Baob079b502016-05-03 08:01:19 -0700201 if os.path.exists(vendor_base_fs_file):
202 d["vendor_base_fs_file"] = vendor_base_fs_file
203 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800204 print("Warning: failed to find vendor base fs file: %s" % (
205 vendor_base_fs_file,))
Tao Baob079b502016-05-03 08:01:19 -0700206 del d["vendor_base_fs_file"]
Tao Baof54216f2016-03-29 15:12:37 -0700207
Doug Zongker37974732010-09-16 17:44:38 -0700208 try:
Doug Zongkerc9253822014-02-04 12:17:58 -0800209 data = read_helper("META/imagesizes.txt")
Doug Zongker37974732010-09-16 17:44:38 -0700210 for line in data.split("\n"):
Dan Albert8b72aef2015-03-23 19:13:21 -0700211 if not line:
212 continue
Doug Zongker1684d9c2010-09-17 07:44:38 -0700213 name, value = line.split(" ", 1)
Dan Albert8b72aef2015-03-23 19:13:21 -0700214 if not value:
215 continue
Doug Zongker37974732010-09-16 17:44:38 -0700216 if name == "blocksize":
217 d[name] = value
218 else:
219 d[name + "_size"] = value
220 except KeyError:
221 pass
222
223 def makeint(key):
224 if key in d:
225 d[key] = int(d[key], 0)
226
227 makeint("recovery_api_version")
228 makeint("blocksize")
229 makeint("system_size")
Daniel Rosenbergf4eabc32014-07-10 15:42:38 -0700230 makeint("vendor_size")
Doug Zongker37974732010-09-16 17:44:38 -0700231 makeint("userdata_size")
Ying Wang9f8e8db2011-11-04 11:37:01 -0700232 makeint("cache_size")
Doug Zongker37974732010-09-16 17:44:38 -0700233 makeint("recovery_size")
234 makeint("boot_size")
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800235 makeint("fstab_version")
Doug Zongkerc19a8d52010-07-01 15:30:11 -0700236
Tianjie Xucfa86222016-03-07 16:31:19 -0800237 system_root_image = d.get("system_root_image", None) == "true"
238 if d.get("no_recovery", None) != "true":
239 recovery_fstab_path = "RECOVERY/RAMDISK/etc/recovery.fstab"
Tao Bao48550cc2015-11-19 17:05:46 -0800240 d["fstab"] = LoadRecoveryFSTab(read_helper, d["fstab_version"],
Tianjie Xucfa86222016-03-07 16:31:19 -0800241 recovery_fstab_path, system_root_image)
242 elif d.get("recovery_as_boot", None) == "true":
243 recovery_fstab_path = "BOOT/RAMDISK/etc/recovery.fstab"
244 d["fstab"] = LoadRecoveryFSTab(read_helper, d["fstab_version"],
245 recovery_fstab_path, system_root_image)
246 else:
247 d["fstab"] = None
248
Doug Zongkerc9253822014-02-04 12:17:58 -0800249 d["build.prop"] = LoadBuildProp(read_helper)
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700250 return d
251
Tao Baod1de6f32017-03-01 16:38:48 -0800252
Doug Zongkerc9253822014-02-04 12:17:58 -0800253def LoadBuildProp(read_helper):
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700254 try:
Doug Zongkerc9253822014-02-04 12:17:58 -0800255 data = read_helper("SYSTEM/build.prop")
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700256 except KeyError:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800257 print("Warning: could not find SYSTEM/build.prop in %s" % (zip,))
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700258 data = ""
Michael Runge6e836112014-04-15 17:40:21 -0700259 return LoadDictionaryFromLines(data.split("\n"))
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700260
Tao Baod1de6f32017-03-01 16:38:48 -0800261
Michael Runge6e836112014-04-15 17:40:21 -0700262def LoadDictionaryFromLines(lines):
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700263 d = {}
Michael Runge6e836112014-04-15 17:40:21 -0700264 for line in lines:
Doug Zongker1eb74dd2012-08-16 16:19:00 -0700265 line = line.strip()
Dan Albert8b72aef2015-03-23 19:13:21 -0700266 if not line or line.startswith("#"):
267 continue
Ying Wang114b46f2014-04-15 11:24:00 -0700268 if "=" in line:
269 name, value = line.split("=", 1)
270 d[name] = value
Doug Zongkerc19a8d52010-07-01 15:30:11 -0700271 return d
272
Tao Baod1de6f32017-03-01 16:38:48 -0800273
Tianjie Xucfa86222016-03-07 16:31:19 -0800274def LoadRecoveryFSTab(read_helper, fstab_version, recovery_fstab_path,
275 system_root_image=False):
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700276 class Partition(object):
Tao Baod1de6f32017-03-01 16:38:48 -0800277 def __init__(self, mount_point, fs_type, device, length, context):
Dan Albert8b72aef2015-03-23 19:13:21 -0700278 self.mount_point = mount_point
279 self.fs_type = fs_type
280 self.device = device
281 self.length = length
Tao Bao548eb762015-06-10 12:32:41 -0700282 self.context = context
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700283
284 try:
Tianjie Xucfa86222016-03-07 16:31:19 -0800285 data = read_helper(recovery_fstab_path)
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700286 except KeyError:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800287 print("Warning: could not find {}".format(recovery_fstab_path))
Jeff Davidson033fbe22011-10-26 18:08:09 -0700288 data = ""
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700289
Tao Baod1de6f32017-03-01 16:38:48 -0800290 assert fstab_version == 2
291
292 d = {}
293 for line in data.split("\n"):
294 line = line.strip()
295 if not line or line.startswith("#"):
296 continue
297
298 # <src> <mnt_point> <type> <mnt_flags and options> <fs_mgr_flags>
299 pieces = line.split()
300 if len(pieces) != 5:
301 raise ValueError("malformed recovery.fstab line: \"%s\"" % (line,))
302
303 # Ignore entries that are managed by vold.
304 options = pieces[4]
305 if "voldmanaged=" in options:
306 continue
307
308 # It's a good line, parse it.
309 length = 0
310 options = options.split(",")
311 for i in options:
312 if i.startswith("length="):
313 length = int(i[7:])
Doug Zongker086cbb02011-02-17 15:54:20 -0800314 else:
Tao Baod1de6f32017-03-01 16:38:48 -0800315 # Ignore all unknown options in the unified fstab.
Dan Albert8b72aef2015-03-23 19:13:21 -0700316 continue
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800317
Tao Baod1de6f32017-03-01 16:38:48 -0800318 mount_flags = pieces[3]
319 # Honor the SELinux context if present.
320 context = None
321 for i in mount_flags.split(","):
322 if i.startswith("context="):
323 context = i
Doug Zongker086cbb02011-02-17 15:54:20 -0800324
Tao Baod1de6f32017-03-01 16:38:48 -0800325 mount_point = pieces[1]
326 d[mount_point] = Partition(mount_point=mount_point, fs_type=pieces[2],
327 device=pieces[0], length=length, context=context)
Ken Sumrall3b07cf12013-02-19 17:35:29 -0800328
Daniel Rosenberge6853b02015-06-05 17:59:27 -0700329 # / is used for the system mount point when the root directory is included in
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700330 # system. Other areas assume system is always at "/system" so point /system
331 # at /.
Daniel Rosenberge6853b02015-06-05 17:59:27 -0700332 if system_root_image:
333 assert not d.has_key("/system") and d.has_key("/")
334 d["/system"] = d["/"]
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700335 return d
336
337
Doug Zongker37974732010-09-16 17:44:38 -0700338def DumpInfoDict(d):
339 for k, v in sorted(d.items()):
Tao Bao89fbb0f2017-01-10 10:47:58 -0800340 print("%-25s = (%s) %s" % (k, type(v).__name__, v))
Doug Zongkerc19a8d52010-07-01 15:30:11 -0700341
Dan Albert8b72aef2015-03-23 19:13:21 -0700342
David Zeuthen2ce63ed2016-09-15 13:43:54 -0400343def AppendAVBSigningArgs(cmd):
344 """Append signing arguments for avbtool."""
345 keypath = OPTIONS.info_dict.get("board_avb_key_path", None)
346 algorithm = OPTIONS.info_dict.get("board_avb_algorithm", None)
347 if not keypath or not algorithm:
348 algorithm = "SHA256_RSA4096"
349 keypath = "external/avb/test/data/testkey_rsa4096.pem"
350 cmd.extend(["--key", keypath, "--algorithm", algorithm])
351
352
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700353def _BuildBootableImage(sourcedir, fs_config_file, info_dict=None,
Tao Baod42e97e2016-11-30 12:11:57 -0800354 has_ramdisk=False, two_step_image=False):
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700355 """Build a bootable image from the specified sourcedir.
Doug Zongkere1c31ba2009-06-23 17:40:35 -0700356
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700357 Take a kernel, cmdline, and optionally a ramdisk directory from the input (in
Tao Baod42e97e2016-11-30 12:11:57 -0800358 'sourcedir'), and turn them into a boot image. 'two_step_image' indicates if
359 we are building a two-step special image (i.e. building a recovery image to
360 be loaded into /boot in two-step OTAs).
361
362 Return the image data, or None if sourcedir does not appear to contains files
363 for building the requested image.
364 """
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700365
366 def make_ramdisk():
367 ramdisk_img = tempfile.NamedTemporaryFile()
368
369 if os.access(fs_config_file, os.F_OK):
370 cmd = ["mkbootfs", "-f", fs_config_file,
371 os.path.join(sourcedir, "RAMDISK")]
372 else:
373 cmd = ["mkbootfs", os.path.join(sourcedir, "RAMDISK")]
374 p1 = Run(cmd, stdout=subprocess.PIPE)
375 p2 = Run(["minigzip"], stdin=p1.stdout, stdout=ramdisk_img.file.fileno())
376
377 p2.wait()
378 p1.wait()
379 assert p1.returncode == 0, "mkbootfs of %s ramdisk failed" % (sourcedir,)
380 assert p2.returncode == 0, "minigzip of %s ramdisk failed" % (sourcedir,)
381
382 return ramdisk_img
383
384 if not os.access(os.path.join(sourcedir, "kernel"), os.F_OK):
385 return None
386
387 if has_ramdisk and not os.access(os.path.join(sourcedir, "RAMDISK"), os.F_OK):
Doug Zongkere1c31ba2009-06-23 17:40:35 -0700388 return None
Doug Zongkereef39442009-04-02 12:14:19 -0700389
Doug Zongkerd5131602012-08-02 14:46:42 -0700390 if info_dict is None:
391 info_dict = OPTIONS.info_dict
392
Doug Zongkereef39442009-04-02 12:14:19 -0700393 img = tempfile.NamedTemporaryFile()
394
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700395 if has_ramdisk:
396 ramdisk_img = make_ramdisk()
Doug Zongkereef39442009-04-02 12:14:19 -0700397
Bjorn Andersson612e2cd2012-11-25 16:53:44 -0800398 # use MKBOOTIMG from environ, or "mkbootimg" if empty or not set
399 mkbootimg = os.getenv('MKBOOTIMG') or "mkbootimg"
400
401 cmd = [mkbootimg, "--kernel", os.path.join(sourcedir, "kernel")]
Doug Zongker38a649f2009-06-17 09:07:09 -0700402
Benoit Fradina45a8682014-07-14 21:00:43 +0200403 fn = os.path.join(sourcedir, "second")
404 if os.access(fn, os.F_OK):
405 cmd.append("--second")
406 cmd.append(fn)
407
Doug Zongker171f1cd2009-06-15 22:36:37 -0700408 fn = os.path.join(sourcedir, "cmdline")
409 if os.access(fn, os.F_OK):
Doug Zongker38a649f2009-06-17 09:07:09 -0700410 cmd.append("--cmdline")
411 cmd.append(open(fn).read().rstrip("\n"))
412
413 fn = os.path.join(sourcedir, "base")
414 if os.access(fn, os.F_OK):
415 cmd.append("--base")
416 cmd.append(open(fn).read().rstrip("\n"))
417
Ying Wang4de6b5b2010-08-25 14:29:34 -0700418 fn = os.path.join(sourcedir, "pagesize")
419 if os.access(fn, os.F_OK):
420 cmd.append("--pagesize")
421 cmd.append(open(fn).read().rstrip("\n"))
422
Doug Zongkerd5131602012-08-02 14:46:42 -0700423 args = info_dict.get("mkbootimg_args", None)
424 if args and args.strip():
Jianxun Zhang09849492013-04-17 15:19:19 -0700425 cmd.extend(shlex.split(args))
Doug Zongkerd5131602012-08-02 14:46:42 -0700426
Sami Tolvanen3303d902016-03-15 16:49:30 +0000427 args = info_dict.get("mkbootimg_version_args", None)
428 if args and args.strip():
429 cmd.extend(shlex.split(args))
430
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700431 if has_ramdisk:
432 cmd.extend(["--ramdisk", ramdisk_img.name])
433
Tao Baod95e9fd2015-03-29 23:07:41 -0700434 img_unsigned = None
435 if info_dict.get("vboot", None):
436 img_unsigned = tempfile.NamedTemporaryFile()
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700437 cmd.extend(["--output", img_unsigned.name])
Tao Baod95e9fd2015-03-29 23:07:41 -0700438 else:
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700439 cmd.extend(["--output", img.name])
Doug Zongker38a649f2009-06-17 09:07:09 -0700440
441 p = Run(cmd, stdout=subprocess.PIPE)
Doug Zongkereef39442009-04-02 12:14:19 -0700442 p.communicate()
Doug Zongkere1c31ba2009-06-23 17:40:35 -0700443 assert p.returncode == 0, "mkbootimg of %s image failed" % (
444 os.path.basename(sourcedir),)
Doug Zongkereef39442009-04-02 12:14:19 -0700445
Sami Tolvanen8b3f08b2015-04-07 15:08:59 +0100446 if (info_dict.get("boot_signer", None) == "true" and
447 info_dict.get("verity_key", None)):
Tao Baod42e97e2016-11-30 12:11:57 -0800448 # Hard-code the path as "/boot" for two-step special recovery image (which
449 # will be loaded into /boot during the two-step OTA).
450 if two_step_image:
451 path = "/boot"
452 else:
453 path = "/" + os.path.basename(sourcedir).lower()
Baligh Uddin601ddea2015-06-09 15:48:14 -0700454 cmd = [OPTIONS.boot_signer_path]
455 cmd.extend(OPTIONS.boot_signer_args)
456 cmd.extend([path, img.name,
457 info_dict["verity_key"] + ".pk8",
458 info_dict["verity_key"] + ".x509.pem", img.name])
Geremy Condra95ebe7a2014-08-19 17:27:56 -0700459 p = Run(cmd, stdout=subprocess.PIPE)
460 p.communicate()
461 assert p.returncode == 0, "boot_signer of %s image failed" % path
462
Tao Baod95e9fd2015-03-29 23:07:41 -0700463 # Sign the image if vboot is non-empty.
464 elif info_dict.get("vboot", None):
465 path = "/" + os.path.basename(sourcedir).lower()
466 img_keyblock = tempfile.NamedTemporaryFile()
Tao Bao4f104d12017-02-17 23:21:31 -0800467 # We have switched from the prebuilt futility binary to using the tool
468 # (futility-host) built from the source. Override the setting in the old
469 # TF.zip.
470 futility = info_dict["futility"]
471 if futility.startswith("prebuilts/"):
472 futility = "futility-host"
473 cmd = [info_dict["vboot_signer_cmd"], futility,
Tao Baod95e9fd2015-03-29 23:07:41 -0700474 img_unsigned.name, info_dict["vboot_key"] + ".vbpubk",
Furquan Shaikh852b8de2015-08-10 11:43:45 -0700475 info_dict["vboot_key"] + ".vbprivk",
476 info_dict["vboot_subkey"] + ".vbprivk",
477 img_keyblock.name,
Tao Baod95e9fd2015-03-29 23:07:41 -0700478 img.name]
479 p = Run(cmd, stdout=subprocess.PIPE)
480 p.communicate()
481 assert p.returncode == 0, "vboot_signer of %s image failed" % path
482
Tao Baof3282b42015-04-01 11:21:55 -0700483 # Clean up the temp files.
484 img_unsigned.close()
485 img_keyblock.close()
486
David Zeuthen2ce63ed2016-09-15 13:43:54 -0400487 # AVB: if enabled, calculate and add hash to boot.img.
Tao Baob31b94e2016-09-29 21:59:06 -0700488 if info_dict.get("board_avb_enable", None) == "true":
David Zeuthen2ce63ed2016-09-15 13:43:54 -0400489 avbtool = os.getenv('AVBTOOL') or "avbtool"
Tao Baob31b94e2016-09-29 21:59:06 -0700490 part_size = info_dict.get("boot_size", None)
David Zeuthen2ce63ed2016-09-15 13:43:54 -0400491 cmd = [avbtool, "add_hash_footer", "--image", img.name,
492 "--partition_size", str(part_size), "--partition_name", "boot"]
493 AppendAVBSigningArgs(cmd)
Tao Baob31b94e2016-09-29 21:59:06 -0700494 args = info_dict.get("board_avb_boot_add_hash_footer_args", None)
David Zeuthen2ce63ed2016-09-15 13:43:54 -0400495 if args and args.strip():
496 cmd.extend(shlex.split(args))
497 p = Run(cmd, stdout=subprocess.PIPE)
498 p.communicate()
499 assert p.returncode == 0, "avbtool add_hash_footer of %s failed" % (
500 os.path.basename(OPTIONS.input_tmp))
David Zeuthend995f4b2016-01-29 16:59:17 -0500501
502 img.seek(os.SEEK_SET, 0)
503 data = img.read()
504
505 if has_ramdisk:
506 ramdisk_img.close()
507 img.close()
508
509 return data
510
511
Doug Zongkerd5131602012-08-02 14:46:42 -0700512def GetBootableImage(name, prebuilt_name, unpack_dir, tree_subdir,
Tao Baod42e97e2016-11-30 12:11:57 -0800513 info_dict=None, two_step_image=False):
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700514 """Return a File object with the desired bootable image.
515
516 Look for it in 'unpack_dir'/BOOTABLE_IMAGES under the name 'prebuilt_name',
517 otherwise look for it under 'unpack_dir'/IMAGES, otherwise construct it from
518 the source files in 'unpack_dir'/'tree_subdir'."""
Doug Zongkereef39442009-04-02 12:14:19 -0700519
Doug Zongker55d93282011-01-25 17:03:34 -0800520 prebuilt_path = os.path.join(unpack_dir, "BOOTABLE_IMAGES", prebuilt_name)
521 if os.path.exists(prebuilt_path):
Tao Bao89fbb0f2017-01-10 10:47:58 -0800522 print("using prebuilt %s from BOOTABLE_IMAGES..." % (prebuilt_name,))
Doug Zongker55d93282011-01-25 17:03:34 -0800523 return File.FromLocalFile(name, prebuilt_path)
Doug Zongker6f1d0312014-08-22 08:07:12 -0700524
525 prebuilt_path = os.path.join(unpack_dir, "IMAGES", prebuilt_name)
526 if os.path.exists(prebuilt_path):
Tao Bao89fbb0f2017-01-10 10:47:58 -0800527 print("using prebuilt %s from IMAGES..." % (prebuilt_name,))
Doug Zongker6f1d0312014-08-22 08:07:12 -0700528 return File.FromLocalFile(name, prebuilt_path)
529
Tao Bao89fbb0f2017-01-10 10:47:58 -0800530 print("building image from target_files %s..." % (tree_subdir,))
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700531
532 if info_dict is None:
533 info_dict = OPTIONS.info_dict
534
535 # With system_root_image == "true", we don't pack ramdisk into the boot image.
Daniel Rosenberg40ef35b2015-11-10 19:21:34 -0800536 # Unless "recovery_as_boot" is specified, in which case we carry the ramdisk
537 # for recovery.
538 has_ramdisk = (info_dict.get("system_root_image") != "true" or
539 prebuilt_name != "boot.img" or
540 info_dict.get("recovery_as_boot") == "true")
Tao Bao7a5bf8a2015-07-21 18:01:20 -0700541
Doug Zongker6f1d0312014-08-22 08:07:12 -0700542 fs_config = "META/" + tree_subdir.lower() + "_filesystem_config.txt"
David Zeuthen2ce63ed2016-09-15 13:43:54 -0400543 data = _BuildBootableImage(os.path.join(unpack_dir, tree_subdir),
544 os.path.join(unpack_dir, fs_config),
Tao Baod42e97e2016-11-30 12:11:57 -0800545 info_dict, has_ramdisk, two_step_image)
Doug Zongker6f1d0312014-08-22 08:07:12 -0700546 if data:
547 return File(name, data)
548 return None
Doug Zongker55d93282011-01-25 17:03:34 -0800549
Doug Zongkereef39442009-04-02 12:14:19 -0700550
Doug Zongker75f17362009-12-08 13:46:44 -0800551def UnzipTemp(filename, pattern=None):
Doug Zongker55d93282011-01-25 17:03:34 -0800552 """Unzip the given archive into a temporary directory and return the name.
553
554 If filename is of the form "foo.zip+bar.zip", unzip foo.zip into a
555 temp dir, then unzip bar.zip into that_dir/BOOTABLE_IMAGES.
556
557 Returns (tempdir, zipobj) where zipobj is a zipfile.ZipFile (of the
558 main file), open for reading.
559 """
Doug Zongkereef39442009-04-02 12:14:19 -0700560
561 tmp = tempfile.mkdtemp(prefix="targetfiles-")
562 OPTIONS.tempfiles.append(tmp)
Doug Zongker55d93282011-01-25 17:03:34 -0800563
564 def unzip_to_dir(filename, dirname):
565 cmd = ["unzip", "-o", "-q", filename, "-d", dirname]
566 if pattern is not None:
Tao Bao6b0b2f92017-03-05 11:38:11 -0800567 cmd.extend(pattern)
Doug Zongker55d93282011-01-25 17:03:34 -0800568 p = Run(cmd, stdout=subprocess.PIPE)
569 p.communicate()
570 if p.returncode != 0:
571 raise ExternalError("failed to unzip input target-files \"%s\"" %
572 (filename,))
573
574 m = re.match(r"^(.*[.]zip)\+(.*[.]zip)$", filename, re.IGNORECASE)
575 if m:
576 unzip_to_dir(m.group(1), tmp)
577 unzip_to_dir(m.group(2), os.path.join(tmp, "BOOTABLE_IMAGES"))
578 filename = m.group(1)
579 else:
580 unzip_to_dir(filename, tmp)
581
582 return tmp, zipfile.ZipFile(filename, "r")
Doug Zongkereef39442009-04-02 12:14:19 -0700583
584
585def GetKeyPasswords(keylist):
586 """Given a list of keys, prompt the user to enter passwords for
587 those which require them. Return a {key: password} dict. password
588 will be None if the key has no password."""
589
Doug Zongker8ce7c252009-05-22 13:34:54 -0700590 no_passwords = []
591 need_passwords = []
T.R. Fullhart37e10522013-03-18 10:31:26 -0700592 key_passwords = {}
Doug Zongkereef39442009-04-02 12:14:19 -0700593 devnull = open("/dev/null", "w+b")
594 for k in sorted(keylist):
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800595 # We don't need a password for things that aren't really keys.
596 if k in SPECIAL_CERT_STRINGS:
Doug Zongker8ce7c252009-05-22 13:34:54 -0700597 no_passwords.append(k)
Doug Zongker43874f82009-04-14 14:05:15 -0700598 continue
599
T.R. Fullhart37e10522013-03-18 10:31:26 -0700600 p = Run(["openssl", "pkcs8", "-in", k+OPTIONS.private_key_suffix,
Doug Zongker602a84e2009-06-18 08:35:12 -0700601 "-inform", "DER", "-nocrypt"],
602 stdin=devnull.fileno(),
603 stdout=devnull.fileno(),
604 stderr=subprocess.STDOUT)
Doug Zongkereef39442009-04-02 12:14:19 -0700605 p.communicate()
606 if p.returncode == 0:
T.R. Fullhart37e10522013-03-18 10:31:26 -0700607 # Definitely an unencrypted key.
Doug Zongker8ce7c252009-05-22 13:34:54 -0700608 no_passwords.append(k)
Doug Zongkereef39442009-04-02 12:14:19 -0700609 else:
T.R. Fullhart37e10522013-03-18 10:31:26 -0700610 p = Run(["openssl", "pkcs8", "-in", k+OPTIONS.private_key_suffix,
611 "-inform", "DER", "-passin", "pass:"],
612 stdin=devnull.fileno(),
613 stdout=devnull.fileno(),
614 stderr=subprocess.PIPE)
Dan Albert8b72aef2015-03-23 19:13:21 -0700615 _, stderr = p.communicate()
T.R. Fullhart37e10522013-03-18 10:31:26 -0700616 if p.returncode == 0:
617 # Encrypted key with empty string as password.
618 key_passwords[k] = ''
619 elif stderr.startswith('Error decrypting key'):
620 # Definitely encrypted key.
621 # It would have said "Error reading key" if it didn't parse correctly.
622 need_passwords.append(k)
623 else:
624 # Potentially, a type of key that openssl doesn't understand.
625 # We'll let the routines in signapk.jar handle it.
626 no_passwords.append(k)
Doug Zongkereef39442009-04-02 12:14:19 -0700627 devnull.close()
Doug Zongker8ce7c252009-05-22 13:34:54 -0700628
T.R. Fullhart37e10522013-03-18 10:31:26 -0700629 key_passwords.update(PasswordManager().GetPasswords(need_passwords))
Doug Zongker8ce7c252009-05-22 13:34:54 -0700630 key_passwords.update(dict.fromkeys(no_passwords, None))
Doug Zongkereef39442009-04-02 12:14:19 -0700631 return key_passwords
632
633
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800634def GetMinSdkVersion(apk_name):
635 """Get the minSdkVersion delared in the APK. This can be both a decimal number
636 (API Level) or a codename.
637 """
638
639 p = Run(["aapt", "dump", "badging", apk_name], stdout=subprocess.PIPE)
640 output, err = p.communicate()
641 if err:
642 raise ExternalError("Failed to obtain minSdkVersion: aapt return code %s"
643 % (p.returncode,))
644
645 for line in output.split("\n"):
646 # Looking for lines such as sdkVersion:'23' or sdkVersion:'M'
647 m = re.match(r'sdkVersion:\'([^\']*)\'', line)
648 if m:
649 return m.group(1)
650 raise ExternalError("No minSdkVersion returned by aapt")
651
652
653def GetMinSdkVersionInt(apk_name, codename_to_api_level_map):
654 """Get the minSdkVersion declared in the APK as a number (API Level). If
655 minSdkVersion is set to a codename, it is translated to a number using the
656 provided map.
657 """
658
659 version = GetMinSdkVersion(apk_name)
660 try:
661 return int(version)
662 except ValueError:
663 # Not a decimal number. Codename?
664 if version in codename_to_api_level_map:
665 return codename_to_api_level_map[version]
666 else:
667 raise ExternalError("Unknown minSdkVersion: '%s'. Known codenames: %s"
668 % (version, codename_to_api_level_map))
669
670
671def SignFile(input_name, output_name, key, password, min_api_level=None,
672 codename_to_api_level_map=dict(),
673 whole_file=False):
Doug Zongkereef39442009-04-02 12:14:19 -0700674 """Sign the input_name zip/jar/apk, producing output_name. Use the
675 given key and password (the latter may be None if the key does not
676 have a password.
677
Doug Zongker951495f2009-08-14 12:44:19 -0700678 If whole_file is true, use the "-w" option to SignApk to embed a
679 signature that covers the whole file in the archive comment of the
680 zip file.
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800681
682 min_api_level is the API Level (int) of the oldest platform this file may end
683 up on. If not specified for an APK, the API Level is obtained by interpreting
684 the minSdkVersion attribute of the APK's AndroidManifest.xml.
685
686 codename_to_api_level_map is needed to translate the codename which may be
687 encountered as the APK's minSdkVersion.
Doug Zongkereef39442009-04-02 12:14:19 -0700688 """
Doug Zongker951495f2009-08-14 12:44:19 -0700689
Alex Klyubin9667b182015-12-10 13:38:50 -0800690 java_library_path = os.path.join(
691 OPTIONS.search_path, OPTIONS.signapk_shared_library_path)
692
Tao Baoe95540e2016-11-08 12:08:53 -0800693 cmd = ([OPTIONS.java_path] + OPTIONS.java_args +
694 ["-Djava.library.path=" + java_library_path,
695 "-jar", os.path.join(OPTIONS.search_path, OPTIONS.signapk_path)] +
696 OPTIONS.extra_signapk_args)
Doug Zongker951495f2009-08-14 12:44:19 -0700697 if whole_file:
698 cmd.append("-w")
Alex Klyubin2cfd1d12016-01-13 10:32:47 -0800699
700 min_sdk_version = min_api_level
701 if min_sdk_version is None:
702 if not whole_file:
703 min_sdk_version = GetMinSdkVersionInt(
704 input_name, codename_to_api_level_map)
705 if min_sdk_version is not None:
706 cmd.extend(["--min-sdk-version", str(min_sdk_version)])
707
T.R. Fullhart37e10522013-03-18 10:31:26 -0700708 cmd.extend([key + OPTIONS.public_key_suffix,
709 key + OPTIONS.private_key_suffix,
Alex Klyubineb756d72015-12-04 09:21:08 -0800710 input_name, output_name])
Doug Zongker951495f2009-08-14 12:44:19 -0700711
712 p = Run(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
Doug Zongkereef39442009-04-02 12:14:19 -0700713 if password is not None:
714 password += "\n"
715 p.communicate(password)
716 if p.returncode != 0:
717 raise ExternalError("signapk.jar failed: return code %s" % (p.returncode,))
718
Doug Zongkereef39442009-04-02 12:14:19 -0700719
Doug Zongker37974732010-09-16 17:44:38 -0700720def CheckSize(data, target, info_dict):
Doug Zongkereef39442009-04-02 12:14:19 -0700721 """Check the data string passed against the max size limit, if
722 any, for the given target. Raise exception if the data is too big.
723 Print a warning if the data is nearing the maximum size."""
Doug Zongkerc77a9ad2010-09-16 11:28:43 -0700724
Dan Albert8b72aef2015-03-23 19:13:21 -0700725 if target.endswith(".img"):
726 target = target[:-4]
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700727 mount_point = "/" + target
728
Ying Wangf8824af2014-06-03 14:07:27 -0700729 fs_type = None
730 limit = None
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700731 if info_dict["fstab"]:
Dan Albert8b72aef2015-03-23 19:13:21 -0700732 if mount_point == "/userdata":
733 mount_point = "/data"
Doug Zongker9ce0fb62010-09-20 18:04:41 -0700734 p = info_dict["fstab"][mount_point]
735 fs_type = p.fs_type
Andrew Boie0f9aec82012-02-14 09:32:52 -0800736 device = p.device
737 if "/" in device:
738 device = device[device.rfind("/")+1:]
739 limit = info_dict.get(device + "_size", None)
Dan Albert8b72aef2015-03-23 19:13:21 -0700740 if not fs_type or not limit:
741 return
Doug Zongkereef39442009-04-02 12:14:19 -0700742
Andrew Boie0f9aec82012-02-14 09:32:52 -0800743 size = len(data)
744 pct = float(size) * 100.0 / limit
745 msg = "%s size (%d) is %.2f%% of limit (%d)" % (target, size, pct, limit)
746 if pct >= 99.0:
747 raise ExternalError(msg)
748 elif pct >= 95.0:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800749 print("\n WARNING: %s\n" % (msg,))
Andrew Boie0f9aec82012-02-14 09:32:52 -0800750 elif OPTIONS.verbose:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800751 print(" ", msg)
Doug Zongkereef39442009-04-02 12:14:19 -0700752
753
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800754def ReadApkCerts(tf_zip):
755 """Given a target_files ZipFile, parse the META/apkcerts.txt file
756 and return a {package: cert} dict."""
757 certmap = {}
758 for line in tf_zip.read("META/apkcerts.txt").split("\n"):
759 line = line.strip()
Dan Albert8b72aef2015-03-23 19:13:21 -0700760 if not line:
761 continue
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800762 m = re.match(r'^name="(.*)"\s+certificate="(.*)"\s+'
763 r'private_key="(.*)"$', line)
764 if m:
765 name, cert, privkey = m.groups()
T.R. Fullhart37e10522013-03-18 10:31:26 -0700766 public_key_suffix_len = len(OPTIONS.public_key_suffix)
767 private_key_suffix_len = len(OPTIONS.private_key_suffix)
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800768 if cert in SPECIAL_CERT_STRINGS and not privkey:
769 certmap[name] = cert
T.R. Fullhart37e10522013-03-18 10:31:26 -0700770 elif (cert.endswith(OPTIONS.public_key_suffix) and
771 privkey.endswith(OPTIONS.private_key_suffix) and
772 cert[:-public_key_suffix_len] == privkey[:-private_key_suffix_len]):
773 certmap[name] = cert[:-public_key_suffix_len]
Doug Zongkerf6a53aa2009-12-15 15:06:55 -0800774 else:
775 raise ValueError("failed to parse line from apkcerts.txt:\n" + line)
776 return certmap
777
778
Doug Zongkereef39442009-04-02 12:14:19 -0700779COMMON_DOCSTRING = """
780 -p (--path) <dir>
Doug Zongker602a84e2009-06-18 08:35:12 -0700781 Prepend <dir>/bin to the list of places to search for binaries
782 run by this script, and expect to find jars in <dir>/framework.
Doug Zongkereef39442009-04-02 12:14:19 -0700783
Doug Zongker05d3dea2009-06-22 11:32:31 -0700784 -s (--device_specific) <file>
785 Path to the python module containing device-specific
786 releasetools code.
787
Doug Zongker8bec09e2009-11-30 15:37:14 -0800788 -x (--extra) <key=value>
789 Add a key/value pair to the 'extras' dict, which device-specific
790 extension code may look at.
791
Doug Zongkereef39442009-04-02 12:14:19 -0700792 -v (--verbose)
793 Show command lines being executed.
794
795 -h (--help)
796 Display this usage message and exit.
797"""
798
799def Usage(docstring):
Tao Bao89fbb0f2017-01-10 10:47:58 -0800800 print(docstring.rstrip("\n"))
801 print(COMMON_DOCSTRING)
Doug Zongkereef39442009-04-02 12:14:19 -0700802
803
804def ParseOptions(argv,
805 docstring,
806 extra_opts="", extra_long_opts=(),
807 extra_option_handler=None):
808 """Parse the options in argv and return any arguments that aren't
809 flags. docstring is the calling module's docstring, to be displayed
810 for errors and -h. extra_opts and extra_long_opts are for flags
811 defined by the caller, which are processed by passing them to
812 extra_option_handler."""
813
814 try:
815 opts, args = getopt.getopt(
Doug Zongker8bec09e2009-11-30 15:37:14 -0800816 argv, "hvp:s:x:" + extra_opts,
Alex Klyubin9667b182015-12-10 13:38:50 -0800817 ["help", "verbose", "path=", "signapk_path=",
818 "signapk_shared_library_path=", "extra_signapk_args=",
Baligh Uddinbdc2e312014-09-05 17:36:20 -0700819 "java_path=", "java_args=", "public_key_suffix=",
Baligh Uddin601ddea2015-06-09 15:48:14 -0700820 "private_key_suffix=", "boot_signer_path=", "boot_signer_args=",
821 "verity_signer_path=", "verity_signer_args=", "device_specific=",
Baligh Uddine2048682014-11-20 09:52:05 -0800822 "extra="] +
T.R. Fullhart37e10522013-03-18 10:31:26 -0700823 list(extra_long_opts))
Dan Albert8b72aef2015-03-23 19:13:21 -0700824 except getopt.GetoptError as err:
Doug Zongkereef39442009-04-02 12:14:19 -0700825 Usage(docstring)
Tao Bao89fbb0f2017-01-10 10:47:58 -0800826 print("**", str(err), "**")
Doug Zongkereef39442009-04-02 12:14:19 -0700827 sys.exit(2)
828
Doug Zongkereef39442009-04-02 12:14:19 -0700829 for o, a in opts:
830 if o in ("-h", "--help"):
831 Usage(docstring)
832 sys.exit()
833 elif o in ("-v", "--verbose"):
834 OPTIONS.verbose = True
835 elif o in ("-p", "--path"):
Doug Zongker602a84e2009-06-18 08:35:12 -0700836 OPTIONS.search_path = a
T.R. Fullhart37e10522013-03-18 10:31:26 -0700837 elif o in ("--signapk_path",):
838 OPTIONS.signapk_path = a
Alex Klyubin9667b182015-12-10 13:38:50 -0800839 elif o in ("--signapk_shared_library_path",):
840 OPTIONS.signapk_shared_library_path = a
T.R. Fullhart37e10522013-03-18 10:31:26 -0700841 elif o in ("--extra_signapk_args",):
842 OPTIONS.extra_signapk_args = shlex.split(a)
843 elif o in ("--java_path",):
844 OPTIONS.java_path = a
Baligh Uddin339ee492014-09-05 11:18:07 -0700845 elif o in ("--java_args",):
Tao Baoe95540e2016-11-08 12:08:53 -0800846 OPTIONS.java_args = shlex.split(a)
T.R. Fullhart37e10522013-03-18 10:31:26 -0700847 elif o in ("--public_key_suffix",):
848 OPTIONS.public_key_suffix = a
849 elif o in ("--private_key_suffix",):
850 OPTIONS.private_key_suffix = a
Baligh Uddine2048682014-11-20 09:52:05 -0800851 elif o in ("--boot_signer_path",):
852 OPTIONS.boot_signer_path = a
Baligh Uddin601ddea2015-06-09 15:48:14 -0700853 elif o in ("--boot_signer_args",):
854 OPTIONS.boot_signer_args = shlex.split(a)
855 elif o in ("--verity_signer_path",):
856 OPTIONS.verity_signer_path = a
857 elif o in ("--verity_signer_args",):
858 OPTIONS.verity_signer_args = shlex.split(a)
Doug Zongker05d3dea2009-06-22 11:32:31 -0700859 elif o in ("-s", "--device_specific"):
860 OPTIONS.device_specific = a
Doug Zongker5ecba702009-12-03 16:36:20 -0800861 elif o in ("-x", "--extra"):
Doug Zongker8bec09e2009-11-30 15:37:14 -0800862 key, value = a.split("=", 1)
863 OPTIONS.extras[key] = value
Doug Zongkereef39442009-04-02 12:14:19 -0700864 else:
865 if extra_option_handler is None or not extra_option_handler(o, a):
866 assert False, "unknown option \"%s\"" % (o,)
867
Doug Zongker85448772014-09-09 14:59:20 -0700868 if OPTIONS.search_path:
869 os.environ["PATH"] = (os.path.join(OPTIONS.search_path, "bin") +
870 os.pathsep + os.environ["PATH"])
Doug Zongkereef39442009-04-02 12:14:19 -0700871
872 return args
873
874
Tao Bao4c851b12016-09-19 13:54:38 -0700875def MakeTempFile(prefix='tmp', suffix=''):
Doug Zongkerfc44a512014-08-26 13:10:25 -0700876 """Make a temp file and add it to the list of things to be deleted
877 when Cleanup() is called. Return the filename."""
878 fd, fn = tempfile.mkstemp(prefix=prefix, suffix=suffix)
879 os.close(fd)
880 OPTIONS.tempfiles.append(fn)
881 return fn
882
883
Doug Zongkereef39442009-04-02 12:14:19 -0700884def Cleanup():
885 for i in OPTIONS.tempfiles:
886 if os.path.isdir(i):
887 shutil.rmtree(i)
888 else:
889 os.remove(i)
Doug Zongker8ce7c252009-05-22 13:34:54 -0700890
891
892class PasswordManager(object):
893 def __init__(self):
894 self.editor = os.getenv("EDITOR", None)
895 self.pwfile = os.getenv("ANDROID_PW_FILE", None)
896
897 def GetPasswords(self, items):
898 """Get passwords corresponding to each string in 'items',
899 returning a dict. (The dict may have keys in addition to the
900 values in 'items'.)
901
902 Uses the passwords in $ANDROID_PW_FILE if available, letting the
903 user edit that file to add more needed passwords. If no editor is
904 available, or $ANDROID_PW_FILE isn't define, prompts the user
905 interactively in the ordinary way.
906 """
907
908 current = self.ReadFile()
909
910 first = True
911 while True:
912 missing = []
913 for i in items:
914 if i not in current or not current[i]:
915 missing.append(i)
916 # Are all the passwords already in the file?
Dan Albert8b72aef2015-03-23 19:13:21 -0700917 if not missing:
918 return current
Doug Zongker8ce7c252009-05-22 13:34:54 -0700919
920 for i in missing:
921 current[i] = ""
922
923 if not first:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800924 print("key file %s still missing some passwords." % (self.pwfile,))
Doug Zongker8ce7c252009-05-22 13:34:54 -0700925 answer = raw_input("try to edit again? [y]> ").strip()
926 if answer and answer[0] not in 'yY':
927 raise RuntimeError("key passwords unavailable")
928 first = False
929
930 current = self.UpdateAndReadFile(current)
931
Dan Albert8b72aef2015-03-23 19:13:21 -0700932 def PromptResult(self, current): # pylint: disable=no-self-use
Doug Zongker8ce7c252009-05-22 13:34:54 -0700933 """Prompt the user to enter a value (password) for each key in
934 'current' whose value is fales. Returns a new dict with all the
935 values.
936 """
937 result = {}
938 for k, v in sorted(current.iteritems()):
939 if v:
940 result[k] = v
941 else:
942 while True:
Dan Albert8b72aef2015-03-23 19:13:21 -0700943 result[k] = getpass.getpass(
944 "Enter password for %s key> " % k).strip()
945 if result[k]:
946 break
Doug Zongker8ce7c252009-05-22 13:34:54 -0700947 return result
948
949 def UpdateAndReadFile(self, current):
950 if not self.editor or not self.pwfile:
951 return self.PromptResult(current)
952
953 f = open(self.pwfile, "w")
Dan Albert8b72aef2015-03-23 19:13:21 -0700954 os.chmod(self.pwfile, 0o600)
Doug Zongker8ce7c252009-05-22 13:34:54 -0700955 f.write("# Enter key passwords between the [[[ ]]] brackets.\n")
956 f.write("# (Additional spaces are harmless.)\n\n")
957
958 first_line = None
Dan Albert8b72aef2015-03-23 19:13:21 -0700959 sorted_list = sorted([(not v, k, v) for (k, v) in current.iteritems()])
960 for i, (_, k, v) in enumerate(sorted_list):
Doug Zongker8ce7c252009-05-22 13:34:54 -0700961 f.write("[[[ %s ]]] %s\n" % (v, k))
962 if not v and first_line is None:
963 # position cursor on first line with no password.
964 first_line = i + 4
965 f.close()
966
967 p = Run([self.editor, "+%d" % (first_line,), self.pwfile])
968 _, _ = p.communicate()
969
970 return self.ReadFile()
971
972 def ReadFile(self):
973 result = {}
Dan Albert8b72aef2015-03-23 19:13:21 -0700974 if self.pwfile is None:
975 return result
Doug Zongker8ce7c252009-05-22 13:34:54 -0700976 try:
977 f = open(self.pwfile, "r")
978 for line in f:
979 line = line.strip()
Dan Albert8b72aef2015-03-23 19:13:21 -0700980 if not line or line[0] == '#':
981 continue
Doug Zongker8ce7c252009-05-22 13:34:54 -0700982 m = re.match(r"^\[\[\[\s*(.*?)\s*\]\]\]\s*(\S+)$", line)
983 if not m:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800984 print("failed to parse password file: ", line)
Doug Zongker8ce7c252009-05-22 13:34:54 -0700985 else:
986 result[m.group(2)] = m.group(1)
987 f.close()
Dan Albert8b72aef2015-03-23 19:13:21 -0700988 except IOError as e:
Doug Zongker8ce7c252009-05-22 13:34:54 -0700989 if e.errno != errno.ENOENT:
Tao Bao89fbb0f2017-01-10 10:47:58 -0800990 print("error reading password file: ", str(e))
Doug Zongker8ce7c252009-05-22 13:34:54 -0700991 return result
Doug Zongker048e7ca2009-06-15 14:31:53 -0700992
993
Dan Albert8e0178d2015-01-27 15:53:15 -0800994def ZipWrite(zip_file, filename, arcname=None, perms=0o644,
995 compress_type=None):
996 import datetime
997
998 # http://b/18015246
999 # Python 2.7's zipfile implementation wrongly thinks that zip64 is required
1000 # for files larger than 2GiB. We can work around this by adjusting their
1001 # limit. Note that `zipfile.writestr()` will not work for strings larger than
1002 # 2GiB. The Python interpreter sometimes rejects strings that large (though
1003 # it isn't clear to me exactly what circumstances cause this).
1004 # `zipfile.write()` must be used directly to work around this.
1005 #
1006 # This mess can be avoided if we port to python3.
1007 saved_zip64_limit = zipfile.ZIP64_LIMIT
1008 zipfile.ZIP64_LIMIT = (1 << 32) - 1
1009
1010 if compress_type is None:
1011 compress_type = zip_file.compression
1012 if arcname is None:
1013 arcname = filename
1014
1015 saved_stat = os.stat(filename)
1016
1017 try:
1018 # `zipfile.write()` doesn't allow us to pass ZipInfo, so just modify the
1019 # file to be zipped and reset it when we're done.
1020 os.chmod(filename, perms)
1021
1022 # Use a fixed timestamp so the output is repeatable.
1023 epoch = datetime.datetime.fromtimestamp(0)
1024 timestamp = (datetime.datetime(2009, 1, 1) - epoch).total_seconds()
1025 os.utime(filename, (timestamp, timestamp))
1026
1027 zip_file.write(filename, arcname=arcname, compress_type=compress_type)
1028 finally:
1029 os.chmod(filename, saved_stat.st_mode)
1030 os.utime(filename, (saved_stat.st_atime, saved_stat.st_mtime))
1031 zipfile.ZIP64_LIMIT = saved_zip64_limit
1032
1033
Tao Bao58c1b962015-05-20 09:32:18 -07001034def ZipWriteStr(zip_file, zinfo_or_arcname, data, perms=None,
Tao Baof3282b42015-04-01 11:21:55 -07001035 compress_type=None):
1036 """Wrap zipfile.writestr() function to work around the zip64 limit.
1037
1038 Even with the ZIP64_LIMIT workaround, it won't allow writing a string
1039 longer than 2GiB. It gives 'OverflowError: size does not fit in an int'
1040 when calling crc32(bytes).
1041
1042 But it still works fine to write a shorter string into a large zip file.
1043 We should use ZipWrite() whenever possible, and only use ZipWriteStr()
1044 when we know the string won't be too long.
1045 """
1046
1047 saved_zip64_limit = zipfile.ZIP64_LIMIT
1048 zipfile.ZIP64_LIMIT = (1 << 32) - 1
1049
1050 if not isinstance(zinfo_or_arcname, zipfile.ZipInfo):
1051 zinfo = zipfile.ZipInfo(filename=zinfo_or_arcname)
Dan Albert8b72aef2015-03-23 19:13:21 -07001052 zinfo.compress_type = zip_file.compression
Tao Bao58c1b962015-05-20 09:32:18 -07001053 if perms is None:
Tao Bao2a410582015-07-10 17:18:23 -07001054 perms = 0o100644
Geremy Condra36bd3652014-02-06 19:45:10 -08001055 else:
Tao Baof3282b42015-04-01 11:21:55 -07001056 zinfo = zinfo_or_arcname
1057
1058 # If compress_type is given, it overrides the value in zinfo.
1059 if compress_type is not None:
1060 zinfo.compress_type = compress_type
1061
Tao Bao58c1b962015-05-20 09:32:18 -07001062 # If perms is given, it has a priority.
1063 if perms is not None:
Tao Bao2a410582015-07-10 17:18:23 -07001064 # If perms doesn't set the file type, mark it as a regular file.
1065 if perms & 0o770000 == 0:
1066 perms |= 0o100000
Tao Bao58c1b962015-05-20 09:32:18 -07001067 zinfo.external_attr = perms << 16
1068
Tao Baof3282b42015-04-01 11:21:55 -07001069 # Use a fixed timestamp so the output is repeatable.
Tao Baof3282b42015-04-01 11:21:55 -07001070 zinfo.date_time = (2009, 1, 1, 0, 0, 0)
1071
Dan Albert8b72aef2015-03-23 19:13:21 -07001072 zip_file.writestr(zinfo, data)
Tao Baof3282b42015-04-01 11:21:55 -07001073 zipfile.ZIP64_LIMIT = saved_zip64_limit
1074
1075
1076def ZipClose(zip_file):
1077 # http://b/18015246
1078 # zipfile also refers to ZIP64_LIMIT during close() when it writes out the
1079 # central directory.
1080 saved_zip64_limit = zipfile.ZIP64_LIMIT
1081 zipfile.ZIP64_LIMIT = (1 << 32) - 1
1082
1083 zip_file.close()
1084
1085 zipfile.ZIP64_LIMIT = saved_zip64_limit
Doug Zongker05d3dea2009-06-22 11:32:31 -07001086
1087
1088class DeviceSpecificParams(object):
1089 module = None
1090 def __init__(self, **kwargs):
1091 """Keyword arguments to the constructor become attributes of this
1092 object, which is passed to all functions in the device-specific
1093 module."""
1094 for k, v in kwargs.iteritems():
1095 setattr(self, k, v)
Doug Zongker8bec09e2009-11-30 15:37:14 -08001096 self.extras = OPTIONS.extras
Doug Zongker05d3dea2009-06-22 11:32:31 -07001097
1098 if self.module is None:
1099 path = OPTIONS.device_specific
Dan Albert8b72aef2015-03-23 19:13:21 -07001100 if not path:
1101 return
Doug Zongker8e2f2b92009-06-24 14:34:57 -07001102 try:
1103 if os.path.isdir(path):
1104 info = imp.find_module("releasetools", [path])
1105 else:
1106 d, f = os.path.split(path)
1107 b, x = os.path.splitext(f)
1108 if x == ".py":
1109 f = b
1110 info = imp.find_module(f, [d])
Tao Bao89fbb0f2017-01-10 10:47:58 -08001111 print("loaded device-specific extensions from", path)
Doug Zongker8e2f2b92009-06-24 14:34:57 -07001112 self.module = imp.load_module("device_specific", *info)
1113 except ImportError:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001114 print("unable to load device-specific module; assuming none")
Doug Zongker05d3dea2009-06-22 11:32:31 -07001115
1116 def _DoCall(self, function_name, *args, **kwargs):
1117 """Call the named function in the device-specific module, passing
1118 the given args and kwargs. The first argument to the call will be
1119 the DeviceSpecific object itself. If there is no module, or the
1120 module does not define the function, return the value of the
1121 'default' kwarg (which itself defaults to None)."""
1122 if self.module is None or not hasattr(self.module, function_name):
1123 return kwargs.get("default", None)
1124 return getattr(self.module, function_name)(*((self,) + args), **kwargs)
1125
1126 def FullOTA_Assertions(self):
1127 """Called after emitting the block of assertions at the top of a
1128 full OTA package. Implementations can add whatever additional
1129 assertions they like."""
1130 return self._DoCall("FullOTA_Assertions")
1131
Doug Zongkere5ff5902012-01-17 10:55:37 -08001132 def FullOTA_InstallBegin(self):
1133 """Called at the start of full OTA installation."""
1134 return self._DoCall("FullOTA_InstallBegin")
1135
Doug Zongker05d3dea2009-06-22 11:32:31 -07001136 def FullOTA_InstallEnd(self):
1137 """Called at the end of full OTA installation; typically this is
1138 used to install the image for the device's baseband processor."""
1139 return self._DoCall("FullOTA_InstallEnd")
1140
1141 def IncrementalOTA_Assertions(self):
1142 """Called after emitting the block of assertions at the top of an
1143 incremental OTA package. Implementations can add whatever
1144 additional assertions they like."""
1145 return self._DoCall("IncrementalOTA_Assertions")
1146
Doug Zongkere5ff5902012-01-17 10:55:37 -08001147 def IncrementalOTA_VerifyBegin(self):
1148 """Called at the start of the verification phase of incremental
1149 OTA installation; additional checks can be placed here to abort
1150 the script before any changes are made."""
1151 return self._DoCall("IncrementalOTA_VerifyBegin")
1152
Doug Zongker05d3dea2009-06-22 11:32:31 -07001153 def IncrementalOTA_VerifyEnd(self):
1154 """Called at the end of the verification phase of incremental OTA
1155 installation; additional checks can be placed here to abort the
1156 script before any changes are made."""
1157 return self._DoCall("IncrementalOTA_VerifyEnd")
1158
Doug Zongkere5ff5902012-01-17 10:55:37 -08001159 def IncrementalOTA_InstallBegin(self):
1160 """Called at the start of incremental OTA installation (after
1161 verification is complete)."""
1162 return self._DoCall("IncrementalOTA_InstallBegin")
1163
Doug Zongker05d3dea2009-06-22 11:32:31 -07001164 def IncrementalOTA_InstallEnd(self):
1165 """Called at the end of incremental OTA installation; typically
1166 this is used to install the image for the device's baseband
1167 processor."""
1168 return self._DoCall("IncrementalOTA_InstallEnd")
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001169
Tao Bao9bc6bb22015-11-09 16:58:28 -08001170 def VerifyOTA_Assertions(self):
1171 return self._DoCall("VerifyOTA_Assertions")
1172
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001173class File(object):
YOUNG HO CHAccc5c402016-10-13 13:40:46 +09001174 def __init__(self, name, data, compress_size = None):
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001175 self.name = name
1176 self.data = data
1177 self.size = len(data)
YOUNG HO CHAccc5c402016-10-13 13:40:46 +09001178 self.compress_size = compress_size or self.size
Doug Zongker55d93282011-01-25 17:03:34 -08001179 self.sha1 = sha1(data).hexdigest()
1180
1181 @classmethod
1182 def FromLocalFile(cls, name, diskname):
1183 f = open(diskname, "rb")
1184 data = f.read()
1185 f.close()
1186 return File(name, data)
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001187
1188 def WriteToTemp(self):
1189 t = tempfile.NamedTemporaryFile()
1190 t.write(self.data)
1191 t.flush()
1192 return t
1193
Dan Willemsen2ee00d52017-03-05 19:51:56 -08001194 def WriteToDir(self, d):
1195 with open(os.path.join(d, self.name), "wb") as fp:
1196 fp.write(self.data)
1197
Geremy Condra36bd3652014-02-06 19:45:10 -08001198 def AddToZip(self, z, compression=None):
Tao Baof3282b42015-04-01 11:21:55 -07001199 ZipWriteStr(z, self.name, self.data, compress_type=compression)
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001200
1201DIFF_PROGRAM_BY_EXT = {
1202 ".gz" : "imgdiff",
1203 ".zip" : ["imgdiff", "-z"],
1204 ".jar" : ["imgdiff", "-z"],
1205 ".apk" : ["imgdiff", "-z"],
1206 ".img" : "imgdiff",
1207 }
1208
1209class Difference(object):
Doug Zongker24cd2802012-08-14 16:36:15 -07001210 def __init__(self, tf, sf, diff_program=None):
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001211 self.tf = tf
1212 self.sf = sf
1213 self.patch = None
Doug Zongker24cd2802012-08-14 16:36:15 -07001214 self.diff_program = diff_program
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001215
1216 def ComputePatch(self):
1217 """Compute the patch (as a string of data) needed to turn sf into
1218 tf. Returns the same tuple as GetPatch()."""
1219
1220 tf = self.tf
1221 sf = self.sf
1222
Doug Zongker24cd2802012-08-14 16:36:15 -07001223 if self.diff_program:
1224 diff_program = self.diff_program
1225 else:
1226 ext = os.path.splitext(tf.name)[1]
1227 diff_program = DIFF_PROGRAM_BY_EXT.get(ext, "bsdiff")
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001228
1229 ttemp = tf.WriteToTemp()
1230 stemp = sf.WriteToTemp()
1231
1232 ext = os.path.splitext(tf.name)[1]
1233
1234 try:
1235 ptemp = tempfile.NamedTemporaryFile()
1236 if isinstance(diff_program, list):
1237 cmd = copy.copy(diff_program)
1238 else:
1239 cmd = [diff_program]
1240 cmd.append(stemp.name)
1241 cmd.append(ttemp.name)
1242 cmd.append(ptemp.name)
1243 p = Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Doug Zongkerf8340082014-08-05 10:39:37 -07001244 err = []
1245 def run():
1246 _, e = p.communicate()
Dan Albert8b72aef2015-03-23 19:13:21 -07001247 if e:
1248 err.append(e)
Doug Zongkerf8340082014-08-05 10:39:37 -07001249 th = threading.Thread(target=run)
1250 th.start()
1251 th.join(timeout=300) # 5 mins
1252 if th.is_alive():
Tao Bao89fbb0f2017-01-10 10:47:58 -08001253 print("WARNING: diff command timed out")
Doug Zongkerf8340082014-08-05 10:39:37 -07001254 p.terminate()
1255 th.join(5)
1256 if th.is_alive():
1257 p.kill()
1258 th.join()
1259
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001260 if err or p.returncode != 0:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001261 print("WARNING: failure running %s:\n%s\n" % (
1262 diff_program, "".join(err)))
Doug Zongkerf8340082014-08-05 10:39:37 -07001263 self.patch = None
1264 return None, None, None
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001265 diff = ptemp.read()
1266 finally:
1267 ptemp.close()
1268 stemp.close()
1269 ttemp.close()
1270
1271 self.patch = diff
1272 return self.tf, self.sf, self.patch
1273
1274
1275 def GetPatch(self):
1276 """Return a tuple (target_file, source_file, patch_data).
1277 patch_data may be None if ComputePatch hasn't been called, or if
1278 computing the patch failed."""
1279 return self.tf, self.sf, self.patch
1280
1281
1282def ComputeDifferences(diffs):
1283 """Call ComputePatch on all the Difference objects in 'diffs'."""
Tao Bao89fbb0f2017-01-10 10:47:58 -08001284 print(len(diffs), "diffs to compute")
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001285
1286 # Do the largest files first, to try and reduce the long-pole effect.
1287 by_size = [(i.tf.size, i) for i in diffs]
1288 by_size.sort(reverse=True)
1289 by_size = [i[1] for i in by_size]
1290
1291 lock = threading.Lock()
1292 diff_iter = iter(by_size) # accessed under lock
1293
1294 def worker():
1295 try:
1296 lock.acquire()
1297 for d in diff_iter:
1298 lock.release()
1299 start = time.time()
1300 d.ComputePatch()
1301 dur = time.time() - start
1302 lock.acquire()
1303
1304 tf, sf, patch = d.GetPatch()
1305 if sf.name == tf.name:
1306 name = tf.name
1307 else:
1308 name = "%s (%s)" % (tf.name, sf.name)
1309 if patch is None:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001310 print("patching failed! %s" % (name,))
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001311 else:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001312 print("%8.2f sec %8d / %8d bytes (%6.2f%%) %s" % (
1313 dur, len(patch), tf.size, 100.0 * len(patch) / tf.size, name))
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001314 lock.release()
Dan Albert8b72aef2015-03-23 19:13:21 -07001315 except Exception as e:
Tao Bao89fbb0f2017-01-10 10:47:58 -08001316 print(e)
Doug Zongkerea5d7a92010-09-12 15:26:16 -07001317 raise
1318
1319 # start worker threads; wait for them all to finish.
1320 threads = [threading.Thread(target=worker)
1321 for i in range(OPTIONS.worker_threads)]
1322 for th in threads:
1323 th.start()
1324 while threads:
1325 threads.pop().join()
Doug Zongker96a57e72010-09-26 14:57:41 -07001326
1327
Dan Albert8b72aef2015-03-23 19:13:21 -07001328class BlockDifference(object):
1329 def __init__(self, partition, tgt, src=None, check_first_block=False,
Tao Bao293fd132016-06-11 12:19:23 -07001330 version=None, disable_imgdiff=False):
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001331 self.tgt = tgt
1332 self.src = src
1333 self.partition = partition
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001334 self.check_first_block = check_first_block
Tao Bao293fd132016-06-11 12:19:23 -07001335 self.disable_imgdiff = disable_imgdiff
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001336
Tao Baodd2a5892015-03-12 12:32:37 -07001337 if version is None:
1338 version = 1
1339 if OPTIONS.info_dict:
1340 version = max(
1341 int(i) for i in
1342 OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(","))
1343 self.version = version
Doug Zongker62338182014-09-08 08:29:55 -07001344
1345 b = blockimgdiff.BlockImageDiff(tgt, src, threads=OPTIONS.worker_threads,
Tao Bao293fd132016-06-11 12:19:23 -07001346 version=self.version,
1347 disable_imgdiff=self.disable_imgdiff)
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001348 tmpdir = tempfile.mkdtemp()
1349 OPTIONS.tempfiles.append(tmpdir)
1350 self.path = os.path.join(tmpdir, partition)
1351 b.Compute(self.path)
Tao Baod8d14be2016-02-04 14:26:02 -08001352 self._required_cache = b.max_stashed_size
Tao Baod522bdc2016-04-12 15:53:16 -07001353 self.touched_src_ranges = b.touched_src_ranges
1354 self.touched_src_sha1 = b.touched_src_sha1
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001355
Tao Baoaac4ad52015-10-16 15:26:34 -07001356 if src is None:
1357 _, self.device = GetTypeAndDevice("/" + partition, OPTIONS.info_dict)
1358 else:
1359 _, self.device = GetTypeAndDevice("/" + partition,
1360 OPTIONS.source_info_dict)
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001361
Tao Baod8d14be2016-02-04 14:26:02 -08001362 @property
1363 def required_cache(self):
1364 return self._required_cache
1365
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001366 def WriteScript(self, script, output_zip, progress=None):
1367 if not self.src:
1368 # write the output unconditionally
Jesse Zhao75bcea02015-01-06 10:59:53 -08001369 script.Print("Patching %s image unconditionally..." % (self.partition,))
1370 else:
1371 script.Print("Patching %s image after verification." % (self.partition,))
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001372
Dan Albert8b72aef2015-03-23 19:13:21 -07001373 if progress:
1374 script.ShowProgress(progress, 0)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001375 self._WriteUpdate(script, output_zip)
Tianjie Xub2deb222016-03-25 15:01:33 -07001376 if OPTIONS.verify:
1377 self._WritePostInstallVerifyScript(script)
Jesse Zhao75bcea02015-01-06 10:59:53 -08001378
Tao Bao9bc6bb22015-11-09 16:58:28 -08001379 def WriteStrictVerifyScript(self, script):
1380 """Verify all the blocks in the care_map, including clobbered blocks.
1381
1382 This differs from the WriteVerifyScript() function: a) it prints different
1383 error messages; b) it doesn't allow half-way updated images to pass the
1384 verification."""
1385
1386 partition = self.partition
1387 script.Print("Verifying %s..." % (partition,))
1388 ranges = self.tgt.care_map
1389 ranges_str = ranges.to_string_raw()
1390 script.AppendExtra('range_sha1("%s", "%s") == "%s" && '
1391 'ui_print(" Verified.") || '
1392 'ui_print("\\"%s\\" has unexpected contents.");' % (
1393 self.device, ranges_str,
1394 self.tgt.TotalSha1(include_clobbered_blocks=True),
1395 self.device))
1396 script.AppendExtra("")
1397
Tao Baod522bdc2016-04-12 15:53:16 -07001398 def WriteVerifyScript(self, script, touched_blocks_only=False):
Sami Tolvanendd67a292014-12-09 16:40:34 +00001399 partition = self.partition
Tao Baof9efe282016-04-14 15:58:05 -07001400
1401 # full OTA
Jesse Zhao75bcea02015-01-06 10:59:53 -08001402 if not self.src:
Sami Tolvanendd67a292014-12-09 16:40:34 +00001403 script.Print("Image %s will be patched unconditionally." % (partition,))
Tao Baof9efe282016-04-14 15:58:05 -07001404
1405 # incremental OTA
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001406 else:
Tao Baod522bdc2016-04-12 15:53:16 -07001407 if touched_blocks_only and self.version >= 3:
1408 ranges = self.touched_src_ranges
1409 expected_sha1 = self.touched_src_sha1
1410 else:
1411 ranges = self.src.care_map.subtract(self.src.clobbered_blocks)
1412 expected_sha1 = self.src.TotalSha1()
Tao Baof9efe282016-04-14 15:58:05 -07001413
1414 # No blocks to be checked, skipping.
1415 if not ranges:
1416 return
1417
Tao Bao5ece99d2015-05-12 11:42:31 -07001418 ranges_str = ranges.to_string_raw()
Tao Bao9beea2a2017-02-28 19:15:21 -08001419 if self.version >= 3:
Sami Tolvanene09d0962015-04-24 11:54:01 +01001420 script.AppendExtra(('if (range_sha1("%s", "%s") == "%s" || '
1421 'block_image_verify("%s", '
Michael Runge910b0052015-02-11 19:28:08 -08001422 'package_extract_file("%s.transfer.list"), '
Sami Tolvanene09d0962015-04-24 11:54:01 +01001423 '"%s.new.dat", "%s.patch.dat")) then') % (
Tao Baod522bdc2016-04-12 15:53:16 -07001424 self.device, ranges_str, expected_sha1,
Sami Tolvanene09d0962015-04-24 11:54:01 +01001425 self.device, partition, partition, partition))
Michael Runge910b0052015-02-11 19:28:08 -08001426 else:
Dan Albert8b72aef2015-03-23 19:13:21 -07001427 script.AppendExtra('if range_sha1("%s", "%s") == "%s" then' % (
Tao Bao5ece99d2015-05-12 11:42:31 -07001428 self.device, ranges_str, self.src.TotalSha1()))
Tao Baodd2a5892015-03-12 12:32:37 -07001429 script.Print('Verified %s image...' % (partition,))
Dan Albert8b72aef2015-03-23 19:13:21 -07001430 script.AppendExtra('else')
Sami Tolvanendd67a292014-12-09 16:40:34 +00001431
Tianjie Xufc3422a2015-12-15 11:53:59 -08001432 if self.version >= 4:
1433
1434 # Bug: 21124327
1435 # When generating incrementals for the system and vendor partitions in
1436 # version 4 or newer, explicitly check the first block (which contains
1437 # the superblock) of the partition to see if it's what we expect. If
1438 # this check fails, give an explicit log message about the partition
1439 # having been remounted R/W (the most likely explanation).
1440 if self.check_first_block:
1441 script.AppendExtra('check_first_block("%s");' % (self.device,))
1442
1443 # If version >= 4, try block recovery before abort update
Tianjie Xu209db462016-05-24 17:34:52 -07001444 if partition == "system":
1445 code = ErrorCode.SYSTEM_RECOVER_FAILURE
1446 else:
1447 code = ErrorCode.VENDOR_RECOVER_FAILURE
Tianjie Xufc3422a2015-12-15 11:53:59 -08001448 script.AppendExtra((
1449 'ifelse (block_image_recover("{device}", "{ranges}") && '
1450 'block_image_verify("{device}", '
1451 'package_extract_file("{partition}.transfer.list"), '
1452 '"{partition}.new.dat", "{partition}.patch.dat"), '
1453 'ui_print("{partition} recovered successfully."), '
Tianjie Xu209db462016-05-24 17:34:52 -07001454 'abort("E{code}: {partition} partition fails to recover"));\n'
Tianjie Xufc3422a2015-12-15 11:53:59 -08001455 'endif;').format(device=self.device, ranges=ranges_str,
Tianjie Xu209db462016-05-24 17:34:52 -07001456 partition=partition, code=code))
Doug Zongkerb34fcce2014-09-11 09:34:56 -07001457
Tao Baodd2a5892015-03-12 12:32:37 -07001458 # Abort the OTA update. Note that the incremental OTA cannot be applied
1459 # even if it may match the checksum of the target partition.
1460 # a) If version < 3, operations like move and erase will make changes
1461 # unconditionally and damage the partition.
1462 # b) If version >= 3, it won't even reach here.
Tianjie Xufc3422a2015-12-15 11:53:59 -08001463 else:
Tianjie Xu209db462016-05-24 17:34:52 -07001464 if partition == "system":
1465 code = ErrorCode.SYSTEM_VERIFICATION_FAILURE
1466 else:
1467 code = ErrorCode.VENDOR_VERIFICATION_FAILURE
1468 script.AppendExtra((
1469 'abort("E%d: %s partition has unexpected contents");\n'
1470 'endif;') % (code, partition))
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001471
Tao Bao5fcaaef2015-06-01 13:40:49 -07001472 def _WritePostInstallVerifyScript(self, script):
1473 partition = self.partition
1474 script.Print('Verifying the updated %s image...' % (partition,))
1475 # Unlike pre-install verification, clobbered_blocks should not be ignored.
1476 ranges = self.tgt.care_map
1477 ranges_str = ranges.to_string_raw()
1478 script.AppendExtra('if range_sha1("%s", "%s") == "%s" then' % (
1479 self.device, ranges_str,
1480 self.tgt.TotalSha1(include_clobbered_blocks=True)))
Tao Baoe9b61912015-07-09 17:37:49 -07001481
1482 # Bug: 20881595
1483 # Verify that extended blocks are really zeroed out.
1484 if self.tgt.extended:
1485 ranges_str = self.tgt.extended.to_string_raw()
1486 script.AppendExtra('if range_sha1("%s", "%s") == "%s" then' % (
1487 self.device, ranges_str,
1488 self._HashZeroBlocks(self.tgt.extended.size())))
1489 script.Print('Verified the updated %s image.' % (partition,))
Tianjie Xu209db462016-05-24 17:34:52 -07001490 if partition == "system":
1491 code = ErrorCode.SYSTEM_NONZERO_CONTENTS
1492 else:
1493 code = ErrorCode.VENDOR_NONZERO_CONTENTS
Tao Baoe9b61912015-07-09 17:37:49 -07001494 script.AppendExtra(
1495 'else\n'
Tianjie Xu209db462016-05-24 17:34:52 -07001496 ' abort("E%d: %s partition has unexpected non-zero contents after '
1497 'OTA update");\n'
1498 'endif;' % (code, partition))
Tao Baoe9b61912015-07-09 17:37:49 -07001499 else:
1500 script.Print('Verified the updated %s image.' % (partition,))
1501
Tianjie Xu209db462016-05-24 17:34:52 -07001502 if partition == "system":
1503 code = ErrorCode.SYSTEM_UNEXPECTED_CONTENTS
1504 else:
1505 code = ErrorCode.VENDOR_UNEXPECTED_CONTENTS
1506
Tao Bao5fcaaef2015-06-01 13:40:49 -07001507 script.AppendExtra(
1508 'else\n'
Tianjie Xu209db462016-05-24 17:34:52 -07001509 ' abort("E%d: %s partition has unexpected contents after OTA '
1510 'update");\n'
1511 'endif;' % (code, partition))
Tao Bao5fcaaef2015-06-01 13:40:49 -07001512
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001513 def _WriteUpdate(self, script, output_zip):
Dan Albert8e0178d2015-01-27 15:53:15 -08001514 ZipWrite(output_zip,
1515 '{}.transfer.list'.format(self.path),
1516 '{}.transfer.list'.format(self.partition))
1517 ZipWrite(output_zip,
1518 '{}.new.dat'.format(self.path),
1519 '{}.new.dat'.format(self.partition))
1520 ZipWrite(output_zip,
1521 '{}.patch.dat'.format(self.path),
1522 '{}.patch.dat'.format(self.partition),
1523 compress_type=zipfile.ZIP_STORED)
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001524
Tianjie Xu209db462016-05-24 17:34:52 -07001525 if self.partition == "system":
1526 code = ErrorCode.SYSTEM_UPDATE_FAILURE
1527 else:
1528 code = ErrorCode.VENDOR_UPDATE_FAILURE
1529
Dan Albert8e0178d2015-01-27 15:53:15 -08001530 call = ('block_image_update("{device}", '
1531 'package_extract_file("{partition}.transfer.list"), '
Tianjie Xub2deb222016-03-25 15:01:33 -07001532 '"{partition}.new.dat", "{partition}.patch.dat") ||\n'
Tianjie Xu209db462016-05-24 17:34:52 -07001533 ' abort("E{code}: Failed to update {partition} image.");'.format(
1534 device=self.device, partition=self.partition, code=code))
Dan Albert8b72aef2015-03-23 19:13:21 -07001535 script.AppendExtra(script.WordWrap(call))
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001536
Dan Albert8b72aef2015-03-23 19:13:21 -07001537 def _HashBlocks(self, source, ranges): # pylint: disable=no-self-use
Sami Tolvanendd67a292014-12-09 16:40:34 +00001538 data = source.ReadRangeSet(ranges)
1539 ctx = sha1()
1540
1541 for p in data:
1542 ctx.update(p)
1543
1544 return ctx.hexdigest()
1545
Tao Baoe9b61912015-07-09 17:37:49 -07001546 def _HashZeroBlocks(self, num_blocks): # pylint: disable=no-self-use
1547 """Return the hash value for all zero blocks."""
1548 zero_block = '\x00' * 4096
1549 ctx = sha1()
1550 for _ in range(num_blocks):
1551 ctx.update(zero_block)
1552
1553 return ctx.hexdigest()
1554
Doug Zongkerab7ca1d2014-08-26 10:40:28 -07001555
1556DataImage = blockimgdiff.DataImage
1557
Doug Zongker96a57e72010-09-26 14:57:41 -07001558# map recovery.fstab's fs_types to mount/format "partition types"
Dan Albert8b72aef2015-03-23 19:13:21 -07001559PARTITION_TYPES = {
Dan Albert8b72aef2015-03-23 19:13:21 -07001560 "ext4": "EMMC",
1561 "emmc": "EMMC",
Mohamad Ayyash95e74c12015-05-01 15:39:36 -07001562 "f2fs": "EMMC",
1563 "squashfs": "EMMC"
Dan Albert8b72aef2015-03-23 19:13:21 -07001564}
Doug Zongker96a57e72010-09-26 14:57:41 -07001565
1566def GetTypeAndDevice(mount_point, info):
1567 fstab = info["fstab"]
1568 if fstab:
Dan Albert8b72aef2015-03-23 19:13:21 -07001569 return (PARTITION_TYPES[fstab[mount_point].fs_type],
1570 fstab[mount_point].device)
Doug Zongker96a57e72010-09-26 14:57:41 -07001571 else:
Dan Albert8b72aef2015-03-23 19:13:21 -07001572 raise KeyError
Baligh Uddinbeb6afd2013-11-13 00:22:34 +00001573
1574
1575def ParseCertificate(data):
1576 """Parse a PEM-format certificate."""
1577 cert = []
1578 save = False
1579 for line in data.split("\n"):
1580 if "--END CERTIFICATE--" in line:
1581 break
1582 if save:
1583 cert.append(line)
1584 if "--BEGIN CERTIFICATE--" in line:
1585 save = True
1586 cert = "".join(cert).decode('base64')
1587 return cert
Doug Zongkerc9253822014-02-04 12:17:58 -08001588
Doug Zongker412c02f2014-02-13 10:58:24 -08001589def MakeRecoveryPatch(input_dir, output_sink, recovery_img, boot_img,
1590 info_dict=None):
Doug Zongkerc9253822014-02-04 12:17:58 -08001591 """Generate a binary patch that creates the recovery image starting
1592 with the boot image. (Most of the space in these images is just the
1593 kernel, which is identical for the two, so the resulting patch
1594 should be efficient.) Add it to the output zip, along with a shell
1595 script that is run from init.rc on first boot to actually do the
1596 patching and install the new recovery image.
1597
1598 recovery_img and boot_img should be File objects for the
1599 corresponding images. info should be the dictionary returned by
1600 common.LoadInfoDict() on the input target_files.
1601 """
1602
Doug Zongker412c02f2014-02-13 10:58:24 -08001603 if info_dict is None:
1604 info_dict = OPTIONS.info_dict
1605
Tao Baof2cffbd2015-07-22 12:33:18 -07001606 full_recovery_image = info_dict.get("full_recovery_image", None) == "true"
Tao Bao7a5bf8a2015-07-21 18:01:20 -07001607 system_root_image = info_dict.get("system_root_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
Tao Bao9f0c8df2015-07-07 18:31:47 -07001663 # in the L release. Parse init.*.rc files to find out where the
Doug Zongkerc9253822014-02-04 12:17:58 -08001664 # target-files expects it to be, and put it there.
1665 sh_location = "etc/install-recovery.sh"
Tao Bao9f0c8df2015-07-07 18:31:47 -07001666 found = False
Tao Bao7a5bf8a2015-07-21 18:01:20 -07001667 if system_root_image:
1668 init_rc_dir = os.path.join(input_dir, "ROOT")
1669 else:
1670 init_rc_dir = os.path.join(input_dir, "BOOT", "RAMDISK")
Tao Bao9f0c8df2015-07-07 18:31:47 -07001671 init_rc_files = os.listdir(init_rc_dir)
1672 for init_rc_file in init_rc_files:
1673 if (not init_rc_file.startswith('init.') or
1674 not init_rc_file.endswith('.rc')):
1675 continue
1676
1677 with open(os.path.join(init_rc_dir, init_rc_file)) as f:
Doug Zongkerc9253822014-02-04 12:17:58 -08001678 for line in f:
Dan Albert8b72aef2015-03-23 19:13:21 -07001679 m = re.match(r"^service flash_recovery /system/(\S+)\s*$", line)
Doug Zongkerc9253822014-02-04 12:17:58 -08001680 if m:
1681 sh_location = m.group(1)
Tao Bao9f0c8df2015-07-07 18:31:47 -07001682 found = True
Doug Zongkerc9253822014-02-04 12:17:58 -08001683 break
Tao Bao9f0c8df2015-07-07 18:31:47 -07001684
1685 if found:
1686 break
1687
Tao Bao89fbb0f2017-01-10 10:47:58 -08001688 print("putting script in", sh_location)
Doug Zongkerc9253822014-02-04 12:17:58 -08001689
1690 output_sink(sh_location, sh)