| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 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 | """ | 
| Tao Bao | 30df8b4 | 2018-04-23 15:32:53 -0700 | [diff] [blame] | 18 | Given a target-files zipfile, produces an OTA package that installs that build. | 
 | 19 | An incremental OTA is produced if -i is given, otherwise a full OTA is produced. | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 20 |  | 
| Tao Bao | 30df8b4 | 2018-04-23 15:32:53 -0700 | [diff] [blame] | 21 | Usage:  ota_from_target_files [options] input_target_files output_ota_package | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 22 |  | 
| Tao Bao | 30df8b4 | 2018-04-23 15:32:53 -0700 | [diff] [blame] | 23 | Common 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 | 
| Tao Bao | faa8e0b | 2018-04-12 14:31:43 -0700 | [diff] [blame] | 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. | 
| Tao Bao | 30df8b4 | 2018-04-23 15:32:53 -0700 | [diff] [blame] | 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 | 
| Tao Bao | 59cf0c5 | 2019-06-25 10:04:24 -0700 | [diff] [blame] | 41 |       META/misc_info.txt, or "build/make/target/product/security/testkey" if | 
 | 42 |       that value is not specified). | 
| Doug Zongker | afb32ea | 2011-09-22 10:28:04 -0700 | [diff] [blame] | 43 |  | 
 | 44 |       For incremental OTAs, the default value is based on the source | 
 | 45 |       target-file, not the target build. | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 46 |  | 
| Tao Bao | 30df8b4 | 2018-04-23 15:32:53 -0700 | [diff] [blame] | 47 |   --override_timestamp | 
 | 48 |       Intentionally generate an incremental OTA that updates from a newer build | 
| Tao Bao | faa8e0b | 2018-04-12 14:31:43 -0700 | [diff] [blame] | 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. | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 62 |  | 
| Tao Bao | 30df8b4 | 2018-04-23 15:32:53 -0700 | [diff] [blame] | 63 |   --wipe_user_data | 
 | 64 |       Generate an OTA package that will wipe the user data partition when | 
 | 65 |       installed. | 
 | 66 |  | 
| Yifan Hong | 50e7954 | 2018-11-08 17:44:12 -0800 | [diff] [blame] | 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 |  | 
| xunchang | abfa265 | 2019-02-19 16:27:10 -0800 | [diff] [blame] | 74 |   --skip_compatibility_check | 
| Yifan Hong | 9276cf0 | 2019-08-21 16:37:04 -0700 | [diff] [blame] | 75 |       Skip checking compatibility of the input target files package. | 
| xunchang | abfa265 | 2019-02-19 16:27:10 -0800 | [diff] [blame] | 76 |  | 
| xunchang | 1cfe251 | 2019-02-19 14:14:48 -0800 | [diff] [blame] | 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 |  | 
| Tao Bao | 30df8b4 | 2018-04-23 15:32:53 -0700 | [diff] [blame] | 81 | Non-A/B OTA specific options | 
 | 82 |  | 
 | 83 |   -b  (--binary) <file> | 
 | 84 |       Use the given binary as the update-binary in the output package, instead | 
 | 85 |       of the binary in the build's target_files. Use for development only. | 
 | 86 |  | 
 | 87 |   --block | 
 | 88 |       Generate a block-based OTA for non-A/B device. We have deprecated the | 
 | 89 |       support for file-based OTA since O. Block-based OTA will be used by | 
 | 90 |       default for all non-A/B devices. Keeping this flag here to not break | 
 | 91 |       existing callers. | 
 | 92 |  | 
 | 93 |   -e  (--extra_script) <file> | 
 | 94 |       Insert the contents of file at the end of the update script. | 
| Tao Bao | 43078aa | 2015-04-21 14:32:35 -0700 | [diff] [blame] | 95 |  | 
| leozwang | aa6c1a1 | 2015-08-14 10:57:58 -0700 | [diff] [blame] | 96 |   --full_bootloader | 
 | 97 |       Similar to --full_radio. When generating an incremental OTA, always | 
 | 98 |       include a full copy of bootloader image. | 
 | 99 |  | 
| Tao Bao | 30df8b4 | 2018-04-23 15:32:53 -0700 | [diff] [blame] | 100 |   --full_radio | 
 | 101 |       When generating an incremental OTA, always include a full copy of radio | 
 | 102 |       image. This option is only meaningful when -i is specified, because a full | 
 | 103 |       radio is always included in a full OTA if applicable. | 
| Michael Runge | 63f01de | 2014-10-28 19:24:19 -0700 | [diff] [blame] | 104 |  | 
| Tao Bao | 30df8b4 | 2018-04-23 15:32:53 -0700 | [diff] [blame] | 105 |   --log_diff <file> | 
 | 106 |       Generate a log file that shows the differences in the source and target | 
 | 107 |       builds for an incremental package. This option is only meaningful when -i | 
 | 108 |       is specified. | 
 | 109 |  | 
 | 110 |   -o  (--oem_settings) <main_file[,additional_files...]> | 
| Alain Vongsouvanh | 7f804ba | 2017-02-16 13:06:55 -0800 | [diff] [blame] | 111 |       Comma seperated list of files used to specify the expected OEM-specific | 
| Tao Bao | 481bab8 | 2017-12-21 11:23:09 -0800 | [diff] [blame] | 112 |       properties on the OEM partition of the intended device. Multiple expected | 
 | 113 |       values can be used by providing multiple files. Only the first dict will | 
 | 114 |       be used to compute fingerprint, while the rest will be used to assert | 
 | 115 |       OEM-specific properties. | 
| Alain Vongsouvanh | 7f804ba | 2017-02-16 13:06:55 -0800 | [diff] [blame] | 116 |  | 
| Tao Bao | 8608cde | 2016-02-25 19:49:55 -0800 | [diff] [blame] | 117 |   --oem_no_mount | 
| Tao Bao | 30df8b4 | 2018-04-23 15:32:53 -0700 | [diff] [blame] | 118 |       For devices with OEM-specific properties but without an OEM partition, do | 
 | 119 |       not mount the OEM partition in the updater-script. This should be very | 
 | 120 |       rarely used, since it's expected to have a dedicated OEM partition for | 
 | 121 |       OEM-specific properties. Only meaningful when -o is specified. | 
| Tao Bao | 8608cde | 2016-02-25 19:49:55 -0800 | [diff] [blame] | 122 |  | 
| Tao Bao | 30df8b4 | 2018-04-23 15:32:53 -0700 | [diff] [blame] | 123 |   --stash_threshold <float> | 
 | 124 |       Specify the threshold that will be used to compute the maximum allowed | 
 | 125 |       stash size (defaults to 0.8). | 
| Doug Zongker | dbfaae5 | 2009-04-21 17:12:54 -0700 | [diff] [blame] | 126 |  | 
| Tao Bao | 30df8b4 | 2018-04-23 15:32:53 -0700 | [diff] [blame] | 127 |   -t  (--worker_threads) <int> | 
 | 128 |       Specify the number of worker-threads that will be used when generating | 
 | 129 |       patches for incremental updates (defaults to 3). | 
| Tao Bao | 3e6161a | 2017-02-28 11:48:48 -0800 | [diff] [blame] | 130 |  | 
| Tao Bao | 30df8b4 | 2018-04-23 15:32:53 -0700 | [diff] [blame] | 131 |   --verify | 
 | 132 |       Verify the checksums of the updated system and vendor (if any) partitions. | 
 | 133 |       Non-A/B incremental OTAs only. | 
| Doug Zongker | 1c390a2 | 2009-05-14 19:06:36 -0700 | [diff] [blame] | 134 |  | 
| Doug Zongker | 9b23f2c | 2013-11-25 14:44:12 -0800 | [diff] [blame] | 135 |   -2  (--two_step) | 
| Tao Bao | 30df8b4 | 2018-04-23 15:32:53 -0700 | [diff] [blame] | 136 |       Generate a 'two-step' OTA package, where recovery is updated first, so | 
 | 137 |       that any changes made to the system partition are done using the new | 
 | 138 |       recovery (new kernel, etc.). | 
 | 139 |  | 
 | 140 | A/B OTA specific options | 
| Doug Zongker | 9b23f2c | 2013-11-25 14:44:12 -0800 | [diff] [blame] | 141 |  | 
| Tianjie Xu | 1b07983 | 2019-08-28 12:19:23 -0700 | [diff] [blame] | 142 |   --disable_fec_computation | 
 | 143 |       Disable the on device FEC data computation for incremental updates. | 
 | 144 |  | 
| Tao Bao | f7140c0 | 2018-01-30 17:09:24 -0800 | [diff] [blame] | 145 |   --include_secondary | 
 | 146 |       Additionally include the payload for secondary slot images (default: | 
 | 147 |       False). Only meaningful when generating A/B OTAs. | 
 | 148 |  | 
 | 149 |       By default, an A/B OTA package doesn't contain the images for the | 
 | 150 |       secondary slot (e.g. system_other.img). Specifying this flag allows | 
 | 151 |       generating a separate payload that will install secondary slot images. | 
 | 152 |  | 
 | 153 |       Such a package needs to be applied in a two-stage manner, with a reboot | 
 | 154 |       in-between. During the first stage, the updater applies the primary | 
 | 155 |       payload only. Upon finishing, it reboots the device into the newly updated | 
 | 156 |       slot. It then continues to install the secondary payload to the inactive | 
 | 157 |       slot, but without switching the active slot at the end (needs the matching | 
 | 158 |       support in update_engine, i.e. SWITCH_SLOT_ON_REBOOT flag). | 
 | 159 |  | 
 | 160 |       Due to the special install procedure, the secondary payload will be always | 
 | 161 |       generated as a full payload. | 
 | 162 |  | 
| Tao Bao | dea0f8b | 2016-06-20 17:55:06 -0700 | [diff] [blame] | 163 |   --payload_signer <signer> | 
 | 164 |       Specify the signer when signing the payload and metadata for A/B OTAs. | 
 | 165 |       By default (i.e. without this flag), it calls 'openssl pkeyutl' to sign | 
 | 166 |       with the package private key. If the private key cannot be accessed | 
 | 167 |       directly, a payload signer that knows how to do that should be specified. | 
 | 168 |       The signer will be supplied with "-inkey <path_to_key>", | 
 | 169 |       "-in <input_file>" and "-out <output_file>" parameters. | 
| Baligh Uddin | 2abbbd0 | 2016-06-22 12:14:16 -0700 | [diff] [blame] | 170 |  | 
 | 171 |   --payload_signer_args <args> | 
 | 172 |       Specify the arguments needed for payload signer. | 
| Tao Bao | 15a146a | 2018-02-21 16:06:59 -0800 | [diff] [blame] | 173 |  | 
| Tianjie Xu | 21e6deb | 2019-10-07 18:01:00 -0700 | [diff] [blame] | 174 |   --payload_signer_maximum_signature_size <signature_size> | 
 | 175 |       The maximum signature size (in bytes) that would be generated by the given | 
 | 176 |       payload signer. Only meaningful when custom payload signer is specified | 
 | 177 |       via '--payload_signer'. | 
 | 178 |       If the signer uses a RSA key, this should be the number of bytes to | 
 | 179 |       represent the modulus. If it uses an EC key, this is the size of a | 
 | 180 |       DER-encoded ECDSA signature. | 
 | 181 |  | 
| xunchang | 376cc7c | 2019-04-08 23:04:58 -0700 | [diff] [blame] | 182 |   --payload_signer_key_size <key_size> | 
| Tianjie Xu | 21e6deb | 2019-10-07 18:01:00 -0700 | [diff] [blame] | 183 |       Deprecated. Use the '--payload_signer_maximum_signature_size' instead. | 
| xunchang | 376cc7c | 2019-04-08 23:04:58 -0700 | [diff] [blame] | 184 |  | 
| Tao Bao | 15a146a | 2018-02-21 16:06:59 -0800 | [diff] [blame] | 185 |   --skip_postinstall | 
 | 186 |       Skip the postinstall hooks when generating an A/B OTA package (default: | 
 | 187 |       False). Note that this discards ALL the hooks, including non-optional | 
 | 188 |       ones. Should only be used if caller knows it's safe to do so (e.g. all the | 
 | 189 |       postinstall work is to dexopt apps and a data wipe will happen immediately | 
 | 190 |       after). Only meaningful when generating A/B OTAs. | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 191 | """ | 
 | 192 |  | 
| Tao Bao | 89fbb0f | 2017-01-10 10:47:58 -0800 | [diff] [blame] | 193 | from __future__ import print_function | 
 | 194 |  | 
| Tianjie Xu | f67dd80 | 2019-05-20 17:50:36 -0700 | [diff] [blame] | 195 | import collections | 
| Tao Bao | 32fcdab | 2018-10-12 10:30:39 -0700 | [diff] [blame] | 196 | import logging | 
| Doug Zongker | fc44a51 | 2014-08-26 13:10:25 -0700 | [diff] [blame] | 197 | import multiprocessing | 
| Tao Bao | 2dd1c48 | 2017-02-03 16:49:39 -0800 | [diff] [blame] | 198 | import os.path | 
| Baligh Uddin | 2abbbd0 | 2016-06-22 12:14:16 -0700 | [diff] [blame] | 199 | import shlex | 
| Tao Bao | 15a146a | 2018-02-21 16:06:59 -0800 | [diff] [blame] | 200 | import shutil | 
| Tao Bao | 85f1698 | 2018-03-08 16:28:33 -0800 | [diff] [blame] | 201 | import struct | 
| Tao Bao | 481bab8 | 2017-12-21 11:23:09 -0800 | [diff] [blame] | 202 | import sys | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 203 | import zipfile | 
 | 204 |  | 
| Yifan Hong | 9276cf0 | 2019-08-21 16:37:04 -0700 | [diff] [blame] | 205 | import check_target_files_vintf | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 206 | import common | 
| Doug Zongker | c494d7c | 2009-06-18 08:43:44 -0700 | [diff] [blame] | 207 | import edify_generator | 
| Tianjie Xu | 67c7cbb | 2018-08-30 00:32:07 -0700 | [diff] [blame] | 208 | import verity_utils | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 209 |  | 
| Tao Bao | 481bab8 | 2017-12-21 11:23:09 -0800 | [diff] [blame] | 210 | if sys.hexversion < 0x02070000: | 
 | 211 |   print("Python 2.7 or newer is required.", file=sys.stderr) | 
 | 212 |   sys.exit(1) | 
 | 213 |  | 
| Tao Bao | 32fcdab | 2018-10-12 10:30:39 -0700 | [diff] [blame] | 214 | logger = logging.getLogger(__name__) | 
| Tao Bao | 481bab8 | 2017-12-21 11:23:09 -0800 | [diff] [blame] | 215 |  | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 216 | OPTIONS = common.OPTIONS | 
| Doug Zongker | afb32ea | 2011-09-22 10:28:04 -0700 | [diff] [blame] | 217 | OPTIONS.package_key = None | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 218 | OPTIONS.incremental_source = None | 
| Michael Runge | 63f01de | 2014-10-28 19:24:19 -0700 | [diff] [blame] | 219 | OPTIONS.verify = False | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 220 | OPTIONS.patch_threshold = 0.95 | 
| Doug Zongker | dbfaae5 | 2009-04-21 17:12:54 -0700 | [diff] [blame] | 221 | OPTIONS.wipe_user_data = False | 
| Tao Bao | 5d18256 | 2016-02-23 11:38:39 -0800 | [diff] [blame] | 222 | OPTIONS.downgrade = False | 
| Doug Zongker | 1c390a2 | 2009-05-14 19:06:36 -0700 | [diff] [blame] | 223 | OPTIONS.extra_script = None | 
| Doug Zongker | fc44a51 | 2014-08-26 13:10:25 -0700 | [diff] [blame] | 224 | OPTIONS.worker_threads = multiprocessing.cpu_count() // 2 | 
 | 225 | if OPTIONS.worker_threads == 0: | 
 | 226 |   OPTIONS.worker_threads = 1 | 
| Doug Zongker | 9b23f2c | 2013-11-25 14:44:12 -0800 | [diff] [blame] | 227 | OPTIONS.two_step = False | 
| Tao Bao | f7140c0 | 2018-01-30 17:09:24 -0800 | [diff] [blame] | 228 | OPTIONS.include_secondary = False | 
| Takeshi Kanemoto | e153b34 | 2013-11-14 17:20:50 +0900 | [diff] [blame] | 229 | OPTIONS.no_signing = False | 
| Tao Bao | 457cbf6 | 2017-03-06 09:56:01 -0800 | [diff] [blame] | 230 | OPTIONS.block_based = True | 
| Doug Zongker | 2556848 | 2014-03-03 10:21:27 -0800 | [diff] [blame] | 231 | OPTIONS.updater_binary = None | 
| Michael Runge | 6e83611 | 2014-04-15 17:40:21 -0700 | [diff] [blame] | 232 | OPTIONS.oem_source = None | 
| Tao Bao | 8608cde | 2016-02-25 19:49:55 -0800 | [diff] [blame] | 233 | OPTIONS.oem_no_mount = False | 
| Tao Bao | 43078aa | 2015-04-21 14:32:35 -0700 | [diff] [blame] | 234 | OPTIONS.full_radio = False | 
| leozwang | aa6c1a1 | 2015-08-14 10:57:58 -0700 | [diff] [blame] | 235 | OPTIONS.full_bootloader = False | 
| Tao Bao | d47d8e1 | 2015-05-21 14:09:49 -0700 | [diff] [blame] | 236 | # Stash size cannot exceed cache_size * threshold. | 
 | 237 | OPTIONS.cache_size = None | 
 | 238 | OPTIONS.stash_threshold = 0.8 | 
| Tao Bao | d62c603 | 2015-11-30 09:40:20 -0800 | [diff] [blame] | 239 | OPTIONS.log_diff = None | 
| Tao Bao | dea0f8b | 2016-06-20 17:55:06 -0700 | [diff] [blame] | 240 | OPTIONS.payload_signer = None | 
| Baligh Uddin | 2abbbd0 | 2016-06-22 12:14:16 -0700 | [diff] [blame] | 241 | OPTIONS.payload_signer_args = [] | 
| Tianjie Xu | 21e6deb | 2019-10-07 18:01:00 -0700 | [diff] [blame] | 242 | OPTIONS.payload_signer_maximum_signature_size = None | 
| Tao Bao | 5f8ff93 | 2017-03-21 22:35:00 -0700 | [diff] [blame] | 243 | OPTIONS.extracted_input = None | 
| Christian Oder | f63e2cd | 2017-05-01 22:30:15 +0200 | [diff] [blame] | 244 | OPTIONS.key_passwords = [] | 
| Tao Bao | 15a146a | 2018-02-21 16:06:59 -0800 | [diff] [blame] | 245 | OPTIONS.skip_postinstall = False | 
| Yifan Hong | 50e7954 | 2018-11-08 17:44:12 -0800 | [diff] [blame] | 246 | OPTIONS.retrofit_dynamic_partitions = False | 
| xunchang | abfa265 | 2019-02-19 16:27:10 -0800 | [diff] [blame] | 247 | OPTIONS.skip_compatibility_check = False | 
| xunchang | 1cfe251 | 2019-02-19 14:14:48 -0800 | [diff] [blame] | 248 | OPTIONS.output_metadata_path = None | 
| Tianjie Xu | 1b07983 | 2019-08-28 12:19:23 -0700 | [diff] [blame] | 249 | OPTIONS.disable_fec_computation = False | 
| Tao Bao | 15a146a | 2018-02-21 16:06:59 -0800 | [diff] [blame] | 250 |  | 
| Tao Bao | 8dcf738 | 2015-05-21 14:09:49 -0700 | [diff] [blame] | 251 |  | 
| Tao Bao | 2dd1c48 | 2017-02-03 16:49:39 -0800 | [diff] [blame] | 252 | METADATA_NAME = 'META-INF/com/android/metadata' | 
| Tao Bao | 15a146a | 2018-02-21 16:06:59 -0800 | [diff] [blame] | 253 | POSTINSTALL_CONFIG = 'META/postinstall_config.txt' | 
| Yifan Hong | 50e7954 | 2018-11-08 17:44:12 -0800 | [diff] [blame] | 254 | DYNAMIC_PARTITION_INFO = 'META/dynamic_partitions_info.txt' | 
| Yifan Hong | b433eba | 2019-03-06 12:42:53 -0800 | [diff] [blame] | 255 | AB_PARTITIONS = 'META/ab_partitions.txt' | 
| Tao Bao | 0480850 | 2019-07-25 23:11:41 -0700 | [diff] [blame] | 256 | UNZIP_PATTERN = ['IMAGES/*', 'META/*', 'OTA/*', 'RADIO/*'] | 
| Tao Bao | f0c4aa2 | 2018-04-30 20:29:30 -0700 | [diff] [blame] | 257 | # Files to be unzipped for target diffing purpose. | 
 | 258 | TARGET_DIFFING_UNZIP_PATTERN = ['BOOT', 'RECOVERY', 'SYSTEM/*', 'VENDOR/*', | 
 | 259 |                                 'PRODUCT/*', 'SYSTEM_EXT/*', 'ODM/*'] | 
| Yifan Hong | b433eba | 2019-03-06 12:42:53 -0800 | [diff] [blame] | 260 | RETROFIT_DAP_UNZIP_PATTERN = ['OTA/super_*.img', AB_PARTITIONS] | 
| Tao Bao | 3e75946 | 2019-09-17 22:43:11 -0700 | [diff] [blame] | 261 |  | 
 | 262 | # Images to be excluded from secondary payload. We essentially only keep | 
 | 263 | # 'system_other' and bootloader partitions. | 
 | 264 | SECONDARY_PAYLOAD_SKIPPED_IMAGES = [ | 
 | 265 |     'boot', 'dtbo', 'modem', 'odm', 'product', 'radio', 'recovery', | 
 | 266 |     'system_ext', 'vbmeta', 'vbmeta_system', 'vbmeta_vendor', 'vendor'] | 
| Tao Bao | 6b0b2f9 | 2017-03-05 11:38:11 -0800 | [diff] [blame] | 267 |  | 
| Tao Bao | 2dd1c48 | 2017-02-03 16:49:39 -0800 | [diff] [blame] | 268 |  | 
| Tao Bao | fabe083 | 2018-01-17 15:52:28 -0800 | [diff] [blame] | 269 | class PayloadSigner(object): | 
 | 270 |   """A class that wraps the payload signing works. | 
 | 271 |  | 
 | 272 |   When generating a Payload, hashes of the payload and metadata files will be | 
 | 273 |   signed with the device key, either by calling an external payload signer or | 
 | 274 |   by calling openssl with the package key. This class provides a unified | 
 | 275 |   interface, so that callers can just call PayloadSigner.Sign(). | 
 | 276 |  | 
 | 277 |   If an external payload signer has been specified (OPTIONS.payload_signer), it | 
 | 278 |   calls the signer with the provided args (OPTIONS.payload_signer_args). Note | 
 | 279 |   that the signing key should be provided as part of the payload_signer_args. | 
 | 280 |   Otherwise without an external signer, it uses the package key | 
 | 281 |   (OPTIONS.package_key) and calls openssl for the signing works. | 
 | 282 |   """ | 
 | 283 |  | 
 | 284 |   def __init__(self): | 
 | 285 |     if OPTIONS.payload_signer is None: | 
 | 286 |       # Prepare the payload signing key. | 
 | 287 |       private_key = OPTIONS.package_key + OPTIONS.private_key_suffix | 
 | 288 |       pw = OPTIONS.key_passwords[OPTIONS.package_key] | 
 | 289 |  | 
 | 290 |       cmd = ["openssl", "pkcs8", "-in", private_key, "-inform", "DER"] | 
 | 291 |       cmd.extend(["-passin", "pass:" + pw] if pw else ["-nocrypt"]) | 
 | 292 |       signing_key = common.MakeTempFile(prefix="key-", suffix=".key") | 
 | 293 |       cmd.extend(["-out", signing_key]) | 
