Merge "Fix missing required dependency caused by circular dependency"
diff --git a/common/math.mk b/common/math.mk
index 83f2218..ec15f88 100644
--- a/common/math.mk
+++ b/common/math.mk
@@ -181,6 +181,22 @@
 $(call math-expect,(call numbers_less_than,4,0 2 1 3),0 2 1 3)
 $(call math-expect,(call numbers_less_than,3,0 2 1 3 2),0 2 1 2)
 
+# Returns the words in $2 that are numbers and are greater or equal to $1
+define numbers_greater_or_equal_to
+$(strip \
+  $(foreach n,$2, \
+    $(if $(call math_is_number,$(n)), \
+      $(if $(call math_gt_or_eq,$(n),$(1)), \
+        $(n)))))
+endef
+
+$(call math-expect,(call numbers_greater_or_equal_to,4,0 1 2 3),)
+$(call math-expect,(call numbers_greater_or_equal_to,3,0 2 1 3),3)
+$(call math-expect,(call numbers_greater_or_equal_to,2,0 2 1 3),2 3)
+$(call math-expect,(call numbers_greater_or_equal_to,1,0 2 1 3),2 1 3)
+$(call math-expect,(call numbers_greater_or_equal_to,0,0 2 1 3),0 2 1 3)
+$(call math-expect,(call numbers_greater_or_equal_to,1,0 2 1 3 2),2 1 3 2)
+
 _INT_LIMIT_WORDS := $(foreach a,x x,$(foreach b,x x x x x x x x x x x x x x x x,\
   $(foreach c,x x x x x x x x x x x x x x x x,x x x x x x x x x x x x x x x x)))
 
diff --git a/core/Makefile b/core/Makefile
index 8ca00ed..add62b4 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -711,6 +711,30 @@
   BUILT_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/boot.img
 endif
 
+# kernel cmdline for GKI
+GENERIC_KERNEL_CMDLINE := rw
+.KATI_READONLY := GENERIC_KERNEL_CMDLINE
+
+INTERNAL_PREBUILT_BOOTIMAGE :=
+
+my_installed_prebuilt_gki_apex := $(strip $(foreach package,$(PRODUCT_PACKAGES),$(if $(ALL_MODULES.$(package).EXTRACTED_BOOT_IMAGE),$(package))))
+ifdef my_installed_prebuilt_gki_apex
+  ifneq (1,$(words $(my_installed_prebuilt_gki_apex))) # len(my_installed_prebuilt_gki_apex) > 1
+    $(error More than one prebuilt GKI APEXes are installed: $(my_installed_prebuilt_gki_apex))
+  endif # len(my_installed_prebuilt_gki_apex) > 1
+
+  ifdef BOARD_PREBUILT_BOOTIMAGE
+    $(error Must not define BOARD_PREBUILT_BOOTIMAGE because a prebuilt GKI APEX is installed: $(my_installed_prebuilt_gki_apex))
+  endif # BOARD_PREBUILT_BOOTIMAGE defined
+
+  my_apex_extracted_boot_image := $(ALL_MODULES.$(my_installed_prebuilt_gki_apex).EXTRACTED_BOOT_IMAGE)
+  INSTALLED_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/boot.img
+  $(eval $(call copy-one-file,$(my_apex_extracted_boot_image),$(INSTALLED_BOOTIMAGE_TARGET)))
+
+  INTERNAL_PREBUILT_BOOTIMAGE := $(my_apex_extracted_boot_image)
+
+else # my_installed_prebuilt_gki_apex not defined
+
 # $1: boot image target
 # returns the kernel used to make the bootimage
 define bootimage-to-kernel
@@ -754,30 +778,27 @@
 
 INTERNAL_KERNEL_CMDLINE := $(strip $(INTERNAL_KERNEL_CMDLINE) buildvariant=$(TARGET_BUILD_VARIANT) $(VERITY_KEYID))
 
-boot_uses_generic_kernel_image :=
-ifdef BUILDING_VENDOR_BOOT_IMAGE
-  # building vendor boot image, dtb/base/pagesize go there
-  boot_uses_generic_kernel_image := true
-else ifeq (true,$(BOARD_USES_GENERIC_KERNEL_IMAGE))
-  boot_uses_generic_kernel_image := true
-endif
-
-ifeq (true,$(boot_uses_generic_kernel_image))
+# kernel cmdline/base/pagesize in boot.
+# - If using GKI, use GENERIC_KERNEL_CMDLINE. Remove kernel base and pagesize because they are
+#   device-specific.
+# - If not using GKI:
+#   - If building vendor_boot, INTERNAL_KERNEL_CMDLINE, base and pagesize goes in vendor_boot.
+#   - Otherwise, put them in boot.
+ifeq (true,$(BOARD_USES_GENERIC_KERNEL_IMAGE))
   ifdef GENERIC_KERNEL_CMDLINE
     INTERNAL_BOOTIMAGE_ARGS += --cmdline "$(GENERIC_KERNEL_CMDLINE)"
   endif
-else # boot_uses_generic_kernel_image != true
-ifdef BOARD_KERNEL_BASE
-  INTERNAL_BOOTIMAGE_ARGS += --base $(BOARD_KERNEL_BASE)
-endif
-ifdef BOARD_KERNEL_PAGESIZE
-  INTERNAL_BOOTIMAGE_ARGS += --pagesize $(BOARD_KERNEL_PAGESIZE)
-endif
-ifdef INTERNAL_KERNEL_CMDLINE
-  INTERNAL_BOOTIMAGE_ARGS += --cmdline "$(INTERNAL_KERNEL_CMDLINE)"
-endif
-endif # boot_uses_generic_kernel_image == true
-boot_uses_generic_kernel_image :=
+else ifndef BUILDING_VENDOR_BOOT_IMAGE # && BOARD_USES_GENERIC_KERNEL_IMAGE != true
+  ifdef INTERNAL_KERNEL_CMDLINE
+    INTERNAL_BOOTIMAGE_ARGS += --cmdline "$(INTERNAL_KERNEL_CMDLINE)"
+  endif
+  ifdef BOARD_KERNEL_BASE
+    INTERNAL_BOOTIMAGE_ARGS += --base $(BOARD_KERNEL_BASE)
+  endif
+  ifdef BOARD_KERNEL_PAGESIZE
+    INTERNAL_BOOTIMAGE_ARGS += --pagesize $(BOARD_KERNEL_PAGESIZE)
+  endif
+endif # BUILDING_VENDOR_BOOT_IMAGE == "" && BOARD_USES_GENERIC_KERNEL_IMAGE != true
 
 INTERNAL_MKBOOTIMG_VERSION_ARGS := \
     --os_version $(PLATFORM_VERSION_LAST_STABLE) \
@@ -877,12 +898,17 @@
 # Remove when b/63676296 is resolved.
 $(error Prebuilt bootimage is only supported for AB targets)
 endif
+INTERNAL_PREBUILT_BOOTIMAGE := $(BOARD_PREBUILT_BOOTIMAGE)
 INSTALLED_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/boot.img
-$(eval $(call copy-one-file,$(BOARD_PREBUILT_BOOTIMAGE),$(INSTALLED_BOOTIMAGE_TARGET)))
+$(eval $(call copy-one-file,$(INTERNAL_PREBUILT_BOOTIMAGE),$(INSTALLED_BOOTIMAGE_TARGET)))
 else # BOARD_PREBUILT_BOOTIMAGE not defined
 INSTALLED_BOOTIMAGE_TARGET :=
 endif # BOARD_PREBUILT_BOOTIMAGE
 endif # TARGET_NO_KERNEL
+endif # my_installed_prebuilt_gki_apex not defined
+
+my_apex_extracted_boot_image :=
+my_installed_prebuilt_gki_apex :=
 
 # -----------------------------------------------------------------
 # declare recovery ramdisk files
@@ -1275,6 +1301,9 @@
 ifneq (true,$(TARGET_USERIMAGES_SPARSE_EXT_DISABLED))
   INTERNAL_USERIMAGES_SPARSE_EXT_FLAG := -s
 endif
+ifneq (true,$(TARGET_USERIMAGES_SPARSE_EROFS_DISABLED))
+  INTERNAL_USERIMAGES_SPARSE_EROFS_FLAG := -s
+endif
 ifneq (true,$(TARGET_USERIMAGES_SPARSE_SQUASHFS_DISABLED))
   INTERNAL_USERIMAGES_SPARSE_SQUASHFS_FLAG := -s
 endif
@@ -1299,6 +1328,18 @@
     $(BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE) \
     $(BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_TYPE) \
     $(BOARD_ODM_DLKMIMAGE_FILE_SYSTEM_TYPE) \
+  ,erofs),)
+INTERNAL_USERIMAGES_DEPS += $(MKEROFSUSERIMG)
+endif
+
+ifneq ($(filter \
+    $(BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE) \
+    $(BOARD_SYSTEM_EXTIMAGE_FILE_SYSTEM_TYPE) \
+    $(BOARD_ODMIMAGE_FILE_SYSTEM_TYPE) \
+    $(BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE) \
+    $(BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE) \
+    $(BOARD_VENDOR_DLKMIMAGE_FILE_SYSTEM_TYPE) \
+    $(BOARD_ODM_DLKMIMAGE_FILE_SYSTEM_TYPE) \
   ,squashfs),)
 INTERNAL_USERIMAGES_DEPS += $(MKSQUASHFSUSERIMG)
 endif
@@ -1471,6 +1512,7 @@
 
 $(if $(INTERNAL_USERIMAGES_EXT_VARIANT),$(hide) echo "fs_type=$(INTERNAL_USERIMAGES_EXT_VARIANT)" >> $(1))
 $(if $(INTERNAL_USERIMAGES_SPARSE_EXT_FLAG),$(hide) echo "extfs_sparse_flag=$(INTERNAL_USERIMAGES_SPARSE_EXT_FLAG)" >> $(1))
+$(if $(INTERNAL_USERIMAGES_SPARSE_EROFS_FLAG),$(hide) echo "erofs_sparse_flag=$(INTERNAL_USERIMAGES_SPARSE_EROFS_FLAG)" >> $(1))
 $(if $(INTERNAL_USERIMAGES_SPARSE_SQUASHFS_FLAG),$(hide) echo "squashfs_sparse_flag=$(INTERNAL_USERIMAGES_SPARSE_SQUASHFS_FLAG)" >> $(1))
 $(if $(INTERNAL_USERIMAGES_SPARSE_F2FS_FLAG),$(hide) echo "f2fs_sparse_flag=$(INTERNAL_USERIMAGES_SPARSE_F2FS_FLAG)" >> $(1))
 $(if $(BOARD_EXT4_SHARE_DUP_BLOCKS),$(hide) echo "ext4_share_dup_blocks=$(BOARD_EXT4_SHARE_DUP_BLOCKS)" >> $(1))
@@ -1893,19 +1935,10 @@
 	$(copy-file-to-target)
 endif
 
-ifeq (truetrue,$(strip $(BUILDING_VENDOR_BOOT_IMAGE))$(strip $(AB_OTA_UPDATER)))
-  INTERNAL_RECOVERYIMAGE_ARGS := --ramdisk $(recovery_ramdisk)
+INTERNAL_RECOVERYIMAGE_ARGS := --ramdisk $(recovery_ramdisk)
 
-ifneq (true,$(BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE))
-ifdef GENERIC_KERNEL_CMDLINE
-  INTERNAL_RECOVERYIMAGE_ARGS += --cmdline "$(GENERIC_KERNEL_CMDLINE)"
-endif # GENERIC_KERNEL_CMDLINE != ""
-endif # BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE != true
-
-else # not (BUILDING_VENDOR_BOOT_IMAGE and AB_OTA_UPDATER)
-  INTERNAL_RECOVERYIMAGE_ARGS := \
-      $(addprefix --second ,$(INSTALLED_2NDBOOTLOADER_TARGET)) \
-      --ramdisk $(recovery_ramdisk)
+ifneq (truetrue,$(strip $(BUILDING_VENDOR_BOOT_IMAGE))$(strip $(BOARD_USES_RECOVERY_AS_BOOT)))
+INTERNAL_RECOVERYIMAGE_ARGS += $(addprefix --second ,$(INSTALLED_2NDBOOTLOADER_TARGET))
 # Assumes this has already been stripped
 ifneq (true,$(BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE))
 ifdef INTERNAL_KERNEL_CMDLINE
