1*33f37583SAndroid Build Coastguard Worker#!/usr/bin/env python3 2*33f37583SAndroid Build Coastguard Worker# 3*33f37583SAndroid Build Coastguard Worker# Copyright (C) 2018 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"""apexer is a command line tool for creating an APEX file, a package format for system components. 17*33f37583SAndroid Build Coastguard Worker 18*33f37583SAndroid Build Coastguard WorkerTypical usage: apexer input_dir output.apex 19*33f37583SAndroid Build Coastguard Worker 20*33f37583SAndroid Build Coastguard Worker""" 21*33f37583SAndroid Build Coastguard Worker 22*33f37583SAndroid Build Coastguard Workerimport apex_build_info_pb2 23*33f37583SAndroid Build Coastguard Workerimport argparse 24*33f37583SAndroid Build Coastguard Workerimport hashlib 25*33f37583SAndroid Build Coastguard Workerimport os 26*33f37583SAndroid Build Coastguard Workerimport pkgutil 27*33f37583SAndroid Build Coastguard Workerimport re 28*33f37583SAndroid Build Coastguard Workerimport shlex 29*33f37583SAndroid Build Coastguard Workerimport shutil 30*33f37583SAndroid Build Coastguard Workerimport subprocess 31*33f37583SAndroid Build Coastguard Workerimport sys 32*33f37583SAndroid Build Coastguard Workerimport tempfile 33*33f37583SAndroid Build Coastguard Workerimport uuid 34*33f37583SAndroid Build Coastguard Workerimport xml.etree.ElementTree as ET 35*33f37583SAndroid Build Coastguard Workerimport zipfile 36*33f37583SAndroid Build Coastguard Workerimport glob 37*33f37583SAndroid Build Coastguard Workerfrom apex_manifest import ValidateApexManifest 38*33f37583SAndroid Build Coastguard Workerfrom apex_manifest import ApexManifestError 39*33f37583SAndroid Build Coastguard Workerfrom apex_manifest import ParseApexManifest 40*33f37583SAndroid Build Coastguard Workerfrom manifest import android_ns 41*33f37583SAndroid Build Coastguard Workerfrom manifest import find_child_with_attribute 42*33f37583SAndroid Build Coastguard Workerfrom manifest import get_children_with_tag 43*33f37583SAndroid Build Coastguard Workerfrom manifest import get_indent 44*33f37583SAndroid Build Coastguard Workerfrom manifest import parse_manifest 45*33f37583SAndroid Build Coastguard Workerfrom manifest import write_xml 46*33f37583SAndroid Build Coastguard Workerfrom xml.dom import minidom 47*33f37583SAndroid Build Coastguard Worker 48*33f37583SAndroid Build Coastguard Workertool_path_list = None 49*33f37583SAndroid Build Coastguard WorkerBLOCK_SIZE = 4096 50*33f37583SAndroid Build Coastguard Worker 51*33f37583SAndroid Build Coastguard Worker 52*33f37583SAndroid Build Coastguard Workerdef ParseArgs(argv): 53*33f37583SAndroid Build Coastguard Worker parser = argparse.ArgumentParser(description='Create an APEX file') 54*33f37583SAndroid Build Coastguard Worker parser.add_argument( 55*33f37583SAndroid Build Coastguard Worker '-f', '--force', action='store_true', help='force overwriting output') 56*33f37583SAndroid Build Coastguard Worker parser.add_argument( 57*33f37583SAndroid Build Coastguard Worker '-v', '--verbose', action='store_true', help='verbose execution') 58*33f37583SAndroid Build Coastguard Worker parser.add_argument( 59*33f37583SAndroid Build Coastguard Worker '--manifest', 60*33f37583SAndroid Build Coastguard Worker default='apex_manifest.pb', 61*33f37583SAndroid Build Coastguard Worker help='path to the APEX manifest file (.pb)') 62*33f37583SAndroid Build Coastguard Worker parser.add_argument( 63*33f37583SAndroid Build Coastguard Worker '--manifest_json', 64*33f37583SAndroid Build Coastguard Worker required=False, 65*33f37583SAndroid Build Coastguard Worker help='path to the APEX manifest file (Q compatible .json)') 66*33f37583SAndroid Build Coastguard Worker parser.add_argument( 67*33f37583SAndroid Build Coastguard Worker '--android_manifest', 68*33f37583SAndroid Build Coastguard Worker help='path to the AndroidManifest file. If omitted, a default one is created and used' 69*33f37583SAndroid Build Coastguard Worker ) 70*33f37583SAndroid Build Coastguard Worker parser.add_argument( 71*33f37583SAndroid Build Coastguard Worker '--logging_parent', 72*33f37583SAndroid Build Coastguard Worker help=('specify logging parent as an additional <meta-data> tag.' 73*33f37583SAndroid Build Coastguard Worker 'This value is ignored if the logging_parent meta-data tag is present.')) 74*33f37583SAndroid Build Coastguard Worker parser.add_argument( 75*33f37583SAndroid Build Coastguard Worker '--assets_dir', 76*33f37583SAndroid Build Coastguard Worker help='an assets directory to be included in the APEX' 77*33f37583SAndroid Build Coastguard Worker ) 78*33f37583SAndroid Build Coastguard Worker parser.add_argument( 79*33f37583SAndroid Build Coastguard Worker '--file_contexts', 80*33f37583SAndroid Build Coastguard Worker help='selinux file contexts file. Required for "image" APEXs.') 81*33f37583SAndroid Build Coastguard Worker parser.add_argument( 82*33f37583SAndroid Build Coastguard Worker '--canned_fs_config', 83*33f37583SAndroid Build Coastguard Worker help='canned_fs_config specifies uid/gid/mode of files. Required for ' + 84*33f37583SAndroid Build Coastguard Worker '"image" APEXS.') 85*33f37583SAndroid Build Coastguard Worker parser.add_argument( 86*33f37583SAndroid Build Coastguard Worker '--key', help='path to the private key file. Required for "image" APEXs.') 87*33f37583SAndroid Build Coastguard Worker parser.add_argument( 88*33f37583SAndroid Build Coastguard Worker '--pubkey', 89*33f37583SAndroid Build Coastguard Worker help='path to the public key file. Used to bundle the public key in APEX for testing.' 90*33f37583SAndroid Build Coastguard Worker ) 91*33f37583SAndroid Build Coastguard Worker parser.add_argument( 92*33f37583SAndroid Build Coastguard Worker '--signing_args', 93*33f37583SAndroid Build Coastguard Worker help='the extra signing arguments passed to avbtool. Used for "image" APEXs.' 94*33f37583SAndroid Build Coastguard Worker ) 95*33f37583SAndroid Build Coastguard Worker parser.add_argument( 96*33f37583SAndroid Build Coastguard Worker 'input_dir', 97*33f37583SAndroid Build Coastguard Worker metavar='INPUT_DIR', 98*33f37583SAndroid Build Coastguard Worker help='the directory having files to be packaged') 99*33f37583SAndroid Build Coastguard Worker parser.add_argument('output', metavar='OUTPUT', help='name of the APEX file') 100*33f37583SAndroid Build Coastguard Worker parser.add_argument( 101*33f37583SAndroid Build Coastguard Worker '--payload_type', 102*33f37583SAndroid Build Coastguard Worker metavar='TYPE', 103*33f37583SAndroid Build Coastguard Worker required=False, 104*33f37583SAndroid Build Coastguard Worker default='image', 105*33f37583SAndroid Build Coastguard Worker choices=['image'], 106*33f37583SAndroid Build Coastguard Worker help='type of APEX payload being built..') 107*33f37583SAndroid Build Coastguard Worker parser.add_argument( 108*33f37583SAndroid Build Coastguard Worker '--payload_fs_type', 109*33f37583SAndroid Build Coastguard Worker metavar='FS_TYPE', 110*33f37583SAndroid Build Coastguard Worker required=False, 111*33f37583SAndroid Build Coastguard Worker choices=['ext4', 'f2fs', 'erofs'], 112*33f37583SAndroid Build Coastguard Worker help='type of filesystem being used for payload image "ext4", "f2fs" or "erofs"') 113*33f37583SAndroid Build Coastguard Worker parser.add_argument( 114*33f37583SAndroid Build Coastguard Worker '--override_apk_package_name', 115*33f37583SAndroid Build Coastguard Worker required=False, 116*33f37583SAndroid Build Coastguard Worker help='package name of the APK container. Default is the apex name in --manifest.' 117*33f37583SAndroid Build Coastguard Worker ) 118*33f37583SAndroid Build Coastguard Worker parser.add_argument( 119*33f37583SAndroid Build Coastguard Worker '--no_hashtree', 120*33f37583SAndroid Build Coastguard Worker required=False, 121*33f37583SAndroid Build Coastguard Worker action='store_true', 122*33f37583SAndroid Build Coastguard Worker help='hashtree is omitted from "image".' 123*33f37583SAndroid Build Coastguard Worker ) 124*33f37583SAndroid Build Coastguard Worker parser.add_argument( 125*33f37583SAndroid Build Coastguard Worker '--android_jar_path', 126*33f37583SAndroid Build Coastguard Worker required=False, 127*33f37583SAndroid Build Coastguard Worker default='prebuilts/sdk/current/public/android.jar', 128*33f37583SAndroid Build Coastguard Worker help='path to use as the source of the android API.') 129*33f37583SAndroid Build Coastguard Worker apexer_path_in_environ = 'APEXER_TOOL_PATH' in os.environ 130*33f37583SAndroid Build Coastguard Worker parser.add_argument( 131*33f37583SAndroid Build Coastguard Worker '--apexer_tool_path', 132*33f37583SAndroid Build Coastguard Worker required=not apexer_path_in_environ, 133*33f37583SAndroid Build Coastguard Worker default=os.environ['APEXER_TOOL_PATH'].split(':') 134*33f37583SAndroid Build Coastguard Worker if apexer_path_in_environ else None, 135*33f37583SAndroid Build Coastguard Worker type=lambda s: s.split(':'), 136*33f37583SAndroid Build Coastguard Worker help="""A list of directories containing all the tools used by apexer (e.g. 137*33f37583SAndroid Build Coastguard Worker mke2fs, avbtool, etc.) separated by ':'. Can also be set using the 138*33f37583SAndroid Build Coastguard Worker APEXER_TOOL_PATH environment variable""") 139*33f37583SAndroid Build Coastguard Worker parser.add_argument( 140*33f37583SAndroid Build Coastguard Worker '--target_sdk_version', 141*33f37583SAndroid Build Coastguard Worker required=False, 142*33f37583SAndroid Build Coastguard Worker help='Default target SDK version to use for AndroidManifest.xml') 143*33f37583SAndroid Build Coastguard Worker parser.add_argument( 144*33f37583SAndroid Build Coastguard Worker '--min_sdk_version', 145*33f37583SAndroid Build Coastguard Worker required=False, 146*33f37583SAndroid Build Coastguard Worker help='Default Min SDK version to use for AndroidManifest.xml') 147*33f37583SAndroid Build Coastguard Worker parser.add_argument( 148*33f37583SAndroid Build Coastguard Worker '--do_not_check_keyname', 149*33f37583SAndroid Build Coastguard Worker required=False, 150*33f37583SAndroid Build Coastguard Worker action='store_true', 151*33f37583SAndroid Build Coastguard Worker help='Do not check key name. Use the name of apex instead of the basename of --key.') 152*33f37583SAndroid Build Coastguard Worker parser.add_argument( 153*33f37583SAndroid Build Coastguard Worker '--include_build_info', 154*33f37583SAndroid Build Coastguard Worker required=False, 155*33f37583SAndroid Build Coastguard Worker action='store_true', 156*33f37583SAndroid Build Coastguard Worker help='Include build information file in the resulting apex.') 157*33f37583SAndroid Build Coastguard Worker parser.add_argument( 158*33f37583SAndroid Build Coastguard Worker '--include_cmd_line_in_build_info', 159*33f37583SAndroid Build Coastguard Worker required=False, 160*33f37583SAndroid Build Coastguard Worker action='store_true', 161*33f37583SAndroid Build Coastguard Worker help='Include the command line in the build information file in the resulting apex. ' 162*33f37583SAndroid Build Coastguard Worker 'Note that this makes it harder to make deterministic builds.') 163*33f37583SAndroid Build Coastguard Worker parser.add_argument( 164*33f37583SAndroid Build Coastguard Worker '--build_info', 165*33f37583SAndroid Build Coastguard Worker required=False, 166*33f37583SAndroid Build Coastguard Worker help='Build information file to be used for default values.') 167*33f37583SAndroid Build Coastguard Worker parser.add_argument( 168*33f37583SAndroid Build Coastguard Worker '--payload_only', 169*33f37583SAndroid Build Coastguard Worker action='store_true', 170*33f37583SAndroid Build Coastguard Worker help='Outputs the payload image/zip only.' 171*33f37583SAndroid Build Coastguard Worker ) 172*33f37583SAndroid Build Coastguard Worker parser.add_argument( 173*33f37583SAndroid Build Coastguard Worker '--unsigned_payload_only', 174*33f37583SAndroid Build Coastguard Worker action='store_true', 175*33f37583SAndroid Build Coastguard Worker help="""Outputs the unsigned payload image/zip only. Also, setting this flag implies 176*33f37583SAndroid Build Coastguard Worker --payload_only is set too.""" 177*33f37583SAndroid Build Coastguard Worker ) 178*33f37583SAndroid Build Coastguard Worker parser.add_argument( 179*33f37583SAndroid Build Coastguard Worker '--unsigned_payload', 180*33f37583SAndroid Build Coastguard Worker action='store_true', 181*33f37583SAndroid Build Coastguard Worker help="""Skip signing the apex payload. Used only for testing purposes.""" 182*33f37583SAndroid Build Coastguard Worker ) 183*33f37583SAndroid Build Coastguard Worker parser.add_argument( 184*33f37583SAndroid Build Coastguard Worker '--test_only', 185*33f37583SAndroid Build Coastguard Worker action='store_true', 186*33f37583SAndroid Build Coastguard Worker help=( 187*33f37583SAndroid Build Coastguard Worker 'Add testOnly=true attribute to application element in ' 188*33f37583SAndroid Build Coastguard Worker 'AndroidManifest file.') 189*33f37583SAndroid Build Coastguard Worker ) 190*33f37583SAndroid Build Coastguard Worker 191*33f37583SAndroid Build Coastguard Worker return parser.parse_args(argv) 192*33f37583SAndroid Build Coastguard Worker 193*33f37583SAndroid Build Coastguard Worker 194*33f37583SAndroid Build Coastguard Workerdef FindBinaryPath(binary): 195*33f37583SAndroid Build Coastguard Worker for path in tool_path_list: 196*33f37583SAndroid Build Coastguard Worker binary_path = os.path.join(path, binary) 197*33f37583SAndroid Build Coastguard Worker if os.path.exists(binary_path): 198*33f37583SAndroid Build Coastguard Worker return binary_path 199*33f37583SAndroid Build Coastguard Worker raise Exception('Failed to find binary ' + binary + ' in path ' + 200*33f37583SAndroid Build Coastguard Worker ':'.join(tool_path_list)) 201*33f37583SAndroid Build Coastguard Worker 202*33f37583SAndroid Build Coastguard Worker 203*33f37583SAndroid Build Coastguard Workerdef RunCommand(cmd, verbose=False, env=None, expected_return_values={0}): 204*33f37583SAndroid Build Coastguard Worker env = env or {} 205*33f37583SAndroid Build Coastguard Worker env.update(os.environ.copy()) 206*33f37583SAndroid Build Coastguard Worker 207*33f37583SAndroid Build Coastguard Worker cmd[0] = FindBinaryPath(cmd[0]) 208*33f37583SAndroid Build Coastguard Worker 209*33f37583SAndroid Build Coastguard Worker if verbose: 210*33f37583SAndroid Build Coastguard Worker print('Running: ' + ' '.join(cmd)) 211*33f37583SAndroid Build Coastguard Worker p = subprocess.Popen( 212*33f37583SAndroid Build Coastguard Worker cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=env) 213*33f37583SAndroid Build Coastguard Worker output, _ = p.communicate() 214*33f37583SAndroid Build Coastguard Worker output = output.decode() 215*33f37583SAndroid Build Coastguard Worker 216*33f37583SAndroid Build Coastguard Worker if verbose or p.returncode not in expected_return_values: 217*33f37583SAndroid Build Coastguard Worker print(output.rstrip()) 218*33f37583SAndroid Build Coastguard Worker 219*33f37583SAndroid Build Coastguard Worker assert p.returncode in expected_return_values, 'Failed to execute: ' + ' '.join(cmd) 220*33f37583SAndroid Build Coastguard Worker 221*33f37583SAndroid Build Coastguard Worker return (output, p.returncode) 222*33f37583SAndroid Build Coastguard Worker 223*33f37583SAndroid Build Coastguard Worker 224*33f37583SAndroid Build Coastguard Workerdef GetDirSize(dir_name): 225*33f37583SAndroid Build Coastguard Worker size = 0 226*33f37583SAndroid Build Coastguard Worker for dirpath, _, filenames in os.walk(dir_name): 227*33f37583SAndroid Build Coastguard Worker size += RoundUp(os.path.getsize(dirpath), BLOCK_SIZE) 228*33f37583SAndroid Build Coastguard Worker for f in filenames: 229*33f37583SAndroid Build Coastguard Worker path = os.path.join(dirpath, f) 230*33f37583SAndroid Build Coastguard Worker if not os.path.isfile(path): 231*33f37583SAndroid Build Coastguard Worker continue 232*33f37583SAndroid Build Coastguard Worker size += RoundUp(os.path.getsize(path), BLOCK_SIZE) 233*33f37583SAndroid Build Coastguard Worker return size 234*33f37583SAndroid Build Coastguard Worker 235*33f37583SAndroid Build Coastguard Worker 236*33f37583SAndroid Build Coastguard Workerdef GetFilesAndDirsCount(dir_name): 237*33f37583SAndroid Build Coastguard Worker count = 0 238*33f37583SAndroid Build Coastguard Worker for root, dirs, files in os.walk(dir_name): 239*33f37583SAndroid Build Coastguard Worker count += (len(dirs) + len(files)) 240*33f37583SAndroid Build Coastguard Worker return count 241*33f37583SAndroid Build Coastguard Worker 242*33f37583SAndroid Build Coastguard Worker 243*33f37583SAndroid Build Coastguard Workerdef RoundUp(size, unit): 244*33f37583SAndroid Build Coastguard Worker assert unit & (unit - 1) == 0 245*33f37583SAndroid Build Coastguard Worker return (size + unit - 1) & (~(unit - 1)) 246*33f37583SAndroid Build Coastguard Worker 247*33f37583SAndroid Build Coastguard Worker 248*33f37583SAndroid Build Coastguard Workerdef PrepareAndroidManifest(package, version, test_only): 249*33f37583SAndroid Build Coastguard Worker template = """\ 250*33f37583SAndroid Build Coastguard Worker<?xml version="1.0" encoding="utf-8"?> 251*33f37583SAndroid Build Coastguard Worker<manifest xmlns:android="http://schemas.android.com/apk/res/android" 252*33f37583SAndroid Build Coastguard Worker package="{package}" android:versionCode="{version}"> 253*33f37583SAndroid Build Coastguard Worker <!-- APEX does not have classes.dex --> 254*33f37583SAndroid Build Coastguard Worker <application android:hasCode="false" {test_only_attribute}/> 255*33f37583SAndroid Build Coastguard Worker</manifest> 256*33f37583SAndroid Build Coastguard Worker""" 257*33f37583SAndroid Build Coastguard Worker 258*33f37583SAndroid Build Coastguard Worker test_only_attribute = 'android:testOnly="true"' if test_only else '' 259*33f37583SAndroid Build Coastguard Worker return template.format(package=package, version=version, 260*33f37583SAndroid Build Coastguard Worker test_only_attribute=test_only_attribute) 261*33f37583SAndroid Build Coastguard Worker 262*33f37583SAndroid Build Coastguard Worker 263*33f37583SAndroid Build Coastguard Workerdef ValidateAndroidManifest(package, android_manifest): 264*33f37583SAndroid Build Coastguard Worker tree = ET.parse(android_manifest) 265*33f37583SAndroid Build Coastguard Worker manifest_tag = tree.getroot() 266*33f37583SAndroid Build Coastguard Worker package_in_xml = manifest_tag.attrib['package'] 267*33f37583SAndroid Build Coastguard Worker if package_in_xml != package: 268*33f37583SAndroid Build Coastguard Worker raise Exception("Package name '" + package_in_xml + "' in '" + 269*33f37583SAndroid Build Coastguard Worker android_manifest + " differ from package name '" + package + 270*33f37583SAndroid Build Coastguard Worker "' in the apex_manifest.pb") 271*33f37583SAndroid Build Coastguard Worker 272*33f37583SAndroid Build Coastguard Worker 273*33f37583SAndroid Build Coastguard Workerdef ValidateGeneratedAndroidManifest(android_manifest, test_only): 274*33f37583SAndroid Build Coastguard Worker tree = ET.parse(android_manifest) 275*33f37583SAndroid Build Coastguard Worker manifest_tag = tree.getroot() 276*33f37583SAndroid Build Coastguard Worker application_tag = manifest_tag.find('./application') 277*33f37583SAndroid Build Coastguard Worker if test_only: 278*33f37583SAndroid Build Coastguard Worker test_only_in_xml = application_tag.attrib[ 279*33f37583SAndroid Build Coastguard Worker '{http://schemas.android.com/apk/res/android}testOnly'] 280*33f37583SAndroid Build Coastguard Worker if test_only_in_xml != 'true': 281*33f37583SAndroid Build Coastguard Worker raise Exception('testOnly attribute must be equal to true.') 282*33f37583SAndroid Build Coastguard Worker 283*33f37583SAndroid Build Coastguard Worker 284*33f37583SAndroid Build Coastguard Workerdef ValidateArgs(args): 285*33f37583SAndroid Build Coastguard Worker build_info = None 286*33f37583SAndroid Build Coastguard Worker 287*33f37583SAndroid Build Coastguard Worker if args.build_info is not None: 288*33f37583SAndroid Build Coastguard Worker if not os.path.exists(args.build_info): 289*33f37583SAndroid Build Coastguard Worker print("Build info file '" + args.build_info + "' does not exist") 290*33f37583SAndroid Build Coastguard Worker return False 291*33f37583SAndroid Build Coastguard Worker with open(args.build_info, 'rb') as buildInfoFile: 292*33f37583SAndroid Build Coastguard Worker build_info = apex_build_info_pb2.ApexBuildInfo() 293*33f37583SAndroid Build Coastguard Worker build_info.ParseFromString(buildInfoFile.read()) 294*33f37583SAndroid Build Coastguard Worker 295*33f37583SAndroid Build Coastguard Worker if not os.path.exists(args.manifest): 296*33f37583SAndroid Build Coastguard Worker print("Manifest file '" + args.manifest + "' does not exist") 297*33f37583SAndroid Build Coastguard Worker return False 298*33f37583SAndroid Build Coastguard Worker 299*33f37583SAndroid Build Coastguard Worker if not os.path.isfile(args.manifest): 300*33f37583SAndroid Build Coastguard Worker print("Manifest file '" + args.manifest + "' is not a file") 301*33f37583SAndroid Build Coastguard Worker return False 302*33f37583SAndroid Build Coastguard Worker 303*33f37583SAndroid Build Coastguard Worker if args.android_manifest is not None: 304*33f37583SAndroid Build Coastguard Worker if not os.path.exists(args.android_manifest): 305*33f37583SAndroid Build Coastguard Worker print("Android Manifest file '" + args.android_manifest + 306*33f37583SAndroid Build Coastguard Worker "' does not exist") 307*33f37583SAndroid Build Coastguard Worker return False 308*33f37583SAndroid Build Coastguard Worker 309*33f37583SAndroid Build Coastguard Worker if not os.path.isfile(args.android_manifest): 310*33f37583SAndroid Build Coastguard Worker print("Android Manifest file '" + args.android_manifest + 311*33f37583SAndroid Build Coastguard Worker "' is not a file") 312*33f37583SAndroid Build Coastguard Worker return False 313*33f37583SAndroid Build Coastguard Worker elif build_info is not None: 314*33f37583SAndroid Build Coastguard Worker with tempfile.NamedTemporaryFile(delete=False) as temp: 315*33f37583SAndroid Build Coastguard Worker temp.write(build_info.android_manifest) 316*33f37583SAndroid Build Coastguard Worker args.android_manifest = temp.name 317*33f37583SAndroid Build Coastguard Worker 318*33f37583SAndroid Build Coastguard Worker if not os.path.exists(args.input_dir): 319*33f37583SAndroid Build Coastguard Worker print("Input directory '" + args.input_dir + "' does not exist") 320*33f37583SAndroid Build Coastguard Worker return False 321*33f37583SAndroid Build Coastguard Worker 322*33f37583SAndroid Build Coastguard Worker if not os.path.isdir(args.input_dir): 323*33f37583SAndroid Build Coastguard Worker print("Input directory '" + args.input_dir + "' is not a directory") 324*33f37583SAndroid Build Coastguard Worker return False 325*33f37583SAndroid Build Coastguard Worker 326*33f37583SAndroid Build Coastguard Worker if not args.force and os.path.exists(args.output): 327*33f37583SAndroid Build Coastguard Worker print(args.output + ' already exists. Use --force to overwrite.') 328*33f37583SAndroid Build Coastguard Worker return False 329*33f37583SAndroid Build Coastguard Worker 330*33f37583SAndroid Build Coastguard Worker if args.unsigned_payload_only: 331*33f37583SAndroid Build Coastguard Worker args.payload_only = True; 332*33f37583SAndroid Build Coastguard Worker args.unsigned_payload = True; 333*33f37583SAndroid Build Coastguard Worker 334*33f37583SAndroid Build Coastguard Worker if not args.key and not args.unsigned_payload: 335*33f37583SAndroid Build Coastguard Worker print('Missing --key {keyfile} argument!') 336*33f37583SAndroid Build Coastguard Worker return False 337*33f37583SAndroid Build Coastguard Worker 338*33f37583SAndroid Build Coastguard Worker if not args.file_contexts: 339*33f37583SAndroid Build Coastguard Worker if build_info is not None: 340*33f37583SAndroid Build Coastguard Worker with tempfile.NamedTemporaryFile(delete=False) as temp: 341*33f37583SAndroid Build Coastguard Worker temp.write(build_info.file_contexts) 342*33f37583SAndroid Build Coastguard Worker args.file_contexts = temp.name 343*33f37583SAndroid Build Coastguard Worker else: 344*33f37583SAndroid Build Coastguard Worker print('Missing --file_contexts {contexts} argument, or a --build_info argument!') 345*33f37583SAndroid Build Coastguard Worker return False 346*33f37583SAndroid Build Coastguard Worker 347*33f37583SAndroid Build Coastguard Worker if not args.canned_fs_config: 348*33f37583SAndroid Build Coastguard Worker if build_info is not None: 349*33f37583SAndroid Build Coastguard Worker with tempfile.NamedTemporaryFile(delete=False) as temp: 350*33f37583SAndroid Build Coastguard Worker temp.write(build_info.canned_fs_config) 351*33f37583SAndroid Build Coastguard Worker args.canned_fs_config = temp.name 352*33f37583SAndroid Build Coastguard Worker else: 353*33f37583SAndroid Build Coastguard Worker print('Missing --canned_fs_config {config} argument, or a --build_info argument!') 354*33f37583SAndroid Build Coastguard Worker return False 355*33f37583SAndroid Build Coastguard Worker 356*33f37583SAndroid Build Coastguard Worker if not args.target_sdk_version: 357*33f37583SAndroid Build Coastguard Worker if build_info is not None: 358*33f37583SAndroid Build Coastguard Worker if build_info.target_sdk_version: 359*33f37583SAndroid Build Coastguard Worker args.target_sdk_version = build_info.target_sdk_version 360*33f37583SAndroid Build Coastguard Worker 361*33f37583SAndroid Build Coastguard Worker if not args.no_hashtree: 362*33f37583SAndroid Build Coastguard Worker if build_info is not None: 363*33f37583SAndroid Build Coastguard Worker if build_info.no_hashtree: 364*33f37583SAndroid Build Coastguard Worker args.no_hashtree = True 365*33f37583SAndroid Build Coastguard Worker 366*33f37583SAndroid Build Coastguard Worker if not args.min_sdk_version: 367*33f37583SAndroid Build Coastguard Worker if build_info is not None: 368*33f37583SAndroid Build Coastguard Worker if build_info.min_sdk_version: 369*33f37583SAndroid Build Coastguard Worker args.min_sdk_version = build_info.min_sdk_version 370*33f37583SAndroid Build Coastguard Worker 371*33f37583SAndroid Build Coastguard Worker if not args.override_apk_package_name: 372*33f37583SAndroid Build Coastguard Worker if build_info is not None: 373*33f37583SAndroid Build Coastguard Worker if build_info.override_apk_package_name: 374*33f37583SAndroid Build Coastguard Worker args.override_apk_package_name = build_info.override_apk_package_name 375*33f37583SAndroid Build Coastguard Worker 376*33f37583SAndroid Build Coastguard Worker if not args.logging_parent: 377*33f37583SAndroid Build Coastguard Worker if build_info is not None: 378*33f37583SAndroid Build Coastguard Worker if build_info.logging_parent: 379*33f37583SAndroid Build Coastguard Worker args.logging_parent = build_info.logging_parent 380*33f37583SAndroid Build Coastguard Worker 381*33f37583SAndroid Build Coastguard Worker if not args.payload_fs_type: 382*33f37583SAndroid Build Coastguard Worker if build_info and build_info.payload_fs_type: 383*33f37583SAndroid Build Coastguard Worker args.payload_fs_type = build_info.payload_fs_type 384*33f37583SAndroid Build Coastguard Worker else: 385*33f37583SAndroid Build Coastguard Worker args.payload_fs_type = 'ext4' 386*33f37583SAndroid Build Coastguard Worker 387*33f37583SAndroid Build Coastguard Worker return True 388*33f37583SAndroid Build Coastguard Worker 389*33f37583SAndroid Build Coastguard Worker 390*33f37583SAndroid Build Coastguard Workerdef GenerateBuildInfo(args): 391*33f37583SAndroid Build Coastguard Worker build_info = apex_build_info_pb2.ApexBuildInfo() 392*33f37583SAndroid Build Coastguard Worker if (args.include_cmd_line_in_build_info): 393*33f37583SAndroid Build Coastguard Worker build_info.apexer_command_line = str(sys.argv) 394*33f37583SAndroid Build Coastguard Worker 395*33f37583SAndroid Build Coastguard Worker with open(args.file_contexts, 'rb') as f: 396*33f37583SAndroid Build Coastguard Worker build_info.file_contexts = f.read() 397*33f37583SAndroid Build Coastguard Worker 398*33f37583SAndroid Build Coastguard Worker with open(args.canned_fs_config, 'rb') as f: 399*33f37583SAndroid Build Coastguard Worker build_info.canned_fs_config = f.read() 400*33f37583SAndroid Build Coastguard Worker 401*33f37583SAndroid Build Coastguard Worker with open(args.android_manifest, 'rb') as f: 402*33f37583SAndroid Build Coastguard Worker build_info.android_manifest = f.read() 403*33f37583SAndroid Build Coastguard Worker 404*33f37583SAndroid Build Coastguard Worker if args.target_sdk_version: 405*33f37583SAndroid Build Coastguard Worker build_info.target_sdk_version = args.target_sdk_version 406*33f37583SAndroid Build Coastguard Worker 407*33f37583SAndroid Build Coastguard Worker if args.min_sdk_version: 408*33f37583SAndroid Build Coastguard Worker build_info.min_sdk_version = args.min_sdk_version 409*33f37583SAndroid Build Coastguard Worker 410*33f37583SAndroid Build Coastguard Worker if args.no_hashtree: 411*33f37583SAndroid Build Coastguard Worker build_info.no_hashtree = True 412*33f37583SAndroid Build Coastguard Worker 413*33f37583SAndroid Build Coastguard Worker if args.override_apk_package_name: 414*33f37583SAndroid Build Coastguard Worker build_info.override_apk_package_name = args.override_apk_package_name 415*33f37583SAndroid Build Coastguard Worker 416*33f37583SAndroid Build Coastguard Worker if args.logging_parent: 417*33f37583SAndroid Build Coastguard Worker build_info.logging_parent = args.logging_parent 418*33f37583SAndroid Build Coastguard Worker 419*33f37583SAndroid Build Coastguard Worker if args.payload_type == 'image': 420*33f37583SAndroid Build Coastguard Worker build_info.payload_fs_type = args.payload_fs_type 421*33f37583SAndroid Build Coastguard Worker 422*33f37583SAndroid Build Coastguard Worker return build_info 423*33f37583SAndroid Build Coastguard Worker 424*33f37583SAndroid Build Coastguard Worker 425*33f37583SAndroid Build Coastguard Workerdef AddLoggingParent(android_manifest, logging_parent_value): 426*33f37583SAndroid Build Coastguard Worker """Add logging parent as an additional <meta-data> tag. 427*33f37583SAndroid Build Coastguard Worker 428*33f37583SAndroid Build Coastguard Worker Args: 429*33f37583SAndroid Build Coastguard Worker android_manifest: A string representing AndroidManifest.xml 430*33f37583SAndroid Build Coastguard Worker logging_parent_value: A string representing the logging 431*33f37583SAndroid Build Coastguard Worker parent value. 432*33f37583SAndroid Build Coastguard Worker Raises: 433*33f37583SAndroid Build Coastguard Worker RuntimeError: Invalid manifest 434*33f37583SAndroid Build Coastguard Worker Returns: 435*33f37583SAndroid Build Coastguard Worker A path to modified AndroidManifest.xml 436*33f37583SAndroid Build Coastguard Worker """ 437*33f37583SAndroid Build Coastguard Worker doc = minidom.parse(android_manifest) 438*33f37583SAndroid Build Coastguard Worker manifest = parse_manifest(doc) 439*33f37583SAndroid Build Coastguard Worker logging_parent_key = 'android.content.pm.LOGGING_PARENT' 440*33f37583SAndroid Build Coastguard Worker elems = get_children_with_tag(manifest, 'application') 441*33f37583SAndroid Build Coastguard Worker application = elems[0] if len(elems) == 1 else None 442*33f37583SAndroid Build Coastguard Worker if len(elems) > 1: 443*33f37583SAndroid Build Coastguard Worker raise RuntimeError('found multiple <application> tags') 444*33f37583SAndroid Build Coastguard Worker elif not elems: 445*33f37583SAndroid Build Coastguard Worker application = doc.createElement('application') 446*33f37583SAndroid Build Coastguard Worker indent = get_indent(manifest.firstChild, 1) 447*33f37583SAndroid Build Coastguard Worker first = manifest.firstChild 448*33f37583SAndroid Build Coastguard Worker manifest.insertBefore(doc.createTextNode(indent), first) 449*33f37583SAndroid Build Coastguard Worker manifest.insertBefore(application, first) 450*33f37583SAndroid Build Coastguard Worker 451*33f37583SAndroid Build Coastguard Worker indent = get_indent(application.firstChild, 2) 452*33f37583SAndroid Build Coastguard Worker last = application.lastChild 453*33f37583SAndroid Build Coastguard Worker if last is not None and last.nodeType != minidom.Node.TEXT_NODE: 454*33f37583SAndroid Build Coastguard Worker last = None 455*33f37583SAndroid Build Coastguard Worker 456*33f37583SAndroid Build Coastguard Worker if not find_child_with_attribute(application, 'meta-data', android_ns, 457*33f37583SAndroid Build Coastguard Worker 'name', logging_parent_key): 458*33f37583SAndroid Build Coastguard Worker ul = doc.createElement('meta-data') 459*33f37583SAndroid Build Coastguard Worker ul.setAttributeNS(android_ns, 'android:name', logging_parent_key) 460*33f37583SAndroid Build Coastguard Worker ul.setAttributeNS(android_ns, 'android:value', logging_parent_value) 461*33f37583SAndroid Build Coastguard Worker application.insertBefore(doc.createTextNode(indent), last) 462*33f37583SAndroid Build Coastguard Worker application.insertBefore(ul, last) 463*33f37583SAndroid Build Coastguard Worker last = application.lastChild 464*33f37583SAndroid Build Coastguard Worker 465*33f37583SAndroid Build Coastguard Worker if last and last.nodeType != minidom.Node.TEXT_NODE: 466*33f37583SAndroid Build Coastguard Worker indent = get_indent(application.previousSibling, 1) 467*33f37583SAndroid Build Coastguard Worker application.appendChild(doc.createTextNode(indent)) 468*33f37583SAndroid Build Coastguard Worker 469*33f37583SAndroid Build Coastguard Worker with tempfile.NamedTemporaryFile(delete=False, mode='w') as temp: 470*33f37583SAndroid Build Coastguard Worker write_xml(temp, doc) 471*33f37583SAndroid Build Coastguard Worker return temp.name 472*33f37583SAndroid Build Coastguard Worker 473*33f37583SAndroid Build Coastguard Worker 474*33f37583SAndroid Build Coastguard Workerdef ShaHashFiles(file_paths): 475*33f37583SAndroid Build Coastguard Worker """get hash for a number of files.""" 476*33f37583SAndroid Build Coastguard Worker h = hashlib.sha256() 477*33f37583SAndroid Build Coastguard Worker for file_path in file_paths: 478*33f37583SAndroid Build Coastguard Worker with open(file_path, 'rb') as file: 479*33f37583SAndroid Build Coastguard Worker while True: 480*33f37583SAndroid Build Coastguard Worker chunk = file.read(h.block_size) 481*33f37583SAndroid Build Coastguard Worker if not chunk: 482*33f37583SAndroid Build Coastguard Worker break 483*33f37583SAndroid Build Coastguard Worker h.update(chunk) 484*33f37583SAndroid Build Coastguard Worker return h.hexdigest() 485*33f37583SAndroid Build Coastguard Worker 486*33f37583SAndroid Build Coastguard Worker 487*33f37583SAndroid Build Coastguard Workerdef CreateImageExt4(args, work_dir, manifests_dir, img_file): 488*33f37583SAndroid Build Coastguard Worker """Create image for ext4 file system.""" 489*33f37583SAndroid Build Coastguard Worker 490*33f37583SAndroid Build Coastguard Worker lost_found_location = os.path.join(args.input_dir, 'lost+found') 491*33f37583SAndroid Build Coastguard Worker if os.path.exists(lost_found_location): 492*33f37583SAndroid Build Coastguard Worker print('Warning: input_dir contains a lost+found/ root folder, which ' 493*33f37583SAndroid Build Coastguard Worker 'has been known to cause non-deterministic apex builds.') 494*33f37583SAndroid Build Coastguard Worker 495*33f37583SAndroid Build Coastguard Worker # sufficiently big = size + 16MB margin 496*33f37583SAndroid Build Coastguard Worker size_in_mb = (GetDirSize(args.input_dir) // (1024 * 1024)) 497*33f37583SAndroid Build Coastguard Worker size_in_mb += 16 498*33f37583SAndroid Build Coastguard Worker 499*33f37583SAndroid Build Coastguard Worker # Margin is for files that are not under args.input_dir. this consists of 500*33f37583SAndroid Build Coastguard Worker # n inodes for apex_manifest files and 11 reserved inodes for ext4. 501*33f37583SAndroid Build Coastguard Worker # TOBO(b/122991714) eliminate these details. Use build_image.py which 502*33f37583SAndroid Build Coastguard Worker # determines the optimal inode count by first building an image and then 503*33f37583SAndroid Build Coastguard Worker # count the inodes actually used. 504*33f37583SAndroid Build Coastguard Worker inode_num_margin = GetFilesAndDirsCount(manifests_dir) + 11 505*33f37583SAndroid Build Coastguard Worker inode_num = GetFilesAndDirsCount(args.input_dir) + inode_num_margin 506*33f37583SAndroid Build Coastguard Worker 507*33f37583SAndroid Build Coastguard Worker cmd = ['mke2fs'] 508*33f37583SAndroid Build Coastguard Worker cmd.extend(['-O', '^has_journal']) # because image is read-only 509*33f37583SAndroid Build Coastguard Worker cmd.extend(['-b', str(BLOCK_SIZE)]) 510*33f37583SAndroid Build Coastguard Worker cmd.extend(['-m', '0']) # reserved block percentage 511*33f37583SAndroid Build Coastguard Worker cmd.extend(['-t', 'ext4']) 512*33f37583SAndroid Build Coastguard Worker cmd.extend(['-I', '256']) # inode size 513*33f37583SAndroid Build Coastguard Worker cmd.extend(['-N', str(inode_num)]) 514*33f37583SAndroid Build Coastguard Worker uu = str(uuid.uuid5(uuid.NAMESPACE_URL, 'www.android.com')) 515*33f37583SAndroid Build Coastguard Worker cmd.extend(['-U', uu]) 516*33f37583SAndroid Build Coastguard Worker cmd.extend(['-E', 'hash_seed=' + uu]) 517*33f37583SAndroid Build Coastguard Worker cmd.append(img_file) 518*33f37583SAndroid Build Coastguard Worker cmd.append(str(size_in_mb) + 'M') 519*33f37583SAndroid Build Coastguard Worker with tempfile.NamedTemporaryFile(dir=work_dir, 520*33f37583SAndroid Build Coastguard Worker suffix='mke2fs.conf') as conf_file: 521*33f37583SAndroid Build Coastguard Worker conf_data = pkgutil.get_data('apexer', 'mke2fs.conf') 522*33f37583SAndroid Build Coastguard Worker conf_file.write(conf_data) 523*33f37583SAndroid Build Coastguard Worker conf_file.flush() 524*33f37583SAndroid Build Coastguard Worker RunCommand(cmd, args.verbose, 525*33f37583SAndroid Build Coastguard Worker {'MKE2FS_CONFIG': conf_file.name, 'E2FSPROGS_FAKE_TIME': '1'}) 526*33f37583SAndroid Build Coastguard Worker 527*33f37583SAndroid Build Coastguard Worker # Compile the file context into the binary form 528*33f37583SAndroid Build Coastguard Worker compiled_file_contexts = os.path.join(work_dir, 'file_contexts.bin') 529*33f37583SAndroid Build Coastguard Worker cmd = ['sefcontext_compile'] 530*33f37583SAndroid Build Coastguard Worker cmd.extend(['-o', compiled_file_contexts]) 531*33f37583SAndroid Build Coastguard Worker cmd.append(args.file_contexts) 532*33f37583SAndroid Build Coastguard Worker RunCommand(cmd, args.verbose) 533*33f37583SAndroid Build Coastguard Worker 534*33f37583SAndroid Build Coastguard Worker # Add files to the image file 535*33f37583SAndroid Build Coastguard Worker cmd = ['e2fsdroid'] 536*33f37583SAndroid Build Coastguard Worker cmd.append('-e') # input is not android_sparse_file 537*33f37583SAndroid Build Coastguard Worker cmd.extend(['-f', args.input_dir]) 538*33f37583SAndroid Build Coastguard Worker cmd.extend(['-T', '0']) # time is set to epoch 539*33f37583SAndroid Build Coastguard Worker cmd.extend(['-S', compiled_file_contexts]) 540*33f37583SAndroid Build Coastguard Worker cmd.extend(['-C', args.canned_fs_config]) 541*33f37583SAndroid Build Coastguard Worker cmd.extend(['-a', '/']) 542*33f37583SAndroid Build Coastguard Worker cmd.append('-s') # share dup blocks 543*33f37583SAndroid Build Coastguard Worker cmd.append(img_file) 544*33f37583SAndroid Build Coastguard Worker RunCommand(cmd, args.verbose, {'E2FSPROGS_FAKE_TIME': '1'}) 545*33f37583SAndroid Build Coastguard Worker 546*33f37583SAndroid Build Coastguard Worker cmd = ['e2fsdroid'] 547*33f37583SAndroid Build Coastguard Worker cmd.append('-e') # input is not android_sparse_file 548*33f37583SAndroid Build Coastguard Worker cmd.extend(['-f', manifests_dir]) 549*33f37583SAndroid Build Coastguard Worker cmd.extend(['-T', '0']) # time is set to epoch 550*33f37583SAndroid Build Coastguard Worker cmd.extend(['-S', compiled_file_contexts]) 551*33f37583SAndroid Build Coastguard Worker cmd.extend(['-C', args.canned_fs_config]) 552*33f37583SAndroid Build Coastguard Worker cmd.extend(['-a', '/']) 553*33f37583SAndroid Build Coastguard Worker cmd.append('-s') # share dup blocks 554*33f37583SAndroid Build Coastguard Worker cmd.append(img_file) 555*33f37583SAndroid Build Coastguard Worker RunCommand(cmd, args.verbose, {'E2FSPROGS_FAKE_TIME': '1'}) 556*33f37583SAndroid Build Coastguard Worker 557*33f37583SAndroid Build Coastguard Worker # Resize the image file to save space 558*33f37583SAndroid Build Coastguard Worker cmd = ['resize2fs'] 559*33f37583SAndroid Build Coastguard Worker cmd.append('-M') # shrink as small as possible 560*33f37583SAndroid Build Coastguard Worker cmd.append(img_file) 561*33f37583SAndroid Build Coastguard Worker RunCommand(cmd, args.verbose, {'E2FSPROGS_FAKE_TIME': '1'}) 562*33f37583SAndroid Build Coastguard Worker 563*33f37583SAndroid Build Coastguard Worker 564*33f37583SAndroid Build Coastguard Workerdef CreateImageF2fs(args, manifests_dir, img_file): 565*33f37583SAndroid Build Coastguard Worker """Create image for f2fs file system.""" 566*33f37583SAndroid Build Coastguard Worker # F2FS requires a ~100M minimum size (necessary for ART, could be reduced 567*33f37583SAndroid Build Coastguard Worker # a bit for other) 568*33f37583SAndroid Build Coastguard Worker # TODO(b/158453869): relax these requirements for readonly devices 569*33f37583SAndroid Build Coastguard Worker size_in_mb = (GetDirSize(args.input_dir) // (1024 * 1024)) 570*33f37583SAndroid Build Coastguard Worker size_in_mb += 100 571*33f37583SAndroid Build Coastguard Worker 572*33f37583SAndroid Build Coastguard Worker # Create an empty image 573*33f37583SAndroid Build Coastguard Worker cmd = ['/usr/bin/fallocate'] 574*33f37583SAndroid Build Coastguard Worker cmd.extend(['-l', str(size_in_mb) + 'M']) 575*33f37583SAndroid Build Coastguard Worker cmd.append(img_file) 576*33f37583SAndroid Build Coastguard Worker RunCommand(cmd, args.verbose) 577*33f37583SAndroid Build Coastguard Worker 578*33f37583SAndroid Build Coastguard Worker # Format the image to F2FS 579*33f37583SAndroid Build Coastguard Worker cmd = ['make_f2fs'] 580*33f37583SAndroid Build Coastguard Worker cmd.extend(['-g', 'android']) 581*33f37583SAndroid Build Coastguard Worker uu = str(uuid.uuid5(uuid.NAMESPACE_URL, 'www.android.com')) 582*33f37583SAndroid Build Coastguard Worker cmd.extend(['-U', uu]) 583*33f37583SAndroid Build Coastguard Worker cmd.extend(['-T', '0']) 584*33f37583SAndroid Build Coastguard Worker cmd.append('-r') # sets checkpointing seed to 0 to remove random bits 585*33f37583SAndroid Build Coastguard Worker cmd.append(img_file) 586*33f37583SAndroid Build Coastguard Worker RunCommand(cmd, args.verbose) 587*33f37583SAndroid Build Coastguard Worker 588*33f37583SAndroid Build Coastguard Worker # Add files to the image 589*33f37583SAndroid Build Coastguard Worker cmd = ['sload_f2fs'] 590*33f37583SAndroid Build Coastguard Worker cmd.extend(['-C', args.canned_fs_config]) 591*33f37583SAndroid Build Coastguard Worker cmd.extend(['-f', manifests_dir]) 592*33f37583SAndroid Build Coastguard Worker cmd.extend(['-s', args.file_contexts]) 593*33f37583SAndroid Build Coastguard Worker cmd.extend(['-T', '0']) 594*33f37583SAndroid Build Coastguard Worker cmd.append(img_file) 595*33f37583SAndroid Build Coastguard Worker RunCommand(cmd, args.verbose, expected_return_values={0, 1}) 596*33f37583SAndroid Build Coastguard Worker 597*33f37583SAndroid Build Coastguard Worker cmd = ['sload_f2fs'] 598*33f37583SAndroid Build Coastguard Worker cmd.extend(['-C', args.canned_fs_config]) 599*33f37583SAndroid Build Coastguard Worker cmd.extend(['-f', args.input_dir]) 600*33f37583SAndroid Build Coastguard Worker cmd.extend(['-s', args.file_contexts]) 601*33f37583SAndroid Build Coastguard Worker cmd.extend(['-T', '0']) 602*33f37583SAndroid Build Coastguard Worker cmd.append(img_file) 603*33f37583SAndroid Build Coastguard Worker RunCommand(cmd, args.verbose, expected_return_values={0, 1}) 604*33f37583SAndroid Build Coastguard Worker 605*33f37583SAndroid Build Coastguard Worker # TODO(b/158453869): resize the image file to save space 606*33f37583SAndroid Build Coastguard Worker 607*33f37583SAndroid Build Coastguard Worker 608*33f37583SAndroid Build Coastguard Workerdef CreateImageErofs(args, work_dir, manifests_dir, img_file): 609*33f37583SAndroid Build Coastguard Worker """Create image for erofs file system.""" 610*33f37583SAndroid Build Coastguard Worker # mkfs.erofs doesn't support multiple input 611*33f37583SAndroid Build Coastguard Worker 612*33f37583SAndroid Build Coastguard Worker tmp_input_dir = os.path.join(work_dir, 'tmp_input_dir') 613*33f37583SAndroid Build Coastguard Worker os.mkdir(tmp_input_dir) 614*33f37583SAndroid Build Coastguard Worker cmd = ['/bin/cp', '-ra'] 615*33f37583SAndroid Build Coastguard Worker cmd.extend(glob.glob(manifests_dir + '/*')) 616*33f37583SAndroid Build Coastguard Worker cmd.extend(glob.glob(args.input_dir + '/*')) 617*33f37583SAndroid Build Coastguard Worker cmd.append(tmp_input_dir) 618*33f37583SAndroid Build Coastguard Worker RunCommand(cmd, args.verbose) 619*33f37583SAndroid Build Coastguard Worker 620*33f37583SAndroid Build Coastguard Worker cmd = ['make_erofs'] 621*33f37583SAndroid Build Coastguard Worker cmd.extend(['-z', 'lz4hc']) 622*33f37583SAndroid Build Coastguard Worker cmd.extend(['--fs-config-file', args.canned_fs_config]) 623*33f37583SAndroid Build Coastguard Worker cmd.extend(['--file-contexts', args.file_contexts]) 624*33f37583SAndroid Build Coastguard Worker uu = str(uuid.uuid5(uuid.NAMESPACE_URL, 'www.android.com')) 625*33f37583SAndroid Build Coastguard Worker cmd.extend(['-U', uu]) 626*33f37583SAndroid Build Coastguard Worker cmd.extend(['-T', '0']) 627*33f37583SAndroid Build Coastguard Worker cmd.extend([img_file, tmp_input_dir]) 628*33f37583SAndroid Build Coastguard Worker RunCommand(cmd, args.verbose) 629*33f37583SAndroid Build Coastguard Worker shutil.rmtree(tmp_input_dir) 630*33f37583SAndroid Build Coastguard Worker 631*33f37583SAndroid Build Coastguard Worker # The minimum image size of erofs is 4k, which will cause an error 632*33f37583SAndroid Build Coastguard Worker # when execute generate_hash_tree in avbtool 633*33f37583SAndroid Build Coastguard Worker cmd = ['/bin/ls', '-lgG', img_file] 634*33f37583SAndroid Build Coastguard Worker output, _ = RunCommand(cmd, verbose=False) 635*33f37583SAndroid Build Coastguard Worker image_size = int(output.split()[2]) 636*33f37583SAndroid Build Coastguard Worker if image_size == 4096: 637*33f37583SAndroid Build Coastguard Worker cmd = ['/usr/bin/fallocate', '-l', '8k', img_file] 638*33f37583SAndroid Build Coastguard Worker RunCommand(cmd, verbose=False) 639*33f37583SAndroid Build Coastguard Worker 640*33f37583SAndroid Build Coastguard Worker 641*33f37583SAndroid Build Coastguard Workerdef CreateImage(args, work_dir, manifests_dir, img_file): 642*33f37583SAndroid Build Coastguard Worker """create payload image.""" 643*33f37583SAndroid Build Coastguard Worker if args.payload_fs_type == 'ext4': 644*33f37583SAndroid Build Coastguard Worker CreateImageExt4(args, work_dir, manifests_dir, img_file) 645*33f37583SAndroid Build Coastguard Worker elif args.payload_fs_type == 'f2fs': 646*33f37583SAndroid Build Coastguard Worker CreateImageF2fs(args, manifests_dir, img_file) 647*33f37583SAndroid Build Coastguard Worker elif args.payload_fs_type == 'erofs': 648*33f37583SAndroid Build Coastguard Worker CreateImageErofs(args, work_dir, manifests_dir, img_file) 649*33f37583SAndroid Build Coastguard Worker 650*33f37583SAndroid Build Coastguard Worker 651*33f37583SAndroid Build Coastguard Workerdef SignImage(args, manifest_apex, img_file): 652*33f37583SAndroid Build Coastguard Worker """sign payload image. 653*33f37583SAndroid Build Coastguard Worker 654*33f37583SAndroid Build Coastguard Worker Args: 655*33f37583SAndroid Build Coastguard Worker args: apexer options 656*33f37583SAndroid Build Coastguard Worker manifest_apex: apex manifest proto 657*33f37583SAndroid Build Coastguard Worker img_file: unsigned payload image file 658*33f37583SAndroid Build Coastguard Worker """ 659*33f37583SAndroid Build Coastguard Worker 660*33f37583SAndroid Build Coastguard Worker if args.do_not_check_keyname or args.unsigned_payload: 661*33f37583SAndroid Build Coastguard Worker key_name = manifest_apex.name 662*33f37583SAndroid Build Coastguard Worker else: 663*33f37583SAndroid Build Coastguard Worker key_name = os.path.basename(os.path.splitext(args.key)[0]) 664*33f37583SAndroid Build Coastguard Worker 665*33f37583SAndroid Build Coastguard Worker cmd = ['avbtool'] 666*33f37583SAndroid Build Coastguard Worker cmd.append('add_hashtree_footer') 667*33f37583SAndroid Build Coastguard Worker cmd.append('--do_not_generate_fec') 668*33f37583SAndroid Build Coastguard Worker cmd.extend(['--algorithm', 'SHA256_RSA4096']) 669*33f37583SAndroid Build Coastguard Worker cmd.extend(['--hash_algorithm', 'sha256']) 670*33f37583SAndroid Build Coastguard Worker cmd.extend(['--key', args.key]) 671*33f37583SAndroid Build Coastguard Worker cmd.extend(['--prop', 'apex.key:' + key_name]) 672*33f37583SAndroid Build Coastguard Worker # Set up the salt based on manifest content which includes name 673*33f37583SAndroid Build Coastguard Worker # and version 674*33f37583SAndroid Build Coastguard Worker salt = hashlib.sha256(manifest_apex.SerializeToString()).hexdigest() 675*33f37583SAndroid Build Coastguard Worker cmd.extend(['--salt', salt]) 676*33f37583SAndroid Build Coastguard Worker cmd.extend(['--image', img_file]) 677*33f37583SAndroid Build Coastguard Worker if args.no_hashtree: 678*33f37583SAndroid Build Coastguard Worker cmd.append('--no_hashtree') 679*33f37583SAndroid Build Coastguard Worker if args.signing_args: 680*33f37583SAndroid Build Coastguard Worker cmd.extend(shlex.split(args.signing_args)) 681*33f37583SAndroid Build Coastguard Worker RunCommand(cmd, args.verbose) 682*33f37583SAndroid Build Coastguard Worker 683*33f37583SAndroid Build Coastguard Worker # Get the minimum size of the partition required. 684*33f37583SAndroid Build Coastguard Worker # TODO(b/113320014) eliminate this step 685*33f37583SAndroid Build Coastguard Worker info, _ = RunCommand(['avbtool', 'info_image', '--image', img_file], 686*33f37583SAndroid Build Coastguard Worker args.verbose) 687*33f37583SAndroid Build Coastguard Worker vbmeta_offset = int(re.search('VBMeta\ offset:\ *([0-9]+)', info).group(1)) 688*33f37583SAndroid Build Coastguard Worker vbmeta_size = int(re.search('VBMeta\ size:\ *([0-9]+)', info).group(1)) 689*33f37583SAndroid Build Coastguard Worker partition_size = RoundUp(vbmeta_offset + vbmeta_size, 690*33f37583SAndroid Build Coastguard Worker BLOCK_SIZE) + BLOCK_SIZE 691*33f37583SAndroid Build Coastguard Worker 692*33f37583SAndroid Build Coastguard Worker # Resize to the minimum size 693*33f37583SAndroid Build Coastguard Worker # TODO(b/113320014) eliminate this step 694*33f37583SAndroid Build Coastguard Worker cmd = ['avbtool'] 695*33f37583SAndroid Build Coastguard Worker cmd.append('resize_image') 696*33f37583SAndroid Build Coastguard Worker cmd.extend(['--image', img_file]) 697*33f37583SAndroid Build Coastguard Worker cmd.extend(['--partition_size', str(partition_size)]) 698*33f37583SAndroid Build Coastguard Worker RunCommand(cmd, args.verbose) 699*33f37583SAndroid Build Coastguard Worker 700*33f37583SAndroid Build Coastguard Worker 701*33f37583SAndroid Build Coastguard Workerdef CreateApexPayload(args, work_dir, content_dir, manifests_dir, 702*33f37583SAndroid Build Coastguard Worker manifest_apex): 703*33f37583SAndroid Build Coastguard Worker """Create payload. 704*33f37583SAndroid Build Coastguard Worker 705*33f37583SAndroid Build Coastguard Worker Args: 706*33f37583SAndroid Build Coastguard Worker args: apexer options 707*33f37583SAndroid Build Coastguard Worker work_dir: apex container working directory 708*33f37583SAndroid Build Coastguard Worker content_dir: the working directory for payload contents 709*33f37583SAndroid Build Coastguard Worker manifests_dir: manifests directory 710*33f37583SAndroid Build Coastguard Worker manifest_apex: apex manifest proto 711*33f37583SAndroid Build Coastguard Worker 712*33f37583SAndroid Build Coastguard Worker Returns: 713*33f37583SAndroid Build Coastguard Worker payload file 714*33f37583SAndroid Build Coastguard Worker """ 715*33f37583SAndroid Build Coastguard Worker img_file = os.path.join(content_dir, 'apex_payload.img') 716*33f37583SAndroid Build Coastguard Worker CreateImage(args, work_dir, manifests_dir, img_file) 717*33f37583SAndroid Build Coastguard Worker if not args.unsigned_payload: 718*33f37583SAndroid Build Coastguard Worker SignImage(args, manifest_apex, img_file) 719*33f37583SAndroid Build Coastguard Worker return img_file 720*33f37583SAndroid Build Coastguard Worker 721*33f37583SAndroid Build Coastguard Worker 722*33f37583SAndroid Build Coastguard Workerdef CreateAndroidManifestXml(args, work_dir, manifest_apex): 723*33f37583SAndroid Build Coastguard Worker """Create AndroidManifest.xml file. 724*33f37583SAndroid Build Coastguard Worker 725*33f37583SAndroid Build Coastguard Worker Args: 726*33f37583SAndroid Build Coastguard Worker args: apexer options 727*33f37583SAndroid Build Coastguard Worker work_dir: apex container working directory 728*33f37583SAndroid Build Coastguard Worker manifest_apex: apex manifest proto 729*33f37583SAndroid Build Coastguard Worker 730*33f37583SAndroid Build Coastguard Worker Returns: 731*33f37583SAndroid Build Coastguard Worker AndroidManifest.xml file inside the work dir 732*33f37583SAndroid Build Coastguard Worker """ 733*33f37583SAndroid Build Coastguard Worker android_manifest_file = os.path.join(work_dir, 'AndroidManifest.xml') 734*33f37583SAndroid Build Coastguard Worker if not args.android_manifest: 735*33f37583SAndroid Build Coastguard Worker if args.verbose: 736*33f37583SAndroid Build Coastguard Worker print('Creating AndroidManifest ' + android_manifest_file) 737*33f37583SAndroid Build Coastguard Worker with open(android_manifest_file, 'w') as f: 738*33f37583SAndroid Build Coastguard Worker app_package_name = manifest_apex.name 739*33f37583SAndroid Build Coastguard Worker f.write(PrepareAndroidManifest(app_package_name, manifest_apex.version, 740*33f37583SAndroid Build Coastguard Worker args.test_only)) 741*33f37583SAndroid Build Coastguard Worker args.android_manifest = android_manifest_file 742*33f37583SAndroid Build Coastguard Worker ValidateGeneratedAndroidManifest(args.android_manifest, args.test_only) 743*33f37583SAndroid Build Coastguard Worker else: 744*33f37583SAndroid Build Coastguard Worker ValidateAndroidManifest(manifest_apex.name, args.android_manifest) 745*33f37583SAndroid Build Coastguard Worker shutil.copyfile(args.android_manifest, android_manifest_file) 746*33f37583SAndroid Build Coastguard Worker 747*33f37583SAndroid Build Coastguard Worker # If logging parent is specified, add it to the AndroidManifest. 748*33f37583SAndroid Build Coastguard Worker if args.logging_parent: 749*33f37583SAndroid Build Coastguard Worker android_manifest_file = AddLoggingParent(android_manifest_file, 750*33f37583SAndroid Build Coastguard Worker args.logging_parent) 751*33f37583SAndroid Build Coastguard Worker return android_manifest_file 752*33f37583SAndroid Build Coastguard Worker 753*33f37583SAndroid Build Coastguard Worker 754*33f37583SAndroid Build Coastguard Workerdef CreateApex(args, work_dir): 755*33f37583SAndroid Build Coastguard Worker if not ValidateArgs(args): 756*33f37583SAndroid Build Coastguard Worker return False 757*33f37583SAndroid Build Coastguard Worker 758*33f37583SAndroid Build Coastguard Worker if args.verbose: 759*33f37583SAndroid Build Coastguard Worker print('Using tools from ' + str(tool_path_list)) 760*33f37583SAndroid Build Coastguard Worker 761*33f37583SAndroid Build Coastguard Worker def CopyFile(src, dst): 762*33f37583SAndroid Build Coastguard Worker if args.verbose: 763*33f37583SAndroid Build Coastguard Worker print('Copying ' + src + ' to ' + dst) 764*33f37583SAndroid Build Coastguard Worker shutil.copyfile(src, dst) 765*33f37583SAndroid Build Coastguard Worker 766*33f37583SAndroid Build Coastguard Worker try: 767*33f37583SAndroid Build Coastguard Worker manifest_apex = CreateApexManifest(args.manifest) 768*33f37583SAndroid Build Coastguard Worker except ApexManifestError as err: 769*33f37583SAndroid Build Coastguard Worker print("'" + args.manifest + "' is not a valid manifest file") 770*33f37583SAndroid Build Coastguard Worker print(err.errmessage) 771*33f37583SAndroid Build Coastguard Worker return False 772*33f37583SAndroid Build Coastguard Worker 773*33f37583SAndroid Build Coastguard Worker # Create content dir and manifests dir, the manifests dir is used to 774*33f37583SAndroid Build Coastguard Worker # create the payload image 775*33f37583SAndroid Build Coastguard Worker content_dir = os.path.join(work_dir, 'content') 776*33f37583SAndroid Build Coastguard Worker os.mkdir(content_dir) 777*33f37583SAndroid Build Coastguard Worker manifests_dir = os.path.join(work_dir, 'manifests') 778*33f37583SAndroid Build Coastguard Worker os.mkdir(manifests_dir) 779*33f37583SAndroid Build Coastguard Worker 780*33f37583SAndroid Build Coastguard Worker # Create AndroidManifest.xml file first so that we can hash the file 781*33f37583SAndroid Build Coastguard Worker # and store the hashed value in the manifest proto buf that goes into 782*33f37583SAndroid Build Coastguard Worker # the payload image. So any change in this file will ensure changes 783*33f37583SAndroid Build Coastguard Worker # in payload image file 784*33f37583SAndroid Build Coastguard Worker android_manifest_file = CreateAndroidManifestXml( 785*33f37583SAndroid Build Coastguard Worker args, work_dir, manifest_apex) 786*33f37583SAndroid Build Coastguard Worker 787*33f37583SAndroid Build Coastguard Worker # APEX manifest is also included in the image. The manifest is included 788*33f37583SAndroid Build Coastguard Worker # twice: once inside the image and once outside the image (but still 789*33f37583SAndroid Build Coastguard Worker # within the zip container). 790*33f37583SAndroid Build Coastguard Worker with open(os.path.join(manifests_dir, 'apex_manifest.pb'), 'wb') as f: 791*33f37583SAndroid Build Coastguard Worker f.write(manifest_apex.SerializeToString()) 792*33f37583SAndroid Build Coastguard Worker with open(os.path.join(content_dir, 'apex_manifest.pb'), 'wb') as f: 793*33f37583SAndroid Build Coastguard Worker f.write(manifest_apex.SerializeToString()) 794*33f37583SAndroid Build Coastguard Worker if args.manifest_json: 795*33f37583SAndroid Build Coastguard Worker CopyFile(args.manifest_json, 796*33f37583SAndroid Build Coastguard Worker os.path.join(manifests_dir, 'apex_manifest.json')) 797*33f37583SAndroid Build Coastguard Worker CopyFile(args.manifest_json, 798*33f37583SAndroid Build Coastguard Worker os.path.join(content_dir, 'apex_manifest.json')) 799*33f37583SAndroid Build Coastguard Worker 800*33f37583SAndroid Build Coastguard Worker # Create payload 801*33f37583SAndroid Build Coastguard Worker img_file = CreateApexPayload(args, work_dir, content_dir, manifests_dir, 802*33f37583SAndroid Build Coastguard Worker manifest_apex) 803*33f37583SAndroid Build Coastguard Worker 804*33f37583SAndroid Build Coastguard Worker if args.unsigned_payload_only or args.payload_only: 805*33f37583SAndroid Build Coastguard Worker shutil.copyfile(img_file, args.output) 806*33f37583SAndroid Build Coastguard Worker if args.verbose: 807*33f37583SAndroid Build Coastguard Worker if args.unsigned_payload_only: 808*33f37583SAndroid Build Coastguard Worker print('Created (unsigned payload only) ' + args.output) 809*33f37583SAndroid Build Coastguard Worker else: 810*33f37583SAndroid Build Coastguard Worker print('Created (payload only) ' + args.output) 811*33f37583SAndroid Build Coastguard Worker return True 812*33f37583SAndroid Build Coastguard Worker 813*33f37583SAndroid Build Coastguard Worker # copy the public key, if specified 814*33f37583SAndroid Build Coastguard Worker if args.pubkey: 815*33f37583SAndroid Build Coastguard Worker shutil.copyfile(args.pubkey, os.path.join(content_dir, 'apex_pubkey')) 816*33f37583SAndroid Build Coastguard Worker 817*33f37583SAndroid Build Coastguard Worker if args.include_build_info: 818*33f37583SAndroid Build Coastguard Worker build_info = GenerateBuildInfo(args) 819*33f37583SAndroid Build Coastguard Worker with open(os.path.join(content_dir, 'apex_build_info.pb'), 'wb') as f: 820*33f37583SAndroid Build Coastguard Worker f.write(build_info.SerializeToString()) 821*33f37583SAndroid Build Coastguard Worker 822*33f37583SAndroid Build Coastguard Worker apk_file = os.path.join(work_dir, 'apex.apk') 823*33f37583SAndroid Build Coastguard Worker cmd = ['aapt2'] 824*33f37583SAndroid Build Coastguard Worker cmd.append('link') 825*33f37583SAndroid Build Coastguard Worker cmd.extend(['--manifest', android_manifest_file]) 826*33f37583SAndroid Build Coastguard Worker if args.override_apk_package_name: 827*33f37583SAndroid Build Coastguard Worker cmd.extend(['--rename-manifest-package', args.override_apk_package_name]) 828*33f37583SAndroid Build Coastguard Worker # This version from apex_manifest.json is used when versionCode isn't 829*33f37583SAndroid Build Coastguard Worker # specified in AndroidManifest.xml 830*33f37583SAndroid Build Coastguard Worker cmd.extend(['--version-code', str(manifest_apex.version)]) 831*33f37583SAndroid Build Coastguard Worker if manifest_apex.versionName: 832*33f37583SAndroid Build Coastguard Worker cmd.extend(['--version-name', manifest_apex.versionName]) 833*33f37583SAndroid Build Coastguard Worker if args.target_sdk_version: 834*33f37583SAndroid Build Coastguard Worker cmd.extend(['--target-sdk-version', args.target_sdk_version]) 835*33f37583SAndroid Build Coastguard Worker if args.min_sdk_version: 836*33f37583SAndroid Build Coastguard Worker cmd.extend(['--min-sdk-version', args.min_sdk_version]) 837*33f37583SAndroid Build Coastguard Worker else: 838*33f37583SAndroid Build Coastguard Worker # Default value for minSdkVersion. 839*33f37583SAndroid Build Coastguard Worker cmd.extend(['--min-sdk-version', '29']) 840*33f37583SAndroid Build Coastguard Worker if args.assets_dir: 841*33f37583SAndroid Build Coastguard Worker cmd.extend(['-A', args.assets_dir]) 842*33f37583SAndroid Build Coastguard Worker cmd.extend(['-o', apk_file]) 843*33f37583SAndroid Build Coastguard Worker cmd.extend(['-I', args.android_jar_path]) 844*33f37583SAndroid Build Coastguard Worker RunCommand(cmd, args.verbose) 845*33f37583SAndroid Build Coastguard Worker 846*33f37583SAndroid Build Coastguard Worker zip_file = os.path.join(work_dir, 'apex.zip') 847*33f37583SAndroid Build Coastguard Worker CreateZip(content_dir, zip_file) 848*33f37583SAndroid Build Coastguard Worker MergeZips([apk_file, zip_file], args.output) 849*33f37583SAndroid Build Coastguard Worker 850*33f37583SAndroid Build Coastguard Worker if args.verbose: 851*33f37583SAndroid Build Coastguard Worker print('Created ' + args.output) 852*33f37583SAndroid Build Coastguard Worker 853*33f37583SAndroid Build Coastguard Worker return True 854*33f37583SAndroid Build Coastguard Worker 855*33f37583SAndroid Build Coastguard Workerdef CreateApexManifest(manifest_path): 856*33f37583SAndroid Build Coastguard Worker try: 857*33f37583SAndroid Build Coastguard Worker manifest_apex = ParseApexManifest(manifest_path) 858*33f37583SAndroid Build Coastguard Worker ValidateApexManifest(manifest_apex) 859*33f37583SAndroid Build Coastguard Worker return manifest_apex 860*33f37583SAndroid Build Coastguard Worker except IOError: 861*33f37583SAndroid Build Coastguard Worker raise ApexManifestError("Cannot read manifest file: '" + manifest_path + "'") 862*33f37583SAndroid Build Coastguard Worker 863*33f37583SAndroid Build Coastguard Workerclass TempDirectory(object): 864*33f37583SAndroid Build Coastguard Worker 865*33f37583SAndroid Build Coastguard Worker def __enter__(self): 866*33f37583SAndroid Build Coastguard Worker self.name = tempfile.mkdtemp() 867*33f37583SAndroid Build Coastguard Worker return self.name 868*33f37583SAndroid Build Coastguard Worker 869*33f37583SAndroid Build Coastguard Worker def __exit__(self, *unused): 870*33f37583SAndroid Build Coastguard Worker shutil.rmtree(self.name) 871*33f37583SAndroid Build Coastguard Worker 872*33f37583SAndroid Build Coastguard Worker 873*33f37583SAndroid Build Coastguard Workerdef CreateZip(content_dir, apex_zip): 874*33f37583SAndroid Build Coastguard Worker with zipfile.ZipFile(apex_zip, 'w', compression=zipfile.ZIP_DEFLATED) as out: 875*33f37583SAndroid Build Coastguard Worker for root, _, files in os.walk(content_dir): 876*33f37583SAndroid Build Coastguard Worker for file in files: 877*33f37583SAndroid Build Coastguard Worker path = os.path.join(root, file) 878*33f37583SAndroid Build Coastguard Worker rel_path = os.path.relpath(path, content_dir) 879*33f37583SAndroid Build Coastguard Worker # "apex_payload.img" shouldn't be compressed 880*33f37583SAndroid Build Coastguard Worker if rel_path == 'apex_payload.img': 881*33f37583SAndroid Build Coastguard Worker out.write(path, rel_path, compress_type=zipfile.ZIP_STORED) 882*33f37583SAndroid Build Coastguard Worker else: 883*33f37583SAndroid Build Coastguard Worker out.write(path, rel_path) 884*33f37583SAndroid Build Coastguard Worker 885*33f37583SAndroid Build Coastguard Worker 886*33f37583SAndroid Build Coastguard Workerdef MergeZips(zip_files, output_zip): 887*33f37583SAndroid Build Coastguard Worker with zipfile.ZipFile(output_zip, 'w') as out: 888*33f37583SAndroid Build Coastguard Worker for file in zip_files: 889*33f37583SAndroid Build Coastguard Worker # copy to output_zip 890*33f37583SAndroid Build Coastguard Worker with zipfile.ZipFile(file, 'r') as inzip: 891*33f37583SAndroid Build Coastguard Worker for info in inzip.infolist(): 892*33f37583SAndroid Build Coastguard Worker # reset timestamp for deterministic output 893*33f37583SAndroid Build Coastguard Worker info.date_time = (1980, 1, 1, 0, 0, 0) 894*33f37583SAndroid Build Coastguard Worker # reset filemode for deterministic output. The high 16 bits are for 895*33f37583SAndroid Build Coastguard Worker # filemode. 0x81A4 corresponds to 0o100644(a regular file with 896*33f37583SAndroid Build Coastguard Worker # '-rw-r--r--' permission). 897*33f37583SAndroid Build Coastguard Worker info.external_attr = 0x81A40000 898*33f37583SAndroid Build Coastguard Worker # "apex_payload.img" should be 4K aligned 899*33f37583SAndroid Build Coastguard Worker if info.filename == 'apex_payload.img': 900*33f37583SAndroid Build Coastguard Worker data_offset = out.fp.tell() + len(info.FileHeader()) 901*33f37583SAndroid Build Coastguard Worker info.extra = b'\0' * (BLOCK_SIZE - data_offset % BLOCK_SIZE) 902*33f37583SAndroid Build Coastguard Worker data = inzip.read(info) 903*33f37583SAndroid Build Coastguard Worker out.writestr(info, data) 904*33f37583SAndroid Build Coastguard Worker 905*33f37583SAndroid Build Coastguard Worker 906*33f37583SAndroid Build Coastguard Workerdef main(argv): 907*33f37583SAndroid Build Coastguard Worker global tool_path_list 908*33f37583SAndroid Build Coastguard Worker args = ParseArgs(argv) 909*33f37583SAndroid Build Coastguard Worker tool_path_list = args.apexer_tool_path 910*33f37583SAndroid Build Coastguard Worker with TempDirectory() as work_dir: 911*33f37583SAndroid Build Coastguard Worker success = CreateApex(args, work_dir) 912*33f37583SAndroid Build Coastguard Worker 913*33f37583SAndroid Build Coastguard Worker if not success: 914*33f37583SAndroid Build Coastguard Worker sys.exit(1) 915*33f37583SAndroid Build Coastguard Worker 916*33f37583SAndroid Build Coastguard Worker 917*33f37583SAndroid Build Coastguard Workerif __name__ == '__main__': 918*33f37583SAndroid Build Coastguard Worker main(sys.argv[1:]) 919