blob: 6f5e78fbfdcf799808057a46e6844d91ebe4e995 [file] [log] [blame]
Tao Bao481bab82017-12-21 11:23:09 -08001#
2# Copyright (C) 2018 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15#
16
17import copy
Tao Baoc7b403a2018-01-30 18:19:04 -080018import os
Tao Baofabe0832018-01-17 15:52:28 -080019import os.path
Tao Baoc7b403a2018-01-30 18:19:04 -080020import zipfile
Tao Bao481bab82017-12-21 11:23:09 -080021
22import common
Tianjiea2076132020-08-19 17:25:32 -070023import ota_metadata_pb2
Tao Bao04e1f012018-02-04 12:13:35 -080024import test_utils
Tianjiea2076132020-08-19 17:25:32 -070025from ota_utils import (
26 BuildLegacyOtaMetadata, CalculateRuntimeDevicesAndFingerprints,
27 FinalizeMetadata, GetPackageMetadata, PropertyFiles)
Tao Bao481bab82017-12-21 11:23:09 -080028from ota_from_target_files import (
Tianjiea2076132020-08-19 17:25:32 -070029 _LoadOemDicts, AbOtaPropertyFiles,
30 GetTargetFilesZipForSecondaryImages,
Kelvin Zhangcff4d762020-07-29 16:37:51 -040031 GetTargetFilesZipWithoutPostinstallConfig,
Tianjiea2076132020-08-19 17:25:32 -070032 Payload, PayloadSigner, POSTINSTALL_CONFIG,
Kelvin Zhangcff4d762020-07-29 16:37:51 -040033 StreamingPropertyFiles)
Kelvin Zhangcff4d762020-07-29 16:37:51 -040034from test_utils import PropertyFilesTestCase
Tao Baofabe0832018-01-17 15:52:28 -080035
Tianjiea2076132020-08-19 17:25:32 -070036
Tao Baof7140c02018-01-30 17:09:24 -080037def construct_target_files(secondary=False):
38 """Returns a target-files.zip file for generating OTA packages."""
39 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
40 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
41 # META/update_engine_config.txt
42 target_files_zip.writestr(
43 'META/update_engine_config.txt',
44 "PAYLOAD_MAJOR_VERSION=2\nPAYLOAD_MINOR_VERSION=4\n")
45
Tao Bao15a146a2018-02-21 16:06:59 -080046 # META/postinstall_config.txt
47 target_files_zip.writestr(
48 POSTINSTALL_CONFIG,
49 '\n'.join([
50 "RUN_POSTINSTALL_system=true",
51 "POSTINSTALL_PATH_system=system/bin/otapreopt_script",
52 "FILESYSTEM_TYPE_system=ext4",
53 "POSTINSTALL_OPTIONAL_system=true",
54 ]))
55
Tao Bao5277d102018-04-17 23:47:21 -070056 ab_partitions = [
57 ('IMAGES', 'boot'),
58 ('IMAGES', 'system'),
59 ('IMAGES', 'vendor'),
60 ('RADIO', 'bootloader'),
61 ('RADIO', 'modem'),
62 ]
Tao Baof7140c02018-01-30 17:09:24 -080063 # META/ab_partitions.txt
Tao Baof7140c02018-01-30 17:09:24 -080064 target_files_zip.writestr(
65 'META/ab_partitions.txt',
Tao Bao5277d102018-04-17 23:47:21 -070066 '\n'.join([partition[1] for partition in ab_partitions]))
Tao Baof7140c02018-01-30 17:09:24 -080067
Kelvin Zhangc693d952020-07-22 19:21:22 -040068 # Create fake images for each of them.
Tao Bao5277d102018-04-17 23:47:21 -070069 for path, partition in ab_partitions:
70 target_files_zip.writestr(
71 '{}/{}.img'.format(path, partition),
72 os.urandom(len(partition)))
Tao Baof7140c02018-01-30 17:09:24 -080073
Tao Bao5277d102018-04-17 23:47:21 -070074 # system_other shouldn't appear in META/ab_partitions.txt.
Tao Baof7140c02018-01-30 17:09:24 -080075 if secondary:
76 target_files_zip.writestr('IMAGES/system_other.img',
77 os.urandom(len("system_other")))
78
79 return target_files
80
81
Tao Bao65b94e92018-10-11 21:57:26 -070082class LoadOemDictsTest(test_utils.ReleaseToolsTestCase):
Tao Bao481bab82017-12-21 11:23:09 -080083
84 def test_NoneDict(self):
85 self.assertIsNone(_LoadOemDicts(None))
86
87 def test_SingleDict(self):
88 dict_file = common.MakeTempFile()
89 with open(dict_file, 'w') as dict_fp:
90 dict_fp.write('abc=1\ndef=2\nxyz=foo\na.b.c=bar\n')
91
92 oem_dicts = _LoadOemDicts([dict_file])
93 self.assertEqual(1, len(oem_dicts))
94 self.assertEqual('foo', oem_dicts[0]['xyz'])
95 self.assertEqual('bar', oem_dicts[0]['a.b.c'])
96
97 def test_MultipleDicts(self):
98 oem_source = []
99 for i in range(3):
100 dict_file = common.MakeTempFile()
101 with open(dict_file, 'w') as dict_fp:
102 dict_fp.write(
103 'ro.build.index={}\ndef=2\nxyz=foo\na.b.c=bar\n'.format(i))
104 oem_source.append(dict_file)
105
106 oem_dicts = _LoadOemDicts(oem_source)
107 self.assertEqual(3, len(oem_dicts))
108 for i, oem_dict in enumerate(oem_dicts):
109 self.assertEqual('2', oem_dict['def'])
110 self.assertEqual('foo', oem_dict['xyz'])
111 self.assertEqual('bar', oem_dict['a.b.c'])
112 self.assertEqual('{}'.format(i), oem_dict['ro.build.index'])
Tao Baodf3a48b2018-01-10 16:30:43 -0800113
114
Tao Bao65b94e92018-10-11 21:57:26 -0700115class OtaFromTargetFilesTest(test_utils.ReleaseToolsTestCase):
Tao Baodf3a48b2018-01-10 16:30:43 -0800116 TEST_TARGET_INFO_DICT = {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000117 'build.prop': common.PartitionBuildProps.FromDictionary(
118 'system', {
119 'ro.product.device': 'product-device',
120 'ro.build.fingerprint': 'build-fingerprint-target',
121 'ro.build.version.incremental': 'build-version-incremental-target',
122 'ro.build.version.sdk': '27',
123 'ro.build.version.security_patch': '2017-12-01',
124 'ro.build.date.utc': '1500000000'}
125 )
Tao Baodf3a48b2018-01-10 16:30:43 -0800126 }
127
128 TEST_SOURCE_INFO_DICT = {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000129 'build.prop': common.PartitionBuildProps.FromDictionary(
130 'system', {
131 'ro.product.device': 'product-device',
132 'ro.build.fingerprint': 'build-fingerprint-source',
133 'ro.build.version.incremental': 'build-version-incremental-source',
134 'ro.build.version.sdk': '25',
135 'ro.build.version.security_patch': '2016-12-01',
136 'ro.build.date.utc': '1400000000'}
137 )
Tao Baodf3a48b2018-01-10 16:30:43 -0800138 }
139
Tao Bao1c320f82019-10-04 23:25:12 -0700140 TEST_INFO_DICT_USES_OEM_PROPS = {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000141 'build.prop': common.PartitionBuildProps.FromDictionary(
142 'system', {
143 'ro.product.name': 'product-name',
144 'ro.build.thumbprint': 'build-thumbprint',
145 'ro.build.bar': 'build-bar'}
146 ),
147 'vendor.build.prop': common.PartitionBuildProps.FromDictionary(
148 'vendor', {
Tianjie2bb14862020-08-28 16:24:34 -0700149 'ro.vendor.build.fingerprint': 'vendor-build-fingerprint'}
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000150 ),
151 'property1': 'value1',
152 'property2': 4096,
153 'oem_fingerprint_properties': 'ro.product.device ro.product.brand',
Tao Bao1c320f82019-10-04 23:25:12 -0700154 }
155
Tao Baodf3a48b2018-01-10 16:30:43 -0800156 def setUp(self):
Tao Bao3bf8c652018-03-16 12:59:42 -0700157 self.testdata_dir = test_utils.get_testdata_dir()
158 self.assertTrue(os.path.exists(self.testdata_dir))
159
Tao Baodf3a48b2018-01-10 16:30:43 -0800160 # Reset the global options as in ota_from_target_files.py.
161 common.OPTIONS.incremental_source = None
162 common.OPTIONS.downgrade = False
Tao Bao393eeb42019-03-06 16:00:38 -0800163 common.OPTIONS.retrofit_dynamic_partitions = False
Tao Baodf3a48b2018-01-10 16:30:43 -0800164 common.OPTIONS.timestamp = False
165 common.OPTIONS.wipe_user_data = False
Tao Bao3bf8c652018-03-16 12:59:42 -0700166 common.OPTIONS.no_signing = False
167 common.OPTIONS.package_key = os.path.join(self.testdata_dir, 'testkey')
168 common.OPTIONS.key_passwords = {
169 common.OPTIONS.package_key : None,
170 }
171
172 common.OPTIONS.search_path = test_utils.get_search_path()
Tao Baodf3a48b2018-01-10 16:30:43 -0800173
Tianjiea2076132020-08-19 17:25:32 -0700174 @staticmethod
175 def GetLegacyOtaMetadata(target_info, source_info=None):
176 metadata_proto = GetPackageMetadata(target_info, source_info)
177 return BuildLegacyOtaMetadata(metadata_proto)
178
Tao Baodf3a48b2018-01-10 16:30:43 -0800179 def test_GetPackageMetadata_abOta_full(self):
180 target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)
181 target_info_dict['ab_update'] = 'true'
Tao Bao1c320f82019-10-04 23:25:12 -0700182 target_info = common.BuildInfo(target_info_dict, None)
Tianjiea2076132020-08-19 17:25:32 -0700183 metadata = self.GetLegacyOtaMetadata(target_info)
Tao Baodf3a48b2018-01-10 16:30:43 -0800184 self.assertDictEqual(
185 {
186 'ota-type' : 'AB',
187 'ota-required-cache' : '0',
188 'post-build' : 'build-fingerprint-target',
189 'post-build-incremental' : 'build-version-incremental-target',
Tao Bao35dc2552018-02-01 13:18:00 -0800190 'post-sdk-level' : '27',
191 'post-security-patch-level' : '2017-12-01',
Tao Baodf3a48b2018-01-10 16:30:43 -0800192 'post-timestamp' : '1500000000',
193 'pre-device' : 'product-device',
194 },
195 metadata)
196
197 def test_GetPackageMetadata_abOta_incremental(self):
198 target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)
199 target_info_dict['ab_update'] = 'true'
Tao Bao1c320f82019-10-04 23:25:12 -0700200 target_info = common.BuildInfo(target_info_dict, None)
201 source_info = common.BuildInfo(self.TEST_SOURCE_INFO_DICT, None)
Tao Baodf3a48b2018-01-10 16:30:43 -0800202 common.OPTIONS.incremental_source = ''
Tianjiea2076132020-08-19 17:25:32 -0700203 metadata = self.GetLegacyOtaMetadata(target_info, source_info)
Tao Baodf3a48b2018-01-10 16:30:43 -0800204 self.assertDictEqual(
205 {
206 'ota-type' : 'AB',
207 'ota-required-cache' : '0',
208 'post-build' : 'build-fingerprint-target',
209 'post-build-incremental' : 'build-version-incremental-target',
Tao Bao35dc2552018-02-01 13:18:00 -0800210 'post-sdk-level' : '27',
211 'post-security-patch-level' : '2017-12-01',
Tao Baodf3a48b2018-01-10 16:30:43 -0800212 'post-timestamp' : '1500000000',
213 'pre-device' : 'product-device',
214 'pre-build' : 'build-fingerprint-source',
215 'pre-build-incremental' : 'build-version-incremental-source',
216 },
217 metadata)
218
219 def test_GetPackageMetadata_nonAbOta_full(self):
Tao Bao1c320f82019-10-04 23:25:12 -0700220 target_info = common.BuildInfo(self.TEST_TARGET_INFO_DICT, None)
Tianjiea2076132020-08-19 17:25:32 -0700221 metadata = self.GetLegacyOtaMetadata(target_info)
Tao Baodf3a48b2018-01-10 16:30:43 -0800222 self.assertDictEqual(
223 {
224 'ota-type' : 'BLOCK',
Tianjiea2076132020-08-19 17:25:32 -0700225 'ota-required-cache' : '0',
Tao Baodf3a48b2018-01-10 16:30:43 -0800226 'post-build' : 'build-fingerprint-target',
227 'post-build-incremental' : 'build-version-incremental-target',
Tao Bao35dc2552018-02-01 13:18:00 -0800228 'post-sdk-level' : '27',
229 'post-security-patch-level' : '2017-12-01',
Tao Baodf3a48b2018-01-10 16:30:43 -0800230 'post-timestamp' : '1500000000',
231 'pre-device' : 'product-device',
232 },
233 metadata)
234
235 def test_GetPackageMetadata_nonAbOta_incremental(self):
Tao Bao1c320f82019-10-04 23:25:12 -0700236 target_info = common.BuildInfo(self.TEST_TARGET_INFO_DICT, None)
237 source_info = common.BuildInfo(self.TEST_SOURCE_INFO_DICT, None)
Tao Baodf3a48b2018-01-10 16:30:43 -0800238 common.OPTIONS.incremental_source = ''
Tianjiea2076132020-08-19 17:25:32 -0700239 metadata = self.GetLegacyOtaMetadata(target_info, source_info)
Tao Baodf3a48b2018-01-10 16:30:43 -0800240 self.assertDictEqual(
241 {
242 'ota-type' : 'BLOCK',
Tianjiea2076132020-08-19 17:25:32 -0700243 'ota-required-cache' : '0',
Tao Baodf3a48b2018-01-10 16:30:43 -0800244 'post-build' : 'build-fingerprint-target',
245 'post-build-incremental' : 'build-version-incremental-target',
Tao Bao35dc2552018-02-01 13:18:00 -0800246 'post-sdk-level' : '27',
247 'post-security-patch-level' : '2017-12-01',
Tao Baodf3a48b2018-01-10 16:30:43 -0800248 'post-timestamp' : '1500000000',
249 'pre-device' : 'product-device',
250 'pre-build' : 'build-fingerprint-source',
251 'pre-build-incremental' : 'build-version-incremental-source',
252 },
253 metadata)
254
255 def test_GetPackageMetadata_wipe(self):
Tao Bao1c320f82019-10-04 23:25:12 -0700256 target_info = common.BuildInfo(self.TEST_TARGET_INFO_DICT, None)
Tao Baodf3a48b2018-01-10 16:30:43 -0800257 common.OPTIONS.wipe_user_data = True
Tianjiea2076132020-08-19 17:25:32 -0700258 metadata = self.GetLegacyOtaMetadata(target_info)
Tao Baodf3a48b2018-01-10 16:30:43 -0800259 self.assertDictEqual(
260 {
261 'ota-type' : 'BLOCK',
Tianjiea2076132020-08-19 17:25:32 -0700262 'ota-required-cache' : '0',
Tao Baodf3a48b2018-01-10 16:30:43 -0800263 'ota-wipe' : 'yes',
264 'post-build' : 'build-fingerprint-target',
265 'post-build-incremental' : 'build-version-incremental-target',
Tao Bao35dc2552018-02-01 13:18:00 -0800266 'post-sdk-level' : '27',
267 'post-security-patch-level' : '2017-12-01',
Tao Baodf3a48b2018-01-10 16:30:43 -0800268 'post-timestamp' : '1500000000',
269 'pre-device' : 'product-device',
270 },
271 metadata)
272
Tao Bao393eeb42019-03-06 16:00:38 -0800273 def test_GetPackageMetadata_retrofitDynamicPartitions(self):
Tao Bao1c320f82019-10-04 23:25:12 -0700274 target_info = common.BuildInfo(self.TEST_TARGET_INFO_DICT, None)
Tao Bao393eeb42019-03-06 16:00:38 -0800275 common.OPTIONS.retrofit_dynamic_partitions = True
Tianjiea2076132020-08-19 17:25:32 -0700276 metadata = self.GetLegacyOtaMetadata(target_info)
Tao Bao393eeb42019-03-06 16:00:38 -0800277 self.assertDictEqual(
278 {
279 'ota-retrofit-dynamic-partitions' : 'yes',
280 'ota-type' : 'BLOCK',
Tianjiea2076132020-08-19 17:25:32 -0700281 'ota-required-cache' : '0',
Tao Bao393eeb42019-03-06 16:00:38 -0800282 'post-build' : 'build-fingerprint-target',
283 'post-build-incremental' : 'build-version-incremental-target',
284 'post-sdk-level' : '27',
285 'post-security-patch-level' : '2017-12-01',
286 'post-timestamp' : '1500000000',
287 'pre-device' : 'product-device',
288 },
289 metadata)
290
Tao Baodf3a48b2018-01-10 16:30:43 -0800291 @staticmethod
292 def _test_GetPackageMetadata_swapBuildTimestamps(target_info, source_info):
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000293 (target_info['build.prop'].build_props['ro.build.date.utc'],
294 source_info['build.prop'].build_props['ro.build.date.utc']) = (
295 source_info['build.prop'].build_props['ro.build.date.utc'],
296 target_info['build.prop'].build_props['ro.build.date.utc'])
Tao Baodf3a48b2018-01-10 16:30:43 -0800297
298 def test_GetPackageMetadata_unintentionalDowngradeDetected(self):
299 target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)
300 source_info_dict = copy.deepcopy(self.TEST_SOURCE_INFO_DICT)
301 self._test_GetPackageMetadata_swapBuildTimestamps(
302 target_info_dict, source_info_dict)
303
Tao Bao1c320f82019-10-04 23:25:12 -0700304 target_info = common.BuildInfo(target_info_dict, None)
305 source_info = common.BuildInfo(source_info_dict, None)
Tao Baodf3a48b2018-01-10 16:30:43 -0800306 common.OPTIONS.incremental_source = ''
Tianjiea2076132020-08-19 17:25:32 -0700307 self.assertRaises(RuntimeError, self.GetLegacyOtaMetadata, target_info,
Tao Baodf3a48b2018-01-10 16:30:43 -0800308 source_info)
309
310 def test_GetPackageMetadata_downgrade(self):
311 target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)
312 source_info_dict = copy.deepcopy(self.TEST_SOURCE_INFO_DICT)
313 self._test_GetPackageMetadata_swapBuildTimestamps(
314 target_info_dict, source_info_dict)
315
Tao Bao1c320f82019-10-04 23:25:12 -0700316 target_info = common.BuildInfo(target_info_dict, None)
317 source_info = common.BuildInfo(source_info_dict, None)
Tao Baodf3a48b2018-01-10 16:30:43 -0800318 common.OPTIONS.incremental_source = ''
319 common.OPTIONS.downgrade = True
320 common.OPTIONS.wipe_user_data = True
Tianjiea2076132020-08-19 17:25:32 -0700321 metadata = self.GetLegacyOtaMetadata(target_info, source_info)
322
Tao Baodf3a48b2018-01-10 16:30:43 -0800323 self.assertDictEqual(
324 {
325 'ota-downgrade' : 'yes',
326 'ota-type' : 'BLOCK',
Tianjiea2076132020-08-19 17:25:32 -0700327 'ota-required-cache' : '0',
Tao Baodf3a48b2018-01-10 16:30:43 -0800328 'ota-wipe' : 'yes',
329 'post-build' : 'build-fingerprint-target',
330 'post-build-incremental' : 'build-version-incremental-target',
Tao Bao35dc2552018-02-01 13:18:00 -0800331 'post-sdk-level' : '27',
332 'post-security-patch-level' : '2017-12-01',
Tao Baofaa8e0b2018-04-12 14:31:43 -0700333 'post-timestamp' : '1400000000',
Tao Baodf3a48b2018-01-10 16:30:43 -0800334 'pre-device' : 'product-device',
335 'pre-build' : 'build-fingerprint-source',
336 'pre-build-incremental' : 'build-version-incremental-source',
337 },
338 metadata)
Tao Baofabe0832018-01-17 15:52:28 -0800339
Tao Bao82490d32019-04-09 00:12:30 -0700340 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof7140c02018-01-30 17:09:24 -0800341 def test_GetTargetFilesZipForSecondaryImages(self):
342 input_file = construct_target_files(secondary=True)
343 target_file = GetTargetFilesZipForSecondaryImages(input_file)
344
345 with zipfile.ZipFile(target_file) as verify_zip:
346 namelist = verify_zip.namelist()
Tao Bao615b65d2019-10-06 22:59:45 -0700347 ab_partitions = verify_zip.read('META/ab_partitions.txt').decode()
Tao Baof7140c02018-01-30 17:09:24 -0800348
349 self.assertIn('META/ab_partitions.txt', namelist)
Tao Baof7140c02018-01-30 17:09:24 -0800350 self.assertIn('IMAGES/system.img', namelist)
Tao Bao12489802018-07-12 14:47:38 -0700351 self.assertIn('RADIO/bootloader.img', namelist)
Tao Bao15a146a2018-02-21 16:06:59 -0800352 self.assertIn(POSTINSTALL_CONFIG, namelist)
Tao Baof7140c02018-01-30 17:09:24 -0800353
Tao Bao3e759462019-09-17 22:43:11 -0700354 self.assertNotIn('IMAGES/boot.img', namelist)
Tao Baof7140c02018-01-30 17:09:24 -0800355 self.assertNotIn('IMAGES/system_other.img', namelist)
356 self.assertNotIn('IMAGES/system.map', namelist)
Tao Bao3e759462019-09-17 22:43:11 -0700357 self.assertNotIn('RADIO/modem.img', namelist)
Tao Baof7140c02018-01-30 17:09:24 -0800358
Tao Bao3e759462019-09-17 22:43:11 -0700359 expected_ab_partitions = ['system', 'bootloader']
Tianjie Xu1c808002019-09-11 00:29:26 -0700360 self.assertEqual('\n'.join(expected_ab_partitions), ab_partitions)
361
Tao Bao82490d32019-04-09 00:12:30 -0700362 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao15a146a2018-02-21 16:06:59 -0800363 def test_GetTargetFilesZipForSecondaryImages_skipPostinstall(self):
364 input_file = construct_target_files(secondary=True)
365 target_file = GetTargetFilesZipForSecondaryImages(
366 input_file, skip_postinstall=True)
367
368 with zipfile.ZipFile(target_file) as verify_zip:
369 namelist = verify_zip.namelist()
370
371 self.assertIn('META/ab_partitions.txt', namelist)
Tao Bao15a146a2018-02-21 16:06:59 -0800372 self.assertIn('IMAGES/system.img', namelist)
Tao Bao12489802018-07-12 14:47:38 -0700373 self.assertIn('RADIO/bootloader.img', namelist)
Tao Bao15a146a2018-02-21 16:06:59 -0800374
Tao Bao3e759462019-09-17 22:43:11 -0700375 self.assertNotIn('IMAGES/boot.img', namelist)
Tao Bao15a146a2018-02-21 16:06:59 -0800376 self.assertNotIn('IMAGES/system_other.img', namelist)
377 self.assertNotIn('IMAGES/system.map', namelist)
Tao Bao3e759462019-09-17 22:43:11 -0700378 self.assertNotIn('RADIO/modem.img', namelist)
Tao Bao15a146a2018-02-21 16:06:59 -0800379 self.assertNotIn(POSTINSTALL_CONFIG, namelist)
380
Tao Bao82490d32019-04-09 00:12:30 -0700381 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao12489802018-07-12 14:47:38 -0700382 def test_GetTargetFilesZipForSecondaryImages_withoutRadioImages(self):
383 input_file = construct_target_files(secondary=True)
384 common.ZipDelete(input_file, 'RADIO/bootloader.img')
385 common.ZipDelete(input_file, 'RADIO/modem.img')
386 target_file = GetTargetFilesZipForSecondaryImages(input_file)
387
388 with zipfile.ZipFile(target_file) as verify_zip:
389 namelist = verify_zip.namelist()
390
391 self.assertIn('META/ab_partitions.txt', namelist)
Tao Bao12489802018-07-12 14:47:38 -0700392 self.assertIn('IMAGES/system.img', namelist)
Tao Bao12489802018-07-12 14:47:38 -0700393 self.assertIn(POSTINSTALL_CONFIG, namelist)
394
Tao Bao3e759462019-09-17 22:43:11 -0700395 self.assertNotIn('IMAGES/boot.img', namelist)
Tao Bao12489802018-07-12 14:47:38 -0700396 self.assertNotIn('IMAGES/system_other.img', namelist)
397 self.assertNotIn('IMAGES/system.map', namelist)
398 self.assertNotIn('RADIO/bootloader.img', namelist)
399 self.assertNotIn('RADIO/modem.img', namelist)
400
Tao Bao82490d32019-04-09 00:12:30 -0700401 @test_utils.SkipIfExternalToolsUnavailable()
Tianjie Xu1c808002019-09-11 00:29:26 -0700402 def test_GetTargetFilesZipForSecondaryImages_dynamicPartitions(self):
403 input_file = construct_target_files(secondary=True)
404 misc_info = '\n'.join([
405 'use_dynamic_partition_size=true',
406 'use_dynamic_partitions=true',
407 'dynamic_partition_list=system vendor product',
408 'super_partition_groups=google_dynamic_partitions',
409 'super_google_dynamic_partitions_group_size=4873781248',
410 'super_google_dynamic_partitions_partition_list=system vendor product',
411 ])
412 dynamic_partitions_info = '\n'.join([
413 'super_partition_groups=google_dynamic_partitions',
414 'super_google_dynamic_partitions_group_size=4873781248',
415 'super_google_dynamic_partitions_partition_list=system vendor product',
416 ])
417
418 with zipfile.ZipFile(input_file, 'a') as append_zip:
419 common.ZipWriteStr(append_zip, 'META/misc_info.txt', misc_info)
420 common.ZipWriteStr(append_zip, 'META/dynamic_partitions_info.txt',
421 dynamic_partitions_info)
422
423 target_file = GetTargetFilesZipForSecondaryImages(input_file)
424
425 with zipfile.ZipFile(target_file) as verify_zip:
426 namelist = verify_zip.namelist()
Tao Bao615b65d2019-10-06 22:59:45 -0700427 updated_misc_info = verify_zip.read('META/misc_info.txt').decode()
Tianjie Xu1c808002019-09-11 00:29:26 -0700428 updated_dynamic_partitions_info = verify_zip.read(
Tao Bao615b65d2019-10-06 22:59:45 -0700429 'META/dynamic_partitions_info.txt').decode()
Tianjie Xu1c808002019-09-11 00:29:26 -0700430
431 self.assertIn('META/ab_partitions.txt', namelist)
Tianjie Xu1c808002019-09-11 00:29:26 -0700432 self.assertIn('IMAGES/system.img', namelist)
433 self.assertIn(POSTINSTALL_CONFIG, namelist)
434 self.assertIn('META/misc_info.txt', namelist)
435 self.assertIn('META/dynamic_partitions_info.txt', namelist)
436
Tao Bao3e759462019-09-17 22:43:11 -0700437 self.assertNotIn('IMAGES/boot.img', namelist)
Tianjie Xu1c808002019-09-11 00:29:26 -0700438 self.assertNotIn('IMAGES/system_other.img', namelist)
439 self.assertNotIn('IMAGES/system.map', namelist)
440
441 # Check the vendor & product are removed from the partitions list.
442 expected_misc_info = misc_info.replace('system vendor product',
443 'system')
444 expected_dynamic_partitions_info = dynamic_partitions_info.replace(
445 'system vendor product', 'system')
446 self.assertEqual(expected_misc_info, updated_misc_info)
447 self.assertEqual(expected_dynamic_partitions_info,
448 updated_dynamic_partitions_info)
449
450 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao15a146a2018-02-21 16:06:59 -0800451 def test_GetTargetFilesZipWithoutPostinstallConfig(self):
452 input_file = construct_target_files()
453 target_file = GetTargetFilesZipWithoutPostinstallConfig(input_file)
454 with zipfile.ZipFile(target_file) as verify_zip:
455 self.assertNotIn(POSTINSTALL_CONFIG, verify_zip.namelist())
456
Tao Bao82490d32019-04-09 00:12:30 -0700457 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao15a146a2018-02-21 16:06:59 -0800458 def test_GetTargetFilesZipWithoutPostinstallConfig_missingEntry(self):
459 input_file = construct_target_files()
460 common.ZipDelete(input_file, POSTINSTALL_CONFIG)
461 target_file = GetTargetFilesZipWithoutPostinstallConfig(input_file)
462 with zipfile.ZipFile(target_file) as verify_zip:
463 self.assertNotIn(POSTINSTALL_CONFIG, verify_zip.namelist())
464
Tao Bao3bf8c652018-03-16 12:59:42 -0700465 def _test_FinalizeMetadata(self, large_entry=False):
466 entries = [
467 'required-entry1',
468 'required-entry2',
469 ]
470 zip_file = PropertyFilesTest.construct_zip_package(entries)
471 # Add a large entry of 1 GiB if requested.
472 if large_entry:
473 with zipfile.ZipFile(zip_file, 'a') as zip_fp:
474 zip_fp.writestr(
475 # Using 'zoo' so that the entry stays behind others after signing.
476 'zoo',
477 'A' * 1024 * 1024 * 1024,
478 zipfile.ZIP_STORED)
479
Tianjiea2076132020-08-19 17:25:32 -0700480 metadata = ota_metadata_pb2.OtaMetadata()
Tao Bao3bf8c652018-03-16 12:59:42 -0700481 output_file = common.MakeTempFile(suffix='.zip')
482 needed_property_files = (
483 TestPropertyFiles(),
484 )
485 FinalizeMetadata(metadata, zip_file, output_file, needed_property_files)
Tianjiea2076132020-08-19 17:25:32 -0700486 self.assertIn('ota-test-property-files', metadata.property_files)
Tao Bao3bf8c652018-03-16 12:59:42 -0700487
Tao Bao82490d32019-04-09 00:12:30 -0700488 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao3bf8c652018-03-16 12:59:42 -0700489 def test_FinalizeMetadata(self):
490 self._test_FinalizeMetadata()
491
Tao Bao82490d32019-04-09 00:12:30 -0700492 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao3bf8c652018-03-16 12:59:42 -0700493 def test_FinalizeMetadata_withNoSigning(self):
494 common.OPTIONS.no_signing = True
495 self._test_FinalizeMetadata()
496
Tao Bao82490d32019-04-09 00:12:30 -0700497 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao3bf8c652018-03-16 12:59:42 -0700498 def test_FinalizeMetadata_largeEntry(self):
499 self._test_FinalizeMetadata(large_entry=True)
500
Tao Bao82490d32019-04-09 00:12:30 -0700501 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao3bf8c652018-03-16 12:59:42 -0700502 def test_FinalizeMetadata_largeEntry_withNoSigning(self):
503 common.OPTIONS.no_signing = True
504 self._test_FinalizeMetadata(large_entry=True)
505
Tao Bao82490d32019-04-09 00:12:30 -0700506 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao3bf8c652018-03-16 12:59:42 -0700507 def test_FinalizeMetadata_insufficientSpace(self):
508 entries = [
509 'required-entry1',
510 'required-entry2',
511 'optional-entry1',
512 'optional-entry2',
513 ]
514 zip_file = PropertyFilesTest.construct_zip_package(entries)
515 with zipfile.ZipFile(zip_file, 'a') as zip_fp:
516 zip_fp.writestr(
517 # 'foo-entry1' will appear ahead of all other entries (in alphabetical
518 # order) after the signing, which will in turn trigger the
519 # InsufficientSpaceException and an automatic retry.
520 'foo-entry1',
521 'A' * 1024 * 1024,
522 zipfile.ZIP_STORED)
523
Tianjiea2076132020-08-19 17:25:32 -0700524 metadata = ota_metadata_pb2.OtaMetadata()
Tao Bao3bf8c652018-03-16 12:59:42 -0700525 needed_property_files = (
526 TestPropertyFiles(),
527 )
528 output_file = common.MakeTempFile(suffix='.zip')
529 FinalizeMetadata(metadata, zip_file, output_file, needed_property_files)
Tianjiea2076132020-08-19 17:25:32 -0700530 self.assertIn('ota-test-property-files', metadata.property_files)
Tao Bao3bf8c652018-03-16 12:59:42 -0700531
Tao Baoae5e4c32018-03-01 19:30:00 -0800532
Tao Bao69203522018-03-08 16:09:01 -0800533class TestPropertyFiles(PropertyFiles):
534 """A class that extends PropertyFiles for testing purpose."""
535
536 def __init__(self):
537 super(TestPropertyFiles, self).__init__()
538 self.name = 'ota-test-property-files'
539 self.required = (
540 'required-entry1',
541 'required-entry2',
542 )
543 self.optional = (
544 'optional-entry1',
545 'optional-entry2',
546 )
547
548
Tianjiea2076132020-08-19 17:25:32 -0700549class PropertyFilesTest(PropertyFilesTestCase):
Tao Baof5110492018-03-02 09:47:43 -0800550
Tao Bao82490d32019-04-09 00:12:30 -0700551 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoae5e4c32018-03-01 19:30:00 -0800552 def test_Compute(self):
Tao Baof5110492018-03-02 09:47:43 -0800553 entries = (
Tao Bao69203522018-03-08 16:09:01 -0800554 'required-entry1',
555 'required-entry2',
Tao Baof5110492018-03-02 09:47:43 -0800556 )
Tao Bao3bf8c652018-03-16 12:59:42 -0700557 zip_file = self.construct_zip_package(entries)
Tao Bao69203522018-03-08 16:09:01 -0800558 property_files = TestPropertyFiles()
Tao Baof5110492018-03-02 09:47:43 -0800559 with zipfile.ZipFile(zip_file, 'r') as zip_fp:
Tao Bao69203522018-03-08 16:09:01 -0800560 property_files_string = property_files.Compute(zip_fp)
Tao Baof5110492018-03-02 09:47:43 -0800561
Tao Bao69203522018-03-08 16:09:01 -0800562 tokens = self._parse_property_files_string(property_files_string)
Tianjiea2076132020-08-19 17:25:32 -0700563 self.assertEqual(4, len(tokens))
Tao Baof5110492018-03-02 09:47:43 -0800564 self._verify_entries(zip_file, tokens, entries)
565
Tao Bao69203522018-03-08 16:09:01 -0800566 def test_Compute_withOptionalEntries(self):
Tao Baof5110492018-03-02 09:47:43 -0800567 entries = (
Tao Bao69203522018-03-08 16:09:01 -0800568 'required-entry1',
569 'required-entry2',
570 'optional-entry1',
571 'optional-entry2',
Tao Baof5110492018-03-02 09:47:43 -0800572 )
Tao Bao3bf8c652018-03-16 12:59:42 -0700573 zip_file = self.construct_zip_package(entries)
Tao Bao69203522018-03-08 16:09:01 -0800574 property_files = TestPropertyFiles()
Tao Baof5110492018-03-02 09:47:43 -0800575 with zipfile.ZipFile(zip_file, 'r') as zip_fp:
Tao Bao69203522018-03-08 16:09:01 -0800576 property_files_string = property_files.Compute(zip_fp)
Tao Baof5110492018-03-02 09:47:43 -0800577
Tao Bao69203522018-03-08 16:09:01 -0800578 tokens = self._parse_property_files_string(property_files_string)
Tianjiea2076132020-08-19 17:25:32 -0700579 self.assertEqual(6, len(tokens))
Tao Baof5110492018-03-02 09:47:43 -0800580 self._verify_entries(zip_file, tokens, entries)
581
Tao Bao69203522018-03-08 16:09:01 -0800582 def test_Compute_missingRequiredEntry(self):
583 entries = (
584 'required-entry2',
585 )
Tao Bao3bf8c652018-03-16 12:59:42 -0700586 zip_file = self.construct_zip_package(entries)
Tao Bao69203522018-03-08 16:09:01 -0800587 property_files = TestPropertyFiles()
588 with zipfile.ZipFile(zip_file, 'r') as zip_fp:
589 self.assertRaises(KeyError, property_files.Compute, zip_fp)
590
Tao Bao82490d32019-04-09 00:12:30 -0700591 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoae5e4c32018-03-01 19:30:00 -0800592 def test_Finalize(self):
Tao Baof5110492018-03-02 09:47:43 -0800593 entries = [
Tao Bao69203522018-03-08 16:09:01 -0800594 'required-entry1',
595 'required-entry2',
Tao Baof5110492018-03-02 09:47:43 -0800596 'META-INF/com/android/metadata',
Tianjiea2076132020-08-19 17:25:32 -0700597 'META-INF/com/android/metadata.pb',
Tao Baof5110492018-03-02 09:47:43 -0800598 ]
Tao Bao3bf8c652018-03-16 12:59:42 -0700599 zip_file = self.construct_zip_package(entries)
Tao Bao69203522018-03-08 16:09:01 -0800600 property_files = TestPropertyFiles()
Tao Baof5110492018-03-02 09:47:43 -0800601 with zipfile.ZipFile(zip_file, 'r') as zip_fp:
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -0700602 raw_metadata = property_files.GetPropertyFilesString(
Tao Baoae5e4c32018-03-01 19:30:00 -0800603 zip_fp, reserve_space=False)
604 streaming_metadata = property_files.Finalize(zip_fp, len(raw_metadata))
Tao Bao69203522018-03-08 16:09:01 -0800605 tokens = self._parse_property_files_string(streaming_metadata)
Tao Baof5110492018-03-02 09:47:43 -0800606
Tianjiea2076132020-08-19 17:25:32 -0700607 self.assertEqual(4, len(tokens))
Tao Baof5110492018-03-02 09:47:43 -0800608 # 'META-INF/com/android/metadata' will be key'd as 'metadata' in the
609 # streaming metadata.
610 entries[2] = 'metadata'
Tianjiea2076132020-08-19 17:25:32 -0700611 entries[3] = 'metadata.pb'
Tao Baof5110492018-03-02 09:47:43 -0800612 self._verify_entries(zip_file, tokens, entries)
613
Tao Bao82490d32019-04-09 00:12:30 -0700614 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoae5e4c32018-03-01 19:30:00 -0800615 def test_Finalize_assertReservedLength(self):
Tao Baof5110492018-03-02 09:47:43 -0800616 entries = (
Tao Bao69203522018-03-08 16:09:01 -0800617 'required-entry1',
618 'required-entry2',
619 'optional-entry1',
620 'optional-entry2',
Tao Baof5110492018-03-02 09:47:43 -0800621 'META-INF/com/android/metadata',
Tianjiea2076132020-08-19 17:25:32 -0700622 'META-INF/com/android/metadata.pb',
Tao Baof5110492018-03-02 09:47:43 -0800623 )
Tao Bao3bf8c652018-03-16 12:59:42 -0700624 zip_file = self.construct_zip_package(entries)
Tao Bao69203522018-03-08 16:09:01 -0800625 property_files = TestPropertyFiles()
Tao Baof5110492018-03-02 09:47:43 -0800626 with zipfile.ZipFile(zip_file, 'r') as zip_fp:
627 # First get the raw metadata string (i.e. without padding space).
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -0700628 raw_metadata = property_files.GetPropertyFilesString(
Tao Baoae5e4c32018-03-01 19:30:00 -0800629 zip_fp, reserve_space=False)
Tao Baof5110492018-03-02 09:47:43 -0800630 raw_length = len(raw_metadata)
631
632 # Now pass in the exact expected length.
Tao Baoae5e4c32018-03-01 19:30:00 -0800633 streaming_metadata = property_files.Finalize(zip_fp, raw_length)
Tao Baof5110492018-03-02 09:47:43 -0800634 self.assertEqual(raw_length, len(streaming_metadata))
635
636 # Or pass in insufficient length.
637 self.assertRaises(
Tao Bao3bf8c652018-03-16 12:59:42 -0700638 PropertyFiles.InsufficientSpaceException,
Tao Baoae5e4c32018-03-01 19:30:00 -0800639 property_files.Finalize,
Tao Baof5110492018-03-02 09:47:43 -0800640 zip_fp,
Tao Baoae5e4c32018-03-01 19:30:00 -0800641 raw_length - 1)
Tao Baof5110492018-03-02 09:47:43 -0800642
643 # Or pass in a much larger size.
Tao Baoae5e4c32018-03-01 19:30:00 -0800644 streaming_metadata = property_files.Finalize(
Tao Baof5110492018-03-02 09:47:43 -0800645 zip_fp,
Tao Baoae5e4c32018-03-01 19:30:00 -0800646 raw_length + 20)
Tao Baof5110492018-03-02 09:47:43 -0800647 self.assertEqual(raw_length + 20, len(streaming_metadata))
648 self.assertEqual(' ' * 20, streaming_metadata[raw_length:])
649
Tao Baoae5e4c32018-03-01 19:30:00 -0800650 def test_Verify(self):
651 entries = (
Tao Bao69203522018-03-08 16:09:01 -0800652 'required-entry1',
653 'required-entry2',
654 'optional-entry1',
655 'optional-entry2',
656 'META-INF/com/android/metadata',
Tianjiea2076132020-08-19 17:25:32 -0700657 'META-INF/com/android/metadata.pb',
Tao Bao69203522018-03-08 16:09:01 -0800658 )
Tao Bao3bf8c652018-03-16 12:59:42 -0700659 zip_file = self.construct_zip_package(entries)
Tao Bao69203522018-03-08 16:09:01 -0800660 property_files = TestPropertyFiles()
661 with zipfile.ZipFile(zip_file, 'r') as zip_fp:
662 # First get the raw metadata string (i.e. without padding space).
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -0700663 raw_metadata = property_files.GetPropertyFilesString(
Tao Bao69203522018-03-08 16:09:01 -0800664 zip_fp, reserve_space=False)
665
666 # Should pass the test if verification passes.
667 property_files.Verify(zip_fp, raw_metadata)
668
669 # Or raise on verification failure.
670 self.assertRaises(
671 AssertionError, property_files.Verify, zip_fp, raw_metadata + 'x')
672
673
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400674class StreamingPropertyFilesTest(PropertyFilesTestCase):
Kelvin Zhangc693d952020-07-22 19:21:22 -0400675 """Additional validity checks specialized for StreamingPropertyFiles."""
Tao Bao69203522018-03-08 16:09:01 -0800676
677 def test_init(self):
678 property_files = StreamingPropertyFiles()
679 self.assertEqual('ota-streaming-property-files', property_files.name)
680 self.assertEqual(
681 (
682 'payload.bin',
683 'payload_properties.txt',
684 ),
685 property_files.required)
686 self.assertEqual(
687 (
Tianjie Xu4c05f4a2018-09-14 16:24:41 -0700688 'care_map.pb',
Tao Bao69203522018-03-08 16:09:01 -0800689 'care_map.txt',
690 'compatibility.zip',
691 ),
692 property_files.optional)
693
694 def test_Compute(self):
695 entries = (
Tao Baoae5e4c32018-03-01 19:30:00 -0800696 'payload.bin',
697 'payload_properties.txt',
698 'care_map.txt',
Tao Bao69203522018-03-08 16:09:01 -0800699 'compatibility.zip',
700 )
Tao Bao3bf8c652018-03-16 12:59:42 -0700701 zip_file = self.construct_zip_package(entries)
Tao Bao69203522018-03-08 16:09:01 -0800702 property_files = StreamingPropertyFiles()
703 with zipfile.ZipFile(zip_file, 'r') as zip_fp:
704 property_files_string = property_files.Compute(zip_fp)
705
706 tokens = self._parse_property_files_string(property_files_string)
Tianjiea2076132020-08-19 17:25:32 -0700707 self.assertEqual(6, len(tokens))
Tao Bao69203522018-03-08 16:09:01 -0800708 self._verify_entries(zip_file, tokens, entries)
709
710 def test_Finalize(self):
711 entries = [
712 'payload.bin',
713 'payload_properties.txt',
714 'care_map.txt',
715 'compatibility.zip',
716 'META-INF/com/android/metadata',
Tianjiea2076132020-08-19 17:25:32 -0700717 'META-INF/com/android/metadata.pb',
Tao Bao69203522018-03-08 16:09:01 -0800718 ]
Tao Bao3bf8c652018-03-16 12:59:42 -0700719 zip_file = self.construct_zip_package(entries)
Tao Bao69203522018-03-08 16:09:01 -0800720 property_files = StreamingPropertyFiles()
721 with zipfile.ZipFile(zip_file, 'r') as zip_fp:
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -0700722 raw_metadata = property_files.GetPropertyFilesString(
Tao Bao69203522018-03-08 16:09:01 -0800723 zip_fp, reserve_space=False)
724 streaming_metadata = property_files.Finalize(zip_fp, len(raw_metadata))
725 tokens = self._parse_property_files_string(streaming_metadata)
726
Tianjiea2076132020-08-19 17:25:32 -0700727 self.assertEqual(6, len(tokens))
Tao Bao69203522018-03-08 16:09:01 -0800728 # 'META-INF/com/android/metadata' will be key'd as 'metadata' in the
729 # streaming metadata.
730 entries[4] = 'metadata'
Tianjiea2076132020-08-19 17:25:32 -0700731 entries[5] = 'metadata.pb'
Tao Bao69203522018-03-08 16:09:01 -0800732 self._verify_entries(zip_file, tokens, entries)
733
734 def test_Verify(self):
735 entries = (
736 'payload.bin',
737 'payload_properties.txt',
738 'care_map.txt',
739 'compatibility.zip',
Tao Baoae5e4c32018-03-01 19:30:00 -0800740 'META-INF/com/android/metadata',
Tianjiea2076132020-08-19 17:25:32 -0700741 'META-INF/com/android/metadata.pb',
Tao Baoae5e4c32018-03-01 19:30:00 -0800742 )
Tao Bao3bf8c652018-03-16 12:59:42 -0700743 zip_file = self.construct_zip_package(entries)
Tao Baoae5e4c32018-03-01 19:30:00 -0800744 property_files = StreamingPropertyFiles()
745 with zipfile.ZipFile(zip_file, 'r') as zip_fp:
746 # First get the raw metadata string (i.e. without padding space).
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -0700747 raw_metadata = property_files.GetPropertyFilesString(
Tao Baoae5e4c32018-03-01 19:30:00 -0800748 zip_fp, reserve_space=False)
749
750 # Should pass the test if verification passes.
751 property_files.Verify(zip_fp, raw_metadata)
752
753 # Or raise on verification failure.
754 self.assertRaises(
755 AssertionError, property_files.Verify, zip_fp, raw_metadata + 'x')
756
Tao Baofabe0832018-01-17 15:52:28 -0800757
Kelvin Zhangcff4d762020-07-29 16:37:51 -0400758class AbOtaPropertyFilesTest(PropertyFilesTestCase):
Kelvin Zhangc693d952020-07-22 19:21:22 -0400759 """Additional validity checks specialized for AbOtaPropertyFiles."""
Tao Baob6304672018-03-08 16:28:33 -0800760
761 # The size for payload and metadata signature size.
762 SIGNATURE_SIZE = 256
763
764 def setUp(self):
765 self.testdata_dir = test_utils.get_testdata_dir()
766 self.assertTrue(os.path.exists(self.testdata_dir))
767
768 common.OPTIONS.wipe_user_data = False
769 common.OPTIONS.payload_signer = None
770 common.OPTIONS.payload_signer_args = None
771 common.OPTIONS.package_key = os.path.join(self.testdata_dir, 'testkey')
772 common.OPTIONS.key_passwords = {
773 common.OPTIONS.package_key : None,
774 }
775
776 def test_init(self):
777 property_files = AbOtaPropertyFiles()
778 self.assertEqual('ota-property-files', property_files.name)
779 self.assertEqual(
780 (
781 'payload.bin',
782 'payload_properties.txt',
783 ),
784 property_files.required)
785 self.assertEqual(
786 (
Tianjie Xu4c05f4a2018-09-14 16:24:41 -0700787 'care_map.pb',
Tao Baob6304672018-03-08 16:28:33 -0800788 'care_map.txt',
789 'compatibility.zip',
790 ),
791 property_files.optional)
792
Tao Bao82490d32019-04-09 00:12:30 -0700793 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baob6304672018-03-08 16:28:33 -0800794 def test_GetPayloadMetadataOffsetAndSize(self):
795 target_file = construct_target_files()
796 payload = Payload()
797 payload.Generate(target_file)
798
799 payload_signer = PayloadSigner()
800 payload.Sign(payload_signer)
801
802 output_file = common.MakeTempFile(suffix='.zip')
803 with zipfile.ZipFile(output_file, 'w') as output_zip:
804 payload.WriteToZip(output_zip)
805
806 # Find out the payload metadata offset and size.
807 property_files = AbOtaPropertyFiles()
808 with zipfile.ZipFile(output_file) as input_zip:
809 # pylint: disable=protected-access
810 payload_offset, metadata_total = (
811 property_files._GetPayloadMetadataOffsetAndSize(input_zip))
812
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700813 # The signature proto has the following format (details in
814 # /platform/system/update_engine/update_metadata.proto):
815 # message Signature {
816 # optional uint32 version = 1;
817 # optional bytes data = 2;
818 # optional fixed32 unpadded_signature_size = 3;
819 # }
820 #
821 # According to the protobuf encoding, the tail of the signature message will
822 # be [signature string(256 bytes) + encoding of the fixed32 number 256]. And
823 # 256 is encoded as 'x1d\x00\x01\x00\x00':
824 # [3 (field number) << 3 | 5 (type) + byte reverse of 0x100 (256)].
825 # Details in (https://developers.google.com/protocol-buffers/docs/encoding)
826 signature_tail_length = self.SIGNATURE_SIZE + 5
827 self.assertGreater(metadata_total, signature_tail_length)
Tao Baob6304672018-03-08 16:28:33 -0800828 with open(output_file, 'rb') as verify_fp:
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700829 verify_fp.seek(payload_offset + metadata_total - signature_tail_length)
830 metadata_signature_proto_tail = verify_fp.read(signature_tail_length)
831
832 self.assertEqual(b'\x1d\x00\x01\x00\x00',
833 metadata_signature_proto_tail[-5:])
834 metadata_signature = metadata_signature_proto_tail[:-5]
Tao Baob6304672018-03-08 16:28:33 -0800835
836 # Now we extract the metadata hash via brillo_update_payload script, which
837 # will serve as the oracle result.
838 payload_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
839 metadata_sig_file = common.MakeTempFile(prefix="sig-", suffix=".bin")
840 cmd = ['brillo_update_payload', 'hash',
841 '--unsigned_payload', payload.payload_file,
842 '--signature_size', str(self.SIGNATURE_SIZE),
843 '--metadata_hash_file', metadata_sig_file,
844 '--payload_hash_file', payload_sig_file]
Tao Bao73dd4f42018-10-04 16:25:33 -0700845 proc = common.Run(cmd)
Tao Baob6304672018-03-08 16:28:33 -0800846 stdoutdata, _ = proc.communicate()
847 self.assertEqual(
848 0, proc.returncode,
Tao Bao73dd4f42018-10-04 16:25:33 -0700849 'Failed to run brillo_update_payload:\n{}'.format(stdoutdata))
Tao Baob6304672018-03-08 16:28:33 -0800850
851 signed_metadata_sig_file = payload_signer.Sign(metadata_sig_file)
852
853 # Finally we can compare the two signatures.
854 with open(signed_metadata_sig_file, 'rb') as verify_fp:
855 self.assertEqual(verify_fp.read(), metadata_signature)
856
857 @staticmethod
Tao Bao3bf8c652018-03-16 12:59:42 -0700858 def construct_zip_package_withValidPayload(with_metadata=False):
859 # Cannot use construct_zip_package() since we need a "valid" payload.bin.
Tao Baob6304672018-03-08 16:28:33 -0800860 target_file = construct_target_files()
861 payload = Payload()
862 payload.Generate(target_file)
863
864 payload_signer = PayloadSigner()
865 payload.Sign(payload_signer)
866
867 zip_file = common.MakeTempFile(suffix='.zip')
868 with zipfile.ZipFile(zip_file, 'w') as zip_fp:
869 # 'payload.bin',
870 payload.WriteToZip(zip_fp)
871
872 # Other entries.
873 entries = ['care_map.txt', 'compatibility.zip']
874
875 # Put META-INF/com/android/metadata if needed.
876 if with_metadata:
877 entries.append('META-INF/com/android/metadata')
Tianjiea2076132020-08-19 17:25:32 -0700878 entries.append('META-INF/com/android/metadata.pb')
Tao Baob6304672018-03-08 16:28:33 -0800879
880 for entry in entries:
881 zip_fp.writestr(
882 entry, entry.replace('.', '-').upper(), zipfile.ZIP_STORED)
883
884 return zip_file
885
Tao Bao82490d32019-04-09 00:12:30 -0700886 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baob6304672018-03-08 16:28:33 -0800887 def test_Compute(self):
Tao Bao3bf8c652018-03-16 12:59:42 -0700888 zip_file = self.construct_zip_package_withValidPayload()
Tao Baob6304672018-03-08 16:28:33 -0800889 property_files = AbOtaPropertyFiles()
890 with zipfile.ZipFile(zip_file, 'r') as zip_fp:
891 property_files_string = property_files.Compute(zip_fp)
892
893 tokens = self._parse_property_files_string(property_files_string)
Tianjiea2076132020-08-19 17:25:32 -0700894 # "7" indcludes the four entries above, two metadata entries, and one entry
Tao Baob6304672018-03-08 16:28:33 -0800895 # for payload-metadata.bin.
Tianjiea2076132020-08-19 17:25:32 -0700896 self.assertEqual(7, len(tokens))
Tao Baob6304672018-03-08 16:28:33 -0800897 self._verify_entries(
898 zip_file, tokens, ('care_map.txt', 'compatibility.zip'))
899
Tao Bao82490d32019-04-09 00:12:30 -0700900 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baob6304672018-03-08 16:28:33 -0800901 def test_Finalize(self):
Tao Bao3bf8c652018-03-16 12:59:42 -0700902 zip_file = self.construct_zip_package_withValidPayload(with_metadata=True)
Tao Baob6304672018-03-08 16:28:33 -0800903 property_files = AbOtaPropertyFiles()
904 with zipfile.ZipFile(zip_file, 'r') as zip_fp:
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -0700905 raw_metadata = property_files.GetPropertyFilesString(
Tao Baob6304672018-03-08 16:28:33 -0800906 zip_fp, reserve_space=False)
907 property_files_string = property_files.Finalize(zip_fp, len(raw_metadata))
908
909 tokens = self._parse_property_files_string(property_files_string)
Tianjiea2076132020-08-19 17:25:32 -0700910 # "7" includes the four entries above, two metadata entries, and one entry
Tao Baob6304672018-03-08 16:28:33 -0800911 # for payload-metadata.bin.
Tianjiea2076132020-08-19 17:25:32 -0700912 self.assertEqual(7, len(tokens))
Tao Baob6304672018-03-08 16:28:33 -0800913 self._verify_entries(
914 zip_file, tokens, ('care_map.txt', 'compatibility.zip'))
915
Tao Bao82490d32019-04-09 00:12:30 -0700916 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baob6304672018-03-08 16:28:33 -0800917 def test_Verify(self):
Tao Bao3bf8c652018-03-16 12:59:42 -0700918 zip_file = self.construct_zip_package_withValidPayload(with_metadata=True)
Tao Baob6304672018-03-08 16:28:33 -0800919 property_files = AbOtaPropertyFiles()
920 with zipfile.ZipFile(zip_file, 'r') as zip_fp:
Zhomart Mukhamejanov603655f2018-05-04 12:35:09 -0700921 raw_metadata = property_files.GetPropertyFilesString(
Tao Baob6304672018-03-08 16:28:33 -0800922 zip_fp, reserve_space=False)
923
924 property_files.Verify(zip_fp, raw_metadata)
925
926
Tao Bao65b94e92018-10-11 21:57:26 -0700927class PayloadSignerTest(test_utils.ReleaseToolsTestCase):
Tao Baofabe0832018-01-17 15:52:28 -0800928
929 SIGFILE = 'sigfile.bin'
930 SIGNED_SIGFILE = 'signed-sigfile.bin'
931
932 def setUp(self):
Tao Bao04e1f012018-02-04 12:13:35 -0800933 self.testdata_dir = test_utils.get_testdata_dir()
Tao Baofabe0832018-01-17 15:52:28 -0800934 self.assertTrue(os.path.exists(self.testdata_dir))
935
936 common.OPTIONS.payload_signer = None
937 common.OPTIONS.payload_signer_args = []
938 common.OPTIONS.package_key = os.path.join(self.testdata_dir, 'testkey')
939 common.OPTIONS.key_passwords = {
940 common.OPTIONS.package_key : None,
941 }
942
Tao Baofabe0832018-01-17 15:52:28 -0800943 def _assertFilesEqual(self, file1, file2):
944 with open(file1, 'rb') as fp1, open(file2, 'rb') as fp2:
945 self.assertEqual(fp1.read(), fp2.read())
946
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700947 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofabe0832018-01-17 15:52:28 -0800948 def test_init(self):
949 payload_signer = PayloadSigner()
950 self.assertEqual('openssl', payload_signer.signer)
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700951 self.assertEqual(256, payload_signer.maximum_signature_size)
Tao Baofabe0832018-01-17 15:52:28 -0800952
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700953 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofabe0832018-01-17 15:52:28 -0800954 def test_init_withPassword(self):
955 common.OPTIONS.package_key = os.path.join(
956 self.testdata_dir, 'testkey_with_passwd')
957 common.OPTIONS.key_passwords = {
958 common.OPTIONS.package_key : 'foo',
959 }
960 payload_signer = PayloadSigner()
961 self.assertEqual('openssl', payload_signer.signer)
962
963 def test_init_withExternalSigner(self):
964 common.OPTIONS.payload_signer = 'abc'
965 common.OPTIONS.payload_signer_args = ['arg1', 'arg2']
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700966 common.OPTIONS.payload_signer_maximum_signature_size = '512'
Tao Baofabe0832018-01-17 15:52:28 -0800967 payload_signer = PayloadSigner()
968 self.assertEqual('abc', payload_signer.signer)
969 self.assertEqual(['arg1', 'arg2'], payload_signer.signer_args)
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700970 self.assertEqual(512, payload_signer.maximum_signature_size)
xunchang376cc7c2019-04-08 23:04:58 -0700971
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700972 @test_utils.SkipIfExternalToolsUnavailable()
973 def test_GetMaximumSignatureSizeInBytes_512Bytes(self):
xunchang376cc7c2019-04-08 23:04:58 -0700974 signing_key = os.path.join(self.testdata_dir, 'testkey_RSA4096.key')
Tao Bao82490d32019-04-09 00:12:30 -0700975 # pylint: disable=protected-access
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700976 signature_size = PayloadSigner._GetMaximumSignatureSizeInBytes(signing_key)
977 self.assertEqual(512, signature_size)
Tao Baofabe0832018-01-17 15:52:28 -0800978
Tianjie Xu21e6deb2019-10-07 18:01:00 -0700979 @test_utils.SkipIfExternalToolsUnavailable()
980 def test_GetMaximumSignatureSizeInBytes_ECKey(self):
981 signing_key = os.path.join(self.testdata_dir, 'testkey_EC.key')
982 # pylint: disable=protected-access
983 signature_size = PayloadSigner._GetMaximumSignatureSizeInBytes(signing_key)
984 self.assertEqual(72, signature_size)
985
986 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofabe0832018-01-17 15:52:28 -0800987 def test_Sign(self):
988 payload_signer = PayloadSigner()
989 input_file = os.path.join(self.testdata_dir, self.SIGFILE)
990 signed_file = payload_signer.Sign(input_file)
991
992 verify_file = os.path.join(self.testdata_dir, self.SIGNED_SIGFILE)
993 self._assertFilesEqual(verify_file, signed_file)
994
995 def test_Sign_withExternalSigner_openssl(self):
996 """Uses openssl as the external payload signer."""
997 common.OPTIONS.payload_signer = 'openssl'
998 common.OPTIONS.payload_signer_args = [
999 'pkeyutl', '-sign', '-keyform', 'DER', '-inkey',
1000 os.path.join(self.testdata_dir, 'testkey.pk8'),
1001 '-pkeyopt', 'digest:sha256']
1002 payload_signer = PayloadSigner()
1003 input_file = os.path.join(self.testdata_dir, self.SIGFILE)
1004 signed_file = payload_signer.Sign(input_file)
1005
1006 verify_file = os.path.join(self.testdata_dir, self.SIGNED_SIGFILE)
1007 self._assertFilesEqual(verify_file, signed_file)
1008
1009 def test_Sign_withExternalSigner_script(self):
1010 """Uses testdata/payload_signer.sh as the external payload signer."""
1011 common.OPTIONS.payload_signer = os.path.join(
1012 self.testdata_dir, 'payload_signer.sh')
Tao Bao30e31142019-04-09 00:12:30 -07001013 os.chmod(common.OPTIONS.payload_signer, 0o700)
Tao Baofabe0832018-01-17 15:52:28 -08001014 common.OPTIONS.payload_signer_args = [
1015 os.path.join(self.testdata_dir, 'testkey.pk8')]
1016 payload_signer = PayloadSigner()
1017 input_file = os.path.join(self.testdata_dir, self.SIGFILE)
1018 signed_file = payload_signer.Sign(input_file)
1019
1020 verify_file = os.path.join(self.testdata_dir, self.SIGNED_SIGFILE)
1021 self._assertFilesEqual(verify_file, signed_file)
Tao Baoc7b403a2018-01-30 18:19:04 -08001022
1023
Tao Bao65b94e92018-10-11 21:57:26 -07001024class PayloadTest(test_utils.ReleaseToolsTestCase):
Tao Baoc7b403a2018-01-30 18:19:04 -08001025
1026 def setUp(self):
Tao Bao04e1f012018-02-04 12:13:35 -08001027 self.testdata_dir = test_utils.get_testdata_dir()
Tao Baoc7b403a2018-01-30 18:19:04 -08001028 self.assertTrue(os.path.exists(self.testdata_dir))
1029
1030 common.OPTIONS.wipe_user_data = False
1031 common.OPTIONS.payload_signer = None
1032 common.OPTIONS.payload_signer_args = None
1033 common.OPTIONS.package_key = os.path.join(self.testdata_dir, 'testkey')
1034 common.OPTIONS.key_passwords = {
1035 common.OPTIONS.package_key : None,
1036 }
1037
Tao Baoc7b403a2018-01-30 18:19:04 -08001038 @staticmethod
Tao Baof7140c02018-01-30 17:09:24 -08001039 def _create_payload_full(secondary=False):
1040 target_file = construct_target_files(secondary)
Tao Bao667ff572018-02-10 00:02:40 -08001041 payload = Payload(secondary)
Tao Baoc7b403a2018-01-30 18:19:04 -08001042 payload.Generate(target_file)
1043 return payload
1044
Tao Baof7140c02018-01-30 17:09:24 -08001045 @staticmethod
1046 def _create_payload_incremental():
1047 target_file = construct_target_files()
1048 source_file = construct_target_files()
Tao Baoc7b403a2018-01-30 18:19:04 -08001049 payload = Payload()
1050 payload.Generate(target_file, source_file)
1051 return payload
1052
Tao Bao82490d32019-04-09 00:12:30 -07001053 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoc7b403a2018-01-30 18:19:04 -08001054 def test_Generate_full(self):
1055 payload = self._create_payload_full()
1056 self.assertTrue(os.path.exists(payload.payload_file))
1057
Tao Bao82490d32019-04-09 00:12:30 -07001058 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoc7b403a2018-01-30 18:19:04 -08001059 def test_Generate_incremental(self):
1060 payload = self._create_payload_incremental()
1061 self.assertTrue(os.path.exists(payload.payload_file))
1062
Tao Bao82490d32019-04-09 00:12:30 -07001063 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoc7b403a2018-01-30 18:19:04 -08001064 def test_Generate_additionalArgs(self):
Tao Baof7140c02018-01-30 17:09:24 -08001065 target_file = construct_target_files()
1066 source_file = construct_target_files()
Tao Baoc7b403a2018-01-30 18:19:04 -08001067 payload = Payload()
1068 # This should work the same as calling payload.Generate(target_file,
1069 # source_file).
1070 payload.Generate(
1071 target_file, additional_args=["--source_image", source_file])
1072 self.assertTrue(os.path.exists(payload.payload_file))
1073
Tao Bao82490d32019-04-09 00:12:30 -07001074 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoc7b403a2018-01-30 18:19:04 -08001075 def test_Generate_invalidInput(self):
Tao Baof7140c02018-01-30 17:09:24 -08001076 target_file = construct_target_files()
Tao Baoc7b403a2018-01-30 18:19:04 -08001077 common.ZipDelete(target_file, 'IMAGES/vendor.img')
1078 payload = Payload()
Tao Baobec89c12018-10-15 11:53:28 -07001079 self.assertRaises(common.ExternalError, payload.Generate, target_file)
Tao Baoc7b403a2018-01-30 18:19:04 -08001080
Tao Bao82490d32019-04-09 00:12:30 -07001081 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoc7b403a2018-01-30 18:19:04 -08001082 def test_Sign_full(self):
1083 payload = self._create_payload_full()
1084 payload.Sign(PayloadSigner())
1085
1086 output_file = common.MakeTempFile(suffix='.zip')
1087 with zipfile.ZipFile(output_file, 'w') as output_zip:
1088 payload.WriteToZip(output_zip)
1089
1090 import check_ota_package_signature
1091 check_ota_package_signature.VerifyAbOtaPayload(
1092 os.path.join(self.testdata_dir, 'testkey.x509.pem'),
1093 output_file)
1094
Tao Bao82490d32019-04-09 00:12:30 -07001095 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoc7b403a2018-01-30 18:19:04 -08001096 def test_Sign_incremental(self):
1097 payload = self._create_payload_incremental()
1098 payload.Sign(PayloadSigner())
1099
1100 output_file = common.MakeTempFile(suffix='.zip')
1101 with zipfile.ZipFile(output_file, 'w') as output_zip:
1102 payload.WriteToZip(output_zip)
1103
1104 import check_ota_package_signature
1105 check_ota_package_signature.VerifyAbOtaPayload(
1106 os.path.join(self.testdata_dir, 'testkey.x509.pem'),
1107 output_file)
1108
Tao Bao82490d32019-04-09 00:12:30 -07001109 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoc7b403a2018-01-30 18:19:04 -08001110 def test_Sign_withDataWipe(self):
1111 common.OPTIONS.wipe_user_data = True
1112 payload = self._create_payload_full()
1113 payload.Sign(PayloadSigner())
1114
1115 with open(payload.payload_properties) as properties_fp:
1116 self.assertIn("POWERWASH=1", properties_fp.read())
1117
Tao Bao82490d32019-04-09 00:12:30 -07001118 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao667ff572018-02-10 00:02:40 -08001119 def test_Sign_secondary(self):
1120 payload = self._create_payload_full(secondary=True)
1121 payload.Sign(PayloadSigner())
1122
1123 with open(payload.payload_properties) as properties_fp:
1124 self.assertIn("SWITCH_SLOT_ON_REBOOT=0", properties_fp.read())
1125
Tao Bao82490d32019-04-09 00:12:30 -07001126 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoc7b403a2018-01-30 18:19:04 -08001127 def test_Sign_badSigner(self):
1128 """Tests that signing failure can be captured."""
1129 payload = self._create_payload_full()
1130 payload_signer = PayloadSigner()
1131 payload_signer.signer_args.append('bad-option')
Tao Baobec89c12018-10-15 11:53:28 -07001132 self.assertRaises(common.ExternalError, payload.Sign, payload_signer)
Tao Baoc7b403a2018-01-30 18:19:04 -08001133
Tao Bao82490d32019-04-09 00:12:30 -07001134 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoc7b403a2018-01-30 18:19:04 -08001135 def test_WriteToZip(self):
1136 payload = self._create_payload_full()
1137 payload.Sign(PayloadSigner())
1138
1139 output_file = common.MakeTempFile(suffix='.zip')
1140 with zipfile.ZipFile(output_file, 'w') as output_zip:
1141 payload.WriteToZip(output_zip)
1142
1143 with zipfile.ZipFile(output_file) as verify_zip:
1144 # First make sure we have the essential entries.
1145 namelist = verify_zip.namelist()
1146 self.assertIn(Payload.PAYLOAD_BIN, namelist)
1147 self.assertIn(Payload.PAYLOAD_PROPERTIES_TXT, namelist)
1148
1149 # Then assert these entries are stored.
1150 for entry_info in verify_zip.infolist():
1151 if entry_info.filename not in (Payload.PAYLOAD_BIN,
1152 Payload.PAYLOAD_PROPERTIES_TXT):
1153 continue
1154 self.assertEqual(zipfile.ZIP_STORED, entry_info.compress_type)
1155
Tao Bao82490d32019-04-09 00:12:30 -07001156 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoc7b403a2018-01-30 18:19:04 -08001157 def test_WriteToZip_unsignedPayload(self):
1158 """Unsigned payloads should not be allowed to be written to zip."""
1159 payload = self._create_payload_full()
1160
1161 output_file = common.MakeTempFile(suffix='.zip')
1162 with zipfile.ZipFile(output_file, 'w') as output_zip:
1163 self.assertRaises(AssertionError, payload.WriteToZip, output_zip)
1164
1165 # Also test with incremental payload.
1166 payload = self._create_payload_incremental()
1167
1168 output_file = common.MakeTempFile(suffix='.zip')
1169 with zipfile.ZipFile(output_file, 'w') as output_zip:
1170 self.assertRaises(AssertionError, payload.WriteToZip, output_zip)
Tao Baof7140c02018-01-30 17:09:24 -08001171
Tao Bao82490d32019-04-09 00:12:30 -07001172 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof7140c02018-01-30 17:09:24 -08001173 def test_WriteToZip_secondary(self):
1174 payload = self._create_payload_full(secondary=True)
1175 payload.Sign(PayloadSigner())
1176
1177 output_file = common.MakeTempFile(suffix='.zip')
1178 with zipfile.ZipFile(output_file, 'w') as output_zip:
Tao Bao667ff572018-02-10 00:02:40 -08001179 payload.WriteToZip(output_zip)
Tao Baof7140c02018-01-30 17:09:24 -08001180
1181 with zipfile.ZipFile(output_file) as verify_zip:
1182 # First make sure we have the essential entries.
1183 namelist = verify_zip.namelist()
1184 self.assertIn(Payload.SECONDARY_PAYLOAD_BIN, namelist)
1185 self.assertIn(Payload.SECONDARY_PAYLOAD_PROPERTIES_TXT, namelist)
1186
1187 # Then assert these entries are stored.
1188 for entry_info in verify_zip.infolist():
1189 if entry_info.filename not in (
1190 Payload.SECONDARY_PAYLOAD_BIN,
1191 Payload.SECONDARY_PAYLOAD_PROPERTIES_TXT):
1192 continue
1193 self.assertEqual(zipfile.ZIP_STORED, entry_info.compress_type)
Tianjie Xu9afb2212020-05-10 21:48:15 +00001194
1195
1196class RuntimeFingerprintTest(test_utils.ReleaseToolsTestCase):
1197 MISC_INFO = [
1198 'recovery_api_version=3',
1199 'fstab_version=2',
1200 'recovery_as_boot=true',
1201 ]
1202
1203 BUILD_PROP = [
1204 'ro.build.version.release=version-release',
1205 'ro.build.id=build-id',
1206 'ro.build.version.incremental=version-incremental',
1207 'ro.build.type=build-type',
1208 'ro.build.tags=build-tags',
Tianjied6867162020-05-10 14:30:13 -07001209 'ro.build.version.sdk=30',
1210 'ro.build.version.security_patch=2020',
Tianjiea2076132020-08-19 17:25:32 -07001211 'ro.build.date.utc=12345678',
1212 'ro.system.build.version.release=version-release',
1213 'ro.system.build.id=build-id',
1214 'ro.system.build.version.incremental=version-incremental',
1215 'ro.system.build.type=build-type',
1216 'ro.system.build.tags=build-tags',
1217 'ro.system.build.version.sdk=30',
1218 'ro.system.build.version.security_patch=2020',
1219 'ro.system.build.date.utc=12345678',
1220 'ro.product.system.brand=generic',
1221 'ro.product.system.name=generic',
1222 'ro.product.system.device=generic',
Tianjie Xu9afb2212020-05-10 21:48:15 +00001223 ]
1224
1225 VENDOR_BUILD_PROP = [
Tianjiea2076132020-08-19 17:25:32 -07001226 'ro.vendor.build.version.release=version-release',
1227 'ro.vendor.build.id=build-id',
1228 'ro.vendor.build.version.incremental=version-incremental',
1229 'ro.vendor.build.type=build-type',
1230 'ro.vendor.build.tags=build-tags',
1231 'ro.vendor.build.version.sdk=30',
1232 'ro.vendor.build.version.security_patch=2020',
1233 'ro.vendor.build.date.utc=12345678',
Tianjie Xu9afb2212020-05-10 21:48:15 +00001234 'ro.product.vendor.brand=vendor-product-brand',
1235 'ro.product.vendor.name=vendor-product-name',
1236 'ro.product.vendor.device=vendor-product-device'
1237 ]
1238
1239 def setUp(self):
1240 common.OPTIONS.oem_dicts = None
1241 self.test_dir = common.MakeTempDir()
Tianjied6867162020-05-10 14:30:13 -07001242 self.writeFiles({'META/misc_info.txt': '\n'.join(self.MISC_INFO)},
1243 self.test_dir)
Tianjie Xu9afb2212020-05-10 21:48:15 +00001244
Tianjied6867162020-05-10 14:30:13 -07001245 def writeFiles(self, contents_dict, out_dir):
Tianjie Xu9afb2212020-05-10 21:48:15 +00001246 for path, content in contents_dict.items():
Tianjied6867162020-05-10 14:30:13 -07001247 abs_path = os.path.join(out_dir, path)
Tianjie Xu9afb2212020-05-10 21:48:15 +00001248 dir_name = os.path.dirname(abs_path)
1249 if not os.path.exists(dir_name):
1250 os.makedirs(dir_name)
1251 with open(abs_path, 'w') as f:
1252 f.write(content)
1253
1254 @staticmethod
1255 def constructFingerprint(prefix):
1256 return '{}:version-release/build-id/version-incremental:' \
1257 'build-type/build-tags'.format(prefix)
1258
1259 def test_CalculatePossibleFingerprints_no_dynamic_fingerprint(self):
1260 build_prop = copy.deepcopy(self.BUILD_PROP)
1261 build_prop.extend([
1262 'ro.product.brand=product-brand',
1263 'ro.product.name=product-name',
1264 'ro.product.device=product-device',
1265 ])
1266 self.writeFiles({
1267 'SYSTEM/build.prop': '\n'.join(build_prop),
1268 'VENDOR/build.prop': '\n'.join(self.VENDOR_BUILD_PROP),
Tianjied6867162020-05-10 14:30:13 -07001269 }, self.test_dir)
Tianjie Xu9afb2212020-05-10 21:48:15 +00001270
Tianjied6867162020-05-10 14:30:13 -07001271 build_info = common.BuildInfo(common.LoadInfoDict(self.test_dir))
1272 expected = ({'product-device'},
1273 {self.constructFingerprint(
1274 'product-brand/product-name/product-device')})
1275 self.assertEqual(expected,
1276 CalculateRuntimeDevicesAndFingerprints(build_info, {}))
Tianjie Xu9afb2212020-05-10 21:48:15 +00001277
1278 def test_CalculatePossibleFingerprints_single_override(self):
1279 vendor_build_prop = copy.deepcopy(self.VENDOR_BUILD_PROP)
1280 vendor_build_prop.extend([
1281 'import /vendor/etc/build_${ro.boot.sku_name}.prop',
1282 ])
1283 self.writeFiles({
1284 'SYSTEM/build.prop': '\n'.join(self.BUILD_PROP),
1285 'VENDOR/build.prop': '\n'.join(vendor_build_prop),
1286 'VENDOR/etc/build_std.prop':
1287 'ro.product.vendor.name=vendor-product-std',
1288 'VENDOR/etc/build_pro.prop':
1289 'ro.product.vendor.name=vendor-product-pro',
Tianjied6867162020-05-10 14:30:13 -07001290 }, self.test_dir)
Tianjie Xu9afb2212020-05-10 21:48:15 +00001291
Tianjied6867162020-05-10 14:30:13 -07001292 build_info = common.BuildInfo(common.LoadInfoDict(self.test_dir))
1293 boot_variable_values = {'ro.boot.sku_name': ['std', 'pro']}
1294
1295 expected = ({'vendor-product-device'}, {
Tianjie Xu9afb2212020-05-10 21:48:15 +00001296 self.constructFingerprint(
1297 'vendor-product-brand/vendor-product-name/vendor-product-device'),
1298 self.constructFingerprint(
1299 'vendor-product-brand/vendor-product-std/vendor-product-device'),
1300 self.constructFingerprint(
1301 'vendor-product-brand/vendor-product-pro/vendor-product-device'),
Tianjied6867162020-05-10 14:30:13 -07001302 })
1303 self.assertEqual(
1304 expected, CalculateRuntimeDevicesAndFingerprints(
1305 build_info, boot_variable_values))
Tianjie Xu9afb2212020-05-10 21:48:15 +00001306
1307 def test_CalculatePossibleFingerprints_multiple_overrides(self):
1308 vendor_build_prop = copy.deepcopy(self.VENDOR_BUILD_PROP)
1309 vendor_build_prop.extend([
1310 'import /vendor/etc/build_${ro.boot.sku_name}.prop',
1311 'import /vendor/etc/build_${ro.boot.device_name}.prop',
1312 ])
1313 self.writeFiles({
1314 'SYSTEM/build.prop': '\n'.join(self.BUILD_PROP),
1315 'VENDOR/build.prop': '\n'.join(vendor_build_prop),
1316 'VENDOR/etc/build_std.prop':
1317 'ro.product.vendor.name=vendor-product-std',
1318 'VENDOR/etc/build_product1.prop':
1319 'ro.product.vendor.device=vendor-device-product1',
1320 'VENDOR/etc/build_pro.prop':
1321 'ro.product.vendor.name=vendor-product-pro',
1322 'VENDOR/etc/build_product2.prop':
1323 'ro.product.vendor.device=vendor-device-product2',
Tianjied6867162020-05-10 14:30:13 -07001324 }, self.test_dir)
1325
1326 build_info = common.BuildInfo(common.LoadInfoDict(self.test_dir))
1327 boot_variable_values = {
Tianjie Xu9afb2212020-05-10 21:48:15 +00001328 'ro.boot.sku_name': ['std', 'pro'],
1329 'ro.boot.device_name': ['product1', 'product2'],
1330 }
1331
Tianjied6867162020-05-10 14:30:13 -07001332 expected_devices = {'vendor-product-device', 'vendor-device-product1',
1333 'vendor-device-product2'}
1334 expected_fingerprints = {
Tianjie Xu9afb2212020-05-10 21:48:15 +00001335 self.constructFingerprint(
1336 'vendor-product-brand/vendor-product-name/vendor-product-device'),
1337 self.constructFingerprint(
1338 'vendor-product-brand/vendor-product-std/vendor-device-product1'),
1339 self.constructFingerprint(
1340 'vendor-product-brand/vendor-product-pro/vendor-device-product1'),
1341 self.constructFingerprint(
1342 'vendor-product-brand/vendor-product-std/vendor-device-product2'),
1343 self.constructFingerprint(
Tianjied6867162020-05-10 14:30:13 -07001344 'vendor-product-brand/vendor-product-pro/vendor-device-product2')
1345 }
1346 self.assertEqual((expected_devices, expected_fingerprints),
1347 CalculateRuntimeDevicesAndFingerprints(
1348 build_info, boot_variable_values))
1349
1350 def test_GetPackageMetadata_full_package(self):
1351 vendor_build_prop = copy.deepcopy(self.VENDOR_BUILD_PROP)
1352 vendor_build_prop.extend([
1353 'import /vendor/etc/build_${ro.boot.sku_name}.prop',
1354 ])
1355 self.writeFiles({
1356 'SYSTEM/build.prop': '\n'.join(self.BUILD_PROP),
1357 'VENDOR/build.prop': '\n'.join(vendor_build_prop),
1358 'VENDOR/etc/build_std.prop':
1359 'ro.product.vendor.name=vendor-product-std',
1360 'VENDOR/etc/build_pro.prop':
1361 'ro.product.vendor.name=vendor-product-pro',
1362 }, self.test_dir)
1363
1364 common.OPTIONS.boot_variable_file = common.MakeTempFile()
1365 with open(common.OPTIONS.boot_variable_file, 'w') as f:
1366 f.write('ro.boot.sku_name=std,pro')
1367
1368 build_info = common.BuildInfo(common.LoadInfoDict(self.test_dir))
Tianjiea2076132020-08-19 17:25:32 -07001369 metadata_dict = BuildLegacyOtaMetadata(GetPackageMetadata(build_info))
1370 self.assertEqual('vendor-product-device', metadata_dict['pre-device'])
Tianjied6867162020-05-10 14:30:13 -07001371 fingerprints = [
1372 self.constructFingerprint(
1373 'vendor-product-brand/vendor-product-name/vendor-product-device'),
1374 self.constructFingerprint(
1375 'vendor-product-brand/vendor-product-pro/vendor-product-device'),
1376 self.constructFingerprint(
1377 'vendor-product-brand/vendor-product-std/vendor-product-device'),
1378 ]
Tianjiea2076132020-08-19 17:25:32 -07001379 self.assertEqual('|'.join(fingerprints), metadata_dict['post-build'])
1380
1381 def CheckMetadataEqual(self, metadata_dict, metadata_proto):
1382 post_build = metadata_proto.postcondition
1383 self.assertEqual('|'.join(post_build.build),
1384 metadata_dict['post-build'])
1385 self.assertEqual(post_build.build_incremental,
1386 metadata_dict['post-build-incremental'])
1387 self.assertEqual(post_build.sdk_level,
1388 metadata_dict['post-sdk-level'])
1389 self.assertEqual(post_build.security_patch_level,
1390 metadata_dict['post-security-patch-level'])
1391
1392 if metadata_proto.type == ota_metadata_pb2.OtaMetadata.AB:
1393 ota_type = 'AB'
1394 elif metadata_proto.type == ota_metadata_pb2.OtaMetadata.BLOCK:
1395 ota_type = 'BLOCK'
1396 else:
1397 ota_type = ''
1398 self.assertEqual(ota_type, metadata_dict['ota-type'])
1399 self.assertEqual(metadata_proto.wipe,
1400 metadata_dict.get('ota-wipe') == 'yes')
1401 self.assertEqual(metadata_proto.required_cache,
1402 int(metadata_dict.get('ota-required-cache', 0)))
1403 self.assertEqual(metadata_proto.retrofit_dynamic_partitions,
1404 metadata_dict.get(
Tianjie2bb14862020-08-28 16:24:34 -07001405 'ota-retrofit-dynamic-partitions') == 'yes')
Tianjied6867162020-05-10 14:30:13 -07001406
1407 def test_GetPackageMetadata_incremental_package(self):
1408 vendor_build_prop = copy.deepcopy(self.VENDOR_BUILD_PROP)
1409 vendor_build_prop.extend([
1410 'import /vendor/etc/build_${ro.boot.sku_name}.prop',
1411 ])
1412 self.writeFiles({
1413 'SYSTEM/build.prop': '\n'.join(self.BUILD_PROP),
1414 'VENDOR/build.prop': '\n'.join(vendor_build_prop),
1415 'VENDOR/etc/build_std.prop':
1416 'ro.product.vendor.device=vendor-device-std',
1417 'VENDOR/etc/build_pro.prop':
1418 'ro.product.vendor.device=vendor-device-pro',
1419 }, self.test_dir)
1420
1421 common.OPTIONS.boot_variable_file = common.MakeTempFile()
1422 with open(common.OPTIONS.boot_variable_file, 'w') as f:
1423 f.write('ro.boot.sku_name=std,pro')
1424
1425 source_dir = common.MakeTempDir()
1426 source_build_prop = [
1427 'ro.build.version.release=source-version-release',
1428 'ro.build.id=source-build-id',
1429 'ro.build.version.incremental=source-version-incremental',
1430 'ro.build.type=build-type',
1431 'ro.build.tags=build-tags',
1432 'ro.build.version.sdk=29',
1433 'ro.build.version.security_patch=2020',
Tianjiea2076132020-08-19 17:25:32 -07001434 'ro.build.date.utc=12340000',
1435 'ro.system.build.version.release=source-version-release',
1436 'ro.system.build.id=source-build-id',
1437 'ro.system.build.version.incremental=source-version-incremental',
1438 'ro.system.build.type=build-type',
1439 'ro.system.build.tags=build-tags',
1440 'ro.system.build.version.sdk=29',
1441 'ro.system.build.version.security_patch=2020',
1442 'ro.system.build.date.utc=12340000',
1443 'ro.product.system.brand=generic',
1444 'ro.product.system.name=generic',
1445 'ro.product.system.device=generic',
Tianjied6867162020-05-10 14:30:13 -07001446 ]
1447 self.writeFiles({
1448 'META/misc_info.txt': '\n'.join(self.MISC_INFO),
1449 'SYSTEM/build.prop': '\n'.join(source_build_prop),
1450 'VENDOR/build.prop': '\n'.join(vendor_build_prop),
1451 'VENDOR/etc/build_std.prop':
1452 'ro.product.vendor.device=vendor-device-std',
1453 'VENDOR/etc/build_pro.prop':
1454 'ro.product.vendor.device=vendor-device-pro',
1455 }, source_dir)
1456 common.OPTIONS.incremental_source = source_dir
1457
1458 target_info = common.BuildInfo(common.LoadInfoDict(self.test_dir))
1459 source_info = common.BuildInfo(common.LoadInfoDict(source_dir))
1460
Tianjiea2076132020-08-19 17:25:32 -07001461 metadata_proto = GetPackageMetadata(target_info, source_info)
1462 metadata_dict = BuildLegacyOtaMetadata(metadata_proto)
Tianjied6867162020-05-10 14:30:13 -07001463 self.assertEqual(
1464 'vendor-device-pro|vendor-device-std|vendor-product-device',
Tianjiea2076132020-08-19 17:25:32 -07001465 metadata_dict['pre-device'])
Tianjie2bb14862020-08-28 16:24:34 -07001466 source_suffix = ':source-version-release/source-build-id/' \
1467 'source-version-incremental:build-type/build-tags'
Tianjied6867162020-05-10 14:30:13 -07001468 pre_fingerprints = [
1469 'vendor-product-brand/vendor-product-name/vendor-device-pro'
Tianjie2bb14862020-08-28 16:24:34 -07001470 '{}'.format(source_suffix),
Tianjied6867162020-05-10 14:30:13 -07001471 'vendor-product-brand/vendor-product-name/vendor-device-std'
Tianjie2bb14862020-08-28 16:24:34 -07001472 '{}'.format(source_suffix),
Tianjied6867162020-05-10 14:30:13 -07001473 'vendor-product-brand/vendor-product-name/vendor-product-device'
Tianjie2bb14862020-08-28 16:24:34 -07001474 '{}'.format(source_suffix),
Tianjied6867162020-05-10 14:30:13 -07001475 ]
Tianjiea2076132020-08-19 17:25:32 -07001476 self.assertEqual('|'.join(pre_fingerprints), metadata_dict['pre-build'])
Tianjied6867162020-05-10 14:30:13 -07001477
1478 post_fingerprints = [
1479 self.constructFingerprint(
1480 'vendor-product-brand/vendor-product-name/vendor-device-pro'),
1481 self.constructFingerprint(
1482 'vendor-product-brand/vendor-product-name/vendor-device-std'),
1483 self.constructFingerprint(
1484 'vendor-product-brand/vendor-product-name/vendor-product-device'),
1485 ]
Tianjiea2076132020-08-19 17:25:32 -07001486 self.assertEqual('|'.join(post_fingerprints), metadata_dict['post-build'])
1487
1488 self.CheckMetadataEqual(metadata_dict, metadata_proto)
Tianjie2bb14862020-08-28 16:24:34 -07001489
1490 pre_partition_states = metadata_proto.precondition.partition_state
1491 self.assertEqual(2, len(pre_partition_states))
1492 self.assertEqual('system', pre_partition_states[0].partition_name)
1493 self.assertEqual(['generic'], pre_partition_states[0].device)
1494 self.assertEqual(['generic/generic/generic{}'.format(source_suffix)],
1495 pre_partition_states[0].build)
1496
1497 self.assertEqual('vendor', pre_partition_states[1].partition_name)
1498 self.assertEqual(['vendor-device-pro', 'vendor-device-std',
1499 'vendor-product-device'], pre_partition_states[1].device)
1500 vendor_fingerprints = post_fingerprints
1501 self.assertEqual(vendor_fingerprints, pre_partition_states[1].build)
1502
1503 post_partition_states = metadata_proto.postcondition.partition_state
1504 self.assertEqual(2, len(post_partition_states))
1505 self.assertEqual('system', post_partition_states[0].partition_name)
1506 self.assertEqual(['generic'], post_partition_states[0].device)
1507 self.assertEqual([self.constructFingerprint('generic/generic/generic')],
1508 post_partition_states[0].build)
1509
1510 self.assertEqual('vendor', post_partition_states[1].partition_name)
1511 self.assertEqual(['vendor-device-pro', 'vendor-device-std',
1512 'vendor-product-device'], post_partition_states[1].device)
1513 self.assertEqual(vendor_fingerprints, post_partition_states[1].build)