• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2#
3# Copyright (C) 2008 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#      http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""
18Given an input target-files, produces an image zipfile suitable for use with
19'fastboot update'.
20
21Usage:  img_from_target_files [flags] input_target_files output_image_zip
22
23input_target_files: Path to the input target_files zip.
24
25Flags:
26  -z  (--bootable_zip)
27      Include only the bootable images (eg 'boot' and 'recovery') in
28      the output.
29
30  --additional <filespec>
31      Include an additional entry into the generated zip file. The filespec is
32      in a format that's accepted by zip2zip (e.g.
33      'OTA/android-info.txt:android-info.txt', to copy `OTA/android-info.txt`
34      from input_file into output_file as `android-info.txt`. Refer to the
35      `filespec` arg in zip2zip's help message). The option can be repeated to
36      include multiple entries.
37
38  --exclude <filespec>
39      Don't include these files. If the file is in --additional and --exclude,
40      the file will not be included.
41
42"""
43
44from __future__ import print_function
45
46import logging
47import os
48import sys
49import zipfile
50
51import common
52from build_super_image import BuildSuperImage
53
54if sys.hexversion < 0x02070000:
55  print('Python 2.7 or newer is required.', file=sys.stderr)
56  sys.exit(1)
57
58logger = logging.getLogger(__name__)
59
60OPTIONS = common.OPTIONS
61
62OPTIONS.additional_entries = []
63OPTIONS.excluded_entries = []
64OPTIONS.bootable_only = False
65OPTIONS.put_super = None
66OPTIONS.put_bootloader = None
67OPTIONS.dynamic_partition_list = None
68OPTIONS.super_device_list = None
69OPTIONS.retrofit_dap = None
70OPTIONS.build_super = None
71OPTIONS.sparse_userimages = None
72OPTIONS.use_fastboot_info = True
73OPTIONS.build_super_image = None
74
75
76def LoadOptions(input_file):
77  """Loads information from input_file to OPTIONS.
78
79  Args:
80    input_file: Path to the input target_files zip file.
81  """
82  with zipfile.ZipFile(input_file) as input_zip:
83    info = OPTIONS.info_dict = common.LoadInfoDict(input_zip)
84
85  OPTIONS.put_super = info.get('super_image_in_update_package') == 'true'
86  OPTIONS.put_bootloader = info.get('bootloader_in_update_package') == 'true'
87  OPTIONS.dynamic_partition_list = info.get('dynamic_partition_list',
88                                            '').strip().split()
89  OPTIONS.super_device_list = info.get('super_block_devices',
90                                       '').strip().split()
91  OPTIONS.retrofit_dap = info.get('dynamic_partition_retrofit') == 'true'
92  OPTIONS.build_super = info.get('build_super_partition') == 'true'
93  OPTIONS.sparse_userimages = bool(info.get('extfs_sparse_flag'))
94
95
96def CopyZipEntries(input_file, output_file, entries):
97  """Copies ZIP entries between input and output files.
98
99  Args:
100    input_file: Path to the input target_files zip.
101    output_file: Output filename.
102    entries: A list of entries to copy, in a format that's accepted by zip2zip
103        (e.g. 'OTA/android-info.txt:android-info.txt', which copies
104        `OTA/android-info.txt` from input_file into output_file as
105        `android-info.txt`. Refer to the `filespec` arg in zip2zip's help
106        message).
107  """
108  logger.info('Writing %d entries to archive...', len(entries))
109  cmd = ['zip2zip', '-i', input_file, '-o', output_file]
110  cmd.extend(entries)
111  common.RunAndCheckOutput(cmd)
112
113
114def LocatePartitionEntry(partition_name, namelist):
115  for subdir in ["IMAGES", "PREBUILT_IMAGES", "RADIO"]:
116    entry_name = os.path.join(subdir, partition_name + ".img")
117    if entry_name in namelist:
118      return entry_name
119
120
121def EntriesForUserImages(input_file):
122  """Returns the user images entries to be copied.
123
124  Args:
125    input_file: Path to the input target_files zip file.
126  """
127  dynamic_images = [p + '.img' for p in OPTIONS.dynamic_partition_list]
128
129  # Filter out system_other for launch DAP devices because it is in super image.
130  if not OPTIONS.retrofit_dap and 'system' in OPTIONS.dynamic_partition_list:
131    dynamic_images.append('system_other.img')
132
133  entries = [
134      'OTA/android-info.txt:android-info.txt',
135  ]
136  if OPTIONS.use_fastboot_info:
137    entries.append('META/fastboot-info.txt:fastboot-info.txt')
138  ab_partitions = []
139  with zipfile.ZipFile(input_file) as input_zip:
140    namelist = input_zip.namelist()
141    if "META/ab_partitions.txt" in namelist:
142      ab_partitions = input_zip.read(
143          "META/ab_partitions.txt").decode().strip().split()
144  if 'PREBUILT_IMAGES/kernel_16k' in namelist:
145    entries.append('PREBUILT_IMAGES/kernel_16k:kernel_16k')
146  if 'PREBUILT_IMAGES/ramdisk_16k.img' in namelist:
147    entries.append('PREBUILT_IMAGES/ramdisk_16k.img:ramdisk_16k.img')
148
149  visited_partitions = set(OPTIONS.dynamic_partition_list)
150  for image_path in [name for name in namelist if name.startswith('IMAGES/')]:
151    image = os.path.basename(image_path)
152    if OPTIONS.bootable_only and image not in ('boot.img', 'recovery.img', 'bootloader', 'init_boot.img'):
153      continue
154    if not image.endswith('.img') and image != 'bootloader':
155      continue
156    if image == 'bootloader' and not OPTIONS.put_bootloader:
157      continue
158    # Filter out super_empty and the images that are already in super partition.
159    if OPTIONS.put_super:
160      if image == 'super_empty.img':
161        continue
162      if image in dynamic_images:
163        continue
164    partition_name = image.rstrip(".img")
165    visited_partitions.add(partition_name)
166    entries.append('{}:{}'.format(image_path, image))
167  for part in [part for part in ab_partitions if part not in visited_partitions]:
168    entry = LocatePartitionEntry(part, namelist)
169    image = os.path.basename(entry)
170    if entry is not None:
171      entries.append('{}:{}'.format(entry, image))
172  return entries
173
174
175def EntriesForSplitSuperImages(input_file):
176  """Returns the entries for split super images.
177
178  This is only done for retrofit dynamic partition devices.
179
180  Args:
181    input_file: Path to the input target_files zip file.
182  """
183  with zipfile.ZipFile(input_file) as input_zip:
184    namelist = input_zip.namelist()
185  entries = []
186  for device in OPTIONS.super_device_list:
187    image = 'OTA/super_{}.img'.format(device)
188    assert image in namelist, 'Failed to find {}'.format(image)
189    entries.append('{}:{}'.format(image, os.path.basename(image)))
190  return entries
191
192
193def RebuildAndWriteSuperImages(input_file, output_file):
194  """Builds and writes super images to the output file."""
195  logger.info('Building super image...')
196
197  # We need files under IMAGES/, OTA/, META/ for img_from_target_files.py.
198  # However, common.LoadInfoDict() may read additional files under BOOT/,
199  # RECOVERY/ and ROOT/. So unzip everything from the target_files.zip.
200  input_tmp = common.UnzipTemp(input_file)
201
202  super_file = common.MakeTempFile('super_', '.img')
203
204  # Allow overriding the BUILD_SUPER_IMAGE binary
205  if OPTIONS.build_super_image:
206    command = [OPTIONS.build_super_image, input_tmp, super_file]
207    common.RunAndCheckOutput(command)
208  else:
209    BuildSuperImage(input_tmp, super_file)
210
211  logger.info('Writing super.img to archive...')
212  with zipfile.ZipFile(
213          output_file, 'a', compression=zipfile.ZIP_DEFLATED,
214          allowZip64=True) as output_zip:
215    common.ZipWrite(output_zip, super_file, 'super.img')
216
217
218def ImgFromTargetFiles(input_file, output_file):
219  """Creates an image archive from the input target_files zip.
220
221  Args:
222    input_file: Path to the input target_files zip.
223    output_file: Output filename.
224
225  Raises:
226    ValueError: On invalid input.
227  """
228  if not os.path.exists(input_file):
229    raise ValueError('%s is not exist' % input_file)
230
231  if not zipfile.is_zipfile(input_file):
232    raise ValueError('%s is not a valid zipfile' % input_file)
233
234  logger.info('Building image zip from target files zip.')
235
236  LoadOptions(input_file)
237
238  # Entries to be copied into the output file.
239  entries = EntriesForUserImages(input_file)
240
241  # Only for devices that retrofit dynamic partitions there're split super
242  # images available in the target_files.zip.
243  rebuild_super = False
244  if OPTIONS.build_super and OPTIONS.put_super:
245    if OPTIONS.retrofit_dap:
246      entries += EntriesForSplitSuperImages(input_file)
247    else:
248      rebuild_super = True
249
250  # Any additional entries provided by caller.
251  entries += OPTIONS.additional_entries
252
253  # Remove any excluded entries
254  entries = [e for e in entries if e not in OPTIONS.excluded_entries]
255
256  CopyZipEntries(input_file, output_file, entries)
257
258  if rebuild_super:
259    RebuildAndWriteSuperImages(input_file, output_file)
260
261
262def main(argv):
263
264  def option_handler(o, a):
265    if o in ('-z', '--bootable_zip'):
266      OPTIONS.bootable_only = True
267    elif o == '--additional':
268      OPTIONS.additional_entries.append(a)
269    elif o == '--exclude':
270      OPTIONS.excluded_entries.append(a)
271    elif o == '--build_super_image':
272      OPTIONS.build_super_image = a
273    else:
274      return False
275    return True
276
277  args = common.ParseOptions(argv, __doc__,
278                             extra_opts='z',
279                             extra_long_opts=[
280                                 'additional=',
281                                 'exclude=',
282                                 'bootable_zip',
283                                 'build_super_image=',
284                             ],
285                             extra_option_handler=option_handler)
286  if len(args) != 2:
287    common.Usage(__doc__)
288    sys.exit(1)
289
290  common.InitLogging()
291
292  ImgFromTargetFiles(args[0], args[1])
293
294  logger.info('done.')
295
296
297if __name__ == '__main__':
298  try:
299    common.CloseInheritedPipes()
300    main(sys.argv[1:])
301  finally:
302    common.Cleanup()
303