| Tao Bao | bec89c1 | 2018-10-15 11:53:28 -0700 | [diff] [blame] | 294 |       common.RunAndCheckOutput(cmd, verbose=False) | 
| Tao Bao | fabe083 | 2018-01-17 15:52:28 -0800 | [diff] [blame] | 295 |  | 
 | 296 |       self.signer = "openssl" | 
 | 297 |       self.signer_args = ["pkeyutl", "-sign", "-inkey", signing_key, | 
 | 298 |                           "-pkeyopt", "digest:sha256"] | 
| Tianjie Xu | 21e6deb | 2019-10-07 18:01:00 -0700 | [diff] [blame] | 299 |       self.maximum_signature_size = self._GetMaximumSignatureSizeInBytes( | 
 | 300 |           signing_key) | 
| Tao Bao | fabe083 | 2018-01-17 15:52:28 -0800 | [diff] [blame] | 301 |     else: | 
 | 302 |       self.signer = OPTIONS.payload_signer | 
 | 303 |       self.signer_args = OPTIONS.payload_signer_args | 
| Tianjie Xu | 21e6deb | 2019-10-07 18:01:00 -0700 | [diff] [blame] | 304 |       if OPTIONS.payload_signer_maximum_signature_size: | 
 | 305 |         self.maximum_signature_size = int( | 
 | 306 |             OPTIONS.payload_signer_maximum_signature_size) | 
| xunchang | 376cc7c | 2019-04-08 23:04:58 -0700 | [diff] [blame] | 307 |       else: | 
| Tianjie Xu | 21e6deb | 2019-10-07 18:01:00 -0700 | [diff] [blame] | 308 |         # The legacy config uses RSA2048 keys. | 
 | 309 |         logger.warning("The maximum signature size for payload signer is not" | 
 | 310 |                        " set, default to 256 bytes.") | 
 | 311 |         self.maximum_signature_size = 256 | 
| xunchang | 376cc7c | 2019-04-08 23:04:58 -0700 | [diff] [blame] | 312 |  | 
 | 313 |   @staticmethod | 
| Tianjie Xu | 21e6deb | 2019-10-07 18:01:00 -0700 | [diff] [blame] | 314 |   def _GetMaximumSignatureSizeInBytes(signing_key): | 
 | 315 |     out_signature_size_file = common.MakeTempFile("signature_size") | 
 | 316 |     cmd = ["delta_generator", "--out_maximum_signature_size_file={}".format( | 
 | 317 |         out_signature_size_file), "--private_key={}".format(signing_key)] | 
 | 318 |     common.RunAndCheckOutput(cmd) | 
 | 319 |     with open(out_signature_size_file) as f: | 
 | 320 |       signature_size = f.read().rstrip() | 
 | 321 |     logger.info("% outputs the maximum signature size: %", cmd[0], | 
 | 322 |                 signature_size) | 
 | 323 |     return int(signature_size) | 
| Tao Bao | fabe083 | 2018-01-17 15:52:28 -0800 | [diff] [blame] | 324 |  | 
 | 325 |   def Sign(self, in_file): | 
 | 326 |     """Signs the given input file. Returns the output filename.""" | 
 | 327 |     out_file = common.MakeTempFile(prefix="signed-", suffix=".bin") | 
 | 328 |     cmd = [self.signer] + self.signer_args + ['-in', in_file, '-out', out_file] | 
| Tao Bao | 718faed | 2019-08-02 13:24:19 -0700 | [diff] [blame] | 329 |     common.RunAndCheckOutput(cmd) | 
| Tao Bao | fabe083 | 2018-01-17 15:52:28 -0800 | [diff] [blame] | 330 |     return out_file | 
 | 331 |  | 
 | 332 |  | 
| Tao Bao | 40b1882 | 2018-01-30 18:19:04 -0800 | [diff] [blame] | 333 | class Payload(object): | 
 | 334 |   """Manages the creation and the signing of an A/B OTA Payload.""" | 
 | 335 |  | 
 | 336 |   PAYLOAD_BIN = 'payload.bin' | 
 | 337 |   PAYLOAD_PROPERTIES_TXT = 'payload_properties.txt' | 
| Tao Bao | f7140c0 | 2018-01-30 17:09:24 -0800 | [diff] [blame] | 338 |   SECONDARY_PAYLOAD_BIN = 'secondary/payload.bin' | 
 | 339 |   SECONDARY_PAYLOAD_PROPERTIES_TXT = 'secondary/payload_properties.txt' | 
| Tao Bao | 40b1882 | 2018-01-30 18:19:04 -0800 | [diff] [blame] | 340 |  | 
| Tao Bao | 667ff57 | 2018-02-10 00:02:40 -0800 | [diff] [blame] | 341 |   def __init__(self, secondary=False): | 
 | 342 |     """Initializes a Payload instance. | 
 | 343 |  | 
 | 344 |     Args: | 
 | 345 |       secondary: Whether it's generating a secondary payload (default: False). | 
 | 346 |     """ | 
| Tao Bao | 40b1882 | 2018-01-30 18:19:04 -0800 | [diff] [blame] | 347 |     self.payload_file = None | 
 | 348 |     self.payload_properties = None | 
| Tao Bao | 667ff57 | 2018-02-10 00:02:40 -0800 | [diff] [blame] | 349 |     self.secondary = secondary | 
| Tao Bao | 40b1882 | 2018-01-30 18:19:04 -0800 | [diff] [blame] | 350 |  | 
| Tao Bao | f0c4aa2 | 2018-04-30 20:29:30 -0700 | [diff] [blame] | 351 |   def _Run(self, cmd):  # pylint: disable=no-self-use | 
| Tao Bao | 718faed | 2019-08-02 13:24:19 -0700 | [diff] [blame] | 352 |     # Don't pipe (buffer) the output if verbose is set. Let | 
 | 353 |     # brillo_update_payload write to stdout/stderr directly, so its progress can | 
 | 354 |     # be monitored. | 
 | 355 |     if OPTIONS.verbose: | 
 | 356 |       common.RunAndCheckOutput(cmd, stdout=None, stderr=None) | 
 | 357 |     else: | 
 | 358 |       common.RunAndCheckOutput(cmd) | 
 | 359 |  | 
| Tao Bao | 40b1882 | 2018-01-30 18:19:04 -0800 | [diff] [blame] | 360 |   def Generate(self, target_file, source_file=None, additional_args=None): | 
 | 361 |     """Generates a payload from the given target-files zip(s). | 
 | 362 |  | 
 | 363 |     Args: | 
 | 364 |       target_file: The filename of the target build target-files zip. | 
 | 365 |       source_file: The filename of the source build target-files zip; or None if | 
 | 366 |           generating a full OTA. | 
 | 367 |       additional_args: A list of additional args that should be passed to | 
 | 368 |           brillo_update_payload script; or None. | 
 | 369 |     """ | 
 | 370 |     if additional_args is None: | 
 | 371 |       additional_args = [] | 
 | 372 |  | 
 | 373 |     payload_file = common.MakeTempFile(prefix="payload-", suffix=".bin") | 
 | 374 |     cmd = ["brillo_update_payload", "generate", | 
 | 375 |            "--payload", payload_file, | 
 | 376 |            "--target_image", target_file] | 
 | 377 |     if source_file is not None: | 
 | 378 |       cmd.extend(["--source_image", source_file]) | 
| Tianjie Xu | 1b07983 | 2019-08-28 12:19:23 -0700 | [diff] [blame] | 379 |       if OPTIONS.disable_fec_computation: | 
 | 380 |         cmd.extend(["--disable_fec_computation", "true"]) | 
| Tao Bao | 40b1882 | 2018-01-30 18:19:04 -0800 | [diff] [blame] | 381 |     cmd.extend(additional_args) | 
| Tao Bao | 718faed | 2019-08-02 13:24:19 -0700 | [diff] [blame] | 382 |     self._Run(cmd) | 
| Tao Bao | 40b1882 | 2018-01-30 18:19:04 -0800 | [diff] [blame] | 383 |  | 
 | 384 |     self.payload_file = payload_file | 
 | 385 |     self.payload_properties = None | 
 | 386 |  | 
 | 387 |   def Sign(self, payload_signer): | 
 | 388 |     """Generates and signs the hashes of the payload and metadata. | 
 | 389 |  | 
 | 390 |     Args: | 
 | 391 |       payload_signer: A PayloadSigner() instance that serves the signing work. | 
 | 392 |  | 
 | 393 |     Raises: | 
 | 394 |       AssertionError: On any failure when calling brillo_update_payload script. | 
 | 395 |     """ | 
 | 396 |     assert isinstance(payload_signer, PayloadSigner) | 
 | 397 |  | 
 | 398 |     # 1. Generate hashes of the payload and metadata files. | 
 | 399 |     payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin") | 
 | 400 |     metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin") | 
 | 401 |     cmd = ["brillo_update_payload", "hash", | 
 | 402 |            "--unsigned_payload", self.payload_file, | 
| Tianjie Xu | 21e6deb | 2019-10-07 18:01:00 -0700 | [diff] [blame] | 403 |            "--signature_size", str(payload_signer.maximum_signature_size), | 
| Tao Bao | 40b1882 | 2018-01-30 18:19:04 -0800 | [diff] [blame] | 404 |            "--metadata_hash_file", metadata_sig_file, | 
 | 405 |            "--payload_hash_file", payload_sig_file] | 
| Tao Bao | 718faed | 2019-08-02 13:24:19 -0700 | [diff] [blame] | 406 |     self._Run(cmd) | 
| Tao Bao | 40b1882 | 2018-01-30 18:19:04 -0800 | [diff] [blame] | 407 |  | 
 | 408 |     # 2. Sign the hashes. | 
 | 409 |     signed_payload_sig_file = payload_signer.Sign(payload_sig_file) | 
 | 410 |     signed_metadata_sig_file = payload_signer.Sign(metadata_sig_file) | 
 | 411 |  | 
 | 412 |     # 3. Insert the signatures back into the payload file. | 
 | 413 |     signed_payload_file = common.MakeTempFile(prefix="signed-payload-", | 
 | 414 |                                               suffix=".bin") | 
 | 415 |     cmd = ["brillo_update_payload", "sign", | 
 | 416 |            "--unsigned_payload", self.payload_file, | 
 | 417 |            "--payload", signed_payload_file, | 
| Tianjie Xu | 21e6deb | 2019-10-07 18:01:00 -0700 | [diff] [blame] | 418 |            "--signature_size", str(payload_signer.maximum_signature_size), | 
| Tao Bao | 40b1882 | 2018-01-30 18:19:04 -0800 | [diff] [blame] | 419 |            "--metadata_signature_file", signed_metadata_sig_file, | 
 | 420 |            "--payload_signature_file", signed_payload_sig_file] | 
| Tao Bao | 718faed | 2019-08-02 13:24:19 -0700 | [diff] [blame] | 421 |     self._Run(cmd) | 
| Tao Bao | 40b1882 | 2018-01-30 18:19:04 -0800 | [diff] [blame] | 422 |  | 
 | 423 |     # 4. Dump the signed payload properties. | 
 | 424 |     properties_file = common.MakeTempFile(prefix="payload-properties-", | 
 | 425 |                                           suffix=".txt") | 
 | 426 |     cmd = ["brillo_update_payload", "properties", | 
 | 427 |            "--payload", signed_payload_file, | 
 | 428 |            "--properties_file", properties_file] | 
| Tao Bao | 718faed | 2019-08-02 13:24:19 -0700 | [diff] [blame] | 429 |     self._Run(cmd) | 
| Tao Bao | 40b1882 | 2018-01-30 18:19:04 -0800 | [diff] [blame] | 430 |  | 
| Tao Bao | 667ff57 | 2018-02-10 00:02:40 -0800 | [diff] [blame] | 431 |     if self.secondary: | 
 | 432 |       with open(properties_file, "a") as f: | 
 | 433 |         f.write("SWITCH_SLOT_ON_REBOOT=0\n") | 
 | 434 |  | 
| Tao Bao | 40b1882 | 2018-01-30 18:19:04 -0800 | [diff] [blame] | 435 |     if OPTIONS.wipe_user_data: | 
 | 436 |       with open(properties_file, "a") as f: | 
 | 437 |         f.write("POWERWASH=1\n") | 
 | 438 |  | 
 | 439 |     self.payload_file = signed_payload_file | 
 | 440 |     self.payload_properties = properties_file | 
 | 441 |  | 
| Tao Bao | 667ff57 | 2018-02-10 00:02:40 -0800 | [diff] [blame] | 442 |   def WriteToZip(self, output_zip): | 
| Tao Bao | 40b1882 | 2018-01-30 18:19:04 -0800 | [diff] [blame] | 443 |     """Writes the payload to the given zip. | 
 | 444 |  | 
 | 445 |     Args: | 
 | 446 |       output_zip: The output ZipFile instance. | 
 | 447 |     """ | 
 | 448 |     assert self.payload_file is not None | 
 | 449 |     assert self.payload_properties is not None | 
 | 450 |  | 
| Tao Bao | 667ff57 | 2018-02-10 00:02:40 -0800 | [diff] [blame] | 451 |     if self.secondary: | 
| Tao Bao | f7140c0 | 2018-01-30 17:09:24 -0800 | [diff] [blame] | 452 |       payload_arcname = Payload.SECONDARY_PAYLOAD_BIN | 
 | 453 |       payload_properties_arcname = Payload.SECONDARY_PAYLOAD_PROPERTIES_TXT | 
 | 454 |     else: | 
 | 455 |       payload_arcname = Payload.PAYLOAD_BIN | 
 | 456 |       payload_properties_arcname = Payload.PAYLOAD_PROPERTIES_TXT | 
 | 457 |  | 
| Tao Bao | 40b1882 | 2018-01-30 18:19:04 -0800 | [diff] [blame] | 458 |     # Add the signed payload file and properties into the zip. In order to | 
 | 459 |     # support streaming, we pack them as ZIP_STORED. So these entries can be | 
 | 460 |     # read directly with the offset and length pairs. | 
| Tao Bao | f7140c0 | 2018-01-30 17:09:24 -0800 | [diff] [blame] | 461 |     common.ZipWrite(output_zip, self.payload_file, arcname=payload_arcname, | 
| Tao Bao | 40b1882 | 2018-01-30 18:19:04 -0800 | [diff] [blame] | 462 |                     compress_type=zipfile.ZIP_STORED) | 
 | 463 |     common.ZipWrite(output_zip, self.payload_properties, | 
| Tao Bao | f7140c0 | 2018-01-30 17:09:24 -0800 | [diff] [blame] | 464 |                     arcname=payload_properties_arcname, | 
| Tao Bao | 40b1882 | 2018-01-30 18:19:04 -0800 | [diff] [blame] | 465 |                     compress_type=zipfile.ZIP_STORED) | 
 | 466 |  | 
 | 467 |  | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 468 | def SignOutput(temp_zip_name, output_zip_name): | 
| Christian Oder | f63e2cd | 2017-05-01 22:30:15 +0200 | [diff] [blame] | 469 |   pw = OPTIONS.key_passwords[OPTIONS.package_key] | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 470 |  | 
| Doug Zongker | 951495f | 2009-08-14 12:44:19 -0700 | [diff] [blame] | 471 |   common.SignFile(temp_zip_name, output_zip_name, OPTIONS.package_key, pw, | 
 | 472 |                   whole_file=True) | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 473 |  | 
 | 474 |  | 
| Tao Bao | 481bab8 | 2017-12-21 11:23:09 -0800 | [diff] [blame] | 475 | def _LoadOemDicts(oem_source): | 
| Alain Vongsouvanh | 7f804ba | 2017-02-16 13:06:55 -0800 | [diff] [blame] | 476 |   """Returns the list of loaded OEM properties dict.""" | 
| Tao Bao | 481bab8 | 2017-12-21 11:23:09 -0800 | [diff] [blame] | 477 |   if not oem_source: | 
 | 478 |     return None | 
 | 479 |  | 
| Alain Vongsouvanh | 7f804ba | 2017-02-16 13:06:55 -0800 | [diff] [blame] | 480 |   oem_dicts = [] | 
| Tao Bao | 481bab8 | 2017-12-21 11:23:09 -0800 | [diff] [blame] | 481 |   for oem_file in oem_source: | 
 | 482 |     with open(oem_file) as fp: | 
 | 483 |       oem_dicts.append(common.LoadDictionaryFromLines(fp.readlines())) | 
| Alain Vongsouvanh | 7f804ba | 2017-02-16 13:06:55 -0800 | [diff] [blame] | 484 |   return oem_dicts | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 485 |  | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 486 |  | 
| Tao Bao | d42e97e | 2016-11-30 12:11:57 -0800 | [diff] [blame] | 487 | def _WriteRecoveryImageToBoot(script, output_zip): | 
 | 488 |   """Find and write recovery image to /boot in two-step OTA. | 
 | 489 |  | 
 | 490 |   In two-step OTAs, we write recovery image to /boot as the first step so that | 
 | 491 |   we can reboot to there and install a new recovery image to /recovery. | 
 | 492 |   A special "recovery-two-step.img" will be preferred, which encodes the correct | 
 | 493 |   path of "/boot". Otherwise the device may show "device is corrupt" message | 
 | 494 |   when booting into /boot. | 
 | 495 |  | 
 | 496 |   Fall back to using the regular recovery.img if the two-step recovery image | 
 | 497 |   doesn't exist. Note that rebuilding the special image at this point may be | 
 | 498 |   infeasible, because we don't have the desired boot signer and keys when | 
 | 499 |   calling ota_from_target_files.py. | 
 | 500 |   """ | 
 | 501 |  | 
 | 502 |   recovery_two_step_img_name = "recovery-two-step.img" | 
 | 503 |   recovery_two_step_img_path = os.path.join( | 
| Tao Bao | 0480850 | 2019-07-25 23:11:41 -0700 | [diff] [blame] | 504 |       OPTIONS.input_tmp, "OTA", recovery_two_step_img_name) | 
| Tao Bao | d42e97e | 2016-11-30 12:11:57 -0800 | [diff] [blame] | 505 |   if os.path.exists(recovery_two_step_img_path): | 
| Tao Bao | 0480850 | 2019-07-25 23:11:41 -0700 | [diff] [blame] | 506 |     common.ZipWrite( | 
 | 507 |         output_zip, | 
 | 508 |         recovery_two_step_img_path, | 
 | 509 |         arcname=recovery_two_step_img_name) | 
| Tao Bao | 32fcdab | 2018-10-12 10:30:39 -0700 | [diff] [blame] | 510 |     logger.info( | 
 | 511 |         "two-step package: using %s in stage 1/3", recovery_two_step_img_name) | 
| Tao Bao | d42e97e | 2016-11-30 12:11:57 -0800 | [diff] [blame] | 512 |     script.WriteRawImage("/boot", recovery_two_step_img_name) | 
 | 513 |   else: | 
| Tao Bao | 32fcdab | 2018-10-12 10:30:39 -0700 | [diff] [blame] | 514 |     logger.info("two-step package: using recovery.img in stage 1/3") | 
| Tao Bao | d42e97e | 2016-11-30 12:11:57 -0800 | [diff] [blame] | 515 |     # The "recovery.img" entry has been written into package earlier. | 
 | 516 |     script.WriteRawImage("/boot", "recovery.img") | 
 | 517 |  | 
 | 518 |  | 
| Bill Peckham | e868aec | 2019-09-17 17:06:47 -0700 | [diff] [blame] | 519 | def HasRecoveryPatch(target_files_zip, info_dict): | 
 | 520 |   board_uses_vendorimage = info_dict.get("board_uses_vendorimage") == "true" | 
 | 521 |  | 
 | 522 |   if board_uses_vendorimage: | 
 | 523 |     target_files_dir = "VENDOR" | 
 | 524 |   else: | 
 | 525 |     target_files_dir = "SYSTEM/vendor" | 
 | 526 |  | 
 | 527 |   patch = "%s/recovery-from-boot.p" % target_files_dir | 
 | 528 |   img = "%s/etc/recovery.img" %target_files_dir | 
 | 529 |  | 
| Tao Bao | f2cffbd | 2015-07-22 12:33:18 -0700 | [diff] [blame] | 530 |   namelist = [name for name in target_files_zip.namelist()] | 
| Bill Peckham | e868aec | 2019-09-17 17:06:47 -0700 | [diff] [blame] | 531 |   return (patch in namelist or img in namelist) | 
| Doug Zongker | 73ef825 | 2009-07-23 15:12:53 -0700 | [diff] [blame] | 532 |  | 
| Tao Bao | 457cbf6 | 2017-03-06 09:56:01 -0800 | [diff] [blame] | 533 |  | 
| Yifan Hong | 51d3756 | 2019-04-23 17:06:46 -0700 | [diff] [blame] | 534 | def HasPartition(target_files_zip, partition): | 
| Doug Zongker | c8b4e84 | 2014-06-16 15:16:31 -0700 | [diff] [blame] | 535 |   try: | 
| Yifan Hong | 51d3756 | 2019-04-23 17:06:46 -0700 | [diff] [blame] | 536 |     target_files_zip.getinfo(partition.upper() + "/") | 
| Doug Zongker | c8b4e84 | 2014-06-16 15:16:31 -0700 | [diff] [blame] | 537 |     return True | 
 | 538 |   except KeyError: | 
 | 539 |     return False | 
 | 540 |  | 
| Tao Bao | 457cbf6 | 2017-03-06 09:56:01 -0800 | [diff] [blame] | 541 |  | 
| Yifan Hong | 9276cf0 | 2019-08-21 16:37:04 -0700 | [diff] [blame] | 542 | def HasTrebleEnabled(target_files, target_info): | 
 | 543 |   def HasVendorPartition(target_files): | 
 | 544 |     if os.path.isdir(target_files): | 
 | 545 |       return os.path.isdir(os.path.join(target_files, "VENDOR")) | 
 | 546 |     if zipfile.is_zipfile(target_files): | 
 | 547 |       return HasPartition(zipfile.ZipFile(target_files), "vendor") | 
 | 548 |     raise ValueError("Unknown target_files argument") | 
| Yifan Hong | 51d3756 | 2019-04-23 17:06:46 -0700 | [diff] [blame] | 549 |  | 
| Yifan Hong | 9276cf0 | 2019-08-21 16:37:04 -0700 | [diff] [blame] | 550 |   return (HasVendorPartition(target_files) and | 
| Tao Bao | 481bab8 | 2017-12-21 11:23:09 -0800 | [diff] [blame] | 551 |           target_info.GetBuildProp("ro.treble.enabled") == "true") | 
| Tao Bao | bcd1d16 | 2017-08-26 13:10:26 -0700 | [diff] [blame] | 552 |  | 
 | 553 |  | 
| Tao Bao | 481bab8 | 2017-12-21 11:23:09 -0800 | [diff] [blame] | 554 | def WriteFingerprintAssertion(script, target_info, source_info): | 
 | 555 |   source_oem_props = source_info.oem_props | 
 | 556 |   target_oem_props = target_info.oem_props | 
| Michael Runge | 6e83611 | 2014-04-15 17:40:21 -0700 | [diff] [blame] | 557 |  | 
| Tao Bao | 481bab8 | 2017-12-21 11:23:09 -0800 | [diff] [blame] | 558 |   if source_oem_props is None and target_oem_props is None: | 
 | 559 |     script.AssertSomeFingerprint( | 
 | 560 |         source_info.fingerprint, target_info.fingerprint) | 
 | 561 |   elif source_oem_props is not None and target_oem_props is not None: | 
 | 562 |     script.AssertSomeThumbprint( | 
 | 563 |         target_info.GetBuildProp("ro.build.thumbprint"), | 
 | 564 |         source_info.GetBuildProp("ro.build.thumbprint")) | 
 | 565 |   elif source_oem_props is None and target_oem_props is not None: | 
 | 566 |     script.AssertFingerprintOrThumbprint( | 
 | 567 |         source_info.fingerprint, | 
 | 568 |         target_info.GetBuildProp("ro.build.thumbprint")) | 
 | 569 |   else: | 
 | 570 |     script.AssertFingerprintOrThumbprint( | 
 | 571 |         target_info.fingerprint, | 
 | 572 |         source_info.GetBuildProp("ro.build.thumbprint")) | 
