blob: 8c9655ad0da42534c17000c2496012551c92ad4f [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(
Kelvin Zhang37a42902022-10-26 12:49:03 -0700225 'product-brand/product-name/product-device:version-release/build-id/'
226 'version-incremental:build-type/build-tags', build_info.fingerprint)
Tianjiefdda51d2021-05-05 14:46:35 -0700227
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(
Kelvin Zhang37a42902022-10-26 12:49:03 -0700233 'product-brand/product-name/product-device:version-release/'
234 'legacy-build-id/version-incremental:build-type/build-tags',
235 build_info.fingerprint)
Tianjiefdda51d2021-05-05 14:46:35 -0700236
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(
Kelvin Zhang37a42902022-10-26 12:49:03 -0700244 'product-brand/product-name/product-device:version-release/'
245 'legacy-build-id.abcde123/version-incremental:build-type/build-tags',
246 build_info.fingerprint)
Tianjiefdda51d2021-05-05 14:46:35 -0700247
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,
Kelvin Zhang37a42902022-10-26 12:49:03 -0700379 'Invalid ro.product.property_source_order'):
Steven Laver8e2086e2020-04-27 16:26:31 -0700380 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)
Kelvin Zhang37a42902022-10-26 12:49:03 -0700462 zip_file.close()
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)
Kelvin Zhang37a42902022-10-26 12:49:03 -0700497 zip_file.close()
Tao Baof3282b42015-04-01 11:21:55 -0700498
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)
Kelvin Zhang37a42902022-10-26 12:49:03 -0700539 zip_file.close()
Tao Baof3282b42015-04-01 11:21:55 -0700540
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
Dan Albert8e0178d2015-01-27 15:53:15 -0800554 def test_ZipWrite(self):
555 file_contents = os.urandom(1024)
556 self._test_ZipWrite(file_contents)
557
558 def test_ZipWrite_with_opts(self):
559 file_contents = os.urandom(1024)
560 self._test_ZipWrite(file_contents, {
561 "arcname": "foobar",
562 "perms": 0o777,
563 "compress_type": zipfile.ZIP_DEFLATED,
564 })
Tao Baof3282b42015-04-01 11:21:55 -0700565 self._test_ZipWrite(file_contents, {
566 "arcname": "foobar",
567 "perms": 0o700,
568 "compress_type": zipfile.ZIP_STORED,
569 })
Dan Albert8e0178d2015-01-27 15:53:15 -0800570
571 def test_ZipWrite_large_file(self):
Tao Baof3282b42015-04-01 11:21:55 -0700572 file_contents = get_2gb_string()
Dan Albert8e0178d2015-01-27 15:53:15 -0800573 self._test_ZipWrite(file_contents, {
574 "compress_type": zipfile.ZIP_DEFLATED,
575 })
576
577 def test_ZipWrite_resets_ZIP64_LIMIT(self):
Kelvin Zhang37a42902022-10-26 12:49:03 -0700578 self._test_ZipWrite("")
Tao Baof3282b42015-04-01 11:21:55 -0700579
580 def test_ZipWriteStr(self):
581 random_string = os.urandom(1024)
582 # Passing arcname
583 self._test_ZipWriteStr("foo", random_string)
584
585 # Passing zinfo
586 zinfo = zipfile.ZipInfo(filename="foo")
587 self._test_ZipWriteStr(zinfo, random_string)
588
589 # Timestamp in the zinfo should be overwritten.
590 zinfo.date_time = (2015, 3, 1, 15, 30, 0)
591 self._test_ZipWriteStr(zinfo, random_string)
592
593 def test_ZipWriteStr_with_opts(self):
594 random_string = os.urandom(1024)
595 # Passing arcname
596 self._test_ZipWriteStr("foo", random_string, {
Tao Bao58c1b962015-05-20 09:32:18 -0700597 "perms": 0o700,
Tao Baof3282b42015-04-01 11:21:55 -0700598 "compress_type": zipfile.ZIP_DEFLATED,
599 })
Tao Bao58c1b962015-05-20 09:32:18 -0700600 self._test_ZipWriteStr("bar", random_string, {
Tao Baof3282b42015-04-01 11:21:55 -0700601 "compress_type": zipfile.ZIP_STORED,
602 })
603
604 # Passing zinfo
605 zinfo = zipfile.ZipInfo(filename="foo")
606 self._test_ZipWriteStr(zinfo, random_string, {
607 "compress_type": zipfile.ZIP_DEFLATED,
608 })
609 self._test_ZipWriteStr(zinfo, random_string, {
Tao Bao58c1b962015-05-20 09:32:18 -0700610 "perms": 0o600,
Tao Baof3282b42015-04-01 11:21:55 -0700611 "compress_type": zipfile.ZIP_STORED,
612 })
Tao Baoc1a1ec32019-06-18 16:29:37 -0700613 self._test_ZipWriteStr(zinfo, random_string, {
614 "perms": 0o000,
615 "compress_type": zipfile.ZIP_STORED,
616 })
Tao Baof3282b42015-04-01 11:21:55 -0700617
618 def test_ZipWriteStr_large_file(self):
619 # zipfile.writestr() doesn't work when the str size is over 2GiB even with
620 # the workaround. We will only test the case of writing a string into a
621 # large archive.
622 long_string = get_2gb_string()
623 short_string = os.urandom(1024)
624 self._test_ZipWriteStr_large_file(long_string, short_string, {
625 "compress_type": zipfile.ZIP_DEFLATED,
626 })
627
628 def test_ZipWriteStr_resets_ZIP64_LIMIT(self):
Kelvin Zhang37a42902022-10-26 12:49:03 -0700629 self._test_ZipWriteStr('foo', b'')
Tao Baof3282b42015-04-01 11:21:55 -0700630 zinfo = zipfile.ZipInfo(filename="foo")
Kelvin Zhang37a42902022-10-26 12:49:03 -0700631 self._test_ZipWriteStr(zinfo, b'')
Tao Bao58c1b962015-05-20 09:32:18 -0700632
633 def test_bug21309935(self):
634 zip_file = tempfile.NamedTemporaryFile(delete=False)
635 zip_file_name = zip_file.name
636 zip_file.close()
637
638 try:
639 random_string = os.urandom(1024)
Kelvin Zhang928c2342020-09-22 16:15:57 -0400640 zip_file = zipfile.ZipFile(zip_file_name, "w", allowZip64=True)
Tao Bao58c1b962015-05-20 09:32:18 -0700641 # Default perms should be 0o644 when passing the filename.
642 common.ZipWriteStr(zip_file, "foo", random_string)
643 # Honor the specified perms.
644 common.ZipWriteStr(zip_file, "bar", random_string, perms=0o755)
645 # The perms in zinfo should be untouched.
646 zinfo = zipfile.ZipInfo(filename="baz")
647 zinfo.external_attr = 0o740 << 16
648 common.ZipWriteStr(zip_file, zinfo, random_string)
649 # Explicitly specified perms has the priority.
650 zinfo = zipfile.ZipInfo(filename="qux")
651 zinfo.external_attr = 0o700 << 16
652 common.ZipWriteStr(zip_file, zinfo, random_string, perms=0o400)
Kelvin Zhang37a42902022-10-26 12:49:03 -0700653 zip_file.close()
Tao Bao58c1b962015-05-20 09:32:18 -0700654
Tao Bao31b08072017-11-08 15:50:59 -0800655 self._verify(zip_file, zip_file_name, "foo",
656 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700657 expected_mode=0o644)
Tao Bao31b08072017-11-08 15:50:59 -0800658 self._verify(zip_file, zip_file_name, "bar",
659 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700660 expected_mode=0o755)
Tao Bao31b08072017-11-08 15:50:59 -0800661 self._verify(zip_file, zip_file_name, "baz",
662 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700663 expected_mode=0o740)
Tao Bao31b08072017-11-08 15:50:59 -0800664 self._verify(zip_file, zip_file_name, "qux",
665 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700666 expected_mode=0o400)
667 finally:
668 os.remove(zip_file_name)
Tianjie Xu9c384d22017-06-20 17:00:55 -0700669
Tao Bao82490d32019-04-09 00:12:30 -0700670 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao89d7ab22017-12-14 17:05:33 -0800671 def test_ZipDelete(self):
672 zip_file = tempfile.NamedTemporaryFile(delete=False, suffix='.zip')
673 output_zip = zipfile.ZipFile(zip_file.name, 'w',
674 compression=zipfile.ZIP_DEFLATED)
675 with tempfile.NamedTemporaryFile() as entry_file:
676 entry_file.write(os.urandom(1024))
677 common.ZipWrite(output_zip, entry_file.name, arcname='Test1')
678 common.ZipWrite(output_zip, entry_file.name, arcname='Test2')
679 common.ZipWrite(output_zip, entry_file.name, arcname='Test3')
Kelvin Zhang37a42902022-10-26 12:49:03 -0700680 output_zip.close()
Tao Bao89d7ab22017-12-14 17:05:33 -0800681 zip_file.close()
682
683 try:
684 common.ZipDelete(zip_file.name, 'Test2')
Kelvin Zhang928c2342020-09-22 16:15:57 -0400685 with zipfile.ZipFile(zip_file.name, 'r', allowZip64=True) as check_zip:
Tao Bao89d7ab22017-12-14 17:05:33 -0800686 entries = check_zip.namelist()
687 self.assertTrue('Test1' in entries)
688 self.assertFalse('Test2' in entries)
689 self.assertTrue('Test3' in entries)
690
Tao Bao986ee862018-10-04 15:46:16 -0700691 self.assertRaises(
692 common.ExternalError, common.ZipDelete, zip_file.name, 'Test2')
Kelvin Zhang928c2342020-09-22 16:15:57 -0400693 with zipfile.ZipFile(zip_file.name, 'r', allowZip64=True) as check_zip:
Tao Bao89d7ab22017-12-14 17:05:33 -0800694 entries = check_zip.namelist()
695 self.assertTrue('Test1' in entries)
696 self.assertFalse('Test2' in entries)
697 self.assertTrue('Test3' in entries)
698
699 common.ZipDelete(zip_file.name, ['Test3'])
Kelvin Zhang928c2342020-09-22 16:15:57 -0400700 with zipfile.ZipFile(zip_file.name, 'r', allowZip64=True) as check_zip:
Tao Bao89d7ab22017-12-14 17:05:33 -0800701 entries = check_zip.namelist()
702 self.assertTrue('Test1' in entries)
703 self.assertFalse('Test2' in entries)
704 self.assertFalse('Test3' in entries)
705
706 common.ZipDelete(zip_file.name, ['Test1', 'Test2'])
Kelvin Zhang928c2342020-09-22 16:15:57 -0400707 with zipfile.ZipFile(zip_file.name, 'r', allowZip64=True) as check_zip:
Tao Bao89d7ab22017-12-14 17:05:33 -0800708 entries = check_zip.namelist()
709 self.assertFalse('Test1' in entries)
710 self.assertFalse('Test2' in entries)
711 self.assertFalse('Test3' in entries)
712 finally:
713 os.remove(zip_file.name)
714
Tao Bao0ff15de2019-03-20 11:26:06 -0700715 @staticmethod
716 def _test_UnzipTemp_createZipFile():
717 zip_file = common.MakeTempFile(suffix='.zip')
718 output_zip = zipfile.ZipFile(
719 zip_file, 'w', compression=zipfile.ZIP_DEFLATED)
720 contents = os.urandom(1024)
721 with tempfile.NamedTemporaryFile() as entry_file:
722 entry_file.write(contents)
723 common.ZipWrite(output_zip, entry_file.name, arcname='Test1')
724 common.ZipWrite(output_zip, entry_file.name, arcname='Test2')
725 common.ZipWrite(output_zip, entry_file.name, arcname='Foo3')
726 common.ZipWrite(output_zip, entry_file.name, arcname='Bar4')
727 common.ZipWrite(output_zip, entry_file.name, arcname='Dir5/Baz5')
Kelvin Zhang37a42902022-10-26 12:49:03 -0700728 output_zip.close()
729 output_zip.close()
Tao Bao0ff15de2019-03-20 11:26:06 -0700730 return zip_file
731
Tao Bao82490d32019-04-09 00:12:30 -0700732 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao0ff15de2019-03-20 11:26:06 -0700733 def test_UnzipTemp(self):
734 zip_file = self._test_UnzipTemp_createZipFile()
735 unzipped_dir = common.UnzipTemp(zip_file)
736 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
737 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
738 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
739 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
740 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
741
Tao Bao82490d32019-04-09 00:12:30 -0700742 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao0ff15de2019-03-20 11:26:06 -0700743 def test_UnzipTemp_withPatterns(self):
744 zip_file = self._test_UnzipTemp_createZipFile()
745
746 unzipped_dir = common.UnzipTemp(zip_file, ['Test1'])
747 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
748 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
749 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
750 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
751 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
752
753 unzipped_dir = common.UnzipTemp(zip_file, ['Test1', 'Foo3'])
754 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
755 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
756 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
757 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
758 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
759
760 unzipped_dir = common.UnzipTemp(zip_file, ['Test*', 'Foo3*'])
761 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
762 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
763 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
764 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
765 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
766
767 unzipped_dir = common.UnzipTemp(zip_file, ['*Test1', '*Baz*'])
768 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
769 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
770 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
771 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
772 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
773
774 def test_UnzipTemp_withEmptyPatterns(self):
775 zip_file = self._test_UnzipTemp_createZipFile()
776 unzipped_dir = common.UnzipTemp(zip_file, [])
777 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
778 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
779 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
780 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
781 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
782
Tao Bao82490d32019-04-09 00:12:30 -0700783 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao0ff15de2019-03-20 11:26:06 -0700784 def test_UnzipTemp_withPartiallyMatchingPatterns(self):
785 zip_file = self._test_UnzipTemp_createZipFile()
786 unzipped_dir = common.UnzipTemp(zip_file, ['Test*', 'Nonexistent*'])
787 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
788 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
789 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
790 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
791 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
792
793 def test_UnzipTemp_withNoMatchingPatterns(self):
794 zip_file = self._test_UnzipTemp_createZipFile()
795 unzipped_dir = common.UnzipTemp(zip_file, ['Foo4', 'Nonexistent*'])
796 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
797 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
798 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
799 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
800 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
801
Tao Bao89d7ab22017-12-14 17:05:33 -0800802
Tao Bao65b94e92018-10-11 21:57:26 -0700803class CommonApkUtilsTest(test_utils.ReleaseToolsTestCase):
Tao Bao818ddf52018-01-05 11:17:34 -0800804 """Tests the APK utils related functions."""
805
806 APKCERTS_TXT1 = (
807 'name="RecoveryLocalizer.apk" certificate="certs/devkey.x509.pem"'
808 ' private_key="certs/devkey.pk8"\n'
809 'name="Settings.apk"'
Dan Willemsen0ab1be62019-04-09 21:35:37 -0700810 ' certificate="build/make/target/product/security/platform.x509.pem"'
811 ' private_key="build/make/target/product/security/platform.pk8"\n'
Tao Bao818ddf52018-01-05 11:17:34 -0800812 'name="TV.apk" certificate="PRESIGNED" private_key=""\n'
813 )
814
815 APKCERTS_CERTMAP1 = {
Kelvin Zhang37a42902022-10-26 12:49:03 -0700816 'RecoveryLocalizer.apk': 'certs/devkey',
817 'Settings.apk': 'build/make/target/product/security/platform',
818 'TV.apk': 'PRESIGNED',
Tao Bao818ddf52018-01-05 11:17:34 -0800819 }
820
821 APKCERTS_TXT2 = (
822 'name="Compressed1.apk" certificate="certs/compressed1.x509.pem"'
823 ' private_key="certs/compressed1.pk8" compressed="gz"\n'
824 'name="Compressed2a.apk" certificate="certs/compressed2.x509.pem"'
825 ' private_key="certs/compressed2.pk8" compressed="gz"\n'
826 'name="Compressed2b.apk" certificate="certs/compressed2.x509.pem"'
827 ' private_key="certs/compressed2.pk8" compressed="gz"\n'
828 'name="Compressed3.apk" certificate="certs/compressed3.x509.pem"'
829 ' private_key="certs/compressed3.pk8" compressed="gz"\n'
830 )
831
832 APKCERTS_CERTMAP2 = {
Kelvin Zhang37a42902022-10-26 12:49:03 -0700833 'Compressed1.apk': 'certs/compressed1',
834 'Compressed2a.apk': 'certs/compressed2',
835 'Compressed2b.apk': 'certs/compressed2',
836 'Compressed3.apk': 'certs/compressed3',
Tao Bao818ddf52018-01-05 11:17:34 -0800837 }
838
839 APKCERTS_TXT3 = (
840 'name="Compressed4.apk" certificate="certs/compressed4.x509.pem"'
841 ' private_key="certs/compressed4.pk8" compressed="xz"\n'
842 )
843
844 APKCERTS_CERTMAP3 = {
Kelvin Zhang37a42902022-10-26 12:49:03 -0700845 'Compressed4.apk': 'certs/compressed4',
Tao Bao818ddf52018-01-05 11:17:34 -0800846 }
847
Bill Peckham5c7b0342020-04-03 15:36:23 -0700848 # Test parsing with no optional fields, both optional fields, and only the
849 # partition optional field.
850 APKCERTS_TXT4 = (
851 'name="RecoveryLocalizer.apk" certificate="certs/devkey.x509.pem"'
852 ' private_key="certs/devkey.pk8"\n'
853 'name="Settings.apk"'
854 ' certificate="build/make/target/product/security/platform.x509.pem"'
855 ' private_key="build/make/target/product/security/platform.pk8"'
856 ' compressed="gz" partition="system"\n'
857 'name="TV.apk" certificate="PRESIGNED" private_key=""'
858 ' partition="product"\n'
859 )
860
861 APKCERTS_CERTMAP4 = {
Kelvin Zhang37a42902022-10-26 12:49:03 -0700862 'RecoveryLocalizer.apk': 'certs/devkey',
863 'Settings.apk': 'build/make/target/product/security/platform',
864 'TV.apk': 'PRESIGNED',
Bill Peckham5c7b0342020-04-03 15:36:23 -0700865 }
866
Tao Bao17e4e612018-02-16 17:12:54 -0800867 def setUp(self):
868 self.testdata_dir = test_utils.get_testdata_dir()
869
Tao Bao818ddf52018-01-05 11:17:34 -0800870 @staticmethod
871 def _write_apkcerts_txt(apkcerts_txt, additional=None):
872 if additional is None:
873 additional = []
874 target_files = common.MakeTempFile(suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -0400875 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800876 target_files_zip.writestr('META/apkcerts.txt', apkcerts_txt)
877 for entry in additional:
878 target_files_zip.writestr(entry, '')
879 return target_files
880
881 def test_ReadApkCerts_NoncompressedApks(self):
882 target_files = self._write_apkcerts_txt(self.APKCERTS_TXT1)
Kelvin Zhang928c2342020-09-22 16:15:57 -0400883 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800884 certmap, ext = common.ReadApkCerts(input_zip)
885
886 self.assertDictEqual(self.APKCERTS_CERTMAP1, certmap)
887 self.assertIsNone(ext)
888
889 def test_ReadApkCerts_CompressedApks(self):
890 # We have "installed" Compressed1.apk.gz only. Note that Compressed3.apk is
891 # not stored in '.gz' format, so it shouldn't be considered as installed.
892 target_files = self._write_apkcerts_txt(
893 self.APKCERTS_TXT2,
894 ['Compressed1.apk.gz', 'Compressed3.apk'])
895
Kelvin Zhang928c2342020-09-22 16:15:57 -0400896 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800897 certmap, ext = common.ReadApkCerts(input_zip)
898
899 self.assertDictEqual(self.APKCERTS_CERTMAP2, certmap)
900 self.assertEqual('.gz', ext)
901
902 # Alternative case with '.xz'.
903 target_files = self._write_apkcerts_txt(
904 self.APKCERTS_TXT3, ['Compressed4.apk.xz'])
905
Kelvin Zhang928c2342020-09-22 16:15:57 -0400906 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800907 certmap, ext = common.ReadApkCerts(input_zip)
908
909 self.assertDictEqual(self.APKCERTS_CERTMAP3, certmap)
910 self.assertEqual('.xz', ext)
911
912 def test_ReadApkCerts_CompressedAndNoncompressedApks(self):
913 target_files = self._write_apkcerts_txt(
914 self.APKCERTS_TXT1 + self.APKCERTS_TXT2,
915 ['Compressed1.apk.gz', 'Compressed3.apk'])
916
Kelvin Zhang928c2342020-09-22 16:15:57 -0400917 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800918 certmap, ext = common.ReadApkCerts(input_zip)
919
920 certmap_merged = self.APKCERTS_CERTMAP1.copy()
921 certmap_merged.update(self.APKCERTS_CERTMAP2)
922 self.assertDictEqual(certmap_merged, certmap)
923 self.assertEqual('.gz', ext)
924
925 def test_ReadApkCerts_MultipleCompressionMethods(self):
926 target_files = self._write_apkcerts_txt(
927 self.APKCERTS_TXT2 + self.APKCERTS_TXT3,
928 ['Compressed1.apk.gz', 'Compressed4.apk.xz'])
929
Kelvin Zhang928c2342020-09-22 16:15:57 -0400930 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800931 self.assertRaises(ValueError, common.ReadApkCerts, input_zip)
932
933 def test_ReadApkCerts_MismatchingKeys(self):
934 malformed_apkcerts_txt = (
935 'name="App1.apk" certificate="certs/cert1.x509.pem"'
936 ' private_key="certs/cert2.pk8"\n'
937 )
938 target_files = self._write_apkcerts_txt(malformed_apkcerts_txt)
939
Kelvin Zhang928c2342020-09-22 16:15:57 -0400940 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800941 self.assertRaises(ValueError, common.ReadApkCerts, input_zip)
942
Bill Peckham5c7b0342020-04-03 15:36:23 -0700943 def test_ReadApkCerts_WithWithoutOptionalFields(self):
944 target_files = self._write_apkcerts_txt(self.APKCERTS_TXT4)
Kelvin Zhang928c2342020-09-22 16:15:57 -0400945 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Bill Peckham5c7b0342020-04-03 15:36:23 -0700946 certmap, ext = common.ReadApkCerts(input_zip)
947
948 self.assertDictEqual(self.APKCERTS_CERTMAP4, certmap)
949 self.assertIsNone(ext)
950
Tao Bao04e1f012018-02-04 12:13:35 -0800951 def test_ExtractPublicKey(self):
Tao Bao17e4e612018-02-16 17:12:54 -0800952 cert = os.path.join(self.testdata_dir, 'testkey.x509.pem')
953 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
Tao Baoda30cfa2017-12-01 16:19:46 -0800954 with open(pubkey) as pubkey_fp:
Tao Bao04e1f012018-02-04 12:13:35 -0800955 self.assertEqual(pubkey_fp.read(), common.ExtractPublicKey(cert))
956
957 def test_ExtractPublicKey_invalidInput(self):
Tao Bao17e4e612018-02-16 17:12:54 -0800958 wrong_input = os.path.join(self.testdata_dir, 'testkey.pk8')
Tao Bao04e1f012018-02-04 12:13:35 -0800959 self.assertRaises(AssertionError, common.ExtractPublicKey, wrong_input)
960
Tao Bao82490d32019-04-09 00:12:30 -0700961 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao2cc0ca12019-03-15 10:44:43 -0700962 def test_ExtractAvbPublicKey(self):
963 privkey = os.path.join(self.testdata_dir, 'testkey.key')
964 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
Tao Bao1ac886e2019-06-26 11:58:22 -0700965 extracted_from_privkey = common.ExtractAvbPublicKey('avbtool', privkey)
966 extracted_from_pubkey = common.ExtractAvbPublicKey('avbtool', pubkey)
967 with open(extracted_from_privkey, 'rb') as privkey_fp, \
Kelvin Zhang37a42902022-10-26 12:49:03 -0700968 open(extracted_from_pubkey, 'rb') as pubkey_fp:
Tao Bao2cc0ca12019-03-15 10:44:43 -0700969 self.assertEqual(privkey_fp.read(), pubkey_fp.read())
970
Tao Bao17e4e612018-02-16 17:12:54 -0800971 def test_ParseCertificate(self):
972 cert = os.path.join(self.testdata_dir, 'testkey.x509.pem')
973
974 cmd = ['openssl', 'x509', '-in', cert, '-outform', 'DER']
Tao Baoda30cfa2017-12-01 16:19:46 -0800975 proc = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
976 universal_newlines=False)
Tao Bao17e4e612018-02-16 17:12:54 -0800977 expected, _ = proc.communicate()
978 self.assertEqual(0, proc.returncode)
979
980 with open(cert) as cert_fp:
981 actual = common.ParseCertificate(cert_fp.read())
982 self.assertEqual(expected, actual)
983
Tao Bao82490d32019-04-09 00:12:30 -0700984 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700985 def test_GetMinSdkVersion(self):
986 test_app = os.path.join(self.testdata_dir, 'TestApp.apk')
987 self.assertEqual('24', common.GetMinSdkVersion(test_app))
988
Tao Bao82490d32019-04-09 00:12:30 -0700989 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700990 def test_GetMinSdkVersion_invalidInput(self):
991 self.assertRaises(
992 common.ExternalError, common.GetMinSdkVersion, 'does-not-exist.apk')
993
Tao Bao82490d32019-04-09 00:12:30 -0700994 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700995 def test_GetMinSdkVersionInt(self):
996 test_app = os.path.join(self.testdata_dir, 'TestApp.apk')
997 self.assertEqual(24, common.GetMinSdkVersionInt(test_app, {}))
998
Tao Bao82490d32019-04-09 00:12:30 -0700999 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -07001000 def test_GetMinSdkVersionInt_invalidInput(self):
1001 self.assertRaises(
1002 common.ExternalError, common.GetMinSdkVersionInt, 'does-not-exist.apk',
1003 {})
1004
Tao Bao818ddf52018-01-05 11:17:34 -08001005
Tao Bao65b94e92018-10-11 21:57:26 -07001006class CommonUtilsTest(test_utils.ReleaseToolsTestCase):
Tao Baofc7e0e02018-02-13 13:54:02 -08001007
Tao Bao02a08592018-07-22 12:40:45 -07001008 def setUp(self):
1009 self.testdata_dir = test_utils.get_testdata_dir()
1010
Tao Bao82490d32019-04-09 00:12:30 -07001011 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001012 def test_GetSparseImage_emptyBlockMapFile(self):
1013 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001014 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baofc7e0e02018-02-13 13:54:02 -08001015 target_files_zip.write(
1016 test_utils.construct_sparse_image([
1017 (0xCAC1, 6),
1018 (0xCAC3, 3),
1019 (0xCAC1, 4)]),
1020 arcname='IMAGES/system.img')
1021 target_files_zip.writestr('IMAGES/system.map', '')
1022 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 8))
1023 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1024
Tao Baodba59ee2018-01-09 13:21:02 -08001025 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001026 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baodba59ee2018-01-09 13:21:02 -08001027 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001028
1029 self.assertDictEqual(
1030 {
1031 '__COPY': RangeSet("0"),
1032 '__NONZERO-0': RangeSet("1-5 9-12"),
1033 },
1034 sparse_image.file_map)
1035
Daniel Norman21c34f72020-11-11 17:25:50 -08001036 def test_PartitionMapFromTargetFiles(self):
1037 target_files_dir = common.MakeTempDir()
1038 os.makedirs(os.path.join(target_files_dir, 'SYSTEM'))
1039 os.makedirs(os.path.join(target_files_dir, 'SYSTEM', 'vendor'))
1040 os.makedirs(os.path.join(target_files_dir, 'PRODUCT'))
1041 os.makedirs(os.path.join(target_files_dir, 'SYSTEM', 'product'))
1042 os.makedirs(os.path.join(target_files_dir, 'SYSTEM', 'vendor', 'odm'))
1043 os.makedirs(os.path.join(target_files_dir, 'VENDOR_DLKM'))
1044 partition_map = common.PartitionMapFromTargetFiles(target_files_dir)
1045 self.assertDictEqual(
1046 partition_map,
1047 {
1048 'system': 'SYSTEM',
1049 'vendor': 'SYSTEM/vendor',
1050 # Prefer PRODUCT over SYSTEM/product
1051 'product': 'PRODUCT',
1052 'odm': 'SYSTEM/vendor/odm',
1053 'vendor_dlkm': 'VENDOR_DLKM',
1054 # No system_ext or odm_dlkm
1055 })
1056
Daniel Normand3351562020-10-29 12:33:11 -07001057 def test_SharedUidPartitionViolations(self):
1058 uid_dict = {
1059 'android.uid.phone': {
1060 'system': ['system_phone.apk'],
1061 'system_ext': ['system_ext_phone.apk'],
1062 },
1063 'android.uid.wifi': {
1064 'vendor': ['vendor_wifi.apk'],
1065 'odm': ['odm_wifi.apk'],
1066 },
1067 }
1068 errors = common.SharedUidPartitionViolations(
1069 uid_dict, [('system', 'system_ext'), ('vendor', 'odm')])
1070 self.assertEqual(errors, [])
1071
1072 def test_SharedUidPartitionViolations_Violation(self):
1073 uid_dict = {
1074 'android.uid.phone': {
1075 'system': ['system_phone.apk'],
1076 'vendor': ['vendor_phone.apk'],
1077 },
1078 }
1079 errors = common.SharedUidPartitionViolations(
1080 uid_dict, [('system', 'system_ext'), ('vendor', 'odm')])
1081 self.assertIn(
1082 ('APK sharedUserId "android.uid.phone" found across partition groups '
1083 'in partitions "system,vendor"'), errors)
1084
Tao Baob2de7d92019-04-10 10:01:47 -07001085 def test_GetSparseImage_missingImageFile(self):
Tao Baofc7e0e02018-02-13 13:54:02 -08001086 self.assertRaises(
Tao Baob2de7d92019-04-10 10:01:47 -07001087 AssertionError, common.GetSparseImage, 'system2', self.testdata_dir,
1088 None, False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001089 self.assertRaises(
Tao Baob2de7d92019-04-10 10:01:47 -07001090 AssertionError, common.GetSparseImage, 'unknown', self.testdata_dir,
1091 None, False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001092
Tao Bao82490d32019-04-09 00:12:30 -07001093 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001094 def test_GetSparseImage_missingBlockMapFile(self):
1095 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001096 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baofc7e0e02018-02-13 13:54:02 -08001097 target_files_zip.write(
1098 test_utils.construct_sparse_image([
1099 (0xCAC1, 6),
1100 (0xCAC3, 3),
1101 (0xCAC1, 4)]),
1102 arcname='IMAGES/system.img')
1103 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 8))
1104 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1105
Tao Baodba59ee2018-01-09 13:21:02 -08001106 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001107 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baodba59ee2018-01-09 13:21:02 -08001108 self.assertRaises(
1109 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
1110 False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001111
Tao Bao82490d32019-04-09 00:12:30 -07001112 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001113 def test_GetSparseImage_sharedBlocks_notAllowed(self):
1114 """Tests the case of having overlapping blocks but disallowed."""
1115 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001116 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baofc7e0e02018-02-13 13:54:02 -08001117 target_files_zip.write(
1118 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1119 arcname='IMAGES/system.img')
1120 # Block 10 is shared between two files.
1121 target_files_zip.writestr(
1122 'IMAGES/system.map',
1123 '\n'.join([
1124 '/system/file1 1-5 9-10',
1125 '/system/file2 10-12']))
1126 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1127 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1128
Tao Baodba59ee2018-01-09 13:21:02 -08001129 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001130 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baodba59ee2018-01-09 13:21:02 -08001131 self.assertRaises(
1132 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
1133 False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001134
Tao Bao82490d32019-04-09 00:12:30 -07001135 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001136 def test_GetSparseImage_sharedBlocks_allowed(self):
1137 """Tests the case for target using BOARD_EXT4_SHARE_DUP_BLOCKS := true."""
1138 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001139 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baofc7e0e02018-02-13 13:54:02 -08001140 # Construct an image with a care_map of "0-5 9-12".
1141 target_files_zip.write(
1142 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1143 arcname='IMAGES/system.img')
1144 # Block 10 is shared between two files.
1145 target_files_zip.writestr(
1146 'IMAGES/system.map',
1147 '\n'.join([
1148 '/system/file1 1-5 9-10',
1149 '/system/file2 10-12']))
1150 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1151 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1152
Tao Baodba59ee2018-01-09 13:21:02 -08001153 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001154 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baodba59ee2018-01-09 13:21:02 -08001155 sparse_image = common.GetSparseImage('system', tempdir, input_zip, True)
Tao Baofc7e0e02018-02-13 13:54:02 -08001156
1157 self.assertDictEqual(
1158 {
1159 '__COPY': RangeSet("0"),
1160 '__NONZERO-0': RangeSet("6-8 13-15"),
1161 '/system/file1': RangeSet("1-5 9-10"),
1162 '/system/file2': RangeSet("11-12"),
1163 },
1164 sparse_image.file_map)
1165
1166 # '/system/file2' should be marked with 'uses_shared_blocks', but not with
1167 # 'incomplete'.
1168 self.assertTrue(
1169 sparse_image.file_map['/system/file2'].extra['uses_shared_blocks'])
1170 self.assertNotIn(
1171 'incomplete', sparse_image.file_map['/system/file2'].extra)
1172
Tao Baoa264fef2019-10-06 21:55:20 -07001173 # '/system/file1' will only contain one field -- a copy of the input text.
1174 self.assertEqual(1, len(sparse_image.file_map['/system/file1'].extra))
1175
1176 # Meta entries should not have any extra tag.
Tao Baofc7e0e02018-02-13 13:54:02 -08001177 self.assertFalse(sparse_image.file_map['__COPY'].extra)
1178 self.assertFalse(sparse_image.file_map['__NONZERO-0'].extra)
Tao Baofc7e0e02018-02-13 13:54:02 -08001179
Tao Bao82490d32019-04-09 00:12:30 -07001180 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001181 def test_GetSparseImage_incompleteRanges(self):
1182 """Tests the case of ext4 images with holes."""
1183 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001184 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baofc7e0e02018-02-13 13:54:02 -08001185 target_files_zip.write(
1186 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1187 arcname='IMAGES/system.img')
1188 target_files_zip.writestr(
1189 'IMAGES/system.map',
1190 '\n'.join([
1191 '/system/file1 1-5 9-10',
1192 '/system/file2 11-12']))
1193 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1194 # '/system/file2' has less blocks listed (2) than actual (3).
1195 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1196
Tao Baodba59ee2018-01-09 13:21:02 -08001197 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001198 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baodba59ee2018-01-09 13:21:02 -08001199 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001200
Tao Baoa264fef2019-10-06 21:55:20 -07001201 self.assertEqual(
1202 '1-5 9-10',
1203 sparse_image.file_map['/system/file1'].extra['text_str'])
Tao Baofc7e0e02018-02-13 13:54:02 -08001204 self.assertTrue(sparse_image.file_map['/system/file2'].extra['incomplete'])
1205
Tao Bao82490d32019-04-09 00:12:30 -07001206 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -07001207 def test_GetSparseImage_systemRootImage_filenameWithExtraLeadingSlash(self):
1208 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001209 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001210 target_files_zip.write(
1211 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1212 arcname='IMAGES/system.img')
1213 target_files_zip.writestr(
1214 'IMAGES/system.map',
1215 '\n'.join([
1216 '//system/file1 1-5 9-10',
1217 '//system/file2 11-12',
1218 '/system/app/file3 13-15']))
1219 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1220 # '/system/file2' has less blocks listed (2) than actual (3).
1221 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1222 # '/system/app/file3' has less blocks listed (3) than actual (4).
1223 target_files_zip.writestr('SYSTEM/app/file3', os.urandom(4096 * 4))
1224
1225 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001226 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001227 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
1228
Tao Baoa264fef2019-10-06 21:55:20 -07001229 self.assertEqual(
1230 '1-5 9-10',
1231 sparse_image.file_map['//system/file1'].extra['text_str'])
Kelvin Zhang37a42902022-10-26 12:49:03 -07001232 self.assertTrue(
1233 sparse_image.file_map['//system/file2'].extra['incomplete'])
Tao Baod3554e62018-07-10 15:31:22 -07001234 self.assertTrue(
1235 sparse_image.file_map['/system/app/file3'].extra['incomplete'])
1236
Tao Bao82490d32019-04-09 00:12:30 -07001237 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -07001238 def test_GetSparseImage_systemRootImage_nonSystemFiles(self):
1239 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001240 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001241 target_files_zip.write(
1242 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1243 arcname='IMAGES/system.img')
1244 target_files_zip.writestr(
1245 'IMAGES/system.map',
1246 '\n'.join([
1247 '//system/file1 1-5 9-10',
1248 '//init.rc 13-15']))
1249 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1250 # '/init.rc' has less blocks listed (3) than actual (4).
1251 target_files_zip.writestr('ROOT/init.rc', os.urandom(4096 * 4))
1252
1253 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001254 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001255 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
1256
Tao Baoa264fef2019-10-06 21:55:20 -07001257 self.assertEqual(
1258 '1-5 9-10',
1259 sparse_image.file_map['//system/file1'].extra['text_str'])
Tao Baod3554e62018-07-10 15:31:22 -07001260 self.assertTrue(sparse_image.file_map['//init.rc'].extra['incomplete'])
1261
Tao Bao82490d32019-04-09 00:12:30 -07001262 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -07001263 def test_GetSparseImage_fileNotFound(self):
1264 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001265 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001266 target_files_zip.write(
1267 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1268 arcname='IMAGES/system.img')
1269 target_files_zip.writestr(
1270 'IMAGES/system.map',
1271 '\n'.join([
1272 '//system/file1 1-5 9-10',
1273 '//system/file2 11-12']))
1274 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1275
1276 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001277 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001278 self.assertRaises(
1279 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
1280 False)
1281
Tao Bao82490d32019-04-09 00:12:30 -07001282 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001283 def test_GetAvbChainedPartitionArg(self):
1284 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
1285 info_dict = {
1286 'avb_avbtool': 'avbtool',
1287 'avb_system_key_path': pubkey,
1288 'avb_system_rollback_index_location': 2,
1289 }
1290 args = common.GetAvbChainedPartitionArg('system', info_dict).split(':')
1291 self.assertEqual(3, len(args))
1292 self.assertEqual('system', args[0])
1293 self.assertEqual('2', args[1])
1294 self.assertTrue(os.path.exists(args[2]))
1295
Tao Bao82490d32019-04-09 00:12:30 -07001296 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001297 def test_GetAvbChainedPartitionArg_withPrivateKey(self):
1298 key = os.path.join(self.testdata_dir, 'testkey.key')
1299 info_dict = {
1300 'avb_avbtool': 'avbtool',
1301 'avb_product_key_path': key,
1302 'avb_product_rollback_index_location': 2,
1303 }
1304 args = common.GetAvbChainedPartitionArg('product', info_dict).split(':')
1305 self.assertEqual(3, len(args))
1306 self.assertEqual('product', args[0])
1307 self.assertEqual('2', args[1])
1308 self.assertTrue(os.path.exists(args[2]))
1309
Tao Bao82490d32019-04-09 00:12:30 -07001310 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001311 def test_GetAvbChainedPartitionArg_withSpecifiedKey(self):
1312 info_dict = {
1313 'avb_avbtool': 'avbtool',
1314 'avb_system_key_path': 'does-not-exist',
1315 'avb_system_rollback_index_location': 2,
1316 }
1317 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
1318 args = common.GetAvbChainedPartitionArg(
1319 'system', info_dict, pubkey).split(':')
1320 self.assertEqual(3, len(args))
1321 self.assertEqual('system', args[0])
1322 self.assertEqual('2', args[1])
1323 self.assertTrue(os.path.exists(args[2]))
1324
Tao Bao82490d32019-04-09 00:12:30 -07001325 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001326 def test_GetAvbChainedPartitionArg_invalidKey(self):
1327 pubkey = os.path.join(self.testdata_dir, 'testkey_with_passwd.x509.pem')
1328 info_dict = {
1329 'avb_avbtool': 'avbtool',
1330 'avb_system_key_path': pubkey,
1331 'avb_system_rollback_index_location': 2,
1332 }
1333 self.assertRaises(
Tao Bao986ee862018-10-04 15:46:16 -07001334 common.ExternalError, common.GetAvbChainedPartitionArg, 'system',
1335 info_dict)
Tao Bao02a08592018-07-22 12:40:45 -07001336
Tao Baoa57ab9f2018-08-24 12:08:38 -07001337 INFO_DICT_DEFAULT = {
1338 'recovery_api_version': 3,
1339 'fstab_version': 2,
1340 'system_root_image': 'true',
Kelvin Zhang37a42902022-10-26 12:49:03 -07001341 'no_recovery': 'true',
Tao Baoa57ab9f2018-08-24 12:08:38 -07001342 'recovery_as_boot': 'true',
1343 }
1344
Daniel Norman4cc9df62019-07-18 10:11:07 -07001345 def test_LoadListFromFile(self):
1346 file_path = os.path.join(self.testdata_dir,
1347 'merge_config_framework_item_list')
1348 contents = common.LoadListFromFile(file_path)
1349 expected_contents = [
1350 'META/apkcerts.txt',
1351 'META/filesystem_config.txt',
1352 'META/root_filesystem_config.txt',
1353 'META/system_manifest.xml',
1354 'META/system_matrix.xml',
1355 'META/update_engine_config.txt',
1356 'PRODUCT/*',
1357 'ROOT/*',
1358 'SYSTEM/*',
1359 ]
1360 self.assertEqual(sorted(contents), sorted(expected_contents))
1361
Tao Baoa57ab9f2018-08-24 12:08:38 -07001362 @staticmethod
1363 def _test_LoadInfoDict_createTargetFiles(info_dict, fstab_path):
1364 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001365 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001366 info_values = ''.join(
Tao Baoda30cfa2017-12-01 16:19:46 -08001367 ['{}={}\n'.format(k, v) for k, v in sorted(info_dict.items())])
Tao Baoa57ab9f2018-08-24 12:08:38 -07001368 common.ZipWriteStr(target_files_zip, 'META/misc_info.txt', info_values)
1369
1370 FSTAB_TEMPLATE = "/dev/block/system {} ext4 ro,barrier=1 defaults"
1371 if info_dict.get('system_root_image') == 'true':
1372 fstab_values = FSTAB_TEMPLATE.format('/')
1373 else:
1374 fstab_values = FSTAB_TEMPLATE.format('/system')
1375 common.ZipWriteStr(target_files_zip, fstab_path, fstab_values)
Tao Bao410ad8b2018-08-24 12:08:38 -07001376
1377 common.ZipWriteStr(
1378 target_files_zip, 'META/file_contexts', 'file-contexts')
Tao Baoa57ab9f2018-08-24 12:08:38 -07001379 return target_files
1380
1381 def test_LoadInfoDict(self):
1382 target_files = self._test_LoadInfoDict_createTargetFiles(
1383 self.INFO_DICT_DEFAULT,
1384 'BOOT/RAMDISK/system/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001385 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001386 loaded_dict = common.LoadInfoDict(target_files_zip)
1387 self.assertEqual(3, loaded_dict['recovery_api_version'])
1388 self.assertEqual(2, loaded_dict['fstab_version'])
1389 self.assertIn('/', loaded_dict['fstab'])
1390 self.assertIn('/system', loaded_dict['fstab'])
1391
1392 def test_LoadInfoDict_legacyRecoveryFstabPath(self):
1393 target_files = self._test_LoadInfoDict_createTargetFiles(
1394 self.INFO_DICT_DEFAULT,
1395 'BOOT/RAMDISK/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001396 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001397 loaded_dict = common.LoadInfoDict(target_files_zip)
1398 self.assertEqual(3, loaded_dict['recovery_api_version'])
1399 self.assertEqual(2, loaded_dict['fstab_version'])
1400 self.assertIn('/', loaded_dict['fstab'])
1401 self.assertIn('/system', loaded_dict['fstab'])
1402
Tao Bao82490d32019-04-09 00:12:30 -07001403 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoa57ab9f2018-08-24 12:08:38 -07001404 def test_LoadInfoDict_dirInput(self):
1405 target_files = self._test_LoadInfoDict_createTargetFiles(
1406 self.INFO_DICT_DEFAULT,
1407 'BOOT/RAMDISK/system/etc/recovery.fstab')
1408 unzipped = common.UnzipTemp(target_files)
1409 loaded_dict = common.LoadInfoDict(unzipped)
1410 self.assertEqual(3, loaded_dict['recovery_api_version'])
1411 self.assertEqual(2, loaded_dict['fstab_version'])
1412 self.assertIn('/', loaded_dict['fstab'])
1413 self.assertIn('/system', loaded_dict['fstab'])
1414
Tao Bao82490d32019-04-09 00:12:30 -07001415 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoa57ab9f2018-08-24 12:08:38 -07001416 def test_LoadInfoDict_dirInput_legacyRecoveryFstabPath(self):
1417 target_files = self._test_LoadInfoDict_createTargetFiles(
1418 self.INFO_DICT_DEFAULT,
1419 'BOOT/RAMDISK/system/etc/recovery.fstab')
1420 unzipped = common.UnzipTemp(target_files)
1421 loaded_dict = common.LoadInfoDict(unzipped)
1422 self.assertEqual(3, loaded_dict['recovery_api_version'])
1423 self.assertEqual(2, loaded_dict['fstab_version'])
1424 self.assertIn('/', loaded_dict['fstab'])
1425 self.assertIn('/system', loaded_dict['fstab'])
1426
1427 def test_LoadInfoDict_systemRootImageFalse(self):
1428 # Devices not using system-as-root nor recovery-as-boot. Non-A/B devices
1429 # launched prior to P will likely have this config.
1430 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
1431 del info_dict['no_recovery']
1432 del info_dict['system_root_image']
1433 del info_dict['recovery_as_boot']
1434 target_files = self._test_LoadInfoDict_createTargetFiles(
1435 info_dict,
1436 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001437 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001438 loaded_dict = common.LoadInfoDict(target_files_zip)
1439 self.assertEqual(3, loaded_dict['recovery_api_version'])
1440 self.assertEqual(2, loaded_dict['fstab_version'])
1441 self.assertNotIn('/', loaded_dict['fstab'])
1442 self.assertIn('/system', loaded_dict['fstab'])
1443
1444 def test_LoadInfoDict_recoveryAsBootFalse(self):
1445 # Devices using system-as-root, but with standalone recovery image. Non-A/B
1446 # devices launched since P will likely have this config.
1447 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
1448 del info_dict['no_recovery']
1449 del info_dict['recovery_as_boot']
1450 target_files = self._test_LoadInfoDict_createTargetFiles(
1451 info_dict,
1452 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001453 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001454 loaded_dict = common.LoadInfoDict(target_files_zip)
1455 self.assertEqual(3, loaded_dict['recovery_api_version'])
1456 self.assertEqual(2, loaded_dict['fstab_version'])
1457 self.assertIn('/', loaded_dict['fstab'])
1458 self.assertIn('/system', loaded_dict['fstab'])
1459
1460 def test_LoadInfoDict_noRecoveryTrue(self):
1461 # Device doesn't have a recovery partition at all.
1462 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
1463 del info_dict['recovery_as_boot']
1464 target_files = self._test_LoadInfoDict_createTargetFiles(
1465 info_dict,
1466 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001467 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001468 loaded_dict = common.LoadInfoDict(target_files_zip)
1469 self.assertEqual(3, loaded_dict['recovery_api_version'])
1470 self.assertEqual(2, loaded_dict['fstab_version'])
1471 self.assertIsNone(loaded_dict['fstab'])
1472
Tao Bao82490d32019-04-09 00:12:30 -07001473 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao410ad8b2018-08-24 12:08:38 -07001474 def test_LoadInfoDict_missingMetaMiscInfoTxt(self):
1475 target_files = self._test_LoadInfoDict_createTargetFiles(
1476 self.INFO_DICT_DEFAULT,
1477 'BOOT/RAMDISK/system/etc/recovery.fstab')
1478 common.ZipDelete(target_files, 'META/misc_info.txt')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001479 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Bao410ad8b2018-08-24 12:08:38 -07001480 self.assertRaises(ValueError, common.LoadInfoDict, target_files_zip)
1481
Tao Bao82490d32019-04-09 00:12:30 -07001482 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao410ad8b2018-08-24 12:08:38 -07001483 def test_LoadInfoDict_repacking(self):
1484 target_files = self._test_LoadInfoDict_createTargetFiles(
1485 self.INFO_DICT_DEFAULT,
1486 'BOOT/RAMDISK/system/etc/recovery.fstab')
1487 unzipped = common.UnzipTemp(target_files)
1488 loaded_dict = common.LoadInfoDict(unzipped, True)
1489 self.assertEqual(3, loaded_dict['recovery_api_version'])
1490 self.assertEqual(2, loaded_dict['fstab_version'])
1491 self.assertIn('/', loaded_dict['fstab'])
1492 self.assertIn('/system', loaded_dict['fstab'])
1493 self.assertEqual(
1494 os.path.join(unzipped, 'ROOT'), loaded_dict['root_dir'])
1495 self.assertEqual(
1496 os.path.join(unzipped, 'META', 'root_filesystem_config.txt'),
1497 loaded_dict['root_fs_config'])
1498
1499 def test_LoadInfoDict_repackingWithZipFileInput(self):
1500 target_files = self._test_LoadInfoDict_createTargetFiles(
1501 self.INFO_DICT_DEFAULT,
1502 'BOOT/RAMDISK/system/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001503 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Bao410ad8b2018-08-24 12:08:38 -07001504 self.assertRaises(
1505 AssertionError, common.LoadInfoDict, target_files_zip, True)
1506
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001507 def test_MergeDynamicPartitionInfoDicts_ReturnsMergedDict(self):
1508 framework_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001509 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001510 'super_partition_groups': 'group_a',
1511 'dynamic_partition_list': 'system',
Daniel Norman55417142019-11-25 16:04:36 -08001512 'super_group_a_partition_list': 'system',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001513 }
1514 vendor_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001515 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001516 'super_partition_groups': 'group_a group_b',
1517 'dynamic_partition_list': 'vendor product',
Daniel Normanb0c75912020-09-24 14:30:21 -07001518 'super_block_devices': 'super',
1519 'super_super_device_size': '3000',
Daniel Norman55417142019-11-25 16:04:36 -08001520 'super_group_a_partition_list': 'vendor',
1521 'super_group_a_group_size': '1000',
1522 'super_group_b_partition_list': 'product',
1523 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001524 }
1525 merged_dict = common.MergeDynamicPartitionInfoDicts(
1526 framework_dict=framework_dict,
Daniel Norman55417142019-11-25 16:04:36 -08001527 vendor_dict=vendor_dict)
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001528 expected_merged_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001529 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001530 'super_partition_groups': 'group_a group_b',
Daniel Normanb0c75912020-09-24 14:30:21 -07001531 'dynamic_partition_list': 'product system vendor',
1532 'super_block_devices': 'super',
1533 'super_super_device_size': '3000',
Daniel Norman55417142019-11-25 16:04:36 -08001534 'super_group_a_partition_list': 'system vendor',
1535 'super_group_a_group_size': '1000',
1536 'super_group_b_partition_list': 'product',
1537 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001538 }
1539 self.assertEqual(merged_dict, expected_merged_dict)
1540
1541 def test_MergeDynamicPartitionInfoDicts_IgnoringFrameworkGroupSize(self):
1542 framework_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001543 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001544 'super_partition_groups': 'group_a',
1545 'dynamic_partition_list': 'system',
Daniel Norman55417142019-11-25 16:04:36 -08001546 'super_group_a_partition_list': 'system',
1547 'super_group_a_group_size': '5000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001548 }
1549 vendor_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001550 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001551 'super_partition_groups': 'group_a group_b',
1552 'dynamic_partition_list': 'vendor product',
Daniel Norman55417142019-11-25 16:04:36 -08001553 'super_group_a_partition_list': 'vendor',
1554 'super_group_a_group_size': '1000',
1555 'super_group_b_partition_list': 'product',
1556 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001557 }
1558 merged_dict = common.MergeDynamicPartitionInfoDicts(
1559 framework_dict=framework_dict,
Daniel Norman55417142019-11-25 16:04:36 -08001560 vendor_dict=vendor_dict)
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001561 expected_merged_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001562 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001563 'super_partition_groups': 'group_a group_b',
Daniel Normanb0c75912020-09-24 14:30:21 -07001564 'dynamic_partition_list': 'product system vendor',
Daniel Norman55417142019-11-25 16:04:36 -08001565 'super_group_a_partition_list': 'system vendor',
1566 'super_group_a_group_size': '1000',
1567 'super_group_b_partition_list': 'product',
1568 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001569 }
1570 self.assertEqual(merged_dict, expected_merged_dict)
1571
Daniel Norman276f0622019-07-26 14:13:51 -07001572 def test_GetAvbPartitionArg(self):
1573 info_dict = {}
1574 cmd = common.GetAvbPartitionArg('system', '/path/to/system.img', info_dict)
1575 self.assertEqual(
1576 ['--include_descriptors_from_image', '/path/to/system.img'], cmd)
1577
1578 @test_utils.SkipIfExternalToolsUnavailable()
1579 def test_AppendVBMetaArgsForPartition_vendorAsChainedPartition(self):
1580 testdata_dir = test_utils.get_testdata_dir()
1581 pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
1582 info_dict = {
1583 'avb_avbtool': 'avbtool',
1584 'avb_vendor_key_path': pubkey,
1585 'avb_vendor_rollback_index_location': 5,
1586 }
1587 cmd = common.GetAvbPartitionArg('vendor', '/path/to/vendor.img', info_dict)
1588 self.assertEqual(2, len(cmd))
1589 self.assertEqual('--chain_partition', cmd[0])
1590 chained_partition_args = cmd[1].split(':')
1591 self.assertEqual(3, len(chained_partition_args))
1592 self.assertEqual('vendor', chained_partition_args[0])
1593 self.assertEqual('5', chained_partition_args[1])
1594 self.assertTrue(os.path.exists(chained_partition_args[2]))
1595
Tao Bao3612c882019-10-14 17:49:31 -07001596 @test_utils.SkipIfExternalToolsUnavailable()
1597 def test_AppendVBMetaArgsForPartition_recoveryAsChainedPartition_nonAb(self):
1598 testdata_dir = test_utils.get_testdata_dir()
1599 pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
1600 info_dict = {
1601 'avb_avbtool': 'avbtool',
1602 'avb_recovery_key_path': pubkey,
1603 'avb_recovery_rollback_index_location': 3,
1604 }
1605 cmd = common.GetAvbPartitionArg(
1606 'recovery', '/path/to/recovery.img', info_dict)
1607 self.assertFalse(cmd)
1608
1609 @test_utils.SkipIfExternalToolsUnavailable()
1610 def test_AppendVBMetaArgsForPartition_recoveryAsChainedPartition_ab(self):
1611 testdata_dir = test_utils.get_testdata_dir()
1612 pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
1613 info_dict = {
1614 'ab_update': 'true',
1615 'avb_avbtool': 'avbtool',
1616 'avb_recovery_key_path': pubkey,
1617 'avb_recovery_rollback_index_location': 3,
1618 }
1619 cmd = common.GetAvbPartitionArg(
1620 'recovery', '/path/to/recovery.img', info_dict)
1621 self.assertEqual(2, len(cmd))
1622 self.assertEqual('--chain_partition', cmd[0])
1623 chained_partition_args = cmd[1].split(':')
1624 self.assertEqual(3, len(chained_partition_args))
1625 self.assertEqual('recovery', chained_partition_args[0])
1626 self.assertEqual('3', chained_partition_args[1])
1627 self.assertTrue(os.path.exists(chained_partition_args[2]))
1628
Yi-Yo Chiang36054e22022-01-08 22:29:30 +08001629 def test_GenerateGkiCertificate_KeyPathNotFound(self):
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001630 pubkey = os.path.join(self.testdata_dir, 'no_testkey_gki.pem')
1631 self.assertFalse(os.path.exists(pubkey))
1632
1633 common.OPTIONS.info_dict = {
1634 'gki_signing_key_path': pubkey,
1635 'gki_signing_algorithm': 'SHA256_RSA4096',
1636 'gki_signing_signature_args': '--prop foo:bar',
1637 }
Yi-Yo Chiang36054e22022-01-08 22:29:30 +08001638 test_file = tempfile.NamedTemporaryFile()
1639 self.assertRaises(common.ExternalError, common._GenerateGkiCertificate,
Yi-Yo Chiang24da1a42022-02-22 19:51:15 +08001640 test_file.name, 'generic_kernel')
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001641
Yi-Yo Chiang36054e22022-01-08 22:29:30 +08001642 def test_GenerateGkiCertificate_SearchKeyPathNotFound(self):
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001643 pubkey = 'no_testkey_gki.pem'
1644 self.assertFalse(os.path.exists(pubkey))
1645
1646 # Tests it should raise ExternalError if no key found under
1647 # OPTIONS.search_path.
1648 search_path_dir = common.MakeTempDir()
1649 search_pubkey = os.path.join(search_path_dir, pubkey)
1650 self.assertFalse(os.path.exists(search_pubkey))
1651
1652 common.OPTIONS.search_path = search_path_dir
1653 common.OPTIONS.info_dict = {
1654 'gki_signing_key_path': pubkey,
1655 'gki_signing_algorithm': 'SHA256_RSA4096',
1656 'gki_signing_signature_args': '--prop foo:bar',
1657 }
Yi-Yo Chiang36054e22022-01-08 22:29:30 +08001658 test_file = tempfile.NamedTemporaryFile()
1659 self.assertRaises(common.ExternalError, common._GenerateGkiCertificate,
Yi-Yo Chiang24da1a42022-02-22 19:51:15 +08001660 test_file.name, 'generic_kernel')
Tao Baofc7e0e02018-02-13 13:54:02 -08001661
Kelvin Zhang37a42902022-10-26 12:49:03 -07001662
Tao Bao65b94e92018-10-11 21:57:26 -07001663class InstallRecoveryScriptFormatTest(test_utils.ReleaseToolsTestCase):
Tao Bao1c830bf2017-12-25 10:43:47 -08001664 """Checks the format of install-recovery.sh.
Tianjie Xu9c384d22017-06-20 17:00:55 -07001665
Tao Bao1c830bf2017-12-25 10:43:47 -08001666 Its format should match between common.py and validate_target_files.py.
1667 """
Tianjie Xu9c384d22017-06-20 17:00:55 -07001668
1669 def setUp(self):
Tao Bao1c830bf2017-12-25 10:43:47 -08001670 self._tempdir = common.MakeTempDir()
Kelvin Zhangc693d952020-07-22 19:21:22 -04001671 # Create a fake dict that contains the fstab info for boot&recovery.
Kelvin Zhang37a42902022-10-26 12:49:03 -07001672 self._info = {"fstab": {}}
Kelvin Zhangc693d952020-07-22 19:21:22 -04001673 fake_fstab = [
Tao Bao1c830bf2017-12-25 10:43:47 -08001674 "/dev/soc.0/by-name/boot /boot emmc defaults defaults",
1675 "/dev/soc.0/by-name/recovery /recovery emmc defaults defaults"]
Kelvin Zhangc693d952020-07-22 19:21:22 -04001676 self._info["fstab"] = common.LoadRecoveryFSTab("\n".join, 2, fake_fstab)
Tianjie Xudf055582017-11-07 12:22:58 -08001677 # Construct the gzipped recovery.img and boot.img
1678 self.recovery_data = bytearray([
1679 0x1f, 0x8b, 0x08, 0x00, 0x81, 0x11, 0x02, 0x5a, 0x00, 0x03, 0x2b, 0x4a,
1680 0x4d, 0xce, 0x2f, 0x4b, 0x2d, 0xaa, 0x04, 0x00, 0xc9, 0x93, 0x43, 0xf3,
1681 0x08, 0x00, 0x00, 0x00
1682 ])
1683 # echo -n "boot" | gzip -f | hd
1684 self.boot_data = bytearray([
1685 0x1f, 0x8b, 0x08, 0x00, 0x8c, 0x12, 0x02, 0x5a, 0x00, 0x03, 0x4b, 0xca,
1686 0xcf, 0x2f, 0x01, 0x00, 0xc4, 0xae, 0xed, 0x46, 0x04, 0x00, 0x00, 0x00
1687 ])
Tianjie Xu9c384d22017-06-20 17:00:55 -07001688
1689 def _out_tmp_sink(self, name, data, prefix="SYSTEM"):
1690 loc = os.path.join(self._tempdir, prefix, name)
1691 if not os.path.exists(os.path.dirname(loc)):
1692 os.makedirs(os.path.dirname(loc))
Tao Baoda30cfa2017-12-01 16:19:46 -08001693 with open(loc, "wb") as f:
Tianjie Xu9c384d22017-06-20 17:00:55 -07001694 f.write(data)
1695
1696 def test_full_recovery(self):
Tao Bao31b08072017-11-08 15:50:59 -08001697 recovery_image = common.File("recovery.img", self.recovery_data)
1698 boot_image = common.File("boot.img", self.boot_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001699 self._info["full_recovery_image"] = "true"
1700
1701 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1702 recovery_image, boot_image, self._info)
1703 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1704 self._info)
1705
Tao Bao82490d32019-04-09 00:12:30 -07001706 @test_utils.SkipIfExternalToolsUnavailable()
Tianjie Xu9c384d22017-06-20 17:00:55 -07001707 def test_recovery_from_boot(self):
Tao Bao31b08072017-11-08 15:50:59 -08001708 recovery_image = common.File("recovery.img", self.recovery_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001709 self._out_tmp_sink("recovery.img", recovery_image.data, "IMAGES")
Tao Bao31b08072017-11-08 15:50:59 -08001710 boot_image = common.File("boot.img", self.boot_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001711 self._out_tmp_sink("boot.img", boot_image.data, "IMAGES")
1712
1713 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1714 recovery_image, boot_image, self._info)
1715 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1716 self._info)
1717 # Validate 'recovery-from-boot' with bonus argument.
Tao Baoda30cfa2017-12-01 16:19:46 -08001718 self._out_tmp_sink("etc/recovery-resource.dat", b"bonus", "SYSTEM")
Tianjie Xu9c384d22017-06-20 17:00:55 -07001719 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1720 recovery_image, boot_image, self._info)
1721 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1722 self._info)
Yifan Hong45433e42019-01-18 13:55:25 -08001723
1724
Yifan Hong45433e42019-01-18 13:55:25 -08001725class MockBlockDifference(object):
Tao Baoda30cfa2017-12-01 16:19:46 -08001726
Yifan Hong45433e42019-01-18 13:55:25 -08001727 def __init__(self, partition, tgt, src=None):
1728 self.partition = partition
1729 self.tgt = tgt
1730 self.src = src
Tao Baoda30cfa2017-12-01 16:19:46 -08001731
Yifan Hong45433e42019-01-18 13:55:25 -08001732 def WriteScript(self, script, _, progress=None,
1733 write_verify_script=False):
1734 if progress:
1735 script.AppendExtra("progress({})".format(progress))
1736 script.AppendExtra("patch({});".format(self.partition))
1737 if write_verify_script:
1738 self.WritePostInstallVerifyScript(script)
Tao Baoda30cfa2017-12-01 16:19:46 -08001739
Yifan Hong45433e42019-01-18 13:55:25 -08001740 def WritePostInstallVerifyScript(self, script):
1741 script.AppendExtra("verify({});".format(self.partition))
1742
1743
1744class FakeSparseImage(object):
Tao Baoda30cfa2017-12-01 16:19:46 -08001745
Yifan Hong45433e42019-01-18 13:55:25 -08001746 def __init__(self, size):
1747 self.blocksize = 4096
1748 self.total_blocks = size // 4096
1749 assert size % 4096 == 0, "{} is not a multiple of 4096".format(size)
1750
1751
1752class DynamicPartitionsDifferenceTest(test_utils.ReleaseToolsTestCase):
Tao Baoda30cfa2017-12-01 16:19:46 -08001753
Yifan Hong45433e42019-01-18 13:55:25 -08001754 @staticmethod
1755 def get_op_list(output_path):
Kelvin Zhang928c2342020-09-22 16:15:57 -04001756 with zipfile.ZipFile(output_path, allowZip64=True) as output_zip:
Tao Baoda30cfa2017-12-01 16:19:46 -08001757 with output_zip.open('dynamic_partitions_op_list') as op_list:
1758 return [line.decode().strip() for line in op_list.readlines()
1759 if not line.startswith(b'#')]
Yifan Hong45433e42019-01-18 13:55:25 -08001760
1761 def setUp(self):
Tao Baoe1148042019-10-07 20:00:34 -07001762 self.script = test_utils.MockScriptWriter()
Yifan Hong45433e42019-01-18 13:55:25 -08001763 self.output_path = common.MakeTempFile(suffix='.zip')
1764
1765 def test_full(self):
1766 target_info = common.LoadDictionaryFromLines("""
1767dynamic_partition_list=system vendor
1768super_partition_groups=group_foo
1769super_group_foo_group_size={group_size}
1770super_group_foo_partition_list=system vendor
1771""".format(group_size=4 * GiB).split("\n"))
1772 block_diffs = [MockBlockDifference("system", FakeSparseImage(3 * GiB)),
1773 MockBlockDifference("vendor", FakeSparseImage(1 * GiB))]
1774
1775 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001776 with zipfile.ZipFile(self.output_path, 'w', allowZip64=True) as output_zip:
Yifan Hong45433e42019-01-18 13:55:25 -08001777 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1778
1779 self.assertEqual(str(self.script).strip(), """
1780assert(update_dynamic_partitions(package_extract_file("dynamic_partitions_op_list")));
Yifan Hong45433e42019-01-18 13:55:25 -08001781patch(system);
1782verify(system);
1783unmap_partition("system");
Tao Baof1113e92019-06-18 12:10:14 -07001784patch(vendor);
1785verify(vendor);
1786unmap_partition("vendor");
Yifan Hong45433e42019-01-18 13:55:25 -08001787""".strip())
1788
1789 lines = self.get_op_list(self.output_path)
1790
1791 remove_all_groups = lines.index("remove_all_groups")
1792 add_group = lines.index("add_group group_foo 4294967296")
1793 add_vendor = lines.index("add vendor group_foo")
1794 add_system = lines.index("add system group_foo")
1795 resize_vendor = lines.index("resize vendor 1073741824")
1796 resize_system = lines.index("resize system 3221225472")
1797
1798 self.assertLess(remove_all_groups, add_group,
1799 "Should add groups after removing all groups")
1800 self.assertLess(add_group, min(add_vendor, add_system),
1801 "Should add partitions after adding group")
1802 self.assertLess(add_system, resize_system,
1803 "Should resize system after adding it")
1804 self.assertLess(add_vendor, resize_vendor,
1805 "Should resize vendor after adding it")
1806
1807 def test_inc_groups(self):
1808 source_info = common.LoadDictionaryFromLines("""
1809super_partition_groups=group_foo group_bar group_baz
1810super_group_foo_group_size={group_foo_size}
1811super_group_bar_group_size={group_bar_size}
1812""".format(group_foo_size=4 * GiB, group_bar_size=3 * GiB).split("\n"))
1813 target_info = common.LoadDictionaryFromLines("""
1814super_partition_groups=group_foo group_baz group_qux
1815super_group_foo_group_size={group_foo_size}
1816super_group_baz_group_size={group_baz_size}
1817super_group_qux_group_size={group_qux_size}
1818""".format(group_foo_size=3 * GiB, group_baz_size=4 * GiB,
1819 group_qux_size=1 * GiB).split("\n"))
1820
1821 dp_diff = common.DynamicPartitionsDifference(target_info,
1822 block_diffs=[],
1823 source_info_dict=source_info)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001824 with zipfile.ZipFile(self.output_path, 'w', allowZip64=True) as output_zip:
Yifan Hong45433e42019-01-18 13:55:25 -08001825 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1826
1827 lines = self.get_op_list(self.output_path)
1828
1829 removed = lines.index("remove_group group_bar")
1830 shrunk = lines.index("resize_group group_foo 3221225472")
1831 grown = lines.index("resize_group group_baz 4294967296")
1832 added = lines.index("add_group group_qux 1073741824")
1833
Tao Baof1113e92019-06-18 12:10:14 -07001834 self.assertLess(max(removed, shrunk),
1835 min(grown, added),
Yifan Hong45433e42019-01-18 13:55:25 -08001836 "ops that remove / shrink partitions must precede ops that "
1837 "grow / add partitions")
1838
Yifan Hongbb2658d2019-01-25 12:30:58 -08001839 def test_incremental(self):
Yifan Hong45433e42019-01-18 13:55:25 -08001840 source_info = common.LoadDictionaryFromLines("""
Justin Yun6151e3f2019-06-25 15:58:13 +09001841dynamic_partition_list=system vendor product system_ext
Yifan Hong45433e42019-01-18 13:55:25 -08001842super_partition_groups=group_foo
1843super_group_foo_group_size={group_foo_size}
Justin Yun6151e3f2019-06-25 15:58:13 +09001844super_group_foo_partition_list=system vendor product system_ext
Yifan Hong45433e42019-01-18 13:55:25 -08001845""".format(group_foo_size=4 * GiB).split("\n"))
1846 target_info = common.LoadDictionaryFromLines("""
1847dynamic_partition_list=system vendor product odm
1848super_partition_groups=group_foo group_bar
1849super_group_foo_group_size={group_foo_size}
1850super_group_foo_partition_list=system vendor odm
1851super_group_bar_group_size={group_bar_size}
1852super_group_bar_partition_list=product
1853""".format(group_foo_size=3 * GiB, group_bar_size=1 * GiB).split("\n"))
1854
1855 block_diffs = [MockBlockDifference("system", FakeSparseImage(1536 * MiB),
1856 src=FakeSparseImage(1024 * MiB)),
1857 MockBlockDifference("vendor", FakeSparseImage(512 * MiB),
1858 src=FakeSparseImage(1024 * MiB)),
1859 MockBlockDifference("product", FakeSparseImage(1024 * MiB),
1860 src=FakeSparseImage(1024 * MiB)),
Justin Yun6151e3f2019-06-25 15:58:13 +09001861 MockBlockDifference("system_ext", None,
Yifan Hong45433e42019-01-18 13:55:25 -08001862 src=FakeSparseImage(1024 * MiB)),
1863 MockBlockDifference("odm", FakeSparseImage(1024 * MiB),
1864 src=None)]
1865
1866 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs,
1867 source_info_dict=source_info)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001868 with zipfile.ZipFile(self.output_path, 'w', allowZip64=True) as output_zip:
Yifan Hong45433e42019-01-18 13:55:25 -08001869 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1870
1871 metadata_idx = self.script.lines.index(
1872 'assert(update_dynamic_partitions(package_extract_file('
1873 '"dynamic_partitions_op_list")));')
1874 self.assertLess(self.script.lines.index('patch(vendor);'), metadata_idx)
1875 self.assertLess(metadata_idx, self.script.lines.index('verify(vendor);'))
1876 for p in ("product", "system", "odm"):
1877 patch_idx = self.script.lines.index("patch({});".format(p))
1878 verify_idx = self.script.lines.index("verify({});".format(p))
1879 self.assertLess(metadata_idx, patch_idx,
1880 "Should patch {} after updating metadata".format(p))
1881 self.assertLess(patch_idx, verify_idx,
1882 "Should verify {} after patching".format(p))
1883
Justin Yun6151e3f2019-06-25 15:58:13 +09001884 self.assertNotIn("patch(system_ext);", self.script.lines)
Yifan Hong45433e42019-01-18 13:55:25 -08001885
1886 lines = self.get_op_list(self.output_path)
1887
Justin Yun6151e3f2019-06-25 15:58:13 +09001888 remove = lines.index("remove system_ext")
Yifan Hong45433e42019-01-18 13:55:25 -08001889 move_product_out = lines.index("move product default")
1890 shrink = lines.index("resize vendor 536870912")
1891 shrink_group = lines.index("resize_group group_foo 3221225472")
1892 add_group_bar = lines.index("add_group group_bar 1073741824")
1893 add_odm = lines.index("add odm group_foo")
1894 grow_existing = lines.index("resize system 1610612736")
1895 grow_added = lines.index("resize odm 1073741824")
1896 move_product_in = lines.index("move product group_bar")
1897
1898 max_idx_move_partition_out_foo = max(remove, move_product_out, shrink)
1899 min_idx_move_partition_in_foo = min(add_odm, grow_existing, grow_added)
1900
1901 self.assertLess(max_idx_move_partition_out_foo, shrink_group,
1902 "Must shrink group after partitions inside group are shrunk"
1903 " / removed")
1904
1905 self.assertLess(add_group_bar, move_product_in,
1906 "Must add partitions to group after group is added")
1907
1908 self.assertLess(max_idx_move_partition_out_foo,
1909 min_idx_move_partition_in_foo,
1910 "Must shrink partitions / remove partitions from group"
1911 "before adding / moving partitions into group")
Yifan Hongbb2658d2019-01-25 12:30:58 -08001912
1913 def test_remove_partition(self):
1914 source_info = common.LoadDictionaryFromLines("""
1915blockimgdiff_versions=3,4
1916use_dynamic_partitions=true
1917dynamic_partition_list=foo
1918super_partition_groups=group_foo
1919super_group_foo_group_size={group_foo_size}
1920super_group_foo_partition_list=foo
1921""".format(group_foo_size=4 * GiB).split("\n"))
1922 target_info = common.LoadDictionaryFromLines("""
1923blockimgdiff_versions=3,4
1924use_dynamic_partitions=true
1925super_partition_groups=group_foo
1926super_group_foo_group_size={group_foo_size}
1927""".format(group_foo_size=4 * GiB).split("\n"))
1928
1929 common.OPTIONS.info_dict = target_info
1930 common.OPTIONS.target_info_dict = target_info
1931 common.OPTIONS.source_info_dict = source_info
1932 common.OPTIONS.cache_size = 4 * 4096
1933
1934 block_diffs = [common.BlockDifference("foo", EmptyImage(),
1935 src=DataImage("source", pad=True))]
1936
1937 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs,
1938 source_info_dict=source_info)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001939 with zipfile.ZipFile(self.output_path, 'w', allowZip64=True) as output_zip:
Yifan Hongbb2658d2019-01-25 12:30:58 -08001940 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1941
1942 self.assertNotIn("block_image_update", str(self.script),
Tao Bao2cc0ca12019-03-15 10:44:43 -07001943 "Removed partition should not be patched.")
Yifan Hongbb2658d2019-01-25 12:30:58 -08001944
1945 lines = self.get_op_list(self.output_path)
1946 self.assertEqual(lines, ["remove foo"])
Tianjie Xu0fde41e2020-05-09 05:24:18 +00001947
1948
1949class PartitionBuildPropsTest(test_utils.ReleaseToolsTestCase):
1950 def setUp(self):
Tianjie Xu9afb2212020-05-10 21:48:15 +00001951 self.odm_build_prop = [
Tianjie Xu0fde41e2020-05-09 05:24:18 +00001952 'ro.odm.build.date.utc=1578430045',
1953 'ro.odm.build.fingerprint='
1954 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
1955 'ro.product.odm.device=coral',
1956 'import /odm/etc/build_${ro.boot.product.device_name}.prop',
1957 ]
1958
1959 @staticmethod
1960 def _BuildZipFile(entries):
1961 input_file = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001962 with zipfile.ZipFile(input_file, 'w', allowZip64=True) as input_zip:
Tianjie Xu0fde41e2020-05-09 05:24:18 +00001963 for name, content in entries.items():
1964 input_zip.writestr(name, content)
1965
1966 return input_file
1967
1968 def test_parseBuildProps_noImportStatement(self):
1969 build_prop = [
Tianjie Xu9afb2212020-05-10 21:48:15 +00001970 'ro.odm.build.date.utc=1578430045',
1971 'ro.odm.build.fingerprint='
1972 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
1973 'ro.product.odm.device=coral',
Tianjie Xu0fde41e2020-05-09 05:24:18 +00001974 ]
1975 input_file = self._BuildZipFile({
Tianjie Xu9afb2212020-05-10 21:48:15 +00001976 'ODM/etc/build.prop': '\n'.join(build_prop),
1977 })
1978
Kelvin Zhang928c2342020-09-22 16:15:57 -04001979 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00001980 placeholder_values = {
1981 'ro.boot.product.device_name': ['std', 'pro']
1982 }
1983 partition_props = common.PartitionBuildProps.FromInputFile(
1984 input_zip, 'odm', placeholder_values)
1985
1986 self.assertEqual({
1987 'ro.odm.build.date.utc': '1578430045',
1988 'ro.odm.build.fingerprint':
1989 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
1990 'ro.product.odm.device': 'coral',
1991 }, partition_props.build_props)
1992
1993 self.assertEqual(set(), partition_props.prop_overrides)
1994
1995 def test_parseBuildProps_singleImportStatement(self):
1996 build_std_prop = [
1997 'ro.product.odm.device=coral',
1998 'ro.product.odm.name=product1',
1999 ]
2000 build_pro_prop = [
2001 'ro.product.odm.device=coralpro',
2002 'ro.product.odm.name=product2',
2003 ]
2004
2005 input_file = self._BuildZipFile({
2006 'ODM/etc/build.prop': '\n'.join(self.odm_build_prop),
2007 'ODM/etc/build_std.prop': '\n'.join(build_std_prop),
2008 'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop),
2009 })
2010
Kelvin Zhang928c2342020-09-22 16:15:57 -04002011 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002012 placeholder_values = {
2013 'ro.boot.product.device_name': 'std'
2014 }
2015 partition_props = common.PartitionBuildProps.FromInputFile(
2016 input_zip, 'odm', placeholder_values)
2017
2018 self.assertEqual({
Kelvin Zhang37a42902022-10-26 12:49:03 -07002019 'ro.odm.build.date.utc': '1578430045',
2020 'ro.odm.build.fingerprint':
2021 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2022 'ro.product.odm.device': 'coral',
2023 'ro.product.odm.name': 'product1',
Tianjie Xu9afb2212020-05-10 21:48:15 +00002024 }, partition_props.build_props)
2025
Kelvin Zhang928c2342020-09-22 16:15:57 -04002026 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002027 placeholder_values = {
2028 'ro.boot.product.device_name': 'pro'
2029 }
2030 partition_props = common.PartitionBuildProps.FromInputFile(
2031 input_zip, 'odm', placeholder_values)
2032
2033 self.assertEqual({
2034 'ro.odm.build.date.utc': '1578430045',
2035 'ro.odm.build.fingerprint':
2036 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2037 'ro.product.odm.device': 'coralpro',
2038 'ro.product.odm.name': 'product2',
2039 }, partition_props.build_props)
2040
2041 def test_parseBuildProps_noPlaceHolders(self):
2042 build_prop = copy.copy(self.odm_build_prop)
2043 input_file = self._BuildZipFile({
2044 'ODM/etc/build.prop': '\n'.join(build_prop),
Tianjie Xu0fde41e2020-05-09 05:24:18 +00002045 })
2046
Kelvin Zhang928c2342020-09-22 16:15:57 -04002047 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu0fde41e2020-05-09 05:24:18 +00002048 partition_props = common.PartitionBuildProps.FromInputFile(
2049 input_zip, 'odm')
2050
2051 self.assertEqual({
Tianjie Xu9afb2212020-05-10 21:48:15 +00002052 'ro.odm.build.date.utc': '1578430045',
2053 'ro.odm.build.fingerprint':
2054 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2055 'ro.product.odm.device': 'coral',
Tianjie Xu0fde41e2020-05-09 05:24:18 +00002056 }, partition_props.build_props)
2057
Tianjie Xu9afb2212020-05-10 21:48:15 +00002058 self.assertEqual(set(), partition_props.prop_overrides)
2059
2060 def test_parseBuildProps_multipleImportStatements(self):
2061 build_prop = copy.deepcopy(self.odm_build_prop)
2062 build_prop.append(
2063 'import /odm/etc/build_${ro.boot.product.product_name}.prop')
2064
2065 build_std_prop = [
2066 'ro.product.odm.device=coral',
2067 ]
2068 build_pro_prop = [
2069 'ro.product.odm.device=coralpro',
2070 ]
2071
2072 product1_prop = [
2073 'ro.product.odm.name=product1',
2074 'ro.product.not_care=not_care',
2075 ]
2076
2077 product2_prop = [
2078 'ro.product.odm.name=product2',
2079 'ro.product.not_care=not_care',
2080 ]
2081
2082 input_file = self._BuildZipFile({
2083 'ODM/etc/build.prop': '\n'.join(build_prop),
2084 'ODM/etc/build_std.prop': '\n'.join(build_std_prop),
2085 'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop),
2086 'ODM/etc/build_product1.prop': '\n'.join(product1_prop),
2087 'ODM/etc/build_product2.prop': '\n'.join(product2_prop),
2088 })
2089
Kelvin Zhang928c2342020-09-22 16:15:57 -04002090 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002091 placeholder_values = {
2092 'ro.boot.product.device_name': 'std',
2093 'ro.boot.product.product_name': 'product1',
2094 'ro.boot.product.not_care': 'not_care',
2095 }
2096 partition_props = common.PartitionBuildProps.FromInputFile(
2097 input_zip, 'odm', placeholder_values)
2098
2099 self.assertEqual({
2100 'ro.odm.build.date.utc': '1578430045',
2101 'ro.odm.build.fingerprint':
2102 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2103 'ro.product.odm.device': 'coral',
2104 'ro.product.odm.name': 'product1'
2105 }, partition_props.build_props)
2106
Kelvin Zhang928c2342020-09-22 16:15:57 -04002107 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002108 placeholder_values = {
2109 'ro.boot.product.device_name': 'pro',
2110 'ro.boot.product.product_name': 'product2',
2111 'ro.boot.product.not_care': 'not_care',
2112 }
2113 partition_props = common.PartitionBuildProps.FromInputFile(
2114 input_zip, 'odm', placeholder_values)
2115
2116 self.assertEqual({
2117 'ro.odm.build.date.utc': '1578430045',
2118 'ro.odm.build.fingerprint':
2119 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2120 'ro.product.odm.device': 'coralpro',
2121 'ro.product.odm.name': 'product2'
2122 }, partition_props.build_props)
2123
2124 def test_parseBuildProps_defineAfterOverride(self):
2125 build_prop = copy.deepcopy(self.odm_build_prop)
2126 build_prop.append('ro.product.odm.device=coral')
2127
2128 build_std_prop = [
2129 'ro.product.odm.device=coral',
2130 ]
2131 build_pro_prop = [
2132 'ro.product.odm.device=coralpro',
2133 ]
2134
2135 input_file = self._BuildZipFile({
2136 'ODM/etc/build.prop': '\n'.join(build_prop),
2137 'ODM/etc/build_std.prop': '\n'.join(build_std_prop),
2138 'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop),
2139 })
2140
Kelvin Zhang928c2342020-09-22 16:15:57 -04002141 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002142 placeholder_values = {
2143 'ro.boot.product.device_name': 'std',
2144 }
2145
2146 self.assertRaises(ValueError, common.PartitionBuildProps.FromInputFile,
2147 input_zip, 'odm', placeholder_values)
2148
2149 def test_parseBuildProps_duplicateOverride(self):
2150 build_prop = copy.deepcopy(self.odm_build_prop)
2151 build_prop.append(
2152 'import /odm/etc/build_${ro.boot.product.product_name}.prop')
2153
2154 build_std_prop = [
2155 'ro.product.odm.device=coral',
2156 'ro.product.odm.name=product1',
2157 ]
2158 build_pro_prop = [
2159 'ro.product.odm.device=coralpro',
2160 ]
2161
2162 product1_prop = [
2163 'ro.product.odm.name=product1',
2164 ]
2165
2166 product2_prop = [
2167 'ro.product.odm.name=product2',
2168 ]
2169
2170 input_file = self._BuildZipFile({
2171 'ODM/etc/build.prop': '\n'.join(build_prop),
2172 'ODM/etc/build_std.prop': '\n'.join(build_std_prop),
2173 'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop),
2174 'ODM/etc/build_product1.prop': '\n'.join(product1_prop),
2175 'ODM/etc/build_product2.prop': '\n'.join(product2_prop),
2176 })
2177
Kelvin Zhang928c2342020-09-22 16:15:57 -04002178 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002179 placeholder_values = {
2180 'ro.boot.product.device_name': 'std',
2181 'ro.boot.product.product_name': 'product1',
2182 }
2183 self.assertRaises(ValueError, common.PartitionBuildProps.FromInputFile,
2184 input_zip, 'odm', placeholder_values)
GeQi3fa9e322022-10-18 11:50:16 +08002185
2186 def test_partitionBuildProps_fromInputFile_deepcopy(self):
2187 build_prop = [
2188 'ro.odm.build.date.utc=1578430045',
2189 'ro.odm.build.fingerprint='
2190 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2191 'ro.product.odm.device=coral',
2192 ]
2193 input_file = self._BuildZipFile({
2194 'ODM/etc/build.prop': '\n'.join(build_prop),
2195 })
2196
2197 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
2198 placeholder_values = {
2199 'ro.boot.product.device_name': ['std', 'pro']
2200 }
2201 partition_props = common.PartitionBuildProps.FromInputFile(
2202 input_zip, 'odm', placeholder_values)
2203
2204 copied_props = copy.deepcopy(partition_props)
2205 self.assertEqual({
Kelvin Zhang37a42902022-10-26 12:49:03 -07002206 'ro.odm.build.date.utc': '1578430045',
2207 'ro.odm.build.fingerprint':
2208 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2209 'ro.product.odm.device': 'coral',
GeQi3fa9e322022-10-18 11:50:16 +08002210 }, copied_props.build_props)