Merge "move device_manifest.xml to base_vendor.mk"
diff --git a/core/config.mk b/core/config.mk
index 967ab5e..df73c56 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -1036,6 +1036,8 @@
 TARGET_SDK_VERSIONS_WITHOUT_JAVA_18_SUPPORT := $(call numbers_less_than,24,$(TARGET_AVAILABLE_SDK_VERSIONS))
 TARGET_SDK_VERSIONS_WITHOUT_JAVA_19_SUPPORT := $(call numbers_less_than,27,$(TARGET_AVAILABLE_SDK_VERSIONS))
 
+INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/hiddenapi-public-list.txt
+INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/hiddenapi-private-list.txt
 INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/hiddenapi-light-greylist.txt
 INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/hiddenapi-dark-greylist.txt
 INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/hiddenapi-blacklist.txt
diff --git a/core/definitions.mk b/core/definitions.mk
index 8d7e3ff..df5aa67 100644
--- a/core/definitions.mk
+++ b/core/definitions.mk
@@ -2837,6 +2837,7 @@
 endef
 
 # Copy dex files, invoking $(HIDDENAPI) on them in the process.
+# Also make the source dex file an input of the hiddenapi singleton rule in dex_preopt.mk.
 define hiddenapi-copy-dex-files
 $(2): $(1) $(HIDDENAPI) $(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST) \
       $(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST) $(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)
@@ -2848,6 +2849,10 @@
 	    --light-greylist=$(INTERNAL_PLATFORM_HIDDENAPI_LIGHT_GREYLIST) \
 	    --dark-greylist=$(INTERNAL_PLATFORM_HIDDENAPI_DARK_GREYLIST) \
 	    --blacklist=$(INTERNAL_PLATFORM_HIDDENAPI_BLACKLIST)
+
+$(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST): $(1)
+$(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST): \
+    PRIVATE_DEX_INPUTS := $$(PRIVATE_DEX_INPUTS) $(1)
 endef
 
 # File names for intermediate dex files of `hiddenapi-copy-soong-jar`.
diff --git a/core/dex_preopt.mk b/core/dex_preopt.mk
index de830d4..6be6c17 100644
--- a/core/dex_preopt.mk
+++ b/core/dex_preopt.mk
@@ -89,3 +89,36 @@
 ifdef TARGET_2ND_ARCH
 $(TARGET_2ND_ARCH_VAR_PREFIX)DEXPREOPT_ONE_FILE_DEPENDENCY_BUILT_BOOT_PREOPT := $($(TARGET_2ND_ARCH_VAR_PREFIX)DEFAULT_DEX_PREOPT_BUILT_IMAGE_FILENAME)
 endif  # TARGET_2ND_ARCH
+
+# === hiddenapi rules ===
+
+hiddenapi_stubs_jar = $(call intermediates-dir-for,JAVA_LIBRARIES,$(1),,COMMON)/javalib.jar
+
+# Public API stubs
+HIDDENAPI_STUBS := \
+    $(call hiddenapi_stubs_jar,android_stubs_current) \
+    $(call hiddenapi_stubs_jar,android.test.base.stubs) \
+    $(call hiddenapi_stubs_jar,android.test.mock.stubs) \
+    $(call hiddenapi_stubs_jar,android.test.runner.stubs)
+
+# System API stubs
+HIDDENAPI_STUBS += \
+    $(call hiddenapi_stubs_jar,android_system_stubs_current)
+
+# Test API stubs
+HIDDENAPI_STUBS += \
+    $(call hiddenapi_stubs_jar,android_test_stubs_current)
+
+# Singleton rule which applies $(HIDDENAPI) on all boot class path dex files.
+# Inputs are filled with `hiddenapi-copy-dex-files` rules.
+$(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST): \
+    PRIVATE_HIDDENAPI_STUBS := $(HIDDENAPI_STUBS)
+$(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST): \
+    .KATI_IMPLICIT_OUTPUTS := $(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST)
+$(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST): $(HIDDENAPI) $(HIDDENAPI_STUBS)
+	for INPUT_DEX in $(PRIVATE_DEX_INPUTS); do \
+		find `dirname $${INPUT_DEX}` -maxdepth 1 -name "classes*.dex"; \
+	done | sort | sed 's/^/--boot-dex=/' | xargs $(HIDDENAPI) list \
+	    $(addprefix --stub-dex=,$(PRIVATE_HIDDENAPI_STUBS)) \
+	    --out-public=$(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST) \
+	    --out-private=$(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST)
diff --git a/core/main.mk b/core/main.mk
index 436971a..b057465 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -26,6 +26,8 @@
 
 else # KATI
 
+$(info [1/1] initializing build system ...)
+
 # Absolute path of the present working direcotry.
 # This overrides the shell variable $PWD, which does not necessarily points to
 # the top of the source tree, for example when "make -C" is used in m/mm/mmm.
@@ -405,6 +407,8 @@
 ENFORCE_RRO_SOURCES :=
 endif
 
+subdir_makefiles_inc := .
+
 ifneq ($(ONE_SHOT_MAKEFILE),)
 # We've probably been invoked by the "mm" shell function
 # with a subdirectory's makefile.
@@ -449,7 +453,7 @@
 #
 
 subdir_makefiles := $(SOONG_ANDROID_MK) $(file <$(OUT_DIR)/.module_paths/Android.mk.list)
