Files
build/tools/releasetools/merge/merge_compatibility_checks.py
Jooyung Han 750aad5c32 Add all apexes to apex_info
Previously, META/apex_info.pb contained only /system/apex apexes. Now,
it has all apexes from all possible partitions.

The main purpose of this file is to caculate the decompressed apex size
when applying OTA. Hence it should have all apexes, not just system
apexes.

Bug: 320228659
Test: m dist # check META/apex_info.pb
Change-Id: I3428dc502e4fe3336d1fc5ca941f1fbc332985cd
2024-01-23 05:52:51 +09:00

207 lines
7.1 KiB
Python

#!/usr/bin/env python
#
# Copyright (C) 2022 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.
#
"""Compatibility checks that should be performed on merged target_files."""
import json
import logging
import os
from xml.etree import ElementTree
import apex_utils
import check_target_files_vintf
import common
import find_shareduid_violation
logger = logging.getLogger(__name__)
OPTIONS = common.OPTIONS
def CheckCompatibility(target_files_dir, partition_map):
"""Runs various compatibility checks.
Returns a possibly-empty list of error messages.
"""
errors = []
errors.extend(CheckVintf(target_files_dir))
errors.extend(CheckShareduidViolation(target_files_dir, partition_map))
errors.extend(CheckApexDuplicatePackages(target_files_dir, partition_map))
# The remaining checks only use the following partitions:
partition_map = {
partition: path
for partition, path in partition_map.items()
if partition in ('system', 'system_ext', 'product', 'vendor', 'odm')
}
errors.extend(CheckInitRcFiles(target_files_dir, partition_map))
errors.extend(CheckCombinedSepolicy(target_files_dir, partition_map))
return errors
def CheckVintf(target_files_dir):
"""Check for any VINTF issues using check_vintf."""
errors = []
try:
if not check_target_files_vintf.CheckVintf(target_files_dir):
errors.append('Incompatible VINTF.')
except RuntimeError as err:
errors.append(str(err))
return errors
def CheckShareduidViolation(target_files_dir, partition_map):
"""Check for any APK sharedUserId violations across partition sets.
Writes results to META/shareduid_violation_modules.json to help
with followup debugging.
"""
errors = []
violation = find_shareduid_violation.FindShareduidViolation(
target_files_dir, partition_map)
shareduid_violation_modules = os.path.join(
target_files_dir, 'META', 'shareduid_violation_modules.json')
with open(shareduid_violation_modules, 'w') as f:
# Write the output to a file to enable debugging.
f.write(violation)
# Check for violations across the partition sets.
shareduid_errors = common.SharedUidPartitionViolations(
json.loads(violation),
[OPTIONS.framework_partition_set, OPTIONS.vendor_partition_set])
if shareduid_errors:
for error in shareduid_errors:
errors.append('APK sharedUserId error: %s' % error)
errors.append('See APK sharedUserId violations file: %s' %
shareduid_violation_modules)
return errors
def CheckInitRcFiles(target_files_dir, partition_map):
"""Check for any init.rc issues using host_init_verifier."""
try:
common.RunHostInitVerifier(
product_out=target_files_dir, partition_map=partition_map)
except RuntimeError as err:
return [str(err)]
return []
def CheckCombinedSepolicy(target_files_dir, partition_map, execute=True):
"""Uses secilc to compile a split sepolicy file.
Depends on various */etc/selinux/* and */etc/vintf/* files within partitions.
"""
errors = []
def get_file(partition, path):
if partition not in partition_map:
logger.warning('Cannot load SEPolicy files for missing partition %s',
partition)
return None
file_path = os.path.join(target_files_dir, partition_map[partition], path)
if os.path.exists(file_path):
return file_path
return None
# Load the kernel sepolicy version from the FCM. This is normally provided
# directly to selinux.cpp as a build flag, but is also available in this file.
fcm_file = get_file('system', 'etc/vintf/compatibility_matrix.device.xml')
if not fcm_file:
errors.append('Missing required file for loading sepolicy: '
'/system/etc/vintf/compatibility_matrix.device.xml')
return errors
kernel_sepolicy_version = ElementTree.parse(fcm_file).getroot().find(
'sepolicy/kernel-sepolicy-version').text
# Load the vendor's plat sepolicy version. This is the version used for
# locating sepolicy mapping files.
vendor_plat_version_file = get_file('vendor',
'etc/selinux/plat_sepolicy_vers.txt')
if not vendor_plat_version_file:
errors.append('Missing required sepolicy file %s' %
vendor_plat_version_file)
return errors
with open(vendor_plat_version_file) as f:
vendor_plat_version = f.read().strip()
# Use the same flags and arguments as selinux.cpp OpenSplitPolicy().
cmd = ['secilc', '-m', '-M', 'true', '-G', '-N']
cmd.extend(['-c', kernel_sepolicy_version])
cmd.extend(['-o', os.path.join(target_files_dir, 'META/combined_sepolicy')])
cmd.extend(['-f', '/dev/null'])
required_policy_files = (
('system', 'etc/selinux/plat_sepolicy.cil'),
('system', 'etc/selinux/mapping/%s.cil' % vendor_plat_version),
('vendor', 'etc/selinux/vendor_sepolicy.cil'),
('vendor', 'etc/selinux/plat_pub_versioned.cil'),
)
for policy in (map(lambda partition_and_path: get_file(*partition_and_path),
required_policy_files)):
if not policy:
errors.append('Missing required sepolicy file %s' % policy)
return errors
cmd.append(policy)
optional_policy_files = (
('system', 'etc/selinux/mapping/%s.compat.cil' % vendor_plat_version),
('system_ext', 'etc/selinux/system_ext_sepolicy.cil'),
('system_ext', 'etc/selinux/mapping/%s.cil' % vendor_plat_version),
('product', 'etc/selinux/product_sepolicy.cil'),
('product', 'etc/selinux/mapping/%s.cil' % vendor_plat_version),
('odm', 'etc/selinux/odm_sepolicy.cil'),
)
for policy in (map(lambda partition_and_path: get_file(*partition_and_path),
optional_policy_files)):
if policy:
cmd.append(policy)
try:
if execute:
common.RunAndCheckOutput(cmd)
else:
return cmd
except RuntimeError as err:
errors.append(str(err))
return errors
def CheckApexDuplicatePackages(target_files_dir, partition_map):
"""Checks if the same APEX package name is provided by multiple partitions."""
errors = []
apex_packages = set()
for partition in partition_map.keys():
try:
apex_info = apex_utils.GetApexInfoForPartition(
target_files_dir, partition)
except RuntimeError as err:
errors.append(str(err))
apex_info = []
partition_apex_packages = set([info.package_name for info in apex_info])
duplicates = apex_packages.intersection(partition_apex_packages)
if duplicates:
errors.append(
'Duplicate APEX package_names found in multiple partitions: %s' %
' '.join(duplicates))
apex_packages.update(partition_apex_packages)
return errors