@@ -1931,7 +1964,7 @@
 ifdef BOARD_INCLUDE_DTB_IN_BOOTIMG
   INTERNAL_RECOVERYIMAGE_ARGS += --dtb $(INSTALLED_DTBIMAGE_TARGET)
 endif
-endif # INSTALLED_VENDOR_BOOTIMAGE_TARGET not defined
+endif # (BUILDING_VENDOR_BOOT_IMAGE and BOARD_USES_RECOVERY_AS_BOOT)
 ifndef BOARD_RECOVERY_MKBOOTIMG_ARGS
   BOARD_RECOVERY_MKBOOTIMG_ARGS := $(BOARD_MKBOOTIMG_ARGS)
 endif
@@ -3927,6 +3960,8 @@
   mkbootimg \
   mke2fs \
   mke2fs.conf \
+  mkfs.erofs \
+  mkerofsimage.sh \
   mkf2fsuserimg.sh \
   mksquashfs \
   mksquashfsimage.sh \
@@ -4411,7 +4446,7 @@
 
 ifdef BUILDING_BOOT_IMAGE
   $(BUILT_TARGET_FILES_PACKAGE): $(INTERNAL_RAMDISK_FILES)
-else ifdef BOARD_PREBUILT_BOOTIMAGE
+else ifdef INTERNAL_PREBUILT_BOOTIMAGE
   $(BUILT_TARGET_FILES_PACKAGE): $(INSTALLED_BOOTIMAGE_TARGET)
 endif
 
@@ -4493,11 +4528,7 @@
 	cp $(firstword $(INSTALLED_KERNEL_TARGET)) $(zip_root)/$(PRIVATE_RECOVERY_OUT)/kernel
 endif
 endif
-ifeq (truetrue,$(strip $(BUILDING_VENDOR_BOOT_IMAGE))$(strip $(AB_OTA_UPDATER)))
-ifneq (true,$(BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE))
-	echo "$(GENERIC_KERNEL_CMDLINE)" > $(zip_root)/$(PRIVATE_RECOVERY_OUT)/cmdline
-endif # BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE != true
-else # not (BUILDING_VENDOR_BOOT_IMAGE and AB_OTA_UPDATER)
+ifneq (truetrue,$(strip $(BUILDING_VENDOR_BOOT_IMAGE))$(strip $(BOARD_USES_RECOVERY_AS_BOOT)))
 ifdef INSTALLED_2NDBOOTLOADER_TARGET
 	cp $(INSTALLED_2NDBOOTLOADER_TARGET) $(zip_root)/$(PRIVATE_RECOVERY_OUT)/second
 endif
@@ -4525,7 +4556,7 @@
 ifdef BOARD_KERNEL_PAGESIZE
 	echo "$(BOARD_KERNEL_PAGESIZE)" > $(zip_root)/$(PRIVATE_RECOVERY_OUT)/pagesize
 endif
-endif # not (BUILDING_VENDOR_BOOT_IMAGE and AB_OTA_UPDATER)
+endif # not (BUILDING_VENDOR_BOOT_IMAGE and BOARD_USES_RECOVERY_AS_BOOT)
 endif # INSTALLED_RECOVERYIMAGE_TARGET defined or BOARD_USES_RECOVERY_AS_BOOT is true
 	@# Components of the boot image
 	$(hide) mkdir -p $(zip_root)/BOOT
@@ -4541,11 +4572,9 @@
 ifdef INSTALLED_KERNEL_TARGET
 	$(hide) cp $(INSTALLED_KERNEL_TARGET) $(zip_root)/BOOT/
 endif
-ifdef INSTALLED_VENDOR_BOOTIMAGE_TARGET
+ifeq (true,$(BOARD_USES_GENERIC_KERNEL_IMAGE))
 	echo "$(GENERIC_KERNEL_CMDLINE)" > $(zip_root)/BOOT/cmdline
-else ifeq (true,$(BOARD_USES_GENERIC_KERNEL_IMAGE))
-	echo "$(GENERIC_KERNEL_CMDLINE)" > $(zip_root)/BOOT/cmdline
-else # INSTALLED_VENDOR_BOOTIMAGE_TARGET == "" && BOARD_USES_GENERIC_KERNEL_IMAGE != true
+else ifndef INSTALLED_VENDOR_BOOTIMAGE_TARGET # && BOARD_USES_GENERIC_KERNEL_IMAGE != true
 	echo "$(INTERNAL_KERNEL_CMDLINE)" > $(zip_root)/BOOT/cmdline
 ifdef INSTALLED_2NDBOOTLOADER_TARGET
 	cp $(INSTALLED_2NDBOOTLOADER_TARGET) $(zip_root)/BOOT/second
@@ -4716,7 +4745,7 @@
 	$(hide) mkdir -p $(zip_root)/IMAGES
 	$(hide) cp $(INSTALLED_SYSTEM_EXTIMAGE_TARGET) $(zip_root)/IMAGES/
 endif
-ifdef BOARD_PREBUILT_BOOTIMAGE
+ifdef INTERNAL_PREBUILT_BOOTIMAGE
 	$(hide) mkdir -p $(zip_root)/IMAGES
 	$(hide) cp $(INSTALLED_BOOTIMAGE_TARGET) $(zip_root)/IMAGES/
 endif
diff --git a/core/config.mk b/core/config.mk
index e975214..f860c43 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -555,6 +555,7 @@
 FS_GET_STATS := $(HOST_OUT_EXECUTABLES)/fs_get_stats$(HOST_EXECUTABLE_SUFFIX)
 MKEXTUSERIMG := $(HOST_OUT_EXECUTABLES)/mkuserimg_mke2fs
 MKE2FS_CONF := system/extras/ext4_utils/mke2fs.conf
+MKEROFSUSERIMG := $(HOST_OUT_EXECUTABLES)/mkerofsimage.sh
 MKSQUASHFSUSERIMG := $(HOST_OUT_EXECUTABLES)/mksquashfsimage.sh
 MKF2FSUSERIMG := $(HOST_OUT_EXECUTABLES)/mkf2fsuserimg.sh
 SIMG2IMG := $(HOST_OUT_EXECUTABLES)/simg2img$(HOST_EXECUTABLE_SUFFIX)
diff --git a/core/dumpvar.mk b/core/dumpvar.mk
index b2ee8fd..6b5c030 100644
--- a/core/dumpvar.mk
+++ b/core/dumpvar.mk
@@ -24,9 +24,14 @@
 # Input variables:
 #   DUMP_MANY_VARS: the list of variable names.
 #   DUMP_VAR_PREFIX: an optional prefix of the variable name added to the output.
+# The value is printed in parts because large variables like PRODUCT_PACKAGES
+# can exceed the maximum linux command line size
 .PHONY: dump-many-vars
 dump-many-vars :
 	@$(foreach v, $(DUMP_MANY_VARS),\
-	  printf "%s='%s'\n" '$(DUMP_VAR_PREFIX)$(v)' '$($(v))';)
+	  printf "%s='%s" '$(DUMP_VAR_PREFIX)$(v)' '$(firstword $($(v)))'; \
+	  $(foreach part, $(wordlist 2, $(words $($(v))), $($(v))),\
+	    printf " %s" '$(part)'$(newline))\
+	  printf "'\n";)
 
 endif # CALLED_FROM_SETUP
diff --git a/core/main.mk b/core/main.mk
index 7179e0b..9b19761 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -893,7 +893,7 @@
 # Scan all modules in general-tests, device-tests and other selected suites and
 # flatten the shared library dependencies.
 define update-host-shared-libs-deps-for-suites
