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.splitext(os.path.basename(path))[ 78 0] for path in args.images] 79 else: 80 args.partition_names = args.partition_names.split(",") 81 with tempfile.NamedTemporaryFile() as unsigned_payload, tempfile.NamedTemporaryFile() as dynamic_partition_info_file: 82 dynamic_partition_info_file.writelines( 83 [b"virtual_ab=true\n", b"super_partition_groups=\n"]) 84 dynamic_partition_info_file.flush() 85 cmd = [ResolveBinaryPath("delta_generator", args.search_path)] 86 cmd.append("--partition_names=" + ":".join(args.partition_names)) 87 cmd.append("--dynamic_partition_info_file=" + 88 dynamic_partition_info_file.name) 89 cmd.append("--old_partitions=" + ":".join(old_imgs)) 90 cmd.append("--new_partitions=" + ":".join(args.images)) 91 cmd.append("--out_file=" + unsigned_payload.name) 92 cmd.append("--is_partial_update") 93 if args.max_timestamp: 94 cmd.append("--max_timestamp=" + str(args.max_timestamp)) 95 cmd.append("--partition_timestamps=boot:" + str(args.max_timestamp)) 96 logger.info("Running %s", cmd) 97 98 subprocess.check_call(cmd) 99 generator = PayloadGenerator() 100 generator.payload_file = unsigned_payload.name 101 logger.info("Payload size: %d", os.path.getsize(generator.payload_file)) 102 103 # Get signing keys 104 key_passwords = common.GetKeyPasswords([args.package_key]) 105 106 if args.package_key: 107 logger.info("Signing payload...") 108 signer = PayloadSigner(args.package_key, args.private_key_suffix, 109 key_passwords[args.package_key], 110 payload_signer=args.payload_signer, 111 payload_signer_args=args.payload_signer_args, 112 payload_signer_maximum_signature_size=args.payload_signer_maximum_signature_size) 113 generator.payload_file = unsigned_payload.name 114 generator.Sign(signer) 115 116 logger.info("Payload size: %d", os.path.getsize(generator.payload_file)) 117 118 logger.info("Writing to %s", args.output) 119 with zipfile.ZipFile(args.output, "w") as zfp: 120 generator.WriteToZip(zfp) 121 122 123if __name__ == "__main__": 124 logging.basicConfig() 125 main(sys.argv) 126