• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/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
22Usage:  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
31      meaningful when system image needs to be rebuilt and there're separate
32      boot / recovery images.
33
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.
45"""
46
47from __future__ import print_function
48
49import datetime
50import logging
51import os
52import shlex
53import shutil
54import sys
55import uuid
56import zipfile
57
58import build_image
59import build_super_image
60import common
61import rangelib
62import sparse_img
63import verity_utils
64
65if sys.hexversion < 0x02070000:
66  print("Python 2.7 or newer is required.", file=sys.stderr)
67  sys.exit(1)
68
69logger = logging.getLogger(__name__)
70
71OPTIONS = common.OPTIONS
72OPTIONS.add_missing = False
73OPTIONS.rebuild_recovery = False
74OPTIONS.replace_updated_files_list = []
75OPTIONS.replace_verity_public_key = False
76OPTIONS.replace_verity_private_key = False
77OPTIONS.is_signing = False
78
79# Use a fixed timestamp (01/01/2009 00:00:00 UTC) for files when packaging
80# images. (b/24377993, b/80600931)
81FIXED_FILE_TIMESTAMP = int((
82    datetime.datetime(2009, 1, 1, 0, 0, 0, 0, None) -
83    datetime.datetime.utcfromtimestamp(0)).total_seconds())
84
85
86class OutputFile(object):
87  """A helper class to write a generated file to the given dir or zip.
88
89  When generating images, we want the outputs to go into the given zip file, or
90  the given dir.
91
92  Attributes:
93    name: The name of the output file, regardless of the final destination.
94  """
95
96  def __init__(self, output_zip, input_dir, prefix, name):
97    # We write the intermediate output file under the given input_dir, even if
98    # the final destination is a zip archive.
99    self.name = os.path.join(input_dir, prefix, name)
100    self._output_zip = output_zip
101    if self._output_zip:
102      self._zip_name = os.path.join(prefix, name)
103
104  def Write(self):
105    if self._output_zip:
106      common.ZipWrite(self._output_zip, self.name, self._zip_name)
107
108
109def GetCareMap(which, imgname):
110  """Returns the care_map string for the given partition.
111
112  Args:
113    which: The partition name, must be listed in PARTITIONS_WITH_CARE_MAP.
114    imgname: The filename of the image.
115
116  Returns:
117    (which, care_map_ranges): care_map_ranges is the raw string of the care_map
118    RangeSet; or None.
119  """
120  assert which in common.PARTITIONS_WITH_CARE_MAP
121
122  # which + "_image_size" contains the size that the actual filesystem image
123  # resides in, which is all that needs to be verified. The additional blocks in
124  # the image file contain verity metadata, by reading which would trigger
125  # invalid reads.
126  image_size = OPTIONS.info_dict.get(which + "_image_size")
127  if not image_size:
128    return None
129
130  image_blocks = int(image_size) // 4096 - 1
131  assert image_blocks > 0, "blocks for {} must be positive".format(which)
132
133  # For sparse images, we will only check the blocks that are listed in the care
134  # map, i.e. the ones with meaningful data.
135  if "extfs_sparse_flag" in OPTIONS.info_dict:
136    simg = sparse_img.SparseImage(imgname)
137    care_map_ranges = simg.care_map.intersect(
138        rangelib.RangeSet("0-{}".format(image_blocks)))
139
140  # Otherwise for non-sparse images, we read all the blocks in the filesystem
141  # image.
142  else:
143    care_map_ranges = rangelib.RangeSet("0-{}".format(image_blocks))
144
145  return [which, care_map_ranges.to_string_raw()]
146
147
148def AddSystem(output_zip, recovery_img=None, boot_img=None):
149  """Turn the contents of SYSTEM into a system image and store it in
150  output_zip. Returns the name of the system image file."""
151
152  img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "system.img")
153  if os.path.exists(img.name):
154    logger.info("system.img already exists; no need to rebuild...")
155    return img.name
156
157  def output_sink(fn, data):
158    output_file = os.path.join(OPTIONS.input_tmp, "SYSTEM", fn)
159    with open(output_file, "wb") as ofile:
160      ofile.write(data)
161
162    if output_zip:
163      arc_name = "SYSTEM/" + fn
164      if arc_name in output_zip.namelist():
165        OPTIONS.replace_updated_files_list.append(arc_name)
166      else:
167        common.ZipWrite(output_zip, output_file, arc_name)
168
169  board_uses_vendorimage = OPTIONS.info_dict.get(
170      "board_uses_vendorimage") == "true"
171
172  if (OPTIONS.rebuild_recovery and not board_uses_vendorimage and
173      recovery_img is not None and boot_img is not None):
174    logger.info("Building new recovery patch on system at system/vendor")
175    common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink, recovery_img,
176                             boot_img, info_dict=OPTIONS.info_dict)
177
178  block_list = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "system.map")
179  CreateImage(OPTIONS.input_tmp, OPTIONS.info_dict, "system", img,
180              block_list=block_list)
181
182  return img.name
183
184
185def AddSystemOther(output_zip):
186  """Turn the contents of SYSTEM_OTHER into a system_other image
187  and store it in output_zip."""
188
189  img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "system_other.img")
190  if os.path.exists(img.name):
191    logger.info("system_other.img already exists; no need to rebuild...")
192    return
193
194  CreateImage(OPTIONS.input_tmp, OPTIONS.info_dict, "system_other", img)
195
196
197def AddVendor(output_zip, recovery_img=None, boot_img=None):
198  """Turn the contents of VENDOR into a vendor image and store in it
199  output_zip."""
200
201  img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "vendor.img")
202  if os.path.exists(img.name):
203    logger.info("vendor.img already exists; no need to rebuild...")
204    return img.name
205
206  def output_sink(fn, data):
207    ofile = open(os.path.join(OPTIONS.input_tmp, "VENDOR", fn), "w")
208    ofile.write(data)
209    ofile.close()
210
211    if output_zip:
212      arc_name = "VENDOR/" + fn
213      if arc_name in output_zip.namelist():
214        OPTIONS.replace_updated_files_list.append(arc_name)
215      else:
216        common.ZipWrite(output_zip, ofile.name, arc_name)
217
218  board_uses_vendorimage = OPTIONS.info_dict.get(
219      "board_uses_vendorimage") == "true"
220
221  if (OPTIONS.rebuild_recovery and board_uses_vendorimage and
222      recovery_img is not None and boot_img is not None):
223    logger.info("Building new recovery patch on vendor")
224    common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink, recovery_img,
225                             boot_img, info_dict=OPTIONS.info_dict)
226
227  block_list = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "vendor.map")
228  CreateImage(OPTIONS.input_tmp, OPTIONS.info_dict, "vendor", img,
229              block_list=block_list)
230  return img.name
231
232
233def AddProduct(output_zip):
234  """Turn the contents of PRODUCT into a product image and store it in
235  output_zip."""
236
237  img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "product.img")
238  if os.path.exists(img.name):
239    logger.info("product.img already exists; no need to rebuild...")
240    return img.name
241
242  block_list = OutputFile(
243      output_zip, OPTIONS.input_tmp, "IMAGES", "product.map")
244  CreateImage(
245      OPTIONS.input_tmp, OPTIONS.info_dict, "product", img,
246      block_list=block_list)
247  return img.name
248
249
250def AddSystemExt(output_zip):
251  """Turn the contents of SYSTEM_EXT into a system_ext image and store it in
252  output_zip."""
253
254  img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES",
255                   "system_ext.img")
256  if os.path.exists(img.name):
257    logger.info("system_ext.img already exists; no need to rebuild...")
258    return img.name
259
260  block_list = OutputFile(
261      output_zip, OPTIONS.input_tmp, "IMAGES", "system_ext.map")
262  CreateImage(
263      OPTIONS.input_tmp, OPTIONS.info_dict, "system_ext", img,
264      block_list=block_list)
265  return img.name
266
267
268def AddOdm(output_zip):
269  """Turn the contents of ODM into an odm image and store it in output_zip."""
270
271  img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "odm.img")
272  if os.path.exists(img.name):
273    logger.info("odm.img already exists; no need to rebuild...")
274    return img.name
275
276  block_list = OutputFile(
277      output_zip, OPTIONS.input_tmp, "IMAGES", "odm.map")
278  CreateImage(
279      OPTIONS.input_tmp, OPTIONS.info_dict, "odm", img,
280      block_list=block_list)
281  return img.name
282
283
284def AddDtbo(output_zip):
285  """Adds the DTBO image.
286
287  Uses the image under IMAGES/ if it already exists. Otherwise looks for the
288  image under PREBUILT_IMAGES/, signs it as needed, and returns the image name.
289  """
290  img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "dtbo.img")
291  if os.path.exists(img.name):
292    logger.info("dtbo.img already exists; no need to rebuild...")
293    return img.name
294
295  dtbo_prebuilt_path = os.path.join(
296      OPTIONS.input_tmp, "PREBUILT_IMAGES", "dtbo.img")
297  assert os.path.exists(dtbo_prebuilt_path)
298  shutil.copy(dtbo_prebuilt_path, img.name)
299
300  # AVB-sign the image as needed.
301  if OPTIONS.info_dict.get("avb_enable") == "true":
302    avbtool = OPTIONS.info_dict["avb_avbtool"]
303    part_size = OPTIONS.info_dict["dtbo_size"]
304    # The AVB hash footer will be replaced if already present.
305    cmd = [avbtool, "add_hash_footer", "--image", img.name,
306           "--partition_size", str(part_size), "--partition_name", "dtbo"]
307    common.AppendAVBSigningArgs(cmd, "dtbo")
308    args = OPTIONS.info_dict.get("avb_dtbo_add_hash_footer_args")
309    if args and args.strip():
310      cmd.extend(shlex.split(args))
311    common.RunAndCheckOutput(cmd)
312
313  img.Write()
314  return img.name
315
316def AddCustomImages(output_zip, partition_name):
317  """Adds and signs custom images in IMAGES/.
318
319  Args:
320    output_zip: The output zip file (needs to be already open), or None to
321        write images to OPTIONS.input_tmp/.
322
323  Uses the image under IMAGES/ if it already exists. Otherwise looks for the
324  image under PREBUILT_IMAGES/, signs it as needed, and returns the image name.
325
326  Raises:
327    AssertionError: If image can't be found.
328  """
329
330  partition_size = OPTIONS.info_dict.get(
331      "avb_{}_partition_size".format(partition_name))
332  key_path = OPTIONS.info_dict.get("avb_{}_key_path".format(partition_name))
333  algorithm = OPTIONS.info_dict.get("avb_{}_algorithm".format(partition_name))
334  extra_args = OPTIONS.info_dict.get(
335      "avb_{}_add_hashtree_footer_args".format(partition_name))
336  partition_size = OPTIONS.info_dict.get(
337      "avb_{}_partition_size".format(partition_name))
338
339  builder = verity_utils.CreateCustomImageBuilder(
340      OPTIONS.info_dict, partition_name, partition_size,
341      key_path, algorithm, extra_args)
342
343  for img_name in OPTIONS.info_dict.get(
344      "avb_{}_image_list".format(partition_name)).split():
345    custom_image = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", img_name)
346    if os.path.exists(custom_image.name):
347      continue
348
349    custom_image_prebuilt_path = os.path.join(
350        OPTIONS.input_tmp, "PREBUILT_IMAGES", img_name)
351    assert os.path.exists(custom_image_prebuilt_path), \
352      "Failed to find %s at %s" % (img_name, custom_image_prebuilt_path)
353
354    shutil.copy(custom_image_prebuilt_path, custom_image.name)
355
356    if builder is not None:
357      builder.Build(custom_image.name)
358
359    custom_image.Write()
360
361  default = os.path.join(OPTIONS.input_tmp, "IMAGES", partition_name + ".img")
362  assert os.path.exists(default), \
363      "There should be one %s.img" % (partition_name)
364  return default
365
366
367def CreateImage(input_dir, info_dict, what, output_file, block_list=None):
368  logger.info("creating %s.img...", what)
369
370  image_props = build_image.ImagePropFromGlobalDict(info_dict, what)
371  image_props["timestamp"] = FIXED_FILE_TIMESTAMP
372
373  if what == "system":
374    fs_config_prefix = ""
375  else:
376    fs_config_prefix = what + "_"
377
378  fs_config = os.path.join(
379      input_dir, "META/" + fs_config_prefix + "filesystem_config.txt")
380  if not os.path.exists(fs_config):
381    fs_config = None
382
383  # Override values loaded from info_dict.
384  if fs_config:
385    image_props["fs_config"] = fs_config
386  if block_list:
387    image_props["block_list"] = block_list.name
388
389  # Use repeatable ext4 FS UUID and hash_seed UUID (based on partition name and
390  # build fingerprint).
391  build_info = common.BuildInfo(info_dict)
392  uuid_seed = what + "-" + build_info.GetPartitionFingerprint(what)
393  image_props["uuid"] = str(uuid.uuid5(uuid.NAMESPACE_URL, uuid_seed))
394  hash_seed = "hash_seed-" + uuid_seed
395  image_props["hash_seed"] = str(uuid.uuid5(uuid.NAMESPACE_URL, hash_seed))
396
397  build_image.BuildImage(
398      os.path.join(input_dir, what.upper()), image_props, output_file.name)
399
400  output_file.Write()
401  if block_list:
402    block_list.Write()
403
404  # Set the '_image_size' for given image size.
405  is_verity_partition = "verity_block_device" in image_props
406  verity_supported = (image_props.get("verity") == "true" or
407                      image_props.get("avb_enable") == "true")
408  is_avb_enable = image_props.get("avb_hashtree_enable") == "true"
409  if verity_supported and (is_verity_partition or is_avb_enable):
410    image_size = image_props.get("image_size")
411    if image_size:
412      image_size_key = what + "_image_size"
413      info_dict[image_size_key] = int(image_size)
414
415  use_dynamic_size = (
416      info_dict.get("use_dynamic_partition_size") == "true" and
417      what in shlex.split(info_dict.get("dynamic_partition_list", "").strip()))
418  if use_dynamic_size:
419    info_dict.update(build_image.GlobalDictFromImageProp(image_props, what))
420
421
422def AddUserdata(output_zip):
423  """Create a userdata image and store it in output_zip.
424
425  In most case we just create and store an empty userdata.img;
426  But the invoker can also request to create userdata.img with real
427  data from the target files, by setting "userdata_img_with_data=true"
428  in OPTIONS.info_dict.
429  """
430
431  img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "userdata.img")
432  if os.path.exists(img.name):
433    logger.info("userdata.img already exists; no need to rebuild...")
434    return
435
436  # Skip userdata.img if no size.
437  image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, "data")
438  if not image_props.get("partition_size"):
439    return
440
441  logger.info("creating userdata.img...")
442
443  image_props["timestamp"] = FIXED_FILE_TIMESTAMP
444
445  if OPTIONS.info_dict.get("userdata_img_with_data") == "true":
446    user_dir = os.path.join(OPTIONS.input_tmp, "DATA")
447  else:
448    user_dir = common.MakeTempDir()
449
450  build_image.BuildImage(user_dir, image_props, img.name)
451
452  common.CheckSize(img.name, "userdata.img", OPTIONS.info_dict)
453  img.Write()
454
455
456def AddVBMeta(output_zip, partitions, name, needed_partitions):
457  """Creates a VBMeta image and stores it in output_zip.
458
459  It generates the requested VBMeta image. The requested image could be for
460  top-level or chained VBMeta image, which is determined based on the name.
461
462  Args:
463    output_zip: The output zip file, which needs to be already open.
464    partitions: A dict that's keyed by partition names with image paths as
465        values. Only valid partition names are accepted, as partitions listed
466        in common.AVB_PARTITIONS and custom partitions listed in
467        OPTIONS.info_dict.get("avb_custom_images_partition_list")
468    name: Name of the VBMeta partition, e.g. 'vbmeta', 'vbmeta_system'.
469    needed_partitions: Partitions whose descriptors should be included into the
470        generated VBMeta image.
471
472  Returns:
473    Path to the created image.
474
475  Raises:
476    AssertionError: On invalid input args.
477  """
478  assert needed_partitions, "Needed partitions must be specified"
479
480  img = OutputFile(
481      output_zip, OPTIONS.input_tmp, "IMAGES", "{}.img".format(name))
482  if os.path.exists(img.name):
483    logger.info("%s.img already exists; not rebuilding...", name)
484    return img.name
485
486  common.BuildVBMeta(img.name, partitions, name, needed_partitions)
487  img.Write()
488  return img.name
489
490
491def AddPartitionTable(output_zip):
492  """Create a partition table image and store it in output_zip."""
493
494  img = OutputFile(
495      output_zip, OPTIONS.input_tmp, "IMAGES", "partition-table.img")
496  bpt = OutputFile(
497      output_zip, OPTIONS.input_tmp, "META", "partition-table.bpt")
498
499  # use BPTTOOL from environ, or "bpttool" if empty or not set.
500  bpttool = os.getenv("BPTTOOL") or "bpttool"
501  cmd = [bpttool, "make_table", "--output_json", bpt.name,
502         "--output_gpt", img.name]
503  input_files_str = OPTIONS.info_dict["board_bpt_input_files"]
504  input_files = input_files_str.split(" ")
505  for i in input_files:
506    cmd.extend(["--input", i])
507  disk_size = OPTIONS.info_dict.get("board_bpt_disk_size")
508  if disk_size:
509    cmd.extend(["--disk_size", disk_size])
510  args = OPTIONS.info_dict.get("board_bpt_make_table_args")
511  if args:
512    cmd.extend(shlex.split(args))
513  common.RunAndCheckOutput(cmd)
514
515  img.Write()
516  bpt.Write()
517
518
519def AddCache(output_zip):
520  """Create an empty cache image and store it in output_zip."""
521
522  img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "cache.img")
523  if os.path.exists(img.name):
524    logger.info("cache.img already exists; no need to rebuild...")
525    return
526
527  image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, "cache")
528  # The build system has to explicitly request for cache.img.
529  if "fs_type" not in image_props:
530    return
531
532  logger.info("creating cache.img...")
533
534  image_props["timestamp"] = FIXED_FILE_TIMESTAMP
535
536  user_dir = common.MakeTempDir()
537  build_image.BuildImage(user_dir, image_props, img.name)
538
539  common.CheckSize(img.name, "cache.img", OPTIONS.info_dict)
540  img.Write()
541
542
543def CheckAbOtaImages(output_zip, ab_partitions):
544  """Checks that all the listed A/B partitions have their images available.
545
546  The images need to be available under IMAGES/ or RADIO/, with the former takes
547  a priority.
548
549  Args:
550    output_zip: The output zip file (needs to be already open), or None to
551        find images in OPTIONS.input_tmp/.
552    ab_partitions: The list of A/B partitions.
553
554  Raises:
555    AssertionError: If it can't find an image.
556  """
557  for partition in ab_partitions:
558    img_name = partition.strip() + ".img"
559
560    # Assert that the image is present under IMAGES/ now.
561    if output_zip:
562      # Zip spec says: All slashes MUST be forward slashes.
563      images_path = "IMAGES/" + img_name
564      radio_path = "RADIO/" + img_name
565      available = (images_path in output_zip.namelist() or
566                   radio_path in output_zip.namelist())
567    else:
568      images_path = os.path.join(OPTIONS.input_tmp, "IMAGES", img_name)
569      radio_path = os.path.join(OPTIONS.input_tmp, "RADIO", img_name)
570      available = os.path.exists(images_path) or os.path.exists(radio_path)
571
572    assert available, "Failed to find " + img_name
573
574
575def AddCareMapForAbOta(output_zip, ab_partitions, image_paths):
576  """Generates and adds care_map.pb for a/b partition that has care_map.
577
578  Args:
579    output_zip: The output zip file (needs to be already open), or None to
580        write care_map.pb to OPTIONS.input_tmp/.
581    ab_partitions: The list of A/B partitions.
582    image_paths: A map from the partition name to the image path.
583  """
584  care_map_list = []
585  for partition in ab_partitions:
586    partition = partition.strip()
587    if partition not in common.PARTITIONS_WITH_CARE_MAP:
588      continue
589
590    verity_block_device = "{}_verity_block_device".format(partition)
591    avb_hashtree_enable = "avb_{}_hashtree_enable".format(partition)
592    if (verity_block_device in OPTIONS.info_dict or
593        OPTIONS.info_dict.get(avb_hashtree_enable) == "true"):
594      image_path = image_paths[partition]
595      assert os.path.exists(image_path)
596
597      care_map = GetCareMap(partition, image_path)
598      if not care_map:
599        continue
600      care_map_list += care_map
601
602      # adds fingerprint field to the care_map
603      # TODO(xunchang) revisit the fingerprint calculation for care_map.
604      partition_props = OPTIONS.info_dict.get(partition + ".build.prop")
605      prop_name_list = ["ro.{}.build.fingerprint".format(partition),
606                        "ro.{}.build.thumbprint".format(partition)]
607
608      present_props = [x for x in prop_name_list if
609                       partition_props and partition_props.GetProp(x)]
610      if not present_props:
611        logger.warning("fingerprint is not present for partition %s", partition)
612        property_id, fingerprint = "unknown", "unknown"
613      else:
614        property_id = present_props[0]
615        fingerprint = partition_props.GetProp(property_id)
616      care_map_list += [property_id, fingerprint]
617
618  if not care_map_list:
619    return
620
621  # Converts the list into proto buf message by calling care_map_generator; and
622  # writes the result to a temp file.
623  temp_care_map_text = common.MakeTempFile(prefix="caremap_text-",
624                                           suffix=".txt")
625  with open(temp_care_map_text, 'w') as text_file:
626    text_file.write('\n'.join(care_map_list))
627
628  temp_care_map = common.MakeTempFile(prefix="caremap-", suffix=".pb")
629  care_map_gen_cmd = ["care_map_generator", temp_care_map_text, temp_care_map]
630  common.RunAndCheckOutput(care_map_gen_cmd)
631
632  care_map_path = "META/care_map.pb"
633  if output_zip and care_map_path not in output_zip.namelist():
634    common.ZipWrite(output_zip, temp_care_map, arcname=care_map_path)
635  else:
636    shutil.copy(temp_care_map, os.path.join(OPTIONS.input_tmp, care_map_path))
637    if output_zip:
638      OPTIONS.replace_updated_files_list.append(care_map_path)
639
640
641def AddPackRadioImages(output_zip, images):
642  """Copies images listed in META/pack_radioimages.txt from RADIO/ to IMAGES/.
643
644  Args:
645    output_zip: The output zip file (needs to be already open), or None to
646        write images to OPTIONS.input_tmp/.
647    images: A list of image names.
648
649  Raises:
650    AssertionError: If a listed image can't be found.
651  """
652  for image in images:
653    img_name = image.strip()
654    _, ext = os.path.splitext(img_name)
655    if not ext:
656      img_name += ".img"
657
658    prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", img_name)
659    if os.path.exists(prebuilt_path):
660      logger.info("%s already exists, no need to overwrite...", img_name)
661      continue
662
663    img_radio_path = os.path.join(OPTIONS.input_tmp, "RADIO", img_name)
664    assert os.path.exists(img_radio_path), \
665        "Failed to find %s at %s" % (img_name, img_radio_path)
666
667    if output_zip:
668      common.ZipWrite(output_zip, img_radio_path, "IMAGES/" + img_name)
669    else:
670      shutil.copy(img_radio_path, prebuilt_path)
671
672
673def AddSuperEmpty(output_zip):
674  """Create a super_empty.img and store it in output_zip."""
675
676  img = OutputFile(output_zip, OPTIONS.input_tmp, "IMAGES", "super_empty.img")
677  build_super_image.BuildSuperImage(OPTIONS.info_dict, img.name)
678  img.Write()
679
680
681def AddSuperSplit(output_zip):
682  """Create split super_*.img and store it in output_zip."""
683
684  outdir = os.path.join(OPTIONS.input_tmp, "OTA")
685  built = build_super_image.BuildSuperImage(OPTIONS.input_tmp, outdir)
686
687  if built:
688    for dev in OPTIONS.info_dict['super_block_devices'].strip().split():
689      img = OutputFile(output_zip, OPTIONS.input_tmp, "OTA",
690                       "super_" + dev + ".img")
691      img.Write()
692
693
694def ReplaceUpdatedFiles(zip_filename, files_list):
695  """Updates all the ZIP entries listed in files_list.
696
697  For now the list includes META/care_map.pb, and the related files under
698  SYSTEM/ after rebuilding recovery.
699  """
700  common.ZipDelete(zip_filename, files_list)
701  output_zip = zipfile.ZipFile(zip_filename, "a",
702                               compression=zipfile.ZIP_DEFLATED,
703                               allowZip64=True)
704  for item in files_list:
705    file_path = os.path.join(OPTIONS.input_tmp, item)
706    assert os.path.exists(file_path)
707    common.ZipWrite(output_zip, file_path, arcname=item)
708  common.ZipClose(output_zip)
709
710
711def AddImagesToTargetFiles(filename):
712  """Creates and adds images (boot/recovery/system/...) to a target_files.zip.
713
714  It works with either a zip file (zip mode), or a directory that contains the
715  files to be packed into a target_files.zip (dir mode). The latter is used when
716  being called from build/make/core/Makefile.
717
718  The images will be created under IMAGES/ in the input target_files.zip.
719
720  Args:
721    filename: the target_files.zip, or the zip root directory.
722  """
723  if os.path.isdir(filename):
724    OPTIONS.input_tmp = os.path.abspath(filename)
725  else:
726    OPTIONS.input_tmp = common.UnzipTemp(filename)
727
728  if not OPTIONS.add_missing:
729    if os.path.isdir(os.path.join(OPTIONS.input_tmp, "IMAGES")):
730      logger.warning("target_files appears to already contain images.")
731      sys.exit(1)
732
733  OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.input_tmp, repacking=True)
734
735  has_recovery = OPTIONS.info_dict.get("no_recovery") != "true"
736  has_boot = OPTIONS.info_dict.get("no_boot") != "true"
737  has_vendor_boot = OPTIONS.info_dict.get("vendor_boot") == "true"
738
739  # {vendor,odm,product,system_ext}.img are unlike system.img or
740  # system_other.img. Because it could be built from source, or dropped into
741  # target_files.zip as a prebuilt blob. We consider either of them as
742  # {vendor,product,system_ext}.img being available, which could be
743  # used when generating vbmeta.img for AVB.
744  has_vendor = (os.path.isdir(os.path.join(OPTIONS.input_tmp, "VENDOR")) or
745                os.path.exists(os.path.join(OPTIONS.input_tmp, "IMAGES",
746                                            "vendor.img")))
747  has_odm = (os.path.isdir(os.path.join(OPTIONS.input_tmp, "ODM")) or
748             os.path.exists(os.path.join(OPTIONS.input_tmp, "IMAGES",
749                                         "odm.img")))
750  has_product = (os.path.isdir(os.path.join(OPTIONS.input_tmp, "PRODUCT")) or
751                 os.path.exists(os.path.join(OPTIONS.input_tmp, "IMAGES",
752                                             "product.img")))
753  has_system_ext = (os.path.isdir(os.path.join(OPTIONS.input_tmp,
754                                               "SYSTEM_EXT")) or
755                    os.path.exists(os.path.join(OPTIONS.input_tmp,
756                                                "IMAGES",
757                                                "system_ext.img")))
758  has_system = os.path.isdir(os.path.join(OPTIONS.input_tmp, "SYSTEM"))
759  has_system_other = os.path.isdir(os.path.join(OPTIONS.input_tmp,
760                                                "SYSTEM_OTHER"))
761
762  # Set up the output destination. It writes to the given directory for dir
763  # mode; otherwise appends to the given ZIP.
764  if os.path.isdir(filename):
765    output_zip = None
766  else:
767    output_zip = zipfile.ZipFile(filename, "a",
768                                 compression=zipfile.ZIP_DEFLATED,
769                                 allowZip64=True)
770
771  # Always make input_tmp/IMAGES available, since we may stage boot / recovery
772  # images there even under zip mode. The directory will be cleaned up as part
773  # of OPTIONS.input_tmp.
774  images_dir = os.path.join(OPTIONS.input_tmp, "IMAGES")
775  if not os.path.isdir(images_dir):
776    os.makedirs(images_dir)
777
778  # A map between partition names and their paths, which could be used when
779  # generating AVB vbmeta image.
780  partitions = {}
781
782  def banner(s):
783    logger.info("\n\n++++ %s  ++++\n\n", s)
784
785  boot_image = None
786  if has_boot:
787    banner("boot")
788    boot_images = OPTIONS.info_dict.get("boot_images")
789    if boot_images is None:
790      boot_images = "boot.img"
791    for index,b in enumerate(boot_images.split()):
792      # common.GetBootableImage() returns the image directly if present.
793      boot_image = common.GetBootableImage(
794          "IMAGES/" + b, b, OPTIONS.input_tmp, "BOOT")
795      # boot.img may be unavailable in some targets (e.g. aosp_arm64).
796      if boot_image:
797        boot_image_path = os.path.join(OPTIONS.input_tmp, "IMAGES", b)
798        # Although multiple boot images can be generated, include the image
799        # descriptor of only the first boot image in vbmeta
800        if index == 0:
801          partitions['boot'] = boot_image_path
802        if not os.path.exists(boot_image_path):
803          boot_image.WriteToDir(OPTIONS.input_tmp)
804          if output_zip:
805            boot_image.AddToZip(output_zip)
806
807  if has_vendor_boot:
808    banner("vendor_boot")
809    vendor_boot_image = common.GetVendorBootImage(
810        "IMAGES/vendor_boot.img", "vendor_boot.img", OPTIONS.input_tmp,
811        "VENDOR_BOOT")
812    if vendor_boot_image:
813      partitions['vendor_boot'] = os.path.join(OPTIONS.input_tmp, "IMAGES",
814                                               "vendor_boot.img")
815      if not os.path.exists(partitions['vendor_boot']):
816        vendor_boot_image.WriteToDir(OPTIONS.input_tmp)
817        if output_zip:
818          vendor_boot_image.AddToZip(output_zip)
819
820  recovery_image = None
821  if has_recovery:
822    banner("recovery")
823    recovery_image = common.GetBootableImage(
824        "IMAGES/recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
825    assert recovery_image, "Failed to create recovery.img."
826    partitions['recovery'] = os.path.join(
827        OPTIONS.input_tmp, "IMAGES", "recovery.img")
828    if not os.path.exists(partitions['recovery']):
829      recovery_image.WriteToDir(OPTIONS.input_tmp)
830      if output_zip:
831        recovery_image.AddToZip(output_zip)
832
833      banner("recovery (two-step image)")
834      # The special recovery.img for two-step package use.
835      recovery_two_step_image = common.GetBootableImage(
836          "OTA/recovery-two-step.img", "recovery-two-step.img",
837          OPTIONS.input_tmp, "RECOVERY", two_step_image=True)
838      assert recovery_two_step_image, "Failed to create recovery-two-step.img."
839      recovery_two_step_image_path = os.path.join(
840          OPTIONS.input_tmp, "OTA", "recovery-two-step.img")
841      if not os.path.exists(recovery_two_step_image_path):
842        recovery_two_step_image.WriteToDir(OPTIONS.input_tmp)
843        if output_zip:
844          recovery_two_step_image.AddToZip(output_zip)
845
846  if has_system:
847    banner("system")
848    partitions['system'] = AddSystem(
849        output_zip, recovery_img=recovery_image, boot_img=boot_image)
850
851  if has_vendor:
852    banner("vendor")
853    partitions['vendor'] = AddVendor(
854        output_zip, recovery_img=recovery_image, boot_img=boot_image)
855
856  if has_product:
857    banner("product")
858    partitions['product'] = AddProduct(output_zip)
859
860  if has_system_ext:
861    banner("system_ext")
862    partitions['system_ext'] = AddSystemExt(output_zip)
863
864  if has_odm:
865    banner("odm")
866    partitions['odm'] = AddOdm(output_zip)
867
868  if has_system_other:
869    banner("system_other")
870    AddSystemOther(output_zip)
871
872  if not OPTIONS.is_signing:
873    banner("userdata")
874    AddUserdata(output_zip)
875    banner("cache")
876    AddCache(output_zip)
877
878  if OPTIONS.info_dict.get("board_bpt_enable") == "true":
879    banner("partition-table")
880    AddPartitionTable(output_zip)
881
882  if OPTIONS.info_dict.get("has_dtbo") == "true":
883    banner("dtbo")
884    partitions['dtbo'] = AddDtbo(output_zip)
885
886  # Custom images.
887  custom_partitions = OPTIONS.info_dict.get(
888      "avb_custom_images_partition_list", "").strip().split()
889  for partition_name in custom_partitions:
890    partition_name = partition_name.strip()
891    banner("custom images for " + partition_name)
892    partitions[partition_name] = AddCustomImages(output_zip, partition_name)
893
894  if OPTIONS.info_dict.get("avb_enable") == "true":
895    # vbmeta_partitions includes the partitions that should be included into
896    # top-level vbmeta.img, which are the ones that are not included in any
897    # chained VBMeta image plus the chained VBMeta images themselves.
898    # Currently custom_partitions are all chained to VBMeta image.
899    vbmeta_partitions = common.AVB_PARTITIONS[:] + tuple(custom_partitions)
900
901    vbmeta_system = OPTIONS.info_dict.get("avb_vbmeta_system", "").strip()
902    if vbmeta_system:
903      banner("vbmeta_system")
904      partitions["vbmeta_system"] = AddVBMeta(
905          output_zip, partitions, "vbmeta_system", vbmeta_system.split())
906      vbmeta_partitions = [
907          item for item in vbmeta_partitions
908          if item not in vbmeta_system.split()]
909      vbmeta_partitions.append("vbmeta_system")
910
911    vbmeta_vendor = OPTIONS.info_dict.get("avb_vbmeta_vendor", "").strip()
912    if vbmeta_vendor:
913      banner("vbmeta_vendor")
914      partitions["vbmeta_vendor"] = AddVBMeta(
915          output_zip, partitions, "vbmeta_vendor", vbmeta_vendor.split())
916      vbmeta_partitions = [
917          item for item in vbmeta_partitions
918          if item not in vbmeta_vendor.split()]
919      vbmeta_partitions.append("vbmeta_vendor")
920
921    banner("vbmeta")
922    AddVBMeta(output_zip, partitions, "vbmeta", vbmeta_partitions)
923
924  if OPTIONS.info_dict.get("use_dynamic_partitions") == "true":
925    banner("super_empty")
926    AddSuperEmpty(output_zip)
927
928  if OPTIONS.info_dict.get("build_super_partition") == "true":
929    if OPTIONS.info_dict.get(
930        "build_retrofit_dynamic_partitions_ota_package") == "true":
931      banner("super split images")
932      AddSuperSplit(output_zip)
933
934  banner("radio")
935  ab_partitions_txt = os.path.join(OPTIONS.input_tmp, "META",
936                                   "ab_partitions.txt")
937  if os.path.exists(ab_partitions_txt):
938    with open(ab_partitions_txt) as f:
939      ab_partitions = f.readlines()
940
941    # For devices using A/B update, make sure we have all the needed images
942    # ready under IMAGES/ or RADIO/.
943    CheckAbOtaImages(output_zip, ab_partitions)
944
945    # Generate care_map.pb for ab_partitions, then write this file to
946    # target_files package.
947    AddCareMapForAbOta(output_zip, ab_partitions, partitions)
948
949  # Radio images that need to be packed into IMAGES/, and product-img.zip.
950  pack_radioimages_txt = os.path.join(
951      OPTIONS.input_tmp, "META", "pack_radioimages.txt")
952  if os.path.exists(pack_radioimages_txt):
953    with open(pack_radioimages_txt) as f:
954      AddPackRadioImages(output_zip, f.readlines())
955
956  if output_zip:
957    common.ZipClose(output_zip)
958    if OPTIONS.replace_updated_files_list:
959      ReplaceUpdatedFiles(output_zip.filename,
960                          OPTIONS.replace_updated_files_list)
961
962
963def main(argv):
964  def option_handler(o, a):
965    if o in ("-a", "--add_missing"):
966      OPTIONS.add_missing = True
967    elif o in ("-r", "--rebuild_recovery",):
968      OPTIONS.rebuild_recovery = True
969    elif o == "--replace_verity_private_key":
970      OPTIONS.replace_verity_private_key = (True, a)
971    elif o == "--replace_verity_public_key":
972      OPTIONS.replace_verity_public_key = (True, a)
973    elif o == "--is_signing":
974      OPTIONS.is_signing = True
975    else:
976      return False
977    return True
978
979  args = common.ParseOptions(
980      argv, __doc__, extra_opts="ar",
981      extra_long_opts=["add_missing", "rebuild_recovery",
982                       "replace_verity_public_key=",
983                       "replace_verity_private_key=",
984                       "is_signing"],
985      extra_option_handler=option_handler)
986
987  if len(args) != 1:
988    common.Usage(__doc__)
989    sys.exit(1)
990
991  common.InitLogging()
992
993  AddImagesToTargetFiles(args[0])
994  logger.info("done.")
995
996if __name__ == '__main__':
997  try:
998    common.CloseInheritedPipes()
999    main(sys.argv[1:])
1000  except common.ExternalError:
1001    logger.exception("\n   ERROR:\n")
1002    sys.exit(1)
1003  finally:
1004    common.Cleanup()
1005