blob: ee5bc53351d187673f76eb92ac9db83195a16a4c [file] [log] [blame]
Tao Bao481bab82017-12-21 11:23:09 -08001#
2# Copyright (C) 2018 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#
16
17import copy
Tao Baoc7b403a2018-01-30 18:19:04 -080018import os
Tao Baofabe0832018-01-17 15:52:28 -080019import os.path
Tao Bao481bab82017-12-21 11:23:09 -080020import unittest
Tao Baoc7b403a2018-01-30 18:19:04 -080021import zipfile
Tao Bao481bab82017-12-21 11:23:09 -080022
23import common
Tao Bao04e1f012018-02-04 12:13:35 -080024import test_utils
Tao Bao481bab82017-12-21 11:23:09 -080025from ota_from_target_files import (
Tao Baof7140c02018-01-30 17:09:24 -080026 _LoadOemDicts, BuildInfo, GetPackageMetadata,
Tao Bao15a146a2018-02-21 16:06:59 -080027 GetTargetFilesZipForSecondaryImages,
28 GetTargetFilesZipWithoutPostinstallConfig,
29 Payload, PayloadSigner, POSTINSTALL_CONFIG,
Tao Baofabe0832018-01-17 15:52:28 -080030 WriteFingerprintAssertion)
31
32
Tao Baof7140c02018-01-30 17:09:24 -080033def construct_target_files(secondary=False):
34 """Returns a target-files.zip file for generating OTA packages."""
35 target_files = common.MakeTempFile(prefix='target_files-', suffix='.zip')
36 with zipfile.ZipFile(target_files, 'w') as target_files_zip:
37 # META/update_engine_config.txt
38 target_files_zip.writestr(
39 'META/update_engine_config.txt',
40 "PAYLOAD_MAJOR_VERSION=2\nPAYLOAD_MINOR_VERSION=4\n")
41
Tao Bao15a146a2018-02-21 16:06:59 -080042 # META/postinstall_config.txt
43 target_files_zip.writestr(
44 POSTINSTALL_CONFIG,
45 '\n'.join([
46 "RUN_POSTINSTALL_system=true",
47 "POSTINSTALL_PATH_system=system/bin/otapreopt_script",
48 "FILESYSTEM_TYPE_system=ext4",
49 "POSTINSTALL_OPTIONAL_system=true",
50 ]))
51
Tao Baof7140c02018-01-30 17:09:24 -080052 # META/ab_partitions.txt
53 ab_partitions = ['boot', 'system', 'vendor']
54 target_files_zip.writestr(
55 'META/ab_partitions.txt',
56 '\n'.join(ab_partitions))
57
58 # Create dummy images for each of them.
59 for partition in ab_partitions:
60 target_files_zip.writestr('IMAGES/' + partition + '.img',
61 os.urandom(len(partition)))
62
63 if secondary:
64 target_files_zip.writestr('IMAGES/system_other.img',
65 os.urandom(len("system_other")))
66
67 return target_files
68
69
Tao Bao481bab82017-12-21 11:23:09 -080070class MockScriptWriter(object):
71 """A class that mocks edify_generator.EdifyGenerator.
72
73 It simply pushes the incoming arguments onto script stack, which is to assert
74 the calls to EdifyGenerator functions.
75 """
76
77 def __init__(self):
78 self.script = []
79
80 def Mount(self, *args):
81 self.script.append(('Mount',) + args)
82
83 def AssertDevice(self, *args):
84 self.script.append(('AssertDevice',) + args)
85
86 def AssertOemProperty(self, *args):
87 self.script.append(('AssertOemProperty',) + args)
88
89 def AssertFingerprintOrThumbprint(self, *args):
90 self.script.append(('AssertFingerprintOrThumbprint',) + args)
91
92 def AssertSomeFingerprint(self, *args):
93 self.script.append(('AssertSomeFingerprint',) + args)
94
95 def AssertSomeThumbprint(self, *args):
96 self.script.append(('AssertSomeThumbprint',) + args)
97
98
99class BuildInfoTest(unittest.TestCase):
100
101 TEST_INFO_DICT = {
102 'build.prop' : {
103 'ro.product.device' : 'product-device',
104 'ro.product.name' : 'product-name',
105 'ro.build.fingerprint' : 'build-fingerprint',
106 'ro.build.foo' : 'build-foo',
107 },
108 'vendor.build.prop' : {
109 'ro.vendor.build.fingerprint' : 'vendor-build-fingerprint',
110 },
111 'property1' : 'value1',
112 'property2' : 4096,
113 }
114
115 TEST_INFO_DICT_USES_OEM_PROPS = {
116 'build.prop' : {
117 'ro.product.name' : 'product-name',
118 'ro.build.thumbprint' : 'build-thumbprint',
119 'ro.build.bar' : 'build-bar',
120 },
121 'vendor.build.prop' : {
122 'ro.vendor.build.fingerprint' : 'vendor-build-fingerprint',
123 },
124 'property1' : 'value1',
125 'property2' : 4096,
126 'oem_fingerprint_properties' : 'ro.product.device ro.product.brand',
127 }
128
129 TEST_OEM_DICTS = [
130 {
131 'ro.product.brand' : 'brand1',
132 'ro.product.device' : 'device1',
133 },
134 {
135 'ro.product.brand' : 'brand2',
136 'ro.product.device' : 'device2',
137 },
138 {
139 'ro.product.brand' : 'brand3',
140 'ro.product.device' : 'device3',
141 },
142 ]
143
144 def test_init(self):
145 target_info = BuildInfo(self.TEST_INFO_DICT, None)
146 self.assertEqual('product-device', target_info.device)
147 self.assertEqual('build-fingerprint', target_info.fingerprint)
148 self.assertFalse(target_info.is_ab)
149 self.assertIsNone(target_info.oem_props)
150
151 def test_init_with_oem_props(self):
152 target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
153 self.TEST_OEM_DICTS)
154 self.assertEqual('device1', target_info.device)
155 self.assertEqual('brand1/product-name/device1:build-thumbprint',
156 target_info.fingerprint)
157
158 # Swap the order in oem_dicts, which would lead to different BuildInfo.
159 oem_dicts = copy.copy(self.TEST_OEM_DICTS)
160 oem_dicts[0], oem_dicts[2] = oem_dicts[2], oem_dicts[0]
161 target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS, oem_dicts)
162 self.assertEqual('device3', target_info.device)
163 self.assertEqual('brand3/product-name/device3:build-thumbprint',
164 target_info.fingerprint)
165
166 # Missing oem_dict should be rejected.
167 self.assertRaises(AssertionError, BuildInfo,
168 self.TEST_INFO_DICT_USES_OEM_PROPS, None)
169
170 def test___getitem__(self):
171 target_info = BuildInfo(self.TEST_INFO_DICT, None)
172 self.assertEqual('value1', target_info['property1'])
173 self.assertEqual(4096, target_info['property2'])
174 self.assertEqual('build-foo', target_info['build.prop']['ro.build.foo'])
175
176 def test___getitem__with_oem_props(self):
177 target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
178 self.TEST_OEM_DICTS)
179 self.assertEqual('value1', target_info['property1'])
180 self.assertEqual(4096, target_info['property2'])
181 self.assertRaises(KeyError,
182 lambda: target_info['build.prop']['ro.build.foo'])
183
184 def test_get(self):
185 target_info = BuildInfo(self.TEST_INFO_DICT, None)
186 self.assertEqual('value1', target_info.get('property1'))
187 self.assertEqual(4096, target_info.get('property2'))
188 self.assertEqual(4096, target_info.get('property2', 1024))
189 self.assertEqual(1024, target_info.get('property-nonexistent', 1024))
190 self.assertEqual('build-foo', target_info.get('build.prop')['ro.build.foo'])
191
192 def test_get_with_oem_props(self):
193 target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
194 self.TEST_OEM_DICTS)
195 self.assertEqual('value1', target_info.get('property1'))
196 self.assertEqual(4096, target_info.get('property2'))
197 self.assertEqual(4096, target_info.get('property2', 1024))
198 self.assertEqual(1024, target_info.get('property-nonexistent', 1024))
199 self.assertIsNone(target_info.get('build.prop').get('ro.build.foo'))
200 self.assertRaises(KeyError,
201 lambda: target_info.get('build.prop')['ro.build.foo'])
202
203 def test_GetBuildProp(self):
204 target_info = BuildInfo(self.TEST_INFO_DICT, None)
205 self.assertEqual('build-foo', target_info.GetBuildProp('ro.build.foo'))
206 self.assertRaises(common.ExternalError, target_info.GetBuildProp,
207 'ro.build.nonexistent')
208
209 def test_GetBuildProp_with_oem_props(self):
210 target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
211 self.TEST_OEM_DICTS)
212 self.assertEqual('build-bar', target_info.GetBuildProp('ro.build.bar'))
213 self.assertRaises(common.ExternalError, target_info.GetBuildProp,
214 'ro.build.nonexistent')
215
216 def test_GetVendorBuildProp(self):
217 target_info = BuildInfo(self.TEST_INFO_DICT, None)
218 self.assertEqual('vendor-build-fingerprint',
219 target_info.GetVendorBuildProp(
220 'ro.vendor.build.fingerprint'))
221 self.assertRaises(common.ExternalError, target_info.GetVendorBuildProp,
222 'ro.build.nonexistent')
223
224 def test_GetVendorBuildProp_with_oem_props(self):
225 target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
226 self.TEST_OEM_DICTS)
227 self.assertEqual('vendor-build-fingerprint',
228 target_info.GetVendorBuildProp(
229 'ro.vendor.build.fingerprint'))
230 self.assertRaises(common.ExternalError, target_info.GetVendorBuildProp,
231 'ro.build.nonexistent')
232
233 def test_WriteMountOemScript(self):
234 target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
235 self.TEST_OEM_DICTS)
236 script_writer = MockScriptWriter()
237 target_info.WriteMountOemScript(script_writer)
238 self.assertEqual([('Mount', '/oem', None)], script_writer.script)
239
240 def test_WriteDeviceAssertions(self):
241 target_info = BuildInfo(self.TEST_INFO_DICT, None)
242 script_writer = MockScriptWriter()
243 target_info.WriteDeviceAssertions(script_writer, False)
244 self.assertEqual([('AssertDevice', 'product-device')], script_writer.script)
245
246 def test_WriteDeviceAssertions_with_oem_props(self):
247 target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
248 self.TEST_OEM_DICTS)
249 script_writer = MockScriptWriter()
250 target_info.WriteDeviceAssertions(script_writer, False)
251 self.assertEqual(
252 [
253 ('AssertOemProperty', 'ro.product.device',
254 ['device1', 'device2', 'device3'], False),
255 ('AssertOemProperty', 'ro.product.brand',
256 ['brand1', 'brand2', 'brand3'], False),
257 ],
258 script_writer.script)
259
260 def test_WriteFingerprintAssertion_without_oem_props(self):
261 target_info = BuildInfo(self.TEST_INFO_DICT, None)
262 source_info_dict = copy.deepcopy(self.TEST_INFO_DICT)
263 source_info_dict['build.prop']['ro.build.fingerprint'] = (
264 'source-build-fingerprint')
265 source_info = BuildInfo(source_info_dict, None)
266
267 script_writer = MockScriptWriter()
268 WriteFingerprintAssertion(script_writer, target_info, source_info)
269 self.assertEqual(
270 [('AssertSomeFingerprint', 'source-build-fingerprint',
271 'build-fingerprint')],
272 script_writer.script)
273
274 def test_WriteFingerprintAssertion_with_source_oem_props(self):
275 target_info = BuildInfo(self.TEST_INFO_DICT, None)
276 source_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
277 self.TEST_OEM_DICTS)
278
279 script_writer = MockScriptWriter()
280 WriteFingerprintAssertion(script_writer, target_info, source_info)
281 self.assertEqual(
282 [('AssertFingerprintOrThumbprint', 'build-fingerprint',
283 'build-thumbprint')],
284 script_writer.script)
285
286 def test_WriteFingerprintAssertion_with_target_oem_props(self):
287 target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
288 self.TEST_OEM_DICTS)
289 source_info = BuildInfo(self.TEST_INFO_DICT, None)
290
291 script_writer = MockScriptWriter()
292 WriteFingerprintAssertion(script_writer, target_info, source_info)
293 self.assertEqual(
294 [('AssertFingerprintOrThumbprint', 'build-fingerprint',
295 'build-thumbprint')],
296 script_writer.script)
297
298 def test_WriteFingerprintAssertion_with_both_oem_props(self):
299 target_info = BuildInfo(self.TEST_INFO_DICT_USES_OEM_PROPS,
300 self.TEST_OEM_DICTS)
301 source_info_dict = copy.deepcopy(self.TEST_INFO_DICT_USES_OEM_PROPS)
302 source_info_dict['build.prop']['ro.build.thumbprint'] = (
303 'source-build-thumbprint')
304 source_info = BuildInfo(source_info_dict, self.TEST_OEM_DICTS)
305
306 script_writer = MockScriptWriter()
307 WriteFingerprintAssertion(script_writer, target_info, source_info)
308 self.assertEqual(
309 [('AssertSomeThumbprint', 'build-thumbprint',
310 'source-build-thumbprint')],
311 script_writer.script)
312
313
314class LoadOemDictsTest(unittest.TestCase):
315
316 def tearDown(self):
317 common.Cleanup()
318
319 def test_NoneDict(self):
320 self.assertIsNone(_LoadOemDicts(None))
321
322 def test_SingleDict(self):
323 dict_file = common.MakeTempFile()
324 with open(dict_file, 'w') as dict_fp:
325 dict_fp.write('abc=1\ndef=2\nxyz=foo\na.b.c=bar\n')
326
327 oem_dicts = _LoadOemDicts([dict_file])
328 self.assertEqual(1, len(oem_dicts))
329 self.assertEqual('foo', oem_dicts[0]['xyz'])
330 self.assertEqual('bar', oem_dicts[0]['a.b.c'])
331
332 def test_MultipleDicts(self):
333 oem_source = []
334 for i in range(3):
335 dict_file = common.MakeTempFile()
336 with open(dict_file, 'w') as dict_fp:
337 dict_fp.write(
338 'ro.build.index={}\ndef=2\nxyz=foo\na.b.c=bar\n'.format(i))
339 oem_source.append(dict_file)
340
341 oem_dicts = _LoadOemDicts(oem_source)
342 self.assertEqual(3, len(oem_dicts))
343 for i, oem_dict in enumerate(oem_dicts):
344 self.assertEqual('2', oem_dict['def'])
345 self.assertEqual('foo', oem_dict['xyz'])
346 self.assertEqual('bar', oem_dict['a.b.c'])
347 self.assertEqual('{}'.format(i), oem_dict['ro.build.index'])
Tao Baodf3a48b2018-01-10 16:30:43 -0800348
349
350class OtaFromTargetFilesTest(unittest.TestCase):
351
352 TEST_TARGET_INFO_DICT = {
353 'build.prop' : {
354 'ro.product.device' : 'product-device',
355 'ro.build.fingerprint' : 'build-fingerprint-target',
356 'ro.build.version.incremental' : 'build-version-incremental-target',
Tao Bao35dc2552018-02-01 13:18:00 -0800357 'ro.build.version.sdk' : '27',
358 'ro.build.version.security_patch' : '2017-12-01',
Tao Baodf3a48b2018-01-10 16:30:43 -0800359 'ro.build.date.utc' : '1500000000',
360 },
361 }
362
363 TEST_SOURCE_INFO_DICT = {
364 'build.prop' : {
365 'ro.product.device' : 'product-device',
366 'ro.build.fingerprint' : 'build-fingerprint-source',
367 'ro.build.version.incremental' : 'build-version-incremental-source',
Tao Bao35dc2552018-02-01 13:18:00 -0800368 'ro.build.version.sdk' : '25',
369 'ro.build.version.security_patch' : '2016-12-01',
Tao Baodf3a48b2018-01-10 16:30:43 -0800370 'ro.build.date.utc' : '1400000000',
371 },
372 }
373
374 def setUp(self):
375 # Reset the global options as in ota_from_target_files.py.
376 common.OPTIONS.incremental_source = None
377 common.OPTIONS.downgrade = False
378 common.OPTIONS.timestamp = False
379 common.OPTIONS.wipe_user_data = False
380
381 def test_GetPackageMetadata_abOta_full(self):
382 target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)
383 target_info_dict['ab_update'] = 'true'
384 target_info = BuildInfo(target_info_dict, None)
385 metadata = GetPackageMetadata(target_info)
386 self.assertDictEqual(
387 {
388 'ota-type' : 'AB',
389 'ota-required-cache' : '0',
390 'post-build' : 'build-fingerprint-target',
391 'post-build-incremental' : 'build-version-incremental-target',
Tao Bao35dc2552018-02-01 13:18:00 -0800392 'post-sdk-level' : '27',
393 'post-security-patch-level' : '2017-12-01',
Tao Baodf3a48b2018-01-10 16:30:43 -0800394 'post-timestamp' : '1500000000',
395 'pre-device' : 'product-device',
396 },
397 metadata)
398
399 def test_GetPackageMetadata_abOta_incremental(self):
400 target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)
401 target_info_dict['ab_update'] = 'true'
402 target_info = BuildInfo(target_info_dict, None)
403 source_info = BuildInfo(self.TEST_SOURCE_INFO_DICT, None)
404 common.OPTIONS.incremental_source = ''
405 metadata = GetPackageMetadata(target_info, source_info)
406 self.assertDictEqual(
407 {
408 'ota-type' : 'AB',
409 'ota-required-cache' : '0',
410 'post-build' : 'build-fingerprint-target',
411 'post-build-incremental' : 'build-version-incremental-target',
Tao Bao35dc2552018-02-01 13:18:00 -0800412 'post-sdk-level' : '27',
413 'post-security-patch-level' : '2017-12-01',
Tao Baodf3a48b2018-01-10 16:30:43 -0800414 'post-timestamp' : '1500000000',
415 'pre-device' : 'product-device',
416 'pre-build' : 'build-fingerprint-source',
417 'pre-build-incremental' : 'build-version-incremental-source',
418 },
419 metadata)
420
421 def test_GetPackageMetadata_nonAbOta_full(self):
422 target_info = BuildInfo(self.TEST_TARGET_INFO_DICT, None)
423 metadata = GetPackageMetadata(target_info)
424 self.assertDictEqual(
425 {
426 'ota-type' : 'BLOCK',
427 'post-build' : 'build-fingerprint-target',
428 'post-build-incremental' : 'build-version-incremental-target',
Tao Bao35dc2552018-02-01 13:18:00 -0800429 'post-sdk-level' : '27',
430 'post-security-patch-level' : '2017-12-01',
Tao Baodf3a48b2018-01-10 16:30:43 -0800431 'post-timestamp' : '1500000000',
432 'pre-device' : 'product-device',
433 },
434 metadata)
435
436 def test_GetPackageMetadata_nonAbOta_incremental(self):
437 target_info = BuildInfo(self.TEST_TARGET_INFO_DICT, None)
438 source_info = BuildInfo(self.TEST_SOURCE_INFO_DICT, None)
439 common.OPTIONS.incremental_source = ''
440 metadata = GetPackageMetadata(target_info, source_info)
441 self.assertDictEqual(
442 {
443 'ota-type' : 'BLOCK',
444 'post-build' : 'build-fingerprint-target',
445 'post-build-incremental' : 'build-version-incremental-target',
Tao Bao35dc2552018-02-01 13:18:00 -0800446 'post-sdk-level' : '27',
447 'post-security-patch-level' : '2017-12-01',
Tao Baodf3a48b2018-01-10 16:30:43 -0800448 'post-timestamp' : '1500000000',
449 'pre-device' : 'product-device',
450 'pre-build' : 'build-fingerprint-source',
451 'pre-build-incremental' : 'build-version-incremental-source',
452 },
453 metadata)
454
455 def test_GetPackageMetadata_wipe(self):
456 target_info = BuildInfo(self.TEST_TARGET_INFO_DICT, None)
457 common.OPTIONS.wipe_user_data = True
458 metadata = GetPackageMetadata(target_info)
459 self.assertDictEqual(
460 {
461 'ota-type' : 'BLOCK',
462 'ota-wipe' : 'yes',
463 'post-build' : 'build-fingerprint-target',
464 'post-build-incremental' : 'build-version-incremental-target',
Tao Bao35dc2552018-02-01 13:18:00 -0800465 'post-sdk-level' : '27',
466 'post-security-patch-level' : '2017-12-01',
Tao Baodf3a48b2018-01-10 16:30:43 -0800467 'post-timestamp' : '1500000000',
468 'pre-device' : 'product-device',
469 },
470 metadata)
471
472 @staticmethod
473 def _test_GetPackageMetadata_swapBuildTimestamps(target_info, source_info):
474 (target_info['build.prop']['ro.build.date.utc'],
475 source_info['build.prop']['ro.build.date.utc']) = (
476 source_info['build.prop']['ro.build.date.utc'],
477 target_info['build.prop']['ro.build.date.utc'])
478
479 def test_GetPackageMetadata_unintentionalDowngradeDetected(self):
480 target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)
481 source_info_dict = copy.deepcopy(self.TEST_SOURCE_INFO_DICT)
482 self._test_GetPackageMetadata_swapBuildTimestamps(
483 target_info_dict, source_info_dict)
484
485 target_info = BuildInfo(target_info_dict, None)
486 source_info = BuildInfo(source_info_dict, None)
487 common.OPTIONS.incremental_source = ''
488 self.assertRaises(RuntimeError, GetPackageMetadata, target_info,
489 source_info)
490
491 def test_GetPackageMetadata_downgrade(self):
492 target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)
493 source_info_dict = copy.deepcopy(self.TEST_SOURCE_INFO_DICT)
494 self._test_GetPackageMetadata_swapBuildTimestamps(
495 target_info_dict, source_info_dict)
496
497 target_info = BuildInfo(target_info_dict, None)
498 source_info = BuildInfo(source_info_dict, None)
499 common.OPTIONS.incremental_source = ''
500 common.OPTIONS.downgrade = True
501 common.OPTIONS.wipe_user_data = True
502 metadata = GetPackageMetadata(target_info, source_info)
503 self.assertDictEqual(
504 {
505 'ota-downgrade' : 'yes',
506 'ota-type' : 'BLOCK',
507 'ota-wipe' : 'yes',
508 'post-build' : 'build-fingerprint-target',
509 'post-build-incremental' : 'build-version-incremental-target',
Tao Bao35dc2552018-02-01 13:18:00 -0800510 'post-sdk-level' : '27',
511 'post-security-patch-level' : '2017-12-01',
Tao Baodf3a48b2018-01-10 16:30:43 -0800512 'pre-device' : 'product-device',
513 'pre-build' : 'build-fingerprint-source',
514 'pre-build-incremental' : 'build-version-incremental-source',
515 },
516 metadata)
517
518 def test_GetPackageMetadata_overrideTimestamp(self):
519 target_info_dict = copy.deepcopy(self.TEST_TARGET_INFO_DICT)
520 source_info_dict = copy.deepcopy(self.TEST_SOURCE_INFO_DICT)
521 self._test_GetPackageMetadata_swapBuildTimestamps(
522 target_info_dict, source_info_dict)
523
524 target_info = BuildInfo(target_info_dict, None)
525 source_info = BuildInfo(source_info_dict, None)
526 common.OPTIONS.incremental_source = ''
527 common.OPTIONS.timestamp = True
528 metadata = GetPackageMetadata(target_info, source_info)
529 self.assertDictEqual(
530 {
531 'ota-type' : 'BLOCK',
532 'post-build' : 'build-fingerprint-target',
533 'post-build-incremental' : 'build-version-incremental-target',
Tao Bao35dc2552018-02-01 13:18:00 -0800534 'post-sdk-level' : '27',
535 'post-security-patch-level' : '2017-12-01',
Tao Baodf3a48b2018-01-10 16:30:43 -0800536 'post-timestamp' : '1500000001',
537 'pre-device' : 'product-device',
538 'pre-build' : 'build-fingerprint-source',
539 'pre-build-incremental' : 'build-version-incremental-source',
540 },
541 metadata)
Tao Baofabe0832018-01-17 15:52:28 -0800542
Tao Baof7140c02018-01-30 17:09:24 -0800543 def test_GetTargetFilesZipForSecondaryImages(self):
544 input_file = construct_target_files(secondary=True)
545 target_file = GetTargetFilesZipForSecondaryImages(input_file)
546
547 with zipfile.ZipFile(target_file) as verify_zip:
548 namelist = verify_zip.namelist()
549
550 self.assertIn('META/ab_partitions.txt', namelist)
551 self.assertIn('IMAGES/boot.img', namelist)
552 self.assertIn('IMAGES/system.img', namelist)
553 self.assertIn('IMAGES/vendor.img', namelist)
Tao Bao15a146a2018-02-21 16:06:59 -0800554 self.assertIn(POSTINSTALL_CONFIG, namelist)
Tao Baof7140c02018-01-30 17:09:24 -0800555
556 self.assertNotIn('IMAGES/system_other.img', namelist)
557 self.assertNotIn('IMAGES/system.map', namelist)
558
Tao Bao15a146a2018-02-21 16:06:59 -0800559 def test_GetTargetFilesZipForSecondaryImages_skipPostinstall(self):
560 input_file = construct_target_files(secondary=True)
561 target_file = GetTargetFilesZipForSecondaryImages(
562 input_file, skip_postinstall=True)
563
564 with zipfile.ZipFile(target_file) as verify_zip:
565 namelist = verify_zip.namelist()
566
567 self.assertIn('META/ab_partitions.txt', namelist)
568 self.assertIn('IMAGES/boot.img', namelist)
569 self.assertIn('IMAGES/system.img', namelist)
570 self.assertIn('IMAGES/vendor.img', namelist)
571
572 self.assertNotIn('IMAGES/system_other.img', namelist)
573 self.assertNotIn('IMAGES/system.map', namelist)
574 self.assertNotIn(POSTINSTALL_CONFIG, namelist)
575
576 def test_GetTargetFilesZipWithoutPostinstallConfig(self):
577 input_file = construct_target_files()
578 target_file = GetTargetFilesZipWithoutPostinstallConfig(input_file)
579 with zipfile.ZipFile(target_file) as verify_zip:
580 self.assertNotIn(POSTINSTALL_CONFIG, verify_zip.namelist())
581
582 def test_GetTargetFilesZipWithoutPostinstallConfig_missingEntry(self):
583 input_file = construct_target_files()
584 common.ZipDelete(input_file, POSTINSTALL_CONFIG)
585 target_file = GetTargetFilesZipWithoutPostinstallConfig(input_file)
586 with zipfile.ZipFile(target_file) as verify_zip:
587 self.assertNotIn(POSTINSTALL_CONFIG, verify_zip.namelist())
588
Tao Baofabe0832018-01-17 15:52:28 -0800589
590class PayloadSignerTest(unittest.TestCase):
591
592 SIGFILE = 'sigfile.bin'
593 SIGNED_SIGFILE = 'signed-sigfile.bin'
594
595 def setUp(self):
Tao Bao04e1f012018-02-04 12:13:35 -0800596 self.testdata_dir = test_utils.get_testdata_dir()
Tao Baofabe0832018-01-17 15:52:28 -0800597 self.assertTrue(os.path.exists(self.testdata_dir))
598
599 common.OPTIONS.payload_signer = None
600 common.OPTIONS.payload_signer_args = []
601 common.OPTIONS.package_key = os.path.join(self.testdata_dir, 'testkey')
602 common.OPTIONS.key_passwords = {
603 common.OPTIONS.package_key : None,
604 }
605
606 def tearDown(self):
607 common.Cleanup()
608
609 def _assertFilesEqual(self, file1, file2):
610 with open(file1, 'rb') as fp1, open(file2, 'rb') as fp2:
611 self.assertEqual(fp1.read(), fp2.read())
612
613 def test_init(self):
614 payload_signer = PayloadSigner()
615 self.assertEqual('openssl', payload_signer.signer)
616
617 def test_init_withPassword(self):
618 common.OPTIONS.package_key = os.path.join(
619 self.testdata_dir, 'testkey_with_passwd')
620 common.OPTIONS.key_passwords = {
621 common.OPTIONS.package_key : 'foo',
622 }
623 payload_signer = PayloadSigner()
624 self.assertEqual('openssl', payload_signer.signer)
625
626 def test_init_withExternalSigner(self):
627 common.OPTIONS.payload_signer = 'abc'
628 common.OPTIONS.payload_signer_args = ['arg1', 'arg2']
629 payload_signer = PayloadSigner()
630 self.assertEqual('abc', payload_signer.signer)
631 self.assertEqual(['arg1', 'arg2'], payload_signer.signer_args)
632
633 def test_Sign(self):
634 payload_signer = PayloadSigner()
635 input_file = os.path.join(self.testdata_dir, self.SIGFILE)
636 signed_file = payload_signer.Sign(input_file)
637
638 verify_file = os.path.join(self.testdata_dir, self.SIGNED_SIGFILE)
639 self._assertFilesEqual(verify_file, signed_file)
640
641 def test_Sign_withExternalSigner_openssl(self):
642 """Uses openssl as the external payload signer."""
643 common.OPTIONS.payload_signer = 'openssl'
644 common.OPTIONS.payload_signer_args = [
645 'pkeyutl', '-sign', '-keyform', 'DER', '-inkey',
646 os.path.join(self.testdata_dir, 'testkey.pk8'),
647 '-pkeyopt', 'digest:sha256']
648 payload_signer = PayloadSigner()
649 input_file = os.path.join(self.testdata_dir, self.SIGFILE)
650 signed_file = payload_signer.Sign(input_file)
651
652 verify_file = os.path.join(self.testdata_dir, self.SIGNED_SIGFILE)
653 self._assertFilesEqual(verify_file, signed_file)
654
655 def test_Sign_withExternalSigner_script(self):
656 """Uses testdata/payload_signer.sh as the external payload signer."""
657 common.OPTIONS.payload_signer = os.path.join(
658 self.testdata_dir, 'payload_signer.sh')
659 common.OPTIONS.payload_signer_args = [
660 os.path.join(self.testdata_dir, 'testkey.pk8')]
661 payload_signer = PayloadSigner()
662 input_file = os.path.join(self.testdata_dir, self.SIGFILE)
663 signed_file = payload_signer.Sign(input_file)
664
665 verify_file = os.path.join(self.testdata_dir, self.SIGNED_SIGFILE)
666 self._assertFilesEqual(verify_file, signed_file)
Tao Baoc7b403a2018-01-30 18:19:04 -0800667
668
669class PayloadTest(unittest.TestCase):
670
671 def setUp(self):
Tao Bao04e1f012018-02-04 12:13:35 -0800672 self.testdata_dir = test_utils.get_testdata_dir()
Tao Baoc7b403a2018-01-30 18:19:04 -0800673 self.assertTrue(os.path.exists(self.testdata_dir))
674
675 common.OPTIONS.wipe_user_data = False
676 common.OPTIONS.payload_signer = None
677 common.OPTIONS.payload_signer_args = None
678 common.OPTIONS.package_key = os.path.join(self.testdata_dir, 'testkey')
679 common.OPTIONS.key_passwords = {
680 common.OPTIONS.package_key : None,
681 }
682
683 def tearDown(self):
684 common.Cleanup()
685
686 @staticmethod
Tao Baof7140c02018-01-30 17:09:24 -0800687 def _create_payload_full(secondary=False):
688 target_file = construct_target_files(secondary)
Tao Bao667ff572018-02-10 00:02:40 -0800689 payload = Payload(secondary)
Tao Baoc7b403a2018-01-30 18:19:04 -0800690 payload.Generate(target_file)
691 return payload
692
Tao Baof7140c02018-01-30 17:09:24 -0800693 @staticmethod
694 def _create_payload_incremental():
695 target_file = construct_target_files()
696 source_file = construct_target_files()
Tao Baoc7b403a2018-01-30 18:19:04 -0800697 payload = Payload()
698 payload.Generate(target_file, source_file)
699 return payload
700
701 def test_Generate_full(self):
702 payload = self._create_payload_full()
703 self.assertTrue(os.path.exists(payload.payload_file))
704
705 def test_Generate_incremental(self):
706 payload = self._create_payload_incremental()
707 self.assertTrue(os.path.exists(payload.payload_file))
708
709 def test_Generate_additionalArgs(self):
Tao Baof7140c02018-01-30 17:09:24 -0800710 target_file = construct_target_files()
711 source_file = construct_target_files()
Tao Baoc7b403a2018-01-30 18:19:04 -0800712 payload = Payload()
713 # This should work the same as calling payload.Generate(target_file,
714 # source_file).
715 payload.Generate(
716 target_file, additional_args=["--source_image", source_file])
717 self.assertTrue(os.path.exists(payload.payload_file))
718
719 def test_Generate_invalidInput(self):
Tao Baof7140c02018-01-30 17:09:24 -0800720 target_file = construct_target_files()
Tao Baoc7b403a2018-01-30 18:19:04 -0800721 common.ZipDelete(target_file, 'IMAGES/vendor.img')
722 payload = Payload()
723 self.assertRaises(AssertionError, payload.Generate, target_file)
724
725 def test_Sign_full(self):
726 payload = self._create_payload_full()
727 payload.Sign(PayloadSigner())
728
729 output_file = common.MakeTempFile(suffix='.zip')
730 with zipfile.ZipFile(output_file, 'w') as output_zip:
731 payload.WriteToZip(output_zip)
732
733 import check_ota_package_signature
734 check_ota_package_signature.VerifyAbOtaPayload(
735 os.path.join(self.testdata_dir, 'testkey.x509.pem'),
736 output_file)
737
738 def test_Sign_incremental(self):
739 payload = self._create_payload_incremental()
740 payload.Sign(PayloadSigner())
741
742 output_file = common.MakeTempFile(suffix='.zip')
743 with zipfile.ZipFile(output_file, 'w') as output_zip:
744 payload.WriteToZip(output_zip)
745
746 import check_ota_package_signature
747 check_ota_package_signature.VerifyAbOtaPayload(
748 os.path.join(self.testdata_dir, 'testkey.x509.pem'),
749 output_file)
750
751 def test_Sign_withDataWipe(self):
752 common.OPTIONS.wipe_user_data = True
753 payload = self._create_payload_full()
754 payload.Sign(PayloadSigner())
755
756 with open(payload.payload_properties) as properties_fp:
757 self.assertIn("POWERWASH=1", properties_fp.read())
758
Tao Bao667ff572018-02-10 00:02:40 -0800759 def test_Sign_secondary(self):
760 payload = self._create_payload_full(secondary=True)
761 payload.Sign(PayloadSigner())
762
763 with open(payload.payload_properties) as properties_fp:
764 self.assertIn("SWITCH_SLOT_ON_REBOOT=0", properties_fp.read())
765
Tao Baoc7b403a2018-01-30 18:19:04 -0800766 def test_Sign_badSigner(self):
767 """Tests that signing failure can be captured."""
768 payload = self._create_payload_full()
769 payload_signer = PayloadSigner()
770 payload_signer.signer_args.append('bad-option')
771 self.assertRaises(AssertionError, payload.Sign, payload_signer)
772
773 def test_WriteToZip(self):
774 payload = self._create_payload_full()
775 payload.Sign(PayloadSigner())
776
777 output_file = common.MakeTempFile(suffix='.zip')
778 with zipfile.ZipFile(output_file, 'w') as output_zip:
779 payload.WriteToZip(output_zip)
780
781 with zipfile.ZipFile(output_file) as verify_zip:
782 # First make sure we have the essential entries.
783 namelist = verify_zip.namelist()
784 self.assertIn(Payload.PAYLOAD_BIN, namelist)
785 self.assertIn(Payload.PAYLOAD_PROPERTIES_TXT, namelist)
786
787 # Then assert these entries are stored.
788 for entry_info in verify_zip.infolist():
789 if entry_info.filename not in (Payload.PAYLOAD_BIN,
790 Payload.PAYLOAD_PROPERTIES_TXT):
791 continue
792 self.assertEqual(zipfile.ZIP_STORED, entry_info.compress_type)
793
794 def test_WriteToZip_unsignedPayload(self):
795 """Unsigned payloads should not be allowed to be written to zip."""
796 payload = self._create_payload_full()
797
798 output_file = common.MakeTempFile(suffix='.zip')
799 with zipfile.ZipFile(output_file, 'w') as output_zip:
800 self.assertRaises(AssertionError, payload.WriteToZip, output_zip)
801
802 # Also test with incremental payload.
803 payload = self._create_payload_incremental()
804
805 output_file = common.MakeTempFile(suffix='.zip')
806 with zipfile.ZipFile(output_file, 'w') as output_zip:
807 self.assertRaises(AssertionError, payload.WriteToZip, output_zip)
Tao Baof7140c02018-01-30 17:09:24 -0800808
809 def test_WriteToZip_secondary(self):
810 payload = self._create_payload_full(secondary=True)
811 payload.Sign(PayloadSigner())
812
813 output_file = common.MakeTempFile(suffix='.zip')
814 with zipfile.ZipFile(output_file, 'w') as output_zip:
Tao Bao667ff572018-02-10 00:02:40 -0800815 payload.WriteToZip(output_zip)
Tao Baof7140c02018-01-30 17:09:24 -0800816
817 with zipfile.ZipFile(output_file) as verify_zip:
818 # First make sure we have the essential entries.
819 namelist = verify_zip.namelist()
820 self.assertIn(Payload.SECONDARY_PAYLOAD_BIN, namelist)
821 self.assertIn(Payload.SECONDARY_PAYLOAD_PROPERTIES_TXT, namelist)
822
823 # Then assert these entries are stored.
824 for entry_info in verify_zip.infolist():
825 if entry_info.filename not in (
826 Payload.SECONDARY_PAYLOAD_BIN,
827 Payload.SECONDARY_PAYLOAD_PROPERTIES_TXT):
828 continue
829 self.assertEqual(zipfile.ZIP_STORED, entry_info.compress_type)