-subdir_makefiles_total := $(words $(subdir_makefiles))
+subdir_makefiles_total := $(words int $(subdir_makefiles) post finish)
 .KATI_READONLY := subdir_makefiles_total
 
 $(foreach mk,$(subdir_makefiles),$(info [$(call inc_and_print,subdir_makefiles_inc)/$(subdir_makefiles_total)] including $(mk) ...)$(eval include $(mk)))
@@ -465,6 +469,12 @@
 
 endif # ONE_SHOT_MAKEFILE
 
+ifndef subdir_makefiles_total
+subdir_makefiles_total := $(words init post finish)
+endif
+
+$(info [$(call inc_and_print,subdir_makefiles_inc)/$(subdir_makefiles_total)] finishing build rules ...)
+
 # -------------------------------------------------------------------
 # All module makefiles have been included at this point.
 # -------------------------------------------------------------------
@@ -1405,4 +1415,6 @@
 ndk: $(SOONG_OUT_DIR)/ndk.timestamp
 .PHONY: ndk
 
+$(info [$(call inc_and_print,subdir_makefiles_inc)/$(subdir_makefiles_total)] writing build rules ...)
+
 endif # KATI
diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py
index 0e25c7e..26d4187 100755
--- a/tools/releasetools/ota_from_target_files.py
+++ b/tools/releasetools/ota_from_target_files.py
@@ -1657,9 +1657,15 @@
   target_file = common.MakeTempFile(prefix="targetfiles-", suffix=".zip")
   target_zip = zipfile.ZipFile(target_file, 'w', allowZip64=True)
 
-  input_tmp = common.UnzipTemp(input_file, UNZIP_PATTERN)
   with zipfile.ZipFile(input_file, 'r') as input_zip:
     infolist = input_zip.infolist()
+    namelist = input_zip.namelist()
+
+  # Additionally unzip 'RADIO/*' if exists.
+  unzip_pattern = UNZIP_PATTERN[:]
+  if any([entry.startswith('RADIO/') for entry in namelist]):
+    unzip_pattern.append('RADIO/*')
+  input_tmp = common.UnzipTemp(input_file, unzip_pattern)
 
   for info in infolist:
     unzipped_file = os.path.join(input_tmp, *info.filename.split('/'))
@@ -1675,7 +1681,7 @@
     elif skip_postinstall and info.filename == POSTINSTALL_CONFIG:
       pass
 
-    elif info.filename.startswith(('META/', 'IMAGES/')):
+    elif info.filename.startswith(('META/', 'IMAGES/', 'RADIO/')):
       common.ZipWrite(target_zip, unzipped_file, arcname=info.filename)
 
   common.ZipClose(target_zip)
diff --git a/tools/releasetools/test_ota_from_target_files.py b/tools/releasetools/test_ota_from_target_files.py
index 7c34b7e..c8e6750 100644
--- a/tools/releasetools/test_ota_from_target_files.py
+++ b/tools/releasetools/test_ota_from_target_files.py
@@ -566,6 +566,8 @@
     self.assertIn('IMAGES/boot.img', namelist)
     self.assertIn('IMAGES/system.img', namelist)
     self.assertIn('IMAGES/vendor.img', namelist)
+    self.assertIn('RADIO/bootloader.img', namelist)
+    self.assertIn('RADIO/modem.img', namelist)
     self.assertIn(POSTINSTALL_CONFIG, namelist)
 
     self.assertNotIn('IMAGES/system_other.img', namelist)
@@ -583,11 +585,33 @@
     self.assertIn('IMAGES/boot.img', namelist)
     self.assertIn('IMAGES/system.img', namelist)
     self.assertIn('IMAGES/vendor.img', namelist)
+    self.assertIn('RADIO/bootloader.img', namelist)
+    self.assertIn('RADIO/modem.img', namelist)
 
     self.assertNotIn('IMAGES/system_other.img', namelist)
     self.assertNotIn('IMAGES/system.map', namelist)
     self.assertNotIn(POSTINSTALL_CONFIG, namelist)
 
+  def test_GetTargetFilesZipForSecondaryImages_withoutRadioImages(self):
+    input_file = construct_target_files(secondary=True)
+    common.ZipDelete(input_file, 'RADIO/bootloader.img')
+    common.ZipDelete(input_file, 'RADIO/modem.img')
+    target_file = GetTargetFilesZipForSecondaryImages(input_file)
+
+    with zipfile.ZipFile(target_file) as verify_zip:
+      namelist = verify_zip.namelist()
+
+    self.assertIn('META/ab_partitions.txt', namelist)
+    self.assertIn('IMAGES/boot.img', namelist)
+    self.assertIn('IMAGES/system.img', namelist)
+    self.assertIn('IMAGES/vendor.img', namelist)
+    self.assertIn(POSTINSTALL_CONFIG, namelist)
+
+    self.assertNotIn('IMAGES/system_other.img', namelist)
+    self.assertNotIn('IMAGES/system.map', namelist)
+    self.assertNotIn('RADIO/bootloader.img', namelist)
+    self.assertNotIn('RADIO/modem.img', namelist)
+
   def test_GetTargetFilesZipWithoutPostinstallConfig(self):
     input_file = construct_target_files()
     target_file = GetTargetFilesZipWithoutPostinstallConfig(input_file)