| Doug Zongker | 73ef825 | 2009-07-23 15:12:53 -0700 | [diff] [blame] | 573 |  | 
| Doug Zongker | fc44a51 | 2014-08-26 13:10:25 -0700 | [diff] [blame] | 574 |  | 
| Yifan Hong | 9276cf0 | 2019-08-21 16:37:04 -0700 | [diff] [blame] | 575 | def CheckVintfIfTrebleEnabled(target_files, target_info): | 
 | 576 |   """Checks compatibility info of the input target files. | 
| Tao Bao | 21803d3 | 2017-04-19 10:16:09 -0700 | [diff] [blame] | 577 |  | 
| Yifan Hong | 9276cf0 | 2019-08-21 16:37:04 -0700 | [diff] [blame] | 578 |   Metadata used for compatibility verification is retrieved from target_zip. | 
| Tao Bao | 21803d3 | 2017-04-19 10:16:09 -0700 | [diff] [blame] | 579 |  | 
| Yifan Hong | 9276cf0 | 2019-08-21 16:37:04 -0700 | [diff] [blame] | 580 |   Compatibility should only be checked for devices that have enabled | 
| Tao Bao | bcd1d16 | 2017-08-26 13:10:26 -0700 | [diff] [blame] | 581 |   Treble support. | 
| Tao Bao | 21803d3 | 2017-04-19 10:16:09 -0700 | [diff] [blame] | 582 |  | 
 | 583 |   Args: | 
| Yifan Hong | 9276cf0 | 2019-08-21 16:37:04 -0700 | [diff] [blame] | 584 |     target_files: Path to zip file containing the source files to be included | 
 | 585 |         for OTA. Can also be the path to extracted directory. | 
| Tao Bao | 481bab8 | 2017-12-21 11:23:09 -0800 | [diff] [blame] | 586 |     target_info: The BuildInfo instance that holds the target build info. | 
| Tao Bao | 21803d3 | 2017-04-19 10:16:09 -0700 | [diff] [blame] | 587 |   """ | 
 | 588 |  | 
| Tao Bao | bcd1d16 | 2017-08-26 13:10:26 -0700 | [diff] [blame] | 589 |   # Will only proceed if the target has enabled the Treble support (as well as | 
 | 590 |   # having a /vendor partition). | 
| Yifan Hong | 9276cf0 | 2019-08-21 16:37:04 -0700 | [diff] [blame] | 591 |   if not HasTrebleEnabled(target_files, target_info): | 
| Tao Bao | bcd1d16 | 2017-08-26 13:10:26 -0700 | [diff] [blame] | 592 |     return | 
 | 593 |  | 
| xunchang | abfa265 | 2019-02-19 16:27:10 -0800 | [diff] [blame] | 594 |   # Skip adding the compatibility package as a workaround for b/114240221. The | 
 | 595 |   # compatibility will always fail on devices without qualified kernels. | 
 | 596 |   if OPTIONS.skip_compatibility_check: | 
 | 597 |     return | 
 | 598 |  | 
| Yifan Hong | 9276cf0 | 2019-08-21 16:37:04 -0700 | [diff] [blame] | 599 |   if not check_target_files_vintf.CheckVintf(target_files, target_info): | 
 | 600 |     raise RuntimeError("VINTF compatibility check failed") | 
| Tao Bao | 21803d3 | 2017-04-19 10:16:09 -0700 | [diff] [blame] | 601 |  | 
 | 602 |  | 
| Tianjie Xu | f67dd80 | 2019-05-20 17:50:36 -0700 | [diff] [blame] | 603 | def GetBlockDifferences(target_zip, source_zip, target_info, source_info, | 
 | 604 |                         device_specific): | 
 | 605 |   """Returns a ordered dict of block differences with partition name as key.""" | 
 | 606 |  | 
 | 607 |   def GetIncrementalBlockDifferenceForPartition(name): | 
 | 608 |     if not HasPartition(source_zip, name): | 
 | 609 |       raise RuntimeError("can't generate incremental that adds {}".format(name)) | 
 | 610 |  | 
 | 611 |     partition_src = common.GetUserImage(name, OPTIONS.source_tmp, source_zip, | 
 | 612 |                                         info_dict=source_info, | 
 | 613 |                                         allow_shared_blocks=allow_shared_blocks) | 
 | 614 |  | 
 | 615 |     hashtree_info_generator = verity_utils.CreateHashtreeInfoGenerator( | 
 | 616 |         name, 4096, target_info) | 
 | 617 |     partition_tgt = common.GetUserImage(name, OPTIONS.target_tmp, target_zip, | 
 | 618 |                                         info_dict=target_info, | 
 | 619 |                                         allow_shared_blocks=allow_shared_blocks, | 
 | 620 |                                         hashtree_info_generator= | 
 | 621 |                                         hashtree_info_generator) | 
 | 622 |  | 
 | 623 |     # Check the first block of the source system partition for remount R/W only | 
 | 624 |     # if the filesystem is ext4. | 
 | 625 |     partition_source_info = source_info["fstab"]["/" + name] | 
 | 626 |     check_first_block = partition_source_info.fs_type == "ext4" | 
 | 627 |     # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be | 
 | 628 |     # in zip formats. However with squashfs, a) all files are compressed in LZ4; | 
 | 629 |     # b) the blocks listed in block map may not contain all the bytes for a | 
 | 630 |     # given file (because they're rounded to be 4K-aligned). | 
 | 631 |     partition_target_info = target_info["fstab"]["/" + name] | 
 | 632 |     disable_imgdiff = (partition_source_info.fs_type == "squashfs" or | 
 | 633 |                        partition_target_info.fs_type == "squashfs") | 
 | 634 |     return common.BlockDifference(name, partition_src, partition_tgt, | 
 | 635 |                                   check_first_block, | 
 | 636 |                                   version=blockimgdiff_version, | 
 | 637 |                                   disable_imgdiff=disable_imgdiff) | 
 | 638 |  | 
 | 639 |   if source_zip: | 
 | 640 |     # See notes in common.GetUserImage() | 
 | 641 |     allow_shared_blocks = (source_info.get('ext4_share_dup_blocks') == "true" or | 
 | 642 |                            target_info.get('ext4_share_dup_blocks') == "true") | 
 | 643 |     blockimgdiff_version = max( | 
 | 644 |         int(i) for i in target_info.get( | 
 | 645 |             "blockimgdiff_versions", "1").split(",")) | 
 | 646 |     assert blockimgdiff_version >= 3 | 
 | 647 |  | 
 | 648 |   block_diff_dict = collections.OrderedDict() | 
 | 649 |   partition_names = ["system", "vendor", "product", "odm", "system_ext"] | 
 | 650 |   for partition in partition_names: | 
 | 651 |     if not HasPartition(target_zip, partition): | 
 | 652 |       continue | 
 | 653 |     # Full OTA update. | 
 | 654 |     if not source_zip: | 
 | 655 |       tgt = common.GetUserImage(partition, OPTIONS.input_tmp, target_zip, | 
 | 656 |                                 info_dict=target_info, | 
 | 657 |                                 reset_file_map=True) | 
 | 658 |       block_diff_dict[partition] = common.BlockDifference(partition, tgt, | 
 | 659 |                                                           src=None) | 
 | 660 |     # Incremental OTA update. | 
 | 661 |     else: | 
 | 662 |       block_diff_dict[partition] = GetIncrementalBlockDifferenceForPartition( | 
 | 663 |           partition) | 
 | 664 |   assert "system" in block_diff_dict | 
 | 665 |  | 
 | 666 |   # Get the block diffs from the device specific script. If there is a | 
 | 667 |   # duplicate block diff for a partition, ignore the diff in the generic script | 
 | 668 |   # and use the one in the device specific script instead. | 
 | 669 |   if source_zip: | 
 | 670 |     device_specific_diffs = device_specific.IncrementalOTA_GetBlockDifferences() | 
 | 671 |     function_name = "IncrementalOTA_GetBlockDifferences" | 
 | 672 |   else: | 
 | 673 |     device_specific_diffs = device_specific.FullOTA_GetBlockDifferences() | 
 | 674 |     function_name = "FullOTA_GetBlockDifferences" | 
 | 675 |  | 
 | 676 |   if device_specific_diffs: | 
 | 677 |     assert all(isinstance(diff, common.BlockDifference) | 
 | 678 |                for diff in device_specific_diffs), \ | 
 | 679 |         "{} is not returning a list of BlockDifference objects".format( | 
 | 680 |             function_name) | 
 | 681 |     for diff in device_specific_diffs: | 
 | 682 |       if diff.partition in block_diff_dict: | 
 | 683 |         logger.warning("Duplicate block difference found. Device specific block" | 
 | 684 |                        " diff for partition '%s' overrides the one in generic" | 
 | 685 |                        " script.", diff.partition) | 
 | 686 |       block_diff_dict[diff.partition] = diff | 
 | 687 |  | 
 | 688 |   return block_diff_dict | 
 | 689 |  | 
 | 690 |  | 
| Tao Bao | 491d7e2 | 2018-02-21 13:17:22 -0800 | [diff] [blame] | 691 | def WriteFullOTAPackage(input_zip, output_file): | 
| Tao Bao | 1c320f8 | 2019-10-04 23:25:12 -0700 | [diff] [blame] | 692 |   target_info = common.BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts) | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 693 |  | 
| Tao Bao | 481bab8 | 2017-12-21 11:23:09 -0800 | [diff] [blame] | 694 |   # We don't know what version it will be installed on top of. We expect the API | 
 | 695 |   # just won't change very often. Similarly for fstab, it might have changed in | 
 | 696 |   # the target build. | 
 | 697 |   target_api_version = target_info["recovery_api_version"] | 
 | 698 |   script = edify_generator.EdifyGenerator(target_api_version, target_info) | 
| Michael Runge | 6e83611 | 2014-04-15 17:40:21 -0700 | [diff] [blame] | 699 |  | 
| Tao Bao | 481bab8 | 2017-12-21 11:23:09 -0800 | [diff] [blame] | 700 |   if target_info.oem_props and not OPTIONS.oem_no_mount: | 
 | 701 |     target_info.WriteMountOemScript(script) | 
 | 702 |  | 
| Tao Bao | df3a48b | 2018-01-10 16:30:43 -0800 | [diff] [blame] | 703 |   metadata = GetPackageMetadata(target_info) | 
| Doug Zongker | 2ea2106 | 2010-04-28 16:05:21 -0700 | [diff] [blame] | 704 |  | 
| Tao Bao | 491d7e2 | 2018-02-21 13:17:22 -0800 | [diff] [blame] | 705 |   if not OPTIONS.no_signing: | 
 | 706 |     staging_file = common.MakeTempFile(suffix='.zip') | 
 | 707 |   else: | 
 | 708 |     staging_file = output_file | 
 | 709 |  | 
 | 710 |   output_zip = zipfile.ZipFile( | 
 | 711 |       staging_file, "w", compression=zipfile.ZIP_DEFLATED) | 
 | 712 |  | 
| Doug Zongker | 05d3dea | 2009-06-22 11:32:31 -0700 | [diff] [blame] | 713 |   device_specific = common.DeviceSpecificParams( | 
 | 714 |       input_zip=input_zip, | 
| Tao Bao | 481bab8 | 2017-12-21 11:23:09 -0800 | [diff] [blame] | 715 |       input_version=target_api_version, | 
| Doug Zongker | 05d3dea | 2009-06-22 11:32:31 -0700 | [diff] [blame] | 716 |       output_zip=output_zip, | 
 | 717 |       script=script, | 
| Doug Zongker | 2ea2106 | 2010-04-28 16:05:21 -0700 | [diff] [blame] | 718 |       input_tmp=OPTIONS.input_tmp, | 
| Doug Zongker | 96a57e7 | 2010-09-26 14:57:41 -0700 | [diff] [blame] | 719 |       metadata=metadata, | 
 | 720 |       info_dict=OPTIONS.info_dict) | 
| Doug Zongker | 05d3dea | 2009-06-22 11:32:31 -0700 | [diff] [blame] | 721 |  | 
| Bill Peckham | e868aec | 2019-09-17 17:06:47 -0700 | [diff] [blame] | 722 |   assert HasRecoveryPatch(input_zip, info_dict=OPTIONS.info_dict) | 
| Doug Zongker | c925382 | 2014-02-04 12:17:58 -0800 | [diff] [blame] | 723 |  | 
| Tao Bao | 481bab8 | 2017-12-21 11:23:09 -0800 | [diff] [blame] | 724 |   # Assertions (e.g. downgrade check, device properties check). | 
 | 725 |   ts = target_info.GetBuildProp("ro.build.date.utc") | 
 | 726 |   ts_text = target_info.GetBuildProp("ro.build.date") | 
| Elliott Hughes | d8a52f9 | 2016-06-20 14:35:47 -0700 | [diff] [blame] | 727 |   script.AssertOlderBuild(ts, ts_text) | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 728 |  | 
| Tao Bao | 481bab8 | 2017-12-21 11:23:09 -0800 | [diff] [blame] | 729 |   target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount) | 
| Doug Zongker | 05d3dea | 2009-06-22 11:32:31 -0700 | [diff] [blame] | 730 |   device_specific.FullOTA_Assertions() | 
| Doug Zongker | 9b23f2c | 2013-11-25 14:44:12 -0800 | [diff] [blame] | 731 |  | 
| Tianjie Xu | f67dd80 | 2019-05-20 17:50:36 -0700 | [diff] [blame] | 732 |   block_diff_dict = GetBlockDifferences(target_zip=input_zip, source_zip=None, | 
 | 733 |                                         target_info=target_info, | 
 | 734 |                                         source_info=None, | 
 | 735 |                                         device_specific=device_specific) | 
 | 736 |  | 
| Doug Zongker | 9b23f2c | 2013-11-25 14:44:12 -0800 | [diff] [blame] | 737 |   # Two-step package strategy (in chronological order, which is *not* | 
 | 738 |   # the order in which the generated script has things): | 
 | 739 |   # | 
 | 740 |   # if stage is not "2/3" or "3/3": | 
 | 741 |   #    write recovery image to boot partition | 
 | 742 |   #    set stage to "2/3" | 
 | 743 |   #    reboot to boot partition and restart recovery | 
 | 744 |   # else if stage is "2/3": | 
 | 745 |   #    write recovery image to recovery partition | 
 | 746 |   #    set stage to "3/3" | 
 | 747 |   #    reboot to recovery partition and restart recovery | 
 | 748 |   # else: | 
 | 749 |   #    (stage must be "3/3") | 
 | 750 |   #    set stage to "" | 
 | 751 |   #    do normal full package installation: | 
 | 752 |   #       wipe and install system, boot image, etc. | 
 | 753 |   #       set up system to update recovery partition on first boot | 
| Dan Albert | 8b72aef | 2015-03-23 19:13:21 -0700 | [diff] [blame] | 754 |   #    complete script normally | 
 | 755 |   #    (allow recovery to mark itself finished and reboot) | 
| Doug Zongker | 9b23f2c | 2013-11-25 14:44:12 -0800 | [diff] [blame] | 756 |  | 
 | 757 |   recovery_img = common.GetBootableImage("recovery.img", "recovery.img", | 
 | 758 |                                          OPTIONS.input_tmp, "RECOVERY") | 
 | 759 |   if OPTIONS.two_step: | 
| Tao Bao | 481bab8 | 2017-12-21 11:23:09 -0800 | [diff] [blame] | 760 |     if not target_info.get("multistage_support"): | 
| Doug Zongker | 9b23f2c | 2013-11-25 14:44:12 -0800 | [diff] [blame] | 761 |       assert False, "two-step packages not supported by this build" | 
| Tao Bao | 481bab8 | 2017-12-21 11:23:09 -0800 | [diff] [blame] | 762 |     fs = target_info["fstab"]["/misc"] | 
| Doug Zongker | 9b23f2c | 2013-11-25 14:44:12 -0800 | [diff] [blame] | 763 |     assert fs.fs_type.upper() == "EMMC", \ | 
 | 764 |         "two-step packages only supported on devices with EMMC /misc partitions" | 
 | 765 |     bcb_dev = {"bcb_dev": fs.device} | 
 | 766 |     common.ZipWriteStr(output_zip, "recovery.img", recovery_img.data) | 
 | 767 |     script.AppendExtra(""" | 
| Michael Runge | fb8886d | 2014-10-23 13:51:04 -0700 | [diff] [blame] | 768 | if get_stage("%(bcb_dev)s") == "2/3" then | 
| Doug Zongker | 9b23f2c | 2013-11-25 14:44:12 -0800 | [diff] [blame] | 769 | """ % bcb_dev) | 
| Tao Bao | d42e97e | 2016-11-30 12:11:57 -0800 | [diff] [blame] | 770 |  | 
 | 771 |     # Stage 2/3: Write recovery image to /recovery (currently running /boot). | 
 | 772 |     script.Comment("Stage 2/3") | 
| Doug Zongker | 9b23f2c | 2013-11-25 14:44:12 -0800 | [diff] [blame] | 773 |     script.WriteRawImage("/recovery", "recovery.img") | 
 | 774 |     script.AppendExtra(""" | 
 | 775 | set_stage("%(bcb_dev)s", "3/3"); | 
 | 776 | reboot_now("%(bcb_dev)s", "recovery"); | 
| Michael Runge | fb8886d | 2014-10-23 13:51:04 -0700 | [diff] [blame] | 777 | else if get_stage("%(bcb_dev)s") == "3/3" then | 
| Doug Zongker | 9b23f2c | 2013-11-25 14:44:12 -0800 | [diff] [blame] | 778 | """ % bcb_dev) | 
 | 779 |  | 
| Tao Bao | d42e97e | 2016-11-30 12:11:57 -0800 | [diff] [blame] | 780 |     # Stage 3/3: Make changes. | 
 | 781 |     script.Comment("Stage 3/3") | 
 | 782 |  | 
| Tao Bao | 6c55a8a | 2015-04-08 15:30:27 -0700 | [diff] [blame] | 783 |   # Dump fingerprints | 
| Tao Bao | 481bab8 | 2017-12-21 11:23:09 -0800 | [diff] [blame] | 784 |   script.Print("Target: {}".format(target_info.fingerprint)) | 
| Tao Bao | 6c55a8a | 2015-04-08 15:30:27 -0700 | [diff] [blame] | 785 |  | 
| Doug Zongker | e5ff590 | 2012-01-17 10:55:37 -0800 | [diff] [blame] | 786 |   device_specific.FullOTA_InstallBegin() | 
| Doug Zongker | 171f1cd | 2009-06-15 22:36:37 -0700 | [diff] [blame] | 787 |  | 
| Tianjie Xu | f67dd80 | 2019-05-20 17:50:36 -0700 | [diff] [blame] | 788 |   # All other partitions as well as the data wipe use 10% of the progress, and | 
 | 789 |   # the update of the system partition takes the remaining progress. | 
 | 790 |   system_progress = 0.9 - (len(block_diff_dict) - 1) * 0.1 | 
| Doug Zongker | dbfaae5 | 2009-04-21 17:12:54 -0700 | [diff] [blame] | 791 |   if OPTIONS.wipe_user_data: | 
| Doug Zongker | 01ce19c | 2014-02-04 13:48:15 -0800 | [diff] [blame] | 792 |     system_progress -= 0.1 | 
| Tianjie Xu | f67dd80 | 2019-05-20 17:50:36 -0700 | [diff] [blame] | 793 |   progress_dict = {partition: 0.1 for partition in block_diff_dict} | 
 | 794 |   progress_dict["system"] = system_progress | 
| Doug Zongker | c8b4e84 | 2014-06-16 15:16:31 -0700 | [diff] [blame] | 795 |  | 
| Yifan Hong | 10c530d | 2018-12-27 17:34:18 -0800 | [diff] [blame] | 796 |   if target_info.get('use_dynamic_partitions') == "true": | 
 | 797 |     # Use empty source_info_dict to indicate that all partitions / groups must | 
 | 798 |     # be re-added. | 
 | 799 |     dynamic_partitions_diff = common.DynamicPartitionsDifference( | 
 | 800 |         info_dict=OPTIONS.info_dict, | 
| Tianjie Xu | f67dd80 | 2019-05-20 17:50:36 -0700 | [diff] [blame] | 801 |         block_diffs=block_diff_dict.values(), | 
| Yifan Hong | 10c530d | 2018-12-27 17:34:18 -0800 | [diff] [blame] | 802 |         progress_dict=progress_dict) | 
 | 803 |     dynamic_partitions_diff.WriteScript(script, output_zip, | 
 | 804 |                                         write_verify_script=OPTIONS.verify) | 
 | 805 |   else: | 
| Tianjie Xu | f67dd80 | 2019-05-20 17:50:36 -0700 | [diff] [blame] | 806 |     for block_diff in block_diff_dict.values(): | 
| Yifan Hong | 10c530d | 2018-12-27 17:34:18 -0800 | [diff] [blame] | 807 |       block_diff.WriteScript(script, output_zip, | 
 | 808 |                              progress=progress_dict.get(block_diff.partition), | 
 | 809 |                              write_verify_script=OPTIONS.verify) | 
| Doug Zongker | 73ef825 | 2009-07-23 15:12:53 -0700 | [diff] [blame] | 810 |  | 
| Yifan Hong | 9276cf0 | 2019-08-21 16:37:04 -0700 | [diff] [blame] | 811 |   CheckVintfIfTrebleEnabled(OPTIONS.input_tmp, target_info) | 
| Tao Bao | bcd1d16 | 2017-08-26 13:10:26 -0700 | [diff] [blame] | 812 |  | 
| Yifan Hong | 10c530d | 2018-12-27 17:34:18 -0800 | [diff] [blame] | 813 |   boot_img = common.GetBootableImage( | 
 | 814 |       "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT") | 
| Tao Bao | 481bab8 | 2017-12-21 11:23:09 -0800 | [diff] [blame] | 815 |   common.CheckSize(boot_img.data, "boot.img", target_info) | 
| Doug Zongker | 73ef825 | 2009-07-23 15:12:53 -0700 | [diff] [blame] | 816 |   common.ZipWriteStr(output_zip, "boot.img", boot_img.data) | 
| Doug Zongker | c494d7c | 2009-06-18 08:43:44 -0700 | [diff] [blame] | 817 |  | 
| Doug Zongker | 9ce0fb6 | 2010-09-20 18:04:41 -0700 | [diff] [blame] | 818 |   script.WriteRawImage("/boot", "boot.img") | 
| Doug Zongker | 05d3dea | 2009-06-22 11:32:31 -0700 | [diff] [blame] | 819 |  | 
| Tianjie Xu | f67dd80 | 2019-05-20 17:50:36 -0700 | [diff] [blame] | 820 |   script.ShowProgress(0.1, 10) | 
| Doug Zongker | 05d3dea | 2009-06-22 11:32:31 -0700 | [diff] [blame] | 821 |   device_specific.FullOTA_InstallEnd() | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 822 |  | 
| Doug Zongker | 1c390a2 | 2009-05-14 19:06:36 -0700 | [diff] [blame] | 823 |   if OPTIONS.extra_script is not None: | 
| Doug Zongker | c494d7c | 2009-06-18 08:43:44 -0700 | [diff] [blame] | 824 |     script.AppendExtra(OPTIONS.extra_script) | 
| Doug Zongker | 1c390a2 | 2009-05-14 19:06:36 -0700 | [diff] [blame] | 825 |  | 
| Doug Zongker | 1483360 | 2010-02-02 13:12:04 -0800 | [diff] [blame] | 826 |   script.UnmountAll() | 
| Doug Zongker | 9b23f2c | 2013-11-25 14:44:12 -0800 | [diff] [blame] | 827 |  | 
| Doug Zongker | 922206e | 2014-03-04 13:16:24 -0800 | [diff] [blame] | 828 |   if OPTIONS.wipe_user_data: | 
 | 829 |     script.ShowProgress(0.1, 10) | 
 | 830 |     script.FormatPartition("/data") | 
| Doug Zongker | c8b4e84 | 2014-06-16 15:16:31 -0700 | [diff] [blame] | 831 |  | 
| Doug Zongker | 9b23f2c | 2013-11-25 14:44:12 -0800 | [diff] [blame] | 832 |   if OPTIONS.two_step: | 
 | 833 |     script.AppendExtra(""" | 
 | 834 | set_stage("%(bcb_dev)s", ""); | 
 | 835 | """ % bcb_dev) | 
 | 836 |     script.AppendExtra("else\n") | 