-$(foreach suite,general-tests device-tests vts art-host-tests,\
+$(foreach suite,general-tests device-tests vts art-host-tests host-unit-tests,\
   $(foreach m,$(COMPATIBILITY.$(suite).MODULES),\
     $(eval my_deps := $(call get-all-shared-libs-deps,$(m)))\
     $(foreach dep,$(my_deps),\
@@ -1897,6 +1897,11 @@
 ndk: $(SOONG_OUT_DIR)/ndk.timestamp
 .PHONY: ndk
 
+# Checks that build/soong/apex/allowed_deps.txt remains up to date
+ifneq ($(UNSAFE_DISABLE_APEX_ALLOWED_DEPS_CHECK),true)
+  droidcore: ${APEX_ALLOWED_DEPS_CHECK}
+endif
+
 $(call dist-write-file,$(KATI_PACKAGE_MK_DIR)/dist.mk)
 
 $(info [$(call inc_and_print,subdir_makefiles_inc)/$(subdir_makefiles_total)] writing build rules ...)
diff --git a/core/soong_config.mk b/core/soong_config.mk
index c221c55..809a4d6 100644
--- a/core/soong_config.mk
+++ b/core/soong_config.mk
@@ -42,6 +42,7 @@
 $(call add_json_bool, Unbundled_build,                   $(TARGET_BUILD_UNBUNDLED))
 $(call add_json_bool, Unbundled_build_apps,              $(TARGET_BUILD_APPS))
 $(call add_json_bool, Always_use_prebuilt_sdks,          $(TARGET_BUILD_USE_PREBUILT_SDKS))
+$(call add_json_bool, Skip_boot_jars_check,              $(SKIP_BOOT_JARS_CHECK))
 
 $(call add_json_bool, Debuggable,                        $(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
 $(call add_json_bool, Eng,                               $(filter eng,$(TARGET_BUILD_VARIANT)))
diff --git a/core/tasks/boot_jars_package_check.mk b/core/tasks/boot_jars_package_check.mk
deleted file mode 100644
index baa378a..0000000
--- a/core/tasks/boot_jars_package_check.mk
+++ /dev/null
@@ -1,61 +0,0 @@
-# Copyright (C) 2014 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.
-
-#
-# Rules to check if classes in the boot jars are from the list of allowed packages.
-#
-
-ifneq ($(SKIP_BOOT_JARS_CHECK),true)
-ifdef PRODUCT_BOOT_JARS
-
-intermediates := $(call intermediates-dir-for, PACKAGING, boot-jars-package-check,,COMMON)
-stamp := $(intermediates)/stamp
-
-# Convert the colon-separated components <apex>:<jar> to <jar>.<apex> names
-# (e.g. com.android.media:updatable-media -> updatable-media.com.android.media).
-# Special cases:
-#   - for the "platform" or "system_ext" apex drop the .<apex> suffix
-boot_jars := $(foreach pair,$(PRODUCT_BOOT_JARS) $(PRODUCT_UPDATABLE_BOOT_JARS), \
-  $(eval apex := $(call word-colon,1,$(pair))) \
-  $(eval jar := $(call word-colon,2,$(pair))) \
-  $(eval q := :) \
-  $(eval sfx := $(q).$(apex)$(q)) \
-  $(eval sfx := $(subst $(q).platform$(q),$(q)$(q),$(sfx))) \
-  $(eval sfx := $(subst $(q).system_ext$(q),$(q)$(q),$(sfx))) \
-  $(eval sfx := $(patsubst $(q)%$(q),%,$(sfx))) \
-  $(jar)$(sfx))
-
-# Convert boot jar names to build paths.
-built_boot_jars := $(foreach j, $(boot_jars), \
-  $(call intermediates-dir-for, JAVA_LIBRARIES, $(j),,COMMON)/classes.jar)
-
-script := build/make/core/tasks/check_boot_jars/check_boot_jars.py
-allowed_file := build/make/core/tasks/check_boot_jars/package_allowed_list.txt
-
-$(stamp): PRIVATE_BOOT_JARS := $(built_boot_jars)
-$(stamp): PRIVATE_SCRIPT := $(script)
-$(stamp): PRIVATE_ALLOWED := $(allowed_file)
-$(stamp) : $(built_boot_jars) $(script) $(allowed_file)
-	@echo "Check package name for $(PRIVATE_BOOT_JARS)"
-	$(hide) $(PRIVATE_SCRIPT) $(PRIVATE_ALLOWED) $(PRIVATE_BOOT_JARS)
-	$(hide) mkdir -p $(dir $@) && touch $@
-
-.PHONY: check-boot-jars
-check-boot-jars : $(stamp)
-
-# Run check-boot-jars by default
-droidcore : check-boot-jars
-
-endif  # PRODUCT_BOOT_JARS
-endif  # SKIP_BOOT_JARS_CHECK not true
diff --git a/core/tasks/check_boot_jars/check_boot_jars.py b/core/tasks/check_boot_jars/check_boot_jars.py
deleted file mode 100755
index cf4ef27..0000000
--- a/core/tasks/check_boot_jars/check_boot_jars.py
+++ /dev/null
@@ -1,89 +0,0 @@
-#!/usr/bin/env python
-
-"""
-Check boot jars.
-
-Usage: check_boot_jars.py <package_allow_list_file> <jar1> <jar2> ...
-"""
-import logging
-import os.path
-import re
-import subprocess
-import sys
-
-
-# The compiled allow list RE.
-allow_list_re = None
-
-
-def LoadAllowList(filename):
-  """ Load and compile allow list regular expressions from filename.
-  """
-  lines = []
-  with open(filename, 'r') as f:
-    for line in f:
-      line = line.strip()
-      if not line or line.startswith('#'):
-        continue
-      lines.append(line)
-  combined_re = r'^(%s)$' % '|'.join(lines)
-  global allow_list_re
-  try:
-    allow_list_re = re.compile(combined_re)
-  except re.error:
-    logging.exception(
-        'Cannot compile package allow list regular expression: %r',
-        combined_re)
-    allow_list_re = None
-    return False
-  return True
-
-
-def CheckJar(allow_list_path, jar):
-  """Check a jar file.
-  """
-  # Get the list of files inside the jar file.
-  p = subprocess.Popen(args='jar tf %s' % jar,
-      stdout=subprocess.PIPE, shell=True)
-  stdout, _ = p.communicate()
-  if p.returncode != 0:
-    return False
-  items = stdout.split()
-  classes = 0
-  for f in items:
-    if f.endswith('.class'):
-      classes += 1
-      package_name = os.path.dirname(f)
-      package_name = package_name.replace('/', '.')
-      if not package_name or not allow_list_re.match(package_name):
-        print >> sys.stderr, ('Error: %s contains class file %s, whose package name %s is empty or'
-                              ' not in the allow list %s of packages allowed on the bootclasspath.'
-                              % (jar, f, package_name, allow_list_path))
-        return False
-  if classes == 0:
-    print >> sys.stderr, ('Error: %s does not contain any class files.' % jar)
-    return False
-  return True
-
-
-def main(argv):
-  if len(argv) < 2:
-    print __doc__
-    return 1
-  allow_list_path = argv[0]
-
-  if not LoadAllowList(allow_list_path):
-    return 1
-
-  passed = True
-  for jar in argv[1:]:
-    if not CheckJar(allow_list_path, jar):
-      passed = False
-  if not passed:
-    return 1
-
-  return 0
-
-
-if __name__ == '__main__':
-  sys.exit(main(sys.argv[1:]))
diff --git a/core/tasks/check_boot_jars/package_allowed_list.txt b/core/tasks/check_boot_jars/package_allowed_list.txt
deleted file mode 100644
index 18ab427..0000000
--- a/core/tasks/check_boot_jars/package_allowed_list.txt
+++ /dev/null
@@ -1,248 +0,0 @@
-# Boot jar package name allowed list.
-# Each line is interpreted as a regular expression.
-
-###################################################
-# core-libart.jar & core-oj.jar
-java\.awt\.font
-java\.beans
-java\.io
-java\.lang
-java\.lang\.annotation
-java\.lang\.invoke
-java\.lang\.ref
-java\.lang\.reflect
-java\.math
-java\.net
-java\.nio
-java\.nio\.file
-java\.nio\.file\.spi
-java\.nio\.file\.attribute
-java\.nio\.channels
-java\.nio\.channels\.spi
-java\.nio\.charset
-java\.nio\.charset\.spi
-java\.security
-java\.security\.acl
-java\.security\.cert
-java\.security\.interfaces
-java\.security\.spec
-java\.sql
-java\.text
-java\.text\.spi
-java\.time
-java\.time\.chrono
-java\.time\.format
-java\.time\.temporal
-java\.time\.zone
-java\.util
-java\.util\.concurrent
-java\.util\.concurrent\.atomic
-java\.util\.concurrent\.locks
-java\.util\.function
-java\.util\.jar
-java\.util\.logging
-java\.util\.prefs
-java\.util\.regex
-java\.util\.spi
-java\.util\.stream
-java\.util\.zip
-# TODO: Remove javax.annotation.processing if possible, see http://b/132338110:
-javax\.annotation\.processing
-javax\.crypto
-javax\.crypto\.interfaces
-javax\.crypto\.spec
-javax\.net
-javax\.net\.ssl
-javax\.security\.auth
-javax\.security\.auth\.callback
-javax\.security\.auth\.login
-javax\.security\.auth\.x500
-javax\.security\.cert
-javax\.sql
-javax\.xml
-javax\.xml\.datatype
-javax\.xml\.namespace
-javax\.xml\.parsers
-javax\.xml\.transform
-javax\.xml\.transform\.dom
-javax\.xml\.transform\.sax
-javax\.xml\.transform\.stream
-javax\.xml\.validation
-javax\.xml\.xpath
-jdk\.internal\.util
-jdk\.internal\.vm\.annotation
-jdk\.net
-org\.w3c\.dom
-org\.w3c\.dom\.ls
-org\.w3c\.dom\.traversal
-# OpenJdk internal implementation.
-sun\.invoke\.util
-sun\.invoke\.empty
-sun\.misc
-sun\.util.*
-sun\.text.*
-sun\.security.*
-sun\.reflect.*
-sun\.nio.*
-sun\.net.*
-com\.sun\..*
-
-# TODO: Move these internal org.apache.harmony classes to libcore.*
-org\.apache\.harmony\.crypto\.internal
-org\.apache\.harmony\.dalvik
-org\.apache\.harmony\.dalvik\.ddmc
-org\.apache\.harmony\.luni\.internal\.util
-org\.apache\.harmony\.security
-org\.apache\.harmony\.security\.asn1
-org\.apache\.harmony\.security\.fortress
-org\.apache\.harmony\.security\.pkcs10
-org\.apache\.harmony\.security\.pkcs7
-org\.apache\.harmony\.security\.pkcs8
-org\.apache\.harmony\.security\.provider\.crypto
-org\.apache\.harmony\.security\.utils
-org\.apache\.harmony\.security\.x501
-org\.apache\.harmony\.security\.x509
-org\.apache\.harmony\.security\.x509\.tsp
-org\.apache\.harmony\.xml
-org\.apache\.harmony\.xml\.dom
-org\.apache\.harmony\.xml\.parsers
-
-org\.json
-org\.xmlpull\.v1
-org\.xmlpull\.v1\.sax2
-
-# TODO:  jarjar org.kxml2.io to com.android org\.kxml2\.io
-org\.kxml2\.io
-org\.xml
-org\.xml\.sax
-org\.xml\.sax\.ext
-org\.xml\.sax\.helpers
-
-dalvik\..*
-libcore\..*
-android\..*
-com\.android\..*
-###################################################
-# android.test.base.jar
-junit\.extensions
-junit\.framework
-android\.test
-android\.test\.suitebuilder\.annotation
-
-
-###################################################
-# ext.jar
-# TODO: jarjar javax.sip to com.android
-javax\.sip
-javax\.sip\.address
-javax\.sip\.header
-javax\.sip\.message
-
-# TODO: jarjar org.apache.commons to com.android
-org\.apache\.commons\.codec
-org\.apache\.commons\.codec\.binary
-org\.apache\.commons\.codec\.language
-org\.apache\.commons\.codec\.net
-org\.apache\.commons\.logging
-org\.apache\.commons\.logging\.impl
-org\.apache\.http
-org\.apache\.http\.auth
-org\.apache\.http\.auth\.params
-org\.apache\.http\.client
-org\.apache\.http\.client\.entity
-org\.apache\.http\.client\.methods
-org\.apache\.http\.client\.params
-org\.apache\.http\.client\.protocol
-org\.apache\.http\.client\.utils
-org\.apache\.http\.conn
-org\.apache\.http\.conn\.params
-org\.apache\.http\.conn\.routing
-org\.apache\.http\.conn\.scheme
-org\.apache\.http\.conn\.ssl
-org\.apache\.http\.conn\.util
-org\.apache\.http\.cookie
-org\.apache\.http\.cookie\.params
-org\.apache\.http\.entity
-org\.apache\.http\.impl
-org\.apache\.http\.impl\.auth
-org\.apache\.http\.impl\.client
-org\.apache\.http\.impl\.client
-org\.apache\.http\.impl\.conn
-org\.apache\.http\.impl\.conn\.tsccm
-org\.apache\.http\.impl\.cookie
-org\.apache\.http\.impl\.entity
-org\.apache\.http\.impl\.io
-org\.apache\.http\.impl\.io
-org\.apache\.http\.io
-org\.apache\.http\.message
-org\.apache\.http\.params
-org\.apache\.http\.protocol
-org\.apache\.http\.util
-
-# TODO: jarjar gov.nist to com.android
-gov\.nist\.core
-gov\.nist\.core\.net
-gov\.nist\.javax\.sip
-gov\.nist\.javax\.sip\.address
-gov\.nist\.javax\.sip\.clientauthutils
-gov\.nist\.javax\.sip\.header
-gov\.nist\.javax\.sip\.header\.extensions
-gov\.nist\.javax\.sip\.header\.ims
-gov\.nist\.javax\.sip\.message
-gov\.nist\.javax\.sip\.parser
-gov\.nist\.javax\.sip\.parser\.extensions
-gov\.nist\.javax\.sip\.parser\.ims
-gov\.nist\.javax\.sip\.stack
-
-org\.ccil\.cowan\.tagsoup
-org\.ccil\.cowan\.tagsoup\.jaxp
-
-###################################################
-# framework.jar
-javax\.microedition\.khronos\.opengles
-javax\.microedition\.khronos\.egl
-
-android
-
-###################################################
-# apache-xml.jar
-org\.apache\.xml\.res
-org\.apache\.xml\.utils
-org\.apache\.xml\.utils\.res
-org\.apache\.xml\.dtm
-org\.apache\.xml\.dtm\.ref
-org\.apache\.xml\.dtm\.ref\.dom2dtm
-org\.apache\.xml\.dtm\.ref\.sax2dtm
-org\.apache\.xml\.serializer
-org\.apache\.xml\.serializer\.utils
-org\.apache\.xml\.serializer\.dom3
-org\.apache\.xpath
-org\.apache\.xpath\.operations
-org\.apache\.xpath\.domapi
-org\.apache\.xpath\.functions
-org\.apache\.xpath\.res
-org\.apache\.xpath\.axes
-org\.apache\.xpath\.objects
-org\.apache\.xpath\.patterns
-org\.apache\.xpath\.jaxp
-org\.apache\.xpath\.compiler
-org\.apache\.xalan
-org\.apache\.xalan\.res
-org\.apache\.xalan\.templates
-org\.apache\.xalan\.serialize
-org\.apache\.xalan\.extensions
-org\.apache\.xalan\.processor
-org\.apache\.xalan\.transformer
-org\.apache\.xalan\.xslt
-
-###################################################
-# Packages in the google namespace across all bootclasspath jars.
-com\.google\.android\..*
-com\.google\.vr\.platform.*
-com\.google\.i18n\.phonenumbers\..*
-com\.google\.i18n\.phonenumbers
-
-###################################################
-# Packages used for Android in Chrome OS
-org\.chromium\.arc
-org\.chromium\.arc\..*
diff --git a/core/tasks/find-shareduid-violation.mk b/core/tasks/find-shareduid-violation.mk
index 972b1ec..d6885eb 100644
--- a/core/tasks/find-shareduid-violation.mk
+++ b/core/tasks/find-shareduid-violation.mk
@@ -16,8 +16,6 @@
 
 shareduid_violation_modules_filename := $(PRODUCT_OUT)/shareduid_violation_modules.json
 
-find_shareduid_script := $(BUILD_SYSTEM)/tasks/find-shareduid-violation.py
-
 $(shareduid_violation_modules_filename): $(INSTALLED_SYSTEMIMAGE_TARGET) \
     $(INSTALLED_RAMDISK_TARGET) \
     $(INSTALLED_BOOTIMAGE_TARGET) \
@@ -26,9 +24,9 @@
     $(INSTALLED_PRODUCTIMAGE_TARGET) \
     $(INSTALLED_SYSTEM_EXTIMAGE_TARGET)
 
-$(shareduid_violation_modules_filename): $(find_shareduid_script)
+$(shareduid_violation_modules_filename): $(HOST_OUT_EXECUTABLES)/find_shareduid_violation
 $(shareduid_violation_modules_filename): $(AAPT2)
-	$(find_shareduid_script) \
+	$(HOST_OUT_EXECUTABLES)/find_shareduid_violation \
 		--product_out $(PRODUCT_OUT) \
 		--aapt $(AAPT2) \
 		--copy_out_system $(TARGET_COPY_OUT_SYSTEM) \
diff --git a/core/tasks/find-shareduid-violation.py b/core/tasks/find-shareduid-violation.py
deleted file mode 100755
index 8dba5a1..0000000
--- a/core/tasks/find-shareduid-violation.py
+++ /dev/null
@@ -1,99 +0,0 @@
-#!/usr/bin/env python3
-#
-# Copyright (C) 2019 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.
-#
-import argparse
-import json
-import os
-import subprocess
-import sys
-
-from collections import defaultdict
-from glob import glob
-
-def parse_args():
-    """Parse commandline arguments."""
-    parser = argparse.ArgumentParser(description='Find sharedUserId violators')
-    parser.add_argument('--product_out', help='PRODUCT_OUT directory',
-                        default=os.environ.get("PRODUCT_OUT"))
-    parser.add_argument('--aapt', help='Path to aapt or aapt2',
-                        default="aapt2")
-    parser.add_argument('--copy_out_system', help='TARGET_COPY_OUT_SYSTEM',
-                        default="system")
-    parser.add_argument('--copy_out_vendor', help='TARGET_COPY_OUT_VENDOR',
-                        default="vendor")
-    parser.add_argument('--copy_out_product', help='TARGET_COPY_OUT_PRODUCT',
-                        default="product")
-    parser.add_argument('--copy_out_system_ext', help='TARGET_COPY_OUT_SYSTEM_EXT',
-                        default="system_ext")
-    return parser.parse_args()
-
-def execute(cmd):
-    p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
-    out, err = map(lambda b: b.decode('utf-8'), p.communicate())
-    return p.returncode == 0, out, err
-
-def make_aapt_cmds(file):
-    return [aapt + ' dump ' + file + ' --file AndroidManifest.xml',
-            aapt + ' dump xmltree ' + file + ' --file AndroidManifest.xml']
-
-def extract_shared_uid(file):
-    for cmd in make_aapt_cmds(file):
-        success, manifest, error_msg = execute(cmd)
-        if success:
-            break
-    else:
-        print(error_msg, file=sys.stderr)
-        sys.exit()
-
-    for l in manifest.split('\n'):
-        if "sharedUserId" in l:
-            return l.split('"')[-2]
-    return None
-
-
-args = parse_args()
-
-product_out = args.product_out
-aapt = args.aapt
-
-partitions = (
-        ("system", args.copy_out_system),
-        ("vendor", args.copy_out_vendor),
-        ("product", args.copy_out_product),
-        ("system_ext", args.copy_out_system_ext),
-)
-
-shareduid_app_dict = defaultdict(list)
-
-for part, location in partitions:
-    for f in glob(os.path.join(product_out, location, "*", "*", "*.apk")):
-        apk_file = os.path.basename(f)
-        shared_uid = extract_shared_uid(f)
-
-        if shared_uid is None:
-            continue
-        shareduid_app_dict[shared_uid].append((part, apk_file))
-
-
-output = defaultdict(lambda: defaultdict(list))
-
-for uid, app_infos in shareduid_app_dict.items():
-    partitions = {p for p, _ in app_infos}
-    if len(partitions) > 1:
-        for part in partitions:
-            output[uid][part].extend([a for p, a in app_infos if p == part])
-
-print(json.dumps(output, indent=2, sort_keys=True))
diff --git a/core/tasks/host-unit-tests.mk b/core/tasks/host-unit-tests.mk
new file mode 100644
index 0000000..755b589
--- /dev/null
+++ b/core/tasks/host-unit-tests.mk
@@ -0,0 +1,50 @@
+# Copyright (C) 2020 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.
+
+# `host-unit-tests` shall only include hostside unittest that don't require a device to run. Tests
+# included will be run as part of presubmit check.
+# To add tests to the suite, do one of the following:
+# * For test modules configured with Android.bp, set attribute `test_options: { unit_test: true }`
+# * For test modules configured with mk, set `LOCAL_IS_UNIT_TEST := true`
+.PHONY: host-unit-tests
+
+intermediates_dir := $(call intermediates-dir-for,PACKAGING,host-unit-tests)
+host_unit_tests_zip := $(PRODUCT_OUT)/host-unit-tests.zip
+# Get the hostside libraries to be packaged in the test zip. Unlike
+# device-tests.mk or general-tests.mk, the files are not copied to the
+# testcases directory.
+my_host_shared_lib_for_host_unit_tests := $(foreach f,$(COMPATIBILITY.host-unit-tests.HOST_SHARED_LIBRARY.FILES),$(strip \
+    $(eval _cmf_tuple := $(subst :, ,$(f))) \
+    $(eval _cmf_src := $(word 1,$(_cmf_tuple))) \
+    $(_cmf_src)))
+
+$(host_unit_tests_zip) : PRIVATE_HOST_SHARED_LIBS := $(my_host_shared_lib_for_host_unit_tests)
+
+$(host_unit_tests_zip) : $(COMPATIBILITY.host-unit-tests.FILES) $(my_host_shared_lib_for_host_unit_tests) $(SOONG_ZIP)
+	echo $(sort $(COMPATIBILITY.host-unit-tests.FILES)) | tr " " "\n" > $@.list
+	grep $(HOST_OUT_TESTCASES) $@.list > $@-host.list || true
+	echo "" >> $@-host-libs.list
+	$(hide) for shared_lib in $(PRIVATE_HOST_SHARED_LIBS); do \
+	  echo $$shared_lib >> $@-host-libs.list; \
+	done
+	grep $(TARGET_OUT_TESTCASES) $@.list > $@-target.list || true
+	$(hide) $(SOONG_ZIP) -d -o $@ -P host -C $(HOST_OUT) -l $@-host.list \
+	  -P target -C $(PRODUCT_OUT) -l $@-target.list \
+	  -P host/testcases -C $(HOST_OUT) -l $@-host-libs.list
+	rm -f $@.list $@-host.list $@-target.list $@-host-libs.list
+
+host-unit-tests: $(host_unit_tests_zip)
+$(call dist-for-goals, host-unit-tests, $(host_unit_tests_zip))
+
+tests: host-unit-tests
diff --git a/core/version_defaults.mk b/core/version_defaults.mk
index 41696e8..7362275 100644
--- a/core/version_defaults.mk
+++ b/core/version_defaults.mk
@@ -240,7 +240,7 @@
     #  It must be of the form "YYYY-MM-DD" on production devices.
     #  It must match one of the Android Security Patch Level strings of the Public Security Bulletins.
     #  If there is no $PLATFORM_SECURITY_PATCH set, keep it empty.
-      PLATFORM_SECURITY_PATCH := 2020-10-05
+      PLATFORM_SECURITY_PATCH := 2020-11-05
 endif
 .KATI_READONLY := PLATFORM_SECURITY_PATCH
 
diff --git a/envsetup.sh b/envsetup.sh
index e4afdb9..a2d5d1d 100644
--- a/envsetup.sh
+++ b/envsetup.sh
@@ -307,6 +307,9 @@
     unset ANDROID_HOST_OUT
     export ANDROID_HOST_OUT=$(get_abs_build_var HOST_OUT)
 
+    unset ANDROID_SOONG_HOST_OUT
+    export ANDROID_SOONG_HOST_OUT=$(get_abs_build_var SOONG_HOST_OUT)
+
     unset ANDROID_HOST_OUT_TESTCASES
     export ANDROID_HOST_OUT_TESTCASES=$(get_abs_build_var HOST_OUT_TESTCASES)
 
diff --git a/target/board/BoardConfigEmuCommon.mk b/target/board/BoardConfigEmuCommon.mk
index 07b07ce..6b2291f 100644
--- a/target/board/BoardConfigEmuCommon.mk
+++ b/target/board/BoardConfigEmuCommon.mk
@@ -77,6 +77,7 @@
 BOARD_BOOT_HEADER_VERSION := 3
 BOARD_MKBOOTIMG_ARGS += --header_version $(BOARD_BOOT_HEADER_VERSION)
 BOARD_VENDOR_BOOTIMAGE_PARTITION_SIZE := 0x06000000
+BOARD_RAMDISK_USE_LZ4 := true
 
 # Enable chain partition for system.
 BOARD_AVB_SYSTEM_KEY_PATH := external/avb/test/data/testkey_rsa2048.pem
diff --git a/target/board/generic_arm64/BoardConfig.mk b/target/board/generic_arm64/BoardConfig.mk
index 42660e5..3d3eb2e 100644
--- a/target/board/generic_arm64/BoardConfig.mk
+++ b/target/board/generic_arm64/BoardConfig.mk
@@ -54,9 +54,6 @@
 
 include build/make/target/board/BoardConfigGsiCommon.mk
 
-TARGET_NO_KERNEL := false
-BOARD_USES_RECOVERY_AS_BOOT := true
-
 BOARD_KERNEL-4.19-GZ_BOOTIMAGE_PARTITION_SIZE := 47185920
 BOARD_KERNEL-5.4_BOOTIMAGE_PARTITION_SIZE := 67108864
 BOARD_KERNEL-5.4-ALLSYMS_BOOTIMAGE_PARTITION_SIZE := 67108864
@@ -79,8 +76,19 @@
 ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))
 BOARD_KERNEL_BINARIES += kernel-5.4-allsyms kernel-5.4-gz-allsyms kernel-5.4-lz4-allsyms
 endif
+
+# Boot image
+BOARD_USES_RECOVERY_AS_BOOT :=
+TARGET_NO_KERNEL := false
+BOARD_USES_GENERIC_KERNEL_IMAGE := true
 BOARD_KERNEL_MODULE_INTERFACE_VERSIONS := 5.4-android12-0
 
+# No vendor_boot
+BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT :=
+
+# No recovery
+BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE :=
+
 # Some vendors still haven't cleaned up all device specific directories under
 # root!
 
diff --git a/target/board/generic_arm64/device.mk b/target/board/generic_arm64/device.mk
index 866d7c7..7b16aea 100644
--- a/target/board/generic_arm64/device.mk
+++ b/target/board/generic_arm64/device.mk
@@ -30,4 +30,7 @@
     kernel/prebuilts/5.4/arm64/kernel-5.4-lz4:kernel-5.4-lz4-allsyms
 endif
 
+PRODUCT_PACKAGES += e2fsck_ramdisk
+
 PRODUCT_BUILD_VENDOR_BOOT_IMAGE := false
+PRODUCT_BUILD_RECOVERY_IMAGE := false
diff --git a/target/board/generic_arm64_ab/BoardConfig.mk b/target/board/generic_arm64_ab/BoardConfig.mk
deleted file mode 100644
index 7c91607..0000000
--- a/target/board/generic_arm64_ab/BoardConfig.mk
+++ /dev/null
@@ -1,39 +0,0 @@
-#
-# Copyright (C) 2017 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.
-#
-
-include build/make/target/board/BoardConfigGsiCommon.mk
-
-TARGET_ARCH := arm64
-TARGET_ARCH_VARIANT := armv8-a
-TARGET_CPU_ABI := arm64-v8a
-TARGET_CPU_ABI2 :=
-TARGET_CPU_VARIANT := generic
-
-TARGET_2ND_ARCH := arm
-TARGET_2ND_ARCH_VARIANT := armv8-a
-TARGET_2ND_CPU_ABI := armeabi-v7a
-TARGET_2ND_CPU_ABI2 := armeabi
-TARGET_2ND_CPU_VARIANT := generic
-
-# TODO(jiyong) These might be SoC specific.
-BOARD_ROOT_EXTRA_FOLDERS += firmware firmware/radio persist
-BOARD_ROOT_EXTRA_SYMLINKS += /vendor/lib/dsp:/dsp
-BOARD_ROOT_EXTRA_SYMLINKS += /vendor/firmware_mnt/image:/firmware/image
-BOARD_ROOT_EXTRA_SYMLINKS += /vendor/firmware_mnt/verinfo:/firmware/verinfo
-
-# TODO(b/36764215): remove this setting when the generic system image
-# no longer has QCOM-specific directories under /.
-BOARD_SEPOLICY_DIRS += build/make/target/board/generic_arm64/sepolicy
diff --git a/target/board/generic_arm_ab/BoardConfig.mk b/target/board/generic_arm_ab/BoardConfig.mk
deleted file mode 100644
index 21b763c..0000000
--- a/target/board/generic_arm_ab/BoardConfig.mk
+++ /dev/null
@@ -1,36 +0,0 @@
-#
-# Copyright (C) 2017 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.
-#
-
-include build/make/target/board/BoardConfigGsiCommon.mk
-
-TARGET_ARCH := arm
-TARGET_ARCH_VARIANT := armv7-a-neon
-TARGET_CPU_ABI := armeabi-v7a
-TARGET_CPU_ABI2 := armeabi
-TARGET_CPU_VARIANT := generic
-
-# Legacy GSI keeps 32 bits binder for 32 bits CPU Arch
-TARGET_USES_64_BIT_BINDER := false
-
-# TODO(jiyong) These might be SoC specific.
-BOARD_ROOT_EXTRA_FOLDERS += firmware firmware/radio persist
-BOARD_ROOT_EXTRA_SYMLINKS += /vendor/lib/dsp:/dsp
-BOARD_ROOT_EXTRA_SYMLINKS += /vendor/firmware_mnt/image:/firmware/image
-BOARD_ROOT_EXTRA_SYMLINKS += /vendor/firmware_mnt/verinfo:/firmware/verinfo
-
-# TODO(b/36764215): remove this setting when the generic system image
-# no longer has QCOM-specific directories under /.
-BOARD_SEPOLICY_DIRS += build/make/target/board/generic_arm64/sepolicy
diff --git a/target/board/generic_x86_64_ab/BoardConfig.mk b/target/board/generic_x86_64_ab/BoardConfig.mk
deleted file mode 100644
index 1dd5e48..0000000
--- a/target/board/generic_x86_64_ab/BoardConfig.mk
+++ /dev/null
@@ -1,25 +0,0 @@
-#
-# Copyright (C) 2017 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.
-#
-
-include build/make/target/board/BoardConfigGsiCommon.mk
-
-TARGET_CPU_ABI := x86_64
-TARGET_ARCH := x86_64
-TARGET_ARCH_VARIANT := x86_64
-
-TARGET_2ND_CPU_ABI := x86
-TARGET_2ND_ARCH := x86
-TARGET_2ND_ARCH_VARIANT := x86_64
diff --git a/target/board/generic_x86_ab/BoardConfig.mk b/target/board/generic_x86_ab/BoardConfig.mk
deleted file mode 100644
index 53acffd..0000000
--- a/target/board/generic_x86_ab/BoardConfig.mk
+++ /dev/null
@@ -1,24 +0,0 @@
-#
-# Copyright (C) 2017 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.
-#
-
-include build/make/target/board/BoardConfigGsiCommon.mk
-
-TARGET_CPU_ABI := x86
-TARGET_ARCH := x86
-TARGET_ARCH_VARIANT := x86
-
-# Legacy GSI keeps 32 bits binder for 32 bits CPU Arch
-TARGET_USES_64_BIT_BINDER := false
diff --git a/target/product/AndroidProducts.mk b/target/product/AndroidProducts.mk
index 61a7583..5094149 100644
--- a/target/product/AndroidProducts.mk
+++ b/target/product/AndroidProducts.mk
@@ -44,13 +44,9 @@
 else
 PRODUCT_MAKEFILES := \
     $(LOCAL_DIR)/aosp_64bitonly_x86_64.mk \
-    $(LOCAL_DIR)/aosp_arm64_ab.mk \
     $(LOCAL_DIR)/aosp_arm64.mk \
-    $(LOCAL_DIR)/aosp_arm_ab.mk \
     $(LOCAL_DIR)/aosp_arm.mk \
-    $(LOCAL_DIR)/aosp_x86_64_ab.mk \
     $(LOCAL_DIR)/aosp_x86_64.mk \
-    $(LOCAL_DIR)/aosp_x86_ab.mk \
     $(LOCAL_DIR)/aosp_x86_arm.mk \
     $(LOCAL_DIR)/aosp_x86.mk \
     $(LOCAL_DIR)/full.mk \
diff --git a/target/product/aosp_arm64_ab.mk b/target/product/aosp_arm64_ab.mk
deleted file mode 100644
index 5510e1b..0000000
--- a/target/product/aosp_arm64_ab.mk
+++ /dev/null
@@ -1,58 +0,0 @@
-#
-# Copyright (C) 2017 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.
-#
-
-# PRODUCT_VENDOR_PROPERTIES cannot be used here because sysprops will be at
-# /vendor/[build|default].prop when build split is on. In order to have sysprops
-# on the generic system image, place them in build/make/target/board/
-# gsi_system.prop.
-
-# aosp_arm64_ab-userdebug is a Legacy GSI for the devices with:
-# - ARM 64 bits user space
-# - 64 bits binder interface
-# - system-as-root
-
-#
-# All components inherited here go to system image
-# (The system image of Legacy GSI is not CSI)
-#
-$(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit.mk)
-$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system.mk)
-
-# Enable mainline checking for excat this product name
-ifeq (aosp_arm64_ab,$(TARGET_PRODUCT))
-PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := relaxed
-endif
-
-#
-# All components inherited here go to system_ext image
-#
-$(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system_ext.mk)
-$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system_ext.mk)
-
-#
-# All components inherited here go to product image
-#
-$(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_product.mk)
-
-#
-# Special settings for GSI releasing
-#
-$(call inherit-product, $(SRC_TARGET_DIR)/product/legacy_gsi_release.mk)
-
-PRODUCT_NAME := aosp_arm64_ab
-PRODUCT_DEVICE := generic_arm64_ab
-PRODUCT_BRAND := Android
-PRODUCT_MODEL := AOSP on ARM64
diff --git a/target/product/aosp_arm_ab.mk b/target/product/aosp_arm_ab.mk
deleted file mode 100644
index 7e06044..0000000
--- a/target/product/aosp_arm_ab.mk
+++ /dev/null
@@ -1,57 +0,0 @@
-#
-# Copyright (C) 2017 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.
-#
-
-# PRODUCT_VENDOR_PROPERTIES cannot be used here because sysprops will be at
-# /vendor/[build|default].prop when build split is on. In order to have sysprops
-# on the generic system image, place them in build/make/target/board/
-# gsi_system.prop.
-
-# aosp_arm_ab-userdebug is a Legacy GSI for the devices with:
-# - ARM 32 bits user space
-# - 32 bits binder interface
-# - system-as-root
-
-#
-# All components inherited here go to system image
-# (The system image of Legacy GSI is not CSI)
-#
-$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system.mk)
-
-# Enable mainline checking for excat this product name
-ifeq (aosp_arm_ab,$(TARGET_PRODUCT))
-PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := relaxed
-endif
-
-#
-# All components inherited here go to system_ext image
-#
-$(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system_ext.mk)
-$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system_ext.mk)
-
-#
-# All components inherited here go to product image
-#
-$(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_product.mk)
-
-#
-# Special settings for GSI releasing
-#
-$(call inherit-product, $(SRC_TARGET_DIR)/product/legacy_gsi_release.mk)
-
-PRODUCT_NAME := aosp_arm_ab
-PRODUCT_DEVICE := generic_arm_ab
-PRODUCT_BRAND := Android
-PRODUCT_MODEL := AOSP on ARM32
diff --git a/target/product/aosp_x86_64_ab.mk b/target/product/aosp_x86_64_ab.mk
deleted file mode 100644
index c31545d..0000000
--- a/target/product/aosp_x86_64_ab.mk
+++ /dev/null
@@ -1,58 +0,0 @@
-#
-# Copyright (C) 2017 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.
-#
-
-# PRODUCT_VENDOR_PROPERTIES cannot be used here because sysprops will be at
-# /vendor/[build|default].prop when build split is on. In order to have sysprops
-# on the generic system image, place them in build/make/target/board/
-# gsi_system.prop.
-
-# aosp_x86_64_ab-userdebug is a Legacy GSI for the devices with:
-# - x86 64 bits user space
-# - 64 bits binder interface
-# - system-as-root
-
-#
-# All components inherited here go to system image
-# (The system image of Legacy GSI is not CSI)
-#
-$(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit.mk)
-$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system.mk)
-
-# Enable mainline checking for excat this product name
-ifeq (aosp_x86_64_ab,$(TARGET_PRODUCT))
-PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := relaxed
-endif
-
-#
-# All components inherited here go to system_ext image
-#
-$(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system_ext.mk)
-$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system_ext.mk)
-
-#
-# All components inherited here go to product image
-#
-$(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_product.mk)
-
-#
-# Special settings for GSI releasing
-#
-$(call inherit-product, $(SRC_TARGET_DIR)/product/legacy_gsi_release.mk)
-
-PRODUCT_NAME := aosp_x86_64_ab
-PRODUCT_DEVICE := generic_x86_64_ab
-PRODUCT_BRAND := Android
-PRODUCT_MODEL := AOSP on x86_64
diff --git a/target/product/aosp_x86_ab.mk b/target/product/aosp_x86_ab.mk
deleted file mode 100644
index 2f02dd1..0000000
--- a/target/product/aosp_x86_ab.mk
+++ /dev/null
@@ -1,58 +0,0 @@
-#
-# Copyright (C) 2017 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.
-#
-
-# PRODUCT_VENDOR_PROPERTIES cannot be used here because sysprops will be at
-# /vendor/[build|default].prop when build split is on. In order to have sysprops
-# on the generic system image, place them in build/make/target/board/
-# gsi_system.prop.
-
-# aosp_x86_ab-userdebug is a Legacy GSI for the devices with:
-# - x86 32 bits user space
-# - 32 bits binder interface
-# - system-as-root
-
-#
-# All components inherited here go to system image
-# (The system image of Legacy GSI is not CSI)
-#
-$(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit.mk)
-$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_system.mk)
-
-# Enable mainline checking for excat this product name
-ifeq (aosp_x86_ab,$(TARGET_PRODUCT))
-PRODUCT_ENFORCE_ARTIFACT_PATH_REQUIREMENTS := relaxed
-endif
-
-#
-# All components inherited here go to system_ext image
-#
-$(call inherit-product, $(SRC_TARGET_DIR)/product/handheld_system_ext.mk)
-$(call inherit-product, $(SRC_TARGET_DIR)/product/telephony_system_ext.mk)
-
-#
-# All components inherited here go to product image
-#
-$(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_product.mk)
-
-#
-# Special settings for GSI releasing
-#
-$(call inherit-product, $(SRC_TARGET_DIR)/product/legacy_gsi_release.mk)
-
-PRODUCT_NAME := aosp_x86_ab
-PRODUCT_DEVICE := generic_x86_ab
-PRODUCT_BRAND := Android
-PRODUCT_MODEL := AOSP on x86
diff --git a/target/product/base_system.mk b/target/product/base_system.mk
index 1a28cf0..4607edb 100644
--- a/target/product/base_system.mk
+++ b/target/product/base_system.mk
@@ -117,6 +117,7 @@
     ip-up-vpn \
     javax.obex \
     keystore \
