xref: /aosp_15_r20/system/apex/apexer/apexer.py (revision 33f3758387333dbd2962d7edbd98681940d895da)
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