| Tao Bao | d42e97e | 2016-11-30 12:11:57 -0800 | [diff] [blame] | 837 |  | 
 | 838 |     # Stage 1/3: Nothing to verify for full OTA. Write recovery image to /boot. | 
 | 839 |     script.Comment("Stage 1/3") | 
 | 840 |     _WriteRecoveryImageToBoot(script, output_zip) | 
 | 841 |  | 
| Doug Zongker | 9b23f2c | 2013-11-25 14:44:12 -0800 | [diff] [blame] | 842 |     script.AppendExtra(""" | 
 | 843 | set_stage("%(bcb_dev)s", "2/3"); | 
 | 844 | reboot_now("%(bcb_dev)s", ""); | 
 | 845 | endif; | 
 | 846 | endif; | 
 | 847 | """ % bcb_dev) | 
| Tao Bao | d8d14be | 2016-02-04 14:26:02 -0800 | [diff] [blame] | 848 |  | 
| Tao Bao | 5d18256 | 2016-02-23 11:38:39 -0800 | [diff] [blame] | 849 |   script.SetProgress(1) | 
 | 850 |   script.AddToZip(input_zip, output_zip, input_path=OPTIONS.updater_binary) | 
| Tao Bao | d8d14be | 2016-02-04 14:26:02 -0800 | [diff] [blame] | 851 |   metadata["ota-required-cache"] = str(script.required_cache) | 
| Tao Bao | 491d7e2 | 2018-02-21 13:17:22 -0800 | [diff] [blame] | 852 |  | 
 | 853 |   # We haven't written the metadata entry, which will be done in | 
 | 854 |   # FinalizeMetadata. | 
 | 855 |   common.ZipClose(output_zip) | 
 | 856 |  | 
 | 857 |   needed_property_files = ( | 
 | 858 |       NonAbOtaPropertyFiles(), | 
 | 859 |   ) | 
 | 860 |   FinalizeMetadata(metadata, staging_file, output_file, needed_property_files) | 
| Doug Zongker | 2ea2106 | 2010-04-28 16:05:21 -0700 | [diff] [blame] | 861 |  | 
| Doug Zongker | fc44a51 | 2014-08-26 13:10:25 -0700 | [diff] [blame] | 862 |  | 
| xunchang | 1cfe251 | 2019-02-19 14:14:48 -0800 | [diff] [blame] | 863 | def WriteMetadata(metadata, output): | 
 | 864 |   """Writes the metadata to the zip archive or a file. | 
 | 865 |  | 
 | 866 |   Args: | 
 | 867 |     metadata: The metadata dict for the package. | 
 | 868 |     output: A ZipFile object or a string of the output file path. | 
 | 869 |   """ | 
 | 870 |  | 
| Tao Bao | 59cf0c5 | 2019-06-25 10:04:24 -0700 | [diff] [blame] | 871 |   value = "".join(["%s=%s\n" % kv for kv in sorted(metadata.items())]) | 
| xunchang | 1cfe251 | 2019-02-19 14:14:48 -0800 | [diff] [blame] | 872 |   if isinstance(output, zipfile.ZipFile): | 
 | 873 |     common.ZipWriteStr(output, METADATA_NAME, value, | 
 | 874 |                        compress_type=zipfile.ZIP_STORED) | 
 | 875 |     return | 
 | 876 |  | 
 | 877 |   with open(output, 'w') as f: | 
 | 878 |     f.write(value) | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 879 |  | 
| Doug Zongker | fc44a51 | 2014-08-26 13:10:25 -0700 | [diff] [blame] | 880 |  | 
| Tao Bao | 481bab8 | 2017-12-21 11:23:09 -0800 | [diff] [blame] | 881 | def HandleDowngradeMetadata(metadata, target_info, source_info): | 
| Tao Bao | b31892e | 2017-02-07 11:21:17 -0800 | [diff] [blame] | 882 |   # Only incremental OTAs are allowed to reach here. | 
 | 883 |   assert OPTIONS.incremental_source is not None | 
 | 884 |  | 
| Tao Bao | 481bab8 | 2017-12-21 11:23:09 -0800 | [diff] [blame] | 885 |   post_timestamp = target_info.GetBuildProp("ro.build.date.utc") | 
 | 886 |   pre_timestamp = source_info.GetBuildProp("ro.build.date.utc") | 
| Tao Bao | 59cf0c5 | 2019-06-25 10:04:24 -0700 | [diff] [blame] | 887 |   is_downgrade = int(post_timestamp) < int(pre_timestamp) | 
| Tao Bao | b31892e | 2017-02-07 11:21:17 -0800 | [diff] [blame] | 888 |  | 
 | 889 |   if OPTIONS.downgrade: | 
| Tao Bao | b31892e | 2017-02-07 11:21:17 -0800 | [diff] [blame] | 890 |     if not is_downgrade: | 
| Tao Bao | faa8e0b | 2018-04-12 14:31:43 -0700 | [diff] [blame] | 891 |       raise RuntimeError( | 
 | 892 |           "--downgrade or --override_timestamp specified but no downgrade " | 
 | 893 |           "detected: pre: %s, post: %s" % (pre_timestamp, post_timestamp)) | 
| Tao Bao | 3e6161a | 2017-02-28 11:48:48 -0800 | [diff] [blame] | 894 |     metadata["ota-downgrade"] = "yes" | 
| Tao Bao | b31892e | 2017-02-07 11:21:17 -0800 | [diff] [blame] | 895 |   else: | 
 | 896 |     if is_downgrade: | 
| Tao Bao | faa8e0b | 2018-04-12 14:31:43 -0700 | [diff] [blame] | 897 |       raise RuntimeError( | 
 | 898 |           "Downgrade detected based on timestamp check: pre: %s, post: %s. " | 
 | 899 |           "Need to specify --override_timestamp OR --downgrade to allow " | 
 | 900 |           "building the incremental." % (pre_timestamp, post_timestamp)) | 
| Tao Bao | b31892e | 2017-02-07 11:21:17 -0800 | [diff] [blame] | 901 |  | 
 | 902 |  | 
| Tao Bao | df3a48b | 2018-01-10 16:30:43 -0800 | [diff] [blame] | 903 | def GetPackageMetadata(target_info, source_info=None): | 
 | 904 |   """Generates and returns the metadata dict. | 
 | 905 |  | 
 | 906 |   It generates a dict() that contains the info to be written into an OTA | 
 | 907 |   package (META-INF/com/android/metadata). It also handles the detection of | 
| Tao Bao | faa8e0b | 2018-04-12 14:31:43 -0700 | [diff] [blame] | 908 |   downgrade / data wipe based on the global options. | 
| Tao Bao | df3a48b | 2018-01-10 16:30:43 -0800 | [diff] [blame] | 909 |  | 
 | 910 |   Args: | 
 | 911 |     target_info: The BuildInfo instance that holds the target build info. | 
 | 912 |     source_info: The BuildInfo instance that holds the source build info, or | 
 | 913 |         None if generating full OTA. | 
 | 914 |  | 
 | 915 |   Returns: | 
 | 916 |     A dict to be written into package metadata entry. | 
 | 917 |   """ | 
| Tao Bao | 1c320f8 | 2019-10-04 23:25:12 -0700 | [diff] [blame] | 918 |   assert isinstance(target_info, common.BuildInfo) | 
 | 919 |   assert source_info is None or isinstance(source_info, common.BuildInfo) | 
| Tao Bao | df3a48b | 2018-01-10 16:30:43 -0800 | [diff] [blame] | 920 |  | 
 | 921 |   metadata = { | 
 | 922 |       'post-build' : target_info.fingerprint, | 
 | 923 |       'post-build-incremental' : target_info.GetBuildProp( | 
 | 924 |           'ro.build.version.incremental'), | 
| Tao Bao | 35dc255 | 2018-02-01 13:18:00 -0800 | [diff] [blame] | 925 |       'post-sdk-level' : target_info.GetBuildProp( | 
 | 926 |           'ro.build.version.sdk'), | 
 | 927 |       'post-security-patch-level' : target_info.GetBuildProp( | 
 | 928 |           'ro.build.version.security_patch'), | 
| Tao Bao | df3a48b | 2018-01-10 16:30:43 -0800 | [diff] [blame] | 929 |   } | 
 | 930 |  | 
 | 931 |   if target_info.is_ab: | 
 | 932 |     metadata['ota-type'] = 'AB' | 
 | 933 |     metadata['ota-required-cache'] = '0' | 
 | 934 |   else: | 
 | 935 |     metadata['ota-type'] = 'BLOCK' | 
 | 936 |  | 
 | 937 |   if OPTIONS.wipe_user_data: | 
 | 938 |     metadata['ota-wipe'] = 'yes' | 
 | 939 |  | 
| Tao Bao | 393eeb4 | 2019-03-06 16:00:38 -0800 | [diff] [blame] | 940 |   if OPTIONS.retrofit_dynamic_partitions: | 
 | 941 |     metadata['ota-retrofit-dynamic-partitions'] = 'yes' | 
 | 942 |  | 
| Tao Bao | df3a48b | 2018-01-10 16:30:43 -0800 | [diff] [blame] | 943 |   is_incremental = source_info is not None | 
 | 944 |   if is_incremental: | 
 | 945 |     metadata['pre-build'] = source_info.fingerprint | 
 | 946 |     metadata['pre-build-incremental'] = source_info.GetBuildProp( | 
 | 947 |         'ro.build.version.incremental') | 
 | 948 |     metadata['pre-device'] = source_info.device | 
 | 949 |   else: | 
 | 950 |     metadata['pre-device'] = target_info.device | 
 | 951 |  | 
| Tao Bao | faa8e0b | 2018-04-12 14:31:43 -0700 | [diff] [blame] | 952 |   # Use the actual post-timestamp, even for a downgrade case. | 
 | 953 |   metadata['post-timestamp'] = target_info.GetBuildProp('ro.build.date.utc') | 
 | 954 |  | 
 | 955 |   # Detect downgrades and set up downgrade flags accordingly. | 
| Tao Bao | df3a48b | 2018-01-10 16:30:43 -0800 | [diff] [blame] | 956 |   if is_incremental: | 
 | 957 |     HandleDowngradeMetadata(metadata, target_info, source_info) | 
| Tao Bao | df3a48b | 2018-01-10 16:30:43 -0800 | [diff] [blame] | 958 |  | 
 | 959 |   return metadata | 
 | 960 |  | 
 | 961 |  | 
| Tao Bao | d3fc38a | 2018-03-08 16:09:01 -0800 | [diff] [blame] | 962 | class PropertyFiles(object): | 
 | 963 |   """A class that computes the property-files string for an OTA package. | 
 | 964 |  | 
 | 965 |   A property-files string is a comma-separated string that contains the | 
 | 966 |   offset/size info for an OTA package. The entries, which must be ZIP_STORED, | 
 | 967 |   can be fetched directly with the package URL along with the offset/size info. | 
 | 968 |   These strings can be used for streaming A/B OTAs, or allowing an updater to | 
 | 969 |   download package metadata entry directly, without paying the cost of | 
 | 970 |   downloading entire package. | 
| Tao Bao | fe5b69a | 2018-03-02 09:47:43 -0800 | [diff] [blame] | 971 |  | 
| Tao Bao | cc8e266 | 2018-03-01 19:30:00 -0800 | [diff] [blame] | 972 |   Computing the final property-files string requires two passes. Because doing | 
 | 973 |   the whole package signing (with signapk.jar) will possibly reorder the ZIP | 
 | 974 |   entries, which may in turn invalidate earlier computed ZIP entry offset/size | 
 | 975 |   values. | 
 | 976 |  | 
 | 977 |   This class provides functions to be called for each pass. The general flow is | 
 | 978 |   as follows. | 
 | 979 |  | 
| Tao Bao | d3fc38a | 2018-03-08 16:09:01 -0800 | [diff] [blame] | 980 |     property_files = PropertyFiles() | 
| Tao Bao | cc8e266 | 2018-03-01 19:30:00 -0800 | [diff] [blame] | 981 |     # The first pass, which writes placeholders before doing initial signing. | 
 | 982 |     property_files.Compute() | 
 | 983 |     SignOutput() | 
 | 984 |  | 
 | 985 |     # The second pass, by replacing the placeholders with actual data. | 
 | 986 |     property_files.Finalize() | 
 | 987 |     SignOutput() | 
 | 988 |  | 
 | 989 |   And the caller can additionally verify the final result. | 
 | 990 |  | 
 | 991 |     property_files.Verify() | 
| Tao Bao | fe5b69a | 2018-03-02 09:47:43 -0800 | [diff] [blame] | 992 |   """ | 
 | 993 |  | 
| Tao Bao | cc8e266 | 2018-03-01 19:30:00 -0800 | [diff] [blame] | 994 |   def __init__(self): | 
| Tao Bao | d3fc38a | 2018-03-08 16:09:01 -0800 | [diff] [blame] | 995 |     self.name = None | 
 | 996 |     self.required = () | 
 | 997 |     self.optional = () | 
| Tao Bao | fe5b69a | 2018-03-02 09:47:43 -0800 | [diff] [blame] | 998 |  | 
| Tao Bao | cc8e266 | 2018-03-01 19:30:00 -0800 | [diff] [blame] | 999 |   def Compute(self, input_zip): | 
 | 1000 |     """Computes and returns a property-files string with placeholders. | 
| Tao Bao | fe5b69a | 2018-03-02 09:47:43 -0800 | [diff] [blame] | 1001 |  | 
| Tao Bao | cc8e266 | 2018-03-01 19:30:00 -0800 | [diff] [blame] | 1002 |     We reserve extra space for the offset and size of the metadata entry itself, | 
 | 1003 |     although we don't know the final values until the package gets signed. | 
| Tao Bao | fe5b69a | 2018-03-02 09:47:43 -0800 | [diff] [blame] | 1004 |  | 
| Tao Bao | cc8e266 | 2018-03-01 19:30:00 -0800 | [diff] [blame] | 1005 |     Args: | 
 | 1006 |       input_zip: The input ZIP file. | 
| Tao Bao | fe5b69a | 2018-03-02 09:47:43 -0800 | [diff] [blame] | 1007 |  | 
| Tao Bao | cc8e266 | 2018-03-01 19:30:00 -0800 | [diff] [blame] | 1008 |     Returns: | 
 | 1009 |       A string with placeholders for the metadata offset/size info, e.g. | 
 | 1010 |       "payload.bin:679:343,payload_properties.txt:378:45,metadata:        ". | 
 | 1011 |     """ | 
| Zhomart Mukhamejanov | 603655f | 2018-05-04 12:35:09 -0700 | [diff] [blame] | 1012 |     return self.GetPropertyFilesString(input_zip, reserve_space=True) | 
| Tao Bao | fe5b69a | 2018-03-02 09:47:43 -0800 | [diff] [blame] | 1013 |  | 
| Tao Bao | d2ce2ed | 2018-03-16 12:59:42 -0700 | [diff] [blame] | 1014 |   class InsufficientSpaceException(Exception): | 
 | 1015 |     pass | 
 | 1016 |  | 
| Tao Bao | cc8e266 | 2018-03-01 19:30:00 -0800 | [diff] [blame] | 1017 |   def Finalize(self, input_zip, reserved_length): | 
 | 1018 |     """Finalizes a property-files string with actual METADATA offset/size info. | 
 | 1019 |  | 
 | 1020 |     The input ZIP file has been signed, with the ZIP entries in the desired | 
 | 1021 |     place (signapk.jar will possibly reorder the ZIP entries). Now we compute | 
 | 1022 |     the ZIP entry offsets and construct the property-files string with actual | 
 | 1023 |     data. Note that during this process, we must pad the property-files string | 
 | 1024 |     to the reserved length, so that the METADATA entry size remains the same. | 
 | 1025 |     Otherwise the entries' offsets and sizes may change again. | 
 | 1026 |  | 
 | 1027 |     Args: | 
 | 1028 |       input_zip: The input ZIP file. | 
 | 1029 |       reserved_length: The reserved length of the property-files string during | 
 | 1030 |           the call to Compute(). The final string must be no more than this | 
 | 1031 |           size. | 
 | 1032 |  | 
 | 1033 |     Returns: | 
 | 1034 |       A property-files string including the metadata offset/size info, e.g. | 
 | 1035 |       "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379  ". | 
 | 1036 |  | 
 | 1037 |     Raises: | 
| Tao Bao | d2ce2ed | 2018-03-16 12:59:42 -0700 | [diff] [blame] | 1038 |       InsufficientSpaceException: If the reserved length is insufficient to hold | 
 | 1039 |           the final string. | 
| Tao Bao | cc8e266 | 2018-03-01 19:30:00 -0800 | [diff] [blame] | 1040 |     """ | 
| Zhomart Mukhamejanov | 603655f | 2018-05-04 12:35:09 -0700 | [diff] [blame] | 1041 |     result = self.GetPropertyFilesString(input_zip, reserve_space=False) | 
| Tao Bao | d2ce2ed | 2018-03-16 12:59:42 -0700 | [diff] [blame] | 1042 |     if len(result) > reserved_length: | 
 | 1043 |       raise self.InsufficientSpaceException( | 
 | 1044 |           'Insufficient reserved space: reserved={}, actual={}'.format( | 
 | 1045 |               reserved_length, len(result))) | 
 | 1046 |  | 
| Tao Bao | cc8e266 | 2018-03-01 19:30:00 -0800 | [diff] [blame] | 1047 |     result += ' ' * (reserved_length - len(result)) | 
 | 1048 |     return result | 
 | 1049 |  | 
 | 1050 |   def Verify(self, input_zip, expected): | 
 | 1051 |     """Verifies the input ZIP file contains the expected property-files string. | 
 | 1052 |  | 
 | 1053 |     Args: | 
 | 1054 |       input_zip: The input ZIP file. | 
 | 1055 |       expected: The property-files string that's computed from Finalize(). | 
 | 1056 |  | 
 | 1057 |     Raises: | 
 | 1058 |       AssertionError: On finding a mismatch. | 
 | 1059 |     """ | 
| Zhomart Mukhamejanov | 603655f | 2018-05-04 12:35:09 -0700 | [diff] [blame] | 1060 |     actual = self.GetPropertyFilesString(input_zip) | 
| Tao Bao | cc8e266 | 2018-03-01 19:30:00 -0800 | [diff] [blame] | 1061 |     assert actual == expected, \ | 
 | 1062 |         "Mismatching streaming metadata: {} vs {}.".format(actual, expected) | 
 | 1063 |  | 
| Zhomart Mukhamejanov | 603655f | 2018-05-04 12:35:09 -0700 | [diff] [blame] | 1064 |   def GetPropertyFilesString(self, zip_file, reserve_space=False): | 
 | 1065 |     """ | 
 | 1066 |     Constructs the property-files string per request. | 
 | 1067 |  | 
 | 1068 |     Args: | 
 | 1069 |       zip_file: The input ZIP file. | 
 | 1070 |       reserved_length: The reserved length of the property-files string. | 
 | 1071 |  | 
 | 1072 |     Returns: | 
 | 1073 |       A property-files string including the metadata offset/size info, e.g. | 
 | 1074 |       "payload.bin:679:343,payload_properties.txt:378:45,metadata:     ". | 
 | 1075 |     """ | 
| Tao Bao | cc8e266 | 2018-03-01 19:30:00 -0800 | [diff] [blame] | 1076 |  | 
 | 1077 |     def ComputeEntryOffsetSize(name): | 
 | 1078 |       """Computes the zip entry offset and size.""" | 
 | 1079 |       info = zip_file.getinfo(name) | 
| Shashikant Baviskar | 338856f | 2018-04-12 12:11:22 +0900 | [diff] [blame] | 1080 |       offset = info.header_offset | 
 | 1081 |       offset += zipfile.sizeFileHeader | 
 | 1082 |       offset += len(info.extra) + len(info.filename) | 
| Tao Bao | cc8e266 | 2018-03-01 19:30:00 -0800 | [diff] [blame] | 1083 |       size = info.file_size | 
 | 1084 |       return '%s:%d:%d' % (os.path.basename(name), offset, size) | 
 | 1085 |  | 
 | 1086 |     tokens = [] | 
| Tao Bao | 85f1698 | 2018-03-08 16:28:33 -0800 | [diff] [blame] | 1087 |     tokens.extend(self._GetPrecomputed(zip_file)) | 
| Tao Bao | cc8e266 | 2018-03-01 19:30:00 -0800 | [diff] [blame] | 1088 |     for entry in self.required: | 
 | 1089 |       tokens.append(ComputeEntryOffsetSize(entry)) | 
 | 1090 |     for entry in self.optional: | 
 | 1091 |       if entry in zip_file.namelist(): | 
 | 1092 |         tokens.append(ComputeEntryOffsetSize(entry)) | 
 | 1093 |  | 
 | 1094 |     # 'META-INF/com/android/metadata' is required. We don't know its actual | 
 | 1095 |     # offset and length (as well as the values for other entries). So we reserve | 
| Tao Bao | d2ce2ed | 2018-03-16 12:59:42 -0700 | [diff] [blame] | 1096 |     # 15-byte as a placeholder ('offset:length'), which is sufficient to cover | 
 | 1097 |     # the space for metadata entry. Because 'offset' allows a max of 10-digit | 
 | 1098 |     # (i.e. ~9 GiB), with a max of 4-digit for the length. Note that all the | 
 | 1099 |     # reserved space serves the metadata entry only. | 
| Tao Bao | cc8e266 | 2018-03-01 19:30:00 -0800 | [diff] [blame] | 1100 |     if reserve_space: | 
| Tao Bao | d2ce2ed | 2018-03-16 12:59:42 -0700 | [diff] [blame] | 1101 |       tokens.append('metadata:' + ' ' * 15) | 
| Tao Bao | cc8e266 | 2018-03-01 19:30:00 -0800 | [diff] [blame] | 1102 |     else: | 
 | 1103 |       tokens.append(ComputeEntryOffsetSize(METADATA_NAME)) | 
 | 1104 |  | 
 | 1105 |     return ','.join(tokens) | 
| Tao Bao | fe5b69a | 2018-03-02 09:47:43 -0800 | [diff] [blame] | 1106 |  | 
| Tao Bao | 85f1698 | 2018-03-08 16:28:33 -0800 | [diff] [blame] | 1107 |   def _GetPrecomputed(self, input_zip): | 
 | 1108 |     """Computes the additional tokens to be included into the property-files. | 
 | 1109 |  | 
 | 1110 |     This applies to tokens without actual ZIP entries, such as | 
 | 1111 |     payload_metadadata.bin. We want to expose the offset/size to updaters, so | 
 | 1112 |     that they can download the payload metadata directly with the info. | 
 | 1113 |  | 
 | 1114 |     Args: | 
 | 1115 |       input_zip: The input zip file. | 
 | 1116 |  | 
 | 1117 |     Returns: | 
 | 1118 |       A list of strings (tokens) to be added to the property-files string. | 
 | 1119 |     """ | 
 | 1120 |     # pylint: disable=no-self-use | 
 | 1121 |     # pylint: disable=unused-argument | 
 | 1122 |     return [] | 
 | 1123 |  | 
| Tao Bao | fe5b69a | 2018-03-02 09:47:43 -0800 | [diff] [blame] | 1124 |  | 
| Tao Bao | d3fc38a | 2018-03-08 16:09:01 -0800 | [diff] [blame] | 1125 | class StreamingPropertyFiles(PropertyFiles): | 
 | 1126 |   """A subclass for computing the property-files for streaming A/B OTAs.""" | 
 | 1127 |  | 
 | 1128 |   def __init__(self): | 
 | 1129 |     super(StreamingPropertyFiles, self).__init__() | 
 | 1130 |     self.name = 'ota-streaming-property-files' | 
 | 1131 |     self.required = ( | 
 | 1132 |         # payload.bin and payload_properties.txt must exist. | 
 | 1133 |         'payload.bin', | 
 | 1134 |         'payload_properties.txt', | 
 | 1135 |     ) | 
 | 1136 |     self.optional = ( | 
| Tianjie Xu | 4c05f4a | 2018-09-14 16:24:41 -0700 | [diff] [blame] | 1137 |         # care_map is available only if dm-verity is enabled. | 
 | 1138 |         'care_map.pb', | 
| Tao Bao | d3fc38a | 2018-03-08 16:09:01 -0800 | [diff] [blame] | 1139 |         'care_map.txt', | 
 | 1140 |         # compatibility.zip is available only if target supports Treble. | 
 | 1141 |         'compatibility.zip', | 
 | 1142 |     ) | 
 | 1143 |  | 
 | 1144 |  | 
