blob: 44e9ac3cbf0c2a721d8c60925e9f71b5e8b71357 [file] [log] [blame]
Zhomart Mukhamejanovd5a41822018-04-24 18:17:57 -07001#!/usr/bin/env python3
2#
3# Copyright (C) 2018 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 OTA package file, produces update config JSON file.
19
20Example: tools/gen_update.config.py \\
21 --ab_install_type=STREAMING \\
22 ota-build-001.zip \\
23 my-config-001.json \\
24 http://foo.bar/ota-builds/ota-build-001.zip
25"""
26
27import argparse
28import json
29import os.path
30import sys
31import zipfile
32
33
34class GenUpdateConfig(object): # pylint: disable=too-few-public-methods
35 """
36 A class that generates update configuration file from an OTA package.
37
38 Currently supports only A/B (seamless) OTA packages.
39 TODO: add non-A/B packages support.
40 """
41
42 AB_INSTALL_TYPE_STREAMING = 'STREAMING'
43 AB_INSTALL_TYPE_NON_STREAMING = 'NON_STREAMING'
44 METADATA_NAME = 'META-INF/com/android/metadata'
45
46 def __init__(self, package, out, url, ab_install_type):
47 self.package = package
48 self.out = out
49 self.url = url
50 self.ab_install_type = ab_install_type
51 self.streaming_required = (
52 # payload.bin and payload_properties.txt must exist.
53 'payload.bin',
54 'payload_properties.txt',
55 )
56 self.streaming_optional = (
57 # care_map.txt is available only if dm-verity is enabled.
58 'care_map.txt',
59 # compatibility.zip is available only if target supports Treble.
60 'compatibility.zip',
61 )
62
63 def run(self):
64 """generate config"""
65 streaming_metadata = None
66 if self.ab_install_type == GenUpdateConfig.AB_INSTALL_TYPE_STREAMING:
67 streaming_metadata = self._gen_ab_streaming_metadata()
68
69 config = {
70 '__': '*** Generated using tools/gen_update_config.py ***',
71 'name': self.ab_install_type[0] + ' ' + os.path.basename(self.package)[:-4],
72 'url': self.url,
73 'ab_streaming_metadata': streaming_metadata,
74 'ab_install_type': self.ab_install_type,
75 }
76
77 with open(self.out, 'w') as out:
78 json.dump(config, out, indent=4, separators=(',', ': '), sort_keys=True)
79 print('Config is written to ' + out.name)
80
81 def _gen_ab_streaming_metadata(self):
82 """Open zip file and get metadata for files required for streaming update."""
83 with zipfile.ZipFile(self.package, 'r') as package_zip:
84 property_files = self._get_property_files(package_zip)
85
86 metadata = {
87 'property_files': property_files
88 }
89
90 return metadata
91
92 def _get_property_files(self, zip_file):
93 """Constructs the property-files list for A/B streaming metadata"""
94
95 def compute_entry_offset_size(name):
96 """Computes the zip entry offset and size."""
97 info = zip_file.getinfo(name)
98 offset = info.header_offset + len(info.FileHeader())
99 size = info.file_size
100 return {
101 'filename': os.path.basename(name),
102 'offset': offset,
103 'size': size,
104 }
105
106 property_files = []
107 for entry in self.streaming_required:
108 property_files.append(compute_entry_offset_size(entry))
109 for entry in self.streaming_optional:
110 if entry in zip_file.namelist():
111 property_files.append(compute_entry_offset_size(entry))
112
113 # 'META-INF/com/android/metadata' is required
114 property_files.append(compute_entry_offset_size(GenUpdateConfig.METADATA_NAME))
115
116 return property_files
117
118
119def main(): # pylint: disable=missing-docstring
120 ab_install_type_choices = [
121 GenUpdateConfig.AB_INSTALL_TYPE_STREAMING,
122 GenUpdateConfig.AB_INSTALL_TYPE_NON_STREAMING]
123 parser = argparse.ArgumentParser(description=__doc__,
124 formatter_class=argparse.RawDescriptionHelpFormatter)
125 parser.add_argument('--ab_install_type',
126 type=str,
127 default=GenUpdateConfig.AB_INSTALL_TYPE_NON_STREAMING,
128 choices=ab_install_type_choices,
129 help='A/B update installation type')
130 parser.add_argument('package',
131 type=str,
132 help='OTA package zip file')
133 parser.add_argument('out',
134 type=str,
135 help='Update configuration JSON file')
136 parser.add_argument('url',
137 type=str,
138 help='OTA package download url')
139 args = parser.parse_args()
140
141 if not args.out.endswith('.json'):
142 print('out must be a json file')
143 sys.exit(1)
144
145 gen = GenUpdateConfig(
146 package=args.package,
147 out=args.out,
148 url=args.url,
149 ab_install_type=args.ab_install_type)
150 gen.run()
151
152
153if __name__ == '__main__':
154 main()