Home | History | Annotate | Download | only in releasetools
      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 """
     18 Given a target-files zipfile, produces an OTA package that installs
     19 that build.  An incremental OTA is produced if -i is given, otherwise
     20 a full OTA is produced.
     21 
     22 Usage:  ota_from_target_files [flags] input_target_files output_ota_package
     23 
     24   --board_config  <file>
     25       Deprecated.
     26 
     27   -k (--package_key) <key> Key to use to sign the package (default is
     28       the value of default_system_dev_certificate from the input
     29       target-files's META/misc_info.txt, or
     30       "build/target/product/security/testkey" if that value is not
     31       specified).
     32 
     33       For incremental OTAs, the default value is based on the source
     34       target-file, not the target build.
     35 
     36   -i  (--incremental_from)  <file>
     37       Generate an incremental OTA using the given target-files zip as
     38       the starting build.
     39 
     40   --full_radio
     41       When generating an incremental OTA, always include a full copy of
     42       radio image. This option is only meaningful when -i is specified,
     43       because a full radio is always included in a full OTA if applicable.
     44 
     45   --full_bootloader
     46       Similar to --full_radio. When generating an incremental OTA, always
     47       include a full copy of bootloader image.
     48 
     49   -v  (--verify)
     50       Remount and verify the checksums of the files written to the
     51       system and vendor (if used) partitions.  Incremental builds only.
     52 
     53   -o  (--oem_settings)  <main_file[,additional_files...]>
     54       Comma seperated list of files used to specify the expected OEM-specific
     55       properties on the OEM partition of the intended device.
     56       Multiple expected values can be used by providing multiple files.
     57 
     58   --oem_no_mount
     59       For devices with OEM-specific properties but without an OEM partition,
     60       do not mount the OEM partition in the updater-script. This should be
     61       very rarely used, since it's expected to have a dedicated OEM partition
     62       for OEM-specific properties. Only meaningful when -o is specified.
     63 
     64   -w  (--wipe_user_data)
     65       Generate an OTA package that will wipe the user data partition
     66       when installed.
     67 
     68   --downgrade
     69       Intentionally generate an incremental OTA that updates from a newer
     70       build to an older one (based on timestamp comparison). "post-timestamp"
     71       will be replaced by "ota-downgrade=yes" in the metadata file. A data
     72       wipe will always be enforced, so "ota-wipe=yes" will also be included in
     73       the metadata file. The update-binary in the source build will be used in
     74       the OTA package, unless --binary flag is specified. Please also check the
     75       doc for --override_timestamp below.
     76 
     77   --override_timestamp
     78       Intentionally generate an incremental OTA that updates from a newer
     79       build to an older one (based on timestamp comparison), by overriding the
     80       timestamp in package metadata. This differs from --downgrade flag: we
     81       know for sure this is NOT an actual downgrade case, but two builds are
     82       cut in a reverse order. A legit use case is that we cut a new build C
     83       (after having A and B), but want to enfore an update path of A -> C -> B.
     84       Specifying --downgrade may not help since that would enforce a data wipe
     85       for C -> B update. The value of "post-timestamp" will be set to the newer
     86       timestamp plus one, so that the package can be pushed and applied.
     87 
     88   -e  (--extra_script)  <file>
     89       Insert the contents of file at the end of the update script.
     90 
     91   -2  (--two_step)
     92       Generate a 'two-step' OTA package, where recovery is updated
     93       first, so that any changes made to the system partition are done
     94       using the new recovery (new kernel, etc.).
     95 
     96   --block
     97       Generate a block-based OTA for non-A/B device. We have deprecated the
     98       support for file-based OTA since O. Block-based OTA will be used by
     99       default for all non-A/B devices. Keeping this flag here to not break
    100       existing callers.
    101 
    102   -b  (--binary)  <file>
    103       Use the given binary as the update-binary in the output package,
    104       instead of the binary in the build's target_files.  Use for
    105       development only.
    106 
    107   -t  (--worker_threads) <int>
    108       Specifies the number of worker-threads that will be used when
    109       generating patches for incremental updates (defaults to 3).
    110 
    111   --stash_threshold <float>
    112       Specifies the threshold that will be used to compute the maximum
    113       allowed stash size (defaults to 0.8).
    114 
    115   --gen_verify
    116       Generate an OTA package that verifies the partitions.
    117 
    118   --log_diff <file>
    119       Generate a log file that shows the differences in the source and target
    120       builds for an incremental package. This option is only meaningful when
    121       -i is specified.
    122 
    123   --payload_signer <signer>
    124       Specify the signer when signing the payload and metadata for A/B OTAs.
    125       By default (i.e. without this flag), it calls 'openssl pkeyutl' to sign
    126       with the package private key. If the private key cannot be accessed
    127       directly, a payload signer that knows how to do that should be specified.
    128       The signer will be supplied with "-inkey <path_to_key>",
    129       "-in <input_file>" and "-out <output_file>" parameters.
    130 
    131   --payload_signer_args <args>
    132       Specify the arguments needed for payload signer.
    133 """
    134 
    135 from __future__ import print_function
    136 
    137 import sys
    138 
    139 if sys.hexversion < 0x02070000:
    140   print("Python 2.7 or newer is required.", file=sys.stderr)
    141   sys.exit(1)
    142 
    143 import copy
    144 import multiprocessing
    145 import os.path
    146 import subprocess
    147 import shlex
    148 import tempfile
    149 import zipfile
    150 
    151 import common
    152 import edify_generator
    153 import sparse_img
    154 
    155 OPTIONS = common.OPTIONS
    156 OPTIONS.package_key = None
    157 OPTIONS.incremental_source = None
    158 OPTIONS.verify = False
    159 OPTIONS.patch_threshold = 0.95
    160 OPTIONS.wipe_user_data = False
    161 OPTIONS.downgrade = False
    162 OPTIONS.timestamp = False
    163 OPTIONS.extra_script = None
    164 OPTIONS.worker_threads = multiprocessing.cpu_count() // 2
    165 if OPTIONS.worker_threads == 0:
    166   OPTIONS.worker_threads = 1
    167 OPTIONS.two_step = False
    168 OPTIONS.no_signing = False
    169 OPTIONS.block_based = True
    170 OPTIONS.updater_binary = None
    171 OPTIONS.oem_source = None
    172 OPTIONS.oem_no_mount = False
    173 OPTIONS.fallback_to_full = True
    174 OPTIONS.full_radio = False
    175 OPTIONS.full_bootloader = False
    176 # Stash size cannot exceed cache_size * threshold.
    177 OPTIONS.cache_size = None
    178 OPTIONS.stash_threshold = 0.8
    179 OPTIONS.gen_verify = False
    180 OPTIONS.log_diff = None
    181 OPTIONS.payload_signer = None
    182 OPTIONS.payload_signer_args = []
    183 OPTIONS.extracted_input = None
    184 
    185 METADATA_NAME = 'META-INF/com/android/metadata'
    186 UNZIP_PATTERN = ['IMAGES/*', 'META/*']
    187 
    188 
    189 def SignOutput(temp_zip_name, output_zip_name):
    190   key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
    191   pw = key_passwords[OPTIONS.package_key]
    192 
    193   common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw,
    194                   whole_file=True)
    195 
    196 
    197 def AppendAssertions(script, info_dict, oem_dicts=None):
    198   oem_props = info_dict.get("oem_fingerprint_properties")
    199   if not oem_props:
    200     device = GetBuildProp("ro.product.device", info_dict)
    201     script.AssertDevice(device)
    202   else:
    203     if not oem_dicts:
    204       raise common.ExternalError(
    205           "No OEM file provided to answer expected assertions")
    206     for prop in oem_props.split():
    207       values = []
    208       for oem_dict in oem_dicts:
    209         if oem_dict.get(prop):
    210           values.append(oem_dict[prop])
    211       if not values:
    212         raise common.ExternalError(
    213             "The OEM file is missing the property %s" % prop)
    214       script.AssertOemProperty(prop, values)
    215 
    216 
    217 def _LoadOemDicts(script, recovery_mount_options=None):
    218   """Returns the list of loaded OEM properties dict."""
    219   oem_dicts = None
    220   if OPTIONS.oem_source is None:
    221     raise common.ExternalError("OEM source required for this build")
    222   if not OPTIONS.oem_no_mount and script:
    223     script.Mount("/oem", recovery_mount_options)
    224   oem_dicts = []
    225   for oem_file in OPTIONS.oem_source:
    226     oem_dicts.append(common.LoadDictionaryFromLines(
    227         open(oem_file).readlines()))
    228   return oem_dicts
    229 
    230 
    231 def _WriteRecoveryImageToBoot(script, output_zip):
    232   """Find and write recovery image to /boot in two-step OTA.
    233 
    234   In two-step OTAs, we write recovery image to /boot as the first step so that
    235   we can reboot to there and install a new recovery image to /recovery.
    236   A special "recovery-two-step.img" will be preferred, which encodes the correct
    237   path of "/boot". Otherwise the device may show "device is corrupt" message
    238   when booting into /boot.
    239 
    240   Fall back to using the regular recovery.img if the two-step recovery image
    241   doesn't exist. Note that rebuilding the special image at this point may be
    242   infeasible, because we don't have the desired boot signer and keys when
    243   calling ota_from_target_files.py.
    244   """
    245 
    246   recovery_two_step_img_name = "recovery-two-step.img"
    247   recovery_two_step_img_path = os.path.join(
    248       OPTIONS.input_tmp, "IMAGES", recovery_two_step_img_name)
    249   if os.path.exists(recovery_two_step_img_path):
    250     recovery_two_step_img = common.GetBootableImage(
    251         recovery_two_step_img_name, recovery_two_step_img_name,
    252         OPTIONS.input_tmp, "RECOVERY")
    253     common.ZipWriteStr(
    254         output_zip, recovery_two_step_img_name, recovery_two_step_img.data)
    255     print("two-step package: using %s in stage 1/3" % (
    256         recovery_two_step_img_name,))
    257     script.WriteRawImage("/boot", recovery_two_step_img_name)
    258   else:
    259     print("two-step package: using recovery.img in stage 1/3")
    260     # The "recovery.img" entry has been written into package earlier.
    261     script.WriteRawImage("/boot", "recovery.img")
    262 
    263 
    264 def HasRecoveryPatch(target_files_zip):
    265   namelist = [name for name in target_files_zip.namelist()]
    266   return ("SYSTEM/recovery-from-boot.p" in namelist or
    267           "SYSTEM/etc/recovery.img" in namelist)
    268 
    269 
    270 def HasVendorPartition(target_files_zip):
    271   try:
    272     target_files_zip.getinfo("VENDOR/")
    273     return True
    274   except KeyError:
    275     return False
    276 
    277 
    278 def GetOemProperty(name, oem_props, oem_dict, info_dict):
    279   if oem_props is not None and name in oem_props:
    280     return oem_dict[name]
    281   return GetBuildProp(name, info_dict)
    282 
    283 
    284 def CalculateFingerprint(oem_props, oem_dict, info_dict):
    285   if oem_props is None:
    286     return GetBuildProp("ro.build.fingerprint", info_dict)
    287   return "%s/%s/%s:%s" % (
    288       GetOemProperty("ro.product.brand", oem_props, oem_dict, info_dict),
    289       GetOemProperty("ro.product.name", oem_props, oem_dict, info_dict),
    290       GetOemProperty("ro.product.device", oem_props, oem_dict, info_dict),
    291       GetBuildProp("ro.build.thumbprint", info_dict))
    292 
    293 
    294 def GetImage(which, tmpdir):
    295   """Returns an image object suitable for passing to BlockImageDiff.
    296 
    297   'which' partition must be "system" or "vendor". A prebuilt image and file
    298   map must already exist in tmpdir.
    299   """
    300 
    301   assert which in ("system", "vendor")
    302 
    303   path = os.path.join(tmpdir, "IMAGES", which + ".img")
    304   mappath = os.path.join(tmpdir, "IMAGES", which + ".map")
    305 
    306   # The image and map files must have been created prior to calling
    307   # ota_from_target_files.py (since LMP).
    308   assert os.path.exists(path) and os.path.exists(mappath)
    309 
    310   # Bug: http://b/20939131
    311   # In ext4 filesystems, block 0 might be changed even being mounted
    312   # R/O. We add it to clobbered_blocks so that it will be written to the
    313   # target unconditionally. Note that they are still part of care_map.
    314   clobbered_blocks = "0"
    315 
    316   return sparse_img.SparseImage(path, mappath, clobbered_blocks)
    317 
    318 
    319 def AddCompatibilityArchive(target_zip, output_zip, system_included=True,
    320                             vendor_included=True):
    321   """Adds compatibility info from target files into the output zip.
    322 
    323   Metadata used for on-device compatibility verification is retrieved from
    324   target_zip then added to compatibility.zip which is added to the output_zip
    325   archive.
    326 
    327   Compatibility archive should only be included for devices with a vendor
    328   partition as checking provides value when system and vendor are independently
    329   versioned.
    330 
    331   Args:
    332     target_zip: Zip file containing the source files to be included for OTA.
    333     output_zip: Zip file that will be sent for OTA.
    334     system_included: If True, the system image will be updated and therefore
    335         its metadata should be included.
    336     vendor_included: If True, the vendor image will be updated and therefore
    337         its metadata should be included.
    338   """
    339 
    340   # Determine what metadata we need. Files are names relative to META/.
    341   compatibility_files = []
    342   vendor_metadata = ("vendor_manifest.xml", "vendor_matrix.xml")
    343   system_metadata = ("system_manifest.xml", "system_matrix.xml")
    344   if vendor_included:
    345     compatibility_files += vendor_metadata
    346   if system_included:
    347     compatibility_files += system_metadata
    348 
    349   # Create new archive.
    350   compatibility_archive = tempfile.NamedTemporaryFile()
    351   compatibility_archive_zip = zipfile.ZipFile(compatibility_archive, "w",
    352       compression=zipfile.ZIP_DEFLATED)
    353 
    354   # Add metadata.
    355   for file_name in compatibility_files:
    356     target_file_name = "META/" + file_name
    357 
    358     if target_file_name in target_zip.namelist():
    359       data = target_zip.read(target_file_name)
    360       common.ZipWriteStr(compatibility_archive_zip, file_name, data)
    361 
    362   # Ensure files are written before we copy into output_zip.
    363   compatibility_archive_zip.close()
    364 
    365   # Only add the archive if we have any compatibility info.
    366   if compatibility_archive_zip.namelist():
    367     common.ZipWrite(output_zip, compatibility_archive.name,
    368                     arcname="compatibility.zip",
    369                     compress_type=zipfile.ZIP_STORED)
    370 
    371 
    372 def WriteFullOTAPackage(input_zip, output_zip):
    373   # TODO: how to determine this?  We don't know what version it will
    374   # be installed on top of. For now, we expect the API just won't
    375   # change very often. Similarly for fstab, it might have changed
    376   # in the target build.
    377   script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
    378 
    379   recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
    380   oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
    381   oem_dicts = None
    382   if oem_props:
    383     oem_dicts = _LoadOemDicts(script, recovery_mount_options)
    384 
    385   target_fp = CalculateFingerprint(oem_props, oem_dicts and oem_dicts[0],
    386                                    OPTIONS.info_dict)
    387   metadata = {
    388       "post-build": target_fp,
    389       "pre-device": GetOemProperty("ro.product.device", oem_props,
    390                                    oem_dicts and oem_dicts[0],
    391                                    OPTIONS.info_dict),
    392       "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
    393   }
    394 
    395   device_specific = common.DeviceSpecificParams(
    396       input_zip=input_zip,
    397       input_version=OPTIONS.info_dict["recovery_api_version"],
    398       output_zip=output_zip,
    399       script=script,
    400       input_tmp=OPTIONS.input_tmp,
    401       metadata=metadata,
    402       info_dict=OPTIONS.info_dict)
    403 
    404   assert HasRecoveryPatch(input_zip)
    405 
    406   metadata["ota-type"] = "BLOCK"
    407 
    408   ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
    409   ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
    410   script.AssertOlderBuild(ts, ts_text)
    411 
    412   AppendAssertions(script, OPTIONS.info_dict, oem_dicts)
    413   device_specific.FullOTA_Assertions()
    414 
    415   # Two-step package strategy (in chronological order, which is *not*
    416   # the order in which the generated script has things):
    417   #
    418   # if stage is not "2/3" or "3/3":
    419   #    write recovery image to boot partition
    420   #    set stage to "2/3"
    421   #    reboot to boot partition and restart recovery
    422   # else if stage is "2/3":
    423   #    write recovery image to recovery partition
    424   #    set stage to "3/3"
    425   #    reboot to recovery partition and restart recovery
    426   # else:
    427   #    (stage must be "3/3")
    428   #    set stage to ""
    429   #    do normal full package installation:
    430   #       wipe and install system, boot image, etc.
    431   #       set up system to update recovery partition on first boot
    432   #    complete script normally
    433   #    (allow recovery to mark itself finished and reboot)
    434 
    435   recovery_img = common.GetBootableImage("recovery.img", "recovery.img",
    436                                          OPTIONS.input_tmp, "RECOVERY")
    437   if OPTIONS.two_step:
    438     if not OPTIONS.info_dict.get("multistage_support", None):
    439       assert False, "two-step packages not supported by this build"
    440     fs = OPTIONS.info_dict["fstab"]["/misc"]
    441     assert fs.fs_type.upper() == "EMMC", \
    442         "two-step packages only supported on devices with EMMC /misc partitions"
    443     bcb_dev = {"bcb_dev": fs.device}
    444     common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data)
    445     script.AppendExtra("""
    446 if get_stage("%(bcb_dev)s") == "2/3" then
    447 """ % bcb_dev)
    448 
    449     # Stage 2/3: Write recovery image to /recovery (currently running /boot).
    450     script.Comment("Stage 2/3")
    451     script.WriteRawImage("/recovery", "recovery.img")
    452     script.AppendExtra("""
    453 set_stage("%(bcb_dev)s", "3/3");
    454 reboot_now("%(bcb_dev)s", "recovery");
    455 else if get_stage("%(bcb_dev)s") == "3/3" then
    456 """ % bcb_dev)
    457 
    458     # Stage 3/3: Make changes.
    459     script.Comment("Stage 3/3")
    460 
    461   # Dump fingerprints
    462   script.Print("Target: %s" % target_fp)
    463 
    464   device_specific.FullOTA_InstallBegin()
    465 
    466   system_progress = 0.75
    467 
    468   if OPTIONS.wipe_user_data:
    469     system_progress -= 0.1
    470   if HasVendorPartition(input_zip):
    471     system_progress -= 0.1
    472 
    473   # Place a copy of file_contexts.bin into the OTA package which will be used
    474   # by the recovery program.
    475   if "selinux_fc" in OPTIONS.info_dict:
    476     WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
    477 
    478   recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
    479 
    480   script.ShowProgress(system_progress, 0)
    481 
    482   # Full OTA is done as an "incremental" against an empty source image. This
    483   # has the effect of writing new data from the package to the entire
    484   # partition, but lets us reuse the updater code that writes incrementals to
    485   # do it.
    486   system_tgt = GetImage("system", OPTIONS.input_tmp)
    487   system_tgt.ResetFileMap()
    488   system_diff = common.BlockDifference("system", system_tgt, src=None)
    489   system_diff.WriteScript(script, output_zip)
    490 
    491   boot_img = common.GetBootableImage(
    492       "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
    493 
    494   if HasVendorPartition(input_zip):
    495     script.ShowProgress(0.1, 0)
    496 
    497     vendor_tgt = GetImage("vendor", OPTIONS.input_tmp)
    498     vendor_tgt.ResetFileMap()
    499     vendor_diff = common.BlockDifference("vendor", vendor_tgt)
    500     vendor_diff.WriteScript(script, output_zip)
    501 
    502   common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
    503   common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
    504 
    505   script.ShowProgress(0.05, 5)
    506   script.WriteRawImage("/boot", "boot.img")
    507 
    508   script.ShowProgress(0.2, 10)
    509   device_specific.FullOTA_InstallEnd()
    510 
    511   if OPTIONS.extra_script is not None:
    512     script.AppendExtra(OPTIONS.extra_script)
    513 
    514   script.UnmountAll()
    515 
    516   if OPTIONS.wipe_user_data:
    517     script.ShowProgress(0.1, 10)
    518     script.FormatPartition("/data")
    519 
    520   if OPTIONS.two_step:
    521     script.AppendExtra("""
    522 set_stage("%(bcb_dev)s", "");
    523 """ % bcb_dev)
    524     script.AppendExtra("else\n")
    525 
    526     # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot.
    527     script.Comment("Stage 1/3")
    528     _WriteRecoveryImageToBoot(script, output_zip)
    529 
    530     script.AppendExtra("""
    531 set_stage("%(bcb_dev)s", "2/3");
    532 reboot_now("%(bcb_dev)s", "");
    533 endif;
    534 endif;
    535 """ % bcb_dev)
    536 
    537   script.SetProgress(1)
    538   script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
    539   metadata["ota-required-cache"] = str(script.required_cache)
    540   WriteMetadata(metadata, output_zip)
    541 
    542 
    543 def WritePolicyConfig(file_name, output_zip):
    544   common.ZipWrite(output_zip, file_name, os.path.basename(file_name))
    545 
    546 
    547 def WriteMetadata(metadata, output_zip):
    548   value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.iteritems())])
    549   common.ZipWriteStr(output_zip, METADATA_NAME, value,
    550                      compress_type=zipfile.ZIP_STORED)
    551 
    552 
    553 def GetBuildProp(prop, info_dict):
    554   """Return the fingerprint of the build of a given target-files info_dict."""
    555   try:
    556     return info_dict.get("build.prop", {})[prop]
    557   except KeyError:
    558     raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
    559 
    560 
    561 def HandleDowngradeMetadata(metadata):
    562   # Only incremental OTAs are allowed to reach here.
    563   assert OPTIONS.incremental_source is not None
    564 
    565   post_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.target_info_dict)
    566   pre_timestamp = GetBuildProp("ro.build.date.utc", OPTIONS.source_info_dict)
    567   is_downgrade = long(post_timestamp) < long(pre_timestamp)
    568 
    569   if OPTIONS.downgrade:
    570     if not is_downgrade:
    571       raise RuntimeError("--downgrade specified but no downgrade detected: "
    572                          "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
    573     metadata["ota-downgrade"] = "yes"
    574   elif OPTIONS.timestamp:
    575     if not is_downgrade:
    576       raise RuntimeError("--timestamp specified but no timestamp hack needed: "
    577                          "pre: %s, post: %s" % (pre_timestamp, post_timestamp))
    578     metadata["post-timestamp"] = str(long(pre_timestamp) + 1)
    579   else:
    580     if is_downgrade:
    581       raise RuntimeError("Downgrade detected based on timestamp check: "
    582                          "pre: %s, post: %s. Need to specify --timestamp OR "
    583                          "--downgrade to allow building the incremental." % (
    584                              pre_timestamp, post_timestamp))
    585     metadata["post-timestamp"] = post_timestamp
    586 
    587 
    588 def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
    589   source_version = OPTIONS.source_info_dict["recovery_api_version"]
    590   target_version = OPTIONS.target_info_dict["recovery_api_version"]
    591 
    592   if source_version == 0:
    593     print("WARNING: generating edify script for a source that "
    594           "can't install it.")
    595   script = edify_generator.EdifyGenerator(
    596       source_version, OPTIONS.target_info_dict,
    597       fstab=OPTIONS.source_info_dict["fstab"])
    598 
    599   recovery_mount_options = OPTIONS.source_info_dict.get(
    600       "recovery_mount_options")
    601   source_oem_props = OPTIONS.source_info_dict.get("oem_fingerprint_properties")
    602   target_oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
    603   oem_dicts = None
    604   if source_oem_props and target_oem_props:
    605     oem_dicts = _LoadOemDicts(script, recovery_mount_options)
    606 
    607   metadata = {
    608       "pre-device": GetOemProperty("ro.product.device", source_oem_props,
    609                                    oem_dicts and oem_dicts[0],
    610                                    OPTIONS.source_info_dict),
    611       "ota-type": "BLOCK",
    612   }
    613 
    614   HandleDowngradeMetadata(metadata)
    615 
    616   device_specific = common.DeviceSpecificParams(
    617       source_zip=source_zip,
    618       source_version=source_version,
    619       target_zip=target_zip,
    620       target_version=target_version,
    621       output_zip=output_zip,
    622       script=script,
    623       metadata=metadata,
    624       info_dict=OPTIONS.source_info_dict)
    625 
    626   source_fp = CalculateFingerprint(source_oem_props, oem_dicts and oem_dicts[0],
    627                                    OPTIONS.source_info_dict)
    628   target_fp = CalculateFingerprint(target_oem_props, oem_dicts and oem_dicts[0],
    629                                    OPTIONS.target_info_dict)
    630   metadata["pre-build"] = source_fp
    631   metadata["post-build"] = target_fp
    632   metadata["pre-build-incremental"] = GetBuildProp(
    633       "ro.build.version.incremental", OPTIONS.source_info_dict)
    634   metadata["post-build-incremental"] = GetBuildProp(
    635       "ro.build.version.incremental", OPTIONS.target_info_dict)
    636 
    637   source_boot = common.GetBootableImage(
    638       "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
    639       OPTIONS.source_info_dict)
    640   target_boot = common.GetBootableImage(
    641       "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
    642   updating_boot = (not OPTIONS.two_step and
    643                    (source_boot.data != target_boot.data))
    644 
    645   target_recovery = common.GetBootableImage(
    646       "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
    647 
    648   system_src = GetImage("system", OPTIONS.source_tmp)
    649   system_tgt = GetImage("system", OPTIONS.target_tmp)
    650 
    651   blockimgdiff_version = 1
    652   if OPTIONS.info_dict:
    653     blockimgdiff_version = max(
    654         int(i) for i in
    655         OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(","))
    656 
    657   # Check the first block of the source system partition for remount R/W only
    658   # if the filesystem is ext4.
    659   system_src_partition = OPTIONS.source_info_dict["fstab"]["/system"]
    660   check_first_block = system_src_partition.fs_type == "ext4"
    661   # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
    662   # in zip formats. However with squashfs, a) all files are compressed in LZ4;
    663   # b) the blocks listed in block map may not contain all the bytes for a given
    664   # file (because they're rounded to be 4K-aligned).
    665   system_tgt_partition = OPTIONS.target_info_dict["fstab"]["/system"]
    666   disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
    667                      system_tgt_partition.fs_type == "squashfs")
    668   system_diff = common.BlockDifference("system", system_tgt, system_src,
    669                                        check_first_block,
    670                                        version=blockimgdiff_version,
    671                                        disable_imgdiff=disable_imgdiff)
    672 
    673   if HasVendorPartition(target_zip):
    674     if not HasVendorPartition(source_zip):
    675       raise RuntimeError("can't generate incremental that adds /vendor")
    676     vendor_src = GetImage("vendor", OPTIONS.source_tmp)
    677     vendor_tgt = GetImage("vendor", OPTIONS.target_tmp)
    678 
    679     # Check first block of vendor partition for remount R/W only if
    680     # disk type is ext4
    681     vendor_partition = OPTIONS.source_info_dict["fstab"]["/vendor"]
    682     check_first_block = vendor_partition.fs_type == "ext4"
    683     disable_imgdiff = vendor_partition.fs_type == "squashfs"
    684     vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
    685                                          check_first_block,
    686                                          version=blockimgdiff_version,
    687                                          disable_imgdiff=disable_imgdiff)
    688   else:
    689     vendor_diff = None
    690 
    691   AppendAssertions(script, OPTIONS.target_info_dict, oem_dicts)
    692   device_specific.IncrementalOTA_Assertions()
    693 
    694   # Two-step incremental package strategy (in chronological order,
    695   # which is *not* the order in which the generated script has
    696   # things):
    697   #
    698   # if stage is not "2/3" or "3/3":
    699   #    do verification on current system
    700   #    write recovery image to boot partition
    701   #    set stage to "2/3"
    702   #    reboot to boot partition and restart recovery
    703   # else if stage is "2/3":
    704   #    write recovery image to recovery partition
    705   #    set stage to "3/3"
    706   #    reboot to recovery partition and restart recovery
    707   # else:
    708   #    (stage must be "3/3")
    709   #    perform update:
    710   #       patch system files, etc.
    711   #       force full install of new boot image
    712   #       set up system to update recovery partition on first boot
    713   #    complete script normally
    714   #    (allow recovery to mark itself finished and reboot)
    715 
    716   if OPTIONS.two_step:
    717     if not OPTIONS.source_info_dict.get("multistage_support", None):
    718       assert False, "two-step packages not supported by this build"
    719     fs = OPTIONS.source_info_dict["fstab"]["/misc"]
    720     assert fs.fs_type.upper() == "EMMC", \
    721         "two-step packages only supported on devices with EMMC /misc partitions"
    722     bcb_dev = {"bcb_dev": fs.device}
    723     common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
    724     script.AppendExtra("""
    725 if get_stage("%(bcb_dev)s") == "2/3" then
    726 """ % bcb_dev)
    727 
    728     # Stage 2/3: Write recovery image to /recovery (currently running /boot).
    729     script.Comment("Stage 2/3")
    730     script.AppendExtra("sleep(20);\n")
    731     script.WriteRawImage("/recovery", "recovery.img")
    732     script.AppendExtra("""
    733 set_stage("%(bcb_dev)s", "3/3");
    734 reboot_now("%(bcb_dev)s", "recovery");
    735 else if get_stage("%(bcb_dev)s") != "3/3" then
    736 """ % bcb_dev)
    737 
    738     # Stage 1/3: (a) Verify the current system.
    739     script.Comment("Stage 1/3")
    740 
    741   # Dump fingerprints
    742   script.Print("Source: %s" % (source_fp,))
    743   script.Print("Target: %s" % (target_fp,))
    744 
    745   script.Print("Verifying current system...")
    746 
    747   device_specific.IncrementalOTA_VerifyBegin()
    748 
    749   # When blockimgdiff version is less than 3 (non-resumable block-based OTA),
    750   # patching on a device that's already on the target build will damage the
    751   # system. Because operations like move don't check the block state, they
    752   # always apply the changes unconditionally.
    753   if blockimgdiff_version <= 2:
    754     if source_oem_props is None:
    755       script.AssertSomeFingerprint(source_fp)
    756     else:
    757       script.AssertSomeThumbprint(
    758           GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
    759 
    760   else: # blockimgdiff_version > 2
    761     if source_oem_props is None and target_oem_props is None:
    762       script.AssertSomeFingerprint(source_fp, target_fp)
    763     elif source_oem_props is not None and target_oem_props is not None:
    764       script.AssertSomeThumbprint(
    765           GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
    766           GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
    767     elif source_oem_props is None and target_oem_props is not None:
    768       script.AssertFingerprintOrThumbprint(
    769           source_fp,
    770           GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict))
    771     else:
    772       script.AssertFingerprintOrThumbprint(
    773           target_fp,
    774           GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
    775 
    776   # Check the required cache size (i.e. stashed blocks).
    777   size = []
    778   if system_diff:
    779     size.append(system_diff.required_cache)
    780   if vendor_diff:
    781     size.append(vendor_diff.required_cache)
    782 
    783   if updating_boot:
    784     boot_type, boot_device = common.GetTypeAndDevice(
    785         "/boot", OPTIONS.source_info_dict)
    786     d = common.Difference(target_boot, source_boot)
    787     _, _, d = d.ComputePatch()
    788     if d is None:
    789       include_full_boot = True
    790       common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
    791     else:
    792       include_full_boot = False
    793 
    794       print("boot      target: %d  source: %d  diff: %d" % (
    795           target_boot.size, source_boot.size, len(d)))
    796 
    797       common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
    798 
    799       script.PatchCheck("%s:%s:%d:%s:%d:%s" %
    800                         (boot_type, boot_device,
    801                          source_boot.size, source_boot.sha1,
    802                          target_boot.size, target_boot.sha1))
    803       size.append(target_boot.size)
    804 
    805   if size:
    806     script.CacheFreeSpaceCheck(max(size))
    807 
    808   device_specific.IncrementalOTA_VerifyEnd()
    809 
    810   if OPTIONS.two_step:
    811     # Stage 1/3: (b) Write recovery image to /boot.
    812     _WriteRecoveryImageToBoot(script, output_zip)
    813 
    814     script.AppendExtra("""
    815 set_stage("%(bcb_dev)s", "2/3");
    816 reboot_now("%(bcb_dev)s", "");
    817 else
    818 """ % bcb_dev)
    819 
    820     # Stage 3/3: Make changes.
    821     script.Comment("Stage 3/3")
    822 
    823   # Verify the existing partitions.
    824   system_diff.WriteVerifyScript(script, touched_blocks_only=True)
    825   if vendor_diff:
    826     vendor_diff.WriteVerifyScript(script, touched_blocks_only=True)
    827 
    828   script.Comment("---- start making changes here ----")
    829 
    830   device_specific.IncrementalOTA_InstallBegin()
    831 
    832   system_diff.WriteScript(script, output_zip,
    833                           progress=0.8 if vendor_diff else 0.9)
    834 
    835   if vendor_diff:
    836     vendor_diff.WriteScript(script, output_zip, progress=0.1)
    837 
    838   if OPTIONS.two_step:
    839     common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
    840     script.WriteRawImage("/boot", "boot.img")
    841     print("writing full boot image (forced by two-step mode)")
    842 
    843   if not OPTIONS.two_step:
    844     if updating_boot:
    845       if include_full_boot:
    846         print("boot image changed; including full.")
    847         script.Print("Installing boot image...")
    848         script.WriteRawImage("/boot", "boot.img")
    849       else:
    850         # Produce the boot image by applying a patch to the current
    851         # contents of the boot partition, and write it back to the
    852         # partition.
    853         print("boot image changed; including patch.")
    854         script.Print("Patching boot image...")
    855         script.ShowProgress(0.1, 10)
    856         script.ApplyPatch("%s:%s:%d:%s:%d:%s"
    857                           % (boot_type, boot_device,
    858                              source_boot.size, source_boot.sha1,
    859                              target_boot.size, target_boot.sha1),
    860                           "-",
    861                           target_boot.size, target_boot.sha1,
    862                           source_boot.sha1, "patch/boot.img.p")
    863     else:
    864       print("boot image unchanged; skipping.")
    865 
    866   # Do device-specific installation (eg, write radio image).
    867   device_specific.IncrementalOTA_InstallEnd()
    868 
    869   if OPTIONS.extra_script is not None:
    870     script.AppendExtra(OPTIONS.extra_script)
    871 
    872   if OPTIONS.wipe_user_data:
    873     script.Print("Erasing user data...")
    874     script.FormatPartition("/data")
    875     metadata["ota-wipe"] = "yes"
    876 
    877   if OPTIONS.two_step:
    878     script.AppendExtra("""
    879 set_stage("%(bcb_dev)s", "");
    880 endif;
    881 endif;
    882 """ % bcb_dev)
    883 
    884   script.SetProgress(1)
    885   # For downgrade OTAs, we prefer to use the update-binary in the source
    886   # build that is actually newer than the one in the target build.
    887   if OPTIONS.downgrade:
    888     script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
    889   else:
    890     script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
    891   metadata["ota-required-cache"] = str(script.required_cache)
    892   WriteMetadata(metadata, output_zip)
    893 
    894 
    895 def WriteVerifyPackage(input_zip, output_zip):
    896   script = edify_generator.EdifyGenerator(3, OPTIONS.info_dict)
    897 
    898   oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties")
    899   recovery_mount_options = OPTIONS.info_dict.get(
    900       "recovery_mount_options")
    901   oem_dicts = None
    902   if oem_props:
    903     oem_dicts = _LoadOemDicts(script, recovery_mount_options)
    904 
    905   target_fp = CalculateFingerprint(oem_props, oem_dicts and oem_dicts[0],
    906                                    OPTIONS.info_dict)
    907   metadata = {
    908       "post-build": target_fp,
    909       "pre-device": GetOemProperty("ro.product.device", oem_props,
    910                                    oem_dicts and oem_dicts[0],
    911                                    OPTIONS.info_dict),
    912       "post-timestamp": GetBuildProp("ro.build.date.utc", OPTIONS.info_dict),
    913   }
    914 
    915   device_specific = common.DeviceSpecificParams(
    916       input_zip=input_zip,
    917       input_version=OPTIONS.info_dict["recovery_api_version"],
    918       output_zip=output_zip,
    919       script=script,
    920       input_tmp=OPTIONS.input_tmp,
    921       metadata=metadata,
    922       info_dict=OPTIONS.info_dict)
    923 
    924   AppendAssertions(script, OPTIONS.info_dict, oem_dicts)
    925 
    926   script.Print("Verifying device images against %s..." % target_fp)
    927   script.AppendExtra("")
    928 
    929   script.Print("Verifying boot...")
    930   boot_img = common.GetBootableImage(
    931       "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
    932   boot_type, boot_device = common.GetTypeAndDevice(
    933       "/boot", OPTIONS.info_dict)
    934   script.Verify("%s:%s:%d:%s" % (
    935       boot_type, boot_device, boot_img.size, boot_img.sha1))
    936   script.AppendExtra("")
    937 
    938   script.Print("Verifying recovery...")
    939   recovery_img = common.GetBootableImage(
    940       "recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
    941   recovery_type, recovery_device = common.GetTypeAndDevice(
    942       "/recovery", OPTIONS.info_dict)
    943   script.Verify("%s:%s:%d:%s" % (
    944       recovery_type, recovery_device, recovery_img.size, recovery_img.sha1))
    945   script.AppendExtra("")
    946 
    947   system_tgt = GetImage("system", OPTIONS.input_tmp)
    948   system_tgt.ResetFileMap()
    949   system_diff = common.BlockDifference("system", system_tgt, src=None)
    950   system_diff.WriteStrictVerifyScript(script)
    951 
    952   if HasVendorPartition(input_zip):
    953     vendor_tgt = GetImage("vendor", OPTIONS.input_tmp)
    954     vendor_tgt.ResetFileMap()
    955     vendor_diff = common.BlockDifference("vendor", vendor_tgt, src=None)
    956     vendor_diff.WriteStrictVerifyScript(script)
    957 
    958   # Device specific partitions, such as radio, bootloader and etc.
    959   device_specific.VerifyOTA_Assertions()
    960 
    961   script.SetProgress(1.0)
    962   script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary)
    963   metadata["ota-required-cache"] = str(script.required_cache)
    964   WriteMetadata(metadata, output_zip)
    965 
    966 
    967 def WriteABOTAPackageWithBrilloScript(target_file, output_file,
    968                                       source_file=None):
    969   """Generate an Android OTA package that has A/B update payload."""
    970 
    971   def ComputeStreamingMetadata(zip_file, reserve_space=False,
    972                                expected_length=None):
    973     """Compute the streaming metadata for a given zip.
    974 
    975     When 'reserve_space' is True, we reserve extra space for the offset and
    976     length of the metadata entry itself, although we don't know the final
    977     values until the package gets signed. This function will be called again
    978     after signing. We then write the actual values and pad the string to the
    979     length we set earlier. Note that we can't use the actual length of the
    980     metadata entry in the second run. Otherwise the offsets for other entries
    981     will be changing again.
    982     """
    983 
    984     def ComputeEntryOffsetSize(name):
    985       """Compute the zip entry offset and size."""
    986       info = zip_file.getinfo(name)
    987       offset = info.header_offset + len(info.FileHeader())
    988       size = info.file_size
    989       return '%s:%d:%d' % (os.path.basename(name), offset, size)
    990 
    991     # payload.bin and payload_properties.txt must exist.
    992     offsets = [ComputeEntryOffsetSize('payload.bin'),
    993                ComputeEntryOffsetSize('payload_properties.txt')]
    994 
    995     # care_map.txt is available only if dm-verity is enabled.
    996     if 'care_map.txt' in zip_file.namelist():
    997       offsets.append(ComputeEntryOffsetSize('care_map.txt'))
    998 
    999     if 'compatibility.zip' in zip_file.namelist():
   1000       offsets.append(ComputeEntryOffsetSize('compatibility.zip'))
   1001 
   1002     # 'META-INF/com/android/metadata' is required. We don't know its actual
   1003     # offset and length (as well as the values for other entries). So we
   1004     # reserve 10-byte as a placeholder, which is to cover the space for metadata
   1005     # entry ('xx:xxx', since it's ZIP_STORED which should appear at the
   1006     # beginning of the zip), as well as the possible value changes in other
   1007     # entries.
   1008     if reserve_space:
   1009       offsets.append('metadata:' + ' ' * 10)
   1010     else:
   1011       offsets.append(ComputeEntryOffsetSize(METADATA_NAME))
   1012 
   1013     value = ','.join(offsets)
   1014     if expected_length is not None:
   1015       assert len(value) <= expected_length, \
   1016           'Insufficient reserved space: reserved=%d, actual=%d' % (
   1017               expected_length, len(value))
   1018       value += ' ' * (expected_length - len(value))
   1019     return value
   1020 
   1021   # The place where the output from the subprocess should go.
   1022   log_file = sys.stdout if OPTIONS.verbose else subprocess.PIPE
   1023 
   1024   # Setup signing keys.
   1025   if OPTIONS.package_key is None:
   1026     OPTIONS.package_key = OPTIONS.info_dict.get(
   1027         "default_system_dev_certificate",
   1028         "build/target/product/security/testkey")
   1029 
   1030   # A/B updater expects a signing key in RSA format. Gets the key ready for
   1031   # later use in step 3, unless a payload_signer has been specified.
   1032   if OPTIONS.payload_signer is None:
   1033     cmd = ["openssl", "pkcs8",
   1034            "-in", OPTIONS.package_key + OPTIONS.private_key_suffix,
   1035            "-inform", "DER", "-nocrypt"]
   1036     rsa_key = common.MakeTempFile(prefix="key-", suffix=".key")
   1037     cmd.extend(["-out", rsa_key])
   1038     p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
   1039     p1.communicate()
   1040     assert p1.returncode == 0, "openssl pkcs8 failed"
   1041 
   1042   # Stage the output zip package for package signing.
   1043   temp_zip_file = tempfile.NamedTemporaryFile()
   1044   output_zip = zipfile.ZipFile(temp_zip_file, "w",
   1045                                compression=zipfile.ZIP_DEFLATED)
   1046 
   1047   # Metadata to comply with Android OTA package format.
   1048   oem_props = OPTIONS.info_dict.get("oem_fingerprint_properties", None)
   1049   oem_dicts = None
   1050   if oem_props:
   1051     oem_dicts = _LoadOemDicts(None)
   1052 
   1053   metadata = {
   1054       "post-build": CalculateFingerprint(oem_props, oem_dicts and oem_dicts[0],
   1055                                          OPTIONS.info_dict),
   1056       "post-build-incremental" : GetBuildProp("ro.build.version.incremental",
   1057                                               OPTIONS.info_dict),
   1058       "pre-device": GetOemProperty("ro.product.device", oem_props,
   1059                                    oem_dicts and oem_dicts[0],
   1060                                    OPTIONS.info_dict),
   1061       "ota-required-cache": "0",
   1062       "ota-type": "AB",
   1063   }
   1064 
   1065   if source_file is not None:
   1066     metadata["pre-build"] = CalculateFingerprint(oem_props,
   1067                                                  oem_dicts and oem_dicts[0],
   1068                                                  OPTIONS.source_info_dict)
   1069     metadata["pre-build-incremental"] = GetBuildProp(
   1070         "ro.build.version.incremental", OPTIONS.source_info_dict)
   1071 
   1072     HandleDowngradeMetadata(metadata)
   1073   else:
   1074     metadata["post-timestamp"] = GetBuildProp(
   1075         "ro.build.date.utc", OPTIONS.info_dict)
   1076 
   1077   # 1. Generate payload.
   1078   payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin")
   1079   cmd = ["brillo_update_payload", "generate",
   1080          "--payload", payload_file,
   1081          "--target_image", target_file]
   1082   if source_file is not None:
   1083     cmd.extend(["--source_image", source_file])
   1084   p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
   1085   p1.communicate()
   1086   assert p1.returncode == 0, "brillo_update_payload generate failed"
   1087 
   1088   # 2. Generate hashes of the payload and metadata files.
   1089   payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
   1090   metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
   1091   cmd = ["brillo_update_payload", "hash",
   1092          "--unsigned_payload", payload_file,
   1093          "--signature_size", "256",
   1094          "--metadata_hash_file", metadata_sig_file,
   1095          "--payload_hash_file", payload_sig_file]
   1096   p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
   1097   p1.communicate()
   1098   assert p1.returncode == 0, "brillo_update_payload hash failed"
   1099 
   1100   # 3. Sign the hashes and insert them back into the payload file.
   1101   signed_payload_sig_file = common.MakeTempFile(prefix="signed-sig-",
   1102                                                 suffix=".bin")
   1103   signed_metadata_sig_file = common.MakeTempFile(prefix="signed-sig-",
   1104                                                  suffix=".bin")
   1105   # 3a. Sign the payload hash.
   1106   if OPTIONS.payload_signer is not None:
   1107     cmd = [OPTIONS.payload_signer]
   1108     cmd.extend(OPTIONS.payload_signer_args)
   1109   else:
   1110     cmd = ["openssl", "pkeyutl", "-sign",
   1111            "-inkey", rsa_key,
   1112            "-pkeyopt", "digest:sha256"]
   1113   cmd.extend(["-in", payload_sig_file,
   1114               "-out", signed_payload_sig_file])
   1115   p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
   1116   p1.communicate()
   1117   assert p1.returncode == 0, "openssl sign payload failed"
   1118 
   1119   # 3b. Sign the metadata hash.
   1120   if OPTIONS.payload_signer is not None:
   1121     cmd = [OPTIONS.payload_signer]
   1122     cmd.extend(OPTIONS.payload_signer_args)
   1123   else:
   1124     cmd = ["openssl", "pkeyutl", "-sign",
   1125            "-inkey", rsa_key,
   1126            "-pkeyopt", "digest:sha256"]
   1127   cmd.extend(["-in", metadata_sig_file,
   1128               "-out", signed_metadata_sig_file])
   1129   p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
   1130   p1.communicate()
   1131   assert p1.returncode == 0, "openssl sign metadata failed"
   1132 
   1133   # 3c. Insert the signatures back into the payload file.
   1134   signed_payload_file = common.MakeTempFile(prefix="signed-payload-",
   1135                                             suffix=".bin")
   1136   cmd = ["brillo_update_payload", "sign",
   1137          "--unsigned_payload", payload_file,
   1138          "--payload", signed_payload_file,
   1139          "--signature_size", "256",
   1140          "--metadata_signature_file", signed_metadata_sig_file,
   1141          "--payload_signature_file", signed_payload_sig_file]
   1142   p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
   1143   p1.communicate()
   1144   assert p1.returncode == 0, "brillo_update_payload sign failed"
   1145 
   1146   # 4. Dump the signed payload properties.
   1147   properties_file = common.MakeTempFile(prefix="payload-properties-",
   1148                                         suffix=".txt")
   1149   cmd = ["brillo_update_payload", "properties",
   1150          "--payload", signed_payload_file,
   1151          "--properties_file", properties_file]
   1152   p1 = common.Run(cmd, stdout=log_file, stderr=subprocess.STDOUT)
   1153   p1.communicate()
   1154   assert p1.returncode == 0, "brillo_update_payload properties failed"
   1155 
   1156   if OPTIONS.wipe_user_data:
   1157     with open(properties_file, "a") as f:
   1158       f.write("POWERWASH=1\n")
   1159     metadata["ota-wipe"] = "yes"
   1160 
   1161   # Add the signed payload file and properties into the zip. In order to
   1162   # support streaming, we pack payload.bin, payload_properties.txt and
   1163   # care_map.txt as ZIP_STORED. So these entries can be read directly with
   1164   # the offset and length pairs.
   1165   common.ZipWrite(output_zip, signed_payload_file, arcname="payload.bin",
   1166                   compress_type=zipfile.ZIP_STORED)
   1167   common.ZipWrite(output_zip, properties_file,
   1168                   arcname="payload_properties.txt",
   1169                   compress_type=zipfile.ZIP_STORED)
   1170 
   1171   # If dm-verity is supported for the device, copy contents of care_map
   1172   # into A/B OTA package.
   1173   target_zip = zipfile.ZipFile(target_file, "r")
   1174   if OPTIONS.info_dict.get("verity") == "true":
   1175     care_map_path = "META/care_map.txt"
   1176     namelist = target_zip.namelist()
   1177     if care_map_path in namelist:
   1178       care_map_data = target_zip.read(care_map_path)
   1179       common.ZipWriteStr(output_zip, "care_map.txt", care_map_data,
   1180           compress_type=zipfile.ZIP_STORED)
   1181     else:
   1182       print("Warning: cannot find care map file in target_file package")
   1183 
   1184   if HasVendorPartition(target_zip):
   1185     update_vendor = True
   1186     update_system = True
   1187 
   1188     # If incremental then figure out what is being updated so metadata only for
   1189     # the updated image is included.
   1190     if source_file is not None:
   1191       input_tmp, input_zip = common.UnzipTemp(
   1192           target_file, UNZIP_PATTERN)
   1193       source_tmp, source_zip = common.UnzipTemp(
   1194           source_file, UNZIP_PATTERN)
   1195 
   1196       vendor_src = GetImage("vendor", source_tmp)
   1197       vendor_tgt = GetImage("vendor", input_tmp)
   1198       system_src = GetImage("system", source_tmp)
   1199       system_tgt = GetImage("system", input_tmp)
   1200 
   1201       update_system = system_src.TotalSha1() != system_tgt.TotalSha1()
   1202       update_vendor = vendor_src.TotalSha1() != vendor_tgt.TotalSha1()
   1203 
   1204       input_zip.close()
   1205       source_zip.close()
   1206 
   1207     target_zip = zipfile.ZipFile(target_file, "r")
   1208     AddCompatibilityArchive(target_zip, output_zip, update_system,
   1209                             update_vendor)
   1210   common.ZipClose(target_zip)
   1211 
   1212   # Write the current metadata entry with placeholders.
   1213   metadata['ota-streaming-property-files'] = ComputeStreamingMetadata(
   1214       output_zip, reserve_space=True)
   1215   WriteMetadata(metadata, output_zip)
   1216   common.ZipClose(output_zip)
   1217 
   1218   # SignOutput(), which in turn calls signapk.jar, will possibly reorder the
   1219   # zip entries, as well as padding the entry headers. We do a preliminary
   1220   # signing (with an incomplete metadata entry) to allow that to happen. Then
   1221   # compute the zip entry offsets, write back the final metadata and do the
   1222   # final signing.
   1223   prelim_signing = tempfile.NamedTemporaryFile()
   1224   SignOutput(temp_zip_file.name, prelim_signing.name)
   1225   common.ZipClose(temp_zip_file)
   1226 
   1227   # Open the signed zip. Compute the final metadata that's needed for streaming.
   1228   prelim_zip = zipfile.ZipFile(prelim_signing, "r",
   1229                                compression=zipfile.ZIP_DEFLATED)
   1230   expected_length = len(metadata['ota-streaming-property-files'])
   1231   metadata['ota-streaming-property-files'] = ComputeStreamingMetadata(
   1232       prelim_zip, reserve_space=False, expected_length=expected_length)
   1233 
   1234   # Copy the zip entries, as we cannot update / delete entries with zipfile.
   1235   final_signing = tempfile.NamedTemporaryFile()
   1236   output_zip = zipfile.ZipFile(final_signing, "w",
   1237                                compression=zipfile.ZIP_DEFLATED)
   1238   for item in prelim_zip.infolist():
   1239     if item.filename == METADATA_NAME:
   1240       continue
   1241 
   1242     data = prelim_zip.read(item.filename)
   1243     out_info = copy.copy(item)
   1244     common.ZipWriteStr(output_zip, out_info, data)
   1245 
   1246   # Now write the final metadata entry.
   1247   WriteMetadata(metadata, output_zip)
   1248   common.ZipClose(prelim_zip)
   1249   common.ZipClose(output_zip)
   1250 
   1251   # Re-sign the package after updating the metadata entry.
   1252   SignOutput(final_signing.name, output_file)
   1253   final_signing.close()
   1254 
   1255   # Reopen the final signed zip to double check the streaming metadata.
   1256   output_zip = zipfile.ZipFile(output_file, "r")
   1257   actual = metadata['ota-streaming-property-files'].strip()
   1258   expected = ComputeStreamingMetadata(output_zip)
   1259   assert actual == expected, \
   1260       "Mismatching streaming metadata: %s vs %s." % (actual, expected)
   1261   common.ZipClose(output_zip)
   1262 
   1263 
   1264 def main(argv):
   1265 
   1266   def option_handler(o, a):
   1267     if o == "--board_config":
   1268       pass   # deprecated
   1269     elif o in ("-k", "--package_key"):
   1270       OPTIONS.package_key = a
   1271     elif o in ("-i", "--incremental_from"):
   1272       OPTIONS.incremental_source = a
   1273     elif o == "--full_radio":
   1274       OPTIONS.full_radio = True
   1275     elif o == "--full_bootloader":
   1276       OPTIONS.full_bootloader = True
   1277     elif o in ("-w", "--wipe_user_data"):
   1278       OPTIONS.wipe_user_data = True
   1279     elif o == "--downgrade":
   1280       OPTIONS.downgrade = True
   1281       OPTIONS.wipe_user_data = True
   1282     elif o == "--override_timestamp":
   1283       OPTIONS.timestamp = True
   1284     elif o in ("-o", "--oem_settings"):
   1285       OPTIONS.oem_source = a.split(',')
   1286     elif o == "--oem_no_mount":
   1287       OPTIONS.oem_no_mount = True
   1288     elif o in ("-e", "--extra_script"):
   1289       OPTIONS.extra_script = a
   1290     elif o in ("-t", "--worker_threads"):
   1291       if a.isdigit():
   1292         OPTIONS.worker_threads = int(a)
   1293       else:
   1294         raise ValueError("Cannot parse value %r for option %r - only "
   1295                          "integers are allowed." % (a, o))
   1296     elif o in ("-2", "--two_step"):
   1297       OPTIONS.two_step = True
   1298     elif o == "--no_signing":
   1299       OPTIONS.no_signing = True
   1300     elif o == "--verify":
   1301       OPTIONS.verify = True
   1302     elif o == "--block":
   1303       OPTIONS.block_based = True
   1304     elif o in ("-b", "--binary"):
   1305       OPTIONS.updater_binary = a
   1306     elif o in ("--no_fallback_to_full",):
   1307       OPTIONS.fallback_to_full = False
   1308     elif o == "--stash_threshold":
   1309       try:
   1310         OPTIONS.stash_threshold = float(a)
   1311       except ValueError:
   1312         raise ValueError("Cannot parse value %r for option %r - expecting "
   1313                          "a float" % (a, o))
   1314     elif o == "--gen_verify":
   1315       OPTIONS.gen_verify = True
   1316     elif o == "--log_diff":
   1317       OPTIONS.log_diff = a
   1318     elif o == "--payload_signer":
   1319       OPTIONS.payload_signer = a
   1320     elif o == "--payload_signer_args":
   1321       OPTIONS.payload_signer_args = shlex.split(a)
   1322     elif o == "--extracted_input_target_files":
   1323       OPTIONS.extracted_input = a
   1324     else:
   1325       return False
   1326     return True
   1327 
   1328   args = common.ParseOptions(argv, __doc__,
   1329                              extra_opts="b:k:i:d:we:t:2o:",
   1330                              extra_long_opts=[
   1331                                  "board_config=",
   1332                                  "package_key=",
   1333                                  "incremental_from=",
   1334                                  "full_radio",
   1335                                  "full_bootloader",
   1336                                  "wipe_user_data",
   1337                                  "downgrade",
   1338                                  "override_timestamp",
   1339                                  "extra_script=",
   1340                                  "worker_threads=",
   1341                                  "two_step",
   1342                                  "no_signing",
   1343                                  "block",
   1344                                  "binary=",
   1345                                  "oem_settings=",
   1346                                  "oem_no_mount",
   1347                                  "verify",
   1348                                  "no_fallback_to_full",
   1349                                  "stash_threshold=",
   1350                                  "gen_verify",
   1351                                  "log_diff=",
   1352                                  "payload_signer=",
   1353                                  "payload_signer_args=",
   1354                                  "extracted_input_target_files=",
   1355                              ], extra_option_handler=option_handler)
   1356 
   1357   if len(args) != 2:
   1358     common.Usage(__doc__)
   1359     sys.exit(1)
   1360 
   1361   if OPTIONS.downgrade:
   1362     # Sanity check to enforce a data wipe.
   1363     if not OPTIONS.wipe_user_data:
   1364       raise ValueError("Cannot downgrade without a data wipe")
   1365 
   1366     # We should only allow downgrading incrementals (as opposed to full).
   1367     # Otherwise the device may go back from arbitrary build with this full
   1368     # OTA package.
   1369     if OPTIONS.incremental_source is None:
   1370       raise ValueError("Cannot generate downgradable full OTAs")
   1371 
   1372   assert not (OPTIONS.downgrade and OPTIONS.timestamp), \
   1373       "Cannot have --downgrade AND --override_timestamp both"
   1374 
   1375   # Load the dict file from the zip directly to have a peek at the OTA type.
   1376   # For packages using A/B update, unzipping is not needed.
   1377   if OPTIONS.extracted_input is not None:
   1378     OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input, OPTIONS.extracted_input)
   1379   else:
   1380     input_zip = zipfile.ZipFile(args[0], "r")
   1381     OPTIONS.info_dict = common.LoadInfoDict(input_zip)
   1382     common.ZipClose(input_zip)
   1383 
   1384   ab_update = OPTIONS.info_dict.get("ab_update") == "true"
   1385 
   1386   if ab_update:
   1387     if OPTIONS.incremental_source is not None:
   1388       OPTIONS.target_info_dict = OPTIONS.info_dict
   1389       source_zip = zipfile.ZipFile(OPTIONS.incremental_source, "r")
   1390       OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
   1391       common.ZipClose(source_zip)
   1392 
   1393     if OPTIONS.verbose:
   1394       print("--- target info ---")
   1395       common.DumpInfoDict(OPTIONS.info_dict)
   1396 
   1397       if OPTIONS.incremental_source is not None:
   1398         print("--- source info ---")
   1399         common.DumpInfoDict(OPTIONS.source_info_dict)
   1400 
   1401     WriteABOTAPackageWithBrilloScript(
   1402         target_file=args[0],
   1403         output_file=args[1],
   1404         source_file=OPTIONS.incremental_source)
   1405 
   1406     print("done.")
   1407     return
   1408 
   1409   if OPTIONS.extra_script is not None:
   1410     OPTIONS.extra_script = open(OPTIONS.extra_script).read()
   1411 
   1412   if OPTIONS.extracted_input is not None:
   1413     OPTIONS.input_tmp = OPTIONS.extracted_input
   1414     OPTIONS.target_tmp = OPTIONS.input_tmp
   1415     OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.input_tmp, OPTIONS.input_tmp)
   1416     input_zip = zipfile.ZipFile(args[0], "r")
   1417   else:
   1418     print("unzipping target target-files...")
   1419     OPTIONS.input_tmp, input_zip = common.UnzipTemp(
   1420         args[0], UNZIP_PATTERN)
   1421 
   1422     OPTIONS.target_tmp = OPTIONS.input_tmp
   1423     OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.target_tmp)
   1424 
   1425   if OPTIONS.verbose:
   1426     print("--- target info ---")
   1427     common.DumpInfoDict(OPTIONS.info_dict)
   1428 
   1429   # If the caller explicitly specified the device-specific extensions
   1430   # path via -s/--device_specific, use that.  Otherwise, use
   1431   # META/releasetools.py if it is present in the target target_files.
   1432   # Otherwise, take the path of the file from 'tool_extensions' in the
   1433   # info dict and look for that in the local filesystem, relative to
   1434   # the current directory.
   1435 
   1436   if OPTIONS.device_specific is None:
   1437     from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py")
   1438     if os.path.exists(from_input):
   1439       print("(using device-specific extensions from target_files)")
   1440       OPTIONS.device_specific = from_input
   1441     else:
   1442       OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions", None)
   1443 
   1444   if OPTIONS.device_specific is not None:
   1445     OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific)
   1446 
   1447   if OPTIONS.info_dict.get("no_recovery") == "true":
   1448     raise common.ExternalError(
   1449         "--- target build has specified no recovery ---")
   1450 
   1451   # Use the default key to sign the package if not specified with package_key.
   1452   if not OPTIONS.no_signing:
   1453     if OPTIONS.package_key is None:
   1454       OPTIONS.package_key = OPTIONS.info_dict.get(
   1455           "default_system_dev_certificate",
   1456           "build/target/product/security/testkey")
   1457 
   1458   # Set up the output zip. Create a temporary zip file if signing is needed.
   1459   if OPTIONS.no_signing:
   1460     if os.path.exists(args[1]):
   1461       os.unlink(args[1])
   1462     output_zip = zipfile.ZipFile(args[1], "w",
   1463                                  compression=zipfile.ZIP_DEFLATED)
   1464   else:
   1465     temp_zip_file = tempfile.NamedTemporaryFile()
   1466     output_zip = zipfile.ZipFile(temp_zip_file, "w",
   1467                                  compression=zipfile.ZIP_DEFLATED)
   1468 
   1469   # Non A/B OTAs rely on /cache partition to store temporary files.
   1470   cache_size = OPTIONS.info_dict.get("cache_size", None)
   1471   if cache_size is None:
   1472     print("--- can't determine the cache partition size ---")
   1473   OPTIONS.cache_size = cache_size
   1474 
   1475   # Generate a verify package.
   1476   if OPTIONS.gen_verify:
   1477     WriteVerifyPackage(input_zip, output_zip)
   1478 
   1479   # Generate a full OTA.
   1480   elif OPTIONS.incremental_source is None:
   1481     WriteFullOTAPackage(input_zip, output_zip)
   1482 
   1483   # Generate an incremental OTA. It will fall back to generate a full OTA on
   1484   # failure unless no_fallback_to_full is specified.
   1485   else:
   1486     print("unzipping source target-files...")
   1487     OPTIONS.source_tmp, source_zip = common.UnzipTemp(
   1488         OPTIONS.incremental_source,
   1489         UNZIP_PATTERN)
   1490     OPTIONS.target_info_dict = OPTIONS.info_dict
   1491     OPTIONS.source_info_dict = common.LoadInfoDict(source_zip,
   1492                                                    OPTIONS.source_tmp)
   1493     if OPTIONS.verbose:
   1494       print("--- source info ---")
   1495       common.DumpInfoDict(OPTIONS.source_info_dict)
   1496     try:
   1497       WriteBlockIncrementalOTAPackage(input_zip, source_zip, output_zip)
   1498       if OPTIONS.log_diff:
   1499         out_file = open(OPTIONS.log_diff, 'w')
   1500         import target_files_diff
   1501         target_files_diff.recursiveDiff('',
   1502                                         OPTIONS.source_tmp,
   1503                                         OPTIONS.input_tmp,
   1504                                         out_file)
   1505         out_file.close()
   1506     except ValueError:
   1507       if not OPTIONS.fallback_to_full:
   1508         raise
   1509       print("--- failed to build incremental; falling back to full ---")
   1510       OPTIONS.incremental_source = None
   1511       WriteFullOTAPackage(input_zip, output_zip)
   1512 
   1513   common.ZipClose(output_zip)
   1514 
   1515   # Sign the generated zip package unless no_signing is specified.
   1516   if not OPTIONS.no_signing:
   1517     SignOutput(temp_zip_file.name, args[1])
   1518     temp_zip_file.close()
   1519 
   1520   print("done.")
   1521 
   1522 
   1523 if __name__ == '__main__':
   1524   try:
   1525     common.CloseInheritedPipes()
   1526     main(sys.argv[1:])
   1527   except common.ExternalError as e:
   1528     print("\n   ERROR: %s\n" % (e,))
   1529     sys.exit(1)
   1530   finally:
   1531     common.Cleanup()
   1532