| Tao Bao | 85f1698 | 2018-03-08 16:28:33 -0800 | [diff] [blame] | 1145 | class AbOtaPropertyFiles(StreamingPropertyFiles): | 
 | 1146 |   """The property-files for A/B OTA that includes payload_metadata.bin info. | 
 | 1147 |  | 
 | 1148 |   Since P, we expose one more token (aka property-file), in addition to the ones | 
 | 1149 |   for streaming A/B OTA, for a virtual entry of 'payload_metadata.bin'. | 
 | 1150 |   'payload_metadata.bin' is the header part of a payload ('payload.bin'), which | 
 | 1151 |   doesn't exist as a separate ZIP entry, but can be used to verify if the | 
 | 1152 |   payload can be applied on the given device. | 
 | 1153 |  | 
 | 1154 |   For backward compatibility, we keep both of the 'ota-streaming-property-files' | 
 | 1155 |   and the newly added 'ota-property-files' in P. The new token will only be | 
 | 1156 |   available in 'ota-property-files'. | 
 | 1157 |   """ | 
 | 1158 |  | 
 | 1159 |   def __init__(self): | 
 | 1160 |     super(AbOtaPropertyFiles, self).__init__() | 
 | 1161 |     self.name = 'ota-property-files' | 
 | 1162 |  | 
 | 1163 |   def _GetPrecomputed(self, input_zip): | 
 | 1164 |     offset, size = self._GetPayloadMetadataOffsetAndSize(input_zip) | 
 | 1165 |     return ['payload_metadata.bin:{}:{}'.format(offset, size)] | 
 | 1166 |  | 
 | 1167 |   @staticmethod | 
 | 1168 |   def _GetPayloadMetadataOffsetAndSize(input_zip): | 
 | 1169 |     """Computes the offset and size of the payload metadata for a given package. | 
 | 1170 |  | 
 | 1171 |     (From system/update_engine/update_metadata.proto) | 
 | 1172 |     A delta update file contains all the deltas needed to update a system from | 
 | 1173 |     one specific version to another specific version. The update format is | 
 | 1174 |     represented by this struct pseudocode: | 
 | 1175 |  | 
 | 1176 |     struct delta_update_file { | 
 | 1177 |       char magic[4] = "CrAU"; | 
 | 1178 |       uint64 file_format_version; | 
 | 1179 |       uint64 manifest_size;  // Size of protobuf DeltaArchiveManifest | 
 | 1180 |  | 
 | 1181 |       // Only present if format_version > 1: | 
 | 1182 |       uint32 metadata_signature_size; | 
 | 1183 |  | 
 | 1184 |       // The Bzip2 compressed DeltaArchiveManifest | 
 | 1185 |       char manifest[metadata_signature_size]; | 
 | 1186 |  | 
 | 1187 |       // The signature of the metadata (from the beginning of the payload up to | 
 | 1188 |       // this location, not including the signature itself). This is a | 
 | 1189 |       // serialized Signatures message. | 
 | 1190 |       char medatada_signature_message[metadata_signature_size]; | 
 | 1191 |  | 
 | 1192 |       // Data blobs for files, no specific format. The specific offset | 
 | 1193 |       // and length of each data blob is recorded in the DeltaArchiveManifest. | 
 | 1194 |       struct { | 
 | 1195 |         char data[]; | 
 | 1196 |       } blobs[]; | 
 | 1197 |  | 
 | 1198 |       // These two are not signed: | 
 | 1199 |       uint64 payload_signatures_message_size; | 
 | 1200 |       char payload_signatures_message[]; | 
 | 1201 |     }; | 
 | 1202 |  | 
 | 1203 |     'payload-metadata.bin' contains all the bytes from the beginning of the | 
 | 1204 |     payload, till the end of 'medatada_signature_message'. | 
 | 1205 |     """ | 
 | 1206 |     payload_info = input_zip.getinfo('payload.bin') | 
| Shashikant Baviskar | 338856f | 2018-04-12 12:11:22 +0900 | [diff] [blame] | 1207 |     payload_offset = payload_info.header_offset | 
 | 1208 |     payload_offset += zipfile.sizeFileHeader | 
 | 1209 |     payload_offset += len(payload_info.extra) + len(payload_info.filename) | 
| Tao Bao | 85f1698 | 2018-03-08 16:28:33 -0800 | [diff] [blame] | 1210 |     payload_size = payload_info.file_size | 
 | 1211 |  | 
| Tao Bao | 59cf0c5 | 2019-06-25 10:04:24 -0700 | [diff] [blame] | 1212 |     with input_zip.open('payload.bin') as payload_fp: | 
| Tao Bao | 85f1698 | 2018-03-08 16:28:33 -0800 | [diff] [blame] | 1213 |       header_bin = payload_fp.read(24) | 
 | 1214 |  | 
 | 1215 |     # network byte order (big-endian) | 
 | 1216 |     header = struct.unpack("!IQQL", header_bin) | 
 | 1217 |  | 
 | 1218 |     # 'CrAU' | 
 | 1219 |     magic = header[0] | 
 | 1220 |     assert magic == 0x43724155, "Invalid magic: {:x}".format(magic) | 
 | 1221 |  | 
 | 1222 |     manifest_size = header[2] | 
 | 1223 |     metadata_signature_size = header[3] | 
 | 1224 |     metadata_total = 24 + manifest_size + metadata_signature_size | 
 | 1225 |     assert metadata_total < payload_size | 
 | 1226 |  | 
 | 1227 |     return (payload_offset, metadata_total) | 
 | 1228 |  | 
 | 1229 |  | 
| Tao Bao | 491d7e2 | 2018-02-21 13:17:22 -0800 | [diff] [blame] | 1230 | class NonAbOtaPropertyFiles(PropertyFiles): | 
 | 1231 |   """The property-files for non-A/B OTA. | 
 | 1232 |  | 
 | 1233 |   For non-A/B OTA, the property-files string contains the info for METADATA | 
 | 1234 |   entry, with which a system updater can be fetched the package metadata prior | 
 | 1235 |   to downloading the entire package. | 
 | 1236 |   """ | 
 | 1237 |  | 
 | 1238 |   def __init__(self): | 
 | 1239 |     super(NonAbOtaPropertyFiles, self).__init__() | 
 | 1240 |     self.name = 'ota-property-files' | 
 | 1241 |  | 
 | 1242 |  | 
| Tao Bao | d3fc38a | 2018-03-08 16:09:01 -0800 | [diff] [blame] | 1243 | def FinalizeMetadata(metadata, input_file, output_file, needed_property_files): | 
| Tao Bao | fe5b69a | 2018-03-02 09:47:43 -0800 | [diff] [blame] | 1244 |   """Finalizes the metadata and signs an A/B OTA package. | 
 | 1245 |  | 
 | 1246 |   In order to stream an A/B OTA package, we need 'ota-streaming-property-files' | 
 | 1247 |   that contains the offsets and sizes for the ZIP entries. An example | 
 | 1248 |   property-files string is as follows. | 
 | 1249 |  | 
 | 1250 |     "payload.bin:679:343,payload_properties.txt:378:45,metadata:69:379" | 
 | 1251 |  | 
 | 1252 |   OTA server can pass down this string, in addition to the package URL, to the | 
 | 1253 |   system update client. System update client can then fetch individual ZIP | 
 | 1254 |   entries (ZIP_STORED) directly at the given offset of the URL. | 
 | 1255 |  | 
 | 1256 |   Args: | 
 | 1257 |     metadata: The metadata dict for the package. | 
 | 1258 |     input_file: The input ZIP filename that doesn't contain the package METADATA | 
 | 1259 |         entry yet. | 
 | 1260 |     output_file: The final output ZIP filename. | 
| Tao Bao | d3fc38a | 2018-03-08 16:09:01 -0800 | [diff] [blame] | 1261 |     needed_property_files: The list of PropertyFiles' to be generated. | 
| Tao Bao | fe5b69a | 2018-03-02 09:47:43 -0800 | [diff] [blame] | 1262 |   """ | 
| Tao Bao | fe5b69a | 2018-03-02 09:47:43 -0800 | [diff] [blame] | 1263 |  | 
| Tao Bao | d2ce2ed | 2018-03-16 12:59:42 -0700 | [diff] [blame] | 1264 |   def ComputeAllPropertyFiles(input_file, needed_property_files): | 
 | 1265 |     # Write the current metadata entry with placeholders. | 
 | 1266 |     with zipfile.ZipFile(input_file) as input_zip: | 
 | 1267 |       for property_files in needed_property_files: | 
 | 1268 |         metadata[property_files.name] = property_files.Compute(input_zip) | 
 | 1269 |       namelist = input_zip.namelist() | 
| Tao Bao | fe5b69a | 2018-03-02 09:47:43 -0800 | [diff] [blame] | 1270 |  | 
| Tao Bao | d2ce2ed | 2018-03-16 12:59:42 -0700 | [diff] [blame] | 1271 |     if METADATA_NAME in namelist: | 
 | 1272 |       common.ZipDelete(input_file, METADATA_NAME) | 
 | 1273 |     output_zip = zipfile.ZipFile(input_file, 'a') | 
 | 1274 |     WriteMetadata(metadata, output_zip) | 
 | 1275 |     common.ZipClose(output_zip) | 
 | 1276 |  | 
 | 1277 |     if OPTIONS.no_signing: | 
 | 1278 |       return input_file | 
 | 1279 |  | 
| Tao Bao | 491d7e2 | 2018-02-21 13:17:22 -0800 | [diff] [blame] | 1280 |     prelim_signing = common.MakeTempFile(suffix='.zip') | 
 | 1281 |     SignOutput(input_file, prelim_signing) | 
| Tao Bao | d2ce2ed | 2018-03-16 12:59:42 -0700 | [diff] [blame] | 1282 |     return prelim_signing | 
| Tao Bao | fe5b69a | 2018-03-02 09:47:43 -0800 | [diff] [blame] | 1283 |  | 
| Tao Bao | d2ce2ed | 2018-03-16 12:59:42 -0700 | [diff] [blame] | 1284 |   def FinalizeAllPropertyFiles(prelim_signing, needed_property_files): | 
 | 1285 |     with zipfile.ZipFile(prelim_signing) as prelim_signing_zip: | 
 | 1286 |       for property_files in needed_property_files: | 
 | 1287 |         metadata[property_files.name] = property_files.Finalize( | 
 | 1288 |             prelim_signing_zip, len(metadata[property_files.name])) | 
 | 1289 |  | 
 | 1290 |   # SignOutput(), which in turn calls signapk.jar, will possibly reorder the ZIP | 
 | 1291 |   # entries, as well as padding the entry headers. We do a preliminary signing | 
 | 1292 |   # (with an incomplete metadata entry) to allow that to happen. Then compute | 
 | 1293 |   # the ZIP entry offsets, write back the final metadata and do the final | 
 | 1294 |   # signing. | 
 | 1295 |   prelim_signing = ComputeAllPropertyFiles(input_file, needed_property_files) | 
 | 1296 |   try: | 
 | 1297 |     FinalizeAllPropertyFiles(prelim_signing, needed_property_files) | 
 | 1298 |   except PropertyFiles.InsufficientSpaceException: | 
 | 1299 |     # Even with the preliminary signing, the entry orders may change | 
 | 1300 |     # dramatically, which leads to insufficiently reserved space during the | 
 | 1301 |     # first call to ComputeAllPropertyFiles(). In that case, we redo all the | 
 | 1302 |     # preliminary signing works, based on the already ordered ZIP entries, to | 
 | 1303 |     # address the issue. | 
 | 1304 |     prelim_signing = ComputeAllPropertyFiles( | 
 | 1305 |         prelim_signing, needed_property_files) | 
 | 1306 |     FinalizeAllPropertyFiles(prelim_signing, needed_property_files) | 
| Tao Bao | fe5b69a | 2018-03-02 09:47:43 -0800 | [diff] [blame] | 1307 |  | 
 | 1308 |   # Replace the METADATA entry. | 
 | 1309 |   common.ZipDelete(prelim_signing, METADATA_NAME) | 
| Tao Bao | d2ce2ed | 2018-03-16 12:59:42 -0700 | [diff] [blame] | 1310 |   output_zip = zipfile.ZipFile(prelim_signing, 'a') | 
| Tao Bao | fe5b69a | 2018-03-02 09:47:43 -0800 | [diff] [blame] | 1311 |   WriteMetadata(metadata, output_zip) | 
 | 1312 |   common.ZipClose(output_zip) | 
 | 1313 |  | 
 | 1314 |   # Re-sign the package after updating the metadata entry. | 
| Tao Bao | 491d7e2 | 2018-02-21 13:17:22 -0800 | [diff] [blame] | 1315 |   if OPTIONS.no_signing: | 
 | 1316 |     output_file = prelim_signing | 
 | 1317 |   else: | 
 | 1318 |     SignOutput(prelim_signing, output_file) | 
| Tao Bao | fe5b69a | 2018-03-02 09:47:43 -0800 | [diff] [blame] | 1319 |  | 
 | 1320 |   # Reopen the final signed zip to double check the streaming metadata. | 
| Tao Bao | d2ce2ed | 2018-03-16 12:59:42 -0700 | [diff] [blame] | 1321 |   with zipfile.ZipFile(output_file) as output_zip: | 
| Tao Bao | d3fc38a | 2018-03-08 16:09:01 -0800 | [diff] [blame] | 1322 |     for property_files in needed_property_files: | 
 | 1323 |       property_files.Verify(output_zip, metadata[property_files.name].strip()) | 
| Tao Bao | fe5b69a | 2018-03-02 09:47:43 -0800 | [diff] [blame] | 1324 |  | 
| xunchang | 1cfe251 | 2019-02-19 14:14:48 -0800 | [diff] [blame] | 1325 |   # If requested, dump the metadata to a separate file. | 
 | 1326 |   output_metadata_path = OPTIONS.output_metadata_path | 
 | 1327 |   if output_metadata_path: | 
 | 1328 |     WriteMetadata(metadata, output_metadata_path) | 
 | 1329 |  | 
| Tao Bao | fe5b69a | 2018-03-02 09:47:43 -0800 | [diff] [blame] | 1330 |  | 
| Tao Bao | 491d7e2 | 2018-02-21 13:17:22 -0800 | [diff] [blame] | 1331 | def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_file): | 
| Tao Bao | 1c320f8 | 2019-10-04 23:25:12 -0700 | [diff] [blame] | 1332 |   target_info = common.BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts) | 
 | 1333 |   source_info = common.BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts) | 
| Geremy Condra | 36bd365 | 2014-02-06 19:45:10 -0800 | [diff] [blame] | 1334 |  | 
| Tao Bao | 481bab8 | 2017-12-21 11:23:09 -0800 | [diff] [blame] | 1335 |   target_api_version = target_info["recovery_api_version"] | 
 | 1336 |   source_api_version = source_info["recovery_api_version"] | 
 | 1337 |   if source_api_version == 0: | 
| Tao Bao | 32fcdab | 2018-10-12 10:30:39 -0700 | [diff] [blame] | 1338 |     logger.warning( | 
 | 1339 |         "Generating edify script for a source that can't install it.") | 
| Geremy Condra | 36bd365 | 2014-02-06 19:45:10 -0800 | [diff] [blame] | 1340 |  | 
| Tao Bao | 481bab8 | 2017-12-21 11:23:09 -0800 | [diff] [blame] | 1341 |   script = edify_generator.EdifyGenerator( | 
 | 1342 |       source_api_version, target_info, fstab=source_info["fstab"]) | 
 | 1343 |  | 
 | 1344 |   if target_info.oem_props or source_info.oem_props: | 
 | 1345 |     if not OPTIONS.oem_no_mount: | 
 | 1346 |       source_info.WriteMountOemScript(script) | 
| Tao Bao | 3806c23 | 2015-07-05 21:08:33 -0700 | [diff] [blame] | 1347 |  | 
| Tao Bao | df3a48b | 2018-01-10 16:30:43 -0800 | [diff] [blame] | 1348 |   metadata = GetPackageMetadata(target_info, source_info) | 
| Tao Bao | 5d18256 | 2016-02-23 11:38:39 -0800 | [diff] [blame] | 1349 |  | 
| Tao Bao | 491d7e2 | 2018-02-21 13:17:22 -0800 | [diff] [blame] | 1350 |   if not OPTIONS.no_signing: | 
 | 1351 |     staging_file = common.MakeTempFile(suffix='.zip') | 
 | 1352 |   else: | 
 | 1353 |     staging_file = output_file | 
 | 1354 |  | 
 | 1355 |   output_zip = zipfile.ZipFile( | 
 | 1356 |       staging_file, "w", compression=zipfile.ZIP_DEFLATED) | 
 | 1357 |  | 
| Geremy Condra | 36bd365 | 2014-02-06 19:45:10 -0800 | [diff] [blame] | 1358 |   device_specific = common.DeviceSpecificParams( | 
 | 1359 |       source_zip=source_zip, | 
| Tao Bao | 481bab8 | 2017-12-21 11:23:09 -0800 | [diff] [blame] | 1360 |       source_version=source_api_version, | 
| Yifan Hong | 8a66a71 | 2019-04-04 15:37:57 -0700 | [diff] [blame] | 1361 |       source_tmp=OPTIONS.source_tmp, | 
| Geremy Condra | 36bd365 | 2014-02-06 19:45:10 -0800 | [diff] [blame] | 1362 |       target_zip=target_zip, | 
| Tao Bao | 481bab8 | 2017-12-21 11:23:09 -0800 | [diff] [blame] | 1363 |       target_version=target_api_version, | 
| Yifan Hong | 8a66a71 | 2019-04-04 15:37:57 -0700 | [diff] [blame] | 1364 |       target_tmp=OPTIONS.target_tmp, | 
| Geremy Condra | 36bd365 | 2014-02-06 19:45:10 -0800 | [diff] [blame] | 1365 |       output_zip=output_zip, | 
 | 1366 |       script=script, | 
 | 1367 |       metadata=metadata, | 
| Tao Bao | 481bab8 | 2017-12-21 11:23:09 -0800 | [diff] [blame] | 1368 |       info_dict=source_info) | 
| Geremy Condra | 36bd365 | 2014-02-06 19:45:10 -0800 | [diff] [blame] | 1369 |  | 
| Geremy Condra | 36bd365 | 2014-02-06 19:45:10 -0800 | [diff] [blame] | 1370 |   source_boot = common.GetBootableImage( | 
| Tao Bao | 481bab8 | 2017-12-21 11:23:09 -0800 | [diff] [blame] | 1371 |       "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT", source_info) | 
| Geremy Condra | 36bd365 | 2014-02-06 19:45:10 -0800 | [diff] [blame] | 1372 |   target_boot = common.GetBootableImage( | 
| Tao Bao | 481bab8 | 2017-12-21 11:23:09 -0800 | [diff] [blame] | 1373 |       "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT", target_info) | 
| Geremy Condra | 36bd365 | 2014-02-06 19:45:10 -0800 | [diff] [blame] | 1374 |   updating_boot = (not OPTIONS.two_step and | 
 | 1375 |                    (source_boot.data != target_boot.data)) | 
 | 1376 |  | 
| Geremy Condra | 36bd365 | 2014-02-06 19:45:10 -0800 | [diff] [blame] | 1377 |   target_recovery = common.GetBootableImage( | 
 | 1378 |       "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY") | 
| Geremy Condra | 36bd365 | 2014-02-06 19:45:10 -0800 | [diff] [blame] | 1379 |  | 
| Tianjie Xu | f67dd80 | 2019-05-20 17:50:36 -0700 | [diff] [blame] | 1380 |   block_diff_dict = GetBlockDifferences(target_zip=target_zip, | 
 | 1381 |                                         source_zip=source_zip, | 
 | 1382 |                                         target_info=target_info, | 
 | 1383 |                                         source_info=source_info, | 
 | 1384 |                                         device_specific=device_specific) | 
| Geremy Condra | 36bd365 | 2014-02-06 19:45:10 -0800 | [diff] [blame] | 1385 |  | 
| Yifan Hong | 9276cf0 | 2019-08-21 16:37:04 -0700 | [diff] [blame] | 1386 |   CheckVintfIfTrebleEnabled(OPTIONS.target_tmp, target_info) | 
| Tao Bao | bcd1d16 | 2017-08-26 13:10:26 -0700 | [diff] [blame] | 1387 |  | 
| Tao Bao | 481bab8 | 2017-12-21 11:23:09 -0800 | [diff] [blame] | 1388 |   # Assertions (e.g. device properties check). | 
 | 1389 |   target_info.WriteDeviceAssertions(script, OPTIONS.oem_no_mount) | 
| Geremy Condra | 36bd365 | 2014-02-06 19:45:10 -0800 | [diff] [blame] | 1390 |   device_specific.IncrementalOTA_Assertions() | 
 | 1391 |  | 
 | 1392 |   # Two-step incremental package strategy (in chronological order, | 
 | 1393 |   # which is *not* the order in which the generated script has | 
 | 1394 |   # things): | 
 | 1395 |   # | 
 | 1396 |   # if stage is not "2/3" or "3/3": | 
 | 1397 |   #    do verification on current system | 
 | 1398 |   #    write recovery image to boot partition | 
 | 1399 |   #    set stage to "2/3" | 
 | 1400 |   #    reboot to boot partition and restart recovery | 
 | 1401 |   # else if stage is "2/3": | 
 | 1402 |   #    write recovery image to recovery partition | 
 | 1403 |   #    set stage to "3/3" | 
 | 1404 |   #    reboot to recovery partition and restart recovery | 
 | 1405 |   # else: | 
 | 1406 |   #    (stage must be "3/3") | 
 | 1407 |   #    perform update: | 
 | 1408 |   #       patch system files, etc. | 
 | 1409 |   #       force full install of new boot image | 
 | 1410 |   #       set up system to update recovery partition on first boot | 
| Dan Albert | 8b72aef | 2015-03-23 19:13:21 -0700 | [diff] [blame] | 1411 |   #    complete script normally | 
 | 1412 |   #    (allow recovery to mark itself finished and reboot) | 
| Geremy Condra | 36bd365 | 2014-02-06 19:45:10 -0800 | [diff] [blame] | 1413 |  | 
 | 1414 |   if OPTIONS.two_step: | 
| Tao Bao | 481bab8 | 2017-12-21 11:23:09 -0800 | [diff] [blame] | 1415 |     if not source_info.get("multistage_support"): | 
| Geremy Condra | 36bd365 | 2014-02-06 19:45:10 -0800 | [diff] [blame] | 1416 |       assert False, "two-step packages not supported by this build" | 
| Tao Bao | 24604cc | 2018-02-01 16:25:44 -0800 | [diff] [blame] | 1417 |     fs = source_info["fstab"]["/misc"] | 
| Geremy Condra | 36bd365 | 2014-02-06 19:45:10 -0800 | [diff] [blame] | 1418 |     assert fs.fs_type.upper() == "EMMC", \ | 
 | 1419 |         "two-step packages only supported on devices with EMMC /misc partitions" | 
| Tao Bao | 481bab8 | 2017-12-21 11:23:09 -0800 | [diff] [blame] | 1420 |     bcb_dev = {"bcb_dev" : fs.device} | 
| Geremy Condra | 36bd365 | 2014-02-06 19:45:10 -0800 | [diff] [blame] | 1421 |     common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data) | 
 | 1422 |     script.AppendExtra(""" | 
| Michael Runge | fb8886d | 2014-10-23 13:51:04 -0700 | [diff] [blame] | 1423 | if get_stage("%(bcb_dev)s") == "2/3" then | 
| Geremy Condra | 36bd365 | 2014-02-06 19:45:10 -0800 | [diff] [blame] | 1424 | """ % bcb_dev) | 
| Tao Bao | d42e97e | 2016-11-30 12:11:57 -0800 | [diff] [blame] | 1425 |  | 
 | 1426 |     # Stage 2/3: Write recovery image to /recovery (currently running /boot). | 
 | 1427 |     script.Comment("Stage 2/3") | 
