blob: 934b73f517e19077c48fc5027b179cdf48587fe8 [file] [log] [blame]
Doug Zongker3c84f562014-07-31 11:06:30 -07001#!/usr/bin/env python
2#
3# Copyright (C) 2014 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""
18Given a target-files zipfile that does not contain images (ie, does
19not have an IMAGES/ top-level subdirectory), produce the images and
20add them to the zipfile.
21
Tianjie Xub48589a2016-08-03 19:21:52 -070022Usage: add_img_to_target_files [flag] target_files
23
24 -a (--add_missing)
25 Build and add missing images to "IMAGES/". If this option is
26 not specified, this script will simply exit when "IMAGES/"
27 directory exists in the target file.
28
29 -r (--rebuild_recovery)
30 Rebuild the recovery patch and write it to the system image. Only
Tao Bao4978fa92019-06-04 16:26:45 -070031 meaningful when system image needs to be rebuilt and there're separate
32 boot / recovery images.
Tianjie Xub48589a2016-08-03 19:21:52 -070033
34 --replace_verity_private_key
35 Replace the private key used for verity signing. (same as the option
36 in sign_target_files_apks)
37
38 --replace_verity_public_key
39 Replace the certificate (public key) used for verity verification. (same
40 as the option in sign_target_files_apks)
41
42 --is_signing
43 Skip building & adding the images for "userdata" and "cache" if we
44 are signing the target files.
Doug Zongker3c84f562014-07-31 11:06:30 -070045"""
46
Tao Bao89fbb0f2017-01-10 10:47:58 -080047from __future__ import print_function
48
Kelvin Zhang1caead02022-09-23 10:06:03 -070049import avbtool
Tao Bao822f5842015-09-30 16:01:14 -070050import datetime
Tao Bao32fcdab2018-10-12 10:30:39 -070051import logging
Doug Zongker3c84f562014-07-31 11:06:30 -070052import os
David Zeuthend995f4b2016-01-29 16:59:17 -050053import shlex
Ying Wang2a048392015-06-25 13:56:53 -070054import shutil
Rupert Shuttleworth72942742020-12-08 06:18:35 +000055import stat
Tao Bao6b9fef52017-12-01 16:13:22 -080056import sys
Tao Baod86e3112017-09-22 15:45:33 -070057import uuid
Kelvin Zhang834f5d42022-01-21 12:44:44 -080058import tempfile
Doug Zongker3c84f562014-07-31 11:06:30 -070059import zipfile
60
Doug Zongker3c84f562014-07-31 11:06:30 -070061import build_image
Yifan Hong055e6cf2018-11-29 13:51:48 -080062import build_super_image
Doug Zongker3c84f562014-07-31 11:06:30 -070063import common
Hongguang Chenf23364d2020-04-27 18:36:36 -070064import verity_utils
Kelvin Zhang5f0fcee2021-01-19 15:30:46 -050065import ota_metadata_pb2
Kelvin Zhangf294c872022-10-06 14:21:36 -070066import rangelib
67import sparse_img
Jeongik Cha03d014a2023-05-11 17:25:51 +090068from concurrent.futures import ThreadPoolExecutor
Kelvin Zhang1caead02022-09-23 10:06:03 -070069from apex_utils import GetApexInfoFromTargetFiles
70from common import ZipDelete, PARTITIONS_WITH_CARE_MAP, ExternalError, RunAndCheckOutput, IsSparseImage, MakeTempFile, ZipWrite
71
Tao Bao6b9fef52017-12-01 16:13:22 -080072if sys.hexversion < 0x02070000:
73 print("Python 2.7 or newer is required.", file=sys.stderr)
74 sys.exit(1)
75
Tao Bao32fcdab2018-10-12 10:30:39 -070076logger = logging.getLogger(__name__)
Doug Zongker3c84f562014-07-31 11:06:30 -070077
Tao Bao32fcdab2018-10-12 10:30:39 -070078OPTIONS = common.OPTIONS
Michael Runge2e0d8fc2014-11-13 21:41:08 -080079OPTIONS.add_missing = False
80OPTIONS.rebuild_recovery = False
Tianjie Xu9ac4cb02017-06-09 16:58:03 -070081OPTIONS.replace_updated_files_list = []
Tianjie Xub48589a2016-08-03 19:21:52 -070082OPTIONS.is_signing = False
Doug Zongker3c84f562014-07-31 11:06:30 -070083
Bryan Henrye6d547d2018-07-31 18:32:00 -070084# Use a fixed timestamp (01/01/2009 00:00:00 UTC) for files when packaging
85# images. (b/24377993, b/80600931)
Tao Baoe30a6a62018-08-27 10:57:19 -070086FIXED_FILE_TIMESTAMP = int((
87 datetime.datetime(2009, 1, 1, 0, 0, 0, 0, None) -
88 datetime.datetime.utcfromtimestamp(0)).total_seconds())
Tao Baoa2ff4c92018-01-17 12:14:43 -080089
Bowgo Tsaid624fa62017-11-14 23:42:30 +080090
Kelvin Zhang1caead02022-09-23 10:06:03 -070091def ParseAvbFooter(img_path) -> avbtool.AvbFooter:
92 with open(img_path, 'rb') as fp:
93 fp.seek(-avbtool.AvbFooter.SIZE, os.SEEK_END)
94 data = fp.read(avbtool.AvbFooter.SIZE)
95 return avbtool.AvbFooter(data)
96
97
Kelvin Zhangf294c872022-10-06 14:21:36 -070098def GetCareMap(which, imgname):
99 """Returns the care_map string for the given partition.
100
101 Args:
102 which: The partition name, must be listed in PARTITIONS_WITH_CARE_MAP.
103 imgname: The filename of the image.
104
105 Returns:
106 (which, care_map_ranges): care_map_ranges is the raw string of the care_map
107 RangeSet; or None.
108 """
109 assert which in PARTITIONS_WITH_CARE_MAP
110
Kelvin Zhang1caead02022-09-23 10:06:03 -0700111 is_sparse_img = IsSparseImage(imgname)
112 unsparsed_image_size = os.path.getsize(imgname)
113
114 # A verified image contains original image + hash tree data + FEC data
115 # + AVB footer, all concatenated together. The caremap specifies a range
116 # of blocks that update_verifier should read on top of dm-verity device
117 # to verify correctness of OTA updates. When reading off of dm-verity device,
118 # the hashtree and FEC part of image isn't available. So caremap should
119 # only contain the original image blocks.
120 try:
121 avbfooter = None
122 if is_sparse_img:
123 with tempfile.NamedTemporaryFile() as tmpfile:
124 img = sparse_img.SparseImage(imgname)
125 unsparsed_image_size = img.total_blocks * img.blocksize
126 for data in img.ReadBlocks(img.total_blocks - 1, 1):
127 tmpfile.write(data)
128 tmpfile.flush()
129 avbfooter = ParseAvbFooter(tmpfile.name)
130 else:
131 avbfooter = ParseAvbFooter(imgname)
132 except LookupError as e:
133 logger.warning(
134 "Failed to parse avbfooter for partition %s image %s, %s", which, imgname, e)
Kelvin Zhangf294c872022-10-06 14:21:36 -0700135 return None
136
Kelvin Zhang1caead02022-09-23 10:06:03 -0700137 image_size = avbfooter.original_image_size
138 assert image_size < unsparsed_image_size, f"AVB footer's original image size {image_size} is larger than or equal to image size on disk {unsparsed_image_size}, this can't happen because a verified image = original image + hash tree data + FEC data + avbfooter."
139 assert image_size > 0
Kelvin Zhangf294c872022-10-06 14:21:36 -0700140
141 image_blocks = int(image_size) // 4096 - 1
142 # It's OK for image_blocks to be 0, because care map ranges are inclusive.
143 # So 0-0 means "just block 0", which is valid.
144 assert image_blocks >= 0, "blocks for {} must be non-negative, image size: {}".format(
145 which, image_size)
146
147 # For sparse images, we will only check the blocks that are listed in the care
148 # map, i.e. the ones with meaningful data.
Kelvin Zhang1caead02022-09-23 10:06:03 -0700149 if is_sparse_img:
Kelvin Zhangf294c872022-10-06 14:21:36 -0700150 simg = sparse_img.SparseImage(imgname)
151 care_map_ranges = simg.care_map.intersect(
152 rangelib.RangeSet("0-{}".format(image_blocks)))
153
154 # Otherwise for non-sparse images, we read all the blocks in the filesystem
155 # image.
156 else:
157 care_map_ranges = rangelib.RangeSet("0-{}".format(image_blocks))
158
159 return [which, care_map_ranges.to_string_raw()]
160
161
162def AddCareMapForAbOta(output_file, ab_partitions, image_paths):
163 """Generates and adds care_map.pb for a/b partition that has care_map.
164
165 Args:
166 output_file: The output zip file (needs to be already open),
167 or file path to write care_map.pb.
168 ab_partitions: The list of A/B partitions.
169 image_paths: A map from the partition name to the image path.
170 """
171 if not output_file:
172 raise ExternalError('Expected output_file for AddCareMapForAbOta')
173
174 care_map_list = []
175 for partition in ab_partitions:
176 partition = partition.strip()
177 if partition not in PARTITIONS_WITH_CARE_MAP:
178 continue
179
180 verity_block_device = "{}_verity_block_device".format(partition)
181 avb_hashtree_enable = "avb_{}_hashtree_enable".format(partition)
182 if (verity_block_device in OPTIONS.info_dict or
183 OPTIONS.info_dict.get(avb_hashtree_enable) == "true"):
184 if partition not in image_paths:
185 logger.warning('Potential partition with care_map missing from images: %s',
186 partition)
187 continue
188 image_path = image_paths[partition]
189 if not os.path.exists(image_path):
190 raise ExternalError('Expected image at path {}'.format(image_path))
191
192 care_map = GetCareMap(partition, image_path)
193 if not care_map:
194 continue
195 care_map_list += care_map
196
197 # adds fingerprint field to the care_map
198 # TODO(xunchang) revisit the fingerprint calculation for care_map.
199 partition_props = OPTIONS.info_dict.get(partition + ".build.prop")
200 prop_name_list = ["ro.{}.build.fingerprint".format(partition),
201 "ro.{}.build.thumbprint".format(partition)]
202
203 present_props = [x for x in prop_name_list if
204 partition_props and partition_props.GetProp(x)]
205 if not present_props:
206 logger.warning(
207 "fingerprint is not present for partition %s", partition)
208 property_id, fingerprint = "unknown", "unknown"
209 else:
210 property_id = present_props[0]
211 fingerprint = partition_props.GetProp(property_id)
212 care_map_list += [property_id, fingerprint]
213
214 if not care_map_list:
215 return
216
217 # Converts the list into proto buf message by calling care_map_generator; and
218 # writes the result to a temp file.
219 temp_care_map_text = MakeTempFile(prefix="caremap_text-",
220 suffix=".txt")
221 with open(temp_care_map_text, 'w') as text_file:
222 text_file.write('\n'.join(care_map_list))
223
224 temp_care_map = MakeTempFile(prefix="caremap-", suffix=".pb")
225 care_map_gen_cmd = ["care_map_generator", temp_care_map_text, temp_care_map]
226 RunAndCheckOutput(care_map_gen_cmd)
227
228 if not isinstance(output_file, zipfile.ZipFile):
229 shutil.copy(temp_care_map, output_file)
230 return
231 # output_file is a zip file
232 care_map_path = "META/care_map.pb"
233 if care_map_path in output_file.namelist():
234 # Copy the temp file into the OPTIONS.input_tmp dir and update the
235 # replace_updated_files_list used by add_img_to_target_files
236 if not OPTIONS.replace_updated_files_list:
237 OPTIONS.replace_updated_files_list = []
238 shutil.copy(temp_care_map, os.path.join(OPTIONS.input_tmp, care_map_path))
239 OPTIONS.replace_updated_files_list.append(care_map_path)
240 else:
241 ZipWrite(output_file, temp_care_map, arcname=care_map_path)
242
243
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800244class OutputFile(object):
Tao Bao93e7ebe2019-01-13 23:23:01 -0800245 """A helper class to write a generated file to the given dir or zip.
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800246
Tao Bao93e7ebe2019-01-13 23:23:01 -0800247 When generating images, we want the outputs to go into the given zip file, or
248 the given dir.
249
250 Attributes:
251 name: The name of the output file, regardless of the final destination.
252 """
253
Kelvin Zhang5f0fcee2021-01-19 15:30:46 -0500254 def __init__(self, output_zip, input_dir, *args):
Tao Bao93e7ebe2019-01-13 23:23:01 -0800255 # We write the intermediate output file under the given input_dir, even if
256 # the final destination is a zip archive.
Kelvin Zhang5f0fcee2021-01-19 15:30:46 -0500257 self.name = os.path.join(input_dir, *args)
Tao Bao93e7ebe2019-01-13 23:23:01 -0800258 self._output_zip = output_zip
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800259 if self._output_zip:
Kelvin Zhang5f0fcee2021-01-19 15:30:46 -0500260 self._zip_name = os.path.join(*args)
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800261
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800262 def Write(self, compress_type=None):
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800263 if self._output_zip:
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800264 common.ZipWrite(self._output_zip, self.name,
265 self._zip_name, compress_type=compress_type)
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800266
Tao Baoe30a6a62018-08-27 10:57:19 -0700267
Tao Bao886d8832018-02-27 11:46:19 -0800268def AddSystem(output_zip, recovery_img=None, boot_img=None):
Doug Zongker3c84f562014-07-31 11:06:30 -0700269 """Turn the contents of SYSTEM into a system image and store it in
David Zeuthend995f4b2016-01-29 16:59:17 -0500270 output_zip. Returns the name of the system image file."""
Michael Runge2e0d8fc2014-11-13 21:41:08 -0800271
Tao Bao886d8832018-02-27 11:46:19 -0800272 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "system.img")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800273 if os.path.exists(img.name):
Tao Bao32fcdab2018-10-12 10:30:39 -0700274 logger.info("system.img already exists; no need to rebuild...")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800275 return img.name
Michael Runge2e0d8fc2014-11-13 21:41:08 -0800276
277 def output_sink(fn, data):
Tao Baoa3705452019-06-24 15:33:41 -0700278 output_file = os.path.join(OPTIONS.input_tmp, "SYSTEM", fn)
279 with open(output_file, "wb") as ofile:
280 ofile.write(data)
Michael Runge2e0d8fc2014-11-13 21:41:08 -0800281
Daniel Normana4911da2019-03-15 14:36:21 -0700282 if output_zip:
283 arc_name = "SYSTEM/" + fn
284 if arc_name in output_zip.namelist():
285 OPTIONS.replace_updated_files_list.append(arc_name)
286 else:
Tao Baoa3705452019-06-24 15:33:41 -0700287 common.ZipWrite(output_zip, output_file, arc_name)
Tianjie Xu38af07f2017-05-25 17:38:53 -0700288
Bill Peckhame868aec2019-09-17 17:06:47 -0700289 board_uses_vendorimage = OPTIONS.info_dict.get(
290 "board_uses_vendorimage") == "true"
291
292 if (OPTIONS.rebuild_recovery and not board_uses_vendorimage and
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800293 recovery_img is not None and boot_img is not None):
Bill Peckhame868aec2019-09-17 17:06:47 -0700294 logger.info("Building new recovery patch on system at system/vendor")
Dan Albert8b72aef2015-03-23 19:13:21 -0700295 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink, recovery_img,
296 boot_img, info_dict=OPTIONS.info_dict)
Michael Runge2e0d8fc2014-11-13 21:41:08 -0800297
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800298 block_list = OutputFile(output_zip, OPTIONS.input_tmp,
299 "IMAGES", "system.map")
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800300 CreateImage(OPTIONS.input_tmp, OPTIONS.info_dict, "system", img,
301 block_list=block_list)
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800302 return img.name
Doug Zongkerfc44a512014-08-26 13:10:25 -0700303
304
Tao Bao886d8832018-02-27 11:46:19 -0800305def AddSystemOther(output_zip):
Alex Light4e358ab2016-06-16 14:47:10 -0700306 """Turn the contents of SYSTEM_OTHER into a system_other image
307 and store it in output_zip."""
308
Tao Bao886d8832018-02-27 11:46:19 -0800309 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "system_other.img")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800310 if os.path.exists(img.name):
Tao Bao32fcdab2018-10-12 10:30:39 -0700311 logger.info("system_other.img already exists; no need to rebuild...")
Alex Light4e358ab2016-06-16 14:47:10 -0700312 return
313
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800314 CreateImage(OPTIONS.input_tmp, OPTIONS.info_dict, "system_other", img)
Alex Light4e358ab2016-06-16 14:47:10 -0700315
316
Bill Peckhame868aec2019-09-17 17:06:47 -0700317def AddVendor(output_zip, recovery_img=None, boot_img=None):
Doug Zongkerfc44a512014-08-26 13:10:25 -0700318 """Turn the contents of VENDOR into a vendor image and store in it
319 output_zip."""
Michael Runge2e0d8fc2014-11-13 21:41:08 -0800320
Tao Bao886d8832018-02-27 11:46:19 -0800321 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "vendor.img")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800322 if os.path.exists(img.name):
Tao Bao32fcdab2018-10-12 10:30:39 -0700323 logger.info("vendor.img already exists; no need to rebuild...")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800324 return img.name
Michael Runge2e0d8fc2014-11-13 21:41:08 -0800325
Bill Peckhame868aec2019-09-17 17:06:47 -0700326 def output_sink(fn, data):
Iavor-Valentin Iftime756b5612022-02-09 15:56:14 +0000327 output_file = os.path.join(OPTIONS.input_tmp, "VENDOR", fn)
328 with open(output_file, "wb") as ofile:
329 ofile.write(data)
Bill Peckhame868aec2019-09-17 17:06:47 -0700330
331 if output_zip:
332 arc_name = "VENDOR/" + fn
333 if arc_name in output_zip.namelist():
334 OPTIONS.replace_updated_files_list.append(arc_name)
335 else:
Iavor-Valentin Iftime756b5612022-02-09 15:56:14 +0000336 common.ZipWrite(output_zip, output_file, arc_name)
Bill Peckhame868aec2019-09-17 17:06:47 -0700337
338 board_uses_vendorimage = OPTIONS.info_dict.get(
339 "board_uses_vendorimage") == "true"
340
341 if (OPTIONS.rebuild_recovery and board_uses_vendorimage and
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800342 recovery_img is not None and boot_img is not None):
Bill Peckhame868aec2019-09-17 17:06:47 -0700343 logger.info("Building new recovery patch on vendor")
344 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink, recovery_img,
345 boot_img, info_dict=OPTIONS.info_dict)
346
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800347 block_list = OutputFile(output_zip, OPTIONS.input_tmp,
348 "IMAGES", "vendor.map")
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800349 CreateImage(OPTIONS.input_tmp, OPTIONS.info_dict, "vendor", img,
350 block_list=block_list)
351 return img.name
Doug Zongker3c84f562014-07-31 11:06:30 -0700352
Yueyao Zhu889ee5e2017-05-12 17:50:46 -0700353
Tao Bao886d8832018-02-27 11:46:19 -0800354def AddProduct(output_zip):
355 """Turn the contents of PRODUCT into a product image and store it in
356 output_zip."""
Jaekyun Seokb7735d82017-11-27 17:04:47 +0900357
Tao Bao886d8832018-02-27 11:46:19 -0800358 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "product.img")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800359 if os.path.exists(img.name):
Tao Bao32fcdab2018-10-12 10:30:39 -0700360 logger.info("product.img already exists; no need to rebuild...")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800361 return img.name
Jaekyun Seokb7735d82017-11-27 17:04:47 +0900362
Tao Bao886d8832018-02-27 11:46:19 -0800363 block_list = OutputFile(
364 output_zip, OPTIONS.input_tmp, "IMAGES", "product.map")
365 CreateImage(
366 OPTIONS.input_tmp, OPTIONS.info_dict, "product", img,
367 block_list=block_list)
Jaekyun Seokb7735d82017-11-27 17:04:47 +0900368 return img.name
369
370
Justin Yun6151e3f2019-06-25 15:58:13 +0900371def AddSystemExt(output_zip):
372 """Turn the contents of SYSTEM_EXT into a system_ext image and store it in
373 output_zip."""
Dario Freni5f681e12018-05-29 13:09:01 +0100374
375 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES",
Justin Yun6151e3f2019-06-25 15:58:13 +0900376 "system_ext.img")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800377 if os.path.exists(img.name):
Justin Yun6151e3f2019-06-25 15:58:13 +0900378 logger.info("system_ext.img already exists; no need to rebuild...")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800379 return img.name
Dario Freni5f681e12018-05-29 13:09:01 +0100380
381 block_list = OutputFile(
Justin Yun6151e3f2019-06-25 15:58:13 +0900382 output_zip, OPTIONS.input_tmp, "IMAGES", "system_ext.map")
Dario Freni5f681e12018-05-29 13:09:01 +0100383 CreateImage(
Justin Yun6151e3f2019-06-25 15:58:13 +0900384 OPTIONS.input_tmp, OPTIONS.info_dict, "system_ext", img,
Dario Freni5f681e12018-05-29 13:09:01 +0100385 block_list=block_list)
386 return img.name
387
388
Bowgo Tsaid624fa62017-11-14 23:42:30 +0800389def AddOdm(output_zip):
390 """Turn the contents of ODM into an odm image and store it in output_zip."""
391
392 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "odm.img")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800393 if os.path.exists(img.name):
Tao Bao32fcdab2018-10-12 10:30:39 -0700394 logger.info("odm.img already exists; no need to rebuild...")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800395 return img.name
Bowgo Tsaid624fa62017-11-14 23:42:30 +0800396
397 block_list = OutputFile(
398 output_zip, OPTIONS.input_tmp, "IMAGES", "odm.map")
399 CreateImage(
400 OPTIONS.input_tmp, OPTIONS.info_dict, "odm", img,
401 block_list=block_list)
402 return img.name
403
404
Yifan Hongcfb917a2020-05-07 14:58:20 -0700405def AddVendorDlkm(output_zip):
406 """Turn the contents of VENDOR_DLKM into an vendor_dlkm image and store it in output_zip."""
407
408 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "vendor_dlkm.img")
409 if os.path.exists(img.name):
410 logger.info("vendor_dlkm.img already exists; no need to rebuild...")
411 return img.name
412
413 block_list = OutputFile(
414 output_zip, OPTIONS.input_tmp, "IMAGES", "vendor_dlkm.map")
415 CreateImage(
416 OPTIONS.input_tmp, OPTIONS.info_dict, "vendor_dlkm", img,
417 block_list=block_list)
418 return img.name
419
Tianjiebbde59f2021-05-03 21:18:56 -0700420
Yifan Hongf496f1b2020-07-15 16:52:59 -0700421def AddOdmDlkm(output_zip):
422 """Turn the contents of OdmDlkm into an odm_dlkm image and store it in output_zip."""
423
424 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "odm_dlkm.img")
425 if os.path.exists(img.name):
426 logger.info("odm_dlkm.img already exists; no need to rebuild...")
427 return img.name
428
429 block_list = OutputFile(
430 output_zip, OPTIONS.input_tmp, "IMAGES", "odm_dlkm.map")
431 CreateImage(
432 OPTIONS.input_tmp, OPTIONS.info_dict, "odm_dlkm", img,
433 block_list=block_list)
434 return img.name
435
Kelvin Zhangf294c872022-10-06 14:21:36 -0700436
Ramji Jiyani13a41372022-01-27 07:05:08 +0000437def AddSystemDlkm(output_zip):
438 """Turn the contents of SystemDlkm into an system_dlkm image and store it in output_zip."""
439
440 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "system_dlkm.img")
441 if os.path.exists(img.name):
442 logger.info("system_dlkm.img already exists; no need to rebuild...")
443 return img.name
444
445 block_list = OutputFile(
446 output_zip, OPTIONS.input_tmp, "IMAGES", "system_dlkm.map")
447 CreateImage(
448 OPTIONS.input_tmp, OPTIONS.info_dict, "system_dlkm", img,
449 block_list=block_list)
450 return img.name
451
Yifan Hongcfb917a2020-05-07 14:58:20 -0700452
Tao Bao886d8832018-02-27 11:46:19 -0800453def AddDtbo(output_zip):
Tao Baoc633ed02017-05-30 21:46:33 -0700454 """Adds the DTBO image.
455
Tao Bao886d8832018-02-27 11:46:19 -0800456 Uses the image under IMAGES/ if it already exists. Otherwise looks for the
Tao Baoc633ed02017-05-30 21:46:33 -0700457 image under PREBUILT_IMAGES/, signs it as needed, and returns the image name.
458 """
Tao Bao886d8832018-02-27 11:46:19 -0800459 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "dtbo.img")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800460 if os.path.exists(img.name):
Tao Bao32fcdab2018-10-12 10:30:39 -0700461 logger.info("dtbo.img already exists; no need to rebuild...")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800462 return img.name
Tao Baoc633ed02017-05-30 21:46:33 -0700463
464 dtbo_prebuilt_path = os.path.join(
465 OPTIONS.input_tmp, "PREBUILT_IMAGES", "dtbo.img")
466 assert os.path.exists(dtbo_prebuilt_path)
467 shutil.copy(dtbo_prebuilt_path, img.name)
468
469 # AVB-sign the image as needed.
Bowgo Tsai3e599ea2017-05-26 18:30:04 +0800470 if OPTIONS.info_dict.get("avb_enable") == "true":
Rupert Shuttleworth72942742020-12-08 06:18:35 +0000471 # Signing requires +w
472 os.chmod(img.name, os.stat(img.name).st_mode | stat.S_IWUSR)
473
Tao Baof88e0ce2019-03-18 14:01:38 -0700474 avbtool = OPTIONS.info_dict["avb_avbtool"]
Tao Bao3ebfdde2017-05-23 23:06:55 -0700475 part_size = OPTIONS.info_dict["dtbo_size"]
Tao Baoc633ed02017-05-30 21:46:33 -0700476 # The AVB hash footer will be replaced if already present.
477 cmd = [avbtool, "add_hash_footer", "--image", img.name,
478 "--partition_size", str(part_size), "--partition_name", "dtbo"]
Bowgo Tsai3e599ea2017-05-26 18:30:04 +0800479 common.AppendAVBSigningArgs(cmd, "dtbo")
480 args = OPTIONS.info_dict.get("avb_dtbo_add_hash_footer_args")
Tao Baoc633ed02017-05-30 21:46:33 -0700481 if args and args.strip():
482 cmd.extend(shlex.split(args))
Tao Bao2764aee2018-11-21 11:02:48 -0800483 common.RunAndCheckOutput(cmd)
Tao Baoc633ed02017-05-30 21:46:33 -0700484
485 img.Write()
486 return img.name
487
Tianjiebbde59f2021-05-03 21:18:56 -0700488
Andrew Sculle077cf72021-02-18 10:27:29 +0000489def AddPvmfw(output_zip):
490 """Adds the pvmfw image.
491
492 Uses the image under IMAGES/ if it already exists. Otherwise looks for the
493 image under PREBUILT_IMAGES/, signs it as needed, and returns the image name.
494 """
495 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "pvmfw.img")
496 if os.path.exists(img.name):
497 logger.info("pvmfw.img already exists; no need to rebuild...")
498 return img.name
499
500 pvmfw_prebuilt_path = os.path.join(
501 OPTIONS.input_tmp, "PREBUILT_IMAGES", "pvmfw.img")
502 assert os.path.exists(pvmfw_prebuilt_path)
503 shutil.copy(pvmfw_prebuilt_path, img.name)
504
505 # AVB-sign the image as needed.
506 if OPTIONS.info_dict.get("avb_enable") == "true":
507 # Signing requires +w
508 os.chmod(img.name, os.stat(img.name).st_mode | stat.S_IWUSR)
509
510 avbtool = OPTIONS.info_dict["avb_avbtool"]
511 part_size = OPTIONS.info_dict["pvmfw_size"]
512 # The AVB hash footer will be replaced if already present.
513 cmd = [avbtool, "add_hash_footer", "--image", img.name,
514 "--partition_size", str(part_size), "--partition_name", "pvmfw"]
515 common.AppendAVBSigningArgs(cmd, "pvmfw")
516 args = OPTIONS.info_dict.get("avb_pvmfw_add_hash_footer_args")
517 if args and args.strip():
518 cmd.extend(shlex.split(args))
519 common.RunAndCheckOutput(cmd)
520
521 img.Write()
522 return img.name
523
Tianjiebbde59f2021-05-03 21:18:56 -0700524
Hongguang Chenf23364d2020-04-27 18:36:36 -0700525def AddCustomImages(output_zip, partition_name):
526 """Adds and signs custom images in IMAGES/.
527
528 Args:
529 output_zip: The output zip file (needs to be already open), or None to
530 write images to OPTIONS.input_tmp/.
531
532 Uses the image under IMAGES/ if it already exists. Otherwise looks for the
533 image under PREBUILT_IMAGES/, signs it as needed, and returns the image name.
534
535 Raises:
536 AssertionError: If image can't be found.
537 """
538
Hongguang Chenf23364d2020-04-27 18:36:36 -0700539 key_path = OPTIONS.info_dict.get("avb_{}_key_path".format(partition_name))
540 algorithm = OPTIONS.info_dict.get("avb_{}_algorithm".format(partition_name))
541 extra_args = OPTIONS.info_dict.get(
542 "avb_{}_add_hashtree_footer_args".format(partition_name))
543 partition_size = OPTIONS.info_dict.get(
544 "avb_{}_partition_size".format(partition_name))
545
546 builder = verity_utils.CreateCustomImageBuilder(
547 OPTIONS.info_dict, partition_name, partition_size,
548 key_path, algorithm, extra_args)
549
550 for img_name in OPTIONS.info_dict.get(
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800551 "avb_{}_image_list".format(partition_name)).split():
552 custom_image = OutputFile(
553 output_zip, OPTIONS.input_tmp, "IMAGES", img_name)
Hongguang Chenf23364d2020-04-27 18:36:36 -0700554 if os.path.exists(custom_image.name):
555 continue
556
557 custom_image_prebuilt_path = os.path.join(
558 OPTIONS.input_tmp, "PREBUILT_IMAGES", img_name)
559 assert os.path.exists(custom_image_prebuilt_path), \
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800560 "Failed to find %s at %s" % (img_name, custom_image_prebuilt_path)
Hongguang Chenf23364d2020-04-27 18:36:36 -0700561
562 shutil.copy(custom_image_prebuilt_path, custom_image.name)
563
564 if builder is not None:
565 builder.Build(custom_image.name)
566
567 custom_image.Write()
568
569 default = os.path.join(OPTIONS.input_tmp, "IMAGES", partition_name + ".img")
570 assert os.path.exists(default), \
571 "There should be one %s.img" % (partition_name)
572 return default
573
Doug Zongker3c84f562014-07-31 11:06:30 -0700574
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800575def CreateImage(input_dir, info_dict, what, output_file, block_list=None):
Tao Baoa3705452019-06-24 15:33:41 -0700576 logger.info("creating %s.img...", what)
Doug Zongker3c84f562014-07-31 11:06:30 -0700577
Doug Zongker3c84f562014-07-31 11:06:30 -0700578 image_props = build_image.ImagePropFromGlobalDict(info_dict, what)
Bryan Henrye6d547d2018-07-31 18:32:00 -0700579 image_props["timestamp"] = FIXED_FILE_TIMESTAMP
Tao Bao822f5842015-09-30 16:01:14 -0700580
Doug Zongker3c84f562014-07-31 11:06:30 -0700581 if what == "system":
582 fs_config_prefix = ""
583 else:
584 fs_config_prefix = what + "_"
585
586 fs_config = os.path.join(
587 input_dir, "META/" + fs_config_prefix + "filesystem_config.txt")
Dan Albert8b72aef2015-03-23 19:13:21 -0700588 if not os.path.exists(fs_config):
589 fs_config = None
Doug Zongker3c84f562014-07-31 11:06:30 -0700590
Ying Wanga2292c92015-03-24 19:07:40 -0700591 # Override values loaded from info_dict.
592 if fs_config:
593 image_props["fs_config"] = fs_config
Ying Wanga2292c92015-03-24 19:07:40 -0700594 if block_list:
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800595 image_props["block_list"] = block_list.name
Ying Wanga2292c92015-03-24 19:07:40 -0700596
Tao Baod86e3112017-09-22 15:45:33 -0700597 # Use repeatable ext4 FS UUID and hash_seed UUID (based on partition name and
Tianjiefdda51d2021-05-05 14:46:35 -0700598 # build fingerprint). Also use the legacy build id, because the vbmeta digest
599 # isn't available at this point.
600 build_info = common.BuildInfo(info_dict, use_legacy_id=True)
Yifan Hongc08cbf02020-09-15 19:07:39 +0000601 uuid_seed = what + "-" + build_info.GetPartitionFingerprint(what)
Tao Baod86e3112017-09-22 15:45:33 -0700602 image_props["uuid"] = str(uuid.uuid5(uuid.NAMESPACE_URL, uuid_seed))
603 hash_seed = "hash_seed-" + uuid_seed
604 image_props["hash_seed"] = str(uuid.uuid5(uuid.NAMESPACE_URL, hash_seed))
605
Tao Baoc6bd70a2018-09-27 16:58:00 -0700606 build_image.BuildImage(
607 os.path.join(input_dir, what.upper()), image_props, output_file.name)
Doug Zongker3c84f562014-07-31 11:06:30 -0700608
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800609 output_file.Write()
610 if block_list:
611 block_list.Write()
612
Shashikant Baviskar16a73892019-02-07 10:57:21 +0900613 # Set the '_image_size' for given image size.
Tianjie Xuf1a13182017-01-19 17:39:30 -0800614 is_verity_partition = "verity_block_device" in image_props
hungweichen65ba3752022-08-17 10:14:01 +0000615 verity_supported = (image_props.get("avb_enable") == "true")
Tianjie Xu6b2e1552017-06-01 11:32:32 -0700616 is_avb_enable = image_props.get("avb_hashtree_enable") == "true"
617 if verity_supported and (is_verity_partition or is_avb_enable):
Tao Bao35f4ebc2018-09-27 15:31:11 -0700618 image_size = image_props.get("image_size")
619 if image_size:
Shashikant Baviskar16a73892019-02-07 10:57:21 +0900620 image_size_key = what + "_image_size"
621 info_dict[image_size_key] = int(image_size)
Tianjie Xuf1a13182017-01-19 17:39:30 -0800622
Yifan Hongc767f7c2018-11-08 15:41:24 -0800623 use_dynamic_size = (
Tao Bao2764aee2018-11-21 11:02:48 -0800624 info_dict.get("use_dynamic_partition_size") == "true" and
625 what in shlex.split(info_dict.get("dynamic_partition_list", "").strip()))
Yifan Hongc767f7c2018-11-08 15:41:24 -0800626 if use_dynamic_size:
627 info_dict.update(build_image.GlobalDictFromImageProp(image_props, what))
628
Doug Zongker3c84f562014-07-31 11:06:30 -0700629
Tao Bao886d8832018-02-27 11:46:19 -0800630def AddUserdata(output_zip):
Ying Wang2a048392015-06-25 13:56:53 -0700631 """Create a userdata image and store it in output_zip.
632
633 In most case we just create and store an empty userdata.img;
634 But the invoker can also request to create userdata.img with real
635 data from the target files, by setting "userdata_img_with_data=true"
636 in OPTIONS.info_dict.
637 """
Doug Zongker3c84f562014-07-31 11:06:30 -0700638
Tao Bao886d8832018-02-27 11:46:19 -0800639 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "userdata.img")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800640 if os.path.exists(img.name):
Tao Bao32fcdab2018-10-12 10:30:39 -0700641 logger.info("userdata.img already exists; no need to rebuild...")
Michael Runge2e0d8fc2014-11-13 21:41:08 -0800642 return
643
Elliott Hughes305b0882016-06-15 17:04:54 -0700644 # Skip userdata.img if no size.
Tao Bao2c15d9e2015-07-09 11:51:16 -0700645 image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, "data")
Elliott Hughes305b0882016-06-15 17:04:54 -0700646 if not image_props.get("partition_size"):
Doug Zongker3c84f562014-07-31 11:06:30 -0700647 return
648
Tao Bao32fcdab2018-10-12 10:30:39 -0700649 logger.info("creating userdata.img...")
Doug Zongker3c84f562014-07-31 11:06:30 -0700650
Bryan Henrye6d547d2018-07-31 18:32:00 -0700651 image_props["timestamp"] = FIXED_FILE_TIMESTAMP
Tao Bao822f5842015-09-30 16:01:14 -0700652
Tao Baofa863c82017-05-23 23:49:03 -0700653 if OPTIONS.info_dict.get("userdata_img_with_data") == "true":
654 user_dir = os.path.join(OPTIONS.input_tmp, "DATA")
Ying Wang2a048392015-06-25 13:56:53 -0700655 else:
Tao Bao1c830bf2017-12-25 10:43:47 -0800656 user_dir = common.MakeTempDir()
Ying Wang2a048392015-06-25 13:56:53 -0700657
Tao Baoc6bd70a2018-09-27 16:58:00 -0700658 build_image.BuildImage(user_dir, image_props, img.name)
Doug Zongker3c84f562014-07-31 11:06:30 -0700659
660 common.CheckSize(img.name, "userdata.img", OPTIONS.info_dict)
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800661 # Always use compression for useradata image.
662 # As it's likely huge and consist of lots of 0s.
663 img.Write(zipfile.ZIP_DEFLATED)
Doug Zongker3c84f562014-07-31 11:06:30 -0700664
665
Tao Bao744c4c72018-08-20 21:09:07 -0700666def AddVBMeta(output_zip, partitions, name, needed_partitions):
667 """Creates a VBMeta image and stores it in output_zip.
668
669 It generates the requested VBMeta image. The requested image could be for
670 top-level or chained VBMeta image, which is determined based on the name.
Tao Baobf70c312017-07-11 17:27:55 -0700671
672 Args:
673 output_zip: The output zip file, which needs to be already open.
674 partitions: A dict that's keyed by partition names with image paths as
Hongguang Chenf23364d2020-04-27 18:36:36 -0700675 values. Only valid partition names are accepted, as partitions listed
676 in common.AVB_PARTITIONS and custom partitions listed in
677 OPTIONS.info_dict.get("avb_custom_images_partition_list")
David Anderson7709ab22018-10-15 14:41:34 -0700678 name: Name of the VBMeta partition, e.g. 'vbmeta', 'vbmeta_system'.
Tao Bao744c4c72018-08-20 21:09:07 -0700679 needed_partitions: Partitions whose descriptors should be included into the
680 generated VBMeta image.
681
Tao Bao71064202018-10-22 15:08:02 -0700682 Returns:
683 Path to the created image.
684
Tao Bao744c4c72018-08-20 21:09:07 -0700685 Raises:
686 AssertionError: On invalid input args.
Tao Baobf70c312017-07-11 17:27:55 -0700687 """
Tao Bao744c4c72018-08-20 21:09:07 -0700688 assert needed_partitions, "Needed partitions must be specified"
689
690 img = OutputFile(
691 output_zip, OPTIONS.input_tmp, "IMAGES", "{}.img".format(name))
Tao Bao93e7ebe2019-01-13 23:23:01 -0800692 if os.path.exists(img.name):
Tao Bao32fcdab2018-10-12 10:30:39 -0700693 logger.info("%s.img already exists; not rebuilding...", name)
Tao Bao93e7ebe2019-01-13 23:23:01 -0800694 return img.name
Tao Bao262bf3f2017-07-11 17:27:55 -0700695
Daniel Norman276f0622019-07-26 14:13:51 -0700696 common.BuildVBMeta(img.name, partitions, name, needed_partitions)
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800697 img.Write()
Tao Bao71064202018-10-22 15:08:02 -0700698 return img.name
David Zeuthen2ce63ed2016-09-15 13:43:54 -0400699
700
Tao Bao886d8832018-02-27 11:46:19 -0800701def AddPartitionTable(output_zip):
David Zeuthen25328622016-04-08 15:08:03 -0400702 """Create a partition table image and store it in output_zip."""
703
Tao Bao886d8832018-02-27 11:46:19 -0800704 img = OutputFile(
705 output_zip, OPTIONS.input_tmp, "IMAGES", "partition-table.img")
706 bpt = OutputFile(
Bryan Henryf130a232018-04-26 11:59:33 -0700707 output_zip, OPTIONS.input_tmp, "META", "partition-table.bpt")
David Zeuthen25328622016-04-08 15:08:03 -0400708
709 # use BPTTOOL from environ, or "bpttool" if empty or not set.
710 bpttool = os.getenv("BPTTOOL") or "bpttool"
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800711 cmd = [bpttool, "make_table", "--output_json", bpt.name,
712 "--output_gpt", img.name]
David Zeuthen25328622016-04-08 15:08:03 -0400713 input_files_str = OPTIONS.info_dict["board_bpt_input_files"]
jiajia tange5ddfcd2022-06-21 10:36:12 +0800714 input_files = input_files_str.split()
David Zeuthen25328622016-04-08 15:08:03 -0400715 for i in input_files:
716 cmd.extend(["--input", i])
717 disk_size = OPTIONS.info_dict.get("board_bpt_disk_size")
718 if disk_size:
719 cmd.extend(["--disk_size", disk_size])
720 args = OPTIONS.info_dict.get("board_bpt_make_table_args")
721 if args:
722 cmd.extend(shlex.split(args))
Tao Bao2764aee2018-11-21 11:02:48 -0800723 common.RunAndCheckOutput(cmd)
David Zeuthen25328622016-04-08 15:08:03 -0400724
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800725 img.Write()
726 bpt.Write()
David Zeuthen25328622016-04-08 15:08:03 -0400727
728
Tao Bao886d8832018-02-27 11:46:19 -0800729def AddCache(output_zip):
Doug Zongker3c84f562014-07-31 11:06:30 -0700730 """Create an empty cache image and store it in output_zip."""
731
Tao Bao886d8832018-02-27 11:46:19 -0800732 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "cache.img")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800733 if os.path.exists(img.name):
Tao Bao32fcdab2018-10-12 10:30:39 -0700734 logger.info("cache.img already exists; no need to rebuild...")
Michael Runge2e0d8fc2014-11-13 21:41:08 -0800735 return
736
Tao Bao2c15d9e2015-07-09 11:51:16 -0700737 image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, "cache")
Doug Zongker3c84f562014-07-31 11:06:30 -0700738 # The build system has to explicitly request for cache.img.
739 if "fs_type" not in image_props:
740 return
741
Tao Bao32fcdab2018-10-12 10:30:39 -0700742 logger.info("creating cache.img...")
Doug Zongker3c84f562014-07-31 11:06:30 -0700743
Bryan Henrye6d547d2018-07-31 18:32:00 -0700744 image_props["timestamp"] = FIXED_FILE_TIMESTAMP
Tao Bao822f5842015-09-30 16:01:14 -0700745
Tao Bao1c830bf2017-12-25 10:43:47 -0800746 user_dir = common.MakeTempDir()
Tao Baoc6bd70a2018-09-27 16:58:00 -0700747 build_image.BuildImage(user_dir, image_props, img.name)
Doug Zongker3c84f562014-07-31 11:06:30 -0700748
749 common.CheckSize(img.name, "cache.img", OPTIONS.info_dict)
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800750 img.Write()
Doug Zongker3c84f562014-07-31 11:06:30 -0700751
752
Tao Bao5277d102018-04-17 23:47:21 -0700753def CheckAbOtaImages(output_zip, ab_partitions):
754 """Checks that all the listed A/B partitions have their images available.
Tao Baobea20ac2018-01-17 17:57:49 -0800755
Tao Bao5277d102018-04-17 23:47:21 -0700756 The images need to be available under IMAGES/ or RADIO/, with the former takes
757 a priority.
Tao Baobea20ac2018-01-17 17:57:49 -0800758
759 Args:
760 output_zip: The output zip file (needs to be already open), or None to
Tao Bao5277d102018-04-17 23:47:21 -0700761 find images in OPTIONS.input_tmp/.
Tao Baobea20ac2018-01-17 17:57:49 -0800762 ab_partitions: The list of A/B partitions.
763
764 Raises:
765 AssertionError: If it can't find an image.
766 """
767 for partition in ab_partitions:
Daniel Norman78554ea2021-09-14 10:29:38 -0700768 img_name = partition + ".img"
Tao Baobea20ac2018-01-17 17:57:49 -0800769
Tao Baoa2ff4c92018-01-17 12:14:43 -0800770 # Assert that the image is present under IMAGES/ now.
Tao Baobea20ac2018-01-17 17:57:49 -0800771 if output_zip:
772 # Zip spec says: All slashes MUST be forward slashes.
Tao Bao5277d102018-04-17 23:47:21 -0700773 images_path = "IMAGES/" + img_name
774 radio_path = "RADIO/" + img_name
775 available = (images_path in output_zip.namelist() or
776 radio_path in output_zip.namelist())
Tao Baobea20ac2018-01-17 17:57:49 -0800777 else:
Tao Bao5277d102018-04-17 23:47:21 -0700778 images_path = os.path.join(OPTIONS.input_tmp, "IMAGES", img_name)
779 radio_path = os.path.join(OPTIONS.input_tmp, "RADIO", img_name)
780 available = os.path.exists(images_path) or os.path.exists(radio_path)
781
782 assert available, "Failed to find " + img_name
Tao Baobea20ac2018-01-17 17:57:49 -0800783
784
Tao Baobea20ac2018-01-17 17:57:49 -0800785def AddPackRadioImages(output_zip, images):
786 """Copies images listed in META/pack_radioimages.txt from RADIO/ to IMAGES/.
787
788 Args:
789 output_zip: The output zip file (needs to be already open), or None to
790 write images to OPTIONS.input_tmp/.
791 images: A list of image names.
792
793 Raises:
794 AssertionError: If a listed image can't be found.
795 """
796 for image in images:
797 img_name = image.strip()
798 _, ext = os.path.splitext(img_name)
799 if not ext:
800 img_name += ".img"
Tao Baoa2ff4c92018-01-17 12:14:43 -0800801
Tao Baobea20ac2018-01-17 17:57:49 -0800802 prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", img_name)
803 if os.path.exists(prebuilt_path):
Tao Bao32fcdab2018-10-12 10:30:39 -0700804 logger.info("%s already exists, no need to overwrite...", img_name)
Tao Baobea20ac2018-01-17 17:57:49 -0800805 continue
806
807 img_radio_path = os.path.join(OPTIONS.input_tmp, "RADIO", img_name)
808 assert os.path.exists(img_radio_path), \
809 "Failed to find %s at %s" % (img_name, img_radio_path)
Tao Baoa2ff4c92018-01-17 12:14:43 -0800810
Tao Baobea20ac2018-01-17 17:57:49 -0800811 if output_zip:
Tao Baoa2ff4c92018-01-17 12:14:43 -0800812 common.ZipWrite(output_zip, img_radio_path, "IMAGES/" + img_name)
Tao Baobea20ac2018-01-17 17:57:49 -0800813 else:
814 shutil.copy(img_radio_path, prebuilt_path)
815
816
David Anderson1ef03e22018-08-30 13:11:47 -0700817def AddSuperEmpty(output_zip):
818 """Create a super_empty.img and store it in output_zip."""
819
820 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "super_empty.img")
Kelvin Zhangaecdc182023-04-17 09:52:39 -0700821 if os.path.exists(img.name):
822 logger.info("super_empty.img already exists; no need to rebuild...")
823 return
Yifan Hong055e6cf2018-11-29 13:51:48 -0800824 build_super_image.BuildSuperImage(OPTIONS.info_dict, img.name)
David Anderson1ef03e22018-08-30 13:11:47 -0700825 img.Write()
826
827
Yifan Hongc767f7c2018-11-08 15:41:24 -0800828def AddSuperSplit(output_zip):
829 """Create split super_*.img and store it in output_zip."""
830
Yifan Hong055e6cf2018-11-29 13:51:48 -0800831 outdir = os.path.join(OPTIONS.input_tmp, "OTA")
Yifan Honge98427a2018-12-07 10:08:27 -0800832 built = build_super_image.BuildSuperImage(OPTIONS.input_tmp, outdir)
Yifan Hongc767f7c2018-11-08 15:41:24 -0800833
Yifan Honge98427a2018-12-07 10:08:27 -0800834 if built:
835 for dev in OPTIONS.info_dict['super_block_devices'].strip().split():
836 img = OutputFile(output_zip, OPTIONS.input_tmp, "OTA",
837 "super_" + dev + ".img")
838 img.Write()
Yifan Hongc767f7c2018-11-08 15:41:24 -0800839
840
Tianjie Xu9ac4cb02017-06-09 16:58:03 -0700841def ReplaceUpdatedFiles(zip_filename, files_list):
Tao Bao89d7ab22017-12-14 17:05:33 -0800842 """Updates all the ZIP entries listed in files_list.
Tianjie Xu38af07f2017-05-25 17:38:53 -0700843
Tianjie Xu4c05f4a2018-09-14 16:24:41 -0700844 For now the list includes META/care_map.pb, and the related files under
Tianjie Xu9ac4cb02017-06-09 16:58:03 -0700845 SYSTEM/ after rebuilding recovery.
846 """
Tao Bao89d7ab22017-12-14 17:05:33 -0800847 common.ZipDelete(zip_filename, files_list)
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000848 output_zip = zipfile.ZipFile(zip_filename, "a",
Tianjie Xu38af07f2017-05-25 17:38:53 -0700849 compression=zipfile.ZIP_DEFLATED,
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000850 allowZip64=True)
851 for item in files_list:
852 file_path = os.path.join(OPTIONS.input_tmp, item)
853 assert os.path.exists(file_path)
854 common.ZipWrite(output_zip, file_path, arcname=item)
855 common.ZipClose(output_zip)
Tianjie Xu38af07f2017-05-25 17:38:53 -0700856
857
Chris Gross435b8fe2020-09-15 09:53:44 -0700858def HasPartition(partition_name):
859 """Determines if the target files archive should build a given partition."""
860
861 return ((os.path.isdir(
862 os.path.join(OPTIONS.input_tmp, partition_name.upper())) and
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800863 OPTIONS.info_dict.get(
864 "building_{}_image".format(partition_name)) == "true") or
865 os.path.exists(
866 os.path.join(OPTIONS.input_tmp, "IMAGES",
867 "{}.img".format(partition_name))))
Chris Gross435b8fe2020-09-15 09:53:44 -0700868
Tianjiea5fca032021-06-01 22:06:28 -0700869
Kelvin Zhang5f0fcee2021-01-19 15:30:46 -0500870def AddApexInfo(output_zip):
Tianjiea5fca032021-06-01 22:06:28 -0700871 apex_infos = GetApexInfoFromTargetFiles(OPTIONS.input_tmp, 'system',
872 compressed_only=False)
Kelvin Zhang5f0fcee2021-01-19 15:30:46 -0500873 apex_metadata_proto = ota_metadata_pb2.ApexMetadata()
874 apex_metadata_proto.apex_info.extend(apex_infos)
875 apex_info_bytes = apex_metadata_proto.SerializeToString()
876
877 output_file = os.path.join(OPTIONS.input_tmp, "META", "apex_info.pb")
878 with open(output_file, "wb") as ofile:
879 ofile.write(apex_info_bytes)
880 if output_zip:
881 arc_name = "META/apex_info.pb"
882 if arc_name in output_zip.namelist():
883 OPTIONS.replace_updated_files_list.append(arc_name)
884 else:
885 common.ZipWrite(output_zip, output_file, arc_name)
886
Chris Gross435b8fe2020-09-15 09:53:44 -0700887
Tianjiec3bf3d02021-07-14 15:56:37 -0700888def AddVbmetaDigest(output_zip):
889 """Write the vbmeta digest to the output dir and zipfile."""
890
891 # Calculate the vbmeta digest and put the result in to META/
892 boot_images = OPTIONS.info_dict.get("boot_images")
893 # Disable the digest calculation if the target_file is used as a container
Bowgo Tsaiaba5c9e2021-09-27 14:08:41 +0800894 # for boot images. A boot container might contain boot-5.4.img, boot-5.10.img
895 # etc., instead of just a boot.img and will fail in vbmeta digest calculation.
896 boot_container = boot_images and (
897 len(boot_images.split()) >= 2 or boot_images.split()[0] != 'boot.img')
Tianjiec3bf3d02021-07-14 15:56:37 -0700898 if (OPTIONS.info_dict.get("avb_enable") == "true" and not boot_container and
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800899 OPTIONS.info_dict.get("avb_building_vbmeta_image") == "true"):
Tianjiec3bf3d02021-07-14 15:56:37 -0700900 avbtool = OPTIONS.info_dict["avb_avbtool"]
901 digest = verity_utils.CalculateVbmetaDigest(OPTIONS.input_tmp, avbtool)
902 vbmeta_digest_txt = os.path.join(OPTIONS.input_tmp, "META",
903 "vbmeta_digest.txt")
904 with open(vbmeta_digest_txt, 'w') as f:
905 f.write(digest)
906 # writes to the output zipfile
907 if output_zip:
908 arc_name = "META/vbmeta_digest.txt"
909 if arc_name in output_zip.namelist():
910 OPTIONS.replace_updated_files_list.append(arc_name)
911 else:
912 common.ZipWriteStr(output_zip, arc_name, digest)
913
914
Doug Zongker3c84f562014-07-31 11:06:30 -0700915def AddImagesToTargetFiles(filename):
Tao Baoae396d92017-11-20 11:56:43 -0800916 """Creates and adds images (boot/recovery/system/...) to a target_files.zip.
917
918 It works with either a zip file (zip mode), or a directory that contains the
919 files to be packed into a target_files.zip (dir mode). The latter is used when
920 being called from build/make/core/Makefile.
921
922 The images will be created under IMAGES/ in the input target_files.zip.
923
924 Args:
Tao Baodba59ee2018-01-09 13:21:02 -0800925 filename: the target_files.zip, or the zip root directory.
Tao Baoae396d92017-11-20 11:56:43 -0800926 """
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800927 if os.path.isdir(filename):
928 OPTIONS.input_tmp = os.path.abspath(filename)
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800929 else:
Tao Baodba59ee2018-01-09 13:21:02 -0800930 OPTIONS.input_tmp = common.UnzipTemp(filename)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700931
Michael Runge2e0d8fc2014-11-13 21:41:08 -0800932 if not OPTIONS.add_missing:
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800933 if os.path.isdir(os.path.join(OPTIONS.input_tmp, "IMAGES")):
Tao Bao32fcdab2018-10-12 10:30:39 -0700934 logger.warning("target_files appears to already contain images.")
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800935 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700936
Tao Bao410ad8b2018-08-24 12:08:38 -0700937 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.input_tmp, repacking=True)
Tao Baodba59ee2018-01-09 13:21:02 -0800938
939 has_recovery = OPTIONS.info_dict.get("no_recovery") != "true"
Chris Grossa784ef12019-04-22 11:09:57 -0700940 has_boot = OPTIONS.info_dict.get("no_boot") != "true"
Devin Mooreafdd7c72021-12-13 22:04:08 +0000941 has_init_boot = OPTIONS.info_dict.get("init_boot") == "true"
Steve Mucklee1b10862019-07-10 10:49:37 -0700942 has_vendor_boot = OPTIONS.info_dict.get("vendor_boot") == "true"
Kelvin Zhangf294c872022-10-06 14:21:36 -0700943 has_vendor_kernel_boot = OPTIONS.info_dict.get(
944 "vendor_kernel_boot") == "true"
Tao Baodba59ee2018-01-09 13:21:02 -0800945
Ramji Jiyani13a41372022-01-27 07:05:08 +0000946 # {vendor,odm,product,system_ext,vendor_dlkm,odm_dlkm, system_dlkm, system, system_other}.img
Chris Gross435b8fe2020-09-15 09:53:44 -0700947 # can be built from source, or dropped into target_files.zip as a prebuilt blob.
948 has_vendor = HasPartition("vendor")
949 has_odm = HasPartition("odm")
950 has_vendor_dlkm = HasPartition("vendor_dlkm")
951 has_odm_dlkm = HasPartition("odm_dlkm")
Ramji Jiyani13a41372022-01-27 07:05:08 +0000952 has_system_dlkm = HasPartition("system_dlkm")
Chris Gross435b8fe2020-09-15 09:53:44 -0700953 has_product = HasPartition("product")
954 has_system_ext = HasPartition("system_ext")
955 has_system = HasPartition("system")
956 has_system_other = HasPartition("system_other")
Chris Gross203191b2020-05-30 02:39:12 +0000957 has_userdata = OPTIONS.info_dict.get("building_userdata_image") == "true"
958 has_cache = OPTIONS.info_dict.get("building_cache_image") == "true"
Doug Zongker3c84f562014-07-31 11:06:30 -0700959
Tao Baodba59ee2018-01-09 13:21:02 -0800960 # Set up the output destination. It writes to the given directory for dir
961 # mode; otherwise appends to the given ZIP.
962 if os.path.isdir(filename):
963 output_zip = None
964 else:
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800965 output_zip = zipfile.ZipFile(filename, "a",
966 compression=zipfile.ZIP_DEFLATED,
967 allowZip64=True)
Tao Baoae396d92017-11-20 11:56:43 -0800968
969 # Always make input_tmp/IMAGES available, since we may stage boot / recovery
970 # images there even under zip mode. The directory will be cleaned up as part
971 # of OPTIONS.input_tmp.
972 images_dir = os.path.join(OPTIONS.input_tmp, "IMAGES")
973 if not os.path.isdir(images_dir):
974 os.makedirs(images_dir)
Doug Zongker3c84f562014-07-31 11:06:30 -0700975
Tao Baobf70c312017-07-11 17:27:55 -0700976 # A map between partition names and their paths, which could be used when
977 # generating AVB vbmeta image.
Tao Bao3ed35d32019-10-07 20:48:48 -0700978 partitions = {}
Tao Baobf70c312017-07-11 17:27:55 -0700979
Doug Zongkerfc44a512014-08-26 13:10:25 -0700980 def banner(s):
Tao Baoa3705452019-06-24 15:33:41 -0700981 logger.info("\n\n++++ %s ++++\n\n", s)
Doug Zongker3c84f562014-07-31 11:06:30 -0700982
Chris Grossa784ef12019-04-22 11:09:57 -0700983 boot_image = None
Greg Kaisere086f722021-09-14 19:32:27 +0000984 if has_boot:
Chris Grossa784ef12019-04-22 11:09:57 -0700985 banner("boot")
Steve Muckle9793cf62020-04-08 18:27:00 -0700986 boot_images = OPTIONS.info_dict.get("boot_images")
987 if boot_images is None:
988 boot_images = "boot.img"
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800989 for index, b in enumerate(boot_images.split()):
Steve Muckle9793cf62020-04-08 18:27:00 -0700990 # common.GetBootableImage() returns the image directly if present.
991 boot_image = common.GetBootableImage(
992 "IMAGES/" + b, b, OPTIONS.input_tmp, "BOOT")
993 # boot.img may be unavailable in some targets (e.g. aosp_arm64).
994 if boot_image:
995 boot_image_path = os.path.join(OPTIONS.input_tmp, "IMAGES", b)
Roopesh Nataraja3e15f6e2020-06-08 19:54:13 -0700996 # Although multiple boot images can be generated, include the image
997 # descriptor of only the first boot image in vbmeta
998 if index == 0:
Steve Muckle9793cf62020-04-08 18:27:00 -0700999 partitions['boot'] = boot_image_path
1000 if not os.path.exists(boot_image_path):
1001 boot_image.WriteToDir(OPTIONS.input_tmp)
1002 if output_zip:
1003 boot_image.AddToZip(output_zip)
Doug Zongker3c84f562014-07-31 11:06:30 -07001004
Devin Mooreafdd7c72021-12-13 22:04:08 +00001005 if has_init_boot:
1006 banner("init_boot")
1007 init_boot_image = common.GetBootableImage(
Vincent Donnefort6e861e92023-02-17 10:12:57 +00001008 "IMAGES/init_boot.img", "init_boot.img", OPTIONS.input_tmp, "INIT_BOOT",
1009 dev_nodes=True)
Devin Mooreafdd7c72021-12-13 22:04:08 +00001010 if init_boot_image:
Kelvin Zhang834f5d42022-01-21 12:44:44 -08001011 partitions['init_boot'] = os.path.join(
1012 OPTIONS.input_tmp, "IMAGES", "init_boot.img")
Devin Mooreafdd7c72021-12-13 22:04:08 +00001013 if not os.path.exists(partitions['init_boot']):
1014 init_boot_image.WriteToDir(OPTIONS.input_tmp)
1015 if output_zip:
1016 init_boot_image.AddToZip(output_zip)
1017
Greg Kaisere086f722021-09-14 19:32:27 +00001018 if has_vendor_boot:
Steve Mucklee1b10862019-07-10 10:49:37 -07001019 banner("vendor_boot")
1020 vendor_boot_image = common.GetVendorBootImage(
1021 "IMAGES/vendor_boot.img", "vendor_boot.img", OPTIONS.input_tmp,
1022 "VENDOR_BOOT")
1023 if vendor_boot_image:
1024 partitions['vendor_boot'] = os.path.join(OPTIONS.input_tmp, "IMAGES",
1025 "vendor_boot.img")
1026 if not os.path.exists(partitions['vendor_boot']):
1027 vendor_boot_image.WriteToDir(OPTIONS.input_tmp)
1028 if output_zip:
1029 vendor_boot_image.AddToZip(output_zip)
1030
Lucas Weif57333f2022-02-24 10:30:15 +08001031 if has_vendor_kernel_boot:
1032 banner("vendor_kernel_boot")
Lucas Wei03230252022-04-18 16:00:40 +08001033 vendor_kernel_boot_image = common.GetVendorKernelBootImage(
Lucas Weif57333f2022-02-24 10:30:15 +08001034 "IMAGES/vendor_kernel_boot.img", "vendor_kernel_boot.img", OPTIONS.input_tmp,
1035 "VENDOR_KERNEL_BOOT")
1036 if vendor_kernel_boot_image:
1037 partitions['vendor_kernel_boot'] = os.path.join(OPTIONS.input_tmp, "IMAGES",
Kelvin Zhangf294c872022-10-06 14:21:36 -07001038 "vendor_kernel_boot.img")
Lucas Weif57333f2022-02-24 10:30:15 +08001039 if not os.path.exists(partitions['vendor_kernel_boot']):
1040 vendor_kernel_boot_image.WriteToDir(OPTIONS.input_tmp)
1041 if output_zip:
1042 vendor_kernel_boot_image.AddToZip(output_zip)
1043
Michael Runge2e0d8fc2014-11-13 21:41:08 -08001044 recovery_image = None
Greg Kaisere086f722021-09-14 19:32:27 +00001045 if has_recovery:
Tao Baodb45efa2015-10-27 19:25:18 -07001046 banner("recovery")
Tao Bao262bf3f2017-07-11 17:27:55 -07001047 recovery_image = common.GetBootableImage(
1048 "IMAGES/recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
1049 assert recovery_image, "Failed to create recovery.img."
Tao Baobf70c312017-07-11 17:27:55 -07001050 partitions['recovery'] = os.path.join(
Tao Bao262bf3f2017-07-11 17:27:55 -07001051 OPTIONS.input_tmp, "IMAGES", "recovery.img")
Tao Baobf70c312017-07-11 17:27:55 -07001052 if not os.path.exists(partitions['recovery']):
Tao Bao262bf3f2017-07-11 17:27:55 -07001053 recovery_image.WriteToDir(OPTIONS.input_tmp)
1054 if output_zip:
1055 recovery_image.AddToZip(output_zip)
Doug Zongker3c84f562014-07-31 11:06:30 -07001056
Tao Baod42e97e2016-11-30 12:11:57 -08001057 banner("recovery (two-step image)")
1058 # The special recovery.img for two-step package use.
1059 recovery_two_step_image = common.GetBootableImage(
Tao Bao04808502019-07-25 23:11:41 -07001060 "OTA/recovery-two-step.img", "recovery-two-step.img",
Tao Baod42e97e2016-11-30 12:11:57 -08001061 OPTIONS.input_tmp, "RECOVERY", two_step_image=True)
Tao Bao262bf3f2017-07-11 17:27:55 -07001062 assert recovery_two_step_image, "Failed to create recovery-two-step.img."
1063 recovery_two_step_image_path = os.path.join(
Tao Bao04808502019-07-25 23:11:41 -07001064 OPTIONS.input_tmp, "OTA", "recovery-two-step.img")
Tao Bao262bf3f2017-07-11 17:27:55 -07001065 if not os.path.exists(recovery_two_step_image_path):
1066 recovery_two_step_image.WriteToDir(OPTIONS.input_tmp)
Dan Willemsen2ee00d52017-03-05 19:51:56 -08001067 if output_zip:
1068 recovery_two_step_image.AddToZip(output_zip)
Tao Baod42e97e2016-11-30 12:11:57 -08001069
Daniel Norman78554ea2021-09-14 10:29:38 -07001070 def add_partition(partition, has_partition, add_func, add_args):
1071 if has_partition:
1072 banner(partition)
1073 partitions[partition] = add_func(output_zip, *add_args)
Tao Baobf70c312017-07-11 17:27:55 -07001074
Daniel Norman78554ea2021-09-14 10:29:38 -07001075 add_partition_calls = (
1076 ("system", has_system, AddSystem, [recovery_image, boot_image]),
1077 ("vendor", has_vendor, AddVendor, [recovery_image, boot_image]),
1078 ("product", has_product, AddProduct, []),
1079 ("system_ext", has_system_ext, AddSystemExt, []),
1080 ("odm", has_odm, AddOdm, []),
1081 ("vendor_dlkm", has_vendor_dlkm, AddVendorDlkm, []),
1082 ("odm_dlkm", has_odm_dlkm, AddOdmDlkm, []),
Ramji Jiyani13a41372022-01-27 07:05:08 +00001083 ("system_dlkm", has_system_dlkm, AddSystemDlkm, []),
Daniel Norman78554ea2021-09-14 10:29:38 -07001084 ("system_other", has_system_other, AddSystemOther, []),
1085 )
Jeongik Cha03d014a2023-05-11 17:25:51 +09001086
1087 with ThreadPoolExecutor(max_workers=len(add_partition_calls)) as executor:
1088 for future in [executor.submit(add_partition, *call) for call in add_partition_calls]:
1089 future.result()
Tao Baobf70c312017-07-11 17:27:55 -07001090
Kelvin Zhang5f0fcee2021-01-19 15:30:46 -05001091 AddApexInfo(output_zip)
1092
Tianjie Xub48589a2016-08-03 19:21:52 -07001093 if not OPTIONS.is_signing:
Greg Kaisere086f722021-09-14 19:32:27 +00001094 banner("userdata")
1095 AddUserdata(output_zip)
1096 banner("cache")
1097 AddCache(output_zip)
Tao Baoc633ed02017-05-30 21:46:33 -07001098
1099 if OPTIONS.info_dict.get("board_bpt_enable") == "true":
David Zeuthen25328622016-04-08 15:08:03 -04001100 banner("partition-table")
1101 AddPartitionTable(output_zip)
Tao Baoc633ed02017-05-30 21:46:33 -07001102
Daniel Norman78554ea2021-09-14 10:29:38 -07001103 add_partition("dtbo",
1104 OPTIONS.info_dict.get("has_dtbo") == "true", AddDtbo, [])
1105 add_partition("pvmfw",
1106 OPTIONS.info_dict.get("has_pvmfw") == "true", AddPvmfw, [])
Andrew Sculle077cf72021-02-18 10:27:29 +00001107
Hongguang Chenf23364d2020-04-27 18:36:36 -07001108 # Custom images.
1109 custom_partitions = OPTIONS.info_dict.get(
1110 "avb_custom_images_partition_list", "").strip().split()
1111 for partition_name in custom_partitions:
1112 partition_name = partition_name.strip()
1113 banner("custom images for " + partition_name)
1114 partitions[partition_name] = AddCustomImages(output_zip, partition_name)
1115
Bowgo Tsai3e599ea2017-05-26 18:30:04 +08001116 if OPTIONS.info_dict.get("avb_enable") == "true":
Tao Bao744c4c72018-08-20 21:09:07 -07001117 # vbmeta_partitions includes the partitions that should be included into
1118 # top-level vbmeta.img, which are the ones that are not included in any
1119 # chained VBMeta image plus the chained VBMeta images themselves.
Hongguang Chenf23364d2020-04-27 18:36:36 -07001120 # Currently custom_partitions are all chained to VBMeta image.
1121 vbmeta_partitions = common.AVB_PARTITIONS[:] + tuple(custom_partitions)
Tao Bao744c4c72018-08-20 21:09:07 -07001122
David Anderson7709ab22018-10-15 14:41:34 -07001123 vbmeta_system = OPTIONS.info_dict.get("avb_vbmeta_system", "").strip()
Greg Kaisere086f722021-09-14 19:32:27 +00001124 if vbmeta_system:
David Anderson7709ab22018-10-15 14:41:34 -07001125 banner("vbmeta_system")
Tao Bao71064202018-10-22 15:08:02 -07001126 partitions["vbmeta_system"] = AddVBMeta(
David Anderson7709ab22018-10-15 14:41:34 -07001127 output_zip, partitions, "vbmeta_system", vbmeta_system.split())
Tao Bao744c4c72018-08-20 21:09:07 -07001128 vbmeta_partitions = [
1129 item for item in vbmeta_partitions
David Anderson7709ab22018-10-15 14:41:34 -07001130 if item not in vbmeta_system.split()]
1131 vbmeta_partitions.append("vbmeta_system")
Tao Bao744c4c72018-08-20 21:09:07 -07001132
1133 vbmeta_vendor = OPTIONS.info_dict.get("avb_vbmeta_vendor", "").strip()
Greg Kaisere086f722021-09-14 19:32:27 +00001134 if vbmeta_vendor:
Tao Bao744c4c72018-08-20 21:09:07 -07001135 banner("vbmeta_vendor")
Tao Bao71064202018-10-22 15:08:02 -07001136 partitions["vbmeta_vendor"] = AddVBMeta(
Tao Bao744c4c72018-08-20 21:09:07 -07001137 output_zip, partitions, "vbmeta_vendor", vbmeta_vendor.split())
1138 vbmeta_partitions = [
1139 item for item in vbmeta_partitions
1140 if item not in vbmeta_vendor.split()]
1141 vbmeta_partitions.append("vbmeta_vendor")
Kelvin Zhangb81b4e32023-01-10 10:37:56 -08001142 custom_avb_partitions = OPTIONS.info_dict.get("avb_custom_vbmeta_images_partition_list", "").strip().split()
1143 if custom_avb_partitions:
1144 for avb_part in custom_avb_partitions:
1145 partition_name = "vbmeta_" + avb_part
1146 included_partitions = OPTIONS.info_dict.get("avb_vbmeta_{}".format(avb_part), "").strip().split()
1147 assert included_partitions, "Custom vbmeta partition {0} missing avb_vbmeta_{0} prop".format(avb_part)
1148 banner(partition_name)
1149 logger.info("VBMeta partition {} needs {}".format(partition_name, included_partitions))
1150 partitions[partition_name] = AddVBMeta(
1151 output_zip, partitions, partition_name, included_partitions)
1152 vbmeta_partitions = [
1153 item for item in vbmeta_partitions
1154 if item not in included_partitions]
1155 vbmeta_partitions.append(partition_name)
1156
Tao Bao744c4c72018-08-20 21:09:07 -07001157
Greg Kaisere086f722021-09-14 19:32:27 +00001158 if OPTIONS.info_dict.get("avb_building_vbmeta_image") == "true":
Bowgo Tsai82182252020-11-13 11:28:17 +08001159 banner("vbmeta")
1160 AddVBMeta(output_zip, partitions, "vbmeta", vbmeta_partitions)
Doug Zongker3c84f562014-07-31 11:06:30 -07001161
Tao Bao48a2feb2019-06-28 11:00:05 -07001162 if OPTIONS.info_dict.get("use_dynamic_partitions") == "true":
Greg Kaisere086f722021-09-14 19:32:27 +00001163 if OPTIONS.info_dict.get("build_super_empty_partition") == "true":
Yo Chiange86bab42021-03-25 10:12:28 +00001164 banner("super_empty")
1165 AddSuperEmpty(output_zip)
David Anderson1ef03e22018-08-30 13:11:47 -07001166
Greg Kaisere086f722021-09-14 19:32:27 +00001167 if OPTIONS.info_dict.get("build_super_partition") == "true":
Tao Bao519d1822018-12-27 12:47:23 -08001168 if OPTIONS.info_dict.get(
Kelvin Zhang834f5d42022-01-21 12:44:44 -08001169 "build_retrofit_dynamic_partitions_ota_package") == "true":
Yifan Hongc767f7c2018-11-08 15:41:24 -08001170 banner("super split images")
1171 AddSuperSplit(output_zip)
Yifan Hongc767f7c2018-11-08 15:41:24 -08001172
Tianjie Xuaaca4212016-06-28 14:34:03 -07001173 banner("radio")
Tao Baobea20ac2018-01-17 17:57:49 -08001174 ab_partitions_txt = os.path.join(OPTIONS.input_tmp, "META",
1175 "ab_partitions.txt")
1176 if os.path.exists(ab_partitions_txt):
Tao Baoa3705452019-06-24 15:33:41 -07001177 with open(ab_partitions_txt) as f:
Daniel Norman78554ea2021-09-14 10:29:38 -07001178 ab_partitions = f.read().splitlines()
Tianjie Xucfa86222016-03-07 16:31:19 -08001179
Greg Kaisere086f722021-09-14 19:32:27 +00001180 # For devices using A/B update, make sure we have all the needed images
1181 # ready under IMAGES/ or RADIO/.
1182 CheckAbOtaImages(output_zip, ab_partitions)
Tianjie Xuaaca4212016-06-28 14:34:03 -07001183
Greg Kaisere086f722021-09-14 19:32:27 +00001184 # Generate care_map.pb for ab_partitions, then write this file to
1185 # target_files package.
1186 output_care_map = os.path.join(OPTIONS.input_tmp, "META", "care_map.pb")
1187 AddCareMapForAbOta(output_zip if output_zip else output_care_map,
1188 ab_partitions, partitions)
Tianjie Xucfa86222016-03-07 16:31:19 -08001189
Tao Bao95a95c32017-06-16 15:30:23 -07001190 # Radio images that need to be packed into IMAGES/, and product-img.zip.
Tao Baobea20ac2018-01-17 17:57:49 -08001191 pack_radioimages_txt = os.path.join(
Tao Bao95a95c32017-06-16 15:30:23 -07001192 OPTIONS.input_tmp, "META", "pack_radioimages.txt")
Tao Baobea20ac2018-01-17 17:57:49 -08001193 if os.path.exists(pack_radioimages_txt):
Tao Baoa3705452019-06-24 15:33:41 -07001194 with open(pack_radioimages_txt) as f:
Tao Baobea20ac2018-01-17 17:57:49 -08001195 AddPackRadioImages(output_zip, f.readlines())
Tao Bao95a95c32017-06-16 15:30:23 -07001196
Greg Kaisere086f722021-09-14 19:32:27 +00001197 AddVbmetaDigest(output_zip)
Tianjiebbde59f2021-05-03 21:18:56 -07001198
Dan Willemsen2ee00d52017-03-05 19:51:56 -08001199 if output_zip:
Kelvin Zhangf92f7f02023-04-14 21:32:54 +00001200 common.ZipClose(output_zip)
Tianjie Xu9ac4cb02017-06-09 16:58:03 -07001201 if OPTIONS.replace_updated_files_list:
1202 ReplaceUpdatedFiles(output_zip.filename,
1203 OPTIONS.replace_updated_files_list)
Tianjie Xu38af07f2017-05-25 17:38:53 -07001204
Doug Zongker3c84f562014-07-31 11:06:30 -07001205
Kelvin Zhang834f5d42022-01-21 12:44:44 -08001206def OptimizeCompressedEntries(zipfile_path):
1207 """Convert files that do not compress well to uncompressed storage
1208
1209 EROFS images tend to be compressed already, so compressing them again
1210 yields little space savings. Leaving them uncompressed will make
1211 downstream tooling's job easier, and save compute time.
1212 """
1213 if not zipfile.is_zipfile(zipfile_path):
1214 return
1215 entries_to_store = []
1216 with tempfile.TemporaryDirectory() as tmpdir:
1217 with zipfile.ZipFile(zipfile_path, "r", allowZip64=True) as zfp:
1218 for zinfo in zfp.filelist:
1219 if not zinfo.filename.startswith("IMAGES/") and not zinfo.filename.startswith("META"):
Kelvin Zhang9d021e92022-02-07 16:37:15 -08001220 continue
Kelvin Zhang834f5d42022-01-21 12:44:44 -08001221 # Don't try to store userdata.img uncompressed, it's usually huge.
1222 if zinfo.filename.endswith("userdata.img"):
Kelvin Zhang9d021e92022-02-07 16:37:15 -08001223 continue
Kelvin Zhang834f5d42022-01-21 12:44:44 -08001224 if zinfo.compress_size > zinfo.file_size * 0.80 and zinfo.compress_type != zipfile.ZIP_STORED:
1225 entries_to_store.append(zinfo)
1226 zfp.extract(zinfo, tmpdir)
Kelvin Zhang70876142022-02-09 16:05:29 -08001227 if len(entries_to_store) == 0:
1228 return
Kelvin Zhang834f5d42022-01-21 12:44:44 -08001229 # Remove these entries, then re-add them as ZIP_STORED
Kelvin Zhang70876142022-02-09 16:05:29 -08001230 ZipDelete(zipfile_path, [entry.filename for entry in entries_to_store])
Kelvin Zhang834f5d42022-01-21 12:44:44 -08001231 with zipfile.ZipFile(zipfile_path, "a", allowZip64=True) as zfp:
1232 for entry in entries_to_store:
Kelvin Zhangf294c872022-10-06 14:21:36 -07001233 zfp.write(os.path.join(tmpdir, entry.filename),
1234 entry.filename, compress_type=zipfile.ZIP_STORED)
Kelvin Zhang834f5d42022-01-21 12:44:44 -08001235
1236
Doug Zongker3c84f562014-07-31 11:06:30 -07001237def main(argv):
Baligh Uddin59f4ff12015-09-16 21:20:30 -07001238 def option_handler(o, a):
Michael Runge2e0d8fc2014-11-13 21:41:08 -08001239 if o in ("-a", "--add_missing"):
1240 OPTIONS.add_missing = True
1241 elif o in ("-r", "--rebuild_recovery",):
1242 OPTIONS.rebuild_recovery = True
Baligh Uddin59f4ff12015-09-16 21:20:30 -07001243 elif o == "--replace_verity_private_key":
hungweichendd3fca02022-08-19 06:33:25 +00001244 raise ValueError("--replace_verity_private_key is no longer supported,"
1245 " please switch to AVB")
Baligh Uddin59f4ff12015-09-16 21:20:30 -07001246 elif o == "--replace_verity_public_key":
hungweichendd3fca02022-08-19 06:33:25 +00001247 raise ValueError("--replace_verity_public_key is no longer supported,"
1248 " please switch to AVB")
Tianjie Xub48589a2016-08-03 19:21:52 -07001249 elif o == "--is_signing":
1250 OPTIONS.is_signing = True
Michael Runge2e0d8fc2014-11-13 21:41:08 -08001251 else:
1252 return False
1253 return True
1254
Dan Albert8b72aef2015-03-23 19:13:21 -07001255 args = common.ParseOptions(
1256 argv, __doc__, extra_opts="ar",
Baligh Uddin59f4ff12015-09-16 21:20:30 -07001257 extra_long_opts=["add_missing", "rebuild_recovery",
1258 "replace_verity_public_key=",
1259 "replace_verity_private_key=",
Greg Kaisere086f722021-09-14 19:32:27 +00001260 "is_signing"],
Dan Albert8b72aef2015-03-23 19:13:21 -07001261 extra_option_handler=option_handler)
Michael Runge2e0d8fc2014-11-13 21:41:08 -08001262
Doug Zongker3c84f562014-07-31 11:06:30 -07001263 if len(args) != 1:
1264 common.Usage(__doc__)
1265 sys.exit(1)
1266
Tao Bao32fcdab2018-10-12 10:30:39 -07001267 common.InitLogging()
1268
Doug Zongker3c84f562014-07-31 11:06:30 -07001269 AddImagesToTargetFiles(args[0])
Kelvin Zhang834f5d42022-01-21 12:44:44 -08001270 OptimizeCompressedEntries(args[0])
Tao Bao32fcdab2018-10-12 10:30:39 -07001271 logger.info("done.")
Doug Zongker3c84f562014-07-31 11:06:30 -07001272
Kelvin Zhang834f5d42022-01-21 12:44:44 -08001273
Doug Zongker3c84f562014-07-31 11:06:30 -07001274if __name__ == '__main__':
1275 try:
1276 common.CloseInheritedPipes()
1277 main(sys.argv[1:])
Doug Zongkerfc44a512014-08-26 13:10:25 -07001278 finally:
1279 common.Cleanup()