blob: f9732632f1fe5ee9857356a307b4e215ed52ca5e [file] [log] [blame]
Dan Albert8e0178d2015-01-27 15:53:15 -08001#
2# Copyright (C) 2015 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#
Tao Baofc7e0e02018-02-13 13:54:02 -080016
Tao Baoa57ab9f2018-08-24 12:08:38 -070017import copy
Daniel Normand3351562020-10-29 12:33:11 -070018import json
Dan Albert8e0178d2015-01-27 15:53:15 -080019import os
Tao Bao17e4e612018-02-16 17:12:54 -080020import subprocess
Dan Albert8e0178d2015-01-27 15:53:15 -080021import tempfile
22import time
Tianjie20dd8f22020-04-19 15:51:16 -070023import unittest
Dan Albert8e0178d2015-01-27 15:53:15 -080024import zipfile
Tao Bao31b08072017-11-08 15:50:59 -080025from hashlib import sha1
26
Dan Albert8e0178d2015-01-27 15:53:15 -080027import common
Tao Bao04e1f012018-02-04 12:13:35 -080028import test_utils
Tianjie Xu9c384d22017-06-20 17:00:55 -070029import validate_target_files
Tianjie Xu41976c72019-07-03 13:57:01 -070030from images import EmptyImage, DataImage
Tao Baofc7e0e02018-02-13 13:54:02 -080031from rangelib import RangeSet
Dan Albert8e0178d2015-01-27 15:53:15 -080032
Tao Bao04e1f012018-02-04 12:13:35 -080033
Tao Bao31b08072017-11-08 15:50:59 -080034KiB = 1024
35MiB = 1024 * KiB
36GiB = 1024 * MiB
Dan Albert8e0178d2015-01-27 15:53:15 -080037
Tao Bao1c830bf2017-12-25 10:43:47 -080038
Tao Baof3282b42015-04-01 11:21:55 -070039def get_2gb_string():
Tao Bao31b08072017-11-08 15:50:59 -080040 size = int(2 * GiB + 1)
41 block_size = 4 * KiB
42 step_size = 4 * MiB
43 # Generate a long string with holes, e.g. 'xyz\x00abc\x00...'.
44 for _ in range(0, size, step_size):
45 yield os.urandom(block_size)
Tao Baoc1a1ec32019-06-18 16:29:37 -070046 yield b'\0' * (step_size - block_size)
Tao Baof3282b42015-04-01 11:21:55 -070047
Dan Albert8e0178d2015-01-27 15:53:15 -080048
Tao Bao1c320f82019-10-04 23:25:12 -070049class BuildInfoTest(test_utils.ReleaseToolsTestCase):
50
Tianjiefdda51d2021-05-05 14:46:35 -070051 TEST_INFO_FINGERPRINT_DICT = {
52 'build.prop': common.PartitionBuildProps.FromDictionary(
53 'system', {
54 'ro.product.brand': 'product-brand',
55 'ro.product.name': 'product-name',
56 'ro.product.device': 'product-device',
57 'ro.build.version.release': 'version-release',
58 'ro.build.id': 'build-id',
59 'ro.build.version.incremental': 'version-incremental',
60 'ro.build.type': 'build-type',
61 'ro.build.tags': 'build-tags',
62 'ro.build.version.sdk': 30,
63 }
64 ),
65 }
66
Tao Bao1c320f82019-10-04 23:25:12 -070067 TEST_INFO_DICT = {
Tianjie Xu0fde41e2020-05-09 05:24:18 +000068 'build.prop': common.PartitionBuildProps.FromDictionary(
69 'system', {
70 'ro.product.device': 'product-device',
71 'ro.product.name': 'product-name',
72 'ro.build.fingerprint': 'build-fingerprint',
73 'ro.build.foo': 'build-foo'}
74 ),
75 'system.build.prop': common.PartitionBuildProps.FromDictionary(
76 'system', {
77 'ro.product.system.brand': 'product-brand',
78 'ro.product.system.name': 'product-name',
79 'ro.product.system.device': 'product-device',
80 'ro.system.build.version.release': 'version-release',
81 'ro.system.build.id': 'build-id',
82 'ro.system.build.version.incremental': 'version-incremental',
83 'ro.system.build.type': 'build-type',
84 'ro.system.build.tags': 'build-tags',
85 'ro.system.build.foo': 'build-foo'}
86 ),
87 'vendor.build.prop': common.PartitionBuildProps.FromDictionary(
88 'vendor', {
89 'ro.product.vendor.brand': 'vendor-product-brand',
90 'ro.product.vendor.name': 'vendor-product-name',
91 'ro.product.vendor.device': 'vendor-product-device',
92 'ro.vendor.build.version.release': 'vendor-version-release',
93 'ro.vendor.build.id': 'vendor-build-id',
94 'ro.vendor.build.version.incremental':
95 'vendor-version-incremental',
96 'ro.vendor.build.type': 'vendor-build-type',
97 'ro.vendor.build.tags': 'vendor-build-tags'}
98 ),
99 'property1': 'value1',
100 'property2': 4096,
Tao Bao1c320f82019-10-04 23:25:12 -0700101 }
102
103 TEST_INFO_DICT_USES_OEM_PROPS = {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000104 'build.prop': common.PartitionBuildProps.FromDictionary(
105 'system', {
106 'ro.product.name': 'product-name',
107 'ro.build.thumbprint': 'build-thumbprint',
108 'ro.build.bar': 'build-bar'}
109 ),
110 'vendor.build.prop': common.PartitionBuildProps.FromDictionary(
111 'vendor', {
112 'ro.vendor.build.fingerprint': 'vendor-build-fingerprint'}
113 ),
114 'property1': 'value1',
115 'property2': 4096,
116 'oem_fingerprint_properties': 'ro.product.device ro.product.brand',
Tao Bao1c320f82019-10-04 23:25:12 -0700117 }
118
119 TEST_OEM_DICTS = [
120 {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000121 'ro.product.brand': 'brand1',
122 'ro.product.device': 'device1',
Tao Bao1c320f82019-10-04 23:25:12 -0700123 },
124 {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000125 'ro.product.brand': 'brand2',
126 'ro.product.device': 'device2',
Tao Bao1c320f82019-10-04 23:25:12 -0700127 },
128 {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000129 'ro.product.brand': 'brand3',
130 'ro.product.device': 'device3',
Tao Bao1c320f82019-10-04 23:25:12 -0700131 },
132 ]
133
Steven Laver8e2086e2020-04-27 16:26:31 -0700134 TEST_INFO_DICT_PROPERTY_SOURCE_ORDER = {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000135 'build.prop': common.PartitionBuildProps.FromDictionary(
136 'system', {
137 'ro.build.fingerprint': 'build-fingerprint',
138 'ro.product.property_source_order':
139 'product,odm,vendor,system_ext,system'}
140 ),
141 'system.build.prop': common.PartitionBuildProps.FromDictionary(
142 'system', {
143 'ro.product.system.device': 'system-product-device'}
144 ),
145 'vendor.build.prop': common.PartitionBuildProps.FromDictionary(
146 'vendor', {
147 'ro.product.vendor.device': 'vendor-product-device'}
148 ),
Steven Laver8e2086e2020-04-27 16:26:31 -0700149 }
150
151 TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_10 = {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000152 'build.prop': common.PartitionBuildProps.FromDictionary(
153 'system', {
154 'ro.build.fingerprint': 'build-fingerprint',
155 'ro.product.property_source_order':
156 'product,product_services,odm,vendor,system',
157 'ro.build.version.release': '10',
158 'ro.build.version.codename': 'REL'}
159 ),
160 'system.build.prop': common.PartitionBuildProps.FromDictionary(
161 'system', {
162 'ro.product.system.device': 'system-product-device'}
163 ),
164 'vendor.build.prop': common.PartitionBuildProps.FromDictionary(
165 'vendor', {
166 'ro.product.vendor.device': 'vendor-product-device'}
167 ),
Steven Laver8e2086e2020-04-27 16:26:31 -0700168 }
169
170 TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_9 = {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000171 'build.prop': common.PartitionBuildProps.FromDictionary(
172 'system', {
173 'ro.product.device': 'product-device',
174 'ro.build.fingerprint': 'build-fingerprint',
175 'ro.build.version.release': '9',
176 'ro.build.version.codename': 'REL'}
177 ),
178 'system.build.prop': common.PartitionBuildProps.FromDictionary(
179 'system', {
180 'ro.product.system.device': 'system-product-device'}
181 ),
182 'vendor.build.prop': common.PartitionBuildProps.FromDictionary(
183 'vendor', {
184 'ro.product.vendor.device': 'vendor-product-device'}
185 ),
Steven Laver8e2086e2020-04-27 16:26:31 -0700186 }
187
Tao Bao1c320f82019-10-04 23:25:12 -0700188 def test_init(self):
189 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
190 self.assertEqual('product-device', target_info.device)
191 self.assertEqual('build-fingerprint', target_info.fingerprint)
192 self.assertFalse(target_info.is_ab)
193 self.assertIsNone(target_info.oem_props)
194
195 def test_init_with_oem_props(self):
196 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
197 self.TEST_OEM_DICTS)
198 self.assertEqual('device1', target_info.device)
199 self.assertEqual('brand1/product-name/device1:build-thumbprint',
200 target_info.fingerprint)
201
202 # Swap the order in oem_dicts, which would lead to different BuildInfo.
203 oem_dicts = copy.copy(self.TEST_OEM_DICTS)
204 oem_dicts[0], oem_dicts[2] = oem_dicts[2], oem_dicts[0]
205 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
206 oem_dicts)
207 self.assertEqual('device3', target_info.device)
208 self.assertEqual('brand3/product-name/device3:build-thumbprint',
209 target_info.fingerprint)
210
Tao Bao1c320f82019-10-04 23:25:12 -0700211 def test_init_badFingerprint(self):
212 info_dict = copy.deepcopy(self.TEST_INFO_DICT)
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000213 info_dict['build.prop'].build_props[
214 'ro.build.fingerprint'] = 'bad fingerprint'
Tao Bao1c320f82019-10-04 23:25:12 -0700215 self.assertRaises(ValueError, common.BuildInfo, info_dict, None)
216
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000217 info_dict['build.prop'].build_props[
218 'ro.build.fingerprint'] = 'bad\x80fingerprint'
Tao Bao1c320f82019-10-04 23:25:12 -0700219 self.assertRaises(ValueError, common.BuildInfo, info_dict, None)
220
Tianjiefdda51d2021-05-05 14:46:35 -0700221 def test_init_goodFingerprint(self):
222 info_dict = copy.deepcopy(self.TEST_INFO_FINGERPRINT_DICT)
223 build_info = common.BuildInfo(info_dict)
224 self.assertEqual(
225 'product-brand/product-name/product-device:version-release/build-id/'
226 'version-incremental:build-type/build-tags', build_info.fingerprint)
227
228 build_props = info_dict['build.prop'].build_props
229 del build_props['ro.build.id']
230 build_props['ro.build.legacy.id'] = 'legacy-build-id'
231 build_info = common.BuildInfo(info_dict, use_legacy_id=True)
232 self.assertEqual(
233 'product-brand/product-name/product-device:version-release/'
234 'legacy-build-id/version-incremental:build-type/build-tags',
235 build_info.fingerprint)
236
237 self.assertRaises(common.ExternalError, common.BuildInfo, info_dict, None,
238 False)
239
240 info_dict['avb_enable'] = 'true'
241 info_dict['vbmeta_digest'] = 'abcde12345'
242 build_info = common.BuildInfo(info_dict, use_legacy_id=False)
243 self.assertEqual(
244 'product-brand/product-name/product-device:version-release/'
245 'legacy-build-id.abcde123/version-incremental:build-type/build-tags',
246 build_info.fingerprint)
247
Tao Bao1c320f82019-10-04 23:25:12 -0700248 def test___getitem__(self):
249 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
250 self.assertEqual('value1', target_info['property1'])
251 self.assertEqual(4096, target_info['property2'])
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000252 self.assertEqual('build-foo',
253 target_info['build.prop'].GetProp('ro.build.foo'))
Tao Bao1c320f82019-10-04 23:25:12 -0700254
255 def test___getitem__with_oem_props(self):
256 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
257 self.TEST_OEM_DICTS)
258 self.assertEqual('value1', target_info['property1'])
259 self.assertEqual(4096, target_info['property2'])
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000260 self.assertIsNone(target_info['build.prop'].GetProp('ro.build.foo'))
Tao Bao1c320f82019-10-04 23:25:12 -0700261
262 def test___setitem__(self):
263 target_info = common.BuildInfo(copy.deepcopy(self.TEST_INFO_DICT), None)
264 self.assertEqual('value1', target_info['property1'])
265 target_info['property1'] = 'value2'
266 self.assertEqual('value2', target_info['property1'])
267
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000268 self.assertEqual('build-foo',
269 target_info['build.prop'].GetProp('ro.build.foo'))
270 target_info['build.prop'].build_props['ro.build.foo'] = 'build-bar'
271 self.assertEqual('build-bar',
272 target_info['build.prop'].GetProp('ro.build.foo'))
Tao Bao1c320f82019-10-04 23:25:12 -0700273
274 def test_get(self):
275 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
276 self.assertEqual('value1', target_info.get('property1'))
277 self.assertEqual(4096, target_info.get('property2'))
278 self.assertEqual(4096, target_info.get('property2', 1024))
279 self.assertEqual(1024, target_info.get('property-nonexistent', 1024))
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000280 self.assertEqual('build-foo',
281 target_info.get('build.prop').GetProp('ro.build.foo'))
Tao Bao1c320f82019-10-04 23:25:12 -0700282
283 def test_get_with_oem_props(self):
284 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
285 self.TEST_OEM_DICTS)
286 self.assertEqual('value1', target_info.get('property1'))
287 self.assertEqual(4096, target_info.get('property2'))
288 self.assertEqual(4096, target_info.get('property2', 1024))
289 self.assertEqual(1024, target_info.get('property-nonexistent', 1024))
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000290 self.assertIsNone(target_info.get('build.prop').GetProp('ro.build.foo'))
Tao Bao1c320f82019-10-04 23:25:12 -0700291
292 def test_items(self):
293 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
294 items = target_info.items()
295 self.assertIn(('property1', 'value1'), items)
296 self.assertIn(('property2', 4096), items)
297
298 def test_GetBuildProp(self):
299 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
300 self.assertEqual('build-foo', target_info.GetBuildProp('ro.build.foo'))
301 self.assertRaises(common.ExternalError, target_info.GetBuildProp,
302 'ro.build.nonexistent')
303
304 def test_GetBuildProp_with_oem_props(self):
305 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
306 self.TEST_OEM_DICTS)
307 self.assertEqual('build-bar', target_info.GetBuildProp('ro.build.bar'))
308 self.assertRaises(common.ExternalError, target_info.GetBuildProp,
309 'ro.build.nonexistent')
310
Daniel Normand5fe8622020-01-08 17:01:11 -0800311 def test_GetPartitionFingerprint(self):
Tao Bao1c320f82019-10-04 23:25:12 -0700312 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
Daniel Normand5fe8622020-01-08 17:01:11 -0800313 self.assertEqual(
314 target_info.GetPartitionFingerprint('vendor'),
315 'vendor-product-brand/vendor-product-name/vendor-product-device'
316 ':vendor-version-release/vendor-build-id/vendor-version-incremental'
317 ':vendor-build-type/vendor-build-tags')
Tao Bao1c320f82019-10-04 23:25:12 -0700318
Daniel Normand5fe8622020-01-08 17:01:11 -0800319 def test_GetPartitionFingerprint_system_other_uses_system(self):
Tao Bao1c320f82019-10-04 23:25:12 -0700320 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
Daniel Normand5fe8622020-01-08 17:01:11 -0800321 self.assertEqual(
322 target_info.GetPartitionFingerprint('system_other'),
323 target_info.GetPartitionFingerprint('system'))
Tao Bao1c320f82019-10-04 23:25:12 -0700324
Daniel Normand5fe8622020-01-08 17:01:11 -0800325 def test_GetPartitionFingerprint_uses_fingerprint_prop_if_available(self):
326 info_dict = copy.deepcopy(self.TEST_INFO_DICT)
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000327 info_dict['vendor.build.prop'].build_props[
328 'ro.vendor.build.fingerprint'] = 'vendor:fingerprint'
Daniel Normand5fe8622020-01-08 17:01:11 -0800329 target_info = common.BuildInfo(info_dict, None)
330 self.assertEqual(
331 target_info.GetPartitionFingerprint('vendor'),
332 'vendor:fingerprint')
Tao Bao1c320f82019-10-04 23:25:12 -0700333
334 def test_WriteMountOemScript(self):
335 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
336 self.TEST_OEM_DICTS)
337 script_writer = test_utils.MockScriptWriter()
338 target_info.WriteMountOemScript(script_writer)
339 self.assertEqual([('Mount', '/oem', None)], script_writer.lines)
340
341 def test_WriteDeviceAssertions(self):
342 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
343 script_writer = test_utils.MockScriptWriter()
344 target_info.WriteDeviceAssertions(script_writer, False)
345 self.assertEqual([('AssertDevice', 'product-device')], script_writer.lines)
346
347 def test_WriteDeviceAssertions_with_oem_props(self):
348 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
349 self.TEST_OEM_DICTS)
350 script_writer = test_utils.MockScriptWriter()
351 target_info.WriteDeviceAssertions(script_writer, False)
352 self.assertEqual(
353 [
354 ('AssertOemProperty', 'ro.product.device',
355 ['device1', 'device2', 'device3'], False),
356 ('AssertOemProperty', 'ro.product.brand',
357 ['brand1', 'brand2', 'brand3'], False),
358 ],
359 script_writer.lines)
360
Steven Laver8e2086e2020-04-27 16:26:31 -0700361 def test_ResolveRoProductProperty_FromVendor(self):
362 info_dict = copy.deepcopy(self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER)
363 info = common.BuildInfo(info_dict, None)
364 self.assertEqual('vendor-product-device',
365 info.GetBuildProp('ro.product.device'))
366
367 def test_ResolveRoProductProperty_FromSystem(self):
368 info_dict = copy.deepcopy(self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER)
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000369 del info_dict['vendor.build.prop'].build_props['ro.product.vendor.device']
Steven Laver8e2086e2020-04-27 16:26:31 -0700370 info = common.BuildInfo(info_dict, None)
371 self.assertEqual('system-product-device',
372 info.GetBuildProp('ro.product.device'))
373
374 def test_ResolveRoProductProperty_InvalidPropertySearchOrder(self):
375 info_dict = copy.deepcopy(self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER)
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000376 info_dict['build.prop'].build_props[
377 'ro.product.property_source_order'] = 'bad-source'
Steven Laver8e2086e2020-04-27 16:26:31 -0700378 with self.assertRaisesRegexp(common.ExternalError,
379 'Invalid ro.product.property_source_order'):
380 info = common.BuildInfo(info_dict, None)
381 info.GetBuildProp('ro.product.device')
382
383 def test_ResolveRoProductProperty_Android10PropertySearchOrder(self):
384 info_dict = copy.deepcopy(
385 self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_10)
386 info = common.BuildInfo(info_dict, None)
387 self.assertEqual('vendor-product-device',
388 info.GetBuildProp('ro.product.device'))
389
390 def test_ResolveRoProductProperty_Android9PropertySearchOrder(self):
391 info_dict = copy.deepcopy(
392 self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_9)
393 info = common.BuildInfo(info_dict, None)
394 self.assertEqual('product-device',
395 info.GetBuildProp('ro.product.device'))
396
Tao Bao1c320f82019-10-04 23:25:12 -0700397
Tao Bao65b94e92018-10-11 21:57:26 -0700398class CommonZipTest(test_utils.ReleaseToolsTestCase):
399
Tao Bao31b08072017-11-08 15:50:59 -0800400 def _verify(self, zip_file, zip_file_name, arcname, expected_hash,
Tao Baof3282b42015-04-01 11:21:55 -0700401 test_file_name=None, expected_stat=None, expected_mode=0o644,
402 expected_compress_type=zipfile.ZIP_STORED):
403 # Verify the stat if present.
404 if test_file_name is not None:
405 new_stat = os.stat(test_file_name)
406 self.assertEqual(int(expected_stat.st_mode), int(new_stat.st_mode))
407 self.assertEqual(int(expected_stat.st_mtime), int(new_stat.st_mtime))
408
409 # Reopen the zip file to verify.
Kelvin Zhang928c2342020-09-22 16:15:57 -0400410 zip_file = zipfile.ZipFile(zip_file_name, "r", allowZip64=True)
Tao Baof3282b42015-04-01 11:21:55 -0700411
412 # Verify the timestamp.
413 info = zip_file.getinfo(arcname)
414 self.assertEqual(info.date_time, (2009, 1, 1, 0, 0, 0))
415
416 # Verify the file mode.
417 mode = (info.external_attr >> 16) & 0o777
418 self.assertEqual(mode, expected_mode)
419
420 # Verify the compress type.
421 self.assertEqual(info.compress_type, expected_compress_type)
422
423 # Verify the zip contents.
Tao Bao31b08072017-11-08 15:50:59 -0800424 entry = zip_file.open(arcname)
425 sha1_hash = sha1()
Tao Baoc1a1ec32019-06-18 16:29:37 -0700426 for chunk in iter(lambda: entry.read(4 * MiB), b''):
Tao Bao31b08072017-11-08 15:50:59 -0800427 sha1_hash.update(chunk)
428 self.assertEqual(expected_hash, sha1_hash.hexdigest())
Tao Baof3282b42015-04-01 11:21:55 -0700429 self.assertIsNone(zip_file.testzip())
430
Dan Albert8e0178d2015-01-27 15:53:15 -0800431 def _test_ZipWrite(self, contents, extra_zipwrite_args=None):
432 extra_zipwrite_args = dict(extra_zipwrite_args or {})
433
434 test_file = tempfile.NamedTemporaryFile(delete=False)
Dan Albert8e0178d2015-01-27 15:53:15 -0800435 test_file_name = test_file.name
Tao Baof3282b42015-04-01 11:21:55 -0700436
437 zip_file = tempfile.NamedTemporaryFile(delete=False)
Dan Albert8e0178d2015-01-27 15:53:15 -0800438 zip_file_name = zip_file.name
439
440 # File names within an archive strip the leading slash.
441 arcname = extra_zipwrite_args.get("arcname", test_file_name)
442 if arcname[0] == "/":
443 arcname = arcname[1:]
444
445 zip_file.close()
Kelvin Zhang928c2342020-09-22 16:15:57 -0400446 zip_file = zipfile.ZipFile(zip_file_name, "w", allowZip64=True)
Dan Albert8e0178d2015-01-27 15:53:15 -0800447
448 try:
Tao Bao31b08072017-11-08 15:50:59 -0800449 sha1_hash = sha1()
450 for data in contents:
Tao Baoc1a1ec32019-06-18 16:29:37 -0700451 sha1_hash.update(bytes(data))
452 test_file.write(bytes(data))
Dan Albert8e0178d2015-01-27 15:53:15 -0800453 test_file.close()
454
Tao Baof3282b42015-04-01 11:21:55 -0700455 expected_stat = os.stat(test_file_name)
Dan Albert8e0178d2015-01-27 15:53:15 -0800456 expected_mode = extra_zipwrite_args.get("perms", 0o644)
Tao Baof3282b42015-04-01 11:21:55 -0700457 expected_compress_type = extra_zipwrite_args.get("compress_type",
458 zipfile.ZIP_STORED)
Dan Albert8e0178d2015-01-27 15:53:15 -0800459 time.sleep(5) # Make sure the atime/mtime will change measurably.
460
461 common.ZipWrite(zip_file, test_file_name, **extra_zipwrite_args)
Tao Baof3282b42015-04-01 11:21:55 -0700462 common.ZipClose(zip_file)
Dan Albert8e0178d2015-01-27 15:53:15 -0800463
Tao Bao31b08072017-11-08 15:50:59 -0800464 self._verify(zip_file, zip_file_name, arcname, sha1_hash.hexdigest(),
465 test_file_name, expected_stat, expected_mode,
466 expected_compress_type)
Dan Albert8e0178d2015-01-27 15:53:15 -0800467 finally:
468 os.remove(test_file_name)
469 os.remove(zip_file_name)
470
Tao Baof3282b42015-04-01 11:21:55 -0700471 def _test_ZipWriteStr(self, zinfo_or_arcname, contents, extra_args=None):
472 extra_args = dict(extra_args or {})
473
474 zip_file = tempfile.NamedTemporaryFile(delete=False)
475 zip_file_name = zip_file.name
476 zip_file.close()
477
Kelvin Zhang928c2342020-09-22 16:15:57 -0400478 zip_file = zipfile.ZipFile(zip_file_name, "w", allowZip64=True)
Tao Baof3282b42015-04-01 11:21:55 -0700479
480 try:
481 expected_compress_type = extra_args.get("compress_type",
482 zipfile.ZIP_STORED)
483 time.sleep(5) # Make sure the atime/mtime will change measurably.
484
485 if not isinstance(zinfo_or_arcname, zipfile.ZipInfo):
Tao Bao58c1b962015-05-20 09:32:18 -0700486 arcname = zinfo_or_arcname
487 expected_mode = extra_args.get("perms", 0o644)
Tao Baof3282b42015-04-01 11:21:55 -0700488 else:
Tao Bao58c1b962015-05-20 09:32:18 -0700489 arcname = zinfo_or_arcname.filename
Tao Baoc1a1ec32019-06-18 16:29:37 -0700490 if zinfo_or_arcname.external_attr:
491 zinfo_perms = zinfo_or_arcname.external_attr >> 16
492 else:
493 zinfo_perms = 0o600
494 expected_mode = extra_args.get("perms", zinfo_perms)
Tao Baof3282b42015-04-01 11:21:55 -0700495
Tao Bao58c1b962015-05-20 09:32:18 -0700496 common.ZipWriteStr(zip_file, zinfo_or_arcname, contents, **extra_args)
Tao Baof3282b42015-04-01 11:21:55 -0700497 common.ZipClose(zip_file)
498
Tao Bao31b08072017-11-08 15:50:59 -0800499 self._verify(zip_file, zip_file_name, arcname, sha1(contents).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700500 expected_mode=expected_mode,
Tao Baof3282b42015-04-01 11:21:55 -0700501 expected_compress_type=expected_compress_type)
502 finally:
503 os.remove(zip_file_name)
504
505 def _test_ZipWriteStr_large_file(self, large, small, extra_args=None):
506 extra_args = dict(extra_args or {})
507
508 zip_file = tempfile.NamedTemporaryFile(delete=False)
509 zip_file_name = zip_file.name
510
511 test_file = tempfile.NamedTemporaryFile(delete=False)
512 test_file_name = test_file.name
513
514 arcname_large = test_file_name
515 arcname_small = "bar"
516
517 # File names within an archive strip the leading slash.
518 if arcname_large[0] == "/":
519 arcname_large = arcname_large[1:]
520
521 zip_file.close()
Kelvin Zhang928c2342020-09-22 16:15:57 -0400522 zip_file = zipfile.ZipFile(zip_file_name, "w", allowZip64=True)
Tao Baof3282b42015-04-01 11:21:55 -0700523
524 try:
Tao Bao31b08072017-11-08 15:50:59 -0800525 sha1_hash = sha1()
526 for data in large:
527 sha1_hash.update(data)
528 test_file.write(data)
Tao Baof3282b42015-04-01 11:21:55 -0700529 test_file.close()
530
531 expected_stat = os.stat(test_file_name)
532 expected_mode = 0o644
533 expected_compress_type = extra_args.get("compress_type",
534 zipfile.ZIP_STORED)
535 time.sleep(5) # Make sure the atime/mtime will change measurably.
536
537 common.ZipWrite(zip_file, test_file_name, **extra_args)
538 common.ZipWriteStr(zip_file, arcname_small, small, **extra_args)
539 common.ZipClose(zip_file)
540
541 # Verify the contents written by ZipWrite().
Tao Bao31b08072017-11-08 15:50:59 -0800542 self._verify(zip_file, zip_file_name, arcname_large,
543 sha1_hash.hexdigest(), test_file_name, expected_stat,
544 expected_mode, expected_compress_type)
Tao Baof3282b42015-04-01 11:21:55 -0700545
546 # Verify the contents written by ZipWriteStr().
Tao Bao31b08072017-11-08 15:50:59 -0800547 self._verify(zip_file, zip_file_name, arcname_small,
548 sha1(small).hexdigest(),
Tao Baof3282b42015-04-01 11:21:55 -0700549 expected_compress_type=expected_compress_type)
550 finally:
551 os.remove(zip_file_name)
552 os.remove(test_file_name)
553
554 def _test_reset_ZIP64_LIMIT(self, func, *args):
555 default_limit = (1 << 31) - 1
556 self.assertEqual(default_limit, zipfile.ZIP64_LIMIT)
557 func(*args)
558 self.assertEqual(default_limit, zipfile.ZIP64_LIMIT)
559
Dan Albert8e0178d2015-01-27 15:53:15 -0800560 def test_ZipWrite(self):
561 file_contents = os.urandom(1024)
562 self._test_ZipWrite(file_contents)
563
564 def test_ZipWrite_with_opts(self):
565 file_contents = os.urandom(1024)
566 self._test_ZipWrite(file_contents, {
567 "arcname": "foobar",
568 "perms": 0o777,
569 "compress_type": zipfile.ZIP_DEFLATED,
570 })
Tao Baof3282b42015-04-01 11:21:55 -0700571 self._test_ZipWrite(file_contents, {
572 "arcname": "foobar",
573 "perms": 0o700,
574 "compress_type": zipfile.ZIP_STORED,
575 })
Dan Albert8e0178d2015-01-27 15:53:15 -0800576
577 def test_ZipWrite_large_file(self):
Tao Baof3282b42015-04-01 11:21:55 -0700578 file_contents = get_2gb_string()
Dan Albert8e0178d2015-01-27 15:53:15 -0800579 self._test_ZipWrite(file_contents, {
580 "compress_type": zipfile.ZIP_DEFLATED,
581 })
582
583 def test_ZipWrite_resets_ZIP64_LIMIT(self):
Tao Baof3282b42015-04-01 11:21:55 -0700584 self._test_reset_ZIP64_LIMIT(self._test_ZipWrite, "")
585
586 def test_ZipWriteStr(self):
587 random_string = os.urandom(1024)
588 # Passing arcname
589 self._test_ZipWriteStr("foo", random_string)
590
591 # Passing zinfo
592 zinfo = zipfile.ZipInfo(filename="foo")
593 self._test_ZipWriteStr(zinfo, random_string)
594
595 # Timestamp in the zinfo should be overwritten.
596 zinfo.date_time = (2015, 3, 1, 15, 30, 0)
597 self._test_ZipWriteStr(zinfo, random_string)
598
599 def test_ZipWriteStr_with_opts(self):
600 random_string = os.urandom(1024)
601 # Passing arcname
602 self._test_ZipWriteStr("foo", random_string, {
Tao Bao58c1b962015-05-20 09:32:18 -0700603 "perms": 0o700,
Tao Baof3282b42015-04-01 11:21:55 -0700604 "compress_type": zipfile.ZIP_DEFLATED,
605 })
Tao Bao58c1b962015-05-20 09:32:18 -0700606 self._test_ZipWriteStr("bar", random_string, {
Tao Baof3282b42015-04-01 11:21:55 -0700607 "compress_type": zipfile.ZIP_STORED,
608 })
609
610 # Passing zinfo
611 zinfo = zipfile.ZipInfo(filename="foo")
612 self._test_ZipWriteStr(zinfo, random_string, {
613 "compress_type": zipfile.ZIP_DEFLATED,
614 })
615 self._test_ZipWriteStr(zinfo, random_string, {
Tao Bao58c1b962015-05-20 09:32:18 -0700616 "perms": 0o600,
Tao Baof3282b42015-04-01 11:21:55 -0700617 "compress_type": zipfile.ZIP_STORED,
618 })
Tao Baoc1a1ec32019-06-18 16:29:37 -0700619 self._test_ZipWriteStr(zinfo, random_string, {
620 "perms": 0o000,
621 "compress_type": zipfile.ZIP_STORED,
622 })
Tao Baof3282b42015-04-01 11:21:55 -0700623
624 def test_ZipWriteStr_large_file(self):
625 # zipfile.writestr() doesn't work when the str size is over 2GiB even with
626 # the workaround. We will only test the case of writing a string into a
627 # large archive.
628 long_string = get_2gb_string()
629 short_string = os.urandom(1024)
630 self._test_ZipWriteStr_large_file(long_string, short_string, {
631 "compress_type": zipfile.ZIP_DEFLATED,
632 })
633
634 def test_ZipWriteStr_resets_ZIP64_LIMIT(self):
Tao Baoc1a1ec32019-06-18 16:29:37 -0700635 self._test_reset_ZIP64_LIMIT(self._test_ZipWriteStr, 'foo', b'')
Tao Baof3282b42015-04-01 11:21:55 -0700636 zinfo = zipfile.ZipInfo(filename="foo")
Tao Baoc1a1ec32019-06-18 16:29:37 -0700637 self._test_reset_ZIP64_LIMIT(self._test_ZipWriteStr, zinfo, b'')
Tao Bao58c1b962015-05-20 09:32:18 -0700638
639 def test_bug21309935(self):
640 zip_file = tempfile.NamedTemporaryFile(delete=False)
641 zip_file_name = zip_file.name
642 zip_file.close()
643
644 try:
645 random_string = os.urandom(1024)
Kelvin Zhang928c2342020-09-22 16:15:57 -0400646 zip_file = zipfile.ZipFile(zip_file_name, "w", allowZip64=True)
Tao Bao58c1b962015-05-20 09:32:18 -0700647 # Default perms should be 0o644 when passing the filename.
648 common.ZipWriteStr(zip_file, "foo", random_string)
649 # Honor the specified perms.
650 common.ZipWriteStr(zip_file, "bar", random_string, perms=0o755)
651 # The perms in zinfo should be untouched.
652 zinfo = zipfile.ZipInfo(filename="baz")
653 zinfo.external_attr = 0o740 << 16
654 common.ZipWriteStr(zip_file, zinfo, random_string)
655 # Explicitly specified perms has the priority.
656 zinfo = zipfile.ZipInfo(filename="qux")
657 zinfo.external_attr = 0o700 << 16
658 common.ZipWriteStr(zip_file, zinfo, random_string, perms=0o400)
659 common.ZipClose(zip_file)
660
Tao Bao31b08072017-11-08 15:50:59 -0800661 self._verify(zip_file, zip_file_name, "foo",
662 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700663 expected_mode=0o644)
Tao Bao31b08072017-11-08 15:50:59 -0800664 self._verify(zip_file, zip_file_name, "bar",
665 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700666 expected_mode=0o755)
Tao Bao31b08072017-11-08 15:50:59 -0800667 self._verify(zip_file, zip_file_name, "baz",
668 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700669 expected_mode=0o740)
Tao Bao31b08072017-11-08 15:50:59 -0800670 self._verify(zip_file, zip_file_name, "qux",
671 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700672 expected_mode=0o400)
673 finally:
674 os.remove(zip_file_name)
Tianjie Xu9c384d22017-06-20 17:00:55 -0700675
Tao Bao82490d32019-04-09 00:12:30 -0700676 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao89d7ab22017-12-14 17:05:33 -0800677 def test_ZipDelete(self):
678 zip_file = tempfile.NamedTemporaryFile(delete=False, suffix='.zip')
679 output_zip = zipfile.ZipFile(zip_file.name, 'w',
680 compression=zipfile.ZIP_DEFLATED)
681 with tempfile.NamedTemporaryFile() as entry_file:
682 entry_file.write(os.urandom(1024))
683 common.ZipWrite(output_zip, entry_file.name, arcname='Test1')
684 common.ZipWrite(output_zip, entry_file.name, arcname='Test2')
685 common.ZipWrite(output_zip, entry_file.name, arcname='Test3')
686 common.ZipClose(output_zip)
687 zip_file.close()
688
689 try:
690 common.ZipDelete(zip_file.name, 'Test2')
Kelvin Zhang928c2342020-09-22 16:15:57 -0400691 with zipfile.ZipFile(zip_file.name, 'r', allowZip64=True) as check_zip:
Tao Bao89d7ab22017-12-14 17:05:33 -0800692 entries = check_zip.namelist()
693 self.assertTrue('Test1' in entries)
694 self.assertFalse('Test2' in entries)
695 self.assertTrue('Test3' in entries)
696
Tao Bao986ee862018-10-04 15:46:16 -0700697 self.assertRaises(
698 common.ExternalError, common.ZipDelete, zip_file.name, 'Test2')
Kelvin Zhang928c2342020-09-22 16:15:57 -0400699 with zipfile.ZipFile(zip_file.name, 'r', allowZip64=True) as check_zip:
Tao Bao89d7ab22017-12-14 17:05:33 -0800700 entries = check_zip.namelist()
701 self.assertTrue('Test1' in entries)
702 self.assertFalse('Test2' in entries)
703 self.assertTrue('Test3' in entries)
704
705 common.ZipDelete(zip_file.name, ['Test3'])
Kelvin Zhang928c2342020-09-22 16:15:57 -0400706 with zipfile.ZipFile(zip_file.name, 'r', allowZip64=True) as check_zip:
Tao Bao89d7ab22017-12-14 17:05:33 -0800707 entries = check_zip.namelist()
708 self.assertTrue('Test1' in entries)
709 self.assertFalse('Test2' in entries)
710 self.assertFalse('Test3' in entries)
711
712 common.ZipDelete(zip_file.name, ['Test1', 'Test2'])
Kelvin Zhang928c2342020-09-22 16:15:57 -0400713 with zipfile.ZipFile(zip_file.name, 'r', allowZip64=True) as check_zip:
Tao Bao89d7ab22017-12-14 17:05:33 -0800714 entries = check_zip.namelist()
715 self.assertFalse('Test1' in entries)
716 self.assertFalse('Test2' in entries)
717 self.assertFalse('Test3' in entries)
718 finally:
719 os.remove(zip_file.name)
720
Tao Bao0ff15de2019-03-20 11:26:06 -0700721 @staticmethod
722 def _test_UnzipTemp_createZipFile():
723 zip_file = common.MakeTempFile(suffix='.zip')
724 output_zip = zipfile.ZipFile(
725 zip_file, 'w', compression=zipfile.ZIP_DEFLATED)
726 contents = os.urandom(1024)
727 with tempfile.NamedTemporaryFile() as entry_file:
728 entry_file.write(contents)
729 common.ZipWrite(output_zip, entry_file.name, arcname='Test1')
730 common.ZipWrite(output_zip, entry_file.name, arcname='Test2')
731 common.ZipWrite(output_zip, entry_file.name, arcname='Foo3')
732 common.ZipWrite(output_zip, entry_file.name, arcname='Bar4')
733 common.ZipWrite(output_zip, entry_file.name, arcname='Dir5/Baz5')
734 common.ZipClose(output_zip)
735 common.ZipClose(output_zip)
736 return zip_file
737
Tao Bao82490d32019-04-09 00:12:30 -0700738 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao0ff15de2019-03-20 11:26:06 -0700739 def test_UnzipTemp(self):
740 zip_file = self._test_UnzipTemp_createZipFile()
741 unzipped_dir = common.UnzipTemp(zip_file)
742 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
743 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
744 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
745 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
746 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
747
Tao Bao82490d32019-04-09 00:12:30 -0700748 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao0ff15de2019-03-20 11:26:06 -0700749 def test_UnzipTemp_withPatterns(self):
750 zip_file = self._test_UnzipTemp_createZipFile()
751
752 unzipped_dir = common.UnzipTemp(zip_file, ['Test1'])
753 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
754 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
755 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
756 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
757 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
758
759 unzipped_dir = common.UnzipTemp(zip_file, ['Test1', 'Foo3'])
760 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
761 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
762 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
763 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
764 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
765
766 unzipped_dir = common.UnzipTemp(zip_file, ['Test*', 'Foo3*'])
767 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
768 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
769 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
770 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
771 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
772
773 unzipped_dir = common.UnzipTemp(zip_file, ['*Test1', '*Baz*'])
774 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
775 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
776 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
777 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
778 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
779
780 def test_UnzipTemp_withEmptyPatterns(self):
781 zip_file = self._test_UnzipTemp_createZipFile()
782 unzipped_dir = common.UnzipTemp(zip_file, [])
783 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
784 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
785 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
786 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
787 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
788
Tao Bao82490d32019-04-09 00:12:30 -0700789 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao0ff15de2019-03-20 11:26:06 -0700790 def test_UnzipTemp_withPartiallyMatchingPatterns(self):
791 zip_file = self._test_UnzipTemp_createZipFile()
792 unzipped_dir = common.UnzipTemp(zip_file, ['Test*', 'Nonexistent*'])
793 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
794 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
795 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
796 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
797 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
798
799 def test_UnzipTemp_withNoMatchingPatterns(self):
800 zip_file = self._test_UnzipTemp_createZipFile()
801 unzipped_dir = common.UnzipTemp(zip_file, ['Foo4', 'Nonexistent*'])
802 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
803 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
804 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
805 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
806 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
807
Tao Bao89d7ab22017-12-14 17:05:33 -0800808
Tao Bao65b94e92018-10-11 21:57:26 -0700809class CommonApkUtilsTest(test_utils.ReleaseToolsTestCase):
Tao Bao818ddf52018-01-05 11:17:34 -0800810 """Tests the APK utils related functions."""
811
812 APKCERTS_TXT1 = (
813 'name="RecoveryLocalizer.apk" certificate="certs/devkey.x509.pem"'
814 ' private_key="certs/devkey.pk8"\n'
815 'name="Settings.apk"'
Dan Willemsen0ab1be62019-04-09 21:35:37 -0700816 ' certificate="build/make/target/product/security/platform.x509.pem"'
817 ' private_key="build/make/target/product/security/platform.pk8"\n'
Tao Bao818ddf52018-01-05 11:17:34 -0800818 'name="TV.apk" certificate="PRESIGNED" private_key=""\n'
819 )
820
821 APKCERTS_CERTMAP1 = {
822 'RecoveryLocalizer.apk' : 'certs/devkey',
Dan Willemsen0ab1be62019-04-09 21:35:37 -0700823 'Settings.apk' : 'build/make/target/product/security/platform',
Tao Bao818ddf52018-01-05 11:17:34 -0800824 'TV.apk' : 'PRESIGNED',
825 }
826
827 APKCERTS_TXT2 = (
828 'name="Compressed1.apk" certificate="certs/compressed1.x509.pem"'
829 ' private_key="certs/compressed1.pk8" compressed="gz"\n'
830 'name="Compressed2a.apk" certificate="certs/compressed2.x509.pem"'
831 ' private_key="certs/compressed2.pk8" compressed="gz"\n'
832 'name="Compressed2b.apk" certificate="certs/compressed2.x509.pem"'
833 ' private_key="certs/compressed2.pk8" compressed="gz"\n'
834 'name="Compressed3.apk" certificate="certs/compressed3.x509.pem"'
835 ' private_key="certs/compressed3.pk8" compressed="gz"\n'
836 )
837
838 APKCERTS_CERTMAP2 = {
839 'Compressed1.apk' : 'certs/compressed1',
840 'Compressed2a.apk' : 'certs/compressed2',
841 'Compressed2b.apk' : 'certs/compressed2',
842 'Compressed3.apk' : 'certs/compressed3',
843 }
844
845 APKCERTS_TXT3 = (
846 'name="Compressed4.apk" certificate="certs/compressed4.x509.pem"'
847 ' private_key="certs/compressed4.pk8" compressed="xz"\n'
848 )
849
850 APKCERTS_CERTMAP3 = {
851 'Compressed4.apk' : 'certs/compressed4',
852 }
853
Bill Peckham5c7b0342020-04-03 15:36:23 -0700854 # Test parsing with no optional fields, both optional fields, and only the
855 # partition optional field.
856 APKCERTS_TXT4 = (
857 'name="RecoveryLocalizer.apk" certificate="certs/devkey.x509.pem"'
858 ' private_key="certs/devkey.pk8"\n'
859 'name="Settings.apk"'
860 ' certificate="build/make/target/product/security/platform.x509.pem"'
861 ' private_key="build/make/target/product/security/platform.pk8"'
862 ' compressed="gz" partition="system"\n'
863 'name="TV.apk" certificate="PRESIGNED" private_key=""'
864 ' partition="product"\n'
865 )
866
867 APKCERTS_CERTMAP4 = {
868 'RecoveryLocalizer.apk' : 'certs/devkey',
869 'Settings.apk' : 'build/make/target/product/security/platform',
870 'TV.apk' : 'PRESIGNED',
871 }
872
Tao Bao17e4e612018-02-16 17:12:54 -0800873 def setUp(self):
874 self.testdata_dir = test_utils.get_testdata_dir()
875
Tao Bao818ddf52018-01-05 11:17:34 -0800876 @staticmethod
877 def _write_apkcerts_txt(apkcerts_txt, additional=None):
878 if additional is None:
879 additional = []
880 target_files = common.MakeTempFile(suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -0400881 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800882 target_files_zip.writestr('META/apkcerts.txt', apkcerts_txt)
883 for entry in additional:
884 target_files_zip.writestr(entry, '')
885 return target_files
886
887 def test_ReadApkCerts_NoncompressedApks(self):
888 target_files = self._write_apkcerts_txt(self.APKCERTS_TXT1)
Kelvin Zhang928c2342020-09-22 16:15:57 -0400889 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800890 certmap, ext = common.ReadApkCerts(input_zip)
891
892 self.assertDictEqual(self.APKCERTS_CERTMAP1, certmap)
893 self.assertIsNone(ext)
894
895 def test_ReadApkCerts_CompressedApks(self):
896 # We have "installed" Compressed1.apk.gz only. Note that Compressed3.apk is
897 # not stored in '.gz' format, so it shouldn't be considered as installed.
898 target_files = self._write_apkcerts_txt(
899 self.APKCERTS_TXT2,
900 ['Compressed1.apk.gz', 'Compressed3.apk'])
901
Kelvin Zhang928c2342020-09-22 16:15:57 -0400902 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800903 certmap, ext = common.ReadApkCerts(input_zip)
904
905 self.assertDictEqual(self.APKCERTS_CERTMAP2, certmap)
906 self.assertEqual('.gz', ext)
907
908 # Alternative case with '.xz'.
909 target_files = self._write_apkcerts_txt(
910 self.APKCERTS_TXT3, ['Compressed4.apk.xz'])
911
Kelvin Zhang928c2342020-09-22 16:15:57 -0400912 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800913 certmap, ext = common.ReadApkCerts(input_zip)
914
915 self.assertDictEqual(self.APKCERTS_CERTMAP3, certmap)
916 self.assertEqual('.xz', ext)
917
918 def test_ReadApkCerts_CompressedAndNoncompressedApks(self):
919 target_files = self._write_apkcerts_txt(
920 self.APKCERTS_TXT1 + self.APKCERTS_TXT2,
921 ['Compressed1.apk.gz', 'Compressed3.apk'])
922
Kelvin Zhang928c2342020-09-22 16:15:57 -0400923 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800924 certmap, ext = common.ReadApkCerts(input_zip)
925
926 certmap_merged = self.APKCERTS_CERTMAP1.copy()
927 certmap_merged.update(self.APKCERTS_CERTMAP2)
928 self.assertDictEqual(certmap_merged, certmap)
929 self.assertEqual('.gz', ext)
930
931 def test_ReadApkCerts_MultipleCompressionMethods(self):
932 target_files = self._write_apkcerts_txt(
933 self.APKCERTS_TXT2 + self.APKCERTS_TXT3,
934 ['Compressed1.apk.gz', 'Compressed4.apk.xz'])
935
Kelvin Zhang928c2342020-09-22 16:15:57 -0400936 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800937 self.assertRaises(ValueError, common.ReadApkCerts, input_zip)
938
939 def test_ReadApkCerts_MismatchingKeys(self):
940 malformed_apkcerts_txt = (
941 'name="App1.apk" certificate="certs/cert1.x509.pem"'
942 ' private_key="certs/cert2.pk8"\n'
943 )
944 target_files = self._write_apkcerts_txt(malformed_apkcerts_txt)
945
Kelvin Zhang928c2342020-09-22 16:15:57 -0400946 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800947 self.assertRaises(ValueError, common.ReadApkCerts, input_zip)
948
Bill Peckham5c7b0342020-04-03 15:36:23 -0700949 def test_ReadApkCerts_WithWithoutOptionalFields(self):
950 target_files = self._write_apkcerts_txt(self.APKCERTS_TXT4)
Kelvin Zhang928c2342020-09-22 16:15:57 -0400951 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Bill Peckham5c7b0342020-04-03 15:36:23 -0700952 certmap, ext = common.ReadApkCerts(input_zip)
953
954 self.assertDictEqual(self.APKCERTS_CERTMAP4, certmap)
955 self.assertIsNone(ext)
956
Tao Bao04e1f012018-02-04 12:13:35 -0800957 def test_ExtractPublicKey(self):
Tao Bao17e4e612018-02-16 17:12:54 -0800958 cert = os.path.join(self.testdata_dir, 'testkey.x509.pem')
959 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
Tao Baoda30cfa2017-12-01 16:19:46 -0800960 with open(pubkey) as pubkey_fp:
Tao Bao04e1f012018-02-04 12:13:35 -0800961 self.assertEqual(pubkey_fp.read(), common.ExtractPublicKey(cert))
962
963 def test_ExtractPublicKey_invalidInput(self):
Tao Bao17e4e612018-02-16 17:12:54 -0800964 wrong_input = os.path.join(self.testdata_dir, 'testkey.pk8')
Tao Bao04e1f012018-02-04 12:13:35 -0800965 self.assertRaises(AssertionError, common.ExtractPublicKey, wrong_input)
966
Tao Bao82490d32019-04-09 00:12:30 -0700967 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao2cc0ca12019-03-15 10:44:43 -0700968 def test_ExtractAvbPublicKey(self):
969 privkey = os.path.join(self.testdata_dir, 'testkey.key')
970 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
Tao Bao1ac886e2019-06-26 11:58:22 -0700971 extracted_from_privkey = common.ExtractAvbPublicKey('avbtool', privkey)
972 extracted_from_pubkey = common.ExtractAvbPublicKey('avbtool', pubkey)
973 with open(extracted_from_privkey, 'rb') as privkey_fp, \
974 open(extracted_from_pubkey, 'rb') as pubkey_fp:
Tao Bao2cc0ca12019-03-15 10:44:43 -0700975 self.assertEqual(privkey_fp.read(), pubkey_fp.read())
976
Tao Bao17e4e612018-02-16 17:12:54 -0800977 def test_ParseCertificate(self):
978 cert = os.path.join(self.testdata_dir, 'testkey.x509.pem')
979
980 cmd = ['openssl', 'x509', '-in', cert, '-outform', 'DER']
Tao Baoda30cfa2017-12-01 16:19:46 -0800981 proc = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
982 universal_newlines=False)
Tao Bao17e4e612018-02-16 17:12:54 -0800983 expected, _ = proc.communicate()
984 self.assertEqual(0, proc.returncode)
985
986 with open(cert) as cert_fp:
987 actual = common.ParseCertificate(cert_fp.read())
988 self.assertEqual(expected, actual)
989
Tao Bao82490d32019-04-09 00:12:30 -0700990 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700991 def test_GetMinSdkVersion(self):
992 test_app = os.path.join(self.testdata_dir, 'TestApp.apk')
993 self.assertEqual('24', common.GetMinSdkVersion(test_app))
994
Tao Bao82490d32019-04-09 00:12:30 -0700995 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700996 def test_GetMinSdkVersion_invalidInput(self):
997 self.assertRaises(
998 common.ExternalError, common.GetMinSdkVersion, 'does-not-exist.apk')
999
Tao Bao82490d32019-04-09 00:12:30 -07001000 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -07001001 def test_GetMinSdkVersionInt(self):
1002 test_app = os.path.join(self.testdata_dir, 'TestApp.apk')
1003 self.assertEqual(24, common.GetMinSdkVersionInt(test_app, {}))
1004
Tao Bao82490d32019-04-09 00:12:30 -07001005 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -07001006 def test_GetMinSdkVersionInt_invalidInput(self):
1007 self.assertRaises(
1008 common.ExternalError, common.GetMinSdkVersionInt, 'does-not-exist.apk',
1009 {})
1010
Tao Bao818ddf52018-01-05 11:17:34 -08001011
Tao Bao65b94e92018-10-11 21:57:26 -07001012class CommonUtilsTest(test_utils.ReleaseToolsTestCase):
Tao Baofc7e0e02018-02-13 13:54:02 -08001013
Tao Bao02a08592018-07-22 12:40:45 -07001014 def setUp(self):
1015 self.testdata_dir = test_utils.get_testdata_dir()
1016
Tao Bao82490d32019-04-09 00:12:30 -07001017 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001018 def test_GetSparseImage_emptyBlockMapFile(self):
1019 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001020 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baofc7e0e02018-02-13 13:54:02 -08001021 target_files_zip.write(
1022 test_utils.construct_sparse_image([
1023 (0xCAC1, 6),
1024 (0xCAC3, 3),
1025 (0xCAC1, 4)]),
1026 arcname='IMAGES/system.img')
1027 target_files_zip.writestr('IMAGES/system.map', '')
1028 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 8))
1029 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1030
Tao Baodba59ee2018-01-09 13:21:02 -08001031 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001032 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baodba59ee2018-01-09 13:21:02 -08001033 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001034
1035 self.assertDictEqual(
1036 {
1037 '__COPY': RangeSet("0"),
1038 '__NONZERO-0': RangeSet("1-5 9-12"),
1039 },
1040 sparse_image.file_map)
1041
Daniel Norman21c34f72020-11-11 17:25:50 -08001042 def test_PartitionMapFromTargetFiles(self):
1043 target_files_dir = common.MakeTempDir()
1044 os.makedirs(os.path.join(target_files_dir, 'SYSTEM'))
1045 os.makedirs(os.path.join(target_files_dir, 'SYSTEM', 'vendor'))
1046 os.makedirs(os.path.join(target_files_dir, 'PRODUCT'))
1047 os.makedirs(os.path.join(target_files_dir, 'SYSTEM', 'product'))
1048 os.makedirs(os.path.join(target_files_dir, 'SYSTEM', 'vendor', 'odm'))
1049 os.makedirs(os.path.join(target_files_dir, 'VENDOR_DLKM'))
1050 partition_map = common.PartitionMapFromTargetFiles(target_files_dir)
1051 self.assertDictEqual(
1052 partition_map,
1053 {
1054 'system': 'SYSTEM',
1055 'vendor': 'SYSTEM/vendor',
1056 # Prefer PRODUCT over SYSTEM/product
1057 'product': 'PRODUCT',
1058 'odm': 'SYSTEM/vendor/odm',
1059 'vendor_dlkm': 'VENDOR_DLKM',
1060 # No system_ext or odm_dlkm
1061 })
1062
Daniel Normand3351562020-10-29 12:33:11 -07001063 def test_SharedUidPartitionViolations(self):
1064 uid_dict = {
1065 'android.uid.phone': {
1066 'system': ['system_phone.apk'],
1067 'system_ext': ['system_ext_phone.apk'],
1068 },
1069 'android.uid.wifi': {
1070 'vendor': ['vendor_wifi.apk'],
1071 'odm': ['odm_wifi.apk'],
1072 },
1073 }
1074 errors = common.SharedUidPartitionViolations(
1075 uid_dict, [('system', 'system_ext'), ('vendor', 'odm')])
1076 self.assertEqual(errors, [])
1077
1078 def test_SharedUidPartitionViolations_Violation(self):
1079 uid_dict = {
1080 'android.uid.phone': {
1081 'system': ['system_phone.apk'],
1082 'vendor': ['vendor_phone.apk'],
1083 },
1084 }
1085 errors = common.SharedUidPartitionViolations(
1086 uid_dict, [('system', 'system_ext'), ('vendor', 'odm')])
1087 self.assertIn(
1088 ('APK sharedUserId "android.uid.phone" found across partition groups '
1089 'in partitions "system,vendor"'), errors)
1090
Tao Baob2de7d92019-04-10 10:01:47 -07001091 def test_GetSparseImage_missingImageFile(self):
Tao Baofc7e0e02018-02-13 13:54:02 -08001092 self.assertRaises(
Tao Baob2de7d92019-04-10 10:01:47 -07001093 AssertionError, common.GetSparseImage, 'system2', self.testdata_dir,
1094 None, False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001095 self.assertRaises(
Tao Baob2de7d92019-04-10 10:01:47 -07001096 AssertionError, common.GetSparseImage, 'unknown', self.testdata_dir,
1097 None, False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001098
Tao Bao82490d32019-04-09 00:12:30 -07001099 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001100 def test_GetSparseImage_missingBlockMapFile(self):
1101 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001102 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baofc7e0e02018-02-13 13:54:02 -08001103 target_files_zip.write(
1104 test_utils.construct_sparse_image([
1105 (0xCAC1, 6),
1106 (0xCAC3, 3),
1107 (0xCAC1, 4)]),
1108 arcname='IMAGES/system.img')
1109 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 8))
1110 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1111
Tao Baodba59ee2018-01-09 13:21:02 -08001112 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001113 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baodba59ee2018-01-09 13:21:02 -08001114 self.assertRaises(
1115 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
1116 False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001117
Tao Bao82490d32019-04-09 00:12:30 -07001118 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001119 def test_GetSparseImage_sharedBlocks_notAllowed(self):
1120 """Tests the case of having overlapping blocks but disallowed."""
1121 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001122 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baofc7e0e02018-02-13 13:54:02 -08001123 target_files_zip.write(
1124 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1125 arcname='IMAGES/system.img')
1126 # Block 10 is shared between two files.
1127 target_files_zip.writestr(
1128 'IMAGES/system.map',
1129 '\n'.join([
1130 '/system/file1 1-5 9-10',
1131 '/system/file2 10-12']))
1132 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1133 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1134
Tao Baodba59ee2018-01-09 13:21:02 -08001135 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001136 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baodba59ee2018-01-09 13:21:02 -08001137 self.assertRaises(
1138 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
1139 False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001140
Tao Bao82490d32019-04-09 00:12:30 -07001141 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001142 def test_GetSparseImage_sharedBlocks_allowed(self):
1143 """Tests the case for target using BOARD_EXT4_SHARE_DUP_BLOCKS := true."""
1144 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001145 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baofc7e0e02018-02-13 13:54:02 -08001146 # Construct an image with a care_map of "0-5 9-12".
1147 target_files_zip.write(
1148 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1149 arcname='IMAGES/system.img')
1150 # Block 10 is shared between two files.
1151 target_files_zip.writestr(
1152 'IMAGES/system.map',
1153 '\n'.join([
1154 '/system/file1 1-5 9-10',
1155 '/system/file2 10-12']))
1156 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1157 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1158
Tao Baodba59ee2018-01-09 13:21:02 -08001159 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001160 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baodba59ee2018-01-09 13:21:02 -08001161 sparse_image = common.GetSparseImage('system', tempdir, input_zip, True)
Tao Baofc7e0e02018-02-13 13:54:02 -08001162
1163 self.assertDictEqual(
1164 {
1165 '__COPY': RangeSet("0"),
1166 '__NONZERO-0': RangeSet("6-8 13-15"),
1167 '/system/file1': RangeSet("1-5 9-10"),
1168 '/system/file2': RangeSet("11-12"),
1169 },
1170 sparse_image.file_map)
1171
1172 # '/system/file2' should be marked with 'uses_shared_blocks', but not with
1173 # 'incomplete'.
1174 self.assertTrue(
1175 sparse_image.file_map['/system/file2'].extra['uses_shared_blocks'])
1176 self.assertNotIn(
1177 'incomplete', sparse_image.file_map['/system/file2'].extra)
1178
Tao Baoa264fef2019-10-06 21:55:20 -07001179 # '/system/file1' will only contain one field -- a copy of the input text.
1180 self.assertEqual(1, len(sparse_image.file_map['/system/file1'].extra))
1181
1182 # Meta entries should not have any extra tag.
Tao Baofc7e0e02018-02-13 13:54:02 -08001183 self.assertFalse(sparse_image.file_map['__COPY'].extra)
1184 self.assertFalse(sparse_image.file_map['__NONZERO-0'].extra)
Tao Baofc7e0e02018-02-13 13:54:02 -08001185
Tao Bao82490d32019-04-09 00:12:30 -07001186 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001187 def test_GetSparseImage_incompleteRanges(self):
1188 """Tests the case of ext4 images with holes."""
1189 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001190 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baofc7e0e02018-02-13 13:54:02 -08001191 target_files_zip.write(
1192 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1193 arcname='IMAGES/system.img')
1194 target_files_zip.writestr(
1195 'IMAGES/system.map',
1196 '\n'.join([
1197 '/system/file1 1-5 9-10',
1198 '/system/file2 11-12']))
1199 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1200 # '/system/file2' has less blocks listed (2) than actual (3).
1201 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1202
Tao Baodba59ee2018-01-09 13:21:02 -08001203 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001204 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baodba59ee2018-01-09 13:21:02 -08001205 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001206
Tao Baoa264fef2019-10-06 21:55:20 -07001207 self.assertEqual(
1208 '1-5 9-10',
1209 sparse_image.file_map['/system/file1'].extra['text_str'])
Tao Baofc7e0e02018-02-13 13:54:02 -08001210 self.assertTrue(sparse_image.file_map['/system/file2'].extra['incomplete'])
1211
Tao Bao82490d32019-04-09 00:12:30 -07001212 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -07001213 def test_GetSparseImage_systemRootImage_filenameWithExtraLeadingSlash(self):
1214 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001215 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001216 target_files_zip.write(
1217 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1218 arcname='IMAGES/system.img')
1219 target_files_zip.writestr(
1220 'IMAGES/system.map',
1221 '\n'.join([
1222 '//system/file1 1-5 9-10',
1223 '//system/file2 11-12',
1224 '/system/app/file3 13-15']))
1225 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1226 # '/system/file2' has less blocks listed (2) than actual (3).
1227 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1228 # '/system/app/file3' has less blocks listed (3) than actual (4).
1229 target_files_zip.writestr('SYSTEM/app/file3', os.urandom(4096 * 4))
1230
1231 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001232 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001233 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
1234
Tao Baoa264fef2019-10-06 21:55:20 -07001235 self.assertEqual(
1236 '1-5 9-10',
1237 sparse_image.file_map['//system/file1'].extra['text_str'])
Tao Baod3554e62018-07-10 15:31:22 -07001238 self.assertTrue(sparse_image.file_map['//system/file2'].extra['incomplete'])
1239 self.assertTrue(
1240 sparse_image.file_map['/system/app/file3'].extra['incomplete'])
1241
Tao Bao82490d32019-04-09 00:12:30 -07001242 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -07001243 def test_GetSparseImage_systemRootImage_nonSystemFiles(self):
1244 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001245 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001246 target_files_zip.write(
1247 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1248 arcname='IMAGES/system.img')
1249 target_files_zip.writestr(
1250 'IMAGES/system.map',
1251 '\n'.join([
1252 '//system/file1 1-5 9-10',
1253 '//init.rc 13-15']))
1254 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1255 # '/init.rc' has less blocks listed (3) than actual (4).
1256 target_files_zip.writestr('ROOT/init.rc', os.urandom(4096 * 4))
1257
1258 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001259 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001260 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
1261
Tao Baoa264fef2019-10-06 21:55:20 -07001262 self.assertEqual(
1263 '1-5 9-10',
1264 sparse_image.file_map['//system/file1'].extra['text_str'])
Tao Baod3554e62018-07-10 15:31:22 -07001265 self.assertTrue(sparse_image.file_map['//init.rc'].extra['incomplete'])
1266
Tao Bao82490d32019-04-09 00:12:30 -07001267 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -07001268 def test_GetSparseImage_fileNotFound(self):
1269 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001270 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001271 target_files_zip.write(
1272 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1273 arcname='IMAGES/system.img')
1274 target_files_zip.writestr(
1275 'IMAGES/system.map',
1276 '\n'.join([
1277 '//system/file1 1-5 9-10',
1278 '//system/file2 11-12']))
1279 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1280
1281 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001282 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001283 self.assertRaises(
1284 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
1285 False)
1286
Tao Bao82490d32019-04-09 00:12:30 -07001287 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001288 def test_GetAvbChainedPartitionArg(self):
1289 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
1290 info_dict = {
1291 'avb_avbtool': 'avbtool',
1292 'avb_system_key_path': pubkey,
1293 'avb_system_rollback_index_location': 2,
1294 }
1295 args = common.GetAvbChainedPartitionArg('system', info_dict).split(':')
1296 self.assertEqual(3, len(args))
1297 self.assertEqual('system', args[0])
1298 self.assertEqual('2', args[1])
1299 self.assertTrue(os.path.exists(args[2]))
1300
Tao Bao82490d32019-04-09 00:12:30 -07001301 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001302 def test_GetAvbChainedPartitionArg_withPrivateKey(self):
1303 key = os.path.join(self.testdata_dir, 'testkey.key')
1304 info_dict = {
1305 'avb_avbtool': 'avbtool',
1306 'avb_product_key_path': key,
1307 'avb_product_rollback_index_location': 2,
1308 }
1309 args = common.GetAvbChainedPartitionArg('product', info_dict).split(':')
1310 self.assertEqual(3, len(args))
1311 self.assertEqual('product', args[0])
1312 self.assertEqual('2', args[1])
1313 self.assertTrue(os.path.exists(args[2]))
1314
Tao Bao82490d32019-04-09 00:12:30 -07001315 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001316 def test_GetAvbChainedPartitionArg_withSpecifiedKey(self):
1317 info_dict = {
1318 'avb_avbtool': 'avbtool',
1319 'avb_system_key_path': 'does-not-exist',
1320 'avb_system_rollback_index_location': 2,
1321 }
1322 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
1323 args = common.GetAvbChainedPartitionArg(
1324 'system', info_dict, pubkey).split(':')
1325 self.assertEqual(3, len(args))
1326 self.assertEqual('system', args[0])
1327 self.assertEqual('2', args[1])
1328 self.assertTrue(os.path.exists(args[2]))
1329
Tao Bao82490d32019-04-09 00:12:30 -07001330 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001331 def test_GetAvbChainedPartitionArg_invalidKey(self):
1332 pubkey = os.path.join(self.testdata_dir, 'testkey_with_passwd.x509.pem')
1333 info_dict = {
1334 'avb_avbtool': 'avbtool',
1335 'avb_system_key_path': pubkey,
1336 'avb_system_rollback_index_location': 2,
1337 }
1338 self.assertRaises(
Tao Bao986ee862018-10-04 15:46:16 -07001339 common.ExternalError, common.GetAvbChainedPartitionArg, 'system',
1340 info_dict)
Tao Bao02a08592018-07-22 12:40:45 -07001341
Tao Baoa57ab9f2018-08-24 12:08:38 -07001342 INFO_DICT_DEFAULT = {
1343 'recovery_api_version': 3,
1344 'fstab_version': 2,
1345 'system_root_image': 'true',
1346 'no_recovery' : 'true',
1347 'recovery_as_boot': 'true',
1348 }
1349
Daniel Norman4cc9df62019-07-18 10:11:07 -07001350 def test_LoadListFromFile(self):
1351 file_path = os.path.join(self.testdata_dir,
1352 'merge_config_framework_item_list')
1353 contents = common.LoadListFromFile(file_path)
1354 expected_contents = [
1355 'META/apkcerts.txt',
1356 'META/filesystem_config.txt',
1357 'META/root_filesystem_config.txt',
1358 'META/system_manifest.xml',
1359 'META/system_matrix.xml',
1360 'META/update_engine_config.txt',
1361 'PRODUCT/*',
1362 'ROOT/*',
1363 'SYSTEM/*',
1364 ]
1365 self.assertEqual(sorted(contents), sorted(expected_contents))
1366
Tao Baoa57ab9f2018-08-24 12:08:38 -07001367 @staticmethod
1368 def _test_LoadInfoDict_createTargetFiles(info_dict, fstab_path):
1369 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001370 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001371 info_values = ''.join(
Tao Baoda30cfa2017-12-01 16:19:46 -08001372 ['{}={}\n'.format(k, v) for k, v in sorted(info_dict.items())])
Tao Baoa57ab9f2018-08-24 12:08:38 -07001373 common.ZipWriteStr(target_files_zip, 'META/misc_info.txt', info_values)
1374
1375 FSTAB_TEMPLATE = "/dev/block/system {} ext4 ro,barrier=1 defaults"
1376 if info_dict.get('system_root_image') == 'true':
1377 fstab_values = FSTAB_TEMPLATE.format('/')
1378 else:
1379 fstab_values = FSTAB_TEMPLATE.format('/system')
1380 common.ZipWriteStr(target_files_zip, fstab_path, fstab_values)
Tao Bao410ad8b2018-08-24 12:08:38 -07001381
1382 common.ZipWriteStr(
1383 target_files_zip, 'META/file_contexts', 'file-contexts')
Tao Baoa57ab9f2018-08-24 12:08:38 -07001384 return target_files
1385
1386 def test_LoadInfoDict(self):
1387 target_files = self._test_LoadInfoDict_createTargetFiles(
1388 self.INFO_DICT_DEFAULT,
1389 'BOOT/RAMDISK/system/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001390 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001391 loaded_dict = common.LoadInfoDict(target_files_zip)
1392 self.assertEqual(3, loaded_dict['recovery_api_version'])
1393 self.assertEqual(2, loaded_dict['fstab_version'])
1394 self.assertIn('/', loaded_dict['fstab'])
1395 self.assertIn('/system', loaded_dict['fstab'])
1396
1397 def test_LoadInfoDict_legacyRecoveryFstabPath(self):
1398 target_files = self._test_LoadInfoDict_createTargetFiles(
1399 self.INFO_DICT_DEFAULT,
1400 'BOOT/RAMDISK/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001401 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001402 loaded_dict = common.LoadInfoDict(target_files_zip)
1403 self.assertEqual(3, loaded_dict['recovery_api_version'])
1404 self.assertEqual(2, loaded_dict['fstab_version'])
1405 self.assertIn('/', loaded_dict['fstab'])
1406 self.assertIn('/system', loaded_dict['fstab'])
1407
Tao Bao82490d32019-04-09 00:12:30 -07001408 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoa57ab9f2018-08-24 12:08:38 -07001409 def test_LoadInfoDict_dirInput(self):
1410 target_files = self._test_LoadInfoDict_createTargetFiles(
1411 self.INFO_DICT_DEFAULT,
1412 'BOOT/RAMDISK/system/etc/recovery.fstab')
1413 unzipped = common.UnzipTemp(target_files)
1414 loaded_dict = common.LoadInfoDict(unzipped)
1415 self.assertEqual(3, loaded_dict['recovery_api_version'])
1416 self.assertEqual(2, loaded_dict['fstab_version'])
1417 self.assertIn('/', loaded_dict['fstab'])
1418 self.assertIn('/system', loaded_dict['fstab'])
1419
Tao Bao82490d32019-04-09 00:12:30 -07001420 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoa57ab9f2018-08-24 12:08:38 -07001421 def test_LoadInfoDict_dirInput_legacyRecoveryFstabPath(self):
1422 target_files = self._test_LoadInfoDict_createTargetFiles(
1423 self.INFO_DICT_DEFAULT,
1424 'BOOT/RAMDISK/system/etc/recovery.fstab')
1425 unzipped = common.UnzipTemp(target_files)
1426 loaded_dict = common.LoadInfoDict(unzipped)
1427 self.assertEqual(3, loaded_dict['recovery_api_version'])
1428 self.assertEqual(2, loaded_dict['fstab_version'])
1429 self.assertIn('/', loaded_dict['fstab'])
1430 self.assertIn('/system', loaded_dict['fstab'])
1431
1432 def test_LoadInfoDict_systemRootImageFalse(self):
1433 # Devices not using system-as-root nor recovery-as-boot. Non-A/B devices
1434 # launched prior to P will likely have this config.
1435 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
1436 del info_dict['no_recovery']
1437 del info_dict['system_root_image']
1438 del info_dict['recovery_as_boot']
1439 target_files = self._test_LoadInfoDict_createTargetFiles(
1440 info_dict,
1441 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001442 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001443 loaded_dict = common.LoadInfoDict(target_files_zip)
1444 self.assertEqual(3, loaded_dict['recovery_api_version'])
1445 self.assertEqual(2, loaded_dict['fstab_version'])
1446 self.assertNotIn('/', loaded_dict['fstab'])
1447 self.assertIn('/system', loaded_dict['fstab'])
1448
1449 def test_LoadInfoDict_recoveryAsBootFalse(self):
1450 # Devices using system-as-root, but with standalone recovery image. Non-A/B
1451 # devices launched since P will likely have this config.
1452 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
1453 del info_dict['no_recovery']
1454 del info_dict['recovery_as_boot']
1455 target_files = self._test_LoadInfoDict_createTargetFiles(
1456 info_dict,
1457 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001458 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001459 loaded_dict = common.LoadInfoDict(target_files_zip)
1460 self.assertEqual(3, loaded_dict['recovery_api_version'])
1461 self.assertEqual(2, loaded_dict['fstab_version'])
1462 self.assertIn('/', loaded_dict['fstab'])
1463 self.assertIn('/system', loaded_dict['fstab'])
1464
1465 def test_LoadInfoDict_noRecoveryTrue(self):
1466 # Device doesn't have a recovery partition at all.
1467 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
1468 del info_dict['recovery_as_boot']
1469 target_files = self._test_LoadInfoDict_createTargetFiles(
1470 info_dict,
1471 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001472 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001473 loaded_dict = common.LoadInfoDict(target_files_zip)
1474 self.assertEqual(3, loaded_dict['recovery_api_version'])
1475 self.assertEqual(2, loaded_dict['fstab_version'])
1476 self.assertIsNone(loaded_dict['fstab'])
1477
Tao Bao82490d32019-04-09 00:12:30 -07001478 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao410ad8b2018-08-24 12:08:38 -07001479 def test_LoadInfoDict_missingMetaMiscInfoTxt(self):
1480 target_files = self._test_LoadInfoDict_createTargetFiles(
1481 self.INFO_DICT_DEFAULT,
1482 'BOOT/RAMDISK/system/etc/recovery.fstab')
1483 common.ZipDelete(target_files, 'META/misc_info.txt')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001484 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Bao410ad8b2018-08-24 12:08:38 -07001485 self.assertRaises(ValueError, common.LoadInfoDict, target_files_zip)
1486
Tao Bao82490d32019-04-09 00:12:30 -07001487 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao410ad8b2018-08-24 12:08:38 -07001488 def test_LoadInfoDict_repacking(self):
1489 target_files = self._test_LoadInfoDict_createTargetFiles(
1490 self.INFO_DICT_DEFAULT,
1491 'BOOT/RAMDISK/system/etc/recovery.fstab')
1492 unzipped = common.UnzipTemp(target_files)
1493 loaded_dict = common.LoadInfoDict(unzipped, True)
1494 self.assertEqual(3, loaded_dict['recovery_api_version'])
1495 self.assertEqual(2, loaded_dict['fstab_version'])
1496 self.assertIn('/', loaded_dict['fstab'])
1497 self.assertIn('/system', loaded_dict['fstab'])
1498 self.assertEqual(
1499 os.path.join(unzipped, 'ROOT'), loaded_dict['root_dir'])
1500 self.assertEqual(
1501 os.path.join(unzipped, 'META', 'root_filesystem_config.txt'),
1502 loaded_dict['root_fs_config'])
1503
1504 def test_LoadInfoDict_repackingWithZipFileInput(self):
1505 target_files = self._test_LoadInfoDict_createTargetFiles(
1506 self.INFO_DICT_DEFAULT,
1507 'BOOT/RAMDISK/system/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001508 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Bao410ad8b2018-08-24 12:08:38 -07001509 self.assertRaises(
1510 AssertionError, common.LoadInfoDict, target_files_zip, True)
1511
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001512 def test_MergeDynamicPartitionInfoDicts_ReturnsMergedDict(self):
1513 framework_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001514 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001515 'super_partition_groups': 'group_a',
1516 'dynamic_partition_list': 'system',
Daniel Norman55417142019-11-25 16:04:36 -08001517 'super_group_a_partition_list': 'system',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001518 }
1519 vendor_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001520 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001521 'super_partition_groups': 'group_a group_b',
1522 'dynamic_partition_list': 'vendor product',
Daniel Normanb0c75912020-09-24 14:30:21 -07001523 'super_block_devices': 'super',
1524 'super_super_device_size': '3000',
Daniel Norman55417142019-11-25 16:04:36 -08001525 'super_group_a_partition_list': 'vendor',
1526 'super_group_a_group_size': '1000',
1527 'super_group_b_partition_list': 'product',
1528 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001529 }
1530 merged_dict = common.MergeDynamicPartitionInfoDicts(
1531 framework_dict=framework_dict,
Daniel Norman55417142019-11-25 16:04:36 -08001532 vendor_dict=vendor_dict)
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001533 expected_merged_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001534 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001535 'super_partition_groups': 'group_a group_b',
Daniel Normanb0c75912020-09-24 14:30:21 -07001536 'dynamic_partition_list': 'product system vendor',
1537 'super_block_devices': 'super',
1538 'super_super_device_size': '3000',
Daniel Norman55417142019-11-25 16:04:36 -08001539 'super_group_a_partition_list': 'system vendor',
1540 'super_group_a_group_size': '1000',
1541 'super_group_b_partition_list': 'product',
1542 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001543 }
1544 self.assertEqual(merged_dict, expected_merged_dict)
1545
1546 def test_MergeDynamicPartitionInfoDicts_IgnoringFrameworkGroupSize(self):
1547 framework_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001548 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001549 'super_partition_groups': 'group_a',
1550 'dynamic_partition_list': 'system',
Daniel Norman55417142019-11-25 16:04:36 -08001551 'super_group_a_partition_list': 'system',
1552 'super_group_a_group_size': '5000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001553 }
1554 vendor_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001555 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001556 'super_partition_groups': 'group_a group_b',
1557 'dynamic_partition_list': 'vendor product',
Daniel Norman55417142019-11-25 16:04:36 -08001558 'super_group_a_partition_list': 'vendor',
1559 'super_group_a_group_size': '1000',
1560 'super_group_b_partition_list': 'product',
1561 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001562 }
1563 merged_dict = common.MergeDynamicPartitionInfoDicts(
1564 framework_dict=framework_dict,
Daniel Norman55417142019-11-25 16:04:36 -08001565 vendor_dict=vendor_dict)
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001566 expected_merged_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001567 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001568 'super_partition_groups': 'group_a group_b',
Daniel Normanb0c75912020-09-24 14:30:21 -07001569 'dynamic_partition_list': 'product system vendor',
Daniel Norman55417142019-11-25 16:04:36 -08001570 'super_group_a_partition_list': 'system vendor',
1571 'super_group_a_group_size': '1000',
1572 'super_group_b_partition_list': 'product',
1573 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001574 }
1575 self.assertEqual(merged_dict, expected_merged_dict)
1576
Daniel Norman276f0622019-07-26 14:13:51 -07001577 def test_GetAvbPartitionArg(self):
1578 info_dict = {}
1579 cmd = common.GetAvbPartitionArg('system', '/path/to/system.img', info_dict)
1580 self.assertEqual(
1581 ['--include_descriptors_from_image', '/path/to/system.img'], cmd)
1582
1583 @test_utils.SkipIfExternalToolsUnavailable()
1584 def test_AppendVBMetaArgsForPartition_vendorAsChainedPartition(self):
1585 testdata_dir = test_utils.get_testdata_dir()
1586 pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
1587 info_dict = {
1588 'avb_avbtool': 'avbtool',
1589 'avb_vendor_key_path': pubkey,
1590 'avb_vendor_rollback_index_location': 5,
1591 }
1592 cmd = common.GetAvbPartitionArg('vendor', '/path/to/vendor.img', info_dict)
1593 self.assertEqual(2, len(cmd))
1594 self.assertEqual('--chain_partition', cmd[0])
1595 chained_partition_args = cmd[1].split(':')
1596 self.assertEqual(3, len(chained_partition_args))
1597 self.assertEqual('vendor', chained_partition_args[0])
1598 self.assertEqual('5', chained_partition_args[1])
1599 self.assertTrue(os.path.exists(chained_partition_args[2]))
1600
Tao Bao3612c882019-10-14 17:49:31 -07001601 @test_utils.SkipIfExternalToolsUnavailable()
1602 def test_AppendVBMetaArgsForPartition_recoveryAsChainedPartition_nonAb(self):
1603 testdata_dir = test_utils.get_testdata_dir()
1604 pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
1605 info_dict = {
1606 'avb_avbtool': 'avbtool',
1607 'avb_recovery_key_path': pubkey,
1608 'avb_recovery_rollback_index_location': 3,
1609 }
1610 cmd = common.GetAvbPartitionArg(
1611 'recovery', '/path/to/recovery.img', info_dict)
1612 self.assertFalse(cmd)
1613
1614 @test_utils.SkipIfExternalToolsUnavailable()
1615 def test_AppendVBMetaArgsForPartition_recoveryAsChainedPartition_ab(self):
1616 testdata_dir = test_utils.get_testdata_dir()
1617 pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
1618 info_dict = {
1619 'ab_update': 'true',
1620 'avb_avbtool': 'avbtool',
1621 'avb_recovery_key_path': pubkey,
1622 'avb_recovery_rollback_index_location': 3,
1623 }
1624 cmd = common.GetAvbPartitionArg(
1625 'recovery', '/path/to/recovery.img', info_dict)
1626 self.assertEqual(2, len(cmd))
1627 self.assertEqual('--chain_partition', cmd[0])
1628 chained_partition_args = cmd[1].split(':')
1629 self.assertEqual(3, len(chained_partition_args))
1630 self.assertEqual('recovery', chained_partition_args[0])
1631 self.assertEqual('3', chained_partition_args[1])
1632 self.assertTrue(os.path.exists(chained_partition_args[2]))
1633
Yi-Yo Chiang36054e22022-01-08 22:29:30 +08001634 def test_GenerateGkiCertificate_KeyPathNotFound(self):
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001635 pubkey = os.path.join(self.testdata_dir, 'no_testkey_gki.pem')
1636 self.assertFalse(os.path.exists(pubkey))
1637
1638 common.OPTIONS.info_dict = {
1639 'gki_signing_key_path': pubkey,
1640 'gki_signing_algorithm': 'SHA256_RSA4096',
1641 'gki_signing_signature_args': '--prop foo:bar',
1642 }
Yi-Yo Chiang36054e22022-01-08 22:29:30 +08001643 test_file = tempfile.NamedTemporaryFile()
1644 self.assertRaises(common.ExternalError, common._GenerateGkiCertificate,
Yi-Yo Chiang24da1a42022-02-22 19:51:15 +08001645 test_file.name, 'generic_kernel')
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001646
Yi-Yo Chiang36054e22022-01-08 22:29:30 +08001647 def test_GenerateGkiCertificate_SearchKeyPathNotFound(self):
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001648 pubkey = 'no_testkey_gki.pem'
1649 self.assertFalse(os.path.exists(pubkey))
1650
1651 # Tests it should raise ExternalError if no key found under
1652 # OPTIONS.search_path.
1653 search_path_dir = common.MakeTempDir()
1654 search_pubkey = os.path.join(search_path_dir, pubkey)
1655 self.assertFalse(os.path.exists(search_pubkey))
1656
1657 common.OPTIONS.search_path = search_path_dir
1658 common.OPTIONS.info_dict = {
1659 'gki_signing_key_path': pubkey,
1660 'gki_signing_algorithm': 'SHA256_RSA4096',
1661 'gki_signing_signature_args': '--prop foo:bar',
1662 }
Yi-Yo Chiang36054e22022-01-08 22:29:30 +08001663 test_file = tempfile.NamedTemporaryFile()
1664 self.assertRaises(common.ExternalError, common._GenerateGkiCertificate,
Yi-Yo Chiang24da1a42022-02-22 19:51:15 +08001665 test_file.name, 'generic_kernel')
Tao Baofc7e0e02018-02-13 13:54:02 -08001666
Tao Bao65b94e92018-10-11 21:57:26 -07001667class InstallRecoveryScriptFormatTest(test_utils.ReleaseToolsTestCase):
Tao Bao1c830bf2017-12-25 10:43:47 -08001668 """Checks the format of install-recovery.sh.
Tianjie Xu9c384d22017-06-20 17:00:55 -07001669
Tao Bao1c830bf2017-12-25 10:43:47 -08001670 Its format should match between common.py and validate_target_files.py.
1671 """
Tianjie Xu9c384d22017-06-20 17:00:55 -07001672
1673 def setUp(self):
Tao Bao1c830bf2017-12-25 10:43:47 -08001674 self._tempdir = common.MakeTempDir()
Kelvin Zhangc693d952020-07-22 19:21:22 -04001675 # Create a fake dict that contains the fstab info for boot&recovery.
Tianjie Xu9c384d22017-06-20 17:00:55 -07001676 self._info = {"fstab" : {}}
Kelvin Zhangc693d952020-07-22 19:21:22 -04001677 fake_fstab = [
Tao Bao1c830bf2017-12-25 10:43:47 -08001678 "/dev/soc.0/by-name/boot /boot emmc defaults defaults",
1679 "/dev/soc.0/by-name/recovery /recovery emmc defaults defaults"]
Kelvin Zhangc693d952020-07-22 19:21:22 -04001680 self._info["fstab"] = common.LoadRecoveryFSTab("\n".join, 2, fake_fstab)
Tianjie Xudf055582017-11-07 12:22:58 -08001681 # Construct the gzipped recovery.img and boot.img
1682 self.recovery_data = bytearray([
1683 0x1f, 0x8b, 0x08, 0x00, 0x81, 0x11, 0x02, 0x5a, 0x00, 0x03, 0x2b, 0x4a,
1684 0x4d, 0xce, 0x2f, 0x4b, 0x2d, 0xaa, 0x04, 0x00, 0xc9, 0x93, 0x43, 0xf3,
1685 0x08, 0x00, 0x00, 0x00
1686 ])
1687 # echo -n "boot" | gzip -f | hd
1688 self.boot_data = bytearray([
1689 0x1f, 0x8b, 0x08, 0x00, 0x8c, 0x12, 0x02, 0x5a, 0x00, 0x03, 0x4b, 0xca,
1690 0xcf, 0x2f, 0x01, 0x00, 0xc4, 0xae, 0xed, 0x46, 0x04, 0x00, 0x00, 0x00
1691 ])
Tianjie Xu9c384d22017-06-20 17:00:55 -07001692
1693 def _out_tmp_sink(self, name, data, prefix="SYSTEM"):
1694 loc = os.path.join(self._tempdir, prefix, name)
1695 if not os.path.exists(os.path.dirname(loc)):
1696 os.makedirs(os.path.dirname(loc))
Tao Baoda30cfa2017-12-01 16:19:46 -08001697 with open(loc, "wb") as f:
Tianjie Xu9c384d22017-06-20 17:00:55 -07001698 f.write(data)
1699
1700 def test_full_recovery(self):
Tao Bao31b08072017-11-08 15:50:59 -08001701 recovery_image = common.File("recovery.img", self.recovery_data)
1702 boot_image = common.File("boot.img", self.boot_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001703 self._info["full_recovery_image"] = "true"
1704
1705 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1706 recovery_image, boot_image, self._info)
1707 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1708 self._info)
1709
Tao Bao82490d32019-04-09 00:12:30 -07001710 @test_utils.SkipIfExternalToolsUnavailable()
Tianjie Xu9c384d22017-06-20 17:00:55 -07001711 def test_recovery_from_boot(self):
Tao Bao31b08072017-11-08 15:50:59 -08001712 recovery_image = common.File("recovery.img", self.recovery_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001713 self._out_tmp_sink("recovery.img", recovery_image.data, "IMAGES")
Tao Bao31b08072017-11-08 15:50:59 -08001714 boot_image = common.File("boot.img", self.boot_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001715 self._out_tmp_sink("boot.img", boot_image.data, "IMAGES")
1716
1717 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1718 recovery_image, boot_image, self._info)
1719 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1720 self._info)
1721 # Validate 'recovery-from-boot' with bonus argument.
Tao Baoda30cfa2017-12-01 16:19:46 -08001722 self._out_tmp_sink("etc/recovery-resource.dat", b"bonus", "SYSTEM")
Tianjie Xu9c384d22017-06-20 17:00:55 -07001723 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1724 recovery_image, boot_image, self._info)
1725 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1726 self._info)
Yifan Hong45433e42019-01-18 13:55:25 -08001727
1728
Yifan Hong45433e42019-01-18 13:55:25 -08001729class MockBlockDifference(object):
Tao Baoda30cfa2017-12-01 16:19:46 -08001730
Yifan Hong45433e42019-01-18 13:55:25 -08001731 def __init__(self, partition, tgt, src=None):
1732 self.partition = partition
1733 self.tgt = tgt
1734 self.src = src
Tao Baoda30cfa2017-12-01 16:19:46 -08001735
Yifan Hong45433e42019-01-18 13:55:25 -08001736 def WriteScript(self, script, _, progress=None,
1737 write_verify_script=False):
1738 if progress:
1739 script.AppendExtra("progress({})".format(progress))
1740 script.AppendExtra("patch({});".format(self.partition))
1741 if write_verify_script:
1742 self.WritePostInstallVerifyScript(script)
Tao Baoda30cfa2017-12-01 16:19:46 -08001743
Yifan Hong45433e42019-01-18 13:55:25 -08001744 def WritePostInstallVerifyScript(self, script):
1745 script.AppendExtra("verify({});".format(self.partition))
1746
1747
1748class FakeSparseImage(object):
Tao Baoda30cfa2017-12-01 16:19:46 -08001749
Yifan Hong45433e42019-01-18 13:55:25 -08001750 def __init__(self, size):
1751 self.blocksize = 4096
1752 self.total_blocks = size // 4096
1753 assert size % 4096 == 0, "{} is not a multiple of 4096".format(size)
1754
1755
1756class DynamicPartitionsDifferenceTest(test_utils.ReleaseToolsTestCase):
Tao Baoda30cfa2017-12-01 16:19:46 -08001757
Yifan Hong45433e42019-01-18 13:55:25 -08001758 @staticmethod
1759 def get_op_list(output_path):
Kelvin Zhang928c2342020-09-22 16:15:57 -04001760 with zipfile.ZipFile(output_path, allowZip64=True) as output_zip:
Tao Baoda30cfa2017-12-01 16:19:46 -08001761 with output_zip.open('dynamic_partitions_op_list') as op_list:
1762 return [line.decode().strip() for line in op_list.readlines()
1763 if not line.startswith(b'#')]
Yifan Hong45433e42019-01-18 13:55:25 -08001764
1765 def setUp(self):
Tao Baoe1148042019-10-07 20:00:34 -07001766 self.script = test_utils.MockScriptWriter()
Yifan Hong45433e42019-01-18 13:55:25 -08001767 self.output_path = common.MakeTempFile(suffix='.zip')
1768
1769 def test_full(self):
1770 target_info = common.LoadDictionaryFromLines("""
1771dynamic_partition_list=system vendor
1772super_partition_groups=group_foo
1773super_group_foo_group_size={group_size}
1774super_group_foo_partition_list=system vendor
1775""".format(group_size=4 * GiB).split("\n"))
1776 block_diffs = [MockBlockDifference("system", FakeSparseImage(3 * GiB)),
1777 MockBlockDifference("vendor", FakeSparseImage(1 * GiB))]
1778
1779 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001780 with zipfile.ZipFile(self.output_path, 'w', allowZip64=True) as output_zip:
Yifan Hong45433e42019-01-18 13:55:25 -08001781 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1782
1783 self.assertEqual(str(self.script).strip(), """
1784assert(update_dynamic_partitions(package_extract_file("dynamic_partitions_op_list")));
Yifan Hong45433e42019-01-18 13:55:25 -08001785patch(system);
1786verify(system);
1787unmap_partition("system");
Tao Baof1113e92019-06-18 12:10:14 -07001788patch(vendor);
1789verify(vendor);
1790unmap_partition("vendor");
Yifan Hong45433e42019-01-18 13:55:25 -08001791""".strip())
1792
1793 lines = self.get_op_list(self.output_path)
1794
1795 remove_all_groups = lines.index("remove_all_groups")
1796 add_group = lines.index("add_group group_foo 4294967296")
1797 add_vendor = lines.index("add vendor group_foo")
1798 add_system = lines.index("add system group_foo")
1799 resize_vendor = lines.index("resize vendor 1073741824")
1800 resize_system = lines.index("resize system 3221225472")
1801
1802 self.assertLess(remove_all_groups, add_group,
1803 "Should add groups after removing all groups")
1804 self.assertLess(add_group, min(add_vendor, add_system),
1805 "Should add partitions after adding group")
1806 self.assertLess(add_system, resize_system,
1807 "Should resize system after adding it")
1808 self.assertLess(add_vendor, resize_vendor,
1809 "Should resize vendor after adding it")
1810
1811 def test_inc_groups(self):
1812 source_info = common.LoadDictionaryFromLines("""
1813super_partition_groups=group_foo group_bar group_baz
1814super_group_foo_group_size={group_foo_size}
1815super_group_bar_group_size={group_bar_size}
1816""".format(group_foo_size=4 * GiB, group_bar_size=3 * GiB).split("\n"))
1817 target_info = common.LoadDictionaryFromLines("""
1818super_partition_groups=group_foo group_baz group_qux
1819super_group_foo_group_size={group_foo_size}
1820super_group_baz_group_size={group_baz_size}
1821super_group_qux_group_size={group_qux_size}
1822""".format(group_foo_size=3 * GiB, group_baz_size=4 * GiB,
1823 group_qux_size=1 * GiB).split("\n"))
1824
1825 dp_diff = common.DynamicPartitionsDifference(target_info,
1826 block_diffs=[],
1827 source_info_dict=source_info)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001828 with zipfile.ZipFile(self.output_path, 'w', allowZip64=True) as output_zip:
Yifan Hong45433e42019-01-18 13:55:25 -08001829 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1830
1831 lines = self.get_op_list(self.output_path)
1832
1833 removed = lines.index("remove_group group_bar")
1834 shrunk = lines.index("resize_group group_foo 3221225472")
1835 grown = lines.index("resize_group group_baz 4294967296")
1836 added = lines.index("add_group group_qux 1073741824")
1837
Tao Baof1113e92019-06-18 12:10:14 -07001838 self.assertLess(max(removed, shrunk),
1839 min(grown, added),
Yifan Hong45433e42019-01-18 13:55:25 -08001840 "ops that remove / shrink partitions must precede ops that "
1841 "grow / add partitions")
1842
Yifan Hongbb2658d2019-01-25 12:30:58 -08001843 def test_incremental(self):
Yifan Hong45433e42019-01-18 13:55:25 -08001844 source_info = common.LoadDictionaryFromLines("""
Justin Yun6151e3f2019-06-25 15:58:13 +09001845dynamic_partition_list=system vendor product system_ext
Yifan Hong45433e42019-01-18 13:55:25 -08001846super_partition_groups=group_foo
1847super_group_foo_group_size={group_foo_size}
Justin Yun6151e3f2019-06-25 15:58:13 +09001848super_group_foo_partition_list=system vendor product system_ext
Yifan Hong45433e42019-01-18 13:55:25 -08001849""".format(group_foo_size=4 * GiB).split("\n"))
1850 target_info = common.LoadDictionaryFromLines("""
1851dynamic_partition_list=system vendor product odm
1852super_partition_groups=group_foo group_bar
1853super_group_foo_group_size={group_foo_size}
1854super_group_foo_partition_list=system vendor odm
1855super_group_bar_group_size={group_bar_size}
1856super_group_bar_partition_list=product
1857""".format(group_foo_size=3 * GiB, group_bar_size=1 * GiB).split("\n"))
1858
1859 block_diffs = [MockBlockDifference("system", FakeSparseImage(1536 * MiB),
1860 src=FakeSparseImage(1024 * MiB)),
1861 MockBlockDifference("vendor", FakeSparseImage(512 * MiB),
1862 src=FakeSparseImage(1024 * MiB)),
1863 MockBlockDifference("product", FakeSparseImage(1024 * MiB),
1864 src=FakeSparseImage(1024 * MiB)),
Justin Yun6151e3f2019-06-25 15:58:13 +09001865 MockBlockDifference("system_ext", None,
Yifan Hong45433e42019-01-18 13:55:25 -08001866 src=FakeSparseImage(1024 * MiB)),
1867 MockBlockDifference("odm", FakeSparseImage(1024 * MiB),
1868 src=None)]
1869
1870 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs,
1871 source_info_dict=source_info)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001872 with zipfile.ZipFile(self.output_path, 'w', allowZip64=True) as output_zip:
Yifan Hong45433e42019-01-18 13:55:25 -08001873 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1874
1875 metadata_idx = self.script.lines.index(
1876 'assert(update_dynamic_partitions(package_extract_file('
1877 '"dynamic_partitions_op_list")));')
1878 self.assertLess(self.script.lines.index('patch(vendor);'), metadata_idx)
1879 self.assertLess(metadata_idx, self.script.lines.index('verify(vendor);'))
1880 for p in ("product", "system", "odm"):
1881 patch_idx = self.script.lines.index("patch({});".format(p))
1882 verify_idx = self.script.lines.index("verify({});".format(p))
1883 self.assertLess(metadata_idx, patch_idx,
1884 "Should patch {} after updating metadata".format(p))
1885 self.assertLess(patch_idx, verify_idx,
1886 "Should verify {} after patching".format(p))
1887
Justin Yun6151e3f2019-06-25 15:58:13 +09001888 self.assertNotIn("patch(system_ext);", self.script.lines)
Yifan Hong45433e42019-01-18 13:55:25 -08001889
1890 lines = self.get_op_list(self.output_path)
1891
Justin Yun6151e3f2019-06-25 15:58:13 +09001892 remove = lines.index("remove system_ext")
Yifan Hong45433e42019-01-18 13:55:25 -08001893 move_product_out = lines.index("move product default")
1894 shrink = lines.index("resize vendor 536870912")
1895 shrink_group = lines.index("resize_group group_foo 3221225472")
1896 add_group_bar = lines.index("add_group group_bar 1073741824")
1897 add_odm = lines.index("add odm group_foo")
1898 grow_existing = lines.index("resize system 1610612736")
1899 grow_added = lines.index("resize odm 1073741824")
1900 move_product_in = lines.index("move product group_bar")
1901
1902 max_idx_move_partition_out_foo = max(remove, move_product_out, shrink)
1903 min_idx_move_partition_in_foo = min(add_odm, grow_existing, grow_added)
1904
1905 self.assertLess(max_idx_move_partition_out_foo, shrink_group,
1906 "Must shrink group after partitions inside group are shrunk"
1907 " / removed")
1908
1909 self.assertLess(add_group_bar, move_product_in,
1910 "Must add partitions to group after group is added")
1911
1912 self.assertLess(max_idx_move_partition_out_foo,
1913 min_idx_move_partition_in_foo,
1914 "Must shrink partitions / remove partitions from group"
1915 "before adding / moving partitions into group")
Yifan Hongbb2658d2019-01-25 12:30:58 -08001916
1917 def test_remove_partition(self):
1918 source_info = common.LoadDictionaryFromLines("""
1919blockimgdiff_versions=3,4
1920use_dynamic_partitions=true
1921dynamic_partition_list=foo
1922super_partition_groups=group_foo
1923super_group_foo_group_size={group_foo_size}
1924super_group_foo_partition_list=foo
1925""".format(group_foo_size=4 * GiB).split("\n"))
1926 target_info = common.LoadDictionaryFromLines("""
1927blockimgdiff_versions=3,4
1928use_dynamic_partitions=true
1929super_partition_groups=group_foo
1930super_group_foo_group_size={group_foo_size}
1931""".format(group_foo_size=4 * GiB).split("\n"))
1932
1933 common.OPTIONS.info_dict = target_info
1934 common.OPTIONS.target_info_dict = target_info
1935 common.OPTIONS.source_info_dict = source_info
1936 common.OPTIONS.cache_size = 4 * 4096
1937
1938 block_diffs = [common.BlockDifference("foo", EmptyImage(),
1939 src=DataImage("source", pad=True))]
1940
1941 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs,
1942 source_info_dict=source_info)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001943 with zipfile.ZipFile(self.output_path, 'w', allowZip64=True) as output_zip:
Yifan Hongbb2658d2019-01-25 12:30:58 -08001944 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1945
1946 self.assertNotIn("block_image_update", str(self.script),
Tao Bao2cc0ca12019-03-15 10:44:43 -07001947 "Removed partition should not be patched.")
Yifan Hongbb2658d2019-01-25 12:30:58 -08001948
1949 lines = self.get_op_list(self.output_path)
1950 self.assertEqual(lines, ["remove foo"])
Tianjie Xu0fde41e2020-05-09 05:24:18 +00001951
1952
1953class PartitionBuildPropsTest(test_utils.ReleaseToolsTestCase):
1954 def setUp(self):
Tianjie Xu9afb2212020-05-10 21:48:15 +00001955 self.odm_build_prop = [
Tianjie Xu0fde41e2020-05-09 05:24:18 +00001956 'ro.odm.build.date.utc=1578430045',
1957 'ro.odm.build.fingerprint='
1958 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
1959 'ro.product.odm.device=coral',
1960 'import /odm/etc/build_${ro.boot.product.device_name}.prop',
1961 ]
1962
1963 @staticmethod
1964 def _BuildZipFile(entries):
1965 input_file = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001966 with zipfile.ZipFile(input_file, 'w', allowZip64=True) as input_zip:
Tianjie Xu0fde41e2020-05-09 05:24:18 +00001967 for name, content in entries.items():
1968 input_zip.writestr(name, content)
1969
1970 return input_file
1971
1972 def test_parseBuildProps_noImportStatement(self):
1973 build_prop = [
Tianjie Xu9afb2212020-05-10 21:48:15 +00001974 'ro.odm.build.date.utc=1578430045',
1975 'ro.odm.build.fingerprint='
1976 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
1977 'ro.product.odm.device=coral',
Tianjie Xu0fde41e2020-05-09 05:24:18 +00001978 ]
1979 input_file = self._BuildZipFile({
Tianjie Xu9afb2212020-05-10 21:48:15 +00001980 'ODM/etc/build.prop': '\n'.join(build_prop),
1981 })
1982
Kelvin Zhang928c2342020-09-22 16:15:57 -04001983 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00001984 placeholder_values = {
1985 'ro.boot.product.device_name': ['std', 'pro']
1986 }
1987 partition_props = common.PartitionBuildProps.FromInputFile(
1988 input_zip, 'odm', placeholder_values)
1989
1990 self.assertEqual({
1991 'ro.odm.build.date.utc': '1578430045',
1992 'ro.odm.build.fingerprint':
1993 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
1994 'ro.product.odm.device': 'coral',
1995 }, partition_props.build_props)
1996
1997 self.assertEqual(set(), partition_props.prop_overrides)
1998
1999 def test_parseBuildProps_singleImportStatement(self):
2000 build_std_prop = [
2001 'ro.product.odm.device=coral',
2002 'ro.product.odm.name=product1',
2003 ]
2004 build_pro_prop = [
2005 'ro.product.odm.device=coralpro',
2006 'ro.product.odm.name=product2',
2007 ]
2008
2009 input_file = self._BuildZipFile({
2010 'ODM/etc/build.prop': '\n'.join(self.odm_build_prop),
2011 'ODM/etc/build_std.prop': '\n'.join(build_std_prop),
2012 'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop),
2013 })
2014
Kelvin Zhang928c2342020-09-22 16:15:57 -04002015 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002016 placeholder_values = {
2017 'ro.boot.product.device_name': 'std'
2018 }
2019 partition_props = common.PartitionBuildProps.FromInputFile(
2020 input_zip, 'odm', placeholder_values)
2021
2022 self.assertEqual({
2023 'ro.odm.build.date.utc': '1578430045',
2024 'ro.odm.build.fingerprint':
2025 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2026 'ro.product.odm.device': 'coral',
2027 'ro.product.odm.name': 'product1',
2028 }, partition_props.build_props)
2029
Kelvin Zhang928c2342020-09-22 16:15:57 -04002030 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002031 placeholder_values = {
2032 'ro.boot.product.device_name': 'pro'
2033 }
2034 partition_props = common.PartitionBuildProps.FromInputFile(
2035 input_zip, 'odm', placeholder_values)
2036
2037 self.assertEqual({
2038 'ro.odm.build.date.utc': '1578430045',
2039 'ro.odm.build.fingerprint':
2040 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2041 'ro.product.odm.device': 'coralpro',
2042 'ro.product.odm.name': 'product2',
2043 }, partition_props.build_props)
2044
2045 def test_parseBuildProps_noPlaceHolders(self):
2046 build_prop = copy.copy(self.odm_build_prop)
2047 input_file = self._BuildZipFile({
2048 'ODM/etc/build.prop': '\n'.join(build_prop),
Tianjie Xu0fde41e2020-05-09 05:24:18 +00002049 })
2050
Kelvin Zhang928c2342020-09-22 16:15:57 -04002051 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu0fde41e2020-05-09 05:24:18 +00002052 partition_props = common.PartitionBuildProps.FromInputFile(
2053 input_zip, 'odm')
2054
2055 self.assertEqual({
Tianjie Xu9afb2212020-05-10 21:48:15 +00002056 'ro.odm.build.date.utc': '1578430045',
2057 'ro.odm.build.fingerprint':
2058 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2059 'ro.product.odm.device': 'coral',
Tianjie Xu0fde41e2020-05-09 05:24:18 +00002060 }, partition_props.build_props)
2061
Tianjie Xu9afb2212020-05-10 21:48:15 +00002062 self.assertEqual(set(), partition_props.prop_overrides)
2063
2064 def test_parseBuildProps_multipleImportStatements(self):
2065 build_prop = copy.deepcopy(self.odm_build_prop)
2066 build_prop.append(
2067 'import /odm/etc/build_${ro.boot.product.product_name}.prop')
2068
2069 build_std_prop = [
2070 'ro.product.odm.device=coral',
2071 ]
2072 build_pro_prop = [
2073 'ro.product.odm.device=coralpro',
2074 ]
2075
2076 product1_prop = [
2077 'ro.product.odm.name=product1',
2078 'ro.product.not_care=not_care',
2079 ]
2080
2081 product2_prop = [
2082 'ro.product.odm.name=product2',
2083 'ro.product.not_care=not_care',
2084 ]
2085
2086 input_file = self._BuildZipFile({
2087 'ODM/etc/build.prop': '\n'.join(build_prop),
2088 'ODM/etc/build_std.prop': '\n'.join(build_std_prop),
2089 'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop),
2090 'ODM/etc/build_product1.prop': '\n'.join(product1_prop),
2091 'ODM/etc/build_product2.prop': '\n'.join(product2_prop),
2092 })
2093
Kelvin Zhang928c2342020-09-22 16:15:57 -04002094 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002095 placeholder_values = {
2096 'ro.boot.product.device_name': 'std',
2097 'ro.boot.product.product_name': 'product1',
2098 'ro.boot.product.not_care': 'not_care',
2099 }
2100 partition_props = common.PartitionBuildProps.FromInputFile(
2101 input_zip, 'odm', placeholder_values)
2102
2103 self.assertEqual({
2104 'ro.odm.build.date.utc': '1578430045',
2105 'ro.odm.build.fingerprint':
2106 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2107 'ro.product.odm.device': 'coral',
2108 'ro.product.odm.name': 'product1'
2109 }, partition_props.build_props)
2110
Kelvin Zhang928c2342020-09-22 16:15:57 -04002111 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002112 placeholder_values = {
2113 'ro.boot.product.device_name': 'pro',
2114 'ro.boot.product.product_name': 'product2',
2115 'ro.boot.product.not_care': 'not_care',
2116 }
2117 partition_props = common.PartitionBuildProps.FromInputFile(
2118 input_zip, 'odm', placeholder_values)
2119
2120 self.assertEqual({
2121 'ro.odm.build.date.utc': '1578430045',
2122 'ro.odm.build.fingerprint':
2123 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2124 'ro.product.odm.device': 'coralpro',
2125 'ro.product.odm.name': 'product2'
2126 }, partition_props.build_props)
2127
2128 def test_parseBuildProps_defineAfterOverride(self):
2129 build_prop = copy.deepcopy(self.odm_build_prop)
2130 build_prop.append('ro.product.odm.device=coral')
2131
2132 build_std_prop = [
2133 'ro.product.odm.device=coral',
2134 ]
2135 build_pro_prop = [
2136 'ro.product.odm.device=coralpro',
2137 ]
2138
2139 input_file = self._BuildZipFile({
2140 'ODM/etc/build.prop': '\n'.join(build_prop),
2141 'ODM/etc/build_std.prop': '\n'.join(build_std_prop),
2142 'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop),
2143 })
2144
Kelvin Zhang928c2342020-09-22 16:15:57 -04002145 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002146 placeholder_values = {
2147 'ro.boot.product.device_name': 'std',
2148 }
2149
2150 self.assertRaises(ValueError, common.PartitionBuildProps.FromInputFile,
2151 input_zip, 'odm', placeholder_values)
2152
2153 def test_parseBuildProps_duplicateOverride(self):
2154 build_prop = copy.deepcopy(self.odm_build_prop)
2155 build_prop.append(
2156 'import /odm/etc/build_${ro.boot.product.product_name}.prop')
2157
2158 build_std_prop = [
2159 'ro.product.odm.device=coral',
2160 'ro.product.odm.name=product1',
2161 ]
2162 build_pro_prop = [
2163 'ro.product.odm.device=coralpro',
2164 ]
2165
2166 product1_prop = [
2167 'ro.product.odm.name=product1',
2168 ]
2169
2170 product2_prop = [
2171 'ro.product.odm.name=product2',
2172 ]
2173
2174 input_file = self._BuildZipFile({
2175 'ODM/etc/build.prop': '\n'.join(build_prop),
2176 'ODM/etc/build_std.prop': '\n'.join(build_std_prop),
2177 'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop),
2178 'ODM/etc/build_product1.prop': '\n'.join(product1_prop),
2179 'ODM/etc/build_product2.prop': '\n'.join(product2_prop),
2180 })
2181
Kelvin Zhang928c2342020-09-22 16:15:57 -04002182 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002183 placeholder_values = {
2184 'ro.boot.product.device_name': 'std',
2185 'ro.boot.product.product_name': 'product1',
2186 }
2187 self.assertRaises(ValueError, common.PartitionBuildProps.FromInputFile,
2188 input_zip, 'odm', placeholder_values)