| Dan Albert | 8b72aef | 2015-03-23 19:13:21 -0700 | [diff] [blame] | 1428 |     script.AppendExtra("sleep(20);\n") | 
| Geremy Condra | 36bd365 | 2014-02-06 19:45:10 -0800 | [diff] [blame] | 1429 |     script.WriteRawImage("/recovery", "recovery.img") | 
 | 1430 |     script.AppendExtra(""" | 
 | 1431 | set_stage("%(bcb_dev)s", "3/3"); | 
 | 1432 | reboot_now("%(bcb_dev)s", "recovery"); | 
| Michael Runge | fb8886d | 2014-10-23 13:51:04 -0700 | [diff] [blame] | 1433 | else if get_stage("%(bcb_dev)s") != "3/3" then | 
| Geremy Condra | 36bd365 | 2014-02-06 19:45:10 -0800 | [diff] [blame] | 1434 | """ % bcb_dev) | 
 | 1435 |  | 
| Tao Bao | d42e97e | 2016-11-30 12:11:57 -0800 | [diff] [blame] | 1436 |     # Stage 1/3: (a) Verify the current system. | 
 | 1437 |     script.Comment("Stage 1/3") | 
 | 1438 |  | 
| Tao Bao | 6c55a8a | 2015-04-08 15:30:27 -0700 | [diff] [blame] | 1439 |   # Dump fingerprints | 
| Tao Bao | 481bab8 | 2017-12-21 11:23:09 -0800 | [diff] [blame] | 1440 |   script.Print("Source: {}".format(source_info.fingerprint)) | 
 | 1441 |   script.Print("Target: {}".format(target_info.fingerprint)) | 
| Tao Bao | 6c55a8a | 2015-04-08 15:30:27 -0700 | [diff] [blame] | 1442 |  | 
| Geremy Condra | 36bd365 | 2014-02-06 19:45:10 -0800 | [diff] [blame] | 1443 |   script.Print("Verifying current system...") | 
 | 1444 |  | 
 | 1445 |   device_specific.IncrementalOTA_VerifyBegin() | 
 | 1446 |  | 
| Tao Bao | 481bab8 | 2017-12-21 11:23:09 -0800 | [diff] [blame] | 1447 |   WriteFingerprintAssertion(script, target_info, source_info) | 
| Geremy Condra | 36bd365 | 2014-02-06 19:45:10 -0800 | [diff] [blame] | 1448 |  | 
| Tao Bao | d8d14be | 2016-02-04 14:26:02 -0800 | [diff] [blame] | 1449 |   # Check the required cache size (i.e. stashed blocks). | 
| Tianjie Xu | f67dd80 | 2019-05-20 17:50:36 -0700 | [diff] [blame] | 1450 |   required_cache_sizes = [diff.required_cache for diff in | 
 | 1451 |                           block_diff_dict.values()] | 
| Geremy Condra | 36bd365 | 2014-02-06 19:45:10 -0800 | [diff] [blame] | 1452 |   if updating_boot: | 
| Tao Bao | 481bab8 | 2017-12-21 11:23:09 -0800 | [diff] [blame] | 1453 |     boot_type, boot_device = common.GetTypeAndDevice("/boot", source_info) | 
| Geremy Condra | 36bd365 | 2014-02-06 19:45:10 -0800 | [diff] [blame] | 1454 |     d = common.Difference(target_boot, source_boot) | 
 | 1455 |     _, _, d = d.ComputePatch() | 
| Doug Zongker | f834008 | 2014-08-05 10:39:37 -0700 | [diff] [blame] | 1456 |     if d is None: | 
 | 1457 |       include_full_boot = True | 
 | 1458 |       common.ZipWriteStr(output_zip, "boot.img", target_boot.data) | 
 | 1459 |     else: | 
 | 1460 |       include_full_boot = False | 
| Geremy Condra | 36bd365 | 2014-02-06 19:45:10 -0800 | [diff] [blame] | 1461 |  | 
| Tao Bao | 32fcdab | 2018-10-12 10:30:39 -0700 | [diff] [blame] | 1462 |       logger.info( | 
 | 1463 |           "boot      target: %d  source: %d  diff: %d", target_boot.size, | 
 | 1464 |           source_boot.size, len(d)) | 
| Geremy Condra | 36bd365 | 2014-02-06 19:45:10 -0800 | [diff] [blame] | 1465 |  | 
| Tao Bao | 5121655 | 2018-08-26 11:53:15 -0700 | [diff] [blame] | 1466 |       common.ZipWriteStr(output_zip, "boot.img.p", d) | 
| Geremy Condra | 36bd365 | 2014-02-06 19:45:10 -0800 | [diff] [blame] | 1467 |  | 
| Tao Bao | 5121655 | 2018-08-26 11:53:15 -0700 | [diff] [blame] | 1468 |       script.PatchPartitionCheck( | 
 | 1469 |           "{}:{}:{}:{}".format( | 
 | 1470 |               boot_type, boot_device, target_boot.size, target_boot.sha1), | 
 | 1471 |           "{}:{}:{}:{}".format( | 
 | 1472 |               boot_type, boot_device, source_boot.size, source_boot.sha1)) | 
 | 1473 |  | 
| Tianjie Xu | f67dd80 | 2019-05-20 17:50:36 -0700 | [diff] [blame] | 1474 |       required_cache_sizes.append(target_boot.size) | 
| Tao Bao | d8d14be | 2016-02-04 14:26:02 -0800 | [diff] [blame] | 1475 |  | 
| Tianjie Xu | f67dd80 | 2019-05-20 17:50:36 -0700 | [diff] [blame] | 1476 |   if required_cache_sizes: | 
 | 1477 |     script.CacheFreeSpaceCheck(max(required_cache_sizes)) | 
 | 1478 |  | 
 | 1479 |   # Verify the existing partitions. | 
 | 1480 |   for diff in block_diff_dict.values(): | 
 | 1481 |     diff.WriteVerifyScript(script, touched_blocks_only=True) | 
| Geremy Condra | 36bd365 | 2014-02-06 19:45:10 -0800 | [diff] [blame] | 1482 |  | 
 | 1483 |   device_specific.IncrementalOTA_VerifyEnd() | 
 | 1484 |  | 
 | 1485 |   if OPTIONS.two_step: | 
| Tao Bao | d42e97e | 2016-11-30 12:11:57 -0800 | [diff] [blame] | 1486 |     # Stage 1/3: (b) Write recovery image to /boot. | 
 | 1487 |     _WriteRecoveryImageToBoot(script, output_zip) | 
 | 1488 |  | 
| Geremy Condra | 36bd365 | 2014-02-06 19:45:10 -0800 | [diff] [blame] | 1489 |     script.AppendExtra(""" | 
 | 1490 | set_stage("%(bcb_dev)s", "2/3"); | 
 | 1491 | reboot_now("%(bcb_dev)s", ""); | 
 | 1492 | else | 
 | 1493 | """ % bcb_dev) | 
 | 1494 |  | 
| Tao Bao | d42e97e | 2016-11-30 12:11:57 -0800 | [diff] [blame] | 1495 |     # Stage 3/3: Make changes. | 
 | 1496 |     script.Comment("Stage 3/3") | 
 | 1497 |  | 
| Geremy Condra | 36bd365 | 2014-02-06 19:45:10 -0800 | [diff] [blame] | 1498 |   script.Comment("---- start making changes here ----") | 
 | 1499 |  | 
 | 1500 |   device_specific.IncrementalOTA_InstallBegin() | 
 | 1501 |  | 
| Tianjie Xu | f67dd80 | 2019-05-20 17:50:36 -0700 | [diff] [blame] | 1502 |   progress_dict = {partition: 0.1 for partition in block_diff_dict} | 
 | 1503 |   progress_dict["system"] = 1 - len(block_diff_dict) * 0.1 | 
| Yifan Hong | 10c530d | 2018-12-27 17:34:18 -0800 | [diff] [blame] | 1504 |  | 
 | 1505 |   if OPTIONS.source_info_dict.get("use_dynamic_partitions") == "true": | 
 | 1506 |     if OPTIONS.target_info_dict.get("use_dynamic_partitions") != "true": | 
 | 1507 |       raise RuntimeError( | 
 | 1508 |           "can't generate incremental that disables dynamic partitions") | 
 | 1509 |     dynamic_partitions_diff = common.DynamicPartitionsDifference( | 
 | 1510 |         info_dict=OPTIONS.target_info_dict, | 
 | 1511 |         source_info_dict=OPTIONS.source_info_dict, | 
| Tianjie Xu | f67dd80 | 2019-05-20 17:50:36 -0700 | [diff] [blame] | 1512 |         block_diffs=block_diff_dict.values(), | 
| Yifan Hong | 10c530d | 2018-12-27 17:34:18 -0800 | [diff] [blame] | 1513 |         progress_dict=progress_dict) | 
 | 1514 |     dynamic_partitions_diff.WriteScript( | 
 | 1515 |         script, output_zip, write_verify_script=OPTIONS.verify) | 
 | 1516 |   else: | 
| Tianjie Xu | f67dd80 | 2019-05-20 17:50:36 -0700 | [diff] [blame] | 1517 |     for block_diff in block_diff_dict.values(): | 
| Yifan Hong | 10c530d | 2018-12-27 17:34:18 -0800 | [diff] [blame] | 1518 |       block_diff.WriteScript(script, output_zip, | 
 | 1519 |                              progress=progress_dict.get(block_diff.partition), | 
 | 1520 |                              write_verify_script=OPTIONS.verify) | 
| Geremy Condra | 36bd365 | 2014-02-06 19:45:10 -0800 | [diff] [blame] | 1521 |  | 
 | 1522 |   if OPTIONS.two_step: | 
 | 1523 |     common.ZipWriteStr(output_zip, "boot.img", target_boot.data) | 
 | 1524 |     script.WriteRawImage("/boot", "boot.img") | 
| Tao Bao | 32fcdab | 2018-10-12 10:30:39 -0700 | [diff] [blame] | 1525 |     logger.info("writing full boot image (forced by two-step mode)") | 
| Geremy Condra | 36bd365 | 2014-02-06 19:45:10 -0800 | [diff] [blame] | 1526 |  | 
 | 1527 |   if not OPTIONS.two_step: | 
 | 1528 |     if updating_boot: | 
| Doug Zongker | f834008 | 2014-08-05 10:39:37 -0700 | [diff] [blame] | 1529 |       if include_full_boot: | 
| Tao Bao | 32fcdab | 2018-10-12 10:30:39 -0700 | [diff] [blame] | 1530 |         logger.info("boot image changed; including full.") | 
| Doug Zongker | f834008 | 2014-08-05 10:39:37 -0700 | [diff] [blame] | 1531 |         script.Print("Installing boot image...") | 
 | 1532 |         script.WriteRawImage("/boot", "boot.img") | 
 | 1533 |       else: | 
 | 1534 |         # Produce the boot image by applying a patch to the current | 
 | 1535 |         # contents of the boot partition, and write it back to the | 
 | 1536 |         # partition. | 
| Tao Bao | 32fcdab | 2018-10-12 10:30:39 -0700 | [diff] [blame] | 1537 |         logger.info("boot image changed; including patch.") | 
| Doug Zongker | f834008 | 2014-08-05 10:39:37 -0700 | [diff] [blame] | 1538 |         script.Print("Patching boot image...") | 
 | 1539 |         script.ShowProgress(0.1, 10) | 
| Tao Bao | 5121655 | 2018-08-26 11:53:15 -0700 | [diff] [blame] | 1540 |         script.PatchPartition( | 
 | 1541 |             '{}:{}:{}:{}'.format( | 
 | 1542 |                 boot_type, boot_device, target_boot.size, target_boot.sha1), | 
 | 1543 |             '{}:{}:{}:{}'.format( | 
 | 1544 |                 boot_type, boot_device, source_boot.size, source_boot.sha1), | 
 | 1545 |             'boot.img.p') | 
| Geremy Condra | 36bd365 | 2014-02-06 19:45:10 -0800 | [diff] [blame] | 1546 |     else: | 
| Tao Bao | 32fcdab | 2018-10-12 10:30:39 -0700 | [diff] [blame] | 1547 |       logger.info("boot image unchanged; skipping.") | 
| Geremy Condra | 36bd365 | 2014-02-06 19:45:10 -0800 | [diff] [blame] | 1548 |  | 
 | 1549 |   # Do device-specific installation (eg, write radio image). | 
 | 1550 |   device_specific.IncrementalOTA_InstallEnd() | 
 | 1551 |  | 
 | 1552 |   if OPTIONS.extra_script is not None: | 
 | 1553 |     script.AppendExtra(OPTIONS.extra_script) | 
 | 1554 |  | 
| Doug Zongker | 922206e | 2014-03-04 13:16:24 -0800 | [diff] [blame] | 1555 |   if OPTIONS.wipe_user_data: | 
 | 1556 |     script.Print("Erasing user data...") | 
 | 1557 |     script.FormatPartition("/data") | 
 | 1558 |  | 
| Geremy Condra | 36bd365 | 2014-02-06 19:45:10 -0800 | [diff] [blame] | 1559 |   if OPTIONS.two_step: | 
 | 1560 |     script.AppendExtra(""" | 
 | 1561 | set_stage("%(bcb_dev)s", ""); | 
 | 1562 | endif; | 
 | 1563 | endif; | 
 | 1564 | """ % bcb_dev) | 
 | 1565 |  | 
 | 1566 |   script.SetProgress(1) | 
| Tao Bao | 4996cf0 | 2016-03-08 17:53:39 -0800 | [diff] [blame] | 1567 |   # For downgrade OTAs, we prefer to use the update-binary in the source | 
 | 1568 |   # build that is actually newer than the one in the target build. | 
 | 1569 |   if OPTIONS.downgrade: | 
 | 1570 |     script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary) | 
 | 1571 |   else: | 
 | 1572 |     script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary) | 
| Tao Bao | d8d14be | 2016-02-04 14:26:02 -0800 | [diff] [blame] | 1573 |   metadata["ota-required-cache"] = str(script.required_cache) | 
| Tao Bao | 491d7e2 | 2018-02-21 13:17:22 -0800 | [diff] [blame] | 1574 |  | 
 | 1575 |   # We haven't written the metadata entry yet, which will be handled in | 
 | 1576 |   # FinalizeMetadata(). | 
 | 1577 |   common.ZipClose(output_zip) | 
 | 1578 |  | 
 | 1579 |   # Sign the generated zip package unless no_signing is specified. | 
 | 1580 |   needed_property_files = ( | 
 | 1581 |       NonAbOtaPropertyFiles(), | 
 | 1582 |   ) | 
 | 1583 |   FinalizeMetadata(metadata, staging_file, output_file, needed_property_files) | 
| Geremy Condra | 36bd365 | 2014-02-06 19:45:10 -0800 | [diff] [blame] | 1584 |  | 
| Doug Zongker | 32b527d | 2014-03-04 10:03:02 -0800 | [diff] [blame] | 1585 |  | 
| Tao Bao | 15a146a | 2018-02-21 16:06:59 -0800 | [diff] [blame] | 1586 | def GetTargetFilesZipForSecondaryImages(input_file, skip_postinstall=False): | 
| Tao Bao | f7140c0 | 2018-01-30 17:09:24 -0800 | [diff] [blame] | 1587 |   """Returns a target-files.zip file for generating secondary payload. | 
 | 1588 |  | 
 | 1589 |   Although the original target-files.zip already contains secondary slot | 
 | 1590 |   images (i.e. IMAGES/system_other.img), we need to rename the files to the | 
 | 1591 |   ones without _other suffix. Note that we cannot instead modify the names in | 
 | 1592 |   META/ab_partitions.txt, because there are no matching partitions on device. | 
 | 1593 |  | 
 | 1594 |   For the partitions that don't have secondary images, the ones for primary | 
 | 1595 |   slot will be used. This is to ensure that we always have valid boot, vbmeta, | 
 | 1596 |   bootloader images in the inactive slot. | 
 | 1597 |  | 
 | 1598 |   Args: | 
 | 1599 |     input_file: The input target-files.zip file. | 
| Tao Bao | 15a146a | 2018-02-21 16:06:59 -0800 | [diff] [blame] | 1600 |     skip_postinstall: Whether to skip copying the postinstall config file. | 
| Tao Bao | f7140c0 | 2018-01-30 17:09:24 -0800 | [diff] [blame] | 1601 |  | 
 | 1602 |   Returns: | 
 | 1603 |     The filename of the target-files.zip for generating secondary payload. | 
 | 1604 |   """ | 
| Tianjie Xu | 1c80800 | 2019-09-11 00:29:26 -0700 | [diff] [blame] | 1605 |  | 
 | 1606 |   def GetInfoForSecondaryImages(info_file): | 
 | 1607 |     """Updates info file for secondary payload generation. | 
 | 1608 |  | 
 | 1609 |     Scan each line in the info file, and remove the unwanted partitions from | 
 | 1610 |     the dynamic partition list in the related properties. e.g. | 
 | 1611 |     "super_google_dynamic_partitions_partition_list=system vendor product" | 
 | 1612 |     will become "super_google_dynamic_partitions_partition_list=system". | 
 | 1613 |  | 
 | 1614 |     Args: | 
 | 1615 |       info_file: The input info file. e.g. misc_info.txt. | 
 | 1616 |  | 
 | 1617 |     Returns: | 
 | 1618 |       A string of the updated info content. | 
 | 1619 |     """ | 
 | 1620 |  | 
 | 1621 |     output_list = [] | 
 | 1622 |     with open(info_file) as f: | 
 | 1623 |       lines = f.read().splitlines() | 
 | 1624 |  | 
 | 1625 |     # The suffix in partition_list variables that follows the name of the | 
 | 1626 |     # partition group. | 
 | 1627 |     LIST_SUFFIX = 'partition_list' | 
 | 1628 |     for line in lines: | 
 | 1629 |       if line.startswith('#') or '=' not in line: | 
 | 1630 |         output_list.append(line) | 
 | 1631 |         continue | 
 | 1632 |       key, value = line.strip().split('=', 1) | 
 | 1633 |       if key == 'dynamic_partition_list' or key.endswith(LIST_SUFFIX): | 
 | 1634 |         partitions = value.split() | 
 | 1635 |         partitions = [partition for partition in partitions if partition | 
| Tao Bao | 3e75946 | 2019-09-17 22:43:11 -0700 | [diff] [blame] | 1636 |                       not in SECONDARY_PAYLOAD_SKIPPED_IMAGES] | 
| Tianjie Xu | 1c80800 | 2019-09-11 00:29:26 -0700 | [diff] [blame] | 1637 |         output_list.append('{}={}'.format(key, ' '.join(partitions))) | 
 | 1638 |       else: | 
 | 1639 |         output_list.append(line) | 
 | 1640 |     return '\n'.join(output_list) | 
 | 1641 |  | 
| Tao Bao | f7140c0 | 2018-01-30 17:09:24 -0800 | [diff] [blame] | 1642 |   target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip") | 
 | 1643 |   target_zip = zipfile.ZipFile(target_file, 'w', allowZip64=True) | 
 | 1644 |  | 
| Tao Bao | dba59ee | 2018-01-09 13:21:02 -0800 | [diff] [blame] | 1645 |   with zipfile.ZipFile(input_file, 'r') as input_zip: | 
 | 1646 |     infolist = input_zip.infolist() | 
| Tao Bao | 1248980 | 2018-07-12 14:47:38 -0700 | [diff] [blame] | 1647 |  | 
| Tao Bao | 0ff15de | 2019-03-20 11:26:06 -0700 | [diff] [blame] | 1648 |   input_tmp = common.UnzipTemp(input_file, UNZIP_PATTERN) | 
| Tao Bao | dba59ee | 2018-01-09 13:21:02 -0800 | [diff] [blame] | 1649 |   for info in infolist: | 
| Tao Bao | f7140c0 | 2018-01-30 17:09:24 -0800 | [diff] [blame] | 1650 |     unzipped_file = os.path.join(input_tmp, *info.filename.split('/')) | 
 | 1651 |     if info.filename == 'IMAGES/system_other.img': | 
 | 1652 |       common.ZipWrite(target_zip, unzipped_file, arcname='IMAGES/system.img') | 
 | 1653 |  | 
 | 1654 |     # Primary images and friends need to be skipped explicitly. | 
 | 1655 |     elif info.filename in ('IMAGES/system.img', | 
 | 1656 |                            'IMAGES/system.map'): | 
 | 1657 |       pass | 
| Tao Bao | 3e75946 | 2019-09-17 22:43:11 -0700 | [diff] [blame] | 1658 |  | 
 | 1659 |     # Copy images that are not in SECONDARY_PAYLOAD_SKIPPED_IMAGES. | 
 | 1660 |     elif info.filename.startswith(('IMAGES/', 'RADIO/')): | 
 | 1661 |       image_name = os.path.basename(info.filename) | 
 | 1662 |       if image_name not in ['{}.img'.format(partition) for partition in | 
 | 1663 |                             SECONDARY_PAYLOAD_SKIPPED_IMAGES]: | 
 | 1664 |         common.ZipWrite(target_zip, unzipped_file, arcname=info.filename) | 
| Tao Bao | f7140c0 | 2018-01-30 17:09:24 -0800 | [diff] [blame] | 1665 |  | 
| Tao Bao | 15a146a | 2018-02-21 16:06:59 -0800 | [diff] [blame] | 1666 |     # Skip copying the postinstall config if requested. | 
 | 1667 |     elif skip_postinstall and info.filename == POSTINSTALL_CONFIG: | 
 | 1668 |       pass | 
 | 1669 |  | 
| Tianjie Xu | 1c80800 | 2019-09-11 00:29:26 -0700 | [diff] [blame] | 1670 |     elif info.filename.startswith('META/'): | 
 | 1671 |       # Remove the unnecessary partitions for secondary images from the | 
 | 1672 |       # ab_partitions file. | 
 | 1673 |       if info.filename == AB_PARTITIONS: | 
 | 1674 |         with open(unzipped_file) as f: | 
 | 1675 |           partition_list = f.read().splitlines() | 
 | 1676 |         partition_list = [partition for partition in partition_list if partition | 
| Tao Bao | 3e75946 | 2019-09-17 22:43:11 -0700 | [diff] [blame] | 1677 |                           and partition not in SECONDARY_PAYLOAD_SKIPPED_IMAGES] | 
| Tianjie Xu | 1c80800 | 2019-09-11 00:29:26 -0700 | [diff] [blame] | 1678 |         common.ZipWriteStr(target_zip, info.filename, '\n'.join(partition_list)) | 
 | 1679 |       # Remove the unnecessary partitions from the dynamic partitions list. | 
 | 1680 |       elif (info.filename == 'META/misc_info.txt' or | 
 | 1681 |             info.filename == DYNAMIC_PARTITION_INFO): | 
 | 1682 |         modified_info = GetInfoForSecondaryImages(unzipped_file) | 
 | 1683 |         common.ZipWriteStr(target_zip, info.filename, modified_info) | 
 | 1684 |       else: | 
 | 1685 |         common.ZipWrite(target_zip, unzipped_file, arcname=info.filename) | 
| Tao Bao | f7140c0 | 2018-01-30 17:09:24 -0800 | [diff] [blame] | 1686 |  | 
| Tao Bao | f7140c0 | 2018-01-30 17:09:24 -0800 | [diff] [blame] | 1687 |   common.ZipClose(target_zip) | 
 | 1688 |  | 
 | 1689 |   return target_file | 
 | 1690 |  | 
 | 1691 |  | 
