Merge "Update the docs for 'mm[m][a]'"
diff --git a/CleanSpec.mk b/CleanSpec.mk
index ee38a74..6c5ecd9 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -695,6 +695,10 @@
 # Switch to symlinks for VNDK libs
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/lib*/vndk-*)
 
+# Remove Android Core Library artifacts from the system partition
+# again, as the original change removing them was reverted.
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/*.jar)
+
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
 # ************************************************
diff --git a/core/Makefile b/core/Makefile
index 6015796..96dc613 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -3616,36 +3616,6 @@
 
 ifeq (true,$(PRODUCT_BUILD_SUPER_PARTITION))
 
-# (1): list of items like "system", "vendor", "product", "system_ext"
-# return: map each item into a command ( wrapped in $$() ) that reads the size
-define read-size-of-partitions
-$(foreach image,$(call images-for-partitions,$(1)),$$($(SPARSE_IMG) --get_partition_size $(image)))
-endef
-
-# round result to BOARD_SUPER_PARTITION_ALIGNMENT
-#$(1): the calculated size
-ifeq (,$(BOARD_SUPER_PARTITION_ALIGNMENT))
-define round-partition-size
-$(1)
-endef
-else
-define round-partition-size
-$$((($(1)+$(BOARD_SUPER_PARTITION_ALIGNMENT)-1)/$(BOARD_SUPER_PARTITION_ALIGNMENT)*$(BOARD_SUPER_PARTITION_ALIGNMENT)))
-endef
-endif
-
-define super-slot-suffix
-$(if $(filter true,$(AB_OTA_UPDATER)),$(if $(filter true,$(PRODUCT_RETROFIT_DYNAMIC_PARTITIONS)),,_a))
-endef
-
-ifndef BOARD_SUPER_PARTITION_WARN_LIMIT
-BOARD_SUPER_PARTITION_WARN_LIMIT := $$(($(BOARD_SUPER_PARTITION_SIZE) * 95 / 100))
-endif
-
-ifndef BOARD_SUPER_PARTITION_ERROR_LIMIT
-BOARD_SUPER_PARTITION_ERROR_LIMIT := $(BOARD_SUPER_PARTITION_SIZE)
-endif
-
 droid_targets: check-all-partition-sizes
 
 .PHONY: check-all-partition-sizes check-all-partition-sizes-nodeps
@@ -3654,103 +3624,28 @@
 
 check-all-partition-sizes: $(check_all_partition_sizes_file)
 
-# Add image dependencies so that generated_*_image_info.txt are written before checking.
 $(check_all_partition_sizes_file): \
-    $(SPARSE_IMG) \
+    $(CHECK_PARTITION_SIZES) \
     $(call images-for-partitions,$(BOARD_SUPER_PARTITION_PARTITION_LIST))
 
-ifeq ($(PRODUCT_RETROFIT_DYNAMIC_PARTITIONS),true)
-# Check sum(super partition block devices) == super partition
-# Non-retrofit devices already defines BOARD_SUPER_PARTITION_SUPER_DEVICE_SIZE = BOARD_SUPER_PARTITION_SIZE
-define check-super-partition-size
-  size_list="$(foreach device,$(call to-upper,$(BOARD_SUPER_PARTITION_BLOCK_DEVICES)),$(BOARD_SUPER_PARTITION_$(device)_DEVICE_SIZE))"; \
-  sum_sizes_expr=$$(sed -e 's/ /+/g' <<< "$${size_list}"); \
-  max_size_expr="$(BOARD_SUPER_PARTITION_SIZE)"; \
-  if [ $$(( $${sum_sizes_expr} )) -ne $$(( $${max_size_expr} )) ]; then \
-    echo "The sum of super partition block device sizes is not equal to BOARD_SUPER_PARTITION_SIZE:"; \
-    echo $${sum_sizes_expr} '!=' $${max_size_expr}; \
-    exit 1; \
-  else \
-    echo "The sum of super partition block device sizes is equal to BOARD_SUPER_PARTITION_SIZE:"; \
-    echo $${sum_sizes_expr} '==' $${max_size_expr}; \
-  fi
-endef
-endif
-
-# $(1): human-readable max size string
-# $(2): max size expression
-# $(3): list of partition names
-# $(4): human-readable warn size string
-# $(5): warn size expression
-# $(6): human readable error size string
-# $(7): error size expression
-define check-sum-of-partition-sizes
-  partition_size_list="$$(for i in $(call read-size-of-partitions,$(3)); do \
-    echo $(call round-partition-size,$${i}); \
-  done)"; \
-  sum_sizes_expr=$$(tr '\n' '+' <<< "$${partition_size_list}" | sed 's/+$$//'); \
-  if [ $$(( $${sum_sizes_expr} )) -gt $$(( $(2) )) ]; then \
-    echo "The sum of sizes of [$(strip $(3))] is larger than $(strip $(1)):"; \
-    echo $${sum_sizes_expr} '==' $$(( $${sum_sizes_expr} )) '>' "$(2)" '==' $$(( $(2) )); \
-    exit 1; \
-  else \
-    if [[ ! -z "$(7)" ]] && [ $$(( $${sum_sizes_expr} )) -gt $$(( $(7) )) ]; then \
-        echo "!!!! ERROR !!!! The sum of sizes of [$(strip $(3))] is larger than $(strip $(6)):"; \
-        echo $${sum_sizes_expr} '==' $$(( $${sum_sizes_expr} )) '>' "$(7)" '==' $$(( $(7) )); \
-        echo "Super partition is" $$(( $$(( $$(( $${sum_sizes_expr} )) * 100)) / $$(( $(2) )) )) "percent occupied!"; \
-        exit 1; \
-    fi; \
-    if [[ ! -z "$(5)" ]] && [ $$(( $${sum_sizes_expr} )) -gt $$(( $(5) )) ]; then \
-        echo "!!!! WARNING !!!! The sum of sizes of [$(strip $(3))] is larger than $(strip $(4)):"; \
-        echo $${sum_sizes_expr} '==' $$(( $${sum_sizes_expr} )) '>' "$(5)" '==' $$(( $(5) )); \
-        echo "Super partition is" $$(( $$(( $$(( $${sum_sizes_expr} )) * 100)) / $$(( $(2) )) )) "percent occupied!"; \
-    fi; \
-    echo "The sum of sizes of [$(strip $(3))] is within $(strip $(1)):"; \
-    echo $${sum_sizes_expr} '==' $$(( $${sum_sizes_expr} )) '<=' "$(2)" '==' $$(( $(2) )); \
-  fi;
-endef
-
+# $(1): misc_info.txt
 define check-all-partition-sizes-target
-  # Check sum(all partitions) <= super partition (/ 2 for A/B devices launched with dynamic partitions)
-  $(if $(BOARD_SUPER_PARTITION_SIZE),$(if $(BOARD_SUPER_PARTITION_PARTITION_LIST), \
-    $(call check-sum-of-partition-sizes,BOARD_SUPER_PARTITION_SIZE$(if $(call super-slot-suffix), / 2), \
-      $(BOARD_SUPER_PARTITION_SIZE)$(if $(call super-slot-suffix), / 2),$(BOARD_SUPER_PARTITION_PARTITION_LIST), \
-      BOARD_SUPER_PARTITION_WARN_LIMIT$(if $(call super-slot-suffix), / 2), \
-      $(BOARD_SUPER_PARTITION_WARN_LIMIT)$(if $(call super-slot-suffix), / 2), \
-      BOARD_SUPER_PARTITION_ERROR_LIMIT$(if $(call super-slot-suffix), / 2), \
-      $(BOARD_SUPER_PARTITION_ERROR_LIMIT)$(if $(call super-slot-suffix), / 2)) \
-  ))
-
-  # For each group, check sum(partitions in group) <= group size
-  $(foreach group,$(call to-upper,$(BOARD_SUPER_PARTITION_GROUPS)), \
-    $(if $(BOARD_$(group)_SIZE),$(if $(BOARD_$(group)_PARTITION_LIST), \
-      $(call check-sum-of-partition-sizes,BOARD_$(group)_SIZE,$(BOARD_$(group)_SIZE),$(BOARD_$(group)_PARTITION_LIST)))))
-
-  # Check sum(all group sizes) <= super partition (/ 2 for A/B devices launched with dynamic partitions)
-  if [[ ! -z $(BOARD_SUPER_PARTITION_SIZE) ]]; then \
-    group_size_list="$(foreach group,$(call to-upper,$(BOARD_SUPER_PARTITION_GROUPS)),$(BOARD_$(group)_SIZE))"; \
-    sum_sizes_expr=$$(sed -e 's/ /+/g' <<< "$${group_size_list}"); \
-    max_size_tail=$(if $(call super-slot-suffix)," / 2"); \
-    max_size_expr="$(BOARD_SUPER_PARTITION_SIZE)$${max_size_tail}"; \
-    if [ $$(( $${sum_sizes_expr} )) -gt $$(( $${max_size_expr} )) ]; then \
-      echo "The sum of sizes of [$(strip $(BOARD_SUPER_PARTITION_GROUPS))] is larger than BOARD_SUPER_PARTITION_SIZE$${max_size_tail}:"; \
-      echo $${sum_sizes_expr} '==' $$(( $${sum_sizes_expr} )) '>' $${max_size_expr} '==' $$(( $${max_size_expr} )); \
-      exit 1; \
-    else \
-      echo "The sum of sizes of [$(strip $(BOARD_SUPER_PARTITION_GROUPS))] is within BOARD_SUPER_PARTITION_SIZE$${max_size_tail}:"; \
-      echo $${sum_sizes_expr} '==' $$(( $${sum_sizes_expr} )) '<=' $${max_size_expr} '==' $$(( $${max_size_expr} )); \
-    fi \
-  fi
+  mkdir -p $(dir $(1))
+  rm -f $(1)
+  $(call dump-super-image-info, $(1))
+  $(foreach partition,$(BOARD_SUPER_PARTITION_PARTITION_LIST), \
+    echo "$(partition)_image="$(call images-for-partitions,$(partition)) >> $(1);)
+  $(CHECK_PARTITION_SIZES) -v $(1)
 endef
 
 $(check_all_partition_sizes_file):
-	$(call check-all-partition-sizes-target)
-	$(call check-super-partition-size)
+	$(call check-all-partition-sizes-target, \
+	  $(call intermediates-dir-for,PACKAGING,check-all-partition-sizes)/misc_info.txt)
 	touch $@
 
 check-all-partition-sizes-nodeps:
-	$(call check-all-partition-sizes-target)
-	$(call check-super-partition-size)
+	$(call check-all-partition-sizes-target, \
+	  $(call intermediates-dir-for,PACKAGING,check-all-partition-sizes-nodeps)/misc_info.txt)
 
 endif # PRODUCT_BUILD_SUPER_PARTITION
 
@@ -4189,6 +4084,14 @@
     echo "build_non_sparse_super_partition=true" >> $(1))
   $(if $(filter true,$(BOARD_SUPER_IMAGE_IN_UPDATE_PACKAGE)), \
     echo "super_image_in_update_package=true" >> $(1))
+  $(if $(BOARD_SUPER_PARTITION_SIZE), \
+    echo "super_partition_size=$(BOARD_SUPER_PARTITION_SIZE)" >> $(1))
+  $(if $(BOARD_SUPER_PARTITION_ALIGNMENT), \
+    echo "super_partition_alignment=$(BOARD_SUPER_PARTITION_ALIGNMENT)" >> $(1))
+  $(if $(BOARD_SUPER_PARTITION_WARN_LIMIT), \
+    echo "super_partition_warn_limit=$(BOARD_SUPER_PARTITION_WARN_LIMIT)" >> $(1))
+  $(if $(BOARD_SUPER_PARTITION_ERROR_LIMIT), \
+    echo "super_partition_error_limit=$(BOARD_SUPER_PARTITION_ERROR_LIMIT)" >> $(1))
 endef
 
 # By conditionally including the dependency of the target files package on the
@@ -4765,6 +4668,10 @@
   $(call dump-dynamic-partitions-info,$(1))
   $(if $(filter true,$(AB_OTA_UPDATER)), \
     echo "ab_update=true" >> $(1))
+  $(if $(filter true,$(PRODUCT_VIRTUAL_AB_OTA)), \
+    echo "virtual_ab=true" >> $(1))
+  $(if $(filter true,$(PRODUCT_VIRTUAL_AB_OTA_RETROFIT)), \
+    echo "virtual_ab_retrofit=true" >> $(1))
 endef
 
 endif # PRODUCT_USE_DYNAMIC_PARTITIONS
diff --git a/core/config.mk b/core/config.mk
index 477d764..a1bbe18 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -602,6 +602,7 @@
 MAKE_RECOVERY_PATCH := $(HOST_OUT_EXECUTABLES)/make_recovery_patch$(HOST_EXECUTABLE_SUFFIX)
 OTA_FROM_TARGET_FILES := $(HOST_OUT_EXECUTABLES)/ota_from_target_files$(HOST_EXECUTABLE_SUFFIX)
 SPARSE_IMG := $(HOST_OUT_EXECUTABLES)/sparse_img$(HOST_EXECUTABLE_SUFFIX)
+CHECK_PARTITION_SIZES := $(HOST_OUT_EXECUTABLES)/check_partition_sizes$(HOST_EXECUTABLE_SUFFIX)
 
 PROGUARD_HOME := external/proguard
 PROGUARD := $(PROGUARD_HOME)/bin/proguard.sh
diff --git a/core/main.mk b/core/main.mk
index 29c5a4c..9b63f69 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -1109,11 +1109,11 @@
 endef
 
 # Check that libraries that should only be in APEXes don't end up in the system
-# image. For the Runtime APEX this complements the checks in
+# image. For the ART APEX this complements the checks in
 # art/build/apex/art_apex_test.py.
 # TODO(b/128708192): Implement this restriction in Soong instead.
 
-# Runtime APEX libraries
+# ART APEX (native) libraries
 APEX_MODULE_LIBS := \
   libadbconnection.so \
   libadbconnectiond.so \
@@ -1129,14 +1129,10 @@
   libartd-dexlayout.so \
   libartd.so \
   libartpalette.so \
-  libc.so \
-  libc_malloc_debug.so \
-  libc_malloc_hooks.so \
   libdexfile.so \
   libdexfile_external.so \
   libdexfiled.so \
   libdexfiled_external.so \
-  libdl.so \
   libdt_fd_forward.so \
   libdt_socket.so \
   libicui18n.so \
@@ -1144,11 +1140,9 @@
   libicu_jni.so \
   libjavacore.so \
   libjdwp.so \
-  libm.so \
   libnativebridge.so \
   libnativehelper.so \
   libnativeloader.so \
-  libneuralnetworks.so \
   libnpt.so \
   libopenjdk.so \
   libopenjdkjvm.so \
@@ -1160,16 +1154,45 @@
   libprofiled.so \
   libsigchain.so \
 
+# Runtime (Bionic) APEX (native) libraries
+APEX_MODULE_LIBS += \
+  libc.so \
+  libc_malloc_debug.so \
+  libc_malloc_hooks.so \
+  libdl.so \
+  libm.so \
+
 # Conscrypt APEX libraries
 APEX_MODULE_LIBS += \
   libjavacrypto.so \
 
+# Android Neural Network API (NNAPI) APEX (native) libraries
+APEX_MODULE_LIBS += \
+  libneuralnetworks.so \
+
+# ART APEX JARs (Java libraries)
+APEX_MODULE_LIBS += \
+  apache-xml.jar \
+  bouncycastle.jar \
+  core-icu4j.jar \
+  core-libart.jar \
+  core-oj.jar \
+  okhttp.jar \
+
+# Conscrypt APEX JARs (Java libraries)
+APEX_MODULE_LIBS += \
+  conscrypt.jar \
+
 # An option to disable the check below, for local use since some build targets
 # still may create these libraries in /system (b/129006418).
 DISABLE_APEX_LIBS_ABSENCE_CHECK ?=
 
+# Allow APEX libraries under /system/apex, which happens when APEX flattening
+# is enabled.
+APEX_LIBS_ABSENCE_CHECK_EXCLUDE := apex
+
 # Bionic should not be in /system, except for the bootstrap instance.
-APEX_LIBS_ABSENCE_CHECK_EXCLUDE := lib/bootstrap lib64/bootstrap
+APEX_LIBS_ABSENCE_CHECK_EXCLUDE += lib/bootstrap lib64/bootstrap
 
 # Exclude lib/arm and lib64/arm64 which contain the native bridge proxy libs. They
 # are compiled for the guest architecture and used with an entirely different
@@ -1216,12 +1239,14 @@
   # APEX might be misconfigured or something is wrong in the build system.
   # Please reach out to the APEX package owners and/or soong-team@, or
   # android-building@googlegroups.com externally.
+  #
+  # Likewise, we check for the absence of APEX Java libraries (JARs).
   define check-apex-libs-absence
     $(call maybe-print-list-and-error, \
       $(filter $(foreach lib,$(APEX_MODULE_LIBS),%/$(lib)), \
         $(filter-out $(foreach dir,$(APEX_LIBS_ABSENCE_CHECK_EXCLUDE), \
                        $(TARGET_OUT)/$(if $(findstring %,$(dir)),$(dir),$(dir)/%)), \
-          $(filter $(TARGET_OUT)/lib/% $(TARGET_OUT)/lib64/%,$(1)))), \
+          $(filter $(TARGET_OUT),$(1)))), \
       APEX libraries found in product_target_FILES (see comment for check-apex-libs-absence in \
       build/make/core/main.mk for details))
   endef
@@ -1235,11 +1260,13 @@
   # try "m installclean && m systemimage" to get a correct system image. For
   # local work you can also disable the check with the
   # DISABLE_APEX_LIBS_ABSENCE_CHECK environment variable.
+  #
+  # Likewise, we check for the absence of APEX Java libraries (JARs).
   define check-apex-libs-absence-on-disk
     $(hide) ( \
       cd $(TARGET_OUT) && \
-      findres=$$(find lib* \
-        $(foreach dir,$(APEX_LIBS_ABSENCE_CHECK_EXCLUDE),-path "$(subst %,*,$(dir))" -prune -o) \
+      findres=$$(find . \
+        $(foreach dir,$(APEX_LIBS_ABSENCE_CHECK_EXCLUDE),-path "./$(subst %,*,$(dir))" -prune -o) \
         -type f \( -false $(foreach lib,$(APEX_MODULE_LIBS),-o -name $(lib)) \) \
         -print) && \
       if [ -n "$$findres" ]; then \
diff --git a/envsetup.sh b/envsetup.sh
index 37993f8..a44cd50 100644
--- a/envsetup.sh
+++ b/envsetup.sh
@@ -21,6 +21,7 @@
 - provision:  Flash device with all required partitions. Options will be passed on to fastboot.
 - cgrep:      Greps on all local C/C++ files.
 - ggrep:      Greps on all local Gradle files.
+- gogrep:     Greps on all local Go files.
 - jgrep:      Greps on all local Java files.
 - resgrep:    Greps on all local res/*.xml files.
 - mangrep:    Greps on all local AndroidManifest.xml files.
@@ -970,6 +971,12 @@
         -exec grep --color -n "$@" {} +
 }
 
+function gogrep()
+{
+    find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f -name "*\.go" \
+        -exec grep --color -n "$@" {} +
+}
+
 function jgrep()
 {
     find . -name .repo -prune -o -name .git -prune -o -name out -prune -o -type f -name "*\.java" \
@@ -1018,7 +1025,7 @@
     Darwin)
         function mgrep()
         {
-            find -E . -name .repo -prune -o -name .git -prune -o -path ./out -prune -o \( -iregex '.*/(Makefile|Makefile\..*|.*\.make|.*\.mak|.*\.mk|.*\.bp)' -o -regex '(.*/)?soong/[^/]*.go' \) -type f \
+            find -E . -name .repo -prune -o -name .git -prune -o -path ./out -prune -o \( -iregex '.*/(Makefile|Makefile\..*|.*\.make|.*\.mak|.*\.mk|.*\.bp)' -o -regex '(.*/)?(build|soong)/.*[^/]*\.go' \) -type f \
                 -exec grep --color -n "$@" {} +
         }
 