+    keystore2 \
     credstore \
     ld.mc \
     libaaudio \
@@ -248,6 +249,7 @@
     shell_and_utilities_system \
     sm \
     snapshotctl \
+    snapuserd \
     SoundPicker \
     storaged \
     surfaceflinger \
@@ -381,6 +383,8 @@
     logpersist.start \
     logtagd.rc \
     procrank \
+    profcollectd \
+    profcollectctl \
     remount \
     showmap \
     sqlite3 \
diff --git a/target/product/legacy_gsi_release.mk b/target/product/legacy_gsi_release.mk
deleted file mode 100644
index 09b96fb..0000000
--- a/target/product/legacy_gsi_release.mk
+++ /dev/null
@@ -1,23 +0,0 @@
-#
-# Copyright (C) 2020 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.
-#
-
-include $(SRC_TARGET_DIR)/product/gsi_release.mk
-
-# Legacy GSI support additional O-MR1 interface
-PRODUCT_EXTRA_VNDK_VERSIONS += 27
-
-# Legacy GSI relax the compatible property checking
-PRODUCT_COMPATIBLE_PROPERTY_OVERRIDE := false
diff --git a/target/product/virtual_ab_ota_compression.mk b/target/product/virtual_ab_ota_compression.mk
index c4849be..eef3c33 100644
--- a/target/product/virtual_ab_ota_compression.mk
+++ b/target/product/virtual_ab_ota_compression.mk
@@ -18,4 +18,4 @@
 
 PRODUCT_VENDOR_PROPERTIES += ro.virtual_ab.compression.enabled=true
 
