blob: 4efa9f1c4872ea1aa02379e4803370274d34d9a6 [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
Zhomart Mukhamejanov96eb59e2018-05-04 12:17:01 -070020Example:
21 $ PYTHONPATH=$ANDROID_BUILD_TOP/build/make/tools/releasetools:$PYTHONPATH \\
22 bootable/recovery/updater_sample/tools/gen_update_config.py \\
23 --ab_install_type=STREAMING \\
24 ota-build-001.zip \\
25 my-config-001.json \\
26 http://foo.bar/ota-builds/ota-build-001.zip
Zhomart Mukhamejanovd5a41822018-04-24 18:17:57 -070027"""
28
29import argparse
30import json
31import os.path
32import sys
33import zipfile
34
Zhomart Mukhamejanov96eb59e2018-05-04 12:17:01 -070035import ota_from_target_files # pylint: disable=import-error
36
Zhomart Mukhamejanovd5a41822018-04-24 18:17:57 -070037
Zhomart Mukhamejanov72a4d462018-04-26 15:49:08 -070038class GenUpdateConfig(object):
Zhomart Mukhamejanovd5a41822018-04-24 18:17:57 -070039 """
40 A class that generates update configuration file from an OTA package.
41
42 Currently supports only A/B (seamless) OTA packages.
43 TODO: add non-A/B packages support.
44 """
45
46 AB_INSTALL_TYPE_STREAMING = 'STREAMING'
47 AB_INSTALL_TYPE_NON_STREAMING = 'NON_STREAMING'
Zhomart Mukhamejanovd5a41822018-04-24 18:17:57 -070048
Zhomart Mukhamejanov72a4d462018-04-26 15:49:08 -070049 def __init__(self, package, url, ab_install_type):
Zhomart Mukhamejanovd5a41822018-04-24 18:17:57 -070050 self.package = package
Zhomart Mukhamejanovd5a41822018-04-24 18:17:57 -070051 self.url = url
52 self.ab_install_type = ab_install_type
53 self.streaming_required = (
54 # payload.bin and payload_properties.txt must exist.
55 'payload.bin',
56 'payload_properties.txt',
57 )
58 self.streaming_optional = (
59 # care_map.txt is available only if dm-verity is enabled.
60 'care_map.txt',
61 # compatibility.zip is available only if target supports Treble.
62 'compatibility.zip',
63 )
Zhomart Mukhamejanov72a4d462018-04-26 15:49:08 -070064 self._config = None
65
66 @property
67 def config(self):
68 """Returns generated config object."""
69 return self._config
Zhomart Mukhamejanovd5a41822018-04-24 18:17:57 -070070
71 def run(self):
Zhomart Mukhamejanov72a4d462018-04-26 15:49:08 -070072 """Generates config."""
Zhomart Mukhamejanovd5a41822018-04-24 18:17:57 -070073 streaming_metadata = None
74 if self.ab_install_type == GenUpdateConfig.AB_INSTALL_TYPE_STREAMING:
75 streaming_metadata = self._gen_ab_streaming_metadata()
76
Zhomart Mukhamejanov72a4d462018-04-26 15:49:08 -070077 self._config = {
Zhomart Mukhamejanovd5a41822018-04-24 18:17:57 -070078 '__': '*** Generated using tools/gen_update_config.py ***',
79 'name': self.ab_install_type[0] + ' ' + os.path.basename(self.package)[:-4],
80 'url': self.url,
81 'ab_streaming_metadata': streaming_metadata,
82 'ab_install_type': self.ab_install_type,
83 }
84
Zhomart Mukhamejanovd5a41822018-04-24 18:17:57 -070085 def _gen_ab_streaming_metadata(self):
Zhomart Mukhamejanov72a4d462018-04-26 15:49:08 -070086 """Builds metadata for files required for streaming update."""
Zhomart Mukhamejanovd5a41822018-04-24 18:17:57 -070087 with zipfile.ZipFile(self.package, 'r') as package_zip:
Zhomart Mukhamejanovd5a41822018-04-24 18:17:57 -070088 metadata = {
Zhomart Mukhamejanov96eb59e2018-05-04 12:17:01 -070089 'property_files': self._get_property_files(package_zip)
Zhomart Mukhamejanovd5a41822018-04-24 18:17:57 -070090 }
91
92 return metadata
93
Zhomart Mukhamejanov96eb59e2018-05-04 12:17:01 -070094 @staticmethod
95 def _get_property_files(package_zip):
Zhomart Mukhamejanov72a4d462018-04-26 15:49:08 -070096 """Constructs the property-files list for A/B streaming metadata."""
Zhomart Mukhamejanovd5a41822018-04-24 18:17:57 -070097
Zhomart Mukhamejanov96eb59e2018-05-04 12:17:01 -070098 ab_ota = ota_from_target_files.AbOtaPropertyFiles()
99 property_str = ab_ota.GetPropertyFilesString(package_zip, False)
Zhomart Mukhamejanovd5a41822018-04-24 18:17:57 -0700100 property_files = []
Zhomart Mukhamejanov96eb59e2018-05-04 12:17:01 -0700101 for file in property_str.split(','):
102 filename, offset, size = file.split(':')
103 inner_file = {
104 'filename': filename,
105 'offset': int(offset),
106 'size': int(size)
107 }
108 property_files.append(inner_file)
Zhomart Mukhamejanovd5a41822018-04-24 18:17:57 -0700109
110 return property_files
111
Zhomart Mukhamejanov72a4d462018-04-26 15:49:08 -0700112 def write(self, out):
113 """Writes config to the output file."""
114 with open(out, 'w') as out_file:
115 json.dump(self.config, out_file, indent=4, separators=(',', ': '), sort_keys=True)
Zhomart Mukhamejanovd5a41822018-04-24 18:17:57 -0700116
Zhomart Mukhamejanov72a4d462018-04-26 15:49:08 -0700117
118def main(): # pylint: disable=missing-docstring
Zhomart Mukhamejanovd5a41822018-04-24 18:17:57 -0700119 ab_install_type_choices = [
120 GenUpdateConfig.AB_INSTALL_TYPE_STREAMING,
121 GenUpdateConfig.AB_INSTALL_TYPE_NON_STREAMING]
122 parser = argparse.ArgumentParser(description=__doc__,
123 formatter_class=argparse.RawDescriptionHelpFormatter)
124 parser.add_argument('--ab_install_type',
125 type=str,
126 default=GenUpdateConfig.AB_INSTALL_TYPE_NON_STREAMING,
127 choices=ab_install_type_choices,
128 help='A/B update installation type')
129 parser.add_argument('package',
130 type=str,
131 help='OTA package zip file')
132 parser.add_argument('out',
133 type=str,
134 help='Update configuration JSON file')
135 parser.add_argument('url',
136 type=str,
137 help='OTA package download url')
138 args = parser.parse_args()
139
140 if not args.out.endswith('.json'):
141 print('out must be a json file')
142 sys.exit(1)
143
144 gen = GenUpdateConfig(
145 package=args.package,
Zhomart Mukhamejanovd5a41822018-04-24 18:17:57 -0700146 url=args.url,
147 ab_install_type=args.ab_install_type)
148 gen.run()
Zhomart Mukhamejanov72a4d462018-04-26 15:49:08 -0700149 gen.write(args.out)
150 print('Config is written to ' + args.out)
Zhomart Mukhamejanovd5a41822018-04-24 18:17:57 -0700151
152
153if __name__ == '__main__':
154 main()