@@ -1032,7 +1039,7 @@
     *)
         function mgrep()
         {
-            find . -name .repo -prune -o -name .git -prune -o -path ./out -prune -o \( -regextype posix-egrep -iregex '(.*\/Makefile|.*\/Makefile\..*|.*\.make|.*\.mak|.*\.mk|.*\.bp)' -o -regextype posix-extended -regex '(.*/)?soong/[^/]*.go' \) -type f \
+            find . -name .repo -prune -o -name .git -prune -o -path ./out -prune -o \( -regextype posix-egrep -iregex '(.*\/Makefile|.*\/Makefile\..*|.*\.make|.*\.mak|.*\.mk|.*\.bp)' -o -regextype posix-extended -regex '(.*/)?(build|soong)/.*[^/]*\.go' \) -type f \
                 -exec grep --color -n "$@" {} +
         }
 
diff --git a/target/product/gsi/Android.mk b/target/product/gsi/Android.mk
index f9402cb..536fe0c 100644
--- a/target/product/gsi/Android.mk
+++ b/target/product/gsi/Android.mk
@@ -2,7 +2,7 @@
 
 #####################################################################
 # list of vndk libraries from the source code.
-INTERNAL_VNDK_LIB_LIST := $(SOONG_OUT_DIR)/vndk/vndk.libraries.txt
+INTERNAL_VNDK_LIB_LIST := $(SOONG_VNDK_LIBRARIES_FILE)
 
 #####################################################################
 # This is the up-to-date list of vndk libs.
