1#!/usr/bin/env python
2#
3# Copyright (C) 2019 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License"); you may not
6# use this file except in compliance with the License. You may obtain a copy of
7# the License at
8#
9#      http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14# License for the specific language governing permissions and limitations under
15# the License.
16#
17"""This script merges two partial target files packages.
18
19One input package contains framework files, and the other contains vendor files.
20
21This script produces a complete, merged target files package:
22  - This package can be used to generate a flashable IMG package.
23    See --output-img.
24  - This package can be used to generate an OTA package. See --output-ota.
25  - The merged package is checked for compatibility between the two inputs.
26
27Usage: merge_target_files [args]
28
29  --framework-target-files framework-target-files-zip-archive
30      The input target files package containing framework bits. This is a zip
31      archive.
32
33  --framework-item-list framework-item-list-file
34      The optional path to a newline-separated config file that replaces the
35      contents of DEFAULT_FRAMEWORK_ITEM_LIST if provided.
36
37  --framework-misc-info-keys framework-misc-info-keys-file
38      The optional path to a newline-separated config file that replaces the
39      contents of DEFAULT_FRAMEWORK_MISC_INFO_KEYS if provided.
40
41  --vendor-target-files vendor-target-files-zip-archive
42      The input target files package containing vendor bits. This is a zip
43      archive.
44
45  --vendor-item-list vendor-item-list-file
46      The optional path to a newline-separated config file that replaces the
47      contents of DEFAULT_VENDOR_ITEM_LIST if provided.
48
49  --output-target-files output-target-files-package
50      If provided, the output merged target files package. Also a zip archive.
51
52  --output-dir output-directory
53      If provided, the destination directory for saving merged files. Requires
54      the --output-item-list flag.
55      Can be provided alongside --output-target-files, or by itself.
56
57  --output-item-list output-item-list-file.
58      The optional path to a newline-separated config file that specifies the
59      file patterns to copy into the --output-dir. Required if providing
60      the --output-dir flag.
61
62  --output-ota output-ota-package
63      The output ota package. This is a zip archive. Use of this flag may
64      require passing the --path common flag; see common.py.
65
66  --output-img output-img-package
67      The output img package, suitable for use with 'fastboot update'. Use of
68      this flag may require passing the --path common flag; see common.py.
69
70  --output-super-empty output-super-empty-image
71      If provided, creates a super_empty.img file from the merged target
72      files package and saves it at this path.
73
74  --rebuild_recovery
75      Deprecated; does nothing.
76
77  --allow-duplicate-apkapex-keys
78      If provided, duplicate APK/APEX keys are ignored and the value from the
79      framework is used.
80
81  --keep-tmp
82      Keep tempoary files for debugging purposes.
83"""
84
85from __future__ import print_function
86
87import fnmatch
88import json
89import logging
90import os
91import re
92import shutil
93import subprocess
94import sys
95import zipfile
96from xml.etree import ElementTree
97
98import add_img_to_target_files
99import apex_utils
100import build_image
101import build_super_image
102import check_target_files_vintf
103import common
104import img_from_target_files
105import find_shareduid_violation
106import ota_from_target_files
107import sparse_img
108import verity_utils
109
110from common import AddCareMapForAbOta, ExternalError, PARTITIONS_WITH_CARE_MAP
111
112logger = logging.getLogger(__name__)
113
114OPTIONS = common.OPTIONS
115# Always turn on verbose logging.
116OPTIONS.verbose = True
117OPTIONS.framework_target_files = None
118OPTIONS.framework_item_list = None
119OPTIONS.framework_misc_info_keys = None
120OPTIONS.vendor_target_files = None
121OPTIONS.vendor_item_list = None
122OPTIONS.output_target_files = None
123OPTIONS.output_dir = None
124OPTIONS.output_item_list = None
125OPTIONS.output_ota = None
126OPTIONS.output_img = None
127OPTIONS.output_super_empty = None
128# TODO(b/132730255): Remove this option.
129OPTIONS.rebuild_recovery = False
130# TODO(b/150582573): Remove this option.
131OPTIONS.allow_duplicate_apkapex_keys = False
132OPTIONS.keep_tmp = False
133
134# In an item list (framework or vendor), we may see entries that select whole
135# partitions. Such an entry might look like this 'SYSTEM/*' (e.g., for the
136# system partition). The following regex matches this and extracts the
137# partition name.
138
139PARTITION_ITEM_PATTERN = re.compile(r'^([A-Z_]+)/\*$')
140
141# In apexkeys.txt or apkcerts.txt, we will find partition tags on each entry in
142# the file. We use these partition tags to filter the entries in those files
143# from the two different target files packages to produce a merged apexkeys.txt
144# or apkcerts.txt file. A partition tag (e.g., for the product partition) looks
145# like this: 'partition="product"'. We use the group syntax grab the value of
146# the tag. We use non-greedy matching in case there are other fields on the
147# same line.
148
149PARTITION_TAG_PATTERN = re.compile(r'partition="(.*?)"')
150
151# The sorting algorithm for apexkeys.txt and apkcerts.txt does not include the
152# ".apex" or ".apk" suffix, so we use the following pattern to extract a key.
153
154MODULE_KEY_PATTERN = re.compile(r'name="(.+)\.(apex|apk)"')
155
156# DEFAULT_FRAMEWORK_ITEM_LIST is a list of items to extract from the partial
157# framework target files package as is, meaning these items will land in the
158# output target files package exactly as they appear in the input partial
159# framework target files package.
160
161DEFAULT_FRAMEWORK_ITEM_LIST = (
162    'META/apkcerts.txt',
163    'META/filesystem_config.txt',
164    'META/root_filesystem_config.txt',
165    'META/update_engine_config.txt',
166    'PRODUCT/*',
167    'ROOT/*',
168    'SYSTEM/*',
169)
170
171# DEFAULT_FRAMEWORK_MISC_INFO_KEYS is a list of keys to obtain from the
172# framework instance of META/misc_info.txt. The remaining keys should come
173# from the vendor instance.
174
175DEFAULT_FRAMEWORK_MISC_INFO_KEYS = (
176    'avb_system_hashtree_enable',
177    'avb_system_add_hashtree_footer_args',
178    'avb_system_key_path',
179    'avb_system_algorithm',
180    'avb_system_rollback_index_location',
181    'avb_product_hashtree_enable',
182    'avb_product_add_hashtree_footer_args',
183    'avb_system_ext_hashtree_enable',
184    'avb_system_ext_add_hashtree_footer_args',
185    'system_root_image',
186    'root_dir',
187    'ab_update',
188    'default_system_dev_certificate',
189    'system_size',
190    'building_system_image',
191    'building_system_ext_image',
192    'building_product_image',
193)
194
195# DEFAULT_VENDOR_ITEM_LIST is a list of items to extract from the partial
196# vendor target files package as is, meaning these items will land in the output
197# target files package exactly as they appear in the input partial vendor target
198# files package.
199
200DEFAULT_VENDOR_ITEM_LIST = (
201    'META/boot_filesystem_config.txt',
202    'META/otakeys.txt',
203    'META/releasetools.py',
204    'META/vendor_filesystem_config.txt',
205    'BOOT/*',
206    'DATA/*',
207    'ODM/*',
208    'OTA/android-info.txt',
209    'PREBUILT_IMAGES/*',
210    'RADIO/*',
211    'VENDOR/*',
212)
213
214# The merge config lists should not attempt to extract items from both
215# builds for any of the following partitions. The partitions in
216# SINGLE_BUILD_PARTITIONS should come entirely from a single build (either
217# framework or vendor, but not both).
218
219SINGLE_BUILD_PARTITIONS = (
220    'BOOT/',
221    'DATA/',
222    'ODM/',
223    'PRODUCT/',
224    'SYSTEM_EXT/',
225    'RADIO/',
226    'RECOVERY/',
227    'ROOT/',
228    'SYSTEM/',
229    'SYSTEM_OTHER/',
230    'VENDOR/',
231    'VENDOR_DLKM/',
232    'ODM_DLKM/',
233)
234
235
236def write_sorted_data(data, path):
237  """Writes the sorted contents of either a list or dict to file.
238
239  This function sorts the contents of the list or dict and then writes the
240  resulting sorted contents to a file specified by path.
241
242  Args:
243    data: The list or dict to sort and write.
244    path: Path to the file to write the sorted values to. The file at path will
245      be overridden if it exists.
246  """
247  with open(path, 'w') as output:
248    for entry in sorted(data):
249      out_str = '{}={}\n'.format(entry, data[entry]) if isinstance(
250          data, dict) else '{}\n'.format(entry)
251      output.write(out_str)
252
253
254def extract_items(target_files, target_files_temp_dir, extract_item_list):
255  """Extracts items from target files to temporary directory.
256
257  This function extracts from the specified target files zip archive into the
258  specified temporary directory, the items specified in the extract item list.
259
260  Args:
261    target_files: The target files zip archive from which to extract items.
262    target_files_temp_dir: The temporary directory where the extracted items
263      will land.
264    extract_item_list: A list of items to extract.
265  """
266
267  logger.info('extracting from %s', target_files)
268
269  # Filter the extract_item_list to remove any items that do not exist in the
270  # zip file. Otherwise, the extraction step will fail.
271
272  with zipfile.ZipFile(target_files, allowZip64=True) as target_files_zipfile:
273    target_files_namelist = target_files_zipfile.namelist()
274
275  filtered_extract_item_list = []
276  for pattern in extract_item_list:
277    matching_namelist = fnmatch.filter(target_files_namelist, pattern)
278    if not matching_namelist:
279      logger.warning('no match for %s', pattern)
280    else:
281      filtered_extract_item_list.append(pattern)
282
283  # Extract from target_files into target_files_temp_dir the
284  # filtered_extract_item_list.
285
286  common.UnzipToDir(target_files, target_files_temp_dir,
287                    filtered_extract_item_list)
288
289
290def copy_items(from_dir, to_dir, patterns):
291  """Similar to extract_items() except uses an input dir instead of zip."""
292  file_paths = []
293  for dirpath, _, filenames in os.walk(from_dir):
294    file_paths.extend(
295        os.path.relpath(path=os.path.join(dirpath, filename), start=from_dir)
296        for filename in filenames)
297
298  filtered_file_paths = set()
299  for pattern in patterns:
300    filtered_file_paths.update(fnmatch.filter(file_paths, pattern))
301
302  for file_path in filtered_file_paths:
303    original_file_path = os.path.join(from_dir, file_path)
304    copied_file_path = os.path.join(to_dir, file_path)
305    copied_file_dir = os.path.dirname(copied_file_path)
306    if not os.path.exists(copied_file_dir):
307      os.makedirs(copied_file_dir)
308    if os.path.islink(original_file_path):
309      os.symlink(os.readlink(original_file_path), copied_file_path)
310    else:
311      shutil.copyfile(original_file_path, copied_file_path)
312
313
314def validate_config_lists(framework_item_list, framework_misc_info_keys,
315                          vendor_item_list):
316  """Performs validations on the merge config lists.
317
318  Args:
319    framework_item_list: The list of items to extract from the partial framework
320      target files package as is.
321    framework_misc_info_keys: A list of keys to obtain from the framework
322      instance of META/misc_info.txt. The remaining keys should come from the
323      vendor instance.
324    vendor_item_list: The list of items to extract from the partial vendor
325      target files package as is.
326
327  Returns:
328    False if a validation fails, otherwise true.
329  """
330  has_error = False
331
332  default_combined_item_set = set(DEFAULT_FRAMEWORK_ITEM_LIST)
333  default_combined_item_set.update(DEFAULT_VENDOR_ITEM_LIST)
334
335  combined_item_set = set(framework_item_list)
336  combined_item_set.update(vendor_item_list)
337
338  # Check that the merge config lists are not missing any item specified
339  # by the default config lists.
340  difference = default_combined_item_set.difference(combined_item_set)
341  if difference:
342    logger.error('Missing merge config items: %s', list(difference))
343    logger.error('Please ensure missing items are in either the '
344                 'framework-item-list or vendor-item-list files provided to '
345                 'this script.')
346    has_error = True
347
348  # Check that partitions only come from one input.
349  for partition in SINGLE_BUILD_PARTITIONS:
350    image_path = 'IMAGES/{}.img'.format(partition.lower().replace('/', ''))
351    in_framework = (
352        any(item.startswith(partition) for item in framework_item_list) or
353        image_path in framework_item_list)
354    in_vendor = (
355        any(item.startswith(partition) for item in vendor_item_list) or
356        image_path in vendor_item_list)
357    if in_framework and in_vendor:
358      logger.error(
359          'Cannot extract items from %s for both the framework and vendor'
360          ' builds. Please ensure only one merge config item list'
361          ' includes %s.', partition, partition)
362      has_error = True
363
364  if ('dynamic_partition_list'
365      in framework_misc_info_keys) or ('super_partition_groups'
366                                       in framework_misc_info_keys):
367    logger.error('Dynamic partition misc info keys should come from '
368                 'the vendor instance of META/misc_info.txt.')
369    has_error = True
370
371  return not has_error
372
373
374def process_ab_partitions_txt(framework_target_files_temp_dir,
375                              vendor_target_files_temp_dir,
376                              output_target_files_temp_dir):
377  """Performs special processing for META/ab_partitions.txt.
378
379  This function merges the contents of the META/ab_partitions.txt files from the
380  framework directory and the vendor directory, placing the merged result in the
381  output directory. The precondition in that the files are already extracted.
382  The post condition is that the output META/ab_partitions.txt contains the
383  merged content. The format for each ab_partitions.txt is one partition name
384  per line. The output file contains the union of the partition names.
385
386  Args:
387    framework_target_files_temp_dir: The name of a directory containing the
388      special items extracted from the framework target files package.
389    vendor_target_files_temp_dir: The name of a directory containing the special
390      items extracted from the vendor target files package.
391    output_target_files_temp_dir: The name of a directory that will be used to
392      create the output target files package after all the special cases are
393      processed.
394  """
395
396  framework_ab_partitions_txt = os.path.join(framework_target_files_temp_dir,
397                                             'META', 'ab_partitions.txt')
398
399  vendor_ab_partitions_txt = os.path.join(vendor_target_files_temp_dir, 'META',
400                                          'ab_partitions.txt')
401
402  with open(framework_ab_partitions_txt) as f:
403    framework_ab_partitions = f.read().splitlines()
404
405  with open(vendor_ab_partitions_txt) as f:
406    vendor_ab_partitions = f.read().splitlines()
407
408  output_ab_partitions = set(framework_ab_partitions + vendor_ab_partitions)
409
410  output_ab_partitions_txt = os.path.join(output_target_files_temp_dir, 'META',
411                                          'ab_partitions.txt')
412
413  write_sorted_data(data=output_ab_partitions, path=output_ab_partitions_txt)
414
415
416def process_misc_info_txt(framework_target_files_temp_dir,
417                          vendor_target_files_temp_dir,
418                          output_target_files_temp_dir,
419                          framework_misc_info_keys):
420  """Performs special processing for META/misc_info.txt.
421
422  This function merges the contents of the META/misc_info.txt files from the
423  framework directory and the vendor directory, placing the merged result in the
424  output directory. The precondition in that the files are already extracted.
425  The post condition is that the output META/misc_info.txt contains the merged
426  content.
427
428  Args:
429    framework_target_files_temp_dir: The name of a directory containing the
430      special items extracted from the framework target files package.
431    vendor_target_files_temp_dir: The name of a directory containing the special
432      items extracted from the vendor target files package.
433    output_target_files_temp_dir: The name of a directory that will be used to
434      create the output target files package after all the special cases are
435      processed.
436    framework_misc_info_keys: A list of keys to obtain from the framework
437      instance of META/misc_info.txt. The remaining keys should come from the
438      vendor instance.
439  """
440
441  misc_info_path = ['META', 'misc_info.txt']
442  framework_dict = common.LoadDictionaryFromFile(
443      os.path.join(framework_target_files_temp_dir, *misc_info_path))
444
445  # We take most of the misc info from the vendor target files.
446
447  merged_dict = common.LoadDictionaryFromFile(
448      os.path.join(vendor_target_files_temp_dir, *misc_info_path))
449
450  # Replace certain values in merged_dict with values from
451  # framework_dict.
452
453  for key in framework_misc_info_keys:
454    merged_dict[key] = framework_dict[key]
455
456  # Merge misc info keys used for Dynamic Partitions.
457  if (merged_dict.get('use_dynamic_partitions')
458      == 'true') and (framework_dict.get('use_dynamic_partitions') == 'true'):
459    merged_dynamic_partitions_dict = common.MergeDynamicPartitionInfoDicts(
460        framework_dict=framework_dict, vendor_dict=merged_dict)
461    merged_dict.update(merged_dynamic_partitions_dict)
462    # Ensure that add_img_to_target_files rebuilds super split images for
463    # devices that retrofit dynamic partitions. This flag may have been set to
464    # false in the partial builds to prevent duplicate building of super.img.
465    merged_dict['build_super_partition'] = 'true'
466
467  # If AVB is enabled then ensure that we build vbmeta.img.
468  # Partial builds with AVB enabled may set PRODUCT_BUILD_VBMETA_IMAGE=false to
469  # skip building an incomplete vbmeta.img.
470  if merged_dict.get('avb_enable') == 'true':
471    merged_dict['avb_building_vbmeta_image'] = 'true'
472
473  # Replace <image>_selinux_fc values with framework or vendor file_contexts.bin
474  # depending on which dictionary the key came from.
475  # Only the file basename is required because all selinux_fc properties are
476  # replaced with the full path to the file under META/ when misc_info.txt is
477  # loaded from target files for repacking. See common.py LoadInfoDict().
478  for key in merged_dict:
479    if key.endswith('_selinux_fc'):
480      merged_dict[key] = 'vendor_file_contexts.bin'
481  for key in framework_dict:
482    if key.endswith('_selinux_fc'):
483      merged_dict[key] = 'framework_file_contexts.bin'
484
485  output_misc_info_txt = os.path.join(output_target_files_temp_dir, 'META',
486                                      'misc_info.txt')
487  write_sorted_data(data=merged_dict, path=output_misc_info_txt)
488
489
490def process_dynamic_partitions_info_txt(framework_target_files_dir,
491                                        vendor_target_files_dir,
492                                        output_target_files_dir):
493  """Performs special processing for META/dynamic_partitions_info.txt.
494
495  This function merges the contents of the META/dynamic_partitions_info.txt
496  files from the framework directory and the vendor directory, placing the
497  merged result in the output directory.
498
499  This function does nothing if META/dynamic_partitions_info.txt from the vendor
500  directory does not exist.
501
502  Args:
503    framework_target_files_dir: The name of a directory containing the special
504      items extracted from the framework target files package.
505    vendor_target_files_dir: The name of a directory containing the special
506      items extracted from the vendor target files package.
507    output_target_files_dir: The name of a directory that will be used to create
508      the output target files package after all the special cases are processed.
509  """
510
511  if not os.path.exists(
512      os.path.join(vendor_target_files_dir, 'META',
513                   'dynamic_partitions_info.txt')):
514    return
515
516  dynamic_partitions_info_path = ['META', 'dynamic_partitions_info.txt']
517
518  framework_dynamic_partitions_dict = common.LoadDictionaryFromFile(
519      os.path.join(framework_target_files_dir, *dynamic_partitions_info_path))
520  vendor_dynamic_partitions_dict = common.LoadDictionaryFromFile(
521      os.path.join(vendor_target_files_dir, *dynamic_partitions_info_path))
522
523  merged_dynamic_partitions_dict = common.MergeDynamicPartitionInfoDicts(
524      framework_dict=framework_dynamic_partitions_dict,
525      vendor_dict=vendor_dynamic_partitions_dict)
526
527  output_dynamic_partitions_info_txt = os.path.join(
528      output_target_files_dir, 'META', 'dynamic_partitions_info.txt')
529  write_sorted_data(
530      data=merged_dynamic_partitions_dict,
531      path=output_dynamic_partitions_info_txt)
532
533
534def item_list_to_partition_set(item_list):
535  """Converts a target files item list to a partition set.
536
537  The item list contains items that might look like 'SYSTEM/*' or 'VENDOR/*' or
538  'OTA/android-info.txt'. Items that end in '/*' are assumed to match entire
539  directories where 'SYSTEM' or 'VENDOR' is a directory name that identifies the
540  contents of a partition of the same name. Other items in the list, such as the
541  'OTA' example contain metadata. This function iterates such a list, returning
542  a set that contains the partition entries.
543
544  Args:
545    item_list: A list of items in a target files package.
546
547  Returns:
548    A set of partitions extracted from the list of items.
549  """
550
551  partition_set = set()
552
553  for item in item_list:
554    match = PARTITION_ITEM_PATTERN.search(item.strip())
555    partition_tag = match.group(1).lower() if match else None
556
557    if partition_tag:
558      partition_set.add(partition_tag)
559
560  return partition_set
561
562
563def process_apex_keys_apk_certs_common(framework_target_files_dir,
564                                       vendor_target_files_dir,
565                                       output_target_files_dir,
566                                       framework_partition_set,
567                                       vendor_partition_set, file_name):
568  """Performs special processing for META/apexkeys.txt or META/apkcerts.txt.
569
570  This function merges the contents of the META/apexkeys.txt or
571  META/apkcerts.txt files from the framework directory and the vendor directory,
572  placing the merged result in the output directory. The precondition in that
573  the files are already extracted. The post condition is that the output
574  META/apexkeys.txt or META/apkcerts.txt contains the merged content.
575
576  Args:
577    framework_target_files_dir: The name of a directory containing the special
578      items extracted from the framework target files package.
579    vendor_target_files_dir: The name of a directory containing the special
580      items extracted from the vendor target files package.
581    output_target_files_dir: The name of a directory that will be used to create
582      the output target files package after all the special cases are processed.
583    framework_partition_set: Partitions that are considered framework
584      partitions. Used to filter apexkeys.txt and apkcerts.txt.
585    vendor_partition_set: Partitions that are considered vendor partitions. Used
586      to filter apexkeys.txt and apkcerts.txt.
587    file_name: The name of the file to merge. One of apkcerts.txt or
588      apexkeys.txt.
589  """
590
591  def read_helper(d):
592    temp = {}
593    file_path = os.path.join(d, 'META', file_name)
594    with open(file_path) as f:
595      for line in f:
596        if line.strip():
597          name = line.split()[0]
598          match = MODULE_KEY_PATTERN.search(name)
599          temp[match.group(1)] = line.strip()
600    return temp
601
602  framework_dict = read_helper(framework_target_files_dir)
603  vendor_dict = read_helper(vendor_target_files_dir)
604  merged_dict = {}
605
606  def filter_into_merged_dict(item_dict, partition_set):
607    for key, value in item_dict.items():
608      match = PARTITION_TAG_PATTERN.search(value)
609
610      if match is None:
611        raise ValueError('Entry missing partition tag: %s' % value)
612
613      partition_tag = match.group(1)
614
615      if partition_tag in partition_set:
616        if key in merged_dict:
617          if OPTIONS.allow_duplicate_apkapex_keys:
618            # TODO(b/150582573) Always raise on duplicates.
619            logger.warning('Duplicate key %s' % key)
620            continue
621          else:
622            raise ValueError('Duplicate key %s' % key)
623
624        merged_dict[key] = value
625
626  filter_into_merged_dict(framework_dict, framework_partition_set)
627  filter_into_merged_dict(vendor_dict, vendor_partition_set)
628
629  output_file = os.path.join(output_target_files_dir, 'META', file_name)
630
631  # The following code is similar to write_sorted_data, but different enough
632  # that we couldn't use that function. We need the output to be sorted by the
633  # basename of the apex/apk (without the ".apex" or ".apk" suffix). This
634  # allows the sort to be consistent with the framework/vendor input data and
635  # eases comparison of input data with merged data.
636  with open(output_file, 'w') as output:
637    for key in sorted(merged_dict.keys()):
638      out_str = merged_dict[key] + '\n'
639      output.write(out_str)
640
641
642def copy_file_contexts(framework_target_files_dir, vendor_target_files_dir,
643                       output_target_files_dir):
644  """Creates named copies of each build's file_contexts.bin in output META/."""
645  framework_fc_path = os.path.join(framework_target_files_dir, 'META',
646                                   'framework_file_contexts.bin')
647  if not os.path.exists(framework_fc_path):
648    framework_fc_path = os.path.join(framework_target_files_dir, 'META',
649                                     'file_contexts.bin')
650    if not os.path.exists(framework_fc_path):
651      raise ValueError('Missing framework file_contexts.bin.')
652  shutil.copyfile(
653      framework_fc_path,
654      os.path.join(output_target_files_dir, 'META',
655                   'framework_file_contexts.bin'))
656
657  vendor_fc_path = os.path.join(vendor_target_files_dir, 'META',
658                                'vendor_file_contexts.bin')
659  if not os.path.exists(vendor_fc_path):
660    vendor_fc_path = os.path.join(vendor_target_files_dir, 'META',
661                                  'file_contexts.bin')
662    if not os.path.exists(vendor_fc_path):
663      raise ValueError('Missing vendor file_contexts.bin.')
664  shutil.copyfile(
665      vendor_fc_path,
666      os.path.join(output_target_files_dir, 'META', 'vendor_file_contexts.bin'))
667
668
669def compile_split_sepolicy(product_out, partition_map, output_policy):
670  """Uses secilc to compile a split sepolicy file.
671
672  Depends on various */etc/selinux/* and */etc/vintf/* files within partitions.
673
674  Args:
675    product_out: PRODUCT_OUT directory, containing partition directories.
676    partition_map: A map of partition name -> relative path within product_out.
677    output_policy: The name of the output policy created by secilc.
678
679  Returns:
680    A command list that can be executed to create the compiled sepolicy.
681  """
682
683  def get_file(partition, path):
684    if partition not in partition_map:
685      logger.warning('Cannot load SEPolicy files for missing partition %s',
686                     partition)
687      return None
688    return os.path.join(product_out, partition_map[partition], path)
689
690  # Load the kernel sepolicy version from the FCM. This is normally provided
691  # directly to selinux.cpp as a build flag, but is also available in this file.
692  fcm_file = get_file('system', 'etc/vintf/compatibility_matrix.device.xml')
693  if not fcm_file or not os.path.exists(fcm_file):
694    raise ExternalError('Missing required file for loading sepolicy: %s', fcm)
695  kernel_sepolicy_version = ElementTree.parse(fcm_file).getroot().find(
696      'sepolicy/kernel-sepolicy-version').text
697
698  # Load the vendor's plat sepolicy version. This is the version used for
699  # locating sepolicy mapping files.
700  vendor_plat_version_file = get_file('vendor',
701                                      'etc/selinux/plat_sepolicy_vers.txt')
702  if not vendor_plat_version_file or not os.path.exists(
703      vendor_plat_version_file):
704    raise ExternalError('Missing required sepolicy file %s',
705                        vendor_plat_version_file)
706  with open(vendor_plat_version_file) as f:
707    vendor_plat_version = f.read().strip()
708
709  # Use the same flags and arguments as selinux.cpp OpenSplitPolicy().
710  cmd = ['secilc', '-m', '-M', 'true', '-G', '-N']
711  cmd.extend(['-c', kernel_sepolicy_version])
712  cmd.extend(['-o', output_policy])
713  cmd.extend(['-f', '/dev/null'])
714
715  required_policy_files = (
716      ('system', 'etc/selinux/plat_sepolicy.cil'),
717      ('system', 'etc/selinux/mapping/%s.cil' % vendor_plat_version),
718      ('vendor', 'etc/selinux/vendor_sepolicy.cil'),
719      ('vendor', 'etc/selinux/plat_pub_versioned.cil'),
720  )
721  for policy in (map(lambda partition_and_path: get_file(*partition_and_path),
722                     required_policy_files)):
723    if not policy or not os.path.exists(policy):
724      raise ExternalError('Missing required sepolicy file %s', policy)
725    cmd.append(policy)
726
727  optional_policy_files = (
728      ('system', 'etc/selinux/mapping/%s.compat.cil' % vendor_plat_version),
729      ('system_ext', 'etc/selinux/system_ext_sepolicy.cil'),
730      ('system_ext', 'etc/selinux/mapping/%s.cil' % vendor_plat_version),
731      ('product', 'etc/selinux/product_sepolicy.cil'),
732      ('product', 'etc/selinux/mapping/%s.cil' % vendor_plat_version),
733      ('odm', 'etc/selinux/odm_sepolicy.cil'),
734  )
735  for policy in (map(lambda partition_and_path: get_file(*partition_and_path),
736                     optional_policy_files)):
737    if policy and os.path.exists(policy):
738      cmd.append(policy)
739
740  return cmd
741
742
743def validate_merged_apex_info(output_target_files_dir, partitions):
744  """Validates the APEX files in the merged target files directory.
745
746  Checks the APEX files in all possible preinstalled APEX directories.
747  Depends on the <partition>/apex/* APEX files within partitions.
748
749  Args:
750    output_target_files_dir: Output directory containing merged partition directories.
751    partitions: A list of all the partitions in the output directory.
752
753  Raises:
754    RuntimeError: if apex_utils fails to parse any APEX file.
755    ExternalError: if the same APEX package is provided by multiple partitions.
756  """
757  apex_packages = set()
758
759  apex_partitions = ('system', 'system_ext', 'product', 'vendor')
760  for partition in filter(lambda p: p in apex_partitions, partitions):
761    apex_info = apex_utils.GetApexInfoFromTargetFiles(
762        output_target_files_dir, partition, compressed_only=False)
763    partition_apex_packages = set([info.package_name for info in apex_info])
764    duplicates = apex_packages.intersection(partition_apex_packages)
765    if duplicates:
766      raise ExternalError(
767          'Duplicate APEX packages found in multiple partitions: %s' %
768          ' '.join(duplicates))
769    apex_packages.update(partition_apex_packages)
770
771
772def generate_care_map(partitions, output_target_files_dir):
773  """Generates a merged META/care_map.pb file in the output target files dir.
774
775  Depends on the info dict from META/misc_info.txt, as well as built images
776  within IMAGES/.
777
778  Args:
779    partitions: A list of partitions to potentially include in the care map.
780    output_target_files_dir: The name of a directory that will be used to create
781      the output target files package after all the special cases are processed.
782  """
783  OPTIONS.info_dict = common.LoadInfoDict(output_target_files_dir)
784  partition_image_map = {}
785  for partition in partitions:
786    image_path = os.path.join(output_target_files_dir, 'IMAGES',
787                              '{}.img'.format(partition))
788    if os.path.exists(image_path):
789      partition_image_map[partition] = image_path
790      # Regenerated images should have their image_size property already set.
791      image_size_prop = '{}_image_size'.format(partition)
792      if image_size_prop not in OPTIONS.info_dict:
793        # Images copied directly from input target files packages will need
794        # their image sizes calculated.
795        partition_size = sparse_img.GetImagePartitionSize(image_path)
796        image_props = build_image.ImagePropFromGlobalDict(
797            OPTIONS.info_dict, partition)
798        verity_image_builder = verity_utils.CreateVerityImageBuilder(
799            image_props)
800        image_size = verity_image_builder.CalculateMaxImageSize(partition_size)
801        OPTIONS.info_dict[image_size_prop] = image_size
802
803  AddCareMapForAbOta(
804      os.path.join(output_target_files_dir, 'META', 'care_map.pb'),
805      PARTITIONS_WITH_CARE_MAP, partition_image_map)
806
807
808def process_special_cases(framework_target_files_temp_dir,
809                          vendor_target_files_temp_dir,
810                          output_target_files_temp_dir,
811                          framework_misc_info_keys, framework_partition_set,
812                          vendor_partition_set):
813  """Performs special-case processing for certain target files items.
814
815  Certain files in the output target files package require special-case
816  processing. This function performs all that special-case processing.
817
818  Args:
819    framework_target_files_temp_dir: The name of a directory containing the
820      special items extracted from the framework target files package.
821    vendor_target_files_temp_dir: The name of a directory containing the special
822      items extracted from the vendor target files package.
823    output_target_files_temp_dir: The name of a directory that will be used to
824      create the output target files package after all the special cases are
825      processed.
826    framework_misc_info_keys: A list of keys to obtain from the framework
827      instance of META/misc_info.txt. The remaining keys should come from the
828      vendor instance.
829    framework_partition_set: Partitions that are considered framework
830      partitions. Used to filter apexkeys.txt and apkcerts.txt.
831    vendor_partition_set: Partitions that are considered vendor partitions. Used
832      to filter apexkeys.txt and apkcerts.txt.
833  """
834
835  if 'ab_update' in framework_misc_info_keys:
836    process_ab_partitions_txt(
837        framework_target_files_temp_dir=framework_target_files_temp_dir,
838        vendor_target_files_temp_dir=vendor_target_files_temp_dir,
839        output_target_files_temp_dir=output_target_files_temp_dir)
840
841  copy_file_contexts(
842      framework_target_files_dir=framework_target_files_temp_dir,
843      vendor_target_files_dir=vendor_target_files_temp_dir,
844      output_target_files_dir=output_target_files_temp_dir)
845
846  process_misc_info_txt(
847      framework_target_files_temp_dir=framework_target_files_temp_dir,
848      vendor_target_files_temp_dir=vendor_target_files_temp_dir,
849      output_target_files_temp_dir=output_target_files_temp_dir,
850      framework_misc_info_keys=framework_misc_info_keys)
851
852  process_dynamic_partitions_info_txt(
853      framework_target_files_dir=framework_target_files_temp_dir,
854      vendor_target_files_dir=vendor_target_files_temp_dir,
855      output_target_files_dir=output_target_files_temp_dir)
856
857  process_apex_keys_apk_certs_common(
858      framework_target_files_dir=framework_target_files_temp_dir,
859      vendor_target_files_dir=vendor_target_files_temp_dir,
860      output_target_files_dir=output_target_files_temp_dir,
861      framework_partition_set=framework_partition_set,
862      vendor_partition_set=vendor_partition_set,
863      file_name='apkcerts.txt')
864
865  process_apex_keys_apk_certs_common(
866      framework_target_files_dir=framework_target_files_temp_dir,
867      vendor_target_files_dir=vendor_target_files_temp_dir,
868      output_target_files_dir=output_target_files_temp_dir,
869      framework_partition_set=framework_partition_set,
870      vendor_partition_set=vendor_partition_set,
871      file_name='apexkeys.txt')
872
873
874def create_merged_package(temp_dir, framework_target_files, framework_item_list,
875                          vendor_target_files, vendor_item_list,
876                          framework_misc_info_keys, rebuild_recovery):
877  """Merges two target files packages into one target files structure.
878
879  Args:
880    temp_dir: The name of a directory we use when we extract items from the
881      input target files packages, and also a scratch directory that we use for
882      temporary files.
883    framework_target_files: The name of the zip archive containing the framework
884      partial target files package.
885    framework_item_list: The list of items to extract from the partial framework
886      target files package as is, meaning these items will land in the output
887      target files package exactly as they appear in the input partial framework
888      target files package.
889    vendor_target_files: The name of the zip archive containing the vendor
890      partial target files package.
891    vendor_item_list: The list of items to extract from the partial vendor
892      target files package as is, meaning these items will land in the output
893      target files package exactly as they appear in the input partial vendor
894      target files package.
895    framework_misc_info_keys: A list of keys to obtain from the framework
896      instance of META/misc_info.txt. The remaining keys should come from the
897      vendor instance.
898    rebuild_recovery: If true, rebuild the recovery patch used by non-A/B
899      devices and write it to the system image.
900
901  Returns:
902    Path to merged package under temp directory.
903  """
904  # Extract "as is" items from the input framework and vendor partial target
905  # files packages directly into the output temporary directory, since these items
906  # do not need special case processing.
907
908  output_target_files_temp_dir = os.path.join(temp_dir, 'output')
909  extract_items(
910      target_files=framework_target_files,
911      target_files_temp_dir=output_target_files_temp_dir,
912      extract_item_list=framework_item_list)
913  extract_items(
914      target_files=vendor_target_files,
915      target_files_temp_dir=output_target_files_temp_dir,
916      extract_item_list=vendor_item_list)
917
918  # Perform special case processing on META/* items.
919  # After this function completes successfully, all the files we need to create
920  # the output target files package are in place.
921  framework_target_files_temp_dir = os.path.join(temp_dir, 'framework')
922  vendor_target_files_temp_dir = os.path.join(temp_dir, 'vendor')
923  extract_items(
924      target_files=framework_target_files,
925      target_files_temp_dir=framework_target_files_temp_dir,
926      extract_item_list=('META/*',))
927  extract_items(
928      target_files=vendor_target_files,
929      target_files_temp_dir=vendor_target_files_temp_dir,
930      extract_item_list=('META/*',))
931  process_special_cases(
932      framework_target_files_temp_dir=framework_target_files_temp_dir,
933      vendor_target_files_temp_dir=vendor_target_files_temp_dir,
934      output_target_files_temp_dir=output_target_files_temp_dir,
935      framework_misc_info_keys=framework_misc_info_keys,
936      framework_partition_set=item_list_to_partition_set(framework_item_list),
937      vendor_partition_set=item_list_to_partition_set(vendor_item_list))
938
939  return output_target_files_temp_dir
940
941
942def generate_images(target_files_dir, rebuild_recovery):
943  """Generate images from target files.
944
945  This function takes merged output temporary directory and create images
946  from it.
947
948  Args:
949    target_files_dir: Path to merged temp directory.
950    rebuild_recovery: If true, rebuild the recovery patch used by non-A/B
951      devices and write it to the system image.
952  """
953
954  # Regenerate IMAGES in the target directory.
955
956  add_img_args = [
957      '--verbose',
958      '--add_missing',
959  ]
960  # TODO(b/132730255): Remove this if statement.
961  if rebuild_recovery:
962    add_img_args.append('--rebuild_recovery')
963  add_img_args.append(target_files_dir)
964
965  add_img_to_target_files.main(add_img_args)
966
967
968def generate_super_empty_image(target_dir, output_super_empty):
969  """Generates super_empty image from target package.
970
971  Args:
972    target_dir: Path to the target file package which contains misc_info.txt for
973      detailed information for super image.
974    output_super_empty: If provided, copies a super_empty.img file from the
975      target files package to this path.
976  """
977  # Create super_empty.img using the merged misc_info.txt.
978
979  misc_info_txt = os.path.join(target_dir, 'META', 'misc_info.txt')
980
981  use_dynamic_partitions = common.LoadDictionaryFromFile(misc_info_txt).get(
982      'use_dynamic_partitions')
983
984  if use_dynamic_partitions != 'true' and output_super_empty:
985    raise ValueError(
986        'Building super_empty.img requires use_dynamic_partitions=true.')
987  elif use_dynamic_partitions == 'true':
988    super_empty_img = os.path.join(target_dir, 'IMAGES', 'super_empty.img')
989    build_super_image_args = [
990        misc_info_txt,
991        super_empty_img,
992    ]
993    build_super_image.main(build_super_image_args)
994
995    # Copy super_empty.img to the user-provided output_super_empty location.
996    if output_super_empty:
997      shutil.copyfile(super_empty_img, output_super_empty)
998
999
1000def create_target_files_archive(output_file, source_dir, temp_dir):
1001  """Creates archive from target package.
1002
1003  Args:
1004    output_file: The name of the zip archive target files package.
1005    source_dir: The target directory contains package to be archived.
1006    temp_dir: Path to temporary directory for any intermediate files.
1007  """
1008  output_target_files_list = os.path.join(temp_dir, 'output.list')
1009  output_zip = os.path.abspath(output_file)
1010  output_target_files_meta_dir = os.path.join(source_dir, 'META')
1011
1012  def files_from_path(target_path, extra_args=None):
1013    """Gets files under the given path and return a sorted list."""
1014    find_command = ['find', target_path] + (extra_args or [])
1015    find_process = common.Run(
1016        find_command, stdout=subprocess.PIPE, verbose=False)
1017    return common.RunAndCheckOutput(['sort'],
1018                                    stdin=find_process.stdout,
1019                                    verbose=False)
1020
1021  meta_content = files_from_path(output_target_files_meta_dir)
1022  other_content = files_from_path(
1023      source_dir,
1024      ['-path', output_target_files_meta_dir, '-prune', '-o', '-print'])
1025
1026  with open(output_target_files_list, 'w') as f:
1027    f.write(meta_content)
1028    f.write(other_content)
1029
1030  command = [
1031      'soong_zip',
1032      '-d',
1033      '-o',
1034      output_zip,
1035      '-C',
1036      source_dir,
1037      '-r',
1038      output_target_files_list,
1039  ]
1040
1041  logger.info('creating %s', output_file)
1042  common.RunAndCheckOutput(command, verbose=True)
1043  logger.info('finished creating %s', output_file)
1044
1045  return output_zip
1046
1047
1048def merge_target_files(temp_dir, framework_target_files, framework_item_list,
1049                       framework_misc_info_keys, vendor_target_files,
1050                       vendor_item_list, output_target_files, output_dir,
1051                       output_item_list, output_ota, output_img,
1052                       output_super_empty, rebuild_recovery):
1053  """Merges two target files packages together.
1054
1055  This function takes framework and vendor target files packages as input,
1056  performs various file extractions, special case processing, and finally
1057  creates a merged zip archive as output.
1058
1059  Args:
1060    temp_dir: The name of a directory we use when we extract items from the
1061      input target files packages, and also a scratch directory that we use for
1062      temporary files.
1063    framework_target_files: The name of the zip archive containing the framework
1064      partial target files package.
1065    framework_item_list: The list of items to extract from the partial framework
1066      target files package as is, meaning these items will land in the output
1067      target files package exactly as they appear in the input partial framework
1068      target files package.
1069    framework_misc_info_keys: A list of keys to obtain from the framework
1070      instance of META/misc_info.txt. The remaining keys should come from the
1071      vendor instance.
1072    vendor_target_files: The name of the zip archive containing the vendor
1073      partial target files package.
1074    vendor_item_list: The list of items to extract from the partial vendor
1075      target files package as is, meaning these items will land in the output
1076      target files package exactly as they appear in the input partial vendor
1077      target files package.
1078    output_target_files: The name of the output zip archive target files package
1079      created by merging framework and vendor.
1080    output_dir: The destination directory for saving merged files.
1081    output_item_list: The list of items to copy into the output_dir.
1082    output_ota: The name of the output zip archive ota package.
1083    output_img: The name of the output zip archive img package.
1084    output_super_empty: If provided, creates a super_empty.img file from the
1085      merged target files package and saves it at this path.
1086    rebuild_recovery: If true, rebuild the recovery patch used by non-A/B
1087      devices and write it to the system image.
1088  """
1089
1090  logger.info('starting: merge framework %s and vendor %s into output %s',
1091              framework_target_files, vendor_target_files, output_target_files)
1092
1093  output_target_files_temp_dir = create_merged_package(
1094      temp_dir, framework_target_files, framework_item_list,
1095      vendor_target_files, vendor_item_list, framework_misc_info_keys,
1096      rebuild_recovery)
1097
1098  if not check_target_files_vintf.CheckVintf(output_target_files_temp_dir):
1099    raise RuntimeError('Incompatible VINTF metadata')
1100
1101  partition_map = common.PartitionMapFromTargetFiles(
1102      output_target_files_temp_dir)
1103
1104  # Generate and check for cross-partition violations of sharedUserId
1105  # values in APKs. This requires the input target-files packages to contain
1106  # *.apk files.
1107  shareduid_violation_modules = os.path.join(
1108      output_target_files_temp_dir, 'META', 'shareduid_violation_modules.json')
1109  with open(shareduid_violation_modules, 'w') as f:
1110    violation = find_shareduid_violation.FindShareduidViolation(
1111        output_target_files_temp_dir, partition_map)
1112
1113    # Write the output to a file to enable debugging.
1114    f.write(violation)
1115
1116    # Check for violations across the input builds' partition groups.
1117    framework_partitions = item_list_to_partition_set(framework_item_list)
1118    vendor_partitions = item_list_to_partition_set(vendor_item_list)
1119    shareduid_errors = common.SharedUidPartitionViolations(
1120        json.loads(violation), [framework_partitions, vendor_partitions])
1121    if shareduid_errors:
1122      for error in shareduid_errors:
1123        logger.error(error)
1124      raise ValueError('sharedUserId APK error. See %s' %
1125                       shareduid_violation_modules)
1126
1127  # host_init_verifier and secilc check only the following partitions:
1128  filtered_partitions = {
1129      partition: path
1130      for partition, path in partition_map.items()
1131      if partition in ['system', 'system_ext', 'product', 'vendor', 'odm']
1132  }
1133
1134  # Run host_init_verifier on the combined init rc files.
1135  common.RunHostInitVerifier(
1136      product_out=output_target_files_temp_dir,
1137      partition_map=filtered_partitions)
1138
1139  # Check that the split sepolicy from the multiple builds can compile.
1140  split_sepolicy_cmd = compile_split_sepolicy(
1141      product_out=output_target_files_temp_dir,
1142      partition_map=filtered_partitions,
1143      output_policy=os.path.join(output_target_files_temp_dir,
1144                                 'META/combined.policy'))
1145  logger.info('Compiling split sepolicy: %s', ' '.join(split_sepolicy_cmd))
1146  common.RunAndCheckOutput(split_sepolicy_cmd)
1147  # TODO(b/178864050): Run tests on the combined.policy file.
1148
1149  # Run validation checks on the pre-installed APEX files.
1150  validate_merged_apex_info(output_target_files_temp_dir, partition_map.keys())
1151
1152  generate_images(output_target_files_temp_dir, rebuild_recovery)
1153
1154  generate_super_empty_image(output_target_files_temp_dir, output_super_empty)
1155
1156  # Finally, create the output target files zip archive and/or copy the
1157  # output items to the output target files directory.
1158
1159  if output_dir:
1160    copy_items(output_target_files_temp_dir, output_dir, output_item_list)
1161
1162  if not output_target_files:
1163    return
1164
1165  # Create the merged META/care_map.bp
1166  generate_care_map(partition_map.keys(), output_target_files_temp_dir)
1167
1168  output_zip = create_target_files_archive(output_target_files,
1169                                           output_target_files_temp_dir,
1170                                           temp_dir)
1171
1172  # Create the IMG package from the merged target files package.
1173  if output_img:
1174    img_from_target_files.main([output_zip, output_img])
1175
1176  # Create the OTA package from the merged target files package.
1177
1178  if output_ota:
1179    ota_from_target_files.main([output_zip, output_ota])
1180
1181
1182def call_func_with_temp_dir(func, keep_tmp):
1183  """Manages the creation and cleanup of the temporary directory.
1184
1185  This function calls the given function after first creating a temporary
1186  directory. It also cleans up the temporary directory.
1187
1188  Args:
1189    func: The function to call. Should accept one parameter, the path to the
1190      temporary directory.
1191    keep_tmp: Keep the temporary directory after processing is complete.
1192  """
1193
1194  # Create a temporary directory. This will serve as the parent of directories
1195  # we use when we extract items from the input target files packages, and also
1196  # a scratch directory that we use for temporary files.
1197
1198  temp_dir = common.MakeTempDir(prefix='merge_target_files_')
1199
1200  try:
1201    func(temp_dir)
1202  finally:
1203    if keep_tmp:
1204      logger.info('keeping %s', temp_dir)
1205    else:
1206      common.Cleanup()
1207
1208
1209def main():
1210  """The main function.
1211
1212  Process command line arguments, then call merge_target_files to
1213  perform the heavy lifting.
1214  """
1215
1216  common.InitLogging()
1217
1218  def option_handler(o, a):
1219    if o == '--system-target-files':
1220      logger.warning(
1221          '--system-target-files has been renamed to --framework-target-files')
1222      OPTIONS.framework_target_files = a
1223    elif o == '--framework-target-files':
1224      OPTIONS.framework_target_files = a
1225    elif o == '--system-item-list':
1226      logger.warning(
1227          '--system-item-list has been renamed to --framework-item-list')
1228      OPTIONS.framework_item_list = a
1229    elif o == '--framework-item-list':
1230      OPTIONS.framework_item_list = a
1231    elif o == '--system-misc-info-keys':
1232      logger.warning('--system-misc-info-keys has been renamed to '
1233                     '--framework-misc-info-keys')
1234      OPTIONS.framework_misc_info_keys = a
1235    elif o == '--framework-misc-info-keys':
1236      OPTIONS.framework_misc_info_keys = a
1237    elif o == '--other-target-files':
1238      logger.warning(
1239          '--other-target-files has been renamed to --vendor-target-files')
1240      OPTIONS.vendor_target_files = a
1241    elif o == '--vendor-target-files':
1242      OPTIONS.vendor_target_files = a
1243    elif o == '--other-item-list':
1244      logger.warning('--other-item-list has been renamed to --vendor-item-list')
1245      OPTIONS.vendor_item_list = a
1246    elif o == '--vendor-item-list':
1247      OPTIONS.vendor_item_list = a
1248    elif o == '--output-target-files':
1249      OPTIONS.output_target_files = a
1250    elif o == '--output-dir':
1251      OPTIONS.output_dir = a
1252    elif o == '--output-item-list':
1253      OPTIONS.output_item_list = a
1254    elif o == '--output-ota':
1255      OPTIONS.output_ota = a
1256    elif o == '--output-img':
1257      OPTIONS.output_img = a
1258    elif o == '--output-super-empty':
1259      OPTIONS.output_super_empty = a
1260    elif o == '--rebuild_recovery':  # TODO(b/132730255): Warn
1261      OPTIONS.rebuild_recovery = True
1262    elif o == '--allow-duplicate-apkapex-keys':
1263      OPTIONS.allow_duplicate_apkapex_keys = True
1264    elif o == '--keep-tmp':
1265      OPTIONS.keep_tmp = True
1266    else:
1267      return False
1268    return True
1269
1270  args = common.ParseOptions(
1271      sys.argv[1:],
1272      __doc__,
1273      extra_long_opts=[
1274          'system-target-files=',
1275          'framework-target-files=',
1276          'system-item-list=',
1277          'framework-item-list=',
1278          'system-misc-info-keys=',
1279          'framework-misc-info-keys=',
1280          'other-target-files=',
1281          'vendor-target-files=',
1282          'other-item-list=',
1283          'vendor-item-list=',
1284          'output-target-files=',
1285          'output-dir=',
1286          'output-item-list=',
1287          'output-ota=',
1288          'output-img=',
1289          'output-super-empty=',
1290          'rebuild_recovery',
1291          'allow-duplicate-apkapex-keys',
1292          'keep-tmp',
1293      ],
1294      extra_option_handler=option_handler)
1295
1296  # pylint: disable=too-many-boolean-expressions
1297  if (args or OPTIONS.framework_target_files is None or
1298      OPTIONS.vendor_target_files is None or
1299      (OPTIONS.output_target_files is None and OPTIONS.output_dir is None) or
1300      (OPTIONS.output_dir is not None and OPTIONS.output_item_list is None)):
1301    common.Usage(__doc__)
1302    sys.exit(1)
1303
1304  if OPTIONS.framework_item_list:
1305    framework_item_list = common.LoadListFromFile(OPTIONS.framework_item_list)
1306  else:
1307    framework_item_list = DEFAULT_FRAMEWORK_ITEM_LIST
1308
1309  if OPTIONS.framework_misc_info_keys:
1310    framework_misc_info_keys = common.LoadListFromFile(
1311        OPTIONS.framework_misc_info_keys)
1312  else:
1313    framework_misc_info_keys = DEFAULT_FRAMEWORK_MISC_INFO_KEYS
1314
1315  if OPTIONS.vendor_item_list:
1316    vendor_item_list = common.LoadListFromFile(OPTIONS.vendor_item_list)
1317  else:
1318    vendor_item_list = DEFAULT_VENDOR_ITEM_LIST
1319
1320  if OPTIONS.output_item_list:
1321    output_item_list = common.LoadListFromFile(OPTIONS.output_item_list)
1322  else:
1323    output_item_list = None
1324
1325  if not validate_config_lists(
1326      framework_item_list=framework_item_list,
1327      framework_misc_info_keys=framework_misc_info_keys,
1328      vendor_item_list=vendor_item_list):
1329    sys.exit(1)
1330
1331  call_func_with_temp_dir(
1332      lambda temp_dir: merge_target_files(
1333          temp_dir=temp_dir,
1334          framework_target_files=OPTIONS.framework_target_files,
1335          framework_item_list=framework_item_list,
1336          framework_misc_info_keys=framework_misc_info_keys,
1337          vendor_target_files=OPTIONS.vendor_target_files,
1338          vendor_item_list=vendor_item_list,
1339          output_target_files=OPTIONS.output_target_files,
1340          output_dir=OPTIONS.output_dir,
1341          output_item_list=output_item_list,
1342          output_ota=OPTIONS.output_ota,
1343          output_img=OPTIONS.output_img,
1344          output_super_empty=OPTIONS.output_super_empty,
1345          rebuild_recovery=OPTIONS.rebuild_recovery), OPTIONS.keep_tmp)
1346
1347
1348if __name__ == '__main__':
1349  main()
1350