Merge "Make test more deterministic by setting search_path to None"
diff --git a/core/Makefile b/core/Makefile
index 6dbbef1..46350f6 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -4827,25 +4827,24 @@
 intermediates := $(call intermediates-dir-for,PACKAGING,check_vintf_all)
 check_vintf_all_deps :=
 
-APEX_OUT := $(PRODUCT_OUT)/apex
 # -----------------------------------------------------------------
-# Create apex-info-file.xml
+# Activate vendor APEXes for checkvintf
 
 apex_dirs := \
-  $(TARGET_OUT)/apex/% \
-  $(TARGET_OUT_SYSTEM_EXT)/apex/% \
   $(TARGET_OUT_VENDOR)/apex/% \
-  $(TARGET_OUT_ODM)/apex/% \
-  $(TARGET_OUT_PRODUCT)/apex/% \
 
 apex_files := $(sort $(filter $(apex_dirs), $(INTERNAL_ALLIMAGES_FILES)))
+
+APEX_OUT := $(intermediates)/apex
 APEX_INFO_FILE := $(APEX_OUT)/apex-info-list.xml
 
-# dump_apex_info scans $(PRODUCT_OUT)/apex and writes apex-info-list.xml there.
-# This relies on the fact that rules for .apex files install the contents in $(PRODUCT_OUT)/apex.
-$(APEX_INFO_FILE): $(HOST_OUT_EXECUTABLES)/dump_apex_info $(apex_files)
-	@echo "Creating apex-info-file in $(PRODUCT_OUT) "
-	$< --root_dir $(PRODUCT_OUT)
+# apexd_host scans/activates APEX files and writes /apex/apex-info-list.xml
+$(APEX_INFO_FILE): $(HOST_OUT_EXECUTABLES)/apexd_host $(apex_files)
+	@echo "Extracting apexes..."
+	@rm -rf $(APEX_OUT)
+	@mkdir -p $(APEX_OUT)
+	$< --vendor_path $(TARGET_OUT_VENDOR) \
+	   --apex_path $(APEX_OUT)
 
 apex_files :=
 apex_dirs :=
@@ -4893,6 +4892,8 @@
 check_vintf_all_deps += $(vintffm_log)
 $(vintffm_log): $(HOST_OUT_EXECUTABLES)/vintffm $(check_vintf_system_deps)
 	@( $< --check --dirmap /system:$(TARGET_OUT) \
+	  --dirmap /system_ext:$(TARGET_OUT_SYSTEM_EXT) \
+	  --dirmap /product:$(TARGET_OUT_PRODUCT) \
 	  $(VINTF_FRAMEWORK_MANIFEST_FROZEN_DIR) > $@ 2>&1 ) || ( cat $@ && exit 1 )
 
 $(call declare-1p-target,$(vintffm_log))
@@ -5299,7 +5300,6 @@
   apex_compression_tool \
   deapexer \
   debugfs_static \
-  dump_apex_info \
   fsck.erofs \
   make_erofs \
   merge_zips \
@@ -5408,6 +5408,9 @@
 ifneq ($(INSTALLED_VENDOR_KERNEL_BOOTIMAGE_TARGET),)
 	$(hide) echo "flash vendor_kernel_boot" >> $@
 endif
+ifneq ($(INSTALLED_RECOVERYIMAGE_TARGET),)
+	$(hide) echo "flash recovery" >> $@
+endif
 ifeq ($(BOARD_USES_PVMFWIMAGE),true)
 	$(hide) echo "flash pvmfw" >> $@
 endif
diff --git a/core/android_soong_config_vars.mk b/core/android_soong_config_vars.mk
index 5dba2d1..f132d13 100644
--- a/core/android_soong_config_vars.mk
+++ b/core/android_soong_config_vars.mk
@@ -114,7 +114,7 @@
 # are controlled by the MODULE_BUILD_FROM_SOURCE environment variable by
 # default.
 INDIVIDUALLY_TOGGLEABLE_PREBUILT_MODULES := \
-  bluetooth \
+  btservices \
   permission \
   rkpd \
   uwb \
