#!/usr/bin/env python # # Copyright (C) 2008 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ Given a series of .img files, produces an OTA package that installs thoese images """ import sys import os import argparse import subprocess import tempfile import logging import zipfile import common from payload_signer import PayloadSigner from ota_utils import PayloadGenerator from ota_signing_utils import AddSigningArgumentParse logger = logging.getLogger(__name__) def ResolveBinaryPath(filename, search_path): if not search_path: return filename if not os.path.exists(search_path): return filename path = os.path.join(search_path, "bin", filename) if os.path.exists(path): return path path = os.path.join(search_path, filename) if os.path.exists(path): return path return path def main(argv): parser = argparse.ArgumentParser( prog=argv[0], description="Given a series of .img files, produces a full OTA package that installs thoese images") parser.add_argument("images", nargs="+", type=str, help="List of images to generate OTA") parser.add_argument("--partition_names", nargs='?', type=str, help="Partition names to install the images, default to basename of the image(no file name extension)") parser.add_argument('--output', type=str, help='Paths to output merged ota', required=True) parser.add_argument('--max_timestamp', type=int, help='Maximum build timestamp allowed to install this OTA') parser.add_argument("-v", action="store_true", help="Enable verbose logging", dest="verbose") AddSigningArgumentParse(parser) args = parser.parse_args(argv[1:]) if args.verbose: logger.setLevel(logging.INFO) logger.info(args) old_imgs = [""] * len(args.images) for (i, img) in enumerate(args.images): if ":" in img: old_imgs[i], args.images[i] = img.split(":", maxsplit=1) if not args.partition_names: args.partition_names = [os.path.splitext(os.path.basename(path))[ 0] for path in args.images] else: args.partition_names = args.partition_names.split(",") with tempfile.NamedTemporaryFile() as unsigned_payload, tempfile.NamedTemporaryFile() as dynamic_partition_info_file: dynamic_partition_info_file.writelines( [b"virtual_ab=true\n", b"super_partition_groups=\n"]) dynamic_partition_info_file.flush() cmd = [ResolveBinaryPath("delta_generator", args.search_path)] cmd.append("--partition_names=" + ":".join(args.partition_names)) cmd.append("--dynamic_partition_info_file=" + dynamic_partition_info_file.name) cmd.append("--old_partitions=" + ":".join(old_imgs)) cmd.append("--new_partitions=" + ":".join(args.images)) cmd.append("--out_file=" + unsigned_payload.name) cmd.append("--is_partial_update") if args.max_timestamp: cmd.append("--max_timestamp=" + str(args.max_timestamp)) cmd.append("--partition_timestamps=boot:" + str(args.max_timestamp)) logger.info("Running %s", cmd) subprocess.check_call(cmd) generator = PayloadGenerator() generator.payload_file = unsigned_payload.name logger.info("Payload size: %d", os.path.getsize(generator.payload_file)) # Get signing keys key_passwords = common.GetKeyPasswords([args.package_key]) if args.package_key: logger.info("Signing payload...") signer = PayloadSigner(args.package_key, args.private_key_suffix, key_passwords[args.package_key], payload_signer=args.payload_signer, payload_signer_args=args.payload_signer_args, payload_signer_maximum_signature_size=args.payload_signer_maximum_signature_size) generator.payload_file = unsigned_payload.name generator.Sign(signer) logger.info("Payload size: %d", os.path.getsize(generator.payload_file)) logger.info("Writing to %s", args.output) with zipfile.ZipFile(args.output, "w") as zfp: generator.WriteToZip(zfp) if __name__ == "__main__": logging.basicConfig() main(sys.argv)