-PRODUCT_PACKAGES += snapuserd_ramdisk
+PRODUCT_PACKAGES += snapuserd_ramdisk snapuserd
diff --git a/tools/releasetools/Android.bp b/tools/releasetools/Android.bp
index e1543e7..11fb584 100644
--- a/tools/releasetools/Android.bp
+++ b/tools/releasetools/Android.bp
@@ -49,6 +49,7 @@
     required: [
         "blk_alloc_to_base_fs",
         "e2fsck",
+        "mkerofsimage.sh",
         "mkuserimg_mke2fs",
         "simg2img",
         "tune2fs",
@@ -368,6 +369,32 @@
     ],
 }
 
+python_defaults {
+    name: "releasetools_find_shareduid_violation_defaults",
+    srcs: [
+        "find_shareduid_violation.py",
+    ],
+    libs: [
+        "releasetools_common",
+    ],
+}
+
+python_binary_host {
+    name: "find_shareduid_violation",
+    defaults: [
+        "releasetools_binary_defaults",
+        "releasetools_find_shareduid_violation_defaults",
+    ],
+}
+
+python_library_host {
+    name: "releasetools_find_shareduid_violation",
+    defaults: [
+        "releasetools_find_shareduid_violation_defaults",
+        "releasetools_library_defaults",
+    ],
+}
+
 python_binary_host {
     name: "make_recovery_patch",
     defaults: ["releasetools_binary_defaults"],
@@ -402,6 +429,7 @@
         "releasetools_build_super_image",
         "releasetools_check_target_files_vintf",
         "releasetools_common",
+        "releasetools_find_shareduid_violation",
         "releasetools_img_from_target_files",
         "releasetools_ota_from_target_files",
     ],
