From e9af70ad6860fed79c5b82260427e36c6ba5277a Mon Sep 17 00:00:00 2001 From: Daniel Norman Date: Thu, 15 Apr 2021 16:39:22 -0700 Subject: [PATCH] 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 --- tools/releasetools/add_img_to_target_files.py | 4 +-- tools/releasetools/apex_utils.py | 9 +++-- tools/releasetools/merge_target_files.py | 33 +++++++++++++++++ tools/releasetools/test_merge_target_files.py | 36 ++++++++++++++++++- .../test_ota_from_target_files.py | 6 ++-- 5 files changed, 79 insertions(+), 9 deletions(-) diff --git a/tools/releasetools/add_img_to_target_files.py b/tools/releasetools/add_img_to_target_files.py index a56c30569..00bbb212d 100644 --- a/tools/releasetools/add_img_to_target_files.py +++ b/tools/releasetools/add_img_to_target_files.py @@ -62,7 +62,7 @@ import common import verity_utils import ota_metadata_pb2 -from apex_utils import GetSystemApexInfoFromTargetFiles +from apex_utils import GetApexInfoFromTargetFiles from common import AddCareMapForAbOta if sys.hexversion < 0x02070000: @@ -686,7 +686,7 @@ def HasPartition(partition_name): "{}.img".format(partition_name)))) 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.apex_info.extend(apex_infos) apex_info_bytes = apex_metadata_proto.SerializeToString() diff --git a/tools/releasetools/apex_utils.py b/tools/releasetools/apex_utils.py index 1c8805345..893266f95 100644 --- a/tools/releasetools/apex_utils.py +++ b/tools/releasetools/apex_utils.py @@ -516,7 +516,7 @@ def SignApex(avbtool, apex_data, payload_key, container_key, container_pw, raise ApexInfoError( '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 @@ -532,15 +532,17 @@ def GetSystemApexInfoFromTargetFiles(input_file): if not isinstance(input_file, str): 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): tmp_dir = input_file else: - tmp_dir = UnzipTemp(input_file, ["SYSTEM/apex/*"]) - target_dir = os.path.join(tmp_dir, "SYSTEM/apex/") + tmp_dir = UnzipTemp(input_file, [os.path.join(apex_subdir, '*')]) + target_dir = os.path.join(tmp_dir, apex_subdir) # Partial target-files packages for vendor-only builds may not contain # a system apex directory. if not os.path.exists(target_dir): + logger.info('No APEX directory at path: %s', target_dir) return [] apex_infos = [] @@ -585,6 +587,7 @@ def GetSystemApexInfoFromTargetFiles(input_file): '--output', 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) return apex_infos diff --git a/tools/releasetools/merge_target_files.py b/tools/releasetools/merge_target_files.py index 17d303088..5e6c42d84 100755 --- a/tools/releasetools/merge_target_files.py +++ b/tools/releasetools/merge_target_files.py @@ -96,6 +96,7 @@ import zipfile from xml.etree import ElementTree import add_img_to_target_files +import apex_utils import build_image import build_super_image import check_target_files_vintf @@ -739,6 +740,35 @@ def compile_split_sepolicy(product_out, partition_map, output_policy): 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 /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): """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) # 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_super_empty_image(output_target_files_temp_dir, output_super_empty) diff --git a/tools/releasetools/test_merge_target_files.py b/tools/releasetools/test_merge_target_files.py index 072bb0135..4f6147257 100644 --- a/tools/releasetools/test_merge_target_files.py +++ b/tools/releasetools/test_merge_target_files.py @@ -15,6 +15,7 @@ # import os.path +import shutil import common import test_utils @@ -22,7 +23,7 @@ from merge_target_files import ( validate_config_lists, DEFAULT_FRAMEWORK_ITEM_LIST, DEFAULT_VENDOR_ITEM_LIST, DEFAULT_FRAMEWORK_MISC_INFO_KEYS, copy_items, 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): @@ -274,3 +275,36 @@ class MergeTargetFilesTest(test_utils.ReleaseToolsTestCase): '{OTP}/vendor/etc/selinux/plat_pub_versioned.cil ' '{OTP}/product/etc/selinux/mapping/30.0.cil').format( 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')) diff --git a/tools/releasetools/test_ota_from_target_files.py b/tools/releasetools/test_ota_from_target_files.py index 9f6484924..661712a80 100644 --- a/tools/releasetools/test_ota_from_target_files.py +++ b/tools/releasetools/test_ota_from_target_files.py @@ -33,7 +33,7 @@ from ota_from_target_files import ( GetTargetFilesZipWithoutPostinstallConfig, Payload, PayloadSigner, POSTINSTALL_CONFIG, StreamingPropertyFiles, AB_PARTITIONS) -from apex_utils import GetSystemApexInfoFromTargetFiles +from apex_utils import GetApexInfoFromTargetFiles from test_utils import PropertyFilesTestCase @@ -281,9 +281,9 @@ class OtaFromTargetFilesTest(test_utils.ReleaseToolsTestCase): metadata) @test_utils.SkipIfExternalToolsUnavailable() - def test_GetSystemApexInfoFromTargetFiles(self): + def test_GetApexInfoFromTargetFiles(self): 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(apex_infos[0].package_name, "com.android.apex.compressed") self.assertEqual(apex_infos[0].version, 1)