Merge "Fix discrepancy in SANITIZE_TARGET format with soong."
diff --git a/core/Makefile b/core/Makefile
index 79faae1..8885172 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -284,7 +284,7 @@
 # -----------------------------------------------------------------
 # vendor build.prop
 #
-# For verifying that the vendor build is what we thing it is
+# For verifying that the vendor build is what we think it is
 ifdef BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE
 INSTALLED_VENDOR_BUILD_PROP_TARGET := $(TARGET_OUT_VENDOR)/build.prop
 ALL_DEFAULT_INSTALLED_MODULES += $(INSTALLED_VENDOR_BUILD_PROP_TARGET)
@@ -400,6 +400,15 @@
 endif
 
 # -----------------------------------------------------------------
+# build system stats
+BUILD_SYSTEM_STATS := $(PRODUCT_OUT)/build_system_stats.txt
+$(BUILD_SYSTEM_STATS):
+	@rm -f $@
+	@$(foreach s,$(STATS.MODULE_TYPE),echo "modules_type_make,$(s),$(words $(STATS.MODULE_TYPE.$(s)))" >>$@;)
+	@$(foreach s,$(STATS.SOONG_MODULE_TYPE),echo "modules_type_soong,$(s),$(STATS.SOONG_MODULE_TYPE.$(s))" >>$@;)
+$(call dist-for-goals,droidcore,$(BUILD_SYSTEM_STATS))
+
+# -----------------------------------------------------------------
 
 # The dev key is used to sign this package, and as the key required
 # for future OTA packages installed by this system.  Actual product
@@ -550,7 +559,7 @@
 endif
 endif
 
-BOARD_KERNEL_CMDLINE := $(strip $(BOARD_KERNEL_CMDLINE) $(VERITY_KEYID))
+BOARD_KERNEL_CMDLINE := $(strip $(BOARD_KERNEL_CMDLINE) buildvariant=$(TARGET_BUILD_VARIANT) $(VERITY_KEYID))
 ifdef BOARD_KERNEL_CMDLINE
 INTERNAL_BOOTIMAGE_ARGS += --cmdline "$(BOARD_KERNEL_CMDLINE)"
 endif
@@ -767,6 +776,10 @@
 	$(hide) rm -f $@
 	$(hide) mkdir -p $(dir $@)
 	$(hide) openssl x509 -pubkey -noout -in $< > $@
+
+ALL_DEFAULT_INSTALLED_MODULES += $(TARGET_RECOVERY_ROOT_OUT)/etc/update_engine/update-payload-key.pub.pem
+$(TARGET_RECOVERY_ROOT_OUT)/etc/update_engine/update-payload-key.pub.pem: $(TARGET_OUT_ETC)/update_engine/update-payload-key.pub.pem
+	$(hide) cp -f $< $@
 endif
 endif
 
@@ -1612,7 +1625,6 @@
   $(HOST_LIBRARY_PATH)/libconscrypt_openjdk_jni$(HOST_SHLIB_SUFFIX) \
   $(HOST_LIBRARY_PATH)/libbrillo$(HOST_SHLIB_SUFFIX) \
   $(HOST_LIBRARY_PATH)/libbrillo-stream$(HOST_SHLIB_SUFFIX) \
-  $(HOST_LIBRARY_PATH)/libbrillo-http$(HOST_SHLIB_SUFFIX) \
   $(HOST_LIBRARY_PATH)/libchrome$(HOST_SHLIB_SUFFIX) \
   $(HOST_LIBRARY_PATH)/libcurl-host$(HOST_SHLIB_SUFFIX) \
   $(HOST_LIBRARY_PATH)/libevent-host$(HOST_SHLIB_SUFFIX) \
@@ -1721,11 +1733,12 @@
 		$(INSTALLED_ANDROID_INFO_TXT_TARGET) \
 		$(SELINUX_FC) \
 		$(APKCERTS_FILE) \
+		$(SOONG_ZIP) \
 		$(HOST_OUT_EXECUTABLES)/fs_config \
 		build/tools/releasetools/add_img_to_target_files \
 		| $(ACP)
 	@echo "Package target files: $@"
-	$(hide) rm -rf $@ $(zip_root)
+	$(hide) rm -rf $@ $@.list $(zip_root)
 	$(hide) mkdir -p $(dir $@) $(zip_root)
 ifneq (,$(INSTALLED_RECOVERYIMAGE_TARGET)$(filter true,$(BOARD_USES_RECOVERY_AS_BOOT)))
 	@# Components of the recovery image
@@ -1917,9 +1930,9 @@
 endif
 	@# Zip everything up, preserving symlinks and placing META/ files first to
 	@# help early validation of the .zip file while uploading it.
-	$(hide) (cd $(zip_root) && \
-	        zip -qryX ../$(notdir $@) ./META && \
-	        zip -qryXu ../$(notdir $@) .)
+	$(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
@@ -1983,13 +1996,10 @@
 
 INTERNAL_UPDATE_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip
 
-$(INTERNAL_UPDATE_PACKAGE_TARGET): $(BUILT_TARGET_FILES_PACKAGE) \
-		build/tools/releasetools/img_from_target_files
+$(INTERNAL_UPDATE_PACKAGE_TARGET): $(BUILT_TARGET_FILES_PACKAGE) $(ZIP2ZIP)
 	@echo "Package: $@"
-	$(hide) PATH=$(foreach p,$(INTERNAL_USERIMAGES_BINARY_PATHS),$(p):)$$PATH MKBOOTIMG=$(MKBOOTIMG) \
-	   ./build/tools/releasetools/img_from_target_files -v \
-	   -p $(HOST_OUT) \
-	   $(BUILT_TARGET_FILES_PACKAGE) $@
+	$(hide) $(ZIP2ZIP) -i $(BUILT_TARGET_FILES_PACKAGE) -o $@ \
+	   OTA/android-info.txt:android-info.txt "IMAGES/*.img:."
 
 .PHONY: updatepackage
 updatepackage: $(INTERNAL_UPDATE_PACKAGE_TARGET)
@@ -2013,11 +2023,13 @@
 		$(INSTALLED_VENDORIMAGE_TARGET) \
 		$(updater_dep)
 endif
-$(SYMBOLS_ZIP):
+$(SYMBOLS_ZIP): PRIVATE_LIST_FILE := $(call intermediates-dir-for,PACKAGING,symbols)/filelist
+$(SYMBOLS_ZIP): $(SOONG_ZIP)
 	@echo "Package symbols: $@"
-	$(hide) rm -rf $@
-	$(hide) mkdir -p $(dir $@) $(TARGET_OUT_UNSTRIPPED)
-	$(hide) zip -qrX $@ $(TARGET_OUT_UNSTRIPPED)
+	$(hide) rm -rf $@ $(PRIVATE_LIST_FILE)
+	$(hide) mkdir -p $(dir $@) $(TARGET_OUT_UNSTRIPPED) $(dir $(PRIVATE_LIST_FILE))
+	$(hide) find $(TARGET_OUT_UNSTRIPPED) | sort >$(PRIVATE_LIST_FILE)
+	$(hide) $(SOONG_ZIP) -d -o $@ -C $(TARGET_OUT_UNSTRIPPED) -l $(PRIVATE_LIST_FILE)
 
 # -----------------------------------------------------------------
 # A zip of the Android Apps. Not keeping full path so that we don't
diff --git a/core/aux_config.mk b/core/aux_config.mk
new file mode 100644
index 0000000..decff34
--- /dev/null
+++ b/core/aux_config.mk
@@ -0,0 +1,183 @@
+variant_list := $(filter AUX-%,$(MAKECMDGOALS))
+
+ifdef variant_list
+AUX_OS_VARIANT_LIST := $(patsubst AUX-%,%,$(variant_list))
+else
+AUX_OS_VARIANT_LIST := $(TARGET_AUX_OS_VARIANT_LIST)
+endif
+
+# exclude AUX targets from build
+ifeq ($(AUX_OS_VARIANT_LIST),none)
+AUX_OS_VARIANT_LIST :=
+endif
+
+# temporary workaround to support external toolchain
+ifeq ($(NANOHUB_TOOLCHAIN),)
+AUX_OS_VARIANT_LIST :=
+endif
+
+# setup toolchain paths for various CPU architectures
+# this one will come from android prebuilts eventually
+AUX_TOOLCHAIN_cortexm4 := $(NANOHUB_TOOLCHAIN)
+ifeq ($(wildcard $(AUX_TOOLCHAIN_cortexm4)gcc),)
+AUX_TOOLCHAIN_cortexm4:=
+endif
+
+# there is no MAKE var that defines path to HOST toolchain
+# all the interesting paths are hardcoded in soong, and are not available from here
+# There is no other way but to hardcode them again, as we may need host x86 toolcain for AUX
+ifeq ($(HOST_OS),linux)
+AUX_TOOLCHAIN_x86 := prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.15-4.8/bin/x86_64-linux-
+endif
+
+# setup AUX globals
+AUX_SHLIB_SUFFIX := .so
+AUX_GLOBAL_ARFLAGS := crsPD
+AUX_STATIC_LIB_SUFFIX := .a
+
+# Load ever-lasting "indexed" version of AUX variant environment; it is treated as READ-ONLY from this
+# moment on.
+#
+# $(1) - variant
+# no return value
+define aux-variant-setup-paths
+$(eval AUX_OUT_ROOT_$(1) := $(PRODUCT_OUT)/aux/$(1)) \
+$(eval AUX_COMMON_OUT_ROOT_$(1) := $(AUX_OUT_ROOT_$(1))/common) \
+$(eval AUX_OUT_$(1) := $(AUX_OUT_ROOT_$(1))/$(AUX_OS_$(1))-$(AUX_ARCH_$(1))-$(AUX_CPU_$(1))) \
+$(eval AUX_OUT_INTERMEDIATES_$(1) := $(AUX_OUT_$(1))/obj) \
+$(eval AUX_OUT_COMMON_INTERMEDIATES_$(1) := $(AUX_COMMON_OUT_ROOT_$(1))/obj) \
+$(eval AUX_OUT_HEADERS_$(1) := $(AUX_OUT_INTERMEDIATES_$(1))/include) \
+$(eval AUX_OUT_INTERMEDIATE_LIBRARIES_$(1) := $(AUX_OUT_INTERMEDIATES_$(1))/lib) \
+$(eval AUX_OUT_NOTICE_FILES_$(1) := $(AUX_OUT_INTERMEDIATES_$(1))/NOTICE_FILES) \
+$(eval AUX_OUT_FAKE_$(1) := $(AUX_OUT_$(1))/fake_packages) \
+$(eval AUX_OUT_GEN_$(1) := $(AUX_OUT_$(1))/gen) \
+$(eval AUX_OUT_COMMON_GEN_$(1) := $(AUX_COMMON_OUT_ROOT_$(1))/gen) \
+$(eval AUX_OUT_EXECUTABLES_$(1) := $(AUX_OUT_$(1))/bin) \
+$(eval AUX_OUT_UNSTRIPPED_$(1) := $(AUX_OUT_$(1))/symbols)
+endef
+
+# Copy "indexed" AUX environment for given VARIANT into
+# volatile not-indexed set of variables for simplicity of access.
+# Injection of index support throughout the build system is suboptimal
+# hence volatile environment is constructed
+# Unlike HOST*, TARGET* variables, AUX* variables are NOT read-only, but their
+# indexed versions are.
+#
+# $(1) - variant
+# no return value
+define aux-variant-load-env
+$(eval AUX_OS_VARIANT:=$(1)) \
+$(eval AUX_OS:=$(AUX_OS_$(1))) \
+$(eval AUX_ARCH:=$(AUX_ARCH_$(1))) \
+$(eval AUX_SUBARCH:=$(AUX_SUBARCH_$(1))) \
+$(eval AUX_CPU:=$(AUX_CPU_$(1))) \
+$(eval AUX_OS_PATH:=$(AUX_OS_PATH_$(1))) \
+$(eval AUX_OUT_ROOT := $(AUX_OUT_ROOT_$(1))) \
+$(eval AUX_COMMON_OUT_ROOT := $(AUX_COMMON_OUT_ROOT_$(1))) \
+$(eval AUX_OUT := $(AUX_OUT_$(1))) \
+$(eval AUX_OUT_INTERMEDIATES := $(AUX_OUT_INTERMEDIATES_$(1))) \
+$(eval AUX_OUT_COMMON_INTERMEDIATES := $(AUX_OUT_COMMON_INTERMEDIATES_$(1))) \
+$(eval AUX_OUT_HEADERS := $(AUX_OUT_HEADERS_$(1))) \
+$(eval AUX_OUT_INTERMEDIATE_LIBRARIES := $(AUX_OUT_INTERMEDIATE_LIBRARIES_$(1))) \
+$(eval AUX_OUT_NOTICE_FILES := $(AUX_OUT_NOTICE_FILES_$(1))) \
+$(eval AUX_OUT_FAKE := $(AUX_OUT_FAKE_$(1))) \
+$(eval AUX_OUT_GEN := $(AUX_OUT_GEN_$(1))) \
+$(eval AUX_OUT_COMMON_GEN := $(AUX_OUT_COMMON_GEN_$(1))) \
+$(eval AUX_OUT_EXECUTABLES := $(AUX_OUT_EXECUTABLES_$(1))) \
+$(eval AUX_OUT_UNSTRIPPED := $(AUX_OUT_UNSTRIPPED_$(1)))
+endef
+
+# given a variant:path pair, load the variant conviguration with aux-variant-setup-paths from file
+# this is a build system extension mechainsm, since configuration typically resides in non-build
+# project space
+#
+# $(1) - variant:path pair
+# $(2) - file suffix
+# no return value
+define aux-variant-import-from-pair
+$(eval _pair := $(subst :, ,$(1))) \
+$(eval _name:=$(word 1,$(_pair))) \
+$(eval _path:=$(word 2,$(_pair))) \
+$(eval include $(_path)/$(_name)$(2)) \
+$(eval AUX_OS_VARIANT_LIST_$(AUX_OS_$(1)):=) \
+$(call aux-variant-setup-paths,$(_name)) \
+$(eval AUX_ALL_VARIANTS += $(_name)) \
+$(eval AUX_ALL_OSES := $(filterout $(AUX_OS_$(_name)),$(AUX_ALL_OSES)) $(AUX_OS_$(_name))) \
+$(eval AUX_ALL_CPUS := $(filterout $(AUX_CPU_$(_name)),$(AUX_ALL_CPUS)) $(AUX_CPU_$(_name))) \
+$(eval AUX_ALL_ARCHS := $(filterout $(AUX_ARCH_$(_name)),$(AUX_ALL_ARCHS)) $(AUX_ARCH_$(_name))) \
+$(eval AUX_ALL_SUBARCHS := $(filterout $(AUX_SUBARCH_$(_name)),$(AUX_ALL_SUBARCHS)) $(AUX_SUBARCH_$(_name)))
+endef
+
+# Load system configuration referenced by AUX variant config;
+# this is a build extension mechanism; typically system config
+# resides in a non-build projects;
+# system config may define new rules and globally visible BUILD*
+# includes to support project-specific build steps and toolchains
+# MAintains list of valiants that reference this os config in OS "indexed" var
+# this facilitates multivariant build of the OS (or whataver it is the name of common component these variants share)
+#
+# $(1) - variant
+# no return value
+define aux-import-os-config
+$(eval _aioc_os := $(AUX_OS_$(1))) \
+$(eval AUX_OS_PATH_$(1) := $(patsubst $(_aioc_os):%,%,$(filter $(_aioc_os):%,$(AUX_ALL_OS_PATHS)))) \
+$(eval _aioc_os_cfg := $(AUX_OS_PATH_$(1))/$(_aioc_os)$(os_sfx)) \
+$(if $(wildcard $(_aioc_os_cfg)),,$(error AUX '$(_aioc_os)' OS config file [$(notdir $(_aioc_os_cfg))] required by AUX variant '$(1)' does not exist)) \
+$(if $(filter $(_aioc_os),$(_os_list)),,$(eval include $(_aioc_os_cfg))) \
+$(eval AUX_OS_VARIANT_LIST_$(_aioc_os) += $(1)) \
+$(eval _os_list += $(_aioc_os))
+endef
+
+# make sure that AUX config variables are minimally sane;
+# as a bare minimum they must contain the vars described by aux_env
+# Generate error if requirement is not met.
+#
+#$(1) - variant
+# no return value
+define aux-variant-validate
+$(eval _all:=) \
+$(eval _req:=$(addsuffix _$(1),$(aux_env))) \
+$(foreach var,$(_req),$(eval _all += $(var))) \
+$(eval _missing := $(filterout $(_all),$(_req))) \
+$(if $(_missing),$(error AUX variant $(1) must define vars: $(_missing)))
+endef
+
+AUX_ALL_VARIANTS :=
+AUX_ALL_OSES :=
+AUX_ALL_CPUS :=
+AUX_ALL_ARCHS :=
+AUX_ALL_SUBARCHS :=
+
+variant_sfx :=_aux_variant_config.mk
+os_sfx :=_aux_os_config.mk
+
+all_configs := $(shell find device vendor -maxdepth 4 -name '*$(variant_sfx)' -o -name '*$(os_sfx)' | sort)
+all_os_configs := $(filter %$(os_sfx),$(all_configs))
+all_variant_configs := $(filter %$(variant_sfx),$(all_configs))
+
+AUX_ALL_OS_PATHS := $(foreach f,$(all_os_configs),$(patsubst %$(os_sfx),%,$(notdir $(f))):$(patsubst %/,%,$(dir $(f))))
+AUX_ALL_OS_VARIANT_PATHS := $(foreach f,$(all_variant_configs),$(patsubst %$(variant_sfx),%,$(notdir $(f))):$(patsubst %/,%,$(dir $(f))))
+
+my_variant_pairs := $(foreach v,$(AUX_OS_VARIANT_LIST),$(filter $(v):%,$(AUX_ALL_OS_VARIANT_PATHS)))
+my_missing_variants := $(foreach v,$(AUX_OS_VARIANT_LIST),$(if $(filter $(v):%,$(AUX_ALL_OS_VARIANT_PATHS)),,$(v)))
+
+ifneq ($(strip $(my_missing_variants)),)
+$(error Don't know how to build variant(s): $(my_missing_variants))
+endif
+
+# mandatory variables
+aux_env := AUX_OS AUX_ARCH AUX_SUBARCH AUX_CPU
+
+$(foreach v,$(my_variant_pairs),$(if $(filter $(v),$(AUX_ALL_VARIANTS)),,$(call aux-variant-import-from-pair,$(v),$(variant_sfx))))
+
+ifdef AUX_ALL_VARIANTS
+_os_list :=
+$(foreach v,$(AUX_ALL_VARIANTS),\
+  $(call aux-import-os-config,$(v)) \
+  $(call aux-variant-validate,$(v)) \
+)
+endif
+
+INSTALLED_AUX_TARGETS :=
+
+droidcore: auxiliary
diff --git a/core/aux_executable.mk b/core/aux_executable.mk
new file mode 100644
index 0000000..daf30e7
--- /dev/null
+++ b/core/aux_executable.mk
@@ -0,0 +1,96 @@
+# caller might have included aux_toolchain, e.g. if custom build steps are defined
+ifeq ($(LOCAL_IS_AUX_MODULE),)
+include $(BUILD_SYSTEM)/aux_toolchain.mk
+endif
+
+ifeq ($(AUX_BUILD_NOT_COMPATIBLE),)
+
+###########################################################
+## Standard rules for building an executable file.
+##
+## Additional inputs from base_rules.make:
+## None.
+###########################################################
+
+ifeq ($(strip $(LOCAL_MODULE_CLASS)),)
+LOCAL_MODULE_CLASS := EXECUTABLES
+endif
+
+$(call $(aux-executable-hook))
+
+###########################################################
+## Standard rules for building any target-side binaries
+## with dynamic linkage (dynamic libraries or executables
+## that link with dynamic libraries)
+##
+## Files including this file must define a rule to build
+## the target $(linked_module).
+###########################################################
+
+# The name of the target file, without any path prepended.
+# This duplicates logic from base_rules.mk because we need to
+# know its results before base_rules.mk is included.
+include $(BUILD_SYSTEM)/configure_module_stem.mk
+
+intermediates := $(call local-intermediates-dir)
+
+# Define the target that is the unmodified output of the linker.
+# The basename of this target must be the same as the final output
+# binary name, because it's used to set the "soname" in the binary.
+# The includer of this file will define a rule to build this target.
+linked_module := $(intermediates)/LINKED/$(my_built_module_stem)
+
+ALL_ORIGINAL_DYNAMIC_BINARIES += $(linked_module)
+
+# Because AUX_SYMBOL_FILTER_FILE depends on ALL_ORIGINAL_DYNAMIC_BINARIES,
+# the linked_module rules won't necessarily inherit the PRIVATE_
+# variables from LOCAL_BUILT_MODULE.  This tells binary.make to explicitly
+# define the PRIVATE_ variables for linked_module as well as for
+# LOCAL_BUILT_MODULE.
+LOCAL_INTERMEDIATE_TARGETS += $(linked_module)
+
+###################################
+include $(BUILD_SYSTEM)/binary.mk
+###################################
+
+aux_output := $(linked_module)
+
+ifneq ($(LOCAL_CUSTOM_BUILD_STEP_INPUT),)
+ifneq ($(LOCAL_CUSTOM_BUILD_STEP_OUTPUT),)
+
+# injecting custom build steps
+$(LOCAL_CUSTOM_BUILD_STEP_INPUT): $(aux_output)
+	@echo "$(AUX_DISPLAY) custom copy: $(PRIVATE_MODULE) ($@)"
+	@mkdir -p $(dir $@)
+	$(hide) $(copy-file-to-target)
+
+aux_output := $(LOCAL_CUSTOM_BUILD_STEP_OUTPUT)
+
+endif
+endif
+
+$(LOCAL_BUILT_MODULE): $(aux_output)
+	@echo "$(AUX_DISPLAY) final copy: $(PRIVATE_MODULE) ($@)"
+	@mkdir -p $(dir $@)
+	$(hide) $(copy-file-to-target)
+
+INSTALLED_AUX_TARGETS += $(LOCAL_INSTALLED_MODULE)
+
+$(cleantarget): PRIVATE_CLEAN_FILES += \
+    $(linked_module) \
+
+# Define PRIVATE_ variables from global vars
+$(linked_module): PRIVATE_TARGET_OUT_INTERMEDIATE_LIBRARIES := $(AUX_OUT_INTERMEDIATE_LIBRARIES)
+$(linked_module): PRIVATE_POST_LINK_CMD := $(LOCAL_POST_LINK_CMD)
+
+ifeq ($(LOCAL_FORCE_STATIC_EXECUTABLE),true)
+$(linked_module): $(all_objects) $(all_libraries) $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(transform-o-to-aux-static-executable)
+	$(PRIVATE_POST_LINK_CMD)
+else
+$(linked_module): $(all_objects) $(all_libraries) $(LOCAL_ADDITIONAL_DEPENDENCIES)
+	$(transform-o-to-aux-executable)
+	$(PRIVATE_POST_LINK_CMD)
+endif
+
+endif # AUX_BUILD_NOT_COMPATIBLE
diff --git a/core/aux_static_library.mk b/core/aux_static_library.mk
new file mode 100644
index 0000000..d88478d
--- /dev/null
+++ b/core/aux_static_library.mk
@@ -0,0 +1,27 @@
+ifeq ($(LOCAL_IS_AUX_MODULE),)
+include $(BUILD_SYSTEM)/aux_toolchain.mk
+endif
+
+ifeq ($(AUX_BUILD_NOT_COMPATIBLE),)
+
+ifeq ($(strip $(LOCAL_MODULE_CLASS)),)
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+endif
+ifeq ($(strip $(LOCAL_MODULE_SUFFIX)),)
+LOCAL_MODULE_SUFFIX := .a
+endif
+
+LOCAL_UNINSTALLABLE_MODULE := true
+
+ifneq ($(strip $(LOCAL_MODULE_STEM)$(LOCAL_BUILT_MODULE_STEM)),)
+$(error $(LOCAL_PATH): Cannot set module stem for a library)
+endif
+
+include $(BUILD_SYSTEM)/binary.mk
+
+$(LOCAL_BUILT_MODULE) : PRIVATE_AR := $(AUX_AR)
+$(LOCAL_BUILT_MODULE) : $(built_whole_libraries)
+$(LOCAL_BUILT_MODULE) : $(all_objects)
+	$(transform-o-to-aux-static-lib)
+
+endif # AUX_BUILD_NOT_COMPATIBLE
diff --git a/core/aux_toolchain.mk b/core/aux_toolchain.mk
new file mode 100644
index 0000000..de0b139
--- /dev/null
+++ b/core/aux_toolchain.mk
@@ -0,0 +1,53 @@
+###########################################################
+# takes form LOCAL_AUX_TOOLCHAIN_$(LOCAL_AUX_CPU)
+###########################################################
+
+###############################
+# setup AUX environment
+###############################
+
+# shortcuts for targets with a single instance of OS, ARCH, VARIANT, CPU
+AUX_TOOLCHAIN := $(if $(LOCAL_AUX_TOOLCHAIN),$(LOCAL_AUX_TOOLCHAIN),$(AUX_TOOLCHAIN_$(AUX_CPU)))
+AUX_BUILD_NOT_COMPATIBLE:=
+ifeq ($(strip $(AUX_TOOLCHAIN)),)
+  ifeq ($(strip $(AUX_CPU)),)
+    $(warning $(LOCAL_PATH): $(LOCAL_MODULE): Undefined CPU for AUX toolchain)
+    AUX_BUILD_NOT_COMPATIBLE += TOOLCHAIN
+  else
+    $(warning $(LOCAL_PATH): $(LOCAL_MODULE): Undefined AUX toolchain for CPU=$(AUX_CPU))
+    AUX_BUILD_NOT_COMPATIBLE += TOOLCHAIN
+  endif
+endif
+
+AUX_BUILD_NOT_COMPATIBLE += $(foreach var,OS ARCH SUBARCH CPU OS_VARIANT,$(if $(LOCAL_AUX_$(var)),$(if \
+    $(filter $(LOCAL_AUX_$(var)),$(AUX_$(var))),,$(var))))
+
+AUX_BUILD_NOT_COMPATIBLE := $(strip $(AUX_BUILD_NOT_COMPATIBLE))
+
+ifneq ($(AUX_BUILD_NOT_COMPATIBLE),)
+$(info $(LOCAL_PATH): $(LOCAL_MODULE): not compatible: "$(AUX_BUILD_NOT_COMPATIBLE)" with)
+$(info ====> OS=$(AUX_OS) CPU=$(AUX_CPU) ARCH=$(AUX_ARCH) SUBARCH=$(AUX_SUBARCH) OS_VARIANT=$(AUX_OS_VARIANT))
+$(info ====> TOOLCHAIN=$(AUX_TOOLCHAIN))
+endif
+
+AUX_AR := $(AUX_TOOLCHAIN)ar
+AUX_AS := $(AUX_TOOLCHAIN)gcc
+AUX_CC := $(AUX_TOOLCHAIN)gcc
+AUX_CXX := $(AUX_TOOLCHAIN)g++
+AUX_LINKER := $(AUX_TOOLCHAIN)ld
+AUX_OBJCOPY := $(AUX_TOOLCHAIN)objcopy
+AUX_OBJDUMP := $(AUX_TOOLCHAIN)objdump
+
+###############################
+# setup Android environment
+###############################
+
+LOCAL_IS_AUX_MODULE := true
+LOCAL_2ND_ARCH_VAR_PREFIX :=
+LOCAL_CC := $(AUX_CC)
+LOCAL_CXX := $(AUX_CXX)
+LOCAL_NO_DEFAULT_COMPILER_FLAGS := true
+LOCAL_SYSTEM_SHARED_LIBRARIES :=
+LOCAL_CXX_STL := none
+LOCAL_NO_PIC := true
+LOCAL_NO_LIBCOMPILER_RT := true
diff --git a/core/base_rules.mk b/core/base_rules.mk
index a84ea27..aac551a 100644
--- a/core/base_rules.mk
+++ b/core/base_rules.mk
@@ -14,6 +14,9 @@
 # limitations under the License.
 #
 
+# Catch users that directly include base_rules.mk
+$(call record-module-type,base_rules)
+
 # Users can define base-rules-hook in their buildspec.mk to perform
 # arbitrary operations as each module is included.
 ifdef base-rules-hook
@@ -30,6 +33,7 @@
 endif
 
 LOCAL_IS_HOST_MODULE := $(strip $(LOCAL_IS_HOST_MODULE))
+LOCAL_IS_AUX_MODULE := $(strip $(LOCAL_IS_AUX_MODULE))
 ifdef LOCAL_IS_HOST_MODULE
   ifneq ($(LOCAL_IS_HOST_MODULE),true)
     $(error $(LOCAL_PATH): LOCAL_IS_HOST_MODULE must be "true" or empty, not "$(LOCAL_IS_HOST_MODULE)")
@@ -40,8 +44,18 @@
     my_prefix := $(LOCAL_HOST_PREFIX)
   endif
   my_host := host-
+  my_kind := HOST
 else
-  my_prefix := TARGET_
+  ifdef LOCAL_IS_AUX_MODULE
+    ifneq ($(LOCAL_IS_AUX_MODULE),true)
+      $(error $(LOCAL_PATH): LOCAL_IS_AUX_MODULE must be "true" or empty, not "$(LOCAL_IS_AUX_MODULE)")
+    endif
+    my_prefix := AUX_
+    my_kind := AUX
+  else
+    my_prefix := TARGET_
+    my_kind :=
+  endif
   my_host :=
 endif
 
@@ -192,9 +206,13 @@
 my_register_name := $(my_register_name)$($(my_prefix)2ND_ARCH_MODULE_SUFFIX)
 endif
 endif
+
+# variant is enough to make nano class unique; it serves as a key to lookup (OS,ARCH) tuple
+aux_class := $($(my_prefix)OS_VARIANT)
 # Make sure that this IS_HOST/CLASS/MODULE combination is unique.
 module_id := MODULE.$(if \
-    $(LOCAL_IS_HOST_MODULE),$($(my_prefix)OS),TARGET).$(LOCAL_MODULE_CLASS).$(my_register_name)
+    $(LOCAL_IS_HOST_MODULE),$($(my_prefix)OS),$(if \
+    $(LOCAL_IS_AUX_MODULE),$(aux_class),TARGET)).$(LOCAL_MODULE_CLASS).$(my_register_name)
 ifdef $(module_id)
 $(error $(LOCAL_PATH): $(module_id) already defined by $($(module_id)))
 endif
@@ -289,6 +307,7 @@
 ###########################################################
 $(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_PATH:=$(LOCAL_PATH)
 $(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_IS_HOST_MODULE := $(LOCAL_IS_HOST_MODULE)
+$(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_IS_AUX_MODULE := $(LOCAL_IS_AUX_MODULE)
 $(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_HOST:= $(my_host)
 $(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_PREFIX := $(my_prefix)
 
@@ -323,6 +342,9 @@
 ## Module installation rule
 ###########################################################
 
+my_init_rc_installed :=
+my_init_rc_pairs :=
+my_installed_symlinks :=
 ifndef LOCAL_UNINSTALLABLE_MODULE
 $(LOCAL_INSTALLED_MODULE): PRIVATE_POST_INSTALL_CMD := $(LOCAL_POST_INSTALL_CMD)
 $(LOCAL_INSTALLED_MODULE): $(LOCAL_BUILT_MODULE)
@@ -330,23 +352,28 @@
 	$(copy-file-to-new-target)
 	$(PRIVATE_POST_INSTALL_CMD)
 
+ifndef LOCAL_IS_HOST_MODULE
 # Rule to install the module's companion init.rc.
-my_init_rc_installed :=
-my_init_rc_pairs :=
-my_init_rc := $(LOCAL_INIT_RC_$(my_32_64_bit_suffix))
-ifneq ($(my_init_rc),)
-my_init_rc_pairs += $(LOCAL_PATH)/$(my_init_rc):$(TARGET_OUT$(partition_tag)_ETC)/init/$(notdir $(my_init_rc))
-endif
-ifneq ($(LOCAL_INIT_RC),)
-my_init_rc_pairs += $(LOCAL_PATH)/$(LOCAL_INIT_RC):$(TARGET_OUT$(partition_tag)_ETC)/init/$(notdir $(LOCAL_INIT_RC))
-# Make sure we don't define the rule twice in multilib module.
-LOCAL_INIT_RC :=
-endif
-ifneq ($(my_init_rc_pairs),)
-my_init_rc_installed := $(call copy-many-files,$(my_init_rc_pairs))
+my_init_rc := $(LOCAL_INIT_RC_$(my_32_64_bit_suffix)) $(LOCAL_INIT_RC)
+ifneq ($(strip $(my_init_rc)),)
+my_init_rc_pairs := $(foreach rc,$(my_init_rc),$(LOCAL_PATH)/$(rc):$(TARGET_OUT$(partition_tag)_ETC)/init/$(notdir $(rc)))
+my_init_rc_installed := $(foreach rc,$(my_init_rc_pairs),$(call word-colon,2,$(rc)))
+
+# Make sure we only set up the copy rules once, even if another arch variant
+# shares a common LOCAL_INIT_RC.
+my_init_rc_new_pairs := $(filter-out $(ALL_INIT_RC_INSTALLED_PAIRS),$(my_init_rc_pairs))
+my_init_rc_new_installed := $(call copy-many-files,$(my_init_rc_new_pairs))
+ALL_INIT_RC_INSTALLED_PAIRS += $(my_init_rc_new_pairs)
 
 $(my_register_name) : $(my_init_rc_installed)
-endif # my_init_rc_pairs
+endif # my_init_rc
+endif # !LOCAL_IS_HOST_MODULE
+
+# Rule to install the module's companion symlinks
+my_installed_symlinks := $(addprefix $(my_module_path)/,$(LOCAL_MODULE_SYMLINKS) $(LOCAL_MODULE_SYMLINKS_$(my_32_64_bit_suffix)))
+$(foreach symlink,$(my_installed_symlinks),\
+    $(call symlink-file,$(LOCAL_INSTALLED_MODULE),$(my_installed_module_stem),$(symlink)))
+
 endif # !LOCAL_UNINSTALLABLE_MODULE
 
 ###########################################################
@@ -395,7 +422,7 @@
 ifneq (true,$(LOCAL_UNINSTALLABLE_MODULE))
 ALL_MODULES.$(my_register_name).INSTALLED := \
     $(strip $(ALL_MODULES.$(my_register_name).INSTALLED) \
-    $(LOCAL_INSTALLED_MODULE) $(my_init_rc_installed))
+    $(LOCAL_INSTALLED_MODULE) $(my_init_rc_installed) $(my_installed_symlinks))
 ALL_MODULES.$(my_register_name).BUILT_INSTALLED := \
     $(strip $(ALL_MODULES.$(my_register_name).BUILT_INSTALLED) \
     $(LOCAL_BUILT_MODULE):$(LOCAL_INSTALLED_MODULE) \
diff --git a/core/binary.mk b/core/binary.mk
index 9d3ef9b..8fddbd7 100644
--- a/core/binary.mk
+++ b/core/binary.mk
@@ -70,6 +70,9 @@
     $(error $(LOCAL_PATH): LOCAL_SDK_VERSION cannot be used in host module)
   endif
 
+  # Make sure we've built the NDK.
+  my_additional_dependencies += $(SOONG_OUT_DIR)/ndk.timestamp
+
   # mips32r6 is not supported by the NDK. No released NDK contains these
   # libraries, but the r10 in prebuilts/ndk had a local hack to add them :(
   #
@@ -81,9 +84,36 @@
     endif
   endif
 
-  my_ndk_source_root := $(HISTORICAL_NDK_VERSIONS_ROOT)/$(LOCAL_NDK_VERSION)/sources
-  my_ndk_sysroot := $(HISTORICAL_NDK_VERSIONS_ROOT)/$(LOCAL_NDK_VERSION)/platforms/android-$(LOCAL_SDK_VERSION)/arch-$(TARGET_$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)
-  my_ndk_sysroot_include := $(my_ndk_sysroot)/usr/include
+  my_arch := $(TARGET_$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)
+  ifneq (,$(filter arm64 mips64 x86_64,$(my_arch)))
+    my_min_sdk_version := 21
+  else
+    my_min_sdk_version := 9
+  endif
+
+  # Historically we've just set up a bunch of symlinks in prebuilts/ndk to map
+  # missing API levels to existing ones where necessary, but we're not doing
+  # that for the generated libraries. Clip the API level to the minimum where
+  # appropriate.
+  my_ndk_api := \
+    $(shell if [ $(LOCAL_SDK_VERSION) -lt $(my_min_sdk_version) ]; then \
+        echo $(my_min_sdk_version); else echo $(LOCAL_SDK_VERSION); fi)
+
+  # Traditionally this has come from android/api-level.h, but with the libc
+  # headers unified it must be set by the build system since we don't have
+  # per-API level copies of that header now.
+  my_cflags += -D__ANDROID_API__=$(my_ndk_api)
+
+  my_ndk_source_root := \
+      $(HISTORICAL_NDK_VERSIONS_ROOT)/$(LOCAL_NDK_VERSION)/sources
+  my_ndk_sysroot := \
+    $(HISTORICAL_NDK_VERSIONS_ROOT)/$(LOCAL_NDK_VERSION)/platforms/android-$(my_ndk_api)/arch-$(my_arch)
+  my_built_ndk := $(SOONG_OUT_DIR)/ndk
+  my_ndk_triple := $($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_NDK_TRIPLE)
+  my_ndk_sysroot_include := \
+      $(my_built_ndk)/sysroot/usr/include \
+      $(my_built_ndk)/sysroot/usr/include/$(my_ndk_triple) \
+      $(my_ndk_sysroot)/usr/include \
 
   # x86_64 and and mips64 are both multilib toolchains, so their libraries are
   # installed in /usr/lib64. Aarch64, on the other hand, is not a multilib
@@ -92,14 +122,19 @@
   # Mips32r6 is yet another variation, with libraries installed in libr6.
   #
   # For the rest, the libraries are installed simply to /usr/lib.
-  ifneq (,$(filter x86_64 mips64,$(TARGET_$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)))
-    my_ndk_sysroot_lib := $(my_ndk_sysroot)/usr/lib64
+  ifneq (,$(filter x86_64 mips64,$(my_arch)))
+    my_ndk_libdir_name := lib64
   else ifeq (mips32r6,$(TARGET_$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH_VARIANT))
-    my_ndk_sysroot_lib := $(my_ndk_sysroot)/usr/libr6
+    my_ndk_libdir_name := libr6
   else
-    my_ndk_sysroot_lib := $(my_ndk_sysroot)/usr/lib
+    my_ndk_libdir_name := lib
   endif
 
+  my_ndk_platform_dir := \
+      $(my_built_ndk)/platforms/android-$(my_ndk_api)/arch-$(my_arch)
+  my_built_ndk_libs := $(my_ndk_platform_dir)/usr/$(my_ndk_libdir_name)
+  my_ndk_sysroot_lib := $(my_ndk_sysroot)/usr/$(my_ndk_libdir_name)
+
   # The bionic linker now has support for packed relocations and gnu style
   # hashes (which are much faster!), but shipping to older devices requires
   # the old style hash. Fortunately, we can build with both and it'll work
@@ -117,7 +152,6 @@
   # See ndk/docs/CPLUSPLUS-SUPPORT.html
   my_ndk_stl_include_path :=
   my_ndk_stl_shared_lib_fullpath :=
-  my_ndk_stl_shared_lib :=
   my_ndk_stl_static_lib :=
   my_ndk_stl_cppflags :=
   my_cpu_variant := $(TARGET_$(LOCAL_2ND_ARCH_VAR_PREFIX)CPU_ABI)
@@ -143,31 +177,58 @@
       my_ldlibs += -ldl
     else
       my_ndk_stl_shared_lib_fullpath := $(my_ndk_source_root)/cxx-stl/stlport/libs/$(my_cpu_variant)/libstlport_shared.so
-      my_ndk_stl_shared_lib := -lstlport_shared
     endif
   else # LOCAL_NDK_STL_VARIANT is not stlport_* either
   ifneq (,$(filter c++_%, $(LOCAL_NDK_STL_VARIANT)))
-    my_ndk_stl_include_path := \
-      $(my_ndk_source_root)/cxx-stl/llvm-libc++/libcxx/include \
-      $(my_ndk_source_root)/android/support/include \
-
     # Pre-r11 NDKs used libgabi++ for libc++'s C++ ABI, but r11 and later use
     # libc++abi.
-    ifeq ($(LOCAL_NDK_VERSION),r10)
+    #
+    # r13 no longer has the inner directory as a side effect of just using
+    # external/libcxx.
+    ifeq (r10,$(LOCAL_NDK_VERSION))
+      my_ndk_stl_include_path := \
+        $(my_ndk_source_root)/cxx-stl/llvm-libc++/libcxx/include
       my_ndk_stl_include_path += \
         $(my_ndk_source_root)/cxx-stl/llvm-libc++/gabi++/include
-    else
+    else ifeq (r11,$(LOCAL_NDK_VERSION))
+      my_ndk_stl_include_path := \
+        $(my_ndk_source_root)/cxx-stl/llvm-libc++/libcxx/include
       my_ndk_stl_include_path += \
         $(my_ndk_source_root)/cxx-stl/llvm-libc++abi/libcxxabi/include
+    else
+      my_ndk_stl_include_path := \
+        $(my_ndk_source_root)/cxx-stl/llvm-libc++/include
+      my_ndk_stl_include_path += \
+        $(my_ndk_source_root)/cxx-stl/llvm-libc++abi/include
+    endif
+    my_ndk_stl_include_path += $(my_ndk_source_root)/android/support/include
+
+    my_libcxx_libdir := \
+      $(my_ndk_source_root)/cxx-stl/llvm-libc++/libs/$(my_cpu_variant)
+
+    ifneq (,$(filter r10 r11,$(LOCAL_NDK_VERSION)))
+      ifeq (c++_static,$(LOCAL_NDK_STL_VARIANT))
+        my_ndk_stl_static_lib := $(my_libcxx_libdir)/libc++_static.a
+        my_ldlibs += -ldl
+      else
+        my_ndk_stl_shared_lib_fullpath := $(my_libcxx_libdir)/libc++_shared.so
+      endif
+    else
+      ifeq (c++_static,$(LOCAL_NDK_STL_VARIANT))
+        my_ndk_stl_static_lib := \
+          $(my_libcxx_libdir)/libc++_static.a \
+          $(my_libcxx_libdir)/libc++abi.a
+        my_ldlibs += -ldl
+      else
+        my_ndk_stl_shared_lib_fullpath := $(my_libcxx_libdir)/libc++_shared.so
+      endif
+
+      my_ndk_stl_static_lib += $(my_libcxx_libdir)/libandroid_support.a
+      ifneq (,$(filter armeabi armeabi-v7a,$(my_cpu_variant)))
+        my_ndk_stl_static_lib += $(my_libcxx_libdir)/libunwind.a
+      endif
     endif
 
-    ifeq (c++_static,$(LOCAL_NDK_STL_VARIANT))
-      my_ndk_stl_static_lib := $(my_ndk_source_root)/cxx-stl/llvm-libc++/libs/$(my_cpu_variant)/libc++_static.a
-      my_ldlibs += -ldl
-    else
-      my_ndk_stl_shared_lib_fullpath := $(my_ndk_source_root)/cxx-stl/llvm-libc++/libs/$(my_cpu_variant)/libc++_shared.so
-      my_ndk_stl_shared_lib := -lc++_shared
-    endif
     my_ndk_stl_cppflags := -std=c++11
   else # LOCAL_NDK_STL_VARIANT is not c++_* either
   ifneq (,$(filter gnustl_%, $(LOCAL_NDK_STL_VARIANT)))
@@ -182,9 +243,41 @@
   endif
 endif
 
+ifndef LOCAL_IS_HOST_MODULE
+# For device libraries, move LOCAL_LDLIBS references to my_shared_libraries. We
+# no longer need to use my_ldlibs to pick up NDK prebuilt libraries since we're
+# linking my_shared_libraries by full path now.
+my_allowed_ldlibs :=
+
+# Sort ldlibs and ldflags between -l and other linker flags
+# We'll do this again later, since there are still changes happening, but that's fine.
+my_ldlib_flags := $(my_ldflags) $(my_ldlibs)
+my_ldlibs := $(filter -l%,$(my_ldlib_flags))
+my_ldflags := $(filter-out -l%,$(my_ldlib_flags))
+my_ldlib_flags :=
+
+# Move other ldlibs back to shared libraries
+my_shared_libraries += $(patsubst -l%,lib%,$(filter-out $(my_allowed_ldlibs),$(my_ldlibs)))
+my_ldlibs := $(filter $(my_allowed_ldlibs),$(my_ldlibs))
+endif
+
+ifdef LOCAL_SDK_VERSION
+  my_all_ndk_libraries := \
+      $(NDK_MIGRATED_LIBS) $(addprefix lib,$(NDK_PREBUILT_SHARED_LIBRARIES))
+  my_ndk_shared_libraries := \
+      $(filter $(my_all_ndk_libraries),\
+        $(my_shared_libraries) $(my_system_shared_libraries))
+
+  my_shared_libraries := \
+      $(filter-out $(my_all_ndk_libraries),$(my_shared_libraries))
+  my_system_shared_libraries := \
+      $(filter-out $(my_all_ndk_libraries),$(my_system_shared_libraries))
+endif
+
 # MinGW spits out warnings about -fPIC even for -fpie?!) being ignored because
 # all code is position independent, and then those warnings get promoted to
 # errors.
+ifneq ($(LOCAL_NO_PIC),true)
 ifneq ($($(my_prefix)OS),windows)
 ifneq ($(filter EXECUTABLES NATIVE_TESTS,$(LOCAL_MODULE_CLASS)),)
 my_cflags += -fpie
@@ -192,6 +285,7 @@
 my_cflags += -fPIC
 endif
 endif
+endif
 
 ifdef LOCAL_IS_HOST_MODULE
 my_src_files += $(LOCAL_SRC_FILES_$($(my_prefix)OS)) $(LOCAL_SRC_FILES_$($(my_prefix)OS)_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH))
@@ -231,6 +325,20 @@
 my_clang := $(strip $(LOCAL_CLANG_$($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)))
 endif
 
+# if custom toolchain is in use, default is not to use clang, if not explicitly required
+ifneq ($(my_cc)$(my_cxx),)
+    ifeq ($(my_clang),)
+        my_clang := false
+    endif
+endif
+# Issue warning if LOCAL_CLANG* is set to false and the local makefile is not found
+# in the exception project list.
+ifeq ($(my_clang),false)
+    ifeq ($(call find_in_local_clang_exception_projects,$(LOCAL_MODULE_MAKEFILE)),)
+        $(error $(LOCAL_MODULE_MAKEFILE): $(LOCAL_MODULE): LOCAL_CLANG is set to false)
+    endif
+endif
+
 # clang is enabled by default for host builds
 # enable it unless we've specifically disabled clang above
 ifdef LOCAL_IS_HOST_MODULE
@@ -294,15 +402,17 @@
 ifneq ($(strip $(CUSTOM_$(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)LINKER)),)
   my_linker := $(CUSTOM_$(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)LINKER)
 else
-  my_linker := $($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_LINKER)
+  my_linker := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)LINKER)
 endif
 
 include $(BUILD_SYSTEM)/config_sanitizers.mk
 
+ifneq ($(LOCAL_NO_LIBCOMPILER_RT),true)
 # Add in libcompiler_rt for all regular device builds
 ifeq (,$(LOCAL_SDK_VERSION)$(WITHOUT_LIBCOMPILER_RT))
   my_static_libraries += $(COMPILER_RT_CONFIG_EXTRA_STATIC_LIBRARIES)
 endif
+endif
 
 ####################################################
 ## Add FDO flags if FDO is turned on and supported
@@ -337,29 +447,33 @@
 ###########################################################
 ifndef LOCAL_IS_HOST_MODULE
 ifdef LOCAL_SDK_VERSION
-my_target_project_includes :=
-my_target_c_includes := $(my_ndk_stl_include_path) $(my_ndk_sysroot_include)
+my_target_global_c_includes :=
+my_target_global_c_system_includes := $(my_ndk_stl_include_path) $(my_ndk_sysroot_include)
 my_target_global_cppflags := $(my_ndk_stl_cppflags)
 else
-my_target_project_includes := $($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_PROJECT_INCLUDES)
-my_target_c_includes := $($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_C_INCLUDES)
+my_target_global_c_includes := $(SRC_HEADERS) \
+    $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)PROJECT_INCLUDES) \
+    $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)C_INCLUDES)
+my_target_global_c_system_includes := $(SRC_SYSTEM_HEADERS) \
+    $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)PROJECT_SYSTEM_INCLUDES) \
+    $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)C_SYSTEM_INCLUDES)
 my_target_global_cppflags :=
 endif # LOCAL_SDK_VERSION
 
 ifeq ($(my_clang),true)
-my_target_global_cflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_TARGET_GLOBAL_CFLAGS)
-my_target_global_conlyflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_TARGET_GLOBAL_CONLYFLAGS)
-my_target_global_cppflags += $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_TARGET_GLOBAL_CPPFLAGS)
-my_target_global_ldflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_TARGET_GLOBAL_LDFLAGS)
+my_target_global_cflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_CFLAGS)
+my_target_global_conlyflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_CONLYFLAGS)
+my_target_global_cppflags += $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_CPPFLAGS)
+my_target_global_ldflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_LDFLAGS)
 else
-my_target_global_cflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_GLOBAL_CFLAGS)
-my_target_global_conlyflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_GLOBAL_CONLYFLAGS)
-my_target_global_cppflags += $($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_GLOBAL_CPPFLAGS)
-my_target_global_ldflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_GLOBAL_LDFLAGS)
+my_target_global_cflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)GLOBAL_CFLAGS)
+my_target_global_conlyflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)GLOBAL_CONLYFLAGS)
+my_target_global_cppflags += $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)GLOBAL_CPPFLAGS)
+my_target_global_ldflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)GLOBAL_LDFLAGS)
 endif # my_clang
 
-$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_TARGET_PROJECT_INCLUDES := $(my_target_project_includes)
-$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_TARGET_C_INCLUDES := $(my_target_c_includes)
+$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_GLOBAL_C_INCLUDES := $(my_target_global_c_includes)
+$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_GLOBAL_C_SYSTEM_INCLUDES := $(my_target_global_c_system_includes)
 $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_TARGET_GLOBAL_CFLAGS := $(my_target_global_cflags)
 $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_TARGET_GLOBAL_CONLYFLAGS := $(my_target_global_conlyflags)
 $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_TARGET_GLOBAL_CPPFLAGS := $(my_target_global_cppflags)
@@ -367,21 +481,27 @@
 
 else # LOCAL_IS_HOST_MODULE
 
+my_host_global_c_includes := $(SRC_HEADERS) \
+    $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)PROJECT_INCLUDES) \
+    $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)C_INCLUDES)
+my_host_global_c_system_includes := $(SRC_SYSTEM_HEADERS) \
+    $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)PROJECT_SYSTEM_INCLUDES) \
+    $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)C_SYSTEM_INCLUDES)
+
 ifeq ($(my_clang),true)
 my_host_global_cflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_CFLAGS)
 my_host_global_conlyflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_CONLYFLAGS)
 my_host_global_cppflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_CPPFLAGS)
 my_host_global_ldflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)CLANG_$(my_prefix)GLOBAL_LDFLAGS)
-my_host_c_includes := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)C_INCLUDES)
 else
 my_host_global_cflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)GLOBAL_CFLAGS)
 my_host_global_conlyflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)GLOBAL_CONLYFLAGS)
 my_host_global_cppflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)GLOBAL_CPPFLAGS)
 my_host_global_ldflags := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)GLOBAL_LDFLAGS)
-my_host_c_includes := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)C_INCLUDES)
 endif # my_clang
 
-$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_HOST_C_INCLUDES := $(my_host_c_includes)
+$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_GLOBAL_C_INCLUDES := $(my_host_global_c_includes)
+$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_GLOBAL_C_SYSTEM_INCLUDES := $(my_host_global_c_system_includes)
 $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_HOST_GLOBAL_CFLAGS := $(my_host_global_cflags)
 $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_HOST_GLOBAL_CONLYFLAGS := $(my_host_global_conlyflags)
 $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_HOST_GLOBAL_CPPFLAGS := $(my_host_global_cppflags)
@@ -408,9 +528,9 @@
 endif
 
 ifeq ($(my_clang),true)
-    my_coverage_lib := $($(LOCAL_2ND_ARCH_VAR_PREFIX)TARGET_LIBPROFILE_RT)
+    my_coverage_lib := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)LIBPROFILE_RT)
 else
-    my_coverage_lib := $(call intermediates-dir-for,STATIC_LIBRARIES,libgcov,,,$(LOCAL_2ND_ARCH_VAR_PREFIX))/libgcov.a
+    my_coverage_lib := $(call intermediates-dir-for,STATIC_LIBRARIES,libgcov,$(filter AUX,$(my_kind)),,$(LOCAL_2ND_ARCH_VAR_PREFIX))/libgcov.a
 endif
 
 $(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_TARGET_COVERAGE_LIB := $(my_coverage_lib)
@@ -438,7 +558,7 @@
 ifneq ($(strip $(LOCAL_IS_HOST_MODULE)),)
   my_syntax_arch := host
 else
-  my_syntax_arch := $(TARGET_$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)
+  my_syntax_arch := $($(my_prefix)$(LOCAL_2ND_ARCH_VAR_PREFIX)ARCH)
 endif
 
 ifeq ($(strip $(my_cc)),)
@@ -1135,34 +1255,6 @@
 endif
 
 
-####################################################
-## For NDK-built libraries, move LOCAL_SHARED_LIBRARY
-## references to my_ldlibs, so that we use the NDK
-## prebuilt library and headers for linking.
-####################################################
-ifndef LOCAL_IS_HOST_MODULE
-my_allowed_ldlibs :=
-ifdef LOCAL_SDK_VERSION
-  my_ndk_shared_libraries := $(filter $(addprefix lib,$(NDK_PREBUILT_SHARED_LIBRARIES)),$(my_shared_libraries))
-  my_shared_libraries := $(filter-out $(my_ndk_shared_libraries),$(my_shared_libraries))
-  my_ldlibs += $(patsubst lib%,-l%,$(my_ndk_shared_libraries))
-  my_ndk_shared_libraries :=
-  my_allowed_ldlibs := $(addprefix -l,$(NDK_PREBUILT_SHARED_LIBRARIES))
-endif
-
-# Sort ldlibs and ldflags between -l and other linker flags
-# We'll do this again later, since there are still changes happening, but that's fine.
-my_ldlib_flags := $(my_ldflags) $(my_ldlibs)
-my_ldlibs := $(filter -l%,$(my_ldlib_flags))
-my_ldflags := $(filter-out -l%,$(my_ldlib_flags))
-my_ldlib_flags :=
-
-# Move other ldlibs back to shared libraries
-my_shared_libraries += $(patsubst -l%,lib%,$(filter-out $(my_allowed_ldlibs),$(my_ldlibs)))
-my_ldlibs := $(filter $(my_allowed_ldlibs),$(my_ldlibs))
-endif
-
-
 ##########################################################
 ## Set up installed module dependency
 ## We cannot compute the full path of the LOCAL_SHARED_LIBRARIES for
@@ -1193,9 +1285,9 @@
 import_includes := $(intermediates)/import_includes
 import_includes_deps := $(strip \
     $(foreach l, $(installed_shared_library_module_names), \
-      $(call intermediates-dir-for,SHARED_LIBRARIES,$(l),$(LOCAL_IS_HOST_MODULE),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))/export_includes) \
+      $(call intermediates-dir-for,SHARED_LIBRARIES,$(l),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))/export_includes) \
     $(foreach l, $(my_static_libraries) $(my_whole_static_libraries), \
-      $(call intermediates-dir-for,STATIC_LIBRARIES,$(l),$(LOCAL_IS_HOST_MODULE),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))/export_includes))
+      $(call intermediates-dir-for,STATIC_LIBRARIES,$(l),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))/export_includes))
 $(import_includes): PRIVATE_IMPORT_EXPORT_INCLUDES := $(import_includes_deps)
 $(import_includes) : $(import_includes_deps)
 	@echo Import includes file: $@
@@ -1208,7 +1300,6 @@
 	$(hide) touch $@
 endif
 
-
 ####################################################
 ## Verify that NDK-built libraries only link against
 ## other NDK-built libraries
@@ -1224,11 +1315,11 @@
 endif
 my_link_type_deps := $(strip \
    $(foreach l,$(my_whole_static_libraries) $(my_static_libraries), \
-     $(call intermediates-dir-for,STATIC_LIBRARIES,$(l),$(LOCAL_IS_HOST_MODULE),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))/link_type))
+     $(call intermediates-dir-for,STATIC_LIBRARIES,$(l),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))/link_type))
 ifneq ($(LOCAL_MODULE_CLASS),STATIC_LIBRARIES)
 my_link_type_deps += $(strip \
    $(foreach l,$(my_shared_libraries), \
-     $(call intermediates-dir-for,SHARED_LIBRARIES,$(l),$(LOCAL_IS_HOST_MODULE),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))/link_type))
+     $(call intermediates-dir-for,SHARED_LIBRARIES,$(l),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))/link_type))
 endif
 $(my_link_type): PRIVATE_DEPS := $(my_link_type_deps)
 $(my_link_type): PRIVATE_MODULE := $(LOCAL_MODULE)
@@ -1239,7 +1330,7 @@
 ifdef my_link_type_deps
 	$(hide) for f in $(PRIVATE_DEPS); do \
 	  grep -qE '^$(PRIVATE_ALLOWED_TYPES)$$' $$f || \
-	    $(call echo-warning,"$(PRIVATE_MAKEFILE): $(PRIVATE_MODULE) ($(PRIVATE_LINK_TYPE)) should not link to $$(basename $${f%_intermediates/link_type}) ($$(cat $$f))"); \
+	    ($(call echo-error,"$(PRIVATE_MAKEFILE): $(PRIVATE_MODULE) ($(PRIVATE_LINK_TYPE)) should not link to $$(basename $${f%_intermediates/link_type}) ($$(cat $$f))"); exit 1) \
 	done
 endif
 	$(hide) echo $(PRIVATE_LINK_TYPE) >$@
@@ -1355,7 +1446,19 @@
     $(addprefix $(my_ndk_sysroot_lib)/, \
         $(addsuffix $(so_suffix), $(my_system_shared_libraries)))
 
-built_shared_libraries += $(my_system_shared_libraries_fullpath)
+# We need to preserve the ordering of LOCAL_SHARED_LIBRARIES regardless of
+# whether the libs are generated or prebuilt, so we simply can't split into two
+# lists and use addprefix.
+my_ndk_shared_libraries_fullpath := \
+    $(foreach _lib,$(my_ndk_shared_libraries),\
+        $(if $(filter $(NDK_MIGRATED_LIBS),$(_lib)),\
+            $(my_built_ndk_libs)/$(_lib)$(so_suffix),\
+            $(my_ndk_sysroot_lib)/$(_lib)$(so_suffix)))
+
+built_shared_libraries += \
+    $(my_ndk_shared_libraries_fullpath) \
+    $(my_system_shared_libraries_fullpath) \
+
 else
 built_shared_libraries := \
     $(addprefix $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)OUT_INTERMEDIATE_LIBRARIES)/, \
@@ -1374,7 +1477,7 @@
 built_static_libraries := \
     $(foreach lib,$(my_static_libraries), \
       $(call intermediates-dir-for, \
-        STATIC_LIBRARIES,$(lib),$(LOCAL_IS_HOST_MODULE),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))/$(lib)$(a_suffix))
+        STATIC_LIBRARIES,$(lib),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))/$(lib)$(a_suffix))
 
 ifdef LOCAL_SDK_VERSION
 built_static_libraries += $(my_ndk_stl_static_lib)
@@ -1383,7 +1486,7 @@
 built_whole_libraries := \
     $(foreach lib,$(my_whole_static_libraries), \
       $(call intermediates-dir-for, \
-        STATIC_LIBRARIES,$(lib),$(LOCAL_IS_HOST_MODULE),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))/$(lib)$(a_suffix))
+        STATIC_LIBRARIES,$(lib),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))/$(lib)$(a_suffix))
 
 # We don't care about installed static libraries, since the
 # libraries have already been linked into the module at that point.
@@ -1562,15 +1665,15 @@
 # Headers exported by whole static libraries are also exported by this library.
 export_include_deps := $(strip \
    $(foreach l,$(my_whole_static_libraries), \
-     $(call intermediates-dir-for,STATIC_LIBRARIES,$(l),$(LOCAL_IS_HOST_MODULE),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))/export_includes))
+     $(call intermediates-dir-for,STATIC_LIBRARIES,$(l),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))/export_includes))
 # Re-export requested headers from shared libraries.
 export_include_deps += $(strip \
    $(foreach l,$(LOCAL_EXPORT_SHARED_LIBRARY_HEADERS), \
-     $(call intermediates-dir-for,SHARED_LIBRARIES,$(l),$(LOCAL_IS_HOST_MODULE),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))/export_includes))
+     $(call intermediates-dir-for,SHARED_LIBRARIES,$(l),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))/export_includes))
 # Re-export requested headers from static libraries.
 export_include_deps += $(strip \
    $(foreach l,$(LOCAL_EXPORT_STATIC_LIBRARY_HEADERS), \
-     $(call intermediates-dir-for,STATIC_LIBRARIES,$(l),$(LOCAL_IS_HOST_MODULE),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))/export_includes))
+     $(call intermediates-dir-for,STATIC_LIBRARIES,$(l),$(my_kind),,$(LOCAL_2ND_ARCH_VAR_PREFIX),$(my_host_cross))/export_includes))
 $(export_includes): PRIVATE_REEXPORTED_INCLUDES := $(export_include_deps)
 # By adding $(my_generated_sources) it makes sure the headers get generated
 # before any dependent source files get compiled.
diff --git a/core/clang/config.mk b/core/clang/config.mk
index 4685514..7113892 100644
--- a/core/clang/config.mk
+++ b/core/clang/config.mk
@@ -143,4 +143,24 @@
 FORCE_BUILD_LLVM_COMPONENTS := true
 endif
 
+# A list of projects that are allowed to set LOCAL_CLANG to false.
+# INTERNAL_LOCAL_CLANG_EXCEPTION_PROJECTS is defined later in other config.mk.
+LOCAL_CLANG_EXCEPTION_PROJECTS = \
+  bionic/tests/ \
+  device/huawei/angler/ \
+  device/lge/bullhead/ \
+  external/gentoo/integration/ \
+  external/valgrind/ \
+  hardware/qcom/ \
+  $(INTERNAL_LOCAL_CLANG_EXCEPTION_PROJECTS)
+
+# Find $1 in the exception project list.
+define find_in_local_clang_exception_projects
+$(subst $(space),, \
+  $(foreach project,$(LOCAL_CLANG_EXCEPTION_PROJECTS), \
+    $(if $(filter $(project)%,$(1)),$(project)) \
+  ) \
+)
+endef
+
 include $(BUILD_SYSTEM)/clang/tidy.mk
diff --git a/core/clang/tidy.mk b/core/clang/tidy.mk
index 019e6f0..860257f 100644
--- a/core/clang/tidy.mk
+++ b/core/clang/tidy.mk
@@ -15,32 +15,34 @@
 #
 
 # Most Android source files are not clang-tidy clean yet.
-# Global tidy checks include only google* and misc-macro-parentheses,
-# but not google-readability*.
+# Global tidy checks include only google*, performance*,
+# and misc-macro-parentheses, but not google-readability*
+# or google-runtime-references.
 DEFAULT_GLOBAL_TIDY_CHECKS := \
-  -*,google*,-google-readability*,misc-macro-parentheses
+  $(subst $(space),, \
+    -*,google*,performance*,misc-macro-parentheses \
+    ,-google-readability*,-google-runtime-references \
+  )
 
-# Disable style rules usually not followed by external projects.
+# There are too many clang-tidy warnings in external and vendor projects.
+# Enable only some google checks for these projects.
+DEFAULT_EXTERNAL_VENDOR_TIDY_CHECKS := \
+  $(subst $(space),, \
+    -*,google*,-google-build-using-namespace \
+    ,-google-readability*,-google-runtime-references \
+    ,-google-explicit-constructor,-google-runtime-int \
+  )
+
 # Every word in DEFAULT_LOCAL_TIDY_CHECKS list has the following format:
-#   <local_path_prefix>:,<tidy-check-pattern>
-# The tidy-check-patterns of all matching local_path_prefixes will be used.
-# For example, external/google* projects will have:
-#   ,-google-build-using-namespace,-google-explicit-constructor
-#   ,-google-runtime-int,-misc-macro-parentheses,
-#   ,google-runtime-int,misc-macro-parentheses
-# where google-runtime-int and misc-macro-parentheses are enabled at the end.
+#   <local_path_prefix>:,<tidy-checks>
+# The last matched local_path_prefix should be the most specific to be used.
 DEFAULT_LOCAL_TIDY_CHECKS := \
-  external/:,-google-build-using-namespace \
-  external/:,-google-explicit-constructor,-google-runtime-int \
-  external/:,-misc-macro-parentheses \
-  external/google:,google-runtime-int,misc-macro-parentheses \
-  external/webrtc/:,google-runtime-int \
-  hardware/qcom:,-google-build-using-namespace \
-  hardware/qcom:,-google-explicit-constructor,-google-runtime-int \
-  vendor/lge:,-google-build-using-namespace \
-  vendor/lge:,-google-explicit-constructor,-google-runtime-int \
-  vendor/widevine:,-google-build-using-namespace \
-  vendor/widevine:,-google-explicit-constructor,-google-runtime-int \
+  external/:$(DEFAULT_EXTERNAL_VENDOR_TIDY_CHECKS) \
+  external/google:$(DEFAULT_GLOBAL_TIDY_CHECKS) \
+  external/webrtc:$(DEFAULT_GLOBAL_TIDY_CHECKS) \
+  hardware/qcom:$(DEFAULT_EXTERNAL_VENDOR_TIDY_CHECKS) \
+  vendor/:$(DEFAULT_EXTERNAL_VENDOR_TIDY_CHECKS) \
+  vendor/google:$(DEFAULT_GLOBAL_TIDY_CHECKS) \
 
 # Returns 2nd word of $(1) if $(2) has prefix of the 1st word of $(1).
 define find_default_local_tidy_check2
@@ -52,11 +54,11 @@
 $(call find_default_local_tidy_check2,$(subst :,$(space),$(1)),$(2))
 endef
 
-# Returns concatenated tidy check patterns from the
-# DEFAULT_GLOBAL_TIDY_CHECKS and all matched patterns
-# in DEFAULT_LOCAL_TIDY_CHECKS based on given directory path $(1).
+# Returns the default tidy check list for local project path $(1).
+# Match $(1) with all patterns in DEFAULT_LOCAL_TIDY_CHECKS and use the last
+# most specific pattern.
 define default_global_tidy_checks
-$(subst $(space),, \
+$(lastword \
   $(DEFAULT_GLOBAL_TIDY_CHECKS) \
   $(foreach pattern,$(DEFAULT_LOCAL_TIDY_CHECKS), \
     $(call find_default_local_tidy_check,$(pattern),$(1)) \
diff --git a/core/clang/versions.mk b/core/clang/versions.mk
index ef28880..5988eff 100644
--- a/core/clang/versions.mk
+++ b/core/clang/versions.mk
@@ -1,5 +1,5 @@
 ## Clang/LLVM release versions.
 
 LLVM_RELEASE_VERSION := 3.8
-LLVM_PREBUILTS_VERSION ?= clang-2812033
+LLVM_PREBUILTS_VERSION ?= clang-3016494
 LLVM_PREBUILTS_BASE ?= prebuilts/clang/host
diff --git a/core/cleanbuild.mk b/core/cleanbuild.mk
index f61e3f7..de6a5eb 100644
--- a/core/cleanbuild.mk
+++ b/core/cleanbuild.mk
@@ -152,16 +152,12 @@
 
 previous_build_config_file := $(PRODUCT_OUT)/previous_build_config.mk
 
-# A change in the list of aapt configs warrants an installclean, too.
-aapt_config_list := $(strip $(PRODUCT_AAPT_CONFIG) $(PRODUCT_AAPT_PREF_CONFIG))
-
 current_build_config := \
-    $(TARGET_PRODUCT)-$(TARGET_BUILD_VARIANT)-{$(aapt_config_list)}
+    $(TARGET_PRODUCT)-$(TARGET_BUILD_VARIANT)
 current_sanitize_target := $(strip $(SANITIZE_TARGET))
 ifeq (,$(current_sanitize_target))
   current_sanitize_target := false
 endif
-aapt_config_list :=
 force_installclean := false
 force_objclean := false
 
@@ -236,7 +232,6 @@
 	$(PRODUCT_OUT)/kernel \
 	$(PRODUCT_OUT)/data \
 	$(PRODUCT_OUT)/skin \
-	$(PRODUCT_OUT)/obj/APPS \
 	$(PRODUCT_OUT)/obj/NOTICE_FILES \
 	$(PRODUCT_OUT)/obj/PACKAGING \
 	$(PRODUCT_OUT)/recovery \
@@ -244,8 +239,6 @@
 	$(PRODUCT_OUT)/system \
 	$(PRODUCT_OUT)/vendor \
 	$(PRODUCT_OUT)/oem \
-	$(PRODUCT_OUT)/dex_bootjars \
-	$(PRODUCT_OUT)/obj/JAVA_LIBRARIES \
 	$(PRODUCT_OUT)/obj/FAKE
 
 # The files/dirs to delete during a dataclean, which removes any files
diff --git a/core/clear_vars.mk b/core/clear_vars.mk
index 7733a3e..55d0021 100644
--- a/core/clear_vars.mk
+++ b/core/clear_vars.mk
@@ -142,7 +142,9 @@
 LOCAL_PROTOC_FLAGS:=
 LOCAL_PROTO_JAVA_OUTPUT_PARAMS:=
 LOCAL_NO_CRT:=
+LOCAL_NO_LIBCOMPILER_RT:=
 LOCAL_NO_LIBGCC:=
+LOCAL_NO_PIC:=
 LOCAL_PROPRIETARY_MODULE:=
 LOCAL_OEM_MODULE:=
 LOCAL_ODM_MODULE:=
@@ -192,13 +194,16 @@
 LOCAL_NOSANITIZE:=
 LOCAL_DBUS_PROXY_PREFIX:=
 LOCAL_INIT_RC:=
+LOCAL_MODULE_SYMLINKS:=
 LOCAL_MODULE_HOST_OS:=
 LOCAL_NOTICE_FILE:=
 # Used to replace the installed file of a presigned prebuilt apk in PDK fusion build,
 # to avoid installing the presigned apks with classes.dex unstripped.
 LOCAL_REPLACE_PREBUILT_APK_INSTALLED:=
 LOCAL_NDK_VERSION:=current
+LOCAL_COPY_TO_INTERMEDIATE_LIBRARIES:=
 LOCAL_LOGTAGS_FILES:=
+LOCAL_RECORDED_MODULE_TYPE:=
 
 # arch specific variables
 LOCAL_SRC_FILES_$(TARGET_ARCH):=
@@ -355,9 +360,21 @@
 LOCAL_CLANG_64:=
 LOCAL_INIT_RC_32:=
 LOCAL_INIT_RC_64:=
+LOCAL_MODULE_SYMLINKS_32:=
+LOCAL_MODULE_SYMLINKS_64:=
 LOCAL_JAVA_LANGUAGE_VERSION:=
 LOCAL_CTS_GTEST_LIST_EXECUTABLE:=
 
+LOCAL_IS_AUX_MODULE :=
+LOCAL_AUX_TOOLCHAIN :=
+LOCAL_AUX_OS :=
+LOCAL_AUX_ARCH :=
+LOCAL_AUX_SUBARCH :=
+LOCAL_AUX_CPU :=
+LOCAL_AUX_OS_VARIANT :=
+LOCAL_CUSTOM_BUILD_STEP_INPUT:=
+LOCAL_CUSTOM_BUILD_STEP_OUTPUT:=
+
 # Trim MAKEFILE_LIST so that $(call my-dir) doesn't need to
 # iterate over thousands of entries every time.
 # Leave the current makefile to make sure we don't break anything
diff --git a/core/config.mk b/core/config.mk
index b179881..4f4b742 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -39,17 +39,17 @@
 # TODO: Enforce some kind of layering; only add include paths
 #       when a module links against a particular library.
 # TODO: See if we can remove most of these from the global list.
-SRC_HEADERS := \
-	$(TOPDIR)system/core/include \
-	$(TOPDIR)system/media/audio/include \
-	$(TOPDIR)hardware/libhardware/include \
-	$(TOPDIR)hardware/libhardware_legacy/include \
-	$(TOPDIR)hardware/ril/include \
-	$(TOPDIR)libnativehelper/include \
-	$(TOPDIR)frameworks/native/include \
-	$(TOPDIR)frameworks/native/opengl/include \
-	$(TOPDIR)frameworks/av/include \
-	$(TOPDIR)frameworks/base/include
+SRC_SYSTEM_HEADERS := \
+	$(wildcard system/core/include) \
+	$(wildcard system/media/audio/include) \
+	$(wildcard hardware/libhardware/include) \
+	$(wildcard hardware/libhardware_legacy/include) \
+	$(wildcard hardware/ril/include) \
+	$(wildcard libnativehelper/include) \
+	$(wildcard frameworks/native/include) \
+	$(wildcard frameworks/native/opengl/include) \
+	$(wildcard frameworks/av/include) \
+	$(wildcard frameworks/base/include)
 SRC_TARGET_DIR := $(TOPDIR)build/target
 SRC_API_DIR := $(TOPDIR)prebuilts/sdk/api
 SRC_SYSTEM_API_DIR := $(TOPDIR)prebuilts/sdk/system-api
@@ -70,6 +70,8 @@
 BUILD_HOST_STATIC_LIBRARY:= $(BUILD_SYSTEM)/host_static_library.mk
 BUILD_HOST_SHARED_LIBRARY:= $(BUILD_SYSTEM)/host_shared_library.mk
 BUILD_STATIC_LIBRARY:= $(BUILD_SYSTEM)/static_library.mk
+BUILD_AUX_STATIC_LIBRARY:= $(BUILD_SYSTEM)/aux_static_library.mk
+BUILD_AUX_EXECUTABLE:= $(BUILD_SYSTEM)/aux_executable.mk
 BUILD_SHARED_LIBRARY:= $(BUILD_SYSTEM)/shared_library.mk
 BUILD_EXECUTABLE:= $(BUILD_SYSTEM)/executable.mk
 BUILD_HOST_EXECUTABLE:= $(BUILD_SYSTEM)/host_executable.mk
@@ -470,6 +472,9 @@
 DX := $(HOST_OUT_EXECUTABLES)/dx
 MAINDEXCLASSES := $(HOST_OUT_EXECUTABLES)/mainDexClasses
 
+SOONG_ZIP := $(SOONG_HOST_OUT_EXECUTABLES)/soong_zip
+ZIP2ZIP := $(SOONG_HOST_OUT_EXECUTABLES)/zip2zip
+
 # Always use prebuilts for ckati and makeparallel
 prebuilt_build_tools := prebuilts/build-tools
 prebuilt_build_tools_bin := $(prebuilt_build_tools)/$(HOST_PREBUILT_TAG)/bin
@@ -645,8 +650,10 @@
 
 GLOBAL_CLANG_CFLAGS_NO_OVERRIDE := \
     -Werror=address-of-temporary \
-    -Werror=null-dereference \
     -Werror=return-type \
+    # Bug: http://b/29823425 Disable -Wnull-dereference until the new cases
+    # detected by this warning in Clang r271374 are fixed.
+    #-Werror=null-dereference \
 
 GLOBAL_CPPFLAGS_NO_OVERRIDE :=
 
@@ -663,28 +670,34 @@
 HOST_GLOBAL_LD_DIRS := -L$(HOST_OUT_INTERMEDIATE_LIBRARIES)
 TARGET_GLOBAL_LD_DIRS := -L$(TARGET_OUT_INTERMEDIATE_LIBRARIES)
 
-HOST_PROJECT_INCLUDES:= $(SRC_HEADERS) $(HOST_OUT_HEADERS)
-TARGET_PROJECT_INCLUDES:= $(SRC_HEADERS) $(TARGET_OUT_HEADERS) \
+HOST_PROJECT_INCLUDES :=
+HOST_PROJECT_SYSTEM_INCLUDES := $(HOST_OUT_HEADERS)
+TARGET_PROJECT_INCLUDES :=
+TARGET_PROJECT_SYSTEM_INCLUDES := $(TARGET_OUT_HEADERS) \
 		$(TARGET_DEVICE_KERNEL_HEADERS) $(TARGET_BOARD_KERNEL_HEADERS) \
 		$(TARGET_PRODUCT_KERNEL_HEADERS)
 
 ifdef TARGET_2ND_ARCH
 $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_GLOBAL_LD_DIRS := -L$($(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_OUT_INTERMEDIATE_LIBRARIES)
 $(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_PROJECT_INCLUDES := $(TARGET_PROJECT_INCLUDES)
+$(TARGET_2ND_ARCH_VAR_PREFIX)TARGET_PROJECT_SYSTEM_INCLUDES := $(TARGET_PROJECT_SYSTEM_INCLUDES)
 endif
 
 ifdef HOST_2ND_ARCH
 $(HOST_2ND_ARCH_VAR_PREFIX)HOST_GLOBAL_LD_DIRS := -L$($(HOST_2ND_ARCH_VAR_PREFIX)HOST_OUT_INTERMEDIATE_LIBRARIES)
 $(HOST_2ND_ARCH_VAR_PREFIX)HOST_PROJECT_INCLUDES := $(HOST_PROJECT_INCLUDES)
+$(HOST_2ND_ARCH_VAR_PREFIX)HOST_PROJECT_SYSTEM_INCLUDES := $(HOST_PROJECT_SYSTEM_INCLUDES)
 endif
 
 ifdef HOST_CROSS_OS
 HOST_CROSS_GLOBAL_LD_DIRS := -L$(HOST_CROSS_OUT_INTERMEDIATE_LIBRARIES)
-HOST_CROSS_PROJECT_INCLUDES:= $(SRC_HEADERS) $(HOST_CROSS_OUT_HEADERS)
+HOST_CROSS_PROJECT_INCLUDES :=
+HOST_CROSS_PROJECT_SYSTEM_INCLUDES := $(HOST_CROSS_OUT_HEADERS)
 
 ifdef HOST_CROSS_2ND_ARCH
 $(HOST_CROSS_2ND_ARCH_VAR_PREFIX)HOST_CROSS_GLOBAL_LD_DIRS := -L$($(HOST_CROSS_2ND_ARCH_VAR_PREFIX)HOST_CROSS_OUT_INTERMEDIATE_LIBRARIES)
-$(HOST_CROSS_2ND_ARCH_VAR_PREFIX)HOST_CROSS_PROJECT_INCLUDES:= $(SRC_HEADERS) $($(HOST_CROSS_2ND_ARCH_VAR_PREFIX)HOST_CROSS_OUT_HEADERS)
+$(HOST_CROSS_2ND_ARCH_VAR_PREFIX)HOST_CROSS_PROJECT_INCLUDES:= $(HOST_CROSS_PROJECT_INCLUDES)
+$(HOST_CROSS_2ND_ARCH_VAR_PREFIX)HOST_CROSS_PROJECT_SYSTEM_INCLUDES:= $(HOST_CROSS_PROJECT_SYSTEM_INCLUDES)
 endif
 endif
 
diff --git a/core/config_sanitizers.mk b/core/config_sanitizers.mk
index 9602626..ac3e4fc 100644
--- a/core/config_sanitizers.mk
+++ b/core/config_sanitizers.mk
@@ -51,6 +51,7 @@
 # Don't apply sanitizers to NDK code.
 ifdef LOCAL_SDK_VERSION
   my_sanitize :=
+  my_global_sanitize :=
 endif
 
 # Never always wins.
@@ -156,9 +157,13 @@
       my_ldflags += -Wl,--as-needed
     endif
 
-    my_linker := $($(LOCAL_2ND_ARCH_VAR_PREFIX)ADDRESS_SANITIZER_LINKER)
-    # Make sure linker_asan get installed.
-    $(LOCAL_INSTALLED_MODULE) : | $(PRODUCT_OUT)$($(LOCAL_2ND_ARCH_VAR_PREFIX)ADDRESS_SANITIZER_LINKER)
+    ifeq ($(LOCAL_MODULE_CLASS),EXECUTABLES)
+      ifneq ($(LOCAL_FORCE_STATIC_EXECUTABLE),true)
+        my_linker := $($(LOCAL_2ND_ARCH_VAR_PREFIX)ADDRESS_SANITIZER_LINKER)
+        # Make sure linker_asan get installed.
+        $(LOCAL_INSTALLED_MODULE) : | $(PRODUCT_OUT)$($(LOCAL_2ND_ARCH_VAR_PREFIX)ADDRESS_SANITIZER_LINKER)
+      endif
+    endif
   endif
 endif
 
diff --git a/core/copy_headers.mk b/core/copy_headers.mk
index 7d5a5d9..417a76c 100644
--- a/core/copy_headers.mk
+++ b/core/copy_headers.mk
@@ -1,6 +1,7 @@
 ###########################################################
 ## Copy headers to the install tree
 ###########################################################
+$(call record-module-type,COPY_HEADERS)
 ifneq ($(strip $(LOCAL_IS_HOST_MODULE)),)
   my_prefix := HOST_
 else
diff --git a/core/definitions.mk b/core/definitions.mk
index 5194918..d1284e6 100644
--- a/core/definitions.mk
+++ b/core/definitions.mk
@@ -92,9 +92,13 @@
 
 # Display names for various build targets
 TARGET_DISPLAY := target
+AUX_DISPLAY := aux
 HOST_DISPLAY := host
 HOST_CROSS_DISPLAY := host cross
 
+# All installed initrc files
+ALL_INIT_RC_INSTALLED_PAIRS :=
+
 ###########################################################
 ## Debugging; prints a variable list to stdout
 ###########################################################
@@ -138,40 +142,12 @@
 endef
 
 
-define _filter-soong-makefile
-$(if $(wildcard $(patsubst %/Android.mk,%/Android.bp,$(1))),\
-  $(info skipping $(1) ...)\
-    $(call _filter-soong-bpfile $(patsubst %/Android.mk,%/Android.bp,$(1))),\
-  $(1))
-endef
-
-define _filter-soong-bpfile
-$(if $(wildcard $(patsubst %/Android.bp,%/Android.soong.mk,$(1))),\
-  $(patsubst %/Android.bp,%/Android.soong.mk,$(1)))
-endef
-
-###########################################################
-## Remove any makefiles that are being handled by soong
-##
-## If passed an Android.mk file, returns the Android.mk file
-## if no Android.bp file exists and the same path.  If an
-## Android.bp file exists, or if passed an Android.bp file,
-## returns the Android.soong.mk file at the same path if it
-## exists, or nothing if it does not.
-###########################################################
-define filter-soong-makefiles
-$(sort $(foreach mk,$(1),\
-  $(if $(filter %/Android.bp,$(mk)),\
-    $(call _filter-soong-bpfile,$(mk)),\
-    $(call _filter-soong-makefile,$(mk)))))
-endef
-
 ###########################################################
 ## Retrieve a list of all makefiles immediately below some directory
 ###########################################################
 
 define all-makefiles-under
-$(call filter-soong-makefiles,$(wildcard $(1)/*/Android.mk $(1)/*/Android.bp))
+$(wildcard $(1)/*/Android.mk)
 endef
 
 ###########################################################
@@ -182,9 +158,8 @@
 # $(1): directory to search under
 # Ignores $(1)/Android.mk
 define first-makefiles-under
-$(call filter-soong-makefiles,\
-  $(shell build/tools/findleaves.py $(FIND_LEAVES_EXCLUDES) \
-        --mindepth=2 $(addprefix --dir=,$(1)) Android.bp Android.mk))
+$(shell build/tools/findleaves.py $(FIND_LEAVES_EXCLUDES) \
+        --mindepth=2 $(addprefix --dir=,$(1)) Android.mk)
 endef
 
 ###########################################################
@@ -204,9 +179,7 @@
 
 # $(1): List of directories to look for under this directory
 define all-named-subdir-makefiles
-$(call filter-soong-makefiles,\
-  $(wildcard $(addsuffix /Android.mk, $(addprefix $(call my-dir)/,$(1))))\
-  $(wildcard $(addsuffix /Android.bp, $(addprefix $(call my-dir)/,$(1)))))
+$(wildcard $(addsuffix /Android.mk, $(addprefix $(call my-dir)/,$(1))))
 endef
 
 ###########################################################
@@ -484,6 +457,28 @@
 $(if $(1),$(call reverse-list,$(wordlist 2,$(words $(1)),$(1)))) $(firstword $(1))
 endef
 
+define def-host-aux-target
+$(eval _idf_val_:=$(if $(strip $(LOCAL_IS_HOST_MODULE)),HOST,$(if $(strip $(LOCAL_IS_AUX_MODULE)),AUX,))) \
+$(_idf_val_)
+endef
+
+###########################################################
+## Returns correct _idfPrefix from the list:
+##   { HOST, HOST_CROSS, AUX, TARGET }
+###########################################################
+# the following rules checked in order:
+# ($1 is in {AUX, HOST_CROSS} => $1;
+# ($1 is empty) => TARGET;
+# ($2 is not empty) => HOST_CROSS;
+# => HOST;
+define find-idf-prefix
+$(strip \
+    $(eval _idf_pfx_:=$(strip $(filter AUX HOST_CROSS,$(1)))) \
+    $(eval _idf_pfx_:=$(if $(strip $(1)),$(if $(_idf_pfx_),$(_idf_pfx_),$(if $(strip $(2)),HOST_CROSS,HOST)),TARGET)) \
+    $(_idf_pfx_)
+)
+endef
+
 ###########################################################
 ## The intermediates directory.  Where object files go for
 ## a given target.  We could technically get away without
@@ -494,7 +489,7 @@
 
 # $(1): target class, like "APPS"
 # $(2): target name, like "NotePad"
-# $(3): if non-empty, this is a HOST target.
+# $(3): { HOST, HOST_CROSS, AUX, <empty (TARGET)>, <other non-empty (HOST)> }
 # $(4): if non-empty, force the intermediates to be COMMON
 # $(5): if non-empty, force the intermediates to be for the 2nd arch
 # $(6): if non-empty, force the intermediates to be for the host cross os
@@ -506,7 +501,7 @@
     $(eval _idfName := $(strip $(2))) \
     $(if $(_idfName),, \
         $(error $(LOCAL_PATH): Name not defined in call to intermediates-dir-for)) \
-    $(eval _idfPrefix := $(if $(strip $(3)),$(if $(strip $(6)),HOST_CROSS,HOST),TARGET)) \
+    $(eval _idfPrefix := $(call find-idf-prefix,$(3),$(6))) \
     $(eval _idf2ndArchPrefix := $(if $(strip $(5)),$(TARGET_2ND_ARCH_VAR_PREFIX))) \
     $(if $(filter $(_idfPrefix)-$(_idfClass),$(COMMON_MODULE_CLASSES))$(4), \
         $(eval _idfIntBase := $($(_idfPrefix)_OUT_COMMON_INTERMEDIATES)) \
@@ -531,7 +526,7 @@
         $(error $(LOCAL_PATH): LOCAL_MODULE_CLASS not defined before call to local-intermediates-dir)) \
     $(if $(strip $(LOCAL_MODULE)),, \
         $(error $(LOCAL_PATH): LOCAL_MODULE not defined before call to local-intermediates-dir)) \
-    $(call intermediates-dir-for,$(LOCAL_MODULE_CLASS),$(LOCAL_MODULE),$(LOCAL_IS_HOST_MODULE),$(1),$(2),$(3)) \
+    $(call intermediates-dir-for,$(LOCAL_MODULE_CLASS),$(LOCAL_MODULE),$(call def-host-aux-target),$(1),$(2),$(3)) \
 )
 endef
 
@@ -546,7 +541,7 @@
 
 # $(1): target class, like "APPS"
 # $(2): target name, like "NotePad"
-# $(3): if non-empty, this is a HOST target.
+# $(3): { HOST, HOST_CROSS, AUX, <empty (TARGET)>, <other non-empty (HOST)> }
 # $(4): if non-empty, force the generated sources to be COMMON
 define generated-sources-dir-for
 $(strip \
@@ -556,7 +551,7 @@
     $(eval _idfName := $(strip $(2))) \
     $(if $(_idfName),, \
         $(error $(LOCAL_PATH): Name not defined in call to generated-sources-dir-for)) \
-    $(eval _idfPrefix := $(if $(strip $(3)),HOST,TARGET)) \
+    $(eval _idfPrefix := $(call find-idf-prefix,$(3),)) \
     $(if $(filter $(_idfPrefix)-$(_idfClass),$(COMMON_MODULE_CLASSES))$(4), \
         $(eval _idfIntBase := $($(_idfPrefix)_OUT_COMMON_GEN)) \
       , \
@@ -576,7 +571,7 @@
         $(error $(LOCAL_PATH): LOCAL_MODULE_CLASS not defined before call to local-generated-sources-dir)) \
     $(if $(strip $(LOCAL_MODULE)),, \
         $(error $(LOCAL_PATH): LOCAL_MODULE not defined before call to local-generated-sources-dir)) \
-    $(call generated-sources-dir-for,$(LOCAL_MODULE_CLASS),$(LOCAL_MODULE),$(LOCAL_IS_HOST_MODULE),$(1)) \
+    $(call generated-sources-dir-for,$(LOCAL_MODULE_CLASS),$(LOCAL_MODULE),$(call def-host-aux-target),$(1)) \
 )
 endef
 
@@ -1180,19 +1175,27 @@
 	$(filter %.dbus-xml,$^)
 endef
 
+###########################################################
+## Helper to set include paths form transform-*-to-o
+###########################################################
+define c-includes
+$(addprefix -I , $(PRIVATE_C_INCLUDES)) \
+$$(cat $(PRIVATE_IMPORT_INCLUDES))\
+$(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),,\
+    $(addprefix -I ,\
+        $(filter-out $(PRIVATE_C_INCLUDES), \
+            $(PRIVATE_GLOBAL_C_INCLUDES))) \
+    $(addprefix -isystem ,\
+        $(filter-out $(PRIVATE_C_INCLUDES), \
+            $(PRIVATE_GLOBAL_C_SYSTEM_INCLUDES))))
+endef
 
 ###########################################################
 ## Commands for running gcc to compile a C++ file
 ###########################################################
 
 define transform-cpp-to-o-compiler-args
-	$(addprefix -I , $(PRIVATE_C_INCLUDES)) \
-	$$(cat $(PRIVATE_IMPORT_INCLUDES)) \
-	$(addprefix -isystem ,\
-	    $(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \
-	        $(filter-out $(PRIVATE_C_INCLUDES), \
-	            $(PRIVATE_TARGET_PROJECT_INCLUDES) \
-	            $(PRIVATE_TARGET_C_INCLUDES)))) \
+	$(c-includes) \
 	-c \
 	$(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \
 	    $(PRIVATE_TARGET_GLOBAL_CFLAGS) \
@@ -1216,12 +1219,12 @@
 ifneq (,$(filter 1 true,$(WITH_TIDY_ONLY)))
 define transform-cpp-to-o
 $(if $(PRIVATE_TIDY_CHECKS),
-  @echo "target tidy $(PRIVATE_ARM_MODE) C++: $<"
+  @echo "$($(PRIVATE_PREFIX)DISPLAY) tidy $(PRIVATE_ARM_MODE) C++: $<"
   $(clang-tidy-cpp))
 endef
 else
 define transform-cpp-to-o
-@echo "target $(PRIVATE_ARM_MODE) C++: $(PRIVATE_MODULE) <= $<"
+@echo "$($(PRIVATE_PREFIX)DISPLAY) $(PRIVATE_ARM_MODE) C++: $(PRIVATE_MODULE) <= $<"
 @mkdir -p $(dir $@)
 $(if $(PRIVATE_TIDY_CHECKS),$(clang-tidy-cpp))
 $(hide) $(RELATIVE_PWD) $(PRIVATE_CXX) \
@@ -1238,13 +1241,7 @@
 
 # $(1): extra flags
 define transform-c-or-s-to-o-compiler-args
-	$(addprefix -I , $(PRIVATE_C_INCLUDES)) \
-	$$(cat $(PRIVATE_IMPORT_INCLUDES)) \
-	$(addprefix -isystem ,\
-	    $(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \
-	        $(filter-out $(PRIVATE_C_INCLUDES), \
-	            $(PRIVATE_TARGET_PROJECT_INCLUDES) \
-	            $(PRIVATE_TARGET_C_INCLUDES)))) \
+	$(c-includes) \
 	-c \
 	$(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \
 	    $(PRIVATE_TARGET_GLOBAL_CFLAGS) \
@@ -1271,12 +1268,12 @@
 ifneq (,$(filter 1 true,$(WITH_TIDY_ONLY)))
 define transform-c-to-o
 $(if $(PRIVATE_TIDY_CHECKS),
-  @echo "target tidy $(PRIVATE_ARM_MODE) C: $<"
+  @echo "$($(PRIVATE_PREFIX)DISPLAY) tidy $(PRIVATE_ARM_MODE) C: $<"
   $(clang-tidy-c))
 endef
 else
 define transform-c-to-o
-@echo "target $(PRIVATE_ARM_MODE) C: $(PRIVATE_MODULE) <= $<"
+@echo "$($(PRIVATE_PREFIX)DISPLAY) $(PRIVATE_ARM_MODE) C: $(PRIVATE_MODULE) <= $<"
 @mkdir -p $(dir $@)
 $(if $(PRIVATE_TIDY_CHECKS),$(clang-tidy-c))
 $(hide) $(RELATIVE_PWD) $(PRIVATE_CC) \
@@ -1287,7 +1284,7 @@
 endif
 
 define transform-s-to-o-no-deps
-@echo "target asm: $(PRIVATE_MODULE) <= $<"
+@echo "$($(PRIVATE_PREFIX)DISPLAY) asm: $(PRIVATE_MODULE) <= $<"
 @mkdir -p $(dir $@)
 $(RELATIVE_PWD) $(PRIVATE_CC) \
   $(call transform-c-or-s-to-o-compiler-args, $(PRIVATE_ASFLAGS)) \
@@ -1316,7 +1313,7 @@
 ###########################################################
 
 define transform-m-to-o-no-deps
-@echo "target ObjC: $(PRIVATE_MODULE) <= $<"
+@echo "$($(PRIVATE_PREFIX)DISPLAY) ObjC: $(PRIVATE_MODULE) <= $<"
 $(call transform-c-or-s-to-o-no-deps, $(PRIVATE_CFLAGS) $(PRIVATE_DEBUG_CFLAGS))
 endef
 
@@ -1330,13 +1327,7 @@
 ###########################################################
 
 define transform-host-cpp-to-o-compiler-args
-	$(addprefix -I , $(PRIVATE_C_INCLUDES)) \
-	$$(cat $(PRIVATE_IMPORT_INCLUDES)) \
-	$(addprefix -isystem ,\
-	    $(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \
-	        $(filter-out $(PRIVATE_C_INCLUDES), \
-	            $($(PRIVATE_PREFIX)PROJECT_INCLUDES) \
-	            $(PRIVATE_HOST_C_INCLUDES)))) \
+	$(c-includes) \
 	-c \
 	$(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \
 	    $(PRIVATE_HOST_GLOBAL_CFLAGS) \
@@ -1379,13 +1370,7 @@
 ###########################################################
 
 define transform-host-c-or-s-to-o-common-args
-	$(addprefix -I , $(PRIVATE_C_INCLUDES)) \
-	$$(cat $(PRIVATE_IMPORT_INCLUDES)) \
-	$(addprefix -isystem ,\
-	    $(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \
-	        $(filter-out $(PRIVATE_C_INCLUDES), \
-	            $($(PRIVATE_PREFIX)PROJECT_INCLUDES) \
-	            $(PRIVATE_HOST_C_INCLUDES)))) \
+	$(c-includes) \
 	-c \
 	$(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \
 	    $(PRIVATE_HOST_GLOBAL_CFLAGS) \
@@ -1587,7 +1572,7 @@
 # Explicitly delete the archive first so that ar doesn't
 # try to add to an existing archive.
 define transform-o-to-static-lib
-@echo "target StaticLib: $(PRIVATE_MODULE) ($@)"
+@echo "$($(PRIVATE_PREFIX)DISPLAY) StaticLib: $(PRIVATE_MODULE) ($@)"
 @mkdir -p $(dir $@)
 @rm -f $@
 $(extract-and-include-target-whole-static-libs)
@@ -1596,6 +1581,89 @@
     $(PRIVATE_ARFLAGS) $@,$(PRIVATE_ALL_OBJECTS))
 endef
 
+# $(1): the full path of the source static library.
+define _extract-and-include-single-aux-whole-static-lib
+$(hide) ldir=$(PRIVATE_INTERMEDIATES_DIR)/WHOLE/$(basename $(notdir $(1)))_objs;\
+    rm -rf $$ldir; \
+    mkdir -p $$ldir; \
+    cp $(1) $$ldir; \
+    lib_to_include=$$ldir/$(notdir $(1)); \
+    filelist=; \
+    subdir=0; \
+    for f in `$(PRIVATE_AR) t $(1)`; do \
+        if [ -e $$ldir/$$f ]; then \
+            mkdir $$ldir/$$subdir; \
+            ext=$$subdir/; \
+            subdir=$$((subdir+1)); \
+            $(PRIVATE_AR) m $$lib_to_include $$f; \
+        else \
+            ext=; \
+        fi; \
+        $(PRIVATE_AR) p $$lib_to_include $$f > $$ldir/$$ext$$f; \
+        filelist="$$filelist $$ldir/$$ext$$f"; \
+    done ; \
+    $(PRIVATE_AR) $(AUX_GLOBAL_ARFLAGS) \
+        $(PRIVATE_ARFLAGS) $@ $$filelist
+
+endef
+
+define extract-and-include-aux-whole-static-libs
+$(call extract-and-include-whole-static-libs-first, $(firstword $(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES)))
+$(foreach lib,$(wordlist 2,999,$(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES)), \
+    $(call _extract-and-include-single-aux-whole-static-lib, $(lib)))
+endef
+
+# Explicitly delete the archive first so that ar doesn't
+# try to add to an existing archive.
+define transform-o-to-aux-static-lib
+@echo "$($(PRIVATE_PREFIX)DISPLAY) StaticLib: $(PRIVATE_MODULE) ($@)"
+@mkdir -p $(dir $@)
+@rm -f $@
+$(extract-and-include-aux-whole-static-libs)
+$(call split-long-arguments,$(PRIVATE_AR) \
+    $(AUX_GLOBAL_ARFLAGS) \
+    $(PRIVATE_ARFLAGS) $@,$(PRIVATE_ALL_OBJECTS))
+endef
+
+define transform-o-to-aux-executable-inner
+$(hide) $(PRIVATE_CXX) -pie \
+	-Bdynamic \
+	-Wl,--gc-sections \
+	$(PRIVATE_ALL_OBJECTS) \
+	-Wl,--whole-archive \
+	$(call normalize-target-libraries,$(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES)) \
+	-Wl,--no-whole-archive \
+	$(call normalize-target-libraries,$(PRIVATE_ALL_STATIC_LIBRARIES)) \
+	$(PRIVATE_LDFLAGS) \
+	-o $@
+endef
+
+define transform-o-to-aux-executable
+@echo "$(AUX_DISPLAY) Executable: $(PRIVATE_MODULE) ($@)"
+@mkdir -p $(dir $@)
+$(transform-o-to-aux-executable-inner)
+endef
+
+define transform-o-to-aux-static-executable-inner
+$(hide) $(PRIVATE_CXX) \
+	-Bstatic \
+	-Wl,--gc-sections \
+	$(PRIVATE_ALL_OBJECTS) \
+	-Wl,--whole-archive \
+	$(call normalize-target-libraries,$(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES)) \
+	-Wl,--no-whole-archive \
+	$(call normalize-target-libraries,$(PRIVATE_ALL_STATIC_LIBRARIES)) \
+	$(PRIVATE_LDFLAGS) \
+	-Wl,-Map=$(@).map \
+	-o $@
+endef
+
+define transform-o-to-aux-static-executable
+@echo "$(AUX_DISPLAY) StaticExecutable: $(PRIVATE_MODULE) ($@)"
+@mkdir -p $(dir $@)
+$(transform-o-to-aux-static-executable-inner)
+endef
+
 ###########################################################
 ## Commands for running host ar
 ###########################################################
@@ -1733,14 +1801,14 @@
 	$(PRIVATE_TARGET_LIBGCC) \
 	$(PRIVATE_TARGET_GLOBAL_LDFLAGS) \
 	$(PRIVATE_LDFLAGS) \
-	$(call normalize-target-libraries,$(PRIVATE_ALL_SHARED_LIBRARIES)) \
+	$(PRIVATE_ALL_SHARED_LIBRARIES) \
 	-o $@ \
 	$(PRIVATE_TARGET_CRTEND_SO_O) \
 	$(PRIVATE_LDLIBS)
 endef
 
 define transform-o-to-shared-lib
-@echo "target SharedLib: $(PRIVATE_MODULE) ($@)"
+@echo "$($(PRIVATE_PREFIX)DISPLAY) SharedLib: $(PRIVATE_MODULE) ($@)"
 @mkdir -p $(dir $@)
 $(transform-o-to-shared-lib-inner)
 endef
@@ -1755,14 +1823,14 @@
 endif
 
 define transform-to-stripped
-@echo "target Strip: $(PRIVATE_MODULE) ($@)"
+@echo "$($(PRIVATE_PREFIX)DISPLAY) Strip: $(PRIVATE_MODULE) ($@)"
 @mkdir -p $(dir $@)
 $(hide) $(PRIVATE_STRIP) --strip-all $< -o $@ \
   $(if $(PRIVATE_NO_DEBUGLINK),,$(TARGET_STRIP_EXTRA))
 endef
 
 define transform-to-stripped-keep-mini-debug-info
-@echo "target Strip (mini debug info): $(PRIVATE_MODULE) ($@)"
+@echo "$($(PRIVATE_PREFIX)DISPLAY) Strip (mini debug info): $(PRIVATE_MODULE) ($@)"
 @mkdir -p $(dir $@)
 $(hide) $(PRIVATE_NM) -D $< --format=posix --defined-only | awk '{ print $$1 }' | sort >$@.dynsyms
 $(hide) $(PRIVATE_NM) $< --format=posix --defined-only | awk '{ if ($$2 == "T" || $$2 == "t" || $$2 == "D") print $$1 }' | sort >$@.funcsyms
@@ -1779,7 +1847,7 @@
 endef
 
 define transform-to-stripped-keep-symbols
-@echo "target Strip (keep symbols): $(PRIVATE_MODULE) ($@)"
+@echo "$($(PRIVATE_PREFIX)DISPLAY) Strip (keep symbols): $(PRIVATE_MODULE) ($@)"
 @mkdir -p $(dir $@)
 $(hide) $(PRIVATE_OBJCOPY) \
     `$(PRIVATE_READELF) -S $< | awk '/.debug_/ {print "-R " $$2}' | xargs` \
@@ -1791,7 +1859,7 @@
 ###########################################################
 
 define pack-elf-relocations
-@echo "target Pack Relocations: $(PRIVATE_MODULE) ($@)"
+@echo "$($(PRIVATE_PREFIX)DISPLAY) Pack Relocations: $(PRIVATE_MODULE) ($@)"
 $(copy-file-to-target)
 $(hide) $(RELOCATION_PACKER) $@
 endef
@@ -1821,14 +1889,14 @@
 	$(PRIVATE_TARGET_LIBGCC) \
 	$(PRIVATE_TARGET_GLOBAL_LDFLAGS) \
 	$(PRIVATE_LDFLAGS) \
-	$(call normalize-target-libraries,$(PRIVATE_ALL_SHARED_LIBRARIES)) \
+	$(PRIVATE_ALL_SHARED_LIBRARIES) \
 	-o $@ \
 	$(PRIVATE_TARGET_CRTEND_O) \
 	$(PRIVATE_LDLIBS)
 endef
 
 define transform-o-to-executable
-@echo "target Executable: $(PRIVATE_MODULE) ($@)"
+@echo "$($(PRIVATE_PREFIX)DISPLAY) Executable: $(PRIVATE_MODULE) ($@)"
 @mkdir -p $(dir $@)
 $(transform-o-to-executable-inner)
 endef
@@ -1871,7 +1939,7 @@
 endef
 
 define transform-o-to-static-executable
-@echo "target StaticExecutable: $(PRIVATE_MODULE) ($@)"
+@echo "$($(PRIVATE_PREFIX)DISPLAY) StaticExecutable: $(PRIVATE_MODULE) ($@)"
 @mkdir -p $(dir $@)
 $(transform-o-to-static-executable-inner)
 endef
@@ -2090,7 +2158,7 @@
 endef
 
 define transform-java-to-classes.jar
-@echo "target Java: $(PRIVATE_MODULE) ($(PRIVATE_CLASS_INTERMEDIATES_DIR))"
+@echo "$($(PRIVATE_PREFIX)DISPLAY) Java: $(PRIVATE_MODULE) ($(PRIVATE_CLASS_INTERMEDIATES_DIR))"
 $(call compile-java,$(TARGET_JAVAC),$(PRIVATE_BOOTCLASSPATH))
 endef
 
@@ -2609,19 +2677,19 @@
 
 # Copy a prebuilt file to a target location.
 define transform-prebuilt-to-target
-@echo "$(if $(PRIVATE_IS_HOST_MODULE),host,target) Prebuilt: $(PRIVATE_MODULE) ($@)"
+@echo "$($(PRIVATE_PREFIX)DISPLAY) Prebuilt: $(PRIVATE_MODULE) ($@)"
 $(copy-file-to-target)
 endef
 
 # Copy a prebuilt file to a target location, using zipalign on it.
 define transform-prebuilt-to-target-with-zipalign
-@echo "$(if $(PRIVATE_IS_HOST_MODULE),host,target) Prebuilt APK: $(PRIVATE_MODULE) ($@)"
+@echo "$($(PRIVATE_PREFIX)DISPLAY) Prebuilt APK: $(PRIVATE_MODULE) ($@)"
 $(copy-file-to-target-with-zipalign)
 endef
 
 # Copy a prebuilt file to a target location, stripping "# comment" comments.
 define transform-prebuilt-to-target-strip-comments
-@echo "$(if $(PRIVATE_IS_HOST_MODULE),host,target) Prebuilt: $(PRIVATE_MODULE) ($@)"
+@echo "$($(PRIVATE_PREFIX)DISPLAY) Prebuilt: $(PRIVATE_MODULE) ($@)"
 $(copy-file-to-target-strip-comments)
 endef
 
@@ -2636,6 +2704,24 @@
   $(hide) mkdir -p $(dir $(3)/$(s)); cp -Rf $(t) $(3)/$(s)$(newline))
 endef
 
+# Define a rule to create a symlink to a file.
+# $(1): full path to source
+# $(2): source (may be relative)
+# $(3): full path to destination
+define symlink-file
+$(eval $(_symlink-file))
+endef
+
+# Order-only dependency because make/ninja will follow the link when checking
+# the timestamp, so the file must exist
+define _symlink-file
+$(3): | $(1)
+	@echo "Symlink: $$@ -> $(2)"
+	@mkdir -p $(dir $$@)
+	@rm -rf $$@
+	$(hide) ln -sf $(2) $$@
+endef
+
 ###########################################################
 ## Commands to call Proguard
 ###########################################################
@@ -2650,7 +2736,7 @@
 ###########################################################
 
 define transform-generated-source
-@echo "target Generated: $(PRIVATE_MODULE) <= $<"
+@echo "$($(PRIVATE_PREFIX)DISPLAY) Generated: $(PRIVATE_MODULE) <= $<"
 @mkdir -p $(dir $@)
 $(hide) $(PRIVATE_CUSTOM_TOOL)
 endef
@@ -2863,6 +2949,48 @@
   $(if $(filter $(TARGET_2ND_ARCH),$(1)),$(TARGET_2ND_ARCH),$(if $(1),none))))
 endef
 
+# ###############################################################
+# Set up statistics gathering
+# ###############################################################
+STATS.MODULE_TYPE := \
+  HOST_STATIC_LIBRARY \
+  HOST_SHARED_LIBRARY \
+  STATIC_LIBRARY \
+  SHARED_LIBRARY \
+  EXECUTABLE \
+  HOST_EXECUTABLE \
+  PACKAGE \
+  PHONY_PACKAGE \
+  HOST_PREBUILT \
+  PREBUILT \
+  MULTI_PREBUILT \
+  JAVA_LIBRARY \
+  STATIC_JAVA_LIBRARY \
+  HOST_JAVA_LIBRARY \
+  DROIDDOC \
+  COPY_HEADERS \
+  NATIVE_TEST \
+  NATIVE_BENCHMARK \
+  HOST_NATIVE_TEST \
+  FUZZ_TEST \
+  HOST_FUZZ_TEST \
+  STATIC_TEST_LIBRARY \
+  HOST_STATIC_TEST_LIBRARY \
+  NOTICE_FILE \
+  HOST_DALVIK_JAVA_LIBRARY \
+  HOST_DALVIK_STATIC_JAVA_LIBRARY \
+  base_rules
+
+$(foreach $(s),$(STATS.MODULE_TYPE),$(eval STATS.MODULE_TYPE.$(s) :=))
+define record-module-type
+$(strip $(if $(LOCAL_RECORDED_MODULE_TYPE),,
+  $(if $(filter-out $(SOONG_ANDROID_MK),$(LOCAL_MODULE_MAKEFILE)),
+    $(if $(filter $(1),$(STATS.MODULE_TYPE)),
+      $(eval LOCAL_RECORDED_MODULE_TYPE := true)
+        $(eval STATS.MODULE_TYPE.$(1) += 1),
+      $(error Invalid module type: $(1))))))
+endef
+
 ###########################################################
 ## Other includes
 ###########################################################
diff --git a/core/droiddoc.mk b/core/droiddoc.mk
index 0f18a58..06506db 100644
--- a/core/droiddoc.mk
+++ b/core/droiddoc.mk
@@ -14,6 +14,7 @@
 # limitations under the License.
 #
 
+$(call record-module-type,DROIDDOC)
 ##
 ##
 ## Common to both droiddoc and javadoc
diff --git a/core/dumpvar.mk b/core/dumpvar.mk
index 4b3486a..74ea3ff 100644
--- a/core/dumpvar.mk
+++ b/core/dumpvar.mk
@@ -22,7 +22,8 @@
   HOST_CROSS_2ND_ARCH \
   HOST_BUILD_TYPE \
   BUILD_ID \
-  OUT_DIR
+  OUT_DIR \
+  AUX_OS_VARIANT_LIST
 
 ifeq ($(TARGET_BUILD_PDK),true)
 print_build_config_vars += \
diff --git a/core/envsetup.mk b/core/envsetup.mk
index 1df25d2..f234702 100644
--- a/core/envsetup.mk
+++ b/core/envsetup.mk
@@ -273,6 +273,9 @@
 HOST_OUT_COMMON_INTERMEDIATES := $(HOST_COMMON_OUT_ROOT)/obj
 HOST_OUT_FAKE := $(HOST_OUT)/fake_packages
 
+# Nano environment config
+include $(BUILD_SYSTEM)/aux_config.mk
+
 HOST_CROSS_OUT_INTERMEDIATES := $(HOST_CROSS_OUT)/obj
 HOST_CROSS_OUT_HEADERS := $(HOST_CROSS_OUT_INTERMEDIATES)/include
 HOST_CROSS_OUT_INTERMEDIATE_LIBRARIES := $(HOST_CROSS_OUT_INTERMEDIATES)/lib
diff --git a/core/executable.mk b/core/executable.mk
index 8652077..f1b2462 100644
--- a/core/executable.mk
+++ b/core/executable.mk
@@ -16,6 +16,7 @@
 endif
 
 ifneq (true,$(my_skip_this_target))
+$(call record-module-type,EXECUTABLE)
 
 ifeq ($(TARGET_TRANSLATE_2ND_ARCH),true)
 # If a native test explicity specifies to build only for the translation arch,
diff --git a/core/executable_prefer_symlink.mk b/core/executable_prefer_symlink.mk
index 1640b32..e59e8c2 100644
--- a/core/executable_prefer_symlink.mk
+++ b/core/executable_prefer_symlink.mk
@@ -10,43 +10,37 @@
 # et al. since those variables make no sense in that context.
 ifneq ($(LOCAL_IS_HOST_MODULE),true)
   my_symlink := $(addprefix $(TARGET_OUT)/bin/, $(LOCAL_MODULE))
+  my_src_binary_name :=
   ifeq ($(TARGET_IS_64_BIT),true)
     ifeq ($(TARGET_SUPPORTS_64_BIT_APPS)|$(TARGET_SUPPORTS_32_BIT_APPS),true|true)
       # We support both 32 and 64 bit apps, so we will have to
       # base our decision on whether the target prefers one or the
       # other.
       ifeq ($(TARGET_PREFER_32_BIT_APPS),true)
-        $(my_symlink): PRIVATE_SRC_BINARY_NAME := $(LOCAL_MODULE_STEM_32)
+        my_src_binary_name := $(LOCAL_MODULE_STEM_32)
       else
-        $(my_symlink): PRIVATE_SRC_BINARY_NAME := $(LOCAL_MODULE_STEM_64)
+        my_src_binary_name := $(LOCAL_MODULE_STEM_64)
       endif
     else ifeq ($(TARGET_SUPPORTS_64_BIT_APPS),true)
       # We support only 64 bit apps.
-      $(my_symlink): PRIVATE_SRC_BINARY_NAME := $(LOCAL_MODULE_STEM_64)
+      my_src_binary_name := $(LOCAL_MODULE_STEM_64)
     else
       # We support only 32 bit apps.
-      $(my_symlink): PRIVATE_SRC_BINARY_NAME := $(LOCAL_MODULE_STEM_32)
+      my_src_binary_name := $(LOCAL_MODULE_STEM_32)
     endif
   else
-    $(my_symlink): PRIVATE_SRC_BINARY_NAME := $(LOCAL_MODULE_STEM_32)
+    my_src_binary_name := $(LOCAL_MODULE_STEM_32)
   endif
 else
   my_symlink := $(addprefix $(HOST_OUT)/bin/, $(LOCAL_MODULE))
   ifneq ($(HOST_PREFER_32_BIT),true)
-$(my_symlink): PRIVATE_SRC_BINARY_NAME := $(LOCAL_MODULE_STEM_64)
+    my_src_binary_name := $(LOCAL_MODULE_STEM_64)
   else
-$(my_symlink): PRIVATE_SRC_BINARY_NAME := $(LOCAL_MODULE_STEM_32)
+    my_src_binary_name := $(LOCAL_MODULE_STEM_32)
   endif
 endif
 
-# $(my_symlink) doesn't need to depend on $(PRIVATE_SRC_BINARY_NAME): we can generate symlink to nonexistent file.
-# If you add the dependency, make would compare the timestamp of a file against that of its symlink:
-# they are always equal, because make follows symlink.
-$(my_symlink):
-	@echo "Symlink: $@ -> $(PRIVATE_SRC_BINARY_NAME)"
-	@mkdir -p $(dir $@)
-	@rm -rf $@
-	$(hide) ln -sf $(PRIVATE_SRC_BINARY_NAME) $@
+$(call symlink-file,$(my_module_path)/$(my_src_binary_name),$(my_src_binary_name),$(my_symlink))
 
 # We need this so that the installed files could be picked up based on the
 # local module name
diff --git a/core/fuzz_test.mk b/core/fuzz_test.mk
index fc582b3..c31e2e7 100644
--- a/core/fuzz_test.mk
+++ b/core/fuzz_test.mk
@@ -2,6 +2,7 @@
 ## A thin wrapper around BUILD_EXECUTABLE
 ## Common flags for fuzz tests are added.
 ###########################################
+$(call record-module-type,FUZZ_TEST)
 
 ifdef LOCAL_SDK_VERSION
     $(error $(LOCAL_PATH): $(LOCAL_MODULE): NDK fuzz tests are not supported.)
diff --git a/core/goma.mk b/core/goma.mk
index 0d5f428..c0c27c5 100644
--- a/core/goma.mk
+++ b/core/goma.mk
@@ -46,7 +46,7 @@
   # gomacc can start goma client's daemon process automatically, but
   # it is safer and faster to start up it beforehand. We run this as a
   # background process so this won't slow down the build.
-  $(shell ( GOMA_HERMETIC=error $(goma_ctl) ensure_start ) &> /dev/null &)
+  $(shell ( $(goma_ctl) ensure_start ) &> /dev/null &)
 
   goma_ctl :=
   goma_dir :=
diff --git a/core/host_dalvik_java_library.mk b/core/host_dalvik_java_library.mk
index 7fdf249..cee3cc4 100644
--- a/core/host_dalvik_java_library.mk
+++ b/core/host_dalvik_java_library.mk
@@ -13,6 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
+$(call record-module-type,HOST_DALVIK_JAVA_LIBRARY)
 
 #
 # Rules for building a host dalvik java library. These libraries
diff --git a/core/host_dalvik_static_java_library.mk b/core/host_dalvik_static_java_library.mk
index b79c0ea..78faf73 100644
--- a/core/host_dalvik_static_java_library.mk
+++ b/core/host_dalvik_static_java_library.mk
@@ -13,6 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
+$(call record-module-type,HOST_DALVIK_STATIC_JAVA_LIBRARY)
 
 #
 # Rules for building a host dalvik static java library.
diff --git a/core/host_executable.mk b/core/host_executable.mk
index 0060c3e..78223eb 100644
--- a/core/host_executable.mk
+++ b/core/host_executable.mk
@@ -1,3 +1,4 @@
+$(call record-module-type,HOST_EXECUTABLE)
 LOCAL_IS_HOST_MODULE := true
 my_prefix := HOST_
 LOCAL_HOST_PREFIX :=
diff --git a/core/host_fuzz_test.mk b/core/host_fuzz_test.mk
index cc7baad..1c9eed2 100644
--- a/core/host_fuzz_test.mk
+++ b/core/host_fuzz_test.mk
@@ -2,6 +2,7 @@
 ## A thin wrapper around BUILD_HOST_EXECUTABLE
 ## Common flags for host fuzz tests are added.
 ################################################
+$(call record-module-type,HOST_FUZZ_TEST)
 
 LOCAL_CFLAGS += -fsanitize-coverage=edge,indirect-calls,8bit-counters,trace-cmp
 LOCAL_STATIC_LIBRARIES += libLLVMFuzzer
diff --git a/core/host_java_library.mk b/core/host_java_library.mk
index 9aa2a7c..0a7dd8a 100644
--- a/core/host_java_library.mk
+++ b/core/host_java_library.mk
@@ -14,6 +14,8 @@
 # limitations under the License.
 #
 
+$(call record-module-type,HOST_JAVA_LIBRARY)
+
 #
 # Standard rules for building a host java library.
 #
diff --git a/core/host_native_test.mk b/core/host_native_test.mk
index 2a6097d..aa05bb3 100644
--- a/core/host_native_test.mk
+++ b/core/host_native_test.mk
@@ -2,6 +2,7 @@
 ## A thin wrapper around BUILD_HOST_EXECUTABLE
 ## Common flags for host native tests are added.
 ################################################
+$(call record-module-type,HOST_NATIVE_TEST)
 
 ifdef LOCAL_MODULE_CLASS
 ifneq ($(LOCAL_MODULE_CLASS),NATIVE_TESTS)
diff --git a/core/host_prebuilt.mk b/core/host_prebuilt.mk
index 7baab69..79f3ffa 100644
--- a/core/host_prebuilt.mk
+++ b/core/host_prebuilt.mk
@@ -14,5 +14,6 @@
 # limitations under the License.
 #
 
+$(call record-module-type,HOST_PREBUILT)
 LOCAL_IS_HOST_MODULE := true
 include $(BUILD_MULTI_PREBUILT)
diff --git a/core/host_shared_library.mk b/core/host_shared_library.mk
index 2e0c9f1c..df24b63 100644
--- a/core/host_shared_library.mk
+++ b/core/host_shared_library.mk
@@ -1,3 +1,4 @@
+$(call record-module-type,HOST_SHARED_LIBRARY)
 LOCAL_IS_HOST_MODULE := true
 my_prefix := HOST_
 LOCAL_HOST_PREFIX :=
diff --git a/core/host_static_library.mk b/core/host_static_library.mk
index 068c702..61f5569 100644
--- a/core/host_static_library.mk
+++ b/core/host_static_library.mk
@@ -1,3 +1,4 @@
+$(call record-module-type,HOST_STATIC_LIBRARY)
 LOCAL_IS_HOST_MODULE := true
 my_prefix := HOST_
 LOCAL_HOST_PREFIX :=
diff --git a/core/host_static_test_lib.mk b/core/host_static_test_lib.mk
index 5423dc6..a24cd62 100644
--- a/core/host_static_test_lib.mk
+++ b/core/host_static_test_lib.mk
@@ -2,6 +2,7 @@
 ## A thin wrapper around BUILD_HOST_STATIC_LIBRARY
 ## Common flags for host native tests are added.
 ##################################################
+$(call record-module-type,HOST_STATIC_TEST_LIBRARY)
 
 include $(BUILD_SYSTEM)/host_test_internal.mk
 
diff --git a/core/install_jni_libs_internal.mk b/core/install_jni_libs_internal.mk
index 6136968..9f5d445 100644
--- a/core/install_jni_libs_internal.mk
+++ b/core/install_jni_libs_internal.mk
@@ -32,10 +32,10 @@
 endif
 ifeq (stlport_shared,$(LOCAL_NDK_STL_VARIANT))
 my_jni_shared_libraries += \
-    $(HISTORICAL_NDK_VERSIONS_ROOT)/current/sources/cxx-stl/stlport/libs/$(TARGET_$(my_2nd_arch_prefix)CPU_ABI)/libstlport_shared.so
+    $(HISTORICAL_NDK_VERSIONS_ROOT)/$(LOCAL_NDK_VERSION)/sources/cxx-stl/stlport/libs/$(TARGET_$(my_2nd_arch_prefix)CPU_ABI)/libstlport_shared.so
 else ifeq (c++_shared,$(LOCAL_NDK_STL_VARIANT))
 my_jni_shared_libraries += \
-    $(HISTORICAL_NDK_VERSIONS_ROOT)/current/sources/cxx-stl/llvm-libc++/libs/$(TARGET_$(my_2nd_arch_prefix)CPU_ABI)/libc++_shared.so
+    $(HISTORICAL_NDK_VERSIONS_ROOT)/$(LOCAL_NDK_VERSION)/sources/cxx-stl/llvm-libc++/libs/$(TARGET_$(my_2nd_arch_prefix)CPU_ABI)/libc++_shared.so
 endif
 
 # Set the abi directory used by the local JNI shared libraries.
diff --git a/core/java_common.mk b/core/java_common.mk
index 1b12ea2..5eac1f2 100644
--- a/core/java_common.mk
+++ b/core/java_common.mk
@@ -369,3 +369,42 @@
 $(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_JARJAR_RULES := $(LOCAL_JARJAR_RULES)
 
 endif  # need_compile_java
+
+
+###########################################################
+# Verify that all libraries are safe to use
+###########################################################
+ifndef LOCAL_IS_HOST_MODULE
+my_link_type := $(intermediates.COMMON)/link_type
+my_link_type_deps := $(strip \
+  $(foreach lib,$(LOCAL_STATIC_JAVA_LIBRARIES),\
+    $(call intermediates-dir-for, \
+      JAVA_LIBRARIES,$(lib),,COMMON)/link_type) \
+  $(foreach lib,$(apk_libraries), \
+    $(call intermediates-dir-for, \
+      APPS,$(lib),,COMMON)/link_type))
+ifeq ($(LOCAL_SDK_VERSION),system_current)
+$(my_link_type): PRIVATE_LINK_TYPE := system
+$(my_link_type): PRIVATE_ALLOWED_TYPES := (sdk|system)
+else ifneq ($(LOCAL_SDK_VERSION),)
+$(my_link_type): PRIVATE_LINK_TYPE := sdk
+$(my_link_type): PRIVATE_ALLOWED_TYPES := sdk
+else
+$(my_link_type): PRIVATE_LINK_TYPE := platform
+$(my_link_type): PRIVATE_ALLOWED_TYPES := (sdk|system|platform)
+endif
+$(my_link_type): PRIVATE_DEPS := $(my_link_type_deps)
+$(my_link_type): PRIVATE_MODULE := $(LOCAL_MODULE)
+$(my_link_type): PRIVATE_MAKEFILE := $(LOCAL_MODULE_MAKEFILE)
+$(my_link_type): $(my_link_type_deps)
+	@echo Check Java library module types: $@
+	$(hide) mkdir -p $(dir $@)
+	$(hide) rm -f $@
+	$(hide) for f in $(PRIVATE_DEPS); do \
+	  grep -qE '^$(PRIVATE_ALLOWED_TYPES)$$' $$f || \
+	    $(call echo-warning,"$(PRIVATE_MAKEFILE): $(PRIVATE_MODULE) ($(PRIVATE_LINK_TYPE)) should not link to $$(basename $${f%_intermediates/link_type}) ($$(cat $$f))"); \
+	done
+	$(hide) echo $(PRIVATE_LINK_TYPE) >$@
+
+$(LOCAL_BUILT_MODULE): $(my_link_type)
+endif  # !LOCAL_IS_HOST_MODULE
diff --git a/core/java_library.mk b/core/java_library.mk
index 283e9ad..b132fa6 100644
--- a/core/java_library.mk
+++ b/core/java_library.mk
@@ -2,6 +2,7 @@
 ## Standard rules for building a java library.
 ##
 ###########################################################
+$(call record-module-type,JAVA_LIBRARY)
 
 ifdef LOCAL_IS_HOST_MODULE
 $(error $(LOCAL_PATH): Host java libraries must use BUILD_HOST_JAVA_LIBRARY)
diff --git a/core/main.mk b/core/main.mk
index a2f80ef..3720971 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -457,6 +457,9 @@
 $(error The 'sdk' target may not be specified with any other targets)
 endif
 
+# AUX dependencies are already added by now; remove triggers from the MAKECMDGOALS
+MAKECMDGOALS := $(strip $(filter-out AUX-%,$(MAKECMDGOALS)))
+
 # TODO: this should be eng I think.  Since the sdk is built from the eng
 # variant.
 tags_to_install := debug eng
@@ -515,7 +518,7 @@
 ifneq ($(ONE_SHOT_MAKEFILE),)
 # We've probably been invoked by the "mm" shell function
 # with a subdirectory's makefile.
-include  $(SOONG_ANDROID_MK) $(call filter-soong-makefiles,$(ONE_SHOT_MAKEFILE))
+include $(SOONG_ANDROID_MK) $(wildcard $(ONE_SHOT_MAKEFILE))
 # Change CUSTOM_MODULES to include only modules that were
 # defined by this makefile; this will install all of those
 # modules as a side-effect.  Do this after including ONE_SHOT_MAKEFILE
@@ -934,6 +937,8 @@
 all_modules: $(my_all_modules)
 endif
 
+.PHONY: auxiliary
+auxiliary: $(INSTALLED_AUX_TARGETS)
 
 # Build files and then package it into the rom formats
 .PHONY: droidcore
@@ -1125,4 +1130,7 @@
 tidy_only:
 	@echo Successfully make tidy_only.
 
+ndk: $(SOONG_OUT_DIR)/ndk.timestamp
+.PHONY: ndk
+
 endif # KATI
diff --git a/core/multi_prebuilt.mk b/core/multi_prebuilt.mk
index ed2fed6..77c57ab 100644
--- a/core/multi_prebuilt.mk
+++ b/core/multi_prebuilt.mk
@@ -14,6 +14,7 @@
 # limitations under the License.
 #
 
+$(call record-module-type,MULTI_PREBUILT)
 ifneq ($(LOCAL_MODULE)$(LOCAL_MODULE_CLASS),)
 $(error $(LOCAL_PATH): LOCAL_MODULE or LOCAL_MODULE_CLASS not needed by \
   BUILD_MULTI_PREBUILT, use BUILD_PREBUILT instead!)
diff --git a/core/native_benchmark.mk b/core/native_benchmark.mk
index fe378b8..7b04d87 100644
--- a/core/native_benchmark.mk
+++ b/core/native_benchmark.mk
@@ -2,6 +2,7 @@
 ## A thin wrapper around BUILD_EXECUTABLE
 ## Common flags for native benchmarks are added.
 ###########################################
+$(call record-module-type,NATIVE_BENCHMARK)
 
 LOCAL_STATIC_LIBRARIES += libgoogle-benchmark
 
diff --git a/core/native_test.mk b/core/native_test.mk
index bb93eb0..8b49fbd 100644
--- a/core/native_test.mk
+++ b/core/native_test.mk
@@ -2,6 +2,7 @@
 ## A thin wrapper around BUILD_EXECUTABLE
 ## Common flags for native tests are added.
 ###########################################
+$(call record-module-type,NATIVE_TEST)
 
 ifdef LOCAL_MODULE_CLASS
 ifneq ($(LOCAL_MODULE_CLASS),NATIVE_TESTS)
diff --git a/core/ninja.mk b/core/ninja.mk
index 62102ba..069a43e 100644
--- a/core/ninja.mk
+++ b/core/ninja.mk
@@ -17,6 +17,7 @@
 	DUMP_% \
 	ECLIPSE-% \
 	PRODUCT-% \
+	AUX-% \
 	boottarball-nodeps \
 	brillo_tests \
 	btnod \
diff --git a/core/notice_files.mk b/core/notice_files.mk
index e7f8974..f0013c2 100644
--- a/core/notice_files.mk
+++ b/core/notice_files.mk
@@ -1,6 +1,7 @@
 ###########################################################
 ## Track NOTICE files
 ###########################################################
+$(call record-module-type,NOTICE_FILE)
 
 ifneq ($(LOCAL_NOTICE_FILE),)
 notice_file:=$(strip $(LOCAL_NOTICE_FILE))
diff --git a/core/package.mk b/core/package.mk
index 8c2c435..4fe058d 100644
--- a/core/package.mk
+++ b/core/package.mk
@@ -2,6 +2,8 @@
 # TARGET_ARCH and TARGET_2ND_ARCH.
 # To build it for TARGET_2ND_ARCH in a 64bit product, use "LOCAL_MULTILIB := 32".
 
+$(call record-module-type,PACKAGE)
+
 ifeq ($(TARGET_TRANSLATE_2ND_ARCH),true)
 LOCAL_MULTILIB := first
 endif
diff --git a/core/phony_package.mk b/core/phony_package.mk
index b534335..578d629 100644
--- a/core/phony_package.mk
+++ b/core/phony_package.mk
@@ -1,3 +1,4 @@
+$(call record-module-type,PHONY_PACKAGE)
 ifneq ($(strip $(LOCAL_SRC_FILES)),)
 $(error LOCAL_SRC_FILES are not allowed for phony packages)
 endif
diff --git a/core/prebuilt.mk b/core/prebuilt.mk
index cbe2079..5831e24 100644
--- a/core/prebuilt.mk
+++ b/core/prebuilt.mk
@@ -5,6 +5,7 @@
 ## None.
 ##
 ###########################################################
+$(call record-module-type,PREBUILT)
 
 ifdef LOCAL_IS_HOST_MODULE
   my_prefix := HOST_
diff --git a/core/prebuilt_internal.mk b/core/prebuilt_internal.mk
index 9c4a98d..701414a 100644
--- a/core/prebuilt_internal.mk
+++ b/core/prebuilt_internal.mk
@@ -46,9 +46,18 @@
   $(LOCAL_PACK_MODULE_RELOCATIONS))
 
 ifeq (SHARED_LIBRARIES,$(LOCAL_MODULE_CLASS))
-  # Put the built targets of all shared libraries in a common directory
-  # to simplify the link line.
-  OVERRIDE_BUILT_MODULE_PATH := $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)OUT_INTERMEDIATE_LIBRARIES)
+  # LOCAL_COPY_TO_INTERMEDIATE_LIBRARIES indicates that this prebuilt should be
+  # installed to the common directory of libraries. This is needed for the NDK
+  # shared libraries built by soong, as we build many different versions of each
+  # library (one for each API level). Since they all have the same basename,
+  # they'd clobber each other (as well as any platform libraries by the same
+  # name).
+  ifneq ($(LOCAL_COPY_TO_INTERMEDIATE_LIBRARIES),false)
+    # Put the built targets of all shared libraries in a common directory
+    # to simplify the link line.
+    OVERRIDE_BUILT_MODULE_PATH :=  \
+        $($(LOCAL_2ND_ARCH_VAR_PREFIX)$(my_prefix)OUT_INTERMEDIATE_LIBRARIES)
+  endif
   ifeq ($(LOCAL_IS_HOST_MODULE)$(my_strip_module),)
     # Strip but not try to add debuglink
     my_strip_module := no_debuglink
@@ -88,7 +97,7 @@
 LOCAL_INSTALLED_MODULE_STEM := $(LOCAL_MODULE).apk
 endif
 
-ifneq ($(filter true no_debuglink,$(my_strip_module) $(my_pack_module_relocations)),)
+ifneq ($(filter true keep_symbols no_debuglink mini-debug-info,$(my_strip_module) $(my_pack_module_relocations)),)
   ifdef LOCAL_IS_HOST_MODULE
     $(error Cannot strip/pack host module LOCAL_PATH=$(LOCAL_PATH))
   endif
@@ -368,6 +377,20 @@
 
 $(common_classes_jar) $(common_javalib_jar): PRIVATE_MODULE := $(LOCAL_MODULE)
 
+my_link_type := $(intermediates.COMMON)/link_type
+ifeq ($(LOCAL_SDK_VERSION),system_current)
+$(my_link_type): PRIVATE_LINK_TYPE := system
+else ifneq ($(LOCAL_SDK_VERSION),)
+$(my_link_type): PRIVATE_LINK_TYPE := sdk
+else
+$(my_link_type): PRIVATE_LINK_TYPE := platform
+endif
+$(my_link_type):
+	@echo Check module type: $@
+	$(hide) mkdir -p $(dir $@) && rm -f $@
+	$(hide) echo $(PRIVATE_LINK_TYPE) >$@
+$(LOCAL_BUILT_MODULE): $(my_link_type)
+
 ifeq ($(prebuilt_module_is_dex_javalib),true)
 # For prebuilt shared Java library we don't have classes.jar.
 $(common_javalib_jar) : $(my_src_jar) | $(ACP)
diff --git a/core/shared_library.mk b/core/shared_library.mk
index 2f48341..a15b1a6 100644
--- a/core/shared_library.mk
+++ b/core/shared_library.mk
@@ -1,3 +1,4 @@
+$(call record-module-type,SHARED_LIBRARY)
 my_prefix := TARGET_
 include $(BUILD_SYSTEM)/multilib.mk
 
diff --git a/core/soong.mk b/core/soong.mk
index 65180d7..3f0ee85 100644
--- a/core/soong.mk
+++ b/core/soong.mk
@@ -25,6 +25,13 @@
 	$(hide) mkdir -p $(dir $@)
 	$(hide) BUILDDIR=$(SOONG_OUT_DIR) ./bootstrap.bash
 
+BINDER32BIT :=
+ifneq ($(TARGET_USES_64_BIT_BINDER),true)
+ifneq ($(TARGET_IS_64_BIT),true)
+BINDER32BIT := true
+endif
+endif
+
 # Create soong.variables with copies of makefile settings.  Runs every build,
 # but only updates soong.variables if it changes
 SOONG_VARIABLES_TMP := $(SOONG_VARIABLES).$$$$
@@ -42,6 +49,9 @@
 	echo '    "SanitizeHost": [$(if $(SANITIZE_HOST),"$(subst $(space),"$(comma)",$(SANITIZE_HOST))")],'; \
 	echo '    "SanitizeDevice": [$(if $(SANITIZE_TARGET),"$(subst $(space),"$(comma)",$(SANITIZE_TARGET))")],'; \
 	echo '    "HostStaticBinaries": $(if $(strip $(BUILD_HOST_static)),true,false),'; \
+	echo '    "Cpusets": $(if $(strip $(ENABLE_CPUSETS)),true,false),'; \
+	echo '    "Schedboost": $(if $(strip $(ENABLE_SCHEDBOOST)),true,false),'; \
+	echo '    "Binder32bit": $(if $(BINDER32BIT),true,false),'; \
 	echo ''; \
 	echo '    "DeviceName": "$(TARGET_DEVICE)",'; \
 	echo '    "DeviceArch": "$(TARGET_ARCH)",'; \
diff --git a/core/static_java_library.mk b/core/static_java_library.mk
index d3324f8..d6dd065 100644
--- a/core/static_java_library.mk
+++ b/core/static_java_library.mk
@@ -19,6 +19,7 @@
 # classpaths.  They can, however, be included wholesale in
 # other java modules.
 
+$(call record-module-type,STATIC_JAVA_LIBRARY)
 LOCAL_UNINSTALLABLE_MODULE := true
 LOCAL_IS_STATIC_JAVA_LIBRARY := true
 LOCAL_MODULE_CLASS := JAVA_LIBRARIES
diff --git a/core/static_library.mk b/core/static_library.mk
index a8ae399..25e5279 100644
--- a/core/static_library.mk
+++ b/core/static_library.mk
@@ -1,3 +1,4 @@
+$(call record-module-type,STATIC_LIBRARY)
 my_prefix := TARGET_
 include $(BUILD_SYSTEM)/multilib.mk
 
diff --git a/core/static_test_lib.mk b/core/static_test_lib.mk
index 9d0bcc8..a0e2970 100644
--- a/core/static_test_lib.mk
+++ b/core/static_test_lib.mk
@@ -2,6 +2,7 @@
 ## A thin wrapper around BUILD_STATIC_LIBRARY
 ## Common flags for native tests are added.
 #############################################
+$(call record-module-type,STATIC_TEST_LIBRARY)
 
 include $(BUILD_SYSTEM)/target_test_internal.mk
 
diff --git a/core/tasks/check_boot_jars/package_whitelist.txt b/core/tasks/check_boot_jars/package_whitelist.txt
index 1478aae..601017b 100644
--- a/core/tasks/check_boot_jars/package_whitelist.txt
+++ b/core/tasks/check_boot_jars/package_whitelist.txt
@@ -8,6 +8,7 @@
 java\.io
 java\.lang
 java\.lang\.annotation
+java\.lang\.invoke
 java\.lang\.ref
 java\.lang\.reflect
 java\.math
@@ -66,6 +67,8 @@
 org\.w3c\.dom\.ls
 org\.w3c\.dom\.traversal
 # OpenJdk internal implementation.
+sun\.invoke\.util
+sun\.invoke\.empty
 sun\.misc
 sun\.util.*
 sun\.text.*
diff --git a/tools/apksigner/Android.mk b/tools/apksigner/Android.mk
deleted file mode 100644
index a7b4414..0000000
--- a/tools/apksigner/Android.mk
+++ /dev/null
@@ -1,19 +0,0 @@
-#
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-LOCAL_PATH := $(call my-dir)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tools/apksigner/core/Android.mk b/tools/apksigner/core/Android.mk
deleted file mode 100644
index 132a6f1..0000000
--- a/tools/apksigner/core/Android.mk
+++ /dev/null
@@ -1,29 +0,0 @@
-#
-# Copyright (C) 2016 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-LOCAL_PATH := $(call my-dir)
-
-# apksigner library, for signing APKs and verification signatures of APKs
-# ============================================================
-include $(CLEAR_VARS)
-LOCAL_MODULE := apksigner-core
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-# Disable warnnings about our use of internal proprietary OpenJDK API.
-# TODO: Remove this workaround by moving to our own implementation of PKCS #7
-# SignedData block generation, parsing, and verification.
-LOCAL_JAVACFLAGS := -XDignore.symbol.file
-
-include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/tools/apksigner/core/src/com/android/apksigner/core/ApkSigner.java b/tools/apksigner/core/src/com/android/apksigner/core/ApkSigner.java
deleted file mode 100644
index 2491302..0000000
--- a/tools/apksigner/core/src/com/android/apksigner/core/ApkSigner.java
+++ /dev/null
@@ -1,711 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.apksigner.core;
-
-import java.io.Closeable;
-import java.io.File;
-import java.io.IOException;
-import java.io.RandomAccessFile;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.security.SignatureException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import com.android.apksigner.core.apk.ApkUtils;
-import com.android.apksigner.core.internal.apk.v2.V2SchemeVerifier;
-import com.android.apksigner.core.internal.util.ByteBufferDataSource;
-import com.android.apksigner.core.internal.util.Pair;
-import com.android.apksigner.core.internal.zip.CentralDirectoryRecord;
-import com.android.apksigner.core.internal.zip.EocdRecord;
-import com.android.apksigner.core.internal.zip.LocalFileRecord;
-import com.android.apksigner.core.internal.zip.ZipUtils;
-import com.android.apksigner.core.util.DataSink;
-import com.android.apksigner.core.util.DataSinks;
-import com.android.apksigner.core.util.DataSource;
-import com.android.apksigner.core.util.DataSources;
-import com.android.apksigner.core.zip.ZipFormatException;
-
-/**
- * APK signer.
- *
- * <p>The signer preserves as much of the input APK as possible. For example, it preserves the
- * order of APK entries and preserves their contents, including compressed form and alignment of
- * data.
- *
- * <p>Use {@link Builder} to obtain instances of this signer.
- */
-public class ApkSigner {
-
-    /**
-     * Extensible data block/field header ID used for storing information about alignment of
-     * uncompressed entries as well as for aligning the entries's data. See ZIP appnote.txt section
-     * 4.5 Extensible data fields.
-     */
-    private static final short ALIGNMENT_ZIP_EXTRA_DATA_FIELD_HEADER_ID = (short) 0xd935;
-
-    /**
-     * Minimum size (in bytes) of the extensible data block/field used for alignment of uncompressed
-     * entries.
-     */
-    private static final short ALIGNMENT_ZIP_EXTRA_DATA_FIELD_MIN_SIZE_BYTES = 6;
-
-    private final ApkSignerEngine mSignerEngine;
-
-    private final File mInputApkFile;
-    private final DataSource mInputApkDataSource;
-
-    private final File mOutputApkFile;
-    private final DataSink mOutputApkDataSink;
-    private final DataSource mOutputApkDataSource;
-
-    private ApkSigner(
-            ApkSignerEngine signerEngine,
-            File inputApkFile,
-            DataSource inputApkDataSource,
-            File outputApkFile,
-            DataSink outputApkDataSink,
-            DataSource outputApkDataSource) {
-        mSignerEngine = signerEngine;
-
-        mInputApkFile = inputApkFile;
-        mInputApkDataSource = inputApkDataSource;
-
-        mOutputApkFile = outputApkFile;
-        mOutputApkDataSink = outputApkDataSink;
-        mOutputApkDataSource = outputApkDataSource;
-    }
-
-    /**
-     * Signs the input APK and outputs the resulting signed APK. The input APK is not modified.
-     *
-     * @throws IOException if an I/O error is encountered while reading or writing the APKs
-     * @throws ZipFormatException if the input APK is malformed at ZIP format level
-     * @throws NoSuchAlgorithmException if the APK signatures cannot be produced or verified because
-     *         a required cryptographic algorithm implementation is missing
-     * @throws InvalidKeyException if a signature could not be generated because a signing key is
-     *         not suitable for generating the signature
-     * @throws SignatureException if an error occurred while generating or verifying a signature
-     * @throws IllegalStateException if this signer's configuration is missing required information
-     *         or if the signing engine is in an invalid state.
-     */
-    public void sign()
-            throws IOException, ZipFormatException, NoSuchAlgorithmException, InvalidKeyException,
-                    SignatureException, IllegalStateException {
-        Closeable in = null;
-        DataSource inputApk;
-        try {
-            if (mInputApkDataSource != null) {
-                inputApk = mInputApkDataSource;
-            } else if (mInputApkFile != null) {
-                RandomAccessFile inputFile = new RandomAccessFile(mInputApkFile, "r");
-                in = inputFile;
-                inputApk = DataSources.asDataSource(inputFile);
-            } else {
-                throw new IllegalStateException("Input APK not specified");
-            }
-
-            Closeable out = null;
-            try {
-                DataSink outputApkOut;
-                DataSource outputApkIn;
-                if (mOutputApkDataSink != null) {
-                    outputApkOut = mOutputApkDataSink;
-                    outputApkIn = mOutputApkDataSource;
-                } else if (mOutputApkFile != null) {
-                    RandomAccessFile outputFile = new RandomAccessFile(mOutputApkFile, "rw");
-                    out = outputFile;
-                    outputFile.setLength(0);
-                    outputApkOut = DataSinks.asDataSink(outputFile);
-                    outputApkIn = DataSources.asDataSource(outputFile);
-                } else {
-                    throw new IllegalStateException("Output APK not specified");
-                }
-
-                sign(mSignerEngine, inputApk, outputApkOut, outputApkIn);
-            } finally {
-                if (out != null) {
-                    out.close();
-                }
-            }
-        } finally {
-            if (in != null) {
-                in.close();
-            }
-        }
-    }
-
-    private static void sign(
-            ApkSignerEngine signerEngine,
-            DataSource inputApk,
-            DataSink outputApkOut,
-            DataSource outputApkIn)
-                    throws IOException, ZipFormatException, NoSuchAlgorithmException,
-                            InvalidKeyException, SignatureException {
-        // Step 1. Find input APK's main ZIP sections
-        ApkUtils.ZipSections inputZipSections = ApkUtils.findZipSections(inputApk);
-        long apkSigningBlockOffset = -1;
-        try {
-            Pair<DataSource, Long> apkSigningBlockAndOffset =
-                    V2SchemeVerifier.findApkSigningBlock(inputApk, inputZipSections);
-            signerEngine.inputApkSigningBlock(apkSigningBlockAndOffset.getFirst());
-            apkSigningBlockOffset = apkSigningBlockAndOffset.getSecond();
-        } catch (V2SchemeVerifier.SignatureNotFoundException e) {
-            // Input APK does not contain an APK Signing Block. That's OK. APKs are not required to
-            // contain this block. It's only needed if the APK is signed using APK Signature Scheme
-            // v2.
-        }
-
-        // Step 2. Parse the input APK's ZIP Central Directory
-        ByteBuffer inputCd = getZipCentralDirectory(inputApk, inputZipSections);
-        List<CentralDirectoryRecord> inputCdRecords =
-                parseZipCentralDirectory(inputCd, inputZipSections);
-
-        // Step 3. Iterate over input APK's entries and output the Local File Header + data of those
-        // entries which need to be output. Entries are iterated in the order in which their Local
-        // File Header records are stored in the file. This is to achieve better data locality in
-        // case Central Directory entries are in the wrong order.
-        List<CentralDirectoryRecord> inputCdRecordsSortedByLfhOffset =
-                new ArrayList<>(inputCdRecords);
-        Collections.sort(
-                inputCdRecordsSortedByLfhOffset,
-                CentralDirectoryRecord.BY_LOCAL_FILE_HEADER_OFFSET_COMPARATOR);
-        DataSource inputApkLfhSection =
-                inputApk.slice(
-                        0,
-                        (apkSigningBlockOffset != -1)
-                                ? apkSigningBlockOffset
-                                : inputZipSections.getZipCentralDirectoryOffset());
-        int lastModifiedDateForNewEntries = -1;
-        int lastModifiedTimeForNewEntries = -1;
-        long inputOffset = 0;
-        long outputOffset = 0;
-        Map<String, CentralDirectoryRecord> outputCdRecordsByName =
-                new HashMap<>(inputCdRecords.size());
-        for (final CentralDirectoryRecord inputCdRecord : inputCdRecordsSortedByLfhOffset) {
-            String entryName = inputCdRecord.getName();
-            ApkSignerEngine.InputJarEntryInstructions entryInstructions =
-                    signerEngine.inputJarEntry(entryName);
-            boolean shouldOutput;
-            switch (entryInstructions.getOutputPolicy()) {
-                case OUTPUT:
-                    shouldOutput = true;
-                    break;
-                case OUTPUT_BY_ENGINE:
-                case SKIP:
-                    shouldOutput = false;
-                    break;
-                default:
-                    throw new RuntimeException(
-                            "Unknown output policy: " + entryInstructions.getOutputPolicy());
-            }
-
-            long inputLocalFileHeaderStartOffset = inputCdRecord.getLocalFileHeaderOffset();
-            if (inputLocalFileHeaderStartOffset > inputOffset) {
-                // Unprocessed data in input starting at inputOffset and ending and the start of
-                // this record's LFH. We output this data verbatim because this signer is supposed
-                // to preserve as much of input as possible.
-                long chunkSize = inputLocalFileHeaderStartOffset - inputOffset;
-                inputApkLfhSection.feed(inputOffset, chunkSize, outputApkOut);
-                outputOffset += chunkSize;
-                inputOffset = inputLocalFileHeaderStartOffset;
-            }
-            LocalFileRecord inputLocalFileRecord =
-                    LocalFileRecord.getRecord(
-                            inputApkLfhSection, inputCdRecord, inputApkLfhSection.size());
-            inputOffset += inputLocalFileRecord.getSize();
-
-            ApkSignerEngine.InspectJarEntryRequest inspectEntryRequest =
-                    entryInstructions.getInspectJarEntryRequest();
-            if (inspectEntryRequest != null) {
-                fulfillInspectInputJarEntryRequest(
-                        inputApkLfhSection, inputLocalFileRecord, inspectEntryRequest);
-            }
-
-            if (shouldOutput) {
-                // Find the max value of last modified, to be used for new entries added by the
-                // signer.
-                int lastModifiedDate = inputCdRecord.getLastModificationDate();
-                int lastModifiedTime = inputCdRecord.getLastModificationTime();
-                if ((lastModifiedDateForNewEntries == -1)
-                        || (lastModifiedDate > lastModifiedDateForNewEntries)
-                        || ((lastModifiedDate == lastModifiedDateForNewEntries)
-                                && (lastModifiedTime > lastModifiedTimeForNewEntries))) {
-                    lastModifiedDateForNewEntries = lastModifiedDate;
-                    lastModifiedTimeForNewEntries = lastModifiedTime;
-                }
-
-                inspectEntryRequest = signerEngine.outputJarEntry(entryName);
-                if (inspectEntryRequest != null) {
-                    fulfillInspectInputJarEntryRequest(
-                            inputApkLfhSection, inputLocalFileRecord, inspectEntryRequest);
-                }
-
-                // Output entry's Local File Header + data
-                long outputLocalFileHeaderOffset = outputOffset;
-                long outputLocalFileRecordSize =
-                        outputInputJarEntryLfhRecordPreservingDataAlignment(
-                                inputApkLfhSection,
-                                inputLocalFileRecord,
-                                outputApkOut,
-                                outputLocalFileHeaderOffset);
-                outputOffset += outputLocalFileRecordSize;
-
-                // Enqueue entry's Central Directory record for output
-                CentralDirectoryRecord outputCdRecord;
-                if (outputLocalFileHeaderOffset == inputLocalFileRecord.getStartOffsetInArchive()) {
-                    outputCdRecord = inputCdRecord;
-                } else {
-                    outputCdRecord =
-                            inputCdRecord.createWithModifiedLocalFileHeaderOffset(
-                                    outputLocalFileHeaderOffset);
-                }
-                outputCdRecordsByName.put(entryName, outputCdRecord);
-            }
-        }
-        long inputLfhSectionSize = inputApkLfhSection.size();
-        if (inputOffset < inputLfhSectionSize) {
-            // Unprocessed data in input starting at inputOffset and ending and the end of the input
-            // APK's LFH section. We output this data verbatim because this signer is supposed
-            // to preserve as much of input as possible.
-            long chunkSize = inputLfhSectionSize - inputOffset;
-            inputApkLfhSection.feed(inputOffset, chunkSize, outputApkOut);
-            outputOffset += chunkSize;
-            inputOffset = inputLfhSectionSize;
-        }
-
-        // Step 4. Sort output APK's Central Directory records in the order in which they should
-        // appear in the output
-        List<CentralDirectoryRecord> outputCdRecords = new ArrayList<>(inputCdRecords.size() + 10);
-        for (CentralDirectoryRecord inputCdRecord : inputCdRecords) {
-            String entryName = inputCdRecord.getName();
-            CentralDirectoryRecord outputCdRecord = outputCdRecordsByName.get(entryName);
-            if (outputCdRecord != null) {
-                outputCdRecords.add(outputCdRecord);
-            }
-        }
-
-        // Step 5. Generate and output JAR signatures, if necessary. This may output more Local File
-        // Header + data entries and add to the list of output Central Directory records.
-        ApkSignerEngine.OutputJarSignatureRequest outputJarSignatureRequest =
-                signerEngine.outputJarEntries();
-        if (outputJarSignatureRequest != null) {
-            if (lastModifiedDateForNewEntries == -1) {
-                lastModifiedDateForNewEntries = 0x3a21; // Jan 1 2009 (DOS)
-                lastModifiedTimeForNewEntries = 0;
-            }
-            for (ApkSignerEngine.OutputJarSignatureRequest.JarEntry entry :
-                    outputJarSignatureRequest.getAdditionalJarEntries()) {
-                String entryName = entry.getName();
-                byte[] uncompressedData = entry.getData();
-                ZipUtils.DeflateResult deflateResult =
-                        ZipUtils.deflate(ByteBuffer.wrap(uncompressedData));
-                byte[] compressedData = deflateResult.output;
-                long uncompressedDataCrc32 = deflateResult.inputCrc32;
-
-                ApkSignerEngine.InspectJarEntryRequest inspectEntryRequest =
-                        signerEngine.outputJarEntry(entryName);
-                if (inspectEntryRequest != null) {
-                    inspectEntryRequest.getDataSink().consume(
-                            uncompressedData, 0, uncompressedData.length);
-                    inspectEntryRequest.done();
-                }
-
-                long localFileHeaderOffset = outputOffset;
-                outputOffset +=
-                        LocalFileRecord.outputRecordWithDeflateCompressedData(
-                                entryName,
-                                lastModifiedTimeForNewEntries,
-                                lastModifiedDateForNewEntries,
-                                compressedData,
-                                uncompressedDataCrc32,
-                                uncompressedData.length,
-                                outputApkOut);
-
-
-                outputCdRecords.add(
-                        CentralDirectoryRecord.createWithDeflateCompressedData(
-                                entryName,
-                                lastModifiedTimeForNewEntries,
-                                lastModifiedDateForNewEntries,
-                                uncompressedDataCrc32,
-                                compressedData.length,
-                                uncompressedData.length,
-                                localFileHeaderOffset));
-            }
-            outputJarSignatureRequest.done();
-        }
-
-        // Step 6. Construct output ZIP Central Directory in an in-memory buffer
-        long outputCentralDirSizeBytes = 0;
-        for (CentralDirectoryRecord record : outputCdRecords) {
-            outputCentralDirSizeBytes += record.getSize();
-        }
-        if (outputCentralDirSizeBytes > Integer.MAX_VALUE) {
-            throw new IOException(
-                    "Output ZIP Central Directory too large: " + outputCentralDirSizeBytes
-                            + " bytes");
-        }
-        ByteBuffer outputCentralDir = ByteBuffer.allocate((int) outputCentralDirSizeBytes);
-        for (CentralDirectoryRecord record : outputCdRecords) {
-            record.copyTo(outputCentralDir);
-        }
-        outputCentralDir.flip();
-        DataSource outputCentralDirDataSource = new ByteBufferDataSource(outputCentralDir);
-        long outputCentralDirStartOffset = outputOffset;
-        int outputCentralDirRecordCount = outputCdRecords.size();
-
-        // Step 7. Construct output ZIP End of Central Directory record in an in-memory buffer
-        ByteBuffer outputEocd =
-                EocdRecord.createWithModifiedCentralDirectoryInfo(
-                        inputZipSections.getZipEndOfCentralDirectory(),
-                        outputCentralDirRecordCount,
-                        outputCentralDirDataSource.size(),
-                        outputCentralDirStartOffset);
-
-        // Step 8. Generate and output APK Signature Scheme v2 signatures, if necessary. This may
-        // insert an APK Signing Block just before the output's ZIP Central Directory
-        ApkSignerEngine.OutputApkSigningBlockRequest outputApkSigingBlockRequest =
-                signerEngine.outputZipSections(
-                        outputApkIn,
-                        outputCentralDirDataSource,
-                        DataSources.asDataSource(outputEocd));
-        if (outputApkSigingBlockRequest != null) {
-            byte[] outputApkSigningBlock = outputApkSigingBlockRequest.getApkSigningBlock();
-            outputApkOut.consume(outputApkSigningBlock, 0, outputApkSigningBlock.length);
-            ZipUtils.setZipEocdCentralDirectoryOffset(
-                    outputEocd, outputCentralDirStartOffset + outputApkSigningBlock.length);
-            outputApkSigingBlockRequest.done();
-        }
-
-        // Step 9. Output ZIP Central Directory and ZIP End of Central Directory
-        outputCentralDirDataSource.feed(0, outputCentralDirDataSource.size(), outputApkOut);
-        outputApkOut.consume(outputEocd);
-        signerEngine.outputDone();
-    }
-
-    private static void fulfillInspectInputJarEntryRequest(
-            DataSource lfhSection,
-            LocalFileRecord localFileRecord,
-            ApkSignerEngine.InspectJarEntryRequest inspectEntryRequest)
-                    throws IOException, ZipFormatException {
-        localFileRecord.outputUncompressedData(lfhSection, inspectEntryRequest.getDataSink());
-        inspectEntryRequest.done();
-    }
-
-    private static long outputInputJarEntryLfhRecordPreservingDataAlignment(
-            DataSource inputLfhSection,
-            LocalFileRecord inputRecord,
-            DataSink outputLfhSection,
-            long outputOffset) throws IOException {
-        long inputOffset = inputRecord.getStartOffsetInArchive();
-        if (inputOffset == outputOffset) {
-            // This record's data will be aligned same as in the input APK.
-            return inputRecord.outputRecord(inputLfhSection, outputLfhSection);
-        }
-        int dataAlignmentMultiple = getInputJarEntryDataAlignmentMultiple(inputRecord);
-        if ((dataAlignmentMultiple <= 1)
-                || ((inputOffset % dataAlignmentMultiple)
-                        == (outputOffset % dataAlignmentMultiple))) {
-            // This record's data will be aligned same as in the input APK.
-            return inputRecord.outputRecord(inputLfhSection, outputLfhSection);
-        }
-
-        long inputDataStartOffset = inputOffset + inputRecord.getDataStartOffsetInRecord();
-        if ((inputDataStartOffset % dataAlignmentMultiple) != 0) {
-            // This record's data is not aligned in the input APK. No need to align it in the
-            // output.
-            return inputRecord.outputRecord(inputLfhSection, outputLfhSection);
-        }
-
-        // This record's data needs to be re-aligned in the output. This is achieved using the
-        // record's extra field.
-        ByteBuffer aligningExtra =
-                createExtraFieldToAlignData(
-                        inputRecord.getExtra(),
-                        outputOffset + inputRecord.getExtraFieldStartOffsetInsideRecord(),
-                        dataAlignmentMultiple);
-        return inputRecord.outputRecordWithModifiedExtra(
-                inputLfhSection, aligningExtra, outputLfhSection);
-    }
-
-    private static int getInputJarEntryDataAlignmentMultiple(LocalFileRecord entry) {
-        if (entry.isDataCompressed()) {
-            // Compressed entries don't need to be aligned
-            return 1;
-        }
-
-        // Attempt to obtain the alignment multiple from the entry's extra field.
-        ByteBuffer extra = entry.getExtra();
-        if (extra.hasRemaining()) {
-            extra.order(ByteOrder.LITTLE_ENDIAN);
-            // FORMAT: sequence of fields. Each field consists of:
-            //   * uint16 ID
-            //   * uint16 size
-            //   * 'size' bytes: payload
-            while (extra.remaining() >= 4) {
-                short headerId  = extra.getShort();
-                int dataSize = ZipUtils.getUnsignedInt16(extra);
-                if (dataSize > extra.remaining()) {
-                    // Malformed field -- insufficient input remaining
-                    break;
-                }
-                if (headerId != ALIGNMENT_ZIP_EXTRA_DATA_FIELD_HEADER_ID) {
-                    // Skip this field
-                    extra.position(extra.position() + dataSize);
-                    continue;
-                }
-                // This is APK alignment field.
-                // FORMAT:
-                //  * uint16 alignment multiple (in bytes)
-                //  * remaining bytes -- padding to achieve alignment of data which starts after
-                //    the extra field
-                if (dataSize < 2) {
-                    // Malformed
-                    break;
-                }
-                return ZipUtils.getUnsignedInt16(extra);
-            }
-        }
-
-        // Fall back to filename-based defaults
-        return (entry.getName().endsWith(".so")) ? 4096 : 4;
-    }
-
-    private static ByteBuffer createExtraFieldToAlignData(
-            ByteBuffer original,
-            long extraStartOffset,
-            int dataAlignmentMultiple) {
-        if (dataAlignmentMultiple <= 1) {
-            return original;
-        }
-
-        // In the worst case scenario, we'll increase the output size by 6 + dataAlignment - 1.
-        ByteBuffer result = ByteBuffer.allocate(original.remaining() + 5 + dataAlignmentMultiple);
-        result.order(ByteOrder.LITTLE_ENDIAN);
-
-        // Step 1. Output all extra fields other than the one which is to do with alignment
-        // FORMAT: sequence of fields. Each field consists of:
-        //   * uint16 ID
-        //   * uint16 size
-        //   * 'size' bytes: payload
-        while (original.remaining() >= 4) {
-            short headerId  = original.getShort();
-            int dataSize = ZipUtils.getUnsignedInt16(original);
-            if (dataSize > original.remaining()) {
-                // Malformed field -- insufficient input remaining
-                break;
-            }
-            if (((headerId == 0) && (dataSize == 0))
-                    || (headerId == ALIGNMENT_ZIP_EXTRA_DATA_FIELD_HEADER_ID)) {
-                // Ignore the field if it has to do with the old APK data alignment method (filling
-                // the extra field with 0x00 bytes) or the new APK data alignment method.
-                original.position(original.position() + dataSize);
-                continue;
-            }
-            // Copy this field (including header) to the output
-            original.position(original.position() - 4);
-            int originalLimit = original.limit();
-            original.limit(original.position() + 4 + dataSize);
-            result.put(original);
-            original.limit(originalLimit);
-        }
-
-        // Step 2. Add alignment field
-        // FORMAT:
-        //  * uint16 extra header ID
-        //  * uint16 extra data size
-        //        Payload ('data size' bytes)
-        //      * uint16 alignment multiple (in bytes)
-        //      * remaining bytes -- padding to achieve alignment of data which starts after the
-        //        extra field
-        long dataMinStartOffset =
-                extraStartOffset + result.position()
-                        + ALIGNMENT_ZIP_EXTRA_DATA_FIELD_MIN_SIZE_BYTES;
-        int paddingSizeBytes =
-                (dataAlignmentMultiple - ((int) (dataMinStartOffset % dataAlignmentMultiple)))
-                        % dataAlignmentMultiple;
-        result.putShort(ALIGNMENT_ZIP_EXTRA_DATA_FIELD_HEADER_ID);
-        ZipUtils.putUnsignedInt16(result, 2 + paddingSizeBytes);
-        ZipUtils.putUnsignedInt16(result, dataAlignmentMultiple);
-        result.position(result.position() + paddingSizeBytes);
-        result.flip();
-
-        return result;
-    }
-
-    private static ByteBuffer getZipCentralDirectory(
-            DataSource apk,
-            ApkUtils.ZipSections apkSections) throws IOException, ZipFormatException {
-        long cdSizeBytes = apkSections.getZipCentralDirectorySizeBytes();
-        if (cdSizeBytes > Integer.MAX_VALUE) {
-            throw new ZipFormatException("ZIP Central Directory too large: " + cdSizeBytes);
-        }
-        long cdOffset = apkSections.getZipCentralDirectoryOffset();
-        ByteBuffer cd = apk.getByteBuffer(cdOffset, (int) cdSizeBytes);
-        cd.order(ByteOrder.LITTLE_ENDIAN);
-        return cd;
-    }
-
-    private static List<CentralDirectoryRecord> parseZipCentralDirectory(
-            ByteBuffer cd,
-            ApkUtils.ZipSections apkSections) throws ZipFormatException {
-        long cdOffset = apkSections.getZipCentralDirectoryOffset();
-        int expectedCdRecordCount = apkSections.getZipCentralDirectoryRecordCount();
-        List<CentralDirectoryRecord> cdRecords = new ArrayList<>(expectedCdRecordCount);
-        Set<String> entryNames = new HashSet<>(expectedCdRecordCount);
-        for (int i = 0; i < expectedCdRecordCount; i++) {
-            CentralDirectoryRecord cdRecord;
-            int offsetInsideCd = cd.position();
-            try {
-                cdRecord = CentralDirectoryRecord.getRecord(cd);
-            } catch (ZipFormatException e) {
-                throw new ZipFormatException(
-                        "Failed to parse ZIP Central Directory record #" + (i + 1)
-                                + " at file offset " + (cdOffset + offsetInsideCd),
-                        e);
-            }
-            String entryName = cdRecord.getName();
-            if (!entryNames.add(entryName)) {
-                throw new ZipFormatException(
-                        "Malformed APK: multiple JAR entries with the same name: " + entryName);
-            }
-            cdRecords.add(cdRecord);
-        }
-        if (cd.hasRemaining()) {
-            throw new ZipFormatException(
-                    "Unused space at the end of ZIP Central Directory: " + cd.remaining()
-                        + " bytes starting at file offset " + (cdOffset + cd.position()));
-        }
-
-        return cdRecords;
-    }
-
-    /**
-     * Builder of {@link ApkSigner} instances.
-     *
-     * <p>The following information is required to construct a working {@code ApkSigner}:
-     * <ul>
-     * <li>{@link ApkSignerEngine} -- provided in the constructor,</li>
-     * <li>APK to be signed -- see {@link #setInputApk(File) setInputApk} variants,</li>
-     * <li>where to store the signed APK -- see {@link #setOutputApk(File) setOutputApk} variants.
-     * </li>
-     * </ul>
-     */
-    public static class Builder {
-        private final ApkSignerEngine mSignerEngine;
-
-        private File mInputApkFile;
-        private DataSource mInputApkDataSource;
-
-        private File mOutputApkFile;
-        private DataSink mOutputApkDataSink;
-        private DataSource mOutputApkDataSource;
-
-        /**
-         * Constructs a new {@code Builder} which will make {@code ApkSigner} use the provided
-         * signing engine.
-         */
-        public Builder(ApkSignerEngine signerEngine) {
-            mSignerEngine = signerEngine;
-        }
-
-        /**
-         * Sets the APK to be signed.
-         *
-         * @see #setInputApk(DataSource)
-         */
-        public Builder setInputApk(File inputApk) {
-            if (inputApk == null) {
-                throw new NullPointerException("inputApk == null");
-            }
-            mInputApkFile = inputApk;
-            mInputApkDataSource = null;
-            return this;
-        }
-
-        /**
-         * Sets the APK to be signed.
-         *
-         * @see #setInputApk(File)
-         */
-        public Builder setInputApk(DataSource inputApk) {
-            if (inputApk == null) {
-                throw new NullPointerException("inputApk == null");
-            }
-            mInputApkDataSource = inputApk;
-            mInputApkFile = null;
-            return this;
-        }
-
-        /**
-         * Sets the location of the output (signed) APK. {@code ApkSigner} will create this file if
-         * it doesn't exist.
-         *
-         * @see #setOutputApk(DataSink, DataSource)
-         */
-        public Builder setOutputApk(File outputApk) {
-            if (outputApk == null) {
-                throw new NullPointerException("outputApk == null");
-            }
-            mOutputApkFile = outputApk;
-            mOutputApkDataSink = null;
-            mOutputApkDataSource = null;
-            return this;
-        }
-
-        /**
-         * Sets the sink which will receive the output (signed) APK. Data received by the
-         * {@code outputApkOut} sink must be visible through the {@code outputApkIn} data source.
-         *
-         * @see #setOutputApk(File)
-         */
-        public Builder setOutputApk(DataSink outputApkOut, DataSource outputApkIn) {
-            if (outputApkOut == null) {
-                throw new NullPointerException("outputApkOut == null");
-            }
-            if (outputApkIn == null) {
-                throw new NullPointerException("outputApkIn == null");
-            }
-            mOutputApkFile = null;
-            mOutputApkDataSink = outputApkOut;
-            mOutputApkDataSource = outputApkIn;
-            return this;
-        }
-
-        /**
-         * Returns a new {@code ApkSigner} instance initialized according to the configuration of
-         * this builder.
-         */
-        public ApkSigner build() {
-            return new ApkSigner(
-                    mSignerEngine,
-                    mInputApkFile,
-                    mInputApkDataSource,
-                    mOutputApkFile,
-                    mOutputApkDataSink,
-                    mOutputApkDataSource);
-        }
-    }
-}
diff --git a/tools/apksigner/core/src/com/android/apksigner/core/ApkSignerEngine.java b/tools/apksigner/core/src/com/android/apksigner/core/ApkSignerEngine.java
deleted file mode 100644
index 21c2706..0000000
--- a/tools/apksigner/core/src/com/android/apksigner/core/ApkSignerEngine.java
+++ /dev/null
@@ -1,417 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.apksigner.core;
-
-import java.io.Closeable;
-import java.io.IOException;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.security.SignatureException;
-import java.util.List;
-
-import com.android.apksigner.core.util.DataSink;
-import com.android.apksigner.core.util.DataSource;
-
-/**
- * APK signing logic which is independent of how input and output APKs are stored, parsed, and
- * generated.
- *
- * <p><h3>Operating Model</h3>
- *
- * The abstract operating model is that there is an input APK which is being signed, thus producing
- * an output APK. In reality, there may be just an output APK being built from scratch, or the input
- * APK and the output APK may be the same file. Because this engine does not deal with reading and
- * writing files, it can handle all of these scenarios.
- *
- * <p>The engine is stateful and thus cannot be used for signing multiple APKs. However, once
- * the engine signed an APK, the engine can be used to re-sign the APK after it has been modified.
- * This may be more efficient than signing the APK using a new instance of the engine. See
- * <a href="#incremental">Incremental Operation</a>.
- *
- * <p>In the engine's operating model, a signed APK is produced as follows.
- * <ol>
- * <li>JAR entries to be signed are output,</li>
- * <li>JAR archive is signed using JAR signing, thus adding the so-called v1 signature to the
- *     output,</li>
- * <li>JAR archive is signed using APK Signature Scheme v2, thus adding the so-called v2 signature
- *     to the output.</li>
- * </ol>
- *
- * <p>The input APK may contain JAR entries which, depending on the engine's configuration, may or
- * may not be output (e.g., existing signatures may need to be preserved or stripped) or which the
- * engine will overwrite as part of signing. The engine thus offers {@link #inputJarEntry(String)}
- * which tells the client whether the input JAR entry needs to be output. This avoids the need for
- * the client to hard-code the aspects of APK signing which determine which parts of input must be
- * ignored. Similarly, the engine offers {@link #inputApkSigningBlock(DataSource)} to help the
- * client avoid dealing with preserving or stripping APK Signature Scheme v2 signature of the input
- * APK.
- *
- * <p>To use the engine to sign an input APK (or a collection of JAR entries), follow these
- * steps:
- * <ol>
- * <li>Obtain a new instance of the engine -- engine instances are stateful and thus cannot be used
- *     for signing multiple APKs.</li>
- * <li>Locate the input APK's APK Signing Block and provide it to
- *     {@link #inputApkSigningBlock(DataSource)}.</li>
- * <li>For each JAR entry in the input APK, invoke {@link #inputJarEntry(String)} to determine
- *     whether this entry should be output. The engine may request to inspect the entry.</li>
- * <li>For each output JAR entry, invoke {@link #outputJarEntry(String)} which may request to
- *     inspect the entry.</li>
- * <li>Once all JAR entries have been output, invoke {@link #outputJarEntries()} which may request
- *     that additional JAR entries are output. These entries comprise the output APK's JAR
- *     signature.</li>
- * <li>Locate the ZIP Central Directory and ZIP End of Central Directory sections in the output and
- *     invoke {@link #outputZipSections(DataSource, DataSource, DataSource)} which may request that
- *     an APK Signature Block is inserted before the ZIP Central Directory. The block contains the
- *     output APK's APK Signature Scheme v2 signature.</li>
- * <li>Invoke {@link #outputDone()} to signal that the APK was output in full. The engine will
- *     confirm that the output APK is signed.</li>
- * <li>Invoke {@link #close()} to signal that the engine will no longer be used. This lets the
- *     engine free any resources it no longer needs.
- * </ol>
- *
- * <p>Some invocations of the engine may provide the client with a task to perform. The client is
- * expected to perform all requested tasks before proceeding to the next stage of signing. See
- * documentation of each method about the deadlines for performing the tasks requested by the
- * method.
- *
- * <p><h3 id="incremental">Incremental Operation</h3></a>
- *
- * The engine supports incremental operation where a signed APK is produced, then modified and
- * re-signed. This may be useful for IDEs, where an app is frequently re-signed after small changes
- * by the developer. Re-signing may be more efficient than signing from scratch.
- *
- * <p>To use the engine in incremental mode, keep notifying the engine of changes to the APK through
- * {@link #inputApkSigningBlock(DataSource)}, {@link #inputJarEntry(String)},
- * {@link #inputJarEntryRemoved(String)}, {@link #outputJarEntry(String)},
- * and {@link #outputJarEntryRemoved(String)}, perform the tasks requested by the engine through
- * these methods, and, when a new signed APK is desired, run through steps 5 onwards to re-sign the
- * APK.
- *
- * <p><h3>Output-only Operation</h3>
- *
- * The engine's abstract operating model consists of an input APK and an output APK. However, it is
- * possible to use the engine in output-only mode where the engine's {@code input...} methods are
- * not invoked. In this mode, the engine has less control over output because it cannot request that
- * some JAR entries are not output. Nevertheless, the engine will attempt to make the output APK
- * signed and will report an error if cannot do so.
- */
-public interface ApkSignerEngine extends Closeable {
-
-    /**
-     * Indicates to this engine that the input APK contains the provided APK Signing Block. The
-     * block may contain signatures of the input APK, such as APK Signature Scheme v2 signatures.
-     *
-     * @param apkSigningBlock APK signing block of the input APK. The provided data source is
-     *        guaranteed to not be used by the engine after this method terminates.
-     *
-     * @throws IOException if an I/O error occurs while reading the APK Signing Block
-     * @throws IllegalStateException if this engine is closed
-     */
-    void inputApkSigningBlock(DataSource apkSigningBlock) throws IOException, IllegalStateException;
-
-    /**
-     * Indicates to this engine that the specified JAR entry was encountered in the input APK.
-     *
-     * <p>When an input entry is updated/changed, it's OK to not invoke
-     * {@link #inputJarEntryRemoved(String)} before invoking this method.
-     *
-     * @return instructions about how to proceed with this entry
-     *
-     * @throws IllegalStateException if this engine is closed
-     */
-    InputJarEntryInstructions inputJarEntry(String entryName) throws IllegalStateException;
-
-    /**
-     * Indicates to this engine that the specified JAR entry was output.
-     *
-     * <p>It is unnecessary to invoke this method for entries added to output by this engine (e.g.,
-     * requested by {@link #outputJarEntries()}) provided the entries were output with exactly the
-     * data requested by the engine.
-     *
-     * <p>When an already output entry is updated/changed, it's OK to not invoke
-     * {@link #outputJarEntryRemoved(String)} before invoking this method.
-     *
-     * @return request to inspect the entry or {@code null} if the engine does not need to inspect
-     *         the entry. The request must be fulfilled before {@link #outputJarEntries()} is
-     *         invoked.
-     *
-     * @throws IllegalStateException if this engine is closed
-     */
-    InspectJarEntryRequest outputJarEntry(String entryName) throws IllegalStateException;
-
-    /**
-     * Indicates to this engine that the specified JAR entry was removed from the input. It's safe
-     * to invoke this for entries for which {@link #inputJarEntry(String)} hasn't been invoked.
-     *
-     * @return output policy of this JAR entry. The policy indicates how this input entry affects
-     *         the output APK. The client of this engine should use this information to determine
-     *         how the removal of this input APK's JAR entry affects the output APK.
-     *
-     * @throws IllegalStateException if this engine is closed
-     */
-    InputJarEntryInstructions.OutputPolicy inputJarEntryRemoved(String entryName)
-            throws IllegalStateException;
-
-    /**
-     * Indicates to this engine that the specified JAR entry was removed from the output. It's safe
-     * to invoke this for entries for which {@link #outputJarEntry(String)} hasn't been invoked.
-     *
-     * @throws IllegalStateException if this engine is closed
-     */
-    void outputJarEntryRemoved(String entryName) throws IllegalStateException;
-
-    /**
-     * Indicates to this engine that all JAR entries have been output.
-     *
-     *
-     * @return request to add JAR signature to the output or {@code null} if there is no need to add
-     *         a JAR signature. The request will contain additional JAR entries to be output. The
-     *         request must be fulfilled before
-     *         {@link #outputZipSections(DataSource, DataSource, DataSource)} is invoked.
-     *
-     * @throws NoSuchAlgorithmException if a signature could not be generated because a required
-     *         cryptographic algorithm implementation is missing
-     * @throws InvalidKeyException if a signature could not be generated because a signing key is
-     *         not suitable for generating the signature
-     * @throws SignatureException if an error occurred while generating a signature
-     * @throws IllegalStateException if there are unfulfilled requests, such as to inspect some JAR
-     *         entries, or if the engine is closed
-     */
-    OutputJarSignatureRequest outputJarEntries()
-            throws NoSuchAlgorithmException, InvalidKeyException, SignatureException,
-                    IllegalStateException;
-
-    /**
-     * Indicates to this engine that the ZIP sections comprising the output APK have been output.
-     *
-     * <p>The provided data sources are guaranteed to not be used by the engine after this method
-     * terminates.
-     *
-     * @param zipEntries the section of ZIP archive containing Local File Header records and data of
-     *        the ZIP entries. In a well-formed archive, this section starts at the start of the
-     *        archive and extends all the way to the ZIP Central Directory.
-     * @param zipCentralDirectory ZIP Central Directory section
-     * @param zipEocd ZIP End of Central Directory (EoCD) record
-     *
-     * @return request to add an APK Signing Block to the output or {@code null} if the output must
-     *         not contain an APK Signing Block. The request must be fulfilled before
-     *         {@link #outputDone()} is invoked.
-     *
-     * @throws IOException if an I/O error occurs while reading the provided ZIP sections
-     * @throws NoSuchAlgorithmException if a signature could not be generated because a required
-     *         cryptographic algorithm implementation is missing
-     * @throws InvalidKeyException if a signature could not be generated because a signing key is
-     *         not suitable for generating the signature
-     * @throws SignatureException if an error occurred while generating a signature
-     * @throws IllegalStateException if there are unfulfilled requests, such as to inspect some JAR
-     *         entries or to output JAR signature, or if the engine is closed
-     */
-    OutputApkSigningBlockRequest outputZipSections(
-            DataSource zipEntries,
-            DataSource zipCentralDirectory,
-            DataSource zipEocd)
-                    throws IOException, NoSuchAlgorithmException, InvalidKeyException,
-                            SignatureException, IllegalStateException;
-
-    /**
-     * Indicates to this engine that the signed APK was output.
-     *
-     * <p>This does not change the output APK. The method helps the client confirm that the current
-     * output is signed.
-     *
-     * @throws IllegalStateException if there are unfulfilled requests, such as to inspect some JAR
-     *         entries or to output signatures, or if the engine is closed
-     */
-    void outputDone() throws IllegalStateException;
-
-    /**
-     * Indicates to this engine that it will no longer be used. Invoking this on an already closed
-     * engine is OK.
-     *
-     * <p>This does not change the output APK. For example, if the output APK is not yet fully
-     * signed, it will remain so after this method terminates.
-     */
-    @Override
-    void close();
-
-    /**
-     * Instructions about how to handle an input APK's JAR entry.
-     *
-     * <p>The instructions indicate whether to output the entry (see {@link #getOutputPolicy()}) and
-     * may contain a request to inspect the entry (see {@link #getInspectJarEntryRequest()}), in
-     * which case the request must be fulfilled before {@link ApkSignerEngine#outputJarEntries()} is
-     * invoked.
-     */
-    public static class InputJarEntryInstructions {
-        private final OutputPolicy mOutputPolicy;
-        private final InspectJarEntryRequest mInspectJarEntryRequest;
-
-        /**
-         * Constructs a new {@code InputJarEntryInstructions} instance with the provided entry
-         * output policy and without a request to inspect the entry.
-         */
-        public InputJarEntryInstructions(OutputPolicy outputPolicy) {
-            this(outputPolicy, null);
-        }
-
-        /**
-         * Constructs a new {@code InputJarEntryInstructions} instance with the provided entry
-         * output mode and with the provided request to inspect the entry.
-         *
-         * @param inspectJarEntryRequest request to inspect the entry or {@code null} if there's no
-         *        need to inspect the entry.
-         */
-        public InputJarEntryInstructions(
-                OutputPolicy outputPolicy,
-                InspectJarEntryRequest inspectJarEntryRequest) {
-            mOutputPolicy = outputPolicy;
-            mInspectJarEntryRequest = inspectJarEntryRequest;
-        }
-
-        /**
-         * Returns the output policy for this entry.
-         */
-        public OutputPolicy getOutputPolicy() {
-            return mOutputPolicy;
-        }
-
-        /**
-         * Returns the request to inspect the JAR entry or {@code null} if there is no need to
-         * inspect the entry.
-         */
-        public InspectJarEntryRequest getInspectJarEntryRequest() {
-            return mInspectJarEntryRequest;
-        }
-
-        /**
-         * Output policy for an input APK's JAR entry.
-         */
-        public static enum OutputPolicy {
-            /** Entry must not be output. */
-            SKIP,
-
-            /** Entry should be output. */
-            OUTPUT,
-
-            /** Entry will be output by the engine. The client can thus ignore this input entry. */
-            OUTPUT_BY_ENGINE,
-        }
-    }
-
-    /**
-     * Request to inspect the specified JAR entry.
-     *
-     * <p>The entry's uncompressed data must be provided to the data sink returned by
-     * {@link #getDataSink()}. Once the entry's data has been provided to the sink, {@link #done()}
-     * must be invoked.
-     */
-    interface InspectJarEntryRequest {
-
-        /**
-         * Returns the data sink into which the entry's uncompressed data should be sent.
-         */
-        DataSink getDataSink();
-
-        /**
-         * Indicates that entry's data has been provided in full.
-         */
-        void done();
-
-        /**
-         * Returns the name of the JAR entry.
-         */
-        String getEntryName();
-    }
-
-    /**
-     * Request to add JAR signature (aka v1 signature) to the output APK.
-     *
-     * <p>Entries listed in {@link #getAdditionalJarEntries()} must be added to the output APK after
-     * which {@link #done()} must be invoked.
-     */
-    interface OutputJarSignatureRequest {
-
-        /**
-         * Returns JAR entries that must be added to the output APK.
-         */
-        List<JarEntry> getAdditionalJarEntries();
-
-        /**
-         * Indicates that the JAR entries contained in this request were added to the output APK.
-         */
-        void done();
-
-        /**
-         * JAR entry.
-         */
-        public static class JarEntry {
-            private final String mName;
-            private final byte[] mData;
-
-            /**
-             * Constructs a new {@code JarEntry} with the provided name and data.
-             *
-             * @param data uncompressed data of the entry. Changes to this array will not be
-             *        reflected in {@link #getData()}.
-             */
-            public JarEntry(String name, byte[] data) {
-                mName = name;
-                mData = data.clone();
-            }
-
-            /**
-             * Returns the name of this ZIP entry.
-             */
-            public String getName() {
-                return mName;
-            }
-
-            /**
-             * Returns the uncompressed data of this JAR entry.
-             */
-            public byte[] getData() {
-                return mData.clone();
-            }
-        }
-    }
-
-    /**
-     * Request to add the specified APK Signing Block to the output APK. APK Signature Scheme v2
-     * signature(s) of the APK are contained in this block.
-     *
-     * <p>The APK Signing Block returned by {@link #getApkSigningBlock()} must be placed into the
-     * output APK such that the block is immediately before the ZIP Central Directory, the offset of
-     * ZIP Central Directory in the ZIP End of Central Directory record must be adjusted
-     * accordingly, and then {@link #done()} must be invoked.
-     *
-     * <p>If the output contains an APK Signing Block, that block must be replaced by the block
-     * contained in this request.
-     */
-    interface OutputApkSigningBlockRequest {
-
-        /**
-         * Returns the APK Signing Block.
-         */
-        byte[] getApkSigningBlock();
-
-        /**
-         * Indicates that the APK Signing Block was output as requested.
-         */
-        void done();
-    }
-}
diff --git a/tools/apksigner/core/src/com/android/apksigner/core/ApkVerifier.java b/tools/apksigner/core/src/com/android/apksigner/core/ApkVerifier.java
deleted file mode 100644
index f12b47f..0000000
--- a/tools/apksigner/core/src/com/android/apksigner/core/ApkVerifier.java
+++ /dev/null
@@ -1,1233 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.apksigner.core;
-
-import com.android.apksigner.core.apk.ApkUtils;
-import com.android.apksigner.core.internal.apk.v1.V1SchemeVerifier;
-import com.android.apksigner.core.internal.apk.v2.ContentDigestAlgorithm;
-import com.android.apksigner.core.internal.apk.v2.SignatureAlgorithm;
-import com.android.apksigner.core.internal.apk.v2.V2SchemeVerifier;
-import com.android.apksigner.core.internal.util.AndroidSdkVersion;
-import com.android.apksigner.core.util.DataSource;
-import com.android.apksigner.core.util.DataSources;
-import com.android.apksigner.core.zip.ZipFormatException;
-
-import java.io.Closeable;
-import java.io.File;
-import java.io.IOException;
-import java.io.RandomAccessFile;
-import java.security.NoSuchAlgorithmException;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * APK signature verifier which mimics the behavior of the Android platform.
- *
- * <p>The verifier is designed to closely mimic the behavior of Android platforms. This is to enable
- * the verifier to be used for checking whether an APK's signatures will verify on Android.
- *
- * <p>Use {@link Builder} to obtain instances of this verifier.
- */
-public class ApkVerifier {
-
-    private static final int APK_SIGNATURE_SCHEME_V2_ID = 2;
-    private static final Map<Integer, String> SUPPORTED_APK_SIG_SCHEME_NAMES =
-            Collections.singletonMap(APK_SIGNATURE_SCHEME_V2_ID, "APK Signature Scheme v2");
-
-    private final File mApkFile;
-    private final DataSource mApkDataSource;
-
-    private final int mMinSdkVersion;
-    private final int mMaxSdkVersion;
-
-    private ApkVerifier(
-            File apkFile,
-            DataSource apkDataSource,
-            int minSdkVersion,
-            int maxSdkVersion) {
-        mApkFile = apkFile;
-        mApkDataSource = apkDataSource;
-        mMinSdkVersion = minSdkVersion;
-        mMaxSdkVersion = maxSdkVersion;
-    }
-
-    /**
-     * Verifies the APK's signatures and returns the result of verification. The APK can be
-     * considered verified iff the result's {@link Result#isVerified()} returns {@code true}.
-     * The verification result also includes errors, warnings, and information about signers.
-     *
-     * @throws IOException if an I/O error is encountered while reading the APK
-     * @throws ZipFormatException if the APK is malformed at ZIP format level
-     * @throws NoSuchAlgorithmException if the APK's signatures cannot be verified because a
-     *         required cryptographic algorithm implementation is missing
-     * @throws IllegalStateException if this verifier's configuration is missing required
-     *         information.
-     */
-    public Result verify() throws IOException, ZipFormatException, NoSuchAlgorithmException,
-            IllegalStateException {
-        Closeable in = null;
-        try {
-            DataSource apk;
-            if (mApkDataSource != null) {
-                apk = mApkDataSource;
-            } else if (mApkFile != null) {
-                RandomAccessFile f = new RandomAccessFile(mApkFile, "r");
-                in = f;
-                apk = DataSources.asDataSource(f, 0, f.length());
-            } else {
-                throw new IllegalStateException("APK not provided");
-            }
-            return verify(apk, mMinSdkVersion, mMaxSdkVersion);
-        } finally {
-            if (in != null) {
-                in.close();
-            }
-        }
-    }
-
-    /**
-     * Verifies the APK's signatures and returns the result of verification. The APK can be
-     * considered verified iff the result's {@link Result#isVerified()} returns {@code true}.
-     * The verification result also includes errors, warnings, and information about signers.
-     *
-     * @param apk APK file contents
-     * @param minSdkVersion API Level of the oldest Android platform on which the APK's signatures
-     *        may need to be verified
-     * @param maxSdkVersion API Level of the newest Android platform on which the APK's signatures
-     *        may need to be verified
-     *
-     * @throws IOException if an I/O error is encountered while reading the APK
-     * @throws ZipFormatException if the APK is malformed at ZIP format level
-     * @throws NoSuchAlgorithmException if the APK's signatures cannot be verified because a
-     *         required cryptographic algorithm implementation is missing
-     */
-    private static Result verify(DataSource apk, int minSdkVersion, int maxSdkVersion)
-            throws IOException, ZipFormatException, NoSuchAlgorithmException {
-        if (minSdkVersion < 0) {
-            throw new IllegalArgumentException(
-                    "minSdkVersion must not be negative: " + minSdkVersion);
-        }
-        if (minSdkVersion > maxSdkVersion) {
-            throw new IllegalArgumentException(
-                    "minSdkVersion (" + minSdkVersion + ") > maxSdkVersion (" + maxSdkVersion
-                            + ")");
-        }
-        ApkUtils.ZipSections zipSections = ApkUtils.findZipSections(apk);
-
-        Result result = new Result();
-
-        // Android N and newer attempts to verify APK Signature Scheme v2 signature of the APK.
-        // If the signature is not found, it falls back to JAR signature verification. If the
-        // signature is found but does not verify, the APK is rejected.
-        Set<Integer> foundApkSigSchemeIds;
-        if (maxSdkVersion >= AndroidSdkVersion.N) {
-            foundApkSigSchemeIds = new HashSet<>(1);
-            try {
-                V2SchemeVerifier.Result v2Result = V2SchemeVerifier.verify(apk, zipSections);
-                foundApkSigSchemeIds.add(APK_SIGNATURE_SCHEME_V2_ID);
-                result.mergeFrom(v2Result);
-            } catch (V2SchemeVerifier.SignatureNotFoundException ignored) {}
-            if (result.containsErrors()) {
-                return result;
-            }
-        } else {
-            foundApkSigSchemeIds = Collections.emptySet();
-        }
-
-        // Attempt to verify the APK using JAR signing if necessary. Platforms prior to Android N
-        // ignore APK Signature Scheme v2 signatures and always attempt to verify JAR signatures.
-        // Android N onwards verifies JAR signatures only if no APK Signature Scheme v2 (or newer
-        // scheme) signatures were found.
-        if ((minSdkVersion < AndroidSdkVersion.N) || (foundApkSigSchemeIds.isEmpty())) {
-            V1SchemeVerifier.Result v1Result =
-                    V1SchemeVerifier.verify(
-                            apk,
-                            zipSections,
-                            SUPPORTED_APK_SIG_SCHEME_NAMES,
-                            foundApkSigSchemeIds,
-                            minSdkVersion,
-                            maxSdkVersion);
-            result.mergeFrom(v1Result);
-        }
-        if (result.containsErrors()) {
-            return result;
-        }
-
-        // Check whether v1 and v2 scheme signer identifies match, provided both v1 and v2
-        // signatures verified.
-        if ((result.isVerifiedUsingV1Scheme()) && (result.isVerifiedUsingV2Scheme())) {
-            ArrayList<Result.V1SchemeSignerInfo> v1Signers =
-                    new ArrayList<>(result.getV1SchemeSigners());
-            ArrayList<Result.V2SchemeSignerInfo> v2Signers =
-                    new ArrayList<>(result.getV2SchemeSigners());
-            ArrayList<ByteArray> v1SignerCerts = new ArrayList<>();
-            ArrayList<ByteArray> v2SignerCerts = new ArrayList<>();
-            for (Result.V1SchemeSignerInfo signer : v1Signers) {
-                try {
-                    v1SignerCerts.add(new ByteArray(signer.getCertificate().getEncoded()));
-                } catch (CertificateEncodingException e) {
-                    throw new RuntimeException(
-                            "Failed to encode JAR signer " + signer.getName() + " certs", e);
-                }
-            }
-            for (Result.V2SchemeSignerInfo signer : v2Signers) {
-                try {
-                    v2SignerCerts.add(new ByteArray(signer.getCertificate().getEncoded()));
-                } catch (CertificateEncodingException e) {
-                    throw new RuntimeException(
-                            "Failed to encode APK Signature Scheme v2 signer (index: "
-                                    + signer.getIndex() + ") certs",
-                            e);
-                }
-            }
-
-            for (int i = 0; i < v1SignerCerts.size(); i++) {
-                ByteArray v1Cert = v1SignerCerts.get(i);
-                if (!v2SignerCerts.contains(v1Cert)) {
-                    Result.V1SchemeSignerInfo v1Signer = v1Signers.get(i);
-                    v1Signer.addError(Issue.V2_SIG_MISSING);
-                    break;
-                }
-            }
-            for (int i = 0; i < v2SignerCerts.size(); i++) {
-                ByteArray v2Cert = v2SignerCerts.get(i);
-                if (!v1SignerCerts.contains(v2Cert)) {
-                    Result.V2SchemeSignerInfo v2Signer = v2Signers.get(i);
-                    v2Signer.addError(Issue.JAR_SIG_MISSING);
-                    break;
-                }
-            }
-        }
-        if (result.containsErrors()) {
-            return result;
-        }
-
-        // Verified
-        result.setVerified();
-        if (result.isVerifiedUsingV2Scheme()) {
-            for (Result.V2SchemeSignerInfo signerInfo : result.getV2SchemeSigners()) {
-                result.addSignerCertificate(signerInfo.getCertificate());
-            }
-        } else if (result.isVerifiedUsingV1Scheme()) {
-            for (Result.V1SchemeSignerInfo signerInfo : result.getV1SchemeSigners()) {
-                result.addSignerCertificate(signerInfo.getCertificate());
-            }
-        } else {
-            throw new RuntimeException(
-                    "APK considered verified, but has not verified using either v1 or v2 schemes");
-        }
-
-        return result;
-    }
-
-    /**
-     * Result of verifying an APKs signatures. The APK can be considered verified iff
-     * {@link #isVerified()} returns {@code true}.
-     */
-    public static class Result {
-        private final List<IssueWithParams> mErrors = new ArrayList<>();
-        private final List<IssueWithParams> mWarnings = new ArrayList<>();
-        private final List<X509Certificate> mSignerCerts = new ArrayList<>();
-        private final List<V1SchemeSignerInfo> mV1SchemeSigners = new ArrayList<>();
-        private final List<V1SchemeSignerInfo> mV1SchemeIgnoredSigners = new ArrayList<>();
-        private final List<V2SchemeSignerInfo> mV2SchemeSigners = new ArrayList<>();
-
-        private boolean mVerified;
-        private boolean mVerifiedUsingV1Scheme;
-        private boolean mVerifiedUsingV2Scheme;
-
-        /**
-         * Returns {@code true} if the APK's signatures verified.
-         */
-        public boolean isVerified() {
-            return mVerified;
-        }
-
-        private void setVerified() {
-            mVerified = true;
-        }
-
-        /**
-         * Returns {@code true} if the APK's JAR signatures verified.
-         */
-        public boolean isVerifiedUsingV1Scheme() {
-            return mVerifiedUsingV1Scheme;
-        }
-
-        /**
-         * Returns {@code true} if the APK's APK Signature Scheme v2 signatures verified.
-         */
-        public boolean isVerifiedUsingV2Scheme() {
-            return mVerifiedUsingV2Scheme;
-        }
-
-        /**
-         * Returns the verified signers' certificates, one per signer.
-         */
-        public List<X509Certificate> getSignerCertificates() {
-            return mSignerCerts;
-        }
-
-        private void addSignerCertificate(X509Certificate cert) {
-            mSignerCerts.add(cert);
-        }
-
-        /**
-         * Returns information about JAR signers associated with the APK's signature. These are the
-         * signers used by Android.
-         *
-         * @see #getV1SchemeIgnoredSigners()
-         */
-        public List<V1SchemeSignerInfo> getV1SchemeSigners() {
-            return mV1SchemeSigners;
-        }
-
-        /**
-         * Returns information about JAR signers ignored by the APK's signature verification
-         * process. These signers are ignored by Android. However, each signer's errors or warnings
-         * will contain information about why they are ignored.
-         *
-         * @see #getV1SchemeSigners()
-         */
-        public List<V1SchemeSignerInfo> getV1SchemeIgnoredSigners() {
-            return mV1SchemeIgnoredSigners;
-        }
-
-        /**
-         * Returns information about APK Signature Scheme v2 signers associated with the APK's
-         * signature.
-         */
-        public List<V2SchemeSignerInfo> getV2SchemeSigners() {
-            return mV2SchemeSigners;
-        }
-
-        /**
-         * Returns errors encountered while verifying the APK's signatures.
-         */
-        public List<IssueWithParams> getErrors() {
-            return mErrors;
-        }
-
-        /**
-         * Returns warnings encountered while verifying the APK's signatures.
-         */
-        public List<IssueWithParams> getWarnings() {
-            return mWarnings;
-        }
-
-        private void mergeFrom(V1SchemeVerifier.Result source) {
-            mVerifiedUsingV1Scheme = source.verified;
-            mErrors.addAll(source.getErrors());
-            mWarnings.addAll(source.getWarnings());
-            for (V1SchemeVerifier.Result.SignerInfo signer : source.signers) {
-                mV1SchemeSigners.add(new V1SchemeSignerInfo(signer));
-            }
-            for (V1SchemeVerifier.Result.SignerInfo signer : source.ignoredSigners) {
-                mV1SchemeIgnoredSigners.add(new V1SchemeSignerInfo(signer));
-            }
-        }
-
-        private void mergeFrom(V2SchemeVerifier.Result source) {
-            mVerifiedUsingV2Scheme = source.verified;
-            mErrors.addAll(source.getErrors());
-            mWarnings.addAll(source.getWarnings());
-            for (V2SchemeVerifier.Result.SignerInfo signer : source.signers) {
-                mV2SchemeSigners.add(new V2SchemeSignerInfo(signer));
-            }
-        }
-
-        /**
-         * Returns {@code true} if an error was encountered while verifying the APK. Any error
-         * prevents the APK from being considered verified.
-         */
-        public boolean containsErrors() {
-            if (!mErrors.isEmpty()) {
-                return true;
-            }
-            if (!mV1SchemeSigners.isEmpty()) {
-                for (V1SchemeSignerInfo signer : mV1SchemeSigners) {
-                    if (signer.containsErrors()) {
-                        return true;
-                    }
-                }
-            }
-            if (!mV2SchemeSigners.isEmpty()) {
-                for (V2SchemeSignerInfo signer : mV2SchemeSigners) {
-                    if (signer.containsErrors()) {
-                        return true;
-                    }
-                }
-            }
-
-            return false;
-        }
-
-        /**
-         * Information about a JAR signer associated with the APK's signature.
-         */
-        public static class V1SchemeSignerInfo {
-            private final String mName;
-            private final List<X509Certificate> mCertChain;
-            private final String mSignatureBlockFileName;
-            private final String mSignatureFileName;
-
-            private final List<IssueWithParams> mErrors;
-            private final List<IssueWithParams> mWarnings;
-
-            private V1SchemeSignerInfo(V1SchemeVerifier.Result.SignerInfo result) {
-                mName = result.name;
-                mCertChain = result.certChain;
-                mSignatureBlockFileName = result.signatureBlockFileName;
-                mSignatureFileName = result.signatureFileName;
-                mErrors = result.getErrors();
-                mWarnings = result.getWarnings();
-            }
-
-            /**
-             * Returns a user-friendly name of the signer.
-             */
-            public String getName() {
-                return mName;
-            }
-
-            /**
-             * Returns the name of the JAR entry containing this signer's JAR signature block file.
-             */
-            public String getSignatureBlockFileName() {
-                return mSignatureBlockFileName;
-            }
-
-            /**
-             * Returns the name of the JAR entry containing this signer's JAR signature file.
-             */
-            public String getSignatureFileName() {
-                return mSignatureFileName;
-            }
-
-            /**
-             * Returns this signer's signing certificate or {@code null} if not available. The
-             * certificate is guaranteed to be available if no errors were encountered during
-             * verification (see {@link #containsErrors()}.
-             *
-             * <p>This certificate contains the signer's public key.
-             */
-            public X509Certificate getCertificate() {
-                return mCertChain.isEmpty() ? null : mCertChain.get(0);
-            }
-
-            /**
-             * Returns the certificate chain for the signer's public key. The certificate containing
-             * the public key is first, followed by the certificate (if any) which issued the
-             * signing certificate, and so forth. An empty list may be returned if an error was
-             * encountered during verification (see {@link #containsErrors()}).
-             */
-            public List<X509Certificate> getCertificateChain() {
-                return mCertChain;
-            }
-
-            /**
-             * Returns {@code true} if an error was encountered while verifying this signer's JAR
-             * signature. Any error prevents the signer's signature from being considered verified.
-             */
-            public boolean containsErrors() {
-                return !mErrors.isEmpty();
-            }
-
-            /**
-             * Returns errors encountered while verifying this signer's JAR signature. Any error
-             * prevents the signer's signature from being considered verified.
-             */
-            public List<IssueWithParams> getErrors() {
-                return mErrors;
-            }
-
-            /**
-             * Returns warnings encountered while verifying this signer's JAR signature. Warnings
-             * do not prevent the signer's signature from being considered verified.
-             */
-            public List<IssueWithParams> getWarnings() {
-                return mWarnings;
-            }
-
-            private void addError(Issue msg, Object... parameters) {
-                mErrors.add(new IssueWithParams(msg, parameters));
-            }
-        }
-
-        /**
-         * Information about an APK Signature Scheme v2 signer associated with the APK's signature.
-         */
-        public static class V2SchemeSignerInfo {
-            private final int mIndex;
-            private final List<X509Certificate> mCerts;
-
-            private final List<IssueWithParams> mErrors;
-            private final List<IssueWithParams> mWarnings;
-
-            private V2SchemeSignerInfo(V2SchemeVerifier.Result.SignerInfo result) {
-                mIndex = result.index;
-                mCerts = result.certs;
-                mErrors = result.getErrors();
-                mWarnings = result.getWarnings();
-            }
-
-            /**
-             * Returns this signer's {@code 0}-based index in the list of signers contained in the
-             * APK's APK Signature Scheme v2 signature.
-             */
-            public int getIndex() {
-                return mIndex;
-            }
-
-            /**
-             * Returns this signer's signing certificate or {@code null} if not available. The
-             * certificate is guaranteed to be available if no errors were encountered during
-             * verification (see {@link #containsErrors()}.
-             *
-             * <p>This certificate contains the signer's public key.
-             */
-            public X509Certificate getCertificate() {
-                return mCerts.isEmpty() ? null : mCerts.get(0);
-            }
-
-            /**
-             * Returns this signer's certificates. The first certificate is for the signer's public
-             * key. An empty list may be returned if an error was encountered during verification
-             * (see {@link #containsErrors()}).
-             */
-            public List<X509Certificate> getCertificates() {
-                return mCerts;
-            }
-
-            private void addError(Issue msg, Object... parameters) {
-                mErrors.add(new IssueWithParams(msg, parameters));
-            }
-
-            public boolean containsErrors() {
-                return !mErrors.isEmpty();
-            }
-
-            public List<IssueWithParams> getErrors() {
-                return mErrors;
-            }
-
-            public List<IssueWithParams> getWarnings() {
-                return mWarnings;
-            }
-        }
-    }
-
-    /**
-     * Error or warning encountered while verifying an APK's signatures.
-     */
-    public static enum Issue {
-
-        /**
-         * APK is not JAR-signed.
-         */
-        JAR_SIG_NO_SIGNATURES("No JAR signatures"),
-
-        /**
-         * APK does not contain any entries covered by JAR signatures.
-         */
-        JAR_SIG_NO_SIGNED_ZIP_ENTRIES("No JAR entries covered by JAR signatures"),
-
-        /**
-         * APK contains multiple entries with the same name.
-         *
-         * <ul>
-         * <li>Parameter 1: name ({@code String})</li>
-         * </ul>
-         */
-        JAR_SIG_DUPLICATE_ZIP_ENTRY("Duplicate entry: %1$s"),
-
-        /**
-         * JAR manifest contains a section with a duplicate name.
-         *
-         * <ul>
-         * <li>Parameter 1: section name ({@code String})</li>
-         * </ul>
-         */
-        JAR_SIG_DUPLICATE_MANIFEST_SECTION("Duplicate section in META-INF/MANIFEST.MF: %1$s"),
-
-        /**
-         * JAR manifest contains a section without a name.
-         *
-         * <ul>
-         * <li>Parameter 1: section index (1-based) ({@code Integer})</li>
-         * </ul>
-         */
-        JAR_SIG_UNNNAMED_MANIFEST_SECTION(
-                "Malformed META-INF/MANIFEST.MF: invidual section #%1$d does not have a name"),
-
-        /**
-         * JAR signature file contains a section without a name.
-         *
-         * <ul>
-         * <li>Parameter 1: signature file name ({@code String})</li>
-         * <li>Parameter 2: section index (1-based) ({@code Integer})</li>
-         * </ul>
-         */
-        JAR_SIG_UNNNAMED_SIG_FILE_SECTION(
-                "Malformed %1$s: invidual section #%2$d does not have a name"),
-
-        /** APK is missing the JAR manifest entry (META-INF/MANIFEST.MF). */
-        JAR_SIG_NO_MANIFEST("Missing META-INF/MANIFEST.MF"),
-
-        /**
-         * JAR manifest references an entry which is not there in the APK.
-         *
-         * <ul>
-         * <li>Parameter 1: entry name ({@code String})</li>
-         * </ul>
-         */
-        JAR_SIG_MISSING_ZIP_ENTRY_REFERENCED_IN_MANIFEST(
-                "%1$s entry referenced by META-INF/MANIFEST.MF not found in the APK"),
-
-        /**
-         * JAR manifest does not list a digest for the specified entry.
-         *
-         * <ul>
-         * <li>Parameter 1: entry name ({@code String})</li>
-         * </ul>
-         */
-        JAR_SIG_NO_ZIP_ENTRY_DIGEST_IN_MANIFEST("No digest for %1$s in META-INF/MANIFEST.MF"),
-
-        /**
-         * JAR signature does not list a digest for the specified entry.
-         *
-         * <ul>
-         * <li>Parameter 1: entry name ({@code String})</li>
-         * <li>Parameter 2: signature file name ({@code String})</li>
-         * </ul>
-         */
-        JAR_SIG_NO_ZIP_ENTRY_DIGEST_IN_SIG_FILE("No digest for %1$s in %2$s"),
-
-        /**
-         * The specified JAR entry is not covered by JAR signature.
-         *
-         * <ul>
-         * <li>Parameter 1: entry name ({@code String})</li>
-         * </ul>
-         */
-        JAR_SIG_ZIP_ENTRY_NOT_SIGNED("%1$s entry not signed"),
-
-        /**
-         * JAR signature uses different set of signers to protect the two specified ZIP entries.
-         *
-         * <ul>
-         * <li>Parameter 1: first entry name ({@code String})</li>
-         * <li>Parameter 2: first entry signer names ({@code List<String>})</li>
-         * <li>Parameter 3: second entry name ({@code String})</li>
-         * <li>Parameter 4: second entry signer names ({@code List<String>})</li>
-         * </ul>
-         */
-        JAR_SIG_ZIP_ENTRY_SIGNERS_MISMATCH(
-                "Entries %1$s and %3$s are signed with different sets of signers"
-                        + " : <%2$s> vs <%4$s>"),
-
-        /**
-         * Digest of the specified ZIP entry's data does not match the digest expected by the JAR
-         * signature.
-         *
-         * <ul>
-         * <li>Parameter 1: entry name ({@code String})</li>
-         * <li>Parameter 2: digest algorithm (e.g., SHA-256) ({@code String})</li>
-         * <li>Parameter 3: name of the entry in which the expected digest is specified
-         *     ({@code String})</li>
-         * <li>Parameter 4: base64-encoded actual digest ({@code String})</li>
-         * <li>Parameter 5: base64-encoded expected digest ({@code String})</li>
-         * </ul>
-         */
-        JAR_SIG_ZIP_ENTRY_DIGEST_DID_NOT_VERIFY(
-                "%2$s digest of %1$s does not match the digest specified in %3$s"
-                        + ". Expected: <%5$s>, actual: <%4$s>"),
-
-        /**
-         * Digest of the JAR manifest main section did not verify.
-         *
-         * <ul>
-         * <li>Parameter 1: digest algorithm (e.g., SHA-256) ({@code String})</li>
-         * <li>Parameter 2: name of the entry in which the expected digest is specified
-         *     ({@code String})</li>
-         * <li>Parameter 3: base64-encoded actual digest ({@code String})</li>
-         * <li>Parameter 4: base64-encoded expected digest ({@code String})</li>
-         * </ul>
-         */
-        JAR_SIG_MANIFEST_MAIN_SECTION_DIGEST_DID_NOT_VERIFY(
-                "%1$s digest of META-INF/MANIFEST.MF main section does not match the digest"
-                        + " specified in %2$s. Expected: <%4$s>, actual: <%3$s>"),
-
-        /**
-         * Digest of the specified JAR manifest section does not match the digest expected by the
-         * JAR signature.
-         *
-         * <ul>
-         * <li>Parameter 1: section name ({@code String})</li>
-         * <li>Parameter 2: digest algorithm (e.g., SHA-256) ({@code String})</li>
-         * <li>Parameter 3: name of the signature file in which the expected digest is specified
-         *     ({@code String})</li>
-         * <li>Parameter 4: base64-encoded actual digest ({@code String})</li>
-         * <li>Parameter 5: base64-encoded expected digest ({@code String})</li>
-         * </ul>
-         */
-        JAR_SIG_MANIFEST_SECTION_DIGEST_DID_NOT_VERIFY(
-                "%2$s digest of META-INF/MANIFEST.MF section for %1$s does not match the digest"
-                        + " specified in %3$s. Expected: <%5$s>, actual: <%4$s>"),
-
-        /**
-         * JAR signature file does not contain the whole-file digest of the JAR manifest file. The
-         * digest speeds up verification of JAR signature.
-         *
-         * <ul>
-         * <li>Parameter 1: name of the signature file ({@code String})</li>
-         * </ul>
-         */
-        JAR_SIG_NO_MANIFEST_DIGEST_IN_SIG_FILE(
-                "%1$s does not specify digest of META-INF/MANIFEST.MF"
-                        + ". This slows down verification."),
-
-        /**
-         * APK is signed using APK Signature Scheme v2 or newer, but JAR signature file does not
-         * contain protections against stripping of these newer scheme signatures.
-         *
-         * <ul>
-         * <li>Parameter 1: name of the signature file ({@code String})</li>
-         * </ul>
-         */
-        JAR_SIG_NO_APK_SIG_STRIP_PROTECTION(
-                "APK is signed using APK Signature Scheme v2 but these signatures may be stripped"
-                        + " without being detected because %1$s does not contain anti-stripping"
-                        + " protections."),
-
-        /**
-         * JAR signature of the signer is missing a file/entry.
-         *
-         * <ul>
-         * <li>Parameter 1: name of the encountered file ({@code String})</li>
-         * <li>Parameter 2: name of the missing file ({@code String})</li>
-         * </ul>
-         */
-        JAR_SIG_MISSING_FILE("Partial JAR signature. Found: %1$s, missing: %2$s"),
-
-        /**
-         * An exception was encountered while verifying JAR signature contained in a signature block
-         * against the signature file.
-         *
-         * <ul>
-         * <li>Parameter 1: name of the signature block file ({@code String})</li>
-         * <li>Parameter 2: name of the signature file ({@code String})</li>
-         * <li>Parameter 3: exception ({@code Throwable})</li>
-         * </ul>
-         */
-        JAR_SIG_VERIFY_EXCEPTION("Failed to verify JAR signature %1$s against %2$s: %3$s"),
-
-        /**
-         * JAR signature contains unsupported digest algorithm.
-         *
-         * <ul>
-         * <li>Parameter 1: name of the signature block file ({@code String})</li>
-         * <li>Parameter 2: digest algorithm OID ({@code String})</li>
-         * <li>Parameter 2: signature algorithm OID ({@code String})</li>
-         * <li>Parameter 3: API Levels on which this combination of algorithms is not supported
-         *     ({@code String})</li>
-         * </ul>
-         */
-        JAR_SIG_UNSUPPORTED_SIG_ALG(
-                "JAR signature %1$s uses digest algorithm %2$s and signature algorithm %3$s which"
-                        + " is not supported on API Levels %4$s"),
-
-        /**
-         * An exception was encountered while parsing JAR signature contained in a signature block.
-         *
-         * <ul>
-         * <li>Parameter 1: name of the signature block file ({@code String})</li>
-         * <li>Parameter 2: exception ({@code Throwable})</li>
-         * </ul>
-         */
-        JAR_SIG_PARSE_EXCEPTION("Failed to parse JAR signature %1$s: %2$s"),
-
-        /**
-         * An exception was encountered while parsing a certificate contained in the JAR signature
-         * block.
-         *
-         * <ul>
-         * <li>Parameter 1: name of the signature block file ({@code String})</li>
-         * <li>Parameter 2: exception ({@code Throwable})</li>
-         * </ul>
-         */
-        JAR_SIG_MALFORMED_CERTIFICATE("Malformed certificate in JAR signature %1$s: %2$s"),
-
-        /**
-         * JAR signature contained in a signature block file did not verify against the signature
-         * file.
-         *
-         * <ul>
-         * <li>Parameter 1: name of the signature block file ({@code String})</li>
-         * <li>Parameter 2: name of the signature file ({@code String})</li>
-         * </ul>
-         */
-        JAR_SIG_DID_NOT_VERIFY("JAR signature %1$s did not verify against %2$s"),
-
-        /**
-         * JAR signature contains no verified signers.
-         *
-         * <ul>
-         * <li>Parameter 1: name of the signature block file ({@code String})</li>
-         * </ul>
-         */
-        JAR_SIG_NO_SIGNERS("JAR signature %1$s contains no signers"),
-
-        /**
-         * JAR signature file contains a section with a duplicate name.
-         *
-         * <ul>
-         * <li>Parameter 1: signature file name ({@code String})</li>
-         * <li>Parameter 1: section name ({@code String})</li>
-         * </ul>
-         */
-        JAR_SIG_DUPLICATE_SIG_FILE_SECTION("Duplicate section in %1$s: %2$s"),
-
-        /**
-         * JAR signature file's main section doesn't contain the mandatory Signature-Version
-         * attribute.
-         *
-         * <ul>
-         * <li>Parameter 1: signature file name ({@code String})</li>
-         * </ul>
-         */
-        JAR_SIG_MISSING_VERSION_ATTR_IN_SIG_FILE(
-                "Malformed %1$s: missing Signature-Version attribute"),
-
-        /**
-         * JAR signature file references an unknown APK signature scheme ID.
-         *
-         * <ul>
-         * <li>Parameter 1: name of the signature file ({@code String})</li>
-         * <li>Parameter 2: unknown APK signature scheme ID ({@code} Integer)</li>
-         * </ul>
-         */
-        JAR_SIG_UNKNOWN_APK_SIG_SCHEME_ID(
-                "JAR signature %1$s references unknown APK signature scheme ID: %2$d"),
-
-        /**
-         * JAR signature file indicates that the APK is supposed to be signed with a supported APK
-         * signature scheme (in addition to the JAR signature) but no such signature was found in
-         * the APK.
-         *
-         * <ul>
-         * <li>Parameter 1: name of the signature file ({@code String})</li>
-         * <li>Parameter 2: APK signature scheme ID ({@code} Integer)</li>
-         * <li>Parameter 3: APK signature scheme English name ({@code} String)</li>
-         * </ul>
-         */
-        JAR_SIG_MISSING_APK_SIG_REFERENCED(
-                "JAR signature %1$s indicates the APK is signed using %3$s but no such signature"
-                        + " was found. Signature stripped?"),
-
-        /**
-         * JAR entry is not covered by signature and thus unauthorized modifications to its contents
-         * will not be detected.
-         *
-         * <ul>
-         * <li>Parameter 1: entry name ({@code String})</li>
-         * </ul>
-         */
-        JAR_SIG_UNPROTECTED_ZIP_ENTRY(
-                "%1$s not protected by signature. Unauthorized modifications to this JAR entry"
-                        + " will not be detected. Delete or move the entry outside of META-INF/."),
-
-        /**
-         * APK which is both JAR-signed and signed using APK Signature Scheme v2 contains an APK
-         * Signature Scheme v2 signature from this signer, but does not contain a JAR signature
-         * from this signer.
-         */
-        JAR_SIG_MISSING(
-                "No APK Signature Scheme v2 signature from this signer despite APK being v2"
-                        + " signed"),
-
-        /**
-         * APK which is both JAR-signed and signed using APK Signature Scheme v2 contains a JAR
-         * signature from this signer, but does not contain an APK Signature Scheme v2 signature
-         * from this signer.
-         */
-        V2_SIG_MISSING(
-                "No APK Signature Scheme v2 signature from this signer despite APK being v2"
-                        + " signed"),
-
-        /**
-         * Failed to parse the list of signers contained in the APK Signature Scheme v2 signature.
-         */
-        V2_SIG_MALFORMED_SIGNERS("Malformed list of signers"),
-
-        /**
-         * Failed to parse this signer's signer block contained in the APK Signature Scheme v2
-         * signature.
-         */
-        V2_SIG_MALFORMED_SIGNER("Malformed signer block"),
-
-        /**
-         * Public key embedded in the APK Signature Scheme v2 signature of this signer could not be
-         * parsed.
-         *
-         * <ul>
-         * <li>Parameter 1: error details ({@code Throwable})</li>
-         * </ul>
-         */
-        V2_SIG_MALFORMED_PUBLIC_KEY("Malformed public key: %1$s"),
-
-        /**
-         * This APK Signature Scheme v2 signer's certificate could not be parsed.
-         *
-         * <ul>
-         * <li>Parameter 1: index ({@code 0}-based) of the certificate in the signer's list of
-         *     certificates ({@code Integer})</li>
-         * <li>Parameter 2: sequence number ({@code 1}-based) of the certificate in the signer's
-         *     list of certificates ({@code Integer})</li>
-         * <li>Parameter 3: error details ({@code Throwable})</li>
-         * </ul>
-         */
-        V2_SIG_MALFORMED_CERTIFICATE("Malformed certificate #%2$d: %3$s"),
-
-        /**
-         * Failed to parse this signer's signature record contained in the APK Signature Scheme v2
-         * signature.
-         *
-         * <ul>
-         * <li>Parameter 1: record number (first record is {@code 1}) ({@code Integer})</li>
-         * </ul>
-         */
-        V2_SIG_MALFORMED_SIGNATURE("Malformed APK Signature Scheme v2 signature record #%1$d"),
-
-        /**
-         * Failed to parse this signer's digest record contained in the APK Signature Scheme v2
-         * signature.
-         *
-         * <ul>
-         * <li>Parameter 1: record number (first record is {@code 1}) ({@code Integer})</li>
-         * </ul>
-         */
-        V2_SIG_MALFORMED_DIGEST("Malformed APK Signature Scheme v2 digest record #%1$d"),
-
-        /**
-         * This APK Signature Scheme v2 signer contains a malformed additional attribute.
-         *
-         * <ul>
-         * <li>Parameter 1: attribute number (first attribute is {@code 1}) {@code Integer})</li>
-         * </ul>
-         */
-        V2_SIG_MALFORMED_ADDITIONAL_ATTRIBUTE("Malformed additional attribute #%1$d"),
-
-        /**
-         * APK Signature Scheme v2 signature contains no signers.
-         */
-        V2_SIG_NO_SIGNERS("No signers in APK Signature Scheme v2 signature"),
-
-        /**
-         * This APK Signature Scheme v2 signer contains a signature produced using an unknown
-         * algorithm.
-         *
-         * <ul>
-         * <li>Parameter 1: algorithm ID ({@code Integer})</li>
-         * </ul>
-         */
-        V2_SIG_UNKNOWN_SIG_ALGORITHM("Unknown signature algorithm: %1$#x"),
-
-        /**
-         * This APK Signature Scheme v2 signer contains an unknown additional attribute.
-         *
-         * <ul>
-         * <li>Parameter 1: attribute ID ({@code Integer})</li>
-         * </ul>
-         */
-        V2_SIG_UNKNOWN_ADDITIONAL_ATTRIBUTE("Unknown additional attribute: ID %1$#x"),
-
-        /**
-         * An exception was encountered while verifying APK Signature Scheme v2 signature of this
-         * signer.
-         *
-         * <ul>
-         * <li>Parameter 1: signature algorithm ({@link SignatureAlgorithm})</li>
-         * <li>Parameter 2: exception ({@code Throwable})</li>
-         * </ul>
-         */
-        V2_SIG_VERIFY_EXCEPTION("Failed to verify %1$s signature: %2$s"),
-
-        /**
-         * APK Signature Scheme v2 signature over this signer's signed-data block did not verify.
-         *
-         * <ul>
-         * <li>Parameter 1: signature algorithm ({@link SignatureAlgorithm})</li>
-         * </ul>
-         */
-        V2_SIG_DID_NOT_VERIFY("%1$s signature over signed-data did not verify"),
-
-        /**
-         * This APK Signature Scheme v2 signer offers no signatures.
-         */
-        V2_SIG_NO_SIGNATURES("No signatures"),
-
-        /**
-         * This APK Signature Scheme v2 signer offers signatures but none of them are supported.
-         */
-        V2_SIG_NO_SUPPORTED_SIGNATURES("No supported signatures"),
-
-        /**
-         * This APK Signature Scheme v2 signer offers no certificates.
-         */
-        V2_SIG_NO_CERTIFICATES("No certificates"),
-
-        /**
-         * This APK Signature Scheme v2 signer's public key listed in the signer's certificate does
-         * not match the public key listed in the signatures record.
-         *
-         * <ul>
-         * <li>Parameter 1: hex-encoded public key from certificate ({@code String})</li>
-         * <li>Parameter 2: hex-encoded public key from signatures record ({@code String})</li>
-         * </ul>
-         */
-        V2_SIG_PUBLIC_KEY_MISMATCH_BETWEEN_CERTIFICATE_AND_SIGNATURES_RECORD(
-                "Public key mismatch between certificate and signature record: <%1$s> vs <%2$s>"),
-
-        /**
-         * This APK Signature Scheme v2 signer's signature algorithms listed in the signatures
-         * record do not match the signature algorithms listed in the signatures record.
-         *
-         * <ul>
-         * <li>Parameter 1: signature algorithms from signatures record ({@code List<Integer>})</li>
-         * <li>Parameter 2: signature algorithms from digests record ({@code List<Integer>})</li>
-         * </ul>
-         */
-        V2_SIG_SIG_ALG_MISMATCH_BETWEEN_SIGNATURES_AND_DIGESTS_RECORDS(
-                "Signature algorithms mismatch between signatures and digests records"
-                        + ": %1$s vs %2$s"),
-
-        /**
-         * The APK's digest does not match the digest contained in the APK Signature Scheme v2
-         * signature.
-         *
-         * <ul>
-         * <li>Parameter 1: content digest algorithm ({@link ContentDigestAlgorithm})</li>
-         * <li>Parameter 2: hex-encoded expected digest of the APK ({@code String})</li>
-         * <li>Parameter 3: hex-encoded actual digest of the APK ({@code String})</li>
-         * </ul>
-         */
-        V2_SIG_APK_DIGEST_DID_NOT_VERIFY(
-                "APK integrity check failed. %1$s digest mismatch."
-                        + " Expected: <%2$s>, actual: <%3$s>"),
-
-        /**
-         * APK Signing Block contains an unknown entry.
-         *
-         * <ul>
-         * <li>Parameter 1: entry ID ({@code Integer})</li>
-         * </ul>
-         */
-        APK_SIG_BLOCK_UNKNOWN_ENTRY_ID("APK Signing Block contains unknown entry: ID %1$#x");
-
-        private final String mFormat;
-
-        private Issue(String format) {
-            mFormat = format;
-        }
-
-        /**
-         * Returns the format string suitable for combining the parameters of this issue into a
-         * readable string. See {@link java.util.Formatter} for format.
-         */
-        private String getFormat() {
-            return mFormat;
-        }
-    }
-
-    /**
-     * {@link Issue} with associated parameters. {@link #toString()} produces a readable formatted
-     * form.
-     */
-    public static class IssueWithParams {
-        private final Issue mIssue;
-        private final Object[] mParams;
-
-        /**
-         * Constructs a new {@code IssueWithParams} of the specified type and with provided
-         * parameters.
-         */
-        public IssueWithParams(Issue issue, Object[] params) {
-            mIssue = issue;
-            mParams = params;
-        }
-
-        /**
-         * Returns the type of this issue.
-         */
-        public Issue getIssue() {
-            return mIssue;
-        }
-
-        /**
-         * Returns the parameters of this issue.
-         */
-        public Object[] getParams() {
-            return mParams.clone();
-        }
-
-        /**
-         * Returns a readable form of this issue.
-         */
-        @Override
-        public String toString() {
-            return String.format(mIssue.getFormat(), mParams);
-        }
-    }
-
-    /**
-     * Wrapped around {@code byte[]} which ensures that {@code equals} and {@code hashCode} operate
-     * on the contents of the arrays rather than on references.
-     */
-    private static class ByteArray {
-        private final byte[] mArray;
-        private final int mHashCode;
-
-        private ByteArray(byte[] arr) {
-            mArray = arr;
-            mHashCode = Arrays.hashCode(mArray);
-        }
-
-        @Override
-        public int hashCode() {
-            return mHashCode;
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (this == obj) {
-                return true;
-            }
-            if (obj == null) {
-                return false;
-            }
-            if (getClass() != obj.getClass()) {
-                return false;
-            }
-            ByteArray other = (ByteArray) obj;
-            if (hashCode() != other.hashCode()) {
-                return false;
-            }
-            if (!Arrays.equals(mArray, other.mArray)) {
-                return false;
-            }
-            return true;
-        }
-    }
-
-    /**
-     * Builder of {@link ApkVerifier} instances.
-     *
-     * <p>Although not required, it is best to provide the SDK version (API Level) of the oldest
-     * Android platform on which the APK is supposed to be installed -- see
-     * {@link #setMinCheckedPlatformVersion(int)}. Without this information, APKs which use security
-     * features not supported on ancient Android platforms (e.g., SHA-256 digests or ECDSA
-     * signatures) will not verify.
-     */
-    public static class Builder {
-        private final File mApkFile;
-        private final DataSource mApkDataSource;
-
-        private int mMinSdkVersion = 1;
-        private int mMaxSdkVersion = Integer.MAX_VALUE;
-
-        /**
-         * Constructs a new {@code Builder} for verifying the provided APK file.
-         */
-        public Builder(File apk) {
-            if (apk == null) {
-                throw new NullPointerException("apk == null");
-            }
-            mApkFile = apk;
-            mApkDataSource = null;
-        }
-
-        /**
-         * Constructs a new {@code Builder} for verifying the provided APK.
-         */
-        public Builder(DataSource apk) {
-            if (apk == null) {
-                throw new NullPointerException("apk == null");
-            }
-            mApkDataSource = apk;
-            mApkFile = null;
-        }
-
-        /**
-         * Sets the oldest Android platform version for which the APK is verified. APK verification
-         * will confirm that the APK is expected to install successfully on all known Android
-         * platforms starting from the platform version with the provided API Level.
-         *
-         * <p>By default, the APK is checked for all platform versions. Thus, APKs which use
-         * security features not supported on ancient Android platforms (e.g., SHA-256 digests or
-         * ECDSA signatures) will not verify by default.
-         *
-         * @param minSdkVersion API Level of the oldest platform for which to verify the APK
-         *
-         * @see #setCheckedPlatformVersions(int, int)
-         */
-        public Builder setMinCheckedPlatformVersion(int minSdkVersion) {
-            mMinSdkVersion = minSdkVersion;
-            mMaxSdkVersion = Integer.MAX_VALUE;
-            return this;
-        }
-
-        /**
-         * Sets the range of Android platform versions for which the APK is verified. APK
-         * verification will confirm that the APK is expected to install successfully on Android
-         * platforms whose API Levels fall into this inclusive range.
-         *
-         * <p>By default, the APK is checked for all platform versions. Thus, APKs which use
-         * security features not supported on ancient Android platforms (e.g., SHA-256 digests or
-         * ECDSA signatures) will not verify by default.
-         *
-         * @param minSdkVersion API Level of the oldest platform for which to verify the APK
-         * @param maxSdkVersion API Level of the newest platform for which to verify the APK
-         *
-         * @see #setMinCheckedPlatformVersion(int)
-         */
-        public Builder setCheckedPlatformVersions(int minSdkVersion, int maxSdkVersion) {
-            mMinSdkVersion = minSdkVersion;
-            mMaxSdkVersion = maxSdkVersion;
-            return this;
-        }
-
-        /**
-         * Returns an {@link ApkVerifier} initialized according to the configuration of this
-         * builder.
-         */
-        public ApkVerifier build() {
-            return new ApkVerifier(
-                    mApkFile,
-                    mApkDataSource,
-                    mMinSdkVersion,
-                    mMaxSdkVersion);
-        }
-    }
-}
diff --git a/tools/apksigner/core/src/com/android/apksigner/core/DefaultApkSignerEngine.java b/tools/apksigner/core/src/com/android/apksigner/core/DefaultApkSignerEngine.java
deleted file mode 100644
index 75b0b20..0000000
--- a/tools/apksigner/core/src/com/android/apksigner/core/DefaultApkSignerEngine.java
+++ /dev/null
@@ -1,900 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.apksigner.core;
-
-import com.android.apksigner.core.internal.apk.v1.DigestAlgorithm;
-import com.android.apksigner.core.internal.apk.v1.V1SchemeSigner;
-import com.android.apksigner.core.internal.apk.v2.V2SchemeSigner;
-import com.android.apksigner.core.internal.util.MessageDigestSink;
-import com.android.apksigner.core.internal.util.Pair;
-import com.android.apksigner.core.util.DataSink;
-import com.android.apksigner.core.util.DataSinks;
-import com.android.apksigner.core.util.DataSource;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.security.InvalidKeyException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.SignatureException;
-import java.security.cert.CertificateException;
-import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Default implementation of {@link ApkSignerEngine}.
- *
- * <p>Use {@link Builder} to obtain instances of this engine.
- */
-public class DefaultApkSignerEngine implements ApkSignerEngine {
-
-    // IMPLEMENTATION NOTE: This engine generates a signed APK as follows:
-    // 1. The engine asks its client to output input JAR entries which are not part of JAR
-    //    signature.
-    // 2. If JAR signing (v1 signing) is enabled, the engine inspects the output JAR entries to
-    //    compute their digests, to be placed into output META-INF/MANIFEST.MF. It also inspects
-    //    the contents of input and output META-INF/MANIFEST.MF to borrow the main section of the
-    //    file. It does not care about individual (i.e., JAR entry-specific) sections. It then
-    //    emits the v1 signature (a set of JAR entries) and asks the client to output them.
-    // 3. If APK Signature Scheme v2 (v2 signing) is enabled, the engine emits an APK Signing Block
-    //    from outputZipSections() and asks its client to insert this block into the output.
-
-    private final boolean mV1SigningEnabled;
-    private final boolean mV2SigningEnabled;
-    private final boolean mOtherSignersSignaturesPreserved;
-    private final List<V1SchemeSigner.SignerConfig> mV1SignerConfigs;
-    private final DigestAlgorithm mV1ContentDigestAlgorithm;
-    private final List<V2SchemeSigner.SignerConfig> mV2SignerConfigs;
-
-    private boolean mClosed;
-
-    private boolean mV1SignaturePending;
-
-    /**
-     * Names of JAR entries which this engine is expected to output as part of v1 signing.
-     */
-    private final Set<String> mSignatureExpectedOutputJarEntryNames;
-
-    /** Requests for digests of output JAR entries. */
-    private final Map<String, GetJarEntryDataDigestRequest> mOutputJarEntryDigestRequests =
-            new HashMap<>();
-
-    /** Digests of output JAR entries. */
-    private final Map<String, byte[]> mOutputJarEntryDigests = new HashMap<>();
-
-    /** Data of JAR entries emitted by this engine as v1 signature. */
-    private final Map<String, byte[]> mEmittedSignatureJarEntryData = new HashMap<>();
-
-    /** Requests for data of output JAR entries which comprise the v1 signature. */
-    private final Map<String, GetJarEntryDataRequest> mOutputSignatureJarEntryDataRequests =
-            new HashMap<>();
-    /**
-     * Request to obtain the data of MANIFEST.MF or {@code null} if the request hasn't been issued.
-     */
-    private GetJarEntryDataRequest mInputJarManifestEntryDataRequest;
-
-    /**
-     * Request to output the emitted v1 signature or {@code null} if the request hasn't been issued.
-     */
-    private OutputJarSignatureRequestImpl mAddV1SignatureRequest;
-
-    private boolean mV2SignaturePending;
-
-    /**
-     * Request to output the emitted v2 signature or {@code null} if the request hasn't been issued.
-     */
-    private OutputApkSigningBlockRequestImpl mAddV2SignatureRequest;
-
-    private DefaultApkSignerEngine(
-            List<SignerConfig> signerConfigs,
-            int minSdkVersion,
-            boolean v1SigningEnabled,
-            boolean v2SigningEnabled,
-            boolean otherSignersSignaturesPreserved) throws InvalidKeyException {
-        if (signerConfigs.isEmpty()) {
-            throw new IllegalArgumentException("At least one signer config must be provided");
-        }
-        if (otherSignersSignaturesPreserved) {
-            throw new UnsupportedOperationException(
-                    "Preserving other signer's signatures is not yet implemented");
-        }
-
-        mV1SigningEnabled = v1SigningEnabled;
-        mV2SigningEnabled = v2SigningEnabled;
-        mOtherSignersSignaturesPreserved = otherSignersSignaturesPreserved;
-        mV1SignerConfigs =
-                (v1SigningEnabled)
-                        ? new ArrayList<>(signerConfigs.size()) : Collections.emptyList();
-        mV2SignerConfigs =
-                (v2SigningEnabled)
-                        ? new ArrayList<>(signerConfigs.size()) : Collections.emptyList();
-        mV1ContentDigestAlgorithm =
-                (v1SigningEnabled)
-                        ? V1SchemeSigner.getSuggestedContentDigestAlgorithm(minSdkVersion) : null;
-        for (SignerConfig signerConfig : signerConfigs) {
-            List<X509Certificate> certificates = signerConfig.getCertificates();
-            PublicKey publicKey = certificates.get(0).getPublicKey();
-
-            if (v1SigningEnabled) {
-                DigestAlgorithm v1SignatureDigestAlgorithm =
-                        V1SchemeSigner.getSuggestedSignatureDigestAlgorithm(
-                                publicKey, minSdkVersion);
-                V1SchemeSigner.SignerConfig v1SignerConfig = new V1SchemeSigner.SignerConfig();
-                v1SignerConfig.name = signerConfig.getName();
-                v1SignerConfig.privateKey = signerConfig.getPrivateKey();
-                v1SignerConfig.certificates = certificates;
-                v1SignerConfig.contentDigestAlgorithm = mV1ContentDigestAlgorithm;
-                v1SignerConfig.signatureDigestAlgorithm = v1SignatureDigestAlgorithm;
-                mV1SignerConfigs.add(v1SignerConfig);
-            }
-
-            if (v2SigningEnabled) {
-                V2SchemeSigner.SignerConfig v2SignerConfig = new V2SchemeSigner.SignerConfig();
-                v2SignerConfig.privateKey = signerConfig.getPrivateKey();
-                v2SignerConfig.certificates = certificates;
-                v2SignerConfig.signatureAlgorithms =
-                        V2SchemeSigner.getSuggestedSignatureAlgorithms(publicKey, minSdkVersion);
-                mV2SignerConfigs.add(v2SignerConfig);
-            }
-        }
-        mSignatureExpectedOutputJarEntryNames =
-                (v1SigningEnabled)
-                        ? V1SchemeSigner.getOutputEntryNames(mV1SignerConfigs)
-                        : Collections.emptySet();
-    }
-
-    @Override
-    public void inputApkSigningBlock(DataSource apkSigningBlock) {
-        checkNotClosed();
-
-        if ((apkSigningBlock == null) || (apkSigningBlock.size() == 0)) {
-            return;
-        }
-
-        if (mOtherSignersSignaturesPreserved) {
-            // TODO: Preserve blocks other than APK Signature Scheme v2 blocks of signers configured
-            // in this engine.
-            return;
-        }
-        // TODO: Preserve blocks other than APK Signature Scheme v2 blocks.
-    }
-
-    @Override
-    public InputJarEntryInstructions inputJarEntry(String entryName) {
-        checkNotClosed();
-
-        InputJarEntryInstructions.OutputPolicy outputPolicy =
-                getInputJarEntryOutputPolicy(entryName);
-        switch (outputPolicy) {
-            case SKIP:
-                return new InputJarEntryInstructions(InputJarEntryInstructions.OutputPolicy.SKIP);
-            case OUTPUT:
-                return new InputJarEntryInstructions(InputJarEntryInstructions.OutputPolicy.OUTPUT);
-            case OUTPUT_BY_ENGINE:
-                if (V1SchemeSigner.MANIFEST_ENTRY_NAME.equals(entryName)) {
-                    // We copy the main section of the JAR manifest from input to output. Thus, this
-                    // invalidates v1 signature and we need to see the entry's data.
-                    mInputJarManifestEntryDataRequest = new GetJarEntryDataRequest(entryName);
-                    return new InputJarEntryInstructions(
-                            InputJarEntryInstructions.OutputPolicy.OUTPUT_BY_ENGINE,
-                            mInputJarManifestEntryDataRequest);
-                }
-                return new InputJarEntryInstructions(
-                        InputJarEntryInstructions.OutputPolicy.OUTPUT_BY_ENGINE);
-            default:
-                throw new RuntimeException("Unsupported output policy: " + outputPolicy);
-        }
-    }
-
-    @Override
-    public InspectJarEntryRequest outputJarEntry(String entryName) {
-        checkNotClosed();
-        invalidateV2Signature();
-        if (!mV1SigningEnabled) {
-            // No need to inspect JAR entries when v1 signing is not enabled.
-            return null;
-        }
-        // v1 signing is enabled
-
-        if (V1SchemeSigner.isJarEntryDigestNeededInManifest(entryName)) {
-            // This entry is covered by v1 signature. We thus need to inspect the entry's data to
-            // compute its digest(s) for v1 signature.
-
-            // TODO: Handle the case where other signer's v1 signatures are present and need to be
-            // preserved. In that scenario we can't modify MANIFEST.MF and add/remove JAR entries
-            // covered by v1 signature.
-            invalidateV1Signature();
-            GetJarEntryDataDigestRequest dataDigestRequest =
-                    new GetJarEntryDataDigestRequest(
-                            entryName,
-                            V1SchemeSigner.getJcaMessageDigestAlgorithm(mV1ContentDigestAlgorithm));
-            mOutputJarEntryDigestRequests.put(entryName, dataDigestRequest);
-            mOutputJarEntryDigests.remove(entryName);
-            return dataDigestRequest;
-        }
-
-        if (mSignatureExpectedOutputJarEntryNames.contains(entryName)) {
-            // This entry is part of v1 signature generated by this engine. We need to check whether
-            // the entry's data is as output by the engine.
-            invalidateV1Signature();
-            GetJarEntryDataRequest dataRequest;
-            if (V1SchemeSigner.MANIFEST_ENTRY_NAME.equals(entryName)) {
-                dataRequest = new GetJarEntryDataRequest(entryName);
-                mInputJarManifestEntryDataRequest = dataRequest;
-            } else {
-                // If this entry is part of v1 signature which has been emitted by this engine,
-                // check whether the output entry's data matches what the engine emitted.
-                dataRequest =
-                        (mEmittedSignatureJarEntryData.containsKey(entryName))
-                                ? new GetJarEntryDataRequest(entryName) : null;
-            }
-
-            if (dataRequest != null) {
-                mOutputSignatureJarEntryDataRequests.put(entryName, dataRequest);
-            }
-            return dataRequest;
-        }
-
-        // This entry is not covered by v1 signature and isn't part of v1 signature.
-        return null;
-    }
-
-    @Override
-    public InputJarEntryInstructions.OutputPolicy inputJarEntryRemoved(String entryName) {
-        checkNotClosed();
-        return getInputJarEntryOutputPolicy(entryName);
-    }
-
-    @Override
-    public void outputJarEntryRemoved(String entryName) {
-        checkNotClosed();
-        invalidateV2Signature();
-        if (!mV1SigningEnabled) {
-            return;
-        }
-
-        if (V1SchemeSigner.isJarEntryDigestNeededInManifest(entryName)) {
-            // This entry is covered by v1 signature.
-            invalidateV1Signature();
-            mOutputJarEntryDigests.remove(entryName);
-            mOutputJarEntryDigestRequests.remove(entryName);
-            mOutputSignatureJarEntryDataRequests.remove(entryName);
-            return;
-        }
-
-        if (mSignatureExpectedOutputJarEntryNames.contains(entryName)) {
-            // This entry is part of the v1 signature generated by this engine.
-            invalidateV1Signature();
-            return;
-        }
-    }
-
-    @Override
-    public OutputJarSignatureRequest outputJarEntries()
-            throws InvalidKeyException, SignatureException, NoSuchAlgorithmException {
-        checkNotClosed();
-
-        if (!mV1SignaturePending) {
-            return null;
-        }
-
-        if ((mInputJarManifestEntryDataRequest != null)
-                && (!mInputJarManifestEntryDataRequest.isDone())) {
-            throw new IllegalStateException(
-                    "Still waiting to inspect input APK's "
-                            + mInputJarManifestEntryDataRequest.getEntryName());
-        }
-
-        for (GetJarEntryDataDigestRequest digestRequest
-                : mOutputJarEntryDigestRequests.values()) {
-            String entryName = digestRequest.getEntryName();
-            if (!digestRequest.isDone()) {
-                throw new IllegalStateException(
-                        "Still waiting to inspect output APK's " + entryName);
-            }
-            mOutputJarEntryDigests.put(entryName, digestRequest.getDigest());
-        }
-        mOutputJarEntryDigestRequests.clear();
-
-        for (GetJarEntryDataRequest dataRequest : mOutputSignatureJarEntryDataRequests.values()) {
-            if (!dataRequest.isDone()) {
-                throw new IllegalStateException(
-                        "Still waiting to inspect output APK's " + dataRequest.getEntryName());
-            }
-        }
-
-        List<Integer> apkSigningSchemeIds =
-                (mV2SigningEnabled) ? Collections.singletonList(2) : Collections.emptyList();
-        byte[] inputJarManifest =
-                (mInputJarManifestEntryDataRequest != null)
-                    ? mInputJarManifestEntryDataRequest.getData() : null;
-
-        // Check whether the most recently used signature (if present) is still fine.
-        List<Pair<String, byte[]>> signatureZipEntries;
-        if ((mAddV1SignatureRequest == null) || (!mAddV1SignatureRequest.isDone())) {
-            try {
-                signatureZipEntries =
-                        V1SchemeSigner.sign(
-                                mV1SignerConfigs,
-                                mV1ContentDigestAlgorithm,
-                                mOutputJarEntryDigests,
-                                apkSigningSchemeIds,
-                                inputJarManifest);
-            } catch (CertificateException e) {
-                throw new SignatureException("Failed to generate v1 signature", e);
-            }
-        } else {
-            V1SchemeSigner.OutputManifestFile newManifest =
-                    V1SchemeSigner.generateManifestFile(
-                            mV1ContentDigestAlgorithm, mOutputJarEntryDigests, inputJarManifest);
-            byte[] emittedSignatureManifest =
-                    mEmittedSignatureJarEntryData.get(V1SchemeSigner.MANIFEST_ENTRY_NAME);
-            if (!Arrays.equals(newManifest.contents, emittedSignatureManifest)) {
-                // Emitted v1 signature is no longer valid.
-                try {
-                    signatureZipEntries =
-                            V1SchemeSigner.signManifest(
-                                    mV1SignerConfigs,
-                                    mV1ContentDigestAlgorithm,
-                                    apkSigningSchemeIds,
-                                    newManifest);
-                } catch (CertificateException e) {
-                    throw new SignatureException("Failed to generate v1 signature", e);
-                }
-            } else {
-                // Emitted v1 signature is still valid. Check whether the signature is there in the
-                // output.
-                signatureZipEntries = new ArrayList<>();
-                for (Map.Entry<String, byte[]> expectedOutputEntry
-                        : mEmittedSignatureJarEntryData.entrySet()) {
-                    String entryName = expectedOutputEntry.getKey();
-                    byte[] expectedData = expectedOutputEntry.getValue();
-                    GetJarEntryDataRequest actualDataRequest =
-                            mOutputSignatureJarEntryDataRequests.get(entryName);
-                    if (actualDataRequest == null) {
-                        // This signature entry hasn't been output.
-                        signatureZipEntries.add(Pair.of(entryName, expectedData));
-                        continue;
-                    }
-                    byte[] actualData = actualDataRequest.getData();
-                    if (!Arrays.equals(expectedData, actualData)) {
-                        signatureZipEntries.add(Pair.of(entryName, expectedData));
-                    }
-                }
-                if (signatureZipEntries.isEmpty()) {
-                    // v1 signature in the output is valid
-                    return null;
-                }
-                // v1 signature in the output is not valid.
-            }
-        }
-
-        if (signatureZipEntries.isEmpty()) {
-            // v1 signature in the output is valid
-            mV1SignaturePending = false;
-            return null;
-        }
-
-        List<OutputJarSignatureRequest.JarEntry> sigEntries =
-                new ArrayList<>(signatureZipEntries.size());
-        for (Pair<String, byte[]> entry : signatureZipEntries) {
-            String entryName = entry.getFirst();
-            byte[] entryData = entry.getSecond();
-            sigEntries.add(new OutputJarSignatureRequest.JarEntry(entryName, entryData));
-            mEmittedSignatureJarEntryData.put(entryName, entryData);
-        }
-        mAddV1SignatureRequest = new OutputJarSignatureRequestImpl(sigEntries);
-        return mAddV1SignatureRequest;
-    }
-
-    @Override
-    public OutputApkSigningBlockRequest outputZipSections(
-            DataSource zipEntries,
-            DataSource zipCentralDirectory,
-            DataSource zipEocd)
-                    throws IOException, InvalidKeyException, SignatureException,
-                            NoSuchAlgorithmException {
-        checkNotClosed();
-        checkV1SigningDoneIfEnabled();
-        if (!mV2SigningEnabled) {
-            return null;
-        }
-        invalidateV2Signature();
-
-        byte[] apkSigningBlock =
-                V2SchemeSigner.generateApkSigningBlock(
-                        zipEntries, zipCentralDirectory, zipEocd, mV2SignerConfigs);
-
-        mAddV2SignatureRequest = new OutputApkSigningBlockRequestImpl(apkSigningBlock);
-        return mAddV2SignatureRequest;
-    }
-
-    @Override
-    public void outputDone() {
-        checkNotClosed();
-        checkV1SigningDoneIfEnabled();
-        checkV2SigningDoneIfEnabled();
-    }
-
-    @Override
-    public void close() {
-        mClosed = true;
-
-        mAddV1SignatureRequest = null;
-        mInputJarManifestEntryDataRequest = null;
-        mOutputJarEntryDigestRequests.clear();
-        mOutputJarEntryDigests.clear();
-        mEmittedSignatureJarEntryData.clear();
-        mOutputSignatureJarEntryDataRequests.clear();
-
-        mAddV2SignatureRequest = null;
-    }
-
-    private void invalidateV1Signature() {
-        if (mV1SigningEnabled) {
-            mV1SignaturePending = true;
-        }
-        invalidateV2Signature();
-    }
-
-    private void invalidateV2Signature() {
-        if (mV2SigningEnabled) {
-            mV2SignaturePending = true;
-            mAddV2SignatureRequest = null;
-        }
-    }
-
-    private void checkNotClosed() {
-        if (mClosed) {
-            throw new IllegalStateException("Engine closed");
-        }
-    }
-
-    private void checkV1SigningDoneIfEnabled() {
-        if (!mV1SignaturePending) {
-            return;
-        }
-
-        if (mAddV1SignatureRequest == null) {
-            throw new IllegalStateException(
-                    "v1 signature (JAR signature) not yet generated. Skipped outputJarEntries()?");
-        }
-        if (!mAddV1SignatureRequest.isDone()) {
-            throw new IllegalStateException(
-                    "v1 signature (JAR signature) addition requested by outputJarEntries() hasn't"
-                            + " been fulfilled");
-        }
-        for (Map.Entry<String, byte[]> expectedOutputEntry
-                : mEmittedSignatureJarEntryData.entrySet()) {
-            String entryName = expectedOutputEntry.getKey();
-            byte[] expectedData = expectedOutputEntry.getValue();
-            GetJarEntryDataRequest actualDataRequest =
-                    mOutputSignatureJarEntryDataRequests.get(entryName);
-            if (actualDataRequest == null) {
-                throw new IllegalStateException(
-                        "APK entry " + entryName + " not yet output despite this having been"
-                                + " requested");
-            } else if (!actualDataRequest.isDone()) {
-                throw new IllegalStateException(
-                        "Still waiting to inspect output APK's " + entryName);
-            }
-            byte[] actualData = actualDataRequest.getData();
-            if (!Arrays.equals(expectedData, actualData)) {
-                throw new IllegalStateException(
-                        "Output APK entry " + entryName + " data differs from what was requested");
-            }
-        }
-        mV1SignaturePending = false;
-    }
-
-    private void checkV2SigningDoneIfEnabled() {
-        if (!mV2SignaturePending) {
-            return;
-        }
-        if (mAddV2SignatureRequest == null) {
-            throw new IllegalStateException(
-                    "v2 signature (APK Signature Scheme v2 signature) not yet generated."
-                            + " Skipped outputZipSections()?");
-        }
-        if (!mAddV2SignatureRequest.isDone()) {
-            throw new IllegalStateException(
-                    "v2 signature (APK Signature Scheme v2 signature) addition requested by"
-                            + " outputZipSections() hasn't been fulfilled yet");
-        }
-        mAddV2SignatureRequest = null;
-        mV2SignaturePending = false;
-    }
-
-    /**
-     * Returns the output policy for the provided input JAR entry.
-     */
-    private InputJarEntryInstructions.OutputPolicy getInputJarEntryOutputPolicy(String entryName) {
-        if (mSignatureExpectedOutputJarEntryNames.contains(entryName)) {
-            return InputJarEntryInstructions.OutputPolicy.OUTPUT_BY_ENGINE;
-        }
-        if ((mOtherSignersSignaturesPreserved)
-                || (V1SchemeSigner.isJarEntryDigestNeededInManifest(entryName))) {
-            return InputJarEntryInstructions.OutputPolicy.OUTPUT;
-        }
-        return InputJarEntryInstructions.OutputPolicy.SKIP;
-    }
-
-    private static class OutputJarSignatureRequestImpl implements OutputJarSignatureRequest {
-        private final List<JarEntry> mAdditionalJarEntries;
-        private volatile boolean mDone;
-
-        private OutputJarSignatureRequestImpl(List<JarEntry> additionalZipEntries) {
-            mAdditionalJarEntries =
-                    Collections.unmodifiableList(new ArrayList<>(additionalZipEntries));
-        }
-
-        @Override
-        public List<JarEntry> getAdditionalJarEntries() {
-            return mAdditionalJarEntries;
-        }
-
-        @Override
-        public void done() {
-            mDone = true;
-        }
-
-        private boolean isDone() {
-            return mDone;
-        }
-    }
-
-    private static class OutputApkSigningBlockRequestImpl implements OutputApkSigningBlockRequest {
-        private final byte[] mApkSigningBlock;
-        private volatile boolean mDone;
-
-        private OutputApkSigningBlockRequestImpl(byte[] apkSigingBlock) {
-            mApkSigningBlock = apkSigingBlock.clone();
-        }
-
-        @Override
-        public byte[] getApkSigningBlock() {
-            return mApkSigningBlock.clone();
-        }
-
-        @Override
-        public void done() {
-            mDone = true;
-        }
-
-        private boolean isDone() {
-            return mDone;
-        }
-    }
-
-    /**
-     * JAR entry inspection request which obtain the entry's uncompressed data.
-     */
-    private static class GetJarEntryDataRequest implements InspectJarEntryRequest {
-        private final String mEntryName;
-        private final Object mLock = new Object();
-
-        private boolean mDone;
-        private DataSink mDataSink;
-        private ByteArrayOutputStream mDataSinkBuf;
-
-        private GetJarEntryDataRequest(String entryName) {
-            mEntryName = entryName;
-        }
-
-        @Override
-        public String getEntryName() {
-            return mEntryName;
-        }
-
-        @Override
-        public DataSink getDataSink() {
-            synchronized (mLock) {
-                checkNotDone();
-                if (mDataSinkBuf == null) {
-                    mDataSinkBuf = new ByteArrayOutputStream();
-                }
-                if (mDataSink == null) {
-                    mDataSink = DataSinks.asDataSink(mDataSinkBuf);
-                }
-                return mDataSink;
-            }
-        }
-
-        @Override
-        public void done() {
-            synchronized (mLock) {
-                if (mDone) {
-                    return;
-                }
-                mDone = true;
-            }
-        }
-
-        private boolean isDone() {
-            synchronized (mLock) {
-                return mDone;
-            }
-        }
-
-        private void checkNotDone() throws IllegalStateException {
-            synchronized (mLock) {
-                if (mDone) {
-                    throw new IllegalStateException("Already done");
-                }
-            }
-        }
-
-        private byte[] getData() {
-            synchronized (mLock) {
-                if (!mDone) {
-                    throw new IllegalStateException("Not yet done");
-                }
-                return (mDataSinkBuf != null) ? mDataSinkBuf.toByteArray() : new byte[0];
-            }
-        }
-    }
-
-    /**
-     * JAR entry inspection request which obtains the digest of the entry's uncompressed data.
-     */
-    private static class GetJarEntryDataDigestRequest implements InspectJarEntryRequest {
-        private final String mEntryName;
-        private final String mJcaDigestAlgorithm;
-        private final Object mLock = new Object();
-
-        private boolean mDone;
-        private DataSink mDataSink;
-        private MessageDigest mMessageDigest;
-        private byte[] mDigest;
-
-        private GetJarEntryDataDigestRequest(String entryName, String jcaDigestAlgorithm) {
-            mEntryName = entryName;
-            mJcaDigestAlgorithm = jcaDigestAlgorithm;
-        }
-
-        @Override
-        public String getEntryName() {
-            return mEntryName;
-        }
-
-        @Override
-        public DataSink getDataSink() {
-            synchronized (mLock) {
-                checkNotDone();
-                if (mDataSink == null) {
-                    mDataSink = new MessageDigestSink(new MessageDigest[] {getMessageDigest()});
-                }
-                return mDataSink;
-            }
-        }
-
-        private MessageDigest getMessageDigest() {
-            synchronized (mLock) {
-                if (mMessageDigest == null) {
-                    try {
-                        mMessageDigest = MessageDigest.getInstance(mJcaDigestAlgorithm);
-                    } catch (NoSuchAlgorithmException e) {
-                        throw new RuntimeException(
-                                mJcaDigestAlgorithm + " MessageDigest not available", e);
-                    }
-                }
-                return mMessageDigest;
-            }
-        }
-
-        @Override
-        public void done() {
-            synchronized (mLock) {
-                if (mDone) {
-                    return;
-                }
-                mDone = true;
-                mDigest = getMessageDigest().digest();
-                mMessageDigest = null;
-                mDataSink = null;
-            }
-        }
-
-        private boolean isDone() {
-            synchronized (mLock) {
-                return mDone;
-            }
-        }
-
-        private void checkNotDone() throws IllegalStateException {
-            synchronized (mLock) {
-                if (mDone) {
-                    throw new IllegalStateException("Already done");
-                }
-            }
-        }
-
-        private byte[] getDigest() {
-            synchronized (mLock) {
-                if (!mDone) {
-                    throw new IllegalStateException("Not yet done");
-                }
-                return mDigest.clone();
-            }
-        }
-    }
-
-    /**
-     * Configuration of a signer.
-     *
-     * <p>Use {@link Builder} to obtain configuration instances.
-     */
-    public static class SignerConfig {
-        private final String mName;
-        private final PrivateKey mPrivateKey;
-        private final List<X509Certificate> mCertificates;
-
-        private SignerConfig(
-                String name,
-                PrivateKey privateKey,
-                List<X509Certificate> certificates) {
-            mName = name;
-            mPrivateKey = privateKey;
-            mCertificates = Collections.unmodifiableList(new ArrayList<>(certificates));
-        }
-
-        /**
-         * Returns the name of this signer.
-         */
-        public String getName() {
-            return mName;
-        }
-
-        /**
-         * Returns the signing key of this signer.
-         */
-        public PrivateKey getPrivateKey() {
-            return mPrivateKey;
-        }
-
-        /**
-         * Returns the certificate(s) of this signer. The first certificate's public key corresponds
-         * to this signer's private key.
-         */
-        public List<X509Certificate> getCertificates() {
-            return mCertificates;
-        }
-
-        /**
-         * Builder of {@link SignerConfig} instances.
-         */
-        public static class Builder {
-            private final String mName;
-            private final PrivateKey mPrivateKey;
-            private final List<X509Certificate> mCertificates;
-
-            /**
-             * Constructs a new {@code Builder}.
-             *
-             * @param name signer's name. The name is reflected in the name of files comprising the
-             *        JAR signature of the APK.
-             * @param privateKey signing key
-             * @param certificates list of one or more X.509 certificates. The subject public key of
-             *        the first certificate must correspond to the {@code privateKey}.
-             */
-            public Builder(
-                    String name,
-                    PrivateKey privateKey,
-                    List<X509Certificate> certificates) {
-                mName = name;
-                mPrivateKey = privateKey;
-                mCertificates = new ArrayList<>(certificates);
-            }
-
-            /**
-             * Returns a new {@code SignerConfig} instance configured based on the configuration of
-             * this builder.
-             */
-            public SignerConfig build() {
-                return new SignerConfig(
-                        mName,
-                        mPrivateKey,
-                        mCertificates);
-            }
-        }
-    }
-
-    /**
-     * Builder of {@link DefaultApkSignerEngine} instances.
-     */
-    public static class Builder {
-        private final List<SignerConfig> mSignerConfigs;
-        private final int mMinSdkVersion;
-
-        private boolean mV1SigningEnabled = true;
-        private boolean mV2SigningEnabled = true;
-        private boolean mOtherSignersSignaturesPreserved;
-
-        /**
-         * Constructs a new {@code Builder}.
-         *
-         * @param signerConfigs information about signers with which the APK will be signed. At
-         *        least one signer configuration must be provided.
-         * @param minSdkVersion API Level of the oldest Android platform on which the APK is
-         *        supposed to be installed. See {@code minSdkVersion} attribute in the APK's
-         *        {@code AndroidManifest.xml}. The higher the version, the stronger signing features
-         *        will be enabled.
-         */
-        public Builder(
-                List<SignerConfig> signerConfigs,
-                int minSdkVersion) {
-            if (signerConfigs.isEmpty()) {
-                throw new IllegalArgumentException("At least one signer config must be provided");
-            }
-            mSignerConfigs = new ArrayList<>(signerConfigs);
-            mMinSdkVersion = minSdkVersion;
-        }
-
-        /**
-         * Returns a new {@code DefaultApkSignerEngine} instance configured based on the
-         * configuration of this builder.
-         */
-        public DefaultApkSignerEngine build() throws InvalidKeyException {
-            return new DefaultApkSignerEngine(
-                    mSignerConfigs,
-                    mMinSdkVersion,
-                    mV1SigningEnabled,
-                    mV2SigningEnabled,
-                    mOtherSignersSignaturesPreserved);
-        }
-
-        /**
-         * Sets whether the APK should be signed using JAR signing (aka v1 signature scheme).
-         *
-         * <p>By default, the APK will be signed using this scheme.
-         */
-        public Builder setV1SigningEnabled(boolean enabled) {
-            mV1SigningEnabled = enabled;
-            return this;
-        }
-
-        /**
-         * Sets whether the APK should be signed using APK Signature Scheme v2 (aka v2 signature
-         * scheme).
-         *
-         * <p>By default, the APK will be signed using this scheme.
-         */
-        public Builder setV2SigningEnabled(boolean enabled) {
-            mV2SigningEnabled = enabled;
-            return this;
-        }
-
-        /**
-         * Sets whether signatures produced by signers other than the ones configured in this engine
-         * should be copied from the input APK to the output APK.
-         *
-         * <p>By default, signatures of other signers are omitted from the output APK.
-         */
-        public Builder setOtherSignersSignaturesPreserved(boolean preserved) {
-            mOtherSignersSignaturesPreserved = preserved;
-            return this;
-        }
-    }
-}
diff --git a/tools/apksigner/core/src/com/android/apksigner/core/apk/ApkUtils.java b/tools/apksigner/core/src/com/android/apksigner/core/apk/ApkUtils.java
deleted file mode 100644
index 8cc8c90..0000000
--- a/tools/apksigner/core/src/com/android/apksigner/core/apk/ApkUtils.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.apksigner.core.apk;
-
-import com.android.apksigner.core.internal.util.Pair;
-import com.android.apksigner.core.internal.zip.ZipUtils;
-import com.android.apksigner.core.util.DataSource;
-import com.android.apksigner.core.zip.ZipFormatException;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-
-/**
- * APK utilities.
- */
-public class ApkUtils {
-
-    private ApkUtils() {}
-
-    /**
-     * Finds the main ZIP sections of the provided APK.
-     *
-     * @throws IOException if an I/O error occurred while reading the APK
-     * @throws ZipFormatException if the APK is malformed
-     */
-    public static ZipSections findZipSections(DataSource apk)
-            throws IOException, ZipFormatException {
-        Pair<ByteBuffer, Long> eocdAndOffsetInFile =
-                ZipUtils.findZipEndOfCentralDirectoryRecord(apk);
-        if (eocdAndOffsetInFile == null) {
-            throw new ZipFormatException("ZIP End of Central Directory record not found");
-        }
-
-        ByteBuffer eocdBuf = eocdAndOffsetInFile.getFirst();
-        long eocdOffset = eocdAndOffsetInFile.getSecond();
-        if (ZipUtils.isZip64EndOfCentralDirectoryLocatorPresent(apk, eocdOffset)) {
-            throw new ZipFormatException("ZIP64 APK not supported");
-        }
-        eocdBuf.order(ByteOrder.LITTLE_ENDIAN);
-        long cdStartOffset = ZipUtils.getZipEocdCentralDirectoryOffset(eocdBuf);
-        if (cdStartOffset >= eocdOffset) {
-            throw new ZipFormatException(
-                    "ZIP Central Directory start offset out of range: " + cdStartOffset
-                        + ". ZIP End of Central Directory offset: " + eocdOffset);
-        }
-
-        long cdSizeBytes = ZipUtils.getZipEocdCentralDirectorySizeBytes(eocdBuf);
-        long cdEndOffset = cdStartOffset + cdSizeBytes;
-        if (cdEndOffset > eocdOffset) {
-            throw new ZipFormatException(
-                    "ZIP Central Directory overlaps with End of Central Directory"
-                            + ". CD end: " + cdEndOffset
-                            + ", EoCD start: " + eocdOffset);
-        }
-
-        int cdRecordCount = ZipUtils.getZipEocdCentralDirectoryTotalRecordCount(eocdBuf);
-
-        return new ZipSections(
-                cdStartOffset,
-                cdSizeBytes,
-                cdRecordCount,
-                eocdOffset,
-                eocdBuf);
-    }
-
-    /**
-     * Information about the ZIP sections of an APK.
-     */
-    public static class ZipSections {
-        private final long mCentralDirectoryOffset;
-        private final long mCentralDirectorySizeBytes;
-        private final int mCentralDirectoryRecordCount;
-        private final long mEocdOffset;
-        private final ByteBuffer mEocd;
-
-        public ZipSections(
-                long centralDirectoryOffset,
-                long centralDirectorySizeBytes,
-                int centralDirectoryRecordCount,
-                long eocdOffset,
-                ByteBuffer eocd) {
-            mCentralDirectoryOffset = centralDirectoryOffset;
-            mCentralDirectorySizeBytes = centralDirectorySizeBytes;
-            mCentralDirectoryRecordCount = centralDirectoryRecordCount;
-            mEocdOffset = eocdOffset;
-            mEocd = eocd;
-        }
-
-        /**
-         * Returns the start offset of the ZIP Central Directory. This value is taken from the
-         * ZIP End of Central Directory record.
-         */
-        public long getZipCentralDirectoryOffset() {
-            return mCentralDirectoryOffset;
-        }
-
-        /**
-         * Returns the size (in bytes) of the ZIP Central Directory. This value is taken from the
-         * ZIP End of Central Directory record.
-         */
-        public long getZipCentralDirectorySizeBytes() {
-            return mCentralDirectorySizeBytes;
-        }
-
-        /**
-         * Returns the number of records in the ZIP Central Directory. This value is taken from the
-         * ZIP End of Central Directory record.
-         */
-        public int getZipCentralDirectoryRecordCount() {
-            return mCentralDirectoryRecordCount;
-        }
-
-        /**
-         * Returns the start offset of the ZIP End of Central Directory record. The record extends
-         * until the very end of the APK.
-         */
-        public long getZipEndOfCentralDirectoryOffset() {
-            return mEocdOffset;
-        }
-
-        /**
-         * Returns the contents of the ZIP End of Central Directory.
-         */
-        public ByteBuffer getZipEndOfCentralDirectory() {
-            return mEocd;
-        }
-    }
-
-    /**
-     * Sets the offset of the start of the ZIP Central Directory in the APK's ZIP End of Central
-     * Directory record.
-     *
-     * @param zipEndOfCentralDirectory APK's ZIP End of Central Directory record
-     * @param offset offset of the ZIP Central Directory relative to the start of the archive. Must
-     *        be between {@code 0} and {@code 2^32 - 1} inclusive.
-     */
-    public static void setZipEocdCentralDirectoryOffset(
-            ByteBuffer zipEndOfCentralDirectory, long offset) {
-        ByteBuffer eocd = zipEndOfCentralDirectory.slice();
-        eocd.order(ByteOrder.LITTLE_ENDIAN);
-        ZipUtils.setZipEocdCentralDirectoryOffset(eocd, offset);
-    }
-}
diff --git a/tools/apksigner/core/src/com/android/apksigner/core/internal/apk/v1/DigestAlgorithm.java b/tools/apksigner/core/src/com/android/apksigner/core/internal/apk/v1/DigestAlgorithm.java
deleted file mode 100644
index 71e698b..0000000
--- a/tools/apksigner/core/src/com/android/apksigner/core/internal/apk/v1/DigestAlgorithm.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.apksigner.core.internal.apk.v1;
-
-/**
- * Digest algorithm used with JAR signing (aka v1 signing scheme).
- */
-public enum DigestAlgorithm {
-    /** SHA-1 */
-    SHA1("SHA-1"),
-
-    /** SHA2-256 */
-    SHA256("SHA-256");
-
-    private final String mJcaMessageDigestAlgorithm;
-
-    private DigestAlgorithm(String jcaMessageDigestAlgoritm) {
-        mJcaMessageDigestAlgorithm = jcaMessageDigestAlgoritm;
-    }
-
-    /**
-     * Returns the {@link java.security.MessageDigest} algorithm represented by this digest
-     * algorithm.
-     */
-    String getJcaMessageDigestAlgorithm() {
-        return mJcaMessageDigestAlgorithm;
-    }
-}
diff --git a/tools/apksigner/core/src/com/android/apksigner/core/internal/apk/v1/V1SchemeSigner.java b/tools/apksigner/core/src/com/android/apksigner/core/internal/apk/v1/V1SchemeSigner.java
deleted file mode 100644
index f124d16..0000000
--- a/tools/apksigner/core/src/com/android/apksigner/core/internal/apk/v1/V1SchemeSigner.java
+++ /dev/null
@@ -1,620 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.apksigner.core.internal.apk.v1;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.security.InvalidKeyException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.Signature;
-import java.security.SignatureException;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateParsingException;
-import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-import java.util.Base64;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-import java.util.SortedMap;
-import java.util.TreeMap;
-import java.util.jar.Attributes;
-import java.util.jar.Manifest;
-
-import sun.security.pkcs.ContentInfo;
-import sun.security.pkcs.PKCS7;
-import sun.security.pkcs.SignerInfo;
-import sun.security.x509.AlgorithmId;
-import sun.security.x509.X500Name;
-
-import com.android.apksigner.core.internal.jar.ManifestWriter;
-import com.android.apksigner.core.internal.jar.SignatureFileWriter;
-import com.android.apksigner.core.internal.util.Pair;
-
-/**
- * APK signer which uses JAR signing (aka v1 signing scheme).
- *
- * @see <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#Signed_JAR_File">Signed JAR File</a>
- */
-public abstract class V1SchemeSigner {
-
-    public static final String MANIFEST_ENTRY_NAME = "META-INF/MANIFEST.MF";
-
-    private static final Attributes.Name ATTRIBUTE_NAME_CREATED_BY =
-            new Attributes.Name("Created-By");
-    private static final String ATTRIBUTE_DEFALT_VALUE_CREATED_BY = "1.0 (Android apksigner)";
-    private static final String ATTRIBUTE_VALUE_MANIFEST_VERSION = "1.0";
-    private static final String ATTRIBUTE_VALUE_SIGNATURE_VERSION = "1.0";
-
-    static final String SF_ATTRIBUTE_NAME_ANDROID_APK_SIGNED_NAME_STR = "X-Android-APK-Signed";
-    private static final Attributes.Name SF_ATTRIBUTE_NAME_ANDROID_APK_SIGNED_NAME =
-            new Attributes.Name(SF_ATTRIBUTE_NAME_ANDROID_APK_SIGNED_NAME_STR);
-
-    /**
-     * Signer configuration.
-     */
-    public static class SignerConfig {
-        /** Name. */
-        public String name;
-
-        /** Private key. */
-        public PrivateKey privateKey;
-
-        /**
-         * Certificates, with the first certificate containing the public key corresponding to
-         * {@link #privateKey}.
-         */
-        public List<X509Certificate> certificates;
-
-        /**
-         * Digest algorithm used for the signature.
-         */
-        public DigestAlgorithm signatureDigestAlgorithm;
-
-        /**
-         * Digest algorithm used for digests of JAR entries and MANIFEST.MF.
-         */
-        public DigestAlgorithm contentDigestAlgorithm;
-    }
-
-    /** Hidden constructor to prevent instantiation. */
-    private V1SchemeSigner() {}
-
-    /**
-     * Gets the JAR signing digest algorithm to be used for signing an APK using the provided key.
-     *
-     * @param minSdkVersion minimum API Level of the platform on which the APK may be installed (see
-     *        AndroidManifest.xml minSdkVersion attribute)
-     *
-     * @throws InvalidKeyException if the provided key is not suitable for signing APKs using
-     *         JAR signing (aka v1 signature scheme)
-     */
-    public static DigestAlgorithm getSuggestedSignatureDigestAlgorithm(
-            PublicKey signingKey, int minSdkVersion) throws InvalidKeyException {
-        String keyAlgorithm = signingKey.getAlgorithm();
-        if ("RSA".equalsIgnoreCase(keyAlgorithm)) {
-            // Prior to API Level 18, only SHA-1 can be used with RSA.
-            if (minSdkVersion < 18) {
-                return DigestAlgorithm.SHA1;
-            }
-            return DigestAlgorithm.SHA256;
-        } else if ("DSA".equalsIgnoreCase(keyAlgorithm)) {
-            // Prior to API Level 21, only SHA-1 can be used with DSA
-            if (minSdkVersion < 21) {
-                return DigestAlgorithm.SHA1;
-            } else {
-                return DigestAlgorithm.SHA256;
-            }
-        } else if ("EC".equalsIgnoreCase(keyAlgorithm)) {
-            if (minSdkVersion < 18) {
-                throw new InvalidKeyException(
-                        "ECDSA signatures only supported for minSdkVersion 18 and higher");
-            }
-            // Prior to API Level 21, only SHA-1 can be used with ECDSA
-            if (minSdkVersion < 21) {
-                return DigestAlgorithm.SHA1;
-            } else {
-                return DigestAlgorithm.SHA256;
-            }
-        } else {
-            throw new InvalidKeyException("Unsupported key algorithm: " + keyAlgorithm);
-        }
-    }
-
-    /**
-     * Returns the JAR signing digest algorithm to be used for JAR entry digests.
-     *
-     * @param minSdkVersion minimum API Level of the platform on which the APK may be installed (see
-     *        AndroidManifest.xml minSdkVersion attribute)
-     */
-    public static DigestAlgorithm getSuggestedContentDigestAlgorithm(int minSdkVersion) {
-        return (minSdkVersion >= 18) ? DigestAlgorithm.SHA256 : DigestAlgorithm.SHA1;
-    }
-
-    /**
-     * Returns a new {@link MessageDigest} instance corresponding to the provided digest algorithm.
-     */
-    private static MessageDigest getMessageDigestInstance(DigestAlgorithm digestAlgorithm)
-            throws NoSuchAlgorithmException {
-        String jcaAlgorithm = digestAlgorithm.getJcaMessageDigestAlgorithm();
-        return MessageDigest.getInstance(jcaAlgorithm);
-    }
-
-    /**
-     * Returns the JCA {@link MessageDigest} algorithm corresponding to the provided digest
-     * algorithm.
-     */
-    public static String getJcaMessageDigestAlgorithm(DigestAlgorithm digestAlgorithm) {
-        return digestAlgorithm.getJcaMessageDigestAlgorithm();
-    }
-
-    /**
-     * Returns {@code true} if the provided JAR entry must be mentioned in signed JAR archive's
-     * manifest.
-     */
-    public static boolean isJarEntryDigestNeededInManifest(String entryName) {
-        // See https://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#Signed_JAR_File
-
-        // Entries outside of META-INF must be listed in the manifest.
-        if (!entryName.startsWith("META-INF/")) {
-            return true;
-        }
-        // Entries in subdirectories of META-INF must be listed in the manifest.
-        if (entryName.indexOf('/', "META-INF/".length()) != -1) {
-            return true;
-        }
-
-        // Ignored file names (case-insensitive) in META-INF directory:
-        //   MANIFEST.MF
-        //   *.SF
-        //   *.RSA
-        //   *.DSA
-        //   *.EC
-        //   SIG-*
-        String fileNameLowerCase =
-                entryName.substring("META-INF/".length()).toLowerCase(Locale.US);
-        if (("manifest.mf".equals(fileNameLowerCase))
-                || (fileNameLowerCase.endsWith(".sf"))
-                || (fileNameLowerCase.endsWith(".rsa"))
-                || (fileNameLowerCase.endsWith(".dsa"))
-                || (fileNameLowerCase.endsWith(".ec"))
-                || (fileNameLowerCase.startsWith("sig-"))) {
-            return false;
-        }
-        return true;
-    }
-
-    /**
-     * Signs the provided APK using JAR signing (aka v1 signature scheme) and returns the list of
-     * JAR entries which need to be added to the APK as part of the signature.
-     *
-     * @param signerConfigs signer configurations, one for each signer. At least one signer config
-     *        must be provided.
-     *
-     * @throws NoSuchAlgorithmException if a required cryptographic algorithm implementation is
-     *         missing
-     * @throws InvalidKeyException if a signing key is not suitable for this signature scheme or
-     *         cannot be used in general
-     * @throws SignatureException if an error occurs when computing digests of generating
-     *         signatures
-     */
-    public static List<Pair<String, byte[]>> sign(
-            List<SignerConfig> signerConfigs,
-            DigestAlgorithm jarEntryDigestAlgorithm,
-            Map<String, byte[]> jarEntryDigests,
-            List<Integer> apkSigningSchemeIds,
-            byte[] sourceManifestBytes)
-                    throws NoSuchAlgorithmException, InvalidKeyException, CertificateException,
-                            SignatureException {
-        if (signerConfigs.isEmpty()) {
-            throw new IllegalArgumentException("At least one signer config must be provided");
-        }
-        OutputManifestFile manifest =
-                generateManifestFile(jarEntryDigestAlgorithm, jarEntryDigests, sourceManifestBytes);
-
-        return signManifest(signerConfigs, jarEntryDigestAlgorithm, apkSigningSchemeIds, manifest);
-    }
-
-    /**
-     * Signs the provided APK using JAR signing (aka v1 signature scheme) and returns the list of
-     * JAR entries which need to be added to the APK as part of the signature.
-     *
-     * @param signerConfigs signer configurations, one for each signer. At least one signer config
-     *        must be provided.
-     *
-     * @throws InvalidKeyException if a signing key is not suitable for this signature scheme or
-     *         cannot be used in general
-     * @throws SignatureException if an error occurs when computing digests of generating
-     *         signatures
-     */
-    public static List<Pair<String, byte[]>> signManifest(
-            List<SignerConfig> signerConfigs,
-            DigestAlgorithm digestAlgorithm,
-            List<Integer> apkSigningSchemeIds,
-            OutputManifestFile manifest)
-                    throws NoSuchAlgorithmException, InvalidKeyException, CertificateException,
-                            SignatureException {
-        if (signerConfigs.isEmpty()) {
-            throw new IllegalArgumentException("At least one signer config must be provided");
-        }
-
-        // For each signer output .SF and .(RSA|DSA|EC) file, then output MANIFEST.MF.
-        List<Pair<String, byte[]>> signatureJarEntries =
-                new ArrayList<>(2 * signerConfigs.size() + 1);
-        byte[] sfBytes =
-                generateSignatureFile(apkSigningSchemeIds, digestAlgorithm, manifest);
-        for (SignerConfig signerConfig : signerConfigs) {
-            String signerName = signerConfig.name;
-            byte[] signatureBlock;
-            try {
-                signatureBlock = generateSignatureBlock(signerConfig, sfBytes);
-            } catch (InvalidKeyException e) {
-                throw new InvalidKeyException(
-                        "Failed to sign using signer \"" + signerName + "\"", e);
-            } catch (CertificateException e) {
-                throw new CertificateException(
-                        "Failed to sign using signer \"" + signerName + "\"", e);
-            } catch (SignatureException e) {
-                throw new SignatureException(
-                        "Failed to sign using signer \"" + signerName + "\"", e);
-            }
-            signatureJarEntries.add(Pair.of("META-INF/" + signerName + ".SF", sfBytes));
-            PublicKey publicKey = signerConfig.certificates.get(0).getPublicKey();
-            String signatureBlockFileName =
-                    "META-INF/" + signerName + "."
-                            + publicKey.getAlgorithm().toUpperCase(Locale.US);
-            signatureJarEntries.add(
-                    Pair.of(signatureBlockFileName, signatureBlock));
-        }
-        signatureJarEntries.add(Pair.of(MANIFEST_ENTRY_NAME, manifest.contents));
-        return signatureJarEntries;
-    }
-
-    /**
-     * Returns the names of JAR entries which this signer will produce as part of v1 signature.
-     */
-    public static Set<String> getOutputEntryNames(List<SignerConfig> signerConfigs) {
-        Set<String> result = new HashSet<>(2 * signerConfigs.size() + 1);
-        for (SignerConfig signerConfig : signerConfigs) {
-            String signerName = signerConfig.name;
-            result.add("META-INF/" + signerName + ".SF");
-            PublicKey publicKey = signerConfig.certificates.get(0).getPublicKey();
-            String signatureBlockFileName =
-                    "META-INF/" + signerName + "."
-                            + publicKey.getAlgorithm().toUpperCase(Locale.US);
-            result.add(signatureBlockFileName);
-        }
-        result.add(MANIFEST_ENTRY_NAME);
-        return result;
-    }
-
-    /**
-     * Generated and returns the {@code META-INF/MANIFEST.MF} file based on the provided (optional)
-     * input {@code MANIFEST.MF} and digests of JAR entries covered by the manifest.
-     */
-    public static OutputManifestFile generateManifestFile(
-            DigestAlgorithm jarEntryDigestAlgorithm,
-            Map<String, byte[]> jarEntryDigests,
-            byte[] sourceManifestBytes) {
-        Manifest sourceManifest = null;
-        if (sourceManifestBytes != null) {
-            try {
-                sourceManifest = new Manifest(new ByteArrayInputStream(sourceManifestBytes));
-            } catch (IOException e) {
-                throw new IllegalArgumentException("Failed to parse source MANIFEST.MF", e);
-            }
-        }
-        ByteArrayOutputStream manifestOut = new ByteArrayOutputStream();
-        Attributes mainAttrs = new Attributes();
-        // Copy the main section from the source manifest (if provided). Otherwise use defaults.
-        if (sourceManifest != null) {
-            mainAttrs.putAll(sourceManifest.getMainAttributes());
-        } else {
-            mainAttrs.put(Attributes.Name.MANIFEST_VERSION, ATTRIBUTE_VALUE_MANIFEST_VERSION);
-            mainAttrs.put(ATTRIBUTE_NAME_CREATED_BY, ATTRIBUTE_DEFALT_VALUE_CREATED_BY);
-        }
-
-        try {
-            ManifestWriter.writeMainSection(manifestOut, mainAttrs);
-        } catch (IOException e) {
-            throw new RuntimeException("Failed to write in-memory MANIFEST.MF", e);
-        }
-
-        List<String> sortedEntryNames = new ArrayList<>(jarEntryDigests.keySet());
-        Collections.sort(sortedEntryNames);
-        SortedMap<String, byte[]> invidualSectionsContents = new TreeMap<>();
-        String entryDigestAttributeName = getEntryDigestAttributeName(jarEntryDigestAlgorithm);
-        for (String entryName : sortedEntryNames) {
-            byte[] entryDigest = jarEntryDigests.get(entryName);
-            Attributes entryAttrs = new Attributes();
-            entryAttrs.putValue(
-                    entryDigestAttributeName,
-                    Base64.getEncoder().encodeToString(entryDigest));
-            ByteArrayOutputStream sectionOut = new ByteArrayOutputStream();
-            byte[] sectionBytes;
-            try {
-                ManifestWriter.writeIndividualSection(sectionOut, entryName, entryAttrs);
-                sectionBytes = sectionOut.toByteArray();
-                manifestOut.write(sectionBytes);
-            } catch (IOException e) {
-                throw new RuntimeException("Failed to write in-memory MANIFEST.MF", e);
-            }
-            invidualSectionsContents.put(entryName, sectionBytes);
-        }
-
-        OutputManifestFile result = new OutputManifestFile();
-        result.contents = manifestOut.toByteArray();
-        result.mainSectionAttributes = mainAttrs;
-        result.individualSectionsContents = invidualSectionsContents;
-        return result;
-    }
-
-    public static class OutputManifestFile {
-        public byte[] contents;
-        public SortedMap<String, byte[]> individualSectionsContents;
-        public Attributes mainSectionAttributes;
-    }
-
-    private static byte[] generateSignatureFile(
-            List<Integer> apkSignatureSchemeIds,
-            DigestAlgorithm manifestDigestAlgorithm,
-            OutputManifestFile manifest) throws NoSuchAlgorithmException {
-        Manifest sf = new Manifest();
-        Attributes mainAttrs = sf.getMainAttributes();
-        mainAttrs.put(Attributes.Name.SIGNATURE_VERSION, ATTRIBUTE_VALUE_SIGNATURE_VERSION);
-        mainAttrs.put(ATTRIBUTE_NAME_CREATED_BY, ATTRIBUTE_DEFALT_VALUE_CREATED_BY);
-        if (!apkSignatureSchemeIds.isEmpty()) {
-            // Add APK Signature Scheme v2 (and newer) signature stripping protection.
-            // This attribute indicates that this APK is supposed to have been signed using one or
-            // more APK-specific signature schemes in addition to the standard JAR signature scheme
-            // used by this code. APK signature verifier should reject the APK if it does not
-            // contain a signature for the signature scheme the verifier prefers out of this set.
-            StringBuilder attrValue = new StringBuilder();
-            for (int id : apkSignatureSchemeIds) {
-                if (attrValue.length() > 0) {
-                    attrValue.append(", ");
-                }
-                attrValue.append(String.valueOf(id));
-            }
-            mainAttrs.put(
-                    SF_ATTRIBUTE_NAME_ANDROID_APK_SIGNED_NAME,
-                    attrValue.toString());
-        }
-
-        // Add main attribute containing the digest of MANIFEST.MF.
-        MessageDigest md = getMessageDigestInstance(manifestDigestAlgorithm);
-        mainAttrs.putValue(
-                getManifestDigestAttributeName(manifestDigestAlgorithm),
-                Base64.getEncoder().encodeToString(md.digest(manifest.contents)));
-        ByteArrayOutputStream out = new ByteArrayOutputStream();
-        try {
-            SignatureFileWriter.writeMainSection(out, mainAttrs);
-        } catch (IOException e) {
-            throw new RuntimeException("Failed to write in-memory .SF file", e);
-        }
-        String entryDigestAttributeName = getEntryDigestAttributeName(manifestDigestAlgorithm);
-        for (Map.Entry<String, byte[]> manifestSection
-                : manifest.individualSectionsContents.entrySet()) {
-            String sectionName = manifestSection.getKey();
-            byte[] sectionContents = manifestSection.getValue();
-            byte[] sectionDigest = md.digest(sectionContents);
-            Attributes attrs = new Attributes();
-            attrs.putValue(
-                    entryDigestAttributeName,
-                    Base64.getEncoder().encodeToString(sectionDigest));
-
-            try {
-                SignatureFileWriter.writeIndividualSection(out, sectionName, attrs);
-            } catch (IOException e) {
-                throw new RuntimeException("Failed to write in-memory .SF file", e);
-            }
-        }
-
-        // A bug in the java.util.jar implementation of Android platforms up to version 1.6 will
-        // cause a spurious IOException to be thrown if the length of the signature file is a
-        // multiple of 1024 bytes. As a workaround, add an extra CRLF in this case.
-        if ((out.size() > 0) && ((out.size() % 1024) == 0)) {
-            try {
-                SignatureFileWriter.writeSectionDelimiter(out);
-            } catch (IOException e) {
-                throw new RuntimeException("Failed to write to ByteArrayOutputStream", e);
-            }
-        }
-
-        return out.toByteArray();
-    }
-
-    @SuppressWarnings("restriction")
-    private static byte[] generateSignatureBlock(
-            SignerConfig signerConfig, byte[] signatureFileBytes)
-                    throws NoSuchAlgorithmException, InvalidKeyException, CertificateException,
-                            SignatureException {
-        List<X509Certificate> signerCerts = signerConfig.certificates;
-        X509Certificate signerCert = signerCerts.get(0);
-        PublicKey signerPublicKey = signerCert.getPublicKey();
-        DigestAlgorithm digestAlgorithm = signerConfig.signatureDigestAlgorithm;
-        Pair<String, AlgorithmId> signatureAlgs =
-                getSignerInfoSignatureAlgorithm(signerPublicKey, digestAlgorithm);
-        String jcaSignatureAlgorithm = signatureAlgs.getFirst();
-        Signature signature = Signature.getInstance(jcaSignatureAlgorithm);
-        signature.initSign(signerConfig.privateKey);
-        signature.update(signatureFileBytes);
-        byte[] signatureBytes = signature.sign();
-
-        X500Name issuerName;
-        try {
-            issuerName = new X500Name(signerCert.getIssuerX500Principal().getName());
-        } catch (IOException e) {
-            throw new CertificateParsingException(
-                    "Failed to parse signer certificate issuer name", e);
-        }
-
-        AlgorithmId digestAlgorithmId = getSignerInfoDigestAlgorithm(digestAlgorithm);
-        SignerInfo signerInfo =
-                new SignerInfo(
-                        issuerName,
-                        signerCert.getSerialNumber(),
-                        digestAlgorithmId,
-                        signatureAlgs.getSecond(),
-                        signatureBytes);
-        PKCS7 pkcs7 =
-                new PKCS7(
-                        new AlgorithmId[] {digestAlgorithmId},
-                        new ContentInfo(ContentInfo.DATA_OID, null),
-                        signerCerts.toArray(new X509Certificate[signerCerts.size()]),
-                        new SignerInfo[] {signerInfo});
-
-        ByteArrayOutputStream result = new ByteArrayOutputStream();
-        try {
-            pkcs7.encodeSignedData(result);
-        } catch (IOException e) {
-            throw new SignatureException("Failed to encode PKCS#7 signed data", e);
-        }
-        return result.toByteArray();
-    }
-
-    @SuppressWarnings("restriction")
-    private static final AlgorithmId OID_DIGEST_SHA1 = getSupportedAlgorithmId("1.3.14.3.2.26");
-    @SuppressWarnings("restriction")
-    private static final AlgorithmId OID_DIGEST_SHA256 =
-            getSupportedAlgorithmId("2.16.840.1.101.3.4.2.1");
-
-    /**
-     * Returns the {@code SignerInfo} {@code DigestAlgorithm} to use for {@code SignerInfo} signing
-     * using the specified digest algorithm.
-     */
-    @SuppressWarnings("restriction")
-    private static AlgorithmId getSignerInfoDigestAlgorithm(DigestAlgorithm digestAlgorithm) {
-        switch (digestAlgorithm) {
-            case SHA1:
-                return OID_DIGEST_SHA1;
-            case SHA256:
-                return OID_DIGEST_SHA256;
-            default:
-                throw new RuntimeException("Unsupported digest algorithm: " + digestAlgorithm);
-        }
-    }
-
-    /**
-     * Returns the JCA {@link Signature} algorithm and {@code SignerInfo} {@code SignatureAlgorithm}
-     * to use for {@code SignerInfo} which signs with the specified key and digest algorithms.
-     */
-    @SuppressWarnings("restriction")
-    private static Pair<String, AlgorithmId> getSignerInfoSignatureAlgorithm(
-            PublicKey publicKey, DigestAlgorithm digestAlgorithm) throws InvalidKeyException {
-        // NOTE: This method on purpose uses hard-coded OIDs instead of
-        // Algorithm.getId(JCA Signature Algorithm). This is to ensure that the generated SignedData
-        // is compatible with all targeted Android platforms and is not dependent on changes in the
-        // JCA Signature Algorithm -> OID mappings maintained by AlgorithmId.get(String).
-
-        String keyAlgorithm = publicKey.getAlgorithm();
-        String digestPrefixForSigAlg;
-        switch (digestAlgorithm) {
-            case SHA1:
-                digestPrefixForSigAlg = "SHA1";
-                break;
-            case SHA256:
-                digestPrefixForSigAlg = "SHA256";
-                break;
-            default:
-                throw new IllegalArgumentException(
-                        "Unexpected digest algorithm: " + digestAlgorithm);
-        }
-        if ("RSA".equalsIgnoreCase(keyAlgorithm)) {
-            return Pair.of(
-                    digestPrefixForSigAlg + "withRSA",
-                    getSupportedAlgorithmId("1.2.840.113549.1.1.1") // RSA encryption
-                    );
-        } else if ("DSA".equalsIgnoreCase(keyAlgorithm)) {
-            AlgorithmId sigAlgId;
-            switch (digestAlgorithm) {
-                case SHA1:
-                    sigAlgId = getSupportedAlgorithmId("1.2.840.10040.4.1"); // DSA
-                    break;
-                case SHA256:
-                    // DSA signatures with SHA-256 in SignedData are accepted by Android API Level
-                    // 21 and higher. However, there are two ways to specify their SignedData
-                    // SignatureAlgorithm: dsaWithSha256 (2.16.840.1.101.3.4.3.2) and
-                    // dsa (1.2.840.10040.4.1). The latter works only on API Level 22+. Thus, we use
-                    // the former.
-                    sigAlgId =
-                            getSupportedAlgorithmId("2.16.840.1.101.3.4.3.2"); // DSA with SHA-256
-                    break;
-                default:
-                    throw new IllegalArgumentException(
-                            "Unexpected digest algorithm: " + digestAlgorithm);
-            }
-            return Pair.of(digestPrefixForSigAlg + "withDSA", sigAlgId);
-        } else if ("EC".equalsIgnoreCase(keyAlgorithm)) {
-            AlgorithmId sigAlgId;
-            switch (digestAlgorithm) {
-                case SHA1:
-                    sigAlgId = getSupportedAlgorithmId("1.2.840.10045.4.1"); // ECDSA with SHA-1
-                    break;
-                case SHA256:
-                    sigAlgId = getSupportedAlgorithmId("1.2.840.10045.4.3.2"); // ECDSA with SHA-256
-                    break;
-                default:
-                    throw new IllegalArgumentException(
-                            "Unexpected digest algorithm: " + digestAlgorithm);
-            }
-            return Pair.of(digestPrefixForSigAlg + "withECDSA", sigAlgId);
-        } else {
-            throw new InvalidKeyException("Unsupported key algorithm: " + keyAlgorithm);
-        }
-    }
-
-    @SuppressWarnings("restriction")
-    private static AlgorithmId getSupportedAlgorithmId(String oid) {
-        try {
-            return AlgorithmId.get(oid);
-        } catch (NoSuchAlgorithmException e) {
-            throw new RuntimeException("Unsupported OID: " + oid, e);
-        }
-    }
-
-    private static String getEntryDigestAttributeName(DigestAlgorithm digestAlgorithm) {
-        switch (digestAlgorithm) {
-            case SHA1:
-                return "SHA1-Digest";
-            case SHA256:
-                return "SHA-256-Digest";
-            default:
-                throw new IllegalArgumentException(
-                        "Unexpected content digest algorithm: " + digestAlgorithm);
-        }
-    }
-
-    private static String getManifestDigestAttributeName(DigestAlgorithm digestAlgorithm) {
-        switch (digestAlgorithm) {
-            case SHA1:
-                return "SHA1-Digest-Manifest";
-            case SHA256:
-                return "SHA-256-Digest-Manifest";
-            default:
-                throw new IllegalArgumentException(
-                        "Unexpected content digest algorithm: " + digestAlgorithm);
-        }
-    }
-}
diff --git a/tools/apksigner/core/src/com/android/apksigner/core/internal/apk/v1/V1SchemeVerifier.java b/tools/apksigner/core/src/com/android/apksigner/core/internal/apk/v1/V1SchemeVerifier.java
deleted file mode 100644
index 752ba7e..0000000
--- a/tools/apksigner/core/src/com/android/apksigner/core/internal/apk/v1/V1SchemeVerifier.java
+++ /dev/null
@@ -1,1559 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.apksigner.core.internal.apk.v1;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.SignatureException;
-import java.security.cert.CertificateException;
-import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Base64;
-import java.util.Base64.Decoder;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-import java.util.StringTokenizer;
-import java.util.jar.Attributes;
-
-import com.android.apksigner.core.ApkVerifier.Issue;
-import com.android.apksigner.core.ApkVerifier.IssueWithParams;
-import com.android.apksigner.core.apk.ApkUtils;
-import com.android.apksigner.core.internal.jar.ManifestParser;
-import com.android.apksigner.core.internal.util.AndroidSdkVersion;
-import com.android.apksigner.core.internal.util.InclusiveIntRange;
-import com.android.apksigner.core.internal.util.MessageDigestSink;
-import com.android.apksigner.core.internal.zip.CentralDirectoryRecord;
-import com.android.apksigner.core.internal.zip.LocalFileRecord;
-import com.android.apksigner.core.util.DataSource;
-import com.android.apksigner.core.zip.ZipFormatException;
-
-import sun.security.pkcs.PKCS7;
-import sun.security.pkcs.SignerInfo;
-
-/**
- * APK verifier which uses JAR signing (aka v1 signing scheme).
- *
- * @see <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#Signed_JAR_File">Signed JAR File</a>
- */
-public abstract class V1SchemeVerifier {
-
-    private static final String MANIFEST_ENTRY_NAME = V1SchemeSigner.MANIFEST_ENTRY_NAME;
-
-    private V1SchemeVerifier() {}
-
-    /**
-     * Verifies the provided APK's JAR signatures and returns the result of verification. APK is
-     * considered verified only if {@link Result#verified} is {@code true}. If verification fails,
-     * the result will contain errors -- see {@link Result#getErrors()}.
-     *
-     * @throws ZipFormatException if the APK is malformed
-     * @throws IOException if an I/O error occurs when reading the APK
-     * @throws NoSuchAlgorithmException if the APK's JAR signatures cannot be verified because a
-     *         required cryptographic algorithm implementation is missing
-     */
-    public static Result verify(
-            DataSource apk,
-            ApkUtils.ZipSections apkSections,
-            Map<Integer, String> supportedApkSigSchemeNames,
-            Set<Integer> foundApkSigSchemeIds,
-            int minSdkVersion,
-            int maxSdkVersion) throws IOException, ZipFormatException, NoSuchAlgorithmException {
-        if (minSdkVersion > maxSdkVersion) {
-            throw new IllegalArgumentException(
-                    "minSdkVersion (" + minSdkVersion + ") > maxSdkVersion (" + maxSdkVersion
-                            + ")");
-        }
-
-        Result result = new Result();
-
-        // Parse the ZIP Central Directory and check that there are no entries with duplicate names.
-        List<CentralDirectoryRecord> cdRecords = parseZipCentralDirectory(apk, apkSections);
-        Set<String> cdEntryNames = checkForDuplicateEntries(cdRecords, result);
-        if (result.containsErrors()) {
-            return result;
-        }
-
-        // Verify JAR signature(s).
-        Signers.verify(
-                apk,
-                apkSections.getZipCentralDirectoryOffset(),
-                cdRecords,
-                cdEntryNames,
-                supportedApkSigSchemeNames,
-                foundApkSigSchemeIds,
-                minSdkVersion,
-                maxSdkVersion,
-                result);
-
-        return result;
-    }
-
-    /**
-     * Returns the set of entry names and reports any duplicate entry names in the {@code result}
-     * as errors.
-     */
-    private static Set<String> checkForDuplicateEntries(
-            List<CentralDirectoryRecord> cdRecords, Result result) {
-        Set<String> cdEntryNames = new HashSet<>(cdRecords.size());
-        Set<String> duplicateCdEntryNames = null;
-        for (CentralDirectoryRecord cdRecord : cdRecords) {
-            String entryName = cdRecord.getName();
-            if (!cdEntryNames.add(entryName)) {
-                // This is an error. Report this once per duplicate name.
-                if (duplicateCdEntryNames == null) {
-                    duplicateCdEntryNames = new HashSet<>();
-                }
-                if (duplicateCdEntryNames.add(entryName)) {
-                    result.addError(Issue.JAR_SIG_DUPLICATE_ZIP_ENTRY, entryName);
-                }
-            }
-        }
-        return cdEntryNames;
-    }
-
-    /**
-     * All JAR signers of an APK.
-     */
-    private static class Signers {
-
-        /**
-         * Verifies JAR signatures of the provided APK and populates the provided result container
-         * with errors, warnings, and information about signers. The APK is considered verified if
-         * the {@link Result#verified} is {@code true}.
-         */
-        private static void verify(
-                DataSource apk,
-                long cdStartOffset,
-                List<CentralDirectoryRecord> cdRecords,
-                Set<String> cdEntryNames,
-                Map<Integer, String> supportedApkSigSchemeNames,
-                Set<Integer> foundApkSigSchemeIds,
-                int minSdkVersion,
-                int maxSdkVersion,
-                Result result) throws ZipFormatException, IOException, NoSuchAlgorithmException {
-
-            // Find JAR manifest and signature block files.
-            CentralDirectoryRecord manifestEntry = null;
-            Map<String, CentralDirectoryRecord> sigFileEntries = new HashMap<>(1);
-            List<CentralDirectoryRecord> sigBlockEntries = new ArrayList<>(1);
-            for (CentralDirectoryRecord cdRecord : cdRecords) {
-                String entryName = cdRecord.getName();
-                if (!entryName.startsWith("META-INF/")) {
-                    continue;
-                }
-                if ((manifestEntry == null) && (MANIFEST_ENTRY_NAME.equals(entryName))) {
-                    manifestEntry = cdRecord;
-                    continue;
-                }
-                if (entryName.endsWith(".SF")) {
-                    sigFileEntries.put(entryName, cdRecord);
-                    continue;
-                }
-                if ((entryName.endsWith(".RSA"))
-                        || (entryName.endsWith(".DSA"))
-                        || (entryName.endsWith(".EC"))) {
-                    sigBlockEntries.add(cdRecord);
-                    continue;
-                }
-            }
-            if (manifestEntry == null) {
-                result.addError(Issue.JAR_SIG_NO_MANIFEST);
-                return;
-            }
-
-            // Parse the JAR manifest and check that all JAR entries it references exist in the APK.
-            byte[] manifestBytes =
-                    LocalFileRecord.getUncompressedData(apk, manifestEntry, cdStartOffset);
-            Map<String, ManifestParser.Section> entryNameToManifestSection = null;
-            ManifestParser manifest = new ManifestParser(manifestBytes);
-            ManifestParser.Section manifestMainSection = manifest.readSection();
-            List<ManifestParser.Section> manifestIndividualSections = manifest.readAllSections();
-            entryNameToManifestSection = new HashMap<>(manifestIndividualSections.size());
-            int manifestSectionNumber = 0;
-            for (ManifestParser.Section manifestSection : manifestIndividualSections) {
-                manifestSectionNumber++;
-                String entryName = manifestSection.getName();
-                if (entryName == null) {
-                    result.addError(Issue.JAR_SIG_UNNNAMED_MANIFEST_SECTION, manifestSectionNumber);
-                    continue;
-                }
-                if (entryNameToManifestSection.put(entryName, manifestSection) != null) {
-                    result.addError(Issue.JAR_SIG_DUPLICATE_MANIFEST_SECTION, entryName);
-                    continue;
-                }
-                if (!cdEntryNames.contains(entryName)) {
-                    result.addError(
-                            Issue.JAR_SIG_MISSING_ZIP_ENTRY_REFERENCED_IN_MANIFEST, entryName);
-                    continue;
-                }
-            }
-            if (result.containsErrors()) {
-                return;
-            }
-            // STATE OF AFFAIRS:
-            // * All JAR entries listed in JAR manifest are present in the APK.
-
-            // Identify signers
-            List<Signer> signers = new ArrayList<>(sigBlockEntries.size());
-            for (CentralDirectoryRecord sigBlockEntry : sigBlockEntries) {
-                String sigBlockEntryName = sigBlockEntry.getName();
-                int extensionDelimiterIndex = sigBlockEntryName.lastIndexOf('.');
-                if (extensionDelimiterIndex == -1) {
-                    throw new RuntimeException(
-                            "Signature block file name does not contain extension: "
-                                    + sigBlockEntryName);
-                }
-                String sigFileEntryName =
-                        sigBlockEntryName.substring(0, extensionDelimiterIndex) + ".SF";
-                CentralDirectoryRecord sigFileEntry = sigFileEntries.get(sigFileEntryName);
-                if (sigFileEntry == null) {
-                    result.addWarning(
-                            Issue.JAR_SIG_MISSING_FILE, sigBlockEntryName, sigFileEntryName);
-                    continue;
-                }
-                String signerName = sigBlockEntryName.substring("META-INF/".length());
-                Result.SignerInfo signerInfo =
-                        new Result.SignerInfo(
-                                signerName, sigBlockEntryName, sigFileEntry.getName());
-                Signer signer = new Signer(signerName, sigBlockEntry, sigFileEntry, signerInfo);
-                signers.add(signer);
-            }
-            if (signers.isEmpty()) {
-                result.addError(Issue.JAR_SIG_NO_SIGNATURES);
-                return;
-            }
-
-            // Verify each signer's signature block file .(RSA|DSA|EC) against the corresponding
-            // signature file .SF. Any error encountered for any signer terminates verification, to
-            // mimic Android's behavior.
-            for (Signer signer : signers) {
-                signer.verifySigBlockAgainstSigFile(
-                        apk, cdStartOffset, minSdkVersion, maxSdkVersion);
-                if (signer.getResult().containsErrors()) {
-                    result.signers.add(signer.getResult());
-                }
-            }
-            if (result.containsErrors()) {
-                return;
-            }
-            // STATE OF AFFAIRS:
-            // * All JAR entries listed in JAR manifest are present in the APK.
-            // * All signature files (.SF) verify against corresponding block files (.RSA|.DSA|.EC).
-
-            // Verify each signer's signature file (.SF) against the JAR manifest.
-            List<Signer> remainingSigners = new ArrayList<>(signers.size());
-            for (Signer signer : signers) {
-                signer.verifySigFileAgainstManifest(
-                        manifestBytes,
-                        manifestMainSection,
-                        entryNameToManifestSection,
-                        supportedApkSigSchemeNames,
-                        foundApkSigSchemeIds,
-                        minSdkVersion,
-                        maxSdkVersion);
-                if (signer.isIgnored()) {
-                    result.ignoredSigners.add(signer.getResult());
-                } else {
-                    if (signer.getResult().containsErrors()) {
-                        result.signers.add(signer.getResult());
-                    } else {
-                        remainingSigners.add(signer);
-                    }
-                }
-            }
-            if (result.containsErrors()) {
-                return;
-            }
-            signers = remainingSigners;
-            if (signers.isEmpty()) {
-                result.addError(Issue.JAR_SIG_NO_SIGNATURES);
-                return;
-            }
-            // STATE OF AFFAIRS:
-            // * All signature files (.SF) verify against corresponding block files (.RSA|.DSA|.EC).
-            // * Contents of all JAR manifest sections listed in .SF files verify against .SF files.
-            // * All JAR entries listed in JAR manifest are present in the APK.
-
-            // Verify data of JAR entries against JAR manifest and .SF files. On Android, an APK's
-            // JAR entry is considered signed by signers associated with an .SF file iff the entry
-            // is mentioned in the .SF file and the entry's digest(s) mentioned in the JAR manifest
-            // match theentry's uncompressed data. Android requires that all such JAR entries are
-            // signed by the same set of signers. This set may be smaller than the set of signers
-            // we've identified so far.
-            Set<Signer> apkSigners =
-                    verifyJarEntriesAgainstManifestAndSigners(
-                            apk,
-                            cdStartOffset,
-                            cdRecords,
-                            entryNameToManifestSection,
-                            signers,
-                            minSdkVersion,
-                            maxSdkVersion,
-                            result);
-            if (result.containsErrors()) {
-                return;
-            }
-            // STATE OF AFFAIRS:
-            // * All signature files (.SF) verify against corresponding block files (.RSA|.DSA|.EC).
-            // * Contents of all JAR manifest sections listed in .SF files verify against .SF files.
-            // * All JAR entries listed in JAR manifest are present in the APK.
-            // * All JAR entries present in the APK and supposed to be covered by JAR signature
-            //   (i.e., reside outside of META-INF/) are covered by signatures from the same set
-            //   of signers.
-
-            // Report any JAR entries which aren't covered by signature.
-            Set<String> signatureEntryNames = new HashSet<>(1 + result.signers.size() * 2);
-            signatureEntryNames.add(manifestEntry.getName());
-            for (Signer signer : apkSigners) {
-                signatureEntryNames.add(signer.getSignatureBlockEntryName());
-                signatureEntryNames.add(signer.getSignatureFileEntryName());
-            }
-            for (CentralDirectoryRecord cdRecord : cdRecords) {
-                String entryName = cdRecord.getName();
-                if ((entryName.startsWith("META-INF/"))
-                        && (!entryName.endsWith("/"))
-                        && (!signatureEntryNames.contains(entryName))) {
-                    result.addWarning(Issue.JAR_SIG_UNPROTECTED_ZIP_ENTRY, entryName);
-                }
-            }
-
-            // Reflect the sets of used signers and ignored signers in the result.
-            for (Signer signer : signers) {
-                if (apkSigners.contains(signer)) {
-                    result.signers.add(signer.getResult());
-                } else {
-                    result.ignoredSigners.add(signer.getResult());
-                }
-            }
-
-            result.verified = true;
-        }
-    }
-
-    private static class Signer {
-        private final String mName;
-        private final Result.SignerInfo mResult;
-        private final CentralDirectoryRecord mSignatureFileEntry;
-        private final CentralDirectoryRecord mSignatureBlockEntry;
-        private boolean mIgnored;
-
-        private byte[] mSigFileBytes;
-        private Set<String> mSigFileEntryNames;
-
-        private Signer(
-                String name,
-                CentralDirectoryRecord sigBlockEntry,
-                CentralDirectoryRecord sigFileEntry,
-                Result.SignerInfo result) {
-            mName = name;
-            mResult = result;
-            mSignatureBlockEntry = sigBlockEntry;
-            mSignatureFileEntry = sigFileEntry;
-        }
-
-        public String getName() {
-            return mName;
-        }
-
-        public String getSignatureFileEntryName() {
-            return mSignatureFileEntry.getName();
-        }
-
-        public String getSignatureBlockEntryName() {
-            return mSignatureBlockEntry.getName();
-        }
-
-        void setIgnored() {
-            mIgnored = true;
-        }
-
-        public boolean isIgnored() {
-            return mIgnored;
-        }
-
-        public Set<String> getSigFileEntryNames() {
-            return mSigFileEntryNames;
-        }
-
-        public Result.SignerInfo getResult() {
-            return mResult;
-        }
-
-        @SuppressWarnings("restriction")
-        public void verifySigBlockAgainstSigFile(
-                DataSource apk, long cdStartOffset, int minSdkVersion, int maxSdkVersion)
-                        throws IOException, ZipFormatException, NoSuchAlgorithmException {
-            byte[] sigBlockBytes =
-                    LocalFileRecord.getUncompressedData(apk, mSignatureBlockEntry, cdStartOffset);
-            mSigFileBytes =
-                    LocalFileRecord.getUncompressedData(apk, mSignatureFileEntry, cdStartOffset);
-            PKCS7 sigBlock;
-            try {
-                sigBlock = new PKCS7(sigBlockBytes);
-            } catch (IOException e) {
-                if (e.getCause() instanceof CertificateException) {
-                    mResult.addError(
-                            Issue.JAR_SIG_MALFORMED_CERTIFICATE, mSignatureBlockEntry.getName(), e);
-                } else {
-                    mResult.addError(
-                            Issue.JAR_SIG_PARSE_EXCEPTION, mSignatureBlockEntry.getName(), e);
-                }
-                return;
-            }
-            SignerInfo[] unverifiedSignerInfos = sigBlock.getSignerInfos();
-            if ((unverifiedSignerInfos == null) || (unverifiedSignerInfos.length == 0)) {
-                mResult.addError(Issue.JAR_SIG_NO_SIGNERS, mSignatureBlockEntry.getName());
-                return;
-            }
-
-            SignerInfo verifiedSignerInfo = null;
-            if ((unverifiedSignerInfos != null) && (unverifiedSignerInfos.length > 0)) {
-                for (int i = 0; i < unverifiedSignerInfos.length; i++) {
-                    SignerInfo unverifiedSignerInfo = unverifiedSignerInfos[i];
-                    String digestAlgorithmOid =
-                            unverifiedSignerInfo.getDigestAlgorithmId().getOID().toString();
-                    String signatureAlgorithmOid =
-                            unverifiedSignerInfo
-                                    .getDigestEncryptionAlgorithmId().getOID().toString();
-                    InclusiveIntRange desiredApiLevels =
-                            InclusiveIntRange.fromTo(minSdkVersion, maxSdkVersion);
-                    List<InclusiveIntRange> apiLevelsWhereDigestAndSigAlgorithmSupported =
-                            getSigAlgSupportedApiLevels(digestAlgorithmOid, signatureAlgorithmOid);
-                    List<InclusiveIntRange> apiLevelsWhereDigestAlgorithmNotSupported =
-                            desiredApiLevels.getValuesNotIn(apiLevelsWhereDigestAndSigAlgorithmSupported);
-                    if (!apiLevelsWhereDigestAlgorithmNotSupported.isEmpty()) {
-                        mResult.addError(
-                                Issue.JAR_SIG_UNSUPPORTED_SIG_ALG,
-                                mSignatureBlockEntry.getName(),
-                                digestAlgorithmOid,
-                                signatureAlgorithmOid,
-                                String.valueOf(apiLevelsWhereDigestAlgorithmNotSupported));
-                        return;
-                    }
-                    try {
-                        verifiedSignerInfo = sigBlock.verify(unverifiedSignerInfo, mSigFileBytes);
-                    } catch (SignatureException e) {
-                        mResult.addError(
-                                Issue.JAR_SIG_VERIFY_EXCEPTION,
-                                mSignatureBlockEntry.getName(),
-                                mSignatureFileEntry.getName(),
-                                e);
-                        return;
-                    }
-                    if (verifiedSignerInfo != null) {
-                        // Verified
-                        break;
-                    }
-
-                    // Did not verify
-                    if (minSdkVersion < AndroidSdkVersion.N) {
-                        // Prior to N, Android attempted to verify only the first SignerInfo.
-                        mResult.addError(
-                                Issue.JAR_SIG_DID_NOT_VERIFY,
-                                mSignatureBlockEntry.getName(),
-                                mSignatureFileEntry.getName());
-                        return;
-                    }
-                }
-            }
-            if (verifiedSignerInfo == null) {
-                mResult.addError(Issue.JAR_SIG_NO_SIGNERS, mSignatureBlockEntry.getName());
-                return;
-            }
-
-            List<X509Certificate> certChain;
-            try {
-                certChain = verifiedSignerInfo.getCertificateChain(sigBlock);
-            } catch (IOException e) {
-                throw new RuntimeException(
-                        "Failed to obtain cert chain from " + mSignatureBlockEntry.getName(), e);
-            }
-            if ((certChain == null) || (certChain.isEmpty())) {
-                throw new RuntimeException("Verified SignerInfo does not have a certificate chain");
-            }
-            mResult.certChain.clear();
-            mResult.certChain.addAll(certChain);
-        }
-
-        private static final String OID_DIGEST_MD5 = "1.2.840.113549.2.5";
-        private static final String OID_DIGEST_SHA1 = "1.3.14.3.2.26";
-        private static final String OID_DIGEST_SHA224 = "2.16.840.1.101.3.4.2.4";
-        private static final String OID_DIGEST_SHA256 = "2.16.840.1.101.3.4.2.1";
-        private static final String OID_DIGEST_SHA384 = "2.16.840.1.101.3.4.2.2";
-        private static final String OID_DIGEST_SHA512 = "2.16.840.1.101.3.4.2.3";
-
-        private static final String OID_SIG_RSA = "1.2.840.113549.1.1.1";
-        private static final String OID_SIG_MD5_WITH_RSA = "1.2.840.113549.1.1.4";
-        private static final String OID_SIG_SHA1_WITH_RSA = "1.2.840.113549.1.1.5";
-        private static final String OID_SIG_SHA224_WITH_RSA = "1.2.840.113549.1.1.14";
-        private static final String OID_SIG_SHA256_WITH_RSA = "1.2.840.113549.1.1.11";
-        private static final String OID_SIG_SHA384_WITH_RSA = "1.2.840.113549.1.1.12";
-        private static final String OID_SIG_SHA512_WITH_RSA = "1.2.840.113549.1.1.13";
-
-        private static final String OID_SIG_DSA = "1.2.840.10040.4.1";
-        private static final String OID_SIG_SHA1_WITH_DSA = "1.2.840.10040.4.3";
-        private static final String OID_SIG_SHA224_WITH_DSA = "2.16.840.1.101.3.4.3.1";
-        private static final String OID_SIG_SHA256_WITH_DSA = "2.16.840.1.101.3.4.3.2";
-
-        private static final String OID_SIG_SHA1_WITH_ECDSA = "1.2.840.10045.4.1";
-        private static final String OID_SIG_SHA224_WITH_ECDSA = "1.2.840.10045.4.3.1";
-        private static final String OID_SIG_SHA256_WITH_ECDSA = "1.2.840.10045.4.3.2";
-        private static final String OID_SIG_SHA384_WITH_ECDSA = "1.2.840.10045.4.3.3";
-        private static final String OID_SIG_SHA512_WITH_ECDSA = "1.2.840.10045.4.3.4";
-
-        private static final Map<String, List<InclusiveIntRange>> SUPPORTED_SIG_ALG_OIDS =
-                new HashMap<>();
-        {
-            addSupportedSigAlg(
-                    OID_DIGEST_MD5, OID_SIG_RSA,
-                    InclusiveIntRange.from(0));
-            addSupportedSigAlg(
-                    OID_DIGEST_MD5, OID_SIG_MD5_WITH_RSA,
-                    InclusiveIntRange.fromTo(0, 8), InclusiveIntRange.from(21));
-            addSupportedSigAlg(
-                    OID_DIGEST_MD5, OID_SIG_SHA1_WITH_RSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_MD5, OID_SIG_SHA224_WITH_RSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_MD5, OID_SIG_SHA256_WITH_RSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_MD5, OID_SIG_SHA384_WITH_RSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_MD5, OID_SIG_SHA512_WITH_RSA,
-                    InclusiveIntRange.fromTo(21, 23));
-
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA1, OID_SIG_RSA,
-                    InclusiveIntRange.from(0));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA1, OID_SIG_MD5_WITH_RSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA1, OID_SIG_SHA1_WITH_RSA,
-                    InclusiveIntRange.from(0));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA1, OID_SIG_SHA224_WITH_RSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA1, OID_SIG_SHA256_WITH_RSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA1, OID_SIG_SHA384_WITH_RSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA1, OID_SIG_SHA512_WITH_RSA,
-                    InclusiveIntRange.fromTo(21, 23));
-
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA224, OID_SIG_RSA,
-                    InclusiveIntRange.fromTo(0, 8), InclusiveIntRange.from(21));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA224, OID_SIG_MD5_WITH_RSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA224, OID_SIG_SHA1_WITH_RSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA224, OID_SIG_SHA224_WITH_RSA,
-                    InclusiveIntRange.fromTo(0, 8), InclusiveIntRange.from(21));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA224, OID_SIG_SHA256_WITH_RSA,
-                    InclusiveIntRange.fromTo(21, 21));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA224, OID_SIG_SHA384_WITH_RSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA224, OID_SIG_SHA512_WITH_RSA,
-                    InclusiveIntRange.fromTo(21, 23));
-
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA256, OID_SIG_RSA,
-                    InclusiveIntRange.fromTo(0, 8), InclusiveIntRange.from(18));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA256, OID_SIG_MD5_WITH_RSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA256, OID_SIG_SHA1_WITH_RSA,
-                    InclusiveIntRange.fromTo(21, 21));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA256, OID_SIG_SHA224_WITH_RSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA256, OID_SIG_SHA256_WITH_RSA,
-                    InclusiveIntRange.fromTo(0, 8), InclusiveIntRange.from(18));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA256, OID_SIG_SHA384_WITH_RSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA256, OID_SIG_SHA512_WITH_RSA,
-                    InclusiveIntRange.fromTo(21, 23));
-
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA384, OID_SIG_RSA,
-                    InclusiveIntRange.from(18));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA384, OID_SIG_MD5_WITH_RSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA384, OID_SIG_SHA1_WITH_RSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA384, OID_SIG_SHA224_WITH_RSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA384, OID_SIG_SHA256_WITH_RSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA384, OID_SIG_SHA384_WITH_RSA,
-                    InclusiveIntRange.from(21));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA384, OID_SIG_SHA512_WITH_RSA,
-                    InclusiveIntRange.fromTo(21, 23));
-
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA512, OID_SIG_RSA,
-                    InclusiveIntRange.from(18));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA512, OID_SIG_MD5_WITH_RSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA512, OID_SIG_SHA1_WITH_RSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA512, OID_SIG_SHA224_WITH_RSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA512, OID_SIG_SHA256_WITH_RSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA512, OID_SIG_SHA384_WITH_RSA,
-                    InclusiveIntRange.fromTo(21, 21));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA512, OID_SIG_SHA512_WITH_RSA,
-                    InclusiveIntRange.from(21));
-
-            addSupportedSigAlg(
-                    OID_DIGEST_MD5, OID_SIG_SHA1_WITH_DSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_MD5, OID_SIG_SHA224_WITH_DSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_MD5, OID_SIG_SHA256_WITH_DSA,
-                    InclusiveIntRange.fromTo(21, 23));
-
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA1, OID_SIG_DSA,
-                    InclusiveIntRange.from(0));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA1, OID_SIG_SHA1_WITH_DSA,
-                    InclusiveIntRange.from(9));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA1, OID_SIG_SHA224_WITH_DSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA1, OID_SIG_SHA256_WITH_DSA,
-                    InclusiveIntRange.fromTo(21, 23));
-
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA224, OID_SIG_DSA,
-                    InclusiveIntRange.from(22));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA224, OID_SIG_SHA1_WITH_DSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA224, OID_SIG_SHA224_WITH_DSA,
-                    InclusiveIntRange.from(21));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA224, OID_SIG_SHA256_WITH_DSA,
-                    InclusiveIntRange.fromTo(21, 23));
-
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA256, OID_SIG_DSA,
-                    InclusiveIntRange.from(22));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA256, OID_SIG_SHA1_WITH_DSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA256, OID_SIG_SHA224_WITH_DSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA256, OID_SIG_SHA256_WITH_DSA,
-                    InclusiveIntRange.from(21));
-
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA384, OID_SIG_SHA1_WITH_DSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA384, OID_SIG_SHA224_WITH_DSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA384, OID_SIG_SHA256_WITH_DSA,
-                    InclusiveIntRange.fromTo(21, 23));
-
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA512, OID_SIG_SHA1_WITH_DSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA512, OID_SIG_SHA224_WITH_DSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA512, OID_SIG_SHA256_WITH_DSA,
-                    InclusiveIntRange.fromTo(21, 23));
-
-
-            addSupportedSigAlg(
-                    OID_DIGEST_MD5, OID_SIG_SHA1_WITH_ECDSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_MD5, OID_SIG_SHA224_WITH_ECDSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_MD5, OID_SIG_SHA256_WITH_ECDSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_MD5, OID_SIG_SHA384_WITH_ECDSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_MD5, OID_SIG_SHA512_WITH_ECDSA,
-                    InclusiveIntRange.fromTo(21, 23));
-
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA1, OID_SIG_SHA1_WITH_ECDSA,
-                    InclusiveIntRange.from(18));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA1, OID_SIG_SHA224_WITH_ECDSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA1, OID_SIG_SHA256_WITH_ECDSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA1, OID_SIG_SHA384_WITH_ECDSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA1, OID_SIG_SHA512_WITH_ECDSA,
-                    InclusiveIntRange.fromTo(21, 23));
-
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA224, OID_SIG_SHA1_WITH_ECDSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA224, OID_SIG_SHA224_WITH_ECDSA,
-                    InclusiveIntRange.from(21));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA224, OID_SIG_SHA256_WITH_ECDSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA224, OID_SIG_SHA384_WITH_ECDSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA224, OID_SIG_SHA512_WITH_ECDSA,
-                    InclusiveIntRange.fromTo(21, 23));
-
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA256, OID_SIG_SHA1_WITH_ECDSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA256, OID_SIG_SHA224_WITH_ECDSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA256, OID_SIG_SHA256_WITH_ECDSA,
-                    InclusiveIntRange.from(21));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA256, OID_SIG_SHA384_WITH_ECDSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA256, OID_SIG_SHA512_WITH_ECDSA,
-                    InclusiveIntRange.fromTo(21, 23));
-
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA384, OID_SIG_SHA1_WITH_ECDSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA384, OID_SIG_SHA224_WITH_ECDSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA384, OID_SIG_SHA256_WITH_ECDSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA384, OID_SIG_SHA384_WITH_ECDSA,
-                    InclusiveIntRange.from(21));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA384, OID_SIG_SHA512_WITH_ECDSA,
-                    InclusiveIntRange.fromTo(21, 23));
-
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA512, OID_SIG_SHA1_WITH_ECDSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA512, OID_SIG_SHA224_WITH_ECDSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA512, OID_SIG_SHA256_WITH_ECDSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA512, OID_SIG_SHA384_WITH_ECDSA,
-                    InclusiveIntRange.fromTo(21, 23));
-            addSupportedSigAlg(
-                    OID_DIGEST_SHA512, OID_SIG_SHA512_WITH_ECDSA,
-                    InclusiveIntRange.from(21));
-        }
-
-        private static void addSupportedSigAlg(
-                String digestAlgorithmOid,
-                String signatureAlgorithmOid,
-                InclusiveIntRange... supportedApiLevels) {
-            SUPPORTED_SIG_ALG_OIDS.put(
-                    digestAlgorithmOid + "with" + signatureAlgorithmOid,
-                    Arrays.asList(supportedApiLevels));
-        }
-
-        private List<InclusiveIntRange> getSigAlgSupportedApiLevels(
-                String digestAlgorithmOid,
-                String signatureAlgorithmOid) {
-            List<InclusiveIntRange> result =
-                    SUPPORTED_SIG_ALG_OIDS.get(digestAlgorithmOid + "with" + signatureAlgorithmOid);
-            return (result != null) ? result : Collections.emptyList();
-        }
-
-        public void verifySigFileAgainstManifest(
-                byte[] manifestBytes,
-                ManifestParser.Section manifestMainSection,
-                Map<String, ManifestParser.Section> entryNameToManifestSection,
-                Map<Integer, String> supportedApkSigSchemeNames,
-                Set<Integer> foundApkSigSchemeIds,
-                int minSdkVersion,
-                int maxSdkVersion) throws NoSuchAlgorithmException {
-            // Inspect the main section of the .SF file.
-            ManifestParser sf = new ManifestParser(mSigFileBytes);
-            ManifestParser.Section sfMainSection = sf.readSection();
-            if (sfMainSection.getAttributeValue(Attributes.Name.SIGNATURE_VERSION) == null) {
-                mResult.addError(
-                        Issue.JAR_SIG_MISSING_VERSION_ATTR_IN_SIG_FILE,
-                        mSignatureFileEntry.getName());
-                setIgnored();
-                return;
-            }
-
-            if (maxSdkVersion >= AndroidSdkVersion.N) {
-                // Android N and newer rejects APKs whose .SF file says they were supposed to be
-                // signed with APK Signature Scheme v2 (or newer) and yet no such signature was
-                // found.
-                checkForStrippedApkSignatures(
-                        sfMainSection, supportedApkSigSchemeNames, foundApkSigSchemeIds);
-                if (mResult.containsErrors()) {
-                    return;
-                }
-            }
-
-            boolean createdBySigntool = false;
-            String createdBy = sfMainSection.getAttributeValue("Created-By");
-            if (createdBy != null) {
-                createdBySigntool = createdBy.indexOf("signtool") != -1;
-            }
-            boolean manifestDigestVerified =
-                    verifyManifestDigest(
-                            sfMainSection,
-                            createdBySigntool,
-                            manifestBytes,
-                            minSdkVersion,
-                            maxSdkVersion);
-            if (!createdBySigntool) {
-                verifyManifestMainSectionDigest(
-                        sfMainSection,
-                        manifestMainSection,
-                        manifestBytes,
-                        minSdkVersion,
-                        maxSdkVersion);
-            }
-            if (mResult.containsErrors()) {
-                return;
-            }
-
-            // Inspect per-entry sections of .SF file. Technically, if the digest of JAR manifest
-            // verifies, per-entry sections should be ignored. However, most Android platform
-            // implementations require that such sections exist.
-            List<ManifestParser.Section> sfSections = sf.readAllSections();
-            Set<String> sfEntryNames = new HashSet<>(sfSections.size());
-            int sfSectionNumber = 0;
-            for (ManifestParser.Section sfSection : sfSections) {
-                sfSectionNumber++;
-                String entryName = sfSection.getName();
-                if (entryName == null) {
-                    mResult.addError(
-                            Issue.JAR_SIG_UNNNAMED_SIG_FILE_SECTION,
-                            mSignatureFileEntry.getName(),
-                            sfSectionNumber);
-                    setIgnored();
-                    return;
-                }
-                if (!sfEntryNames.add(entryName)) {
-                    mResult.addError(
-                            Issue.JAR_SIG_DUPLICATE_SIG_FILE_SECTION,
-                            mSignatureFileEntry.getName(),
-                            entryName);
-                    setIgnored();
-                    return;
-                }
-                if (manifestDigestVerified) {
-                    // No need to verify this entry's corresponding JAR manifest entry because the
-                    // JAR manifest verifies in full.
-                    continue;
-                }
-                // Whole-file digest of JAR manifest hasn't been verified. Thus, we need to verify
-                // the digest of the JAR manifest section corresponding to this .SF section.
-                ManifestParser.Section manifestSection = entryNameToManifestSection.get(entryName);
-                if (manifestSection == null) {
-                    mResult.addError(
-                            Issue.JAR_SIG_NO_ZIP_ENTRY_DIGEST_IN_SIG_FILE,
-                            entryName,
-                            mSignatureFileEntry.getName());
-                    setIgnored();
-                    continue;
-                }
-                verifyManifestIndividualSectionDigest(
-                        sfSection,
-                        createdBySigntool,
-                        manifestSection,
-                        manifestBytes,
-                        minSdkVersion,
-                        maxSdkVersion);
-            }
-            mSigFileEntryNames = sfEntryNames;
-        }
-
-
-        /**
-         * Returns {@code true} if the whole-file digest of the manifest against the main section of
-         * the .SF file.
-         */
-        private boolean verifyManifestDigest(
-                ManifestParser.Section sfMainSection,
-                boolean createdBySigntool,
-                byte[] manifestBytes,
-                int minSdkVersion,
-                int maxSdkVersion) throws NoSuchAlgorithmException {
-            Collection<NamedDigest> expectedDigests =
-                    getDigestsToVerify(
-                            sfMainSection,
-                            ((createdBySigntool) ? "-Digest" : "-Digest-Manifest"),
-                            minSdkVersion,
-                            maxSdkVersion);
-            boolean digestFound = !expectedDigests.isEmpty();
-            if (!digestFound) {
-                mResult.addWarning(
-                        Issue.JAR_SIG_NO_MANIFEST_DIGEST_IN_SIG_FILE,
-                        mSignatureFileEntry.getName());
-                return false;
-            }
-
-            boolean verified = true;
-            for (NamedDigest expectedDigest : expectedDigests) {
-                String jcaDigestAlgorithm = expectedDigest.jcaDigestAlgorithm;
-                byte[] actual = digest(jcaDigestAlgorithm, manifestBytes);
-                byte[] expected = expectedDigest.digest;
-                if (!Arrays.equals(expected, actual)) {
-                    mResult.addWarning(
-                            Issue.JAR_SIG_ZIP_ENTRY_DIGEST_DID_NOT_VERIFY,
-                            V1SchemeSigner.MANIFEST_ENTRY_NAME,
-                            jcaDigestAlgorithm,
-                            mSignatureFileEntry.getName(),
-                            Base64.getEncoder().encodeToString(actual),
-                            Base64.getEncoder().encodeToString(expected));
-                    verified = false;
-                }
-            }
-            return verified;
-        }
-
-        /**
-         * Verifies the digest of the manifest's main section against the main section of the .SF
-         * file.
-         */
-        private void verifyManifestMainSectionDigest(
-                ManifestParser.Section sfMainSection,
-                ManifestParser.Section manifestMainSection,
-                byte[] manifestBytes,
-                int minSdkVersion,
-                int maxSdkVersion) throws NoSuchAlgorithmException {
-            Collection<NamedDigest> expectedDigests =
-                    getDigestsToVerify(
-                            sfMainSection,
-                            "-Digest-Manifest-Main-Attributes",
-                            minSdkVersion,
-                            maxSdkVersion);
-            if (expectedDigests.isEmpty()) {
-                return;
-            }
-
-            for (NamedDigest expectedDigest : expectedDigests) {
-                String jcaDigestAlgorithm = expectedDigest.jcaDigestAlgorithm;
-                byte[] actual =
-                        digest(
-                                jcaDigestAlgorithm,
-                                manifestBytes,
-                                manifestMainSection.getStartOffset(),
-                                manifestMainSection.getSizeBytes());
-                byte[] expected = expectedDigest.digest;
-                if (!Arrays.equals(expected, actual)) {
-                    mResult.addError(
-                            Issue.JAR_SIG_MANIFEST_MAIN_SECTION_DIGEST_DID_NOT_VERIFY,
-                            jcaDigestAlgorithm,
-                            mSignatureFileEntry.getName(),
-                            Base64.getEncoder().encodeToString(actual),
-                            Base64.getEncoder().encodeToString(expected));
-                }
-            }
-        }
-
-        /**
-         * Verifies the digest of the manifest's individual section against the corresponding
-         * individual section of the .SF file.
-         */
-        private void verifyManifestIndividualSectionDigest(
-                ManifestParser.Section sfIndividualSection,
-                boolean createdBySigntool,
-                ManifestParser.Section manifestIndividualSection,
-                byte[] manifestBytes,
-                int minSdkVersion,
-                int maxSdkVersion) throws NoSuchAlgorithmException {
-            String entryName = sfIndividualSection.getName();
-            Collection<NamedDigest> expectedDigests =
-                    getDigestsToVerify(
-                            sfIndividualSection, "-Digest", minSdkVersion, maxSdkVersion);
-            if (expectedDigests.isEmpty()) {
-                mResult.addError(
-                        Issue.JAR_SIG_NO_ZIP_ENTRY_DIGEST_IN_SIG_FILE,
-                        entryName,
-                        mSignatureFileEntry.getName());
-                return;
-            }
-
-            int sectionStartIndex = manifestIndividualSection.getStartOffset();
-            int sectionSizeBytes = manifestIndividualSection.getSizeBytes();
-            if (createdBySigntool) {
-                int sectionEndIndex = sectionStartIndex + sectionSizeBytes;
-                if ((manifestBytes[sectionEndIndex - 1] == '\n')
-                        && (manifestBytes[sectionEndIndex - 2] == '\n')) {
-                    sectionSizeBytes--;
-                }
-            }
-            for (NamedDigest expectedDigest : expectedDigests) {
-                String jcaDigestAlgorithm = expectedDigest.jcaDigestAlgorithm;
-                byte[] actual =
-                        digest(
-                                jcaDigestAlgorithm,
-                                manifestBytes,
-                                sectionStartIndex,
-                                sectionSizeBytes);
-                byte[] expected = expectedDigest.digest;
-                if (!Arrays.equals(expected, actual)) {
-                    mResult.addError(
-                            Issue.JAR_SIG_MANIFEST_SECTION_DIGEST_DID_NOT_VERIFY,
-                            entryName,
-                            jcaDigestAlgorithm,
-                            mSignatureFileEntry.getName(),
-                            Base64.getEncoder().encodeToString(actual),
-                            Base64.getEncoder().encodeToString(expected));
-                }
-            }
-        }
-
-        private void checkForStrippedApkSignatures(
-                ManifestParser.Section sfMainSection,
-                Map<Integer, String> supportedApkSigSchemeNames,
-                Set<Integer> foundApkSigSchemeIds) {
-            String signedWithApkSchemes =
-                    sfMainSection.getAttributeValue(
-                            V1SchemeSigner.SF_ATTRIBUTE_NAME_ANDROID_APK_SIGNED_NAME_STR);
-            // This field contains a comma-separated list of APK signature scheme IDs which were
-            // used to sign this APK. Android rejects APKs where an ID is known to the platform but
-            // the APK didn't verify using that scheme.
-
-            if (signedWithApkSchemes == null) {
-                // APK signature (e.g., v2 scheme) stripping protections not enabled.
-                if (!foundApkSigSchemeIds.isEmpty()) {
-                    // APK is signed with an APK signature scheme such as v2 scheme.
-                    mResult.addWarning(
-                            Issue.JAR_SIG_NO_APK_SIG_STRIP_PROTECTION,
-                            mSignatureFileEntry.getName());
-                }
-                return;
-            }
-
-            if (supportedApkSigSchemeNames.isEmpty()) {
-                return;
-            }
-
-            Set<Integer> supportedApkSigSchemeIds = supportedApkSigSchemeNames.keySet();
-            Set<Integer> supportedExpectedApkSigSchemeIds = new HashSet<>(1);
-            StringTokenizer tokenizer = new StringTokenizer(signedWithApkSchemes, ",");
-            while (tokenizer.hasMoreTokens()) {
-                String idText = tokenizer.nextToken().trim();
-                if (idText.isEmpty()) {
-                    continue;
-                }
-                int id;
-                try {
-                    id = Integer.parseInt(idText);
-                } catch (Exception ignored) {
-                    continue;
-                }
-                // This APK was supposed to be signed with the APK signature scheme having
-                // this ID.
-                if (supportedApkSigSchemeIds.contains(id)) {
-                    supportedExpectedApkSigSchemeIds.add(id);
-                } else {
-                    mResult.addWarning(
-                            Issue.JAR_SIG_UNKNOWN_APK_SIG_SCHEME_ID,
-                            mSignatureFileEntry.getName(),
-                            id);
-                }
-            }
-
-            for (int id : supportedExpectedApkSigSchemeIds) {
-                if (!foundApkSigSchemeIds.contains(id)) {
-                    String apkSigSchemeName = supportedApkSigSchemeNames.get(id);
-                    mResult.addError(
-                            Issue.JAR_SIG_MISSING_APK_SIG_REFERENCED,
-                            mSignatureFileEntry.getName(),
-                            id,
-                            apkSigSchemeName);
-                }
-            }
-        }
-    }
-
-    private static Collection<NamedDigest> getDigestsToVerify(
-            ManifestParser.Section section,
-            String digestAttrSuffix,
-            int minSdkVersion,
-            int maxSdkVersion) {
-        Decoder base64Decoder = Base64.getDecoder();
-        List<NamedDigest> result = new ArrayList<>(1);
-        if (minSdkVersion < AndroidSdkVersion.JELLY_BEAN_MR2) {
-            // Prior to JB MR2, Android platform's logic for picking a digest algorithm to verify is
-            // to rely on the ancient Digest-Algorithms attribute which contains
-            // whitespace-separated list of digest algorithms (defaulting to SHA-1) to try. The
-            // first digest attribute (with supported digest algorithm) found using the list is
-            // used.
-            String algs = section.getAttributeValue("Digest-Algorithms");
-            if (algs == null) {
-                algs = "SHA SHA1";
-            }
-            StringTokenizer tokens = new StringTokenizer(algs);
-            while (tokens.hasMoreTokens()) {
-                String alg = tokens.nextToken();
-                String attrName = alg + digestAttrSuffix;
-                String digestBase64 = section.getAttributeValue(attrName);
-                if (digestBase64 == null) {
-                    // Attribute not found
-                    continue;
-                }
-                alg = getCanonicalJcaMessageDigestAlgorithm(alg);
-                if ((alg == null)
-                        || (getMinSdkVersionFromWhichSupportedInManifestOrSignatureFile(alg)
-                                > minSdkVersion)) {
-                    // Unsupported digest algorithm
-                    continue;
-                }
-                // Supported digest algorithm
-                result.add(new NamedDigest(alg, base64Decoder.decode(digestBase64)));
-                break;
-            }
-            // No supported digests found -- this will fail to verify on pre-JB MR2 Androids.
-            if (result.isEmpty()) {
-                return result;
-            }
-        }
-
-        if (maxSdkVersion >= AndroidSdkVersion.JELLY_BEAN_MR2) {
-            // On JB MR2 and newer, Android platform picks the strongest algorithm out of:
-            // SHA-512, SHA-384, SHA-256, SHA-1.
-            for (String alg : JB_MR2_AND_NEWER_DIGEST_ALGS) {
-                String attrName = getJarDigestAttributeName(alg, digestAttrSuffix);
-                String digestBase64 = section.getAttributeValue(attrName);
-                if (digestBase64 == null) {
-                    // Attribute not found
-                    continue;
-                }
-                byte[] digest = base64Decoder.decode(digestBase64);
-                byte[] digestInResult = getDigest(result, alg);
-                if ((digestInResult == null) || (!Arrays.equals(digestInResult, digest))) {
-                    result.add(new NamedDigest(alg, digest));
-                }
-                break;
-            }
-        }
-
-        return result;
-    }
-
-    private static String[] JB_MR2_AND_NEWER_DIGEST_ALGS = {
-            "SHA-512",
-            "SHA-384",
-            "SHA-256",
-            "SHA-1",
-    };
-
-    private static String getCanonicalJcaMessageDigestAlgorithm(String algorithm) {
-        return UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL.get(algorithm.toUpperCase(Locale.US));
-    }
-
-    public static int getMinSdkVersionFromWhichSupportedInManifestOrSignatureFile(
-            String jcaAlgorithmName) {
-        Integer result =
-                MIN_SDK_VESION_FROM_WHICH_DIGEST_SUPPORTED_IN_MANIFEST.get(
-                        jcaAlgorithmName.toUpperCase(Locale.US));
-        return (result != null) ? result : Integer.MAX_VALUE;
-    }
-
-    private static String getJarDigestAttributeName(
-            String jcaDigestAlgorithm, String attrNameSuffix) {
-        if ("SHA-1".equalsIgnoreCase(jcaDigestAlgorithm)) {
-            return "SHA1" + attrNameSuffix;
-        } else {
-            return jcaDigestAlgorithm + attrNameSuffix;
-        }
-    }
-
-    private static Map<String, String> UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL;
-    static {
-        UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL = new HashMap<>(8);
-        UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL.put("MD5", "MD5");
-        UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL.put("SHA", "SHA-1");
-        UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL.put("SHA1", "SHA-1");
-        UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL.put("SHA-1", "SHA-1");
-        UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL.put("SHA-256", "SHA-256");
-        UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL.put("SHA-384", "SHA-384");
-        UPPER_CASE_JCA_DIGEST_ALG_TO_CANONICAL.put("SHA-512", "SHA-512");
-    }
-
-    private static Map<String, Integer> MIN_SDK_VESION_FROM_WHICH_DIGEST_SUPPORTED_IN_MANIFEST;
-    static {
-        MIN_SDK_VESION_FROM_WHICH_DIGEST_SUPPORTED_IN_MANIFEST = new HashMap<>(5);
-        MIN_SDK_VESION_FROM_WHICH_DIGEST_SUPPORTED_IN_MANIFEST.put("MD5", 0);
-        MIN_SDK_VESION_FROM_WHICH_DIGEST_SUPPORTED_IN_MANIFEST.put("SHA-1", 0);
-        MIN_SDK_VESION_FROM_WHICH_DIGEST_SUPPORTED_IN_MANIFEST.put("SHA-256", 0);
-        MIN_SDK_VESION_FROM_WHICH_DIGEST_SUPPORTED_IN_MANIFEST.put(
-                "SHA-384", AndroidSdkVersion.GINGERBREAD);
-        MIN_SDK_VESION_FROM_WHICH_DIGEST_SUPPORTED_IN_MANIFEST.put(
-                "SHA-512", AndroidSdkVersion.GINGERBREAD);
-    }
-
-    private static byte[] getDigest(Collection<NamedDigest> digests, String jcaDigestAlgorithm) {
-        for (NamedDigest digest : digests) {
-            if (digest.jcaDigestAlgorithm.equalsIgnoreCase(jcaDigestAlgorithm)) {
-                return digest.digest;
-            }
-        }
-        return null;
-    }
-
-    private static List<CentralDirectoryRecord> parseZipCentralDirectory(
-            DataSource apk,
-            ApkUtils.ZipSections apkSections)
-                    throws IOException, ZipFormatException {
-        // Read the ZIP Central Directory
-        long cdSizeBytes = apkSections.getZipCentralDirectorySizeBytes();
-        if (cdSizeBytes > Integer.MAX_VALUE) {
-            throw new ZipFormatException("ZIP Central Directory too large: " + cdSizeBytes);
-        }
-        long cdOffset = apkSections.getZipCentralDirectoryOffset();
-        ByteBuffer cd = apk.getByteBuffer(cdOffset, (int) cdSizeBytes);
-        cd.order(ByteOrder.LITTLE_ENDIAN);
-
-        // Parse the ZIP Central Directory
-        int expectedCdRecordCount = apkSections.getZipCentralDirectoryRecordCount();
-        List<CentralDirectoryRecord> cdRecords = new ArrayList<>(expectedCdRecordCount);
-        for (int i = 0; i < expectedCdRecordCount; i++) {
-            CentralDirectoryRecord cdRecord;
-            int offsetInsideCd = cd.position();
-            try {
-                cdRecord = CentralDirectoryRecord.getRecord(cd);
-            } catch (ZipFormatException e) {
-                throw new ZipFormatException(
-                        "Failed to parse Central Directory record #" + (i + 1)
-                                + " at file offset " + (cdOffset + offsetInsideCd),
-                        e);
-            }
-            String entryName = cdRecord.getName();
-            if (entryName.endsWith("/")) {
-                // Ignore directory entries
-                continue;
-            }
-            cdRecords.add(cdRecord);
-        }
-        // There may be more data in Central Directory, but we don't warn or throw because Android
-        // ignores unused CD data.
-
-        return cdRecords;
-    }
-
-    /**
-     * Returns {@code true} if the provided JAR entry must be mentioned in signed JAR archive's
-     * manifest for the APK to verify on Android.
-     */
-    private static boolean isJarEntryDigestNeededInManifest(String entryName) {
-        // NOTE: This logic is different from what's required by the JAR signing scheme. This is
-        // because Android's APK verification logic differs from that spec. In particular, JAR
-        // signing spec includes into JAR manifest all files in subdirectories of META-INF and
-        // any files inside META-INF not related to signatures.
-        if (entryName.startsWith("META-INF/")) {
-            return false;
-        }
-        return !entryName.endsWith("/");
-    }
-
-    private static Set<Signer> verifyJarEntriesAgainstManifestAndSigners(
-            DataSource apk,
-            long cdOffsetInApk,
-            Collection<CentralDirectoryRecord> cdRecords,
-            Map<String, ManifestParser.Section> entryNameToManifestSection,
-            List<Signer> signers,
-            int minSdkVersion,
-            int maxSdkVersion,
-            Result result) throws ZipFormatException, IOException, NoSuchAlgorithmException {
-        // Iterate over APK contents as sequentially as possible to improve performance.
-        List<CentralDirectoryRecord> cdRecordsSortedByLocalFileHeaderOffset =
-                new ArrayList<>(cdRecords);
-        Collections.sort(
-                cdRecordsSortedByLocalFileHeaderOffset,
-                CentralDirectoryRecord.BY_LOCAL_FILE_HEADER_OFFSET_COMPARATOR);
-        Set<String> manifestEntryNamesMissingFromApk =
-                new HashSet<>(entryNameToManifestSection.keySet());
-        List<Signer> firstSignedEntrySigners = null;
-        String firstSignedEntryName = null;
-        for (CentralDirectoryRecord cdRecord : cdRecordsSortedByLocalFileHeaderOffset) {
-            String entryName = cdRecord.getName();
-            manifestEntryNamesMissingFromApk.remove(entryName);
-            if (!isJarEntryDigestNeededInManifest(entryName)) {
-                continue;
-            }
-
-            ManifestParser.Section manifestSection = entryNameToManifestSection.get(entryName);
-            if (manifestSection == null) {
-                result.addError(Issue.JAR_SIG_NO_ZIP_ENTRY_DIGEST_IN_MANIFEST, entryName);
-                continue;
-            }
-
-            List<Signer> entrySigners = new ArrayList<>(signers.size());
-            for (Signer signer : signers) {
-                if (signer.getSigFileEntryNames().contains(entryName)) {
-                    entrySigners.add(signer);
-                }
-            }
-            if (entrySigners.isEmpty()) {
-                result.addError(Issue.JAR_SIG_ZIP_ENTRY_NOT_SIGNED, entryName);
-                continue;
-            }
-            if (firstSignedEntrySigners == null) {
-                firstSignedEntrySigners = entrySigners;
-                firstSignedEntryName = entryName;
-            } else if (!entrySigners.equals(firstSignedEntrySigners)) {
-                result.addError(
-                        Issue.JAR_SIG_ZIP_ENTRY_SIGNERS_MISMATCH,
-                        firstSignedEntryName,
-                        getSignerNames(firstSignedEntrySigners),
-                        entryName,
-                        getSignerNames(entrySigners));
-                continue;
-            }
-
-            Collection<NamedDigest> expectedDigests =
-                    getDigestsToVerify(manifestSection, "-Digest", minSdkVersion, maxSdkVersion);
-            if (expectedDigests.isEmpty()) {
-                result.addError(Issue.JAR_SIG_NO_ZIP_ENTRY_DIGEST_IN_MANIFEST, entryName);
-                continue;
-            }
-
-            MessageDigest[] mds = new MessageDigest[expectedDigests.size()];
-            int mdIndex = 0;
-            for (NamedDigest expectedDigest : expectedDigests) {
-                mds[mdIndex] = getMessageDigest(expectedDigest.jcaDigestAlgorithm);
-                mdIndex++;
-            }
-
-            try {
-                LocalFileRecord.outputUncompressedData(
-                        apk,
-                        cdRecord,
-                        cdOffsetInApk,
-                        new MessageDigestSink(mds));
-            } catch (ZipFormatException e) {
-                throw new ZipFormatException("Malformed entry: " + entryName, e);
-            } catch (IOException e) {
-                throw new IOException("Failed to read entry: " + entryName, e);
-            }
-
-            mdIndex = 0;
-            for (NamedDigest expectedDigest : expectedDigests) {
-                byte[] actualDigest = mds[mdIndex].digest();
-                if (!Arrays.equals(expectedDigest.digest, actualDigest)) {
-                    result.addError(
-                            Issue.JAR_SIG_ZIP_ENTRY_DIGEST_DID_NOT_VERIFY,
-                            entryName,
-                            expectedDigest.jcaDigestAlgorithm,
-                            V1SchemeSigner.MANIFEST_ENTRY_NAME,
-                            Base64.getEncoder().encodeToString(actualDigest),
-                            Base64.getEncoder().encodeToString(expectedDigest.digest));
-                }
-            }
-        }
-
-        if (firstSignedEntrySigners == null) {
-            result.addError(Issue.JAR_SIG_NO_SIGNED_ZIP_ENTRIES);
-            return Collections.emptySet();
-        } else {
-            return new HashSet<>(firstSignedEntrySigners);
-        }
-    }
-
-    private static List<String> getSignerNames(List<Signer> signers) {
-        if (signers.isEmpty()) {
-            return Collections.emptyList();
-        }
-        List<String> result = new ArrayList<>(signers.size());
-        for (Signer signer : signers) {
-            result.add(signer.getName());
-        }
-        return result;
-    }
-
-    private static MessageDigest getMessageDigest(String algorithm)
-            throws NoSuchAlgorithmException {
-        return MessageDigest.getInstance(algorithm);
-    }
-
-    private static byte[] digest(String algorithm, byte[] data, int offset, int length)
-            throws NoSuchAlgorithmException {
-        MessageDigest md = getMessageDigest(algorithm);
-        md.update(data, offset, length);
-        return md.digest();
-    }
-
-    private static byte[] digest(String algorithm, byte[] data) throws NoSuchAlgorithmException {
-        return getMessageDigest(algorithm).digest(data);
-    }
-
-    private static class NamedDigest {
-        private final String jcaDigestAlgorithm;
-        private final byte[] digest;
-
-        private NamedDigest(String jcaDigestAlgorithm, byte[] digest) {
-            this.jcaDigestAlgorithm = jcaDigestAlgorithm;
-            this.digest = digest;
-        }
-    }
-
-    public static class Result {
-
-        /** Whether the APK's JAR signature verifies. */
-        public boolean verified;
-
-        /** List of APK's signers. These signers are used by Android. */
-        public final List<SignerInfo> signers = new ArrayList<>();
-
-        /**
-         * Signers encountered in the APK but not included in the set of the APK's signers. These
-         * signers are ignored by Android.
-         */
-        public final List<SignerInfo> ignoredSigners = new ArrayList<>();
-
-        private final List<IssueWithParams> mWarnings = new ArrayList<>();
-        private final List<IssueWithParams> mErrors = new ArrayList<>();
-
-        private boolean containsErrors() {
-            if (!mErrors.isEmpty()) {
-                return true;
-            }
-            for (SignerInfo signer : signers) {
-                if (signer.containsErrors()) {
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        private void addError(Issue msg, Object... parameters) {
-            mErrors.add(new IssueWithParams(msg, parameters));
-        }
-
-        private void addWarning(Issue msg, Object... parameters) {
-            mWarnings.add(new IssueWithParams(msg, parameters));
-        }
-
-        public List<IssueWithParams> getErrors() {
-            return mErrors;
-        }
-
-        public List<IssueWithParams> getWarnings() {
-            return mWarnings;
-        }
-
-        public static class SignerInfo {
-            public final String name;
-            public final String signatureFileName;
-            public final String signatureBlockFileName;
-            public final List<X509Certificate> certChain = new ArrayList<>();
-
-            private final List<IssueWithParams> mWarnings = new ArrayList<>();
-            private final List<IssueWithParams> mErrors = new ArrayList<>();
-
-            private SignerInfo(
-                    String name, String signatureBlockFileName, String signatureFileName) {
-                this.name = name;
-                this.signatureBlockFileName = signatureBlockFileName;
-                this.signatureFileName = signatureFileName;
-            }
-
-            private boolean containsErrors() {
-                return !mErrors.isEmpty();
-            }
-
-            private void addError(Issue msg, Object... parameters) {
-                mErrors.add(new IssueWithParams(msg, parameters));
-            }
-
-            private void addWarning(Issue msg, Object... parameters) {
-                mWarnings.add(new IssueWithParams(msg, parameters));
-            }
-
-            public List<IssueWithParams> getErrors() {
-                return mErrors;
-            }
-
-            public List<IssueWithParams> getWarnings() {
-                return mWarnings;
-            }
-        }
-    }
-}
diff --git a/tools/apksigner/core/src/com/android/apksigner/core/internal/apk/v2/ContentDigestAlgorithm.java b/tools/apksigner/core/src/com/android/apksigner/core/internal/apk/v2/ContentDigestAlgorithm.java
deleted file mode 100644
index 7c136be..0000000
--- a/tools/apksigner/core/src/com/android/apksigner/core/internal/apk/v2/ContentDigestAlgorithm.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.apksigner.core.internal.apk.v2;
-
-/**
- * APK Signature Scheme v2 content digest algorithm.
- */
-public enum ContentDigestAlgorithm {
-    /** SHA2-256 over 1 MB chunks. */
-    CHUNKED_SHA256("SHA-256", 256 / 8),
-
-    /** SHA2-512 over 1 MB chunks. */
-    CHUNKED_SHA512("SHA-512", 512 / 8);
-
-    private final String mJcaMessageDigestAlgorithm;
-    private final int mChunkDigestOutputSizeBytes;
-
-    private ContentDigestAlgorithm(
-            String jcaMessageDigestAlgorithm, int chunkDigestOutputSizeBytes) {
-        mJcaMessageDigestAlgorithm = jcaMessageDigestAlgorithm;
-        mChunkDigestOutputSizeBytes = chunkDigestOutputSizeBytes;
-    }
-
-    /**
-     * Returns the {@link java.security.MessageDigest} algorithm used for computing digests of
-     * chunks by this content digest algorithm.
-     */
-    String getJcaMessageDigestAlgorithm() {
-        return mJcaMessageDigestAlgorithm;
-    }
-
-    /**
-     * Returns the size (in bytes) of the digest of a chunk of content.
-     */
-    int getChunkDigestOutputSizeBytes() {
-        return mChunkDigestOutputSizeBytes;
-    }
-}
\ No newline at end of file
diff --git a/tools/apksigner/core/src/com/android/apksigner/core/internal/apk/v2/SignatureAlgorithm.java b/tools/apksigner/core/src/com/android/apksigner/core/internal/apk/v2/SignatureAlgorithm.java
deleted file mode 100644
index 20f890d..0000000
--- a/tools/apksigner/core/src/com/android/apksigner/core/internal/apk/v2/SignatureAlgorithm.java
+++ /dev/null
@@ -1,142 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.apksigner.core.internal.apk.v2;
-
-import com.android.apksigner.core.internal.util.Pair;
-
-import java.security.spec.AlgorithmParameterSpec;
-import java.security.spec.MGF1ParameterSpec;
-import java.security.spec.PSSParameterSpec;
-
-/**
- * APK Signature Scheme v2 signature algorithm.
- */
-public enum SignatureAlgorithm {
-    /**
-     * RSASSA-PSS with SHA2-256 digest, SHA2-256 MGF1, 32 bytes of salt, trailer: 0xbc, content
-     * digested using SHA2-256 in 1 MB chunks.
-     */
-    RSA_PSS_WITH_SHA256(
-            0x0101,
-            ContentDigestAlgorithm.CHUNKED_SHA256,
-            "RSA",
-            Pair.of("SHA256withRSA/PSS",
-                    new PSSParameterSpec(
-                            "SHA-256", "MGF1", MGF1ParameterSpec.SHA256, 256 / 8, 1))),
-
-    /**
-     * RSASSA-PSS with SHA2-512 digest, SHA2-512 MGF1, 64 bytes of salt, trailer: 0xbc, content
-     * digested using SHA2-512 in 1 MB chunks.
-     */
-    RSA_PSS_WITH_SHA512(
-            0x0102,
-            ContentDigestAlgorithm.CHUNKED_SHA512,
-            "RSA",
-            Pair.of(
-                    "SHA512withRSA/PSS",
-                    new PSSParameterSpec(
-                            "SHA-512", "MGF1", MGF1ParameterSpec.SHA512, 512 / 8, 1))),
-
-    /** RSASSA-PKCS1-v1_5 with SHA2-256 digest, content digested using SHA2-256 in 1 MB chunks. */
-    RSA_PKCS1_V1_5_WITH_SHA256(
-            0x0103,
-            ContentDigestAlgorithm.CHUNKED_SHA256,
-            "RSA",
-            Pair.of("SHA256withRSA", null)),
-
-    /** RSASSA-PKCS1-v1_5 with SHA2-512 digest, content digested using SHA2-512 in 1 MB chunks. */
-    RSA_PKCS1_V1_5_WITH_SHA512(
-            0x0104,
-            ContentDigestAlgorithm.CHUNKED_SHA512,
-            "RSA",
-            Pair.of("SHA512withRSA", null)),
-
-    /** ECDSA with SHA2-256 digest, content digested using SHA2-256 in 1 MB chunks. */
-    ECDSA_WITH_SHA256(
-            0x0201,
-            ContentDigestAlgorithm.CHUNKED_SHA256,
-            "EC",
-            Pair.of("SHA256withECDSA", null)),
-
-    /** ECDSA with SHA2-512 digest, content digested using SHA2-512 in 1 MB chunks. */
-    ECDSA_WITH_SHA512(
-            0x0202,
-            ContentDigestAlgorithm.CHUNKED_SHA512,
-            "EC",
-            Pair.of("SHA512withECDSA", null)),
-
-    /** DSA with SHA2-256 digest, content digested using SHA2-256 in 1 MB chunks. */
-    DSA_WITH_SHA256(
-            0x0301,
-            ContentDigestAlgorithm.CHUNKED_SHA256,
-            "DSA",
-            Pair.of("SHA256withDSA", null));
-
-    private final int mId;
-    private final String mJcaKeyAlgorithm;
-    private final ContentDigestAlgorithm mContentDigestAlgorithm;
-    private final Pair<String, ? extends AlgorithmParameterSpec> mJcaSignatureAlgAndParams;
-
-    private SignatureAlgorithm(int id,
-            ContentDigestAlgorithm contentDigestAlgorithm,
-            String jcaKeyAlgorithm,
-            Pair<String, ? extends AlgorithmParameterSpec> jcaSignatureAlgAndParams) {
-        mId = id;
-        mContentDigestAlgorithm = contentDigestAlgorithm;
-        mJcaKeyAlgorithm = jcaKeyAlgorithm;
-        mJcaSignatureAlgAndParams = jcaSignatureAlgAndParams;
-    }
-
-    /**
-     * Returns the ID of this signature algorithm as used in APK Signature Scheme v2 wire format.
-     */
-    int getId() {
-        return mId;
-    }
-
-    /**
-     * Returns the content digest algorithm associated with this signature algorithm.
-     */
-    ContentDigestAlgorithm getContentDigestAlgorithm() {
-        return mContentDigestAlgorithm;
-    }
-
-    /**
-     * Returns the JCA {@link java.security.Key} algorithm used by this signature scheme.
-     */
-    String getJcaKeyAlgorithm() {
-        return mJcaKeyAlgorithm;
-    }
-
-    /**
-     * Returns the {@link java.security.Signature} algorithm and the {@link AlgorithmParameterSpec}
-     * (or null if not needed) to parameterize the {@code Signature}.
-     */
-    Pair<String, ? extends AlgorithmParameterSpec> getJcaSignatureAlgorithmAndParams() {
-        return mJcaSignatureAlgAndParams;
-    }
-
-    static SignatureAlgorithm findById(int id) {
-        for (SignatureAlgorithm alg : SignatureAlgorithm.values()) {
-            if (alg.getId() == id) {
-                return alg;
-            }
-        }
-
-        return null;
-    }
-}
diff --git a/tools/apksigner/core/src/com/android/apksigner/core/internal/apk/v2/V2SchemeSigner.java b/tools/apksigner/core/src/com/android/apksigner/core/internal/apk/v2/V2SchemeSigner.java
deleted file mode 100644
index 06d31dd..0000000
--- a/tools/apksigner/core/src/com/android/apksigner/core/internal/apk/v2/V2SchemeSigner.java
+++ /dev/null
@@ -1,600 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.apksigner.core.internal.apk.v2;
-
-import com.android.apksigner.core.internal.util.MessageDigestSink;
-import com.android.apksigner.core.internal.util.Pair;
-import com.android.apksigner.core.internal.zip.ZipUtils;
-import com.android.apksigner.core.util.DataSource;
-import com.android.apksigner.core.util.DataSources;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.security.DigestException;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.KeyFactory;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.Signature;
-import java.security.SignatureException;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.X509Certificate;
-import java.security.interfaces.ECKey;
-import java.security.interfaces.RSAKey;
-import java.security.spec.AlgorithmParameterSpec;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.X509EncodedKeySpec;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * APK Signature Scheme v2 signer.
- *
- * <p>APK Signature Scheme v2 is a whole-file signature scheme which aims to protect every single
- * bit of the APK, as opposed to the JAR Signature Scheme which protects only the names and
- * uncompressed contents of ZIP entries.
- *
- * <p>TODO: Link to APK Signature Scheme v2 documentation once it's available.
- */
-public abstract class V2SchemeSigner {
-    /*
-     * The two main goals of APK Signature Scheme v2 are:
-     * 1. Detect any unauthorized modifications to the APK. This is achieved by making the signature
-     *    cover every byte of the APK being signed.
-     * 2. Enable much faster signature and integrity verification. This is achieved by requiring
-     *    only a minimal amount of APK parsing before the signature is verified, thus completely
-     *    bypassing ZIP entry decompression and by making integrity verification parallelizable by
-     *    employing a hash tree.
-     *
-     * The generated signature block is wrapped into an APK Signing Block and inserted into the
-     * original APK immediately before the start of ZIP Central Directory. This is to ensure that
-     * JAR and ZIP parsers continue to work on the signed APK. The APK Signing Block is designed for
-     * extensibility. For example, a future signature scheme could insert its signatures there as
-     * well. The contract of the APK Signing Block is that all contents outside of the block must be
-     * protected by signatures inside the block.
-     */
-
-    private static final int CONTENT_DIGESTED_CHUNK_MAX_SIZE_BYTES = 1024 * 1024;
-
-    private static final byte[] APK_SIGNING_BLOCK_MAGIC =
-          new byte[] {
-              0x41, 0x50, 0x4b, 0x20, 0x53, 0x69, 0x67, 0x20,
-              0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x34, 0x32,
-          };
-    private static final int APK_SIGNATURE_SCHEME_V2_BLOCK_ID = 0x7109871a;
-
-    /**
-     * Signer configuration.
-     */
-    public static class SignerConfig {
-        /** Private key. */
-        public PrivateKey privateKey;
-
-        /**
-         * Certificates, with the first certificate containing the public key corresponding to
-         * {@link #privateKey}.
-         */
-        public List<X509Certificate> certificates;
-
-        /**
-         * List of signature algorithms with which to sign.
-         */
-        public List<SignatureAlgorithm> signatureAlgorithms;
-    }
-
-    /** Hidden constructor to prevent instantiation. */
-    private V2SchemeSigner() {}
-
-    /**
-     * Gets the APK Signature Scheme v2 signature algorithms to be used for signing an APK using the
-     * provided key.
-     *
-     * @param minSdkVersion minimum API Level of the platform on which the APK may be installed (see
-     *        AndroidManifest.xml minSdkVersion attribute).
-     *
-     * @throws InvalidKeyException if the provided key is not suitable for signing APKs using
-     *         APK Signature Scheme v2
-     */
-    public static List<SignatureAlgorithm> getSuggestedSignatureAlgorithms(
-            PublicKey signingKey, int minSdkVersion) throws InvalidKeyException {
-        String keyAlgorithm = signingKey.getAlgorithm();
-        if ("RSA".equalsIgnoreCase(keyAlgorithm)) {
-            // Use RSASSA-PKCS1-v1_5 signature scheme instead of RSASSA-PSS to guarantee
-            // deterministic signatures which make life easier for OTA updates (fewer files
-            // changed when deterministic signature schemes are used).
-
-            // Pick a digest which is no weaker than the key.
-            int modulusLengthBits = ((RSAKey) signingKey).getModulus().bitLength();
-            if (modulusLengthBits <= 3072) {
-                // 3072-bit RSA is roughly 128-bit strong, meaning SHA-256 is a good fit.
-                return Collections.singletonList(SignatureAlgorithm.RSA_PKCS1_V1_5_WITH_SHA256);
-            } else {
-                // Keys longer than 3072 bit need to be paired with a stronger digest to avoid the
-                // digest being the weak link. SHA-512 is the next strongest supported digest.
-                return Collections.singletonList(SignatureAlgorithm.RSA_PKCS1_V1_5_WITH_SHA512);
-            }
-        } else if ("DSA".equalsIgnoreCase(keyAlgorithm)) {
-            // DSA is supported only with SHA-256.
-            return Collections.singletonList(SignatureAlgorithm.DSA_WITH_SHA256);
-        } else if ("EC".equalsIgnoreCase(keyAlgorithm)) {
-            // Pick a digest which is no weaker than the key.
-            int keySizeBits = ((ECKey) signingKey).getParams().getOrder().bitLength();
-            if (keySizeBits <= 256) {
-                // 256-bit Elliptic Curve is roughly 128-bit strong, meaning SHA-256 is a good fit.
-                return Collections.singletonList(SignatureAlgorithm.ECDSA_WITH_SHA256);
-            } else {
-                // Keys longer than 256 bit need to be paired with a stronger digest to avoid the
-                // digest being the weak link. SHA-512 is the next strongest supported digest.
-                return Collections.singletonList(SignatureAlgorithm.ECDSA_WITH_SHA512);
-            }
-        } else {
-            throw new InvalidKeyException("Unsupported key algorithm: " + keyAlgorithm);
-        }
-    }
-
-    /**
-     * Signs the provided APK using APK Signature Scheme v2 and returns the APK Signing Block
-     * containing the signature.
-     *
-     * @param signerConfigs signer configurations, one for each signer At least one signer config
-     *        must be provided.
-     *
-     * @throws IOException if an I/O error occurs
-     * @throws NoSuchAlgorithmException if a required cryptographic algorithm implementation is
-     *         missing
-     * @throws InvalidKeyException if a signing key is not suitable for this signature scheme or
-     *         cannot be used in general
-     * @throws SignatureException if an error occurs when computing digests of generating
-     *         signatures
-     */
-    public static byte[] generateApkSigningBlock(
-            DataSource beforeCentralDir,
-            DataSource centralDir,
-            DataSource eocd,
-            List<SignerConfig> signerConfigs)
-                        throws IOException, NoSuchAlgorithmException, InvalidKeyException,
-                                SignatureException {
-        if (signerConfigs.isEmpty()) {
-            throw new IllegalArgumentException(
-                    "No signer configs provided. At least one is required");
-        }
-
-        // Figure out which digest(s) to use for APK contents.
-        Set<ContentDigestAlgorithm> contentDigestAlgorithms = new HashSet<>(1);
-        for (SignerConfig signerConfig : signerConfigs) {
-            for (SignatureAlgorithm signatureAlgorithm : signerConfig.signatureAlgorithms) {
-                contentDigestAlgorithms.add(signatureAlgorithm.getContentDigestAlgorithm());
-            }
-        }
-
-        // Ensure that, when digesting, ZIP End of Central Directory record's Central Directory
-        // offset field is treated as pointing to the offset at which the APK Signing Block will
-        // start.
-        long centralDirOffsetForDigesting = beforeCentralDir.size();
-        ByteBuffer eocdBuf = ByteBuffer.allocate((int) eocd.size());
-        eocdBuf.order(ByteOrder.LITTLE_ENDIAN);
-        eocd.copyTo(0, (int) eocd.size(), eocdBuf);
-        eocdBuf.flip();
-        ZipUtils.setZipEocdCentralDirectoryOffset(eocdBuf, centralDirOffsetForDigesting);
-
-        // Compute digests of APK contents.
-        Map<ContentDigestAlgorithm, byte[]> contentDigests; // digest algorithm ID -> digest
-        try {
-            contentDigests =
-                    computeContentDigests(
-                            contentDigestAlgorithms,
-                            new DataSource[] {
-                                    beforeCentralDir,
-                                    centralDir,
-                                    DataSources.asDataSource(eocdBuf)});
-        } catch (IOException e) {
-            throw new IOException("Failed to read APK being signed", e);
-        } catch (DigestException e) {
-            throw new SignatureException("Failed to compute digests of APK", e);
-        }
-
-        // Sign the digests and wrap the signatures and signer info into an APK Signing Block.
-        return generateApkSigningBlock(signerConfigs, contentDigests);
-    }
-
-    static Map<ContentDigestAlgorithm, byte[]> computeContentDigests(
-            Set<ContentDigestAlgorithm> digestAlgorithms,
-            DataSource[] contents) throws IOException, NoSuchAlgorithmException, DigestException {
-        // For each digest algorithm the result is computed as follows:
-        // 1. Each segment of contents is split into consecutive chunks of 1 MB in size.
-        //    The final chunk will be shorter iff the length of segment is not a multiple of 1 MB.
-        //    No chunks are produced for empty (zero length) segments.
-        // 2. The digest of each chunk is computed over the concatenation of byte 0xa5, the chunk's
-        //    length in bytes (uint32 little-endian) and the chunk's contents.
-        // 3. The output digest is computed over the concatenation of the byte 0x5a, the number of
-        //    chunks (uint32 little-endian) and the concatenation of digests of chunks of all
-        //    segments in-order.
-
-        long chunkCountLong = 0;
-        for (DataSource input : contents) {
-            chunkCountLong +=
-                    getChunkCount(input.size(), CONTENT_DIGESTED_CHUNK_MAX_SIZE_BYTES);
-        }
-        if (chunkCountLong > Integer.MAX_VALUE) {
-            throw new DigestException("Input too long: " + chunkCountLong + " chunks");
-        }
-        int chunkCount = (int) chunkCountLong;
-
-        ContentDigestAlgorithm[] digestAlgorithmsArray =
-                digestAlgorithms.toArray(new ContentDigestAlgorithm[digestAlgorithms.size()]);
-        MessageDigest[] mds = new MessageDigest[digestAlgorithmsArray.length];
-        byte[][] digestsOfChunks = new byte[digestAlgorithmsArray.length][];
-        int[] digestOutputSizes = new int[digestAlgorithmsArray.length];
-        for (int i = 0; i < digestAlgorithmsArray.length; i++) {
-            ContentDigestAlgorithm digestAlgorithm = digestAlgorithmsArray[i];
-            int digestOutputSizeBytes = digestAlgorithm.getChunkDigestOutputSizeBytes();
-            digestOutputSizes[i] = digestOutputSizeBytes;
-            byte[] concatenationOfChunkCountAndChunkDigests =
-                    new byte[5 + chunkCount * digestOutputSizeBytes];
-            concatenationOfChunkCountAndChunkDigests[0] = 0x5a;
-            setUnsignedInt32LittleEndian(
-                    chunkCount, concatenationOfChunkCountAndChunkDigests, 1);
-            digestsOfChunks[i] = concatenationOfChunkCountAndChunkDigests;
-            String jcaAlgorithm = digestAlgorithm.getJcaMessageDigestAlgorithm();
-            mds[i] = MessageDigest.getInstance(jcaAlgorithm);
-        }
-
-        MessageDigestSink mdSink = new MessageDigestSink(mds);
-        byte[] chunkContentPrefix = new byte[5];
-        chunkContentPrefix[0] = (byte) 0xa5;
-        int chunkIndex = 0;
-        // Optimization opportunity: digests of chunks can be computed in parallel. However,
-        // determining the number of computations to be performed in parallel is non-trivial. This
-        // depends on a wide range of factors, such as data source type (e.g., in-memory or fetched
-        // from file), CPU/memory/disk cache bandwidth and latency, interconnect architecture of CPU
-        // cores, load on the system from other threads of execution and other processes, size of
-        // input.
-        // For now, we compute these digests sequentially and thus have the luxury of improving
-        // performance by writing the digest of each chunk into a pre-allocated buffer at exactly
-        // the right position. This avoids unnecessary allocations, copying, and enables the final
-        // digest to be more efficient because it's presented with all of its input in one go.
-        for (DataSource input : contents) {
-            long inputOffset = 0;
-            long inputRemaining = input.size();
-            while (inputRemaining > 0) {
-                int chunkSize =
-                        (int) Math.min(inputRemaining, CONTENT_DIGESTED_CHUNK_MAX_SIZE_BYTES);
-                setUnsignedInt32LittleEndian(chunkSize, chunkContentPrefix, 1);
-                for (int i = 0; i < mds.length; i++) {
-                    mds[i].update(chunkContentPrefix);
-                }
-                try {
-                    input.feed(inputOffset, chunkSize, mdSink);
-                } catch (IOException e) {
-                    throw new IOException("Failed to read chunk #" + chunkIndex, e);
-                }
-                for (int i = 0; i < digestAlgorithmsArray.length; i++) {
-                    MessageDigest md = mds[i];
-                    byte[] concatenationOfChunkCountAndChunkDigests = digestsOfChunks[i];
-                    int expectedDigestSizeBytes = digestOutputSizes[i];
-                    int actualDigestSizeBytes =
-                            md.digest(
-                                    concatenationOfChunkCountAndChunkDigests,
-                                    5 + chunkIndex * expectedDigestSizeBytes,
-                                    expectedDigestSizeBytes);
-                    if (actualDigestSizeBytes != expectedDigestSizeBytes) {
-                        throw new RuntimeException(
-                                "Unexpected output size of " + md.getAlgorithm()
-                                        + " digest: " + actualDigestSizeBytes);
-                    }
-                }
-                inputOffset += chunkSize;
-                inputRemaining -= chunkSize;
-                chunkIndex++;
-            }
-        }
-
-        Map<ContentDigestAlgorithm, byte[]> result = new HashMap<>(digestAlgorithmsArray.length);
-        for (int i = 0; i < digestAlgorithmsArray.length; i++) {
-            ContentDigestAlgorithm digestAlgorithm = digestAlgorithmsArray[i];
-            byte[] concatenationOfChunkCountAndChunkDigests = digestsOfChunks[i];
-            MessageDigest md = mds[i];
-            byte[] digest = md.digest(concatenationOfChunkCountAndChunkDigests);
-            result.put(digestAlgorithm, digest);
-        }
-        return result;
-    }
-
-    private static final long getChunkCount(long inputSize, int chunkSize) {
-        return (inputSize + chunkSize - 1) / chunkSize;
-    }
-
-    private static void setUnsignedInt32LittleEndian(int value, byte[] result, int offset) {
-        result[offset] = (byte) (value & 0xff);
-        result[offset + 1] = (byte) ((value >> 8) & 0xff);
-        result[offset + 2] = (byte) ((value >> 16) & 0xff);
-        result[offset + 3] = (byte) ((value >> 24) & 0xff);
-    }
-
-    private static byte[] generateApkSigningBlock(
-            List<SignerConfig> signerConfigs,
-            Map<ContentDigestAlgorithm, byte[]> contentDigests)
-                    throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
-        byte[] apkSignatureSchemeV2Block =
-                generateApkSignatureSchemeV2Block(signerConfigs, contentDigests);
-        return generateApkSigningBlock(apkSignatureSchemeV2Block);
-    }
-
-    private static byte[] generateApkSigningBlock(byte[] apkSignatureSchemeV2Block) {
-        // FORMAT:
-        // uint64:  size (excluding this field)
-        // repeated ID-value pairs:
-        //     uint64:           size (excluding this field)
-        //     uint32:           ID
-        //     (size - 4) bytes: value
-        // uint64:  size (same as the one above)
-        // uint128: magic
-
-        int resultSize =
-                8 // size
-                + 8 + 4 + apkSignatureSchemeV2Block.length // v2Block as ID-value pair
-                + 8 // size
-                + 16 // magic
-                ;
-        ByteBuffer result = ByteBuffer.allocate(resultSize);
-        result.order(ByteOrder.LITTLE_ENDIAN);
-        long blockSizeFieldValue = resultSize - 8;
-        result.putLong(blockSizeFieldValue);
-
-        long pairSizeFieldValue = 4 + apkSignatureSchemeV2Block.length;
-        result.putLong(pairSizeFieldValue);
-        result.putInt(APK_SIGNATURE_SCHEME_V2_BLOCK_ID);
-        result.put(apkSignatureSchemeV2Block);
-
-        result.putLong(blockSizeFieldValue);
-        result.put(APK_SIGNING_BLOCK_MAGIC);
-
-        return result.array();
-    }
-
-    private static byte[] generateApkSignatureSchemeV2Block(
-            List<SignerConfig> signerConfigs,
-            Map<ContentDigestAlgorithm, byte[]> contentDigests)
-                    throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
-        // FORMAT:
-        // * length-prefixed sequence of length-prefixed signer blocks.
-
-        List<byte[]> signerBlocks = new ArrayList<>(signerConfigs.size());
-        int signerNumber = 0;
-        for (SignerConfig signerConfig : signerConfigs) {
-            signerNumber++;
-            byte[] signerBlock;
-            try {
-                signerBlock = generateSignerBlock(signerConfig, contentDigests);
-            } catch (InvalidKeyException e) {
-                throw new InvalidKeyException("Signer #" + signerNumber + " failed", e);
-            } catch (SignatureException e) {
-                throw new SignatureException("Signer #" + signerNumber + " failed", e);
-            }
-            signerBlocks.add(signerBlock);
-        }
-
-        return encodeAsSequenceOfLengthPrefixedElements(
-                new byte[][] {
-                    encodeAsSequenceOfLengthPrefixedElements(signerBlocks),
-                });
-    }
-
-    private static byte[] generateSignerBlock(
-            SignerConfig signerConfig,
-            Map<ContentDigestAlgorithm, byte[]> contentDigests)
-                    throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
-        if (signerConfig.certificates.isEmpty()) {
-            throw new SignatureException("No certificates configured for signer");
-        }
-        PublicKey publicKey = signerConfig.certificates.get(0).getPublicKey();
-
-        byte[] encodedPublicKey = encodePublicKey(publicKey);
-
-        V2SignatureSchemeBlock.SignedData signedData = new V2SignatureSchemeBlock.SignedData();
-        try {
-            signedData.certificates = encodeCertificates(signerConfig.certificates);
-        } catch (CertificateEncodingException e) {
-            throw new SignatureException("Failed to encode certificates", e);
-        }
-
-        List<Pair<Integer, byte[]>> digests =
-                new ArrayList<>(signerConfig.signatureAlgorithms.size());
-        for (SignatureAlgorithm signatureAlgorithm : signerConfig.signatureAlgorithms) {
-            ContentDigestAlgorithm contentDigestAlgorithm =
-                    signatureAlgorithm.getContentDigestAlgorithm();
-            byte[] contentDigest = contentDigests.get(contentDigestAlgorithm);
-            if (contentDigest == null) {
-                throw new RuntimeException(
-                        contentDigestAlgorithm + " content digest for " + signatureAlgorithm
-                                + " not computed");
-            }
-            digests.add(Pair.of(signatureAlgorithm.getId(), contentDigest));
-        }
-        signedData.digests = digests;
-
-        V2SignatureSchemeBlock.Signer signer = new V2SignatureSchemeBlock.Signer();
-        // FORMAT:
-        // * length-prefixed sequence of length-prefixed digests:
-        //   * uint32: signature algorithm ID
-        //   * length-prefixed bytes: digest of contents
-        // * length-prefixed sequence of certificates:
-        //   * length-prefixed bytes: X.509 certificate (ASN.1 DER encoded).
-        // * length-prefixed sequence of length-prefixed additional attributes:
-        //   * uint32: ID
-        //   * (length - 4) bytes: value
-        signer.signedData = encodeAsSequenceOfLengthPrefixedElements(new byte[][] {
-            encodeAsSequenceOfLengthPrefixedPairsOfIntAndLengthPrefixedBytes(signedData.digests),
-            encodeAsSequenceOfLengthPrefixedElements(signedData.certificates),
-            // additional attributes
-            new byte[0],
-        });
-        signer.publicKey = encodedPublicKey;
-        signer.signatures = new ArrayList<>(signerConfig.signatureAlgorithms.size());
-        for (SignatureAlgorithm signatureAlgorithm : signerConfig.signatureAlgorithms) {
-            Pair<String, ? extends AlgorithmParameterSpec> sigAlgAndParams =
-                    signatureAlgorithm.getJcaSignatureAlgorithmAndParams();
-            String jcaSignatureAlgorithm = sigAlgAndParams.getFirst();
-            AlgorithmParameterSpec jcaSignatureAlgorithmParams = sigAlgAndParams.getSecond();
-            byte[] signatureBytes;
-            try {
-                Signature signature = Signature.getInstance(jcaSignatureAlgorithm);
-                signature.initSign(signerConfig.privateKey);
-                if (jcaSignatureAlgorithmParams != null) {
-                    signature.setParameter(jcaSignatureAlgorithmParams);
-                }
-                signature.update(signer.signedData);
-                signatureBytes = signature.sign();
-            } catch (InvalidKeyException e) {
-                throw new InvalidKeyException("Failed to sign using " + jcaSignatureAlgorithm, e);
-            } catch (InvalidAlgorithmParameterException | SignatureException e) {
-                throw new SignatureException("Failed to sign using " + jcaSignatureAlgorithm, e);
-            }
-
-            try {
-                Signature signature = Signature.getInstance(jcaSignatureAlgorithm);
-                signature.initVerify(publicKey);
-                if (jcaSignatureAlgorithmParams != null) {
-                    signature.setParameter(jcaSignatureAlgorithmParams);
-                }
-                signature.update(signer.signedData);
-                if (!signature.verify(signatureBytes)) {
-                    throw new SignatureException("Signature did not verify");
-                }
-            } catch (InvalidKeyException e) {
-                throw new InvalidKeyException(
-                        "Failed to verify generated " + jcaSignatureAlgorithm + " signature using"
-                                + " public key from certificate", e);
-            } catch (InvalidAlgorithmParameterException | SignatureException e) {
-                throw new SignatureException(
-                        "Failed to verify generated " + jcaSignatureAlgorithm + " signature using"
-                                + " public key from certificate", e);
-            }
-
-            signer.signatures.add(Pair.of(signatureAlgorithm.getId(), signatureBytes));
-        }
-
-        // FORMAT:
-        // * length-prefixed signed data
-        // * length-prefixed sequence of length-prefixed signatures:
-        //   * uint32: signature algorithm ID
-        //   * length-prefixed bytes: signature of signed data
-        // * length-prefixed bytes: public key (X.509 SubjectPublicKeyInfo, ASN.1 DER encoded)
-        return encodeAsSequenceOfLengthPrefixedElements(
-                new byte[][] {
-                    signer.signedData,
-                    encodeAsSequenceOfLengthPrefixedPairsOfIntAndLengthPrefixedBytes(
-                            signer.signatures),
-                    signer.publicKey,
-                });
-    }
-
-    private static final class V2SignatureSchemeBlock {
-        private static final class Signer {
-            public byte[] signedData;
-            public List<Pair<Integer, byte[]>> signatures;
-            public byte[] publicKey;
-        }
-
-        private static final class SignedData {
-            public List<Pair<Integer, byte[]>> digests;
-            public List<byte[]> certificates;
-        }
-    }
-
-    private static byte[] encodePublicKey(PublicKey publicKey)
-            throws InvalidKeyException, NoSuchAlgorithmException {
-        byte[] encodedPublicKey = null;
-        if ("X.509".equals(publicKey.getFormat())) {
-            encodedPublicKey = publicKey.getEncoded();
-        }
-        if (encodedPublicKey == null) {
-            try {
-                encodedPublicKey =
-                        KeyFactory.getInstance(publicKey.getAlgorithm())
-                                .getKeySpec(publicKey, X509EncodedKeySpec.class)
-                                .getEncoded();
-            } catch (InvalidKeySpecException e) {
-                throw new InvalidKeyException(
-                        "Failed to obtain X.509 encoded form of public key " + publicKey
-                                + " of class " + publicKey.getClass().getName(),
-                        e);
-            }
-        }
-        if ((encodedPublicKey == null) || (encodedPublicKey.length == 0)) {
-            throw new InvalidKeyException(
-                    "Failed to obtain X.509 encoded form of public key " + publicKey
-                            + " of class " + publicKey.getClass().getName());
-        }
-        return encodedPublicKey;
-    }
-
-    private static List<byte[]> encodeCertificates(List<X509Certificate> certificates)
-            throws CertificateEncodingException {
-        List<byte[]> result = new ArrayList<>(certificates.size());
-        for (X509Certificate certificate : certificates) {
-            result.add(certificate.getEncoded());
-        }
-        return result;
-    }
-
-    private static byte[] encodeAsSequenceOfLengthPrefixedElements(List<byte[]> sequence) {
-        return encodeAsSequenceOfLengthPrefixedElements(
-                sequence.toArray(new byte[sequence.size()][]));
-    }
-
-    private static byte[] encodeAsSequenceOfLengthPrefixedElements(byte[][] sequence) {
-        int payloadSize = 0;
-        for (byte[] element : sequence) {
-            payloadSize += 4 + element.length;
-        }
-        ByteBuffer result = ByteBuffer.allocate(payloadSize);
-        result.order(ByteOrder.LITTLE_ENDIAN);
-        for (byte[] element : sequence) {
-            result.putInt(element.length);
-            result.put(element);
-        }
-        return result.array();
-      }
-
-    private static byte[] encodeAsSequenceOfLengthPrefixedPairsOfIntAndLengthPrefixedBytes(
-            List<Pair<Integer, byte[]>> sequence) {
-        int resultSize = 0;
-        for (Pair<Integer, byte[]> element : sequence) {
-            resultSize += 12 + element.getSecond().length;
-        }
-        ByteBuffer result = ByteBuffer.allocate(resultSize);
-        result.order(ByteOrder.LITTLE_ENDIAN);
-        for (Pair<Integer, byte[]> element : sequence) {
-            byte[] second = element.getSecond();
-            result.putInt(8 + second.length);
-            result.putInt(element.getFirst());
-            result.putInt(second.length);
-            result.put(second);
-        }
-        return result.array();
-    }
-}
diff --git a/tools/apksigner/core/src/com/android/apksigner/core/internal/apk/v2/V2SchemeVerifier.java b/tools/apksigner/core/src/com/android/apksigner/core/internal/apk/v2/V2SchemeVerifier.java
deleted file mode 100644
index 5e1e8fb..0000000
--- a/tools/apksigner/core/src/com/android/apksigner/core/internal/apk/v2/V2SchemeVerifier.java
+++ /dev/null
@@ -1,955 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.apksigner.core.internal.apk.v2;
-
-import com.android.apksigner.core.ApkVerifier.Issue;
-import com.android.apksigner.core.ApkVerifier.IssueWithParams;
-import com.android.apksigner.core.apk.ApkUtils;
-import com.android.apksigner.core.internal.util.ByteBufferDataSource;
-import com.android.apksigner.core.internal.util.DelegatingX509Certificate;
-import com.android.apksigner.core.internal.util.Pair;
-import com.android.apksigner.core.internal.zip.ZipUtils;
-import com.android.apksigner.core.util.DataSource;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.nio.BufferUnderflowException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.security.DigestException;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.InvalidKeyException;
-import java.security.KeyFactory;
-import java.security.NoSuchAlgorithmException;
-import java.security.PublicKey;
-import java.security.Signature;
-import java.security.SignatureException;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
-import java.security.spec.AlgorithmParameterSpec;
-import java.security.spec.X509EncodedKeySpec;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * APK Signature Scheme v2 verifier.
- *
- * <p>APK Signature Scheme v2 is a whole-file signature scheme which aims to protect every single
- * bit of the APK, as opposed to the JAR Signature Scheme which protects only the names and
- * uncompressed contents of ZIP entries.
- *
- * <p>TODO: Link to APK Signature Scheme v2 documentation once it's available.
- */
-public abstract class V2SchemeVerifier {
-
-    private static final long APK_SIG_BLOCK_MAGIC_HI = 0x3234206b636f6c42L;
-    private static final long APK_SIG_BLOCK_MAGIC_LO = 0x20676953204b5041L;
-    private static final int APK_SIG_BLOCK_MIN_SIZE = 32;
-
-    private static final int APK_SIGNATURE_SCHEME_V2_BLOCK_ID = 0x7109871a;
-
-    /** Hidden constructor to prevent instantiation. */
-    private V2SchemeVerifier() {}
-
-    /**
-     * Verifies the provided APK's APK Signature Scheme v2 signatures and returns the result of
-     * verification. APK is considered verified only if {@link Result#verified} is {@code true}. If
-     * verification fails, the result will contain errors -- see {@link Result#getErrors()}.
-     *
-     * @throws NoSuchAlgorithmException if the APK's signatures cannot be verified because a
-     *         required cryptographic algorithm implementation is missing
-     * @throws SignatureNotFoundException if no APK Signature Scheme v2 signatures are found
-     * @throws IOException if an I/O error occurs when reading the APK
-     */
-    public static Result verify(DataSource apk, ApkUtils.ZipSections zipSections)
-            throws IOException, NoSuchAlgorithmException, SignatureNotFoundException {
-        Result result = new Result();
-        SignatureInfo signatureInfo = findSignature(apk, zipSections, result);
-
-        DataSource beforeApkSigningBlock = apk.slice(0, signatureInfo.apkSigningBlockOffset);
-        DataSource centralDir =
-                apk.slice(
-                        signatureInfo.centralDirOffset,
-                        signatureInfo.eocdOffset - signatureInfo.centralDirOffset);
-        ByteBuffer eocd = signatureInfo.eocd;
-
-        verify(beforeApkSigningBlock,
-                signatureInfo.signatureBlock,
-                centralDir,
-                eocd,
-                result);
-        return result;
-    }
-
-    /**
-     * Verifies the provided APK's v2 signatures and outputs the results into the provided
-     * {@code result}. APK is considered verified only if there are no errors reported in the
-     * {@code result}.
-     */
-    private static void verify(
-            DataSource beforeApkSigningBlock,
-            ByteBuffer apkSignatureSchemeV2Block,
-            DataSource centralDir,
-            ByteBuffer eocd,
-            Result result) throws IOException, NoSuchAlgorithmException {
-        Set<ContentDigestAlgorithm> contentDigestsToVerify = new HashSet<>(1);
-        parseSigners(apkSignatureSchemeV2Block, contentDigestsToVerify, result);
-        if (result.containsErrors()) {
-            return;
-        }
-        verifyIntegrity(
-                beforeApkSigningBlock, centralDir, eocd, contentDigestsToVerify, result);
-        if (!result.containsErrors()) {
-            result.verified = true;
-        }
-    }
-
-    /**
-     * Parses each signer in the provided APK Signature Scheme v2 block and populates
-     * {@code signerInfos} of the provided {@code result}.
-     *
-     * <p>This verifies signatures over {@code signed-data} block contained in each signer block.
-     * However, this does not verify the integrity of the rest of the APK but rather simply reports
-     * the expected digests of the rest of the APK (see {@code contentDigestsToVerify}).
-     */
-    private static void parseSigners(
-            ByteBuffer apkSignatureSchemeV2Block,
-            Set<ContentDigestAlgorithm> contentDigestsToVerify,
-            Result result) throws NoSuchAlgorithmException {
-        ByteBuffer signers;
-        try {
-            signers = getLengthPrefixedSlice(apkSignatureSchemeV2Block);
-        } catch (IOException e) {
-            result.addError(Issue.V2_SIG_MALFORMED_SIGNERS);
-            return;
-        }
-        if (!signers.hasRemaining()) {
-            result.addError(Issue.V2_SIG_NO_SIGNERS);
-            return;
-        }
-
-        CertificateFactory certFactory;
-        try {
-            certFactory = CertificateFactory.getInstance("X.509");
-        } catch (CertificateException e) {
-            throw new RuntimeException("Failed to obtain X.509 CertificateFactory", e);
-        }
-        int signerCount = 0;
-        while (signers.hasRemaining()) {
-            int signerIndex = signerCount;
-            signerCount++;
-            Result.SignerInfo signerInfo = new Result.SignerInfo();
-            signerInfo.index = signerIndex;
-            result.signers.add(signerInfo);
-            try {
-                ByteBuffer signer = getLengthPrefixedSlice(signers);
-                parseSigner(signer, certFactory, signerInfo, contentDigestsToVerify);
-            } catch (IOException | BufferUnderflowException e) {
-                signerInfo.addError(Issue.V2_SIG_MALFORMED_SIGNER);
-                return;
-            }
-        }
-    }
-
-    /**
-     * Parses the provided signer block and populates the {@code result}.
-     *
-     * <p>This verifies signatures over {@code signed-data} contained in this block but does not
-     * verify the integrity of the rest of the APK. Rather, this method adds to the
-     * {@code contentDigestsToVerify}.
-     */
-    private static void parseSigner(
-            ByteBuffer signerBlock,
-            CertificateFactory certFactory,
-            Result.SignerInfo result,
-            Set<ContentDigestAlgorithm> contentDigestsToVerify)
-                    throws IOException, NoSuchAlgorithmException {
-        ByteBuffer signedData = getLengthPrefixedSlice(signerBlock);
-        byte[] signedDataBytes = new byte[signedData.remaining()];
-        signedData.get(signedDataBytes);
-        signedData.flip();
-        result.signedData = signedDataBytes;
-
-        ByteBuffer signatures = getLengthPrefixedSlice(signerBlock);
-        byte[] publicKeyBytes = readLengthPrefixedByteArray(signerBlock);
-
-        // Parse the signatures block and identify supported signatures
-        int signatureCount = 0;
-        List<SupportedSignature> supportedSignatures = new ArrayList<>(1);
-        while (signatures.hasRemaining()) {
-            signatureCount++;
-            try {
-                ByteBuffer signature = getLengthPrefixedSlice(signatures);
-                int sigAlgorithmId = signature.getInt();
-                byte[] sigBytes = readLengthPrefixedByteArray(signature);
-                result.signatures.add(
-                        new Result.SignerInfo.Signature(sigAlgorithmId, sigBytes));
-                SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.findById(sigAlgorithmId);
-                if (signatureAlgorithm == null) {
-                    result.addWarning(Issue.V2_SIG_UNKNOWN_SIG_ALGORITHM, sigAlgorithmId);
-                    continue;
-                }
-                supportedSignatures.add(new SupportedSignature(signatureAlgorithm, sigBytes));
-            } catch (IOException | BufferUnderflowException e) {
-                result.addError(Issue.V2_SIG_MALFORMED_SIGNATURE, signatureCount);
-                return;
-            }
-        }
-        if (result.signatures.isEmpty()) {
-            result.addError(Issue.V2_SIG_NO_SIGNATURES);
-            return;
-        }
-
-        // Verify signatures over signed-data block using the public key
-        List<SupportedSignature> signaturesToVerify = getSignaturesToVerify(supportedSignatures);
-        if (signaturesToVerify.isEmpty()) {
-            result.addError(Issue.V2_SIG_NO_SUPPORTED_SIGNATURES);
-            return;
-        }
-        for (SupportedSignature signature : signaturesToVerify) {
-            SignatureAlgorithm signatureAlgorithm = signature.algorithm;
-            String jcaSignatureAlgorithm =
-                    signatureAlgorithm.getJcaSignatureAlgorithmAndParams().getFirst();
-            AlgorithmParameterSpec jcaSignatureAlgorithmParams =
-                    signatureAlgorithm.getJcaSignatureAlgorithmAndParams().getSecond();
-            String keyAlgorithm = signatureAlgorithm.getJcaKeyAlgorithm();
-            PublicKey publicKey;
-            try {
-                publicKey =
-                        KeyFactory.getInstance(keyAlgorithm).generatePublic(
-                                new X509EncodedKeySpec(publicKeyBytes));
-            } catch (Exception e) {
-                result.addError(Issue.V2_SIG_MALFORMED_PUBLIC_KEY, e);
-                return;
-            }
-            try {
-                Signature sig = Signature.getInstance(jcaSignatureAlgorithm);
-                sig.initVerify(publicKey);
-                if (jcaSignatureAlgorithmParams != null) {
-                    sig.setParameter(jcaSignatureAlgorithmParams);
-                }
-                signedData.position(0);
-                sig.update(signedData);
-                byte[] sigBytes = signature.signature;
-                if (!sig.verify(sigBytes)) {
-                    result.addError(Issue.V2_SIG_DID_NOT_VERIFY, signatureAlgorithm);
-                    return;
-                }
-                result.verifiedSignatures.put(signatureAlgorithm, sigBytes);
-                contentDigestsToVerify.add(signatureAlgorithm.getContentDigestAlgorithm());
-            } catch (InvalidKeyException | InvalidAlgorithmParameterException
-                    | SignatureException e) {
-                result.addError(Issue.V2_SIG_VERIFY_EXCEPTION, signatureAlgorithm, e);
-                return;
-            }
-        }
-
-        // At least one signature over signedData has verified. We can now parse signed-data.
-        signedData.position(0);
-        ByteBuffer digests = getLengthPrefixedSlice(signedData);
-        ByteBuffer certificates = getLengthPrefixedSlice(signedData);
-        ByteBuffer additionalAttributes = getLengthPrefixedSlice(signedData);
-
-        // Parse the certificates block
-        int certificateIndex = -1;
-        while (certificates.hasRemaining()) {
-            certificateIndex++;
-            byte[] encodedCert = readLengthPrefixedByteArray(certificates);
-            X509Certificate certificate;
-            try {
-                certificate =
-                        (X509Certificate)
-                                certFactory.generateCertificate(
-                                        new ByteArrayInputStream(encodedCert));
-            } catch (CertificateException e) {
-                result.addError(
-                        Issue.V2_SIG_MALFORMED_CERTIFICATE,
-                        certificateIndex,
-                        certificateIndex + 1,
-                        e);
-                return;
-            }
-            // Wrap the cert so that the result's getEncoded returns exactly the original encoded
-            // form. Without this, getEncoded may return a different form from what was stored in
-            // the signature. This is becase some X509Certificate(Factory) implementations re-encode
-            // certificates.
-            certificate = new GuaranteedEncodedFormX509Certificate(certificate, encodedCert);
-            result.certs.add(certificate);
-        }
-
-        if (result.certs.isEmpty()) {
-            result.addError(Issue.V2_SIG_NO_CERTIFICATES);
-            return;
-        }
-        X509Certificate mainCertificate = result.certs.get(0);
-        byte[] certificatePublicKeyBytes = mainCertificate.getPublicKey().getEncoded();
-        if (!Arrays.equals(publicKeyBytes, certificatePublicKeyBytes)) {
-            result.addError(
-                    Issue.V2_SIG_PUBLIC_KEY_MISMATCH_BETWEEN_CERTIFICATE_AND_SIGNATURES_RECORD,
-                    toHex(certificatePublicKeyBytes),
-                    toHex(publicKeyBytes));
-            return;
-        }
-
-        // Parse the digests block
-        int digestCount = 0;
-        while (digests.hasRemaining()) {
-            digestCount++;
-            try {
-                ByteBuffer digest = getLengthPrefixedSlice(digests);
-                int sigAlgorithmId = digest.getInt();
-                byte[] digestBytes = readLengthPrefixedByteArray(digest);
-                result.contentDigests.add(
-                        new Result.SignerInfo.ContentDigest(sigAlgorithmId, digestBytes));
-            } catch (IOException | BufferUnderflowException e) {
-                result.addError(Issue.V2_SIG_MALFORMED_DIGEST, digestCount);
-                return;
-            }
-        }
-
-        List<Integer> sigAlgsFromSignaturesRecord = new ArrayList<>(result.signatures.size());
-        for (Result.SignerInfo.Signature signature : result.signatures) {
-            sigAlgsFromSignaturesRecord.add(signature.getAlgorithmId());
-        }
-        List<Integer> sigAlgsFromDigestsRecord = new ArrayList<>(result.contentDigests.size());
-        for (Result.SignerInfo.ContentDigest digest : result.contentDigests) {
-            sigAlgsFromDigestsRecord.add(digest.getSignatureAlgorithmId());
-        }
-
-        if (!sigAlgsFromSignaturesRecord.equals(sigAlgsFromDigestsRecord)) {
-            result.addError(
-                    Issue.V2_SIG_SIG_ALG_MISMATCH_BETWEEN_SIGNATURES_AND_DIGESTS_RECORDS,
-                    sigAlgsFromSignaturesRecord,
-                    sigAlgsFromDigestsRecord);
-            return;
-        }
-
-        // Parse the additional attributes block.
-        int additionalAttributeCount = 0;
-        while (additionalAttributes.hasRemaining()) {
-            additionalAttributeCount++;
-            try {
-                ByteBuffer attribute = getLengthPrefixedSlice(additionalAttributes);
-                int id = attribute.getInt();
-                byte[] value = readLengthPrefixedByteArray(attribute);
-                result.additionalAttributes.add(
-                        new Result.SignerInfo.AdditionalAttribute(id, value));
-                result.addWarning(Issue.V2_SIG_UNKNOWN_ADDITIONAL_ATTRIBUTE, id);
-            } catch (IOException | BufferUnderflowException e) {
-                result.addError(
-                        Issue.V2_SIG_MALFORMED_ADDITIONAL_ATTRIBUTE, additionalAttributeCount);
-                return;
-            }
-        }
-    }
-
-    private static List<SupportedSignature> getSignaturesToVerify(
-            List<SupportedSignature> signatures) {
-        // Pick the signature with the strongest algorithm, to mimic Android's behavior.
-        SignatureAlgorithm bestSigAlgorithm = null;
-        byte[] bestSigAlgorithmSignatureBytes = null;
-        for (SupportedSignature sig : signatures) {
-            SignatureAlgorithm sigAlgorithm = sig.algorithm;
-            if ((bestSigAlgorithm == null)
-                    || (compareSignatureAlgorithm(sigAlgorithm, bestSigAlgorithm) > 0)) {
-                bestSigAlgorithm = sigAlgorithm;
-                bestSigAlgorithmSignatureBytes = sig.signature;
-            }
-        }
-
-        if (bestSigAlgorithm == null) {
-            return Collections.emptyList();
-        } else {
-            return Collections.singletonList(
-                    new SupportedSignature(bestSigAlgorithm, bestSigAlgorithmSignatureBytes));
-        }
-    }
-
-    private static class SupportedSignature {
-        private final SignatureAlgorithm algorithm;
-        private final byte[] signature;
-
-        private SupportedSignature(SignatureAlgorithm algorithm, byte[] signature) {
-            this.algorithm = algorithm;
-            this.signature = signature;
-        }
-    }
-
-    /**
-     * Returns positive number if {@code alg1} is preferred over {@code alg2}, {@code -1} if
-     * {@code alg2} is preferred over {@code alg1}, and {@code 0} if there is no preference.
-     */
-    private static int compareSignatureAlgorithm(SignatureAlgorithm alg1, SignatureAlgorithm alg2) {
-        ContentDigestAlgorithm digestAlg1 = alg1.getContentDigestAlgorithm();
-        ContentDigestAlgorithm digestAlg2 = alg2.getContentDigestAlgorithm();
-        return compareContentDigestAlgorithm(digestAlg1, digestAlg2);
-    }
-
-    /**
-     * Returns positive number if {@code alg1} is preferred over {@code alg2}, {@code -1} if
-     * {@code alg2} is preferred over {@code alg1}, and {@code 0} if there is no preference.
-     */
-    private static int compareContentDigestAlgorithm(
-            ContentDigestAlgorithm alg1,
-            ContentDigestAlgorithm alg2) {
-        switch (alg1) {
-            case CHUNKED_SHA256:
-                switch (alg2) {
-                    case CHUNKED_SHA256:
-                        return 0;
-                    case CHUNKED_SHA512:
-                        return -1;
-                    default:
-                        throw new IllegalArgumentException("Unknown alg2: " + alg2);
-                }
-            case CHUNKED_SHA512:
-                switch (alg2) {
-                    case CHUNKED_SHA256:
-                        return 1;
-                    case CHUNKED_SHA512:
-                        return 0;
-                    default:
-                        throw new IllegalArgumentException("Unknown alg2: " + alg2);
-                }
-            default:
-                throw new IllegalArgumentException("Unknown alg1: " + alg1);
-        }
-    }
-
-    /**
-     * Verifies integrity of the APK outside of the APK Signing Block by computing digests of the
-     * APK and comparing them against the digests listed in APK Signing Block. The expected digests
-     * taken from {@code v2SchemeSignerInfos} of the provided {@code result}.
-     */
-    private static void verifyIntegrity(
-            DataSource beforeApkSigningBlock,
-            DataSource centralDir,
-            ByteBuffer eocd,
-            Set<ContentDigestAlgorithm> contentDigestAlgorithms,
-            Result result) throws IOException, NoSuchAlgorithmException {
-        if (contentDigestAlgorithms.isEmpty()) {
-            // This should never occur because this method is invoked once at least one signature
-            // is verified, meaning at least one content digest is known.
-            throw new RuntimeException("No content digests found");
-        }
-
-        // For the purposes of verifying integrity, ZIP End of Central Directory (EoCD) must be
-        // treated as though its Central Directory offset points to the start of APK Signing Block.
-        // We thus modify the EoCD accordingly.
-        ByteBuffer modifiedEocd = ByteBuffer.allocate(eocd.remaining());
-        modifiedEocd.order(ByteOrder.LITTLE_ENDIAN);
-        modifiedEocd.put(eocd);
-        modifiedEocd.flip();
-        ZipUtils.setZipEocdCentralDirectoryOffset(modifiedEocd, beforeApkSigningBlock.size());
-        Map<ContentDigestAlgorithm, byte[]> actualContentDigests;
-        try {
-            actualContentDigests =
-                    V2SchemeSigner.computeContentDigests(
-                            contentDigestAlgorithms,
-                            new DataSource[] {
-                                    beforeApkSigningBlock,
-                                    centralDir,
-                                    new ByteBufferDataSource(modifiedEocd)
-                            });
-        } catch (DigestException e) {
-            throw new RuntimeException("Failed to compute content digests", e);
-        }
-        if (!contentDigestAlgorithms.equals(actualContentDigests.keySet())) {
-            throw new RuntimeException(
-                    "Mismatch between sets of requested and computed content digests"
-                            + " . Requested: " + contentDigestAlgorithms
-                            + ", computed: " + actualContentDigests.keySet());
-        }
-
-        // Compare digests computed over the rest of APK against the corresponding expected digests
-        // in signer blocks.
-        for (Result.SignerInfo signerInfo : result.signers) {
-            for (Result.SignerInfo.ContentDigest expected : signerInfo.contentDigests) {
-                SignatureAlgorithm signatureAlgorithm =
-                        SignatureAlgorithm.findById(expected.getSignatureAlgorithmId());
-                if (signatureAlgorithm == null) {
-                    continue;
-                }
-                ContentDigestAlgorithm contentDigestAlgorithm =
-                        signatureAlgorithm.getContentDigestAlgorithm();
-                byte[] expectedDigest = expected.getValue();
-                byte[] actualDigest = actualContentDigests.get(contentDigestAlgorithm);
-                if (!Arrays.equals(expectedDigest, actualDigest)) {
-                    signerInfo.addError(
-                            Issue.V2_SIG_APK_DIGEST_DID_NOT_VERIFY,
-                            contentDigestAlgorithm,
-                            toHex(expectedDigest),
-                            toHex(actualDigest));
-                    continue;
-                }
-                signerInfo.verifiedContentDigests.put(contentDigestAlgorithm, actualDigest);
-            }
-        }
-    }
-
-    /**
-     * APK Signature Scheme v2 block and additional information relevant to verifying the signatures
-     * contained in the block against the file.
-     */
-    private static class SignatureInfo {
-        /** Contents of APK Signature Scheme v2 block. */
-        private final ByteBuffer signatureBlock;
-
-        /** Position of the APK Signing Block in the file. */
-        private final long apkSigningBlockOffset;
-
-        /** Position of the ZIP Central Directory in the file. */
-        private final long centralDirOffset;
-
-        /** Position of the ZIP End of Central Directory (EoCD) in the file. */
-        private final long eocdOffset;
-
-        /** Contents of ZIP End of Central Directory (EoCD) of the file. */
-        private final ByteBuffer eocd;
-
-        private SignatureInfo(
-                ByteBuffer signatureBlock,
-                long apkSigningBlockOffset,
-                long centralDirOffset,
-                long eocdOffset,
-                ByteBuffer eocd) {
-            this.signatureBlock = signatureBlock;
-            this.apkSigningBlockOffset = apkSigningBlockOffset;
-            this.centralDirOffset = centralDirOffset;
-            this.eocdOffset = eocdOffset;
-            this.eocd = eocd;
-        }
-    }
-
-    /**
-     * Returns the APK Signature Scheme v2 block contained in the provided APK file and the
-     * additional information relevant for verifying the block against the file.
-     *
-     * @throws SignatureNotFoundException if the APK is not signed using APK Signature Scheme v2
-     * @throws IOException if an I/O error occurs while reading the APK
-     */
-    private static SignatureInfo findSignature(
-            DataSource apk, ApkUtils.ZipSections zipSections, Result result)
-                    throws IOException, SignatureNotFoundException {
-        // Find the APK Signing Block. The block immediately precedes the Central Directory.
-        ByteBuffer eocd = zipSections.getZipEndOfCentralDirectory();
-        Pair<DataSource, Long> apkSigningBlockAndOffset = findApkSigningBlock(apk, zipSections);
-        DataSource apkSigningBlock = apkSigningBlockAndOffset.getFirst();
-        long apkSigningBlockOffset = apkSigningBlockAndOffset.getSecond();
-        ByteBuffer apkSigningBlockBuf =
-                apkSigningBlock.getByteBuffer(0, (int) apkSigningBlock.size());
-        apkSigningBlockBuf.order(ByteOrder.LITTLE_ENDIAN);
-
-        // Find the APK Signature Scheme v2 Block inside the APK Signing Block.
-        ByteBuffer apkSignatureSchemeV2Block =
-                findApkSignatureSchemeV2Block(apkSigningBlockBuf, result);
-
-        return new SignatureInfo(
-                apkSignatureSchemeV2Block,
-                apkSigningBlockOffset,
-                zipSections.getZipCentralDirectoryOffset(),
-                zipSections.getZipEndOfCentralDirectoryOffset(),
-                eocd);
-    }
-
-    /**
-     * Returns the APK Signing Block and its offset in the provided APK.
-     *
-     * @throws SignatureNotFoundException if the APK does not contain an APK Signing Block
-     */
-    public static Pair<DataSource, Long> findApkSigningBlock(
-            DataSource apk, ApkUtils.ZipSections zipSections)
-                    throws IOException, SignatureNotFoundException {
-        // FORMAT:
-        // OFFSET       DATA TYPE  DESCRIPTION
-        // * @+0  bytes uint64:    size in bytes (excluding this field)
-        // * @+8  bytes payload
-        // * @-24 bytes uint64:    size in bytes (same as the one above)
-        // * @-16 bytes uint128:   magic
-
-        long centralDirStartOffset = zipSections.getZipCentralDirectoryOffset();
-        long centralDirEndOffset =
-                centralDirStartOffset + zipSections.getZipCentralDirectorySizeBytes();
-        long eocdStartOffset = zipSections.getZipEndOfCentralDirectoryOffset();
-        if (centralDirEndOffset != eocdStartOffset) {
-            throw new SignatureNotFoundException(
-                    "ZIP Central Directory is not immediately followed by End of Central Directory"
-                            + ". CD end: " + centralDirEndOffset
-                            + ", EoCD start: " + eocdStartOffset);
-        }
-
-        if (centralDirStartOffset < APK_SIG_BLOCK_MIN_SIZE) {
-            throw new SignatureNotFoundException(
-                    "APK too small for APK Signing Block. ZIP Central Directory offset: "
-                            + centralDirStartOffset);
-        }
-        // Read the magic and offset in file from the footer section of the block:
-        // * uint64:   size of block
-        // * 16 bytes: magic
-        ByteBuffer footer = apk.getByteBuffer(centralDirStartOffset - 24, 24);
-        footer.order(ByteOrder.LITTLE_ENDIAN);
-        if ((footer.getLong(8) != APK_SIG_BLOCK_MAGIC_LO)
-                || (footer.getLong(16) != APK_SIG_BLOCK_MAGIC_HI)) {
-            throw new SignatureNotFoundException(
-                    "No APK Signing Block before ZIP Central Directory");
-        }
-        // Read and compare size fields
-        long apkSigBlockSizeInFooter = footer.getLong(0);
-        if ((apkSigBlockSizeInFooter < footer.capacity())
-                || (apkSigBlockSizeInFooter > Integer.MAX_VALUE - 8)) {
-            throw new SignatureNotFoundException(
-                    "APK Signing Block size out of range: " + apkSigBlockSizeInFooter);
-        }
-        int totalSize = (int) (apkSigBlockSizeInFooter + 8);
-        long apkSigBlockOffset = centralDirStartOffset - totalSize;
-        if (apkSigBlockOffset < 0) {
-            throw new SignatureNotFoundException(
-                    "APK Signing Block offset out of range: " + apkSigBlockOffset);
-        }
-        ByteBuffer apkSigBlock = apk.getByteBuffer(apkSigBlockOffset, 8);
-        apkSigBlock.order(ByteOrder.LITTLE_ENDIAN);
-        long apkSigBlockSizeInHeader = apkSigBlock.getLong(0);
-        if (apkSigBlockSizeInHeader != apkSigBlockSizeInFooter) {
-            throw new SignatureNotFoundException(
-                    "APK Signing Block sizes in header and footer do not match: "
-                            + apkSigBlockSizeInHeader + " vs " + apkSigBlockSizeInFooter);
-        }
-        return Pair.of(apk.slice(apkSigBlockOffset, totalSize), apkSigBlockOffset);
-    }
-
-    private static ByteBuffer findApkSignatureSchemeV2Block(
-            ByteBuffer apkSigningBlock,
-            Result result) throws SignatureNotFoundException {
-        checkByteOrderLittleEndian(apkSigningBlock);
-        // FORMAT:
-        // OFFSET       DATA TYPE  DESCRIPTION
-        // * @+0  bytes uint64:    size in bytes (excluding this field)
-        // * @+8  bytes pairs
-        // * @-24 bytes uint64:    size in bytes (same as the one above)
-        // * @-16 bytes uint128:   magic
-        ByteBuffer pairs = sliceFromTo(apkSigningBlock, 8, apkSigningBlock.capacity() - 24);
-
-        int entryCount = 0;
-        while (pairs.hasRemaining()) {
-            entryCount++;
-            if (pairs.remaining() < 8) {
-                throw new SignatureNotFoundException(
-                        "Insufficient data to read size of APK Signing Block entry #" + entryCount);
-            }
-            long lenLong = pairs.getLong();
-            if ((lenLong < 4) || (lenLong > Integer.MAX_VALUE)) {
-                throw new SignatureNotFoundException(
-                        "APK Signing Block entry #" + entryCount
-                                + " size out of range: " + lenLong);
-            }
-            int len = (int) lenLong;
-            int nextEntryPos = pairs.position() + len;
-            if (len > pairs.remaining()) {
-                throw new SignatureNotFoundException(
-                        "APK Signing Block entry #" + entryCount + " size out of range: " + len
-                                + ", available: " + pairs.remaining());
-            }
-            int id = pairs.getInt();
-            if (id == APK_SIGNATURE_SCHEME_V2_BLOCK_ID) {
-                return getByteBuffer(pairs, len - 4);
-            }
-            result.addWarning(Issue.APK_SIG_BLOCK_UNKNOWN_ENTRY_ID, id);
-            pairs.position(nextEntryPos);
-        }
-
-        throw new SignatureNotFoundException(
-                "No APK Signature Scheme v2 block in APK Signing Block");
-    }
-
-    private static void checkByteOrderLittleEndian(ByteBuffer buffer) {
-        if (buffer.order() != ByteOrder.LITTLE_ENDIAN) {
-            throw new IllegalArgumentException("ByteBuffer byte order must be little endian");
-        }
-    }
-
-    public static class SignatureNotFoundException extends Exception {
-        private static final long serialVersionUID = 1L;
-
-        public SignatureNotFoundException(String message) {
-            super(message);
-        }
-
-        public SignatureNotFoundException(String message, Throwable cause) {
-            super(message, cause);
-        }
-    }
-
-    /**
-     * Returns new byte buffer whose content is a shared subsequence of this buffer's content
-     * between the specified start (inclusive) and end (exclusive) positions. As opposed to
-     * {@link ByteBuffer#slice()}, the returned buffer's byte order is the same as the source
-     * buffer's byte order.
-     */
-    private static ByteBuffer sliceFromTo(ByteBuffer source, int start, int end) {
-        if (start < 0) {
-            throw new IllegalArgumentException("start: " + start);
-        }
-        if (end < start) {
-            throw new IllegalArgumentException("end < start: " + end + " < " + start);
-        }
-        int capacity = source.capacity();
-        if (end > source.capacity()) {
-            throw new IllegalArgumentException("end > capacity: " + end + " > " + capacity);
-        }
-        int originalLimit = source.limit();
-        int originalPosition = source.position();
-        try {
-            source.position(0);
-            source.limit(end);
-            source.position(start);
-            ByteBuffer result = source.slice();
-            result.order(source.order());
-            return result;
-        } finally {
-            source.position(0);
-            source.limit(originalLimit);
-            source.position(originalPosition);
-        }
-    }
-
-    /**
-     * Relative <em>get</em> method for reading {@code size} number of bytes from the current
-     * position of this buffer.
-     *
-     * <p>This method reads the next {@code size} bytes at this buffer's current position,
-     * returning them as a {@code ByteBuffer} with start set to 0, limit and capacity set to
-     * {@code size}, byte order set to this buffer's byte order; and then increments the position by
-     * {@code size}.
-     */
-    private static ByteBuffer getByteBuffer(ByteBuffer source, int size)
-            throws BufferUnderflowException {
-        if (size < 0) {
-            throw new IllegalArgumentException("size: " + size);
-        }
-        int originalLimit = source.limit();
-        int position = source.position();
-        int limit = position + size;
-        if ((limit < position) || (limit > originalLimit)) {
-            throw new BufferUnderflowException();
-        }
-        source.limit(limit);
-        try {
-            ByteBuffer result = source.slice();
-            result.order(source.order());
-            source.position(limit);
-            return result;
-        } finally {
-            source.limit(originalLimit);
-        }
-    }
-
-    private static ByteBuffer getLengthPrefixedSlice(ByteBuffer source) throws IOException {
-        if (source.remaining() < 4) {
-            throw new IOException(
-                    "Remaining buffer too short to contain length of length-prefixed field."
-                            + " Remaining: " + source.remaining());
-        }
-        int len = source.getInt();
-        if (len < 0) {
-            throw new IllegalArgumentException("Negative length");
-        } else if (len > source.remaining()) {
-            throw new IOException("Length-prefixed field longer than remaining buffer."
-                    + " Field length: " + len + ", remaining: " + source.remaining());
-        }
-        return getByteBuffer(source, len);
-    }
-
-    private static byte[] readLengthPrefixedByteArray(ByteBuffer buf) throws IOException {
-        int len = buf.getInt();
-        if (len < 0) {
-            throw new IOException("Negative length");
-        } else if (len > buf.remaining()) {
-            throw new IOException("Underflow while reading length-prefixed value. Length: " + len
-                    + ", available: " + buf.remaining());
-        }
-        byte[] result = new byte[len];
-        buf.get(result);
-        return result;
-    }
-
-    /**
-     * {@link X509Certificate} whose {@link #getEncoded()} returns the data provided at construction
-     * time.
-     */
-    private static class GuaranteedEncodedFormX509Certificate extends DelegatingX509Certificate {
-        private byte[] mEncodedForm;
-
-        public GuaranteedEncodedFormX509Certificate(X509Certificate wrapped, byte[] encodedForm) {
-            super(wrapped);
-            this.mEncodedForm = (encodedForm != null) ? encodedForm.clone() : null;
-        }
-
-        @Override
-        public byte[] getEncoded() throws CertificateEncodingException {
-            return (mEncodedForm != null) ? mEncodedForm.clone() : null;
-        }
-    }
-
-    private static final char[] HEX_DIGITS = "01234567890abcdef".toCharArray();
-
-    private static String toHex(byte[] value) {
-        StringBuilder sb = new StringBuilder(value.length * 2);
-        int len = value.length;
-        for (int i = 0; i < len; i++) {
-            int hi = (value[i] & 0xff) >>> 4;
-            int lo = value[i] & 0x0f;
-            sb.append(HEX_DIGITS[hi]).append(HEX_DIGITS[lo]);
-        }
-        return sb.toString();
-    }
-
-    public static class Result {
-
-        /** Whether the APK's APK Signature Scheme v2 signature verifies. */
-        public boolean verified;
-
-        public final List<SignerInfo> signers = new ArrayList<>();
-        private final List<IssueWithParams> mWarnings = new ArrayList<>();
-        private final List<IssueWithParams> mErrors = new ArrayList<>();
-
-        public boolean containsErrors() {
-            if (!mErrors.isEmpty()) {
-                return true;
-            }
-            if (!signers.isEmpty()) {
-                for (SignerInfo signer : signers) {
-                    if (signer.containsErrors()) {
-                        return true;
-                    }
-                }
-            }
-            return false;
-        }
-
-        public void addError(Issue msg, Object... parameters) {
-            mErrors.add(new IssueWithParams(msg, parameters));
-        }
-
-        public void addWarning(Issue msg, Object... parameters) {
-            mWarnings.add(new IssueWithParams(msg, parameters));
-        }
-
-        public List<IssueWithParams> getErrors() {
-            return mErrors;
-        }
-
-        public List<IssueWithParams> getWarnings() {
-            return mWarnings;
-        }
-
-        public static class SignerInfo {
-            public int index;
-            public List<X509Certificate> certs = new ArrayList<>();
-            public List<ContentDigest> contentDigests = new ArrayList<>();
-            public Map<ContentDigestAlgorithm, byte[]> verifiedContentDigests = new HashMap<>();
-            public List<Signature> signatures = new ArrayList<>();
-            public Map<SignatureAlgorithm, byte[]> verifiedSignatures = new HashMap<>();
-            public List<AdditionalAttribute> additionalAttributes = new ArrayList<>();
-            public byte[] signedData;
-
-            private final List<IssueWithParams> mWarnings = new ArrayList<>();
-            private final List<IssueWithParams> mErrors = new ArrayList<>();
-
-            public void addError(Issue msg, Object... parameters) {
-                mErrors.add(new IssueWithParams(msg, parameters));
-            }
-
-            public void addWarning(Issue msg, Object... parameters) {
-                mWarnings.add(new IssueWithParams(msg, parameters));
-            }
-
-            public boolean containsErrors() {
-                return !mErrors.isEmpty();
-            }
-
-            public List<IssueWithParams> getErrors() {
-                return mErrors;
-            }
-
-            public List<IssueWithParams> getWarnings() {
-                return mWarnings;
-            }
-
-            public static class ContentDigest {
-                private final int mSignatureAlgorithmId;
-                private final byte[] mValue;
-
-                public ContentDigest(int signatureAlgorithmId, byte[] value) {
-                    mSignatureAlgorithmId  = signatureAlgorithmId;
-                    mValue = value;
-                }
-
-                public int getSignatureAlgorithmId() {
-                    return mSignatureAlgorithmId;
-                }
-
-                public byte[] getValue() {
-                    return mValue;
-                }
-            }
-
-            public static class Signature {
-                private final int mAlgorithmId;
-                private final byte[] mValue;
-
-                public Signature(int algorithmId, byte[] value) {
-                    mAlgorithmId  = algorithmId;
-                    mValue = value;
-                }
-
-                public int getAlgorithmId() {
-                    return mAlgorithmId;
-                }
-
-                public byte[] getValue() {
-                    return mValue;
-                }
-            }
-
-            public static class AdditionalAttribute {
-                private final int mId;
-                private final byte[] mValue;
-
-                public AdditionalAttribute(int id, byte[] value) {
-                    mId  = id;
-                    mValue = value.clone();
-                }
-
-                public int getId() {
-                    return mId;
-                }
-
-                public byte[] getValue() {
-                    return mValue.clone();
-                }
-            }
-        }
-    }
-}
diff --git a/tools/apksigner/core/src/com/android/apksigner/core/internal/jar/ManifestParser.java b/tools/apksigner/core/src/com/android/apksigner/core/internal/jar/ManifestParser.java
deleted file mode 100644
index 793300c..0000000
--- a/tools/apksigner/core/src/com/android/apksigner/core/internal/jar/ManifestParser.java
+++ /dev/null
@@ -1,335 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.apksigner.core.internal.jar;
-
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.jar.Attributes;
-
-/**
- * JAR manifest and signature file parser.
- *
- * <p>These files consist of a main section followed by individual sections. Individual sections
- * are named, their names referring to JAR entries.
- *
- * @see <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#JAR_Manifest">JAR Manifest format</a>
- */
-public class ManifestParser {
-
-    private final byte[] mManifest;
-    private int mOffset;
-    private int mEndOffset;
-
-    private String mBufferedLine;
-
-    /**
-     * Constructs a new {@code ManifestParser} with the provided input.
-     */
-    public ManifestParser(byte[] data) {
-        this(data, 0, data.length);
-    }
-
-    /**
-     * Constructs a new {@code ManifestParser} with the provided input.
-     */
-    public ManifestParser(byte[] data, int offset, int length) {
-        mManifest = data;
-        mOffset = offset;
-        mEndOffset = offset + length;
-    }
-
-    /**
-     * Returns the remaining sections of this file.
-     */
-    public List<Section> readAllSections() {
-        List<Section> sections = new ArrayList<>();
-        Section section;
-        while ((section = readSection()) != null) {
-            sections.add(section);
-        }
-        return sections;
-    }
-
-    /**
-     * Returns the next section from this file or {@code null} if end of file has been reached.
-     */
-    public Section readSection() {
-        // Locate the first non-empty line
-        int sectionStartOffset;
-        String attr;
-        do {
-            sectionStartOffset = mOffset;
-            attr = readAttribute();
-            if (attr == null) {
-                return null;
-            }
-        } while (attr.length() == 0);
-        List<Attribute> attrs = new ArrayList<>();
-        attrs.add(parseAttr(attr));
-
-        // Read attributes until end of section reached
-        while (true) {
-            attr = readAttribute();
-            if ((attr == null) || (attr.length() == 0)) {
-                // End of section
-                break;
-            }
-            attrs.add(parseAttr(attr));
-        }
-
-        int sectionEndOffset = mOffset;
-        int sectionSizeBytes = sectionEndOffset - sectionStartOffset;
-
-        return new Section(sectionStartOffset, sectionSizeBytes, attrs);
-    }
-
-    private static Attribute parseAttr(String attr) {
-        int delimiterIndex = attr.indexOf(':');
-        if (delimiterIndex == -1) {
-            return new Attribute(attr.trim(), "");
-        } else {
-            return new Attribute(
-                    attr.substring(0, delimiterIndex).trim(),
-                    attr.substring(delimiterIndex + 1).trim());
-        }
-    }
-
-    /**
-     * Returns the next attribute or empty {@code String} if end of section has been reached or
-     * {@code null} if end of input has been reached.
-     */
-    private String readAttribute() {
-        // Check whether end of section was reached during previous invocation
-        if ((mBufferedLine != null) && (mBufferedLine.length() == 0)) {
-            mBufferedLine = null;
-            return "";
-        }
-
-        // Read the next line
-        String line = readLine();
-        if (line == null) {
-            // End of input
-            if (mBufferedLine != null) {
-                String result = mBufferedLine;
-                mBufferedLine = null;
-                return result;
-            }
-            return null;
-        }
-
-        // Consume the read line
-        if (line.length() == 0) {
-            // End of section
-            if (mBufferedLine != null) {
-                String result = mBufferedLine;
-                mBufferedLine = "";
-                return result;
-            }
-            return "";
-        }
-        StringBuilder attrLine;
-        if (mBufferedLine == null) {
-            attrLine = new StringBuilder(line);
-        } else {
-            if (!line.startsWith(" ")) {
-                // The most common case: buffered line is a full attribute
-                String result = mBufferedLine;
-                mBufferedLine = line;
-                return result;
-            }
-            attrLine = new StringBuilder(mBufferedLine);
-            mBufferedLine = null;
-            attrLine.append(line.substring(1));
-        }
-
-        // Everything's buffered in attrLine now. mBufferedLine is null
-
-        // Read more lines
-        while (true) {
-            line = readLine();
-            if (line == null) {
-                // End of input
-                return attrLine.toString();
-            } else if (line.length() == 0) {
-                // End of section
-                mBufferedLine = ""; // make this method return "end of section" next time
-                return attrLine.toString();
-            }
-            if (line.startsWith(" ")) {
-                // Continuation line
-                attrLine.append(line.substring(1));
-            } else {
-                // Next attribute
-                mBufferedLine = line;
-                return attrLine.toString();
-            }
-        }
-    }
-
-    /**
-     * Returns the next line (without line delimiter characters) or {@code null} if end of input has
-     * been reached.
-     */
-    private String readLine() {
-        if (mOffset >= mEndOffset) {
-            return null;
-        }
-        int startOffset = mOffset;
-        int newlineStartOffset = -1;
-        int newlineEndOffset = -1;
-        for (int i = startOffset; i < mEndOffset; i++) {
-            byte b = mManifest[i];
-            if (b == '\r') {
-                newlineStartOffset = i;
-                int nextIndex = i + 1;
-                if ((nextIndex < mEndOffset) && (mManifest[nextIndex] == '\n')) {
-                    newlineEndOffset = nextIndex + 1;
-                    break;
-                }
-                newlineEndOffset = nextIndex;
-                break;
-            } else if (b == '\n') {
-                newlineStartOffset = i;
-                newlineEndOffset = i + 1;
-                break;
-            }
-        }
-        if (newlineStartOffset == -1) {
-            newlineStartOffset = mEndOffset;
-            newlineEndOffset = mEndOffset;
-        }
-        mOffset = newlineEndOffset;
-
-        int lineLengthBytes = newlineStartOffset - startOffset;
-        if (lineLengthBytes == 0) {
-            return "";
-        }
-        return new String(mManifest, startOffset, lineLengthBytes, StandardCharsets.UTF_8);
-    }
-
-
-    /**
-     * Attribute.
-     */
-    public static class Attribute {
-        private final String mName;
-        private final String mValue;
-
-        /**
-         * Constructs a new {@code Attribute} with the provided name and value.
-         */
-        public Attribute(String name, String value) {
-            mName = name;
-            mValue = value;
-        }
-
-        /**
-         * Returns this attribute's name.
-         */
-        public String getName() {
-            return mName;
-        }
-
-        /**
-         * Returns this attribute's value.
-         */
-        public String getValue() {
-            return mValue;
-        }
-    }
-
-    /**
-     * Section.
-     */
-    public static class Section {
-        private final int mStartOffset;
-        private final int mSizeBytes;
-        private final String mName;
-        private final List<Attribute> mAttributes;
-
-        /**
-         * Constructs a new {@code Section}.
-         *
-         * @param startOffset start offset (in bytes) of the section in the input file
-         * @param sizeBytes size (in bytes) of the section in the input file
-         * @param attrs attributes contained in the section
-         */
-        public Section(int startOffset, int sizeBytes, List<Attribute> attrs) {
-            mStartOffset = startOffset;
-            mSizeBytes = sizeBytes;
-            String sectionName = null;
-            if (!attrs.isEmpty()) {
-                Attribute firstAttr = attrs.get(0);
-                if ("Name".equalsIgnoreCase(firstAttr.getName())) {
-                    sectionName = firstAttr.getValue();
-                }
-            }
-            mName = sectionName;
-            mAttributes = Collections.unmodifiableList(new ArrayList<>(attrs));
-        }
-
-        public String getName() {
-            return mName;
-        }
-
-        /**
-         * Returns the offset (in bytes) at which this section starts in the input.
-         */
-        public int getStartOffset() {
-            return mStartOffset;
-        }
-
-        /**
-         * Returns the size (in bytes) of this section in the input.
-         */
-        public int getSizeBytes() {
-            return mSizeBytes;
-        }
-
-        /**
-         * Returns this section's attributes, in the order in which they appear in the input.
-         */
-        public List<Attribute> getAttributes() {
-            return mAttributes;
-        }
-
-        /**
-         * Returns the value of the specified attribute in this section or {@code null} if this
-         * section does not contain a matching attribute.
-         */
-        public String getAttributeValue(Attributes.Name name) {
-            return getAttributeValue(name.toString());
-        }
-
-        /**
-         * Returns the value of the specified attribute in this section or {@code null} if this
-         * section does not contain a matching attribute.
-         *
-         * @param name name of the attribute. Attribute names are case-insensitive.
-         */
-        public String getAttributeValue(String name) {
-            for (Attribute attr : mAttributes) {
-                if (attr.getName().equalsIgnoreCase(name)) {
-                    return attr.getValue();
-                }
-            }
-            return null;
-        }
-    }
-}
diff --git a/tools/apksigner/core/src/com/android/apksigner/core/internal/jar/ManifestWriter.java b/tools/apksigner/core/src/com/android/apksigner/core/internal/jar/ManifestWriter.java
deleted file mode 100644
index 13b1aaf..0000000
--- a/tools/apksigner/core/src/com/android/apksigner/core/internal/jar/ManifestWriter.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.apksigner.core.internal.jar;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.charset.StandardCharsets;
-import java.util.Map;
-import java.util.Set;
-import java.util.SortedMap;
-import java.util.TreeMap;
-import java.util.jar.Attributes;
-
-/**
- * Producer of {@code META-INF/MANIFEST.MF} file.
- *
- * @see <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#JAR_Manifest">JAR Manifest format</a>
- */
-public abstract class ManifestWriter {
-
-    private static final byte[] CRLF = new byte[] {'\r', '\n'};
-    private static final int MAX_LINE_LENGTH = 70;
-
-    private ManifestWriter() {}
-
-    public static void writeMainSection(OutputStream out, Attributes attributes)
-            throws IOException {
-
-        // Main section must start with the Manifest-Version attribute.
-        // See https://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#Signed_JAR_File.
-        String manifestVersion = attributes.getValue(Attributes.Name.MANIFEST_VERSION);
-        if (manifestVersion == null) {
-            throw new IllegalArgumentException(
-                    "Mandatory " + Attributes.Name.MANIFEST_VERSION + " attribute missing");
-        }
-        writeAttribute(out, Attributes.Name.MANIFEST_VERSION, manifestVersion);
-
-        if (attributes.size() > 1) {
-            SortedMap<String, String> namedAttributes = getAttributesSortedByName(attributes);
-            namedAttributes.remove(Attributes.Name.MANIFEST_VERSION.toString());
-            writeAttributes(out, namedAttributes);
-        }
-        writeSectionDelimiter(out);
-    }
-
-    public static void writeIndividualSection(OutputStream out, String name, Attributes attributes)
-            throws IOException {
-        writeAttribute(out, "Name", name);
-
-        if (!attributes.isEmpty()) {
-            writeAttributes(out, getAttributesSortedByName(attributes));
-        }
-        writeSectionDelimiter(out);
-    }
-
-    static void writeSectionDelimiter(OutputStream out) throws IOException {
-        out.write(CRLF);
-    }
-
-    static void writeAttribute(OutputStream  out, Attributes.Name name, String value)
-            throws IOException {
-        writeAttribute(out, name.toString(), value);
-    }
-
-    private static void writeAttribute(OutputStream  out, String name, String value)
-            throws IOException {
-        writeLine(out, name + ": " + value);
-    }
-
-    private static void writeLine(OutputStream  out, String line) throws IOException {
-        byte[] lineBytes = line.getBytes(StandardCharsets.UTF_8);
-        int offset = 0;
-        int remaining = lineBytes.length;
-        boolean firstLine = true;
-        while (remaining > 0) {
-            int chunkLength;
-            if (firstLine) {
-                // First line
-                chunkLength = Math.min(remaining, MAX_LINE_LENGTH);
-            } else {
-                // Continuation line
-                out.write(CRLF);
-                out.write(' ');
-                chunkLength = Math.min(remaining, MAX_LINE_LENGTH - 1);
-            }
-            out.write(lineBytes, offset, chunkLength);
-            offset += chunkLength;
-            remaining -= chunkLength;
-            firstLine = false;
-        }
-        out.write(CRLF);
-    }
-
-    static SortedMap<String, String> getAttributesSortedByName(Attributes attributes) {
-        Set<Map.Entry<Object, Object>> attributesEntries = attributes.entrySet();
-        SortedMap<String, String> namedAttributes = new TreeMap<String, String>();
-        for (Map.Entry<Object, Object> attribute : attributesEntries) {
-            String attrName = attribute.getKey().toString();
-            String attrValue = attribute.getValue().toString();
-            namedAttributes.put(attrName, attrValue);
-        }
-        return namedAttributes;
-    }
-
-    static void writeAttributes(
-            OutputStream out, SortedMap<String, String> attributesSortedByName) throws IOException {
-        for (Map.Entry<String, String> attribute : attributesSortedByName.entrySet()) {
-            String attrName = attribute.getKey();
-            String attrValue = attribute.getValue();
-            writeAttribute(out, attrName, attrValue);
-        }
-    }
-}
diff --git a/tools/apksigner/core/src/com/android/apksigner/core/internal/jar/SignatureFileWriter.java b/tools/apksigner/core/src/com/android/apksigner/core/internal/jar/SignatureFileWriter.java
deleted file mode 100644
index 94ae280..0000000
--- a/tools/apksigner/core/src/com/android/apksigner/core/internal/jar/SignatureFileWriter.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.apksigner.core.internal.jar;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.SortedMap;
-import java.util.jar.Attributes;
-
-/**
- * Producer of JAR signature file ({@code *.SF}).
- *
- * @see <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#JAR_Manifest">JAR Manifest format</a>
- */
-public abstract class SignatureFileWriter {
-    private SignatureFileWriter() {}
-
-    public static void writeMainSection(OutputStream out, Attributes attributes)
-            throws IOException {
-
-        // Main section must start with the Signature-Version attribute.
-        // See https://docs.oracle.com/javase/8/docs/technotes/guides/jar/jar.html#Signed_JAR_File.
-        String signatureVersion = attributes.getValue(Attributes.Name.SIGNATURE_VERSION);
-        if (signatureVersion == null) {
-            throw new IllegalArgumentException(
-                    "Mandatory " + Attributes.Name.SIGNATURE_VERSION + " attribute missing");
-        }
-        ManifestWriter.writeAttribute(out, Attributes.Name.SIGNATURE_VERSION, signatureVersion);
-
-        if (attributes.size() > 1) {
-            SortedMap<String, String> namedAttributes =
-                    ManifestWriter.getAttributesSortedByName(attributes);
-            namedAttributes.remove(Attributes.Name.SIGNATURE_VERSION.toString());
-            ManifestWriter.writeAttributes(out, namedAttributes);
-        }
-        writeSectionDelimiter(out);
-    }
-
-    public static void writeIndividualSection(OutputStream out, String name, Attributes attributes)
-            throws IOException {
-        ManifestWriter.writeIndividualSection(out, name, attributes);
-    }
-
-    public static void writeSectionDelimiter(OutputStream out) throws IOException {
-        ManifestWriter.writeSectionDelimiter(out);
-    }
-}
diff --git a/tools/apksigner/core/src/com/android/apksigner/core/internal/util/AndroidSdkVersion.java b/tools/apksigner/core/src/com/android/apksigner/core/internal/util/AndroidSdkVersion.java
deleted file mode 100644
index 3e9d039..0000000
--- a/tools/apksigner/core/src/com/android/apksigner/core/internal/util/AndroidSdkVersion.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.apksigner.core.internal.util;
-
-/**
- * Android SDK version / API Level constants.
- */
-public abstract class AndroidSdkVersion {
-
-    /** Hidden constructor to prevent instantiation. */
-    private AndroidSdkVersion() {}
-
-    /** Android 2.3. */
-    public static final int GINGERBREAD = 9;
-
-    /** Android 4.3. The revenge of the beans. */
-    public static final int JELLY_BEAN_MR2 = 18;
-
-    /** Android 5.0. A flat one with beautiful shadows. But still tasty. */
-    public static final int LOLLIPOP = 21;
-
-    // TODO: Update Javadoc / constant name once N is assigned a proper name / version code.
-    /** Android N. */
-    public static final int N = 24;
-}
diff --git a/tools/apksigner/core/src/com/android/apksigner/core/internal/util/ByteBufferDataSource.java b/tools/apksigner/core/src/com/android/apksigner/core/internal/util/ByteBufferDataSource.java
deleted file mode 100644
index b2d9ca1..0000000
--- a/tools/apksigner/core/src/com/android/apksigner/core/internal/util/ByteBufferDataSource.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.apksigner.core.internal.util;
-
-import com.android.apksigner.core.util.DataSink;
-import com.android.apksigner.core.util.DataSource;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-
-/**
- * {@link DataSource} backed by a {@link ByteBuffer}.
- */
-public class ByteBufferDataSource implements DataSource {
-
-    private final ByteBuffer mBuffer;
-    private final int mSize;
-
-    /**
-     * Constructs a new {@code ByteBufferDigestSource} based on the data contained in the provided
-     * buffer between the buffer's position and limit.
-     */
-    public ByteBufferDataSource(ByteBuffer buffer) {
-        this(buffer, true);
-    }
-
-    /**
-     * Constructs a new {@code ByteBufferDigestSource} based on the data contained in the provided
-     * buffer between the buffer's position and limit.
-     */
-    private ByteBufferDataSource(ByteBuffer buffer, boolean sliceRequired) {
-        mBuffer = (sliceRequired) ? buffer.slice() : buffer;
-        mSize = buffer.remaining();
-    }
-
-    @Override
-    public long size() {
-        return mSize;
-    }
-
-    @Override
-    public ByteBuffer getByteBuffer(long offset, int size) {
-        checkChunkValid(offset, size);
-
-        // checkChunkValid ensures that it's OK to cast offset to int.
-        int chunkPosition = (int) offset;
-        int chunkLimit = chunkPosition + size;
-        // Creating a slice of ByteBuffer modifies the state of the source ByteBuffer (position
-        // and limit fields, to be more specific). We thus use synchronization around these
-        // state-changing operations to make instances of this class thread-safe.
-        synchronized (mBuffer) {
-            // ByteBuffer.limit(int) and .position(int) check that that the position >= limit
-            // invariant is not broken. Thus, the only way to safely change position and limit
-            // without caring about their current values is to first set position to 0 or set the
-            // limit to capacity.
-            mBuffer.position(0);
-
-            mBuffer.limit(chunkLimit);
-            mBuffer.position(chunkPosition);
-            return mBuffer.slice();
-        }
-    }
-
-    @Override
-    public void copyTo(long offset, int size, ByteBuffer dest) {
-        dest.put(getByteBuffer(offset, size));
-    }
-
-    @Override
-    public void feed(long offset, long size, DataSink sink) throws IOException {
-        if ((size < 0) || (size > mSize)) {
-            throw new IllegalArgumentException("size: " + size + ", source size: " + mSize);
-        }
-        sink.consume(getByteBuffer(offset, (int) size));
-    }
-
-    @Override
-    public ByteBufferDataSource slice(long offset, long size) {
-        if ((offset == 0) && (size == mSize)) {
-            return this;
-        }
-        if ((size < 0) || (size > mSize)) {
-            throw new IllegalArgumentException("size: " + size + ", source size: " + mSize);
-        }
-        return new ByteBufferDataSource(
-                getByteBuffer(offset, (int) size),
-                false // no need to slice -- it's already a slice
-                );
-    }
-
-    private void checkChunkValid(long offset, long size) {
-        if (offset < 0) {
-            throw new IllegalArgumentException("offset: " + offset);
-        }
-        if (size < 0) {
-            throw new IllegalArgumentException("size: " + size);
-        }
-        if (offset > mSize) {
-            throw new IllegalArgumentException(
-                    "offset (" + offset + ") > source size (" + mSize + ")");
-        }
-        long endOffset = offset + size;
-        if (endOffset < offset) {
-            throw new IllegalArgumentException(
-                    "offset (" + offset + ") + size (" + size + ") overflow");
-        }
-        if (endOffset > mSize) {
-            throw new IllegalArgumentException(
-                    "offset (" + offset + ") + size (" + size + ") > source size (" + mSize  +")");
-        }
-    }
-}
diff --git a/tools/apksigner/core/src/com/android/apksigner/core/internal/util/ByteBufferSink.java b/tools/apksigner/core/src/com/android/apksigner/core/internal/util/ByteBufferSink.java
deleted file mode 100644
index 8c57905..0000000
--- a/tools/apksigner/core/src/com/android/apksigner/core/internal/util/ByteBufferSink.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.apksigner.core.internal.util;
-
-import com.android.apksigner.core.util.DataSink;
-
-import java.io.IOException;
-import java.nio.BufferOverflowException;
-import java.nio.ByteBuffer;
-
-/**
- * Data sink which stores all received data into the associated {@link ByteBuffer}.
- */
-public class ByteBufferSink implements DataSink {
-
-    private final ByteBuffer mBuffer;
-
-    public ByteBufferSink(ByteBuffer buffer) {
-        mBuffer = buffer;
-    }
-
-    @Override
-    public void consume(byte[] buf, int offset, int length) throws IOException {
-        try {
-            mBuffer.put(buf, offset, length);
-        } catch (BufferOverflowException e) {
-            throw new IOException(
-                    "Insufficient space in output buffer for " + length + " bytes", e);
-        }
-    }
-
-    @Override
-    public void consume(ByteBuffer buf) throws IOException {
-        int length = buf.remaining();
-        try {
-            mBuffer.put(buf);
-        } catch (BufferOverflowException e) {
-            throw new IOException(
-                    "Insufficient space in output buffer for " + length + " bytes", e);
-        }
-    }
-}
diff --git a/tools/apksigner/core/src/com/android/apksigner/core/internal/util/DelegatingX509Certificate.java b/tools/apksigner/core/src/com/android/apksigner/core/internal/util/DelegatingX509Certificate.java
deleted file mode 100644
index 936cfa9..0000000
--- a/tools/apksigner/core/src/com/android/apksigner/core/internal/util/DelegatingX509Certificate.java
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.apksigner.core.internal.util;
-
-import java.math.BigInteger;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.Principal;
-import java.security.PublicKey;
-import java.security.SignatureException;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateExpiredException;
-import java.security.cert.CertificateNotYetValidException;
-import java.security.cert.X509Certificate;
-import java.util.Date;
-import java.util.Set;
-
-/**
- * {@link X509Certificate} which delegates all method invocations to the provided delegate
- * {@code X509Certificate}.
- */
-public class DelegatingX509Certificate extends X509Certificate {
-    private final X509Certificate mDelegate;
-
-    public DelegatingX509Certificate(X509Certificate delegate) {
-        this.mDelegate = delegate;
-    }
-
-    @Override
-    public Set<String> getCriticalExtensionOIDs() {
-        return mDelegate.getCriticalExtensionOIDs();
-    }
-
-    @Override
-    public byte[] getExtensionValue(String oid) {
-        return mDelegate.getExtensionValue(oid);
-    }
-
-    @Override
-    public Set<String> getNonCriticalExtensionOIDs() {
-        return mDelegate.getNonCriticalExtensionOIDs();
-    }
-
-    @Override
-    public boolean hasUnsupportedCriticalExtension() {
-        return mDelegate.hasUnsupportedCriticalExtension();
-    }
-
-    @Override
-    public void checkValidity()
-            throws CertificateExpiredException, CertificateNotYetValidException {
-        mDelegate.checkValidity();
-    }
-
-    @Override
-    public void checkValidity(Date date)
-            throws CertificateExpiredException, CertificateNotYetValidException {
-        mDelegate.checkValidity(date);
-    }
-
-    @Override
-    public int getVersion() {
-        return mDelegate.getVersion();
-    }
-
-    @Override
-    public BigInteger getSerialNumber() {
-        return mDelegate.getSerialNumber();
-    }
-
-    @Override
-    public Principal getIssuerDN() {
-        return mDelegate.getIssuerDN();
-    }
-
-    @Override
-    public Principal getSubjectDN() {
-        return mDelegate.getSubjectDN();
-    }
-
-    @Override
-    public Date getNotBefore() {
-        return mDelegate.getNotBefore();
-    }
-
-    @Override
-    public Date getNotAfter() {
-        return mDelegate.getNotAfter();
-    }
-
-    @Override
-    public byte[] getTBSCertificate() throws CertificateEncodingException {
-        return mDelegate.getTBSCertificate();
-    }
-
-    @Override
-    public byte[] getSignature() {
-        return mDelegate.getSignature();
-    }
-
-    @Override
-    public String getSigAlgName() {
-        return mDelegate.getSigAlgName();
-    }
-
-    @Override
-    public String getSigAlgOID() {
-        return mDelegate.getSigAlgOID();
-    }
-
-    @Override
-    public byte[] getSigAlgParams() {
-        return mDelegate.getSigAlgParams();
-    }
-
-    @Override
-    public boolean[] getIssuerUniqueID() {
-        return mDelegate.getIssuerUniqueID();
-    }
-
-    @Override
-    public boolean[] getSubjectUniqueID() {
-        return mDelegate.getSubjectUniqueID();
-    }
-
-    @Override
-    public boolean[] getKeyUsage() {
-        return mDelegate.getKeyUsage();
-    }
-
-    @Override
-    public int getBasicConstraints() {
-        return mDelegate.getBasicConstraints();
-    }
-
-    @Override
-    public byte[] getEncoded() throws CertificateEncodingException {
-        return mDelegate.getEncoded();
-    }
-
-    @Override
-    public void verify(PublicKey key) throws CertificateException, NoSuchAlgorithmException,
-            InvalidKeyException, NoSuchProviderException, SignatureException {
-        mDelegate.verify(key);
-    }
-
-    @Override
-    public void verify(PublicKey key, String sigProvider)
-            throws CertificateException, NoSuchAlgorithmException, InvalidKeyException,
-            NoSuchProviderException, SignatureException {
-        mDelegate.verify(key, sigProvider);
-    }
-
-    @Override
-    public String toString() {
-        return mDelegate.toString();
-    }
-
-    @Override
-    public PublicKey getPublicKey() {
-        return mDelegate.getPublicKey();
-    }
-}
diff --git a/tools/apksigner/core/src/com/android/apksigner/core/internal/util/InclusiveIntRange.java b/tools/apksigner/core/src/com/android/apksigner/core/internal/util/InclusiveIntRange.java
deleted file mode 100644
index baf3655..0000000
--- a/tools/apksigner/core/src/com/android/apksigner/core/internal/util/InclusiveIntRange.java
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.apksigner.core.internal.util;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Inclusive interval of integers.
- */
-public class InclusiveIntRange {
-    private final int min;
-    private final int max;
-
-    private InclusiveIntRange(int min, int max) {
-        this.min = min;
-        this.max = max;
-    }
-
-    public int getMin() {
-        return min;
-    }
-
-    public int getMax() {
-        return max;
-    }
-
-    public static InclusiveIntRange fromTo(int min, int max) {
-        return new InclusiveIntRange(min, max);
-    }
-
-    public static InclusiveIntRange from(int min) {
-        return new InclusiveIntRange(min, Integer.MAX_VALUE);
-    }
-
-    public List<InclusiveIntRange> getValuesNotIn(
-            List<InclusiveIntRange> sortedNonOverlappingRanges) {
-        if (sortedNonOverlappingRanges.isEmpty()) {
-            return Collections.singletonList(this);
-        }
-
-        int testValue = min;
-        List<InclusiveIntRange> result = null;
-        for (InclusiveIntRange range : sortedNonOverlappingRanges) {
-            int rangeMax = range.max;
-            if (testValue > rangeMax) {
-                continue;
-            }
-            int rangeMin = range.min;
-            if (testValue < range.min) {
-                if (result == null) {
-                    result = new ArrayList<>();
-                }
-                result.add(fromTo(testValue, rangeMin - 1));
-            }
-            if (rangeMax >= max) {
-                return (result != null) ? result : Collections.emptyList();
-            }
-            testValue = rangeMax + 1;
-        }
-        if (testValue <= max) {
-            if (result == null) {
-                result = new ArrayList<>(1);
-            }
-            result.add(fromTo(testValue, max));
-        }
-        return (result != null) ? result : Collections.emptyList();
-    }
-
-    @Override
-    public String toString() {
-        return "[" + min + ", " + ((max < Integer.MAX_VALUE) ? (max + "]") : "\u221e)");
-    }
-}
diff --git a/tools/apksigner/core/src/com/android/apksigner/core/internal/util/MessageDigestSink.java b/tools/apksigner/core/src/com/android/apksigner/core/internal/util/MessageDigestSink.java
deleted file mode 100644
index 45bb30e..0000000
--- a/tools/apksigner/core/src/com/android/apksigner/core/internal/util/MessageDigestSink.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.apksigner.core.internal.util;
-
-import com.android.apksigner.core.util.DataSink;
-
-import java.nio.ByteBuffer;
-import java.security.MessageDigest;
-
-/**
- * Data sink which feeds all received data into the associated {@link MessageDigest} instances. Each
- * {@code MessageDigest} instance receives the same data.
- */
-public class MessageDigestSink implements DataSink {
-
-    private final MessageDigest[] mMessageDigests;
-
-    public MessageDigestSink(MessageDigest[] digests) {
-        mMessageDigests = digests;
-    }
-
-    @Override
-    public void consume(byte[] buf, int offset, int length) {
-        for (MessageDigest md : mMessageDigests) {
-            md.update(buf, offset, length);
-        }
-    }
-
-    @Override
-    public void consume(ByteBuffer buf) {
-        int originalPosition = buf.position();
-        for (MessageDigest md : mMessageDigests) {
-            // Reset the position back to the original because the previous iteration's
-            // MessageDigest.update set the buffer's position to the buffer's limit.
-            buf.position(originalPosition);
-            md.update(buf);
-        }
-    }
-}
diff --git a/tools/apksigner/core/src/com/android/apksigner/core/internal/util/OutputStreamDataSink.java b/tools/apksigner/core/src/com/android/apksigner/core/internal/util/OutputStreamDataSink.java
deleted file mode 100644
index bf18ca0..0000000
--- a/tools/apksigner/core/src/com/android/apksigner/core/internal/util/OutputStreamDataSink.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.apksigner.core.internal.util;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.ByteBuffer;
-
-import com.android.apksigner.core.util.DataSink;
-
-/**
- * {@link DataSink} which outputs received data into the associated {@link OutputStream}.
- */
-public class OutputStreamDataSink implements DataSink {
-
-    private static final int MAX_READ_CHUNK_SIZE = 65536;
-
-    private final OutputStream mOut;
-
-    /**
-     * Constructs a new {@code OutputStreamDataSink} which outputs received data into the provided
-     * {@link OutputStream}.
-     */
-    public OutputStreamDataSink(OutputStream out) {
-        if (out == null) {
-            throw new NullPointerException("out == null");
-        }
-        mOut = out;
-    }
-
-    /**
-     * Returns {@link OutputStream} into which this data sink outputs received data.
-     */
-    public OutputStream getOutputStream() {
-        return mOut;
-    }
-
-    @Override
-    public void consume(byte[] buf, int offset, int length) throws IOException {
-        mOut.write(buf, offset, length);
-    }
-
-    @Override
-    public void consume(ByteBuffer buf) throws IOException {
-        if (!buf.hasRemaining()) {
-            return;
-        }
-
-        if (buf.hasArray()) {
-            mOut.write(
-                    buf.array(),
-                    buf.arrayOffset() + buf.position(),
-                    buf.remaining());
-            buf.position(buf.limit());
-        } else {
-            byte[] tmp = new byte[Math.min(buf.remaining(), MAX_READ_CHUNK_SIZE)];
-            while (buf.hasRemaining()) {
-                int chunkSize = Math.min(buf.remaining(), tmp.length);
-                buf.get(tmp, 0, chunkSize);
-                mOut.write(tmp, 0, chunkSize);
-            }
-        }
-    }
-}
diff --git a/tools/apksigner/core/src/com/android/apksigner/core/internal/util/Pair.java b/tools/apksigner/core/src/com/android/apksigner/core/internal/util/Pair.java
deleted file mode 100644
index d59af41..0000000
--- a/tools/apksigner/core/src/com/android/apksigner/core/internal/util/Pair.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.apksigner.core.internal.util;
-
-/**
- * Pair of two elements.
- */
-public final class Pair<A, B> {
-    private final A mFirst;
-    private final B mSecond;
-
-    private Pair(A first, B second) {
-        mFirst = first;
-        mSecond = second;
-    }
-
-    public static <A, B> Pair<A, B> of(A first, B second) {
-        return new Pair<A, B>(first, second);
-    }
-
-    public A getFirst() {
-        return mFirst;
-    }
-
-    public B getSecond() {
-        return mSecond;
-    }
-
-    @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + ((mFirst == null) ? 0 : mFirst.hashCode());
-        result = prime * result + ((mSecond == null) ? 0 : mSecond.hashCode());
-        return result;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null) {
-            return false;
-        }
-        if (getClass() != obj.getClass()) {
-            return false;
-        }
-        @SuppressWarnings("rawtypes")
-        Pair other = (Pair) obj;
-        if (mFirst == null) {
-            if (other.mFirst != null) {
-                return false;
-            }
-        } else if (!mFirst.equals(other.mFirst)) {
-            return false;
-        }
-        if (mSecond == null) {
-            if (other.mSecond != null) {
-                return false;
-            }
-        } else if (!mSecond.equals(other.mSecond)) {
-            return false;
-        }
-        return true;
-    }
-}
diff --git a/tools/apksigner/core/src/com/android/apksigner/core/internal/util/RandomAccessFileDataSink.java b/tools/apksigner/core/src/com/android/apksigner/core/internal/util/RandomAccessFileDataSink.java
deleted file mode 100644
index 2198492..0000000
--- a/tools/apksigner/core/src/com/android/apksigner/core/internal/util/RandomAccessFileDataSink.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.apksigner.core.internal.util;
-
-import com.android.apksigner.core.util.DataSink;
-
-import java.io.IOException;
-import java.io.RandomAccessFile;
-import java.nio.ByteBuffer;
-import java.nio.channels.FileChannel;
-
-/**
- * {@link DataSink} which outputs received data into the associated file, sequentially.
- */
-public class RandomAccessFileDataSink implements DataSink {
-
-    private final RandomAccessFile mFile;
-    private final FileChannel mFileChannel;
-    private long mPosition;
-
-    /**
-     * Constructs a new {@code RandomAccessFileDataSink} which stores output starting from the
-     * beginning of the provided file.
-     */
-    public RandomAccessFileDataSink(RandomAccessFile file) {
-        this(file, 0);
-    }
-
-    /**
-     * Constructs a new {@code RandomAccessFileDataSink} which stores output starting from the
-     * specified position of the provided file.
-     */
-    public RandomAccessFileDataSink(RandomAccessFile file, long startPosition) {
-        if (file == null) {
-            throw new NullPointerException("file == null");
-        }
-        if (startPosition < 0) {
-            throw new IllegalArgumentException("startPosition: " + startPosition);
-        }
-        mFile = file;
-        mFileChannel = file.getChannel();
-        mPosition = startPosition;
-    }
-
-    @Override
-    public void consume(byte[] buf, int offset, int length) throws IOException {
-        if (length == 0) {
-            return;
-        }
-
-        synchronized (mFile) {
-            mFile.seek(mPosition);
-            mFile.write(buf, offset, length);
-            mPosition += length;
-        }
-    }
-
-    @Override
-    public void consume(ByteBuffer buf) throws IOException {
-        int length = buf.remaining();
-        if (length == 0) {
-            return;
-        }
-
-        synchronized (mFile) {
-            mFile.seek(mPosition);
-            while (buf.hasRemaining()) {
-                mFileChannel.write(buf);
-            }
-            mPosition += length;
-        }
-    }
-}
diff --git a/tools/apksigner/core/src/com/android/apksigner/core/internal/util/RandomAccessFileDataSource.java b/tools/apksigner/core/src/com/android/apksigner/core/internal/util/RandomAccessFileDataSource.java
deleted file mode 100644
index 208033d..0000000
--- a/tools/apksigner/core/src/com/android/apksigner/core/internal/util/RandomAccessFileDataSource.java
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.apksigner.core.internal.util;
-
-import java.io.IOException;
-import java.io.RandomAccessFile;
-import java.nio.ByteBuffer;
-import java.nio.channels.FileChannel;
-
-import com.android.apksigner.core.util.DataSink;
-import com.android.apksigner.core.util.DataSource;
-
-/**
- * {@link DataSource} backed by a {@link RandomAccessFile}.
- */
-public class RandomAccessFileDataSource implements DataSource {
-
-    private static final int MAX_READ_CHUNK_SIZE = 65536;
-
-    private final RandomAccessFile mFile;
-    private final long mOffset;
-    private final long mSize;
-
-    /**
-     * Constructs a new {@code RandomAccessFileDataSource} based on the data contained in the
-     * specified the whole file. Changes to the contents of the file, including the size of the
-     * file, will be visible in this data source.
-     */
-    public RandomAccessFileDataSource(RandomAccessFile file) {
-        mFile = file;
-        mOffset = 0;
-        mSize = -1;
-    }
-
-    /**
-     * Constructs a new {@code RandomAccessFileDataSource} based on the data contained in the
-     * specified region of the provided file. Changes to the contents of the file will be visible in
-     * this data source.
-     */
-    public RandomAccessFileDataSource(RandomAccessFile file, long offset, long size) {
-        if (offset < 0) {
-            throw new IllegalArgumentException("offset: " + size);
-        }
-        if (size < 0) {
-            throw new IllegalArgumentException("size: " + size);
-        }
-        mFile = file;
-        mOffset = offset;
-        mSize = size;
-    }
-
-    @Override
-    public long size() {
-        if (mSize == -1) {
-            try {
-                return mFile.length();
-            } catch (IOException e) {
-                return 0;
-            }
-        } else {
-            return mSize;
-        }
-    }
-
-    @Override
-    public RandomAccessFileDataSource slice(long offset, long size) {
-        long sourceSize = size();
-        checkChunkValid(offset, size, sourceSize);
-        if ((offset == 0) && (size == sourceSize)) {
-            return this;
-        }
-
-        return new RandomAccessFileDataSource(mFile, mOffset + offset, size);
-    }
-
-    @Override
-    public void feed(long offset, long size, DataSink sink) throws IOException {
-        long sourceSize = size();
-        checkChunkValid(offset, size, sourceSize);
-        if (size == 0) {
-            return;
-        }
-
-        long chunkOffsetInFile = mOffset + offset;
-        long remaining = size;
-        byte[] buf = new byte[(int) Math.min(remaining, MAX_READ_CHUNK_SIZE)];
-        while (remaining > 0) {
-            int chunkSize = (int) Math.min(remaining, buf.length);
-            synchronized (mFile) {
-                mFile.seek(chunkOffsetInFile);
-                mFile.readFully(buf, 0, chunkSize);
-            }
-            sink.consume(buf, 0, chunkSize);
-            chunkOffsetInFile += chunkSize;
-            remaining -= chunkSize;
-        }
-    }
-
-    @Override
-    public void copyTo(long offset, int size, ByteBuffer dest) throws IOException {
-        long sourceSize = size();
-        checkChunkValid(offset, size, sourceSize);
-        if (size == 0) {
-            return;
-        }
-
-        long offsetInFile = mOffset + offset;
-        int remaining = size;
-        FileChannel fileChannel = mFile.getChannel();
-        while (remaining > 0) {
-            int chunkSize;
-            synchronized (mFile) {
-                fileChannel.position(offsetInFile);
-                chunkSize = fileChannel.read(dest);
-            }
-            offsetInFile += chunkSize;
-            remaining -= chunkSize;
-        }
-    }
-
-    @Override
-    public ByteBuffer getByteBuffer(long offset, int size) throws IOException {
-        ByteBuffer result = ByteBuffer.allocate(size);
-        copyTo(offset, size, result);
-        result.flip();
-        return result;
-    }
-
-    private static void checkChunkValid(long offset, long size, long sourceSize) {
-        if (offset < 0) {
-            throw new IllegalArgumentException("offset: " + offset);
-        }
-        if (size < 0) {
-            throw new IllegalArgumentException("size: " + size);
-        }
-        if (offset > sourceSize) {
-            throw new IllegalArgumentException(
-                    "offset (" + offset + ") > source size (" + sourceSize + ")");
-        }
-        long endOffset = offset + size;
-        if (endOffset < offset) {
-            throw new IllegalArgumentException(
-                    "offset (" + offset + ") + size (" + size + ") overflow");
-        }
-        if (endOffset > sourceSize) {
-            throw new IllegalArgumentException(
-                    "offset (" + offset + ") + size (" + size
-                            + ") > source size (" + sourceSize  +")");
-        }
-    }
-}
diff --git a/tools/apksigner/core/src/com/android/apksigner/core/internal/zip/CentralDirectoryRecord.java b/tools/apksigner/core/src/com/android/apksigner/core/internal/zip/CentralDirectoryRecord.java
deleted file mode 100644
index 141d01e..0000000
--- a/tools/apksigner/core/src/com/android/apksigner/core/internal/zip/CentralDirectoryRecord.java
+++ /dev/null
@@ -1,281 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.apksigner.core.internal.zip;
-
-import com.android.apksigner.core.zip.ZipFormatException;
-
-import java.nio.BufferUnderflowException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.charset.StandardCharsets;
-import java.util.Comparator;
-
-/**
- * ZIP Central Directory (CD) Record.
- */
-public class CentralDirectoryRecord {
-
-    /**
-     * Comparator which compares records by the offset of the corresponding Local File Header in the
-     * archive.
-     */
-    public static final Comparator<CentralDirectoryRecord> BY_LOCAL_FILE_HEADER_OFFSET_COMPARATOR =
-            new ByLocalFileHeaderOffsetComparator();
-
-    private static final int RECORD_SIGNATURE = 0x02014b50;
-    private static final int HEADER_SIZE_BYTES = 46;
-
-    private static final int LAST_MODIFICATION_TIME_OFFSET =  12;
-    private static final int LOCAL_FILE_HEADER_OFFSET_OFFSET = 42;
-    private static final int NAME_OFFSET = HEADER_SIZE_BYTES;
-
-    private final ByteBuffer mData;
-    private final int mLastModificationTime;
-    private final int mLastModificationDate;
-    private final long mCrc32;
-    private final long mCompressedSize;
-    private final long mUncompressedSize;
-    private final long mLocalFileHeaderOffset;
-    private final String mName;
-    private final int mNameSizeBytes;
-
-    private CentralDirectoryRecord(
-            ByteBuffer data,
-            int lastModificationTime,
-            int lastModificationDate,
-            long crc32,
-            long compressedSize,
-            long uncompressedSize,
-            long localFileHeaderOffset,
-            String name,
-            int nameSizeBytes) {
-        mData = data;
-        mLastModificationDate = lastModificationDate;
-        mLastModificationTime = lastModificationTime;
-        mCrc32 = crc32;
-        mCompressedSize = compressedSize;
-        mUncompressedSize = uncompressedSize;
-        mLocalFileHeaderOffset = localFileHeaderOffset;
-        mName = name;
-        mNameSizeBytes = nameSizeBytes;
-    }
-
-    public int getSize() {
-        return mData.remaining();
-    }
-
-    public String getName() {
-        return mName;
-    }
-
-    public int getNameSizeBytes() {
-        return mNameSizeBytes;
-    }
-
-    public int getLastModificationTime() {
-        return mLastModificationTime;
-    }
-
-    public int getLastModificationDate() {
-        return mLastModificationDate;
-    }
-
-    public long getCrc32() {
-        return mCrc32;
-    }
-
-    public long getCompressedSize() {
-        return mCompressedSize;
-    }
-
-    public long getUncompressedSize() {
-        return mUncompressedSize;
-    }
-
-    public long getLocalFileHeaderOffset() {
-        return mLocalFileHeaderOffset;
-    }
-
-    /**
-     * Returns the Central Directory Record starting at the current position of the provided buffer
-     * and advances the buffer's position immediately past the end of the record.
-     */
-    public static CentralDirectoryRecord getRecord(ByteBuffer buf) throws ZipFormatException {
-        ZipUtils.assertByteOrderLittleEndian(buf);
-        if (buf.remaining() < HEADER_SIZE_BYTES) {
-            throw new ZipFormatException(
-                    "Input too short. Need at least: " + HEADER_SIZE_BYTES
-                            + " bytes, available: " + buf.remaining() + " bytes",
-                    new BufferUnderflowException());
-        }
-        int originalPosition = buf.position();
-        int recordSignature = buf.getInt();
-        if (recordSignature != RECORD_SIGNATURE) {
-            throw new ZipFormatException(
-                    "Not a Central Directory record. Signature: 0x"
-                            + Long.toHexString(recordSignature & 0xffffffffL));
-        }
-        buf.position(originalPosition + LAST_MODIFICATION_TIME_OFFSET);
-        int lastModificationTime = ZipUtils.getUnsignedInt16(buf);
-        int lastModificationDate = ZipUtils.getUnsignedInt16(buf);
-        long crc32 = ZipUtils.getUnsignedInt32(buf);
-        long compressedSize = ZipUtils.getUnsignedInt32(buf);
-        long uncompressedSize = ZipUtils.getUnsignedInt32(buf);
-        int nameSize = ZipUtils.getUnsignedInt16(buf);
-        int extraSize = ZipUtils.getUnsignedInt16(buf);
-        int commentSize = ZipUtils.getUnsignedInt16(buf);
-        buf.position(originalPosition + LOCAL_FILE_HEADER_OFFSET_OFFSET);
-        long localFileHeaderOffset = ZipUtils.getUnsignedInt32(buf);
-        buf.position(originalPosition);
-        int recordSize = HEADER_SIZE_BYTES + nameSize + extraSize + commentSize;
-        if (recordSize > buf.remaining()) {
-            throw new ZipFormatException(
-                    "Input too short. Need: " + recordSize + " bytes, available: "
-                            + buf.remaining() + " bytes",
-                    new BufferUnderflowException());
-        }
-        String name = getName(buf, originalPosition + NAME_OFFSET, nameSize);
-        buf.position(originalPosition);
-        int originalLimit = buf.limit();
-        int recordEndInBuf = originalPosition + recordSize;
-        ByteBuffer recordBuf;
-        try {
-            buf.limit(recordEndInBuf);
-            recordBuf = buf.slice();
-        } finally {
-            buf.limit(originalLimit);
-        }
-        // Consume this record
-        buf.position(recordEndInBuf);
-        return new CentralDirectoryRecord(
-                recordBuf,
-                lastModificationTime,
-                lastModificationDate,
-                crc32,
-                compressedSize,
-                uncompressedSize,
-                localFileHeaderOffset,
-                name,
-                nameSize);
-    }
-
-    public void copyTo(ByteBuffer output) {
-        output.put(mData.slice());
-    }
-
-    public CentralDirectoryRecord createWithModifiedLocalFileHeaderOffset(
-            long localFileHeaderOffset) {
-        ByteBuffer result = ByteBuffer.allocate(mData.remaining());
-        result.put(mData.slice());
-        result.flip();
-        result.order(ByteOrder.LITTLE_ENDIAN);
-        ZipUtils.setUnsignedInt32(result, LOCAL_FILE_HEADER_OFFSET_OFFSET, localFileHeaderOffset);
-        return new CentralDirectoryRecord(
-                result,
-                mLastModificationTime,
-                mLastModificationDate,
-                mCrc32,
-                mCompressedSize,
-                mUncompressedSize,
-                localFileHeaderOffset,
-                mName,
-                mNameSizeBytes);
-    }
-
-    public static CentralDirectoryRecord createWithDeflateCompressedData(
-            String name,
-            int lastModifiedTime,
-            int lastModifiedDate,
-            long crc32,
-            long compressedSize,
-            long uncompressedSize,
-            long localFileHeaderOffset) {
-        byte[] nameBytes = name.getBytes(StandardCharsets.UTF_8);
-        int recordSize = HEADER_SIZE_BYTES + nameBytes.length;
-        ByteBuffer result = ByteBuffer.allocate(recordSize);
-        result.order(ByteOrder.LITTLE_ENDIAN);
-        result.putInt(RECORD_SIGNATURE);
-        ZipUtils.putUnsignedInt16(result, 0x14); // Version made by
-        ZipUtils.putUnsignedInt16(result, 0x14); // Minimum version needed to extract
-        result.putShort(ZipUtils.GP_FLAG_EFS); // UTF-8 character encoding used for entry name
-        result.putShort(ZipUtils.COMPRESSION_METHOD_DEFLATED);
-        ZipUtils.putUnsignedInt16(result, lastModifiedTime);
-        ZipUtils.putUnsignedInt16(result, lastModifiedDate);
-        ZipUtils.putUnsignedInt32(result, crc32);
-        ZipUtils.putUnsignedInt32(result, compressedSize);
-        ZipUtils.putUnsignedInt32(result, uncompressedSize);
-        ZipUtils.putUnsignedInt16(result, nameBytes.length);
-        ZipUtils.putUnsignedInt16(result, 0); // Extra field length
-        ZipUtils.putUnsignedInt16(result, 0); // File comment length
-        ZipUtils.putUnsignedInt16(result, 0); // Disk number
-        ZipUtils.putUnsignedInt16(result, 0); // Internal file attributes
-        ZipUtils.putUnsignedInt32(result, 0); // External file attributes
-        ZipUtils.putUnsignedInt32(result, localFileHeaderOffset);
-        result.put(nameBytes);
-
-        if (result.hasRemaining()) {
-            throw new RuntimeException("pos: " + result.position() + ", limit: " + result.limit());
-        }
-        result.flip();
-        return new CentralDirectoryRecord(
-                result,
-                lastModifiedTime,
-                lastModifiedDate,
-                crc32,
-                compressedSize,
-                uncompressedSize,
-                localFileHeaderOffset,
-                name,
-                nameBytes.length);
-    }
-
-    static String getName(ByteBuffer record, int position, int nameLengthBytes) {
-        byte[] nameBytes;
-        int nameBytesOffset;
-        if (record.hasArray()) {
-            nameBytes = record.array();
-            nameBytesOffset = record.arrayOffset() + position;
-        } else {
-            nameBytes = new byte[nameLengthBytes];
-            nameBytesOffset = 0;
-            int originalPosition = record.position();
-            try {
-                record.position(position);
-                record.get(nameBytes);
-            } finally {
-                record.position(originalPosition);
-            }
-        }
-        return new String(nameBytes, nameBytesOffset, nameLengthBytes, StandardCharsets.UTF_8);
-    }
-
-    private static class ByLocalFileHeaderOffsetComparator
-            implements Comparator<CentralDirectoryRecord> {
-        @Override
-        public int compare(CentralDirectoryRecord r1, CentralDirectoryRecord r2) {
-            long offset1 = r1.getLocalFileHeaderOffset();
-            long offset2 = r2.getLocalFileHeaderOffset();
-            if (offset1 > offset2) {
-                return 1;
-            } else if (offset1 < offset2) {
-                return -1;
-            } else {
-                return 0;
-            }
-        }
-    }
-}
diff --git a/tools/apksigner/core/src/com/android/apksigner/core/internal/zip/EocdRecord.java b/tools/apksigner/core/src/com/android/apksigner/core/internal/zip/EocdRecord.java
deleted file mode 100644
index 8777591..0000000
--- a/tools/apksigner/core/src/com/android/apksigner/core/internal/zip/EocdRecord.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.apksigner.core.internal.zip;
-
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-
-/**
- * ZIP End of Central Directory record.
- */
-public class EocdRecord {
-    private static final int CD_RECORD_COUNT_ON_DISK_OFFSET = 8;
-    private static final int CD_RECORD_COUNT_TOTAL_OFFSET = 10;
-    private static final int CD_SIZE_OFFSET = 12;
-    private static final int CD_OFFSET_OFFSET = 16;
-
-    public static ByteBuffer createWithModifiedCentralDirectoryInfo(
-            ByteBuffer original,
-            int centralDirectoryRecordCount,
-            long centralDirectorySizeBytes,
-            long centralDirectoryOffset) {
-        ByteBuffer result = ByteBuffer.allocate(original.remaining());
-        result.order(ByteOrder.LITTLE_ENDIAN);
-        result.put(original.slice());
-        result.flip();
-        ZipUtils.setUnsignedInt16(
-                result, CD_RECORD_COUNT_ON_DISK_OFFSET, centralDirectoryRecordCount);
-        ZipUtils.setUnsignedInt16(
-                result, CD_RECORD_COUNT_TOTAL_OFFSET, centralDirectoryRecordCount);
-        ZipUtils.setUnsignedInt32(result, CD_SIZE_OFFSET, centralDirectorySizeBytes);
-        ZipUtils.setUnsignedInt32(result, CD_OFFSET_OFFSET, centralDirectoryOffset);
-        return result;
-    }
-}
diff --git a/tools/apksigner/core/src/com/android/apksigner/core/internal/zip/LocalFileRecord.java b/tools/apksigner/core/src/com/android/apksigner/core/internal/zip/LocalFileRecord.java
deleted file mode 100644
index 397a450..0000000
--- a/tools/apksigner/core/src/com/android/apksigner/core/internal/zip/LocalFileRecord.java
+++ /dev/null
@@ -1,540 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.apksigner.core.internal.zip;
-
-import java.io.Closeable;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.charset.StandardCharsets;
-import java.util.zip.DataFormatException;
-import java.util.zip.Inflater;
-
-import com.android.apksigner.core.internal.util.ByteBufferSink;
-import com.android.apksigner.core.util.DataSink;
-import com.android.apksigner.core.util.DataSource;
-import com.android.apksigner.core.zip.ZipFormatException;
-
-/**
- * ZIP Local File record.
- *
- * <p>The record consists of the Local File Header, file data, and (if present) Data Descriptor.
- */
-public class LocalFileRecord {
-    private static final int RECORD_SIGNATURE = 0x04034b50;
-    private static final int HEADER_SIZE_BYTES = 30;
-
-    private static final int GP_FLAGS_OFFSET = 6;
-    private static final int COMPRESSION_METHOD_OFFSET = 8;
-    private static final int CRC32_OFFSET = 14;
-    private static final int COMPRESSED_SIZE_OFFSET = 18;
-    private static final int UNCOMPRESSED_SIZE_OFFSET = 22;
-    private static final int NAME_LENGTH_OFFSET = 26;
-    private static final int EXTRA_LENGTH_OFFSET = 28;
-    private static final int NAME_OFFSET = HEADER_SIZE_BYTES;
-
-    private static final int DATA_DESCRIPTOR_SIZE_BYTES_WITHOUT_SIGNATURE = 12;
-    private static final int DATA_DESCRIPTOR_SIGNATURE = 0x08074b50;
-
-    private final String mName;
-    private final int mNameSizeBytes;
-    private final ByteBuffer mExtra;
-
-    private final long mStartOffsetInArchive;
-    private final long mSize;
-
-    private final int mDataStartOffset;
-    private final long mDataSize;
-    private final boolean mDataCompressed;
-    private final long mUncompressedDataSize;
-
-    private LocalFileRecord(
-            String name,
-            int nameSizeBytes,
-            ByteBuffer extra,
-            long startOffsetInArchive,
-            long size,
-            int dataStartOffset,
-            long dataSize,
-            boolean dataCompressed,
-            long uncompressedDataSize) {
-        mName = name;
-        mNameSizeBytes = nameSizeBytes;
-        mExtra = extra;
-        mStartOffsetInArchive = startOffsetInArchive;
-        mSize = size;
-        mDataStartOffset = dataStartOffset;
-        mDataSize = dataSize;
-        mDataCompressed = dataCompressed;
-        mUncompressedDataSize = uncompressedDataSize;
-    }
-
-    public String getName() {
-        return mName;
-    }
-
-    public ByteBuffer getExtra() {
-        return (mExtra.capacity() > 0) ? mExtra.slice() : mExtra;
-    }
-
-    public int getExtraFieldStartOffsetInsideRecord() {
-        return HEADER_SIZE_BYTES + mNameSizeBytes;
-    }
-
-    public long getStartOffsetInArchive() {
-        return mStartOffsetInArchive;
-    }
-
-    public int getDataStartOffsetInRecord() {
-        return mDataStartOffset;
-    }
-
-    /**
-     * Returns the size (in bytes) of this record.
-     */
-    public long getSize() {
-        return mSize;
-    }
-
-    /**
-     * Returns {@code true} if this record's file data is stored in compressed form.
-     */
-    public boolean isDataCompressed() {
-        return mDataCompressed;
-    }
-
-    /**
-     * Returns the Local File record starting at the current position of the provided buffer
-     * and advances the buffer's position immediately past the end of the record. The record
-     * consists of the Local File Header, data, and (if present) Data Descriptor.
-     */
-    public static LocalFileRecord getRecord(
-            DataSource apk,
-            CentralDirectoryRecord cdRecord,
-            long cdStartOffset) throws ZipFormatException, IOException {
-        return getRecord(
-                apk,
-                cdRecord,
-                cdStartOffset,
-                true, // obtain extra field contents
-                true // include Data Descriptor (if present)
-                );
-    }
-
-    /**
-     * Returns the Local File record starting at the current position of the provided buffer
-     * and advances the buffer's position immediately past the end of the record. The record
-     * consists of the Local File Header, data, and (if present) Data Descriptor.
-     */
-    private static LocalFileRecord getRecord(
-            DataSource apk,
-            CentralDirectoryRecord cdRecord,
-            long cdStartOffset,
-            boolean extraFieldContentsNeeded,
-            boolean dataDescriptorIncluded) throws ZipFormatException, IOException {
-        // IMPLEMENTATION NOTE: This method attempts to mimic the behavior of Android platform
-        // exhibited when reading an APK for the purposes of verifying its signatures.
-
-        String entryName = cdRecord.getName();
-        int cdRecordEntryNameSizeBytes = cdRecord.getNameSizeBytes();
-        int headerSizeWithName = HEADER_SIZE_BYTES + cdRecordEntryNameSizeBytes;
-        long headerStartOffset = cdRecord.getLocalFileHeaderOffset();
-        long headerEndOffset = headerStartOffset + headerSizeWithName;
-        if (headerEndOffset >= cdStartOffset) {
-            throw new ZipFormatException(
-                    "Local File Header of " + entryName + " extends beyond start of Central"
-                            + " Directory. LFH end: " + headerEndOffset
-                            + ", CD start: " + cdStartOffset);
-        }
-        ByteBuffer header;
-        try {
-            header = apk.getByteBuffer(headerStartOffset, headerSizeWithName);
-        } catch (IOException e) {
-            throw new IOException("Failed to read Local File Header of " + entryName, e);
-        }
-        header.order(ByteOrder.LITTLE_ENDIAN);
-
-        int recordSignature = header.getInt();
-        if (recordSignature != RECORD_SIGNATURE) {
-            throw new ZipFormatException(
-                    "Not a Local File Header record for entry " + entryName + ". Signature: 0x"
-                            + Long.toHexString(recordSignature & 0xffffffffL));
-        }
-        short gpFlags = header.getShort(GP_FLAGS_OFFSET);
-        boolean dataDescriptorUsed = (gpFlags & ZipUtils.GP_FLAG_DATA_DESCRIPTOR_USED) != 0;
-        long uncompressedDataCrc32FromCdRecord = cdRecord.getCrc32();
-        long compressedDataSizeFromCdRecord = cdRecord.getCompressedSize();
-        long uncompressedDataSizeFromCdRecord = cdRecord.getUncompressedSize();
-        if (!dataDescriptorUsed) {
-            long crc32 = ZipUtils.getUnsignedInt32(header, CRC32_OFFSET);
-            if (crc32 != uncompressedDataCrc32FromCdRecord) {
-                throw new ZipFormatException(
-                        "CRC-32 mismatch between Local File Header and Central Directory for entry "
-                                + entryName + ". LFH: " + crc32
-                                + ", CD: " + uncompressedDataCrc32FromCdRecord);
-            }
-            long compressedSize = ZipUtils.getUnsignedInt32(header, COMPRESSED_SIZE_OFFSET);
-            if (compressedSize != compressedDataSizeFromCdRecord) {
-                throw new ZipFormatException(
-                        "Compressed size mismatch between Local File Header and Central Directory"
-                                + " for entry " + entryName + ". LFH: " + compressedSize
-                                + ", CD: " + compressedDataSizeFromCdRecord);
-            }
-            long uncompressedSize = ZipUtils.getUnsignedInt32(header, UNCOMPRESSED_SIZE_OFFSET);
-            if (uncompressedSize != uncompressedDataSizeFromCdRecord) {
-                throw new ZipFormatException(
-                        "Uncompressed size mismatch between Local File Header and Central Directory"
-                                + " for entry " + entryName + ". LFH: " + uncompressedSize
-                                + ", CD: " + uncompressedDataSizeFromCdRecord);
-            }
-        }
-        int nameLength = ZipUtils.getUnsignedInt16(header, NAME_LENGTH_OFFSET);
-        if (nameLength > cdRecordEntryNameSizeBytes) {
-            throw new ZipFormatException(
-                    "Name mismatch between Local File Header and Central Directory for entry"
-                            + entryName + ". LFH: " + nameLength
-                            + " bytes, CD: " + cdRecordEntryNameSizeBytes + " bytes");
-        }
-        String name = CentralDirectoryRecord.getName(header, NAME_OFFSET, nameLength);
-        if (!entryName.equals(name)) {
-            throw new ZipFormatException(
-                    "Name mismatch between Local File Header and Central Directory. LFH: \""
-                            + name + "\", CD: \"" + entryName + "\"");
-        }
-        int extraLength = ZipUtils.getUnsignedInt16(header, EXTRA_LENGTH_OFFSET);
-
-        short compressionMethod = header.getShort(COMPRESSION_METHOD_OFFSET);
-        boolean compressed;
-        switch (compressionMethod) {
-            case ZipUtils.COMPRESSION_METHOD_STORED:
-                compressed = false;
-                break;
-            case ZipUtils.COMPRESSION_METHOD_DEFLATED:
-                compressed = true;
-                break;
-            default:
-                throw new ZipFormatException(
-                        "Unsupported compression method of entry " + entryName
-                                + ": " + (compressionMethod & 0xffff));
-        }
-
-        long dataStartOffset = headerStartOffset + HEADER_SIZE_BYTES + nameLength + extraLength;
-        long dataSize;
-        if (compressed) {
-            dataSize = compressedDataSizeFromCdRecord;
-        } else {
-            dataSize = uncompressedDataSizeFromCdRecord;
-        }
-        long dataEndOffset = dataStartOffset + dataSize;
-        if (dataEndOffset > cdStartOffset) {
-            throw new ZipFormatException(
-                    "Local File Header data of " + entryName + " overlaps with Central Directory"
-                            + ". LFH data start: " + dataStartOffset
-                            + ", LFH data end: " + dataEndOffset + ", CD start: " + cdStartOffset);
-        }
-
-        ByteBuffer extra = EMPTY_BYTE_BUFFER;
-        if ((extraFieldContentsNeeded) && (extraLength > 0)) {
-            extra = apk.getByteBuffer(
-                    headerStartOffset + HEADER_SIZE_BYTES + nameLength, extraLength);
-        }
-
-        long recordEndOffset = dataEndOffset;
-        // Include the Data Descriptor (if requested and present) into the record.
-        if ((dataDescriptorIncluded) && ((gpFlags & ZipUtils.GP_FLAG_DATA_DESCRIPTOR_USED) != 0)) {
-            // The record's data is supposed to be followed by the Data Descriptor. Unfortunately,
-            // the descriptor's size is not known in advance because the spec lets the signature
-            // field (the first four bytes) be omitted. Thus, there's no 100% reliable way to tell
-            // how long the Data Descriptor record is. Most parsers (including Android) check
-            // whether the first four bytes look like Data Descriptor record signature and, if so,
-            // assume that it is indeed the record's signature. However, this is the wrong
-            // conclusion if the record's CRC-32 (next field after the signature) has the same value
-            // as the signature. In any case, we're doing what Android is doing.
-            long dataDescriptorEndOffset =
-                    dataEndOffset + DATA_DESCRIPTOR_SIZE_BYTES_WITHOUT_SIGNATURE;
-            if (dataDescriptorEndOffset > cdStartOffset) {
-                throw new ZipFormatException(
-                        "Data Descriptor of " + entryName + " overlaps with Central Directory"
-                                + ". Data Descriptor end: " + dataEndOffset
-                                + ", CD start: " + cdStartOffset);
-            }
-            ByteBuffer dataDescriptorPotentialSig = apk.getByteBuffer(dataEndOffset, 4);
-            dataDescriptorPotentialSig.order(ByteOrder.LITTLE_ENDIAN);
-            if (dataDescriptorPotentialSig.getInt() == DATA_DESCRIPTOR_SIGNATURE) {
-                dataDescriptorEndOffset += 4;
-                if (dataDescriptorEndOffset > cdStartOffset) {
-                    throw new ZipFormatException(
-                            "Data Descriptor of " + entryName + " overlaps with Central Directory"
-                                    + ". Data Descriptor end: " + dataEndOffset
-                                    + ", CD start: " + cdStartOffset);
-                }
-            }
-            recordEndOffset = dataDescriptorEndOffset;
-        }
-
-        long recordSize = recordEndOffset - headerStartOffset;
-        int dataStartOffsetInRecord = HEADER_SIZE_BYTES + nameLength + extraLength;
-
-        return new LocalFileRecord(
-                entryName,
-                cdRecordEntryNameSizeBytes,
-                extra,
-                headerStartOffset,
-                recordSize,
-                dataStartOffsetInRecord,
-                dataSize,
-                compressed,
-                uncompressedDataSizeFromCdRecord);
-    }
-
-    /**
-     * Outputs this record and returns returns the number of bytes output.
-     */
-    public long outputRecord(DataSource sourceApk, DataSink output) throws IOException {
-        long size = getSize();
-        sourceApk.feed(getStartOffsetInArchive(), size, output);
-        return size;
-    }
-
-    /**
-     * Outputs this record, replacing its extra field with the provided one, and returns returns the
-     * number of bytes output.
-     */
-    public long outputRecordWithModifiedExtra(
-            DataSource sourceApk,
-            ByteBuffer extra,
-            DataSink output) throws IOException {
-        long recordStartOffsetInSource = getStartOffsetInArchive();
-        int extraStartOffsetInRecord = getExtraFieldStartOffsetInsideRecord();
-        int extraSizeBytes = extra.remaining();
-        int headerSize = extraStartOffsetInRecord + extraSizeBytes;
-        ByteBuffer header = ByteBuffer.allocate(headerSize);
-        header.order(ByteOrder.LITTLE_ENDIAN);
-        sourceApk.copyTo(recordStartOffsetInSource, extraStartOffsetInRecord, header);
-        header.put(extra.slice());
-        header.flip();
-        ZipUtils.setUnsignedInt16(header, EXTRA_LENGTH_OFFSET, extraSizeBytes);
-
-        long outputByteCount = header.remaining();
-        output.consume(header);
-        long remainingRecordSize = getSize() - mDataStartOffset;
-        sourceApk.feed(recordStartOffsetInSource + mDataStartOffset, remainingRecordSize, output);
-        outputByteCount += remainingRecordSize;
-        return outputByteCount;
-    }
-
-    /**
-     * Outputs the specified Local File Header record with its data and returns the number of bytes
-     * output.
-     */
-    public static long outputRecordWithDeflateCompressedData(
-            String name,
-            int lastModifiedTime,
-            int lastModifiedDate,
-            byte[] compressedData,
-            long crc32,
-            long uncompressedSize,
-            DataSink output) throws IOException {
-        byte[] nameBytes = name.getBytes(StandardCharsets.UTF_8);
-        int recordSize = HEADER_SIZE_BYTES + nameBytes.length;
-        ByteBuffer result = ByteBuffer.allocate(recordSize);
-        result.order(ByteOrder.LITTLE_ENDIAN);
-        result.putInt(RECORD_SIGNATURE);
-        ZipUtils.putUnsignedInt16(result,  0x14); // Minimum version needed to extract
-        result.putShort(ZipUtils.GP_FLAG_EFS); // General purpose flag: UTF-8 encoded name
-        result.putShort(ZipUtils.COMPRESSION_METHOD_DEFLATED);
-        ZipUtils.putUnsignedInt16(result, lastModifiedTime);
-        ZipUtils.putUnsignedInt16(result, lastModifiedDate);
-        ZipUtils.putUnsignedInt32(result, crc32);
-        ZipUtils.putUnsignedInt32(result, compressedData.length);
-        ZipUtils.putUnsignedInt32(result, uncompressedSize);
-        ZipUtils.putUnsignedInt16(result, nameBytes.length);
-        ZipUtils.putUnsignedInt16(result, 0); // Extra field length
-        result.put(nameBytes);
-        if (result.hasRemaining()) {
-            throw new RuntimeException("pos: " + result.position() + ", limit: " + result.limit());
-        }
-        result.flip();
-
-        long outputByteCount = result.remaining();
-        output.consume(result);
-        outputByteCount += compressedData.length;
-        output.consume(compressedData, 0, compressedData.length);
-        return outputByteCount;
-    }
-
-    private static final ByteBuffer EMPTY_BYTE_BUFFER = ByteBuffer.allocate(0);
-
-    /**
-     * Sends uncompressed data of this record into the the provided data sink.
-     */
-    public void outputUncompressedData(
-            DataSource lfhSection,
-            DataSink sink) throws IOException, ZipFormatException {
-        long dataStartOffsetInArchive = mStartOffsetInArchive + mDataStartOffset;
-        try {
-            if (mDataCompressed) {
-                try (InflateSinkAdapter inflateAdapter = new InflateSinkAdapter(sink)) {
-                    lfhSection.feed(dataStartOffsetInArchive, mDataSize, inflateAdapter);
-                    long actualUncompressedSize = inflateAdapter.getOutputByteCount();
-                    if (actualUncompressedSize != mUncompressedDataSize) {
-                        throw new ZipFormatException(
-                                "Unexpected size of uncompressed data of " + mName
-                                        + ". Expected: " + mUncompressedDataSize + " bytes"
-                                        + ", actual: " + actualUncompressedSize + " bytes");
-                    }
-                }
-            } else {
-                lfhSection.feed(dataStartOffsetInArchive, mDataSize, sink);
-                // No need to check whether output size is as expected because DataSource.feed is
-                // guaranteed to output exactly the number of bytes requested.
-            }
-        } catch (IOException e) {
-            throw new IOException(
-                    "Failed to read data of " + ((mDataCompressed) ? "compressed" : "uncompressed")
-                        + " entry " + mName,
-                    e);
-        }
-        // Interestingly, Android doesn't check that uncompressed data's CRC-32 is as expected. We
-        // thus don't check either.
-    }
-
-    /**
-     * Sends uncompressed data pointed to by the provided ZIP Central Directory (CD) record into the
-     * provided data sink.
-     */
-    public static void outputUncompressedData(
-            DataSource source,
-            CentralDirectoryRecord cdRecord,
-            long cdStartOffsetInArchive,
-            DataSink sink) throws ZipFormatException, IOException {
-        // IMPLEMENTATION NOTE: This method attempts to mimic the behavior of Android platform
-        // exhibited when reading an APK for the purposes of verifying its signatures.
-        // When verifying an APK, Android doesn't care reading the extra field or the Data
-        // Descriptor.
-        LocalFileRecord lfhRecord =
-                getRecord(
-                        source,
-                        cdRecord,
-                        cdStartOffsetInArchive,
-                        false, // don't care about the extra field
-                        false // don't read the Data Descriptor
-                        );
-        lfhRecord.outputUncompressedData(source, sink);
-    }
-
-    /**
-     * Returns the uncompressed data pointed to by the provided ZIP Central Directory (CD) record.
-     */
-    public static byte[] getUncompressedData(
-            DataSource source,
-            CentralDirectoryRecord cdRecord,
-            long cdStartOffsetInArchive) throws ZipFormatException, IOException {
-        if (cdRecord.getUncompressedSize() > Integer.MAX_VALUE) {
-            throw new IOException(
-                    cdRecord.getName() + " too large: " + cdRecord.getUncompressedSize());
-        }
-        byte[] result = new byte[(int) cdRecord.getUncompressedSize()];
-        ByteBuffer resultBuf = ByteBuffer.wrap(result);
-        ByteBufferSink resultSink = new ByteBufferSink(resultBuf);
-        outputUncompressedData(
-                source,
-                cdRecord,
-                cdStartOffsetInArchive,
-                resultSink);
-        return result;
-    }
-
-    /**
-     * {@link DataSink} which inflates received data and outputs the deflated data into the provided
-     * delegate sink.
-     */
-    private static class InflateSinkAdapter implements DataSink, Closeable {
-        private final DataSink mDelegate;
-
-        private Inflater mInflater = new Inflater(true);
-        private byte[] mOutputBuffer;
-        private byte[] mInputBuffer;
-        private long mOutputByteCount;
-        private boolean mClosed;
-
-        private InflateSinkAdapter(DataSink delegate) {
-            mDelegate = delegate;
-        }
-
-        @Override
-        public void consume(byte[] buf, int offset, int length) throws IOException {
-            checkNotClosed();
-            mInflater.setInput(buf, offset, length);
-            if (mOutputBuffer == null) {
-                mOutputBuffer = new byte[65536];
-            }
-            while (!mInflater.finished()) {
-                int outputChunkSize;
-                try {
-                    outputChunkSize = mInflater.inflate(mOutputBuffer);
-                } catch (DataFormatException e) {
-                    throw new IOException("Failed to inflate data", e);
-                }
-                if (outputChunkSize == 0) {
-                    return;
-                }
-                mDelegate.consume(mOutputBuffer, 0, outputChunkSize);
-                mOutputByteCount += outputChunkSize;
-            }
-        }
-
-        @Override
-        public void consume(ByteBuffer buf) throws IOException {
-            checkNotClosed();
-            if (buf.hasArray()) {
-                consume(buf.array(), buf.arrayOffset() + buf.position(), buf.remaining());
-                buf.position(buf.limit());
-            } else {
-                if (mInputBuffer == null) {
-                    mInputBuffer = new byte[65536];
-                }
-                while (buf.hasRemaining()) {
-                    int chunkSize = Math.min(buf.remaining(), mInputBuffer.length);
-                    buf.get(mInputBuffer, 0, chunkSize);
-                    consume(mInputBuffer, 0, chunkSize);
-                }
-            }
-        }
-
-        public long getOutputByteCount() {
-            return mOutputByteCount;
-        }
-
-        @Override
-        public void close() throws IOException {
-            mClosed = true;
-            mInputBuffer = null;
-            mOutputBuffer = null;
-            if (mInflater != null) {
-                mInflater.end();
-                mInflater = null;
-            }
-        }
-
-        private void checkNotClosed() {
-            if (mClosed) {
-                throw new IllegalStateException("Closed");
-            }
-        }
-    }
-}
diff --git a/tools/apksigner/core/src/com/android/apksigner/core/internal/zip/ZipUtils.java b/tools/apksigner/core/src/com/android/apksigner/core/internal/zip/ZipUtils.java
deleted file mode 100644
index 6a0c501..0000000
--- a/tools/apksigner/core/src/com/android/apksigner/core/internal/zip/ZipUtils.java
+++ /dev/null
@@ -1,353 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.apksigner.core.internal.zip;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.zip.CRC32;
-import java.util.zip.Deflater;
-
-import com.android.apksigner.core.internal.util.Pair;
-import com.android.apksigner.core.util.DataSource;
-
-/**
- * Assorted ZIP format helpers.
- *
- * <p>NOTE: Most helper methods operating on {@code ByteBuffer} instances expect that the byte
- * order of these buffers is little-endian.
- */
-public abstract class ZipUtils {
-    private ZipUtils() {}
-
-    public static final short COMPRESSION_METHOD_STORED = 0;
-    public static final short COMPRESSION_METHOD_DEFLATED = 8;
-
-    public static final short GP_FLAG_DATA_DESCRIPTOR_USED = 0x08;
-    public static final short GP_FLAG_EFS = 0x0800;
-
-    private static final int ZIP_EOCD_REC_MIN_SIZE = 22;
-    private static final int ZIP_EOCD_REC_SIG = 0x06054b50;
-    private static final int ZIP_EOCD_CENTRAL_DIR_TOTAL_RECORD_COUNT_OFFSET = 10;
-    private static final int ZIP_EOCD_CENTRAL_DIR_SIZE_FIELD_OFFSET = 12;
-    private static final int ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_OFFSET = 16;
-    private static final int ZIP_EOCD_COMMENT_LENGTH_FIELD_OFFSET = 20;
-
-    private static final int ZIP64_EOCD_LOCATOR_SIZE = 20;
-    private static final int ZIP64_EOCD_LOCATOR_SIG = 0x07064b50;
-
-    private static final int UINT16_MAX_VALUE = 0xffff;
-
-    /**
-     * Sets the offset of the start of the ZIP Central Directory in the archive.
-     *
-     * <p>NOTE: Byte order of {@code zipEndOfCentralDirectory} must be little-endian.
-     */
-    public static void setZipEocdCentralDirectoryOffset(
-            ByteBuffer zipEndOfCentralDirectory, long offset) {
-        assertByteOrderLittleEndian(zipEndOfCentralDirectory);
-        setUnsignedInt32(
-                zipEndOfCentralDirectory,
-                zipEndOfCentralDirectory.position() + ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_OFFSET,
-                offset);
-    }
-
-    /**
-     * Returns the offset of the start of the ZIP Central Directory in the archive.
-     *
-     * <p>NOTE: Byte order of {@code zipEndOfCentralDirectory} must be little-endian.
-     */
-    public static long getZipEocdCentralDirectoryOffset(ByteBuffer zipEndOfCentralDirectory) {
-        assertByteOrderLittleEndian(zipEndOfCentralDirectory);
-        return getUnsignedInt32(
-                zipEndOfCentralDirectory,
-                zipEndOfCentralDirectory.position() + ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_OFFSET);
-    }
-
-    /**
-     * Returns the size (in bytes) of the ZIP Central Directory.
-     *
-     * <p>NOTE: Byte order of {@code zipEndOfCentralDirectory} must be little-endian.
-     */
-    public static long getZipEocdCentralDirectorySizeBytes(ByteBuffer zipEndOfCentralDirectory) {
-        assertByteOrderLittleEndian(zipEndOfCentralDirectory);
-        return getUnsignedInt32(
-                zipEndOfCentralDirectory,
-                zipEndOfCentralDirectory.position() + ZIP_EOCD_CENTRAL_DIR_SIZE_FIELD_OFFSET);
-    }
-
-    /**
-     * Returns the total number of records in ZIP Central Directory.
-     *
-     * <p>NOTE: Byte order of {@code zipEndOfCentralDirectory} must be little-endian.
-     */
-    public static int getZipEocdCentralDirectoryTotalRecordCount(
-            ByteBuffer zipEndOfCentralDirectory) {
-        assertByteOrderLittleEndian(zipEndOfCentralDirectory);
-        return getUnsignedInt16(
-                zipEndOfCentralDirectory,
-                zipEndOfCentralDirectory.position()
-                        + ZIP_EOCD_CENTRAL_DIR_TOTAL_RECORD_COUNT_OFFSET);
-    }
-
-    /**
-     * Returns the ZIP End of Central Directory record of the provided ZIP file.
-     *
-     * @return contents of the ZIP End of Central Directory record and the record's offset in the
-     *         file or {@code null} if the file does not contain the record.
-     *
-     * @throws IOException if an I/O error occurs while reading the file.
-     */
-    public static Pair<ByteBuffer, Long> findZipEndOfCentralDirectoryRecord(DataSource zip)
-            throws IOException {
-        // ZIP End of Central Directory (EOCD) record is located at the very end of the ZIP archive.
-        // The record can be identified by its 4-byte signature/magic which is located at the very
-        // beginning of the record. A complication is that the record is variable-length because of
-        // the comment field.
-        // The algorithm for locating the ZIP EOCD record is as follows. We search backwards from
-        // end of the buffer for the EOCD record signature. Whenever we find a signature, we check
-        // the candidate record's comment length is such that the remainder of the record takes up
-        // exactly the remaining bytes in the buffer. The search is bounded because the maximum
-        // size of the comment field is 65535 bytes because the field is an unsigned 16-bit number.
-
-        long fileSize = zip.size();
-        if (fileSize < ZIP_EOCD_REC_MIN_SIZE) {
-            return null;
-        }
-
-        // Optimization: 99.99% of APKs have a zero-length comment field in the EoCD record and thus
-        // the EoCD record offset is known in advance. Try that offset first to avoid unnecessarily
-        // reading more data.
-        Pair<ByteBuffer, Long> result = findZipEndOfCentralDirectoryRecord(zip, 0);
-        if (result != null) {
-            return result;
-        }
-
-        // EoCD does not start where we expected it to. Perhaps it contains a non-empty comment
-        // field. Expand the search. The maximum size of the comment field in EoCD is 65535 because
-        // the comment length field is an unsigned 16-bit number.
-        return findZipEndOfCentralDirectoryRecord(zip, UINT16_MAX_VALUE);
-    }
-
-    /**
-     * Returns the ZIP End of Central Directory record of the provided ZIP file.
-     *
-     * @param maxCommentSize maximum accepted size (in bytes) of EoCD comment field. The permitted
-     *        value is from 0 to 65535 inclusive. The smaller the value, the faster this method
-     *        locates the record, provided its comment field is no longer than this value.
-     *
-     * @return contents of the ZIP End of Central Directory record and the record's offset in the
-     *         file or {@code null} if the file does not contain the record.
-     *
-     * @throws IOException if an I/O error occurs while reading the file.
-     */
-    private static Pair<ByteBuffer, Long> findZipEndOfCentralDirectoryRecord(
-            DataSource zip, int maxCommentSize) throws IOException {
-        // ZIP End of Central Directory (EOCD) record is located at the very end of the ZIP archive.
-        // The record can be identified by its 4-byte signature/magic which is located at the very
-        // beginning of the record. A complication is that the record is variable-length because of
-        // the comment field.
-        // The algorithm for locating the ZIP EOCD record is as follows. We search backwards from
-        // end of the buffer for the EOCD record signature. Whenever we find a signature, we check
-        // the candidate record's comment length is such that the remainder of the record takes up
-        // exactly the remaining bytes in the buffer. The search is bounded because the maximum
-        // size of the comment field is 65535 bytes because the field is an unsigned 16-bit number.
-
-        if ((maxCommentSize < 0) || (maxCommentSize > UINT16_MAX_VALUE)) {
-            throw new IllegalArgumentException("maxCommentSize: " + maxCommentSize);
-        }
-
-        long fileSize = zip.size();
-        if (fileSize < ZIP_EOCD_REC_MIN_SIZE) {
-            // No space for EoCD record in the file.
-            return null;
-        }
-        // Lower maxCommentSize if the file is too small.
-        maxCommentSize = (int) Math.min(maxCommentSize, fileSize - ZIP_EOCD_REC_MIN_SIZE);
-
-        int maxEocdSize = ZIP_EOCD_REC_MIN_SIZE + maxCommentSize;
-        long bufOffsetInFile = fileSize - maxEocdSize;
-        ByteBuffer buf = zip.getByteBuffer(bufOffsetInFile, maxEocdSize);
-        buf.order(ByteOrder.LITTLE_ENDIAN);
-        int eocdOffsetInBuf = findZipEndOfCentralDirectoryRecord(buf);
-        if (eocdOffsetInBuf == -1) {
-            // No EoCD record found in the buffer
-            return null;
-        }
-        // EoCD found
-        buf.position(eocdOffsetInBuf);
-        ByteBuffer eocd = buf.slice();
-        eocd.order(ByteOrder.LITTLE_ENDIAN);
-        return Pair.of(eocd, bufOffsetInFile + eocdOffsetInBuf);
-    }
-
-    /**
-     * Returns the position at which ZIP End of Central Directory record starts in the provided
-     * buffer or {@code -1} if the record is not present.
-     *
-     * <p>NOTE: Byte order of {@code zipContents} must be little-endian.
-     */
-    private static int findZipEndOfCentralDirectoryRecord(ByteBuffer zipContents) {
-        assertByteOrderLittleEndian(zipContents);
-
-        // ZIP End of Central Directory (EOCD) record is located at the very end of the ZIP archive.
-        // The record can be identified by its 4-byte signature/magic which is located at the very
-        // beginning of the record. A complication is that the record is variable-length because of
-        // the comment field.
-        // The algorithm for locating the ZIP EOCD record is as follows. We search backwards from
-        // end of the buffer for the EOCD record signature. Whenever we find a signature, we check
-        // the candidate record's comment length is such that the remainder of the record takes up
-        // exactly the remaining bytes in the buffer. The search is bounded because the maximum
-        // size of the comment field is 65535 bytes because the field is an unsigned 16-bit number.
-
-        int archiveSize = zipContents.capacity();
-        if (archiveSize < ZIP_EOCD_REC_MIN_SIZE) {
-            return -1;
-        }
-        int maxCommentLength = Math.min(archiveSize - ZIP_EOCD_REC_MIN_SIZE, UINT16_MAX_VALUE);
-        int eocdWithEmptyCommentStartPosition = archiveSize - ZIP_EOCD_REC_MIN_SIZE;
-        for (int expectedCommentLength = 0; expectedCommentLength < maxCommentLength;
-                expectedCommentLength++) {
-            int eocdStartPos = eocdWithEmptyCommentStartPosition - expectedCommentLength;
-            if (zipContents.getInt(eocdStartPos) == ZIP_EOCD_REC_SIG) {
-                int actualCommentLength =
-                        getUnsignedInt16(
-                                zipContents, eocdStartPos + ZIP_EOCD_COMMENT_LENGTH_FIELD_OFFSET);
-                if (actualCommentLength == expectedCommentLength) {
-                    return eocdStartPos;
-                }
-            }
-        }
-
-        return -1;
-    }
-
-    /**
-     * Returns {@code true} if the provided file contains a ZIP64 End of Central Directory
-     * Locator.
-     *
-     * @param zipEndOfCentralDirectoryPosition offset of the ZIP End of Central Directory record
-     *        in the file.
-     *
-     * @throws IOException if an I/O error occurs while reading the data source
-     */
-    public static final boolean isZip64EndOfCentralDirectoryLocatorPresent(
-            DataSource zip, long zipEndOfCentralDirectoryPosition) throws IOException {
-
-        // ZIP64 End of Central Directory Locator immediately precedes the ZIP End of Central
-        // Directory Record.
-        long locatorPosition = zipEndOfCentralDirectoryPosition - ZIP64_EOCD_LOCATOR_SIZE;
-        if (locatorPosition < 0) {
-            return false;
-        }
-
-        ByteBuffer sig = zip.getByteBuffer(locatorPosition, 4);
-        sig.order(ByteOrder.LITTLE_ENDIAN);
-        return sig.getInt(0) == ZIP64_EOCD_LOCATOR_SIG;
-    }
-
-    static void assertByteOrderLittleEndian(ByteBuffer buffer) {
-        if (buffer.order() != ByteOrder.LITTLE_ENDIAN) {
-            throw new IllegalArgumentException("ByteBuffer byte order must be little endian");
-        }
-    }
-
-    public static int getUnsignedInt16(ByteBuffer buffer, int offset) {
-        return buffer.getShort(offset) & 0xffff;
-    }
-
-    public static int getUnsignedInt16(ByteBuffer buffer) {
-        return buffer.getShort() & 0xffff;
-    }
-
-    static void setUnsignedInt16(ByteBuffer buffer, int offset, int value) {
-        if ((value < 0) || (value > 0xffff)) {
-            throw new IllegalArgumentException("uint16 value of out range: " + value);
-        }
-        buffer.putShort(offset, (short) value);
-    }
-
-    static void setUnsignedInt32(ByteBuffer buffer, int offset, long value) {
-        if ((value < 0) || (value > 0xffffffffL)) {
-            throw new IllegalArgumentException("uint32 value of out range: " + value);
-        }
-        buffer.putInt(offset, (int) value);
-    }
-
-    public static void putUnsignedInt16(ByteBuffer buffer, int value) {
-        if ((value < 0) || (value > 0xffff)) {
-            throw new IllegalArgumentException("uint16 value of out range: " + value);
-        }
-        buffer.putShort((short) value);
-    }
-
-    static long getUnsignedInt32(ByteBuffer buffer, int offset) {
-        return buffer.getInt(offset) & 0xffffffffL;
-    }
-
-    static long getUnsignedInt32(ByteBuffer buffer) {
-        return buffer.getInt() & 0xffffffffL;
-    }
-
-    static void putUnsignedInt32(ByteBuffer buffer, long value) {
-        if ((value < 0) || (value > 0xffffffffL)) {
-            throw new IllegalArgumentException("uint32 value of out range: " + value);
-        }
-        buffer.putInt((int) value);
-    }
-
-    public static DeflateResult deflate(ByteBuffer input) {
-        byte[] inputBuf;
-        int inputOffset;
-        int inputLength = input.remaining();
-        if (input.hasArray()) {
-            inputBuf = input.array();
-            inputOffset = input.arrayOffset() + input.position();
-            input.position(input.limit());
-        } else {
-            inputBuf = new byte[inputLength];
-            inputOffset = 0;
-            input.get(inputBuf);
-        }
-        CRC32 crc32 = new CRC32();
-        crc32.update(inputBuf, inputOffset, inputLength);
-        long crc32Value = crc32.getValue();
-        ByteArrayOutputStream out = new ByteArrayOutputStream();
-        Deflater deflater = new Deflater(9, true);
-        deflater.setInput(inputBuf, inputOffset, inputLength);
-        deflater.finish();
-        byte[] buf = new byte[65536];
-        while (!deflater.finished()) {
-            int chunkSize = deflater.deflate(buf);
-            out.write(buf, 0, chunkSize);
-        }
-        return new DeflateResult(inputLength, crc32Value, out.toByteArray());
-    }
-
-    public static class DeflateResult {
-        public final int inputSizeBytes;
-        public final long inputCrc32;
-        public final byte[] output;
-
-        public DeflateResult(int inputSizeBytes, long inputCrc32, byte[] output) {
-            this.inputSizeBytes = inputSizeBytes;
-            this.inputCrc32 = inputCrc32;
-            this.output = output;
-        }
-    }
-}
\ No newline at end of file
diff --git a/tools/apksigner/core/src/com/android/apksigner/core/util/DataSink.java b/tools/apksigner/core/src/com/android/apksigner/core/util/DataSink.java
deleted file mode 100644
index 35a61fc..0000000
--- a/tools/apksigner/core/src/com/android/apksigner/core/util/DataSink.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.apksigner.core.util;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-
-/**
- * Consumer of input data which may be provided in one go or in chunks.
- */
-public interface DataSink {
-
-    /**
-     * Consumes the provided chunk of data.
-     *
-     * <p>This data sink guarantees to not hold references to the provided buffer after this method
-     * terminates.
-     */
-    void consume(byte[] buf, int offset, int length) throws IOException;
-
-    /**
-     * Consumes all remaining data in the provided buffer and advances the buffer's position
-     * to the buffer's limit.
-     *
-     * <p>This data sink guarantees to not hold references to the provided buffer after this method
-     * terminates.
-     */
-    void consume(ByteBuffer buf) throws IOException;
-}
diff --git a/tools/apksigner/core/src/com/android/apksigner/core/util/DataSinks.java b/tools/apksigner/core/src/com/android/apksigner/core/util/DataSinks.java
deleted file mode 100644
index 4aefedb..0000000
--- a/tools/apksigner/core/src/com/android/apksigner/core/util/DataSinks.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.apksigner.core.util;
-
-import java.io.OutputStream;
-import java.io.RandomAccessFile;
-
-import com.android.apksigner.core.internal.util.OutputStreamDataSink;
-import com.android.apksigner.core.internal.util.RandomAccessFileDataSink;
-
-/**
- * Utility methods for working with {@link DataSink} abstraction.
- */
-public abstract class DataSinks {
-    private DataSinks() {}
-
-    /**
-     * Returns a {@link DataSink} which outputs received data into the provided
-     * {@link OutputStream}.
-     */
-    public static DataSink asDataSink(OutputStream out) {
-        return new OutputStreamDataSink(out);
-    }
-
-    /**
-     * Returns a {@link DataSink} which outputs received data into the provided file, sequentially,
-     * starting at the beginning of the file.
-     */
-    public static DataSink asDataSink(RandomAccessFile file) {
-        return new RandomAccessFileDataSink(file);
-    }
-}
diff --git a/tools/apksigner/core/src/com/android/apksigner/core/util/DataSource.java b/tools/apksigner/core/src/com/android/apksigner/core/util/DataSource.java
deleted file mode 100644
index e268dd2..0000000
--- a/tools/apksigner/core/src/com/android/apksigner/core/util/DataSource.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.apksigner.core.util;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-
-/**
- * Abstract representation of a source of data.
- *
- * <p>This abstraction serves three purposes:
- * <ul>
- * <li>Transparent handling of different types of sources, such as {@code byte[]},
- *     {@link java.nio.ByteBuffer}, {@link java.io.RandomAccessFile}, memory-mapped file.</li>
- * <li>Support sources larger than 2 GB. If all sources were smaller than 2 GB, {@code ByteBuffer}
- *     may have worked as the unifying abstraction.</li>
- * <li>Support sources which do not fit into logical memory as a contiguous region.</li>
- * </ul>
- *
- * <p>There are following ways to obtain a chunk of data from the data source:
- * <ul>
- * <li>Stream the chunk's data into a {@link DataSink} using
- *     {@link #feed(long, long, DataSink) feed}. This is best suited for scenarios where there is no
- *     need to have the chunk's data accessible at the same time, for example, when computing the
- *     digest of the chunk. If you need to keep the chunk's data around after {@code feed}
- *     completes, you must create a copy during {@code feed}. However, in that case the following
- *     methods of obtaining the chunk's data may be more appropriate.</li>
- * <li>Obtain a {@link ByteBuffer} containing the chunk's data using
- *     {@link #getByteBuffer(long, int) getByteBuffer}. Depending on the data source, the chunk's
- *     data may or may not be copied by this operation. This is best suited for scenarios where
- *     you need to access the chunk's data in arbitrary order, but don't need to modify the data and
- *     thus don't require a copy of the data.</li>
- * <li>Copy the chunk's data to a {@link ByteBuffer} using
- *     {@link #copyTo(long, int, ByteBuffer) copyTo}. This is best suited for scenarios where
- *     you require a copy of the chunk's data, such as to when you need to modify the data.
- *     </li>
- * </ul>
- */
-public interface DataSource {
-
-    /**
-     * Returns the amount of data (in bytes) contained in this data source.
-     */
-    long size();
-
-    /**
-     * Feeds the specified chunk from this data source into the provided sink.
-     *
-     * @param offset index (in bytes) at which the chunk starts inside data source
-     * @param size size (in bytes) of the chunk
-     */
-    void feed(long offset, long size, DataSink sink) throws IOException;
-
-    /**
-     * Returns a buffer holding the contents of the specified chunk of data from this data source.
-     * Changes to the data source are not guaranteed to be reflected in the returned buffer.
-     * Similarly, changes in the buffer are not guaranteed to be reflected in the data source.
-     *
-     * <p>The returned buffer's position is {@code 0}, and the buffer's limit and capacity is
-     * {@code size}.
-     *
-     * @param offset index (in bytes) at which the chunk starts inside data source
-     * @param size size (in bytes) of the chunk
-     */
-    ByteBuffer getByteBuffer(long offset, int size) throws IOException;
-
-    /**
-     * Copies the specified chunk from this data source into the provided destination buffer,
-     * advancing the destination buffer's position by {@code size}.
-     *
-     * @param offset index (in bytes) at which the chunk starts inside data source
-     * @param size size (in bytes) of the chunk
-     */
-    void copyTo(long offset, int size, ByteBuffer dest) throws IOException;
-
-    /**
-     * Returns a data source representing the specified region of data of this data source. Changes
-     * to data represented by this data source will also be visible in the returned data source.
-     *
-     * @param offset index (in bytes) at which the region starts inside data source
-     * @param size size (in bytes) of the region
-     */
-    DataSource slice(long offset, long size);
-}
diff --git a/tools/apksigner/core/src/com/android/apksigner/core/util/DataSources.java b/tools/apksigner/core/src/com/android/apksigner/core/util/DataSources.java
deleted file mode 100644
index 1cbb0af..0000000
--- a/tools/apksigner/core/src/com/android/apksigner/core/util/DataSources.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package com.android.apksigner.core.util;
-
-import com.android.apksigner.core.internal.util.ByteBufferDataSource;
-import com.android.apksigner.core.internal.util.RandomAccessFileDataSource;
-
-import java.io.RandomAccessFile;
-import java.nio.ByteBuffer;
-
-/**
- * Utility methods for working with {@link DataSource} abstraction.
- */
-public abstract class DataSources {
-    private DataSources() {}
-
-    /**
-     * Returns a {@link DataSource} backed by the provided {@link ByteBuffer}. The data source
-     * represents the data contained between the position and limit of the buffer. Changes to the
-     * buffer's contents will be visible in the data source.
-     */
-    public static DataSource asDataSource(ByteBuffer buffer) {
-        if (buffer == null) {
-            throw new NullPointerException();
-        }
-        return new ByteBufferDataSource(buffer);
-    }
-
-    /**
-     * Returns a {@link DataSource} backed by the provided {@link RandomAccessFile}. Changes to the
-     * file, including changes to size of file, will be visible in the data source.
-     */
-    public static DataSource asDataSource(RandomAccessFile file) {
-        if (file == null) {
-            throw new NullPointerException();
-        }
-        return new RandomAccessFileDataSource(file);
-    }
-
-    /**
-     * Returns a {@link DataSource} backed by the provided region of the {@link RandomAccessFile}.
-     * Changes to the file will be visible in the data source.
-     */
-    public static DataSource asDataSource(RandomAccessFile file, long offset, long size) {
-        if (file == null) {
-            throw new NullPointerException();
-        }
-        return new RandomAccessFileDataSource(file, offset, size);
-    }
-}
diff --git a/tools/apksigner/core/src/com/android/apksigner/core/zip/ZipFormatException.java b/tools/apksigner/core/src/com/android/apksigner/core/zip/ZipFormatException.java
deleted file mode 100644
index 7da57d9..0000000
--- a/tools/apksigner/core/src/com/android/apksigner/core/zip/ZipFormatException.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.apksigner.core.zip;
-
-/**
- * Indicates that a ZIP archive is not well-formed.
- */
-public class ZipFormatException extends Exception {
-    private static final long serialVersionUID = 1L;
-
-    public ZipFormatException(String message) {
-        super(message);
-    }
-
-    public ZipFormatException(String message, Throwable cause) {
-        super(message, cause);
-    }
-}
diff --git a/tools/atree/atree.cpp b/tools/atree/atree.cpp
index b134e01..7deca7e 100644
--- a/tools/atree/atree.cpp
+++ b/tools/atree/atree.cpp
@@ -92,7 +92,7 @@
 
 // Escape the filename so that it can be added to the makefile properly.
 static string
-escape_filename(const string name)
+escape_filename(const string& name)
 {
     ostringstream new_name;
     for (string::const_iterator iter = name.begin(); iter != name.end(); ++iter)
diff --git a/tools/fs_config/fs_config.c b/tools/fs_config/fs_config.c
index e797957..48f300b 100644
--- a/tools/fs_config/fs_config.c
+++ b/tools/fs_config/fs_config.c
@@ -105,6 +105,9 @@
       switch (buffer[i]) {
         case '\n':
           buffer[i-is_dir] = '\0';
+          if (i == 0) {
+            is_dir = 1; // empty line is considered as root directory
+          }
           i = 1025;
           break;
         case '/':
diff --git a/tools/releasetools/blockimgdiff.py b/tools/releasetools/blockimgdiff.py
index d49112f..b5a8cf0 100644
--- a/tools/releasetools/blockimgdiff.py
+++ b/tools/releasetools/blockimgdiff.py
@@ -261,7 +261,8 @@
 # original image.
 
 class BlockImageDiff(object):
-  def __init__(self, tgt, src=None, threads=None, version=4):
+  def __init__(self, tgt, src=None, threads=None, version=4,
+               disable_imgdiff=False):
     if threads is None:
       threads = multiprocessing.cpu_count() // 2
       if threads == 0:
@@ -274,6 +275,7 @@
     self._max_stashed_size = 0
     self.touched_src_ranges = RangeSet()
     self.touched_src_sha1 = None
+    self.disable_imgdiff = disable_imgdiff
 
     assert version in (1, 2, 3, 4)
 
@@ -704,6 +706,7 @@
             # produces significantly smaller patches than bsdiff).
             # This is permissible if:
             #
+            #  - imgdiff is not disabled, and
             #  - the source and target files are monotonic (ie, the
             #    data is stored with blocks in increasing order), and
             #  - we haven't removed any blocks from the source set.
@@ -713,7 +716,7 @@
             # zip file (plus possibly extra zeros in the last block),
             # which is what imgdiff needs to operate.  (imgdiff is
             # fine with extra zeros at the end of the file.)
-            imgdiff = (xf.intact and
+            imgdiff = (not self.disable_imgdiff and xf.intact and
                        xf.tgt_name.split(".")[-1].lower()
                        in ("apk", "jar", "zip"))
             xf.style = "imgdiff" if imgdiff else "bsdiff"
diff --git a/tools/releasetools/check_target_files_signatures.py b/tools/releasetools/check_target_files_signatures.py
index 5c541ab..3048488 100755
--- a/tools/releasetools/check_target_files_signatures.py
+++ b/tools/releasetools/check_target_files_signatures.py
@@ -245,7 +245,7 @@
             fullname = os.path.join(dirpath, fn)
             displayname = fullname[len(d)+1:]
             apk = APK(fullname, displayname)
-            self.apks[apk.package] = apk
+            self.apks[apk.filename] = apk
             self.apks_by_basename[os.path.basename(apk.filename)] = apk
 
             self.max_pkg_len = max(self.max_pkg_len, len(apk.package))
@@ -316,8 +316,7 @@
                                         self.max_pkg_len, apk.package,
                                         apk.shared_uid)
         else:
-          print "  %-*s  %-*s" % (self.max_fn_len, apk.filename,
-                                  self.max_pkg_len, apk.package)
+          print "  %-*s  %s" % (self.max_fn_len, apk.filename, apk.package)
       print
 
   def CompareWith(self, other):
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index 8a5eb89..8d9e855 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -1392,11 +1392,12 @@
 
 class BlockDifference(object):
   def __init__(self, partition, tgt, src=None, check_first_block=False,
-               version=None):
+               version=None, disable_imgdiff=False):
     self.tgt = tgt
     self.src = src
     self.partition = partition
     self.check_first_block = check_first_block
+    self.disable_imgdiff = disable_imgdiff
 
     if version is None:
       version = 1
@@ -1407,7 +1408,8 @@
     self.version = version
 
     b = blockimgdiff.BlockImageDiff(tgt, src, threads=OPTIONS.worker_threads,
-                                    version=self.version)
+                                    version=self.version,
+                                    disable_imgdiff=self.disable_imgdiff)
     tmpdir = tempfile.mkdtemp()
     OPTIONS.tempfiles.append(tmpdir)
     self.path = os.path.join(tmpdir, partition)
diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py
index 6966964..f6db56f 100755
--- a/tools/releasetools/ota_from_target_files.py
+++ b/tools/releasetools/ota_from_target_files.py
@@ -862,13 +862,21 @@
         int(i) for i in
         OPTIONS.info_dict.get("blockimgdiff_versions", "1").split(","))
 
-  # Check first block of system partition for remount R/W only if
-  # disk type is ext4
-  system_partition = OPTIONS.source_info_dict["fstab"]["/system"]
-  check_first_block = system_partition.fs_type == "ext4"
+  # Check the first block of the source system partition for remount R/W only
+  # if the filesystem is ext4.
+  system_src_partition = OPTIONS.source_info_dict["fstab"]["/system"]
+  check_first_block = system_src_partition.fs_type == "ext4"
+  # Disable using imgdiff for squashfs. 'imgdiff -z' expects input files to be
+  # in zip formats. However with squashfs, a) all files are compressed in LZ4;
+  # b) the blocks listed in block map may not contain all the bytes for a given
+  # file (because they're rounded to be 4K-aligned).
+  system_tgt_partition = OPTIONS.target_info_dict["fstab"]["/system"]
+  disable_imgdiff = (system_src_partition.fs_type == "squashfs" or
+                     system_tgt_partition.fs_type == "squashfs")
   system_diff = common.BlockDifference("system", system_tgt, system_src,
                                        check_first_block,
-                                       version=blockimgdiff_version)
+                                       version=blockimgdiff_version,
+                                       disable_imgdiff=disable_imgdiff)
 
   if HasVendorPartition(target_zip):
     if not HasVendorPartition(source_zip):
@@ -882,9 +890,11 @@
     # disk type is ext4
     vendor_partition = OPTIONS.source_info_dict["fstab"]["/vendor"]
     check_first_block = vendor_partition.fs_type == "ext4"
+    disable_imgdiff = vendor_partition.fs_type == "squashfs"
     vendor_diff = common.BlockDifference("vendor", vendor_tgt, vendor_src,
                                          check_first_block,
-                                         version=blockimgdiff_version)
+                                         version=blockimgdiff_version,
+                                         disable_imgdiff=disable_imgdiff)
   else:
     vendor_diff = None
 
diff --git a/tools/releasetools/sign_target_files_apks.py b/tools/releasetools/sign_target_files_apks.py
index 3341f9f..14ee963 100755
--- a/tools/releasetools/sign_target_files_apks.py
+++ b/tools/releasetools/sign_target_files_apks.py
@@ -232,11 +232,13 @@
     elif info.filename in ("SYSTEM/build.prop",
                            "VENDOR/build.prop",
                            "BOOT/RAMDISK/default.prop",
+                           "ROOT/default.prop",
                            "RECOVERY/RAMDISK/default.prop"):
       print "rewriting %s:" % (info.filename,)
       new_data = RewriteProps(data, misc_info)
       common.ZipWriteStr(output_tf_zip, out_info, new_data)
       if info.filename in ("BOOT/RAMDISK/default.prop",
+                           "ROOT/default.prop",
                            "RECOVERY/RAMDISK/default.prop"):
         write_to_temp(info.filename, info.external_attr, new_data)
 
@@ -255,6 +257,7 @@
     elif (OPTIONS.replace_ota_keys and
           info.filename in (
               "BOOT/RAMDISK/res/keys",
+              "BOOT/RAMDISK/etc/update_engine/update-payload-key.pub.pem",
               "RECOVERY/RAMDISK/res/keys",
               "SYSTEM/etc/security/otacerts.zip",
               "SYSTEM/etc/update_engine/update-payload-key.pub.pem")):
@@ -502,10 +505,18 @@
             " as payload verification key.\n\n")
 
     print "Using %s for payload verification." % (mapped_keys[0],)
-    common.ZipWrite(
+    cmd = common.Run(
+        ["openssl", "x509", "-pubkey", "-noout", "-in", mapped_keys[0]],
+        stdout=subprocess.PIPE)
+    pubkey, _ = cmd.communicate()
+    common.ZipWriteStr(
         output_tf_zip,
-        mapped_keys[0],
-        arcname="SYSTEM/etc/update_engine/update-payload-key.pub.pem")
+        "SYSTEM/etc/update_engine/update-payload-key.pub.pem",
+        pubkey)
+    common.ZipWriteStr(
+        output_tf_zip,
+        "BOOT/RAMDISK/etc/update_engine/update-payload-key.pub.pem",
+        pubkey)
 
   return new_recovery_keys
 
diff --git a/tools/signapk/Android.mk b/tools/signapk/Android.mk
index eff066c..4506e2f 100644
--- a/tools/signapk/Android.mk
+++ b/tools/signapk/Android.mk
@@ -22,7 +22,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_JAR_MANIFEST := SignApk.mf
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    apksigner-core \
+    apksig \
     bouncycastle-host \
     bouncycastle-bcpkix-host \
     conscrypt-host
diff --git a/tools/signapk/src/com/android/signapk/SignApk.java b/tools/signapk/src/com/android/signapk/SignApk.java
index f1f340d..84fddec 100644
--- a/tools/signapk/src/com/android/signapk/SignApk.java
+++ b/tools/signapk/src/com/android/signapk/SignApk.java
@@ -34,12 +34,12 @@
 import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
 import org.conscrypt.OpenSSLProvider;
 
-import com.android.apksigner.core.ApkSignerEngine;
-import com.android.apksigner.core.DefaultApkSignerEngine;
-import com.android.apksigner.core.apk.ApkUtils;
-import com.android.apksigner.core.util.DataSink;
-import com.android.apksigner.core.util.DataSources;
-import com.android.apksigner.core.zip.ZipFormatException;
+import com.android.apksig.ApkSignerEngine;
+import com.android.apksig.DefaultApkSignerEngine;
+import com.android.apksig.apk.ApkUtils;
+import com.android.apksig.util.DataSink;
+import com.android.apksig.util.DataSources;
+import com.android.apksig.zip.ZipFormatException;
 
 import java.io.Console;
 import java.io.BufferedReader;
diff --git a/tools/warn.py b/tools/warn.py
index b61505b..f118263 100755
--- a/tools/warn.py
+++ b/tools/warn.py
@@ -6,6 +6,14 @@
 import re
 
 parser = argparse.ArgumentParser(description='Convert a build log into HTML')
+parser.add_argument('--gencsv',
+                    help='Generate a CSV file with number of various warnings',
+                    action="store_true",
+                    default=False)
+parser.add_argument('--byproject',
+                    help='Separate warnings in HTML output by project names',
+                    action="store_true",
+                    default=False)
 parser.add_argument('--url',
                     help='Root URL of an Android source code tree prefixed '
                     'before files in warnings')
@@ -66,9 +74,13 @@
         'description':'make: overriding commands/ignoring old commands',
         'patterns':[r".*: warning: overriding commands for target .+",
                     r".*: warning: ignoring old commands for target .+"] },
+    { 'category':'make',    'severity':severity.HIGH,   'members':[], 'option':'',
+        'description':'make: LOCAL_CLANG is false',
+        'patterns':[r".*: warning: LOCAL_CLANG is set to false"] },
     { 'category':'C/C++',   'severity':severity.HIGH,     'members':[], 'option':'-Wimplicit-function-declaration',
         'description':'Implicit function declaration',
-        'patterns':[r".*: warning: implicit declaration of function .+"] },
+        'patterns':[r".*: warning: implicit declaration of function .+",
+                    r".*: warning: implicitly declaring library function" ] },
     { 'category':'C/C++',   'severity':severity.SKIP,     'members':[], 'option':'',
         'description':'',
         'patterns':[r".*: warning: conflicting types for '.+'"] },
@@ -81,6 +93,7 @@
         'description':'Potential leak of memory, bad free, use after free',
         'patterns':[r".*: warning: Potential leak of memory",
                     r".*: warning: Potential memory leak",
+                    r".*: warning: Memory allocated by alloca\(\) should not be deallocated",
                     r".*: warning: Memory allocated by .+ should be deallocated by .+ not .+",
                     r".*: warning: 'delete' applied to a pointer that was allocated",
                     r".*: warning: Use of memory after it is freed",
@@ -88,13 +101,16 @@
                     r".*: warning: Argument to free\(\) is offset by .+ of memory allocated by",
                     r".*: warning: Attempt to .+ released memory"] },
     { 'category':'C/C++',   'severity':severity.HIGH,     'members':[], 'option':'',
+        'description':'Use transient memory for control value',
+        'patterns':[r".*: warning: .+Using such transient memory for the control value is .*dangerous."] },
+    { 'category':'C/C++',   'severity':severity.HIGH,     'members':[], 'option':'',
         'description':'Return address of stack memory',
         'patterns':[r".*: warning: Address of stack memory .+ returned to caller",
                     r".*: warning: Address of stack memory .+ will be a dangling reference"] },
     { 'category':'C/C++',   'severity':severity.HIGH,     'members':[], 'option':'',
         'description':'Problem with vfork',
         'patterns':[r".*: warning: This .+ is prohibited after a successful vfork",
-                    r".*: warning: Call to function 'vfork' is insecure "] },
+                    r".*: warning: Call to function '.+' is insecure "] },
     { 'category':'C/C++',   'severity':severity.HIGH,     'members':[], 'option':'infinite-recursion',
         'description':'Infinite recursion',
         'patterns':[r".*: warning: all paths through this function will call itself"] },
@@ -112,6 +128,9 @@
     { 'category':'C/C++',   'severity':severity.HIGH,     'members':[], 'option':'-fno-builtin',
         'description':'Incompatible declaration of built in function',
         'patterns':[r".*: warning: incompatible implicit declaration of built-in function .+"] },
+    { 'category':'C/C++',   'severity':severity.HIGH,     'members':[], 'option':'',
+        'description':'Null passed as non-null argument',
+        'patterns':[r".*: warning: Null passed to a callee that requires a non-null"] },
     { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wunused-parameter',
         'description':'Unused parameter',
         'patterns':[r".*: warning: unused parameter '.*'"] },
@@ -131,15 +150,22 @@
     { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wmissing-field-initializers',
         'description':'Missing initializer',
         'patterns':[r".*: warning: missing initializer"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wdelete-non-virtual-dtor',
+        'description':'Need virtual destructor',
+        'patterns':[r".*: warning: delete called .* has virtual functions but non-virtual destructor"] },
     { 'category':'cont.',   'severity':severity.SKIP,     'members':[], 'option':'',
         'description':'',
         'patterns':[r".*: warning: \(near initialization for '.+'\)"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,     'members':[], 'option':'-Wdate-time',
+        'description':'Expansion of data or time macro',
+        'patterns':[r".*: warning: expansion of date or time macro is not reproducible"] },
     { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wformat',
         'description':'Format string does not match arguments',
         'patterns':[r".*: warning: format '.+' expects type '.+', but argument [0-9]+ has type '.+'",
                     r".*: warning: more '%' conversions than data arguments",
                     r".*: warning: data argument not used by format string",
                     r".*: warning: incomplete format specifier",
+                    r".*: warning: unknown conversion type .* in format",
                     r".*: warning: format .+ expects .+ but argument .+Wformat=",
                     r".*: warning: field precision should have .+ but argument has .+Wformat",
                     r".*: warning: format specifies type .+ but the argument has .*type .+Wformat"] },
@@ -169,28 +195,52 @@
     { 'category':'C/C++',   'severity':severity.HARMLESS, 'members':[], 'option':'',
         'description':'No newline at end of file',
         'patterns':[r".*: warning: no newline at end of file"] },
+    { 'category':'C/C++',   'severity':severity.HARMLESS, 'members':[], 'option':'',
+        'description':'Missing space after macro name',
+        'patterns':[r".*: warning: missing whitespace after the macro name"] },
+    { 'category':'C/C++',   'severity':severity.LOW, 'members':[], 'option':'-Wcast-align',
+        'description':'Cast increases required alignment',
+        'patterns':[r".*: warning: cast from .* to .* increases required alignment .*"] },
     { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wcast-qual',
         'description':'Qualifier discarded',
         'patterns':[r".*: warning: passing argument [0-9]+ of '.+' discards qualifiers from pointer target type",
                     r".*: warning: assignment discards qualifiers from pointer target type",
                     r".*: warning: passing .+ to parameter of type .+ discards qualifiers",
                     r".*: warning: assigning to .+ from .+ discards qualifiers",
+                    r".*: warning: initializing .+ discards qualifiers .+types-discards-qualifiers",
                     r".*: warning: return discards qualifiers from pointer target type"] },
-    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wattributes',
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wunknown-attributes',
+        'description':'Unknown attribute',
+        'patterns':[r".*: warning: unknown attribute '.+'"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wignored-attributes',
         'description':'Attribute ignored',
-        'patterns':[r".*: warning: '_*packed_*' attribute ignored"] },
+        'patterns':[r".*: warning: '_*packed_*' attribute ignored",
+                    r".*: warning: attribute declaration must precede definition .+ignored-attributes"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wvisibility',
+        'description':'Visibility problem',
+        'patterns':[r".*: warning: declaration of '.+' will not be visible outside of this function"] },
     { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wattributes',
         'description':'Visibility mismatch',
         'patterns':[r".*: warning: '.+' declared with greater visibility than the type of its field '.+'"] },
     { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
         'description':'Shift count greater than width of type',
         'patterns':[r".*: warning: (left|right) shift count >= width of type"] },
-    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wextern-initializer',
         'description':'extern &lt;foo&gt; is initialized',
-        'patterns':[r".*: warning: '.+' initialized and declared 'extern'"] },
+        'patterns':[r".*: warning: '.+' initialized and declared 'extern'",
+                    r".*: warning: 'extern' variable has an initializer"] },
     { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wold-style-declaration',
         'description':'Old style declaration',
         'patterns':[r".*: warning: 'static' is not at beginning of declaration"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wreturn-type',
+        'description':'Missing return value',
+        'patterns':[r".*: warning: control reaches end of non-void function"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wimplicit-int',
+        'description':'Implicit int type',
+        'patterns':[r".*: warning: type specifier missing, defaults to 'int'"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wmain-return-type',
+        'description':'Main function should return int',
+        'patterns':[r".*: warning: return type of 'main' is not 'int'"] },
     { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wuninitialized',
         'description':'Variable may be used uninitialized',
         'patterns':[r".*: warning: '.+' may be used uninitialized in this function"] },
@@ -253,7 +303,8 @@
         'patterns':[r".*: warning: dereferencing .* break strict-aliasing rules"] },
     { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wpointer-to-int-cast',
         'description':'Cast from pointer to integer of different size',
-        'patterns':[r".*: warning: cast from pointer to integer of different size"] },
+        'patterns':[r".*: warning: cast from pointer to integer of different size",
+                    r".*: warning: initialization makes pointer from integer without a cast"] } ,
     { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wint-to-pointer-cast',
         'description':'Cast to pointer from integer of different size',
         'patterns':[r".*: warning: cast to pointer from integer of different size"] },
@@ -1326,6 +1377,12 @@
     { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
         'description':'Possible broken line continuation',
         'patterns':[r".*: warning: backslash and newline separated by space"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wundefined-var-template',
+        'description':'Undefined variable template',
+        'patterns':[r".*: warning: instantiation of variable .* no definition is available"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wundefined-inline',
+        'description':'Inline function is not defined',
+        'patterns':[r".*: warning: inline function '.*' is not defined"] },
     { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Warray-bounds',
         'description':'Array subscript out of bounds',
         'patterns':[r".*: warning: array subscript is above array bounds",
@@ -1358,6 +1415,9 @@
     { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Woverflow',
         'description':'Constant too large for type, truncated',
         'patterns':[r".*: warning: large integer implicitly truncated to unsigned type"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Winteger-overflow',
+        'description':'Overflow in expression',
+        'patterns':[r".*: warning: overflow in expression; .*Winteger-overflow"] },
     { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Woverflow',
         'description':'Overflow in implicit constant conversion',
         'patterns':[r".*: warning: overflow in implicit constant conversion"] },
@@ -1380,18 +1440,31 @@
     { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wmissing-parameter-type',
         'description':'Parameter type not specified',
         'patterns':[r".*: warning: type of '.+' defaults to 'int'"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wmissing-declarations',
+        'description':'Missing declarations',
+        'patterns':[r".*: warning: declaration does not declare anything"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wmissing-noreturn',
+        'description':'Missing noreturn',
+        'patterns':[r".*: warning: function '.*' could be declared with attribute 'noreturn'"] },
     { 'category':'gcc',     'severity':severity.MEDIUM,   'members':[], 'option':'',
         'description':'Invalid option for C file',
         'patterns':[r".*: warning: command line option "".+"" is valid for C\+\+\/ObjC\+\+ but not for C"] },
     { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
         'description':'User warning',
         'patterns':[r".*: warning: #warning "".+"""] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wvexing-parse',
+        'description':'Vexing parsing problem',
+        'patterns':[r".*: warning: empty parentheses interpreted as a function declaration"] },
     { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wextra',
         'description':'Dereferencing void*',
         'patterns':[r".*: warning: dereferencing 'void \*' pointer"] },
-    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wextra',
-        'description':'Comparison of pointer to zero',
-        'patterns':[r".*: warning: ordered comparison of pointer with integer zero"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'Comparison of pointer and integer',
+        'patterns':[r".*: warning: ordered comparison of pointer with integer zero",
+                    r".*: warning: .*comparison between pointer and integer"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'Use of error-prone unary operator',
+        'patterns':[r".*: warning: use of unary operator that may be intended as compound assignment"] },
     { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wwrite-strings',
         'description':'Conversion of string constant to non-const char*',
         'patterns':[r".*: warning: deprecated conversion from string constant to '.+'"] },
@@ -1433,8 +1506,21 @@
         'description':'Comparison between different enums',
         'patterns':[r".*: warning: comparison between '.+' and '.+'.+Wenum-compare"] },
     { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wconversion',
-        'description':'Implicit conversion of negative number to unsigned type',
-        'patterns':[r".*: warning: converting negative value '.+' to '.+'"] },
+        'description':'Conversion may change value',
+        'patterns':[r".*: warning: converting negative value '.+' to '.+'",
+                    r".*: warning: conversion to '.+' .+ may alter its value"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wconversion-null',
+        'description':'Converting to non-pointer type from NULL',
+        'patterns':[r".*: warning: converting to non-pointer type '.+' from NULL"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wnull-conversion',
+        'description':'Converting NULL to non-pointer type',
+        'patterns':[r".*: warning: implicit conversion of NULL constant to '.+'"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'-Wnon-literal-null-conversion',
+        'description':'Zero used as null pointer',
+        'patterns':[r".*: warning: expression .* zero treated as a null pointer constant"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
+        'description':'Implicit conversion changes value',
+        'patterns':[r".*: warning: implicit conversion .* changes value from .* to .*-conversion"] },
     { 'category':'C/C++',   'severity':severity.MEDIUM,   'members':[], 'option':'',
         'description':'Passing NULL as non-pointer argument',
         'patterns':[r".*: warning: passing NULL to non-pointer argument [0-9]+ of '.+'"] },
@@ -1480,6 +1566,9 @@
     { 'category':'C/C++',   'severity':severity.MEDIUM,     'members':[], 'option':'writable-strings',
         'description':'Conversion from string literal to char*',
         'patterns':[r".*: warning: .+ does not allow conversion from string literal to 'char \*'"] },
+    { 'category':'C/C++',   'severity':severity.LOW,     'members':[], 'option':'-Wextra-semi',
+        'description':'Extra \';\'',
+        'patterns':[r".*: warning: extra ';' .+extra-semi"] },
     { 'category':'C/C++',   'severity':severity.LOW,     'members':[], 'option':'',
         'description':'Useless specifier',
         'patterns':[r".*: warning: useless storage class specifier in empty declaration"] },
@@ -1530,6 +1619,12 @@
     { 'category':'logtags',   'severity':severity.LOW,     'members':[], 'option':'asm-operand-widths',
         'description':'ASM value size does not match register size',
         'patterns':[r".*: warning: value size does not match register size specified by the constraint and modifier"] },
+    { 'category':'C/C++',   'severity':severity.LOW,     'members':[], 'option':'tautological-compare',
+        'description':'Comparison of self is always false',
+        'patterns':[r".*: self-comparison always evaluates to false"] },
+    { 'category':'C/C++',   'severity':severity.LOW,     'members':[], 'option':'constant-logical-operand',
+        'description':'Logical op with constant operand',
+        'patterns':[r".*: use of logical '.+' with constant operand"] },
     { 'category':'C/C++',   'severity':severity.LOW,     'members':[], 'option':'literal-suffix',
         'description':'Needs a space between literal and string macro',
         'patterns':[r".*: warning: invalid suffix on literal.+ requires a space .+Wliteral-suffix"] },
@@ -1538,7 +1633,11 @@
         'patterns':[r".*: warning: .+-W#warnings"] },
     { 'category':'C/C++',   'severity':severity.LOW,     'members':[], 'option':'absolute-value',
         'description':'Using float/int absolute value function with int/float argument',
-        'patterns':[r".*: warning: using .+ absolute value function .+ when argument is .+ type .+Wabsolute-value"] },
+        'patterns':[r".*: warning: using .+ absolute value function .+ when argument is .+ type .+Wabsolute-value",
+                    r".*: warning: absolute value function '.+' given .+ which may cause truncation .+Wabsolute-value"] },
+    { 'category':'C/C++',   'severity':severity.LOW,     'members':[], 'option':'-Wc++11-extensions',
+        'description':'Using C++11 extensions',
+        'patterns':[r".*: warning: 'auto' type specifier is a C\+\+11 extension"] },
     { 'category':'C/C++',   'severity':severity.LOW,     'members':[], 'option':'',
         'description':'Refers to implicitly defined namespace',
         'patterns':[r".*: warning: using directive refers to implicitly-defined namespace .+"] },
@@ -1549,9 +1648,10 @@
     { 'category':'C/C++',   'severity':severity.MEDIUM,     'members':[], 'option':'',
         'description':'Operator new returns NULL',
         'patterns':[r".*: warning: 'operator new' must not return NULL unless it is declared 'throw\(\)' .+"] },
-    { 'category':'C/C++',   'severity':severity.MEDIUM,     'members':[], 'option':'',
+    { 'category':'C/C++',   'severity':severity.MEDIUM,     'members':[], 'option':'-Wnull-arithmetic',
         'description':'NULL used in arithmetic',
-        'patterns':[r".*: warning: NULL used in arithmetic"] },
+        'patterns':[r".*: warning: NULL used in arithmetic",
+                    r".*: warning: comparison between NULL and non-pointer"] },
     { 'category':'C/C++',   'severity':severity.MEDIUM,     'members':[], 'option':'header-guard',
         'description':'Misspelled header guard',
         'patterns':[r".*: warning: '.+' is used as a header guard .+ followed by .+ different macro"] },
@@ -1567,6 +1667,7 @@
     { 'category':'C/C++',   'severity':severity.MEDIUM,     'members':[], 'option':'',
         'description':'Undefined result',
         'patterns':[r".*: warning: The result of .+ is undefined",
+                    r".*: warning: passing an object that .+ has undefined behavior \[-Wvarargs\]",
                     r".*: warning: 'this' pointer cannot be null in well-defined C\+\+ code;",
                     r".*: warning: shifting a negative signed value is undefined"] },
     { 'category':'C/C++',   'severity':severity.MEDIUM,     'members':[], 'option':'',
@@ -1592,6 +1693,12 @@
     { 'category':'C/C++',   'severity':severity.MEDIUM,     'members':[], 'option':'',
         'description':'Result of malloc type incompatible with sizeof operand type',
         'patterns':[r".*: warning: Result of '.+' is converted to .+ incompatible with sizeof operand type"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,     'members':[], 'option':'-Wsizeof-array-argument',
+        'description':'Sizeof on array argument',
+        'patterns':[r".*: warning: sizeof on array function parameter will return"] },
+    { 'category':'C/C++',   'severity':severity.MEDIUM,     'members':[], 'option':'-Wsizeof-pointer-memacces',
+        'description':'Bad argument size of memory access functions',
+        'patterns':[r".*: warning: .+\[-Wsizeof-pointer-memaccess\]"] },
     { 'category':'C/C++',   'severity':severity.MEDIUM,     'members':[], 'option':'',
         'description':'Return value not checked',
         'patterns':[r".*: warning: The return value from .+ is not checked"] },
@@ -1642,8 +1749,17 @@
         'description':'clang-tidy c++ core guidelines',
         'patterns':[r".*: .+\[cppcoreguidelines-.+\]$"] },
     { 'category':'C/C++',   'severity':severity.TIDY,     'members':[], 'option':'',
-        'description':'clang-tidy google-runtime',
-        'patterns':[r".*: .+\[google-runtime-.+\]$"] },
+        'description':'clang-tidy google-default-arguments',
+        'patterns':[r".*: .+\[google-default-arguments\]$"] },
+    { 'category':'C/C++',   'severity':severity.TIDY,     'members':[], 'option':'',
+        'description':'clang-tidy google-runtime-int',
+        'patterns':[r".*: .+\[google-runtime-int\]$"] },
+    { 'category':'C/C++',   'severity':severity.TIDY,     'members':[], 'option':'',
+        'description':'clang-tidy google-runtime-operator',
+        'patterns':[r".*: .+\[google-runtime-operator\]$"] },
+    { 'category':'C/C++',   'severity':severity.TIDY,     'members':[], 'option':'',
+        'description':'clang-tidy google-runtime-references',
+        'patterns':[r".*: .+\[google-runtime-references\]$"] },
     { 'category':'C/C++',   'severity':severity.TIDY,     'members':[], 'option':'',
         'description':'clang-tidy google-build',
         'patterns':[r".*: .+\[google-build-.+\]$"] },
@@ -1657,12 +1773,30 @@
         'description':'clang-tidy google-global',
         'patterns':[r".*: .+\[google-global-.+\]$"] },
     { 'category':'C/C++',   'severity':severity.TIDY,     'members':[], 'option':'',
+        'description':'clang-tidy google- other',
+        'patterns':[r".*: .+\[google-.+\]$"] },
+    { 'category':'C/C++',   'severity':severity.TIDY,     'members':[], 'option':'',
         'description':'clang-tidy modernize',
         'patterns':[r".*: .+\[modernize-.+\]$"] },
     { 'category':'C/C++',   'severity':severity.TIDY,     'members':[], 'option':'',
         'description':'clang-tidy misc',
         'patterns':[r".*: .+\[misc-.+\]$"] },
     { 'category':'C/C++',   'severity':severity.TIDY,     'members':[], 'option':'',
+        'description':'clang-tidy performance-faster-string-find',
+        'patterns':[r".*: .+\[performance-faster-string-find\]$"] },
+    { 'category':'C/C++',   'severity':severity.TIDY,     'members':[], 'option':'',
+        'description':'clang-tidy performance-for-range-copy',
+        'patterns':[r".*: .+\[performance-for-range-copy\]$"] },
+    { 'category':'C/C++',   'severity':severity.TIDY,     'members':[], 'option':'',
+        'description':'clang-tidy performance-implicit-cast-in-loop',
+        'patterns':[r".*: .+\[performance-implicit-cast-in-loop\]$"] },
+    { 'category':'C/C++',   'severity':severity.TIDY,     'members':[], 'option':'',
+        'description':'clang-tidy performance-unnecessary-copy-initialization',
+        'patterns':[r".*: .+\[performance-unnecessary-copy-initialization\]$"] },
+    { 'category':'C/C++',   'severity':severity.TIDY,     'members':[], 'option':'',
+        'description':'clang-tidy performance-unnecessary-value-param',
+        'patterns':[r".*: .+\[performance-unnecessary-value-param\]$"] },
+    { 'category':'C/C++',   'severity':severity.TIDY,     'members':[], 'option':'',
         'description':'clang-tidy CERT',
         'patterns':[r".*: .+\[cert-.+\]$"] },
     { 'category':'C/C++',   'severity':severity.TIDY,     'members':[], 'option':'',
@@ -1682,6 +1816,63 @@
         'patterns':[r".*: warning: .+"] },
 ]
 
+# A list of [project_name, file_path_pattern].
+# project_name should not contain comma, to be used in CSV output.
+projectlist = [
+    ['art',                 r"(^|.*/)art/.*: warning:"],
+    ['bionic',              r"(^|.*/)bionic/.*: warning:"],
+    ['bootable',            r"(^|.*/)bootable/.*: warning:"],
+    ['build',               r"(^|.*/)build/.*: warning:"],
+    ['cts',                 r"(^|.*/)cts/.*: warning:"],
+    ['dalvik',              r"(^|.*/)dalvik/.*: warning:"],
+    ['developers',          r"(^|.*/)developers/.*: warning:"],
+    ['development',         r"(^|.*/)development/.*: warning:"],
+    ['device',              r"(^|.*/)device/.*: warning:"],
+    ['doc',                 r"(^|.*/)doc/.*: warning:"],
+    # match external/google* before external/
+    ['external/google',     r"(^|.*/)external/google.*: warning:"],
+    ['external/non-google', r"(^|.*/)external/.*: warning:"],
+    ['frameworks',          r"(^|.*/)frameworks/.*: warning:"],
+    ['hardware',            r"(^|.*/)hardware/.*: warning:"],
+    ['kernel',              r"(^|.*/)kernel/.*: warning:"],
+    ['libcore',             r"(^|.*/)libcore/.*: warning:"],
+    ['libnativehelper',      r"(^|.*/)libnativehelper/.*: warning:"],
+    ['ndk',                 r"(^|.*/)ndk/.*: warning:"],
+    ['packages',            r"(^|.*/)packages/.*: warning:"],
+    ['pdk',                 r"(^|.*/)pdk/.*: warning:"],
+    ['prebuilts',           r"(^|.*/)prebuilts/.*: warning:"],
+    ['system',              r"(^|.*/)system/.*: warning:"],
+    ['toolchain',           r"(^|.*/)toolchain/.*: warning:"],
+    ['test',                r"(^|.*/)test/.*: warning:"],
+    ['tools',               r"(^|.*/)tools/.*: warning:"],
+    # match vendor/google* before vendor/
+    ['vendor/google',       r"(^|.*/)vendor/google.*: warning:"],
+    ['vendor/non-google',   r"(^|.*/)vendor/.*: warning:"],
+    # keep out/obj and other patterns at the end.
+    ['out/obj', r".*/(gen|obj[^/]*)/(include|EXECUTABLES|SHARED_LIBRARIES|STATIC_LIBRARIES)/.*: warning:"],
+    ['other',   r".*: warning:"],
+]
+
+projectpatterns = []
+for p in projectlist:
+    projectpatterns.append({'description':p[0], 'members':[], 'pattern':re.compile(p[1])})
+
+# Each warning pattern has 3 dictionaries:
+# (1) 'projects' maps a project name to number of warnings in that project.
+# (2) 'projectanchor' maps a project name to its anchor number for HTML.
+# (3) 'projectwarning' maps a project name to a list of warning of that project.
+for w in warnpatterns:
+    w['projects'] = {}
+    w['projectanchor'] = {}
+    w['projectwarning'] = {}
+
+platformversion = 'unknown'
+targetproduct = 'unknown'
+targetvariant = 'unknown'
+
+
+##### Data and functions to dump html file. ##################################
+
 anchor = 0
 cur_row_class = 0
 
@@ -1730,7 +1921,6 @@
     output('<title>' + title + '</title>\n')
     output(html_script_style)
     output('</head>\n<body>\n')
-    output('<a name="PageTop">')
     output(htmlbig(title))
     output('<p>\n')
 
@@ -1744,12 +1934,16 @@
     output(text)
     output('</td></tr>')
 
+def sortwarnings():
+    for i in warnpatterns:
+        i['members'] = sorted(set(i['members']))
+
 # dump some stats about total number of warnings and such
 def dumpstats():
     known = 0
     unknown = 0
+    sortwarnings()
     for i in warnpatterns:
-        i['members'] = sorted(set(i['members']))
         if i['severity'] == severity.UNKNOWN:
             unknown += len(i['members'])
         elif i['severity'] != severity.SKIP:
@@ -1775,17 +1969,12 @@
     output('<blockquote>\n')
     for i in warnpatterns:
       if i['severity'] == sev and len(i['members']) > 0:
-          output('\n<table frame="box">\n')
           anchor += 1
           i['anchor'] = str(anchor)
-          mark = str(anchor) + '_mark'
-          output('<tr bgcolor="' + colorforseverity(sev) + '">' +
-                 '<td><button class="bt" id="' + mark +
-                 '" onclick="expand(\'' + str(anchor) + '\');">' +
-                 '&#x2295</button> ' + descriptionfor(i) +
-                 ' (' + str(len(i['members'])) + ')</td></tr>\n')
-          output('</table>\n')
-          dumpcategory(i)
+          if args.byproject:
+              dumpcategorybyproject(sev, i)
+          else:
+              dumpcategory(sev, i)
     output('</blockquote>\n')
 
 def allpatterns(cat):
@@ -1840,31 +2029,69 @@
     else:
         return '<a href="' + args.url + '/' + filepath + '">' + filepath + '</a>:' + linenumber + ':' + warning
 
-# dump a category, provided it is not marked as 'SKIP' and has more than 0 occurrences
-def dumpcategory(cat):
-    if cat['severity'] != severity.SKIP and len(cat['members']) != 0:
-        header = [descriptionfor(cat),str(len(cat['members'])) + ' occurences:']
-        if cat['option'] != '':
-            header[1:1] = [' (related option: ' + cat['option'] +')']
+def dumpgroup(sev, anchor, description, warnings):
+    mark = anchor + '_mark'
+    output('\n<table frame="box">\n')
+    output('<tr bgcolor="' + colorforseverity(sev) + '">' +
+           '<td><button class="bt" id="' + mark +
+           '" onclick="expand(\'' + anchor + '\');">' +
+           '&#x2295</button> ' + description + '</td></tr>\n')
+    output('</table>\n')
+    output('<div id="' + anchor + '" style="display:none;">')
+    output('<table>\n')
+    for i in warnings:
+        tablerow(warningwithurl(i))
+    output('</table></div>\n')
 
-        output('<div id="' + cat['anchor'] + '" style="display:none;">')
-        output('<table>\n')
-        for i in cat['members']:
-            tablerow(warningwithurl(i))
-        output('</table></div>\n')
+# dump warnings in a category
+def dumpcategory(sev, cat):
+    description = descriptionfor(cat) + ' (' + str(len(cat['members'])) + ')'
+    dumpgroup(sev, cat['anchor'], description, cat['members'])
 
+# similar to dumpcategory but output one table per project.
+def dumpcategorybyproject(sev, cat):
+    warning = descriptionfor(cat)
+    projects = cat['projectwarning'].keys()
+    projects.sort()
+    for p in projects:
+        anchor = cat['projectanchor'][p]
+        projectwarnings = cat['projectwarning'][p]
+        description = '{}, in {} ({})'.format(warning, p, len(projectwarnings))
+        dumpgroup(sev, anchor, description, projectwarnings)
+
+def findproject(line):
+    for p in projectpatterns:
+        if p['pattern'].match(line):
+            return p['description']
+    return '???'
 
 def classifywarning(line):
+    global anchor
     for i in warnpatterns:
         for cpat in i['compiledpatterns']:
             if cpat.match(line):
                 i['members'].append(line)
+                pname = findproject(line)
+                # Count warnings by project.
+                if pname in i['projects']:
+                    i['projects'][pname] += 1
+                else:
+                    i['projects'][pname] = 1
+                # Collect warnings by project.
+                if args.byproject:
+                    if pname in i['projectwarning']:
+                        i['projectwarning'][pname].append(line)
+                    else:
+                        i['projectwarning'][pname] = [line]
+                    if pname not in i['projectanchor']:
+                        anchor += 1
+                        i['projectanchor'][pname] = str(anchor)
                 return
-    else:
-        # If we end up here, there was a problem parsing the log
-        # probably caused by 'make -j' mixing the output from
-        # 2 or more concurrent compiles
-        pass
+            else:
+                # If we end up here, there was a problem parsing the log
+                # probably caused by 'make -j' mixing the output from
+                # 2 or more concurrent compiles
+                pass
 
 # precompiling every pattern speeds up parsing by about 30x
 def compilepatterns():
@@ -1873,54 +2100,103 @@
         for pat in i['patterns']:
             i['compiledpatterns'].append(re.compile(pat))
 
-infile = open(args.buildlog, 'r')
-warnings = []
+def parseinputfile():
+    global platformversion
+    global targetproduct
+    global targetvariant
+    infile = open(args.buildlog, 'r')
+    linecounter = 0
 
-platformversion = 'unknown'
-targetproduct = 'unknown'
-targetvariant = 'unknown'
-linecounter = 0
+    warningpattern = re.compile('.* warning:.*')
+    compilepatterns()
 
-warningpattern = re.compile('.* warning:.*')
-compilepatterns()
-
-# read the log file and classify all the warnings
-lastmatchedline = ''
-for line in infile:
-    # replace fancy quotes with plain ol' quotes
-    line = line.replace("‘", "'");
-    line = line.replace("’", "'");
-    if warningpattern.match(line):
-        if line != lastmatchedline:
-            classifywarning(line)
-            lastmatchedline = line
-    else:
-        # save a little bit of time by only doing this for the first few lines
-        if linecounter < 50:
-            linecounter +=1
-            m = re.search('(?<=^PLATFORM_VERSION=).*', line)
-            if m != None:
-                platformversion = m.group(0)
-            m = re.search('(?<=^TARGET_PRODUCT=).*', line)
-            if m != None:
-                targetproduct = m.group(0)
-            m = re.search('(?<=^TARGET_BUILD_VARIANT=).*', line)
-            if m != None:
-                targetvariant = m.group(0)
+    # read the log file and classify all the warnings
+    warninglines = set()
+    for line in infile:
+        # replace fancy quotes with plain ol' quotes
+        line = line.replace("‘", "'");
+        line = line.replace("’", "'");
+        if warningpattern.match(line):
+            if line not in warninglines:
+                classifywarning(line)
+                warninglines.add(line)
+        else:
+            # save a little bit of time by only doing this for the first few lines
+            if linecounter < 50:
+                linecounter +=1
+                m = re.search('(?<=^PLATFORM_VERSION=).*', line)
+                if m != None:
+                    platformversion = m.group(0)
+                m = re.search('(?<=^TARGET_PRODUCT=).*', line)
+                if m != None:
+                    targetproduct = m.group(0)
+                m = re.search('(?<=^TARGET_BUILD_VARIANT=).*', line)
+                if m != None:
+                    targetvariant = m.group(0)
 
 
 # dump the html output to stdout
-dumphtmlprologue('Warnings for ' + platformversion + ' - ' + targetproduct + ' - ' + targetvariant)
-dumpstats()
-# sort table based on number of members once dumpstats has deduplicated the
-# members.
-warnpatterns.sort(reverse=True, key=lambda i: len(i['members']))
-dumpseverity(severity.FIXMENOW)
-dumpseverity(severity.HIGH)
-dumpseverity(severity.MEDIUM)
-dumpseverity(severity.LOW)
-dumpseverity(severity.TIDY)
-dumpseverity(severity.HARMLESS)
-dumpseverity(severity.UNKNOWN)
-dumpfixed()
-dumphtmlepilogue()
+def dumphtml():
+    dumphtmlprologue('Warnings for ' + platformversion + ' - ' + targetproduct + ' - ' + targetvariant)
+    dumpstats()
+    # sort table based on number of members once dumpstats has deduplicated the
+    # members.
+    warnpatterns.sort(reverse=True, key=lambda i: len(i['members']))
+    dumpseverity(severity.FIXMENOW)
+    dumpseverity(severity.HIGH)
+    dumpseverity(severity.MEDIUM)
+    dumpseverity(severity.LOW)
+    dumpseverity(severity.TIDY)
+    dumpseverity(severity.HARMLESS)
+    dumpseverity(severity.UNKNOWN)
+    dumpfixed()
+    dumphtmlepilogue()
+
+
+##### Functions to count warnings and dump csv file. #########################
+
+def descriptionforcsv(cat):
+    if cat['description'] == '':
+        return '?'
+    return cat['description']
+
+def stringforcsv(s):
+    if ',' in s:
+        return '"{}"'.format(s)
+    return s
+
+def countseverity(sev, kind):
+  sum = 0
+  for i in warnpatterns:
+      if i['severity'] == sev and len(i['members']) > 0:
+          n = len(i['members'])
+          sum += n
+          warning = stringforcsv(kind + ': ' + descriptionforcsv(i))
+          print '{},,{}'.format(n, warning)
+          # print number of warnings for each project, ordered by project name.
+          projects = i['projects'].keys()
+          projects.sort()
+          for p in projects:
+              print '{},{},{}'.format(i['projects'][p], p, warning)
+  print '{},,{}'.format(sum, kind + ' warnings')
+  return sum
+
+# dump number of warnings in csv format to stdout
+def dumpcsv():
+    sortwarnings()
+    total = 0
+    total += countseverity(severity.FIXMENOW, 'FixNow')
+    total += countseverity(severity.HIGH, 'High')
+    total += countseverity(severity.MEDIUM, 'Medium')
+    total += countseverity(severity.LOW, 'Low')
+    total += countseverity(severity.TIDY, 'Tidy')
+    total += countseverity(severity.HARMLESS, 'Harmless')
+    total += countseverity(severity.UNKNOWN, 'Unknown')
+    print '{},,{}'.format(total, 'All warnings')
+
+
+parseinputfile()
+if args.gencsv:
+    dumpcsv()
+else:
+    dumphtml()