blob: 03b44f15d6e6b91ed951d76a9251b115be0952a6 [file] [log] [blame]
Kelvin Zhangc7441e52023-08-22 08:56:30 -07001#!/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")
Kelvin Zhang9ef8a022024-06-26 10:10:18 -070057 parser.add_argument("--partition_names", nargs='?', type=str,
Kelvin Zhangc7441e52023-08-22 08:56:30 -070058 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)
Kelvin Zhang506a3082023-08-25 19:49:30 -070061 parser.add_argument('--max_timestamp', type=int,
62 help='Maximum build timestamp allowed to install this OTA')
Kelvin Zhangc7441e52023-08-22 08:56:30 -070063 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)
Kelvin Zhang65c11622023-10-02 12:45:30 -070071 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
Kelvin Zhangc7441e52023-08-22 08:56:30 -070076 if not args.partition_names:
Kelvin Zhang9ef8a022024-06-26 10:10:18 -070077 args.partition_names = [os.path.splitext(os.path.basename(path))[
Kelvin Zhangc7441e52023-08-22 08:56:30 -070078 0] for path in args.images]
Kelvin Zhang9ef8a022024-06-26 10:10:18 -070079 else:
80 args.partition_names = args.partition_names.split(",")
Kelvin Zhangc4f73742023-08-30 14:41:40 -070081 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()
Kelvin Zhangc7441e52023-08-22 08:56:30 -070085 cmd = [ResolveBinaryPath("delta_generator", args.search_path)]
Kelvin Zhang9ef8a022024-06-26 10:10:18 -070086 cmd.append("--partition_names=" + ":".join(args.partition_names))
Kelvin Zhangc4f73742023-08-30 14:41:40 -070087 cmd.append("--dynamic_partition_info_file=" +
88 dynamic_partition_info_file.name)
Kelvin Zhang9ef8a022024-06-26 10:10:18 -070089 cmd.append("--old_partitions=" + ":".join(old_imgs))
90 cmd.append("--new_partitions=" + ":".join(args.images))
Kelvin Zhangc7441e52023-08-22 08:56:30 -070091 cmd.append("--out_file=" + unsigned_payload.name)
Kelvin Zhangc4f73742023-08-30 14:41:40 -070092 cmd.append("--is_partial_update")
Kelvin Zhang506a3082023-08-25 19:49:30 -070093 if args.max_timestamp:
94 cmd.append("--max_timestamp=" + str(args.max_timestamp))
Kelvin Zhangb5661d62023-09-06 12:55:08 -070095 cmd.append("--partition_timestamps=boot:" + str(args.max_timestamp))
Kelvin Zhangc7441e52023-08-22 08:56:30 -070096 logger.info("Running %s", cmd)
97
Kelvin Zhangc4f73742023-08-30 14:41:40 -070098 subprocess.check_call(cmd)
Kelvin Zhangc7441e52023-08-22 08:56:30 -070099 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 # TODO: remove OPTIONS when no longer used as fallback in payload_signer
109 common.OPTIONS.payload_signer_args = None
110 common.OPTIONS.payload_signer_maximum_signature_size = None
111 signer = PayloadSigner(args.package_key, args.private_key_suffix,
112 key_passwords[args.package_key],
113 payload_signer=args.payload_signer,
114 payload_signer_args=args.payload_signer_args,
115 payload_signer_maximum_signature_size=args.payload_signer_maximum_signature_size)
116 generator.payload_file = unsigned_payload.name
117 generator.Sign(signer)
118
119 logger.info("Payload size: %d", os.path.getsize(generator.payload_file))
120
121 logger.info("Writing to %s", args.output)
122 with zipfile.ZipFile(args.output, "w") as zfp:
123 generator.WriteToZip(zfp)
124
125
126if __name__ == "__main__":
127 logging.basicConfig()
128 main(sys.argv)