diff --git a/core/base_rules.mk b/core/base_rules.mk
index c61c653..9ad1cc5 100644
--- a/core/base_rules.mk
+++ b/core/base_rules.mk
@@ -450,6 +450,12 @@
 $(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_IS_HOST_MODULE := $(LOCAL_IS_HOST_MODULE)
 $(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_HOST:= $(my_host)
 $(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_PREFIX := $(my_prefix)
+$(LOCAL_INTERMEDIATE_TARGETS) : .KATI_TAGS += ;module_name=$(LOCAL_MODULE)
+ifeq ($(LOCAL_MODULE_CLASS),)
+$(error "$(LOCAL_MODULE) in $(LOCAL_PATH) does not set $(LOCAL_MODULE_CLASS)")
+else
+$(LOCAL_INTERMEDIATE_TARGETS) : .KATI_TAGS += ;module_type=$(LOCAL_MODULE_CLASS)
+endif
 
 $(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_INTERMEDIATES_DIR:= $(intermediates)
 $(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_2ND_ARCH_VAR_PREFIX := $(LOCAL_2ND_ARCH_VAR_PREFIX)
diff --git a/core/tasks/sts-lite.mk b/core/tasks/sts-lite.mk
index dee25d4..65c65c3 100644
--- a/core/tasks/sts-lite.mk
+++ b/core/tasks/sts-lite.mk
@@ -29,7 +29,8 @@
 	$(ZIP2ZIP) -i $(STS_LITE_ZIP) -o $(STS_LITE_ZIP)_filtered \
 		-x android-sts-lite/tools/sts-tradefed-tests.jar \
 		'android-sts-lite/tools/*:sts-test/libs/' \
-		'android-sts-lite/testcases/*:sts-test/utils/'
+		'android-sts-lite/testcases/*:sts-test/utils/' \
+		'android-sts-lite/jdk/**/*:sts-test/jdk/'
 	$(MERGE_ZIPS) $@ $(STS_LITE_ZIP)_filtered $(STS_SDK_SAMPLES)
 	rm -f $(STS_LITE_ZIP)_filtered
 
diff --git a/core/version_defaults.mk b/core/version_defaults.mk
index 4a42783..c107254 100644
--- a/core/version_defaults.mk
+++ b/core/version_defaults.mk
@@ -103,7 +103,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 := 2023-05-05
+    PLATFORM_SECURITY_PATCH := 2023-06-05
 endif
 
 include $(BUILD_SYSTEM)/version_util.mk
diff --git a/packaging/distdir.mk b/packaging/distdir.mk
index c9508af..153ecf6 100644
--- a/packaging/distdir.mk
+++ b/packaging/distdir.mk
@@ -30,6 +30,7 @@
     $(eval _dist_$$(goal):)))
 
 define copy-one-dist-file
+$(2): .KATI_TAGS += ;rule_name=dist-cp
 $(2): $(1)
 	@echo "Dist: $$@"
 	rm -f $$@
diff --git a/target/product/cfi-common.mk b/target/product/cfi-common.mk
index 11c01a2..559963c 100644
--- a/target/product/cfi-common.mk
+++ b/target/product/cfi-common.mk
@@ -28,6 +28,7 @@
     hardware/broadcom/wlan/bcmdhd/wpa_supplicant_8_lib \
     hardware/synaptics/wlan/synadhd/wpa_supplicant_8_lib \
     hardware/interfaces/nfc \
+    hardware/qcom/wlan/qcwcn/wpa_supplicant_8_lib \
     hardware/qcom/wlan/legacy/qcwcn/wpa_supplicant_8_lib \
     hardware/qcom/wlan/wcn6740/qcwcn/wpa_supplicant_8_lib \
     hardware/interfaces/keymaster \
diff --git a/tools/aconfig/src/codegen_java.rs b/tools/aconfig/src/codegen_java.rs
index 47516b7..15eb2d6 100644
--- a/tools/aconfig/src/codegen_java.rs
+++ b/tools/aconfig/src/codegen_java.rs
@@ -20,17 +20,23 @@
 use tinytemplate::TinyTemplate;
 
 use crate::codegen;
-use crate::commands::OutputFile;
+use crate::commands::{CodegenMode, OutputFile};
 use crate::protos::{ProtoFlagPermission, ProtoFlagState, ProtoParsedFlag};
 
-pub fn generate_java_code<'a, I>(package: &str, parsed_flags_iter: I) -> Result<Vec<OutputFile>>
+pub fn generate_java_code<'a, I>(
+    package: &str,
+    parsed_flags_iter: I,
+    codegen_mode: CodegenMode,
+) -> Result<Vec<OutputFile>>
 where
     I: Iterator<Item = &'a ProtoParsedFlag>,
 {
     let class_elements: Vec<ClassElement> =
         parsed_flags_iter.map(|pf| create_class_element(package, pf)).collect();
     let is_read_write = class_elements.iter().any(|elem| elem.is_read_write);
-    let context = Context { package_name: package.to_string(), is_read_write, class_elements };
+    let is_test_mode = codegen_mode == CodegenMode::Test;
+    let context =
+        Context { class_elements, is_test_mode, is_read_write, package_name: package.to_string() };
     let mut template = TinyTemplate::new();
     template.add_template("Flags.java", include_str!("../templates/Flags.java.template"))?;
     template.add_template(
@@ -56,14 +62,15 @@
 
 #[derive(Serialize)]
 struct Context {
-    pub package_name: String,
-    pub is_read_write: bool,
     pub class_elements: Vec<ClassElement>,
+    pub is_test_mode: bool,
+    pub is_read_write: bool,
+    pub package_name: String,
 }
 
 #[derive(Serialize)]
 struct ClassElement {
-    pub default_value: String,
+    pub default_value: bool,
     pub device_config_namespace: String,
     pub device_config_flag: String,
     pub flag_name_constant_suffix: String,
@@ -75,11 +82,7 @@
     let device_config_flag = codegen::create_device_config_ident(package, pf.name())
         .expect("values checked at flag parse time");
     ClassElement {
-        default_value: if pf.state() == ProtoFlagState::ENABLED {
-            "true".to_string()
-        } else {
-            "false".to_string()
-        },
+        default_value: pf.state() == ProtoFlagState::ENABLED,
         device_config_namespace: pf.namespace().to_string(),
         device_config_flag,
         flag_name_constant_suffix: pf.name().to_ascii_uppercase(),
@@ -109,34 +112,50 @@
     use super::*;
     use std::collections::HashMap;
 
-    #[test]
-    fn test_generate_java_code() {
-        let parsed_flags = crate::test::parse_test_flags();
-        let generated_files =
-            generate_java_code(crate::test::TEST_PACKAGE, parsed_flags.parsed_flag.iter()).unwrap();
-        let expect_flags_content = r#"
-        package com.android.aconfig.test;
-        public final class Flags {
-            public static final String FLAG_DISABLED_RO = "com.android.aconfig.test.disabled_ro";
-            public static final String FLAG_DISABLED_RW = "com.android.aconfig.test.disabled_rw";
-            public static final String FLAG_ENABLED_RO = "com.android.aconfig.test.enabled_ro";
-            public static final String FLAG_ENABLED_RW = "com.android.aconfig.test.enabled_rw";
+    const EXPECTED_FEATUREFLAGS_CONTENT: &str = r#"
+    package com.android.aconfig.test;
+    public interface FeatureFlags {
+        boolean disabledRo();
+        boolean disabledRw();
+        boolean enabledRo();
+        boolean enabledRw();
+    }"#;
 
-            public static boolean disabledRo() {
-                return FEATURE_FLAGS.disabledRo();
-            }
-            public static boolean disabledRw() {
-                return FEATURE_FLAGS.disabledRw();
-            }
-            public static boolean enabledRo() {
-                return FEATURE_FLAGS.enabledRo();
-            }
-            public static boolean enabledRw() {
-                return FEATURE_FLAGS.enabledRw();
-            }
-            private static FeatureFlags FEATURE_FLAGS = new FeatureFlagsImpl();
+    const EXPECTED_FLAG_COMMON_CONTENT: &str = r#"
+    package com.android.aconfig.test;
+    public final class Flags {
+        public static final String FLAG_DISABLED_RO = "com.android.aconfig.test.disabled_ro";
+        public static final String FLAG_DISABLED_RW = "com.android.aconfig.test.disabled_rw";
+        public static final String FLAG_ENABLED_RO = "com.android.aconfig.test.enabled_ro";
+        public static final String FLAG_ENABLED_RW = "com.android.aconfig.test.enabled_rw";
+
+        public static boolean disabledRo() {
+            return FEATURE_FLAGS.disabledRo();
         }
-        "#;
+        public static boolean disabledRw() {
+            return FEATURE_FLAGS.disabledRw();
+        }
+        public static boolean enabledRo() {
+            return FEATURE_FLAGS.enabledRo();
+        }
+        public static boolean enabledRw() {
+            return FEATURE_FLAGS.enabledRw();
+        }
+    "#;
+
+    #[test]
+    fn test_generate_java_code_production() {
+        let parsed_flags = crate::test::parse_test_flags();
+        let generated_files = generate_java_code(
+            crate::test::TEST_PACKAGE,
+            parsed_flags.parsed_flag.iter(),
+            CodegenMode::Production,
+        )
+        .unwrap();
+        let expect_flags_content = EXPECTED_FLAG_COMMON_CONTENT.to_string()
+            + r#"
+            private static FeatureFlags FEATURE_FLAGS = new FeatureFlagsImpl();
+        }"#;
         let expected_featureflagsimpl_content = r#"
         package com.android.aconfig.test;
         import android.provider.DeviceConfig;
@@ -167,19 +186,102 @@
             }
         }
         "#;
-        let expected_featureflags_content = r#"
+        let mut file_set = HashMap::from([
+            ("com/android/aconfig/test/Flags.java", expect_flags_content.as_str()),
+            ("com/android/aconfig/test/FeatureFlagsImpl.java", expected_featureflagsimpl_content),
+            ("com/android/aconfig/test/FeatureFlags.java", EXPECTED_FEATUREFLAGS_CONTENT),
+        ]);
+
+        for file in generated_files {
+            let file_path = file.path.to_str().unwrap();
+            assert!(file_set.contains_key(file_path), "Cannot find {}", file_path);
+            assert_eq!(
+                None,
+                crate::test::first_significant_code_diff(
+                    file_set.get(file_path).unwrap(),
+                    &String::from_utf8(file.contents.clone()).unwrap()
+                ),
+                "File {} content is not correct",
+                file_path
+            );
+            file_set.remove(file_path);
+        }
+
+        assert!(file_set.is_empty());
+    }
+
+    #[test]
+    fn test_generate_java_code_test() {
+        let parsed_flags = crate::test::parse_test_flags();
+        let generated_files = generate_java_code(
+            crate::test::TEST_PACKAGE,
+            parsed_flags.parsed_flag.iter(),
+            CodegenMode::Test,
+        )
+        .unwrap();
+        let expect_flags_content = EXPECTED_FLAG_COMMON_CONTENT.to_string()
+            + r#"
+            public static void setFeatureFlagsImpl(FeatureFlags featureFlags) {
+                Flags.FEATURE_FLAGS = featureFlags;
+            }
+            public static void unsetFeatureFlagsImpl() {
+                Flags.FEATURE_FLAGS = null;
+            }
+            private static FeatureFlags FEATURE_FLAGS;
+        }
+        "#;
+        let expected_featureflagsimpl_content = r#"
         package com.android.aconfig.test;
-        public interface FeatureFlags {
-            boolean disabledRo();
-            boolean disabledRw();
-            boolean enabledRo();
-            boolean enabledRw();
+        import static java.util.stream.Collectors.toMap;
+        import java.util.stream.Stream;
+        import java.util.HashMap;
+        public final class FeatureFlagsImpl implements FeatureFlags {
+            @Override
+            public boolean disabledRo() {
+                return getFlag(Flags.FLAG_DISABLED_RO);
+            }
+            @Override
+            public boolean disabledRw() {
+                return getFlag(Flags.FLAG_DISABLED_RW);
+            }
+            @Override
+            public boolean enabledRo() {
+                return getFlag(Flags.FLAG_ENABLED_RO);
+            }
+            @Override
+            public boolean enabledRw() {
+                return getFlag(Flags.FLAG_ENABLED_RW);
+            }
+            public void setFlag(String flagName, boolean value) {
+                if (!this.mFlagMap.containsKey(flagName)) {
+                    throw new IllegalArgumentException("no such flag" + flagName);
+                }
+                this.mFlagMap.put(flagName, value);
+            }
+            private boolean getFlag(String flagName) {
+                Boolean value = this.mFlagMap.get(flagName);
+                if (value == null) {
+                    throw new IllegalArgumentException(flagName + " is not set");
+                }
+                return value;
+            }
+            private HashMap<String, Boolean> mFlagMap = Stream.of(
+                    Flags.FLAG_DISABLED_RO,
+                    Flags.FLAG_DISABLED_RW,
+                    Flags.FLAG_ENABLED_RO,
+                    Flags.FLAG_ENABLED_RW
+                )
+                .collect(
+                    HashMap::new,
+                    (map, elem) -> map.put(elem, null),
+                    HashMap::putAll
+                );
         }
         "#;
         let mut file_set = HashMap::from([
-            ("com/android/aconfig/test/Flags.java", expect_flags_content),
+            ("com/android/aconfig/test/Flags.java", expect_flags_content.as_str()),
             ("com/android/aconfig/test/FeatureFlagsImpl.java", expected_featureflagsimpl_content),
-            ("com/android/aconfig/test/FeatureFlags.java", expected_featureflags_content),
+            ("com/android/aconfig/test/FeatureFlags.java", EXPECTED_FEATUREFLAGS_CONTENT),
         ]);
 
         for file in generated_files {
diff --git a/tools/aconfig/src/commands.rs b/tools/aconfig/src/commands.rs
index 58831cc..dd2087b 100644
--- a/tools/aconfig/src/commands.rs
+++ b/tools/aconfig/src/commands.rs
@@ -129,12 +129,18 @@
     Ok(output)
 }
 
-pub fn create_java_lib(mut input: Input) -> Result<Vec<OutputFile>> {
+#[derive(Copy, Clone, Debug, PartialEq, Eq, ValueEnum)]
+pub enum CodegenMode {
+    Production,
+    Test,
+}
+
+pub fn create_java_lib(mut input: Input, codegen_mode: CodegenMode) -> Result<Vec<OutputFile>> {
     let parsed_flags = input.try_parse_flags()?;
     let Some(package) = find_unique_package(&parsed_flags) else {
         bail!("no parsed flags, or the parsed flags use different packages");
     };
-    generate_java_code(package, parsed_flags.parsed_flag.iter())
+    generate_java_code(package, parsed_flags.parsed_flag.iter(), codegen_mode)
 }
 
 pub fn create_cpp_lib(mut input: Input) -> Result<OutputFile> {
diff --git a/tools/aconfig/src/main.rs b/tools/aconfig/src/main.rs
index e6a325d..e20c60c 100644
--- a/tools/aconfig/src/main.rs
+++ b/tools/aconfig/src/main.rs
@@ -34,7 +34,7 @@
 #[cfg(test)]
 mod test;
 
-use commands::{DumpFormat, Input, OutputFile};
+use commands::{CodegenMode, DumpFormat, Input, OutputFile};
 
 fn cli() -> Command {
     Command::new("aconfig")
@@ -49,7 +49,13 @@
         .subcommand(
             Command::new("create-java-lib")
                 .arg(Arg::new("cache").long("cache").required(true))
-                .arg(Arg::new("out").long("out").required(true)),
+                .arg(Arg::new("out").long("out").required(true))
+                .arg(
+                    Arg::new("mode")
+                        .long("mode")
+                        .value_parser(EnumValueParser::<commands::CodegenMode>::new())
+                        .default_value("production"),
+                ),
         )
         .subcommand(
             Command::new("create-cpp-lib")
@@ -148,7 +154,8 @@
         }
         Some(("create-java-lib", sub_matches)) => {
             let cache = open_single_file(sub_matches, "cache")?;
-            let generated_files = commands::create_java_lib(cache)?;
+            let mode = get_required_arg::<CodegenMode>(sub_matches, "mode")?;
+            let generated_files = commands::create_java_lib(cache, *mode)?;
             let dir = PathBuf::from(get_required_arg::<String>(sub_matches, "out")?);
             generated_files
                 .iter()
diff --git a/tools/aconfig/templates/FeatureFlagsImpl.java.template b/tools/aconfig/templates/FeatureFlagsImpl.java.template
index 2b031f1..dafd99e 100644
--- a/tools/aconfig/templates/FeatureFlagsImpl.java.template
+++ b/tools/aconfig/templates/FeatureFlagsImpl.java.template
@@ -1,20 +1,61 @@
 package {package_name};
-{{ if is_read_write }}
+{{ -if is_test_mode }}
+import static java.util.stream.Collectors.toMap;
+
+import java.util.stream.Stream;
+import java.util.HashMap;
+{{ else}}
+{{ if is_read_write- }}
 import android.provider.DeviceConfig;
+{{ -endif- }}
 {{ endif }}
 public final class FeatureFlagsImpl implements FeatureFlags \{
     {{ for item in class_elements}}
     @Override
     public boolean {item.method_name}() \{
-        {{ if item.is_read_write- }}
+        {{ -if not is_test_mode- }}
+        {{ if item.is_read_write }}
         return DeviceConfig.getBoolean(
             "{item.device_config_namespace}",
             "{item.device_config_flag}",
             {item.default_value}
         );
-        {{ -else- }}
+        {{ else }}
         return {item.default_value};
+        {{ -endif- }}
+        {{ else }}
+        return getFlag(Flags.FLAG_{item.flag_name_constant_suffix});
         {{ -endif }}
     }
     {{ endfor }}
-}
\ No newline at end of file
+
+    {{ if is_test_mode- }}
+    public void setFlag(String flagName, boolean value) \{
+        if (!this.mFlagMap.containsKey(flagName)) \{
+            throw new IllegalArgumentException("no such flag" + flagName);
+        }
+        this.mFlagMap.put(flagName, value);
+    }
+
+    private boolean getFlag(String flagName) \{
+        Boolean value = this.mFlagMap.get(flagName);
+        if (value == null) \{
+            throw new IllegalArgumentException(flagName + " is not set");
+        }
+        return value;
+    }
+
+    private HashMap<String, Boolean> mFlagMap = Stream.of(
+            {{-for item in class_elements}}
+            Flags.FLAG_{item.flag_name_constant_suffix}{{ if not @last }},{{ endif }}
+            {{ -endfor }}
+        )
+        .collect(
+            HashMap::new,
+            (map, elem) -> map.put(elem, null),
+            HashMap::putAll
+        );
+    {{ -endif }}
+}
+
+
diff --git a/tools/aconfig/templates/Flags.java.template b/tools/aconfig/templates/Flags.java.template
index 62116c5..eef98eb 100644
--- a/tools/aconfig/templates/Flags.java.template
+++ b/tools/aconfig/templates/Flags.java.template
@@ -9,6 +9,16 @@
         return FEATURE_FLAGS.{item.method_name}();
     }
     {{ endfor }}
-    private static FeatureFlags FEATURE_FLAGS = new FeatureFlagsImpl();
+    {{ if is_test_mode }}
+    public static void setFeatureFlagsImpl(FeatureFlags featureFlags) \{
+        Flags.FEATURE_FLAGS = featureFlags;
+    }
+
+    public static void unsetFeatureFlagsImpl() \{
+        Flags.FEATURE_FLAGS = null;
+    }
+    {{ -endif}}
+
+    private static FeatureFlags FEATURE_FLAGS{{ -if not is_test_mode }} = new FeatureFlagsImpl(){{ -endif- }};
 
 }
diff --git a/tools/releasetools/Android.bp b/tools/releasetools/Android.bp
index a76dc8a..7a2dcb7 100644
--- a/tools/releasetools/Android.bp
+++ b/tools/releasetools/Android.bp
@@ -99,9 +99,8 @@
         "releasetools_common",
     ],
     required: [
+        "apexd_host",
         "checkvintf",
-        "deapexer",
-        "dump_apex_info",
     ],
 }
 
diff --git a/tools/releasetools/check_target_files_vintf.py b/tools/releasetools/check_target_files_vintf.py
index 906ad1f..33624f5 100755
--- a/tools/releasetools/check_target_files_vintf.py
+++ b/tools/releasetools/check_target_files_vintf.py
@@ -129,8 +129,9 @@
 
   dirmap = GetDirmap(input_tmp)
 
-  # Simulate apexd from target-files.
-  dirmap['/apex'] = PrepareApexDirectory(input_tmp)
+  # Simulate apexd with target-files.
+  # add a mapping('/apex' => ${input_tmp}/APEX) to dirmap
+  PrepareApexDirectory(input_tmp, dirmap)
 
   args_for_skus = GetArgsForSkus(info_dict)
   shipping_api_level_args = GetArgsForShippingApiLevel(info_dict)
@@ -204,7 +205,8 @@
 
   return patterns
 
-def PrepareApexDirectory(inp):
+
+def PrepareApexDirectory(inp, dirmap):
   """ Prepare /apex directory before running checkvintf
 
   Apex binaries do not support dirmaps, in order to use these binaries we
@@ -212,93 +214,25 @@
   expected device locations.
 
   This simulates how apexd activates APEXes.
-  1. create {inp}/APEX which is treated as a "/" on device.
-  2. copy apexes from target-files to {root}/{partition}/apex.
-  3. mount apexes under {root}/{partition}/apex at {root}/apex.
-  4. generate info files with dump_apex_info.
-
-  We'll get the following layout
-       {inp}/APEX/apex             # Activated APEXes + some info files
-       {inp}/APEX/system/apex      # System APEXes
-       {inp}/APEX/vendor/apex      # Vendor APEXes
-       ...
-
-  Args:
-    inp: path to the directory that contains the extracted target files archive.
-
-  Returns:
-    directory representing /apex on device
+  1. create {inp}/APEX which is treated as a "/apex" on device.
+  2. invoke apexd_host with vendor APEXes.
   """
 
-  deapexer = 'deapexer'
-  debugfs_path = 'debugfs'
-  fsckerofs_path = 'fsck.erofs'
-  if OPTIONS.search_path:
-    debugfs_path = os.path.join(OPTIONS.search_path, 'bin', 'debugfs_static')
-    deapexer_path = os.path.join(OPTIONS.search_path, 'bin', 'deapexer')
-    fsckerofs_path = os.path.join(OPTIONS.search_path, 'bin', 'fsck.erofs')
-    if os.path.isfile(deapexer_path):
-      deapexer = deapexer_path
-
-  def ExtractApexes(path, outp):
-    # Extract all APEXes found in input path.
-    logger.info('Extracting APEXs in %s', path)
-    for f in os.listdir(path):
-      logger.info('  adding APEX %s', os.path.basename(f))
-      apex = os.path.join(path, f)
-      if os.path.isdir(apex) and os.path.isfile(os.path.join(apex, 'apex_manifest.pb')):
-        info = ParseApexManifest(os.path.join(apex, 'apex_manifest.pb'))
-        # Flattened APEXes may have symlinks for libs (linked to /system/lib)
-        # We need to blindly copy them all.
-        shutil.copytree(apex, os.path.join(outp, info.name), symlinks=True)
-      elif os.path.isfile(apex) and apex.endswith(('.apex', '.capex')):
-        cmd = [deapexer,
-               '--debugfs_path', debugfs_path,
-               'info',
-               apex]
-        info = json.loads(common.RunAndCheckOutput(cmd))
-
-        cmd = [deapexer,
-               '--debugfs_path', debugfs_path,
-               '--fsckerofs_path', fsckerofs_path,
-               'extract',
-               apex,
-               os.path.join(outp, info['name'])]
-        common.RunAndCheckOutput(cmd)
-      else:
-        logger.info('  .. skipping %s (is it APEX?)', path)
-
-  root_dir_name = 'APEX'
-  root_dir = os.path.join(inp, root_dir_name)
-  extracted_root = os.path.join(root_dir, 'apex')
+  apex_dir = os.path.join(inp, 'APEX')
+  # checkvintf needs /apex dirmap
+  dirmap['/apex'] = apex_dir
 
   # Always create /apex directory for dirmap
-  os.makedirs(extracted_root)
+  os.makedirs(apex_dir)
 
-  create_info_file = False
+  # Invoke apexd_host to activate vendor APEXes for checkvintf
+  apex_host = os.path.join(OPTIONS.search_path, 'bin', 'apexd_host')
+  cmd = [apex_host, '--tool_path', OPTIONS.search_path]
+  cmd += ['--apex_path', dirmap['/apex']]
+  if '/vendor' in dirmap:
+      cmd += ['--vendor_path', dirmap['/vendor']]
+  common.RunAndCheckOutput(cmd)
 
-  # Loop through search path looking for and processing apex/ directories.
-  for device_path, target_files_rel_paths in DIR_SEARCH_PATHS.items():
-    # checkvintf only needs vendor apexes. skip other partitions for efficiency
-    if device_path not in ['/vendor', '/odm']:
-      continue
-    # First, copy VENDOR/apex/foo.apex to APEX/vendor/apex/foo.apex
-    # Then, extract the contents to APEX/apex/foo/
-    for target_files_rel_path in target_files_rel_paths:
-      inp_partition = os.path.join(inp, target_files_rel_path,"apex")
-      if os.path.exists(inp_partition):
-        apex_dir = root_dir + os.path.join(device_path + "/apex");
-        os.makedirs(root_dir + device_path)
-        shutil.copytree(inp_partition, apex_dir, symlinks=True)
-        ExtractApexes(apex_dir, extracted_root)
-        create_info_file = True
-
-  if create_info_file:
-    ### Dump apex info files
-    dump_cmd = ['dump_apex_info', '--root_dir', root_dir]
-    common.RunAndCheckOutput(dump_cmd)
-
-  return extracted_root
 
 def CheckVintfFromTargetFiles(inp, info_dict=None):
   """
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index 39e1bdd..091121f 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -2451,12 +2451,22 @@
   try:
     return int(version)
   except ValueError:
-    # Not a decimal number. Codename?
-    if version in codename_to_api_level_map:
-      return codename_to_api_level_map[version]
+    # Not a decimal number.
+    #
+    # It could be either a straight codename, e.g.
+    #     UpsideDownCake
+    #
+    # Or a codename with API fingerprint SHA, e.g.
+    #     UpsideDownCake.e7d3947f14eb9dc4fec25ff6c5f8563e
+    #
+    # Extract the codename and try and map it to a version number.
+    split = version.split(".")
+    codename = split[0]
+    if codename in codename_to_api_level_map:
+      return codename_to_api_level_map[codename]
     raise ExternalError(
-        "Unknown minSdkVersion: '{}'. Known codenames: {}".format(
-            version, codename_to_api_level_map))
+        "Unknown codename: '{}' from minSdkVersion: '{}'. Known codenames: {}".format(
+            codename, version, codename_to_api_level_map))
 
 
 def SignFile(input_name, output_name, key, password, min_api_level=None,
diff --git a/tools/releasetools/merge/Android.bp b/tools/releasetools/merge/Android.bp
index 219acf8..96ec73e 100644
--- a/tools/releasetools/merge/Android.bp
+++ b/tools/releasetools/merge/Android.bp
@@ -50,6 +50,7 @@
         "releasetools_ota_from_target_files",
     ],
     required: [
+        "apexd_host",
         "checkvintf",
         "host_init_verifier",
         "secilc",
diff --git a/tools/releasetools/merge/merge_dexopt.py b/tools/releasetools/merge/merge_dexopt.py
index 1dd591b..1c0c743 100644
--- a/tools/releasetools/merge/merge_dexopt.py
+++ b/tools/releasetools/merge/merge_dexopt.py
@@ -72,7 +72,6 @@
   #         <contents of vendor dexpreopt_config.zip>
   #     system -> output/SYSTEM
   #     vendor -> output/VENDOR
-  #     apex -> output/SYSTEM/apex (only for flattened APEX builds)
   #     apex/ (extracted updatable APEX)
   #         <apex 1>/
   #             ...
@@ -114,68 +113,20 @@
       os.path.join(output_target_files_dir, 'VENDOR'),
       os.path.join(temp_dir, 'vendor'))
 
-  # The directory structure for flatteded APEXes is:
-  #
-  # SYSTEM
-  #     apex
-  #         <APEX name, e.g., com.android.wifi>
-  #             apex_manifest.pb
-  #             apex_pubkey
-  #             etc/
-  #             javalib/
-  #             lib/
-  #             lib64/
-  #             priv-app/
-  #
-  # The directory structure for updatable APEXes is:
-  #
-  # SYSTEM
-  #     apex
-  #         com.android.adbd.apex
-  #         com.android.appsearch.apex
-  #         com.android.art.apex
-  #         ...
-  apex_root = os.path.join(output_target_files_dir, 'SYSTEM', 'apex')
+  # Extract APEX.
+  logging.info('extracting APEX')
+  apex_extract_root_dir = os.path.join(temp_dir, 'apex')
+  os.makedirs(apex_extract_root_dir)
 
-  # Check for flattended versus updatable APEX.
-  if OPTIONS.framework_misc_info.get('target_flatten_apex') == 'false':
-    # Extract APEX.
-    logging.info('extracting APEX')
-
-    apex_extract_root_dir = os.path.join(temp_dir, 'apex')
-    os.makedirs(apex_extract_root_dir)
-
-    for apex in (glob.glob(os.path.join(apex_root, '*.apex')) +
-                 glob.glob(os.path.join(apex_root, '*.capex'))):
-      logging.info('  apex: %s', apex)
-      # deapexer is in the same directory as the merge_target_files binary extracted
-      # from otatools.zip.
-      apex_json_info = subprocess.check_output(['deapexer', 'info', apex])
-      logging.info('    info: %s', apex_json_info)
-      apex_info = json.loads(apex_json_info)
-      apex_name = apex_info['name']
-      logging.info('    name: %s', apex_name)
-
-      apex_extract_dir = os.path.join(apex_extract_root_dir, apex_name)
-      os.makedirs(apex_extract_dir)
-
-      # deapexer uses debugfs_static, which is part of otatools.zip.
-      command = [
-          'deapexer',
-          '--debugfs_path',
-          'debugfs_static',
-          '--fsckerofs_path',
-          'fsck.erofs',
-          'extract',
-          apex,
-          apex_extract_dir,
-      ]
-      logging.info('    running %s', command)
-      subprocess.check_call(command)
-  else:
-    # Flattened APEXes don't need to be extracted since they have the necessary
-    # directory structure.
-    os.symlink(os.path.join(apex_root), os.path.join(temp_dir, 'apex'))
+  command = [
+      'apexd_host',
+      '--system_path',
+      os.path.join(temp_dir, 'system'),
+      '--apex_path',
+      apex_extract_root_dir,
+  ]
+  logging.info('    running %s', command)
+  subprocess.check_call(command)
 
   # Modify system config to point to the tools that have been extracted.
   # Absolute or .. paths are not allowed  by the dexpreopt_gen tool in