| Tao Bao | 15a146a | 2018-02-21 16:06:59 -0800 | [diff] [blame] | 1692 | def GetTargetFilesZipWithoutPostinstallConfig(input_file): | 
 | 1693 |   """Returns a target-files.zip that's not containing postinstall_config.txt. | 
 | 1694 |  | 
 | 1695 |   This allows brillo_update_payload script to skip writing all the postinstall | 
 | 1696 |   hooks in the generated payload. The input target-files.zip file will be | 
 | 1697 |   duplicated, with 'META/postinstall_config.txt' skipped. If input_file doesn't | 
 | 1698 |   contain the postinstall_config.txt entry, the input file will be returned. | 
 | 1699 |  | 
 | 1700 |   Args: | 
 | 1701 |     input_file: The input target-files.zip filename. | 
 | 1702 |  | 
 | 1703 |   Returns: | 
 | 1704 |     The filename of target-files.zip that doesn't contain postinstall config. | 
 | 1705 |   """ | 
 | 1706 |   # We should only make a copy if postinstall_config entry exists. | 
 | 1707 |   with zipfile.ZipFile(input_file, 'r') as input_zip: | 
 | 1708 |     if POSTINSTALL_CONFIG not in input_zip.namelist(): | 
 | 1709 |       return input_file | 
 | 1710 |  | 
 | 1711 |   target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip") | 
 | 1712 |   shutil.copyfile(input_file, target_file) | 
 | 1713 |   common.ZipDelete(target_file, POSTINSTALL_CONFIG) | 
 | 1714 |   return target_file | 
 | 1715 |  | 
 | 1716 |  | 
| Yifan Hong | 50e7954 | 2018-11-08 17:44:12 -0800 | [diff] [blame] | 1717 | def GetTargetFilesZipForRetrofitDynamicPartitions(input_file, | 
| Yifan Hong | b433eba | 2019-03-06 12:42:53 -0800 | [diff] [blame] | 1718 |                                                   super_block_devices, | 
 | 1719 |                                                   dynamic_partition_list): | 
| Yifan Hong | 50e7954 | 2018-11-08 17:44:12 -0800 | [diff] [blame] | 1720 |   """Returns a target-files.zip for retrofitting dynamic partitions. | 
 | 1721 |  | 
 | 1722 |   This allows brillo_update_payload to generate an OTA based on the exact | 
 | 1723 |   bits on the block devices. Postinstall is disabled. | 
 | 1724 |  | 
 | 1725 |   Args: | 
 | 1726 |     input_file: The input target-files.zip filename. | 
 | 1727 |     super_block_devices: The list of super block devices | 
| Yifan Hong | b433eba | 2019-03-06 12:42:53 -0800 | [diff] [blame] | 1728 |     dynamic_partition_list: The list of dynamic partitions | 
| Yifan Hong | 50e7954 | 2018-11-08 17:44:12 -0800 | [diff] [blame] | 1729 |  | 
 | 1730 |   Returns: | 
 | 1731 |     The filename of target-files.zip with *.img replaced with super_*.img for | 
 | 1732 |     each block device in super_block_devices. | 
 | 1733 |   """ | 
 | 1734 |   assert super_block_devices, "No super_block_devices are specified." | 
 | 1735 |  | 
 | 1736 |   replace = {'OTA/super_{}.img'.format(dev): 'IMAGES/{}.img'.format(dev) | 
| Tao Bao | 03fecb6 | 2018-11-28 10:59:23 -0800 | [diff] [blame] | 1737 |              for dev in super_block_devices} | 
| Yifan Hong | 50e7954 | 2018-11-08 17:44:12 -0800 | [diff] [blame] | 1738 |  | 
 | 1739 |   target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip") | 
 | 1740 |   shutil.copyfile(input_file, target_file) | 
 | 1741 |  | 
| Tao Bao | a370545 | 2019-06-24 15:33:41 -0700 | [diff] [blame] | 1742 |   with zipfile.ZipFile(input_file) as input_zip: | 
| Yifan Hong | 50e7954 | 2018-11-08 17:44:12 -0800 | [diff] [blame] | 1743 |     namelist = input_zip.namelist() | 
 | 1744 |  | 
| Yifan Hong | b433eba | 2019-03-06 12:42:53 -0800 | [diff] [blame] | 1745 |   input_tmp = common.UnzipTemp(input_file, RETROFIT_DAP_UNZIP_PATTERN) | 
 | 1746 |  | 
 | 1747 |   # Remove partitions from META/ab_partitions.txt that is in | 
 | 1748 |   # dynamic_partition_list but not in super_block_devices so that | 
 | 1749 |   # brillo_update_payload won't generate update for those logical partitions. | 
 | 1750 |   ab_partitions_file = os.path.join(input_tmp, *AB_PARTITIONS.split('/')) | 
 | 1751 |   with open(ab_partitions_file) as f: | 
 | 1752 |     ab_partitions_lines = f.readlines() | 
 | 1753 |     ab_partitions = [line.strip() for line in ab_partitions_lines] | 
 | 1754 |   # Assert that all super_block_devices are in ab_partitions | 
 | 1755 |   super_device_not_updated = [partition for partition in super_block_devices | 
 | 1756 |                               if partition not in ab_partitions] | 
 | 1757 |   assert not super_device_not_updated, \ | 
 | 1758 |       "{} is in super_block_devices but not in {}".format( | 
 | 1759 |           super_device_not_updated, AB_PARTITIONS) | 
 | 1760 |   # ab_partitions -= (dynamic_partition_list - super_block_devices) | 
 | 1761 |   new_ab_partitions = common.MakeTempFile(prefix="ab_partitions", suffix=".txt") | 
 | 1762 |   with open(new_ab_partitions, 'w') as f: | 
 | 1763 |     for partition in ab_partitions: | 
 | 1764 |       if (partition in dynamic_partition_list and | 
 | 1765 |           partition not in super_block_devices): | 
| Tao Bao | 59cf0c5 | 2019-06-25 10:04:24 -0700 | [diff] [blame] | 1766 |         logger.info("Dropping %s from ab_partitions.txt", partition) | 
 | 1767 |         continue | 
| Yifan Hong | b433eba | 2019-03-06 12:42:53 -0800 | [diff] [blame] | 1768 |       f.write(partition + "\n") | 
 | 1769 |   to_delete = [AB_PARTITIONS] | 
 | 1770 |  | 
| Yifan Hong | 50e7954 | 2018-11-08 17:44:12 -0800 | [diff] [blame] | 1771 |   # Always skip postinstall for a retrofit update. | 
| Yifan Hong | b433eba | 2019-03-06 12:42:53 -0800 | [diff] [blame] | 1772 |   to_delete += [POSTINSTALL_CONFIG] | 
| Yifan Hong | 50e7954 | 2018-11-08 17:44:12 -0800 | [diff] [blame] | 1773 |  | 
 | 1774 |   # Delete dynamic_partitions_info.txt so that brillo_update_payload thinks this | 
 | 1775 |   # is a regular update on devices without dynamic partitions support. | 
 | 1776 |   to_delete += [DYNAMIC_PARTITION_INFO] | 
 | 1777 |  | 
| Tao Bao | 03fecb6 | 2018-11-28 10:59:23 -0800 | [diff] [blame] | 1778 |   # Remove the existing partition images as well as the map files. | 
| Tao Bao | 59cf0c5 | 2019-06-25 10:04:24 -0700 | [diff] [blame] | 1779 |   to_delete += list(replace.values()) | 
| Tao Bao | 03fecb6 | 2018-11-28 10:59:23 -0800 | [diff] [blame] | 1780 |   to_delete += ['IMAGES/{}.map'.format(dev) for dev in super_block_devices] | 
| Yifan Hong | 50e7954 | 2018-11-08 17:44:12 -0800 | [diff] [blame] | 1781 |  | 
 | 1782 |   common.ZipDelete(target_file, to_delete) | 
 | 1783 |  | 
| Yifan Hong | 50e7954 | 2018-11-08 17:44:12 -0800 | [diff] [blame] | 1784 |   target_zip = zipfile.ZipFile(target_file, 'a', allowZip64=True) | 
 | 1785 |  | 
 | 1786 |   # Write super_{foo}.img as {foo}.img. | 
 | 1787 |   for src, dst in replace.items(): | 
 | 1788 |     assert src in namelist, \ | 
| Tao Bao | 59cf0c5 | 2019-06-25 10:04:24 -0700 | [diff] [blame] | 1789 |         'Missing {} in {}; {} cannot be written'.format(src, input_file, dst) | 
| Yifan Hong | 50e7954 | 2018-11-08 17:44:12 -0800 | [diff] [blame] | 1790 |     unzipped_file = os.path.join(input_tmp, *src.split('/')) | 
 | 1791 |     common.ZipWrite(target_zip, unzipped_file, arcname=dst) | 
 | 1792 |  | 
| Yifan Hong | b433eba | 2019-03-06 12:42:53 -0800 | [diff] [blame] | 1793 |   # Write new ab_partitions.txt file | 
 | 1794 |   common.ZipWrite(target_zip, new_ab_partitions, arcname=AB_PARTITIONS) | 
 | 1795 |  | 
| Yifan Hong | 50e7954 | 2018-11-08 17:44:12 -0800 | [diff] [blame] | 1796 |   common.ZipClose(target_zip) | 
 | 1797 |  | 
 | 1798 |   return target_file | 
 | 1799 |  | 
 | 1800 |  | 
| Tao Bao | f0c4aa2 | 2018-04-30 20:29:30 -0700 | [diff] [blame] | 1801 | def GenerateAbOtaPackage(target_file, output_file, source_file=None): | 
| Tao Bao | fe5b69a | 2018-03-02 09:47:43 -0800 | [diff] [blame] | 1802 |   """Generates an Android OTA package that has A/B update payload.""" | 
| Tao Bao | dea0f8b | 2016-06-20 17:55:06 -0700 | [diff] [blame] | 1803 |   # Stage the output zip package for package signing. | 
| Tao Bao | 491d7e2 | 2018-02-21 13:17:22 -0800 | [diff] [blame] | 1804 |   if not OPTIONS.no_signing: | 
 | 1805 |     staging_file = common.MakeTempFile(suffix='.zip') | 
 | 1806 |   else: | 
 | 1807 |     staging_file = output_file | 
| Tao Bao | a652c00 | 2018-03-01 19:31:38 -0800 | [diff] [blame] | 1808 |   output_zip = zipfile.ZipFile(staging_file, "w", | 
| Tao Bao | c098e9e | 2016-01-07 13:03:56 -0800 | [diff] [blame] | 1809 |                                compression=zipfile.ZIP_DEFLATED) | 
 | 1810 |  | 
| Tao Bao | 481bab8 | 2017-12-21 11:23:09 -0800 | [diff] [blame] | 1811 |   if source_file is not None: | 
| Tao Bao | 1c320f8 | 2019-10-04 23:25:12 -0700 | [diff] [blame] | 1812 |     target_info = common.BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts) | 
 | 1813 |     source_info = common.BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts) | 
| Tao Bao | 481bab8 | 2017-12-21 11:23:09 -0800 | [diff] [blame] | 1814 |   else: | 
| Tao Bao | 1c320f8 | 2019-10-04 23:25:12 -0700 | [diff] [blame] | 1815 |     target_info = common.BuildInfo(OPTIONS.info_dict, OPTIONS.oem_dicts) | 
| Tao Bao | 481bab8 | 2017-12-21 11:23:09 -0800 | [diff] [blame] | 1816 |     source_info = None | 
| Tao Bao | c098e9e | 2016-01-07 13:03:56 -0800 | [diff] [blame] | 1817 |  | 
| Tao Bao | 481bab8 | 2017-12-21 11:23:09 -0800 | [diff] [blame] | 1818 |   # Metadata to comply with Android OTA package format. | 
| Tao Bao | df3a48b | 2018-01-10 16:30:43 -0800 | [diff] [blame] | 1819 |   metadata = GetPackageMetadata(target_info, source_info) | 
| Tao Bao | b31892e | 2017-02-07 11:21:17 -0800 | [diff] [blame] | 1820 |  | 
| Yifan Hong | 50e7954 | 2018-11-08 17:44:12 -0800 | [diff] [blame] | 1821 |   if OPTIONS.retrofit_dynamic_partitions: | 
 | 1822 |     target_file = GetTargetFilesZipForRetrofitDynamicPartitions( | 
| Yifan Hong | b433eba | 2019-03-06 12:42:53 -0800 | [diff] [blame] | 1823 |         target_file, target_info.get("super_block_devices").strip().split(), | 
 | 1824 |         target_info.get("dynamic_partition_list").strip().split()) | 
| Yifan Hong | 50e7954 | 2018-11-08 17:44:12 -0800 | [diff] [blame] | 1825 |   elif OPTIONS.skip_postinstall: | 
| Tao Bao | 15a146a | 2018-02-21 16:06:59 -0800 | [diff] [blame] | 1826 |     target_file = GetTargetFilesZipWithoutPostinstallConfig(target_file) | 
 | 1827 |  | 
| Tao Bao | 40b1882 | 2018-01-30 18:19:04 -0800 | [diff] [blame] | 1828 |   # Generate payload. | 
 | 1829 |   payload = Payload() | 
 | 1830 |  | 
 | 1831 |   # Enforce a max timestamp this payload can be applied on top of. | 
| Tao Bao | ff1b86e | 2017-10-03 14:17:57 -0700 | [diff] [blame] | 1832 |   if OPTIONS.downgrade: | 
| Tao Bao | 2a12ed7 | 2018-01-22 11:35:00 -0800 | [diff] [blame] | 1833 |     max_timestamp = source_info.GetBuildProp("ro.build.date.utc") | 
| Tao Bao | ff1b86e | 2017-10-03 14:17:57 -0700 | [diff] [blame] | 1834 |   else: | 
 | 1835 |     max_timestamp = metadata["post-timestamp"] | 
| Tao Bao | 40b1882 | 2018-01-30 18:19:04 -0800 | [diff] [blame] | 1836 |   additional_args = ["--max_timestamp", max_timestamp] | 
| Tao Bao | c098e9e | 2016-01-07 13:03:56 -0800 | [diff] [blame] | 1837 |  | 
| Tao Bao | 40b1882 | 2018-01-30 18:19:04 -0800 | [diff] [blame] | 1838 |   payload.Generate(target_file, source_file, additional_args) | 
| Tao Bao | c098e9e | 2016-01-07 13:03:56 -0800 | [diff] [blame] | 1839 |  | 
| Tao Bao | 40b1882 | 2018-01-30 18:19:04 -0800 | [diff] [blame] | 1840 |   # Sign the payload. | 
| Tao Bao | f7140c0 | 2018-01-30 17:09:24 -0800 | [diff] [blame] | 1841 |   payload_signer = PayloadSigner() | 
 | 1842 |   payload.Sign(payload_signer) | 
| Tao Bao | c098e9e | 2016-01-07 13:03:56 -0800 | [diff] [blame] | 1843 |  | 
| Tao Bao | 40b1882 | 2018-01-30 18:19:04 -0800 | [diff] [blame] | 1844 |   # Write the payload into output zip. | 
 | 1845 |   payload.WriteToZip(output_zip) | 
| Tao Bao | c098e9e | 2016-01-07 13:03:56 -0800 | [diff] [blame] | 1846 |  | 
| Tao Bao | f7140c0 | 2018-01-30 17:09:24 -0800 | [diff] [blame] | 1847 |   # Generate and include the secondary payload that installs secondary images | 
 | 1848 |   # (e.g. system_other.img). | 
 | 1849 |   if OPTIONS.include_secondary: | 
 | 1850 |     # We always include a full payload for the secondary slot, even when | 
 | 1851 |     # building an incremental OTA. See the comments for "--include_secondary". | 
| Tao Bao | 15a146a | 2018-02-21 16:06:59 -0800 | [diff] [blame] | 1852 |     secondary_target_file = GetTargetFilesZipForSecondaryImages( | 
 | 1853 |         target_file, OPTIONS.skip_postinstall) | 
| Tao Bao | 667ff57 | 2018-02-10 00:02:40 -0800 | [diff] [blame] | 1854 |     secondary_payload = Payload(secondary=True) | 
| Tao Bao | db1fe41 | 2018-02-09 23:15:05 -0800 | [diff] [blame] | 1855 |     secondary_payload.Generate(secondary_target_file, | 
 | 1856 |                                additional_args=additional_args) | 
| Tao Bao | f7140c0 | 2018-01-30 17:09:24 -0800 | [diff] [blame] | 1857 |     secondary_payload.Sign(payload_signer) | 
| Tao Bao | 667ff57 | 2018-02-10 00:02:40 -0800 | [diff] [blame] | 1858 |     secondary_payload.WriteToZip(output_zip) | 
| Tao Bao | f7140c0 | 2018-01-30 17:09:24 -0800 | [diff] [blame] | 1859 |  | 
| Tianjie Xu | cfa8622 | 2016-03-07 16:31:19 -0800 | [diff] [blame] | 1860 |   # If dm-verity is supported for the device, copy contents of care_map | 
 | 1861 |   # into A/B OTA package. | 
| Tao Bao | 21803d3 | 2017-04-19 10:16:09 -0700 | [diff] [blame] | 1862 |   target_zip = zipfile.ZipFile(target_file, "r") | 
| Tao Bao | 481bab8 | 2017-12-21 11:23:09 -0800 | [diff] [blame] | 1863 |   if (target_info.get("verity") == "true" or | 
 | 1864 |       target_info.get("avb_enable") == "true"): | 
| Tianjie Xu | 4c05f4a | 2018-09-14 16:24:41 -0700 | [diff] [blame] | 1865 |     care_map_list = [x for x in ["care_map.pb", "care_map.txt"] if | 
 | 1866 |                      "META/" + x in target_zip.namelist()] | 
 | 1867 |  | 
 | 1868 |     # Adds care_map if either the protobuf format or the plain text one exists. | 
 | 1869 |     if care_map_list: | 
 | 1870 |       care_map_name = care_map_list[0] | 
 | 1871 |       care_map_data = target_zip.read("META/" + care_map_name) | 
 | 1872 |       # In order to support streaming, care_map needs to be packed as | 
| Tao Bao | 40b1882 | 2018-01-30 18:19:04 -0800 | [diff] [blame] | 1873 |       # ZIP_STORED. | 
| Tianjie Xu | 4c05f4a | 2018-09-14 16:24:41 -0700 | [diff] [blame] | 1874 |       common.ZipWriteStr(output_zip, care_map_name, care_map_data, | 
| Tao Bao | 481bab8 | 2017-12-21 11:23:09 -0800 | [diff] [blame] | 1875 |                          compress_type=zipfile.ZIP_STORED) | 
| Tianjie Xu | cfa8622 | 2016-03-07 16:31:19 -0800 | [diff] [blame] | 1876 |     else: | 
| Tao Bao | 32fcdab | 2018-10-12 10:30:39 -0700 | [diff] [blame] | 1877 |       logger.warning("Cannot find care map file in target_file package") | 
| Tao Bao | 21803d3 | 2017-04-19 10:16:09 -0700 | [diff] [blame] | 1878 |  | 
| Tao Bao | 21803d3 | 2017-04-19 10:16:09 -0700 | [diff] [blame] | 1879 |   common.ZipClose(target_zip) | 
| Tianjie Xu | cfa8622 | 2016-03-07 16:31:19 -0800 | [diff] [blame] | 1880 |  | 
| Yifan Hong | 9276cf0 | 2019-08-21 16:37:04 -0700 | [diff] [blame] | 1881 |   CheckVintfIfTrebleEnabled(target_file, target_info) | 
 | 1882 |  | 
| Tao Bao | fe5b69a | 2018-03-02 09:47:43 -0800 | [diff] [blame] | 1883 |   # We haven't written the metadata entry yet, which will be handled in | 
 | 1884 |   # FinalizeMetadata(). | 
| Tao Bao | c96316c | 2017-01-24 22:10:49 -0800 | [diff] [blame] | 1885 |   common.ZipClose(output_zip) | 
 | 1886 |  | 
| Tao Bao | 85f1698 | 2018-03-08 16:28:33 -0800 | [diff] [blame] | 1887 |   # AbOtaPropertyFiles intends to replace StreamingPropertyFiles, as it covers | 
 | 1888 |   # all the info of the latter. However, system updaters and OTA servers need to | 
 | 1889 |   # take time to switch to the new flag. We keep both of the flags for | 
 | 1890 |   # P-timeframe, and will remove StreamingPropertyFiles in later release. | 
| Tao Bao | d3fc38a | 2018-03-08 16:09:01 -0800 | [diff] [blame] | 1891 |   needed_property_files = ( | 
| Tao Bao | 85f1698 | 2018-03-08 16:28:33 -0800 | [diff] [blame] | 1892 |       AbOtaPropertyFiles(), | 
| Tao Bao | d3fc38a | 2018-03-08 16:09:01 -0800 | [diff] [blame] | 1893 |       StreamingPropertyFiles(), | 
 | 1894 |   ) | 
 | 1895 |   FinalizeMetadata(metadata, staging_file, output_file, needed_property_files) | 
| Tao Bao | c96316c | 2017-01-24 22:10:49 -0800 | [diff] [blame] | 1896 |  | 
| Tao Bao | c098e9e | 2016-01-07 13:03:56 -0800 | [diff] [blame] | 1897 |  | 
| Tao Bao | f0c4aa2 | 2018-04-30 20:29:30 -0700 | [diff] [blame] | 1898 | def GenerateNonAbOtaPackage(target_file, output_file, source_file=None): | 
 | 1899 |   """Generates a non-A/B OTA package.""" | 
 | 1900 |   # Sanity check the loaded info dicts first. | 
 | 1901 |   if OPTIONS.info_dict.get("no_recovery") == "true": | 
 | 1902 |     raise common.ExternalError( | 
 | 1903 |         "--- target build has specified no recovery ---") | 
 | 1904 |  | 
 | 1905 |   # Non-A/B OTAs rely on /cache partition to store temporary files. | 
 | 1906 |   cache_size = OPTIONS.info_dict.get("cache_size") | 
 | 1907 |   if cache_size is None: | 
 | 1908 |     logger.warning("--- can't determine the cache partition size ---") | 
 | 1909 |   OPTIONS.cache_size = cache_size | 
 | 1910 |  | 
 | 1911 |   if OPTIONS.extra_script is not None: | 
 | 1912 |     with open(OPTIONS.extra_script) as fp: | 
 | 1913 |       OPTIONS.extra_script = fp.read() | 
 | 1914 |  | 
 | 1915 |   if OPTIONS.extracted_input is not None: | 
 | 1916 |     OPTIONS.input_tmp = OPTIONS.extracted_input | 
 | 1917 |   else: | 
 | 1918 |     logger.info("unzipping target target-files...") | 
 | 1919 |     OPTIONS.input_tmp = common.UnzipTemp(target_file, UNZIP_PATTERN) | 
 | 1920 |   OPTIONS.target_tmp = OPTIONS.input_tmp | 
 | 1921 |  | 
 | 1922 |   # If the caller explicitly specified the device-specific extensions path via | 
 | 1923 |   # -s / --device_specific, use that. Otherwise, use META/releasetools.py if it | 
 | 1924 |   # is present in the target target_files. Otherwise, take the path of the file | 
 | 1925 |   # from 'tool_extensions' in the info dict and look for that in the local | 
 | 1926 |   # filesystem, relative to the current directory. | 
 | 1927 |   if OPTIONS.device_specific is None: | 
 | 1928 |     from_input = os.path.join(OPTIONS.input_tmp, "META", "releasetools.py") | 
 | 1929 |     if os.path.exists(from_input): | 
 | 1930 |       logger.info("(using device-specific extensions from target_files)") | 
 | 1931 |       OPTIONS.device_specific = from_input | 
 | 1932 |     else: | 
 | 1933 |       OPTIONS.device_specific = OPTIONS.info_dict.get("tool_extensions") | 
 | 1934 |  | 
 | 1935 |   if OPTIONS.device_specific is not None: | 
 | 1936 |     OPTIONS.device_specific = os.path.abspath(OPTIONS.device_specific) | 
 | 1937 |  | 
 | 1938 |   # Generate a full OTA. | 
 | 1939 |   if source_file is None: | 
 | 1940 |     with zipfile.ZipFile(target_file) as input_zip: | 
 | 1941 |       WriteFullOTAPackage( | 
 | 1942 |           input_zip, | 
 | 1943 |           output_file) | 
 | 1944 |  | 
 | 1945 |   # Generate an incremental OTA. | 
 | 1946 |   else: | 
 | 1947 |     logger.info("unzipping source target-files...") | 
 | 1948 |     OPTIONS.source_tmp = common.UnzipTemp( | 
 | 1949 |         OPTIONS.incremental_source, UNZIP_PATTERN) | 
 | 1950 |     with zipfile.ZipFile(target_file) as input_zip, \ | 
 | 1951 |         zipfile.ZipFile(source_file) as source_zip: | 
 | 1952 |       WriteBlockIncrementalOTAPackage( | 
 | 1953 |           input_zip, | 
 | 1954 |           source_zip, | 
 | 1955 |           output_file) | 
 | 1956 |  | 
 | 1957 |  | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 1958 | def main(argv): | 
 | 1959 |  | 
 | 1960 |   def option_handler(o, a): | 
