Merge tag 'android-11.0.0_r48' into int/11/fp3

Android 11.0.0 Release 48 (RD2A.211001.002)

* tag 'android-11.0.0_r48':

Change-Id: Ifd87537fe1386f6acc0f68aaddfe240265ea155c
diff --git a/android/arch.go b/android/arch.go
index 18ff13e..ad91c7e 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -127,6 +127,7 @@
 		"cortex-a76",
 		"krait",
 		"kryo",
+		"kryo300",
 		"kryo385",
 		"exynos-m1",
 		"exynos-m2",
@@ -141,6 +142,7 @@
 		"cortex-a75",
 		"cortex-a76",
 		"kryo",
+		"kryo300",
 		"kryo385",
 		"exynos-m1",
 		"exynos-m2",
@@ -1625,6 +1627,7 @@
 		{"arm", "armv7-a-neon", "cortex-a76", []string{"armeabi-v7a"}},
 		{"arm", "armv7-a-neon", "krait", []string{"armeabi-v7a"}},
 		{"arm", "armv7-a-neon", "kryo", []string{"armeabi-v7a"}},
+		{"arm", "armv7-a-neon", "kryo300", []string{"armeabi-v7a"}},
 		{"arm", "armv7-a-neon", "kryo385", []string{"armeabi-v7a"}},
 		{"arm", "armv7-a-neon", "exynos-m1", []string{"armeabi-v7a"}},
 		{"arm", "armv7-a-neon", "exynos-m2", []string{"armeabi-v7a"}},
@@ -1636,6 +1639,7 @@
 		{"arm64", "armv8-a", "exynos-m2", []string{"arm64-v8a"}},
 		{"arm64", "armv8-2a", "cortex-a75", []string{"arm64-v8a"}},
 		{"arm64", "armv8-2a", "cortex-a76", []string{"arm64-v8a"}},
+		{"arm64", "armv8-2a", "kryo300", []string{"arm64-v8a"}},
 		{"arm64", "armv8-2a", "kryo385", []string{"arm64-v8a"}},
 		{"mips", "mips32-fp", "", []string{"mips"}},
 		{"mips", "mips32r2-fp", "", []string{"mips"}},
diff --git a/android/config.go b/android/config.go
index 3417729..28d9de9 100644
--- a/android/config.go
+++ b/android/config.go
@@ -36,6 +36,9 @@
 var Bool = proptools.Bool
 var String = proptools.String
 
+// StringDefault re-exports proptools.StringDefault for the android package.
+var StringDefault = proptools.StringDefault
+
 const FutureApiLevel = 10000
 
 // The configuration file name
@@ -964,6 +967,10 @@
 	return c.multilibConflicts[arch]
 }
 
+func (c *config) PrebuiltHiddenApiDir(ctx PathContext) string {
+	return String(c.productVariables.PrebuiltHiddenApiDir)
+}
+
 func (c *deviceConfig) Arches() []Arch {
 	var arches []Arch
 	for _, target := range c.config.Targets[Android] {
@@ -991,6 +998,14 @@
 	return String(c.config.productVariables.DeviceVndkVersion)
 }
 
+func (c *deviceConfig) RecoverySnapshotVersion() string {
+	return String(c.config.productVariables.RecoverySnapshotVersion)
+}
+
+func (c *deviceConfig) RamdiskSnapshotVersion() string {
+	return String(c.config.productVariables.RamdiskSnapshotVersion)
+}
+
 func (c *deviceConfig) PlatformVndkVersion() string {
 	return String(c.config.productVariables.Platform_vndk_version)
 }
@@ -1172,6 +1187,27 @@
 	return HasAnyPrefix(path, c.productVariables.IntegerOverflowExcludePaths)
 }
 
+func (c *config) IntegerOverflowEnabledForPath(path string) bool {
+	if c.productVariables.IntegerOverflowIncludePaths == nil {
+		return false
+	}
+	return HasAnyPrefix(path, c.productVariables.IntegerOverflowIncludePaths)
+}
+
+func (c *config) BoundSanitizerEnabledForPath(path string) bool {
+	if c.productVariables.BoundSanitizerIncludePaths == nil {
+		return false
+	}
+	return HasAnyPrefix(path, c.productVariables.BoundSanitizerIncludePaths)
+}
+
+func (c *config) BoundSanitizerDisabledForPath(path string) bool {
+	if c.productVariables.BoundSanitizerExcludePaths == nil {
+		return false
+	}
+	return HasAnyPrefix(path, c.productVariables.BoundSanitizerExcludePaths)
+}
+
 func (c *config) CFIDisabledForPath(path string) bool {
 	if c.productVariables.CFIExcludePaths == nil {
 		return false
@@ -1277,3 +1313,83 @@
 func (c *deviceConfig) BoardUsesRecoveryAsBoot() bool {
 	return Bool(c.config.productVariables.BoardUsesRecoveryAsBoot)
 }
+
+func (c *deviceConfig) DirectedVendorSnapshot() bool {
+	return c.config.productVariables.DirectedVendorSnapshot
+}
+
+func (c *deviceConfig) VendorSnapshotModules() map[string]bool {
+	return c.config.productVariables.VendorSnapshotModules
+}
+
+func (c *deviceConfig) DirectedRecoverySnapshot() bool {
+	return c.config.productVariables.DirectedRecoverySnapshot
+}
+
+func (c *deviceConfig) RecoverySnapshotModules() map[string]bool {
+	return c.config.productVariables.RecoverySnapshotModules
+}
+
+func (c *deviceConfig) DirectedRamdiskSnapshot() bool {
+	return c.config.productVariables.DirectedRamdiskSnapshot
+}
+
+func (c *deviceConfig) RamdiskSnapshotModules() map[string]bool {
+	return c.config.productVariables.RamdiskSnapshotModules
+}
+
+func createDirsMap(previous map[string]bool, dirs []string) (map[string]bool, error) {
+	var ret = make(map[string]bool)
+	for _, dir := range dirs {
+		clean := filepath.Clean(dir)
+		if previous[clean] || ret[clean] {
+			return nil, fmt.Errorf("Duplicate entry %s", dir)
+		}
+		ret[clean] = true
+	}
+	return ret, nil
+}
+
+func (c *deviceConfig) createDirsMapOnce(onceKey OnceKey, previous map[string]bool, dirs []string) map[string]bool {
+	dirMap := c.Once(onceKey, func() interface{} {
+		ret, err := createDirsMap(previous, dirs)
+		if err != nil {
+			panic(fmt.Errorf("%s: %w", *onceKey.key.(*string), err))
+		}
+		return ret
+	})
+	if dirMap == nil {
+		return nil
+	}
+	return dirMap.(map[string]bool)
+}
+
+var vendorSnapshotDirsExcludedKey = NewOnceKey("VendorSnapshotDirsExcludedMap")
+
+func (c *deviceConfig) VendorSnapshotDirsExcludedMap() map[string]bool {
+	return c.createDirsMapOnce(vendorSnapshotDirsExcludedKey, nil,
+		c.config.productVariables.VendorSnapshotDirsExcluded)
+}
+
+var vendorSnapshotDirsIncludedKey = NewOnceKey("VendorSnapshotDirsIncludedMap")
+
+func (c *deviceConfig) VendorSnapshotDirsIncludedMap() map[string]bool {
+	excludedMap := c.VendorSnapshotDirsExcludedMap()
+	return c.createDirsMapOnce(vendorSnapshotDirsIncludedKey, excludedMap,
+		c.config.productVariables.VendorSnapshotDirsIncluded)
+}
+
+var recoverySnapshotDirsExcludedKey = NewOnceKey("RecoverySnapshotDirsExcludedMap")
+
+func (c *deviceConfig) RecoverySnapshotDirsExcludedMap() map[string]bool {
+	return c.createDirsMapOnce(recoverySnapshotDirsExcludedKey, nil,
+		c.config.productVariables.RecoverySnapshotDirsExcluded)
+}
+
+var recoverySnapshotDirsIncludedKey = NewOnceKey("RecoverySnapshotDirsIncludedMap")
+
+func (c *deviceConfig) RecoverySnapshotDirsIncludedMap() map[string]bool {
+	excludedMap := c.RecoverySnapshotDirsExcludedMap()
+	return c.createDirsMapOnce(recoverySnapshotDirsIncludedKey, excludedMap,
+		c.config.productVariables.RecoverySnapshotDirsIncluded)
+}
diff --git a/android/env.go b/android/env.go
index 46bd3d6..59e0887 100644
--- a/android/env.go
+++ b/android/env.go
@@ -32,6 +32,7 @@
 var originalEnv map[string]string
 var SoongDelveListen string
 var SoongDelvePath string
+var SdclangEnv map[string]string
 
 func init() {
 	// Delve support needs to read this environment variable very early, before NewConfig has created a way to
@@ -41,10 +42,12 @@
 	SoongDelvePath, _ = exec.LookPath("dlv")
 
 	originalEnv = make(map[string]string)
+	SdclangEnv = make(map[string]string)
 	for _, env := range os.Environ() {
 		idx := strings.IndexRune(env, '=')
 		if idx != -1 {
 			originalEnv[env[:idx]] = env[idx+1:]
+			SdclangEnv[env[:idx]] = env[idx+1:]
 		}
 	}
 	// Clear the environment to prevent use of os.Getenv(), which would not provide dependencies on environment
diff --git a/android/module.go b/android/module.go
index a498839..8809153 100644
--- a/android/module.go
+++ b/android/module.go
@@ -231,6 +231,8 @@
 	InstallBypassMake() bool
 	SkipInstall()
 	IsSkipInstall() bool
+	ReplacedByPrebuilt()
+	IsReplacedByPrebuilt() bool
 	ExportedToMake() bool
 	InitRc() Paths
 	VintfFragments() Paths
@@ -512,6 +514,12 @@
 
 	SkipInstall bool `blueprint:"mutated"`
 
+	// Whether the module has been replaced by a prebuilt
+	ReplacedByPrebuilt bool `blueprint:"mutated"`
+
+	// Disabled by mutators. If set to true, it overrides Enabled property.
+	ForcedDisabled bool `blueprint:"mutated"`
+
 	NamespaceExportedToMake bool `blueprint:"mutated"`
 
 	MissingDeps []string `blueprint:"mutated"`
@@ -941,6 +949,9 @@
 }
 
 func (m *ModuleBase) Enabled() bool {
+	if m.commonProperties.ForcedDisabled {
+		return false
+	}
 	if m.commonProperties.Enabled == nil {
 		return !m.Os().DefaultDisabled
 	}
@@ -948,7 +959,7 @@
 }
 
 func (m *ModuleBase) Disable() {
-	m.commonProperties.Enabled = proptools.BoolPtr(false)
+	m.commonProperties.ForcedDisabled = true
 }
 
 func (m *ModuleBase) SkipInstall() {
@@ -959,6 +970,15 @@
 	return m.commonProperties.SkipInstall == true
 }
 
+func (m *ModuleBase) ReplacedByPrebuilt() {
+	m.commonProperties.ReplacedByPrebuilt = true
+	m.SkipInstall()
+}
+
+func (m *ModuleBase) IsReplacedByPrebuilt() bool {
+	return m.commonProperties.ReplacedByPrebuilt
+}
+
 func (m *ModuleBase) ExportedToMake() bool {
 	return m.commonProperties.NamespaceExportedToMake
 }
diff --git a/android/prebuilt.go b/android/prebuilt.go
index ee4a13a..8ad81e6 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -227,7 +227,7 @@
 			p := m.(PrebuiltInterface).Prebuilt()
 			if p.usePrebuilt(ctx, s) {
 				p.properties.UsePrebuilt = true
-				s.SkipInstall()
+				s.ReplacedByPrebuilt()
 			}
 		})
 	}
diff --git a/android/variable.go b/android/variable.go
index 983c235..34d145e 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -131,6 +131,34 @@
 			Srcs         []string `android:"arch_variant"`
 			Exclude_srcs []string `android:"arch_variant"`
 		} `android:"arch_variant"`
+
+		Real_hal struct {
+			Cflags       []string
+			Exclude_srcs []string
+			Include_dirs []string
+			Shared_libs  []string
+			Static_libs  []string
+			Srcs         []string
+		}
+
+		Qmaa_hal struct {
+			Cflags       []string
+			Exclude_srcs []string
+			Include_dirs []string
+			Shared_libs  []string
+			Static_libs  []string
+			Srcs         []string
+		}
+
+		Device_support_hwfde struct {
+			Cflags      []string
+			Header_libs []string
+			Shared_libs []string
+		}
+
+		Device_support_hwfde_perf struct {
+			Cflags []string
+		}
 	} `android:"arch_variant"`
 }
 
@@ -163,6 +191,9 @@
 	DeviceVndkVersion       *string  `json:",omitempty"`
 	DeviceSystemSdkVersions []string `json:",omitempty"`
 
+	RecoverySnapshotVersion *string `json:",omitempty"`
+	RamdiskSnapshotVersion  *string `json:",omitempty"`
+
 	DeviceSecondaryArch        *string  `json:",omitempty"`
 	DeviceSecondaryArchVariant *string  `json:",omitempty"`
 	DeviceSecondaryCpuVariant  *string  `json:",omitempty"`
@@ -203,6 +234,10 @@
 
 	AppsDefaultVersionName *string `json:",omitempty"`
 
+	Real_hal                         *bool `json:",omitempty"`
+	Qmaa_hal                         *bool `json:",omitempty"`
+	Device_support_hwfde             *bool `json:",omitempty"`
+	Device_support_hwfde_perf        *bool `json:",omitempty"`
 	Allow_missing_dependencies       *bool `json:",omitempty"`
 	Unbundled_build                  *bool `json:",omitempty"`
 	Unbundled_build_sdks_from_source *bool `json:",omitempty"`
@@ -234,6 +269,10 @@
 	UpdatableBootJars []string `json:",omitempty"`
 
 	IntegerOverflowExcludePaths []string `json:",omitempty"`
+	IntegerOverflowIncludePaths []string `json:",omitempty"`
+
+	BoundSanitizerExcludePaths []string `json:",omitempty"`
+	BoundSanitizerIncludePaths []string `json:",omitempty"`
 
 	EnableCFI       *bool    `json:",omitempty"`
 	CFIExcludePaths []string `json:",omitempty"`
@@ -292,6 +331,20 @@
 	VndkUseCoreVariant         *bool `json:",omitempty"`
 	VndkSnapshotBuildArtifacts *bool `json:",omitempty"`
 
+	DirectedVendorSnapshot bool            `json:",omitempty"`
+	VendorSnapshotModules  map[string]bool `json:",omitempty"`
+
+	DirectedRecoverySnapshot bool            `json:",omitempty"`
+	RecoverySnapshotModules  map[string]bool `json:",omitempty"`
+
+	DirectedRamdiskSnapshot bool            `json:",omitempty"`
+	RamdiskSnapshotModules  map[string]bool `json:",omitempty"`
+
+	VendorSnapshotDirsIncluded   []string `json:",omitempty"`
+	VendorSnapshotDirsExcluded   []string `json:",omitempty"`
+	RecoverySnapshotDirsExcluded []string `json:",omitempty"`
+	RecoverySnapshotDirsIncluded []string `json:",omitempty"`
+
 	BoardVendorSepolicyDirs      []string `json:",omitempty"`
 	BoardOdmSepolicyDirs         []string `json:",omitempty"`
 	BoardPlatPublicSepolicyDirs  []string `json:",omitempty"`
@@ -336,6 +389,8 @@
 	InstallExtraFlattenedApexes *bool `json:",omitempty"`
 
 	BoardUsesRecoveryAsBoot *bool `json:",omitempty"`
+
+	PrebuiltHiddenApiDir *string `json:",omitempty"`
 }
 
 func boolPtr(v bool) *bool {
diff --git a/apex/apex.go b/apex/apex.go
index 7da8e1c..3c07903 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -627,6 +627,13 @@
 		"android.hidl.manager-V1.0-java",
 		"android.hidl.manager-V1.1-java",
 		"android.hidl.manager-V1.2-java",
+		"vendor.qti.hardware.wifi.hostapd-V1.0-java",
+		"vendor.qti.hardware.wifi.hostapd-V1.1-java",
+		"vendor.qti.hardware.wifi.hostapd-V1.2-java",
+		"vendor.qti.hardware.wifi.supplicant-V2.0-java",
+		"vendor.qti.hardware.wifi.supplicant-V2.1-java",
+		"vendor.qti.hardware.wifi.supplicant-V2.2-java",
+		"vendor.qti.hardware.fstman-V1.0-java",
 		"bouncycastle-unbundled",
 		"dnsresolver_aidl_interface-V2-java",
 		"error_prone_annotations",
@@ -1759,6 +1766,7 @@
 }
 
 var _ javaDependency = (*java.Library)(nil)
+var _ javaDependency = (*java.Import)(nil)
 var _ javaDependency = (*java.SdkLibrary)(nil)
 var _ javaDependency = (*java.DexImport)(nil)
 var _ javaDependency = (*java.SdkLibraryImport)(nil)
@@ -2048,7 +2056,7 @@
 				}
 			case javaLibTag:
 				switch child.(type) {
-				case *java.Library, *java.SdkLibrary, *java.DexImport, *java.SdkLibraryImport:
+				case *java.Library, *java.SdkLibrary, *java.DexImport, *java.SdkLibraryImport, *java.Import:
 					af := apexFileForJavaLibrary(ctx, child.(javaDependency), child.(android.Module))
 					if !af.Ok() {
 						ctx.PropertyErrorf("java_libs", "%q is not configured to be compiled into dex", depName)
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 8803a5f..9f930d1 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -3484,6 +3484,34 @@
 	`)
 }
 
+func TestApexWithJavaImport(t *testing.T) {
+	ctx, _ := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			java_libs: ["myjavaimport"],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		java_import {
+			name: "myjavaimport",
+			apex_available: ["myapex"],
+			jars: ["my.jar"],
+			compile_dex: true,
+		}
+	`)
+
+	module := ctx.ModuleForTests("myapex", "android_common_myapex_image")
+	apexRule := module.Rule("apexRule")
+	copyCmds := apexRule.Args["copy_commands"]
+	ensureContains(t, copyCmds, "image.apex/javalib/myjavaimport.jar")
+}
+
 func TestApexWithApps(t *testing.T) {
 	ctx, _ := testApex(t, `
 		apex {
diff --git a/cc/Android.bp b/cc/Android.bp
index 9ece05f..c2c08d1 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -19,6 +19,7 @@
         "check.go",
         "coverage.go",
         "gen.go",
+        "image.go",
         "linkable.go",
         "lto.go",
         "makevars.go",
@@ -29,6 +30,7 @@
         "sanitize.go",
         "sabi.go",
         "sdk.go",
+        "snapshot_prebuilt.go",
         "snapshot_utils.go",
         "stl.go",
         "strip.go",
diff --git a/cc/androidmk.go b/cc/androidmk.go
index fede601..0ad2fcf 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -92,6 +92,12 @@
 				if len(c.Properties.AndroidMkWholeStaticLibs) > 0 {
 					entries.AddStrings("LOCAL_WHOLE_STATIC_LIBRARIES", c.Properties.AndroidMkWholeStaticLibs...)
 				}
+				if len(c.Properties.AndroidMkHeaderLibs) > 0 {
+					entries.AddStrings("LOCAL_HEADER_LIBRARIES", c.Properties.AndroidMkHeaderLibs...)
+				}
+				if lib, ok := c.compiler.(*libraryDecorator); ok {
+					entries.AddStrings("LOCAL_SRC_FILES", lib.baseCompiler.srcsBeforeGen.Strings()...)
+				}
 				entries.SetString("LOCAL_SOONG_LINK_TYPE", c.makeLinkType)
 				if c.UseVndk() {
 					entries.SetBool("LOCAL_USE_VNDK", true)
@@ -486,7 +492,7 @@
 	})
 }
 
-func (c *vendorSnapshotLibraryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
+func (c *snapshotLibraryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
 	// Each vendor snapshot is exported to androidMk only when BOARD_VNDK_VERSION != current
 	// and the version of the prebuilt is same as BOARD_VNDK_VERSION.
 	if c.shared() {
@@ -497,12 +503,14 @@
 		entries.Class = "HEADER_LIBRARIES"
 	}
 
-	if c.androidMkVendorSuffix {
-		entries.SubName = vendorSuffix
-	} else {
-		entries.SubName = ""
+	entries.SubName = ""
+
+	if c.sanitizerProperties.CfiEnabled {
+		entries.SubName += ".cfi"
 	}
 
+	entries.SubName += c.androidMkSuffix
+
 	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
 		c.libraryDecorator.androidMkWriteExportedFlags(entries)
 
@@ -512,7 +520,10 @@
 			entries.SetString("LOCAL_BUILT_MODULE_STEM", "$(LOCAL_MODULE)"+ext)
 			entries.SetString("LOCAL_MODULE_SUFFIX", suffix)
 			entries.SetString("LOCAL_MODULE_STEM", stem)
-			if c.shared() {
+			// TODO(b/181815415) remove isLlndk() when possible
+			if c.isLlndk() {
+				entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
+			} else if c.shared() {
 				entries.SetString("LOCAL_MODULE_PATH", path)
 			}
 			if c.tocFile.Valid() {
@@ -526,28 +537,18 @@
 	})
 }
 
-func (c *vendorSnapshotBinaryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
+func (c *snapshotBinaryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
 	entries.Class = "EXECUTABLES"
-
-	if c.androidMkVendorSuffix {
-		entries.SubName = vendorSuffix
-	} else {
-		entries.SubName = ""
-	}
+	entries.SubName = c.androidMkSuffix
 
 	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
 		entries.AddStrings("LOCAL_MODULE_SYMLINKS", c.Properties.Symlinks...)
 	})
 }
 
-func (c *vendorSnapshotObjectLinker) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
+func (c *snapshotObjectLinker) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
 	entries.Class = "STATIC_LIBRARIES"
-
-	if c.androidMkVendorSuffix {
-		entries.SubName = vendorSuffix
-	} else {
-		entries.SubName = ""
-	}
+	entries.SubName = c.androidMkSuffix
 
 	entries.ExtraFooters = append(entries.ExtraFooters,
 		func(w io.Writer, name, prefix, moduleDir string, entries *android.AndroidMkEntries) {
diff --git a/cc/builder.go b/cc/builder.go
index 5deb129..6423339 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -322,6 +322,7 @@
 	aidlFlags     string
 	rsFlags       string
 	toolchain     config.Toolchain
+	sdclang       bool
 	tidy          bool
 	gcovCoverage  bool
 	sAbiDump      bool
@@ -519,7 +520,13 @@
 
 		ccDesc := ccCmd
 
-		ccCmd = "${config.ClangBin}/" + ccCmd
+		var extraFlags string
+		if flags.sdclang {
+			ccCmd = "${config.SDClangBin}/" + ccCmd
+			extraFlags = " ${config.SDClangFlags}"
+		} else {
+			ccCmd = "${config.ClangBin}/" + ccCmd
+		}
 
 		var implicitOutputs android.WritablePaths
 		if coverage {
@@ -537,7 +544,7 @@
 			Implicits:       cFlagsDeps,
 			OrderOnly:       pathDeps,
 			Args: map[string]string{
-				"cFlags": moduleFlags,
+				"cFlags": moduleFlags + extraFlags,
 				"ccCmd":  ccCmd,
 			},
 		})
@@ -618,6 +625,9 @@
 	flags builderFlags, outputFile android.ModuleOutPath, deps android.Paths) {
 
 	arCmd := "${config.ClangBin}/llvm-ar"
+	if flags.sdclang {
+                arCmd = "${config.ClangBin}/llvm-ar"
+        }
 	arFlags := "crsPD"
 	if !ctx.Darwin() {
 		arFlags += " -format=gnu"
@@ -642,7 +652,14 @@
 	objFiles, sharedLibs, staticLibs, lateStaticLibs, wholeStaticLibs, deps android.Paths,
 	crtBegin, crtEnd android.OptionalPath, groupLate bool, flags builderFlags, outputFile android.WritablePath, implicitOutputs android.WritablePaths) {
 
-	ldCmd := "${config.ClangBin}/clang++"
+	var ldCmd string
+	var extraFlags string
+	if flags.sdclang {
+		ldCmd = "${config.SDClangBin}/clang++"
+		extraFlags = " ${config.SDClangFlags}"
+	} else {
+		ldCmd = "${config.ClangBin}/clang++"
+	}
 
 	var libFlagsList []string
 
@@ -697,7 +714,7 @@
 		"crtBegin":      crtBegin.String(),
 		"libFlags":      strings.Join(libFlagsList, " "),
 		"extraLibFlags": flags.extraLibFlags,
-		"ldFlags":       flags.globalLdFlags + " " + flags.localLdFlags,
+		"ldFlags":       flags.globalLdFlags + " " + flags.localLdFlags + " " + extraFlags,
 		"crtEnd":        crtEnd.String(),
 	}
 	if ctx.Config().IsEnvTrue("RBE_CXX_LINKS") {
@@ -800,6 +817,19 @@
 	if isVndkExt {
 		localAbiCheckAllowFlags = append(localAbiCheckAllowFlags, "-allow-extensions")
 	}
+	var sdclangAbiCheckIgnoreList = []string{
+		"libbinder",
+		"libhwbinder",
+		"libprotobuf-cpp-lite",
+		"libprotobuf-cpp-full",
+		"libunwindstack",
+		"libvixl-arm64",
+		"libvixl-arm",
+	}
+	if config.SDClang && !inList("-advice-only", localAbiCheckAllowFlags) &&
+		inList(ctx.ModuleName(), sdclangAbiCheckIgnoreList) {
+		localAbiCheckAllowFlags = append(localAbiCheckAllowFlags, "-advice-only")
+	}
 
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        sAbiDiff,
@@ -851,12 +881,19 @@
 func TransformObjsToObj(ctx android.ModuleContext, objFiles android.Paths,
 	flags builderFlags, outputFile android.WritablePath, deps android.Paths) {
 
-	ldCmd := "${config.ClangBin}/clang++"
+	var ldCmd string
+	var extraFlags string
+	if flags.sdclang {
+		ldCmd = "${config.SDClangBin}/clang++"
+		extraFlags = " ${config.SDClangFlags}"
+	} else {
+		ldCmd = "${config.ClangBin}/clang++"
+	}
 
 	rule := partialLd
 	args := map[string]string{
 		"ldCmd":   ldCmd,
-		"ldFlags": flags.globalLdFlags + " " + flags.localLdFlags,
+		"ldFlags": flags.globalLdFlags + " " + flags.localLdFlags + " " + extraFlags,
 	}
 	if ctx.Config().IsEnvTrue("RBE_CXX_LINKS") {
 		rule = partialLdRE
diff --git a/cc/cc.go b/cc/cc.go
index 0f874f1..bc1e5ec 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -52,6 +52,10 @@
 		ctx.BottomUp("sysprop_cc", SyspropMutator).Parallel()
 		ctx.BottomUp("vendor_snapshot", VendorSnapshotMutator).Parallel()
 		ctx.BottomUp("vendor_snapshot_source", VendorSnapshotSourceMutator).Parallel()
+		ctx.BottomUp("recovery_snapshot", RecoverySnapshotMutator).Parallel()
+		ctx.BottomUp("recovery_snapshot_source", RecoverySnapshotSourceMutator).Parallel()
+		ctx.BottomUp("ramdisk_snapshot", RamdiskSnapshotMutator).Parallel()
+		ctx.BottomUp("ramdisk_snapshot_source", RamdiskSnapshotSourceMutator).Parallel()
 	})
 
 	ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
@@ -183,6 +187,7 @@
 	SystemIncludeFlags []string
 
 	Toolchain    config.Toolchain
+	Sdclang      bool
 	Tidy         bool
 	GcovCoverage bool
 	SAbiDump     bool
@@ -209,6 +214,9 @@
 	// Deprecated. true is the default, false is invalid.
 	Clang *bool `android:"arch_variant"`
 
+	// compile module with SDLLVM instead of AOSP LLVM
+	Sdclang *bool `android:"arch_variant"`
+
 	// Minimum sdk version supported when compiling against the ndk. Setting this property causes
 	// two variants to be built, one for the platform and one for apps.
 	Sdk_version *string
@@ -223,6 +231,7 @@
 	AndroidMkStaticLibs       []string `blueprint:"mutated"`
 	AndroidMkRuntimeLibs      []string `blueprint:"mutated"`
 	AndroidMkWholeStaticLibs  []string `blueprint:"mutated"`
+	AndroidMkHeaderLibs       []string `blueprint:"mutated"`
 	HideFromMake              bool     `blueprint:"mutated"`
 	PreventInstall            bool     `blueprint:"mutated"`
 	ApexesProvidingSharedLibs []string `blueprint:"mutated"`
@@ -270,6 +279,27 @@
 	// Set when both SDK and platform variants are exported to Make to trigger renaming the SDK
 	// variant to have a ".sdk" suffix.
 	SdkAndPlatformVariantVisibleToMake bool `blueprint:"mutated"`
+
+	// Normally Soong uses the directory structure to decide which modules
+	// should be included (framework) or excluded (non-framework) from the
+	// different snapshots (vendor, recovery, etc.), but this property
+	// allows a partner to exclude a module normally thought of as a
+	// framework module from the vendor snapshot.
+	Exclude_from_vendor_snapshot *bool
+
+	// Normally Soong uses the directory structure to decide which modules
+	// should be included (framework) or excluded (non-framework) from the
+	// different snapshots (vendor, recovery, etc.), but this property
+	// allows a partner to exclude a module normally thought of as a
+	// framework module from the recovery snapshot.
+	Exclude_from_recovery_snapshot *bool
+
+    // Normally Soong uses the directory structure to decide which modules
+	// should be included (framework) or excluded (non-framework) from the
+	// different snapshots (vendor, recovery, etc.), but this property
+	// allows a partner to exclude a module normally thought of as a
+	// framework module from the ramdisk snapshot.
+	Exclude_from_ramdisk_snapshot *bool
 }
 
 type VendorProperties struct {
@@ -930,48 +960,6 @@
 	return ""
 }
 
-// Returns true only when this module is configured to have core, product and vendor
-// variants.
-func (c *Module) HasVendorVariant() bool {
-	return c.IsVndk() || Bool(c.VendorProperties.Vendor_available)
-}
-
-const (
-	// VendorVariationPrefix is the variant prefix used for /vendor code that compiles
-	// against the VNDK.
-	VendorVariationPrefix = "vendor."
-
-	// ProductVariationPrefix is the variant prefix used for /product code that compiles
-	// against the VNDK.
-	ProductVariationPrefix = "product."
-)
-
-// Returns true if the module is "product" variant. Usually these modules are installed in /product
-func (c *Module) inProduct() bool {
-	return c.Properties.ImageVariationPrefix == ProductVariationPrefix
-}
-
-// Returns true if the module is "vendor" variant. Usually these modules are installed in /vendor
-func (c *Module) inVendor() bool {
-	return c.Properties.ImageVariationPrefix == VendorVariationPrefix
-}
-
-func (c *Module) InRamdisk() bool {
-	return c.ModuleBase.InRamdisk() || c.ModuleBase.InstallInRamdisk()
-}
-
-func (c *Module) InRecovery() bool {
-	return c.ModuleBase.InRecovery() || c.ModuleBase.InstallInRecovery()
-}
-
-func (c *Module) OnlyInRamdisk() bool {
-	return c.ModuleBase.InstallInRamdisk()
-}
-
-func (c *Module) OnlyInRecovery() bool {
-	return c.ModuleBase.InstallInRecovery()
-}
-
 func (c *Module) IsStubs() bool {
 	if library, ok := c.linker.(*libraryDecorator); ok {
 		return library.buildStubs()
@@ -1045,6 +1033,19 @@
 	return nil
 }
 
+func (c *Module) ExcludeFromVendorSnapshot() bool {
+	return Bool(c.Properties.Exclude_from_vendor_snapshot)
+}
+
+func (c *Module) ExcludeFromRecoverySnapshot() bool {
+	return Bool(c.Properties.Exclude_from_recovery_snapshot)
+}
+
+func (c *Module) ExcludeFromRamdiskSnapshot() bool {
+	return Bool(c.Properties.Exclude_from_ramdisk_snapshot)
+}
+
+
 func isBionic(name string) bool {
 	switch name {
 	case "libc", "libm", "libdl", "libdl_android", "linker":
@@ -1079,16 +1080,6 @@
 	moduleContextImpl
 }
 
-func (ctx *moduleContext) ProductSpecific() bool {
-	return ctx.ModuleContext.ProductSpecific() ||
-		(ctx.mod.HasVendorVariant() && ctx.mod.inProduct() && !ctx.mod.IsVndk())
-}
-
-func (ctx *moduleContext) SocSpecific() bool {
-	return ctx.ModuleContext.SocSpecific() ||
-		(ctx.mod.HasVendorVariant() && ctx.mod.inVendor() && !ctx.mod.IsVndk())
-}
-
 type moduleContextImpl struct {
 	mod *Module
 	ctx BaseModuleContext
@@ -1184,22 +1175,6 @@
 	return ctx.mod.MustUseVendorVariant()
 }
 
-func (ctx *moduleContextImpl) inProduct() bool {
-	return ctx.mod.inProduct()
-}
-
-func (ctx *moduleContextImpl) inVendor() bool {
-	return ctx.mod.inVendor()
-}
-
-func (ctx *moduleContextImpl) inRamdisk() bool {
-	return ctx.mod.InRamdisk()
-}
-
-func (ctx *moduleContextImpl) inRecovery() bool {
-	return ctx.mod.InRecovery()
-}
-
 // Check whether ABI dumps should be created for this module.
 func (ctx *moduleContextImpl) shouldCreateSourceAbiDump() bool {
 	if ctx.ctx.Config().IsEnvTrue("SKIP_ABI_CHECKS") {
@@ -1466,6 +1441,7 @@
 	flags := Flags{
 		Toolchain: c.toolchain(ctx),
 		EmitXrefs: ctx.Config().EmitXrefRules(),
+		Sdclang:   c.sdclang(ctx),
 	}
 	if c.compiler != nil {
 		flags = c.compiler.compilerFlags(ctx, flags, deps)
@@ -1555,9 +1531,10 @@
 			// Note: this is still non-installable
 		}
 
-		// glob exported headers for snapshot, if BOARD_VNDK_VERSION is current.
-		if i, ok := c.linker.(snapshotLibraryInterface); ok && ctx.DeviceConfig().VndkVersion() == "current" {
-			if isSnapshotAware(ctx, c) {
+		// glob exported headers for snapshot, if BOARD_VNDK_VERSION is current or
+		// RECOVERY_SNAPSHOT_VERSION is current or RAMDISK_SNAPSHOT_VERSION is current.
+		if i, ok := c.linker.(snapshotLibraryInterface); ok {
+			if shouldCollectHeadersForSnapshot(ctx, c) {
 				i.collectHeadersForSnapshot(ctx)
 			}
 		}
@@ -1756,21 +1733,24 @@
 
 		vendorPublicLibraries := vendorPublicLibraries(actx.Config())
 		vendorSnapshotSharedLibs := vendorSnapshotSharedLibs(actx.Config())
+		recoverySnapshotSharedLibs := recoverySnapshotSharedLibs(actx.Config())
+		ramdiskSnapshotSharedLibs := ramdiskSnapshotSharedLibs(actx.Config())
 
 		rewriteVendorLibs := func(lib string) string {
+			// Only a module with BOARD_VNDK_VERSION uses snapshot.
+			// We check this before checking if the library is an
+			// llndk so that the snapshot can contain llndk
+			// libraries.
+			if c.VndkVersion() == actx.DeviceConfig().VndkVersion() {
+				if snapshot, ok := vendorSnapshotSharedLibs.get(lib, actx.Arch().ArchType); ok {
+					return snapshot
+				}
+			}
+
 			if isLlndkLibrary(lib, ctx.Config()) {
 				return lib + llndkLibrarySuffix
 			}
 
-			// only modules with BOARD_VNDK_VERSION uses snapshot.
-			if c.VndkVersion() != actx.DeviceConfig().VndkVersion() {
-				return lib
-			}
-
-			if snapshot, ok := vendorSnapshotSharedLibs.get(lib, actx.Arch().ArchType); ok {
-				return snapshot
-			}
-
 			return lib
 		}
 
@@ -1780,7 +1760,31 @@
 			for _, entry := range list {
 				// strip #version suffix out
 				name, _ := StubsLibNameAndVersion(entry)
-				if ctx.useSdk() && inList(name, ndkPrebuiltSharedLibraries) {
+				if c.InRecovery() {
+					recoverySnapshotVersion :=
+						actx.DeviceConfig().RecoverySnapshotVersion()
+					if recoverySnapshotVersion == "current" ||
+						recoverySnapshotVersion == "" {
+						nonvariantLibs = append(nonvariantLibs, name)
+					} else if snapshot, ok := recoverySnapshotSharedLibs.get(
+						name, actx.Arch().ArchType); ok {
+						nonvariantLibs = append(nonvariantLibs, snapshot)
+					} else {
+						nonvariantLibs = append(nonvariantLibs, name)
+					}
+				} else if c.InRamdisk() {
+					ramdiskSnapshotVersion :=
+						actx.DeviceConfig().RamdiskSnapshotVersion()
+					if ramdiskSnapshotVersion == "current" ||
+						ramdiskSnapshotVersion == "" {
+						nonvariantLibs = append(nonvariantLibs, name)
+					} else if snapshot, ok := ramdiskSnapshotSharedLibs.get(
+						name, actx.Arch().ArchType); ok {
+						nonvariantLibs = append(nonvariantLibs, snapshot)
+					} else {
+						nonvariantLibs = append(nonvariantLibs, name)
+					}
+				} else if ctx.useSdk() && inList(name, ndkPrebuiltSharedLibraries) {
 					if !inList(name, ndkMigratedLibs) {
 						nonvariantLibs = append(nonvariantLibs, name+".ndk."+version)
 					} else {
@@ -1838,14 +1842,55 @@
 		return lib
 	}
 
-	vendorSnapshotHeaderLibs := vendorSnapshotHeaderLibs(actx.Config())
+	snapshotHeaderLibs := vendorSnapshotHeaderLibs(actx.Config())
+	snapshotStaticLibs := vendorSnapshotStaticLibs(actx.Config())
+	snapshotObjects := vendorSnapshotObjects(actx.Config())
+
+	if c.InRecovery() {
+		rewriteSnapshotLibs = func(lib string, snapshotMap *snapshotMap) string {
+			recoverySnapshotVersion :=
+				actx.DeviceConfig().RecoverySnapshotVersion()
+			if recoverySnapshotVersion == "current" ||
+				recoverySnapshotVersion == "" {
+				return lib
+			} else if snapshot, ok := snapshotMap.get(lib, actx.Arch().ArchType); ok {
+				return snapshot
+			}
+
+			return lib
+		}
+
+		snapshotHeaderLibs = recoverySnapshotHeaderLibs(actx.Config())
+		snapshotStaticLibs = recoverySnapshotStaticLibs(actx.Config())
+		snapshotObjects = recoverySnapshotObjects(actx.Config())
+	}
+
+	if c.InRamdisk() {
+		rewriteSnapshotLibs = func(lib string, snapshotMap *snapshotMap) string {
+			ramdiskSnapshotVersion :=
+				actx.DeviceConfig().RamdiskSnapshotVersion()
+			if ramdiskSnapshotVersion == "current" ||
+				ramdiskSnapshotVersion == "" {
+				return lib
+			} else if snapshot, ok := snapshotMap.get(lib, actx.Arch().ArchType); ok {
+				return snapshot
+			}
+
+			return lib
+		}
+
+		snapshotHeaderLibs = ramdiskSnapshotHeaderLibs(actx.Config())
+		snapshotStaticLibs = ramdiskSnapshotStaticLibs(actx.Config())
+		snapshotObjects = ramdiskSnapshotObjects(actx.Config())
+	}
+
 	for _, lib := range deps.HeaderLibs {
 		depTag := headerDepTag
 		if inList(lib, deps.ReexportHeaderLibHeaders) {
 			depTag = headerExportDepTag
 		}
 
-		lib = rewriteSnapshotLibs(lib, vendorSnapshotHeaderLibs)
+		lib = rewriteSnapshotLibs(lib, snapshotHeaderLibs)
 
 		if buildStubs {
 			actx.AddFarVariationDependencies(append(ctx.Target().Variations(), c.ImageVariation()),
@@ -1862,7 +1907,6 @@
 	}
 
 	syspropImplLibraries := syspropImplLibraries(actx.Config())
-	vendorSnapshotStaticLibs := vendorSnapshotStaticLibs(actx.Config())
 
 	for _, lib := range deps.WholeStaticLibs {
 		depTag := wholeStaticDepTag
@@ -1870,7 +1914,7 @@
 			lib = impl
 		}
 
-		lib = rewriteSnapshotLibs(lib, vendorSnapshotStaticLibs)
+		lib = rewriteSnapshotLibs(lib, snapshotStaticLibs)
 
 		actx.AddVariationDependencies([]blueprint.Variation{
 			{Mutator: "link", Variation: "static"},
@@ -1887,7 +1931,7 @@
 			lib = impl
 		}
 
-		lib = rewriteSnapshotLibs(lib, vendorSnapshotStaticLibs)
+		lib = rewriteSnapshotLibs(lib, snapshotStaticLibs)
 
 		actx.AddVariationDependencies([]blueprint.Variation{
 			{Mutator: "link", Variation: "static"},
@@ -1900,13 +1944,13 @@
 	if deps.StaticUnwinderIfLegacy {
 		actx.AddVariationDependencies([]blueprint.Variation{
 			{Mutator: "link", Variation: "static"},
-		}, staticUnwinderDepTag, rewriteSnapshotLibs(staticUnwinder(actx), vendorSnapshotStaticLibs))
+		}, staticUnwinderDepTag, rewriteSnapshotLibs(staticUnwinder(actx), snapshotStaticLibs))
 	}
 
 	for _, lib := range deps.LateStaticLibs {
 		actx.AddVariationDependencies([]blueprint.Variation{
 			{Mutator: "link", Variation: "static"},
-		}, lateStaticDepTag, rewriteSnapshotLibs(lib, vendorSnapshotStaticLibs))
+		}, lateStaticDepTag, rewriteSnapshotLibs(lib, snapshotStaticLibs))
 	}
 
 	addSharedLibDependencies := func(depTag DependencyTag, name string, version string) {
@@ -1980,14 +2024,13 @@
 	}
 
 	actx.AddVariationDependencies(nil, objDepTag, deps.ObjFiles...)
-
-	vendorSnapshotObjects := vendorSnapshotObjects(actx.Config())
-
 	if deps.CrtBegin != "" {
-		actx.AddVariationDependencies(nil, CrtBeginDepTag, rewriteSnapshotLibs(deps.CrtBegin, vendorSnapshotObjects))
+		actx.AddVariationDependencies(nil, CrtBeginDepTag,
+			rewriteSnapshotLibs(deps.CrtBegin, snapshotObjects))
 	}
 	if deps.CrtEnd != "" {
-		actx.AddVariationDependencies(nil, CrtEndDepTag, rewriteSnapshotLibs(deps.CrtEnd, vendorSnapshotObjects))
+		actx.AddVariationDependencies(nil, CrtEndDepTag,
+			rewriteSnapshotLibs(deps.CrtEnd, snapshotObjects))
 	}
 	if deps.LinkerFlagsFile != "" {
 		actx.AddDependency(c, linkerFlagsDepTag, deps.LinkerFlagsFile)
@@ -2175,6 +2218,21 @@
 	}
 }
 
+func (c *Module) sdclang(ctx BaseModuleContext) bool {
+	sdclang := Bool(c.Properties.Sdclang)
+
+	// SDLLVM is not for host build
+	if ctx.Host() || config.ForceSDClangOff {
+		return false
+	}
+
+	if c.Properties.Sdclang == nil && config.SDClang {
+		return true
+	}
+
+	return sdclang
+}
+
 // Convert dependencies to paths.  Returns a PathDeps containing paths
 func (c *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
 	var depPaths PathDeps
@@ -2182,8 +2240,6 @@
 	directStaticDeps := []LinkableInterface{}
 	directSharedDeps := []LinkableInterface{}
 
-	vendorPublicLibraries := vendorPublicLibraries(ctx.Config())
-
 	reexportExporter := func(exporter exportedFlagsProducer) {
 		depPaths.ReexportedDirs = append(depPaths.ReexportedDirs, exporter.exportedDirs()...)
 		depPaths.ReexportedSystemDirs = append(depPaths.ReexportedSystemDirs, exporter.exportedSystemDirs()...)
@@ -2523,59 +2579,6 @@
 			*depPtr = append(*depPtr, dep.Path())
 		}
 
-		vendorSuffixModules := vendorSuffixModules(ctx.Config())
-
-		baseLibName := func(depName string) string {
-			libName := strings.TrimSuffix(depName, llndkLibrarySuffix)
-			libName = strings.TrimSuffix(libName, vendorPublicLibrarySuffix)
-			libName = strings.TrimPrefix(libName, "prebuilt_")
-			return libName
-		}
-
-		makeLibName := func(depName string) string {
-			libName := baseLibName(depName)
-			isLLndk := isLlndkLibrary(libName, ctx.Config())
-			isVendorPublicLib := inList(libName, *vendorPublicLibraries)
-			bothVendorAndCoreVariantsExist := ccDep.HasVendorVariant() || isLLndk
-
-			if c, ok := ccDep.(*Module); ok {
-				// Use base module name for snapshots when exporting to Makefile.
-				if c.isSnapshotPrebuilt() {
-					baseName := c.BaseModuleName()
-
-					if c.IsVndk() {
-						return baseName + ".vendor"
-					}
-
-					if vendorSuffixModules[baseName] {
-						return baseName + ".vendor"
-					} else {
-						return baseName
-					}
-				}
-			}
-
-			if ctx.DeviceConfig().VndkUseCoreVariant() && ccDep.IsVndk() && !ccDep.MustUseVendorVariant() && !c.InRamdisk() && !c.InRecovery() {
-				// The vendor module is a no-vendor-variant VNDK library.  Depend on the
-				// core module instead.
-				return libName
-			} else if c.UseVndk() && bothVendorAndCoreVariantsExist {
-				// The vendor module in Make will have been renamed to not conflict with the core
-				// module, so update the dependency name here accordingly.
-				return libName + c.getNameSuffixWithVndkVersion(ctx)
-			} else if (ctx.Platform() || ctx.ProductSpecific()) && isVendorPublicLib {
-				return libName + vendorPublicLibrarySuffix
-			} else if ccDep.InRamdisk() && !ccDep.OnlyInRamdisk() {
-				return libName + ramdiskSuffix
-			} else if ccDep.InRecovery() && !ccDep.OnlyInRecovery() {
-				return libName + recoverySuffix
-			} else if ccDep.Module().Target().NativeBridge == android.NativeBridgeEnabled {
-				return libName + nativeBridgeSuffix
-			} else {
-				return libName
-			}
-		}
-
 		// Export the shared libs to Make.
 		switch depTag {
 		case SharedDepTag, sharedExportDepTag, lateSharedDepTag, earlySharedDepTag:
@@ -2593,7 +2596,7 @@
 			// Note: the order of libs in this list is not important because
 			// they merely serve as Make dependencies and do not affect this lib itself.
 			c.Properties.AndroidMkSharedLibs = append(
-				c.Properties.AndroidMkSharedLibs, makeLibName(depName))
+				c.Properties.AndroidMkSharedLibs, c.makeLibName(ctx, ccDep, depName))
 			// Record baseLibName for snapshots.
 			c.Properties.SnapshotSharedLibs = append(c.Properties.SnapshotSharedLibs, baseLibName(depName))
 		case ndkStubDepTag, ndkLateStubDepTag:
@@ -2602,15 +2605,18 @@
 				depName+"."+ccDep.ApiLevel())
 		case StaticDepTag, staticExportDepTag, lateStaticDepTag:
 			c.Properties.AndroidMkStaticLibs = append(
-				c.Properties.AndroidMkStaticLibs, makeLibName(depName))
+				c.Properties.AndroidMkStaticLibs, c.makeLibName(ctx, ccDep, depName))
 		case runtimeDepTag:
 			c.Properties.AndroidMkRuntimeLibs = append(
-				c.Properties.AndroidMkRuntimeLibs, makeLibName(depName))
+				c.Properties.AndroidMkRuntimeLibs, c.makeLibName(ctx, ccDep, depName))
 			// Record baseLibName for snapshots.
 			c.Properties.SnapshotRuntimeLibs = append(c.Properties.SnapshotRuntimeLibs, baseLibName(depName))
 		case wholeStaticDepTag:
 			c.Properties.AndroidMkWholeStaticLibs = append(
-				c.Properties.AndroidMkWholeStaticLibs, makeLibName(depName))
+				c.Properties.AndroidMkWholeStaticLibs, c.makeLibName(ctx, ccDep, depName))
+		case headerDepTag:
+			c.Properties.AndroidMkHeaderLibs = append(
+				c.Properties.AndroidMkHeaderLibs, c.makeLibName(ctx, ccDep, depName))
 		}
 	})
 
@@ -2636,6 +2642,68 @@
 	return depPaths
 }
 
+// baseLibName trims known prefixes and suffixes
+func baseLibName(depName string) string {
+	libName := strings.TrimSuffix(depName, llndkLibrarySuffix)
+	libName = strings.TrimSuffix(libName, vendorPublicLibrarySuffix)
+	libName = strings.TrimPrefix(libName, "prebuilt_")
+	return libName
+}
+
+func (c *Module) makeLibName(ctx android.ModuleContext, ccDep LinkableInterface, depName string) string {
+	vendorSuffixModules := vendorSuffixModules(ctx.Config())
+	recoverySuffixModules := recoverySuffixModules(ctx.Config())
+	ramdiskSuffixModules := ramdiskSuffixModules(ctx.Config())
+	vendorPublicLibraries := vendorPublicLibraries(ctx.Config())
+
+	libName := baseLibName(depName)
+	isLLndk := isLlndkLibrary(libName, ctx.Config())
+	isVendorPublicLib := inList(libName, *vendorPublicLibraries)
+	bothVendorAndCoreVariantsExist := ccDep.HasVendorVariant() || isLLndk
+
+	if c, ok := ccDep.(*Module); ok {
+		// Use base module name for snapshots when exporting to Makefile.
+		if c.isSnapshotPrebuilt() {
+			baseName := c.BaseModuleName()
+
+			if c.IsVndk() {
+				return baseName + ".vendor"
+			}
+
+			if c.inVendor() && vendorSuffixModules[baseName] {
+				return baseName + ".vendor"
+			} else if c.InRecovery() && recoverySuffixModules[baseName] {
+				return baseName + ".recovery"
+			} else if c.InRamdisk() && ramdiskSuffixModules[baseName] {
+				return baseName + ".ramdisk"
+			} else {
+				return baseName
+			}
+		}
+	}
+
+	if ctx.DeviceConfig().VndkUseCoreVariant() && ccDep.IsVndk() && !ccDep.MustUseVendorVariant() &&
+		!c.InRamdisk() && !c.InRecovery() {
+		// The vendor module is a no-vendor-variant VNDK library.  Depend on the
+		// core module instead.
+		return libName
+	} else if c.UseVndk() && bothVendorAndCoreVariantsExist {
+		// The vendor module in Make will have been renamed to not conflict with the core
+		// module, so update the dependency name here accordingly.
+		return libName + c.getNameSuffixWithVndkVersion(ctx)
+	} else if (ctx.Platform() || ctx.ProductSpecific()) && isVendorPublicLib {
+		return libName + vendorPublicLibrarySuffix
+	} else if ccDep.InRamdisk() && !ccDep.OnlyInRamdisk() {
+		return libName + ramdiskSuffix
+	} else if ccDep.InRecovery() && !ccDep.OnlyInRecovery() {
+		return libName + recoverySuffix
+	} else if ccDep.Module().Target().NativeBridge == android.NativeBridgeEnabled {
+		return libName + nativeBridgeSuffix
+	} else {
+		return libName
+	}
+}
+
 func (c *Module) InstallInData() bool {
 	if c.installer == nil {
 		return false
@@ -2944,227 +3012,6 @@
 	}
 }
 
-var _ android.ImageInterface = (*Module)(nil)
-
-func (m *Module) ImageMutatorBegin(mctx android.BaseModuleContext) {
-	// Sanity check
-	vendorSpecific := mctx.SocSpecific() || mctx.DeviceSpecific()
-	productSpecific := mctx.ProductSpecific()
-
-	if m.VendorProperties.Vendor_available != nil && vendorSpecific {
-		mctx.PropertyErrorf("vendor_available",
-			"doesn't make sense at the same time as `vendor: true`, `proprietary: true`, or `device_specific:true`")
-	}
-
-	if vndkdep := m.vndkdep; vndkdep != nil {
-		if vndkdep.isVndk() {
-			if vendorSpecific || productSpecific {
-				if !vndkdep.isVndkExt() {
-					mctx.PropertyErrorf("vndk",
-						"must set `extends: \"...\"` to vndk extension")
-				} else if m.VendorProperties.Vendor_available != nil {
-					mctx.PropertyErrorf("vendor_available",
-						"must not set at the same time as `vndk: {extends: \"...\"}`")
-				}
-			} else {
-				if vndkdep.isVndkExt() {
-					mctx.PropertyErrorf("vndk",
-						"must set `vendor: true` or `product_specific: true` to set `extends: %q`",
-						m.getVndkExtendsModuleName())
-				}
-				if m.VendorProperties.Vendor_available == nil {
-					mctx.PropertyErrorf("vndk",
-						"vendor_available must be set to either true or false when `vndk: {enabled: true}`")
-				}
-			}
-		} else {
-			if vndkdep.isVndkSp() {
-				mctx.PropertyErrorf("vndk",
-					"must set `enabled: true` to set `support_system_process: true`")
-			}
-			if vndkdep.isVndkExt() {
-				mctx.PropertyErrorf("vndk",
-					"must set `enabled: true` to set `extends: %q`",
-					m.getVndkExtendsModuleName())
-			}
-		}
-	}
-
-	var coreVariantNeeded bool = false
-	var ramdiskVariantNeeded bool = false
-	var recoveryVariantNeeded bool = false
-
-	var vendorVariants []string
-	var productVariants []string
-
-	platformVndkVersion := mctx.DeviceConfig().PlatformVndkVersion()
-	boardVndkVersion := mctx.DeviceConfig().VndkVersion()
-	productVndkVersion := mctx.DeviceConfig().ProductVndkVersion()
-	if boardVndkVersion == "current" {
-		boardVndkVersion = platformVndkVersion
-	}
-	if productVndkVersion == "current" {
-		productVndkVersion = platformVndkVersion
-	}
-
-	if boardVndkVersion == "" {
-		// If the device isn't compiling against the VNDK, we always
-		// use the core mode.
-		coreVariantNeeded = true
-	} else if _, ok := m.linker.(*llndkStubDecorator); ok {
-		// LL-NDK stubs only exist in the vendor and product variants,
-		// since the real libraries will be used in the core variant.
-		vendorVariants = append(vendorVariants,
-			platformVndkVersion,
-			boardVndkVersion,
-		)
-		productVariants = append(productVariants,
-			platformVndkVersion,
-			productVndkVersion,
-		)
-	} else if _, ok := m.linker.(*llndkHeadersDecorator); ok {
-		// ... and LL-NDK headers as well
-		vendorVariants = append(vendorVariants,
-			platformVndkVersion,
-			boardVndkVersion,
-		)
-		productVariants = append(productVariants,
-			platformVndkVersion,
-			productVndkVersion,
-		)
-	} else if m.isSnapshotPrebuilt() {
-		// Make vendor variants only for the versions in BOARD_VNDK_VERSION and
-		// PRODUCT_EXTRA_VNDK_VERSIONS.
-		if snapshot, ok := m.linker.(interface {
-			version() string
-		}); ok {
-			vendorVariants = append(vendorVariants, snapshot.version())
-		} else {
-			mctx.ModuleErrorf("version is unknown for snapshot prebuilt")
-		}
-	} else if m.HasVendorVariant() && !m.isVndkExt() {
-		// This will be available in /system, /vendor and /product
-		// or a /system directory that is available to vendor and product.
-		coreVariantNeeded = true
-
-		// We assume that modules under proprietary paths are compatible for
-		// BOARD_VNDK_VERSION. The other modules are regarded as AOSP, or
-		// PLATFORM_VNDK_VERSION.
-		if isVendorProprietaryPath(mctx.ModuleDir()) {
-			vendorVariants = append(vendorVariants, boardVndkVersion)
-		} else {
-			vendorVariants = append(vendorVariants, platformVndkVersion)
-		}
-
-		// vendor_available modules are also available to /product.
-		productVariants = append(productVariants, platformVndkVersion)
-		// VNDK is always PLATFORM_VNDK_VERSION
-		if !m.IsVndk() {
-			productVariants = append(productVariants, productVndkVersion)
-		}
-	} else if vendorSpecific && String(m.Properties.Sdk_version) == "" {
-		// This will be available in /vendor (or /odm) only
-		// We assume that modules under proprietary paths are compatible for
-		// BOARD_VNDK_VERSION. The other modules are regarded as AOSP, or
-		// PLATFORM_VNDK_VERSION.
-		if isVendorProprietaryPath(mctx.ModuleDir()) {
-			vendorVariants = append(vendorVariants, boardVndkVersion)
-		} else {
-			vendorVariants = append(vendorVariants, platformVndkVersion)
-		}
-	} else {
-		// This is either in /system (or similar: /data), or is a
-		// modules built with the NDK. Modules built with the NDK
-		// will be restricted using the existing link type checks.
-		coreVariantNeeded = true
-	}
-
-	if boardVndkVersion != "" && productVndkVersion != "" {
-		if coreVariantNeeded && productSpecific && String(m.Properties.Sdk_version) == "" {
-			// The module has "product_specific: true" that does not create core variant.
-			coreVariantNeeded = false
-			productVariants = append(productVariants, productVndkVersion)
-		}
-	} else {
-		// Unless PRODUCT_PRODUCT_VNDK_VERSION is set, product partition has no
-		// restriction to use system libs.
-		// No product variants defined in this case.
-		productVariants = []string{}
-	}
-
-	if Bool(m.Properties.Ramdisk_available) {
-		ramdiskVariantNeeded = true
-	}
-
-	if m.ModuleBase.InstallInRamdisk() {
-		ramdiskVariantNeeded = true
-		coreVariantNeeded = false
-	}
-
-	if Bool(m.Properties.Recovery_available) {
-		recoveryVariantNeeded = true
-	}
-
-	if m.ModuleBase.InstallInRecovery() {
-		recoveryVariantNeeded = true
-		coreVariantNeeded = false
-	}
-
-	for _, variant := range android.FirstUniqueStrings(vendorVariants) {
-		m.Properties.ExtraVariants = append(m.Properties.ExtraVariants, VendorVariationPrefix+variant)
-	}
-
-	for _, variant := range android.FirstUniqueStrings(productVariants) {
-		m.Properties.ExtraVariants = append(m.Properties.ExtraVariants, ProductVariationPrefix+variant)
-	}
-
-	m.Properties.RamdiskVariantNeeded = ramdiskVariantNeeded
-	m.Properties.RecoveryVariantNeeded = recoveryVariantNeeded
-	m.Properties.CoreVariantNeeded = coreVariantNeeded
-}
-
-func (c *Module) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
-	return c.Properties.CoreVariantNeeded
-}
-
-func (c *Module) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
-	return c.Properties.RamdiskVariantNeeded
-}
-
-func (c *Module) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
-	return c.Properties.RecoveryVariantNeeded
-}
-
-func (c *Module) ExtraImageVariations(ctx android.BaseModuleContext) []string {
-	return c.Properties.ExtraVariants
-}
-
-func (c *Module) SetImageVariation(ctx android.BaseModuleContext, variant string, module android.Module) {
-	m := module.(*Module)
-	if variant == android.RamdiskVariation {
-		m.MakeAsPlatform()
-	} else if variant == android.RecoveryVariation {
-		m.MakeAsPlatform()
-		squashRecoverySrcs(m)
-	} else if strings.HasPrefix(variant, VendorVariationPrefix) {
-		m.Properties.ImageVariationPrefix = VendorVariationPrefix
-		m.Properties.VndkVersion = strings.TrimPrefix(variant, VendorVariationPrefix)
-		squashVendorSrcs(m)
-
-		// Makefile shouldn't know vendor modules other than BOARD_VNDK_VERSION.
-		// Hide other vendor variants to avoid collision.
-		vndkVersion := ctx.DeviceConfig().VndkVersion()
-		if vndkVersion != "current" && vndkVersion != "" && vndkVersion != m.Properties.VndkVersion {
-			m.Properties.HideFromMake = true
-			m.SkipInstall()
-		}
-	} else if strings.HasPrefix(variant, ProductVariationPrefix) {
-		m.Properties.ImageVariationPrefix = ProductVariationPrefix
-		m.Properties.VndkVersion = strings.TrimPrefix(variant, ProductVariationPrefix)
-		squashVendorSrcs(m)
-	}
-}
-
 func getCurrentNdkPrebuiltVersion(ctx DepsContext) string {
 	if ctx.Config().PlatformSdkVersionInt() > config.NdkMaxPrebuiltVersionInt {
 		return strconv.Itoa(config.NdkMaxPrebuiltVersionInt)
diff --git a/cc/cc_test.go b/cc/cc_test.go
index f73e021..2d2a99a 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -258,7 +258,8 @@
 	}
 }
 
-func checkSnapshot(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string) {
+func checkSnapshotIncludeExclude(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string, include bool, fake bool) {
+	t.Helper()
 	mod, ok := ctx.ModuleForTests(moduleName, variant).Module().(android.OutputFileProducer)
 	if !ok {
 		t.Errorf("%q must have output\n", moduleName)
@@ -271,12 +272,37 @@
 	}
 	snapshotPath := filepath.Join(subDir, snapshotFilename)
 
-	out := singleton.Output(snapshotPath)
-	if out.Input.String() != outputFiles[0].String() {
-		t.Errorf("The input of snapshot %q must be %q, but %q", moduleName, out.Input.String(), outputFiles[0])
+	if include {
+		out := singleton.Output(snapshotPath)
+		if fake {
+			if out.Rule == nil {
+				t.Errorf("Missing rule for module %q output file %q", moduleName, outputFiles[0])
+			}
+		} else {
+			if out.Input.String() != outputFiles[0].String() {
+				t.Errorf("The input of snapshot %q must be %q, but %q", moduleName, out.Input.String(), outputFiles[0])
+			}
+		}
+	} else {
+		out := singleton.MaybeOutput(snapshotPath)
+		if out.Rule != nil {
+			t.Errorf("There must be no rule for module %q output file %q", moduleName, outputFiles[0])
+		}
 	}
 }
 
+func checkSnapshot(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string) {
+	checkSnapshotIncludeExclude(t, ctx, singleton, moduleName, snapshotFilename, subDir, variant, true, false)
+}
+
+func checkSnapshotExclude(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string) {
+	checkSnapshotIncludeExclude(t, ctx, singleton, moduleName, snapshotFilename, subDir, variant, false, false)
+}
+
+func checkSnapshotRule(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string) {
+	checkSnapshotIncludeExclude(t, ctx, singleton, moduleName, snapshotFilename, subDir, variant, true, true)
+}
+
 func checkWriteFileOutput(t *testing.T, params android.TestingBuildParams, expected []string) {
 	t.Helper()
 	assertString(t, params.Rule.String(), android.WriteFile.String())
@@ -833,131 +859,6 @@
 	`)
 }
 
-func TestVendorSnapshot(t *testing.T) {
-	bp := `
-	cc_library {
-		name: "libvndk",
-		vendor_available: true,
-		vndk: {
-			enabled: true,
-		},
-		nocrt: true,
-	}
-
-	cc_library {
-		name: "libvendor",
-		vendor: true,
-		nocrt: true,
-	}
-
-	cc_library {
-		name: "libvendor_available",
-		vendor_available: true,
-		nocrt: true,
-	}
-
-	cc_library_headers {
-		name: "libvendor_headers",
-		vendor_available: true,
-		nocrt: true,
-	}
-
-	cc_binary {
-		name: "vendor_bin",
-		vendor: true,
-		nocrt: true,
-	}
-
-	cc_binary {
-		name: "vendor_available_bin",
-		vendor_available: true,
-		nocrt: true,
-	}
-
-	toolchain_library {
-		name: "libb",
-		vendor_available: true,
-		src: "libb.a",
-	}
-
-	cc_object {
-		name: "obj",
-		vendor_available: true,
-	}
-`
-	config := TestConfig(buildDir, android.Android, nil, bp, nil)
-	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
-	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
-	ctx := testCcWithConfig(t, config)
-
-	// Check Vendor snapshot output.
-
-	snapshotDir := "vendor-snapshot"
-	snapshotVariantPath := filepath.Join(buildDir, snapshotDir, "arm64")
-	snapshotSingleton := ctx.SingletonForTests("vendor-snapshot")
-
-	var jsonFiles []string
-
-	for _, arch := range [][]string{
-		[]string{"arm64", "armv8-a"},
-		[]string{"arm", "armv7-a-neon"},
-	} {
-		archType := arch[0]
-		archVariant := arch[1]
-		archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
-
-		// For shared libraries, only non-VNDK vendor_available modules are captured
-		sharedVariant := fmt.Sprintf("android_vendor.VER_%s_%s_shared", archType, archVariant)
-		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
-		checkSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.so", sharedDir, sharedVariant)
-		checkSnapshot(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.so", sharedDir, sharedVariant)
-		jsonFiles = append(jsonFiles,
-			filepath.Join(sharedDir, "libvendor.so.json"),
-			filepath.Join(sharedDir, "libvendor_available.so.json"))
-
-		// For static libraries, all vendor:true and vendor_available modules (including VNDK) are captured.
-		staticVariant := fmt.Sprintf("android_vendor.VER_%s_%s_static", archType, archVariant)
-		staticDir := filepath.Join(snapshotVariantPath, archDir, "static")
-		checkSnapshot(t, ctx, snapshotSingleton, "libb", "libb.a", staticDir, staticVariant)
-		checkSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.a", staticDir, staticVariant)
-		checkSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.a", staticDir, staticVariant)
-		checkSnapshot(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.a", staticDir, staticVariant)
-		jsonFiles = append(jsonFiles,
-			filepath.Join(staticDir, "libb.a.json"),
-			filepath.Join(staticDir, "libvndk.a.json"),
-			filepath.Join(staticDir, "libvendor.a.json"),
-			filepath.Join(staticDir, "libvendor_available.a.json"))
-
-		// For binary executables, all vendor:true and vendor_available modules are captured.
-		if archType == "arm64" {
-			binaryVariant := fmt.Sprintf("android_vendor.VER_%s_%s", archType, archVariant)
-			binaryDir := filepath.Join(snapshotVariantPath, archDir, "binary")
-			checkSnapshot(t, ctx, snapshotSingleton, "vendor_bin", "vendor_bin", binaryDir, binaryVariant)
-			checkSnapshot(t, ctx, snapshotSingleton, "vendor_available_bin", "vendor_available_bin", binaryDir, binaryVariant)
-			jsonFiles = append(jsonFiles,
-				filepath.Join(binaryDir, "vendor_bin.json"),
-				filepath.Join(binaryDir, "vendor_available_bin.json"))
-		}
-
-		// For header libraries, all vendor:true and vendor_available modules are captured.
-		headerDir := filepath.Join(snapshotVariantPath, archDir, "header")
-		jsonFiles = append(jsonFiles, filepath.Join(headerDir, "libvendor_headers.json"))
-
-		// For object modules, all vendor:true and vendor_available modules are captured.
-		objectVariant := fmt.Sprintf("android_vendor.VER_%s_%s", archType, archVariant)
-		objectDir := filepath.Join(snapshotVariantPath, archDir, "object")
-		checkSnapshot(t, ctx, snapshotSingleton, "obj", "obj.o", objectDir, objectVariant)
-		jsonFiles = append(jsonFiles, filepath.Join(objectDir, "obj.o.json"))
-	}
-
-	for _, jsonFile := range jsonFiles {
-		// verify all json files exist
-		if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
-			t.Errorf("%q expected but not found", jsonFile)
-		}
-	}
-}
-
 func TestDoubleLoadableDepError(t *testing.T) {
 	// Check whether an error is emitted when a LLNDK depends on a non-double_loadable VNDK lib.
 	testCcError(t, "module \".*\" variant \".*\": link.* \".*\" which is not LL-NDK, VNDK-SP, .*double_loadable", `
diff --git a/cc/config/global.go b/cc/config/global.go
index 473c806..b633a72 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -15,6 +15,13 @@
 package config
 
 import (
+	"encoding/json"
+	"fmt"
+	//"io/ioutil"
+	"os"
+	//"path"
+	//"path/filepath"
+	"strconv"
 	"strings"
 
 	"android/soong/android"
@@ -112,7 +119,9 @@
 	noOverrideGlobalCflags = []string{
 		"-Werror=int-to-pointer-cast",
 		"-Werror=pointer-to-int-cast",
-		"-Werror=fortify-source",
+		// SDClang does not support -Werror=fortify-source.
+		// TODO: b/142476859
+		// "-Werror=fortify-source",
 	}
 
 	IllegalFlags = []string{
@@ -126,6 +135,10 @@
 
 	NdkMaxPrebuiltVersionInt = 27
 
+	SDClang                  = false
+	SDClangPath              = ""
+	ForceSDClangOff          = false
+
 	// prebuilts/clang default settings.
 	ClangDefaultBase         = "prebuilts/clang/host"
 	ClangDefaultVersion      = "clang-r383902b1"
@@ -212,6 +225,8 @@
 	pctx.PrefixedExistentPathsForSourcesVariable("CommonNativehelperInclude", "-I",
 		[]string{"libnativehelper/include_jni"})
 
+	setSdclangVars()
+
 	pctx.SourcePathVariable("ClangDefaultBase", ClangDefaultBase)
 	pctx.VariableFunc("ClangBase", func(ctx android.PackageVarContext) string {
 		if override := ctx.Config().Getenv("LLVM_PREBUILTS_BASE"); override != "" {
@@ -264,6 +279,149 @@
 	pctx.VariableFunc("REAbiLinkerExecStrategy", remoteexec.EnvOverrideFunc("RBE_ABI_LINKER_EXEC_STRATEGY", remoteexec.LocalExecStrategy))
 }
 
+func setSdclangVars() {
+	sdclangPath := ""
+	sdclangAEFlag := ""
+	sdclangFlags := ""
+
+	product := android.SdclangEnv["TARGET_PRODUCT"]
+	aeConfigPath := android.SdclangEnv["SDCLANG_AE_CONFIG"]
+	sdclangConfigPath := android.SdclangEnv["SDCLANG_CONFIG"]
+	sdclangSA := android.SdclangEnv["SDCLANG_SA_ENABLED"]
+
+	type sdclangAEConfig struct {
+		SDCLANG_AE_FLAG string
+	}
+
+	// Load AE config file and set AE flag
+	if file, err := os.Open(aeConfigPath); err == nil {
+		decoder := json.NewDecoder(file)
+		aeConfig := sdclangAEConfig{}
+		if err := decoder.Decode(&aeConfig); err == nil {
+			sdclangAEFlag = aeConfig.SDCLANG_AE_FLAG
+		} else {
+			panic(err)
+		}
+	}
+
+	// Load SD Clang config file and set SD Clang variables
+	var sdclangConfig interface{}
+	if file, err := os.Open(sdclangConfigPath); err == nil {
+		decoder := json.NewDecoder(file)
+                // Parse the config file
+		if err := decoder.Decode(&sdclangConfig); err == nil {
+			config := sdclangConfig.(map[string]interface{})
+			// Retrieve the default block
+			if dev, ok := config["default"]; ok {
+				devConfig := dev.(map[string]interface{})
+				// FORCE_SDCLANG_OFF is required in the default block
+				if _, ok := devConfig["FORCE_SDCLANG_OFF"]; ok {
+					ForceSDClangOff = devConfig["FORCE_SDCLANG_OFF"].(bool)
+				}
+				// SDCLANG is optional in the default block
+				if _, ok := devConfig["SDCLANG"]; ok {
+					SDClang = devConfig["SDCLANG"].(bool)
+				}
+				// SDCLANG_PATH is required in the default block
+				if _, ok := devConfig["SDCLANG_PATH"]; ok {
+					sdclangPath = devConfig["SDCLANG_PATH"].(string)
+				} else {
+					panic("SDCLANG_PATH is required in the default block")
+				}
+				// SDCLANG_FLAGS is optional in the default block
+				if _, ok := devConfig["SDCLANG_FLAGS"]; ok {
+					sdclangFlags = devConfig["SDCLANG_FLAGS"].(string)
+				}
+			} else {
+				panic("Default block is required in the SD Clang config file")
+			}
+			// Retrieve the device specific block if it exists in the config file
+			if dev, ok := config[product]; ok {
+				devConfig := dev.(map[string]interface{})
+				// SDCLANG is optional in the device specific block
+				if _, ok := devConfig["SDCLANG"]; ok {
+					SDClang = devConfig["SDCLANG"].(bool)
+				}
+				// SDCLANG_PATH is optional in the device specific block
+				if _, ok := devConfig["SDCLANG_PATH"]; ok {
+					sdclangPath = devConfig["SDCLANG_PATH"].(string)
+				}
+				// SDCLANG_FLAGS is optional in the device specific block
+				if _, ok := devConfig["SDCLANG_FLAGS"]; ok {
+					sdclangFlags = devConfig["SDCLANG_FLAGS"].(string)
+				}
+			}
+			b, _ := strconv.ParseBool(sdclangSA)
+			if(b) {
+				llvmsa_loc := "llvmsa"
+				s := []string{sdclangFlags, "--compile-and-analyze", llvmsa_loc}
+				sdclangFlags = strings.Join(s, " ")
+				fmt.Println("Clang SA is enabled: ", sdclangFlags)
+			} else {
+				fmt.Println("Clang SA is not enabled")
+			}
+		} else {
+			panic(err)
+		}
+	} else {
+		fmt.Println(err)
+	}
+
+	// Override SDCLANG if the varialbe is set in the environment
+	if sdclang := android.SdclangEnv["SDCLANG"]; sdclang != "" {
+		if override, err := strconv.ParseBool(sdclang); err == nil {
+			SDClang = override
+		}
+	}
+
+	// Sanity check SDCLANG_PATH
+	if envPath := android.SdclangEnv["SDCLANG_PATH"]; sdclangPath == "" && envPath == "" {
+		panic("SDCLANG_PATH can not be empty")
+	}
+
+	// Override SDCLANG_PATH if the variable is set in the environment
+	pctx.VariableFunc("SDClangBin", func(ctx android.PackageVarContext) string {
+		if override := ctx.Config().Getenv("SDCLANG_PATH"); override != "" {
+			return override
+		}
+		return sdclangPath
+	})
+
+	// Override SDCLANG_COMMON_FLAGS if the variable is set in the environment
+	pctx.VariableFunc("SDClangFlags", func(ctx android.PackageVarContext) string {
+		if override := ctx.Config().Getenv("SDCLANG_COMMON_FLAGS"); override != "" {
+			return override
+		}
+		return sdclangAEFlag + " " + sdclangFlags
+	})
+
+	SDClangPath = sdclangPath
+	// Find the path to SDLLVM's ASan libraries
+	// TODO (b/117846004): Disable setting SDClangAsanLibDir due to unit test path issues
+	//absPath := sdclangPath
+	//if envPath := android.SdclangEnv["SDCLANG_PATH"]; envPath != "" {
+	//	absPath = envPath
+	//}
+	//if !filepath.IsAbs(absPath) {
+	//	absPath = path.Join(androidRoot, absPath)
+	//}
+	//
+	//libDirPrefix := "../lib/clang"
+	//libDir, err := ioutil.ReadDir(path.Join(absPath, libDirPrefix))
+	//if err != nil {
+	//	libDirPrefix = "../lib64/clang"
+	//	libDir, err = ioutil.ReadDir(path.Join(absPath, libDirPrefix))
+	//}
+	//if err != nil {
+	//	panic(err)
+	//}
+	//if len(libDir) != 1 || !libDir[0].IsDir() {
+	//	panic("Failed to find sanitizer libraries")
+	//}
+	//
+	//pctx.StaticVariable("SDClangAsanLibDir", path.Join(absPath, libDirPrefix, libDir[0].Name(), "lib/linux"))
+}
+
 var HostPrebuiltTag = pctx.VariableConfigMethod("HostPrebuiltTag", android.Config.PrebuiltOS)
 
 func bionicHeaders(kernelArch string) string {
diff --git a/cc/config/vndk.go b/cc/config/vndk.go
index 6f2e807..ccf6eed 100644
--- a/cc/config/vndk.go
+++ b/cc/config/vndk.go
@@ -47,4 +47,5 @@
 	"libstagefright_xmlparser",
 	"libui",
 	"libxml2",
+	"libmedia_helper",//Remove it from the list once the workaround patch is cleared in S
 }
diff --git a/cc/config/x86_linux_host.go b/cc/config/x86_linux_host.go
index 13b5511..7e306c5 100644
--- a/cc/config/x86_linux_host.go
+++ b/cc/config/x86_linux_host.go
@@ -107,6 +107,7 @@
 		"resolv",
 		"rt",
 		"util",
+		"supc++",
 	}, "-l")
 )
 
diff --git a/cc/genrule.go b/cc/genrule.go
index 66d1784..e6ef62a 100644
--- a/cc/genrule.go
+++ b/cc/genrule.go
@@ -64,12 +64,29 @@
 	return Bool(g.Vendor_available) || !(ctx.SocSpecific() || ctx.DeviceSpecific())
 }
 
-func (g *GenruleExtraProperties) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
-	return Bool(g.Ramdisk_available)
-}
+
 
 func (g *GenruleExtraProperties) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
-	return Bool(g.Recovery_available)
+	// If the build is using a snapshot, the recovery variant under AOSP directories
+	// is not needed.
+	recoverySnapshotVersion := ctx.DeviceConfig().RecoverySnapshotVersion()
+	if recoverySnapshotVersion != "current" && recoverySnapshotVersion != "" &&
+		!isRecoveryProprietaryModule(ctx) {
+		return false
+	} else {
+		return Bool(g.Recovery_available)
+	}
+}
+func (g *GenruleExtraProperties) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+	// If the build is using a snapshot, the ramdisk variant under AOSP directories
+	// is not needed.
+	ramdiskSnapshotVersion := ctx.DeviceConfig().RamdiskSnapshotVersion()
+	if ramdiskSnapshotVersion != "current" && ramdiskSnapshotVersion != "" &&
+		!isRamdiskProprietaryModule(ctx) {
+		return false
+	} else {
+		return Bool(g.Ramdisk_available)
+	}
 }
 
 func (g *GenruleExtraProperties) ExtraImageVariations(ctx android.BaseModuleContext) []string {
@@ -84,7 +101,7 @@
 		// If not, we assume modules under proprietary paths are compatible for
 		// BOARD_VNDK_VERSION. The other modules are regarded as AOSP, that is
 		// PLATFORM_VNDK_VERSION.
-		if vndkVersion == "current" || !isVendorProprietaryPath(ctx.ModuleDir()) {
+		if vndkVersion == "current" || !isVendorProprietaryModule(ctx) {
 			variants = append(variants, VendorVariationPrefix+ctx.DeviceConfig().PlatformVndkVersion())
 		} else {
 			variants = append(variants, VendorVariationPrefix+vndkVersion)
diff --git a/cc/image.go b/cc/image.go
new file mode 100644
index 0000000..6b3b083
--- /dev/null
+++ b/cc/image.go
@@ -0,0 +1,408 @@
+// Copyright 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.
+package cc
+
+// This file contains image variant related things, including image mutator functions, utility
+// functions to determine where a module is installed, etc.
+
+import (
+	"strings"
+
+	"android/soong/android"
+)
+
+var _ android.ImageInterface = (*Module)(nil)
+
+type imageVariantType string
+
+const (
+	coreImageVariant     imageVariantType = "core"
+	vendorImageVariant   imageVariantType = "vendor"
+	productImageVariant  imageVariantType = "product"
+	ramdiskImageVariant  imageVariantType = "ramdisk"
+	recoveryImageVariant imageVariantType = "recovery"
+	hostImageVariant     imageVariantType = "host"
+)
+
+func (c *Module) getImageVariantType() imageVariantType {
+	if c.Host() {
+		return hostImageVariant
+	} else if c.inVendor() {
+		return vendorImageVariant
+	} else if c.inProduct() {
+		return productImageVariant
+	} else if c.InRamdisk() {
+		return ramdiskImageVariant
+	} else if c.InRecovery() {
+		return recoveryImageVariant
+	} else {
+		return coreImageVariant
+	}
+}
+
+const (
+	// VendorVariationPrefix is the variant prefix used for /vendor code that compiles
+	// against the VNDK.
+	VendorVariationPrefix = "vendor."
+
+	// ProductVariationPrefix is the variant prefix used for /product code that compiles
+	// against the VNDK.
+	ProductVariationPrefix = "product."
+)
+
+func (ctx *moduleContext) ProductSpecific() bool {
+	return ctx.ModuleContext.ProductSpecific() ||
+		(ctx.mod.HasVendorVariant() && ctx.mod.inProduct() && !ctx.mod.IsVndk())
+}
+
+func (ctx *moduleContext) SocSpecific() bool {
+	return ctx.ModuleContext.SocSpecific() ||
+		(ctx.mod.HasVendorVariant() && ctx.mod.inVendor() && !ctx.mod.IsVndk())
+}
+
+func (ctx *moduleContextImpl) inProduct() bool {
+	return ctx.mod.inProduct()
+}
+
+func (ctx *moduleContextImpl) inVendor() bool {
+	return ctx.mod.inVendor()
+}
+
+func (ctx *moduleContextImpl) inRamdisk() bool {
+	return ctx.mod.InRamdisk()
+}
+
+func (ctx *moduleContextImpl) inRecovery() bool {
+	return ctx.mod.InRecovery()
+}
+
+// Returns true only when this module is configured to have core, product and vendor
+// variants.
+func (c *Module) HasVendorVariant() bool {
+	return c.IsVndk() || Bool(c.VendorProperties.Vendor_available)
+}
+
+// Returns true if the module is "product" variant. Usually these modules are installed in /product
+func (c *Module) inProduct() bool {
+	return c.Properties.ImageVariationPrefix == ProductVariationPrefix
+}
+
+// Returns true if the module is "vendor" variant. Usually these modules are installed in /vendor
+func (c *Module) inVendor() bool {
+	return c.Properties.ImageVariationPrefix == VendorVariationPrefix
+}
+
+func (c *Module) InRamdisk() bool {
+	return c.ModuleBase.InRamdisk() || c.ModuleBase.InstallInRamdisk()
+}
+
+func (c *Module) InRecovery() bool {
+	return c.ModuleBase.InRecovery() || c.ModuleBase.InstallInRecovery()
+}
+
+func (c *Module) OnlyInRamdisk() bool {
+	return c.ModuleBase.InstallInRamdisk()
+}
+
+func (c *Module) OnlyInRecovery() bool {
+	return c.ModuleBase.InstallInRecovery()
+}
+
+func (m *Module) ImageMutatorBegin(mctx android.BaseModuleContext) {
+	// Validation check
+	vendorSpecific := mctx.SocSpecific() || mctx.DeviceSpecific()
+	productSpecific := mctx.ProductSpecific()
+
+	if m.VendorProperties.Vendor_available != nil && vendorSpecific {
+		mctx.PropertyErrorf("vendor_available",
+			"doesn't make sense at the same time as `vendor: true`, `proprietary: true`, or `device_specific:true`")
+	}
+
+	if vndkdep := m.vndkdep; vndkdep != nil {
+		if vndkdep.isVndk() {
+			if vendorSpecific || productSpecific {
+				if !vndkdep.isVndkExt() {
+					mctx.PropertyErrorf("vndk",
+						"must set `extends: \"...\"` to vndk extension")
+				} else if m.VendorProperties.Vendor_available != nil {
+					mctx.PropertyErrorf("vendor_available",
+						"must not set at the same time as `vndk: {extends: \"...\"}`")
+				}
+			} else {
+				if vndkdep.isVndkExt() {
+					mctx.PropertyErrorf("vndk",
+						"must set `vendor: true` or `product_specific: true` to set `extends: %q`",
+						m.getVndkExtendsModuleName())
+				}
+				if m.VendorProperties.Vendor_available == nil {
+					mctx.PropertyErrorf("vndk",
+						"vendor_available must be set to either true or false when `vndk: {enabled: true}`")
+				}
+			}
+		} else {
+			if vndkdep.isVndkSp() {
+				mctx.PropertyErrorf("vndk",
+					"must set `enabled: true` to set `support_system_process: true`")
+			}
+			if vndkdep.isVndkExt() {
+				mctx.PropertyErrorf("vndk",
+					"must set `enabled: true` to set `extends: %q`",
+					m.getVndkExtendsModuleName())
+			}
+		}
+	}
+
+	var coreVariantNeeded bool = false
+	var ramdiskVariantNeeded bool = false
+	var recoveryVariantNeeded bool = false
+
+	var vendorVariants []string
+	var productVariants []string
+
+	platformVndkVersion := mctx.DeviceConfig().PlatformVndkVersion()
+	boardVndkVersion := mctx.DeviceConfig().VndkVersion()
+	productVndkVersion := mctx.DeviceConfig().ProductVndkVersion()
+	recoverySnapshotVersion := mctx.DeviceConfig().RecoverySnapshotVersion()
+	usingRecoverySnapshot := recoverySnapshotVersion != "current" &&
+		recoverySnapshotVersion != ""
+	ramdiskSnapshotVersion := mctx.DeviceConfig().RamdiskSnapshotVersion()
+	usingRamdiskSnapshot := ramdiskSnapshotVersion != "current" &&
+		ramdiskSnapshotVersion != ""
+	if boardVndkVersion == "current" {
+		boardVndkVersion = platformVndkVersion
+	}
+	if productVndkVersion == "current" {
+		productVndkVersion = platformVndkVersion
+	}
+	usingVendorSnapshot := boardVndkVersion != platformVndkVersion
+
+	if boardVndkVersion == "" {
+		// If the device isn't compiling against the VNDK, we always
+		// use the core mode.
+		coreVariantNeeded = true
+	} else if _, ok := m.linker.(*llndkStubDecorator); ok {
+		// LL-NDK stubs only exist in the vendor and product variants,
+		// since the real libraries will be used in the core variant.
+
+		if usingVendorSnapshot {
+			// If we're using the vendor snapshot, avoid providing
+			// the boardVndkVersion. This requires that the vendor
+			// snapshot provide the library instead, which happens
+			// below as a snapshot prebuilt.
+			vendorVariants = append(vendorVariants,
+				platformVndkVersion,
+			)
+		} else {
+			vendorVariants = append(vendorVariants,
+				platformVndkVersion,
+				boardVndkVersion,
+			)
+		}
+
+		productVariants = append(productVariants,
+			platformVndkVersion,
+			productVndkVersion,
+		)
+	} else if _, ok := m.linker.(*llndkHeadersDecorator); ok {
+		// ... and LL-NDK headers as well
+
+		if usingVendorSnapshot {
+			// If we're using the vendor snapshot, avoid providing
+			// the boardVndkVersion. This requires that the vendor
+			// snapshot provide the headers instead, which happens
+			// below as a snapshot prebuilt.
+			vendorVariants = append(vendorVariants,
+				platformVndkVersion,
+			)
+		} else {
+			vendorVariants = append(vendorVariants,
+				platformVndkVersion,
+				boardVndkVersion,
+			)
+		}
+
+		productVariants = append(productVariants,
+			platformVndkVersion,
+			productVndkVersion,
+		)
+	} else if m.isSnapshotPrebuilt() {
+		// Make vendor variants only for the versions in BOARD_VNDK_VERSION and
+		// PRODUCT_EXTRA_VNDK_VERSIONS.
+		if snapshot, ok := m.linker.(interface {
+			version() string
+		}); ok {
+			if m.InstallInRecovery() {
+				recoveryVariantNeeded = true
+			} else if m.InstallInRamdisk() {
+				ramdiskVariantNeeded = true
+			} else {
+				vendorVariants = append(vendorVariants, snapshot.version())
+			}
+		} else {
+			mctx.ModuleErrorf("version is unknown for snapshot prebuilt")
+		}
+	} else if m.HasVendorVariant() && !m.isVndkExt() {
+		// This will be available in /system, /vendor and /product
+		// or a /system directory that is available to vendor and product.
+		coreVariantNeeded = true
+
+		// We assume that modules under proprietary paths are compatible for
+		// BOARD_VNDK_VERSION. The other modules are regarded as AOSP, or
+		// PLATFORM_VNDK_VERSION.
+		if isVendorProprietaryModule(mctx) {
+			vendorVariants = append(vendorVariants, boardVndkVersion)
+		} else {
+			vendorVariants = append(vendorVariants, platformVndkVersion)
+		}
+
+		// vendor_available modules are also available to /product.
+		productVariants = append(productVariants, platformVndkVersion)
+		// VNDK is always PLATFORM_VNDK_VERSION
+		if !m.IsVndk() {
+			productVariants = append(productVariants, productVndkVersion)
+		}
+	} else if vendorSpecific && String(m.Properties.Sdk_version) == "" {
+		// This will be available in /vendor (or /odm) only
+
+		// kernel_headers is a special module type whose exported headers
+		// are coming from DeviceKernelHeaders() which is always vendor
+		// dependent. They'll always have both vendor variants.
+		// For other modules, we assume that modules under proprietary
+		// paths are compatible for BOARD_VNDK_VERSION. The other modules
+		// are regarded as AOSP, which is PLATFORM_VNDK_VERSION.
+		if _, ok := m.linker.(*kernelHeadersDecorator); ok {
+			vendorVariants = append(vendorVariants,
+				platformVndkVersion,
+				boardVndkVersion,
+			)
+		} else if isVendorProprietaryModule(mctx) {
+			vendorVariants = append(vendorVariants, boardVndkVersion)
+		} else {
+			vendorVariants = append(vendorVariants, platformVndkVersion)
+		}
+	} else {
+		// This is either in /system (or similar: /data), or is a
+		// modules built with the NDK. Modules built with the NDK
+		// will be restricted using the existing link type checks.
+		coreVariantNeeded = true
+	}
+
+	if boardVndkVersion != "" && productVndkVersion != "" {
+		if coreVariantNeeded && productSpecific && String(m.Properties.Sdk_version) == "" {
+			// The module has "product_specific: true" that does not create core variant.
+			coreVariantNeeded = false
+			productVariants = append(productVariants, productVndkVersion)
+		}
+	} else {
+		// Unless PRODUCT_PRODUCT_VNDK_VERSION is set, product partition has no
+		// restriction to use system libs.
+		// No product variants defined in this case.
+		productVariants = []string{}
+	}
+
+	if Bool(m.Properties.Ramdisk_available) {
+		ramdiskVariantNeeded = true
+	}
+
+	if m.ModuleBase.InstallInRamdisk() {
+		ramdiskVariantNeeded = true
+		coreVariantNeeded = false
+	}
+
+	if Bool(m.Properties.Recovery_available) {
+		recoveryVariantNeeded = true
+	}
+
+	if m.ModuleBase.InstallInRecovery() {
+		recoveryVariantNeeded = true
+		coreVariantNeeded = false
+	}
+
+	// If using a snapshot, the recovery variant under AOSP directories is not needed,
+	// except for kernel headers, which needs all variants.
+	if _, ok := m.linker.(*kernelHeadersDecorator); !ok &&
+		!m.isSnapshotPrebuilt() &&
+		usingRecoverySnapshot &&
+		!isRecoveryProprietaryModule(mctx) {
+		recoveryVariantNeeded = false
+	}
+	if _, ok := m.linker.(*kernelHeadersDecorator); !ok &&
+		!m.isSnapshotPrebuilt() &&
+		usingRamdiskSnapshot &&
+		!isRamdiskProprietaryModule(mctx) {
+		ramdiskVariantNeeded = false
+	}
+
+	for _, variant := range android.FirstUniqueStrings(vendorVariants) {
+		m.Properties.ExtraVariants = append(m.Properties.ExtraVariants, VendorVariationPrefix+variant)
+	}
+
+	for _, variant := range android.FirstUniqueStrings(productVariants) {
+		m.Properties.ExtraVariants = append(m.Properties.ExtraVariants, ProductVariationPrefix+variant)
+	}
+
+	m.Properties.RamdiskVariantNeeded = ramdiskVariantNeeded
+	m.Properties.RecoveryVariantNeeded = recoveryVariantNeeded
+	m.Properties.CoreVariantNeeded = coreVariantNeeded
+
+	// Disable the module if no variants are needed.
+	if !ramdiskVariantNeeded &&
+		!recoveryVariantNeeded &&
+		!coreVariantNeeded &&
+		len(m.Properties.ExtraVariants) == 0 {
+		m.Disable()
+	}
+}
+
+func (c *Module) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
+	return c.Properties.CoreVariantNeeded
+}
+
+func (c *Module) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+	return c.Properties.RamdiskVariantNeeded
+}
+
+func (c *Module) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
+	return c.Properties.RecoveryVariantNeeded
+}
+
+func (c *Module) ExtraImageVariations(ctx android.BaseModuleContext) []string {
+	return c.Properties.ExtraVariants
+}
+
+func (c *Module) SetImageVariation(ctx android.BaseModuleContext, variant string, module android.Module) {
+	m := module.(*Module)
+	if variant == android.RamdiskVariation {
+		m.MakeAsPlatform()
+	} else if variant == android.RecoveryVariation {
+		m.MakeAsPlatform()
+		squashRecoverySrcs(m)
+	} else if strings.HasPrefix(variant, VendorVariationPrefix) {
+		m.Properties.ImageVariationPrefix = VendorVariationPrefix
+		m.Properties.VndkVersion = strings.TrimPrefix(variant, VendorVariationPrefix)
+		squashVendorSrcs(m)
+
+		// Makefile shouldn't know vendor modules other than BOARD_VNDK_VERSION.
+		// Hide other vendor variants to avoid collision.
+		vndkVersion := ctx.DeviceConfig().VndkVersion()
+		if vndkVersion != "current" && vndkVersion != "" && vndkVersion != m.Properties.VndkVersion {
+			m.Properties.HideFromMake = true
+			m.SkipInstall()
+		}
+	} else if strings.HasPrefix(variant, ProductVariationPrefix) {
+		m.Properties.ImageVariationPrefix = ProductVariationPrefix
+		m.Properties.VndkVersion = strings.TrimPrefix(variant, ProductVariationPrefix)
+		squashVendorSrcs(m)
+	}
+}
diff --git a/cc/library.go b/cc/library.go
index 3deb173..147865b 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -1073,9 +1073,14 @@
 
 func (library *libraryDecorator) linkSAbiDumpFiles(ctx ModuleContext, objs Objects, fileName string, soFile android.Path) {
 	if library.shouldCreateSourceAbiDump(ctx) {
-		vndkVersion := ctx.DeviceConfig().PlatformVndkVersion()
-		if ver := ctx.DeviceConfig().VndkVersion(); ver != "" && ver != "current" {
-			vndkVersion = ver
+		var vndkVersion string
+
+		if ctx.useVndk() {
+			// For modules linking against vndk, follow its vndk version
+			vndkVersion = ctx.Module().(*Module).VndkVersion()
+		} else {
+			// Regard the other modules as PLATFORM_VNDK_VERSION
+			vndkVersion = ctx.DeviceConfig().PlatformVndkVersion()
 		}
 
 		exportIncludeDirs := library.flagExporter.exportedIncludes(ctx)
@@ -1560,9 +1565,8 @@
 func VersionVariantAvailable(module interface {
 	Host() bool
 	InRamdisk() bool
-	InRecovery() bool
 }) bool {
-	return !module.Host() && !module.InRamdisk() && !module.InRecovery()
+	return !module.Host() && !module.InRamdisk()
 }
 
 // VersionMutator splits a module into the mandatory non-stubs variant
diff --git a/cc/llndk_library.go b/cc/llndk_library.go
index 7ff20f4..19f5641 100644
--- a/cc/llndk_library.go
+++ b/cc/llndk_library.go
@@ -116,6 +116,7 @@
 	srcDir := android.PathForModuleSrc(ctx, srcHeaderDir)
 	srcFiles := ctx.GlobFiles(filepath.Join(srcDir.String(), "**/*.h"), nil)
 
+	var depPaths []android.Path
 	var installPaths []android.WritablePath
 	for _, header := range srcFiles {
 		headerDir := filepath.Dir(header.String())
@@ -126,9 +127,14 @@
 			continue
 		}
 
-		installPaths = append(installPaths, outDir.Join(ctx, relHeaderDir, header.Base()))
+		outputPath := outDir.Join(ctx, relHeaderDir, header.Base())
+		depPaths = append(depPaths, outputPath)
+		installPaths = append(installPaths, outputPath)
 	}
 
+	// Add the processed, exported headers so that they can be collected in
+	// a snapshot later, if necessary.
+	stub.addExportedGeneratedHeaders(depPaths...)
 	return processHeadersWithVersioner(ctx, srcDir, outDir, srcFiles, installPaths)
 }
 
@@ -225,6 +231,8 @@
 func llndkHeadersFactory() android.Module {
 	module, library := NewLibrary(android.DeviceSupported)
 	library.HeaderOnly()
+	module.stl = nil
+	module.sanitize = nil
 
 	decorator := &llndkHeadersDecorator{
 		libraryDecorator: library,
diff --git a/cc/lto.go b/cc/lto.go
index 4489fc7..0ac3ccc 100644
--- a/cc/lto.go
+++ b/cc/lto.go
@@ -16,6 +16,8 @@
 
 import (
 	"android/soong/android"
+	"android/soong/cc/config"
+	"strings"
 )
 
 // LTO (link-time optimization) allows the compiler to optimize and generate
@@ -89,7 +91,13 @@
 	if lto.LTO() {
 		var ltoFlag string
 		if Bool(lto.Properties.Lto.Thin) {
-			ltoFlag = "-flto=thin -fsplit-lto-unit"
+			// TODO(b/129607781) sdclang does not currently support
+			// the "-fsplit-lto-unit" option
+			if flags.Sdclang && !strings.Contains(config.SDClangPath, "9.0") {
+				ltoFlag = "-flto=thin"
+			} else {
+				ltoFlag = "-flto=thin -fsplit-lto-unit"
+			}
 		} else {
 			ltoFlag = "-flto"
 		}
diff --git a/cc/makevars.go b/cc/makevars.go
index 0f9f4c1..3c8c355 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"sort"
+	"strconv"
 	"strings"
 	"sync"
 
@@ -71,6 +72,7 @@
 }
 
 func makeVarsProvider(ctx android.MakeVarsContext) {
+	sdclangMakeVars(ctx)
 	vendorPublicLibraries := vendorPublicLibraries(ctx.Config())
 
 	ctx.Strict("LLVM_RELEASE_VERSION", "${config.ClangShortVersion}")
@@ -100,6 +102,8 @@
 	ctx.Strict("NDK_PREBUILT_SHARED_LIBRARIES", strings.Join(ndkPrebuiltSharedLibs, " "))
 
 	ctx.Strict("BOARD_VNDK_VERSION", ctx.DeviceConfig().VndkVersion())
+	ctx.Strict("RECOVERY_SNAPSHOT_VERSION", ctx.DeviceConfig().RecoverySnapshotVersion())
+	ctx.Strict("RAMDISK_SNAPSHOT_VERSION", ctx.DeviceConfig().RamdiskSnapshotVersion())
 
 	// Filter vendor_public_library that are exported to make
 	exportedVendorPublicLibraries := []string{}
@@ -190,6 +194,17 @@
 	}
 }
 
+func sdclangMakeVars(ctx android.MakeVarsContext) {
+	if config.ForceSDClangOff {
+		ctx.Strict("FORCE_SDCLANG_OFF", strconv.FormatBool(config.ForceSDClangOff))
+	}
+	if config.SDClang {
+		ctx.Strict("SDCLANG", strconv.FormatBool(config.SDClang))
+	}
+	ctx.Strict("SDCLANG_PATH", "${config.SDClangBin}")
+	ctx.Strict("SDCLANG_COMMON_FLAGS", "${config.SDClangFlags}")
+}
+
 func makeVarsToolchain(ctx android.MakeVarsContext, secondPrefix string,
 	target android.Target) {
 	var typePrefix string
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 463a02a..54020e8 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -61,9 +61,7 @@
 	cfiAsflags = []string{"-flto", "-fvisibility=default"}
 	cfiLdflags = []string{"-flto", "-fsanitize-cfi-cross-dso", "-fsanitize=cfi",
 		"-Wl,-plugin-opt,O1"}
-	cfiExportsMapPath     = "build/soong/cc/config/cfi_exports.map"
-	cfiStaticLibsMutex    sync.Mutex
-	hwasanStaticLibsMutex sync.Mutex
+	cfiExportsMapPath = "build/soong/cc/config/cfi_exports.map"
 
 	intOverflowCflags = []string{"-fsanitize-blacklist=build/soong/cc/config/integer_overflow_blacklist.txt"}
 
@@ -303,6 +301,35 @@
 		}
 	}
 
+	if s.Integer_overflow == nil && ctx.Config().IntegerOverflowEnabledForPath(ctx.ModuleDir()) && ctx.Arch().ArchType == android.Arm64 {
+		s.Integer_overflow = boolPtr(true)
+	}
+
+	if  ctx.Config().BoundSanitizerEnabledForPath(ctx.ModuleDir()) && ctx.Arch().ArchType == android.Arm64 {
+		s.Misc_undefined = append(s.Misc_undefined, "bounds")
+	}
+
+	if ctx.Config().BoundSanitizerDisabledForPath(ctx.ModuleDir()) && ctx.Arch().ArchType == android.Arm64 {
+		indx := indexList("bounds", s.Misc_undefined)
+		if (indexList("bounds", s.Misc_undefined) != -1) {
+			s.Misc_undefined = append(s.Misc_undefined[0:indx], s.Misc_undefined[indx+1:]...)
+		}
+	}
+
+    // Disable integer-overflow in exclude path
+	if ctx.Config().IntegerOverflowDisabledForPath(ctx.ModuleDir()) && ctx.Arch().ArchType == android.Arm64 {
+		indx := indexList("signed-integer-overflow", s.Misc_undefined)
+		if (indexList("signed-integer-overflow", s.Misc_undefined) != -1) {
+			s.Misc_undefined = append(s.Misc_undefined[0:indx], s.Misc_undefined[indx+1:]...)
+		}
+
+		indx = indexList("unsigned-integer-overflow", s.Misc_undefined)
+		if (indexList("unsigned-integer-overflow", s.Misc_undefined) != -1) {
+			s.Misc_undefined = append(s.Misc_undefined[0:indx], s.Misc_undefined[indx+1:]...)
+		}
+        s.Integer_overflow = nil
+	}
+
 	// Enable CFI for all components in the include paths (for Aarch64 only)
 	if s.Cfi == nil && ctx.Config().CFIEnabledForPath(ctx.ModuleDir()) && ctx.Arch().ArchType == android.Arm64 {
 		s.Cfi = boolPtr(true)
@@ -310,17 +337,24 @@
 			s.Diag.Cfi = boolPtr(true)
 		}
 	}
+        // Disable CFI for all component in the exclude path (for Aarch64 only)
+	if ctx.Config().CFIDisabledForPath(ctx.ModuleDir()) && ctx.Arch().ArchType == android.Arm64 {
+		s.Cfi = nil
+		if inList("cfi", ctx.Config().SanitizeDeviceDiag()) {
+			s.Diag.Cfi = nil
+		}
+	}
 
 	// CFI needs gold linker, and mips toolchain does not have one.
 	if !ctx.Config().EnableCFI() || ctx.Arch().ArchType == android.Mips || ctx.Arch().ArchType == android.Mips64 {
-		s.Cfi = nil
-		s.Diag.Cfi = nil
+		s.Cfi = boolPtr(false)
+		s.Diag.Cfi = boolPtr(false)
 	}
 
 	// Also disable CFI for arm32 until b/35157333 is fixed.
 	if ctx.Arch().ArchType == android.Arm {
-		s.Cfi = nil
-		s.Diag.Cfi = nil
+		s.Cfi = boolPtr(false)
+		s.Diag.Cfi = boolPtr(false)
 	}
 
 	// HWASan requires AArch64 hardware feature (top-byte-ignore).
@@ -335,14 +369,14 @@
 
 	// Also disable CFI if ASAN is enabled.
 	if Bool(s.Address) || Bool(s.Hwaddress) {
-		s.Cfi = nil
-		s.Diag.Cfi = nil
+		s.Cfi = boolPtr(false)
+		s.Diag.Cfi = boolPtr(false)
 	}
 
 	// Disable sanitizers that depend on the UBSan runtime for windows/darwin builds.
 	if !ctx.Os().Linux() {
-		s.Cfi = nil
-		s.Diag.Cfi = nil
+		s.Cfi = boolPtr(false)
+		s.Diag.Cfi = boolPtr(false)
 		s.Misc_undefined = nil
 		s.Undefined = nil
 		s.All_undefined = nil
@@ -351,14 +385,15 @@
 
 	// Also disable CFI for VNDK variants of components
 	if ctx.isVndk() && ctx.useVndk() {
-		s.Cfi = nil
-		s.Diag.Cfi = nil
-	}
-
-	// Also disable CFI if building against snapshot.
-	vndkVersion := ctx.DeviceConfig().VndkVersion()
-	if ctx.useVndk() && vndkVersion != "current" && vndkVersion != "" {
-		s.Cfi = nil
+		if ctx.static() {
+			// Cfi variant for static vndk should be captured as vendor snapshot,
+			// so don't strictly disable Cfi.
+			s.Cfi = nil
+			s.Diag.Cfi = nil
+		} else {
+			s.Cfi = boolPtr(false)
+			s.Diag.Cfi = boolPtr(false)
+		}
 	}
 
 	// HWASan ramdisk (which is built from recovery) goes over some bootloader limit.
@@ -403,7 +438,7 @@
 	// TODO(b/131771163): CFI transiently depends on LTO, and thus Fuzzer is
 	// mutually incompatible.
 	if Bool(s.Fuzzer) {
-		s.Cfi = nil
+		s.Cfi = boolPtr(false)
 	}
 }
 
@@ -724,27 +759,64 @@
 	return ok && t.Library || t == reuseObjTag || t == objDepTag
 }
 
+// Determines if the current module is a static library going to be captured
+// as vendor snapshot. Such modules must create both cfi and non-cfi variants,
+// except for ones which explicitly disable cfi.
+func needsCfiForVendorSnapshot(mctx android.TopDownMutatorContext) bool {
+	if isVendorProprietaryModule(mctx) {
+		return false
+	}
+
+	c := mctx.Module().(*Module)
+
+	if !c.inVendor() {
+		return false
+	}
+
+	if !c.static() {
+		return false
+	}
+
+	if c.Prebuilt() != nil {
+		return false
+	}
+
+	return c.sanitize != nil &&
+		!Bool(c.sanitize.Properties.Sanitize.Never) &&
+		!c.sanitize.isSanitizerExplicitlyDisabled(cfi)
+}
+
 // Propagate sanitizer requirements down from binaries
 func sanitizerDepsMutator(t sanitizerType) func(android.TopDownMutatorContext) {
 	return func(mctx android.TopDownMutatorContext) {
-		if c, ok := mctx.Module().(*Module); ok && c.sanitize.isSanitizerEnabled(t) {
-			mctx.WalkDeps(func(child, parent android.Module) bool {
-				if !isSanitizableDependencyTag(mctx.OtherModuleDependencyTag(child)) {
-					return false
-				}
-				if d, ok := child.(*Module); ok && d.sanitize != nil &&
-					!Bool(d.sanitize.Properties.Sanitize.Never) &&
-					!d.sanitize.isSanitizerExplicitlyDisabled(t) {
-					if t == cfi || t == hwasan || t == scs {
-						if d.static() {
+		if c, ok := mctx.Module().(*Module); ok {
+			enabled := c.sanitize.isSanitizerEnabled(t)
+			if t == cfi && needsCfiForVendorSnapshot(mctx) {
+				// We shouldn't change the result of isSanitizerEnabled(cfi) to correctly
+				// determine defaultVariation in sanitizerMutator below.
+				// Instead, just mark SanitizeDep to forcefully create cfi variant.
+				enabled = true
+				c.sanitize.Properties.SanitizeDep = true
+			}
+			if enabled {
+				mctx.WalkDeps(func(child, parent android.Module) bool {
+					if !isSanitizableDependencyTag(mctx.OtherModuleDependencyTag(child)) {
+						return false
+					}
+					if d, ok := child.(*Module); ok && d.sanitize != nil &&
+						!Bool(d.sanitize.Properties.Sanitize.Never) &&
+						!d.sanitize.isSanitizerExplicitlyDisabled(t) {
+						if t == cfi || t == hwasan || t == scs {
+							if d.static() {
+								d.sanitize.Properties.SanitizeDep = true
+							}
+						} else {
 							d.sanitize.Properties.SanitizeDep = true
 						}
-					} else {
-						d.sanitize.Properties.SanitizeDep = true
 					}
-				}
-				return true
-			})
+					return true
+				})
+			}
 		} else if sanitizeable, ok := mctx.Module().(Sanitizeable); ok {
 			// If an APEX module includes a lib which is enabled for a sanitizer T, then
 			// the APEX module is also enabled for the same sanitizer type.
@@ -793,7 +865,7 @@
 				return true
 			}
 
-			if p, ok := d.linker.(*vendorSnapshotLibraryDecorator); ok {
+			if p, ok := d.linker.(*snapshotLibraryDecorator); ok {
 				if Bool(p.properties.Sanitize_minimal_dep) {
 					c.sanitize.Properties.MinimalRuntimeDep = true
 				}
@@ -1047,15 +1119,9 @@
 					// Export the static lib name to make
 					if c.static() && c.ExportedToMake() {
 						if t == cfi {
-							appendStringSync(c.Name(), cfiStaticLibs(mctx.Config()), &cfiStaticLibsMutex)
+							cfiStaticLibs(mctx.Config()).add(c, c.Name())
 						} else if t == hwasan {
-							if c.UseVndk() {
-								appendStringSync(c.Name(), hwasanVendorStaticLibs(mctx.Config()),
-									&hwasanStaticLibsMutex)
-							} else {
-								appendStringSync(c.Name(), hwasanStaticLibs(mctx.Config()),
-									&hwasanStaticLibsMutex)
-							}
+							hwasanStaticLibs(mctx.Config()).add(c, c.Name())
 						}
 					}
 				} else {
@@ -1080,38 +1146,96 @@
 		} else if sanitizeable, ok := mctx.Module().(Sanitizeable); ok && sanitizeable.IsSanitizerEnabled(mctx, t.name()) {
 			// APEX modules fall here
 			mctx.CreateVariations(t.variationName())
+		} else if c, ok := mctx.Module().(*Module); ok {
+			// Check if it's a snapshot module supporting sanitizer
+			if s, ok := c.linker.(snapshotSanitizer); ok && s.isSanitizerEnabled(t) {
+				// Set default variation as above.
+				defaultVariation := t.variationName()
+				mctx.SetDefaultDependencyVariation(&defaultVariation)
+				modules := mctx.CreateVariations("", t.variationName())
+				modules[0].(*Module).linker.(snapshotSanitizer).setSanitizerVariation(t, false)
+				modules[1].(*Module).linker.(snapshotSanitizer).setSanitizerVariation(t, true)
+
+				// Export the static lib name to make
+				if c.static() && c.ExportedToMake() {
+					if t == cfi {
+						// use BaseModuleName which is the name for Make.
+						cfiStaticLibs(mctx.Config()).add(c, c.BaseModuleName())
+					}
+				}
+			}
+		}
+	}
+}
+
+type sanitizerStaticLibsMap struct {
+	// libsMap contains one list of modules per each image and each arch.
+	// e.g. libs[vendor]["arm"] contains arm modules installed to vendor
+	libsMap       map[imageVariantType]map[string][]string
+	libsMapLock   sync.Mutex
+	sanitizerType sanitizerType
+}
+
+func newSanitizerStaticLibsMap(t sanitizerType) *sanitizerStaticLibsMap {
+	return &sanitizerStaticLibsMap{
+		sanitizerType: t,
+		libsMap:       make(map[imageVariantType]map[string][]string),
+	}
+}
+
+// Add the current module to sanitizer static libs maps
+// Each module should pass its exported name as names of Make and Soong can differ.
+func (s *sanitizerStaticLibsMap) add(c *Module, name string) {
+	image := c.getImageVariantType()
+	arch := c.Arch().ArchType.String()
+
+	s.libsMapLock.Lock()
+	defer s.libsMapLock.Unlock()
+
+	if _, ok := s.libsMap[image]; !ok {
+		s.libsMap[image] = make(map[string][]string)
+	}
+
+	s.libsMap[image][arch] = append(s.libsMap[image][arch], name)
+}
+
+// Exports makefile variables in the following format:
+// SOONG_{sanitizer}_{image}_{arch}_STATIC_LIBRARIES
+// e.g. SOONG_cfi_core_x86_STATIC_LIBRARIES
+// These are to be used by use_soong_sanitized_static_libraries.
+// See build/make/core/binary.mk for more details.
+func (s *sanitizerStaticLibsMap) exportToMake(ctx android.MakeVarsContext) {
+	for _, image := range android.SortedStringKeys(s.libsMap) {
+		archMap := s.libsMap[imageVariantType(image)]
+		for _, arch := range android.SortedStringKeys(archMap) {
+			libs := archMap[arch]
+			sort.Strings(libs)
+
+			key := fmt.Sprintf(
+				"SOONG_%s_%s_%s_STATIC_LIBRARIES",
+				s.sanitizerType.variationName(),
+				image, // already upper
+				arch)
+
+			ctx.Strict(key, strings.Join(libs, " "))
 		}
 	}
 }
 
 var cfiStaticLibsKey = android.NewOnceKey("cfiStaticLibs")
 
-func cfiStaticLibs(config android.Config) *[]string {
+func cfiStaticLibs(config android.Config) *sanitizerStaticLibsMap {
 	return config.Once(cfiStaticLibsKey, func() interface{} {
-		return &[]string{}
-	}).(*[]string)
+		return newSanitizerStaticLibsMap(cfi)
+	}).(*sanitizerStaticLibsMap)
 }
 
 var hwasanStaticLibsKey = android.NewOnceKey("hwasanStaticLibs")
 
-func hwasanStaticLibs(config android.Config) *[]string {
+func hwasanStaticLibs(config android.Config) *sanitizerStaticLibsMap {
 	return config.Once(hwasanStaticLibsKey, func() interface{} {
-		return &[]string{}
-	}).(*[]string)
-}
-
-var hwasanVendorStaticLibsKey = android.NewOnceKey("hwasanVendorStaticLibs")
-
-func hwasanVendorStaticLibs(config android.Config) *[]string {
-	return config.Once(hwasanVendorStaticLibsKey, func() interface{} {
-		return &[]string{}
-	}).(*[]string)
-}
-
-func appendStringSync(item string, list *[]string, mutex *sync.Mutex) {
-	mutex.Lock()
-	*list = append(*list, item)
-	mutex.Unlock()
+		return newSanitizerStaticLibsMap(hwasan)
+	}).(*sanitizerStaticLibsMap)
 }
 
 func enableMinimalRuntime(sanitize *sanitize) bool {
@@ -1141,17 +1265,9 @@
 }
 
 func cfiMakeVarsProvider(ctx android.MakeVarsContext) {
-	cfiStaticLibs := cfiStaticLibs(ctx.Config())
-	sort.Strings(*cfiStaticLibs)
-	ctx.Strict("SOONG_CFI_STATIC_LIBRARIES", strings.Join(*cfiStaticLibs, " "))
+	cfiStaticLibs(ctx.Config()).exportToMake(ctx)
 }
 
 func hwasanMakeVarsProvider(ctx android.MakeVarsContext) {
-	hwasanStaticLibs := hwasanStaticLibs(ctx.Config())
-	sort.Strings(*hwasanStaticLibs)
-	ctx.Strict("SOONG_HWASAN_STATIC_LIBRARIES", strings.Join(*hwasanStaticLibs, " "))
-
-	hwasanVendorStaticLibs := hwasanVendorStaticLibs(ctx.Config())
-	sort.Strings(*hwasanVendorStaticLibs)
-	ctx.Strict("SOONG_HWASAN_VENDOR_STATIC_LIBRARIES", strings.Join(*hwasanVendorStaticLibs, " "))
+	hwasanStaticLibs(ctx.Config()).exportToMake(ctx)
 }
diff --git a/cc/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go
new file mode 100644
index 0000000..5989dd8
--- /dev/null
+++ b/cc/snapshot_prebuilt.go
@@ -0,0 +1,1310 @@
+// Copyright 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.
+package cc
+
+// This file defines snapshot prebuilt modules, e.g. vendor snapshot and recovery snapshot. Such
+// snapshot modules will override original source modules with setting BOARD_VNDK_VERSION, with
+// snapshot mutators and snapshot information maps which are also defined in this file.
+
+import (
+	"path/filepath"
+	"strings"
+	"sync"
+
+	"android/soong/android"
+
+	"github.com/google/blueprint/proptools"
+)
+
+// Defines the specifics of different images to which the snapshot process is applicable, e.g.,
+// vendor, recovery, ramdisk.
+type snapshotImage interface {
+	// Used to register callbacks with the build system.
+	init()
+
+	// Returns true if a snapshot should be generated for this image.
+	shouldGenerateSnapshot(ctx android.SingletonContext) bool
+
+	// Function that returns true if the module is included in this image.
+	// Using a function return instead of a value to prevent early
+	// evalution of a function that may be not be defined.
+	inImage(m *Module) func() bool
+
+	// Returns the value of the "available" property for a given module for
+	// and snapshot, e.g., "vendor_available", "recovery_available", etc.
+	// or nil if the property is not defined.
+	available(m *Module) *bool
+
+	// Returns true if a dir under source tree is an SoC-owned proprietary
+	// directory, such as device/, vendor/, etc.
+	//
+	// For a given snapshot (e.g., vendor, recovery, etc.) if
+	// isProprietaryPath(dir, deviceConfig) returns true, then the module in dir
+	// will be built from sources.
+	isProprietaryPath(dir string, deviceConfig android.DeviceConfig) bool
+
+	// Whether to include VNDK in the snapshot for this image.
+	includeVndk() bool
+
+	// Whether a given module has been explicitly excluded from the
+	// snapshot, e.g., using the exclude_from_vendor_snapshot or
+	// exclude_from_recovery_snapshot properties.
+	excludeFromSnapshot(m *Module) bool
+
+	// Returns the snapshotMap to be used for a given module and config, or nil if the
+	// module is not included in this image.
+	getSnapshotMap(m *Module, cfg android.Config) *snapshotMap
+
+	// Returns mutex used for mutual exclusion when updating the snapshot maps.
+	getMutex() *sync.Mutex
+
+	// For a given arch, a maps of which modules are included in this image.
+	suffixModules(config android.Config) map[string]bool
+
+	// Whether to add a given module to the suffix map.
+	shouldBeAddedToSuffixModules(m *Module) bool
+
+	// Returns true if the build is using a snapshot for this image.
+	isUsingSnapshot(cfg android.DeviceConfig) bool
+
+	// Whether to skip the module mutator for a module in a given context.
+	skipModuleMutator(ctx android.BottomUpMutatorContext) bool
+
+	// Whether to skip the source mutator for a given module.
+	skipSourceMutator(ctx android.BottomUpMutatorContext) bool
+
+	// Whether to exclude a given module from the directed snapshot or not.
+	// If the makefile variable DIRECTED_{IMAGE}_SNAPSHOT is true, directed snapshot is turned on,
+	// and only modules listed in {IMAGE}_SNAPSHOT_MODULES will be captured.
+	excludeFromDirectedSnapshot(cfg android.DeviceConfig, name string) bool
+}
+
+type vendorSnapshotImage struct{}
+type recoverySnapshotImage struct{}
+type ramdiskSnapshotImage struct{}
+
+type directoryMap map[string]bool
+
+var (
+	// Modules under following directories are ignored. They are OEM's and vendor's
+	// proprietary modules(device/, kernel/, vendor/, and hardware/).
+	defaultDirectoryExcludedMap = directoryMap{
+		"device":   true,
+		"hardware": true,
+		"kernel":   true,
+		"vendor":   true,
+		// QC specific directories to be ignored
+		"disregard": true,
+	}
+
+	// Modules under following directories are included as they are in AOSP,
+	// although hardware/ and kernel/ are normally for vendor's own.
+	defaultDirectoryIncludedMap = directoryMap{
+		"kernel/configs":              true,
+		"kernel/prebuilts":            true,
+		"kernel/tests":                true,
+		"hardware/interfaces":         true,
+		"hardware/libhardware":        true,
+		"hardware/libhardware_legacy": true,
+		"hardware/ril":                true,
+	}
+)
+
+func (vendorSnapshotImage) init() {
+	android.RegisterSingletonType("vendor-snapshot", VendorSnapshotSingleton)
+	android.RegisterModuleType("vendor_snapshot_shared", VendorSnapshotSharedFactory)
+	android.RegisterModuleType("vendor_snapshot_static", VendorSnapshotStaticFactory)
+	android.RegisterModuleType("vendor_snapshot_header", VendorSnapshotHeaderFactory)
+	android.RegisterModuleType("vendor_snapshot_binary", VendorSnapshotBinaryFactory)
+	android.RegisterModuleType("vendor_snapshot_object", VendorSnapshotObjectFactory)
+
+	android.RegisterSingletonType("vendor-fake-snapshot", VendorFakeSnapshotSingleton)
+}
+
+func (vendorSnapshotImage) shouldGenerateSnapshot(ctx android.SingletonContext) bool {
+	// BOARD_VNDK_VERSION must be set to 'current' in order to generate a snapshot.
+	return ctx.DeviceConfig().VndkVersion() == "current"
+}
+
+func (vendorSnapshotImage) inImage(m *Module) func() bool {
+	return m.inVendor
+}
+
+func (vendorSnapshotImage) available(m *Module) *bool {
+	return m.VendorProperties.Vendor_available
+}
+
+func isDirectoryExcluded(dir string, excludedMap directoryMap, includedMap directoryMap) bool {
+	if dir == "." || dir == "/" {
+		return false
+	}
+	if includedMap[dir] {
+		return false
+	} else if excludedMap[dir] {
+		return true
+	} else if defaultDirectoryIncludedMap[dir] {
+		return false
+	} else if defaultDirectoryExcludedMap[dir] {
+		return true
+	} else {
+		return isDirectoryExcluded(filepath.Dir(dir), excludedMap, includedMap)
+	}
+}
+
+func (vendorSnapshotImage) isProprietaryPath(dir string, deviceConfig android.DeviceConfig) bool {
+	return isDirectoryExcluded(dir, deviceConfig.VendorSnapshotDirsExcludedMap(), deviceConfig.VendorSnapshotDirsIncludedMap())
+}
+
+// vendor snapshot includes static/header libraries with vndk: {enabled: true}.
+func (vendorSnapshotImage) includeVndk() bool {
+	return true
+}
+
+func (vendorSnapshotImage) excludeFromSnapshot(m *Module) bool {
+	return m.ExcludeFromVendorSnapshot()
+}
+
+func (vendorSnapshotImage) getSnapshotMap(m *Module, cfg android.Config) *snapshotMap {
+	if lib, ok := m.linker.(libraryInterface); ok {
+		if lib.static() {
+			return vendorSnapshotStaticLibs(cfg)
+		} else if lib.shared() {
+			return vendorSnapshotSharedLibs(cfg)
+		} else {
+			// header
+			return vendorSnapshotHeaderLibs(cfg)
+		}
+	} else if m.binary() {
+		return vendorSnapshotBinaries(cfg)
+	} else if m.object() {
+		return vendorSnapshotObjects(cfg)
+	} else {
+		return nil
+	}
+}
+
+func (vendorSnapshotImage) getMutex() *sync.Mutex {
+	return &vendorSnapshotsLock
+}
+
+func (vendorSnapshotImage) suffixModules(config android.Config) map[string]bool {
+	return vendorSuffixModules(config)
+}
+
+func (vendorSnapshotImage) shouldBeAddedToSuffixModules(module *Module) bool {
+	// vendor suffix should be added to snapshots if the source module isn't vendor: true.
+	if module.SocSpecific() {
+		return false
+	}
+
+	// But we can't just check SocSpecific() since we already passed the image mutator.
+	// Check ramdisk and recovery to see if we are real "vendor: true" module.
+	ramdiskAvailable := module.InRamdisk() && !module.OnlyInRamdisk()
+	recoveryAvailable := module.InRecovery() && !module.OnlyInRecovery()
+
+	return !ramdiskAvailable && !recoveryAvailable
+}
+
+func (vendorSnapshotImage) isUsingSnapshot(cfg android.DeviceConfig) bool {
+	vndkVersion := cfg.VndkVersion()
+	return vndkVersion != "current" && vndkVersion != ""
+}
+
+func (vendorSnapshotImage) skipModuleMutator(ctx android.BottomUpMutatorContext) bool {
+	vndkVersion := ctx.DeviceConfig().VndkVersion()
+	module, ok := ctx.Module().(*Module)
+	return !ok || module.VndkVersion() != vndkVersion
+}
+
+func (vendorSnapshotImage) skipSourceMutator(ctx android.BottomUpMutatorContext) bool {
+	vndkVersion := ctx.DeviceConfig().VndkVersion()
+	module, ok := ctx.Module().(*Module)
+	if !ok {
+		return true
+	}
+	if module.VndkVersion() != vndkVersion {
+		return true
+	}
+	return false
+}
+
+// returns true iff a given module SHOULD BE EXCLUDED, false if included
+func (vendorSnapshotImage) excludeFromDirectedSnapshot(cfg android.DeviceConfig, name string) bool {
+	// If we're using full snapshot, not directed snapshot, capture every module
+	if !cfg.DirectedVendorSnapshot() {
+		return false
+	}
+	// Else, checks if name is in VENDOR_SNAPSHOT_MODULES.
+	return !cfg.VendorSnapshotModules()[name]
+}
+
+func (recoverySnapshotImage) init() {
+	android.RegisterSingletonType("recovery-snapshot", RecoverySnapshotSingleton)
+	android.RegisterModuleType("recovery_snapshot_shared", RecoverySnapshotSharedFactory)
+	android.RegisterModuleType("recovery_snapshot_static", RecoverySnapshotStaticFactory)
+	android.RegisterModuleType("recovery_snapshot_header", RecoverySnapshotHeaderFactory)
+	android.RegisterModuleType("recovery_snapshot_binary", RecoverySnapshotBinaryFactory)
+	android.RegisterModuleType("recovery_snapshot_object", RecoverySnapshotObjectFactory)
+}
+
+func (recoverySnapshotImage) shouldGenerateSnapshot(ctx android.SingletonContext) bool {
+	// RECOVERY_SNAPSHOT_VERSION must be set to 'current' in order to generate a
+	// snapshot.
+	return ctx.DeviceConfig().RecoverySnapshotVersion() == "current"
+}
+
+func (recoverySnapshotImage) inImage(m *Module) func() bool {
+	return m.InRecovery
+}
+
+func (recoverySnapshotImage) available(m *Module) *bool {
+	return m.Properties.Recovery_available
+}
+
+func (recoverySnapshotImage) isProprietaryPath(dir string, deviceConfig android.DeviceConfig) bool {
+	return isDirectoryExcluded(dir, deviceConfig.RecoverySnapshotDirsExcludedMap(), deviceConfig.RecoverySnapshotDirsIncludedMap())
+}
+
+// recovery snapshot does NOT treat vndk specially.
+func (recoverySnapshotImage) includeVndk() bool {
+	return false
+}
+
+func (recoverySnapshotImage) excludeFromSnapshot(m *Module) bool {
+	return m.ExcludeFromRecoverySnapshot()
+}
+
+func (recoverySnapshotImage) getSnapshotMap(m *Module, cfg android.Config) *snapshotMap {
+	if lib, ok := m.linker.(libraryInterface); ok {
+		if lib.static() {
+			return recoverySnapshotStaticLibs(cfg)
+		} else if lib.shared() {
+			return recoverySnapshotSharedLibs(cfg)
+		} else {
+			// header
+			return recoverySnapshotHeaderLibs(cfg)
+		}
+	} else if m.binary() {
+		return recoverySnapshotBinaries(cfg)
+	} else if m.object() {
+		return recoverySnapshotObjects(cfg)
+	} else {
+		return nil
+	}
+}
+
+func (recoverySnapshotImage) getMutex() *sync.Mutex {
+	return &recoverySnapshotsLock
+}
+
+func (recoverySnapshotImage) suffixModules(config android.Config) map[string]bool {
+	return recoverySuffixModules(config)
+}
+
+func (recoverySnapshotImage) shouldBeAddedToSuffixModules(module *Module) bool {
+	return proptools.BoolDefault(module.Properties.Recovery_available, false)
+}
+
+func (recoverySnapshotImage) isUsingSnapshot(cfg android.DeviceConfig) bool {
+	recoverySnapshotVersion := cfg.RecoverySnapshotVersion()
+	return recoverySnapshotVersion != "current" && recoverySnapshotVersion != ""
+}
+
+func (recoverySnapshotImage) skipModuleMutator(ctx android.BottomUpMutatorContext) bool {
+	module, ok := ctx.Module().(*Module)
+	return !ok || !module.InRecovery()
+}
+
+func (recoverySnapshotImage) skipSourceMutator(ctx android.BottomUpMutatorContext) bool {
+	module, ok := ctx.Module().(*Module)
+	return !ok || !module.InRecovery()
+}
+
+func (recoverySnapshotImage) excludeFromDirectedSnapshot(cfg android.DeviceConfig, name string) bool {
+	// If we're using full snapshot, not directed snapshot, capture every module
+	if !cfg.DirectedRecoverySnapshot() {
+		return false
+	}
+	// Else, checks if name is in RECOVERY_SNAPSHOT_MODULES.
+	return !cfg.RecoverySnapshotModules()[name]
+}
+
+func (ramdiskSnapshotImage) init() {
+	android.RegisterSingletonType("ramdisk-snapshot", RamdiskSnapshotSingleton)
+	android.RegisterModuleType("ramdisk_snapshot_shared", RamdiskSnapshotSharedFactory)
+	android.RegisterModuleType("ramdisk_snapshot_static", RamdiskSnapshotStaticFactory)
+	android.RegisterModuleType("ramdisk_snapshot_header", RamdiskSnapshotHeaderFactory)
+	android.RegisterModuleType("ramdisk_snapshot_binary", RamdiskSnapshotBinaryFactory)
+	android.RegisterModuleType("ramdisk_snapshot_object", RamdiskSnapshotObjectFactory)
+}
+
+func (ramdiskSnapshotImage) shouldGenerateSnapshot(ctx android.SingletonContext) bool {
+	// RAMDISK_SNAPSHOT_VERSION must be set to 'current' in order to generate a
+	// snapshot.
+	return ctx.DeviceConfig().RamdiskSnapshotVersion() == "current"
+}
+
+func (ramdiskSnapshotImage) inImage(m *Module) func() bool {
+	return m.InRamdisk
+}
+
+func (ramdiskSnapshotImage) available(m *Module) *bool {
+	return m.Properties.Ramdisk_available
+}
+
+func (ramdiskSnapshotImage) isProprietaryPath(dir string, deviceConfig android.DeviceConfig) bool {
+	return isDirectoryExcluded(dir, nil, nil)
+}
+
+// ramdisk snapshot does NOT treat vndk specially.
+func (ramdiskSnapshotImage) includeVndk() bool {
+	return false
+}
+
+func (ramdiskSnapshotImage) excludeFromSnapshot(m *Module) bool {
+	return m.ExcludeFromRamdiskSnapshot()
+}
+
+func (ramdiskSnapshotImage) getSnapshotMap(m *Module, cfg android.Config) *snapshotMap {
+	if lib, ok := m.linker.(libraryInterface); ok {
+		if lib.static() {
+			return ramdiskSnapshotStaticLibs(cfg)
+		} else if lib.shared() {
+			return ramdiskSnapshotSharedLibs(cfg)
+		} else {
+			// header
+			return ramdiskSnapshotHeaderLibs(cfg)
+		}
+	} else if m.binary() {
+		return ramdiskSnapshotBinaries(cfg)
+	} else if m.object() {
+		return ramdiskSnapshotObjects(cfg)
+	} else {
+		return nil
+	}
+}
+
+func (ramdiskSnapshotImage) getMutex() *sync.Mutex {
+	return &ramdiskSnapshotsLock
+}
+
+func (ramdiskSnapshotImage) suffixModules(config android.Config) map[string]bool {
+	return ramdiskSuffixModules(config)
+}
+
+func (ramdiskSnapshotImage) shouldBeAddedToSuffixModules(module *Module) bool {
+	return proptools.BoolDefault(module.Properties.Ramdisk_available, false)
+}
+
+func (ramdiskSnapshotImage) isUsingSnapshot(cfg android.DeviceConfig) bool {
+	ramdiskSnapshotVersion := cfg.RamdiskSnapshotVersion()
+	return ramdiskSnapshotVersion != "current" && ramdiskSnapshotVersion != ""
+}
+
+func (ramdiskSnapshotImage) skipModuleMutator(ctx android.BottomUpMutatorContext) bool {
+	module, ok := ctx.Module().(*Module)
+	return !ok || !module.InRamdisk()
+}
+
+func (ramdiskSnapshotImage) skipSourceMutator(ctx android.BottomUpMutatorContext) bool {
+	module, ok := ctx.Module().(*Module)
+	return !ok || !module.InRamdisk()
+}
+
+func (ramdiskSnapshotImage) excludeFromDirectedSnapshot(cfg android.DeviceConfig, name string) bool {
+	// If we're using full snapshot, not directed snapshot, capture every module
+	if !cfg.DirectedRamdiskSnapshot() {
+		return false
+	}
+	// Else, checks if name is in RAMDSIK_SNAPSHOT_MODULES.
+	return !cfg.RamdiskSnapshotModules()[name]
+}
+
+var vendorSnapshotImageSingleton vendorSnapshotImage
+var recoverySnapshotImageSingleton recoverySnapshotImage
+var ramdiskSnapshotImageSingleton ramdiskSnapshotImage
+
+func init() {
+	vendorSnapshotImageSingleton.init()
+	recoverySnapshotImageSingleton.init()
+	ramdiskSnapshotImageSingleton.init()
+}
+
+const (
+	vendorSnapshotHeaderSuffix = ".vendor_header."
+	vendorSnapshotSharedSuffix = ".vendor_shared."
+	vendorSnapshotStaticSuffix = ".vendor_static."
+	vendorSnapshotBinarySuffix = ".vendor_binary."
+	vendorSnapshotObjectSuffix = ".vendor_object."
+)
+
+const (
+	recoverySnapshotHeaderSuffix = ".recovery_header."
+	recoverySnapshotSharedSuffix = ".recovery_shared."
+	recoverySnapshotStaticSuffix = ".recovery_static."
+	recoverySnapshotBinarySuffix = ".recovery_binary."
+	recoverySnapshotObjectSuffix = ".recovery_object."
+)
+
+const (
+	ramdiskSnapshotHeaderSuffix = ".ramdisk_header."
+	ramdiskSnapshotSharedSuffix = ".ramdisk_shared."
+	ramdiskSnapshotStaticSuffix = ".ramdisk_static."
+	ramdiskSnapshotBinarySuffix = ".ramdisk_binary."
+	ramdiskSnapshotObjectSuffix = ".ramdisk_object."
+)
+
+var (
+	vendorSnapshotsLock         sync.Mutex
+	vendorSuffixModulesKey      = android.NewOnceKey("vendorSuffixModules")
+	vendorSnapshotHeaderLibsKey = android.NewOnceKey("vendorSnapshotHeaderLibs")
+	vendorSnapshotStaticLibsKey = android.NewOnceKey("vendorSnapshotStaticLibs")
+	vendorSnapshotSharedLibsKey = android.NewOnceKey("vendorSnapshotSharedLibs")
+	vendorSnapshotBinariesKey   = android.NewOnceKey("vendorSnapshotBinaries")
+	vendorSnapshotObjectsKey    = android.NewOnceKey("vendorSnapshotObjects")
+)
+
+var (
+	recoverySnapshotsLock         sync.Mutex
+	recoverySuffixModulesKey      = android.NewOnceKey("recoverySuffixModules")
+	recoverySnapshotHeaderLibsKey = android.NewOnceKey("recoverySnapshotHeaderLibs")
+	recoverySnapshotStaticLibsKey = android.NewOnceKey("recoverySnapshotStaticLibs")
+	recoverySnapshotSharedLibsKey = android.NewOnceKey("recoverySnapshotSharedLibs")
+	recoverySnapshotBinariesKey   = android.NewOnceKey("recoverySnapshotBinaries")
+	recoverySnapshotObjectsKey    = android.NewOnceKey("recoverySnapshotObjects")
+)
+var (
+	ramdiskSnapshotsLock         sync.Mutex
+	ramdiskSuffixModulesKey      = android.NewOnceKey("ramdiskSuffixModules")
+	ramdiskSnapshotHeaderLibsKey = android.NewOnceKey("ramdiskSnapshotHeaderLibs")
+	ramdiskSnapshotStaticLibsKey = android.NewOnceKey("ramdiskSnapshotStaticLibs")
+	ramdiskSnapshotSharedLibsKey = android.NewOnceKey("ramdiskSnapshotSharedLibs")
+	ramdiskSnapshotBinariesKey   = android.NewOnceKey("ramdiskSnapshotBinaries")
+	ramdiskSnapshotObjectsKey    = android.NewOnceKey("ramdiskSnapshotObjects")
+)
+
+// vendorSuffixModules holds names of modules whose vendor variants should have the vendor suffix.
+// This is determined by source modules, and then this will be used when exporting snapshot modules
+// to Makefile.
+//
+// For example, if libbase has "vendor_available: true", the name of core variant will be "libbase"
+// while the name of vendor variant will be "libbase.vendor". In such cases, the vendor snapshot of
+// "libbase" should be exported with the name "libbase.vendor".
+//
+// Refer to VendorSnapshotSourceMutator and makeLibName which use this.
+func vendorSuffixModules(config android.Config) map[string]bool {
+	return config.Once(vendorSuffixModulesKey, func() interface{} {
+		return make(map[string]bool)
+	}).(map[string]bool)
+}
+
+// these are vendor snapshot maps holding names of vendor snapshot modules
+func vendorSnapshotHeaderLibs(config android.Config) *snapshotMap {
+	return config.Once(vendorSnapshotHeaderLibsKey, func() interface{} {
+		return newSnapshotMap()
+	}).(*snapshotMap)
+}
+
+func vendorSnapshotSharedLibs(config android.Config) *snapshotMap {
+	return config.Once(vendorSnapshotSharedLibsKey, func() interface{} {
+		return newSnapshotMap()
+	}).(*snapshotMap)
+}
+
+func vendorSnapshotStaticLibs(config android.Config) *snapshotMap {
+	return config.Once(vendorSnapshotStaticLibsKey, func() interface{} {
+		return newSnapshotMap()
+	}).(*snapshotMap)
+}
+
+func vendorSnapshotBinaries(config android.Config) *snapshotMap {
+	return config.Once(vendorSnapshotBinariesKey, func() interface{} {
+		return newSnapshotMap()
+	}).(*snapshotMap)
+}
+
+func vendorSnapshotObjects(config android.Config) *snapshotMap {
+	return config.Once(vendorSnapshotObjectsKey, func() interface{} {
+		return newSnapshotMap()
+	}).(*snapshotMap)
+}
+
+func recoverySuffixModules(config android.Config) map[string]bool {
+	return config.Once(recoverySuffixModulesKey, func() interface{} {
+		return make(map[string]bool)
+	}).(map[string]bool)
+}
+
+func recoverySnapshotHeaderLibs(config android.Config) *snapshotMap {
+	return config.Once(recoverySnapshotHeaderLibsKey, func() interface{} {
+		return newSnapshotMap()
+	}).(*snapshotMap)
+}
+
+func recoverySnapshotSharedLibs(config android.Config) *snapshotMap {
+	return config.Once(recoverySnapshotSharedLibsKey, func() interface{} {
+		return newSnapshotMap()
+	}).(*snapshotMap)
+}
+
+func recoverySnapshotStaticLibs(config android.Config) *snapshotMap {
+	return config.Once(recoverySnapshotStaticLibsKey, func() interface{} {
+		return newSnapshotMap()
+	}).(*snapshotMap)
+}
+
+func recoverySnapshotBinaries(config android.Config) *snapshotMap {
+	return config.Once(recoverySnapshotBinariesKey, func() interface{} {
+		return newSnapshotMap()
+	}).(*snapshotMap)
+}
+
+func recoverySnapshotObjects(config android.Config) *snapshotMap {
+	return config.Once(recoverySnapshotObjectsKey, func() interface{} {
+		return newSnapshotMap()
+	}).(*snapshotMap)
+}
+
+func ramdiskSuffixModules(config android.Config) map[string]bool {
+	return config.Once(ramdiskSuffixModulesKey, func() interface{} {
+		return make(map[string]bool)
+	}).(map[string]bool)
+}
+
+func ramdiskSnapshotHeaderLibs(config android.Config) *snapshotMap {
+	return config.Once(ramdiskSnapshotHeaderLibsKey, func() interface{} {
+		return newSnapshotMap()
+	}).(*snapshotMap)
+}
+
+func ramdiskSnapshotSharedLibs(config android.Config) *snapshotMap {
+	return config.Once(ramdiskSnapshotSharedLibsKey, func() interface{} {
+		return newSnapshotMap()
+	}).(*snapshotMap)
+}
+
+func ramdiskSnapshotStaticLibs(config android.Config) *snapshotMap {
+	return config.Once(ramdiskSnapshotStaticLibsKey, func() interface{} {
+		return newSnapshotMap()
+	}).(*snapshotMap)
+}
+
+func ramdiskSnapshotBinaries(config android.Config) *snapshotMap {
+	return config.Once(ramdiskSnapshotBinariesKey, func() interface{} {
+		return newSnapshotMap()
+	}).(*snapshotMap)
+}
+
+func ramdiskSnapshotObjects(config android.Config) *snapshotMap {
+	return config.Once(ramdiskSnapshotObjectsKey, func() interface{} {
+		return newSnapshotMap()
+	}).(*snapshotMap)
+}
+
+type baseSnapshotDecoratorProperties struct {
+	// snapshot version.
+	Version string
+
+	// Target arch name of the snapshot (e.g. 'arm64' for variant 'aosp_arm64')
+	Target_arch string
+
+	// Suffix to be added to the module name, e.g., vendor_shared,
+	// recovery_shared, etc.
+	Module_suffix string
+}
+
+// baseSnapshotDecorator provides common basic functions for all snapshot modules, such as snapshot
+// version, snapshot arch, etc. It also adds a special suffix to Soong module name, so it doesn't
+// collide with source modules. e.g. the following example module,
+//
+// vendor_snapshot_static {
+//     name: "libbase",
+//     arch: "arm64",
+//     version: 30,
+//     ...
+// }
+//
+// will be seen as "libbase.vendor_static.30.arm64" by Soong.
+type baseSnapshotDecorator struct {
+	baseProperties baseSnapshotDecoratorProperties
+}
+
+func (p *baseSnapshotDecorator) Name(name string) string {
+	return name + p.NameSuffix()
+}
+
+func (p *baseSnapshotDecorator) NameSuffix() string {
+	versionSuffix := p.version()
+	if p.arch() != "" {
+		versionSuffix += "." + p.arch()
+	}
+
+	return p.baseProperties.Module_suffix + versionSuffix
+}
+
+func (p *baseSnapshotDecorator) version() string {
+	return p.baseProperties.Version
+}
+
+func (p *baseSnapshotDecorator) arch() string {
+	return p.baseProperties.Target_arch
+}
+
+func (p *baseSnapshotDecorator) module_suffix() string {
+	return p.baseProperties.Module_suffix
+}
+
+func (p *baseSnapshotDecorator) isSnapshotPrebuilt() bool {
+	return true
+}
+
+// Call this with a module suffix after creating a snapshot module, such as
+// vendorSnapshotSharedSuffix, recoverySnapshotBinarySuffix, etc.
+func (p *baseSnapshotDecorator) init(m *Module, suffix string) {
+	p.baseProperties.Module_suffix = suffix
+	m.AddProperties(&p.baseProperties)
+	android.AddLoadHook(m, func(ctx android.LoadHookContext) {
+		vendorSnapshotLoadHook(ctx, p)
+	})
+}
+
+// vendorSnapshotLoadHook disables snapshots if it's not BOARD_VNDK_VERSION.
+// As vendor snapshot is only for vendor, such modules won't be used at all.
+func vendorSnapshotLoadHook(ctx android.LoadHookContext, p *baseSnapshotDecorator) {
+	if p.version() != ctx.DeviceConfig().VndkVersion() {
+		ctx.Module().Disable()
+		return
+	}
+}
+
+//
+// Module definitions for snapshots of libraries (shared, static, header).
+//
+// Modules (vendor|recovery)_snapshot_(shared|static|header) are defined here. Shared libraries and
+// static libraries have their prebuilt library files (.so for shared, .a for static) as their src,
+// which can be installed or linked against. Also they export flags needed when linked, such as
+// include directories, c flags, sanitize dependency information, etc.
+//
+// These modules are auto-generated by development/vendor_snapshot/update.py.
+type snapshotLibraryProperties struct {
+	// Prebuilt file for each arch.
+	Src *string `android:"arch_variant"`
+
+	// list of directories that will be added to the include path (using -I).
+	Export_include_dirs []string `android:"arch_variant"`
+
+	// list of directories that will be added to the system path (using -isystem).
+	Export_system_include_dirs []string `android:"arch_variant"`
+
+	// list of flags that will be used for any module that links against this module.
+	Export_flags []string `android:"arch_variant"`
+
+	// Whether this prebuilt needs to depend on sanitize ubsan runtime or not.
+	Sanitize_ubsan_dep *bool `android:"arch_variant"`
+
+	// Whether this prebuilt needs to depend on sanitize minimal runtime or not.
+	Sanitize_minimal_dep *bool `android:"arch_variant"`
+
+	// TODO(b/181815415) remove Is_llndk when possible
+	// Whether this prebuilt is a snapshot of an llndk library.
+	Is_llndk *bool
+}
+
+type snapshotSanitizer interface {
+	isSanitizerEnabled(t sanitizerType) bool
+	setSanitizerVariation(t sanitizerType, enabled bool)
+}
+
+type snapshotLibraryDecorator struct {
+	baseSnapshotDecorator
+	*libraryDecorator
+	properties          snapshotLibraryProperties
+	sanitizerProperties struct {
+		CfiEnabled bool `blueprint:"mutated"`
+
+		// Library flags for cfi variant.
+		Cfi snapshotLibraryProperties `android:"arch_variant"`
+	}
+	androidMkSuffix string
+}
+
+func (p *snapshotLibraryDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
+	p.libraryDecorator.libName = strings.TrimSuffix(ctx.ModuleName(), p.NameSuffix())
+	return p.libraryDecorator.linkerFlags(ctx, flags)
+}
+
+func (p *snapshotLibraryDecorator) matchesWithDevice(config android.DeviceConfig) bool {
+	arches := config.Arches()
+	if len(arches) == 0 || arches[0].ArchType.String() != p.arch() {
+		return false
+	}
+	if !p.header() && p.properties.Src == nil {
+		return false
+	}
+	return true
+}
+
+// cc modules' link functions are to link compiled objects into final binaries.
+// As snapshots are prebuilts, this just returns the prebuilt binary after doing things which are
+// done by normal library decorator, e.g. exporting flags.
+func (p *snapshotLibraryDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps, objs Objects) android.Path {
+	m := ctx.Module().(*Module)
+
+	if m.inVendor() && vendorSuffixModules(ctx.Config())[m.BaseModuleName()] {
+		p.androidMkSuffix = vendorSuffix
+	} else if m.InRecovery() && recoverySuffixModules(ctx.Config())[m.BaseModuleName()] {
+		p.androidMkSuffix = recoverySuffix
+	} else if m.InRamdisk() && ramdiskSuffixModules(ctx.Config())[m.BaseModuleName()] {
+		p.androidMkSuffix = ramdiskSuffix
+	}
+	if p.header() {
+		return p.libraryDecorator.link(ctx, flags, deps, objs)
+	}
+
+	if p.sanitizerProperties.CfiEnabled {
+		p.properties = p.sanitizerProperties.Cfi
+	}
+
+	if !p.matchesWithDevice(ctx.DeviceConfig()) {
+		return nil
+	}
+
+	p.libraryDecorator.reexportDirs(android.PathsForModuleSrc(ctx, p.properties.Export_include_dirs)...)
+	p.libraryDecorator.reexportSystemDirs(android.PathsForModuleSrc(ctx, p.properties.Export_system_include_dirs)...)
+	p.libraryDecorator.reexportFlags(p.properties.Export_flags...)
+
+	in := android.PathForModuleSrc(ctx, *p.properties.Src)
+	p.unstrippedOutputFile = in
+
+	if p.shared() {
+		libName := in.Base()
+		builderFlags := flagsToBuilderFlags(flags)
+
+		// Optimize out relinking against shared libraries whose interface hasn't changed by
+		// depending on a table of contents file instead of the library itself.
+		tocFile := android.PathForModuleOut(ctx, libName+".toc")
+		p.tocFile = android.OptionalPathForPath(tocFile)
+		TransformSharedObjectToToc(ctx, in, tocFile, builderFlags)
+	}
+
+	return in
+}
+
+func (p *snapshotLibraryDecorator) install(ctx ModuleContext, file android.Path) {
+	if p.matchesWithDevice(ctx.DeviceConfig()) && (p.shared() || p.static()) {
+		p.baseInstaller.install(ctx, file)
+	}
+}
+
+func (p *snapshotLibraryDecorator) nativeCoverage() bool {
+	return false
+}
+
+func (p *snapshotLibraryDecorator) isSanitizerEnabled(t sanitizerType) bool {
+	switch t {
+	case cfi:
+		return p.sanitizerProperties.Cfi.Src != nil
+	default:
+		return false
+	}
+}
+
+func (p *snapshotLibraryDecorator) setSanitizerVariation(t sanitizerType, enabled bool) {
+	if !enabled {
+		return
+	}
+	switch t {
+	case cfi:
+		p.sanitizerProperties.CfiEnabled = true
+	default:
+		return
+	}
+}
+
+// TODO(b/181815415) remove isLlndk() when possible
+func (p *snapshotLibraryDecorator) isLlndk() bool {
+	return Bool(p.properties.Is_llndk)
+}
+
+func snapshotLibraryFactory(suffix string) (*Module, *snapshotLibraryDecorator) {
+	module, library := NewLibrary(android.DeviceSupported)
+
+	module.stl = nil
+	module.sanitize = nil
+	library.StripProperties.Strip.None = BoolPtr(true)
+
+	prebuilt := &snapshotLibraryDecorator{
+		libraryDecorator: library,
+	}
+
+	prebuilt.baseLinker.Properties.No_libcrt = BoolPtr(true)
+	prebuilt.baseLinker.Properties.Nocrt = BoolPtr(true)
+
+	// Prevent default system libs (libc, libm, and libdl) from being linked
+	if prebuilt.baseLinker.Properties.System_shared_libs == nil {
+		prebuilt.baseLinker.Properties.System_shared_libs = []string{}
+	}
+
+	module.compiler = nil
+	module.linker = prebuilt
+	module.installer = prebuilt
+
+	prebuilt.init(module, suffix)
+	module.AddProperties(
+		&prebuilt.properties,
+		&prebuilt.sanitizerProperties,
+	)
+
+	return module, prebuilt
+}
+
+// vendor_snapshot_shared is a special prebuilt shared library which is auto-generated by
+// development/vendor_snapshot/update.py. As a part of vendor snapshot, vendor_snapshot_shared
+// overrides the vendor variant of the cc shared library with the same name, if BOARD_VNDK_VERSION
+// is set.
+func VendorSnapshotSharedFactory() android.Module {
+	module, prebuilt := snapshotLibraryFactory(vendorSnapshotSharedSuffix)
+	prebuilt.libraryDecorator.BuildOnlyShared()
+	return module.Init()
+}
+
+// recovery_snapshot_shared is a special prebuilt shared library which is auto-generated by
+// development/vendor_snapshot/update.py. As a part of recovery snapshot, recovery_snapshot_shared
+// overrides the recovery variant of the cc shared library with the same name, if BOARD_VNDK_VERSION
+// is set.
+func RecoverySnapshotSharedFactory() android.Module {
+	module, prebuilt := snapshotLibraryFactory(recoverySnapshotSharedSuffix)
+	prebuilt.libraryDecorator.BuildOnlyShared()
+	return module.Init()
+}
+
+// ramdisk_snapshot_shared is a special prebuilt shared library which is auto-generated by
+// development/vendor_snapshot/update.py. As a part of ramdisk snapshot, ramdisk_snapshot_shared
+// overrides the ramdisk variant of the cc shared library with the same name, if BOARD_VNDK_VERSION
+// is set.
+func RamdiskSnapshotSharedFactory() android.Module {
+	module, prebuilt := snapshotLibraryFactory(ramdiskSnapshotSharedSuffix)
+	prebuilt.libraryDecorator.BuildOnlyShared()
+	return module.Init()
+}
+
+// vendor_snapshot_static is a special prebuilt static library which is auto-generated by
+// development/vendor_snapshot/update.py. As a part of vendor snapshot, vendor_snapshot_static
+// overrides the vendor variant of the cc static library with the same name, if BOARD_VNDK_VERSION
+// is set.
+func VendorSnapshotStaticFactory() android.Module {
+	module, prebuilt := snapshotLibraryFactory(vendorSnapshotStaticSuffix)
+	prebuilt.libraryDecorator.BuildOnlyStatic()
+	return module.Init()
+}
+
+// recovery_snapshot_static is a special prebuilt static library which is auto-generated by
+// development/vendor_snapshot/update.py. As a part of recovery snapshot, recovery_snapshot_static
+// overrides the recovery variant of the cc static library with the same name, if BOARD_VNDK_VERSION
+// is set.
+func RecoverySnapshotStaticFactory() android.Module {
+	module, prebuilt := snapshotLibraryFactory(recoverySnapshotStaticSuffix)
+	prebuilt.libraryDecorator.BuildOnlyStatic()
+	return module.Init()
+}
+
+// ramdisk_snapshot_static is a special prebuilt static library which is auto-generated by
+// development/vendor_snapshot/update.py. As a part of ramdisk snapshot, ramdisk_snapshot_static
+// overrides the ramdisk variant of the cc static library with the same name, if BOARD_VNDK_VERSION
+// is set.
+func RamdiskSnapshotStaticFactory() android.Module {
+	module, prebuilt := snapshotLibraryFactory(ramdiskSnapshotStaticSuffix)
+	prebuilt.libraryDecorator.BuildOnlyStatic()
+	return module.Init()
+}
+
+// vendor_snapshot_header is a special header library which is auto-generated by
+// development/vendor_snapshot/update.py. As a part of vendor snapshot, vendor_snapshot_header
+// overrides the vendor variant of the cc header library with the same name, if BOARD_VNDK_VERSION
+// is set.
+func VendorSnapshotHeaderFactory() android.Module {
+	module, prebuilt := snapshotLibraryFactory(vendorSnapshotHeaderSuffix)
+	prebuilt.libraryDecorator.HeaderOnly()
+	return module.Init()
+}
+
+// recovery_snapshot_header is a special header library which is auto-generated by
+// development/vendor_snapshot/update.py. As a part of recovery snapshot, recovery_snapshot_header
+// overrides the recovery variant of the cc header library with the same name, if BOARD_VNDK_VERSION
+// is set.
+func RecoverySnapshotHeaderFactory() android.Module {
+	module, prebuilt := snapshotLibraryFactory(recoverySnapshotHeaderSuffix)
+	prebuilt.libraryDecorator.HeaderOnly()
+	return module.Init()
+}
+
+// ramdisk_snapshot_header is a special header library which is auto-generated by
+// development/vendor_snapshot/update.py. As a part of ramdisk snapshot, ramdisk_snapshot_header
+// overrides the ramdisk variant of the cc header library with the same name, if BOARD_VNDK_VERSION
+// is set.
+func RamdiskSnapshotHeaderFactory() android.Module {
+	module, prebuilt := snapshotLibraryFactory(ramdiskSnapshotHeaderSuffix)
+	prebuilt.libraryDecorator.HeaderOnly()
+	return module.Init()
+}
+
+var _ snapshotSanitizer = (*snapshotLibraryDecorator)(nil)
+
+//
+// Module definitions for snapshots of executable binaries.
+//
+// Modules (vendor|recovery)_snapshot_binary are defined here. They have their prebuilt executable
+// binaries (e.g. toybox, sh) as their src, which can be installed.
+//
+// These modules are auto-generated by development/vendor_snapshot/update.py.
+type snapshotBinaryProperties struct {
+	// Prebuilt file for each arch.
+	Src *string `android:"arch_variant"`
+}
+
+type snapshotBinaryDecorator struct {
+	baseSnapshotDecorator
+	*binaryDecorator
+	properties      snapshotBinaryProperties
+	androidMkSuffix string
+}
+
+func (p *snapshotBinaryDecorator) matchesWithDevice(config android.DeviceConfig) bool {
+	if config.DeviceArch() != p.arch() {
+		return false
+	}
+	if p.properties.Src == nil {
+		return false
+	}
+	return true
+}
+
+// cc modules' link functions are to link compiled objects into final binaries.
+// As snapshots are prebuilts, this just returns the prebuilt binary
+func (p *snapshotBinaryDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps, objs Objects) android.Path {
+	if !p.matchesWithDevice(ctx.DeviceConfig()) {
+		return nil
+	}
+
+	in := android.PathForModuleSrc(ctx, *p.properties.Src)
+	p.unstrippedOutputFile = in
+	binName := in.Base()
+
+	m := ctx.Module().(*Module)
+	if m.inVendor() && vendorSuffixModules(ctx.Config())[m.BaseModuleName()] {
+		p.androidMkSuffix = vendorSuffix
+	} else if m.InRecovery() && recoverySuffixModules(ctx.Config())[m.BaseModuleName()] {
+		p.androidMkSuffix = recoverySuffix
+	} else if m.InRamdisk() && ramdiskSuffixModules(ctx.Config())[m.BaseModuleName()] {
+		p.androidMkSuffix = ramdiskSuffix
+
+	}
+
+	// use cpExecutable to make it executable
+	outputFile := android.PathForModuleOut(ctx, binName)
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        android.CpExecutable,
+		Description: "prebuilt",
+		Output:      outputFile,
+		Input:       in,
+	})
+
+	return outputFile
+}
+
+func (p *snapshotBinaryDecorator) nativeCoverage() bool {
+	return false
+}
+
+// vendor_snapshot_binary is a special prebuilt executable binary which is auto-generated by
+// development/vendor_snapshot/update.py. As a part of vendor snapshot, vendor_snapshot_binary
+// overrides the vendor variant of the cc binary with the same name, if BOARD_VNDK_VERSION is set.
+func VendorSnapshotBinaryFactory() android.Module {
+	return snapshotBinaryFactory(vendorSnapshotBinarySuffix)
+}
+
+// recovery_snapshot_binary is a special prebuilt executable binary which is auto-generated by
+// development/vendor_snapshot/update.py. As a part of recovery snapshot, recovery_snapshot_binary
+// overrides the recovery variant of the cc binary with the same name, if BOARD_VNDK_VERSION is set.
+func RecoverySnapshotBinaryFactory() android.Module {
+	return snapshotBinaryFactory(recoverySnapshotBinarySuffix)
+}
+
+// ramdisk_snapshot_binary is a special prebuilt executable binary which is auto-generated by
+// development/vendor_snapshot/update.py. As a part of ramdisk snapshot, ramdisk_snapshot_binary
+// overrides the ramdisk variant of the cc binary with the same name, if BOARD_VNDK_VERSION is set.
+func RamdiskSnapshotBinaryFactory() android.Module {
+	return snapshotBinaryFactory(ramdiskSnapshotBinarySuffix)
+}
+
+func snapshotBinaryFactory(suffix string) android.Module {
+	module, binary := NewBinary(android.DeviceSupported)
+	binary.baseLinker.Properties.No_libcrt = BoolPtr(true)
+	binary.baseLinker.Properties.Nocrt = BoolPtr(true)
+
+	// Prevent default system libs (libc, libm, and libdl) from being linked
+	if binary.baseLinker.Properties.System_shared_libs == nil {
+		binary.baseLinker.Properties.System_shared_libs = []string{}
+	}
+
+	prebuilt := &snapshotBinaryDecorator{
+		binaryDecorator: binary,
+	}
+
+	module.compiler = nil
+	module.sanitize = nil
+	module.stl = nil
+	module.linker = prebuilt
+
+	prebuilt.init(module, suffix)
+	module.AddProperties(&prebuilt.properties)
+	return module.Init()
+}
+
+//
+// Module definitions for snapshots of object files (*.o).
+//
+// Modules (vendor|recovery)_snapshot_object are defined here. They have their prebuilt object
+// files (*.o) as their src.
+//
+// These modules are auto-generated by development/vendor_snapshot/update.py.
+type vendorSnapshotObjectProperties struct {
+	// Prebuilt file for each arch.
+	Src *string `android:"arch_variant"`
+}
+
+type snapshotObjectLinker struct {
+	baseSnapshotDecorator
+	objectLinker
+	properties      vendorSnapshotObjectProperties
+	androidMkSuffix string
+}
+
+func (p *snapshotObjectLinker) matchesWithDevice(config android.DeviceConfig) bool {
+	if config.DeviceArch() != p.arch() {
+		return false
+	}
+	if p.properties.Src == nil {
+		return false
+	}
+	return true
+}
+
+// cc modules' link functions are to link compiled objects into final binaries.
+// As snapshots are prebuilts, this just returns the prebuilt binary
+func (p *snapshotObjectLinker) link(ctx ModuleContext, flags Flags, deps PathDeps, objs Objects) android.Path {
+	if !p.matchesWithDevice(ctx.DeviceConfig()) {
+		return nil
+	}
+
+	m := ctx.Module().(*Module)
+
+	if m.inVendor() && vendorSuffixModules(ctx.Config())[m.BaseModuleName()] {
+		p.androidMkSuffix = vendorSuffix
+	} else if m.InRecovery() && recoverySuffixModules(ctx.Config())[m.BaseModuleName()] {
+		p.androidMkSuffix = recoverySuffix
+	} else if m.InRamdisk() && ramdiskSuffixModules(ctx.Config())[m.BaseModuleName()] {
+		p.androidMkSuffix = ramdiskSuffix
+	}
+
+	return android.PathForModuleSrc(ctx, *p.properties.Src)
+}
+
+func (p *snapshotObjectLinker) nativeCoverage() bool {
+	return false
+}
+
+// vendor_snapshot_object is a special prebuilt compiled object file which is auto-generated by
+// development/vendor_snapshot/update.py. As a part of vendor snapshot, vendor_snapshot_object
+// overrides the vendor variant of the cc object with the same name, if BOARD_VNDK_VERSION is set.
+func VendorSnapshotObjectFactory() android.Module {
+	module := newObject()
+
+	prebuilt := &snapshotObjectLinker{
+		objectLinker: objectLinker{
+			baseLinker: NewBaseLinker(nil),
+		},
+	}
+	module.linker = prebuilt
+
+	prebuilt.init(module, vendorSnapshotObjectSuffix)
+	module.AddProperties(&prebuilt.properties)
+	return module.Init()
+}
+
+// recovery_snapshot_object is a special prebuilt compiled object file which is auto-generated by
+// development/vendor_snapshot/update.py. As a part of recovery snapshot, recovery_snapshot_object
+// overrides the recovery variant of the cc object with the same name, if BOARD_VNDK_VERSION is set.
+func RecoverySnapshotObjectFactory() android.Module {
+	module := newObject()
+
+	prebuilt := &snapshotObjectLinker{
+		objectLinker: objectLinker{
+			baseLinker: NewBaseLinker(nil),
+		},
+	}
+	module.linker = prebuilt
+
+	prebuilt.init(module, recoverySnapshotObjectSuffix)
+	module.AddProperties(&prebuilt.properties)
+	return module.Init()
+}
+
+// ramdisk_snapshot_object is a special prebuilt compiled object file which is auto-generated by
+// development/vendor_snapshot/update.py. As a part of ramdisk snapshot, ramdisk_snapshot_object
+// overrides the recovery variant of the cc object with the same name, if BOARD_VNDK_VERSION is set.
+func RamdiskSnapshotObjectFactory() android.Module {
+	module := newObject()
+
+	prebuilt := &snapshotObjectLinker{
+		objectLinker: objectLinker{
+			baseLinker: NewBaseLinker(nil),
+		},
+	}
+	module.linker = prebuilt
+
+	prebuilt.init(module, ramdiskSnapshotObjectSuffix)
+	module.AddProperties(&prebuilt.properties)
+	return module.Init()
+}
+
+type snapshotInterface interface {
+	matchesWithDevice(config android.DeviceConfig) bool
+}
+
+var _ snapshotInterface = (*vndkPrebuiltLibraryDecorator)(nil)
+var _ snapshotInterface = (*snapshotLibraryDecorator)(nil)
+var _ snapshotInterface = (*snapshotBinaryDecorator)(nil)
+var _ snapshotInterface = (*snapshotObjectLinker)(nil)
+
+//
+// Mutators that helps vendor snapshot modules override source modules.
+//
+
+// VendorSnapshotMutator gathers all snapshots for vendor, and disable all snapshots which don't
+// match with device, e.g.
+//   - snapshot version is different with BOARD_VNDK_VERSION
+//   - snapshot arch is different with device's arch (e.g. arm vs x86)
+//
+// This also handles vndk_prebuilt_shared, except for they won't be disabled in any cases, given
+// that any versions of VNDK might be packed into vndk APEX.
+//
+// TODO(b/145966707): remove mutator and utilize android.Prebuilt to override source modules
+func VendorSnapshotMutator(ctx android.BottomUpMutatorContext) {
+	snapshotMutator(ctx, vendorSnapshotImageSingleton)
+}
+
+func RecoverySnapshotMutator(ctx android.BottomUpMutatorContext) {
+	snapshotMutator(ctx, recoverySnapshotImageSingleton)
+}
+
+func RamdiskSnapshotMutator(ctx android.BottomUpMutatorContext) {
+	snapshotMutator(ctx, ramdiskSnapshotImageSingleton)
+}
+
+func snapshotMutator(ctx android.BottomUpMutatorContext, image snapshotImage) {
+	if !image.isUsingSnapshot(ctx.DeviceConfig()) {
+		return
+	}
+	module, ok := ctx.Module().(*Module)
+	if !ok || !module.Enabled() {
+		return
+	}
+	if image.skipModuleMutator(ctx) {
+		return
+	}
+	if !module.isSnapshotPrebuilt() {
+		return
+	}
+
+	// isSnapshotPrebuilt ensures snapshotInterface
+	if !module.linker.(snapshotInterface).matchesWithDevice(ctx.DeviceConfig()) {
+		// Disable unnecessary snapshot module, but do not disable
+		// vndk_prebuilt_shared because they might be packed into vndk APEX
+		if !module.IsVndk() {
+			module.Disable()
+		}
+		return
+	}
+
+	var snapshotMap *snapshotMap = image.getSnapshotMap(module, ctx.Config())
+	if snapshotMap == nil {
+		return
+	}
+
+	mutex := image.getMutex()
+	mutex.Lock()
+	defer mutex.Unlock()
+	snapshotMap.add(module.BaseModuleName(), ctx.Arch().ArchType, ctx.ModuleName())
+}
+
+// VendorSnapshotSourceMutator disables source modules which have corresponding snapshots.
+func VendorSnapshotSourceMutator(ctx android.BottomUpMutatorContext) {
+	snapshotSourceMutator(ctx, vendorSnapshotImageSingleton)
+}
+
+func RecoverySnapshotSourceMutator(ctx android.BottomUpMutatorContext) {
+	snapshotSourceMutator(ctx, recoverySnapshotImageSingleton)
+}
+func RamdiskSnapshotSourceMutator(ctx android.BottomUpMutatorContext) {
+	snapshotSourceMutator(ctx, ramdiskSnapshotImageSingleton)
+}
+
+func snapshotSourceMutator(ctx android.BottomUpMutatorContext, image snapshotImage) {
+	if !ctx.Device() {
+		return
+	}
+	if !image.isUsingSnapshot(ctx.DeviceConfig()) {
+		return
+	}
+
+	module, ok := ctx.Module().(*Module)
+	if !ok {
+		return
+	}
+
+	if image.shouldBeAddedToSuffixModules(module) {
+		mutex := image.getMutex()
+		mutex.Lock()
+		defer mutex.Unlock()
+
+		image.suffixModules(ctx.Config())[ctx.ModuleName()] = true
+	}
+
+	if module.isSnapshotPrebuilt() {
+		return
+	}
+	if image.skipSourceMutator(ctx) {
+		return
+	}
+
+	var snapshotMap *snapshotMap = image.getSnapshotMap(module, ctx.Config())
+	if snapshotMap == nil {
+		return
+	}
+
+	if _, ok := snapshotMap.get(ctx.ModuleName(), ctx.Arch().ArchType); !ok {
+		// Corresponding snapshot doesn't exist
+		return
+	}
+
+	// Disables source modules if corresponding snapshot exists.
+	if lib, ok := module.linker.(libraryInterface); ok && lib.buildStatic() && lib.buildShared() {
+		// But do not disable because the shared variant depends on the static variant.
+		module.SkipInstall()
+		module.Properties.HideFromMake = true
+	} else {
+		module.Disable()
+	}
+}
diff --git a/cc/snapshot_utils.go b/cc/snapshot_utils.go
index 26e7c8d..2739a78 100644
--- a/cc/snapshot_utils.go
+++ b/cc/snapshot_utils.go
@@ -58,38 +58,22 @@
 	return snapshot, found
 }
 
-func isSnapshotAware(ctx android.ModuleContext, m *Module) bool {
+// shouldCollectHeadersForSnapshot determines if the module is a possible candidate for snapshot.
+// If it's true, collectHeadersForSnapshot will be called in GenerateAndroidBuildActions.
+func shouldCollectHeadersForSnapshot(ctx android.ModuleContext, m *Module) bool {
+	if ctx.DeviceConfig().VndkVersion() != "current" &&
+		ctx.DeviceConfig().RecoverySnapshotVersion() != "current" &&
+		ctx.DeviceConfig().RamdiskSnapshotVersion() != "current" {
+		return false
+	}
 	if _, _, ok := isVndkSnapshotLibrary(ctx.DeviceConfig(), m); ok {
 		return ctx.Config().VndkSnapshotBuildArtifacts()
-	} else if isVendorSnapshotModule(m, ctx.ModuleDir()) {
-		return true
+	}
+	for _, image := range []snapshotImage{vendorSnapshotImageSingleton, recoverySnapshotImageSingleton,
+                                              ramdiskSnapshotImageSingleton } {
+		if isSnapshotAware(ctx.DeviceConfig(), m, image.isProprietaryPath(ctx.ModuleDir(), ctx.DeviceConfig()), image) {
+			return true
+		}
 	}
 	return false
 }
-
-func copyFile(ctx android.SingletonContext, path android.Path, out string) android.OutputPath {
-	outPath := android.PathForOutput(ctx, out)
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        android.Cp,
-		Input:       path,
-		Output:      outPath,
-		Description: "Cp " + out,
-		Args: map[string]string{
-			"cpFlags": "-f -L",
-		},
-	})
-	return outPath
-}
-
-func writeStringToFile(ctx android.SingletonContext, content, out string) android.OutputPath {
-	outPath := android.PathForOutput(ctx, out)
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        android.WriteFile,
-		Output:      outPath,
-		Description: "WriteFile " + out,
-		Args: map[string]string{
-			"content": content,
-		},
-	})
-	return outPath
-}
diff --git a/cc/testing.go b/cc/testing.go
index 7a86a8d..a8c494b 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -16,6 +16,7 @@
 
 import (
 	"android/soong/android"
+	"android/soong/genrule"
 )
 
 func RegisterRequiredBuildComponentsForTest(ctx android.RegistrationContext) {
@@ -32,6 +33,7 @@
 	ctx.RegisterModuleType("cc_object", ObjectFactory)
 	ctx.RegisterModuleType("ndk_prebuilt_shared_stl", NdkPrebuiltSharedStlFactory)
 	ctx.RegisterModuleType("ndk_prebuilt_object", NdkPrebuiltObjectFactory)
+	ctx.RegisterModuleType("genrule", genrule.GenRuleFactory)
 }
 
 func GatherRequiredDepsForTest(oses ...android.OsType) string {
@@ -464,10 +466,14 @@
 	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
 	ctx.RegisterModuleType("vndk_prebuilt_shared", VndkPrebuiltSharedFactory)
 	ctx.RegisterModuleType("vndk_libraries_txt", VndkLibrariesTxtFactory)
+	ctx.RegisterModuleType("vendor_snapshot_static", VendorSnapshotStaticFactory)
 	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
 	RegisterRequiredBuildComponentsForTest(ctx)
 	ctx.RegisterSingletonType("vndk-snapshot", VndkSnapshotSingleton)
 	ctx.RegisterSingletonType("vendor-snapshot", VendorSnapshotSingleton)
+	ctx.RegisterSingletonType("vendor-fake-snapshot", VendorFakeSnapshotSingleton)
+	ctx.RegisterSingletonType("recovery-snapshot", RecoverySnapshotSingleton)
+	ctx.RegisterSingletonType("ramdisk-snapshot", RamdiskSnapshotSingleton)
 
 	return ctx
 }
diff --git a/cc/util.go b/cc/util.go
index af26268..ce29a08 100644
--- a/cc/util.go
+++ b/cc/util.go
@@ -82,6 +82,7 @@
 		tidyFlags:     strings.Join(in.TidyFlags, " "),
 		sAbiFlags:     strings.Join(in.SAbiFlags, " "),
 		toolchain:     in.Toolchain,
+		sdclang:       in.Sdclang,
 		gcovCoverage:  in.GcovCoverage,
 		tidy:          in.Tidy,
 		sAbiDump:      in.SAbiDump,
@@ -120,3 +121,41 @@
 	return "mkdir -p " + dir + " && " +
 		"ln -sf " + target + " " + filepath.Join(dir, linkName)
 }
+
+func copyFileRule(ctx android.SingletonContext, path android.Path, out string) android.OutputPath {
+	outPath := android.PathForOutput(ctx, out)
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        android.Cp,
+		Input:       path,
+		Output:      outPath,
+		Description: "Cp " + out,
+		Args: map[string]string{
+			"cpFlags": "-f -L",
+		},
+	})
+	return outPath
+}
+
+func combineNoticesRule(ctx android.SingletonContext, paths android.Paths, out string) android.OutputPath {
+	outPath := android.PathForOutput(ctx, out)
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        android.Cat,
+		Inputs:      paths,
+		Output:      outPath,
+		Description: "combine notices for " + out,
+	})
+	return outPath
+}
+
+func writeStringToFileRule(ctx android.SingletonContext, content, out string) android.OutputPath {
+	outPath := android.PathForOutput(ctx, out)
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        android.WriteFile,
+		Output:      outPath,
+		Description: "WriteFile " + out,
+		Args: map[string]string{
+			"content": content,
+		},
+	})
+	return outPath
+}
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index b11b1a8..ed2c370 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -18,524 +18,198 @@
 	"path/filepath"
 	"sort"
 	"strings"
-	"sync"
 
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
 )
 
-const (
-	vendorSnapshotHeaderSuffix = ".vendor_header."
-	vendorSnapshotSharedSuffix = ".vendor_shared."
-	vendorSnapshotStaticSuffix = ".vendor_static."
-	vendorSnapshotBinarySuffix = ".vendor_binary."
-	vendorSnapshotObjectSuffix = ".vendor_object."
-)
-
-var (
-	vendorSnapshotsLock         sync.Mutex
-	vendorSuffixModulesKey      = android.NewOnceKey("vendorSuffixModules")
-	vendorSnapshotHeaderLibsKey = android.NewOnceKey("vendorSnapshotHeaderLibs")
-	vendorSnapshotStaticLibsKey = android.NewOnceKey("vendorSnapshotStaticLibs")
-	vendorSnapshotSharedLibsKey = android.NewOnceKey("vendorSnapshotSharedLibs")
-	vendorSnapshotBinariesKey   = android.NewOnceKey("vendorSnapshotBinaries")
-	vendorSnapshotObjectsKey    = android.NewOnceKey("vendorSnapshotObjects")
-)
-
-// vendor snapshot maps hold names of vendor snapshot modules per arch
-func vendorSuffixModules(config android.Config) map[string]bool {
-	return config.Once(vendorSuffixModulesKey, func() interface{} {
-		return make(map[string]bool)
-	}).(map[string]bool)
+var vendorSnapshotSingleton = snapshotSingleton{
+	"vendor",
+	"SOONG_VENDOR_SNAPSHOT_ZIP",
+	android.OptionalPath{},
+	true,
+	vendorSnapshotImageSingleton,
+	false, /* fake */
+	true,  /* usesLlndkLibraries */
 }
 
-func vendorSnapshotHeaderLibs(config android.Config) *snapshotMap {
-	return config.Once(vendorSnapshotHeaderLibsKey, func() interface{} {
-		return newSnapshotMap()
-	}).(*snapshotMap)
+var vendorFakeSnapshotSingleton = snapshotSingleton{
+	"vendor",
+	"SOONG_VENDOR_FAKE_SNAPSHOT_ZIP",
+	android.OptionalPath{},
+	true,
+	vendorSnapshotImageSingleton,
+	true, /* fake */
+	true, /* usesLlndkLibraries */
 }
 
-func vendorSnapshotSharedLibs(config android.Config) *snapshotMap {
-	return config.Once(vendorSnapshotSharedLibsKey, func() interface{} {
-		return newSnapshotMap()
-	}).(*snapshotMap)
+var recoverySnapshotSingleton = snapshotSingleton{
+	"recovery",
+	"SOONG_RECOVERY_SNAPSHOT_ZIP",
+	android.OptionalPath{},
+	false,
+	recoverySnapshotImageSingleton,
+	false, /* fake */
+	false, /* usesLlndkLibraries */
 }
 
-func vendorSnapshotStaticLibs(config android.Config) *snapshotMap {
-	return config.Once(vendorSnapshotStaticLibsKey, func() interface{} {
-		return newSnapshotMap()
-	}).(*snapshotMap)
-}
-
-func vendorSnapshotBinaries(config android.Config) *snapshotMap {
-	return config.Once(vendorSnapshotBinariesKey, func() interface{} {
-		return newSnapshotMap()
-	}).(*snapshotMap)
-}
-
-func vendorSnapshotObjects(config android.Config) *snapshotMap {
-	return config.Once(vendorSnapshotObjectsKey, func() interface{} {
-		return newSnapshotMap()
-	}).(*snapshotMap)
-}
-
-type vendorSnapshotLibraryProperties struct {
-	// snapshot version.
-	Version string
-
-	// Target arch name of the snapshot (e.g. 'arm64' for variant 'aosp_arm64')
-	Target_arch string
-
-	// Prebuilt file for each arch.
-	Src *string `android:"arch_variant"`
-
-	// list of flags that will be used for any module that links against this module.
-	Export_flags []string `android:"arch_variant"`
-
-	// Check the prebuilt ELF files (e.g. DT_SONAME, DT_NEEDED, resolution of undefined symbols,
-	// etc).
-	Check_elf_files *bool
-
-	// Whether this prebuilt needs to depend on sanitize ubsan runtime or not.
-	Sanitize_ubsan_dep *bool `android:"arch_variant"`
-
-	// Whether this prebuilt needs to depend on sanitize minimal runtime or not.
-	Sanitize_minimal_dep *bool `android:"arch_variant"`
-}
-
-type vendorSnapshotLibraryDecorator struct {
-	*libraryDecorator
-	properties            vendorSnapshotLibraryProperties
-	androidMkVendorSuffix bool
-}
-
-func (p *vendorSnapshotLibraryDecorator) Name(name string) string {
-	return name + p.NameSuffix()
-}
-
-func (p *vendorSnapshotLibraryDecorator) NameSuffix() string {
-	versionSuffix := p.version()
-	if p.arch() != "" {
-		versionSuffix += "." + p.arch()
-	}
-
-	var linkageSuffix string
-	if p.buildShared() {
-		linkageSuffix = vendorSnapshotSharedSuffix
-	} else if p.buildStatic() {
-		linkageSuffix = vendorSnapshotStaticSuffix
-	} else {
-		linkageSuffix = vendorSnapshotHeaderSuffix
-	}
-
-	return linkageSuffix + versionSuffix
-}
-
-func (p *vendorSnapshotLibraryDecorator) version() string {
-	return p.properties.Version
-}
-
-func (p *vendorSnapshotLibraryDecorator) arch() string {
-	return p.properties.Target_arch
-}
-
-func (p *vendorSnapshotLibraryDecorator) linkerFlags(ctx ModuleContext, flags Flags) Flags {
-	p.libraryDecorator.libName = strings.TrimSuffix(ctx.ModuleName(), p.NameSuffix())
-	return p.libraryDecorator.linkerFlags(ctx, flags)
-}
-
-func (p *vendorSnapshotLibraryDecorator) matchesWithDevice(config android.DeviceConfig) bool {
-	arches := config.Arches()
-	if len(arches) == 0 || arches[0].ArchType.String() != p.arch() {
-		return false
-	}
-	if !p.header() && p.properties.Src == nil {
-		return false
-	}
-	return true
-}
-
-func (p *vendorSnapshotLibraryDecorator) link(ctx ModuleContext,
-	flags Flags, deps PathDeps, objs Objects) android.Path {
-	m := ctx.Module().(*Module)
-	p.androidMkVendorSuffix = vendorSuffixModules(ctx.Config())[m.BaseModuleName()]
-
-	if p.header() {
-		return p.libraryDecorator.link(ctx, flags, deps, objs)
-	}
-
-	if !p.matchesWithDevice(ctx.DeviceConfig()) {
-		return nil
-	}
-
-	p.libraryDecorator.exportIncludes(ctx)
-	p.libraryDecorator.reexportFlags(p.properties.Export_flags...)
-
-	in := android.PathForModuleSrc(ctx, *p.properties.Src)
-	p.unstrippedOutputFile = in
-
-	if p.shared() {
-		libName := in.Base()
-		builderFlags := flagsToBuilderFlags(flags)
-
-		// Optimize out relinking against shared libraries whose interface hasn't changed by
-		// depending on a table of contents file instead of the library itself.
-		tocFile := android.PathForModuleOut(ctx, libName+".toc")
-		p.tocFile = android.OptionalPathForPath(tocFile)
-		TransformSharedObjectToToc(ctx, in, tocFile, builderFlags)
-	}
-
-	return in
-}
-
-func (p *vendorSnapshotLibraryDecorator) nativeCoverage() bool {
-	return false
-}
-
-func (p *vendorSnapshotLibraryDecorator) isSnapshotPrebuilt() bool {
-	return true
-}
-
-func (p *vendorSnapshotLibraryDecorator) install(ctx ModuleContext, file android.Path) {
-	if p.matchesWithDevice(ctx.DeviceConfig()) && (p.shared() || p.static()) {
-		p.baseInstaller.install(ctx, file)
-	}
-}
-
-type vendorSnapshotInterface interface {
-	version() string
-}
-
-func vendorSnapshotLoadHook(ctx android.LoadHookContext, p vendorSnapshotInterface) {
-	if p.version() != ctx.DeviceConfig().VndkVersion() {
-		ctx.Module().Disable()
-		return
-	}
-}
-
-func vendorSnapshotLibrary() (*Module, *vendorSnapshotLibraryDecorator) {
-	module, library := NewLibrary(android.DeviceSupported)
-
-	module.stl = nil
-	module.sanitize = nil
-	library.StripProperties.Strip.None = BoolPtr(true)
-
-	prebuilt := &vendorSnapshotLibraryDecorator{
-		libraryDecorator: library,
-	}
-
-	prebuilt.baseLinker.Properties.No_libcrt = BoolPtr(true)
-	prebuilt.baseLinker.Properties.Nocrt = BoolPtr(true)
-
-	// Prevent default system libs (libc, libm, and libdl) from being linked
-	if prebuilt.baseLinker.Properties.System_shared_libs == nil {
-		prebuilt.baseLinker.Properties.System_shared_libs = []string{}
-	}
-
-	module.compiler = nil
-	module.linker = prebuilt
-	module.installer = prebuilt
-
-	module.AddProperties(
-		&prebuilt.properties,
-	)
-
-	return module, prebuilt
-}
-
-func VendorSnapshotSharedFactory() android.Module {
-	module, prebuilt := vendorSnapshotLibrary()
-	prebuilt.libraryDecorator.BuildOnlyShared()
-	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
-		vendorSnapshotLoadHook(ctx, prebuilt)
-	})
-	return module.Init()
-}
-
-func VendorSnapshotStaticFactory() android.Module {
-	module, prebuilt := vendorSnapshotLibrary()
-	prebuilt.libraryDecorator.BuildOnlyStatic()
-	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
-		vendorSnapshotLoadHook(ctx, prebuilt)
-	})
-	return module.Init()
-}
-
-func VendorSnapshotHeaderFactory() android.Module {
-	module, prebuilt := vendorSnapshotLibrary()
-	prebuilt.libraryDecorator.HeaderOnly()
-	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
-		vendorSnapshotLoadHook(ctx, prebuilt)
-	})
-	return module.Init()
-}
-
-type vendorSnapshotBinaryProperties struct {
-	// snapshot version.
-	Version string
-
-	// Target arch name of the snapshot (e.g. 'arm64' for variant 'aosp_arm64_ab')
-	Target_arch string
-
-	// Prebuilt file for each arch.
-	Src *string `android:"arch_variant"`
-}
-
-type vendorSnapshotBinaryDecorator struct {
-	*binaryDecorator
-	properties            vendorSnapshotBinaryProperties
-	androidMkVendorSuffix bool
-}
-
-func (p *vendorSnapshotBinaryDecorator) Name(name string) string {
-	return name + p.NameSuffix()
-}
-
-func (p *vendorSnapshotBinaryDecorator) NameSuffix() string {
-	versionSuffix := p.version()
-	if p.arch() != "" {
-		versionSuffix += "." + p.arch()
-	}
-	return vendorSnapshotBinarySuffix + versionSuffix
-}
-
-func (p *vendorSnapshotBinaryDecorator) version() string {
-	return p.properties.Version
-}
-
-func (p *vendorSnapshotBinaryDecorator) arch() string {
-	return p.properties.Target_arch
-}
-
-func (p *vendorSnapshotBinaryDecorator) matchesWithDevice(config android.DeviceConfig) bool {
-	if config.DeviceArch() != p.arch() {
-		return false
-	}
-	if p.properties.Src == nil {
-		return false
-	}
-	return true
-}
-
-func (p *vendorSnapshotBinaryDecorator) link(ctx ModuleContext,
-	flags Flags, deps PathDeps, objs Objects) android.Path {
-	if !p.matchesWithDevice(ctx.DeviceConfig()) {
-		return nil
-	}
-
-	in := android.PathForModuleSrc(ctx, *p.properties.Src)
-	builderFlags := flagsToBuilderFlags(flags)
-	p.unstrippedOutputFile = in
-	binName := in.Base()
-	if p.needsStrip(ctx) {
-		stripped := android.PathForModuleOut(ctx, "stripped", binName)
-		p.stripExecutableOrSharedLib(ctx, in, stripped, builderFlags)
-		in = stripped
-	}
-
-	m := ctx.Module().(*Module)
-	p.androidMkVendorSuffix = vendorSuffixModules(ctx.Config())[m.BaseModuleName()]
-
-	// use cpExecutable to make it executable
-	outputFile := android.PathForModuleOut(ctx, binName)
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        android.CpExecutable,
-		Description: "prebuilt",
-		Output:      outputFile,
-		Input:       in,
-	})
-
-	return outputFile
-}
-
-func (p *vendorSnapshotBinaryDecorator) isSnapshotPrebuilt() bool {
-	return true
-}
-
-func VendorSnapshotBinaryFactory() android.Module {
-	module, binary := NewBinary(android.DeviceSupported)
-	binary.baseLinker.Properties.No_libcrt = BoolPtr(true)
-	binary.baseLinker.Properties.Nocrt = BoolPtr(true)
-
-	// Prevent default system libs (libc, libm, and libdl) from being linked
-	if binary.baseLinker.Properties.System_shared_libs == nil {
-		binary.baseLinker.Properties.System_shared_libs = []string{}
-	}
-
-	prebuilt := &vendorSnapshotBinaryDecorator{
-		binaryDecorator: binary,
-	}
-
-	module.compiler = nil
-	module.sanitize = nil
-	module.stl = nil
-	module.linker = prebuilt
-
-	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
-		vendorSnapshotLoadHook(ctx, prebuilt)
-	})
-
-	module.AddProperties(&prebuilt.properties)
-	return module.Init()
-}
-
-type vendorSnapshotObjectProperties struct {
-	// snapshot version.
-	Version string
-
-	// Target arch name of the snapshot (e.g. 'arm64' for variant 'aosp_arm64_ab')
-	Target_arch string
-
-	// Prebuilt file for each arch.
-	Src *string `android:"arch_variant"`
-}
-
-type vendorSnapshotObjectLinker struct {
-	objectLinker
-	properties            vendorSnapshotObjectProperties
-	androidMkVendorSuffix bool
-}
-
-func (p *vendorSnapshotObjectLinker) Name(name string) string {
-	return name + p.NameSuffix()
-}
-
-func (p *vendorSnapshotObjectLinker) NameSuffix() string {
-	versionSuffix := p.version()
-	if p.arch() != "" {
-		versionSuffix += "." + p.arch()
-	}
-	return vendorSnapshotObjectSuffix + versionSuffix
-}
-
-func (p *vendorSnapshotObjectLinker) version() string {
-	return p.properties.Version
-}
-
-func (p *vendorSnapshotObjectLinker) arch() string {
-	return p.properties.Target_arch
-}
-
-func (p *vendorSnapshotObjectLinker) matchesWithDevice(config android.DeviceConfig) bool {
-	if config.DeviceArch() != p.arch() {
-		return false
-	}
-	if p.properties.Src == nil {
-		return false
-	}
-	return true
-}
-
-func (p *vendorSnapshotObjectLinker) link(ctx ModuleContext,
-	flags Flags, deps PathDeps, objs Objects) android.Path {
-	if !p.matchesWithDevice(ctx.DeviceConfig()) {
-		return nil
-	}
-
-	m := ctx.Module().(*Module)
-	p.androidMkVendorSuffix = vendorSuffixModules(ctx.Config())[m.BaseModuleName()]
-
-	return android.PathForModuleSrc(ctx, *p.properties.Src)
-}
-
-func (p *vendorSnapshotObjectLinker) nativeCoverage() bool {
-	return false
-}
-
-func (p *vendorSnapshotObjectLinker) isSnapshotPrebuilt() bool {
-	return true
-}
-
-func VendorSnapshotObjectFactory() android.Module {
-	module := newObject()
-
-	prebuilt := &vendorSnapshotObjectLinker{
-		objectLinker: objectLinker{
-			baseLinker: NewBaseLinker(nil),
-		},
-	}
-	module.linker = prebuilt
-
-	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
-		vendorSnapshotLoadHook(ctx, prebuilt)
-	})
-
-	module.AddProperties(&prebuilt.properties)
-	return module.Init()
-}
-
-func init() {
-	android.RegisterSingletonType("vendor-snapshot", VendorSnapshotSingleton)
-	android.RegisterModuleType("vendor_snapshot_shared", VendorSnapshotSharedFactory)
-	android.RegisterModuleType("vendor_snapshot_static", VendorSnapshotStaticFactory)
-	android.RegisterModuleType("vendor_snapshot_header", VendorSnapshotHeaderFactory)
-	android.RegisterModuleType("vendor_snapshot_binary", VendorSnapshotBinaryFactory)
-	android.RegisterModuleType("vendor_snapshot_object", VendorSnapshotObjectFactory)
+var ramdiskSnapshotSingleton = snapshotSingleton{
+	"ramdisk",
+	"SOONG_RAMDISK_SNAPSHOT_ZIP",
+	android.OptionalPath{},
+	false,
+	ramdiskSnapshotImageSingleton,
+	false, /* fake */
+	false, /* usesLlndkLibraries */
 }
 
 func VendorSnapshotSingleton() android.Singleton {
-	return &vendorSnapshotSingleton{}
+	return &vendorSnapshotSingleton
 }
 
-type vendorSnapshotSingleton struct {
-	vendorSnapshotZipFile android.OptionalPath
+func VendorFakeSnapshotSingleton() android.Singleton {
+	return &vendorFakeSnapshotSingleton
 }
 
-var (
-	// Modules under following directories are ignored. They are OEM's and vendor's
-	// proprietary modules(device/, vendor/, and hardware/).
-	// TODO(b/65377115): Clean up these with more maintainable way
-	vendorProprietaryDirs = []string{
-		"device",
-		"vendor",
-		"hardware",
+func RecoverySnapshotSingleton() android.Singleton {
+	return &recoverySnapshotSingleton
+}
+
+func RamdiskSnapshotSingleton() android.Singleton {
+	return &ramdiskSnapshotSingleton
+}
+
+type snapshotSingleton struct {
+	// Name, e.g., "vendor", "recovery", "ramdisk".
+	name string
+
+	// Make variable that points to the snapshot file, e.g.,
+	// "SOONG_RECOVERY_SNAPSHOT_ZIP".
+	makeVar string
+
+	// Path to the snapshot zip file.
+	snapshotZipFile android.OptionalPath
+
+	// Whether the image supports VNDK extension modules.
+	supportsVndkExt bool
+
+	// Implementation of the image interface specific to the image
+	// associated with this snapshot (e.g., specific to the vendor image,
+	// recovery image, etc.).
+	image snapshotImage
+
+	// Whether this singleton is for fake snapshot or not.
+	// Fake snapshot is a snapshot whose prebuilt binaries and headers are empty.
+	// It is much faster to generate, and can be used to inspect dependencies.
+	fake bool
+
+	// Whether the image uses llndk libraries.
+	usesLlndkLibraries bool
+}
+
+// Determine if a dir under source tree is an SoC-owned proprietary directory based
+// on vendor snapshot configuration
+// Examples: device/, vendor/
+func isVendorProprietaryPath(dir string, deviceConfig android.DeviceConfig) bool {
+	return VendorSnapshotSingleton().(*snapshotSingleton).image.isProprietaryPath(dir, deviceConfig)
+}
+
+// Determine if a dir under source tree is an SoC-owned proprietary directory based
+// on recovery snapshot configuration
+// Examples: device/, vendor/
+func isRecoveryProprietaryPath(dir string, deviceConfig android.DeviceConfig) bool {
+	return RecoverySnapshotSingleton().(*snapshotSingleton).image.isProprietaryPath(dir, deviceConfig)
+}
+
+// Determine if a dir under source tree is an SoC-owned proprietary directory based
+// on ramdisk snapshot configuration
+// Examples: device/, vendor/
+func isRamdiskProprietaryPath(dir string, deviceConfig android.DeviceConfig) bool {
+	return RamdiskSnapshotSingleton().(*snapshotSingleton).image.isProprietaryPath(dir, deviceConfig)
+}
+
+func isVendorProprietaryModule(ctx android.BaseModuleContext) bool {
+	// Any module in a vendor proprietary path is a vendor proprietary
+	// module.
+	if isVendorProprietaryPath(ctx.ModuleDir(), ctx.DeviceConfig()) {
+		return true
 	}
 
-	// Modules under following directories are included as they are in AOSP,
-	// although hardware/ is normally for vendor's own.
-	// TODO(b/65377115): Clean up these with more maintainable way
-	aospDirsUnderProprietary = []string{
-		"hardware/interfaces",
-		"hardware/libhardware",
-		"hardware/libhardware_legacy",
-		"hardware/ril",
-	}
-)
-
-// Determine if a dir under source tree is an SoC-owned proprietary directory, such as
-// device/, vendor/, etc.
-func isVendorProprietaryPath(dir string) bool {
-	for _, p := range vendorProprietaryDirs {
-		if strings.HasPrefix(dir, p) {
-			// filter out AOSP defined directories, e.g. hardware/interfaces/
-			aosp := false
-			for _, p := range aospDirsUnderProprietary {
-				if strings.HasPrefix(dir, p) {
-					aosp = true
-					break
-				}
-			}
-			if !aosp {
-				return true
-			}
+	// However if the module is not in a vendor proprietary path, it may
+	// still be a vendor proprietary module. This happens for cc modules
+	// that are excluded from the vendor snapshot, and it means that the
+	// vendor has assumed control of the framework-provided module.
+	if c, ok := ctx.Module().(*Module); ok {
+		if c.ExcludeFromVendorSnapshot() {
+			return true
 		}
 	}
+
 	return false
 }
 
-// Determine if a module is going to be included in vendor snapshot or not.
-//
-// Targets of vendor snapshot are "vendor: true" or "vendor_available: true" modules in
-// AOSP. They are not guaranteed to be compatible with older vendor images. (e.g. might
-// depend on newer VNDK) So they are captured as vendor snapshot To build older vendor
-// image and newer system image altogether.
-func isVendorSnapshotModule(m *Module, moduleDir string) bool {
+func isRecoveryProprietaryModule(ctx android.BaseModuleContext) bool {
+
+	// Any module in a vendor proprietary path is a vendor proprietary
+	// module.
+	if isRecoveryProprietaryPath(ctx.ModuleDir(), ctx.DeviceConfig()) {
+		return true
+	}
+
+	// However if the module is not in a vendor proprietary path, it may
+	// still be a vendor proprietary module. This happens for cc modules
+	// that are excluded from the vendor snapshot, and it means that the
+	// vendor has assumed control of the framework-provided module.
+
+	if c, ok := ctx.Module().(*Module); ok {
+		if c.ExcludeFromRecoverySnapshot() {
+			return true
+		}
+	}
+
+	return false
+}
+func isRamdiskProprietaryModule(ctx android.BaseModuleContext) bool {
+
+	// Any module in a vendor proprietary path is a vendor proprietary
+	// module.
+	if isRamdiskProprietaryPath(ctx.ModuleDir(), ctx.DeviceConfig()) {
+		return true
+	}
+
+	// However if the module is not in a vendor proprietary path, it may
+	// still be a vendor proprietary module. This happens for cc modules
+	// that are excluded from the vendor snapshot, and it means that the
+	// vendor has assumed control of the framework-provided module.
+
+	if c, ok := ctx.Module().(*Module); ok {
+		if c.ExcludeFromRamdiskSnapshot() {
+			return true
+		}
+	}
+
+	return false
+}
+
+// Determines if the module is a candidate for snapshot.
+func isSnapshotAware(cfg android.DeviceConfig, m *Module, inProprietaryPath bool, image snapshotImage) bool {
 	if !m.Enabled() || m.Properties.HideFromMake {
 		return false
 	}
-	// skip proprietary modules, but include all VNDK (static)
-	if isVendorProprietaryPath(moduleDir) && !m.IsVndk() {
+	// When android/prebuilt.go selects between source and prebuilt, it sets
+	// SkipInstall on the other one to avoid duplicate install rules in make.
+	if m.IsSkipInstall() {
+		return false
+	}
+	// skip proprietary modules, but (for the vendor snapshot only)
+	// include all VNDK (static)
+	if inProprietaryPath && (!image.includeVndk() || !m.IsVndk()) {
+		return false
+	}
+	// If the module would be included based on its path, check to see if
+	// the module is marked to be excluded. If so, skip it.
+	if image.excludeFromSnapshot(m) {
 		return false
 	}
 	if m.Target().Os.Class != android.Device {
@@ -545,7 +219,7 @@
 		return false
 	}
 	// the module must be installed in /vendor
-	if !m.IsForPlatform() || m.isSnapshotPrebuilt() || !m.inVendor() {
+	if !m.IsForPlatform() || m.isSnapshotPrebuilt() || !image.inImage(m)() {
 		return false
 	}
 	// skip kernel_headers which always depend on vendor
@@ -557,40 +231,45 @@
 	if l, ok := m.linker.(snapshotLibraryInterface); ok {
 		// TODO(b/65377115): add full support for sanitizer
 		if m.sanitize != nil {
-			// cfi, scs and hwasan export both sanitized and unsanitized variants for static and header
+			// scs and hwasan export both sanitized and unsanitized variants for static and header
 			// Always use unsanitized variants of them.
-			for _, t := range []sanitizerType{cfi, scs, hwasan} {
+			for _, t := range []sanitizerType{scs, hwasan} {
 				if !l.shared() && m.sanitize.isSanitizerEnabled(t) {
 					return false
 				}
 			}
+			// cfi also exports both variants. But for static, we capture both.
+			if !l.static() && !l.shared() && m.sanitize.isSanitizerEnabled(cfi) {
+				return false
+			}
 		}
 		if l.static() {
-			return m.outputFile.Valid() && proptools.BoolDefault(m.VendorProperties.Vendor_available, true)
+			return m.outputFile.Valid() && proptools.BoolDefault(image.available(m), true)
 		}
 		if l.shared() {
 			if !m.outputFile.Valid() {
 				return false
 			}
-			if !m.IsVndk() {
-				return true
+			if image.includeVndk() {
+				if !m.IsVndk() {
+					return true
+				}
+				return m.isVndkExt()
 			}
-			return m.isVndkExt()
 		}
 		return true
 	}
 
 	// Binaries and Objects
 	if m.binary() || m.object() {
-		return m.outputFile.Valid() && proptools.BoolDefault(m.VendorProperties.Vendor_available, true)
+		return m.outputFile.Valid() && proptools.BoolDefault(image.available(m), true)
 	}
 
 	return false
 }
 
-func (c *vendorSnapshotSingleton) GenerateBuildActions(ctx android.SingletonContext) {
-	// BOARD_VNDK_VERSION must be set to 'current' in order to generate a vendor snapshot.
-	if ctx.DeviceConfig().VndkVersion() != "current" {
+func (c *snapshotSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	if !c.image.shouldGenerateSnapshot(ctx) {
 		return
 	}
 
@@ -629,7 +308,12 @@
 				(header files of same directory structure with source tree)
 	*/
 
-	snapshotDir := "vendor-snapshot"
+	snapshotDir := c.name + "-snapshot"
+	if c.fake {
+		// If this is a fake snapshot singleton, place all files under fake/ subdirectory to avoid
+		// collision with real snapshot files
+		snapshotDir = filepath.Join("fake", snapshotDir)
+	}
 	snapshotArchDir := filepath.Join(snapshotDir, ctx.DeviceConfig().DeviceArch())
 
 	includeDir := filepath.Join(snapshotArchDir, "include")
@@ -641,7 +325,19 @@
 
 	var headers android.Paths
 
-	installSnapshot := func(m *Module) android.Paths {
+	copyFile := func(ctx android.SingletonContext, path android.Path, out string, fake bool) android.OutputPath {
+		if fake {
+			// All prebuilt binaries and headers are installed by copyFile function. This makes a fake
+			// snapshot just touch prebuilts and headers, rather than installing real files.
+			return writeStringToFileRule(ctx, "", out)
+		} else {
+			return copyFileRule(ctx, path, out)
+		}
+	}
+
+	// installSnapshot function copies prebuilt file (.so, .a, or executable) and json flag file.
+	// For executables, init_rc and vintf_fragments files are also copied.
+	installSnapshot := func(m *Module, fake bool) android.Paths {
 		targetArch := "arch-" + m.Target().Arch.ArchType.String()
 		if m.Target().Arch.ArchVariant != "" {
 			targetArch += "-" + m.Target().Arch.ArchVariant
@@ -657,8 +353,10 @@
 			ExportedDirs       []string `json:",omitempty"`
 			ExportedSystemDirs []string `json:",omitempty"`
 			ExportedFlags      []string `json:",omitempty"`
+			Sanitize           string   `json:",omitempty"`
 			SanitizeMinimalDep bool     `json:",omitempty"`
 			SanitizeUbsanDep   bool     `json:",omitempty"`
+			IsLlndk            bool     `json:",omitempty"`
 
 			// binary flags
 			Symlinks []string `json:",omitempty"`
@@ -674,8 +372,13 @@
 		}{}
 
 		// Common properties among snapshots.
-		prop.ModuleName = ctx.ModuleName(m)
-		if m.isVndkExt() {
+		if m.isLlndk(ctx.Config()) && c.usesLlndkLibraries {
+			prop.ModuleName = m.BaseModuleName()
+			prop.IsLlndk = true
+		} else {
+			prop.ModuleName = ctx.ModuleName(m)
+		}
+		if c.supportsVndkExt && m.isVndkExt() {
 			// vndk exts are installed to /vendor/lib(64)?/vndk(-sp)?
 			if m.isVndkSp() {
 				prop.RelativeInstallPath = "vndk-sp"
@@ -699,13 +402,14 @@
 			out := filepath.Join(configsDir, path.Base())
 			if !installedConfigs[out] {
 				installedConfigs[out] = true
-				ret = append(ret, copyFile(ctx, path, out))
+				ret = append(ret, copyFile(ctx, path, out, fake))
 			}
 		}
 
 		var propOut string
 
 		if l, ok := m.linker.(snapshotLibraryInterface); ok {
+
 			// library flags
 			prop.ExportedFlags = l.exportedFlags()
 			for _, dir := range l.exportedDirs() {
@@ -738,8 +442,17 @@
 			if libType != "header" {
 				libPath := m.outputFile.Path()
 				stem = libPath.Base()
+				if l.static() && m.sanitize != nil && m.sanitize.isSanitizerEnabled(cfi) {
+					// both cfi and non-cfi variant for static libraries can exist.
+					// attach .cfi to distinguish between cfi and non-cfi.
+					// e.g. libbase.a -> libbase.cfi.a
+					ext := filepath.Ext(stem)
+					stem = strings.TrimSuffix(stem, ext) + ".cfi" + ext
+					prop.Sanitize = "cfi"
+					prop.ModuleName += ".cfi"
+				}
 				snapshotLibOut := filepath.Join(snapshotArchDir, targetArch, libType, stem)
-				ret = append(ret, copyFile(ctx, libPath, snapshotLibOut))
+				ret = append(ret, copyFile(ctx, libPath, snapshotLibOut, fake))
 			} else {
 				stem = ctx.ModuleName(m)
 			}
@@ -753,7 +466,7 @@
 			// install bin
 			binPath := m.outputFile.Path()
 			snapshotBinOut := filepath.Join(snapshotArchDir, targetArch, "binary", binPath.Base())
-			ret = append(ret, copyFile(ctx, binPath, snapshotBinOut))
+			ret = append(ret, copyFile(ctx, binPath, snapshotBinOut, fake))
 			propOut = snapshotBinOut + ".json"
 		} else if m.object() {
 			// object files aren't installed to the device, so their names can conflict.
@@ -761,7 +474,7 @@
 			objPath := m.outputFile.Path()
 			snapshotObjOut := filepath.Join(snapshotArchDir, targetArch, "object",
 				ctx.ModuleName(m)+filepath.Ext(objPath.Base()))
-			ret = append(ret, copyFile(ctx, objPath, snapshotObjOut))
+			ret = append(ret, copyFile(ctx, objPath, snapshotObjOut, fake))
 			propOut = snapshotObjOut + ".json"
 		} else {
 			ctx.Errorf("unknown module %q in vendor snapshot", m.String())
@@ -773,7 +486,7 @@
 			ctx.Errorf("json marshal to %q failed: %#v", propOut, err)
 			return nil
 		}
-		ret = append(ret, writeStringToFile(ctx, string(j), propOut))
+		ret = append(ret, writeStringToFileRule(ctx, string(j), propOut))
 
 		return ret
 	}
@@ -785,11 +498,44 @@
 		}
 
 		moduleDir := ctx.ModuleDir(module)
-		if !isVendorSnapshotModule(m, moduleDir) {
+		inProprietaryPath := c.image.isProprietaryPath(moduleDir, ctx.DeviceConfig())
+
+		if c.image.excludeFromSnapshot(m) {
+			if inProprietaryPath {
+				// Error: exclude_from_vendor_snapshot applies
+				// to framework-path modules only.
+				ctx.Errorf("module %q in vendor proprietary path %q may not use \"exclude_from_vendor_snapshot: true\"", m.String(), moduleDir)
+				return
+			}
+			if Bool(c.image.available(m)) {
+				// Error: may not combine "vendor_available:
+				// true" with "exclude_from_vendor_snapshot:
+				// true".
+				ctx.Errorf(
+					"module %q may not use both \""+
+						c.name+
+						"_available: true\" and \"exclude_from_vendor_snapshot: true\"",
+					m.String())
+				return
+			}
+		}
+
+		if !isSnapshotAware(ctx.DeviceConfig(), m, inProprietaryPath, c.image) {
 			return
 		}
 
-		snapshotOutputs = append(snapshotOutputs, installSnapshot(m)...)
+		// If we are using directed snapshot and a module is not included in the
+		// list, we will still include the module as if it was a fake module.
+		// The reason is that soong needs all the dependencies to be present, even
+		// if they are not using during the build.
+		installAsFake := c.fake
+		if c.image.excludeFromDirectedSnapshot(ctx.DeviceConfig(), m.BaseModuleName()) {
+			installAsFake = true
+		}
+
+		// installSnapshot installs prebuilts and json flag files
+		snapshotOutputs = append(snapshotOutputs, installSnapshot(m, installAsFake)...)
+		// just gather headers and notice files here, because they are to be deduplicated
 		if l, ok := m.linker.(snapshotLibraryInterface); ok {
 			headers = append(headers, l.snapshotHeaders()...)
 		}
@@ -800,16 +546,14 @@
 			// skip already copied notice file
 			if !installedNotices[noticeOut] {
 				installedNotices[noticeOut] = true
-				snapshotOutputs = append(snapshotOutputs, copyFile(
-					ctx, m.NoticeFile().Path(), noticeOut))
+				snapshotOutputs = append(snapshotOutputs, copyFile(ctx, m.NoticeFile().Path(), noticeOut, installAsFake))
 			}
 		}
 	})
 
 	// install all headers after removing duplicates
 	for _, header := range android.FirstUniquePaths(headers) {
-		snapshotOutputs = append(snapshotOutputs, copyFile(
-			ctx, header, filepath.Join(includeDir, header.String())))
+		snapshotOutputs = append(snapshotOutputs, copyFile(ctx, header, filepath.Join(includeDir, header.String()), c.fake))
 	}
 
 	// All artifacts are ready. Sort them to normalize ninja and then zip.
@@ -817,11 +561,17 @@
 		return snapshotOutputs[i].String() < snapshotOutputs[j].String()
 	})
 
-	zipPath := android.PathForOutput(ctx, snapshotDir, "vendor-"+ctx.Config().DeviceName()+".zip")
+	zipPath := android.PathForOutput(
+		ctx,
+		snapshotDir,
+		c.name+"-"+ctx.Config().DeviceName()+".zip")
 	zipRule := android.NewRuleBuilder()
 
 	// filenames in rspfile from FlagWithRspFileInputList might be single-quoted. Remove it with tr
-	snapshotOutputList := android.PathForOutput(ctx, snapshotDir, "vendor-"+ctx.Config().DeviceName()+"_list")
+	snapshotOutputList := android.PathForOutput(
+		ctx,
+		snapshotDir,
+		c.name+"-"+ctx.Config().DeviceName()+"_list")
 	zipRule.Command().
 		Text("tr").
 		FlagWithArg("-d ", "\\'").
@@ -836,148 +586,13 @@
 		FlagWithArg("-C ", android.PathForOutput(ctx, snapshotDir).String()).
 		FlagWithInput("-l ", snapshotOutputList)
 
-	zipRule.Build(pctx, ctx, zipPath.String(), "vendor snapshot "+zipPath.String())
+	zipRule.Build(pctx, ctx, zipPath.String(), c.name+" snapshot "+zipPath.String())
 	zipRule.DeleteTemporaryFiles()
-	c.vendorSnapshotZipFile = android.OptionalPathForPath(zipPath)
+	c.snapshotZipFile = android.OptionalPathForPath(zipPath)
 }
 
-func (c *vendorSnapshotSingleton) MakeVars(ctx android.MakeVarsContext) {
-	ctx.Strict("SOONG_VENDOR_SNAPSHOT_ZIP", c.vendorSnapshotZipFile.String())
-}
-
-type snapshotInterface interface {
-	matchesWithDevice(config android.DeviceConfig) bool
-}
-
-var _ snapshotInterface = (*vndkPrebuiltLibraryDecorator)(nil)
-var _ snapshotInterface = (*vendorSnapshotLibraryDecorator)(nil)
-var _ snapshotInterface = (*vendorSnapshotBinaryDecorator)(nil)
-var _ snapshotInterface = (*vendorSnapshotObjectLinker)(nil)
-
-// gathers all snapshot modules for vendor, and disable unnecessary snapshots
-// TODO(b/145966707): remove mutator and utilize android.Prebuilt to override source modules
-func VendorSnapshotMutator(ctx android.BottomUpMutatorContext) {
-	vndkVersion := ctx.DeviceConfig().VndkVersion()
-	// don't need snapshot if current
-	if vndkVersion == "current" || vndkVersion == "" {
-		return
-	}
-
-	module, ok := ctx.Module().(*Module)
-	if !ok || !module.Enabled() || module.VndkVersion() != vndkVersion {
-		return
-	}
-
-	if !module.isSnapshotPrebuilt() {
-		return
-	}
-
-	// isSnapshotPrebuilt ensures snapshotInterface
-	if !module.linker.(snapshotInterface).matchesWithDevice(ctx.DeviceConfig()) {
-		// Disable unnecessary snapshot module, but do not disable
-		// vndk_prebuilt_shared because they might be packed into vndk APEX
-		if !module.IsVndk() {
-			module.Disable()
-		}
-		return
-	}
-
-	var snapshotMap *snapshotMap
-
-	if lib, ok := module.linker.(libraryInterface); ok {
-		if lib.static() {
-			snapshotMap = vendorSnapshotStaticLibs(ctx.Config())
-		} else if lib.shared() {
-			snapshotMap = vendorSnapshotSharedLibs(ctx.Config())
-		} else {
-			// header
-			snapshotMap = vendorSnapshotHeaderLibs(ctx.Config())
-		}
-	} else if _, ok := module.linker.(*vendorSnapshotBinaryDecorator); ok {
-		snapshotMap = vendorSnapshotBinaries(ctx.Config())
-	} else if _, ok := module.linker.(*vendorSnapshotObjectLinker); ok {
-		snapshotMap = vendorSnapshotObjects(ctx.Config())
-	} else {
-		return
-	}
-
-	vendorSnapshotsLock.Lock()
-	defer vendorSnapshotsLock.Unlock()
-	snapshotMap.add(module.BaseModuleName(), ctx.Arch().ArchType, ctx.ModuleName())
-}
-
-// Disables source modules which have snapshots
-func VendorSnapshotSourceMutator(ctx android.BottomUpMutatorContext) {
-	if !ctx.Device() {
-		return
-	}
-
-	vndkVersion := ctx.DeviceConfig().VndkVersion()
-	// don't need snapshot if current
-	if vndkVersion == "current" || vndkVersion == "" {
-		return
-	}
-
-	module, ok := ctx.Module().(*Module)
-	if !ok {
-		return
-	}
-
-	// vendor suffix should be added to snapshots if the source module isn't vendor: true.
-	if !module.SocSpecific() {
-		// But we can't just check SocSpecific() since we already passed the image mutator.
-		// Check ramdisk and recovery to see if we are real "vendor: true" module.
-		ramdisk_available := module.InRamdisk() && !module.OnlyInRamdisk()
-		recovery_available := module.InRecovery() && !module.OnlyInRecovery()
-
-		if !ramdisk_available && !recovery_available {
-			vendorSnapshotsLock.Lock()
-			defer vendorSnapshotsLock.Unlock()
-
-			vendorSuffixModules(ctx.Config())[ctx.ModuleName()] = true
-		}
-	}
-
-	if module.isSnapshotPrebuilt() || module.VndkVersion() != ctx.DeviceConfig().VndkVersion() {
-		// only non-snapshot modules with BOARD_VNDK_VERSION
-		return
-	}
-
-	// .. and also filter out llndk library
-	if module.isLlndk(ctx.Config()) {
-		return
-	}
-
-	var snapshotMap *snapshotMap
-
-	if lib, ok := module.linker.(libraryInterface); ok {
-		if lib.static() {
-			snapshotMap = vendorSnapshotStaticLibs(ctx.Config())
-		} else if lib.shared() {
-			snapshotMap = vendorSnapshotSharedLibs(ctx.Config())
-		} else {
-			// header
-			snapshotMap = vendorSnapshotHeaderLibs(ctx.Config())
-		}
-	} else if module.binary() {
-		snapshotMap = vendorSnapshotBinaries(ctx.Config())
-	} else if module.object() {
-		snapshotMap = vendorSnapshotObjects(ctx.Config())
-	} else {
-		return
-	}
-
-	if _, ok := snapshotMap.get(ctx.ModuleName(), ctx.Arch().ArchType); !ok {
-		// Corresponding snapshot doesn't exist
-		return
-	}
-
-	// Disables source modules if corresponding snapshot exists.
-	if lib, ok := module.linker.(libraryInterface); ok && lib.buildStatic() && lib.buildShared() {
-		// But do not disable because the shared variant depends on the static variant.
-		module.SkipInstall()
-		module.Properties.HideFromMake = true
-	} else {
-		module.Disable()
-	}
+func (c *snapshotSingleton) MakeVars(ctx android.MakeVarsContext) {
+	ctx.Strict(
+		c.makeVar,
+		c.snapshotZipFile.String())
 }
diff --git a/cc/vendor_snapshot_test.go b/cc/vendor_snapshot_test.go
new file mode 100644
index 0000000..67f2b3d
--- /dev/null
+++ b/cc/vendor_snapshot_test.go
@@ -0,0 +1,1456 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// 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.
+
+package cc
+
+import (
+	"android/soong/android"
+	"fmt"
+	"path/filepath"
+	"reflect"
+	"strings"
+	"testing"
+)
+
+func TestVendorSnapshotCapture(t *testing.T) {
+	bp := `
+	cc_library {
+		name: "libvndk",
+		vendor_available: true,
+		product_available: true,
+		vndk: {
+			enabled: true,
+		},
+		nocrt: true,
+	}
+
+	cc_library {
+		name: "libvendor",
+		vendor: true,
+		nocrt: true,
+	}
+
+	cc_library {
+		name: "libvendor_available",
+		vendor_available: true,
+		nocrt: true,
+	}
+
+	cc_library_headers {
+		name: "libvendor_headers",
+		vendor_available: true,
+		nocrt: true,
+	}
+
+	cc_binary {
+		name: "vendor_bin",
+		vendor: true,
+		nocrt: true,
+	}
+
+	cc_binary {
+		name: "vendor_available_bin",
+		vendor_available: true,
+		nocrt: true,
+	}
+
+	toolchain_library {
+		name: "libb",
+		vendor_available: true,
+		src: "libb.a",
+	}
+
+	cc_object {
+		name: "obj",
+		vendor_available: true,
+	}
+
+	cc_library {
+		name: "libllndk",
+		llndk_stubs: "libllndk.llndk",
+	}
+
+	llndk_library {
+		name: "libllndk.llndk",
+		symbol_file: "",
+	}
+`
+	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
+	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+	ctx := testCcWithConfig(t, config)
+
+	// Check Vendor snapshot output.
+
+	snapshotDir := "vendor-snapshot"
+	snapshotVariantPath := filepath.Join(buildDir, snapshotDir, "arm64")
+	snapshotSingleton := ctx.SingletonForTests("vendor-snapshot")
+
+	var jsonFiles []string
+
+	for _, arch := range [][]string{
+		[]string{"arm64", "armv8-a"},
+		[]string{"arm", "armv7-a-neon"},
+	} {
+		archType := arch[0]
+		archVariant := arch[1]
+		archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
+
+		// For shared libraries, only non-VNDK vendor_available modules are captured
+		sharedVariant := fmt.Sprintf("android_vendor.VER_%s_%s_shared", archType, archVariant)
+		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
+		checkSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.so", sharedDir, sharedVariant)
+		checkSnapshot(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.so", sharedDir, sharedVariant)
+		jsonFiles = append(jsonFiles,
+			filepath.Join(sharedDir, "libvendor.so.json"),
+			filepath.Join(sharedDir, "libvendor_available.so.json"))
+
+		// LLNDK modules are not captured
+		checkSnapshotExclude(t, ctx, snapshotSingleton, "libllndk", "libllndk.so", sharedDir, sharedVariant)
+
+		// For static libraries, all vendor:true and vendor_available modules (including VNDK) are captured.
+		// Also cfi variants are captured, except for prebuilts like toolchain_library
+		staticVariant := fmt.Sprintf("android_vendor.VER_%s_%s_static", archType, archVariant)
+		staticCfiVariant := fmt.Sprintf("android_vendor.VER_%s_%s_static_cfi", archType, archVariant)
+		staticDir := filepath.Join(snapshotVariantPath, archDir, "static")
+		checkSnapshot(t, ctx, snapshotSingleton, "libb", "libb.a", staticDir, staticVariant)
+		checkSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.a", staticDir, staticVariant)
+		checkSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.cfi.a", staticDir, staticCfiVariant)
+		checkSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.a", staticDir, staticVariant)
+		checkSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.cfi.a", staticDir, staticCfiVariant)
+		checkSnapshot(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.a", staticDir, staticVariant)
+		checkSnapshot(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.cfi.a", staticDir, staticCfiVariant)
+		jsonFiles = append(jsonFiles,
+			filepath.Join(staticDir, "libb.a.json"),
+			filepath.Join(staticDir, "libvndk.a.json"),
+			filepath.Join(staticDir, "libvndk.cfi.a.json"),
+			filepath.Join(staticDir, "libvendor.a.json"),
+			filepath.Join(staticDir, "libvendor.cfi.a.json"),
+			filepath.Join(staticDir, "libvendor_available.a.json"),
+			filepath.Join(staticDir, "libvendor_available.cfi.a.json"))
+
+		// For binary executables, all vendor:true and vendor_available modules are captured.
+		if archType == "arm64" {
+			binaryVariant := fmt.Sprintf("android_vendor.VER_%s_%s", archType, archVariant)
+			binaryDir := filepath.Join(snapshotVariantPath, archDir, "binary")
+			checkSnapshot(t, ctx, snapshotSingleton, "vendor_bin", "vendor_bin", binaryDir, binaryVariant)
+			checkSnapshot(t, ctx, snapshotSingleton, "vendor_available_bin", "vendor_available_bin", binaryDir, binaryVariant)
+			jsonFiles = append(jsonFiles,
+				filepath.Join(binaryDir, "vendor_bin.json"),
+				filepath.Join(binaryDir, "vendor_available_bin.json"))
+		}
+
+		// For header libraries, all vendor:true and vendor_available modules are captured.
+		headerDir := filepath.Join(snapshotVariantPath, archDir, "header")
+		jsonFiles = append(jsonFiles, filepath.Join(headerDir, "libvendor_headers.json"))
+
+		// For object modules, all vendor:true and vendor_available modules are captured.
+		objectVariant := fmt.Sprintf("android_vendor.VER_%s_%s", archType, archVariant)
+		objectDir := filepath.Join(snapshotVariantPath, archDir, "object")
+		checkSnapshot(t, ctx, snapshotSingleton, "obj", "obj.o", objectDir, objectVariant)
+		jsonFiles = append(jsonFiles, filepath.Join(objectDir, "obj.o.json"))
+	}
+
+	for _, jsonFile := range jsonFiles {
+		// verify all json files exist
+		if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
+			t.Errorf("%q expected but not found", jsonFile)
+		}
+	}
+
+	// fake snapshot should have all outputs in the normal snapshot.
+	fakeSnapshotSingleton := ctx.SingletonForTests("vendor-fake-snapshot")
+	for _, output := range snapshotSingleton.AllOutputs() {
+		fakeOutput := strings.Replace(output, "/vendor-snapshot/", "/fake/vendor-snapshot/", 1)
+		if fakeSnapshotSingleton.MaybeOutput(fakeOutput).Rule == nil {
+			t.Errorf("%q expected but not found", fakeOutput)
+		}
+	}
+}
+
+func TestVendorSnapshotDirected(t *testing.T) {
+	bp := `
+	cc_library_shared {
+		name: "libvendor",
+		vendor: true,
+		nocrt: true,
+	}
+
+	cc_library_shared {
+		name: "libvendor_available",
+		vendor_available: true,
+		nocrt: true,
+	}
+
+	genrule {
+		name: "libfoo_gen",
+		cmd: "",
+		out: ["libfoo.so"],
+	}
+
+	cc_prebuilt_library_shared {
+		name: "libfoo",
+		vendor: true,
+		prefer: true,
+		srcs: [":libfoo_gen"],
+	}
+
+	cc_library_shared {
+		name: "libfoo",
+		vendor: true,
+		nocrt: true,
+	}
+`
+	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
+	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+	config.TestProductVariables.DirectedVendorSnapshot = true
+	config.TestProductVariables.VendorSnapshotModules = make(map[string]bool)
+	config.TestProductVariables.VendorSnapshotModules["libvendor"] = true
+	config.TestProductVariables.VendorSnapshotModules["libfoo"] = true
+	ctx := testCcWithConfig(t, config)
+
+	// Check Vendor snapshot output.
+
+	snapshotDir := "vendor-snapshot"
+	snapshotVariantPath := filepath.Join(buildDir, snapshotDir, "arm64")
+	snapshotSingleton := ctx.SingletonForTests("vendor-snapshot")
+
+	var includeJsonFiles []string
+
+	for _, arch := range [][]string{
+		[]string{"arm64", "armv8-a"},
+		[]string{"arm", "armv7-a-neon"},
+	} {
+		archType := arch[0]
+		archVariant := arch[1]
+		archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
+
+		sharedVariant := fmt.Sprintf("android_vendor.VER_%s_%s_shared", archType, archVariant)
+		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
+
+		// Included modules
+		checkSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.so", sharedDir, sharedVariant)
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libvendor.so.json"))
+		// Check that snapshot captures "prefer: true" prebuilt
+		checkSnapshot(t, ctx, snapshotSingleton, "prebuilt_libfoo", "libfoo.so", sharedDir, sharedVariant)
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libfoo.so.json"))
+
+		// Excluded modules. Modules not included in the directed vendor snapshot
+		// are still include as fake modules.
+		checkSnapshotRule(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.so", sharedDir, sharedVariant)
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libvendor_available.so.json"))
+	}
+
+	// Verify that each json file for an included module has a rule.
+	for _, jsonFile := range includeJsonFiles {
+		if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
+			t.Errorf("include json file %q not found", jsonFile)
+		}
+	}
+}
+
+func TestVendorSnapshotUse(t *testing.T) {
+	frameworkBp := `
+	cc_library {
+		name: "libvndk",
+		vendor_available: true,
+		product_available: true,
+		vndk: {
+			enabled: true,
+		},
+		nocrt: true,
+		compile_multilib: "64",
+	}
+
+	cc_library {
+		name: "libvendor",
+		vendor: true,
+		nocrt: true,
+		no_libcrt: true,
+		stl: "none",
+		system_shared_libs: [],
+		compile_multilib: "64",
+	}
+
+	cc_library {
+		name: "libvendor_available",
+		vendor_available: true,
+		nocrt: true,
+		no_libcrt: true,
+		stl: "none",
+		system_shared_libs: [],
+		compile_multilib: "64",
+	}
+
+	cc_binary {
+		name: "bin",
+		vendor: true,
+		nocrt: true,
+		no_libcrt: true,
+		stl: "none",
+		system_shared_libs: [],
+		compile_multilib: "64",
+	}
+`
+
+	vndkBp := `
+	vndk_prebuilt_shared {
+		name: "libvndk",
+		version: "BOARD",
+		target_arch: "arm64",
+		vendor_available: true,
+		product_available: true,
+		vndk: {
+			enabled: true,
+		},
+		arch: {
+			arm64: {
+				srcs: ["libvndk.so"],
+				export_include_dirs: ["include/libvndk"],
+			},
+		},
+	}
+
+	// old snapshot module which has to be ignored
+	vndk_prebuilt_shared {
+		name: "libvndk",
+		version: "OLD",
+		target_arch: "arm64",
+		vendor_available: true,
+		product_available: true,
+		vndk: {
+			enabled: true,
+		},
+		arch: {
+			arm64: {
+				srcs: ["libvndk.so"],
+				export_include_dirs: ["include/libvndk"],
+			},
+		},
+	}
+`
+
+	vendorProprietaryBp := `
+	cc_library {
+		name: "libvendor_without_snapshot",
+		vendor: true,
+		nocrt: true,
+		no_libcrt: true,
+		stl: "none",
+		system_shared_libs: [],
+		compile_multilib: "64",
+	}
+
+	cc_library_shared {
+		name: "libclient",
+		vendor: true,
+		nocrt: true,
+		no_libcrt: true,
+		stl: "none",
+		system_shared_libs: [],
+		shared_libs: ["libvndk", "libvendor_available"],
+		static_libs: ["libvendor", "libvendor_without_snapshot"],
+		compile_multilib: "64",
+		srcs: ["client.cpp"],
+	}
+
+	cc_binary {
+		name: "bin_without_snapshot",
+		vendor: true,
+		nocrt: true,
+		no_libcrt: true,
+		stl: "none",
+		system_shared_libs: [],
+		static_libs: ["libvndk"],
+		compile_multilib: "64",
+		srcs: ["bin.cpp"],
+	}
+
+	vendor_snapshot {
+		name: "vendor_snapshot",
+		compile_multilib: "first",
+		version: "BOARD",
+		vndk_libs: [
+			"libvndk",
+		],
+		static_libs: [
+			"libvendor",
+			"libvendor_available",
+			"libvndk",
+		],
+		shared_libs: [
+			"libvendor",
+			"libvendor_available",
+		],
+		binaries: [
+			"bin",
+		],
+	}
+
+	vendor_snapshot_static {
+		name: "libvndk",
+		version: "BOARD",
+		target_arch: "arm64",
+		vendor: true,
+		arch: {
+			arm64: {
+				src: "libvndk.a",
+				export_include_dirs: ["include/libvndk"],
+			},
+		},
+	}
+
+	vendor_snapshot_shared {
+		name: "libvendor",
+		version: "BOARD",
+		target_arch: "arm64",
+		vendor: true,
+		arch: {
+			arm64: {
+				src: "libvendor.so",
+				export_include_dirs: ["include/libvendor"],
+			},
+		},
+	}
+
+	vendor_snapshot_static {
+		name: "libvendor",
+		version: "BOARD",
+		target_arch: "arm64",
+		vendor: true,
+		arch: {
+			arm64: {
+				src: "libvendor.a",
+				export_include_dirs: ["include/libvendor"],
+			},
+		},
+	}
+
+	vendor_snapshot_shared {
+		name: "libvendor_available",
+		androidmk_suffix: ".vendor",
+		version: "BOARD",
+		target_arch: "arm64",
+		vendor: true,
+		arch: {
+			arm64: {
+				src: "libvendor_available.so",
+				export_include_dirs: ["include/libvendor"],
+			},
+		},
+	}
+
+	vendor_snapshot_static {
+		name: "libvendor_available",
+		androidmk_suffix: ".vendor",
+		version: "BOARD",
+		target_arch: "arm64",
+		vendor: true,
+		arch: {
+			arm64: {
+				src: "libvendor_available.a",
+				export_include_dirs: ["include/libvendor"],
+			},
+		},
+	}
+
+	vendor_snapshot_binary {
+		name: "bin",
+		version: "BOARD",
+		target_arch: "arm64",
+		vendor: true,
+		arch: {
+			arm64: {
+				src: "bin",
+			},
+		},
+	}
+
+	// old snapshot module which has to be ignored
+	vendor_snapshot_binary {
+		name: "bin",
+		version: "OLD",
+		target_arch: "arm64",
+		vendor: true,
+		arch: {
+			arm64: {
+				src: "bin",
+			},
+		},
+	}
+`
+	depsBp := GatherRequiredDepsForTest(android.Android)
+
+	mockFS := map[string][]byte{
+		"deps/Android.bp":              []byte(depsBp),
+		"framework/Android.bp":         []byte(frameworkBp),
+		"vendor/Android.bp":            []byte(vendorProprietaryBp),
+		"vendor/bin":                   nil,
+		"vendor/bin.cpp":               nil,
+		"vendor/client.cpp":            nil,
+		"vendor/include/libvndk/a.h":   nil,
+		"vendor/include/libvendor/b.h": nil,
+		"vendor/libvndk.a":             nil,
+		"vendor/libvendor.a":           nil,
+		"vendor/libvendor.so":          nil,
+		"vndk/Android.bp":              []byte(vndkBp),
+		"vndk/include/libvndk/a.h":     nil,
+		"vndk/libvndk.so":              nil,
+	}
+
+	config := TestConfig(buildDir, android.Android, nil, "", mockFS)
+	config.TestProductVariables.DeviceVndkVersion = StringPtr("BOARD")
+	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+	ctx := CreateTestContext(config)
+	ctx.Register()
+
+	_, errs := ctx.ParseFileList(".", []string{"deps/Android.bp", "framework/Android.bp", "vendor/Android.bp", "vndk/Android.bp"})
+	android.FailIfErrored(t, errs)
+	_, errs = ctx.PrepareBuildActions(config)
+	android.FailIfErrored(t, errs)
+
+	sharedVariant := "android_vendor.BOARD_arm64_armv8-a_shared"
+	staticVariant := "android_vendor.BOARD_arm64_armv8-a_static"
+	binaryVariant := "android_vendor.BOARD_arm64_armv8-a"
+
+	// libclient uses libvndk.vndk.BOARD.arm64, libvendor.vendor_static.BOARD.arm64, libvendor_without_snapshot
+	libclientCcFlags := ctx.ModuleForTests("libclient", sharedVariant).Rule("cc").Args["cFlags"]
+	for _, includeFlags := range []string{
+		"-Ivndk/include/libvndk",     // libvndk
+		"-Ivendor/include/libvendor", // libvendor
+	} {
+		if !strings.Contains(libclientCcFlags, includeFlags) {
+			t.Errorf("flags for libclient must contain %#v, but was %#v.",
+				includeFlags, libclientCcFlags)
+		}
+	}
+
+	libclientLdFlags := ctx.ModuleForTests("libclient", sharedVariant).Rule("ld").Args["libFlags"]
+	for _, input := range [][]string{
+		[]string{sharedVariant, "libvndk.vndk.BOARD.arm64"},
+		[]string{staticVariant, "libvendor.vendor_static.BOARD.arm64"},
+		[]string{staticVariant, "libvendor_without_snapshot"},
+	} {
+		outputPaths := getOutputPaths(ctx, input[0] /* variant */, []string{input[1]} /* module name */)
+		if !strings.Contains(libclientLdFlags, outputPaths[0].String()) {
+			t.Errorf("libflags for libclient must contain %#v, but was %#v", outputPaths[0], libclientLdFlags)
+		}
+	}
+
+	libclientAndroidMkSharedLibs := ctx.ModuleForTests("libclient", sharedVariant).Module().(*Module).Properties.AndroidMkSharedLibs
+	if g, w := libclientAndroidMkSharedLibs, []string{"libvndk.vendor", "libvendor_available.vendor"}; !reflect.DeepEqual(g, w) {
+		t.Errorf("wanted libclient AndroidMkSharedLibs %q, got %q", w, g)
+	}
+
+	libclientAndroidMkStaticLibs := ctx.ModuleForTests("libclient", sharedVariant).Module().(*Module).Properties.AndroidMkStaticLibs
+	if g, w := libclientAndroidMkStaticLibs, []string{"libvendor", "libvendor_without_snapshot"}; !reflect.DeepEqual(g, w) {
+		t.Errorf("wanted libclient AndroidMkStaticLibs %q, got %q", w, g)
+	}
+
+	// bin_without_snapshot uses libvndk.vendor_static.BOARD.arm64
+	binWithoutSnapshotCcFlags := ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Rule("cc").Args["cFlags"]
+	if !strings.Contains(binWithoutSnapshotCcFlags, "-Ivendor/include/libvndk") {
+		t.Errorf("flags for bin_without_snapshot must contain %#v, but was %#v.",
+			"-Ivendor/include/libvndk", binWithoutSnapshotCcFlags)
+	}
+
+	binWithoutSnapshotLdFlags := ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Rule("ld").Args["libFlags"]
+	libVndkStaticOutputPaths := getOutputPaths(ctx, staticVariant, []string{"libvndk.vendor_static.BOARD.arm64"})
+	if !strings.Contains(binWithoutSnapshotLdFlags, libVndkStaticOutputPaths[0].String()) {
+		t.Errorf("libflags for bin_without_snapshot must contain %#v, but was %#v",
+			libVndkStaticOutputPaths[0], binWithoutSnapshotLdFlags)
+	}
+
+	// libvendor.so is installed by libvendor.vendor_shared.BOARD.arm64
+	ctx.ModuleForTests("libvendor.vendor_shared.BOARD.arm64", sharedVariant).Output("libvendor.so")
+
+	// libvendor_available.so is installed by libvendor_available.vendor_shared.BOARD.arm64
+	ctx.ModuleForTests("libvendor_available.vendor_shared.BOARD.arm64", sharedVariant).Output("libvendor_available.so")
+
+	// libvendor_without_snapshot.so is installed by libvendor_without_snapshot
+	ctx.ModuleForTests("libvendor_without_snapshot", sharedVariant).Output("libvendor_without_snapshot.so")
+
+	// bin is installed by bin.vendor_binary.BOARD.arm64
+	ctx.ModuleForTests("bin.vendor_binary.BOARD.arm64", binaryVariant).Output("bin")
+
+	// bin_without_snapshot is installed by bin_without_snapshot
+	ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Output("bin_without_snapshot")
+
+	// libvendor, libvendor_available and bin don't have vendor.BOARD variant
+	libvendorVariants := ctx.ModuleVariantsForTests("libvendor")
+	if inList(sharedVariant, libvendorVariants) {
+		t.Errorf("libvendor must not have variant %#v, but it does", sharedVariant)
+	}
+
+	libvendorAvailableVariants := ctx.ModuleVariantsForTests("libvendor_available")
+	if inList(sharedVariant, libvendorAvailableVariants) {
+		t.Errorf("libvendor_available must not have variant %#v, but it does", sharedVariant)
+	}
+
+	binVariants := ctx.ModuleVariantsForTests("bin")
+	if inList(binaryVariant, binVariants) {
+		t.Errorf("bin must not have variant %#v, but it does", sharedVariant)
+	}
+}
+
+func TestVendorSnapshotSanitizer(t *testing.T) {
+	bp := `
+	vendor_snapshot_static {
+		name: "libsnapshot",
+		vendor: true,
+		target_arch: "arm64",
+		version: "BOARD",
+		arch: {
+			arm64: {
+				src: "libsnapshot.a",
+				cfi: {
+					src: "libsnapshot.cfi.a",
+				}
+			},
+		},
+	}
+`
+	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config.TestProductVariables.DeviceVndkVersion = StringPtr("BOARD")
+	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+	ctx := testCcWithConfig(t, config)
+
+	// Check non-cfi and cfi variant.
+	staticVariant := "android_vendor.BOARD_arm64_armv8-a_static"
+	staticCfiVariant := "android_vendor.BOARD_arm64_armv8-a_static_cfi"
+
+	staticModule := ctx.ModuleForTests("libsnapshot.vendor_static.BOARD.arm64", staticVariant).Module().(*Module)
+	assertString(t, staticModule.outputFile.Path().Base(), "libsnapshot.a")
+
+	staticCfiModule := ctx.ModuleForTests("libsnapshot.vendor_static.BOARD.arm64", staticCfiVariant).Module().(*Module)
+	assertString(t, staticCfiModule.outputFile.Path().Base(), "libsnapshot.cfi.a")
+}
+
+func assertExcludeFromVendorSnapshotIs(t *testing.T, ctx *android.TestContext, name string, expected bool) {
+	t.Helper()
+	m := ctx.ModuleForTests(name, vendorVariant).Module().(*Module)
+	if m.ExcludeFromVendorSnapshot() != expected {
+		t.Errorf("expected %q ExcludeFromVendorSnapshot to be %t", m.String(), expected)
+	}
+}
+
+func assertExcludeFromRecoverySnapshotIs(t *testing.T, ctx *android.TestContext, name string, expected bool) {
+	t.Helper()
+	m := ctx.ModuleForTests(name, recoveryVariant).Module().(*Module)
+	if m.ExcludeFromRecoverySnapshot() != expected {
+		t.Errorf("expected %q ExcludeFromRecoverySnapshot to be %t", m.String(), expected)
+	}
+}
+
+func TestVendorSnapshotExclude(t *testing.T) {
+
+	// This test verifies that the exclude_from_vendor_snapshot property
+	// makes its way from the Android.bp source file into the module data
+	// structure. It also verifies that modules are correctly included or
+	// excluded in the vendor snapshot based on their path (framework or
+	// vendor) and the exclude_from_vendor_snapshot property.
+
+	frameworkBp := `
+		cc_library_shared {
+			name: "libinclude",
+			srcs: ["src/include.cpp"],
+			vendor_available: true,
+		}
+		cc_library_shared {
+			name: "libexclude",
+			srcs: ["src/exclude.cpp"],
+			vendor: true,
+			exclude_from_vendor_snapshot: true,
+		}
+		cc_library_shared {
+			name: "libavailable_exclude",
+			srcs: ["src/exclude.cpp"],
+			vendor_available: true,
+			exclude_from_vendor_snapshot: true,
+		}
+	`
+
+	vendorProprietaryBp := `
+		cc_library_shared {
+			name: "libvendor",
+			srcs: ["vendor.cpp"],
+			vendor: true,
+		}
+	`
+
+	depsBp := GatherRequiredDepsForTest(android.Android)
+
+	mockFS := map[string][]byte{
+		"deps/Android.bp":       []byte(depsBp),
+		"framework/Android.bp":  []byte(frameworkBp),
+		"framework/include.cpp": nil,
+		"framework/exclude.cpp": nil,
+		"device/Android.bp":     []byte(vendorProprietaryBp),
+		"device/vendor.cpp":     nil,
+	}
+
+	config := TestConfig(buildDir, android.Android, nil, "", mockFS)
+	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
+	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+	ctx := CreateTestContext(config)
+	ctx.Register()
+
+	_, errs := ctx.ParseFileList(".", []string{"deps/Android.bp", "framework/Android.bp", "device/Android.bp"})
+	android.FailIfErrored(t, errs)
+	_, errs = ctx.PrepareBuildActions(config)
+	android.FailIfErrored(t, errs)
+
+	// Test an include and exclude framework module.
+	assertExcludeFromVendorSnapshotIs(t, ctx, "libinclude", false)
+	assertExcludeFromVendorSnapshotIs(t, ctx, "libexclude", true)
+	assertExcludeFromVendorSnapshotIs(t, ctx, "libavailable_exclude", true)
+
+	// A vendor module is excluded, but by its path, not the
+	// exclude_from_vendor_snapshot property.
+	assertExcludeFromVendorSnapshotIs(t, ctx, "libvendor", false)
+
+	// Verify the content of the vendor snapshot.
+
+	snapshotDir := "vendor-snapshot"
+	snapshotVariantPath := filepath.Join(buildDir, snapshotDir, "arm64")
+	snapshotSingleton := ctx.SingletonForTests("vendor-snapshot")
+
+	var includeJsonFiles []string
+	var excludeJsonFiles []string
+
+	for _, arch := range [][]string{
+		[]string{"arm64", "armv8-a"},
+		[]string{"arm", "armv7-a-neon"},
+	} {
+		archType := arch[0]
+		archVariant := arch[1]
+		archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
+
+		sharedVariant := fmt.Sprintf("android_vendor.VER_%s_%s_shared", archType, archVariant)
+		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
+
+		// Included modules
+		checkSnapshot(t, ctx, snapshotSingleton, "libinclude", "libinclude.so", sharedDir, sharedVariant)
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libinclude.so.json"))
+
+		// Excluded modules
+		checkSnapshotExclude(t, ctx, snapshotSingleton, "libexclude", "libexclude.so", sharedDir, sharedVariant)
+		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libexclude.so.json"))
+		checkSnapshotExclude(t, ctx, snapshotSingleton, "libvendor", "libvendor.so", sharedDir, sharedVariant)
+		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libvendor.so.json"))
+		checkSnapshotExclude(t, ctx, snapshotSingleton, "libavailable_exclude", "libavailable_exclude.so", sharedDir, sharedVariant)
+		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libavailable_exclude.so.json"))
+	}
+
+	// Verify that each json file for an included module has a rule.
+	for _, jsonFile := range includeJsonFiles {
+		if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
+			t.Errorf("include json file %q not found", jsonFile)
+		}
+	}
+
+	// Verify that each json file for an excluded module has no rule.
+	for _, jsonFile := range excludeJsonFiles {
+		if snapshotSingleton.MaybeOutput(jsonFile).Rule != nil {
+			t.Errorf("exclude json file %q found", jsonFile)
+		}
+	}
+}
+
+func TestVendorSnapshotExcludeInVendorProprietaryPathErrors(t *testing.T) {
+
+	// This test verifies that using the exclude_from_vendor_snapshot
+	// property on a module in a vendor proprietary path generates an
+	// error. These modules are already excluded, so we prohibit using the
+	// property in this way, which could add to confusion.
+
+	vendorProprietaryBp := `
+		cc_library_shared {
+			name: "libvendor",
+			srcs: ["vendor.cpp"],
+			vendor: true,
+			exclude_from_vendor_snapshot: true,
+		}
+	`
+
+	depsBp := GatherRequiredDepsForTest(android.Android)
+
+	mockFS := map[string][]byte{
+		"deps/Android.bp":   []byte(depsBp),
+		"device/Android.bp": []byte(vendorProprietaryBp),
+		"device/vendor.cpp": nil,
+	}
+
+	config := TestConfig(buildDir, android.Android, nil, "", mockFS)
+	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
+	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+	ctx := CreateTestContext(config)
+	ctx.Register()
+
+	_, errs := ctx.ParseFileList(".", []string{"deps/Android.bp", "device/Android.bp"})
+	android.FailIfErrored(t, errs)
+
+	_, errs = ctx.PrepareBuildActions(config)
+	android.CheckErrorsAgainstExpectations(t, errs, []string{
+		`module "libvendor\{.+,image:vendor.+,arch:arm64_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
+		`module "libvendor\{.+,image:vendor.+,arch:arm_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
+		`module "libvendor\{.+,image:vendor.+,arch:arm64_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
+		`module "libvendor\{.+,image:vendor.+,arch:arm_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
+		`module "libvendor\{.+,image:vendor.+,arch:arm64_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
+		`module "libvendor\{.+,image:vendor.+,arch:arm_.+\}" in vendor proprietary path "device" may not use "exclude_from_vendor_snapshot: true"`,
+	})
+}
+
+func TestRecoverySnapshotCapture(t *testing.T) {
+	bp := `
+	cc_library {
+		name: "libvndk",
+		vendor_available: true,
+		recovery_available: true,
+		product_available: true,
+		vndk: {
+			enabled: true,
+		},
+		nocrt: true,
+	}
+
+	cc_library {
+		name: "librecovery",
+		recovery: true,
+		nocrt: true,
+	}
+
+	cc_library {
+		name: "librecovery_available",
+		recovery_available: true,
+		nocrt: true,
+	}
+
+	cc_library_headers {
+		name: "librecovery_headers",
+		recovery_available: true,
+		nocrt: true,
+	}
+
+	cc_binary {
+		name: "recovery_bin",
+		recovery: true,
+		nocrt: true,
+	}
+
+	cc_binary {
+		name: "recovery_available_bin",
+		recovery_available: true,
+		nocrt: true,
+	}
+
+	toolchain_library {
+		name: "libb",
+		recovery_available: true,
+		src: "libb.a",
+	}
+
+	cc_object {
+		name: "obj",
+		recovery_available: true,
+	}
+`
+	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config.TestProductVariables.RecoverySnapshotVersion = StringPtr("current")
+	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+	ctx := testCcWithConfig(t, config)
+
+	// Check Recovery snapshot output.
+
+	snapshotDir := "recovery-snapshot"
+	snapshotVariantPath := filepath.Join(buildDir, snapshotDir, "arm64")
+	snapshotSingleton := ctx.SingletonForTests("recovery-snapshot")
+
+	var jsonFiles []string
+
+	for _, arch := range [][]string{
+		[]string{"arm64", "armv8-a"},
+	} {
+		archType := arch[0]
+		archVariant := arch[1]
+		archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
+
+		// For shared libraries, only recovery_available modules are captured.
+		sharedVariant := fmt.Sprintf("android_recovery_%s_%s_shared", archType, archVariant)
+		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
+		checkSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.so", sharedDir, sharedVariant)
+		checkSnapshot(t, ctx, snapshotSingleton, "librecovery", "librecovery.so", sharedDir, sharedVariant)
+		checkSnapshot(t, ctx, snapshotSingleton, "librecovery_available", "librecovery_available.so", sharedDir, sharedVariant)
+		jsonFiles = append(jsonFiles,
+			filepath.Join(sharedDir, "libvndk.so.json"),
+			filepath.Join(sharedDir, "librecovery.so.json"),
+			filepath.Join(sharedDir, "librecovery_available.so.json"))
+
+		// For static libraries, all recovery:true and recovery_available modules are captured.
+		staticVariant := fmt.Sprintf("android_recovery_%s_%s_static", archType, archVariant)
+		staticDir := filepath.Join(snapshotVariantPath, archDir, "static")
+		checkSnapshot(t, ctx, snapshotSingleton, "libb", "libb.a", staticDir, staticVariant)
+		checkSnapshot(t, ctx, snapshotSingleton, "librecovery", "librecovery.a", staticDir, staticVariant)
+		checkSnapshot(t, ctx, snapshotSingleton, "librecovery_available", "librecovery_available.a", staticDir, staticVariant)
+		jsonFiles = append(jsonFiles,
+			filepath.Join(staticDir, "libb.a.json"),
+			filepath.Join(staticDir, "librecovery.a.json"),
+			filepath.Join(staticDir, "librecovery_available.a.json"))
+
+		// For binary executables, all recovery:true and recovery_available modules are captured.
+		if archType == "arm64" {
+			binaryVariant := fmt.Sprintf("android_recovery_%s_%s", archType, archVariant)
+			binaryDir := filepath.Join(snapshotVariantPath, archDir, "binary")
+			checkSnapshot(t, ctx, snapshotSingleton, "recovery_bin", "recovery_bin", binaryDir, binaryVariant)
+			checkSnapshot(t, ctx, snapshotSingleton, "recovery_available_bin", "recovery_available_bin", binaryDir, binaryVariant)
+			jsonFiles = append(jsonFiles,
+				filepath.Join(binaryDir, "recovery_bin.json"),
+				filepath.Join(binaryDir, "recovery_available_bin.json"))
+		}
+
+		// For header libraries, all vendor:true and vendor_available modules are captured.
+		headerDir := filepath.Join(snapshotVariantPath, archDir, "header")
+		jsonFiles = append(jsonFiles, filepath.Join(headerDir, "librecovery_headers.json"))
+
+		// For object modules, all vendor:true and vendor_available modules are captured.
+		objectVariant := fmt.Sprintf("android_recovery_%s_%s", archType, archVariant)
+		objectDir := filepath.Join(snapshotVariantPath, archDir, "object")
+		checkSnapshot(t, ctx, snapshotSingleton, "obj", "obj.o", objectDir, objectVariant)
+		jsonFiles = append(jsonFiles, filepath.Join(objectDir, "obj.o.json"))
+	}
+
+	for _, jsonFile := range jsonFiles {
+		// verify all json files exist
+		if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
+			t.Errorf("%q expected but not found", jsonFile)
+		}
+	}
+}
+
+func TestRecoverySnapshotExclude(t *testing.T) {
+	// This test verifies that the exclude_from_recovery_snapshot property
+	// makes its way from the Android.bp source file into the module data
+	// structure. It also verifies that modules are correctly included or
+	// excluded in the recovery snapshot based on their path (framework or
+	// vendor) and the exclude_from_recovery_snapshot property.
+
+	frameworkBp := `
+		cc_library_shared {
+			name: "libinclude",
+			srcs: ["src/include.cpp"],
+			recovery_available: true,
+		}
+		cc_library_shared {
+			name: "libexclude",
+			srcs: ["src/exclude.cpp"],
+			recovery: true,
+			exclude_from_recovery_snapshot: true,
+		}
+		cc_library_shared {
+			name: "libavailable_exclude",
+			srcs: ["src/exclude.cpp"],
+			recovery_available: true,
+			exclude_from_recovery_snapshot: true,
+		}
+	`
+
+	vendorProprietaryBp := `
+		cc_library_shared {
+			name: "librecovery",
+			srcs: ["recovery.cpp"],
+			recovery: true,
+		}
+	`
+
+	depsBp := GatherRequiredDepsForTest(android.Android)
+
+	mockFS := map[string][]byte{
+		"deps/Android.bp":       []byte(depsBp),
+		"framework/Android.bp":  []byte(frameworkBp),
+		"framework/include.cpp": nil,
+		"framework/exclude.cpp": nil,
+		"device/Android.bp":     []byte(vendorProprietaryBp),
+		"device/recovery.cpp":   nil,
+	}
+
+	config := TestConfig(buildDir, android.Android, nil, "", mockFS)
+	config.TestProductVariables.RecoverySnapshotVersion = StringPtr("current")
+	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+	ctx := CreateTestContext(config)
+	ctx.Register()
+
+	_, errs := ctx.ParseFileList(".", []string{"deps/Android.bp", "framework/Android.bp", "device/Android.bp"})
+	android.FailIfErrored(t, errs)
+	_, errs = ctx.PrepareBuildActions(config)
+	android.FailIfErrored(t, errs)
+
+	// Test an include and exclude framework module.
+	assertExcludeFromRecoverySnapshotIs(t, ctx, "libinclude", false)
+	assertExcludeFromRecoverySnapshotIs(t, ctx, "libexclude", true)
+	assertExcludeFromRecoverySnapshotIs(t, ctx, "libavailable_exclude", true)
+
+	// A recovery module is excluded, but by its path, not the
+	// exclude_from_recovery_snapshot property.
+	assertExcludeFromRecoverySnapshotIs(t, ctx, "librecovery", false)
+
+	// Verify the content of the recovery snapshot.
+
+	snapshotDir := "recovery-snapshot"
+	snapshotVariantPath := filepath.Join(buildDir, snapshotDir, "arm64")
+	snapshotSingleton := ctx.SingletonForTests("recovery-snapshot")
+
+	var includeJsonFiles []string
+	var excludeJsonFiles []string
+
+	for _, arch := range [][]string{
+		[]string{"arm64", "armv8-a"},
+	} {
+		archType := arch[0]
+		archVariant := arch[1]
+		archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
+
+		sharedVariant := fmt.Sprintf("android_recovery_%s_%s_shared", archType, archVariant)
+		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
+
+		// Included modules
+		checkSnapshot(t, ctx, snapshotSingleton, "libinclude", "libinclude.so", sharedDir, sharedVariant)
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libinclude.so.json"))
+
+		// Excluded modules
+		checkSnapshotExclude(t, ctx, snapshotSingleton, "libexclude", "libexclude.so", sharedDir, sharedVariant)
+		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libexclude.so.json"))
+		checkSnapshotExclude(t, ctx, snapshotSingleton, "librecovery", "librecovery.so", sharedDir, sharedVariant)
+		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "librecovery.so.json"))
+		checkSnapshotExclude(t, ctx, snapshotSingleton, "libavailable_exclude", "libavailable_exclude.so", sharedDir, sharedVariant)
+		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libavailable_exclude.so.json"))
+	}
+
+	// Verify that each json file for an included module has a rule.
+	for _, jsonFile := range includeJsonFiles {
+		if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
+			t.Errorf("include json file %q not found", jsonFile)
+		}
+	}
+
+	// Verify that each json file for an excluded module has no rule.
+	for _, jsonFile := range excludeJsonFiles {
+		if snapshotSingleton.MaybeOutput(jsonFile).Rule != nil {
+			t.Errorf("exclude json file %q found", jsonFile)
+		}
+	}
+}
+
+func TestRecoverySnapshotDirected(t *testing.T) {
+	bp := `
+	cc_library_shared {
+		name: "librecovery",
+		recovery: true,
+		nocrt: true,
+	}
+
+	cc_library_shared {
+		name: "librecovery_available",
+		recovery_available: true,
+		nocrt: true,
+	}
+
+	genrule {
+		name: "libfoo_gen",
+		cmd: "",
+		out: ["libfoo.so"],
+	}
+
+	cc_prebuilt_library_shared {
+		name: "libfoo",
+		recovery: true,
+		prefer: true,
+		srcs: [":libfoo_gen"],
+	}
+
+	cc_library_shared {
+		name: "libfoo",
+		recovery: true,
+		nocrt: true,
+	}
+`
+	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
+	config.TestProductVariables.RecoverySnapshotVersion = StringPtr("current")
+	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+	config.TestProductVariables.DirectedRecoverySnapshot = true
+	config.TestProductVariables.RecoverySnapshotModules = make(map[string]bool)
+	config.TestProductVariables.RecoverySnapshotModules["librecovery"] = true
+	config.TestProductVariables.RecoverySnapshotModules["libfoo"] = true
+	ctx := testCcWithConfig(t, config)
+
+	// Check recovery snapshot output.
+
+	snapshotDir := "recovery-snapshot"
+	snapshotVariantPath := filepath.Join(buildDir, snapshotDir, "arm64")
+	snapshotSingleton := ctx.SingletonForTests("recovery-snapshot")
+
+	var includeJsonFiles []string
+
+	for _, arch := range [][]string{
+		[]string{"arm64", "armv8-a"},
+	} {
+		archType := arch[0]
+		archVariant := arch[1]
+		archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
+
+		sharedVariant := fmt.Sprintf("android_recovery_%s_%s_shared", archType, archVariant)
+		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
+
+		// Included modules
+		checkSnapshot(t, ctx, snapshotSingleton, "librecovery", "librecovery.so", sharedDir, sharedVariant)
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "librecovery.so.json"))
+		// Check that snapshot captures "prefer: true" prebuilt
+		checkSnapshot(t, ctx, snapshotSingleton, "prebuilt_libfoo", "libfoo.so", sharedDir, sharedVariant)
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libfoo.so.json"))
+
+		// Excluded modules. Modules not included in the directed recovery snapshot
+		// are still include as fake modules.
+		checkSnapshotRule(t, ctx, snapshotSingleton, "librecovery_available", "librecovery_available.so", sharedDir, sharedVariant)
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "librecovery_available.so.json"))
+	}
+
+	// Verify that each json file for an included module has a rule.
+	for _, jsonFile := range includeJsonFiles {
+		if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
+			t.Errorf("include json file %q not found", jsonFile)
+		}
+	}
+}
+
+func TestRamdiskSnapshotCapture(t *testing.T) {
+	bp := `
+	cc_library {
+		name: "libvndk",
+		vendor_available: true,
+		ramdisk_available: true,
+		product_available: true,
+		vndk: {
+			enabled: true,
+		},
+		nocrt: true,
+	}
+
+	cc_library {
+		name: "libramdisk",
+		ramdisk: true,
+		nocrt: true,
+	}
+
+	cc_library {
+		name: "libramdisk_available",
+		ramdisk_available: true,
+		nocrt: true,
+	}
+
+	cc_library_headers {
+		name: "libramdisk_headers",
+		ramdisk_available: true,
+		nocrt: true,
+	}
+
+	cc_binary {
+		name: "ramdisk_bin",
+		ramdisk: true,
+		nocrt: true,
+	}
+
+	cc_binary {
+		name: "ramdisk_available_bin",
+		ramdisk_available: true,
+		nocrt: true,
+	}
+
+	toolchain_library {
+		name: "libb",
+		ramdisk_available: true,
+		src: "libb.a",
+	}
+
+	cc_object {
+		name: "obj",
+		ramdisk_available: true,
+	}
+`
+	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config.TestProductVariables.RamdiskSnapshotVersion = StringPtr("current")
+	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+	ctx := testCcWithConfig(t, config)
+
+	// Check Ramdisk snapshot output.
+
+	snapshotDir := "ramdisk-snapshot"
+	snapshotVariantPath := filepath.Join(buildDir, snapshotDir, "arm64")
+	snapshotSingleton := ctx.SingletonForTests("ramdisk-snapshot")
+
+	var jsonFiles []string
+
+	for _, arch := range [][]string{
+		[]string{"arm64", "armv8-a"},
+	} {
+		archType := arch[0]
+		archVariant := arch[1]
+		archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
+
+		// For shared libraries, only ramdisk_available modules are captured.
+		sharedVariant := fmt.Sprintf("android_ramdisk_%s_%s_shared", archType, archVariant)
+		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
+		checkSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.so", sharedDir, sharedVariant)
+		checkSnapshot(t, ctx, snapshotSingleton, "libramdisk", "libramdisk.so", sharedDir, sharedVariant)
+		checkSnapshot(t, ctx, snapshotSingleton, "libramdisk_available", "libramdisk_available.so", sharedDir, sharedVariant)
+		jsonFiles = append(jsonFiles,
+			filepath.Join(sharedDir, "libvndk.so.json"),
+			filepath.Join(sharedDir, "libramdisk.so.json"),
+			filepath.Join(sharedDir, "libramdisk_available.so.json"))
+
+		// For static libraries, all ramdisk:true and ramdisk_available modules are captured.
+		staticVariant := fmt.Sprintf("android_ramdisk_%s_%s_static", archType, archVariant)
+		staticDir := filepath.Join(snapshotVariantPath, archDir, "static")
+		checkSnapshot(t, ctx, snapshotSingleton, "libb", "libb.a", staticDir, staticVariant)
+		checkSnapshot(t, ctx, snapshotSingleton, "libramdisk", "libramdisk.a", staticDir, staticVariant)
+		checkSnapshot(t, ctx, snapshotSingleton, "libramdisk_available", "libramdisk_available.a", staticDir, staticVariant)
+		jsonFiles = append(jsonFiles,
+			filepath.Join(staticDir, "libb.a.json"),
+			filepath.Join(staticDir, "libramdisk.a.json"),
+			filepath.Join(staticDir, "libramdisk_available.a.json"))
+
+		// For binary executables, all ramdisk:true and ramdisk_available modules are captured.
+		if archType == "arm64" {
+			binaryVariant := fmt.Sprintf("android_ramdisk_%s_%s", archType, archVariant)
+			binaryDir := filepath.Join(snapshotVariantPath, archDir, "binary")
+			checkSnapshot(t, ctx, snapshotSingleton, "ramdisk_bin", "ramdisk_bin", binaryDir, binaryVariant)
+			checkSnapshot(t, ctx, snapshotSingleton, "ramdisk_available_bin", "ramdisk_available_bin", binaryDir, binaryVariant)
+			jsonFiles = append(jsonFiles,
+				filepath.Join(binaryDir, "ramdisk_bin.json"),
+				filepath.Join(binaryDir, "ramdisk_available_bin.json"))
+		}
+
+		// For header libraries, all vendor:true and vendor_available modules are captured.
+		headerDir := filepath.Join(snapshotVariantPath, archDir, "header")
+		jsonFiles = append(jsonFiles, filepath.Join(headerDir, "libramdisk_headers.json"))
+
+		// For object modules, all vendor:true and vendor_available modules are captured.
+		objectVariant := fmt.Sprintf("android_ramdisk_%s_%s", archType, archVariant)
+		objectDir := filepath.Join(snapshotVariantPath, archDir, "object")
+		checkSnapshot(t, ctx, snapshotSingleton, "obj", "obj.o", objectDir, objectVariant)
+		jsonFiles = append(jsonFiles, filepath.Join(objectDir, "obj.o.json"))
+	}
+
+	for _, jsonFile := range jsonFiles {
+		// verify all json files exist
+		if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
+			t.Errorf("%q expected but not found", jsonFile)
+		}
+	}
+}
+
+func TestRamdiskSnapshotExclude(t *testing.T) {
+	// This test verifies that the exclude_from_ramdisk_snapshot property
+	// makes its way from the Android.bp source file into the module data
+	// structure. It also verifies that modules are correctly included or
+	// excluded in the ramdisk snapshot based on their path (framework or
+	// vendor) and the exclude_from_ramdisk_snapshot property.
+
+	frameworkBp := `
+		cc_library_shared {
+			name: "libinclude",
+			srcs: ["src/include.cpp"],
+			ramdisk_available: true,
+		}
+		cc_library_shared {
+			name: "libexclude",
+			srcs: ["src/exclude.cpp"],
+			ramdisk: true,
+			exclude_from_ramdisk_snapshot: true,
+		}
+		cc_library_shared {
+			name: "libavailable_exclude",
+			srcs: ["src/exclude.cpp"],
+			ramdisk_available: true,
+			exclude_from_ramdisk_snapshot: true,
+		}
+	`
+
+	vendorProprietaryBp := `
+		cc_library_shared {
+			name: "libramdisk",
+			srcs: ["ramdisk.cpp"],
+			ramdisk: true,
+		}
+	`
+
+	depsBp := GatherRequiredDepsForTest(android.Android)
+
+	mockFS := map[string][]byte{
+		"deps/Android.bp":       []byte(depsBp),
+		"framework/Android.bp":  []byte(frameworkBp),
+		"framework/include.cpp": nil,
+		"framework/exclude.cpp": nil,
+		"device/Android.bp":     []byte(vendorProprietaryBp),
+		"device/ramdisk.cpp":   nil,
+	}
+
+	config := TestConfig(buildDir, android.Android, nil, "", mockFS)
+	config.TestProductVariables.RamdiskSnapshotVersion = StringPtr("current")
+	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+	ctx := CreateTestContext(config)
+	ctx.Register()
+
+	_, errs := ctx.ParseFileList(".", []string{"deps/Android.bp", "framework/Android.bp", "device/Android.bp"})
+	android.FailIfErrored(t, errs)
+	_, errs = ctx.PrepareBuildActions(config)
+	android.FailIfErrored(t, errs)
+
+	// Test an include and exclude framework module.
+	assertExcludeFromRamdiskSnapshotIs(t, ctx, "libinclude", false)
+	assertExcludeFromRamdiskSnapshotIs(t, ctx, "libexclude", true)
+	assertExcludeFromRamdiskSnapshotIs(t, ctx, "libavailable_exclude", true)
+
+	// A ramdisk module is excluded, but by its path, not the
+	// exclude_from_ramdisk_snapshot property.
+	assertExcludeFromRamdiskSnapshotIs(t, ctx, "libramdisk", false)
+
+	// Verify the content of the ramdisk snapshot.
+
+	snapshotDir := "ramdisk-snapshot"
+	snapshotVariantPath := filepath.Join(buildDir, snapshotDir, "arm64")
+	snapshotSingleton := ctx.SingletonForTests("ramdisk-snapshot")
+
+	var includeJsonFiles []string
+	var excludeJsonFiles []string
+
+	for _, arch := range [][]string{
+		[]string{"arm64", "armv8-a"},
+	} {
+		archType := arch[0]
+		archVariant := arch[1]
+		archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
+
+		sharedVariant := fmt.Sprintf("android_ramdisk_%s_%s_shared", archType, archVariant)
+		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
+
+		// Included modules
+		checkSnapshot(t, ctx, snapshotSingleton, "libinclude", "libinclude.so", sharedDir, sharedVariant)
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libinclude.so.json"))
+
+		// Excluded modules
+		checkSnapshotExclude(t, ctx, snapshotSingleton, "libexclude", "libexclude.so", sharedDir, sharedVariant)
+		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libexclude.so.json"))
+		checkSnapshotExclude(t, ctx, snapshotSingleton, "libramdisk", "libramdisk.so", sharedDir, sharedVariant)
+		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libramdisk.so.json"))
+		checkSnapshotExclude(t, ctx, snapshotSingleton, "libavailable_exclude", "libavailable_exclude.so", sharedDir, sharedVariant)
+		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libavailable_exclude.so.json"))
+	}
+
+	// Verify that each json file for an included module has a rule.
+	for _, jsonFile := range includeJsonFiles {
+		if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
+			t.Errorf("include json file %q not found", jsonFile)
+		}
+	}
+
+	// Verify that each json file for an excluded module has no rule.
+	for _, jsonFile := range excludeJsonFiles {
+		if snapshotSingleton.MaybeOutput(jsonFile).Rule != nil {
+			t.Errorf("exclude json file %q found", jsonFile)
+		}
+	}
+}
+
+func TestRamdiskSnapshotDirected(t *testing.T) {
+	bp := `
+	cc_library_shared {
+		name: "libramdisk",
+		ramdisk: true,
+		nocrt: true,
+	}
+
+	cc_library_shared {
+		name: "libramdisk_available",
+		ramdisk_available: true,
+		nocrt: true,
+	}
+
+	genrule {
+		name: "libfoo_gen",
+		cmd: "",
+		out: ["libfoo.so"],
+	}
+
+	cc_prebuilt_library_shared {
+		name: "libfoo",
+		ramdisk: true,
+		prefer: true,
+		srcs: [":libfoo_gen"],
+	}
+
+	cc_library_shared {
+		name: "libfoo",
+		ramdisk: true,
+		nocrt: true,
+	}
+`
+	config := TestConfig(buildDir, android.Android, nil, bp, nil)
+	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
+	config.TestProductVariables.RamdiskSnapshotVersion = StringPtr("current")
+	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+	config.TestProductVariables.DirectedRamdiskSnapshot = true
+	config.TestProductVariables.RamdiskSnapshotModules = make(map[string]bool)
+	config.TestProductVariables.RamdiskSnapshotModules["libramdisk"] = true
+	config.TestProductVariables.RamdiskSnapshotModules["libfoo"] = true
+	ctx := testCcWithConfig(t, config)
+
+	// Check ramdisk snapshot output.
+
+	snapshotDir := "ramdisk-snapshot"
+	snapshotVariantPath := filepath.Join(buildDir, snapshotDir, "arm64")
+	snapshotSingleton := ctx.SingletonForTests("ramdisk-snapshot")
+
+	var includeJsonFiles []string
+
+	for _, arch := range [][]string{
+		[]string{"arm64", "armv8-a"},
+	} {
+		archType := arch[0]
+		archVariant := arch[1]
+		archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
+
+		sharedVariant := fmt.Sprintf("android_ramdisk_%s_%s_shared", archType, archVariant)
+		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
+
+		// Included modules
+		checkSnapshot(t, ctx, snapshotSingleton, "libramdisk", "libramdisk.so", sharedDir, sharedVariant)
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libramdisk.so.json"))
+		// Check that snapshot captures "prefer: true" prebuilt
+		checkSnapshot(t, ctx, snapshotSingleton, "prebuilt_libfoo", "libfoo.so", sharedDir, sharedVariant)
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libfoo.so.json"))
+
+		// Excluded modules. Modules not included in the directed ramdisk snapshot
+		// are still include as fake modules.
+		checkSnapshotRule(t, ctx, snapshotSingleton, "libramdisk_available", "libramdisk_available.so", sharedDir, sharedVariant)
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libramdisk_available.so.json"))
+	}
+
+	// Verify that each json file for an included module has a rule.
+	for _, jsonFile := range includeJsonFiles {
+		if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
+			t.Errorf("include json file %q not found", jsonFile)
+		}
+	}
+}
diff --git a/cc/vndk.go b/cc/vndk.go
index a060869..339102e 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -616,7 +616,7 @@
 
 		libPath := m.outputFile.Path()
 		snapshotLibOut := filepath.Join(snapshotArchDir, targetArch, "shared", vndkType, libPath.Base())
-		ret = append(ret, copyFile(ctx, libPath, snapshotLibOut))
+		ret = append(ret, copyFileRule(ctx, libPath, snapshotLibOut))
 
 		if ctx.Config().VndkSnapshotBuildArtifacts() {
 			prop := struct {
@@ -637,7 +637,7 @@
 				ctx.Errorf("json marshal to %q failed: %#v", propOut, err)
 				return nil, false
 			}
-			ret = append(ret, writeStringToFile(ctx, string(j), propOut))
+			ret = append(ret, writeStringToFileRule(ctx, string(j), propOut))
 		}
 		return ret, true
 	}
@@ -671,7 +671,7 @@
 			// skip already copied notice file
 			if _, ok := noticeBuilt[noticeName]; !ok {
 				noticeBuilt[noticeName] = true
-				snapshotOutputs = append(snapshotOutputs, copyFile(
+				snapshotOutputs = append(snapshotOutputs, copyFileRule(
 					ctx, m.NoticeFile().Path(), filepath.Join(noticeDir, noticeName)))
 			}
 		}
@@ -683,7 +683,7 @@
 
 	// install all headers after removing duplicates
 	for _, header := range android.FirstUniquePaths(headers) {
-		snapshotOutputs = append(snapshotOutputs, copyFile(
+		snapshotOutputs = append(snapshotOutputs, copyFileRule(
 			ctx, header, filepath.Join(includeDir, header.String())))
 	}
 
@@ -693,7 +693,7 @@
 		if !ok || !m.Enabled() || m.Name() == vndkUsingCoreVariantLibrariesTxt {
 			return
 		}
-		snapshotOutputs = append(snapshotOutputs, copyFile(
+		snapshotOutputs = append(snapshotOutputs, copyFileRule(
 			ctx, m.OutputFile(), filepath.Join(configsDir, m.Name())))
 	})
 
@@ -714,7 +714,7 @@
 			txtBuilder.WriteString(" ")
 			txtBuilder.WriteString(m[k])
 		}
-		return writeStringToFile(ctx, txtBuilder.String(), path)
+		return writeStringToFileRule(ctx, txtBuilder.String(), path)
 	}
 
 	/*
diff --git a/genrule/genrule.go b/genrule/genrule.go
index fe877fe..9f21a35 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -31,10 +31,10 @@
 )
 
 func init() {
-	registerGenruleBuildComponents(android.InitRegistrationContext)
+	RegisterGenruleBuildComponents(android.InitRegistrationContext)
 }
 
-func registerGenruleBuildComponents(ctx android.RegistrationContext) {
+func RegisterGenruleBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("genrule_defaults", defaultsFactory)
 
 	ctx.RegisterModuleType("gensrcs", GenSrcsFactory)
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index 4b36600..ff5c8e9 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -57,7 +57,7 @@
 	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
 	ctx.RegisterModuleType("tool", toolFactory)
 
-	registerGenruleBuildComponents(ctx)
+	RegisterGenruleBuildComponents(ctx)
 
 	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
 	ctx.Register(config)
diff --git a/java/Android.bp b/java/Android.bp
index 1fda7f7..f9ef551 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -57,6 +57,7 @@
         "device_host_converter_test.go",
         "dexpreopt_test.go",
         "dexpreopt_bootjars_test.go",
+        "hiddenapi_singleton_test.go",
         "java_test.go",
         "jdeps_test.go",
         "kotlin_test.go",
diff --git a/java/androidmk.go b/java/androidmk.go
index ae257d7..8b7df6f 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -127,8 +127,8 @@
 						entries.AddStrings("LOCAL_ADDITIONAL_CHECKED_MODULE", library.additionalCheckedModules.Strings()...)
 					}
 
-					if library.proguardDictionary != nil {
-						entries.SetPath("LOCAL_SOONG_PROGUARD_DICT", library.proguardDictionary)
+					if library.dexer.proguardDictionary.Valid() {
+						entries.SetPath("LOCAL_SOONG_PROGUARD_DICT", library.dexer.proguardDictionary.Path())
 					}
 					entries.SetString("LOCAL_MODULE_STEM", library.Stem())
 
@@ -194,9 +194,12 @@
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 			func(entries *android.AndroidMkEntries) {
 				entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", !Bool(prebuilt.properties.Installable))
+				if prebuilt.dexJarFile != nil {
+					entries.SetPath("LOCAL_SOONG_DEX_JAR", prebuilt.dexJarFile)
+				}
 				entries.SetPath("LOCAL_SOONG_HEADER_JAR", prebuilt.combinedClasspathFile)
 				entries.SetPath("LOCAL_SOONG_CLASSES_JAR", prebuilt.combinedClasspathFile)
-				entries.SetString("LOCAL_SDK_VERSION", prebuilt.sdkVersion().raw)
+				entries.SetString("LOCAL_SDK_VERSION", prebuilt.makeSdkVersion())
 				entries.SetString("LOCAL_MODULE_STEM", prebuilt.Stem())
 			},
 		},
@@ -332,8 +335,8 @@
 				if app.jacocoReportClassesFile != nil {
 					entries.SetPath("LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR", app.jacocoReportClassesFile)
 				}
-				if app.proguardDictionary != nil {
-					entries.SetPath("LOCAL_SOONG_PROGUARD_DICT", app.proguardDictionary)
+				if app.dexer.proguardDictionary.Valid() {
+					entries.SetPath("LOCAL_SOONG_PROGUARD_DICT", app.dexer.proguardDictionary.Path())
 				}
 
 				if app.Name() == "framework-res" {
@@ -678,6 +681,9 @@
 					entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", a.dexpreopter.builtInstalled)
 				}
 				entries.AddStrings("LOCAL_INSTALLED_MODULE_STEM", a.installPath.Rel())
+				if Bool(a.properties.Export_package_resources) {
+					entries.SetPath("LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE", a.outputFile)
+				}
 			},
 		},
 	}}
diff --git a/java/androidmk_test.go b/java/androidmk_test.go
index 7daa624..32e6f0e 100644
--- a/java/androidmk_test.go
+++ b/java/androidmk_test.go
@@ -169,3 +169,25 @@
 		t.Errorf("did not expect explicit DistFile, got %v", without_tag_entries[0].DistFile)
 	}
 }
+
+func TestImportSoongDexJar(t *testing.T) {
+	ctx, config := testJava(t, `
+		java_import {
+			name: "my-java-import",
+			jars: ["a.jar"],
+			prefer: true,
+			compile_dex: true,
+		}
+	`)
+
+	mod := ctx.ModuleForTests("my-java-import", "android_common").Module()
+	entries := android.AndroidMkEntriesForTest(t, config, "", mod)[0]
+	expectedSoongDexJar := buildDir + "/.intermediates/my-java-import/android_common/dex/my-java-import.jar"
+	actualSoongDexJar := entries.EntryMap["LOCAL_SOONG_DEX_JAR"]
+
+	if len(actualSoongDexJar) != 1 {
+		t.Errorf("LOCAL_SOONG_DEX_JAR incorrect len %d", len(actualSoongDexJar))
+	} else if actualSoongDexJar[0] != expectedSoongDexJar {
+		t.Errorf("LOCAL_SOONG_DEX_JAR mismatch, actual: %s, expected: %s", actualSoongDexJar[0], expectedSoongDexJar)
+	}
+}
diff --git a/java/app.go b/java/app.go
index 2ec27f3..0c9fb9a 100755
--- a/java/app.go
+++ b/java/app.go
@@ -585,11 +585,11 @@
 
 func (a *AndroidApp) dexBuildActions(ctx android.ModuleContext) android.Path {
 	a.dexpreopter.installPath = a.installPath(ctx)
-	if a.deviceProperties.Uncompress_dex == nil {
+	if a.dexProperties.Uncompress_dex == nil {
 		// If the value was not force-set by the user, use reasonable default based on the module.
-		a.deviceProperties.Uncompress_dex = proptools.BoolPtr(a.shouldUncompressDex(ctx))
+		a.dexProperties.Uncompress_dex = proptools.BoolPtr(a.shouldUncompressDex(ctx))
 	}
-	a.dexpreopter.uncompressedDex = *a.deviceProperties.Uncompress_dex
+	a.dexpreopter.uncompressedDex = *a.dexProperties.Uncompress_dex
 	a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries()
 	a.dexpreopter.usesLibs = a.usesLibrary.usesLibraryProperties.Uses_libs
 	a.dexpreopter.optionalUsesLibs = a.usesLibrary.presentOptionalUsesLibs(ctx)
@@ -1001,8 +1001,8 @@
 func AndroidAppFactory() android.Module {
 	module := &AndroidApp{}
 
-	module.Module.deviceProperties.Optimize.EnabledByDefault = true
-	module.Module.deviceProperties.Optimize.Shrink = proptools.BoolPtr(true)
+	module.Module.dexProperties.Optimize.EnabledByDefault = true
+	module.Module.dexProperties.Optimize.Shrink = proptools.BoolPtr(true)
 
 	module.Module.properties.Instrument = true
 	module.Module.properties.Installable = proptools.BoolPtr(true)
@@ -1119,7 +1119,7 @@
 func AndroidTestFactory() android.Module {
 	module := &AndroidTest{}
 
-	module.Module.deviceProperties.Optimize.EnabledByDefault = true
+	module.Module.dexProperties.Optimize.EnabledByDefault = true
 
 	module.Module.properties.Instrument = true
 	module.Module.properties.Installable = proptools.BoolPtr(true)
@@ -1170,7 +1170,7 @@
 func AndroidTestHelperAppFactory() android.Module {
 	module := &AndroidTestHelperApp{}
 
-	module.Module.deviceProperties.Optimize.EnabledByDefault = true
+	module.Module.dexProperties.Optimize.EnabledByDefault = true
 
 	module.Module.properties.Installable = proptools.BoolPtr(true)
 	module.appProperties.Use_embedded_native_libs = proptools.BoolPtr(true)
@@ -1338,6 +1338,10 @@
 
 	// Optional name for the installed app. If unspecified, it is derived from the module name.
 	Filename *string
+
+	// If set, create package-export.apk, which other packages can
+	// use to get PRODUCT-agnostic resource data like IDs and type definitions.
+	Export_package_resources *bool
 }
 
 func (a *AndroidAppImport) IsInstallable() bool {
@@ -1381,13 +1385,17 @@
 	}
 }
 
+func (a *AndroidAppImport) isPrebuiltFrameworkRes() bool {
+	return a.Name() == "prebuilt_framework-res"
+}
+
 func (a *AndroidAppImport) DepsMutator(ctx android.BottomUpMutatorContext) {
 	cert := android.SrcIsModule(String(a.properties.Certificate))
 	if cert != "" {
 		ctx.AddDependency(ctx.Module(), certificateTag, cert)
 	}
 
-	a.usesLibrary.deps(ctx, true)
+	a.usesLibrary.deps(ctx, !a.isPrebuiltFrameworkRes())
 }
 
 func (a *AndroidAppImport) uncompressEmbeddedJniLibs(
@@ -1481,7 +1489,12 @@
 	a.uncompressEmbeddedJniLibs(ctx, srcApk, jnisUncompressed.OutputPath)
 
 	var installDir android.InstallPath
-	if Bool(a.properties.Privileged) {
+
+	if a.isPrebuiltFrameworkRes() {
+		// framework-res.apk is installed as system/framework/framework-res.apk
+		installDir = android.PathForModuleInstall(ctx, "framework")
+		a.preprocessed = true
+	} else if Bool(a.properties.Privileged) {
 		installDir = android.PathForModuleInstall(ctx, "priv-app", a.BaseModuleName())
 	} else if ctx.InstallInTestcases() {
 		installDir = android.PathForModuleInstall(ctx, a.BaseModuleName(), ctx.DeviceConfig().DeviceArch())
@@ -1510,7 +1523,15 @@
 	// TODO: Handle EXTERNAL
 
 	// Sign or align the package if package has not been preprocessed
-	if a.preprocessed {
+
+	if a.isPrebuiltFrameworkRes() {
+		a.outputFile = srcApk
+		certificates = processMainCert(a.ModuleBase, String(a.properties.Certificate), certificates, ctx)
+		if len(certificates) != 1 {
+			ctx.ModuleErrorf("Unexpected number of certificates were extracted: %q", certificates)
+		}
+		a.certificate = certificates[0]
+	} else if a.preprocessed {
 		a.outputFile = srcApk
 		a.certificate = PresignedCertificate
 	} else if !Bool(a.properties.Presigned) {
@@ -1596,6 +1617,71 @@
 	return sdkSpecFrom("")
 }
 
+func (a *AndroidAppImport) HeaderJars() android.Paths {
+	return nil
+}
+
+func (a *AndroidAppImport) ImplementationAndResourcesJars() android.Paths {
+	return nil
+}
+
+func (a *AndroidAppImport) ImplementationJars() android.Paths {
+	return nil
+}
+
+func (a *AndroidAppImport) ResourceJars() android.Paths {
+	return nil
+}
+
+func (a *AndroidAppImport) DexJar() android.Path {
+	return nil
+}
+
+func (a *AndroidAppImport) AidlIncludeDirs() android.Paths {
+	return nil
+}
+
+func (a *AndroidAppImport) ExportedSdkLibs() []string {
+	return nil
+}
+
+func (a *AndroidAppImport) ExportedPlugins() (android.Paths, []string) {
+	return nil, nil
+}
+
+func (a *AndroidAppImport) SrcJarArgs() ([]string, android.Paths) {
+	return nil, nil
+}
+
+func (a *AndroidAppImport) ExportPackage() android.Path {
+	if Bool(a.properties.Export_package_resources) {
+		return a.outputFile
+	}
+	return nil
+}
+
+func (a *AndroidAppImport) ExportedProguardFlagFiles() android.Paths {
+	return nil
+}
+
+func (a *AndroidAppImport) ExportedRRODirs() []rroDir {
+	return nil
+}
+
+func (a *AndroidAppImport) ExportedStaticPackages() android.Paths {
+	return nil
+}
+
+func (a *AndroidAppImport) ExportedManifests() android.Paths {
+	return nil
+}
+
+func (a *AndroidAppImport) ExportedAssets() android.OptionalPath {
+	return android.OptionalPath{}
+}
+
+var _ AndroidLibraryDependency = (*AndroidAppImport)(nil)
+
 func createVariantGroupType(variants []string, variantGroupName string) reflect.Type {
 	props := reflect.TypeOf((*AndroidAppImportProperties)(nil))
 
diff --git a/java/app_test.go b/java/app_test.go
index 8ef3152..513767d 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -2374,6 +2374,69 @@
 	}
 }
 
+func TestAndroidAppImport_frameworkRes(t *testing.T) {
+	ctx, config := testJava(t, `
+		android_app_import {
+			name: "framework-res",
+			certificate: "platform",
+			apk: "package-res.apk",
+			prefer: true,
+			export_package_resources: true,
+			// Disable dexpreopt and verify_uses_libraries check as the app
+			// contains no Java code to be dexpreopted.
+			enforce_uses_libs: false,
+			dex_preopt: {
+				enabled: false,
+			},
+		}
+		`)
+
+	mod := ctx.ModuleForTests("prebuilt_framework-res", "android_common").Module()
+	a := mod.(*AndroidAppImport)
+
+	if !a.preprocessed {
+		t.Errorf("prebuilt framework-res is not preprocessed")
+	}
+
+	expectedInstallPath := buildDir + "/target/product/test_device/system/framework/framework-res.apk"
+
+	if a.dexpreopter.installPath.String() != expectedInstallPath {
+		t.Errorf("prebuilt framework-res installed to incorrect location, actual: %s, expected: %s", a.dexpreopter.installPath, expectedInstallPath)
+
+	}
+
+	entries := android.AndroidMkEntriesForTest(t, config, "", mod)[0]
+
+	expectedPath := "."
+	// From apk property above, in the root of the source tree.
+	expectedPrebuiltModuleFile := "package-res.apk"
+	// Verify that the apk is preprocessed: The export package is the same
+	// as the prebuilt.
+	expectedSoongResourceExportPackage := expectedPrebuiltModuleFile
+
+	actualPath := entries.EntryMap["LOCAL_PATH"]
+	actualPrebuiltModuleFile := entries.EntryMap["LOCAL_PREBUILT_MODULE_FILE"]
+	actualSoongResourceExportPackage := entries.EntryMap["LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE"]
+
+	if len(actualPath) != 1 {
+		t.Errorf("LOCAL_PATH incorrect len %d", len(actualPath))
+	} else if actualPath[0] != expectedPath {
+		t.Errorf("LOCAL_PATH mismatch, actual: %s, expected: %s", actualPath[0], expectedPath)
+	}
+
+	if len(actualPrebuiltModuleFile) != 1 {
+		t.Errorf("LOCAL_PREBUILT_MODULE_FILE incorrect len %d", len(actualPrebuiltModuleFile))
+	} else if actualPrebuiltModuleFile[0] != expectedPrebuiltModuleFile {
+		t.Errorf("LOCAL_PREBUILT_MODULE_FILE mismatch, actual: %s, expected: %s", actualPrebuiltModuleFile[0], expectedPrebuiltModuleFile)
+	}
+
+	if len(actualSoongResourceExportPackage) != 1 {
+		t.Errorf("LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE incorrect len %d", len(actualSoongResourceExportPackage))
+	} else if actualSoongResourceExportPackage[0] != expectedSoongResourceExportPackage {
+		t.Errorf("LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE mismatch, actual: %s, expected: %s", actualSoongResourceExportPackage[0], expectedSoongResourceExportPackage)
+	}
+}
+
 func TestAndroidTestImport(t *testing.T) {
 	ctx, config := testJava(t, `
 		android_test_import {
diff --git a/java/dex.go b/java/dex.go
index 9e61e95..cd45a93 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -24,6 +24,60 @@
 	"android/soong/remoteexec"
 )
 
+type DexProperties struct {
+	// If set to true, compile dex regardless of installable.  Defaults to false.
+	Compile_dex *bool
+
+	// list of module-specific flags that will be used for dex compiles
+	Dxflags []string `android:"arch_variant"`
+
+	Optimize struct {
+		// If false, disable all optimization.  Defaults to true for android_app and android_test
+		// modules, false for java_library and java_test modules.
+		Enabled *bool
+		// True if the module containing this has it set by default.
+		EnabledByDefault bool `blueprint:"mutated"`
+
+		// If true, optimize for size by removing unused code.  Defaults to true for apps,
+		// false for libraries and tests.
+		Shrink *bool
+
+		// If true, optimize bytecode.  Defaults to false.
+		Optimize *bool
+
+		// If true, obfuscate bytecode.  Defaults to false.
+		Obfuscate *bool
+
+		// If true, do not use the flag files generated by aapt that automatically keep
+		// classes referenced by the app manifest.  Defaults to false.
+		No_aapt_flags *bool
+
+		// Flags to pass to proguard.
+		Proguard_flags []string
+
+		// Specifies the locations of files containing proguard flags.
+		Proguard_flags_files []string `android:"path"`
+	}
+
+	// Keep the data uncompressed. We always need uncompressed dex for execution,
+	// so this might actually save space by avoiding storing the same data twice.
+	// This defaults to reasonable value based on module and should not be set.
+	// It exists only to support ART tests.
+	Uncompress_dex *bool
+}
+
+type dexer struct {
+	dexProperties DexProperties
+
+	// list of extra proguard flag files
+	extraProguardFlagFiles android.Paths
+	proguardDictionary     android.OptionalPath
+}
+
+func (d *dexer) effectiveOptimizeEnabled() bool {
+	return BoolDefault(d.dexProperties.Optimize.Enabled, d.dexProperties.Optimize.EnabledByDefault)
+}
+
 var d8, d8RE = remoteexec.MultiCommandStaticRules(pctx, "d8",
 	blueprint.RuleParams{
 		Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
@@ -86,8 +140,8 @@
 		},
 	}, []string{"outDir", "outDict", "r8Flags", "zipFlags"}, []string{"implicits"})
 
-func (j *Module) dexCommonFlags(ctx android.ModuleContext) []string {
-	flags := j.deviceProperties.Dxflags
+func (d *dexer) dexCommonFlags(ctx android.ModuleContext, minSdkVersion sdkSpec) []string {
+	flags := d.dexProperties.Dxflags
 	// Translate all the DX flags to D8 ones until all the build files have been migrated
 	// to D8 flags. See: b/69377755
 	flags = android.RemoveListFromList(flags,
@@ -103,30 +157,27 @@
 			"--verbose")
 	}
 
-	minSdkVersion, err := j.minSdkVersion().effectiveVersion(ctx)
+	effectiveVersion, err := minSdkVersion.effectiveVersion(ctx)
 	if err != nil {
 		ctx.PropertyErrorf("min_sdk_version", "%s", err)
 	}
 
-	flags = append(flags, "--min-api "+minSdkVersion.asNumberString())
+	flags = append(flags, "--min-api "+effectiveVersion.asNumberString())
 	return flags
 }
 
-func (j *Module) d8Flags(ctx android.ModuleContext, flags javaBuilderFlags) ([]string, android.Paths) {
-	d8Flags := j.dexCommonFlags(ctx)
-
+func d8Flags(flags javaBuilderFlags) (d8Flags []string, d8Deps android.Paths) {
 	d8Flags = append(d8Flags, flags.bootClasspath.FormRepeatedClassPath("--lib ")...)
 	d8Flags = append(d8Flags, flags.classpath.FormRepeatedClassPath("--lib ")...)
 
-	var d8Deps android.Paths
 	d8Deps = append(d8Deps, flags.bootClasspath...)
 	d8Deps = append(d8Deps, flags.classpath...)
 
 	return d8Flags, d8Deps
 }
 
-func (j *Module) r8Flags(ctx android.ModuleContext, flags javaBuilderFlags) (r8Flags []string, r8Deps android.Paths) {
-	opt := j.deviceProperties.Optimize
+func (d *dexer) r8Flags(ctx android.ModuleContext, flags javaBuilderFlags) (r8Flags []string, r8Deps android.Paths) {
+	opt := d.dexProperties.Optimize
 
 	// When an app contains references to APIs that are not in the SDK specified by
 	// its LOCAL_SDK_VERSION for example added by support library or by runtime
@@ -140,8 +191,6 @@
 		proguardRaiseDeps = append(proguardRaiseDeps, dep.(Dependency).HeaderJars()...)
 	})
 
-	r8Flags = append(r8Flags, j.dexCommonFlags(ctx)...)
-
 	r8Flags = append(r8Flags, proguardRaiseDeps.FormJavaClassPath("-libraryjars"))
 	r8Flags = append(r8Flags, flags.bootClasspath.FormJavaClassPath("-libraryjars"))
 	r8Flags = append(r8Flags, flags.classpath.FormJavaClassPath("-libraryjars"))
@@ -154,15 +203,10 @@
 		android.PathForSource(ctx, "build/make/core/proguard.flags"),
 	}
 
-	if j.shouldInstrumentStatic(ctx) {
-		flagFiles = append(flagFiles,
-			android.PathForSource(ctx, "build/make/core/proguard.jacoco.flags"))
-	}
-
-	flagFiles = append(flagFiles, j.extraProguardFlagFiles...)
+	flagFiles = append(flagFiles, d.extraProguardFlagFiles...)
 	// TODO(ccross): static android library proguard files
 
-	flagFiles = append(flagFiles, android.PathsForModuleSrc(ctx, j.deviceProperties.Optimize.Proguard_flags_files)...)
+	flagFiles = append(flagFiles, android.PathsForModuleSrc(ctx, opt.Proguard_flags_files)...)
 
 	r8Flags = append(r8Flags, android.JoinWithPrefix(flagFiles.Strings(), "-include "))
 	r8Deps = append(r8Deps, flagFiles...)
@@ -171,7 +215,7 @@
 	r8Deps = append(r8Deps, android.PathForSource(ctx,
 		"build/make/core/proguard_basic_keeps.flags"))
 
-	r8Flags = append(r8Flags, j.deviceProperties.Optimize.Proguard_flags...)
+	r8Flags = append(r8Flags, opt.Proguard_flags...)
 
 	// TODO(ccross): Don't shrink app instrumentation tests by default.
 	if !Bool(opt.Shrink) {
@@ -197,29 +241,30 @@
 	return r8Flags, r8Deps
 }
 
-func (j *Module) compileDex(ctx android.ModuleContext, flags javaBuilderFlags,
+func (d *dexer) compileDex(ctx android.ModuleContext, flags javaBuilderFlags, minSdkVersion sdkSpec,
 	classesJar android.Path, jarName string) android.ModuleOutPath {
 
-	useR8 := j.deviceProperties.EffectiveOptimizeEnabled()
-
 	// Compile classes.jar into classes.dex and then javalib.jar
 	javalibJar := android.PathForModuleOut(ctx, "dex", jarName)
 	outDir := android.PathForModuleOut(ctx, "dex")
 
 	zipFlags := "--ignore_missing_files"
-	if proptools.Bool(j.deviceProperties.Uncompress_dex) {
+	if proptools.Bool(d.dexProperties.Uncompress_dex) {
 		zipFlags += " -L 0"
 	}
 
+	commonFlags := d.dexCommonFlags(ctx, minSdkVersion)
+
+	useR8 := d.effectiveOptimizeEnabled()
 	if useR8 {
 		proguardDictionary := android.PathForModuleOut(ctx, "proguard_dictionary")
-		j.proguardDictionary = proguardDictionary
-		r8Flags, r8Deps := j.r8Flags(ctx, flags)
+		d.proguardDictionary = android.OptionalPathForPath(proguardDictionary)
+		r8Flags, r8Deps := d.r8Flags(ctx, flags)
 		rule := r8
 		args := map[string]string{
-			"r8Flags":  strings.Join(r8Flags, " "),
+			"r8Flags":  strings.Join(append(commonFlags, r8Flags...), " "),
 			"zipFlags": zipFlags,
-			"outDict":  j.proguardDictionary.String(),
+			"outDict":  proguardDictionary.String(),
 			"outDir":   outDir.String(),
 		}
 		if ctx.Config().IsEnvTrue("RBE_R8") {
@@ -236,7 +281,7 @@
 			Args:           args,
 		})
 	} else {
-		d8Flags, d8Deps := j.d8Flags(ctx, flags)
+		d8Flags, d8Deps := d8Flags(flags)
 		rule := d8
 		if ctx.Config().IsEnvTrue("RBE_D8") {
 			rule = d8RE
@@ -248,13 +293,13 @@
 			Input:       classesJar,
 			Implicits:   d8Deps,
 			Args: map[string]string{
-				"d8Flags":  strings.Join(d8Flags, " "),
+				"d8Flags":  strings.Join(append(commonFlags, d8Flags...), " "),
 				"zipFlags": zipFlags,
 				"outDir":   outDir.String(),
 			},
 		})
 	}
-	if proptools.Bool(j.deviceProperties.Uncompress_dex) {
+	if proptools.Bool(d.dexProperties.Uncompress_dex) {
 		alignedJavalibJar := android.PathForModuleOut(ctx, "aligned", jarName)
 		TransformZipAlign(ctx, alignedJavalibJar, javalibJar)
 		javalibJar = alignedJavalibJar
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index 95dd0bb..9a0729d 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -66,6 +66,19 @@
 
 	stubFlagsRule(ctx)
 
+	// If there is a prebuilt hiddenapi dir, generate rules to use the
+	// files within. Generally, we build the hiddenapi files from source
+	// during the build, ensuring consistency. It's possible, in a split
+	// build (framework and vendor) scenario, for the vendor build to use
+	// prebuilt hiddenapi files from the framework build. In this scenario,
+	// the framework and vendor builds must use the same source to ensure
+	// consistency.
+
+	if ctx.Config().PrebuiltHiddenApiDir(ctx) != "" {
+		h.flags = prebuiltFlagsRule(ctx)
+		return
+	}
+
 	// These rules depend on files located in frameworks/base, skip them if running in a tree that doesn't have them.
 	if ctx.Config().FrameworksBaseDirExists(ctx) {
 		h.flags = flagsRule(ctx)
@@ -163,6 +176,7 @@
 						return
 					}
 				}
+
 				bootDexJars = append(bootDexJars, jar)
 			}
 		}
@@ -216,6 +230,19 @@
 	}
 }
 
+func prebuiltFlagsRule(ctx android.SingletonContext) android.Path {
+	outputPath := hiddenAPISingletonPaths(ctx).flags
+	inputPath := android.PathForSource(ctx, ctx.Config().PrebuiltHiddenApiDir(ctx), "hiddenapi-flags.csv")
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   android.Cp,
+		Output: outputPath,
+		Input:  inputPath,
+	})
+
+	return outputPath
+}
+
 // flagsRule creates a rule to build hiddenapi-flags.csv out of flags.csv files generated for boot image modules and
 // the greylists.
 func flagsRule(ctx android.SingletonContext) android.Path {
@@ -397,6 +424,20 @@
 		return
 	}
 
+	if ctx.Config().PrebuiltHiddenApiDir(ctx) != "" {
+		outputPath := hiddenAPISingletonPaths(ctx).index
+		inputPath := android.PathForSource(ctx, ctx.Config().PrebuiltHiddenApiDir(ctx), "hiddenapi-index.csv")
+
+		ctx.Build(pctx, android.BuildParams{
+			Rule:   android.Cp,
+			Output: outputPath,
+			Input:  inputPath,
+		})
+
+		h.index = outputPath
+		return
+	}
+
 	indexes := android.Paths{}
 	ctx.VisitAllModules(func(module android.Module) {
 		if h, ok := module.(hiddenAPIIntf); ok {
diff --git a/java/hiddenapi_singleton_test.go b/java/hiddenapi_singleton_test.go
new file mode 100644
index 0000000..e4dc037
--- /dev/null
+++ b/java/hiddenapi_singleton_test.go
@@ -0,0 +1,182 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// 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.
+
+package java
+
+import (
+	"android/soong/android"
+	"strings"
+	"testing"
+)
+
+func testConfigWithBootJars(bp string, bootJars []string, prebuiltHiddenApiDir *string) android.Config {
+	config := testConfig(nil, bp, nil)
+	config.TestProductVariables.BootJars = bootJars
+	config.TestProductVariables.PrebuiltHiddenApiDir = prebuiltHiddenApiDir
+	return config
+}
+
+func testContextWithHiddenAPI() *android.TestContext {
+	ctx := testContext()
+	ctx.RegisterSingletonType("hiddenapi", hiddenAPISingletonFactory)
+	return ctx
+}
+
+func testHiddenAPI(t *testing.T, bp string, bootJars []string, prebuiltHiddenApiDir *string) (*android.TestContext, android.Config) {
+	t.Helper()
+
+	config := testConfigWithBootJars(bp, bootJars, prebuiltHiddenApiDir)
+	ctx := testContextWithHiddenAPI()
+
+	run(t, ctx, config)
+
+	return ctx, config
+}
+
+func TestHiddenAPISingleton(t *testing.T) {
+	ctx, _ := testHiddenAPI(t, `
+		java_library {
+			name: "foo",
+			srcs: ["a.java"],
+			compile_dex: true,
+	}
+	`, []string{"foo"}, nil)
+
+	hiddenAPI := ctx.SingletonForTests("hiddenapi")
+	hiddenapiRule := hiddenAPI.Rule("hiddenapi")
+	want := "--boot-dex=" + buildDir + "/.intermediates/foo/android_common/aligned/foo.jar"
+	if !strings.Contains(hiddenapiRule.RuleParams.Command, want) {
+		t.Errorf("Expected %s in hiddenapi command, but it was not present: %s", want, hiddenapiRule.RuleParams.Command)
+	}
+}
+
+func TestHiddenAPISingletonWithPrebuilt(t *testing.T) {
+	ctx, _ := testHiddenAPI(t, `
+		java_import {
+			name: "foo",
+			jars: ["a.jar"],
+			compile_dex: true,
+	}
+	`, []string{"foo"}, nil)
+
+	hiddenAPI := ctx.SingletonForTests("hiddenapi")
+	hiddenapiRule := hiddenAPI.Rule("hiddenapi")
+	want := "--boot-dex=" + buildDir + "/.intermediates/foo/android_common/aligned/foo.jar"
+	if !strings.Contains(hiddenapiRule.RuleParams.Command, want) {
+		t.Errorf("Expected %s in hiddenapi command, but it was not present: %s", want, hiddenapiRule.RuleParams.Command)
+	}
+}
+
+func TestHiddenAPISingletonWithPrebuiltUseSource(t *testing.T) {
+	ctx, _ := testHiddenAPI(t, `
+		java_library {
+			name: "foo",
+			srcs: ["a.java"],
+			compile_dex: true,
+	}
+
+		java_import {
+			name: "foo",
+			jars: ["a.jar"],
+			compile_dex: true,
+			prefer: false,
+	}
+	`, []string{"foo"}, nil)
+
+	hiddenAPI := ctx.SingletonForTests("hiddenapi")
+	hiddenapiRule := hiddenAPI.Rule("hiddenapi")
+	fromSourceJarArg := "--boot-dex=" + buildDir + "/.intermediates/foo/android_common/aligned/foo.jar"
+	if !strings.Contains(hiddenapiRule.RuleParams.Command, fromSourceJarArg) {
+		t.Errorf("Expected %s in hiddenapi command, but it was not present: %s", fromSourceJarArg, hiddenapiRule.RuleParams.Command)
+	}
+
+	prebuiltJarArg := "--boot-dex=" + buildDir + "/.intermediates/foo/android_common/dex/foo.jar"
+	if strings.Contains(hiddenapiRule.RuleParams.Command, prebuiltJarArg) {
+		t.Errorf("Did not expect %s in hiddenapi command, but it was present: %s", prebuiltJarArg, hiddenapiRule.RuleParams.Command)
+	}
+}
+
+func TestHiddenAPISingletonWithPrebuiltOverrideSource(t *testing.T) {
+	ctx, _ := testHiddenAPI(t, `
+		java_library {
+			name: "foo",
+			srcs: ["a.java"],
+			compile_dex: true,
+	}
+
+		java_import {
+			name: "foo",
+			jars: ["a.jar"],
+			compile_dex: true,
+			prefer: true,
+	}
+	`, []string{"foo"}, nil)
+
+	hiddenAPI := ctx.SingletonForTests("hiddenapi")
+	hiddenapiRule := hiddenAPI.Rule("hiddenapi")
+	prebuiltJarArg := "--boot-dex=" + buildDir + "/.intermediates/prebuilt_foo/android_common/dex/foo.jar"
+	if !strings.Contains(hiddenapiRule.RuleParams.Command, prebuiltJarArg) {
+		t.Errorf("Expected %s in hiddenapi command, but it was not present: %s", prebuiltJarArg, hiddenapiRule.RuleParams.Command)
+	}
+
+	fromSourceJarArg := "--boot-dex=" + buildDir + "/.intermediates/foo/android_common/aligned/foo.jar"
+	if strings.Contains(hiddenapiRule.RuleParams.Command, fromSourceJarArg) {
+		t.Errorf("Did not expect %s in hiddenapi command, but it was present: %s", fromSourceJarArg, hiddenapiRule.RuleParams.Command)
+	}
+}
+
+func TestHiddenAPISingletonWithPrebuiltCsvFile(t *testing.T) {
+
+	// The idea behind this test is to ensure that when the build is
+	// confugured with a PrebuiltHiddenApiDir that the rules for the
+	// hiddenapi singleton copy the prebuilts to the typical output
+	// location, and then use that output location for the hiddenapi encode
+	// dex step.
+
+	// Where to find the prebuilt hiddenapi files:
+	prebuiltHiddenApiDir := "path/to/prebuilt/hiddenapi"
+
+	ctx, _ := testHiddenAPI(t, `
+		java_import {
+			name: "foo",
+			jars: ["a.jar"],
+			compile_dex: true,
+	}
+	`, []string{"foo"}, &prebuiltHiddenApiDir)
+
+	expectedCpInput := prebuiltHiddenApiDir + "/hiddenapi-flags.csv"
+	expectedCpOutput := buildDir + "/hiddenapi/hiddenapi-flags.csv"
+	expectedFlagsCsv := buildDir + "/hiddenapi/hiddenapi-flags.csv"
+
+	foo := ctx.ModuleForTests("foo", "android_common")
+
+	hiddenAPI := ctx.SingletonForTests("hiddenapi")
+	cpRule := hiddenAPI.Rule("Cp")
+	actualCpInput := cpRule.BuildParams.Input
+	actualCpOutput := cpRule.BuildParams.Output
+	encodeDexRule := foo.Rule("hiddenAPIEncodeDex")
+	actualFlagsCsv := encodeDexRule.BuildParams.Args["flagsCsv"]
+
+	if actualCpInput.String() != expectedCpInput {
+		t.Errorf("Prebuilt hiddenapi cp rule input mismatch, actual: %s, expected: %s", actualCpInput, expectedCpInput)
+	}
+
+	if actualCpOutput.String() != expectedCpOutput {
+		t.Errorf("Prebuilt hiddenapi cp rule output mismatch, actual: %s, expected: %s", actualCpOutput, expectedCpOutput)
+	}
+
+	if actualFlagsCsv != expectedFlagsCsv {
+		t.Errorf("Prebuilt hiddenapi encode dex rule flags csv mismatch, actual: %s, expected: %s", actualFlagsCsv, expectedFlagsCsv)
+	}
+}
diff --git a/java/java.go b/java/java.go
index 69826ee..adeb0f7 100644
--- a/java/java.go
+++ b/java/java.go
@@ -259,9 +259,6 @@
 }
 
 type CompilerDeviceProperties struct {
-	// list of module-specific flags that will be used for dex compiles
-	Dxflags []string `android:"arch_variant"`
-
 	// if not blank, set to the version of the sdk to compile against.
 	// Defaults to compiling against the current platform.
 	Sdk_version *string
@@ -307,37 +304,6 @@
 		}
 	}
 
-	// If set to true, compile dex regardless of installable.  Defaults to false.
-	Compile_dex *bool
-
-	Optimize struct {
-		// If false, disable all optimization.  Defaults to true for android_app and android_test
-		// modules, false for java_library and java_test modules.
-		Enabled *bool
-		// True if the module containing this has it set by default.
-		EnabledByDefault bool `blueprint:"mutated"`
-
-		// If true, optimize for size by removing unused code.  Defaults to true for apps,
-		// false for libraries and tests.
-		Shrink *bool
-
-		// If true, optimize bytecode.  Defaults to false.
-		Optimize *bool
-
-		// If true, obfuscate bytecode.  Defaults to false.
-		Obfuscate *bool
-
-		// If true, do not use the flag files generated by aapt that automatically keep
-		// classes referenced by the app manifest.  Defaults to false.
-		No_aapt_flags *bool
-
-		// Flags to pass to proguard.
-		Proguard_flags []string
-
-		// Specifies the locations of files containing proguard flags.
-		Proguard_flags_files []string `android:"path"`
-	}
-
 	// When targeting 1.9 and above, override the modules to use with --system,
 	// otherwise provides defaults libraries to add to the bootclasspath.
 	System_modules *string
@@ -351,12 +317,6 @@
 	// set the name of the output
 	Stem *string
 
-	// Keep the data uncompressed. We always need uncompressed dex for execution,
-	// so this might actually save space by avoiding storing the same data twice.
-	// This defaults to reasonable value based on module and should not be set.
-	// It exists only to support ART tests.
-	Uncompress_dex *bool
-
 	IsSDKLibrary bool `blueprint:"mutated"`
 
 	// If true, generate the signature file of APK Signing Scheme V4, along side the signed APK file.
@@ -364,10 +324,6 @@
 	V4_signature *bool
 }
 
-func (me *CompilerDeviceProperties) EffectiveOptimizeEnabled() bool {
-	return BoolDefault(me.Optimize.Enabled, me.Optimize.EnabledByDefault)
-}
-
 // Functionality common to Module and Import
 //
 // It is embedded in Module so its functionality can be used by methods in Module
@@ -436,9 +392,6 @@
 	// output file containing uninstrumented classes that will be instrumented by jacoco
 	jacocoReportClassesFile android.Path
 
-	// output file containing mapping of obfuscated names
-	proguardDictionary android.Path
-
 	// output file of the module, which may be a classes jar or a dex jar
 	outputFile       android.Path
 	extraOutputFiles android.Paths
@@ -454,9 +407,6 @@
 	compiledJavaSrcs android.Paths
 	compiledSrcJars  android.Paths
 
-	// list of extra progurad flag files
-	extraProguardFlagFiles android.Paths
-
 	// manifest file to use instead of properties.Manifest
 	overrideManifest android.OptionalPath
 
@@ -483,6 +433,7 @@
 	extraResources android.Paths
 
 	hiddenAPI
+	dexer
 	dexpreopter
 	linter
 
@@ -503,6 +454,7 @@
 	j.addHostProperties()
 	j.AddProperties(
 		&j.deviceProperties,
+		&j.dexer.dexProperties,
 		&j.dexpreoptProperties,
 		&j.linter.properties,
 	)
@@ -515,7 +467,10 @@
 	case ".jar":
 		return android.Paths{j.implementationAndResourcesJar}, nil
 	case ".proguard_map":
-		return android.Paths{j.proguardDictionary}, nil
+		if j.dexer.proguardDictionary.Valid() {
+			return android.Paths{j.dexer.proguardDictionary.Path()}, nil
+		}
+		return nil, fmt.Errorf("%q was requested, but no output file was found.", tag)
 	default:
 		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
 	}
@@ -644,6 +599,21 @@
 			ctx.Config().UnbundledBuild())
 }
 
+func (j *Module) shouldInstrumentInApex(ctx android.BaseModuleContext) bool {
+	// Force enable the instrumentation for java code that is built for APEXes ...
+	// except for the jacocoagent itself (because instrumenting jacocoagent using jacocoagent
+	// doesn't make sense) or framework libraries (e.g. libraries found in the InstrumentFrameworkModules list) unless EMMA_INSTRUMENT_FRAMEWORK is true.
+	isJacocoAgent := ctx.ModuleName() == "jacocoagent"
+	if android.DirectlyInAnyApex(ctx, ctx.ModuleName()) && !isJacocoAgent && !j.IsForPlatform() {
+		if !inList(ctx.ModuleName(), config.InstrumentFrameworkModules) {
+			return true
+		} else if ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") {
+			return true
+		}
+	}
+	return false
+}
+
 func (j *Module) sdkVersion() sdkSpec {
 	return sdkSpecFrom(String(j.deviceProperties.Sdk_version))
 }
@@ -680,28 +650,32 @@
 	return j.ApexModuleBase.AvailableFor(what)
 }
 
+func sdkDeps(ctx android.BottomUpMutatorContext, sdkContext sdkContext, d dexer) {
+	sdkDep := decodeSdkDep(ctx, sdkContext)
+	if sdkDep.useDefaultLibs {
+		ctx.AddVariationDependencies(nil, bootClasspathTag, config.DefaultBootclasspathLibraries...)
+		ctx.AddVariationDependencies(nil, systemModulesTag, config.DefaultSystemModules)
+		if sdkDep.hasFrameworkLibs() {
+			ctx.AddVariationDependencies(nil, libTag, config.DefaultLibraries...)
+		}
+	} else if sdkDep.useModule {
+		ctx.AddVariationDependencies(nil, bootClasspathTag, sdkDep.bootclasspath...)
+		ctx.AddVariationDependencies(nil, java9LibTag, sdkDep.java9Classpath...)
+		if d.effectiveOptimizeEnabled() && sdkDep.hasStandardLibs() {
+			ctx.AddVariationDependencies(nil, proguardRaiseTag, config.DefaultBootclasspathLibraries...)
+			ctx.AddVariationDependencies(nil, proguardRaiseTag, config.DefaultLibraries...)
+		}
+	}
+	if sdkDep.systemModules != "" {
+		ctx.AddVariationDependencies(nil, systemModulesTag, sdkDep.systemModules)
+	}
+}
+
 func (j *Module) deps(ctx android.BottomUpMutatorContext) {
 	if ctx.Device() {
 		j.linter.deps(ctx)
 
-		sdkDep := decodeSdkDep(ctx, sdkContext(j))
-		if sdkDep.useDefaultLibs {
-			ctx.AddVariationDependencies(nil, bootClasspathTag, config.DefaultBootclasspathLibraries...)
-			ctx.AddVariationDependencies(nil, systemModulesTag, config.DefaultSystemModules)
-			if sdkDep.hasFrameworkLibs() {
-				ctx.AddVariationDependencies(nil, libTag, config.DefaultLibraries...)
-			}
-		} else if sdkDep.useModule {
-			ctx.AddVariationDependencies(nil, bootClasspathTag, sdkDep.bootclasspath...)
-			ctx.AddVariationDependencies(nil, java9LibTag, sdkDep.java9Classpath...)
-			if j.deviceProperties.EffectiveOptimizeEnabled() && sdkDep.hasStandardLibs() {
-				ctx.AddVariationDependencies(nil, proguardRaiseTag, config.DefaultBootclasspathLibraries...)
-				ctx.AddVariationDependencies(nil, proguardRaiseTag, config.DefaultLibraries...)
-			}
-		}
-		if sdkDep.systemModules != "" {
-			ctx.AddVariationDependencies(nil, systemModulesTag, sdkDep.systemModules)
-		}
+		sdkDeps(ctx, sdkContext(j), j.dexer)
 	}
 
 	syspropPublicStubs := syspropPublicStubs(ctx.Config())
@@ -1564,11 +1538,7 @@
 		j.headerJarFile = j.implementationJarFile
 	}
 
-	// Force enable the instrumentation for java code that is built for APEXes ...
-	// except for the jacocoagent itself (because instrumenting jacocoagent using jacocoagent
-	// doesn't make sense)
-	isJacocoAgent := ctx.ModuleName() == "jacocoagent"
-	if android.DirectlyInAnyApex(ctx, ctx.ModuleName()) && !isJacocoAgent && !j.IsForPlatform() {
+	if j.shouldInstrumentInApex(ctx) {
 		j.properties.Instrument = true
 	}
 
@@ -1590,8 +1560,8 @@
 
 	// Enable dex compilation for the APEX variants, unless it is disabled explicitly
 	if android.DirectlyInAnyApex(ctx, ctx.ModuleName()) && !j.IsForPlatform() {
-		if j.deviceProperties.Compile_dex == nil {
-			j.deviceProperties.Compile_dex = proptools.BoolPtr(true)
+		if j.dexProperties.Compile_dex == nil {
+			j.dexProperties.Compile_dex = proptools.BoolPtr(true)
 		}
 		if j.deviceProperties.Hostdex == nil {
 			j.deviceProperties.Hostdex = proptools.BoolPtr(true)
@@ -1599,10 +1569,14 @@
 	}
 
 	if ctx.Device() && j.hasCode(ctx) &&
-		(Bool(j.properties.Installable) || Bool(j.deviceProperties.Compile_dex)) {
+		(Bool(j.properties.Installable) || Bool(j.dexProperties.Compile_dex)) {
+		if j.shouldInstrumentStatic(ctx) {
+			j.dexer.extraProguardFlagFiles = append(j.dexer.extraProguardFlagFiles,
+				android.PathForSource(ctx, "build/make/core/proguard.jacoco.flags"))
+		}
 		// Dex compilation
 		var dexOutputFile android.ModuleOutPath
-		dexOutputFile = j.compileDex(ctx, flags, outputFile, jarName)
+		dexOutputFile = j.dexer.compileDex(ctx, flags, j.minSdkVersion(), outputFile, jarName)
 		if ctx.Failed() {
 			return
 		}
@@ -1610,9 +1584,25 @@
 		configurationName := j.ConfigurationName()
 		primary := configurationName == ctx.ModuleName()
 
+		// A source module that has been replaced by a prebuilt can never be the primary module
+		// as long as the prebuilt actually provides a build dex jar. If it doesn't then use
+		// the source module for now.
+		if j.IsReplacedByPrebuilt() {
+			ctx.VisitDirectDepsWithTag(android.PrebuiltDepTag, func(prebuilt android.Module) {
+				if h, ok := prebuilt.(hiddenAPIIntf); ok && h.bootDexJar() != nil {
+					// It is safe to ignore the source module as the prebuilt module provides an
+					// appropriate boot dex jar.
+					primary = false
+				} else {
+					// The prebuilt doesn't provide a suitable boot dex jar so keep using the
+					// source module instead.
+				}
+			})
+		}
+
 		// Hidden API CSV generation and dex encoding
 		dexOutputFile = j.hiddenAPI.hiddenAPI(ctx, configurationName, primary, dexOutputFile, j.implementationJarFile,
-			proptools.Bool(j.deviceProperties.Uncompress_dex))
+			proptools.Bool(j.dexProperties.Uncompress_dex))
 
 		// merge dex jar with resources if necessary
 		if j.resourceJar != nil {
@@ -1620,7 +1610,7 @@
 			combinedJar := android.PathForModuleOut(ctx, "dex-withres", jarName)
 			TransformJarsToJar(ctx, combinedJar, "for dex resources", jars, android.OptionalPath{},
 				false, nil, nil)
-			if *j.deviceProperties.Uncompress_dex {
+			if *j.dexProperties.Uncompress_dex {
 				combinedAlignedJar := android.PathForModuleOut(ctx, "dex-withres-aligned", jarName)
 				TransformZipAlign(ctx, combinedAlignedJar, combinedJar)
 				dexOutputFile = combinedAlignedJar
@@ -1937,11 +1927,11 @@
 	j.checkSdkVersions(ctx)
 	j.dexpreopter.installPath = android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar")
 	j.dexpreopter.isSDKLibrary = j.deviceProperties.IsSDKLibrary
-	if j.deviceProperties.Uncompress_dex == nil {
+	if j.dexProperties.Uncompress_dex == nil {
 		// If the value was not force-set by the user, use reasonable default based on the module.
-		j.deviceProperties.Uncompress_dex = proptools.BoolPtr(shouldUncompressDex(ctx, &j.dexpreopter))
+		j.dexProperties.Uncompress_dex = proptools.BoolPtr(shouldUncompressDex(ctx, &j.dexpreopter))
 	}
-	j.dexpreopter.uncompressedDex = *j.deviceProperties.Uncompress_dex
+	j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex
 	j.compile(ctx, nil)
 
 	exclusivelyForApex := android.InAnyApex(ctx.ModuleName()) && !j.IsForPlatform()
@@ -2178,6 +2168,7 @@
 	prebuiltTestProperties prebuiltTestProperties
 
 	testConfig android.Path
+	dexJarFile android.Path
 }
 
 func (j *Test) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -2476,8 +2467,15 @@
 	// Functionality common to Module and Import.
 	embeddableInModuleAndImport
 
+	hiddenAPI
+	dexer
+	dexpreopter
+
 	properties ImportProperties
 
+	// output file containing classes.dex and resources
+	dexJarFile android.Path
+
 	combinedClasspathFile android.Path
 	exportedSdkLibs       []string
 }
@@ -2486,10 +2484,22 @@
 	return sdkSpecFrom(String(j.properties.Sdk_version))
 }
 
+func (j *Import) makeSdkVersion() string {
+	return j.sdkVersion().raw
+}
+
+func (j *Import) systemModules() string {
+	return "none"
+}
+
 func (j *Import) minSdkVersion() sdkSpec {
 	return j.sdkVersion()
 }
 
+func (j *Import) targetSdkVersion() sdkSpec {
+	return j.sdkVersion()
+}
+
 func (j *Import) MinSdkVersion() string {
 	return j.minSdkVersion().version.String()
 }
@@ -2514,8 +2524,16 @@
 	return nil
 }
 
+func (j *Import) LintDepSets() LintDepSets {
+	return LintDepSets{}
+}
+
 func (j *Import) DepsMutator(ctx android.BottomUpMutatorContext) {
 	ctx.AddVariationDependencies(nil, libTag, j.properties.Libs...)
+
+	if ctx.Device() && Bool(j.dexProperties.Compile_dex) {
+		sdkDeps(ctx, sdkContext(j), j.dexer)
+	}
 }
 
 func (j *Import) GenerateAndroidBuildActions(ctx android.ModuleContext) {
@@ -2538,6 +2556,8 @@
 	// added to the Android manifest.
 	j.exportedSdkLibs = append(j.exportedSdkLibs, j.OptionalImplicitSdkLibrary()...)
 
+	var flags javaBuilderFlags
+
 	ctx.VisitDirectDeps(func(module android.Module) {
 		otherName := ctx.OtherModuleName(module)
 		tag := ctx.OtherModuleDependencyTag(module)
@@ -2546,12 +2566,16 @@
 		case Dependency:
 			switch tag {
 			case libTag, staticLibTag:
+				flags.classpath = append(flags.classpath, dep.HeaderJars()...)
 				// sdk lib names from dependencies are re-exported
 				j.exportedSdkLibs = append(j.exportedSdkLibs, dep.ExportedSdkLibs()...)
+			case bootClasspathTag:
+				flags.bootClasspath = append(flags.bootClasspath, dep.HeaderJars()...)
 			}
 		case SdkLibraryDependency:
 			switch tag {
 			case libTag:
+				flags.classpath = append(flags.classpath, dep.SdkHeaderJars(ctx, j.sdkVersion())...)
 				// names of sdk libs that are directly depended are exported
 				j.exportedSdkLibs = append(j.exportedSdkLibs, otherName)
 			}
@@ -2563,6 +2587,41 @@
 		ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"),
 			jarName, outputFile)
 	}
+
+	if ctx.Device() && Bool(j.dexProperties.Compile_dex) {
+		sdkDep := decodeSdkDep(ctx, sdkContext(j))
+		if sdkDep.invalidVersion {
+			ctx.AddMissingDependencies(sdkDep.bootclasspath)
+			ctx.AddMissingDependencies(sdkDep.java9Classpath)
+		} else if sdkDep.useFiles {
+			// sdkDep.jar is actually equivalent to turbine header.jar.
+			flags.classpath = append(flags.classpath, sdkDep.jars...)
+		}
+
+		// Dex compilation
+
+		j.dexpreopter.installPath = android.PathForModuleInstall(ctx, "framework", jarName)
+		if j.dexProperties.Uncompress_dex == nil {
+			// If the value was not force-set by the user, use reasonable default based on the module.
+			j.dexProperties.Uncompress_dex = proptools.BoolPtr(shouldUncompressDex(ctx, &j.dexpreopter))
+		}
+		j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex
+
+		var dexOutputFile android.ModuleOutPath
+		dexOutputFile = j.dexer.compileDex(ctx, flags, j.minSdkVersion(), outputFile, jarName)
+		if ctx.Failed() {
+			return
+		}
+
+		configurationName := j.BaseModuleName()
+		primary := j.Prebuilt().UsePrebuilt()
+
+		// Hidden API CSV generation and dex encoding
+		dexOutputFile = j.hiddenAPI.hiddenAPI(ctx, configurationName, primary, dexOutputFile, outputFile,
+			proptools.Bool(j.dexProperties.Uncompress_dex))
+
+		j.dexJarFile = dexOutputFile
+	}
 }
 
 var _ Dependency = (*Import)(nil)
@@ -2593,7 +2652,7 @@
 }
 
 func (j *Import) DexJar() android.Path {
-	return nil
+	return j.dexJarFile
 }
 
 func (j *Import) AidlIncludeDirs() android.Paths {
@@ -2642,6 +2701,12 @@
 
 var _ android.PrebuiltInterface = (*Import)(nil)
 
+func (j *Import) IsInstallable() bool {
+	return Bool(j.properties.Installable)
+}
+
+var _ dexpreopterInterface = (*Import)(nil)
+
 // java_import imports one or more `.jar` files into the build graph as if they were built by a java_library module.
 //
 // By default, a java_import has a single variant that expects a `.jar` file containing `.class` files that were
@@ -2652,10 +2717,15 @@
 func ImportFactory() android.Module {
 	module := &Import{}
 
-	module.AddProperties(&module.properties)
+	module.AddProperties(
+		&module.properties,
+		&module.dexer.dexProperties,
+	)
 
 	module.initModuleAndImport(&module.ModuleBase)
 
+	module.dexProperties.Optimize.EnabledByDefault = false
+
 	android.InitPrebuiltModule(module, &module.properties.Jars)
 	android.InitApexModule(module)
 	android.InitSdkAwareModule(module)
@@ -2854,6 +2924,7 @@
 	module.AddProperties(
 		&CompilerProperties{},
 		&CompilerDeviceProperties{},
+		&DexProperties{},
 		&DexpreoptProperties{},
 		&android.ProtoProperties{},
 		&aaptProperties{},
diff --git a/java/java_test.go b/java/java_test.go
index 8797119..97ec426 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -30,7 +30,6 @@
 	"android/soong/android"
 	"android/soong/cc"
 	"android/soong/dexpreopt"
-	"android/soong/genrule"
 )
 
 var buildDir string
@@ -80,7 +79,6 @@
 	RegisterSystemModulesBuildComponents(ctx)
 	ctx.RegisterModuleType("java_plugin", PluginFactory)
 	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
-	ctx.RegisterModuleType("genrule", genrule.GenRuleFactory)
 	RegisterDocsBuildComponents(ctx)
 	RegisterStubsBuildComponents(ctx)
 	RegisterSdkLibraryBuildComponents(ctx)
@@ -486,6 +484,8 @@
 		java_import {
 			name: "baz",
 			jars: ["b.jar"],
+			sdk_version: "current",
+			compile_dex: true,
 		}
 
 		dex_import {
@@ -516,8 +516,10 @@
 	fooModule := ctx.ModuleForTests("foo", "android_common")
 	javac := fooModule.Rule("javac")
 	combineJar := ctx.ModuleForTests("foo", "android_common").Description("for javac")
-	barJar := ctx.ModuleForTests("bar", "android_common").Rule("combineJar").Output
-	bazJar := ctx.ModuleForTests("baz", "android_common").Rule("combineJar").Output
+	barModule := ctx.ModuleForTests("bar", "android_common")
+	barJar := barModule.Rule("combineJar").Output
+	bazModule := ctx.ModuleForTests("baz", "android_common")
+	bazJar := bazModule.Rule("combineJar").Output
 	sdklibStubsJar := ctx.ModuleForTests("sdklib.stubs", "android_common").Rule("combineJar").Output
 
 	fooLibrary := fooModule.Module().(*Library)
@@ -532,6 +534,11 @@
 		t.Errorf("foo classpath %v does not contain %q", javac.Args["classpath"], barJar.String())
 	}
 
+	barDexJar := barModule.Module().(*Import).DexJar()
+	if barDexJar != nil {
+		t.Errorf("bar dex jar build path expected to be nil, got %q", barDexJar)
+	}
+
 	if !strings.Contains(javac.Args["classpath"], sdklibStubsJar.String()) {
 		t.Errorf("foo classpath %v does not contain %q", javac.Args["classpath"], sdklibStubsJar.String())
 	}
@@ -540,6 +547,12 @@
 		t.Errorf("foo combineJar inputs %v does not contain %q", combineJar.Inputs, bazJar.String())
 	}
 
+	bazDexJar := bazModule.Module().(*Import).DexJar().String()
+	expectedDexJar := buildDir + "/.intermediates/baz/android_common/dex/baz.jar"
+	if bazDexJar != expectedDexJar {
+		t.Errorf("baz dex jar build path expected %q, got %q", expectedDexJar, bazDexJar)
+	}
+
 	ctx.ModuleForTests("qux", "android_common").Rule("Cp")
 }
 
diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go
index 999c72f..bcc6cc0 100644
--- a/java/prebuilt_apis.go
+++ b/java/prebuilt_apis.go
@@ -34,6 +34,13 @@
 type prebuiltApisProperties struct {
 	// list of api version directories
 	Api_dirs []string
+
+	// The sdk_version of java_import modules generated based on jar files.
+	// Defaults to "current"
+	Imports_sdk_version *string
+
+	// If set to true, compile dex for java_import modules. Defaults to false.
+	Imports_compile_dex *bool
 }
 
 type prebuiltApis struct {
@@ -74,18 +81,19 @@
 	return mctx.ModuleName() + "_" + scope + "_" + apiver + "_" + module
 }
 
-func createImport(mctx android.LoadHookContext, module string, scope string, apiver string, path string) {
+func createImport(mctx android.LoadHookContext, module, scope, apiver, path, sdkVersion string, compileDex bool) {
 	props := struct {
 		Name        *string
 		Jars        []string
 		Sdk_version *string
 		Installable *bool
+		Compile_dex *bool
 	}{}
 	props.Name = proptools.StringPtr(prebuiltApiModuleName(mctx, module, scope, apiver))
 	props.Jars = append(props.Jars, path)
-	// TODO(hansson): change to scope after migration is done.
-	props.Sdk_version = proptools.StringPtr("current")
+	props.Sdk_version = proptools.StringPtr(sdkVersion)
 	props.Installable = proptools.BoolPtr(false)
+	props.Compile_dex = proptools.BoolPtr(compileDex)
 
 	mctx.CreateModule(ImportFactory, &props)
 }
@@ -101,10 +109,10 @@
 	mctx.CreateModule(android.FileGroupFactory, &filegroupProps)
 }
 
-func getPrebuiltFiles(mctx android.LoadHookContext, name string) []string {
+func getPrebuiltFiles(mctx android.LoadHookContext, p *prebuiltApis, name string) []string {
 	mydir := mctx.ModuleDir() + "/"
 	var files []string
-	for _, apiver := range mctx.Module().(*prebuiltApis).properties.Api_dirs {
+	for _, apiver := range p.properties.Api_dirs {
 		for _, scope := range []string{"public", "system", "test", "core", "module-lib", "system-server"} {
 			vfiles, err := mctx.GlobWithDeps(mydir+apiver+"/"+scope+"/"+name, nil)
 			if err != nil {
@@ -116,16 +124,19 @@
 	return files
 }
 
-func prebuiltSdkStubs(mctx android.LoadHookContext) {
+func prebuiltSdkStubs(mctx android.LoadHookContext, p *prebuiltApis) {
 	mydir := mctx.ModuleDir() + "/"
 	// <apiver>/<scope>/<module>.jar
-	files := getPrebuiltFiles(mctx, "*.jar")
+	files := getPrebuiltFiles(mctx, p, "*.jar")
+
+	sdkVersion := proptools.StringDefault(p.properties.Imports_sdk_version, "current")
+	compileDex := proptools.BoolDefault(p.properties.Imports_compile_dex, false)
 
 	for _, f := range files {
 		// create a Import module for each jar file
 		localPath := strings.TrimPrefix(f, mydir)
 		module, apiver, scope := parseJarPath(localPath)
-		createImport(mctx, module, scope, apiver, localPath)
+		createImport(mctx, module, scope, apiver, localPath, sdkVersion, compileDex)
 	}
 }
 
@@ -140,8 +151,8 @@
 	mctx.CreateModule(SystemModulesFactory, &props)
 }
 
-func prebuiltSdkSystemModules(mctx android.LoadHookContext) {
-	for _, apiver := range mctx.Module().(*prebuiltApis).properties.Api_dirs {
+func prebuiltSdkSystemModules(mctx android.LoadHookContext, p *prebuiltApis) {
+	for _, apiver := range p.properties.Api_dirs {
 		jar := android.ExistentPathForSource(mctx,
 			mctx.ModuleDir(), apiver, "public", "core-for-system-modules.jar")
 		if jar.Valid() {
@@ -150,10 +161,10 @@
 	}
 }
 
-func prebuiltApiFiles(mctx android.LoadHookContext) {
+func prebuiltApiFiles(mctx android.LoadHookContext, p *prebuiltApis) {
 	mydir := mctx.ModuleDir() + "/"
 	// <apiver>/<scope>/api/<module>.txt
-	files := getPrebuiltFiles(mctx, "api/*.txt")
+	files := getPrebuiltFiles(mctx, p, "api/*.txt")
 
 	if len(files) == 0 {
 		mctx.ModuleErrorf("no api file found under %q", mydir)
@@ -201,10 +212,10 @@
 }
 
 func createPrebuiltApiModules(mctx android.LoadHookContext) {
-	if _, ok := mctx.Module().(*prebuiltApis); ok {
-		prebuiltApiFiles(mctx)
-		prebuiltSdkStubs(mctx)
-		prebuiltSdkSystemModules(mctx)
+	if p, ok := mctx.Module().(*prebuiltApis); ok {
+		prebuiltApiFiles(mctx, p)
+		prebuiltSdkStubs(mctx, p)
+		prebuiltSdkSystemModules(mctx, p)
 	}
 }
 
diff --git a/java/sdk_library.go b/java/sdk_library.go
index f2a509a..7d973c4 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -1110,6 +1110,7 @@
 		&module.properties,
 		&module.protoProperties,
 		&module.deviceProperties,
+		&module.dexProperties,
 		&module.dexpreoptProperties,
 		&module.linter.properties,
 		&props,
@@ -1174,8 +1175,8 @@
 	// We compile the stubs for 1.8 in line with the main android.jar stubs, and potential
 	// interop with older developer tools that don't support 1.9.
 	props.Java_version = proptools.StringPtr("1.8")
-	if module.deviceProperties.Compile_dex != nil {
-		props.Compile_dex = module.deviceProperties.Compile_dex
+	if module.dexProperties.Compile_dex != nil {
+		props.Compile_dex = module.dexProperties.Compile_dex
 	}
 
 	// Dist the class jar artifact for sdk builds.
diff --git a/java/testing.go b/java/testing.go
index 48e449f..500b8de 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -132,6 +132,7 @@
 				srcs: ["a.java"],
 				sdk_version: "none",
 				system_modules: "core-platform-api-stubs-system-modules",
+				compile_dex: true,
 			}
 		`, extra)
 	}
diff --git a/ui/build/cleanbuild.go b/ui/build/cleanbuild.go
index 0bcdccb..e1123e0 100644
--- a/ui/build/cleanbuild.go
+++ b/ui/build/cleanbuild.go
@@ -104,6 +104,7 @@
 		productOut("*.img"),
 		productOut("*.zip"),
 		productOut("android-info.txt"),
+		productOut("misc_info.txt"),
 		productOut("apex"),
 		productOut("kernel"),
 		productOut("data"),
diff --git a/ui/build/paths/config.go b/ui/build/paths/config.go
index 5717401..405f379 100644
--- a/ui/build/paths/config.go
+++ b/ui/build/paths/config.go
@@ -74,6 +74,12 @@
 }
 
 var Configuration = map[string]PathConfig{
+	"aarch64-linux-android-ar":      Log,
+	"aarch64-linux-android-gcc":     Log,
+	"aarch64-linux-android-ld":      Log,
+	"aarch64-linux-android-nm":      Log,
+	"aarch64-linux-android-objcopy": Log,
+	"aarch64-linux-android-objdump": Log,
 	"bash":    Allowed,
 	"dd":      Allowed,
 	"diff":    Allowed,
@@ -89,6 +95,8 @@
 	"lsof":    Allowed,
 	"openssl": Allowed,
 	"patch":   Allowed,
+	"perl":    Log,
+	"printf":  Log,
 	"pstree":  Allowed,
 	"rsync":   Allowed,
 	"sh":      Allowed,
@@ -110,10 +118,72 @@
 	"ld.gold":    Forbidden,
 	"pkg-config": Forbidden,
 
-	// These are toybox tools that only work on Linux.
-	"pgrep": LinuxOnlyPrebuilt,
-	"pkill": LinuxOnlyPrebuilt,
-	"ps":    LinuxOnlyPrebuilt,
+	// On Linux we'll use the toybox versions of these instead.
+	"basename":  LinuxOnlyPrebuilt,
+	"cat":       LinuxOnlyPrebuilt,
+	"chmod":     LinuxOnlyPrebuilt,
+	// TODO (b/121282416): switch back to LinuxOnlyPrebuilt when build is hermetic
+	"cmp":       Log,
+	// TODO (b/121282416): switch back to LinuxOnlyPrebuilt when build is hermetic
+	"cp":        Log,
+	"comm":      LinuxOnlyPrebuilt,
+	"cut":       LinuxOnlyPrebuilt,
+	"date":      LinuxOnlyPrebuilt,
+	// TODO (b/121282416): switch back to LinuxOnlyPrebuilt when build is hermetic
+	"dirname":   Log,
+	"du":        LinuxOnlyPrebuilt,
+	"echo":      LinuxOnlyPrebuilt,
+	"egrep":     LinuxOnlyPrebuilt,
+	"env":       LinuxOnlyPrebuilt,
+	"getconf":   LinuxOnlyPrebuilt,
+	"grep":      LinuxOnlyPrebuilt,
+	"head":      LinuxOnlyPrebuilt,
+	"hostname":  LinuxOnlyPrebuilt,
+	"id":        LinuxOnlyPrebuilt,
+	"ln":        LinuxOnlyPrebuilt,
+	"ls":        LinuxOnlyPrebuilt,
+	"md5sum":    LinuxOnlyPrebuilt,
+	// TODO (b/121282416): switch back to LinuxOnlyPrebuilt when build is hermetic
+	"mkdir":     Log,
+	"mktemp":    LinuxOnlyPrebuilt,
+	"mv":        LinuxOnlyPrebuilt,
+	"od":        LinuxOnlyPrebuilt,
+	// TODO (b/121282416): switch back to LinuxOnlyPrebuilt when build is hermetic
+	"paste":     Log,
+	"pgrep":     LinuxOnlyPrebuilt,
+	"pkill":     LinuxOnlyPrebuilt,
+	"ps":        LinuxOnlyPrebuilt,
+	// TODO (b/121282416): switch back to LinuxOnlyPrebuilt when build is hermetic
+	"pwd":       Log,
+	"readlink":  LinuxOnlyPrebuilt,
+	"rm":        LinuxOnlyPrebuilt,
+	// TODO (b/121282416): switch back to LinuxOnlyPrebuilt when build is hermetic
+	"rmdir":     Log,
+	"sed":       LinuxOnlyPrebuilt,
+	"seq":       LinuxOnlyPrebuilt,
+	"setsid":    LinuxOnlyPrebuilt,
+	"sha1sum":   LinuxOnlyPrebuilt,
+	"sha256sum": LinuxOnlyPrebuilt,
+	"sha512sum": LinuxOnlyPrebuilt,
+	"sleep":     LinuxOnlyPrebuilt,
+	"sort":      LinuxOnlyPrebuilt,
+	"stat":      LinuxOnlyPrebuilt,
+	// TODO (b/121282416): switch back to LinuxOnlyPrebuilt when build is hermetic
+	"tail":      Log,
+	"tar":       LinuxOnlyPrebuilt,
+	"tee":       LinuxOnlyPrebuilt,
+	"timeout":   LinuxOnlyPrebuilt,
+	// TODO (b/121282416): switch back to LinuxOnlyPrebuilt when build is hermetic
+	"touch":     Log,
+	"true":      LinuxOnlyPrebuilt,
+	"uname":     LinuxOnlyPrebuilt,
+	"uniq":      LinuxOnlyPrebuilt,
+	"unix2dos":  LinuxOnlyPrebuilt,
+	"wc":        LinuxOnlyPrebuilt,
+	"whoami":    LinuxOnlyPrebuilt,
+	"which":     LinuxOnlyPrebuilt,
+	// TODO (b/121282416): switch back to LinuxOnlyPrebuilt when build is hermetic
+	"xxd":       Log,
 }
 
 func init() {
diff --git a/ui/build/sandbox_linux.go b/ui/build/sandbox_linux.go
index 2de772b..850ca3b 100644
--- a/ui/build/sandbox_linux.go
+++ b/ui/build/sandbox_linux.go
@@ -33,14 +33,14 @@
 var (
 	noSandbox    = Sandbox{}
 	basicSandbox = Sandbox{
-		Enabled: true,
+		Enabled: false,
 	}
 
 	dumpvarsSandbox = basicSandbox
 	katiSandbox     = basicSandbox
 	soongSandbox    = basicSandbox
 	ninjaSandbox    = Sandbox{
-		Enabled:              true,
+		Enabled:              false,
 		DisableWhenUsingGoma: true,
 
 		AllowBuildBrokenUsesNetwork: true,
@@ -54,6 +54,9 @@
 
 	working bool
 	group   string
+	srcDir  string
+	outDir  string
+	distDir string
 }
 
 func (c *Cmd) sandboxSupported() bool {
@@ -72,15 +75,34 @@
 			sandboxConfig.group = "nobody"
 		}
 
-		cmd := exec.CommandContext(c.ctx.Context, nsjailPath,
+		sandboxConfig.srcDir = absPath(c.ctx, ".")
+		sandboxConfig.outDir = absPath(c.ctx, c.config.OutDir())
+		sandboxConfig.distDir = absPath(c.ctx, c.config.DistDir())
+
+		sandboxArgs := []string{
 			"-H", "android-build",
 			"-e",
 			"-u", "nobody",
 			"-g", sandboxConfig.group,
-			"-B", "/",
+			"-R", "/",
+			"-B", sandboxConfig.srcDir,
+			"-B", "/tmp",
+			"-B", sandboxConfig.outDir,
+		}
+
+		if _, err := os.Stat(sandboxConfig.distDir); !os.IsNotExist(err) {
+			//Mount dist dir as read-write if it already exists
+			sandboxArgs = append(sandboxArgs, "-B",
+				sandboxConfig.distDir)
+		}
+
+		sandboxArgs = append(sandboxArgs,
 			"--disable_clone_newcgroup",
 			"--",
 			"/bin/bash", "-c", `if [ $(hostname) == "android-build" ]; then echo "Android" "Success"; else echo Failure; fi`)
+
+		cmd := exec.CommandContext(c.ctx.Context, nsjailPath, sandboxArgs...)
+
 		cmd.Env = c.config.Environment().Environ()
 
 		c.ctx.Verboseln(cmd.Args)
@@ -144,8 +166,17 @@
 		"--rlimit_fsize", "soft",
 		"--rlimit_nofile", "soft",
 
-		// For now, just map everything. Eventually we should limit this, especially to make most things readonly.
-		"-B", "/",
+		// For now, just map everything. Make most things readonly.
+		"-R", "/",
+
+		// Mount source are read-write
+		"-B", sandboxConfig.srcDir,
+
+		//Mount out dir as read-write
+		"-B", sandboxConfig.outDir,
+
+		// Mount a writable tmp dir
+		"-B", "/tmp",
 
 		// Disable newcgroup for now, since it may require newer kernels
 		// TODO: try out cgroups
@@ -155,6 +186,11 @@
 		"-q",
 	}
 
+	if _, err := os.Stat(sandboxConfig.distDir); !os.IsNotExist(err) {
+		//Mount dist dir as read-write if it already exists
+		sandboxArgs = append(sandboxArgs, "-B", sandboxConfig.distDir)
+	}
+
 	if c.Sandbox.AllowBuildBrokenUsesNetwork && c.config.BuildBrokenUsesNetwork() {
 		c.ctx.Printf("AllowBuildBrokenUsesNetwork: %v", c.Sandbox.AllowBuildBrokenUsesNetwork)
 		c.ctx.Printf("BuildBrokenUsesNetwork: %v", c.config.BuildBrokenUsesNetwork())