blob: 0b368f9b61392d2e4fa0805b4e292f58655ea5fb [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
51 TEST_INFO_DICT = {
Tianjie Xu0fde41e2020-05-09 05:24:18 +000052 'build.prop': common.PartitionBuildProps.FromDictionary(
53 'system', {
54 'ro.product.device': 'product-device',
55 'ro.product.name': 'product-name',
56 'ro.build.fingerprint': 'build-fingerprint',
57 'ro.build.foo': 'build-foo'}
58 ),
59 'system.build.prop': common.PartitionBuildProps.FromDictionary(
60 'system', {
61 'ro.product.system.brand': 'product-brand',
62 'ro.product.system.name': 'product-name',
63 'ro.product.system.device': 'product-device',
64 'ro.system.build.version.release': 'version-release',
65 'ro.system.build.id': 'build-id',
66 'ro.system.build.version.incremental': 'version-incremental',
67 'ro.system.build.type': 'build-type',
68 'ro.system.build.tags': 'build-tags',
69 'ro.system.build.foo': 'build-foo'}
70 ),
71 'vendor.build.prop': common.PartitionBuildProps.FromDictionary(
72 'vendor', {
73 'ro.product.vendor.brand': 'vendor-product-brand',
74 'ro.product.vendor.name': 'vendor-product-name',
75 'ro.product.vendor.device': 'vendor-product-device',
76 'ro.vendor.build.version.release': 'vendor-version-release',
77 'ro.vendor.build.id': 'vendor-build-id',
78 'ro.vendor.build.version.incremental':
79 'vendor-version-incremental',
80 'ro.vendor.build.type': 'vendor-build-type',
81 'ro.vendor.build.tags': 'vendor-build-tags'}
82 ),
83 'property1': 'value1',
84 'property2': 4096,
Tao Bao1c320f82019-10-04 23:25:12 -070085 }
86
87 TEST_INFO_DICT_USES_OEM_PROPS = {
Tianjie Xu0fde41e2020-05-09 05:24:18 +000088 'build.prop': common.PartitionBuildProps.FromDictionary(
89 'system', {
90 'ro.product.name': 'product-name',
91 'ro.build.thumbprint': 'build-thumbprint',
92 'ro.build.bar': 'build-bar'}
93 ),
94 'vendor.build.prop': common.PartitionBuildProps.FromDictionary(
95 'vendor', {
96 'ro.vendor.build.fingerprint': 'vendor-build-fingerprint'}
97 ),
98 'property1': 'value1',
99 'property2': 4096,
100 'oem_fingerprint_properties': 'ro.product.device ro.product.brand',
Tao Bao1c320f82019-10-04 23:25:12 -0700101 }
102
103 TEST_OEM_DICTS = [
104 {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000105 'ro.product.brand': 'brand1',
106 'ro.product.device': 'device1',
Tao Bao1c320f82019-10-04 23:25:12 -0700107 },
108 {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000109 'ro.product.brand': 'brand2',
110 'ro.product.device': 'device2',
Tao Bao1c320f82019-10-04 23:25:12 -0700111 },
112 {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000113 'ro.product.brand': 'brand3',
114 'ro.product.device': 'device3',
Tao Bao1c320f82019-10-04 23:25:12 -0700115 },
116 ]
117
Steven Laver8e2086e2020-04-27 16:26:31 -0700118 TEST_INFO_DICT_PROPERTY_SOURCE_ORDER = {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000119 'build.prop': common.PartitionBuildProps.FromDictionary(
120 'system', {
121 'ro.build.fingerprint': 'build-fingerprint',
122 'ro.product.property_source_order':
123 'product,odm,vendor,system_ext,system'}
124 ),
125 'system.build.prop': common.PartitionBuildProps.FromDictionary(
126 'system', {
127 'ro.product.system.device': 'system-product-device'}
128 ),
129 'vendor.build.prop': common.PartitionBuildProps.FromDictionary(
130 'vendor', {
131 'ro.product.vendor.device': 'vendor-product-device'}
132 ),
Steven Laver8e2086e2020-04-27 16:26:31 -0700133 }
134
135 TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_10 = {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000136 'build.prop': common.PartitionBuildProps.FromDictionary(
137 'system', {
138 'ro.build.fingerprint': 'build-fingerprint',
139 'ro.product.property_source_order':
140 'product,product_services,odm,vendor,system',
141 'ro.build.version.release': '10',
142 'ro.build.version.codename': 'REL'}
143 ),
144 'system.build.prop': common.PartitionBuildProps.FromDictionary(
145 'system', {
146 'ro.product.system.device': 'system-product-device'}
147 ),
148 'vendor.build.prop': common.PartitionBuildProps.FromDictionary(
149 'vendor', {
150 'ro.product.vendor.device': 'vendor-product-device'}
151 ),
Steven Laver8e2086e2020-04-27 16:26:31 -0700152 }
153
154 TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_9 = {
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000155 'build.prop': common.PartitionBuildProps.FromDictionary(
156 'system', {
157 'ro.product.device': 'product-device',
158 'ro.build.fingerprint': 'build-fingerprint',
159 'ro.build.version.release': '9',
160 'ro.build.version.codename': 'REL'}
161 ),
162 'system.build.prop': common.PartitionBuildProps.FromDictionary(
163 'system', {
164 'ro.product.system.device': 'system-product-device'}
165 ),
166 'vendor.build.prop': common.PartitionBuildProps.FromDictionary(
167 'vendor', {
168 'ro.product.vendor.device': 'vendor-product-device'}
169 ),
Steven Laver8e2086e2020-04-27 16:26:31 -0700170 }
171
Tao Bao1c320f82019-10-04 23:25:12 -0700172 def test_init(self):
173 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
174 self.assertEqual('product-device', target_info.device)
175 self.assertEqual('build-fingerprint', target_info.fingerprint)
176 self.assertFalse(target_info.is_ab)
177 self.assertIsNone(target_info.oem_props)
178
179 def test_init_with_oem_props(self):
180 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
181 self.TEST_OEM_DICTS)
182 self.assertEqual('device1', target_info.device)
183 self.assertEqual('brand1/product-name/device1:build-thumbprint',
184 target_info.fingerprint)
185
186 # Swap the order in oem_dicts, which would lead to different BuildInfo.
187 oem_dicts = copy.copy(self.TEST_OEM_DICTS)
188 oem_dicts[0], oem_dicts[2] = oem_dicts[2], oem_dicts[0]
189 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
190 oem_dicts)
191 self.assertEqual('device3', target_info.device)
192 self.assertEqual('brand3/product-name/device3:build-thumbprint',
193 target_info.fingerprint)
194
Tao Bao1c320f82019-10-04 23:25:12 -0700195 def test_init_badFingerprint(self):
196 info_dict = copy.deepcopy(self.TEST_INFO_DICT)
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000197 info_dict['build.prop'].build_props[
198 'ro.build.fingerprint'] = 'bad fingerprint'
Tao Bao1c320f82019-10-04 23:25:12 -0700199 self.assertRaises(ValueError, common.BuildInfo, info_dict, None)
200
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000201 info_dict['build.prop'].build_props[
202 'ro.build.fingerprint'] = 'bad\x80fingerprint'
Tao Bao1c320f82019-10-04 23:25:12 -0700203 self.assertRaises(ValueError, common.BuildInfo, info_dict, None)
204
205 def test___getitem__(self):
206 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
207 self.assertEqual('value1', target_info['property1'])
208 self.assertEqual(4096, target_info['property2'])
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000209 self.assertEqual('build-foo',
210 target_info['build.prop'].GetProp('ro.build.foo'))
Tao Bao1c320f82019-10-04 23:25:12 -0700211
212 def test___getitem__with_oem_props(self):
213 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
214 self.TEST_OEM_DICTS)
215 self.assertEqual('value1', target_info['property1'])
216 self.assertEqual(4096, target_info['property2'])
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000217 self.assertIsNone(target_info['build.prop'].GetProp('ro.build.foo'))
Tao Bao1c320f82019-10-04 23:25:12 -0700218
219 def test___setitem__(self):
220 target_info = common.BuildInfo(copy.deepcopy(self.TEST_INFO_DICT), None)
221 self.assertEqual('value1', target_info['property1'])
222 target_info['property1'] = 'value2'
223 self.assertEqual('value2', target_info['property1'])
224
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000225 self.assertEqual('build-foo',
226 target_info['build.prop'].GetProp('ro.build.foo'))
227 target_info['build.prop'].build_props['ro.build.foo'] = 'build-bar'
228 self.assertEqual('build-bar',
229 target_info['build.prop'].GetProp('ro.build.foo'))
Tao Bao1c320f82019-10-04 23:25:12 -0700230
231 def test_get(self):
232 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
233 self.assertEqual('value1', target_info.get('property1'))
234 self.assertEqual(4096, target_info.get('property2'))
235 self.assertEqual(4096, target_info.get('property2', 1024))
236 self.assertEqual(1024, target_info.get('property-nonexistent', 1024))
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000237 self.assertEqual('build-foo',
238 target_info.get('build.prop').GetProp('ro.build.foo'))
Tao Bao1c320f82019-10-04 23:25:12 -0700239
240 def test_get_with_oem_props(self):
241 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
242 self.TEST_OEM_DICTS)
243 self.assertEqual('value1', target_info.get('property1'))
244 self.assertEqual(4096, target_info.get('property2'))
245 self.assertEqual(4096, target_info.get('property2', 1024))
246 self.assertEqual(1024, target_info.get('property-nonexistent', 1024))
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000247 self.assertIsNone(target_info.get('build.prop').GetProp('ro.build.foo'))
Tao Bao1c320f82019-10-04 23:25:12 -0700248
249 def test_items(self):
250 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
251 items = target_info.items()
252 self.assertIn(('property1', 'value1'), items)
253 self.assertIn(('property2', 4096), items)
254
255 def test_GetBuildProp(self):
256 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
257 self.assertEqual('build-foo', target_info.GetBuildProp('ro.build.foo'))
258 self.assertRaises(common.ExternalError, target_info.GetBuildProp,
259 'ro.build.nonexistent')
260
261 def test_GetBuildProp_with_oem_props(self):
262 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
263 self.TEST_OEM_DICTS)
264 self.assertEqual('build-bar', target_info.GetBuildProp('ro.build.bar'))
265 self.assertRaises(common.ExternalError, target_info.GetBuildProp,
266 'ro.build.nonexistent')
267
Daniel Normand5fe8622020-01-08 17:01:11 -0800268 def test_GetPartitionFingerprint(self):
Tao Bao1c320f82019-10-04 23:25:12 -0700269 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
Daniel Normand5fe8622020-01-08 17:01:11 -0800270 self.assertEqual(
271 target_info.GetPartitionFingerprint('vendor'),
272 'vendor-product-brand/vendor-product-name/vendor-product-device'
273 ':vendor-version-release/vendor-build-id/vendor-version-incremental'
274 ':vendor-build-type/vendor-build-tags')
Tao Bao1c320f82019-10-04 23:25:12 -0700275
Daniel Normand5fe8622020-01-08 17:01:11 -0800276 def test_GetPartitionFingerprint_system_other_uses_system(self):
Tao Bao1c320f82019-10-04 23:25:12 -0700277 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
Daniel Normand5fe8622020-01-08 17:01:11 -0800278 self.assertEqual(
279 target_info.GetPartitionFingerprint('system_other'),
280 target_info.GetPartitionFingerprint('system'))
Tao Bao1c320f82019-10-04 23:25:12 -0700281
Daniel Normand5fe8622020-01-08 17:01:11 -0800282 def test_GetPartitionFingerprint_uses_fingerprint_prop_if_available(self):
283 info_dict = copy.deepcopy(self.TEST_INFO_DICT)
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000284 info_dict['vendor.build.prop'].build_props[
285 'ro.vendor.build.fingerprint'] = 'vendor:fingerprint'
Daniel Normand5fe8622020-01-08 17:01:11 -0800286 target_info = common.BuildInfo(info_dict, None)
287 self.assertEqual(
288 target_info.GetPartitionFingerprint('vendor'),
289 'vendor:fingerprint')
Tao Bao1c320f82019-10-04 23:25:12 -0700290
291 def test_WriteMountOemScript(self):
292 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
293 self.TEST_OEM_DICTS)
294 script_writer = test_utils.MockScriptWriter()
295 target_info.WriteMountOemScript(script_writer)
296 self.assertEqual([('Mount', '/oem', None)], script_writer.lines)
297
298 def test_WriteDeviceAssertions(self):
299 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
300 script_writer = test_utils.MockScriptWriter()
301 target_info.WriteDeviceAssertions(script_writer, False)
302 self.assertEqual([('AssertDevice', 'product-device')], script_writer.lines)
303
304 def test_WriteDeviceAssertions_with_oem_props(self):
305 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
306 self.TEST_OEM_DICTS)
307 script_writer = test_utils.MockScriptWriter()
308 target_info.WriteDeviceAssertions(script_writer, False)
309 self.assertEqual(
310 [
311 ('AssertOemProperty', 'ro.product.device',
312 ['device1', 'device2', 'device3'], False),
313 ('AssertOemProperty', 'ro.product.brand',
314 ['brand1', 'brand2', 'brand3'], False),
315 ],
316 script_writer.lines)
317
Steven Laver8e2086e2020-04-27 16:26:31 -0700318 def test_ResolveRoProductProperty_FromVendor(self):
319 info_dict = copy.deepcopy(self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER)
320 info = common.BuildInfo(info_dict, None)
321 self.assertEqual('vendor-product-device',
322 info.GetBuildProp('ro.product.device'))
323
324 def test_ResolveRoProductProperty_FromSystem(self):
325 info_dict = copy.deepcopy(self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER)
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000326 del info_dict['vendor.build.prop'].build_props['ro.product.vendor.device']
Steven Laver8e2086e2020-04-27 16:26:31 -0700327 info = common.BuildInfo(info_dict, None)
328 self.assertEqual('system-product-device',
329 info.GetBuildProp('ro.product.device'))
330
331 def test_ResolveRoProductProperty_InvalidPropertySearchOrder(self):
332 info_dict = copy.deepcopy(self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER)
Tianjie Xu0fde41e2020-05-09 05:24:18 +0000333 info_dict['build.prop'].build_props[
334 'ro.product.property_source_order'] = 'bad-source'
Steven Laver8e2086e2020-04-27 16:26:31 -0700335 with self.assertRaisesRegexp(common.ExternalError,
336 'Invalid ro.product.property_source_order'):
337 info = common.BuildInfo(info_dict, None)
338 info.GetBuildProp('ro.product.device')
339
340 def test_ResolveRoProductProperty_Android10PropertySearchOrder(self):
341 info_dict = copy.deepcopy(
342 self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_10)
343 info = common.BuildInfo(info_dict, None)
344 self.assertEqual('vendor-product-device',
345 info.GetBuildProp('ro.product.device'))
346
347 def test_ResolveRoProductProperty_Android9PropertySearchOrder(self):
348 info_dict = copy.deepcopy(
349 self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_9)
350 info = common.BuildInfo(info_dict, None)
351 self.assertEqual('product-device',
352 info.GetBuildProp('ro.product.device'))
353
Tao Bao1c320f82019-10-04 23:25:12 -0700354
Tao Bao65b94e92018-10-11 21:57:26 -0700355class CommonZipTest(test_utils.ReleaseToolsTestCase):
356
Tao Bao31b08072017-11-08 15:50:59 -0800357 def _verify(self, zip_file, zip_file_name, arcname, expected_hash,
Tao Baof3282b42015-04-01 11:21:55 -0700358 test_file_name=None, expected_stat=None, expected_mode=0o644,
359 expected_compress_type=zipfile.ZIP_STORED):
360 # Verify the stat if present.
361 if test_file_name is not None:
362 new_stat = os.stat(test_file_name)
363 self.assertEqual(int(expected_stat.st_mode), int(new_stat.st_mode))
364 self.assertEqual(int(expected_stat.st_mtime), int(new_stat.st_mtime))
365
366 # Reopen the zip file to verify.
Kelvin Zhang928c2342020-09-22 16:15:57 -0400367 zip_file = zipfile.ZipFile(zip_file_name, "r", allowZip64=True)
Tao Baof3282b42015-04-01 11:21:55 -0700368
369 # Verify the timestamp.
370 info = zip_file.getinfo(arcname)
371 self.assertEqual(info.date_time, (2009, 1, 1, 0, 0, 0))
372
373 # Verify the file mode.
374 mode = (info.external_attr >> 16) & 0o777
375 self.assertEqual(mode, expected_mode)
376
377 # Verify the compress type.
378 self.assertEqual(info.compress_type, expected_compress_type)
379
380 # Verify the zip contents.
Tao Bao31b08072017-11-08 15:50:59 -0800381 entry = zip_file.open(arcname)
382 sha1_hash = sha1()
Tao Baoc1a1ec32019-06-18 16:29:37 -0700383 for chunk in iter(lambda: entry.read(4 * MiB), b''):
Tao Bao31b08072017-11-08 15:50:59 -0800384 sha1_hash.update(chunk)
385 self.assertEqual(expected_hash, sha1_hash.hexdigest())
Tao Baof3282b42015-04-01 11:21:55 -0700386 self.assertIsNone(zip_file.testzip())
387
Dan Albert8e0178d2015-01-27 15:53:15 -0800388 def _test_ZipWrite(self, contents, extra_zipwrite_args=None):
389 extra_zipwrite_args = dict(extra_zipwrite_args or {})
390
391 test_file = tempfile.NamedTemporaryFile(delete=False)
Dan Albert8e0178d2015-01-27 15:53:15 -0800392 test_file_name = test_file.name
Tao Baof3282b42015-04-01 11:21:55 -0700393
394 zip_file = tempfile.NamedTemporaryFile(delete=False)
Dan Albert8e0178d2015-01-27 15:53:15 -0800395 zip_file_name = zip_file.name
396
397 # File names within an archive strip the leading slash.
398 arcname = extra_zipwrite_args.get("arcname", test_file_name)
399 if arcname[0] == "/":
400 arcname = arcname[1:]
401
402 zip_file.close()
Kelvin Zhang928c2342020-09-22 16:15:57 -0400403 zip_file = zipfile.ZipFile(zip_file_name, "w", allowZip64=True)
Dan Albert8e0178d2015-01-27 15:53:15 -0800404
405 try:
Tao Bao31b08072017-11-08 15:50:59 -0800406 sha1_hash = sha1()
407 for data in contents:
Tao Baoc1a1ec32019-06-18 16:29:37 -0700408 sha1_hash.update(bytes(data))
409 test_file.write(bytes(data))
Dan Albert8e0178d2015-01-27 15:53:15 -0800410 test_file.close()
411
Tao Baof3282b42015-04-01 11:21:55 -0700412 expected_stat = os.stat(test_file_name)
Dan Albert8e0178d2015-01-27 15:53:15 -0800413 expected_mode = extra_zipwrite_args.get("perms", 0o644)
Tao Baof3282b42015-04-01 11:21:55 -0700414 expected_compress_type = extra_zipwrite_args.get("compress_type",
415 zipfile.ZIP_STORED)
Dan Albert8e0178d2015-01-27 15:53:15 -0800416 time.sleep(5) # Make sure the atime/mtime will change measurably.
417
418 common.ZipWrite(zip_file, test_file_name, **extra_zipwrite_args)
Tao Baof3282b42015-04-01 11:21:55 -0700419 common.ZipClose(zip_file)
Dan Albert8e0178d2015-01-27 15:53:15 -0800420
Tao Bao31b08072017-11-08 15:50:59 -0800421 self._verify(zip_file, zip_file_name, arcname, sha1_hash.hexdigest(),
422 test_file_name, expected_stat, expected_mode,
423 expected_compress_type)
Dan Albert8e0178d2015-01-27 15:53:15 -0800424 finally:
425 os.remove(test_file_name)
426 os.remove(zip_file_name)
427
Tao Baof3282b42015-04-01 11:21:55 -0700428 def _test_ZipWriteStr(self, zinfo_or_arcname, contents, extra_args=None):
429 extra_args = dict(extra_args or {})
430
431 zip_file = tempfile.NamedTemporaryFile(delete=False)
432 zip_file_name = zip_file.name
433 zip_file.close()
434
Kelvin Zhang928c2342020-09-22 16:15:57 -0400435 zip_file = zipfile.ZipFile(zip_file_name, "w", allowZip64=True)
Tao Baof3282b42015-04-01 11:21:55 -0700436
437 try:
438 expected_compress_type = extra_args.get("compress_type",
439 zipfile.ZIP_STORED)
440 time.sleep(5) # Make sure the atime/mtime will change measurably.
441
442 if not isinstance(zinfo_or_arcname, zipfile.ZipInfo):
Tao Bao58c1b962015-05-20 09:32:18 -0700443 arcname = zinfo_or_arcname
444 expected_mode = extra_args.get("perms", 0o644)
Tao Baof3282b42015-04-01 11:21:55 -0700445 else:
Tao Bao58c1b962015-05-20 09:32:18 -0700446 arcname = zinfo_or_arcname.filename
Tao Baoc1a1ec32019-06-18 16:29:37 -0700447 if zinfo_or_arcname.external_attr:
448 zinfo_perms = zinfo_or_arcname.external_attr >> 16
449 else:
450 zinfo_perms = 0o600
451 expected_mode = extra_args.get("perms", zinfo_perms)
Tao Baof3282b42015-04-01 11:21:55 -0700452
Tao Bao58c1b962015-05-20 09:32:18 -0700453 common.ZipWriteStr(zip_file, zinfo_or_arcname, contents, **extra_args)
Tao Baof3282b42015-04-01 11:21:55 -0700454 common.ZipClose(zip_file)
455
Tao Bao31b08072017-11-08 15:50:59 -0800456 self._verify(zip_file, zip_file_name, arcname, sha1(contents).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700457 expected_mode=expected_mode,
Tao Baof3282b42015-04-01 11:21:55 -0700458 expected_compress_type=expected_compress_type)
459 finally:
460 os.remove(zip_file_name)
461
462 def _test_ZipWriteStr_large_file(self, large, small, extra_args=None):
463 extra_args = dict(extra_args or {})
464
465 zip_file = tempfile.NamedTemporaryFile(delete=False)
466 zip_file_name = zip_file.name
467
468 test_file = tempfile.NamedTemporaryFile(delete=False)
469 test_file_name = test_file.name
470
471 arcname_large = test_file_name
472 arcname_small = "bar"
473
474 # File names within an archive strip the leading slash.
475 if arcname_large[0] == "/":
476 arcname_large = arcname_large[1:]
477
478 zip_file.close()
Kelvin Zhang928c2342020-09-22 16:15:57 -0400479 zip_file = zipfile.ZipFile(zip_file_name, "w", allowZip64=True)
Tao Baof3282b42015-04-01 11:21:55 -0700480
481 try:
Tao Bao31b08072017-11-08 15:50:59 -0800482 sha1_hash = sha1()
483 for data in large:
484 sha1_hash.update(data)
485 test_file.write(data)
Tao Baof3282b42015-04-01 11:21:55 -0700486 test_file.close()
487
488 expected_stat = os.stat(test_file_name)
489 expected_mode = 0o644
490 expected_compress_type = extra_args.get("compress_type",
491 zipfile.ZIP_STORED)
492 time.sleep(5) # Make sure the atime/mtime will change measurably.
493
494 common.ZipWrite(zip_file, test_file_name, **extra_args)
495 common.ZipWriteStr(zip_file, arcname_small, small, **extra_args)
496 common.ZipClose(zip_file)
497
498 # Verify the contents written by ZipWrite().
Tao Bao31b08072017-11-08 15:50:59 -0800499 self._verify(zip_file, zip_file_name, arcname_large,
500 sha1_hash.hexdigest(), test_file_name, expected_stat,
501 expected_mode, expected_compress_type)
Tao Baof3282b42015-04-01 11:21:55 -0700502
503 # Verify the contents written by ZipWriteStr().
Tao Bao31b08072017-11-08 15:50:59 -0800504 self._verify(zip_file, zip_file_name, arcname_small,
505 sha1(small).hexdigest(),
Tao Baof3282b42015-04-01 11:21:55 -0700506 expected_compress_type=expected_compress_type)
507 finally:
508 os.remove(zip_file_name)
509 os.remove(test_file_name)
510
511 def _test_reset_ZIP64_LIMIT(self, func, *args):
512 default_limit = (1 << 31) - 1
513 self.assertEqual(default_limit, zipfile.ZIP64_LIMIT)
514 func(*args)
515 self.assertEqual(default_limit, zipfile.ZIP64_LIMIT)
516
Dan Albert8e0178d2015-01-27 15:53:15 -0800517 def test_ZipWrite(self):
518 file_contents = os.urandom(1024)
519 self._test_ZipWrite(file_contents)
520
521 def test_ZipWrite_with_opts(self):
522 file_contents = os.urandom(1024)
523 self._test_ZipWrite(file_contents, {
524 "arcname": "foobar",
525 "perms": 0o777,
526 "compress_type": zipfile.ZIP_DEFLATED,
527 })
Tao Baof3282b42015-04-01 11:21:55 -0700528 self._test_ZipWrite(file_contents, {
529 "arcname": "foobar",
530 "perms": 0o700,
531 "compress_type": zipfile.ZIP_STORED,
532 })
Dan Albert8e0178d2015-01-27 15:53:15 -0800533
534 def test_ZipWrite_large_file(self):
Tao Baof3282b42015-04-01 11:21:55 -0700535 file_contents = get_2gb_string()
Dan Albert8e0178d2015-01-27 15:53:15 -0800536 self._test_ZipWrite(file_contents, {
537 "compress_type": zipfile.ZIP_DEFLATED,
538 })
539
540 def test_ZipWrite_resets_ZIP64_LIMIT(self):
Tao Baof3282b42015-04-01 11:21:55 -0700541 self._test_reset_ZIP64_LIMIT(self._test_ZipWrite, "")
542
543 def test_ZipWriteStr(self):
544 random_string = os.urandom(1024)
545 # Passing arcname
546 self._test_ZipWriteStr("foo", random_string)
547
548 # Passing zinfo
549 zinfo = zipfile.ZipInfo(filename="foo")
550 self._test_ZipWriteStr(zinfo, random_string)
551
552 # Timestamp in the zinfo should be overwritten.
553 zinfo.date_time = (2015, 3, 1, 15, 30, 0)
554 self._test_ZipWriteStr(zinfo, random_string)
555
556 def test_ZipWriteStr_with_opts(self):
557 random_string = os.urandom(1024)
558 # Passing arcname
559 self._test_ZipWriteStr("foo", random_string, {
Tao Bao58c1b962015-05-20 09:32:18 -0700560 "perms": 0o700,
Tao Baof3282b42015-04-01 11:21:55 -0700561 "compress_type": zipfile.ZIP_DEFLATED,
562 })
Tao Bao58c1b962015-05-20 09:32:18 -0700563 self._test_ZipWriteStr("bar", random_string, {
Tao Baof3282b42015-04-01 11:21:55 -0700564 "compress_type": zipfile.ZIP_STORED,
565 })
566
567 # Passing zinfo
568 zinfo = zipfile.ZipInfo(filename="foo")
569 self._test_ZipWriteStr(zinfo, random_string, {
570 "compress_type": zipfile.ZIP_DEFLATED,
571 })
572 self._test_ZipWriteStr(zinfo, random_string, {
Tao Bao58c1b962015-05-20 09:32:18 -0700573 "perms": 0o600,
Tao Baof3282b42015-04-01 11:21:55 -0700574 "compress_type": zipfile.ZIP_STORED,
575 })
Tao Baoc1a1ec32019-06-18 16:29:37 -0700576 self._test_ZipWriteStr(zinfo, random_string, {
577 "perms": 0o000,
578 "compress_type": zipfile.ZIP_STORED,
579 })
Tao Baof3282b42015-04-01 11:21:55 -0700580
581 def test_ZipWriteStr_large_file(self):
582 # zipfile.writestr() doesn't work when the str size is over 2GiB even with
583 # the workaround. We will only test the case of writing a string into a
584 # large archive.
585 long_string = get_2gb_string()
586 short_string = os.urandom(1024)
587 self._test_ZipWriteStr_large_file(long_string, short_string, {
588 "compress_type": zipfile.ZIP_DEFLATED,
589 })
590
591 def test_ZipWriteStr_resets_ZIP64_LIMIT(self):
Tao Baoc1a1ec32019-06-18 16:29:37 -0700592 self._test_reset_ZIP64_LIMIT(self._test_ZipWriteStr, 'foo', b'')
Tao Baof3282b42015-04-01 11:21:55 -0700593 zinfo = zipfile.ZipInfo(filename="foo")
Tao Baoc1a1ec32019-06-18 16:29:37 -0700594 self._test_reset_ZIP64_LIMIT(self._test_ZipWriteStr, zinfo, b'')
Tao Bao58c1b962015-05-20 09:32:18 -0700595
596 def test_bug21309935(self):
597 zip_file = tempfile.NamedTemporaryFile(delete=False)
598 zip_file_name = zip_file.name
599 zip_file.close()
600
601 try:
602 random_string = os.urandom(1024)
Kelvin Zhang928c2342020-09-22 16:15:57 -0400603 zip_file = zipfile.ZipFile(zip_file_name, "w", allowZip64=True)
Tao Bao58c1b962015-05-20 09:32:18 -0700604 # Default perms should be 0o644 when passing the filename.
605 common.ZipWriteStr(zip_file, "foo", random_string)
606 # Honor the specified perms.
607 common.ZipWriteStr(zip_file, "bar", random_string, perms=0o755)
608 # The perms in zinfo should be untouched.
609 zinfo = zipfile.ZipInfo(filename="baz")
610 zinfo.external_attr = 0o740 << 16
611 common.ZipWriteStr(zip_file, zinfo, random_string)
612 # Explicitly specified perms has the priority.
613 zinfo = zipfile.ZipInfo(filename="qux")
614 zinfo.external_attr = 0o700 << 16
615 common.ZipWriteStr(zip_file, zinfo, random_string, perms=0o400)
616 common.ZipClose(zip_file)
617
Tao Bao31b08072017-11-08 15:50:59 -0800618 self._verify(zip_file, zip_file_name, "foo",
619 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700620 expected_mode=0o644)
Tao Bao31b08072017-11-08 15:50:59 -0800621 self._verify(zip_file, zip_file_name, "bar",
622 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700623 expected_mode=0o755)
Tao Bao31b08072017-11-08 15:50:59 -0800624 self._verify(zip_file, zip_file_name, "baz",
625 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700626 expected_mode=0o740)
Tao Bao31b08072017-11-08 15:50:59 -0800627 self._verify(zip_file, zip_file_name, "qux",
628 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700629 expected_mode=0o400)
630 finally:
631 os.remove(zip_file_name)
Tianjie Xu9c384d22017-06-20 17:00:55 -0700632
Tao Bao82490d32019-04-09 00:12:30 -0700633 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao89d7ab22017-12-14 17:05:33 -0800634 def test_ZipDelete(self):
635 zip_file = tempfile.NamedTemporaryFile(delete=False, suffix='.zip')
636 output_zip = zipfile.ZipFile(zip_file.name, 'w',
637 compression=zipfile.ZIP_DEFLATED)
638 with tempfile.NamedTemporaryFile() as entry_file:
639 entry_file.write(os.urandom(1024))
640 common.ZipWrite(output_zip, entry_file.name, arcname='Test1')
641 common.ZipWrite(output_zip, entry_file.name, arcname='Test2')
642 common.ZipWrite(output_zip, entry_file.name, arcname='Test3')
643 common.ZipClose(output_zip)
644 zip_file.close()
645
646 try:
647 common.ZipDelete(zip_file.name, 'Test2')
Kelvin Zhang928c2342020-09-22 16:15:57 -0400648 with zipfile.ZipFile(zip_file.name, 'r', allowZip64=True) as check_zip:
Tao Bao89d7ab22017-12-14 17:05:33 -0800649 entries = check_zip.namelist()
650 self.assertTrue('Test1' in entries)
651 self.assertFalse('Test2' in entries)
652 self.assertTrue('Test3' in entries)
653
Tao Bao986ee862018-10-04 15:46:16 -0700654 self.assertRaises(
655 common.ExternalError, common.ZipDelete, zip_file.name, 'Test2')
Kelvin Zhang928c2342020-09-22 16:15:57 -0400656 with zipfile.ZipFile(zip_file.name, 'r', allowZip64=True) as check_zip:
Tao Bao89d7ab22017-12-14 17:05:33 -0800657 entries = check_zip.namelist()
658 self.assertTrue('Test1' in entries)
659 self.assertFalse('Test2' in entries)
660 self.assertTrue('Test3' in entries)
661
662 common.ZipDelete(zip_file.name, ['Test3'])
Kelvin Zhang928c2342020-09-22 16:15:57 -0400663 with zipfile.ZipFile(zip_file.name, 'r', allowZip64=True) as check_zip:
Tao Bao89d7ab22017-12-14 17:05:33 -0800664 entries = check_zip.namelist()
665 self.assertTrue('Test1' in entries)
666 self.assertFalse('Test2' in entries)
667 self.assertFalse('Test3' in entries)
668
669 common.ZipDelete(zip_file.name, ['Test1', 'Test2'])
Kelvin Zhang928c2342020-09-22 16:15:57 -0400670 with zipfile.ZipFile(zip_file.name, 'r', allowZip64=True) as check_zip:
Tao Bao89d7ab22017-12-14 17:05:33 -0800671 entries = check_zip.namelist()
672 self.assertFalse('Test1' in entries)
673 self.assertFalse('Test2' in entries)
674 self.assertFalse('Test3' in entries)
675 finally:
676 os.remove(zip_file.name)
677
Tao Bao0ff15de2019-03-20 11:26:06 -0700678 @staticmethod
679 def _test_UnzipTemp_createZipFile():
680 zip_file = common.MakeTempFile(suffix='.zip')
681 output_zip = zipfile.ZipFile(
682 zip_file, 'w', compression=zipfile.ZIP_DEFLATED)
683 contents = os.urandom(1024)
684 with tempfile.NamedTemporaryFile() as entry_file:
685 entry_file.write(contents)
686 common.ZipWrite(output_zip, entry_file.name, arcname='Test1')
687 common.ZipWrite(output_zip, entry_file.name, arcname='Test2')
688 common.ZipWrite(output_zip, entry_file.name, arcname='Foo3')
689 common.ZipWrite(output_zip, entry_file.name, arcname='Bar4')
690 common.ZipWrite(output_zip, entry_file.name, arcname='Dir5/Baz5')
691 common.ZipClose(output_zip)
692 common.ZipClose(output_zip)
693 return zip_file
694
Tao Bao82490d32019-04-09 00:12:30 -0700695 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao0ff15de2019-03-20 11:26:06 -0700696 def test_UnzipTemp(self):
697 zip_file = self._test_UnzipTemp_createZipFile()
698 unzipped_dir = common.UnzipTemp(zip_file)
699 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
700 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
701 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
702 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
703 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
704
Tao Bao82490d32019-04-09 00:12:30 -0700705 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao0ff15de2019-03-20 11:26:06 -0700706 def test_UnzipTemp_withPatterns(self):
707 zip_file = self._test_UnzipTemp_createZipFile()
708
709 unzipped_dir = common.UnzipTemp(zip_file, ['Test1'])
710 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
711 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
712 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
713 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
714 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
715
716 unzipped_dir = common.UnzipTemp(zip_file, ['Test1', 'Foo3'])
717 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
718 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
719 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
720 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
721 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
722
723 unzipped_dir = common.UnzipTemp(zip_file, ['Test*', 'Foo3*'])
724 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
725 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
726 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
727 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
728 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
729
730 unzipped_dir = common.UnzipTemp(zip_file, ['*Test1', '*Baz*'])
731 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
732 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
733 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
734 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
735 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
736
737 def test_UnzipTemp_withEmptyPatterns(self):
738 zip_file = self._test_UnzipTemp_createZipFile()
739 unzipped_dir = common.UnzipTemp(zip_file, [])
740 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
741 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
742 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
743 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
744 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
745
Tao Bao82490d32019-04-09 00:12:30 -0700746 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao0ff15de2019-03-20 11:26:06 -0700747 def test_UnzipTemp_withPartiallyMatchingPatterns(self):
748 zip_file = self._test_UnzipTemp_createZipFile()
749 unzipped_dir = common.UnzipTemp(zip_file, ['Test*', 'Nonexistent*'])
750 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
751 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
752 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
753 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
754 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
755
756 def test_UnzipTemp_withNoMatchingPatterns(self):
757 zip_file = self._test_UnzipTemp_createZipFile()
758 unzipped_dir = common.UnzipTemp(zip_file, ['Foo4', 'Nonexistent*'])
759 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
760 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
761 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
762 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
763 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
764
Tao Bao89d7ab22017-12-14 17:05:33 -0800765
Tao Bao65b94e92018-10-11 21:57:26 -0700766class CommonApkUtilsTest(test_utils.ReleaseToolsTestCase):
Tao Bao818ddf52018-01-05 11:17:34 -0800767 """Tests the APK utils related functions."""
768
769 APKCERTS_TXT1 = (
770 'name="RecoveryLocalizer.apk" certificate="certs/devkey.x509.pem"'
771 ' private_key="certs/devkey.pk8"\n'
772 'name="Settings.apk"'
Dan Willemsen0ab1be62019-04-09 21:35:37 -0700773 ' certificate="build/make/target/product/security/platform.x509.pem"'
774 ' private_key="build/make/target/product/security/platform.pk8"\n'
Tao Bao818ddf52018-01-05 11:17:34 -0800775 'name="TV.apk" certificate="PRESIGNED" private_key=""\n'
776 )
777
778 APKCERTS_CERTMAP1 = {
779 'RecoveryLocalizer.apk' : 'certs/devkey',
Dan Willemsen0ab1be62019-04-09 21:35:37 -0700780 'Settings.apk' : 'build/make/target/product/security/platform',
Tao Bao818ddf52018-01-05 11:17:34 -0800781 'TV.apk' : 'PRESIGNED',
782 }
783
784 APKCERTS_TXT2 = (
785 'name="Compressed1.apk" certificate="certs/compressed1.x509.pem"'
786 ' private_key="certs/compressed1.pk8" compressed="gz"\n'
787 'name="Compressed2a.apk" certificate="certs/compressed2.x509.pem"'
788 ' private_key="certs/compressed2.pk8" compressed="gz"\n'
789 'name="Compressed2b.apk" certificate="certs/compressed2.x509.pem"'
790 ' private_key="certs/compressed2.pk8" compressed="gz"\n'
791 'name="Compressed3.apk" certificate="certs/compressed3.x509.pem"'
792 ' private_key="certs/compressed3.pk8" compressed="gz"\n'
793 )
794
795 APKCERTS_CERTMAP2 = {
796 'Compressed1.apk' : 'certs/compressed1',
797 'Compressed2a.apk' : 'certs/compressed2',
798 'Compressed2b.apk' : 'certs/compressed2',
799 'Compressed3.apk' : 'certs/compressed3',
800 }
801
802 APKCERTS_TXT3 = (
803 'name="Compressed4.apk" certificate="certs/compressed4.x509.pem"'
804 ' private_key="certs/compressed4.pk8" compressed="xz"\n'
805 )
806
807 APKCERTS_CERTMAP3 = {
808 'Compressed4.apk' : 'certs/compressed4',
809 }
810
Bill Peckham5c7b0342020-04-03 15:36:23 -0700811 # Test parsing with no optional fields, both optional fields, and only the
812 # partition optional field.
813 APKCERTS_TXT4 = (
814 'name="RecoveryLocalizer.apk" certificate="certs/devkey.x509.pem"'
815 ' private_key="certs/devkey.pk8"\n'
816 'name="Settings.apk"'
817 ' certificate="build/make/target/product/security/platform.x509.pem"'
818 ' private_key="build/make/target/product/security/platform.pk8"'
819 ' compressed="gz" partition="system"\n'
820 'name="TV.apk" certificate="PRESIGNED" private_key=""'
821 ' partition="product"\n'
822 )
823
824 APKCERTS_CERTMAP4 = {
825 'RecoveryLocalizer.apk' : 'certs/devkey',
826 'Settings.apk' : 'build/make/target/product/security/platform',
827 'TV.apk' : 'PRESIGNED',
828 }
829
Tao Bao17e4e612018-02-16 17:12:54 -0800830 def setUp(self):
831 self.testdata_dir = test_utils.get_testdata_dir()
832
Tao Bao818ddf52018-01-05 11:17:34 -0800833 @staticmethod
834 def _write_apkcerts_txt(apkcerts_txt, additional=None):
835 if additional is None:
836 additional = []
837 target_files = common.MakeTempFile(suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -0400838 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800839 target_files_zip.writestr('META/apkcerts.txt', apkcerts_txt)
840 for entry in additional:
841 target_files_zip.writestr(entry, '')
842 return target_files
843
844 def test_ReadApkCerts_NoncompressedApks(self):
845 target_files = self._write_apkcerts_txt(self.APKCERTS_TXT1)
Kelvin Zhang928c2342020-09-22 16:15:57 -0400846 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800847 certmap, ext = common.ReadApkCerts(input_zip)
848
849 self.assertDictEqual(self.APKCERTS_CERTMAP1, certmap)
850 self.assertIsNone(ext)
851
852 def test_ReadApkCerts_CompressedApks(self):
853 # We have "installed" Compressed1.apk.gz only. Note that Compressed3.apk is
854 # not stored in '.gz' format, so it shouldn't be considered as installed.
855 target_files = self._write_apkcerts_txt(
856 self.APKCERTS_TXT2,
857 ['Compressed1.apk.gz', 'Compressed3.apk'])
858
Kelvin Zhang928c2342020-09-22 16:15:57 -0400859 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800860 certmap, ext = common.ReadApkCerts(input_zip)
861
862 self.assertDictEqual(self.APKCERTS_CERTMAP2, certmap)
863 self.assertEqual('.gz', ext)
864
865 # Alternative case with '.xz'.
866 target_files = self._write_apkcerts_txt(
867 self.APKCERTS_TXT3, ['Compressed4.apk.xz'])
868
Kelvin Zhang928c2342020-09-22 16:15:57 -0400869 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800870 certmap, ext = common.ReadApkCerts(input_zip)
871
872 self.assertDictEqual(self.APKCERTS_CERTMAP3, certmap)
873 self.assertEqual('.xz', ext)
874
875 def test_ReadApkCerts_CompressedAndNoncompressedApks(self):
876 target_files = self._write_apkcerts_txt(
877 self.APKCERTS_TXT1 + self.APKCERTS_TXT2,
878 ['Compressed1.apk.gz', 'Compressed3.apk'])
879
Kelvin Zhang928c2342020-09-22 16:15:57 -0400880 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800881 certmap, ext = common.ReadApkCerts(input_zip)
882
883 certmap_merged = self.APKCERTS_CERTMAP1.copy()
884 certmap_merged.update(self.APKCERTS_CERTMAP2)
885 self.assertDictEqual(certmap_merged, certmap)
886 self.assertEqual('.gz', ext)
887
888 def test_ReadApkCerts_MultipleCompressionMethods(self):
889 target_files = self._write_apkcerts_txt(
890 self.APKCERTS_TXT2 + self.APKCERTS_TXT3,
891 ['Compressed1.apk.gz', 'Compressed4.apk.xz'])
892
Kelvin Zhang928c2342020-09-22 16:15:57 -0400893 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800894 self.assertRaises(ValueError, common.ReadApkCerts, input_zip)
895
896 def test_ReadApkCerts_MismatchingKeys(self):
897 malformed_apkcerts_txt = (
898 'name="App1.apk" certificate="certs/cert1.x509.pem"'
899 ' private_key="certs/cert2.pk8"\n'
900 )
901 target_files = self._write_apkcerts_txt(malformed_apkcerts_txt)
902
Kelvin Zhang928c2342020-09-22 16:15:57 -0400903 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Bao818ddf52018-01-05 11:17:34 -0800904 self.assertRaises(ValueError, common.ReadApkCerts, input_zip)
905
Bill Peckham5c7b0342020-04-03 15:36:23 -0700906 def test_ReadApkCerts_WithWithoutOptionalFields(self):
907 target_files = self._write_apkcerts_txt(self.APKCERTS_TXT4)
Kelvin Zhang928c2342020-09-22 16:15:57 -0400908 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Bill Peckham5c7b0342020-04-03 15:36:23 -0700909 certmap, ext = common.ReadApkCerts(input_zip)
910
911 self.assertDictEqual(self.APKCERTS_CERTMAP4, certmap)
912 self.assertIsNone(ext)
913
Tao Bao04e1f012018-02-04 12:13:35 -0800914 def test_ExtractPublicKey(self):
Tao Bao17e4e612018-02-16 17:12:54 -0800915 cert = os.path.join(self.testdata_dir, 'testkey.x509.pem')
916 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
Tao Baoda30cfa2017-12-01 16:19:46 -0800917 with open(pubkey) as pubkey_fp:
Tao Bao04e1f012018-02-04 12:13:35 -0800918 self.assertEqual(pubkey_fp.read(), common.ExtractPublicKey(cert))
919
920 def test_ExtractPublicKey_invalidInput(self):
Tao Bao17e4e612018-02-16 17:12:54 -0800921 wrong_input = os.path.join(self.testdata_dir, 'testkey.pk8')
Tao Bao04e1f012018-02-04 12:13:35 -0800922 self.assertRaises(AssertionError, common.ExtractPublicKey, wrong_input)
923
Tao Bao82490d32019-04-09 00:12:30 -0700924 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao2cc0ca12019-03-15 10:44:43 -0700925 def test_ExtractAvbPublicKey(self):
926 privkey = os.path.join(self.testdata_dir, 'testkey.key')
927 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
Tao Bao1ac886e2019-06-26 11:58:22 -0700928 extracted_from_privkey = common.ExtractAvbPublicKey('avbtool', privkey)
929 extracted_from_pubkey = common.ExtractAvbPublicKey('avbtool', pubkey)
930 with open(extracted_from_privkey, 'rb') as privkey_fp, \
931 open(extracted_from_pubkey, 'rb') as pubkey_fp:
Tao Bao2cc0ca12019-03-15 10:44:43 -0700932 self.assertEqual(privkey_fp.read(), pubkey_fp.read())
933
Tao Bao17e4e612018-02-16 17:12:54 -0800934 def test_ParseCertificate(self):
935 cert = os.path.join(self.testdata_dir, 'testkey.x509.pem')
936
937 cmd = ['openssl', 'x509', '-in', cert, '-outform', 'DER']
Tao Baoda30cfa2017-12-01 16:19:46 -0800938 proc = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
939 universal_newlines=False)
Tao Bao17e4e612018-02-16 17:12:54 -0800940 expected, _ = proc.communicate()
941 self.assertEqual(0, proc.returncode)
942
943 with open(cert) as cert_fp:
944 actual = common.ParseCertificate(cert_fp.read())
945 self.assertEqual(expected, actual)
946
Tao Bao82490d32019-04-09 00:12:30 -0700947 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700948 def test_GetMinSdkVersion(self):
949 test_app = os.path.join(self.testdata_dir, 'TestApp.apk')
950 self.assertEqual('24', common.GetMinSdkVersion(test_app))
951
Tao Bao82490d32019-04-09 00:12:30 -0700952 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700953 def test_GetMinSdkVersion_invalidInput(self):
954 self.assertRaises(
955 common.ExternalError, common.GetMinSdkVersion, 'does-not-exist.apk')
956
Tao Bao82490d32019-04-09 00:12:30 -0700957 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700958 def test_GetMinSdkVersionInt(self):
959 test_app = os.path.join(self.testdata_dir, 'TestApp.apk')
960 self.assertEqual(24, common.GetMinSdkVersionInt(test_app, {}))
961
Tao Bao82490d32019-04-09 00:12:30 -0700962 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700963 def test_GetMinSdkVersionInt_invalidInput(self):
964 self.assertRaises(
965 common.ExternalError, common.GetMinSdkVersionInt, 'does-not-exist.apk',
966 {})
967
Tao Bao818ddf52018-01-05 11:17:34 -0800968
Tao Bao65b94e92018-10-11 21:57:26 -0700969class CommonUtilsTest(test_utils.ReleaseToolsTestCase):
Tao Baofc7e0e02018-02-13 13:54:02 -0800970
Tao Bao02a08592018-07-22 12:40:45 -0700971 def setUp(self):
972 self.testdata_dir = test_utils.get_testdata_dir()
973
Tao Bao82490d32019-04-09 00:12:30 -0700974 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -0800975 def test_GetSparseImage_emptyBlockMapFile(self):
976 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -0400977 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baofc7e0e02018-02-13 13:54:02 -0800978 target_files_zip.write(
979 test_utils.construct_sparse_image([
980 (0xCAC1, 6),
981 (0xCAC3, 3),
982 (0xCAC1, 4)]),
983 arcname='IMAGES/system.img')
984 target_files_zip.writestr('IMAGES/system.map', '')
985 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 8))
986 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
987
Tao Baodba59ee2018-01-09 13:21:02 -0800988 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -0400989 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baodba59ee2018-01-09 13:21:02 -0800990 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
Tao Baofc7e0e02018-02-13 13:54:02 -0800991
992 self.assertDictEqual(
993 {
994 '__COPY': RangeSet("0"),
995 '__NONZERO-0': RangeSet("1-5 9-12"),
996 },
997 sparse_image.file_map)
998
Daniel Normand3351562020-10-29 12:33:11 -0700999 def test_SharedUidPartitionViolations(self):
1000 uid_dict = {
1001 'android.uid.phone': {
1002 'system': ['system_phone.apk'],
1003 'system_ext': ['system_ext_phone.apk'],
1004 },
1005 'android.uid.wifi': {
1006 'vendor': ['vendor_wifi.apk'],
1007 'odm': ['odm_wifi.apk'],
1008 },
1009 }
1010 errors = common.SharedUidPartitionViolations(
1011 uid_dict, [('system', 'system_ext'), ('vendor', 'odm')])
1012 self.assertEqual(errors, [])
1013
1014 def test_SharedUidPartitionViolations_Violation(self):
1015 uid_dict = {
1016 'android.uid.phone': {
1017 'system': ['system_phone.apk'],
1018 'vendor': ['vendor_phone.apk'],
1019 },
1020 }
1021 errors = common.SharedUidPartitionViolations(
1022 uid_dict, [('system', 'system_ext'), ('vendor', 'odm')])
1023 self.assertIn(
1024 ('APK sharedUserId "android.uid.phone" found across partition groups '
1025 'in partitions "system,vendor"'), errors)
1026
Tao Baob2de7d92019-04-10 10:01:47 -07001027 def test_GetSparseImage_missingImageFile(self):
Tao Baofc7e0e02018-02-13 13:54:02 -08001028 self.assertRaises(
Tao Baob2de7d92019-04-10 10:01:47 -07001029 AssertionError, common.GetSparseImage, 'system2', self.testdata_dir,
1030 None, False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001031 self.assertRaises(
Tao Baob2de7d92019-04-10 10:01:47 -07001032 AssertionError, common.GetSparseImage, 'unknown', self.testdata_dir,
1033 None, False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001034
Tao Bao82490d32019-04-09 00:12:30 -07001035 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001036 def test_GetSparseImage_missingBlockMapFile(self):
1037 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001038 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baofc7e0e02018-02-13 13:54:02 -08001039 target_files_zip.write(
1040 test_utils.construct_sparse_image([
1041 (0xCAC1, 6),
1042 (0xCAC3, 3),
1043 (0xCAC1, 4)]),
1044 arcname='IMAGES/system.img')
1045 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 8))
1046 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1047
Tao Baodba59ee2018-01-09 13:21:02 -08001048 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001049 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baodba59ee2018-01-09 13:21:02 -08001050 self.assertRaises(
1051 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
1052 False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001053
Tao Bao82490d32019-04-09 00:12:30 -07001054 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001055 def test_GetSparseImage_sharedBlocks_notAllowed(self):
1056 """Tests the case of having overlapping blocks but disallowed."""
1057 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001058 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baofc7e0e02018-02-13 13:54:02 -08001059 target_files_zip.write(
1060 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1061 arcname='IMAGES/system.img')
1062 # Block 10 is shared between two files.
1063 target_files_zip.writestr(
1064 'IMAGES/system.map',
1065 '\n'.join([
1066 '/system/file1 1-5 9-10',
1067 '/system/file2 10-12']))
1068 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1069 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1070
Tao Baodba59ee2018-01-09 13:21:02 -08001071 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001072 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baodba59ee2018-01-09 13:21:02 -08001073 self.assertRaises(
1074 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
1075 False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001076
Tao Bao82490d32019-04-09 00:12:30 -07001077 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001078 def test_GetSparseImage_sharedBlocks_allowed(self):
1079 """Tests the case for target using BOARD_EXT4_SHARE_DUP_BLOCKS := true."""
1080 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001081 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baofc7e0e02018-02-13 13:54:02 -08001082 # Construct an image with a care_map of "0-5 9-12".
1083 target_files_zip.write(
1084 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1085 arcname='IMAGES/system.img')
1086 # Block 10 is shared between two files.
1087 target_files_zip.writestr(
1088 'IMAGES/system.map',
1089 '\n'.join([
1090 '/system/file1 1-5 9-10',
1091 '/system/file2 10-12']))
1092 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1093 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1094
Tao Baodba59ee2018-01-09 13:21:02 -08001095 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001096 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baodba59ee2018-01-09 13:21:02 -08001097 sparse_image = common.GetSparseImage('system', tempdir, input_zip, True)
Tao Baofc7e0e02018-02-13 13:54:02 -08001098
1099 self.assertDictEqual(
1100 {
1101 '__COPY': RangeSet("0"),
1102 '__NONZERO-0': RangeSet("6-8 13-15"),
1103 '/system/file1': RangeSet("1-5 9-10"),
1104 '/system/file2': RangeSet("11-12"),
1105 },
1106 sparse_image.file_map)
1107
1108 # '/system/file2' should be marked with 'uses_shared_blocks', but not with
1109 # 'incomplete'.
1110 self.assertTrue(
1111 sparse_image.file_map['/system/file2'].extra['uses_shared_blocks'])
1112 self.assertNotIn(
1113 'incomplete', sparse_image.file_map['/system/file2'].extra)
1114
Tao Baoa264fef2019-10-06 21:55:20 -07001115 # '/system/file1' will only contain one field -- a copy of the input text.
1116 self.assertEqual(1, len(sparse_image.file_map['/system/file1'].extra))
1117
1118 # Meta entries should not have any extra tag.
Tao Baofc7e0e02018-02-13 13:54:02 -08001119 self.assertFalse(sparse_image.file_map['__COPY'].extra)
1120 self.assertFalse(sparse_image.file_map['__NONZERO-0'].extra)
Tao Baofc7e0e02018-02-13 13:54:02 -08001121
Tao Bao82490d32019-04-09 00:12:30 -07001122 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001123 def test_GetSparseImage_incompleteRanges(self):
1124 """Tests the case of ext4 images with holes."""
1125 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001126 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baofc7e0e02018-02-13 13:54:02 -08001127 target_files_zip.write(
1128 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1129 arcname='IMAGES/system.img')
1130 target_files_zip.writestr(
1131 'IMAGES/system.map',
1132 '\n'.join([
1133 '/system/file1 1-5 9-10',
1134 '/system/file2 11-12']))
1135 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1136 # '/system/file2' has less blocks listed (2) than actual (3).
1137 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1138
Tao Baodba59ee2018-01-09 13:21:02 -08001139 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001140 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baodba59ee2018-01-09 13:21:02 -08001141 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001142
Tao Baoa264fef2019-10-06 21:55:20 -07001143 self.assertEqual(
1144 '1-5 9-10',
1145 sparse_image.file_map['/system/file1'].extra['text_str'])
Tao Baofc7e0e02018-02-13 13:54:02 -08001146 self.assertTrue(sparse_image.file_map['/system/file2'].extra['incomplete'])
1147
Tao Bao82490d32019-04-09 00:12:30 -07001148 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -07001149 def test_GetSparseImage_systemRootImage_filenameWithExtraLeadingSlash(self):
1150 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001151 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001152 target_files_zip.write(
1153 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1154 arcname='IMAGES/system.img')
1155 target_files_zip.writestr(
1156 'IMAGES/system.map',
1157 '\n'.join([
1158 '//system/file1 1-5 9-10',
1159 '//system/file2 11-12',
1160 '/system/app/file3 13-15']))
1161 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1162 # '/system/file2' has less blocks listed (2) than actual (3).
1163 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1164 # '/system/app/file3' has less blocks listed (3) than actual (4).
1165 target_files_zip.writestr('SYSTEM/app/file3', os.urandom(4096 * 4))
1166
1167 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001168 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001169 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
1170
Tao Baoa264fef2019-10-06 21:55:20 -07001171 self.assertEqual(
1172 '1-5 9-10',
1173 sparse_image.file_map['//system/file1'].extra['text_str'])
Tao Baod3554e62018-07-10 15:31:22 -07001174 self.assertTrue(sparse_image.file_map['//system/file2'].extra['incomplete'])
1175 self.assertTrue(
1176 sparse_image.file_map['/system/app/file3'].extra['incomplete'])
1177
Tao Bao82490d32019-04-09 00:12:30 -07001178 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -07001179 def test_GetSparseImage_systemRootImage_nonSystemFiles(self):
1180 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001181 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001182 target_files_zip.write(
1183 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1184 arcname='IMAGES/system.img')
1185 target_files_zip.writestr(
1186 'IMAGES/system.map',
1187 '\n'.join([
1188 '//system/file1 1-5 9-10',
1189 '//init.rc 13-15']))
1190 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1191 # '/init.rc' has less blocks listed (3) than actual (4).
1192 target_files_zip.writestr('ROOT/init.rc', os.urandom(4096 * 4))
1193
1194 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001195 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001196 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
1197
Tao Baoa264fef2019-10-06 21:55:20 -07001198 self.assertEqual(
1199 '1-5 9-10',
1200 sparse_image.file_map['//system/file1'].extra['text_str'])
Tao Baod3554e62018-07-10 15:31:22 -07001201 self.assertTrue(sparse_image.file_map['//init.rc'].extra['incomplete'])
1202
Tao Bao82490d32019-04-09 00:12:30 -07001203 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -07001204 def test_GetSparseImage_fileNotFound(self):
1205 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001206 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001207 target_files_zip.write(
1208 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1209 arcname='IMAGES/system.img')
1210 target_files_zip.writestr(
1211 'IMAGES/system.map',
1212 '\n'.join([
1213 '//system/file1 1-5 9-10',
1214 '//system/file2 11-12']))
1215 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1216
1217 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001218 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001219 self.assertRaises(
1220 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
1221 False)
1222
Tao Bao82490d32019-04-09 00:12:30 -07001223 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001224 def test_GetAvbChainedPartitionArg(self):
1225 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
1226 info_dict = {
1227 'avb_avbtool': 'avbtool',
1228 'avb_system_key_path': pubkey,
1229 'avb_system_rollback_index_location': 2,
1230 }
1231 args = common.GetAvbChainedPartitionArg('system', info_dict).split(':')
1232 self.assertEqual(3, len(args))
1233 self.assertEqual('system', args[0])
1234 self.assertEqual('2', args[1])
1235 self.assertTrue(os.path.exists(args[2]))
1236
Tao Bao82490d32019-04-09 00:12:30 -07001237 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001238 def test_GetAvbChainedPartitionArg_withPrivateKey(self):
1239 key = os.path.join(self.testdata_dir, 'testkey.key')
1240 info_dict = {
1241 'avb_avbtool': 'avbtool',
1242 'avb_product_key_path': key,
1243 'avb_product_rollback_index_location': 2,
1244 }
1245 args = common.GetAvbChainedPartitionArg('product', info_dict).split(':')
1246 self.assertEqual(3, len(args))
1247 self.assertEqual('product', args[0])
1248 self.assertEqual('2', args[1])
1249 self.assertTrue(os.path.exists(args[2]))
1250
Tao Bao82490d32019-04-09 00:12:30 -07001251 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001252 def test_GetAvbChainedPartitionArg_withSpecifiedKey(self):
1253 info_dict = {
1254 'avb_avbtool': 'avbtool',
1255 'avb_system_key_path': 'does-not-exist',
1256 'avb_system_rollback_index_location': 2,
1257 }
1258 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
1259 args = common.GetAvbChainedPartitionArg(
1260 'system', info_dict, pubkey).split(':')
1261 self.assertEqual(3, len(args))
1262 self.assertEqual('system', args[0])
1263 self.assertEqual('2', args[1])
1264 self.assertTrue(os.path.exists(args[2]))
1265
Tao Bao82490d32019-04-09 00:12:30 -07001266 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001267 def test_GetAvbChainedPartitionArg_invalidKey(self):
1268 pubkey = os.path.join(self.testdata_dir, 'testkey_with_passwd.x509.pem')
1269 info_dict = {
1270 'avb_avbtool': 'avbtool',
1271 'avb_system_key_path': pubkey,
1272 'avb_system_rollback_index_location': 2,
1273 }
1274 self.assertRaises(
Tao Bao986ee862018-10-04 15:46:16 -07001275 common.ExternalError, common.GetAvbChainedPartitionArg, 'system',
1276 info_dict)
Tao Bao02a08592018-07-22 12:40:45 -07001277
Tao Baoa57ab9f2018-08-24 12:08:38 -07001278 INFO_DICT_DEFAULT = {
1279 'recovery_api_version': 3,
1280 'fstab_version': 2,
1281 'system_root_image': 'true',
1282 'no_recovery' : 'true',
1283 'recovery_as_boot': 'true',
1284 }
1285
Daniel Norman4cc9df62019-07-18 10:11:07 -07001286 def test_LoadListFromFile(self):
1287 file_path = os.path.join(self.testdata_dir,
1288 'merge_config_framework_item_list')
1289 contents = common.LoadListFromFile(file_path)
1290 expected_contents = [
1291 'META/apkcerts.txt',
1292 'META/filesystem_config.txt',
1293 'META/root_filesystem_config.txt',
1294 'META/system_manifest.xml',
1295 'META/system_matrix.xml',
1296 'META/update_engine_config.txt',
1297 'PRODUCT/*',
1298 'ROOT/*',
1299 'SYSTEM/*',
1300 ]
1301 self.assertEqual(sorted(contents), sorted(expected_contents))
1302
Tao Baoa57ab9f2018-08-24 12:08:38 -07001303 @staticmethod
1304 def _test_LoadInfoDict_createTargetFiles(info_dict, fstab_path):
1305 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001306 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001307 info_values = ''.join(
Tao Baoda30cfa2017-12-01 16:19:46 -08001308 ['{}={}\n'.format(k, v) for k, v in sorted(info_dict.items())])
Tao Baoa57ab9f2018-08-24 12:08:38 -07001309 common.ZipWriteStr(target_files_zip, 'META/misc_info.txt', info_values)
1310
1311 FSTAB_TEMPLATE = "/dev/block/system {} ext4 ro,barrier=1 defaults"
1312 if info_dict.get('system_root_image') == 'true':
1313 fstab_values = FSTAB_TEMPLATE.format('/')
1314 else:
1315 fstab_values = FSTAB_TEMPLATE.format('/system')
1316 common.ZipWriteStr(target_files_zip, fstab_path, fstab_values)
Tao Bao410ad8b2018-08-24 12:08:38 -07001317
1318 common.ZipWriteStr(
1319 target_files_zip, 'META/file_contexts', 'file-contexts')
Tao Baoa57ab9f2018-08-24 12:08:38 -07001320 return target_files
1321
1322 def test_LoadInfoDict(self):
1323 target_files = self._test_LoadInfoDict_createTargetFiles(
1324 self.INFO_DICT_DEFAULT,
1325 'BOOT/RAMDISK/system/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001326 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001327 loaded_dict = common.LoadInfoDict(target_files_zip)
1328 self.assertEqual(3, loaded_dict['recovery_api_version'])
1329 self.assertEqual(2, loaded_dict['fstab_version'])
1330 self.assertIn('/', loaded_dict['fstab'])
1331 self.assertIn('/system', loaded_dict['fstab'])
1332
1333 def test_LoadInfoDict_legacyRecoveryFstabPath(self):
1334 target_files = self._test_LoadInfoDict_createTargetFiles(
1335 self.INFO_DICT_DEFAULT,
1336 'BOOT/RAMDISK/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001337 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001338 loaded_dict = common.LoadInfoDict(target_files_zip)
1339 self.assertEqual(3, loaded_dict['recovery_api_version'])
1340 self.assertEqual(2, loaded_dict['fstab_version'])
1341 self.assertIn('/', loaded_dict['fstab'])
1342 self.assertIn('/system', loaded_dict['fstab'])
1343
Tao Bao82490d32019-04-09 00:12:30 -07001344 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoa57ab9f2018-08-24 12:08:38 -07001345 def test_LoadInfoDict_dirInput(self):
1346 target_files = self._test_LoadInfoDict_createTargetFiles(
1347 self.INFO_DICT_DEFAULT,
1348 'BOOT/RAMDISK/system/etc/recovery.fstab')
1349 unzipped = common.UnzipTemp(target_files)
1350 loaded_dict = common.LoadInfoDict(unzipped)
1351 self.assertEqual(3, loaded_dict['recovery_api_version'])
1352 self.assertEqual(2, loaded_dict['fstab_version'])
1353 self.assertIn('/', loaded_dict['fstab'])
1354 self.assertIn('/system', loaded_dict['fstab'])
1355
Tao Bao82490d32019-04-09 00:12:30 -07001356 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoa57ab9f2018-08-24 12:08:38 -07001357 def test_LoadInfoDict_dirInput_legacyRecoveryFstabPath(self):
1358 target_files = self._test_LoadInfoDict_createTargetFiles(
1359 self.INFO_DICT_DEFAULT,
1360 'BOOT/RAMDISK/system/etc/recovery.fstab')
1361 unzipped = common.UnzipTemp(target_files)
1362 loaded_dict = common.LoadInfoDict(unzipped)
1363 self.assertEqual(3, loaded_dict['recovery_api_version'])
1364 self.assertEqual(2, loaded_dict['fstab_version'])
1365 self.assertIn('/', loaded_dict['fstab'])
1366 self.assertIn('/system', loaded_dict['fstab'])
1367
1368 def test_LoadInfoDict_systemRootImageFalse(self):
1369 # Devices not using system-as-root nor recovery-as-boot. Non-A/B devices
1370 # launched prior to P will likely have this config.
1371 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
1372 del info_dict['no_recovery']
1373 del info_dict['system_root_image']
1374 del info_dict['recovery_as_boot']
1375 target_files = self._test_LoadInfoDict_createTargetFiles(
1376 info_dict,
1377 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001378 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001379 loaded_dict = common.LoadInfoDict(target_files_zip)
1380 self.assertEqual(3, loaded_dict['recovery_api_version'])
1381 self.assertEqual(2, loaded_dict['fstab_version'])
1382 self.assertNotIn('/', loaded_dict['fstab'])
1383 self.assertIn('/system', loaded_dict['fstab'])
1384
1385 def test_LoadInfoDict_recoveryAsBootFalse(self):
1386 # Devices using system-as-root, but with standalone recovery image. Non-A/B
1387 # devices launched since P will likely have this config.
1388 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
1389 del info_dict['no_recovery']
1390 del info_dict['recovery_as_boot']
1391 target_files = self._test_LoadInfoDict_createTargetFiles(
1392 info_dict,
1393 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001394 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001395 loaded_dict = common.LoadInfoDict(target_files_zip)
1396 self.assertEqual(3, loaded_dict['recovery_api_version'])
1397 self.assertEqual(2, loaded_dict['fstab_version'])
1398 self.assertIn('/', loaded_dict['fstab'])
1399 self.assertIn('/system', loaded_dict['fstab'])
1400
1401 def test_LoadInfoDict_noRecoveryTrue(self):
1402 # Device doesn't have a recovery partition at all.
1403 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
1404 del info_dict['recovery_as_boot']
1405 target_files = self._test_LoadInfoDict_createTargetFiles(
1406 info_dict,
1407 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001408 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001409 loaded_dict = common.LoadInfoDict(target_files_zip)
1410 self.assertEqual(3, loaded_dict['recovery_api_version'])
1411 self.assertEqual(2, loaded_dict['fstab_version'])
1412 self.assertIsNone(loaded_dict['fstab'])
1413
Tao Bao82490d32019-04-09 00:12:30 -07001414 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao410ad8b2018-08-24 12:08:38 -07001415 def test_LoadInfoDict_missingMetaMiscInfoTxt(self):
1416 target_files = self._test_LoadInfoDict_createTargetFiles(
1417 self.INFO_DICT_DEFAULT,
1418 'BOOT/RAMDISK/system/etc/recovery.fstab')
1419 common.ZipDelete(target_files, 'META/misc_info.txt')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001420 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Bao410ad8b2018-08-24 12:08:38 -07001421 self.assertRaises(ValueError, common.LoadInfoDict, target_files_zip)
1422
Tao Bao82490d32019-04-09 00:12:30 -07001423 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao410ad8b2018-08-24 12:08:38 -07001424 def test_LoadInfoDict_repacking(self):
1425 target_files = self._test_LoadInfoDict_createTargetFiles(
1426 self.INFO_DICT_DEFAULT,
1427 'BOOT/RAMDISK/system/etc/recovery.fstab')
1428 unzipped = common.UnzipTemp(target_files)
1429 loaded_dict = common.LoadInfoDict(unzipped, True)
1430 self.assertEqual(3, loaded_dict['recovery_api_version'])
1431 self.assertEqual(2, loaded_dict['fstab_version'])
1432 self.assertIn('/', loaded_dict['fstab'])
1433 self.assertIn('/system', loaded_dict['fstab'])
1434 self.assertEqual(
1435 os.path.join(unzipped, 'ROOT'), loaded_dict['root_dir'])
1436 self.assertEqual(
1437 os.path.join(unzipped, 'META', 'root_filesystem_config.txt'),
1438 loaded_dict['root_fs_config'])
1439
1440 def test_LoadInfoDict_repackingWithZipFileInput(self):
1441 target_files = self._test_LoadInfoDict_createTargetFiles(
1442 self.INFO_DICT_DEFAULT,
1443 'BOOT/RAMDISK/system/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001444 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Bao410ad8b2018-08-24 12:08:38 -07001445 self.assertRaises(
1446 AssertionError, common.LoadInfoDict, target_files_zip, True)
1447
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001448 def test_MergeDynamicPartitionInfoDicts_ReturnsMergedDict(self):
1449 framework_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001450 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001451 'super_partition_groups': 'group_a',
1452 'dynamic_partition_list': 'system',
Daniel Norman55417142019-11-25 16:04:36 -08001453 'super_group_a_partition_list': 'system',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001454 }
1455 vendor_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001456 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001457 'super_partition_groups': 'group_a group_b',
1458 'dynamic_partition_list': 'vendor product',
Daniel Normanb0c75912020-09-24 14:30:21 -07001459 'super_block_devices': 'super',
1460 'super_super_device_size': '3000',
Daniel Norman55417142019-11-25 16:04:36 -08001461 'super_group_a_partition_list': 'vendor',
1462 'super_group_a_group_size': '1000',
1463 'super_group_b_partition_list': 'product',
1464 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001465 }
1466 merged_dict = common.MergeDynamicPartitionInfoDicts(
1467 framework_dict=framework_dict,
Daniel Norman55417142019-11-25 16:04:36 -08001468 vendor_dict=vendor_dict)
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001469 expected_merged_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001470 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001471 'super_partition_groups': 'group_a group_b',
Daniel Normanb0c75912020-09-24 14:30:21 -07001472 'dynamic_partition_list': 'product system vendor',
1473 'super_block_devices': 'super',
1474 'super_super_device_size': '3000',
Daniel Norman55417142019-11-25 16:04:36 -08001475 'super_group_a_partition_list': 'system vendor',
1476 'super_group_a_group_size': '1000',
1477 'super_group_b_partition_list': 'product',
1478 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001479 }
1480 self.assertEqual(merged_dict, expected_merged_dict)
1481
1482 def test_MergeDynamicPartitionInfoDicts_IgnoringFrameworkGroupSize(self):
1483 framework_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001484 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001485 'super_partition_groups': 'group_a',
1486 'dynamic_partition_list': 'system',
Daniel Norman55417142019-11-25 16:04:36 -08001487 'super_group_a_partition_list': 'system',
1488 'super_group_a_group_size': '5000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001489 }
1490 vendor_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001491 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001492 'super_partition_groups': 'group_a group_b',
1493 'dynamic_partition_list': 'vendor product',
Daniel Norman55417142019-11-25 16:04:36 -08001494 'super_group_a_partition_list': 'vendor',
1495 'super_group_a_group_size': '1000',
1496 'super_group_b_partition_list': 'product',
1497 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001498 }
1499 merged_dict = common.MergeDynamicPartitionInfoDicts(
1500 framework_dict=framework_dict,
Daniel Norman55417142019-11-25 16:04:36 -08001501 vendor_dict=vendor_dict)
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001502 expected_merged_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001503 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001504 'super_partition_groups': 'group_a group_b',
Daniel Normanb0c75912020-09-24 14:30:21 -07001505 'dynamic_partition_list': 'product system vendor',
Daniel Norman55417142019-11-25 16:04:36 -08001506 'super_group_a_partition_list': 'system vendor',
1507 'super_group_a_group_size': '1000',
1508 'super_group_b_partition_list': 'product',
1509 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001510 }
1511 self.assertEqual(merged_dict, expected_merged_dict)
1512
Daniel Norman276f0622019-07-26 14:13:51 -07001513 def test_GetAvbPartitionArg(self):
1514 info_dict = {}
1515 cmd = common.GetAvbPartitionArg('system', '/path/to/system.img', info_dict)
1516 self.assertEqual(
1517 ['--include_descriptors_from_image', '/path/to/system.img'], cmd)
1518
1519 @test_utils.SkipIfExternalToolsUnavailable()
1520 def test_AppendVBMetaArgsForPartition_vendorAsChainedPartition(self):
1521 testdata_dir = test_utils.get_testdata_dir()
1522 pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
1523 info_dict = {
1524 'avb_avbtool': 'avbtool',
1525 'avb_vendor_key_path': pubkey,
1526 'avb_vendor_rollback_index_location': 5,
1527 }
1528 cmd = common.GetAvbPartitionArg('vendor', '/path/to/vendor.img', info_dict)
1529 self.assertEqual(2, len(cmd))
1530 self.assertEqual('--chain_partition', cmd[0])
1531 chained_partition_args = cmd[1].split(':')
1532 self.assertEqual(3, len(chained_partition_args))
1533 self.assertEqual('vendor', chained_partition_args[0])
1534 self.assertEqual('5', chained_partition_args[1])
1535 self.assertTrue(os.path.exists(chained_partition_args[2]))
1536
Tao Bao3612c882019-10-14 17:49:31 -07001537 @test_utils.SkipIfExternalToolsUnavailable()
1538 def test_AppendVBMetaArgsForPartition_recoveryAsChainedPartition_nonAb(self):
1539 testdata_dir = test_utils.get_testdata_dir()
1540 pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
1541 info_dict = {
1542 'avb_avbtool': 'avbtool',
1543 'avb_recovery_key_path': pubkey,
1544 'avb_recovery_rollback_index_location': 3,
1545 }
1546 cmd = common.GetAvbPartitionArg(
1547 'recovery', '/path/to/recovery.img', info_dict)
1548 self.assertFalse(cmd)
1549
1550 @test_utils.SkipIfExternalToolsUnavailable()
1551 def test_AppendVBMetaArgsForPartition_recoveryAsChainedPartition_ab(self):
1552 testdata_dir = test_utils.get_testdata_dir()
1553 pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
1554 info_dict = {
1555 'ab_update': 'true',
1556 'avb_avbtool': 'avbtool',
1557 'avb_recovery_key_path': pubkey,
1558 'avb_recovery_rollback_index_location': 3,
1559 }
1560 cmd = common.GetAvbPartitionArg(
1561 'recovery', '/path/to/recovery.img', info_dict)
1562 self.assertEqual(2, len(cmd))
1563 self.assertEqual('--chain_partition', cmd[0])
1564 chained_partition_args = cmd[1].split(':')
1565 self.assertEqual(3, len(chained_partition_args))
1566 self.assertEqual('recovery', chained_partition_args[0])
1567 self.assertEqual('3', chained_partition_args[1])
1568 self.assertTrue(os.path.exists(chained_partition_args[2]))
1569
Tianjie20dd8f22020-04-19 15:51:16 -07001570 def test_BuildVBMeta_appendAftlCommandSyntax(self):
1571 testdata_dir = test_utils.get_testdata_dir()
1572 common.OPTIONS.info_dict = {
1573 'ab_update': 'true',
1574 'avb_avbtool': 'avbtool',
Tianjie Xu0fde41e2020-05-09 05:24:18 +00001575 'build.prop': common.PartitionBuildProps.FromDictionary(
1576 'system', {
1577 'ro.build.version.incremental': '6285659',
1578 'ro.product.device': 'coral',
1579 'ro.build.fingerprint':
1580 'google/coral/coral:R/RP1A.200311.002/'
1581 '6285659:userdebug/dev-keys'}
1582 ),
Tianjie20dd8f22020-04-19 15:51:16 -07001583 }
1584 common.OPTIONS.aftl_tool_path = 'aftltool'
1585 common.OPTIONS.aftl_server = 'log.endpoints.aftl-dev.cloud.goog:9000'
1586 common.OPTIONS.aftl_key_path = os.path.join(testdata_dir,
1587 'test_transparency_key.pub')
1588 common.OPTIONS.aftl_manufacturer_key_path = os.path.join(
1589 testdata_dir, 'test_aftl_rsa4096.pem')
1590
1591 vbmeta_image = tempfile.NamedTemporaryFile(delete=False)
1592 cmd = common.ConstructAftlMakeImageCommands(vbmeta_image.name)
1593 expected_cmd = [
1594 'aftltool', 'make_icp_from_vbmeta',
1595 '--vbmeta_image_path', 'place_holder',
1596 '--output', vbmeta_image.name,
1597 '--version_incremental', '6285659',
1598 '--transparency_log_servers',
1599 'log.endpoints.aftl-dev.cloud.goog:9000,{}'.format(
1600 common.OPTIONS.aftl_key_path),
1601 '--manufacturer_key', common.OPTIONS.aftl_manufacturer_key_path,
1602 '--algorithm', 'SHA256_RSA4096',
1603 '--padding', '4096']
1604
1605 # ignore the place holder, i.e. path to a temp file
1606 self.assertEqual(cmd[:3], expected_cmd[:3])
1607 self.assertEqual(cmd[4:], expected_cmd[4:])
1608
1609 @unittest.skip("enable after we have a server for public")
1610 def test_BuildVBMeta_appendAftlContactServer(self):
Tianjie Xueaed60c2020-03-12 00:33:28 -07001611 testdata_dir = test_utils.get_testdata_dir()
1612 common.OPTIONS.info_dict = {
1613 'ab_update': 'true',
1614 'avb_avbtool': 'avbtool',
Tianjie Xu0fde41e2020-05-09 05:24:18 +00001615 'build.prop': common.PartitionBuildProps.FromDictionary(
1616 'system', {
1617 'ro.build.version.incremental': '6285659',
1618 'ro.product.device': 'coral',
1619 'ro.build.fingerprint':
1620 'google/coral/coral:R/RP1A.200311.002/'
1621 '6285659:userdebug/dev-keys'}
1622 )
Tianjie Xueaed60c2020-03-12 00:33:28 -07001623 }
Tianjie0f307452020-04-01 12:20:21 -07001624 common.OPTIONS.aftl_tool_path = "aftltool"
Tianjie Xueaed60c2020-03-12 00:33:28 -07001625 common.OPTIONS.aftl_server = "log.endpoints.aftl-dev.cloud.goog:9000"
1626 common.OPTIONS.aftl_key_path = os.path.join(testdata_dir,
1627 'test_transparency_key.pub')
1628 common.OPTIONS.aftl_manufacturer_key_path = os.path.join(
1629 testdata_dir, 'test_aftl_rsa4096.pem')
1630
1631 input_dir = common.MakeTempDir()
1632 system_image = common.MakeTempFile()
1633 build_image_cmd = ['mkuserimg_mke2fs', input_dir, system_image, 'ext4',
1634 '/system', str(4096 * 100), '-j', '0', '-s']
1635 common.RunAndCheckOutput(build_image_cmd)
1636
1637 add_footer_cmd = ['avbtool', 'add_hashtree_footer',
1638 '--partition_size', str(4096 * 150),
1639 '--partition_name', 'system',
1640 '--image', system_image]
1641 common.RunAndCheckOutput(add_footer_cmd)
1642
1643 vbmeta_image = common.MakeTempFile()
1644 common.BuildVBMeta(vbmeta_image, {'system': system_image}, 'vbmeta',
1645 ['system'])
1646
1647 verify_cmd = ['aftltool', 'verify_image_icp', '--vbmeta_image_path',
1648 vbmeta_image, '--transparency_log_pub_keys',
1649 common.OPTIONS.aftl_key_path]
1650 common.RunAndCheckOutput(verify_cmd)
1651
Tao Baofc7e0e02018-02-13 13:54:02 -08001652
Tao Bao65b94e92018-10-11 21:57:26 -07001653class InstallRecoveryScriptFormatTest(test_utils.ReleaseToolsTestCase):
Tao Bao1c830bf2017-12-25 10:43:47 -08001654 """Checks the format of install-recovery.sh.
Tianjie Xu9c384d22017-06-20 17:00:55 -07001655
Tao Bao1c830bf2017-12-25 10:43:47 -08001656 Its format should match between common.py and validate_target_files.py.
1657 """
Tianjie Xu9c384d22017-06-20 17:00:55 -07001658
1659 def setUp(self):
Tao Bao1c830bf2017-12-25 10:43:47 -08001660 self._tempdir = common.MakeTempDir()
Kelvin Zhangc693d952020-07-22 19:21:22 -04001661 # Create a fake dict that contains the fstab info for boot&recovery.
Tianjie Xu9c384d22017-06-20 17:00:55 -07001662 self._info = {"fstab" : {}}
Kelvin Zhangc693d952020-07-22 19:21:22 -04001663 fake_fstab = [
Tao Bao1c830bf2017-12-25 10:43:47 -08001664 "/dev/soc.0/by-name/boot /boot emmc defaults defaults",
1665 "/dev/soc.0/by-name/recovery /recovery emmc defaults defaults"]
Kelvin Zhangc693d952020-07-22 19:21:22 -04001666 self._info["fstab"] = common.LoadRecoveryFSTab("\n".join, 2, fake_fstab)
Tianjie Xudf055582017-11-07 12:22:58 -08001667 # Construct the gzipped recovery.img and boot.img
1668 self.recovery_data = bytearray([
1669 0x1f, 0x8b, 0x08, 0x00, 0x81, 0x11, 0x02, 0x5a, 0x00, 0x03, 0x2b, 0x4a,
1670 0x4d, 0xce, 0x2f, 0x4b, 0x2d, 0xaa, 0x04, 0x00, 0xc9, 0x93, 0x43, 0xf3,
1671 0x08, 0x00, 0x00, 0x00
1672 ])
1673 # echo -n "boot" | gzip -f | hd
1674 self.boot_data = bytearray([
1675 0x1f, 0x8b, 0x08, 0x00, 0x8c, 0x12, 0x02, 0x5a, 0x00, 0x03, 0x4b, 0xca,
1676 0xcf, 0x2f, 0x01, 0x00, 0xc4, 0xae, 0xed, 0x46, 0x04, 0x00, 0x00, 0x00
1677 ])
Tianjie Xu9c384d22017-06-20 17:00:55 -07001678
1679 def _out_tmp_sink(self, name, data, prefix="SYSTEM"):
1680 loc = os.path.join(self._tempdir, prefix, name)
1681 if not os.path.exists(os.path.dirname(loc)):
1682 os.makedirs(os.path.dirname(loc))
Tao Baoda30cfa2017-12-01 16:19:46 -08001683 with open(loc, "wb") as f:
Tianjie Xu9c384d22017-06-20 17:00:55 -07001684 f.write(data)
1685
1686 def test_full_recovery(self):
Tao Bao31b08072017-11-08 15:50:59 -08001687 recovery_image = common.File("recovery.img", self.recovery_data)
1688 boot_image = common.File("boot.img", self.boot_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001689 self._info["full_recovery_image"] = "true"
1690
1691 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1692 recovery_image, boot_image, self._info)
1693 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1694 self._info)
1695
Tao Bao82490d32019-04-09 00:12:30 -07001696 @test_utils.SkipIfExternalToolsUnavailable()
Tianjie Xu9c384d22017-06-20 17:00:55 -07001697 def test_recovery_from_boot(self):
Tao Bao31b08072017-11-08 15:50:59 -08001698 recovery_image = common.File("recovery.img", self.recovery_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001699 self._out_tmp_sink("recovery.img", recovery_image.data, "IMAGES")
Tao Bao31b08072017-11-08 15:50:59 -08001700 boot_image = common.File("boot.img", self.boot_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001701 self._out_tmp_sink("boot.img", boot_image.data, "IMAGES")
1702
1703 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1704 recovery_image, boot_image, self._info)
1705 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1706 self._info)
1707 # Validate 'recovery-from-boot' with bonus argument.
Tao Baoda30cfa2017-12-01 16:19:46 -08001708 self._out_tmp_sink("etc/recovery-resource.dat", b"bonus", "SYSTEM")
Tianjie Xu9c384d22017-06-20 17:00:55 -07001709 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1710 recovery_image, boot_image, self._info)
1711 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1712 self._info)
Yifan Hong45433e42019-01-18 13:55:25 -08001713
1714
Yifan Hong45433e42019-01-18 13:55:25 -08001715class MockBlockDifference(object):
Tao Baoda30cfa2017-12-01 16:19:46 -08001716
Yifan Hong45433e42019-01-18 13:55:25 -08001717 def __init__(self, partition, tgt, src=None):
1718 self.partition = partition
1719 self.tgt = tgt
1720 self.src = src
Tao Baoda30cfa2017-12-01 16:19:46 -08001721
Yifan Hong45433e42019-01-18 13:55:25 -08001722 def WriteScript(self, script, _, progress=None,
1723 write_verify_script=False):
1724 if progress:
1725 script.AppendExtra("progress({})".format(progress))
1726 script.AppendExtra("patch({});".format(self.partition))
1727 if write_verify_script:
1728 self.WritePostInstallVerifyScript(script)
Tao Baoda30cfa2017-12-01 16:19:46 -08001729
Yifan Hong45433e42019-01-18 13:55:25 -08001730 def WritePostInstallVerifyScript(self, script):
1731 script.AppendExtra("verify({});".format(self.partition))
1732
1733
1734class FakeSparseImage(object):
Tao Baoda30cfa2017-12-01 16:19:46 -08001735
Yifan Hong45433e42019-01-18 13:55:25 -08001736 def __init__(self, size):
1737 self.blocksize = 4096
1738 self.total_blocks = size // 4096
1739 assert size % 4096 == 0, "{} is not a multiple of 4096".format(size)
1740
1741
1742class DynamicPartitionsDifferenceTest(test_utils.ReleaseToolsTestCase):
Tao Baoda30cfa2017-12-01 16:19:46 -08001743
Yifan Hong45433e42019-01-18 13:55:25 -08001744 @staticmethod
1745 def get_op_list(output_path):
Kelvin Zhang928c2342020-09-22 16:15:57 -04001746 with zipfile.ZipFile(output_path, allowZip64=True) as output_zip:
Tao Baoda30cfa2017-12-01 16:19:46 -08001747 with output_zip.open('dynamic_partitions_op_list') as op_list:
1748 return [line.decode().strip() for line in op_list.readlines()
1749 if not line.startswith(b'#')]
Yifan Hong45433e42019-01-18 13:55:25 -08001750
1751 def setUp(self):
Tao Baoe1148042019-10-07 20:00:34 -07001752 self.script = test_utils.MockScriptWriter()
Yifan Hong45433e42019-01-18 13:55:25 -08001753 self.output_path = common.MakeTempFile(suffix='.zip')
1754
1755 def test_full(self):
1756 target_info = common.LoadDictionaryFromLines("""
1757dynamic_partition_list=system vendor
1758super_partition_groups=group_foo
1759super_group_foo_group_size={group_size}
1760super_group_foo_partition_list=system vendor
1761""".format(group_size=4 * GiB).split("\n"))
1762 block_diffs = [MockBlockDifference("system", FakeSparseImage(3 * GiB)),
1763 MockBlockDifference("vendor", FakeSparseImage(1 * GiB))]
1764
1765 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001766 with zipfile.ZipFile(self.output_path, 'w', allowZip64=True) as output_zip:
Yifan Hong45433e42019-01-18 13:55:25 -08001767 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1768
1769 self.assertEqual(str(self.script).strip(), """
1770assert(update_dynamic_partitions(package_extract_file("dynamic_partitions_op_list")));
Yifan Hong45433e42019-01-18 13:55:25 -08001771patch(system);
1772verify(system);
1773unmap_partition("system");
Tao Baof1113e92019-06-18 12:10:14 -07001774patch(vendor);
1775verify(vendor);
1776unmap_partition("vendor");
Yifan Hong45433e42019-01-18 13:55:25 -08001777""".strip())
1778
1779 lines = self.get_op_list(self.output_path)
1780
1781 remove_all_groups = lines.index("remove_all_groups")
1782 add_group = lines.index("add_group group_foo 4294967296")
1783 add_vendor = lines.index("add vendor group_foo")
1784 add_system = lines.index("add system group_foo")
1785 resize_vendor = lines.index("resize vendor 1073741824")
1786 resize_system = lines.index("resize system 3221225472")
1787
1788 self.assertLess(remove_all_groups, add_group,
1789 "Should add groups after removing all groups")
1790 self.assertLess(add_group, min(add_vendor, add_system),
1791 "Should add partitions after adding group")
1792 self.assertLess(add_system, resize_system,
1793 "Should resize system after adding it")
1794 self.assertLess(add_vendor, resize_vendor,
1795 "Should resize vendor after adding it")
1796
1797 def test_inc_groups(self):
1798 source_info = common.LoadDictionaryFromLines("""
1799super_partition_groups=group_foo group_bar group_baz
1800super_group_foo_group_size={group_foo_size}
1801super_group_bar_group_size={group_bar_size}
1802""".format(group_foo_size=4 * GiB, group_bar_size=3 * GiB).split("\n"))
1803 target_info = common.LoadDictionaryFromLines("""
1804super_partition_groups=group_foo group_baz group_qux
1805super_group_foo_group_size={group_foo_size}
1806super_group_baz_group_size={group_baz_size}
1807super_group_qux_group_size={group_qux_size}
1808""".format(group_foo_size=3 * GiB, group_baz_size=4 * GiB,
1809 group_qux_size=1 * GiB).split("\n"))
1810
1811 dp_diff = common.DynamicPartitionsDifference(target_info,
1812 block_diffs=[],
1813 source_info_dict=source_info)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001814 with zipfile.ZipFile(self.output_path, 'w', allowZip64=True) as output_zip:
Yifan Hong45433e42019-01-18 13:55:25 -08001815 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1816
1817 lines = self.get_op_list(self.output_path)
1818
1819 removed = lines.index("remove_group group_bar")
1820 shrunk = lines.index("resize_group group_foo 3221225472")
1821 grown = lines.index("resize_group group_baz 4294967296")
1822 added = lines.index("add_group group_qux 1073741824")
1823
Tao Baof1113e92019-06-18 12:10:14 -07001824 self.assertLess(max(removed, shrunk),
1825 min(grown, added),
Yifan Hong45433e42019-01-18 13:55:25 -08001826 "ops that remove / shrink partitions must precede ops that "
1827 "grow / add partitions")
1828
Yifan Hongbb2658d2019-01-25 12:30:58 -08001829 def test_incremental(self):
Yifan Hong45433e42019-01-18 13:55:25 -08001830 source_info = common.LoadDictionaryFromLines("""
Justin Yun6151e3f2019-06-25 15:58:13 +09001831dynamic_partition_list=system vendor product system_ext
Yifan Hong45433e42019-01-18 13:55:25 -08001832super_partition_groups=group_foo
1833super_group_foo_group_size={group_foo_size}
Justin Yun6151e3f2019-06-25 15:58:13 +09001834super_group_foo_partition_list=system vendor product system_ext
Yifan Hong45433e42019-01-18 13:55:25 -08001835""".format(group_foo_size=4 * GiB).split("\n"))
1836 target_info = common.LoadDictionaryFromLines("""
1837dynamic_partition_list=system vendor product odm
1838super_partition_groups=group_foo group_bar
1839super_group_foo_group_size={group_foo_size}
1840super_group_foo_partition_list=system vendor odm
1841super_group_bar_group_size={group_bar_size}
1842super_group_bar_partition_list=product
1843""".format(group_foo_size=3 * GiB, group_bar_size=1 * GiB).split("\n"))
1844
1845 block_diffs = [MockBlockDifference("system", FakeSparseImage(1536 * MiB),
1846 src=FakeSparseImage(1024 * MiB)),
1847 MockBlockDifference("vendor", FakeSparseImage(512 * MiB),
1848 src=FakeSparseImage(1024 * MiB)),
1849 MockBlockDifference("product", FakeSparseImage(1024 * MiB),
1850 src=FakeSparseImage(1024 * MiB)),
Justin Yun6151e3f2019-06-25 15:58:13 +09001851 MockBlockDifference("system_ext", None,
Yifan Hong45433e42019-01-18 13:55:25 -08001852 src=FakeSparseImage(1024 * MiB)),
1853 MockBlockDifference("odm", FakeSparseImage(1024 * MiB),
1854 src=None)]
1855
1856 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs,
1857 source_info_dict=source_info)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001858 with zipfile.ZipFile(self.output_path, 'w', allowZip64=True) as output_zip:
Yifan Hong45433e42019-01-18 13:55:25 -08001859 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1860
1861 metadata_idx = self.script.lines.index(
1862 'assert(update_dynamic_partitions(package_extract_file('
1863 '"dynamic_partitions_op_list")));')
1864 self.assertLess(self.script.lines.index('patch(vendor);'), metadata_idx)
1865 self.assertLess(metadata_idx, self.script.lines.index('verify(vendor);'))
1866 for p in ("product", "system", "odm"):
1867 patch_idx = self.script.lines.index("patch({});".format(p))
1868 verify_idx = self.script.lines.index("verify({});".format(p))
1869 self.assertLess(metadata_idx, patch_idx,
1870 "Should patch {} after updating metadata".format(p))
1871 self.assertLess(patch_idx, verify_idx,
1872 "Should verify {} after patching".format(p))
1873
Justin Yun6151e3f2019-06-25 15:58:13 +09001874 self.assertNotIn("patch(system_ext);", self.script.lines)
Yifan Hong45433e42019-01-18 13:55:25 -08001875
1876 lines = self.get_op_list(self.output_path)
1877
Justin Yun6151e3f2019-06-25 15:58:13 +09001878 remove = lines.index("remove system_ext")
Yifan Hong45433e42019-01-18 13:55:25 -08001879 move_product_out = lines.index("move product default")
1880 shrink = lines.index("resize vendor 536870912")
1881 shrink_group = lines.index("resize_group group_foo 3221225472")
1882 add_group_bar = lines.index("add_group group_bar 1073741824")
1883 add_odm = lines.index("add odm group_foo")
1884 grow_existing = lines.index("resize system 1610612736")
1885 grow_added = lines.index("resize odm 1073741824")
1886 move_product_in = lines.index("move product group_bar")
1887
1888 max_idx_move_partition_out_foo = max(remove, move_product_out, shrink)
1889 min_idx_move_partition_in_foo = min(add_odm, grow_existing, grow_added)
1890
1891 self.assertLess(max_idx_move_partition_out_foo, shrink_group,
1892 "Must shrink group after partitions inside group are shrunk"
1893 " / removed")
1894
1895 self.assertLess(add_group_bar, move_product_in,
1896 "Must add partitions to group after group is added")
1897
1898 self.assertLess(max_idx_move_partition_out_foo,
1899 min_idx_move_partition_in_foo,
1900 "Must shrink partitions / remove partitions from group"
1901 "before adding / moving partitions into group")
Yifan Hongbb2658d2019-01-25 12:30:58 -08001902
1903 def test_remove_partition(self):
1904 source_info = common.LoadDictionaryFromLines("""
1905blockimgdiff_versions=3,4
1906use_dynamic_partitions=true
1907dynamic_partition_list=foo
1908super_partition_groups=group_foo
1909super_group_foo_group_size={group_foo_size}
1910super_group_foo_partition_list=foo
1911""".format(group_foo_size=4 * GiB).split("\n"))
1912 target_info = common.LoadDictionaryFromLines("""
1913blockimgdiff_versions=3,4
1914use_dynamic_partitions=true
1915super_partition_groups=group_foo
1916super_group_foo_group_size={group_foo_size}
1917""".format(group_foo_size=4 * GiB).split("\n"))
1918
1919 common.OPTIONS.info_dict = target_info
1920 common.OPTIONS.target_info_dict = target_info
1921 common.OPTIONS.source_info_dict = source_info
1922 common.OPTIONS.cache_size = 4 * 4096
1923
1924 block_diffs = [common.BlockDifference("foo", EmptyImage(),
1925 src=DataImage("source", pad=True))]
1926
1927 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs,
1928 source_info_dict=source_info)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001929 with zipfile.ZipFile(self.output_path, 'w', allowZip64=True) as output_zip:
Yifan Hongbb2658d2019-01-25 12:30:58 -08001930 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1931
1932 self.assertNotIn("block_image_update", str(self.script),
Tao Bao2cc0ca12019-03-15 10:44:43 -07001933 "Removed partition should not be patched.")
Yifan Hongbb2658d2019-01-25 12:30:58 -08001934
1935 lines = self.get_op_list(self.output_path)
1936 self.assertEqual(lines, ["remove foo"])
Tianjie Xu0fde41e2020-05-09 05:24:18 +00001937
1938
1939class PartitionBuildPropsTest(test_utils.ReleaseToolsTestCase):
1940 def setUp(self):
Tianjie Xu9afb2212020-05-10 21:48:15 +00001941 self.odm_build_prop = [
Tianjie Xu0fde41e2020-05-09 05:24:18 +00001942 'ro.odm.build.date.utc=1578430045',
1943 'ro.odm.build.fingerprint='
1944 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
1945 'ro.product.odm.device=coral',
1946 'import /odm/etc/build_${ro.boot.product.device_name}.prop',
1947 ]
1948
1949 @staticmethod
1950 def _BuildZipFile(entries):
1951 input_file = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001952 with zipfile.ZipFile(input_file, 'w', allowZip64=True) as input_zip:
Tianjie Xu0fde41e2020-05-09 05:24:18 +00001953 for name, content in entries.items():
1954 input_zip.writestr(name, content)
1955
1956 return input_file
1957
1958 def test_parseBuildProps_noImportStatement(self):
1959 build_prop = [
Tianjie Xu9afb2212020-05-10 21:48:15 +00001960 'ro.odm.build.date.utc=1578430045',
1961 'ro.odm.build.fingerprint='
1962 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
1963 'ro.product.odm.device=coral',
Tianjie Xu0fde41e2020-05-09 05:24:18 +00001964 ]
1965 input_file = self._BuildZipFile({
Tianjie Xu9afb2212020-05-10 21:48:15 +00001966 'ODM/etc/build.prop': '\n'.join(build_prop),
1967 })
1968
Kelvin Zhang928c2342020-09-22 16:15:57 -04001969 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00001970 placeholder_values = {
1971 'ro.boot.product.device_name': ['std', 'pro']
1972 }
1973 partition_props = common.PartitionBuildProps.FromInputFile(
1974 input_zip, 'odm', placeholder_values)
1975
1976 self.assertEqual({
1977 'ro.odm.build.date.utc': '1578430045',
1978 'ro.odm.build.fingerprint':
1979 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
1980 'ro.product.odm.device': 'coral',
1981 }, partition_props.build_props)
1982
1983 self.assertEqual(set(), partition_props.prop_overrides)
1984
1985 def test_parseBuildProps_singleImportStatement(self):
1986 build_std_prop = [
1987 'ro.product.odm.device=coral',
1988 'ro.product.odm.name=product1',
1989 ]
1990 build_pro_prop = [
1991 'ro.product.odm.device=coralpro',
1992 'ro.product.odm.name=product2',
1993 ]
1994
1995 input_file = self._BuildZipFile({
1996 'ODM/etc/build.prop': '\n'.join(self.odm_build_prop),
1997 'ODM/etc/build_std.prop': '\n'.join(build_std_prop),
1998 'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop),
1999 })
2000
Kelvin Zhang928c2342020-09-22 16:15:57 -04002001 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002002 placeholder_values = {
2003 'ro.boot.product.device_name': 'std'
2004 }
2005 partition_props = common.PartitionBuildProps.FromInputFile(
2006 input_zip, 'odm', placeholder_values)
2007
2008 self.assertEqual({
2009 'ro.odm.build.date.utc': '1578430045',
2010 'ro.odm.build.fingerprint':
2011 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2012 'ro.product.odm.device': 'coral',
2013 'ro.product.odm.name': 'product1',
2014 }, partition_props.build_props)
2015
Kelvin Zhang928c2342020-09-22 16:15:57 -04002016 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002017 placeholder_values = {
2018 'ro.boot.product.device_name': 'pro'
2019 }
2020 partition_props = common.PartitionBuildProps.FromInputFile(
2021 input_zip, 'odm', placeholder_values)
2022
2023 self.assertEqual({
2024 'ro.odm.build.date.utc': '1578430045',
2025 'ro.odm.build.fingerprint':
2026 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2027 'ro.product.odm.device': 'coralpro',
2028 'ro.product.odm.name': 'product2',
2029 }, partition_props.build_props)
2030
2031 def test_parseBuildProps_noPlaceHolders(self):
2032 build_prop = copy.copy(self.odm_build_prop)
2033 input_file = self._BuildZipFile({
2034 'ODM/etc/build.prop': '\n'.join(build_prop),
Tianjie Xu0fde41e2020-05-09 05:24:18 +00002035 })
2036
Kelvin Zhang928c2342020-09-22 16:15:57 -04002037 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu0fde41e2020-05-09 05:24:18 +00002038 partition_props = common.PartitionBuildProps.FromInputFile(
2039 input_zip, 'odm')
2040
2041 self.assertEqual({
Tianjie Xu9afb2212020-05-10 21:48:15 +00002042 'ro.odm.build.date.utc': '1578430045',
2043 'ro.odm.build.fingerprint':
2044 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2045 'ro.product.odm.device': 'coral',
Tianjie Xu0fde41e2020-05-09 05:24:18 +00002046 }, partition_props.build_props)
2047
Tianjie Xu9afb2212020-05-10 21:48:15 +00002048 self.assertEqual(set(), partition_props.prop_overrides)
2049
2050 def test_parseBuildProps_multipleImportStatements(self):
2051 build_prop = copy.deepcopy(self.odm_build_prop)
2052 build_prop.append(
2053 'import /odm/etc/build_${ro.boot.product.product_name}.prop')
2054
2055 build_std_prop = [
2056 'ro.product.odm.device=coral',
2057 ]
2058 build_pro_prop = [
2059 'ro.product.odm.device=coralpro',
2060 ]
2061
2062 product1_prop = [
2063 'ro.product.odm.name=product1',
2064 'ro.product.not_care=not_care',
2065 ]
2066
2067 product2_prop = [
2068 'ro.product.odm.name=product2',
2069 'ro.product.not_care=not_care',
2070 ]
2071
2072 input_file = self._BuildZipFile({
2073 'ODM/etc/build.prop': '\n'.join(build_prop),
2074 'ODM/etc/build_std.prop': '\n'.join(build_std_prop),
2075 'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop),
2076 'ODM/etc/build_product1.prop': '\n'.join(product1_prop),
2077 'ODM/etc/build_product2.prop': '\n'.join(product2_prop),
2078 })
2079
Kelvin Zhang928c2342020-09-22 16:15:57 -04002080 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002081 placeholder_values = {
2082 'ro.boot.product.device_name': 'std',
2083 'ro.boot.product.product_name': 'product1',
2084 'ro.boot.product.not_care': 'not_care',
2085 }
2086 partition_props = common.PartitionBuildProps.FromInputFile(
2087 input_zip, 'odm', placeholder_values)
2088
2089 self.assertEqual({
2090 'ro.odm.build.date.utc': '1578430045',
2091 'ro.odm.build.fingerprint':
2092 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2093 'ro.product.odm.device': 'coral',
2094 'ro.product.odm.name': 'product1'
2095 }, partition_props.build_props)
2096
Kelvin Zhang928c2342020-09-22 16:15:57 -04002097 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002098 placeholder_values = {
2099 'ro.boot.product.device_name': 'pro',
2100 'ro.boot.product.product_name': 'product2',
2101 'ro.boot.product.not_care': 'not_care',
2102 }
2103 partition_props = common.PartitionBuildProps.FromInputFile(
2104 input_zip, 'odm', placeholder_values)
2105
2106 self.assertEqual({
2107 'ro.odm.build.date.utc': '1578430045',
2108 'ro.odm.build.fingerprint':
2109 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2110 'ro.product.odm.device': 'coralpro',
2111 'ro.product.odm.name': 'product2'
2112 }, partition_props.build_props)
2113
2114 def test_parseBuildProps_defineAfterOverride(self):
2115 build_prop = copy.deepcopy(self.odm_build_prop)
2116 build_prop.append('ro.product.odm.device=coral')
2117
2118 build_std_prop = [
2119 'ro.product.odm.device=coral',
2120 ]
2121 build_pro_prop = [
2122 'ro.product.odm.device=coralpro',
2123 ]
2124
2125 input_file = self._BuildZipFile({
2126 'ODM/etc/build.prop': '\n'.join(build_prop),
2127 'ODM/etc/build_std.prop': '\n'.join(build_std_prop),
2128 'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop),
2129 })
2130
Kelvin Zhang928c2342020-09-22 16:15:57 -04002131 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002132 placeholder_values = {
2133 'ro.boot.product.device_name': 'std',
2134 }
2135
2136 self.assertRaises(ValueError, common.PartitionBuildProps.FromInputFile,
2137 input_zip, 'odm', placeholder_values)
2138
2139 def test_parseBuildProps_duplicateOverride(self):
2140 build_prop = copy.deepcopy(self.odm_build_prop)
2141 build_prop.append(
2142 'import /odm/etc/build_${ro.boot.product.product_name}.prop')
2143
2144 build_std_prop = [
2145 'ro.product.odm.device=coral',
2146 'ro.product.odm.name=product1',
2147 ]
2148 build_pro_prop = [
2149 'ro.product.odm.device=coralpro',
2150 ]
2151
2152 product1_prop = [
2153 'ro.product.odm.name=product1',
2154 ]
2155
2156 product2_prop = [
2157 'ro.product.odm.name=product2',
2158 ]
2159
2160 input_file = self._BuildZipFile({
2161 'ODM/etc/build.prop': '\n'.join(build_prop),
2162 'ODM/etc/build_std.prop': '\n'.join(build_std_prop),
2163 'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop),
2164 'ODM/etc/build_product1.prop': '\n'.join(product1_prop),
2165 'ODM/etc/build_product2.prop': '\n'.join(product2_prop),
2166 })
2167
Kelvin Zhang928c2342020-09-22 16:15:57 -04002168 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002169 placeholder_values = {
2170 'ro.boot.product.device_name': 'std',
2171 'ro.boot.product.product_name': 'product1',
2172 }
2173 self.assertRaises(ValueError, common.PartitionBuildProps.FromInputFile,
2174 input_zip, 'odm', placeholder_values)