| Tao Bao | 4b76a0e | 2017-10-31 12:13:33 -0700 | [diff] [blame] | 1961 |     if o in ("-k", "--package_key"): | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 1962 |       OPTIONS.package_key = a | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 1963 |     elif o in ("-i", "--incremental_from"): | 
 | 1964 |       OPTIONS.incremental_source = a | 
| Tao Bao | 43078aa | 2015-04-21 14:32:35 -0700 | [diff] [blame] | 1965 |     elif o == "--full_radio": | 
 | 1966 |       OPTIONS.full_radio = True | 
| leozwang | aa6c1a1 | 2015-08-14 10:57:58 -0700 | [diff] [blame] | 1967 |     elif o == "--full_bootloader": | 
 | 1968 |       OPTIONS.full_bootloader = True | 
| Tao Bao | 337633f | 2017-12-06 15:20:19 -0800 | [diff] [blame] | 1969 |     elif o == "--wipe_user_data": | 
| Doug Zongker | dbfaae5 | 2009-04-21 17:12:54 -0700 | [diff] [blame] | 1970 |       OPTIONS.wipe_user_data = True | 
| Tao Bao | 5d18256 | 2016-02-23 11:38:39 -0800 | [diff] [blame] | 1971 |     elif o == "--downgrade": | 
 | 1972 |       OPTIONS.downgrade = True | 
 | 1973 |       OPTIONS.wipe_user_data = True | 
| Tao Bao | 3e6161a | 2017-02-28 11:48:48 -0800 | [diff] [blame] | 1974 |     elif o == "--override_timestamp": | 
| Tao Bao | faa8e0b | 2018-04-12 14:31:43 -0700 | [diff] [blame] | 1975 |       OPTIONS.downgrade = True | 
| Michael Runge | 6e83611 | 2014-04-15 17:40:21 -0700 | [diff] [blame] | 1976 |     elif o in ("-o", "--oem_settings"): | 
| Alain Vongsouvanh | 7f804ba | 2017-02-16 13:06:55 -0800 | [diff] [blame] | 1977 |       OPTIONS.oem_source = a.split(',') | 
| Tao Bao | 8608cde | 2016-02-25 19:49:55 -0800 | [diff] [blame] | 1978 |     elif o == "--oem_no_mount": | 
 | 1979 |       OPTIONS.oem_no_mount = True | 
| Doug Zongker | 1c390a2 | 2009-05-14 19:06:36 -0700 | [diff] [blame] | 1980 |     elif o in ("-e", "--extra_script"): | 
 | 1981 |       OPTIONS.extra_script = a | 
| Martin Blumenstingl | 374e114 | 2014-05-31 20:42:55 +0200 | [diff] [blame] | 1982 |     elif o in ("-t", "--worker_threads"): | 
 | 1983 |       if a.isdigit(): | 
 | 1984 |         OPTIONS.worker_threads = int(a) | 
 | 1985 |       else: | 
 | 1986 |         raise ValueError("Cannot parse value %r for option %r - only " | 
 | 1987 |                          "integers are allowed." % (a, o)) | 
| Doug Zongker | 9b23f2c | 2013-11-25 14:44:12 -0800 | [diff] [blame] | 1988 |     elif o in ("-2", "--two_step"): | 
 | 1989 |       OPTIONS.two_step = True | 
| Tao Bao | f7140c0 | 2018-01-30 17:09:24 -0800 | [diff] [blame] | 1990 |     elif o == "--include_secondary": | 
 | 1991 |       OPTIONS.include_secondary = True | 
| Doug Zongker | 26e6619 | 2014-02-20 13:22:07 -0800 | [diff] [blame] | 1992 |     elif o == "--no_signing": | 
| Takeshi Kanemoto | e153b34 | 2013-11-14 17:20:50 +0900 | [diff] [blame] | 1993 |       OPTIONS.no_signing = True | 
| Dan Albert | 8b72aef | 2015-03-23 19:13:21 -0700 | [diff] [blame] | 1994 |     elif o == "--verify": | 
| Michael Runge | 63f01de | 2014-10-28 19:24:19 -0700 | [diff] [blame] | 1995 |       OPTIONS.verify = True | 
| Doug Zongker | 26e6619 | 2014-02-20 13:22:07 -0800 | [diff] [blame] | 1996 |     elif o == "--block": | 
 | 1997 |       OPTIONS.block_based = True | 
| Doug Zongker | 2556848 | 2014-03-03 10:21:27 -0800 | [diff] [blame] | 1998 |     elif o in ("-b", "--binary"): | 
 | 1999 |       OPTIONS.updater_binary = a | 
| Tao Bao | 8dcf738 | 2015-05-21 14:09:49 -0700 | [diff] [blame] | 2000 |     elif o == "--stash_threshold": | 
 | 2001 |       try: | 
 | 2002 |         OPTIONS.stash_threshold = float(a) | 
 | 2003 |       except ValueError: | 
 | 2004 |         raise ValueError("Cannot parse value %r for option %r - expecting " | 
 | 2005 |                          "a float" % (a, o)) | 
| Tao Bao | d62c603 | 2015-11-30 09:40:20 -0800 | [diff] [blame] | 2006 |     elif o == "--log_diff": | 
 | 2007 |       OPTIONS.log_diff = a | 
| Tao Bao | dea0f8b | 2016-06-20 17:55:06 -0700 | [diff] [blame] | 2008 |     elif o == "--payload_signer": | 
 | 2009 |       OPTIONS.payload_signer = a | 
| Baligh Uddin | 2abbbd0 | 2016-06-22 12:14:16 -0700 | [diff] [blame] | 2010 |     elif o == "--payload_signer_args": | 
 | 2011 |       OPTIONS.payload_signer_args = shlex.split(a) | 
| Tianjie Xu | 21e6deb | 2019-10-07 18:01:00 -0700 | [diff] [blame] | 2012 |     elif o == "--payload_signer_maximum_signature_size": | 
 | 2013 |       OPTIONS.payload_signer_maximum_signature_size = a | 
| xunchang | 376cc7c | 2019-04-08 23:04:58 -0700 | [diff] [blame] | 2014 |     elif o == "--payload_signer_key_size": | 
| Tianjie Xu | 21e6deb | 2019-10-07 18:01:00 -0700 | [diff] [blame] | 2015 |       # TODO(Xunchang) remove this option after cleaning up the callers. | 
 | 2016 |       logger.warning("The option '--payload_signer_key_size' is deprecated." | 
 | 2017 |                      " Use '--payload_signer_maximum_signature_size' instead.") | 
 | 2018 |       OPTIONS.payload_signer_maximum_signature_size = a | 
| Dan Willemsen | cea5cd2 | 2017-03-21 14:44:27 -0700 | [diff] [blame] | 2019 |     elif o == "--extracted_input_target_files": | 
 | 2020 |       OPTIONS.extracted_input = a | 
| Tao Bao | 15a146a | 2018-02-21 16:06:59 -0800 | [diff] [blame] | 2021 |     elif o == "--skip_postinstall": | 
 | 2022 |       OPTIONS.skip_postinstall = True | 
| Yifan Hong | 50e7954 | 2018-11-08 17:44:12 -0800 | [diff] [blame] | 2023 |     elif o == "--retrofit_dynamic_partitions": | 
 | 2024 |       OPTIONS.retrofit_dynamic_partitions = True | 
| xunchang | abfa265 | 2019-02-19 16:27:10 -0800 | [diff] [blame] | 2025 |     elif o == "--skip_compatibility_check": | 
 | 2026 |       OPTIONS.skip_compatibility_check = True | 
| xunchang | 1cfe251 | 2019-02-19 14:14:48 -0800 | [diff] [blame] | 2027 |     elif o == "--output_metadata_path": | 
 | 2028 |       OPTIONS.output_metadata_path = a | 
| Tianjie Xu | 1b07983 | 2019-08-28 12:19:23 -0700 | [diff] [blame] | 2029 |     elif o == "--disable_fec_computation": | 
 | 2030 |       OPTIONS.disable_fec_computation = True | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 2031 |     else: | 
 | 2032 |       return False | 
| Doug Zongker | dbfaae5 | 2009-04-21 17:12:54 -0700 | [diff] [blame] | 2033 |     return True | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 2034 |  | 
 | 2035 |   args = common.ParseOptions(argv, __doc__, | 
| Tao Bao | 337633f | 2017-12-06 15:20:19 -0800 | [diff] [blame] | 2036 |                              extra_opts="b:k:i:d:e:t:2o:", | 
| Dan Albert | 8b72aef | 2015-03-23 19:13:21 -0700 | [diff] [blame] | 2037 |                              extra_long_opts=[ | 
| Dan Albert | 8b72aef | 2015-03-23 19:13:21 -0700 | [diff] [blame] | 2038 |                                  "package_key=", | 
 | 2039 |                                  "incremental_from=", | 
| Tao Bao | 43078aa | 2015-04-21 14:32:35 -0700 | [diff] [blame] | 2040 |                                  "full_radio", | 
| leozwang | aa6c1a1 | 2015-08-14 10:57:58 -0700 | [diff] [blame] | 2041 |                                  "full_bootloader", | 
| Dan Albert | 8b72aef | 2015-03-23 19:13:21 -0700 | [diff] [blame] | 2042 |                                  "wipe_user_data", | 
| Tao Bao | 5d18256 | 2016-02-23 11:38:39 -0800 | [diff] [blame] | 2043 |                                  "downgrade", | 
| Tao Bao | 3e6161a | 2017-02-28 11:48:48 -0800 | [diff] [blame] | 2044 |                                  "override_timestamp", | 
| Dan Albert | 8b72aef | 2015-03-23 19:13:21 -0700 | [diff] [blame] | 2045 |                                  "extra_script=", | 
 | 2046 |                                  "worker_threads=", | 
| Dan Albert | 8b72aef | 2015-03-23 19:13:21 -0700 | [diff] [blame] | 2047 |                                  "two_step", | 
| Tao Bao | f7140c0 | 2018-01-30 17:09:24 -0800 | [diff] [blame] | 2048 |                                  "include_secondary", | 
| Dan Albert | 8b72aef | 2015-03-23 19:13:21 -0700 | [diff] [blame] | 2049 |                                  "no_signing", | 
 | 2050 |                                  "block", | 
 | 2051 |                                  "binary=", | 
 | 2052 |                                  "oem_settings=", | 
| Tao Bao | 8608cde | 2016-02-25 19:49:55 -0800 | [diff] [blame] | 2053 |                                  "oem_no_mount", | 
| Dan Albert | 8b72aef | 2015-03-23 19:13:21 -0700 | [diff] [blame] | 2054 |                                  "verify", | 
| Tao Bao | 8dcf738 | 2015-05-21 14:09:49 -0700 | [diff] [blame] | 2055 |                                  "stash_threshold=", | 
| Tao Bao | d62c603 | 2015-11-30 09:40:20 -0800 | [diff] [blame] | 2056 |                                  "log_diff=", | 
| Tao Bao | dea0f8b | 2016-06-20 17:55:06 -0700 | [diff] [blame] | 2057 |                                  "payload_signer=", | 
| Baligh Uddin | 2abbbd0 | 2016-06-22 12:14:16 -0700 | [diff] [blame] | 2058 |                                  "payload_signer_args=", | 
| Tianjie Xu | 21e6deb | 2019-10-07 18:01:00 -0700 | [diff] [blame] | 2059 |                                  "payload_signer_maximum_signature_size=", | 
| xunchang | 376cc7c | 2019-04-08 23:04:58 -0700 | [diff] [blame] | 2060 |                                  "payload_signer_key_size=", | 
| Dan Willemsen | cea5cd2 | 2017-03-21 14:44:27 -0700 | [diff] [blame] | 2061 |                                  "extracted_input_target_files=", | 
| Tao Bao | 15a146a | 2018-02-21 16:06:59 -0800 | [diff] [blame] | 2062 |                                  "skip_postinstall", | 
| Yifan Hong | 50e7954 | 2018-11-08 17:44:12 -0800 | [diff] [blame] | 2063 |                                  "retrofit_dynamic_partitions", | 
| xunchang | abfa265 | 2019-02-19 16:27:10 -0800 | [diff] [blame] | 2064 |                                  "skip_compatibility_check", | 
| xunchang | 1cfe251 | 2019-02-19 14:14:48 -0800 | [diff] [blame] | 2065 |                                  "output_metadata_path=", | 
| Tianjie Xu | 1b07983 | 2019-08-28 12:19:23 -0700 | [diff] [blame] | 2066 |                                  "disable_fec_computation", | 
| Dan Albert | 8b72aef | 2015-03-23 19:13:21 -0700 | [diff] [blame] | 2067 |                              ], extra_option_handler=option_handler) | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 2068 |  | 
 | 2069 |   if len(args) != 2: | 
 | 2070 |     common.Usage(__doc__) | 
 | 2071 |     sys.exit(1) | 
 | 2072 |  | 
| Tao Bao | 32fcdab | 2018-10-12 10:30:39 -0700 | [diff] [blame] | 2073 |   common.InitLogging() | 
 | 2074 |  | 
| Tao Bao | 5d18256 | 2016-02-23 11:38:39 -0800 | [diff] [blame] | 2075 |   if OPTIONS.downgrade: | 
| Tao Bao | 5d18256 | 2016-02-23 11:38:39 -0800 | [diff] [blame] | 2076 |     # We should only allow downgrading incrementals (as opposed to full). | 
 | 2077 |     # Otherwise the device may go back from arbitrary build with this full | 
 | 2078 |     # OTA package. | 
 | 2079 |     if OPTIONS.incremental_source is None: | 
| Elliott Hughes | d8a52f9 | 2016-06-20 14:35:47 -0700 | [diff] [blame] | 2080 |       raise ValueError("Cannot generate downgradable full OTAs") | 
| Tao Bao | 5d18256 | 2016-02-23 11:38:39 -0800 | [diff] [blame] | 2081 |  | 
| Tao Bao | 2db1385 | 2018-01-08 22:28:57 -0800 | [diff] [blame] | 2082 |   # Load the build info dicts from the zip directly or the extracted input | 
 | 2083 |   # directory. We don't need to unzip the entire target-files zips, because they | 
 | 2084 |   # won't be needed for A/B OTAs (brillo_update_payload does that on its own). | 
 | 2085 |   # When loading the info dicts, we don't need to provide the second parameter | 
 | 2086 |   # to common.LoadInfoDict(). Specifying the second parameter allows replacing | 
 | 2087 |   # some properties with their actual paths, such as 'selinux_fc', | 
 | 2088 |   # 'ramdisk_dir', which won't be used during OTA generation. | 
| Dan Willemsen | cea5cd2 | 2017-03-21 14:44:27 -0700 | [diff] [blame] | 2089 |   if OPTIONS.extracted_input is not None: | 
| Tao Bao | 2db1385 | 2018-01-08 22:28:57 -0800 | [diff] [blame] | 2090 |     OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input) | 
| Dan Willemsen | cea5cd2 | 2017-03-21 14:44:27 -0700 | [diff] [blame] | 2091 |   else: | 
| Tao Bao | 2db1385 | 2018-01-08 22:28:57 -0800 | [diff] [blame] | 2092 |     with zipfile.ZipFile(args[0], 'r') as input_zip: | 
 | 2093 |       OPTIONS.info_dict = common.LoadInfoDict(input_zip) | 
| Tao Bao | c098e9e | 2016-01-07 13:03:56 -0800 | [diff] [blame] | 2094 |  | 
| Tao Bao | 32fcdab | 2018-10-12 10:30:39 -0700 | [diff] [blame] | 2095 |   logger.info("--- target info ---") | 
 | 2096 |   common.DumpInfoDict(OPTIONS.info_dict) | 
| Tao Bao | 2db1385 | 2018-01-08 22:28:57 -0800 | [diff] [blame] | 2097 |  | 
 | 2098 |   # Load the source build dict if applicable. | 
 | 2099 |   if OPTIONS.incremental_source is not None: | 
 | 2100 |     OPTIONS.target_info_dict = OPTIONS.info_dict | 
 | 2101 |     with zipfile.ZipFile(OPTIONS.incremental_source, 'r') as source_zip: | 
 | 2102 |       OPTIONS.source_info_dict = common.LoadInfoDict(source_zip) | 
 | 2103 |  | 
| Tao Bao | 32fcdab | 2018-10-12 10:30:39 -0700 | [diff] [blame] | 2104 |     logger.info("--- source info ---") | 
 | 2105 |     common.DumpInfoDict(OPTIONS.source_info_dict) | 
| Tao Bao | 2db1385 | 2018-01-08 22:28:57 -0800 | [diff] [blame] | 2106 |  | 
 | 2107 |   # Load OEM dicts if provided. | 
| Tao Bao | 481bab8 | 2017-12-21 11:23:09 -0800 | [diff] [blame] | 2108 |   OPTIONS.oem_dicts = _LoadOemDicts(OPTIONS.oem_source) | 
 | 2109 |  | 
| Yifan Hong | 50e7954 | 2018-11-08 17:44:12 -0800 | [diff] [blame] | 2110 |   # Assume retrofitting dynamic partitions when base build does not set | 
| Yifan Hong | 5061103 | 2018-11-20 14:27:38 -0800 | [diff] [blame] | 2111 |   # use_dynamic_partitions but target build does. | 
| Yifan Hong | 50e7954 | 2018-11-08 17:44:12 -0800 | [diff] [blame] | 2112 |   if (OPTIONS.source_info_dict and | 
| Yifan Hong | 5061103 | 2018-11-20 14:27:38 -0800 | [diff] [blame] | 2113 |       OPTIONS.source_info_dict.get("use_dynamic_partitions") != "true" and | 
 | 2114 |       OPTIONS.target_info_dict.get("use_dynamic_partitions") == "true"): | 
| Yifan Hong | 50e7954 | 2018-11-08 17:44:12 -0800 | [diff] [blame] | 2115 |     if OPTIONS.target_info_dict.get("dynamic_partition_retrofit") != "true": | 
 | 2116 |       raise common.ExternalError( | 
 | 2117 |           "Expect to generate incremental OTA for retrofitting dynamic " | 
 | 2118 |           "partitions, but dynamic_partition_retrofit is not set in target " | 
 | 2119 |           "build.") | 
 | 2120 |     logger.info("Implicitly generating retrofit incremental OTA.") | 
 | 2121 |     OPTIONS.retrofit_dynamic_partitions = True | 
 | 2122 |  | 
 | 2123 |   # Skip postinstall for retrofitting dynamic partitions. | 
 | 2124 |   if OPTIONS.retrofit_dynamic_partitions: | 
 | 2125 |     OPTIONS.skip_postinstall = True | 
 | 2126 |  | 
| Tao Bao | c098e9e | 2016-01-07 13:03:56 -0800 | [diff] [blame] | 2127 |   ab_update = OPTIONS.info_dict.get("ab_update") == "true" | 
 | 2128 |  | 
| Christian Oder | f63e2cd | 2017-05-01 22:30:15 +0200 | [diff] [blame] | 2129 |   # Use the default key to sign the package if not specified with package_key. | 
 | 2130 |   # package_keys are needed on ab_updates, so always define them if an | 
 | 2131 |   # ab_update is getting created. | 
 | 2132 |   if not OPTIONS.no_signing or ab_update: | 
 | 2133 |     if OPTIONS.package_key is None: | 
 | 2134 |       OPTIONS.package_key = OPTIONS.info_dict.get( | 
 | 2135 |           "default_system_dev_certificate", | 
| Dan Willemsen | 0ab1be6 | 2019-04-09 21:35:37 -0700 | [diff] [blame] | 2136 |           "build/make/target/product/security/testkey") | 
| Christian Oder | f63e2cd | 2017-05-01 22:30:15 +0200 | [diff] [blame] | 2137 |     # Get signing keys | 
 | 2138 |     OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key]) | 
 | 2139 |  | 
| Tao Bao | c098e9e | 2016-01-07 13:03:56 -0800 | [diff] [blame] | 2140 |   if ab_update: | 
| Tao Bao | f0c4aa2 | 2018-04-30 20:29:30 -0700 | [diff] [blame] | 2141 |     GenerateAbOtaPackage( | 
| Tao Bao | c098e9e | 2016-01-07 13:03:56 -0800 | [diff] [blame] | 2142 |         target_file=args[0], | 
 | 2143 |         output_file=args[1], | 
 | 2144 |         source_file=OPTIONS.incremental_source) | 
 | 2145 |  | 
| Dan Willemsen | cea5cd2 | 2017-03-21 14:44:27 -0700 | [diff] [blame] | 2146 |   else: | 
| Tao Bao | f0c4aa2 | 2018-04-30 20:29:30 -0700 | [diff] [blame] | 2147 |     GenerateNonAbOtaPackage( | 
 | 2148 |         target_file=args[0], | 
 | 2149 |         output_file=args[1], | 
 | 2150 |         source_file=OPTIONS.incremental_source) | 
| Doug Zongker | fdd8e69 | 2009-08-03 17:27:48 -0700 | [diff] [blame] | 2151 |  | 
| Tao Bao | f0c4aa2 | 2018-04-30 20:29:30 -0700 | [diff] [blame] | 2152 |   # Post OTA generation works. | 
 | 2153 |   if OPTIONS.incremental_source is not None and OPTIONS.log_diff: | 
 | 2154 |     logger.info("Generating diff logs...") | 
 | 2155 |     logger.info("Unzipping target-files for diffing...") | 
 | 2156 |     target_dir = common.UnzipTemp(args[0], TARGET_DIFFING_UNZIP_PATTERN) | 
 | 2157 |     source_dir = common.UnzipTemp( | 
 | 2158 |         OPTIONS.incremental_source, TARGET_DIFFING_UNZIP_PATTERN) | 
| Doug Zongker | eb0a78a | 2014-01-27 10:01:06 -0800 | [diff] [blame] | 2159 |  | 
| Tao Bao | f0c4aa2 | 2018-04-30 20:29:30 -0700 | [diff] [blame] | 2160 |     with open(OPTIONS.log_diff, 'w') as out_file: | 
 | 2161 |       import target_files_diff | 
 | 2162 |       target_files_diff.recursiveDiff( | 
 | 2163 |           '', source_dir, target_dir, out_file) | 
| Doug Zongker | 62d4f18 | 2014-08-04 16:06:43 -0700 | [diff] [blame] | 2164 |  | 
| Tao Bao | 32fcdab | 2018-10-12 10:30:39 -0700 | [diff] [blame] | 2165 |   logger.info("done.") | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 2166 |  | 
 | 2167 |  | 
 | 2168 | if __name__ == '__main__': | 
 | 2169 |   try: | 
| Ying Wang | 7e6d4e4 | 2010-12-13 16:25:36 -0800 | [diff] [blame] | 2170 |     common.CloseInheritedPipes() | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 2171 |     main(sys.argv[1:]) | 
| Tao Bao | 32fcdab | 2018-10-12 10:30:39 -0700 | [diff] [blame] | 2172 |   except common.ExternalError: | 
 | 2173 |     logger.exception("\n   ERROR:\n") | 
| Doug Zongker | eef3944 | 2009-04-02 12:14:19 -0700 | [diff] [blame] | 2174 |     sys.exit(1) | 
| Doug Zongker | fc44a51 | 2014-08-26 13:10:25 -0700 | [diff] [blame] | 2175 |   finally: | 
 | 2176 |     common.Cleanup() |