Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 1 | #!/usr/bin/env python |
| 2 | # |
| 3 | # Copyright (C) 2018 The Android Open Source Project |
| 4 | # |
| 5 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 | # you may not use this file except in compliance with the License. |
| 7 | # You may obtain a copy of the License at |
| 8 | # |
| 9 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | # |
| 11 | # Unless required by applicable law or agreed to in writing, software |
| 12 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | # See the License for the specific language governing permissions and |
| 15 | # limitations under the License. |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 16 | """apexer is a command line tool for creating an APEX file, a package format for system components. |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 17 | |
| 18 | Typical usage: apexer input_dir output.apex |
| 19 | |
| 20 | """ |
| 21 | |
Dario Freni | ed536ee | 2020-01-02 18:52:59 +0000 | [diff] [blame] | 22 | import apex_build_info_pb2 |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 23 | import argparse |
Jiyong Park | 3d44bb4 | 2018-12-18 19:44:54 +0900 | [diff] [blame] | 24 | import hashlib |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 25 | import os |
Jiyong Park | d372d3b | 2018-08-28 22:06:56 +0900 | [diff] [blame] | 26 | import re |
Tianjie Xu | 65bf331 | 2020-01-30 17:27:03 -0800 | [diff] [blame] | 27 | import shlex |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 28 | import shutil |
| 29 | import subprocess |
| 30 | import sys |
| 31 | import tempfile |
Jiyong Park | 3d44bb4 | 2018-12-18 19:44:54 +0900 | [diff] [blame] | 32 | import uuid |
Jiyong Park | 1c4605a | 2019-02-08 02:49:59 +0900 | [diff] [blame] | 33 | import xml.etree.ElementTree as ET |
Dario Freni | adbee5d | 2018-12-27 12:44:01 +0000 | [diff] [blame] | 34 | from apex_manifest import ValidateApexManifest |
| 35 | from apex_manifest import ApexManifestError |
Baligh Uddin | 68f16e5 | 2020-02-19 21:39:33 -0800 | [diff] [blame] | 36 | from manifest import android_ns |
| 37 | from manifest import find_child_with_attribute |
| 38 | from manifest import get_children_with_tag |
| 39 | from manifest import get_indent |
| 40 | from manifest import parse_manifest |
| 41 | from manifest import write_xml |
| 42 | from xml.dom import minidom |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 43 | |
Alex Light | 18a1b19 | 2019-03-04 14:27:28 -0800 | [diff] [blame] | 44 | tool_path_list = None |
Jiyong Park | 4cd9b2b | 2019-01-19 13:42:35 +0900 | [diff] [blame] | 45 | BLOCK_SIZE = 4096 |
| 46 | |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 47 | |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 48 | def ParseArgs(argv): |
| 49 | parser = argparse.ArgumentParser(description='Create an APEX file') |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 50 | parser.add_argument( |
| 51 | '-f', '--force', action='store_true', help='force overwriting output') |
| 52 | parser.add_argument( |
| 53 | '-v', '--verbose', action='store_true', help='verbose execution') |
| 54 | parser.add_argument( |
| 55 | '--manifest', |
Jooyung Han | cd4d81e | 2019-11-02 02:47:22 +0900 | [diff] [blame] | 56 | default='apex_manifest.pb', |
| 57 | help='path to the APEX manifest file (.pb)') |
| 58 | parser.add_argument( |
| 59 | '--manifest_json', |
Jooyung Han | 2426fc9 | 2019-11-12 13:03:50 +0900 | [diff] [blame] | 60 | required=False, |
Jooyung Han | cd4d81e | 2019-11-02 02:47:22 +0900 | [diff] [blame] | 61 | help='path to the APEX manifest file (Q compatible .json)') |
| 62 | parser.add_argument( |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 63 | '--android_manifest', |
| 64 | help='path to the AndroidManifest file. If omitted, a default one is created and used' |
| 65 | ) |
| 66 | parser.add_argument( |
Baligh Uddin | 68f16e5 | 2020-02-19 21:39:33 -0800 | [diff] [blame] | 67 | '--logging_parent', |
| 68 | help=('specify logging parent as an additional <meta-data> tag.' |
| 69 | 'This value is ignored if the logging_parent meta-data tag is present.')) |
| 70 | parser.add_argument( |
Jaewoong Jung | 2247d1d | 2019-06-18 13:10:28 -0700 | [diff] [blame] | 71 | '--assets_dir', |
| 72 | help='an assets directory to be included in the APEX' |
| 73 | ) |
| 74 | parser.add_argument( |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 75 | '--file_contexts', |
| 76 | help='selinux file contexts file. Required for "image" APEXs.') |
| 77 | parser.add_argument( |
| 78 | '--canned_fs_config', |
| 79 | help='canned_fs_config specifies uid/gid/mode of files. Required for ' + |
| 80 | '"image" APEXS.') |
| 81 | parser.add_argument( |
| 82 | '--key', help='path to the private key file. Required for "image" APEXs.') |
| 83 | parser.add_argument( |
| 84 | '--pubkey', |
| 85 | help='path to the public key file. Used to bundle the public key in APEX for testing.' |
| 86 | ) |
| 87 | parser.add_argument( |
Tianjie Xu | 65bf331 | 2020-01-30 17:27:03 -0800 | [diff] [blame] | 88 | '--signing_args', |
| 89 | help='the extra signing arguments passed to avbtool. Used for "image" APEXs.' |
| 90 | ) |
| 91 | parser.add_argument( |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 92 | 'input_dir', |
| 93 | metavar='INPUT_DIR', |
| 94 | help='the directory having files to be packaged') |
| 95 | parser.add_argument('output', metavar='OUTPUT', help='name of the APEX file') |
| 96 | parser.add_argument( |
| 97 | '--payload_type', |
| 98 | metavar='TYPE', |
| 99 | required=False, |
| 100 | default='image', |
| 101 | choices=['zip', 'image'], |
| 102 | help='type of APEX payload being built "zip" or "image"') |
| 103 | parser.add_argument( |
| 104 | '--override_apk_package_name', |
| 105 | required=False, |
| 106 | help='package name of the APK container. Default is the apex name in --manifest.' |
| 107 | ) |
| 108 | parser.add_argument( |
Jooyung Han | 7897685 | 2019-06-15 03:36:34 +0900 | [diff] [blame] | 109 | '--no_hashtree', |
| 110 | required=False, |
| 111 | action='store_true', |
| 112 | help='hashtree is omitted from "image".' |
| 113 | ) |
| 114 | parser.add_argument( |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 115 | '--android_jar_path', |
| 116 | required=False, |
| 117 | default='prebuilts/sdk/current/public/android.jar', |
| 118 | help='path to use as the source of the android API.') |
| 119 | apexer_path_in_environ = 'APEXER_TOOL_PATH' in os.environ |
| 120 | parser.add_argument( |
| 121 | '--apexer_tool_path', |
| 122 | required=not apexer_path_in_environ, |
| 123 | default=os.environ['APEXER_TOOL_PATH'].split(':') |
| 124 | if apexer_path_in_environ else None, |
| 125 | type=lambda s: s.split(':'), |
| 126 | help="""A list of directories containing all the tools used by apexer (e.g. |
Alex Light | 18a1b19 | 2019-03-04 14:27:28 -0800 | [diff] [blame] | 127 | mke2fs, avbtool, etc.) separated by ':'. Can also be set using the |
| 128 | APEXER_TOOL_PATH environment variable""") |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 129 | parser.add_argument( |
| 130 | '--target_sdk_version', |
| 131 | required=False, |
| 132 | help='Default target SDK version to use for AndroidManifest.xml') |
Jooyung Han | 500eba6 | 2019-09-27 00:01:03 +0900 | [diff] [blame] | 133 | parser.add_argument( |
Baligh Uddin | 38b77da | 2020-01-18 20:28:11 -0800 | [diff] [blame] | 134 | '--min_sdk_version', |
| 135 | required=False, |
| 136 | help='Default Min SDK version to use for AndroidManifest.xml') |
| 137 | parser.add_argument( |
Jooyung Han | 500eba6 | 2019-09-27 00:01:03 +0900 | [diff] [blame] | 138 | '--do_not_check_keyname', |
| 139 | required=False, |
| 140 | action='store_true', |
| 141 | help='Do not check key name. Use the name of apex instead of the basename of --key.') |
Dario Freni | ed536ee | 2020-01-02 18:52:59 +0000 | [diff] [blame] | 142 | parser.add_argument( |
| 143 | '--include_build_info', |
| 144 | required=False, |
| 145 | action='store_true', |
| 146 | help='Include build information file in the resulting apex.') |
| 147 | parser.add_argument( |
Dario Freni | 29f6e45 | 2020-01-07 15:48:04 +0000 | [diff] [blame] | 148 | '--include_cmd_line_in_build_info', |
| 149 | required=False, |
| 150 | action='store_true', |
| 151 | help='Include the command line in the build information file in the resulting apex. ' |
| 152 | 'Note that this makes it harder to make deterministic builds.') |
| 153 | parser.add_argument( |
Dario Freni | ed536ee | 2020-01-02 18:52:59 +0000 | [diff] [blame] | 154 | '--build_info', |
| 155 | required=False, |
| 156 | help='Build information file to be used for default values.') |
Mohammad Samiul Islam | 6b4bce2 | 2020-02-26 11:37:29 +0000 | [diff] [blame] | 157 | parser.add_argument( |
| 158 | '--payload_only', |
| 159 | action='store_true', |
| 160 | help='Outputs the payload image/zip only.' |
| 161 | ) |
Mohammad Samiul Islam | 2cc12eb | 2020-02-27 17:26:59 +0000 | [diff] [blame] | 162 | parser.add_argument( |
| 163 | '--unsigned_payload_only', |
| 164 | action='store_true', |
| 165 | help="""Outputs the unsigned payload image/zip only. Also, setting this flag implies |
| 166 | --payload_only is set too.""" |
| 167 | ) |
Dario Freni | bb779fc | 2020-04-27 18:26:47 +0100 | [diff] [blame] | 168 | parser.add_argument( |
| 169 | '--unsigned_payload', |
| 170 | action='store_true', |
| 171 | help="""Skip signing the apex payload. Used only for testing purposes.""" |
| 172 | ) |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 173 | return parser.parse_args(argv) |
| 174 | |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 175 | |
Roland Levillain | 3f1c3c9 | 2018-10-02 18:23:58 +0100 | [diff] [blame] | 176 | def FindBinaryPath(binary): |
| 177 | for path in tool_path_list: |
| 178 | binary_path = os.path.join(path, binary) |
| 179 | if os.path.exists(binary_path): |
| 180 | return binary_path |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 181 | raise Exception('Failed to find binary ' + binary + ' in path ' + |
| 182 | ':'.join(tool_path_list)) |
| 183 | |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 184 | |
| 185 | def RunCommand(cmd, verbose=False, env=None): |
| 186 | env = env or {} |
| 187 | env.update(os.environ.copy()) |
Jiyong Park | 8537e2a | 2018-09-05 21:34:21 +0900 | [diff] [blame] | 188 | |
Roland Levillain | 3f1c3c9 | 2018-10-02 18:23:58 +0100 | [diff] [blame] | 189 | cmd[0] = FindBinaryPath(cmd[0]) |
Jiyong Park | 8537e2a | 2018-09-05 21:34:21 +0900 | [diff] [blame] | 190 | |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 191 | if verbose: |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 192 | print('Running: ' + ' '.join(cmd)) |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 193 | p = subprocess.Popen( |
| 194 | cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=env) |
| 195 | output, _ = p.communicate() |
| 196 | |
Andreas Gampe | 8e1ac68 | 2018-11-28 10:44:35 -0800 | [diff] [blame] | 197 | if verbose or p.returncode is not 0: |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 198 | print(output.rstrip()) |
| 199 | |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 200 | assert p.returncode is 0, 'Failed to execute: ' + ' '.join(cmd) |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 201 | |
| 202 | return (output, p.returncode) |
| 203 | |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 204 | |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 205 | def GetDirSize(dir_name): |
| 206 | size = 0 |
Jiyong Park | 4edc623 | 2018-09-03 12:31:58 +0900 | [diff] [blame] | 207 | for dirpath, _, filenames in os.walk(dir_name): |
Jiyong Park | 4cd9b2b | 2019-01-19 13:42:35 +0900 | [diff] [blame] | 208 | size += RoundUp(os.path.getsize(dirpath), BLOCK_SIZE) |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 209 | for f in filenames: |
Jiyong Park | 3c12675 | 2019-11-22 17:44:31 +0900 | [diff] [blame] | 210 | path = os.path.join(dirpath, f) |
| 211 | if not os.path.isfile(path): |
| 212 | continue |
| 213 | size += RoundUp(os.path.getsize(path), BLOCK_SIZE) |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 214 | return size |
| 215 | |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 216 | |
Jiyong Park | 4cd9b2b | 2019-01-19 13:42:35 +0900 | [diff] [blame] | 217 | def GetFilesAndDirsCount(dir_name): |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 218 | count = 0 |
Jiyong Park | 4cd9b2b | 2019-01-19 13:42:35 +0900 | [diff] [blame] | 219 | for root, dirs, files in os.walk(dir_name): |
| 220 | count += (len(dirs) + len(files)) |
| 221 | return count |
Jiyong Park | a515307 | 2018-09-04 10:01:41 +0900 | [diff] [blame] | 222 | |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 223 | |
Jiyong Park | d372d3b | 2018-08-28 22:06:56 +0900 | [diff] [blame] | 224 | def RoundUp(size, unit): |
| 225 | assert unit & (unit - 1) == 0 |
| 226 | return (size + unit - 1) & (~(unit - 1)) |
| 227 | |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 228 | |
Jiyong Park | a515307 | 2018-09-04 10:01:41 +0900 | [diff] [blame] | 229 | def PrepareAndroidManifest(package, version): |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 230 | template = """\ |
| 231 | <?xml version="1.0" encoding="utf-8"?> |
| 232 | <manifest xmlns:android="http://schemas.android.com/apk/res/android" |
Nicholas Lativy | 0e69652 | 2018-11-23 15:16:33 +0000 | [diff] [blame] | 233 | package="{package}" android:versionCode="{version}"> |
Jiyong Park | 5fbeac1 | 2018-11-15 16:05:05 +0900 | [diff] [blame] | 234 | <!-- APEX does not have classes.dex --> |
| 235 | <application android:hasCode="false" /> |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 236 | </manifest> |
| 237 | """ |
Jiyong Park | a515307 | 2018-09-04 10:01:41 +0900 | [diff] [blame] | 238 | return template.format(package=package, version=version) |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 239 | |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 240 | |
Jiyong Park | 1c4605a | 2019-02-08 02:49:59 +0900 | [diff] [blame] | 241 | def ValidateAndroidManifest(package, android_manifest): |
| 242 | tree = ET.parse(android_manifest) |
| 243 | manifest_tag = tree.getroot() |
| 244 | package_in_xml = manifest_tag.attrib['package'] |
| 245 | if package_in_xml != package: |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 246 | raise Exception("Package name '" + package_in_xml + "' in '" + |
| 247 | android_manifest + " differ from package name '" + package + |
Jooyung Han | 2426fc9 | 2019-11-12 13:03:50 +0900 | [diff] [blame] | 248 | "' in the apex_manifest.pb") |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 249 | |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 250 | |
| 251 | def ValidateArgs(args): |
Dario Freni | ed536ee | 2020-01-02 18:52:59 +0000 | [diff] [blame] | 252 | build_info = None |
| 253 | |
| 254 | if args.build_info is not None: |
| 255 | if not os.path.exists(args.build_info): |
| 256 | print("Build info file '" + args.build_info + "' does not exist") |
| 257 | return False |
| 258 | with open(args.build_info) as buildInfoFile: |
| 259 | build_info = apex_build_info_pb2.ApexBuildInfo() |
| 260 | build_info.ParseFromString(buildInfoFile.read()) |
| 261 | |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 262 | if not os.path.exists(args.manifest): |
| 263 | print("Manifest file '" + args.manifest + "' does not exist") |
| 264 | return False |
| 265 | |
| 266 | if not os.path.isfile(args.manifest): |
| 267 | print("Manifest file '" + args.manifest + "' is not a file") |
| 268 | return False |
| 269 | |
Jiyong Park | 1c4605a | 2019-02-08 02:49:59 +0900 | [diff] [blame] | 270 | if args.android_manifest is not None: |
| 271 | if not os.path.exists(args.android_manifest): |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 272 | print("Android Manifest file '" + args.android_manifest + |
| 273 | "' does not exist") |
Jiyong Park | 1c4605a | 2019-02-08 02:49:59 +0900 | [diff] [blame] | 274 | return False |
| 275 | |
| 276 | if not os.path.isfile(args.android_manifest): |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 277 | print("Android Manifest file '" + args.android_manifest + |
| 278 | "' is not a file") |
Jiyong Park | 1c4605a | 2019-02-08 02:49:59 +0900 | [diff] [blame] | 279 | return False |
Dario Freni | ed536ee | 2020-01-02 18:52:59 +0000 | [diff] [blame] | 280 | elif build_info is not None: |
| 281 | with tempfile.NamedTemporaryFile(delete=False) as temp: |
| 282 | temp.write(build_info.android_manifest) |
| 283 | args.android_manifest = temp.name |
Jiyong Park | 1c4605a | 2019-02-08 02:49:59 +0900 | [diff] [blame] | 284 | |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 285 | if not os.path.exists(args.input_dir): |
| 286 | print("Input directory '" + args.input_dir + "' does not exist") |
| 287 | return False |
| 288 | |
| 289 | if not os.path.isdir(args.input_dir): |
| 290 | print("Input directory '" + args.input_dir + "' is not a directory") |
| 291 | return False |
| 292 | |
| 293 | if not args.force and os.path.exists(args.output): |
| 294 | print(args.output + ' already exists. Use --force to overwrite.') |
| 295 | return False |
| 296 | |
Mohammad Samiul Islam | 2cc12eb | 2020-02-27 17:26:59 +0000 | [diff] [blame] | 297 | if args.unsigned_payload_only: |
| 298 | args.payload_only = True; |
Dario Freni | bb779fc | 2020-04-27 18:26:47 +0100 | [diff] [blame] | 299 | args.unsigned_payload = True; |
Mohammad Samiul Islam | 2cc12eb | 2020-02-27 17:26:59 +0000 | [diff] [blame] | 300 | |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 301 | if args.payload_type == 'image': |
Dario Freni | bb779fc | 2020-04-27 18:26:47 +0100 | [diff] [blame] | 302 | if not args.key and not args.unsigned_payload: |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 303 | print('Missing --key {keyfile} argument!') |
Alex Light | cc62585 | 2018-11-29 17:15:45 -0800 | [diff] [blame] | 304 | return False |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 305 | |
Alex Light | cc62585 | 2018-11-29 17:15:45 -0800 | [diff] [blame] | 306 | if not args.file_contexts: |
Dario Freni | ed536ee | 2020-01-02 18:52:59 +0000 | [diff] [blame] | 307 | if build_info is not None: |
| 308 | with tempfile.NamedTemporaryFile(delete=False) as temp: |
| 309 | temp.write(build_info.file_contexts) |
| 310 | args.file_contexts = temp.name |
| 311 | else: |
| 312 | print('Missing --file_contexts {contexts} argument, or a --build_info argument!') |
| 313 | return False |
Alex Light | cc62585 | 2018-11-29 17:15:45 -0800 | [diff] [blame] | 314 | |
| 315 | if not args.canned_fs_config: |
Dario Freni | ed536ee | 2020-01-02 18:52:59 +0000 | [diff] [blame] | 316 | if not args.canned_fs_config: |
| 317 | if build_info is not None: |
| 318 | with tempfile.NamedTemporaryFile(delete=False) as temp: |
| 319 | temp.write(build_info.canned_fs_config) |
| 320 | args.canned_fs_config = temp.name |
| 321 | else: |
| 322 | print('Missing ----canned_fs_config {config} argument, or a --build_info argument!') |
| 323 | return False |
Alex Light | cc62585 | 2018-11-29 17:15:45 -0800 | [diff] [blame] | 324 | |
Dario Freni | 3601ea4 | 2020-01-23 10:03:25 +0000 | [diff] [blame] | 325 | if not args.target_sdk_version: |
| 326 | if build_info is not None: |
| 327 | if build_info.target_sdk_version: |
| 328 | args.target_sdk_version = build_info.target_sdk_version |
| 329 | |
| 330 | if not args.no_hashtree: |
| 331 | if build_info is not None: |
| 332 | if build_info.no_hashtree: |
| 333 | args.no_hashtree = True |
| 334 | |
| 335 | if not args.min_sdk_version: |
| 336 | if build_info is not None: |
| 337 | if build_info.min_sdk_version: |
| 338 | args.min_sdk_version = build_info.min_sdk_version |
| 339 | |
Nikita Ioffe | 81df65e | 2020-03-23 22:04:50 +0000 | [diff] [blame] | 340 | if not args.override_apk_package_name: |
| 341 | if build_info is not None: |
| 342 | if build_info.override_apk_package_name: |
| 343 | args.override_apk_package_name = build_info.override_apk_package_name |
| 344 | |
| 345 | if not args.logging_parent: |
| 346 | if build_info is not None: |
| 347 | if build_info.logging_parent: |
| 348 | args.logging_parent = build_info.logging_parent |
| 349 | |
Alex Light | cc62585 | 2018-11-29 17:15:45 -0800 | [diff] [blame] | 350 | return True |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 351 | |
Dario Freni | ed536ee | 2020-01-02 18:52:59 +0000 | [diff] [blame] | 352 | def GenerateBuildInfo(args): |
| 353 | build_info = apex_build_info_pb2.ApexBuildInfo() |
Dario Freni | 29f6e45 | 2020-01-07 15:48:04 +0000 | [diff] [blame] | 354 | if (args.include_cmd_line_in_build_info): |
| 355 | build_info.apexer_command_line = str(sys.argv) |
Dario Freni | ed536ee | 2020-01-02 18:52:59 +0000 | [diff] [blame] | 356 | |
| 357 | with open(args.file_contexts) as f: |
| 358 | build_info.file_contexts = f.read() |
| 359 | |
| 360 | with open(args.canned_fs_config) as f: |
| 361 | build_info.canned_fs_config = f.read() |
| 362 | |
| 363 | with open(args.android_manifest) as f: |
| 364 | build_info.android_manifest = f.read() |
| 365 | |
Dario Freni | 3601ea4 | 2020-01-23 10:03:25 +0000 | [diff] [blame] | 366 | if args.target_sdk_version: |
| 367 | build_info.target_sdk_version = args.target_sdk_version |
| 368 | |
| 369 | if args.min_sdk_version: |
| 370 | build_info.min_sdk_version = args.min_sdk_version |
| 371 | |
| 372 | if args.no_hashtree: |
| 373 | build_info.no_hashtree = True |
| 374 | |
Nikita Ioffe | 81df65e | 2020-03-23 22:04:50 +0000 | [diff] [blame] | 375 | if args.override_apk_package_name: |
| 376 | build_info.override_apk_package_name = args.override_apk_package_name |
| 377 | |
| 378 | if args.logging_parent: |
| 379 | build_info.logging_parent = args.logging_parent |
| 380 | |
Dario Freni | ed536ee | 2020-01-02 18:52:59 +0000 | [diff] [blame] | 381 | return build_info |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 382 | |
Baligh Uddin | 68f16e5 | 2020-02-19 21:39:33 -0800 | [diff] [blame] | 383 | def AddLoggingParent(android_manifest, logging_parent_value): |
| 384 | """Add logging parent as an additional <meta-data> tag. |
| 385 | |
| 386 | Args: |
| 387 | android_manifest: A string representing AndroidManifest.xml |
| 388 | logging_parent_value: A string representing the logging |
| 389 | parent value. |
| 390 | Raises: |
| 391 | RuntimeError: Invalid manifest |
| 392 | Returns: |
| 393 | A path to modified AndroidManifest.xml |
| 394 | """ |
| 395 | doc = minidom.parse(android_manifest) |
| 396 | manifest = parse_manifest(doc) |
| 397 | logging_parent_key = 'android.content.pm.LOGGING_PARENT' |
| 398 | elems = get_children_with_tag(manifest, 'application') |
| 399 | application = elems[0] if len(elems) == 1 else None |
| 400 | if len(elems) > 1: |
| 401 | raise RuntimeError('found multiple <application> tags') |
| 402 | elif not elems: |
| 403 | application = doc.createElement('application') |
| 404 | indent = get_indent(manifest.firstChild, 1) |
| 405 | first = manifest.firstChild |
| 406 | manifest.insertBefore(doc.createTextNode(indent), first) |
| 407 | manifest.insertBefore(application, first) |
| 408 | |
| 409 | indent = get_indent(application.firstChild, 2) |
| 410 | last = application.lastChild |
| 411 | if last is not None and last.nodeType != minidom.Node.TEXT_NODE: |
| 412 | last = None |
| 413 | |
| 414 | if not find_child_with_attribute(application, 'meta-data', android_ns, |
| 415 | 'name', logging_parent_key): |
| 416 | ul = doc.createElement('meta-data') |
| 417 | ul.setAttributeNS(android_ns, 'android:name', logging_parent_key) |
| 418 | ul.setAttributeNS(android_ns, 'android:value', logging_parent_value) |
| 419 | application.insertBefore(doc.createTextNode(indent), last) |
| 420 | application.insertBefore(ul, last) |
| 421 | last = application.lastChild |
| 422 | |
| 423 | if last and last.nodeType != minidom.Node.TEXT_NODE: |
| 424 | indent = get_indent(application.previousSibling, 1) |
| 425 | application.appendChild(doc.createTextNode(indent)) |
| 426 | |
| 427 | with tempfile.NamedTemporaryFile(delete=False) as temp: |
| 428 | write_xml(temp, doc) |
| 429 | return temp.name |
| 430 | |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 431 | def CreateApex(args, work_dir): |
| 432 | if not ValidateArgs(args): |
| 433 | return False |
| 434 | |
Jiyong Park | 847d759 | 2018-11-20 10:02:13 +0900 | [diff] [blame] | 435 | if args.verbose: |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 436 | print 'Using tools from ' + str(tool_path_list) |
Jiyong Park | 847d759 | 2018-11-20 10:02:13 +0900 | [diff] [blame] | 437 | |
Jooyung Han | cd4d81e | 2019-11-02 02:47:22 +0900 | [diff] [blame] | 438 | def copyfile(src, dst): |
| 439 | if args.verbose: |
| 440 | print('Copying ' + src + ' to ' + dst) |
| 441 | shutil.copyfile(src, dst) |
| 442 | |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 443 | try: |
Jooyung Han | cd4d81e | 2019-11-02 02:47:22 +0900 | [diff] [blame] | 444 | manifest_apex = ValidateApexManifest(args.manifest) |
Dario Freni | adbee5d | 2018-12-27 12:44:01 +0000 | [diff] [blame] | 445 | except ApexManifestError as err: |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 446 | print("'" + args.manifest + "' is not a valid manifest file") |
Dario Freni | adbee5d | 2018-12-27 12:44:01 +0000 | [diff] [blame] | 447 | print err.errmessage |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 448 | return False |
| 449 | except IOError: |
| 450 | print("Cannot read manifest file: '" + args.manifest + "'") |
| 451 | return False |
| 452 | |
Jiyong Park | 7c10f41 | 2018-08-21 21:28:41 +0900 | [diff] [blame] | 453 | # create an empty ext4 image that is sufficiently big |
Jiyong Park | 4cd9b2b | 2019-01-19 13:42:35 +0900 | [diff] [blame] | 454 | # sufficiently big = size + 16MB margin |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 455 | size_in_mb = (GetDirSize(args.input_dir) / (1024 * 1024)) + 16 |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 456 | |
| 457 | content_dir = os.path.join(work_dir, 'content') |
| 458 | os.mkdir(content_dir) |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 459 | |
| 460 | # APEX manifest is also included in the image. The manifest is included |
| 461 | # twice: once inside the image and once outside the image (but still |
| 462 | # within the zip container). |
| 463 | manifests_dir = os.path.join(work_dir, 'manifests') |
| 464 | os.mkdir(manifests_dir) |
Jooyung Han | cd4d81e | 2019-11-02 02:47:22 +0900 | [diff] [blame] | 465 | copyfile(args.manifest, os.path.join(manifests_dir, 'apex_manifest.pb')) |
| 466 | if args.manifest_json: |
| 467 | # manifest_json is for compatibility |
| 468 | copyfile(args.manifest_json, os.path.join(manifests_dir, 'apex_manifest.json')) |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 469 | |
Alex Light | cc62585 | 2018-11-29 17:15:45 -0800 | [diff] [blame] | 470 | if args.payload_type == 'image': |
Dario Freni | bb779fc | 2020-04-27 18:26:47 +0100 | [diff] [blame] | 471 | if args.do_not_check_keyname or args.unsigned_payload: |
Jooyung Han | 500eba6 | 2019-09-27 00:01:03 +0900 | [diff] [blame] | 472 | key_name = manifest_apex.name |
Mohammad Samiul Islam | 2cc12eb | 2020-02-27 17:26:59 +0000 | [diff] [blame] | 473 | else: |
| 474 | key_name = os.path.basename(os.path.splitext(args.key)[0]) |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 475 | |
Abhijeet Kaur | c4b2183 | 2019-01-02 16:50:21 +0000 | [diff] [blame] | 476 | if manifest_apex.name != key_name: |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 477 | print("package name '" + manifest_apex.name + |
| 478 | "' does not match with key name '" + key_name + "'") |
Alex Light | cc62585 | 2018-11-29 17:15:45 -0800 | [diff] [blame] | 479 | return False |
| 480 | img_file = os.path.join(content_dir, 'apex_payload.img') |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 481 | |
Jiyong Park | 4cd9b2b | 2019-01-19 13:42:35 +0900 | [diff] [blame] | 482 | # margin is for files that are not under args.input_dir. this consists of |
Jooyung Han | cd4d81e | 2019-11-02 02:47:22 +0900 | [diff] [blame] | 483 | # n inodes for apex_manifest files and 11 reserved inodes for ext4. |
Jiyong Park | 4cd9b2b | 2019-01-19 13:42:35 +0900 | [diff] [blame] | 484 | # TOBO(b/122991714) eliminate these details. use build_image.py which |
| 485 | # determines the optimal inode count by first building an image and then |
| 486 | # count the inodes actually used. |
Jooyung Han | cd4d81e | 2019-11-02 02:47:22 +0900 | [diff] [blame] | 487 | inode_num_margin = GetFilesAndDirsCount(manifests_dir) + 11 |
Jiyong Park | 4cd9b2b | 2019-01-19 13:42:35 +0900 | [diff] [blame] | 488 | inode_num = GetFilesAndDirsCount(args.input_dir) + inode_num_margin |
| 489 | |
Alex Light | cc62585 | 2018-11-29 17:15:45 -0800 | [diff] [blame] | 490 | cmd = ['mke2fs'] |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 491 | cmd.extend(['-O', '^has_journal']) # because image is read-only |
Jiyong Park | 4cd9b2b | 2019-01-19 13:42:35 +0900 | [diff] [blame] | 492 | cmd.extend(['-b', str(BLOCK_SIZE)]) |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 493 | cmd.extend(['-m', '0']) # reserved block percentage |
Alex Light | cc62585 | 2018-11-29 17:15:45 -0800 | [diff] [blame] | 494 | cmd.extend(['-t', 'ext4']) |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 495 | cmd.extend(['-I', '256']) # inode size |
Jiyong Park | 4cd9b2b | 2019-01-19 13:42:35 +0900 | [diff] [blame] | 496 | cmd.extend(['-N', str(inode_num)]) |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 497 | uu = str(uuid.uuid5(uuid.NAMESPACE_URL, 'www.android.com')) |
Jiyong Park | 3d44bb4 | 2018-12-18 19:44:54 +0900 | [diff] [blame] | 498 | cmd.extend(['-U', uu]) |
| 499 | cmd.extend(['-E', 'hash_seed=' + uu]) |
Alex Light | cc62585 | 2018-11-29 17:15:45 -0800 | [diff] [blame] | 500 | cmd.append(img_file) |
| 501 | cmd.append(str(size_in_mb) + 'M') |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 502 | RunCommand(cmd, args.verbose, {'E2FSPROGS_FAKE_TIME': '1'}) |
Jiyong Park | d372d3b | 2018-08-28 22:06:56 +0900 | [diff] [blame] | 503 | |
Alex Light | cc62585 | 2018-11-29 17:15:45 -0800 | [diff] [blame] | 504 | # Compile the file context into the binary form |
| 505 | compiled_file_contexts = os.path.join(work_dir, 'file_contexts.bin') |
| 506 | cmd = ['sefcontext_compile'] |
| 507 | cmd.extend(['-o', compiled_file_contexts]) |
| 508 | cmd.append(args.file_contexts) |
| 509 | RunCommand(cmd, args.verbose) |
Jiyong Park | d372d3b | 2018-08-28 22:06:56 +0900 | [diff] [blame] | 510 | |
Alex Light | cc62585 | 2018-11-29 17:15:45 -0800 | [diff] [blame] | 511 | # Add files to the image file |
| 512 | cmd = ['e2fsdroid'] |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 513 | cmd.append('-e') # input is not android_sparse_file |
Alex Light | cc62585 | 2018-11-29 17:15:45 -0800 | [diff] [blame] | 514 | cmd.extend(['-f', args.input_dir]) |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 515 | cmd.extend(['-T', '0']) # time is set to epoch |
Alex Light | cc62585 | 2018-11-29 17:15:45 -0800 | [diff] [blame] | 516 | cmd.extend(['-S', compiled_file_contexts]) |
| 517 | cmd.extend(['-C', args.canned_fs_config]) |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 518 | cmd.append('-s') # share dup blocks |
Alex Light | cc62585 | 2018-11-29 17:15:45 -0800 | [diff] [blame] | 519 | cmd.append(img_file) |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 520 | RunCommand(cmd, args.verbose, {'E2FSPROGS_FAKE_TIME': '1'}) |
Alex Light | cc62585 | 2018-11-29 17:15:45 -0800 | [diff] [blame] | 521 | |
| 522 | cmd = ['e2fsdroid'] |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 523 | cmd.append('-e') # input is not android_sparse_file |
Alex Light | cc62585 | 2018-11-29 17:15:45 -0800 | [diff] [blame] | 524 | cmd.extend(['-f', manifests_dir]) |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 525 | cmd.extend(['-T', '0']) # time is set to epoch |
Alex Light | cc62585 | 2018-11-29 17:15:45 -0800 | [diff] [blame] | 526 | cmd.extend(['-S', compiled_file_contexts]) |
| 527 | cmd.extend(['-C', args.canned_fs_config]) |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 528 | cmd.append('-s') # share dup blocks |
Alex Light | cc62585 | 2018-11-29 17:15:45 -0800 | [diff] [blame] | 529 | cmd.append(img_file) |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 530 | RunCommand(cmd, args.verbose, {'E2FSPROGS_FAKE_TIME': '1'}) |
Alex Light | cc62585 | 2018-11-29 17:15:45 -0800 | [diff] [blame] | 531 | |
| 532 | # Resize the image file to save space |
| 533 | cmd = ['resize2fs'] |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 534 | cmd.append('-M') # shrink as small as possible |
Alex Light | cc62585 | 2018-11-29 17:15:45 -0800 | [diff] [blame] | 535 | cmd.append(img_file) |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 536 | RunCommand(cmd, args.verbose, {'E2FSPROGS_FAKE_TIME': '1'}) |
Alex Light | cc62585 | 2018-11-29 17:15:45 -0800 | [diff] [blame] | 537 | |
Mohammad Samiul Islam | 2cc12eb | 2020-02-27 17:26:59 +0000 | [diff] [blame] | 538 | if args.unsigned_payload_only: |
| 539 | shutil.copyfile(img_file, args.output) |
| 540 | if (args.verbose): |
| 541 | print('Created (unsigned payload only) ' + args.output) |
| 542 | return True |
| 543 | |
Dario Freni | bb779fc | 2020-04-27 18:26:47 +0100 | [diff] [blame] | 544 | if not args.unsigned_payload: |
| 545 | cmd = ['avbtool'] |
| 546 | cmd.append('add_hashtree_footer') |
| 547 | cmd.append('--do_not_generate_fec') |
| 548 | cmd.extend(['--algorithm', 'SHA256_RSA4096']) |
Jiyong Park | 2934d55 | 2020-05-09 15:09:43 +0900 | [diff] [blame] | 549 | cmd.extend(['--hash_algorithm', 'sha256']) |
Dario Freni | bb779fc | 2020-04-27 18:26:47 +0100 | [diff] [blame] | 550 | cmd.extend(['--key', args.key]) |
| 551 | cmd.extend(['--prop', 'apex.key:' + key_name]) |
| 552 | # Set up the salt based on manifest content which includes name |
| 553 | # and version |
| 554 | salt = hashlib.sha256(manifest_apex.SerializeToString()).hexdigest() |
| 555 | cmd.extend(['--salt', salt]) |
| 556 | cmd.extend(['--image', img_file]) |
| 557 | if args.no_hashtree: |
| 558 | cmd.append('--no_hashtree') |
| 559 | if args.signing_args: |
| 560 | cmd.extend(shlex.split(args.signing_args)) |
| 561 | RunCommand(cmd, args.verbose) |
Alex Light | cc62585 | 2018-11-29 17:15:45 -0800 | [diff] [blame] | 562 | |
Dario Freni | bb779fc | 2020-04-27 18:26:47 +0100 | [diff] [blame] | 563 | # Get the minimum size of the partition required. |
| 564 | # TODO(b/113320014) eliminate this step |
| 565 | info, _ = RunCommand(['avbtool', 'info_image', '--image', img_file], |
| 566 | args.verbose) |
| 567 | vbmeta_offset = int(re.search('VBMeta\ offset:\ *([0-9]+)', info).group(1)) |
| 568 | vbmeta_size = int(re.search('VBMeta\ size:\ *([0-9]+)', info).group(1)) |
| 569 | partition_size = RoundUp(vbmeta_offset + vbmeta_size, |
| 570 | BLOCK_SIZE) + BLOCK_SIZE |
Alex Light | cc62585 | 2018-11-29 17:15:45 -0800 | [diff] [blame] | 571 | |
Dario Freni | bb779fc | 2020-04-27 18:26:47 +0100 | [diff] [blame] | 572 | # Resize to the minimum size |
| 573 | # TODO(b/113320014) eliminate this step |
| 574 | cmd = ['avbtool'] |
| 575 | cmd.append('resize_image') |
| 576 | cmd.extend(['--image', img_file]) |
| 577 | cmd.extend(['--partition_size', str(partition_size)]) |
| 578 | RunCommand(cmd, args.verbose) |
Alex Light | cc62585 | 2018-11-29 17:15:45 -0800 | [diff] [blame] | 579 | else: |
| 580 | img_file = os.path.join(content_dir, 'apex_payload.zip') |
| 581 | cmd = ['soong_zip'] |
| 582 | cmd.extend(['-o', img_file]) |
| 583 | cmd.extend(['-C', args.input_dir]) |
| 584 | cmd.extend(['-D', args.input_dir]) |
| 585 | cmd.extend(['-C', manifests_dir]) |
| 586 | cmd.extend(['-D', manifests_dir]) |
| 587 | RunCommand(cmd, args.verbose) |
Jiyong Park | d372d3b | 2018-08-28 22:06:56 +0900 | [diff] [blame] | 588 | |
Mohammad Samiul Islam | 6b4bce2 | 2020-02-26 11:37:29 +0000 | [diff] [blame] | 589 | if args.payload_only: |
| 590 | shutil.copyfile(img_file, args.output) |
| 591 | if (args.verbose): |
| 592 | print('Created (payload only) ' + args.output) |
| 593 | return True |
| 594 | |
Jiyong Park | 7c10f41 | 2018-08-21 21:28:41 +0900 | [diff] [blame] | 595 | # package the image file and APEX manifest as an APK. |
Jiyong Park | 1c4605a | 2019-02-08 02:49:59 +0900 | [diff] [blame] | 596 | # The AndroidManifest file is automatically generated if not given. |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 597 | android_manifest_file = os.path.join(work_dir, 'AndroidManifest.xml') |
Jiyong Park | 1c4605a | 2019-02-08 02:49:59 +0900 | [diff] [blame] | 598 | if not args.android_manifest: |
| 599 | if args.verbose: |
| 600 | print('Creating AndroidManifest ' + android_manifest_file) |
| 601 | with open(android_manifest_file, 'w+') as f: |
| 602 | app_package_name = manifest_apex.name |
| 603 | f.write(PrepareAndroidManifest(app_package_name, manifest_apex.version)) |
Dario Freni | ed536ee | 2020-01-02 18:52:59 +0000 | [diff] [blame] | 604 | args.android_manifest = android_manifest_file |
Jiyong Park | 1c4605a | 2019-02-08 02:49:59 +0900 | [diff] [blame] | 605 | else: |
| 606 | ValidateAndroidManifest(manifest_apex.name, args.android_manifest) |
| 607 | shutil.copyfile(args.android_manifest, android_manifest_file) |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 608 | |
Baligh Uddin | 68f16e5 | 2020-02-19 21:39:33 -0800 | [diff] [blame] | 609 | # If logging parent is specified, add it to the AndroidManifest. |
| 610 | if args.logging_parent != "": |
| 611 | android_manifest_file = AddLoggingParent(android_manifest_file, |
| 612 | args.logging_parent) |
| 613 | |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 614 | # copy manifest to the content dir so that it is also accessible |
| 615 | # without mounting the image |
Jooyung Han | cd4d81e | 2019-11-02 02:47:22 +0900 | [diff] [blame] | 616 | copyfile(args.manifest, os.path.join(content_dir, 'apex_manifest.pb')) |
| 617 | if args.manifest_json: |
| 618 | copyfile(args.manifest_json, os.path.join(content_dir, 'apex_manifest.json')) |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 619 | |
Jiyong Park | 9c3defa | 2018-12-27 15:09:17 +0900 | [diff] [blame] | 620 | # copy the public key, if specified |
| 621 | if args.pubkey: |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 622 | shutil.copyfile(args.pubkey, os.path.join(content_dir, 'apex_pubkey')) |
Jiyong Park | 9c3defa | 2018-12-27 15:09:17 +0900 | [diff] [blame] | 623 | |
Dario Freni | ed536ee | 2020-01-02 18:52:59 +0000 | [diff] [blame] | 624 | if args.include_build_info: |
| 625 | build_info = GenerateBuildInfo(args) |
| 626 | with open(os.path.join(content_dir, 'apex_build_info.pb'), "wb") as f: |
| 627 | f.write(build_info.SerializeToString()) |
| 628 | |
Jiyong Park | bb35247 | 2018-08-25 23:07:07 +0900 | [diff] [blame] | 629 | apk_file = os.path.join(work_dir, 'apex.apk') |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 630 | cmd = ['aapt2'] |
| 631 | cmd.append('link') |
| 632 | cmd.extend(['--manifest', android_manifest_file]) |
Jiyong Park | 1c4605a | 2019-02-08 02:49:59 +0900 | [diff] [blame] | 633 | if args.override_apk_package_name: |
| 634 | cmd.extend(['--rename-manifest-package', args.override_apk_package_name]) |
| 635 | # This version from apex_manifest.json is used when versionCode isn't |
| 636 | # specified in AndroidManifest.xml |
| 637 | cmd.extend(['--version-code', str(manifest_apex.version)]) |
Gavin Corkery | 9463856 | 2019-04-30 10:40:12 +0100 | [diff] [blame] | 638 | if manifest_apex.versionName: |
| 639 | cmd.extend(['--version-name', manifest_apex.versionName]) |
Jiyong Park | ea0208f | 2019-04-18 17:32:03 +0900 | [diff] [blame] | 640 | if args.target_sdk_version: |
| 641 | cmd.extend(['--target-sdk-version', args.target_sdk_version]) |
Baligh Uddin | 38b77da | 2020-01-18 20:28:11 -0800 | [diff] [blame] | 642 | if args.min_sdk_version: |
| 643 | cmd.extend(['--min-sdk-version', args.min_sdk_version]) |
| 644 | else: |
| 645 | # Default value for minSdkVersion. |
| 646 | cmd.extend(['--min-sdk-version', '29']) |
Jaewoong Jung | 2247d1d | 2019-06-18 13:10:28 -0700 | [diff] [blame] | 647 | if args.assets_dir: |
| 648 | cmd.extend(['-A', args.assets_dir]) |
Jiyong Park | bb35247 | 2018-08-25 23:07:07 +0900 | [diff] [blame] | 649 | cmd.extend(['-o', apk_file]) |
Alex Light | 9e6a2e6 | 2019-02-27 14:07:49 -0800 | [diff] [blame] | 650 | cmd.extend(['-I', args.android_jar_path]) |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 651 | RunCommand(cmd, args.verbose) |
| 652 | |
Jiyong Park | bb35247 | 2018-08-25 23:07:07 +0900 | [diff] [blame] | 653 | zip_file = os.path.join(work_dir, 'apex.zip') |
| 654 | cmd = ['soong_zip'] |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 655 | cmd.append('-d') # include directories |
| 656 | cmd.extend(['-C', content_dir]) # relative root |
| 657 | cmd.extend(['-D', content_dir]) # input dir |
Martijn Coenen | 09c106c | 2018-09-11 11:00:26 +0200 | [diff] [blame] | 658 | for file_ in os.listdir(content_dir): |
| 659 | if os.path.isfile(os.path.join(content_dir, file_)): |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 660 | cmd.extend(['-s', file_]) # don't compress any files |
Jiyong Park | bb35247 | 2018-08-25 23:07:07 +0900 | [diff] [blame] | 661 | cmd.extend(['-o', zip_file]) |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 662 | RunCommand(cmd, args.verbose) |
Jiyong Park | bb35247 | 2018-08-25 23:07:07 +0900 | [diff] [blame] | 663 | |
| 664 | unaligned_apex_file = os.path.join(work_dir, 'unaligned.apex') |
| 665 | cmd = ['merge_zips'] |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 666 | cmd.append('-j') # sort |
| 667 | cmd.append(unaligned_apex_file) # output |
| 668 | cmd.append(apk_file) # input |
| 669 | cmd.append(zip_file) # input |
Jiyong Park | bb35247 | 2018-08-25 23:07:07 +0900 | [diff] [blame] | 670 | RunCommand(cmd, args.verbose) |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 671 | |
Jiyong Park | 7c10f41 | 2018-08-21 21:28:41 +0900 | [diff] [blame] | 672 | # Align the files at page boundary for efficient access |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 673 | cmd = ['zipalign'] |
| 674 | cmd.append('-f') |
Jiyong Park | 4cd9b2b | 2019-01-19 13:42:35 +0900 | [diff] [blame] | 675 | cmd.append(str(BLOCK_SIZE)) |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 676 | cmd.append(unaligned_apex_file) |
| 677 | cmd.append(args.output) |
| 678 | RunCommand(cmd, args.verbose) |
| 679 | |
| 680 | if (args.verbose): |
| 681 | print('Created ' + args.output) |
| 682 | |
| 683 | return True |
| 684 | |
| 685 | |
| 686 | class TempDirectory(object): |
Jiyong Park | 34c821c | 2019-06-03 21:36:53 +0900 | [diff] [blame] | 687 | |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 688 | def __enter__(self): |
| 689 | self.name = tempfile.mkdtemp() |
| 690 | return self.name |
| 691 | |
Jiyong Park | 4edc623 | 2018-09-03 12:31:58 +0900 | [diff] [blame] | 692 | def __exit__(self, *unused): |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 693 | shutil.rmtree(self.name) |
| 694 | |
| 695 | |
| 696 | def main(argv): |
Alex Light | 18a1b19 | 2019-03-04 14:27:28 -0800 | [diff] [blame] | 697 | global tool_path_list |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 698 | args = ParseArgs(argv) |
Alex Light | 18a1b19 | 2019-03-04 14:27:28 -0800 | [diff] [blame] | 699 | tool_path_list = args.apexer_tool_path |
Jiyong Park | da6ab6e | 2018-08-14 23:44:51 +0900 | [diff] [blame] | 700 | with TempDirectory() as work_dir: |
| 701 | success = CreateApex(args, work_dir) |
| 702 | |
| 703 | if not success: |
| 704 | sys.exit(1) |
| 705 | |
| 706 | |
| 707 | if __name__ == '__main__': |
| 708 | main(sys.argv[1:]) |