blob: 465d222a8318fd3611aff442e66a67625d3c81a7 [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
Kelvin Zhangc819b292023-06-02 16:41:19 -070071from build_image import FIXED_FILE_TIMESTAMP
Kelvin Zhang1caead02022-09-23 10:06:03 -070072
Tao Bao6b9fef52017-12-01 16:13:22 -080073if sys.hexversion < 0x02070000:
74 print("Python 2.7 or newer is required.", file=sys.stderr)
75 sys.exit(1)
76
Tao Bao32fcdab2018-10-12 10:30:39 -070077logger = logging.getLogger(__name__)
Doug Zongker3c84f562014-07-31 11:06:30 -070078
Tao Bao32fcdab2018-10-12 10:30:39 -070079OPTIONS = common.OPTIONS
Michael Runge2e0d8fc2014-11-13 21:41:08 -080080OPTIONS.add_missing = False
81OPTIONS.rebuild_recovery = False
Tianjie Xu9ac4cb02017-06-09 16:58:03 -070082OPTIONS.replace_updated_files_list = []
Tianjie Xub48589a2016-08-03 19:21:52 -070083OPTIONS.is_signing = False
Doug Zongker3c84f562014-07-31 11:06:30 -070084
Bowgo Tsaid624fa62017-11-14 23:42:30 +080085
Kelvin Zhang1caead02022-09-23 10:06:03 -070086def ParseAvbFooter(img_path) -> avbtool.AvbFooter:
87 with open(img_path, 'rb') as fp:
88 fp.seek(-avbtool.AvbFooter.SIZE, os.SEEK_END)
89 data = fp.read(avbtool.AvbFooter.SIZE)
90 return avbtool.AvbFooter(data)
91
92
Kelvin Zhangf294c872022-10-06 14:21:36 -070093def GetCareMap(which, imgname):
94 """Returns the care_map string for the given partition.
95
96 Args:
97 which: The partition name, must be listed in PARTITIONS_WITH_CARE_MAP.
98 imgname: The filename of the image.
99
100 Returns:
101 (which, care_map_ranges): care_map_ranges is the raw string of the care_map
102 RangeSet; or None.
103 """
104 assert which in PARTITIONS_WITH_CARE_MAP
105
Kelvin Zhang1caead02022-09-23 10:06:03 -0700106 is_sparse_img = IsSparseImage(imgname)
107 unsparsed_image_size = os.path.getsize(imgname)
108
109 # A verified image contains original image + hash tree data + FEC data
110 # + AVB footer, all concatenated together. The caremap specifies a range
111 # of blocks that update_verifier should read on top of dm-verity device
112 # to verify correctness of OTA updates. When reading off of dm-verity device,
113 # the hashtree and FEC part of image isn't available. So caremap should
114 # only contain the original image blocks.
115 try:
116 avbfooter = None
117 if is_sparse_img:
118 with tempfile.NamedTemporaryFile() as tmpfile:
119 img = sparse_img.SparseImage(imgname)
120 unsparsed_image_size = img.total_blocks * img.blocksize
121 for data in img.ReadBlocks(img.total_blocks - 1, 1):
122 tmpfile.write(data)
123 tmpfile.flush()
124 avbfooter = ParseAvbFooter(tmpfile.name)
125 else:
126 avbfooter = ParseAvbFooter(imgname)
127 except LookupError as e:
128 logger.warning(
129 "Failed to parse avbfooter for partition %s image %s, %s", which, imgname, e)
Kelvin Zhangf294c872022-10-06 14:21:36 -0700130 return None
131
Kelvin Zhang1caead02022-09-23 10:06:03 -0700132 image_size = avbfooter.original_image_size
133 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."
134 assert image_size > 0
Kelvin Zhangf294c872022-10-06 14:21:36 -0700135
136 image_blocks = int(image_size) // 4096 - 1
137 # It's OK for image_blocks to be 0, because care map ranges are inclusive.
138 # So 0-0 means "just block 0", which is valid.
139 assert image_blocks >= 0, "blocks for {} must be non-negative, image size: {}".format(
140 which, image_size)
141
142 # For sparse images, we will only check the blocks that are listed in the care
143 # map, i.e. the ones with meaningful data.
Kelvin Zhang1caead02022-09-23 10:06:03 -0700144 if is_sparse_img:
Kelvin Zhangf294c872022-10-06 14:21:36 -0700145 simg = sparse_img.SparseImage(imgname)
146 care_map_ranges = simg.care_map.intersect(
147 rangelib.RangeSet("0-{}".format(image_blocks)))
148
149 # Otherwise for non-sparse images, we read all the blocks in the filesystem
150 # image.
151 else:
152 care_map_ranges = rangelib.RangeSet("0-{}".format(image_blocks))
153
154 return [which, care_map_ranges.to_string_raw()]
155
156
157def AddCareMapForAbOta(output_file, ab_partitions, image_paths):
158 """Generates and adds care_map.pb for a/b partition that has care_map.
159
160 Args:
161 output_file: The output zip file (needs to be already open),
162 or file path to write care_map.pb.
163 ab_partitions: The list of A/B partitions.
164 image_paths: A map from the partition name to the image path.
165 """
166 if not output_file:
167 raise ExternalError('Expected output_file for AddCareMapForAbOta')
168
169 care_map_list = []
170 for partition in ab_partitions:
171 partition = partition.strip()
172 if partition not in PARTITIONS_WITH_CARE_MAP:
173 continue
174
175 verity_block_device = "{}_verity_block_device".format(partition)
176 avb_hashtree_enable = "avb_{}_hashtree_enable".format(partition)
177 if (verity_block_device in OPTIONS.info_dict or
178 OPTIONS.info_dict.get(avb_hashtree_enable) == "true"):
179 if partition not in image_paths:
180 logger.warning('Potential partition with care_map missing from images: %s',
181 partition)
182 continue
183 image_path = image_paths[partition]
184 if not os.path.exists(image_path):
185 raise ExternalError('Expected image at path {}'.format(image_path))
186
187 care_map = GetCareMap(partition, image_path)
188 if not care_map:
189 continue
190 care_map_list += care_map
191
192 # adds fingerprint field to the care_map
193 # TODO(xunchang) revisit the fingerprint calculation for care_map.
194 partition_props = OPTIONS.info_dict.get(partition + ".build.prop")
195 prop_name_list = ["ro.{}.build.fingerprint".format(partition),
196 "ro.{}.build.thumbprint".format(partition)]
197
198 present_props = [x for x in prop_name_list if
199 partition_props and partition_props.GetProp(x)]
200 if not present_props:
201 logger.warning(
202 "fingerprint is not present for partition %s", partition)
203 property_id, fingerprint = "unknown", "unknown"
204 else:
205 property_id = present_props[0]
206 fingerprint = partition_props.GetProp(property_id)
207 care_map_list += [property_id, fingerprint]
208
209 if not care_map_list:
210 return
211
212 # Converts the list into proto buf message by calling care_map_generator; and
213 # writes the result to a temp file.
214 temp_care_map_text = MakeTempFile(prefix="caremap_text-",
215 suffix=".txt")
216 with open(temp_care_map_text, 'w') as text_file:
217 text_file.write('\n'.join(care_map_list))
218
219 temp_care_map = MakeTempFile(prefix="caremap-", suffix=".pb")
220 care_map_gen_cmd = ["care_map_generator", temp_care_map_text, temp_care_map]
221 RunAndCheckOutput(care_map_gen_cmd)
222
223 if not isinstance(output_file, zipfile.ZipFile):
224 shutil.copy(temp_care_map, output_file)
225 return
226 # output_file is a zip file
227 care_map_path = "META/care_map.pb"
228 if care_map_path in output_file.namelist():
229 # Copy the temp file into the OPTIONS.input_tmp dir and update the
230 # replace_updated_files_list used by add_img_to_target_files
231 if not OPTIONS.replace_updated_files_list:
232 OPTIONS.replace_updated_files_list = []
233 shutil.copy(temp_care_map, os.path.join(OPTIONS.input_tmp, care_map_path))
234 OPTIONS.replace_updated_files_list.append(care_map_path)
235 else:
236 ZipWrite(output_file, temp_care_map, arcname=care_map_path)
237
238
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800239class OutputFile(object):
Tao Bao93e7ebe2019-01-13 23:23:01 -0800240 """A helper class to write a generated file to the given dir or zip.
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800241
Tao Bao93e7ebe2019-01-13 23:23:01 -0800242 When generating images, we want the outputs to go into the given zip file, or
243 the given dir.
244
245 Attributes:
246 name: The name of the output file, regardless of the final destination.
247 """
248
Kelvin Zhang5f0fcee2021-01-19 15:30:46 -0500249 def __init__(self, output_zip, input_dir, *args):
Tao Bao93e7ebe2019-01-13 23:23:01 -0800250 # We write the intermediate output file under the given input_dir, even if
251 # the final destination is a zip archive.
Kelvin Zhang5f0fcee2021-01-19 15:30:46 -0500252 self.name = os.path.join(input_dir, *args)
Tao Bao93e7ebe2019-01-13 23:23:01 -0800253 self._output_zip = output_zip
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800254 if self._output_zip:
Kelvin Zhang5f0fcee2021-01-19 15:30:46 -0500255 self._zip_name = os.path.join(*args)
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800256
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800257 def Write(self, compress_type=None):
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800258 if self._output_zip:
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800259 common.ZipWrite(self._output_zip, self.name,
260 self._zip_name, compress_type=compress_type)
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800261
Tao Baoe30a6a62018-08-27 10:57:19 -0700262
Tao Bao886d8832018-02-27 11:46:19 -0800263def AddSystem(output_zip, recovery_img=None, boot_img=None):
Doug Zongker3c84f562014-07-31 11:06:30 -0700264 """Turn the contents of SYSTEM into a system image and store it in
David Zeuthend995f4b2016-01-29 16:59:17 -0500265 output_zip. Returns the name of the system image file."""
Michael Runge2e0d8fc2014-11-13 21:41:08 -0800266
Tao Bao886d8832018-02-27 11:46:19 -0800267 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "system.img")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800268 if os.path.exists(img.name):
Tao Bao32fcdab2018-10-12 10:30:39 -0700269 logger.info("system.img already exists; no need to rebuild...")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800270 return img.name
Michael Runge2e0d8fc2014-11-13 21:41:08 -0800271
272 def output_sink(fn, data):
Tao Baoa3705452019-06-24 15:33:41 -0700273 output_file = os.path.join(OPTIONS.input_tmp, "SYSTEM", fn)
274 with open(output_file, "wb") as ofile:
275 ofile.write(data)
Michael Runge2e0d8fc2014-11-13 21:41:08 -0800276
Daniel Normana4911da2019-03-15 14:36:21 -0700277 if output_zip:
278 arc_name = "SYSTEM/" + fn
279 if arc_name in output_zip.namelist():
280 OPTIONS.replace_updated_files_list.append(arc_name)
281 else:
Tao Baoa3705452019-06-24 15:33:41 -0700282 common.ZipWrite(output_zip, output_file, arc_name)
Tianjie Xu38af07f2017-05-25 17:38:53 -0700283
Bill Peckhame868aec2019-09-17 17:06:47 -0700284 board_uses_vendorimage = OPTIONS.info_dict.get(
285 "board_uses_vendorimage") == "true"
286
287 if (OPTIONS.rebuild_recovery and not board_uses_vendorimage and
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800288 recovery_img is not None and boot_img is not None):
Bill Peckhame868aec2019-09-17 17:06:47 -0700289 logger.info("Building new recovery patch on system at system/vendor")
Dan Albert8b72aef2015-03-23 19:13:21 -0700290 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink, recovery_img,
291 boot_img, info_dict=OPTIONS.info_dict)
Michael Runge2e0d8fc2014-11-13 21:41:08 -0800292
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800293 block_list = OutputFile(output_zip, OPTIONS.input_tmp,
294 "IMAGES", "system.map")
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800295 CreateImage(OPTIONS.input_tmp, OPTIONS.info_dict, "system", img,
296 block_list=block_list)
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800297 return img.name
Doug Zongkerfc44a512014-08-26 13:10:25 -0700298
299
Tao Bao886d8832018-02-27 11:46:19 -0800300def AddSystemOther(output_zip):
Alex Light4e358ab2016-06-16 14:47:10 -0700301 """Turn the contents of SYSTEM_OTHER into a system_other image
302 and store it in output_zip."""
303
Tao Bao886d8832018-02-27 11:46:19 -0800304 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "system_other.img")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800305 if os.path.exists(img.name):
Tao Bao32fcdab2018-10-12 10:30:39 -0700306 logger.info("system_other.img already exists; no need to rebuild...")
Alex Light4e358ab2016-06-16 14:47:10 -0700307 return
308
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800309 CreateImage(OPTIONS.input_tmp, OPTIONS.info_dict, "system_other", img)
Alex Light4e358ab2016-06-16 14:47:10 -0700310
311
Bill Peckhame868aec2019-09-17 17:06:47 -0700312def AddVendor(output_zip, recovery_img=None, boot_img=None):
Doug Zongkerfc44a512014-08-26 13:10:25 -0700313 """Turn the contents of VENDOR into a vendor image and store in it
314 output_zip."""
Michael Runge2e0d8fc2014-11-13 21:41:08 -0800315
Tao Bao886d8832018-02-27 11:46:19 -0800316 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "vendor.img")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800317 if os.path.exists(img.name):
Tao Bao32fcdab2018-10-12 10:30:39 -0700318 logger.info("vendor.img already exists; no need to rebuild...")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800319 return img.name
Michael Runge2e0d8fc2014-11-13 21:41:08 -0800320
Bill Peckhame868aec2019-09-17 17:06:47 -0700321 def output_sink(fn, data):
Iavor-Valentin Iftime756b5612022-02-09 15:56:14 +0000322 output_file = os.path.join(OPTIONS.input_tmp, "VENDOR", fn)
323 with open(output_file, "wb") as ofile:
324 ofile.write(data)
Bill Peckhame868aec2019-09-17 17:06:47 -0700325
326 if output_zip:
327 arc_name = "VENDOR/" + fn
328 if arc_name in output_zip.namelist():
329 OPTIONS.replace_updated_files_list.append(arc_name)
330 else:
Iavor-Valentin Iftime756b5612022-02-09 15:56:14 +0000331 common.ZipWrite(output_zip, output_file, arc_name)
Bill Peckhame868aec2019-09-17 17:06:47 -0700332
333 board_uses_vendorimage = OPTIONS.info_dict.get(
334 "board_uses_vendorimage") == "true"
335
336 if (OPTIONS.rebuild_recovery and board_uses_vendorimage and
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800337 recovery_img is not None and boot_img is not None):
Bill Peckhame868aec2019-09-17 17:06:47 -0700338 logger.info("Building new recovery patch on vendor")
339 common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink, recovery_img,
340 boot_img, info_dict=OPTIONS.info_dict)
341
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800342 block_list = OutputFile(output_zip, OPTIONS.input_tmp,
343 "IMAGES", "vendor.map")
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800344 CreateImage(OPTIONS.input_tmp, OPTIONS.info_dict, "vendor", img,
345 block_list=block_list)
346 return img.name
Doug Zongker3c84f562014-07-31 11:06:30 -0700347
Yueyao Zhu889ee5e2017-05-12 17:50:46 -0700348
Tao Bao886d8832018-02-27 11:46:19 -0800349def AddProduct(output_zip):
350 """Turn the contents of PRODUCT into a product image and store it in
351 output_zip."""
Jaekyun Seokb7735d82017-11-27 17:04:47 +0900352
Tao Bao886d8832018-02-27 11:46:19 -0800353 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "product.img")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800354 if os.path.exists(img.name):
Tao Bao32fcdab2018-10-12 10:30:39 -0700355 logger.info("product.img already exists; no need to rebuild...")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800356 return img.name
Jaekyun Seokb7735d82017-11-27 17:04:47 +0900357
Tao Bao886d8832018-02-27 11:46:19 -0800358 block_list = OutputFile(
359 output_zip, OPTIONS.input_tmp, "IMAGES", "product.map")
360 CreateImage(
361 OPTIONS.input_tmp, OPTIONS.info_dict, "product", img,
362 block_list=block_list)
Jaekyun Seokb7735d82017-11-27 17:04:47 +0900363 return img.name
364
365
Justin Yun6151e3f2019-06-25 15:58:13 +0900366def AddSystemExt(output_zip):
367 """Turn the contents of SYSTEM_EXT into a system_ext image and store it in
368 output_zip."""
Dario Freni5f681e12018-05-29 13:09:01 +0100369
370 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES",
Justin Yun6151e3f2019-06-25 15:58:13 +0900371 "system_ext.img")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800372 if os.path.exists(img.name):
Justin Yun6151e3f2019-06-25 15:58:13 +0900373 logger.info("system_ext.img already exists; no need to rebuild...")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800374 return img.name
Dario Freni5f681e12018-05-29 13:09:01 +0100375
376 block_list = OutputFile(
Justin Yun6151e3f2019-06-25 15:58:13 +0900377 output_zip, OPTIONS.input_tmp, "IMAGES", "system_ext.map")
Dario Freni5f681e12018-05-29 13:09:01 +0100378 CreateImage(
Justin Yun6151e3f2019-06-25 15:58:13 +0900379 OPTIONS.input_tmp, OPTIONS.info_dict, "system_ext", img,
Dario Freni5f681e12018-05-29 13:09:01 +0100380 block_list=block_list)
381 return img.name
382
383
Bowgo Tsaid624fa62017-11-14 23:42:30 +0800384def AddOdm(output_zip):
385 """Turn the contents of ODM into an odm image and store it in output_zip."""
386
387 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "odm.img")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800388 if os.path.exists(img.name):
Tao Bao32fcdab2018-10-12 10:30:39 -0700389 logger.info("odm.img already exists; no need to rebuild...")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800390 return img.name
Bowgo Tsaid624fa62017-11-14 23:42:30 +0800391
392 block_list = OutputFile(
393 output_zip, OPTIONS.input_tmp, "IMAGES", "odm.map")
394 CreateImage(
395 OPTIONS.input_tmp, OPTIONS.info_dict, "odm", img,
396 block_list=block_list)
397 return img.name
398
399
Yifan Hongcfb917a2020-05-07 14:58:20 -0700400def AddVendorDlkm(output_zip):
401 """Turn the contents of VENDOR_DLKM into an vendor_dlkm image and store it in output_zip."""
402
403 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "vendor_dlkm.img")
404 if os.path.exists(img.name):
405 logger.info("vendor_dlkm.img already exists; no need to rebuild...")
406 return img.name
407
408 block_list = OutputFile(
409 output_zip, OPTIONS.input_tmp, "IMAGES", "vendor_dlkm.map")
410 CreateImage(
411 OPTIONS.input_tmp, OPTIONS.info_dict, "vendor_dlkm", img,
412 block_list=block_list)
413 return img.name
414
Tianjiebbde59f2021-05-03 21:18:56 -0700415
Yifan Hongf496f1b2020-07-15 16:52:59 -0700416def AddOdmDlkm(output_zip):
417 """Turn the contents of OdmDlkm into an odm_dlkm image and store it in output_zip."""
418
419 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "odm_dlkm.img")
420 if os.path.exists(img.name):
421 logger.info("odm_dlkm.img already exists; no need to rebuild...")
422 return img.name
423
424 block_list = OutputFile(
425 output_zip, OPTIONS.input_tmp, "IMAGES", "odm_dlkm.map")
426 CreateImage(
427 OPTIONS.input_tmp, OPTIONS.info_dict, "odm_dlkm", img,
428 block_list=block_list)
429 return img.name
430
Kelvin Zhangf294c872022-10-06 14:21:36 -0700431
Ramji Jiyani13a41372022-01-27 07:05:08 +0000432def AddSystemDlkm(output_zip):
433 """Turn the contents of SystemDlkm into an system_dlkm image and store it in output_zip."""
434
435 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "system_dlkm.img")
436 if os.path.exists(img.name):
437 logger.info("system_dlkm.img already exists; no need to rebuild...")
438 return img.name
439
440 block_list = OutputFile(
441 output_zip, OPTIONS.input_tmp, "IMAGES", "system_dlkm.map")
442 CreateImage(
443 OPTIONS.input_tmp, OPTIONS.info_dict, "system_dlkm", img,
444 block_list=block_list)
445 return img.name
446
Yifan Hongcfb917a2020-05-07 14:58:20 -0700447
Tao Bao886d8832018-02-27 11:46:19 -0800448def AddDtbo(output_zip):
Tao Baoc633ed02017-05-30 21:46:33 -0700449 """Adds the DTBO image.
450
Tao Bao886d8832018-02-27 11:46:19 -0800451 Uses the image under IMAGES/ if it already exists. Otherwise looks for the
Tao Baoc633ed02017-05-30 21:46:33 -0700452 image under PREBUILT_IMAGES/, signs it as needed, and returns the image name.
453 """
Tao Bao886d8832018-02-27 11:46:19 -0800454 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "dtbo.img")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800455 if os.path.exists(img.name):
Tao Bao32fcdab2018-10-12 10:30:39 -0700456 logger.info("dtbo.img already exists; no need to rebuild...")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800457 return img.name
Tao Baoc633ed02017-05-30 21:46:33 -0700458
459 dtbo_prebuilt_path = os.path.join(
460 OPTIONS.input_tmp, "PREBUILT_IMAGES", "dtbo.img")
461 assert os.path.exists(dtbo_prebuilt_path)
462 shutil.copy(dtbo_prebuilt_path, img.name)
463
464 # AVB-sign the image as needed.
Bowgo Tsai3e599ea2017-05-26 18:30:04 +0800465 if OPTIONS.info_dict.get("avb_enable") == "true":
Rupert Shuttleworth72942742020-12-08 06:18:35 +0000466 # Signing requires +w
467 os.chmod(img.name, os.stat(img.name).st_mode | stat.S_IWUSR)
468
Tao Baof88e0ce2019-03-18 14:01:38 -0700469 avbtool = OPTIONS.info_dict["avb_avbtool"]
Tao Bao3ebfdde2017-05-23 23:06:55 -0700470 part_size = OPTIONS.info_dict["dtbo_size"]
Tao Baoc633ed02017-05-30 21:46:33 -0700471 # The AVB hash footer will be replaced if already present.
472 cmd = [avbtool, "add_hash_footer", "--image", img.name,
473 "--partition_size", str(part_size), "--partition_name", "dtbo"]
Bowgo Tsai3e599ea2017-05-26 18:30:04 +0800474 common.AppendAVBSigningArgs(cmd, "dtbo")
475 args = OPTIONS.info_dict.get("avb_dtbo_add_hash_footer_args")
Tao Baoc633ed02017-05-30 21:46:33 -0700476 if args and args.strip():
477 cmd.extend(shlex.split(args))
Tao Bao2764aee2018-11-21 11:02:48 -0800478 common.RunAndCheckOutput(cmd)
Tao Baoc633ed02017-05-30 21:46:33 -0700479
480 img.Write()
481 return img.name
482
Tianjiebbde59f2021-05-03 21:18:56 -0700483
Andrew Sculle077cf72021-02-18 10:27:29 +0000484def AddPvmfw(output_zip):
485 """Adds the pvmfw image.
486
487 Uses the image under IMAGES/ if it already exists. Otherwise looks for the
488 image under PREBUILT_IMAGES/, signs it as needed, and returns the image name.
489 """
490 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "pvmfw.img")
491 if os.path.exists(img.name):
492 logger.info("pvmfw.img already exists; no need to rebuild...")
493 return img.name
494
495 pvmfw_prebuilt_path = os.path.join(
496 OPTIONS.input_tmp, "PREBUILT_IMAGES", "pvmfw.img")
497 assert os.path.exists(pvmfw_prebuilt_path)
498 shutil.copy(pvmfw_prebuilt_path, img.name)
499
500 # AVB-sign the image as needed.
501 if OPTIONS.info_dict.get("avb_enable") == "true":
502 # Signing requires +w
503 os.chmod(img.name, os.stat(img.name).st_mode | stat.S_IWUSR)
504
505 avbtool = OPTIONS.info_dict["avb_avbtool"]
506 part_size = OPTIONS.info_dict["pvmfw_size"]
507 # The AVB hash footer will be replaced if already present.
508 cmd = [avbtool, "add_hash_footer", "--image", img.name,
509 "--partition_size", str(part_size), "--partition_name", "pvmfw"]
510 common.AppendAVBSigningArgs(cmd, "pvmfw")
511 args = OPTIONS.info_dict.get("avb_pvmfw_add_hash_footer_args")
512 if args and args.strip():
513 cmd.extend(shlex.split(args))
514 common.RunAndCheckOutput(cmd)
515
516 img.Write()
517 return img.name
518
Tianjiebbde59f2021-05-03 21:18:56 -0700519
Hongguang Chenf23364d2020-04-27 18:36:36 -0700520def AddCustomImages(output_zip, partition_name):
521 """Adds and signs custom images in IMAGES/.
522
523 Args:
524 output_zip: The output zip file (needs to be already open), or None to
525 write images to OPTIONS.input_tmp/.
526
527 Uses the image under IMAGES/ if it already exists. Otherwise looks for the
528 image under PREBUILT_IMAGES/, signs it as needed, and returns the image name.
529
530 Raises:
531 AssertionError: If image can't be found.
532 """
533
Hongguang Chenf23364d2020-04-27 18:36:36 -0700534 key_path = OPTIONS.info_dict.get("avb_{}_key_path".format(partition_name))
535 algorithm = OPTIONS.info_dict.get("avb_{}_algorithm".format(partition_name))
536 extra_args = OPTIONS.info_dict.get(
537 "avb_{}_add_hashtree_footer_args".format(partition_name))
538 partition_size = OPTIONS.info_dict.get(
539 "avb_{}_partition_size".format(partition_name))
540
541 builder = verity_utils.CreateCustomImageBuilder(
542 OPTIONS.info_dict, partition_name, partition_size,
543 key_path, algorithm, extra_args)
544
545 for img_name in OPTIONS.info_dict.get(
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800546 "avb_{}_image_list".format(partition_name)).split():
547 custom_image = OutputFile(
548 output_zip, OPTIONS.input_tmp, "IMAGES", img_name)
Hongguang Chenf23364d2020-04-27 18:36:36 -0700549 if os.path.exists(custom_image.name):
550 continue
551
552 custom_image_prebuilt_path = os.path.join(
553 OPTIONS.input_tmp, "PREBUILT_IMAGES", img_name)
554 assert os.path.exists(custom_image_prebuilt_path), \
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800555 "Failed to find %s at %s" % (img_name, custom_image_prebuilt_path)
Hongguang Chenf23364d2020-04-27 18:36:36 -0700556
557 shutil.copy(custom_image_prebuilt_path, custom_image.name)
558
559 if builder is not None:
560 builder.Build(custom_image.name)
561
562 custom_image.Write()
563
564 default = os.path.join(OPTIONS.input_tmp, "IMAGES", partition_name + ".img")
565 assert os.path.exists(default), \
566 "There should be one %s.img" % (partition_name)
567 return default
568
Doug Zongker3c84f562014-07-31 11:06:30 -0700569
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800570def CreateImage(input_dir, info_dict, what, output_file, block_list=None):
Tao Baoa3705452019-06-24 15:33:41 -0700571 logger.info("creating %s.img...", what)
Doug Zongker3c84f562014-07-31 11:06:30 -0700572
Doug Zongker3c84f562014-07-31 11:06:30 -0700573 image_props = build_image.ImagePropFromGlobalDict(info_dict, what)
Bryan Henrye6d547d2018-07-31 18:32:00 -0700574 image_props["timestamp"] = FIXED_FILE_TIMESTAMP
Tao Bao822f5842015-09-30 16:01:14 -0700575
Doug Zongker3c84f562014-07-31 11:06:30 -0700576 if what == "system":
577 fs_config_prefix = ""
578 else:
579 fs_config_prefix = what + "_"
580
581 fs_config = os.path.join(
582 input_dir, "META/" + fs_config_prefix + "filesystem_config.txt")
Dan Albert8b72aef2015-03-23 19:13:21 -0700583 if not os.path.exists(fs_config):
584 fs_config = None
Doug Zongker3c84f562014-07-31 11:06:30 -0700585
Ying Wanga2292c92015-03-24 19:07:40 -0700586 # Override values loaded from info_dict.
587 if fs_config:
588 image_props["fs_config"] = fs_config
Ying Wanga2292c92015-03-24 19:07:40 -0700589 if block_list:
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800590 image_props["block_list"] = block_list.name
Ying Wanga2292c92015-03-24 19:07:40 -0700591
Tao Baoc6bd70a2018-09-27 16:58:00 -0700592 build_image.BuildImage(
593 os.path.join(input_dir, what.upper()), image_props, output_file.name)
Doug Zongker3c84f562014-07-31 11:06:30 -0700594
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800595 output_file.Write()
596 if block_list:
597 block_list.Write()
598
Shashikant Baviskar16a73892019-02-07 10:57:21 +0900599 # Set the '_image_size' for given image size.
Tianjie Xuf1a13182017-01-19 17:39:30 -0800600 is_verity_partition = "verity_block_device" in image_props
hungweichen65ba3752022-08-17 10:14:01 +0000601 verity_supported = (image_props.get("avb_enable") == "true")
Tianjie Xu6b2e1552017-06-01 11:32:32 -0700602 is_avb_enable = image_props.get("avb_hashtree_enable") == "true"
603 if verity_supported and (is_verity_partition or is_avb_enable):
Tao Bao35f4ebc2018-09-27 15:31:11 -0700604 image_size = image_props.get("image_size")
605 if image_size:
Shashikant Baviskar16a73892019-02-07 10:57:21 +0900606 image_size_key = what + "_image_size"
607 info_dict[image_size_key] = int(image_size)
Tianjie Xuf1a13182017-01-19 17:39:30 -0800608
Yifan Hongc767f7c2018-11-08 15:41:24 -0800609 use_dynamic_size = (
Tao Bao2764aee2018-11-21 11:02:48 -0800610 info_dict.get("use_dynamic_partition_size") == "true" and
611 what in shlex.split(info_dict.get("dynamic_partition_list", "").strip()))
Yifan Hongc767f7c2018-11-08 15:41:24 -0800612 if use_dynamic_size:
613 info_dict.update(build_image.GlobalDictFromImageProp(image_props, what))
614
Doug Zongker3c84f562014-07-31 11:06:30 -0700615
Tao Bao886d8832018-02-27 11:46:19 -0800616def AddUserdata(output_zip):
Ying Wang2a048392015-06-25 13:56:53 -0700617 """Create a userdata image and store it in output_zip.
618
619 In most case we just create and store an empty userdata.img;
620 But the invoker can also request to create userdata.img with real
621 data from the target files, by setting "userdata_img_with_data=true"
622 in OPTIONS.info_dict.
623 """
Doug Zongker3c84f562014-07-31 11:06:30 -0700624
Tao Bao886d8832018-02-27 11:46:19 -0800625 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "userdata.img")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800626 if os.path.exists(img.name):
Tao Bao32fcdab2018-10-12 10:30:39 -0700627 logger.info("userdata.img already exists; no need to rebuild...")
Michael Runge2e0d8fc2014-11-13 21:41:08 -0800628 return
629
Elliott Hughes305b0882016-06-15 17:04:54 -0700630 # Skip userdata.img if no size.
Tao Bao2c15d9e2015-07-09 11:51:16 -0700631 image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, "data")
Elliott Hughes305b0882016-06-15 17:04:54 -0700632 if not image_props.get("partition_size"):
Doug Zongker3c84f562014-07-31 11:06:30 -0700633 return
634
Tao Bao32fcdab2018-10-12 10:30:39 -0700635 logger.info("creating userdata.img...")
Doug Zongker3c84f562014-07-31 11:06:30 -0700636
Bryan Henrye6d547d2018-07-31 18:32:00 -0700637 image_props["timestamp"] = FIXED_FILE_TIMESTAMP
Tao Bao822f5842015-09-30 16:01:14 -0700638
Tao Baofa863c82017-05-23 23:49:03 -0700639 if OPTIONS.info_dict.get("userdata_img_with_data") == "true":
640 user_dir = os.path.join(OPTIONS.input_tmp, "DATA")
Ying Wang2a048392015-06-25 13:56:53 -0700641 else:
Tao Bao1c830bf2017-12-25 10:43:47 -0800642 user_dir = common.MakeTempDir()
Ying Wang2a048392015-06-25 13:56:53 -0700643
Tao Baoc6bd70a2018-09-27 16:58:00 -0700644 build_image.BuildImage(user_dir, image_props, img.name)
Doug Zongker3c84f562014-07-31 11:06:30 -0700645
646 common.CheckSize(img.name, "userdata.img", OPTIONS.info_dict)
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800647 # Always use compression for useradata image.
648 # As it's likely huge and consist of lots of 0s.
649 img.Write(zipfile.ZIP_DEFLATED)
Doug Zongker3c84f562014-07-31 11:06:30 -0700650
651
Tao Bao744c4c72018-08-20 21:09:07 -0700652def AddVBMeta(output_zip, partitions, name, needed_partitions):
653 """Creates a VBMeta image and stores it in output_zip.
654
655 It generates the requested VBMeta image. The requested image could be for
656 top-level or chained VBMeta image, which is determined based on the name.
Tao Baobf70c312017-07-11 17:27:55 -0700657
658 Args:
659 output_zip: The output zip file, which needs to be already open.
660 partitions: A dict that's keyed by partition names with image paths as
Hongguang Chenf23364d2020-04-27 18:36:36 -0700661 values. Only valid partition names are accepted, as partitions listed
662 in common.AVB_PARTITIONS and custom partitions listed in
663 OPTIONS.info_dict.get("avb_custom_images_partition_list")
David Anderson7709ab22018-10-15 14:41:34 -0700664 name: Name of the VBMeta partition, e.g. 'vbmeta', 'vbmeta_system'.
Tao Bao744c4c72018-08-20 21:09:07 -0700665 needed_partitions: Partitions whose descriptors should be included into the
666 generated VBMeta image.
667
Tao Bao71064202018-10-22 15:08:02 -0700668 Returns:
669 Path to the created image.
670
Tao Bao744c4c72018-08-20 21:09:07 -0700671 Raises:
672 AssertionError: On invalid input args.
Tao Baobf70c312017-07-11 17:27:55 -0700673 """
Tao Bao744c4c72018-08-20 21:09:07 -0700674 assert needed_partitions, "Needed partitions must be specified"
675
676 img = OutputFile(
677 output_zip, OPTIONS.input_tmp, "IMAGES", "{}.img".format(name))
Tao Bao93e7ebe2019-01-13 23:23:01 -0800678 if os.path.exists(img.name):
Tao Bao32fcdab2018-10-12 10:30:39 -0700679 logger.info("%s.img already exists; not rebuilding...", name)
Tao Bao93e7ebe2019-01-13 23:23:01 -0800680 return img.name
Tao Bao262bf3f2017-07-11 17:27:55 -0700681
Daniel Norman276f0622019-07-26 14:13:51 -0700682 common.BuildVBMeta(img.name, partitions, name, needed_partitions)
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800683 img.Write()
Tao Bao71064202018-10-22 15:08:02 -0700684 return img.name
David Zeuthen2ce63ed2016-09-15 13:43:54 -0400685
686
Tao Bao886d8832018-02-27 11:46:19 -0800687def AddPartitionTable(output_zip):
David Zeuthen25328622016-04-08 15:08:03 -0400688 """Create a partition table image and store it in output_zip."""
689
Tao Bao886d8832018-02-27 11:46:19 -0800690 img = OutputFile(
691 output_zip, OPTIONS.input_tmp, "IMAGES", "partition-table.img")
692 bpt = OutputFile(
Bryan Henryf130a232018-04-26 11:59:33 -0700693 output_zip, OPTIONS.input_tmp, "META", "partition-table.bpt")
David Zeuthen25328622016-04-08 15:08:03 -0400694
695 # use BPTTOOL from environ, or "bpttool" if empty or not set.
696 bpttool = os.getenv("BPTTOOL") or "bpttool"
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800697 cmd = [bpttool, "make_table", "--output_json", bpt.name,
698 "--output_gpt", img.name]
David Zeuthen25328622016-04-08 15:08:03 -0400699 input_files_str = OPTIONS.info_dict["board_bpt_input_files"]
jiajia tange5ddfcd2022-06-21 10:36:12 +0800700 input_files = input_files_str.split()
David Zeuthen25328622016-04-08 15:08:03 -0400701 for i in input_files:
702 cmd.extend(["--input", i])
703 disk_size = OPTIONS.info_dict.get("board_bpt_disk_size")
704 if disk_size:
705 cmd.extend(["--disk_size", disk_size])
706 args = OPTIONS.info_dict.get("board_bpt_make_table_args")
707 if args:
708 cmd.extend(shlex.split(args))
Tao Bao2764aee2018-11-21 11:02:48 -0800709 common.RunAndCheckOutput(cmd)
David Zeuthen25328622016-04-08 15:08:03 -0400710
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800711 img.Write()
712 bpt.Write()
David Zeuthen25328622016-04-08 15:08:03 -0400713
714
Tao Bao886d8832018-02-27 11:46:19 -0800715def AddCache(output_zip):
Doug Zongker3c84f562014-07-31 11:06:30 -0700716 """Create an empty cache image and store it in output_zip."""
717
Tao Bao886d8832018-02-27 11:46:19 -0800718 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "cache.img")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800719 if os.path.exists(img.name):
Tao Bao32fcdab2018-10-12 10:30:39 -0700720 logger.info("cache.img already exists; no need to rebuild...")
Michael Runge2e0d8fc2014-11-13 21:41:08 -0800721 return
722
Tao Bao2c15d9e2015-07-09 11:51:16 -0700723 image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, "cache")
Doug Zongker3c84f562014-07-31 11:06:30 -0700724 # The build system has to explicitly request for cache.img.
725 if "fs_type" not in image_props:
726 return
727
Tao Bao32fcdab2018-10-12 10:30:39 -0700728 logger.info("creating cache.img...")
Doug Zongker3c84f562014-07-31 11:06:30 -0700729
Bryan Henrye6d547d2018-07-31 18:32:00 -0700730 image_props["timestamp"] = FIXED_FILE_TIMESTAMP
Tao Bao822f5842015-09-30 16:01:14 -0700731
Tao Bao1c830bf2017-12-25 10:43:47 -0800732 user_dir = common.MakeTempDir()
Tao Baoc6bd70a2018-09-27 16:58:00 -0700733 build_image.BuildImage(user_dir, image_props, img.name)
Doug Zongker3c84f562014-07-31 11:06:30 -0700734
735 common.CheckSize(img.name, "cache.img", OPTIONS.info_dict)
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800736 img.Write()
Doug Zongker3c84f562014-07-31 11:06:30 -0700737
738
Tao Bao5277d102018-04-17 23:47:21 -0700739def CheckAbOtaImages(output_zip, ab_partitions):
740 """Checks that all the listed A/B partitions have their images available.
Tao Baobea20ac2018-01-17 17:57:49 -0800741
Tao Bao5277d102018-04-17 23:47:21 -0700742 The images need to be available under IMAGES/ or RADIO/, with the former takes
743 a priority.
Tao Baobea20ac2018-01-17 17:57:49 -0800744
745 Args:
746 output_zip: The output zip file (needs to be already open), or None to
Tao Bao5277d102018-04-17 23:47:21 -0700747 find images in OPTIONS.input_tmp/.
Tao Baobea20ac2018-01-17 17:57:49 -0800748 ab_partitions: The list of A/B partitions.
749
750 Raises:
751 AssertionError: If it can't find an image.
752 """
753 for partition in ab_partitions:
Daniel Norman78554ea2021-09-14 10:29:38 -0700754 img_name = partition + ".img"
Tao Baobea20ac2018-01-17 17:57:49 -0800755
Tao Baoa2ff4c92018-01-17 12:14:43 -0800756 # Assert that the image is present under IMAGES/ now.
Tao Baobea20ac2018-01-17 17:57:49 -0800757 if output_zip:
758 # Zip spec says: All slashes MUST be forward slashes.
Tao Bao5277d102018-04-17 23:47:21 -0700759 images_path = "IMAGES/" + img_name
760 radio_path = "RADIO/" + img_name
761 available = (images_path in output_zip.namelist() or
762 radio_path in output_zip.namelist())
Tao Baobea20ac2018-01-17 17:57:49 -0800763 else:
Tao Bao5277d102018-04-17 23:47:21 -0700764 images_path = os.path.join(OPTIONS.input_tmp, "IMAGES", img_name)
765 radio_path = os.path.join(OPTIONS.input_tmp, "RADIO", img_name)
766 available = os.path.exists(images_path) or os.path.exists(radio_path)
767
768 assert available, "Failed to find " + img_name
Tao Baobea20ac2018-01-17 17:57:49 -0800769
770
Tao Baobea20ac2018-01-17 17:57:49 -0800771def AddPackRadioImages(output_zip, images):
772 """Copies images listed in META/pack_radioimages.txt from RADIO/ to IMAGES/.
773
774 Args:
775 output_zip: The output zip file (needs to be already open), or None to
776 write images to OPTIONS.input_tmp/.
777 images: A list of image names.
778
779 Raises:
780 AssertionError: If a listed image can't be found.
781 """
782 for image in images:
783 img_name = image.strip()
784 _, ext = os.path.splitext(img_name)
785 if not ext:
786 img_name += ".img"
Tao Baoa2ff4c92018-01-17 12:14:43 -0800787
Tao Baobea20ac2018-01-17 17:57:49 -0800788 prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", img_name)
789 if os.path.exists(prebuilt_path):
Tao Bao32fcdab2018-10-12 10:30:39 -0700790 logger.info("%s already exists, no need to overwrite...", img_name)
Tao Baobea20ac2018-01-17 17:57:49 -0800791 continue
792
793 img_radio_path = os.path.join(OPTIONS.input_tmp, "RADIO", img_name)
794 assert os.path.exists(img_radio_path), \
795 "Failed to find %s at %s" % (img_name, img_radio_path)
Tao Baoa2ff4c92018-01-17 12:14:43 -0800796
Tao Baobea20ac2018-01-17 17:57:49 -0800797 if output_zip:
Tao Baoa2ff4c92018-01-17 12:14:43 -0800798 common.ZipWrite(output_zip, img_radio_path, "IMAGES/" + img_name)
Tao Baobea20ac2018-01-17 17:57:49 -0800799 else:
800 shutil.copy(img_radio_path, prebuilt_path)
801
802
David Anderson1ef03e22018-08-30 13:11:47 -0700803def AddSuperEmpty(output_zip):
804 """Create a super_empty.img and store it in output_zip."""
805
806 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "super_empty.img")
Kelvin Zhangaecdc182023-04-17 09:52:39 -0700807 if os.path.exists(img.name):
808 logger.info("super_empty.img already exists; no need to rebuild...")
809 return
Yifan Hong055e6cf2018-11-29 13:51:48 -0800810 build_super_image.BuildSuperImage(OPTIONS.info_dict, img.name)
David Anderson1ef03e22018-08-30 13:11:47 -0700811 img.Write()
812
813
Yifan Hongc767f7c2018-11-08 15:41:24 -0800814def AddSuperSplit(output_zip):
815 """Create split super_*.img and store it in output_zip."""
816
Yifan Hong055e6cf2018-11-29 13:51:48 -0800817 outdir = os.path.join(OPTIONS.input_tmp, "OTA")
Yifan Honge98427a2018-12-07 10:08:27 -0800818 built = build_super_image.BuildSuperImage(OPTIONS.input_tmp, outdir)
Yifan Hongc767f7c2018-11-08 15:41:24 -0800819
Yifan Honge98427a2018-12-07 10:08:27 -0800820 if built:
821 for dev in OPTIONS.info_dict['super_block_devices'].strip().split():
822 img = OutputFile(output_zip, OPTIONS.input_tmp, "OTA",
823 "super_" + dev + ".img")
824 img.Write()
Yifan Hongc767f7c2018-11-08 15:41:24 -0800825
826
Tianjie Xu9ac4cb02017-06-09 16:58:03 -0700827def ReplaceUpdatedFiles(zip_filename, files_list):
Tao Bao89d7ab22017-12-14 17:05:33 -0800828 """Updates all the ZIP entries listed in files_list.
Tianjie Xu38af07f2017-05-25 17:38:53 -0700829
Tianjie Xu4c05f4a2018-09-14 16:24:41 -0700830 For now the list includes META/care_map.pb, and the related files under
Tianjie Xu9ac4cb02017-06-09 16:58:03 -0700831 SYSTEM/ after rebuilding recovery.
832 """
Tao Bao89d7ab22017-12-14 17:05:33 -0800833 common.ZipDelete(zip_filename, files_list)
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000834 output_zip = zipfile.ZipFile(zip_filename, "a",
Tianjie Xu38af07f2017-05-25 17:38:53 -0700835 compression=zipfile.ZIP_DEFLATED,
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000836 allowZip64=True)
837 for item in files_list:
838 file_path = os.path.join(OPTIONS.input_tmp, item)
839 assert os.path.exists(file_path)
840 common.ZipWrite(output_zip, file_path, arcname=item)
841 common.ZipClose(output_zip)
Tianjie Xu38af07f2017-05-25 17:38:53 -0700842
843
Chris Gross435b8fe2020-09-15 09:53:44 -0700844def HasPartition(partition_name):
845 """Determines if the target files archive should build a given partition."""
846
847 return ((os.path.isdir(
848 os.path.join(OPTIONS.input_tmp, partition_name.upper())) and
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800849 OPTIONS.info_dict.get(
850 "building_{}_image".format(partition_name)) == "true") or
851 os.path.exists(
852 os.path.join(OPTIONS.input_tmp, "IMAGES",
853 "{}.img".format(partition_name))))
Chris Gross435b8fe2020-09-15 09:53:44 -0700854
Tianjiea5fca032021-06-01 22:06:28 -0700855
Kelvin Zhang5f0fcee2021-01-19 15:30:46 -0500856def AddApexInfo(output_zip):
Tianjiea5fca032021-06-01 22:06:28 -0700857 apex_infos = GetApexInfoFromTargetFiles(OPTIONS.input_tmp, 'system',
858 compressed_only=False)
Kelvin Zhang5f0fcee2021-01-19 15:30:46 -0500859 apex_metadata_proto = ota_metadata_pb2.ApexMetadata()
860 apex_metadata_proto.apex_info.extend(apex_infos)
861 apex_info_bytes = apex_metadata_proto.SerializeToString()
862
863 output_file = os.path.join(OPTIONS.input_tmp, "META", "apex_info.pb")
864 with open(output_file, "wb") as ofile:
865 ofile.write(apex_info_bytes)
866 if output_zip:
867 arc_name = "META/apex_info.pb"
868 if arc_name in output_zip.namelist():
869 OPTIONS.replace_updated_files_list.append(arc_name)
870 else:
871 common.ZipWrite(output_zip, output_file, arc_name)
872
Chris Gross435b8fe2020-09-15 09:53:44 -0700873
Tianjiec3bf3d02021-07-14 15:56:37 -0700874def AddVbmetaDigest(output_zip):
875 """Write the vbmeta digest to the output dir and zipfile."""
876
877 # Calculate the vbmeta digest and put the result in to META/
878 boot_images = OPTIONS.info_dict.get("boot_images")
879 # Disable the digest calculation if the target_file is used as a container
Bowgo Tsaiaba5c9e2021-09-27 14:08:41 +0800880 # for boot images. A boot container might contain boot-5.4.img, boot-5.10.img
881 # etc., instead of just a boot.img and will fail in vbmeta digest calculation.
882 boot_container = boot_images and (
883 len(boot_images.split()) >= 2 or boot_images.split()[0] != 'boot.img')
Tianjiec3bf3d02021-07-14 15:56:37 -0700884 if (OPTIONS.info_dict.get("avb_enable") == "true" and not boot_container and
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800885 OPTIONS.info_dict.get("avb_building_vbmeta_image") == "true"):
Tianjiec3bf3d02021-07-14 15:56:37 -0700886 avbtool = OPTIONS.info_dict["avb_avbtool"]
887 digest = verity_utils.CalculateVbmetaDigest(OPTIONS.input_tmp, avbtool)
888 vbmeta_digest_txt = os.path.join(OPTIONS.input_tmp, "META",
889 "vbmeta_digest.txt")
890 with open(vbmeta_digest_txt, 'w') as f:
891 f.write(digest)
892 # writes to the output zipfile
893 if output_zip:
894 arc_name = "META/vbmeta_digest.txt"
895 if arc_name in output_zip.namelist():
896 OPTIONS.replace_updated_files_list.append(arc_name)
897 else:
898 common.ZipWriteStr(output_zip, arc_name, digest)
899
900
Doug Zongker3c84f562014-07-31 11:06:30 -0700901def AddImagesToTargetFiles(filename):
Tao Baoae396d92017-11-20 11:56:43 -0800902 """Creates and adds images (boot/recovery/system/...) to a target_files.zip.
903
904 It works with either a zip file (zip mode), or a directory that contains the
905 files to be packed into a target_files.zip (dir mode). The latter is used when
906 being called from build/make/core/Makefile.
907
908 The images will be created under IMAGES/ in the input target_files.zip.
909
910 Args:
Tao Baodba59ee2018-01-09 13:21:02 -0800911 filename: the target_files.zip, or the zip root directory.
Tao Baoae396d92017-11-20 11:56:43 -0800912 """
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800913 if os.path.isdir(filename):
914 OPTIONS.input_tmp = os.path.abspath(filename)
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800915 else:
Tao Baodba59ee2018-01-09 13:21:02 -0800916 OPTIONS.input_tmp = common.UnzipTemp(filename)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700917
Michael Runge2e0d8fc2014-11-13 21:41:08 -0800918 if not OPTIONS.add_missing:
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800919 if os.path.isdir(os.path.join(OPTIONS.input_tmp, "IMAGES")):
Tao Bao32fcdab2018-10-12 10:30:39 -0700920 logger.warning("target_files appears to already contain images.")
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800921 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700922
Tao Bao410ad8b2018-08-24 12:08:38 -0700923 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.input_tmp, repacking=True)
Tao Baodba59ee2018-01-09 13:21:02 -0800924
925 has_recovery = OPTIONS.info_dict.get("no_recovery") != "true"
Chris Grossa784ef12019-04-22 11:09:57 -0700926 has_boot = OPTIONS.info_dict.get("no_boot") != "true"
Devin Mooreafdd7c72021-12-13 22:04:08 +0000927 has_init_boot = OPTIONS.info_dict.get("init_boot") == "true"
Steve Mucklee1b10862019-07-10 10:49:37 -0700928 has_vendor_boot = OPTIONS.info_dict.get("vendor_boot") == "true"
Kelvin Zhangf294c872022-10-06 14:21:36 -0700929 has_vendor_kernel_boot = OPTIONS.info_dict.get(
930 "vendor_kernel_boot") == "true"
Tao Baodba59ee2018-01-09 13:21:02 -0800931
Ramji Jiyani13a41372022-01-27 07:05:08 +0000932 # {vendor,odm,product,system_ext,vendor_dlkm,odm_dlkm, system_dlkm, system, system_other}.img
Chris Gross435b8fe2020-09-15 09:53:44 -0700933 # can be built from source, or dropped into target_files.zip as a prebuilt blob.
934 has_vendor = HasPartition("vendor")
935 has_odm = HasPartition("odm")
936 has_vendor_dlkm = HasPartition("vendor_dlkm")
937 has_odm_dlkm = HasPartition("odm_dlkm")
Ramji Jiyani13a41372022-01-27 07:05:08 +0000938 has_system_dlkm = HasPartition("system_dlkm")
Chris Gross435b8fe2020-09-15 09:53:44 -0700939 has_product = HasPartition("product")
940 has_system_ext = HasPartition("system_ext")
941 has_system = HasPartition("system")
942 has_system_other = HasPartition("system_other")
Chris Gross203191b2020-05-30 02:39:12 +0000943 has_userdata = OPTIONS.info_dict.get("building_userdata_image") == "true"
944 has_cache = OPTIONS.info_dict.get("building_cache_image") == "true"
Doug Zongker3c84f562014-07-31 11:06:30 -0700945
Tao Baodba59ee2018-01-09 13:21:02 -0800946 # Set up the output destination. It writes to the given directory for dir
947 # mode; otherwise appends to the given ZIP.
948 if os.path.isdir(filename):
949 output_zip = None
950 else:
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800951 output_zip = zipfile.ZipFile(filename, "a",
952 compression=zipfile.ZIP_DEFLATED,
953 allowZip64=True)
Tao Baoae396d92017-11-20 11:56:43 -0800954
955 # Always make input_tmp/IMAGES available, since we may stage boot / recovery
956 # images there even under zip mode. The directory will be cleaned up as part
957 # of OPTIONS.input_tmp.
958 images_dir = os.path.join(OPTIONS.input_tmp, "IMAGES")
959 if not os.path.isdir(images_dir):
960 os.makedirs(images_dir)
Doug Zongker3c84f562014-07-31 11:06:30 -0700961
Tao Baobf70c312017-07-11 17:27:55 -0700962 # A map between partition names and their paths, which could be used when
963 # generating AVB vbmeta image.
Tao Bao3ed35d32019-10-07 20:48:48 -0700964 partitions = {}
Tao Baobf70c312017-07-11 17:27:55 -0700965
Doug Zongkerfc44a512014-08-26 13:10:25 -0700966 def banner(s):
Tao Baoa3705452019-06-24 15:33:41 -0700967 logger.info("\n\n++++ %s ++++\n\n", s)
Doug Zongker3c84f562014-07-31 11:06:30 -0700968
Chris Grossa784ef12019-04-22 11:09:57 -0700969 boot_image = None
Greg Kaisere086f722021-09-14 19:32:27 +0000970 if has_boot:
Chris Grossa784ef12019-04-22 11:09:57 -0700971 banner("boot")
Steve Muckle9793cf62020-04-08 18:27:00 -0700972 boot_images = OPTIONS.info_dict.get("boot_images")
973 if boot_images is None:
974 boot_images = "boot.img"
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800975 for index, b in enumerate(boot_images.split()):
Steve Muckle9793cf62020-04-08 18:27:00 -0700976 # common.GetBootableImage() returns the image directly if present.
977 boot_image = common.GetBootableImage(
978 "IMAGES/" + b, b, OPTIONS.input_tmp, "BOOT")
979 # boot.img may be unavailable in some targets (e.g. aosp_arm64).
980 if boot_image:
981 boot_image_path = os.path.join(OPTIONS.input_tmp, "IMAGES", b)
Roopesh Nataraja3e15f6e2020-06-08 19:54:13 -0700982 # Although multiple boot images can be generated, include the image
983 # descriptor of only the first boot image in vbmeta
984 if index == 0:
Steve Muckle9793cf62020-04-08 18:27:00 -0700985 partitions['boot'] = boot_image_path
986 if not os.path.exists(boot_image_path):
987 boot_image.WriteToDir(OPTIONS.input_tmp)
988 if output_zip:
989 boot_image.AddToZip(output_zip)
Doug Zongker3c84f562014-07-31 11:06:30 -0700990
Devin Mooreafdd7c72021-12-13 22:04:08 +0000991 if has_init_boot:
992 banner("init_boot")
993 init_boot_image = common.GetBootableImage(
Vincent Donnefort6e861e92023-02-17 10:12:57 +0000994 "IMAGES/init_boot.img", "init_boot.img", OPTIONS.input_tmp, "INIT_BOOT",
995 dev_nodes=True)
Devin Mooreafdd7c72021-12-13 22:04:08 +0000996 if init_boot_image:
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800997 partitions['init_boot'] = os.path.join(
998 OPTIONS.input_tmp, "IMAGES", "init_boot.img")
Devin Mooreafdd7c72021-12-13 22:04:08 +0000999 if not os.path.exists(partitions['init_boot']):
1000 init_boot_image.WriteToDir(OPTIONS.input_tmp)
1001 if output_zip:
1002 init_boot_image.AddToZip(output_zip)
1003
Greg Kaisere086f722021-09-14 19:32:27 +00001004 if has_vendor_boot:
Steve Mucklee1b10862019-07-10 10:49:37 -07001005 banner("vendor_boot")
1006 vendor_boot_image = common.GetVendorBootImage(
1007 "IMAGES/vendor_boot.img", "vendor_boot.img", OPTIONS.input_tmp,
1008 "VENDOR_BOOT")
1009 if vendor_boot_image:
1010 partitions['vendor_boot'] = os.path.join(OPTIONS.input_tmp, "IMAGES",
1011 "vendor_boot.img")
1012 if not os.path.exists(partitions['vendor_boot']):
1013 vendor_boot_image.WriteToDir(OPTIONS.input_tmp)
1014 if output_zip:
1015 vendor_boot_image.AddToZip(output_zip)
1016
Lucas Weif57333f2022-02-24 10:30:15 +08001017 if has_vendor_kernel_boot:
1018 banner("vendor_kernel_boot")
Lucas Wei03230252022-04-18 16:00:40 +08001019 vendor_kernel_boot_image = common.GetVendorKernelBootImage(
Lucas Weif57333f2022-02-24 10:30:15 +08001020 "IMAGES/vendor_kernel_boot.img", "vendor_kernel_boot.img", OPTIONS.input_tmp,
1021 "VENDOR_KERNEL_BOOT")
1022 if vendor_kernel_boot_image:
1023 partitions['vendor_kernel_boot'] = os.path.join(OPTIONS.input_tmp, "IMAGES",
Kelvin Zhangf294c872022-10-06 14:21:36 -07001024 "vendor_kernel_boot.img")
Lucas Weif57333f2022-02-24 10:30:15 +08001025 if not os.path.exists(partitions['vendor_kernel_boot']):
1026 vendor_kernel_boot_image.WriteToDir(OPTIONS.input_tmp)
1027 if output_zip:
1028 vendor_kernel_boot_image.AddToZip(output_zip)
1029
Michael Runge2e0d8fc2014-11-13 21:41:08 -08001030 recovery_image = None
Greg Kaisere086f722021-09-14 19:32:27 +00001031 if has_recovery:
Tao Baodb45efa2015-10-27 19:25:18 -07001032 banner("recovery")
Tao Bao262bf3f2017-07-11 17:27:55 -07001033 recovery_image = common.GetBootableImage(
1034 "IMAGES/recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
1035 assert recovery_image, "Failed to create recovery.img."
Tao Baobf70c312017-07-11 17:27:55 -07001036 partitions['recovery'] = os.path.join(
Tao Bao262bf3f2017-07-11 17:27:55 -07001037 OPTIONS.input_tmp, "IMAGES", "recovery.img")
Tao Baobf70c312017-07-11 17:27:55 -07001038 if not os.path.exists(partitions['recovery']):
Tao Bao262bf3f2017-07-11 17:27:55 -07001039 recovery_image.WriteToDir(OPTIONS.input_tmp)
1040 if output_zip:
1041 recovery_image.AddToZip(output_zip)
Doug Zongker3c84f562014-07-31 11:06:30 -07001042
Tao Baod42e97e2016-11-30 12:11:57 -08001043 banner("recovery (two-step image)")
1044 # The special recovery.img for two-step package use.
1045 recovery_two_step_image = common.GetBootableImage(
Tao Bao04808502019-07-25 23:11:41 -07001046 "OTA/recovery-two-step.img", "recovery-two-step.img",
Tao Baod42e97e2016-11-30 12:11:57 -08001047 OPTIONS.input_tmp, "RECOVERY", two_step_image=True)
Tao Bao262bf3f2017-07-11 17:27:55 -07001048 assert recovery_two_step_image, "Failed to create recovery-two-step.img."
1049 recovery_two_step_image_path = os.path.join(
Tao Bao04808502019-07-25 23:11:41 -07001050 OPTIONS.input_tmp, "OTA", "recovery-two-step.img")
Tao Bao262bf3f2017-07-11 17:27:55 -07001051 if not os.path.exists(recovery_two_step_image_path):
1052 recovery_two_step_image.WriteToDir(OPTIONS.input_tmp)
Dan Willemsen2ee00d52017-03-05 19:51:56 -08001053 if output_zip:
1054 recovery_two_step_image.AddToZip(output_zip)
Tao Baod42e97e2016-11-30 12:11:57 -08001055
Daniel Norman78554ea2021-09-14 10:29:38 -07001056 def add_partition(partition, has_partition, add_func, add_args):
1057 if has_partition:
1058 banner(partition)
1059 partitions[partition] = add_func(output_zip, *add_args)
Tao Baobf70c312017-07-11 17:27:55 -07001060
Daniel Norman78554ea2021-09-14 10:29:38 -07001061 add_partition_calls = (
1062 ("system", has_system, AddSystem, [recovery_image, boot_image]),
1063 ("vendor", has_vendor, AddVendor, [recovery_image, boot_image]),
1064 ("product", has_product, AddProduct, []),
1065 ("system_ext", has_system_ext, AddSystemExt, []),
1066 ("odm", has_odm, AddOdm, []),
1067 ("vendor_dlkm", has_vendor_dlkm, AddVendorDlkm, []),
1068 ("odm_dlkm", has_odm_dlkm, AddOdmDlkm, []),
Ramji Jiyani13a41372022-01-27 07:05:08 +00001069 ("system_dlkm", has_system_dlkm, AddSystemDlkm, []),
Daniel Norman78554ea2021-09-14 10:29:38 -07001070 ("system_other", has_system_other, AddSystemOther, []),
1071 )
Jeongik Chafa909092023-05-12 14:43:09 +09001072 # If output_zip exists, each add_partition_calls writes bytes to the same output_zip,
1073 # which is not thread-safe. So, run them in serial if output_zip exists.
1074 if output_zip:
1075 for call in add_partition_calls:
1076 add_partition(*call)
1077 else:
1078 with ThreadPoolExecutor(max_workers=len(add_partition_calls)) as executor:
1079 for future in [executor.submit(add_partition, *call) for call in add_partition_calls]:
1080 future.result()
Tao Baobf70c312017-07-11 17:27:55 -07001081
Kelvin Zhang5f0fcee2021-01-19 15:30:46 -05001082 AddApexInfo(output_zip)
1083
Tianjie Xub48589a2016-08-03 19:21:52 -07001084 if not OPTIONS.is_signing:
Greg Kaisere086f722021-09-14 19:32:27 +00001085 banner("userdata")
1086 AddUserdata(output_zip)
1087 banner("cache")
1088 AddCache(output_zip)
Tao Baoc633ed02017-05-30 21:46:33 -07001089
1090 if OPTIONS.info_dict.get("board_bpt_enable") == "true":
David Zeuthen25328622016-04-08 15:08:03 -04001091 banner("partition-table")
1092 AddPartitionTable(output_zip)
Tao Baoc633ed02017-05-30 21:46:33 -07001093
Daniel Norman78554ea2021-09-14 10:29:38 -07001094 add_partition("dtbo",
1095 OPTIONS.info_dict.get("has_dtbo") == "true", AddDtbo, [])
1096 add_partition("pvmfw",
1097 OPTIONS.info_dict.get("has_pvmfw") == "true", AddPvmfw, [])
Andrew Sculle077cf72021-02-18 10:27:29 +00001098
Hongguang Chenf23364d2020-04-27 18:36:36 -07001099 # Custom images.
1100 custom_partitions = OPTIONS.info_dict.get(
1101 "avb_custom_images_partition_list", "").strip().split()
1102 for partition_name in custom_partitions:
1103 partition_name = partition_name.strip()
1104 banner("custom images for " + partition_name)
1105 partitions[partition_name] = AddCustomImages(output_zip, partition_name)
1106
Bowgo Tsai3e599ea2017-05-26 18:30:04 +08001107 if OPTIONS.info_dict.get("avb_enable") == "true":
Tao Bao744c4c72018-08-20 21:09:07 -07001108 # vbmeta_partitions includes the partitions that should be included into
1109 # top-level vbmeta.img, which are the ones that are not included in any
1110 # chained VBMeta image plus the chained VBMeta images themselves.
Hongguang Chenf23364d2020-04-27 18:36:36 -07001111 # Currently custom_partitions are all chained to VBMeta image.
1112 vbmeta_partitions = common.AVB_PARTITIONS[:] + tuple(custom_partitions)
Tao Bao744c4c72018-08-20 21:09:07 -07001113
David Anderson7709ab22018-10-15 14:41:34 -07001114 vbmeta_system = OPTIONS.info_dict.get("avb_vbmeta_system", "").strip()
Greg Kaisere086f722021-09-14 19:32:27 +00001115 if vbmeta_system:
David Anderson7709ab22018-10-15 14:41:34 -07001116 banner("vbmeta_system")
Tao Bao71064202018-10-22 15:08:02 -07001117 partitions["vbmeta_system"] = AddVBMeta(
David Anderson7709ab22018-10-15 14:41:34 -07001118 output_zip, partitions, "vbmeta_system", vbmeta_system.split())
Tao Bao744c4c72018-08-20 21:09:07 -07001119 vbmeta_partitions = [
1120 item for item in vbmeta_partitions
David Anderson7709ab22018-10-15 14:41:34 -07001121 if item not in vbmeta_system.split()]
1122 vbmeta_partitions.append("vbmeta_system")
Tao Bao744c4c72018-08-20 21:09:07 -07001123
1124 vbmeta_vendor = OPTIONS.info_dict.get("avb_vbmeta_vendor", "").strip()
Greg Kaisere086f722021-09-14 19:32:27 +00001125 if vbmeta_vendor:
Tao Bao744c4c72018-08-20 21:09:07 -07001126 banner("vbmeta_vendor")
Tao Bao71064202018-10-22 15:08:02 -07001127 partitions["vbmeta_vendor"] = AddVBMeta(
Tao Bao744c4c72018-08-20 21:09:07 -07001128 output_zip, partitions, "vbmeta_vendor", vbmeta_vendor.split())
1129 vbmeta_partitions = [
1130 item for item in vbmeta_partitions
1131 if item not in vbmeta_vendor.split()]
1132 vbmeta_partitions.append("vbmeta_vendor")
Kelvin Zhangc819b292023-06-02 16:41:19 -07001133 custom_avb_partitions = OPTIONS.info_dict.get(
1134 "avb_custom_vbmeta_images_partition_list", "").strip().split()
Kelvin Zhangb81b4e32023-01-10 10:37:56 -08001135 if custom_avb_partitions:
1136 for avb_part in custom_avb_partitions:
1137 partition_name = "vbmeta_" + avb_part
Kelvin Zhangc819b292023-06-02 16:41:19 -07001138 included_partitions = OPTIONS.info_dict.get(
1139 "avb_vbmeta_{}".format(avb_part), "").strip().split()
1140 assert included_partitions, "Custom vbmeta partition {0} missing avb_vbmeta_{0} prop".format(
1141 avb_part)
Kelvin Zhangb81b4e32023-01-10 10:37:56 -08001142 banner(partition_name)
Kelvin Zhangc819b292023-06-02 16:41:19 -07001143 logger.info("VBMeta partition {} needs {}".format(
1144 partition_name, included_partitions))
Kelvin Zhangb81b4e32023-01-10 10:37:56 -08001145 partitions[partition_name] = AddVBMeta(
1146 output_zip, partitions, partition_name, included_partitions)
1147 vbmeta_partitions = [
1148 item for item in vbmeta_partitions
1149 if item not in included_partitions]
1150 vbmeta_partitions.append(partition_name)
1151
Greg Kaisere086f722021-09-14 19:32:27 +00001152 if OPTIONS.info_dict.get("avb_building_vbmeta_image") == "true":
Bowgo Tsai82182252020-11-13 11:28:17 +08001153 banner("vbmeta")
1154 AddVBMeta(output_zip, partitions, "vbmeta", vbmeta_partitions)
Doug Zongker3c84f562014-07-31 11:06:30 -07001155
Tao Bao48a2feb2019-06-28 11:00:05 -07001156 if OPTIONS.info_dict.get("use_dynamic_partitions") == "true":
Greg Kaisere086f722021-09-14 19:32:27 +00001157 if OPTIONS.info_dict.get("build_super_empty_partition") == "true":
Yo Chiange86bab42021-03-25 10:12:28 +00001158 banner("super_empty")
1159 AddSuperEmpty(output_zip)
David Anderson1ef03e22018-08-30 13:11:47 -07001160
Greg Kaisere086f722021-09-14 19:32:27 +00001161 if OPTIONS.info_dict.get("build_super_partition") == "true":
Tao Bao519d1822018-12-27 12:47:23 -08001162 if OPTIONS.info_dict.get(
Kelvin Zhang834f5d42022-01-21 12:44:44 -08001163 "build_retrofit_dynamic_partitions_ota_package") == "true":
Yifan Hongc767f7c2018-11-08 15:41:24 -08001164 banner("super split images")
1165 AddSuperSplit(output_zip)
Yifan Hongc767f7c2018-11-08 15:41:24 -08001166
Tianjie Xuaaca4212016-06-28 14:34:03 -07001167 banner("radio")
Tao Baobea20ac2018-01-17 17:57:49 -08001168 ab_partitions_txt = os.path.join(OPTIONS.input_tmp, "META",
1169 "ab_partitions.txt")
1170 if os.path.exists(ab_partitions_txt):
Tao Baoa3705452019-06-24 15:33:41 -07001171 with open(ab_partitions_txt) as f:
Daniel Norman78554ea2021-09-14 10:29:38 -07001172 ab_partitions = f.read().splitlines()
Tianjie Xucfa86222016-03-07 16:31:19 -08001173
Greg Kaisere086f722021-09-14 19:32:27 +00001174 # For devices using A/B update, make sure we have all the needed images
1175 # ready under IMAGES/ or RADIO/.
1176 CheckAbOtaImages(output_zip, ab_partitions)
Tianjie Xuaaca4212016-06-28 14:34:03 -07001177
Greg Kaisere086f722021-09-14 19:32:27 +00001178 # Generate care_map.pb for ab_partitions, then write this file to
1179 # target_files package.
1180 output_care_map = os.path.join(OPTIONS.input_tmp, "META", "care_map.pb")
1181 AddCareMapForAbOta(output_zip if output_zip else output_care_map,
1182 ab_partitions, partitions)
Tianjie Xucfa86222016-03-07 16:31:19 -08001183
Tao Bao95a95c32017-06-16 15:30:23 -07001184 # Radio images that need to be packed into IMAGES/, and product-img.zip.
Tao Baobea20ac2018-01-17 17:57:49 -08001185 pack_radioimages_txt = os.path.join(
Tao Bao95a95c32017-06-16 15:30:23 -07001186 OPTIONS.input_tmp, "META", "pack_radioimages.txt")
Tao Baobea20ac2018-01-17 17:57:49 -08001187 if os.path.exists(pack_radioimages_txt):
Tao Baoa3705452019-06-24 15:33:41 -07001188 with open(pack_radioimages_txt) as f:
Tao Baobea20ac2018-01-17 17:57:49 -08001189 AddPackRadioImages(output_zip, f.readlines())
Tao Bao95a95c32017-06-16 15:30:23 -07001190
Greg Kaisere086f722021-09-14 19:32:27 +00001191 AddVbmetaDigest(output_zip)
Tianjiebbde59f2021-05-03 21:18:56 -07001192
Dan Willemsen2ee00d52017-03-05 19:51:56 -08001193 if output_zip:
Kelvin Zhangf92f7f02023-04-14 21:32:54 +00001194 common.ZipClose(output_zip)
Tianjie Xu9ac4cb02017-06-09 16:58:03 -07001195 if OPTIONS.replace_updated_files_list:
1196 ReplaceUpdatedFiles(output_zip.filename,
1197 OPTIONS.replace_updated_files_list)
Tianjie Xu38af07f2017-05-25 17:38:53 -07001198
Doug Zongker3c84f562014-07-31 11:06:30 -07001199
Kelvin Zhang834f5d42022-01-21 12:44:44 -08001200def OptimizeCompressedEntries(zipfile_path):
1201 """Convert files that do not compress well to uncompressed storage
1202
1203 EROFS images tend to be compressed already, so compressing them again
1204 yields little space savings. Leaving them uncompressed will make
1205 downstream tooling's job easier, and save compute time.
1206 """
1207 if not zipfile.is_zipfile(zipfile_path):
1208 return
1209 entries_to_store = []
1210 with tempfile.TemporaryDirectory() as tmpdir:
1211 with zipfile.ZipFile(zipfile_path, "r", allowZip64=True) as zfp:
1212 for zinfo in zfp.filelist:
1213 if not zinfo.filename.startswith("IMAGES/") and not zinfo.filename.startswith("META"):
Kelvin Zhang9d021e92022-02-07 16:37:15 -08001214 continue
Kelvin Zhang834f5d42022-01-21 12:44:44 -08001215 # Don't try to store userdata.img uncompressed, it's usually huge.
1216 if zinfo.filename.endswith("userdata.img"):
Kelvin Zhang9d021e92022-02-07 16:37:15 -08001217 continue
Kelvin Zhang834f5d42022-01-21 12:44:44 -08001218 if zinfo.compress_size > zinfo.file_size * 0.80 and zinfo.compress_type != zipfile.ZIP_STORED:
1219 entries_to_store.append(zinfo)
1220 zfp.extract(zinfo, tmpdir)
Kelvin Zhang70876142022-02-09 16:05:29 -08001221 if len(entries_to_store) == 0:
1222 return
Kelvin Zhang834f5d42022-01-21 12:44:44 -08001223 # Remove these entries, then re-add them as ZIP_STORED
Kelvin Zhang70876142022-02-09 16:05:29 -08001224 ZipDelete(zipfile_path, [entry.filename for entry in entries_to_store])
Kelvin Zhang834f5d42022-01-21 12:44:44 -08001225 with zipfile.ZipFile(zipfile_path, "a", allowZip64=True) as zfp:
1226 for entry in entries_to_store:
Kelvin Zhangf294c872022-10-06 14:21:36 -07001227 zfp.write(os.path.join(tmpdir, entry.filename),
1228 entry.filename, compress_type=zipfile.ZIP_STORED)
Kelvin Zhang834f5d42022-01-21 12:44:44 -08001229
1230
Doug Zongker3c84f562014-07-31 11:06:30 -07001231def main(argv):
Baligh Uddin59f4ff12015-09-16 21:20:30 -07001232 def option_handler(o, a):
Michael Runge2e0d8fc2014-11-13 21:41:08 -08001233 if o in ("-a", "--add_missing"):
1234 OPTIONS.add_missing = True
1235 elif o in ("-r", "--rebuild_recovery",):
1236 OPTIONS.rebuild_recovery = True
Baligh Uddin59f4ff12015-09-16 21:20:30 -07001237 elif o == "--replace_verity_private_key":
hungweichendd3fca02022-08-19 06:33:25 +00001238 raise ValueError("--replace_verity_private_key is no longer supported,"
1239 " please switch to AVB")
Baligh Uddin59f4ff12015-09-16 21:20:30 -07001240 elif o == "--replace_verity_public_key":
hungweichendd3fca02022-08-19 06:33:25 +00001241 raise ValueError("--replace_verity_public_key is no longer supported,"
1242 " please switch to AVB")
Tianjie Xub48589a2016-08-03 19:21:52 -07001243 elif o == "--is_signing":
1244 OPTIONS.is_signing = True
Michael Runge2e0d8fc2014-11-13 21:41:08 -08001245 else:
1246 return False
1247 return True
1248
Dan Albert8b72aef2015-03-23 19:13:21 -07001249 args = common.ParseOptions(
1250 argv, __doc__, extra_opts="ar",
Baligh Uddin59f4ff12015-09-16 21:20:30 -07001251 extra_long_opts=["add_missing", "rebuild_recovery",
1252 "replace_verity_public_key=",
1253 "replace_verity_private_key=",
Greg Kaisere086f722021-09-14 19:32:27 +00001254 "is_signing"],
Dan Albert8b72aef2015-03-23 19:13:21 -07001255 extra_option_handler=option_handler)
Michael Runge2e0d8fc2014-11-13 21:41:08 -08001256
Doug Zongker3c84f562014-07-31 11:06:30 -07001257 if len(args) != 1:
1258 common.Usage(__doc__)
1259 sys.exit(1)
1260
Tao Bao32fcdab2018-10-12 10:30:39 -07001261 common.InitLogging()
1262
Doug Zongker3c84f562014-07-31 11:06:30 -07001263 AddImagesToTargetFiles(args[0])
Kelvin Zhang834f5d42022-01-21 12:44:44 -08001264 OptimizeCompressedEntries(args[0])
Tao Bao32fcdab2018-10-12 10:30:39 -07001265 logger.info("done.")
Doug Zongker3c84f562014-07-31 11:06:30 -07001266
Kelvin Zhang834f5d42022-01-21 12:44:44 -08001267
Doug Zongker3c84f562014-07-31 11:06:30 -07001268if __name__ == '__main__':
1269 try:
1270 common.CloseInheritedPipes()
1271 main(sys.argv[1:])
Doug Zongkerfc44a512014-08-26 13:10:25 -07001272 finally:
1273 common.Cleanup()