430 lines
16 KiB
Python
430 lines
16 KiB
Python
#
|
|
# Copyright (C) 2018 The Android Open Source Project
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
#
|
|
|
|
import os
|
|
import os.path
|
|
import zipfile
|
|
|
|
import common
|
|
import test_utils
|
|
from add_img_to_target_files import (
|
|
AddPackRadioImages,
|
|
CheckAbOtaImages)
|
|
from rangelib import RangeSet
|
|
from common import AddCareMapForAbOta, GetCareMap
|
|
|
|
|
|
OPTIONS = common.OPTIONS
|
|
|
|
|
|
class AddImagesToTargetFilesTest(test_utils.ReleaseToolsTestCase):
|
|
|
|
def setUp(self):
|
|
OPTIONS.input_tmp = common.MakeTempDir()
|
|
|
|
@staticmethod
|
|
def _create_images(images, prefix):
|
|
"""Creates images under OPTIONS.input_tmp/prefix."""
|
|
path = os.path.join(OPTIONS.input_tmp, prefix)
|
|
if not os.path.exists(path):
|
|
os.mkdir(path)
|
|
|
|
for image in images:
|
|
image_path = os.path.join(path, image + '.img')
|
|
with open(image_path, 'wb') as image_fp:
|
|
image_fp.write(image.encode())
|
|
|
|
images_path = os.path.join(OPTIONS.input_tmp, 'IMAGES')
|
|
if not os.path.exists(images_path):
|
|
os.mkdir(images_path)
|
|
return images, images_path
|
|
|
|
def test_CheckAbOtaImages_imageExistsUnderImages(self):
|
|
"""Tests the case with existing images under IMAGES/."""
|
|
images, _ = self._create_images(['aboot', 'xbl'], 'IMAGES')
|
|
CheckAbOtaImages(None, images)
|
|
|
|
def test_CheckAbOtaImages_imageExistsUnderRadio(self):
|
|
"""Tests the case with some image under RADIO/."""
|
|
images, _ = self._create_images(['system', 'vendor'], 'IMAGES')
|
|
radio_path = os.path.join(OPTIONS.input_tmp, 'RADIO')
|
|
if not os.path.exists(radio_path):
|
|
os.mkdir(radio_path)
|
|
with open(os.path.join(radio_path, 'modem.img'), 'wb') as image_fp:
|
|
image_fp.write('modem'.encode())
|
|
CheckAbOtaImages(None, images + ['modem'])
|
|
|
|
def test_CheckAbOtaImages_missingImages(self):
|
|
images, _ = self._create_images(['aboot', 'xbl'], 'RADIO')
|
|
self.assertRaises(
|
|
AssertionError, CheckAbOtaImages, None, images + ['baz'])
|
|
|
|
def test_AddPackRadioImages(self):
|
|
images, images_path = self._create_images(['foo', 'bar'], 'RADIO')
|
|
AddPackRadioImages(None, images)
|
|
|
|
for image in images:
|
|
self.assertTrue(
|
|
os.path.exists(os.path.join(images_path, image + '.img')))
|
|
|
|
def test_AddPackRadioImages_with_suffix(self):
|
|
images, images_path = self._create_images(['foo', 'bar'], 'RADIO')
|
|
images_with_suffix = [image + '.img' for image in images]
|
|
AddPackRadioImages(None, images_with_suffix)
|
|
|
|
for image in images:
|
|
self.assertTrue(
|
|
os.path.exists(os.path.join(images_path, image + '.img')))
|
|
|
|
def test_AddPackRadioImages_zipOutput(self):
|
|
images, _ = self._create_images(['foo', 'bar'], 'RADIO')
|
|
|
|
# Set up the output zip.
|
|
output_file = common.MakeTempFile(suffix='.zip')
|
|
with zipfile.ZipFile(output_file, 'w', allowZip64=True) as output_zip:
|
|
AddPackRadioImages(output_zip, images)
|
|
|
|
with zipfile.ZipFile(output_file, 'r', allowZip64=True) as verify_zip:
|
|
for image in images:
|
|
self.assertIn('IMAGES/' + image + '.img', verify_zip.namelist())
|
|
|
|
def test_AddPackRadioImages_imageExists(self):
|
|
images, images_path = self._create_images(['foo', 'bar'], 'RADIO')
|
|
|
|
# Additionally create images under IMAGES/ so that they should be skipped.
|
|
images, images_path = self._create_images(['foo', 'bar'], 'IMAGES')
|
|
|
|
AddPackRadioImages(None, images)
|
|
|
|
for image in images:
|
|
self.assertTrue(
|
|
os.path.exists(os.path.join(images_path, image + '.img')))
|
|
|
|
def test_AddPackRadioImages_missingImages(self):
|
|
images, _ = self._create_images(['foo', 'bar'], 'RADIO')
|
|
AddPackRadioImages(None, images)
|
|
|
|
self.assertRaises(AssertionError, AddPackRadioImages, None,
|
|
images + ['baz'])
|
|
|
|
@staticmethod
|
|
def _test_AddCareMapForAbOta():
|
|
"""Helper function to set up the test for test_AddCareMapForAbOta()."""
|
|
OPTIONS.info_dict = {
|
|
'extfs_sparse_flag' : '-s',
|
|
'system_image_size' : 65536,
|
|
'vendor_image_size' : 40960,
|
|
'system_verity_block_device': '/dev/block/system',
|
|
'vendor_verity_block_device': '/dev/block/vendor',
|
|
'system.build.prop': common.PartitionBuildProps.FromDictionary(
|
|
'system', {
|
|
'ro.system.build.fingerprint':
|
|
'google/sailfish/12345:user/dev-keys'}
|
|
),
|
|
'vendor.build.prop': common.PartitionBuildProps.FromDictionary(
|
|
'vendor', {
|
|
'ro.vendor.build.fingerprint':
|
|
'google/sailfish/678:user/dev-keys'}
|
|
),
|
|
}
|
|
|
|
# Prepare the META/ folder.
|
|
meta_path = os.path.join(OPTIONS.input_tmp, 'META')
|
|
if not os.path.exists(meta_path):
|
|
os.mkdir(meta_path)
|
|
|
|
system_image = test_utils.construct_sparse_image([
|
|
(0xCAC1, 6),
|
|
(0xCAC3, 4),
|
|
(0xCAC1, 8)])
|
|
vendor_image = test_utils.construct_sparse_image([
|
|
(0xCAC2, 12)])
|
|
|
|
image_paths = {
|
|
'system' : system_image,
|
|
'vendor' : vendor_image,
|
|
}
|
|
return image_paths
|
|
|
|
def _verifyCareMap(self, expected, file_name):
|
|
"""Parses the care_map.pb; and checks the content in plain text."""
|
|
text_file = common.MakeTempFile(prefix="caremap-", suffix=".txt")
|
|
|
|
# Calls an external binary to convert the proto message.
|
|
cmd = ["care_map_generator", "--parse_proto", file_name, text_file]
|
|
common.RunAndCheckOutput(cmd)
|
|
|
|
with open(text_file) as verify_fp:
|
|
plain_text = verify_fp.read()
|
|
self.assertEqual('\n'.join(expected), plain_text)
|
|
|
|
@test_utils.SkipIfExternalToolsUnavailable()
|
|
def test_AddCareMapForAbOta(self):
|
|
image_paths = self._test_AddCareMapForAbOta()
|
|
|
|
care_map_file = os.path.join(OPTIONS.input_tmp, 'META', 'care_map.pb')
|
|
AddCareMapForAbOta(care_map_file, ['system', 'vendor'], image_paths)
|
|
|
|
expected = ['system', RangeSet("0-5 10-15").to_string_raw(),
|
|
"ro.system.build.fingerprint",
|
|
"google/sailfish/12345:user/dev-keys",
|
|
'vendor', RangeSet("0-9").to_string_raw(),
|
|
"ro.vendor.build.fingerprint",
|
|
"google/sailfish/678:user/dev-keys"]
|
|
|
|
self._verifyCareMap(expected, care_map_file)
|
|
|
|
@test_utils.SkipIfExternalToolsUnavailable()
|
|
def test_AddCareMapForAbOta_withNonCareMapPartitions(self):
|
|
"""Partitions without care_map should be ignored."""
|
|
image_paths = self._test_AddCareMapForAbOta()
|
|
|
|
care_map_file = os.path.join(OPTIONS.input_tmp, 'META', 'care_map.pb')
|
|
AddCareMapForAbOta(
|
|
care_map_file, ['boot', 'system', 'vendor', 'vbmeta'], image_paths)
|
|
|
|
expected = ['system', RangeSet("0-5 10-15").to_string_raw(),
|
|
"ro.system.build.fingerprint",
|
|
"google/sailfish/12345:user/dev-keys",
|
|
'vendor', RangeSet("0-9").to_string_raw(),
|
|
"ro.vendor.build.fingerprint",
|
|
"google/sailfish/678:user/dev-keys"]
|
|
|
|
self._verifyCareMap(expected, care_map_file)
|
|
|
|
@test_utils.SkipIfExternalToolsUnavailable()
|
|
def test_AddCareMapForAbOta_withAvb(self):
|
|
"""Tests the case for device using AVB."""
|
|
image_paths = self._test_AddCareMapForAbOta()
|
|
OPTIONS.info_dict = {
|
|
'extfs_sparse_flag': '-s',
|
|
'system_image_size': 65536,
|
|
'vendor_image_size': 40960,
|
|
'avb_system_hashtree_enable': 'true',
|
|
'avb_vendor_hashtree_enable': 'true',
|
|
'system.build.prop': common.PartitionBuildProps.FromDictionary(
|
|
'system', {
|
|
'ro.system.build.fingerprint':
|
|
'google/sailfish/12345:user/dev-keys'}
|
|
),
|
|
'vendor.build.prop': common.PartitionBuildProps.FromDictionary(
|
|
'vendor', {
|
|
'ro.vendor.build.fingerprint':
|
|
'google/sailfish/678:user/dev-keys'}
|
|
),
|
|
}
|
|
|
|
care_map_file = os.path.join(OPTIONS.input_tmp, 'META', 'care_map.pb')
|
|
AddCareMapForAbOta(care_map_file, ['system', 'vendor'], image_paths)
|
|
|
|
expected = ['system', RangeSet("0-5 10-15").to_string_raw(),
|
|
"ro.system.build.fingerprint",
|
|
"google/sailfish/12345:user/dev-keys",
|
|
'vendor', RangeSet("0-9").to_string_raw(),
|
|
"ro.vendor.build.fingerprint",
|
|
"google/sailfish/678:user/dev-keys"]
|
|
|
|
self._verifyCareMap(expected, care_map_file)
|
|
|
|
@test_utils.SkipIfExternalToolsUnavailable()
|
|
def test_AddCareMapForAbOta_noFingerprint(self):
|
|
"""Tests the case for partitions without fingerprint."""
|
|
image_paths = self._test_AddCareMapForAbOta()
|
|
OPTIONS.info_dict = {
|
|
'extfs_sparse_flag' : '-s',
|
|
'system_image_size' : 65536,
|
|
'vendor_image_size' : 40960,
|
|
'system_verity_block_device': '/dev/block/system',
|
|
'vendor_verity_block_device': '/dev/block/vendor',
|
|
}
|
|
|
|
care_map_file = os.path.join(OPTIONS.input_tmp, 'META', 'care_map.pb')
|
|
AddCareMapForAbOta(care_map_file, ['system', 'vendor'], image_paths)
|
|
|
|
expected = ['system', RangeSet("0-5 10-15").to_string_raw(), "unknown",
|
|
"unknown", 'vendor', RangeSet("0-9").to_string_raw(), "unknown",
|
|
"unknown"]
|
|
|
|
self._verifyCareMap(expected, care_map_file)
|
|
|
|
@test_utils.SkipIfExternalToolsUnavailable()
|
|
def test_AddCareMapForAbOta_withThumbprint(self):
|
|
"""Tests the case for partitions with thumbprint."""
|
|
image_paths = self._test_AddCareMapForAbOta()
|
|
OPTIONS.info_dict = {
|
|
'extfs_sparse_flag': '-s',
|
|
'system_image_size': 65536,
|
|
'vendor_image_size': 40960,
|
|
'system_verity_block_device': '/dev/block/system',
|
|
'vendor_verity_block_device': '/dev/block/vendor',
|
|
'system.build.prop': common.PartitionBuildProps.FromDictionary(
|
|
'system', {
|
|
'ro.system.build.thumbprint':
|
|
'google/sailfish/123:user/dev-keys'}
|
|
),
|
|
'vendor.build.prop': common.PartitionBuildProps.FromDictionary(
|
|
'vendor', {
|
|
'ro.vendor.build.thumbprint':
|
|
'google/sailfish/456:user/dev-keys'}
|
|
),
|
|
}
|
|
|
|
care_map_file = os.path.join(OPTIONS.input_tmp, 'META', 'care_map.pb')
|
|
AddCareMapForAbOta(care_map_file, ['system', 'vendor'], image_paths)
|
|
|
|
expected = ['system', RangeSet("0-5 10-15").to_string_raw(),
|
|
"ro.system.build.thumbprint",
|
|
"google/sailfish/123:user/dev-keys",
|
|
'vendor', RangeSet("0-9").to_string_raw(),
|
|
"ro.vendor.build.thumbprint",
|
|
"google/sailfish/456:user/dev-keys"]
|
|
|
|
self._verifyCareMap(expected, care_map_file)
|
|
|
|
@test_utils.SkipIfExternalToolsUnavailable()
|
|
def test_AddCareMapForAbOta_skipPartition(self):
|
|
image_paths = self._test_AddCareMapForAbOta()
|
|
|
|
# Remove vendor_image_size to invalidate the care_map for vendor.img.
|
|
del OPTIONS.info_dict['vendor_image_size']
|
|
|
|
care_map_file = os.path.join(OPTIONS.input_tmp, 'META', 'care_map.pb')
|
|
AddCareMapForAbOta(care_map_file, ['system', 'vendor'], image_paths)
|
|
|
|
expected = ['system', RangeSet("0-5 10-15").to_string_raw(),
|
|
"ro.system.build.fingerprint",
|
|
"google/sailfish/12345:user/dev-keys"]
|
|
|
|
self._verifyCareMap(expected, care_map_file)
|
|
|
|
@test_utils.SkipIfExternalToolsUnavailable()
|
|
def test_AddCareMapForAbOta_skipAllPartitions(self):
|
|
image_paths = self._test_AddCareMapForAbOta()
|
|
|
|
# Remove the image_size properties for all the partitions.
|
|
del OPTIONS.info_dict['system_image_size']
|
|
del OPTIONS.info_dict['vendor_image_size']
|
|
|
|
care_map_file = os.path.join(OPTIONS.input_tmp, 'META', 'care_map.pb')
|
|
AddCareMapForAbOta(care_map_file, ['system', 'vendor'], image_paths)
|
|
|
|
self.assertFalse(os.path.exists(care_map_file))
|
|
|
|
def test_AddCareMapForAbOta_verityNotEnabled(self):
|
|
"""No care_map.pb should be generated if verity not enabled."""
|
|
image_paths = self._test_AddCareMapForAbOta()
|
|
OPTIONS.info_dict = {}
|
|
care_map_file = os.path.join(OPTIONS.input_tmp, 'META', 'care_map.pb')
|
|
AddCareMapForAbOta(care_map_file, ['system', 'vendor'], image_paths)
|
|
|
|
self.assertFalse(os.path.exists(care_map_file))
|
|
|
|
def test_AddCareMapForAbOta_missingImageFile(self):
|
|
"""Missing image file should be considered fatal."""
|
|
image_paths = self._test_AddCareMapForAbOta()
|
|
image_paths['vendor'] = ''
|
|
care_map_file = os.path.join(OPTIONS.input_tmp, 'META', 'care_map.pb')
|
|
self.assertRaises(common.ExternalError, AddCareMapForAbOta, care_map_file,
|
|
['system', 'vendor'], image_paths)
|
|
|
|
@test_utils.SkipIfExternalToolsUnavailable()
|
|
def test_AddCareMapForAbOta_zipOutput(self):
|
|
"""Tests the case with ZIP output."""
|
|
image_paths = self._test_AddCareMapForAbOta()
|
|
|
|
output_file = common.MakeTempFile(suffix='.zip')
|
|
with zipfile.ZipFile(output_file, 'w', allowZip64=True) as output_zip:
|
|
AddCareMapForAbOta(output_zip, ['system', 'vendor'], image_paths)
|
|
|
|
care_map_name = "META/care_map.pb"
|
|
temp_dir = common.MakeTempDir()
|
|
with zipfile.ZipFile(output_file, 'r', allowZip64=True) as verify_zip:
|
|
self.assertTrue(care_map_name in verify_zip.namelist())
|
|
verify_zip.extract(care_map_name, path=temp_dir)
|
|
|
|
expected = ['system', RangeSet("0-5 10-15").to_string_raw(),
|
|
"ro.system.build.fingerprint",
|
|
"google/sailfish/12345:user/dev-keys",
|
|
'vendor', RangeSet("0-9").to_string_raw(),
|
|
"ro.vendor.build.fingerprint",
|
|
"google/sailfish/678:user/dev-keys"]
|
|
self._verifyCareMap(expected, os.path.join(temp_dir, care_map_name))
|
|
|
|
@test_utils.SkipIfExternalToolsUnavailable()
|
|
def test_AddCareMapForAbOta_zipOutput_careMapEntryExists(self):
|
|
"""Tests the case with ZIP output which already has care_map entry."""
|
|
image_paths = self._test_AddCareMapForAbOta()
|
|
|
|
output_file = common.MakeTempFile(suffix='.zip')
|
|
with zipfile.ZipFile(output_file, 'w', allowZip64=True) as output_zip:
|
|
# Create an existing META/care_map.pb entry.
|
|
common.ZipWriteStr(output_zip, 'META/care_map.pb',
|
|
'fake care_map.pb')
|
|
|
|
# Request to add META/care_map.pb again.
|
|
AddCareMapForAbOta(output_zip, ['system', 'vendor'], image_paths)
|
|
|
|
# The one under OPTIONS.input_tmp must have been replaced.
|
|
care_map_file = os.path.join(OPTIONS.input_tmp, 'META', 'care_map.pb')
|
|
expected = ['system', RangeSet("0-5 10-15").to_string_raw(),
|
|
"ro.system.build.fingerprint",
|
|
"google/sailfish/12345:user/dev-keys",
|
|
'vendor', RangeSet("0-9").to_string_raw(),
|
|
"ro.vendor.build.fingerprint",
|
|
"google/sailfish/678:user/dev-keys"]
|
|
|
|
self._verifyCareMap(expected, care_map_file)
|
|
|
|
# The existing entry should be scheduled to be replaced.
|
|
self.assertIn('META/care_map.pb', OPTIONS.replace_updated_files_list)
|
|
|
|
def test_GetCareMap(self):
|
|
sparse_image = test_utils.construct_sparse_image([
|
|
(0xCAC1, 6),
|
|
(0xCAC3, 4),
|
|
(0xCAC1, 6)])
|
|
OPTIONS.info_dict = {
|
|
'extfs_sparse_flag' : '-s',
|
|
'system_image_size' : 53248,
|
|
}
|
|
name, care_map = GetCareMap('system', sparse_image)
|
|
self.assertEqual('system', name)
|
|
self.assertEqual(RangeSet("0-5 10-12").to_string_raw(), care_map)
|
|
|
|
def test_GetCareMap_invalidPartition(self):
|
|
self.assertRaises(AssertionError, GetCareMap, 'oem', None)
|
|
|
|
def test_GetCareMap_invalidAdjustedPartitionSize(self):
|
|
sparse_image = test_utils.construct_sparse_image([
|
|
(0xCAC1, 6),
|
|
(0xCAC3, 4),
|
|
(0xCAC1, 6)])
|
|
OPTIONS.info_dict = {
|
|
'extfs_sparse_flag' : '-s',
|
|
'system_image_size' : -45056,
|
|
}
|
|
self.assertRaises(AssertionError, GetCareMap, 'system', sparse_image)
|
|
|
|
def test_GetCareMap_nonSparseImage(self):
|
|
OPTIONS.info_dict = {
|
|
'system_image_size' : 53248,
|
|
}
|
|
# 'foo' is the image filename, which is expected to be not used by
|
|
# GetCareMap().
|
|
name, care_map = GetCareMap('system', 'foo')
|
|
self.assertEqual('system', name)
|
|
self.assertEqual(RangeSet("0-12").to_string_raw(), care_map)
|