@@ -504,6 +532,7 @@
         "releasetools_build_super_image",
         "releasetools_check_target_files_vintf",
         "releasetools_common",
+        "releasetools_find_shareduid_violation",
         "releasetools_img_from_target_files",
         "releasetools_ota_from_target_files",
         "releasetools_verity_utils",
diff --git a/tools/releasetools/build_image.py b/tools/releasetools/build_image.py
index 169a112..6487b9b 100755
--- a/tools/releasetools/build_image.py
+++ b/tools/releasetools/build_image.py
@@ -296,6 +296,18 @@
       build_command.extend(["--inode_size", "256"])
     if "selinux_fc" in prop_dict:
       build_command.append(prop_dict["selinux_fc"])
+  elif fs_type.startswith("erofs"):
+    build_command = ["mkerofsimage.sh"]
+    build_command.extend([in_dir, out_file])
+    if "erofs_sparse_flag" in prop_dict:
+      build_command.extend([prop_dict["erofs_sparse_flag"]])
+    build_command.extend(["-m", prop_dict["mount_point"]])
+    if target_out:
+      build_command.extend(["-d", target_out])
+    if fs_config:
+      build_command.extend(["-C", fs_config])
+    if "selinux_fc" in prop_dict:
+      build_command.extend(["-c", prop_dict["selinux_fc"]])
   elif fs_type.startswith("squash"):
     build_command = ["mksquashfsimage.sh"]
     build_command.extend([in_dir, out_file])
@@ -532,6 +544,7 @@
 
   common_props = (
       "extfs_sparse_flag",
+      "erofs_sparse_flag",
       "squashfs_sparse_flag",
       "f2fs_sparse_flag",
       "skip_fsck",
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index 5e70af1..0683678 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -1085,6 +1085,38 @@
   return merged_dict
 
 
+def SharedUidPartitionViolations(uid_dict, partition_groups):
+  """Checks for APK sharedUserIds that cross partition group boundaries.
+
+  This uses a single or merged build's shareduid_violation_modules.json
+  output file, as generated by find_shareduid_violation.py or
+  core/tasks/find-shareduid-violation.mk.
+
+  An error is defined as a sharedUserId that is found in a set of partitions
+  that span more than one partition group.
+
+  Args:
+    uid_dict: A dictionary created by using the standard json module to read a
+      complete shareduid_violation_modules.json file.
+    partition_groups: A list of groups, where each group is a list of
+      partitions.
+
+  Returns:
+    A list of error messages.
+  """
+  errors = []
+  for uid, partitions in uid_dict.items():
+    found_in_groups = [
+        group for group in partition_groups
+        if set(partitions.keys()) & set(group)
+    ]
+    if len(found_in_groups) > 1:
+      errors.append(
+          "APK sharedUserId \"%s\" found across partition groups in partitions \"%s\""
+          % (uid, ",".join(sorted(partitions.keys()))))
+  return errors
+
+
 def AppendAVBSigningArgs(cmd, partition):
   """Append signing arguments for avbtool."""
   # e.g., "--key path/to/signing_key --algorithm SHA256_RSA4096"
diff --git a/tools/releasetools/find_shareduid_violation.py b/tools/releasetools/find_shareduid_violation.py
new file mode 100755
index 0000000..35acde3
--- /dev/null
+++ b/tools/releasetools/find_shareduid_violation.py
@@ -0,0 +1,175 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2019 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.
+#
+"""Find APK sharedUserId violators.
+
+Usage: find_shareduid_violation [args]
+
+  --product_out
+    PRODUCT_OUT directory
+
+  --aapt
+    Path to aapt or aapt2
+
+  --copy_out_system
+    TARGET_COPY_OUT_SYSTEM
+
+  --copy_out_vendor_
+    TARGET_COPY_OUT_VENDOR
+
+  --copy_out_product
+    TARGET_COPY_OUT_PRODUCT
+
+  --copy_out_system_ext
+    TARGET_COPY_OUT_SYSTEM_EXT
+"""
+
+import json
+import logging
+import os
+import re
+import subprocess
+import sys
+
+from collections import defaultdict
+from glob import glob
+
+import common
+
+logger = logging.getLogger(__name__)
+
+OPTIONS = common.OPTIONS
+OPTIONS.product_out = os.environ.get("PRODUCT_OUT")
+OPTIONS.aapt = "aapt2"
+OPTIONS.copy_out_system = "system"
+OPTIONS.copy_out_vendor = "vendor"
+OPTIONS.copy_out_product = "product"
+OPTIONS.copy_out_system_ext = "system_ext"
+
+
+def execute(cmd):
+  p = subprocess.Popen(
+      cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+  out, err = map(lambda b: b.decode("utf-8"), p.communicate())
+  return p.returncode == 0, out, err
+
+
+def make_aapt_cmds(aapt, apk):
+  return [
+      aapt + " dump " + apk + " --file AndroidManifest.xml",
+      aapt + " dump xmltree " + apk + " --file AndroidManifest.xml"
+  ]
+
+
+def extract_shared_uid(aapt, apk):
+  for cmd in make_aapt_cmds(aapt, apk):
+    success, manifest, error_msg = execute(cmd)
+    if success:
+      break
+  else:
+    logger.error(error_msg)
+    sys.exit()
+
+  pattern = re.compile(r"sharedUserId.*=\"([^\"]*)")
+
+  for line in manifest.split("\n"):
+    match = pattern.search(line)
+    if match:
+      return match.group(1)
+  return None
+
+
+def FindShareduidViolation(product_out, partition_map, aapt="aapt2"):
+  """Find sharedUserId violators in the given partitions.
+
+  Args:
+    product_out: The base directory containing the partition directories.
+    partition_map: A map of partition name -> directory name.
+    aapt: The name of the aapt binary. Defaults to aapt2.
+
+  Returns:
+    A string containing a JSON object describing the shared UIDs.
+  """
+  shareduid_app_dict = defaultdict(lambda: defaultdict(list))
+
+  for part, location in partition_map.items():
+    for f in glob(os.path.join(product_out, location, "*", "*", "*.apk")):
+      apk_file = os.path.basename(f)
+      shared_uid = extract_shared_uid(aapt, f)
+
+      if shared_uid is None:
+        continue
+      shareduid_app_dict[shared_uid][part].append(apk_file)
+
+  # Only output sharedUserId values that appear in >1 partition.
+  output = {}
+  for uid, partitions in shareduid_app_dict.items():
+    if len(partitions) > 1:
+      output[uid] = shareduid_app_dict[uid]
+
+  return json.dumps(output, indent=2, sort_keys=True)
+
+
+def main():
+  common.InitLogging()
+
+  def option_handler(o, a):
+    if o == "--product_out":
+      OPTIONS.product_out = a
+    elif o == "--aapt":
+      OPTIONS.aapt = a
+    elif o == "--copy_out_system":
+      OPTIONS.copy_out_system = a
+    elif o == "--copy_out_vendor":
+      OPTIONS.copy_out_vendor = a
+    elif o == "--copy_out_product":
+      OPTIONS.copy_out_product = a
+    elif o == "--copy_out_system_ext":
+      OPTIONS.copy_out_system_ext = a
+    else:
+      return False
+    return True
+
+  args = common.ParseOptions(
+      sys.argv[1:],
+      __doc__,
+      extra_long_opts=[
+          "product_out=",
+          "aapt=",
+          "copy_out_system=",
+          "copy_out_vendor=",
+          "copy_out_product=",
+          "copy_out_system_ext=",
+      ],
+      extra_option_handler=option_handler)
+
+  if args:
+    common.Usage(__doc__)
+    sys.exit(1)
+
+  partition_map = {
+      "system": OPTIONS.copy_out_system,
+      "vendor": OPTIONS.copy_out_vendor,
+      "product": OPTIONS.copy_out_product,
+      "system_ext": OPTIONS.copy_out_system_ext,
+  }
+
+  print(
+      FindShareduidViolation(OPTIONS.product_out, partition_map, OPTIONS.aapt))
+
+
+if __name__ == "__main__":
+  main()
diff --git a/tools/releasetools/merge_target_files.py b/tools/releasetools/merge_target_files.py
index 2da5cc0..ba9e740 100755
--- a/tools/releasetools/merge_target_files.py
+++ b/tools/releasetools/merge_target_files.py
@@ -85,6 +85,7 @@
 from __future__ import print_function
 
 import fnmatch
+import json
 import logging
 import os
 import re
@@ -98,6 +99,7 @@
 import check_target_files_vintf
 import common
 import img_from_target_files
+import find_shareduid_violation
 import ota_from_target_files
 
 logger = logging.getLogger(__name__)
@@ -943,6 +945,33 @@
   if not check_target_files_vintf.CheckVintf(output_target_files_temp_dir):
     raise RuntimeError('Incompatible VINTF metadata')
 
+  # Generate and check for cross-partition violations of sharedUserId
+  # values in APKs. This requires the input target-files packages to contain
+  # *.apk files.
+  shareduid_violation_modules = os.path.join(
+      output_target_files_temp_dir, 'META', 'shareduid_violation_modules.json')
+  with open(shareduid_violation_modules, 'w') as f:
+    framework_partitions = item_list_to_partition_set(framework_item_list)
+    vendor_partitions = item_list_to_partition_set(vendor_item_list)
+
+    partition_map = {}
+    for partition in (framework_partitions.union(vendor_partitions)):
+      partition_map[partition.lower()] = partition.upper()
+    violation = find_shareduid_violation.FindShareduidViolation(
+        output_target_files_temp_dir, partition_map)
+
+    # Write the output to a file to enable debugging.
+    f.write(violation)
+
+    # Check for violations across the input builds' partition groups.
+    shareduid_errors = common.SharedUidPartitionViolations(
+        json.loads(violation), [framework_partitions, vendor_partitions])
+    if shareduid_errors:
+      for error in shareduid_errors:
+        logger.error(error)
+      raise ValueError('sharedUserId APK error. See %s' %
+                       shareduid_violation_modules)
+
   generate_images(output_target_files_temp_dir, rebuild_recovery)
 
   generate_super_empty_image(output_target_files_temp_dir, output_super_empty)
diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py
index fba43e9..9a57c8a 100755
--- a/tools/releasetools/ota_from_target_files.py
+++ b/tools/releasetools/ota_from_target_files.py
@@ -751,6 +751,9 @@
   common.ZipDelete(target_file, POSTINSTALL_CONFIG)
   return target_file
 
+def ParseInfoDict(target_file_path):
+  with zipfile.ZipFile(target_file_path, 'r', allowZip64=True) as zfp:
+    return common.LoadInfoDict(zfp)
 
 def GetTargetFilesZipForPartialUpdates(input_file, ab_partitions):
   """Returns a target-files.zip for partial ota update package generation.
@@ -781,7 +784,8 @@
     raise ValueError("Cannot find {} in input zipfile".format(partition_name))
 
   with zipfile.ZipFile(input_file, allowZip64=True) as input_zip:
-    original_ab_partitions = input_zip.read(AB_PARTITIONS).decode().splitlines()
+    original_ab_partitions = input_zip.read(
+        AB_PARTITIONS).decode().splitlines()
     namelist = input_zip.namelist()
 
   unrecognized_partitions = [partition for partition in ab_partitions if
@@ -871,7 +875,7 @@
   with open(new_ab_partitions, 'w') as f:
     for partition in ab_partitions:
       if (partition in dynamic_partition_list and
-              partition not in super_block_devices):
+          partition not in super_block_devices):
         logger.info("Dropping %s from ab_partitions.txt", partition)
         continue
       f.write(partition + "\n")
@@ -906,6 +910,7 @@
 
   return target_file
 
+
 def GetTargetFilesZipForCustomImagesUpdates(input_file, custom_images):
   """Returns a target-files.zip for custom partitions update.
 
@@ -944,6 +949,12 @@
 
   return target_file
 
+def GeneratePartitionTimestampFlags(partition_state):
+  partition_timestamps = [
+      part.partition_name + ":" + part.version
+      for part in partition_state]
+  return ["--partition_timestamps", ",".join(partition_timestamps)]
+
 def GenerateAbOtaPackage(target_file, output_file, source_file=None):
   """Generates an Android OTA package that has A/B update payload."""
   # Stage the output zip package for package signing.
@@ -961,6 +972,12 @@
         "META/ab_partitions.txt is required for ab_update."
     target_info = common.BuildInfo(OPTIONS.target_info_dict, OPTIONS.oem_dicts)
     source_info = common.BuildInfo(OPTIONS.source_info_dict, OPTIONS.oem_dicts)
+    vendor_prop = source_info.info_dict.get("vendor.build.prop")
+    if vendor_prop and \
+        vendor_prop.GetProp("ro.virtual_ab.compression.enabled") == "true":
+      # TODO(zhangkelvin) Remove this once FEC on VABC is supported
+      logger.info("Virtual AB Compression enabled, disabling FEC")
+      OPTIONS.disable_fec_computation = True
   else:
     assert "ab_partitions" in OPTIONS.info_dict, \
         "META/ab_partitions.txt is required for ab_update."
@@ -987,30 +1004,29 @@
   # Target_file may have been modified, reparse ab_partitions
   with zipfile.ZipFile(target_file, allowZip64=True) as zfp:
     target_info.info_dict['ab_partitions'] = zfp.read(
-        AB_PARTITIONS).strip().split("\n")
+        AB_PARTITIONS).decode().strip().split("\n")
 
   # Metadata to comply with Android OTA package format.
   metadata = GetPackageMetadata(target_info, source_info)
   # Generate payload.
   payload = Payload()
 
-  partition_timestamps = []
+  partition_timestamps_flags = []
   # Enforce a max timestamp this payload can be applied on top of.
   if OPTIONS.downgrade:
     max_timestamp = source_info.GetBuildProp("ro.build.date.utc")
   else:
     max_timestamp = str(metadata.postcondition.timestamp)
-    partition_timestamps = [
-        part.partition_name + ":" + part.version
-        for part in metadata.postcondition.partition_state]
-  additional_args += ["--max_timestamp", max_timestamp]
-  if partition_timestamps:
-    additional_args.extend(
-        ["--partition_timestamps", ",".join(
-            partition_timestamps)]
-    )
+    partition_timestamps_flags = GeneratePartitionTimestampFlags(
+        metadata.postcondition.partition_state)
 
-  payload.Generate(target_file, source_file, additional_args)
+  additional_args += ["--max_timestamp", max_timestamp]
+
+  payload.Generate(
+      target_file,
+      source_file,
+      additional_args + partition_timestamps_flags
+   )
 
   # Sign the payload.
   payload_signer = PayloadSigner()
@@ -1027,8 +1043,15 @@
     secondary_target_file = GetTargetFilesZipForSecondaryImages(
         target_file, OPTIONS.skip_postinstall)
     secondary_payload = Payload(secondary=True)
+    assert not OPTIONS.downgrade
+    partition_timestamps_flags = GeneratePartitionTimestampFlags(
+      [part
+       for part in metadata.postcondition.partition_state
+       if part.partition_name not in SECONDARY_PAYLOAD_SKIPPED_IMAGES]
+    )
     secondary_payload.Generate(secondary_target_file,
-                               additional_args=additional_args)
+                               additional_args=["--max_timestamp",
+                               max_timestamp]+partition_timestamps_flags)
     secondary_payload.Sign(payload_signer)
     secondary_payload.WriteToZip(output_zip)
 
