• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2#
3# Copyright (C) 2008 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, produces an OTA package that installs that build.
19An incremental OTA is produced if -i is given, otherwise a full OTA is produced.
20
21Usage:  ota_from_target_files [options] input_target_files output_ota_package
22
23Common options that apply to both of non-A/B and A/B OTAs
24
25  --downgrade
26      Intentionally generate an incremental OTA that updates from a newer build
27      to an older one (e.g. downgrading from P preview back to O MR1).
28      "ota-downgrade=yes" will be set in the package metadata file. A data wipe
29      will always be enforced when using this flag, so "ota-wipe=yes" will also
30      be included in the metadata file. The update-binary in the source build
31      will be used in the OTA package, unless --binary flag is specified. Please
32      also check the comment for --override_timestamp below.
33
34  -i  (--incremental_from) <file>
35      Generate an incremental OTA using the given target-files zip as the
36      starting build.
37
38  -k  (--package_key) <key>
39      Key to use to sign the package (default is the value of
40      default_system_dev_certificate from the input target-files's
41      META/misc_info.txt, or "build/make/target/product/security/testkey" if
42      that value is not specified).
43
44      For incremental OTAs, the default value is based on the source
45      target-file, not the target build.
46
47  --override_timestamp
48      Intentionally generate an incremental OTA that updates from a newer build
49      to an older one (based on timestamp comparison), by setting the downgrade
50      flag in the package metadata. This differs from --downgrade flag, as we
51      don't enforce a data wipe with this flag. Because we know for sure this is
52      NOT an actual downgrade case, but two builds happen to be cut in a reverse
53      order (e.g. from two branches). A legit use case is that we cut a new
54      build C (after having A and B), but want to enfore an update path of A ->
55      C -> B. Specifying --downgrade may not help since that would enforce a
56      data wipe for C -> B update.
57
58      We used to set a fake timestamp in the package metadata for this flow. But
59      now we consolidate the two cases (i.e. an actual downgrade, or a downgrade
60      based on timestamp) with the same "ota-downgrade=yes" flag, with the
61      difference being whether "ota-wipe=yes" is set.
62
63  --wipe_user_data
64      Generate an OTA package that will wipe the user data partition when
65      installed.
66
67  --retrofit_dynamic_partitions
68      Generates an OTA package that updates a device to support dynamic
69      partitions (default False). This flag is implied when generating
70      an incremental OTA where the base build does not support dynamic
71      partitions but the target build does. For A/B, when this flag is set,
72      --skip_postinstall is implied.
73
74  --skip_compatibility_check
75      Skip checking compatibility of the input target files package.
76
77  --output_metadata_path
78      Write a copy of the metadata to a separate file. Therefore, users can
79      read the post build fingerprint without extracting the OTA package.
80
81  --force_non_ab
82      This flag can only be set on an A/B device that also supports non-A/B
83      updates. Implies --two_step.
84      If set, generate that non-A/B update package.
85      If not set, generates A/B package for A/B device and non-A/B package for
86      non-A/B device.
87
88  -o  (--oem_settings) <main_file[,additional_files...]>
89      Comma separated list of files used to specify the expected OEM-specific
90      properties on the OEM partition of the intended device. Multiple expected
91      values can be used by providing multiple files. Only the first dict will
92      be used to compute fingerprint, while the rest will be used to assert
93      OEM-specific properties.
94
95Non-A/B OTA specific options
96
97  -b  (--binary) <file>
98      Use the given binary as the update-binary in the output package, instead
99      of the binary in the build's target_files. Use for development only.
100
101  --block
102      Generate a block-based OTA for non-A/B device. We have deprecated the
103      support for file-based OTA since O. Block-based OTA will be used by
104      default for all non-A/B devices. Keeping this flag here to not break
105      existing callers.
106
107  -e  (--extra_script) <file>
108      Insert the contents of file at the end of the update script.
109
110  --full_bootloader
111      Similar to --full_radio. When generating an incremental OTA, always
112      include a full copy of bootloader image.
113
114  --full_radio
115      When generating an incremental OTA, always include a full copy of radio
116      image. This option is only meaningful when -i is specified, because a full
117      radio is always included in a full OTA if applicable.
118
119  --log_diff <file>
120      Generate a log file that shows the differences in the source and target
121      builds for an incremental package. This option is only meaningful when -i
122      is specified.
123
124  --oem_no_mount
125      For devices with OEM-specific properties but without an OEM partition, do
126      not mount the OEM partition in the updater-script. This should be very
127      rarely used, since it's expected to have a dedicated OEM partition for
128      OEM-specific properties. Only meaningful when -o is specified.
129
130  --stash_threshold <float>
131      Specify the threshold that will be used to compute the maximum allowed
132      stash size (defaults to 0.8).
133
134  -t  (--worker_threads) <int>
135      Specify the number of worker-threads that will be used when generating
136      patches for incremental updates (defaults to 3).
137
138  --verify
139      Verify the checksums of the updated system and vendor (if any) partitions.
140      Non-A/B incremental OTAs only.
141
142  -2  (--two_step)
143      Generate a 'two-step' OTA package, where recovery is updated first, so
144      that any changes made to the system partition are done using the new
145      recovery (new kernel, etc.).
146
147A/B OTA specific options
148
149  --disable_fec_computation
150      Disable the on device FEC data computation for incremental updates.
151
152  --include_secondary
153      Additionally include the payload for secondary slot images (default:
154      False). Only meaningful when generating A/B OTAs.
155
156      By default, an A/B OTA package doesn't contain the images for the
157      secondary slot (e.g. system_other.img). Specifying this flag allows
158      generating a separate payload that will install secondary slot images.
159
160      Such a package needs to be applied in a two-stage manner, with a reboot
161      in-between. During the first stage, the updater applies the primary
162      payload only. Upon finishing, it reboots the device into the newly updated
163      slot. It then continues to install the secondary payload to the inactive
164      slot, but without switching the active slot at the end (needs the matching
165      support in update_engine, i.e. SWITCH_SLOT_ON_REBOOT flag).
166
167      Due to the special install procedure, the secondary payload will be always
168      generated as a full payload.
169
170  --payload_signer <signer>
171      Specify the signer when signing the payload and metadata for A/B OTAs.
172      By default (i.e. without this flag), it calls 'openssl pkeyutl' to sign
173      with the package private key. If the private key cannot be accessed
174      directly, a payload signer that knows how to do that should be specified.
175      The signer will be supplied with "-inkey <path_to_key>",
176      "-in <input_file>" and "-out <output_file>" parameters.
177
178  --payload_signer_args <args>
179      Specify the arguments needed for payload signer.
180
181  --payload_signer_maximum_signature_size <signature_size>
182      The maximum signature size (in bytes) that would be generated by the given
183      payload signer. Only meaningful when custom payload signer is specified
184      via '--payload_signer'.
185      If the signer uses a RSA key, this should be the number of bytes to
186      represent the modulus. If it uses an EC key, this is the size of a
187      DER-encoded ECDSA signature.
188
189  --payload_signer_key_size <key_size>
190      Deprecated. Use the '--payload_signer_maximum_signature_size' instead.
191
192  --boot_variable_file <path>
193      A file that contains the possible values of ro.boot.* properties. It's
194      used to calculate the possible runtime fingerprints when some
195      ro.product.* properties are overridden by the 'import' statement.
196      The file expects one property per line, and each line has the following
197      format: 'prop_name=value1,value2'. e.g. 'ro.boot.product.sku=std,pro'
198
199  --skip_postinstall
200      Skip the postinstall hooks when generating an A/B OTA package (default:
201      False). Note that this discards ALL the hooks, including non-optional
202      ones. Should only be used if caller knows it's safe to do so (e.g. all the
203      postinstall work is to dexopt apps and a data wipe will happen immediately
204      after). Only meaningful when generating A/B OTAs.
205
206  --partial "<PARTITION> [<PARTITION>[...]]"
207      Generate partial updates, overriding ab_partitions list with the given
208      list. Specify --partial= without partition list to let tooling auto detect
209      partial partition list.
210
211  --custom_image <custom_partition=custom_image>
212      Use the specified custom_image to update custom_partition when generating
213      an A/B OTA package. e.g. "--custom_image oem=oem.img --custom_image
214      cus=cus_test.img"
215
216  --disable_vabc
217      Disable Virtual A/B Compression, for builds that have compression enabled
218      by default.
219
220  --vabc_downgrade
221      Don't disable Virtual A/B Compression for downgrading OTAs.
222      For VABC downgrades, we must finish merging before doing data wipe, and
223      since data wipe is required for downgrading OTA, this might cause long
224      wait time in recovery.
225
226  --enable_vabc_xor
227      Enable the VABC xor feature. Will reduce space requirements for OTA
228
229  --force_minor_version
230      Override the update_engine minor version for delta generation.
231
232  --compressor_types
233      A colon ':' separated list of compressors. Allowed values are bz2 and brotli.
234
235  --enable_zucchini
236      Whether to enable to zucchini feature. Will generate smaller OTA but uses more memory.
237
238  --enable_lz4diff
239      Whether to enable lz4diff feature. Will generate smaller OTA for EROFS but
240      uses more memory.
241
242  --spl_downgrade
243      Force generate an SPL downgrade OTA. Only needed if target build has an
244      older SPL.
245
246  --vabc_compression_param
247      Compression algorithm to be used for VABC. Available options: gz, brotli, none
248
249  --security_patch_level
250      Override the security patch level in target files
251
252  --max_threads
253      Specify max number of threads allowed when generating A/B OTA
254"""
255
256from __future__ import print_function
257
258import logging
259import multiprocessing
260import os
261import os.path
262import re
263import shlex
264import shutil
265import subprocess
266import sys
267import zipfile
268
269import care_map_pb2
270import common
271import ota_utils
272from ota_utils import (UNZIP_PATTERN, FinalizeMetadata, GetPackageMetadata,
273                       PayloadGenerator, SECURITY_PATCH_LEVEL_PROP_NAME, CopyTargetFilesDir, VABC_COMPRESSION_PARAM_SUPPORT)
274from common import DoesInputFileContain, IsSparseImage
275import target_files_diff
276from check_target_files_vintf import CheckVintfIfTrebleEnabled
277from non_ab_ota import GenerateNonAbOtaPackage
278from payload_signer import PayloadSigner
279
280if sys.hexversion < 0x02070000:
281  print("Python 2.7 or newer is required.", file=sys.stderr)
282  sys.exit(1)
283
284logger = logging.getLogger(__name__)
285
286OPTIONS = ota_utils.OPTIONS
287OPTIONS.verify = False
288OPTIONS.patch_threshold = 0.95
289OPTIONS.wipe_user_data = False
290OPTIONS.extra_script = None
291OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
292if OPTIONS.worker_threads == 0:
293  OPTIONS.worker_threads = 1
294OPTIONS.two_step = False
295OPTIONS.include_secondary = False
296OPTIONS.block_based = True
297OPTIONS.updater_binary = None
298OPTIONS.oem_dicts = None
299OPTIONS.oem_source = None
300OPTIONS.oem_no_mount = False
301OPTIONS.full_radio = False
302OPTIONS.full_bootloader = False
303# Stash size cannot exceed cache_size * threshold.
304OPTIONS.cache_size = None
305OPTIONS.stash_threshold = 0.8
306OPTIONS.log_diff = None
307OPTIONS.payload_signer = None
308OPTIONS.payload_signer_args = []
309OPTIONS.payload_signer_maximum_signature_size = None
310OPTIONS.extracted_input = None
311OPTIONS.skip_postinstall = False
312OPTIONS.skip_compatibility_check = False
313OPTIONS.disable_fec_computation = False
314OPTIONS.disable_verity_computation = False
315OPTIONS.partial = None
316OPTIONS.custom_images = {}
317OPTIONS.disable_vabc = False
318OPTIONS.spl_downgrade = False
319OPTIONS.vabc_downgrade = False
320OPTIONS.enable_vabc_xor = True
321OPTIONS.force_minor_version = None
322OPTIONS.compressor_types = None
323OPTIONS.enable_zucchini = True
324OPTIONS.enable_lz4diff = False
325OPTIONS.vabc_compression_param = None
326OPTIONS.security_patch_level = None
327OPTIONS.max_threads = None
328
329
330POSTINSTALL_CONFIG = 'META/postinstall_config.txt'
331DYNAMIC_PARTITION_INFO = 'META/dynamic_partitions_info.txt'
332AB_PARTITIONS = 'META/ab_partitions.txt'
333
334# Files to be unzipped for target diffing purpose.
335TARGET_DIFFING_UNZIP_PATTERN = ['BOOT', 'RECOVERY', 'SYSTEM/*', 'VENDOR/*',
336                                'PRODUCT/*', 'SYSTEM_EXT/*', 'ODM/*',
337                                'VENDOR_DLKM/*', 'ODM_DLKM/*', 'SYSTEM_DLKM/*']
338RETROFIT_DAP_UNZIP_PATTERN = ['OTA/super_*.img', AB_PARTITIONS]
339
340# Images to be excluded from secondary payload. We essentially only keep
341# 'system_other' and bootloader partitions.
342SECONDARY_PAYLOAD_SKIPPED_IMAGES = [
343    'boot', 'dtbo', 'modem', 'odm', 'odm_dlkm', 'product', 'radio', 'recovery',
344    'system_dlkm', 'system_ext', 'vbmeta', 'vbmeta_system', 'vbmeta_vendor',
345    'vendor', 'vendor_boot']
346
347
348def _LoadOemDicts(oem_source):
349  """Returns the list of loaded OEM properties dict."""
350  if not oem_source:
351    return None
352
353  oem_dicts = []
354  for oem_file in oem_source:
355    oem_dicts.append(common.LoadDictionaryFromFile(oem_file))
356  return oem_dicts
357
358
359def ModifyVABCCompressionParam(content, algo):
360  """ Update update VABC Compression Param in dynamic_partitions_info.txt
361  Args:
362    content: The string content of dynamic_partitions_info.txt
363    algo: The compression algorithm should be used for VABC. See
364          https://cs.android.com/android/platform/superproject/+/master:system/core/fs_mgr/libsnapshot/cow_writer.cpp;l=127;bpv=1;bpt=1?q=CowWriter::ParseOptions&sq=
365  Returns:
366    Updated content of dynamic_partitions_info.txt , with custom compression algo
367  """
368  output_list = []
369  for line in content.splitlines():
370    if line.startswith("virtual_ab_compression_method="):
371      continue
372    output_list.append(line)
373  output_list.append("virtual_ab_compression_method="+algo)
374  return "\n".join(output_list)
375
376
377def UpdatesInfoForSpecialUpdates(content, partitions_filter,
378                                 delete_keys=None):
379  """ Updates info file for secondary payload generation, partial update, etc.
380
381    Scan each line in the info file, and remove the unwanted partitions from
382    the dynamic partition list in the related properties. e.g.
383    "super_google_dynamic_partitions_partition_list=system vendor product"
384    will become "super_google_dynamic_partitions_partition_list=system".
385
386  Args:
387    content: The content of the input info file. e.g. misc_info.txt.
388    partitions_filter: A function to filter the desired partitions from a given
389      list
390    delete_keys: A list of keys to delete in the info file
391
392  Returns:
393    A string of the updated info content.
394  """
395
396  output_list = []
397  # The suffix in partition_list variables that follows the name of the
398  # partition group.
399  list_suffix = 'partition_list'
400  for line in content.splitlines():
401    if line.startswith('#') or '=' not in line:
402      output_list.append(line)
403      continue
404    key, value = line.strip().split('=', 1)
405
406    if delete_keys and key in delete_keys:
407      pass
408    elif key.endswith(list_suffix):
409      partitions = value.split()
410      # TODO for partial update, partitions in the same group must be all
411      # updated or all omitted
412      partitions = filter(partitions_filter, partitions)
413      output_list.append('{}={}'.format(key, ' '.join(partitions)))
414    else:
415      output_list.append(line)
416  return '\n'.join(output_list)
417
418
419def GetTargetFilesZipForSecondaryImages(input_file, skip_postinstall=False):
420  """Returns a target-files.zip file for generating secondary payload.
421
422  Although the original target-files.zip already contains secondary slot
423  images (i.e. IMAGES/system_other.img), we need to rename the files to the
424  ones without _other suffix. Note that we cannot instead modify the names in
425  META/ab_partitions.txt, because there are no matching partitions on device.
426
427  For the partitions that don't have secondary images, the ones for primary
428  slot will be used. This is to ensure that we always have valid boot, vbmeta,
429  bootloader images in the inactive slot.
430
431  After writing system_other to inactive slot's system partiiton,
432  PackageManagerService will read `ro.cp_system_other_odex`, and set
433  `sys.cppreopt` to "requested". Then, according to
434  system/extras/cppreopts/cppreopts.rc , init will mount system_other at
435  /postinstall, and execute `cppreopts` to copy optimized APKs from
436  /postinstall to /data .
437
438  Args:
439    input_file: The input target-files.zip file.
440    skip_postinstall: Whether to skip copying the postinstall config file.
441
442  Returns:
443    The filename of the target-files.zip for generating secondary payload.
444  """
445
446  def GetInfoForSecondaryImages(info_file):
447    """Updates info file for secondary payload generation."""
448    with open(info_file) as f:
449      content = f.read()
450    # Remove virtual_ab flag from secondary payload so that OTA client
451    # don't use snapshots for secondary update
452    delete_keys = ['virtual_ab', "virtual_ab_retrofit"]
453    return UpdatesInfoForSpecialUpdates(
454        content, lambda p: p not in SECONDARY_PAYLOAD_SKIPPED_IMAGES,
455        delete_keys)
456
457  target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
458  target_zip = zipfile.ZipFile(target_file, 'w', allowZip64=True)
459
460  with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
461    infolist = input_zip.infolist()
462
463  input_tmp = common.UnzipTemp(input_file, UNZIP_PATTERN)
464  for info in infolist:
465    unzipped_file = os.path.join(input_tmp, *info.filename.split('/'))
466    if info.filename == 'IMAGES/system_other.img':
467      common.ZipWrite(target_zip, unzipped_file, arcname='IMAGES/system.img')
468
469    # Primary images and friends need to be skipped explicitly.
470    elif info.filename in ('IMAGES/system.img',
471                           'IMAGES/system.map'):
472      pass
473
474    # Copy images that are not in SECONDARY_PAYLOAD_SKIPPED_IMAGES.
475    elif info.filename.startswith(('IMAGES/', 'RADIO/')):
476      image_name = os.path.basename(info.filename)
477      if image_name not in ['{}.img'.format(partition) for partition in
478                            SECONDARY_PAYLOAD_SKIPPED_IMAGES]:
479        common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
480
481    # Skip copying the postinstall config if requested.
482    elif skip_postinstall and info.filename == POSTINSTALL_CONFIG:
483      pass
484
485    elif info.filename.startswith('META/'):
486      # Remove the unnecessary partitions for secondary images from the
487      # ab_partitions file.
488      if info.filename == AB_PARTITIONS:
489        with open(unzipped_file) as f:
490          partition_list = f.read().splitlines()
491        partition_list = [partition for partition in partition_list if partition
492                          and partition not in SECONDARY_PAYLOAD_SKIPPED_IMAGES]
493        common.ZipWriteStr(target_zip, info.filename,
494                           '\n'.join(partition_list))
495      # Remove the unnecessary partitions from the dynamic partitions list.
496      elif (info.filename == 'META/misc_info.txt' or
497            info.filename == DYNAMIC_PARTITION_INFO):
498        modified_info = GetInfoForSecondaryImages(unzipped_file)
499        common.ZipWriteStr(target_zip, info.filename, modified_info)
500      else:
501        common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
502
503  common.ZipClose(target_zip)
504
505  return target_file
506
507
508def GetTargetFilesZipWithoutPostinstallConfig(input_file):
509  """Returns a target-files.zip that's not containing postinstall_config.txt.
510
511  This allows brillo_update_payload script to skip writing all the postinstall
512  hooks in the generated payload. The input target-files.zip file will be
513  duplicated, with 'META/postinstall_config.txt' skipped. If input_file doesn't
514  contain the postinstall_config.txt entry, the input file will be returned.
515
516  Args:
517    input_file: The input target-files.zip filename.
518
519  Returns:
520    The filename of target-files.zip that doesn't contain postinstall config.
521  """
522  # We should only make a copy if postinstall_config entry exists.
523  with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
524    if POSTINSTALL_CONFIG not in input_zip.namelist():
525      return input_file
526
527  target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
528  shutil.copyfile(input_file, target_file)
529  common.ZipDelete(target_file, POSTINSTALL_CONFIG)
530  return target_file
531
532
533def ParseInfoDict(target_file_path):
534  with zipfile.ZipFile(target_file_path, 'r', allowZip64=True) as zfp:
535    return common.LoadInfoDict(zfp)
536
537
538def GetTargetFilesZipForCustomVABCCompression(input_file, vabc_compression_param):
539  """Returns a target-files.zip with a custom VABC compression param.
540  Args:
541    input_file: The input target-files.zip path
542    vabc_compression_param: Custom Virtual AB Compression algorithm
543
544  Returns:
545    The path to modified target-files.zip
546  """
547  target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
548  shutil.copyfile(input_file, target_file)
549  common.ZipDelete(target_file, DYNAMIC_PARTITION_INFO)
550  with zipfile.ZipFile(input_file, 'r', allowZip64=True) as zfp:
551    dynamic_partition_info = zfp.read(DYNAMIC_PARTITION_INFO).decode()
552    dynamic_partition_info = ModifyVABCCompressionParam(
553        dynamic_partition_info, vabc_compression_param)
554    with zipfile.ZipFile(target_file, "a", allowZip64=True) as output_zip:
555      output_zip.writestr(DYNAMIC_PARTITION_INFO, dynamic_partition_info)
556  return target_file
557
558
559def GetTargetFilesZipForPartialUpdates(input_file, ab_partitions):
560  """Returns a target-files.zip for partial ota update package generation.
561
562  This function modifies ab_partitions list with the desired partitions before
563  calling the brillo_update_payload script. It also cleans up the reference to
564  the excluded partitions in the info file, e.g misc_info.txt.
565
566  Args:
567    input_file: The input target-files.zip filename.
568    ab_partitions: A list of partitions to include in the partial update
569
570  Returns:
571    The filename of target-files.zip used for partial ota update.
572  """
573
574  def AddImageForPartition(partition_name):
575    """Add the archive name for a given partition to the copy list."""
576    for prefix in ['IMAGES', 'RADIO']:
577      image_path = '{}/{}.img'.format(prefix, partition_name)
578      if image_path in namelist:
579        copy_entries.append(image_path)
580        map_path = '{}/{}.map'.format(prefix, partition_name)
581        if map_path in namelist:
582          copy_entries.append(map_path)
583        return
584
585    raise ValueError("Cannot find {} in input zipfile".format(partition_name))
586
587  with zipfile.ZipFile(input_file, allowZip64=True) as input_zip:
588    original_ab_partitions = input_zip.read(
589        AB_PARTITIONS).decode().splitlines()
590    namelist = input_zip.namelist()
591
592  unrecognized_partitions = [partition for partition in ab_partitions if
593                             partition not in original_ab_partitions]
594  if unrecognized_partitions:
595    raise ValueError("Unrecognized partitions when generating partial updates",
596                     unrecognized_partitions)
597
598  logger.info("Generating partial updates for %s", ab_partitions)
599
600  copy_entries = ['META/update_engine_config.txt']
601  for partition_name in ab_partitions:
602    AddImageForPartition(partition_name)
603
604  # Use zip2zip to avoid extracting the zipfile.
605  partial_target_file = common.MakeTempFile(suffix='.zip')
606  cmd = ['zip2zip', '-i', input_file, '-o', partial_target_file]
607  cmd.extend(['{}:{}'.format(name, name) for name in copy_entries])
608  common.RunAndCheckOutput(cmd)
609
610  partial_target_zip = zipfile.ZipFile(partial_target_file, 'a',
611                                       allowZip64=True)
612  with zipfile.ZipFile(input_file, allowZip64=True) as input_zip:
613    common.ZipWriteStr(partial_target_zip, 'META/ab_partitions.txt',
614                       '\n'.join(ab_partitions))
615    CARE_MAP_ENTRY = "META/care_map.pb"
616    if CARE_MAP_ENTRY in input_zip.namelist():
617      caremap = care_map_pb2.CareMap()
618      caremap.ParseFromString(input_zip.read(CARE_MAP_ENTRY))
619      filtered = [
620          part for part in caremap.partitions if part.name in ab_partitions]
621      del caremap.partitions[:]
622      caremap.partitions.extend(filtered)
623      common.ZipWriteStr(partial_target_zip, CARE_MAP_ENTRY,
624                         caremap.SerializeToString())
625
626    for info_file in ['META/misc_info.txt', DYNAMIC_PARTITION_INFO]:
627      if info_file not in input_zip.namelist():
628        logger.warning('Cannot find %s in input zipfile', info_file)
629        continue
630      content = input_zip.read(info_file).decode()
631      modified_info = UpdatesInfoForSpecialUpdates(
632          content, lambda p: p in ab_partitions)
633      if OPTIONS.vabc_compression_param and info_file == DYNAMIC_PARTITION_INFO:
634        modified_info = ModifyVABCCompressionParam(
635            modified_info, OPTIONS.vabc_compression_param)
636      common.ZipWriteStr(partial_target_zip, info_file, modified_info)
637
638    # TODO(xunchang) handle META/postinstall_config.txt'
639
640  common.ZipClose(partial_target_zip)
641
642  return partial_target_file
643
644
645def GetTargetFilesZipForRetrofitDynamicPartitions(input_file,
646                                                  super_block_devices,
647                                                  dynamic_partition_list):
648  """Returns a target-files.zip for retrofitting dynamic partitions.
649
650  This allows brillo_update_payload to generate an OTA based on the exact
651  bits on the block devices. Postinstall is disabled.
652
653  Args:
654    input_file: The input target-files.zip filename.
655    super_block_devices: The list of super block devices
656    dynamic_partition_list: The list of dynamic partitions
657
658  Returns:
659    The filename of target-files.zip with *.img replaced with super_*.img for
660    each block device in super_block_devices.
661  """
662  assert super_block_devices, "No super_block_devices are specified."
663
664  replace = {'OTA/super_{}.img'.format(dev): 'IMAGES/{}.img'.format(dev)
665             for dev in super_block_devices}
666
667  target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
668  shutil.copyfile(input_file, target_file)
669
670  with zipfile.ZipFile(input_file, allowZip64=True) as input_zip:
671    namelist = input_zip.namelist()
672
673  input_tmp = common.UnzipTemp(input_file, RETROFIT_DAP_UNZIP_PATTERN)
674
675  # Remove partitions from META/ab_partitions.txt that is in
676  # dynamic_partition_list but not in super_block_devices so that
677  # brillo_update_payload won't generate update for those logical partitions.
678  ab_partitions_file = os.path.join(input_tmp, *AB_PARTITIONS.split('/'))
679  with open(ab_partitions_file) as f:
680    ab_partitions_lines = f.readlines()
681    ab_partitions = [line.strip() for line in ab_partitions_lines]
682  # Assert that all super_block_devices are in ab_partitions
683  super_device_not_updated = [partition for partition in super_block_devices
684                              if partition not in ab_partitions]
685  assert not super_device_not_updated, \
686      "{} is in super_block_devices but not in {}".format(
687          super_device_not_updated, AB_PARTITIONS)
688  # ab_partitions -= (dynamic_partition_list - super_block_devices)
689  new_ab_partitions = common.MakeTempFile(
690      prefix="ab_partitions", suffix=".txt")
691  with open(new_ab_partitions, 'w') as f:
692    for partition in ab_partitions:
693      if (partition in dynamic_partition_list and
694              partition not in super_block_devices):
695        logger.info("Dropping %s from ab_partitions.txt", partition)
696        continue
697      f.write(partition + "\n")
698  to_delete = [AB_PARTITIONS]
699
700  # Always skip postinstall for a retrofit update.
701  to_delete += [POSTINSTALL_CONFIG]
702
703  # Delete dynamic_partitions_info.txt so that brillo_update_payload thinks this
704  # is a regular update on devices without dynamic partitions support.
705  to_delete += [DYNAMIC_PARTITION_INFO]
706
707  # Remove the existing partition images as well as the map files.
708  to_delete += list(replace.values())
709  to_delete += ['IMAGES/{}.map'.format(dev) for dev in super_block_devices]
710
711  common.ZipDelete(target_file, to_delete)
712
713  target_zip = zipfile.ZipFile(target_file, 'a', allowZip64=True)
714
715  # Write super_{foo}.img as {foo}.img.
716  for src, dst in replace.items():
717    assert src in namelist, \
718        'Missing {} in {}; {} cannot be written'.format(src, input_file, dst)
719    unzipped_file = os.path.join(input_tmp, *src.split('/'))
720    common.ZipWrite(target_zip, unzipped_file, arcname=dst)
721
722  # Write new ab_partitions.txt file
723  common.ZipWrite(target_zip, new_ab_partitions, arcname=AB_PARTITIONS)
724
725  common.ZipClose(target_zip)
726
727  return target_file
728
729
730def GetTargetFilesZipForCustomImagesUpdates(input_file, custom_images):
731  """Returns a target-files.zip for custom partitions update.
732
733  This function modifies ab_partitions list with the desired custom partitions
734  and puts the custom images into the target target-files.zip.
735
736  Args:
737    input_file: The input target-files.zip filename.
738    custom_images: A map of custom partitions and custom images.
739
740  Returns:
741    The filename of a target-files.zip which has renamed the custom images in
742    the IMAGES/ to their partition names.
743  """
744
745  # First pass: use zip2zip to copy the target files contents, excluding
746  # the "custom" images that will be replaced.
747  target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
748  cmd = ['zip2zip', '-i', input_file, '-o', target_file]
749
750  images = {}
751  for custom_partition, custom_image in custom_images.items():
752    default_custom_image = '{}.img'.format(custom_partition)
753    if default_custom_image != custom_image:
754      src = 'IMAGES/' + custom_image
755      dst = 'IMAGES/' + default_custom_image
756      cmd.extend(['-x', dst])
757      images[dst] = src
758
759  common.RunAndCheckOutput(cmd)
760
761  # Second pass: write {custom_image}.img as {custom_partition}.img.
762  with zipfile.ZipFile(input_file, allowZip64=True) as input_zip:
763    with zipfile.ZipFile(target_file, 'a', allowZip64=True) as output_zip:
764      for dst, src in images.items():
765        data = input_zip.read(src)
766        logger.info("Update custom partition '%s'", dst)
767        common.ZipWriteStr(output_zip, dst, data)
768      output_zip.close()
769
770  return target_file
771
772
773def GeneratePartitionTimestampFlags(partition_state):
774  partition_timestamps = [
775      part.partition_name + ":" + part.version
776      for part in partition_state]
777  return ["--partition_timestamps", ",".join(partition_timestamps)]
778
779
780def GeneratePartitionTimestampFlagsDowngrade(
781        pre_partition_state, post_partition_state):
782  assert pre_partition_state is not None
783  partition_timestamps = {}
784  for part in post_partition_state:
785    partition_timestamps[part.partition_name] = part.version
786  for part in pre_partition_state:
787    if part.partition_name in partition_timestamps:
788      partition_timestamps[part.partition_name] = \
789          max(part.version, partition_timestamps[part.partition_name])
790  return [
791      "--partition_timestamps",
792      ",".join([key + ":" + val for (key, val)
793                in partition_timestamps.items()])
794  ]
795
796
797def SupportsMainlineGkiUpdates(target_file):
798  """Return True if the build supports MainlineGKIUpdates.
799
800  This function scans the product.img file in IMAGES/ directory for
801  pattern |*/apex/com.android.gki.*.apex|. If there are files
802  matching this pattern, conclude that build supports mainline
803  GKI and return True
804
805  Args:
806    target_file: Path to a target_file.zip, or an extracted directory
807  Return:
808    True if thisb uild supports Mainline GKI Updates.
809  """
810  if target_file is None:
811    return False
812  if os.path.isfile(target_file):
813    target_file = common.UnzipTemp(target_file, ["IMAGES/product.img"])
814  if not os.path.isdir(target_file):
815    assert os.path.isdir(target_file), \
816        "{} must be a path to zip archive or dir containing extracted"\
817        " target_files".format(target_file)
818  image_file = os.path.join(target_file, "IMAGES", "product.img")
819
820  if not os.path.isfile(image_file):
821    return False
822
823  if IsSparseImage(image_file):
824    # Unsparse the image
825    tmp_img = common.MakeTempFile(suffix=".img")
826    subprocess.check_output(["simg2img", image_file, tmp_img])
827    image_file = tmp_img
828
829  cmd = ["debugfs_static", "-R", "ls -p /apex", image_file]
830  output = subprocess.check_output(cmd).decode()
831
832  pattern = re.compile(r"com\.android\.gki\..*\.apex")
833  return pattern.search(output) is not None
834
835
836def GenerateAbOtaPackage(target_file, output_file, source_file=None):
837  """Generates an Android OTA package that has A/B update payload."""
838  # If input target_files are directories, create a copy so that we can modify
839  # them directly
840  if os.path.isdir(target_file):
841    target_file = CopyTargetFilesDir(target_file)
842  if source_file is not None and os.path.isdir(source_file):
843    source_file = CopyTargetFilesDir(source_file)
844  # Stage the output zip package for package signing.
845  if not OPTIONS.no_signing:
846    staging_file = common.MakeTempFile(suffix='.zip')
847  else:
848    staging_file = output_file
849  output_zip = zipfile.ZipFile(staging_file, "w",
850                               compression=zipfile.ZIP_DEFLATED,
851                               allowZip64=True)
852
853  if source_file is not None:
854    source_file = ota_utils.ExtractTargetFiles(source_file)
855    assert "ab_partitions" in OPTIONS.source_info_dict, \
856        "META/ab_partitions.txt is required for ab_update."
857    assert "ab_partitions" in OPTIONS.target_info_dict, \
858        "META/ab_partitions.txt is required for ab_update."
859    target_info = common.BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
860    source_info = common.BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
861    # If source supports VABC, delta_generator/update_engine will attempt to
862    # use VABC. This dangerous, as the target build won't have snapuserd to
863    # serve I/O request when device boots. Therefore, disable VABC if source
864    # build doesn't supports it.
865    if not source_info.is_vabc or not target_info.is_vabc:
866      logger.info("Either source or target does not support VABC, disabling.")
867      OPTIONS.disable_vabc = True
868    if source_info.vabc_compression_param != target_info.vabc_compression_param:
869      logger.info("Source build and target build use different compression methods {} vs {}, default to source builds parameter {}".format(
870          source_info.vabc_compression_param, target_info.vabc_compression_param, source_info.vabc_compression_param))
871      OPTIONS.vabc_compression_param = source_info.vabc_compression_param
872
873    # Virtual AB Compression was introduced in Androd S.
874    # Later, we backported VABC to Android R. But verity support was not
875    # backported, so if VABC is used and we are on Android R, disable
876    # verity computation.
877    if not OPTIONS.disable_vabc and source_info.is_android_r:
878      OPTIONS.disable_verity_computation = True
879      OPTIONS.disable_fec_computation = True
880
881  else:
882    assert "ab_partitions" in OPTIONS.info_dict, \
883        "META/ab_partitions.txt is required for ab_update."
884    target_info = common.BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts)
885    source_info = None
886    if target_info.vabc_compression_param:
887      minimum_api_level_required = VABC_COMPRESSION_PARAM_SUPPORT[
888          target_info.vabc_compression_param]
889      if target_info.vendor_api_level < minimum_api_level_required:
890        logger.warning(
891            "This full OTA is configured to use VABC compression algorithm"
892            " {}, which is supported since"
893            " Android API level {}, but device is "
894            "launched with {} . If this full OTA is"
895            " served to a device running old build, OTA might fail due to "
896            "unsupported compression parameter. For safety, gz is used because "
897            "it's supported since day 1.".format(
898                target_info.vabc_compression_param,
899                minimum_api_level_required,
900                target_info.vendor_api_level))
901        OPTIONS.vabc_compression_param = "gz"
902
903  if OPTIONS.partial == []:
904    logger.info(
905        "Automatically detecting partial partition list from input target files.")
906    OPTIONS.partial = target_info.get(
907        "partial_ota_update_partitions_list").split()
908    assert OPTIONS.partial, "Input target_file does not have"
909    " partial_ota_update_partitions_list defined, failed to auto detect partial"
910    " partition list. Please specify list of partitions to update manually via"
911    " --partial=a,b,c , or generate a complete OTA by removing the --partial"
912    " option"
913    OPTIONS.partial.sort()
914    if source_info:
915      source_partial_list = source_info.get(
916          "partial_ota_update_partitions_list").split()
917      if source_partial_list:
918        source_partial_list.sort()
919        if source_partial_list != OPTIONS.partial:
920          logger.warning("Source build and target build have different partial partition lists. Source: %s, target: %s, taking the intersection.",
921                         source_partial_list, OPTIONS.partial)
922          OPTIONS.partial = list(
923              set(OPTIONS.partial) and set(source_partial_list))
924          OPTIONS.partial.sort()
925    logger.info("Automatically deduced partial partition list: %s",
926                OPTIONS.partial)
927
928  if target_info.vendor_suppressed_vabc:
929    logger.info("Vendor suppressed VABC. Disabling")
930    OPTIONS.disable_vabc = True
931
932  # Both source and target build need to support VABC XOR for us to use it.
933  # Source build's update_engine must be able to write XOR ops, and target
934  # build's snapuserd must be able to interpret XOR ops.
935  if not target_info.is_vabc_xor or OPTIONS.disable_vabc or \
936          (source_info is not None and not source_info.is_vabc_xor):
937    logger.info("VABC XOR Not supported, disabling")
938    OPTIONS.enable_vabc_xor = False
939
940  if OPTIONS.vabc_compression_param == "none":
941    logger.info(
942        "VABC Compression algorithm is set to 'none', disabling VABC xor")
943    OPTIONS.enable_vabc_xor = False
944
945  if OPTIONS.enable_vabc_xor:
946    api_level = -1
947    if source_info is not None:
948      api_level = source_info.vendor_api_level
949    if api_level == -1:
950      api_level = target_info.vendor_api_level
951
952    # XOR is only supported on T and higher.
953    if api_level < 33:
954      logger.error("VABC XOR not supported on this vendor, disabling")
955      OPTIONS.enable_vabc_xor = False
956
957  additional_args = []
958
959  # Prepare custom images.
960  if OPTIONS.custom_images:
961    target_file = GetTargetFilesZipForCustomImagesUpdates(
962        target_file, OPTIONS.custom_images)
963
964  if OPTIONS.retrofit_dynamic_partitions:
965    target_file = GetTargetFilesZipForRetrofitDynamicPartitions(
966        target_file, target_info.get("super_block_devices").strip().split(),
967        target_info.get("dynamic_partition_list").strip().split())
968  elif OPTIONS.partial:
969    target_file = GetTargetFilesZipForPartialUpdates(target_file,
970                                                     OPTIONS.partial)
971  elif OPTIONS.vabc_compression_param:
972    target_file = GetTargetFilesZipForCustomVABCCompression(
973        target_file, OPTIONS.vabc_compression_param)
974  elif OPTIONS.skip_postinstall:
975    target_file = GetTargetFilesZipWithoutPostinstallConfig(target_file)
976  # Target_file may have been modified, reparse ab_partitions
977  target_info.info_dict['ab_partitions'] = common.ReadFromInputFile(target_file,
978                                                                    AB_PARTITIONS).strip().split("\n")
979
980  CheckVintfIfTrebleEnabled(target_file, target_info)
981
982  # Metadata to comply with Android OTA package format.
983  metadata = GetPackageMetadata(target_info, source_info)
984  # Generate payload.
985  payload = PayloadGenerator(
986      wipe_user_data=OPTIONS.wipe_user_data, minor_version=OPTIONS.force_minor_version, is_partial_update=OPTIONS.partial)
987
988  partition_timestamps_flags = []
989  # Enforce a max timestamp this payload can be applied on top of.
990  if OPTIONS.downgrade:
991    max_timestamp = source_info.GetBuildProp("ro.build.date.utc")
992    partition_timestamps_flags = GeneratePartitionTimestampFlagsDowngrade(
993        metadata.precondition.partition_state,
994        metadata.postcondition.partition_state
995    )
996  else:
997    max_timestamp = str(metadata.postcondition.timestamp)
998    partition_timestamps_flags = GeneratePartitionTimestampFlags(
999        metadata.postcondition.partition_state)
1000
1001  if not ota_utils.IsZucchiniCompatible(source_file, target_file):
1002    logger.warning(
1003        "Builds doesn't support zucchini, or source/target don't have compatible zucchini versions. Disabling zucchini.")
1004    OPTIONS.enable_zucchini = False
1005
1006  security_patch_level = target_info.GetBuildProp(
1007      "ro.build.version.security_patch")
1008  if OPTIONS.security_patch_level is not None:
1009    security_patch_level = OPTIONS.security_patch_level
1010
1011  additional_args += ["--security_patch_level", security_patch_level]
1012
1013  if OPTIONS.max_threads:
1014    additional_args += ["--max_threads", OPTIONS.max_threads]
1015
1016  additional_args += ["--enable_zucchini=" +
1017                      str(OPTIONS.enable_zucchini).lower()]
1018
1019  if not ota_utils.IsLz4diffCompatible(source_file, target_file):
1020    logger.warning(
1021        "Source build doesn't support lz4diff, or source/target don't have compatible lz4diff versions. Disabling lz4diff.")
1022    OPTIONS.enable_lz4diff = False
1023
1024  additional_args += ["--enable_lz4diff=" +
1025                      str(OPTIONS.enable_lz4diff).lower()]
1026
1027  if source_file and OPTIONS.enable_lz4diff:
1028    input_tmp = common.UnzipTemp(source_file, ["META/liblz4.so"])
1029    liblz4_path = os.path.join(input_tmp, "META", "liblz4.so")
1030    assert os.path.exists(
1031        liblz4_path), "liblz4.so not found in META/ dir of target file {}".format(liblz4_path)
1032    logger.info("Enabling lz4diff %s", liblz4_path)
1033    additional_args += ["--liblz4_path", liblz4_path]
1034    erofs_compression_param = OPTIONS.target_info_dict.get(
1035        "erofs_default_compressor")
1036    assert erofs_compression_param is not None, "'erofs_default_compressor' not found in META/misc_info.txt of target build. This is required to enable lz4diff."
1037    additional_args += ["--erofs_compression_param", erofs_compression_param]
1038
1039  if OPTIONS.disable_vabc:
1040    additional_args += ["--disable_vabc=true"]
1041  if OPTIONS.enable_vabc_xor:
1042    additional_args += ["--enable_vabc_xor=true"]
1043  if OPTIONS.compressor_types:
1044    additional_args += ["--compressor_types", OPTIONS.compressor_types]
1045  additional_args += ["--max_timestamp", max_timestamp]
1046
1047  payload.Generate(
1048      target_file,
1049      source_file,
1050      additional_args + partition_timestamps_flags
1051  )
1052
1053  # Sign the payload.
1054  pw = OPTIONS.key_passwords[OPTIONS.package_key]
1055  payload_signer = PayloadSigner(
1056      OPTIONS.package_key, OPTIONS.private_key_suffix,
1057      pw, OPTIONS.payload_signer)
1058  payload.Sign(payload_signer)
1059
1060  # Write the payload into output zip.
1061  payload.WriteToZip(output_zip)
1062
1063  # Generate and include the secondary payload that installs secondary images
1064  # (e.g. system_other.img).
1065  if OPTIONS.include_secondary:
1066    # We always include a full payload for the secondary slot, even when
1067    # building an incremental OTA. See the comments for "--include_secondary".
1068    secondary_target_file = GetTargetFilesZipForSecondaryImages(
1069        target_file, OPTIONS.skip_postinstall)
1070    secondary_payload = PayloadGenerator(secondary=True)
1071    secondary_payload.Generate(secondary_target_file,
1072                               additional_args=["--max_timestamp",
1073                                                max_timestamp])
1074    secondary_payload.Sign(payload_signer)
1075    secondary_payload.WriteToZip(output_zip)
1076
1077  # If dm-verity is supported for the device, copy contents of care_map
1078  # into A/B OTA package.
1079  if target_info.get("avb_enable") == "true":
1080    # Adds care_map if either the protobuf format or the plain text one exists.
1081    for care_map_name in ["care_map.pb", "care_map.txt"]:
1082      if not DoesInputFileContain(target_file, "META/" + care_map_name):
1083        continue
1084      care_map_data = common.ReadBytesFromInputFile(
1085          target_file, "META/" + care_map_name)
1086      # In order to support streaming, care_map needs to be packed as
1087      # ZIP_STORED.
1088      common.ZipWriteStr(output_zip, care_map_name, care_map_data,
1089                         compress_type=zipfile.ZIP_STORED)
1090    else:
1091      logger.warning("Cannot find care map file in target_file package")
1092
1093  # Add the source apex version for incremental ota updates, and write the
1094  # result apex info to the ota package.
1095  ota_apex_info = ota_utils.ConstructOtaApexInfo(target_file, source_file)
1096  if ota_apex_info is not None:
1097    common.ZipWriteStr(output_zip, "apex_info.pb", ota_apex_info,
1098                       compress_type=zipfile.ZIP_STORED)
1099
1100  # We haven't written the metadata entry yet, which will be handled in
1101  # FinalizeMetadata().
1102  common.ZipClose(output_zip)
1103
1104  FinalizeMetadata(metadata, staging_file, output_file,
1105                   package_key=OPTIONS.package_key)
1106
1107
1108def main(argv):
1109
1110  def option_handler(o, a):
1111    if o in ("-k", "--package_key"):
1112      OPTIONS.package_key = a
1113    elif o in ("-i", "--incremental_from"):
1114      OPTIONS.incremental_source = a
1115    elif o == "--full_radio":
1116      OPTIONS.full_radio = True
1117    elif o == "--full_bootloader":
1118      OPTIONS.full_bootloader = True
1119    elif o == "--wipe_user_data":
1120      OPTIONS.wipe_user_data = True
1121    elif o == "--downgrade":
1122      OPTIONS.downgrade = True
1123      OPTIONS.wipe_user_data = True
1124    elif o == "--override_timestamp":
1125      OPTIONS.downgrade = True
1126    elif o in ("-o", "--oem_settings"):
1127      OPTIONS.oem_source = a.split(',')
1128    elif o == "--oem_no_mount":
1129      OPTIONS.oem_no_mount = True
1130    elif o in ("-e", "--extra_script"):
1131      OPTIONS.extra_script = a
1132    elif o in ("-t", "--worker_threads"):
1133      if a.isdigit():
1134        OPTIONS.worker_threads = int(a)
1135      else:
1136        raise ValueError("Cannot parse value %r for option %r - only "
1137                         "integers are allowed." % (a, o))
1138    elif o in ("-2", "--two_step"):
1139      OPTIONS.two_step = True
1140    elif o == "--include_secondary":
1141      OPTIONS.include_secondary = True
1142    elif o == "--no_signing":
1143      OPTIONS.no_signing = True
1144    elif o == "--verify":
1145      OPTIONS.verify = True
1146    elif o == "--block":
1147      OPTIONS.block_based = True
1148    elif o in ("-b", "--binary"):
1149      OPTIONS.updater_binary = a
1150    elif o == "--stash_threshold":
1151      try:
1152        OPTIONS.stash_threshold = float(a)
1153      except ValueError:
1154        raise ValueError("Cannot parse value %r for option %r - expecting "
1155                         "a float" % (a, o))
1156    elif o == "--log_diff":
1157      OPTIONS.log_diff = a
1158    elif o == "--payload_signer":
1159      OPTIONS.payload_signer = a
1160    elif o == "--payload_signer_args":
1161      OPTIONS.payload_signer_args = shlex.split(a)
1162    elif o == "--payload_signer_maximum_signature_size":
1163      OPTIONS.payload_signer_maximum_signature_size = a
1164    elif o == "--payload_signer_key_size":
1165      # TODO(Xunchang) remove this option after cleaning up the callers.
1166      logger.warning("The option '--payload_signer_key_size' is deprecated."
1167                     " Use '--payload_signer_maximum_signature_size' instead.")
1168      OPTIONS.payload_signer_maximum_signature_size = a
1169    elif o == "--extracted_input_target_files":
1170      OPTIONS.extracted_input = a
1171    elif o == "--skip_postinstall":
1172      OPTIONS.skip_postinstall = True
1173    elif o == "--retrofit_dynamic_partitions":
1174      OPTIONS.retrofit_dynamic_partitions = True
1175    elif o == "--skip_compatibility_check":
1176      OPTIONS.skip_compatibility_check = True
1177    elif o == "--output_metadata_path":
1178      OPTIONS.output_metadata_path = a
1179    elif o == "--disable_fec_computation":
1180      OPTIONS.disable_fec_computation = True
1181    elif o == "--disable_verity_computation":
1182      OPTIONS.disable_verity_computation = True
1183    elif o == "--force_non_ab":
1184      OPTIONS.force_non_ab = True
1185    elif o == "--boot_variable_file":
1186      OPTIONS.boot_variable_file = a
1187    elif o == "--partial":
1188      if a:
1189        partitions = a.split()
1190        if not partitions:
1191          raise ValueError("Cannot parse partitions in {}".format(a))
1192      else:
1193        partitions = []
1194      OPTIONS.partial = partitions
1195    elif o == "--custom_image":
1196      custom_partition, custom_image = a.split("=")
1197      OPTIONS.custom_images[custom_partition] = custom_image
1198    elif o == "--disable_vabc":
1199      OPTIONS.disable_vabc = True
1200    elif o == "--spl_downgrade":
1201      OPTIONS.spl_downgrade = True
1202      OPTIONS.wipe_user_data = True
1203    elif o == "--vabc_downgrade":
1204      OPTIONS.vabc_downgrade = True
1205    elif o == "--enable_vabc_xor":
1206      assert a.lower() in ["true", "false"]
1207      OPTIONS.enable_vabc_xor = a.lower() != "false"
1208    elif o == "--force_minor_version":
1209      OPTIONS.force_minor_version = a
1210    elif o == "--compressor_types":
1211      OPTIONS.compressor_types = a
1212    elif o == "--enable_zucchini":
1213      assert a.lower() in ["true", "false"]
1214      OPTIONS.enable_zucchini = a.lower() != "false"
1215    elif o == "--enable_lz4diff":
1216      assert a.lower() in ["true", "false"]
1217      OPTIONS.enable_lz4diff = a.lower() != "false"
1218    elif o == "--vabc_compression_param":
1219      OPTIONS.vabc_compression_param = a.lower()
1220    elif o == "--security_patch_level":
1221      OPTIONS.security_patch_level = a
1222    elif o in ("--max_threads"):
1223      if a.isdigit():
1224        OPTIONS.max_threads = a
1225      else:
1226        raise ValueError("Cannot parse value %r for option %r - only "
1227                         "integers are allowed." % (a, o))
1228    else:
1229      return False
1230    return True
1231
1232  args = common.ParseOptions(argv, __doc__,
1233                             extra_opts="b:k:i:d:e:t:2o:",
1234                             extra_long_opts=[
1235                                 "package_key=",
1236                                 "incremental_from=",
1237                                 "full_radio",
1238                                 "full_bootloader",
1239                                 "wipe_user_data",
1240                                 "downgrade",
1241                                 "override_timestamp",
1242                                 "extra_script=",
1243                                 "worker_threads=",
1244                                 "two_step",
1245                                 "include_secondary",
1246                                 "no_signing",
1247                                 "block",
1248                                 "binary=",
1249                                 "oem_settings=",
1250                                 "oem_no_mount",
1251                                 "verify",
1252                                 "stash_threshold=",
1253                                 "log_diff=",
1254                                 "payload_signer=",
1255                                 "payload_signer_args=",
1256                                 "payload_signer_maximum_signature_size=",
1257                                 "payload_signer_key_size=",
1258                                 "extracted_input_target_files=",
1259                                 "skip_postinstall",
1260                                 "retrofit_dynamic_partitions",
1261                                 "skip_compatibility_check",
1262                                 "output_metadata_path=",
1263                                 "disable_fec_computation",
1264                                 "disable_verity_computation",
1265                                 "force_non_ab",
1266                                 "boot_variable_file=",
1267                                 "partial=",
1268                                 "custom_image=",
1269                                 "disable_vabc",
1270                                 "spl_downgrade",
1271                                 "vabc_downgrade",
1272                                 "enable_vabc_xor=",
1273                                 "force_minor_version=",
1274                                 "compressor_types=",
1275                                 "enable_zucchini=",
1276                                 "enable_lz4diff=",
1277                                 "vabc_compression_param=",
1278                                 "security_patch_level=",
1279                                 "max_threads=",
1280                             ], extra_option_handler=option_handler)
1281  common.InitLogging()
1282
1283  if len(args) != 2:
1284    common.Usage(__doc__)
1285    sys.exit(1)
1286
1287  # Load the build info dicts from the zip directly or the extracted input
1288  # directory. We don't need to unzip the entire target-files zips, because they
1289  # won't be needed for A/B OTAs (brillo_update_payload does that on its own).
1290  # When loading the info dicts, we don't need to provide the second parameter
1291  # to common.LoadInfoDict(). Specifying the second parameter allows replacing
1292  # some properties with their actual paths, such as 'selinux_fc',
1293  # 'ramdisk_dir', which won't be used during OTA generation.
1294  if OPTIONS.extracted_input is not None:
1295    OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input)
1296  else:
1297    OPTIONS.info_dict = common.LoadInfoDict(args[0])
1298
1299  if OPTIONS.wipe_user_data:
1300    if not OPTIONS.vabc_downgrade:
1301      logger.info("Detected downgrade/datawipe OTA."
1302                  "When wiping userdata, VABC OTA makes the user "
1303                  "wait in recovery mode for merge to finish. Disable VABC by "
1304                  "default. If you really want to do VABC downgrade, pass "
1305                  "--vabc_downgrade")
1306      OPTIONS.disable_vabc = True
1307    # We should only allow downgrading incrementals (as opposed to full).
1308    # Otherwise the device may go back from arbitrary build with this full
1309    # OTA package.
1310  if OPTIONS.incremental_source is None and OPTIONS.downgrade:
1311    raise ValueError("Cannot generate downgradable full OTAs")
1312
1313  # TODO(xunchang) for retrofit and partial updates, maybe we should rebuild the
1314  # target-file and reload the info_dict. So the info will be consistent with
1315  # the modified target-file.
1316
1317  logger.info("--- target info ---")
1318  common.DumpInfoDict(OPTIONS.info_dict)
1319
1320  # Load the source build dict if applicable.
1321  if OPTIONS.incremental_source is not None:
1322    OPTIONS.target_info_dict = OPTIONS.info_dict
1323    OPTIONS.source_info_dict = ParseInfoDict(OPTIONS.incremental_source)
1324
1325    logger.info("--- source info ---")
1326    common.DumpInfoDict(OPTIONS.source_info_dict)
1327
1328  if OPTIONS.partial:
1329    OPTIONS.info_dict['ab_partitions'] = \
1330        list(
1331        set(OPTIONS.info_dict['ab_partitions']) & set(OPTIONS.partial)
1332    )
1333    if OPTIONS.source_info_dict:
1334      OPTIONS.source_info_dict['ab_partitions'] = \
1335          list(
1336          set(OPTIONS.source_info_dict['ab_partitions']) &
1337          set(OPTIONS.partial)
1338      )
1339
1340  # Load OEM dicts if provided.
1341  OPTIONS.oem_dicts = _LoadOemDicts(OPTIONS.oem_source)
1342
1343  # Assume retrofitting dynamic partitions when base build does not set
1344  # use_dynamic_partitions but target build does.
1345  if (OPTIONS.source_info_dict and
1346      OPTIONS.source_info_dict.get("use_dynamic_partitions") != "true" and
1347          OPTIONS.target_info_dict.get("use_dynamic_partitions") == "true"):
1348    if OPTIONS.target_info_dict.get("dynamic_partition_retrofit") != "true":
1349      raise common.ExternalError(
1350          "Expect to generate incremental OTA for retrofitting dynamic "
1351          "partitions, but dynamic_partition_retrofit is not set in target "
1352          "build.")
1353    logger.info("Implicitly generating retrofit incremental OTA.")
1354    OPTIONS.retrofit_dynamic_partitions = True
1355
1356  # Skip postinstall for retrofitting dynamic partitions.
1357  if OPTIONS.retrofit_dynamic_partitions:
1358    OPTIONS.skip_postinstall = True
1359
1360  ab_update = OPTIONS.info_dict.get("ab_update") == "true"
1361  allow_non_ab = OPTIONS.info_dict.get("allow_non_ab") == "true"
1362  if OPTIONS.force_non_ab:
1363    assert allow_non_ab,\
1364        "--force_non_ab only allowed on devices that supports non-A/B"
1365    assert ab_update, "--force_non_ab only allowed on A/B devices"
1366
1367  generate_ab = not OPTIONS.force_non_ab and ab_update
1368
1369  # Use the default key to sign the package if not specified with package_key.
1370  # package_keys are needed on ab_updates, so always define them if an
1371  # A/B update is getting created.
1372  if not OPTIONS.no_signing or generate_ab:
1373    if OPTIONS.package_key is None:
1374      OPTIONS.package_key = OPTIONS.info_dict.get(
1375          "default_system_dev_certificate",
1376          "build/make/target/product/security/testkey")
1377    # Get signing keys
1378    OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
1379
1380    # Only check for existence of key file if using the default signer.
1381    # Because the custom signer might not need the key file AT all.
1382    # b/191704641
1383    if not OPTIONS.payload_signer:
1384      private_key_path = OPTIONS.package_key + OPTIONS.private_key_suffix
1385      if not os.path.exists(private_key_path):
1386        raise common.ExternalError(
1387            "Private key {} doesn't exist. Make sure you passed the"
1388            " correct key path through -k option".format(
1389                private_key_path)
1390        )
1391      signapk_abs_path = os.path.join(
1392          OPTIONS.search_path, OPTIONS.signapk_path)
1393      if not os.path.exists(signapk_abs_path):
1394        raise common.ExternalError(
1395            "Failed to find sign apk binary {} in search path {}. Make sure the correct search path is passed via -p".format(OPTIONS.signapk_path, OPTIONS.search_path))
1396
1397  if OPTIONS.source_info_dict:
1398    source_build_prop = OPTIONS.source_info_dict["build.prop"]
1399    target_build_prop = OPTIONS.target_info_dict["build.prop"]
1400    source_spl = source_build_prop.GetProp(SECURITY_PATCH_LEVEL_PROP_NAME)
1401    target_spl = target_build_prop.GetProp(SECURITY_PATCH_LEVEL_PROP_NAME)
1402    is_spl_downgrade = target_spl < source_spl
1403    if is_spl_downgrade and target_build_prop.GetProp("ro.build.tags") == "release-keys":
1404      raise common.ExternalError(
1405          "Target security patch level {} is older than source SPL {} "
1406          "A locked bootloader will reject SPL downgrade no matter "
1407          "what(even if data wipe is done), so SPL downgrade on any "
1408          "release-keys build is not allowed.".format(target_spl, source_spl))
1409
1410    logger.info("SPL downgrade on %s",
1411                target_build_prop.GetProp("ro.build.tags"))
1412    if is_spl_downgrade and not OPTIONS.spl_downgrade and not OPTIONS.downgrade:
1413      raise common.ExternalError(
1414          "Target security patch level {} is older than source SPL {} applying "
1415          "such OTA will likely cause device fail to boot. Pass --spl_downgrade "
1416          "to override this check. This script expects security patch level to "
1417          "be in format yyyy-mm-dd (e.x. 2021-02-05). It's possible to use "
1418          "separators other than -, so as long as it's used consistenly across "
1419          "all SPL dates".format(target_spl, source_spl))
1420    elif not is_spl_downgrade and OPTIONS.spl_downgrade:
1421      raise ValueError("--spl_downgrade specified but no actual SPL downgrade"
1422                       " detected. Please only pass in this flag if you want a"
1423                       " SPL downgrade. Target SPL: {} Source SPL: {}"
1424                       .format(target_spl, source_spl))
1425  if generate_ab:
1426    GenerateAbOtaPackage(
1427        target_file=args[0],
1428        output_file=args[1],
1429        source_file=OPTIONS.incremental_source)
1430
1431  else:
1432    GenerateNonAbOtaPackage(
1433        target_file=args[0],
1434        output_file=args[1],
1435        source_file=OPTIONS.incremental_source)
1436
1437  # Post OTA generation works.
1438  if OPTIONS.incremental_source is not None and OPTIONS.log_diff:
1439    logger.info("Generating diff logs...")
1440    logger.info("Unzipping target-files for diffing...")
1441    target_dir = common.UnzipTemp(args[0], TARGET_DIFFING_UNZIP_PATTERN)
1442    source_dir = common.UnzipTemp(
1443        OPTIONS.incremental_source, TARGET_DIFFING_UNZIP_PATTERN)
1444
1445    with open(OPTIONS.log_diff, 'w') as out_file:
1446      target_files_diff.recursiveDiff(
1447          '', source_dir, target_dir, out_file)
1448
1449  logger.info("done.")
1450
1451
1452if __name__ == '__main__':
1453  try:
1454    common.CloseInheritedPipes()
1455    main(sys.argv[1:])
1456  finally:
1457    common.Cleanup()
1458