1*33f37583SAndroid Build Coastguard Worker#!/usr/bin/env python 2*33f37583SAndroid Build Coastguard Worker# 3*33f37583SAndroid Build Coastguard Worker# Copyright (C) 2023 The Android Open Source Project 4*33f37583SAndroid Build Coastguard Worker# 5*33f37583SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 6*33f37583SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 7*33f37583SAndroid Build Coastguard Worker# You may obtain a copy of the License at 8*33f37583SAndroid Build Coastguard Worker# 9*33f37583SAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 10*33f37583SAndroid Build Coastguard Worker# 11*33f37583SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 12*33f37583SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 13*33f37583SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14*33f37583SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 15*33f37583SAndroid Build Coastguard Worker# limitations under the License. 16*33f37583SAndroid Build Coastguard Worker"""apexd_host simulates apexd on host. 17*33f37583SAndroid Build Coastguard Worker 18*33f37583SAndroid Build Coastguard WorkerBasically the tool scans .apex/.capex files from given directories 19*33f37583SAndroid Build Coastguard Worker(e.g --system_path) and extracts them under the output directory (--apex_path) 20*33f37583SAndroid Build Coastguard Workerusing the deapexer tool. It also generates apex-info-list.xml file because 21*33f37583SAndroid Build Coastguard Workersome tools need that file as well to know the partitions of APEX files. 22*33f37583SAndroid Build Coastguard Worker 23*33f37583SAndroid Build Coastguard WorkerThis can be used when handling APEX files on host at buildtime or with 24*33f37583SAndroid Build Coastguard Workertarget-files. For example, check_target_files_vintf tool invokes checkvintf with 25*33f37583SAndroid Build Coastguard Workertarget-files, which, in turn, needs to read VINTF fragment files in APEX files. 26*33f37583SAndroid Build Coastguard WorkerHence, check_target_files_vintf can use apexd_host before checkvintf. 27*33f37583SAndroid Build Coastguard Worker 28*33f37583SAndroid Build Coastguard WorkerExample: 29*33f37583SAndroid Build Coastguard Worker $ apexd_host --apex_path /path/to/apex --system_path /path/to/system 30*33f37583SAndroid Build Coastguard Worker""" 31*33f37583SAndroid Build Coastguard Workerfrom __future__ import print_function 32*33f37583SAndroid Build Coastguard Worker 33*33f37583SAndroid Build Coastguard Workerimport argparse 34*33f37583SAndroid Build Coastguard Workerimport glob 35*33f37583SAndroid Build Coastguard Workerimport os 36*33f37583SAndroid Build Coastguard Workerimport subprocess 37*33f37583SAndroid Build Coastguard Workerimport sys 38*33f37583SAndroid Build Coastguard Workerfrom xml.dom import minidom 39*33f37583SAndroid Build Coastguard Worker 40*33f37583SAndroid Build Coastguard Workerimport apex_manifest 41*33f37583SAndroid Build Coastguard Worker 42*33f37583SAndroid Build Coastguard Worker 43*33f37583SAndroid Build Coastguard Worker# This should be in sync with kApexPackageBuiltinDirs in 44*33f37583SAndroid Build Coastguard Worker# system/apex/apexd/apex_constants.h 45*33f37583SAndroid Build Coastguard WorkerPARTITIONS = ['system', 'system_ext', 'product', 'vendor', 'odm'] 46*33f37583SAndroid Build Coastguard Worker 47*33f37583SAndroid Build Coastguard Worker 48*33f37583SAndroid Build Coastguard Workerdef DirectoryType(path): 49*33f37583SAndroid Build Coastguard Worker if not os.path.exists(path): 50*33f37583SAndroid Build Coastguard Worker return None 51*33f37583SAndroid Build Coastguard Worker if not os.path.isdir(path): 52*33f37583SAndroid Build Coastguard Worker raise argparse.ArgumentTypeError(f'{path} is not a directory') 53*33f37583SAndroid Build Coastguard Worker return os.path.realpath(path) 54*33f37583SAndroid Build Coastguard Worker 55*33f37583SAndroid Build Coastguard Worker 56*33f37583SAndroid Build Coastguard Workerdef ExistentDirectoryType(path): 57*33f37583SAndroid Build Coastguard Worker if not os.path.exists(path): 58*33f37583SAndroid Build Coastguard Worker raise argparse.ArgumentTypeError(f'{path} is not found') 59*33f37583SAndroid Build Coastguard Worker return DirectoryType(path) 60*33f37583SAndroid Build Coastguard Worker 61*33f37583SAndroid Build Coastguard Worker 62*33f37583SAndroid Build Coastguard Workerdef ParseArgs(): 63*33f37583SAndroid Build Coastguard Worker parser = argparse.ArgumentParser() 64*33f37583SAndroid Build Coastguard Worker parser.add_argument('--tool_path', help='Tools are searched in TOOL_PATH/bin') 65*33f37583SAndroid Build Coastguard Worker parser.add_argument( 66*33f37583SAndroid Build Coastguard Worker '--apex_path', 67*33f37583SAndroid Build Coastguard Worker required=True, 68*33f37583SAndroid Build Coastguard Worker type=ExistentDirectoryType, 69*33f37583SAndroid Build Coastguard Worker help='Path to the directory where to activate APEXes', 70*33f37583SAndroid Build Coastguard Worker ) 71*33f37583SAndroid Build Coastguard Worker for part in PARTITIONS: 72*33f37583SAndroid Build Coastguard Worker parser.add_argument( 73*33f37583SAndroid Build Coastguard Worker f'--{part}_path', 74*33f37583SAndroid Build Coastguard Worker help=f'Path to the directory corresponding /{part} on device', 75*33f37583SAndroid Build Coastguard Worker type=DirectoryType, 76*33f37583SAndroid Build Coastguard Worker ) 77*33f37583SAndroid Build Coastguard Worker return parser.parse_args() 78*33f37583SAndroid Build Coastguard Worker 79*33f37583SAndroid Build Coastguard Worker 80*33f37583SAndroid Build Coastguard Workerclass ApexFile(object): 81*33f37583SAndroid Build Coastguard Worker """Represents an APEX file.""" 82*33f37583SAndroid Build Coastguard Worker 83*33f37583SAndroid Build Coastguard Worker def __init__(self, path_on_host, path_on_device): 84*33f37583SAndroid Build Coastguard Worker self._path_on_host = path_on_host 85*33f37583SAndroid Build Coastguard Worker self._path_on_device = path_on_device 86*33f37583SAndroid Build Coastguard Worker self._manifest = apex_manifest.fromApex(path_on_host) 87*33f37583SAndroid Build Coastguard Worker 88*33f37583SAndroid Build Coastguard Worker @property 89*33f37583SAndroid Build Coastguard Worker def name(self): 90*33f37583SAndroid Build Coastguard Worker return self._manifest.name 91*33f37583SAndroid Build Coastguard Worker 92*33f37583SAndroid Build Coastguard Worker @property 93*33f37583SAndroid Build Coastguard Worker def path_on_host(self): 94*33f37583SAndroid Build Coastguard Worker return self._path_on_host 95*33f37583SAndroid Build Coastguard Worker 96*33f37583SAndroid Build Coastguard Worker @property 97*33f37583SAndroid Build Coastguard Worker def path_on_device(self): 98*33f37583SAndroid Build Coastguard Worker return self._path_on_device 99*33f37583SAndroid Build Coastguard Worker 100*33f37583SAndroid Build Coastguard Worker # Helper to create apex-info element 101*33f37583SAndroid Build Coastguard Worker @property 102*33f37583SAndroid Build Coastguard Worker def attrs(self): 103*33f37583SAndroid Build Coastguard Worker return { 104*33f37583SAndroid Build Coastguard Worker 'moduleName': self.name, 105*33f37583SAndroid Build Coastguard Worker 'modulePath': self.path_on_device, 106*33f37583SAndroid Build Coastguard Worker 'preinstalledModulePath': self.path_on_device, 107*33f37583SAndroid Build Coastguard Worker 'versionCode': str(self._manifest.version), 108*33f37583SAndroid Build Coastguard Worker 'versionName': self._manifest.versionName, 109*33f37583SAndroid Build Coastguard Worker 'isFactory': 'true', 110*33f37583SAndroid Build Coastguard Worker 'isActive': 'true', 111*33f37583SAndroid Build Coastguard Worker 'provideSharedApexLibs': ( 112*33f37583SAndroid Build Coastguard Worker 'true' if self._manifest.provideSharedApexLibs else 'false' 113*33f37583SAndroid Build Coastguard Worker ), 114*33f37583SAndroid Build Coastguard Worker } 115*33f37583SAndroid Build Coastguard Worker 116*33f37583SAndroid Build Coastguard Worker 117*33f37583SAndroid Build Coastguard Workerdef InitTools(tool_path): 118*33f37583SAndroid Build Coastguard Worker if tool_path is None: 119*33f37583SAndroid Build Coastguard Worker exec_path = os.path.realpath(sys.argv[0]) 120*33f37583SAndroid Build Coastguard Worker if exec_path.endswith('.py'): 121*33f37583SAndroid Build Coastguard Worker script_name = os.path.basename(exec_path)[:-3] 122*33f37583SAndroid Build Coastguard Worker sys.exit( 123*33f37583SAndroid Build Coastguard Worker f'Do not invoke {exec_path} directly. Instead, use {script_name}' 124*33f37583SAndroid Build Coastguard Worker ) 125*33f37583SAndroid Build Coastguard Worker tool_path = os.path.dirname(os.path.dirname(exec_path)) 126*33f37583SAndroid Build Coastguard Worker 127*33f37583SAndroid Build Coastguard Worker def ToolPath(name): 128*33f37583SAndroid Build Coastguard Worker path = os.path.join(tool_path, 'bin', name) 129*33f37583SAndroid Build Coastguard Worker if not os.path.exists(path): 130*33f37583SAndroid Build Coastguard Worker sys.exit(f'Required tool({name}) not found in {tool_path}') 131*33f37583SAndroid Build Coastguard Worker return path 132*33f37583SAndroid Build Coastguard Worker 133*33f37583SAndroid Build Coastguard Worker return { 134*33f37583SAndroid Build Coastguard Worker tool: ToolPath(tool) 135*33f37583SAndroid Build Coastguard Worker for tool in [ 136*33f37583SAndroid Build Coastguard Worker 'deapexer', 137*33f37583SAndroid Build Coastguard Worker 'debugfs_static', 138*33f37583SAndroid Build Coastguard Worker 'fsck.erofs', 139*33f37583SAndroid Build Coastguard Worker ] 140*33f37583SAndroid Build Coastguard Worker } 141*33f37583SAndroid Build Coastguard Worker 142*33f37583SAndroid Build Coastguard Worker 143*33f37583SAndroid Build Coastguard Workerdef ScanApexes(partition, real_path) -> list[ApexFile]: 144*33f37583SAndroid Build Coastguard Worker apexes = [] 145*33f37583SAndroid Build Coastguard Worker for path_on_host in glob.glob( 146*33f37583SAndroid Build Coastguard Worker os.path.join(real_path, 'apex/*.apex') 147*33f37583SAndroid Build Coastguard Worker ) + glob.glob(os.path.join(real_path, 'apex/*.capex')): 148*33f37583SAndroid Build Coastguard Worker path_on_device = f'/{partition}/apex/' + os.path.basename(path_on_host) 149*33f37583SAndroid Build Coastguard Worker apexes.append(ApexFile(path_on_host, path_on_device)) 150*33f37583SAndroid Build Coastguard Worker # sort list for stability 151*33f37583SAndroid Build Coastguard Worker return sorted(apexes, key=lambda apex: apex.path_on_device) 152*33f37583SAndroid Build Coastguard Worker 153*33f37583SAndroid Build Coastguard Worker 154*33f37583SAndroid Build Coastguard Workerdef ActivateApexes(partitions, apex_dir, tools): 155*33f37583SAndroid Build Coastguard Worker # Emit apex-info-list.xml 156*33f37583SAndroid Build Coastguard Worker impl = minidom.getDOMImplementation() 157*33f37583SAndroid Build Coastguard Worker doc = impl.createDocument(None, 'apex-info-list', None) 158*33f37583SAndroid Build Coastguard Worker apex_info_list = doc.documentElement 159*33f37583SAndroid Build Coastguard Worker 160*33f37583SAndroid Build Coastguard Worker # Scan each partition for apexes and activate them 161*33f37583SAndroid Build Coastguard Worker for partition, real_path in partitions.items(): 162*33f37583SAndroid Build Coastguard Worker apexes = ScanApexes(partition, real_path) 163*33f37583SAndroid Build Coastguard Worker 164*33f37583SAndroid Build Coastguard Worker # Activate each apex with deapexer 165*33f37583SAndroid Build Coastguard Worker for apex_file in apexes: 166*33f37583SAndroid Build Coastguard Worker # Multi-apex is ignored 167*33f37583SAndroid Build Coastguard Worker if os.path.exists(os.path.join(apex_dir, apex_file.name)): 168*33f37583SAndroid Build Coastguard Worker continue 169*33f37583SAndroid Build Coastguard Worker 170*33f37583SAndroid Build Coastguard Worker cmd = [tools['deapexer']] 171*33f37583SAndroid Build Coastguard Worker cmd += ['--debugfs_path', tools['debugfs_static']] 172*33f37583SAndroid Build Coastguard Worker cmd += ['--fsckerofs_path', tools['fsck.erofs']] 173*33f37583SAndroid Build Coastguard Worker cmd += [ 174*33f37583SAndroid Build Coastguard Worker 'extract', 175*33f37583SAndroid Build Coastguard Worker apex_file.path_on_host, 176*33f37583SAndroid Build Coastguard Worker os.path.join(apex_dir, apex_file.name), 177*33f37583SAndroid Build Coastguard Worker ] 178*33f37583SAndroid Build Coastguard Worker subprocess.check_output(cmd, text=True, stderr=subprocess.STDOUT) 179*33f37583SAndroid Build Coastguard Worker 180*33f37583SAndroid Build Coastguard Worker # Add activated apex_info to apex_info_list 181*33f37583SAndroid Build Coastguard Worker apex_info = doc.createElement('apex-info') 182*33f37583SAndroid Build Coastguard Worker for name, value in apex_file.attrs.items(): 183*33f37583SAndroid Build Coastguard Worker apex_info.setAttribute(name, value) 184*33f37583SAndroid Build Coastguard Worker apex_info_list.appendChild(apex_info) 185*33f37583SAndroid Build Coastguard Worker 186*33f37583SAndroid Build Coastguard Worker apex_info_list_file = os.path.join(apex_dir, 'apex-info-list.xml') 187*33f37583SAndroid Build Coastguard Worker with open(apex_info_list_file, 'wt', encoding='utf-8') as f: 188*33f37583SAndroid Build Coastguard Worker doc.writexml(f, encoding='utf-8', addindent=' ', newl='\n') 189*33f37583SAndroid Build Coastguard Worker 190*33f37583SAndroid Build Coastguard Worker 191*33f37583SAndroid Build Coastguard Workerdef main(): 192*33f37583SAndroid Build Coastguard Worker args = ParseArgs() 193*33f37583SAndroid Build Coastguard Worker 194*33f37583SAndroid Build Coastguard Worker partitions = {} 195*33f37583SAndroid Build Coastguard Worker for part in PARTITIONS: 196*33f37583SAndroid Build Coastguard Worker if vars(args).get(f'{part}_path'): 197*33f37583SAndroid Build Coastguard Worker partitions[part] = vars(args).get(f'{part}_path') 198*33f37583SAndroid Build Coastguard Worker 199*33f37583SAndroid Build Coastguard Worker tools = InitTools(args.tool_path) 200*33f37583SAndroid Build Coastguard Worker ActivateApexes(partitions, args.apex_path, tools) 201*33f37583SAndroid Build Coastguard Worker 202*33f37583SAndroid Build Coastguard Worker 203*33f37583SAndroid Build Coastguard Workerif __name__ == '__main__': 204*33f37583SAndroid Build Coastguard Worker main() 205