blob: a516366ec126f4b24bd155b346f224bd2fd708d4 [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 Norman21c34f72020-11-11 17:25:50 -0800999 def test_PartitionMapFromTargetFiles(self):
1000 target_files_dir = common.MakeTempDir()
1001 os.makedirs(os.path.join(target_files_dir, 'SYSTEM'))
1002 os.makedirs(os.path.join(target_files_dir, 'SYSTEM', 'vendor'))
1003 os.makedirs(os.path.join(target_files_dir, 'PRODUCT'))
1004 os.makedirs(os.path.join(target_files_dir, 'SYSTEM', 'product'))
1005 os.makedirs(os.path.join(target_files_dir, 'SYSTEM', 'vendor', 'odm'))
1006 os.makedirs(os.path.join(target_files_dir, 'VENDOR_DLKM'))
1007 partition_map = common.PartitionMapFromTargetFiles(target_files_dir)
1008 self.assertDictEqual(
1009 partition_map,
1010 {
1011 'system': 'SYSTEM',
1012 'vendor': 'SYSTEM/vendor',
1013 # Prefer PRODUCT over SYSTEM/product
1014 'product': 'PRODUCT',
1015 'odm': 'SYSTEM/vendor/odm',
1016 'vendor_dlkm': 'VENDOR_DLKM',
1017 # No system_ext or odm_dlkm
1018 })
1019
Daniel Normand3351562020-10-29 12:33:11 -07001020 def test_SharedUidPartitionViolations(self):
1021 uid_dict = {
1022 'android.uid.phone': {
1023 'system': ['system_phone.apk'],
1024 'system_ext': ['system_ext_phone.apk'],
1025 },
1026 'android.uid.wifi': {
1027 'vendor': ['vendor_wifi.apk'],
1028 'odm': ['odm_wifi.apk'],
1029 },
1030 }
1031 errors = common.SharedUidPartitionViolations(
1032 uid_dict, [('system', 'system_ext'), ('vendor', 'odm')])
1033 self.assertEqual(errors, [])
1034
1035 def test_SharedUidPartitionViolations_Violation(self):
1036 uid_dict = {
1037 'android.uid.phone': {
1038 'system': ['system_phone.apk'],
1039 'vendor': ['vendor_phone.apk'],
1040 },
1041 }
1042 errors = common.SharedUidPartitionViolations(
1043 uid_dict, [('system', 'system_ext'), ('vendor', 'odm')])
1044 self.assertIn(
1045 ('APK sharedUserId "android.uid.phone" found across partition groups '
1046 'in partitions "system,vendor"'), errors)
1047
Tao Baob2de7d92019-04-10 10:01:47 -07001048 def test_GetSparseImage_missingImageFile(self):
Tao Baofc7e0e02018-02-13 13:54:02 -08001049 self.assertRaises(
Tao Baob2de7d92019-04-10 10:01:47 -07001050 AssertionError, common.GetSparseImage, 'system2', self.testdata_dir,
1051 None, False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001052 self.assertRaises(
Tao Baob2de7d92019-04-10 10:01:47 -07001053 AssertionError, common.GetSparseImage, 'unknown', self.testdata_dir,
1054 None, False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001055
Tao Bao82490d32019-04-09 00:12:30 -07001056 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001057 def test_GetSparseImage_missingBlockMapFile(self):
1058 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001059 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baofc7e0e02018-02-13 13:54:02 -08001060 target_files_zip.write(
1061 test_utils.construct_sparse_image([
1062 (0xCAC1, 6),
1063 (0xCAC3, 3),
1064 (0xCAC1, 4)]),
1065 arcname='IMAGES/system.img')
1066 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 8))
1067 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1068
Tao Baodba59ee2018-01-09 13:21:02 -08001069 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001070 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baodba59ee2018-01-09 13:21:02 -08001071 self.assertRaises(
1072 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
1073 False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001074
Tao Bao82490d32019-04-09 00:12:30 -07001075 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001076 def test_GetSparseImage_sharedBlocks_notAllowed(self):
1077 """Tests the case of having overlapping blocks but disallowed."""
1078 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001079 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baofc7e0e02018-02-13 13:54:02 -08001080 target_files_zip.write(
1081 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1082 arcname='IMAGES/system.img')
1083 # Block 10 is shared between two files.
1084 target_files_zip.writestr(
1085 'IMAGES/system.map',
1086 '\n'.join([
1087 '/system/file1 1-5 9-10',
1088 '/system/file2 10-12']))
1089 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1090 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1091
Tao Baodba59ee2018-01-09 13:21:02 -08001092 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001093 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baodba59ee2018-01-09 13:21:02 -08001094 self.assertRaises(
1095 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
1096 False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001097
Tao Bao82490d32019-04-09 00:12:30 -07001098 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001099 def test_GetSparseImage_sharedBlocks_allowed(self):
1100 """Tests the case for target using BOARD_EXT4_SHARE_DUP_BLOCKS := true."""
1101 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001102 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baofc7e0e02018-02-13 13:54:02 -08001103 # Construct an image with a care_map of "0-5 9-12".
1104 target_files_zip.write(
1105 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1106 arcname='IMAGES/system.img')
1107 # Block 10 is shared between two files.
1108 target_files_zip.writestr(
1109 'IMAGES/system.map',
1110 '\n'.join([
1111 '/system/file1 1-5 9-10',
1112 '/system/file2 10-12']))
1113 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1114 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1115
Tao Baodba59ee2018-01-09 13:21:02 -08001116 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001117 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baodba59ee2018-01-09 13:21:02 -08001118 sparse_image = common.GetSparseImage('system', tempdir, input_zip, True)
Tao Baofc7e0e02018-02-13 13:54:02 -08001119
1120 self.assertDictEqual(
1121 {
1122 '__COPY': RangeSet("0"),
1123 '__NONZERO-0': RangeSet("6-8 13-15"),
1124 '/system/file1': RangeSet("1-5 9-10"),
1125 '/system/file2': RangeSet("11-12"),
1126 },
1127 sparse_image.file_map)
1128
1129 # '/system/file2' should be marked with 'uses_shared_blocks', but not with
1130 # 'incomplete'.
1131 self.assertTrue(
1132 sparse_image.file_map['/system/file2'].extra['uses_shared_blocks'])
1133 self.assertNotIn(
1134 'incomplete', sparse_image.file_map['/system/file2'].extra)
1135
Tao Baoa264fef2019-10-06 21:55:20 -07001136 # '/system/file1' will only contain one field -- a copy of the input text.
1137 self.assertEqual(1, len(sparse_image.file_map['/system/file1'].extra))
1138
1139 # Meta entries should not have any extra tag.
Tao Baofc7e0e02018-02-13 13:54:02 -08001140 self.assertFalse(sparse_image.file_map['__COPY'].extra)
1141 self.assertFalse(sparse_image.file_map['__NONZERO-0'].extra)
Tao Baofc7e0e02018-02-13 13:54:02 -08001142
Tao Bao82490d32019-04-09 00:12:30 -07001143 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001144 def test_GetSparseImage_incompleteRanges(self):
1145 """Tests the case of ext4 images with holes."""
1146 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001147 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baofc7e0e02018-02-13 13:54:02 -08001148 target_files_zip.write(
1149 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1150 arcname='IMAGES/system.img')
1151 target_files_zip.writestr(
1152 'IMAGES/system.map',
1153 '\n'.join([
1154 '/system/file1 1-5 9-10',
1155 '/system/file2 11-12']))
1156 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1157 # '/system/file2' has less blocks listed (2) than actual (3).
1158 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1159
Tao Baodba59ee2018-01-09 13:21:02 -08001160 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001161 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baodba59ee2018-01-09 13:21:02 -08001162 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001163
Tao Baoa264fef2019-10-06 21:55:20 -07001164 self.assertEqual(
1165 '1-5 9-10',
1166 sparse_image.file_map['/system/file1'].extra['text_str'])
Tao Baofc7e0e02018-02-13 13:54:02 -08001167 self.assertTrue(sparse_image.file_map['/system/file2'].extra['incomplete'])
1168
Tao Bao82490d32019-04-09 00:12:30 -07001169 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -07001170 def test_GetSparseImage_systemRootImage_filenameWithExtraLeadingSlash(self):
1171 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001172 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001173 target_files_zip.write(
1174 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1175 arcname='IMAGES/system.img')
1176 target_files_zip.writestr(
1177 'IMAGES/system.map',
1178 '\n'.join([
1179 '//system/file1 1-5 9-10',
1180 '//system/file2 11-12',
1181 '/system/app/file3 13-15']))
1182 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1183 # '/system/file2' has less blocks listed (2) than actual (3).
1184 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1185 # '/system/app/file3' has less blocks listed (3) than actual (4).
1186 target_files_zip.writestr('SYSTEM/app/file3', os.urandom(4096 * 4))
1187
1188 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001189 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001190 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
1191
Tao Baoa264fef2019-10-06 21:55:20 -07001192 self.assertEqual(
1193 '1-5 9-10',
1194 sparse_image.file_map['//system/file1'].extra['text_str'])
Tao Baod3554e62018-07-10 15:31:22 -07001195 self.assertTrue(sparse_image.file_map['//system/file2'].extra['incomplete'])
1196 self.assertTrue(
1197 sparse_image.file_map['/system/app/file3'].extra['incomplete'])
1198
Tao Bao82490d32019-04-09 00:12:30 -07001199 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -07001200 def test_GetSparseImage_systemRootImage_nonSystemFiles(self):
1201 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001202 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001203 target_files_zip.write(
1204 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1205 arcname='IMAGES/system.img')
1206 target_files_zip.writestr(
1207 'IMAGES/system.map',
1208 '\n'.join([
1209 '//system/file1 1-5 9-10',
1210 '//init.rc 13-15']))
1211 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1212 # '/init.rc' has less blocks listed (3) than actual (4).
1213 target_files_zip.writestr('ROOT/init.rc', os.urandom(4096 * 4))
1214
1215 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001216 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001217 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
1218
Tao Baoa264fef2019-10-06 21:55:20 -07001219 self.assertEqual(
1220 '1-5 9-10',
1221 sparse_image.file_map['//system/file1'].extra['text_str'])
Tao Baod3554e62018-07-10 15:31:22 -07001222 self.assertTrue(sparse_image.file_map['//init.rc'].extra['incomplete'])
1223
Tao Bao82490d32019-04-09 00:12:30 -07001224 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -07001225 def test_GetSparseImage_fileNotFound(self):
1226 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001227 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001228 target_files_zip.write(
1229 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1230 arcname='IMAGES/system.img')
1231 target_files_zip.writestr(
1232 'IMAGES/system.map',
1233 '\n'.join([
1234 '//system/file1 1-5 9-10',
1235 '//system/file2 11-12']))
1236 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1237
1238 tempdir = common.UnzipTemp(target_files)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001239 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as input_zip:
Tao Baod3554e62018-07-10 15:31:22 -07001240 self.assertRaises(
1241 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
1242 False)
1243
Tao Bao82490d32019-04-09 00:12:30 -07001244 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001245 def test_GetAvbChainedPartitionArg(self):
1246 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
1247 info_dict = {
1248 'avb_avbtool': 'avbtool',
1249 'avb_system_key_path': pubkey,
1250 'avb_system_rollback_index_location': 2,
1251 }
1252 args = common.GetAvbChainedPartitionArg('system', info_dict).split(':')
1253 self.assertEqual(3, len(args))
1254 self.assertEqual('system', args[0])
1255 self.assertEqual('2', args[1])
1256 self.assertTrue(os.path.exists(args[2]))
1257
Tao Bao82490d32019-04-09 00:12:30 -07001258 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001259 def test_GetAvbChainedPartitionArg_withPrivateKey(self):
1260 key = os.path.join(self.testdata_dir, 'testkey.key')
1261 info_dict = {
1262 'avb_avbtool': 'avbtool',
1263 'avb_product_key_path': key,
1264 'avb_product_rollback_index_location': 2,
1265 }
1266 args = common.GetAvbChainedPartitionArg('product', info_dict).split(':')
1267 self.assertEqual(3, len(args))
1268 self.assertEqual('product', args[0])
1269 self.assertEqual('2', args[1])
1270 self.assertTrue(os.path.exists(args[2]))
1271
Tao Bao82490d32019-04-09 00:12:30 -07001272 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001273 def test_GetAvbChainedPartitionArg_withSpecifiedKey(self):
1274 info_dict = {
1275 'avb_avbtool': 'avbtool',
1276 'avb_system_key_path': 'does-not-exist',
1277 'avb_system_rollback_index_location': 2,
1278 }
1279 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
1280 args = common.GetAvbChainedPartitionArg(
1281 'system', info_dict, pubkey).split(':')
1282 self.assertEqual(3, len(args))
1283 self.assertEqual('system', args[0])
1284 self.assertEqual('2', args[1])
1285 self.assertTrue(os.path.exists(args[2]))
1286
Tao Bao82490d32019-04-09 00:12:30 -07001287 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001288 def test_GetAvbChainedPartitionArg_invalidKey(self):
1289 pubkey = os.path.join(self.testdata_dir, 'testkey_with_passwd.x509.pem')
1290 info_dict = {
1291 'avb_avbtool': 'avbtool',
1292 'avb_system_key_path': pubkey,
1293 'avb_system_rollback_index_location': 2,
1294 }
1295 self.assertRaises(
Tao Bao986ee862018-10-04 15:46:16 -07001296 common.ExternalError, common.GetAvbChainedPartitionArg, 'system',
1297 info_dict)
Tao Bao02a08592018-07-22 12:40:45 -07001298
Tao Baoa57ab9f2018-08-24 12:08:38 -07001299 INFO_DICT_DEFAULT = {
1300 'recovery_api_version': 3,
1301 'fstab_version': 2,
1302 'system_root_image': 'true',
1303 'no_recovery' : 'true',
1304 'recovery_as_boot': 'true',
1305 }
1306
Daniel Norman4cc9df62019-07-18 10:11:07 -07001307 def test_LoadListFromFile(self):
1308 file_path = os.path.join(self.testdata_dir,
1309 'merge_config_framework_item_list')
1310 contents = common.LoadListFromFile(file_path)
1311 expected_contents = [
1312 'META/apkcerts.txt',
1313 'META/filesystem_config.txt',
1314 'META/root_filesystem_config.txt',
1315 'META/system_manifest.xml',
1316 'META/system_matrix.xml',
1317 'META/update_engine_config.txt',
1318 'PRODUCT/*',
1319 'ROOT/*',
1320 'SYSTEM/*',
1321 ]
1322 self.assertEqual(sorted(contents), sorted(expected_contents))
1323
Tao Baoa57ab9f2018-08-24 12:08:38 -07001324 @staticmethod
1325 def _test_LoadInfoDict_createTargetFiles(info_dict, fstab_path):
1326 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001327 with zipfile.ZipFile(target_files, 'w', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001328 info_values = ''.join(
Tao Baoda30cfa2017-12-01 16:19:46 -08001329 ['{}={}\n'.format(k, v) for k, v in sorted(info_dict.items())])
Tao Baoa57ab9f2018-08-24 12:08:38 -07001330 common.ZipWriteStr(target_files_zip, 'META/misc_info.txt', info_values)
1331
1332 FSTAB_TEMPLATE = "/dev/block/system {} ext4 ro,barrier=1 defaults"
1333 if info_dict.get('system_root_image') == 'true':
1334 fstab_values = FSTAB_TEMPLATE.format('/')
1335 else:
1336 fstab_values = FSTAB_TEMPLATE.format('/system')
1337 common.ZipWriteStr(target_files_zip, fstab_path, fstab_values)
Tao Bao410ad8b2018-08-24 12:08:38 -07001338
1339 common.ZipWriteStr(
1340 target_files_zip, 'META/file_contexts', 'file-contexts')
Tao Baoa57ab9f2018-08-24 12:08:38 -07001341 return target_files
1342
1343 def test_LoadInfoDict(self):
1344 target_files = self._test_LoadInfoDict_createTargetFiles(
1345 self.INFO_DICT_DEFAULT,
1346 'BOOT/RAMDISK/system/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001347 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001348 loaded_dict = common.LoadInfoDict(target_files_zip)
1349 self.assertEqual(3, loaded_dict['recovery_api_version'])
1350 self.assertEqual(2, loaded_dict['fstab_version'])
1351 self.assertIn('/', loaded_dict['fstab'])
1352 self.assertIn('/system', loaded_dict['fstab'])
1353
1354 def test_LoadInfoDict_legacyRecoveryFstabPath(self):
1355 target_files = self._test_LoadInfoDict_createTargetFiles(
1356 self.INFO_DICT_DEFAULT,
1357 'BOOT/RAMDISK/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001358 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001359 loaded_dict = common.LoadInfoDict(target_files_zip)
1360 self.assertEqual(3, loaded_dict['recovery_api_version'])
1361 self.assertEqual(2, loaded_dict['fstab_version'])
1362 self.assertIn('/', loaded_dict['fstab'])
1363 self.assertIn('/system', loaded_dict['fstab'])
1364
Tao Bao82490d32019-04-09 00:12:30 -07001365 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoa57ab9f2018-08-24 12:08:38 -07001366 def test_LoadInfoDict_dirInput(self):
1367 target_files = self._test_LoadInfoDict_createTargetFiles(
1368 self.INFO_DICT_DEFAULT,
1369 'BOOT/RAMDISK/system/etc/recovery.fstab')
1370 unzipped = common.UnzipTemp(target_files)
1371 loaded_dict = common.LoadInfoDict(unzipped)
1372 self.assertEqual(3, loaded_dict['recovery_api_version'])
1373 self.assertEqual(2, loaded_dict['fstab_version'])
1374 self.assertIn('/', loaded_dict['fstab'])
1375 self.assertIn('/system', loaded_dict['fstab'])
1376
Tao Bao82490d32019-04-09 00:12:30 -07001377 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoa57ab9f2018-08-24 12:08:38 -07001378 def test_LoadInfoDict_dirInput_legacyRecoveryFstabPath(self):
1379 target_files = self._test_LoadInfoDict_createTargetFiles(
1380 self.INFO_DICT_DEFAULT,
1381 'BOOT/RAMDISK/system/etc/recovery.fstab')
1382 unzipped = common.UnzipTemp(target_files)
1383 loaded_dict = common.LoadInfoDict(unzipped)
1384 self.assertEqual(3, loaded_dict['recovery_api_version'])
1385 self.assertEqual(2, loaded_dict['fstab_version'])
1386 self.assertIn('/', loaded_dict['fstab'])
1387 self.assertIn('/system', loaded_dict['fstab'])
1388
1389 def test_LoadInfoDict_systemRootImageFalse(self):
1390 # Devices not using system-as-root nor recovery-as-boot. Non-A/B devices
1391 # launched prior to P will likely have this config.
1392 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
1393 del info_dict['no_recovery']
1394 del info_dict['system_root_image']
1395 del info_dict['recovery_as_boot']
1396 target_files = self._test_LoadInfoDict_createTargetFiles(
1397 info_dict,
1398 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001399 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001400 loaded_dict = common.LoadInfoDict(target_files_zip)
1401 self.assertEqual(3, loaded_dict['recovery_api_version'])
1402 self.assertEqual(2, loaded_dict['fstab_version'])
1403 self.assertNotIn('/', loaded_dict['fstab'])
1404 self.assertIn('/system', loaded_dict['fstab'])
1405
1406 def test_LoadInfoDict_recoveryAsBootFalse(self):
1407 # Devices using system-as-root, but with standalone recovery image. Non-A/B
1408 # devices launched since P will likely have this config.
1409 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
1410 del info_dict['no_recovery']
1411 del info_dict['recovery_as_boot']
1412 target_files = self._test_LoadInfoDict_createTargetFiles(
1413 info_dict,
1414 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001415 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001416 loaded_dict = common.LoadInfoDict(target_files_zip)
1417 self.assertEqual(3, loaded_dict['recovery_api_version'])
1418 self.assertEqual(2, loaded_dict['fstab_version'])
1419 self.assertIn('/', loaded_dict['fstab'])
1420 self.assertIn('/system', loaded_dict['fstab'])
1421
1422 def test_LoadInfoDict_noRecoveryTrue(self):
1423 # Device doesn't have a recovery partition at all.
1424 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
1425 del info_dict['recovery_as_boot']
1426 target_files = self._test_LoadInfoDict_createTargetFiles(
1427 info_dict,
1428 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001429 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Baoa57ab9f2018-08-24 12:08:38 -07001430 loaded_dict = common.LoadInfoDict(target_files_zip)
1431 self.assertEqual(3, loaded_dict['recovery_api_version'])
1432 self.assertEqual(2, loaded_dict['fstab_version'])
1433 self.assertIsNone(loaded_dict['fstab'])
1434
Tao Bao82490d32019-04-09 00:12:30 -07001435 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao410ad8b2018-08-24 12:08:38 -07001436 def test_LoadInfoDict_missingMetaMiscInfoTxt(self):
1437 target_files = self._test_LoadInfoDict_createTargetFiles(
1438 self.INFO_DICT_DEFAULT,
1439 'BOOT/RAMDISK/system/etc/recovery.fstab')
1440 common.ZipDelete(target_files, 'META/misc_info.txt')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001441 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Bao410ad8b2018-08-24 12:08:38 -07001442 self.assertRaises(ValueError, common.LoadInfoDict, target_files_zip)
1443
Tao Bao82490d32019-04-09 00:12:30 -07001444 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao410ad8b2018-08-24 12:08:38 -07001445 def test_LoadInfoDict_repacking(self):
1446 target_files = self._test_LoadInfoDict_createTargetFiles(
1447 self.INFO_DICT_DEFAULT,
1448 'BOOT/RAMDISK/system/etc/recovery.fstab')
1449 unzipped = common.UnzipTemp(target_files)
1450 loaded_dict = common.LoadInfoDict(unzipped, True)
1451 self.assertEqual(3, loaded_dict['recovery_api_version'])
1452 self.assertEqual(2, loaded_dict['fstab_version'])
1453 self.assertIn('/', loaded_dict['fstab'])
1454 self.assertIn('/system', loaded_dict['fstab'])
1455 self.assertEqual(
1456 os.path.join(unzipped, 'ROOT'), loaded_dict['root_dir'])
1457 self.assertEqual(
1458 os.path.join(unzipped, 'META', 'root_filesystem_config.txt'),
1459 loaded_dict['root_fs_config'])
1460
1461 def test_LoadInfoDict_repackingWithZipFileInput(self):
1462 target_files = self._test_LoadInfoDict_createTargetFiles(
1463 self.INFO_DICT_DEFAULT,
1464 'BOOT/RAMDISK/system/etc/recovery.fstab')
Kelvin Zhang928c2342020-09-22 16:15:57 -04001465 with zipfile.ZipFile(target_files, 'r', allowZip64=True) as target_files_zip:
Tao Bao410ad8b2018-08-24 12:08:38 -07001466 self.assertRaises(
1467 AssertionError, common.LoadInfoDict, target_files_zip, True)
1468
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001469 def test_MergeDynamicPartitionInfoDicts_ReturnsMergedDict(self):
1470 framework_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001471 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001472 'super_partition_groups': 'group_a',
1473 'dynamic_partition_list': 'system',
Daniel Norman55417142019-11-25 16:04:36 -08001474 'super_group_a_partition_list': 'system',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001475 }
1476 vendor_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001477 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001478 'super_partition_groups': 'group_a group_b',
1479 'dynamic_partition_list': 'vendor product',
Daniel Normanb0c75912020-09-24 14:30:21 -07001480 'super_block_devices': 'super',
1481 'super_super_device_size': '3000',
Daniel Norman55417142019-11-25 16:04:36 -08001482 'super_group_a_partition_list': 'vendor',
1483 'super_group_a_group_size': '1000',
1484 'super_group_b_partition_list': 'product',
1485 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001486 }
1487 merged_dict = common.MergeDynamicPartitionInfoDicts(
1488 framework_dict=framework_dict,
Daniel Norman55417142019-11-25 16:04:36 -08001489 vendor_dict=vendor_dict)
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001490 expected_merged_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',
Daniel Normanb0c75912020-09-24 14:30:21 -07001493 'dynamic_partition_list': 'product system vendor',
1494 'super_block_devices': 'super',
1495 'super_super_device_size': '3000',
Daniel Norman55417142019-11-25 16:04:36 -08001496 'super_group_a_partition_list': 'system vendor',
1497 'super_group_a_group_size': '1000',
1498 'super_group_b_partition_list': 'product',
1499 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001500 }
1501 self.assertEqual(merged_dict, expected_merged_dict)
1502
1503 def test_MergeDynamicPartitionInfoDicts_IgnoringFrameworkGroupSize(self):
1504 framework_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001505 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001506 'super_partition_groups': 'group_a',
1507 'dynamic_partition_list': 'system',
Daniel Norman55417142019-11-25 16:04:36 -08001508 'super_group_a_partition_list': 'system',
1509 'super_group_a_group_size': '5000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001510 }
1511 vendor_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001512 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001513 'super_partition_groups': 'group_a group_b',
1514 'dynamic_partition_list': 'vendor product',
Daniel Norman55417142019-11-25 16:04:36 -08001515 'super_group_a_partition_list': 'vendor',
1516 'super_group_a_group_size': '1000',
1517 'super_group_b_partition_list': 'product',
1518 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001519 }
1520 merged_dict = common.MergeDynamicPartitionInfoDicts(
1521 framework_dict=framework_dict,
Daniel Norman55417142019-11-25 16:04:36 -08001522 vendor_dict=vendor_dict)
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001523 expected_merged_dict = {
Daniel Normanb0c75912020-09-24 14:30:21 -07001524 'use_dynamic_partitions': 'true',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001525 'super_partition_groups': 'group_a group_b',
Daniel Normanb0c75912020-09-24 14:30:21 -07001526 'dynamic_partition_list': 'product system vendor',
Daniel Norman55417142019-11-25 16:04:36 -08001527 'super_group_a_partition_list': 'system vendor',
1528 'super_group_a_group_size': '1000',
1529 'super_group_b_partition_list': 'product',
1530 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001531 }
1532 self.assertEqual(merged_dict, expected_merged_dict)
1533
Daniel Norman276f0622019-07-26 14:13:51 -07001534 def test_GetAvbPartitionArg(self):
1535 info_dict = {}
1536 cmd = common.GetAvbPartitionArg('system', '/path/to/system.img', info_dict)
1537 self.assertEqual(
1538 ['--include_descriptors_from_image', '/path/to/system.img'], cmd)
1539
1540 @test_utils.SkipIfExternalToolsUnavailable()
1541 def test_AppendVBMetaArgsForPartition_vendorAsChainedPartition(self):
1542 testdata_dir = test_utils.get_testdata_dir()
1543 pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
1544 info_dict = {
1545 'avb_avbtool': 'avbtool',
1546 'avb_vendor_key_path': pubkey,
1547 'avb_vendor_rollback_index_location': 5,
1548 }
1549 cmd = common.GetAvbPartitionArg('vendor', '/path/to/vendor.img', info_dict)
1550 self.assertEqual(2, len(cmd))
1551 self.assertEqual('--chain_partition', cmd[0])
1552 chained_partition_args = cmd[1].split(':')
1553 self.assertEqual(3, len(chained_partition_args))
1554 self.assertEqual('vendor', chained_partition_args[0])
1555 self.assertEqual('5', chained_partition_args[1])
1556 self.assertTrue(os.path.exists(chained_partition_args[2]))
1557
Tao Bao3612c882019-10-14 17:49:31 -07001558 @test_utils.SkipIfExternalToolsUnavailable()
1559 def test_AppendVBMetaArgsForPartition_recoveryAsChainedPartition_nonAb(self):
1560 testdata_dir = test_utils.get_testdata_dir()
1561 pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
1562 info_dict = {
1563 'avb_avbtool': 'avbtool',
1564 'avb_recovery_key_path': pubkey,
1565 'avb_recovery_rollback_index_location': 3,
1566 }
1567 cmd = common.GetAvbPartitionArg(
1568 'recovery', '/path/to/recovery.img', info_dict)
1569 self.assertFalse(cmd)
1570
1571 @test_utils.SkipIfExternalToolsUnavailable()
1572 def test_AppendVBMetaArgsForPartition_recoveryAsChainedPartition_ab(self):
1573 testdata_dir = test_utils.get_testdata_dir()
1574 pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
1575 info_dict = {
1576 'ab_update': 'true',
1577 'avb_avbtool': 'avbtool',
1578 'avb_recovery_key_path': pubkey,
1579 'avb_recovery_rollback_index_location': 3,
1580 }
1581 cmd = common.GetAvbPartitionArg(
1582 'recovery', '/path/to/recovery.img', info_dict)
1583 self.assertEqual(2, len(cmd))
1584 self.assertEqual('--chain_partition', cmd[0])
1585 chained_partition_args = cmd[1].split(':')
1586 self.assertEqual(3, len(chained_partition_args))
1587 self.assertEqual('recovery', chained_partition_args[0])
1588 self.assertEqual('3', chained_partition_args[1])
1589 self.assertTrue(os.path.exists(chained_partition_args[2]))
1590
Tianjie20dd8f22020-04-19 15:51:16 -07001591 def test_BuildVBMeta_appendAftlCommandSyntax(self):
1592 testdata_dir = test_utils.get_testdata_dir()
1593 common.OPTIONS.info_dict = {
1594 'ab_update': 'true',
1595 'avb_avbtool': 'avbtool',
Tianjie Xu0fde41e2020-05-09 05:24:18 +00001596 'build.prop': common.PartitionBuildProps.FromDictionary(
1597 'system', {
1598 'ro.build.version.incremental': '6285659',
1599 'ro.product.device': 'coral',
1600 'ro.build.fingerprint':
1601 'google/coral/coral:R/RP1A.200311.002/'
1602 '6285659:userdebug/dev-keys'}
1603 ),
Tianjie20dd8f22020-04-19 15:51:16 -07001604 }
1605 common.OPTIONS.aftl_tool_path = 'aftltool'
1606 common.OPTIONS.aftl_server = 'log.endpoints.aftl-dev.cloud.goog:9000'
1607 common.OPTIONS.aftl_key_path = os.path.join(testdata_dir,
1608 'test_transparency_key.pub')
1609 common.OPTIONS.aftl_manufacturer_key_path = os.path.join(
1610 testdata_dir, 'test_aftl_rsa4096.pem')
1611
1612 vbmeta_image = tempfile.NamedTemporaryFile(delete=False)
1613 cmd = common.ConstructAftlMakeImageCommands(vbmeta_image.name)
1614 expected_cmd = [
1615 'aftltool', 'make_icp_from_vbmeta',
1616 '--vbmeta_image_path', 'place_holder',
1617 '--output', vbmeta_image.name,
1618 '--version_incremental', '6285659',
1619 '--transparency_log_servers',
1620 'log.endpoints.aftl-dev.cloud.goog:9000,{}'.format(
1621 common.OPTIONS.aftl_key_path),
1622 '--manufacturer_key', common.OPTIONS.aftl_manufacturer_key_path,
1623 '--algorithm', 'SHA256_RSA4096',
1624 '--padding', '4096']
1625
1626 # ignore the place holder, i.e. path to a temp file
1627 self.assertEqual(cmd[:3], expected_cmd[:3])
1628 self.assertEqual(cmd[4:], expected_cmd[4:])
1629
1630 @unittest.skip("enable after we have a server for public")
1631 def test_BuildVBMeta_appendAftlContactServer(self):
Tianjie Xueaed60c2020-03-12 00:33:28 -07001632 testdata_dir = test_utils.get_testdata_dir()
1633 common.OPTIONS.info_dict = {
1634 'ab_update': 'true',
1635 'avb_avbtool': 'avbtool',
Tianjie Xu0fde41e2020-05-09 05:24:18 +00001636 'build.prop': common.PartitionBuildProps.FromDictionary(
1637 'system', {
1638 'ro.build.version.incremental': '6285659',
1639 'ro.product.device': 'coral',
1640 'ro.build.fingerprint':
1641 'google/coral/coral:R/RP1A.200311.002/'
1642 '6285659:userdebug/dev-keys'}
1643 )
Tianjie Xueaed60c2020-03-12 00:33:28 -07001644 }
Tianjie0f307452020-04-01 12:20:21 -07001645 common.OPTIONS.aftl_tool_path = "aftltool"
Tianjie Xueaed60c2020-03-12 00:33:28 -07001646 common.OPTIONS.aftl_server = "log.endpoints.aftl-dev.cloud.goog:9000"
1647 common.OPTIONS.aftl_key_path = os.path.join(testdata_dir,
1648 'test_transparency_key.pub')
1649 common.OPTIONS.aftl_manufacturer_key_path = os.path.join(
1650 testdata_dir, 'test_aftl_rsa4096.pem')
1651
1652 input_dir = common.MakeTempDir()
1653 system_image = common.MakeTempFile()
1654 build_image_cmd = ['mkuserimg_mke2fs', input_dir, system_image, 'ext4',
1655 '/system', str(4096 * 100), '-j', '0', '-s']
1656 common.RunAndCheckOutput(build_image_cmd)
1657
1658 add_footer_cmd = ['avbtool', 'add_hashtree_footer',
1659 '--partition_size', str(4096 * 150),
1660 '--partition_name', 'system',
1661 '--image', system_image]
1662 common.RunAndCheckOutput(add_footer_cmd)
1663
1664 vbmeta_image = common.MakeTempFile()
1665 common.BuildVBMeta(vbmeta_image, {'system': system_image}, 'vbmeta',
1666 ['system'])
1667
1668 verify_cmd = ['aftltool', 'verify_image_icp', '--vbmeta_image_path',
1669 vbmeta_image, '--transparency_log_pub_keys',
1670 common.OPTIONS.aftl_key_path]
1671 common.RunAndCheckOutput(verify_cmd)
1672
Bowgo Tsai27c39b02021-03-12 21:40:32 +08001673 @test_utils.SkipIfExternalToolsUnavailable()
1674 def test_AppendGkiSigningArgs_NoSigningKeyPath(self):
1675 # A non-GKI boot.img has no gki_signing_key_path.
1676 common.OPTIONS.info_dict = {
1677 # 'gki_signing_key_path': pubkey,
1678 'gki_signing_algorithm': 'SHA256_RSA4096',
1679 'gki_signing_signature_args': '--prop foo:bar',
1680 }
1681
1682 # Tests no --gki_signing_* args are appended if there is no
1683 # gki_signing_key_path.
1684 cmd = ['mkbootimg', '--header_version', '4']
1685 expected_cmd = ['mkbootimg', '--header_version', '4']
1686 common.AppendGkiSigningArgs(cmd)
1687 self.assertEqual(cmd, expected_cmd)
1688
1689 def test_AppendGkiSigningArgs_NoSigningAlgorithm(self):
1690 pubkey = os.path.join(self.testdata_dir, 'testkey_gki.pem')
1691 with open(pubkey, 'wb') as f:
1692 f.write(b'\x00' * 100)
1693 self.assertTrue(os.path.exists(pubkey))
1694
1695 # Tests no --gki_signing_* args are appended if there is no
1696 # gki_signing_algorithm.
1697 common.OPTIONS.info_dict = {
1698 'gki_signing_key_path': pubkey,
1699 # 'gki_signing_algorithm': 'SHA256_RSA4096',
1700 'gki_signing_signature_args': '--prop foo:bar',
1701 }
1702
1703 cmd = ['mkbootimg', '--header_version', '4']
1704 expected_cmd = ['mkbootimg', '--header_version', '4']
1705 common.AppendGkiSigningArgs(cmd)
1706 self.assertEqual(cmd, expected_cmd)
1707
1708 @test_utils.SkipIfExternalToolsUnavailable()
1709 def test_AppendGkiSigningArgs(self):
1710 pubkey = os.path.join(self.testdata_dir, 'testkey_gki.pem')
1711 with open(pubkey, 'wb') as f:
1712 f.write(b'\x00' * 100)
1713 self.assertTrue(os.path.exists(pubkey))
1714
1715 common.OPTIONS.info_dict = {
1716 'gki_signing_key_path': pubkey,
1717 'gki_signing_algorithm': 'SHA256_RSA4096',
1718 'gki_signing_signature_args': '--prop foo:bar',
1719 }
1720 cmd = ['mkbootimg', '--header_version', '4']
1721 common.AppendGkiSigningArgs(cmd)
1722
1723 expected_cmd = [
1724 'mkbootimg', '--header_version', '4',
1725 '--gki_signing_key', pubkey,
1726 '--gki_signing_algorithm', 'SHA256_RSA4096',
1727 '--gki_signing_signature_args', '--prop foo:bar'
1728 ]
1729 self.assertEqual(cmd, expected_cmd)
1730
1731 @test_utils.SkipIfExternalToolsUnavailable()
1732 def test_AppendGkiSigningArgs_KeyPathNotFound(self):
1733 pubkey = os.path.join(self.testdata_dir, 'no_testkey_gki.pem')
1734 self.assertFalse(os.path.exists(pubkey))
1735
1736 common.OPTIONS.info_dict = {
1737 'gki_signing_key_path': pubkey,
1738 'gki_signing_algorithm': 'SHA256_RSA4096',
1739 'gki_signing_signature_args': '--prop foo:bar',
1740 }
1741 cmd = ['mkbootimg', '--header_version', '4']
1742 self.assertRaises(common.ExternalError, common.AppendGkiSigningArgs, cmd)
1743
1744 @test_utils.SkipIfExternalToolsUnavailable()
1745 def test_AppendGkiSigningArgs_SearchKeyPath(self):
1746 pubkey = 'testkey_gki.pem'
1747 self.assertFalse(os.path.exists(pubkey))
1748
1749 # Tests it should replace the pubkey with an existed key under
1750 # OPTIONS.search_path, i.e., os.path.join(OPTIONS.search_path, pubkey).
1751 search_path_dir = common.MakeTempDir()
1752 search_pubkey = os.path.join(search_path_dir, pubkey)
1753 with open(search_pubkey, 'wb') as f:
1754 f.write(b'\x00' * 100)
1755 self.assertTrue(os.path.exists(search_pubkey))
1756
1757 common.OPTIONS.search_path = search_path_dir
1758 common.OPTIONS.info_dict = {
1759 'gki_signing_key_path': pubkey,
1760 'gki_signing_algorithm': 'SHA256_RSA4096',
1761 'gki_signing_signature_args': '--prop foo:bar',
1762 }
1763 cmd = ['mkbootimg', '--header_version', '4']
1764 common.AppendGkiSigningArgs(cmd)
1765
1766 expected_cmd = [
1767 'mkbootimg', '--header_version', '4',
1768 '--gki_signing_key', search_pubkey,
1769 '--gki_signing_algorithm', 'SHA256_RSA4096',
1770 '--gki_signing_signature_args', '--prop foo:bar'
1771 ]
1772 self.assertEqual(cmd, expected_cmd)
1773
1774 @test_utils.SkipIfExternalToolsUnavailable()
1775 def test_AppendGkiSigningArgs_SearchKeyPathNotFound(self):
1776 pubkey = 'no_testkey_gki.pem'
1777 self.assertFalse(os.path.exists(pubkey))
1778
1779 # Tests it should raise ExternalError if no key found under
1780 # OPTIONS.search_path.
1781 search_path_dir = common.MakeTempDir()
1782 search_pubkey = os.path.join(search_path_dir, pubkey)
1783 self.assertFalse(os.path.exists(search_pubkey))
1784
1785 common.OPTIONS.search_path = search_path_dir
1786 common.OPTIONS.info_dict = {
1787 'gki_signing_key_path': pubkey,
1788 'gki_signing_algorithm': 'SHA256_RSA4096',
1789 'gki_signing_signature_args': '--prop foo:bar',
1790 }
1791 cmd = ['mkbootimg', '--header_version', '4']
1792 self.assertRaises(common.ExternalError, common.AppendGkiSigningArgs, cmd)
1793
Tao Baofc7e0e02018-02-13 13:54:02 -08001794
Tao Bao65b94e92018-10-11 21:57:26 -07001795class InstallRecoveryScriptFormatTest(test_utils.ReleaseToolsTestCase):
Tao Bao1c830bf2017-12-25 10:43:47 -08001796 """Checks the format of install-recovery.sh.
Tianjie Xu9c384d22017-06-20 17:00:55 -07001797
Tao Bao1c830bf2017-12-25 10:43:47 -08001798 Its format should match between common.py and validate_target_files.py.
1799 """
Tianjie Xu9c384d22017-06-20 17:00:55 -07001800
1801 def setUp(self):
Tao Bao1c830bf2017-12-25 10:43:47 -08001802 self._tempdir = common.MakeTempDir()
Kelvin Zhangc693d952020-07-22 19:21:22 -04001803 # Create a fake dict that contains the fstab info for boot&recovery.
Tianjie Xu9c384d22017-06-20 17:00:55 -07001804 self._info = {"fstab" : {}}
Kelvin Zhangc693d952020-07-22 19:21:22 -04001805 fake_fstab = [
Tao Bao1c830bf2017-12-25 10:43:47 -08001806 "/dev/soc.0/by-name/boot /boot emmc defaults defaults",
1807 "/dev/soc.0/by-name/recovery /recovery emmc defaults defaults"]
Kelvin Zhangc693d952020-07-22 19:21:22 -04001808 self._info["fstab"] = common.LoadRecoveryFSTab("\n".join, 2, fake_fstab)
Tianjie Xudf055582017-11-07 12:22:58 -08001809 # Construct the gzipped recovery.img and boot.img
1810 self.recovery_data = bytearray([
1811 0x1f, 0x8b, 0x08, 0x00, 0x81, 0x11, 0x02, 0x5a, 0x00, 0x03, 0x2b, 0x4a,
1812 0x4d, 0xce, 0x2f, 0x4b, 0x2d, 0xaa, 0x04, 0x00, 0xc9, 0x93, 0x43, 0xf3,
1813 0x08, 0x00, 0x00, 0x00
1814 ])
1815 # echo -n "boot" | gzip -f | hd
1816 self.boot_data = bytearray([
1817 0x1f, 0x8b, 0x08, 0x00, 0x8c, 0x12, 0x02, 0x5a, 0x00, 0x03, 0x4b, 0xca,
1818 0xcf, 0x2f, 0x01, 0x00, 0xc4, 0xae, 0xed, 0x46, 0x04, 0x00, 0x00, 0x00
1819 ])
Tianjie Xu9c384d22017-06-20 17:00:55 -07001820
1821 def _out_tmp_sink(self, name, data, prefix="SYSTEM"):
1822 loc = os.path.join(self._tempdir, prefix, name)
1823 if not os.path.exists(os.path.dirname(loc)):
1824 os.makedirs(os.path.dirname(loc))
Tao Baoda30cfa2017-12-01 16:19:46 -08001825 with open(loc, "wb") as f:
Tianjie Xu9c384d22017-06-20 17:00:55 -07001826 f.write(data)
1827
1828 def test_full_recovery(self):
Tao Bao31b08072017-11-08 15:50:59 -08001829 recovery_image = common.File("recovery.img", self.recovery_data)
1830 boot_image = common.File("boot.img", self.boot_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001831 self._info["full_recovery_image"] = "true"
1832
1833 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1834 recovery_image, boot_image, self._info)
1835 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1836 self._info)
1837
Tao Bao82490d32019-04-09 00:12:30 -07001838 @test_utils.SkipIfExternalToolsUnavailable()
Tianjie Xu9c384d22017-06-20 17:00:55 -07001839 def test_recovery_from_boot(self):
Tao Bao31b08072017-11-08 15:50:59 -08001840 recovery_image = common.File("recovery.img", self.recovery_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001841 self._out_tmp_sink("recovery.img", recovery_image.data, "IMAGES")
Tao Bao31b08072017-11-08 15:50:59 -08001842 boot_image = common.File("boot.img", self.boot_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001843 self._out_tmp_sink("boot.img", boot_image.data, "IMAGES")
1844
1845 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1846 recovery_image, boot_image, self._info)
1847 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1848 self._info)
1849 # Validate 'recovery-from-boot' with bonus argument.
Tao Baoda30cfa2017-12-01 16:19:46 -08001850 self._out_tmp_sink("etc/recovery-resource.dat", b"bonus", "SYSTEM")
Tianjie Xu9c384d22017-06-20 17:00:55 -07001851 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1852 recovery_image, boot_image, self._info)
1853 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1854 self._info)
Yifan Hong45433e42019-01-18 13:55:25 -08001855
1856
Yifan Hong45433e42019-01-18 13:55:25 -08001857class MockBlockDifference(object):
Tao Baoda30cfa2017-12-01 16:19:46 -08001858
Yifan Hong45433e42019-01-18 13:55:25 -08001859 def __init__(self, partition, tgt, src=None):
1860 self.partition = partition
1861 self.tgt = tgt
1862 self.src = src
Tao Baoda30cfa2017-12-01 16:19:46 -08001863
Yifan Hong45433e42019-01-18 13:55:25 -08001864 def WriteScript(self, script, _, progress=None,
1865 write_verify_script=False):
1866 if progress:
1867 script.AppendExtra("progress({})".format(progress))
1868 script.AppendExtra("patch({});".format(self.partition))
1869 if write_verify_script:
1870 self.WritePostInstallVerifyScript(script)
Tao Baoda30cfa2017-12-01 16:19:46 -08001871
Yifan Hong45433e42019-01-18 13:55:25 -08001872 def WritePostInstallVerifyScript(self, script):
1873 script.AppendExtra("verify({});".format(self.partition))
1874
1875
1876class FakeSparseImage(object):
Tao Baoda30cfa2017-12-01 16:19:46 -08001877
Yifan Hong45433e42019-01-18 13:55:25 -08001878 def __init__(self, size):
1879 self.blocksize = 4096
1880 self.total_blocks = size // 4096
1881 assert size % 4096 == 0, "{} is not a multiple of 4096".format(size)
1882
1883
1884class DynamicPartitionsDifferenceTest(test_utils.ReleaseToolsTestCase):
Tao Baoda30cfa2017-12-01 16:19:46 -08001885
Yifan Hong45433e42019-01-18 13:55:25 -08001886 @staticmethod
1887 def get_op_list(output_path):
Kelvin Zhang928c2342020-09-22 16:15:57 -04001888 with zipfile.ZipFile(output_path, allowZip64=True) as output_zip:
Tao Baoda30cfa2017-12-01 16:19:46 -08001889 with output_zip.open('dynamic_partitions_op_list') as op_list:
1890 return [line.decode().strip() for line in op_list.readlines()
1891 if not line.startswith(b'#')]
Yifan Hong45433e42019-01-18 13:55:25 -08001892
1893 def setUp(self):
Tao Baoe1148042019-10-07 20:00:34 -07001894 self.script = test_utils.MockScriptWriter()
Yifan Hong45433e42019-01-18 13:55:25 -08001895 self.output_path = common.MakeTempFile(suffix='.zip')
1896
1897 def test_full(self):
1898 target_info = common.LoadDictionaryFromLines("""
1899dynamic_partition_list=system vendor
1900super_partition_groups=group_foo
1901super_group_foo_group_size={group_size}
1902super_group_foo_partition_list=system vendor
1903""".format(group_size=4 * GiB).split("\n"))
1904 block_diffs = [MockBlockDifference("system", FakeSparseImage(3 * GiB)),
1905 MockBlockDifference("vendor", FakeSparseImage(1 * GiB))]
1906
1907 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001908 with zipfile.ZipFile(self.output_path, 'w', allowZip64=True) as output_zip:
Yifan Hong45433e42019-01-18 13:55:25 -08001909 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1910
1911 self.assertEqual(str(self.script).strip(), """
1912assert(update_dynamic_partitions(package_extract_file("dynamic_partitions_op_list")));
Yifan Hong45433e42019-01-18 13:55:25 -08001913patch(system);
1914verify(system);
1915unmap_partition("system");
Tao Baof1113e92019-06-18 12:10:14 -07001916patch(vendor);
1917verify(vendor);
1918unmap_partition("vendor");
Yifan Hong45433e42019-01-18 13:55:25 -08001919""".strip())
1920
1921 lines = self.get_op_list(self.output_path)
1922
1923 remove_all_groups = lines.index("remove_all_groups")
1924 add_group = lines.index("add_group group_foo 4294967296")
1925 add_vendor = lines.index("add vendor group_foo")
1926 add_system = lines.index("add system group_foo")
1927 resize_vendor = lines.index("resize vendor 1073741824")
1928 resize_system = lines.index("resize system 3221225472")
1929
1930 self.assertLess(remove_all_groups, add_group,
1931 "Should add groups after removing all groups")
1932 self.assertLess(add_group, min(add_vendor, add_system),
1933 "Should add partitions after adding group")
1934 self.assertLess(add_system, resize_system,
1935 "Should resize system after adding it")
1936 self.assertLess(add_vendor, resize_vendor,
1937 "Should resize vendor after adding it")
1938
1939 def test_inc_groups(self):
1940 source_info = common.LoadDictionaryFromLines("""
1941super_partition_groups=group_foo group_bar group_baz
1942super_group_foo_group_size={group_foo_size}
1943super_group_bar_group_size={group_bar_size}
1944""".format(group_foo_size=4 * GiB, group_bar_size=3 * GiB).split("\n"))
1945 target_info = common.LoadDictionaryFromLines("""
1946super_partition_groups=group_foo group_baz group_qux
1947super_group_foo_group_size={group_foo_size}
1948super_group_baz_group_size={group_baz_size}
1949super_group_qux_group_size={group_qux_size}
1950""".format(group_foo_size=3 * GiB, group_baz_size=4 * GiB,
1951 group_qux_size=1 * GiB).split("\n"))
1952
1953 dp_diff = common.DynamicPartitionsDifference(target_info,
1954 block_diffs=[],
1955 source_info_dict=source_info)
Kelvin Zhang928c2342020-09-22 16:15:57 -04001956 with zipfile.ZipFile(self.output_path, 'w', allowZip64=True) as output_zip:
Yifan Hong45433e42019-01-18 13:55:25 -08001957 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1958
1959 lines = self.get_op_list(self.output_path)
1960
1961 removed = lines.index("remove_group group_bar")
1962 shrunk = lines.index("resize_group group_foo 3221225472")
1963 grown = lines.index("resize_group group_baz 4294967296")
1964 added = lines.index("add_group group_qux 1073741824")
1965
Tao Baof1113e92019-06-18 12:10:14 -07001966 self.assertLess(max(removed, shrunk),
1967 min(grown, added),
Yifan Hong45433e42019-01-18 13:55:25 -08001968 "ops that remove / shrink partitions must precede ops that "
1969 "grow / add partitions")
1970
Yifan Hongbb2658d2019-01-25 12:30:58 -08001971 def test_incremental(self):
Yifan Hong45433e42019-01-18 13:55:25 -08001972 source_info = common.LoadDictionaryFromLines("""
Justin Yun6151e3f2019-06-25 15:58:13 +09001973dynamic_partition_list=system vendor product system_ext
Yifan Hong45433e42019-01-18 13:55:25 -08001974super_partition_groups=group_foo
1975super_group_foo_group_size={group_foo_size}
Justin Yun6151e3f2019-06-25 15:58:13 +09001976super_group_foo_partition_list=system vendor product system_ext
Yifan Hong45433e42019-01-18 13:55:25 -08001977""".format(group_foo_size=4 * GiB).split("\n"))
1978 target_info = common.LoadDictionaryFromLines("""
1979dynamic_partition_list=system vendor product odm
1980super_partition_groups=group_foo group_bar
1981super_group_foo_group_size={group_foo_size}
1982super_group_foo_partition_list=system vendor odm
1983super_group_bar_group_size={group_bar_size}
1984super_group_bar_partition_list=product
1985""".format(group_foo_size=3 * GiB, group_bar_size=1 * GiB).split("\n"))
1986
1987 block_diffs = [MockBlockDifference("system", FakeSparseImage(1536 * MiB),
1988 src=FakeSparseImage(1024 * MiB)),
1989 MockBlockDifference("vendor", FakeSparseImage(512 * MiB),
1990 src=FakeSparseImage(1024 * MiB)),
1991 MockBlockDifference("product", FakeSparseImage(1024 * MiB),
1992 src=FakeSparseImage(1024 * MiB)),
Justin Yun6151e3f2019-06-25 15:58:13 +09001993 MockBlockDifference("system_ext", None,
Yifan Hong45433e42019-01-18 13:55:25 -08001994 src=FakeSparseImage(1024 * MiB)),
1995 MockBlockDifference("odm", FakeSparseImage(1024 * MiB),
1996 src=None)]
1997
1998 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs,
1999 source_info_dict=source_info)
Kelvin Zhang928c2342020-09-22 16:15:57 -04002000 with zipfile.ZipFile(self.output_path, 'w', allowZip64=True) as output_zip:
Yifan Hong45433e42019-01-18 13:55:25 -08002001 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
2002
2003 metadata_idx = self.script.lines.index(
2004 'assert(update_dynamic_partitions(package_extract_file('
2005 '"dynamic_partitions_op_list")));')
2006 self.assertLess(self.script.lines.index('patch(vendor);'), metadata_idx)
2007 self.assertLess(metadata_idx, self.script.lines.index('verify(vendor);'))
2008 for p in ("product", "system", "odm"):
2009 patch_idx = self.script.lines.index("patch({});".format(p))
2010 verify_idx = self.script.lines.index("verify({});".format(p))
2011 self.assertLess(metadata_idx, patch_idx,
2012 "Should patch {} after updating metadata".format(p))
2013 self.assertLess(patch_idx, verify_idx,
2014 "Should verify {} after patching".format(p))
2015
Justin Yun6151e3f2019-06-25 15:58:13 +09002016 self.assertNotIn("patch(system_ext);", self.script.lines)
Yifan Hong45433e42019-01-18 13:55:25 -08002017
2018 lines = self.get_op_list(self.output_path)
2019
Justin Yun6151e3f2019-06-25 15:58:13 +09002020 remove = lines.index("remove system_ext")
Yifan Hong45433e42019-01-18 13:55:25 -08002021 move_product_out = lines.index("move product default")
2022 shrink = lines.index("resize vendor 536870912")
2023 shrink_group = lines.index("resize_group group_foo 3221225472")
2024 add_group_bar = lines.index("add_group group_bar 1073741824")
2025 add_odm = lines.index("add odm group_foo")
2026 grow_existing = lines.index("resize system 1610612736")
2027 grow_added = lines.index("resize odm 1073741824")
2028 move_product_in = lines.index("move product group_bar")
2029
2030 max_idx_move_partition_out_foo = max(remove, move_product_out, shrink)
2031 min_idx_move_partition_in_foo = min(add_odm, grow_existing, grow_added)
2032
2033 self.assertLess(max_idx_move_partition_out_foo, shrink_group,
2034 "Must shrink group after partitions inside group are shrunk"
2035 " / removed")
2036
2037 self.assertLess(add_group_bar, move_product_in,
2038 "Must add partitions to group after group is added")
2039
2040 self.assertLess(max_idx_move_partition_out_foo,
2041 min_idx_move_partition_in_foo,
2042 "Must shrink partitions / remove partitions from group"
2043 "before adding / moving partitions into group")
Yifan Hongbb2658d2019-01-25 12:30:58 -08002044
2045 def test_remove_partition(self):
2046 source_info = common.LoadDictionaryFromLines("""
2047blockimgdiff_versions=3,4
2048use_dynamic_partitions=true
2049dynamic_partition_list=foo
2050super_partition_groups=group_foo
2051super_group_foo_group_size={group_foo_size}
2052super_group_foo_partition_list=foo
2053""".format(group_foo_size=4 * GiB).split("\n"))
2054 target_info = common.LoadDictionaryFromLines("""
2055blockimgdiff_versions=3,4
2056use_dynamic_partitions=true
2057super_partition_groups=group_foo
2058super_group_foo_group_size={group_foo_size}
2059""".format(group_foo_size=4 * GiB).split("\n"))
2060
2061 common.OPTIONS.info_dict = target_info
2062 common.OPTIONS.target_info_dict = target_info
2063 common.OPTIONS.source_info_dict = source_info
2064 common.OPTIONS.cache_size = 4 * 4096
2065
2066 block_diffs = [common.BlockDifference("foo", EmptyImage(),
2067 src=DataImage("source", pad=True))]
2068
2069 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs,
2070 source_info_dict=source_info)
Kelvin Zhang928c2342020-09-22 16:15:57 -04002071 with zipfile.ZipFile(self.output_path, 'w', allowZip64=True) as output_zip:
Yifan Hongbb2658d2019-01-25 12:30:58 -08002072 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
2073
2074 self.assertNotIn("block_image_update", str(self.script),
Tao Bao2cc0ca12019-03-15 10:44:43 -07002075 "Removed partition should not be patched.")
Yifan Hongbb2658d2019-01-25 12:30:58 -08002076
2077 lines = self.get_op_list(self.output_path)
2078 self.assertEqual(lines, ["remove foo"])
Tianjie Xu0fde41e2020-05-09 05:24:18 +00002079
2080
2081class PartitionBuildPropsTest(test_utils.ReleaseToolsTestCase):
2082 def setUp(self):
Tianjie Xu9afb2212020-05-10 21:48:15 +00002083 self.odm_build_prop = [
Tianjie Xu0fde41e2020-05-09 05:24:18 +00002084 'ro.odm.build.date.utc=1578430045',
2085 'ro.odm.build.fingerprint='
2086 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2087 'ro.product.odm.device=coral',
2088 'import /odm/etc/build_${ro.boot.product.device_name}.prop',
2089 ]
2090
2091 @staticmethod
2092 def _BuildZipFile(entries):
2093 input_file = common.MakeTempFile(prefix='target_files-', suffix='.zip')
Kelvin Zhang928c2342020-09-22 16:15:57 -04002094 with zipfile.ZipFile(input_file, 'w', allowZip64=True) as input_zip:
Tianjie Xu0fde41e2020-05-09 05:24:18 +00002095 for name, content in entries.items():
2096 input_zip.writestr(name, content)
2097
2098 return input_file
2099
2100 def test_parseBuildProps_noImportStatement(self):
2101 build_prop = [
Tianjie Xu9afb2212020-05-10 21:48:15 +00002102 'ro.odm.build.date.utc=1578430045',
2103 'ro.odm.build.fingerprint='
2104 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2105 'ro.product.odm.device=coral',
Tianjie Xu0fde41e2020-05-09 05:24:18 +00002106 ]
2107 input_file = self._BuildZipFile({
Tianjie Xu9afb2212020-05-10 21:48:15 +00002108 'ODM/etc/build.prop': '\n'.join(build_prop),
2109 })
2110
Kelvin Zhang928c2342020-09-22 16:15:57 -04002111 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002112 placeholder_values = {
2113 'ro.boot.product.device_name': ['std', 'pro']
2114 }
2115 partition_props = common.PartitionBuildProps.FromInputFile(
2116 input_zip, 'odm', placeholder_values)
2117
2118 self.assertEqual({
2119 'ro.odm.build.date.utc': '1578430045',
2120 'ro.odm.build.fingerprint':
2121 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2122 'ro.product.odm.device': 'coral',
2123 }, partition_props.build_props)
2124
2125 self.assertEqual(set(), partition_props.prop_overrides)
2126
2127 def test_parseBuildProps_singleImportStatement(self):
2128 build_std_prop = [
2129 'ro.product.odm.device=coral',
2130 'ro.product.odm.name=product1',
2131 ]
2132 build_pro_prop = [
2133 'ro.product.odm.device=coralpro',
2134 'ro.product.odm.name=product2',
2135 ]
2136
2137 input_file = self._BuildZipFile({
2138 'ODM/etc/build.prop': '\n'.join(self.odm_build_prop),
2139 'ODM/etc/build_std.prop': '\n'.join(build_std_prop),
2140 'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop),
2141 })
2142
Kelvin Zhang928c2342020-09-22 16:15:57 -04002143 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002144 placeholder_values = {
2145 'ro.boot.product.device_name': 'std'
2146 }
2147 partition_props = common.PartitionBuildProps.FromInputFile(
2148 input_zip, 'odm', placeholder_values)
2149
2150 self.assertEqual({
2151 'ro.odm.build.date.utc': '1578430045',
2152 'ro.odm.build.fingerprint':
2153 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2154 'ro.product.odm.device': 'coral',
2155 'ro.product.odm.name': 'product1',
2156 }, partition_props.build_props)
2157
Kelvin Zhang928c2342020-09-22 16:15:57 -04002158 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002159 placeholder_values = {
2160 'ro.boot.product.device_name': 'pro'
2161 }
2162 partition_props = common.PartitionBuildProps.FromInputFile(
2163 input_zip, 'odm', placeholder_values)
2164
2165 self.assertEqual({
2166 'ro.odm.build.date.utc': '1578430045',
2167 'ro.odm.build.fingerprint':
2168 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2169 'ro.product.odm.device': 'coralpro',
2170 'ro.product.odm.name': 'product2',
2171 }, partition_props.build_props)
2172
2173 def test_parseBuildProps_noPlaceHolders(self):
2174 build_prop = copy.copy(self.odm_build_prop)
2175 input_file = self._BuildZipFile({
2176 'ODM/etc/build.prop': '\n'.join(build_prop),
Tianjie Xu0fde41e2020-05-09 05:24:18 +00002177 })
2178
Kelvin Zhang928c2342020-09-22 16:15:57 -04002179 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu0fde41e2020-05-09 05:24:18 +00002180 partition_props = common.PartitionBuildProps.FromInputFile(
2181 input_zip, 'odm')
2182
2183 self.assertEqual({
Tianjie Xu9afb2212020-05-10 21:48:15 +00002184 'ro.odm.build.date.utc': '1578430045',
2185 'ro.odm.build.fingerprint':
2186 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2187 'ro.product.odm.device': 'coral',
Tianjie Xu0fde41e2020-05-09 05:24:18 +00002188 }, partition_props.build_props)
2189
Tianjie Xu9afb2212020-05-10 21:48:15 +00002190 self.assertEqual(set(), partition_props.prop_overrides)
2191
2192 def test_parseBuildProps_multipleImportStatements(self):
2193 build_prop = copy.deepcopy(self.odm_build_prop)
2194 build_prop.append(
2195 'import /odm/etc/build_${ro.boot.product.product_name}.prop')
2196
2197 build_std_prop = [
2198 'ro.product.odm.device=coral',
2199 ]
2200 build_pro_prop = [
2201 'ro.product.odm.device=coralpro',
2202 ]
2203
2204 product1_prop = [
2205 'ro.product.odm.name=product1',
2206 'ro.product.not_care=not_care',
2207 ]
2208
2209 product2_prop = [
2210 'ro.product.odm.name=product2',
2211 'ro.product.not_care=not_care',
2212 ]
2213
2214 input_file = self._BuildZipFile({
2215 'ODM/etc/build.prop': '\n'.join(build_prop),
2216 'ODM/etc/build_std.prop': '\n'.join(build_std_prop),
2217 'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop),
2218 'ODM/etc/build_product1.prop': '\n'.join(product1_prop),
2219 'ODM/etc/build_product2.prop': '\n'.join(product2_prop),
2220 })
2221
Kelvin Zhang928c2342020-09-22 16:15:57 -04002222 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002223 placeholder_values = {
2224 'ro.boot.product.device_name': 'std',
2225 'ro.boot.product.product_name': 'product1',
2226 'ro.boot.product.not_care': 'not_care',
2227 }
2228 partition_props = common.PartitionBuildProps.FromInputFile(
2229 input_zip, 'odm', placeholder_values)
2230
2231 self.assertEqual({
2232 'ro.odm.build.date.utc': '1578430045',
2233 'ro.odm.build.fingerprint':
2234 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2235 'ro.product.odm.device': 'coral',
2236 'ro.product.odm.name': 'product1'
2237 }, partition_props.build_props)
2238
Kelvin Zhang928c2342020-09-22 16:15:57 -04002239 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002240 placeholder_values = {
2241 'ro.boot.product.device_name': 'pro',
2242 'ro.boot.product.product_name': 'product2',
2243 'ro.boot.product.not_care': 'not_care',
2244 }
2245 partition_props = common.PartitionBuildProps.FromInputFile(
2246 input_zip, 'odm', placeholder_values)
2247
2248 self.assertEqual({
2249 'ro.odm.build.date.utc': '1578430045',
2250 'ro.odm.build.fingerprint':
2251 'google/coral/coral:10/RP1A.200325.001/6337676:user/dev-keys',
2252 'ro.product.odm.device': 'coralpro',
2253 'ro.product.odm.name': 'product2'
2254 }, partition_props.build_props)
2255
2256 def test_parseBuildProps_defineAfterOverride(self):
2257 build_prop = copy.deepcopy(self.odm_build_prop)
2258 build_prop.append('ro.product.odm.device=coral')
2259
2260 build_std_prop = [
2261 'ro.product.odm.device=coral',
2262 ]
2263 build_pro_prop = [
2264 'ro.product.odm.device=coralpro',
2265 ]
2266
2267 input_file = self._BuildZipFile({
2268 'ODM/etc/build.prop': '\n'.join(build_prop),
2269 'ODM/etc/build_std.prop': '\n'.join(build_std_prop),
2270 'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop),
2271 })
2272
Kelvin Zhang928c2342020-09-22 16:15:57 -04002273 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002274 placeholder_values = {
2275 'ro.boot.product.device_name': 'std',
2276 }
2277
2278 self.assertRaises(ValueError, common.PartitionBuildProps.FromInputFile,
2279 input_zip, 'odm', placeholder_values)
2280
2281 def test_parseBuildProps_duplicateOverride(self):
2282 build_prop = copy.deepcopy(self.odm_build_prop)
2283 build_prop.append(
2284 'import /odm/etc/build_${ro.boot.product.product_name}.prop')
2285
2286 build_std_prop = [
2287 'ro.product.odm.device=coral',
2288 'ro.product.odm.name=product1',
2289 ]
2290 build_pro_prop = [
2291 'ro.product.odm.device=coralpro',
2292 ]
2293
2294 product1_prop = [
2295 'ro.product.odm.name=product1',
2296 ]
2297
2298 product2_prop = [
2299 'ro.product.odm.name=product2',
2300 ]
2301
2302 input_file = self._BuildZipFile({
2303 'ODM/etc/build.prop': '\n'.join(build_prop),
2304 'ODM/etc/build_std.prop': '\n'.join(build_std_prop),
2305 'ODM/etc/build_pro.prop': '\n'.join(build_pro_prop),
2306 'ODM/etc/build_product1.prop': '\n'.join(product1_prop),
2307 'ODM/etc/build_product2.prop': '\n'.join(product2_prop),
2308 })
2309
Kelvin Zhang928c2342020-09-22 16:15:57 -04002310 with zipfile.ZipFile(input_file, 'r', allowZip64=True) as input_zip:
Tianjie Xu9afb2212020-05-10 21:48:15 +00002311 placeholder_values = {
2312 'ro.boot.product.device_name': 'std',
2313 'ro.boot.product.product_name': 'product1',
2314 }
2315 self.assertRaises(ValueError, common.PartitionBuildProps.FromInputFile,
2316 input_zip, 'odm', placeholder_values)