diff --git a/target/product/runtime_libart.mk b/target/product/runtime_libart.mk
index 581a72b..b8cb2ff 100644
--- a/target/product/runtime_libart.mk
+++ b/target/product/runtime_libart.mk
@@ -20,11 +20,6 @@
 $(error TARGET_CORE_JARS is empty; cannot update PRODUCT_PACKAGES variable)
 endif
 
-# Minimal boot classpath. This should be a subset of PRODUCT_BOOT_JARS, and equivalent to
-# TARGET_CORE_JARS.
-PRODUCT_PACKAGES += \
-    $(TARGET_CORE_JARS)
-
 # Additional mixins to the boot classpath.
 PRODUCT_PACKAGES += \
     android.test.base \
@@ -37,6 +32,9 @@
 PRODUCT_PACKAGES += com.android.runtime
 
 # ART APEX module.
+# Note that this package includes the minimal boot classpath JARs (listed in
+# TARGET_CORE_JARS), which should no longer be added directly to
+# PRODUCT_PACKAGES.
 PRODUCT_PACKAGES += com.android.art
 PRODUCT_HOST_PACKAGES += com.android.art
 
diff --git a/target/product/security/com_google_android_permissioncontroller-container.x509.pem b/target/product/security/com_google_android_permissioncontroller-container.x509.pem
new file mode 100644
index 0000000..95f476f
--- /dev/null
+++ b/target/product/security/com_google_android_permissioncontroller-container.x509.pem
@@ -0,0 +1,30 @@
+-----BEGIN CERTIFICATE-----

