Merge "Allow downgradable partial OTA"
diff --git a/core/Makefile b/core/Makefile
index df5c6b2..add62b4 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -715,6 +715,26 @@
 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
@@ -878,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
@@ -4421,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
 
@@ -4720,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/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 6a35417..9b19761 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -748,7 +748,7 @@
     $(eval r := $(call module-installed-files,$(r))) \
     $(eval h_m := $(filter $(HOST_OUT)/%, $(ALL_MODULES.$(m).INSTALLED))) \
     $(eval h_r := $(filter $(HOST_OUT)/%, $(r))) \
-    $(eval h_m := $(filter-out $(h_r), $(h_m))) \
+    $(eval h_r := $(filter-out $(h_m), $(h_r))) \
     $(if $(h_m), $(eval $(call add-required-deps, $(h_m),$(h_r)))) \
   ) \
 )
@@ -764,7 +764,7 @@
     $(eval r := $(call module-installed-files,$(r))) \
     $(eval hc_m := $(filter $(HOST_CROSS_OUT)/%, $(ALL_MODULES.$(m).INSTALLED))) \
     $(eval hc_r := $(filter $(HOST_CROSS_OUT)/%, $(r))) \
-    $(eval hc_m := $(filter-out $(hc_r), $(hc_m))) \
+    $(eval hc_r := $(filter-out $(hc_m), $(hc_r))) \
     $(if $(hc_m), $(eval $(call add-required-deps, $(hc_m),$(hc_r)))) \
   ) \
 )
@@ -780,7 +780,7 @@
     $(eval r := $(call module-installed-files,$(r))) \
     $(eval t_m := $(filter $(TARGET_OUT_ROOT)/%, $(ALL_MODULES.$(m).INSTALLED))) \
     $(eval t_r := $(filter $(TARGET_OUT_ROOT)/%, $(r))) \
-    $(eval t_m := $(filter-out $(t_r), $(t_m))) \
+    $(eval t_r := $(filter-out $(t_m), $(t_r))) \
     $(if $(t_m), $(eval $(call add-required-deps, $(t_m),$(t_r)))) \
   ) \
 )
@@ -807,7 +807,6 @@
     )\
     $(eval req_files := $(strip $(req_files)))\
     $(eval mod_files := $(filter $(HOST_OUT)/%, $(call module-installed-files,$(m)))) \
-    $(eval mod_files := $(filter-out $(req_files),$(mod_files)))\
     $(if $(mod_files),\
       $(eval $(call add-required-deps, $(mod_files),$(req_files))) \
     )\
@@ -836,7 +835,6 @@
     )\
     $(eval req_files := $(strip $(req_files)))\
     $(eval mod_files := $(filter $(TARGET_OUT_ROOT)/%, $(call module-installed-files,$(m))))\
-    $(eval mod_files := $(filter-out $(req_files),$(mod_files)))\
     $(if $(mod_files),\
       $(eval $(call add-required-deps, $(mod_files),$(req_files))) \
     )\
@@ -895,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),\
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/target/board/generic_arm64/device.mk b/target/board/generic_arm64/device.mk
index 7c19279..7b16aea 100644
--- a/target/board/generic_arm64/device.mk
+++ b/target/board/generic_arm64/device.mk
@@ -30,5 +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/product/base_system.mk b/target/product/base_system.mk
index 8b7a9aa..4607edb 100644
--- a/target/product/base_system.mk
+++ b/target/product/base_system.mk
@@ -249,6 +249,7 @@
     shell_and_utilities_system \
     sm \
     snapshotctl \
+    snapuserd \
     SoundPicker \
     storaged \
     surfaceflinger \
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/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/merge_target_files.py b/tools/releasetools/merge_target_files.py
index 0d135d6..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
@@ -944,20 +945,32 @@
   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:
-    partition_map = {
-        'system': 'SYSTEM',
-        'vendor': 'VENDOR',
-        'product': 'PRODUCT',
-        'system_ext': 'SYSTEM_EXT',
-    }
+    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)
-    # TODO(b/171431774): Add a check to common.py to check if the
-    # shared UIDs cross the input build partition boundary.
+
+    # 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)
 
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 3eb660d..d88ee74 100644
--- a/tools/zipalign/Android.bp
+++ b/tools/zipalign/Android.bp
@@ -58,6 +58,7 @@
         "tests/src/*_test.cpp",
     ],
     static_libs: [
+        "libbase",
         "libzipalign",
         "libgmock",
     ],
@@ -65,4 +66,5 @@
          "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/tests/src/align_test.cpp b/tools/zipalign/tests/src/align_test.cpp
index b8f2e15..073a156 100644
--- a/tools/zipalign/tests/src/align_test.cpp
+++ b/tools/zipalign/tests/src/align_test.cpp
@@ -4,12 +4,21 @@
 #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 char* src = "tests/data/unaligned.zip";
-  const char* dst = "tests/data/unaligned_out.zip";
-  int result = process(src, dst, 4, true, false, 4096);
+  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);
 }