add_img_to_target_files.py revision d42e97ebb45fdc5a30799a3f37e482948d318010
1709a0978ae141198018ca9769f8d96292a8928e6Jason Sams#!/usr/bin/env python
2709a0978ae141198018ca9769f8d96292a8928e6Jason Sams#
3709a0978ae141198018ca9769f8d96292a8928e6Jason Sams# Copyright (C) 2014 The Android Open Source Project
4709a0978ae141198018ca9769f8d96292a8928e6Jason Sams#
5709a0978ae141198018ca9769f8d96292a8928e6Jason Sams# Licensed under the Apache License, Version 2.0 (the "License");
6709a0978ae141198018ca9769f8d96292a8928e6Jason Sams# you may not use this file except in compliance with the License.
7709a0978ae141198018ca9769f8d96292a8928e6Jason Sams# You may obtain a copy of the License at
8709a0978ae141198018ca9769f8d96292a8928e6Jason Sams#
9709a0978ae141198018ca9769f8d96292a8928e6Jason Sams#      http://www.apache.org/licenses/LICENSE-2.0
10709a0978ae141198018ca9769f8d96292a8928e6Jason Sams#
11709a0978ae141198018ca9769f8d96292a8928e6Jason Sams# Unless required by applicable law or agreed to in writing, software
12709a0978ae141198018ca9769f8d96292a8928e6Jason Sams# distributed under the License is distributed on an "AS IS" BASIS,
13709a0978ae141198018ca9769f8d96292a8928e6Jason Sams# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14709a0978ae141198018ca9769f8d96292a8928e6Jason Sams# See the License for the specific language governing permissions and
15709a0978ae141198018ca9769f8d96292a8928e6Jason Sams# limitations under the License.
16709a0978ae141198018ca9769f8d96292a8928e6Jason Sams
176a236ad3a3760e8124b68a1b6220ed6e4fbfb152Stephen Hines"""
186a236ad3a3760e8124b68a1b6220ed6e4fbfb152Stephen HinesGiven a target-files zipfile that does not contain images (ie, does
19709a0978ae141198018ca9769f8d96292a8928e6Jason Samsnot have an IMAGES/ top-level subdirectory), produce the images and
20709a0978ae141198018ca9769f8d96292a8928e6Jason Samsadd them to the zipfile.
21709a0978ae141198018ca9769f8d96292a8928e6Jason Sams
22709a0978ae141198018ca9769f8d96292a8928e6Jason SamsUsage:  add_img_to_target_files [flag] target_files
2325e3af55a43faddced1a9931574dfdc3cc8ad8fdStephen Hines
2429809d1f95d4cd4cbc6b2f9384b3321759691e13Tim Murray  -a  (--add_missing)
2525e3af55a43faddced1a9931574dfdc3cc8ad8fdStephen Hines      Build and add missing images to "IMAGES/". If this option is
2625e3af55a43faddced1a9931574dfdc3cc8ad8fdStephen Hines      not specified, this script will simply exit when "IMAGES/"
27709a0978ae141198018ca9769f8d96292a8928e6Jason Sams      directory exists in the target file.
28709a0978ae141198018ca9769f8d96292a8928e6Jason Sams
2982e135c4bbe18855d8ed02632bb074f8da0b96e0Miao Wang  -r  (--rebuild_recovery)
3082e135c4bbe18855d8ed02632bb074f8da0b96e0Miao Wang      Rebuild the recovery patch and write it to the system image. Only
319ab5094dd32352b33e251e540934f6e814c5fa5bJean-Luc Brouillet      meaningful when system image needs to be rebuilt.
329ab5094dd32352b33e251e540934f6e814c5fa5bJean-Luc Brouillet
337974fc03e11f3a8dd40f794f3b33b4889483090cRahul Chaudhry  --replace_verity_private_key
349ab5094dd32352b33e251e540934f6e814c5fa5bJean-Luc Brouillet      Replace the private key used for verity signing. (same as the option
35709a0978ae141198018ca9769f8d96292a8928e6Jason Sams      in sign_target_files_apks)
36709a0978ae141198018ca9769f8d96292a8928e6Jason Sams
37709a0978ae141198018ca9769f8d96292a8928e6Jason Sams  --replace_verity_public_key
382abfcc6d129fe3defddef4540aa95cc445c03a7aYang Ni       Replace the certificate (public key) used for verity verification. (same
39709a0978ae141198018ca9769f8d96292a8928e6Jason Sams       as the option in sign_target_files_apks)
40709a0978ae141198018ca9769f8d96292a8928e6Jason Sams
41709a0978ae141198018ca9769f8d96292a8928e6Jason Sams  --is_signing
42709a0978ae141198018ca9769f8d96292a8928e6Jason Sams      Skip building & adding the images for "userdata" and "cache" if we
43005113297b19ed256b6db9d6bc293ed9266899fcStephen Hines      are signing the target files.
4444bef6fba6244292b751387f3d6c31cca96c28adChris Wailes"""
45c060f1435e7b9405f3be8974417fa6f410f03753Stephen Hines
46709a0978ae141198018ca9769f8d96292a8928e6Jason Samsimport sys
47c060f1435e7b9405f3be8974417fa6f410f03753Stephen Hines
48c060f1435e7b9405f3be8974417fa6f410f03753Stephen Hinesif sys.hexversion < 0x02070000:
49f37121300217d3b39ab66dd9c8881bcbcad932dfChris Wailes  print >> sys.stderr, "Python 2.7 or newer is required."
50f37121300217d3b39ab66dd9c8881bcbcad932dfChris Wailes  sys.exit(1)
5117e3cdc24776d8fdbf1ce16287b9b4dcd516708fJason Sams
52f37121300217d3b39ab66dd9c8881bcbcad932dfChris Wailesimport datetime
53f37121300217d3b39ab66dd9c8881bcbcad932dfChris Wailesimport errno
54f37121300217d3b39ab66dd9c8881bcbcad932dfChris Wailesimport os
55f37121300217d3b39ab66dd9c8881bcbcad932dfChris Wailesimport shlex
56f37121300217d3b39ab66dd9c8881bcbcad932dfChris Wailesimport shutil
57c060f1435e7b9405f3be8974417fa6f410f03753Stephen Hinesimport subprocess
58c060f1435e7b9405f3be8974417fa6f410f03753Stephen Hinesimport tempfile
59c060f1435e7b9405f3be8974417fa6f410f03753Stephen Hinesimport zipfile
60c060f1435e7b9405f3be8974417fa6f410f03753Stephen Hines
61c060f1435e7b9405f3be8974417fa6f410f03753Stephen Hinesimport build_image
62c060f1435e7b9405f3be8974417fa6f410f03753Stephen Hinesimport common
63c060f1435e7b9405f3be8974417fa6f410f03753Stephen Hines
64c060f1435e7b9405f3be8974417fa6f410f03753Stephen HinesOPTIONS = common.OPTIONS
6514ce007a633b10e3b9a3fae29d8f53a7e8c9b59fMatt Wala
66ae2ec3febedfc29376b9104413fb4042028f1265David GrossOPTIONS.add_missing = False
6714ce007a633b10e3b9a3fae29d8f53a7e8c9b59fMatt WalaOPTIONS.rebuild_recovery = False
6814ce007a633b10e3b9a3fae29d8f53a7e8c9b59fMatt WalaOPTIONS.replace_verity_public_key = False
6914ce007a633b10e3b9a3fae29d8f53a7e8c9b59fMatt WalaOPTIONS.replace_verity_private_key = False
70c060f1435e7b9405f3be8974417fa6f410f03753Stephen HinesOPTIONS.is_signing = False
71c060f1435e7b9405f3be8974417fa6f410f03753Stephen Hines
72c060f1435e7b9405f3be8974417fa6f410f03753Stephen Hinesdef AddSystem(output_zip, prefix="IMAGES/", recovery_img=None, boot_img=None):
73c060f1435e7b9405f3be8974417fa6f410f03753Stephen Hines  """Turn the contents of SYSTEM into a system image and store it in
74c060f1435e7b9405f3be8974417fa6f410f03753Stephen Hines  output_zip. Returns the name of the system image file."""
75c060f1435e7b9405f3be8974417fa6f410f03753Stephen Hines
76c060f1435e7b9405f3be8974417fa6f410f03753Stephen Hines  prebuilt_path = os.path.join(OPTIONS.input_tmp, prefix, "system.img")
77c060f1435e7b9405f3be8974417fa6f410f03753Stephen Hines  if os.path.exists(prebuilt_path):
78c060f1435e7b9405f3be8974417fa6f410f03753Stephen Hines    print "system.img already exists in %s, no need to rebuild..." % (prefix,)
79c060f1435e7b9405f3be8974417fa6f410f03753Stephen Hines    return prebuilt_path
80709a0978ae141198018ca9769f8d96292a8928e6Jason Sams
81062c287f573ecc06c38ee4295e5627e12c52ac3dYang Ni  def output_sink(fn, data):
82709a0978ae141198018ca9769f8d96292a8928e6Jason Sams    ofile = open(os.path.join(OPTIONS.input_tmp, "SYSTEM", fn), "w")
83c060f1435e7b9405f3be8974417fa6f410f03753Stephen Hines    ofile.write(data)
84709a0978ae141198018ca9769f8d96292a8928e6Jason Sams    ofile.close()
85709a0978ae141198018ca9769f8d96292a8928e6Jason Sams
86709a0978ae141198018ca9769f8d96292a8928e6Jason Sams  if OPTIONS.rebuild_recovery:
87709a0978ae141198018ca9769f8d96292a8928e6Jason Sams    print "Building new recovery patch"
88bf2111d3b3de310932099514f06924e48fa1d7b2Jason Sams    common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink, recovery_img,
894b3c34e6833e39bc89c2128002806b654b8e623dChris Wailes                             boot_img, info_dict=OPTIONS.info_dict)
9014ce007a633b10e3b9a3fae29d8f53a7e8c9b59fMatt Wala
914b3c34e6833e39bc89c2128002806b654b8e623dChris Wailes  block_list = common.MakeTempFile(prefix="system-blocklist-", suffix=".map")
9214ce007a633b10e3b9a3fae29d8f53a7e8c9b59fMatt Wala  imgname = BuildSystem(OPTIONS.input_tmp, OPTIONS.info_dict,
93709a0978ae141198018ca9769f8d96292a8928e6Jason Sams                        block_list=block_list)
946c1876bbef1b2c89975dce91230a168bd2d2ce4cDavid Gross
95ae2ec3febedfc29376b9104413fb4042028f1265David Gross  common.ZipWrite(output_zip, imgname, prefix + "system.img")
96ae2ec3febedfc29376b9104413fb4042028f1265David Gross  common.ZipWrite(output_zip, block_list, prefix + "system.map")
976c1876bbef1b2c89975dce91230a168bd2d2ce4cDavid Gross  return imgname
98ae2ec3febedfc29376b9104413fb4042028f1265David Gross
996c1876bbef1b2c89975dce91230a168bd2d2ce4cDavid Gross
100709a0978ae141198018ca9769f8d96292a8928e6Jason Samsdef BuildSystem(input_dir, info_dict, block_list=None):
101709a0978ae141198018ca9769f8d96292a8928e6Jason Sams  """Build the (sparse) system image and return the name of a temp
102709a0978ae141198018ca9769f8d96292a8928e6Jason Sams  file containing it."""
103c060f1435e7b9405f3be8974417fa6f410f03753Stephen Hines  return CreateImage(input_dir, info_dict, "system", block_list=block_list)
104dc0d8f7c0f1f43f25c34fbc04656ad578f6e953bPirama Arumuga Nainar
105709a0978ae141198018ca9769f8d96292a8928e6Jason Sams
1068409d6414dd4a42aa59779fcfe9fce18648cb135Stephen Hinesdef AddVendor(output_zip, prefix="IMAGES/"):
1078409d6414dd4a42aa59779fcfe9fce18648cb135Stephen Hines  """Turn the contents of VENDOR into a vendor image and store in it
1088409d6414dd4a42aa59779fcfe9fce18648cb135Stephen Hines  output_zip."""
1098409d6414dd4a42aa59779fcfe9fce18648cb135Stephen Hines
1105aa018cc36e589b07674957714d27ae3d1fa1c4eStephen Hines  prebuilt_path = os.path.join(OPTIONS.input_tmp, prefix, "vendor.img")
1118409d6414dd4a42aa59779fcfe9fce18648cb135Stephen Hines  if os.path.exists(prebuilt_path):
112709a0978ae141198018ca9769f8d96292a8928e6Jason Sams    print "vendor.img already exists in %s, no need to rebuild..." % (prefix,)
113709a0978ae141198018ca9769f8d96292a8928e6Jason Sams    return
114709a0978ae141198018ca9769f8d96292a8928e6Jason Sams
115dc0d8f7c0f1f43f25c34fbc04656ad578f6e953bPirama Arumuga Nainar  block_list = common.MakeTempFile(prefix="vendor-blocklist-", suffix=".map")
116709a0978ae141198018ca9769f8d96292a8928e6Jason Sams  imgname = BuildVendor(OPTIONS.input_tmp, OPTIONS.info_dict,
117110f181b7966212a36ef18016f9b81c7322d0a2fJason Sams                        block_list=block_list)
11840e35cdbe217ec8bf9fc3c69873c7d62fc14158fJean-Luc Brouillet  common.ZipWrite(output_zip, imgname, prefix + "vendor.img")
11940e35cdbe217ec8bf9fc3c69873c7d62fc14158fJean-Luc Brouillet  common.ZipWrite(output_zip, block_list, prefix + "vendor.map")
1209ab5094dd32352b33e251e540934f6e814c5fa5bJean-Luc Brouillet
12145e753a46e587c69b3b0d0c5138e88715a24a29aStephen Hines
12245e753a46e587c69b3b0d0c5138e88715a24a29aStephen Hinesdef BuildVendor(input_dir, info_dict, block_list=None):
123110f181b7966212a36ef18016f9b81c7322d0a2fJason Sams  """Build the (sparse) vendor image and return the name of a temp
124110f181b7966212a36ef18016f9b81c7322d0a2fJason Sams  file containing it."""
12514ce007a633b10e3b9a3fae29d8f53a7e8c9b59fMatt Wala  return CreateImage(input_dir, info_dict, "vendor", block_list=block_list)
12614ce007a633b10e3b9a3fae29d8f53a7e8c9b59fMatt Wala
127d9bae689c1b8c3f2ed1a5f2b374dc9393584b8ddYang Ni
128709a0978ae141198018ca9769f8d96292a8928e6Jason Samsdef CreateImage(input_dir, info_dict, what, block_list=None):
129709a0978ae141198018ca9769f8d96292a8928e6Jason Sams  print "creating " + what + ".img..."
130709a0978ae141198018ca9769f8d96292a8928e6Jason Sams
131709a0978ae141198018ca9769f8d96292a8928e6Jason Sams  img = common.MakeTempFile(prefix=what + "-", suffix=".img")
132da0f069871343119251d6b0586be356dc2146a62Yang Ni
1332abfcc6d129fe3defddef4540aa95cc445c03a7aYang Ni  # The name of the directory it is making an image out of matters to
1342abfcc6d129fe3defddef4540aa95cc445c03a7aYang Ni  # mkyaffs2image.  It wants "system" but we have a directory named
13582e135c4bbe18855d8ed02632bb074f8da0b96e0Miao Wang  # "SYSTEM", so create a symlink.
136da0f069871343119251d6b0586be356dc2146a62Yang Ni  try:
1372abfcc6d129fe3defddef4540aa95cc445c03a7aYang Ni    os.symlink(os.path.join(input_dir, what.upper()),
13814ce007a633b10e3b9a3fae29d8f53a7e8c9b59fMatt Wala               os.path.join(input_dir, what))
13914ce007a633b10e3b9a3fae29d8f53a7e8c9b59fMatt Wala  except OSError as e:
14014ce007a633b10e3b9a3fae29d8f53a7e8c9b59fMatt Wala    # bogus error on my mac version?
14114ce007a633b10e3b9a3fae29d8f53a7e8c9b59fMatt Wala    #   File "./build/tools/releasetools/img_from_target_files"
14282e135c4bbe18855d8ed02632bb074f8da0b96e0Miao Wang    #     os.path.join(OPTIONS.input_tmp, "system"))
143cb17015fed6b11a5028f31cc804a3847e379945dYang Ni    # OSError: [Errno 17] File exists
144aa6757ffc1b23d771566439c3179fdbc1e5ba569Pirama Arumuga Nainar    if e.errno == errno.EEXIST:
145709a0978ae141198018ca9769f8d96292a8928e6Jason Sams      pass
146709a0978ae141198018ca9769f8d96292a8928e6Jason Sams
147709a0978ae141198018ca9769f8d96292a8928e6Jason Sams  image_props = build_image.ImagePropFromGlobalDict(info_dict, what)
148709a0978ae141198018ca9769f8d96292a8928e6Jason Sams  fstab = info_dict["fstab"]
149709a0978ae141198018ca9769f8d96292a8928e6Jason Sams  if fstab:
150709a0978ae141198018ca9769f8d96292a8928e6Jason Sams    image_props["fs_type"] = fstab["/" + what].fs_type
151709a0978ae141198018ca9769f8d96292a8928e6Jason Sams
152cb17015fed6b11a5028f31cc804a3847e379945dYang Ni  # Use a fixed timestamp (01/01/2009) when packaging the image.
153cb17015fed6b11a5028f31cc804a3847e379945dYang Ni  # Bug: 24377993
154cb17015fed6b11a5028f31cc804a3847e379945dYang Ni  epoch = datetime.datetime.fromtimestamp(0)
155f02a2b0a2749d4a4f07edbc23eddff2e51d11b72Yang Ni  timestamp = (datetime.datetime(2009, 1, 1) - epoch).total_seconds()
1567974fc03e11f3a8dd40f794f3b33b4889483090cRahul Chaudhry  image_props["timestamp"] = int(timestamp)
157d9bae689c1b8c3f2ed1a5f2b374dc9393584b8ddYang Ni
158da0f069871343119251d6b0586be356dc2146a62Yang Ni  if what == "system":
159da0f069871343119251d6b0586be356dc2146a62Yang Ni    fs_config_prefix = ""
160da43d5866f6224e17e756fdfea405ca4b00a38fcJiyong Park  else:
161cfb1d0b3ed5a775689c20ce053bfc023ea9d8e5fVictor Khimenko    fs_config_prefix = what + "_"
162682672e36b05349bc4d9dee74e9fab73ce804183Pirama Arumuga Nainar
163cfb1d0b3ed5a775689c20ce053bfc023ea9d8e5fVictor Khimenko  fs_config = os.path.join(
164cfb1d0b3ed5a775689c20ce053bfc023ea9d8e5fVictor Khimenko      input_dir, "META/" + fs_config_prefix + "filesystem_config.txt")
165da43d5866f6224e17e756fdfea405ca4b00a38fcJiyong Park  if not os.path.exists(fs_config):
166cfb1d0b3ed5a775689c20ce053bfc023ea9d8e5fVictor Khimenko    fs_config = None
167cfb1d0b3ed5a775689c20ce053bfc023ea9d8e5fVictor Khimenko
168da0f069871343119251d6b0586be356dc2146a62Yang Ni  # Override values loaded from info_dict.
169da0f069871343119251d6b0586be356dc2146a62Yang Ni  if fs_config:
170da43d5866f6224e17e756fdfea405ca4b00a38fcJiyong Park    image_props["fs_config"] = fs_config
171cfb1d0b3ed5a775689c20ce053bfc023ea9d8e5fVictor Khimenko  if block_list:
172682672e36b05349bc4d9dee74e9fab73ce804183Pirama Arumuga Nainar    image_props["block_list"] = block_list
173da0f069871343119251d6b0586be356dc2146a62Yang Ni
174da0f069871343119251d6b0586be356dc2146a62Yang Ni  succ = build_image.BuildImage(os.path.join(input_dir, what),
1757974fc03e11f3a8dd40f794f3b33b4889483090cRahul Chaudhry                                image_props, img)
176709a0978ae141198018ca9769f8d96292a8928e6Jason Sams  assert succ, "build " + what + ".img image failed"
177a845c35880f8434619ac299e8af47aa6a5049e8dYang Ni
178a845c35880f8434619ac299e8af47aa6a5049e8dYang Ni  return img
179a845c35880f8434619ac299e8af47aa6a5049e8dYang Ni
180a845c35880f8434619ac299e8af47aa6a5049e8dYang Ni
181a845c35880f8434619ac299e8af47aa6a5049e8dYang Nidef AddUserdata(output_zip, prefix="IMAGES/"):
182a845c35880f8434619ac299e8af47aa6a5049e8dYang Ni  """Create a userdata image and store it in output_zip.
183a845c35880f8434619ac299e8af47aa6a5049e8dYang Ni
184a845c35880f8434619ac299e8af47aa6a5049e8dYang Ni  In most case we just create and store an empty userdata.img;
185a845c35880f8434619ac299e8af47aa6a5049e8dYang Ni  But the invoker can also request to create userdata.img with real
186a845c35880f8434619ac299e8af47aa6a5049e8dYang Ni  data from the target files, by setting "userdata_img_with_data=true"
187a845c35880f8434619ac299e8af47aa6a5049e8dYang Ni  in OPTIONS.info_dict.
188a845c35880f8434619ac299e8af47aa6a5049e8dYang Ni  """
189a845c35880f8434619ac299e8af47aa6a5049e8dYang Ni
190a845c35880f8434619ac299e8af47aa6a5049e8dYang Ni  prebuilt_path = os.path.join(OPTIONS.input_tmp, prefix, "userdata.img")
191a845c35880f8434619ac299e8af47aa6a5049e8dYang Ni  if os.path.exists(prebuilt_path):
192a845c35880f8434619ac299e8af47aa6a5049e8dYang Ni    print "userdata.img already exists in %s, no need to rebuild..." % (prefix,)
193a845c35880f8434619ac299e8af47aa6a5049e8dYang Ni    return
194a845c35880f8434619ac299e8af47aa6a5049e8dYang Ni
195a845c35880f8434619ac299e8af47aa6a5049e8dYang Ni  # Skip userdata.img if no size.
196a845c35880f8434619ac299e8af47aa6a5049e8dYang Ni  image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, "data")
197e0f1e2ef6df041d4d88a4d4135c46ddcb0c50029Justin Yun  if not image_props.get("partition_size"):
198e0f1e2ef6df041d4d88a4d4135c46ddcb0c50029Justin Yun    return
199e0f1e2ef6df041d4d88a4d4135c46ddcb0c50029Justin Yun
200e0f1e2ef6df041d4d88a4d4135c46ddcb0c50029Justin Yun  print "creating userdata.img..."
201e0f1e2ef6df041d4d88a4d4135c46ddcb0c50029Justin Yun
202e0f1e2ef6df041d4d88a4d4135c46ddcb0c50029Justin Yun  # Use a fixed timestamp (01/01/2009) when packaging the image.
203e0f1e2ef6df041d4d88a4d4135c46ddcb0c50029Justin Yun  # Bug: 24377993
204e0f1e2ef6df041d4d88a4d4135c46ddcb0c50029Justin Yun  epoch = datetime.datetime.fromtimestamp(0)
205e0f1e2ef6df041d4d88a4d4135c46ddcb0c50029Justin Yun  timestamp = (datetime.datetime(2009, 1, 1) - epoch).total_seconds()
206e0f1e2ef6df041d4d88a4d4135c46ddcb0c50029Justin Yun  image_props["timestamp"] = int(timestamp)
207a845c35880f8434619ac299e8af47aa6a5049e8dYang Ni
208a845c35880f8434619ac299e8af47aa6a5049e8dYang Ni  # The name of the directory it is making an image out of matters to
2096a236ad3a3760e8124b68a1b6220ed6e4fbfb152Stephen Hines  # mkyaffs2image.  So we create a temp dir, and within it we create an
210  # empty dir named "data", or a symlink to the DATA dir,
211  # and build the image from that.
212  temp_dir = tempfile.mkdtemp()
213  user_dir = os.path.join(temp_dir, "data")
214  empty = (OPTIONS.info_dict.get("userdata_img_with_data") != "true")
215  if empty:
216    # Create an empty dir.
217    os.mkdir(user_dir)
218  else:
219    # Symlink to the DATA dir.
220    os.symlink(os.path.join(OPTIONS.input_tmp, "DATA"),
221               user_dir)
222
223  img = tempfile.NamedTemporaryFile()
224
225  fstab = OPTIONS.info_dict["fstab"]
226  if fstab:
227    image_props["fs_type"] = fstab["/data"].fs_type
228  succ = build_image.BuildImage(user_dir, image_props, img.name)
229  assert succ, "build userdata.img image failed"
230
231  common.CheckSize(img.name, "userdata.img", OPTIONS.info_dict)
232  common.ZipWrite(output_zip, img.name, prefix + "userdata.img")
233  img.close()
234  shutil.rmtree(temp_dir)
235
236
237def AddVBMeta(output_zip, boot_img_path, system_img_path, prefix="IMAGES/"):
238  """Create a VBMeta image and store it in output_zip."""
239  _, img_file_name = tempfile.mkstemp()
240  avbtool = os.getenv('AVBTOOL') or "avbtool"
241  cmd = [avbtool, "make_vbmeta_image",
242         "--output", img_file_name,
243         "--include_descriptors_from_image", boot_img_path,
244         "--include_descriptors_from_image", system_img_path,
245         "--generate_dm_verity_cmdline_from_hashtree", system_img_path]
246  common.AppendAVBSigningArgs(cmd)
247  args = OPTIONS.info_dict.get("board_avb_make_vbmeta_image_args", None)
248  if args and args.strip():
249    cmd.extend(shlex.split(args))
250  p = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
251  p.communicate()
252  assert p.returncode == 0, "avbtool make_vbmeta_image failed"
253  common.ZipWrite(output_zip, img_file_name, prefix + "vbmeta.img")
254
255
256def AddPartitionTable(output_zip, prefix="IMAGES/"):
257  """Create a partition table image and store it in output_zip."""
258
259  _, img_file_name = tempfile.mkstemp()
260  _, bpt_file_name = tempfile.mkstemp()
261
262  # use BPTTOOL from environ, or "bpttool" if empty or not set.
263  bpttool = os.getenv("BPTTOOL") or "bpttool"
264  cmd = [bpttool, "make_table", "--output_json", bpt_file_name,
265         "--output_gpt", img_file_name]
266  input_files_str = OPTIONS.info_dict["board_bpt_input_files"]
267  input_files = input_files_str.split(" ")
268  for i in input_files:
269    cmd.extend(["--input", i])
270  disk_size = OPTIONS.info_dict.get("board_bpt_disk_size")
271  if disk_size:
272    cmd.extend(["--disk_size", disk_size])
273  args = OPTIONS.info_dict.get("board_bpt_make_table_args")
274  if args:
275    cmd.extend(shlex.split(args))
276
277  p = common.Run(cmd, stdout=subprocess.PIPE)
278  p.communicate()
279  assert p.returncode == 0, "bpttool make_table failed"
280
281  common.ZipWrite(output_zip, img_file_name, prefix + "partition-table.img")
282  common.ZipWrite(output_zip, bpt_file_name, prefix + "partition-table.bpt")
283
284
285def AddCache(output_zip, prefix="IMAGES/"):
286  """Create an empty cache image and store it in output_zip."""
287
288  prebuilt_path = os.path.join(OPTIONS.input_tmp, prefix, "cache.img")
289  if os.path.exists(prebuilt_path):
290    print "cache.img already exists in %s, no need to rebuild..." % (prefix,)
291    return
292
293  image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, "cache")
294  # The build system has to explicitly request for cache.img.
295  if "fs_type" not in image_props:
296    return
297
298  print "creating cache.img..."
299
300  # Use a fixed timestamp (01/01/2009) when packaging the image.
301  # Bug: 24377993
302  epoch = datetime.datetime.fromtimestamp(0)
303  timestamp = (datetime.datetime(2009, 1, 1) - epoch).total_seconds()
304  image_props["timestamp"] = int(timestamp)
305
306  # The name of the directory it is making an image out of matters to
307  # mkyaffs2image.  So we create a temp dir, and within it we create an
308  # empty dir named "cache", and build the image from that.
309  temp_dir = tempfile.mkdtemp()
310  user_dir = os.path.join(temp_dir, "cache")
311  os.mkdir(user_dir)
312  img = tempfile.NamedTemporaryFile()
313
314  fstab = OPTIONS.info_dict["fstab"]
315  if fstab:
316    image_props["fs_type"] = fstab["/cache"].fs_type
317  succ = build_image.BuildImage(user_dir, image_props, img.name)
318  assert succ, "build cache.img image failed"
319
320  common.CheckSize(img.name, "cache.img", OPTIONS.info_dict)
321  common.ZipWrite(output_zip, img.name, prefix + "cache.img")
322  img.close()
323  os.rmdir(user_dir)
324  os.rmdir(temp_dir)
325
326
327def AddImagesToTargetFiles(filename):
328  OPTIONS.input_tmp, input_zip = common.UnzipTemp(filename)
329
330  if not OPTIONS.add_missing:
331    for n in input_zip.namelist():
332      if n.startswith("IMAGES/"):
333        print "target_files appears to already contain images."
334        sys.exit(1)
335
336  try:
337    input_zip.getinfo("VENDOR/")
338    has_vendor = True
339  except KeyError:
340    has_vendor = False
341
342  OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.input_tmp)
343
344  common.ZipClose(input_zip)
345  output_zip = zipfile.ZipFile(filename, "a",
346                               compression=zipfile.ZIP_DEFLATED,
347                               allowZip64=True)
348
349  has_recovery = (OPTIONS.info_dict.get("no_recovery") != "true")
350  system_root_image = (OPTIONS.info_dict.get("system_root_image", None) == "true")
351
352  def banner(s):
353    print "\n\n++++ " + s + " ++++\n\n"
354
355  prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", "boot.img")
356  boot_image = None
357  if os.path.exists(prebuilt_path):
358    banner("boot")
359    print "boot.img already exists in IMAGES/, no need to rebuild..."
360    if OPTIONS.rebuild_recovery:
361      boot_image = common.GetBootableImage(
362          "IMAGES/boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
363  else:
364    banner("boot")
365    boot_image = common.GetBootableImage(
366        "IMAGES/boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
367    if boot_image:
368      boot_image.AddToZip(output_zip)
369
370  recovery_image = None
371  if has_recovery:
372    banner("recovery")
373    prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", "recovery.img")
374    if os.path.exists(prebuilt_path):
375      print "recovery.img already exists in IMAGES/, no need to rebuild..."
376      if OPTIONS.rebuild_recovery:
377        recovery_image = common.GetBootableImage(
378            "IMAGES/recovery.img", "recovery.img", OPTIONS.input_tmp,
379            "RECOVERY")
380    else:
381      recovery_image = common.GetBootableImage(
382          "IMAGES/recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
383      if recovery_image:
384        recovery_image.AddToZip(output_zip)
385
386      banner("recovery (two-step image)")
387      # The special recovery.img for two-step package use.
388      recovery_two_step_image = common.GetBootableImage(
389          "IMAGES/recovery-two-step.img", "recovery-two-step.img",
390          OPTIONS.input_tmp, "RECOVERY", two_step_image=True)
391      if recovery_two_step_image:
392        recovery_two_step_image.AddToZip(output_zip)
393
394  banner("system")
395  system_img_path = AddSystem(
396    output_zip, recovery_img=recovery_image, boot_img=boot_image)
397  if has_vendor:
398    banner("vendor")
399    AddVendor(output_zip)
400  if not OPTIONS.is_signing:
401    banner("userdata")
402    AddUserdata(output_zip)
403    banner("cache")
404    AddCache(output_zip)
405  if OPTIONS.info_dict.get("board_bpt_enable", None) == "true":
406    banner("partition-table")
407    AddPartitionTable(output_zip)
408  if OPTIONS.info_dict.get("board_avb_enable", None) == "true":
409    banner("vbmeta")
410    boot_contents = boot_image.WriteToTemp()
411    AddVBMeta(output_zip, boot_contents.name, system_img_path)
412
413  # For devices using A/B update, copy over images from RADIO/ and/or
414  # VENDOR_IMAGES/ to IMAGES/ and make sure we have all the needed
415  # images ready under IMAGES/. All images should have '.img' as extension.
416  banner("radio")
417  ab_partitions = os.path.join(OPTIONS.input_tmp, "META", "ab_partitions.txt")
418  if os.path.exists(ab_partitions):
419    with open(ab_partitions, 'r') as f:
420      lines = f.readlines()
421    for line in lines:
422      img_name = line.strip() + ".img"
423      prebuilt_path = os.path.join(OPTIONS.input_tmp, "IMAGES", img_name)
424      if os.path.exists(prebuilt_path):
425        print "%s already exists, no need to overwrite..." % (img_name,)
426        continue
427
428      img_radio_path = os.path.join(OPTIONS.input_tmp, "RADIO", img_name)
429      img_vendor_dir = os.path.join(
430        OPTIONS.input_tmp, "VENDOR_IMAGES")
431      if os.path.exists(img_radio_path):
432        common.ZipWrite(output_zip, img_radio_path,
433                        os.path.join("IMAGES", img_name))
434      else:
435        for root, _, files in os.walk(img_vendor_dir):
436          if img_name in files:
437            common.ZipWrite(output_zip, os.path.join(root, img_name),
438              os.path.join("IMAGES", img_name))
439            break
440
441      # Zip spec says: All slashes MUST be forward slashes.
442      img_path = 'IMAGES/' + img_name
443      assert img_path in output_zip.namelist(), "cannot find " + img_name
444
445  common.ZipClose(output_zip)
446
447def main(argv):
448  def option_handler(o, a):
449    if o in ("-a", "--add_missing"):
450      OPTIONS.add_missing = True
451    elif o in ("-r", "--rebuild_recovery",):
452      OPTIONS.rebuild_recovery = True
453    elif o == "--replace_verity_private_key":
454      OPTIONS.replace_verity_private_key = (True, a)
455    elif o == "--replace_verity_public_key":
456      OPTIONS.replace_verity_public_key = (True, a)
457    elif o == "--is_signing":
458      OPTIONS.is_signing = True
459    else:
460      return False
461    return True
462
463  args = common.ParseOptions(
464      argv, __doc__, extra_opts="ar",
465      extra_long_opts=["add_missing", "rebuild_recovery",
466                       "replace_verity_public_key=",
467                       "replace_verity_private_key=",
468                       "is_signing"],
469      extra_option_handler=option_handler)
470
471
472  if len(args) != 1:
473    common.Usage(__doc__)
474    sys.exit(1)
475
476  AddImagesToTargetFiles(args[0])
477  print "done."
478
479if __name__ == '__main__':
480  try:
481    common.CloseInheritedPipes()
482    main(sys.argv[1:])
483  except common.ExternalError as e:
484    print
485    print "   ERROR: %s" % (e,)
486    print
487    sys.exit(1)
488  finally:
489    common.Cleanup()
490