+MIIGHDCCBASgAwIBAgIUQ6US4BMtEWcSr2vuqJ5tlHLrBuYwDQYJKoZIhvcNAQELBQAwgZ4xCzAJ

+BgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQw

+EgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDE6MDgGA1UEAwwxY29tX2dvb2ds

+ZV9hbmRyb2lkX3Blcm1pc3Npb25jb250cm9sbGVyLWNvbnRhaW5lcjAeFw0xOTAyMDEwMzU2MDZa

+Fw00OTAyMDEwMzU2MDZaMIGeMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQG

+A1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLR29vZ2xlIEluYy4xEDAOBgNVBAsTB0FuZHJv

+aWQxOjA4BgNVBAMMMWNvbV9nb29nbGVfYW5kcm9pZF9wZXJtaXNzaW9uY29udHJvbGxlci1jb250

+YWluZXIwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCFBJYVLl8bg+dW9nE2R7pnwPAA

+q6JpnMzYnQb4lycDxugACxiWRTZ7wUSpIv/QY3Mu/dv6GnWJhrRwJFhCq0jISzsmRwMMi7DFEujc

+osO80s6kVjaQ71yYqMRhw2IfSIphFU0YMEgcucNAUG/ELFBDsnqxHqVdLxGDQZAwJ/Iz30olEBS4