@@ -1222,8 +1245,7 @@
   if OPTIONS.extracted_input is not None:
     OPTIONS.info_dict = common.LoadInfoDict(OPTIONS.extracted_input)
   else:
-    with zipfile.ZipFile(args[0], 'r', allowZip64=True) as input_zip:
-      OPTIONS.info_dict = common.LoadInfoDict(input_zip)
+    OPTIONS.info_dict = ParseInfoDict(args[0])
 
   # TODO(xunchang) for retrofit and partial updates, maybe we should rebuild the
   # target-file and reload the info_dict. So the info will be consistent with
@@ -1235,8 +1257,7 @@
   # Load the source build dict if applicable.
   if OPTIONS.incremental_source is not None:
     OPTIONS.target_info_dict = OPTIONS.info_dict
-    with zipfile.ZipFile(OPTIONS.incremental_source, 'r', allowZip64=True) as source_zip:
-      OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
+    OPTIONS.source_info_dict = ParseInfoDict(OPTIONS.incremental_source)
 
     logger.info("--- source info ---")
     common.DumpInfoDict(OPTIONS.source_info_dict)
diff --git a/tools/releasetools/test_common.py b/tools/releasetools/test_common.py
index ee28571..0b368f9 100644
--- a/tools/releasetools/test_common.py
+++ b/tools/releasetools/test_common.py
@@ -15,6 +15,7 @@
 #
 
 import copy
+import json
 import os
 import subprocess
 import tempfile
@@ -995,6 +996,34 @@
         },
         sparse_image.file_map)
 
