• 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 a target-files zipfile, produces an image zipfile suitable for
19use with 'fastboot update'.
20
21Usage:  img_from_target_files [flags] input_target_files output_image_zip
22
23  -z  (--bootable_zip)
24      Include only the bootable images (eg 'boot' and 'recovery') in
25      the output.
26
27"""
28
29from __future__ import print_function
30
31import logging
32import os
33import shutil
34import sys
35import zipfile
36
37import common
38from build_super_image import BuildSuperImage
39
40if sys.hexversion < 0x02070000:
41  print("Python 2.7 or newer is required.", file=sys.stderr)
42  sys.exit(1)
43
44logger = logging.getLogger(__name__)
45
46OPTIONS = common.OPTIONS
47
48
49def LoadOptions(input_file):
50  """
51  Load information from input_file to OPTIONS.
52
53  Args:
54    input_file: A Zipfile instance of input zip file, or path to the directory
55      of extracted zip.
56  """
57  info = OPTIONS.info_dict = common.LoadInfoDict(input_file)
58
59  OPTIONS.put_super = info.get("super_image_in_update_package") == "true"
60  OPTIONS.dynamic_partition_list = info.get("dynamic_partition_list",
61                                            "").strip().split()
62  OPTIONS.super_device_list = info.get("super_block_devices",
63                                       "").strip().split()
64  OPTIONS.retrofit_dap = info.get("dynamic_partition_retrofit") == "true"
65  OPTIONS.build_super = info.get("build_super_partition") == "true"
66  OPTIONS.sparse_userimages = bool(info.get("extfs_sparse_flag"))
67
68
69def CopyInfo(input_tmp, output_zip):
70  """Copy the android-info.txt file from the input to the output."""
71  common.ZipWrite(
72      output_zip, os.path.join(input_tmp, "OTA", "android-info.txt"),
73      "android-info.txt")
74
75
76def CopyUserImages(input_tmp, output_zip):
77  """
78  Copy user images from the unzipped input and write to output_zip.
79
80  Args:
81    input_tmp: path to the unzipped input.
82    output_zip: a ZipFile instance to write images to.
83  """
84  dynamic_images = [p + ".img" for p in OPTIONS.dynamic_partition_list]
85
86  # Filter out system_other for launch DAP devices because it is in super image.
87  if not OPTIONS.retrofit_dap and "system" in OPTIONS.dynamic_partition_list:
88    dynamic_images.append("system_other.img")
89
90  images_path = os.path.join(input_tmp, "IMAGES")
91  # A target-files zip must contain the images since Lollipop.
92  assert os.path.exists(images_path)
93  for image in sorted(os.listdir(images_path)):
94    if OPTIONS.bootable_only and image not in ("boot.img", "recovery.img"):
95      continue
96    if not image.endswith(".img"):
97      continue
98    if image == "recovery-two-step.img":
99      continue
100    if OPTIONS.put_super:
101      if image == "super_empty.img":
102        continue
103      if image in dynamic_images:
104        continue
105    logger.info("writing %s to archive...", os.path.join("IMAGES", image))
106    common.ZipWrite(output_zip, os.path.join(images_path, image), image)
107
108
109def WriteSuperImages(input_tmp, output_zip):
110  """
111  Write super images from the unzipped input and write to output_zip. This is
112  only done if super_image_in_update_package is set to "true".
113
114  - For retrofit dynamic partition devices, copy split super images from target
115    files package.
116  - For devices launched with dynamic partitions, build super image from target
117    files package.
118
119  Args:
120    input_tmp: path to the unzipped input.
121    output_zip: a ZipFile instance to write images to.
122  """
123  if not OPTIONS.build_super or not OPTIONS.put_super:
124    return
125
126  if OPTIONS.retrofit_dap:
127    # retrofit devices already have split super images under OTA/
128    images_path = os.path.join(input_tmp, "OTA")
129    for device in OPTIONS.super_device_list:
130      image = "super_%s.img" % device
131      image_path = os.path.join(images_path, image)
132      assert os.path.exists(image_path)
133      logger.info("writing %s to archive...", os.path.join("OTA", image))
134      common.ZipWrite(output_zip, image_path, image)
135  else:
136    # super image for non-retrofit devices aren't in target files package,
137    # so build it.
138    super_file = common.MakeTempFile("super_", ".img")
139    logger.info("building super image %s...", super_file)
140    BuildSuperImage(input_tmp, super_file)
141    logger.info("writing super.img to archive...")
142    common.ZipWrite(output_zip, super_file, "super.img")
143
144
145def main(argv):
146  # This allows modifying the value from inner function.
147  bootable_only_array = [False]
148
149  def option_handler(o, _):
150    if o in ("-z", "--bootable_zip"):
151      bootable_only_array[0] = True
152    else:
153      return False
154    return True
155
156  args = common.ParseOptions(argv, __doc__,
157                             extra_opts="z",
158                             extra_long_opts=["bootable_zip"],
159                             extra_option_handler=option_handler)
160
161  OPTIONS.bootable_only = bootable_only_array[0]
162
163  if len(args) != 2:
164    common.Usage(__doc__)
165    sys.exit(1)
166
167  common.InitLogging()
168
169  # We need files under IMAGES/, OTA/, META/ for img_from_target_files.py.
170  # However, common.LoadInfoDict() may read additional files under BOOT/,
171  # RECOVERY/ and ROOT/. So unzip everything from the target_files.zip.
172  OPTIONS.input_tmp = common.UnzipTemp(args[0])
173  LoadOptions(OPTIONS.input_tmp)
174  output_zip = zipfile.ZipFile(args[1], "w", compression=zipfile.ZIP_DEFLATED,
175                               allowZip64=not OPTIONS.sparse_userimages)
176
177  try:
178    CopyInfo(OPTIONS.input_tmp, output_zip)
179    CopyUserImages(OPTIONS.input_tmp, output_zip)
180    WriteSuperImages(OPTIONS.input_tmp, output_zip)
181  finally:
182    logger.info("cleaning up...")
183    common.ZipClose(output_zip)
184    shutil.rmtree(OPTIONS.input_tmp)
185
186  logger.info("done.")
187
188
189if __name__ == '__main__':
190  try:
191    common.CloseInheritedPipes()
192    main(sys.argv[1:])
193  except common.ExternalError as e:
194    logger.exception("\n   ERROR:\n")
195    sys.exit(1)
196