Performs basic APEX validation in the merged target files package.
Uses apex_utils.GetApexInfoFromTargetFiles to find and parse APEX files in the target files partition dirs. Raises an error on failure to parse or duplicate package names. Bug: 177225446 Test: releasetools_test Test: Create a merged build that provides the VNDK APEX on both vendor and system. Observe failure. Change-Id: I1356e263b7b32d6063129e079f3ba7ab4ff132a7
This commit is contained in:
@@ -62,7 +62,7 @@ import common
|
|||||||
import verity_utils
|
import verity_utils
|
||||||
import ota_metadata_pb2
|
import ota_metadata_pb2
|
||||||
|
|
||||||
from apex_utils import GetSystemApexInfoFromTargetFiles
|
from apex_utils import GetApexInfoFromTargetFiles
|
||||||
from common import AddCareMapForAbOta
|
from common import AddCareMapForAbOta
|
||||||
|
|
||||||
if sys.hexversion < 0x02070000:
|
if sys.hexversion < 0x02070000:
|
||||||
@@ -686,7 +686,7 @@ def HasPartition(partition_name):
|
|||||||
"{}.img".format(partition_name))))
|
"{}.img".format(partition_name))))
|
||||||
|
|
||||||
def AddApexInfo(output_zip):
|
def AddApexInfo(output_zip):
|
||||||
apex_infos = GetSystemApexInfoFromTargetFiles(OPTIONS.input_tmp)
|
apex_infos = GetApexInfoFromTargetFiles(OPTIONS.input_tmp, 'system')
|
||||||
apex_metadata_proto = ota_metadata_pb2.ApexMetadata()
|
apex_metadata_proto = ota_metadata_pb2.ApexMetadata()
|
||||||
apex_metadata_proto.apex_info.extend(apex_infos)
|
apex_metadata_proto.apex_info.extend(apex_infos)
|
||||||
apex_info_bytes = apex_metadata_proto.SerializeToString()
|
apex_info_bytes = apex_metadata_proto.SerializeToString()
|
||||||
|
@@ -516,7 +516,7 @@ def SignApex(avbtool, apex_data, payload_key, container_key, container_pw,
|
|||||||
raise ApexInfoError(
|
raise ApexInfoError(
|
||||||
'Failed to get type for {}:\n{}'.format(apex_file, e))
|
'Failed to get type for {}:\n{}'.format(apex_file, e))
|
||||||
|
|
||||||
def GetSystemApexInfoFromTargetFiles(input_file):
|
def GetApexInfoFromTargetFiles(input_file, partition, compressed_only=True):
|
||||||
"""
|
"""
|
||||||
Get information about system APEX stored in the input_file zip
|
Get information about system APEX stored in the input_file zip
|
||||||
|
|
||||||
@@ -532,15 +532,17 @@ def GetSystemApexInfoFromTargetFiles(input_file):
|
|||||||
if not isinstance(input_file, str):
|
if not isinstance(input_file, str):
|
||||||
raise RuntimeError("must pass filepath to target-files zip or directory")
|
raise RuntimeError("must pass filepath to target-files zip or directory")
|
||||||
|
|
||||||
|
apex_subdir = os.path.join(partition.upper(), 'apex')
|
||||||
if os.path.isdir(input_file):
|
if os.path.isdir(input_file):
|
||||||
tmp_dir = input_file
|
tmp_dir = input_file
|
||||||
else:
|
else:
|
||||||
tmp_dir = UnzipTemp(input_file, ["SYSTEM/apex/*"])
|
tmp_dir = UnzipTemp(input_file, [os.path.join(apex_subdir, '*')])
|
||||||
target_dir = os.path.join(tmp_dir, "SYSTEM/apex/")
|
target_dir = os.path.join(tmp_dir, apex_subdir)
|
||||||
|
|
||||||
# Partial target-files packages for vendor-only builds may not contain
|
# Partial target-files packages for vendor-only builds may not contain
|
||||||
# a system apex directory.
|
# a system apex directory.
|
||||||
if not os.path.exists(target_dir):
|
if not os.path.exists(target_dir):
|
||||||
|
logger.info('No APEX directory at path: %s', target_dir)
|
||||||
return []
|
return []
|
||||||
|
|
||||||
apex_infos = []
|
apex_infos = []
|
||||||
@@ -585,6 +587,7 @@ def GetSystemApexInfoFromTargetFiles(input_file):
|
|||||||
'--output', decompressed_file_path])
|
'--output', decompressed_file_path])
|
||||||
apex_info.decompressed_size = os.path.getsize(decompressed_file_path)
|
apex_info.decompressed_size = os.path.getsize(decompressed_file_path)
|
||||||
|
|
||||||
|
if not compressed_only or apex_info.is_compressed:
|
||||||
apex_infos.append(apex_info)
|
apex_infos.append(apex_info)
|
||||||
|
|
||||||
return apex_infos
|
return apex_infos
|
||||||
|
@@ -96,6 +96,7 @@ import zipfile
|
|||||||
from xml.etree import ElementTree
|
from xml.etree import ElementTree
|
||||||
|
|
||||||
import add_img_to_target_files
|
import add_img_to_target_files
|
||||||
|
import apex_utils
|
||||||
import build_image
|
import build_image
|
||||||
import build_super_image
|
import build_super_image
|
||||||
import check_target_files_vintf
|
import check_target_files_vintf
|
||||||
@@ -739,6 +740,35 @@ def compile_split_sepolicy(product_out, partition_map, output_policy):
|
|||||||
return cmd
|
return cmd
|
||||||
|
|
||||||
|
|
||||||
|
def validate_merged_apex_info(output_target_files_dir, partitions):
|
||||||
|
"""Validates the APEX files in the merged target files directory.
|
||||||
|
|
||||||
|
Checks the APEX files in all possible preinstalled APEX directories.
|
||||||
|
Depends on the <partition>/apex/* APEX files within partitions.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
output_target_files_dir: Output directory containing merged partition directories.
|
||||||
|
partitions: A list of all the partitions in the output directory.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
RuntimeError: if apex_utils fails to parse any APEX file.
|
||||||
|
ExternalError: if the same APEX package is provided by multiple partitions.
|
||||||
|
"""
|
||||||
|
apex_packages = set()
|
||||||
|
|
||||||
|
apex_partitions = ('system', 'system_ext', 'product', 'vendor')
|
||||||
|
for partition in filter(lambda p: p in apex_partitions, partitions):
|
||||||
|
apex_info = apex_utils.GetApexInfoFromTargetFiles(
|
||||||
|
output_target_files_dir, partition, compressed_only=False)
|
||||||
|
partition_apex_packages = set([info.package_name for info in apex_info])
|
||||||
|
duplicates = apex_packages.intersection(partition_apex_packages)
|
||||||
|
if duplicates:
|
||||||
|
raise ExternalError(
|
||||||
|
'Duplicate APEX packages found in multiple partitions: %s' %
|
||||||
|
' '.join(duplicates))
|
||||||
|
apex_packages.update(partition_apex_packages)
|
||||||
|
|
||||||
|
|
||||||
def generate_care_map(partitions, output_target_files_dir):
|
def generate_care_map(partitions, output_target_files_dir):
|
||||||
"""Generates a merged META/care_map.pb file in the output target files dir.
|
"""Generates a merged META/care_map.pb file in the output target files dir.
|
||||||
|
|
||||||
@@ -1116,6 +1146,9 @@ def merge_target_files(temp_dir, framework_target_files, framework_item_list,
|
|||||||
common.RunAndCheckOutput(split_sepolicy_cmd)
|
common.RunAndCheckOutput(split_sepolicy_cmd)
|
||||||
# TODO(b/178864050): Run tests on the combined.policy file.
|
# TODO(b/178864050): Run tests on the combined.policy file.
|
||||||
|
|
||||||
|
# Run validation checks on the pre-installed APEX files.
|
||||||
|
validate_merged_apex_info(output_target_files_temp_dir, partition_map.keys())
|
||||||
|
|
||||||
generate_images(output_target_files_temp_dir, rebuild_recovery)
|
generate_images(output_target_files_temp_dir, rebuild_recovery)
|
||||||
|
|
||||||
generate_super_empty_image(output_target_files_temp_dir, output_super_empty)
|
generate_super_empty_image(output_target_files_temp_dir, output_super_empty)
|
||||||
|
@@ -15,6 +15,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
import os.path
|
import os.path
|
||||||
|
import shutil
|
||||||
|
|
||||||
import common
|
import common
|
||||||
import test_utils
|
import test_utils
|
||||||
@@ -22,7 +23,7 @@ from merge_target_files import (
|
|||||||
validate_config_lists, DEFAULT_FRAMEWORK_ITEM_LIST,
|
validate_config_lists, DEFAULT_FRAMEWORK_ITEM_LIST,
|
||||||
DEFAULT_VENDOR_ITEM_LIST, DEFAULT_FRAMEWORK_MISC_INFO_KEYS, copy_items,
|
DEFAULT_VENDOR_ITEM_LIST, DEFAULT_FRAMEWORK_MISC_INFO_KEYS, copy_items,
|
||||||
item_list_to_partition_set, process_apex_keys_apk_certs_common,
|
item_list_to_partition_set, process_apex_keys_apk_certs_common,
|
||||||
compile_split_sepolicy)
|
compile_split_sepolicy, validate_merged_apex_info)
|
||||||
|
|
||||||
|
|
||||||
class MergeTargetFilesTest(test_utils.ReleaseToolsTestCase):
|
class MergeTargetFilesTest(test_utils.ReleaseToolsTestCase):
|
||||||
@@ -274,3 +275,36 @@ class MergeTargetFilesTest(test_utils.ReleaseToolsTestCase):
|
|||||||
'{OTP}/vendor/etc/selinux/plat_pub_versioned.cil '
|
'{OTP}/vendor/etc/selinux/plat_pub_versioned.cil '
|
||||||
'{OTP}/product/etc/selinux/mapping/30.0.cil').format(
|
'{OTP}/product/etc/selinux/mapping/30.0.cil').format(
|
||||||
OTP=product_out_dir))
|
OTP=product_out_dir))
|
||||||
|
|
||||||
|
def _copy_apex(self, source, output_dir, partition):
|
||||||
|
shutil.copy(
|
||||||
|
source,
|
||||||
|
os.path.join(output_dir, partition, 'apex', os.path.basename(source)))
|
||||||
|
|
||||||
|
@test_utils.SkipIfExternalToolsUnavailable()
|
||||||
|
def test_validate_merged_apex_info(self):
|
||||||
|
output_dir = common.MakeTempDir()
|
||||||
|
os.makedirs(os.path.join(output_dir, 'SYSTEM/apex'))
|
||||||
|
os.makedirs(os.path.join(output_dir, 'VENDOR/apex'))
|
||||||
|
|
||||||
|
self._copy_apex(
|
||||||
|
os.path.join(self.testdata_dir, 'has_apk.apex'), output_dir, 'SYSTEM')
|
||||||
|
self._copy_apex(
|
||||||
|
os.path.join(test_utils.get_current_dir(),
|
||||||
|
'com.android.apex.compressed.v1.capex'), output_dir,
|
||||||
|
'VENDOR')
|
||||||
|
validate_merged_apex_info(output_dir, ('system', 'vendor'))
|
||||||
|
|
||||||
|
@test_utils.SkipIfExternalToolsUnavailable()
|
||||||
|
def test_validate_merged_apex_info_RaisesOnPackageInMultiplePartitions(self):
|
||||||
|
output_dir = common.MakeTempDir()
|
||||||
|
os.makedirs(os.path.join(output_dir, 'SYSTEM/apex'))
|
||||||
|
os.makedirs(os.path.join(output_dir, 'VENDOR/apex'))
|
||||||
|
|
||||||
|
same_apex_package = os.path.join(self.testdata_dir, 'has_apk.apex')
|
||||||
|
self._copy_apex(same_apex_package, output_dir, 'SYSTEM')
|
||||||
|
self._copy_apex(same_apex_package, output_dir, 'VENDOR')
|
||||||
|
self.assertRaisesRegexp(
|
||||||
|
common.ExternalError,
|
||||||
|
'Duplicate APEX packages found in multiple partitions: com.android.wifi',
|
||||||
|
validate_merged_apex_info, output_dir, ('system', 'vendor'))
|
||||||
|
@@ -33,7 +33,7 @@ from ota_from_target_files import (
|
|||||||
GetTargetFilesZipWithoutPostinstallConfig,
|
GetTargetFilesZipWithoutPostinstallConfig,
|
||||||
Payload, PayloadSigner, POSTINSTALL_CONFIG,
|
Payload, PayloadSigner, POSTINSTALL_CONFIG,
|
||||||
StreamingPropertyFiles, AB_PARTITIONS)
|
StreamingPropertyFiles, AB_PARTITIONS)
|
||||||
from apex_utils import GetSystemApexInfoFromTargetFiles
|
from apex_utils import GetApexInfoFromTargetFiles
|
||||||
from test_utils import PropertyFilesTestCase
|
from test_utils import PropertyFilesTestCase
|
||||||
|
|
||||||
|
|
||||||
@@ -281,9 +281,9 @@ class OtaFromTargetFilesTest(test_utils.ReleaseToolsTestCase):
|
|||||||
metadata)
|
metadata)
|
||||||
|
|
||||||
@test_utils.SkipIfExternalToolsUnavailable()
|
@test_utils.SkipIfExternalToolsUnavailable()
|
||||||
def test_GetSystemApexInfoFromTargetFiles(self):
|
def test_GetApexInfoFromTargetFiles(self):
|
||||||
target_files = construct_target_files(compressedApex=True)
|
target_files = construct_target_files(compressedApex=True)
|
||||||
apex_infos = GetSystemApexInfoFromTargetFiles(target_files)
|
apex_infos = GetApexInfoFromTargetFiles(target_files, 'system')
|
||||||
self.assertEqual(len(apex_infos), 1)
|
self.assertEqual(len(apex_infos), 1)
|
||||||
self.assertEqual(apex_infos[0].package_name, "com.android.apex.compressed")
|
self.assertEqual(apex_infos[0].package_name, "com.android.apex.compressed")
|
||||||
self.assertEqual(apex_infos[0].version, 1)
|
self.assertEqual(apex_infos[0].version, 1)
|
||||||
|
Reference in New Issue
Block a user