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