Merge "hardware/interfaces: Android.mk always -Werror"
diff --git a/core/Makefile b/core/Makefile
index a98e400..e6b5878 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -1701,8 +1701,8 @@
$(INSTALLED_VENDORIMAGE_TARGET): $(INTERNAL_USERIMAGES_DEPS) $(INTERNAL_VENDORIMAGE_FILES) $(INSTALLED_FILES_FILE_VENDOR) $(BUILD_IMAGE_SRCS) $(DEPMOD) $(BOARD_VENDOR_KERNEL_MODULES)
$(build-vendorimage-target)
-.PHONY: vendorimage-nodeps
-vendorimage-nodeps: | $(INTERNAL_USERIMAGES_DEPS) $(DEPMOD)
+.PHONY: vendorimage-nodeps vnod
+vendorimage-nodeps vnod: | $(INTERNAL_USERIMAGES_DEPS) $(DEPMOD)
$(build-vendorimage-target)
else ifdef BOARD_PREBUILT_VENDORIMAGE
@@ -1905,6 +1905,13 @@
$(BUILT_TARGET_FILES_PACKAGE): PRIVATE_RECOVERY_OUT := RECOVERY
endif
+# Run fs_config while creating the target files package
+# $1: root directory
+# $2: add prefix
+define fs_config
+(cd $(1); find . -type d | sed 's,$$,/,'; find . \! -type d) | cut -c 3- | sort | sed 's,^,$(2),' | $(HOST_OUT_EXECUTABLES)/fs_config -C -D $(TARGET_OUT) -S $(SELINUX_FC)
+endef
+
# Depending on the various images guarantees that the underlying
# directories are up-to-date.
$(BUILT_TARGET_FILES_PACKAGE): \
@@ -2117,6 +2124,7 @@
ifdef OSRELEASED_DIRECTORY
$(hide) $(ACP) $(TARGET_OUT_OEM)/$(OSRELEASED_DIRECTORY)/product_id $(zip_root)/META/product_id.txt
$(hide) $(ACP) $(TARGET_OUT_OEM)/$(OSRELEASED_DIRECTORY)/product_version $(zip_root)/META/product_version.txt
+ $(hide) $(ACP) $(TARGET_OUT_ETC)/$(OSRELEASED_DIRECTORY)/system_version $(zip_root)/META/system_version.txt
endif
endif
ifeq ($(BREAKPAD_GENERATE_SYMBOLS),true)
@@ -2127,28 +2135,29 @@
$(hide) mkdir -p $(zip_root)/IMAGES
$(hide) cp $(INSTALLED_VENDORIMAGE_TARGET) $(zip_root)/IMAGES/
endif
+ @# Run fs_config on all the system, vendor, boot ramdisk,
+ @# and recovery ramdisk files in the zip, and save the output
+ $(hide) $(call fs_config,$(zip_root)/SYSTEM,system/) > $(zip_root)/META/filesystem_config.txt
+ifdef BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE
+ $(hide) $(call fs_config,$(zip_root)/VENDOR,vendor/) > $(zip_root)/META/vendor_filesystem_config.txt
+endif
+ifeq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
+ $(hide) $(call fs_config,$(zip_root)/ROOT,) > $(zip_root)/META/root_filesystem_config.txt
+endif
+ $(hide) $(call fs_config,$(zip_root)/BOOT/RAMDISK,) > $(zip_root)/META/boot_filesystem_config.txt
+ifneq ($(INSTALLED_RECOVERYIMAGE_TARGET),)
+ $(hide) $(call fs_config,$(zip_root)/RECOVERY/RAMDISK,) > $(zip_root)/META/recovery_filesystem_config.txt
+endif
+ifdef INSTALLED_SYSTEMOTHERIMAGE_TARGET
+ $(hide) $(call fs_config,$(zip_root)/SYSTEM_OTHER,system/) > $(zip_root)/META/system_other_filesystem_config.txt
+endif
+ $(hide) PATH=$(foreach p,$(INTERNAL_USERIMAGES_BINARY_PATHS),$(p):)$$PATH MKBOOTIMG=$(MKBOOTIMG) \
+ ./build/tools/releasetools/add_img_to_target_files -a -v -p $(HOST_OUT) $(zip_root)
@# Zip everything up, preserving symlinks and placing META/ files first to
@# help early validation of the .zip file while uploading it.
$(hide) find $(zip_root)/META | sort >$@.list
$(hide) find $(zip_root) | grep -v "^$(zip_root)/META/" | sort >>$@.list
$(hide) $(SOONG_ZIP) -d -o $@ -C $(zip_root) -l $@.list
- @# Run fs_config on all the system, vendor, boot ramdisk,
- @# and recovery ramdisk files in the zip, and save the output
- $(hide) zipinfo -1 $@ | awk 'BEGIN { FS="SYSTEM/" } /^SYSTEM\// {print "system/" $$2}' | $(HOST_OUT_EXECUTABLES)/fs_config -C -D $(TARGET_OUT) -S $(SELINUX_FC) > $(zip_root)/META/filesystem_config.txt
- $(hide) zipinfo -1 $@ | awk 'BEGIN { FS="VENDOR/" } /^VENDOR\// {print "vendor/" $$2}' | $(HOST_OUT_EXECUTABLES)/fs_config -C -D $(TARGET_OUT) -S $(SELINUX_FC) > $(zip_root)/META/vendor_filesystem_config.txt
-ifeq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
- $(hide) zipinfo -1 $@ | awk 'BEGIN { FS="ROOT/" } /^ROOT\// {print $$2}' | $(HOST_OUT_EXECUTABLES)/fs_config -C -D $(TARGET_OUT) -S $(SELINUX_FC) > $(zip_root)/META/root_filesystem_config.txt
-endif
- $(hide) zipinfo -1 $@ | awk 'BEGIN { FS="BOOT/RAMDISK/" } /^BOOT\/RAMDISK\// {print $$2}' | $(HOST_OUT_EXECUTABLES)/fs_config -C -D $(TARGET_OUT) -S $(SELINUX_FC) > $(zip_root)/META/boot_filesystem_config.txt
-ifneq ($(INSTALLED_RECOVERYIMAGE_TARGET),)
- $(hide) zipinfo -1 $@ | awk 'BEGIN { FS="RECOVERY/RAMDISK/" } /^RECOVERY\/RAMDISK\// {print $$2}' | $(HOST_OUT_EXECUTABLES)/fs_config -C -D $(TARGET_OUT) -S $(SELINUX_FC) > $(zip_root)/META/recovery_filesystem_config.txt
-endif
-ifdef INSTALLED_SYSTEMOTHERIMAGE_TARGET
- $(hide) zipinfo -1 $@ | awk 'BEGIN { FS="SYSTEM_OTHER/" } /^SYSTEM_OTHER\// { print "system/" $$2}' | $(HOST_OUT_EXECUTABLES)/fs_config -C -D $(TARGET_OUT) -S $(SELINUX_FC) > $(zip_root)/META/system_other_filesystem_config.txt
-endif
- $(hide) (cd $(zip_root) && zip -qX ../$(notdir $@) META/*filesystem_config.txt)
- $(hide) PATH=$(foreach p,$(INTERNAL_USERIMAGES_BINARY_PATHS),$(p):)$$PATH MKBOOTIMG=$(MKBOOTIMG) \
- ./build/tools/releasetools/add_img_to_target_files -a -v -p $(HOST_OUT) $@
.PHONY: target-files-package
target-files-package: $(BUILT_TARGET_FILES_PACKAGE)
diff --git a/core/config.mk b/core/config.mk
index 968c0de..319a069 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -852,7 +852,7 @@
userdataimage-nodeps userdatatarball-nodeps \
cacheimage-nodeps \
bptimage-nodeps \
- vendorimage-nodeps \
+ vnod vendorimage-nodeps \
systemotherimage-nodeps \
ramdisk-nodeps \
bootimage-nodeps \
diff --git a/core/help.mk b/core/help.mk
index 6e0b2c0..c034e79 100644
--- a/core/help.mk
+++ b/core/help.mk
@@ -22,6 +22,7 @@
@echo "droid Default target"
@echo "clean (aka clobber) equivalent to rm -rf out/"
@echo "snod Quickly rebuild the system image from built packages"
+ @echo "vnod Quickly rebuild the vendor image from built packages"
@echo "offline-sdk-docs Generate the HTML for the developer SDK docs"
@echo "doc-comment-check-docs Check HTML doc links & validity, without generating HTML"
@echo "libandroid_runtime All the JNI framework stuff"
diff --git a/tools/releasetools/add_img_to_target_files.py b/tools/releasetools/add_img_to_target_files.py
index 9403a77..0b8f3a0 100755
--- a/tools/releasetools/add_img_to_target_files.py
+++ b/tools/releasetools/add_img_to_target_files.py
@@ -73,6 +73,25 @@
OPTIONS.replace_verity_private_key = False
OPTIONS.is_signing = False
+
+class OutputFile(object):
+ def __init__(self, output_zip, input_dir, prefix, name):
+ self._output_zip = output_zip
+ self.input_name = os.path.join(input_dir, prefix, name)
+
+ if self._output_zip:
+ self._zip_name = os.path.join(prefix, name)
+
+ root, suffix = os.path.splitext(name)
+ self.name = common.MakeTempFile(prefix=root + '-', suffix=suffix)
+ else:
+ self.name = self.input_name
+
+ def Write(self):
+ if self._output_zip:
+ common.ZipWrite(self._output_zip, self.name, self._zip_name)
+
+
def GetCareMap(which, imgname):
"""Generate care_map of system (or vendor) partition"""
@@ -98,10 +117,10 @@
"""Turn the contents of SYSTEM into a system image and store it in
output_zip. Returns the name of the system image file."""
- prebuilt_path = os.path.join(OPTIONS.input_tmp, prefix, "system.img")
- if os.path.exists(prebuilt_path):
+ img = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "system.img")
+ if os.path.exists(img.input_name):
print("system.img already exists in %s, no need to rebuild..." % (prefix,))
- return prebuilt_path
+ return img.input_name
def output_sink(fn, data):
ofile = open(os.path.join(OPTIONS.input_tmp, "SYSTEM", fn), "w")
@@ -113,74 +132,52 @@
common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink, recovery_img,
boot_img, info_dict=OPTIONS.info_dict)
- block_list = common.MakeTempFile(prefix="system-blocklist-", suffix=".map")
- imgname = BuildSystem(OPTIONS.input_tmp, OPTIONS.info_dict,
- block_list=block_list)
+ block_list = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "system.map")
+ CreateImage(OPTIONS.input_tmp, OPTIONS.info_dict, "system", img,
+ block_list=block_list)
- common.ZipWrite(output_zip, imgname, prefix + "system.img")
- common.ZipWrite(output_zip, block_list, prefix + "system.map")
- return imgname
-
-
-def BuildSystem(input_dir, info_dict, block_list=None):
- """Build the (sparse) system image and return the name of a temp
- file containing it."""
- return CreateImage(input_dir, info_dict, "system", block_list=block_list)
+ return img.name
def AddSystemOther(output_zip, prefix="IMAGES/"):
"""Turn the contents of SYSTEM_OTHER into a system_other image
and store it in output_zip."""
- prebuilt_path = os.path.join(OPTIONS.input_tmp, prefix, "system_other.img")
- if os.path.exists(prebuilt_path):
+ img = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "system_other.img")
+ if os.path.exists(img.input_name):
print("system_other.img already exists in %s, no need to rebuild..." % (
prefix,))
return
- imgname = BuildSystemOther(OPTIONS.input_tmp, OPTIONS.info_dict)
- common.ZipWrite(output_zip, imgname, prefix + "system_other.img")
-
-def BuildSystemOther(input_dir, info_dict):
- """Build the (sparse) system_other image and return the name of a temp
- file containing it."""
- return CreateImage(input_dir, info_dict, "system_other", block_list=None)
+ CreateImage(OPTIONS.input_tmp, OPTIONS.info_dict, "system_other", img)
def AddVendor(output_zip, prefix="IMAGES/"):
"""Turn the contents of VENDOR into a vendor image and store in it
output_zip."""
- prebuilt_path = os.path.join(OPTIONS.input_tmp, prefix, "vendor.img")
- if os.path.exists(prebuilt_path):
+ img = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "vendor.img")
+ if os.path.exists(img.input_name):
print("vendor.img already exists in %s, no need to rebuild..." % (prefix,))
- return prebuilt_path
+ return img.input_name
- block_list = common.MakeTempFile(prefix="vendor-blocklist-", suffix=".map")
- imgname = BuildVendor(OPTIONS.input_tmp, OPTIONS.info_dict,
- block_list=block_list)
- common.ZipWrite(output_zip, imgname, prefix + "vendor.img")
- common.ZipWrite(output_zip, block_list, prefix + "vendor.map")
- return imgname
+ block_list = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "vendor.map")
+ CreateImage(OPTIONS.input_tmp, OPTIONS.info_dict, "vendor", img,
+ block_list=block_list)
+ return img.name
-def BuildVendor(input_dir, info_dict, block_list=None):
- """Build the (sparse) vendor image and return the name of a temp
- file containing it."""
- return CreateImage(input_dir, info_dict, "vendor", block_list=block_list)
-
-
-def CreateImage(input_dir, info_dict, what, block_list=None):
+def CreateImage(input_dir, info_dict, what, output_file, block_list=None):
print("creating " + what + ".img...")
- img = common.MakeTempFile(prefix=what + "-", suffix=".img")
-
# The name of the directory it is making an image out of matters to
# mkyaffs2image. It wants "system" but we have a directory named
# "SYSTEM", so create a symlink.
+ temp_dir = tempfile.mkdtemp()
+ OPTIONS.tempfiles.append(temp_dir)
try:
os.symlink(os.path.join(input_dir, what.upper()),
- os.path.join(input_dir, what))
+ os.path.join(temp_dir, what))
except OSError as e:
# bogus error on my mac version?
# File "./build/tools/releasetools/img_from_target_files"
@@ -215,12 +212,16 @@
if fs_config:
image_props["fs_config"] = fs_config
if block_list:
- image_props["block_list"] = block_list
+ image_props["block_list"] = block_list.name
- succ = build_image.BuildImage(os.path.join(input_dir, what),
- image_props, img)
+ succ = build_image.BuildImage(os.path.join(temp_dir, what),
+ image_props, output_file.name)
assert succ, "build " + what + ".img image failed"
+ output_file.Write()
+ if block_list:
+ block_list.Write()
+
is_verity_partition = "verity_block_device" in image_props
verity_supported = image_props.get("verity") == "true"
if is_verity_partition and verity_supported:
@@ -229,8 +230,6 @@
adjusted_blocks_key = what + "_adjusted_partition_size"
info_dict[adjusted_blocks_key] = int(adjusted_blocks_value)/4096 - 1
- return img
-
def AddUserdata(output_zip, prefix="IMAGES/"):
"""Create a userdata image and store it in output_zip.
@@ -241,8 +240,8 @@
in OPTIONS.info_dict.
"""
- prebuilt_path = os.path.join(OPTIONS.input_tmp, prefix, "userdata.img")
- if os.path.exists(prebuilt_path):
+ img = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "userdata.img")
+ if os.path.exists(img.input_name):
print("userdata.img already exists in %s, no need to rebuild..." % (
prefix,))
return
@@ -265,6 +264,7 @@
# empty dir named "data", or a symlink to the DATA dir,
# and build the image from that.
temp_dir = tempfile.mkdtemp()
+ OPTIONS.tempfiles.append(temp_dir)
user_dir = os.path.join(temp_dir, "data")
empty = (OPTIONS.info_dict.get("userdata_img_with_data") != "true")
if empty:
@@ -275,8 +275,6 @@
os.symlink(os.path.join(OPTIONS.input_tmp, "DATA"),
user_dir)
- img = tempfile.NamedTemporaryFile()
-
fstab = OPTIONS.info_dict["fstab"]
if fstab:
image_props["fs_type"] = fstab["/data"].fs_type
@@ -284,17 +282,15 @@
assert succ, "build userdata.img image failed"
common.CheckSize(img.name, "userdata.img", OPTIONS.info_dict)
- common.ZipWrite(output_zip, img.name, prefix + "userdata.img")
- img.close()
- shutil.rmtree(temp_dir)
+ img.Write()
def AddVBMeta(output_zip, boot_img_path, system_img_path, prefix="IMAGES/"):
"""Create a VBMeta image and store it in output_zip."""
- _, img_file_name = tempfile.mkstemp()
+ img = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "vbmeta.img")
avbtool = os.getenv('AVBTOOL') or "avbtool"
cmd = [avbtool, "make_vbmeta_image",
- "--output", img_file_name,
+ "--output", img.name,
"--include_descriptors_from_image", boot_img_path,
"--include_descriptors_from_image", system_img_path,
"--generate_dm_verity_cmdline_from_hashtree", system_img_path]
@@ -305,19 +301,19 @@
p = common.Run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p.communicate()
assert p.returncode == 0, "avbtool make_vbmeta_image failed"
- common.ZipWrite(output_zip, img_file_name, prefix + "vbmeta.img")
+ img.Write()
def AddPartitionTable(output_zip, prefix="IMAGES/"):
"""Create a partition table image and store it in output_zip."""
- _, img_file_name = tempfile.mkstemp()
- _, bpt_file_name = tempfile.mkstemp()
+ img = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "partition-table.img")
+ bpt = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "partition-table.bpt")
# use BPTTOOL from environ, or "bpttool" if empty or not set.
bpttool = os.getenv("BPTTOOL") or "bpttool"
- cmd = [bpttool, "make_table", "--output_json", bpt_file_name,
- "--output_gpt", img_file_name]
+ cmd = [bpttool, "make_table", "--output_json", bpt.name,
+ "--output_gpt", img.name]
input_files_str = OPTIONS.info_dict["board_bpt_input_files"]
input_files = input_files_str.split(" ")
for i in input_files:
@@ -333,15 +329,15 @@
p.communicate()
assert p.returncode == 0, "bpttool make_table failed"
- common.ZipWrite(output_zip, img_file_name, prefix + "partition-table.img")
- common.ZipWrite(output_zip, bpt_file_name, prefix + "partition-table.bpt")
+ img.Write()
+ bpt.Write()
def AddCache(output_zip, prefix="IMAGES/"):
"""Create an empty cache image and store it in output_zip."""
- prebuilt_path = os.path.join(OPTIONS.input_tmp, prefix, "cache.img")
- if os.path.exists(prebuilt_path):
+ img = OutputFile(output_zip, OPTIONS.input_tmp, prefix, "cache.img")
+ if os.path.exists(img.input_name):
print("cache.img already exists in %s, no need to rebuild..." % (prefix,))
return
@@ -362,9 +358,9 @@
# mkyaffs2image. So we create a temp dir, and within it we create an
# empty dir named "cache", and build the image from that.
temp_dir = tempfile.mkdtemp()
+ OPTIONS.tempfiles.append(temp_dir)
user_dir = os.path.join(temp_dir, "cache")
os.mkdir(user_dir)
- img = tempfile.NamedTemporaryFile()
fstab = OPTIONS.info_dict["fstab"]
if fstab:
@@ -373,35 +369,39 @@
assert succ, "build cache.img image failed"
common.CheckSize(img.name, "cache.img", OPTIONS.info_dict)
- common.ZipWrite(output_zip, img.name, prefix + "cache.img")
- img.close()
- os.rmdir(user_dir)
- os.rmdir(temp_dir)
+ img.Write()
def AddImagesToTargetFiles(filename):
- OPTIONS.input_tmp, input_zip = common.UnzipTemp(filename)
+ if os.path.isdir(filename):
+ OPTIONS.input_tmp = os.path.abspath(filename)
+ input_zip = None
+ else:
+ OPTIONS.input_tmp, input_zip = common.UnzipTemp(filename)
if not OPTIONS.add_missing:
- for n in input_zip.namelist():
- if n.startswith("IMAGES/"):
- print("target_files appears to already contain images.")
- sys.exit(1)
+ if os.path.isdir(os.path.join(OPTIONS.input_tmp, "IMAGES")):
+ print("target_files appears to already contain images.")
+ sys.exit(1)
- try:
- input_zip.getinfo("VENDOR/")
- has_vendor = True
- except KeyError:
- has_vendor = False
+ has_vendor = os.path.isdir(os.path.join(OPTIONS.input_tmp, "VENDOR"))
+ has_system_other = os.path.isdir(os.path.join(OPTIONS.input_tmp,
+ "SYSTEM_OTHER"))
- has_system_other = "SYSTEM_OTHER/" in input_zip.namelist()
+ if input_zip:
+ OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.input_tmp)
- OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.input_tmp)
-
- common.ZipClose(input_zip)
- output_zip = zipfile.ZipFile(filename, "a",
- compression=zipfile.ZIP_DEFLATED,
- allowZip64=True)
+ common.ZipClose(input_zip)
+ output_zip = zipfile.ZipFile(filename, "a",
+ compression=zipfile.ZIP_DEFLATED,
+ allowZip64=True)
+ else:
+ OPTIONS.info_dict = common.LoadInfoDict(filename, filename)
+ output_zip = None
+ images_dir = os.path.join(OPTIONS.input_tmp, "IMAGES")
+ if not os.path.isdir(images_dir):
+ os.makedirs(images_dir)
+ images_dir = None
has_recovery = (OPTIONS.info_dict.get("no_recovery") != "true")
system_root_image = (OPTIONS.info_dict.get("system_root_image", None) == "true")
@@ -422,7 +422,10 @@
boot_image = common.GetBootableImage(
"IMAGES/boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
if boot_image:
- boot_image.AddToZip(output_zip)
+ if output_zip:
+ boot_image.AddToZip(output_zip)
+ else:
+ boot_image.WriteToDir(OPTIONS.input_tmp)
recovery_image = None
if has_recovery:
@@ -438,7 +441,10 @@
recovery_image = common.GetBootableImage(
"IMAGES/recovery.img", "recovery.img", OPTIONS.input_tmp, "RECOVERY")
if recovery_image:
- recovery_image.AddToZip(output_zip)
+ if output_zip:
+ recovery_image.AddToZip(output_zip)
+ else:
+ recovery_image.WriteToDir(OPTIONS.input_tmp)
banner("recovery (two-step image)")
# The special recovery.img for two-step package use.
@@ -446,7 +452,10 @@
"IMAGES/recovery-two-step.img", "recovery-two-step.img",
OPTIONS.input_tmp, "RECOVERY", two_step_image=True)
if recovery_two_step_image:
- recovery_two_step_image.AddToZip(output_zip)
+ if output_zip:
+ recovery_two_step_image.AddToZip(output_zip)
+ else:
+ recovery_two_step_image.WriteToDir(OPTIONS.input_tmp)
banner("system")
system_img_path = AddSystem(
@@ -502,24 +511,39 @@
img_vendor_dir = os.path.join(
OPTIONS.input_tmp, "VENDOR_IMAGES")
if os.path.exists(img_radio_path):
- common.ZipWrite(output_zip, img_radio_path,
- os.path.join("IMAGES", img_name))
+ if output_zip:
+ common.ZipWrite(output_zip, img_radio_path,
+ os.path.join("IMAGES", img_name))
+ else:
+ shutil.copy(img_radio_path, prebuilt_path)
else:
for root, _, files in os.walk(img_vendor_dir):
if img_name in files:
- common.ZipWrite(output_zip, os.path.join(root, img_name),
- os.path.join("IMAGES", img_name))
+ if output_zip:
+ common.ZipWrite(output_zip, os.path.join(root, img_name),
+ os.path.join("IMAGES", img_name))
+ else:
+ shutil.copy(os.path.join(root, img_name), prebuilt_path)
break
- # Zip spec says: All slashes MUST be forward slashes.
- img_path = 'IMAGES/' + img_name
- assert img_path in output_zip.namelist(), "cannot find " + img_name
+ if output_zip:
+ # Zip spec says: All slashes MUST be forward slashes.
+ img_path = 'IMAGES/' + img_name
+ assert img_path in output_zip.namelist(), "cannot find " + img_name
+ else:
+ img_path = os.path.join(OPTIONS.input_tmp, "IMAGES", img_name)
+ assert os.path.exists(img_path), "cannot find " + img_name
if care_map_list:
file_path = "META/care_map.txt"
- common.ZipWriteStr(output_zip, file_path, '\n'.join(care_map_list))
+ if output_zip:
+ common.ZipWriteStr(output_zip, file_path, '\n'.join(care_map_list))
+ else:
+ with open(os.path.join(OPTIONS.input_tmp, file_path), 'w') as fp:
+ fp.write('\n'.join(care_map_list))
- common.ZipClose(output_zip)
+ if output_zip:
+ common.ZipClose(output_zip)
def main(argv):
def option_handler(o, a):
diff --git a/tools/releasetools/blockimgdiff.py b/tools/releasetools/blockimgdiff.py
index c204c90..6c842dc 100644
--- a/tools/releasetools/blockimgdiff.py
+++ b/tools/releasetools/blockimgdiff.py
@@ -24,8 +24,8 @@
import os.path
import re
import subprocess
+import sys
import threading
-import tempfile
from collections import deque, OrderedDict
from hashlib import sha1
@@ -35,69 +35,67 @@
__all__ = ["EmptyImage", "DataImage", "BlockImageDiff"]
-def compute_patch(src, tgt, imgdiff=False):
- srcfd, srcfile = tempfile.mkstemp(prefix="src-")
- tgtfd, tgtfile = tempfile.mkstemp(prefix="tgt-")
- patchfd, patchfile = tempfile.mkstemp(prefix="patch-")
- os.close(patchfd)
+def compute_patch(srcfile, tgtfile, imgdiff=False):
+ patchfile = common.MakeTempFile(prefix="patch-")
- try:
- with os.fdopen(srcfd, "wb") as f_src:
- for p in src:
- f_src.write(p)
+ if imgdiff:
+ p = subprocess.call(
+ ["imgdiff", "-z", srcfile, tgtfile, patchfile],
+ stdout=open(os.devnull, 'w'),
+ stderr=subprocess.STDOUT)
+ else:
+ p = subprocess.call(
+ ["bsdiff", srcfile, tgtfile, patchfile],
+ stdout=open(os.devnull, 'w'),
+ stderr=subprocess.STDOUT)
- with os.fdopen(tgtfd, "wb") as f_tgt:
- for p in tgt:
- f_tgt.write(p)
- try:
- os.unlink(patchfile)
- except OSError:
- pass
- if imgdiff:
- p = subprocess.call(["imgdiff", "-z", srcfile, tgtfile, patchfile],
- stdout=open("/dev/null", "a"),
- stderr=subprocess.STDOUT)
- else:
- p = subprocess.call(["bsdiff", srcfile, tgtfile, patchfile])
+ if p:
+ raise ValueError("diff failed: " + str(p))
- if p:
- raise ValueError("diff failed: " + str(p))
-
- with open(patchfile, "rb") as f:
- return f.read()
- finally:
- try:
- os.unlink(srcfile)
- os.unlink(tgtfile)
- os.unlink(patchfile)
- except OSError:
- pass
+ with open(patchfile, "rb") as f:
+ return f.read()
class Image(object):
+ def RangeSha1(self, ranges):
+ raise NotImplementedError
+
def ReadRangeSet(self, ranges):
raise NotImplementedError
def TotalSha1(self, include_clobbered_blocks=False):
raise NotImplementedError
+ def WriteRangeDataToFd(self, ranges, fd):
+ raise NotImplementedError
+
class EmptyImage(Image):
"""A zero-length image."""
- blocksize = 4096
- care_map = RangeSet()
- clobbered_blocks = RangeSet()
- extended = RangeSet()
- total_blocks = 0
- file_map = {}
+
+ def __init__(self):
+ self.blocksize = 4096
+ self.care_map = RangeSet()
+ self.clobbered_blocks = RangeSet()
+ self.extended = RangeSet()
+ self.total_blocks = 0
+ self.file_map = {}
+
+ def RangeSha1(self, ranges):
+ return sha1().hexdigest()
+
def ReadRangeSet(self, ranges):
return ()
+
def TotalSha1(self, include_clobbered_blocks=False):
# EmptyImage always carries empty clobbered_blocks, so
# include_clobbered_blocks can be ignored.
assert self.clobbered_blocks.size() == 0
return sha1().hexdigest()
+ def WriteRangeDataToFd(self, ranges, fd):
+ raise ValueError("Can't write data from EmptyImage to file")
+
class DataImage(Image):
"""An image wrapped around a single string of data."""
@@ -160,23 +158,39 @@
if clobbered_blocks:
self.file_map["__COPY"] = RangeSet(data=clobbered_blocks)
+ def _GetRangeData(self, ranges):
+ for s, e in ranges:
+ yield self.data[s*self.blocksize:e*self.blocksize]
+
+ def RangeSha1(self, ranges):
+ h = sha1()
+ for data in self._GetRangeData(ranges):
+ h.update(data)
+ return h.hexdigest()
+
def ReadRangeSet(self, ranges):
- return [self.data[s*self.blocksize:e*self.blocksize] for (s, e) in ranges]
+ return [self._GetRangeData(ranges)]
def TotalSha1(self, include_clobbered_blocks=False):
if not include_clobbered_blocks:
- ranges = self.care_map.subtract(self.clobbered_blocks)
- return sha1(self.ReadRangeSet(ranges)).hexdigest()
+ return self.RangeSha1(self.care_map.subtract(self.clobbered_blocks))
else:
return sha1(self.data).hexdigest()
+ def WriteRangeDataToFd(self, ranges, fd):
+ for data in self._GetRangeData(ranges):
+ fd.write(data)
+
class Transfer(object):
- def __init__(self, tgt_name, src_name, tgt_ranges, src_ranges, style, by_id):
+ def __init__(self, tgt_name, src_name, tgt_ranges, src_ranges, tgt_sha1,
+ src_sha1, style, by_id):
self.tgt_name = tgt_name
self.src_name = src_name
self.tgt_ranges = tgt_ranges
self.src_ranges = src_ranges
+ self.tgt_sha1 = tgt_sha1
+ self.src_sha1 = src_sha1
self.style = style
self.intact = (getattr(tgt_ranges, "monotonic", False) and
getattr(src_ranges, "monotonic", False))
@@ -251,6 +265,9 @@
# Implementations are free to break up the data into list/tuple
# elements in any way that is convenient.
#
+# RangeSha1(): a function that returns (as a hex string) the SHA-1
+# hash of all the data in the specified range.
+#
# TotalSha1(): a function that returns (as a hex string) the SHA-1
# hash of all the data in the image (ie, all the blocks in the
# care_map minus clobbered_blocks, or including the clobbered
@@ -332,15 +349,6 @@
self.ComputePatches(prefix)
self.WriteTransfers(prefix)
- def HashBlocks(self, source, ranges): # pylint: disable=no-self-use
- data = source.ReadRangeSet(ranges)
- ctx = sha1()
-
- for p in data:
- ctx.update(p)
-
- return ctx.hexdigest()
-
def WriteTransfers(self, prefix):
def WriteSplitTransfers(out, style, target_blocks):
"""Limit the size of operand in command 'new' and 'zero' to 1024 blocks.
@@ -397,7 +405,7 @@
stashed_blocks += sr.size()
out.append("stash %d %s\n" % (sid, sr.to_string_raw()))
else:
- sh = self.HashBlocks(self.src, sr)
+ sh = self.src.RangeSha1(sr)
if sh in stashes:
stashes[sh] += 1
else:
@@ -429,7 +437,7 @@
mapped_stashes = []
for stash_raw_id, sr in xf.use_stash:
unstashed_src_ranges = unstashed_src_ranges.subtract(sr)
- sh = self.HashBlocks(self.src, sr)
+ sh = self.src.RangeSha1(sr)
sr = xf.src_ranges.map_within(sr)
mapped_stashes.append(sr)
if self.version == 2:
@@ -515,7 +523,7 @@
out.append("%s %s %s %s\n" % (
xf.style,
- self.HashBlocks(self.tgt, xf.tgt_ranges),
+ xf.tgt_sha1,
xf.tgt_ranges.to_string_raw(), src_str))
total += tgt_size
elif xf.style in ("bsdiff", "imgdiff"):
@@ -542,8 +550,8 @@
out.append("%s %d %d %s %s %s %s\n" % (
xf.style,
xf.patch_start, xf.patch_len,
- self.HashBlocks(self.src, xf.src_ranges),
- self.HashBlocks(self.tgt, xf.tgt_ranges),
+ xf.src_sha1,
+ xf.tgt_sha1,
xf.tgt_ranges.to_string_raw(), src_str))
total += tgt_size
elif xf.style == "zero":
@@ -574,8 +582,7 @@
stash_threshold)
if self.version >= 3:
- self.touched_src_sha1 = self.HashBlocks(
- self.src, self.touched_src_ranges)
+ self.touched_src_sha1 = self.src.RangeSha1(self.touched_src_ranges)
# Zero out extended blocks as a workaround for bug 20881595.
if self.tgt.extended:
@@ -674,7 +681,7 @@
if self.version == 2:
stashed_blocks_after += sr.size()
else:
- sh = self.HashBlocks(self.src, sr)
+ sh = self.src.RangeSha1(sr)
if sh not in stashes:
stashed_blocks_after += sr.size()
@@ -731,7 +738,7 @@
stashed_blocks -= sr.size()
heapq.heappush(free_stash_ids, sid)
else:
- sh = self.HashBlocks(self.src, sr)
+ sh = self.src.RangeSha1(sr)
assert sh in stashes
stashes[sh] -= 1
if stashes[sh] == 0:
@@ -745,10 +752,10 @@
def ComputePatches(self, prefix):
print("Reticulating splines...")
- diff_q = []
+ diff_queue = []
patch_num = 0
with open(prefix + ".new.dat", "wb") as new_f:
- for xf in self.transfers:
+ for index, xf in enumerate(self.transfers):
if xf.style == "zero":
tgt_size = xf.tgt_ranges.size() * self.tgt.blocksize
print("%10d %10d (%6.2f%%) %7s %s %s" % (
@@ -756,17 +763,13 @@
str(xf.tgt_ranges)))
elif xf.style == "new":
- for piece in self.tgt.ReadRangeSet(xf.tgt_ranges):
- new_f.write(piece)
+ self.tgt.WriteRangeDataToFd(xf.tgt_ranges, new_f)
tgt_size = xf.tgt_ranges.size() * self.tgt.blocksize
print("%10d %10d (%6.2f%%) %7s %s %s" % (
tgt_size, tgt_size, 100.0, xf.style,
xf.tgt_name, str(xf.tgt_ranges)))
elif xf.style == "diff":
- src = self.src.ReadRangeSet(xf.src_ranges)
- tgt = self.tgt.ReadRangeSet(xf.tgt_ranges)
-
# We can't compare src and tgt directly because they may have
# the same content but be broken up into blocks differently, eg:
#
@@ -775,20 +778,11 @@
# We want those to compare equal, ideally without having to
# actually concatenate the strings (these may be tens of
# megabytes).
-
- src_sha1 = sha1()
- for p in src:
- src_sha1.update(p)
- tgt_sha1 = sha1()
- tgt_size = 0
- for p in tgt:
- tgt_sha1.update(p)
- tgt_size += len(p)
-
- if src_sha1.digest() == tgt_sha1.digest():
+ if xf.src_sha1 == xf.tgt_sha1:
# These are identical; we don't need to generate a patch,
# just issue copy commands on the device.
xf.style = "move"
+ tgt_size = xf.tgt_ranges.size() * self.tgt.blocksize
if xf.src_ranges != xf.tgt_ranges:
print("%10d %10d (%6.2f%%) %7s %s %s (from %s)" % (
tgt_size, tgt_size, 100.0, xf.style,
@@ -815,38 +809,64 @@
xf.tgt_name.split(".")[-1].lower()
in ("apk", "jar", "zip"))
xf.style = "imgdiff" if imgdiff else "bsdiff"
- diff_q.append((tgt_size, src, tgt, xf, patch_num))
+ diff_queue.append((index, imgdiff, patch_num))
patch_num += 1
else:
assert False, "unknown style " + xf.style
- if diff_q:
+ if diff_queue:
if self.threads > 1:
print("Computing patches (using %d threads)..." % (self.threads,))
else:
print("Computing patches...")
- diff_q.sort()
- patches = [None] * patch_num
+ diff_total = len(diff_queue)
+ patches = [None] * diff_total
- # TODO: Rewrite with multiprocessing.ThreadPool?
+ # Using multiprocessing doesn't give additional benefits, due to the
+ # pattern of the code. The diffing work is done by subprocess.call, which
+ # already runs in a separate process (not affected much by the GIL -
+ # Global Interpreter Lock). Using multiprocess also requires either a)
+ # writing the diff input files in the main process before forking, or b)
+ # reopening the image file (SparseImage) in the worker processes. Doing
+ # neither of them further improves the performance.
lock = threading.Lock()
def diff_worker():
while True:
with lock:
- if not diff_q:
+ if not diff_queue:
return
- tgt_size, src, tgt, xf, patchnum = diff_q.pop()
- patch = compute_patch(src, tgt, imgdiff=(xf.style == "imgdiff"))
- size = len(patch)
+ xf_index, imgdiff, patch_index = diff_queue.pop()
+
+ xf = self.transfers[xf_index]
+ src_ranges = xf.src_ranges
+ tgt_ranges = xf.tgt_ranges
+
+ # Needs lock since WriteRangeDataToFd() is stateful (calling seek).
with lock:
- patches[patchnum] = (patch, xf)
- print("%10d %10d (%6.2f%%) %7s %s %s %s" % (
- size, tgt_size, size * 100.0 / tgt_size, xf.style,
- xf.tgt_name if xf.tgt_name == xf.src_name else (
- xf.tgt_name + " (from " + xf.src_name + ")"),
- str(xf.tgt_ranges), str(xf.src_ranges)))
+ src_file = common.MakeTempFile(prefix="src-")
+ with open(src_file, "wb") as fd:
+ self.src.WriteRangeDataToFd(src_ranges, fd)
+
+ tgt_file = common.MakeTempFile(prefix="tgt-")
+ with open(tgt_file, "wb") as fd:
+ self.tgt.WriteRangeDataToFd(tgt_ranges, fd)
+
+ try:
+ patch = compute_patch(src_file, tgt_file, imgdiff)
+ except ValueError as e:
+ raise ValueError(
+ "Failed to generate diff for %s: src=%s, tgt=%s: %s" % (
+ xf.tgt_name, xf.src_ranges, xf.tgt_ranges, e.message))
+
+ with lock:
+ patches[patch_index] = (xf_index, patch)
+ if sys.stdout.isatty():
+ progress = len(patches) * 100 / diff_total
+ # '\033[K' is to clear to EOL.
+ print(' [%d%%] %s\033[K' % (progress, xf.tgt_name), end='\r')
+ sys.stdout.flush()
threads = [threading.Thread(target=diff_worker)
for _ in range(self.threads)]
@@ -854,16 +874,29 @@
th.start()
while threads:
threads.pop().join()
+
+ if sys.stdout.isatty():
+ print('\n')
else:
patches = []
- p = 0
- with open(prefix + ".patch.dat", "wb") as patch_f:
- for patch, xf in patches:
- xf.patch_start = p
+ offset = 0
+ with open(prefix + ".patch.dat", "wb") as patch_fd:
+ for index, patch in patches:
+ xf = self.transfers[index]
xf.patch_len = len(patch)
- patch_f.write(patch)
- p += len(patch)
+ xf.patch_start = offset
+ offset += xf.patch_len
+ patch_fd.write(patch)
+
+ if common.OPTIONS.verbose:
+ tgt_size = xf.tgt_ranges.size() * self.tgt.blocksize
+ print("%10d %10d (%6.2f%%) %7s %s %s %s" % (
+ xf.patch_len, tgt_size, xf.patch_len * 100.0 / tgt_size,
+ xf.style,
+ xf.tgt_name if xf.tgt_name == xf.src_name else (
+ xf.tgt_name + " (from " + xf.src_name + ")"),
+ xf.tgt_ranges, xf.src_ranges))
def AssertSequenceGood(self):
# Simulate the sequences of transfers we will output, and check that:
@@ -1211,7 +1244,9 @@
# Change nothing for small files.
if (tgt_ranges.size() <= max_blocks_per_transfer and
src_ranges.size() <= max_blocks_per_transfer):
- Transfer(tgt_name, src_name, tgt_ranges, src_ranges, style, by_id)
+ Transfer(tgt_name, src_name, tgt_ranges, src_ranges,
+ self.tgt.RangeSha1(tgt_ranges), self.src.RangeSha1(src_ranges),
+ style, by_id)
return
while (tgt_ranges.size() > max_blocks_per_transfer and
@@ -1221,8 +1256,9 @@
tgt_first = tgt_ranges.first(max_blocks_per_transfer)
src_first = src_ranges.first(max_blocks_per_transfer)
- Transfer(tgt_split_name, src_split_name, tgt_first, src_first, style,
- by_id)
+ Transfer(tgt_split_name, src_split_name, tgt_first, src_first,
+ self.tgt.RangeSha1(tgt_first), self.src.RangeSha1(src_first),
+ style, by_id)
tgt_ranges = tgt_ranges.subtract(tgt_first)
src_ranges = src_ranges.subtract(src_first)
@@ -1234,8 +1270,9 @@
assert tgt_ranges.size() and src_ranges.size()
tgt_split_name = "%s-%d" % (tgt_name, pieces)
src_split_name = "%s-%d" % (src_name, pieces)
- Transfer(tgt_split_name, src_split_name, tgt_ranges, src_ranges, style,
- by_id)
+ Transfer(tgt_split_name, src_split_name, tgt_ranges, src_ranges,
+ self.tgt.RangeSha1(tgt_ranges), self.src.RangeSha1(src_ranges),
+ style, by_id)
def AddTransfer(tgt_name, src_name, tgt_ranges, src_ranges, style, by_id,
split=False):
@@ -1244,7 +1281,9 @@
# We specialize diff transfers only (which covers bsdiff/imgdiff/move);
# otherwise add the Transfer() as is.
if style != "diff" or not split:
- Transfer(tgt_name, src_name, tgt_ranges, src_ranges, style, by_id)
+ Transfer(tgt_name, src_name, tgt_ranges, src_ranges,
+ self.tgt.RangeSha1(tgt_ranges), self.src.RangeSha1(src_ranges),
+ style, by_id)
return
# Handle .odex files specially to analyze the block-wise difference. If
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index 2b97518..3224333 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -1191,6 +1191,10 @@
t.flush()
return t
+ def WriteToDir(self, d):
+ with open(os.path.join(d, self.name), "wb") as fp:
+ fp.write(self.data)
+
def AddToZip(self, z, compression=None):
ZipWriteStr(z, self.name, self.data, compress_type=compression)
diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py
index 277d633..7e970a9 100755
--- a/tools/releasetools/ota_from_target_files.py
+++ b/tools/releasetools/ota_from_target_files.py
@@ -94,9 +94,10 @@
using the new recovery (new kernel, etc.).
--block
- Generate a block-based OTA if possible. Will fall back to a
- file-based OTA if the target_files is older and doesn't support
- block-based OTAs.
+ Generate a block-based OTA for non-A/B device. We have deprecated the
+ support for file-based OTA since O. Block-based OTA will be used by
+ default for all non-A/B devices. Keeping this flag here to not break
+ existing callers.
-b (--binary) <file>
Use the given binary as the update-binary in the output package,
@@ -155,8 +156,6 @@
OPTIONS.package_key = None
OPTIONS.incremental_source = None
OPTIONS.verify = False
-OPTIONS.require_verbatim = set()
-OPTIONS.prohibit_verbatim = set(("system/build.prop",))
OPTIONS.patch_threshold = 0.95
OPTIONS.wipe_user_data = False
OPTIONS.downgrade = False
@@ -167,7 +166,7 @@
OPTIONS.worker_threads = 1
OPTIONS.two_step = False
OPTIONS.no_signing = False
-OPTIONS.block_based = False
+OPTIONS.block_based = True
OPTIONS.updater_binary = None
OPTIONS.oem_source = None
OPTIONS.oem_no_mount = False
@@ -186,273 +185,6 @@
UNZIP_PATTERN = ['IMAGES/*', 'META/*']
-def MostPopularKey(d, default):
- """Given a dict, return the key corresponding to the largest
- value. Returns 'default' if the dict is empty."""
- x = [(v, k) for (k, v) in d.iteritems()]
- if not x:
- return default
- x.sort()
- return x[-1][1]
-
-
-def IsSymlink(info):
- """Return true if the zipfile.ZipInfo object passed in represents a
- symlink."""
- return (info.external_attr >> 16) & 0o770000 == 0o120000
-
-def IsRegular(info):
- """Return true if the zipfile.ZipInfo object passed in represents a
- regular file."""
- return (info.external_attr >> 16) & 0o770000 == 0o100000
-
-def ClosestFileMatch(src, tgtfiles, existing):
- """Returns the closest file match between a source file and list
- of potential matches. The exact filename match is preferred,
- then the sha1 is searched for, and finally a file with the same
- basename is evaluated. Rename support in the updater-binary is
- required for the latter checks to be used."""
-
- result = tgtfiles.get("path:" + src.name)
- if result is not None:
- return result
-
- if not OPTIONS.target_info_dict.get("update_rename_support", False):
- return None
-
- if src.size < 1000:
- return None
-
- result = tgtfiles.get("sha1:" + src.sha1)
- if result is not None and existing.get(result.name) is None:
- return result
- result = tgtfiles.get("file:" + src.name.split("/")[-1])
- if result is not None and existing.get(result.name) is None:
- return result
- return None
-
-class ItemSet(object):
- def __init__(self, partition, fs_config):
- self.partition = partition
- self.fs_config = fs_config
- self.ITEMS = {}
-
- def Get(self, name, is_dir=False):
- if name not in self.ITEMS:
- self.ITEMS[name] = Item(self, name, is_dir=is_dir)
- return self.ITEMS[name]
-
- def GetMetadata(self, input_zip):
- # The target_files contains a record of what the uid,
- # gid, and mode are supposed to be.
- output = input_zip.read(self.fs_config)
-
- for line in output.split("\n"):
- if not line:
- continue
- columns = line.split()
- name, uid, gid, mode = columns[:4]
- selabel = None
- capabilities = None
-
- # After the first 4 columns, there are a series of key=value
- # pairs. Extract out the fields we care about.
- for element in columns[4:]:
- key, value = element.split("=")
- if key == "selabel":
- selabel = value
- if key == "capabilities":
- capabilities = value
-
- i = self.ITEMS.get(name, None)
- if i is not None:
- i.uid = int(uid)
- i.gid = int(gid)
- i.mode = int(mode, 8)
- i.selabel = selabel
- i.capabilities = capabilities
- if i.is_dir:
- i.children.sort(key=lambda i: i.name)
-
- # Set metadata for the files generated by this script. For full recovery
- # image at system/etc/recovery.img, it will be taken care by fs_config.
- i = self.ITEMS.get("system/recovery-from-boot.p", None)
- if i:
- i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0o644, None, None
- i = self.ITEMS.get("system/etc/install-recovery.sh", None)
- if i:
- i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0o544, None, None
-
-
-class Item(object):
- """Items represent the metadata (user, group, mode) of files and
- directories in the system image."""
- def __init__(self, itemset, name, is_dir=False):
- self.itemset = itemset
- self.name = name
- self.uid = None
- self.gid = None
- self.mode = None
- self.selabel = None
- self.capabilities = None
- self.is_dir = is_dir
- self.descendants = None
- self.best_subtree = None
-
- if name:
- self.parent = itemset.Get(os.path.dirname(name), is_dir=True)
- self.parent.children.append(self)
- else:
- self.parent = None
- if self.is_dir:
- self.children = []
-
- def Dump(self, indent=0):
- if self.uid is not None:
- print("%s%s %d %d %o" % (
- " " * indent, self.name, self.uid, self.gid, self.mode))
- else:
- print("%s%s %s %s %s" % (
- " " * indent, self.name, self.uid, self.gid, self.mode))
- if self.is_dir:
- print("%s%s" % (" " * indent, self.descendants))
- print("%s%s" % (" " * indent, self.best_subtree))
- for i in self.children:
- i.Dump(indent=indent+1)
-
- def CountChildMetadata(self):
- """Count up the (uid, gid, mode, selabel, capabilities) tuples for
- all children and determine the best strategy for using set_perm_recursive
- and set_perm to correctly chown/chmod all the files to their desired
- values. Recursively calls itself for all descendants.
-
- Returns a dict of {(uid, gid, dmode, fmode, selabel, capabilities): count}
- counting up all descendants of this node. (dmode or fmode may be None.)
- Also sets the best_subtree of each directory Item to the (uid, gid, dmode,
- fmode, selabel, capabilities) tuple that will match the most descendants of
- that Item.
- """
-
- assert self.is_dir
- key = (self.uid, self.gid, self.mode, None, self.selabel,
- self.capabilities)
- self.descendants = {key: 1}
- d = self.descendants
- for i in self.children:
- if i.is_dir:
- for k, v in i.CountChildMetadata().iteritems():
- d[k] = d.get(k, 0) + v
- else:
- k = (i.uid, i.gid, None, i.mode, i.selabel, i.capabilities)
- d[k] = d.get(k, 0) + 1
-
- # Find the (uid, gid, dmode, fmode, selabel, capabilities)
- # tuple that matches the most descendants.
-
- # First, find the (uid, gid) pair that matches the most
- # descendants.
- ug = {}
- for (uid, gid, _, _, _, _), count in d.iteritems():
- ug[(uid, gid)] = ug.get((uid, gid), 0) + count
- ug = MostPopularKey(ug, (0, 0))
-
- # Now find the dmode, fmode, selabel, and capabilities that match
- # the most descendants with that (uid, gid), and choose those.
- best_dmode = (0, 0o755)
- best_fmode = (0, 0o644)
- best_selabel = (0, None)
- best_capabilities = (0, None)
- for k, count in d.iteritems():
- if k[:2] != ug:
- continue
- if k[2] is not None and count >= best_dmode[0]:
- best_dmode = (count, k[2])
- if k[3] is not None and count >= best_fmode[0]:
- best_fmode = (count, k[3])
- if k[4] is not None and count >= best_selabel[0]:
- best_selabel = (count, k[4])
- if k[5] is not None and count >= best_capabilities[0]:
- best_capabilities = (count, k[5])
- self.best_subtree = ug + (
- best_dmode[1], best_fmode[1], best_selabel[1], best_capabilities[1])
-
- return d
-
- def SetPermissions(self, script):
- """Append set_perm/set_perm_recursive commands to 'script' to
- set all permissions, users, and groups for the tree of files
- rooted at 'self'."""
-
- self.CountChildMetadata()
-
- def recurse(item, current):
- # current is the (uid, gid, dmode, fmode, selabel, capabilities) tuple
- # that the current item (and all its children) have already been set to.
- # We only need to issue set_perm/set_perm_recursive commands if we're
- # supposed to be something different.
- if item.is_dir:
- if current != item.best_subtree:
- script.SetPermissionsRecursive("/"+item.name, *item.best_subtree)
- current = item.best_subtree
-
- if item.uid != current[0] or item.gid != current[1] or \
- item.mode != current[2] or item.selabel != current[4] or \
- item.capabilities != current[5]:
- script.SetPermissions("/"+item.name, item.uid, item.gid,
- item.mode, item.selabel, item.capabilities)
-
- for i in item.children:
- recurse(i, current)
- else:
- if item.uid != current[0] or item.gid != current[1] or \
- item.mode != current[3] or item.selabel != current[4] or \
- item.capabilities != current[5]:
- script.SetPermissions("/"+item.name, item.uid, item.gid,
- item.mode, item.selabel, item.capabilities)
-
- recurse(self, (-1, -1, -1, -1, None, None))
-
-
-def CopyPartitionFiles(itemset, input_zip, output_zip=None, substitute=None):
- """Copies files for the partition in the input zip to the output
- zip. Populates the Item class with their metadata, and returns a
- list of symlinks. output_zip may be None, in which case the copy is
- skipped (but the other side effects still happen). substitute is an
- optional dict of {output filename: contents} to be output instead of
- certain input files.
- """
-
- symlinks = []
-
- partition = itemset.partition
-
- for info in input_zip.infolist():
- prefix = partition.upper() + "/"
- if info.filename.startswith(prefix):
- basefilename = info.filename[len(prefix):]
- if IsSymlink(info):
- symlinks.append((input_zip.read(info.filename),
- "/" + partition + "/" + basefilename))
- else:
- info2 = copy.copy(info)
- fn = info2.filename = partition + "/" + basefilename
- if substitute and fn in substitute and substitute[fn] is None:
- continue
- if output_zip is not None:
- if substitute and fn in substitute:
- data = substitute[fn]
- else:
- data = input_zip.read(info.filename)
- common.ZipWriteStr(output_zip, info2, data)
- if fn.endswith("/"):
- itemset.Get(fn[:-1], is_dir=True)
- else:
- itemset.Get(fn)
-
- symlinks.sort()
- return symlinks
-
-
def SignOutput(temp_zip_name, output_zip_name):
key_passwords = common.GetKeyPasswords([OPTIONS.package_key])
pw = key_passwords[OPTIONS.package_key]
@@ -533,6 +265,7 @@
return ("SYSTEM/recovery-from-boot.p" in namelist or
"SYSTEM/etc/recovery.img" in namelist)
+
def HasVendorPartition(target_files_zip):
try:
target_files_zip.getinfo("VENDOR/")
@@ -540,6 +273,7 @@
except KeyError:
return False
+
def GetOemProperty(name, oem_props, oem_dict, info_dict):
if oem_props is not None and name in oem_props:
return oem_dict[name]
@@ -612,10 +346,9 @@
metadata=metadata,
info_dict=OPTIONS.info_dict)
- has_recovery_patch = HasRecoveryPatch(input_zip)
- block_based = OPTIONS.block_based and has_recovery_patch
+ assert HasRecoveryPatch(input_zip)
- metadata["ota-type"] = "BLOCK" if block_based else "FILE"
+ metadata["ota-type"] = "BLOCK"
ts = GetBuildProp("ro.build.date.utc", OPTIONS.info_dict)
ts_text = GetBuildProp("ro.build.date", OPTIONS.info_dict)
@@ -689,61 +422,27 @@
recovery_mount_options = OPTIONS.info_dict.get("recovery_mount_options")
- system_items = ItemSet("system", "META/filesystem_config.txt")
script.ShowProgress(system_progress, 0)
- if block_based:
- # Full OTA is done as an "incremental" against an empty source
- # image. This has the effect of writing new data from the package
- # to the entire partition, but lets us reuse the updater code that
- # writes incrementals to do it.
- system_tgt = GetImage("system", OPTIONS.input_tmp)
- system_tgt.ResetFileMap()
- system_diff = common.BlockDifference("system", system_tgt, src=None)
- system_diff.WriteScript(script, output_zip)
- else:
- script.FormatPartition("/system")
- script.Mount("/system", recovery_mount_options)
- if not has_recovery_patch:
- script.UnpackPackageDir("recovery", "/system")
- script.UnpackPackageDir("system", "/system")
-
- symlinks = CopyPartitionFiles(system_items, input_zip, output_zip)
- script.MakeSymlinks(symlinks)
+ # Full OTA is done as an "incremental" against an empty source image. This
+ # has the effect of writing new data from the package to the entire
+ # partition, but lets us reuse the updater code that writes incrementals to
+ # do it.
+ system_tgt = GetImage("system", OPTIONS.input_tmp)
+ system_tgt.ResetFileMap()
+ system_diff = common.BlockDifference("system", system_tgt, src=None)
+ system_diff.WriteScript(script, output_zip)
boot_img = common.GetBootableImage(
"boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
- if not block_based:
- def output_sink(fn, data):
- common.ZipWriteStr(output_zip, "recovery/" + fn, data)
- system_items.Get("system/" + fn)
-
- common.MakeRecoveryPatch(OPTIONS.input_tmp, output_sink,
- recovery_img, boot_img)
-
- system_items.GetMetadata(input_zip)
- system_items.Get("system").SetPermissions(script)
-
if HasVendorPartition(input_zip):
- vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
script.ShowProgress(0.1, 0)
- if block_based:
- vendor_tgt = GetImage("vendor", OPTIONS.input_tmp)
- vendor_tgt.ResetFileMap()
- vendor_diff = common.BlockDifference("vendor", vendor_tgt)
- vendor_diff.WriteScript(script, output_zip)
- else:
- script.FormatPartition("/vendor")
- script.Mount("/vendor", recovery_mount_options)
- script.UnpackPackageDir("vendor", "/vendor")
-
- symlinks = CopyPartitionFiles(vendor_items, input_zip, output_zip)
- script.MakeSymlinks(symlinks)
-
- vendor_items.GetMetadata(input_zip)
- vendor_items.Get("vendor").SetPermissions(script)
+ vendor_tgt = GetImage("vendor", OPTIONS.input_tmp)
+ vendor_tgt.ResetFileMap()
+ vendor_diff = common.BlockDifference("vendor", vendor_tgt)
+ vendor_diff.WriteScript(script, output_zip)
common.CheckSize(boot_img.data, "boot.img", OPTIONS.info_dict)
common.ZipWriteStr(output_zip, "boot.img", boot_img.data)
@@ -796,20 +495,6 @@
compress_type=zipfile.ZIP_STORED)
-def LoadPartitionFiles(z, partition):
- """Load all the files from the given partition in a given target-files
- ZipFile, and return a dict of {filename: File object}."""
- out = {}
- prefix = partition.upper() + "/"
- for info in z.infolist():
- if info.filename.startswith(prefix) and not IsSymlink(info):
- basefilename = info.filename[len(prefix):]
- fn = partition + "/" + basefilename
- data = z.read(info.filename)
- out[fn] = common.File(fn, data, info.compress_size)
- return out
-
-
def GetBuildProp(prop, info_dict):
"""Return the fingerprint of the build of a given target-files info_dict."""
try:
@@ -818,18 +503,6 @@
raise common.ExternalError("couldn't find %s in build.prop" % (prop,))
-def AddToKnownPaths(filename, known_paths):
- if filename[-1] == "/":
- return
- dirs = filename.split("/")[:-1]
- while len(dirs) > 0:
- path = "/".join(dirs)
- if path in known_paths:
- break
- known_paths.add(path)
- dirs.pop()
-
-
def HandleDowngradeMetadata(metadata):
# Only incremental OTAs are allowed to reach here.
assert OPTIONS.incremental_source is not None
@@ -858,8 +531,6 @@
def WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip):
- # TODO(tbao): We should factor out the common parts between
- # WriteBlockIncrementalOTAPackage() and WriteIncrementalOTAPackage().
source_version = OPTIONS.source_info_dict["recovery_api_version"]
target_version = OPTIONS.target_info_dict["recovery_api_version"]
@@ -1505,556 +1176,6 @@
common.ZipClose(output_zip)
-class FileDifference(object):
- def __init__(self, partition, source_zip, target_zip, output_zip):
- self.deferred_patch_list = None
- print("Loading target...")
- self.target_data = target_data = LoadPartitionFiles(target_zip, partition)
- print("Loading source...")
- self.source_data = source_data = LoadPartitionFiles(source_zip, partition)
-
- self.verbatim_targets = verbatim_targets = []
- self.patch_list = patch_list = []
- diffs = []
- self.renames = renames = {}
- known_paths = set()
- largest_source_size = 0
-
- matching_file_cache = {}
- for fn, sf in source_data.items():
- assert fn == sf.name
- matching_file_cache["path:" + fn] = sf
- if fn in target_data.keys():
- AddToKnownPaths(fn, known_paths)
- # Only allow eligibility for filename/sha matching
- # if there isn't a perfect path match.
- if target_data.get(sf.name) is None:
- matching_file_cache["file:" + fn.split("/")[-1]] = sf
- matching_file_cache["sha:" + sf.sha1] = sf
-
- for fn in sorted(target_data.keys()):
- tf = target_data[fn]
- assert fn == tf.name
- sf = ClosestFileMatch(tf, matching_file_cache, renames)
- if sf is not None and sf.name != tf.name:
- print("File has moved from " + sf.name + " to " + tf.name)
- renames[sf.name] = tf
-
- if sf is None or fn in OPTIONS.require_verbatim:
- # This file should be included verbatim
- if fn in OPTIONS.prohibit_verbatim:
- raise common.ExternalError("\"%s\" must be sent verbatim" % (fn,))
- print("send", fn, "verbatim")
- tf.AddToZip(output_zip)
- verbatim_targets.append((fn, tf.size, tf.sha1))
- if fn in target_data.keys():
- AddToKnownPaths(fn, known_paths)
- elif tf.sha1 != sf.sha1:
- # File is different; consider sending as a patch
- diffs.append(common.Difference(tf, sf))
- else:
- # Target file data identical to source (may still be renamed)
- pass
-
- common.ComputeDifferences(diffs)
-
- for diff in diffs:
- tf, sf, d = diff.GetPatch()
- path = "/".join(tf.name.split("/")[:-1])
- if d is None or len(d) > tf.compress_size * OPTIONS.patch_threshold or \
- path not in known_paths:
- # patch is almost as big as the file; don't bother patching
- # or a patch + rename cannot take place due to the target
- # directory not existing
- tf.AddToZip(output_zip)
- verbatim_targets.append((tf.name, tf.size, tf.sha1))
- if sf.name in renames:
- del renames[sf.name]
- AddToKnownPaths(tf.name, known_paths)
- else:
- common.ZipWriteStr(output_zip, "patch/" + sf.name + ".p", d)
- patch_list.append((tf, sf, tf.size, common.sha1(d).hexdigest()))
- largest_source_size = max(largest_source_size, sf.size)
-
- self.largest_source_size = largest_source_size
-
- def EmitVerification(self, script):
- so_far = 0
- for tf, sf, _, _ in self.patch_list:
- if tf.name != sf.name:
- script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
- script.PatchCheck("/"+sf.name, tf.sha1, sf.sha1)
- so_far += sf.size
- return so_far
-
- def EmitExplicitTargetVerification(self, script):
- for fn, _, sha1 in self.verbatim_targets:
- if fn[-1] != "/":
- script.FileCheck("/"+fn, sha1)
- for tf, _, _, _ in self.patch_list:
- script.FileCheck(tf.name, tf.sha1)
-
- def RemoveUnneededFiles(self, script, extras=()):
- file_list = ["/" + i[0] for i in self.verbatim_targets]
- file_list += ["/" + i for i in self.source_data
- if i not in self.target_data and i not in self.renames]
- file_list += list(extras)
- # Sort the list in descending order, which removes all the files first
- # before attempting to remove the folder. (Bug: 22960996)
- script.DeleteFiles(sorted(file_list, reverse=True))
-
- def TotalPatchSize(self):
- return sum(i[1].size for i in self.patch_list)
-
- def EmitPatches(self, script, total_patch_size, so_far):
- self.deferred_patch_list = deferred_patch_list = []
- for item in self.patch_list:
- tf, sf, _, _ = item
- if tf.name == "system/build.prop":
- deferred_patch_list.append(item)
- continue
- if sf.name != tf.name:
- script.SkipNextActionIfTargetExists(tf.name, tf.sha1)
- script.ApplyPatch("/" + sf.name, "-", tf.size, tf.sha1, sf.sha1,
- "patch/" + sf.name + ".p")
- so_far += tf.size
- script.SetProgress(so_far / total_patch_size)
- return so_far
-
- def EmitDeferredPatches(self, script):
- for item in self.deferred_patch_list:
- tf, sf, _, _ = item
- script.ApplyPatch("/"+sf.name, "-", tf.size, tf.sha1, sf.sha1,
- "patch/" + sf.name + ".p")
- script.SetPermissions("/system/build.prop", 0, 0, 0o644, None, None)
-
- def EmitRenames(self, script):
- if len(self.renames) > 0:
- script.Print("Renaming files...")
- for src, tgt in self.renames.iteritems():
- print("Renaming " + src + " to " + tgt.name)
- script.RenameFile(src, tgt.name)
-
-
-def WriteIncrementalOTAPackage(target_zip, source_zip, output_zip):
- target_has_recovery_patch = HasRecoveryPatch(target_zip)
- source_has_recovery_patch = HasRecoveryPatch(source_zip)
-
- if (OPTIONS.block_based and
- target_has_recovery_patch and
- source_has_recovery_patch):
- return WriteBlockIncrementalOTAPackage(target_zip, source_zip, output_zip)
-
- source_version = OPTIONS.source_info_dict["recovery_api_version"]
- target_version = OPTIONS.target_info_dict["recovery_api_version"]
-
- if source_version == 0:
- print("WARNING: generating edify script for a source that "
- "can't install it.")
- script = edify_generator.EdifyGenerator(
- source_version, OPTIONS.target_info_dict,
- fstab=OPTIONS.source_info_dict["fstab"])
-
- recovery_mount_options = OPTIONS.source_info_dict.get(
- "recovery_mount_options")
- source_oem_props = OPTIONS.source_info_dict.get("oem_fingerprint_properties")
- target_oem_props = OPTIONS.target_info_dict.get("oem_fingerprint_properties")
- oem_dicts = None
- if source_oem_props or target_oem_props:
- oem_dicts = _LoadOemDicts(script, recovery_mount_options)
-
- metadata = {
- "pre-device": GetOemProperty("ro.product.device", source_oem_props,
- oem_dicts and oem_dicts[0],
- OPTIONS.source_info_dict),
- "ota-type": "FILE",
- }
-
- HandleDowngradeMetadata(metadata)
-
- device_specific = common.DeviceSpecificParams(
- source_zip=source_zip,
- source_version=source_version,
- target_zip=target_zip,
- target_version=target_version,
- output_zip=output_zip,
- script=script,
- metadata=metadata,
- info_dict=OPTIONS.source_info_dict)
-
- system_diff = FileDifference("system", source_zip, target_zip, output_zip)
- script.Mount("/system", recovery_mount_options)
- if HasVendorPartition(target_zip):
- vendor_diff = FileDifference("vendor", source_zip, target_zip, output_zip)
- script.Mount("/vendor", recovery_mount_options)
- else:
- vendor_diff = None
-
- target_fp = CalculateFingerprint(target_oem_props, oem_dicts and oem_dicts[0],
- OPTIONS.target_info_dict)
- source_fp = CalculateFingerprint(source_oem_props, oem_dicts and oem_dicts[0],
- OPTIONS.source_info_dict)
-
- if source_oem_props is None and target_oem_props is None:
- script.AssertSomeFingerprint(source_fp, target_fp)
- elif source_oem_props is not None and target_oem_props is not None:
- script.AssertSomeThumbprint(
- GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict),
- GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
- elif source_oem_props is None and target_oem_props is not None:
- script.AssertFingerprintOrThumbprint(
- source_fp,
- GetBuildProp("ro.build.thumbprint", OPTIONS.target_info_dict))
- else:
- script.AssertFingerprintOrThumbprint(
- target_fp,
- GetBuildProp("ro.build.thumbprint", OPTIONS.source_info_dict))
-
- metadata["pre-build"] = source_fp
- metadata["post-build"] = target_fp
- metadata["pre-build-incremental"] = GetBuildProp(
- "ro.build.version.incremental", OPTIONS.source_info_dict)
- metadata["post-build-incremental"] = GetBuildProp(
- "ro.build.version.incremental", OPTIONS.target_info_dict)
-
- source_boot = common.GetBootableImage(
- "/tmp/boot.img", "boot.img", OPTIONS.source_tmp, "BOOT",
- OPTIONS.source_info_dict)
- target_boot = common.GetBootableImage(
- "/tmp/boot.img", "boot.img", OPTIONS.target_tmp, "BOOT")
- updating_boot = (not OPTIONS.two_step and
- (source_boot.data != target_boot.data))
-
- source_recovery = common.GetBootableImage(
- "/tmp/recovery.img", "recovery.img", OPTIONS.source_tmp, "RECOVERY",
- OPTIONS.source_info_dict)
- target_recovery = common.GetBootableImage(
- "/tmp/recovery.img", "recovery.img", OPTIONS.target_tmp, "RECOVERY")
- updating_recovery = (source_recovery.data != target_recovery.data)
-
- # Here's how we divide up the progress bar:
- # 0.1 for verifying the start state (PatchCheck calls)
- # 0.8 for applying patches (ApplyPatch calls)
- # 0.1 for unpacking verbatim files, symlinking, and doing the
- # device-specific commands.
-
- AppendAssertions(script, OPTIONS.target_info_dict, oem_dicts)
- device_specific.IncrementalOTA_Assertions()
-
- # Two-step incremental package strategy (in chronological order,
- # which is *not* the order in which the generated script has
- # things):
- #
- # if stage is not "2/3" or "3/3":
- # do verification on current system
- # write recovery image to boot partition
- # set stage to "2/3"
- # reboot to boot partition and restart recovery
- # else if stage is "2/3":
- # write recovery image to recovery partition
- # set stage to "3/3"
- # reboot to recovery partition and restart recovery
- # else:
- # (stage must be "3/3")
- # perform update:
- # patch system files, etc.
- # force full install of new boot image
- # set up system to update recovery partition on first boot
- # complete script normally
- # (allow recovery to mark itself finished and reboot)
-
- if OPTIONS.two_step:
- if not OPTIONS.source_info_dict.get("multistage_support", None):
- assert False, "two-step packages not supported by this build"
- fs = OPTIONS.source_info_dict["fstab"]["/misc"]
- assert fs.fs_type.upper() == "EMMC", \
- "two-step packages only supported on devices with EMMC /misc partitions"
- bcb_dev = {"bcb_dev": fs.device}
- common.ZipWriteStr(output_zip, "recovery.img", target_recovery.data)
- script.AppendExtra("""
-if get_stage("%(bcb_dev)s") == "2/3" then
-""" % bcb_dev)
-
- # Stage 2/3: Write recovery image to /recovery (currently running /boot).
- script.Comment("Stage 2/3")
- script.AppendExtra("sleep(20);\n")
- script.WriteRawImage("/recovery", "recovery.img")
- script.AppendExtra("""
-set_stage("%(bcb_dev)s", "3/3");
-reboot_now("%(bcb_dev)s", "recovery");
-else if get_stage("%(bcb_dev)s") != "3/3" then
-""" % bcb_dev)
-
- # Stage 1/3: (a) Verify the current system.
- script.Comment("Stage 1/3")
-
- # Dump fingerprints
- script.Print("Source: %s" % (source_fp,))
- script.Print("Target: %s" % (target_fp,))
-
- script.Print("Verifying current system...")
-
- device_specific.IncrementalOTA_VerifyBegin()
-
- script.ShowProgress(0.1, 0)
- so_far = system_diff.EmitVerification(script)
- if vendor_diff:
- so_far += vendor_diff.EmitVerification(script)
-
- size = []
- if system_diff.patch_list:
- size.append(system_diff.largest_source_size)
- if vendor_diff:
- if vendor_diff.patch_list:
- size.append(vendor_diff.largest_source_size)
-
- if updating_boot:
- d = common.Difference(target_boot, source_boot)
- _, _, d = d.ComputePatch()
- print("boot target: %d source: %d diff: %d" % (
- target_boot.size, source_boot.size, len(d)))
-
- common.ZipWriteStr(output_zip, "patch/boot.img.p", d)
-
- boot_type, boot_device = common.GetTypeAndDevice(
- "/boot", OPTIONS.source_info_dict)
-
- script.PatchCheck("%s:%s:%d:%s:%d:%s" %
- (boot_type, boot_device,
- source_boot.size, source_boot.sha1,
- target_boot.size, target_boot.sha1))
- so_far += source_boot.size
- size.append(target_boot.size)
-
- if size:
- script.CacheFreeSpaceCheck(max(size))
-
- device_specific.IncrementalOTA_VerifyEnd()
-
- if OPTIONS.two_step:
- # Stage 1/3: (b) Write recovery image to /boot.
- _WriteRecoveryImageToBoot(script, output_zip)
-
- script.AppendExtra("""
-set_stage("%(bcb_dev)s", "2/3");
-reboot_now("%(bcb_dev)s", "");
-else
-""" % bcb_dev)
-
- # Stage 3/3: Make changes.
- script.Comment("Stage 3/3")
-
- script.Comment("---- start making changes here ----")
-
- device_specific.IncrementalOTA_InstallBegin()
-
- if OPTIONS.two_step:
- common.ZipWriteStr(output_zip, "boot.img", target_boot.data)
- script.WriteRawImage("/boot", "boot.img")
- print("writing full boot image (forced by two-step mode)")
-
- script.Print("Removing unneeded files...")
- system_diff.RemoveUnneededFiles(script, ("/system/recovery.img",))
- if vendor_diff:
- vendor_diff.RemoveUnneededFiles(script)
-
- script.ShowProgress(0.8, 0)
- total_patch_size = 1.0 + system_diff.TotalPatchSize()
- if vendor_diff:
- total_patch_size += vendor_diff.TotalPatchSize()
- if updating_boot:
- total_patch_size += target_boot.size
-
- script.Print("Patching system files...")
- so_far = system_diff.EmitPatches(script, total_patch_size, 0)
- if vendor_diff:
- script.Print("Patching vendor files...")
- so_far = vendor_diff.EmitPatches(script, total_patch_size, so_far)
-
- if not OPTIONS.two_step:
- if updating_boot:
- # Produce the boot image by applying a patch to the current
- # contents of the boot partition, and write it back to the
- # partition.
- script.Print("Patching boot image...")
- script.ApplyPatch("%s:%s:%d:%s:%d:%s"
- % (boot_type, boot_device,
- source_boot.size, source_boot.sha1,
- target_boot.size, target_boot.sha1),
- "-",
- target_boot.size, target_boot.sha1,
- source_boot.sha1, "patch/boot.img.p")
- so_far += target_boot.size
- script.SetProgress(so_far / total_patch_size)
- print("boot image changed; including.")
- else:
- print("boot image unchanged; skipping.")
-
- system_items = ItemSet("system", "META/filesystem_config.txt")
- if vendor_diff:
- vendor_items = ItemSet("vendor", "META/vendor_filesystem_config.txt")
-
- if updating_recovery:
- # Recovery is generated as a patch using both the boot image
- # (which contains the same linux kernel as recovery) and the file
- # /system/etc/recovery-resource.dat (which contains all the images
- # used in the recovery UI) as sources. This lets us minimize the
- # size of the patch, which must be included in every OTA package.
- #
- # For older builds where recovery-resource.dat is not present, we
- # use only the boot image as the source.
-
- if not target_has_recovery_patch:
- def output_sink(fn, data):
- common.ZipWriteStr(output_zip, "recovery/" + fn, data)
- system_items.Get("system/" + fn)
-
- common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
- target_recovery, target_boot)
- script.DeleteFiles(["/system/recovery-from-boot.p",
- "/system/etc/recovery.img",
- "/system/etc/install-recovery.sh"])
- print("recovery image changed; including as patch from boot.")
- else:
- print("recovery image unchanged; skipping.")
-
- script.ShowProgress(0.1, 10)
-
- target_symlinks = CopyPartitionFiles(system_items, target_zip, None)
- if vendor_diff:
- target_symlinks.extend(CopyPartitionFiles(vendor_items, target_zip, None))
-
- temp_script = script.MakeTemporary()
- system_items.GetMetadata(target_zip)
- system_items.Get("system").SetPermissions(temp_script)
- if vendor_diff:
- vendor_items.GetMetadata(target_zip)
- vendor_items.Get("vendor").SetPermissions(temp_script)
-
- # Note that this call will mess up the trees of Items, so make sure
- # we're done with them.
- source_symlinks = CopyPartitionFiles(system_items, source_zip, None)
- if vendor_diff:
- source_symlinks.extend(CopyPartitionFiles(vendor_items, source_zip, None))
-
- target_symlinks_d = dict([(i[1], i[0]) for i in target_symlinks])
- source_symlinks_d = dict([(i[1], i[0]) for i in source_symlinks])
-
- # Delete all the symlinks in source that aren't in target. This
- # needs to happen before verbatim files are unpacked, in case a
- # symlink in the source is replaced by a real file in the target.
-
- # If a symlink in the source will be replaced by a regular file, we cannot
- # delete the symlink/file in case the package gets applied again. For such
- # a symlink, we prepend a sha1_check() to detect if it has been updated.
- # (Bug: 23646151)
- replaced_symlinks = dict()
- if system_diff:
- for i in system_diff.verbatim_targets:
- replaced_symlinks["/%s" % (i[0],)] = i[2]
- if vendor_diff:
- for i in vendor_diff.verbatim_targets:
- replaced_symlinks["/%s" % (i[0],)] = i[2]
-
- if system_diff:
- for tf in system_diff.renames.values():
- replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
- if vendor_diff:
- for tf in vendor_diff.renames.values():
- replaced_symlinks["/%s" % (tf.name,)] = tf.sha1
-
- always_delete = []
- may_delete = []
- for dest, link in source_symlinks:
- if link not in target_symlinks_d:
- if link in replaced_symlinks:
- may_delete.append((link, replaced_symlinks[link]))
- else:
- always_delete.append(link)
- script.DeleteFiles(always_delete)
- script.DeleteFilesIfNotMatching(may_delete)
-
- if system_diff.verbatim_targets:
- script.Print("Unpacking new system files...")
- script.UnpackPackageDir("system", "/system")
- if vendor_diff and vendor_diff.verbatim_targets:
- script.Print("Unpacking new vendor files...")
- script.UnpackPackageDir("vendor", "/vendor")
-
- if updating_recovery and not target_has_recovery_patch:
- script.Print("Unpacking new recovery...")
- script.UnpackPackageDir("recovery", "/system")
-
- system_diff.EmitRenames(script)
- if vendor_diff:
- vendor_diff.EmitRenames(script)
-
- script.Print("Symlinks and permissions...")
-
- # Create all the symlinks that don't already exist, or point to
- # somewhere different than what we want. Delete each symlink before
- # creating it, since the 'symlink' command won't overwrite.
- to_create = []
- for dest, link in target_symlinks:
- if link in source_symlinks_d:
- if dest != source_symlinks_d[link]:
- to_create.append((dest, link))
- else:
- to_create.append((dest, link))
- script.DeleteFiles([i[1] for i in to_create])
- script.MakeSymlinks(to_create)
-
- # Now that the symlinks are created, we can set all the
- # permissions.
- script.AppendScript(temp_script)
-
- # Do device-specific installation (eg, write radio image).
- device_specific.IncrementalOTA_InstallEnd()
-
- if OPTIONS.extra_script is not None:
- script.AppendExtra(OPTIONS.extra_script)
-
- # Patch the build.prop file last, so if something fails but the
- # device can still come up, it appears to be the old build and will
- # get set the OTA package again to retry.
- script.Print("Patching remaining system files...")
- system_diff.EmitDeferredPatches(script)
-
- if OPTIONS.wipe_user_data:
- script.Print("Erasing user data...")
- script.FormatPartition("/data")
- metadata["ota-wipe"] = "yes"
-
- if OPTIONS.two_step:
- script.AppendExtra("""
-set_stage("%(bcb_dev)s", "");
-endif;
-endif;
-""" % bcb_dev)
-
- if OPTIONS.verify and system_diff:
- script.Print("Remounting and verifying system partition files...")
- script.Unmount("/system")
- script.Mount("/system", recovery_mount_options)
- system_diff.EmitExplicitTargetVerification(script)
-
- if OPTIONS.verify and vendor_diff:
- script.Print("Remounting and verifying vendor partition files...")
- script.Unmount("/vendor")
- script.Mount("/vendor", recovery_mount_options)
- vendor_diff.EmitExplicitTargetVerification(script)
-
- # For downgrade OTAs, we prefer to use the update-binary in the source
- # build that is actually newer than the one in the target build.
- if OPTIONS.downgrade:
- script.AddToZip(source_zip, output_zip, input_path=OPTIONS.updater_binary)
- else:
- script.AddToZip(target_zip, output_zip, input_path=OPTIONS.updater_binary)
-
- metadata["ota-required-cache"] = str(script.required_cache)
- WriteMetadata(metadata, output_zip)
-
-
def main(argv):
def option_handler(o, a):
@@ -2199,7 +1320,7 @@
print("unzipping target target-files...")
OPTIONS.input_tmp, input_zip = common.UnzipTemp(
- args[0], UNZIP_PATTERN if OPTIONS.block_based else None)
+ args[0], UNZIP_PATTERN)
OPTIONS.target_tmp = OPTIONS.input_tmp
OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.target_tmp)
@@ -2268,7 +1389,7 @@
print("unzipping source target-files...")
OPTIONS.source_tmp, source_zip = common.UnzipTemp(
OPTIONS.incremental_source,
- UNZIP_PATTERN if OPTIONS.block_based else None)
+ UNZIP_PATTERN)
OPTIONS.target_info_dict = OPTIONS.info_dict
OPTIONS.source_info_dict = common.LoadInfoDict(source_zip,
OPTIONS.source_tmp)
@@ -2276,7 +1397,7 @@
print("--- source info ---")
common.DumpInfoDict(OPTIONS.source_info_dict)
try:
- WriteIncrementalOTAPackage(input_zip, source_zip, output_zip)
+ WriteBlockIncrementalOTAPackage(input_zip, source_zip, output_zip)
if OPTIONS.log_diff:
out_file = open(OPTIONS.log_diff, 'w')
import target_files_diff
diff --git a/tools/releasetools/sparse_img.py b/tools/releasetools/sparse_img.py
index 4ba7560..7eb60d9 100644
--- a/tools/releasetools/sparse_img.py
+++ b/tools/releasetools/sparse_img.py
@@ -144,6 +144,12 @@
f.seek(16, os.SEEK_SET)
f.write(struct.pack("<2I", self.total_blocks, self.total_chunks))
+ def RangeSha1(self, ranges):
+ h = sha1()
+ for data in self._GetRangeData(ranges):
+ h.update(data)
+ return h.hexdigest()
+
def ReadRangeSet(self, ranges):
return [d for d in self._GetRangeData(ranges)]
@@ -155,10 +161,11 @@
ranges = self.care_map
if not include_clobbered_blocks:
ranges = ranges.subtract(self.clobbered_blocks)
- h = sha1()
- for d in self._GetRangeData(ranges):
- h.update(d)
- return h.hexdigest()
+ return self.RangeSha1(ranges)
+
+ def WriteRangeDataToFd(self, ranges, fd):
+ for data in self._GetRangeData(ranges):
+ fd.write(data)
def _GetRangeData(self, ranges):
"""Generator that produces all the image data in 'ranges'. The