1#!/usr/bin/env python 2# 3# Copyright (C) 2008 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17""" 18Given a series of .img files, produces an OTA package that installs thoese images 19""" 20 21import sys 22import os 23import argparse 24import subprocess 25import tempfile 26import logging 27import zipfile 28 29import common 30from payload_signer import PayloadSigner 31from ota_utils import PayloadGenerator 32from ota_signing_utils import AddSigningArgumentParse 33 34 35logger = logging.getLogger(__name__) 36 37 38def ResolveBinaryPath(filename, search_path): 39 if not search_path: 40 return filename 41 if not os.path.exists(search_path): 42 return filename 43 path = os.path.join(search_path, "bin", filename) 44 if os.path.exists(path): 45 return path 46 path = os.path.join(search_path, filename) 47 if os.path.exists(path): 48 return path 49 return path 50 51 52def main(argv): 53 parser = argparse.ArgumentParser( 54 prog=argv[0], description="Given a series of .img files, produces a full OTA package that installs thoese images") 55 parser.add_argument("images", nargs="+", type=str, 56 help="List of images to generate OTA") 57 parser.add_argument("--partition_names", nargs='+', type=str, 58 help="Partition names to install the images, default to basename of the image(no file name extension)") 59 parser.add_argument('--output', type=str, 60 help='Paths to output merged ota', required=True) 61 parser.add_argument('--max_timestamp', type=int, 62 help='Maximum build timestamp allowed to install this OTA') 63 parser.add_argument("-v", action="store_true", 64 help="Enable verbose logging", dest="verbose") 65 AddSigningArgumentParse(parser) 66 67 args = parser.parse_args(argv[1:]) 68 if args.verbose: 69 logger.setLevel(logging.INFO) 70 logger.info(args) 71 old_imgs = [""] * len(args.images) 72 for (i, img) in enumerate(args.images): 73 if ":" in img: 74 old_imgs[i], args.images[i] = img.split(":", maxsplit=1) 75 76 if not args.partition_names: 77 args.partition_names = [os.path.os.path.splitext(os.path.basename(path))[ 78 0] for path in args.images] 79 with tempfile.NamedTemporaryFile() as unsigned_payload, tempfile.NamedTemporaryFile() as dynamic_partition_info_file: 80 dynamic_partition_info_file.writelines( 81 [b"virtual_ab=true\n", b"super_partition_groups=\n"]) 82 dynamic_partition_info_file.flush() 83 cmd = [ResolveBinaryPath("delta_generator", args.search_path)] 84 cmd.append("--partition_names=" + ",".join(args.partition_names)) 85 cmd.append("--dynamic_partition_info_file=" + 86 dynamic_partition_info_file.name) 87 cmd.append("--old_partitions=" + ",".join(old_imgs)) 88 cmd.append("--new_partitions=" + ",".join(args.images)) 89 cmd.append("--out_file=" + unsigned_payload.name) 90 cmd.append("--is_partial_update") 91 if args.max_timestamp: 92 cmd.append("--max_timestamp=" + str(args.max_timestamp)) 93 cmd.append("--partition_timestamps=boot:" + str(args.max_timestamp)) 94 logger.info("Running %s", cmd) 95 96 subprocess.check_call(cmd) 97 generator = PayloadGenerator() 98 generator.payload_file = unsigned_payload.name 99 logger.info("Payload size: %d", os.path.getsize(generator.payload_file)) 100 101 # Get signing keys 102 key_passwords = common.GetKeyPasswords([args.package_key]) 103 104 if args.package_key: 105 logger.info("Signing payload...") 106 # TODO: remove OPTIONS when no longer used as fallback in payload_signer 107 common.OPTIONS.payload_signer_args = None 108 common.OPTIONS.payload_signer_maximum_signature_size = None 109 signer = PayloadSigner(args.package_key, args.private_key_suffix, 110 key_passwords[args.package_key], 111 payload_signer=args.payload_signer, 112 payload_signer_args=args.payload_signer_args, 113 payload_signer_maximum_signature_size=args.payload_signer_maximum_signature_size) 114 generator.payload_file = unsigned_payload.name 115 generator.Sign(signer) 116 117 logger.info("Payload size: %d", os.path.getsize(generator.payload_file)) 118 119 logger.info("Writing to %s", args.output) 120 with zipfile.ZipFile(args.output, "w") as zfp: 121 generator.WriteToZip(zfp) 122 123 124if __name__ == "__main__": 125 logging.basicConfig() 126 main(sys.argv) 127