+  def test_SharedUidPartitionViolations(self):
+    uid_dict = {
+        'android.uid.phone': {
+            'system': ['system_phone.apk'],
+            'system_ext': ['system_ext_phone.apk'],
+        },
+        'android.uid.wifi': {
+            'vendor': ['vendor_wifi.apk'],
+            'odm': ['odm_wifi.apk'],
+        },
+    }
+    errors = common.SharedUidPartitionViolations(
+        uid_dict, [('system', 'system_ext'), ('vendor', 'odm')])
+    self.assertEqual(errors, [])
+
+  def test_SharedUidPartitionViolations_Violation(self):
+    uid_dict = {
+        'android.uid.phone': {
+            'system': ['system_phone.apk'],
+            'vendor': ['vendor_phone.apk'],
+        },
+    }
+    errors = common.SharedUidPartitionViolations(
+        uid_dict, [('system', 'system_ext'), ('vendor', 'odm')])
+    self.assertIn(
+        ('APK sharedUserId "android.uid.phone" found across partition groups '
+         'in partitions "system,vendor"'), errors)
+
   def test_GetSparseImage_missingImageFile(self):
     self.assertRaises(
         AssertionError, common.GetSparseImage, 'system2', self.testdata_dir,
diff --git a/tools/zipalign/Android.bp b/tools/zipalign/Android.bp
index 8e6196d..d88ee74 100644
--- a/tools/zipalign/Android.bp
+++ b/tools/zipalign/Android.bp
@@ -4,20 +4,31 @@
 // Zip alignment tool
 //
 
-cc_binary_host {
-    name: "zipalign",
+cc_defaults {
+    name: "zipalign_defaults",
+    target: {
+        windows: {
+            host_ldlibs: ["-lpthread"],
+            enabled: true,
+        },
+    },
+}
 
+cc_library_host_static {
+    name: "libzipalign",
     srcs: [
         "ZipAlign.cpp",
         "ZipEntry.cpp",
         "ZipFile.cpp",
     ],
-
+    export_include_dirs: [
+        "include",
+    ],
     cflags: ["-Wall", "-Werror"],
 
     // NOTE: Do not add any shared_libs dependencies because they will break the
     // static_sdk_tools target.
-    static_libs: [
+    whole_static_libs: [
         "libutils",
         "libcutils",
         "liblog",
@@ -26,11 +37,34 @@
         "libbase",
         "libzopfli",
     ],
+    defaults: ["zipalign_defaults"],
+}
 
-    target: {
-        windows: {
-            host_ldlibs: ["-lpthread"],
-            enabled: true,
-        },
-    },
+cc_binary_host {
+    name: "zipalign",
+    srcs: [
+        "ZipAlignMain.cpp",
+    ],
+    cflags: ["-Wall", "-Werror"],
+    static_libs: [
+        "libzipalign",
+    ],
+    defaults: ["zipalign_defaults"],
+}
+
+cc_test_host {
+    name: "zipalign_tests",
+    srcs: [
+        "tests/src/*_test.cpp",
+    ],
+    static_libs: [
+        "libbase",
+        "libzipalign",
+        "libgmock",
+    ],
+    data: [
+         "tests/data/unaligned.zip",
+    ],
+    defaults: ["zipalign_defaults"],
+    test_suites: ["general-tests"],
 }
diff --git a/tools/zipalign/OWNERS b/tools/zipalign/OWNERS
new file mode 100644
index 0000000..d701e4a
--- /dev/null
+++ b/tools/zipalign/OWNERS
@@ -0,0 +1,2 @@
+include platform/system/core:/janitors/OWNERS
+sanglardf@google.com
diff --git a/tools/zipalign/TEST_MAPPING b/tools/zipalign/TEST_MAPPING
new file mode 100644
index 0000000..98420f3
--- /dev/null
+++ b/tools/zipalign/TEST_MAPPING
@@ -0,0 +1,8 @@
+{
+  "presubmit": [
+    {
+      "name": "zipalign_tests",
+      "host": true
+    }
+  ]
+}
diff --git a/tools/zipalign/ZipAlign.cpp b/tools/zipalign/ZipAlign.cpp
index eea1749..1851ac5 100644
--- a/tools/zipalign/ZipAlign.cpp
+++ b/tools/zipalign/ZipAlign.cpp
@@ -14,35 +14,13 @@
  * limitations under the License.
  */
 
-/*
- * Zip alignment tool
- */
 #include "ZipFile.h"
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 
-using namespace android;
-
-/*
- * Show program usage.
- */
-void usage(void)
-{
-    fprintf(stderr, "Zip alignment utility\n");
-    fprintf(stderr, "Copyright (C) 2009 The Android Open Source Project\n\n");
-    fprintf(stderr,
-        "Usage: zipalign [-f] [-p] [-v] [-z] <align> infile.zip outfile.zip\n"
-        "       zipalign -c [-p] [-v] <align> infile.zip\n\n" );
-    fprintf(stderr,
-        "  <align>: alignment in bytes, e.g. '4' provides 32-bit alignment\n");
-    fprintf(stderr, "  -c: check alignment only (does not modify file)\n");
-    fprintf(stderr, "  -f: overwrite existing outfile.zip\n");
-    fprintf(stderr, "  -p: memory page alignment for stored shared object files\n");
-    fprintf(stderr, "  -v: verbose output\n");
-    fprintf(stderr, "  -z: recompress using Zopfli\n");
-}
+namespace android {
 
 static int getAlignment(bool pageAlignSharedLibs, int defaultAlignment,
     ZipEntry* pEntry) {
@@ -126,7 +104,7 @@
  * Process a file.  We open the input and output files, failing if the
  * output file exists and "force" wasn't specified.
  */
-static int process(const char* inFileName, const char* outFileName,
+int process(const char* inFileName, const char* outFileName,
     int alignment, bool force, bool zopfli, bool pageAlignSharedLibs)
 {
     ZipFile zin, zout;
@@ -169,7 +147,7 @@
 /*
  * Verify the alignment of a zip archive.
  */
-static int verify(const char* fileName, int alignment, bool verbose,
+int verify(const char* fileName, int alignment, bool verbose,
     bool pageAlignSharedLibs)
 {
     ZipFile zipFile;
@@ -218,92 +196,4 @@
     return foundBad ? 1 : 0;
 }
 
-/*
- * Parse args.
- */
-int main(int argc, char* const argv[])
-{
-    bool wantUsage = false;
-    bool check = false;
-    bool force = false;
-    bool verbose = false;
-    bool zopfli = false;
-    bool pageAlignSharedLibs = false;
-    int result = 1;
-    int alignment;
-    char* endp;
-
-    if (argc < 4) {
-        wantUsage = true;
-        goto bail;
-    }
-
-    argc--;
-    argv++;
-
-    while (argc && argv[0][0] == '-') {
-        const char* cp = argv[0] +1;
-
-        while (*cp != '\0') {
-            switch (*cp) {
-            case 'c':
-                check = true;
-                break;
-            case 'f':
-                force = true;
-                break;
-            case 'v':
-                verbose = true;
-                break;
-            case 'z':
-                zopfli = true;
-                break;
-            case 'p':
-                pageAlignSharedLibs = true;
-                break;
-            default:
-                fprintf(stderr, "ERROR: unknown flag -%c\n", *cp);
-                wantUsage = true;
-                goto bail;
-            }
-
-            cp++;
-        }
-
-        argc--;
-        argv++;
-    }
-
-    if (!((check && argc == 2) || (!check && argc == 3))) {
-        wantUsage = true;
-        goto bail;
-    }
-
-    alignment = strtol(argv[0], &endp, 10);
-    if (*endp != '\0' || alignment <= 0) {
-        fprintf(stderr, "Invalid value for alignment: %s\n", argv[0]);
-        wantUsage = true;
-        goto bail;
-    }
-
-    if (check) {
-        /* check existing archive for correct alignment */
-        result = verify(argv[1], alignment, verbose, pageAlignSharedLibs);
-    } else {
-        /* create the new archive */
-        result = process(argv[1], argv[2], alignment, force, zopfli, pageAlignSharedLibs);
-
-        /* trust, but verify */
-        if (result == 0) {
-            result = verify(argv[2], alignment, verbose, pageAlignSharedLibs);
-        }
-    }
-
-bail:
-    if (wantUsage) {
-        usage();
-        result = 2;
-    }
-
-    return result;
-}
+} // namespace android
diff --git a/tools/zipalign/ZipAlignMain.cpp b/tools/zipalign/ZipAlignMain.cpp
new file mode 100644
index 0000000..49be916
--- /dev/null
+++ b/tools/zipalign/ZipAlignMain.cpp
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+/*
+ * Zip alignment tool
+ */
+
+#include "ZipAlign.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+using namespace android;
+
+/*
+ * Show program usage.
+ */
+void usage(void)
+{
+    fprintf(stderr, "Zip alignment utility\n");
+    fprintf(stderr, "Copyright (C) 2009 The Android Open Source Project\n\n");
+    fprintf(stderr,
+        "Usage: zipalign [-f] [-p] [-v] [-z] <align> infile.zip outfile.zip\n"
+        "       zipalign -c [-p] [-v] <align> infile.zip\n\n" );
+    fprintf(stderr,
+        "  <align>: alignment in bytes, e.g. '4' provides 32-bit alignment\n");
+    fprintf(stderr, "  -c: check alignment only (does not modify file)\n");
+    fprintf(stderr, "  -f: overwrite existing outfile.zip\n");
+    fprintf(stderr, "  -p: memory page alignment for stored shared object files\n");
+    fprintf(stderr, "  -v: verbose output\n");
+    fprintf(stderr, "  -z: recompress using Zopfli\n");
+}
+
+
+/*
+ * Parse args.
+ */
+int main(int argc, char* const argv[])
+{
+    bool wantUsage = false;
+    bool check = false;
+    bool force = false;
+    bool verbose = false;
+    bool zopfli = false;
+    bool pageAlignSharedLibs = false;
+    int result = 1;
+    int alignment;
+    char* endp;
+
+    if (argc < 4) {
+        wantUsage = true;
+        goto bail;
+    }
+
+    argc--;
+    argv++;
+
+    while (argc && argv[0][0] == '-') {
+        const char* cp = argv[0] +1;
+
+        while (*cp != '\0') {
+            switch (*cp) {
+            case 'c':
+                check = true;
+                break;
+            case 'f':
+                force = true;
+                break;
+            case 'v':
+                verbose = true;
+                break;
+            case 'z':
+                zopfli = true;
+                break;
+            case 'p':
+                pageAlignSharedLibs = true;
+                break;
+            default:
+                fprintf(stderr, "ERROR: unknown flag -%c\n", *cp);
+                wantUsage = true;
+                goto bail;
+            }
+
+            cp++;
+        }
+
+        argc--;
+        argv++;
+    }
+
+    if (!((check && argc == 2) || (!check && argc == 3))) {
+        wantUsage = true;
+        goto bail;
+    }
+
+    alignment = strtol(argv[0], &endp, 10);
+    if (*endp != '\0' || alignment <= 0) {
+        fprintf(stderr, "Invalid value for alignment: %s\n", argv[0]);
+        wantUsage = true;
+        goto bail;
+    }
+
+    if (check) {
+        /* check existing archive for correct alignment */
+        result = verify(argv[1], alignment, verbose, pageAlignSharedLibs);
+    } else {
+        /* create the new archive */
+        result = process(argv[1], argv[2], alignment, force, zopfli, pageAlignSharedLibs);
+
+        /* trust, but verify */
+        if (result == 0) {
+            result = verify(argv[2], alignment, verbose, pageAlignSharedLibs);
+        }
+    }
+
+bail:
+    if (wantUsage) {
+        usage();
+        result = 2;
+    }
+
+    return result;
+}
diff --git a/tools/zipalign/ZipEntry.cpp b/tools/zipalign/ZipEntry.cpp
index 810d74a..5233f0a 100644
--- a/tools/zipalign/ZipEntry.cpp
+++ b/tools/zipalign/ZipEntry.cpp
@@ -29,7 +29,7 @@
 #include <string.h>
 #include <time.h>
 
-using namespace android;
+namespace android {
 
 /*
  * Initialize a new ZipEntry structure from a FILE* positioned at a
@@ -696,3 +696,5 @@
         ALOGD("  comment: '%s'\n", mFileComment);
 }
 
+} // namespace android
+
diff --git a/tools/zipalign/ZipFile.cpp b/tools/zipalign/ZipFile.cpp
index 88505b7..29d1bc6 100644
--- a/tools/zipalign/ZipFile.cpp
+++ b/tools/zipalign/ZipFile.cpp
@@ -35,7 +35,7 @@
 #include <assert.h>
 #include <inttypes.h>
 
-using namespace android;
+namespace android {
 
 /*
  * Some environments require the "b", some choke on it.
@@ -134,7 +134,7 @@
 /*
  * Return the Nth entry in the archive.
  */
-android::ZipEntry* ZipFile::getEntryByIndex(int idx) const
+ZipEntry* ZipFile::getEntryByIndex(int idx) const
 {
     if (idx < 0 || idx >= (int) mEntries.size())
         return NULL;
@@ -145,7 +145,7 @@
 /*
  * Find an entry by name.
  */
-android::ZipEntry* ZipFile::getEntryByName(const char* fileName) const
+ZipEntry* ZipFile::getEntryByName(const char* fileName) const
 {
     /*
      * Do a stupid linear string-compare search.
@@ -1397,3 +1397,4 @@
         mCentralDirSize, mCentralDirOffset, mCommentLen);
 }
 
+} // namespace android
diff --git a/tools/zipalign/include/ZipAlign.h b/tools/zipalign/include/ZipAlign.h
new file mode 100644
index 0000000..ab36086
--- /dev/null
+++ b/tools/zipalign/include/ZipAlign.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef ZIPALIGN_H
+#define ZIPALIGN_H
+
+namespace android {
+
+/*
+ * Generate a new, aligned, zip "output" from an "input" zip.
+ * - alignTo: Alignment (in bytes) for uncompressed entries.
+ * - force  : Overwrite output if it exists, fail otherwise.
+ * - zopfli : Recompress compressed entries with more efficient algorithm.
+ *            Copy compressed entries as-is, and unaligned, otherwise.
+ * - pageAlignSharedLibs: Align .so files to 4096 and other files to
+ *   alignTo, or all files to alignTo if false..
+ *
+ * Returns 0 on success.
+ */
+int process(const char* input, const char* output, int alignTo, bool force,
+    bool zopfli, bool pageAlignSharedLibs);
+
+/*
+ * Verify the alignment of a zip archive.
+ * - alignTo: Alignment (in bytes) for uncompressed entries.
+ * - pageAlignSharedLibs: Align .so files to 4096 and other files to
+ *   alignTo, or all files to alignTo if false..
+ *
+ * Returns 0 on success.
+ */
+int verify(const char* fileName, int alignTo, bool verbose,
+    bool pageAlignSharedLibs);
+
+} // namespace android
+
+#endif // ZIPALIGN_H
diff --git a/tools/zipalign/tests/data/unaligned.zip b/tools/zipalign/tests/data/unaligned.zip
new file mode 100644
index 0000000..d572b1a
--- /dev/null
+++ b/tools/zipalign/tests/data/unaligned.zip
Binary files differ
diff --git a/tools/zipalign/tests/src/align_test.cpp b/tools/zipalign/tests/src/align_test.cpp
new file mode 100644
index 0000000..073a156
--- /dev/null
+++ b/tools/zipalign/tests/src/align_test.cpp
@@ -0,0 +1,24 @@
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+#include "ZipAlign.h"
+
+#include <stdio.h>
+#include <string>
+
+#include <android-base/file.h>
+
+using namespace android;
+
+static std::string GetTestPath(const std::string& filename) {
+  static std::string test_data_dir = android::base::GetExecutableDirectory() + "/tests/data/";
+  return test_data_dir + filename;
+}
+
+TEST(Align, Unaligned) {
+  const std::string src = GetTestPath("unaligned.zip");
+  const std::string dst = GetTestPath("unaligned_out.zip");
+
+  int result = process(src.c_str(), dst.c_str(), 4, true, false, 4096);
+  ASSERT_EQ(0, result);
+}