+26VuwlpZrbIku/k0IO0B5NdzvSAQJECo2uOzEhEgQFQlTE5Lku9MX1PsC+pkokIRcYiOkXoXT6Qk

+wSkP23bpvsjbjwNOgavPs8Pdx4tHfTs60ysOdbNpHl5dWlDKvKRelxxUPS9yjTRFd6U8MZN0Ldt1

+8k4bzuptQ1yXML4csFGjsbhapqzEEO267ULdrWz58L+C1Rx/fBIV1efZOkNfdrrgGTIfnD8kbI9j

+kLPPPhOuPgMl2huflB4CcLLGyk2h7BkTYn6tM16Q/+i/qHtph81GZqdeDZRG9dG/VFqR8vD6JwCS

+aVci28zmzBR1kUlWoSRk2xiQmv7/ZGtgKVXppipVVasR2XUr+z3AFJm41TPAqei9xqeA6eRUmBbs

+YS0q/zSBPef0zTyyrQQLhkSW/D5sELEg9lidbpMUwe+myE9yNXnvbsQbzJFHrPanVLLeY896opaV

+GSjiIMaJdWrcp9OOYZQHO7W05UWmDvQHKg7Un6OxdFL/TEaZAwIDAQABo1AwTjAMBgNVHRMEBTAD

