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
207 lines
7.1 KiB
Python
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
|