blob: 665eb513d62772bf126c32558938649e4cfc6105 [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
Dan Albert8e0178d2015-01-27 15:53:15 -080018import os
Tao Bao17e4e612018-02-16 17:12:54 -080019import subprocess
Dan Albert8e0178d2015-01-27 15:53:15 -080020import tempfile
21import time
Tianjie20dd8f22020-04-19 15:51:16 -070022import unittest
Dan Albert8e0178d2015-01-27 15:53:15 -080023import zipfile
Tao Bao31b08072017-11-08 15:50:59 -080024from hashlib import sha1
25
Dan Albert8e0178d2015-01-27 15:53:15 -080026import common
Tao Bao04e1f012018-02-04 12:13:35 -080027import test_utils
Tianjie Xu9c384d22017-06-20 17:00:55 -070028import validate_target_files
Tianjie Xu41976c72019-07-03 13:57:01 -070029from images import EmptyImage, DataImage
Tao Baofc7e0e02018-02-13 13:54:02 -080030from rangelib import RangeSet
Dan Albert8e0178d2015-01-27 15:53:15 -080031
Tao Bao04e1f012018-02-04 12:13:35 -080032
Tao Bao31b08072017-11-08 15:50:59 -080033KiB = 1024
34MiB = 1024 * KiB
35GiB = 1024 * MiB
Dan Albert8e0178d2015-01-27 15:53:15 -080036
Tao Bao1c830bf2017-12-25 10:43:47 -080037
Tao Baof3282b42015-04-01 11:21:55 -070038def get_2gb_string():
Tao Bao31b08072017-11-08 15:50:59 -080039 size = int(2 * GiB + 1)
40 block_size = 4 * KiB
41 step_size = 4 * MiB
42 # Generate a long string with holes, e.g. 'xyz\x00abc\x00...'.
43 for _ in range(0, size, step_size):
44 yield os.urandom(block_size)
Tao Baoc1a1ec32019-06-18 16:29:37 -070045 yield b'\0' * (step_size - block_size)
Tao Baof3282b42015-04-01 11:21:55 -070046
Dan Albert8e0178d2015-01-27 15:53:15 -080047
Tao Bao1c320f82019-10-04 23:25:12 -070048class BuildInfoTest(test_utils.ReleaseToolsTestCase):
49
50 TEST_INFO_DICT = {
Greg Kaiser60225452020-05-09 00:30:33 +000051 'build.prop' : {
52 'ro.product.device' : 'product-device',
53 'ro.product.name' : 'product-name',
54 'ro.build.fingerprint' : 'build-fingerprint',
55 'ro.build.foo' : 'build-foo',
56 },
57 'system.build.prop' : {
58 'ro.product.system.brand' : 'product-brand',
59 'ro.product.system.name' : 'product-name',
60 'ro.product.system.device' : 'product-device',
61 'ro.system.build.version.release' : 'version-release',
62 'ro.system.build.id' : 'build-id',
63 'ro.system.build.version.incremental' : 'version-incremental',
64 'ro.system.build.type' : 'build-type',
65 'ro.system.build.tags' : 'build-tags',
66 'ro.system.build.foo' : 'build-foo',
67 },
68 'vendor.build.prop' : {
69 'ro.product.vendor.brand' : 'vendor-product-brand',
70 'ro.product.vendor.name' : 'vendor-product-name',
71 'ro.product.vendor.device' : 'vendor-product-device',
72 'ro.vendor.build.version.release' : 'vendor-version-release',
73 'ro.vendor.build.id' : 'vendor-build-id',
74 'ro.vendor.build.version.incremental' : 'vendor-version-incremental',
75 'ro.vendor.build.type' : 'vendor-build-type',
76 'ro.vendor.build.tags' : 'vendor-build-tags',
77 },
78 'property1' : 'value1',
79 'property2' : 4096,
Tao Bao1c320f82019-10-04 23:25:12 -070080 }
81
82 TEST_INFO_DICT_USES_OEM_PROPS = {
Greg Kaiser60225452020-05-09 00:30:33 +000083 'build.prop' : {
84 'ro.product.name' : 'product-name',
85 'ro.build.thumbprint' : 'build-thumbprint',
86 'ro.build.bar' : 'build-bar',
87 },
88 'vendor.build.prop' : {
89 'ro.vendor.build.fingerprint' : 'vendor-build-fingerprint',
90 },
91 'property1' : 'value1',
92 'property2' : 4096,
93 'oem_fingerprint_properties' : 'ro.product.device ro.product.brand',
Tao Bao1c320f82019-10-04 23:25:12 -070094 }
95
96 TEST_OEM_DICTS = [
97 {
Greg Kaiser60225452020-05-09 00:30:33 +000098 'ro.product.brand' : 'brand1',
99 'ro.product.device' : 'device1',
Tao Bao1c320f82019-10-04 23:25:12 -0700100 },
101 {
Greg Kaiser60225452020-05-09 00:30:33 +0000102 'ro.product.brand' : 'brand2',
103 'ro.product.device' : 'device2',
Tao Bao1c320f82019-10-04 23:25:12 -0700104 },
105 {
Greg Kaiser60225452020-05-09 00:30:33 +0000106 'ro.product.brand' : 'brand3',
107 'ro.product.device' : 'device3',
Tao Bao1c320f82019-10-04 23:25:12 -0700108 },
109 ]
110
Steven Laver8e2086e2020-04-27 16:26:31 -0700111 TEST_INFO_DICT_PROPERTY_SOURCE_ORDER = {
Greg Kaiser60225452020-05-09 00:30:33 +0000112 'build.prop' : {
113 'ro.build.fingerprint' : 'build-fingerprint',
114 'ro.product.property_source_order' :
115 'product,odm,vendor,system_ext,system',
116 },
117 'system.build.prop' : {
118 'ro.product.system.device' : 'system-product-device',
119 },
120 'vendor.build.prop' : {
121 'ro.product.vendor.device' : 'vendor-product-device',
122 },
Steven Laver8e2086e2020-04-27 16:26:31 -0700123 }
124
125 TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_10 = {
Greg Kaiser60225452020-05-09 00:30:33 +0000126 'build.prop' : {
127 'ro.build.fingerprint' : 'build-fingerprint',
128 'ro.product.property_source_order' :
129 'product,product_services,odm,vendor,system',
130 'ro.build.version.release' : '10',
131 'ro.build.version.codename' : 'REL',
132 },
133 'system.build.prop' : {
134 'ro.product.system.device' : 'system-product-device',
135 },
136 'vendor.build.prop' : {
137 'ro.product.vendor.device' : 'vendor-product-device',
138 },
Steven Laver8e2086e2020-04-27 16:26:31 -0700139 }
140
141 TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_9 = {
Greg Kaiser60225452020-05-09 00:30:33 +0000142 'build.prop' : {
143 'ro.product.device' : 'product-device',
144 'ro.build.fingerprint' : 'build-fingerprint',
145 'ro.build.version.release' : '9',
146 'ro.build.version.codename' : 'REL',
147 },
148 'system.build.prop' : {
149 'ro.product.system.device' : 'system-product-device',
150 },
151 'vendor.build.prop' : {
152 'ro.product.vendor.device' : 'vendor-product-device',
153 },
Steven Laver8e2086e2020-04-27 16:26:31 -0700154 }
155
Tao Bao1c320f82019-10-04 23:25:12 -0700156 def test_init(self):
157 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
158 self.assertEqual('product-device', target_info.device)
159 self.assertEqual('build-fingerprint', target_info.fingerprint)
160 self.assertFalse(target_info.is_ab)
161 self.assertIsNone(target_info.oem_props)
162
163 def test_init_with_oem_props(self):
164 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
165 self.TEST_OEM_DICTS)
166 self.assertEqual('device1', target_info.device)
167 self.assertEqual('brand1/product-name/device1:build-thumbprint',
168 target_info.fingerprint)
169
170 # Swap the order in oem_dicts, which would lead to different BuildInfo.
171 oem_dicts = copy.copy(self.TEST_OEM_DICTS)
172 oem_dicts[0], oem_dicts[2] = oem_dicts[2], oem_dicts[0]
173 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
174 oem_dicts)
175 self.assertEqual('device3', target_info.device)
176 self.assertEqual('brand3/product-name/device3:build-thumbprint',
177 target_info.fingerprint)
178
Tao Bao1c320f82019-10-04 23:25:12 -0700179 def test_init_badFingerprint(self):
180 info_dict = copy.deepcopy(self.TEST_INFO_DICT)
Greg Kaiser60225452020-05-09 00:30:33 +0000181 info_dict['build.prop']['ro.build.fingerprint'] = 'bad fingerprint'
Tao Bao1c320f82019-10-04 23:25:12 -0700182 self.assertRaises(ValueError, common.BuildInfo, info_dict, None)
183
Greg Kaiser60225452020-05-09 00:30:33 +0000184 info_dict['build.prop']['ro.build.fingerprint'] = 'bad\x80fingerprint'
Tao Bao1c320f82019-10-04 23:25:12 -0700185 self.assertRaises(ValueError, common.BuildInfo, info_dict, None)
186
187 def test___getitem__(self):
188 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
189 self.assertEqual('value1', target_info['property1'])
190 self.assertEqual(4096, target_info['property2'])
Greg Kaiser60225452020-05-09 00:30:33 +0000191 self.assertEqual('build-foo', target_info['build.prop']['ro.build.foo'])
Tao Bao1c320f82019-10-04 23:25:12 -0700192
193 def test___getitem__with_oem_props(self):
194 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
195 self.TEST_OEM_DICTS)
196 self.assertEqual('value1', target_info['property1'])
197 self.assertEqual(4096, target_info['property2'])
Greg Kaiser60225452020-05-09 00:30:33 +0000198 self.assertRaises(KeyError,
199 lambda: target_info['build.prop']['ro.build.foo'])
Tao Bao1c320f82019-10-04 23:25:12 -0700200
201 def test___setitem__(self):
202 target_info = common.BuildInfo(copy.deepcopy(self.TEST_INFO_DICT), None)
203 self.assertEqual('value1', target_info['property1'])
204 target_info['property1'] = 'value2'
205 self.assertEqual('value2', target_info['property1'])
206
Greg Kaiser60225452020-05-09 00:30:33 +0000207 self.assertEqual('build-foo', target_info['build.prop']['ro.build.foo'])
208 target_info['build.prop']['ro.build.foo'] = 'build-bar'
209 self.assertEqual('build-bar', target_info['build.prop']['ro.build.foo'])
Tao Bao1c320f82019-10-04 23:25:12 -0700210
211 def test_get(self):
212 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
213 self.assertEqual('value1', target_info.get('property1'))
214 self.assertEqual(4096, target_info.get('property2'))
215 self.assertEqual(4096, target_info.get('property2', 1024))
216 self.assertEqual(1024, target_info.get('property-nonexistent', 1024))
Greg Kaiser60225452020-05-09 00:30:33 +0000217 self.assertEqual('build-foo', target_info.get('build.prop')['ro.build.foo'])
Tao Bao1c320f82019-10-04 23:25:12 -0700218
219 def test_get_with_oem_props(self):
220 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
221 self.TEST_OEM_DICTS)
222 self.assertEqual('value1', target_info.get('property1'))
223 self.assertEqual(4096, target_info.get('property2'))
224 self.assertEqual(4096, target_info.get('property2', 1024))
225 self.assertEqual(1024, target_info.get('property-nonexistent', 1024))
Greg Kaiser60225452020-05-09 00:30:33 +0000226 self.assertIsNone(target_info.get('build.prop').get('ro.build.foo'))
227 self.assertRaises(KeyError,
228 lambda: target_info.get('build.prop')['ro.build.foo'])
Tao Bao1c320f82019-10-04 23:25:12 -0700229
230 def test_items(self):
231 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
232 items = target_info.items()
233 self.assertIn(('property1', 'value1'), items)
234 self.assertIn(('property2', 4096), items)
235
236 def test_GetBuildProp(self):
237 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
238 self.assertEqual('build-foo', target_info.GetBuildProp('ro.build.foo'))
239 self.assertRaises(common.ExternalError, target_info.GetBuildProp,
240 'ro.build.nonexistent')
241
242 def test_GetBuildProp_with_oem_props(self):
243 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
244 self.TEST_OEM_DICTS)
245 self.assertEqual('build-bar', target_info.GetBuildProp('ro.build.bar'))
246 self.assertRaises(common.ExternalError, target_info.GetBuildProp,
247 'ro.build.nonexistent')
248
Daniel Normand5fe8622020-01-08 17:01:11 -0800249 def test_GetPartitionFingerprint(self):
Tao Bao1c320f82019-10-04 23:25:12 -0700250 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
Daniel Normand5fe8622020-01-08 17:01:11 -0800251 self.assertEqual(
252 target_info.GetPartitionFingerprint('vendor'),
253 'vendor-product-brand/vendor-product-name/vendor-product-device'
254 ':vendor-version-release/vendor-build-id/vendor-version-incremental'
255 ':vendor-build-type/vendor-build-tags')
Tao Bao1c320f82019-10-04 23:25:12 -0700256
Daniel Normand5fe8622020-01-08 17:01:11 -0800257 def test_GetPartitionFingerprint_system_other_uses_system(self):
Tao Bao1c320f82019-10-04 23:25:12 -0700258 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
Daniel Normand5fe8622020-01-08 17:01:11 -0800259 self.assertEqual(
260 target_info.GetPartitionFingerprint('system_other'),
261 target_info.GetPartitionFingerprint('system'))
Tao Bao1c320f82019-10-04 23:25:12 -0700262
Daniel Normand5fe8622020-01-08 17:01:11 -0800263 def test_GetPartitionFingerprint_uses_fingerprint_prop_if_available(self):
264 info_dict = copy.deepcopy(self.TEST_INFO_DICT)
Greg Kaiser60225452020-05-09 00:30:33 +0000265 info_dict['vendor.build.prop']['ro.vendor.build.fingerprint'] = 'vendor:fingerprint'
Daniel Normand5fe8622020-01-08 17:01:11 -0800266 target_info = common.BuildInfo(info_dict, None)
267 self.assertEqual(
268 target_info.GetPartitionFingerprint('vendor'),
269 'vendor:fingerprint')
Tao Bao1c320f82019-10-04 23:25:12 -0700270
271 def test_WriteMountOemScript(self):
272 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
273 self.TEST_OEM_DICTS)
274 script_writer = test_utils.MockScriptWriter()
275 target_info.WriteMountOemScript(script_writer)
276 self.assertEqual([('Mount', '/oem', None)], script_writer.lines)
277
278 def test_WriteDeviceAssertions(self):
279 target_info = common.BuildInfo(self.TEST_INFO_DICT, None)
280 script_writer = test_utils.MockScriptWriter()
281 target_info.WriteDeviceAssertions(script_writer, False)
282 self.assertEqual([('AssertDevice', 'product-device')], script_writer.lines)
283
284 def test_WriteDeviceAssertions_with_oem_props(self):
285 target_info = common.BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
286 self.TEST_OEM_DICTS)
287 script_writer = test_utils.MockScriptWriter()
288 target_info.WriteDeviceAssertions(script_writer, False)
289 self.assertEqual(
290 [
291 ('AssertOemProperty', 'ro.product.device',
292 ['device1', 'device2', 'device3'], False),
293 ('AssertOemProperty', 'ro.product.brand',
294 ['brand1', 'brand2', 'brand3'], False),
295 ],
296 script_writer.lines)
297
Steven Laver8e2086e2020-04-27 16:26:31 -0700298 def test_ResolveRoProductProperty_FromVendor(self):
299 info_dict = copy.deepcopy(self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER)
300 info = common.BuildInfo(info_dict, None)
301 self.assertEqual('vendor-product-device',
302 info.GetBuildProp('ro.product.device'))
303
304 def test_ResolveRoProductProperty_FromSystem(self):
305 info_dict = copy.deepcopy(self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER)
Greg Kaiser60225452020-05-09 00:30:33 +0000306 del info_dict['vendor.build.prop']['ro.product.vendor.device']
Steven Laver8e2086e2020-04-27 16:26:31 -0700307 info = common.BuildInfo(info_dict, None)
308 self.assertEqual('system-product-device',
309 info.GetBuildProp('ro.product.device'))
310
311 def test_ResolveRoProductProperty_InvalidPropertySearchOrder(self):
312 info_dict = copy.deepcopy(self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER)
Greg Kaiser60225452020-05-09 00:30:33 +0000313 info_dict['build.prop']['ro.product.property_source_order'] = 'bad-source'
Steven Laver8e2086e2020-04-27 16:26:31 -0700314 with self.assertRaisesRegexp(common.ExternalError,
315 'Invalid ro.product.property_source_order'):
316 info = common.BuildInfo(info_dict, None)
317 info.GetBuildProp('ro.product.device')
318
319 def test_ResolveRoProductProperty_Android10PropertySearchOrder(self):
320 info_dict = copy.deepcopy(
321 self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_10)
322 info = common.BuildInfo(info_dict, None)
323 self.assertEqual('vendor-product-device',
324 info.GetBuildProp('ro.product.device'))
325
326 def test_ResolveRoProductProperty_Android9PropertySearchOrder(self):
327 info_dict = copy.deepcopy(
328 self.TEST_INFO_DICT_PROPERTY_SOURCE_ORDER_ANDROID_9)
329 info = common.BuildInfo(info_dict, None)
330 self.assertEqual('product-device',
331 info.GetBuildProp('ro.product.device'))
332
Tao Bao1c320f82019-10-04 23:25:12 -0700333
Tao Bao65b94e92018-10-11 21:57:26 -0700334class CommonZipTest(test_utils.ReleaseToolsTestCase):
335
Tao Bao31b08072017-11-08 15:50:59 -0800336 def _verify(self, zip_file, zip_file_name, arcname, expected_hash,
Tao Baof3282b42015-04-01 11:21:55 -0700337 test_file_name=None, expected_stat=None, expected_mode=0o644,
338 expected_compress_type=zipfile.ZIP_STORED):
339 # Verify the stat if present.
340 if test_file_name is not None:
341 new_stat = os.stat(test_file_name)
342 self.assertEqual(int(expected_stat.st_mode), int(new_stat.st_mode))
343 self.assertEqual(int(expected_stat.st_mtime), int(new_stat.st_mtime))
344
345 # Reopen the zip file to verify.
346 zip_file = zipfile.ZipFile(zip_file_name, "r")
347
348 # Verify the timestamp.
349 info = zip_file.getinfo(arcname)
350 self.assertEqual(info.date_time, (2009, 1, 1, 0, 0, 0))
351
352 # Verify the file mode.
353 mode = (info.external_attr >> 16) & 0o777
354 self.assertEqual(mode, expected_mode)
355
356 # Verify the compress type.
357 self.assertEqual(info.compress_type, expected_compress_type)
358
359 # Verify the zip contents.
Tao Bao31b08072017-11-08 15:50:59 -0800360 entry = zip_file.open(arcname)
361 sha1_hash = sha1()
Tao Baoc1a1ec32019-06-18 16:29:37 -0700362 for chunk in iter(lambda: entry.read(4 * MiB), b''):
Tao Bao31b08072017-11-08 15:50:59 -0800363 sha1_hash.update(chunk)
364 self.assertEqual(expected_hash, sha1_hash.hexdigest())
Tao Baof3282b42015-04-01 11:21:55 -0700365 self.assertIsNone(zip_file.testzip())
366
Dan Albert8e0178d2015-01-27 15:53:15 -0800367 def _test_ZipWrite(self, contents, extra_zipwrite_args=None):
368 extra_zipwrite_args = dict(extra_zipwrite_args or {})
369
370 test_file = tempfile.NamedTemporaryFile(delete=False)
Dan Albert8e0178d2015-01-27 15:53:15 -0800371 test_file_name = test_file.name
Tao Baof3282b42015-04-01 11:21:55 -0700372
373 zip_file = tempfile.NamedTemporaryFile(delete=False)
Dan Albert8e0178d2015-01-27 15:53:15 -0800374 zip_file_name = zip_file.name
375
376 # File names within an archive strip the leading slash.
377 arcname = extra_zipwrite_args.get("arcname", test_file_name)
378 if arcname[0] == "/":
379 arcname = arcname[1:]
380
381 zip_file.close()
382 zip_file = zipfile.ZipFile(zip_file_name, "w")
383
384 try:
Tao Bao31b08072017-11-08 15:50:59 -0800385 sha1_hash = sha1()
386 for data in contents:
Tao Baoc1a1ec32019-06-18 16:29:37 -0700387 sha1_hash.update(bytes(data))
388 test_file.write(bytes(data))
Dan Albert8e0178d2015-01-27 15:53:15 -0800389 test_file.close()
390
Tao Baof3282b42015-04-01 11:21:55 -0700391 expected_stat = os.stat(test_file_name)
Dan Albert8e0178d2015-01-27 15:53:15 -0800392 expected_mode = extra_zipwrite_args.get("perms", 0o644)
Tao Baof3282b42015-04-01 11:21:55 -0700393 expected_compress_type = extra_zipwrite_args.get("compress_type",
394 zipfile.ZIP_STORED)
Dan Albert8e0178d2015-01-27 15:53:15 -0800395 time.sleep(5) # Make sure the atime/mtime will change measurably.
396
397 common.ZipWrite(zip_file, test_file_name, **extra_zipwrite_args)
Tao Baof3282b42015-04-01 11:21:55 -0700398 common.ZipClose(zip_file)
Dan Albert8e0178d2015-01-27 15:53:15 -0800399
Tao Bao31b08072017-11-08 15:50:59 -0800400 self._verify(zip_file, zip_file_name, arcname, sha1_hash.hexdigest(),
401 test_file_name, expected_stat, expected_mode,
402 expected_compress_type)
Dan Albert8e0178d2015-01-27 15:53:15 -0800403 finally:
404 os.remove(test_file_name)
405 os.remove(zip_file_name)
406
Tao Baof3282b42015-04-01 11:21:55 -0700407 def _test_ZipWriteStr(self, zinfo_or_arcname, contents, extra_args=None):
408 extra_args = dict(extra_args or {})
409
410 zip_file = tempfile.NamedTemporaryFile(delete=False)
411 zip_file_name = zip_file.name
412 zip_file.close()
413
414 zip_file = zipfile.ZipFile(zip_file_name, "w")
415
416 try:
417 expected_compress_type = extra_args.get("compress_type",
418 zipfile.ZIP_STORED)
419 time.sleep(5) # Make sure the atime/mtime will change measurably.
420
421 if not isinstance(zinfo_or_arcname, zipfile.ZipInfo):
Tao Bao58c1b962015-05-20 09:32:18 -0700422 arcname = zinfo_or_arcname
423 expected_mode = extra_args.get("perms", 0o644)
Tao Baof3282b42015-04-01 11:21:55 -0700424 else:
Tao Bao58c1b962015-05-20 09:32:18 -0700425 arcname = zinfo_or_arcname.filename
Tao Baoc1a1ec32019-06-18 16:29:37 -0700426 if zinfo_or_arcname.external_attr:
427 zinfo_perms = zinfo_or_arcname.external_attr >> 16
428 else:
429 zinfo_perms = 0o600
430 expected_mode = extra_args.get("perms", zinfo_perms)
Tao Baof3282b42015-04-01 11:21:55 -0700431
Tao Bao58c1b962015-05-20 09:32:18 -0700432 common.ZipWriteStr(zip_file, zinfo_or_arcname, contents, **extra_args)
Tao Baof3282b42015-04-01 11:21:55 -0700433 common.ZipClose(zip_file)
434
Tao Bao31b08072017-11-08 15:50:59 -0800435 self._verify(zip_file, zip_file_name, arcname, sha1(contents).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700436 expected_mode=expected_mode,
Tao Baof3282b42015-04-01 11:21:55 -0700437 expected_compress_type=expected_compress_type)
438 finally:
439 os.remove(zip_file_name)
440
441 def _test_ZipWriteStr_large_file(self, large, small, extra_args=None):
442 extra_args = dict(extra_args or {})
443
444 zip_file = tempfile.NamedTemporaryFile(delete=False)
445 zip_file_name = zip_file.name
446
447 test_file = tempfile.NamedTemporaryFile(delete=False)
448 test_file_name = test_file.name
449
450 arcname_large = test_file_name
451 arcname_small = "bar"
452
453 # File names within an archive strip the leading slash.
454 if arcname_large[0] == "/":
455 arcname_large = arcname_large[1:]
456
457 zip_file.close()
458 zip_file = zipfile.ZipFile(zip_file_name, "w")
459
460 try:
Tao Bao31b08072017-11-08 15:50:59 -0800461 sha1_hash = sha1()
462 for data in large:
463 sha1_hash.update(data)
464 test_file.write(data)
Tao Baof3282b42015-04-01 11:21:55 -0700465 test_file.close()
466
467 expected_stat = os.stat(test_file_name)
468 expected_mode = 0o644
469 expected_compress_type = extra_args.get("compress_type",
470 zipfile.ZIP_STORED)
471 time.sleep(5) # Make sure the atime/mtime will change measurably.
472
473 common.ZipWrite(zip_file, test_file_name, **extra_args)
474 common.ZipWriteStr(zip_file, arcname_small, small, **extra_args)
475 common.ZipClose(zip_file)
476
477 # Verify the contents written by ZipWrite().
Tao Bao31b08072017-11-08 15:50:59 -0800478 self._verify(zip_file, zip_file_name, arcname_large,
479 sha1_hash.hexdigest(), test_file_name, expected_stat,
480 expected_mode, expected_compress_type)
Tao Baof3282b42015-04-01 11:21:55 -0700481
482 # Verify the contents written by ZipWriteStr().
Tao Bao31b08072017-11-08 15:50:59 -0800483 self._verify(zip_file, zip_file_name, arcname_small,
484 sha1(small).hexdigest(),
Tao Baof3282b42015-04-01 11:21:55 -0700485 expected_compress_type=expected_compress_type)
486 finally:
487 os.remove(zip_file_name)
488 os.remove(test_file_name)
489
490 def _test_reset_ZIP64_LIMIT(self, func, *args):
491 default_limit = (1 << 31) - 1
492 self.assertEqual(default_limit, zipfile.ZIP64_LIMIT)
493 func(*args)
494 self.assertEqual(default_limit, zipfile.ZIP64_LIMIT)
495
Dan Albert8e0178d2015-01-27 15:53:15 -0800496 def test_ZipWrite(self):
497 file_contents = os.urandom(1024)
498 self._test_ZipWrite(file_contents)
499
500 def test_ZipWrite_with_opts(self):
501 file_contents = os.urandom(1024)
502 self._test_ZipWrite(file_contents, {
503 "arcname": "foobar",
504 "perms": 0o777,
505 "compress_type": zipfile.ZIP_DEFLATED,
506 })
Tao Baof3282b42015-04-01 11:21:55 -0700507 self._test_ZipWrite(file_contents, {
508 "arcname": "foobar",
509 "perms": 0o700,
510 "compress_type": zipfile.ZIP_STORED,
511 })
Dan Albert8e0178d2015-01-27 15:53:15 -0800512
513 def test_ZipWrite_large_file(self):
Tao Baof3282b42015-04-01 11:21:55 -0700514 file_contents = get_2gb_string()
Dan Albert8e0178d2015-01-27 15:53:15 -0800515 self._test_ZipWrite(file_contents, {
516 "compress_type": zipfile.ZIP_DEFLATED,
517 })
518
519 def test_ZipWrite_resets_ZIP64_LIMIT(self):
Tao Baof3282b42015-04-01 11:21:55 -0700520 self._test_reset_ZIP64_LIMIT(self._test_ZipWrite, "")
521
522 def test_ZipWriteStr(self):
523 random_string = os.urandom(1024)
524 # Passing arcname
525 self._test_ZipWriteStr("foo", random_string)
526
527 # Passing zinfo
528 zinfo = zipfile.ZipInfo(filename="foo")
529 self._test_ZipWriteStr(zinfo, random_string)
530
531 # Timestamp in the zinfo should be overwritten.
532 zinfo.date_time = (2015, 3, 1, 15, 30, 0)
533 self._test_ZipWriteStr(zinfo, random_string)
534
535 def test_ZipWriteStr_with_opts(self):
536 random_string = os.urandom(1024)
537 # Passing arcname
538 self._test_ZipWriteStr("foo", random_string, {
Tao Bao58c1b962015-05-20 09:32:18 -0700539 "perms": 0o700,
Tao Baof3282b42015-04-01 11:21:55 -0700540 "compress_type": zipfile.ZIP_DEFLATED,
541 })
Tao Bao58c1b962015-05-20 09:32:18 -0700542 self._test_ZipWriteStr("bar", random_string, {
Tao Baof3282b42015-04-01 11:21:55 -0700543 "compress_type": zipfile.ZIP_STORED,
544 })
545
546 # Passing zinfo
547 zinfo = zipfile.ZipInfo(filename="foo")
548 self._test_ZipWriteStr(zinfo, random_string, {
549 "compress_type": zipfile.ZIP_DEFLATED,
550 })
551 self._test_ZipWriteStr(zinfo, random_string, {
Tao Bao58c1b962015-05-20 09:32:18 -0700552 "perms": 0o600,
Tao Baof3282b42015-04-01 11:21:55 -0700553 "compress_type": zipfile.ZIP_STORED,
554 })
Tao Baoc1a1ec32019-06-18 16:29:37 -0700555 self._test_ZipWriteStr(zinfo, random_string, {
556 "perms": 0o000,
557 "compress_type": zipfile.ZIP_STORED,
558 })
Tao Baof3282b42015-04-01 11:21:55 -0700559
560 def test_ZipWriteStr_large_file(self):
561 # zipfile.writestr() doesn't work when the str size is over 2GiB even with
562 # the workaround. We will only test the case of writing a string into a
563 # large archive.
564 long_string = get_2gb_string()
565 short_string = os.urandom(1024)
566 self._test_ZipWriteStr_large_file(long_string, short_string, {
567 "compress_type": zipfile.ZIP_DEFLATED,
568 })
569
570 def test_ZipWriteStr_resets_ZIP64_LIMIT(self):
Tao Baoc1a1ec32019-06-18 16:29:37 -0700571 self._test_reset_ZIP64_LIMIT(self._test_ZipWriteStr, 'foo', b'')
Tao Baof3282b42015-04-01 11:21:55 -0700572 zinfo = zipfile.ZipInfo(filename="foo")
Tao Baoc1a1ec32019-06-18 16:29:37 -0700573 self._test_reset_ZIP64_LIMIT(self._test_ZipWriteStr, zinfo, b'')
Tao Bao58c1b962015-05-20 09:32:18 -0700574
575 def test_bug21309935(self):
576 zip_file = tempfile.NamedTemporaryFile(delete=False)
577 zip_file_name = zip_file.name
578 zip_file.close()
579
580 try:
581 random_string = os.urandom(1024)
582 zip_file = zipfile.ZipFile(zip_file_name, "w")
583 # Default perms should be 0o644 when passing the filename.
584 common.ZipWriteStr(zip_file, "foo", random_string)
585 # Honor the specified perms.
586 common.ZipWriteStr(zip_file, "bar", random_string, perms=0o755)
587 # The perms in zinfo should be untouched.
588 zinfo = zipfile.ZipInfo(filename="baz")
589 zinfo.external_attr = 0o740 << 16
590 common.ZipWriteStr(zip_file, zinfo, random_string)
591 # Explicitly specified perms has the priority.
592 zinfo = zipfile.ZipInfo(filename="qux")
593 zinfo.external_attr = 0o700 << 16
594 common.ZipWriteStr(zip_file, zinfo, random_string, perms=0o400)
595 common.ZipClose(zip_file)
596
Tao Bao31b08072017-11-08 15:50:59 -0800597 self._verify(zip_file, zip_file_name, "foo",
598 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700599 expected_mode=0o644)
Tao Bao31b08072017-11-08 15:50:59 -0800600 self._verify(zip_file, zip_file_name, "bar",
601 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700602 expected_mode=0o755)
Tao Bao31b08072017-11-08 15:50:59 -0800603 self._verify(zip_file, zip_file_name, "baz",
604 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700605 expected_mode=0o740)
Tao Bao31b08072017-11-08 15:50:59 -0800606 self._verify(zip_file, zip_file_name, "qux",
607 sha1(random_string).hexdigest(),
Tao Bao58c1b962015-05-20 09:32:18 -0700608 expected_mode=0o400)
609 finally:
610 os.remove(zip_file_name)
Tianjie Xu9c384d22017-06-20 17:00:55 -0700611
Tao Bao82490d32019-04-09 00:12:30 -0700612 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao89d7ab22017-12-14 17:05:33 -0800613 def test_ZipDelete(self):
614 zip_file = tempfile.NamedTemporaryFile(delete=False, suffix='.zip')
615 output_zip = zipfile.ZipFile(zip_file.name, 'w',
616 compression=zipfile.ZIP_DEFLATED)
617 with tempfile.NamedTemporaryFile() as entry_file:
618 entry_file.write(os.urandom(1024))
619 common.ZipWrite(output_zip, entry_file.name, arcname='Test1')
620 common.ZipWrite(output_zip, entry_file.name, arcname='Test2')
621 common.ZipWrite(output_zip, entry_file.name, arcname='Test3')
622 common.ZipClose(output_zip)
623 zip_file.close()
624
625 try:
626 common.ZipDelete(zip_file.name, 'Test2')
627 with zipfile.ZipFile(zip_file.name, 'r') as check_zip:
628 entries = check_zip.namelist()
629 self.assertTrue('Test1' in entries)
630 self.assertFalse('Test2' in entries)
631 self.assertTrue('Test3' in entries)
632
Tao Bao986ee862018-10-04 15:46:16 -0700633 self.assertRaises(
634 common.ExternalError, common.ZipDelete, zip_file.name, 'Test2')
Tao Bao89d7ab22017-12-14 17:05:33 -0800635 with zipfile.ZipFile(zip_file.name, 'r') as check_zip:
636 entries = check_zip.namelist()
637 self.assertTrue('Test1' in entries)
638 self.assertFalse('Test2' in entries)
639 self.assertTrue('Test3' in entries)
640
641 common.ZipDelete(zip_file.name, ['Test3'])
642 with zipfile.ZipFile(zip_file.name, 'r') as check_zip:
643 entries = check_zip.namelist()
644 self.assertTrue('Test1' in entries)
645 self.assertFalse('Test2' in entries)
646 self.assertFalse('Test3' in entries)
647
648 common.ZipDelete(zip_file.name, ['Test1', 'Test2'])
649 with zipfile.ZipFile(zip_file.name, 'r') as check_zip:
650 entries = check_zip.namelist()
651 self.assertFalse('Test1' in entries)
652 self.assertFalse('Test2' in entries)
653 self.assertFalse('Test3' in entries)
654 finally:
655 os.remove(zip_file.name)
656
Tao Bao0ff15de2019-03-20 11:26:06 -0700657 @staticmethod
658 def _test_UnzipTemp_createZipFile():
659 zip_file = common.MakeTempFile(suffix='.zip')
660 output_zip = zipfile.ZipFile(
661 zip_file, 'w', compression=zipfile.ZIP_DEFLATED)
662 contents = os.urandom(1024)
663 with tempfile.NamedTemporaryFile() as entry_file:
664 entry_file.write(contents)
665 common.ZipWrite(output_zip, entry_file.name, arcname='Test1')
666 common.ZipWrite(output_zip, entry_file.name, arcname='Test2')
667 common.ZipWrite(output_zip, entry_file.name, arcname='Foo3')
668 common.ZipWrite(output_zip, entry_file.name, arcname='Bar4')
669 common.ZipWrite(output_zip, entry_file.name, arcname='Dir5/Baz5')
670 common.ZipClose(output_zip)
671 common.ZipClose(output_zip)
672 return zip_file
673
Tao Bao82490d32019-04-09 00:12:30 -0700674 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao0ff15de2019-03-20 11:26:06 -0700675 def test_UnzipTemp(self):
676 zip_file = self._test_UnzipTemp_createZipFile()
677 unzipped_dir = common.UnzipTemp(zip_file)
678 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
679 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
680 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
681 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
682 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
683
Tao Bao82490d32019-04-09 00:12:30 -0700684 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao0ff15de2019-03-20 11:26:06 -0700685 def test_UnzipTemp_withPatterns(self):
686 zip_file = self._test_UnzipTemp_createZipFile()
687
688 unzipped_dir = common.UnzipTemp(zip_file, ['Test1'])
689 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
690 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
691 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
692 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
693 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
694
695 unzipped_dir = common.UnzipTemp(zip_file, ['Test1', 'Foo3'])
696 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
697 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
698 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
699 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
700 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
701
702 unzipped_dir = common.UnzipTemp(zip_file, ['Test*', 'Foo3*'])
703 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
704 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
705 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
706 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
707 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
708
709 unzipped_dir = common.UnzipTemp(zip_file, ['*Test1', '*Baz*'])
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.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
715
716 def test_UnzipTemp_withEmptyPatterns(self):
717 zip_file = self._test_UnzipTemp_createZipFile()
718 unzipped_dir = common.UnzipTemp(zip_file, [])
719 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
720 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
721 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
722 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
723 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
724
Tao Bao82490d32019-04-09 00:12:30 -0700725 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao0ff15de2019-03-20 11:26:06 -0700726 def test_UnzipTemp_withPartiallyMatchingPatterns(self):
727 zip_file = self._test_UnzipTemp_createZipFile()
728 unzipped_dir = common.UnzipTemp(zip_file, ['Test*', 'Nonexistent*'])
729 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
730 self.assertTrue(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
731 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
732 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
733 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
734
735 def test_UnzipTemp_withNoMatchingPatterns(self):
736 zip_file = self._test_UnzipTemp_createZipFile()
737 unzipped_dir = common.UnzipTemp(zip_file, ['Foo4', 'Nonexistent*'])
738 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test1')))
739 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Test2')))
740 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Foo3')))
741 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Bar4')))
742 self.assertFalse(os.path.exists(os.path.join(unzipped_dir, 'Dir5/Baz5')))
743
Tao Bao89d7ab22017-12-14 17:05:33 -0800744
Tao Bao65b94e92018-10-11 21:57:26 -0700745class CommonApkUtilsTest(test_utils.ReleaseToolsTestCase):
Tao Bao818ddf52018-01-05 11:17:34 -0800746 """Tests the APK utils related functions."""
747
748 APKCERTS_TXT1 = (
749 'name="RecoveryLocalizer.apk" certificate="certs/devkey.x509.pem"'
750 ' private_key="certs/devkey.pk8"\n'
751 'name="Settings.apk"'
Dan Willemsen0ab1be62019-04-09 21:35:37 -0700752 ' certificate="build/make/target/product/security/platform.x509.pem"'
753 ' private_key="build/make/target/product/security/platform.pk8"\n'
Tao Bao818ddf52018-01-05 11:17:34 -0800754 'name="TV.apk" certificate="PRESIGNED" private_key=""\n'
755 )
756
757 APKCERTS_CERTMAP1 = {
758 'RecoveryLocalizer.apk' : 'certs/devkey',
Dan Willemsen0ab1be62019-04-09 21:35:37 -0700759 'Settings.apk' : 'build/make/target/product/security/platform',
Tao Bao818ddf52018-01-05 11:17:34 -0800760 'TV.apk' : 'PRESIGNED',
761 }
762
763 APKCERTS_TXT2 = (
764 'name="Compressed1.apk" certificate="certs/compressed1.x509.pem"'
765 ' private_key="certs/compressed1.pk8" compressed="gz"\n'
766 'name="Compressed2a.apk" certificate="certs/compressed2.x509.pem"'
767 ' private_key="certs/compressed2.pk8" compressed="gz"\n'
768 'name="Compressed2b.apk" certificate="certs/compressed2.x509.pem"'
769 ' private_key="certs/compressed2.pk8" compressed="gz"\n'
770 'name="Compressed3.apk" certificate="certs/compressed3.x509.pem"'
771 ' private_key="certs/compressed3.pk8" compressed="gz"\n'
772 )
773
774 APKCERTS_CERTMAP2 = {
775 'Compressed1.apk' : 'certs/compressed1',
776 'Compressed2a.apk' : 'certs/compressed2',
777 'Compressed2b.apk' : 'certs/compressed2',
778 'Compressed3.apk' : 'certs/compressed3',
779 }
780
781 APKCERTS_TXT3 = (
782 'name="Compressed4.apk" certificate="certs/compressed4.x509.pem"'
783 ' private_key="certs/compressed4.pk8" compressed="xz"\n'
784 )
785
786 APKCERTS_CERTMAP3 = {
787 'Compressed4.apk' : 'certs/compressed4',
788 }
789
Bill Peckham5c7b0342020-04-03 15:36:23 -0700790 # Test parsing with no optional fields, both optional fields, and only the
791 # partition optional field.
792 APKCERTS_TXT4 = (
793 'name="RecoveryLocalizer.apk" certificate="certs/devkey.x509.pem"'
794 ' private_key="certs/devkey.pk8"\n'
795 'name="Settings.apk"'
796 ' certificate="build/make/target/product/security/platform.x509.pem"'
797 ' private_key="build/make/target/product/security/platform.pk8"'
798 ' compressed="gz" partition="system"\n'
799 'name="TV.apk" certificate="PRESIGNED" private_key=""'
800 ' partition="product"\n'
801 )
802
803 APKCERTS_CERTMAP4 = {
804 'RecoveryLocalizer.apk' : 'certs/devkey',
805 'Settings.apk' : 'build/make/target/product/security/platform',
806 'TV.apk' : 'PRESIGNED',
807 }
808
Tao Bao17e4e612018-02-16 17:12:54 -0800809 def setUp(self):
810 self.testdata_dir = test_utils.get_testdata_dir()
811
Tao Bao818ddf52018-01-05 11:17:34 -0800812 @staticmethod
813 def _write_apkcerts_txt(apkcerts_txt, additional=None):
814 if additional is None:
815 additional = []
816 target_files = common.MakeTempFile(suffix='.zip')
817 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
818 target_files_zip.writestr('META/apkcerts.txt', apkcerts_txt)
819 for entry in additional:
820 target_files_zip.writestr(entry, '')
821 return target_files
822
823 def test_ReadApkCerts_NoncompressedApks(self):
824 target_files = self._write_apkcerts_txt(self.APKCERTS_TXT1)
825 with zipfile.ZipFile(target_files, 'r') as input_zip:
826 certmap, ext = common.ReadApkCerts(input_zip)
827
828 self.assertDictEqual(self.APKCERTS_CERTMAP1, certmap)
829 self.assertIsNone(ext)
830
831 def test_ReadApkCerts_CompressedApks(self):
832 # We have "installed" Compressed1.apk.gz only. Note that Compressed3.apk is
833 # not stored in '.gz' format, so it shouldn't be considered as installed.
834 target_files = self._write_apkcerts_txt(
835 self.APKCERTS_TXT2,
836 ['Compressed1.apk.gz', 'Compressed3.apk'])
837
838 with zipfile.ZipFile(target_files, 'r') as input_zip:
839 certmap, ext = common.ReadApkCerts(input_zip)
840
841 self.assertDictEqual(self.APKCERTS_CERTMAP2, certmap)
842 self.assertEqual('.gz', ext)
843
844 # Alternative case with '.xz'.
845 target_files = self._write_apkcerts_txt(
846 self.APKCERTS_TXT3, ['Compressed4.apk.xz'])
847
848 with zipfile.ZipFile(target_files, 'r') as input_zip:
849 certmap, ext = common.ReadApkCerts(input_zip)
850
851 self.assertDictEqual(self.APKCERTS_CERTMAP3, certmap)
852 self.assertEqual('.xz', ext)
853
854 def test_ReadApkCerts_CompressedAndNoncompressedApks(self):
855 target_files = self._write_apkcerts_txt(
856 self.APKCERTS_TXT1 + self.APKCERTS_TXT2,
857 ['Compressed1.apk.gz', 'Compressed3.apk'])
858
859 with zipfile.ZipFile(target_files, 'r') as input_zip:
860 certmap, ext = common.ReadApkCerts(input_zip)
861
862 certmap_merged = self.APKCERTS_CERTMAP1.copy()
863 certmap_merged.update(self.APKCERTS_CERTMAP2)
864 self.assertDictEqual(certmap_merged, certmap)
865 self.assertEqual('.gz', ext)
866
867 def test_ReadApkCerts_MultipleCompressionMethods(self):
868 target_files = self._write_apkcerts_txt(
869 self.APKCERTS_TXT2 + self.APKCERTS_TXT3,
870 ['Compressed1.apk.gz', 'Compressed4.apk.xz'])
871
872 with zipfile.ZipFile(target_files, 'r') as input_zip:
873 self.assertRaises(ValueError, common.ReadApkCerts, input_zip)
874
875 def test_ReadApkCerts_MismatchingKeys(self):
876 malformed_apkcerts_txt = (
877 'name="App1.apk" certificate="certs/cert1.x509.pem"'
878 ' private_key="certs/cert2.pk8"\n'
879 )
880 target_files = self._write_apkcerts_txt(malformed_apkcerts_txt)
881
882 with zipfile.ZipFile(target_files, 'r') as input_zip:
883 self.assertRaises(ValueError, common.ReadApkCerts, input_zip)
884
Bill Peckham5c7b0342020-04-03 15:36:23 -0700885 def test_ReadApkCerts_WithWithoutOptionalFields(self):
886 target_files = self._write_apkcerts_txt(self.APKCERTS_TXT4)
887 with zipfile.ZipFile(target_files, 'r') as input_zip:
888 certmap, ext = common.ReadApkCerts(input_zip)
889
890 self.assertDictEqual(self.APKCERTS_CERTMAP4, certmap)
891 self.assertIsNone(ext)
892
Tao Bao04e1f012018-02-04 12:13:35 -0800893 def test_ExtractPublicKey(self):
Tao Bao17e4e612018-02-16 17:12:54 -0800894 cert = os.path.join(self.testdata_dir, 'testkey.x509.pem')
895 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
Tao Baoda30cfa2017-12-01 16:19:46 -0800896 with open(pubkey) as pubkey_fp:
Tao Bao04e1f012018-02-04 12:13:35 -0800897 self.assertEqual(pubkey_fp.read(), common.ExtractPublicKey(cert))
898
899 def test_ExtractPublicKey_invalidInput(self):
Tao Bao17e4e612018-02-16 17:12:54 -0800900 wrong_input = os.path.join(self.testdata_dir, 'testkey.pk8')
Tao Bao04e1f012018-02-04 12:13:35 -0800901 self.assertRaises(AssertionError, common.ExtractPublicKey, wrong_input)
902
Tao Bao82490d32019-04-09 00:12:30 -0700903 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao2cc0ca12019-03-15 10:44:43 -0700904 def test_ExtractAvbPublicKey(self):
905 privkey = os.path.join(self.testdata_dir, 'testkey.key')
906 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
Tao Bao1ac886e2019-06-26 11:58:22 -0700907 extracted_from_privkey = common.ExtractAvbPublicKey('avbtool', privkey)
908 extracted_from_pubkey = common.ExtractAvbPublicKey('avbtool', pubkey)
909 with open(extracted_from_privkey, 'rb') as privkey_fp, \
910 open(extracted_from_pubkey, 'rb') as pubkey_fp:
Tao Bao2cc0ca12019-03-15 10:44:43 -0700911 self.assertEqual(privkey_fp.read(), pubkey_fp.read())
912
Tao Bao17e4e612018-02-16 17:12:54 -0800913 def test_ParseCertificate(self):
914 cert = os.path.join(self.testdata_dir, 'testkey.x509.pem')
915
916 cmd = ['openssl', 'x509', '-in', cert, '-outform', 'DER']
Tao Baoda30cfa2017-12-01 16:19:46 -0800917 proc = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
918 universal_newlines=False)
Tao Bao17e4e612018-02-16 17:12:54 -0800919 expected, _ = proc.communicate()
920 self.assertEqual(0, proc.returncode)
921
922 with open(cert) as cert_fp:
923 actual = common.ParseCertificate(cert_fp.read())
924 self.assertEqual(expected, actual)
925
Tao Bao82490d32019-04-09 00:12:30 -0700926 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700927 def test_GetMinSdkVersion(self):
928 test_app = os.path.join(self.testdata_dir, 'TestApp.apk')
929 self.assertEqual('24', common.GetMinSdkVersion(test_app))
930
Tao Bao82490d32019-04-09 00:12:30 -0700931 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700932 def test_GetMinSdkVersion_invalidInput(self):
933 self.assertRaises(
934 common.ExternalError, common.GetMinSdkVersion, 'does-not-exist.apk')
935
Tao Bao82490d32019-04-09 00:12:30 -0700936 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700937 def test_GetMinSdkVersionInt(self):
938 test_app = os.path.join(self.testdata_dir, 'TestApp.apk')
939 self.assertEqual(24, common.GetMinSdkVersionInt(test_app, {}))
940
Tao Bao82490d32019-04-09 00:12:30 -0700941 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baof47bf0f2018-03-21 23:28:51 -0700942 def test_GetMinSdkVersionInt_invalidInput(self):
943 self.assertRaises(
944 common.ExternalError, common.GetMinSdkVersionInt, 'does-not-exist.apk',
945 {})
946
Tao Bao818ddf52018-01-05 11:17:34 -0800947
Tao Bao65b94e92018-10-11 21:57:26 -0700948class CommonUtilsTest(test_utils.ReleaseToolsTestCase):
Tao Baofc7e0e02018-02-13 13:54:02 -0800949
Tao Bao02a08592018-07-22 12:40:45 -0700950 def setUp(self):
951 self.testdata_dir = test_utils.get_testdata_dir()
952
Tao Bao82490d32019-04-09 00:12:30 -0700953 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -0800954 def test_GetSparseImage_emptyBlockMapFile(self):
955 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
956 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
957 target_files_zip.write(
958 test_utils.construct_sparse_image([
959 (0xCAC1, 6),
960 (0xCAC3, 3),
961 (0xCAC1, 4)]),
962 arcname='IMAGES/system.img')
963 target_files_zip.writestr('IMAGES/system.map', '')
964 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 8))
965 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
966
Tao Baodba59ee2018-01-09 13:21:02 -0800967 tempdir = common.UnzipTemp(target_files)
968 with zipfile.ZipFile(target_files, 'r') as input_zip:
969 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
Tao Baofc7e0e02018-02-13 13:54:02 -0800970
971 self.assertDictEqual(
972 {
973 '__COPY': RangeSet("0"),
974 '__NONZERO-0': RangeSet("1-5 9-12"),
975 },
976 sparse_image.file_map)
977
Tao Baob2de7d92019-04-10 10:01:47 -0700978 def test_GetSparseImage_missingImageFile(self):
Tao Baofc7e0e02018-02-13 13:54:02 -0800979 self.assertRaises(
Tao Baob2de7d92019-04-10 10:01:47 -0700980 AssertionError, common.GetSparseImage, 'system2', self.testdata_dir,
981 None, False)
Tao Baofc7e0e02018-02-13 13:54:02 -0800982 self.assertRaises(
Tao Baob2de7d92019-04-10 10:01:47 -0700983 AssertionError, common.GetSparseImage, 'unknown', self.testdata_dir,
984 None, False)
Tao Baofc7e0e02018-02-13 13:54:02 -0800985
Tao Bao82490d32019-04-09 00:12:30 -0700986 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -0800987 def test_GetSparseImage_missingBlockMapFile(self):
988 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
989 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
990 target_files_zip.write(
991 test_utils.construct_sparse_image([
992 (0xCAC1, 6),
993 (0xCAC3, 3),
994 (0xCAC1, 4)]),
995 arcname='IMAGES/system.img')
996 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 8))
997 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
998
Tao Baodba59ee2018-01-09 13:21:02 -0800999 tempdir = common.UnzipTemp(target_files)
1000 with zipfile.ZipFile(target_files, 'r') as input_zip:
1001 self.assertRaises(
1002 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
1003 False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001004
Tao Bao82490d32019-04-09 00:12:30 -07001005 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001006 def test_GetSparseImage_sharedBlocks_notAllowed(self):
1007 """Tests the case of having overlapping blocks but disallowed."""
1008 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
1009 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
1010 target_files_zip.write(
1011 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1012 arcname='IMAGES/system.img')
1013 # Block 10 is shared between two files.
1014 target_files_zip.writestr(
1015 'IMAGES/system.map',
1016 '\n'.join([
1017 '/system/file1 1-5 9-10',
1018 '/system/file2 10-12']))
1019 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1020 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1021
Tao Baodba59ee2018-01-09 13:21:02 -08001022 tempdir = common.UnzipTemp(target_files)
1023 with zipfile.ZipFile(target_files, 'r') as input_zip:
1024 self.assertRaises(
1025 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
1026 False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001027
Tao Bao82490d32019-04-09 00:12:30 -07001028 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001029 def test_GetSparseImage_sharedBlocks_allowed(self):
1030 """Tests the case for target using BOARD_EXT4_SHARE_DUP_BLOCKS := true."""
1031 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
1032 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
1033 # Construct an image with a care_map of "0-5 9-12".
1034 target_files_zip.write(
1035 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1036 arcname='IMAGES/system.img')
1037 # Block 10 is shared between two files.
1038 target_files_zip.writestr(
1039 'IMAGES/system.map',
1040 '\n'.join([
1041 '/system/file1 1-5 9-10',
1042 '/system/file2 10-12']))
1043 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1044 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1045
Tao Baodba59ee2018-01-09 13:21:02 -08001046 tempdir = common.UnzipTemp(target_files)
1047 with zipfile.ZipFile(target_files, 'r') as input_zip:
1048 sparse_image = common.GetSparseImage('system', tempdir, input_zip, True)
Tao Baofc7e0e02018-02-13 13:54:02 -08001049
1050 self.assertDictEqual(
1051 {
1052 '__COPY': RangeSet("0"),
1053 '__NONZERO-0': RangeSet("6-8 13-15"),
1054 '/system/file1': RangeSet("1-5 9-10"),
1055 '/system/file2': RangeSet("11-12"),
1056 },
1057 sparse_image.file_map)
1058
1059 # '/system/file2' should be marked with 'uses_shared_blocks', but not with
1060 # 'incomplete'.
1061 self.assertTrue(
1062 sparse_image.file_map['/system/file2'].extra['uses_shared_blocks'])
1063 self.assertNotIn(
1064 'incomplete', sparse_image.file_map['/system/file2'].extra)
1065
Tao Baoa264fef2019-10-06 21:55:20 -07001066 # '/system/file1' will only contain one field -- a copy of the input text.
1067 self.assertEqual(1, len(sparse_image.file_map['/system/file1'].extra))
1068
1069 # Meta entries should not have any extra tag.
Tao Baofc7e0e02018-02-13 13:54:02 -08001070 self.assertFalse(sparse_image.file_map['__COPY'].extra)
1071 self.assertFalse(sparse_image.file_map['__NONZERO-0'].extra)
Tao Baofc7e0e02018-02-13 13:54:02 -08001072
Tao Bao82490d32019-04-09 00:12:30 -07001073 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baofc7e0e02018-02-13 13:54:02 -08001074 def test_GetSparseImage_incompleteRanges(self):
1075 """Tests the case of ext4 images with holes."""
1076 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
1077 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
1078 target_files_zip.write(
1079 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1080 arcname='IMAGES/system.img')
1081 target_files_zip.writestr(
1082 'IMAGES/system.map',
1083 '\n'.join([
1084 '/system/file1 1-5 9-10',
1085 '/system/file2 11-12']))
1086 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1087 # '/system/file2' has less blocks listed (2) than actual (3).
1088 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1089
Tao Baodba59ee2018-01-09 13:21:02 -08001090 tempdir = common.UnzipTemp(target_files)
1091 with zipfile.ZipFile(target_files, 'r') as input_zip:
1092 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
Tao Baofc7e0e02018-02-13 13:54:02 -08001093
Tao Baoa264fef2019-10-06 21:55:20 -07001094 self.assertEqual(
1095 '1-5 9-10',
1096 sparse_image.file_map['/system/file1'].extra['text_str'])
Tao Baofc7e0e02018-02-13 13:54:02 -08001097 self.assertTrue(sparse_image.file_map['/system/file2'].extra['incomplete'])
1098
Tao Bao82490d32019-04-09 00:12:30 -07001099 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -07001100 def test_GetSparseImage_systemRootImage_filenameWithExtraLeadingSlash(self):
1101 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
1102 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
1103 target_files_zip.write(
1104 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1105 arcname='IMAGES/system.img')
1106 target_files_zip.writestr(
1107 'IMAGES/system.map',
1108 '\n'.join([
1109 '//system/file1 1-5 9-10',
1110 '//system/file2 11-12',
1111 '/system/app/file3 13-15']))
1112 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1113 # '/system/file2' has less blocks listed (2) than actual (3).
1114 target_files_zip.writestr('SYSTEM/file2', os.urandom(4096 * 3))
1115 # '/system/app/file3' has less blocks listed (3) than actual (4).
1116 target_files_zip.writestr('SYSTEM/app/file3', os.urandom(4096 * 4))
1117
1118 tempdir = common.UnzipTemp(target_files)
1119 with zipfile.ZipFile(target_files, 'r') as input_zip:
1120 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
1121
Tao Baoa264fef2019-10-06 21:55:20 -07001122 self.assertEqual(
1123 '1-5 9-10',
1124 sparse_image.file_map['//system/file1'].extra['text_str'])
Tao Baod3554e62018-07-10 15:31:22 -07001125 self.assertTrue(sparse_image.file_map['//system/file2'].extra['incomplete'])
1126 self.assertTrue(
1127 sparse_image.file_map['/system/app/file3'].extra['incomplete'])
1128
Tao Bao82490d32019-04-09 00:12:30 -07001129 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -07001130 def test_GetSparseImage_systemRootImage_nonSystemFiles(self):
1131 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
1132 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
1133 target_files_zip.write(
1134 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1135 arcname='IMAGES/system.img')
1136 target_files_zip.writestr(
1137 'IMAGES/system.map',
1138 '\n'.join([
1139 '//system/file1 1-5 9-10',
1140 '//init.rc 13-15']))
1141 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1142 # '/init.rc' has less blocks listed (3) than actual (4).
1143 target_files_zip.writestr('ROOT/init.rc', os.urandom(4096 * 4))
1144
1145 tempdir = common.UnzipTemp(target_files)
1146 with zipfile.ZipFile(target_files, 'r') as input_zip:
1147 sparse_image = common.GetSparseImage('system', tempdir, input_zip, False)
1148
Tao Baoa264fef2019-10-06 21:55:20 -07001149 self.assertEqual(
1150 '1-5 9-10',
1151 sparse_image.file_map['//system/file1'].extra['text_str'])
Tao Baod3554e62018-07-10 15:31:22 -07001152 self.assertTrue(sparse_image.file_map['//init.rc'].extra['incomplete'])
1153
Tao Bao82490d32019-04-09 00:12:30 -07001154 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baod3554e62018-07-10 15:31:22 -07001155 def test_GetSparseImage_fileNotFound(self):
1156 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
1157 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
1158 target_files_zip.write(
1159 test_utils.construct_sparse_image([(0xCAC2, 16)]),
1160 arcname='IMAGES/system.img')
1161 target_files_zip.writestr(
1162 'IMAGES/system.map',
1163 '\n'.join([
1164 '//system/file1 1-5 9-10',
1165 '//system/file2 11-12']))
1166 target_files_zip.writestr('SYSTEM/file1', os.urandom(4096 * 7))
1167
1168 tempdir = common.UnzipTemp(target_files)
1169 with zipfile.ZipFile(target_files, 'r') as input_zip:
1170 self.assertRaises(
1171 AssertionError, common.GetSparseImage, 'system', tempdir, input_zip,
1172 False)
1173
Tao Bao82490d32019-04-09 00:12:30 -07001174 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001175 def test_GetAvbChainedPartitionArg(self):
1176 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
1177 info_dict = {
1178 'avb_avbtool': 'avbtool',
1179 'avb_system_key_path': pubkey,
1180 'avb_system_rollback_index_location': 2,
1181 }
1182 args = common.GetAvbChainedPartitionArg('system', info_dict).split(':')
1183 self.assertEqual(3, len(args))
1184 self.assertEqual('system', args[0])
1185 self.assertEqual('2', args[1])
1186 self.assertTrue(os.path.exists(args[2]))
1187
Tao Bao82490d32019-04-09 00:12:30 -07001188 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001189 def test_GetAvbChainedPartitionArg_withPrivateKey(self):
1190 key = os.path.join(self.testdata_dir, 'testkey.key')
1191 info_dict = {
1192 'avb_avbtool': 'avbtool',
1193 'avb_product_key_path': key,
1194 'avb_product_rollback_index_location': 2,
1195 }
1196 args = common.GetAvbChainedPartitionArg('product', info_dict).split(':')
1197 self.assertEqual(3, len(args))
1198 self.assertEqual('product', args[0])
1199 self.assertEqual('2', args[1])
1200 self.assertTrue(os.path.exists(args[2]))
1201
Tao Bao82490d32019-04-09 00:12:30 -07001202 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001203 def test_GetAvbChainedPartitionArg_withSpecifiedKey(self):
1204 info_dict = {
1205 'avb_avbtool': 'avbtool',
1206 'avb_system_key_path': 'does-not-exist',
1207 'avb_system_rollback_index_location': 2,
1208 }
1209 pubkey = os.path.join(self.testdata_dir, 'testkey.pubkey.pem')
1210 args = common.GetAvbChainedPartitionArg(
1211 'system', info_dict, pubkey).split(':')
1212 self.assertEqual(3, len(args))
1213 self.assertEqual('system', args[0])
1214 self.assertEqual('2', args[1])
1215 self.assertTrue(os.path.exists(args[2]))
1216
Tao Bao82490d32019-04-09 00:12:30 -07001217 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao02a08592018-07-22 12:40:45 -07001218 def test_GetAvbChainedPartitionArg_invalidKey(self):
1219 pubkey = os.path.join(self.testdata_dir, 'testkey_with_passwd.x509.pem')
1220 info_dict = {
1221 'avb_avbtool': 'avbtool',
1222 'avb_system_key_path': pubkey,
1223 'avb_system_rollback_index_location': 2,
1224 }
1225 self.assertRaises(
Tao Bao986ee862018-10-04 15:46:16 -07001226 common.ExternalError, common.GetAvbChainedPartitionArg, 'system',
1227 info_dict)
Tao Bao02a08592018-07-22 12:40:45 -07001228
Tao Baoa57ab9f2018-08-24 12:08:38 -07001229 INFO_DICT_DEFAULT = {
1230 'recovery_api_version': 3,
1231 'fstab_version': 2,
1232 'system_root_image': 'true',
1233 'no_recovery' : 'true',
1234 'recovery_as_boot': 'true',
1235 }
1236
Daniel Norman4cc9df62019-07-18 10:11:07 -07001237 def test_LoadListFromFile(self):
1238 file_path = os.path.join(self.testdata_dir,
1239 'merge_config_framework_item_list')
1240 contents = common.LoadListFromFile(file_path)
1241 expected_contents = [
1242 'META/apkcerts.txt',
1243 'META/filesystem_config.txt',
1244 'META/root_filesystem_config.txt',
1245 'META/system_manifest.xml',
1246 'META/system_matrix.xml',
1247 'META/update_engine_config.txt',
1248 'PRODUCT/*',
1249 'ROOT/*',
1250 'SYSTEM/*',
1251 ]
1252 self.assertEqual(sorted(contents), sorted(expected_contents))
1253
Tao Baoa57ab9f2018-08-24 12:08:38 -07001254 @staticmethod
1255 def _test_LoadInfoDict_createTargetFiles(info_dict, fstab_path):
1256 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
1257 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
1258 info_values = ''.join(
Tao Baoda30cfa2017-12-01 16:19:46 -08001259 ['{}={}\n'.format(k, v) for k, v in sorted(info_dict.items())])
Tao Baoa57ab9f2018-08-24 12:08:38 -07001260 common.ZipWriteStr(target_files_zip, 'META/misc_info.txt', info_values)
1261
1262 FSTAB_TEMPLATE = "/dev/block/system {} ext4 ro,barrier=1 defaults"
1263 if info_dict.get('system_root_image') == 'true':
1264 fstab_values = FSTAB_TEMPLATE.format('/')
1265 else:
1266 fstab_values = FSTAB_TEMPLATE.format('/system')
1267 common.ZipWriteStr(target_files_zip, fstab_path, fstab_values)
Tao Bao410ad8b2018-08-24 12:08:38 -07001268
1269 common.ZipWriteStr(
1270 target_files_zip, 'META/file_contexts', 'file-contexts')
Tao Baoa57ab9f2018-08-24 12:08:38 -07001271 return target_files
1272
1273 def test_LoadInfoDict(self):
1274 target_files = self._test_LoadInfoDict_createTargetFiles(
1275 self.INFO_DICT_DEFAULT,
1276 'BOOT/RAMDISK/system/etc/recovery.fstab')
1277 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1278 loaded_dict = common.LoadInfoDict(target_files_zip)
1279 self.assertEqual(3, loaded_dict['recovery_api_version'])
1280 self.assertEqual(2, loaded_dict['fstab_version'])
1281 self.assertIn('/', loaded_dict['fstab'])
1282 self.assertIn('/system', loaded_dict['fstab'])
1283
1284 def test_LoadInfoDict_legacyRecoveryFstabPath(self):
1285 target_files = self._test_LoadInfoDict_createTargetFiles(
1286 self.INFO_DICT_DEFAULT,
1287 'BOOT/RAMDISK/etc/recovery.fstab')
1288 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1289 loaded_dict = common.LoadInfoDict(target_files_zip)
1290 self.assertEqual(3, loaded_dict['recovery_api_version'])
1291 self.assertEqual(2, loaded_dict['fstab_version'])
1292 self.assertIn('/', loaded_dict['fstab'])
1293 self.assertIn('/system', loaded_dict['fstab'])
1294
Tao Bao82490d32019-04-09 00:12:30 -07001295 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoa57ab9f2018-08-24 12:08:38 -07001296 def test_LoadInfoDict_dirInput(self):
1297 target_files = self._test_LoadInfoDict_createTargetFiles(
1298 self.INFO_DICT_DEFAULT,
1299 'BOOT/RAMDISK/system/etc/recovery.fstab')
1300 unzipped = common.UnzipTemp(target_files)
1301 loaded_dict = common.LoadInfoDict(unzipped)
1302 self.assertEqual(3, loaded_dict['recovery_api_version'])
1303 self.assertEqual(2, loaded_dict['fstab_version'])
1304 self.assertIn('/', loaded_dict['fstab'])
1305 self.assertIn('/system', loaded_dict['fstab'])
1306
Tao Bao82490d32019-04-09 00:12:30 -07001307 @test_utils.SkipIfExternalToolsUnavailable()
Tao Baoa57ab9f2018-08-24 12:08:38 -07001308 def test_LoadInfoDict_dirInput_legacyRecoveryFstabPath(self):
1309 target_files = self._test_LoadInfoDict_createTargetFiles(
1310 self.INFO_DICT_DEFAULT,
1311 'BOOT/RAMDISK/system/etc/recovery.fstab')
1312 unzipped = common.UnzipTemp(target_files)
1313 loaded_dict = common.LoadInfoDict(unzipped)
1314 self.assertEqual(3, loaded_dict['recovery_api_version'])
1315 self.assertEqual(2, loaded_dict['fstab_version'])
1316 self.assertIn('/', loaded_dict['fstab'])
1317 self.assertIn('/system', loaded_dict['fstab'])
1318
1319 def test_LoadInfoDict_systemRootImageFalse(self):
1320 # Devices not using system-as-root nor recovery-as-boot. Non-A/B devices
1321 # launched prior to P will likely have this config.
1322 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
1323 del info_dict['no_recovery']
1324 del info_dict['system_root_image']
1325 del info_dict['recovery_as_boot']
1326 target_files = self._test_LoadInfoDict_createTargetFiles(
1327 info_dict,
1328 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
1329 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1330 loaded_dict = common.LoadInfoDict(target_files_zip)
1331 self.assertEqual(3, loaded_dict['recovery_api_version'])
1332 self.assertEqual(2, loaded_dict['fstab_version'])
1333 self.assertNotIn('/', loaded_dict['fstab'])
1334 self.assertIn('/system', loaded_dict['fstab'])
1335
1336 def test_LoadInfoDict_recoveryAsBootFalse(self):
1337 # Devices using system-as-root, but with standalone recovery image. Non-A/B
1338 # devices launched since P will likely have this config.
1339 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
1340 del info_dict['no_recovery']
1341 del info_dict['recovery_as_boot']
1342 target_files = self._test_LoadInfoDict_createTargetFiles(
1343 info_dict,
1344 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
1345 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1346 loaded_dict = common.LoadInfoDict(target_files_zip)
1347 self.assertEqual(3, loaded_dict['recovery_api_version'])
1348 self.assertEqual(2, loaded_dict['fstab_version'])
1349 self.assertIn('/', loaded_dict['fstab'])
1350 self.assertIn('/system', loaded_dict['fstab'])
1351
1352 def test_LoadInfoDict_noRecoveryTrue(self):
1353 # Device doesn't have a recovery partition at all.
1354 info_dict = copy.copy(self.INFO_DICT_DEFAULT)
1355 del info_dict['recovery_as_boot']
1356 target_files = self._test_LoadInfoDict_createTargetFiles(
1357 info_dict,
1358 'RECOVERY/RAMDISK/system/etc/recovery.fstab')
1359 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1360 loaded_dict = common.LoadInfoDict(target_files_zip)
1361 self.assertEqual(3, loaded_dict['recovery_api_version'])
1362 self.assertEqual(2, loaded_dict['fstab_version'])
1363 self.assertIsNone(loaded_dict['fstab'])
1364
Tao Bao82490d32019-04-09 00:12:30 -07001365 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao410ad8b2018-08-24 12:08:38 -07001366 def test_LoadInfoDict_missingMetaMiscInfoTxt(self):
1367 target_files = self._test_LoadInfoDict_createTargetFiles(
1368 self.INFO_DICT_DEFAULT,
1369 'BOOT/RAMDISK/system/etc/recovery.fstab')
1370 common.ZipDelete(target_files, 'META/misc_info.txt')
1371 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1372 self.assertRaises(ValueError, common.LoadInfoDict, target_files_zip)
1373
Tao Bao82490d32019-04-09 00:12:30 -07001374 @test_utils.SkipIfExternalToolsUnavailable()
Tao Bao410ad8b2018-08-24 12:08:38 -07001375 def test_LoadInfoDict_repacking(self):
1376 target_files = self._test_LoadInfoDict_createTargetFiles(
1377 self.INFO_DICT_DEFAULT,
1378 'BOOT/RAMDISK/system/etc/recovery.fstab')
1379 unzipped = common.UnzipTemp(target_files)
1380 loaded_dict = common.LoadInfoDict(unzipped, True)
1381 self.assertEqual(3, loaded_dict['recovery_api_version'])
1382 self.assertEqual(2, loaded_dict['fstab_version'])
1383 self.assertIn('/', loaded_dict['fstab'])
1384 self.assertIn('/system', loaded_dict['fstab'])
1385 self.assertEqual(
1386 os.path.join(unzipped, 'ROOT'), loaded_dict['root_dir'])
1387 self.assertEqual(
1388 os.path.join(unzipped, 'META', 'root_filesystem_config.txt'),
1389 loaded_dict['root_fs_config'])
1390
1391 def test_LoadInfoDict_repackingWithZipFileInput(self):
1392 target_files = self._test_LoadInfoDict_createTargetFiles(
1393 self.INFO_DICT_DEFAULT,
1394 'BOOT/RAMDISK/system/etc/recovery.fstab')
1395 with zipfile.ZipFile(target_files, 'r') as target_files_zip:
1396 self.assertRaises(
1397 AssertionError, common.LoadInfoDict, target_files_zip, True)
1398
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001399 def test_MergeDynamicPartitionInfoDicts_ReturnsMergedDict(self):
1400 framework_dict = {
1401 'super_partition_groups': 'group_a',
1402 'dynamic_partition_list': 'system',
Daniel Norman55417142019-11-25 16:04:36 -08001403 'super_group_a_partition_list': 'system',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001404 }
1405 vendor_dict = {
1406 'super_partition_groups': 'group_a group_b',
1407 'dynamic_partition_list': 'vendor product',
Daniel Norman55417142019-11-25 16:04:36 -08001408 'super_group_a_partition_list': 'vendor',
1409 'super_group_a_group_size': '1000',
1410 'super_group_b_partition_list': 'product',
1411 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001412 }
1413 merged_dict = common.MergeDynamicPartitionInfoDicts(
1414 framework_dict=framework_dict,
Daniel Norman55417142019-11-25 16:04:36 -08001415 vendor_dict=vendor_dict)
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001416 expected_merged_dict = {
1417 'super_partition_groups': 'group_a group_b',
1418 'dynamic_partition_list': 'system vendor product',
Daniel Norman55417142019-11-25 16:04:36 -08001419 'super_group_a_partition_list': 'system vendor',
1420 'super_group_a_group_size': '1000',
1421 'super_group_b_partition_list': 'product',
1422 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001423 }
1424 self.assertEqual(merged_dict, expected_merged_dict)
1425
1426 def test_MergeDynamicPartitionInfoDicts_IgnoringFrameworkGroupSize(self):
1427 framework_dict = {
1428 'super_partition_groups': 'group_a',
1429 'dynamic_partition_list': 'system',
Daniel Norman55417142019-11-25 16:04:36 -08001430 'super_group_a_partition_list': 'system',
1431 'super_group_a_group_size': '5000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001432 }
1433 vendor_dict = {
1434 'super_partition_groups': 'group_a group_b',
1435 'dynamic_partition_list': 'vendor product',
Daniel Norman55417142019-11-25 16:04:36 -08001436 'super_group_a_partition_list': 'vendor',
1437 'super_group_a_group_size': '1000',
1438 'super_group_b_partition_list': 'product',
1439 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001440 }
1441 merged_dict = common.MergeDynamicPartitionInfoDicts(
1442 framework_dict=framework_dict,
Daniel Norman55417142019-11-25 16:04:36 -08001443 vendor_dict=vendor_dict)
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001444 expected_merged_dict = {
1445 'super_partition_groups': 'group_a group_b',
1446 'dynamic_partition_list': 'system vendor product',
Daniel Norman55417142019-11-25 16:04:36 -08001447 'super_group_a_partition_list': 'system vendor',
1448 'super_group_a_group_size': '1000',
1449 'super_group_b_partition_list': 'product',
1450 'super_group_b_group_size': '2000',
Daniel Normanbfc51ef2019-07-24 14:34:54 -07001451 }
1452 self.assertEqual(merged_dict, expected_merged_dict)
1453
Daniel Norman276f0622019-07-26 14:13:51 -07001454 def test_GetAvbPartitionArg(self):
1455 info_dict = {}
1456 cmd = common.GetAvbPartitionArg('system', '/path/to/system.img', info_dict)
1457 self.assertEqual(
1458 ['--include_descriptors_from_image', '/path/to/system.img'], cmd)
1459
1460 @test_utils.SkipIfExternalToolsUnavailable()
1461 def test_AppendVBMetaArgsForPartition_vendorAsChainedPartition(self):
1462 testdata_dir = test_utils.get_testdata_dir()
1463 pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
1464 info_dict = {
1465 'avb_avbtool': 'avbtool',
1466 'avb_vendor_key_path': pubkey,
1467 'avb_vendor_rollback_index_location': 5,
1468 }
1469 cmd = common.GetAvbPartitionArg('vendor', '/path/to/vendor.img', info_dict)
1470 self.assertEqual(2, len(cmd))
1471 self.assertEqual('--chain_partition', cmd[0])
1472 chained_partition_args = cmd[1].split(':')
1473 self.assertEqual(3, len(chained_partition_args))
1474 self.assertEqual('vendor', chained_partition_args[0])
1475 self.assertEqual('5', chained_partition_args[1])
1476 self.assertTrue(os.path.exists(chained_partition_args[2]))
1477
Tao Bao3612c882019-10-14 17:49:31 -07001478 @test_utils.SkipIfExternalToolsUnavailable()
1479 def test_AppendVBMetaArgsForPartition_recoveryAsChainedPartition_nonAb(self):
1480 testdata_dir = test_utils.get_testdata_dir()
1481 pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
1482 info_dict = {
1483 'avb_avbtool': 'avbtool',
1484 'avb_recovery_key_path': pubkey,
1485 'avb_recovery_rollback_index_location': 3,
1486 }
1487 cmd = common.GetAvbPartitionArg(
1488 'recovery', '/path/to/recovery.img', info_dict)
1489 self.assertFalse(cmd)
1490
1491 @test_utils.SkipIfExternalToolsUnavailable()
1492 def test_AppendVBMetaArgsForPartition_recoveryAsChainedPartition_ab(self):
1493 testdata_dir = test_utils.get_testdata_dir()
1494 pubkey = os.path.join(testdata_dir, 'testkey.pubkey.pem')
1495 info_dict = {
1496 'ab_update': 'true',
1497 'avb_avbtool': 'avbtool',
1498 'avb_recovery_key_path': pubkey,
1499 'avb_recovery_rollback_index_location': 3,
1500 }
1501 cmd = common.GetAvbPartitionArg(
1502 'recovery', '/path/to/recovery.img', info_dict)
1503 self.assertEqual(2, len(cmd))
1504 self.assertEqual('--chain_partition', cmd[0])
1505 chained_partition_args = cmd[1].split(':')
1506 self.assertEqual(3, len(chained_partition_args))
1507 self.assertEqual('recovery', chained_partition_args[0])
1508 self.assertEqual('3', chained_partition_args[1])
1509 self.assertTrue(os.path.exists(chained_partition_args[2]))
1510
Tianjie20dd8f22020-04-19 15:51:16 -07001511 def test_BuildVBMeta_appendAftlCommandSyntax(self):
1512 testdata_dir = test_utils.get_testdata_dir()
1513 common.OPTIONS.info_dict = {
1514 'ab_update': 'true',
1515 'avb_avbtool': 'avbtool',
Greg Kaiser60225452020-05-09 00:30:33 +00001516 'build.prop': {
1517 'ro.build.version.incremental': '6285659',
1518 'ro.product.device': 'coral',
1519 'ro.build.fingerprint': 'google/coral/coral:R/RP1A.200311.002/'
1520 '6285659:userdebug/dev-keys'
1521 }
Tianjie20dd8f22020-04-19 15:51:16 -07001522 }
1523 common.OPTIONS.aftl_tool_path = 'aftltool'
1524 common.OPTIONS.aftl_server = 'log.endpoints.aftl-dev.cloud.goog:9000'
1525 common.OPTIONS.aftl_key_path = os.path.join(testdata_dir,
1526 'test_transparency_key.pub')
1527 common.OPTIONS.aftl_manufacturer_key_path = os.path.join(
1528 testdata_dir, 'test_aftl_rsa4096.pem')
1529
1530 vbmeta_image = tempfile.NamedTemporaryFile(delete=False)
1531 cmd = common.ConstructAftlMakeImageCommands(vbmeta_image.name)
1532 expected_cmd = [
1533 'aftltool', 'make_icp_from_vbmeta',
1534 '--vbmeta_image_path', 'place_holder',
1535 '--output', vbmeta_image.name,
1536 '--version_incremental', '6285659',
1537 '--transparency_log_servers',
1538 'log.endpoints.aftl-dev.cloud.goog:9000,{}'.format(
1539 common.OPTIONS.aftl_key_path),
1540 '--manufacturer_key', common.OPTIONS.aftl_manufacturer_key_path,
1541 '--algorithm', 'SHA256_RSA4096',
1542 '--padding', '4096']
1543
1544 # ignore the place holder, i.e. path to a temp file
1545 self.assertEqual(cmd[:3], expected_cmd[:3])
1546 self.assertEqual(cmd[4:], expected_cmd[4:])
1547
1548 @unittest.skip("enable after we have a server for public")
1549 def test_BuildVBMeta_appendAftlContactServer(self):
Tianjie Xueaed60c2020-03-12 00:33:28 -07001550 testdata_dir = test_utils.get_testdata_dir()
1551 common.OPTIONS.info_dict = {
1552 'ab_update': 'true',
1553 'avb_avbtool': 'avbtool',
Greg Kaiser60225452020-05-09 00:30:33 +00001554 'build.prop': {
1555 'ro.build.version.incremental': '6285659',
1556 'ro.product.device': 'coral',
1557 'ro.build.fingerprint': 'google/coral/coral:R/RP1A.200311.002/'
1558 '6285659:userdebug/dev-keys'
1559 }
Tianjie Xueaed60c2020-03-12 00:33:28 -07001560 }
Tianjie0f307452020-04-01 12:20:21 -07001561 common.OPTIONS.aftl_tool_path = "aftltool"
Tianjie Xueaed60c2020-03-12 00:33:28 -07001562 common.OPTIONS.aftl_server = "log.endpoints.aftl-dev.cloud.goog:9000"
1563 common.OPTIONS.aftl_key_path = os.path.join(testdata_dir,
1564 'test_transparency_key.pub')
1565 common.OPTIONS.aftl_manufacturer_key_path = os.path.join(
1566 testdata_dir, 'test_aftl_rsa4096.pem')
1567
1568 input_dir = common.MakeTempDir()
1569 system_image = common.MakeTempFile()
1570 build_image_cmd = ['mkuserimg_mke2fs', input_dir, system_image, 'ext4',
1571 '/system', str(4096 * 100), '-j', '0', '-s']
1572 common.RunAndCheckOutput(build_image_cmd)
1573
1574 add_footer_cmd = ['avbtool', 'add_hashtree_footer',
1575 '--partition_size', str(4096 * 150),
1576 '--partition_name', 'system',
1577 '--image', system_image]
1578 common.RunAndCheckOutput(add_footer_cmd)
1579
1580 vbmeta_image = common.MakeTempFile()
1581 common.BuildVBMeta(vbmeta_image, {'system': system_image}, 'vbmeta',
1582 ['system'])
1583
1584 verify_cmd = ['aftltool', 'verify_image_icp', '--vbmeta_image_path',
1585 vbmeta_image, '--transparency_log_pub_keys',
1586 common.OPTIONS.aftl_key_path]
1587 common.RunAndCheckOutput(verify_cmd)
1588
Tao Baofc7e0e02018-02-13 13:54:02 -08001589
Tao Bao65b94e92018-10-11 21:57:26 -07001590class InstallRecoveryScriptFormatTest(test_utils.ReleaseToolsTestCase):
Tao Bao1c830bf2017-12-25 10:43:47 -08001591 """Checks the format of install-recovery.sh.
Tianjie Xu9c384d22017-06-20 17:00:55 -07001592
Tao Bao1c830bf2017-12-25 10:43:47 -08001593 Its format should match between common.py and validate_target_files.py.
1594 """
Tianjie Xu9c384d22017-06-20 17:00:55 -07001595
1596 def setUp(self):
Tao Bao1c830bf2017-12-25 10:43:47 -08001597 self._tempdir = common.MakeTempDir()
Tianjie Xu9c384d22017-06-20 17:00:55 -07001598 # Create a dummy dict that contains the fstab info for boot&recovery.
1599 self._info = {"fstab" : {}}
Tao Bao1c830bf2017-12-25 10:43:47 -08001600 dummy_fstab = [
1601 "/dev/soc.0/by-name/boot /boot emmc defaults defaults",
1602 "/dev/soc.0/by-name/recovery /recovery emmc defaults defaults"]
Tao Bao31b08072017-11-08 15:50:59 -08001603 self._info["fstab"] = common.LoadRecoveryFSTab("\n".join, 2, dummy_fstab)
Tianjie Xudf055582017-11-07 12:22:58 -08001604 # Construct the gzipped recovery.img and boot.img
1605 self.recovery_data = bytearray([
1606 0x1f, 0x8b, 0x08, 0x00, 0x81, 0x11, 0x02, 0x5a, 0x00, 0x03, 0x2b, 0x4a,
1607 0x4d, 0xce, 0x2f, 0x4b, 0x2d, 0xaa, 0x04, 0x00, 0xc9, 0x93, 0x43, 0xf3,
1608 0x08, 0x00, 0x00, 0x00
1609 ])
1610 # echo -n "boot" | gzip -f | hd
1611 self.boot_data = bytearray([
1612 0x1f, 0x8b, 0x08, 0x00, 0x8c, 0x12, 0x02, 0x5a, 0x00, 0x03, 0x4b, 0xca,
1613 0xcf, 0x2f, 0x01, 0x00, 0xc4, 0xae, 0xed, 0x46, 0x04, 0x00, 0x00, 0x00
1614 ])
Tianjie Xu9c384d22017-06-20 17:00:55 -07001615
1616 def _out_tmp_sink(self, name, data, prefix="SYSTEM"):
1617 loc = os.path.join(self._tempdir, prefix, name)
1618 if not os.path.exists(os.path.dirname(loc)):
1619 os.makedirs(os.path.dirname(loc))
Tao Baoda30cfa2017-12-01 16:19:46 -08001620 with open(loc, "wb") as f:
Tianjie Xu9c384d22017-06-20 17:00:55 -07001621 f.write(data)
1622
1623 def test_full_recovery(self):
Tao Bao31b08072017-11-08 15:50:59 -08001624 recovery_image = common.File("recovery.img", self.recovery_data)
1625 boot_image = common.File("boot.img", self.boot_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001626 self._info["full_recovery_image"] = "true"
1627
1628 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1629 recovery_image, boot_image, self._info)
1630 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1631 self._info)
1632
Tao Bao82490d32019-04-09 00:12:30 -07001633 @test_utils.SkipIfExternalToolsUnavailable()
Tianjie Xu9c384d22017-06-20 17:00:55 -07001634 def test_recovery_from_boot(self):
Tao Bao31b08072017-11-08 15:50:59 -08001635 recovery_image = common.File("recovery.img", self.recovery_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001636 self._out_tmp_sink("recovery.img", recovery_image.data, "IMAGES")
Tao Bao31b08072017-11-08 15:50:59 -08001637 boot_image = common.File("boot.img", self.boot_data)
Tianjie Xu9c384d22017-06-20 17:00:55 -07001638 self._out_tmp_sink("boot.img", boot_image.data, "IMAGES")
1639
1640 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1641 recovery_image, boot_image, self._info)
1642 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1643 self._info)
1644 # Validate 'recovery-from-boot' with bonus argument.
Tao Baoda30cfa2017-12-01 16:19:46 -08001645 self._out_tmp_sink("etc/recovery-resource.dat", b"bonus", "SYSTEM")
Tianjie Xu9c384d22017-06-20 17:00:55 -07001646 common.MakeRecoveryPatch(self._tempdir, self._out_tmp_sink,
1647 recovery_image, boot_image, self._info)
1648 validate_target_files.ValidateInstallRecoveryScript(self._tempdir,
1649 self._info)
Yifan Hong45433e42019-01-18 13:55:25 -08001650
1651
Yifan Hong45433e42019-01-18 13:55:25 -08001652class MockBlockDifference(object):
Tao Baoda30cfa2017-12-01 16:19:46 -08001653
Yifan Hong45433e42019-01-18 13:55:25 -08001654 def __init__(self, partition, tgt, src=None):
1655 self.partition = partition
1656 self.tgt = tgt
1657 self.src = src
Tao Baoda30cfa2017-12-01 16:19:46 -08001658
Yifan Hong45433e42019-01-18 13:55:25 -08001659 def WriteScript(self, script, _, progress=None,
1660 write_verify_script=False):
1661 if progress:
1662 script.AppendExtra("progress({})".format(progress))
1663 script.AppendExtra("patch({});".format(self.partition))
1664 if write_verify_script:
1665 self.WritePostInstallVerifyScript(script)
Tao Baoda30cfa2017-12-01 16:19:46 -08001666
Yifan Hong45433e42019-01-18 13:55:25 -08001667 def WritePostInstallVerifyScript(self, script):
1668 script.AppendExtra("verify({});".format(self.partition))
1669
1670
1671class FakeSparseImage(object):
Tao Baoda30cfa2017-12-01 16:19:46 -08001672
Yifan Hong45433e42019-01-18 13:55:25 -08001673 def __init__(self, size):
1674 self.blocksize = 4096
1675 self.total_blocks = size // 4096
1676 assert size % 4096 == 0, "{} is not a multiple of 4096".format(size)
1677
1678
1679class DynamicPartitionsDifferenceTest(test_utils.ReleaseToolsTestCase):
Tao Baoda30cfa2017-12-01 16:19:46 -08001680
Yifan Hong45433e42019-01-18 13:55:25 -08001681 @staticmethod
1682 def get_op_list(output_path):
Tao Baof1113e92019-06-18 12:10:14 -07001683 with zipfile.ZipFile(output_path) as output_zip:
Tao Baoda30cfa2017-12-01 16:19:46 -08001684 with output_zip.open('dynamic_partitions_op_list') as op_list:
1685 return [line.decode().strip() for line in op_list.readlines()
1686 if not line.startswith(b'#')]
Yifan Hong45433e42019-01-18 13:55:25 -08001687
1688 def setUp(self):
Tao Baoe1148042019-10-07 20:00:34 -07001689 self.script = test_utils.MockScriptWriter()
Yifan Hong45433e42019-01-18 13:55:25 -08001690 self.output_path = common.MakeTempFile(suffix='.zip')
1691
1692 def test_full(self):
1693 target_info = common.LoadDictionaryFromLines("""
1694dynamic_partition_list=system vendor
1695super_partition_groups=group_foo
1696super_group_foo_group_size={group_size}
1697super_group_foo_partition_list=system vendor
1698""".format(group_size=4 * GiB).split("\n"))
1699 block_diffs = [MockBlockDifference("system", FakeSparseImage(3 * GiB)),
1700 MockBlockDifference("vendor", FakeSparseImage(1 * GiB))]
1701
1702 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs)
1703 with zipfile.ZipFile(self.output_path, 'w') as output_zip:
1704 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1705
1706 self.assertEqual(str(self.script).strip(), """
1707assert(update_dynamic_partitions(package_extract_file("dynamic_partitions_op_list")));
Yifan Hong45433e42019-01-18 13:55:25 -08001708patch(system);
1709verify(system);
1710unmap_partition("system");
Tao Baof1113e92019-06-18 12:10:14 -07001711patch(vendor);
1712verify(vendor);
1713unmap_partition("vendor");
Yifan Hong45433e42019-01-18 13:55:25 -08001714""".strip())
1715
1716 lines = self.get_op_list(self.output_path)
1717
1718 remove_all_groups = lines.index("remove_all_groups")
1719 add_group = lines.index("add_group group_foo 4294967296")
1720 add_vendor = lines.index("add vendor group_foo")
1721 add_system = lines.index("add system group_foo")
1722 resize_vendor = lines.index("resize vendor 1073741824")
1723 resize_system = lines.index("resize system 3221225472")
1724
1725 self.assertLess(remove_all_groups, add_group,
1726 "Should add groups after removing all groups")
1727 self.assertLess(add_group, min(add_vendor, add_system),
1728 "Should add partitions after adding group")
1729 self.assertLess(add_system, resize_system,
1730 "Should resize system after adding it")
1731 self.assertLess(add_vendor, resize_vendor,
1732 "Should resize vendor after adding it")
1733
1734 def test_inc_groups(self):
1735 source_info = common.LoadDictionaryFromLines("""
1736super_partition_groups=group_foo group_bar group_baz
1737super_group_foo_group_size={group_foo_size}
1738super_group_bar_group_size={group_bar_size}
1739""".format(group_foo_size=4 * GiB, group_bar_size=3 * GiB).split("\n"))
1740 target_info = common.LoadDictionaryFromLines("""
1741super_partition_groups=group_foo group_baz group_qux
1742super_group_foo_group_size={group_foo_size}
1743super_group_baz_group_size={group_baz_size}
1744super_group_qux_group_size={group_qux_size}
1745""".format(group_foo_size=3 * GiB, group_baz_size=4 * GiB,
1746 group_qux_size=1 * GiB).split("\n"))
1747
1748 dp_diff = common.DynamicPartitionsDifference(target_info,
1749 block_diffs=[],
1750 source_info_dict=source_info)
1751 with zipfile.ZipFile(self.output_path, 'w') as output_zip:
1752 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1753
1754 lines = self.get_op_list(self.output_path)
1755
1756 removed = lines.index("remove_group group_bar")
1757 shrunk = lines.index("resize_group group_foo 3221225472")
1758 grown = lines.index("resize_group group_baz 4294967296")
1759 added = lines.index("add_group group_qux 1073741824")
1760
Tao Baof1113e92019-06-18 12:10:14 -07001761 self.assertLess(max(removed, shrunk),
1762 min(grown, added),
Yifan Hong45433e42019-01-18 13:55:25 -08001763 "ops that remove / shrink partitions must precede ops that "
1764 "grow / add partitions")
1765
Yifan Hongbb2658d2019-01-25 12:30:58 -08001766 def test_incremental(self):
Yifan Hong45433e42019-01-18 13:55:25 -08001767 source_info = common.LoadDictionaryFromLines("""
Justin Yun6151e3f2019-06-25 15:58:13 +09001768dynamic_partition_list=system vendor product system_ext
Yifan Hong45433e42019-01-18 13:55:25 -08001769super_partition_groups=group_foo
1770super_group_foo_group_size={group_foo_size}
Justin Yun6151e3f2019-06-25 15:58:13 +09001771super_group_foo_partition_list=system vendor product system_ext
Yifan Hong45433e42019-01-18 13:55:25 -08001772""".format(group_foo_size=4 * GiB).split("\n"))
1773 target_info = common.LoadDictionaryFromLines("""
1774dynamic_partition_list=system vendor product odm
1775super_partition_groups=group_foo group_bar
1776super_group_foo_group_size={group_foo_size}
1777super_group_foo_partition_list=system vendor odm
1778super_group_bar_group_size={group_bar_size}
1779super_group_bar_partition_list=product
1780""".format(group_foo_size=3 * GiB, group_bar_size=1 * GiB).split("\n"))
1781
1782 block_diffs = [MockBlockDifference("system", FakeSparseImage(1536 * MiB),
1783 src=FakeSparseImage(1024 * MiB)),
1784 MockBlockDifference("vendor", FakeSparseImage(512 * MiB),
1785 src=FakeSparseImage(1024 * MiB)),
1786 MockBlockDifference("product", FakeSparseImage(1024 * MiB),
1787 src=FakeSparseImage(1024 * MiB)),
Justin Yun6151e3f2019-06-25 15:58:13 +09001788 MockBlockDifference("system_ext", None,
Yifan Hong45433e42019-01-18 13:55:25 -08001789 src=FakeSparseImage(1024 * MiB)),
1790 MockBlockDifference("odm", FakeSparseImage(1024 * MiB),
1791 src=None)]
1792
1793 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs,
1794 source_info_dict=source_info)
1795 with zipfile.ZipFile(self.output_path, 'w') as output_zip:
1796 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1797
1798 metadata_idx = self.script.lines.index(
1799 'assert(update_dynamic_partitions(package_extract_file('
1800 '"dynamic_partitions_op_list")));')
1801 self.assertLess(self.script.lines.index('patch(vendor);'), metadata_idx)
1802 self.assertLess(metadata_idx, self.script.lines.index('verify(vendor);'))
1803 for p in ("product", "system", "odm"):
1804 patch_idx = self.script.lines.index("patch({});".format(p))
1805 verify_idx = self.script.lines.index("verify({});".format(p))
1806 self.assertLess(metadata_idx, patch_idx,
1807 "Should patch {} after updating metadata".format(p))
1808 self.assertLess(patch_idx, verify_idx,
1809 "Should verify {} after patching".format(p))
1810
Justin Yun6151e3f2019-06-25 15:58:13 +09001811 self.assertNotIn("patch(system_ext);", self.script.lines)
Yifan Hong45433e42019-01-18 13:55:25 -08001812
1813 lines = self.get_op_list(self.output_path)
1814
Justin Yun6151e3f2019-06-25 15:58:13 +09001815 remove = lines.index("remove system_ext")
Yifan Hong45433e42019-01-18 13:55:25 -08001816 move_product_out = lines.index("move product default")
1817 shrink = lines.index("resize vendor 536870912")
1818 shrink_group = lines.index("resize_group group_foo 3221225472")
1819 add_group_bar = lines.index("add_group group_bar 1073741824")
1820 add_odm = lines.index("add odm group_foo")
1821 grow_existing = lines.index("resize system 1610612736")
1822 grow_added = lines.index("resize odm 1073741824")
1823 move_product_in = lines.index("move product group_bar")
1824
1825 max_idx_move_partition_out_foo = max(remove, move_product_out, shrink)
1826 min_idx_move_partition_in_foo = min(add_odm, grow_existing, grow_added)
1827
1828 self.assertLess(max_idx_move_partition_out_foo, shrink_group,
1829 "Must shrink group after partitions inside group are shrunk"
1830 " / removed")
1831
1832 self.assertLess(add_group_bar, move_product_in,
1833 "Must add partitions to group after group is added")
1834
1835 self.assertLess(max_idx_move_partition_out_foo,
1836 min_idx_move_partition_in_foo,
1837 "Must shrink partitions / remove partitions from group"
1838 "before adding / moving partitions into group")
Yifan Hongbb2658d2019-01-25 12:30:58 -08001839
1840 def test_remove_partition(self):
1841 source_info = common.LoadDictionaryFromLines("""
1842blockimgdiff_versions=3,4
1843use_dynamic_partitions=true
1844dynamic_partition_list=foo
1845super_partition_groups=group_foo
1846super_group_foo_group_size={group_foo_size}
1847super_group_foo_partition_list=foo
1848""".format(group_foo_size=4 * GiB).split("\n"))
1849 target_info = common.LoadDictionaryFromLines("""
1850blockimgdiff_versions=3,4
1851use_dynamic_partitions=true
1852super_partition_groups=group_foo
1853super_group_foo_group_size={group_foo_size}
1854""".format(group_foo_size=4 * GiB).split("\n"))
1855
1856 common.OPTIONS.info_dict = target_info
1857 common.OPTIONS.target_info_dict = target_info
1858 common.OPTIONS.source_info_dict = source_info
1859 common.OPTIONS.cache_size = 4 * 4096
1860
1861 block_diffs = [common.BlockDifference("foo", EmptyImage(),
1862 src=DataImage("source", pad=True))]
1863
1864 dp_diff = common.DynamicPartitionsDifference(target_info, block_diffs,
1865 source_info_dict=source_info)
1866 with zipfile.ZipFile(self.output_path, 'w') as output_zip:
1867 dp_diff.WriteScript(self.script, output_zip, write_verify_script=True)
1868
1869 self.assertNotIn("block_image_update", str(self.script),
Tao Bao2cc0ca12019-03-15 10:44:43 -07001870 "Removed partition should not be patched.")
Yifan Hongbb2658d2019-01-25 12:30:58 -08001871
1872 lines = self.get_op_list(self.output_path)
1873 self.assertEqual(lines, ["remove foo"])