1#!/usr/bin/env python3 2 3# Copyright (C) 2020 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""" 18Generate payload.bin from a single image. 19""" 20 21import argparse 22import logging 23import os 24import re 25import shutil 26import sys 27from zipfile import ZipFile 28 29import common 30from ota_from_target_files import (Payload, PayloadSigner) 31 32logger = logging.getLogger(__name__) 33OPTIONS = common.OPTIONS 34 35 36def _ParseArgs(): 37 parser = argparse.ArgumentParser(description=__doc__) 38 parser.add_argument("--tools", metavar="PATH", type=str, nargs="*", 39 help="A list of real paths of tools that this script depends on.") 40 parser.add_argument("--key", type=str, 41 help="Key to use to sign the package. If unspecified, script does not sign " 42 "the package and payload_properties.txt is not generated.") 43 parser.add_argument("--out", type=str, required=True, 44 help="Required output directory to payload.bin and payload_properties.txt") 45 parser.add_argument("input", metavar="NAME:IMAGE", nargs="+", 46 help="Name of the image and path to the image, e.g. boot:path/to/boot.img") 47 args = parser.parse_args() 48 49 return args 50 51 52def _PrepareEnvironment(args): 53 common.InitLogging() 54 if not args.tools: 55 return 56 for path in args.tools: 57 name = os.path.basename(path) 58 # Use absolute path because GetBootImageTimestamp changes cwd when running some tools. 59 common.SetHostToolLocation(name, os.path.abspath(path)) 60 # brillo_update_payload is a shell script that depends on this environment variable. 61 if name == "delta_generator": 62 os.environ["GENERATOR"] = path 63 64 65def CreateOtaFromRawImages(args): 66 _PrepareEnvironment(args) 67 68 tf = common.MakeTempFile("target_files", ".zip") 69 payload_additional_args = ["--is_partial_update", "true"] 70 with ZipFile(tf, "w") as zip: 71 names = [] 72 for pair_str in args.input: 73 pair = pair_str.split(":") 74 assert len(pair) == 2, "Incorrect format: " + pair_str 75 name, img_path = tuple(pair) 76 zip.write(img_path, arcname=os.path.join("IMAGES", name + ".img")) 77 names.append(name) 78 if name == "boot": 79 timestamp = common.GetBootImageTimestamp(img_path) 80 assert timestamp is not None, "Cannot extract timestamp from boot image" 81 payload_additional_args += ["--partition_timestamps", 82 "boot:" + str(timestamp)] 83 84 zip.writestr("META/ab_partitions.txt", "\n".join(names) + "\n") 85 zip.writestr("META/dynamic_partitions_info.txt", """ 86virtual_ab=true 87super_partition_groups= 88 """) 89 90 payload = Payload() 91 payload.Generate(tf, None, payload_additional_args) 92 93 if args.key: 94 OPTIONS.package_key = args.key 95 OPTIONS.key_passwords = common.GetKeyPasswords([OPTIONS.package_key]) 96 payload_signer = PayloadSigner() 97 payload.Sign(payload_signer) 98 99 shutil.copy(payload.payload_file, os.path.join(args.out, "payload.bin")) 100 if payload.payload_properties: 101 shutil.copy(payload.payload_properties, os.path.join(args.out, "payload_properties.txt")) 102 103 104if __name__ == "__main__": 105 try: 106 common.CloseInheritedPipes() 107 args = _ParseArgs() 108 CreateOtaFromRawImages(args) 109 except common.ExternalError: 110 logger.exception("\n ERROR:\n") 111 sys.exit(1) 112 finally: 113 common.Cleanup() 114