blob: 31f8736e4657f6f6a4a63476b42207e6f21617d1 [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
Ray-cy.leee97e0cb2023-06-27 11:44:46 +0800520def AddCustomImages(output_zip, partition_name, image_list):
521 """Adds and signs avb custom images as needed in IMAGES/.
Hongguang Chenf23364d2020-04-27 18:36:36 -0700522
523 Args:
524 output_zip: The output zip file (needs to be already open), or None to
525 write images to OPTIONS.input_tmp/.
Ray-cy.leee97e0cb2023-06-27 11:44:46 +0800526 partition_name: The custom image partition name.
527 image_list: The image list of the custom image partition.
Hongguang Chenf23364d2020-04-27 18:36:36 -0700528
529 Uses the image under IMAGES/ if it already exists. Otherwise looks for the
530 image under PREBUILT_IMAGES/, signs it as needed, and returns the image name.
531
532 Raises:
533 AssertionError: If image can't be found.
534 """
535
Ray-cy.leee97e0cb2023-06-27 11:44:46 +0800536 builder = None
Hongguang Chenf23364d2020-04-27 18:36:36 -0700537 key_path = OPTIONS.info_dict.get("avb_{}_key_path".format(partition_name))
Ray-cy.leee97e0cb2023-06-27 11:44:46 +0800538 if key_path is not None:
539 algorithm = OPTIONS.info_dict.get("avb_{}_algorithm".format(partition_name))
540 extra_args = OPTIONS.info_dict.get(
541 "avb_{}_add_hashtree_footer_args".format(partition_name))
542 partition_size = OPTIONS.info_dict.get(
543 "avb_{}_partition_size".format(partition_name))
Hongguang Chenf23364d2020-04-27 18:36:36 -0700544
Ray-cy.leee97e0cb2023-06-27 11:44:46 +0800545 builder = verity_utils.CreateCustomImageBuilder(
546 OPTIONS.info_dict, partition_name, partition_size,
547 key_path, algorithm, extra_args)
Hongguang Chenf23364d2020-04-27 18:36:36 -0700548
Ray-cy.leee97e0cb2023-06-27 11:44:46 +0800549 for img_name in image_list:
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800550 custom_image = OutputFile(
551 output_zip, OPTIONS.input_tmp, "IMAGES", img_name)
Hongguang Chenf23364d2020-04-27 18:36:36 -0700552 if os.path.exists(custom_image.name):
553 continue
554
555 custom_image_prebuilt_path = os.path.join(
556 OPTIONS.input_tmp, "PREBUILT_IMAGES", img_name)
557 assert os.path.exists(custom_image_prebuilt_path), \
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800558 "Failed to find %s at %s" % (img_name, custom_image_prebuilt_path)
Hongguang Chenf23364d2020-04-27 18:36:36 -0700559
560 shutil.copy(custom_image_prebuilt_path, custom_image.name)
561
562 if builder is not None:
563 builder.Build(custom_image.name)
564
565 custom_image.Write()
566
567 default = os.path.join(OPTIONS.input_tmp, "IMAGES", partition_name + ".img")
568 assert os.path.exists(default), \
569 "There should be one %s.img" % (partition_name)
570 return default
571
Doug Zongker3c84f562014-07-31 11:06:30 -0700572
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800573def CreateImage(input_dir, info_dict, what, output_file, block_list=None):
Tao Baoa3705452019-06-24 15:33:41 -0700574 logger.info("creating %s.img...", what)
Doug Zongker3c84f562014-07-31 11:06:30 -0700575
Doug Zongker3c84f562014-07-31 11:06:30 -0700576 image_props = build_image.ImagePropFromGlobalDict(info_dict, what)
Bryan Henrye6d547d2018-07-31 18:32:00 -0700577 image_props["timestamp"] = FIXED_FILE_TIMESTAMP
Tao Bao822f5842015-09-30 16:01:14 -0700578
Doug Zongker3c84f562014-07-31 11:06:30 -0700579 if what == "system":
580 fs_config_prefix = ""
581 else:
582 fs_config_prefix = what + "_"
583
584 fs_config = os.path.join(
585 input_dir, "META/" + fs_config_prefix + "filesystem_config.txt")
Dan Albert8b72aef2015-03-23 19:13:21 -0700586 if not os.path.exists(fs_config):
587 fs_config = None
Doug Zongker3c84f562014-07-31 11:06:30 -0700588
Ying Wanga2292c92015-03-24 19:07:40 -0700589 # Override values loaded from info_dict.
590 if fs_config:
591 image_props["fs_config"] = fs_config
Ying Wanga2292c92015-03-24 19:07:40 -0700592 if block_list:
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800593 image_props["block_list"] = block_list.name
Ying Wanga2292c92015-03-24 19:07:40 -0700594
Tao Baoc6bd70a2018-09-27 16:58:00 -0700595 build_image.BuildImage(
596 os.path.join(input_dir, what.upper()), image_props, output_file.name)
Doug Zongker3c84f562014-07-31 11:06:30 -0700597
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800598 output_file.Write()
599 if block_list:
600 block_list.Write()
601
Shashikant Baviskar16a73892019-02-07 10:57:21 +0900602 # Set the '_image_size' for given image size.
Tianjie Xuf1a13182017-01-19 17:39:30 -0800603 is_verity_partition = "verity_block_device" in image_props
hungweichen65ba3752022-08-17 10:14:01 +0000604 verity_supported = (image_props.get("avb_enable") == "true")
Tianjie Xu6b2e1552017-06-01 11:32:32 -0700605 is_avb_enable = image_props.get("avb_hashtree_enable") == "true"
606 if verity_supported and (is_verity_partition or is_avb_enable):
Tao Bao35f4ebc2018-09-27 15:31:11 -0700607 image_size = image_props.get("image_size")
608 if image_size:
Shashikant Baviskar16a73892019-02-07 10:57:21 +0900609 image_size_key = what + "_image_size"
610 info_dict[image_size_key] = int(image_size)
Tianjie Xuf1a13182017-01-19 17:39:30 -0800611
Yifan Hongc767f7c2018-11-08 15:41:24 -0800612 use_dynamic_size = (
Tao Bao2764aee2018-11-21 11:02:48 -0800613 info_dict.get("use_dynamic_partition_size") == "true" and
614 what in shlex.split(info_dict.get("dynamic_partition_list", "").strip()))
Yifan Hongc767f7c2018-11-08 15:41:24 -0800615 if use_dynamic_size:
616 info_dict.update(build_image.GlobalDictFromImageProp(image_props, what))
617
Doug Zongker3c84f562014-07-31 11:06:30 -0700618
Tao Bao886d8832018-02-27 11:46:19 -0800619def AddUserdata(output_zip):
Ying Wang2a048392015-06-25 13:56:53 -0700620 """Create a userdata image and store it in output_zip.
621
622 In most case we just create and store an empty userdata.img;
623 But the invoker can also request to create userdata.img with real
624 data from the target files, by setting "userdata_img_with_data=true"
625 in OPTIONS.info_dict.
626 """
Doug Zongker3c84f562014-07-31 11:06:30 -0700627
Tao Bao886d8832018-02-27 11:46:19 -0800628 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "userdata.img")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800629 if os.path.exists(img.name):
Tao Bao32fcdab2018-10-12 10:30:39 -0700630 logger.info("userdata.img already exists; no need to rebuild...")
Michael Runge2e0d8fc2014-11-13 21:41:08 -0800631 return
632
Elliott Hughes305b0882016-06-15 17:04:54 -0700633 # Skip userdata.img if no size.
Tao Bao2c15d9e2015-07-09 11:51:16 -0700634 image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, "data")
Elliott Hughes305b0882016-06-15 17:04:54 -0700635 if not image_props.get("partition_size"):
Doug Zongker3c84f562014-07-31 11:06:30 -0700636 return
637
Tao Bao32fcdab2018-10-12 10:30:39 -0700638 logger.info("creating userdata.img...")
Doug Zongker3c84f562014-07-31 11:06:30 -0700639
Bryan Henrye6d547d2018-07-31 18:32:00 -0700640 image_props["timestamp"] = FIXED_FILE_TIMESTAMP
Tao Bao822f5842015-09-30 16:01:14 -0700641
Tao Baofa863c82017-05-23 23:49:03 -0700642 if OPTIONS.info_dict.get("userdata_img_with_data") == "true":
643 user_dir = os.path.join(OPTIONS.input_tmp, "DATA")
Ying Wang2a048392015-06-25 13:56:53 -0700644 else:
Tao Bao1c830bf2017-12-25 10:43:47 -0800645 user_dir = common.MakeTempDir()
Ying Wang2a048392015-06-25 13:56:53 -0700646
Tao Baoc6bd70a2018-09-27 16:58:00 -0700647 build_image.BuildImage(user_dir, image_props, img.name)
Doug Zongker3c84f562014-07-31 11:06:30 -0700648
649 common.CheckSize(img.name, "userdata.img", OPTIONS.info_dict)
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800650 # Always use compression for useradata image.
651 # As it's likely huge and consist of lots of 0s.
652 img.Write(zipfile.ZIP_DEFLATED)
Doug Zongker3c84f562014-07-31 11:06:30 -0700653
654
Tao Bao744c4c72018-08-20 21:09:07 -0700655def AddVBMeta(output_zip, partitions, name, needed_partitions):
656 """Creates a VBMeta image and stores it in output_zip.
657
658 It generates the requested VBMeta image. The requested image could be for
659 top-level or chained VBMeta image, which is determined based on the name.
Tao Baobf70c312017-07-11 17:27:55 -0700660
661 Args:
662 output_zip: The output zip file, which needs to be already open.
663 partitions: A dict that's keyed by partition names with image paths as
Hongguang Chenf23364d2020-04-27 18:36:36 -0700664 values. Only valid partition names are accepted, as partitions listed
665 in common.AVB_PARTITIONS and custom partitions listed in
666 OPTIONS.info_dict.get("avb_custom_images_partition_list")
David Anderson7709ab22018-10-15 14:41:34 -0700667 name: Name of the VBMeta partition, e.g. 'vbmeta', 'vbmeta_system'.
Tao Bao744c4c72018-08-20 21:09:07 -0700668 needed_partitions: Partitions whose descriptors should be included into the
669 generated VBMeta image.
670
Tao Bao71064202018-10-22 15:08:02 -0700671 Returns:
672 Path to the created image.
673
Tao Bao744c4c72018-08-20 21:09:07 -0700674 Raises:
675 AssertionError: On invalid input args.
Tao Baobf70c312017-07-11 17:27:55 -0700676 """
Tao Bao744c4c72018-08-20 21:09:07 -0700677 assert needed_partitions, "Needed partitions must be specified"
678
679 img = OutputFile(
680 output_zip, OPTIONS.input_tmp, "IMAGES", "{}.img".format(name))
Tao Bao93e7ebe2019-01-13 23:23:01 -0800681 if os.path.exists(img.name):
Tao Bao32fcdab2018-10-12 10:30:39 -0700682 logger.info("%s.img already exists; not rebuilding...", name)
Tao Bao93e7ebe2019-01-13 23:23:01 -0800683 return img.name
Tao Bao262bf3f2017-07-11 17:27:55 -0700684
Daniel Norman276f0622019-07-26 14:13:51 -0700685 common.BuildVBMeta(img.name, partitions, name, needed_partitions)
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800686 img.Write()
Tao Bao71064202018-10-22 15:08:02 -0700687 return img.name
David Zeuthen2ce63ed2016-09-15 13:43:54 -0400688
689
Tao Bao886d8832018-02-27 11:46:19 -0800690def AddCache(output_zip):
Doug Zongker3c84f562014-07-31 11:06:30 -0700691 """Create an empty cache image and store it in output_zip."""
692
Tao Bao886d8832018-02-27 11:46:19 -0800693 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "cache.img")
Tao Bao93e7ebe2019-01-13 23:23:01 -0800694 if os.path.exists(img.name):
Tao Bao32fcdab2018-10-12 10:30:39 -0700695 logger.info("cache.img already exists; no need to rebuild...")
Michael Runge2e0d8fc2014-11-13 21:41:08 -0800696 return
697
Tao Bao2c15d9e2015-07-09 11:51:16 -0700698 image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, "cache")
Doug Zongker3c84f562014-07-31 11:06:30 -0700699 # The build system has to explicitly request for cache.img.
700 if "fs_type" not in image_props:
701 return
702
Tao Bao32fcdab2018-10-12 10:30:39 -0700703 logger.info("creating cache.img...")
Doug Zongker3c84f562014-07-31 11:06:30 -0700704
Bryan Henrye6d547d2018-07-31 18:32:00 -0700705 image_props["timestamp"] = FIXED_FILE_TIMESTAMP
Tao Bao822f5842015-09-30 16:01:14 -0700706
Tao Bao1c830bf2017-12-25 10:43:47 -0800707 user_dir = common.MakeTempDir()
Tao Baoc6bd70a2018-09-27 16:58:00 -0700708 build_image.BuildImage(user_dir, image_props, img.name)
Doug Zongker3c84f562014-07-31 11:06:30 -0700709
710 common.CheckSize(img.name, "cache.img", OPTIONS.info_dict)
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800711 img.Write()
Doug Zongker3c84f562014-07-31 11:06:30 -0700712
713
Tao Bao5277d102018-04-17 23:47:21 -0700714def CheckAbOtaImages(output_zip, ab_partitions):
715 """Checks that all the listed A/B partitions have their images available.
Tao Baobea20ac2018-01-17 17:57:49 -0800716
Tao Bao5277d102018-04-17 23:47:21 -0700717 The images need to be available under IMAGES/ or RADIO/, with the former takes
718 a priority.
Tao Baobea20ac2018-01-17 17:57:49 -0800719
720 Args:
721 output_zip: The output zip file (needs to be already open), or None to
Tao Bao5277d102018-04-17 23:47:21 -0700722 find images in OPTIONS.input_tmp/.
Tao Baobea20ac2018-01-17 17:57:49 -0800723 ab_partitions: The list of A/B partitions.
724
725 Raises:
726 AssertionError: If it can't find an image.
727 """
728 for partition in ab_partitions:
Daniel Norman78554ea2021-09-14 10:29:38 -0700729 img_name = partition + ".img"
Tao Baobea20ac2018-01-17 17:57:49 -0800730
Tao Baoa2ff4c92018-01-17 12:14:43 -0800731 # Assert that the image is present under IMAGES/ now.
Tao Baobea20ac2018-01-17 17:57:49 -0800732 if output_zip:
733 # Zip spec says: All slashes MUST be forward slashes.
Tao Bao5277d102018-04-17 23:47:21 -0700734 images_path = "IMAGES/" + img_name
735 radio_path = "RADIO/" + img_name
736 available = (images_path in output_zip.namelist() or
737 radio_path in output_zip.namelist())
Tao Baobea20ac2018-01-17 17:57:49 -0800738 else:
Tao Bao5277d102018-04-17 23:47:21 -0700739 images_path = os.path.join(OPTIONS.input_tmp, "IMAGES", img_name)
740 radio_path = os.path.join(OPTIONS.input_tmp, "RADIO", img_name)
741 available = os.path.exists(images_path) or os.path.exists(radio_path)
742
743 assert available, "Failed to find " + img_name
Tao Baobea20ac2018-01-17 17:57:49 -0800744
745
Tao Baobea20ac2018-01-17 17:57:49 -0800746def AddPackRadioImages(output_zip, images):
747 """Copies images listed in META/pack_radioimages.txt from RADIO/ to IMAGES/.
748
749 Args:
750 output_zip: The output zip file (needs to be already open), or None to
751 write images to OPTIONS.input_tmp/.
752 images: A list of image names.
753
754 Raises:
755 AssertionError: If a listed image can't be found.
756 """
757 for image in images:
758 img_name = image.strip()
759 _, ext = os.path.splitext(img_name)
760 if not ext:
761 img_name += ".img"
Tao Baoa2ff4c92018-01-17 12:14:43 -0800762
Tao Baobea20ac2018-01-17 17:57:49 -0800763 prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", img_name)
764 if os.path.exists(prebuilt_path):
Tao Bao32fcdab2018-10-12 10:30:39 -0700765 logger.info("%s already exists, no need to overwrite...", img_name)
Tao Baobea20ac2018-01-17 17:57:49 -0800766 continue
767
768 img_radio_path = os.path.join(OPTIONS.input_tmp, "RADIO", img_name)
769 assert os.path.exists(img_radio_path), \
770 "Failed to find %s at %s" % (img_name, img_radio_path)
Tao Baoa2ff4c92018-01-17 12:14:43 -0800771
Tao Baobea20ac2018-01-17 17:57:49 -0800772 if output_zip:
Tao Baoa2ff4c92018-01-17 12:14:43 -0800773 common.ZipWrite(output_zip, img_radio_path, "IMAGES/" + img_name)
Tao Baobea20ac2018-01-17 17:57:49 -0800774 else:
775 shutil.copy(img_radio_path, prebuilt_path)
776
777
David Anderson1ef03e22018-08-30 13:11:47 -0700778def AddSuperEmpty(output_zip):
779 """Create a super_empty.img and store it in output_zip."""
780
781 img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "super_empty.img")
Kelvin Zhangaecdc182023-04-17 09:52:39 -0700782 if os.path.exists(img.name):
783 logger.info("super_empty.img already exists; no need to rebuild...")
784 return
Yifan Hong055e6cf2018-11-29 13:51:48 -0800785 build_super_image.BuildSuperImage(OPTIONS.info_dict, img.name)
David Anderson1ef03e22018-08-30 13:11:47 -0700786 img.Write()
787
788
Yifan Hongc767f7c2018-11-08 15:41:24 -0800789def AddSuperSplit(output_zip):
790 """Create split super_*.img and store it in output_zip."""
791
Yifan Hong055e6cf2018-11-29 13:51:48 -0800792 outdir = os.path.join(OPTIONS.input_tmp, "OTA")
Yifan Honge98427a2018-12-07 10:08:27 -0800793 built = build_super_image.BuildSuperImage(OPTIONS.input_tmp, outdir)
Yifan Hongc767f7c2018-11-08 15:41:24 -0800794
Yifan Honge98427a2018-12-07 10:08:27 -0800795 if built:
796 for dev in OPTIONS.info_dict['super_block_devices'].strip().split():
797 img = OutputFile(output_zip, OPTIONS.input_tmp, "OTA",
798 "super_" + dev + ".img")
799 img.Write()
Yifan Hongc767f7c2018-11-08 15:41:24 -0800800
801
Tianjie Xu9ac4cb02017-06-09 16:58:03 -0700802def ReplaceUpdatedFiles(zip_filename, files_list):
Tao Bao89d7ab22017-12-14 17:05:33 -0800803 """Updates all the ZIP entries listed in files_list.
Tianjie Xu38af07f2017-05-25 17:38:53 -0700804
Tianjie Xu4c05f4a2018-09-14 16:24:41 -0700805 For now the list includes META/care_map.pb, and the related files under
Tianjie Xu9ac4cb02017-06-09 16:58:03 -0700806 SYSTEM/ after rebuilding recovery.
807 """
Tao Bao89d7ab22017-12-14 17:05:33 -0800808 common.ZipDelete(zip_filename, files_list)
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000809 output_zip = zipfile.ZipFile(zip_filename, "a",
Tianjie Xu38af07f2017-05-25 17:38:53 -0700810 compression=zipfile.ZIP_DEFLATED,
Kelvin Zhangf92f7f02023-04-14 21:32:54 +0000811 allowZip64=True)
812 for item in files_list:
813 file_path = os.path.join(OPTIONS.input_tmp, item)
814 assert os.path.exists(file_path)
815 common.ZipWrite(output_zip, file_path, arcname=item)
816 common.ZipClose(output_zip)
Tianjie Xu38af07f2017-05-25 17:38:53 -0700817
818
Chris Gross435b8fe2020-09-15 09:53:44 -0700819def HasPartition(partition_name):
820 """Determines if the target files archive should build a given partition."""
821
822 return ((os.path.isdir(
823 os.path.join(OPTIONS.input_tmp, partition_name.upper())) and
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800824 OPTIONS.info_dict.get(
825 "building_{}_image".format(partition_name)) == "true") or
826 os.path.exists(
827 os.path.join(OPTIONS.input_tmp, "IMAGES",
828 "{}.img".format(partition_name))))
Chris Gross435b8fe2020-09-15 09:53:44 -0700829
Tianjiea5fca032021-06-01 22:06:28 -0700830
Kelvin Zhang5f0fcee2021-01-19 15:30:46 -0500831def AddApexInfo(output_zip):
Tianjiea5fca032021-06-01 22:06:28 -0700832 apex_infos = GetApexInfoFromTargetFiles(OPTIONS.input_tmp, 'system',
833 compressed_only=False)
Kelvin Zhang5f0fcee2021-01-19 15:30:46 -0500834 apex_metadata_proto = ota_metadata_pb2.ApexMetadata()
835 apex_metadata_proto.apex_info.extend(apex_infos)
836 apex_info_bytes = apex_metadata_proto.SerializeToString()
837
838 output_file = os.path.join(OPTIONS.input_tmp, "META", "apex_info.pb")
839 with open(output_file, "wb") as ofile:
840 ofile.write(apex_info_bytes)
841 if output_zip:
842 arc_name = "META/apex_info.pb"
843 if arc_name in output_zip.namelist():
844 OPTIONS.replace_updated_files_list.append(arc_name)
845 else:
846 common.ZipWrite(output_zip, output_file, arc_name)
847
Chris Gross435b8fe2020-09-15 09:53:44 -0700848
Tianjiec3bf3d02021-07-14 15:56:37 -0700849def AddVbmetaDigest(output_zip):
850 """Write the vbmeta digest to the output dir and zipfile."""
851
852 # Calculate the vbmeta digest and put the result in to META/
853 boot_images = OPTIONS.info_dict.get("boot_images")
854 # Disable the digest calculation if the target_file is used as a container
Bowgo Tsaiaba5c9e2021-09-27 14:08:41 +0800855 # for boot images. A boot container might contain boot-5.4.img, boot-5.10.img
856 # etc., instead of just a boot.img and will fail in vbmeta digest calculation.
857 boot_container = boot_images and (
858 len(boot_images.split()) >= 2 or boot_images.split()[0] != 'boot.img')
Tianjiec3bf3d02021-07-14 15:56:37 -0700859 if (OPTIONS.info_dict.get("avb_enable") == "true" and not boot_container and
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800860 OPTIONS.info_dict.get("avb_building_vbmeta_image") == "true"):
Tianjiec3bf3d02021-07-14 15:56:37 -0700861 avbtool = OPTIONS.info_dict["avb_avbtool"]
862 digest = verity_utils.CalculateVbmetaDigest(OPTIONS.input_tmp, avbtool)
863 vbmeta_digest_txt = os.path.join(OPTIONS.input_tmp, "META",
864 "vbmeta_digest.txt")
865 with open(vbmeta_digest_txt, 'w') as f:
866 f.write(digest)
867 # writes to the output zipfile
868 if output_zip:
869 arc_name = "META/vbmeta_digest.txt"
870 if arc_name in output_zip.namelist():
871 OPTIONS.replace_updated_files_list.append(arc_name)
872 else:
873 common.ZipWriteStr(output_zip, arc_name, digest)
874
875
Doug Zongker3c84f562014-07-31 11:06:30 -0700876def AddImagesToTargetFiles(filename):
Tao Baoae396d92017-11-20 11:56:43 -0800877 """Creates and adds images (boot/recovery/system/...) to a target_files.zip.
878
879 It works with either a zip file (zip mode), or a directory that contains the
880 files to be packed into a target_files.zip (dir mode). The latter is used when
881 being called from build/make/core/Makefile.
882
883 The images will be created under IMAGES/ in the input target_files.zip.
884
885 Args:
Tao Baodba59ee2018-01-09 13:21:02 -0800886 filename: the target_files.zip, or the zip root directory.
Tao Baoae396d92017-11-20 11:56:43 -0800887 """
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800888 if os.path.isdir(filename):
889 OPTIONS.input_tmp = os.path.abspath(filename)
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800890 else:
Tao Baodba59ee2018-01-09 13:21:02 -0800891 OPTIONS.input_tmp = common.UnzipTemp(filename)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700892
Michael Runge2e0d8fc2014-11-13 21:41:08 -0800893 if not OPTIONS.add_missing:
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800894 if os.path.isdir(os.path.join(OPTIONS.input_tmp, "IMAGES")):
Tao Bao32fcdab2018-10-12 10:30:39 -0700895 logger.warning("target_files appears to already contain images.")
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800896 sys.exit(1)
Doug Zongkerfc44a512014-08-26 13:10:25 -0700897
Tao Bao410ad8b2018-08-24 12:08:38 -0700898 OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.input_tmp, repacking=True)
Tao Baodba59ee2018-01-09 13:21:02 -0800899
900 has_recovery = OPTIONS.info_dict.get("no_recovery") != "true"
Chris Grossa784ef12019-04-22 11:09:57 -0700901 has_boot = OPTIONS.info_dict.get("no_boot") != "true"
Devin Mooreafdd7c72021-12-13 22:04:08 +0000902 has_init_boot = OPTIONS.info_dict.get("init_boot") == "true"
Steve Mucklee1b10862019-07-10 10:49:37 -0700903 has_vendor_boot = OPTIONS.info_dict.get("vendor_boot") == "true"
Kelvin Zhangf294c872022-10-06 14:21:36 -0700904 has_vendor_kernel_boot = OPTIONS.info_dict.get(
905 "vendor_kernel_boot") == "true"
Tao Baodba59ee2018-01-09 13:21:02 -0800906
Ramji Jiyani13a41372022-01-27 07:05:08 +0000907 # {vendor,odm,product,system_ext,vendor_dlkm,odm_dlkm, system_dlkm, system, system_other}.img
Chris Gross435b8fe2020-09-15 09:53:44 -0700908 # can be built from source, or dropped into target_files.zip as a prebuilt blob.
909 has_vendor = HasPartition("vendor")
910 has_odm = HasPartition("odm")
911 has_vendor_dlkm = HasPartition("vendor_dlkm")
912 has_odm_dlkm = HasPartition("odm_dlkm")
Ramji Jiyani13a41372022-01-27 07:05:08 +0000913 has_system_dlkm = HasPartition("system_dlkm")
Chris Gross435b8fe2020-09-15 09:53:44 -0700914 has_product = HasPartition("product")
915 has_system_ext = HasPartition("system_ext")
916 has_system = HasPartition("system")
917 has_system_other = HasPartition("system_other")
Chris Gross203191b2020-05-30 02:39:12 +0000918 has_userdata = OPTIONS.info_dict.get("building_userdata_image") == "true"
919 has_cache = OPTIONS.info_dict.get("building_cache_image") == "true"
Doug Zongker3c84f562014-07-31 11:06:30 -0700920
Tao Baodba59ee2018-01-09 13:21:02 -0800921 # Set up the output destination. It writes to the given directory for dir
922 # mode; otherwise appends to the given ZIP.
923 if os.path.isdir(filename):
924 output_zip = None
925 else:
Dan Willemsen2ee00d52017-03-05 19:51:56 -0800926 output_zip = zipfile.ZipFile(filename, "a",
927 compression=zipfile.ZIP_DEFLATED,
928 allowZip64=True)
Tao Baoae396d92017-11-20 11:56:43 -0800929
930 # Always make input_tmp/IMAGES available, since we may stage boot / recovery
931 # images there even under zip mode. The directory will be cleaned up as part
932 # of OPTIONS.input_tmp.
933 images_dir = os.path.join(OPTIONS.input_tmp, "IMAGES")
934 if not os.path.isdir(images_dir):
935 os.makedirs(images_dir)
Doug Zongker3c84f562014-07-31 11:06:30 -0700936
Tao Baobf70c312017-07-11 17:27:55 -0700937 # A map between partition names and their paths, which could be used when
938 # generating AVB vbmeta image.
Tao Bao3ed35d32019-10-07 20:48:48 -0700939 partitions = {}
Tao Baobf70c312017-07-11 17:27:55 -0700940
Doug Zongkerfc44a512014-08-26 13:10:25 -0700941 def banner(s):
Tao Baoa3705452019-06-24 15:33:41 -0700942 logger.info("\n\n++++ %s ++++\n\n", s)
Doug Zongker3c84f562014-07-31 11:06:30 -0700943
Chris Grossa784ef12019-04-22 11:09:57 -0700944 boot_image = None
Greg Kaisere086f722021-09-14 19:32:27 +0000945 if has_boot:
Chris Grossa784ef12019-04-22 11:09:57 -0700946 banner("boot")
Steve Muckle9793cf62020-04-08 18:27:00 -0700947 boot_images = OPTIONS.info_dict.get("boot_images")
948 if boot_images is None:
949 boot_images = "boot.img"
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800950 for index, b in enumerate(boot_images.split()):
Steve Muckle9793cf62020-04-08 18:27:00 -0700951 # common.GetBootableImage() returns the image directly if present.
952 boot_image = common.GetBootableImage(
953 "IMAGES/" + b, b, OPTIONS.input_tmp, "BOOT")
954 # boot.img may be unavailable in some targets (e.g. aosp_arm64).
955 if boot_image:
956 boot_image_path = os.path.join(OPTIONS.input_tmp, "IMAGES", b)
Roopesh Nataraja3e15f6e2020-06-08 19:54:13 -0700957 # Although multiple boot images can be generated, include the image
958 # descriptor of only the first boot image in vbmeta
959 if index == 0:
Steve Muckle9793cf62020-04-08 18:27:00 -0700960 partitions['boot'] = boot_image_path
961 if not os.path.exists(boot_image_path):
962 boot_image.WriteToDir(OPTIONS.input_tmp)
963 if output_zip:
964 boot_image.AddToZip(output_zip)
Doug Zongker3c84f562014-07-31 11:06:30 -0700965
Devin Mooreafdd7c72021-12-13 22:04:08 +0000966 if has_init_boot:
967 banner("init_boot")
968 init_boot_image = common.GetBootableImage(
Vincent Donnefort6e861e92023-02-17 10:12:57 +0000969 "IMAGES/init_boot.img", "init_boot.img", OPTIONS.input_tmp, "INIT_BOOT",
970 dev_nodes=True)
Devin Mooreafdd7c72021-12-13 22:04:08 +0000971 if init_boot_image:
Kelvin Zhang834f5d42022-01-21 12:44:44 -0800972 partitions['init_boot'] = os.path.join(
973 OPTIONS.input_tmp, "IMAGES", "init_boot.img")
Devin Mooreafdd7c72021-12-13 22:04:08 +0000974 if not os.path.exists(partitions['init_boot']):
975 init_boot_image.WriteToDir(OPTIONS.input_tmp)
976 if output_zip:
977 init_boot_image.AddToZip(output_zip)
978
Greg Kaisere086f722021-09-14 19:32:27 +0000979 if has_vendor_boot:
Steve Mucklee1b10862019-07-10 10:49:37 -0700980 banner("vendor_boot")
981 vendor_boot_image = common.GetVendorBootImage(
982 "IMAGES/vendor_boot.img", "vendor_boot.img", OPTIONS.input_tmp,
983 "VENDOR_BOOT")
984 if vendor_boot_image:
985 partitions['vendor_boot'] = os.path.join(OPTIONS.input_tmp, "IMAGES",
986 "vendor_boot.img")
987 if not os.path.exists(partitions['vendor_boot']):
988 vendor_boot_image.WriteToDir(OPTIONS.input_tmp)
989 if output_zip:
990 vendor_boot_image.AddToZip(output_zip)
991
Lucas Weif57333f2022-02-24 10:30:15 +0800992 if has_vendor_kernel_boot:
993 banner("vendor_kernel_boot")
Lucas Wei03230252022-04-18 16:00:40 +0800994 vendor_kernel_boot_image = common.GetVendorKernelBootImage(
Lucas Weif57333f2022-02-24 10:30:15 +0800995 "IMAGES/vendor_kernel_boot.img", "vendor_kernel_boot.img", OPTIONS.input_tmp,
996 "VENDOR_KERNEL_BOOT")
997 if vendor_kernel_boot_image:
998 partitions['vendor_kernel_boot'] = os.path.join(OPTIONS.input_tmp, "IMAGES",
Kelvin Zhangf294c872022-10-06 14:21:36 -0700999 "vendor_kernel_boot.img")
Lucas Weif57333f2022-02-24 10:30:15 +08001000 if not os.path.exists(partitions['vendor_kernel_boot']):
1001 vendor_kernel_boot_image.WriteToDir(OPTIONS.input_tmp)
1002 if output_zip:
1003 vendor_kernel_boot_image.AddToZip(output_zip)
1004
Michael Runge2e0d8fc2014-11-13 21:41:08 -08001005 recovery_image = None
Greg Kaisere086f722021-09-14 19:32:27 +00001006 if has_recovery:
Tao Baodb45efa2015-10-27 19:25:18 -07001007 banner("recovery")
Tao Bao262bf3f2017-07-11 17:27:55 -07001008 recovery_image = common.GetBootableImage(
1009 "IMAGES/recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
1010 assert recovery_image, "Failed to create recovery.img."
Tao Baobf70c312017-07-11 17:27:55 -07001011 partitions['recovery'] = os.path.join(
Tao Bao262bf3f2017-07-11 17:27:55 -07001012 OPTIONS.input_tmp, "IMAGES", "recovery.img")
Tao Baobf70c312017-07-11 17:27:55 -07001013 if not os.path.exists(partitions['recovery']):
Tao Bao262bf3f2017-07-11 17:27:55 -07001014 recovery_image.WriteToDir(OPTIONS.input_tmp)
1015 if output_zip:
1016 recovery_image.AddToZip(output_zip)
Doug Zongker3c84f562014-07-31 11:06:30 -07001017
Tao Baod42e97e2016-11-30 12:11:57 -08001018 banner("recovery (two-step image)")
1019 # The special recovery.img for two-step package use.
1020 recovery_two_step_image = common.GetBootableImage(
Tao Bao04808502019-07-25 23:11:41 -07001021 "OTA/recovery-two-step.img", "recovery-two-step.img",
Tao Baod42e97e2016-11-30 12:11:57 -08001022 OPTIONS.input_tmp, "RECOVERY", two_step_image=True)
Tao Bao262bf3f2017-07-11 17:27:55 -07001023 assert recovery_two_step_image, "Failed to create recovery-two-step.img."
1024 recovery_two_step_image_path = os.path.join(
Tao Bao04808502019-07-25 23:11:41 -07001025 OPTIONS.input_tmp, "OTA", "recovery-two-step.img")
Tao Bao262bf3f2017-07-11 17:27:55 -07001026 if not os.path.exists(recovery_two_step_image_path):
1027 recovery_two_step_image.WriteToDir(OPTIONS.input_tmp)
Dan Willemsen2ee00d52017-03-05 19:51:56 -08001028 if output_zip:
1029 recovery_two_step_image.AddToZip(output_zip)
Tao Baod42e97e2016-11-30 12:11:57 -08001030
Daniel Norman78554ea2021-09-14 10:29:38 -07001031 def add_partition(partition, has_partition, add_func, add_args):
1032 if has_partition:
1033 banner(partition)
1034 partitions[partition] = add_func(output_zip, *add_args)
Tao Baobf70c312017-07-11 17:27:55 -07001035
Daniel Norman78554ea2021-09-14 10:29:38 -07001036 add_partition_calls = (
1037 ("system", has_system, AddSystem, [recovery_image, boot_image]),
1038 ("vendor", has_vendor, AddVendor, [recovery_image, boot_image]),
1039 ("product", has_product, AddProduct, []),
1040 ("system_ext", has_system_ext, AddSystemExt, []),
1041 ("odm", has_odm, AddOdm, []),
1042 ("vendor_dlkm", has_vendor_dlkm, AddVendorDlkm, []),
1043 ("odm_dlkm", has_odm_dlkm, AddOdmDlkm, []),
Ramji Jiyani13a41372022-01-27 07:05:08 +00001044 ("system_dlkm", has_system_dlkm, AddSystemDlkm, []),
Daniel Norman78554ea2021-09-14 10:29:38 -07001045 ("system_other", has_system_other, AddSystemOther, []),
1046 )
Jeongik Chafa909092023-05-12 14:43:09 +09001047 # If output_zip exists, each add_partition_calls writes bytes to the same output_zip,
1048 # which is not thread-safe. So, run them in serial if output_zip exists.
1049 if output_zip:
1050 for call in add_partition_calls:
1051 add_partition(*call)
1052 else:
1053 with ThreadPoolExecutor(max_workers=len(add_partition_calls)) as executor:
1054 for future in [executor.submit(add_partition, *call) for call in add_partition_calls]:
1055 future.result()
Tao Baobf70c312017-07-11 17:27:55 -07001056
Kelvin Zhang5f0fcee2021-01-19 15:30:46 -05001057 AddApexInfo(output_zip)
1058
Tianjie Xub48589a2016-08-03 19:21:52 -07001059 if not OPTIONS.is_signing:
Greg Kaisere086f722021-09-14 19:32:27 +00001060 banner("userdata")
1061 AddUserdata(output_zip)
1062 banner("cache")
1063 AddCache(output_zip)
Tao Baoc633ed02017-05-30 21:46:33 -07001064
Daniel Norman78554ea2021-09-14 10:29:38 -07001065 add_partition("dtbo",
1066 OPTIONS.info_dict.get("has_dtbo") == "true", AddDtbo, [])
1067 add_partition("pvmfw",
1068 OPTIONS.info_dict.get("has_pvmfw") == "true", AddPvmfw, [])
Andrew Sculle077cf72021-02-18 10:27:29 +00001069
Hongguang Chenf23364d2020-04-27 18:36:36 -07001070 # Custom images.
1071 custom_partitions = OPTIONS.info_dict.get(
Ray-cy.leee97e0cb2023-06-27 11:44:46 +08001072 "custom_images_partition_list", "").strip().split()
Hongguang Chenf23364d2020-04-27 18:36:36 -07001073 for partition_name in custom_partitions:
1074 partition_name = partition_name.strip()
1075 banner("custom images for " + partition_name)
Ray-cy.leee97e0cb2023-06-27 11:44:46 +08001076 image_list = OPTIONS.info_dict.get(
1077 "{}_image_list".format(partition_name)).split()
1078 partitions[partition_name] = AddCustomImages(output_zip, partition_name, image_list)
1079
1080 avb_custom_partitions = OPTIONS.info_dict.get(
1081 "avb_custom_images_partition_list", "").strip().split()
1082 for partition_name in avb_custom_partitions:
1083 partition_name = partition_name.strip()
1084 banner("avb custom images for " + partition_name)
1085 image_list = OPTIONS.info_dict.get(
1086 "avb_{}_image_list".format(partition_name)).split()
1087 partitions[partition_name] = AddCustomImages(output_zip, partition_name, image_list)
Hongguang Chenf23364d2020-04-27 18:36:36 -07001088
Bowgo Tsai3e599ea2017-05-26 18:30:04 +08001089 if OPTIONS.info_dict.get("avb_enable") == "true":
Tao Bao744c4c72018-08-20 21:09:07 -07001090 # vbmeta_partitions includes the partitions that should be included into
1091 # top-level vbmeta.img, which are the ones that are not included in any
1092 # chained VBMeta image plus the chained VBMeta images themselves.
Ray-cy.leee97e0cb2023-06-27 11:44:46 +08001093 # Currently avb_custom_partitions are all chained to VBMeta image.
1094 vbmeta_partitions = common.AVB_PARTITIONS[:] + tuple(avb_custom_partitions)
Tao Bao744c4c72018-08-20 21:09:07 -07001095
David Anderson7709ab22018-10-15 14:41:34 -07001096 vbmeta_system = OPTIONS.info_dict.get("avb_vbmeta_system", "").strip()
Greg Kaisere086f722021-09-14 19:32:27 +00001097 if vbmeta_system:
David Anderson7709ab22018-10-15 14:41:34 -07001098 banner("vbmeta_system")
Tao Bao71064202018-10-22 15:08:02 -07001099 partitions["vbmeta_system"] = AddVBMeta(
David Anderson7709ab22018-10-15 14:41:34 -07001100 output_zip, partitions, "vbmeta_system", vbmeta_system.split())
Tao Bao744c4c72018-08-20 21:09:07 -07001101 vbmeta_partitions = [
1102 item for item in vbmeta_partitions
David Anderson7709ab22018-10-15 14:41:34 -07001103 if item not in vbmeta_system.split()]
1104 vbmeta_partitions.append("vbmeta_system")
Tao Bao744c4c72018-08-20 21:09:07 -07001105
1106 vbmeta_vendor = OPTIONS.info_dict.get("avb_vbmeta_vendor", "").strip()
Greg Kaisere086f722021-09-14 19:32:27 +00001107 if vbmeta_vendor:
Tao Bao744c4c72018-08-20 21:09:07 -07001108 banner("vbmeta_vendor")
Tao Bao71064202018-10-22 15:08:02 -07001109 partitions["vbmeta_vendor"] = AddVBMeta(
Tao Bao744c4c72018-08-20 21:09:07 -07001110 output_zip, partitions, "vbmeta_vendor", vbmeta_vendor.split())
1111 vbmeta_partitions = [
1112 item for item in vbmeta_partitions
1113 if item not in vbmeta_vendor.split()]
1114 vbmeta_partitions.append("vbmeta_vendor")
Kelvin Zhangc819b292023-06-02 16:41:19 -07001115 custom_avb_partitions = OPTIONS.info_dict.get(
1116 "avb_custom_vbmeta_images_partition_list", "").strip().split()
Kelvin Zhangb81b4e32023-01-10 10:37:56 -08001117 if custom_avb_partitions:
1118 for avb_part in custom_avb_partitions:
1119 partition_name = "vbmeta_" + avb_part
Kelvin Zhangc819b292023-06-02 16:41:19 -07001120 included_partitions = OPTIONS.info_dict.get(
1121 "avb_vbmeta_{}".format(avb_part), "").strip().split()
1122 assert included_partitions, "Custom vbmeta partition {0} missing avb_vbmeta_{0} prop".format(
1123 avb_part)
Kelvin Zhangb81b4e32023-01-10 10:37:56 -08001124 banner(partition_name)
Kelvin Zhangc819b292023-06-02 16:41:19 -07001125 logger.info("VBMeta partition {} needs {}".format(
1126 partition_name, included_partitions))
Kelvin Zhangb81b4e32023-01-10 10:37:56 -08001127 partitions[partition_name] = AddVBMeta(
1128 output_zip, partitions, partition_name, included_partitions)
1129 vbmeta_partitions = [
1130 item for item in vbmeta_partitions
1131 if item not in included_partitions]
1132 vbmeta_partitions.append(partition_name)
1133
Greg Kaisere086f722021-09-14 19:32:27 +00001134 if OPTIONS.info_dict.get("avb_building_vbmeta_image") == "true":
Bowgo Tsai82182252020-11-13 11:28:17 +08001135 banner("vbmeta")
1136 AddVBMeta(output_zip, partitions, "vbmeta", vbmeta_partitions)
Doug Zongker3c84f562014-07-31 11:06:30 -07001137
Tao Bao48a2feb2019-06-28 11:00:05 -07001138 if OPTIONS.info_dict.get("use_dynamic_partitions") == "true":
Greg Kaisere086f722021-09-14 19:32:27 +00001139 if OPTIONS.info_dict.get("build_super_empty_partition") == "true":
Yo Chiange86bab42021-03-25 10:12:28 +00001140 banner("super_empty")
1141 AddSuperEmpty(output_zip)
David Anderson1ef03e22018-08-30 13:11:47 -07001142
Greg Kaisere086f722021-09-14 19:32:27 +00001143 if OPTIONS.info_dict.get("build_super_partition") == "true":
Tao Bao519d1822018-12-27 12:47:23 -08001144 if OPTIONS.info_dict.get(
Kelvin Zhang834f5d42022-01-21 12:44:44 -08001145 "build_retrofit_dynamic_partitions_ota_package") == "true":
Yifan Hongc767f7c2018-11-08 15:41:24 -08001146 banner("super split images")
1147 AddSuperSplit(output_zip)
Yifan Hongc767f7c2018-11-08 15:41:24 -08001148
Tianjie Xuaaca4212016-06-28 14:34:03 -07001149 banner("radio")
Tao Baobea20ac2018-01-17 17:57:49 -08001150 ab_partitions_txt = os.path.join(OPTIONS.input_tmp, "META",
1151 "ab_partitions.txt")
1152 if os.path.exists(ab_partitions_txt):
Tao Baoa3705452019-06-24 15:33:41 -07001153 with open(ab_partitions_txt) as f:
Daniel Norman78554ea2021-09-14 10:29:38 -07001154 ab_partitions = f.read().splitlines()
Tianjie Xucfa86222016-03-07 16:31:19 -08001155
Greg Kaisere086f722021-09-14 19:32:27 +00001156 # For devices using A/B update, make sure we have all the needed images
1157 # ready under IMAGES/ or RADIO/.
1158 CheckAbOtaImages(output_zip, ab_partitions)
Tianjie Xuaaca4212016-06-28 14:34:03 -07001159
Greg Kaisere086f722021-09-14 19:32:27 +00001160 # Generate care_map.pb for ab_partitions, then write this file to
1161 # target_files package.
1162 output_care_map = os.path.join(OPTIONS.input_tmp, "META", "care_map.pb")
1163 AddCareMapForAbOta(output_zip if output_zip else output_care_map,
1164 ab_partitions, partitions)
Tianjie Xucfa86222016-03-07 16:31:19 -08001165
Tao Bao95a95c32017-06-16 15:30:23 -07001166 # Radio images that need to be packed into IMAGES/, and product-img.zip.
Tao Baobea20ac2018-01-17 17:57:49 -08001167 pack_radioimages_txt = os.path.join(
Tao Bao95a95c32017-06-16 15:30:23 -07001168 OPTIONS.input_tmp, "META", "pack_radioimages.txt")
Tao Baobea20ac2018-01-17 17:57:49 -08001169 if os.path.exists(pack_radioimages_txt):
Tao Baoa3705452019-06-24 15:33:41 -07001170 with open(pack_radioimages_txt) as f:
Tao Baobea20ac2018-01-17 17:57:49 -08001171 AddPackRadioImages(output_zip, f.readlines())
Tao Bao95a95c32017-06-16 15:30:23 -07001172
Greg Kaisere086f722021-09-14 19:32:27 +00001173 AddVbmetaDigest(output_zip)
Tianjiebbde59f2021-05-03 21:18:56 -07001174
Dan Willemsen2ee00d52017-03-05 19:51:56 -08001175 if output_zip:
Kelvin Zhangf92f7f02023-04-14 21:32:54 +00001176 common.ZipClose(output_zip)
Tianjie Xu9ac4cb02017-06-09 16:58:03 -07001177 if OPTIONS.replace_updated_files_list:
1178 ReplaceUpdatedFiles(output_zip.filename,
1179 OPTIONS.replace_updated_files_list)
Tianjie Xu38af07f2017-05-25 17:38:53 -07001180
Doug Zongker3c84f562014-07-31 11:06:30 -07001181
Kelvin Zhang834f5d42022-01-21 12:44:44 -08001182def OptimizeCompressedEntries(zipfile_path):
1183 """Convert files that do not compress well to uncompressed storage
1184
1185 EROFS images tend to be compressed already, so compressing them again
1186 yields little space savings. Leaving them uncompressed will make
1187 downstream tooling's job easier, and save compute time.
1188 """
1189 if not zipfile.is_zipfile(zipfile_path):
1190 return
1191 entries_to_store = []
1192 with tempfile.TemporaryDirectory() as tmpdir:
1193 with zipfile.ZipFile(zipfile_path, "r", allowZip64=True) as zfp:
1194 for zinfo in zfp.filelist:
1195 if not zinfo.filename.startswith("IMAGES/") and not zinfo.filename.startswith("META"):
Kelvin Zhang9d021e92022-02-07 16:37:15 -08001196 continue
Kelvin Zhang834f5d42022-01-21 12:44:44 -08001197 # Don't try to store userdata.img uncompressed, it's usually huge.
1198 if zinfo.filename.endswith("userdata.img"):
Kelvin Zhang9d021e92022-02-07 16:37:15 -08001199 continue
Kelvin Zhang834f5d42022-01-21 12:44:44 -08001200 if zinfo.compress_size > zinfo.file_size * 0.80 and zinfo.compress_type != zipfile.ZIP_STORED:
1201 entries_to_store.append(zinfo)
1202 zfp.extract(zinfo, tmpdir)
Kelvin Zhang70876142022-02-09 16:05:29 -08001203 if len(entries_to_store) == 0:
1204 return
Kelvin Zhang834f5d42022-01-21 12:44:44 -08001205 # Remove these entries, then re-add them as ZIP_STORED
Kelvin Zhang70876142022-02-09 16:05:29 -08001206 ZipDelete(zipfile_path, [entry.filename for entry in entries_to_store])
Kelvin Zhang834f5d42022-01-21 12:44:44 -08001207 with zipfile.ZipFile(zipfile_path, "a", allowZip64=True) as zfp:
1208 for entry in entries_to_store:
Kelvin Zhangf294c872022-10-06 14:21:36 -07001209 zfp.write(os.path.join(tmpdir, entry.filename),
1210 entry.filename, compress_type=zipfile.ZIP_STORED)
Kelvin Zhang834f5d42022-01-21 12:44:44 -08001211
1212
Doug Zongker3c84f562014-07-31 11:06:30 -07001213def main(argv):
Baligh Uddin59f4ff12015-09-16 21:20:30 -07001214 def option_handler(o, a):
Michael Runge2e0d8fc2014-11-13 21:41:08 -08001215 if o in ("-a", "--add_missing"):
1216 OPTIONS.add_missing = True
1217 elif o in ("-r", "--rebuild_recovery",):
1218 OPTIONS.rebuild_recovery = True
Baligh Uddin59f4ff12015-09-16 21:20:30 -07001219 elif o == "--replace_verity_private_key":
hungweichendd3fca02022-08-19 06:33:25 +00001220 raise ValueError("--replace_verity_private_key is no longer supported,"
1221 " please switch to AVB")
Baligh Uddin59f4ff12015-09-16 21:20:30 -07001222 elif o == "--replace_verity_public_key":
hungweichendd3fca02022-08-19 06:33:25 +00001223 raise ValueError("--replace_verity_public_key is no longer supported,"
1224 " please switch to AVB")
Tianjie Xub48589a2016-08-03 19:21:52 -07001225 elif o == "--is_signing":
1226 OPTIONS.is_signing = True
Michael Runge2e0d8fc2014-11-13 21:41:08 -08001227 else:
1228 return False
1229 return True
1230
Dan Albert8b72aef2015-03-23 19:13:21 -07001231 args = common.ParseOptions(
1232 argv, __doc__, extra_opts="ar",
Baligh Uddin59f4ff12015-09-16 21:20:30 -07001233 extra_long_opts=["add_missing", "rebuild_recovery",
1234 "replace_verity_public_key=",
1235 "replace_verity_private_key=",
Greg Kaisere086f722021-09-14 19:32:27 +00001236 "is_signing"],
Dan Albert8b72aef2015-03-23 19:13:21 -07001237 extra_option_handler=option_handler)
Michael Runge2e0d8fc2014-11-13 21:41:08 -08001238
Doug Zongker3c84f562014-07-31 11:06:30 -07001239 if len(args) != 1:
1240 common.Usage(__doc__)
1241 sys.exit(1)
1242
Tao Bao32fcdab2018-10-12 10:30:39 -07001243 common.InitLogging()
1244
Doug Zongker3c84f562014-07-31 11:06:30 -07001245 AddImagesToTargetFiles(args[0])
Kelvin Zhang834f5d42022-01-21 12:44:44 -08001246 OptimizeCompressedEntries(args[0])
Tao Bao32fcdab2018-10-12 10:30:39 -07001247 logger.info("done.")
Doug Zongker3c84f562014-07-31 11:06:30 -07001248
Kelvin Zhang834f5d42022-01-21 12:44:44 -08001249
Doug Zongker3c84f562014-07-31 11:06:30 -07001250if __name__ == '__main__':
1251 try:
1252 common.CloseInheritedPipes()
1253 main(sys.argv[1:])
Doug Zongkerfc44a512014-08-26 13:10:25 -07001254 finally:
1255 common.Cleanup()