+AQH/MB0GA1UdDgQWBBTuRKWeFsHYvfDR4eoKuuMEz3iJrTAfBgNVHSMEGDAWgBTuRKWeFsHYvfDR

+4eoKuuMEz3iJrTANBgkqhkiG9w0BAQsFAAOCAgEAY6ua1I9ERwbs6SXZFqMGe5oryg/3Kbhx5Q+z

+uL8B8Nr85Ez/DR6Lr9PNBqR4Mc0/Rb8f1lHlxx5DcKggT1YjbbM9aULdrGI+wpvDwHp6kCo1T58L

+xn97KeJbTMy5uogLiBabXIiScyDEzz2GpdajbgsI9Io1i+v9uDv/RFWnZV+C7l3ZEZ2klo8Elexo

+BFOD0WdAptxERyBF549URonUxq3UWLbUisg0j8P3yKe9lmhZwO2tUmxaKQGlL4LAGRsQ4kTgXCwN

+WWUMRIYcdmW4TxLZy/KDKwLc6ezik07Ifc5bHajY3OSzRtp17Hf3U+/LnQt5KZD6MuoQu7Rxh1Fv

+pbUAgsD4UPEjUcsEEa3BbIcSfN6l68mkbf7AioGaKvOKXx9DVrrQYfW4gy5vskFyRMbLw5iTQ3Od

+WdViiw3ZmrCtnZLnUQY29B/04Ma0ejrtSonEJBl2VIakpyM0g5gyOcg17vl9xrAoITkbMp26GcUd

+1DQOlyBI2Uct7KKM2d9nuIEJwHn6D2eboRBk0Y/XPeg8IDNDUMx6K2ArOL5RUkBz3oGCdPQkLT+b

+PIps1fAOCGbE6HEPB6OEcTZNv8XXnchEjdwocTUgjAtnDhuGqcooQnCFbPodXISJURNZb5QUH2Ic

+Q4BDiSNSs38AF9dzBaFoUowHUySA0wrVDjT5tC0=

+-----END CERTIFICATE-----

