xref: /aosp_15_r20/build/make/tools/releasetools/common.py (revision 9e94795a3d4ef5c1d47486f9a02bb378756cea8a)
1*9e94795aSAndroid Build Coastguard Worker# Copyright (C) 2008 The Android Open Source Project
2*9e94795aSAndroid Build Coastguard Worker#
3*9e94795aSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
4*9e94795aSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
5*9e94795aSAndroid Build Coastguard Worker# You may obtain a copy of the License at
6*9e94795aSAndroid Build Coastguard Worker#
7*9e94795aSAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
8*9e94795aSAndroid Build Coastguard Worker#
9*9e94795aSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
10*9e94795aSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
11*9e94795aSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*9e94795aSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
13*9e94795aSAndroid Build Coastguard Worker# limitations under the License.
14*9e94795aSAndroid Build Coastguard Worker
15*9e94795aSAndroid Build Coastguard Workerfrom __future__ import print_function
16*9e94795aSAndroid Build Coastguard Worker
17*9e94795aSAndroid Build Coastguard Workerimport base64
18*9e94795aSAndroid Build Coastguard Workerimport collections
19*9e94795aSAndroid Build Coastguard Workerimport copy
20*9e94795aSAndroid Build Coastguard Workerimport datetime
21*9e94795aSAndroid Build Coastguard Workerimport errno
22*9e94795aSAndroid Build Coastguard Workerimport fnmatch
23*9e94795aSAndroid Build Coastguard Workerimport getopt
24*9e94795aSAndroid Build Coastguard Workerimport getpass
25*9e94795aSAndroid Build Coastguard Workerimport gzip
26*9e94795aSAndroid Build Coastguard Workerimport imp
27*9e94795aSAndroid Build Coastguard Workerimport json
28*9e94795aSAndroid Build Coastguard Workerimport logging
29*9e94795aSAndroid Build Coastguard Workerimport logging.config
30*9e94795aSAndroid Build Coastguard Workerimport os
31*9e94795aSAndroid Build Coastguard Workerimport platform
32*9e94795aSAndroid Build Coastguard Workerimport re
33*9e94795aSAndroid Build Coastguard Workerimport shlex
34*9e94795aSAndroid Build Coastguard Workerimport shutil
35*9e94795aSAndroid Build Coastguard Workerimport subprocess
36*9e94795aSAndroid Build Coastguard Workerimport stat
37*9e94795aSAndroid Build Coastguard Workerimport sys
38*9e94795aSAndroid Build Coastguard Workerimport tempfile
39*9e94795aSAndroid Build Coastguard Workerimport threading
40*9e94795aSAndroid Build Coastguard Workerimport time
41*9e94795aSAndroid Build Coastguard Workerimport zipfile
42*9e94795aSAndroid Build Coastguard Worker
43*9e94795aSAndroid Build Coastguard Workerfrom typing import Iterable, Callable
44*9e94795aSAndroid Build Coastguard Workerfrom dataclasses import dataclass
45*9e94795aSAndroid Build Coastguard Workerfrom hashlib import sha1, sha256
46*9e94795aSAndroid Build Coastguard Worker
47*9e94795aSAndroid Build Coastguard Workerimport images
48*9e94795aSAndroid Build Coastguard Workerimport sparse_img
49*9e94795aSAndroid Build Coastguard Workerfrom blockimgdiff import BlockImageDiff
50*9e94795aSAndroid Build Coastguard Worker
51*9e94795aSAndroid Build Coastguard Workerlogger = logging.getLogger(__name__)
52*9e94795aSAndroid Build Coastguard Worker
53*9e94795aSAndroid Build Coastguard Worker
54*9e94795aSAndroid Build Coastguard Worker@dataclass
55*9e94795aSAndroid Build Coastguard Workerclass OptionHandler:
56*9e94795aSAndroid Build Coastguard Worker  extra_long_opts: Iterable[str]
57*9e94795aSAndroid Build Coastguard Worker  handler: Callable
58*9e94795aSAndroid Build Coastguard Worker
59*9e94795aSAndroid Build Coastguard Workerclass Options(object):
60*9e94795aSAndroid Build Coastguard Worker
61*9e94795aSAndroid Build Coastguard Worker  def __init__(self):
62*9e94795aSAndroid Build Coastguard Worker    # Set up search path, in order to find framework/ and lib64/. At the time of
63*9e94795aSAndroid Build Coastguard Worker    # running this function, user-supplied search path (`--path`) hasn't been
64*9e94795aSAndroid Build Coastguard Worker    # available. So the value set here is the default, which might be overridden
65*9e94795aSAndroid Build Coastguard Worker    # by commandline flag later.
66*9e94795aSAndroid Build Coastguard Worker    exec_path = os.path.realpath(sys.argv[0])
67*9e94795aSAndroid Build Coastguard Worker    if exec_path.endswith('.py'):
68*9e94795aSAndroid Build Coastguard Worker      script_name = os.path.basename(exec_path)
69*9e94795aSAndroid Build Coastguard Worker      # logger hasn't been initialized yet at this point. Use print to output
70*9e94795aSAndroid Build Coastguard Worker      # warnings.
71*9e94795aSAndroid Build Coastguard Worker      print(
72*9e94795aSAndroid Build Coastguard Worker          'Warning: releasetools script should be invoked as hermetic Python '
73*9e94795aSAndroid Build Coastguard Worker          'executable -- build and run `{}` directly.'.format(
74*9e94795aSAndroid Build Coastguard Worker              script_name[:-3]),
75*9e94795aSAndroid Build Coastguard Worker          file=sys.stderr)
76*9e94795aSAndroid Build Coastguard Worker    self.search_path = os.path.dirname(os.path.dirname(exec_path))
77*9e94795aSAndroid Build Coastguard Worker
78*9e94795aSAndroid Build Coastguard Worker    self.signapk_path = "framework/signapk.jar"  # Relative to search_path
79*9e94795aSAndroid Build Coastguard Worker    if not os.path.exists(os.path.join(self.search_path, self.signapk_path)):
80*9e94795aSAndroid Build Coastguard Worker      if "ANDROID_HOST_OUT" in os.environ:
81*9e94795aSAndroid Build Coastguard Worker        self.search_path = os.environ["ANDROID_HOST_OUT"]
82*9e94795aSAndroid Build Coastguard Worker    self.signapk_shared_library_path = "lib64"   # Relative to search_path
83*9e94795aSAndroid Build Coastguard Worker    self.extra_signapk_args = []
84*9e94795aSAndroid Build Coastguard Worker    self.aapt2_path = "aapt2"
85*9e94795aSAndroid Build Coastguard Worker    self.java_path = "java"  # Use the one on the path by default.
86*9e94795aSAndroid Build Coastguard Worker    self.java_args = ["-Xmx4096m"]  # The default JVM args.
87*9e94795aSAndroid Build Coastguard Worker    self.android_jar_path = None
88*9e94795aSAndroid Build Coastguard Worker    self.public_key_suffix = ".x509.pem"
89*9e94795aSAndroid Build Coastguard Worker    self.private_key_suffix = ".pk8"
90*9e94795aSAndroid Build Coastguard Worker    # use otatools built boot_signer by default
91*9e94795aSAndroid Build Coastguard Worker    self.verbose = False
92*9e94795aSAndroid Build Coastguard Worker    self.tempfiles = []
93*9e94795aSAndroid Build Coastguard Worker    self.device_specific = None
94*9e94795aSAndroid Build Coastguard Worker    self.extras = {}
95*9e94795aSAndroid Build Coastguard Worker    self.info_dict = None
96*9e94795aSAndroid Build Coastguard Worker    self.source_info_dict = None
97*9e94795aSAndroid Build Coastguard Worker    self.target_info_dict = None
98*9e94795aSAndroid Build Coastguard Worker    self.worker_threads = None
99*9e94795aSAndroid Build Coastguard Worker    # Stash size cannot exceed cache_size * threshold.
100*9e94795aSAndroid Build Coastguard Worker    self.cache_size = None
101*9e94795aSAndroid Build Coastguard Worker    self.stash_threshold = 0.8
102*9e94795aSAndroid Build Coastguard Worker    self.logfile = None
103*9e94795aSAndroid Build Coastguard Worker
104*9e94795aSAndroid Build Coastguard Worker
105*9e94795aSAndroid Build Coastguard WorkerOPTIONS = Options()
106*9e94795aSAndroid Build Coastguard Worker
107*9e94795aSAndroid Build Coastguard Worker# The block size that's used across the releasetools scripts.
108*9e94795aSAndroid Build Coastguard WorkerBLOCK_SIZE = 4096
109*9e94795aSAndroid Build Coastguard Worker
110*9e94795aSAndroid Build Coastguard Worker# Values for "certificate" in apkcerts that mean special things.
111*9e94795aSAndroid Build Coastguard WorkerSPECIAL_CERT_STRINGS = ("PRESIGNED", "EXTERNAL")
112*9e94795aSAndroid Build Coastguard Worker
113*9e94795aSAndroid Build Coastguard Worker# The partitions allowed to be signed by AVB (Android Verified Boot 2.0). Note
114*9e94795aSAndroid Build Coastguard Worker# that system_other is not in the list because we don't want to include its
115*9e94795aSAndroid Build Coastguard Worker# descriptor into vbmeta.img. When adding a new entry here, the
116*9e94795aSAndroid Build Coastguard Worker# AVB_FOOTER_ARGS_BY_PARTITION in sign_target_files_apks need to be updated
117*9e94795aSAndroid Build Coastguard Worker# accordingly.
118*9e94795aSAndroid Build Coastguard WorkerAVB_PARTITIONS = ('boot', 'init_boot', 'dtbo', 'odm', 'product', 'pvmfw',
119*9e94795aSAndroid Build Coastguard Worker                  'recovery', 'system', 'system_ext', 'vendor', 'vendor_boot',
120*9e94795aSAndroid Build Coastguard Worker                  'vendor_kernel_boot', 'vendor_dlkm', 'odm_dlkm',
121*9e94795aSAndroid Build Coastguard Worker                  'system_dlkm')
122*9e94795aSAndroid Build Coastguard Worker
123*9e94795aSAndroid Build Coastguard Worker# Chained VBMeta partitions.
124*9e94795aSAndroid Build Coastguard WorkerAVB_VBMETA_PARTITIONS = ('vbmeta_system', 'vbmeta_vendor')
125*9e94795aSAndroid Build Coastguard Worker
126*9e94795aSAndroid Build Coastguard Worker# avbtool arguments name
127*9e94795aSAndroid Build Coastguard WorkerAVB_ARG_NAME_INCLUDE_DESC_FROM_IMG = '--include_descriptors_from_image'
128*9e94795aSAndroid Build Coastguard WorkerAVB_ARG_NAME_CHAIN_PARTITION = '--chain_partition'
129*9e94795aSAndroid Build Coastguard Worker
130*9e94795aSAndroid Build Coastguard Worker# Partitions that should have their care_map added to META/care_map.pb
131*9e94795aSAndroid Build Coastguard WorkerPARTITIONS_WITH_CARE_MAP = [
132*9e94795aSAndroid Build Coastguard Worker    'system',
133*9e94795aSAndroid Build Coastguard Worker    'vendor',
134*9e94795aSAndroid Build Coastguard Worker    'product',
135*9e94795aSAndroid Build Coastguard Worker    'system_ext',
136*9e94795aSAndroid Build Coastguard Worker    'odm',
137*9e94795aSAndroid Build Coastguard Worker    'vendor_dlkm',
138*9e94795aSAndroid Build Coastguard Worker    'odm_dlkm',
139*9e94795aSAndroid Build Coastguard Worker    'system_dlkm',
140*9e94795aSAndroid Build Coastguard Worker]
141*9e94795aSAndroid Build Coastguard Worker
142*9e94795aSAndroid Build Coastguard Worker# Partitions with a build.prop file
143*9e94795aSAndroid Build Coastguard WorkerPARTITIONS_WITH_BUILD_PROP = PARTITIONS_WITH_CARE_MAP + ['boot', 'init_boot']
144*9e94795aSAndroid Build Coastguard Worker
145*9e94795aSAndroid Build Coastguard Worker# See sysprop.mk. If file is moved, add new search paths here; don't remove
146*9e94795aSAndroid Build Coastguard Worker# existing search paths.
147*9e94795aSAndroid Build Coastguard WorkerRAMDISK_BUILD_PROP_REL_PATHS = ['system/etc/ramdisk/build.prop']
148*9e94795aSAndroid Build Coastguard Worker
149*9e94795aSAndroid Build Coastguard Worker
150*9e94795aSAndroid Build Coastguard Worker@dataclass
151*9e94795aSAndroid Build Coastguard Workerclass AvbChainedPartitionArg:
152*9e94795aSAndroid Build Coastguard Worker  """The required arguments for avbtool --chain_partition."""
153*9e94795aSAndroid Build Coastguard Worker  partition: str
154*9e94795aSAndroid Build Coastguard Worker  rollback_index_location: int
155*9e94795aSAndroid Build Coastguard Worker  pubkey_path: str
156*9e94795aSAndroid Build Coastguard Worker
157*9e94795aSAndroid Build Coastguard Worker  def to_string(self):
158*9e94795aSAndroid Build Coastguard Worker    """Convert to string command arguments."""
159*9e94795aSAndroid Build Coastguard Worker    return '{}:{}:{}'.format(
160*9e94795aSAndroid Build Coastguard Worker        self.partition, self.rollback_index_location, self.pubkey_path)
161*9e94795aSAndroid Build Coastguard Worker
162*9e94795aSAndroid Build Coastguard Worker
163*9e94795aSAndroid Build Coastguard Workerclass ErrorCode(object):
164*9e94795aSAndroid Build Coastguard Worker  """Define error_codes for failures that happen during the actual
165*9e94795aSAndroid Build Coastguard Worker  update package installation.
166*9e94795aSAndroid Build Coastguard Worker
167*9e94795aSAndroid Build Coastguard Worker  Error codes 0-999 are reserved for failures before the package
168*9e94795aSAndroid Build Coastguard Worker  installation (i.e. low battery, package verification failure).
169*9e94795aSAndroid Build Coastguard Worker  Detailed code in 'bootable/recovery/error_code.h' """
170*9e94795aSAndroid Build Coastguard Worker
171*9e94795aSAndroid Build Coastguard Worker  SYSTEM_VERIFICATION_FAILURE = 1000
172*9e94795aSAndroid Build Coastguard Worker  SYSTEM_UPDATE_FAILURE = 1001
173*9e94795aSAndroid Build Coastguard Worker  SYSTEM_UNEXPECTED_CONTENTS = 1002
174*9e94795aSAndroid Build Coastguard Worker  SYSTEM_NONZERO_CONTENTS = 1003
175*9e94795aSAndroid Build Coastguard Worker  SYSTEM_RECOVER_FAILURE = 1004
176*9e94795aSAndroid Build Coastguard Worker  VENDOR_VERIFICATION_FAILURE = 2000
177*9e94795aSAndroid Build Coastguard Worker  VENDOR_UPDATE_FAILURE = 2001
178*9e94795aSAndroid Build Coastguard Worker  VENDOR_UNEXPECTED_CONTENTS = 2002
179*9e94795aSAndroid Build Coastguard Worker  VENDOR_NONZERO_CONTENTS = 2003
180*9e94795aSAndroid Build Coastguard Worker  VENDOR_RECOVER_FAILURE = 2004
181*9e94795aSAndroid Build Coastguard Worker  OEM_PROP_MISMATCH = 3000
182*9e94795aSAndroid Build Coastguard Worker  FINGERPRINT_MISMATCH = 3001
183*9e94795aSAndroid Build Coastguard Worker  THUMBPRINT_MISMATCH = 3002
184*9e94795aSAndroid Build Coastguard Worker  OLDER_BUILD = 3003
185*9e94795aSAndroid Build Coastguard Worker  DEVICE_MISMATCH = 3004
186*9e94795aSAndroid Build Coastguard Worker  BAD_PATCH_FILE = 3005
187*9e94795aSAndroid Build Coastguard Worker  INSUFFICIENT_CACHE_SPACE = 3006
188*9e94795aSAndroid Build Coastguard Worker  TUNE_PARTITION_FAILURE = 3007
189*9e94795aSAndroid Build Coastguard Worker  APPLY_PATCH_FAILURE = 3008
190*9e94795aSAndroid Build Coastguard Worker
191*9e94795aSAndroid Build Coastguard Worker
192*9e94795aSAndroid Build Coastguard Workerclass ExternalError(RuntimeError):
193*9e94795aSAndroid Build Coastguard Worker  pass
194*9e94795aSAndroid Build Coastguard Worker
195*9e94795aSAndroid Build Coastguard Worker
196*9e94795aSAndroid Build Coastguard Workerdef InitLogging():
197*9e94795aSAndroid Build Coastguard Worker  DEFAULT_LOGGING_CONFIG = {
198*9e94795aSAndroid Build Coastguard Worker      'version': 1,
199*9e94795aSAndroid Build Coastguard Worker      'disable_existing_loggers': False,
200*9e94795aSAndroid Build Coastguard Worker      'formatters': {
201*9e94795aSAndroid Build Coastguard Worker          'standard': {
202*9e94795aSAndroid Build Coastguard Worker              'format':
203*9e94795aSAndroid Build Coastguard Worker                  '%(asctime)s - %(filename)s - %(levelname)-8s: %(message)s',
204*9e94795aSAndroid Build Coastguard Worker              'datefmt': '%Y-%m-%d %H:%M:%S',
205*9e94795aSAndroid Build Coastguard Worker          },
206*9e94795aSAndroid Build Coastguard Worker      },
207*9e94795aSAndroid Build Coastguard Worker      'handlers': {
208*9e94795aSAndroid Build Coastguard Worker          'default': {
209*9e94795aSAndroid Build Coastguard Worker              'class': 'logging.StreamHandler',
210*9e94795aSAndroid Build Coastguard Worker              'formatter': 'standard',
211*9e94795aSAndroid Build Coastguard Worker              'level': 'WARNING',
212*9e94795aSAndroid Build Coastguard Worker          },
213*9e94795aSAndroid Build Coastguard Worker      },
214*9e94795aSAndroid Build Coastguard Worker      'loggers': {
215*9e94795aSAndroid Build Coastguard Worker          '': {
216*9e94795aSAndroid Build Coastguard Worker              'handlers': ['default'],
217*9e94795aSAndroid Build Coastguard Worker              'propagate': True,
218*9e94795aSAndroid Build Coastguard Worker              'level': 'NOTSET',
219*9e94795aSAndroid Build Coastguard Worker          }
220*9e94795aSAndroid Build Coastguard Worker      }
221*9e94795aSAndroid Build Coastguard Worker  }
222*9e94795aSAndroid Build Coastguard Worker  env_config = os.getenv('LOGGING_CONFIG')
223*9e94795aSAndroid Build Coastguard Worker  if env_config:
224*9e94795aSAndroid Build Coastguard Worker    with open(env_config) as f:
225*9e94795aSAndroid Build Coastguard Worker      config = json.load(f)
226*9e94795aSAndroid Build Coastguard Worker  else:
227*9e94795aSAndroid Build Coastguard Worker    config = DEFAULT_LOGGING_CONFIG
228*9e94795aSAndroid Build Coastguard Worker
229*9e94795aSAndroid Build Coastguard Worker    # Increase the logging level for verbose mode.
230*9e94795aSAndroid Build Coastguard Worker    if OPTIONS.verbose:
231*9e94795aSAndroid Build Coastguard Worker      config = copy.deepcopy(config)
232*9e94795aSAndroid Build Coastguard Worker      config['handlers']['default']['level'] = 'INFO'
233*9e94795aSAndroid Build Coastguard Worker
234*9e94795aSAndroid Build Coastguard Worker    if OPTIONS.logfile:
235*9e94795aSAndroid Build Coastguard Worker      config = copy.deepcopy(config)
236*9e94795aSAndroid Build Coastguard Worker      config['handlers']['logfile'] = {
237*9e94795aSAndroid Build Coastguard Worker          'class': 'logging.FileHandler',
238*9e94795aSAndroid Build Coastguard Worker          'formatter': 'standard',
239*9e94795aSAndroid Build Coastguard Worker          'level': 'INFO',
240*9e94795aSAndroid Build Coastguard Worker          'mode': 'w',
241*9e94795aSAndroid Build Coastguard Worker          'filename': OPTIONS.logfile,
242*9e94795aSAndroid Build Coastguard Worker      }
243*9e94795aSAndroid Build Coastguard Worker      config['loggers']['']['handlers'].append('logfile')
244*9e94795aSAndroid Build Coastguard Worker
245*9e94795aSAndroid Build Coastguard Worker  logging.config.dictConfig(config)
246*9e94795aSAndroid Build Coastguard Worker
247*9e94795aSAndroid Build Coastguard Worker
248*9e94795aSAndroid Build Coastguard Workerdef FindHostToolPath(tool_name):
249*9e94795aSAndroid Build Coastguard Worker  """Finds the path to the host tool.
250*9e94795aSAndroid Build Coastguard Worker
251*9e94795aSAndroid Build Coastguard Worker  Args:
252*9e94795aSAndroid Build Coastguard Worker    tool_name: name of the tool to find
253*9e94795aSAndroid Build Coastguard Worker  Returns:
254*9e94795aSAndroid Build Coastguard Worker    path to the tool if found under the same directory as this binary is located at. If not found,
255*9e94795aSAndroid Build Coastguard Worker    tool_name is returned.
256*9e94795aSAndroid Build Coastguard Worker  """
257*9e94795aSAndroid Build Coastguard Worker  my_dir = os.path.dirname(os.path.realpath(sys.argv[0]))
258*9e94795aSAndroid Build Coastguard Worker  tool_path = os.path.join(my_dir, tool_name)
259*9e94795aSAndroid Build Coastguard Worker  if os.path.exists(tool_path):
260*9e94795aSAndroid Build Coastguard Worker    return tool_path
261*9e94795aSAndroid Build Coastguard Worker
262*9e94795aSAndroid Build Coastguard Worker  return tool_name
263*9e94795aSAndroid Build Coastguard Worker
264*9e94795aSAndroid Build Coastguard Worker
265*9e94795aSAndroid Build Coastguard Workerdef Run(args, verbose=None, **kwargs):
266*9e94795aSAndroid Build Coastguard Worker  """Creates and returns a subprocess.Popen object.
267*9e94795aSAndroid Build Coastguard Worker
268*9e94795aSAndroid Build Coastguard Worker  Args:
269*9e94795aSAndroid Build Coastguard Worker    args: The command represented as a list of strings.
270*9e94795aSAndroid Build Coastguard Worker    verbose: Whether the commands should be shown. Default to the global
271*9e94795aSAndroid Build Coastguard Worker        verbosity if unspecified.
272*9e94795aSAndroid Build Coastguard Worker    kwargs: Any additional args to be passed to subprocess.Popen(), such as env,
273*9e94795aSAndroid Build Coastguard Worker        stdin, etc. stdout and stderr will default to subprocess.PIPE and
274*9e94795aSAndroid Build Coastguard Worker        subprocess.STDOUT respectively unless caller specifies any of them.
275*9e94795aSAndroid Build Coastguard Worker        universal_newlines will default to True, as most of the users in
276*9e94795aSAndroid Build Coastguard Worker        releasetools expect string output.
277*9e94795aSAndroid Build Coastguard Worker
278*9e94795aSAndroid Build Coastguard Worker  Returns:
279*9e94795aSAndroid Build Coastguard Worker    A subprocess.Popen object.
280*9e94795aSAndroid Build Coastguard Worker  """
281*9e94795aSAndroid Build Coastguard Worker  if 'stdout' not in kwargs and 'stderr' not in kwargs:
282*9e94795aSAndroid Build Coastguard Worker    kwargs['stdout'] = subprocess.PIPE
283*9e94795aSAndroid Build Coastguard Worker    kwargs['stderr'] = subprocess.STDOUT
284*9e94795aSAndroid Build Coastguard Worker  if 'universal_newlines' not in kwargs:
285*9e94795aSAndroid Build Coastguard Worker    kwargs['universal_newlines'] = True
286*9e94795aSAndroid Build Coastguard Worker
287*9e94795aSAndroid Build Coastguard Worker  if args:
288*9e94795aSAndroid Build Coastguard Worker    # Make a copy of args in case client relies on the content of args later.
289*9e94795aSAndroid Build Coastguard Worker    args = args[:]
290*9e94795aSAndroid Build Coastguard Worker    args[0] = FindHostToolPath(args[0])
291*9e94795aSAndroid Build Coastguard Worker
292*9e94795aSAndroid Build Coastguard Worker  if verbose is None:
293*9e94795aSAndroid Build Coastguard Worker    verbose = OPTIONS.verbose
294*9e94795aSAndroid Build Coastguard Worker
295*9e94795aSAndroid Build Coastguard Worker  # Don't log any if caller explicitly says so.
296*9e94795aSAndroid Build Coastguard Worker  if verbose:
297*9e94795aSAndroid Build Coastguard Worker    logger.info("  Running: \"%s\"", " ".join(args))
298*9e94795aSAndroid Build Coastguard Worker  return subprocess.Popen(args, **kwargs)
299*9e94795aSAndroid Build Coastguard Worker
300*9e94795aSAndroid Build Coastguard Worker
301*9e94795aSAndroid Build Coastguard Workerdef RunAndCheckOutput(args, verbose=None, **kwargs):
302*9e94795aSAndroid Build Coastguard Worker  """Runs the given command and returns the output.
303*9e94795aSAndroid Build Coastguard Worker
304*9e94795aSAndroid Build Coastguard Worker  Args:
305*9e94795aSAndroid Build Coastguard Worker    args: The command represented as a list of strings.
306*9e94795aSAndroid Build Coastguard Worker    verbose: Whether the commands should be shown. Default to the global
307*9e94795aSAndroid Build Coastguard Worker        verbosity if unspecified.
308*9e94795aSAndroid Build Coastguard Worker    kwargs: Any additional args to be passed to subprocess.Popen(), such as env,
309*9e94795aSAndroid Build Coastguard Worker        stdin, etc. stdout and stderr will default to subprocess.PIPE and
310*9e94795aSAndroid Build Coastguard Worker        subprocess.STDOUT respectively unless caller specifies any of them.
311*9e94795aSAndroid Build Coastguard Worker
312*9e94795aSAndroid Build Coastguard Worker  Returns:
313*9e94795aSAndroid Build Coastguard Worker    The output string.
314*9e94795aSAndroid Build Coastguard Worker
315*9e94795aSAndroid Build Coastguard Worker  Raises:
316*9e94795aSAndroid Build Coastguard Worker    ExternalError: On non-zero exit from the command.
317*9e94795aSAndroid Build Coastguard Worker  """
318*9e94795aSAndroid Build Coastguard Worker  if verbose is None:
319*9e94795aSAndroid Build Coastguard Worker    verbose = OPTIONS.verbose
320*9e94795aSAndroid Build Coastguard Worker  proc = Run(args, verbose=verbose, **kwargs)
321*9e94795aSAndroid Build Coastguard Worker  output, _ = proc.communicate()
322*9e94795aSAndroid Build Coastguard Worker  if output is None:
323*9e94795aSAndroid Build Coastguard Worker    output = ""
324*9e94795aSAndroid Build Coastguard Worker  # Don't log any if caller explicitly says so.
325*9e94795aSAndroid Build Coastguard Worker  if verbose:
326*9e94795aSAndroid Build Coastguard Worker    logger.info("%s", output.rstrip())
327*9e94795aSAndroid Build Coastguard Worker  if proc.returncode != 0:
328*9e94795aSAndroid Build Coastguard Worker    raise ExternalError(
329*9e94795aSAndroid Build Coastguard Worker        "Failed to run command '{}' (exit code {}):\n{}".format(
330*9e94795aSAndroid Build Coastguard Worker            args, proc.returncode, output))
331*9e94795aSAndroid Build Coastguard Worker  return output
332*9e94795aSAndroid Build Coastguard Worker
333*9e94795aSAndroid Build Coastguard Worker
334*9e94795aSAndroid Build Coastguard Workerdef RoundUpTo4K(value):
335*9e94795aSAndroid Build Coastguard Worker  rounded_up = value + 4095
336*9e94795aSAndroid Build Coastguard Worker  return rounded_up - (rounded_up % 4096)
337*9e94795aSAndroid Build Coastguard Worker
338*9e94795aSAndroid Build Coastguard Worker
339*9e94795aSAndroid Build Coastguard Workerdef CloseInheritedPipes():
340*9e94795aSAndroid Build Coastguard Worker  """ Gmake in MAC OS has file descriptor (PIPE) leak. We close those fds
341*9e94795aSAndroid Build Coastguard Worker  before doing other work."""
342*9e94795aSAndroid Build Coastguard Worker  if platform.system() != "Darwin":
343*9e94795aSAndroid Build Coastguard Worker    return
344*9e94795aSAndroid Build Coastguard Worker  for d in range(3, 1025):
345*9e94795aSAndroid Build Coastguard Worker    try:
346*9e94795aSAndroid Build Coastguard Worker      stat = os.fstat(d)
347*9e94795aSAndroid Build Coastguard Worker      if stat is not None:
348*9e94795aSAndroid Build Coastguard Worker        pipebit = stat[0] & 0x1000
349*9e94795aSAndroid Build Coastguard Worker        if pipebit != 0:
350*9e94795aSAndroid Build Coastguard Worker          os.close(d)
351*9e94795aSAndroid Build Coastguard Worker    except OSError:
352*9e94795aSAndroid Build Coastguard Worker      pass
353*9e94795aSAndroid Build Coastguard Worker
354*9e94795aSAndroid Build Coastguard Worker
355*9e94795aSAndroid Build Coastguard Workerclass BuildInfo(object):
356*9e94795aSAndroid Build Coastguard Worker  """A class that holds the information for a given build.
357*9e94795aSAndroid Build Coastguard Worker
358*9e94795aSAndroid Build Coastguard Worker  This class wraps up the property querying for a given source or target build.
359*9e94795aSAndroid Build Coastguard Worker  It abstracts away the logic of handling OEM-specific properties, and caches
360*9e94795aSAndroid Build Coastguard Worker  the commonly used properties such as fingerprint.
361*9e94795aSAndroid Build Coastguard Worker
362*9e94795aSAndroid Build Coastguard Worker  There are two types of info dicts: a) build-time info dict, which is generated
363*9e94795aSAndroid Build Coastguard Worker  at build time (i.e. included in a target_files zip); b) OEM info dict that is
364*9e94795aSAndroid Build Coastguard Worker  specified at package generation time (via command line argument
365*9e94795aSAndroid Build Coastguard Worker  '--oem_settings'). If a build doesn't use OEM-specific properties (i.e. not
366*9e94795aSAndroid Build Coastguard Worker  having "oem_fingerprint_properties" in build-time info dict), all the queries
367*9e94795aSAndroid Build Coastguard Worker  would be answered based on build-time info dict only. Otherwise if using
368*9e94795aSAndroid Build Coastguard Worker  OEM-specific properties, some of them will be calculated from two info dicts.
369*9e94795aSAndroid Build Coastguard Worker
370*9e94795aSAndroid Build Coastguard Worker  Users can query properties similarly as using a dict() (e.g. info['fstab']),
371*9e94795aSAndroid Build Coastguard Worker  or to query build properties via GetBuildProp() or GetPartitionBuildProp().
372*9e94795aSAndroid Build Coastguard Worker
373*9e94795aSAndroid Build Coastguard Worker  Attributes:
374*9e94795aSAndroid Build Coastguard Worker    info_dict: The build-time info dict.
375*9e94795aSAndroid Build Coastguard Worker    is_ab: Whether it's a build that uses A/B OTA.
376*9e94795aSAndroid Build Coastguard Worker    oem_dicts: A list of OEM dicts.
377*9e94795aSAndroid Build Coastguard Worker    oem_props: A list of OEM properties that should be read from OEM dicts; None
378*9e94795aSAndroid Build Coastguard Worker        if the build doesn't use any OEM-specific property.
379*9e94795aSAndroid Build Coastguard Worker    fingerprint: The fingerprint of the build, which would be calculated based
380*9e94795aSAndroid Build Coastguard Worker        on OEM properties if applicable.
381*9e94795aSAndroid Build Coastguard Worker    device: The device name, which could come from OEM dicts if applicable.
382*9e94795aSAndroid Build Coastguard Worker  """
383*9e94795aSAndroid Build Coastguard Worker
384*9e94795aSAndroid Build Coastguard Worker  _RO_PRODUCT_RESOLVE_PROPS = ["ro.product.brand", "ro.product.device",
385*9e94795aSAndroid Build Coastguard Worker                               "ro.product.manufacturer", "ro.product.model",
386*9e94795aSAndroid Build Coastguard Worker                               "ro.product.name"]
387*9e94795aSAndroid Build Coastguard Worker  _RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER_CURRENT = [
388*9e94795aSAndroid Build Coastguard Worker      "product", "odm", "vendor", "system_ext", "system"]
389*9e94795aSAndroid Build Coastguard Worker  _RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER_ANDROID_10 = [
390*9e94795aSAndroid Build Coastguard Worker      "product", "product_services", "odm", "vendor", "system"]
391*9e94795aSAndroid Build Coastguard Worker  _RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER_LEGACY = []
392*9e94795aSAndroid Build Coastguard Worker
393*9e94795aSAndroid Build Coastguard Worker  # The length of vbmeta digest to append to the fingerprint
394*9e94795aSAndroid Build Coastguard Worker  _VBMETA_DIGEST_SIZE_USED = 8
395*9e94795aSAndroid Build Coastguard Worker
396*9e94795aSAndroid Build Coastguard Worker  def __init__(self, info_dict, oem_dicts=None, use_legacy_id=False):
397*9e94795aSAndroid Build Coastguard Worker    """Initializes a BuildInfo instance with the given dicts.
398*9e94795aSAndroid Build Coastguard Worker
399*9e94795aSAndroid Build Coastguard Worker    Note that it only wraps up the given dicts, without making copies.
400*9e94795aSAndroid Build Coastguard Worker
401*9e94795aSAndroid Build Coastguard Worker    Arguments:
402*9e94795aSAndroid Build Coastguard Worker      info_dict: The build-time info dict.
403*9e94795aSAndroid Build Coastguard Worker      oem_dicts: A list of OEM dicts (which is parsed from --oem_settings). Note
404*9e94795aSAndroid Build Coastguard Worker          that it always uses the first dict to calculate the fingerprint or the
405*9e94795aSAndroid Build Coastguard Worker          device name. The rest would be used for asserting OEM properties only
406*9e94795aSAndroid Build Coastguard Worker          (e.g. one package can be installed on one of these devices).
407*9e94795aSAndroid Build Coastguard Worker      use_legacy_id: Use the legacy build id to construct the fingerprint. This
408*9e94795aSAndroid Build Coastguard Worker          is used when we need a BuildInfo class, while the vbmeta digest is
409*9e94795aSAndroid Build Coastguard Worker          unavailable.
410*9e94795aSAndroid Build Coastguard Worker
411*9e94795aSAndroid Build Coastguard Worker    Raises:
412*9e94795aSAndroid Build Coastguard Worker      ValueError: On invalid inputs.
413*9e94795aSAndroid Build Coastguard Worker    """
414*9e94795aSAndroid Build Coastguard Worker    self.info_dict = info_dict
415*9e94795aSAndroid Build Coastguard Worker    self.oem_dicts = oem_dicts
416*9e94795aSAndroid Build Coastguard Worker
417*9e94795aSAndroid Build Coastguard Worker    self._is_ab = info_dict.get("ab_update") == "true"
418*9e94795aSAndroid Build Coastguard Worker    self.use_legacy_id = use_legacy_id
419*9e94795aSAndroid Build Coastguard Worker
420*9e94795aSAndroid Build Coastguard Worker    # Skip _oem_props if oem_dicts is None to use BuildInfo in
421*9e94795aSAndroid Build Coastguard Worker    # sign_target_files_apks
422*9e94795aSAndroid Build Coastguard Worker    if self.oem_dicts:
423*9e94795aSAndroid Build Coastguard Worker      self._oem_props = info_dict.get("oem_fingerprint_properties")
424*9e94795aSAndroid Build Coastguard Worker    else:
425*9e94795aSAndroid Build Coastguard Worker      self._oem_props = None
426*9e94795aSAndroid Build Coastguard Worker
427*9e94795aSAndroid Build Coastguard Worker    def check_fingerprint(fingerprint):
428*9e94795aSAndroid Build Coastguard Worker      if (" " in fingerprint or any(ord(ch) > 127 for ch in fingerprint)):
429*9e94795aSAndroid Build Coastguard Worker        raise ValueError(
430*9e94795aSAndroid Build Coastguard Worker            'Invalid build fingerprint: "{}". See the requirement in Android CDD '
431*9e94795aSAndroid Build Coastguard Worker            "3.2.2. Build Parameters.".format(fingerprint))
432*9e94795aSAndroid Build Coastguard Worker
433*9e94795aSAndroid Build Coastguard Worker    self._partition_fingerprints = {}
434*9e94795aSAndroid Build Coastguard Worker    for partition in PARTITIONS_WITH_BUILD_PROP:
435*9e94795aSAndroid Build Coastguard Worker      try:
436*9e94795aSAndroid Build Coastguard Worker        fingerprint = self.CalculatePartitionFingerprint(partition)
437*9e94795aSAndroid Build Coastguard Worker        check_fingerprint(fingerprint)
438*9e94795aSAndroid Build Coastguard Worker        self._partition_fingerprints[partition] = fingerprint
439*9e94795aSAndroid Build Coastguard Worker      except ExternalError:
440*9e94795aSAndroid Build Coastguard Worker        continue
441*9e94795aSAndroid Build Coastguard Worker    if "system" in self._partition_fingerprints:
442*9e94795aSAndroid Build Coastguard Worker      # system_other is not included in PARTITIONS_WITH_BUILD_PROP, but does
443*9e94795aSAndroid Build Coastguard Worker      # need a fingerprint when creating the image.
444*9e94795aSAndroid Build Coastguard Worker      self._partition_fingerprints[
445*9e94795aSAndroid Build Coastguard Worker          "system_other"] = self._partition_fingerprints["system"]
446*9e94795aSAndroid Build Coastguard Worker
447*9e94795aSAndroid Build Coastguard Worker    # These two should be computed only after setting self._oem_props.
448*9e94795aSAndroid Build Coastguard Worker    self._device = self.GetOemProperty("ro.product.device")
449*9e94795aSAndroid Build Coastguard Worker    self._fingerprint = self.CalculateFingerprint()
450*9e94795aSAndroid Build Coastguard Worker    check_fingerprint(self._fingerprint)
451*9e94795aSAndroid Build Coastguard Worker
452*9e94795aSAndroid Build Coastguard Worker  @property
453*9e94795aSAndroid Build Coastguard Worker  def is_ab(self):
454*9e94795aSAndroid Build Coastguard Worker    return self._is_ab
455*9e94795aSAndroid Build Coastguard Worker
456*9e94795aSAndroid Build Coastguard Worker  @property
457*9e94795aSAndroid Build Coastguard Worker  def device(self):
458*9e94795aSAndroid Build Coastguard Worker    return self._device
459*9e94795aSAndroid Build Coastguard Worker
460*9e94795aSAndroid Build Coastguard Worker  @property
461*9e94795aSAndroid Build Coastguard Worker  def fingerprint(self):
462*9e94795aSAndroid Build Coastguard Worker    return self._fingerprint
463*9e94795aSAndroid Build Coastguard Worker
464*9e94795aSAndroid Build Coastguard Worker  @property
465*9e94795aSAndroid Build Coastguard Worker  def is_vabc(self):
466*9e94795aSAndroid Build Coastguard Worker    return self.info_dict.get("virtual_ab_compression") == "true"
467*9e94795aSAndroid Build Coastguard Worker
468*9e94795aSAndroid Build Coastguard Worker  @property
469*9e94795aSAndroid Build Coastguard Worker  def is_android_r(self):
470*9e94795aSAndroid Build Coastguard Worker    system_prop = self.info_dict.get("system.build.prop")
471*9e94795aSAndroid Build Coastguard Worker    return system_prop and system_prop.GetProp("ro.build.version.release") == "11"
472*9e94795aSAndroid Build Coastguard Worker
473*9e94795aSAndroid Build Coastguard Worker  @property
474*9e94795aSAndroid Build Coastguard Worker  def is_release_key(self):
475*9e94795aSAndroid Build Coastguard Worker    system_prop = self.info_dict.get("build.prop")
476*9e94795aSAndroid Build Coastguard Worker    return system_prop and system_prop.GetProp("ro.build.tags") == "release-key"
477*9e94795aSAndroid Build Coastguard Worker
478*9e94795aSAndroid Build Coastguard Worker  @property
479*9e94795aSAndroid Build Coastguard Worker  def vabc_compression_param(self):
480*9e94795aSAndroid Build Coastguard Worker    return self.get("virtual_ab_compression_method", "")
481*9e94795aSAndroid Build Coastguard Worker
482*9e94795aSAndroid Build Coastguard Worker  @property
483*9e94795aSAndroid Build Coastguard Worker  def vabc_cow_version(self):
484*9e94795aSAndroid Build Coastguard Worker    return self.get("virtual_ab_cow_version", "")
485*9e94795aSAndroid Build Coastguard Worker
486*9e94795aSAndroid Build Coastguard Worker  @property
487*9e94795aSAndroid Build Coastguard Worker  def vendor_api_level(self):
488*9e94795aSAndroid Build Coastguard Worker    vendor_prop = self.info_dict.get("vendor.build.prop")
489*9e94795aSAndroid Build Coastguard Worker    if not vendor_prop:
490*9e94795aSAndroid Build Coastguard Worker      return -1
491*9e94795aSAndroid Build Coastguard Worker
492*9e94795aSAndroid Build Coastguard Worker    props = [
493*9e94795aSAndroid Build Coastguard Worker        "ro.board.first_api_level",
494*9e94795aSAndroid Build Coastguard Worker        "ro.product.first_api_level",
495*9e94795aSAndroid Build Coastguard Worker    ]
496*9e94795aSAndroid Build Coastguard Worker    for prop in props:
497*9e94795aSAndroid Build Coastguard Worker      value = vendor_prop.GetProp(prop)
498*9e94795aSAndroid Build Coastguard Worker      try:
499*9e94795aSAndroid Build Coastguard Worker        return int(value)
500*9e94795aSAndroid Build Coastguard Worker      except:
501*9e94795aSAndroid Build Coastguard Worker        pass
502*9e94795aSAndroid Build Coastguard Worker    return -1
503*9e94795aSAndroid Build Coastguard Worker
504*9e94795aSAndroid Build Coastguard Worker  @property
505*9e94795aSAndroid Build Coastguard Worker  def is_vabc_xor(self):
506*9e94795aSAndroid Build Coastguard Worker    vendor_prop = self.info_dict.get("vendor.build.prop")
507*9e94795aSAndroid Build Coastguard Worker    vabc_xor_enabled = vendor_prop and \
508*9e94795aSAndroid Build Coastguard Worker        vendor_prop.GetProp("ro.virtual_ab.compression.xor.enabled") == "true"
509*9e94795aSAndroid Build Coastguard Worker    return vabc_xor_enabled
510*9e94795aSAndroid Build Coastguard Worker
511*9e94795aSAndroid Build Coastguard Worker  @property
512*9e94795aSAndroid Build Coastguard Worker  def vendor_suppressed_vabc(self):
513*9e94795aSAndroid Build Coastguard Worker    vendor_prop = self.info_dict.get("vendor.build.prop")
514*9e94795aSAndroid Build Coastguard Worker    vabc_suppressed = vendor_prop and \
515*9e94795aSAndroid Build Coastguard Worker        vendor_prop.GetProp("ro.vendor.build.dont_use_vabc")
516*9e94795aSAndroid Build Coastguard Worker    return vabc_suppressed and vabc_suppressed.lower() == "true"
517*9e94795aSAndroid Build Coastguard Worker
518*9e94795aSAndroid Build Coastguard Worker  @property
519*9e94795aSAndroid Build Coastguard Worker  def oem_props(self):
520*9e94795aSAndroid Build Coastguard Worker    return self._oem_props
521*9e94795aSAndroid Build Coastguard Worker
522*9e94795aSAndroid Build Coastguard Worker  def __getitem__(self, key):
523*9e94795aSAndroid Build Coastguard Worker    return self.info_dict[key]
524*9e94795aSAndroid Build Coastguard Worker
525*9e94795aSAndroid Build Coastguard Worker  def __setitem__(self, key, value):
526*9e94795aSAndroid Build Coastguard Worker    self.info_dict[key] = value
527*9e94795aSAndroid Build Coastguard Worker
528*9e94795aSAndroid Build Coastguard Worker  def get(self, key, default=None):
529*9e94795aSAndroid Build Coastguard Worker    return self.info_dict.get(key, default)
530*9e94795aSAndroid Build Coastguard Worker
531*9e94795aSAndroid Build Coastguard Worker  def items(self):
532*9e94795aSAndroid Build Coastguard Worker    return self.info_dict.items()
533*9e94795aSAndroid Build Coastguard Worker
534*9e94795aSAndroid Build Coastguard Worker  def _GetRawBuildProp(self, prop, partition):
535*9e94795aSAndroid Build Coastguard Worker    prop_file = '{}.build.prop'.format(
536*9e94795aSAndroid Build Coastguard Worker        partition) if partition else 'build.prop'
537*9e94795aSAndroid Build Coastguard Worker    partition_props = self.info_dict.get(prop_file)
538*9e94795aSAndroid Build Coastguard Worker    if not partition_props:
539*9e94795aSAndroid Build Coastguard Worker      return None
540*9e94795aSAndroid Build Coastguard Worker    return partition_props.GetProp(prop)
541*9e94795aSAndroid Build Coastguard Worker
542*9e94795aSAndroid Build Coastguard Worker  def GetPartitionBuildProp(self, prop, partition):
543*9e94795aSAndroid Build Coastguard Worker    """Returns the inquired build property for the provided partition."""
544*9e94795aSAndroid Build Coastguard Worker
545*9e94795aSAndroid Build Coastguard Worker    # Boot image and init_boot image uses ro.[product.]bootimage instead of boot.
546*9e94795aSAndroid Build Coastguard Worker    # This comes from the generic ramdisk
547*9e94795aSAndroid Build Coastguard Worker    prop_partition = "bootimage" if partition == "boot" or partition == "init_boot" else partition
548*9e94795aSAndroid Build Coastguard Worker
549*9e94795aSAndroid Build Coastguard Worker    # If provided a partition for this property, only look within that
550*9e94795aSAndroid Build Coastguard Worker    # partition's build.prop.
551*9e94795aSAndroid Build Coastguard Worker    if prop in BuildInfo._RO_PRODUCT_RESOLVE_PROPS:
552*9e94795aSAndroid Build Coastguard Worker      prop = prop.replace("ro.product", "ro.product.{}".format(prop_partition))
553*9e94795aSAndroid Build Coastguard Worker    else:
554*9e94795aSAndroid Build Coastguard Worker      prop = prop.replace("ro.", "ro.{}.".format(prop_partition))
555*9e94795aSAndroid Build Coastguard Worker
556*9e94795aSAndroid Build Coastguard Worker    prop_val = self._GetRawBuildProp(prop, partition)
557*9e94795aSAndroid Build Coastguard Worker    if prop_val is not None:
558*9e94795aSAndroid Build Coastguard Worker      return prop_val
559*9e94795aSAndroid Build Coastguard Worker    raise ExternalError("couldn't find %s in %s.build.prop" %
560*9e94795aSAndroid Build Coastguard Worker                        (prop, partition))
561*9e94795aSAndroid Build Coastguard Worker
562*9e94795aSAndroid Build Coastguard Worker  def GetBuildProp(self, prop):
563*9e94795aSAndroid Build Coastguard Worker    """Returns the inquired build property from the standard build.prop file."""
564*9e94795aSAndroid Build Coastguard Worker    if prop in BuildInfo._RO_PRODUCT_RESOLVE_PROPS:
565*9e94795aSAndroid Build Coastguard Worker      return self._ResolveRoProductBuildProp(prop)
566*9e94795aSAndroid Build Coastguard Worker
567*9e94795aSAndroid Build Coastguard Worker    if prop == "ro.build.id":
568*9e94795aSAndroid Build Coastguard Worker      return self._GetBuildId()
569*9e94795aSAndroid Build Coastguard Worker
570*9e94795aSAndroid Build Coastguard Worker    prop_val = self._GetRawBuildProp(prop, None)
571*9e94795aSAndroid Build Coastguard Worker    if prop_val is not None:
572*9e94795aSAndroid Build Coastguard Worker      return prop_val
573*9e94795aSAndroid Build Coastguard Worker
574*9e94795aSAndroid Build Coastguard Worker    raise ExternalError("couldn't find %s in build.prop" % (prop,))
575*9e94795aSAndroid Build Coastguard Worker
576*9e94795aSAndroid Build Coastguard Worker  def _ResolveRoProductBuildProp(self, prop):
577*9e94795aSAndroid Build Coastguard Worker    """Resolves the inquired ro.product.* build property"""
578*9e94795aSAndroid Build Coastguard Worker    prop_val = self._GetRawBuildProp(prop, None)
579*9e94795aSAndroid Build Coastguard Worker    if prop_val:
580*9e94795aSAndroid Build Coastguard Worker      return prop_val
581*9e94795aSAndroid Build Coastguard Worker
582*9e94795aSAndroid Build Coastguard Worker    default_source_order = self._GetRoProductPropsDefaultSourceOrder()
583*9e94795aSAndroid Build Coastguard Worker    source_order_val = self._GetRawBuildProp(
584*9e94795aSAndroid Build Coastguard Worker        "ro.product.property_source_order", None)
585*9e94795aSAndroid Build Coastguard Worker    if source_order_val:
586*9e94795aSAndroid Build Coastguard Worker      source_order = source_order_val.split(",")
587*9e94795aSAndroid Build Coastguard Worker    else:
588*9e94795aSAndroid Build Coastguard Worker      source_order = default_source_order
589*9e94795aSAndroid Build Coastguard Worker
590*9e94795aSAndroid Build Coastguard Worker    # Check that all sources in ro.product.property_source_order are valid
591*9e94795aSAndroid Build Coastguard Worker    if any([x not in default_source_order for x in source_order]):
592*9e94795aSAndroid Build Coastguard Worker      raise ExternalError(
593*9e94795aSAndroid Build Coastguard Worker          "Invalid ro.product.property_source_order '{}'".format(source_order))
594*9e94795aSAndroid Build Coastguard Worker
595*9e94795aSAndroid Build Coastguard Worker    for source_partition in source_order:
596*9e94795aSAndroid Build Coastguard Worker      source_prop = prop.replace(
597*9e94795aSAndroid Build Coastguard Worker          "ro.product", "ro.product.{}".format(source_partition), 1)
598*9e94795aSAndroid Build Coastguard Worker      prop_val = self._GetRawBuildProp(source_prop, source_partition)
599*9e94795aSAndroid Build Coastguard Worker      if prop_val:
600*9e94795aSAndroid Build Coastguard Worker        return prop_val
601*9e94795aSAndroid Build Coastguard Worker
602*9e94795aSAndroid Build Coastguard Worker    raise ExternalError("couldn't resolve {}".format(prop))
603*9e94795aSAndroid Build Coastguard Worker
604*9e94795aSAndroid Build Coastguard Worker  def _GetRoProductPropsDefaultSourceOrder(self):
605*9e94795aSAndroid Build Coastguard Worker    # NOTE: refer to CDDs and android.os.Build.VERSION for the definition and
606*9e94795aSAndroid Build Coastguard Worker    # values of these properties for each Android release.
607*9e94795aSAndroid Build Coastguard Worker    android_codename = self._GetRawBuildProp("ro.build.version.codename", None)
608*9e94795aSAndroid Build Coastguard Worker    if android_codename == "REL":
609*9e94795aSAndroid Build Coastguard Worker      android_version = self._GetRawBuildProp("ro.build.version.release", None)
610*9e94795aSAndroid Build Coastguard Worker      if android_version == "10":
611*9e94795aSAndroid Build Coastguard Worker        return BuildInfo._RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER_ANDROID_10
612*9e94795aSAndroid Build Coastguard Worker      # NOTE: float() conversion of android_version will have rounding error.
613*9e94795aSAndroid Build Coastguard Worker      # We are checking for "9" or less, and using "< 10" is well outside of
614*9e94795aSAndroid Build Coastguard Worker      # possible floating point rounding.
615*9e94795aSAndroid Build Coastguard Worker      try:
616*9e94795aSAndroid Build Coastguard Worker        android_version_val = float(android_version)
617*9e94795aSAndroid Build Coastguard Worker      except ValueError:
618*9e94795aSAndroid Build Coastguard Worker        android_version_val = 0
619*9e94795aSAndroid Build Coastguard Worker      if android_version_val < 10:
620*9e94795aSAndroid Build Coastguard Worker        return BuildInfo._RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER_LEGACY
621*9e94795aSAndroid Build Coastguard Worker    return BuildInfo._RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER_CURRENT
622*9e94795aSAndroid Build Coastguard Worker
623*9e94795aSAndroid Build Coastguard Worker  def _GetPlatformVersion(self):
624*9e94795aSAndroid Build Coastguard Worker    version_sdk = self.GetBuildProp("ro.build.version.sdk")
625*9e94795aSAndroid Build Coastguard Worker    # init code switches to version_release_or_codename (see b/158483506). After
626*9e94795aSAndroid Build Coastguard Worker    # API finalization, release_or_codename will be the same as release. This
627*9e94795aSAndroid Build Coastguard Worker    # is the best effort to support pre-S dev stage builds.
628*9e94795aSAndroid Build Coastguard Worker    if int(version_sdk) >= 30:
629*9e94795aSAndroid Build Coastguard Worker      try:
630*9e94795aSAndroid Build Coastguard Worker        return self.GetBuildProp("ro.build.version.release_or_codename")
631*9e94795aSAndroid Build Coastguard Worker      except ExternalError:
632*9e94795aSAndroid Build Coastguard Worker        logger.warning('Failed to find ro.build.version.release_or_codename')
633*9e94795aSAndroid Build Coastguard Worker
634*9e94795aSAndroid Build Coastguard Worker    return self.GetBuildProp("ro.build.version.release")
635*9e94795aSAndroid Build Coastguard Worker
636*9e94795aSAndroid Build Coastguard Worker  def _GetBuildId(self):
637*9e94795aSAndroid Build Coastguard Worker    build_id = self._GetRawBuildProp("ro.build.id", None)
638*9e94795aSAndroid Build Coastguard Worker    if build_id:
639*9e94795aSAndroid Build Coastguard Worker      return build_id
640*9e94795aSAndroid Build Coastguard Worker
641*9e94795aSAndroid Build Coastguard Worker    legacy_build_id = self.GetBuildProp("ro.build.legacy.id")
642*9e94795aSAndroid Build Coastguard Worker    if not legacy_build_id:
643*9e94795aSAndroid Build Coastguard Worker      raise ExternalError("Couldn't find build id in property file")
644*9e94795aSAndroid Build Coastguard Worker
645*9e94795aSAndroid Build Coastguard Worker    if self.use_legacy_id:
646*9e94795aSAndroid Build Coastguard Worker      return legacy_build_id
647*9e94795aSAndroid Build Coastguard Worker
648*9e94795aSAndroid Build Coastguard Worker    # Append the top 8 chars of vbmeta digest to the existing build id. The
649*9e94795aSAndroid Build Coastguard Worker    # logic needs to match the one in init, so that OTA can deliver correctly.
650*9e94795aSAndroid Build Coastguard Worker    avb_enable = self.info_dict.get("avb_enable") == "true"
651*9e94795aSAndroid Build Coastguard Worker    if not avb_enable:
652*9e94795aSAndroid Build Coastguard Worker      raise ExternalError("AVB isn't enabled when using legacy build id")
653*9e94795aSAndroid Build Coastguard Worker
654*9e94795aSAndroid Build Coastguard Worker    vbmeta_digest = self.info_dict.get("vbmeta_digest")
655*9e94795aSAndroid Build Coastguard Worker    if not vbmeta_digest:
656*9e94795aSAndroid Build Coastguard Worker      raise ExternalError("Vbmeta digest isn't provided when using legacy build"
657*9e94795aSAndroid Build Coastguard Worker                          " id")
658*9e94795aSAndroid Build Coastguard Worker    if len(vbmeta_digest) < self._VBMETA_DIGEST_SIZE_USED:
659*9e94795aSAndroid Build Coastguard Worker      raise ExternalError("Invalid vbmeta digest " + vbmeta_digest)
660*9e94795aSAndroid Build Coastguard Worker
661*9e94795aSAndroid Build Coastguard Worker    digest_prefix = vbmeta_digest[:self._VBMETA_DIGEST_SIZE_USED]
662*9e94795aSAndroid Build Coastguard Worker    return legacy_build_id + '.' + digest_prefix
663*9e94795aSAndroid Build Coastguard Worker
664*9e94795aSAndroid Build Coastguard Worker  def _GetPartitionPlatformVersion(self, partition):
665*9e94795aSAndroid Build Coastguard Worker    try:
666*9e94795aSAndroid Build Coastguard Worker      return self.GetPartitionBuildProp("ro.build.version.release_or_codename",
667*9e94795aSAndroid Build Coastguard Worker                                        partition)
668*9e94795aSAndroid Build Coastguard Worker    except ExternalError:
669*9e94795aSAndroid Build Coastguard Worker      return self.GetPartitionBuildProp("ro.build.version.release",
670*9e94795aSAndroid Build Coastguard Worker                                        partition)
671*9e94795aSAndroid Build Coastguard Worker
672*9e94795aSAndroid Build Coastguard Worker  def GetOemProperty(self, key):
673*9e94795aSAndroid Build Coastguard Worker    if self.oem_props is not None and key in self.oem_props:
674*9e94795aSAndroid Build Coastguard Worker      return self.oem_dicts[0][key]
675*9e94795aSAndroid Build Coastguard Worker    return self.GetBuildProp(key)
676*9e94795aSAndroid Build Coastguard Worker
677*9e94795aSAndroid Build Coastguard Worker  def GetPartitionFingerprint(self, partition):
678*9e94795aSAndroid Build Coastguard Worker    return self._partition_fingerprints.get(partition, None)
679*9e94795aSAndroid Build Coastguard Worker
680*9e94795aSAndroid Build Coastguard Worker  def CalculatePartitionFingerprint(self, partition):
681*9e94795aSAndroid Build Coastguard Worker    try:
682*9e94795aSAndroid Build Coastguard Worker      return self.GetPartitionBuildProp("ro.build.fingerprint", partition)
683*9e94795aSAndroid Build Coastguard Worker    except ExternalError:
684*9e94795aSAndroid Build Coastguard Worker      return "{}/{}/{}:{}/{}/{}:{}/{}".format(
685*9e94795aSAndroid Build Coastguard Worker          self.GetPartitionBuildProp("ro.product.brand", partition),
686*9e94795aSAndroid Build Coastguard Worker          self.GetPartitionBuildProp("ro.product.name", partition),
687*9e94795aSAndroid Build Coastguard Worker          self.GetPartitionBuildProp("ro.product.device", partition),
688*9e94795aSAndroid Build Coastguard Worker          self._GetPartitionPlatformVersion(partition),
689*9e94795aSAndroid Build Coastguard Worker          self.GetPartitionBuildProp("ro.build.id", partition),
690*9e94795aSAndroid Build Coastguard Worker          self.GetPartitionBuildProp(
691*9e94795aSAndroid Build Coastguard Worker              "ro.build.version.incremental", partition),
692*9e94795aSAndroid Build Coastguard Worker          self.GetPartitionBuildProp("ro.build.type", partition),
693*9e94795aSAndroid Build Coastguard Worker          self.GetPartitionBuildProp("ro.build.tags", partition))
694*9e94795aSAndroid Build Coastguard Worker
695*9e94795aSAndroid Build Coastguard Worker  def CalculateFingerprint(self):
696*9e94795aSAndroid Build Coastguard Worker    if self.oem_props is None:
697*9e94795aSAndroid Build Coastguard Worker      try:
698*9e94795aSAndroid Build Coastguard Worker        return self.GetBuildProp("ro.build.fingerprint")
699*9e94795aSAndroid Build Coastguard Worker      except ExternalError:
700*9e94795aSAndroid Build Coastguard Worker        return "{}/{}/{}:{}/{}/{}:{}/{}".format(
701*9e94795aSAndroid Build Coastguard Worker            self.GetBuildProp("ro.product.brand"),
702*9e94795aSAndroid Build Coastguard Worker            self.GetBuildProp("ro.product.name"),
703*9e94795aSAndroid Build Coastguard Worker            self.GetBuildProp("ro.product.device"),
704*9e94795aSAndroid Build Coastguard Worker            self._GetPlatformVersion(),
705*9e94795aSAndroid Build Coastguard Worker            self.GetBuildProp("ro.build.id"),
706*9e94795aSAndroid Build Coastguard Worker            self.GetBuildProp("ro.build.version.incremental"),
707*9e94795aSAndroid Build Coastguard Worker            self.GetBuildProp("ro.build.type"),
708*9e94795aSAndroid Build Coastguard Worker            self.GetBuildProp("ro.build.tags"))
709*9e94795aSAndroid Build Coastguard Worker    return "%s/%s/%s:%s" % (
710*9e94795aSAndroid Build Coastguard Worker        self.GetOemProperty("ro.product.brand"),
711*9e94795aSAndroid Build Coastguard Worker        self.GetOemProperty("ro.product.name"),
712*9e94795aSAndroid Build Coastguard Worker        self.GetOemProperty("ro.product.device"),
713*9e94795aSAndroid Build Coastguard Worker        self.GetBuildProp("ro.build.thumbprint"))
714*9e94795aSAndroid Build Coastguard Worker
715*9e94795aSAndroid Build Coastguard Worker  def WriteMountOemScript(self, script):
716*9e94795aSAndroid Build Coastguard Worker    assert self.oem_props is not None
717*9e94795aSAndroid Build Coastguard Worker    recovery_mount_options = self.info_dict.get("recovery_mount_options")
718*9e94795aSAndroid Build Coastguard Worker    script.Mount("/oem", recovery_mount_options)
719*9e94795aSAndroid Build Coastguard Worker
720*9e94795aSAndroid Build Coastguard Worker  def WriteDeviceAssertions(self, script, oem_no_mount):
721*9e94795aSAndroid Build Coastguard Worker    # Read the property directly if not using OEM properties.
722*9e94795aSAndroid Build Coastguard Worker    if not self.oem_props:
723*9e94795aSAndroid Build Coastguard Worker      script.AssertDevice(self.device)
724*9e94795aSAndroid Build Coastguard Worker      return
725*9e94795aSAndroid Build Coastguard Worker
726*9e94795aSAndroid Build Coastguard Worker    # Otherwise assert OEM properties.
727*9e94795aSAndroid Build Coastguard Worker    if not self.oem_dicts:
728*9e94795aSAndroid Build Coastguard Worker      raise ExternalError(
729*9e94795aSAndroid Build Coastguard Worker          "No OEM file provided to answer expected assertions")
730*9e94795aSAndroid Build Coastguard Worker
731*9e94795aSAndroid Build Coastguard Worker    for prop in self.oem_props.split():
732*9e94795aSAndroid Build Coastguard Worker      values = []
733*9e94795aSAndroid Build Coastguard Worker      for oem_dict in self.oem_dicts:
734*9e94795aSAndroid Build Coastguard Worker        if prop in oem_dict:
735*9e94795aSAndroid Build Coastguard Worker          values.append(oem_dict[prop])
736*9e94795aSAndroid Build Coastguard Worker      if not values:
737*9e94795aSAndroid Build Coastguard Worker        raise ExternalError(
738*9e94795aSAndroid Build Coastguard Worker            "The OEM file is missing the property %s" % (prop,))
739*9e94795aSAndroid Build Coastguard Worker      script.AssertOemProperty(prop, values, oem_no_mount)
740*9e94795aSAndroid Build Coastguard Worker
741*9e94795aSAndroid Build Coastguard Worker
742*9e94795aSAndroid Build Coastguard Workerdef DoesInputFileContain(input_file, fn):
743*9e94795aSAndroid Build Coastguard Worker  """Check whether the input target_files.zip contain an entry `fn`"""
744*9e94795aSAndroid Build Coastguard Worker  if isinstance(input_file, zipfile.ZipFile):
745*9e94795aSAndroid Build Coastguard Worker    return fn in input_file.namelist()
746*9e94795aSAndroid Build Coastguard Worker  elif zipfile.is_zipfile(input_file):
747*9e94795aSAndroid Build Coastguard Worker    with zipfile.ZipFile(input_file, "r", allowZip64=True) as zfp:
748*9e94795aSAndroid Build Coastguard Worker      return fn in zfp.namelist()
749*9e94795aSAndroid Build Coastguard Worker  else:
750*9e94795aSAndroid Build Coastguard Worker    if not os.path.isdir(input_file):
751*9e94795aSAndroid Build Coastguard Worker      raise ValueError(
752*9e94795aSAndroid Build Coastguard Worker          "Invalid input_file, accepted inputs are ZipFile object, path to .zip file on disk, or path to extracted directory. Actual: " + input_file)
753*9e94795aSAndroid Build Coastguard Worker    path = os.path.join(input_file, *fn.split("/"))
754*9e94795aSAndroid Build Coastguard Worker    return os.path.exists(path)
755*9e94795aSAndroid Build Coastguard Worker
756*9e94795aSAndroid Build Coastguard Worker
757*9e94795aSAndroid Build Coastguard Workerdef ReadBytesFromInputFile(input_file, fn):
758*9e94795aSAndroid Build Coastguard Worker  """Reads the bytes of fn from input zipfile or directory."""
759*9e94795aSAndroid Build Coastguard Worker  if isinstance(input_file, zipfile.ZipFile):
760*9e94795aSAndroid Build Coastguard Worker    return input_file.read(fn)
761*9e94795aSAndroid Build Coastguard Worker  elif zipfile.is_zipfile(input_file):
762*9e94795aSAndroid Build Coastguard Worker    with zipfile.ZipFile(input_file, "r", allowZip64=True) as zfp:
763*9e94795aSAndroid Build Coastguard Worker      return zfp.read(fn)
764*9e94795aSAndroid Build Coastguard Worker  else:
765*9e94795aSAndroid Build Coastguard Worker    if not os.path.isdir(input_file):
766*9e94795aSAndroid Build Coastguard Worker      raise ValueError(
767*9e94795aSAndroid Build Coastguard Worker          "Invalid input_file, accepted inputs are ZipFile object, path to .zip file on disk, or path to extracted directory. Actual: " + input_file)
768*9e94795aSAndroid Build Coastguard Worker    path = os.path.join(input_file, *fn.split("/"))
769*9e94795aSAndroid Build Coastguard Worker    try:
770*9e94795aSAndroid Build Coastguard Worker      with open(path, "rb") as f:
771*9e94795aSAndroid Build Coastguard Worker        return f.read()
772*9e94795aSAndroid Build Coastguard Worker    except IOError as e:
773*9e94795aSAndroid Build Coastguard Worker      if e.errno == errno.ENOENT:
774*9e94795aSAndroid Build Coastguard Worker        raise KeyError(fn)
775*9e94795aSAndroid Build Coastguard Worker
776*9e94795aSAndroid Build Coastguard Worker
777*9e94795aSAndroid Build Coastguard Workerdef ReadFromInputFile(input_file, fn):
778*9e94795aSAndroid Build Coastguard Worker  """Reads the str contents of fn from input zipfile or directory."""
779*9e94795aSAndroid Build Coastguard Worker  return ReadBytesFromInputFile(input_file, fn).decode()
780*9e94795aSAndroid Build Coastguard Worker
781*9e94795aSAndroid Build Coastguard Worker
782*9e94795aSAndroid Build Coastguard Workerdef WriteBytesToInputFile(input_file, fn, data):
783*9e94795aSAndroid Build Coastguard Worker  """Write bytes |data| contents to fn of input zipfile or directory."""
784*9e94795aSAndroid Build Coastguard Worker  if isinstance(input_file, zipfile.ZipFile):
785*9e94795aSAndroid Build Coastguard Worker    with input_file.open(fn, "w") as entry_fp:
786*9e94795aSAndroid Build Coastguard Worker      return entry_fp.write(data)
787*9e94795aSAndroid Build Coastguard Worker  elif zipfile.is_zipfile(input_file):
788*9e94795aSAndroid Build Coastguard Worker    with zipfile.ZipFile(input_file, "r", allowZip64=True) as zfp:
789*9e94795aSAndroid Build Coastguard Worker      with zfp.open(fn, "w") as entry_fp:
790*9e94795aSAndroid Build Coastguard Worker        return entry_fp.write(data)
791*9e94795aSAndroid Build Coastguard Worker  else:
792*9e94795aSAndroid Build Coastguard Worker    if not os.path.isdir(input_file):
793*9e94795aSAndroid Build Coastguard Worker      raise ValueError(
794*9e94795aSAndroid Build Coastguard Worker          "Invalid input_file, accepted inputs are ZipFile object, path to .zip file on disk, or path to extracted directory. Actual: " + input_file)
795*9e94795aSAndroid Build Coastguard Worker    path = os.path.join(input_file, *fn.split("/"))
796*9e94795aSAndroid Build Coastguard Worker    try:
797*9e94795aSAndroid Build Coastguard Worker      with open(path, "wb") as f:
798*9e94795aSAndroid Build Coastguard Worker        return f.write(data)
799*9e94795aSAndroid Build Coastguard Worker    except IOError as e:
800*9e94795aSAndroid Build Coastguard Worker      if e.errno == errno.ENOENT:
801*9e94795aSAndroid Build Coastguard Worker        raise KeyError(fn)
802*9e94795aSAndroid Build Coastguard Worker
803*9e94795aSAndroid Build Coastguard Worker
804*9e94795aSAndroid Build Coastguard Workerdef WriteToInputFile(input_file, fn, str: str):
805*9e94795aSAndroid Build Coastguard Worker  """Write str content to fn of input file or directory"""
806*9e94795aSAndroid Build Coastguard Worker  return WriteBytesToInputFile(input_file, fn, str.encode())
807*9e94795aSAndroid Build Coastguard Worker
808*9e94795aSAndroid Build Coastguard Worker
809*9e94795aSAndroid Build Coastguard Workerdef ExtractFromInputFile(input_file, fn):
810*9e94795aSAndroid Build Coastguard Worker  """Extracts the contents of fn from input zipfile or directory into a file."""
811*9e94795aSAndroid Build Coastguard Worker  if isinstance(input_file, zipfile.ZipFile):
812*9e94795aSAndroid Build Coastguard Worker    tmp_file = MakeTempFile(os.path.basename(fn))
813*9e94795aSAndroid Build Coastguard Worker    with open(tmp_file, 'wb') as f:
814*9e94795aSAndroid Build Coastguard Worker      f.write(input_file.read(fn))
815*9e94795aSAndroid Build Coastguard Worker    return tmp_file
816*9e94795aSAndroid Build Coastguard Worker  elif zipfile.is_zipfile(input_file):
817*9e94795aSAndroid Build Coastguard Worker    with zipfile.ZipFile(input_file, "r", allowZip64=True) as zfp:
818*9e94795aSAndroid Build Coastguard Worker      tmp_file = MakeTempFile(os.path.basename(fn))
819*9e94795aSAndroid Build Coastguard Worker      with open(tmp_file, "wb") as fp:
820*9e94795aSAndroid Build Coastguard Worker        fp.write(zfp.read(fn))
821*9e94795aSAndroid Build Coastguard Worker      return tmp_file
822*9e94795aSAndroid Build Coastguard Worker  else:
823*9e94795aSAndroid Build Coastguard Worker    if not os.path.isdir(input_file):
824*9e94795aSAndroid Build Coastguard Worker      raise ValueError(
825*9e94795aSAndroid Build Coastguard Worker          "Invalid input_file, accepted inputs are ZipFile object, path to .zip file on disk, or path to extracted directory. Actual: " + input_file)
826*9e94795aSAndroid Build Coastguard Worker    file = os.path.join(input_file, *fn.split("/"))
827*9e94795aSAndroid Build Coastguard Worker    if not os.path.exists(file):
828*9e94795aSAndroid Build Coastguard Worker      raise KeyError(fn)
829*9e94795aSAndroid Build Coastguard Worker    return file
830*9e94795aSAndroid Build Coastguard Worker
831*9e94795aSAndroid Build Coastguard Worker
832*9e94795aSAndroid Build Coastguard Workerclass RamdiskFormat(object):
833*9e94795aSAndroid Build Coastguard Worker  LZ4 = 1
834*9e94795aSAndroid Build Coastguard Worker  GZ = 2
835*9e94795aSAndroid Build Coastguard Worker
836*9e94795aSAndroid Build Coastguard Worker
837*9e94795aSAndroid Build Coastguard Workerdef GetRamdiskFormat(info_dict):
838*9e94795aSAndroid Build Coastguard Worker  if info_dict.get('lz4_ramdisks') == 'true':
839*9e94795aSAndroid Build Coastguard Worker    ramdisk_format = RamdiskFormat.LZ4
840*9e94795aSAndroid Build Coastguard Worker  else:
841*9e94795aSAndroid Build Coastguard Worker    ramdisk_format = RamdiskFormat.GZ
842*9e94795aSAndroid Build Coastguard Worker  return ramdisk_format
843*9e94795aSAndroid Build Coastguard Worker
844*9e94795aSAndroid Build Coastguard Worker
845*9e94795aSAndroid Build Coastguard Workerdef LoadInfoDict(input_file, repacking=False):
846*9e94795aSAndroid Build Coastguard Worker  """Loads the key/value pairs from the given input target_files.
847*9e94795aSAndroid Build Coastguard Worker
848*9e94795aSAndroid Build Coastguard Worker  It reads `META/misc_info.txt` file in the target_files input, does validation
849*9e94795aSAndroid Build Coastguard Worker  checks and returns the parsed key/value pairs for to the given build. It's
850*9e94795aSAndroid Build Coastguard Worker  usually called early when working on input target_files files, e.g. when
851*9e94795aSAndroid Build Coastguard Worker  generating OTAs, or signing builds. Note that the function may be called
852*9e94795aSAndroid Build Coastguard Worker  against an old target_files file (i.e. from past dessert releases). So the
853*9e94795aSAndroid Build Coastguard Worker  property parsing needs to be backward compatible.
854*9e94795aSAndroid Build Coastguard Worker
855*9e94795aSAndroid Build Coastguard Worker  In a `META/misc_info.txt`, a few properties are stored as links to the files
856*9e94795aSAndroid Build Coastguard Worker  in the PRODUCT_OUT directory. It works fine with the build system. However,
857*9e94795aSAndroid Build Coastguard Worker  they are no longer available when (re)generating images from target_files zip.
858*9e94795aSAndroid Build Coastguard Worker  When `repacking` is True, redirect these properties to the actual files in the
859*9e94795aSAndroid Build Coastguard Worker  unzipped directory.
860*9e94795aSAndroid Build Coastguard Worker
861*9e94795aSAndroid Build Coastguard Worker  Args:
862*9e94795aSAndroid Build Coastguard Worker    input_file: The input target_files file, which could be an open
863*9e94795aSAndroid Build Coastguard Worker        zipfile.ZipFile instance, or a str for the dir that contains the files
864*9e94795aSAndroid Build Coastguard Worker        unzipped from a target_files file.
865*9e94795aSAndroid Build Coastguard Worker    repacking: Whether it's trying repack an target_files file after loading the
866*9e94795aSAndroid Build Coastguard Worker        info dict (default: False). If so, it will rewrite a few loaded
867*9e94795aSAndroid Build Coastguard Worker        properties (e.g. selinux_fc, root_dir) to point to the actual files in
868*9e94795aSAndroid Build Coastguard Worker        target_files file. When doing repacking, `input_file` must be a dir.
869*9e94795aSAndroid Build Coastguard Worker
870*9e94795aSAndroid Build Coastguard Worker  Returns:
871*9e94795aSAndroid Build Coastguard Worker    A dict that contains the parsed key/value pairs.
872*9e94795aSAndroid Build Coastguard Worker
873*9e94795aSAndroid Build Coastguard Worker  Raises:
874*9e94795aSAndroid Build Coastguard Worker    AssertionError: On invalid input arguments.
875*9e94795aSAndroid Build Coastguard Worker    ValueError: On malformed input values.
876*9e94795aSAndroid Build Coastguard Worker  """
877*9e94795aSAndroid Build Coastguard Worker  if repacking:
878*9e94795aSAndroid Build Coastguard Worker    assert isinstance(input_file, str), \
879*9e94795aSAndroid Build Coastguard Worker        "input_file must be a path str when doing repacking"
880*9e94795aSAndroid Build Coastguard Worker
881*9e94795aSAndroid Build Coastguard Worker  def read_helper(fn):
882*9e94795aSAndroid Build Coastguard Worker    return ReadFromInputFile(input_file, fn)
883*9e94795aSAndroid Build Coastguard Worker
884*9e94795aSAndroid Build Coastguard Worker  try:
885*9e94795aSAndroid Build Coastguard Worker    d = LoadDictionaryFromLines(read_helper("META/misc_info.txt").split("\n"))
886*9e94795aSAndroid Build Coastguard Worker  except KeyError:
887*9e94795aSAndroid Build Coastguard Worker    raise ValueError("Failed to find META/misc_info.txt in input target-files")
888*9e94795aSAndroid Build Coastguard Worker
889*9e94795aSAndroid Build Coastguard Worker  if "recovery_api_version" not in d:
890*9e94795aSAndroid Build Coastguard Worker    raise ValueError("Failed to find 'recovery_api_version'")
891*9e94795aSAndroid Build Coastguard Worker  if "fstab_version" not in d:
892*9e94795aSAndroid Build Coastguard Worker    raise ValueError("Failed to find 'fstab_version'")
893*9e94795aSAndroid Build Coastguard Worker
894*9e94795aSAndroid Build Coastguard Worker  if repacking:
895*9e94795aSAndroid Build Coastguard Worker    # "selinux_fc" properties should point to the file_contexts files
896*9e94795aSAndroid Build Coastguard Worker    # (file_contexts.bin) under META/.
897*9e94795aSAndroid Build Coastguard Worker    for key in d:
898*9e94795aSAndroid Build Coastguard Worker      if key.endswith("selinux_fc"):
899*9e94795aSAndroid Build Coastguard Worker        fc_basename = os.path.basename(d[key])
900*9e94795aSAndroid Build Coastguard Worker        fc_config = os.path.join(input_file, "META", fc_basename)
901*9e94795aSAndroid Build Coastguard Worker        assert os.path.exists(fc_config), "{} does not exist".format(fc_config)
902*9e94795aSAndroid Build Coastguard Worker
903*9e94795aSAndroid Build Coastguard Worker        d[key] = fc_config
904*9e94795aSAndroid Build Coastguard Worker
905*9e94795aSAndroid Build Coastguard Worker    # Similarly we need to redirect "root_dir", and "root_fs_config".
906*9e94795aSAndroid Build Coastguard Worker    d["root_dir"] = os.path.join(input_file, "ROOT")
907*9e94795aSAndroid Build Coastguard Worker    d["root_fs_config"] = os.path.join(
908*9e94795aSAndroid Build Coastguard Worker        input_file, "META", "root_filesystem_config.txt")
909*9e94795aSAndroid Build Coastguard Worker
910*9e94795aSAndroid Build Coastguard Worker    partitions = ["system", "vendor", "system_ext", "product", "odm",
911*9e94795aSAndroid Build Coastguard Worker                  "vendor_dlkm", "odm_dlkm", "system_dlkm"]
912*9e94795aSAndroid Build Coastguard Worker    # Redirect {partition}_base_fs_file for each of the named partitions.
913*9e94795aSAndroid Build Coastguard Worker    for part_name in partitions:
914*9e94795aSAndroid Build Coastguard Worker      key_name = part_name + "_base_fs_file"
915*9e94795aSAndroid Build Coastguard Worker      if key_name not in d:
916*9e94795aSAndroid Build Coastguard Worker        continue
917*9e94795aSAndroid Build Coastguard Worker      basename = os.path.basename(d[key_name])
918*9e94795aSAndroid Build Coastguard Worker      base_fs_file = os.path.join(input_file, "META", basename)
919*9e94795aSAndroid Build Coastguard Worker      if os.path.exists(base_fs_file):
920*9e94795aSAndroid Build Coastguard Worker        d[key_name] = base_fs_file
921*9e94795aSAndroid Build Coastguard Worker      else:
922*9e94795aSAndroid Build Coastguard Worker        logger.warning(
923*9e94795aSAndroid Build Coastguard Worker            "Failed to find %s base fs file: %s", part_name, base_fs_file)
924*9e94795aSAndroid Build Coastguard Worker        del d[key_name]
925*9e94795aSAndroid Build Coastguard Worker
926*9e94795aSAndroid Build Coastguard Worker    # Redirecting helper for optional properties like erofs_compress_hints
927*9e94795aSAndroid Build Coastguard Worker    def redirect_file(prop, filename):
928*9e94795aSAndroid Build Coastguard Worker      if prop not in d:
929*9e94795aSAndroid Build Coastguard Worker        return
930*9e94795aSAndroid Build Coastguard Worker      config_file = os.path.join(input_file, "META/" + filename)
931*9e94795aSAndroid Build Coastguard Worker      if os.path.exists(config_file):
932*9e94795aSAndroid Build Coastguard Worker        d[prop] = config_file
933*9e94795aSAndroid Build Coastguard Worker      else:
934*9e94795aSAndroid Build Coastguard Worker        logger.warning(
935*9e94795aSAndroid Build Coastguard Worker            "Failed to find %s fro %s", filename, prop)
936*9e94795aSAndroid Build Coastguard Worker        del d[prop]
937*9e94795aSAndroid Build Coastguard Worker
938*9e94795aSAndroid Build Coastguard Worker    # Redirect erofs_[default_]compress_hints files
939*9e94795aSAndroid Build Coastguard Worker    redirect_file("erofs_default_compress_hints",
940*9e94795aSAndroid Build Coastguard Worker                  "erofs_default_compress_hints.txt")
941*9e94795aSAndroid Build Coastguard Worker    for part in partitions:
942*9e94795aSAndroid Build Coastguard Worker      redirect_file(part + "_erofs_compress_hints",
943*9e94795aSAndroid Build Coastguard Worker                    part + "_erofs_compress_hints.txt")
944*9e94795aSAndroid Build Coastguard Worker
945*9e94795aSAndroid Build Coastguard Worker  def makeint(key):
946*9e94795aSAndroid Build Coastguard Worker    if key in d:
947*9e94795aSAndroid Build Coastguard Worker      d[key] = int(d[key], 0)
948*9e94795aSAndroid Build Coastguard Worker
949*9e94795aSAndroid Build Coastguard Worker  makeint("recovery_api_version")
950*9e94795aSAndroid Build Coastguard Worker  makeint("blocksize")
951*9e94795aSAndroid Build Coastguard Worker  makeint("system_size")
952*9e94795aSAndroid Build Coastguard Worker  makeint("vendor_size")
953*9e94795aSAndroid Build Coastguard Worker  makeint("userdata_size")
954*9e94795aSAndroid Build Coastguard Worker  makeint("cache_size")
955*9e94795aSAndroid Build Coastguard Worker  makeint("recovery_size")
956*9e94795aSAndroid Build Coastguard Worker  makeint("fstab_version")
957*9e94795aSAndroid Build Coastguard Worker
958*9e94795aSAndroid Build Coastguard Worker  boot_images = "boot.img"
959*9e94795aSAndroid Build Coastguard Worker  if "boot_images" in d:
960*9e94795aSAndroid Build Coastguard Worker    boot_images = d["boot_images"]
961*9e94795aSAndroid Build Coastguard Worker  for b in boot_images.split():
962*9e94795aSAndroid Build Coastguard Worker    makeint(b.replace(".img", "_size"))
963*9e94795aSAndroid Build Coastguard Worker
964*9e94795aSAndroid Build Coastguard Worker  # Load recovery fstab if applicable.
965*9e94795aSAndroid Build Coastguard Worker  d["fstab"] = _FindAndLoadRecoveryFstab(d, input_file, read_helper)
966*9e94795aSAndroid Build Coastguard Worker  ramdisk_format = GetRamdiskFormat(d)
967*9e94795aSAndroid Build Coastguard Worker
968*9e94795aSAndroid Build Coastguard Worker  # Tries to load the build props for all partitions with care_map, including
969*9e94795aSAndroid Build Coastguard Worker  # system and vendor.
970*9e94795aSAndroid Build Coastguard Worker  for partition in PARTITIONS_WITH_BUILD_PROP:
971*9e94795aSAndroid Build Coastguard Worker    partition_prop = "{}.build.prop".format(partition)
972*9e94795aSAndroid Build Coastguard Worker    d[partition_prop] = PartitionBuildProps.FromInputFile(
973*9e94795aSAndroid Build Coastguard Worker        input_file, partition, ramdisk_format=ramdisk_format)
974*9e94795aSAndroid Build Coastguard Worker  d["build.prop"] = d["system.build.prop"]
975*9e94795aSAndroid Build Coastguard Worker
976*9e94795aSAndroid Build Coastguard Worker  if d.get("avb_enable") == "true":
977*9e94795aSAndroid Build Coastguard Worker    build_info = BuildInfo(d, use_legacy_id=True)
978*9e94795aSAndroid Build Coastguard Worker    # Set up the salt for partitions without build.prop
979*9e94795aSAndroid Build Coastguard Worker    if build_info.fingerprint:
980*9e94795aSAndroid Build Coastguard Worker      if "fingerprint" not in d:
981*9e94795aSAndroid Build Coastguard Worker        d["fingerprint"] = build_info.fingerprint
982*9e94795aSAndroid Build Coastguard Worker      if "avb_salt" not in d:
983*9e94795aSAndroid Build Coastguard Worker        d["avb_salt"] = sha256(build_info.fingerprint.encode()).hexdigest()
984*9e94795aSAndroid Build Coastguard Worker    # Set the vbmeta digest if exists
985*9e94795aSAndroid Build Coastguard Worker    try:
986*9e94795aSAndroid Build Coastguard Worker      d["vbmeta_digest"] = read_helper("META/vbmeta_digest.txt").rstrip()
987*9e94795aSAndroid Build Coastguard Worker    except KeyError:
988*9e94795aSAndroid Build Coastguard Worker      pass
989*9e94795aSAndroid Build Coastguard Worker
990*9e94795aSAndroid Build Coastguard Worker  try:
991*9e94795aSAndroid Build Coastguard Worker    d["ab_partitions"] = read_helper("META/ab_partitions.txt").split("\n")
992*9e94795aSAndroid Build Coastguard Worker  except KeyError:
993*9e94795aSAndroid Build Coastguard Worker    logger.warning("Can't find META/ab_partitions.txt")
994*9e94795aSAndroid Build Coastguard Worker  return d
995*9e94795aSAndroid Build Coastguard Worker
996*9e94795aSAndroid Build Coastguard Worker
997*9e94795aSAndroid Build Coastguard Workerdef LoadListFromFile(file_path):
998*9e94795aSAndroid Build Coastguard Worker  with open(file_path) as f:
999*9e94795aSAndroid Build Coastguard Worker    return f.read().splitlines()
1000*9e94795aSAndroid Build Coastguard Worker
1001*9e94795aSAndroid Build Coastguard Worker
1002*9e94795aSAndroid Build Coastguard Workerdef LoadDictionaryFromFile(file_path):
1003*9e94795aSAndroid Build Coastguard Worker  lines = LoadListFromFile(file_path)
1004*9e94795aSAndroid Build Coastguard Worker  return LoadDictionaryFromLines(lines)
1005*9e94795aSAndroid Build Coastguard Worker
1006*9e94795aSAndroid Build Coastguard Worker
1007*9e94795aSAndroid Build Coastguard Workerdef LoadDictionaryFromLines(lines):
1008*9e94795aSAndroid Build Coastguard Worker  d = {}
1009*9e94795aSAndroid Build Coastguard Worker  for line in lines:
1010*9e94795aSAndroid Build Coastguard Worker    line = line.strip()
1011*9e94795aSAndroid Build Coastguard Worker    if not line or line.startswith("#"):
1012*9e94795aSAndroid Build Coastguard Worker      continue
1013*9e94795aSAndroid Build Coastguard Worker    if "=" in line:
1014*9e94795aSAndroid Build Coastguard Worker      name, value = line.split("=", 1)
1015*9e94795aSAndroid Build Coastguard Worker      d[name] = value
1016*9e94795aSAndroid Build Coastguard Worker  return d
1017*9e94795aSAndroid Build Coastguard Worker
1018*9e94795aSAndroid Build Coastguard Worker
1019*9e94795aSAndroid Build Coastguard Workerclass PartitionBuildProps(object):
1020*9e94795aSAndroid Build Coastguard Worker  """The class holds the build prop of a particular partition.
1021*9e94795aSAndroid Build Coastguard Worker
1022*9e94795aSAndroid Build Coastguard Worker  This class loads the build.prop and holds the build properties for a given
1023*9e94795aSAndroid Build Coastguard Worker  partition. It also partially recognizes the 'import' statement in the
1024*9e94795aSAndroid Build Coastguard Worker  build.prop; and calculates alternative values of some specific build
1025*9e94795aSAndroid Build Coastguard Worker  properties during runtime.
1026*9e94795aSAndroid Build Coastguard Worker
1027*9e94795aSAndroid Build Coastguard Worker  Attributes:
1028*9e94795aSAndroid Build Coastguard Worker    input_file: a zipped target-file or an unzipped target-file directory.
1029*9e94795aSAndroid Build Coastguard Worker    partition: name of the partition.
1030*9e94795aSAndroid Build Coastguard Worker    props_allow_override: a list of build properties to search for the
1031*9e94795aSAndroid Build Coastguard Worker        alternative values during runtime.
1032*9e94795aSAndroid Build Coastguard Worker    build_props: a dict of build properties for the given partition.
1033*9e94795aSAndroid Build Coastguard Worker    prop_overrides: a set of props that are overridden by import.
1034*9e94795aSAndroid Build Coastguard Worker    placeholder_values: A dict of runtime variables' values to replace the
1035*9e94795aSAndroid Build Coastguard Worker        placeholders in the build.prop file. We expect exactly one value for
1036*9e94795aSAndroid Build Coastguard Worker        each of the variables.
1037*9e94795aSAndroid Build Coastguard Worker    ramdisk_format: If name is "boot", the format of ramdisk inside the
1038*9e94795aSAndroid Build Coastguard Worker        boot image. Otherwise, its value is ignored.
1039*9e94795aSAndroid Build Coastguard Worker        Use lz4 to decompress by default. If its value is gzip, use gzip.
1040*9e94795aSAndroid Build Coastguard Worker  """
1041*9e94795aSAndroid Build Coastguard Worker
1042*9e94795aSAndroid Build Coastguard Worker  def __init__(self, input_file, name, placeholder_values=None):
1043*9e94795aSAndroid Build Coastguard Worker    self.input_file = input_file
1044*9e94795aSAndroid Build Coastguard Worker    self.partition = name
1045*9e94795aSAndroid Build Coastguard Worker    self.props_allow_override = [props.format(name) for props in [
1046*9e94795aSAndroid Build Coastguard Worker        'ro.product.{}.brand', 'ro.product.{}.name', 'ro.product.{}.device']]
1047*9e94795aSAndroid Build Coastguard Worker    self.build_props = {}
1048*9e94795aSAndroid Build Coastguard Worker    self.prop_overrides = set()
1049*9e94795aSAndroid Build Coastguard Worker    self.placeholder_values = {}
1050*9e94795aSAndroid Build Coastguard Worker    if placeholder_values:
1051*9e94795aSAndroid Build Coastguard Worker      self.placeholder_values = copy.deepcopy(placeholder_values)
1052*9e94795aSAndroid Build Coastguard Worker
1053*9e94795aSAndroid Build Coastguard Worker  @staticmethod
1054*9e94795aSAndroid Build Coastguard Worker  def FromDictionary(name, build_props):
1055*9e94795aSAndroid Build Coastguard Worker    """Constructs an instance from a build prop dictionary."""
1056*9e94795aSAndroid Build Coastguard Worker
1057*9e94795aSAndroid Build Coastguard Worker    props = PartitionBuildProps("unknown", name)
1058*9e94795aSAndroid Build Coastguard Worker    props.build_props = build_props.copy()
1059*9e94795aSAndroid Build Coastguard Worker    return props
1060*9e94795aSAndroid Build Coastguard Worker
1061*9e94795aSAndroid Build Coastguard Worker  @staticmethod
1062*9e94795aSAndroid Build Coastguard Worker  def FromInputFile(input_file, name, placeholder_values=None, ramdisk_format=RamdiskFormat.LZ4):
1063*9e94795aSAndroid Build Coastguard Worker    """Loads the build.prop file and builds the attributes."""
1064*9e94795aSAndroid Build Coastguard Worker
1065*9e94795aSAndroid Build Coastguard Worker    if name in ("boot", "init_boot"):
1066*9e94795aSAndroid Build Coastguard Worker      data = PartitionBuildProps._ReadBootPropFile(
1067*9e94795aSAndroid Build Coastguard Worker          input_file, name, ramdisk_format=ramdisk_format)
1068*9e94795aSAndroid Build Coastguard Worker    else:
1069*9e94795aSAndroid Build Coastguard Worker      data = PartitionBuildProps._ReadPartitionPropFile(input_file, name)
1070*9e94795aSAndroid Build Coastguard Worker
1071*9e94795aSAndroid Build Coastguard Worker    props = PartitionBuildProps(input_file, name, placeholder_values)
1072*9e94795aSAndroid Build Coastguard Worker    props._LoadBuildProp(data)
1073*9e94795aSAndroid Build Coastguard Worker    return props
1074*9e94795aSAndroid Build Coastguard Worker
1075*9e94795aSAndroid Build Coastguard Worker  @staticmethod
1076*9e94795aSAndroid Build Coastguard Worker  def _ReadBootPropFile(input_file, partition_name, ramdisk_format):
1077*9e94795aSAndroid Build Coastguard Worker    """
1078*9e94795aSAndroid Build Coastguard Worker    Read build.prop for boot image from input_file.
1079*9e94795aSAndroid Build Coastguard Worker    Return empty string if not found.
1080*9e94795aSAndroid Build Coastguard Worker    """
1081*9e94795aSAndroid Build Coastguard Worker    image_path = 'IMAGES/' + partition_name + '.img'
1082*9e94795aSAndroid Build Coastguard Worker    try:
1083*9e94795aSAndroid Build Coastguard Worker      boot_img = ExtractFromInputFile(input_file, image_path)
1084*9e94795aSAndroid Build Coastguard Worker    except KeyError:
1085*9e94795aSAndroid Build Coastguard Worker      logger.warning('Failed to read %s', image_path)
1086*9e94795aSAndroid Build Coastguard Worker      return ''
1087*9e94795aSAndroid Build Coastguard Worker    prop_file = GetBootImageBuildProp(boot_img, ramdisk_format=ramdisk_format)
1088*9e94795aSAndroid Build Coastguard Worker    if prop_file is None:
1089*9e94795aSAndroid Build Coastguard Worker      return ''
1090*9e94795aSAndroid Build Coastguard Worker    with open(prop_file, "r") as f:
1091*9e94795aSAndroid Build Coastguard Worker      return f.read()
1092*9e94795aSAndroid Build Coastguard Worker
1093*9e94795aSAndroid Build Coastguard Worker  @staticmethod
1094*9e94795aSAndroid Build Coastguard Worker  def _ReadPartitionPropFile(input_file, name):
1095*9e94795aSAndroid Build Coastguard Worker    """
1096*9e94795aSAndroid Build Coastguard Worker    Read build.prop for name from input_file.
1097*9e94795aSAndroid Build Coastguard Worker    Return empty string if not found.
1098*9e94795aSAndroid Build Coastguard Worker    """
1099*9e94795aSAndroid Build Coastguard Worker    data = ''
1100*9e94795aSAndroid Build Coastguard Worker    for prop_file in ['{}/etc/build.prop'.format(name.upper()),
1101*9e94795aSAndroid Build Coastguard Worker                      '{}/build.prop'.format(name.upper())]:
1102*9e94795aSAndroid Build Coastguard Worker      try:
1103*9e94795aSAndroid Build Coastguard Worker        data = ReadFromInputFile(input_file, prop_file)
1104*9e94795aSAndroid Build Coastguard Worker        break
1105*9e94795aSAndroid Build Coastguard Worker      except KeyError:
1106*9e94795aSAndroid Build Coastguard Worker        logger.warning('Failed to read %s', prop_file)
1107*9e94795aSAndroid Build Coastguard Worker    if data == '':
1108*9e94795aSAndroid Build Coastguard Worker      logger.warning("Failed to read build.prop for partition {}".format(name))
1109*9e94795aSAndroid Build Coastguard Worker    return data
1110*9e94795aSAndroid Build Coastguard Worker
1111*9e94795aSAndroid Build Coastguard Worker  @staticmethod
1112*9e94795aSAndroid Build Coastguard Worker  def FromBuildPropFile(name, build_prop_file):
1113*9e94795aSAndroid Build Coastguard Worker    """Constructs an instance from a build prop file."""
1114*9e94795aSAndroid Build Coastguard Worker
1115*9e94795aSAndroid Build Coastguard Worker    props = PartitionBuildProps("unknown", name)
1116*9e94795aSAndroid Build Coastguard Worker    with open(build_prop_file) as f:
1117*9e94795aSAndroid Build Coastguard Worker      props._LoadBuildProp(f.read())
1118*9e94795aSAndroid Build Coastguard Worker    return props
1119*9e94795aSAndroid Build Coastguard Worker
1120*9e94795aSAndroid Build Coastguard Worker  def _LoadBuildProp(self, data):
1121*9e94795aSAndroid Build Coastguard Worker    for line in data.split('\n'):
1122*9e94795aSAndroid Build Coastguard Worker      line = line.strip()
1123*9e94795aSAndroid Build Coastguard Worker      if not line or line.startswith("#"):
1124*9e94795aSAndroid Build Coastguard Worker        continue
1125*9e94795aSAndroid Build Coastguard Worker      if line.startswith("import"):
1126*9e94795aSAndroid Build Coastguard Worker        overrides = self._ImportParser(line)
1127*9e94795aSAndroid Build Coastguard Worker        duplicates = self.prop_overrides.intersection(overrides.keys())
1128*9e94795aSAndroid Build Coastguard Worker        if duplicates:
1129*9e94795aSAndroid Build Coastguard Worker          raise ValueError('prop {} is overridden multiple times'.format(
1130*9e94795aSAndroid Build Coastguard Worker              ','.join(duplicates)))
1131*9e94795aSAndroid Build Coastguard Worker        self.prop_overrides = self.prop_overrides.union(overrides.keys())
1132*9e94795aSAndroid Build Coastguard Worker        self.build_props.update(overrides)
1133*9e94795aSAndroid Build Coastguard Worker      elif "=" in line:
1134*9e94795aSAndroid Build Coastguard Worker        name, value = line.split("=", 1)
1135*9e94795aSAndroid Build Coastguard Worker        if name in self.prop_overrides:
1136*9e94795aSAndroid Build Coastguard Worker          raise ValueError('prop {} is set again after overridden by import '
1137*9e94795aSAndroid Build Coastguard Worker                           'statement'.format(name))
1138*9e94795aSAndroid Build Coastguard Worker        self.build_props[name] = value
1139*9e94795aSAndroid Build Coastguard Worker
1140*9e94795aSAndroid Build Coastguard Worker  def _ImportParser(self, line):
1141*9e94795aSAndroid Build Coastguard Worker    """Parses the build prop in a given import statement."""
1142*9e94795aSAndroid Build Coastguard Worker
1143*9e94795aSAndroid Build Coastguard Worker    tokens = line.split()
1144*9e94795aSAndroid Build Coastguard Worker    if tokens[0] != 'import' or (len(tokens) != 2 and len(tokens) != 3):
1145*9e94795aSAndroid Build Coastguard Worker      raise ValueError('Unrecognized import statement {}'.format(line))
1146*9e94795aSAndroid Build Coastguard Worker
1147*9e94795aSAndroid Build Coastguard Worker    if len(tokens) == 3:
1148*9e94795aSAndroid Build Coastguard Worker      logger.info("Import %s from %s, skip", tokens[2], tokens[1])
1149*9e94795aSAndroid Build Coastguard Worker      return {}
1150*9e94795aSAndroid Build Coastguard Worker
1151*9e94795aSAndroid Build Coastguard Worker    import_path = tokens[1]
1152*9e94795aSAndroid Build Coastguard Worker    if not re.match(r'^/{}/.*\.prop$'.format(self.partition), import_path):
1153*9e94795aSAndroid Build Coastguard Worker      logger.warn('Unrecognized import path {}'.format(line))
1154*9e94795aSAndroid Build Coastguard Worker      return {}
1155*9e94795aSAndroid Build Coastguard Worker
1156*9e94795aSAndroid Build Coastguard Worker    # We only recognize a subset of import statement that the init process
1157*9e94795aSAndroid Build Coastguard Worker    # supports. And we can loose the restriction based on how the dynamic
1158*9e94795aSAndroid Build Coastguard Worker    # fingerprint is used in practice. The placeholder format should be
1159*9e94795aSAndroid Build Coastguard Worker    # ${placeholder}, and its value should be provided by the caller through
1160*9e94795aSAndroid Build Coastguard Worker    # the placeholder_values.
1161*9e94795aSAndroid Build Coastguard Worker    for prop, value in self.placeholder_values.items():
1162*9e94795aSAndroid Build Coastguard Worker      prop_place_holder = '${{{}}}'.format(prop)
1163*9e94795aSAndroid Build Coastguard Worker      if prop_place_holder in import_path:
1164*9e94795aSAndroid Build Coastguard Worker        import_path = import_path.replace(prop_place_holder, value)
1165*9e94795aSAndroid Build Coastguard Worker    if '$' in import_path:
1166*9e94795aSAndroid Build Coastguard Worker      logger.info('Unresolved place holder in import path %s', import_path)
1167*9e94795aSAndroid Build Coastguard Worker      return {}
1168*9e94795aSAndroid Build Coastguard Worker
1169*9e94795aSAndroid Build Coastguard Worker    import_path = import_path.replace('/{}'.format(self.partition),
1170*9e94795aSAndroid Build Coastguard Worker                                      self.partition.upper())
1171*9e94795aSAndroid Build Coastguard Worker    logger.info('Parsing build props override from %s', import_path)
1172*9e94795aSAndroid Build Coastguard Worker
1173*9e94795aSAndroid Build Coastguard Worker    lines = ReadFromInputFile(self.input_file, import_path).split('\n')
1174*9e94795aSAndroid Build Coastguard Worker    d = LoadDictionaryFromLines(lines)
1175*9e94795aSAndroid Build Coastguard Worker    return {key: val for key, val in d.items()
1176*9e94795aSAndroid Build Coastguard Worker            if key in self.props_allow_override}
1177*9e94795aSAndroid Build Coastguard Worker
1178*9e94795aSAndroid Build Coastguard Worker  def __getstate__(self):
1179*9e94795aSAndroid Build Coastguard Worker    state = self.__dict__.copy()
1180*9e94795aSAndroid Build Coastguard Worker    # Don't pickle baz
1181*9e94795aSAndroid Build Coastguard Worker    if "input_file" in state and isinstance(state["input_file"], zipfile.ZipFile):
1182*9e94795aSAndroid Build Coastguard Worker      state["input_file"] = state["input_file"].filename
1183*9e94795aSAndroid Build Coastguard Worker    return state
1184*9e94795aSAndroid Build Coastguard Worker
1185*9e94795aSAndroid Build Coastguard Worker  def GetProp(self, prop):
1186*9e94795aSAndroid Build Coastguard Worker    return self.build_props.get(prop)
1187*9e94795aSAndroid Build Coastguard Worker
1188*9e94795aSAndroid Build Coastguard Worker
1189*9e94795aSAndroid Build Coastguard Workerdef LoadRecoveryFSTab(read_helper, fstab_version, recovery_fstab_path):
1190*9e94795aSAndroid Build Coastguard Worker  class Partition(object):
1191*9e94795aSAndroid Build Coastguard Worker    def __init__(self, mount_point, fs_type, device, length, context, slotselect):
1192*9e94795aSAndroid Build Coastguard Worker      self.mount_point = mount_point
1193*9e94795aSAndroid Build Coastguard Worker      self.fs_type = fs_type
1194*9e94795aSAndroid Build Coastguard Worker      self.device = device
1195*9e94795aSAndroid Build Coastguard Worker      self.length = length
1196*9e94795aSAndroid Build Coastguard Worker      self.context = context
1197*9e94795aSAndroid Build Coastguard Worker      self.slotselect = slotselect
1198*9e94795aSAndroid Build Coastguard Worker
1199*9e94795aSAndroid Build Coastguard Worker  try:
1200*9e94795aSAndroid Build Coastguard Worker    data = read_helper(recovery_fstab_path)
1201*9e94795aSAndroid Build Coastguard Worker  except KeyError:
1202*9e94795aSAndroid Build Coastguard Worker    logger.warning("Failed to find %s", recovery_fstab_path)
1203*9e94795aSAndroid Build Coastguard Worker    data = ""
1204*9e94795aSAndroid Build Coastguard Worker
1205*9e94795aSAndroid Build Coastguard Worker  assert fstab_version == 2
1206*9e94795aSAndroid Build Coastguard Worker
1207*9e94795aSAndroid Build Coastguard Worker  d = {}
1208*9e94795aSAndroid Build Coastguard Worker  for line in data.split("\n"):
1209*9e94795aSAndroid Build Coastguard Worker    line = line.strip()
1210*9e94795aSAndroid Build Coastguard Worker    if not line or line.startswith("#"):
1211*9e94795aSAndroid Build Coastguard Worker      continue
1212*9e94795aSAndroid Build Coastguard Worker
1213*9e94795aSAndroid Build Coastguard Worker    # <src> <mnt_point> <type> <mnt_flags and options> <fs_mgr_flags>
1214*9e94795aSAndroid Build Coastguard Worker    pieces = line.split()
1215*9e94795aSAndroid Build Coastguard Worker    if len(pieces) != 5:
1216*9e94795aSAndroid Build Coastguard Worker      raise ValueError("malformed recovery.fstab line: \"%s\"" % (line,))
1217*9e94795aSAndroid Build Coastguard Worker
1218*9e94795aSAndroid Build Coastguard Worker    # Ignore entries that are managed by vold.
1219*9e94795aSAndroid Build Coastguard Worker    options = pieces[4]
1220*9e94795aSAndroid Build Coastguard Worker    if "voldmanaged=" in options:
1221*9e94795aSAndroid Build Coastguard Worker      continue
1222*9e94795aSAndroid Build Coastguard Worker
1223*9e94795aSAndroid Build Coastguard Worker    # It's a good line, parse it.
1224*9e94795aSAndroid Build Coastguard Worker    length = 0
1225*9e94795aSAndroid Build Coastguard Worker    slotselect = False
1226*9e94795aSAndroid Build Coastguard Worker    options = options.split(",")
1227*9e94795aSAndroid Build Coastguard Worker    for i in options:
1228*9e94795aSAndroid Build Coastguard Worker      if i.startswith("length="):
1229*9e94795aSAndroid Build Coastguard Worker        length = int(i[7:])
1230*9e94795aSAndroid Build Coastguard Worker      elif i == "slotselect":
1231*9e94795aSAndroid Build Coastguard Worker        slotselect = True
1232*9e94795aSAndroid Build Coastguard Worker      else:
1233*9e94795aSAndroid Build Coastguard Worker        # Ignore all unknown options in the unified fstab.
1234*9e94795aSAndroid Build Coastguard Worker        continue
1235*9e94795aSAndroid Build Coastguard Worker
1236*9e94795aSAndroid Build Coastguard Worker    mount_flags = pieces[3]
1237*9e94795aSAndroid Build Coastguard Worker    # Honor the SELinux context if present.
1238*9e94795aSAndroid Build Coastguard Worker    context = None
1239*9e94795aSAndroid Build Coastguard Worker    for i in mount_flags.split(","):
1240*9e94795aSAndroid Build Coastguard Worker      if i.startswith("context="):
1241*9e94795aSAndroid Build Coastguard Worker        context = i
1242*9e94795aSAndroid Build Coastguard Worker
1243*9e94795aSAndroid Build Coastguard Worker    mount_point = pieces[1]
1244*9e94795aSAndroid Build Coastguard Worker    d[mount_point] = Partition(mount_point=mount_point, fs_type=pieces[2],
1245*9e94795aSAndroid Build Coastguard Worker                               device=pieces[0], length=length, context=context,
1246*9e94795aSAndroid Build Coastguard Worker                               slotselect=slotselect)
1247*9e94795aSAndroid Build Coastguard Worker
1248*9e94795aSAndroid Build Coastguard Worker  return d
1249*9e94795aSAndroid Build Coastguard Worker
1250*9e94795aSAndroid Build Coastguard Worker
1251*9e94795aSAndroid Build Coastguard Workerdef _FindAndLoadRecoveryFstab(info_dict, input_file, read_helper):
1252*9e94795aSAndroid Build Coastguard Worker  """Finds the path to recovery fstab and loads its contents."""
1253*9e94795aSAndroid Build Coastguard Worker  # recovery fstab is only meaningful when installing an update via recovery
1254*9e94795aSAndroid Build Coastguard Worker  # (i.e. non-A/B OTA). Skip loading fstab if device used A/B OTA.
1255*9e94795aSAndroid Build Coastguard Worker  if info_dict.get('ab_update') == 'true' and \
1256*9e94795aSAndroid Build Coastguard Worker     info_dict.get("allow_non_ab") != "true":
1257*9e94795aSAndroid Build Coastguard Worker    return None
1258*9e94795aSAndroid Build Coastguard Worker
1259*9e94795aSAndroid Build Coastguard Worker  # We changed recovery.fstab path in Q, from ../RAMDISK/etc/recovery.fstab to
1260*9e94795aSAndroid Build Coastguard Worker  # ../RAMDISK/system/etc/recovery.fstab. This function has to handle both
1261*9e94795aSAndroid Build Coastguard Worker  # cases, since it may load the info_dict from an old build (e.g. when
1262*9e94795aSAndroid Build Coastguard Worker  # generating incremental OTAs from that build).
1263*9e94795aSAndroid Build Coastguard Worker  if info_dict.get('no_recovery') != 'true':
1264*9e94795aSAndroid Build Coastguard Worker    recovery_fstab_path = 'RECOVERY/RAMDISK/system/etc/recovery.fstab'
1265*9e94795aSAndroid Build Coastguard Worker    if not DoesInputFileContain(input_file, recovery_fstab_path):
1266*9e94795aSAndroid Build Coastguard Worker      recovery_fstab_path = 'RECOVERY/RAMDISK/etc/recovery.fstab'
1267*9e94795aSAndroid Build Coastguard Worker    return LoadRecoveryFSTab(
1268*9e94795aSAndroid Build Coastguard Worker        read_helper, info_dict['fstab_version'], recovery_fstab_path)
1269*9e94795aSAndroid Build Coastguard Worker
1270*9e94795aSAndroid Build Coastguard Worker  if info_dict.get('recovery_as_boot') == 'true':
1271*9e94795aSAndroid Build Coastguard Worker    recovery_fstab_path = 'BOOT/RAMDISK/system/etc/recovery.fstab'
1272*9e94795aSAndroid Build Coastguard Worker    if not DoesInputFileContain(input_file, recovery_fstab_path):
1273*9e94795aSAndroid Build Coastguard Worker      recovery_fstab_path = 'BOOT/RAMDISK/etc/recovery.fstab'
1274*9e94795aSAndroid Build Coastguard Worker    return LoadRecoveryFSTab(
1275*9e94795aSAndroid Build Coastguard Worker        read_helper, info_dict['fstab_version'], recovery_fstab_path)
1276*9e94795aSAndroid Build Coastguard Worker
1277*9e94795aSAndroid Build Coastguard Worker  return None
1278*9e94795aSAndroid Build Coastguard Worker
1279*9e94795aSAndroid Build Coastguard Worker
1280*9e94795aSAndroid Build Coastguard Workerdef DumpInfoDict(d):
1281*9e94795aSAndroid Build Coastguard Worker  for k, v in sorted(d.items()):
1282*9e94795aSAndroid Build Coastguard Worker    logger.info("%-25s = (%s) %s", k, type(v).__name__, v)
1283*9e94795aSAndroid Build Coastguard Worker
1284*9e94795aSAndroid Build Coastguard Worker
1285*9e94795aSAndroid Build Coastguard Workerdef MergeDynamicPartitionInfoDicts(framework_dict, vendor_dict):
1286*9e94795aSAndroid Build Coastguard Worker  """Merges dynamic partition info variables.
1287*9e94795aSAndroid Build Coastguard Worker
1288*9e94795aSAndroid Build Coastguard Worker  Args:
1289*9e94795aSAndroid Build Coastguard Worker    framework_dict: The dictionary of dynamic partition info variables from the
1290*9e94795aSAndroid Build Coastguard Worker      partial framework target files.
1291*9e94795aSAndroid Build Coastguard Worker    vendor_dict: The dictionary of dynamic partition info variables from the
1292*9e94795aSAndroid Build Coastguard Worker      partial vendor target files.
1293*9e94795aSAndroid Build Coastguard Worker
1294*9e94795aSAndroid Build Coastguard Worker  Returns:
1295*9e94795aSAndroid Build Coastguard Worker    The merged dynamic partition info dictionary.
1296*9e94795aSAndroid Build Coastguard Worker  """
1297*9e94795aSAndroid Build Coastguard Worker
1298*9e94795aSAndroid Build Coastguard Worker  def uniq_concat(a, b):
1299*9e94795aSAndroid Build Coastguard Worker    combined = set(a.split())
1300*9e94795aSAndroid Build Coastguard Worker    combined.update(set(b.split()))
1301*9e94795aSAndroid Build Coastguard Worker    combined = [item.strip() for item in combined if item.strip()]
1302*9e94795aSAndroid Build Coastguard Worker    return " ".join(sorted(combined))
1303*9e94795aSAndroid Build Coastguard Worker
1304*9e94795aSAndroid Build Coastguard Worker  if (framework_dict.get("use_dynamic_partitions") !=
1305*9e94795aSAndroid Build Coastguard Worker          "true") or (vendor_dict.get("use_dynamic_partitions") != "true"):
1306*9e94795aSAndroid Build Coastguard Worker    raise ValueError("Both dictionaries must have use_dynamic_partitions=true")
1307*9e94795aSAndroid Build Coastguard Worker
1308*9e94795aSAndroid Build Coastguard Worker  merged_dict = {"use_dynamic_partitions": "true"}
1309*9e94795aSAndroid Build Coastguard Worker  # For keys-value pairs that are the same, copy to merged dict
1310*9e94795aSAndroid Build Coastguard Worker  for key in vendor_dict.keys():
1311*9e94795aSAndroid Build Coastguard Worker    if key in framework_dict and framework_dict[key] == vendor_dict[key]:
1312*9e94795aSAndroid Build Coastguard Worker      merged_dict[key] = vendor_dict[key]
1313*9e94795aSAndroid Build Coastguard Worker
1314*9e94795aSAndroid Build Coastguard Worker  merged_dict["dynamic_partition_list"] = uniq_concat(
1315*9e94795aSAndroid Build Coastguard Worker      framework_dict.get("dynamic_partition_list", ""),
1316*9e94795aSAndroid Build Coastguard Worker      vendor_dict.get("dynamic_partition_list", ""))
1317*9e94795aSAndroid Build Coastguard Worker
1318*9e94795aSAndroid Build Coastguard Worker  # Super block devices are defined by the vendor dict.
1319*9e94795aSAndroid Build Coastguard Worker  if "super_block_devices" in vendor_dict:
1320*9e94795aSAndroid Build Coastguard Worker    merged_dict["super_block_devices"] = vendor_dict["super_block_devices"]
1321*9e94795aSAndroid Build Coastguard Worker    for block_device in merged_dict["super_block_devices"].split():
1322*9e94795aSAndroid Build Coastguard Worker      key = "super_%s_device_size" % block_device
1323*9e94795aSAndroid Build Coastguard Worker      if key not in vendor_dict:
1324*9e94795aSAndroid Build Coastguard Worker        raise ValueError("Vendor dict does not contain required key %s." % key)
1325*9e94795aSAndroid Build Coastguard Worker      merged_dict[key] = vendor_dict[key]
1326*9e94795aSAndroid Build Coastguard Worker
1327*9e94795aSAndroid Build Coastguard Worker  # Partition groups and group sizes are defined by the vendor dict because
1328*9e94795aSAndroid Build Coastguard Worker  # these values may vary for each board that uses a shared system image.
1329*9e94795aSAndroid Build Coastguard Worker  merged_dict["super_partition_groups"] = vendor_dict["super_partition_groups"]
1330*9e94795aSAndroid Build Coastguard Worker  for partition_group in merged_dict["super_partition_groups"].split():
1331*9e94795aSAndroid Build Coastguard Worker    # Set the partition group's size using the value from the vendor dict.
1332*9e94795aSAndroid Build Coastguard Worker    key = "super_%s_group_size" % partition_group
1333*9e94795aSAndroid Build Coastguard Worker    if key not in vendor_dict:
1334*9e94795aSAndroid Build Coastguard Worker      raise ValueError("Vendor dict does not contain required key %s." % key)
1335*9e94795aSAndroid Build Coastguard Worker    merged_dict[key] = vendor_dict[key]
1336*9e94795aSAndroid Build Coastguard Worker
1337*9e94795aSAndroid Build Coastguard Worker    # Set the partition group's partition list using a concatenation of the
1338*9e94795aSAndroid Build Coastguard Worker    # framework and vendor partition lists.
1339*9e94795aSAndroid Build Coastguard Worker    key = "super_%s_partition_list" % partition_group
1340*9e94795aSAndroid Build Coastguard Worker    merged_dict[key] = uniq_concat(
1341*9e94795aSAndroid Build Coastguard Worker        framework_dict.get(key, ""), vendor_dict.get(key, ""))
1342*9e94795aSAndroid Build Coastguard Worker  # in the case that vendor is on s build, but is taking a v3 -> v3 vabc ota, we want to fallback to v2
1343*9e94795aSAndroid Build Coastguard Worker  if "vabc_cow_version" not in vendor_dict or "vabc_cow_version" not in framework_dict:
1344*9e94795aSAndroid Build Coastguard Worker    merged_dict["vabc_cow_version"] = '2'
1345*9e94795aSAndroid Build Coastguard Worker  else:
1346*9e94795aSAndroid Build Coastguard Worker    merged_dict["vabc_cow_version"] = min(vendor_dict["vabc_cow_version"], framework_dict["vabc_cow_version"])
1347*9e94795aSAndroid Build Coastguard Worker  # Various other flags should be copied from the vendor dict, if defined.
1348*9e94795aSAndroid Build Coastguard Worker  for key in ("virtual_ab", "virtual_ab_retrofit", "lpmake",
1349*9e94795aSAndroid Build Coastguard Worker              "super_metadata_device", "super_partition_error_limit",
1350*9e94795aSAndroid Build Coastguard Worker              "super_partition_size"):
1351*9e94795aSAndroid Build Coastguard Worker    if key in vendor_dict.keys():
1352*9e94795aSAndroid Build Coastguard Worker      merged_dict[key] = vendor_dict[key]
1353*9e94795aSAndroid Build Coastguard Worker
1354*9e94795aSAndroid Build Coastguard Worker  return merged_dict
1355*9e94795aSAndroid Build Coastguard Worker
1356*9e94795aSAndroid Build Coastguard Worker
1357*9e94795aSAndroid Build Coastguard Workerdef PartitionMapFromTargetFiles(target_files_dir):
1358*9e94795aSAndroid Build Coastguard Worker  """Builds a map from partition -> path within an extracted target files directory."""
1359*9e94795aSAndroid Build Coastguard Worker  # Keep possible_subdirs in sync with build/make/core/board_config.mk.
1360*9e94795aSAndroid Build Coastguard Worker  possible_subdirs = {
1361*9e94795aSAndroid Build Coastguard Worker      "system": ["SYSTEM"],
1362*9e94795aSAndroid Build Coastguard Worker      "vendor": ["VENDOR", "SYSTEM/vendor"],
1363*9e94795aSAndroid Build Coastguard Worker      "product": ["PRODUCT", "SYSTEM/product"],
1364*9e94795aSAndroid Build Coastguard Worker      "system_ext": ["SYSTEM_EXT", "SYSTEM/system_ext"],
1365*9e94795aSAndroid Build Coastguard Worker      "odm": ["ODM", "VENDOR/odm", "SYSTEM/vendor/odm"],
1366*9e94795aSAndroid Build Coastguard Worker      "vendor_dlkm": [
1367*9e94795aSAndroid Build Coastguard Worker          "VENDOR_DLKM", "VENDOR/vendor_dlkm", "SYSTEM/vendor/vendor_dlkm"
1368*9e94795aSAndroid Build Coastguard Worker      ],
1369*9e94795aSAndroid Build Coastguard Worker      "odm_dlkm": ["ODM_DLKM", "VENDOR/odm_dlkm", "SYSTEM/vendor/odm_dlkm"],
1370*9e94795aSAndroid Build Coastguard Worker      "system_dlkm": ["SYSTEM_DLKM", "SYSTEM/system_dlkm"],
1371*9e94795aSAndroid Build Coastguard Worker  }
1372*9e94795aSAndroid Build Coastguard Worker  partition_map = {}
1373*9e94795aSAndroid Build Coastguard Worker  for partition, subdirs in possible_subdirs.items():
1374*9e94795aSAndroid Build Coastguard Worker    for subdir in subdirs:
1375*9e94795aSAndroid Build Coastguard Worker      if os.path.exists(os.path.join(target_files_dir, subdir)):
1376*9e94795aSAndroid Build Coastguard Worker        partition_map[partition] = subdir
1377*9e94795aSAndroid Build Coastguard Worker        break
1378*9e94795aSAndroid Build Coastguard Worker  return partition_map
1379*9e94795aSAndroid Build Coastguard Worker
1380*9e94795aSAndroid Build Coastguard Worker
1381*9e94795aSAndroid Build Coastguard Workerdef SharedUidPartitionViolations(uid_dict, partition_groups):
1382*9e94795aSAndroid Build Coastguard Worker  """Checks for APK sharedUserIds that cross partition group boundaries.
1383*9e94795aSAndroid Build Coastguard Worker
1384*9e94795aSAndroid Build Coastguard Worker  This uses a single or merged build's shareduid_violation_modules.json
1385*9e94795aSAndroid Build Coastguard Worker  output file, as generated by find_shareduid_violation.py or
1386*9e94795aSAndroid Build Coastguard Worker  core/tasks/find-shareduid-violation.mk.
1387*9e94795aSAndroid Build Coastguard Worker
1388*9e94795aSAndroid Build Coastguard Worker  An error is defined as a sharedUserId that is found in a set of partitions
1389*9e94795aSAndroid Build Coastguard Worker  that span more than one partition group.
1390*9e94795aSAndroid Build Coastguard Worker
1391*9e94795aSAndroid Build Coastguard Worker  Args:
1392*9e94795aSAndroid Build Coastguard Worker    uid_dict: A dictionary created by using the standard json module to read a
1393*9e94795aSAndroid Build Coastguard Worker      complete shareduid_violation_modules.json file.
1394*9e94795aSAndroid Build Coastguard Worker    partition_groups: A list of groups, where each group is a list of
1395*9e94795aSAndroid Build Coastguard Worker      partitions.
1396*9e94795aSAndroid Build Coastguard Worker
1397*9e94795aSAndroid Build Coastguard Worker  Returns:
1398*9e94795aSAndroid Build Coastguard Worker    A list of error messages.
1399*9e94795aSAndroid Build Coastguard Worker  """
1400*9e94795aSAndroid Build Coastguard Worker  errors = []
1401*9e94795aSAndroid Build Coastguard Worker  for uid, partitions in uid_dict.items():
1402*9e94795aSAndroid Build Coastguard Worker    found_in_groups = [
1403*9e94795aSAndroid Build Coastguard Worker        group for group in partition_groups
1404*9e94795aSAndroid Build Coastguard Worker        if set(partitions.keys()) & set(group)
1405*9e94795aSAndroid Build Coastguard Worker    ]
1406*9e94795aSAndroid Build Coastguard Worker    if len(found_in_groups) > 1:
1407*9e94795aSAndroid Build Coastguard Worker      errors.append(
1408*9e94795aSAndroid Build Coastguard Worker          "APK sharedUserId \"%s\" found across partition groups in partitions \"%s\""
1409*9e94795aSAndroid Build Coastguard Worker          % (uid, ",".join(sorted(partitions.keys()))))
1410*9e94795aSAndroid Build Coastguard Worker  return errors
1411*9e94795aSAndroid Build Coastguard Worker
1412*9e94795aSAndroid Build Coastguard Worker
1413*9e94795aSAndroid Build Coastguard Workerdef RunHostInitVerifier(product_out, partition_map):
1414*9e94795aSAndroid Build Coastguard Worker  """Runs host_init_verifier on the init rc files within partitions.
1415*9e94795aSAndroid Build Coastguard Worker
1416*9e94795aSAndroid Build Coastguard Worker  host_init_verifier searches the etc/init path within each partition.
1417*9e94795aSAndroid Build Coastguard Worker
1418*9e94795aSAndroid Build Coastguard Worker  Args:
1419*9e94795aSAndroid Build Coastguard Worker    product_out: PRODUCT_OUT directory, containing partition directories.
1420*9e94795aSAndroid Build Coastguard Worker    partition_map: A map of partition name -> relative path within product_out.
1421*9e94795aSAndroid Build Coastguard Worker  """
1422*9e94795aSAndroid Build Coastguard Worker  allowed_partitions = ("system", "system_ext", "product", "vendor", "odm")
1423*9e94795aSAndroid Build Coastguard Worker  cmd = ["host_init_verifier"]
1424*9e94795aSAndroid Build Coastguard Worker  for partition, path in partition_map.items():
1425*9e94795aSAndroid Build Coastguard Worker    if partition not in allowed_partitions:
1426*9e94795aSAndroid Build Coastguard Worker      raise ExternalError("Unable to call host_init_verifier for partition %s" %
1427*9e94795aSAndroid Build Coastguard Worker                          partition)
1428*9e94795aSAndroid Build Coastguard Worker    cmd.extend(["--out_%s" % partition, os.path.join(product_out, path)])
1429*9e94795aSAndroid Build Coastguard Worker    # Add --property-contexts if the file exists on the partition.
1430*9e94795aSAndroid Build Coastguard Worker    property_contexts = "%s_property_contexts" % (
1431*9e94795aSAndroid Build Coastguard Worker        "plat" if partition == "system" else partition)
1432*9e94795aSAndroid Build Coastguard Worker    property_contexts_path = os.path.join(product_out, path, "etc", "selinux",
1433*9e94795aSAndroid Build Coastguard Worker                                          property_contexts)
1434*9e94795aSAndroid Build Coastguard Worker    if os.path.exists(property_contexts_path):
1435*9e94795aSAndroid Build Coastguard Worker      cmd.append("--property-contexts=%s" % property_contexts_path)
1436*9e94795aSAndroid Build Coastguard Worker    # Add the passwd file if the file exists on the partition.
1437*9e94795aSAndroid Build Coastguard Worker    passwd_path = os.path.join(product_out, path, "etc", "passwd")
1438*9e94795aSAndroid Build Coastguard Worker    if os.path.exists(passwd_path):
1439*9e94795aSAndroid Build Coastguard Worker      cmd.extend(["-p", passwd_path])
1440*9e94795aSAndroid Build Coastguard Worker  return RunAndCheckOutput(cmd)
1441*9e94795aSAndroid Build Coastguard Worker
1442*9e94795aSAndroid Build Coastguard Worker
1443*9e94795aSAndroid Build Coastguard Workerdef AppendAVBSigningArgs(cmd, partition, avb_salt=None):
1444*9e94795aSAndroid Build Coastguard Worker  """Append signing arguments for avbtool."""
1445*9e94795aSAndroid Build Coastguard Worker  # e.g., "--key path/to/signing_key --algorithm SHA256_RSA4096"
1446*9e94795aSAndroid Build Coastguard Worker  key_path = ResolveAVBSigningPathArgs(
1447*9e94795aSAndroid Build Coastguard Worker      OPTIONS.info_dict.get("avb_" + partition + "_key_path"))
1448*9e94795aSAndroid Build Coastguard Worker  algorithm = OPTIONS.info_dict.get("avb_" + partition + "_algorithm")
1449*9e94795aSAndroid Build Coastguard Worker  if key_path and algorithm:
1450*9e94795aSAndroid Build Coastguard Worker    cmd.extend(["--key", key_path, "--algorithm", algorithm])
1451*9e94795aSAndroid Build Coastguard Worker  if avb_salt is None:
1452*9e94795aSAndroid Build Coastguard Worker    avb_salt = OPTIONS.info_dict.get("avb_salt")
1453*9e94795aSAndroid Build Coastguard Worker  # make_vbmeta_image doesn't like "--salt" (and it's not needed).
1454*9e94795aSAndroid Build Coastguard Worker  if avb_salt and not partition.startswith("vbmeta"):
1455*9e94795aSAndroid Build Coastguard Worker    cmd.extend(["--salt", avb_salt])
1456*9e94795aSAndroid Build Coastguard Worker
1457*9e94795aSAndroid Build Coastguard Worker
1458*9e94795aSAndroid Build Coastguard Workerdef ResolveAVBSigningPathArgs(split_args):
1459*9e94795aSAndroid Build Coastguard Worker
1460*9e94795aSAndroid Build Coastguard Worker  def ResolveBinaryPath(path):
1461*9e94795aSAndroid Build Coastguard Worker    if os.path.exists(path):
1462*9e94795aSAndroid Build Coastguard Worker      return path
1463*9e94795aSAndroid Build Coastguard Worker    if OPTIONS.search_path:
1464*9e94795aSAndroid Build Coastguard Worker      new_path = os.path.join(OPTIONS.search_path, path)
1465*9e94795aSAndroid Build Coastguard Worker      if os.path.exists(new_path):
1466*9e94795aSAndroid Build Coastguard Worker        return new_path
1467*9e94795aSAndroid Build Coastguard Worker    raise ExternalError(
1468*9e94795aSAndroid Build Coastguard Worker        "Failed to find {}".format(path))
1469*9e94795aSAndroid Build Coastguard Worker
1470*9e94795aSAndroid Build Coastguard Worker  if not split_args:
1471*9e94795aSAndroid Build Coastguard Worker    return split_args
1472*9e94795aSAndroid Build Coastguard Worker
1473*9e94795aSAndroid Build Coastguard Worker  if isinstance(split_args, list):
1474*9e94795aSAndroid Build Coastguard Worker    for index, arg in enumerate(split_args[:-1]):
1475*9e94795aSAndroid Build Coastguard Worker      if arg == '--signing_helper':
1476*9e94795aSAndroid Build Coastguard Worker        signing_helper_path = split_args[index + 1]
1477*9e94795aSAndroid Build Coastguard Worker        split_args[index + 1] = ResolveBinaryPath(signing_helper_path)
1478*9e94795aSAndroid Build Coastguard Worker        break
1479*9e94795aSAndroid Build Coastguard Worker  elif isinstance(split_args, str):
1480*9e94795aSAndroid Build Coastguard Worker    split_args = ResolveBinaryPath(split_args)
1481*9e94795aSAndroid Build Coastguard Worker
1482*9e94795aSAndroid Build Coastguard Worker  return split_args
1483*9e94795aSAndroid Build Coastguard Worker
1484*9e94795aSAndroid Build Coastguard Worker
1485*9e94795aSAndroid Build Coastguard Workerdef GetAvbPartitionArg(partition, image, info_dict=None):
1486*9e94795aSAndroid Build Coastguard Worker  """Returns the VBMeta arguments for one partition.
1487*9e94795aSAndroid Build Coastguard Worker
1488*9e94795aSAndroid Build Coastguard Worker  It sets up the VBMeta argument by including the partition descriptor from the
1489*9e94795aSAndroid Build Coastguard Worker  given 'image', or by configuring the partition as a chained partition.
1490*9e94795aSAndroid Build Coastguard Worker
1491*9e94795aSAndroid Build Coastguard Worker  Args:
1492*9e94795aSAndroid Build Coastguard Worker    partition: The name of the partition (e.g. "system").
1493*9e94795aSAndroid Build Coastguard Worker    image: The path to the partition image.
1494*9e94795aSAndroid Build Coastguard Worker    info_dict: A dict returned by common.LoadInfoDict(). Will use
1495*9e94795aSAndroid Build Coastguard Worker        OPTIONS.info_dict if None has been given.
1496*9e94795aSAndroid Build Coastguard Worker
1497*9e94795aSAndroid Build Coastguard Worker  Returns:
1498*9e94795aSAndroid Build Coastguard Worker    A list of VBMeta arguments for one partition.
1499*9e94795aSAndroid Build Coastguard Worker  """
1500*9e94795aSAndroid Build Coastguard Worker  if info_dict is None:
1501*9e94795aSAndroid Build Coastguard Worker    info_dict = OPTIONS.info_dict
1502*9e94795aSAndroid Build Coastguard Worker
1503*9e94795aSAndroid Build Coastguard Worker  # Check if chain partition is used.
1504*9e94795aSAndroid Build Coastguard Worker  key_path = info_dict.get("avb_" + partition + "_key_path")
1505*9e94795aSAndroid Build Coastguard Worker  if not key_path:
1506*9e94795aSAndroid Build Coastguard Worker    return [AVB_ARG_NAME_INCLUDE_DESC_FROM_IMG, image]
1507*9e94795aSAndroid Build Coastguard Worker
1508*9e94795aSAndroid Build Coastguard Worker  # For a non-A/B device, we don't chain /recovery nor include its descriptor
1509*9e94795aSAndroid Build Coastguard Worker  # into vbmeta.img. The recovery image will be configured on an independent
1510*9e94795aSAndroid Build Coastguard Worker  # boot chain, to be verified with AVB_SLOT_VERIFY_FLAGS_NO_VBMETA_PARTITION.
1511*9e94795aSAndroid Build Coastguard Worker  # See details at
1512*9e94795aSAndroid Build Coastguard Worker  # https://android.googlesource.com/platform/external/avb/+/master/README.md#booting-into-recovery.
1513*9e94795aSAndroid Build Coastguard Worker  if info_dict.get("ab_update") != "true" and partition == "recovery":
1514*9e94795aSAndroid Build Coastguard Worker    return []
1515*9e94795aSAndroid Build Coastguard Worker
1516*9e94795aSAndroid Build Coastguard Worker  # Otherwise chain the partition into vbmeta.
1517*9e94795aSAndroid Build Coastguard Worker  chained_partition_arg = GetAvbChainedPartitionArg(partition, info_dict)
1518*9e94795aSAndroid Build Coastguard Worker  return [AVB_ARG_NAME_CHAIN_PARTITION, chained_partition_arg]
1519*9e94795aSAndroid Build Coastguard Worker
1520*9e94795aSAndroid Build Coastguard Worker
1521*9e94795aSAndroid Build Coastguard Workerdef GetAvbPartitionsArg(partitions,
1522*9e94795aSAndroid Build Coastguard Worker                        resolve_rollback_index_location_conflict=False,
1523*9e94795aSAndroid Build Coastguard Worker                        info_dict=None):
1524*9e94795aSAndroid Build Coastguard Worker  """Returns the VBMeta arguments for all AVB partitions.
1525*9e94795aSAndroid Build Coastguard Worker
1526*9e94795aSAndroid Build Coastguard Worker  It sets up the VBMeta argument by calling GetAvbPartitionArg of all
1527*9e94795aSAndroid Build Coastguard Worker  partitions.
1528*9e94795aSAndroid Build Coastguard Worker
1529*9e94795aSAndroid Build Coastguard Worker  Args:
1530*9e94795aSAndroid Build Coastguard Worker    partitions: A dict of all AVB partitions.
1531*9e94795aSAndroid Build Coastguard Worker    resolve_rollback_index_location_conflict: If true, resolve conflicting avb
1532*9e94795aSAndroid Build Coastguard Worker        rollback index locations by assigning the smallest unused value.
1533*9e94795aSAndroid Build Coastguard Worker    info_dict: A dict returned by common.LoadInfoDict().
1534*9e94795aSAndroid Build Coastguard Worker
1535*9e94795aSAndroid Build Coastguard Worker  Returns:
1536*9e94795aSAndroid Build Coastguard Worker    A list of VBMeta arguments for all partitions.
1537*9e94795aSAndroid Build Coastguard Worker  """
1538*9e94795aSAndroid Build Coastguard Worker  # An AVB partition will be linked into a vbmeta partition by either
1539*9e94795aSAndroid Build Coastguard Worker  # AVB_ARG_NAME_INCLUDE_DESC_FROM_IMG or AVB_ARG_NAME_CHAIN_PARTITION, there
1540*9e94795aSAndroid Build Coastguard Worker  # should be no other cases.
1541*9e94795aSAndroid Build Coastguard Worker  valid_args = {
1542*9e94795aSAndroid Build Coastguard Worker      AVB_ARG_NAME_INCLUDE_DESC_FROM_IMG: [],
1543*9e94795aSAndroid Build Coastguard Worker      AVB_ARG_NAME_CHAIN_PARTITION: []
1544*9e94795aSAndroid Build Coastguard Worker  }
1545*9e94795aSAndroid Build Coastguard Worker
1546*9e94795aSAndroid Build Coastguard Worker  for partition, path in sorted(partitions.items()):
1547*9e94795aSAndroid Build Coastguard Worker    avb_partition_arg = GetAvbPartitionArg(partition, path, info_dict)
1548*9e94795aSAndroid Build Coastguard Worker    if not avb_partition_arg:
1549*9e94795aSAndroid Build Coastguard Worker      continue
1550*9e94795aSAndroid Build Coastguard Worker    arg_name, arg_value = avb_partition_arg
1551*9e94795aSAndroid Build Coastguard Worker    assert arg_name in valid_args
1552*9e94795aSAndroid Build Coastguard Worker    valid_args[arg_name].append(arg_value)
1553*9e94795aSAndroid Build Coastguard Worker
1554*9e94795aSAndroid Build Coastguard Worker  # Copy the arguments for non-chained AVB partitions directly without
1555*9e94795aSAndroid Build Coastguard Worker  # intervention.
1556*9e94795aSAndroid Build Coastguard Worker  avb_args = []
1557*9e94795aSAndroid Build Coastguard Worker  for image in valid_args[AVB_ARG_NAME_INCLUDE_DESC_FROM_IMG]:
1558*9e94795aSAndroid Build Coastguard Worker    avb_args.extend([AVB_ARG_NAME_INCLUDE_DESC_FROM_IMG, image])
1559*9e94795aSAndroid Build Coastguard Worker
1560*9e94795aSAndroid Build Coastguard Worker  # Handle chained AVB partitions. The rollback index location might be
1561*9e94795aSAndroid Build Coastguard Worker  # adjusted if two partitions use the same value. This may happen when mixing
1562*9e94795aSAndroid Build Coastguard Worker  # a shared system image with other vendor images.
1563*9e94795aSAndroid Build Coastguard Worker  used_index_loc = set()
1564*9e94795aSAndroid Build Coastguard Worker  for chained_partition_arg in valid_args[AVB_ARG_NAME_CHAIN_PARTITION]:
1565*9e94795aSAndroid Build Coastguard Worker    if resolve_rollback_index_location_conflict:
1566*9e94795aSAndroid Build Coastguard Worker      while chained_partition_arg.rollback_index_location in used_index_loc:
1567*9e94795aSAndroid Build Coastguard Worker        chained_partition_arg.rollback_index_location += 1
1568*9e94795aSAndroid Build Coastguard Worker
1569*9e94795aSAndroid Build Coastguard Worker    used_index_loc.add(chained_partition_arg.rollback_index_location)
1570*9e94795aSAndroid Build Coastguard Worker    avb_args.extend([AVB_ARG_NAME_CHAIN_PARTITION,
1571*9e94795aSAndroid Build Coastguard Worker                     chained_partition_arg.to_string()])
1572*9e94795aSAndroid Build Coastguard Worker
1573*9e94795aSAndroid Build Coastguard Worker  return avb_args
1574*9e94795aSAndroid Build Coastguard Worker
1575*9e94795aSAndroid Build Coastguard Worker
1576*9e94795aSAndroid Build Coastguard Workerdef GetAvbChainedPartitionArg(partition, info_dict, key=None):
1577*9e94795aSAndroid Build Coastguard Worker  """Constructs and returns the arg to build or verify a chained partition.
1578*9e94795aSAndroid Build Coastguard Worker
1579*9e94795aSAndroid Build Coastguard Worker  Args:
1580*9e94795aSAndroid Build Coastguard Worker    partition: The partition name.
1581*9e94795aSAndroid Build Coastguard Worker    info_dict: The info dict to look up the key info and rollback index
1582*9e94795aSAndroid Build Coastguard Worker        location.
1583*9e94795aSAndroid Build Coastguard Worker    key: The key to be used for building or verifying the partition. Defaults to
1584*9e94795aSAndroid Build Coastguard Worker        the key listed in info_dict.
1585*9e94795aSAndroid Build Coastguard Worker
1586*9e94795aSAndroid Build Coastguard Worker  Returns:
1587*9e94795aSAndroid Build Coastguard Worker    An AvbChainedPartitionArg object with rollback_index_location and
1588*9e94795aSAndroid Build Coastguard Worker    pubkey_path that can be used to build or verify vbmeta image.
1589*9e94795aSAndroid Build Coastguard Worker  """
1590*9e94795aSAndroid Build Coastguard Worker  if key is None:
1591*9e94795aSAndroid Build Coastguard Worker    key = info_dict["avb_" + partition + "_key_path"]
1592*9e94795aSAndroid Build Coastguard Worker  key = ResolveAVBSigningPathArgs(key)
1593*9e94795aSAndroid Build Coastguard Worker  pubkey_path = ExtractAvbPublicKey(info_dict["avb_avbtool"], key)
1594*9e94795aSAndroid Build Coastguard Worker  rollback_index_location = info_dict[
1595*9e94795aSAndroid Build Coastguard Worker      "avb_" + partition + "_rollback_index_location"]
1596*9e94795aSAndroid Build Coastguard Worker  return AvbChainedPartitionArg(
1597*9e94795aSAndroid Build Coastguard Worker      partition=partition,
1598*9e94795aSAndroid Build Coastguard Worker      rollback_index_location=int(rollback_index_location),
1599*9e94795aSAndroid Build Coastguard Worker      pubkey_path=pubkey_path)
1600*9e94795aSAndroid Build Coastguard Worker
1601*9e94795aSAndroid Build Coastguard Worker
1602*9e94795aSAndroid Build Coastguard Workerdef BuildVBMeta(image_path, partitions, name, needed_partitions,
1603*9e94795aSAndroid Build Coastguard Worker                resolve_rollback_index_location_conflict=False):
1604*9e94795aSAndroid Build Coastguard Worker  """Creates a VBMeta image.
1605*9e94795aSAndroid Build Coastguard Worker
1606*9e94795aSAndroid Build Coastguard Worker  It generates the requested VBMeta image. The requested image could be for
1607*9e94795aSAndroid Build Coastguard Worker  top-level or chained VBMeta image, which is determined based on the name.
1608*9e94795aSAndroid Build Coastguard Worker
1609*9e94795aSAndroid Build Coastguard Worker  Args:
1610*9e94795aSAndroid Build Coastguard Worker    image_path: The output path for the new VBMeta image.
1611*9e94795aSAndroid Build Coastguard Worker    partitions: A dict that's keyed by partition names with image paths as
1612*9e94795aSAndroid Build Coastguard Worker        values. Only valid partition names are accepted, as partitions listed
1613*9e94795aSAndroid Build Coastguard Worker        in common.AVB_PARTITIONS and custom partitions listed in
1614*9e94795aSAndroid Build Coastguard Worker        OPTIONS.info_dict.get("avb_custom_images_partition_list")
1615*9e94795aSAndroid Build Coastguard Worker    name: Name of the VBMeta partition, e.g. 'vbmeta', 'vbmeta_system'.
1616*9e94795aSAndroid Build Coastguard Worker    needed_partitions: Partitions whose descriptors should be included into the
1617*9e94795aSAndroid Build Coastguard Worker        generated VBMeta image.
1618*9e94795aSAndroid Build Coastguard Worker    resolve_rollback_index_location_conflict: If true, resolve conflicting avb
1619*9e94795aSAndroid Build Coastguard Worker        rollback index locations by assigning the smallest unused value.
1620*9e94795aSAndroid Build Coastguard Worker
1621*9e94795aSAndroid Build Coastguard Worker  Raises:
1622*9e94795aSAndroid Build Coastguard Worker    AssertionError: On invalid input args.
1623*9e94795aSAndroid Build Coastguard Worker  """
1624*9e94795aSAndroid Build Coastguard Worker  avbtool = OPTIONS.info_dict["avb_avbtool"]
1625*9e94795aSAndroid Build Coastguard Worker  cmd = [avbtool, "make_vbmeta_image", "--output", image_path]
1626*9e94795aSAndroid Build Coastguard Worker  AppendAVBSigningArgs(cmd, name)
1627*9e94795aSAndroid Build Coastguard Worker
1628*9e94795aSAndroid Build Coastguard Worker  custom_partitions = OPTIONS.info_dict.get(
1629*9e94795aSAndroid Build Coastguard Worker      "avb_custom_images_partition_list", "").strip().split()
1630*9e94795aSAndroid Build Coastguard Worker  custom_avb_partitions = ["vbmeta_" + part for part in OPTIONS.info_dict.get(
1631*9e94795aSAndroid Build Coastguard Worker      "avb_custom_vbmeta_images_partition_list", "").strip().split()]
1632*9e94795aSAndroid Build Coastguard Worker
1633*9e94795aSAndroid Build Coastguard Worker  avb_partitions = {}
1634*9e94795aSAndroid Build Coastguard Worker  for partition, path in sorted(partitions.items()):
1635*9e94795aSAndroid Build Coastguard Worker    if partition not in needed_partitions:
1636*9e94795aSAndroid Build Coastguard Worker      continue
1637*9e94795aSAndroid Build Coastguard Worker    assert (partition in AVB_PARTITIONS or
1638*9e94795aSAndroid Build Coastguard Worker            partition in AVB_VBMETA_PARTITIONS or
1639*9e94795aSAndroid Build Coastguard Worker            partition in custom_avb_partitions or
1640*9e94795aSAndroid Build Coastguard Worker            partition in custom_partitions), \
1641*9e94795aSAndroid Build Coastguard Worker        'Unknown partition: {}'.format(partition)
1642*9e94795aSAndroid Build Coastguard Worker    assert os.path.exists(path), \
1643*9e94795aSAndroid Build Coastguard Worker        'Failed to find {} for {}'.format(path, partition)
1644*9e94795aSAndroid Build Coastguard Worker    avb_partitions[partition] = path
1645*9e94795aSAndroid Build Coastguard Worker  cmd.extend(GetAvbPartitionsArg(avb_partitions,
1646*9e94795aSAndroid Build Coastguard Worker                                 resolve_rollback_index_location_conflict))
1647*9e94795aSAndroid Build Coastguard Worker
1648*9e94795aSAndroid Build Coastguard Worker  args = OPTIONS.info_dict.get("avb_{}_args".format(name))
1649*9e94795aSAndroid Build Coastguard Worker  if args and args.strip():
1650*9e94795aSAndroid Build Coastguard Worker    split_args = shlex.split(args)
1651*9e94795aSAndroid Build Coastguard Worker    for index, arg in enumerate(split_args[:-1]):
1652*9e94795aSAndroid Build Coastguard Worker      # Check that the image file exists. Some images might be defined
1653*9e94795aSAndroid Build Coastguard Worker      # as a path relative to source tree, which may not be available at the
1654*9e94795aSAndroid Build Coastguard Worker      # same location when running this script (we have the input target_files
1655*9e94795aSAndroid Build Coastguard Worker      # zip only). For such cases, we additionally scan other locations (e.g.
1656*9e94795aSAndroid Build Coastguard Worker      # IMAGES/, RADIO/, etc) before bailing out.
1657*9e94795aSAndroid Build Coastguard Worker      if arg == AVB_ARG_NAME_INCLUDE_DESC_FROM_IMG:
1658*9e94795aSAndroid Build Coastguard Worker        chained_image = split_args[index + 1]
1659*9e94795aSAndroid Build Coastguard Worker        if os.path.exists(chained_image):
1660*9e94795aSAndroid Build Coastguard Worker          continue
1661*9e94795aSAndroid Build Coastguard Worker        found = False
1662*9e94795aSAndroid Build Coastguard Worker        for dir_name in ['IMAGES', 'RADIO', 'PREBUILT_IMAGES']:
1663*9e94795aSAndroid Build Coastguard Worker          alt_path = os.path.join(
1664*9e94795aSAndroid Build Coastguard Worker              OPTIONS.input_tmp, dir_name, os.path.basename(chained_image))
1665*9e94795aSAndroid Build Coastguard Worker          if os.path.exists(alt_path):
1666*9e94795aSAndroid Build Coastguard Worker            split_args[index + 1] = alt_path
1667*9e94795aSAndroid Build Coastguard Worker            found = True
1668*9e94795aSAndroid Build Coastguard Worker            break
1669*9e94795aSAndroid Build Coastguard Worker        assert found, 'Failed to find {}'.format(chained_image)
1670*9e94795aSAndroid Build Coastguard Worker
1671*9e94795aSAndroid Build Coastguard Worker    split_args = ResolveAVBSigningPathArgs(split_args)
1672*9e94795aSAndroid Build Coastguard Worker    cmd.extend(split_args)
1673*9e94795aSAndroid Build Coastguard Worker
1674*9e94795aSAndroid Build Coastguard Worker  RunAndCheckOutput(cmd)
1675*9e94795aSAndroid Build Coastguard Worker
1676*9e94795aSAndroid Build Coastguard Worker
1677*9e94795aSAndroid Build Coastguard Workerdef _MakeRamdisk(sourcedir, fs_config_file=None,
1678*9e94795aSAndroid Build Coastguard Worker                 dev_node_file=None,
1679*9e94795aSAndroid Build Coastguard Worker                 ramdisk_format=RamdiskFormat.GZ):
1680*9e94795aSAndroid Build Coastguard Worker  ramdisk_img = tempfile.NamedTemporaryFile()
1681*9e94795aSAndroid Build Coastguard Worker
1682*9e94795aSAndroid Build Coastguard Worker  cmd = ["mkbootfs"]
1683*9e94795aSAndroid Build Coastguard Worker
1684*9e94795aSAndroid Build Coastguard Worker  if fs_config_file and os.access(fs_config_file, os.F_OK):
1685*9e94795aSAndroid Build Coastguard Worker    cmd.extend(["-f", fs_config_file])
1686*9e94795aSAndroid Build Coastguard Worker
1687*9e94795aSAndroid Build Coastguard Worker  if dev_node_file and os.access(dev_node_file, os.F_OK):
1688*9e94795aSAndroid Build Coastguard Worker    cmd.extend(["-n", dev_node_file])
1689*9e94795aSAndroid Build Coastguard Worker
1690*9e94795aSAndroid Build Coastguard Worker  cmd.append(os.path.join(sourcedir, "RAMDISK"))
1691*9e94795aSAndroid Build Coastguard Worker
1692*9e94795aSAndroid Build Coastguard Worker  p1 = Run(cmd, stdout=subprocess.PIPE)
1693*9e94795aSAndroid Build Coastguard Worker  if ramdisk_format == RamdiskFormat.LZ4:
1694*9e94795aSAndroid Build Coastguard Worker    p2 = Run(["lz4", "-l", "-12", "--favor-decSpeed"], stdin=p1.stdout,
1695*9e94795aSAndroid Build Coastguard Worker             stdout=ramdisk_img.file.fileno())
1696*9e94795aSAndroid Build Coastguard Worker  elif ramdisk_format == RamdiskFormat.GZ:
1697*9e94795aSAndroid Build Coastguard Worker    p2 = Run(["gzip"], stdin=p1.stdout, stdout=ramdisk_img.file.fileno())
1698*9e94795aSAndroid Build Coastguard Worker  else:
1699*9e94795aSAndroid Build Coastguard Worker    raise ValueError("Only support lz4 or gzip ramdisk format.")
1700*9e94795aSAndroid Build Coastguard Worker
1701*9e94795aSAndroid Build Coastguard Worker  p2.wait()
1702*9e94795aSAndroid Build Coastguard Worker  p1.wait()
1703*9e94795aSAndroid Build Coastguard Worker  assert p1.returncode == 0, "mkbootfs of %s ramdisk failed" % (sourcedir,)
1704*9e94795aSAndroid Build Coastguard Worker  assert p2.returncode == 0, "compression of %s ramdisk failed" % (sourcedir,)
1705*9e94795aSAndroid Build Coastguard Worker
1706*9e94795aSAndroid Build Coastguard Worker  return ramdisk_img
1707*9e94795aSAndroid Build Coastguard Worker
1708*9e94795aSAndroid Build Coastguard Worker
1709*9e94795aSAndroid Build Coastguard Workerdef _BuildBootableImage(image_name, sourcedir, fs_config_file,
1710*9e94795aSAndroid Build Coastguard Worker                        dev_node_file=None, info_dict=None,
1711*9e94795aSAndroid Build Coastguard Worker                        has_ramdisk=False, two_step_image=False):
1712*9e94795aSAndroid Build Coastguard Worker  """Build a bootable image from the specified sourcedir.
1713*9e94795aSAndroid Build Coastguard Worker
1714*9e94795aSAndroid Build Coastguard Worker  Take a kernel, cmdline, and optionally a ramdisk directory from the input (in
1715*9e94795aSAndroid Build Coastguard Worker  'sourcedir'), and turn them into a boot image. 'two_step_image' indicates if
1716*9e94795aSAndroid Build Coastguard Worker  we are building a two-step special image (i.e. building a recovery image to
1717*9e94795aSAndroid Build Coastguard Worker  be loaded into /boot in two-step OTAs).
1718*9e94795aSAndroid Build Coastguard Worker
1719*9e94795aSAndroid Build Coastguard Worker  Return the image data, or None if sourcedir does not appear to contains files
1720*9e94795aSAndroid Build Coastguard Worker  for building the requested image.
1721*9e94795aSAndroid Build Coastguard Worker  """
1722*9e94795aSAndroid Build Coastguard Worker
1723*9e94795aSAndroid Build Coastguard Worker  if info_dict is None:
1724*9e94795aSAndroid Build Coastguard Worker    info_dict = OPTIONS.info_dict
1725*9e94795aSAndroid Build Coastguard Worker
1726*9e94795aSAndroid Build Coastguard Worker  # "boot" or "recovery", without extension.
1727*9e94795aSAndroid Build Coastguard Worker  partition_name = os.path.basename(sourcedir).lower()
1728*9e94795aSAndroid Build Coastguard Worker
1729*9e94795aSAndroid Build Coastguard Worker  kernel = None
1730*9e94795aSAndroid Build Coastguard Worker  if partition_name == "recovery":
1731*9e94795aSAndroid Build Coastguard Worker    if info_dict.get("exclude_kernel_from_recovery_image") == "true":
1732*9e94795aSAndroid Build Coastguard Worker      logger.info("Excluded kernel binary from recovery image.")
1733*9e94795aSAndroid Build Coastguard Worker    else:
1734*9e94795aSAndroid Build Coastguard Worker      kernel = "kernel"
1735*9e94795aSAndroid Build Coastguard Worker  elif partition_name == "init_boot":
1736*9e94795aSAndroid Build Coastguard Worker    pass
1737*9e94795aSAndroid Build Coastguard Worker  else:
1738*9e94795aSAndroid Build Coastguard Worker    kernel = image_name.replace("boot", "kernel")
1739*9e94795aSAndroid Build Coastguard Worker    kernel = kernel.replace(".img", "")
1740*9e94795aSAndroid Build Coastguard Worker  if kernel and not os.access(os.path.join(sourcedir, kernel), os.F_OK):
1741*9e94795aSAndroid Build Coastguard Worker    return None
1742*9e94795aSAndroid Build Coastguard Worker
1743*9e94795aSAndroid Build Coastguard Worker  kernel_path = os.path.join(sourcedir, kernel) if kernel else None
1744*9e94795aSAndroid Build Coastguard Worker
1745*9e94795aSAndroid Build Coastguard Worker  if has_ramdisk and not os.access(os.path.join(sourcedir, "RAMDISK"), os.F_OK):
1746*9e94795aSAndroid Build Coastguard Worker    return None
1747*9e94795aSAndroid Build Coastguard Worker
1748*9e94795aSAndroid Build Coastguard Worker  img = tempfile.NamedTemporaryFile()
1749*9e94795aSAndroid Build Coastguard Worker
1750*9e94795aSAndroid Build Coastguard Worker  if has_ramdisk:
1751*9e94795aSAndroid Build Coastguard Worker    ramdisk_format = GetRamdiskFormat(info_dict)
1752*9e94795aSAndroid Build Coastguard Worker    ramdisk_img = _MakeRamdisk(sourcedir, fs_config_file, dev_node_file,
1753*9e94795aSAndroid Build Coastguard Worker                               ramdisk_format=ramdisk_format)
1754*9e94795aSAndroid Build Coastguard Worker
1755*9e94795aSAndroid Build Coastguard Worker  # use MKBOOTIMG from environ, or "mkbootimg" if empty or not set
1756*9e94795aSAndroid Build Coastguard Worker  mkbootimg = os.getenv('MKBOOTIMG') or "mkbootimg"
1757*9e94795aSAndroid Build Coastguard Worker
1758*9e94795aSAndroid Build Coastguard Worker  cmd = [mkbootimg]
1759*9e94795aSAndroid Build Coastguard Worker  if kernel_path is not None:
1760*9e94795aSAndroid Build Coastguard Worker    cmd.extend(["--kernel", kernel_path])
1761*9e94795aSAndroid Build Coastguard Worker
1762*9e94795aSAndroid Build Coastguard Worker  fn = os.path.join(sourcedir, "second")
1763*9e94795aSAndroid Build Coastguard Worker  if os.access(fn, os.F_OK):
1764*9e94795aSAndroid Build Coastguard Worker    cmd.append("--second")
1765*9e94795aSAndroid Build Coastguard Worker    cmd.append(fn)
1766*9e94795aSAndroid Build Coastguard Worker
1767*9e94795aSAndroid Build Coastguard Worker  fn = os.path.join(sourcedir, "dtb")
1768*9e94795aSAndroid Build Coastguard Worker  if os.access(fn, os.F_OK):
1769*9e94795aSAndroid Build Coastguard Worker    cmd.append("--dtb")
1770*9e94795aSAndroid Build Coastguard Worker    cmd.append(fn)
1771*9e94795aSAndroid Build Coastguard Worker
1772*9e94795aSAndroid Build Coastguard Worker  fn = os.path.join(sourcedir, "cmdline")
1773*9e94795aSAndroid Build Coastguard Worker  if os.access(fn, os.F_OK):
1774*9e94795aSAndroid Build Coastguard Worker    cmd.append("--cmdline")
1775*9e94795aSAndroid Build Coastguard Worker    cmd.append(open(fn).read().rstrip("\n"))
1776*9e94795aSAndroid Build Coastguard Worker
1777*9e94795aSAndroid Build Coastguard Worker  fn = os.path.join(sourcedir, "base")
1778*9e94795aSAndroid Build Coastguard Worker  if os.access(fn, os.F_OK):
1779*9e94795aSAndroid Build Coastguard Worker    cmd.append("--base")
1780*9e94795aSAndroid Build Coastguard Worker    cmd.append(open(fn).read().rstrip("\n"))
1781*9e94795aSAndroid Build Coastguard Worker
1782*9e94795aSAndroid Build Coastguard Worker  fn = os.path.join(sourcedir, "pagesize")
1783*9e94795aSAndroid Build Coastguard Worker  if os.access(fn, os.F_OK):
1784*9e94795aSAndroid Build Coastguard Worker    cmd.append("--pagesize")
1785*9e94795aSAndroid Build Coastguard Worker    cmd.append(open(fn).read().rstrip("\n"))
1786*9e94795aSAndroid Build Coastguard Worker
1787*9e94795aSAndroid Build Coastguard Worker  if partition_name == "recovery":
1788*9e94795aSAndroid Build Coastguard Worker    args = info_dict.get("recovery_mkbootimg_args")
1789*9e94795aSAndroid Build Coastguard Worker    if not args:
1790*9e94795aSAndroid Build Coastguard Worker      # Fall back to "mkbootimg_args" for recovery image
1791*9e94795aSAndroid Build Coastguard Worker      # in case "recovery_mkbootimg_args" is not set.
1792*9e94795aSAndroid Build Coastguard Worker      args = info_dict.get("mkbootimg_args")
1793*9e94795aSAndroid Build Coastguard Worker  elif partition_name == "init_boot":
1794*9e94795aSAndroid Build Coastguard Worker    args = info_dict.get("mkbootimg_init_args")
1795*9e94795aSAndroid Build Coastguard Worker  else:
1796*9e94795aSAndroid Build Coastguard Worker    args = info_dict.get("mkbootimg_args")
1797*9e94795aSAndroid Build Coastguard Worker  if args and args.strip():
1798*9e94795aSAndroid Build Coastguard Worker    cmd.extend(shlex.split(args))
1799*9e94795aSAndroid Build Coastguard Worker
1800*9e94795aSAndroid Build Coastguard Worker  args = info_dict.get("mkbootimg_version_args")
1801*9e94795aSAndroid Build Coastguard Worker  if args and args.strip():
1802*9e94795aSAndroid Build Coastguard Worker    cmd.extend(shlex.split(args))
1803*9e94795aSAndroid Build Coastguard Worker
1804*9e94795aSAndroid Build Coastguard Worker  if has_ramdisk:
1805*9e94795aSAndroid Build Coastguard Worker    cmd.extend(["--ramdisk", ramdisk_img.name])
1806*9e94795aSAndroid Build Coastguard Worker
1807*9e94795aSAndroid Build Coastguard Worker  cmd.extend(["--output", img.name])
1808*9e94795aSAndroid Build Coastguard Worker
1809*9e94795aSAndroid Build Coastguard Worker  if partition_name == "recovery":
1810*9e94795aSAndroid Build Coastguard Worker    if info_dict.get("include_recovery_dtbo") == "true":
1811*9e94795aSAndroid Build Coastguard Worker      fn = os.path.join(sourcedir, "recovery_dtbo")
1812*9e94795aSAndroid Build Coastguard Worker      cmd.extend(["--recovery_dtbo", fn])
1813*9e94795aSAndroid Build Coastguard Worker    if info_dict.get("include_recovery_acpio") == "true":
1814*9e94795aSAndroid Build Coastguard Worker      fn = os.path.join(sourcedir, "recovery_acpio")
1815*9e94795aSAndroid Build Coastguard Worker      cmd.extend(["--recovery_acpio", fn])
1816*9e94795aSAndroid Build Coastguard Worker
1817*9e94795aSAndroid Build Coastguard Worker  RunAndCheckOutput(cmd)
1818*9e94795aSAndroid Build Coastguard Worker
1819*9e94795aSAndroid Build Coastguard Worker  # AVB: if enabled, calculate and add hash to boot.img or recovery.img.
1820*9e94795aSAndroid Build Coastguard Worker  if info_dict.get("avb_enable") == "true":
1821*9e94795aSAndroid Build Coastguard Worker    avbtool = info_dict["avb_avbtool"]
1822*9e94795aSAndroid Build Coastguard Worker    if partition_name == "recovery":
1823*9e94795aSAndroid Build Coastguard Worker      part_size = info_dict["recovery_size"]
1824*9e94795aSAndroid Build Coastguard Worker    else:
1825*9e94795aSAndroid Build Coastguard Worker      part_size = info_dict[image_name.replace(".img", "_size")]
1826*9e94795aSAndroid Build Coastguard Worker    cmd = [avbtool, "add_hash_footer", "--image", img.name,
1827*9e94795aSAndroid Build Coastguard Worker           "--partition_size", str(part_size), "--partition_name",
1828*9e94795aSAndroid Build Coastguard Worker           partition_name]
1829*9e94795aSAndroid Build Coastguard Worker    salt = None
1830*9e94795aSAndroid Build Coastguard Worker    if kernel_path is not None:
1831*9e94795aSAndroid Build Coastguard Worker      with open(kernel_path, "rb") as fp:
1832*9e94795aSAndroid Build Coastguard Worker        salt = sha256(fp.read()).hexdigest()
1833*9e94795aSAndroid Build Coastguard Worker    AppendAVBSigningArgs(cmd, partition_name, salt)
1834*9e94795aSAndroid Build Coastguard Worker    args = info_dict.get("avb_" + partition_name + "_add_hash_footer_args")
1835*9e94795aSAndroid Build Coastguard Worker    if args and args.strip():
1836*9e94795aSAndroid Build Coastguard Worker      split_args = ResolveAVBSigningPathArgs(shlex.split(args))
1837*9e94795aSAndroid Build Coastguard Worker      cmd.extend(split_args)
1838*9e94795aSAndroid Build Coastguard Worker    RunAndCheckOutput(cmd)
1839*9e94795aSAndroid Build Coastguard Worker
1840*9e94795aSAndroid Build Coastguard Worker  img.seek(os.SEEK_SET, 0)
1841*9e94795aSAndroid Build Coastguard Worker  data = img.read()
1842*9e94795aSAndroid Build Coastguard Worker
1843*9e94795aSAndroid Build Coastguard Worker  if has_ramdisk:
1844*9e94795aSAndroid Build Coastguard Worker    ramdisk_img.close()
1845*9e94795aSAndroid Build Coastguard Worker  img.close()
1846*9e94795aSAndroid Build Coastguard Worker
1847*9e94795aSAndroid Build Coastguard Worker  return data
1848*9e94795aSAndroid Build Coastguard Worker
1849*9e94795aSAndroid Build Coastguard Worker
1850*9e94795aSAndroid Build Coastguard Workerdef _SignBootableImage(image_path, prebuilt_name, partition_name,
1851*9e94795aSAndroid Build Coastguard Worker                       info_dict=None):
1852*9e94795aSAndroid Build Coastguard Worker  """Performs AVB signing for a prebuilt boot.img.
1853*9e94795aSAndroid Build Coastguard Worker
1854*9e94795aSAndroid Build Coastguard Worker  Args:
1855*9e94795aSAndroid Build Coastguard Worker    image_path: The full path of the image, e.g., /path/to/boot.img.
1856*9e94795aSAndroid Build Coastguard Worker    prebuilt_name: The prebuilt image name, e.g., boot.img, boot-5.4-gz.img,
1857*9e94795aSAndroid Build Coastguard Worker        boot-5.10.img, recovery.img or init_boot.img.
1858*9e94795aSAndroid Build Coastguard Worker    partition_name: The partition name, e.g., 'boot', 'init_boot' or 'recovery'.
1859*9e94795aSAndroid Build Coastguard Worker    info_dict: The information dict read from misc_info.txt.
1860*9e94795aSAndroid Build Coastguard Worker  """
1861*9e94795aSAndroid Build Coastguard Worker  if info_dict is None:
1862*9e94795aSAndroid Build Coastguard Worker    info_dict = OPTIONS.info_dict
1863*9e94795aSAndroid Build Coastguard Worker
1864*9e94795aSAndroid Build Coastguard Worker  # AVB: if enabled, calculate and add hash to boot.img or recovery.img.
1865*9e94795aSAndroid Build Coastguard Worker  if info_dict.get("avb_enable") == "true":
1866*9e94795aSAndroid Build Coastguard Worker    avbtool = info_dict["avb_avbtool"]
1867*9e94795aSAndroid Build Coastguard Worker    if partition_name == "recovery":
1868*9e94795aSAndroid Build Coastguard Worker      part_size = info_dict["recovery_size"]
1869*9e94795aSAndroid Build Coastguard Worker    else:
1870*9e94795aSAndroid Build Coastguard Worker      part_size = info_dict[prebuilt_name.replace(".img", "_size")]
1871*9e94795aSAndroid Build Coastguard Worker
1872*9e94795aSAndroid Build Coastguard Worker    cmd = [avbtool, "add_hash_footer", "--image", image_path,
1873*9e94795aSAndroid Build Coastguard Worker           "--partition_size", str(part_size), "--partition_name",
1874*9e94795aSAndroid Build Coastguard Worker           partition_name]
1875*9e94795aSAndroid Build Coastguard Worker    # Use sha256 of the kernel as salt for reproducible builds
1876*9e94795aSAndroid Build Coastguard Worker    with tempfile.TemporaryDirectory() as tmpdir:
1877*9e94795aSAndroid Build Coastguard Worker      RunAndCheckOutput(["unpack_bootimg", "--boot_img", image_path, "--out", tmpdir])
1878*9e94795aSAndroid Build Coastguard Worker      for filename in ["kernel", "ramdisk", "vendor_ramdisk00"]:
1879*9e94795aSAndroid Build Coastguard Worker        path = os.path.join(tmpdir, filename)
1880*9e94795aSAndroid Build Coastguard Worker        if os.path.exists(path) and os.path.getsize(path):
1881*9e94795aSAndroid Build Coastguard Worker          print("Using {} as salt for avb footer of {}".format(
1882*9e94795aSAndroid Build Coastguard Worker              filename, partition_name))
1883*9e94795aSAndroid Build Coastguard Worker          with open(path, "rb") as fp:
1884*9e94795aSAndroid Build Coastguard Worker            salt = sha256(fp.read()).hexdigest()
1885*9e94795aSAndroid Build Coastguard Worker            break
1886*9e94795aSAndroid Build Coastguard Worker    AppendAVBSigningArgs(cmd, partition_name, salt)
1887*9e94795aSAndroid Build Coastguard Worker    args = info_dict.get("avb_" + partition_name + "_add_hash_footer_args")
1888*9e94795aSAndroid Build Coastguard Worker    if args and args.strip():
1889*9e94795aSAndroid Build Coastguard Worker      split_args = ResolveAVBSigningPathArgs(shlex.split(args))
1890*9e94795aSAndroid Build Coastguard Worker      cmd.extend(split_args)
1891*9e94795aSAndroid Build Coastguard Worker    RunAndCheckOutput(cmd)
1892*9e94795aSAndroid Build Coastguard Worker
1893*9e94795aSAndroid Build Coastguard Worker
1894*9e94795aSAndroid Build Coastguard Workerdef HasRamdisk(partition_name, info_dict=None):
1895*9e94795aSAndroid Build Coastguard Worker  """Returns true/false to see if a bootable image should have a ramdisk.
1896*9e94795aSAndroid Build Coastguard Worker
1897*9e94795aSAndroid Build Coastguard Worker  Args:
1898*9e94795aSAndroid Build Coastguard Worker    partition_name: The partition name, e.g., 'boot', 'init_boot' or 'recovery'.
1899*9e94795aSAndroid Build Coastguard Worker    info_dict: The information dict read from misc_info.txt.
1900*9e94795aSAndroid Build Coastguard Worker  """
1901*9e94795aSAndroid Build Coastguard Worker  if info_dict is None:
1902*9e94795aSAndroid Build Coastguard Worker    info_dict = OPTIONS.info_dict
1903*9e94795aSAndroid Build Coastguard Worker
1904*9e94795aSAndroid Build Coastguard Worker  if partition_name != "boot":
1905*9e94795aSAndroid Build Coastguard Worker    return True  # init_boot.img or recovery.img has a ramdisk.
1906*9e94795aSAndroid Build Coastguard Worker
1907*9e94795aSAndroid Build Coastguard Worker  if info_dict.get("recovery_as_boot") == "true":
1908*9e94795aSAndroid Build Coastguard Worker    return True  # the recovery-as-boot boot.img has a RECOVERY ramdisk.
1909*9e94795aSAndroid Build Coastguard Worker
1910*9e94795aSAndroid Build Coastguard Worker  if info_dict.get("gki_boot_image_without_ramdisk") == "true":
1911*9e94795aSAndroid Build Coastguard Worker    return False  # A GKI boot.img has no ramdisk since Android-13.
1912*9e94795aSAndroid Build Coastguard Worker
1913*9e94795aSAndroid Build Coastguard Worker  if info_dict.get("init_boot") == "true":
1914*9e94795aSAndroid Build Coastguard Worker    # The ramdisk is moved to the init_boot.img, so there is NO
1915*9e94795aSAndroid Build Coastguard Worker    # ramdisk in the boot.img or boot-<kernel version>.img.
1916*9e94795aSAndroid Build Coastguard Worker    return False
1917*9e94795aSAndroid Build Coastguard Worker
1918*9e94795aSAndroid Build Coastguard Worker  return True
1919*9e94795aSAndroid Build Coastguard Worker
1920*9e94795aSAndroid Build Coastguard Worker
1921*9e94795aSAndroid Build Coastguard Workerdef GetBootableImage(name, prebuilt_name, unpack_dir, tree_subdir,
1922*9e94795aSAndroid Build Coastguard Worker                     info_dict=None, two_step_image=False,
1923*9e94795aSAndroid Build Coastguard Worker                     dev_nodes=False):
1924*9e94795aSAndroid Build Coastguard Worker  """Return a File object with the desired bootable image.
1925*9e94795aSAndroid Build Coastguard Worker
1926*9e94795aSAndroid Build Coastguard Worker  Look for it in 'unpack_dir'/BOOTABLE_IMAGES under the name 'prebuilt_name',
1927*9e94795aSAndroid Build Coastguard Worker  otherwise look for it under 'unpack_dir'/IMAGES, otherwise construct it from
1928*9e94795aSAndroid Build Coastguard Worker  the source files in 'unpack_dir'/'tree_subdir'."""
1929*9e94795aSAndroid Build Coastguard Worker
1930*9e94795aSAndroid Build Coastguard Worker  if info_dict is None:
1931*9e94795aSAndroid Build Coastguard Worker    info_dict = OPTIONS.info_dict
1932*9e94795aSAndroid Build Coastguard Worker
1933*9e94795aSAndroid Build Coastguard Worker  prebuilt_path = os.path.join(unpack_dir, "BOOTABLE_IMAGES", prebuilt_name)
1934*9e94795aSAndroid Build Coastguard Worker  if os.path.exists(prebuilt_path):
1935*9e94795aSAndroid Build Coastguard Worker    logger.info("using prebuilt %s from BOOTABLE_IMAGES...", prebuilt_name)
1936*9e94795aSAndroid Build Coastguard Worker    return File.FromLocalFile(name, prebuilt_path)
1937*9e94795aSAndroid Build Coastguard Worker
1938*9e94795aSAndroid Build Coastguard Worker  prebuilt_path = os.path.join(unpack_dir, "IMAGES", prebuilt_name)
1939*9e94795aSAndroid Build Coastguard Worker  if os.path.exists(prebuilt_path):
1940*9e94795aSAndroid Build Coastguard Worker    logger.info("using prebuilt %s from IMAGES...", prebuilt_name)
1941*9e94795aSAndroid Build Coastguard Worker    return File.FromLocalFile(name, prebuilt_path)
1942*9e94795aSAndroid Build Coastguard Worker
1943*9e94795aSAndroid Build Coastguard Worker  partition_name = tree_subdir.lower()
1944*9e94795aSAndroid Build Coastguard Worker  prebuilt_path = os.path.join(unpack_dir, "PREBUILT_IMAGES", prebuilt_name)
1945*9e94795aSAndroid Build Coastguard Worker  if os.path.exists(prebuilt_path):
1946*9e94795aSAndroid Build Coastguard Worker    logger.info("Re-signing prebuilt %s from PREBUILT_IMAGES...", prebuilt_name)
1947*9e94795aSAndroid Build Coastguard Worker    signed_img = MakeTempFile()
1948*9e94795aSAndroid Build Coastguard Worker    shutil.copy(prebuilt_path, signed_img)
1949*9e94795aSAndroid Build Coastguard Worker    _SignBootableImage(signed_img, prebuilt_name, partition_name, info_dict)
1950*9e94795aSAndroid Build Coastguard Worker    return File.FromLocalFile(name, signed_img)
1951*9e94795aSAndroid Build Coastguard Worker
1952*9e94795aSAndroid Build Coastguard Worker  logger.info("building image from target_files %s...", tree_subdir)
1953*9e94795aSAndroid Build Coastguard Worker
1954*9e94795aSAndroid Build Coastguard Worker  has_ramdisk = HasRamdisk(partition_name, info_dict)
1955*9e94795aSAndroid Build Coastguard Worker
1956*9e94795aSAndroid Build Coastguard Worker  fs_config = "META/" + tree_subdir.lower() + "_filesystem_config.txt"
1957*9e94795aSAndroid Build Coastguard Worker  data = _BuildBootableImage(prebuilt_name, os.path.join(unpack_dir, tree_subdir),
1958*9e94795aSAndroid Build Coastguard Worker                             os.path.join(unpack_dir, fs_config),
1959*9e94795aSAndroid Build Coastguard Worker                             os.path.join(unpack_dir, 'META/ramdisk_node_list')
1960*9e94795aSAndroid Build Coastguard Worker                             if dev_nodes else None,
1961*9e94795aSAndroid Build Coastguard Worker                             info_dict, has_ramdisk, two_step_image)
1962*9e94795aSAndroid Build Coastguard Worker  if data:
1963*9e94795aSAndroid Build Coastguard Worker    return File(name, data)
1964*9e94795aSAndroid Build Coastguard Worker  return None
1965*9e94795aSAndroid Build Coastguard Worker
1966*9e94795aSAndroid Build Coastguard Worker
1967*9e94795aSAndroid Build Coastguard Workerdef _BuildVendorBootImage(sourcedir, fs_config_file, partition_name, info_dict=None):
1968*9e94795aSAndroid Build Coastguard Worker  """Build a vendor boot image from the specified sourcedir.
1969*9e94795aSAndroid Build Coastguard Worker
1970*9e94795aSAndroid Build Coastguard Worker  Take a ramdisk, dtb, and vendor_cmdline from the input (in 'sourcedir'), and
1971*9e94795aSAndroid Build Coastguard Worker  turn them into a vendor boot image.
1972*9e94795aSAndroid Build Coastguard Worker
1973*9e94795aSAndroid Build Coastguard Worker  Return the image data, or None if sourcedir does not appear to contains files
1974*9e94795aSAndroid Build Coastguard Worker  for building the requested image.
1975*9e94795aSAndroid Build Coastguard Worker  """
1976*9e94795aSAndroid Build Coastguard Worker
1977*9e94795aSAndroid Build Coastguard Worker  if info_dict is None:
1978*9e94795aSAndroid Build Coastguard Worker    info_dict = OPTIONS.info_dict
1979*9e94795aSAndroid Build Coastguard Worker
1980*9e94795aSAndroid Build Coastguard Worker  img = tempfile.NamedTemporaryFile()
1981*9e94795aSAndroid Build Coastguard Worker
1982*9e94795aSAndroid Build Coastguard Worker  ramdisk_format = GetRamdiskFormat(info_dict)
1983*9e94795aSAndroid Build Coastguard Worker  ramdisk_img = _MakeRamdisk(sourcedir, fs_config_file=fs_config_file, ramdisk_format=ramdisk_format)
1984*9e94795aSAndroid Build Coastguard Worker
1985*9e94795aSAndroid Build Coastguard Worker  # use MKBOOTIMG from environ, or "mkbootimg" if empty or not set
1986*9e94795aSAndroid Build Coastguard Worker  mkbootimg = os.getenv('MKBOOTIMG') or "mkbootimg"
1987*9e94795aSAndroid Build Coastguard Worker
1988*9e94795aSAndroid Build Coastguard Worker  cmd = [mkbootimg]
1989*9e94795aSAndroid Build Coastguard Worker
1990*9e94795aSAndroid Build Coastguard Worker  fn = os.path.join(sourcedir, "dtb")
1991*9e94795aSAndroid Build Coastguard Worker  if os.access(fn, os.F_OK):
1992*9e94795aSAndroid Build Coastguard Worker    has_vendor_kernel_boot = (info_dict.get(
1993*9e94795aSAndroid Build Coastguard Worker        "vendor_kernel_boot", "").lower() == "true")
1994*9e94795aSAndroid Build Coastguard Worker
1995*9e94795aSAndroid Build Coastguard Worker    # Pack dtb into vendor_kernel_boot if building vendor_kernel_boot.
1996*9e94795aSAndroid Build Coastguard Worker    # Otherwise pack dtb into vendor_boot.
1997*9e94795aSAndroid Build Coastguard Worker    if not has_vendor_kernel_boot or partition_name == "vendor_kernel_boot":
1998*9e94795aSAndroid Build Coastguard Worker      cmd.append("--dtb")
1999*9e94795aSAndroid Build Coastguard Worker      cmd.append(fn)
2000*9e94795aSAndroid Build Coastguard Worker
2001*9e94795aSAndroid Build Coastguard Worker  fn = os.path.join(sourcedir, "vendor_cmdline")
2002*9e94795aSAndroid Build Coastguard Worker  if os.access(fn, os.F_OK):
2003*9e94795aSAndroid Build Coastguard Worker    cmd.append("--vendor_cmdline")
2004*9e94795aSAndroid Build Coastguard Worker    cmd.append(open(fn).read().rstrip("\n"))
2005*9e94795aSAndroid Build Coastguard Worker
2006*9e94795aSAndroid Build Coastguard Worker  fn = os.path.join(sourcedir, "base")
2007*9e94795aSAndroid Build Coastguard Worker  if os.access(fn, os.F_OK):
2008*9e94795aSAndroid Build Coastguard Worker    cmd.append("--base")
2009*9e94795aSAndroid Build Coastguard Worker    cmd.append(open(fn).read().rstrip("\n"))
2010*9e94795aSAndroid Build Coastguard Worker
2011*9e94795aSAndroid Build Coastguard Worker  fn = os.path.join(sourcedir, "pagesize")
2012*9e94795aSAndroid Build Coastguard Worker  if os.access(fn, os.F_OK):
2013*9e94795aSAndroid Build Coastguard Worker    cmd.append("--pagesize")
2014*9e94795aSAndroid Build Coastguard Worker    cmd.append(open(fn).read().rstrip("\n"))
2015*9e94795aSAndroid Build Coastguard Worker
2016*9e94795aSAndroid Build Coastguard Worker  args = info_dict.get("mkbootimg_args")
2017*9e94795aSAndroid Build Coastguard Worker  if args and args.strip():
2018*9e94795aSAndroid Build Coastguard Worker    cmd.extend(shlex.split(args))
2019*9e94795aSAndroid Build Coastguard Worker
2020*9e94795aSAndroid Build Coastguard Worker  args = info_dict.get("mkbootimg_version_args")
2021*9e94795aSAndroid Build Coastguard Worker  if args and args.strip():
2022*9e94795aSAndroid Build Coastguard Worker    cmd.extend(shlex.split(args))
2023*9e94795aSAndroid Build Coastguard Worker
2024*9e94795aSAndroid Build Coastguard Worker  cmd.extend(["--vendor_ramdisk", ramdisk_img.name])
2025*9e94795aSAndroid Build Coastguard Worker  cmd.extend(["--vendor_boot", img.name])
2026*9e94795aSAndroid Build Coastguard Worker
2027*9e94795aSAndroid Build Coastguard Worker  fn = os.path.join(sourcedir, "vendor_bootconfig")
2028*9e94795aSAndroid Build Coastguard Worker  if os.access(fn, os.F_OK):
2029*9e94795aSAndroid Build Coastguard Worker    cmd.append("--vendor_bootconfig")
2030*9e94795aSAndroid Build Coastguard Worker    cmd.append(fn)
2031*9e94795aSAndroid Build Coastguard Worker
2032*9e94795aSAndroid Build Coastguard Worker  ramdisk_fragment_imgs = []
2033*9e94795aSAndroid Build Coastguard Worker  fn = os.path.join(sourcedir, "vendor_ramdisk_fragments")
2034*9e94795aSAndroid Build Coastguard Worker  if os.access(fn, os.F_OK):
2035*9e94795aSAndroid Build Coastguard Worker    ramdisk_fragments = shlex.split(open(fn).read().rstrip("\n"))
2036*9e94795aSAndroid Build Coastguard Worker    for ramdisk_fragment in ramdisk_fragments:
2037*9e94795aSAndroid Build Coastguard Worker      fn = os.path.join(sourcedir, "RAMDISK_FRAGMENTS",
2038*9e94795aSAndroid Build Coastguard Worker                        ramdisk_fragment, "mkbootimg_args")
2039*9e94795aSAndroid Build Coastguard Worker      cmd.extend(shlex.split(open(fn).read().rstrip("\n")))
2040*9e94795aSAndroid Build Coastguard Worker      fn = os.path.join(sourcedir, "RAMDISK_FRAGMENTS",
2041*9e94795aSAndroid Build Coastguard Worker                        ramdisk_fragment, "prebuilt_ramdisk")
2042*9e94795aSAndroid Build Coastguard Worker      # Use prebuilt image if found, else create ramdisk from supplied files.
2043*9e94795aSAndroid Build Coastguard Worker      if os.access(fn, os.F_OK):
2044*9e94795aSAndroid Build Coastguard Worker        ramdisk_fragment_pathname = fn
2045*9e94795aSAndroid Build Coastguard Worker      else:
2046*9e94795aSAndroid Build Coastguard Worker        ramdisk_fragment_root = os.path.join(
2047*9e94795aSAndroid Build Coastguard Worker            sourcedir, "RAMDISK_FRAGMENTS", ramdisk_fragment)
2048*9e94795aSAndroid Build Coastguard Worker        ramdisk_fragment_img = _MakeRamdisk(ramdisk_fragment_root,
2049*9e94795aSAndroid Build Coastguard Worker                                            ramdisk_format=ramdisk_format)
2050*9e94795aSAndroid Build Coastguard Worker        ramdisk_fragment_imgs.append(ramdisk_fragment_img)
2051*9e94795aSAndroid Build Coastguard Worker        ramdisk_fragment_pathname = ramdisk_fragment_img.name
2052*9e94795aSAndroid Build Coastguard Worker      cmd.extend(["--vendor_ramdisk_fragment", ramdisk_fragment_pathname])
2053*9e94795aSAndroid Build Coastguard Worker
2054*9e94795aSAndroid Build Coastguard Worker  RunAndCheckOutput(cmd)
2055*9e94795aSAndroid Build Coastguard Worker
2056*9e94795aSAndroid Build Coastguard Worker  # AVB: if enabled, calculate and add hash.
2057*9e94795aSAndroid Build Coastguard Worker  if info_dict.get("avb_enable") == "true":
2058*9e94795aSAndroid Build Coastguard Worker    avbtool = info_dict["avb_avbtool"]
2059*9e94795aSAndroid Build Coastguard Worker    part_size = info_dict[f'{partition_name}_size']
2060*9e94795aSAndroid Build Coastguard Worker    cmd = [avbtool, "add_hash_footer", "--image", img.name,
2061*9e94795aSAndroid Build Coastguard Worker           "--partition_size", str(part_size), "--partition_name", partition_name]
2062*9e94795aSAndroid Build Coastguard Worker    AppendAVBSigningArgs(cmd, partition_name)
2063*9e94795aSAndroid Build Coastguard Worker    args = info_dict.get(f'avb_{partition_name}_add_hash_footer_args')
2064*9e94795aSAndroid Build Coastguard Worker    if args and args.strip():
2065*9e94795aSAndroid Build Coastguard Worker      split_args = ResolveAVBSigningPathArgs(shlex.split(args))
2066*9e94795aSAndroid Build Coastguard Worker      cmd.extend(split_args)
2067*9e94795aSAndroid Build Coastguard Worker    RunAndCheckOutput(cmd)
2068*9e94795aSAndroid Build Coastguard Worker
2069*9e94795aSAndroid Build Coastguard Worker  img.seek(os.SEEK_SET, 0)
2070*9e94795aSAndroid Build Coastguard Worker  data = img.read()
2071*9e94795aSAndroid Build Coastguard Worker
2072*9e94795aSAndroid Build Coastguard Worker  for f in ramdisk_fragment_imgs:
2073*9e94795aSAndroid Build Coastguard Worker    f.close()
2074*9e94795aSAndroid Build Coastguard Worker  ramdisk_img.close()
2075*9e94795aSAndroid Build Coastguard Worker  img.close()
2076*9e94795aSAndroid Build Coastguard Worker
2077*9e94795aSAndroid Build Coastguard Worker  return data
2078*9e94795aSAndroid Build Coastguard Worker
2079*9e94795aSAndroid Build Coastguard Worker
2080*9e94795aSAndroid Build Coastguard Workerdef GetVendorBootImage(name, prebuilt_name, unpack_dir, tree_subdir,
2081*9e94795aSAndroid Build Coastguard Worker                       info_dict=None):
2082*9e94795aSAndroid Build Coastguard Worker  """Return a File object with the desired vendor boot image.
2083*9e94795aSAndroid Build Coastguard Worker
2084*9e94795aSAndroid Build Coastguard Worker  Look for it under 'unpack_dir'/IMAGES, otherwise construct it from
2085*9e94795aSAndroid Build Coastguard Worker  the source files in 'unpack_dir'/'tree_subdir'."""
2086*9e94795aSAndroid Build Coastguard Worker
2087*9e94795aSAndroid Build Coastguard Worker  prebuilt_path = os.path.join(unpack_dir, "IMAGES", prebuilt_name)
2088*9e94795aSAndroid Build Coastguard Worker  if os.path.exists(prebuilt_path):
2089*9e94795aSAndroid Build Coastguard Worker    logger.info("using prebuilt %s from IMAGES...", prebuilt_name)
2090*9e94795aSAndroid Build Coastguard Worker    return File.FromLocalFile(name, prebuilt_path)
2091*9e94795aSAndroid Build Coastguard Worker
2092*9e94795aSAndroid Build Coastguard Worker  logger.info("building image from target_files %s...", tree_subdir)
2093*9e94795aSAndroid Build Coastguard Worker
2094*9e94795aSAndroid Build Coastguard Worker  if info_dict is None:
2095*9e94795aSAndroid Build Coastguard Worker    info_dict = OPTIONS.info_dict
2096*9e94795aSAndroid Build Coastguard Worker
2097*9e94795aSAndroid Build Coastguard Worker  fs_config = "META/" + tree_subdir.lower() + "_filesystem_config.txt"
2098*9e94795aSAndroid Build Coastguard Worker  data = _BuildVendorBootImage(
2099*9e94795aSAndroid Build Coastguard Worker      os.path.join(unpack_dir, tree_subdir), os.path.join(unpack_dir, fs_config), "vendor_boot", info_dict)
2100*9e94795aSAndroid Build Coastguard Worker  if data:
2101*9e94795aSAndroid Build Coastguard Worker    return File(name, data)
2102*9e94795aSAndroid Build Coastguard Worker  return None
2103*9e94795aSAndroid Build Coastguard Worker
2104*9e94795aSAndroid Build Coastguard Worker
2105*9e94795aSAndroid Build Coastguard Workerdef GetVendorKernelBootImage(name, prebuilt_name, unpack_dir, tree_subdir,
2106*9e94795aSAndroid Build Coastguard Worker                             info_dict=None):
2107*9e94795aSAndroid Build Coastguard Worker  """Return a File object with the desired vendor kernel boot image.
2108*9e94795aSAndroid Build Coastguard Worker
2109*9e94795aSAndroid Build Coastguard Worker  Look for it under 'unpack_dir'/IMAGES, otherwise construct it from
2110*9e94795aSAndroid Build Coastguard Worker  the source files in 'unpack_dir'/'tree_subdir'."""
2111*9e94795aSAndroid Build Coastguard Worker
2112*9e94795aSAndroid Build Coastguard Worker  prebuilt_path = os.path.join(unpack_dir, "IMAGES", prebuilt_name)
2113*9e94795aSAndroid Build Coastguard Worker  if os.path.exists(prebuilt_path):
2114*9e94795aSAndroid Build Coastguard Worker    logger.info("using prebuilt %s from IMAGES...", prebuilt_name)
2115*9e94795aSAndroid Build Coastguard Worker    return File.FromLocalFile(name, prebuilt_path)
2116*9e94795aSAndroid Build Coastguard Worker
2117*9e94795aSAndroid Build Coastguard Worker  logger.info("building image from target_files %s...", tree_subdir)
2118*9e94795aSAndroid Build Coastguard Worker
2119*9e94795aSAndroid Build Coastguard Worker  if info_dict is None:
2120*9e94795aSAndroid Build Coastguard Worker    info_dict = OPTIONS.info_dict
2121*9e94795aSAndroid Build Coastguard Worker
2122*9e94795aSAndroid Build Coastguard Worker  data = _BuildVendorBootImage(
2123*9e94795aSAndroid Build Coastguard Worker      os.path.join(unpack_dir, tree_subdir), None, "vendor_kernel_boot", info_dict)
2124*9e94795aSAndroid Build Coastguard Worker  if data:
2125*9e94795aSAndroid Build Coastguard Worker    return File(name, data)
2126*9e94795aSAndroid Build Coastguard Worker  return None
2127*9e94795aSAndroid Build Coastguard Worker
2128*9e94795aSAndroid Build Coastguard Worker
2129*9e94795aSAndroid Build Coastguard Workerdef Gunzip(in_filename, out_filename):
2130*9e94795aSAndroid Build Coastguard Worker  """Gunzips the given gzip compressed file to a given output file."""
2131*9e94795aSAndroid Build Coastguard Worker  with gzip.open(in_filename, "rb") as in_file, \
2132*9e94795aSAndroid Build Coastguard Worker          open(out_filename, "wb") as out_file:
2133*9e94795aSAndroid Build Coastguard Worker    shutil.copyfileobj(in_file, out_file)
2134*9e94795aSAndroid Build Coastguard Worker
2135*9e94795aSAndroid Build Coastguard Worker
2136*9e94795aSAndroid Build Coastguard Workerdef UnzipSingleFile(input_zip: zipfile.ZipFile, info: zipfile.ZipInfo, dirname: str):
2137*9e94795aSAndroid Build Coastguard Worker  # According to https://stackoverflow.com/questions/434641/how-do-i-set-permissions-attributes-on-a-file-in-a-zip-file-using-pythons-zip/6297838#6297838
2138*9e94795aSAndroid Build Coastguard Worker  # higher bits of |external_attr| are unix file permission and types
2139*9e94795aSAndroid Build Coastguard Worker  unix_filetype = info.external_attr >> 16
2140*9e94795aSAndroid Build Coastguard Worker  file_perm = unix_filetype & 0o777
2141*9e94795aSAndroid Build Coastguard Worker
2142*9e94795aSAndroid Build Coastguard Worker  def CheckMask(a, mask):
2143*9e94795aSAndroid Build Coastguard Worker    return (a & mask) == mask
2144*9e94795aSAndroid Build Coastguard Worker
2145*9e94795aSAndroid Build Coastguard Worker  def IsSymlink(a):
2146*9e94795aSAndroid Build Coastguard Worker    return CheckMask(a, stat.S_IFLNK)
2147*9e94795aSAndroid Build Coastguard Worker
2148*9e94795aSAndroid Build Coastguard Worker  def IsDir(a):
2149*9e94795aSAndroid Build Coastguard Worker    return CheckMask(a, stat.S_IFDIR)
2150*9e94795aSAndroid Build Coastguard Worker  # python3.11 zipfile implementation doesn't handle symlink correctly
2151*9e94795aSAndroid Build Coastguard Worker  if not IsSymlink(unix_filetype):
2152*9e94795aSAndroid Build Coastguard Worker    target = input_zip.extract(info, dirname)
2153*9e94795aSAndroid Build Coastguard Worker    # We want to ensure that the file is at least read/writable by owner and readable by all users
2154*9e94795aSAndroid Build Coastguard Worker    if IsDir(unix_filetype):
2155*9e94795aSAndroid Build Coastguard Worker      os.chmod(target, file_perm | 0o755)
2156*9e94795aSAndroid Build Coastguard Worker    else:
2157*9e94795aSAndroid Build Coastguard Worker      os.chmod(target, file_perm | 0o644)
2158*9e94795aSAndroid Build Coastguard Worker    return target
2159*9e94795aSAndroid Build Coastguard Worker  if dirname is None:
2160*9e94795aSAndroid Build Coastguard Worker    dirname = os.getcwd()
2161*9e94795aSAndroid Build Coastguard Worker  target = os.path.join(dirname, info.filename)
2162*9e94795aSAndroid Build Coastguard Worker  os.makedirs(os.path.dirname(target), exist_ok=True)
2163*9e94795aSAndroid Build Coastguard Worker  if os.path.exists(target):
2164*9e94795aSAndroid Build Coastguard Worker    os.unlink(target)
2165*9e94795aSAndroid Build Coastguard Worker  os.symlink(input_zip.read(info).decode(), target)
2166*9e94795aSAndroid Build Coastguard Worker  return target
2167*9e94795aSAndroid Build Coastguard Worker
2168*9e94795aSAndroid Build Coastguard Worker
2169*9e94795aSAndroid Build Coastguard Workerdef UnzipToDir(filename, dirname, patterns=None):
2170*9e94795aSAndroid Build Coastguard Worker  """Unzips the archive to the given directory.
2171*9e94795aSAndroid Build Coastguard Worker
2172*9e94795aSAndroid Build Coastguard Worker  Args:
2173*9e94795aSAndroid Build Coastguard Worker    filename: The name of the zip file to unzip.
2174*9e94795aSAndroid Build Coastguard Worker    dirname: Where the unziped files will land.
2175*9e94795aSAndroid Build Coastguard Worker    patterns: Files to unzip from the archive. If omitted, will unzip the entire
2176*9e94795aSAndroid Build Coastguard Worker        archvie. Non-matching patterns will be filtered out. If there's no match
2177*9e94795aSAndroid Build Coastguard Worker        after the filtering, no file will be unzipped.
2178*9e94795aSAndroid Build Coastguard Worker  """
2179*9e94795aSAndroid Build Coastguard Worker  with zipfile.ZipFile(filename, allowZip64=True, mode="r") as input_zip:
2180*9e94795aSAndroid Build Coastguard Worker    # Filter out non-matching patterns. unzip will complain otherwise.
2181*9e94795aSAndroid Build Coastguard Worker    entries = input_zip.infolist()
2182*9e94795aSAndroid Build Coastguard Worker    # b/283033491
2183*9e94795aSAndroid Build Coastguard Worker    # Per https://en.wikipedia.org/wiki/ZIP_(file_format)#Central_directory_file_header
2184*9e94795aSAndroid Build Coastguard Worker    # In zip64 mode, central directory record's header_offset field might be
2185*9e94795aSAndroid Build Coastguard Worker    # set to 0xFFFFFFFF if header offset is > 2^32. In this case, the extra
2186*9e94795aSAndroid Build Coastguard Worker    # fields will contain an 8 byte little endian integer at offset 20
2187*9e94795aSAndroid Build Coastguard Worker    # to indicate the actual local header offset.
2188*9e94795aSAndroid Build Coastguard Worker    # As of python3.11, python does not handle zip64 central directories
2189*9e94795aSAndroid Build Coastguard Worker    # correctly, so we will manually do the parsing here.
2190*9e94795aSAndroid Build Coastguard Worker
2191*9e94795aSAndroid Build Coastguard Worker    # ZIP64 central directory extra field has two required fields:
2192*9e94795aSAndroid Build Coastguard Worker    # 2 bytes header ID and 2 bytes size field. Thes two require fields have
2193*9e94795aSAndroid Build Coastguard Worker    # a total size of 4 bytes. Then it has three other 8 bytes field, followed
2194*9e94795aSAndroid Build Coastguard Worker    # by a 4 byte disk number field. The last disk number field is not required
2195*9e94795aSAndroid Build Coastguard Worker    # to be present, but if it is present, the total size of extra field will be
2196*9e94795aSAndroid Build Coastguard Worker    # divisible by 8(because 2+2+4+8*n is always going to be multiple of 8)
2197*9e94795aSAndroid Build Coastguard Worker    # Most extra fields are optional, but when they appear, their must appear
2198*9e94795aSAndroid Build Coastguard Worker    # in the order defined by zip64 spec. Since file header offset is the 2nd
2199*9e94795aSAndroid Build Coastguard Worker    # to last field in zip64 spec, it will only be at last 8 bytes or last 12-4
2200*9e94795aSAndroid Build Coastguard Worker    # bytes, depending on whether disk number is present.
2201*9e94795aSAndroid Build Coastguard Worker    for entry in entries:
2202*9e94795aSAndroid Build Coastguard Worker      if entry.header_offset == 0xFFFFFFFF:
2203*9e94795aSAndroid Build Coastguard Worker        if len(entry.extra) % 8 == 0:
2204*9e94795aSAndroid Build Coastguard Worker          entry.header_offset = int.from_bytes(entry.extra[-12:-4], "little")
2205*9e94795aSAndroid Build Coastguard Worker        else:
2206*9e94795aSAndroid Build Coastguard Worker          entry.header_offset = int.from_bytes(entry.extra[-8:], "little")
2207*9e94795aSAndroid Build Coastguard Worker    if patterns is not None:
2208*9e94795aSAndroid Build Coastguard Worker      filtered = [info for info in entries if any(
2209*9e94795aSAndroid Build Coastguard Worker          [fnmatch.fnmatch(info.filename, p) for p in patterns])]
2210*9e94795aSAndroid Build Coastguard Worker
2211*9e94795aSAndroid Build Coastguard Worker      # There isn't any matching files. Don't unzip anything.
2212*9e94795aSAndroid Build Coastguard Worker      if not filtered:
2213*9e94795aSAndroid Build Coastguard Worker        return
2214*9e94795aSAndroid Build Coastguard Worker      for info in filtered:
2215*9e94795aSAndroid Build Coastguard Worker        UnzipSingleFile(input_zip, info, dirname)
2216*9e94795aSAndroid Build Coastguard Worker    else:
2217*9e94795aSAndroid Build Coastguard Worker      for info in entries:
2218*9e94795aSAndroid Build Coastguard Worker        UnzipSingleFile(input_zip, info, dirname)
2219*9e94795aSAndroid Build Coastguard Worker
2220*9e94795aSAndroid Build Coastguard Worker
2221*9e94795aSAndroid Build Coastguard Workerdef UnzipTemp(filename, patterns=None):
2222*9e94795aSAndroid Build Coastguard Worker  """Unzips the given archive into a temporary directory and returns the name.
2223*9e94795aSAndroid Build Coastguard Worker
2224*9e94795aSAndroid Build Coastguard Worker  Args:
2225*9e94795aSAndroid Build Coastguard Worker    filename: If filename is of the form "foo.zip+bar.zip", unzip foo.zip into
2226*9e94795aSAndroid Build Coastguard Worker    a temp dir, then unzip bar.zip into that_dir/BOOTABLE_IMAGES.
2227*9e94795aSAndroid Build Coastguard Worker
2228*9e94795aSAndroid Build Coastguard Worker    patterns: Files to unzip from the archive. If omitted, will unzip the entire
2229*9e94795aSAndroid Build Coastguard Worker    archvie.
2230*9e94795aSAndroid Build Coastguard Worker
2231*9e94795aSAndroid Build Coastguard Worker  Returns:
2232*9e94795aSAndroid Build Coastguard Worker    The name of the temporary directory.
2233*9e94795aSAndroid Build Coastguard Worker  """
2234*9e94795aSAndroid Build Coastguard Worker
2235*9e94795aSAndroid Build Coastguard Worker  tmp = MakeTempDir(prefix="targetfiles-")
2236*9e94795aSAndroid Build Coastguard Worker  m = re.match(r"^(.*[.]zip)\+(.*[.]zip)$", filename, re.IGNORECASE)
2237*9e94795aSAndroid Build Coastguard Worker  if m:
2238*9e94795aSAndroid Build Coastguard Worker    UnzipToDir(m.group(1), tmp, patterns)
2239*9e94795aSAndroid Build Coastguard Worker    UnzipToDir(m.group(2), os.path.join(tmp, "BOOTABLE_IMAGES"), patterns)
2240*9e94795aSAndroid Build Coastguard Worker    filename = m.group(1)
2241*9e94795aSAndroid Build Coastguard Worker  else:
2242*9e94795aSAndroid Build Coastguard Worker    UnzipToDir(filename, tmp, patterns)
2243*9e94795aSAndroid Build Coastguard Worker
2244*9e94795aSAndroid Build Coastguard Worker  return tmp
2245*9e94795aSAndroid Build Coastguard Worker
2246*9e94795aSAndroid Build Coastguard Worker
2247*9e94795aSAndroid Build Coastguard Workerdef GetUserImage(which, tmpdir, input_zip,
2248*9e94795aSAndroid Build Coastguard Worker                 info_dict=None,
2249*9e94795aSAndroid Build Coastguard Worker                 allow_shared_blocks=None,
2250*9e94795aSAndroid Build Coastguard Worker                 reset_file_map=False):
2251*9e94795aSAndroid Build Coastguard Worker  """Returns an Image object suitable for passing to BlockImageDiff.
2252*9e94795aSAndroid Build Coastguard Worker
2253*9e94795aSAndroid Build Coastguard Worker  This function loads the specified image from the given path. If the specified
2254*9e94795aSAndroid Build Coastguard Worker  image is sparse, it also performs additional processing for OTA purpose. For
2255*9e94795aSAndroid Build Coastguard Worker  example, it always adds block 0 to clobbered blocks list. It also detects
2256*9e94795aSAndroid Build Coastguard Worker  files that cannot be reconstructed from the block list, for whom we should
2257*9e94795aSAndroid Build Coastguard Worker  avoid applying imgdiff.
2258*9e94795aSAndroid Build Coastguard Worker
2259*9e94795aSAndroid Build Coastguard Worker  Args:
2260*9e94795aSAndroid Build Coastguard Worker    which: The partition name.
2261*9e94795aSAndroid Build Coastguard Worker    tmpdir: The directory that contains the prebuilt image and block map file.
2262*9e94795aSAndroid Build Coastguard Worker    input_zip: The target-files ZIP archive.
2263*9e94795aSAndroid Build Coastguard Worker    info_dict: The dict to be looked up for relevant info.
2264*9e94795aSAndroid Build Coastguard Worker    allow_shared_blocks: If image is sparse, whether having shared blocks is
2265*9e94795aSAndroid Build Coastguard Worker        allowed. If none, it is looked up from info_dict.
2266*9e94795aSAndroid Build Coastguard Worker    reset_file_map: If true and image is sparse, reset file map before returning
2267*9e94795aSAndroid Build Coastguard Worker        the image.
2268*9e94795aSAndroid Build Coastguard Worker  Returns:
2269*9e94795aSAndroid Build Coastguard Worker    A Image object. If it is a sparse image and reset_file_map is False, the
2270*9e94795aSAndroid Build Coastguard Worker    image will have file_map info loaded.
2271*9e94795aSAndroid Build Coastguard Worker  """
2272*9e94795aSAndroid Build Coastguard Worker  if info_dict is None:
2273*9e94795aSAndroid Build Coastguard Worker    info_dict = LoadInfoDict(input_zip)
2274*9e94795aSAndroid Build Coastguard Worker
2275*9e94795aSAndroid Build Coastguard Worker  is_sparse = IsSparseImage(os.path.join(tmpdir, "IMAGES", which + ".img"))
2276*9e94795aSAndroid Build Coastguard Worker
2277*9e94795aSAndroid Build Coastguard Worker  # When target uses 'BOARD_EXT4_SHARE_DUP_BLOCKS := true', images may contain
2278*9e94795aSAndroid Build Coastguard Worker  # shared blocks (i.e. some blocks will show up in multiple files' block
2279*9e94795aSAndroid Build Coastguard Worker  # list). We can only allocate such shared blocks to the first "owner", and
2280*9e94795aSAndroid Build Coastguard Worker  # disable imgdiff for all later occurrences.
2281*9e94795aSAndroid Build Coastguard Worker  if allow_shared_blocks is None:
2282*9e94795aSAndroid Build Coastguard Worker    allow_shared_blocks = info_dict.get("ext4_share_dup_blocks") == "true"
2283*9e94795aSAndroid Build Coastguard Worker
2284*9e94795aSAndroid Build Coastguard Worker  if is_sparse:
2285*9e94795aSAndroid Build Coastguard Worker    img = GetSparseImage(which, tmpdir, input_zip, allow_shared_blocks)
2286*9e94795aSAndroid Build Coastguard Worker    if reset_file_map:
2287*9e94795aSAndroid Build Coastguard Worker      img.ResetFileMap()
2288*9e94795aSAndroid Build Coastguard Worker    return img
2289*9e94795aSAndroid Build Coastguard Worker  return GetNonSparseImage(which, tmpdir)
2290*9e94795aSAndroid Build Coastguard Worker
2291*9e94795aSAndroid Build Coastguard Worker
2292*9e94795aSAndroid Build Coastguard Workerdef GetNonSparseImage(which, tmpdir):
2293*9e94795aSAndroid Build Coastguard Worker  """Returns a Image object suitable for passing to BlockImageDiff.
2294*9e94795aSAndroid Build Coastguard Worker
2295*9e94795aSAndroid Build Coastguard Worker  This function loads the specified non-sparse image from the given path.
2296*9e94795aSAndroid Build Coastguard Worker
2297*9e94795aSAndroid Build Coastguard Worker  Args:
2298*9e94795aSAndroid Build Coastguard Worker    which: The partition name.
2299*9e94795aSAndroid Build Coastguard Worker    tmpdir: The directory that contains the prebuilt image and block map file.
2300*9e94795aSAndroid Build Coastguard Worker  Returns:
2301*9e94795aSAndroid Build Coastguard Worker    A Image object.
2302*9e94795aSAndroid Build Coastguard Worker  """
2303*9e94795aSAndroid Build Coastguard Worker  path = os.path.join(tmpdir, "IMAGES", which + ".img")
2304*9e94795aSAndroid Build Coastguard Worker  mappath = os.path.join(tmpdir, "IMAGES", which + ".map")
2305*9e94795aSAndroid Build Coastguard Worker
2306*9e94795aSAndroid Build Coastguard Worker  # The image and map files must have been created prior to calling
2307*9e94795aSAndroid Build Coastguard Worker  # ota_from_target_files.py (since LMP).
2308*9e94795aSAndroid Build Coastguard Worker  assert os.path.exists(path) and os.path.exists(mappath)
2309*9e94795aSAndroid Build Coastguard Worker
2310*9e94795aSAndroid Build Coastguard Worker  return images.FileImage(path)
2311*9e94795aSAndroid Build Coastguard Worker
2312*9e94795aSAndroid Build Coastguard Worker
2313*9e94795aSAndroid Build Coastguard Workerdef GetSparseImage(which, tmpdir, input_zip, allow_shared_blocks):
2314*9e94795aSAndroid Build Coastguard Worker  """Returns a SparseImage object suitable for passing to BlockImageDiff.
2315*9e94795aSAndroid Build Coastguard Worker
2316*9e94795aSAndroid Build Coastguard Worker  This function loads the specified sparse image from the given path, and
2317*9e94795aSAndroid Build Coastguard Worker  performs additional processing for OTA purpose. For example, it always adds
2318*9e94795aSAndroid Build Coastguard Worker  block 0 to clobbered blocks list. It also detects files that cannot be
2319*9e94795aSAndroid Build Coastguard Worker  reconstructed from the block list, for whom we should avoid applying imgdiff.
2320*9e94795aSAndroid Build Coastguard Worker
2321*9e94795aSAndroid Build Coastguard Worker  Args:
2322*9e94795aSAndroid Build Coastguard Worker    which: The partition name, e.g. "system", "vendor".
2323*9e94795aSAndroid Build Coastguard Worker    tmpdir: The directory that contains the prebuilt image and block map file.
2324*9e94795aSAndroid Build Coastguard Worker    input_zip: The target-files ZIP archive.
2325*9e94795aSAndroid Build Coastguard Worker    allow_shared_blocks: Whether having shared blocks is allowed.
2326*9e94795aSAndroid Build Coastguard Worker  Returns:
2327*9e94795aSAndroid Build Coastguard Worker    A SparseImage object, with file_map info loaded.
2328*9e94795aSAndroid Build Coastguard Worker  """
2329*9e94795aSAndroid Build Coastguard Worker  path = os.path.join(tmpdir, "IMAGES", which + ".img")
2330*9e94795aSAndroid Build Coastguard Worker  mappath = os.path.join(tmpdir, "IMAGES", which + ".map")
2331*9e94795aSAndroid Build Coastguard Worker
2332*9e94795aSAndroid Build Coastguard Worker  # The image and map files must have been created prior to calling
2333*9e94795aSAndroid Build Coastguard Worker  # ota_from_target_files.py (since LMP).
2334*9e94795aSAndroid Build Coastguard Worker  assert os.path.exists(path) and os.path.exists(mappath)
2335*9e94795aSAndroid Build Coastguard Worker
2336*9e94795aSAndroid Build Coastguard Worker  # In ext4 filesystems, block 0 might be changed even being mounted R/O. We add
2337*9e94795aSAndroid Build Coastguard Worker  # it to clobbered_blocks so that it will be written to the target
2338*9e94795aSAndroid Build Coastguard Worker  # unconditionally. Note that they are still part of care_map. (Bug: 20939131)
2339*9e94795aSAndroid Build Coastguard Worker  clobbered_blocks = "0"
2340*9e94795aSAndroid Build Coastguard Worker
2341*9e94795aSAndroid Build Coastguard Worker  image = sparse_img.SparseImage(
2342*9e94795aSAndroid Build Coastguard Worker      path, mappath, clobbered_blocks, allow_shared_blocks=allow_shared_blocks)
2343*9e94795aSAndroid Build Coastguard Worker
2344*9e94795aSAndroid Build Coastguard Worker  # block.map may contain less blocks, because mke2fs may skip allocating blocks
2345*9e94795aSAndroid Build Coastguard Worker  # if they contain all zeros. We can't reconstruct such a file from its block
2346*9e94795aSAndroid Build Coastguard Worker  # list. Tag such entries accordingly. (Bug: 65213616)
2347*9e94795aSAndroid Build Coastguard Worker  for entry in image.file_map:
2348*9e94795aSAndroid Build Coastguard Worker    # Skip artificial names, such as "__ZERO", "__NONZERO-1".
2349*9e94795aSAndroid Build Coastguard Worker    if not entry.startswith('/'):
2350*9e94795aSAndroid Build Coastguard Worker      continue
2351*9e94795aSAndroid Build Coastguard Worker
2352*9e94795aSAndroid Build Coastguard Worker    # "/system/framework/am.jar" => "SYSTEM/framework/am.jar". Note that the
2353*9e94795aSAndroid Build Coastguard Worker    # filename listed in system.map may contain an additional leading slash
2354*9e94795aSAndroid Build Coastguard Worker    # (i.e. "//system/framework/am.jar"). Using lstrip to get consistent
2355*9e94795aSAndroid Build Coastguard Worker    # results.
2356*9e94795aSAndroid Build Coastguard Worker    # And handle another special case, where files not under /system
2357*9e94795aSAndroid Build Coastguard Worker    # (e.g. "/sbin/charger") are packed under ROOT/ in a target_files.zip.
2358*9e94795aSAndroid Build Coastguard Worker    arcname = entry.lstrip('/')
2359*9e94795aSAndroid Build Coastguard Worker    if which == 'system' and not arcname.startswith('system'):
2360*9e94795aSAndroid Build Coastguard Worker      arcname = 'ROOT/' + arcname
2361*9e94795aSAndroid Build Coastguard Worker    else:
2362*9e94795aSAndroid Build Coastguard Worker      arcname = arcname.replace(which, which.upper(), 1)
2363*9e94795aSAndroid Build Coastguard Worker
2364*9e94795aSAndroid Build Coastguard Worker    assert arcname in input_zip.namelist(), \
2365*9e94795aSAndroid Build Coastguard Worker        "Failed to find the ZIP entry for {}".format(entry)
2366*9e94795aSAndroid Build Coastguard Worker
2367*9e94795aSAndroid Build Coastguard Worker    info = input_zip.getinfo(arcname)
2368*9e94795aSAndroid Build Coastguard Worker    ranges = image.file_map[entry]
2369*9e94795aSAndroid Build Coastguard Worker
2370*9e94795aSAndroid Build Coastguard Worker    # If a RangeSet has been tagged as using shared blocks while loading the
2371*9e94795aSAndroid Build Coastguard Worker    # image, check the original block list to determine its completeness. Note
2372*9e94795aSAndroid Build Coastguard Worker    # that the 'incomplete' flag would be tagged to the original RangeSet only.
2373*9e94795aSAndroid Build Coastguard Worker    if ranges.extra.get('uses_shared_blocks'):
2374*9e94795aSAndroid Build Coastguard Worker      ranges = ranges.extra['uses_shared_blocks']
2375*9e94795aSAndroid Build Coastguard Worker
2376*9e94795aSAndroid Build Coastguard Worker    if RoundUpTo4K(info.file_size) > ranges.size() * 4096:
2377*9e94795aSAndroid Build Coastguard Worker      ranges.extra['incomplete'] = True
2378*9e94795aSAndroid Build Coastguard Worker
2379*9e94795aSAndroid Build Coastguard Worker  return image
2380*9e94795aSAndroid Build Coastguard Worker
2381*9e94795aSAndroid Build Coastguard Worker
2382*9e94795aSAndroid Build Coastguard Workerdef GetKeyPasswords(keylist):
2383*9e94795aSAndroid Build Coastguard Worker  """Given a list of keys, prompt the user to enter passwords for
2384*9e94795aSAndroid Build Coastguard Worker  those which require them.  Return a {key: password} dict.  password
2385*9e94795aSAndroid Build Coastguard Worker  will be None if the key has no password."""
2386*9e94795aSAndroid Build Coastguard Worker
2387*9e94795aSAndroid Build Coastguard Worker  no_passwords = []
2388*9e94795aSAndroid Build Coastguard Worker  need_passwords = []
2389*9e94795aSAndroid Build Coastguard Worker  key_passwords = {}
2390*9e94795aSAndroid Build Coastguard Worker  devnull = open("/dev/null", "w+b")
2391*9e94795aSAndroid Build Coastguard Worker
2392*9e94795aSAndroid Build Coastguard Worker  # sorted() can't compare strings to None, so convert Nones to strings
2393*9e94795aSAndroid Build Coastguard Worker  for k in sorted(keylist, key=lambda x: x if x is not None else ""):
2394*9e94795aSAndroid Build Coastguard Worker    # We don't need a password for things that aren't really keys.
2395*9e94795aSAndroid Build Coastguard Worker    if k in SPECIAL_CERT_STRINGS or k is None:
2396*9e94795aSAndroid Build Coastguard Worker      no_passwords.append(k)
2397*9e94795aSAndroid Build Coastguard Worker      continue
2398*9e94795aSAndroid Build Coastguard Worker
2399*9e94795aSAndroid Build Coastguard Worker    p = Run(["openssl", "pkcs8", "-in", k+OPTIONS.private_key_suffix,
2400*9e94795aSAndroid Build Coastguard Worker             "-inform", "DER", "-nocrypt"],
2401*9e94795aSAndroid Build Coastguard Worker            stdin=devnull.fileno(),
2402*9e94795aSAndroid Build Coastguard Worker            stdout=devnull.fileno(),
2403*9e94795aSAndroid Build Coastguard Worker            stderr=subprocess.STDOUT)
2404*9e94795aSAndroid Build Coastguard Worker    p.communicate()
2405*9e94795aSAndroid Build Coastguard Worker    if p.returncode == 0:
2406*9e94795aSAndroid Build Coastguard Worker      # Definitely an unencrypted key.
2407*9e94795aSAndroid Build Coastguard Worker      no_passwords.append(k)
2408*9e94795aSAndroid Build Coastguard Worker    else:
2409*9e94795aSAndroid Build Coastguard Worker      p = Run(["openssl", "pkcs8", "-in", k+OPTIONS.private_key_suffix,
2410*9e94795aSAndroid Build Coastguard Worker               "-inform", "DER", "-passin", "pass:"],
2411*9e94795aSAndroid Build Coastguard Worker              stdin=devnull.fileno(),
2412*9e94795aSAndroid Build Coastguard Worker              stdout=devnull.fileno(),
2413*9e94795aSAndroid Build Coastguard Worker              stderr=subprocess.PIPE)
2414*9e94795aSAndroid Build Coastguard Worker      _, stderr = p.communicate()
2415*9e94795aSAndroid Build Coastguard Worker      if p.returncode == 0:
2416*9e94795aSAndroid Build Coastguard Worker        # Encrypted key with empty string as password.
2417*9e94795aSAndroid Build Coastguard Worker        key_passwords[k] = ''
2418*9e94795aSAndroid Build Coastguard Worker      elif stderr.startswith('Error decrypting key'):
2419*9e94795aSAndroid Build Coastguard Worker        # Definitely encrypted key.
2420*9e94795aSAndroid Build Coastguard Worker        # It would have said "Error reading key" if it didn't parse correctly.
2421*9e94795aSAndroid Build Coastguard Worker        need_passwords.append(k)
2422*9e94795aSAndroid Build Coastguard Worker      else:
2423*9e94795aSAndroid Build Coastguard Worker        # Potentially, a type of key that openssl doesn't understand.
2424*9e94795aSAndroid Build Coastguard Worker        # We'll let the routines in signapk.jar handle it.
2425*9e94795aSAndroid Build Coastguard Worker        no_passwords.append(k)
2426*9e94795aSAndroid Build Coastguard Worker  devnull.close()
2427*9e94795aSAndroid Build Coastguard Worker
2428*9e94795aSAndroid Build Coastguard Worker  key_passwords.update(PasswordManager().GetPasswords(need_passwords))
2429*9e94795aSAndroid Build Coastguard Worker  key_passwords.update(dict.fromkeys(no_passwords))
2430*9e94795aSAndroid Build Coastguard Worker  return key_passwords
2431*9e94795aSAndroid Build Coastguard Worker
2432*9e94795aSAndroid Build Coastguard Worker
2433*9e94795aSAndroid Build Coastguard Workerdef GetMinSdkVersion(apk_name):
2434*9e94795aSAndroid Build Coastguard Worker  """Gets the minSdkVersion declared in the APK.
2435*9e94795aSAndroid Build Coastguard Worker
2436*9e94795aSAndroid Build Coastguard Worker  It calls OPTIONS.aapt2_path to query the embedded minSdkVersion from the given
2437*9e94795aSAndroid Build Coastguard Worker  APK file. This can be both a decimal number (API Level) or a codename.
2438*9e94795aSAndroid Build Coastguard Worker
2439*9e94795aSAndroid Build Coastguard Worker  Args:
2440*9e94795aSAndroid Build Coastguard Worker    apk_name: The APK filename.
2441*9e94795aSAndroid Build Coastguard Worker
2442*9e94795aSAndroid Build Coastguard Worker  Returns:
2443*9e94795aSAndroid Build Coastguard Worker    The parsed SDK version string.
2444*9e94795aSAndroid Build Coastguard Worker
2445*9e94795aSAndroid Build Coastguard Worker  Raises:
2446*9e94795aSAndroid Build Coastguard Worker    ExternalError: On failing to obtain the min SDK version.
2447*9e94795aSAndroid Build Coastguard Worker  """
2448*9e94795aSAndroid Build Coastguard Worker  proc = Run(
2449*9e94795aSAndroid Build Coastguard Worker      [OPTIONS.aapt2_path, "dump", "badging", apk_name], stdout=subprocess.PIPE,
2450*9e94795aSAndroid Build Coastguard Worker      stderr=subprocess.PIPE)
2451*9e94795aSAndroid Build Coastguard Worker  stdoutdata, stderrdata = proc.communicate()
2452*9e94795aSAndroid Build Coastguard Worker  if proc.returncode != 0:
2453*9e94795aSAndroid Build Coastguard Worker    raise ExternalError(
2454*9e94795aSAndroid Build Coastguard Worker        "Failed to obtain minSdkVersion for {}: aapt2 return code {}:\n{}\n{}".format(
2455*9e94795aSAndroid Build Coastguard Worker            apk_name, proc.returncode, stdoutdata, stderrdata))
2456*9e94795aSAndroid Build Coastguard Worker
2457*9e94795aSAndroid Build Coastguard Worker  is_split_apk = False
2458*9e94795aSAndroid Build Coastguard Worker  for line in stdoutdata.split("\n"):
2459*9e94795aSAndroid Build Coastguard Worker    # See b/353837347 , split APKs do not have sdk version defined,
2460*9e94795aSAndroid Build Coastguard Worker    # so we default to 21 as split APKs are only supported since SDK
2461*9e94795aSAndroid Build Coastguard Worker    # 21.
2462*9e94795aSAndroid Build Coastguard Worker    if (re.search(r"split=[\"'].*[\"']", line)):
2463*9e94795aSAndroid Build Coastguard Worker      is_split_apk = True
2464*9e94795aSAndroid Build Coastguard Worker    # Due to ag/24161708, looking for lines such as minSdkVersion:'23',minSdkVersion:'M'
2465*9e94795aSAndroid Build Coastguard Worker    # or sdkVersion:'23', sdkVersion:'M'.
2466*9e94795aSAndroid Build Coastguard Worker    m = re.match(r'(?:minSdkVersion|sdkVersion):\'([^\']*)\'', line)
2467*9e94795aSAndroid Build Coastguard Worker    if m:
2468*9e94795aSAndroid Build Coastguard Worker      return m.group(1)
2469*9e94795aSAndroid Build Coastguard Worker  if is_split_apk:
2470*9e94795aSAndroid Build Coastguard Worker    logger.info("%s is a split APK, it does not have minimum SDK version"
2471*9e94795aSAndroid Build Coastguard Worker                " defined. Defaulting to 21 because split APK isn't supported"
2472*9e94795aSAndroid Build Coastguard Worker                " before that.", apk_name)
2473*9e94795aSAndroid Build Coastguard Worker    return 21
2474*9e94795aSAndroid Build Coastguard Worker  raise ExternalError("No minSdkVersion returned by aapt2 for apk: {}".format(apk_name))
2475*9e94795aSAndroid Build Coastguard Worker
2476*9e94795aSAndroid Build Coastguard Worker
2477*9e94795aSAndroid Build Coastguard Workerdef GetMinSdkVersionInt(apk_name, codename_to_api_level_map):
2478*9e94795aSAndroid Build Coastguard Worker  """Returns the minSdkVersion declared in the APK as a number (API Level).
2479*9e94795aSAndroid Build Coastguard Worker
2480*9e94795aSAndroid Build Coastguard Worker  If minSdkVersion is set to a codename, it is translated to a number using the
2481*9e94795aSAndroid Build Coastguard Worker  provided map.
2482*9e94795aSAndroid Build Coastguard Worker
2483*9e94795aSAndroid Build Coastguard Worker  Args:
2484*9e94795aSAndroid Build Coastguard Worker    apk_name: The APK filename.
2485*9e94795aSAndroid Build Coastguard Worker
2486*9e94795aSAndroid Build Coastguard Worker  Returns:
2487*9e94795aSAndroid Build Coastguard Worker    The parsed SDK version number.
2488*9e94795aSAndroid Build Coastguard Worker
2489*9e94795aSAndroid Build Coastguard Worker  Raises:
2490*9e94795aSAndroid Build Coastguard Worker    ExternalError: On failing to get the min SDK version number.
2491*9e94795aSAndroid Build Coastguard Worker  """
2492*9e94795aSAndroid Build Coastguard Worker  version = GetMinSdkVersion(apk_name)
2493*9e94795aSAndroid Build Coastguard Worker  try:
2494*9e94795aSAndroid Build Coastguard Worker    return int(version)
2495*9e94795aSAndroid Build Coastguard Worker  except ValueError:
2496*9e94795aSAndroid Build Coastguard Worker    # Not a decimal number.
2497*9e94795aSAndroid Build Coastguard Worker    #
2498*9e94795aSAndroid Build Coastguard Worker    # It could be either a straight codename, e.g.
2499*9e94795aSAndroid Build Coastguard Worker    #     UpsideDownCake
2500*9e94795aSAndroid Build Coastguard Worker    #
2501*9e94795aSAndroid Build Coastguard Worker    # Or a codename with API fingerprint SHA, e.g.
2502*9e94795aSAndroid Build Coastguard Worker    #     UpsideDownCake.e7d3947f14eb9dc4fec25ff6c5f8563e
2503*9e94795aSAndroid Build Coastguard Worker    #
2504*9e94795aSAndroid Build Coastguard Worker    # Extract the codename and try and map it to a version number.
2505*9e94795aSAndroid Build Coastguard Worker    split = version.split(".")
2506*9e94795aSAndroid Build Coastguard Worker    codename = split[0]
2507*9e94795aSAndroid Build Coastguard Worker    if codename in codename_to_api_level_map:
2508*9e94795aSAndroid Build Coastguard Worker      return codename_to_api_level_map[codename]
2509*9e94795aSAndroid Build Coastguard Worker    raise ExternalError(
2510*9e94795aSAndroid Build Coastguard Worker        "Unknown codename: '{}' from minSdkVersion: '{}'. Known codenames: {}".format(
2511*9e94795aSAndroid Build Coastguard Worker            codename, version, codename_to_api_level_map))
2512*9e94795aSAndroid Build Coastguard Worker
2513*9e94795aSAndroid Build Coastguard Worker
2514*9e94795aSAndroid Build Coastguard Workerdef SignFile(input_name, output_name, key, password, min_api_level=None,
2515*9e94795aSAndroid Build Coastguard Worker             codename_to_api_level_map=None, whole_file=False,
2516*9e94795aSAndroid Build Coastguard Worker             extra_signapk_args=None):
2517*9e94795aSAndroid Build Coastguard Worker  """Sign the input_name zip/jar/apk, producing output_name.  Use the
2518*9e94795aSAndroid Build Coastguard Worker  given key and password (the latter may be None if the key does not
2519*9e94795aSAndroid Build Coastguard Worker  have a password.
2520*9e94795aSAndroid Build Coastguard Worker
2521*9e94795aSAndroid Build Coastguard Worker  If whole_file is true, use the "-w" option to SignApk to embed a
2522*9e94795aSAndroid Build Coastguard Worker  signature that covers the whole file in the archive comment of the
2523*9e94795aSAndroid Build Coastguard Worker  zip file.
2524*9e94795aSAndroid Build Coastguard Worker
2525*9e94795aSAndroid Build Coastguard Worker  min_api_level is the API Level (int) of the oldest platform this file may end
2526*9e94795aSAndroid Build Coastguard Worker  up on. If not specified for an APK, the API Level is obtained by interpreting
2527*9e94795aSAndroid Build Coastguard Worker  the minSdkVersion attribute of the APK's AndroidManifest.xml.
2528*9e94795aSAndroid Build Coastguard Worker
2529*9e94795aSAndroid Build Coastguard Worker  codename_to_api_level_map is needed to translate the codename which may be
2530*9e94795aSAndroid Build Coastguard Worker  encountered as the APK's minSdkVersion.
2531*9e94795aSAndroid Build Coastguard Worker
2532*9e94795aSAndroid Build Coastguard Worker  Caller may optionally specify extra args to be passed to SignApk, which
2533*9e94795aSAndroid Build Coastguard Worker  defaults to OPTIONS.extra_signapk_args if omitted.
2534*9e94795aSAndroid Build Coastguard Worker  """
2535*9e94795aSAndroid Build Coastguard Worker  if codename_to_api_level_map is None:
2536*9e94795aSAndroid Build Coastguard Worker    codename_to_api_level_map = {}
2537*9e94795aSAndroid Build Coastguard Worker  if extra_signapk_args is None:
2538*9e94795aSAndroid Build Coastguard Worker    extra_signapk_args = OPTIONS.extra_signapk_args
2539*9e94795aSAndroid Build Coastguard Worker
2540*9e94795aSAndroid Build Coastguard Worker  java_library_path = os.path.join(
2541*9e94795aSAndroid Build Coastguard Worker      OPTIONS.search_path, OPTIONS.signapk_shared_library_path)
2542*9e94795aSAndroid Build Coastguard Worker
2543*9e94795aSAndroid Build Coastguard Worker  cmd = ([OPTIONS.java_path] + OPTIONS.java_args +
2544*9e94795aSAndroid Build Coastguard Worker         ["-Djava.library.path=" + java_library_path,
2545*9e94795aSAndroid Build Coastguard Worker          "-jar", os.path.join(OPTIONS.search_path, OPTIONS.signapk_path)] +
2546*9e94795aSAndroid Build Coastguard Worker         extra_signapk_args)
2547*9e94795aSAndroid Build Coastguard Worker  if whole_file:
2548*9e94795aSAndroid Build Coastguard Worker    cmd.append("-w")
2549*9e94795aSAndroid Build Coastguard Worker
2550*9e94795aSAndroid Build Coastguard Worker  min_sdk_version = min_api_level
2551*9e94795aSAndroid Build Coastguard Worker  if min_sdk_version is None:
2552*9e94795aSAndroid Build Coastguard Worker    if not whole_file:
2553*9e94795aSAndroid Build Coastguard Worker      min_sdk_version = GetMinSdkVersionInt(
2554*9e94795aSAndroid Build Coastguard Worker          input_name, codename_to_api_level_map)
2555*9e94795aSAndroid Build Coastguard Worker  if min_sdk_version is not None:
2556*9e94795aSAndroid Build Coastguard Worker    cmd.extend(["--min-sdk-version", str(min_sdk_version)])
2557*9e94795aSAndroid Build Coastguard Worker
2558*9e94795aSAndroid Build Coastguard Worker  cmd.extend([key + OPTIONS.public_key_suffix,
2559*9e94795aSAndroid Build Coastguard Worker              key + OPTIONS.private_key_suffix,
2560*9e94795aSAndroid Build Coastguard Worker              input_name, output_name])
2561*9e94795aSAndroid Build Coastguard Worker
2562*9e94795aSAndroid Build Coastguard Worker  proc = Run(cmd, stdin=subprocess.PIPE)
2563*9e94795aSAndroid Build Coastguard Worker  if password is not None:
2564*9e94795aSAndroid Build Coastguard Worker    password += "\n"
2565*9e94795aSAndroid Build Coastguard Worker  stdoutdata, _ = proc.communicate(password)
2566*9e94795aSAndroid Build Coastguard Worker  if proc.returncode != 0:
2567*9e94795aSAndroid Build Coastguard Worker    raise ExternalError(
2568*9e94795aSAndroid Build Coastguard Worker        "Failed to run {}: return code {}:\n{}".format(cmd,
2569*9e94795aSAndroid Build Coastguard Worker                                                       proc.returncode, stdoutdata))
2570*9e94795aSAndroid Build Coastguard Worker
2571*9e94795aSAndroid Build Coastguard Worker
2572*9e94795aSAndroid Build Coastguard Workerdef CheckSize(data, target, info_dict):
2573*9e94795aSAndroid Build Coastguard Worker  """Checks the data string passed against the max size limit.
2574*9e94795aSAndroid Build Coastguard Worker
2575*9e94795aSAndroid Build Coastguard Worker  For non-AVB images, raise exception if the data is too big. Print a warning
2576*9e94795aSAndroid Build Coastguard Worker  if the data is nearing the maximum size.
2577*9e94795aSAndroid Build Coastguard Worker
2578*9e94795aSAndroid Build Coastguard Worker  For AVB images, the actual image size should be identical to the limit.
2579*9e94795aSAndroid Build Coastguard Worker
2580*9e94795aSAndroid Build Coastguard Worker  Args:
2581*9e94795aSAndroid Build Coastguard Worker    data: A string that contains all the data for the partition.
2582*9e94795aSAndroid Build Coastguard Worker    target: The partition name. The ".img" suffix is optional.
2583*9e94795aSAndroid Build Coastguard Worker    info_dict: The dict to be looked up for relevant info.
2584*9e94795aSAndroid Build Coastguard Worker  """
2585*9e94795aSAndroid Build Coastguard Worker  if target.endswith(".img"):
2586*9e94795aSAndroid Build Coastguard Worker    target = target[:-4]
2587*9e94795aSAndroid Build Coastguard Worker  mount_point = "/" + target
2588*9e94795aSAndroid Build Coastguard Worker
2589*9e94795aSAndroid Build Coastguard Worker  fs_type = None
2590*9e94795aSAndroid Build Coastguard Worker  limit = None
2591*9e94795aSAndroid Build Coastguard Worker  if info_dict["fstab"]:
2592*9e94795aSAndroid Build Coastguard Worker    if mount_point == "/userdata":
2593*9e94795aSAndroid Build Coastguard Worker      mount_point = "/data"
2594*9e94795aSAndroid Build Coastguard Worker    p = info_dict["fstab"][mount_point]
2595*9e94795aSAndroid Build Coastguard Worker    fs_type = p.fs_type
2596*9e94795aSAndroid Build Coastguard Worker    device = p.device
2597*9e94795aSAndroid Build Coastguard Worker    if "/" in device:
2598*9e94795aSAndroid Build Coastguard Worker      device = device[device.rfind("/")+1:]
2599*9e94795aSAndroid Build Coastguard Worker    limit = info_dict.get(device + "_size", 0)
2600*9e94795aSAndroid Build Coastguard Worker    if isinstance(limit, str):
2601*9e94795aSAndroid Build Coastguard Worker      limit = int(limit, 0)
2602*9e94795aSAndroid Build Coastguard Worker  if not fs_type or not limit:
2603*9e94795aSAndroid Build Coastguard Worker    return
2604*9e94795aSAndroid Build Coastguard Worker
2605*9e94795aSAndroid Build Coastguard Worker  size = len(data)
2606*9e94795aSAndroid Build Coastguard Worker  # target could be 'userdata' or 'cache'. They should follow the non-AVB image
2607*9e94795aSAndroid Build Coastguard Worker  # path.
2608*9e94795aSAndroid Build Coastguard Worker  if info_dict.get("avb_enable") == "true" and target in AVB_PARTITIONS:
2609*9e94795aSAndroid Build Coastguard Worker    if size != limit:
2610*9e94795aSAndroid Build Coastguard Worker      raise ExternalError(
2611*9e94795aSAndroid Build Coastguard Worker          "Mismatching image size for %s: expected %d actual %d" % (
2612*9e94795aSAndroid Build Coastguard Worker              target, limit, size))
2613*9e94795aSAndroid Build Coastguard Worker  else:
2614*9e94795aSAndroid Build Coastguard Worker    pct = float(size) * 100.0 / limit
2615*9e94795aSAndroid Build Coastguard Worker    msg = "%s size (%d) is %.2f%% of limit (%d)" % (target, size, pct, limit)
2616*9e94795aSAndroid Build Coastguard Worker    if pct >= 99.0:
2617*9e94795aSAndroid Build Coastguard Worker      raise ExternalError(msg)
2618*9e94795aSAndroid Build Coastguard Worker
2619*9e94795aSAndroid Build Coastguard Worker    if pct >= 95.0:
2620*9e94795aSAndroid Build Coastguard Worker      logger.warning("\n  WARNING: %s\n", msg)
2621*9e94795aSAndroid Build Coastguard Worker    else:
2622*9e94795aSAndroid Build Coastguard Worker      logger.info("  %s", msg)
2623*9e94795aSAndroid Build Coastguard Worker
2624*9e94795aSAndroid Build Coastguard Worker
2625*9e94795aSAndroid Build Coastguard Workerdef ReadApkCerts(tf_zip):
2626*9e94795aSAndroid Build Coastguard Worker  """Parses the APK certs info from a given target-files zip.
2627*9e94795aSAndroid Build Coastguard Worker
2628*9e94795aSAndroid Build Coastguard Worker  Given a target-files ZipFile, parses the META/apkcerts.txt entry and returns a
2629*9e94795aSAndroid Build Coastguard Worker  tuple with the following elements: (1) a dictionary that maps packages to
2630*9e94795aSAndroid Build Coastguard Worker  certs (based on the "certificate" and "private_key" attributes in the file;
2631*9e94795aSAndroid Build Coastguard Worker  (2) a string representing the extension of compressed APKs in the target files
2632*9e94795aSAndroid Build Coastguard Worker  (e.g ".gz", ".bro").
2633*9e94795aSAndroid Build Coastguard Worker
2634*9e94795aSAndroid Build Coastguard Worker  Args:
2635*9e94795aSAndroid Build Coastguard Worker    tf_zip: The input target_files ZipFile (already open).
2636*9e94795aSAndroid Build Coastguard Worker
2637*9e94795aSAndroid Build Coastguard Worker  Returns:
2638*9e94795aSAndroid Build Coastguard Worker    (certmap, ext): certmap is a dictionary that maps packages to certs; ext is
2639*9e94795aSAndroid Build Coastguard Worker        the extension string of compressed APKs (e.g. ".gz"), or None if there's
2640*9e94795aSAndroid Build Coastguard Worker        no compressed APKs.
2641*9e94795aSAndroid Build Coastguard Worker  """
2642*9e94795aSAndroid Build Coastguard Worker  certmap = {}
2643*9e94795aSAndroid Build Coastguard Worker  compressed_extension = None
2644*9e94795aSAndroid Build Coastguard Worker
2645*9e94795aSAndroid Build Coastguard Worker  # META/apkcerts.txt contains the info for _all_ the packages known at build
2646*9e94795aSAndroid Build Coastguard Worker  # time. Filter out the ones that are not installed.
2647*9e94795aSAndroid Build Coastguard Worker  installed_files = set()
2648*9e94795aSAndroid Build Coastguard Worker  for name in tf_zip.namelist():
2649*9e94795aSAndroid Build Coastguard Worker    basename = os.path.basename(name)
2650*9e94795aSAndroid Build Coastguard Worker    if basename:
2651*9e94795aSAndroid Build Coastguard Worker      installed_files.add(basename)
2652*9e94795aSAndroid Build Coastguard Worker
2653*9e94795aSAndroid Build Coastguard Worker  for line in tf_zip.read('META/apkcerts.txt').decode().split('\n'):
2654*9e94795aSAndroid Build Coastguard Worker    line = line.strip()
2655*9e94795aSAndroid Build Coastguard Worker    if not line:
2656*9e94795aSAndroid Build Coastguard Worker      continue
2657*9e94795aSAndroid Build Coastguard Worker    m = re.match(
2658*9e94795aSAndroid Build Coastguard Worker        r'^name="(?P<NAME>.*)"\s+certificate="(?P<CERT>.*)"\s+'
2659*9e94795aSAndroid Build Coastguard Worker        r'private_key="(?P<PRIVKEY>.*?)"(\s+compressed="(?P<COMPRESSED>.*?)")?'
2660*9e94795aSAndroid Build Coastguard Worker        r'(\s+partition="(?P<PARTITION>.*?)")?$',
2661*9e94795aSAndroid Build Coastguard Worker        line)
2662*9e94795aSAndroid Build Coastguard Worker    if not m:
2663*9e94795aSAndroid Build Coastguard Worker      continue
2664*9e94795aSAndroid Build Coastguard Worker
2665*9e94795aSAndroid Build Coastguard Worker    matches = m.groupdict()
2666*9e94795aSAndroid Build Coastguard Worker    cert = matches["CERT"]
2667*9e94795aSAndroid Build Coastguard Worker    privkey = matches["PRIVKEY"]
2668*9e94795aSAndroid Build Coastguard Worker    name = matches["NAME"]
2669*9e94795aSAndroid Build Coastguard Worker    this_compressed_extension = matches["COMPRESSED"]
2670*9e94795aSAndroid Build Coastguard Worker
2671*9e94795aSAndroid Build Coastguard Worker    public_key_suffix_len = len(OPTIONS.public_key_suffix)
2672*9e94795aSAndroid Build Coastguard Worker    private_key_suffix_len = len(OPTIONS.private_key_suffix)
2673*9e94795aSAndroid Build Coastguard Worker    if cert in SPECIAL_CERT_STRINGS and not privkey:
2674*9e94795aSAndroid Build Coastguard Worker      certmap[name] = cert
2675*9e94795aSAndroid Build Coastguard Worker    elif (cert.endswith(OPTIONS.public_key_suffix) and
2676*9e94795aSAndroid Build Coastguard Worker          privkey.endswith(OPTIONS.private_key_suffix) and
2677*9e94795aSAndroid Build Coastguard Worker          cert[:-public_key_suffix_len] == privkey[:-private_key_suffix_len]):
2678*9e94795aSAndroid Build Coastguard Worker      certmap[name] = cert[:-public_key_suffix_len]
2679*9e94795aSAndroid Build Coastguard Worker    else:
2680*9e94795aSAndroid Build Coastguard Worker      raise ValueError("Failed to parse line from apkcerts.txt:\n" + line)
2681*9e94795aSAndroid Build Coastguard Worker
2682*9e94795aSAndroid Build Coastguard Worker    if not this_compressed_extension:
2683*9e94795aSAndroid Build Coastguard Worker      continue
2684*9e94795aSAndroid Build Coastguard Worker
2685*9e94795aSAndroid Build Coastguard Worker    # Only count the installed files.
2686*9e94795aSAndroid Build Coastguard Worker    filename = name + '.' + this_compressed_extension
2687*9e94795aSAndroid Build Coastguard Worker    if filename not in installed_files:
2688*9e94795aSAndroid Build Coastguard Worker      continue
2689*9e94795aSAndroid Build Coastguard Worker
2690*9e94795aSAndroid Build Coastguard Worker    # Make sure that all the values in the compression map have the same
2691*9e94795aSAndroid Build Coastguard Worker    # extension. We don't support multiple compression methods in the same
2692*9e94795aSAndroid Build Coastguard Worker    # system image.
2693*9e94795aSAndroid Build Coastguard Worker    if compressed_extension:
2694*9e94795aSAndroid Build Coastguard Worker      if this_compressed_extension != compressed_extension:
2695*9e94795aSAndroid Build Coastguard Worker        raise ValueError(
2696*9e94795aSAndroid Build Coastguard Worker            "Multiple compressed extensions: {} vs {}".format(
2697*9e94795aSAndroid Build Coastguard Worker                compressed_extension, this_compressed_extension))
2698*9e94795aSAndroid Build Coastguard Worker    else:
2699*9e94795aSAndroid Build Coastguard Worker      compressed_extension = this_compressed_extension
2700*9e94795aSAndroid Build Coastguard Worker
2701*9e94795aSAndroid Build Coastguard Worker  return (certmap,
2702*9e94795aSAndroid Build Coastguard Worker          ("." + compressed_extension) if compressed_extension else None)
2703*9e94795aSAndroid Build Coastguard Worker
2704*9e94795aSAndroid Build Coastguard Worker
2705*9e94795aSAndroid Build Coastguard WorkerCOMMON_DOCSTRING = """
2706*9e94795aSAndroid Build Coastguard WorkerGlobal options
2707*9e94795aSAndroid Build Coastguard Worker
2708*9e94795aSAndroid Build Coastguard Worker  -p  (--path) <dir>
2709*9e94795aSAndroid Build Coastguard Worker      Prepend <dir>/bin to the list of places to search for binaries run by this
2710*9e94795aSAndroid Build Coastguard Worker      script, and expect to find jars in <dir>/framework.
2711*9e94795aSAndroid Build Coastguard Worker
2712*9e94795aSAndroid Build Coastguard Worker  -s  (--device_specific) <file>
2713*9e94795aSAndroid Build Coastguard Worker      Path to the Python module containing device-specific releasetools code.
2714*9e94795aSAndroid Build Coastguard Worker
2715*9e94795aSAndroid Build Coastguard Worker  -x  (--extra) <key=value>
2716*9e94795aSAndroid Build Coastguard Worker      Add a key/value pair to the 'extras' dict, which device-specific extension
2717*9e94795aSAndroid Build Coastguard Worker      code may look at.
2718*9e94795aSAndroid Build Coastguard Worker
2719*9e94795aSAndroid Build Coastguard Worker  -v  (--verbose)
2720*9e94795aSAndroid Build Coastguard Worker      Show command lines being executed.
2721*9e94795aSAndroid Build Coastguard Worker
2722*9e94795aSAndroid Build Coastguard Worker  -h  (--help)
2723*9e94795aSAndroid Build Coastguard Worker      Display this usage message and exit.
2724*9e94795aSAndroid Build Coastguard Worker
2725*9e94795aSAndroid Build Coastguard Worker  --logfile <file>
2726*9e94795aSAndroid Build Coastguard Worker      Put verbose logs to specified file (regardless of --verbose option.)
2727*9e94795aSAndroid Build Coastguard Worker"""
2728*9e94795aSAndroid Build Coastguard Worker
2729*9e94795aSAndroid Build Coastguard Worker
2730*9e94795aSAndroid Build Coastguard Workerdef Usage(docstring):
2731*9e94795aSAndroid Build Coastguard Worker  print(docstring.rstrip("\n"))
2732*9e94795aSAndroid Build Coastguard Worker  print(COMMON_DOCSTRING)
2733*9e94795aSAndroid Build Coastguard Worker
2734*9e94795aSAndroid Build Coastguard Worker
2735*9e94795aSAndroid Build Coastguard Workerdef ParseOptions(argv,
2736*9e94795aSAndroid Build Coastguard Worker                 docstring,
2737*9e94795aSAndroid Build Coastguard Worker                 extra_opts="", extra_long_opts=(),
2738*9e94795aSAndroid Build Coastguard Worker                 extra_option_handler: Iterable[OptionHandler] = None):
2739*9e94795aSAndroid Build Coastguard Worker  """Parse the options in argv and return any arguments that aren't
2740*9e94795aSAndroid Build Coastguard Worker  flags.  docstring is the calling module's docstring, to be displayed
2741*9e94795aSAndroid Build Coastguard Worker  for errors and -h.  extra_opts and extra_long_opts are for flags
2742*9e94795aSAndroid Build Coastguard Worker  defined by the caller, which are processed by passing them to
2743*9e94795aSAndroid Build Coastguard Worker  extra_option_handler."""
2744*9e94795aSAndroid Build Coastguard Worker  extra_long_opts = list(extra_long_opts)
2745*9e94795aSAndroid Build Coastguard Worker  if not isinstance(extra_option_handler, Iterable):
2746*9e94795aSAndroid Build Coastguard Worker    extra_option_handler = [extra_option_handler]
2747*9e94795aSAndroid Build Coastguard Worker
2748*9e94795aSAndroid Build Coastguard Worker  for handler in extra_option_handler:
2749*9e94795aSAndroid Build Coastguard Worker    if isinstance(handler, OptionHandler):
2750*9e94795aSAndroid Build Coastguard Worker      extra_long_opts.extend(handler.extra_long_opts)
2751*9e94795aSAndroid Build Coastguard Worker
2752*9e94795aSAndroid Build Coastguard Worker  try:
2753*9e94795aSAndroid Build Coastguard Worker    opts, args = getopt.getopt(
2754*9e94795aSAndroid Build Coastguard Worker        argv, "hvp:s:x:" + extra_opts,
2755*9e94795aSAndroid Build Coastguard Worker        ["help", "verbose", "path=", "signapk_path=",
2756*9e94795aSAndroid Build Coastguard Worker         "signapk_shared_library_path=", "extra_signapk_args=", "aapt2_path=",
2757*9e94795aSAndroid Build Coastguard Worker         "java_path=", "java_args=", "android_jar_path=", "public_key_suffix=",
2758*9e94795aSAndroid Build Coastguard Worker         "private_key_suffix=", "boot_signer_path=", "boot_signer_args=",
2759*9e94795aSAndroid Build Coastguard Worker         "verity_signer_path=", "verity_signer_args=", "device_specific=",
2760*9e94795aSAndroid Build Coastguard Worker         "extra=", "logfile="] + list(extra_long_opts))
2761*9e94795aSAndroid Build Coastguard Worker  except getopt.GetoptError as err:
2762*9e94795aSAndroid Build Coastguard Worker    Usage(docstring)
2763*9e94795aSAndroid Build Coastguard Worker    print("**", str(err), "**")
2764*9e94795aSAndroid Build Coastguard Worker    sys.exit(2)
2765*9e94795aSAndroid Build Coastguard Worker
2766*9e94795aSAndroid Build Coastguard Worker  for o, a in opts:
2767*9e94795aSAndroid Build Coastguard Worker    if o in ("-h", "--help"):
2768*9e94795aSAndroid Build Coastguard Worker      Usage(docstring)
2769*9e94795aSAndroid Build Coastguard Worker      sys.exit()
2770*9e94795aSAndroid Build Coastguard Worker    elif o in ("-v", "--verbose"):
2771*9e94795aSAndroid Build Coastguard Worker      OPTIONS.verbose = True
2772*9e94795aSAndroid Build Coastguard Worker    elif o in ("-p", "--path"):
2773*9e94795aSAndroid Build Coastguard Worker      OPTIONS.search_path = a
2774*9e94795aSAndroid Build Coastguard Worker    elif o in ("--signapk_path",):
2775*9e94795aSAndroid Build Coastguard Worker      OPTIONS.signapk_path = a
2776*9e94795aSAndroid Build Coastguard Worker    elif o in ("--signapk_shared_library_path",):
2777*9e94795aSAndroid Build Coastguard Worker      OPTIONS.signapk_shared_library_path = a
2778*9e94795aSAndroid Build Coastguard Worker    elif o in ("--extra_signapk_args",):
2779*9e94795aSAndroid Build Coastguard Worker      OPTIONS.extra_signapk_args = shlex.split(a)
2780*9e94795aSAndroid Build Coastguard Worker    elif o in ("--aapt2_path",):
2781*9e94795aSAndroid Build Coastguard Worker      OPTIONS.aapt2_path = a
2782*9e94795aSAndroid Build Coastguard Worker    elif o in ("--java_path",):
2783*9e94795aSAndroid Build Coastguard Worker      OPTIONS.java_path = a
2784*9e94795aSAndroid Build Coastguard Worker    elif o in ("--java_args",):
2785*9e94795aSAndroid Build Coastguard Worker      OPTIONS.java_args = shlex.split(a)
2786*9e94795aSAndroid Build Coastguard Worker    elif o in ("--android_jar_path",):
2787*9e94795aSAndroid Build Coastguard Worker      OPTIONS.android_jar_path = a
2788*9e94795aSAndroid Build Coastguard Worker    elif o in ("--public_key_suffix",):
2789*9e94795aSAndroid Build Coastguard Worker      OPTIONS.public_key_suffix = a
2790*9e94795aSAndroid Build Coastguard Worker    elif o in ("--private_key_suffix",):
2791*9e94795aSAndroid Build Coastguard Worker      OPTIONS.private_key_suffix = a
2792*9e94795aSAndroid Build Coastguard Worker    elif o in ("--boot_signer_path",):
2793*9e94795aSAndroid Build Coastguard Worker      raise ValueError(
2794*9e94795aSAndroid Build Coastguard Worker          "--boot_signer_path is no longer supported, please switch to AVB")
2795*9e94795aSAndroid Build Coastguard Worker    elif o in ("--boot_signer_args",):
2796*9e94795aSAndroid Build Coastguard Worker      raise ValueError(
2797*9e94795aSAndroid Build Coastguard Worker          "--boot_signer_args is no longer supported, please switch to AVB")
2798*9e94795aSAndroid Build Coastguard Worker    elif o in ("--verity_signer_path",):
2799*9e94795aSAndroid Build Coastguard Worker      raise ValueError(
2800*9e94795aSAndroid Build Coastguard Worker          "--verity_signer_path is no longer supported, please switch to AVB")
2801*9e94795aSAndroid Build Coastguard Worker    elif o in ("--verity_signer_args",):
2802*9e94795aSAndroid Build Coastguard Worker      raise ValueError(
2803*9e94795aSAndroid Build Coastguard Worker          "--verity_signer_args is no longer supported, please switch to AVB")
2804*9e94795aSAndroid Build Coastguard Worker    elif o in ("-s", "--device_specific"):
2805*9e94795aSAndroid Build Coastguard Worker      OPTIONS.device_specific = a
2806*9e94795aSAndroid Build Coastguard Worker    elif o in ("-x", "--extra"):
2807*9e94795aSAndroid Build Coastguard Worker      key, value = a.split("=", 1)
2808*9e94795aSAndroid Build Coastguard Worker      OPTIONS.extras[key] = value
2809*9e94795aSAndroid Build Coastguard Worker    elif o in ("--logfile",):
2810*9e94795aSAndroid Build Coastguard Worker      OPTIONS.logfile = a
2811*9e94795aSAndroid Build Coastguard Worker    else:
2812*9e94795aSAndroid Build Coastguard Worker      if extra_option_handler is None:
2813*9e94795aSAndroid Build Coastguard Worker        raise ValueError("unknown option \"%s\"" % (o,))
2814*9e94795aSAndroid Build Coastguard Worker      success = False
2815*9e94795aSAndroid Build Coastguard Worker      for handler in extra_option_handler:
2816*9e94795aSAndroid Build Coastguard Worker        if isinstance(handler, OptionHandler):
2817*9e94795aSAndroid Build Coastguard Worker          if handler.handler(o, a):
2818*9e94795aSAndroid Build Coastguard Worker            success = True
2819*9e94795aSAndroid Build Coastguard Worker            break
2820*9e94795aSAndroid Build Coastguard Worker        elif handler(o, a):
2821*9e94795aSAndroid Build Coastguard Worker          success = True
2822*9e94795aSAndroid Build Coastguard Worker          break
2823*9e94795aSAndroid Build Coastguard Worker      if not success:
2824*9e94795aSAndroid Build Coastguard Worker        raise ValueError("unknown option \"%s\"" % (o,))
2825*9e94795aSAndroid Build Coastguard Worker
2826*9e94795aSAndroid Build Coastguard Worker
2827*9e94795aSAndroid Build Coastguard Worker  if OPTIONS.search_path:
2828*9e94795aSAndroid Build Coastguard Worker    os.environ["PATH"] = (os.path.join(OPTIONS.search_path, "bin") +
2829*9e94795aSAndroid Build Coastguard Worker                          os.pathsep + os.environ["PATH"])
2830*9e94795aSAndroid Build Coastguard Worker
2831*9e94795aSAndroid Build Coastguard Worker  return args
2832*9e94795aSAndroid Build Coastguard Worker
2833*9e94795aSAndroid Build Coastguard Worker
2834*9e94795aSAndroid Build Coastguard Workerdef MakeTempFile(prefix='tmp', suffix=''):
2835*9e94795aSAndroid Build Coastguard Worker  """Make a temp file and add it to the list of things to be deleted
2836*9e94795aSAndroid Build Coastguard Worker  when Cleanup() is called.  Return the filename."""
2837*9e94795aSAndroid Build Coastguard Worker  fd, fn = tempfile.mkstemp(prefix=prefix, suffix=suffix)
2838*9e94795aSAndroid Build Coastguard Worker  os.close(fd)
2839*9e94795aSAndroid Build Coastguard Worker  OPTIONS.tempfiles.append(fn)
2840*9e94795aSAndroid Build Coastguard Worker  return fn
2841*9e94795aSAndroid Build Coastguard Worker
2842*9e94795aSAndroid Build Coastguard Worker
2843*9e94795aSAndroid Build Coastguard Workerdef MakeTempDir(prefix='tmp', suffix=''):
2844*9e94795aSAndroid Build Coastguard Worker  """Makes a temporary dir that will be cleaned up with a call to Cleanup().
2845*9e94795aSAndroid Build Coastguard Worker
2846*9e94795aSAndroid Build Coastguard Worker  Returns:
2847*9e94795aSAndroid Build Coastguard Worker    The absolute pathname of the new directory.
2848*9e94795aSAndroid Build Coastguard Worker  """
2849*9e94795aSAndroid Build Coastguard Worker  dir_name = tempfile.mkdtemp(suffix=suffix, prefix=prefix)
2850*9e94795aSAndroid Build Coastguard Worker  OPTIONS.tempfiles.append(dir_name)
2851*9e94795aSAndroid Build Coastguard Worker  return dir_name
2852*9e94795aSAndroid Build Coastguard Worker
2853*9e94795aSAndroid Build Coastguard Worker
2854*9e94795aSAndroid Build Coastguard Workerdef Cleanup():
2855*9e94795aSAndroid Build Coastguard Worker  for i in OPTIONS.tempfiles:
2856*9e94795aSAndroid Build Coastguard Worker    if not os.path.exists(i):
2857*9e94795aSAndroid Build Coastguard Worker      continue
2858*9e94795aSAndroid Build Coastguard Worker    if os.path.isdir(i):
2859*9e94795aSAndroid Build Coastguard Worker      shutil.rmtree(i, ignore_errors=True)
2860*9e94795aSAndroid Build Coastguard Worker    else:
2861*9e94795aSAndroid Build Coastguard Worker      os.remove(i)
2862*9e94795aSAndroid Build Coastguard Worker  del OPTIONS.tempfiles[:]
2863*9e94795aSAndroid Build Coastguard Worker
2864*9e94795aSAndroid Build Coastguard Worker
2865*9e94795aSAndroid Build Coastguard Workerclass PasswordManager(object):
2866*9e94795aSAndroid Build Coastguard Worker  def __init__(self):
2867*9e94795aSAndroid Build Coastguard Worker    self.editor = os.getenv("EDITOR")
2868*9e94795aSAndroid Build Coastguard Worker    self.pwfile = os.getenv("ANDROID_PW_FILE")
2869*9e94795aSAndroid Build Coastguard Worker
2870*9e94795aSAndroid Build Coastguard Worker  def GetPasswords(self, items):
2871*9e94795aSAndroid Build Coastguard Worker    """Get passwords corresponding to each string in 'items',
2872*9e94795aSAndroid Build Coastguard Worker    returning a dict.  (The dict may have keys in addition to the
2873*9e94795aSAndroid Build Coastguard Worker    values in 'items'.)
2874*9e94795aSAndroid Build Coastguard Worker
2875*9e94795aSAndroid Build Coastguard Worker    Uses the passwords in $ANDROID_PW_FILE if available, letting the
2876*9e94795aSAndroid Build Coastguard Worker    user edit that file to add more needed passwords.  If no editor is
2877*9e94795aSAndroid Build Coastguard Worker    available, or $ANDROID_PW_FILE isn't define, prompts the user
2878*9e94795aSAndroid Build Coastguard Worker    interactively in the ordinary way.
2879*9e94795aSAndroid Build Coastguard Worker    """
2880*9e94795aSAndroid Build Coastguard Worker
2881*9e94795aSAndroid Build Coastguard Worker    current = self.ReadFile()
2882*9e94795aSAndroid Build Coastguard Worker
2883*9e94795aSAndroid Build Coastguard Worker    first = True
2884*9e94795aSAndroid Build Coastguard Worker    while True:
2885*9e94795aSAndroid Build Coastguard Worker      missing = []
2886*9e94795aSAndroid Build Coastguard Worker      for i in items:
2887*9e94795aSAndroid Build Coastguard Worker        if i not in current or not current[i]:
2888*9e94795aSAndroid Build Coastguard Worker          missing.append(i)
2889*9e94795aSAndroid Build Coastguard Worker      # Are all the passwords already in the file?
2890*9e94795aSAndroid Build Coastguard Worker      if not missing:
2891*9e94795aSAndroid Build Coastguard Worker        return current
2892*9e94795aSAndroid Build Coastguard Worker
2893*9e94795aSAndroid Build Coastguard Worker      for i in missing:
2894*9e94795aSAndroid Build Coastguard Worker        current[i] = ""
2895*9e94795aSAndroid Build Coastguard Worker
2896*9e94795aSAndroid Build Coastguard Worker      if not first:
2897*9e94795aSAndroid Build Coastguard Worker        print("key file %s still missing some passwords." % (self.pwfile,))
2898*9e94795aSAndroid Build Coastguard Worker        if sys.version_info[0] >= 3:
2899*9e94795aSAndroid Build Coastguard Worker          raw_input = input  # pylint: disable=redefined-builtin
2900*9e94795aSAndroid Build Coastguard Worker        answer = raw_input("try to edit again? [y]> ").strip()
2901*9e94795aSAndroid Build Coastguard Worker        if answer and answer[0] not in 'yY':
2902*9e94795aSAndroid Build Coastguard Worker          raise RuntimeError("key passwords unavailable")
2903*9e94795aSAndroid Build Coastguard Worker      first = False
2904*9e94795aSAndroid Build Coastguard Worker
2905*9e94795aSAndroid Build Coastguard Worker      current = self.UpdateAndReadFile(current)
2906*9e94795aSAndroid Build Coastguard Worker
2907*9e94795aSAndroid Build Coastguard Worker  def PromptResult(self, current):  # pylint: disable=no-self-use
2908*9e94795aSAndroid Build Coastguard Worker    """Prompt the user to enter a value (password) for each key in
2909*9e94795aSAndroid Build Coastguard Worker    'current' whose value is fales.  Returns a new dict with all the
2910*9e94795aSAndroid Build Coastguard Worker    values.
2911*9e94795aSAndroid Build Coastguard Worker    """
2912*9e94795aSAndroid Build Coastguard Worker    result = {}
2913*9e94795aSAndroid Build Coastguard Worker    for k, v in sorted(current.items()):
2914*9e94795aSAndroid Build Coastguard Worker      if v:
2915*9e94795aSAndroid Build Coastguard Worker        result[k] = v
2916*9e94795aSAndroid Build Coastguard Worker      else:
2917*9e94795aSAndroid Build Coastguard Worker        while True:
2918*9e94795aSAndroid Build Coastguard Worker          result[k] = getpass.getpass(
2919*9e94795aSAndroid Build Coastguard Worker              "Enter password for %s key> " % k).strip()
2920*9e94795aSAndroid Build Coastguard Worker          if result[k]:
2921*9e94795aSAndroid Build Coastguard Worker            break
2922*9e94795aSAndroid Build Coastguard Worker    return result
2923*9e94795aSAndroid Build Coastguard Worker
2924*9e94795aSAndroid Build Coastguard Worker  def UpdateAndReadFile(self, current):
2925*9e94795aSAndroid Build Coastguard Worker    if not self.editor or not self.pwfile:
2926*9e94795aSAndroid Build Coastguard Worker      return self.PromptResult(current)
2927*9e94795aSAndroid Build Coastguard Worker
2928*9e94795aSAndroid Build Coastguard Worker    f = open(self.pwfile, "w")
2929*9e94795aSAndroid Build Coastguard Worker    os.chmod(self.pwfile, 0o600)
2930*9e94795aSAndroid Build Coastguard Worker    f.write("# Enter key passwords between the [[[ ]]] brackets.\n")
2931*9e94795aSAndroid Build Coastguard Worker    f.write("# (Additional spaces are harmless.)\n\n")
2932*9e94795aSAndroid Build Coastguard Worker
2933*9e94795aSAndroid Build Coastguard Worker    first_line = None
2934*9e94795aSAndroid Build Coastguard Worker    sorted_list = sorted([(not v, k, v) for (k, v) in current.items()])
2935*9e94795aSAndroid Build Coastguard Worker    for i, (_, k, v) in enumerate(sorted_list):
2936*9e94795aSAndroid Build Coastguard Worker      f.write("[[[  %s  ]]] %s\n" % (v, k))
2937*9e94795aSAndroid Build Coastguard Worker      if not v and first_line is None:
2938*9e94795aSAndroid Build Coastguard Worker        # position cursor on first line with no password.
2939*9e94795aSAndroid Build Coastguard Worker        first_line = i + 4
2940*9e94795aSAndroid Build Coastguard Worker    f.close()
2941*9e94795aSAndroid Build Coastguard Worker
2942*9e94795aSAndroid Build Coastguard Worker    RunAndCheckOutput([self.editor, "+%d" % (first_line,), self.pwfile])
2943*9e94795aSAndroid Build Coastguard Worker
2944*9e94795aSAndroid Build Coastguard Worker    return self.ReadFile()
2945*9e94795aSAndroid Build Coastguard Worker
2946*9e94795aSAndroid Build Coastguard Worker  def ReadFile(self):
2947*9e94795aSAndroid Build Coastguard Worker    result = {}
2948*9e94795aSAndroid Build Coastguard Worker    if self.pwfile is None:
2949*9e94795aSAndroid Build Coastguard Worker      return result
2950*9e94795aSAndroid Build Coastguard Worker    try:
2951*9e94795aSAndroid Build Coastguard Worker      f = open(self.pwfile, "r")
2952*9e94795aSAndroid Build Coastguard Worker      for line in f:
2953*9e94795aSAndroid Build Coastguard Worker        line = line.strip()
2954*9e94795aSAndroid Build Coastguard Worker        if not line or line[0] == '#':
2955*9e94795aSAndroid Build Coastguard Worker          continue
2956*9e94795aSAndroid Build Coastguard Worker        m = re.match(r"^\[\[\[\s*(.*?)\s*\]\]\]\s*(\S+)$", line)
2957*9e94795aSAndroid Build Coastguard Worker        if not m:
2958*9e94795aSAndroid Build Coastguard Worker          logger.warning("Failed to parse password file: %s", line)
2959*9e94795aSAndroid Build Coastguard Worker        else:
2960*9e94795aSAndroid Build Coastguard Worker          result[m.group(2)] = m.group(1)
2961*9e94795aSAndroid Build Coastguard Worker      f.close()
2962*9e94795aSAndroid Build Coastguard Worker    except IOError as e:
2963*9e94795aSAndroid Build Coastguard Worker      if e.errno != errno.ENOENT:
2964*9e94795aSAndroid Build Coastguard Worker        logger.exception("Error reading password file:")
2965*9e94795aSAndroid Build Coastguard Worker    return result
2966*9e94795aSAndroid Build Coastguard Worker
2967*9e94795aSAndroid Build Coastguard Worker
2968*9e94795aSAndroid Build Coastguard Workerdef ZipWrite(zip_file, filename, arcname=None, perms=0o644,
2969*9e94795aSAndroid Build Coastguard Worker             compress_type=None):
2970*9e94795aSAndroid Build Coastguard Worker
2971*9e94795aSAndroid Build Coastguard Worker  # http://b/18015246
2972*9e94795aSAndroid Build Coastguard Worker  # Python 2.7's zipfile implementation wrongly thinks that zip64 is required
2973*9e94795aSAndroid Build Coastguard Worker  # for files larger than 2GiB. We can work around this by adjusting their
2974*9e94795aSAndroid Build Coastguard Worker  # limit. Note that `zipfile.writestr()` will not work for strings larger than
2975*9e94795aSAndroid Build Coastguard Worker  # 2GiB. The Python interpreter sometimes rejects strings that large (though
2976*9e94795aSAndroid Build Coastguard Worker  # it isn't clear to me exactly what circumstances cause this).
2977*9e94795aSAndroid Build Coastguard Worker  # `zipfile.write()` must be used directly to work around this.
2978*9e94795aSAndroid Build Coastguard Worker  #
2979*9e94795aSAndroid Build Coastguard Worker  # This mess can be avoided if we port to python3.
2980*9e94795aSAndroid Build Coastguard Worker  saved_zip64_limit = zipfile.ZIP64_LIMIT
2981*9e94795aSAndroid Build Coastguard Worker  zipfile.ZIP64_LIMIT = (1 << 32) - 1
2982*9e94795aSAndroid Build Coastguard Worker
2983*9e94795aSAndroid Build Coastguard Worker  if compress_type is None:
2984*9e94795aSAndroid Build Coastguard Worker    compress_type = zip_file.compression
2985*9e94795aSAndroid Build Coastguard Worker  if arcname is None:
2986*9e94795aSAndroid Build Coastguard Worker    arcname = filename
2987*9e94795aSAndroid Build Coastguard Worker
2988*9e94795aSAndroid Build Coastguard Worker  saved_stat = os.stat(filename)
2989*9e94795aSAndroid Build Coastguard Worker
2990*9e94795aSAndroid Build Coastguard Worker  try:
2991*9e94795aSAndroid Build Coastguard Worker    # `zipfile.write()` doesn't allow us to pass ZipInfo, so just modify the
2992*9e94795aSAndroid Build Coastguard Worker    # file to be zipped and reset it when we're done.
2993*9e94795aSAndroid Build Coastguard Worker    os.chmod(filename, perms)
2994*9e94795aSAndroid Build Coastguard Worker
2995*9e94795aSAndroid Build Coastguard Worker    # Use a fixed timestamp so the output is repeatable.
2996*9e94795aSAndroid Build Coastguard Worker    # Note: Use of fromtimestamp rather than utcfromtimestamp here is
2997*9e94795aSAndroid Build Coastguard Worker    # intentional. zip stores datetimes in local time without a time zone
2998*9e94795aSAndroid Build Coastguard Worker    # attached, so we need "epoch" but in the local time zone to get 2009/01/01
2999*9e94795aSAndroid Build Coastguard Worker    # in the zip archive.
3000*9e94795aSAndroid Build Coastguard Worker    local_epoch = datetime.datetime.fromtimestamp(0)
3001*9e94795aSAndroid Build Coastguard Worker    timestamp = (datetime.datetime(2009, 1, 1) - local_epoch).total_seconds()
3002*9e94795aSAndroid Build Coastguard Worker    os.utime(filename, (timestamp, timestamp))
3003*9e94795aSAndroid Build Coastguard Worker
3004*9e94795aSAndroid Build Coastguard Worker    zip_file.write(filename, arcname=arcname, compress_type=compress_type)
3005*9e94795aSAndroid Build Coastguard Worker  finally:
3006*9e94795aSAndroid Build Coastguard Worker    os.chmod(filename, saved_stat.st_mode)
3007*9e94795aSAndroid Build Coastguard Worker    os.utime(filename, (saved_stat.st_atime, saved_stat.st_mtime))
3008*9e94795aSAndroid Build Coastguard Worker    zipfile.ZIP64_LIMIT = saved_zip64_limit
3009*9e94795aSAndroid Build Coastguard Worker
3010*9e94795aSAndroid Build Coastguard Worker
3011*9e94795aSAndroid Build Coastguard Workerdef ZipWriteStr(zip_file: zipfile.ZipFile, zinfo_or_arcname, data, perms=None,
3012*9e94795aSAndroid Build Coastguard Worker                compress_type=None):
3013*9e94795aSAndroid Build Coastguard Worker  """Wrap zipfile.writestr() function to work around the zip64 limit.
3014*9e94795aSAndroid Build Coastguard Worker
3015*9e94795aSAndroid Build Coastguard Worker  Even with the ZIP64_LIMIT workaround, it won't allow writing a string
3016*9e94795aSAndroid Build Coastguard Worker  longer than 2GiB. It gives 'OverflowError: size does not fit in an int'
3017*9e94795aSAndroid Build Coastguard Worker  when calling crc32(bytes).
3018*9e94795aSAndroid Build Coastguard Worker
3019*9e94795aSAndroid Build Coastguard Worker  But it still works fine to write a shorter string into a large zip file.
3020*9e94795aSAndroid Build Coastguard Worker  We should use ZipWrite() whenever possible, and only use ZipWriteStr()
3021*9e94795aSAndroid Build Coastguard Worker  when we know the string won't be too long.
3022*9e94795aSAndroid Build Coastguard Worker  """
3023*9e94795aSAndroid Build Coastguard Worker
3024*9e94795aSAndroid Build Coastguard Worker  saved_zip64_limit = zipfile.ZIP64_LIMIT
3025*9e94795aSAndroid Build Coastguard Worker  zipfile.ZIP64_LIMIT = (1 << 32) - 1
3026*9e94795aSAndroid Build Coastguard Worker
3027*9e94795aSAndroid Build Coastguard Worker  if not isinstance(zinfo_or_arcname, zipfile.ZipInfo):
3028*9e94795aSAndroid Build Coastguard Worker    zinfo = zipfile.ZipInfo(filename=zinfo_or_arcname)
3029*9e94795aSAndroid Build Coastguard Worker    zinfo.compress_type = zip_file.compression
3030*9e94795aSAndroid Build Coastguard Worker    if perms is None:
3031*9e94795aSAndroid Build Coastguard Worker      perms = 0o100644
3032*9e94795aSAndroid Build Coastguard Worker  else:
3033*9e94795aSAndroid Build Coastguard Worker    zinfo = zinfo_or_arcname
3034*9e94795aSAndroid Build Coastguard Worker    # Python 2 and 3 behave differently when calling ZipFile.writestr() with
3035*9e94795aSAndroid Build Coastguard Worker    # zinfo.external_attr being 0. Python 3 uses `0o600 << 16` as the value for
3036*9e94795aSAndroid Build Coastguard Worker    # such a case (since
3037*9e94795aSAndroid Build Coastguard Worker    # https://github.com/python/cpython/commit/18ee29d0b870caddc0806916ca2c823254f1a1f9),
3038*9e94795aSAndroid Build Coastguard Worker    # which seems to make more sense. Otherwise the entry will have 0o000 as the
3039*9e94795aSAndroid Build Coastguard Worker    # permission bits. We follow the logic in Python 3 to get consistent
3040*9e94795aSAndroid Build Coastguard Worker    # behavior between using the two versions.
3041*9e94795aSAndroid Build Coastguard Worker    if not zinfo.external_attr:
3042*9e94795aSAndroid Build Coastguard Worker      zinfo.external_attr = 0o600 << 16
3043*9e94795aSAndroid Build Coastguard Worker
3044*9e94795aSAndroid Build Coastguard Worker  # If compress_type is given, it overrides the value in zinfo.
3045*9e94795aSAndroid Build Coastguard Worker  if compress_type is not None:
3046*9e94795aSAndroid Build Coastguard Worker    zinfo.compress_type = compress_type
3047*9e94795aSAndroid Build Coastguard Worker
3048*9e94795aSAndroid Build Coastguard Worker  # If perms is given, it has a priority.
3049*9e94795aSAndroid Build Coastguard Worker  if perms is not None:
3050*9e94795aSAndroid Build Coastguard Worker    # If perms doesn't set the file type, mark it as a regular file.
3051*9e94795aSAndroid Build Coastguard Worker    if perms & 0o770000 == 0:
3052*9e94795aSAndroid Build Coastguard Worker      perms |= 0o100000
3053*9e94795aSAndroid Build Coastguard Worker    zinfo.external_attr = perms << 16
3054*9e94795aSAndroid Build Coastguard Worker
3055*9e94795aSAndroid Build Coastguard Worker  # Use a fixed timestamp so the output is repeatable.
3056*9e94795aSAndroid Build Coastguard Worker  zinfo.date_time = (2009, 1, 1, 0, 0, 0)
3057*9e94795aSAndroid Build Coastguard Worker
3058*9e94795aSAndroid Build Coastguard Worker  zip_file.writestr(zinfo, data)
3059*9e94795aSAndroid Build Coastguard Worker  zipfile.ZIP64_LIMIT = saved_zip64_limit
3060*9e94795aSAndroid Build Coastguard Worker
3061*9e94795aSAndroid Build Coastguard Workerdef ZipExclude(input_zip, output_zip, entries, force=False):
3062*9e94795aSAndroid Build Coastguard Worker  """Deletes entries from a ZIP file.
3063*9e94795aSAndroid Build Coastguard Worker
3064*9e94795aSAndroid Build Coastguard Worker  Args:
3065*9e94795aSAndroid Build Coastguard Worker    zip_filename: The name of the ZIP file.
3066*9e94795aSAndroid Build Coastguard Worker    entries: The name of the entry, or the list of names to be deleted.
3067*9e94795aSAndroid Build Coastguard Worker  """
3068*9e94795aSAndroid Build Coastguard Worker  if isinstance(entries, str):
3069*9e94795aSAndroid Build Coastguard Worker    entries = [entries]
3070*9e94795aSAndroid Build Coastguard Worker  # If list is empty, nothing to do
3071*9e94795aSAndroid Build Coastguard Worker  if not entries:
3072*9e94795aSAndroid Build Coastguard Worker    shutil.copy(input_zip, output_zip)
3073*9e94795aSAndroid Build Coastguard Worker    return
3074*9e94795aSAndroid Build Coastguard Worker
3075*9e94795aSAndroid Build Coastguard Worker  with zipfile.ZipFile(input_zip, 'r') as zin:
3076*9e94795aSAndroid Build Coastguard Worker    if not force and len(set(zin.namelist()).intersection(entries)) == 0:
3077*9e94795aSAndroid Build Coastguard Worker      raise ExternalError(
3078*9e94795aSAndroid Build Coastguard Worker          "Failed to delete zip entries, name not matched: %s" % entries)
3079*9e94795aSAndroid Build Coastguard Worker
3080*9e94795aSAndroid Build Coastguard Worker    fd, new_zipfile = tempfile.mkstemp(dir=os.path.dirname(input_zip))
3081*9e94795aSAndroid Build Coastguard Worker    os.close(fd)
3082*9e94795aSAndroid Build Coastguard Worker    cmd = ["zip2zip", "-i", input_zip, "-o", new_zipfile]
3083*9e94795aSAndroid Build Coastguard Worker    for entry in entries:
3084*9e94795aSAndroid Build Coastguard Worker      cmd.append("-x")
3085*9e94795aSAndroid Build Coastguard Worker      cmd.append(entry)
3086*9e94795aSAndroid Build Coastguard Worker    RunAndCheckOutput(cmd)
3087*9e94795aSAndroid Build Coastguard Worker  os.replace(new_zipfile, output_zip)
3088*9e94795aSAndroid Build Coastguard Worker
3089*9e94795aSAndroid Build Coastguard Worker
3090*9e94795aSAndroid Build Coastguard Workerdef ZipDelete(zip_filename, entries, force=False):
3091*9e94795aSAndroid Build Coastguard Worker  """Deletes entries from a ZIP file.
3092*9e94795aSAndroid Build Coastguard Worker
3093*9e94795aSAndroid Build Coastguard Worker  Args:
3094*9e94795aSAndroid Build Coastguard Worker    zip_filename: The name of the ZIP file.
3095*9e94795aSAndroid Build Coastguard Worker    entries: The name of the entry, or the list of names to be deleted.
3096*9e94795aSAndroid Build Coastguard Worker  """
3097*9e94795aSAndroid Build Coastguard Worker  if isinstance(entries, str):
3098*9e94795aSAndroid Build Coastguard Worker    entries = [entries]
3099*9e94795aSAndroid Build Coastguard Worker  # If list is empty, nothing to do
3100*9e94795aSAndroid Build Coastguard Worker  if not entries:
3101*9e94795aSAndroid Build Coastguard Worker    return
3102*9e94795aSAndroid Build Coastguard Worker
3103*9e94795aSAndroid Build Coastguard Worker  ZipExclude(zip_filename, zip_filename, entries, force)
3104*9e94795aSAndroid Build Coastguard Worker
3105*9e94795aSAndroid Build Coastguard Worker
3106*9e94795aSAndroid Build Coastguard Workerdef ZipClose(zip_file):
3107*9e94795aSAndroid Build Coastguard Worker  # http://b/18015246
3108*9e94795aSAndroid Build Coastguard Worker  # zipfile also refers to ZIP64_LIMIT during close() when it writes out the
3109*9e94795aSAndroid Build Coastguard Worker  # central directory.
3110*9e94795aSAndroid Build Coastguard Worker  saved_zip64_limit = zipfile.ZIP64_LIMIT
3111*9e94795aSAndroid Build Coastguard Worker  zipfile.ZIP64_LIMIT = (1 << 32) - 1
3112*9e94795aSAndroid Build Coastguard Worker
3113*9e94795aSAndroid Build Coastguard Worker  zip_file.close()
3114*9e94795aSAndroid Build Coastguard Worker
3115*9e94795aSAndroid Build Coastguard Worker  zipfile.ZIP64_LIMIT = saved_zip64_limit
3116*9e94795aSAndroid Build Coastguard Worker
3117*9e94795aSAndroid Build Coastguard Worker
3118*9e94795aSAndroid Build Coastguard Workerclass DeviceSpecificParams(object):
3119*9e94795aSAndroid Build Coastguard Worker  module = None
3120*9e94795aSAndroid Build Coastguard Worker
3121*9e94795aSAndroid Build Coastguard Worker  def __init__(self, **kwargs):
3122*9e94795aSAndroid Build Coastguard Worker    """Keyword arguments to the constructor become attributes of this
3123*9e94795aSAndroid Build Coastguard Worker    object, which is passed to all functions in the device-specific
3124*9e94795aSAndroid Build Coastguard Worker    module."""
3125*9e94795aSAndroid Build Coastguard Worker    for k, v in kwargs.items():
3126*9e94795aSAndroid Build Coastguard Worker      setattr(self, k, v)
3127*9e94795aSAndroid Build Coastguard Worker    self.extras = OPTIONS.extras
3128*9e94795aSAndroid Build Coastguard Worker
3129*9e94795aSAndroid Build Coastguard Worker    if self.module is None:
3130*9e94795aSAndroid Build Coastguard Worker      path = OPTIONS.device_specific
3131*9e94795aSAndroid Build Coastguard Worker      if not path:
3132*9e94795aSAndroid Build Coastguard Worker        return
3133*9e94795aSAndroid Build Coastguard Worker      try:
3134*9e94795aSAndroid Build Coastguard Worker        if os.path.isdir(path):
3135*9e94795aSAndroid Build Coastguard Worker          info = imp.find_module("releasetools", [path])
3136*9e94795aSAndroid Build Coastguard Worker        else:
3137*9e94795aSAndroid Build Coastguard Worker          d, f = os.path.split(path)
3138*9e94795aSAndroid Build Coastguard Worker          b, x = os.path.splitext(f)
3139*9e94795aSAndroid Build Coastguard Worker          if x == ".py":
3140*9e94795aSAndroid Build Coastguard Worker            f = b
3141*9e94795aSAndroid Build Coastguard Worker          info = imp.find_module(f, [d])
3142*9e94795aSAndroid Build Coastguard Worker        logger.info("loaded device-specific extensions from %s", path)
3143*9e94795aSAndroid Build Coastguard Worker        self.module = imp.load_module("device_specific", *info)
3144*9e94795aSAndroid Build Coastguard Worker      except ImportError:
3145*9e94795aSAndroid Build Coastguard Worker        logger.info("unable to load device-specific module; assuming none")
3146*9e94795aSAndroid Build Coastguard Worker
3147*9e94795aSAndroid Build Coastguard Worker  def _DoCall(self, function_name, *args, **kwargs):
3148*9e94795aSAndroid Build Coastguard Worker    """Call the named function in the device-specific module, passing
3149*9e94795aSAndroid Build Coastguard Worker    the given args and kwargs.  The first argument to the call will be
3150*9e94795aSAndroid Build Coastguard Worker    the DeviceSpecific object itself.  If there is no module, or the
3151*9e94795aSAndroid Build Coastguard Worker    module does not define the function, return the value of the
3152*9e94795aSAndroid Build Coastguard Worker    'default' kwarg (which itself defaults to None)."""
3153*9e94795aSAndroid Build Coastguard Worker    if self.module is None or not hasattr(self.module, function_name):
3154*9e94795aSAndroid Build Coastguard Worker      return kwargs.get("default")
3155*9e94795aSAndroid Build Coastguard Worker    return getattr(self.module, function_name)(*((self,) + args), **kwargs)
3156*9e94795aSAndroid Build Coastguard Worker
3157*9e94795aSAndroid Build Coastguard Worker  def FullOTA_Assertions(self):
3158*9e94795aSAndroid Build Coastguard Worker    """Called after emitting the block of assertions at the top of a
3159*9e94795aSAndroid Build Coastguard Worker    full OTA package.  Implementations can add whatever additional
3160*9e94795aSAndroid Build Coastguard Worker    assertions they like."""
3161*9e94795aSAndroid Build Coastguard Worker    return self._DoCall("FullOTA_Assertions")
3162*9e94795aSAndroid Build Coastguard Worker
3163*9e94795aSAndroid Build Coastguard Worker  def FullOTA_InstallBegin(self):
3164*9e94795aSAndroid Build Coastguard Worker    """Called at the start of full OTA installation."""
3165*9e94795aSAndroid Build Coastguard Worker    return self._DoCall("FullOTA_InstallBegin")
3166*9e94795aSAndroid Build Coastguard Worker
3167*9e94795aSAndroid Build Coastguard Worker  def FullOTA_GetBlockDifferences(self):
3168*9e94795aSAndroid Build Coastguard Worker    """Called during full OTA installation and verification.
3169*9e94795aSAndroid Build Coastguard Worker    Implementation should return a list of BlockDifference objects describing
3170*9e94795aSAndroid Build Coastguard Worker    the update on each additional partitions.
3171*9e94795aSAndroid Build Coastguard Worker    """
3172*9e94795aSAndroid Build Coastguard Worker    return self._DoCall("FullOTA_GetBlockDifferences")
3173*9e94795aSAndroid Build Coastguard Worker
3174*9e94795aSAndroid Build Coastguard Worker  def FullOTA_InstallEnd(self):
3175*9e94795aSAndroid Build Coastguard Worker    """Called at the end of full OTA installation; typically this is
3176*9e94795aSAndroid Build Coastguard Worker    used to install the image for the device's baseband processor."""
3177*9e94795aSAndroid Build Coastguard Worker    return self._DoCall("FullOTA_InstallEnd")
3178*9e94795aSAndroid Build Coastguard Worker
3179*9e94795aSAndroid Build Coastguard Worker  def IncrementalOTA_Assertions(self):
3180*9e94795aSAndroid Build Coastguard Worker    """Called after emitting the block of assertions at the top of an
3181*9e94795aSAndroid Build Coastguard Worker    incremental OTA package.  Implementations can add whatever
3182*9e94795aSAndroid Build Coastguard Worker    additional assertions they like."""
3183*9e94795aSAndroid Build Coastguard Worker    return self._DoCall("IncrementalOTA_Assertions")
3184*9e94795aSAndroid Build Coastguard Worker
3185*9e94795aSAndroid Build Coastguard Worker  def IncrementalOTA_VerifyBegin(self):
3186*9e94795aSAndroid Build Coastguard Worker    """Called at the start of the verification phase of incremental
3187*9e94795aSAndroid Build Coastguard Worker    OTA installation; additional checks can be placed here to abort
3188*9e94795aSAndroid Build Coastguard Worker    the script before any changes are made."""
3189*9e94795aSAndroid Build Coastguard Worker    return self._DoCall("IncrementalOTA_VerifyBegin")
3190*9e94795aSAndroid Build Coastguard Worker
3191*9e94795aSAndroid Build Coastguard Worker  def IncrementalOTA_VerifyEnd(self):
3192*9e94795aSAndroid Build Coastguard Worker    """Called at the end of the verification phase of incremental OTA
3193*9e94795aSAndroid Build Coastguard Worker    installation; additional checks can be placed here to abort the
3194*9e94795aSAndroid Build Coastguard Worker    script before any changes are made."""
3195*9e94795aSAndroid Build Coastguard Worker    return self._DoCall("IncrementalOTA_VerifyEnd")
3196*9e94795aSAndroid Build Coastguard Worker
3197*9e94795aSAndroid Build Coastguard Worker  def IncrementalOTA_InstallBegin(self):
3198*9e94795aSAndroid Build Coastguard Worker    """Called at the start of incremental OTA installation (after
3199*9e94795aSAndroid Build Coastguard Worker    verification is complete)."""
3200*9e94795aSAndroid Build Coastguard Worker    return self._DoCall("IncrementalOTA_InstallBegin")
3201*9e94795aSAndroid Build Coastguard Worker
3202*9e94795aSAndroid Build Coastguard Worker  def IncrementalOTA_GetBlockDifferences(self):
3203*9e94795aSAndroid Build Coastguard Worker    """Called during incremental OTA installation and verification.
3204*9e94795aSAndroid Build Coastguard Worker    Implementation should return a list of BlockDifference objects describing
3205*9e94795aSAndroid Build Coastguard Worker    the update on each additional partitions.
3206*9e94795aSAndroid Build Coastguard Worker    """
3207*9e94795aSAndroid Build Coastguard Worker    return self._DoCall("IncrementalOTA_GetBlockDifferences")
3208*9e94795aSAndroid Build Coastguard Worker
3209*9e94795aSAndroid Build Coastguard Worker  def IncrementalOTA_InstallEnd(self):
3210*9e94795aSAndroid Build Coastguard Worker    """Called at the end of incremental OTA installation; typically
3211*9e94795aSAndroid Build Coastguard Worker    this is used to install the image for the device's baseband
3212*9e94795aSAndroid Build Coastguard Worker    processor."""
3213*9e94795aSAndroid Build Coastguard Worker    return self._DoCall("IncrementalOTA_InstallEnd")
3214*9e94795aSAndroid Build Coastguard Worker
3215*9e94795aSAndroid Build Coastguard Worker  def VerifyOTA_Assertions(self):
3216*9e94795aSAndroid Build Coastguard Worker    return self._DoCall("VerifyOTA_Assertions")
3217*9e94795aSAndroid Build Coastguard Worker
3218*9e94795aSAndroid Build Coastguard Worker
3219*9e94795aSAndroid Build Coastguard Workerclass File(object):
3220*9e94795aSAndroid Build Coastguard Worker  def __init__(self, name, data, compress_size=None):
3221*9e94795aSAndroid Build Coastguard Worker    self.name = name
3222*9e94795aSAndroid Build Coastguard Worker    self.data = data
3223*9e94795aSAndroid Build Coastguard Worker    self.size = len(data)
3224*9e94795aSAndroid Build Coastguard Worker    self.compress_size = compress_size or self.size
3225*9e94795aSAndroid Build Coastguard Worker    self.sha1 = sha1(data).hexdigest()
3226*9e94795aSAndroid Build Coastguard Worker
3227*9e94795aSAndroid Build Coastguard Worker  @classmethod
3228*9e94795aSAndroid Build Coastguard Worker  def FromLocalFile(cls, name, diskname):
3229*9e94795aSAndroid Build Coastguard Worker    f = open(diskname, "rb")
3230*9e94795aSAndroid Build Coastguard Worker    data = f.read()
3231*9e94795aSAndroid Build Coastguard Worker    f.close()
3232*9e94795aSAndroid Build Coastguard Worker    return File(name, data)
3233*9e94795aSAndroid Build Coastguard Worker
3234*9e94795aSAndroid Build Coastguard Worker  def WriteToTemp(self):
3235*9e94795aSAndroid Build Coastguard Worker    t = tempfile.NamedTemporaryFile()
3236*9e94795aSAndroid Build Coastguard Worker    t.write(self.data)
3237*9e94795aSAndroid Build Coastguard Worker    t.flush()
3238*9e94795aSAndroid Build Coastguard Worker    return t
3239*9e94795aSAndroid Build Coastguard Worker
3240*9e94795aSAndroid Build Coastguard Worker  def WriteToDir(self, d):
3241*9e94795aSAndroid Build Coastguard Worker    output_path = os.path.join(d, self.name)
3242*9e94795aSAndroid Build Coastguard Worker    os.makedirs(os.path.dirname(output_path), exist_ok=True)
3243*9e94795aSAndroid Build Coastguard Worker    with open(output_path, "wb") as fp:
3244*9e94795aSAndroid Build Coastguard Worker      fp.write(self.data)
3245*9e94795aSAndroid Build Coastguard Worker
3246*9e94795aSAndroid Build Coastguard Worker  def AddToZip(self, z, compression=None):
3247*9e94795aSAndroid Build Coastguard Worker    ZipWriteStr(z, self.name, self.data, compress_type=compression)
3248*9e94795aSAndroid Build Coastguard Worker
3249*9e94795aSAndroid Build Coastguard Worker
3250*9e94795aSAndroid Build Coastguard WorkerDIFF_PROGRAM_BY_EXT = {
3251*9e94795aSAndroid Build Coastguard Worker    ".gz": "imgdiff",
3252*9e94795aSAndroid Build Coastguard Worker    ".zip": ["imgdiff", "-z"],
3253*9e94795aSAndroid Build Coastguard Worker    ".jar": ["imgdiff", "-z"],
3254*9e94795aSAndroid Build Coastguard Worker    ".apk": ["imgdiff", "-z"],
3255*9e94795aSAndroid Build Coastguard Worker    ".img": "imgdiff",
3256*9e94795aSAndroid Build Coastguard Worker}
3257*9e94795aSAndroid Build Coastguard Worker
3258*9e94795aSAndroid Build Coastguard Worker
3259*9e94795aSAndroid Build Coastguard Workerclass Difference(object):
3260*9e94795aSAndroid Build Coastguard Worker  def __init__(self, tf, sf, diff_program=None):
3261*9e94795aSAndroid Build Coastguard Worker    self.tf = tf
3262*9e94795aSAndroid Build Coastguard Worker    self.sf = sf
3263*9e94795aSAndroid Build Coastguard Worker    self.patch = None
3264*9e94795aSAndroid Build Coastguard Worker    self.diff_program = diff_program
3265*9e94795aSAndroid Build Coastguard Worker
3266*9e94795aSAndroid Build Coastguard Worker  def ComputePatch(self):
3267*9e94795aSAndroid Build Coastguard Worker    """Compute the patch (as a string of data) needed to turn sf into
3268*9e94795aSAndroid Build Coastguard Worker    tf.  Returns the same tuple as GetPatch()."""
3269*9e94795aSAndroid Build Coastguard Worker
3270*9e94795aSAndroid Build Coastguard Worker    tf = self.tf
3271*9e94795aSAndroid Build Coastguard Worker    sf = self.sf
3272*9e94795aSAndroid Build Coastguard Worker
3273*9e94795aSAndroid Build Coastguard Worker    if self.diff_program:
3274*9e94795aSAndroid Build Coastguard Worker      diff_program = self.diff_program
3275*9e94795aSAndroid Build Coastguard Worker    else:
3276*9e94795aSAndroid Build Coastguard Worker      ext = os.path.splitext(tf.name)[1]
3277*9e94795aSAndroid Build Coastguard Worker      diff_program = DIFF_PROGRAM_BY_EXT.get(ext, "bsdiff")
3278*9e94795aSAndroid Build Coastguard Worker
3279*9e94795aSAndroid Build Coastguard Worker    ttemp = tf.WriteToTemp()
3280*9e94795aSAndroid Build Coastguard Worker    stemp = sf.WriteToTemp()
3281*9e94795aSAndroid Build Coastguard Worker
3282*9e94795aSAndroid Build Coastguard Worker    ext = os.path.splitext(tf.name)[1]
3283*9e94795aSAndroid Build Coastguard Worker
3284*9e94795aSAndroid Build Coastguard Worker    try:
3285*9e94795aSAndroid Build Coastguard Worker      ptemp = tempfile.NamedTemporaryFile()
3286*9e94795aSAndroid Build Coastguard Worker      if isinstance(diff_program, list):
3287*9e94795aSAndroid Build Coastguard Worker        cmd = copy.copy(diff_program)
3288*9e94795aSAndroid Build Coastguard Worker      else:
3289*9e94795aSAndroid Build Coastguard Worker        cmd = [diff_program]
3290*9e94795aSAndroid Build Coastguard Worker      cmd.append(stemp.name)
3291*9e94795aSAndroid Build Coastguard Worker      cmd.append(ttemp.name)
3292*9e94795aSAndroid Build Coastguard Worker      cmd.append(ptemp.name)
3293*9e94795aSAndroid Build Coastguard Worker      p = Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
3294*9e94795aSAndroid Build Coastguard Worker      err = []
3295*9e94795aSAndroid Build Coastguard Worker
3296*9e94795aSAndroid Build Coastguard Worker      def run():
3297*9e94795aSAndroid Build Coastguard Worker        _, e = p.communicate()
3298*9e94795aSAndroid Build Coastguard Worker        if e:
3299*9e94795aSAndroid Build Coastguard Worker          err.append(e)
3300*9e94795aSAndroid Build Coastguard Worker      th = threading.Thread(target=run)
3301*9e94795aSAndroid Build Coastguard Worker      th.start()
3302*9e94795aSAndroid Build Coastguard Worker      th.join(timeout=300)   # 5 mins
3303*9e94795aSAndroid Build Coastguard Worker      if th.is_alive():
3304*9e94795aSAndroid Build Coastguard Worker        logger.warning("diff command timed out")
3305*9e94795aSAndroid Build Coastguard Worker        p.terminate()
3306*9e94795aSAndroid Build Coastguard Worker        th.join(5)
3307*9e94795aSAndroid Build Coastguard Worker        if th.is_alive():
3308*9e94795aSAndroid Build Coastguard Worker          p.kill()
3309*9e94795aSAndroid Build Coastguard Worker          th.join()
3310*9e94795aSAndroid Build Coastguard Worker
3311*9e94795aSAndroid Build Coastguard Worker      if p.returncode != 0:
3312*9e94795aSAndroid Build Coastguard Worker        logger.warning("Failure running %s:\n%s\n", cmd, "".join(err))
3313*9e94795aSAndroid Build Coastguard Worker        self.patch = None
3314*9e94795aSAndroid Build Coastguard Worker        return None, None, None
3315*9e94795aSAndroid Build Coastguard Worker      diff = ptemp.read()
3316*9e94795aSAndroid Build Coastguard Worker    finally:
3317*9e94795aSAndroid Build Coastguard Worker      ptemp.close()
3318*9e94795aSAndroid Build Coastguard Worker      stemp.close()
3319*9e94795aSAndroid Build Coastguard Worker      ttemp.close()
3320*9e94795aSAndroid Build Coastguard Worker
3321*9e94795aSAndroid Build Coastguard Worker    self.patch = diff
3322*9e94795aSAndroid Build Coastguard Worker    return self.tf, self.sf, self.patch
3323*9e94795aSAndroid Build Coastguard Worker
3324*9e94795aSAndroid Build Coastguard Worker  def GetPatch(self):
3325*9e94795aSAndroid Build Coastguard Worker    """Returns a tuple of (target_file, source_file, patch_data).
3326*9e94795aSAndroid Build Coastguard Worker
3327*9e94795aSAndroid Build Coastguard Worker    patch_data may be None if ComputePatch hasn't been called, or if
3328*9e94795aSAndroid Build Coastguard Worker    computing the patch failed.
3329*9e94795aSAndroid Build Coastguard Worker    """
3330*9e94795aSAndroid Build Coastguard Worker    return self.tf, self.sf, self.patch
3331*9e94795aSAndroid Build Coastguard Worker
3332*9e94795aSAndroid Build Coastguard Worker
3333*9e94795aSAndroid Build Coastguard Workerdef ComputeDifferences(diffs):
3334*9e94795aSAndroid Build Coastguard Worker  """Call ComputePatch on all the Difference objects in 'diffs'."""
3335*9e94795aSAndroid Build Coastguard Worker  logger.info("%d diffs to compute", len(diffs))
3336*9e94795aSAndroid Build Coastguard Worker
3337*9e94795aSAndroid Build Coastguard Worker  # Do the largest files first, to try and reduce the long-pole effect.
3338*9e94795aSAndroid Build Coastguard Worker  by_size = [(i.tf.size, i) for i in diffs]
3339*9e94795aSAndroid Build Coastguard Worker  by_size.sort(reverse=True)
3340*9e94795aSAndroid Build Coastguard Worker  by_size = [i[1] for i in by_size]
3341*9e94795aSAndroid Build Coastguard Worker
3342*9e94795aSAndroid Build Coastguard Worker  lock = threading.Lock()
3343*9e94795aSAndroid Build Coastguard Worker  diff_iter = iter(by_size)   # accessed under lock
3344*9e94795aSAndroid Build Coastguard Worker
3345*9e94795aSAndroid Build Coastguard Worker  def worker():
3346*9e94795aSAndroid Build Coastguard Worker    try:
3347*9e94795aSAndroid Build Coastguard Worker      lock.acquire()
3348*9e94795aSAndroid Build Coastguard Worker      for d in diff_iter:
3349*9e94795aSAndroid Build Coastguard Worker        lock.release()
3350*9e94795aSAndroid Build Coastguard Worker        start = time.time()
3351*9e94795aSAndroid Build Coastguard Worker        d.ComputePatch()
3352*9e94795aSAndroid Build Coastguard Worker        dur = time.time() - start
3353*9e94795aSAndroid Build Coastguard Worker        lock.acquire()
3354*9e94795aSAndroid Build Coastguard Worker
3355*9e94795aSAndroid Build Coastguard Worker        tf, sf, patch = d.GetPatch()
3356*9e94795aSAndroid Build Coastguard Worker        if sf.name == tf.name:
3357*9e94795aSAndroid Build Coastguard Worker          name = tf.name
3358*9e94795aSAndroid Build Coastguard Worker        else:
3359*9e94795aSAndroid Build Coastguard Worker          name = "%s (%s)" % (tf.name, sf.name)
3360*9e94795aSAndroid Build Coastguard Worker        if patch is None:
3361*9e94795aSAndroid Build Coastguard Worker          logger.error("patching failed! %40s", name)
3362*9e94795aSAndroid Build Coastguard Worker        else:
3363*9e94795aSAndroid Build Coastguard Worker          logger.info(
3364*9e94795aSAndroid Build Coastguard Worker              "%8.2f sec %8d / %8d bytes (%6.2f%%) %s", dur, len(patch),
3365*9e94795aSAndroid Build Coastguard Worker              tf.size, 100.0 * len(patch) / tf.size, name)
3366*9e94795aSAndroid Build Coastguard Worker      lock.release()
3367*9e94795aSAndroid Build Coastguard Worker    except Exception:
3368*9e94795aSAndroid Build Coastguard Worker      logger.exception("Failed to compute diff from worker")
3369*9e94795aSAndroid Build Coastguard Worker      raise
3370*9e94795aSAndroid Build Coastguard Worker
3371*9e94795aSAndroid Build Coastguard Worker  # start worker threads; wait for them all to finish.
3372*9e94795aSAndroid Build Coastguard Worker  threads = [threading.Thread(target=worker)
3373*9e94795aSAndroid Build Coastguard Worker             for i in range(OPTIONS.worker_threads)]
3374*9e94795aSAndroid Build Coastguard Worker  for th in threads:
3375*9e94795aSAndroid Build Coastguard Worker    th.start()
3376*9e94795aSAndroid Build Coastguard Worker  while threads:
3377*9e94795aSAndroid Build Coastguard Worker    threads.pop().join()
3378*9e94795aSAndroid Build Coastguard Worker
3379*9e94795aSAndroid Build Coastguard Worker
3380*9e94795aSAndroid Build Coastguard Workerclass BlockDifference(object):
3381*9e94795aSAndroid Build Coastguard Worker  def __init__(self, partition, tgt, src=None, check_first_block=False,
3382*9e94795aSAndroid Build Coastguard Worker               version=None, disable_imgdiff=False):
3383*9e94795aSAndroid Build Coastguard Worker    self.tgt = tgt
3384*9e94795aSAndroid Build Coastguard Worker    self.src = src
3385*9e94795aSAndroid Build Coastguard Worker    self.partition = partition
3386*9e94795aSAndroid Build Coastguard Worker    self.check_first_block = check_first_block
3387*9e94795aSAndroid Build Coastguard Worker    self.disable_imgdiff = disable_imgdiff
3388*9e94795aSAndroid Build Coastguard Worker
3389*9e94795aSAndroid Build Coastguard Worker    if version is None:
3390*9e94795aSAndroid Build Coastguard Worker      version = max(
3391*9e94795aSAndroid Build Coastguard Worker          int(i) for i in
3392*9e94795aSAndroid Build Coastguard Worker          OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(","))
3393*9e94795aSAndroid Build Coastguard Worker    assert version >= 3
3394*9e94795aSAndroid Build Coastguard Worker    self.version = version
3395*9e94795aSAndroid Build Coastguard Worker
3396*9e94795aSAndroid Build Coastguard Worker    b = BlockImageDiff(tgt, src, threads=OPTIONS.worker_threads,
3397*9e94795aSAndroid Build Coastguard Worker                       version=self.version,
3398*9e94795aSAndroid Build Coastguard Worker                       disable_imgdiff=self.disable_imgdiff)
3399*9e94795aSAndroid Build Coastguard Worker    self.path = os.path.join(MakeTempDir(), partition)
3400*9e94795aSAndroid Build Coastguard Worker    b.Compute(self.path)
3401*9e94795aSAndroid Build Coastguard Worker    self._required_cache = b.max_stashed_size
3402*9e94795aSAndroid Build Coastguard Worker    self.touched_src_ranges = b.touched_src_ranges
3403*9e94795aSAndroid Build Coastguard Worker    self.touched_src_sha1 = b.touched_src_sha1
3404*9e94795aSAndroid Build Coastguard Worker
3405*9e94795aSAndroid Build Coastguard Worker    # On devices with dynamic partitions, for new partitions,
3406*9e94795aSAndroid Build Coastguard Worker    # src is None but OPTIONS.source_info_dict is not.
3407*9e94795aSAndroid Build Coastguard Worker    if OPTIONS.source_info_dict is None:
3408*9e94795aSAndroid Build Coastguard Worker      is_dynamic_build = OPTIONS.info_dict.get(
3409*9e94795aSAndroid Build Coastguard Worker          "use_dynamic_partitions") == "true"
3410*9e94795aSAndroid Build Coastguard Worker      is_dynamic_source = False
3411*9e94795aSAndroid Build Coastguard Worker    else:
3412*9e94795aSAndroid Build Coastguard Worker      is_dynamic_build = OPTIONS.source_info_dict.get(
3413*9e94795aSAndroid Build Coastguard Worker          "use_dynamic_partitions") == "true"
3414*9e94795aSAndroid Build Coastguard Worker      is_dynamic_source = partition in shlex.split(
3415*9e94795aSAndroid Build Coastguard Worker          OPTIONS.source_info_dict.get("dynamic_partition_list", "").strip())
3416*9e94795aSAndroid Build Coastguard Worker
3417*9e94795aSAndroid Build Coastguard Worker    is_dynamic_target = partition in shlex.split(
3418*9e94795aSAndroid Build Coastguard Worker        OPTIONS.info_dict.get("dynamic_partition_list", "").strip())
3419*9e94795aSAndroid Build Coastguard Worker
3420*9e94795aSAndroid Build Coastguard Worker    # For dynamic partitions builds, check partition list in both source
3421*9e94795aSAndroid Build Coastguard Worker    # and target build because new partitions may be added, and existing
3422*9e94795aSAndroid Build Coastguard Worker    # partitions may be removed.
3423*9e94795aSAndroid Build Coastguard Worker    is_dynamic = is_dynamic_build and (is_dynamic_source or is_dynamic_target)
3424*9e94795aSAndroid Build Coastguard Worker
3425*9e94795aSAndroid Build Coastguard Worker    if is_dynamic:
3426*9e94795aSAndroid Build Coastguard Worker      self.device = 'map_partition("%s")' % partition
3427*9e94795aSAndroid Build Coastguard Worker    else:
3428*9e94795aSAndroid Build Coastguard Worker      if OPTIONS.source_info_dict is None:
3429*9e94795aSAndroid Build Coastguard Worker        _, device_expr = GetTypeAndDeviceExpr("/" + partition,
3430*9e94795aSAndroid Build Coastguard Worker                                              OPTIONS.info_dict)
3431*9e94795aSAndroid Build Coastguard Worker      else:
3432*9e94795aSAndroid Build Coastguard Worker        _, device_expr = GetTypeAndDeviceExpr("/" + partition,
3433*9e94795aSAndroid Build Coastguard Worker                                              OPTIONS.source_info_dict)
3434*9e94795aSAndroid Build Coastguard Worker      self.device = device_expr
3435*9e94795aSAndroid Build Coastguard Worker
3436*9e94795aSAndroid Build Coastguard Worker  @property
3437*9e94795aSAndroid Build Coastguard Worker  def required_cache(self):
3438*9e94795aSAndroid Build Coastguard Worker    return self._required_cache
3439*9e94795aSAndroid Build Coastguard Worker
3440*9e94795aSAndroid Build Coastguard Worker  def WriteScript(self, script, output_zip, progress=None,
3441*9e94795aSAndroid Build Coastguard Worker                  write_verify_script=False):
3442*9e94795aSAndroid Build Coastguard Worker    if not self.src:
3443*9e94795aSAndroid Build Coastguard Worker      # write the output unconditionally
3444*9e94795aSAndroid Build Coastguard Worker      script.Print("Patching %s image unconditionally..." % (self.partition,))
3445*9e94795aSAndroid Build Coastguard Worker    else:
3446*9e94795aSAndroid Build Coastguard Worker      script.Print("Patching %s image after verification." % (self.partition,))
3447*9e94795aSAndroid Build Coastguard Worker
3448*9e94795aSAndroid Build Coastguard Worker    if progress:
3449*9e94795aSAndroid Build Coastguard Worker      script.ShowProgress(progress, 0)
3450*9e94795aSAndroid Build Coastguard Worker    self._WriteUpdate(script, output_zip)
3451*9e94795aSAndroid Build Coastguard Worker
3452*9e94795aSAndroid Build Coastguard Worker    if write_verify_script:
3453*9e94795aSAndroid Build Coastguard Worker      self.WritePostInstallVerifyScript(script)
3454*9e94795aSAndroid Build Coastguard Worker
3455*9e94795aSAndroid Build Coastguard Worker  def WriteStrictVerifyScript(self, script):
3456*9e94795aSAndroid Build Coastguard Worker    """Verify all the blocks in the care_map, including clobbered blocks.
3457*9e94795aSAndroid Build Coastguard Worker
3458*9e94795aSAndroid Build Coastguard Worker    This differs from the WriteVerifyScript() function: a) it prints different
3459*9e94795aSAndroid Build Coastguard Worker    error messages; b) it doesn't allow half-way updated images to pass the
3460*9e94795aSAndroid Build Coastguard Worker    verification."""
3461*9e94795aSAndroid Build Coastguard Worker
3462*9e94795aSAndroid Build Coastguard Worker    partition = self.partition
3463*9e94795aSAndroid Build Coastguard Worker    script.Print("Verifying %s..." % (partition,))
3464*9e94795aSAndroid Build Coastguard Worker    ranges = self.tgt.care_map
3465*9e94795aSAndroid Build Coastguard Worker    ranges_str = ranges.to_string_raw()
3466*9e94795aSAndroid Build Coastguard Worker    script.AppendExtra(
3467*9e94795aSAndroid Build Coastguard Worker        'range_sha1(%s, "%s") == "%s" && ui_print("    Verified.") || '
3468*9e94795aSAndroid Build Coastguard Worker        'ui_print("%s has unexpected contents.");' % (
3469*9e94795aSAndroid Build Coastguard Worker            self.device, ranges_str,
3470*9e94795aSAndroid Build Coastguard Worker            self.tgt.TotalSha1(include_clobbered_blocks=True),
3471*9e94795aSAndroid Build Coastguard Worker            self.partition))
3472*9e94795aSAndroid Build Coastguard Worker    script.AppendExtra("")
3473*9e94795aSAndroid Build Coastguard Worker
3474*9e94795aSAndroid Build Coastguard Worker  def WriteVerifyScript(self, script, touched_blocks_only=False):
3475*9e94795aSAndroid Build Coastguard Worker    partition = self.partition
3476*9e94795aSAndroid Build Coastguard Worker
3477*9e94795aSAndroid Build Coastguard Worker    # full OTA
3478*9e94795aSAndroid Build Coastguard Worker    if not self.src:
3479*9e94795aSAndroid Build Coastguard Worker      script.Print("Image %s will be patched unconditionally." % (partition,))
3480*9e94795aSAndroid Build Coastguard Worker
3481*9e94795aSAndroid Build Coastguard Worker    # incremental OTA
3482*9e94795aSAndroid Build Coastguard Worker    else:
3483*9e94795aSAndroid Build Coastguard Worker      if touched_blocks_only:
3484*9e94795aSAndroid Build Coastguard Worker        ranges = self.touched_src_ranges
3485*9e94795aSAndroid Build Coastguard Worker        expected_sha1 = self.touched_src_sha1
3486*9e94795aSAndroid Build Coastguard Worker      else:
3487*9e94795aSAndroid Build Coastguard Worker        ranges = self.src.care_map.subtract(self.src.clobbered_blocks)
3488*9e94795aSAndroid Build Coastguard Worker        expected_sha1 = self.src.TotalSha1()
3489*9e94795aSAndroid Build Coastguard Worker
3490*9e94795aSAndroid Build Coastguard Worker      # No blocks to be checked, skipping.
3491*9e94795aSAndroid Build Coastguard Worker      if not ranges:
3492*9e94795aSAndroid Build Coastguard Worker        return
3493*9e94795aSAndroid Build Coastguard Worker
3494*9e94795aSAndroid Build Coastguard Worker      ranges_str = ranges.to_string_raw()
3495*9e94795aSAndroid Build Coastguard Worker      script.AppendExtra(
3496*9e94795aSAndroid Build Coastguard Worker          'if (range_sha1(%s, "%s") == "%s" || block_image_verify(%s, '
3497*9e94795aSAndroid Build Coastguard Worker          'package_extract_file("%s.transfer.list"), "%s.new.dat", '
3498*9e94795aSAndroid Build Coastguard Worker          '"%s.patch.dat")) then' % (
3499*9e94795aSAndroid Build Coastguard Worker              self.device, ranges_str, expected_sha1,
3500*9e94795aSAndroid Build Coastguard Worker              self.device, partition, partition, partition))
3501*9e94795aSAndroid Build Coastguard Worker      script.Print('Verified %s image...' % (partition,))
3502*9e94795aSAndroid Build Coastguard Worker      script.AppendExtra('else')
3503*9e94795aSAndroid Build Coastguard Worker
3504*9e94795aSAndroid Build Coastguard Worker      if self.version >= 4:
3505*9e94795aSAndroid Build Coastguard Worker
3506*9e94795aSAndroid Build Coastguard Worker        # Bug: 21124327
3507*9e94795aSAndroid Build Coastguard Worker        # When generating incrementals for the system and vendor partitions in
3508*9e94795aSAndroid Build Coastguard Worker        # version 4 or newer, explicitly check the first block (which contains
3509*9e94795aSAndroid Build Coastguard Worker        # the superblock) of the partition to see if it's what we expect. If
3510*9e94795aSAndroid Build Coastguard Worker        # this check fails, give an explicit log message about the partition
3511*9e94795aSAndroid Build Coastguard Worker        # having been remounted R/W (the most likely explanation).
3512*9e94795aSAndroid Build Coastguard Worker        if self.check_first_block:
3513*9e94795aSAndroid Build Coastguard Worker          script.AppendExtra('check_first_block(%s);' % (self.device,))
3514*9e94795aSAndroid Build Coastguard Worker
3515*9e94795aSAndroid Build Coastguard Worker        # If version >= 4, try block recovery before abort update
3516*9e94795aSAndroid Build Coastguard Worker        if partition == "system":
3517*9e94795aSAndroid Build Coastguard Worker          code = ErrorCode.SYSTEM_RECOVER_FAILURE
3518*9e94795aSAndroid Build Coastguard Worker        else:
3519*9e94795aSAndroid Build Coastguard Worker          code = ErrorCode.VENDOR_RECOVER_FAILURE
3520*9e94795aSAndroid Build Coastguard Worker        script.AppendExtra((
3521*9e94795aSAndroid Build Coastguard Worker            'ifelse (block_image_recover({device}, "{ranges}") && '
3522*9e94795aSAndroid Build Coastguard Worker            'block_image_verify({device}, '
3523*9e94795aSAndroid Build Coastguard Worker            'package_extract_file("{partition}.transfer.list"), '
3524*9e94795aSAndroid Build Coastguard Worker            '"{partition}.new.dat", "{partition}.patch.dat"), '
3525*9e94795aSAndroid Build Coastguard Worker            'ui_print("{partition} recovered successfully."), '
3526*9e94795aSAndroid Build Coastguard Worker            'abort("E{code}: {partition} partition fails to recover"));\n'
3527*9e94795aSAndroid Build Coastguard Worker            'endif;').format(device=self.device, ranges=ranges_str,
3528*9e94795aSAndroid Build Coastguard Worker                             partition=partition, code=code))
3529*9e94795aSAndroid Build Coastguard Worker
3530*9e94795aSAndroid Build Coastguard Worker      # Abort the OTA update. Note that the incremental OTA cannot be applied
3531*9e94795aSAndroid Build Coastguard Worker      # even if it may match the checksum of the target partition.
3532*9e94795aSAndroid Build Coastguard Worker      # a) If version < 3, operations like move and erase will make changes
3533*9e94795aSAndroid Build Coastguard Worker      #    unconditionally and damage the partition.
3534*9e94795aSAndroid Build Coastguard Worker      # b) If version >= 3, it won't even reach here.
3535*9e94795aSAndroid Build Coastguard Worker      else:
3536*9e94795aSAndroid Build Coastguard Worker        if partition == "system":
3537*9e94795aSAndroid Build Coastguard Worker          code = ErrorCode.SYSTEM_VERIFICATION_FAILURE
3538*9e94795aSAndroid Build Coastguard Worker        else:
3539*9e94795aSAndroid Build Coastguard Worker          code = ErrorCode.VENDOR_VERIFICATION_FAILURE
3540*9e94795aSAndroid Build Coastguard Worker        script.AppendExtra((
3541*9e94795aSAndroid Build Coastguard Worker            'abort("E%d: %s partition has unexpected contents");\n'
3542*9e94795aSAndroid Build Coastguard Worker            'endif;') % (code, partition))
3543*9e94795aSAndroid Build Coastguard Worker
3544*9e94795aSAndroid Build Coastguard Worker  def WritePostInstallVerifyScript(self, script):
3545*9e94795aSAndroid Build Coastguard Worker    partition = self.partition
3546*9e94795aSAndroid Build Coastguard Worker    script.Print('Verifying the updated %s image...' % (partition,))
3547*9e94795aSAndroid Build Coastguard Worker    # Unlike pre-install verification, clobbered_blocks should not be ignored.
3548*9e94795aSAndroid Build Coastguard Worker    ranges = self.tgt.care_map
3549*9e94795aSAndroid Build Coastguard Worker    ranges_str = ranges.to_string_raw()
3550*9e94795aSAndroid Build Coastguard Worker    script.AppendExtra(
3551*9e94795aSAndroid Build Coastguard Worker        'if range_sha1(%s, "%s") == "%s" then' % (
3552*9e94795aSAndroid Build Coastguard Worker            self.device, ranges_str,
3553*9e94795aSAndroid Build Coastguard Worker            self.tgt.TotalSha1(include_clobbered_blocks=True)))
3554*9e94795aSAndroid Build Coastguard Worker
3555*9e94795aSAndroid Build Coastguard Worker    # Bug: 20881595
3556*9e94795aSAndroid Build Coastguard Worker    # Verify that extended blocks are really zeroed out.
3557*9e94795aSAndroid Build Coastguard Worker    if self.tgt.extended:
3558*9e94795aSAndroid Build Coastguard Worker      ranges_str = self.tgt.extended.to_string_raw()
3559*9e94795aSAndroid Build Coastguard Worker      script.AppendExtra(
3560*9e94795aSAndroid Build Coastguard Worker          'if range_sha1(%s, "%s") == "%s" then' % (
3561*9e94795aSAndroid Build Coastguard Worker              self.device, ranges_str,
3562*9e94795aSAndroid Build Coastguard Worker              self._HashZeroBlocks(self.tgt.extended.size())))
3563*9e94795aSAndroid Build Coastguard Worker      script.Print('Verified the updated %s image.' % (partition,))
3564*9e94795aSAndroid Build Coastguard Worker      if partition == "system":
3565*9e94795aSAndroid Build Coastguard Worker        code = ErrorCode.SYSTEM_NONZERO_CONTENTS
3566*9e94795aSAndroid Build Coastguard Worker      else:
3567*9e94795aSAndroid Build Coastguard Worker        code = ErrorCode.VENDOR_NONZERO_CONTENTS
3568*9e94795aSAndroid Build Coastguard Worker      script.AppendExtra(
3569*9e94795aSAndroid Build Coastguard Worker          'else\n'
3570*9e94795aSAndroid Build Coastguard Worker          '  abort("E%d: %s partition has unexpected non-zero contents after '
3571*9e94795aSAndroid Build Coastguard Worker          'OTA update");\n'
3572*9e94795aSAndroid Build Coastguard Worker          'endif;' % (code, partition))
3573*9e94795aSAndroid Build Coastguard Worker    else:
3574*9e94795aSAndroid Build Coastguard Worker      script.Print('Verified the updated %s image.' % (partition,))
3575*9e94795aSAndroid Build Coastguard Worker
3576*9e94795aSAndroid Build Coastguard Worker    if partition == "system":
3577*9e94795aSAndroid Build Coastguard Worker      code = ErrorCode.SYSTEM_UNEXPECTED_CONTENTS
3578*9e94795aSAndroid Build Coastguard Worker    else:
3579*9e94795aSAndroid Build Coastguard Worker      code = ErrorCode.VENDOR_UNEXPECTED_CONTENTS
3580*9e94795aSAndroid Build Coastguard Worker
3581*9e94795aSAndroid Build Coastguard Worker    script.AppendExtra(
3582*9e94795aSAndroid Build Coastguard Worker        'else\n'
3583*9e94795aSAndroid Build Coastguard Worker        '  abort("E%d: %s partition has unexpected contents after OTA '
3584*9e94795aSAndroid Build Coastguard Worker        'update");\n'
3585*9e94795aSAndroid Build Coastguard Worker        'endif;' % (code, partition))
3586*9e94795aSAndroid Build Coastguard Worker
3587*9e94795aSAndroid Build Coastguard Worker  def _WriteUpdate(self, script, output_zip):
3588*9e94795aSAndroid Build Coastguard Worker    ZipWrite(output_zip,
3589*9e94795aSAndroid Build Coastguard Worker             '{}.transfer.list'.format(self.path),
3590*9e94795aSAndroid Build Coastguard Worker             '{}.transfer.list'.format(self.partition))
3591*9e94795aSAndroid Build Coastguard Worker
3592*9e94795aSAndroid Build Coastguard Worker    # For full OTA, compress the new.dat with brotli with quality 6 to reduce
3593*9e94795aSAndroid Build Coastguard Worker    # its size. Quailty 9 almost triples the compression time but doesn't
3594*9e94795aSAndroid Build Coastguard Worker    # further reduce the size too much. For a typical 1.8G system.new.dat
3595*9e94795aSAndroid Build Coastguard Worker    #                       zip  | brotli(quality 6)  | brotli(quality 9)
3596*9e94795aSAndroid Build Coastguard Worker    #   compressed_size:    942M | 869M (~8% reduced) | 854M
3597*9e94795aSAndroid Build Coastguard Worker    #   compression_time:   75s  | 265s               | 719s
3598*9e94795aSAndroid Build Coastguard Worker    #   decompression_time: 15s  | 25s                | 25s
3599*9e94795aSAndroid Build Coastguard Worker
3600*9e94795aSAndroid Build Coastguard Worker    if not self.src:
3601*9e94795aSAndroid Build Coastguard Worker      brotli_cmd = ['brotli', '--quality=6',
3602*9e94795aSAndroid Build Coastguard Worker                    '--output={}.new.dat.br'.format(self.path),
3603*9e94795aSAndroid Build Coastguard Worker                    '{}.new.dat'.format(self.path)]
3604*9e94795aSAndroid Build Coastguard Worker      print("Compressing {}.new.dat with brotli".format(self.partition))
3605*9e94795aSAndroid Build Coastguard Worker      RunAndCheckOutput(brotli_cmd)
3606*9e94795aSAndroid Build Coastguard Worker
3607*9e94795aSAndroid Build Coastguard Worker      new_data_name = '{}.new.dat.br'.format(self.partition)
3608*9e94795aSAndroid Build Coastguard Worker      ZipWrite(output_zip,
3609*9e94795aSAndroid Build Coastguard Worker               '{}.new.dat.br'.format(self.path),
3610*9e94795aSAndroid Build Coastguard Worker               new_data_name,
3611*9e94795aSAndroid Build Coastguard Worker               compress_type=zipfile.ZIP_STORED)
3612*9e94795aSAndroid Build Coastguard Worker    else:
3613*9e94795aSAndroid Build Coastguard Worker      new_data_name = '{}.new.dat'.format(self.partition)
3614*9e94795aSAndroid Build Coastguard Worker      ZipWrite(output_zip, '{}.new.dat'.format(self.path), new_data_name)
3615*9e94795aSAndroid Build Coastguard Worker
3616*9e94795aSAndroid Build Coastguard Worker    ZipWrite(output_zip,
3617*9e94795aSAndroid Build Coastguard Worker             '{}.patch.dat'.format(self.path),
3618*9e94795aSAndroid Build Coastguard Worker             '{}.patch.dat'.format(self.partition),
3619*9e94795aSAndroid Build Coastguard Worker             compress_type=zipfile.ZIP_STORED)
3620*9e94795aSAndroid Build Coastguard Worker
3621*9e94795aSAndroid Build Coastguard Worker    if self.partition == "system":
3622*9e94795aSAndroid Build Coastguard Worker      code = ErrorCode.SYSTEM_UPDATE_FAILURE
3623*9e94795aSAndroid Build Coastguard Worker    else:
3624*9e94795aSAndroid Build Coastguard Worker      code = ErrorCode.VENDOR_UPDATE_FAILURE
3625*9e94795aSAndroid Build Coastguard Worker
3626*9e94795aSAndroid Build Coastguard Worker    call = ('block_image_update({device}, '
3627*9e94795aSAndroid Build Coastguard Worker            'package_extract_file("{partition}.transfer.list"), '
3628*9e94795aSAndroid Build Coastguard Worker            '"{new_data_name}", "{partition}.patch.dat") ||\n'
3629*9e94795aSAndroid Build Coastguard Worker            '  abort("E{code}: Failed to update {partition} image.");'.format(
3630*9e94795aSAndroid Build Coastguard Worker                device=self.device, partition=self.partition,
3631*9e94795aSAndroid Build Coastguard Worker                new_data_name=new_data_name, code=code))
3632*9e94795aSAndroid Build Coastguard Worker    script.AppendExtra(script.WordWrap(call))
3633*9e94795aSAndroid Build Coastguard Worker
3634*9e94795aSAndroid Build Coastguard Worker  def _HashBlocks(self, source, ranges):  # pylint: disable=no-self-use
3635*9e94795aSAndroid Build Coastguard Worker    data = source.ReadRangeSet(ranges)
3636*9e94795aSAndroid Build Coastguard Worker    ctx = sha1()
3637*9e94795aSAndroid Build Coastguard Worker
3638*9e94795aSAndroid Build Coastguard Worker    for p in data:
3639*9e94795aSAndroid Build Coastguard Worker      ctx.update(p)
3640*9e94795aSAndroid Build Coastguard Worker
3641*9e94795aSAndroid Build Coastguard Worker    return ctx.hexdigest()
3642*9e94795aSAndroid Build Coastguard Worker
3643*9e94795aSAndroid Build Coastguard Worker  def _HashZeroBlocks(self, num_blocks):  # pylint: disable=no-self-use
3644*9e94795aSAndroid Build Coastguard Worker    """Return the hash value for all zero blocks."""
3645*9e94795aSAndroid Build Coastguard Worker    zero_block = '\x00' * 4096
3646*9e94795aSAndroid Build Coastguard Worker    ctx = sha1()
3647*9e94795aSAndroid Build Coastguard Worker    for _ in range(num_blocks):
3648*9e94795aSAndroid Build Coastguard Worker      ctx.update(zero_block)
3649*9e94795aSAndroid Build Coastguard Worker
3650*9e94795aSAndroid Build Coastguard Worker    return ctx.hexdigest()
3651*9e94795aSAndroid Build Coastguard Worker
3652*9e94795aSAndroid Build Coastguard Worker
3653*9e94795aSAndroid Build Coastguard Worker# Expose these two classes to support vendor-specific scripts
3654*9e94795aSAndroid Build Coastguard WorkerDataImage = images.DataImage
3655*9e94795aSAndroid Build Coastguard WorkerEmptyImage = images.EmptyImage
3656*9e94795aSAndroid Build Coastguard Worker
3657*9e94795aSAndroid Build Coastguard Worker
3658*9e94795aSAndroid Build Coastguard Worker# map recovery.fstab's fs_types to mount/format "partition types"
3659*9e94795aSAndroid Build Coastguard WorkerPARTITION_TYPES = {
3660*9e94795aSAndroid Build Coastguard Worker    "ext4": "EMMC",
3661*9e94795aSAndroid Build Coastguard Worker    "emmc": "EMMC",
3662*9e94795aSAndroid Build Coastguard Worker    "f2fs": "EMMC",
3663*9e94795aSAndroid Build Coastguard Worker    "squashfs": "EMMC",
3664*9e94795aSAndroid Build Coastguard Worker    "erofs": "EMMC"
3665*9e94795aSAndroid Build Coastguard Worker}
3666*9e94795aSAndroid Build Coastguard Worker
3667*9e94795aSAndroid Build Coastguard Worker
3668*9e94795aSAndroid Build Coastguard Workerdef GetTypeAndDevice(mount_point, info, check_no_slot=True):
3669*9e94795aSAndroid Build Coastguard Worker  """
3670*9e94795aSAndroid Build Coastguard Worker  Use GetTypeAndDeviceExpr whenever possible. This function is kept for
3671*9e94795aSAndroid Build Coastguard Worker  backwards compatibility. It aborts if the fstab entry has slotselect option
3672*9e94795aSAndroid Build Coastguard Worker  (unless check_no_slot is explicitly set to False).
3673*9e94795aSAndroid Build Coastguard Worker  """
3674*9e94795aSAndroid Build Coastguard Worker  fstab = info["fstab"]
3675*9e94795aSAndroid Build Coastguard Worker  if fstab:
3676*9e94795aSAndroid Build Coastguard Worker    if check_no_slot:
3677*9e94795aSAndroid Build Coastguard Worker      assert not fstab[mount_point].slotselect, \
3678*9e94795aSAndroid Build Coastguard Worker          "Use GetTypeAndDeviceExpr instead"
3679*9e94795aSAndroid Build Coastguard Worker    return (PARTITION_TYPES[fstab[mount_point].fs_type],
3680*9e94795aSAndroid Build Coastguard Worker            fstab[mount_point].device)
3681*9e94795aSAndroid Build Coastguard Worker  raise KeyError
3682*9e94795aSAndroid Build Coastguard Worker
3683*9e94795aSAndroid Build Coastguard Worker
3684*9e94795aSAndroid Build Coastguard Workerdef GetTypeAndDeviceExpr(mount_point, info):
3685*9e94795aSAndroid Build Coastguard Worker  """
3686*9e94795aSAndroid Build Coastguard Worker  Return the filesystem of the partition, and an edify expression that evaluates
3687*9e94795aSAndroid Build Coastguard Worker  to the device at runtime.
3688*9e94795aSAndroid Build Coastguard Worker  """
3689*9e94795aSAndroid Build Coastguard Worker  fstab = info["fstab"]
3690*9e94795aSAndroid Build Coastguard Worker  if fstab:
3691*9e94795aSAndroid Build Coastguard Worker    p = fstab[mount_point]
3692*9e94795aSAndroid Build Coastguard Worker    device_expr = '"%s"' % fstab[mount_point].device
3693*9e94795aSAndroid Build Coastguard Worker    if p.slotselect:
3694*9e94795aSAndroid Build Coastguard Worker      device_expr = 'add_slot_suffix(%s)' % device_expr
3695*9e94795aSAndroid Build Coastguard Worker    return (PARTITION_TYPES[fstab[mount_point].fs_type], device_expr)
3696*9e94795aSAndroid Build Coastguard Worker  raise KeyError
3697*9e94795aSAndroid Build Coastguard Worker
3698*9e94795aSAndroid Build Coastguard Worker
3699*9e94795aSAndroid Build Coastguard Workerdef GetEntryForDevice(fstab, device):
3700*9e94795aSAndroid Build Coastguard Worker  """
3701*9e94795aSAndroid Build Coastguard Worker  Returns:
3702*9e94795aSAndroid Build Coastguard Worker    The first entry in fstab whose device is the given value.
3703*9e94795aSAndroid Build Coastguard Worker  """
3704*9e94795aSAndroid Build Coastguard Worker  if not fstab:
3705*9e94795aSAndroid Build Coastguard Worker    return None
3706*9e94795aSAndroid Build Coastguard Worker  for mount_point in fstab:
3707*9e94795aSAndroid Build Coastguard Worker    if fstab[mount_point].device == device:
3708*9e94795aSAndroid Build Coastguard Worker      return fstab[mount_point]
3709*9e94795aSAndroid Build Coastguard Worker  return None
3710*9e94795aSAndroid Build Coastguard Worker
3711*9e94795aSAndroid Build Coastguard Worker
3712*9e94795aSAndroid Build Coastguard Workerdef ParseCertificate(data):
3713*9e94795aSAndroid Build Coastguard Worker  """Parses and converts a PEM-encoded certificate into DER-encoded.
3714*9e94795aSAndroid Build Coastguard Worker
3715*9e94795aSAndroid Build Coastguard Worker  This gives the same result as `openssl x509 -in <filename> -outform DER`.
3716*9e94795aSAndroid Build Coastguard Worker
3717*9e94795aSAndroid Build Coastguard Worker  Returns:
3718*9e94795aSAndroid Build Coastguard Worker    The decoded certificate bytes.
3719*9e94795aSAndroid Build Coastguard Worker  """
3720*9e94795aSAndroid Build Coastguard Worker  cert_buffer = []
3721*9e94795aSAndroid Build Coastguard Worker  save = False
3722*9e94795aSAndroid Build Coastguard Worker  for line in data.split("\n"):
3723*9e94795aSAndroid Build Coastguard Worker    if "--END CERTIFICATE--" in line:
3724*9e94795aSAndroid Build Coastguard Worker      break
3725*9e94795aSAndroid Build Coastguard Worker    if save:
3726*9e94795aSAndroid Build Coastguard Worker      cert_buffer.append(line)
3727*9e94795aSAndroid Build Coastguard Worker    if "--BEGIN CERTIFICATE--" in line:
3728*9e94795aSAndroid Build Coastguard Worker      save = True
3729*9e94795aSAndroid Build Coastguard Worker  cert = base64.b64decode("".join(cert_buffer))
3730*9e94795aSAndroid Build Coastguard Worker  return cert
3731*9e94795aSAndroid Build Coastguard Worker
3732*9e94795aSAndroid Build Coastguard Worker
3733*9e94795aSAndroid Build Coastguard Workerdef ExtractPublicKey(cert):
3734*9e94795aSAndroid Build Coastguard Worker  """Extracts the public key (PEM-encoded) from the given certificate file.
3735*9e94795aSAndroid Build Coastguard Worker
3736*9e94795aSAndroid Build Coastguard Worker  Args:
3737*9e94795aSAndroid Build Coastguard Worker    cert: The certificate filename.
3738*9e94795aSAndroid Build Coastguard Worker
3739*9e94795aSAndroid Build Coastguard Worker  Returns:
3740*9e94795aSAndroid Build Coastguard Worker    The public key string.
3741*9e94795aSAndroid Build Coastguard Worker
3742*9e94795aSAndroid Build Coastguard Worker  Raises:
3743*9e94795aSAndroid Build Coastguard Worker    AssertionError: On non-zero return from 'openssl'.
3744*9e94795aSAndroid Build Coastguard Worker  """
3745*9e94795aSAndroid Build Coastguard Worker  # The behavior with '-out' is different between openssl 1.1 and openssl 1.0.
3746*9e94795aSAndroid Build Coastguard Worker  # While openssl 1.1 writes the key into the given filename followed by '-out',
3747*9e94795aSAndroid Build Coastguard Worker  # openssl 1.0 (both of 1.0.1 and 1.0.2) doesn't. So we collect the output from
3748*9e94795aSAndroid Build Coastguard Worker  # stdout instead.
3749*9e94795aSAndroid Build Coastguard Worker  cmd = ['openssl', 'x509', '-pubkey', '-noout', '-in', cert]
3750*9e94795aSAndroid Build Coastguard Worker  proc = Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
3751*9e94795aSAndroid Build Coastguard Worker  pubkey, stderrdata = proc.communicate()
3752*9e94795aSAndroid Build Coastguard Worker  assert proc.returncode == 0, \
3753*9e94795aSAndroid Build Coastguard Worker      'Failed to dump public key from certificate: %s\n%s' % (cert, stderrdata)
3754*9e94795aSAndroid Build Coastguard Worker  return pubkey
3755*9e94795aSAndroid Build Coastguard Worker
3756*9e94795aSAndroid Build Coastguard Worker
3757*9e94795aSAndroid Build Coastguard Workerdef ExtractAvbPublicKey(avbtool, key):
3758*9e94795aSAndroid Build Coastguard Worker  """Extracts the AVB public key from the given public or private key.
3759*9e94795aSAndroid Build Coastguard Worker
3760*9e94795aSAndroid Build Coastguard Worker  Args:
3761*9e94795aSAndroid Build Coastguard Worker    avbtool: The AVB tool to use.
3762*9e94795aSAndroid Build Coastguard Worker    key: The input key file, which should be PEM-encoded public or private key.
3763*9e94795aSAndroid Build Coastguard Worker
3764*9e94795aSAndroid Build Coastguard Worker  Returns:
3765*9e94795aSAndroid Build Coastguard Worker    The path to the extracted AVB public key file.
3766*9e94795aSAndroid Build Coastguard Worker  """
3767*9e94795aSAndroid Build Coastguard Worker  output = MakeTempFile(prefix='avb-', suffix='.avbpubkey')
3768*9e94795aSAndroid Build Coastguard Worker  RunAndCheckOutput(
3769*9e94795aSAndroid Build Coastguard Worker      [avbtool, 'extract_public_key', "--key", key, "--output", output])
3770*9e94795aSAndroid Build Coastguard Worker  return output
3771*9e94795aSAndroid Build Coastguard Worker
3772*9e94795aSAndroid Build Coastguard Worker
3773*9e94795aSAndroid Build Coastguard Workerdef MakeRecoveryPatch(input_dir, output_sink, recovery_img, boot_img,
3774*9e94795aSAndroid Build Coastguard Worker                      info_dict=None):
3775*9e94795aSAndroid Build Coastguard Worker  """Generates the recovery-from-boot patch and writes the script to output.
3776*9e94795aSAndroid Build Coastguard Worker
3777*9e94795aSAndroid Build Coastguard Worker  Most of the space in the boot and recovery images is just the kernel, which is
3778*9e94795aSAndroid Build Coastguard Worker  identical for the two, so the resulting patch should be efficient. Add it to
3779*9e94795aSAndroid Build Coastguard Worker  the output zip, along with a shell script that is run from init.rc on first
3780*9e94795aSAndroid Build Coastguard Worker  boot to actually do the patching and install the new recovery image.
3781*9e94795aSAndroid Build Coastguard Worker
3782*9e94795aSAndroid Build Coastguard Worker  Args:
3783*9e94795aSAndroid Build Coastguard Worker    input_dir: The top-level input directory of the target-files.zip.
3784*9e94795aSAndroid Build Coastguard Worker    output_sink: The callback function that writes the result.
3785*9e94795aSAndroid Build Coastguard Worker    recovery_img: File object for the recovery image.
3786*9e94795aSAndroid Build Coastguard Worker    boot_img: File objects for the boot image.
3787*9e94795aSAndroid Build Coastguard Worker    info_dict: A dict returned by common.LoadInfoDict() on the input
3788*9e94795aSAndroid Build Coastguard Worker        target_files. Will use OPTIONS.info_dict if None has been given.
3789*9e94795aSAndroid Build Coastguard Worker  """
3790*9e94795aSAndroid Build Coastguard Worker  if info_dict is None:
3791*9e94795aSAndroid Build Coastguard Worker    info_dict = OPTIONS.info_dict
3792*9e94795aSAndroid Build Coastguard Worker
3793*9e94795aSAndroid Build Coastguard Worker  full_recovery_image = info_dict.get("full_recovery_image") == "true"
3794*9e94795aSAndroid Build Coastguard Worker  board_uses_vendorimage = info_dict.get("board_uses_vendorimage") == "true"
3795*9e94795aSAndroid Build Coastguard Worker
3796*9e94795aSAndroid Build Coastguard Worker  if board_uses_vendorimage:
3797*9e94795aSAndroid Build Coastguard Worker    # In this case, the output sink is rooted at VENDOR
3798*9e94795aSAndroid Build Coastguard Worker    recovery_img_path = "etc/recovery.img"
3799*9e94795aSAndroid Build Coastguard Worker    recovery_resource_dat_path = "VENDOR/etc/recovery-resource.dat"
3800*9e94795aSAndroid Build Coastguard Worker    sh_dir = "bin"
3801*9e94795aSAndroid Build Coastguard Worker  else:
3802*9e94795aSAndroid Build Coastguard Worker    # In this case the output sink is rooted at SYSTEM
3803*9e94795aSAndroid Build Coastguard Worker    recovery_img_path = "vendor/etc/recovery.img"
3804*9e94795aSAndroid Build Coastguard Worker    recovery_resource_dat_path = "SYSTEM/vendor/etc/recovery-resource.dat"
3805*9e94795aSAndroid Build Coastguard Worker    sh_dir = "vendor/bin"
3806*9e94795aSAndroid Build Coastguard Worker
3807*9e94795aSAndroid Build Coastguard Worker  if full_recovery_image:
3808*9e94795aSAndroid Build Coastguard Worker    output_sink(recovery_img_path, recovery_img.data)
3809*9e94795aSAndroid Build Coastguard Worker
3810*9e94795aSAndroid Build Coastguard Worker  else:
3811*9e94795aSAndroid Build Coastguard Worker    include_recovery_dtbo = info_dict.get("include_recovery_dtbo") == "true"
3812*9e94795aSAndroid Build Coastguard Worker    include_recovery_acpio = info_dict.get("include_recovery_acpio") == "true"
3813*9e94795aSAndroid Build Coastguard Worker    path = os.path.join(input_dir, recovery_resource_dat_path)
3814*9e94795aSAndroid Build Coastguard Worker    # Use bsdiff to handle mismatching entries (Bug: 72731506)
3815*9e94795aSAndroid Build Coastguard Worker    if include_recovery_dtbo or include_recovery_acpio:
3816*9e94795aSAndroid Build Coastguard Worker      diff_program = ["bsdiff"]
3817*9e94795aSAndroid Build Coastguard Worker      bonus_args = ""
3818*9e94795aSAndroid Build Coastguard Worker      assert not os.path.exists(path)
3819*9e94795aSAndroid Build Coastguard Worker    else:
3820*9e94795aSAndroid Build Coastguard Worker      diff_program = ["imgdiff"]
3821*9e94795aSAndroid Build Coastguard Worker      if os.path.exists(path):
3822*9e94795aSAndroid Build Coastguard Worker        diff_program.append("-b")
3823*9e94795aSAndroid Build Coastguard Worker        diff_program.append(path)
3824*9e94795aSAndroid Build Coastguard Worker        bonus_args = "--bonus /vendor/etc/recovery-resource.dat"
3825*9e94795aSAndroid Build Coastguard Worker      else:
3826*9e94795aSAndroid Build Coastguard Worker        bonus_args = ""
3827*9e94795aSAndroid Build Coastguard Worker
3828*9e94795aSAndroid Build Coastguard Worker    d = Difference(recovery_img, boot_img, diff_program=diff_program)
3829*9e94795aSAndroid Build Coastguard Worker    _, _, patch = d.ComputePatch()
3830*9e94795aSAndroid Build Coastguard Worker    output_sink("recovery-from-boot.p", patch)
3831*9e94795aSAndroid Build Coastguard Worker
3832*9e94795aSAndroid Build Coastguard Worker  try:
3833*9e94795aSAndroid Build Coastguard Worker    # The following GetTypeAndDevice()s need to use the path in the target
3834*9e94795aSAndroid Build Coastguard Worker    # info_dict instead of source_info_dict.
3835*9e94795aSAndroid Build Coastguard Worker    boot_type, boot_device = GetTypeAndDevice("/boot", info_dict,
3836*9e94795aSAndroid Build Coastguard Worker                                              check_no_slot=False)
3837*9e94795aSAndroid Build Coastguard Worker    recovery_type, recovery_device = GetTypeAndDevice("/recovery", info_dict,
3838*9e94795aSAndroid Build Coastguard Worker                                                      check_no_slot=False)
3839*9e94795aSAndroid Build Coastguard Worker  except KeyError:
3840*9e94795aSAndroid Build Coastguard Worker    return
3841*9e94795aSAndroid Build Coastguard Worker
3842*9e94795aSAndroid Build Coastguard Worker  if full_recovery_image:
3843*9e94795aSAndroid Build Coastguard Worker
3844*9e94795aSAndroid Build Coastguard Worker    # Note that we use /vendor to refer to the recovery resources. This will
3845*9e94795aSAndroid Build Coastguard Worker    # work for a separate vendor partition mounted at /vendor or a
3846*9e94795aSAndroid Build Coastguard Worker    # /system/vendor subdirectory on the system partition, for which init will
3847*9e94795aSAndroid Build Coastguard Worker    # create a symlink from /vendor to /system/vendor.
3848*9e94795aSAndroid Build Coastguard Worker
3849*9e94795aSAndroid Build Coastguard Worker    sh = """#!/vendor/bin/sh
3850*9e94795aSAndroid Build Coastguard Workerif ! applypatch --check %(type)s:%(device)s:%(size)d:%(sha1)s; then
3851*9e94795aSAndroid Build Coastguard Worker  applypatch \\
3852*9e94795aSAndroid Build Coastguard Worker          --flash /vendor/etc/recovery.img \\
3853*9e94795aSAndroid Build Coastguard Worker          --target %(type)s:%(device)s:%(size)d:%(sha1)s && \\
3854*9e94795aSAndroid Build Coastguard Worker      log -t recovery "Installing new recovery image: succeeded" || \\
3855*9e94795aSAndroid Build Coastguard Worker      log -t recovery "Installing new recovery image: failed"
3856*9e94795aSAndroid Build Coastguard Workerelse
3857*9e94795aSAndroid Build Coastguard Worker  log -t recovery "Recovery image already installed"
3858*9e94795aSAndroid Build Coastguard Workerfi
3859*9e94795aSAndroid Build Coastguard Worker""" % {'type': recovery_type,
3860*9e94795aSAndroid Build Coastguard Worker       'device': recovery_device,
3861*9e94795aSAndroid Build Coastguard Worker       'sha1': recovery_img.sha1,
3862*9e94795aSAndroid Build Coastguard Worker       'size': recovery_img.size}
3863*9e94795aSAndroid Build Coastguard Worker  else:
3864*9e94795aSAndroid Build Coastguard Worker    sh = """#!/vendor/bin/sh
3865*9e94795aSAndroid Build Coastguard Workerif ! applypatch --check %(recovery_type)s:%(recovery_device)s:%(recovery_size)d:%(recovery_sha1)s; then
3866*9e94795aSAndroid Build Coastguard Worker  applypatch %(bonus_args)s \\
3867*9e94795aSAndroid Build Coastguard Worker          --patch /vendor/recovery-from-boot.p \\
3868*9e94795aSAndroid Build Coastguard Worker          --source %(boot_type)s:%(boot_device)s:%(boot_size)d:%(boot_sha1)s \\
3869*9e94795aSAndroid Build Coastguard Worker          --target %(recovery_type)s:%(recovery_device)s:%(recovery_size)d:%(recovery_sha1)s && \\
3870*9e94795aSAndroid Build Coastguard Worker      log -t recovery "Installing new recovery image: succeeded" || \\
3871*9e94795aSAndroid Build Coastguard Worker      log -t recovery "Installing new recovery image: failed"
3872*9e94795aSAndroid Build Coastguard Workerelse
3873*9e94795aSAndroid Build Coastguard Worker  log -t recovery "Recovery image already installed"
3874*9e94795aSAndroid Build Coastguard Workerfi
3875*9e94795aSAndroid Build Coastguard Worker""" % {'boot_size': boot_img.size,
3876*9e94795aSAndroid Build Coastguard Worker       'boot_sha1': boot_img.sha1,
3877*9e94795aSAndroid Build Coastguard Worker       'recovery_size': recovery_img.size,
3878*9e94795aSAndroid Build Coastguard Worker       'recovery_sha1': recovery_img.sha1,
3879*9e94795aSAndroid Build Coastguard Worker       'boot_type': boot_type,
3880*9e94795aSAndroid Build Coastguard Worker       'boot_device': boot_device + '$(getprop ro.boot.slot_suffix)',
3881*9e94795aSAndroid Build Coastguard Worker       'recovery_type': recovery_type,
3882*9e94795aSAndroid Build Coastguard Worker       'recovery_device': recovery_device + '$(getprop ro.boot.slot_suffix)',
3883*9e94795aSAndroid Build Coastguard Worker       'bonus_args': bonus_args}
3884*9e94795aSAndroid Build Coastguard Worker
3885*9e94795aSAndroid Build Coastguard Worker  # The install script location moved from /system/etc to /system/bin in the L
3886*9e94795aSAndroid Build Coastguard Worker  # release. In the R release it is in VENDOR/bin or SYSTEM/vendor/bin.
3887*9e94795aSAndroid Build Coastguard Worker  sh_location = os.path.join(sh_dir, "install-recovery.sh")
3888*9e94795aSAndroid Build Coastguard Worker
3889*9e94795aSAndroid Build Coastguard Worker  logger.info("putting script in %s", sh_location)
3890*9e94795aSAndroid Build Coastguard Worker
3891*9e94795aSAndroid Build Coastguard Worker  output_sink(sh_location, sh.encode())
3892*9e94795aSAndroid Build Coastguard Worker
3893*9e94795aSAndroid Build Coastguard Worker
3894*9e94795aSAndroid Build Coastguard Workerclass DynamicPartitionUpdate(object):
3895*9e94795aSAndroid Build Coastguard Worker  def __init__(self, src_group=None, tgt_group=None, progress=None,
3896*9e94795aSAndroid Build Coastguard Worker               block_difference=None):
3897*9e94795aSAndroid Build Coastguard Worker    self.src_group = src_group
3898*9e94795aSAndroid Build Coastguard Worker    self.tgt_group = tgt_group
3899*9e94795aSAndroid Build Coastguard Worker    self.progress = progress
3900*9e94795aSAndroid Build Coastguard Worker    self.block_difference = block_difference
3901*9e94795aSAndroid Build Coastguard Worker
3902*9e94795aSAndroid Build Coastguard Worker  @property
3903*9e94795aSAndroid Build Coastguard Worker  def src_size(self):
3904*9e94795aSAndroid Build Coastguard Worker    if not self.block_difference:
3905*9e94795aSAndroid Build Coastguard Worker      return 0
3906*9e94795aSAndroid Build Coastguard Worker    return DynamicPartitionUpdate._GetSparseImageSize(self.block_difference.src)
3907*9e94795aSAndroid Build Coastguard Worker
3908*9e94795aSAndroid Build Coastguard Worker  @property
3909*9e94795aSAndroid Build Coastguard Worker  def tgt_size(self):
3910*9e94795aSAndroid Build Coastguard Worker    if not self.block_difference:
3911*9e94795aSAndroid Build Coastguard Worker      return 0
3912*9e94795aSAndroid Build Coastguard Worker    return DynamicPartitionUpdate._GetSparseImageSize(self.block_difference.tgt)
3913*9e94795aSAndroid Build Coastguard Worker
3914*9e94795aSAndroid Build Coastguard Worker  @staticmethod
3915*9e94795aSAndroid Build Coastguard Worker  def _GetSparseImageSize(img):
3916*9e94795aSAndroid Build Coastguard Worker    if not img:
3917*9e94795aSAndroid Build Coastguard Worker      return 0
3918*9e94795aSAndroid Build Coastguard Worker    return img.blocksize * img.total_blocks
3919*9e94795aSAndroid Build Coastguard Worker
3920*9e94795aSAndroid Build Coastguard Worker
3921*9e94795aSAndroid Build Coastguard Workerclass DynamicGroupUpdate(object):
3922*9e94795aSAndroid Build Coastguard Worker  def __init__(self, src_size=None, tgt_size=None):
3923*9e94795aSAndroid Build Coastguard Worker    # None: group does not exist. 0: no size limits.
3924*9e94795aSAndroid Build Coastguard Worker    self.src_size = src_size
3925*9e94795aSAndroid Build Coastguard Worker    self.tgt_size = tgt_size
3926*9e94795aSAndroid Build Coastguard Worker
3927*9e94795aSAndroid Build Coastguard Worker
3928*9e94795aSAndroid Build Coastguard Workerclass DynamicPartitionsDifference(object):
3929*9e94795aSAndroid Build Coastguard Worker  def __init__(self, info_dict, block_diffs, progress_dict=None,
3930*9e94795aSAndroid Build Coastguard Worker               source_info_dict=None):
3931*9e94795aSAndroid Build Coastguard Worker    if progress_dict is None:
3932*9e94795aSAndroid Build Coastguard Worker      progress_dict = {}
3933*9e94795aSAndroid Build Coastguard Worker
3934*9e94795aSAndroid Build Coastguard Worker    self._remove_all_before_apply = False
3935*9e94795aSAndroid Build Coastguard Worker    if source_info_dict is None:
3936*9e94795aSAndroid Build Coastguard Worker      self._remove_all_before_apply = True
3937*9e94795aSAndroid Build Coastguard Worker      source_info_dict = {}
3938*9e94795aSAndroid Build Coastguard Worker
3939*9e94795aSAndroid Build Coastguard Worker    block_diff_dict = collections.OrderedDict(
3940*9e94795aSAndroid Build Coastguard Worker        [(e.partition, e) for e in block_diffs])
3941*9e94795aSAndroid Build Coastguard Worker
3942*9e94795aSAndroid Build Coastguard Worker    assert len(block_diff_dict) == len(block_diffs), \
3943*9e94795aSAndroid Build Coastguard Worker        "Duplicated BlockDifference object for {}".format(
3944*9e94795aSAndroid Build Coastguard Worker            [partition for partition, count in
3945*9e94795aSAndroid Build Coastguard Worker             collections.Counter(e.partition for e in block_diffs).items()
3946*9e94795aSAndroid Build Coastguard Worker             if count > 1])
3947*9e94795aSAndroid Build Coastguard Worker
3948*9e94795aSAndroid Build Coastguard Worker    self._partition_updates = collections.OrderedDict()
3949*9e94795aSAndroid Build Coastguard Worker
3950*9e94795aSAndroid Build Coastguard Worker    for p, block_diff in block_diff_dict.items():
3951*9e94795aSAndroid Build Coastguard Worker      self._partition_updates[p] = DynamicPartitionUpdate()
3952*9e94795aSAndroid Build Coastguard Worker      self._partition_updates[p].block_difference = block_diff
3953*9e94795aSAndroid Build Coastguard Worker
3954*9e94795aSAndroid Build Coastguard Worker    for p, progress in progress_dict.items():
3955*9e94795aSAndroid Build Coastguard Worker      if p in self._partition_updates:
3956*9e94795aSAndroid Build Coastguard Worker        self._partition_updates[p].progress = progress
3957*9e94795aSAndroid Build Coastguard Worker
3958*9e94795aSAndroid Build Coastguard Worker    tgt_groups = shlex.split(info_dict.get(
3959*9e94795aSAndroid Build Coastguard Worker        "super_partition_groups", "").strip())
3960*9e94795aSAndroid Build Coastguard Worker    src_groups = shlex.split(source_info_dict.get(
3961*9e94795aSAndroid Build Coastguard Worker        "super_partition_groups", "").strip())
3962*9e94795aSAndroid Build Coastguard Worker
3963*9e94795aSAndroid Build Coastguard Worker    for g in tgt_groups:
3964*9e94795aSAndroid Build Coastguard Worker      for p in shlex.split(info_dict.get(
3965*9e94795aSAndroid Build Coastguard Worker              "super_%s_partition_list" % g, "").strip()):
3966*9e94795aSAndroid Build Coastguard Worker        assert p in self._partition_updates, \
3967*9e94795aSAndroid Build Coastguard Worker            "{} is in target super_{}_partition_list but no BlockDifference " \
3968*9e94795aSAndroid Build Coastguard Worker            "object is provided.".format(p, g)
3969*9e94795aSAndroid Build Coastguard Worker        self._partition_updates[p].tgt_group = g
3970*9e94795aSAndroid Build Coastguard Worker
3971*9e94795aSAndroid Build Coastguard Worker    for g in src_groups:
3972*9e94795aSAndroid Build Coastguard Worker      for p in shlex.split(source_info_dict.get(
3973*9e94795aSAndroid Build Coastguard Worker              "super_%s_partition_list" % g, "").strip()):
3974*9e94795aSAndroid Build Coastguard Worker        assert p in self._partition_updates, \
3975*9e94795aSAndroid Build Coastguard Worker            "{} is in source super_{}_partition_list but no BlockDifference " \
3976*9e94795aSAndroid Build Coastguard Worker            "object is provided.".format(p, g)
3977*9e94795aSAndroid Build Coastguard Worker        self._partition_updates[p].src_group = g
3978*9e94795aSAndroid Build Coastguard Worker
3979*9e94795aSAndroid Build Coastguard Worker    target_dynamic_partitions = set(shlex.split(info_dict.get(
3980*9e94795aSAndroid Build Coastguard Worker        "dynamic_partition_list", "").strip()))
3981*9e94795aSAndroid Build Coastguard Worker    block_diffs_with_target = set(p for p, u in self._partition_updates.items()
3982*9e94795aSAndroid Build Coastguard Worker                                  if u.tgt_size)
3983*9e94795aSAndroid Build Coastguard Worker    assert block_diffs_with_target == target_dynamic_partitions, \
3984*9e94795aSAndroid Build Coastguard Worker        "Target Dynamic partitions: {}, BlockDifference with target: {}".format(
3985*9e94795aSAndroid Build Coastguard Worker            list(target_dynamic_partitions), list(block_diffs_with_target))
3986*9e94795aSAndroid Build Coastguard Worker
3987*9e94795aSAndroid Build Coastguard Worker    source_dynamic_partitions = set(shlex.split(source_info_dict.get(
3988*9e94795aSAndroid Build Coastguard Worker        "dynamic_partition_list", "").strip()))
3989*9e94795aSAndroid Build Coastguard Worker    block_diffs_with_source = set(p for p, u in self._partition_updates.items()
3990*9e94795aSAndroid Build Coastguard Worker                                  if u.src_size)
3991*9e94795aSAndroid Build Coastguard Worker    assert block_diffs_with_source == source_dynamic_partitions, \
3992*9e94795aSAndroid Build Coastguard Worker        "Source Dynamic partitions: {}, BlockDifference with source: {}".format(
3993*9e94795aSAndroid Build Coastguard Worker            list(source_dynamic_partitions), list(block_diffs_with_source))
3994*9e94795aSAndroid Build Coastguard Worker
3995*9e94795aSAndroid Build Coastguard Worker    if self._partition_updates:
3996*9e94795aSAndroid Build Coastguard Worker      logger.info("Updating dynamic partitions %s",
3997*9e94795aSAndroid Build Coastguard Worker                  self._partition_updates.keys())
3998*9e94795aSAndroid Build Coastguard Worker
3999*9e94795aSAndroid Build Coastguard Worker    self._group_updates = collections.OrderedDict()
4000*9e94795aSAndroid Build Coastguard Worker
4001*9e94795aSAndroid Build Coastguard Worker    for g in tgt_groups:
4002*9e94795aSAndroid Build Coastguard Worker      self._group_updates[g] = DynamicGroupUpdate()
4003*9e94795aSAndroid Build Coastguard Worker      self._group_updates[g].tgt_size = int(info_dict.get(
4004*9e94795aSAndroid Build Coastguard Worker          "super_%s_group_size" % g, "0").strip())
4005*9e94795aSAndroid Build Coastguard Worker
4006*9e94795aSAndroid Build Coastguard Worker    for g in src_groups:
4007*9e94795aSAndroid Build Coastguard Worker      if g not in self._group_updates:
4008*9e94795aSAndroid Build Coastguard Worker        self._group_updates[g] = DynamicGroupUpdate()
4009*9e94795aSAndroid Build Coastguard Worker      self._group_updates[g].src_size = int(source_info_dict.get(
4010*9e94795aSAndroid Build Coastguard Worker          "super_%s_group_size" % g, "0").strip())
4011*9e94795aSAndroid Build Coastguard Worker
4012*9e94795aSAndroid Build Coastguard Worker    self._Compute()
4013*9e94795aSAndroid Build Coastguard Worker
4014*9e94795aSAndroid Build Coastguard Worker  def WriteScript(self, script, output_zip, write_verify_script=False):
4015*9e94795aSAndroid Build Coastguard Worker    script.Comment('--- Start patching dynamic partitions ---')
4016*9e94795aSAndroid Build Coastguard Worker    for p, u in self._partition_updates.items():
4017*9e94795aSAndroid Build Coastguard Worker      if u.src_size and u.tgt_size and u.src_size > u.tgt_size:
4018*9e94795aSAndroid Build Coastguard Worker        script.Comment('Patch partition %s' % p)
4019*9e94795aSAndroid Build Coastguard Worker        u.block_difference.WriteScript(script, output_zip, progress=u.progress,
4020*9e94795aSAndroid Build Coastguard Worker                                       write_verify_script=False)
4021*9e94795aSAndroid Build Coastguard Worker
4022*9e94795aSAndroid Build Coastguard Worker    op_list_path = MakeTempFile()
4023*9e94795aSAndroid Build Coastguard Worker    with open(op_list_path, 'w') as f:
4024*9e94795aSAndroid Build Coastguard Worker      for line in self._op_list:
4025*9e94795aSAndroid Build Coastguard Worker        f.write('{}\n'.format(line))
4026*9e94795aSAndroid Build Coastguard Worker
4027*9e94795aSAndroid Build Coastguard Worker    ZipWrite(output_zip, op_list_path, "dynamic_partitions_op_list")
4028*9e94795aSAndroid Build Coastguard Worker
4029*9e94795aSAndroid Build Coastguard Worker    script.Comment('Update dynamic partition metadata')
4030*9e94795aSAndroid Build Coastguard Worker    script.AppendExtra('assert(update_dynamic_partitions('
4031*9e94795aSAndroid Build Coastguard Worker                       'package_extract_file("dynamic_partitions_op_list")));')
4032*9e94795aSAndroid Build Coastguard Worker
4033*9e94795aSAndroid Build Coastguard Worker    if write_verify_script:
4034*9e94795aSAndroid Build Coastguard Worker      for p, u in self._partition_updates.items():
4035*9e94795aSAndroid Build Coastguard Worker        if u.src_size and u.tgt_size and u.src_size > u.tgt_size:
4036*9e94795aSAndroid Build Coastguard Worker          u.block_difference.WritePostInstallVerifyScript(script)
4037*9e94795aSAndroid Build Coastguard Worker          script.AppendExtra('unmap_partition("%s");' % p)  # ignore errors
4038*9e94795aSAndroid Build Coastguard Worker
4039*9e94795aSAndroid Build Coastguard Worker    for p, u in self._partition_updates.items():
4040*9e94795aSAndroid Build Coastguard Worker      if u.tgt_size and u.src_size <= u.tgt_size:
4041*9e94795aSAndroid Build Coastguard Worker        script.Comment('Patch partition %s' % p)
4042*9e94795aSAndroid Build Coastguard Worker        u.block_difference.WriteScript(script, output_zip, progress=u.progress,
4043*9e94795aSAndroid Build Coastguard Worker                                       write_verify_script=write_verify_script)
4044*9e94795aSAndroid Build Coastguard Worker        if write_verify_script:
4045*9e94795aSAndroid Build Coastguard Worker          script.AppendExtra('unmap_partition("%s");' % p)  # ignore errors
4046*9e94795aSAndroid Build Coastguard Worker
4047*9e94795aSAndroid Build Coastguard Worker    script.Comment('--- End patching dynamic partitions ---')
4048*9e94795aSAndroid Build Coastguard Worker
4049*9e94795aSAndroid Build Coastguard Worker  def _Compute(self):
4050*9e94795aSAndroid Build Coastguard Worker    self._op_list = list()
4051*9e94795aSAndroid Build Coastguard Worker
4052*9e94795aSAndroid Build Coastguard Worker    def append(line):
4053*9e94795aSAndroid Build Coastguard Worker      self._op_list.append(line)
4054*9e94795aSAndroid Build Coastguard Worker
4055*9e94795aSAndroid Build Coastguard Worker    def comment(line):
4056*9e94795aSAndroid Build Coastguard Worker      self._op_list.append("# %s" % line)
4057*9e94795aSAndroid Build Coastguard Worker
4058*9e94795aSAndroid Build Coastguard Worker    if self._remove_all_before_apply:
4059*9e94795aSAndroid Build Coastguard Worker      comment('Remove all existing dynamic partitions and groups before '
4060*9e94795aSAndroid Build Coastguard Worker              'applying full OTA')
4061*9e94795aSAndroid Build Coastguard Worker      append('remove_all_groups')
4062*9e94795aSAndroid Build Coastguard Worker
4063*9e94795aSAndroid Build Coastguard Worker    for p, u in self._partition_updates.items():
4064*9e94795aSAndroid Build Coastguard Worker      if u.src_group and not u.tgt_group:
4065*9e94795aSAndroid Build Coastguard Worker        append('remove %s' % p)
4066*9e94795aSAndroid Build Coastguard Worker
4067*9e94795aSAndroid Build Coastguard Worker    for p, u in self._partition_updates.items():
4068*9e94795aSAndroid Build Coastguard Worker      if u.src_group and u.tgt_group and u.src_group != u.tgt_group:
4069*9e94795aSAndroid Build Coastguard Worker        comment('Move partition %s from %s to default' % (p, u.src_group))
4070*9e94795aSAndroid Build Coastguard Worker        append('move %s default' % p)
4071*9e94795aSAndroid Build Coastguard Worker
4072*9e94795aSAndroid Build Coastguard Worker    for p, u in self._partition_updates.items():
4073*9e94795aSAndroid Build Coastguard Worker      if u.src_size and u.tgt_size and u.src_size > u.tgt_size:
4074*9e94795aSAndroid Build Coastguard Worker        comment('Shrink partition %s from %d to %d' %
4075*9e94795aSAndroid Build Coastguard Worker                (p, u.src_size, u.tgt_size))
4076*9e94795aSAndroid Build Coastguard Worker        append('resize %s %s' % (p, u.tgt_size))
4077*9e94795aSAndroid Build Coastguard Worker
4078*9e94795aSAndroid Build Coastguard Worker    for g, u in self._group_updates.items():
4079*9e94795aSAndroid Build Coastguard Worker      if u.src_size is not None and u.tgt_size is None:
4080*9e94795aSAndroid Build Coastguard Worker        append('remove_group %s' % g)
4081*9e94795aSAndroid Build Coastguard Worker      if (u.src_size is not None and u.tgt_size is not None and
4082*9e94795aSAndroid Build Coastguard Worker              u.src_size > u.tgt_size):
4083*9e94795aSAndroid Build Coastguard Worker        comment('Shrink group %s from %d to %d' % (g, u.src_size, u.tgt_size))
4084*9e94795aSAndroid Build Coastguard Worker        append('resize_group %s %d' % (g, u.tgt_size))
4085*9e94795aSAndroid Build Coastguard Worker
4086*9e94795aSAndroid Build Coastguard Worker    for g, u in self._group_updates.items():
4087*9e94795aSAndroid Build Coastguard Worker      if u.src_size is None and u.tgt_size is not None:
4088*9e94795aSAndroid Build Coastguard Worker        comment('Add group %s with maximum size %d' % (g, u.tgt_size))
4089*9e94795aSAndroid Build Coastguard Worker        append('add_group %s %d' % (g, u.tgt_size))
4090*9e94795aSAndroid Build Coastguard Worker      if (u.src_size is not None and u.tgt_size is not None and
4091*9e94795aSAndroid Build Coastguard Worker              u.src_size < u.tgt_size):
4092*9e94795aSAndroid Build Coastguard Worker        comment('Grow group %s from %d to %d' % (g, u.src_size, u.tgt_size))
4093*9e94795aSAndroid Build Coastguard Worker        append('resize_group %s %d' % (g, u.tgt_size))
4094*9e94795aSAndroid Build Coastguard Worker
4095*9e94795aSAndroid Build Coastguard Worker    for p, u in self._partition_updates.items():
4096*9e94795aSAndroid Build Coastguard Worker      if u.tgt_group and not u.src_group:
4097*9e94795aSAndroid Build Coastguard Worker        comment('Add partition %s to group %s' % (p, u.tgt_group))
4098*9e94795aSAndroid Build Coastguard Worker        append('add %s %s' % (p, u.tgt_group))
4099*9e94795aSAndroid Build Coastguard Worker
4100*9e94795aSAndroid Build Coastguard Worker    for p, u in self._partition_updates.items():
4101*9e94795aSAndroid Build Coastguard Worker      if u.tgt_size and u.src_size < u.tgt_size:
4102*9e94795aSAndroid Build Coastguard Worker        comment('Grow partition %s from %d to %d' %
4103*9e94795aSAndroid Build Coastguard Worker                (p, u.src_size, u.tgt_size))
4104*9e94795aSAndroid Build Coastguard Worker        append('resize %s %d' % (p, u.tgt_size))
4105*9e94795aSAndroid Build Coastguard Worker
4106*9e94795aSAndroid Build Coastguard Worker    for p, u in self._partition_updates.items():
4107*9e94795aSAndroid Build Coastguard Worker      if u.src_group and u.tgt_group and u.src_group != u.tgt_group:
4108*9e94795aSAndroid Build Coastguard Worker        comment('Move partition %s from default to %s' %
4109*9e94795aSAndroid Build Coastguard Worker                (p, u.tgt_group))
4110*9e94795aSAndroid Build Coastguard Worker        append('move %s %s' % (p, u.tgt_group))
4111*9e94795aSAndroid Build Coastguard Worker
4112*9e94795aSAndroid Build Coastguard Worker
4113*9e94795aSAndroid Build Coastguard Workerdef GetBootImageBuildProp(boot_img, ramdisk_format=RamdiskFormat.LZ4):
4114*9e94795aSAndroid Build Coastguard Worker  """
4115*9e94795aSAndroid Build Coastguard Worker  Get build.prop from ramdisk within the boot image
4116*9e94795aSAndroid Build Coastguard Worker
4117*9e94795aSAndroid Build Coastguard Worker  Args:
4118*9e94795aSAndroid Build Coastguard Worker    boot_img: the boot image file. Ramdisk must be compressed with lz4 or gzip format.
4119*9e94795aSAndroid Build Coastguard Worker
4120*9e94795aSAndroid Build Coastguard Worker  Return:
4121*9e94795aSAndroid Build Coastguard Worker    An extracted file that stores properties in the boot image.
4122*9e94795aSAndroid Build Coastguard Worker  """
4123*9e94795aSAndroid Build Coastguard Worker  tmp_dir = MakeTempDir('boot_', suffix='.img')
4124*9e94795aSAndroid Build Coastguard Worker  try:
4125*9e94795aSAndroid Build Coastguard Worker    RunAndCheckOutput(['unpack_bootimg', '--boot_img',
4126*9e94795aSAndroid Build Coastguard Worker                      boot_img, '--out', tmp_dir])
4127*9e94795aSAndroid Build Coastguard Worker    ramdisk = os.path.join(tmp_dir, 'ramdisk')
4128*9e94795aSAndroid Build Coastguard Worker    if not os.path.isfile(ramdisk):
4129*9e94795aSAndroid Build Coastguard Worker      logger.warning('Unable to get boot image timestamp: no ramdisk in boot')
4130*9e94795aSAndroid Build Coastguard Worker      return None
4131*9e94795aSAndroid Build Coastguard Worker    uncompressed_ramdisk = os.path.join(tmp_dir, 'uncompressed_ramdisk')
4132*9e94795aSAndroid Build Coastguard Worker    if ramdisk_format == RamdiskFormat.LZ4:
4133*9e94795aSAndroid Build Coastguard Worker      RunAndCheckOutput(['lz4', '-d', ramdisk, uncompressed_ramdisk])
4134*9e94795aSAndroid Build Coastguard Worker    elif ramdisk_format == RamdiskFormat.GZ:
4135*9e94795aSAndroid Build Coastguard Worker      with open(ramdisk, 'rb') as input_stream:
4136*9e94795aSAndroid Build Coastguard Worker        with open(uncompressed_ramdisk, 'wb') as output_stream:
4137*9e94795aSAndroid Build Coastguard Worker          p2 = Run(['gzip', '-d'], stdin=input_stream.fileno(),
4138*9e94795aSAndroid Build Coastguard Worker                   stdout=output_stream.fileno())
4139*9e94795aSAndroid Build Coastguard Worker          p2.wait()
4140*9e94795aSAndroid Build Coastguard Worker    else:
4141*9e94795aSAndroid Build Coastguard Worker      logger.error('Only support lz4 or gzip ramdisk format.')
4142*9e94795aSAndroid Build Coastguard Worker      return None
4143*9e94795aSAndroid Build Coastguard Worker
4144*9e94795aSAndroid Build Coastguard Worker    abs_uncompressed_ramdisk = os.path.abspath(uncompressed_ramdisk)
4145*9e94795aSAndroid Build Coastguard Worker    extracted_ramdisk = MakeTempDir('extracted_ramdisk')
4146*9e94795aSAndroid Build Coastguard Worker    # Use "toybox cpio" instead of "cpio" because the latter invokes cpio from
4147*9e94795aSAndroid Build Coastguard Worker    # the host environment.
4148*9e94795aSAndroid Build Coastguard Worker    RunAndCheckOutput(['toybox', 'cpio', '-F', abs_uncompressed_ramdisk, '-i'],
4149*9e94795aSAndroid Build Coastguard Worker                      cwd=extracted_ramdisk)
4150*9e94795aSAndroid Build Coastguard Worker
4151*9e94795aSAndroid Build Coastguard Worker    for search_path in RAMDISK_BUILD_PROP_REL_PATHS:
4152*9e94795aSAndroid Build Coastguard Worker      prop_file = os.path.join(extracted_ramdisk, search_path)
4153*9e94795aSAndroid Build Coastguard Worker      if os.path.isfile(prop_file):
4154*9e94795aSAndroid Build Coastguard Worker        return prop_file
4155*9e94795aSAndroid Build Coastguard Worker      logger.warning(
4156*9e94795aSAndroid Build Coastguard Worker          'Unable to get boot image timestamp: no %s in ramdisk', search_path)
4157*9e94795aSAndroid Build Coastguard Worker
4158*9e94795aSAndroid Build Coastguard Worker    return None
4159*9e94795aSAndroid Build Coastguard Worker
4160*9e94795aSAndroid Build Coastguard Worker  except ExternalError as e:
4161*9e94795aSAndroid Build Coastguard Worker    logger.warning('Unable to get boot image build props: %s', e)
4162*9e94795aSAndroid Build Coastguard Worker    return None
4163*9e94795aSAndroid Build Coastguard Worker
4164*9e94795aSAndroid Build Coastguard Worker
4165*9e94795aSAndroid Build Coastguard Workerdef GetBootImageTimestamp(boot_img):
4166*9e94795aSAndroid Build Coastguard Worker  """
4167*9e94795aSAndroid Build Coastguard Worker  Get timestamp from ramdisk within the boot image
4168*9e94795aSAndroid Build Coastguard Worker
4169*9e94795aSAndroid Build Coastguard Worker  Args:
4170*9e94795aSAndroid Build Coastguard Worker    boot_img: the boot image file. Ramdisk must be compressed with lz4 format.
4171*9e94795aSAndroid Build Coastguard Worker
4172*9e94795aSAndroid Build Coastguard Worker  Return:
4173*9e94795aSAndroid Build Coastguard Worker    An integer that corresponds to the timestamp of the boot image, or None
4174*9e94795aSAndroid Build Coastguard Worker    if file has unknown format. Raise exception if an unexpected error has
4175*9e94795aSAndroid Build Coastguard Worker    occurred.
4176*9e94795aSAndroid Build Coastguard Worker  """
4177*9e94795aSAndroid Build Coastguard Worker  prop_file = GetBootImageBuildProp(boot_img)
4178*9e94795aSAndroid Build Coastguard Worker  if not prop_file:
4179*9e94795aSAndroid Build Coastguard Worker    return None
4180*9e94795aSAndroid Build Coastguard Worker
4181*9e94795aSAndroid Build Coastguard Worker  props = PartitionBuildProps.FromBuildPropFile('boot', prop_file)
4182*9e94795aSAndroid Build Coastguard Worker  if props is None:
4183*9e94795aSAndroid Build Coastguard Worker    return None
4184*9e94795aSAndroid Build Coastguard Worker
4185*9e94795aSAndroid Build Coastguard Worker  try:
4186*9e94795aSAndroid Build Coastguard Worker    timestamp = props.GetProp('ro.bootimage.build.date.utc')
4187*9e94795aSAndroid Build Coastguard Worker    if timestamp:
4188*9e94795aSAndroid Build Coastguard Worker      return int(timestamp)
4189*9e94795aSAndroid Build Coastguard Worker    logger.warning(
4190*9e94795aSAndroid Build Coastguard Worker        'Unable to get boot image timestamp: ro.bootimage.build.date.utc is undefined')
4191*9e94795aSAndroid Build Coastguard Worker    return None
4192*9e94795aSAndroid Build Coastguard Worker
4193*9e94795aSAndroid Build Coastguard Worker  except ExternalError as e:
4194*9e94795aSAndroid Build Coastguard Worker    logger.warning('Unable to get boot image timestamp: %s', e)
4195*9e94795aSAndroid Build Coastguard Worker    return None
4196*9e94795aSAndroid Build Coastguard Worker
4197*9e94795aSAndroid Build Coastguard Worker
4198*9e94795aSAndroid Build Coastguard Workerdef IsSparseImage(filepath):
4199*9e94795aSAndroid Build Coastguard Worker  if not os.path.exists(filepath):
4200*9e94795aSAndroid Build Coastguard Worker    return False
4201*9e94795aSAndroid Build Coastguard Worker  with open(filepath, 'rb') as fp:
4202*9e94795aSAndroid Build Coastguard Worker    # Magic for android sparse image format
4203*9e94795aSAndroid Build Coastguard Worker    # https://source.android.com/devices/bootloader/images
4204*9e94795aSAndroid Build Coastguard Worker    return fp.read(4) == b'\x3A\xFF\x26\xED'
4205*9e94795aSAndroid Build Coastguard Worker
4206*9e94795aSAndroid Build Coastguard Worker
4207*9e94795aSAndroid Build Coastguard Workerdef UnsparseImage(filepath, target_path=None):
4208*9e94795aSAndroid Build Coastguard Worker  if not IsSparseImage(filepath):
4209*9e94795aSAndroid Build Coastguard Worker    return
4210*9e94795aSAndroid Build Coastguard Worker  if target_path is None:
4211*9e94795aSAndroid Build Coastguard Worker    tmp_img = MakeTempFile(suffix=".img")
4212*9e94795aSAndroid Build Coastguard Worker    RunAndCheckOutput(["simg2img", filepath, tmp_img])
4213*9e94795aSAndroid Build Coastguard Worker    os.rename(tmp_img, filepath)
4214*9e94795aSAndroid Build Coastguard Worker  else:
4215*9e94795aSAndroid Build Coastguard Worker    RunAndCheckOutput(["simg2img", filepath, target_path])
4216*9e94795aSAndroid Build Coastguard Worker
4217*9e94795aSAndroid Build Coastguard Worker
4218*9e94795aSAndroid Build Coastguard Workerdef ParseUpdateEngineConfig(path: str):
4219*9e94795aSAndroid Build Coastguard Worker  """Parse the update_engine config stored in file `path`
4220*9e94795aSAndroid Build Coastguard Worker  Args
4221*9e94795aSAndroid Build Coastguard Worker    path: Path to update_engine_config.txt file in target_files
4222*9e94795aSAndroid Build Coastguard Worker
4223*9e94795aSAndroid Build Coastguard Worker  Returns
4224*9e94795aSAndroid Build Coastguard Worker    A tuple of (major, minor) version number . E.g. (2, 8)
4225*9e94795aSAndroid Build Coastguard Worker  """
4226*9e94795aSAndroid Build Coastguard Worker  with open(path, "r") as fp:
4227*9e94795aSAndroid Build Coastguard Worker    # update_engine_config.txt is only supposed to contain two lines,
4228*9e94795aSAndroid Build Coastguard Worker    # PAYLOAD_MAJOR_VERSION and PAYLOAD_MINOR_VERSION. 1024 should be more than
4229*9e94795aSAndroid Build Coastguard Worker    # sufficient. If the length is more than that, something is wrong.
4230*9e94795aSAndroid Build Coastguard Worker    data = fp.read(1024)
4231*9e94795aSAndroid Build Coastguard Worker    major = re.search(r"PAYLOAD_MAJOR_VERSION=(\d+)", data)
4232*9e94795aSAndroid Build Coastguard Worker    if not major:
4233*9e94795aSAndroid Build Coastguard Worker      raise ValueError(
4234*9e94795aSAndroid Build Coastguard Worker          f"{path} is an invalid update_engine config, missing PAYLOAD_MAJOR_VERSION {data}")
4235*9e94795aSAndroid Build Coastguard Worker    minor = re.search(r"PAYLOAD_MINOR_VERSION=(\d+)", data)
4236*9e94795aSAndroid Build Coastguard Worker    if not minor:
4237*9e94795aSAndroid Build Coastguard Worker      raise ValueError(
4238*9e94795aSAndroid Build Coastguard Worker          f"{path} is an invalid update_engine config, missing PAYLOAD_MINOR_VERSION {data}")
4239*9e94795aSAndroid Build Coastguard Worker    return (int(major.group(1)), int(minor.group(1)))
4240