diff --git a/tools/releasetools/Android.bp b/tools/releasetools/Android.bp
index 6cde77e..90a6485 100644
--- a/tools/releasetools/Android.bp
+++ b/tools/releasetools/Android.bp
@@ -264,6 +264,19 @@
 }
 
 python_binary_host {
+    name: "check_partition_sizes",
+    srcs: [
+        "check_partition_sizes.py",
+    ],
+    libs: [
+        "releasetools_common",
+    ],
+    defaults: [
+        "releasetools_binary_defaults",
+    ],
+}
+
+python_binary_host {
     name: "check_ota_package_signature",
     defaults: ["releasetools_binary_defaults"],
     srcs: [
@@ -419,6 +432,7 @@
     name: "releasetools_test_defaults",
     srcs: [
         "check_ota_package_signature.py",
+        "check_partition_sizes.py",
         "check_target_files_signatures.py",
         "make_recovery_patch.py",
         "merge_target_files.py",
diff --git a/tools/releasetools/check_partition_sizes.py b/tools/releasetools/check_partition_sizes.py
new file mode 100644
index 0000000..04d832c
--- /dev/null
+++ b/tools/releasetools/check_partition_sizes.py
@@ -0,0 +1,262 @@
+#!/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.
+
+"""
+Check dynamic partition sizes.
+
+usage: check_partition_sizes [info.txt]
+
+Check dump-super-partitions-info procedure for expected keys in info.txt. In
+addition, *_image (e.g. system_image, vendor_image, etc.) must be defined for
+each partition in dynamic_partition_list.
+
+Exit code is 0 if successful and non-zero if any failures.
+"""
+
+from __future__ import print_function
+
+import logging
+import sys
+
+import common
+import sparse_img
+
+if sys.hexversion < 0x02070000:
+  print("Python 2.7 or newer is required.", file=sys.stderr)
+  sys.exit(1)
+
+logger = logging.getLogger(__name__)
+
+class Expression(object):
+  def __init__(self, desc, expr, value=None):
+    # Human-readable description
+    self.desc = str(desc)
+    # Numeric expression
+    self.expr = str(expr)
+    # Value of expression
+    self.value = int(expr) if value is None else value
+
+  def CheckLe(self, other, level=logging.ERROR):
+    format_args = (self.desc, other.desc, self.expr, self.value,
+                   other.expr, other.value)
+    if self.value <= other.value:
+      logger.info("%s is less than or equal to %s:\n%s == %d <= %s == %d",
+                  *format_args)
+    else:
+      msg = "{} is greater than {}:\n{} == {} > {} == {}".format(*format_args)
+      if level == logging.ERROR:
+        raise RuntimeError(msg)
+      else:
+        logger.log(level, msg)
+
+  def CheckEq(self, other):
+    format_args = (self.desc, other.desc, self.expr, self.value,
+                   other.expr, other.value)
+    if self.value == other.value:
+      logger.info("%s equals %s:\n%s == %d == %s == %d", *format_args)
+    else:
+      raise RuntimeError("{} does not equal {}:\n{} == {} != {} == {}".format(
+          *format_args))
+
+
+# A/B feature flags
+class DeviceType(object):
+  NONE = 0
+  AB = 1
+
+  @staticmethod
+  def Get(info_dict):
+    if info_dict.get("ab_update") != "true":
+      return DeviceType.NONE
+    return DeviceType.AB
+
+
+# Dynamic partition feature flags
+class Dap(object):
+  NONE = 0
+  RDAP = 1
+  DAP = 2
+
+  @staticmethod
+  def Get(info_dict):
+    if info_dict.get("use_dynamic_partitions") != "true":
+      return Dap.NONE
+    if info_dict.get("dynamic_partition_retrofit") == "true":
+      return Dap.RDAP
+    return Dap.DAP
+
+
+class DynamicPartitionSizeChecker(object):
+  def __init__(self, info_dict):
+    if "super_partition_size" in info_dict:
+      if "super_partition_warn_limit" not in info_dict:
+        info_dict["super_partition_warn_limit"] = \
+            int(info_dict["super_partition_size"]) * 95 // 100
+      if "super_partition_error_limit" not in info_dict:
+        info_dict["super_partition_error_limit"] = \
+            int(info_dict["super_partition_size"])
+    self.info_dict = info_dict
+
+
+  def _ReadSizeOfPartition(self, name):
+    # Tests uses *_image_size instead (to avoid creating empty sparse images
+    # on disk)
+    if name + "_image_size" in self.info_dict:
+      return int(self.info_dict[name + "_image_size"])
+    return sparse_img.GetImagePartitionSize(self.info_dict[name + "_image"])
+
+
+  # Round result to BOARD_SUPER_PARTITION_ALIGNMENT
+  def _RoundPartitionSize(self, size):
+    alignment = self.info_dict.get("super_partition_alignment")
+    if alignment is None:
+      return size
+    return (size + alignment - 1) // alignment * alignment
+
+
+  def _CheckSuperPartitionSize(self):
+    info_dict = self.info_dict
+    super_block_devices = \
+        info_dict.get("super_block_devices", "").strip().split()
+    size_list = [int(info_dict.get("super_{}_device_size".format(b), "0"))
+                 for b in super_block_devices]
+    sum_size = Expression("sum of super partition block device sizes",
+                          "+".join(str(size) for size in size_list),
+                          sum(size_list))
+    super_partition_size = Expression("BOARD_SUPER_PARTITION_SIZE",
+                                      info_dict["super_partition_size"])
+    sum_size.CheckEq(super_partition_size)
+
+  def _CheckSumOfPartitionSizes(self, max_size, partition_names,
+                                warn_size=None, error_size=None):
+    partition_size_list = [self._RoundPartitionSize(
+        self._ReadSizeOfPartition(p)) for p in partition_names]
+    sum_size = Expression("sum of sizes of {}".format(partition_names),
+                          "+".join(str(size) for size in partition_size_list),
+                          sum(partition_size_list))
+    sum_size.CheckLe(max_size)
+    if error_size:
+      sum_size.CheckLe(error_size)
+    if warn_size:
+      sum_size.CheckLe(warn_size, level=logging.WARNING)
+
+  def _NumDeviceTypesInSuper(self):
+    slot = DeviceType.Get(self.info_dict)
+    dap = Dap.Get(self.info_dict)
+
+    if dap == Dap.NONE:
+      raise RuntimeError("check_partition_sizes should only be executed on "
+                         "builds with dynamic partitions enabled")
+
+    # Retrofit dynamic partitions: 1 slot per "super", 2 "super"s on the device
+    if dap == Dap.RDAP:
+      if slot != DeviceType.AB:
+        raise RuntimeError("Device with retrofit dynamic partitions must use "
+                           "regular (non-Virtual) A/B")
+      return 1
+
+    # Launch DAP: 1 super on the device
+    assert dap == Dap.DAP
+
+    # DAP + A/B: 2 slots in super
+    if slot == DeviceType.AB:
+      return 2
+
+    # DAP + non-A/B: 1 slot in super
+    assert slot == DeviceType.NONE
+    return 1
+
+  def _CheckAllPartitionSizes(self):
+    info_dict = self.info_dict
+    num_slots = self._NumDeviceTypesInSuper()
+    size_limit_suffix = (" / %d" % num_slots) if num_slots > 1 else ""
+
+    # Check sum(all partitions) <= super partition (/ 2 for A/B devices launched
+    # with dynamic partitions)
+    if "super_partition_size" in info_dict and \
+        "dynamic_partition_list" in info_dict:
+      max_size = Expression(
+          "BOARD_SUPER_PARTITION_SIZE{}".format(size_limit_suffix),
+          int(info_dict["super_partition_size"]) // num_slots)
+      warn_limit = Expression(
+          "BOARD_SUPER_PARTITION_WARN_LIMIT{}".format(size_limit_suffix),
+          int(info_dict["super_partition_warn_limit"]) // num_slots)
+      error_limit = Expression(
+          "BOARD_SUPER_PARTITION_ERROR_LIMIT{}".format(size_limit_suffix),
+          int(info_dict["super_partition_error_limit"]) // num_slots)
+      self._CheckSumOfPartitionSizes(
+          max_size, info_dict["dynamic_partition_list"].strip().split(),
+          warn_limit, error_limit)
+
+    groups = info_dict.get("super_partition_groups", "").strip().split()
+
+    # For each group, check sum(partitions in group) <= group size
+    for group in groups:
+      if "super_{}_group_size".format(group) in info_dict and \
+          "super_{}_partition_list".format(group) in info_dict:
+        group_size = Expression(
+            "BOARD_{}_SIZE".format(group),
+            int(info_dict["super_{}_group_size".format(group)]))
+        self._CheckSumOfPartitionSizes(
+            group_size,
+            info_dict["super_{}_partition_list".format(group)].strip().split())
+
+    # Check sum(all group sizes) <= super partition (/ 2 for A/B devices
+    # launched with dynamic partitions)
+    if "super_partition_size" in info_dict:
+      group_size_list = [int(info_dict.get(
+          "super_{}_group_size".format(group), 0)) for group in groups]
+      sum_size = Expression("sum of sizes of {}".format(groups),
+                            "+".join(str(size) for size in group_size_list),
+                            sum(group_size_list))
+      max_size = Expression(
+          "BOARD_SUPER_PARTITION_SIZE{}".format(size_limit_suffix),
+          int(info_dict["super_partition_size"]) // num_slots)
+      sum_size.CheckLe(max_size)
+
+  def Run(self):
+    self._CheckAllPartitionSizes()
+    if self.info_dict.get("dynamic_partition_retrofit") == "true":
+      self._CheckSuperPartitionSize()
+
+
+def CheckPartitionSizes(inp):
+  if isinstance(inp, str):
+    info_dict = common.LoadDictionaryFromFile(inp)
+    return DynamicPartitionSizeChecker(info_dict).Run()
+  if isinstance(inp, dict):
+    return DynamicPartitionSizeChecker(inp).Run()
+  raise ValueError("{} is not a dictionary or a valid path".format(inp))
+
+
+def main(argv):
+  args = common.ParseOptions(argv, __doc__)
+  if len(args) != 1:
+    common.Usage(__doc__)
+    sys.exit(1)
+  common.InitLogging()
+  CheckPartitionSizes(args[0])
+
+
+if __name__ == "__main__":
+  try:
+    common.CloseInheritedPipes()
+    main(sys.argv[1:])
+  except common.ExternalError:
+    logger.exception("\n   ERROR:\n")
+    sys.exit(1)
+  finally:
+    common.Cleanup()
diff --git a/tools/releasetools/test_check_partition_sizes.py b/tools/releasetools/test_check_partition_sizes.py
new file mode 100644
index 0000000..5482b1c
--- /dev/null
+++ b/tools/releasetools/test_check_partition_sizes.py
@@ -0,0 +1,94 @@
+#
+# 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 common
+import test_utils
+from check_partition_sizes import CheckPartitionSizes
+
+class CheckPartitionSizesTest(test_utils.ReleaseToolsTestCase):
+  def setUp(self):
+    self.info_dict = common.LoadDictionaryFromLines("""
+        use_dynamic_partitions=true
+        ab_update=true
+        super_block_devices=super
+        dynamic_partition_list=system vendor product
+        super_partition_groups=group
+        super_group_partition_list=system vendor product
+        super_partition_size=200
+        super_super_device_size=200
+        super_group_group_size=100
+        system_image_size=50
+        vendor_image_size=20
+        product_image_size=20
+        """.split("\n"))
+
+  def test_ab(self):
+    CheckPartitionSizes(self.info_dict)
+
+  def test_non_ab(self):
+    self.info_dict.update(common.LoadDictionaryFromLines("""
+        ab_update=false
+        super_partition_size=100
+        super_super_device_size=100
+        """.split("\n")))
+    CheckPartitionSizes(self.info_dict)
+
+  def test_non_dap(self):
+    self.info_dict.update(common.LoadDictionaryFromLines("""
+        use_dynamic_partitions=false
+        """.split("\n")))
+    with self.assertRaises(RuntimeError):
+      CheckPartitionSizes(self.info_dict)
+
+  def test_retrofit_dap(self):
+    self.info_dict.update(common.LoadDictionaryFromLines("""
+        dynamic_partition_retrofit=true
+        super_block_devices=system vendor
+        super_system_device_size=75
+        super_vendor_device_size=25
+        super_partition_size=100
+        """.split("\n")))
+    CheckPartitionSizes(self.info_dict)
+
+  def test_ab_partition_too_big(self):
+    self.info_dict.update(common.LoadDictionaryFromLines("""
+        system_image_size=100
+        """.split("\n")))
+    with self.assertRaises(RuntimeError):
+      CheckPartitionSizes(self.info_dict)
+
+  def test_ab_group_too_big(self):
+    self.info_dict.update(common.LoadDictionaryFromLines("""
+        super_group_group_size=110
+        """.split("\n")))
+    with self.assertRaises(RuntimeError):
+      CheckPartitionSizes(self.info_dict)
+
+  def test_no_image(self):
+    del self.info_dict["system_image_size"]
+    with self.assertRaises(KeyError):
+      CheckPartitionSizes(self.info_dict)
+
+  def test_block_devices_not_match(self):
+    self.info_dict.update(common.LoadDictionaryFromLines("""
+        dynamic_partition_retrofit=true
+        super_block_devices=system vendor
+        super_system_device_size=80
+        super_vendor_device_size=25
+        super_partition_size=100
+        """.split("\n")))
+    with self.assertRaises(RuntimeError):
+      CheckPartitionSizes(self.info_dict)